@maol-1997/remote-cc 0.1.1 → 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.
- 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 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 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)});
|