ratchetai-runner 0.3.0 → 0.3.1

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.
Files changed (2) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -67,4 +67,4 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
67
67
  `))>=0;){let a=o.slice(0,n);o=o.slice(n+1),a.trim()&&r.onLine(a)}}),i.stderr?.setEncoding("utf8"),i.stderr?.on("data",e=>r.onStderr(e)),i.then(e=>(o.trim()&&r.onLine(o),{exitCode:e.exitCode??0}),e=>{throw e.code==="ENOENT"?new Error(`Could not find the '${t.bin}' CLI. Install it and ensure it is on PATH, or set the matching RATCHET_*_BIN env var to its full path.`):e})}function Jd(t){let r=t.trim();if(!r)return null;try{return JSON.parse(r)}catch{return null}}function ee(t){return typeof t=="object"&&t!==null?t:null}function Fd(t){let i=ee(t)?.content;return Array.isArray(i)?i.map(ee).filter(o=>o!==null):[]}function At(t,r=2e3){let i=t.replace(/\s+$/,"");return i.length<=r?i:`${i.slice(0,r)}\u2026`}function Vd(t){let r=ee(t),i=r?.type;if(i==="system"){let o=typeof r?.subtype=="string"?r.subtype:"system",e=typeof r?.model=="string"?` \xB7 ${r.model}`:"";return{kind:"system",text:`${o}${e}`}}if(i==="assistant"){let o=Fd(r?.message),e=[],n=[];for(let a of o)a.type==="text"&&typeof a.text=="string"&&e.push(a.text),a.type==="tool_use"&&typeof a.name=="string"&&n.push(a.name);return e.length>0?{kind:"assistant",text:At(e.join(`
68
68
  `))}:n.length>0?{kind:"tool",text:`\u{1F527} ${n.join(", ")}`}:{kind:"assistant"}}if(i==="user")return{kind:Fd(r?.message).some(n=>n.type==="tool_result")?"tool":"user"};if(i==="result"){let o=typeof r?.result=="string"?r.result:void 0;return{kind:"result",text:o?At(o):void 0}}return{kind:"system",text:typeof i=="string"?i:void 0}}function Md(t){let r=ee(t);if(r?.type!=="system"||r.subtype!=="init")return null;let i=Array.isArray(r.tools)?r.tools.filter(a=>typeof a=="string"):[],o=r.mcp_servers,e=Array.isArray(o)?o.map(a=>{if(typeof a=="string")return a;let c=ee(a),s=typeof c?.name=="string"?c.name:null;if(!s)return null;let u=c?.status;return typeof u=="string"&&u!=="connected"?null:s}).filter(a=>!!a):[],n=typeof r.permissionMode=="string"?r.permissionMode:typeof r.permission_mode=="string"?r.permission_mode:void 0;return{tools:i,mcpServers:e,permissionMode:n}}function sg(t){if(t)for(let r of["text","message","content"]){let i=t[r];if(typeof i=="string"&&i.trim())return i;if(Array.isArray(i)){let o=i.map(e=>{let n=ee(e);return typeof n?.text=="string"?n.text:""}).filter(Boolean).join(`
