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