@maol-1997/remote-cc 0.1.2 → 0.1.3

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/cli.js +3 -3
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import{ConvexClient as Tt}from"convex/browser";import{randomUUID as kt}from"crypto";import{anyApi as Ce,componentsGeneric as Ee}from"convex/server";var m=Ce;var At=Ee();var j="https://good-emu-457.convex.cloud";import{chmodSync as Te,existsSync as ke,mkdirSync as be,readFileSync as Ie,writeFileSync as Ae}from"fs";import{homedir as Re}from"os";import{join as F}from"path";var z=F(Re(),".remote-cc"),I=F(z,"config.json");function B(){try{return ke(I)?JSON.parse(Ie(I,"utf8")):null}catch{return null}}function J(e){be(z,{recursive:!0}),Ae(I,JSON.stringify(e,null,2));try{Te(I,384)}catch{}}var G=["default","acceptEdits","plan"],H=["opus","sonnet","haiku","claude-fable-5"];async function K(e,t){switch(t.type){case"start_session":await e.start(x(t),W(t));break;case"resume_session":await e.resume(x(t),Me(t));break;case"stop_session":t.sessionId&&await e.stop(x(t));break;case"send_message":await e.sendMessage(x(t),Pe(t).text);break;case"interrupt_turn":await e.interrupt(x(t));break;case"set_permission_mode":break;case"set_model":await e.setModel(x(t),Ne(t).model);break;default:throw new Error(`unknown command: ${t.type}`)}}function x(e){if(!e.sessionId)throw new Error(`${e.type} without sessionId`);return e.sessionId}function W(e){let t=A(e);return{name:_(e,t,"name"),cwd:_(e,t,"cwd"),mode:Oe(e,t)}}function Me(e){let t=A(e);return{...W(e),claudeSessionId:_(e,t,"claudeSessionId")}}function Pe(e){return{text:_(e,A(e),"text")}}function A(e){if(typeof e.args!="object"||e.args===null)throw new Error(`${e.type} with malformed args`);return e.args}function _(e,t,s){let n=t[s];if(typeof n!="string"||!n)throw new Error(`${e.type} without ${s}`);return n}function Oe(e,t){let s=_(e,t,"mode");if(!G.includes(s))throw new Error(`${e.type} with unknown mode "${s}"`);return s}function Ne(e){let t=_(e,A(e),"model");if(!H.includes(t))throw new Error(`${e.type} with unknown model "${t}"`);return{model:t}}function Y(e){return e.replace(/\.convex\.cloud\/?$/,".convex.site")}import{execFileSync as qe}from"child_process";import{arch as Le,homedir as Ve,release as D,type as je}from"os";import{existsSync as Ue}from"fs";import{homedir as $e}from"os";import{join as De}from"path";function R(){let e=De($e(),".local","bin","claude");return Ue(e)?e:"claude"}var Fe="0.1.2";function X(){return{platform:`${ze()} \xB7 ${Le()}`,claudeVersion:Be(),baseDir:Ve(),agentVersion:Fe}}function ze(){let e=je();return e==="Darwin"?`macOS ${D()}`:e==="Linux"?`Linux ${D()}`:`${e} ${D()}`}function Be(){try{let t=qe(R(),["--version"],{encoding:"utf8",timeout:5e3}).match(/\d+\.\d+\.\d+/);return t?t[0]:void 0}catch{return}}async function Z(e,t,s,n){let i=await fetch(`${e}/pair/claim`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:t,name:s,platform:n.platform,claudeVersion:n.claudeVersion,baseDir:n.baseDir,agentVersion:n.agentVersion})}),o=await i.text(),a;try{a=JSON.parse(o)}catch{a={}}if(!i.ok)throw new Error(`pairing failed (${i.status}): ${a.error??o.slice(0,200)}`);let{deviceToken:l,deviceId:f,userId:c,name:u}=a;if(!l||!f||!c||!u)throw new Error(`pairing response is missing fields: ${o.slice(0,200)}`);return{deviceToken:l,deviceId:f,userId:c,name:u}}function Q(e,t,s){let n=async l=>{try{await e.mutation(m.devices.heartbeat,{deviceToken:t,...l})}catch(f){console.error("heartbeat error:",f instanceof Error?f.message:f)}},i=()=>n({});n({platform:s.platform,claudeVersion:s.claudeVersion,baseDir:s.baseDir,agentVersion:s.agentVersion});let a=setInterval(()=>{i()},2e4);return()=>clearInterval(a)}import{randomUUID as at}from"crypto";import{chmodSync as ct,existsSync as ve,mkdirSync as dt,readFileSync as we,realpathSync as ut,writeFileSync as lt}from"fs";import{createRequire as pt}from"module";import{homedir as xe,tmpdir as mt}from"os";import{dirname as ft,join as U,resolve as gt}from"path";import yt from"node-pty";import{randomUUID as b}from"crypto";import{readFileSync as se}from"fs";import{homedir as re}from"os";import{join as oe}from"path";import{execFileSync as ee}from"child_process";var te="Claude Code-credentials";function Ge(){let e="";try{e=ee("/usr/bin/security",["dump-keychain"],{encoding:"utf8",maxBuffer:64*1024*1024})}catch{}let t=new Set;for(let s of e.split(/\nkeychain: /)){if(!s.includes(`"svce"<blob>="${te}"`))continue;let n=s.match(/"acct"<blob>="([^"]*)"/);n&&t.add(n[1])}return[...t]}function He(e){let t=["find-generic-password","-s",te];e&&t.push("-a",e),t.push("-w");try{return{account:e,raw:ee("/usr/bin/security",t,{encoding:"utf8"}).trim()}}catch{return null}}function ne(){let e=Ge();return e.length||e.push(void 0),e.map(He).filter(t=>t!==null)}var Ke=oe(re(),".claude",".credentials.json");function ie(e,t){let s;try{s=JSON.parse(e)?.claudeAiOauth}catch{return null}if(typeof s!="object"||s===null)return null;let{accessToken:n,refreshToken:i,expiresAt:o,scopes:a}=s;return typeof n!="string"||!n||typeof o!="number"||Number.isNaN(o)?null:{account:t,accessToken:n,refreshToken:typeof i=="string"?i:void 0,expiresAt:o,scopes:Array.isArray(a)?a.filter(l=>typeof l=="string"):void 0}}function We(){try{return ie(se(Ke,"utf8"))}catch{return null}}function Ye(){return ne().map(e=>ie(e.raw,e.account)).filter(e=>e!==null)}function Xe(){return JSON.parse(se(oe(re(),".claude.json"),"utf8"))?.oauthAccount?.organizationUuid}function M(){let e=process.platform==="darwin"?Ye():[];if(!e.length){let n=We();n&&e.push(n)}if(!e.length)throw new Error("no claude.ai credentials (Keychain on macOS / ~/.claude/.credentials.json on Linux). Run `claude` and log in.");e.sort((n,i)=>i.expiresAt-n.expiresAt);let s=e.find(n=>n.expiresAt>Date.now())??e[0];return{...s,orgUuid:Xe(),isExpired:s.expiresAt<=Date.now()}}var C=null;function Ze(){if((!C||C.expiresAt-Date.now()<6e4)&&(C=M(),C.isExpired))throw new Error("No valid claude.ai token (Keychain on macOS / ~/.claude/.credentials.json on Linux). You need a running Claude Code instance (an agent session works) to refresh and persist it.");return C}function Qe(){C=null}function et(){let e=Ze();return{Authorization:`Bearer ${e.accessToken}`,"Content-Type":"application/json","anthropic-version":"2023-06-01","anthropic-beta":"ccr-byoc-2025-07-29","x-organization-uuid":e.orgUuid??""}}async function P(e,t={}){let s=()=>{let i=new Headers(t.headers);for(let[o,a]of Object.entries(et()))i.has(o)||i.set(o,a);return{...t,headers:i}},n=await fetch(e,s());return n.status===401&&(Qe(),n=await fetch(e,s())),n}var h={controlRequest:"control_request",controlCancelRequest:"control_cancel_request",controlResponse:"control_response",result:"result",streamEvent:"stream_event",system:"system",user:"user"},k={canUseTool:"can_use_tool",interrupt:"interrupt",setModel:"set_model"};var q="https://api.anthropic.com";async function ae(e,t){let s={uuid:b(),session_id:e,type:h.user,message:{role:"user",content:t}},n=await P(`${q}/v1/sessions/${e}/events`,{method:"POST",body:JSON.stringify({events:[s]})}),i=await n.text();if(!n.ok)throw new Error(`POST events ${n.status}: ${i.slice(0,200)}`);return{status:n.status,body:i?tt(i):null,sentUuid:s.uuid}}async function ce(e){let t={type:h.controlRequest,request_id:b(),request:{subtype:k.interrupt},uuid:b()},s=await P(`${q}/v1/sessions/${e}/events`,{method:"POST",body:JSON.stringify({events:[t]})}),n=await s.text();if(!s.ok)throw new Error(`POST interrupt ${s.status}: ${n.slice(0,200)}`)}async function de(e,t){let s={type:h.controlRequest,request_id:b(),request:{subtype:k.setModel,model:t},uuid:b()},n=await P(`${q}/v1/sessions/${e}/events`,{method:"POST",body:JSON.stringify({events:[s]})}),i=await n.text();if(!n.ok)throw new Error(`POST set_model ${n.status}: ${i.slice(0,200)}`)}function tt(e){try{return JSON.parse(e)}catch{return e}}function E(e){return typeof e=="object"&&e!==null}function v(e){return typeof e=="string"?e:void 0}function ue(e){let t=E(e.message)?e.message:void 0,s=v(e.type),n=v(t?.role)||s||"system",i=t?.content,o=[];if(typeof i=="string")o.push({kind:"text",text:i});else if(Array.isArray(i))for(let a of i){if(!E(a))continue;let l=v(a.type)??"unknown";l==="thinking"||l==="redacted_thinking"||(l==="text"?o.push({kind:"text",text:v(a.text)??""}):l==="tool_use"?o.push({kind:"tool_use",text:`${v(a.name)??"?"} ${JSON.stringify(a.input||{}).slice(0,300)}`}):l==="tool_result"?o.push({kind:"tool_result",text:nt(a.content)}):o.push({kind:l,text:JSON.stringify(a).slice(0,200)}))}return{uuid:v(e.uuid)??null,type:s,role:n,parts:o}}function le(e){let t=e.find(i=>i.kind==="text")?.text??"";if(t.includes("<local-command-caveat>"))return{type:"caveat"};let s=t.match(/<command-name>([^<]*)<\/command-name>/);if(s)return{type:"command",name:s[1].trim()};let n=t.match(/<local-command-stdout>([\s\S]*?)<\/local-command-stdout>/);return n?{type:"stdout",text:n[1].trim()}:null}function nt(e){return typeof e=="string"?e:Array.isArray(e)?e.map(t=>E(t)?t.type==="text"?v(t.text)??"":`[${v(t.type)??"unknown"}]`:"[unknown]").join(`
3
- `):JSON.stringify(e||"").slice(0,300)}import pe from"ws";var st=3e4,me=2e3,rt=5,O=4003;function ot(e){return e===O?me*2:me}function fe(e,{onEvent:t,onStatus:s}={}){let n=null,i=null,o=0,a=!1;function l(){let f;try{f=M()}catch(u){s?.("unauthorized",u instanceof Error?u.message:String(u));return}let c=`wss://api.anthropic.com/v1/sessions/ws/${e}/subscribe?organization_uuid=${f.orgUuid}`;n=new pe(c,{headers:{Authorization:`Bearer ${f.accessToken}`,"anthropic-version":"2023-06-01"}}),n.on("open",()=>{o=0,s?.("connected"),i=setInterval(()=>{try{n?.ping()}catch{}},st)}),n.on("message",u=>{let p;try{p=JSON.parse(u.toString())}catch{return}p&&typeof p.type=="string"&&t?.(p)}),n.on("close",u=>{i&&clearInterval(i),!a&&(s?.(u===O?"unauthorized":"reconnecting",u===O?`WS ${O}`:void 0),o++<rt?setTimeout(l,ot(u)):s?.("disconnected"))}),n.on("error",()=>{})}return l(),{close(){a=!0,i&&clearInterval(i);try{n?.close()}catch{}},send(f){if(!n||n.readyState!==pe.OPEN)return!1;try{return n.send(JSON.stringify(f)),!0}catch{return!1}}}}var ge="<synthetic>",it={connected:"connected",unauthorized:"unauthorized",disconnected:"disconnected",reconnecting:"reconnecting"};function Se(e){let{client:t,deviceToken:s,sessionId:n}=e,i,o=c=>{typeof c!="string"||!c||c===ge||c===i||(i=c,t.mutation(m.sessions.adoptOrUpsert,{deviceToken:s,sessionId:n,model:c}).catch(()=>{}))},a=fe(e.bridgeSessionId,{onStatus:c=>{let u=it[c]??"reconnecting";t.mutation(m.sessions.setStreamStatus,{deviceToken:s,sessionId:n,streamStatus:u}).catch(()=>{})},onEvent:c=>{l(c)}});async function l(c){let u=c.type;if(u===h.controlRequest){let r=E(c.request)?c.request:{};r.subtype===k.canUseTool&&await t.mutation(m.permissions.open,{deviceToken:s,sessionId:n,requestId:String(c.request_id),toolName:typeof r.tool_name=="string"?r.tool_name:"?",input:r.input??{},description:typeof r.description=="string"?r.description:void 0,suggestions:Array.isArray(r.permission_suggestions)?r.permission_suggestions:void 0}).catch(d=>console.error("permissions.open:",ye(d)));return}if(u===h.controlCancelRequest){await t.mutation(m.permissions.cancel,{deviceToken:s,sessionId:n,requestId:String(c.request_id)}).catch(()=>{});return}if(u===h.result){await t.mutation(m.sessions.adoptOrUpsert,{deviceToken:s,sessionId:n,turnState:"idle"}).catch(()=>{});return}if(u===h.streamEvent)return;if(u===h.system){o(c.model);return}let p=ue(c);if(!p.parts.some(r=>r.text||r.kind!=="text"))return;let g=E(c.message)?c.message:void 0,S=g?.role;if(S==="user"){let r=le(p.parts);if(r){await t.mutation(m.sessions.adoptOrUpsert,{deviceToken:s,sessionId:n,turnState:"idle"}).catch(()=>{}),r.type==="stdout"&&r.text&&await f("tool",p.uuid,[{kind:"text",text:r.text}]);return}}let T=S==="assistant"&&g?.model===ge;if(S==="assistant"&&o(g?.model),T&&p.parts.every(r=>r.kind==="text"&&r.text==="(no content)"))return;let w=S==="assistant"?"assistant":p.parts.some(r=>r.kind==="tool_result")?"tool":"user";await f(w,p.uuid,p.parts,w==="assistant"&&!T?"working":void 0)}async function f(c,u,p,y){let g=p.find(S=>S.kind==="text");await t.mutation(m.messages.ingest,{deviceToken:s,sessionId:n,bridgeUuid:u??void 0,kind:c,ts:Date.now(),parts:p,text:g?.text,preview:g?.text?.slice(0,140),turnState:y}).catch(S=>console.error("messages.ingest:",ye(S)))}return{respondPermission(c,u,p,y){let g=u==="approved"||u==="allow"?{behavior:"allow",updatedInput:p??{}}:{behavior:"deny",message:y??"Denied by the user"};return a.send({type:h.controlResponse,response:{subtype:"success",request_id:c,response:g}})},close:()=>a.close()}}function ye(e){return e instanceof Error?e.message:String(e)}var St=1e3,N=class{constructor(t,s){this.client=t;this.deviceToken=s;_t()}client;deviceToken;sessions=new Map;has(t){return this.sessions.has(t)}getRuntime(t){return this.sessions.get(t)?.runtime??void 0}async start(t,s){this.sessions.has(t)||await this.launch(t,s,["--session-id",at()])}async resume(t,s){this.sessions.has(t)||await this.launch(t,s,["--resume",s.claudeSessionId])}async launch(t,s,n){let i=n[1],o=Ct(s.cwd);dt(o,{recursive:!0}),xt(o);let a=U(mt(),`remote-cc-${Et(s.name)}-${process.pid}-${this.sessions.size}.log`),l=yt.spawn(R(),["--remote-control",s.name,...n,"--permission-mode",s.mode,"--debug-file",a],{name:"xterm-256color",cols:120,rows:40,cwd:o,env:ht()});l.onData(()=>{});let f,c,u=new Promise((g,S)=>{f=g,c=S});u.catch(()=>{});let p={child:l,bridgeSessionId:null,runtime:null,ready:u};this.sessions.set(t,p),l.onExit(()=>{let g=this.sessions.get(t);g&&(g.runtime?.close(),this.sessions.delete(t),this.reportSessionState(t,"stopped","idle"))});let y;try{y=await wt(a)}catch(g){throw c(g),this.sessions.delete(t),L(l),await this.reportSessionState(t,"error","disconnected"),g}p.bridgeSessionId=y,p.runtime=Se({client:this.client,deviceToken:this.deviceToken,sessionId:t,bridgeSessionId:y}),f(),await this.client.mutation(m.sessions.adoptOrUpsert,{deviceToken:this.deviceToken,sessionId:t,bridgeSessionId:y,claudeSessionId:i,agentPid:l.pid,turnState:"idle",streamStatus:"connected"})}async stop(t){let s=this.sessions.get(t);s&&(L(s.child),s.runtime?.close(),this.sessions.delete(t)),await this.reportSessionState(t,"stopped","idle")}async sendMessage(t,s){let n=this.sessions.get(t);if(!n)throw new Error("the session is not running on this agent");if(await n.ready,!n.bridgeSessionId)throw new Error("the session is not running on this agent");await ae(n.bridgeSessionId,s)}async interrupt(t){let s=this.sessions.get(t);if(!s)throw new Error("the session is not running on this agent");if(await s.ready,!s.bridgeSessionId)throw new Error("the session is not running on this agent");await ce(s.bridgeSessionId),await this.reportSessionState(t,"idle")}async setModel(t,s){let n=this.sessions.get(t);if(!n)throw new Error("the session is not running on this agent");if(await n.ready,!n.bridgeSessionId)throw new Error("the session is not running on this agent");await de(n.bridgeSessionId,s)}closeAll(){for(let t of this.sessions.values())L(t.child),t.runtime?.close();this.sessions.clear()}async reportSessionState(t,s,n){await this.client.mutation(m.sessions.adoptOrUpsert,{deviceToken:this.deviceToken,sessionId:t,turnState:s,...n!==void 0?{streamStatus:n}:{}}).catch(()=>{})}};function L(e){try{e.kill()}catch{}}function ht(){return Object.fromEntries(Object.entries(process.env).filter(e=>e[1]!==void 0))}function vt(e){return"session_"+e.slice(4)}function wt(e){return new Promise((t,s)=>{let n=Date.now(),i=setInterval(()=>{try{let o=we(e,"utf8").match(/(?:Created session|Reattaching to session) (cse_[A-Za-z0-9]+)/);if(o){clearInterval(i),t(vt(o[1]));return}}catch{}Date.now()-n>45e3&&(clearInterval(i),s(new Error(`bridgeSessionId was not captured within ${45e3/1e3}s`)))},St)})}function xt(e){try{let t=U(xe(),".claude.json");if(!ve(t))return;let s=JSON.parse(we(t,"utf8")),n=gt(e),i=n;try{i=ut(n)}catch{}s.projects??={};let o=!1;for(let a of new Set([n,i]))s.projects[a]??={},s.projects[a].hasTrustDialogAccepted!==!0&&(s.projects[a].hasTrustDialogAccepted=!0,o=!0);o&&lt(t,JSON.stringify(s,null,2))}catch{}}function _t(){if(process.platform==="darwin")try{let e=pt(import.meta.url),t=ft(e.resolve("node-pty/package.json"));for(let s of["darwin-arm64","darwin-x64"]){let n=U(t,"prebuilds",s,"spawn-helper");ve(n)&&ct(n,493)}}catch{}}function Ct(e){return e.startsWith("~")?U(xe(),e.slice(1)):e}function Et(e){return e.replace(/[^a-zA-Z0-9_-]/g,"_").slice(0,40)}async function _e(e){let t=B(),s=X();if(e.pairCode){let r=e.convexUrl??(j||void 0)??t?.convexUrl;if(!r)throw new Error("Missing --convex-url to pair (or a previous config).");console.log(`\u25B8 pairing with code ${e.pairCode}\u2026`);let d=await Z(Y(r),e.pairCode,void 0,s);t={convexUrl:r,deviceToken:d.deviceToken,deviceId:d.deviceId,userId:d.userId,name:d.name},J(t),console.log(`\u2705 paired as "${d.name}" (device ${d.deviceId})`)}t||(console.error("\u2717 This PC is not paired."),console.error(" Run: remote-cc agent --pair <code> --convex-url <url>"),process.exit(1));let n=t,i=kt(),o=new Tt(n.convexUrl),a=new N(o,n.deviceToken);console.log(`\u25B8 connected to Convex as "${n.name}" (${n.convexUrl})`);let l=Q(o,n.deviceToken,s);try{let r=await o.query(m.sessions.listForAgent,{deviceToken:n.deviceToken});for(let d of r)a.has(d._id)||await o.mutation(m.sessions.adoptOrUpsert,{deviceToken:n.deviceToken,sessionId:d._id,turnState:"error",streamStatus:"disconnected"})}catch(r){console.error("re-adoption:",r instanceof Error?r.message:r)}let f=r=>d=>{/invalid or revoked device token/i.test(d.message)?console.error("\u2717 This PC was revoked from the web. Pair it again with: npx remote-cc agent --pair <code>"):console.error(`\u2717 ${r} subscription failed:`,d.message),w(1)},c=new Set,u=o.onUpdate(m.commands.listPending,{deviceToken:n.deviceToken},r=>{for(let d of r)c.has(d._id)||(c.add(d._id),p(d._id).finally(()=>c.delete(d._id)))},f("command queue"));async function p(r){let d=await o.mutation(m.commands.claim,{deviceToken:n.deviceToken,commandId:r,claimToken:i});if(!(!d.ok||!d.command))try{await K(a,d.command),await o.mutation(m.commands.complete,{deviceToken:n.deviceToken,commandId:r})}catch($){let V=$ instanceof Error?$.message:String($);console.error(`command ${d.command.type} failed:`,V),await o.mutation(m.commands.fail,{deviceToken:n.deviceToken,commandId:r,error:V})}}let y=new Set,g=o.onUpdate(m.permissions.pendingForDevice,{deviceToken:n.deviceToken},r=>{for(let d of r)y.has(d._id)||(y.add(d._id),S(d).finally(()=>y.delete(d._id)))},f("permission decisions"));async function S(r){let d=a.getRuntime(r.sessionId);d?d.respondPermission(r.requestId,r.status,r.decisionInput,r.message):console.error(`decision for session ${r.sessionId} has no local runtime \u2014 cannot respond`),await o.mutation(m.permissions.markConsumed,{deviceToken:n.deviceToken,permissionRequestId:r._id})}let T=!1,w=async(r=0)=>{if(!T){T=!0,console.log(`
4
- \u25B8 disconnecting\u2026`),u(),g(),l(),a.closeAll();try{await o.mutation(m.devices.goingOffline,{deviceToken:n.deviceToken})}catch{}await o.close(),process.exit(r)}};process.on("SIGINT",()=>{w()}),process.on("SIGTERM",()=>{w()}),console.log("\u25B8 listening for commands and permissions\u2026")}function bt(e){let t={};for(let s=0;s<e.length;s++){let n=e[s];if(n==="--pair")t.pairCode=e[++s];else if(n==="--convex-url")t.convexUrl=e[++s];else{if(n==="agent")continue;n.startsWith("--")&&(console.error(`unknown flag: ${n}`),process.exit(1))}}return t}_e(bt(process.argv.slice(2))).catch(e=>{console.error("\u2717",e instanceof Error?e.message:e),process.exit(1)});
2
+ import{ConvexClient as Dt}from"convex/browser";import{randomUUID as Vt}from"crypto";var Z={default:"default",acceptEdits:"acceptEdits",plan:"plan"},Q={opus:"opus",sonnet:"sonnet",haiku:"haiku",fable:"claude-fable-5"},w={starting:"starting",idle:"idle",working:"working",waiting:"waiting",error:"error",stopped:"stopped"},S={connected:"connected",reconnecting:"reconnecting",disconnected:"disconnected",unauthorized:"unauthorized",idle:"idle"},E={user:"user",assistant:"assistant",tool:"tool",permission:"permission"},y={text:"text",toolUse:"tool_use",toolResult:"tool_result"},k={startSession:"start_session",resumeSession:"resume_session",stopSession:"stop_session",sendMessage:"send_message",interruptTurn:"interrupt_turn",setPermissionMode:"set_permission_mode",setModel:"set_model"};var ee={pending:"pending",approved:"approved",denied:"denied",cancelled:"cancelled",expired:"expired"},te={allow:"allow",deny:"deny"};var ne=2e4;var Lt=10*6e4,B=45e3;import{anyApi as Ne,componentsGeneric as De}from"convex/server";var p=Ne;var Jt=De();var se="https://good-emu-457.convex.cloud";import{chmodSync as Ve,existsSync as qe,mkdirSync as Le,readFileSync as je,writeFileSync as Fe}from"fs";import{homedir as Ke}from"os";import{join as oe}from"path";var re=oe(Ke(),".remote-cc"),N=oe(re,"config.json");function ie(){try{return qe(N)?JSON.parse(je(N,"utf8")):null}catch{return null}}function ae(e){Le(re,{recursive:!0}),Fe(N,JSON.stringify(e,null,2));try{Ve(N,384)}catch{}}async function ce(e,t){switch(t.type){case k.startSession:await e.start(R(t),de(t));break;case k.resumeSession:await e.resume(R(t),ze(t));break;case k.stopSession:t.sessionId&&await e.stop(R(t));break;case k.sendMessage:await e.sendMessage(R(t),Be(t).text);break;case k.interruptTurn:await e.interrupt(R(t));break;case k.setPermissionMode:break;case k.setModel:await e.setModel(R(t),Je(t).model);break;default:throw new Error(`unknown command: ${t.type}`)}}function R(e){if(!e.sessionId)throw new Error(`${e.type} without sessionId`);return e.sessionId}function de(e){let t=D(e);return{name:O(e,t,"name"),cwd:O(e,t,"cwd"),mode:ue(e,t,"mode",Z)}}function ze(e){let t=D(e);return{...de(e),claudeSessionId:O(e,t,"claudeSessionId")}}function Be(e){return{text:O(e,D(e),"text")}}function D(e){if(typeof e.args!="object"||e.args===null)throw new Error(`${e.type} with malformed args`);return e.args}function O(e,t,s){let n=t[s];if(typeof n!="string"||!n)throw new Error(`${e.type} without ${s}`);return n}function ue(e,t,s,n){let r=O(e,t,s);if(!Object.values(n).includes(r))throw new Error(`${e.type} with unknown ${s} "${r}"`);return r}function Je(e){return{model:ue(e,D(e),"model",Q)}}function le(e){return e.replace(/\.convex\.cloud\/?$/,".convex.site")}import{execFileSync as Xe}from"child_process";import{arch as Ye,homedir as Ze,release as J,type as Qe}from"os";import{existsSync as We}from"fs";import{homedir as Ge}from"os";import{join as He}from"path";function V(){let e=He(Ge(),".local","bin","claude");return We(e)?e:"claude"}var et="0.1.3";function pe(){return{platform:`${tt()} \xB7 ${Ye()}`,claudeVersion:nt(),baseDir:Ze(),agentVersion:et}}function tt(){let e=Qe();return e==="Darwin"?`macOS ${J()}`:e==="Linux"?`Linux ${J()}`:`${e} ${J()}`}function nt(){try{let t=Xe(V(),["--version"],{encoding:"utf8",timeout:5e3}).match(/\d+\.\d+\.\d+/);return t?t[0]:void 0}catch{return}}async function me(e,t,s,n){let r=await fetch(`${e}/pair/claim`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:t,name:s,platform:n.platform,claudeVersion:n.claudeVersion,baseDir:n.baseDir,agentVersion:n.agentVersion})}),i=await r.text(),a;try{a=JSON.parse(i)}catch{a={}}if(!r.ok)throw new Error(`pairing failed (${r.status}): ${a.error??i.slice(0,200)}`);let{deviceToken:l,deviceId:g,userId:c,name:u}=a;if(!l||!g||!c||!u)throw new Error(`pairing response is missing fields: ${i.slice(0,200)}`);return{deviceToken:l,deviceId:g,userId:c,name:u}}function ge(e,t,s){let n=async l=>{try{await e.mutation(p.devices.heartbeat,{deviceToken:t,...l})}catch(g){console.error("heartbeat error:",g instanceof Error?g.message:g)}},r=()=>n({});n({platform:s.platform,claudeVersion:s.claudeVersion,baseDir:s.baseDir,agentVersion:s.agentVersion});let a=setInterval(()=>{r()},ne);return()=>clearInterval(a)}import{randomUUID as vt}from"crypto";import{chmodSync as wt,existsSync as Me,mkdirSync as xt,readFileSync as Oe,realpathSync as Ct,writeFileSync as kt}from"fs";import{createRequire as _t}from"module";import{homedir as $e,tmpdir as Tt}from"os";import{dirname as bt,join as K,resolve as Et}from"path";import Rt from"node-pty";import{randomUUID as U}from"crypto";import{readFileSync as Se}from"fs";import{homedir as ve}from"os";import{join as we}from"path";import{execFileSync as fe}from"child_process";var ye="Claude Code-credentials";function st(){let e="";try{e=fe("/usr/bin/security",["dump-keychain"],{encoding:"utf8",maxBuffer:64*1024*1024})}catch{}let t=new Set;for(let s of e.split(/\nkeychain: /)){if(!s.includes(`"svce"<blob>="${ye}"`))continue;let n=s.match(/"acct"<blob>="([^"]*)"/);n&&t.add(n[1])}return[...t]}function ot(e){let t=["find-generic-password","-s",ye];e&&t.push("-a",e),t.push("-w");try{return{account:e,raw:fe("/usr/bin/security",t,{encoding:"utf8"}).trim()}}catch{return null}}function he(){let e=st();return e.length||e.push(void 0),e.map(ot).filter(t=>t!==null)}var rt=we(ve(),".claude",".credentials.json");function xe(e,t){let s;try{s=JSON.parse(e)?.claudeAiOauth}catch{return null}if(typeof s!="object"||s===null)return null;let{accessToken:n,refreshToken:r,expiresAt:i,scopes:a}=s;return typeof n!="string"||!n||typeof i!="number"||Number.isNaN(i)?null:{account:t,accessToken:n,refreshToken:typeof r=="string"?r:void 0,expiresAt:i,scopes:Array.isArray(a)?a.filter(l=>typeof l=="string"):void 0}}function it(){try{return xe(Se(rt,"utf8"))}catch{return null}}function at(){return he().map(e=>xe(e.raw,e.account)).filter(e=>e!==null)}function ct(){return JSON.parse(Se(we(ve(),".claude.json"),"utf8"))?.oauthAccount?.organizationUuid}function q(){let e=process.platform==="darwin"?at():[];if(!e.length){let n=it();n&&e.push(n)}if(!e.length)throw new Error("no claude.ai credentials (Keychain on macOS / ~/.claude/.credentials.json on Linux). Run `claude` and log in.");e.sort((n,r)=>r.expiresAt-n.expiresAt);let s=e.find(n=>n.expiresAt>Date.now())??e[0];return{...s,orgUuid:ct(),isExpired:s.expiresAt<=Date.now()}}var I=null;function dt(){if((!I||I.expiresAt-Date.now()<6e4)&&(I=q(),I.isExpired))throw new Error("No valid claude.ai token (Keychain on macOS / ~/.claude/.credentials.json on Linux). You need a running Claude Code instance (an agent session works) to refresh and persist it.");return I}function ut(){I=null}function lt(){let e=dt();return{Authorization:`Bearer ${e.accessToken}`,"Content-Type":"application/json","anthropic-version":"2023-06-01","anthropic-beta":"ccr-byoc-2025-07-29","x-organization-uuid":e.orgUuid??""}}async function L(e,t={}){let s=()=>{let r=new Headers(t.headers);for(let[i,a]of Object.entries(lt()))r.has(i)||r.set(i,a);return{...t,headers:r}},n=await fetch(e,s());return n.status===401&&(ut(),n=await fetch(e,s())),n}var x={controlRequest:"control_request",controlCancelRequest:"control_cancel_request",controlResponse:"control_response",result:"result",streamEvent:"stream_event",system:"system",user:"user"},$={canUseTool:"can_use_tool",interrupt:"interrupt",setModel:"set_model"},T={user:"user",assistant:"assistant"},W={allow:"allow",deny:"deny"};var G="https://api.anthropic.com";async function Ce(e,t){let s={uuid:U(),session_id:e,type:x.user,message:{role:T.user,content:t}},n=await L(`${G}/v1/sessions/${e}/events`,{method:"POST",body:JSON.stringify({events:[s]})}),r=await n.text();if(!n.ok)throw new Error(`POST events ${n.status}: ${r.slice(0,200)}`);return{status:n.status,body:r?pt(r):null,sentUuid:s.uuid}}async function ke(e){let t={type:x.controlRequest,request_id:U(),request:{subtype:$.interrupt},uuid:U()},s=await L(`${G}/v1/sessions/${e}/events`,{method:"POST",body:JSON.stringify({events:[t]})}),n=await s.text();if(!s.ok)throw new Error(`POST interrupt ${s.status}: ${n.slice(0,200)}`)}async function _e(e,t){let s={type:x.controlRequest,request_id:U(),request:{subtype:$.setModel,model:t},uuid:U()},n=await L(`${G}/v1/sessions/${e}/events`,{method:"POST",body:JSON.stringify({events:[s]})}),r=await n.text();if(!n.ok)throw new Error(`POST set_model ${n.status}: ${r.slice(0,200)}`)}function pt(e){try{return JSON.parse(e)}catch{return e}}function A(e){return typeof e=="object"&&e!==null}function _(e){return typeof e=="string"?e:void 0}function Te(e){let t=A(e.message)?e.message:void 0,s=_(e.type),n=_(t?.role)||s||"system",r=t?.content,i=[];if(typeof r=="string")i.push({kind:y.text,text:r});else if(Array.isArray(r))for(let a of r){if(!A(a))continue;let l=_(a.type)??"unknown";l==="thinking"||l==="redacted_thinking"||(l===y.text?i.push({kind:y.text,text:_(a.text)??""}):l===y.toolUse?i.push({kind:y.toolUse,text:`${_(a.name)??"?"} ${JSON.stringify(a.input||{}).slice(0,300)}`}):l===y.toolResult?i.push({kind:y.toolResult,text:mt(a.content)}):i.push({kind:l,text:JSON.stringify(a).slice(0,200)}))}return{uuid:_(e.uuid)??null,type:s,role:n,parts:i}}var P={caveat:"caveat",command:"command",stdout:"stdout"};function be(e){let t=e.find(r=>r.kind===y.text)?.text??"";if(t.includes("<local-command-caveat>"))return{type:P.caveat};let s=t.match(/<command-name>([^<]*)<\/command-name>/);if(s)return{type:P.command,name:s[1].trim()};let n=t.match(/<local-command-stdout>([\s\S]*?)<\/local-command-stdout>/);return n?{type:P.stdout,text:n[1].trim()}:null}function mt(e){return typeof e=="string"?e:Array.isArray(e)?e.map(t=>A(t)?t.type===y.text?_(t.text)??"":`[${_(t.type)??"unknown"}]`:"[unknown]").join(`
3
+ `):JSON.stringify(e||"").slice(0,300)}import Ee from"ws";var gt=3e4,Re=2e3,ft=5,j=4003;function yt(e){return e===j?Re*2:Re}var C={connected:"connected",reconnecting:"reconnecting",disconnected:"disconnected",unauthorized:"unauthorized"};function Ie(e,{onEvent:t,onStatus:s}={}){let n=null,r=null,i=0,a=!1;function l(){let g;try{g=q()}catch(u){s?.(C.unauthorized,u instanceof Error?u.message:String(u));return}let c=`wss://api.anthropic.com/v1/sessions/ws/${e}/subscribe?organization_uuid=${g.orgUuid}`;n=new Ee(c,{headers:{Authorization:`Bearer ${g.accessToken}`,"anthropic-version":"2023-06-01"}}),n.on("open",()=>{i=0,s?.(C.connected),r=setInterval(()=>{try{n?.ping()}catch{}},gt)}),n.on("message",u=>{let m;try{m=JSON.parse(u.toString())}catch{return}m&&typeof m.type=="string"&&t?.(m)}),n.on("close",u=>{r&&clearInterval(r),!a&&(s?.(u===j?C.unauthorized:C.reconnecting,u===j?`WS ${j}`:void 0),i++<ft?setTimeout(l,yt(u)):s?.(C.disconnected))}),n.on("error",()=>{})}return l(),{close(){a=!0,r&&clearInterval(r);try{n?.close()}catch{}},send(g){if(!n||n.readyState!==Ee.OPEN)return!1;try{return n.send(JSON.stringify(g)),!0}catch{return!1}}}}var Pe="<synthetic>",ht=new Set(["/clear","/reset","/new"]),St={[C.connected]:S.connected,[C.unauthorized]:S.unauthorized,[C.disconnected]:S.disconnected,[C.reconnecting]:S.reconnecting};function Ae(e){let{client:t,deviceToken:s,sessionId:n}=e,r,i=c=>{typeof c!="string"||!c||c===Pe||c===r||(r=c,t.mutation(p.sessions.adoptOrUpsert,{deviceToken:s,sessionId:n,model:c}).catch(()=>{}))},a=Ie(e.bridgeSessionId,{onStatus:c=>{let u=St[c]??S.reconnecting;t.mutation(p.sessions.setStreamStatus,{deviceToken:s,sessionId:n,streamStatus:u}).catch(()=>{})},onEvent:c=>{l(c)}});async function l(c){let u=c.type;if(u===x.controlRequest){let o=A(c.request)?c.request:{};o.subtype===$.canUseTool&&await t.mutation(p.permissions.open,{deviceToken:s,sessionId:n,requestId:String(c.request_id),toolName:typeof o.tool_name=="string"?o.tool_name:"?",input:o.input??{},description:typeof o.description=="string"?o.description:void 0,suggestions:Array.isArray(o.permission_suggestions)?o.permission_suggestions:void 0}).catch(d=>console.error("permissions.open:",H(d)));return}if(u===x.controlCancelRequest){await t.mutation(p.permissions.cancel,{deviceToken:s,sessionId:n,requestId:String(c.request_id)}).catch(()=>{});return}if(u===x.result){await t.mutation(p.sessions.adoptOrUpsert,{deviceToken:s,sessionId:n,turnState:w.idle}).catch(()=>{});return}if(u===x.streamEvent)return;if(u===x.system){i(c.model);return}let m=Te(c);if(!m.parts.some(o=>o.text||o.kind!==y.text))return;let f=A(c.message)?c.message:void 0,v=f?.role;if(v===T.user){let o=be(m.parts);if(o){await t.mutation(p.sessions.adoptOrUpsert,{deviceToken:s,sessionId:n,turnState:w.idle}).catch(()=>{}),o.type===P.command&&ht.has(o.name)&&await t.mutation(p.messages.purge,{deviceToken:s,sessionId:n}).catch(d=>console.error("messages.purge:",H(d))),o.type===P.stdout&&o.text&&await g(E.tool,m.uuid,[{kind:y.text,text:o.text}]);return}}let M=v===T.assistant&&f?.model===Pe;if(v===T.assistant&&i(f?.model),M&&m.parts.every(o=>o.kind===y.text&&o.text==="(no content)"))return;let b=v===T.assistant?E.assistant:m.parts.some(o=>o.kind===y.toolResult)?E.tool:E.user;await g(b,m.uuid,m.parts,b===E.assistant&&!M?w.working:void 0)}async function g(c,u,m,h){let f=m.find(v=>v.kind===y.text);await t.mutation(p.messages.ingest,{deviceToken:s,sessionId:n,bridgeUuid:u??void 0,kind:c,ts:Date.now(),parts:m,text:f?.text,preview:f?.text?.slice(0,140),turnState:h}).catch(v=>console.error("messages.ingest:",H(v)))}return{respondPermission(c,u,m,h){let f=u===ee.approved||u===te.allow?{behavior:W.allow,updatedInput:m??{}}:{behavior:W.deny,message:h??"Denied by the user"};return a.send({type:x.controlResponse,response:{subtype:"success",request_id:c,response:f}})},close:()=>a.close()}}function H(e){return e instanceof Error?e.message:String(e)}var It=1e3,F=class{constructor(t,s){this.client=t;this.deviceToken=s;$t()}client;deviceToken;sessions=new Map;has(t){return this.sessions.has(t)}getRuntime(t){return this.sessions.get(t)?.runtime??void 0}async start(t,s){this.sessions.has(t)||await this.launch(t,s,["--session-id",vt()])}async resume(t,s){this.sessions.has(t)||await this.launch(t,s,["--resume",s.claudeSessionId])}async launch(t,s,n){let r=n[1],i=Ut(s.cwd);xt(i,{recursive:!0}),Ot(i);let a=K(Tt(),`remote-cc-${Nt(s.name)}-${process.pid}-${this.sessions.size}.log`),l=Rt.spawn(V(),["--remote-control",s.name,...n,"--permission-mode",s.mode,"--debug-file",a],{name:"xterm-256color",cols:120,rows:40,cwd:i,env:Pt()});l.onData(()=>{});let g,c,u=new Promise((f,v)=>{g=f,c=v});u.catch(()=>{});let m={child:l,bridgeSessionId:null,runtime:null,ready:u};this.sessions.set(t,m),l.onExit(()=>{let f=this.sessions.get(t);f&&(f.runtime?.close(),this.sessions.delete(t),this.reportSessionState(t,w.stopped,S.idle))});let h;try{h=await Mt(a)}catch(f){throw c(f),this.sessions.delete(t),X(l),await this.reportSessionState(t,w.error,S.disconnected),f}m.bridgeSessionId=h,m.runtime=Ae({client:this.client,deviceToken:this.deviceToken,sessionId:t,bridgeSessionId:h}),g(),await this.client.mutation(p.sessions.adoptOrUpsert,{deviceToken:this.deviceToken,sessionId:t,bridgeSessionId:h,claudeSessionId:r,agentPid:l.pid,turnState:w.idle,streamStatus:S.connected})}async stop(t){let s=this.sessions.get(t);s&&(X(s.child),s.runtime?.close(),this.sessions.delete(t)),await this.reportSessionState(t,w.stopped,S.idle)}async sendMessage(t,s){let n=this.sessions.get(t);if(!n)throw new Error("the session is not running on this agent");if(await n.ready,!n.bridgeSessionId)throw new Error("the session is not running on this agent");await Ce(n.bridgeSessionId,s)}async interrupt(t){let s=this.sessions.get(t);if(!s)throw new Error("the session is not running on this agent");if(await s.ready,!s.bridgeSessionId)throw new Error("the session is not running on this agent");await ke(s.bridgeSessionId),await this.reportSessionState(t,w.idle)}async setModel(t,s){let n=this.sessions.get(t);if(!n)throw new Error("the session is not running on this agent");if(await n.ready,!n.bridgeSessionId)throw new Error("the session is not running on this agent");await _e(n.bridgeSessionId,s)}closeAll(){for(let t of this.sessions.values())X(t.child),t.runtime?.close();this.sessions.clear()}async reportSessionState(t,s,n){await this.client.mutation(p.sessions.adoptOrUpsert,{deviceToken:this.deviceToken,sessionId:t,turnState:s,...n!==void 0?{streamStatus:n}:{}}).catch(()=>{})}};function X(e){try{e.kill()}catch{}}function Pt(){return Object.fromEntries(Object.entries(process.env).filter(e=>e[1]!==void 0))}function At(e){return"session_"+e.slice(4)}function Mt(e){return new Promise((t,s)=>{let n=Date.now(),r=setInterval(()=>{try{let i=Oe(e,"utf8").match(/(?:Created session|Reattaching to session) (cse_[A-Za-z0-9]+)/);if(i){clearInterval(r),t(At(i[1]));return}}catch{}Date.now()-n>B&&(clearInterval(r),s(new Error(`bridgeSessionId was not captured within ${B/1e3}s`)))},It)})}function Ot(e){try{let t=K($e(),".claude.json");if(!Me(t))return;let s=JSON.parse(Oe(t,"utf8")),n=Et(e),r=n;try{r=Ct(n)}catch{}s.projects??={};let i=!1;for(let a of new Set([n,r]))s.projects[a]??={},s.projects[a].hasTrustDialogAccepted!==!0&&(s.projects[a].hasTrustDialogAccepted=!0,i=!0);i&&kt(t,JSON.stringify(s,null,2))}catch{}}function $t(){if(process.platform==="darwin")try{let e=_t(import.meta.url),t=bt(e.resolve("node-pty/package.json"));for(let s of["darwin-arm64","darwin-x64"]){let n=K(t,"prebuilds",s,"spawn-helper");Me(n)&&wt(n,493)}}catch{}}function Ut(e){return e.startsWith("~")?K($e(),e.slice(1)):e}function Nt(e){return e.replace(/[^a-zA-Z0-9_-]/g,"_").slice(0,40)}async function Ue(e){let t=ie(),s=pe();if(e.pairCode){let o=e.convexUrl??(se||void 0)??t?.convexUrl;if(!o)throw new Error("Missing --convex-url to pair (or a previous config).");console.log(`\u25B8 pairing with code ${e.pairCode}\u2026`);let d=await me(le(o),e.pairCode,void 0,s);t={convexUrl:o,deviceToken:d.deviceToken,deviceId:d.deviceId,userId:d.userId,name:d.name},ae(t),console.log(`\u2705 paired as "${d.name}" (device ${d.deviceId})`)}t||(console.error("\u2717 This PC is not paired."),console.error(" Run: remote-cc agent --pair <code> --convex-url <url>"),process.exit(1));let n=t,r=Vt(),i=new Dt(n.convexUrl),a=new F(i,n.deviceToken);console.log(`\u25B8 connected to Convex as "${n.name}" (${n.convexUrl})`);let l=ge(i,n.deviceToken,s);try{let o=await i.query(p.sessions.listForAgent,{deviceToken:n.deviceToken});for(let d of o)a.has(d._id)||await i.mutation(p.sessions.adoptOrUpsert,{deviceToken:n.deviceToken,sessionId:d._id,turnState:w.error,streamStatus:S.disconnected})}catch(o){console.error("re-adoption:",o instanceof Error?o.message:o)}let g=o=>d=>{/invalid or revoked device token/i.test(d.message)?console.error("\u2717 This PC was revoked from the web. Pair it again with: npx remote-cc agent --pair <code>"):console.error(`\u2717 ${o} subscription failed:`,d.message),b(1)},c=new Set,u=i.onUpdate(p.commands.listPending,{deviceToken:n.deviceToken},o=>{for(let d of o)c.has(d._id)||(c.add(d._id),m(d._id).finally(()=>c.delete(d._id)))},g("command queue"));async function m(o){let d=await i.mutation(p.commands.claim,{deviceToken:n.deviceToken,commandId:o,claimToken:r});if(!(!d.ok||!d.command))try{await ce(a,d.command),await i.mutation(p.commands.complete,{deviceToken:n.deviceToken,commandId:o})}catch(z){let Y=z instanceof Error?z.message:String(z);console.error(`command ${d.command.type} failed:`,Y),await i.mutation(p.commands.fail,{deviceToken:n.deviceToken,commandId:o,error:Y})}}let h=new Set,f=i.onUpdate(p.permissions.pendingForDevice,{deviceToken:n.deviceToken},o=>{for(let d of o)h.has(d._id)||(h.add(d._id),v(d).finally(()=>h.delete(d._id)))},g("permission decisions"));async function v(o){let d=a.getRuntime(o.sessionId);d?d.respondPermission(o.requestId,o.status,o.decisionInput,o.message):console.error(`decision for session ${o.sessionId} has no local runtime \u2014 cannot respond`),await i.mutation(p.permissions.markConsumed,{deviceToken:n.deviceToken,permissionRequestId:o._id})}let M=!1,b=async(o=0)=>{if(!M){M=!0,console.log(`
4
+ \u25B8 disconnecting\u2026`),u(),f(),l(),a.closeAll();try{await i.mutation(p.devices.goingOffline,{deviceToken:n.deviceToken})}catch{}await i.close(),process.exit(o)}};process.on("SIGINT",()=>{b()}),process.on("SIGTERM",()=>{b()}),console.log("\u25B8 listening for commands and permissions\u2026")}function qt(e){let t={};for(let s=0;s<e.length;s++){let n=e[s];if(n==="--pair")t.pairCode=e[++s];else if(n==="--convex-url")t.convexUrl=e[++s];else{if(n==="agent")continue;n.startsWith("--")&&(console.error(`unknown flag: ${n}`),process.exit(1))}}return t}Ue(qt(process.argv.slice(2))).catch(e=>{console.error("\u2717",e instanceof Error?e.message:e),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maol-1997/remote-cc",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "remote-cc agent: control Claude Code on this PC from the web.",
5
5
  "type": "module",
6
6
  "bin": {