69
69
  `);if(o.trim())return o}}}function Kd(t){let r=ee(t),i=typeof r?.type=="string"?r.type:"";if(i.startsWith("thread.")||i==="turn.started")return{kind:"system",text:i};if(i==="turn.completed")return{kind:"result"};if(i==="turn.failed"||i==="error"){let o=typeof r?.message=="string"?r.message:i;return{kind:"error",text:At(o,500)}}if(i.startsWith("item.")){let o=ee(r?.item),e=typeof o?.type=="string"&&o.type||typeof o?.item_type=="string"&&o.item_type||"",n=sg(o);return/command|exec|tool|shell/i.test(e)?{kind:"tool",text:n??e}:/reason/i.test(e)?{kind:"system",text:n?At(n):"reasoning"}:n?{kind:"assistant",text:At(n)}:{kind:"system",text:e||i}}return{kind:"system",text:i||void 0}}function Gd(t){let r=ee(t)?.type;return r==="turn.completed"?{status:"completed"}:r==="turn.failed"||r==="error"?{status:"failed"}:null}function Wd(t){let r=ee(t);if(r?.type!=="result")return null;let i=r.is_error===!0,o=r.subtype,e=!i&&(o===void 0||o==="success"),n=typeof r.total_cost_usd=="number"?r.total_cost_usd:void 0;return{status:e?"completed":"failed",costUsd:n}}function dg(t,r){return t==="codex"?{bin:process.env.RATCHET_CODEX_BIN??"codex",args:["exec","--json","--skip-git-repo-check","-s","read-only",r],normalize:Kd,extractResult:Gd}:{bin:process.env.RATCHET_CLAUDE_BIN??"claude",args:["-p",r,"--output-format","stream-json","--verbose"],normalize:Vd,extractResult:Wd}}async function In(t,r){let i=lg(),o=Date.now(),e=0,n={status:"completed"},a;await Ie(t,{type:"session_start",sessionId:i,title:r.title,prompt:r.prompt,cliModel:r.cliModel,cwd:r.cwd,startedAt:o,taskId:r.taskId,automationId:r.automationId});let c=Promise.resolve(),s=m=>{c=c.then(()=>Ie(t,{type:"event",sessionId:i,event:m}).then(()=>{},f=>console.error(" event send failed:",f instanceof Error?f.message:f)))},u=dg(r.cliModel,r.prompt);try{let{exitCode:m}=await Ld({bin:u.bin,args:u.args,cwd:r.cwd},{onLine:_=>{let v=Jd(_);if(v===null)return;let x=Md(v);x&&(a=x);let Z=u.normalize(v),U=u.extractResult(v);U&&(n=U),s({seq:e++,ts:Date.now(),kind:Z.kind,text:Z.text,raw:v})},onStderr:_=>{let v=_.trim();v&&s({seq:e++,ts:Date.now(),kind:"stderr",text:v,raw:_})}});await c;let f=m===0?n.status:"failed";return await Ie(t,{type:"session_end",sessionId:i,status:f,exitCode:m,endedAt:Date.now(),costUsd:n.costUsd,capabilities:a}),{sessionId:i,status:f}}catch(m){let f=m instanceof Error?m.message:String(m);throw await c.catch(()=>{}),await Ie(t,{type:"session_end",sessionId:i,status:"failed",exitCode:-1,endedAt:Date.now(),error:f}).catch(()=>{}),m}}import Sn from"fs";import ge from"path";function mg(t){let r="",i=t;for(;;)try{return r?ge.join(Sn.realpathSync(i),r):Sn.realpathSync(i)}catch{let o=ge.dirname(i);if(o===i)return t;r=ge.join(ge.basename(i),r),i=o}}function Tu(t,r){let i=ge.resolve(t??process.cwd()),o;try{o=Sn.realpathSync(i)}catch{o=i}let e=r?.trim();if(!e)return o;let n=mg(ge.resolve(o,e)),a=ge.relative(o,n);if(!(a===""||!a.startsWith("..")&&!ge.isAbsolute(a)))throw new Error(`refusing to run outside the runner's allowed folder: "${e}" escapes "${o}". Launch the runner with --cwd at or above the target folder.`);return n}var pg=new Set(["node_modules",".git",".svn",".hg","dist","build","out","coverage",".next",".turbo",".cache","vendor","__pycache__","target"]);function Bd(t){let r;try{r=Sn.readdirSync(t,{withFileTypes:!0})}catch{return[]}return r.filter(i=>i.isDirectory()&&!i.name.startsWith(".")&&!pg.has(i.name)).map(i=>i.name).sort((i,o)=>i.localeCompare(o))}function qd(t){return new Promise(r=>setTimeout(r,t))}function fg(t){let r=t.trim().replace(/\s+/g," ");return r.length<=60?r:`${r.slice(0,57)}...`}async function Xd(t,r){let i=r.intervalMs??2500,o=!1;process.on("SIGINT",()=>{o&&process.exit(130),console.log(`
