@kindlm/cli 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,52 @@
1
+ "use strict";var ft=Object.create;var te=Object.defineProperty;var gt=Object.getOwnPropertyDescriptor;var pt=Object.getOwnPropertyNames;var ht=Object.getPrototypeOf,yt=Object.prototype.hasOwnProperty;var Ct=(e,t)=>{for(var n in t)te(e,n,{get:t[n],enumerable:!0})},Se=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of pt(t))!yt.call(e,o)&&o!==n&&te(e,o,{get:()=>t[o],enumerable:!(r=gt(t,o))||r.enumerable});return e};var $=(e,t,n)=>(n=e!=null?ft(ht(e)):{},Se(t||!e||!e.__esModule?te(n,"default",{value:e,enumerable:!0}):n,e)),vt=e=>Se(te({},"__esModule",{value:!0}),e);var zt={};Ct(zt,{createProgram:()=>Kt});module.exports=vt(zt);var dt=require("commander");var ne=require("fs"),Te=require("path"),z=$(require("chalk"),1),Rt=`kindlm: 1
2
+ project: my-project
3
+
4
+ suite:
5
+ name: my-agent-tests
6
+ description: Behavioral tests for my AI agent
7
+
8
+ providers:
9
+ openai:
10
+ apiKeyEnv: OPENAI_API_KEY
11
+
12
+ models:
13
+ - id: gpt-4o
14
+ provider: openai
15
+ model: gpt-4o
16
+ params:
17
+ temperature: 0
18
+ maxTokens: 1024
19
+
20
+ prompts:
21
+ greeting:
22
+ system: You are a helpful assistant.
23
+ user: "{{message}}"
24
+
25
+ tests:
26
+ - name: basic-greeting
27
+ prompt: greeting
28
+ vars:
29
+ message: Hello, how are you?
30
+ expect:
31
+ output:
32
+ contains:
33
+ - hello
34
+ guardrails:
35
+ pii:
36
+ enabled: true
37
+
38
+ gates:
39
+ passRateMin: 0.95
40
+
41
+ defaults:
42
+ repeat: 1
43
+ concurrency: 4
44
+ timeoutMs: 60000
45
+ `;function be(e){e.command("init").description("Create a kindlm.yaml template").option("--force","Overwrite existing kindlm.yaml").action(t=>{let n=(0,Te.resolve)(process.cwd(),"kindlm.yaml");(0,ne.existsSync)(n)&&!t.force&&(console.error(z.default.red("kindlm.yaml already exists. Use --force to overwrite.")),process.exit(1)),(0,ne.writeFileSync)(n,Rt,"utf-8"),console.log(z.default.green("Created kindlm.yaml")),console.log(""),console.log("Next steps:"),console.log(` 1. Edit ${z.default.bold("kindlm.yaml")} with your test configuration`),console.log(` 2. Set your API key: ${z.default.bold("export OPENAI_API_KEY=sk-...")}`),console.log(` 3. Run tests: ${z.default.bold("kindlm test")}`)})}var xe=require("fs"),oe=require("path"),_=$(require("chalk"),1),Pe=require("@kindlm/core");var ke=require("fs");function H(){return{readFile(e){try{return{success:!0,data:(0,ke.readFileSync)(e,"utf-8")}}catch(t){return{success:!1,error:{code:"CONFIG_FILE_REF_ERROR",message:`Cannot read file: ${e}: ${t instanceof Error?t.message:String(t)}`}}}}}}function Ie(e){e.command("validate").description("Validate kindlm.yaml configuration").option("-c, --config <path>","Path to config file","kindlm.yaml").action(t=>{let n=(0,oe.resolve)(process.cwd(),t.config),r=(0,oe.dirname)(n),o;try{o=(0,xe.readFileSync)(n,"utf-8")}catch{console.error(_.default.red(`Config file not found: ${n}`)),process.exit(1)}let a=H(),i=(0,Pe.parseConfig)(o,{configDir:r,fileReader:a});if(!i.success){console.error(_.default.red("Validation failed:"));let c=i.error.details;if(c&&Array.isArray(c.errors))for(let m of c.errors)console.error(_.default.red(` - ${m}`));else console.error(_.default.red(` ${i.error.message}`));process.exit(1)}let s=i.data;console.log(_.default.green("Config is valid!")),console.log(""),console.log(` Suite: ${_.default.bold(s.suite.name)}`),console.log(` Tests: ${_.default.bold(String(s.tests.length))}`),console.log(` Models: ${_.default.bold(String(s.models.length))}`)})}var w=$(require("chalk"),1),P=require("@kindlm/core");var se=require("fs"),ie=require("path"),E=$(require("chalk"),1),J=require("@kindlm/core");function re(){return{async fetch(e,t){let n=new AbortController,r=t.timeoutMs?setTimeout(()=>n.abort(),t.timeoutMs):void 0;try{let o=await globalThis.fetch(e,{method:t.method,headers:t.headers,body:t.body,signal:n.signal});return{ok:o.ok,status:o.status,json:()=>o.json()}}finally{r!==void 0&&clearTimeout(r)}}}}var $e=$(require("ora"),1);function G(){let e;return{start(t){e=(0,$e.default)(t).start()},succeed(t){e?.succeed(t),e=void 0},fail(t){e?.fail(t),e=void 0},stop(){e?.stop(),e=void 0}}}var Ee=require("child_process"),q=require("@kindlm/core");function Oe(){return{async execute(e,t){return new Promise(n=>{let r=(0,Ee.spawn)("sh",["-c",e],{cwd:t.cwd,env:{...process.env,...t.env},stdio:["ignore","pipe","pipe"]}),o=[],a=[];r.stdout.on("data",s=>o.push(s)),r.stderr.on("data",s=>a.push(s));let i=setTimeout(()=>{r.kill("SIGTERM"),setTimeout(()=>{r.killed||r.kill("SIGKILL")},1e3)},t.timeoutMs);r.on("close",(s,c)=>{if(clearTimeout(i),c==="SIGTERM"||c==="SIGKILL"){n((0,q.err)({code:"PROVIDER_TIMEOUT",message:`Command timed out after ${t.timeoutMs}ms`}));return}n((0,q.ok)({stdout:Buffer.concat(o).toString("utf-8"),stderr:Buffer.concat(a).toString("utf-8"),exitCode:s??1}))}),r.on("error",s=>{clearTimeout(i),n((0,q.err)({code:"UNKNOWN_ERROR",message:`Failed to spawn command: ${s.message}`}))})})}}}var wt=1048576;async function V(e){let t=G(),n=!1,r=()=>{n&&process.exit(130),n=!0,t.stop(),console.error(E.default.yellow(`
46
+ Interrupted. Exiting...`)),process.exit(130)};process.on("SIGINT",r);try{return await St(e,t)}finally{process.removeListener("SIGINT",r)}}async function St(e,t){let n=(0,ie.resolve)(process.cwd(),e.configPath),r=(0,ie.dirname)(n);try{let g=(0,se.statSync)(n);g.size>wt&&(console.error(E.default.red(`Config file exceeds 1MB limit (${(g.size/1048576).toFixed(1)}MB): ${n}`)),process.exit(1))}catch{console.error(E.default.red(`Config file not found: ${n}`)),process.exit(1)}let o;try{o=(0,se.readFileSync)(n,"utf-8")}catch{console.error(E.default.red(`Config file not found: ${n}`)),process.exit(1)}let a=H(),i=(0,J.parseConfig)(o,{configDir:r,fileReader:a});i.success||(console.error(E.default.red(`Config validation failed: ${i.error.message}`)),process.exit(1));let s=i.data;e.runs!==void 0&&(s.defaults.repeat=e.runs),e.gate!==void 0&&(s.gates?s.gates.passRateMin=e.gate/100:s.gates={passRateMin:e.gate/100});let c=re(),m=new Map,u=s.providers;for(let[g,I]of Object.entries(u)){if(!I)continue;let U="";if(I.apiKeyEnv){let F=process.env[I.apiKeyEnv];F||(console.error(E.default.red(`Missing environment variable: ${I.apiKeyEnv}`)),process.exit(1)),U=F.trim()}else g!=="ollama"&&(console.error(E.default.red(`Provider "${g}" requires apiKeyEnv to be configured`)),process.exit(1));let X;try{X=(0,J.createProvider)(g,c)}catch(F){let K=F instanceof Error?F.message:String(F);console.error(E.default.red(`Failed to create provider "${g}": ${K}`)),process.exit(1)}await X.initialize({apiKey:U,baseUrl:I.baseUrl,organization:I.organization,timeoutMs:s.defaults.timeoutMs,maxRetries:2}),m.set(g,X)}let f=0,y=Tt(s),d=g=>{g.type==="test_start"?t.start(`Running ${g.test} [${g.model}] (${f}/${y})`):g.type==="test_complete"&&f++},b=s.tests.some(g=>g.command)?Oe():void 0,l=await(0,J.createRunner)(s,{adapters:m,configDir:r,fileReader:a,onProgress:d,baselineData:e.baselineData,commandExecutor:b}).run();return t.stop(),l.success||(console.error(E.default.red(`Run failed: ${l.error.message}`)),process.exit(1)),{config:s,runnerResult:l.data,configDir:r,yamlContent:o}}function Tt(e){let t=0;for(let n of e.tests){if(n.skip)continue;let r=n.repeat??e.defaults.repeat;if(n.command)t+=r;else{let o=n.models?.length??e.models.length;t+=o*r}}return t}var W=require("fs"),ge=require("path"),Ae=require("crypto");function Me(){return(0,ge.join)(process.cwd(),".kindlm","last-run.json")}function Ne(e){let t=Me(),n=(0,ge.join)(process.cwd(),".kindlm");(0,W.mkdirSync)(n,{recursive:!0,mode:448});let r={...e,runnerResult:{...e.runnerResult,aggregated:e.runnerResult.aggregated.map(o=>{let a=o.runs.flatMap(i=>i.assertions.filter(s=>!s.passed).map(s=>s.failureMessage)).filter(i=>i!==void 0);return{...o,failureMessages:a,runs:[]}})}};(0,W.writeFileSync)(t,JSON.stringify(r),{mode:384})}function Le(){try{let e=(0,W.readFileSync)(Me(),"utf-8"),t=JSON.parse(e);return t.runnerResult?.runResult&&Array.isArray(t.runnerResult.aggregated)&&typeof t.suiteName=="string"&&typeof t.configHash=="string"&&typeof t.timestamp=="string"?t:null}catch{return null}}function _e(e){return(0,Ae.createHash)("sha256").update(e).digest("hex")}var je=$(require("pdfkit"),1),Fe=require("fs"),He=require("fs/promises"),Be=require("path");function bt(e){let t=e.split(`
47
+ `),n=[],r="",o=2,a=[];for(let i of t){let s=i.match(/^(#{2,4})\s+(.+)$/);s&&s[1]?.length===2?((r||a.length>0)&&n.push({heading:r,headingLevel:o,body:a.join(`
48
+ `).trim()}),r=s[2]?.trim()??"",o=2,a=[]):a.push(i)}return(r||a.length>0)&&n.push({heading:r,headingLevel:o,body:a.join(`
49
+ `).trim()}),n}function kt(e){return e.match(/^# (.+)$/m)?.[1]?.trim()??"KindLM Compliance Report"}function xt(e){return e.match(/SHA-256:\s*`([a-f0-9]+)`/i)?.[1]??null}function De(e){switch(e){case 2:return 18;case 3:return 15;case 4:return 13;default:return 13}}function Pt(e){if(e.length<2)return null;let t=e[0]??"",n=e[1]??"";if(!t.includes("|")||!n.match(/^\s*\|[-:\s|]+\|\s*$/))return null;let r=i=>i.split("|").slice(1,-1).map(s=>s.trim()),o={cells:r(t)},a=[];for(let i=2;i<e.length;i++){let s=e[i]??"";if(!s.includes("|"))break;a.push({cells:r(s)})}return{header:o,rows:a}}function B(e,t){let n=e.page.margins.bottom;e.page.height-n-30-e.y<t&&(e.addPage(),Ue(e))}function Ue(e){let t=new Date().toISOString();e.fontSize(8).font("Helvetica").fillColor("#a8a29e").text("KindLM Compliance Report",60,40),e.text(t,60,40,{align:"right"}),e.moveDown(3)}function It(e){e.fontSize(8).font("Helvetica").fillColor("#a8a29e").text("Generated by KindLM \xB7 kindlm.com",60,e.page.height-50,{align:"center",width:e.page.width-120})}function $t(e,t,n){let r=t.header.cells.length,o=n/r,a=e.page.margins.left,i=18;B(e,i*2);let s=(c,m,u)=>{let f=e.y;u&&(e.save(),e.rect(a,f-2,n,i).fill(u),e.restore());for(let y=0;y<c.length;y++){let d=a+y*o;e.fontSize(8).font(m?"Helvetica-Bold":"Courier").fillColor("#44403c").text(c[y]??"",d+4,f,{width:o-8,height:i,lineBreak:!1})}e.y=f+i};s(t.header.cells,!0,"#f5f5f4");for(let c of t.rows)B(e,i),s(c.cells,!1)}async function Ke(e,t){return await(0,He.mkdir)((0,Be.dirname)(t),{recursive:!0}),new Promise((n,r)=>{let o=new je.default({size:"A4",margins:{top:72,bottom:72,left:60,right:60},info:{Title:"KindLM EU AI Act Compliance Report",Author:"KindLM",Creator:"KindLM CLI"}}),a=(0,Fe.createWriteStream)(t);o.pipe(a);let i=o.page.width-o.page.margins.left-o.page.margins.right,s=kt(e),c=xt(e);o.moveDown(6),o.fontSize(28).font("Helvetica-Bold").fillColor("#1c1917").text(s,{align:"center",width:i}),o.moveDown(.5),o.fontSize(14).font("Helvetica").fillColor("#57534e").text("EU AI Act Annex IV Documentation",{align:"center",width:i}),o.moveDown(1),o.fontSize(10).fillColor("#a8a29e").text(`Generated: ${new Date().toISOString()}`,{align:"center",width:i}),c&&(o.moveDown(.3),o.fontSize(9).font("Courier").fillColor("#78716c").text(`SHA-256: ${c}`,{align:"center",width:i})),o.moveDown(2),o.fontSize(10).font("Helvetica").fillColor("#6366f1").text("kindlm.com",{align:"center",link:"https://kindlm.com",width:i});let m=bt(e);for(let u of m){if(o.addPage(),Ue(o),u.heading){let C=De(u.headingLevel);o.fontSize(C).font("Helvetica-Bold").fillColor("#1c1917").text(u.heading,{width:i}),o.moveDown(.5),o.moveTo(60,o.y).lineTo(60+i,o.y).strokeColor("#e7e5e4").lineWidth(1).stroke(),o.moveDown(.8)}let f=u.body.split(`
50
+ `),y=!1,d=0;for(;d<f.length;){let C=f[d]??"";if(C.startsWith("```")){y=!y,y&&B(o,30),d++;continue}if(y){B(o,14);let l=o.y;o.save(),o.rect(o.page.margins.left,l-2,i,14).fill("#f5f5f4"),o.restore(),o.fontSize(9).font("Courier").fillColor("#44403c").text(C,{width:i}),d++;continue}if(!C.trim()){o.moveDown(.4),d++;continue}let b=f[d+1]??"";if(C.includes("|")&&d+1<f.length&&b.match(/^\s*\|[-:\s|]+\|\s*$/)){let l=[],g=d;for(;g<f.length&&(f[g]??"").includes("|");)l.push(f[g]??""),g++;let I=Pt(l);if(I){$t(o,I,i),d=g;continue}}if(C.match(/^\s*\|[-:]+/)||C.match(/^---+$/)){d++;continue}let h=C.match(/^(#{3,4})\s+(.+)$/);if(h?.[1]&&h[2]){let l=h[1].length,g=De(l);B(o,g+10),o.moveDown(.3),o.fontSize(g).font("Helvetica-Bold").fillColor("#1c1917").text(h[2].trim(),{width:i}),o.moveDown(.3),d++;continue}if(C.match(/^\s*[-*] /)){B(o,14),o.fontSize(10).font("Helvetica").fillColor("#44403c").text(C.trim(),{indent:12,width:i-12}),d++;continue}B(o,14),o.fontSize(10).font("Helvetica").fillColor("#44403c").text(C.trim(),{width:i}),d++}It(o)}o.end(),a.on("finish",()=>n(t)),a.on("error",r)})}function ze(e){e.command("test").description("Run test suites").option("-s, --suite <name>","Run a specific suite").option("--compliance","Generate compliance report").option("--reporter <type>","Output format: pretty, json, junit","pretty").option("--runs <count>","Override run count").option("--gate <percent>","Fail if pass rate below threshold").option("--pdf <path>","Export compliance report as PDF (requires --compliance)").option("-c, --config <path>","Path to config file","kindlm.yaml").action(async t=>{try{let{runnerResult:n,config:r,yamlContent:o}=await V({configPath:t.config,runs:t.runs?parseInt(t.runs,10):void 0,gate:t.gate?parseFloat(t.gate):void 0}),{runResult:a,aggregated:i}=n,s=(0,P.evaluateGates)(r.gates,i),m=Ot(t.reporter).generate(a,s);if(console.log(m.content),t.compliance){let y=(0,P.createComplianceReporter)().generate(a,s);if(console.log(""),console.log(y.content),t.pdf){let d=await Ke(y.content,t.pdf);console.log(""),console.log(w.default.green(`PDF report saved to ${d}`))}}try{Ne({runnerResult:n,suiteName:r.suite.name,configHash:_e(o),timestamp:new Date().toISOString()})}catch{}let u=a.failed===0&&a.errored===0&&s.passed;process.exit(u?0:1)}catch(n){if(n instanceof P.ProviderError){let r=n.code==="TIMEOUT"?"Provider timeout":n.code==="NETWORK_ERROR"?"Network error":n.code==="AUTH_FAILED"?"Authentication failed":n.code==="RATE_LIMITED"?"Rate limited":`Provider error (${n.code})`;console.error(w.default.red(`${r}: ${n.message}`)),n.retryable&&console.error(w.default.yellow("This error may be transient. Try again or increase --timeout."))}else if(At(n)){let o=n.code.startsWith("CONFIG_")?"Config error":"Error";console.error(w.default.red(`${o}: ${n.message}`))}else n instanceof Error&&n.name==="AbortError"?console.error(w.default.red("Request timed out. Check network connectivity or increase timeout.")):console.error(w.default.red(`Error: ${n instanceof Error?n.message:String(n)}`));process.exit(1)}})}var Et={bold:e=>w.default.bold(e),red:e=>w.default.red(e),green:e=>w.default.green(e),yellow:e=>w.default.yellow(e),cyan:e=>w.default.cyan(e),dim:e=>w.default.dim(e),greenBold:e=>w.default.green.bold(e),redBold:e=>w.default.red.bold(e)};function Ot(e){switch(e){case"json":return(0,P.createJsonReporter)();case"junit":return(0,P.createJunitReporter)();default:return(0,P.createPrettyReporter)(Et)}}function At(e){return typeof e=="object"&&e!==null&&"code"in e&&"message"in e&&typeof e.code=="string"&&typeof e.message=="string"}var k=require("path"),Je=require("fs"),p=$(require("chalk"),1),S=require("@kindlm/core");var D=require("fs"),ae=require("path");function Ge(e){return e.replace(/[^a-zA-Z0-9_-]/g,"_")}function ce(e){let t=(0,ae.join)(e,"baselines");return{read(n){let r=(0,ae.join)(t,`${Ge(n)}.json`);try{return{success:!0,data:(0,D.readFileSync)(r,"utf-8")}}catch{return{success:!1,error:{code:"BASELINE_NOT_FOUND",message:`No baseline found for suite "${n}" at ${r}`}}}},write(n,r){try{(0,D.mkdirSync)(t,{recursive:!0});let o=(0,ae.join)(t,`${Ge(n)}.json`);return(0,D.writeFileSync)(o,r,"utf-8"),{success:!0,data:void 0}}catch(o){return{success:!1,error:{code:"UNKNOWN_ERROR",message:`Failed to write baseline: ${o instanceof Error?o.message:String(o)}`}}}},list(){try{return(0,D.mkdirSync)(t,{recursive:!0}),{success:!0,data:(0,D.readdirSync)(t).filter(o=>o.endsWith(".json")).map(o=>o.replace(/\.json$/,""))}}catch(n){return{success:!1,error:{code:"UNKNOWN_ERROR",message:`Failed to list baselines: ${n instanceof Error?n.message:String(n)}`}}}}}}function We(e){let t=e.command("baseline").description("Manage test baselines");t.command("set").description("Save current results as baseline").option("-c, --config <path>","Path to config file","kindlm.yaml").option("--runs <count>","Override run count").action(async n=>{try{let r=(0,k.dirname)((0,k.resolve)(process.cwd(),n.config)),o=(0,k.join)(r,".kindlm"),a=ce(o),{config:i,runnerResult:s}=await V({configPath:n.config,runs:n.runs?parseInt(n.runs,10):void 0}),{aggregated:c}=s,m=(0,S.buildBaselineData)(i.suite.name,c,new Date().toISOString()),u=(0,S.writeBaseline)(m,a);u.success||(console.error(p.default.red(`Failed to save baseline: ${u.error.message}`)),process.exit(1));let f=Object.keys(m.results).length;console.log(""),console.log(p.default.green(`Baseline saved for suite "${i.suite.name}" (${f} test${f===1?"":"s"})`)),console.log(p.default.dim(` Location: ${o}/baselines/`)),process.exit(0)}catch(r){console.error(p.default.red(`Error: ${r instanceof Error?r.message:String(r)}`)),process.exit(1)}}),t.command("compare").description("Compare latest against baseline").option("-c, --config <path>","Path to config file","kindlm.yaml").option("--runs <count>","Override run count").action(async n=>{try{let r=(0,k.dirname)((0,k.resolve)(process.cwd(),n.config)),o=(0,k.join)(r,".kindlm"),a=ce(o),i=(0,k.resolve)(process.cwd(),n.config),s;try{s=(0,Je.readFileSync)(i,"utf-8")}catch{console.error(p.default.red(`Config file not found: ${i}`)),process.exit(1)}let c=H(),m=(0,S.parseConfig)(s,{configDir:r,fileReader:c});m.success||(console.error(p.default.red(`Config validation failed: ${m.error.message}`)),process.exit(1));let u=m.data.suite.name,f=(0,S.readBaseline)(u,a);f.success||(f.error.code==="BASELINE_NOT_FOUND"?console.error(p.default.red(`No baseline found for suite "${u}". Run \`kindlm baseline set\` first.`)):console.error(p.default.red(`Failed to read baseline: ${f.error.message}`)),process.exit(1));let y=f.data,{runnerResult:d}=await V({configPath:n.config,runs:n.runs?parseInt(n.runs,10):void 0,baselineData:y}),{aggregated:C}=d,b=(0,S.buildBaselineData)(u,C,new Date().toISOString()),h=(0,S.compareBaseline)(y,b.results);if(console.log(""),console.log(p.default.bold(`Baseline comparison for "${u}"`)),console.log(p.default.dim(` Baseline from: ${y.createdAt}`)),console.log(""),h.regressions.length>0){console.log(p.default.red.bold(` Regressions (${h.regressions.length}):`));for(let l of h.regressions)console.log(p.default.red(` ${l.testName}: ${Z(l.baselinePassRate)} \u2192 ${Z(l.currentPassRate)}`)),l.newFailureCodes.length>0&&console.log(p.default.red(` New failures: ${l.newFailureCodes.join(", ")}`));console.log("")}if(h.improvements.length>0){console.log(p.default.green.bold(` Improvements (${h.improvements.length}):`));for(let l of h.improvements)console.log(p.default.green(` ${l.testName}: ${Z(l.baselinePassRate)} \u2192 ${Z(l.currentPassRate)}`));console.log("")}if(h.unchanged.length>0){console.log(p.default.dim(` Unchanged (${h.unchanged.length}):`));for(let l of h.unchanged)console.log(p.default.dim(` ${l.testName}: ${Z(l.passRate)}`));console.log("")}if(h.newTests.length>0){console.log(p.default.cyan(` New tests (${h.newTests.length}):`));for(let l of h.newTests)console.log(p.default.cyan(` ${l}`));console.log("")}if(h.removedTests.length>0){console.log(p.default.yellow(` Removed tests (${h.removedTests.length}):`));for(let l of h.removedTests)console.log(p.default.yellow(` ${l}`));console.log("")}process.exit(h.regressions.length>0?1:0)}catch(r){console.error(p.default.red(`Error: ${r instanceof Error?r.message:String(r)}`)),process.exit(1)}}),t.command("list").description("List saved baselines").option("-c, --config <path>","Path to config file","kindlm.yaml").action(n=>{try{let r=(0,k.dirname)((0,k.resolve)(process.cwd(),n.config)),o=(0,k.join)(r,".kindlm"),a=ce(o),i=(0,S.listBaselines)(a);i.success||(console.error(p.default.red(`Failed to list baselines: ${i.error.message}`)),process.exit(1));let s=i.data;s.length===0&&(console.log(p.default.dim("No baselines saved yet. Run `kindlm baseline set` to create one.")),process.exit(0)),console.log(p.default.bold("Saved baselines:")),console.log("");for(let c of s){let m=a.read(c);if(!m.success){console.log(` ${c} ${p.default.dim("(unreadable)")}`);continue}let u=(0,S.deserializeBaseline)(m.data);if(!u.success){console.log(` ${c} ${p.default.dim("(corrupt)")}`);continue}let f=Object.keys(u.data.results).length;console.log(` ${p.default.cyan(u.data.suiteName)} \u2014 ${f} test${f===1?"":"s"}, saved ${p.default.dim(u.data.createdAt)}`)}process.exit(0)}catch(r){console.error(p.default.red(`Error: ${r instanceof Error?r.message:String(r)}`)),process.exit(1)}})}function Z(e){return`${(e*100).toFixed(1)}%`}var qe=require("readline"),Ve=require("stream"),A=$(require("chalk"),1);var O=require("fs"),pe=require("path"),he=require("os");function ye(){return(0,pe.join)((0,he.homedir)(),".kindlm","credentials")}function le(){try{let e=(0,O.readFileSync)(ye(),"utf-8"),t=JSON.parse(e);return typeof t.token=="string"&&t.token.length>0?t.token:null}catch{return null}}function Ye(e){let t=ye(),n=(0,pe.join)((0,he.homedir)(),".kindlm");(0,O.mkdirSync)(n,{recursive:!0,mode:448});let r={token:e,savedAt:new Date().toISOString()};(0,O.writeFileSync)(t,JSON.stringify(r,null,2),{mode:384}),(0,O.chmodSync)(t,384)}function Xe(){try{(0,O.unlinkSync)(ye())}catch{}}var Mt="https://api.kindlm.com";var j=class extends Error{status;constructor(t,n){super(n),this.name="CloudApiError",this.status=t}};function Y(){let e=process.env.KINDLM_CLOUD_URL??Mt;if(e.startsWith("http://")&&!Nt(e))throw new Error(`Refusing to use insecure HTTP for Cloud API: ${e}. Use HTTPS or target localhost for development.`);return e}function Nt(e){try{let t=new URL(e);return t.hostname==="localhost"||t.hostname==="127.0.0.1"||t.hostname==="::1"}catch{return!1}}function Lt(e){return new Promise(t=>setTimeout(t,e))}function Q(e,t){async function n(r,o,a){let i=`${e}${o}`,s={Authorization:`Bearer ${t}`},c={method:r,headers:s};a!==void 0&&(s["Content-Type"]="application/json",c.body=JSON.stringify(a));let m;for(let u=0;u<=1;u++){u>0&&await Lt(1e3);let f=new AbortController,y=setTimeout(()=>f.abort(),3e4);c.signal=f.signal;try{let d=await fetch(i,c);if(!d.ok){if(d.status>=500&&u<1){m=new j(d.status,`HTTP ${d.status}`);continue}let b=`HTTP ${d.status}`;if((d.headers.get("content-type")??"").includes("application/json"))try{let l=await d.json();l.error&&(b=l.error)}catch{}throw new j(d.status,b)}if(d.status===204)return;let C=d.headers.get("content-type")??"";if(!C.includes("application/json"))throw new j(d.status,`Expected JSON response but got content-type: ${C}`);return await d.json()}catch(d){if(d instanceof j)throw d;if(m=d instanceof Error?d:new Error(String(d)),u<1)continue}finally{clearTimeout(y)}}throw m??new Error("Request failed")}return{baseUrl:e,get:r=>n("GET",r),post:(r,o)=>n("POST",r,o),patch:(r,o)=>n("PATCH",r,o),delete:r=>n("DELETE",r)}}function Ze(e){e.command("login").description("Authenticate with KindLM Cloud").option("-t, --token <token>","API token (skips interactive prompt)").option("--status","Show current authentication status").option("--logout","Remove stored credentials").action(async t=>{try{if(t.logout){Xe(),console.log(A.default.green("Logged out. Credentials removed."));return}if(t.status){await _t();return}let n=t.token??process.env.KINDLM_API_TOKEN??await Dt();n.startsWith("klm_")||(console.error(A.default.red('Invalid token format. KindLM tokens start with "klm_".')),process.exit(1));let r=Q(Y(),n);try{await r.get("/v1/auth/tokens")}catch(o){throw o instanceof j&&o.status===401&&(console.error(A.default.red("Invalid or expired token.")),process.exit(1)),o}Ye(n),console.log(A.default.green("Authenticated successfully. Token saved."))}catch(n){console.error(A.default.red(`Login failed: ${n instanceof Error?n.message:String(n)}`)),process.exit(1)}})}async function _t(){let e=le();if(!e){console.log(A.default.yellow('Not authenticated. Run "kindlm login" to authenticate.'));return}let t=Q(Y(),e);try{await t.get("/v1/auth/tokens"),console.log(A.default.green("Authenticated.")),console.log(` Cloud URL: ${Y()}`)}catch(n){n instanceof j&&n.status===401?console.log(A.default.yellow('Stored token is invalid or expired. Run "kindlm login" to re-authenticate.')):console.log(A.default.yellow(`Cannot reach Cloud API: ${n instanceof Error?n.message:String(n)}`))}}function Dt(){return new Promise((e,t)=>{let n=new Ve.Writable({write(o,a,i){i()}});process.stderr.write("Paste your KindLM API token: ");let r=(0,qe.createInterface)({input:process.stdin,output:n,terminal:!0});r.question("",o=>{r.close(),process.stderr.write(`
51
+ `);let a=o.trim();if(!a){t(new Error("No token provided"));return}e(a)})})}var nt=require("path"),ot=require("child_process"),me=$(require("chalk"),1);var de=require("child_process");function Qe(){try{let e=(0,de.execSync)("git rev-parse HEAD",{encoding:"utf-8"}).trim()||null,t=(0,de.execSync)("git rev-parse --abbrev-ref HEAD",{encoding:"utf-8"}).trim()||null,r=(0,de.execSync)("git status --porcelain",{encoding:"utf-8"}).trim().length>0;return{commitSha:e,branch:t,dirty:r}}catch{return{commitSha:null,branch:null,dirty:!1}}}function et(){return process.env.GITHUB_ACTIONS?{name:"github_actions",isCI:!0,commitSha:process.env.GITHUB_SHA??null,branch:process.env.GITHUB_REF_NAME??null}:process.env.GITLAB_CI?{name:"gitlab_ci",isCI:!0,commitSha:process.env.CI_COMMIT_SHA??null,branch:process.env.CI_COMMIT_BRANCH??null}:process.env.CI?{name:null,isCI:!0,commitSha:null,branch:null}:{name:null,isCI:!1,commitSha:null,branch:null}}function ee(e){return encodeURIComponent(e)}async function tt(e,t,n){let r=await jt(e,n.projectName),o=await Ft(e,r,n.suiteName,n.configHash),a=await e.post(`/v1/runs/${ee(r)}/runs`,{suiteId:o,commitSha:n.commitSha,branch:n.branch,environment:n.environment,triggeredBy:n.triggeredBy}),i=Ht(t.aggregated),s=50;for(let l=0;l<i.length;l+=s){let g=i.slice(l,l+s);await e.post(`/v1/results/${ee(a.id)}/results`,{results:g})}let{runResult:c}=t,m=c.totalTests>0?c.passed/c.totalTests:0,u=new Set(t.aggregated.map(l=>l.modelId)),f=t.aggregated.map(l=>l.assertionScores.judge?.mean).filter(l=>l!==void 0),y=f.length>0?f.reduce((l,g)=>l+g,0)/f.length:void 0,d=t.aggregated.map(l=>l.latencyAvgMs),C=d.length>0?d.reduce((l,g)=>l+g,0)/d.length:void 0,b=t.aggregated.reduce((l,g)=>l+g.totalCostUsd,0),h=b>0?b:void 0;return await e.patch(`/v1/runs/${ee(a.id)}`,{status:"completed",passRate:m,testCount:c.totalTests,modelCount:u.size,judgeAvgScore:y,latencyAvgMs:C,costEstimateUsd:h,finishedAt:new Date().toISOString()}),{runId:a.id,projectId:r}}async function jt(e,t){let{projects:n}=await e.get("/v1/projects"),r=n.find(a=>a.name===t);return r?r.id:(await e.post("/v1/projects",{name:t})).id}async function Ft(e,t,n,r){let{suites:o}=await e.get(`/v1/suites/${ee(t)}/suites`),a=o.find(s=>s.name===n);return a?a.id:(await e.post(`/v1/suites/${ee(t)}/suites`,{name:n,configHash:r})).id}function Ht(e){return e.map(t=>{let n=t,r=t.runs.length>0?t.runs.flatMap(o=>o.assertions.filter(a=>!a.passed).map(a=>a.failureMessage)).filter(o=>o!==void 0):n.failureMessages??[];return{testCaseName:t.testCaseName,modelId:t.modelId,passed:t.passed?1:0,passRate:t.passRate,runCount:t.runCount,judgeAvg:t.assertionScores.judge?.mean??null,driftScore:t.assertionScores.drift?.mean??null,latencyAvgMs:t.latencyAvgMs??null,costUsd:t.totalCostUsd??null,totalTokens:t.totalTokens??null,failureCodes:t.failureCodes.length>0?JSON.stringify(t.failureCodes):null,failureMessages:r.length>0?JSON.stringify(r):null,assertionScores:Object.keys(t.assertionScores).length>0?JSON.stringify(t.assertionScores):null}})}function rt(e){e.command("upload").description("Push last run results to KindLM Cloud").option("-t, --token <token>","API token (overrides stored token)").option("-p, --project <name>","Project name").action(async t=>{try{let n=t.token??process.env.KINDLM_API_TOKEN??le();n||(console.error(me.default.red('Not authenticated. Run "kindlm login" first or pass --token.')),process.exit(1));let r=Le();r||(console.error(me.default.red('No test run found. Run "kindlm test" first.')),process.exit(1));let o=Qe(),a=et(),i=t.project??Ut(),s=Q(Y(),n),c=G();c.start("Uploading results to KindLM Cloud...");try{let m=await tt(s,r.runnerResult,{projectName:i,suiteName:r.suiteName,configHash:r.configHash,commitSha:a.commitSha??o.commitSha??void 0,branch:a.branch??o.branch??void 0,environment:a.isCI?"ci":"local",triggeredBy:a.name??"local"});c.succeed("Uploaded successfully."),console.log(` Run ID: ${m.runId}`),console.log(` Project: ${i}`),console.log(` Suite: ${r.suiteName}`)}catch(m){throw c.fail("Upload failed."),m}}catch(n){console.error(me.default.red(`Upload failed: ${n instanceof Error?n.message:String(n)}`)),process.exit(1)}})}function Bt(e){try{let r=new URL(e).pathname.split("/").filter(Boolean),o=r[r.length-1];if(o)return o.replace(/\.git$/,"")}catch{}let t=e.match(/^[\w.-]+@[\w.-]+:(.+?)(?:\.git)?$/);if(t?.[1]){let n=t[1].split("/");return n[n.length-1]??null}return null}function Ut(){try{let e=(0,ot.execSync)("git remote get-url origin",{encoding:"utf-8"}).trim(),t=Bt(e);if(t)return t}catch{}return(0,nt.basename)(process.cwd())}var ue=require("fs"),fe=require("path"),ct=require("child_process"),x=$(require("chalk"),1),T=require("@kindlm/core");var st=require("http"),it=require("@kindlm/core");function at(e){let t=[],n=null,r=[];function o(){for(let i of r)i()}function a(i,s){if(s.setHeader("Access-Control-Allow-Origin","*"),s.setHeader("Access-Control-Allow-Methods","POST, OPTIONS"),s.setHeader("Access-Control-Allow-Headers","Content-Type"),i.method==="OPTIONS"){s.writeHead(204),s.end();return}if(i.method!=="POST"||i.url!=="/v1/traces"){s.writeHead(404,{"Content-Type":"application/json"}),s.end(JSON.stringify({error:"Not found"}));return}let c=[];i.on("data",m=>c.push(m)),i.on("end",()=>{try{let m=Buffer.concat(c).toString("utf-8"),u=JSON.parse(m),f=(0,it.parseOtlpPayload)(u);f.success?(t.push(...f.data),o(),s.writeHead(200,{"Content-Type":"application/json"}),s.end(JSON.stringify({partialSuccess:{}}))):(s.writeHead(400,{"Content-Type":"application/json"}),s.end(JSON.stringify({error:f.error.message})))}catch{s.writeHead(400,{"Content-Type":"application/json"}),s.end(JSON.stringify({error:"Invalid JSON"}))}})}return{start(){return new Promise((i,s)=>{n=(0,st.createServer)(a),n.on("error",s),n.listen(e,()=>i())})},stop(){return new Promise(i=>{n?n.close(()=>i()):i()})},getSpans(){return[...t]},waitForSpans({timeoutMs:i}){return new Promise(s=>{if(t.length>0){s([...t]);return}let c=setTimeout(()=>{r=r.filter(u=>u!==m),s([...t])},i),m=()=>{clearTimeout(c),r=r.filter(u=>u!==m),setTimeout(()=>s([...t]),500)};r.push(m)})}}}function lt(e){e.command("trace").description("Ingest OpenTelemetry traces and run assertions against them").option("-c, --config <path>","Config file path","kindlm.yaml").option("--port <port>","OTLP HTTP port","4318").option("--command <cmd>","Command to spawn (traces are collected while it runs)").option("--timeout <ms>","Timeout in ms to wait for traces","30000").option("--reporter <type>","Report format: pretty, json, junit","pretty").action(async t=>{let n=G();try{let r=(0,fe.resolve)(process.cwd(),t.config),o=(0,fe.dirname)(r);try{(0,ue.statSync)(r).size>1048576&&(console.error(x.default.red("Config file exceeds 1MB limit")),process.exit(1))}catch{console.error(x.default.red(`Config file not found: ${r}`)),process.exit(1)}let a;try{a=(0,ue.readFileSync)(r,"utf-8")}catch{console.error(x.default.red(`Config file not found: ${r}`)),process.exit(1)}let i=H(),s=(0,T.parseConfig)(a,{configDir:o,fileReader:i});s.success||(console.error(x.default.red(`Config validation failed: ${s.error.message}`)),process.exit(1));let c=s.data,m=c.trace??{port:parseInt(t.port,10),timeoutMs:parseInt(t.timeout,10),spanMapping:{outputTextAttr:"gen_ai.completion.0.content",modelAttr:"gen_ai.response.model",systemAttr:"gen_ai.system",inputTokensAttr:"gen_ai.usage.input_tokens",outputTokensAttr:"gen_ai.usage.output_tokens"}},u=parseInt(t.port,10)||m.port,f=parseInt(t.timeout,10)||m.timeoutMs,y=at(u);await y.start(),n.start(`Listening for OTLP traces on port ${u}...`),t.command&&(0,ct.spawn)("sh",["-c",t.command],{cwd:o,env:{...process.env,OTEL_EXPORTER_OTLP_ENDPOINT:`http://localhost:${u}`,OTEL_EXPORTER_OTLP_PROTOCOL:"http/json"},stdio:"inherit"}).on("error",R=>{n.fail(`Command failed: ${R.message}`)});let d=await y.waitForSpans({timeoutMs:f});await y.stop(),d.length===0&&(n.fail("No traces received"),process.exit(1)),n.succeed(`Received ${d.length} spans`);let C=(0,T.filterSpans)(d,m.spanFilter),b=(0,T.mapSpansToResult)(C,m.spanMapping),h=re(),l=new Map,g=c.providers;for(let[v,R]of Object.entries(g)){if(!R)continue;let M="";if(R.apiKeyEnv){let N=process.env[R.apiKeyEnv];N&&(M=N.trim())}if(!(!M&&v!=="ollama"))try{let N=(0,T.createProvider)(v,h);await N.initialize({apiKey:M,baseUrl:R.baseUrl,organization:R.organization,timeoutMs:c.defaults.timeoutMs,maxRetries:2}),l.set(v,N)}catch{}}let I=c.defaults.judgeModel??c.models[0]?.id,U=c.models.find(v=>v.id===I),X=U?l.get(U.provider):void 0,F=(0,T.buildContextFromTrace)(b,{configDir:o,judgeAdapter:X,judgeModel:U?.model}),K=[];for(let v of c.tests){if(v.skip)continue;let R=(0,T.createAssertionsFromExpect)(v.expect),M=[];for(let N of R){let L=await N.evaluate(F);M.push(...L)}K.push({testName:v.name,assertions:M})}let Ce=K.reduce((v,R)=>v+R.assertions.length,0),ve=K.reduce((v,R)=>v+R.assertions.filter(M=>M.passed).length,0),Re=Ce-ve;console.log(),console.log(x.default.bold("Trace Test Results")),console.log(x.default.dim("\u2500".repeat(50)));for(let{testName:v,assertions:R}of K){let N=R.every(L=>L.passed)?x.default.green("\u2713"):x.default.red("\u2717");console.log(`${N} ${v}`);for(let L of R){let mt=L.passed?x.default.green(" \u2713"):x.default.red(" \u2717"),ut=L.failureMessage?`${L.label}: ${L.failureMessage}`:L.label;console.log(`${mt} ${ut}`)}}console.log(),console.log(`${x.default.bold("Total:")} ${ve} passed, ${Re} failed out of ${Ce} assertions`);let we=(0,T.evaluateGates)(c.gates,[]);if(!we.passed)for(let v of we.gates.filter(R=>!R.passed))console.log(x.default.red(`Gate failed: ${v.message}`));process.exit(Re>0?1:0)}catch(r){n.fail(`Trace command failed: ${r instanceof Error?r.message:String(r)}`),process.exit(1)}})}function Kt(){let e=new dt.Command;return e.name("kindlm").description("AI agent behavioral regression testing").version("0.0.0"),be(e),Ie(e),ze(e),We(e),Ze(e),rt(e),lt(e),e}0&&(module.exports={createProgram});
52
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/commands/validate.ts","../src/utils/file-reader.ts","../src/commands/test.ts","../src/utils/run-tests.ts","../src/utils/http.ts","../src/utils/spinner.ts","../src/utils/command-executor.ts","../src/utils/last-run.ts","../src/utils/pdf-renderer.ts","../src/commands/baseline.ts","../src/utils/baseline-io.ts","../src/commands/login.ts","../src/cloud/auth.ts","../src/cloud/client.ts","../src/commands/upload.ts","../src/utils/git.ts","../src/utils/env.ts","../src/cloud/upload.ts","../src/commands/trace.ts","../src/utils/trace-server.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { registerInitCommand } from \"./commands/init.js\";\nimport { registerValidateCommand } from \"./commands/validate.js\";\nimport { registerTestCommand } from \"./commands/test.js\";\nimport { registerBaselineCommand } from \"./commands/baseline.js\";\nimport { registerLoginCommand } from \"./commands/login.js\";\nimport { registerUploadCommand } from \"./commands/upload.js\";\nimport { registerTraceCommand } from \"./commands/trace.js\";\n\nexport function createProgram(): Command {\n const program = new Command();\n\n program\n .name(\"kindlm\")\n .description(\"AI agent behavioral regression testing\")\n .version(\"0.0.0\");\n\n registerInitCommand(program);\n registerValidateCommand(program);\n registerTestCommand(program);\n registerBaselineCommand(program);\n registerLoginCommand(program);\n registerUploadCommand(program);\n registerTraceCommand(program);\n\n return program;\n}\n","/* eslint-disable no-console */\nimport type { Command } from \"commander\";\nimport { existsSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport chalk from \"chalk\";\n\nconst TEMPLATE = `kindlm: 1\nproject: my-project\n\nsuite:\n name: my-agent-tests\n description: Behavioral tests for my AI agent\n\nproviders:\n openai:\n apiKeyEnv: OPENAI_API_KEY\n\nmodels:\n - id: gpt-4o\n provider: openai\n model: gpt-4o\n params:\n temperature: 0\n maxTokens: 1024\n\nprompts:\n greeting:\n system: You are a helpful assistant.\n user: \"{{message}}\"\n\ntests:\n - name: basic-greeting\n prompt: greeting\n vars:\n message: Hello, how are you?\n expect:\n output:\n contains:\n - hello\n guardrails:\n pii:\n enabled: true\n\ngates:\n passRateMin: 0.95\n\ndefaults:\n repeat: 1\n concurrency: 4\n timeoutMs: 60000\n`;\n\nexport function registerInitCommand(program: Command): void {\n program\n .command(\"init\")\n .description(\"Create a kindlm.yaml template\")\n .option(\"--force\", \"Overwrite existing kindlm.yaml\")\n .action((options: { force?: boolean }) => {\n const filePath = resolve(process.cwd(), \"kindlm.yaml\");\n\n if (existsSync(filePath) && !options.force) {\n console.error(chalk.red(\"kindlm.yaml already exists. Use --force to overwrite.\"));\n process.exit(1);\n }\n\n writeFileSync(filePath, TEMPLATE, \"utf-8\");\n console.log(chalk.green(\"Created kindlm.yaml\"));\n console.log(\"\");\n console.log(\"Next steps:\");\n console.log(` 1. Edit ${chalk.bold(\"kindlm.yaml\")} with your test configuration`);\n console.log(` 2. Set your API key: ${chalk.bold(\"export OPENAI_API_KEY=sk-...\")}`);\n console.log(` 3. Run tests: ${chalk.bold(\"kindlm test\")}`);\n });\n}\n","/* eslint-disable no-console */\nimport type { Command } from \"commander\";\nimport { readFileSync } from \"node:fs\";\nimport { resolve, dirname } from \"node:path\";\nimport chalk from \"chalk\";\nimport { parseConfig } from \"@kindlm/core\";\nimport { createNodeFileReader } from \"../utils/file-reader.js\";\n\nexport function registerValidateCommand(program: Command): void {\n program\n .command(\"validate\")\n .description(\"Validate kindlm.yaml configuration\")\n .option(\"-c, --config <path>\", \"Path to config file\", \"kindlm.yaml\")\n .action((options: { config: string }) => {\n const configPath = resolve(process.cwd(), options.config);\n const configDir = dirname(configPath);\n\n let yamlContent: string;\n try {\n yamlContent = readFileSync(configPath, \"utf-8\");\n } catch {\n console.error(chalk.red(`Config file not found: ${configPath}`));\n process.exit(1);\n }\n\n const fileReader = createNodeFileReader();\n const result = parseConfig(yamlContent, { configDir, fileReader });\n\n if (!result.success) {\n console.error(chalk.red(\"Validation failed:\"));\n const details = result.error.details;\n if (details && Array.isArray(details[\"errors\"])) {\n for (const e of details[\"errors\"] as string[]) {\n console.error(chalk.red(` - ${e}`));\n }\n } else {\n console.error(chalk.red(` ${result.error.message}`));\n }\n process.exit(1);\n }\n\n const config = result.data;\n console.log(chalk.green(\"Config is valid!\"));\n console.log(\"\");\n console.log(` Suite: ${chalk.bold(config.suite.name)}`);\n console.log(` Tests: ${chalk.bold(String(config.tests.length))}`);\n console.log(` Models: ${chalk.bold(String(config.models.length))}`);\n });\n}\n","import { readFileSync } from \"node:fs\";\nimport type { FileReader, Result } from \"@kindlm/core\";\n\nexport function createNodeFileReader(): FileReader {\n return {\n readFile(path: string): Result<string> {\n try {\n return { success: true, data: readFileSync(path, \"utf-8\") };\n } catch (e) {\n return {\n success: false,\n error: {\n code: \"CONFIG_FILE_REF_ERROR\",\n message: `Cannot read file: ${path}: ${e instanceof Error ? e.message : String(e)}`,\n },\n };\n }\n },\n };\n}\n","/* eslint-disable no-console */\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport {\n evaluateGates,\n createPrettyReporter,\n createJsonReporter,\n createJunitReporter,\n createComplianceReporter,\n ProviderError,\n} from \"@kindlm/core\";\nimport type { Colorize, KindlmError } from \"@kindlm/core\";\nimport { runTests } from \"../utils/run-tests.js\";\nimport { saveLastRun, computeConfigHash } from \"../utils/last-run.js\";\nimport { renderCompliancePdf } from \"../utils/pdf-renderer.js\";\n\ninterface TestOptions {\n suite?: string;\n compliance?: boolean;\n reporter: string;\n runs?: string;\n gate?: string;\n config: string;\n pdf?: string;\n}\n\nexport function registerTestCommand(program: Command): void {\n program\n .command(\"test\")\n .description(\"Run test suites\")\n .option(\"-s, --suite <name>\", \"Run a specific suite\")\n .option(\"--compliance\", \"Generate compliance report\")\n .option(\"--reporter <type>\", \"Output format: pretty, json, junit\", \"pretty\")\n .option(\"--runs <count>\", \"Override run count\")\n .option(\"--gate <percent>\", \"Fail if pass rate below threshold\")\n .option(\"--pdf <path>\", \"Export compliance report as PDF (requires --compliance)\")\n .option(\"-c, --config <path>\", \"Path to config file\", \"kindlm.yaml\")\n .action(async (options: TestOptions) => {\n try {\n const { runnerResult, config, yamlContent } = await runTests({\n configPath: options.config,\n runs: options.runs ? parseInt(options.runs, 10) : undefined,\n gate: options.gate ? parseFloat(options.gate) : undefined,\n });\n\n const { runResult: result, aggregated } = runnerResult;\n\n // Evaluate gates\n const gateEvaluation = evaluateGates(config.gates, aggregated);\n\n // Select + generate report\n const reporter = selectReporter(options.reporter);\n const report = reporter.generate(result, gateEvaluation);\n console.log(report.content);\n\n // Compliance report\n if (options.compliance) {\n const complianceReporter = createComplianceReporter();\n const complianceReport = complianceReporter.generate(result, gateEvaluation);\n console.log(\"\");\n console.log(complianceReport.content);\n\n // PDF export\n if (options.pdf) {\n const pdfPath = await renderCompliancePdf(complianceReport.content, options.pdf);\n console.log(\"\");\n console.log(chalk.green(`PDF report saved to ${pdfPath}`));\n }\n }\n\n // Cache last run for upload\n try {\n saveLastRun({\n runnerResult,\n suiteName: config.suite.name,\n configHash: computeConfigHash(yamlContent),\n timestamp: new Date().toISOString(),\n });\n } catch {\n // Non-fatal — don't block exit on cache failure\n }\n\n // Exit code\n const allPassed = result.failed === 0 && result.errored === 0 && gateEvaluation.passed;\n process.exit(allPassed ? 0 : 1);\n } catch (e) {\n if (e instanceof ProviderError) {\n const prefix = e.code === \"TIMEOUT\"\n ? \"Provider timeout\"\n : e.code === \"NETWORK_ERROR\"\n ? \"Network error\"\n : e.code === \"AUTH_FAILED\"\n ? \"Authentication failed\"\n : e.code === \"RATE_LIMITED\"\n ? \"Rate limited\"\n : `Provider error (${e.code})`;\n console.error(chalk.red(`${prefix}: ${e.message}`));\n if (e.retryable) {\n console.error(chalk.yellow(\"This error may be transient. Try again or increase --timeout.\"));\n }\n } else if (isKindlmError(e)) {\n const isConfig = e.code.startsWith(\"CONFIG_\");\n const label = isConfig ? \"Config error\" : \"Error\";\n console.error(chalk.red(`${label}: ${e.message}`));\n } else if (e instanceof Error && e.name === \"AbortError\") {\n console.error(chalk.red(\"Request timed out. Check network connectivity or increase timeout.\"));\n } else {\n console.error(chalk.red(`Error: ${e instanceof Error ? e.message : String(e)}`));\n }\n process.exit(1);\n }\n });\n}\n\nconst chalkColorize: Colorize = {\n bold: (t) => chalk.bold(t),\n red: (t) => chalk.red(t),\n green: (t) => chalk.green(t),\n yellow: (t) => chalk.yellow(t),\n cyan: (t) => chalk.cyan(t),\n dim: (t) => chalk.dim(t),\n greenBold: (t) => chalk.green.bold(t),\n redBold: (t) => chalk.red.bold(t),\n};\n\nfunction selectReporter(type: string) {\n switch (type) {\n case \"json\":\n return createJsonReporter();\n case \"junit\":\n return createJunitReporter();\n case \"pretty\":\n default:\n return createPrettyReporter(chalkColorize);\n }\n}\n\nfunction isKindlmError(e: unknown): e is KindlmError {\n return (\n typeof e === \"object\" &&\n e !== null &&\n \"code\" in e &&\n \"message\" in e &&\n typeof (e as KindlmError).code === \"string\" &&\n typeof (e as KindlmError).message === \"string\"\n );\n}\n","/* eslint-disable no-console */\nimport { readFileSync, statSync } from \"node:fs\";\nimport { resolve, dirname } from \"node:path\";\nimport chalk from \"chalk\";\nimport {\n parseConfig,\n createProvider,\n createRunner,\n} from \"@kindlm/core\";\nimport type {\n ProviderAdapter,\n KindLMConfig,\n ProgressEvent,\n RunnerResult,\n BaselineData,\n} from \"@kindlm/core\";\nimport { createHttpClient } from \"./http.js\";\nimport { createSpinner } from \"./spinner.js\";\nimport { createNodeFileReader } from \"./file-reader.js\";\nimport { createNodeCommandExecutor } from \"./command-executor.js\";\n\nexport interface RunTestsOptions {\n configPath: string;\n runs?: number;\n gate?: number;\n baselineData?: BaselineData;\n}\n\nexport interface RunTestsResult {\n config: KindLMConfig;\n runnerResult: RunnerResult;\n configDir: string;\n yamlContent: string;\n}\n\nconst MAX_CONFIG_SIZE = 1_048_576; // 1MB\n\nexport async function runTests(options: RunTestsOptions): Promise<RunTestsResult> {\n const spinner = createSpinner();\n\n // SIGINT handler: stop spinner, print partial results, exit 130\n let interrupted = false;\n const sigintHandler = () => {\n if (interrupted) process.exit(130);\n interrupted = true;\n spinner.stop();\n console.error(chalk.yellow(\"\\nInterrupted. Exiting...\"));\n process.exit(130);\n };\n process.on(\"SIGINT\", sigintHandler);\n\n try {\n return await runTestsInner(options, spinner);\n } finally {\n process.removeListener(\"SIGINT\", sigintHandler);\n }\n}\n\nasync function runTestsInner(\n options: RunTestsOptions,\n spinner: ReturnType<typeof createSpinner>,\n): Promise<RunTestsResult> {\n // 1. Read config\n const configPath = resolve(process.cwd(), options.configPath);\n const configDir = dirname(configPath);\n\n // Check file size before reading\n try {\n const stat = statSync(configPath);\n if (stat.size > MAX_CONFIG_SIZE) {\n console.error(chalk.red(`Config file exceeds 1MB limit (${(stat.size / 1_048_576).toFixed(1)}MB): ${configPath}`));\n process.exit(1);\n }\n } catch {\n console.error(chalk.red(`Config file not found: ${configPath}`));\n process.exit(1);\n }\n\n let yamlContent: string;\n try {\n yamlContent = readFileSync(configPath, \"utf-8\");\n } catch {\n console.error(chalk.red(`Config file not found: ${configPath}`));\n process.exit(1);\n }\n\n // 2. Parse + validate\n const fileReader = createNodeFileReader();\n const parseResult = parseConfig(yamlContent, { configDir, fileReader });\n if (!parseResult.success) {\n console.error(chalk.red(`Config validation failed: ${parseResult.error.message}`));\n process.exit(1);\n }\n\n const config: KindLMConfig = parseResult.data;\n\n // 3. Apply CLI overrides\n if (options.runs !== undefined) {\n config.defaults.repeat = options.runs;\n }\n if (options.gate !== undefined) {\n if (!config.gates) {\n config.gates = { passRateMin: options.gate / 100 } as KindLMConfig[\"gates\"];\n } else {\n config.gates.passRateMin = options.gate / 100;\n }\n }\n\n // 4. Resolve API keys + create provider adapters\n const httpClient = createHttpClient();\n const adapters = new Map<string, ProviderAdapter>();\n\n const providers = config.providers as Record<string, { apiKeyEnv?: string; baseUrl?: string; organization?: string } | undefined>;\n for (const [name, providerConfig] of Object.entries(providers)) {\n if (!providerConfig) continue;\n\n let apiKey = \"\";\n if (providerConfig.apiKeyEnv) {\n const key = process.env[providerConfig.apiKeyEnv];\n if (!key) {\n console.error(chalk.red(`Missing environment variable: ${providerConfig.apiKeyEnv}`));\n process.exit(1);\n }\n apiKey = key.trim();\n } else if (name !== \"ollama\") {\n console.error(chalk.red(`Provider \"${name}\" requires apiKeyEnv to be configured`));\n process.exit(1);\n }\n\n let adapter: ProviderAdapter;\n try {\n adapter = createProvider(name, httpClient);\n } catch (cause) {\n const msg = cause instanceof Error ? cause.message : String(cause);\n console.error(chalk.red(`Failed to create provider \"${name}\": ${msg}`));\n process.exit(1);\n }\n\n await adapter.initialize({\n apiKey,\n baseUrl: providerConfig.baseUrl,\n organization: providerConfig.organization,\n timeoutMs: config.defaults.timeoutMs,\n maxRetries: 2,\n });\n\n adapters.set(name, adapter);\n }\n\n // 5. Create + run\n let completedTests = 0;\n const totalTests = countExecutionUnits(config);\n\n const onProgress = (event: ProgressEvent) => {\n if (event.type === \"test_start\") {\n spinner.start(`Running ${event.test} [${event.model}] (${completedTests}/${totalTests})`);\n } else if (event.type === \"test_complete\") {\n completedTests++;\n }\n };\n\n // Check if any tests use command mode\n const hasCommandTests = config.tests.some((t) => t.command);\n const commandExecutor = hasCommandTests ? createNodeCommandExecutor() : undefined;\n\n const runner = createRunner(config, {\n adapters,\n configDir,\n fileReader,\n onProgress,\n baselineData: options.baselineData,\n commandExecutor,\n });\n\n const runResult = await runner.run();\n spinner.stop();\n\n if (!runResult.success) {\n console.error(chalk.red(`Run failed: ${runResult.error.message}`));\n process.exit(1);\n }\n\n return {\n config,\n runnerResult: runResult.data,\n configDir,\n yamlContent,\n };\n}\n\nfunction countExecutionUnits(config: KindLMConfig): number {\n let count = 0;\n for (const test of config.tests) {\n if (test.skip) continue;\n const repeat = test.repeat ?? config.defaults.repeat;\n if (test.command) {\n count += repeat;\n } else {\n const modelCount = test.models?.length ?? config.models.length;\n count += modelCount * repeat;\n }\n }\n return count;\n}\n","import type { HttpClient, HttpRequestInit, HttpResponse } from \"@kindlm/core\";\n\nexport function createHttpClient(): HttpClient {\n return {\n async fetch(url: string, init: HttpRequestInit): Promise<HttpResponse> {\n const controller = new AbortController();\n const timeoutId = init.timeoutMs\n ? setTimeout(() => controller.abort(), init.timeoutMs)\n : undefined;\n\n try {\n const response = await globalThis.fetch(url, {\n method: init.method,\n headers: init.headers,\n body: init.body,\n signal: controller.signal,\n });\n\n return {\n ok: response.ok,\n status: response.status,\n json: () => response.json() as Promise<unknown>,\n };\n } finally {\n if (timeoutId !== undefined) clearTimeout(timeoutId);\n }\n },\n };\n}\n","import ora, { type Ora } from \"ora\";\n\nexport interface Spinner {\n start(text: string): void;\n succeed(text: string): void;\n fail(text: string): void;\n stop(): void;\n}\n\nexport function createSpinner(): Spinner {\n let instance: Ora | undefined;\n\n return {\n start(text: string): void {\n instance = ora(text).start();\n },\n succeed(text: string): void {\n instance?.succeed(text);\n instance = undefined;\n },\n fail(text: string): void {\n instance?.fail(text);\n instance = undefined;\n },\n stop(): void {\n instance?.stop();\n instance = undefined;\n },\n };\n}\n","import { spawn } from \"node:child_process\";\nimport { ok, err } from \"@kindlm/core\";\nimport type { CommandExecutor, CommandExecuteOptions, RawCommandOutput, Result } from \"@kindlm/core\";\n\nexport function createNodeCommandExecutor(): CommandExecutor {\n return {\n async execute(command: string, options: CommandExecuteOptions): Promise<Result<RawCommandOutput>> {\n return new Promise((resolve) => {\n const child = spawn(\"sh\", [\"-c\", command], {\n cwd: options.cwd,\n env: { ...process.env, ...options.env },\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n\n child.stdout.on(\"data\", (chunk: Buffer) => stdoutChunks.push(chunk));\n child.stderr.on(\"data\", (chunk: Buffer) => stderrChunks.push(chunk));\n\n const timer = setTimeout(() => {\n child.kill(\"SIGTERM\");\n setTimeout(() => {\n if (!child.killed) child.kill(\"SIGKILL\");\n }, 1000);\n }, options.timeoutMs);\n\n child.on(\"close\", (code, signal) => {\n clearTimeout(timer);\n\n if (signal === \"SIGTERM\" || signal === \"SIGKILL\") {\n resolve(err({\n code: \"PROVIDER_TIMEOUT\",\n message: `Command timed out after ${options.timeoutMs}ms`,\n }));\n return;\n }\n\n resolve(ok({\n stdout: Buffer.concat(stdoutChunks).toString(\"utf-8\"),\n stderr: Buffer.concat(stderrChunks).toString(\"utf-8\"),\n exitCode: code ?? 1,\n }));\n });\n\n child.on(\"error\", (e) => {\n clearTimeout(timer);\n resolve(err({\n code: \"UNKNOWN_ERROR\",\n message: `Failed to spawn command: ${e.message}`,\n }));\n });\n });\n },\n };\n}\n","import { readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport type { RunnerResult } from \"@kindlm/core\";\n\nexport interface LastRunData {\n runnerResult: RunnerResult;\n suiteName: string;\n configHash: string;\n timestamp: string;\n}\n\nfunction getLastRunPath(): string {\n return join(process.cwd(), \".kindlm\", \"last-run.json\");\n}\n\nexport function saveLastRun(data: LastRunData): void {\n const filePath = getLastRunPath();\n const dir = join(process.cwd(), \".kindlm\");\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n\n // Strip individual run data (outputText, full assertions) to reduce file size.\n // Pre-extract failure messages since upload needs them.\n const slimmed: LastRunData = {\n ...data,\n runnerResult: {\n ...data.runnerResult,\n aggregated: data.runnerResult.aggregated.map((agg) => {\n const failureMessages = agg.runs\n .flatMap((r) => r.assertions.filter((a) => !a.passed).map((a) => a.failureMessage))\n .filter((m): m is string => m !== undefined);\n\n return {\n ...agg,\n failureMessages,\n runs: [],\n };\n }),\n },\n };\n\n writeFileSync(filePath, JSON.stringify(slimmed), { mode: 0o600 });\n}\n\nexport function loadLastRun(): LastRunData | null {\n try {\n const raw = readFileSync(getLastRunPath(), \"utf-8\");\n const parsed = JSON.parse(raw) as LastRunData;\n if (\n parsed.runnerResult?.runResult &&\n Array.isArray(parsed.runnerResult.aggregated) &&\n typeof parsed.suiteName === \"string\" &&\n typeof parsed.configHash === \"string\" &&\n typeof parsed.timestamp === \"string\"\n ) {\n return parsed;\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport function computeConfigHash(yamlContent: string): string {\n return createHash(\"sha256\").update(yamlContent).digest(\"hex\");\n}\n","// @ts-expect-error pdfkit has no type declarations\nimport PDFDocument from \"pdfkit\";\nimport { createWriteStream } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\ninterface Section {\n heading: string;\n headingLevel: number;\n body: string;\n}\n\nfunction parseSections(markdown: string): Section[] {\n const lines = markdown.split(\"\\n\");\n const sections: Section[] = [];\n let currentHeading = \"\";\n let currentLevel = 2;\n let currentBody: string[] = [];\n\n for (const line of lines) {\n const headingMatch = line.match(/^(#{2,4})\\s+(.+)$/);\n if (headingMatch && headingMatch[1]?.length === 2) {\n if (currentHeading || currentBody.length > 0) {\n sections.push({ heading: currentHeading, headingLevel: currentLevel, body: currentBody.join(\"\\n\").trim() });\n }\n currentHeading = headingMatch[2]?.trim() ?? \"\";\n currentLevel = 2;\n currentBody = [];\n } else {\n currentBody.push(line);\n }\n }\n\n if (currentHeading || currentBody.length > 0) {\n sections.push({ heading: currentHeading, headingLevel: currentLevel, body: currentBody.join(\"\\n\").trim() });\n }\n\n return sections;\n}\n\nfunction extractTitle(markdown: string): string {\n const match = markdown.match(/^# (.+)$/m);\n return match?.[1]?.trim() ?? \"KindLM Compliance Report\";\n}\n\nfunction extractHash(markdown: string): string | null {\n const match = markdown.match(/SHA-256:\\s*`([a-f0-9]+)`/i);\n return match?.[1] ?? null;\n}\n\nfunction headingSize(level: number): number {\n switch (level) {\n case 2: return 18;\n case 3: return 15;\n case 4: return 13;\n default: return 13;\n }\n}\n\ninterface TableRow {\n cells: string[];\n}\n\nfunction parseTable(lines: string[]): { header: TableRow; rows: TableRow[] } | null {\n if (lines.length < 2) return null;\n const headerLine = lines[0] ?? \"\";\n const separatorLine = lines[1] ?? \"\";\n if (!headerLine.includes(\"|\") || !separatorLine.match(/^\\s*\\|[-:\\s|]+\\|\\s*$/)) return null;\n\n const parseCells = (line: string): string[] =>\n line.split(\"|\").slice(1, -1).map((c) => c.trim());\n\n const header: TableRow = { cells: parseCells(headerLine) };\n const rows: TableRow[] = [];\n for (let i = 2; i < lines.length; i++) {\n const row = lines[i] ?? \"\";\n if (!row.includes(\"|\")) break;\n rows.push({ cells: parseCells(row) });\n }\n return { header, rows };\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype PDFDoc = any;\n\nfunction ensureSpace(doc: PDFDoc, needed: number): void {\n const bottomMargin = doc.page.margins.bottom as number;\n const available = (doc.page.height as number) - bottomMargin - 30 - (doc.y as number);\n if (available < needed) {\n doc.addPage();\n renderPageHeader(doc);\n }\n}\n\nfunction renderPageHeader(doc: PDFDoc): void {\n const timestamp = new Date().toISOString();\n doc.fontSize(8).font(\"Helvetica\").fillColor(\"#a8a29e\").text(\"KindLM Compliance Report\", 60, 40);\n doc.text(timestamp, 60, 40, { align: \"right\" });\n doc.moveDown(3);\n}\n\nfunction renderPageFooter(doc: PDFDoc): void {\n doc.fontSize(8).font(\"Helvetica\").fillColor(\"#a8a29e\").text(\n \"Generated by KindLM · kindlm.com\",\n 60,\n (doc.page.height as number) - 50,\n { align: \"center\", width: (doc.page.width as number) - 120 },\n );\n}\n\nfunction renderTable(\n doc: PDFDoc,\n table: { header: TableRow; rows: TableRow[] },\n contentWidth: number,\n): void {\n const colCount = table.header.cells.length;\n const colWidth = contentWidth / colCount;\n const leftMargin = doc.page.margins.left as number;\n const rowHeight = 18;\n\n ensureSpace(doc, rowHeight * 2);\n\n const drawRow = (cells: string[], bold: boolean, bgColor?: string) => {\n const y = doc.y as number;\n if (bgColor) {\n doc.save();\n doc.rect(leftMargin, y - 2, contentWidth, rowHeight).fill(bgColor);\n doc.restore();\n }\n for (let i = 0; i < cells.length; i++) {\n const x = leftMargin + i * colWidth;\n doc.fontSize(8)\n .font(bold ? \"Helvetica-Bold\" : \"Courier\")\n .fillColor(\"#44403c\")\n .text(cells[i] ?? \"\", x + 4, y, { width: colWidth - 8, height: rowHeight, lineBreak: false });\n }\n doc.y = y + rowHeight;\n };\n\n drawRow(table.header.cells, true, \"#f5f5f4\");\n for (const row of table.rows) {\n ensureSpace(doc, rowHeight);\n drawRow(row.cells, false);\n }\n}\n\nexport async function renderCompliancePdf(\n markdownContent: string,\n outputPath: string,\n): Promise<string> {\n await mkdir(dirname(outputPath), { recursive: true });\n\n return new Promise((resolve, reject) => {\n const doc = new PDFDocument({\n size: \"A4\",\n margins: { top: 72, bottom: 72, left: 60, right: 60 },\n info: {\n Title: \"KindLM EU AI Act Compliance Report\",\n Author: \"KindLM\",\n Creator: \"KindLM CLI\",\n },\n });\n\n const stream = createWriteStream(outputPath);\n doc.pipe(stream);\n\n const contentWidth = (doc.page.width as number) - (doc.page.margins.left as number) - (doc.page.margins.right as number);\n const title = extractTitle(markdownContent);\n const hash = extractHash(markdownContent);\n\n // --- Title Page ---\n doc.moveDown(6);\n doc.fontSize(28).font(\"Helvetica-Bold\").fillColor(\"#1c1917\").text(title, { align: \"center\", width: contentWidth });\n doc.moveDown(0.5);\n doc.fontSize(14).font(\"Helvetica\").fillColor(\"#57534e\").text(\"EU AI Act Annex IV Documentation\", { align: \"center\", width: contentWidth });\n doc.moveDown(1);\n doc.fontSize(10).fillColor(\"#a8a29e\").text(`Generated: ${new Date().toISOString()}`, { align: \"center\", width: contentWidth });\n\n if (hash) {\n doc.moveDown(0.3);\n doc.fontSize(9).font(\"Courier\").fillColor(\"#78716c\").text(`SHA-256: ${hash}`, { align: \"center\", width: contentWidth });\n }\n\n doc.moveDown(2);\n doc.fontSize(10).font(\"Helvetica\").fillColor(\"#6366f1\").text(\"kindlm.com\", { align: \"center\", link: \"https://kindlm.com\", width: contentWidth });\n\n // --- Content Pages ---\n const sections = parseSections(markdownContent);\n\n for (const section of sections) {\n doc.addPage();\n renderPageHeader(doc);\n\n if (section.heading) {\n const size = headingSize(section.headingLevel);\n doc.fontSize(size).font(\"Helvetica-Bold\").fillColor(\"#1c1917\").text(section.heading, { width: contentWidth });\n doc.moveDown(0.5);\n doc.moveTo(60, doc.y).lineTo(60 + contentWidth, doc.y).strokeColor(\"#e7e5e4\").lineWidth(1).stroke();\n doc.moveDown(0.8);\n }\n\n const bodyLines = section.body.split(\"\\n\");\n let inCodeBlock = false;\n let i = 0;\n while (i < bodyLines.length) {\n const line = bodyLines[i] ?? \"\";\n\n if (line.startsWith(\"```\")) {\n inCodeBlock = !inCodeBlock;\n if (inCodeBlock) {\n ensureSpace(doc, 30);\n }\n i++;\n continue;\n }\n\n if (inCodeBlock) {\n ensureSpace(doc, 14);\n const codeY = doc.y as number;\n doc.save();\n doc.rect(doc.page.margins.left, codeY - 2, contentWidth, 14).fill(\"#f5f5f4\");\n doc.restore();\n doc.fontSize(9).font(\"Courier\").fillColor(\"#44403c\").text(line, { width: contentWidth });\n i++;\n continue;\n }\n\n if (!line.trim()) {\n doc.moveDown(0.4);\n i++;\n continue;\n }\n\n // Table detection: look ahead for separator line\n const nextLine = bodyLines[i + 1] ?? \"\";\n if (line.includes(\"|\") && i + 1 < bodyLines.length && nextLine.match(/^\\s*\\|[-:\\s|]+\\|\\s*$/)) {\n const tableLines: string[] = [];\n let j = i;\n while (j < bodyLines.length && (bodyLines[j] ?? \"\").includes(\"|\")) {\n tableLines.push(bodyLines[j] ?? \"\");\n j++;\n }\n const table = parseTable(tableLines);\n if (table) {\n renderTable(doc, table, contentWidth);\n i = j;\n continue;\n }\n }\n\n // Skip standalone separator lines\n if (line.match(/^\\s*\\|[-:]+/) || line.match(/^---+$/)) {\n i++;\n continue;\n }\n\n // Subheadings (### and ####)\n const subheadingMatch = line.match(/^(#{3,4})\\s+(.+)$/);\n if (subheadingMatch?.[1] && subheadingMatch[2]) {\n const level = subheadingMatch[1].length;\n const size = headingSize(level);\n ensureSpace(doc, size + 10);\n doc.moveDown(0.3);\n doc.fontSize(size).font(\"Helvetica-Bold\").fillColor(\"#1c1917\").text(subheadingMatch[2].trim(), { width: contentWidth });\n doc.moveDown(0.3);\n i++;\n continue;\n }\n\n // Bullet points\n if (line.match(/^\\s*[-*] /)) {\n ensureSpace(doc, 14);\n doc.fontSize(10).font(\"Helvetica\").fillColor(\"#44403c\").text(line.trim(), { indent: 12, width: contentWidth - 12 });\n i++;\n continue;\n }\n\n // Regular text\n ensureSpace(doc, 14);\n doc.fontSize(10).font(\"Helvetica\").fillColor(\"#44403c\").text(line.trim(), { width: contentWidth });\n i++;\n }\n\n renderPageFooter(doc);\n }\n\n doc.end();\n\n stream.on(\"finish\", () => resolve(outputPath));\n stream.on(\"error\", reject);\n });\n}\n","/* eslint-disable no-console */\nimport type { Command } from \"commander\";\nimport { resolve, dirname, join } from \"node:path\";\nimport { readFileSync } from \"node:fs\";\nimport chalk from \"chalk\";\nimport {\n parseConfig,\n readBaseline,\n writeBaseline,\n listBaselines,\n buildBaselineData,\n compareBaseline,\n deserializeBaseline,\n} from \"@kindlm/core\";\nimport { runTests } from \"../utils/run-tests.js\";\nimport { createFileBaselineIO } from \"../utils/baseline-io.js\";\nimport { createNodeFileReader } from \"../utils/file-reader.js\";\n\ninterface BaselineSetOptions {\n config: string;\n runs?: string;\n}\n\ninterface BaselineCompareOptions {\n config: string;\n runs?: string;\n}\n\ninterface BaselineListOptions {\n config: string;\n}\n\nexport function registerBaselineCommand(program: Command): void {\n const baseline = program\n .command(\"baseline\")\n .description(\"Manage test baselines\");\n\n baseline\n .command(\"set\")\n .description(\"Save current results as baseline\")\n .option(\"-c, --config <path>\", \"Path to config file\", \"kindlm.yaml\")\n .option(\"--runs <count>\", \"Override run count\")\n .action(async (options: BaselineSetOptions) => {\n try {\n const configDir = dirname(resolve(process.cwd(), options.config));\n const kindlmDir = join(configDir, \".kindlm\");\n const io = createFileBaselineIO(kindlmDir);\n\n // Run tests\n const { config, runnerResult } = await runTests({\n configPath: options.config,\n runs: options.runs ? parseInt(options.runs, 10) : undefined,\n });\n\n const { aggregated } = runnerResult;\n\n // Build + write baseline\n const baselineData = buildBaselineData(\n config.suite.name,\n aggregated,\n new Date().toISOString(),\n );\n\n const writeResult = writeBaseline(baselineData, io);\n if (!writeResult.success) {\n console.error(chalk.red(`Failed to save baseline: ${writeResult.error.message}`));\n process.exit(1);\n }\n\n const testCount = Object.keys(baselineData.results).length;\n console.log(\"\");\n console.log(\n chalk.green(`Baseline saved for suite \"${config.suite.name}\" (${testCount} test${testCount === 1 ? \"\" : \"s\"})`),\n );\n console.log(chalk.dim(` Location: ${kindlmDir}/baselines/`));\n process.exit(0);\n } catch (e) {\n console.error(chalk.red(`Error: ${e instanceof Error ? e.message : String(e)}`));\n process.exit(1);\n }\n });\n\n baseline\n .command(\"compare\")\n .description(\"Compare latest against baseline\")\n .option(\"-c, --config <path>\", \"Path to config file\", \"kindlm.yaml\")\n .option(\"--runs <count>\", \"Override run count\")\n .action(async (options: BaselineCompareOptions) => {\n try {\n const configDir = dirname(resolve(process.cwd(), options.config));\n const kindlmDir = join(configDir, \".kindlm\");\n const io = createFileBaselineIO(kindlmDir);\n\n // We need the suite name from config before running tests.\n // Parse config minimally to get the suite name for baseline lookup.\n const configPath = resolve(process.cwd(), options.config);\n let yamlContent: string;\n try {\n yamlContent = readFileSync(configPath, \"utf-8\");\n } catch {\n console.error(chalk.red(`Config file not found: ${configPath}`));\n process.exit(1);\n }\n\n const fileReader = createNodeFileReader();\n const parseResult = parseConfig(yamlContent, {\n configDir,\n fileReader,\n });\n\n if (!parseResult.success) {\n console.error(chalk.red(`Config validation failed: ${parseResult.error.message}`));\n process.exit(1);\n }\n\n const suiteName = parseResult.data.suite.name;\n\n // Load baseline — fail fast if missing\n const baselineResult = readBaseline(suiteName, io);\n if (!baselineResult.success) {\n if (baselineResult.error.code === \"BASELINE_NOT_FOUND\") {\n console.error(chalk.red(`No baseline found for suite \"${suiteName}\". Run \\`kindlm baseline set\\` first.`));\n } else {\n console.error(chalk.red(`Failed to read baseline: ${baselineResult.error.message}`));\n }\n process.exit(1);\n }\n\n const baselineData = baselineResult.data;\n\n // Run tests with baseline injected for drift assertions\n const { runnerResult } = await runTests({\n configPath: options.config,\n runs: options.runs ? parseInt(options.runs, 10) : undefined,\n baselineData,\n });\n\n const { aggregated } = runnerResult;\n\n // Build current baseline data for comparison\n const currentData = buildBaselineData(\n suiteName,\n aggregated,\n new Date().toISOString(),\n );\n\n // Compare\n const comparison = compareBaseline(baselineData, currentData.results);\n\n // Print comparison report\n console.log(\"\");\n console.log(chalk.bold(`Baseline comparison for \"${suiteName}\"`));\n console.log(chalk.dim(` Baseline from: ${baselineData.createdAt}`));\n console.log(\"\");\n\n if (comparison.regressions.length > 0) {\n console.log(chalk.red.bold(` Regressions (${comparison.regressions.length}):`));\n for (const r of comparison.regressions) {\n console.log(chalk.red(` ${r.testName}: ${formatPercent(r.baselinePassRate)} → ${formatPercent(r.currentPassRate)}`));\n if (r.newFailureCodes.length > 0) {\n console.log(chalk.red(` New failures: ${r.newFailureCodes.join(\", \")}`));\n }\n }\n console.log(\"\");\n }\n\n if (comparison.improvements.length > 0) {\n console.log(chalk.green.bold(` Improvements (${comparison.improvements.length}):`));\n for (const imp of comparison.improvements) {\n console.log(chalk.green(` ${imp.testName}: ${formatPercent(imp.baselinePassRate)} → ${formatPercent(imp.currentPassRate)}`));\n }\n console.log(\"\");\n }\n\n if (comparison.unchanged.length > 0) {\n console.log(chalk.dim(` Unchanged (${comparison.unchanged.length}):`));\n for (const u of comparison.unchanged) {\n console.log(chalk.dim(` ${u.testName}: ${formatPercent(u.passRate)}`));\n }\n console.log(\"\");\n }\n\n if (comparison.newTests.length > 0) {\n console.log(chalk.cyan(` New tests (${comparison.newTests.length}):`));\n for (const t of comparison.newTests) {\n console.log(chalk.cyan(` ${t}`));\n }\n console.log(\"\");\n }\n\n if (comparison.removedTests.length > 0) {\n console.log(chalk.yellow(` Removed tests (${comparison.removedTests.length}):`));\n for (const t of comparison.removedTests) {\n console.log(chalk.yellow(` ${t}`));\n }\n console.log(\"\");\n }\n\n // Exit 1 if any regressions\n process.exit(comparison.regressions.length > 0 ? 1 : 0);\n } catch (e) {\n console.error(chalk.red(`Error: ${e instanceof Error ? e.message : String(e)}`));\n process.exit(1);\n }\n });\n\n baseline\n .command(\"list\")\n .description(\"List saved baselines\")\n .option(\"-c, --config <path>\", \"Path to config file\", \"kindlm.yaml\")\n .action((options: BaselineListOptions) => {\n try {\n const configDir = dirname(resolve(process.cwd(), options.config));\n const kindlmDir = join(configDir, \".kindlm\");\n const io = createFileBaselineIO(kindlmDir);\n\n const listResult = listBaselines(io);\n if (!listResult.success) {\n console.error(chalk.red(`Failed to list baselines: ${listResult.error.message}`));\n process.exit(1);\n }\n\n const names = listResult.data;\n\n if (names.length === 0) {\n console.log(chalk.dim(\"No baselines saved yet. Run `kindlm baseline set` to create one.\"));\n process.exit(0);\n }\n\n console.log(chalk.bold(\"Saved baselines:\"));\n console.log(\"\");\n\n for (const name of names) {\n const readResult = io.read(name);\n if (!readResult.success) {\n console.log(` ${name} ${chalk.dim(\"(unreadable)\")}`);\n continue;\n }\n\n const parsed = deserializeBaseline(readResult.data);\n if (!parsed.success) {\n console.log(` ${name} ${chalk.dim(\"(corrupt)\")}`);\n continue;\n }\n\n const testCount = Object.keys(parsed.data.results).length;\n console.log(` ${chalk.cyan(parsed.data.suiteName)} — ${testCount} test${testCount === 1 ? \"\" : \"s\"}, saved ${chalk.dim(parsed.data.createdAt)}`);\n }\n\n process.exit(0);\n } catch (e) {\n console.error(chalk.red(`Error: ${e instanceof Error ? e.message : String(e)}`));\n process.exit(1);\n }\n });\n}\n\nfunction formatPercent(rate: number): string {\n return `${(rate * 100).toFixed(1)}%`;\n}\n","import { readFileSync, writeFileSync, mkdirSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { BaselineIO } from \"@kindlm/core\";\nimport type { Result } from \"@kindlm/core\";\n\nfunction sanitizeFilename(name: string): string {\n return name.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\nexport function createFileBaselineIO(kindlmDir: string): BaselineIO {\n const baselinesDir = join(kindlmDir, \"baselines\");\n\n return {\n read(suiteName: string): Result<string> {\n const filePath = join(baselinesDir, `${sanitizeFilename(suiteName)}.json`);\n try {\n const content = readFileSync(filePath, \"utf-8\");\n return { success: true, data: content };\n } catch {\n return {\n success: false,\n error: {\n code: \"BASELINE_NOT_FOUND\",\n message: `No baseline found for suite \"${suiteName}\" at ${filePath}`,\n },\n };\n }\n },\n\n write(suiteName: string, content: string): Result<void> {\n try {\n mkdirSync(baselinesDir, { recursive: true });\n const filePath = join(baselinesDir, `${sanitizeFilename(suiteName)}.json`);\n writeFileSync(filePath, content, \"utf-8\");\n return { success: true, data: undefined };\n } catch (e) {\n return {\n success: false,\n error: {\n code: \"UNKNOWN_ERROR\",\n message: `Failed to write baseline: ${e instanceof Error ? e.message : String(e)}`,\n },\n };\n }\n },\n\n list(): Result<string[]> {\n try {\n mkdirSync(baselinesDir, { recursive: true });\n const files = readdirSync(baselinesDir);\n const names = files\n .filter((f) => f.endsWith(\".json\"))\n .map((f) => f.replace(/\\.json$/, \"\"));\n return { success: true, data: names };\n } catch (e) {\n return {\n success: false,\n error: {\n code: \"UNKNOWN_ERROR\",\n message: `Failed to list baselines: ${e instanceof Error ? e.message : String(e)}`,\n },\n };\n }\n },\n };\n}\n","/* eslint-disable no-console */\nimport type { Command } from \"commander\";\nimport { createInterface } from \"node:readline\";\nimport { Writable } from \"node:stream\";\nimport chalk from \"chalk\";\nimport { loadToken, saveToken, clearToken } from \"../cloud/auth.js\";\nimport { createCloudClient, getCloudUrl, CloudApiError } from \"../cloud/client.js\";\n\ninterface LoginOptions {\n token?: string;\n status?: boolean;\n logout?: boolean;\n}\n\nexport function registerLoginCommand(program: Command): void {\n program\n .command(\"login\")\n .description(\"Authenticate with KindLM Cloud\")\n .option(\"-t, --token <token>\", \"API token (skips interactive prompt)\")\n .option(\"--status\", \"Show current authentication status\")\n .option(\"--logout\", \"Remove stored credentials\")\n .action(async (options: LoginOptions) => {\n try {\n if (options.logout) {\n clearToken();\n console.log(chalk.green(\"Logged out. Credentials removed.\"));\n return;\n }\n\n if (options.status) {\n await showStatus();\n return;\n }\n\n const token = options.token ?? process.env[\"KINDLM_API_TOKEN\"] ?? (await promptForToken());\n\n if (!token.startsWith(\"klm_\")) {\n console.error(chalk.red(\"Invalid token format. KindLM tokens start with \\\"klm_\\\".\"));\n process.exit(1);\n }\n\n // Validate token against Cloud API\n const client = createCloudClient(getCloudUrl(), token);\n try {\n await client.get(\"/v1/auth/tokens\");\n } catch (e) {\n if (e instanceof CloudApiError && e.status === 401) {\n console.error(chalk.red(\"Invalid or expired token.\"));\n process.exit(1);\n }\n throw e;\n }\n\n saveToken(token);\n console.log(chalk.green(\"Authenticated successfully. Token saved.\"));\n } catch (e) {\n console.error(chalk.red(`Login failed: ${e instanceof Error ? e.message : String(e)}`));\n process.exit(1);\n }\n });\n}\n\nasync function showStatus(): Promise<void> {\n const token = loadToken();\n if (!token) {\n console.log(chalk.yellow(\"Not authenticated. Run \\\"kindlm login\\\" to authenticate.\"));\n return;\n }\n\n const client = createCloudClient(getCloudUrl(), token);\n try {\n await client.get(\"/v1/auth/tokens\");\n console.log(chalk.green(\"Authenticated.\"));\n console.log(` Cloud URL: ${getCloudUrl()}`);\n } catch (e) {\n if (e instanceof CloudApiError && e.status === 401) {\n console.log(chalk.yellow(\"Stored token is invalid or expired. Run \\\"kindlm login\\\" to re-authenticate.\"));\n } else {\n console.log(chalk.yellow(`Cannot reach Cloud API: ${e instanceof Error ? e.message : String(e)}`));\n }\n }\n}\n\nfunction promptForToken(): Promise<string> {\n return new Promise((resolve, reject) => {\n // Muted output stream suppresses echo of typed characters\n const muted = new Writable({\n write(_chunk, _encoding, callback) {\n callback();\n },\n });\n\n process.stderr.write(\"Paste your KindLM API token: \");\n const rl = createInterface({ input: process.stdin, output: muted, terminal: true });\n\n rl.question(\"\", (answer) => {\n rl.close();\n process.stderr.write(\"\\n\");\n const trimmed = answer.trim();\n if (!trimmed) {\n reject(new Error(\"No token provided\"));\n return;\n }\n resolve(trimmed);\n });\n });\n}\n","import { readFileSync, writeFileSync, mkdirSync, unlinkSync, chmodSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\ninterface CredentialsFile {\n token: string;\n savedAt: string;\n}\n\nexport function getCredentialsPath(): string {\n return join(homedir(), \".kindlm\", \"credentials\");\n}\n\nexport function loadToken(): string | null {\n try {\n const raw = readFileSync(getCredentialsPath(), \"utf-8\");\n const parsed = JSON.parse(raw) as Partial<CredentialsFile>;\n if (typeof parsed.token === \"string\" && parsed.token.length > 0) {\n return parsed.token;\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport function saveToken(token: string): void {\n const filePath = getCredentialsPath();\n const dir = join(homedir(), \".kindlm\");\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n const data: CredentialsFile = { token, savedAt: new Date().toISOString() };\n writeFileSync(filePath, JSON.stringify(data, null, 2), { mode: 0o600 });\n chmodSync(filePath, 0o600);\n}\n\nexport function clearToken(): void {\n try {\n unlinkSync(getCredentialsPath());\n } catch {\n // File doesn't exist — nothing to clear\n }\n}\n","const DEFAULT_CLOUD_URL = \"https://api.kindlm.com\";\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst RETRY_DELAY_MS = 1_000;\nconst MAX_RETRIES = 1;\n\nexport class CloudApiError extends Error {\n readonly status: number;\n\n constructor(status: number, message: string) {\n super(message);\n this.name = \"CloudApiError\";\n this.status = status;\n }\n}\n\nexport interface CloudClient {\n baseUrl: string;\n get<T>(path: string): Promise<T>;\n post<T>(path: string, body: unknown): Promise<T>;\n patch<T>(path: string, body: unknown): Promise<T>;\n delete(path: string): Promise<void>;\n}\n\nexport function getCloudUrl(): string {\n const url = process.env[\"KINDLM_CLOUD_URL\"] ?? DEFAULT_CLOUD_URL;\n if (url.startsWith(\"http://\") && !isLocalhost(url)) {\n throw new Error(\n `Refusing to use insecure HTTP for Cloud API: ${url}. Use HTTPS or target localhost for development.`,\n );\n }\n return url;\n}\n\nfunction isLocalhost(url: string): boolean {\n try {\n const parsed = new URL(url);\n return parsed.hostname === \"localhost\" || parsed.hostname === \"127.0.0.1\" || parsed.hostname === \"::1\";\n } catch {\n return false;\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function createCloudClient(baseUrl: string, token: string): CloudClient {\n async function request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const url = `${baseUrl}${path}`;\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n };\n\n const init: RequestInit = { method, headers };\n if (body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n init.body = JSON.stringify(body);\n }\n\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n if (attempt > 0) {\n await sleep(RETRY_DELAY_MS);\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);\n init.signal = controller.signal;\n\n try {\n const response = await fetch(url, init);\n\n if (!response.ok) {\n // Retry on 5xx\n if (response.status >= 500 && attempt < MAX_RETRIES) {\n lastError = new CloudApiError(response.status, `HTTP ${response.status}`);\n continue;\n }\n\n let message = `HTTP ${response.status}`;\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (contentType.includes(\"application/json\")) {\n try {\n const errorBody = (await response.json()) as { error?: string };\n if (errorBody.error) {\n message = errorBody.error;\n }\n } catch {\n // ignore parse errors\n }\n }\n throw new CloudApiError(response.status, message);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n // Validate response content-type is JSON before parsing\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (!contentType.includes(\"application/json\")) {\n throw new CloudApiError(\n response.status,\n `Expected JSON response but got content-type: ${contentType}`,\n );\n }\n\n return (await response.json()) as T;\n } catch (error) {\n if (error instanceof CloudApiError) throw error;\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Retry on network errors (not CloudApiErrors)\n if (attempt < MAX_RETRIES) continue;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n throw lastError ?? new Error(\"Request failed\");\n }\n\n return {\n baseUrl,\n get: <T>(path: string) => request<T>(\"GET\", path),\n post: <T>(path: string, body: unknown) => request<T>(\"POST\", path, body),\n patch: <T>(path: string, body: unknown) => request<T>(\"PATCH\", path, body),\n delete: (path: string) => request<void>(\"DELETE\", path),\n };\n}\n","/* eslint-disable no-console */\nimport type { Command } from \"commander\";\nimport { basename } from \"node:path\";\nimport { execSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { loadToken } from \"../cloud/auth.js\";\nimport { createCloudClient, getCloudUrl } from \"../cloud/client.js\";\nimport { loadLastRun } from \"../utils/last-run.js\";\nimport { getGitInfo } from \"../utils/git.js\";\nimport { detectCI } from \"../utils/env.js\";\nimport { uploadResults } from \"../cloud/upload.js\";\nimport { createSpinner } from \"../utils/spinner.js\";\n\ninterface UploadCommandOptions {\n token?: string;\n project?: string;\n}\n\nexport function registerUploadCommand(program: Command): void {\n program\n .command(\"upload\")\n .description(\"Push last run results to KindLM Cloud\")\n .option(\"-t, --token <token>\", \"API token (overrides stored token)\")\n .option(\"-p, --project <name>\", \"Project name\")\n .action(async (options: UploadCommandOptions) => {\n try {\n const token = options.token ?? process.env[\"KINDLM_API_TOKEN\"] ?? loadToken();\n if (!token) {\n console.error(chalk.red(\"Not authenticated. Run \\\"kindlm login\\\" first or pass --token.\"));\n process.exit(1);\n }\n\n const lastRun = loadLastRun();\n if (!lastRun) {\n console.error(chalk.red(\"No test run found. Run \\\"kindlm test\\\" first.\"));\n process.exit(1);\n }\n\n const gitInfo = getGitInfo();\n const ciEnv = detectCI();\n const projectName = options.project ?? resolveProjectName();\n\n const client = createCloudClient(getCloudUrl(), token);\n const spinner = createSpinner();\n spinner.start(\"Uploading results to KindLM Cloud...\");\n\n try {\n const result = await uploadResults(client, lastRun.runnerResult, {\n projectName,\n suiteName: lastRun.suiteName,\n configHash: lastRun.configHash,\n commitSha: ciEnv.commitSha ?? gitInfo.commitSha ?? undefined,\n branch: ciEnv.branch ?? gitInfo.branch ?? undefined,\n environment: ciEnv.isCI ? \"ci\" : \"local\",\n triggeredBy: ciEnv.name ?? \"local\",\n });\n\n spinner.succeed(\"Uploaded successfully.\");\n console.log(` Run ID: ${result.runId}`);\n console.log(` Project: ${projectName}`);\n console.log(` Suite: ${lastRun.suiteName}`);\n } catch (e) {\n spinner.fail(\"Upload failed.\");\n throw e;\n }\n } catch (e) {\n console.error(chalk.red(`Upload failed: ${e instanceof Error ? e.message : String(e)}`));\n process.exit(1);\n }\n });\n}\n\nfunction extractRepoName(remoteUrl: string): string | null {\n // HTTPS: https://github.com/org/repo.git\n try {\n const parsed = new URL(remoteUrl);\n const segments = parsed.pathname.split(\"/\").filter(Boolean);\n const last = segments[segments.length - 1];\n if (last) return last.replace(/\\.git$/, \"\");\n } catch {\n // Not a standard URL — try SSH format\n }\n\n // SSH: git@github.com:org/repo.git\n const sshMatch = remoteUrl.match(/^[\\w.-]+@[\\w.-]+:(.+?)(?:\\.git)?$/);\n if (sshMatch?.[1]) {\n const segments = sshMatch[1].split(\"/\");\n return segments[segments.length - 1] ?? null;\n }\n\n return null;\n}\n\nfunction resolveProjectName(): string {\n try {\n const remote = execSync(\"git remote get-url origin\", { encoding: \"utf-8\" }).trim();\n const name = extractRepoName(remote);\n if (name) return name;\n } catch {\n // Not in a git repo or no remote\n }\n return basename(process.cwd());\n}\n","import { execSync } from \"node:child_process\";\n\nexport interface GitInfo {\n commitSha: string | null;\n branch: string | null;\n dirty: boolean;\n}\n\nexport function getGitInfo(): GitInfo {\n try {\n const commitSha = execSync(\"git rev-parse HEAD\", { encoding: \"utf-8\" }).trim() || null;\n const branch = execSync(\"git rev-parse --abbrev-ref HEAD\", { encoding: \"utf-8\" }).trim() || null;\n const status = execSync(\"git status --porcelain\", { encoding: \"utf-8\" }).trim();\n const dirty = status.length > 0;\n return { commitSha, branch, dirty };\n } catch {\n return { commitSha: null, branch: null, dirty: false };\n }\n}\n","export interface CIEnvironment {\n name: string | null;\n isCI: boolean;\n commitSha: string | null;\n branch: string | null;\n}\n\nexport function detectCI(): CIEnvironment {\n if (process.env[\"GITHUB_ACTIONS\"]) {\n return {\n name: \"github_actions\",\n isCI: true,\n commitSha: process.env[\"GITHUB_SHA\"] ?? null,\n branch: process.env[\"GITHUB_REF_NAME\"] ?? null,\n };\n }\n\n if (process.env[\"GITLAB_CI\"]) {\n return {\n name: \"gitlab_ci\",\n isCI: true,\n commitSha: process.env[\"CI_COMMIT_SHA\"] ?? null,\n branch: process.env[\"CI_COMMIT_BRANCH\"] ?? null,\n };\n }\n\n if (process.env[\"CI\"]) {\n return {\n name: null,\n isCI: true,\n commitSha: null,\n branch: null,\n };\n }\n\n return {\n name: null,\n isCI: false,\n commitSha: null,\n branch: null,\n };\n}\n","import type { CloudClient } from \"./client.js\";\nimport type { RunnerResult } from \"@kindlm/core\";\nimport type { AggregatedTestResult } from \"@kindlm/core\";\n\nfunction e(segment: string): string {\n return encodeURIComponent(segment);\n}\n\nexport interface UploadOptions {\n projectName: string;\n suiteName: string;\n configHash: string;\n commitSha?: string;\n branch?: string;\n environment?: string;\n triggeredBy?: string;\n}\n\ninterface CloudProject {\n id: string;\n name: string;\n}\n\ninterface CloudSuite {\n id: string;\n name: string;\n}\n\ninterface CloudRun {\n id: string;\n}\n\nexport interface UploadResult {\n runId: string;\n projectId: string;\n}\n\nexport async function uploadResults(\n client: CloudClient,\n runnerResult: RunnerResult,\n options: UploadOptions,\n): Promise<UploadResult> {\n // 1. Find or create project\n const projectId = await findOrCreateProject(client, options.projectName);\n\n // 2. Find or create suite\n const suiteId = await findOrCreateSuite(client, projectId, options.suiteName, options.configHash);\n\n // 3. Create run\n const run = await client.post<CloudRun>(`/v1/runs/${e(projectId)}/runs`, {\n suiteId,\n commitSha: options.commitSha,\n branch: options.branch,\n environment: options.environment,\n triggeredBy: options.triggeredBy,\n });\n\n // 4. Batch insert results (chunks of 50 to avoid payload limits)\n const results = mapAggregatedResults(runnerResult.aggregated);\n const BATCH_SIZE = 50;\n for (let i = 0; i < results.length; i += BATCH_SIZE) {\n const batch = results.slice(i, i + BATCH_SIZE);\n await client.post(`/v1/results/${e(run.id)}/results`, { results: batch });\n }\n\n // 5. Compute run-level metrics and finalize\n const { runResult } = runnerResult;\n const passRate = runResult.totalTests > 0\n ? runResult.passed / runResult.totalTests\n : 0;\n\n const modelIds = new Set(runnerResult.aggregated.map((a) => a.modelId));\n const judgeScores = runnerResult.aggregated\n .map((a) => a.assertionScores[\"judge\"]?.mean)\n .filter((s): s is number => s !== undefined);\n const judgeAvgScore = judgeScores.length > 0\n ? judgeScores.reduce((a, b) => a + b, 0) / judgeScores.length\n : undefined;\n\n const latencies = runnerResult.aggregated.map((a) => a.latencyAvgMs);\n const latencyAvgMs = latencies.length > 0\n ? latencies.reduce((a, b) => a + b, 0) / latencies.length\n : undefined;\n\n const totalCost = runnerResult.aggregated.reduce((sum, a) => sum + a.totalCostUsd, 0);\n const costEstimateUsd = totalCost > 0 ? totalCost : undefined;\n\n await client.patch(`/v1/runs/${e(run.id)}`, {\n status: \"completed\",\n passRate,\n testCount: runResult.totalTests,\n modelCount: modelIds.size,\n judgeAvgScore,\n latencyAvgMs,\n costEstimateUsd,\n finishedAt: new Date().toISOString(),\n });\n\n return { runId: run.id, projectId };\n}\n\nasync function findOrCreateProject(client: CloudClient, name: string): Promise<string> {\n const { projects } = await client.get<{ projects: CloudProject[] }>(\"/v1/projects\");\n const existing = projects.find((p) => p.name === name);\n if (existing) return existing.id;\n\n const created = await client.post<CloudProject>(\"/v1/projects\", { name });\n return created.id;\n}\n\nasync function findOrCreateSuite(\n client: CloudClient,\n projectId: string,\n name: string,\n configHash: string,\n): Promise<string> {\n const { suites } = await client.get<{ suites: CloudSuite[] }>(\n `/v1/suites/${e(projectId)}/suites`,\n );\n const existing = suites.find((s) => s.name === name);\n if (existing) return existing.id;\n\n const created = await client.post<CloudSuite>(`/v1/suites/${e(projectId)}/suites`, {\n name,\n configHash,\n });\n return created.id;\n}\n\ninterface CloudTestResult {\n testCaseName: string;\n modelId: string;\n passed: number;\n passRate: number;\n runCount: number;\n judgeAvg: number | null;\n driftScore: number | null;\n latencyAvgMs: number | null;\n costUsd: number | null;\n totalTokens: number | null;\n failureCodes: string | null;\n failureMessages: string | null;\n assertionScores: string | null;\n}\n\nexport function mapAggregatedResults(aggregated: AggregatedTestResult[]): CloudTestResult[] {\n return aggregated.map((agg) => {\n // Extract failure messages from runs, or use pre-computed value from cache\n const cached = agg as AggregatedTestResult & { failureMessages?: string[] };\n const failureMessages = agg.runs.length > 0\n ? agg.runs\n .flatMap((r) => r.assertions.filter((a) => !a.passed).map((a) => a.failureMessage))\n .filter((m): m is string => m !== undefined)\n : (cached.failureMessages ?? []);\n\n return {\n testCaseName: agg.testCaseName,\n modelId: agg.modelId,\n passed: agg.passed ? 1 : 0,\n passRate: agg.passRate,\n runCount: agg.runCount,\n judgeAvg: agg.assertionScores[\"judge\"]?.mean ?? null,\n driftScore: agg.assertionScores[\"drift\"]?.mean ?? null,\n latencyAvgMs: agg.latencyAvgMs ?? null,\n costUsd: agg.totalCostUsd ?? null,\n totalTokens: agg.totalTokens ?? null,\n failureCodes: agg.failureCodes.length > 0 ? JSON.stringify(agg.failureCodes) : null,\n failureMessages: failureMessages.length > 0 ? JSON.stringify(failureMessages) : null,\n assertionScores: Object.keys(agg.assertionScores).length > 0\n ? JSON.stringify(agg.assertionScores)\n : null,\n };\n });\n}\n","/* eslint-disable no-console */\nimport { readFileSync, statSync } from \"node:fs\";\nimport { resolve, dirname } from \"node:path\";\nimport { spawn } from \"node:child_process\";\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport {\n parseConfig,\n createProvider,\n filterSpans,\n mapSpansToResult,\n buildContextFromTrace,\n createAssertionsFromExpect,\n evaluateGates,\n} from \"@kindlm/core\";\nimport type {\n KindLMConfig,\n ProviderAdapter,\n TraceConfig,\n AssertionResult,\n} from \"@kindlm/core\";\nimport { createTraceServer } from \"../utils/trace-server.js\";\nimport { createSpinner } from \"../utils/spinner.js\";\nimport { createNodeFileReader } from \"../utils/file-reader.js\";\nimport { createHttpClient } from \"../utils/http.js\";\n\nexport function registerTraceCommand(program: Command): void {\n program\n .command(\"trace\")\n .description(\"Ingest OpenTelemetry traces and run assertions against them\")\n .option(\"-c, --config <path>\", \"Config file path\", \"kindlm.yaml\")\n .option(\"--port <port>\", \"OTLP HTTP port\", \"4318\")\n .option(\"--command <cmd>\", \"Command to spawn (traces are collected while it runs)\")\n .option(\"--timeout <ms>\", \"Timeout in ms to wait for traces\", \"30000\")\n .option(\"--reporter <type>\", \"Report format: pretty, json, junit\", \"pretty\")\n .action(async (opts: {\n config: string;\n port: string;\n command?: string;\n timeout: string;\n reporter: string;\n }) => {\n const spinner = createSpinner();\n\n try {\n // 1. Read and parse config\n const configPath = resolve(process.cwd(), opts.config);\n const configDir = dirname(configPath);\n\n try {\n const stat = statSync(configPath);\n if (stat.size > 1_048_576) {\n console.error(chalk.red(\"Config file exceeds 1MB limit\"));\n process.exit(1);\n }\n } catch {\n console.error(chalk.red(`Config file not found: ${configPath}`));\n process.exit(1);\n }\n\n let yamlContent: string;\n try {\n yamlContent = readFileSync(configPath, \"utf-8\");\n } catch {\n console.error(chalk.red(`Config file not found: ${configPath}`));\n process.exit(1);\n }\n\n const fileReader = createNodeFileReader();\n const parseResult = parseConfig(yamlContent, { configDir, fileReader });\n if (!parseResult.success) {\n console.error(chalk.red(`Config validation failed: ${parseResult.error.message}`));\n process.exit(1);\n }\n\n const config: KindLMConfig = parseResult.data;\n const traceConfig: TraceConfig = config.trace ?? {\n port: parseInt(opts.port, 10),\n timeoutMs: parseInt(opts.timeout, 10),\n spanMapping: {\n outputTextAttr: \"gen_ai.completion.0.content\",\n modelAttr: \"gen_ai.response.model\",\n systemAttr: \"gen_ai.system\",\n inputTokensAttr: \"gen_ai.usage.input_tokens\",\n outputTokensAttr: \"gen_ai.usage.output_tokens\",\n },\n };\n\n const port = parseInt(opts.port, 10) || traceConfig.port;\n const timeoutMs = parseInt(opts.timeout, 10) || traceConfig.timeoutMs;\n\n // 2. Start trace server\n const traceServer = createTraceServer(port);\n await traceServer.start();\n spinner.start(`Listening for OTLP traces on port ${port}...`);\n\n // 3. Optionally spawn command\n if (opts.command) {\n const child = spawn(\"sh\", [\"-c\", opts.command], {\n cwd: configDir,\n env: {\n ...process.env,\n OTEL_EXPORTER_OTLP_ENDPOINT: `http://localhost:${port}`,\n OTEL_EXPORTER_OTLP_PROTOCOL: \"http/json\",\n },\n stdio: \"inherit\",\n });\n\n child.on(\"error\", (e) => {\n spinner.fail(`Command failed: ${e.message}`);\n });\n }\n\n // 4. Wait for traces\n const spans = await traceServer.waitForSpans({ timeoutMs });\n await traceServer.stop();\n\n if (spans.length === 0) {\n spinner.fail(\"No traces received\");\n process.exit(1);\n }\n\n spinner.succeed(`Received ${spans.length} spans`);\n\n // 5. Filter and map spans\n const filtered = filterSpans(spans, traceConfig.spanFilter);\n const mappingResult = mapSpansToResult(filtered, traceConfig.spanMapping);\n\n // 6. Resolve judge adapter if needed\n const httpClient = createHttpClient();\n const adapters = new Map<string, ProviderAdapter>();\n const providers = config.providers as Record<string, { apiKeyEnv?: string; baseUrl?: string; organization?: string } | undefined>;\n\n for (const [name, providerConfig] of Object.entries(providers)) {\n if (!providerConfig) continue;\n let apiKey = \"\";\n if (providerConfig.apiKeyEnv) {\n const key = process.env[providerConfig.apiKeyEnv];\n if (key) apiKey = key.trim();\n }\n if (!apiKey && name !== \"ollama\") continue;\n\n try {\n const adapter = createProvider(name, httpClient);\n await adapter.initialize({\n apiKey,\n baseUrl: providerConfig.baseUrl,\n organization: providerConfig.organization,\n timeoutMs: config.defaults.timeoutMs,\n maxRetries: 2,\n });\n adapters.set(name, adapter);\n } catch {\n // Skip providers that fail to initialize\n }\n }\n\n const judgeModelId = config.defaults.judgeModel ?? config.models[0]?.id;\n const judgeModelConfig = config.models.find((m) => m.id === judgeModelId);\n const judgeAdapter = judgeModelConfig ? adapters.get(judgeModelConfig.provider) : undefined;\n\n // 7. Build context and evaluate assertions for each test\n const context = buildContextFromTrace(mappingResult, {\n configDir,\n judgeAdapter,\n judgeModel: judgeModelConfig?.model,\n });\n\n const testResults: { testName: string; assertions: AssertionResult[] }[] = [];\n\n for (const test of config.tests) {\n if (test.skip) continue;\n\n const assertions = createAssertionsFromExpect(test.expect);\n const allResults: AssertionResult[] = [];\n for (const assertion of assertions) {\n const results = await assertion.evaluate(context);\n allResults.push(...results);\n }\n testResults.push({ testName: test.name, assertions: allResults });\n }\n\n // 8. Report results\n const totalAssertions = testResults.reduce((s, t) => s + t.assertions.length, 0);\n const passedAssertions = testResults.reduce(\n (s, t) => s + t.assertions.filter((a) => a.passed).length,\n 0,\n );\n const failedAssertions = totalAssertions - passedAssertions;\n\n console.log();\n console.log(chalk.bold(\"Trace Test Results\"));\n console.log(chalk.dim(\"─\".repeat(50)));\n\n for (const { testName, assertions } of testResults) {\n const allPassed = assertions.every((a) => a.passed);\n const icon = allPassed ? chalk.green(\"✓\") : chalk.red(\"✗\");\n console.log(`${icon} ${testName}`);\n\n for (const a of assertions) {\n const aIcon = a.passed ? chalk.green(\" ✓\") : chalk.red(\" ✗\");\n const label = a.failureMessage ? `${a.label}: ${a.failureMessage}` : a.label;\n console.log(`${aIcon} ${label}`);\n }\n }\n\n console.log();\n console.log(\n `${chalk.bold(\"Total:\")} ${passedAssertions} passed, ${failedAssertions} failed out of ${totalAssertions} assertions`,\n );\n\n // 9. Evaluate gates\n // Build minimal aggregated results for gate evaluation\n const gateEval = evaluateGates(config.gates, []);\n if (!gateEval.passed) {\n for (const g of gateEval.gates.filter((g) => !g.passed)) {\n console.log(chalk.red(`Gate failed: ${g.message}`));\n }\n }\n\n // 10. Exit code\n process.exit(failedAssertions > 0 ? 1 : 0);\n } catch (e) {\n spinner.fail(`Trace command failed: ${e instanceof Error ? e.message : String(e)}`);\n process.exit(1);\n }\n });\n}\n","import { createServer } from \"node:http\";\nimport type { Server, IncomingMessage, ServerResponse } from \"node:http\";\nimport { parseOtlpPayload } from \"@kindlm/core\";\nimport type { ParsedSpan } from \"@kindlm/core\";\n\nexport interface TraceServer {\n start(): Promise<void>;\n stop(): Promise<void>;\n getSpans(): ParsedSpan[];\n waitForSpans(options: { timeoutMs: number }): Promise<ParsedSpan[]>;\n}\n\nexport function createTraceServer(port: number): TraceServer {\n const collectedSpans: ParsedSpan[] = [];\n let server: Server | null = null;\n let spanListeners: (() => void)[] = [];\n\n function notifyListeners() {\n for (const listener of spanListeners) {\n listener();\n }\n }\n\n function handleRequest(req: IncomingMessage, res: ServerResponse) {\n // CORS headers\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"POST, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type\");\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (req.method !== \"POST\" || req.url !== \"/v1/traces\") {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n return;\n }\n\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => {\n try {\n const body = Buffer.concat(chunks).toString(\"utf-8\");\n const parsed = JSON.parse(body) as unknown;\n const result = parseOtlpPayload(parsed);\n\n if (result.success) {\n collectedSpans.push(...result.data);\n notifyListeners();\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ partialSuccess: {} }));\n } else {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: result.error.message }));\n }\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid JSON\" }));\n }\n });\n }\n\n return {\n start() {\n return new Promise<void>((resolve, reject) => {\n server = createServer(handleRequest);\n server.on(\"error\", reject);\n server.listen(port, () => resolve());\n });\n },\n\n stop() {\n return new Promise<void>((resolve) => {\n if (server) {\n server.close(() => resolve());\n } else {\n resolve();\n }\n });\n },\n\n getSpans() {\n return [...collectedSpans];\n },\n\n waitForSpans({ timeoutMs }) {\n return new Promise<ParsedSpan[]>((resolve) => {\n if (collectedSpans.length > 0) {\n resolve([...collectedSpans]);\n return;\n }\n\n const timer = setTimeout(() => {\n spanListeners = spanListeners.filter((l) => l !== onSpan);\n resolve([...collectedSpans]);\n }, timeoutMs);\n\n const onSpan = () => {\n clearTimeout(timer);\n spanListeners = spanListeners.filter((l) => l !== onSpan);\n // Wait a short period for any remaining spans to arrive\n setTimeout(() => resolve([...collectedSpans]), 500);\n };\n spanListeners.push(onSpan);\n });\n },\n };\n}\n"],"mappings":"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","names":["index_exports","__export","createProgram","__toCommonJS","import_commander","import_node_fs","import_node_path","import_chalk","TEMPLATE","registerInitCommand","program","options","filePath","chalk","import_node_fs","import_node_path","import_chalk","import_core","import_node_fs","createNodeFileReader","path","e","registerValidateCommand","program","options","configPath","configDir","yamlContent","chalk","fileReader","createNodeFileReader","result","details","e","config","import_chalk","import_core","import_node_fs","import_node_path","import_chalk","import_core","createHttpClient","url","init","controller","timeoutId","response","import_ora","createSpinner","instance","text","ora","import_node_child_process","import_core","createNodeCommandExecutor","command","options","resolve","child","stdoutChunks","stderrChunks","chunk","timer","code","signal","e","MAX_CONFIG_SIZE","runTests","options","spinner","createSpinner","interrupted","sigintHandler","chalk","runTestsInner","configPath","configDir","stat","yamlContent","fileReader","createNodeFileReader","parseResult","config","httpClient","createHttpClient","adapters","providers","name","providerConfig","apiKey","key","adapter","cause","msg","completedTests","totalTests","countExecutionUnits","onProgress","event","commandExecutor","t","createNodeCommandExecutor","runResult","count","test","repeat","modelCount","import_node_fs","import_node_path","import_node_crypto","getLastRunPath","saveLastRun","data","filePath","dir","slimmed","agg","failureMessages","r","a","m","loadLastRun","raw","parsed","computeConfigHash","yamlContent","import_pdfkit","import_node_fs","import_promises","import_node_path","parseSections","markdown","lines","sections","currentHeading","currentLevel","currentBody","line","headingMatch","extractTitle","extractHash","headingSize","level","parseTable","headerLine","separatorLine","parseCells","c","header","rows","row","ensureSpace","doc","needed","bottomMargin","renderPageHeader","timestamp","renderPageFooter","renderTable","table","contentWidth","colCount","colWidth","leftMargin","rowHeight","drawRow","cells","bold","bgColor","y","i","x","renderCompliancePdf","markdownContent","outputPath","resolve","reject","PDFDocument","stream","title","hash","section","size","bodyLines","inCodeBlock","codeY","nextLine","tableLines","j","subheadingMatch","registerTestCommand","program","options","runnerResult","config","yamlContent","runTests","result","aggregated","gateEvaluation","report","selectReporter","complianceReport","pdfPath","renderCompliancePdf","chalk","saveLastRun","computeConfigHash","allPassed","e","prefix","isKindlmError","label","chalkColorize","t","type","import_node_path","import_node_fs","import_chalk","import_core","import_node_fs","import_node_path","sanitizeFilename","name","createFileBaselineIO","kindlmDir","baselinesDir","suiteName","filePath","content","e","f","registerBaselineCommand","program","baseline","options","configDir","kindlmDir","io","createFileBaselineIO","config","runnerResult","runTests","aggregated","baselineData","writeResult","chalk","testCount","e","configPath","yamlContent","fileReader","createNodeFileReader","parseResult","suiteName","baselineResult","currentData","comparison","r","formatPercent","imp","u","t","listResult","names","name","readResult","parsed","rate","import_node_readline","import_node_stream","import_chalk","import_node_fs","import_node_path","import_node_os","getCredentialsPath","loadToken","raw","parsed","saveToken","token","filePath","dir","data","clearToken","DEFAULT_CLOUD_URL","CloudApiError","status","message","getCloudUrl","url","DEFAULT_CLOUD_URL","isLocalhost","parsed","sleep","ms","resolve","createCloudClient","baseUrl","token","request","method","path","body","headers","init","lastError","attempt","controller","timeoutId","response","errorBody","contentType","error","registerLoginCommand","program","options","clearToken","chalk","showStatus","token","promptForToken","client","createCloudClient","getCloudUrl","e","CloudApiError","saveToken","loadToken","resolve","reject","muted","_chunk","_encoding","callback","rl","answer","trimmed","import_node_path","import_node_child_process","import_chalk","import_node_child_process","getGitInfo","commitSha","branch","dirty","detectCI","e","segment","uploadResults","client","runnerResult","options","projectId","findOrCreateProject","suiteId","findOrCreateSuite","run","results","mapAggregatedResults","BATCH_SIZE","i","batch","runResult","passRate","modelIds","a","judgeScores","s","judgeAvgScore","b","latencies","latencyAvgMs","totalCost","sum","costEstimateUsd","name","projects","existing","p","configHash","suites","aggregated","agg","cached","failureMessages","r","m","registerUploadCommand","program","options","token","loadToken","chalk","lastRun","loadLastRun","gitInfo","getGitInfo","ciEnv","detectCI","projectName","resolveProjectName","client","createCloudClient","getCloudUrl","spinner","createSpinner","result","uploadResults","e","extractRepoName","remoteUrl","segments","last","sshMatch","remote","name","import_node_fs","import_node_path","import_node_child_process","import_chalk","import_core","import_node_http","import_core","createTraceServer","port","collectedSpans","server","spanListeners","notifyListeners","listener","handleRequest","req","res","chunks","chunk","body","parsed","result","resolve","reject","timeoutMs","timer","l","onSpan","registerTraceCommand","program","opts","spinner","createSpinner","configPath","configDir","chalk","yamlContent","fileReader","createNodeFileReader","parseResult","config","traceConfig","port","timeoutMs","traceServer","createTraceServer","e","spans","filtered","mappingResult","httpClient","createHttpClient","adapters","providers","name","providerConfig","apiKey","key","adapter","judgeModelId","judgeModelConfig","m","judgeAdapter","context","testResults","test","assertions","allResults","assertion","results","totalAssertions","s","t","passedAssertions","a","failedAssertions","testName","icon","aIcon","label","gateEval","g","createProgram","program","registerInitCommand","registerValidateCommand","registerTestCommand","registerBaselineCommand","registerLoginCommand","registerUploadCommand","registerTraceCommand"]}
@@ -0,0 +1,5 @@
1
+ import { Command } from 'commander';
2
+
3
+ declare function createProgram(): Command;
4
+
5
+ export { createProgram };
@@ -0,0 +1,5 @@
1
+ import { Command } from 'commander';
2
+
3
+ declare function createProgram(): Command;
4
+
5
+ export { createProgram };
package/dist/index.js ADDED
@@ -0,0 +1,52 @@
1
+ import{Command as hn}from"commander";import{existsSync as Le,writeFileSync as _e}from"fs";import{resolve as De}from"path";import H from"chalk";var je=`kindlm: 1
2
+ project: my-project
3
+
4
+ suite:
5
+ name: my-agent-tests
6
+ description: Behavioral tests for my AI agent
7
+
8
+ providers:
9
+ openai:
10
+ apiKeyEnv: OPENAI_API_KEY
11
+
12
+ models:
13
+ - id: gpt-4o
14
+ provider: openai
15
+ model: gpt-4o
16
+ params:
17
+ temperature: 0
18
+ maxTokens: 1024
19
+
20
+ prompts:
21
+ greeting:
22
+ system: You are a helpful assistant.
23
+ user: "{{message}}"
24
+
25
+ tests:
26
+ - name: basic-greeting
27
+ prompt: greeting
28
+ vars:
29
+ message: Hello, how are you?
30
+ expect:
31
+ output:
32
+ contains:
33
+ - hello
34
+ guardrails:
35
+ pii:
36
+ enabled: true
37
+
38
+ gates:
39
+ passRateMin: 0.95
40
+
41
+ defaults:
42
+ repeat: 1
43
+ concurrency: 4
44
+ timeoutMs: 60000
45
+ `;function se(e){e.command("init").description("Create a kindlm.yaml template").option("--force","Overwrite existing kindlm.yaml").action(t=>{let n=De(process.cwd(),"kindlm.yaml");Le(n)&&!t.force&&(console.error(H.red("kindlm.yaml already exists. Use --force to overwrite.")),process.exit(1)),_e(n,je,"utf-8"),console.log(H.green("Created kindlm.yaml")),console.log(""),console.log("Next steps:"),console.log(` 1. Edit ${H.bold("kindlm.yaml")} with your test configuration`),console.log(` 2. Set your API key: ${H.bold("export OPENAI_API_KEY=sk-...")}`),console.log(` 3. Run tests: ${H.bold("kindlm test")}`)})}import{readFileSync as He}from"fs";import{resolve as Be,dirname as Ue}from"path";import M from"chalk";import{parseConfig as Ke}from"@kindlm/core";import{readFileSync as Fe}from"fs";function A(){return{readFile(e){try{return{success:!0,data:Fe(e,"utf-8")}}catch(t){return{success:!1,error:{code:"CONFIG_FILE_REF_ERROR",message:`Cannot read file: ${e}: ${t instanceof Error?t.message:String(t)}`}}}}}}function ie(e){e.command("validate").description("Validate kindlm.yaml configuration").option("-c, --config <path>","Path to config file","kindlm.yaml").action(t=>{let n=Be(process.cwd(),t.config),o=Ue(n),r;try{r=He(n,"utf-8")}catch{console.error(M.red(`Config file not found: ${n}`)),process.exit(1)}let a=A(),i=Ke(r,{configDir:o,fileReader:a});if(!i.success){console.error(M.red("Validation failed:"));let c=i.error.details;if(c&&Array.isArray(c.errors))for(let m of c.errors)console.error(M.red(` - ${m}`));else console.error(M.red(` ${i.error.message}`));process.exit(1)}let s=i.data;console.log(M.green("Config is valid!")),console.log(""),console.log(` Suite: ${M.bold(s.suite.name)}`),console.log(` Tests: ${M.bold(String(s.tests.length))}`),console.log(` Models: ${M.bold(String(s.models.length))}`)})}import w from"chalk";import{evaluateGates as yt,createPrettyReporter as Ct,createJsonReporter as vt,createJunitReporter as Rt,createComplianceReporter as wt,ProviderError as St}from"@kindlm/core";import{readFileSync as We,statSync as Ye}from"fs";import{resolve as Xe,dirname as qe}from"path";import I from"chalk";import{parseConfig as Ve,createProvider as Ze,createRunner as Qe}from"@kindlm/core";function G(){return{async fetch(e,t){let n=new AbortController,o=t.timeoutMs?setTimeout(()=>n.abort(),t.timeoutMs):void 0;try{let r=await globalThis.fetch(e,{method:t.method,headers:t.headers,body:t.body,signal:n.signal});return{ok:r.ok,status:r.status,json:()=>r.json()}}finally{o!==void 0&&clearTimeout(o)}}}}import ze from"ora";function D(){let e;return{start(t){e=ze(t).start()},succeed(t){e?.succeed(t),e=void 0},fail(t){e?.fail(t),e=void 0},stop(){e?.stop(),e=void 0}}}import{spawn as Ge}from"child_process";import{ok as Je,err as ae}from"@kindlm/core";function ce(){return{async execute(e,t){return new Promise(n=>{let o=Ge("sh",["-c",e],{cwd:t.cwd,env:{...process.env,...t.env},stdio:["ignore","pipe","pipe"]}),r=[],a=[];o.stdout.on("data",s=>r.push(s)),o.stderr.on("data",s=>a.push(s));let i=setTimeout(()=>{o.kill("SIGTERM"),setTimeout(()=>{o.killed||o.kill("SIGKILL")},1e3)},t.timeoutMs);o.on("close",(s,c)=>{if(clearTimeout(i),c==="SIGTERM"||c==="SIGKILL"){n(ae({code:"PROVIDER_TIMEOUT",message:`Command timed out after ${t.timeoutMs}ms`}));return}n(Je({stdout:Buffer.concat(r).toString("utf-8"),stderr:Buffer.concat(a).toString("utf-8"),exitCode:s??1}))}),o.on("error",s=>{clearTimeout(i),n(ae({code:"UNKNOWN_ERROR",message:`Failed to spawn command: ${s.message}`}))})})}}}var et=1048576;async function B(e){let t=D(),n=!1,o=()=>{n&&process.exit(130),n=!0,t.stop(),console.error(I.yellow(`
46
+ Interrupted. Exiting...`)),process.exit(130)};process.on("SIGINT",o);try{return await tt(e,t)}finally{process.removeListener("SIGINT",o)}}async function tt(e,t){let n=Xe(process.cwd(),e.configPath),o=qe(n);try{let g=Ye(n);g.size>et&&(console.error(I.red(`Config file exceeds 1MB limit (${(g.size/1048576).toFixed(1)}MB): ${n}`)),process.exit(1))}catch{console.error(I.red(`Config file not found: ${n}`)),process.exit(1)}let r;try{r=We(n,"utf-8")}catch{console.error(I.red(`Config file not found: ${n}`)),process.exit(1)}let a=A(),i=Ve(r,{configDir:o,fileReader:a});i.success||(console.error(I.red(`Config validation failed: ${i.error.message}`)),process.exit(1));let s=i.data;e.runs!==void 0&&(s.defaults.repeat=e.runs),e.gate!==void 0&&(s.gates?s.gates.passRateMin=e.gate/100:s.gates={passRateMin:e.gate/100});let c=G(),m=new Map,u=s.providers;for(let[g,b]of Object.entries(u)){if(!b)continue;let L="";if(b.apiKeyEnv){let O=process.env[b.apiKeyEnv];O||(console.error(I.red(`Missing environment variable: ${b.apiKeyEnv}`)),process.exit(1)),L=O.trim()}else g!=="ollama"&&(console.error(I.red(`Provider "${g}" requires apiKeyEnv to be configured`)),process.exit(1));let F;try{F=Ze(g,c)}catch(O){let _=O instanceof Error?O.message:String(O);console.error(I.red(`Failed to create provider "${g}": ${_}`)),process.exit(1)}await F.initialize({apiKey:L,baseUrl:b.baseUrl,organization:b.organization,timeoutMs:s.defaults.timeoutMs,maxRetries:2}),m.set(g,F)}let f=0,y=nt(s),d=g=>{g.type==="test_start"?t.start(`Running ${g.test} [${g.model}] (${f}/${y})`):g.type==="test_complete"&&f++},S=s.tests.some(g=>g.command)?ce():void 0,l=await Qe(s,{adapters:m,configDir:o,fileReader:a,onProgress:d,baselineData:e.baselineData,commandExecutor:S}).run();return t.stop(),l.success||(console.error(I.red(`Run failed: ${l.error.message}`)),process.exit(1)),{config:s,runnerResult:l.data,configDir:o,yamlContent:r}}function nt(e){let t=0;for(let n of e.tests){if(n.skip)continue;let o=n.repeat??e.defaults.repeat;if(n.command)t+=o;else{let r=n.models?.length??e.models.length;t+=r*o}}return t}import{readFileSync as ot,writeFileSync as rt,mkdirSync as st}from"fs";import{join as le}from"path";import{createHash as it}from"crypto";function de(){return le(process.cwd(),".kindlm","last-run.json")}function me(e){let t=de(),n=le(process.cwd(),".kindlm");st(n,{recursive:!0,mode:448});let o={...e,runnerResult:{...e.runnerResult,aggregated:e.runnerResult.aggregated.map(r=>{let a=r.runs.flatMap(i=>i.assertions.filter(s=>!s.passed).map(s=>s.failureMessage)).filter(i=>i!==void 0);return{...r,failureMessages:a,runs:[]}})}};rt(t,JSON.stringify(o),{mode:384})}function ue(){try{let e=ot(de(),"utf-8"),t=JSON.parse(e);return t.runnerResult?.runResult&&Array.isArray(t.runnerResult.aggregated)&&typeof t.suiteName=="string"&&typeof t.configHash=="string"&&typeof t.timestamp=="string"?t:null}catch{return null}}function fe(e){return it("sha256").update(e).digest("hex")}import at from"pdfkit";import{createWriteStream as ct}from"fs";import{mkdir as lt}from"fs/promises";import{dirname as dt}from"path";function mt(e){let t=e.split(`
47
+ `),n=[],o="",r=2,a=[];for(let i of t){let s=i.match(/^(#{2,4})\s+(.+)$/);s&&s[1]?.length===2?((o||a.length>0)&&n.push({heading:o,headingLevel:r,body:a.join(`
48
+ `).trim()}),o=s[2]?.trim()??"",r=2,a=[]):a.push(i)}return(o||a.length>0)&&n.push({heading:o,headingLevel:r,body:a.join(`
49
+ `).trim()}),n}function ut(e){return e.match(/^# (.+)$/m)?.[1]?.trim()??"KindLM Compliance Report"}function ft(e){return e.match(/SHA-256:\s*`([a-f0-9]+)`/i)?.[1]??null}function ge(e){switch(e){case 2:return 18;case 3:return 15;case 4:return 13;default:return 13}}function gt(e){if(e.length<2)return null;let t=e[0]??"",n=e[1]??"";if(!t.includes("|")||!n.match(/^\s*\|[-:\s|]+\|\s*$/))return null;let o=i=>i.split("|").slice(1,-1).map(s=>s.trim()),r={cells:o(t)},a=[];for(let i=2;i<e.length;i++){let s=e[i]??"";if(!s.includes("|"))break;a.push({cells:o(s)})}return{header:r,rows:a}}function N(e,t){let n=e.page.margins.bottom;e.page.height-n-30-e.y<t&&(e.addPage(),pe(e))}function pe(e){let t=new Date().toISOString();e.fontSize(8).font("Helvetica").fillColor("#a8a29e").text("KindLM Compliance Report",60,40),e.text(t,60,40,{align:"right"}),e.moveDown(3)}function pt(e){e.fontSize(8).font("Helvetica").fillColor("#a8a29e").text("Generated by KindLM \xB7 kindlm.com",60,e.page.height-50,{align:"center",width:e.page.width-120})}function ht(e,t,n){let o=t.header.cells.length,r=n/o,a=e.page.margins.left,i=18;N(e,i*2);let s=(c,m,u)=>{let f=e.y;u&&(e.save(),e.rect(a,f-2,n,i).fill(u),e.restore());for(let y=0;y<c.length;y++){let d=a+y*r;e.fontSize(8).font(m?"Helvetica-Bold":"Courier").fillColor("#44403c").text(c[y]??"",d+4,f,{width:r-8,height:i,lineBreak:!1})}e.y=f+i};s(t.header.cells,!0,"#f5f5f4");for(let c of t.rows)N(e,i),s(c.cells,!1)}async function he(e,t){return await lt(dt(t),{recursive:!0}),new Promise((n,o)=>{let r=new at({size:"A4",margins:{top:72,bottom:72,left:60,right:60},info:{Title:"KindLM EU AI Act Compliance Report",Author:"KindLM",Creator:"KindLM CLI"}}),a=ct(t);r.pipe(a);let i=r.page.width-r.page.margins.left-r.page.margins.right,s=ut(e),c=ft(e);r.moveDown(6),r.fontSize(28).font("Helvetica-Bold").fillColor("#1c1917").text(s,{align:"center",width:i}),r.moveDown(.5),r.fontSize(14).font("Helvetica").fillColor("#57534e").text("EU AI Act Annex IV Documentation",{align:"center",width:i}),r.moveDown(1),r.fontSize(10).fillColor("#a8a29e").text(`Generated: ${new Date().toISOString()}`,{align:"center",width:i}),c&&(r.moveDown(.3),r.fontSize(9).font("Courier").fillColor("#78716c").text(`SHA-256: ${c}`,{align:"center",width:i})),r.moveDown(2),r.fontSize(10).font("Helvetica").fillColor("#6366f1").text("kindlm.com",{align:"center",link:"https://kindlm.com",width:i});let m=mt(e);for(let u of m){if(r.addPage(),pe(r),u.heading){let C=ge(u.headingLevel);r.fontSize(C).font("Helvetica-Bold").fillColor("#1c1917").text(u.heading,{width:i}),r.moveDown(.5),r.moveTo(60,r.y).lineTo(60+i,r.y).strokeColor("#e7e5e4").lineWidth(1).stroke(),r.moveDown(.8)}let f=u.body.split(`
50
+ `),y=!1,d=0;for(;d<f.length;){let C=f[d]??"";if(C.startsWith("```")){y=!y,y&&N(r,30),d++;continue}if(y){N(r,14);let l=r.y;r.save(),r.rect(r.page.margins.left,l-2,i,14).fill("#f5f5f4"),r.restore(),r.fontSize(9).font("Courier").fillColor("#44403c").text(C,{width:i}),d++;continue}if(!C.trim()){r.moveDown(.4),d++;continue}let S=f[d+1]??"";if(C.includes("|")&&d+1<f.length&&S.match(/^\s*\|[-:\s|]+\|\s*$/)){let l=[],g=d;for(;g<f.length&&(f[g]??"").includes("|");)l.push(f[g]??""),g++;let b=gt(l);if(b){ht(r,b,i),d=g;continue}}if(C.match(/^\s*\|[-:]+/)||C.match(/^---+$/)){d++;continue}let h=C.match(/^(#{3,4})\s+(.+)$/);if(h?.[1]&&h[2]){let l=h[1].length,g=ge(l);N(r,g+10),r.moveDown(.3),r.fontSize(g).font("Helvetica-Bold").fillColor("#1c1917").text(h[2].trim(),{width:i}),r.moveDown(.3),d++;continue}if(C.match(/^\s*[-*] /)){N(r,14),r.fontSize(10).font("Helvetica").fillColor("#44403c").text(C.trim(),{indent:12,width:i-12}),d++;continue}N(r,14),r.fontSize(10).font("Helvetica").fillColor("#44403c").text(C.trim(),{width:i}),d++}pt(r)}r.end(),a.on("finish",()=>n(t)),a.on("error",o)})}function ye(e){e.command("test").description("Run test suites").option("-s, --suite <name>","Run a specific suite").option("--compliance","Generate compliance report").option("--reporter <type>","Output format: pretty, json, junit","pretty").option("--runs <count>","Override run count").option("--gate <percent>","Fail if pass rate below threshold").option("--pdf <path>","Export compliance report as PDF (requires --compliance)").option("-c, --config <path>","Path to config file","kindlm.yaml").action(async t=>{try{let{runnerResult:n,config:o,yamlContent:r}=await B({configPath:t.config,runs:t.runs?parseInt(t.runs,10):void 0,gate:t.gate?parseFloat(t.gate):void 0}),{runResult:a,aggregated:i}=n,s=yt(o.gates,i),m=bt(t.reporter).generate(a,s);if(console.log(m.content),t.compliance){let y=wt().generate(a,s);if(console.log(""),console.log(y.content),t.pdf){let d=await he(y.content,t.pdf);console.log(""),console.log(w.green(`PDF report saved to ${d}`))}}try{me({runnerResult:n,suiteName:o.suite.name,configHash:fe(r),timestamp:new Date().toISOString()})}catch{}let u=a.failed===0&&a.errored===0&&s.passed;process.exit(u?0:1)}catch(n){if(n instanceof St){let o=n.code==="TIMEOUT"?"Provider timeout":n.code==="NETWORK_ERROR"?"Network error":n.code==="AUTH_FAILED"?"Authentication failed":n.code==="RATE_LIMITED"?"Rate limited":`Provider error (${n.code})`;console.error(w.red(`${o}: ${n.message}`)),n.retryable&&console.error(w.yellow("This error may be transient. Try again or increase --timeout."))}else if(kt(n)){let r=n.code.startsWith("CONFIG_")?"Config error":"Error";console.error(w.red(`${r}: ${n.message}`))}else n instanceof Error&&n.name==="AbortError"?console.error(w.red("Request timed out. Check network connectivity or increase timeout.")):console.error(w.red(`Error: ${n instanceof Error?n.message:String(n)}`));process.exit(1)}})}var Tt={bold:e=>w.bold(e),red:e=>w.red(e),green:e=>w.green(e),yellow:e=>w.yellow(e),cyan:e=>w.cyan(e),dim:e=>w.dim(e),greenBold:e=>w.green.bold(e),redBold:e=>w.red.bold(e)};function bt(e){switch(e){case"json":return vt();case"junit":return Rt();default:return Ct(Tt)}}function kt(e){return typeof e=="object"&&e!==null&&"code"in e&&"message"in e&&typeof e.code=="string"&&typeof e.message=="string"}import{resolve as W,dirname as q,join as V}from"path";import{readFileSync as $t}from"fs";import p from"chalk";import{parseConfig as Et,readBaseline as Ot,writeBaseline as At,listBaselines as Mt,buildBaselineData as Re,compareBaseline as Nt,deserializeBaseline as Lt}from"@kindlm/core";import{readFileSync as xt,writeFileSync as Pt,mkdirSync as Ce,readdirSync as It}from"fs";import{join as X}from"path";function ve(e){return e.replace(/[^a-zA-Z0-9_-]/g,"_")}function J(e){let t=X(e,"baselines");return{read(n){let o=X(t,`${ve(n)}.json`);try{return{success:!0,data:xt(o,"utf-8")}}catch{return{success:!1,error:{code:"BASELINE_NOT_FOUND",message:`No baseline found for suite "${n}" at ${o}`}}}},write(n,o){try{Ce(t,{recursive:!0});let r=X(t,`${ve(n)}.json`);return Pt(r,o,"utf-8"),{success:!0,data:void 0}}catch(r){return{success:!1,error:{code:"UNKNOWN_ERROR",message:`Failed to write baseline: ${r instanceof Error?r.message:String(r)}`}}}},list(){try{return Ce(t,{recursive:!0}),{success:!0,data:It(t).filter(r=>r.endsWith(".json")).map(r=>r.replace(/\.json$/,""))}}catch(n){return{success:!1,error:{code:"UNKNOWN_ERROR",message:`Failed to list baselines: ${n instanceof Error?n.message:String(n)}`}}}}}}function we(e){let t=e.command("baseline").description("Manage test baselines");t.command("set").description("Save current results as baseline").option("-c, --config <path>","Path to config file","kindlm.yaml").option("--runs <count>","Override run count").action(async n=>{try{let o=q(W(process.cwd(),n.config)),r=V(o,".kindlm"),a=J(r),{config:i,runnerResult:s}=await B({configPath:n.config,runs:n.runs?parseInt(n.runs,10):void 0}),{aggregated:c}=s,m=Re(i.suite.name,c,new Date().toISOString()),u=At(m,a);u.success||(console.error(p.red(`Failed to save baseline: ${u.error.message}`)),process.exit(1));let f=Object.keys(m.results).length;console.log(""),console.log(p.green(`Baseline saved for suite "${i.suite.name}" (${f} test${f===1?"":"s"})`)),console.log(p.dim(` Location: ${r}/baselines/`)),process.exit(0)}catch(o){console.error(p.red(`Error: ${o instanceof Error?o.message:String(o)}`)),process.exit(1)}}),t.command("compare").description("Compare latest against baseline").option("-c, --config <path>","Path to config file","kindlm.yaml").option("--runs <count>","Override run count").action(async n=>{try{let o=q(W(process.cwd(),n.config)),r=V(o,".kindlm"),a=J(r),i=W(process.cwd(),n.config),s;try{s=$t(i,"utf-8")}catch{console.error(p.red(`Config file not found: ${i}`)),process.exit(1)}let c=A(),m=Et(s,{configDir:o,fileReader:c});m.success||(console.error(p.red(`Config validation failed: ${m.error.message}`)),process.exit(1));let u=m.data.suite.name,f=Ot(u,a);f.success||(f.error.code==="BASELINE_NOT_FOUND"?console.error(p.red(`No baseline found for suite "${u}". Run \`kindlm baseline set\` first.`)):console.error(p.red(`Failed to read baseline: ${f.error.message}`)),process.exit(1));let y=f.data,{runnerResult:d}=await B({configPath:n.config,runs:n.runs?parseInt(n.runs,10):void 0,baselineData:y}),{aggregated:C}=d,S=Re(u,C,new Date().toISOString()),h=Nt(y,S.results);if(console.log(""),console.log(p.bold(`Baseline comparison for "${u}"`)),console.log(p.dim(` Baseline from: ${y.createdAt}`)),console.log(""),h.regressions.length>0){console.log(p.red.bold(` Regressions (${h.regressions.length}):`));for(let l of h.regressions)console.log(p.red(` ${l.testName}: ${U(l.baselinePassRate)} \u2192 ${U(l.currentPassRate)}`)),l.newFailureCodes.length>0&&console.log(p.red(` New failures: ${l.newFailureCodes.join(", ")}`));console.log("")}if(h.improvements.length>0){console.log(p.green.bold(` Improvements (${h.improvements.length}):`));for(let l of h.improvements)console.log(p.green(` ${l.testName}: ${U(l.baselinePassRate)} \u2192 ${U(l.currentPassRate)}`));console.log("")}if(h.unchanged.length>0){console.log(p.dim(` Unchanged (${h.unchanged.length}):`));for(let l of h.unchanged)console.log(p.dim(` ${l.testName}: ${U(l.passRate)}`));console.log("")}if(h.newTests.length>0){console.log(p.cyan(` New tests (${h.newTests.length}):`));for(let l of h.newTests)console.log(p.cyan(` ${l}`));console.log("")}if(h.removedTests.length>0){console.log(p.yellow(` Removed tests (${h.removedTests.length}):`));for(let l of h.removedTests)console.log(p.yellow(` ${l}`));console.log("")}process.exit(h.regressions.length>0?1:0)}catch(o){console.error(p.red(`Error: ${o instanceof Error?o.message:String(o)}`)),process.exit(1)}}),t.command("list").description("List saved baselines").option("-c, --config <path>","Path to config file","kindlm.yaml").action(n=>{try{let o=q(W(process.cwd(),n.config)),r=V(o,".kindlm"),a=J(r),i=Mt(a);i.success||(console.error(p.red(`Failed to list baselines: ${i.error.message}`)),process.exit(1));let s=i.data;s.length===0&&(console.log(p.dim("No baselines saved yet. Run `kindlm baseline set` to create one.")),process.exit(0)),console.log(p.bold("Saved baselines:")),console.log("");for(let c of s){let m=a.read(c);if(!m.success){console.log(` ${c} ${p.dim("(unreadable)")}`);continue}let u=Lt(m.data);if(!u.success){console.log(` ${c} ${p.dim("(corrupt)")}`);continue}let f=Object.keys(u.data.results).length;console.log(` ${p.cyan(u.data.suiteName)} \u2014 ${f} test${f===1?"":"s"}, saved ${p.dim(u.data.createdAt)}`)}process.exit(0)}catch(o){console.error(p.red(`Error: ${o instanceof Error?o.message:String(o)}`)),process.exit(1)}})}function U(e){return`${(e*100).toFixed(1)}%`}import{createInterface as zt}from"readline";import{Writable as Gt}from"stream";import E from"chalk";import{readFileSync as _t,writeFileSync as Dt,mkdirSync as jt,unlinkSync as Ft,chmodSync as Ht}from"fs";import{join as Se}from"path";import{homedir as Te}from"os";function Z(){return Se(Te(),".kindlm","credentials")}function Y(){try{let e=_t(Z(),"utf-8"),t=JSON.parse(e);return typeof t.token=="string"&&t.token.length>0?t.token:null}catch{return null}}function be(e){let t=Z(),n=Se(Te(),".kindlm");jt(n,{recursive:!0,mode:448});let o={token:e,savedAt:new Date().toISOString()};Dt(t,JSON.stringify(o,null,2),{mode:384}),Ht(t,384)}function ke(){try{Ft(Z())}catch{}}var Bt="https://api.kindlm.com";var $=class extends Error{status;constructor(t,n){super(n),this.name="CloudApiError",this.status=t}};function j(){let e=process.env.KINDLM_CLOUD_URL??Bt;if(e.startsWith("http://")&&!Ut(e))throw new Error(`Refusing to use insecure HTTP for Cloud API: ${e}. Use HTTPS or target localhost for development.`);return e}function Ut(e){try{let t=new URL(e);return t.hostname==="localhost"||t.hostname==="127.0.0.1"||t.hostname==="::1"}catch{return!1}}function Kt(e){return new Promise(t=>setTimeout(t,e))}function K(e,t){async function n(o,r,a){let i=`${e}${r}`,s={Authorization:`Bearer ${t}`},c={method:o,headers:s};a!==void 0&&(s["Content-Type"]="application/json",c.body=JSON.stringify(a));let m;for(let u=0;u<=1;u++){u>0&&await Kt(1e3);let f=new AbortController,y=setTimeout(()=>f.abort(),3e4);c.signal=f.signal;try{let d=await fetch(i,c);if(!d.ok){if(d.status>=500&&u<1){m=new $(d.status,`HTTP ${d.status}`);continue}let S=`HTTP ${d.status}`;if((d.headers.get("content-type")??"").includes("application/json"))try{let l=await d.json();l.error&&(S=l.error)}catch{}throw new $(d.status,S)}if(d.status===204)return;let C=d.headers.get("content-type")??"";if(!C.includes("application/json"))throw new $(d.status,`Expected JSON response but got content-type: ${C}`);return await d.json()}catch(d){if(d instanceof $)throw d;if(m=d instanceof Error?d:new Error(String(d)),u<1)continue}finally{clearTimeout(y)}}throw m??new Error("Request failed")}return{baseUrl:e,get:o=>n("GET",o),post:(o,r)=>n("POST",o,r),patch:(o,r)=>n("PATCH",o,r),delete:o=>n("DELETE",o)}}function xe(e){e.command("login").description("Authenticate with KindLM Cloud").option("-t, --token <token>","API token (skips interactive prompt)").option("--status","Show current authentication status").option("--logout","Remove stored credentials").action(async t=>{try{if(t.logout){ke(),console.log(E.green("Logged out. Credentials removed."));return}if(t.status){await Jt();return}let n=t.token??process.env.KINDLM_API_TOKEN??await Wt();n.startsWith("klm_")||(console.error(E.red('Invalid token format. KindLM tokens start with "klm_".')),process.exit(1));let o=K(j(),n);try{await o.get("/v1/auth/tokens")}catch(r){throw r instanceof $&&r.status===401&&(console.error(E.red("Invalid or expired token.")),process.exit(1)),r}be(n),console.log(E.green("Authenticated successfully. Token saved."))}catch(n){console.error(E.red(`Login failed: ${n instanceof Error?n.message:String(n)}`)),process.exit(1)}})}async function Jt(){let e=Y();if(!e){console.log(E.yellow('Not authenticated. Run "kindlm login" to authenticate.'));return}let t=K(j(),e);try{await t.get("/v1/auth/tokens"),console.log(E.green("Authenticated.")),console.log(` Cloud URL: ${j()}`)}catch(n){n instanceof $&&n.status===401?console.log(E.yellow('Stored token is invalid or expired. Run "kindlm login" to re-authenticate.')):console.log(E.yellow(`Cannot reach Cloud API: ${n instanceof Error?n.message:String(n)}`))}}function Wt(){return new Promise((e,t)=>{let n=new Gt({write(r,a,i){i()}});process.stderr.write("Paste your KindLM API token: ");let o=zt({input:process.stdin,output:n,terminal:!0});o.question("",r=>{o.close(),process.stderr.write(`
51
+ `);let a=r.trim();if(!a){t(new Error("No token provided"));return}e(a)})})}import{basename as Vt}from"path";import{execSync as Zt}from"child_process";import ee from"chalk";import{execSync as Q}from"child_process";function Pe(){try{let e=Q("git rev-parse HEAD",{encoding:"utf-8"}).trim()||null,t=Q("git rev-parse --abbrev-ref HEAD",{encoding:"utf-8"}).trim()||null,o=Q("git status --porcelain",{encoding:"utf-8"}).trim().length>0;return{commitSha:e,branch:t,dirty:o}}catch{return{commitSha:null,branch:null,dirty:!1}}}function Ie(){return process.env.GITHUB_ACTIONS?{name:"github_actions",isCI:!0,commitSha:process.env.GITHUB_SHA??null,branch:process.env.GITHUB_REF_NAME??null}:process.env.GITLAB_CI?{name:"gitlab_ci",isCI:!0,commitSha:process.env.CI_COMMIT_SHA??null,branch:process.env.CI_COMMIT_BRANCH??null}:process.env.CI?{name:null,isCI:!0,commitSha:null,branch:null}:{name:null,isCI:!1,commitSha:null,branch:null}}function z(e){return encodeURIComponent(e)}async function $e(e,t,n){let o=await Yt(e,n.projectName),r=await Xt(e,o,n.suiteName,n.configHash),a=await e.post(`/v1/runs/${z(o)}/runs`,{suiteId:r,commitSha:n.commitSha,branch:n.branch,environment:n.environment,triggeredBy:n.triggeredBy}),i=qt(t.aggregated),s=50;for(let l=0;l<i.length;l+=s){let g=i.slice(l,l+s);await e.post(`/v1/results/${z(a.id)}/results`,{results:g})}let{runResult:c}=t,m=c.totalTests>0?c.passed/c.totalTests:0,u=new Set(t.aggregated.map(l=>l.modelId)),f=t.aggregated.map(l=>l.assertionScores.judge?.mean).filter(l=>l!==void 0),y=f.length>0?f.reduce((l,g)=>l+g,0)/f.length:void 0,d=t.aggregated.map(l=>l.latencyAvgMs),C=d.length>0?d.reduce((l,g)=>l+g,0)/d.length:void 0,S=t.aggregated.reduce((l,g)=>l+g.totalCostUsd,0),h=S>0?S:void 0;return await e.patch(`/v1/runs/${z(a.id)}`,{status:"completed",passRate:m,testCount:c.totalTests,modelCount:u.size,judgeAvgScore:y,latencyAvgMs:C,costEstimateUsd:h,finishedAt:new Date().toISOString()}),{runId:a.id,projectId:o}}async function Yt(e,t){let{projects:n}=await e.get("/v1/projects"),o=n.find(a=>a.name===t);return o?o.id:(await e.post("/v1/projects",{name:t})).id}async function Xt(e,t,n,o){let{suites:r}=await e.get(`/v1/suites/${z(t)}/suites`),a=r.find(s=>s.name===n);return a?a.id:(await e.post(`/v1/suites/${z(t)}/suites`,{name:n,configHash:o})).id}function qt(e){return e.map(t=>{let n=t,o=t.runs.length>0?t.runs.flatMap(r=>r.assertions.filter(a=>!a.passed).map(a=>a.failureMessage)).filter(r=>r!==void 0):n.failureMessages??[];return{testCaseName:t.testCaseName,modelId:t.modelId,passed:t.passed?1:0,passRate:t.passRate,runCount:t.runCount,judgeAvg:t.assertionScores.judge?.mean??null,driftScore:t.assertionScores.drift?.mean??null,latencyAvgMs:t.latencyAvgMs??null,costUsd:t.totalCostUsd??null,totalTokens:t.totalTokens??null,failureCodes:t.failureCodes.length>0?JSON.stringify(t.failureCodes):null,failureMessages:o.length>0?JSON.stringify(o):null,assertionScores:Object.keys(t.assertionScores).length>0?JSON.stringify(t.assertionScores):null}})}function Ee(e){e.command("upload").description("Push last run results to KindLM Cloud").option("-t, --token <token>","API token (overrides stored token)").option("-p, --project <name>","Project name").action(async t=>{try{let n=t.token??process.env.KINDLM_API_TOKEN??Y();n||(console.error(ee.red('Not authenticated. Run "kindlm login" first or pass --token.')),process.exit(1));let o=ue();o||(console.error(ee.red('No test run found. Run "kindlm test" first.')),process.exit(1));let r=Pe(),a=Ie(),i=t.project??en(),s=K(j(),n),c=D();c.start("Uploading results to KindLM Cloud...");try{let m=await $e(s,o.runnerResult,{projectName:i,suiteName:o.suiteName,configHash:o.configHash,commitSha:a.commitSha??r.commitSha??void 0,branch:a.branch??r.branch??void 0,environment:a.isCI?"ci":"local",triggeredBy:a.name??"local"});c.succeed("Uploaded successfully."),console.log(` Run ID: ${m.runId}`),console.log(` Project: ${i}`),console.log(` Suite: ${o.suiteName}`)}catch(m){throw c.fail("Upload failed."),m}}catch(n){console.error(ee.red(`Upload failed: ${n instanceof Error?n.message:String(n)}`)),process.exit(1)}})}function Qt(e){try{let o=new URL(e).pathname.split("/").filter(Boolean),r=o[o.length-1];if(r)return r.replace(/\.git$/,"")}catch{}let t=e.match(/^[\w.-]+@[\w.-]+:(.+?)(?:\.git)?$/);if(t?.[1]){let n=t[1].split("/");return n[n.length-1]??null}return null}function en(){try{let e=Zt("git remote get-url origin",{encoding:"utf-8"}).trim(),t=Qt(e);if(t)return t}catch{}return Vt(process.cwd())}import{readFileSync as on,statSync as rn}from"fs";import{resolve as sn,dirname as an}from"path";import{spawn as cn}from"child_process";import T from"chalk";import{parseConfig as ln,createProvider as dn,filterSpans as mn,mapSpansToResult as un,buildContextFromTrace as fn,createAssertionsFromExpect as gn,evaluateGates as pn}from"@kindlm/core";import{createServer as tn}from"http";import{parseOtlpPayload as nn}from"@kindlm/core";function Oe(e){let t=[],n=null,o=[];function r(){for(let i of o)i()}function a(i,s){if(s.setHeader("Access-Control-Allow-Origin","*"),s.setHeader("Access-Control-Allow-Methods","POST, OPTIONS"),s.setHeader("Access-Control-Allow-Headers","Content-Type"),i.method==="OPTIONS"){s.writeHead(204),s.end();return}if(i.method!=="POST"||i.url!=="/v1/traces"){s.writeHead(404,{"Content-Type":"application/json"}),s.end(JSON.stringify({error:"Not found"}));return}let c=[];i.on("data",m=>c.push(m)),i.on("end",()=>{try{let m=Buffer.concat(c).toString("utf-8"),u=JSON.parse(m),f=nn(u);f.success?(t.push(...f.data),r(),s.writeHead(200,{"Content-Type":"application/json"}),s.end(JSON.stringify({partialSuccess:{}}))):(s.writeHead(400,{"Content-Type":"application/json"}),s.end(JSON.stringify({error:f.error.message})))}catch{s.writeHead(400,{"Content-Type":"application/json"}),s.end(JSON.stringify({error:"Invalid JSON"}))}})}return{start(){return new Promise((i,s)=>{n=tn(a),n.on("error",s),n.listen(e,()=>i())})},stop(){return new Promise(i=>{n?n.close(()=>i()):i()})},getSpans(){return[...t]},waitForSpans({timeoutMs:i}){return new Promise(s=>{if(t.length>0){s([...t]);return}let c=setTimeout(()=>{o=o.filter(u=>u!==m),s([...t])},i),m=()=>{clearTimeout(c),o=o.filter(u=>u!==m),setTimeout(()=>s([...t]),500)};o.push(m)})}}}function Ae(e){e.command("trace").description("Ingest OpenTelemetry traces and run assertions against them").option("-c, --config <path>","Config file path","kindlm.yaml").option("--port <port>","OTLP HTTP port","4318").option("--command <cmd>","Command to spawn (traces are collected while it runs)").option("--timeout <ms>","Timeout in ms to wait for traces","30000").option("--reporter <type>","Report format: pretty, json, junit","pretty").action(async t=>{let n=D();try{let o=sn(process.cwd(),t.config),r=an(o);try{rn(o).size>1048576&&(console.error(T.red("Config file exceeds 1MB limit")),process.exit(1))}catch{console.error(T.red(`Config file not found: ${o}`)),process.exit(1)}let a;try{a=on(o,"utf-8")}catch{console.error(T.red(`Config file not found: ${o}`)),process.exit(1)}let i=A(),s=ln(a,{configDir:r,fileReader:i});s.success||(console.error(T.red(`Config validation failed: ${s.error.message}`)),process.exit(1));let c=s.data,m=c.trace??{port:parseInt(t.port,10),timeoutMs:parseInt(t.timeout,10),spanMapping:{outputTextAttr:"gen_ai.completion.0.content",modelAttr:"gen_ai.response.model",systemAttr:"gen_ai.system",inputTokensAttr:"gen_ai.usage.input_tokens",outputTokensAttr:"gen_ai.usage.output_tokens"}},u=parseInt(t.port,10)||m.port,f=parseInt(t.timeout,10)||m.timeoutMs,y=Oe(u);await y.start(),n.start(`Listening for OTLP traces on port ${u}...`),t.command&&cn("sh",["-c",t.command],{cwd:r,env:{...process.env,OTEL_EXPORTER_OTLP_ENDPOINT:`http://localhost:${u}`,OTEL_EXPORTER_OTLP_PROTOCOL:"http/json"},stdio:"inherit"}).on("error",R=>{n.fail(`Command failed: ${R.message}`)});let d=await y.waitForSpans({timeoutMs:f});await y.stop(),d.length===0&&(n.fail("No traces received"),process.exit(1)),n.succeed(`Received ${d.length} spans`);let C=mn(d,m.spanFilter),S=un(C,m.spanMapping),h=G(),l=new Map,g=c.providers;for(let[v,R]of Object.entries(g)){if(!R)continue;let k="";if(R.apiKeyEnv){let x=process.env[R.apiKeyEnv];x&&(k=x.trim())}if(!(!k&&v!=="ollama"))try{let x=dn(v,h);await x.initialize({apiKey:k,baseUrl:R.baseUrl,organization:R.organization,timeoutMs:c.defaults.timeoutMs,maxRetries:2}),l.set(v,x)}catch{}}let b=c.defaults.judgeModel??c.models[0]?.id,L=c.models.find(v=>v.id===b),F=L?l.get(L.provider):void 0,O=fn(S,{configDir:r,judgeAdapter:F,judgeModel:L?.model}),_=[];for(let v of c.tests){if(v.skip)continue;let R=gn(v.expect),k=[];for(let x of R){let P=await x.evaluate(O);k.push(...P)}_.push({testName:v.name,assertions:k})}let te=_.reduce((v,R)=>v+R.assertions.length,0),ne=_.reduce((v,R)=>v+R.assertions.filter(k=>k.passed).length,0),oe=te-ne;console.log(),console.log(T.bold("Trace Test Results")),console.log(T.dim("\u2500".repeat(50)));for(let{testName:v,assertions:R}of _){let x=R.every(P=>P.passed)?T.green("\u2713"):T.red("\u2717");console.log(`${x} ${v}`);for(let P of R){let Me=P.passed?T.green(" \u2713"):T.red(" \u2717"),Ne=P.failureMessage?`${P.label}: ${P.failureMessage}`:P.label;console.log(`${Me} ${Ne}`)}}console.log(),console.log(`${T.bold("Total:")} ${ne} passed, ${oe} failed out of ${te} assertions`);let re=pn(c.gates,[]);if(!re.passed)for(let v of re.gates.filter(R=>!R.passed))console.log(T.red(`Gate failed: ${v.message}`));process.exit(oe>0?1:0)}catch(o){n.fail(`Trace command failed: ${o instanceof Error?o.message:String(o)}`),process.exit(1)}})}function dr(){let e=new hn;return e.name("kindlm").description("AI agent behavioral regression testing").version("0.0.0"),se(e),ie(e),ye(e),we(e),xe(e),Ee(e),Ae(e),e}export{dr as createProgram};
52
+ //# sourceMappingURL=index.js.map