@maol-1997/remote-cc 0.1.1 → 0.1.2
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 Tt}from"convex/browser";import{randomUUID as
|
|
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`),
|
|
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&<(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)});
|