70
- \u25C7 stopping after current task\u2026 (Ctrl-C again to force)`),o=!0}),console.log(`\u25C7 runner connected to ${t.baseUrl} \u2014 polling for tasks every ${i}ms`);let e=Tu(r.cwd,void 0),n=Bd(e);try{await Ie(t,{type:"runner_folders",root:e,folders:n}),console.log(`\u25C7 reported ${n.length} folder(s) under ${e}`)}catch(a){console.error(" folder report failed:",a instanceof Error?a.message:a)}for(;!o;){let a;try{a=await Cd(t)}catch(s){console.error(" poll error:",s instanceof Error?s.message:s),await qd(i);continue}if(!a){await qd(i);continue}let c=a.goal??fg(a.prompt);console.log(`\u25B6 dispatch ${a.taskId.slice(0,8)} \xB7 ${c}`);try{let s=Tu(r.cwd,a.cwd),{status:u}=await In(t,{prompt:a.prompt,title:c,cliModel:a.cliModel,cwd:s,taskId:a.taskId,automationId:a.automationId});console.log(`\u25A0 dispatch ${a.taskId.slice(0,8)} \u2192 ${u}`)}catch(s){console.error(`\u2716 dispatch ${a.taskId.slice(0,8)} failed:`,s instanceof Error?s.message:s)}}console.log("\u25C7 runner stopped.")}function vg(t){let r=t.trim().replace(/\s+/g," ");return r.length<=60?r:`${r.slice(0,57)}...`}var wn=new gg;wn.name("ratchet").description("ratchetAI local runner").version("0.1.0");wn.command("run").description("Run a non-interactive Claude Code session and stream it to ratchetAI").requiredOption("--prompt <text>","prompt for the CLI session").option("--model <engine>","engine: claude or codex","claude").option("--title <text>","session title (defaults to a prompt excerpt)").option("--cwd <dir>","working directory for the session").option("--url <url>","control-plane base URL (or RATCHET_URL)").option("--token <token>","device token (or RATCHET_DEVICE_TOKEN)").action(async t=>{let r=Pn({url:t.url,token:t.token}),i=t.title??vg(t.prompt);console.log(`\u25B6 starting session: ${i}`);let{sessionId:o,status:e}=await In(r,{prompt:t.prompt,title:i,cliModel:t.model==="codex"?"codex":"claude",cwd:t.cwd});console.log(`\u25A0 session ${o} \u2192 ${e}`),e!=="completed"&&(process.exitCode=1)});wn.command("connect").description("Run as a daemon: poll for scheduled/dispatched tasks and execute them").option("--cwd <dir>","working directory for dispatched sessions").option("--interval <ms>","poll interval in ms","2500").option("--url <url>","control-plane base URL (or RATCHET_URL)").option("--token <token>","device token (or RATCHET_DEVICE_TOKEN)").action(async t=>{let r=Pn({url:t.url,token:t.token});await Xd(r,{cwd:t.cwd,intervalMs:Number(t.interval)})});wn.parseAsync(process.argv).catch(t=>{console.error("\u2716",t instanceof Error?t.message:t),process.exit(1)});
70
+ \u25C7 stopping after current task\u2026 (Ctrl-C again to force)`),o=!0}),console.log(`\u25C7 runner connected to ${t.baseUrl} \u2014 polling for tasks every ${i}ms`);let e=Tu(r.cwd,void 0),n=Bd(e);try{await Ie(t,{type:"runner_folders",root:e,folders:n}),console.log(n.length===0?`\u25C7 ready \xB7 automations run in ${e}`:`\u25C7 ready \xB7 ${n.length} folder(s) available under ${e}`)}catch(a){console.error(" folder report failed:",a instanceof Error?a.message:a)}for(;!o;){let a;try{a=await Cd(t)}catch(s){console.error(" poll error:",s instanceof Error?s.message:s),await qd(i);continue}if(!a){await qd(i);continue}let c=a.goal??fg(a.prompt);console.log(`\u25B6 dispatch ${a.taskId.slice(0,8)} \xB7 ${c}`);try{let s=Tu(r.cwd,a.cwd),{status:u}=await In(t,{prompt:a.prompt,title:c,cliModel:a.cliModel,cwd:s,taskId:a.taskId,automationId:a.automationId});console.log(`\u25A0 dispatch ${a.taskId.slice(0,8)} \u2192 ${u}`)}catch(s){console.error(`\u2716 dispatch ${a.taskId.slice(0,8)} failed:`,s instanceof Error?s.message:s)}}console.log("\u25C7 runner stopped.")}function vg(t){let r=t.trim().replace(/\s+/g," ");return r.length<=60?r:`${r.slice(0,57)}...`}var wn=new gg;wn.name("ratchet").description("ratchetAI local runner").version("0.1.0");wn.command("run").description("Run a non-interactive Claude Code session and stream it to ratchetAI").requiredOption("--prompt <text>","prompt for the CLI session").option("--model <engine>","engine: claude or codex","claude").option("--title <text>","session title (defaults to a prompt excerpt)").option("--cwd <dir>","working directory for the session").option("--url <url>","control-plane base URL (or RATCHET_URL)").option("--token <token>","device token (or RATCHET_DEVICE_TOKEN)").action(async t=>{let r=Pn({url:t.url,token:t.token}),i=t.title??vg(t.prompt);console.log(`\u25B6 starting session: ${i}`);let{sessionId:o,status:e}=await In(r,{prompt:t.prompt,title:i,cliModel:t.model==="codex"?"codex":"claude",cwd:t.cwd});console.log(`\u25A0 session ${o} \u2192 ${e}`),e!=="completed"&&(process.exitCode=1)});wn.command("connect").description("Run as a daemon: poll for scheduled/dispatched tasks and execute them").option("--cwd <dir>","working directory for dispatched sessions").option("--interval <ms>","poll interval in ms","2500").option("--url <url>","control-plane base URL (or RATCHET_URL)").option("--token <token>","device token (or RATCHET_DEVICE_TOKEN)").action(async t=>{let r=Pn({url:t.url,token:t.token});await Xd(r,{cwd:t.cwd,intervalMs:Number(t.interval)})});wn.parseAsync(process.argv).catch(t=>{console.error("\u2716",t instanceof Error?t.message:t),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ratchetai-runner",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Local runner for ratchetAI — drives your own Claude Code / Codex CLI in non-interactive, streamed sessions. Your vendor credentials never leave your machine.",
5
5
  "type": "module",
6
6
  "license": "MIT",