@diggerhq/catty 0.3.7 → 0.3.8
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/dist/index.js +15 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{program as
|
|
2
|
+
import{program as m}from"commander";import{Command as Le}from"commander";import Ue from"open";var b="https://api.catty.dev",_=".catty",P="credentials.json";function p(e){return e||(process.env.CATTY_API_ADDR?process.env.CATTY_API_ADDR:b)}function T(e){return new Promise(t=>setTimeout(t,e))}import{homedir as fe}from"os";import{join as M}from"path";import{readFileSync as he,writeFileSync as ye,mkdirSync as _e,unlinkSync as we,existsSync as Se}from"fs";function N(){return M(fe(),_)}function A(){return M(N(),P)}function f(){let e=A();try{let t=he(e,"utf-8");return JSON.parse(t)}catch{return null}}function w(e){let t=N(),s=A();_e(t,{recursive:!0,mode:448}),ye(s,JSON.stringify(e,null,2),{mode:384})}function D(){let e=A();Se(e)&&we(e)}function h(){let e=f();return!(!e||!e.access_token||e.expires_at&&!e.refresh_token&&new Date(e.expires_at)<=new Date)}function O(){return f()?.access_token||null}function $(){return f()?.refresh_token||null}var y=class extends Error{constructor(s,n,o,r){super(o);this.statusCode=s;this.errorCode=n;this.upgradeURL=r;this.name="APIError"}isQuotaExceeded(){return this.statusCode===402&&this.errorCode==="quota_exceeded"}},u=class{baseURL;authToken;constructor(t){this.baseURL=t||process.env.CATTY_API_ADDR||b,this.authToken=O()}async doRequest(t,s,n){let o=new AbortController,r=setTimeout(()=>o.abort(),12e4);try{let i={"Content-Type":"application/json"};return this.authToken&&(i.Authorization=`Bearer ${this.authToken}`),await fetch(`${this.baseURL}${s}`,{method:t,headers:i,body:n?JSON.stringify(n):void 0,signal:o.signal})}finally{clearTimeout(r)}}async doRequestWithRefresh(t,s,n){let o=await this.doRequest(t,s,n);return o.status===401&&await this.refreshAuthToken()&&(o=await this.doRequest(t,s,n)),o}async refreshAuthToken(){let t=$();if(!t)return!1;try{let s=await this.doRequest("POST","/v1/auth/refresh",{refresh_token:t});if(!s.ok)return!1;let n=await s.json();if(!n.access_token)return!1;let o=f();return o&&(o.access_token=n.access_token,n.refresh_token&&(o.refresh_token=n.refresh_token),n.expires_in&&(o.expires_at=new Date(Date.now()+(n.expires_in-30)*1e3).toISOString()),w(o),this.authToken=n.access_token),!0}catch{return!1}}async handleResponse(t){if(!t.ok){let s;try{s=await t.json()}catch{s={error:t.statusText}}throw new y(t.status,s.code||"",s.error||t.statusText,s.upgrade_url)}return t.json()}async createSession(t){let s=await this.doRequestWithRefresh("POST","/v1/sessions",t);return this.handleResponse(s)}async listSessions(){let t=await this.doRequestWithRefresh("GET","/v1/sessions");return this.handleResponse(t)}async getSession(t,s){let n=s?`/v1/sessions/${t}?live=true`:`/v1/sessions/${t}`,o=await this.doRequestWithRefresh("GET",n);return this.handleResponse(o)}async stopSession(t,s){let n=s?`/v1/sessions/${t}?delete=true`:`/v1/sessions/${t}`,o=await this.doRequestWithRefresh("DELETE",n);if(!o.ok){let r;try{r=await o.json()}catch{r={error:o.statusText}}throw new y(o.status,r.code||"",r.error||o.statusText)}}async createCheckoutSession(){let t=await this.doRequestWithRefresh("POST","/v1/checkout");return(await this.handleResponse(t)).url}};import C from"ws";var S=class{wasRaw=!1;cleanupDone=!1;isTerminal(){return process.stdin.isTTY===!0}makeRaw(){if(!this.isTerminal()||this.wasRaw)return;process.stdin.setRawMode(!0),process.stdin.resume(),this.wasRaw=!0;let t=()=>this.restore();process.on("exit",t),process.on("SIGINT",()=>{t(),process.exit(130)}),process.on("SIGTERM",()=>{t(),process.exit(143)})}restore(){if(!this.cleanupDone){if(this.wasRaw&&process.stdin.isTTY){try{process.stdin.setRawMode(!1)}catch{}this.wasRaw=!1}this.cleanupDone=!0}}getSize(){return{cols:process.stdout.columns||80,rows:process.stdout.rows||24}}onResize(t){process.stdout.on("resize",t)}offResize(t){process.stdout.off("resize",t)}};var d={RESIZE:"resize",SIGNAL:"signal",PING:"ping",PONG:"pong",READY:"ready",EXIT:"exit",ERROR:"error",SYNC_BACK:"sync_back",SYNC_BACK_ACK:"sync_back_ack",FILE_CHANGE:"file_change"};function L(e){let t=JSON.parse(e);switch(t.type){case d.RESIZE:return JSON.parse(e);case d.SIGNAL:return JSON.parse(e);case d.PING:return{type:"ping"};case d.PONG:return{type:"pong"};case d.READY:return{type:"ready"};case d.EXIT:return JSON.parse(e);case d.ERROR:return JSON.parse(e);case d.SYNC_BACK:return JSON.parse(e);case d.SYNC_BACK_ACK:return JSON.parse(e);case d.FILE_CHANGE:return JSON.parse(e);default:return t}}function E(e,t){return JSON.stringify({type:d.RESIZE,cols:e,rows:t})}function U(){return JSON.stringify({type:d.PONG})}function V(e){return JSON.stringify({type:d.SYNC_BACK,enabled:e})}import{writeFileSync as ke,unlinkSync as Re,mkdirSync as xe,renameSync as be}from"fs";import{join as B,dirname as F,normalize as v,isAbsolute as Ae,sep as W}from"path";function z(e){let t=v(e.path.replace(/^\.\//,""));if(!t||t===".")return;if(Ae(t)){console.error(`sync-back rejected absolute path: ${t}`);return}if(t===".."||t.startsWith(".."+W)){console.error(`sync-back rejected traversal path: ${t}`);return}let s=process.cwd(),n=B(s,t),o=v(s),r=v(n);if(!r.startsWith(o+W)&&r!==o){console.error(`sync-back rejected path outside base: ${n}`);return}try{if(e.action==="delete")Re(n);else if(e.action==="write"){let i=Buffer.from(e.content||"","base64");xe(F(n),{recursive:!0});let l=B(F(n),`.catty-sync-${Date.now()}`);ke(l,i,{mode:e.mode||420}),be(l,n)}}catch{}}async function k(e){let t=new S;if(!t.isTerminal())throw new Error("stdin is not a terminal");let s=new C(e.connectURL,{headers:{...e.headers,Authorization:`Bearer ${e.connectToken}`}});return new Promise((n,o)=>{let r=!1,i=0,l=()=>{let{cols:a,rows:g}=t.getSize();s.readyState===C.OPEN&&s.send(E(a,g))},c=()=>{t.restore(),t.offResize(l),process.stdin.off("data",I)},I=a=>{s.readyState===C.OPEN&&s.send(a)};s.on("open",()=>{e.syncBack&&(s.send(V(!0)),setTimeout(()=>{r||process.stderr.write(`\r
|
|
3
3
|
(sync-back) No ack from executor yet \u2014 this machine may be running an older catty-exec image without sync-back.\r
|
|
4
|
-
`)},2e3)),
|
|
5
|
-
Process exited with code ${
|
|
6
|
-
`),
|
|
7
|
-
Error: ${
|
|
8
|
-
`);break}case"ping":
|
|
4
|
+
`)},2e3)),t.makeRaw();let{cols:a,rows:g}=t.getSize();s.send(E(a,g)),t.onResize(l),process.stdin.on("data",I)}),s.on("message",(a,g)=>{if(g)process.stdout.write(a);else try{let ge=L(a.toString());me(ge)}catch{}});function me(a){switch(a.type){case"exit":{let g=a;i=g.code,e.onExit?.(g.code),process.stderr.write(`\r
|
|
5
|
+
Process exited with code ${g.code}\r
|
|
6
|
+
`),c(),s.close(),n();break}case"error":{let g=a;process.stderr.write(`\r
|
|
7
|
+
Error: ${g.message}\r
|
|
8
|
+
`);break}case"ping":s.send(U());break;case"file_change":z(a);break;case"sync_back_ack":r=!0;break}}s.on("close",a=>{c(),n()}),s.on("error",a=>{c(),o(a)}),process.on("exit",()=>{c(),s.readyState===C.OPEN&&s.close()})})}import Ie from"archiver";import Pe from"ignore";import{readFileSync as Te,readdirSync as Me,statSync as Ne}from"fs";import{join as G,relative as De}from"path";var Oe=[".git",".git/**","node_modules","node_modules/**","__pycache__","__pycache__/**",".venv",".venv/**","venv","venv/**",".env","*.pyc",".DS_Store","*.log"];async function $e(e){let t=Pe().add(Oe);try{let s=Te(G(e,".gitignore"),"utf-8");t.add(s)}catch{}return new Promise((s,n)=>{let o=[],r=Ie("zip",{zlib:{level:9}});r.on("data",i=>o.push(i)),r.on("end",()=>s(Buffer.concat(o))),r.on("error",n),Y(e,e,t,r),r.finalize()})}function Y(e,t,s,n){let o;try{o=Me(t)}catch{return}for(let r of o){let i=G(t,r),l=De(e,i);if(s.ignores(l))continue;let c;try{c=Ne(i)}catch{continue}if(c.isDirectory()){if(s.ignores(l+"/"))continue;Y(e,i,s,n)}else c.isFile()&&n.file(i,{name:l})}}async function q(e,t,s){let n=process.cwd(),o=await $e(n);if(o.length>104857600)throw new Error(`Workspace too large (${o.length} bytes, max ${104857600})`);let r=await fetch(e,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/zip","fly-force-instance-id":s},body:o});if(!r.ok){let i=await r.text();throw new Error(`Upload failed: ${r.status} - ${i}`)}}function J(e){return e.replace("wss://","https://").replace("ws://","http://").replace("/connect","/upload")}var K=new Le("new").description("Start a new remote agent session").option("--agent <name>","Agent to use: claude or codex","claude").option("--no-upload","Don't upload current directory").option("--no-sync-back","Disable sync-back").option("--enable-prompts","Enable permission prompts (by default, all permissions are auto-approved)",!1).action(async function(){let e=this.opts(),t=p(this.optsWithGlobals().api);h()||(console.error("Not logged in. Please run 'catty login' first."),process.exit(1));let s=new u(t);console.log("Creating session...");let n;switch(e.agent){case"claude":e.enablePrompts?n=["claude-wrapper"]:n=["claude-wrapper","--dangerously-skip-permissions"];break;case"codex":n=["codex"];break;default:console.error(`Unknown agent: ${e.agent} (must be 'claude' or 'codex')`),process.exit(1)}let o;try{o=await s.createSession({agent:e.agent,cmd:n,region:"iad",ttl_sec:7200})}catch(r){if(r instanceof y&&r.isQuotaExceeded()){await Ve(s);return}throw r}if(console.log(`Session created: ${o.label}`),console.log(` Reconnect with: catty connect ${o.label}`),e.upload!==!1){console.log("Uploading workspace...");let r=J(o.connect_url);await q(r,o.connect_token,o.headers["fly-force-instance-id"]),console.log("Workspace uploaded.")}console.log(`Connecting to ${o.connect_url}...`),await k({connectURL:o.connect_url,connectToken:o.connect_token,headers:o.headers,syncBack:e.syncBack!==!1})});async function Ve(e){console.error(""),console.error("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),console.error(" Free tier quota exceeded (1M tokens/month)"),console.error(" Upgrade to Pro for unlimited usage."),console.error("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),console.error("");try{let t=await e.createCheckoutSession();console.error("Opening upgrade page in your browser..."),await Ue(t)}catch(t){console.error(`Failed to create checkout session: ${t}`),console.error("Please visit https://catty.dev to upgrade.")}}import{Command as Be}from"commander";var Z=new Be("connect").description("Reconnect to an existing session").argument("<label>","Session label (e.g., brave-tiger-1234)").option("--sync-back","Sync remote file changes back to local",!0).option("--no-sync-back","Disable sync-back").action(async function(e){let t=this.opts(),s=p(this.optsWithGlobals().api);h()||(console.error("Not logged in. Please run 'catty login' first."),process.exit(1));let n=new u(s);console.log(`Looking up session ${e}...`);let o=await n.getSession(e,!0);if(o.status==="stopped")throw new Error(`Session ${o.label} is stopped`);if(o.machine_state&&o.machine_state!=="started")throw new Error(`Machine is not running (state: ${o.machine_state})`);console.log(`Reconnecting to ${o.label}...`),await k({connectURL:o.connect_url,connectToken:o.connect_token,headers:{"fly-force-instance-id":o.machine_id},syncBack:t.syncBack})});import{Command as Fe}from"commander";var X=new Fe("list").aliases(["ls"]).description("List all sessions").action(async function(){let e=p(this.optsWithGlobals().api),s=await new u(e).listSessions();if(s.length===0){console.log("No sessions found");return}console.log("LABEL STATUS REGION CREATED");for(let o of s){let r=We(new Date(o.created_at)),i=[o.label.padEnd(22),o.status.padEnd(9),o.region.padEnd(7),r].join(" ");console.log(i)}});function We(e){let t=Math.floor((Date.now()-e.getTime())/1e3);if(t<60)return`${t}s ago`;let s=Math.floor(t/60);if(s<60)return`${s}m ago`;let n=Math.floor(s/60);return n<24?`${n}h ago`:`${Math.floor(n/24)}d ago`}import{Command as ze}from"commander";var H=new ze("stop").description("Stop a session").argument("<label>","Session ID or label").option("--delete","Delete the machine after stopping",!1).action(async function(e){let t=this.opts(),s=p(this.optsWithGlobals().api);await new u(s).stopSession(e,t.delete),t.delete?console.log(`Session ${e} stopped and deleted`):console.log(`Session ${e} stopped`)});import{Command as je}from"commander";var Q=new je("stop-all-sessions-dangerously").description("Stop and delete ALL sessions").option("--yes-i-mean-it","Confirm you want to stop all sessions",!1).action(async function(){let e=this.opts(),t=p(this.optsWithGlobals().api);if(!e.yesIMeanIt)throw new Error("Must pass --yes-i-mean-it to confirm");let s=new u(t),n=await s.listSessions();if(n.length===0){console.log("No sessions to stop");return}console.log(`Stopping ${n.length} sessions...`);for(let o of n){process.stdout.write(` Stopping ${o.session_id}... `);try{await s.stopSession(o.session_id,!0),console.log("done")}catch(r){console.log(`ERROR: ${r}`)}}});import{Command as Ge}from"commander";import Ye from"open";var ee=new Ge("login").description("Log in to Catty").action(async function(){let e=p(this.optsWithGlobals().api);if(h()){let r=f();console.log(`Already logged in as ${r?.email}`),console.log("Run 'catty logout' to log out first");return}console.log("Starting login...");let t=await fetch(`${e}/v1/auth/device`,{method:"POST",headers:{"Content-Type":"application/json"},body:"{}"});if(!t.ok)throw new Error(`Failed to start auth: ${t.statusText}`);let s=await t.json();console.log(`
|
|
9
9
|
Your confirmation code:
|
|
10
|
-
`),console.log(` ${
|
|
11
|
-
`),console.log(`Opening ${
|
|
12
|
-
`),await
|
|
13
|
-
Logged in as ${i.user?.email}`),console.log("You can now run 'catty new' to start a session");return}}}throw new Error("Authentication timed out")});import{Command as
|
|
10
|
+
`),console.log(` ${s.user_code}
|
|
11
|
+
`),console.log(`Opening ${s.verification_uri_complete}
|
|
12
|
+
`),await Ye(s.verification_uri_complete),console.log("Waiting for authentication...");let n=(s.interval||5)*1e3,o=Date.now()+s.expires_in*1e3;for(;Date.now()<o;){await T(n);let i=await(await fetch(`${e}/v1/auth/device/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:s.device_code})})).json();if(!i.pending){if(i.error)throw new Error(i.error);if(i.access_token){w({access_token:i.access_token,refresh_token:i.refresh_token,user_id:i.user?.id||"",email:i.user?.email||"",expires_at:i.expires_in?new Date(Date.now()+(i.expires_in-30)*1e3).toISOString():void 0}),console.log(`
|
|
13
|
+
Logged in as ${i.user?.email}`),console.log("You can now run 'catty new' to start a session");return}}}throw new Error("Authentication timed out")});import{Command as qe}from"commander";var te=new qe("logout").description("Log out of Catty").action(async()=>{if(!h()){console.log("Not logged in");return}let t=f()?.email||"";D(),console.log(t?`Logged out from ${t}`:"Logged out")});import{Command as Je}from"commander";var oe=new Je("version").description("Print the version number").action(()=>{console.log("0.3.8")});import{Command as it}from"commander";import{readFileSync as Ke,writeFileSync as Ze,existsSync as se,mkdirSync as Xe}from"fs";import{join as ne}from"path";import{homedir as re}from"os";import{createInterface as He}from"readline";import{spawn as Qe}from"child_process";var et=900*1e3,tt=2880*60*1e3,ot="https://registry.npmjs.org/@diggerhq/catty/latest";function ie(){return ne(re(),_,"version-cache.json")}function ae(){try{let e=ie();if(!se(e))return null;let t=Ke(e,"utf-8");return JSON.parse(t)}catch{return null}}function ce(e,t){try{let s=ie(),n=ne(re(),_);se(n)||Xe(n,{recursive:!0});let o=ae(),r={latestVersion:e,lastChecked:Date.now(),declinedVersion:t?e:o?.declinedVersion,declinedAt:t?Date.now():o?.declinedAt};Ze(s,JSON.stringify(r,null,2))}catch{}}async function st(){try{let e=await fetch(ot,{signal:AbortSignal.timeout(5e3)});return e.ok&&(await e.json()).version||null}catch{return null}}function nt(e,t){let s=e.split(".").map(Number),n=t.split(".").map(Number);for(let o=0;o<3;o++){let r=s[o]||0,i=n[o]||0;if(i>r)return!0;if(i<r)return!1}return!1}async function R(){let e="0.3.8";if(e==="dev")return{updateAvailable:!1,currentVersion:e,latestVersion:null,shouldPrompt:!1};let t=ae(),s=Date.now(),n=null;if(t&&s-t.lastChecked<et?n=t.latestVersion:(n=await st(),n&&ce(n)),!n)return{updateAvailable:!1,currentVersion:e,latestVersion:null,shouldPrompt:!1};let o=nt(e,n),r=o;return o&&t?.declinedVersion===n&&t.declinedAt&&(r=s-t.declinedAt>=tt),{updateAvailable:o,currentVersion:e,latestVersion:n,shouldPrompt:r}}function le(e,t){console.log(""),console.log(`\x1B[33mUpdate available:\x1B[0m \x1B[2m${e}\x1B[0m \u2192 \x1B[32m${t}\x1B[0m`)}async function de(){let e=He({input:process.stdin,output:process.stdout});return new Promise(t=>{e.question("Would you like to update? (Y/n): ",s=>{e.close();let n=s.trim().toLowerCase();t(n===""||n==="y"||n==="yes")})})}function pe(e){ce(e,!0)}function rt(){let e=process.env.npm_config_user_agent||"";return e.includes("yarn")?"yarn":e.includes("pnpm")?"pnpm":e.includes("bun")?"bun":"npm"}async function x(e,t){console.log(`
|
|
14
|
+
Updating from ${e} to ${t}...
|
|
15
|
+
`);let s=rt(),n,o;switch(s){case"yarn":n="yarn",o=["global","add","@diggerhq/catty@latest"];break;case"pnpm":n="pnpm",o=["add","-g","@diggerhq/catty@latest"];break;case"bun":n="bun",o=["install","-g","@diggerhq/catty@latest"];break;default:n="npm",o=["install","-g","@diggerhq/catty@latest"]}return new Promise((r,i)=>{let l=Qe(n,o,{stdio:"inherit",shell:process.platform==="win32"});l.on("close",c=>{c===0?(console.log(`
|
|
16
|
+
\x1B[32m\u2713\x1B[0m Successfully updated to version ${t}`),r()):(console.error(`
|
|
17
|
+
\x1B[31m\u2717\x1B[0m Update failed with exit code ${c}`),i(new Error(`Update process exited with code ${c}`)))}),l.on("error",c=>{console.error(`
|
|
18
|
+
\x1B[31m\u2717\x1B[0m Failed to run update command: ${c.message}`),i(c)})})}var ue=new it("update").description("Update catty to the latest version").action(async()=>{try{let e="0.3.8";if(e==="dev"){console.log("Cannot update in development mode.");return}console.log("Checking for updates...");let{updateAvailable:t,latestVersion:s}=await R();if(!t||!s){console.log(`You are already using the latest version (${e}).`);return}await x(e,s)}catch(e){e instanceof Error&&console.error(`Error: ${e.message}`),process.exit(1)}});var at="0.3.8";m.name("catty").description("Catty - Remote AI agent sessions").option("--api <url>","API server address").version(at);m.addCommand(K);m.addCommand(Z);m.addCommand(X);m.addCommand(H);m.addCommand(Q,{hidden:!0});m.addCommand(ee);m.addCommand(te);m.addCommand(oe);m.addCommand(ue);m.exitOverride();async function ct(){try{await m.parseAsync(process.argv);let e=process.argv[2];if(e&&!["version","update","-v","--version","-h","--help","help"].includes(e)){let{updateAvailable:s,currentVersion:n,latestVersion:o,shouldPrompt:r}=await R();if(s&&o&&r)if(le(n,o),await de())try{await x(n,o)}catch{}else pe(o)}}catch(e){e instanceof Error?(e.name==="CommanderError"&&["commander.helpDisplayed","commander.version"].includes(e.code||"")&&process.exit(0),console.error(`Error: ${e.message}`)):console.error(`Error: ${e}`),process.exit(1)}}ct();
|
|
14
19
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/new.ts","../src/lib/config.ts","../src/lib/auth.ts","../src/lib/api-client.ts","../src/lib/websocket.ts","../src/lib/terminal.ts","../src/protocol/messages.ts","../src/lib/syncback.ts","../src/lib/workspace.ts","../src/commands/connect.ts","../src/commands/list.ts","../src/commands/stop.ts","../src/commands/stopall.ts","../src/commands/login.ts","../src/commands/logout.ts","../src/commands/version.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { program } from 'commander';\nimport { newCommand } from './commands/new.js';\nimport { connectCommand } from './commands/connect.js';\nimport { listCommand } from './commands/list.js';\nimport { stopCommand } from './commands/stop.js';\nimport { stopAllCommand } from './commands/stopall.js';\nimport { loginCommand } from './commands/login.js';\nimport { logoutCommand } from './commands/logout.js';\nimport { versionCommand } from './commands/version.js';\n\n// VERSION is replaced at build time by tsup\ndeclare const __VERSION__: string;\nconst version = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'dev';\n\nprogram\n .name('catty')\n .description('Catty - Remote AI agent sessions')\n .option('--api <url>', 'API server address')\n .version(version);\n\nprogram.addCommand(newCommand);\nprogram.addCommand(connectCommand);\nprogram.addCommand(listCommand);\nprogram.addCommand(stopCommand);\nprogram.addCommand(stopAllCommand, { hidden: true });\nprogram.addCommand(loginCommand);\nprogram.addCommand(logoutCommand);\nprogram.addCommand(versionCommand);\n\n// Handle errors gracefully\nprogram.exitOverride();\n\nasync function main() {\n try {\n await program.parseAsync(process.argv);\n } catch (err: unknown) {\n if (err instanceof Error) {\n // Commander throws for help/version, ignore those\n if (\n err.name === 'CommanderError' &&\n ['commander.helpDisplayed', 'commander.version'].includes(\n (err as { code?: string }).code || ''\n )\n ) {\n process.exit(0);\n }\n\n console.error(`Error: ${err.message}`);\n } else {\n console.error(`Error: ${err}`);\n }\n process.exit(1);\n }\n}\n\nmain();\n","import { Command } from 'commander';\nimport open from 'open';\nimport { getAPIAddr } from '../lib/config.js';\nimport { isLoggedIn } from '../lib/auth.js';\nimport { APIClient, APIError } from '../lib/api-client.js';\nimport { connectToSession } from '../lib/websocket.js';\nimport { uploadWorkspace, buildUploadURL } from '../lib/workspace.js';\n\nexport const newCommand = new Command('new')\n .description('Start a new remote agent session')\n .option('--agent <name>', 'Agent to use: claude or codex', 'claude')\n .option('--no-upload', \"Don't upload current directory\")\n .option('--no-sync-back', 'Disable sync-back')\n .option(\n '--enable-prompts',\n 'Enable permission prompts (by default, all permissions are auto-approved)',\n false\n )\n .action(async function (this: Command) {\n const opts = this.opts();\n const apiAddr = getAPIAddr(this.optsWithGlobals().api);\n\n if (!isLoggedIn()) {\n console.error(\"Not logged in. Please run 'catty login' first.\");\n process.exit(1);\n }\n\n const client = new APIClient(apiAddr);\n\n console.log('Creating session...');\n\n // Determine command arguments based on agent and prompts setting\n let cmdArgs: string[];\n switch (opts.agent) {\n case 'claude':\n if (opts.enablePrompts) {\n // User wants prompts - don't skip permissions\n cmdArgs = ['claude-wrapper'];\n } else {\n // Default: auto-approve all permissions\n cmdArgs = ['claude-wrapper', '--dangerously-skip-permissions'];\n }\n break;\n case 'codex':\n cmdArgs = ['codex'];\n break;\n default:\n console.error(\n `Unknown agent: ${opts.agent} (must be 'claude' or 'codex')`\n );\n process.exit(1);\n }\n\n let session;\n try {\n session = await client.createSession({\n agent: opts.agent,\n cmd: cmdArgs,\n region: 'iad',\n ttl_sec: 7200,\n });\n } catch (err) {\n if (err instanceof APIError && err.isQuotaExceeded()) {\n await handleQuotaExceeded(client);\n return;\n }\n throw err;\n }\n\n console.log(`Session created: ${session.label}`);\n console.log(` Reconnect with: catty connect ${session.label}`);\n\n // Upload workspace\n if (opts.upload !== false) {\n console.log('Uploading workspace...');\n const uploadURL = buildUploadURL(session.connect_url);\n\n await uploadWorkspace(\n uploadURL,\n session.connect_token,\n session.headers['fly-force-instance-id']\n );\n console.log('Workspace uploaded.');\n }\n\n console.log(`Connecting to ${session.connect_url}...`);\n\n await connectToSession({\n connectURL: session.connect_url,\n connectToken: session.connect_token,\n headers: session.headers,\n syncBack: opts.syncBack !== false,\n });\n });\n\nasync function handleQuotaExceeded(client: APIClient): Promise<void> {\n console.error('');\n console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');\n console.error(' Free tier quota exceeded (1M tokens/month)');\n console.error(' Upgrade to Pro for unlimited usage.');\n console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');\n console.error('');\n\n try {\n const checkoutURL = await client.createCheckoutSession();\n console.error('Opening upgrade page in your browser...');\n await open(checkoutURL);\n } catch (err) {\n console.error(`Failed to create checkout session: ${err}`);\n console.error('Please visit https://catty.dev to upgrade.');\n }\n}\n","export const DEFAULT_API_ADDR = 'https://api.catty.dev';\nexport const CREDENTIALS_DIR = '.catty';\nexport const CREDENTIALS_FILE = 'credentials.json';\nexport const MAX_UPLOAD_SIZE = 100 * 1024 * 1024; // 100MB\n\n// Timeouts\nexport const API_TIMEOUT_MS = 120_000; // 120 seconds for API requests (machine creation can be slow)\nexport const WS_WRITE_TIMEOUT_MS = 10_000; // 10 seconds for WebSocket writes\nexport const WS_READ_TIMEOUT_MS = 60_000; // 60 seconds (must be > 25s ping interval)\nexport const SYNC_BACK_ACK_TIMEOUT_MS = 2_000; // Warn if no sync-back ack after 2s\n\n// WebSocket close codes\nexport const WS_POLICY_VIOLATION = 1008; // Connection replaced by new one\n\n// Helper to get API address (checks flag, env var, or default)\nexport function getAPIAddr(cliOption?: string): string {\n if (cliOption) return cliOption;\n if (process.env.CATTY_API_ADDR) return process.env.CATTY_API_ADDR;\n return DEFAULT_API_ADDR;\n}\n\n// Sleep helper for polling\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import { homedir } from 'os';\nimport { join } from 'path';\nimport {\n readFileSync,\n writeFileSync,\n mkdirSync,\n unlinkSync,\n existsSync,\n} from 'fs';\nimport { CREDENTIALS_DIR, CREDENTIALS_FILE } from './config.js';\nimport type { Credentials } from '../types/index.js';\n\nexport function getCredentialsDir(): string {\n return join(homedir(), CREDENTIALS_DIR);\n}\n\nexport function getCredentialsPath(): string {\n return join(getCredentialsDir(), CREDENTIALS_FILE);\n}\n\nexport function loadCredentials(): Credentials | null {\n const path = getCredentialsPath();\n try {\n const content = readFileSync(path, 'utf-8');\n return JSON.parse(content) as Credentials;\n } catch {\n return null;\n }\n}\n\nexport function saveCredentials(creds: Credentials): void {\n const dir = getCredentialsDir();\n const path = getCredentialsPath();\n\n // Create directory with 0700 permissions\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n\n // Write file with 0600 permissions\n writeFileSync(path, JSON.stringify(creds, null, 2), { mode: 0o600 });\n}\n\nexport function deleteCredentials(): void {\n const path = getCredentialsPath();\n if (existsSync(path)) {\n unlinkSync(path);\n }\n}\n\nexport function isLoggedIn(): boolean {\n const creds = loadCredentials();\n if (!creds) return false;\n\n // Check if token exists\n if (!creds.access_token) return false;\n\n // If there's an expiry and no refresh token, check if expired\n if (creds.expires_at && !creds.refresh_token) {\n const expiresAt = new Date(creds.expires_at);\n if (expiresAt <= new Date()) {\n return false;\n }\n }\n\n // If we have a refresh token, we can refresh even if access token expired\n return true;\n}\n\nexport function getAccessToken(): string | null {\n const creds = loadCredentials();\n return creds?.access_token || null;\n}\n\nexport function getRefreshToken(): string | null {\n const creds = loadCredentials();\n return creds?.refresh_token || null;\n}\n","import {\n DEFAULT_API_ADDR,\n API_TIMEOUT_MS,\n} from './config.js';\nimport {\n getAccessToken,\n getRefreshToken,\n loadCredentials,\n saveCredentials,\n} from './auth.js';\nimport type {\n CreateSessionRequest,\n CreateSessionResponse,\n SessionInfo,\n APIErrorResponse,\n} from '../types/index.js';\n\nexport class APIError extends Error {\n constructor(\n public statusCode: number,\n public errorCode: string,\n message: string,\n public upgradeURL?: string\n ) {\n super(message);\n this.name = 'APIError';\n }\n\n isQuotaExceeded(): boolean {\n return this.statusCode === 402 && this.errorCode === 'quota_exceeded';\n }\n}\n\nexport class APIClient {\n private baseURL: string;\n private authToken: string | null;\n\n constructor(baseURL?: string) {\n this.baseURL = baseURL || process.env.CATTY_API_ADDR || DEFAULT_API_ADDR;\n this.authToken = getAccessToken();\n }\n\n private async doRequest(\n method: string,\n path: string,\n body?: unknown\n ): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), API_TIMEOUT_MS);\n\n try {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (this.authToken) {\n headers['Authorization'] = `Bearer ${this.authToken}`;\n }\n\n const response = await fetch(`${this.baseURL}${path}`, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n return response;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private async doRequestWithRefresh(\n method: string,\n path: string,\n body?: unknown\n ): Promise<Response> {\n let response = await this.doRequest(method, path, body);\n\n if (response.status === 401) {\n const refreshed = await this.refreshAuthToken();\n if (refreshed) {\n response = await this.doRequest(method, path, body);\n }\n }\n\n return response;\n }\n\n private async refreshAuthToken(): Promise<boolean> {\n const refreshToken = getRefreshToken();\n if (!refreshToken) return false;\n\n try {\n const response = await this.doRequest('POST', '/v1/auth/refresh', {\n refresh_token: refreshToken,\n });\n\n if (!response.ok) return false;\n\n const data = await response.json();\n if (!data.access_token) return false;\n\n // Update stored credentials\n const creds = loadCredentials();\n if (creds) {\n creds.access_token = data.access_token;\n if (data.refresh_token) {\n creds.refresh_token = data.refresh_token;\n }\n if (data.expires_in) {\n creds.expires_at = new Date(\n Date.now() + (data.expires_in - 30) * 1000\n ).toISOString();\n }\n saveCredentials(creds);\n this.authToken = data.access_token;\n }\n\n return true;\n } catch {\n return false;\n }\n }\n\n private async handleResponse<T>(response: Response): Promise<T> {\n if (!response.ok) {\n let errorData: APIErrorResponse;\n try {\n errorData = await response.json();\n } catch {\n errorData = { error: response.statusText };\n }\n\n throw new APIError(\n response.status,\n errorData.code || '',\n errorData.error || response.statusText,\n errorData.upgrade_url\n );\n }\n\n return response.json() as Promise<T>;\n }\n\n async createSession(req: CreateSessionRequest): Promise<CreateSessionResponse> {\n const response = await this.doRequestWithRefresh('POST', '/v1/sessions', req);\n return this.handleResponse<CreateSessionResponse>(response);\n }\n\n async listSessions(): Promise<SessionInfo[]> {\n const response = await this.doRequestWithRefresh('GET', '/v1/sessions');\n return this.handleResponse<SessionInfo[]>(response);\n }\n\n async getSession(idOrLabel: string, live?: boolean): Promise<SessionInfo> {\n const path = live\n ? `/v1/sessions/${idOrLabel}?live=true`\n : `/v1/sessions/${idOrLabel}`;\n const response = await this.doRequestWithRefresh('GET', path);\n return this.handleResponse<SessionInfo>(response);\n }\n\n async stopSession(idOrLabel: string, del?: boolean): Promise<void> {\n const path = del\n ? `/v1/sessions/${idOrLabel}?delete=true`\n : `/v1/sessions/${idOrLabel}`;\n const response = await this.doRequestWithRefresh('DELETE', path);\n\n if (!response.ok) {\n let errorData: APIErrorResponse;\n try {\n errorData = await response.json();\n } catch {\n errorData = { error: response.statusText };\n }\n throw new APIError(\n response.status,\n errorData.code || '',\n errorData.error || response.statusText\n );\n }\n }\n\n async createCheckoutSession(): Promise<string> {\n const response = await this.doRequestWithRefresh('POST', '/v1/checkout');\n const data = await this.handleResponse<{ url: string }>(response);\n return data.url;\n }\n}\n","import WebSocket from 'ws';\nimport { Terminal } from './terminal.js';\nimport {\n SYNC_BACK_ACK_TIMEOUT_MS,\n WS_POLICY_VIOLATION,\n} from './config.js';\nimport {\n parseMessage,\n createResizeMessage,\n createPongMessage,\n createSyncBackMessage,\n type Message,\n type ExitMessage,\n type ErrorMessage,\n type FileChangeMessage,\n} from '../protocol/messages.js';\nimport { applyRemoteFileChange } from './syncback.js';\n\nexport interface WebSocketConnectOptions {\n connectURL: string;\n connectToken: string;\n headers: Record<string, string>;\n syncBack: boolean;\n onExit?: (code: number) => void;\n}\n\nexport async function connectToSession(\n opts: WebSocketConnectOptions\n): Promise<void> {\n const terminal = new Terminal();\n\n if (!terminal.isTerminal()) {\n throw new Error('stdin is not a terminal');\n }\n\n const ws = new WebSocket(opts.connectURL, {\n headers: {\n ...opts.headers,\n Authorization: `Bearer ${opts.connectToken}`,\n },\n });\n\n return new Promise((resolve, reject) => {\n let syncBackAcked = false;\n let exitCode = 0;\n\n const handleResize = () => {\n const { cols, rows } = terminal.getSize();\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(createResizeMessage(cols, rows));\n }\n };\n\n const cleanup = () => {\n terminal.restore();\n terminal.offResize(handleResize);\n process.stdin.off('data', handleStdinData);\n };\n\n const handleStdinData = (data: Buffer) => {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(data); // Binary\n }\n };\n\n ws.on('open', () => {\n // Enable sync-back if requested\n if (opts.syncBack) {\n ws.send(createSyncBackMessage(true));\n\n // Warn if no ack after timeout\n setTimeout(() => {\n if (!syncBackAcked) {\n process.stderr.write(\n '\\r\\n(sync-back) No ack from executor yet — this machine may be running an older catty-exec image without sync-back.\\r\\n'\n );\n }\n }, SYNC_BACK_ACK_TIMEOUT_MS);\n }\n\n // Enter raw mode\n terminal.makeRaw();\n\n // Send initial size\n const { cols, rows } = terminal.getSize();\n ws.send(createResizeMessage(cols, rows));\n\n // Handle resize\n terminal.onResize(handleResize);\n\n // Relay stdin -> WebSocket\n process.stdin.on('data', handleStdinData);\n });\n\n // Relay WebSocket -> stdout\n ws.on('message', (data: WebSocket.RawData, isBinary: boolean) => {\n if (isBinary) {\n process.stdout.write(data as Buffer);\n } else {\n try {\n const msg = parseMessage(data.toString());\n handleControlMessage(msg);\n } catch {\n // Ignore parse errors\n }\n }\n });\n\n function handleControlMessage(msg: Message) {\n switch (msg.type) {\n case 'exit': {\n const exitMsg = msg as ExitMessage;\n exitCode = exitMsg.code;\n opts.onExit?.(exitMsg.code);\n process.stderr.write(`\\r\\nProcess exited with code ${exitMsg.code}\\r\\n`);\n cleanup();\n ws.close();\n resolve();\n break;\n }\n case 'error': {\n const errorMsg = msg as ErrorMessage;\n process.stderr.write(`\\r\\nError: ${errorMsg.message}\\r\\n`);\n break;\n }\n case 'ping':\n ws.send(createPongMessage());\n break;\n case 'file_change':\n applyRemoteFileChange(msg as FileChangeMessage);\n break;\n case 'sync_back_ack':\n syncBackAcked = true;\n break;\n }\n }\n\n ws.on('close', (code: number) => {\n cleanup();\n // Code 1008 (WS_POLICY_VIOLATION) = connection replaced by new one\n // This is a clean termination, not an error\n if (code === WS_POLICY_VIOLATION) {\n resolve();\n } else {\n resolve();\n }\n });\n\n ws.on('error', (err: Error) => {\n cleanup();\n reject(err);\n });\n\n // Handle process exit\n process.on('exit', () => {\n cleanup();\n if (ws.readyState === WebSocket.OPEN) {\n ws.close();\n }\n });\n });\n}\n","export class Terminal {\n private wasRaw = false;\n private cleanupDone = false;\n\n isTerminal(): boolean {\n return process.stdin.isTTY === true;\n }\n\n makeRaw(): void {\n if (!this.isTerminal()) return;\n if (this.wasRaw) return;\n\n process.stdin.setRawMode(true);\n process.stdin.resume();\n this.wasRaw = true;\n\n // Ensure terminal is restored on process exit\n const cleanup = () => this.restore();\n\n process.on('exit', cleanup);\n\n process.on('SIGINT', () => {\n cleanup();\n process.exit(130); // 128 + SIGINT(2)\n });\n\n process.on('SIGTERM', () => {\n cleanup();\n process.exit(143); // 128 + SIGTERM(15)\n });\n }\n\n restore(): void {\n if (this.cleanupDone) return;\n if (this.wasRaw && process.stdin.isTTY) {\n try {\n process.stdin.setRawMode(false);\n } catch {\n // Ignore errors during cleanup\n }\n this.wasRaw = false;\n }\n this.cleanupDone = true;\n }\n\n getSize(): { cols: number; rows: number } {\n return {\n cols: process.stdout.columns || 80,\n rows: process.stdout.rows || 24,\n };\n }\n\n onResize(callback: () => void): void {\n process.stdout.on('resize', callback);\n }\n\n offResize(callback: () => void): void {\n process.stdout.off('resize', callback);\n }\n}\n","export const MessageType = {\n RESIZE: 'resize',\n SIGNAL: 'signal',\n PING: 'ping',\n PONG: 'pong',\n READY: 'ready',\n EXIT: 'exit',\n ERROR: 'error',\n SYNC_BACK: 'sync_back',\n SYNC_BACK_ACK: 'sync_back_ack',\n FILE_CHANGE: 'file_change',\n} as const;\n\nexport interface BaseMessage {\n type: string;\n}\n\nexport interface ResizeMessage {\n type: 'resize';\n cols: number;\n rows: number;\n}\n\nexport interface SignalMessage {\n type: 'signal';\n name: string;\n}\n\nexport interface PingMessage {\n type: 'ping';\n}\n\nexport interface PongMessage {\n type: 'pong';\n}\n\nexport interface ReadyMessage {\n type: 'ready';\n}\n\nexport interface ExitMessage {\n type: 'exit';\n code: number;\n signal: string | null;\n}\n\nexport interface ErrorMessage {\n type: 'error';\n message: string;\n}\n\nexport interface SyncBackMessage {\n type: 'sync_back';\n enabled: boolean;\n}\n\nexport interface SyncBackAckMessage {\n type: 'sync_back_ack';\n enabled: boolean;\n workspace_dir?: string;\n interval_ms?: number;\n}\n\nexport interface FileChangeMessage {\n type: 'file_change';\n action: 'write' | 'delete';\n path: string;\n content?: string; // base64 encoded\n mode?: number;\n}\n\nexport type Message =\n | ResizeMessage\n | SignalMessage\n | PingMessage\n | PongMessage\n | ReadyMessage\n | ExitMessage\n | ErrorMessage\n | SyncBackMessage\n | SyncBackAckMessage\n | FileChangeMessage\n | BaseMessage;\n\nexport function parseMessage(data: string): Message {\n const base = JSON.parse(data) as BaseMessage;\n\n switch (base.type) {\n case MessageType.RESIZE:\n return JSON.parse(data) as ResizeMessage;\n case MessageType.SIGNAL:\n return JSON.parse(data) as SignalMessage;\n case MessageType.PING:\n return { type: 'ping' } as PingMessage;\n case MessageType.PONG:\n return { type: 'pong' } as PongMessage;\n case MessageType.READY:\n return { type: 'ready' } as ReadyMessage;\n case MessageType.EXIT:\n return JSON.parse(data) as ExitMessage;\n case MessageType.ERROR:\n return JSON.parse(data) as ErrorMessage;\n case MessageType.SYNC_BACK:\n return JSON.parse(data) as SyncBackMessage;\n case MessageType.SYNC_BACK_ACK:\n return JSON.parse(data) as SyncBackAckMessage;\n case MessageType.FILE_CHANGE:\n return JSON.parse(data) as FileChangeMessage;\n default:\n return base;\n }\n}\n\nexport function createResizeMessage(cols: number, rows: number): string {\n return JSON.stringify({ type: MessageType.RESIZE, cols, rows });\n}\n\nexport function createSignalMessage(name: string): string {\n return JSON.stringify({ type: MessageType.SIGNAL, name });\n}\n\nexport function createPingMessage(): string {\n return JSON.stringify({ type: MessageType.PING });\n}\n\nexport function createPongMessage(): string {\n return JSON.stringify({ type: MessageType.PONG });\n}\n\nexport function createSyncBackMessage(enabled: boolean): string {\n return JSON.stringify({ type: MessageType.SYNC_BACK, enabled });\n}\n","import { writeFileSync, unlinkSync, mkdirSync, renameSync } from 'fs';\nimport { join, dirname, normalize, isAbsolute, sep } from 'path';\nimport type { FileChangeMessage } from '../protocol/messages.js';\n\n/**\n * Apply a remote file change to the local filesystem.\n * Best-effort: errors are logged but don't break the terminal.\n */\nexport function applyRemoteFileChange(msg: FileChangeMessage): void {\n // Validate path (no absolute, no traversal)\n const rel = normalize(msg.path.replace(/^\\.\\//, ''));\n\n if (!rel || rel === '.') return;\n\n if (isAbsolute(rel)) {\n console.error(`sync-back rejected absolute path: ${rel}`);\n return;\n }\n\n if (rel === '..' || rel.startsWith('..' + sep)) {\n console.error(`sync-back rejected traversal path: ${rel}`);\n return;\n }\n\n const cwd = process.cwd();\n const destPath = join(cwd, rel);\n\n // Ensure destPath is within cwd (resolve any remaining symlinks/tricks)\n const resolvedCwd = normalize(cwd);\n const resolvedDest = normalize(destPath);\n\n if (!resolvedDest.startsWith(resolvedCwd + sep) && resolvedDest !== resolvedCwd) {\n console.error(`sync-back rejected path outside base: ${destPath}`);\n return;\n }\n\n try {\n if (msg.action === 'delete') {\n unlinkSync(destPath);\n } else if (msg.action === 'write') {\n const content = Buffer.from(msg.content || '', 'base64');\n mkdirSync(dirname(destPath), { recursive: true });\n\n // Atomic write via temp file\n const tmpPath = join(dirname(destPath), `.catty-sync-${Date.now()}`);\n writeFileSync(tmpPath, content, { mode: msg.mode || 0o644 });\n renameSync(tmpPath, destPath);\n }\n } catch {\n // Best-effort, don't break terminal\n }\n}\n","import archiver from 'archiver';\nimport ignore, { type Ignore } from 'ignore';\nimport { createReadStream, readFileSync, readdirSync, statSync } from 'fs';\nimport { join, relative } from 'path';\nimport { MAX_UPLOAD_SIZE } from './config.js';\n\nconst DEFAULT_IGNORES = [\n '.git',\n '.git/**',\n 'node_modules',\n 'node_modules/**',\n '__pycache__',\n '__pycache__/**',\n '.venv',\n '.venv/**',\n 'venv',\n 'venv/**',\n '.env',\n '*.pyc',\n '.DS_Store',\n '*.log',\n];\n\nexport async function createWorkspaceZip(dir: string): Promise<Buffer> {\n const ig = ignore().add(DEFAULT_IGNORES);\n\n // Load .gitignore if exists\n try {\n const gitignore = readFileSync(join(dir, '.gitignore'), 'utf-8');\n ig.add(gitignore);\n } catch {\n // No .gitignore, use defaults only\n }\n\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n const archive = archiver('zip', { zlib: { level: 9 } });\n\n archive.on('data', (chunk: Buffer) => chunks.push(chunk));\n archive.on('end', () => resolve(Buffer.concat(chunks)));\n archive.on('error', reject);\n\n // Walk directory and add files\n walkDir(dir, dir, ig, archive);\n archive.finalize();\n });\n}\n\nfunction walkDir(\n baseDir: string,\n currentDir: string,\n ig: Ignore,\n archive: archiver.Archiver\n): void {\n let entries: string[];\n try {\n entries = readdirSync(currentDir);\n } catch {\n return;\n }\n\n for (const entry of entries) {\n const fullPath = join(currentDir, entry);\n const relativePath = relative(baseDir, fullPath);\n\n // Check if ignored\n if (ig.ignores(relativePath)) {\n continue;\n }\n\n let stat;\n try {\n stat = statSync(fullPath);\n } catch {\n continue;\n }\n\n if (stat.isDirectory()) {\n // Also check directory with trailing slash\n if (ig.ignores(relativePath + '/')) {\n continue;\n }\n walkDir(baseDir, fullPath, ig, archive);\n } else if (stat.isFile()) {\n archive.file(fullPath, { name: relativePath });\n }\n }\n}\n\nexport async function uploadWorkspace(\n uploadURL: string,\n token: string,\n machineID: string\n): Promise<void> {\n const cwd = process.cwd();\n const zipData = await createWorkspaceZip(cwd);\n\n if (zipData.length > MAX_UPLOAD_SIZE) {\n throw new Error(\n `Workspace too large (${zipData.length} bytes, max ${MAX_UPLOAD_SIZE})`\n );\n }\n\n const response = await fetch(uploadURL, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/zip',\n 'fly-force-instance-id': machineID,\n },\n body: zipData,\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`Upload failed: ${response.status} - ${text}`);\n }\n}\n\n/**\n * Build upload URL from connect URL.\n * Converts wss://app.fly.dev/connect to https://app.fly.dev/upload\n */\nexport function buildUploadURL(connectURL: string): string {\n return connectURL\n .replace('wss://', 'https://')\n .replace('ws://', 'http://')\n .replace('/connect', '/upload');\n}\n","import { Command } from 'commander';\nimport { getAPIAddr } from '../lib/config.js';\nimport { isLoggedIn } from '../lib/auth.js';\nimport { APIClient } from '../lib/api-client.js';\nimport { connectToSession } from '../lib/websocket.js';\n\nexport const connectCommand = new Command('connect')\n .description('Reconnect to an existing session')\n .argument('<label>', 'Session label (e.g., brave-tiger-1234)')\n .option('--sync-back', 'Sync remote file changes back to local', true)\n .option('--no-sync-back', 'Disable sync-back')\n .action(async function (this: Command, label: string) {\n const opts = this.opts();\n const apiAddr = getAPIAddr(this.optsWithGlobals().api);\n\n if (!isLoggedIn()) {\n console.error(\"Not logged in. Please run 'catty login' first.\");\n process.exit(1);\n }\n\n const client = new APIClient(apiAddr);\n\n console.log(`Looking up session ${label}...`);\n const session = await client.getSession(label, true);\n\n if (session.status === 'stopped') {\n throw new Error(`Session ${session.label} is stopped`);\n }\n if (session.machine_state && session.machine_state !== 'started') {\n throw new Error(`Machine is not running (state: ${session.machine_state})`);\n }\n\n console.log(`Reconnecting to ${session.label}...`);\n\n await connectToSession({\n connectURL: session.connect_url,\n connectToken: session.connect_token!,\n headers: { 'fly-force-instance-id': session.machine_id },\n syncBack: opts.syncBack,\n });\n });\n","import { Command } from 'commander';\nimport { getAPIAddr } from '../lib/config.js';\nimport { APIClient } from '../lib/api-client.js';\n\nexport const listCommand = new Command('list')\n .aliases(['ls'])\n .description('List all sessions')\n .action(async function (this: Command) {\n const apiAddr = getAPIAddr(this.optsWithGlobals().api);\n const client = new APIClient(apiAddr);\n\n const sessions = await client.listSessions();\n\n if (sessions.length === 0) {\n console.log('No sessions found');\n return;\n }\n\n // Simple table output\n const header = 'LABEL STATUS REGION CREATED';\n console.log(header);\n\n for (const s of sessions) {\n const age = formatAge(new Date(s.created_at));\n const row = [\n s.label.padEnd(22),\n s.status.padEnd(9),\n s.region.padEnd(7),\n age,\n ].join(' ');\n console.log(row);\n }\n });\n\n/**\n * Human-readable time ago formatting\n */\nfunction formatAge(date: Date): string {\n const seconds = Math.floor((Date.now() - date.getTime()) / 1000);\n\n if (seconds < 60) return `${seconds}s ago`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n return `${days}d ago`;\n}\n","import { Command } from 'commander';\nimport { getAPIAddr } from '../lib/config.js';\nimport { APIClient } from '../lib/api-client.js';\n\nexport const stopCommand = new Command('stop')\n .description('Stop a session')\n .argument('<label>', 'Session ID or label')\n .option('--delete', 'Delete the machine after stopping', false)\n .action(async function (this: Command, label: string) {\n const opts = this.opts();\n const apiAddr = getAPIAddr(this.optsWithGlobals().api);\n const client = new APIClient(apiAddr);\n\n await client.stopSession(label, opts.delete);\n\n if (opts.delete) {\n console.log(`Session ${label} stopped and deleted`);\n } else {\n console.log(`Session ${label} stopped`);\n }\n });\n","import { Command } from 'commander';\nimport { getAPIAddr } from '../lib/config.js';\nimport { APIClient } from '../lib/api-client.js';\n\nexport const stopAllCommand = new Command('stop-all-sessions-dangerously')\n .description('Stop and delete ALL sessions')\n .option('--yes-i-mean-it', 'Confirm you want to stop all sessions', false)\n .action(async function (this: Command) {\n const opts = this.opts();\n const apiAddr = getAPIAddr(this.optsWithGlobals().api);\n\n if (!opts.yesIMeanIt) {\n throw new Error('Must pass --yes-i-mean-it to confirm');\n }\n\n const client = new APIClient(apiAddr);\n const sessions = await client.listSessions();\n\n if (sessions.length === 0) {\n console.log('No sessions to stop');\n return;\n }\n\n console.log(`Stopping ${sessions.length} sessions...`);\n\n for (const s of sessions) {\n process.stdout.write(` Stopping ${s.session_id}... `);\n try {\n await client.stopSession(s.session_id, true);\n console.log('done');\n } catch (err) {\n console.log(`ERROR: ${err}`);\n }\n }\n });\n","import { Command } from 'commander';\nimport open from 'open';\nimport { getAPIAddr, sleep } from '../lib/config.js';\nimport { isLoggedIn, loadCredentials, saveCredentials } from '../lib/auth.js';\nimport type { DeviceAuthResponse, TokenResponse } from '../types/index.js';\n\nexport const loginCommand = new Command('login')\n .description('Log in to Catty')\n .action(async function (this: Command) {\n const apiAddr = getAPIAddr(this.optsWithGlobals().api);\n\n if (isLoggedIn()) {\n const creds = loadCredentials();\n console.log(`Already logged in as ${creds?.email}`);\n console.log(\"Run 'catty logout' to log out first\");\n return;\n }\n\n console.log('Starting login...');\n\n // Step 1: Start device auth flow\n const authResp = await fetch(`${apiAddr}/v1/auth/device`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: '{}',\n });\n\n if (!authResp.ok) {\n throw new Error(`Failed to start auth: ${authResp.statusText}`);\n }\n\n const auth: DeviceAuthResponse = await authResp.json();\n\n // Step 2: Show code and open browser\n console.log('\\nYour confirmation code:\\n');\n console.log(` ${auth.user_code}\\n`);\n console.log(`Opening ${auth.verification_uri_complete}\\n`);\n\n await open(auth.verification_uri_complete);\n console.log('Waiting for authentication...');\n\n // Step 3: Poll for token\n const interval = (auth.interval || 5) * 1000;\n const deadline = Date.now() + auth.expires_in * 1000;\n\n while (Date.now() < deadline) {\n await sleep(interval);\n\n const tokenResp = await fetch(`${apiAddr}/v1/auth/device/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ device_code: auth.device_code }),\n });\n\n const token: TokenResponse = await tokenResp.json();\n\n if (token.pending) continue;\n if (token.error) throw new Error(token.error);\n\n if (token.access_token) {\n saveCredentials({\n access_token: token.access_token,\n refresh_token: token.refresh_token,\n user_id: token.user?.id || '',\n email: token.user?.email || '',\n expires_at: token.expires_in\n ? new Date(Date.now() + (token.expires_in - 30) * 1000).toISOString()\n : undefined,\n });\n console.log(`\\nLogged in as ${token.user?.email}`);\n console.log(\"You can now run 'catty new' to start a session\");\n return;\n }\n }\n\n throw new Error('Authentication timed out');\n });\n","import { Command } from 'commander';\nimport { isLoggedIn, loadCredentials, deleteCredentials } from '../lib/auth.js';\n\nexport const logoutCommand = new Command('logout')\n .description('Log out of Catty')\n .action(async () => {\n if (!isLoggedIn()) {\n console.log('Not logged in');\n return;\n }\n\n const creds = loadCredentials();\n const email = creds?.email || '';\n\n deleteCredentials();\n\n if (email) {\n console.log(`Logged out from ${email}`);\n } else {\n console.log('Logged out');\n }\n });\n","import { Command } from 'commander';\n\n// VERSION is replaced at build time by tsup\ndeclare const __VERSION__: string;\n\nexport const versionCommand = new Command('version')\n .description('Print the version number')\n .action(() => {\n console.log(typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'dev');\n });\n"],"mappings":"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","names":["program","Command","open","DEFAULT_API_ADDR","CREDENTIALS_DIR","CREDENTIALS_FILE","getAPIAddr","cliOption","DEFAULT_API_ADDR","sleep","ms","resolve","homedir","join","readFileSync","writeFileSync","mkdirSync","unlinkSync","existsSync","getCredentialsDir","join","homedir","CREDENTIALS_DIR","getCredentialsPath","CREDENTIALS_FILE","loadCredentials","path","content","readFileSync","saveCredentials","creds","dir","mkdirSync","writeFileSync","deleteCredentials","existsSync","unlinkSync","isLoggedIn","getAccessToken","getRefreshToken","APIError","statusCode","errorCode","message","upgradeURL","APIClient","baseURL","DEFAULT_API_ADDR","getAccessToken","method","path","body","controller","timeoutId","headers","response","refreshToken","getRefreshToken","data","creds","loadCredentials","saveCredentials","errorData","req","idOrLabel","live","del","WebSocket","Terminal","cleanup","callback","MessageType","parseMessage","data","base","createResizeMessage","cols","rows","createPongMessage","MessageType","createSyncBackMessage","enabled","writeFileSync","unlinkSync","mkdirSync","renameSync","join","dirname","normalize","isAbsolute","sep","applyRemoteFileChange","msg","rel","cwd","destPath","resolvedCwd","resolvedDest","content","tmpPath","connectToSession","opts","terminal","Terminal","ws","WebSocket","resolve","reject","syncBackAcked","exitCode","handleResize","cols","rows","createResizeMessage","cleanup","handleStdinData","data","createSyncBackMessage","isBinary","msg","parseMessage","handleControlMessage","exitMsg","errorMsg","createPongMessage","applyRemoteFileChange","code","err","archiver","ignore","readFileSync","readdirSync","statSync","join","relative","DEFAULT_IGNORES","createWorkspaceZip","dir","ig","ignore","gitignore","readFileSync","join","resolve","reject","chunks","archive","archiver","chunk","walkDir","baseDir","currentDir","entries","readdirSync","entry","fullPath","relativePath","relative","stat","statSync","uploadWorkspace","uploadURL","token","machineID","cwd","zipData","response","text","buildUploadURL","connectURL","newCommand","Command","opts","apiAddr","getAPIAddr","isLoggedIn","client","APIClient","cmdArgs","session","err","APIError","handleQuotaExceeded","uploadURL","buildUploadURL","uploadWorkspace","connectToSession","checkoutURL","open","Command","connectCommand","Command","label","opts","apiAddr","getAPIAddr","isLoggedIn","client","APIClient","session","connectToSession","Command","listCommand","Command","apiAddr","getAPIAddr","sessions","APIClient","age","formatAge","row","date","seconds","minutes","hours","Command","stopCommand","Command","label","opts","apiAddr","getAPIAddr","APIClient","Command","stopAllCommand","Command","opts","apiAddr","getAPIAddr","client","APIClient","sessions","err","Command","open","loginCommand","Command","apiAddr","getAPIAddr","isLoggedIn","creds","loadCredentials","authResp","auth","open","interval","deadline","sleep","token","saveCredentials","Command","logoutCommand","Command","isLoggedIn","email","loadCredentials","deleteCredentials","Command","versionCommand","version","program","newCommand","connectCommand","listCommand","stopCommand","stopAllCommand","loginCommand","logoutCommand","versionCommand","main","err"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/new.ts","../src/lib/config.ts","../src/lib/auth.ts","../src/lib/api-client.ts","../src/lib/websocket.ts","../src/lib/terminal.ts","../src/protocol/messages.ts","../src/lib/syncback.ts","../src/lib/workspace.ts","../src/commands/connect.ts","../src/commands/list.ts","../src/commands/stop.ts","../src/commands/stopall.ts","../src/commands/login.ts","../src/commands/logout.ts","../src/commands/version.ts","../src/commands/update.ts","../src/lib/version-checker.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { program } from 'commander';\nimport { newCommand } from './commands/new.js';\nimport { connectCommand } from './commands/connect.js';\nimport { listCommand } from './commands/list.js';\nimport { stopCommand } from './commands/stop.js';\nimport { stopAllCommand } from './commands/stopall.js';\nimport { loginCommand } from './commands/login.js';\nimport { logoutCommand } from './commands/logout.js';\nimport { versionCommand } from './commands/version.js';\nimport { updateCommand } from './commands/update.js';\nimport {\n checkForUpdate,\n printUpdateAvailable,\n promptForUpdate,\n runUpdate,\n recordDeclinedUpdate,\n} from './lib/version-checker.js';\n\n// VERSION is replaced at build time by tsup\ndeclare const __VERSION__: string;\nconst version = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'dev';\n\nprogram\n .name('catty')\n .description('Catty - Remote AI agent sessions')\n .option('--api <url>', 'API server address')\n .version(version);\n\nprogram.addCommand(newCommand);\nprogram.addCommand(connectCommand);\nprogram.addCommand(listCommand);\nprogram.addCommand(stopCommand);\nprogram.addCommand(stopAllCommand, { hidden: true });\nprogram.addCommand(loginCommand);\nprogram.addCommand(logoutCommand);\nprogram.addCommand(versionCommand);\nprogram.addCommand(updateCommand);\n\n// Handle errors gracefully\nprogram.exitOverride();\n\nasync function main() {\n try {\n await program.parseAsync(process.argv);\n\n // Check for updates after command execution\n // Skip if running version, update, or help commands\n const command = process.argv[2];\n const skipUpdateCheck = [\n 'version',\n 'update',\n '-v',\n '--version',\n '-h',\n '--help',\n 'help',\n ];\n if (command && !skipUpdateCheck.includes(command)) {\n const { updateAvailable, currentVersion, latestVersion, shouldPrompt } =\n await checkForUpdate();\n if (updateAvailable && latestVersion && shouldPrompt) {\n printUpdateAvailable(currentVersion, latestVersion);\n const shouldUpdate = await promptForUpdate();\n if (shouldUpdate) {\n try {\n await runUpdate(currentVersion, latestVersion);\n } catch (err) {\n // Update failed, but don't exit the process\n // Error message already printed by runUpdate\n }\n } else {\n // User declined - record it so we don't ask again for 2 days\n recordDeclinedUpdate(latestVersion);\n }\n }\n }\n } catch (err: unknown) {\n if (err instanceof Error) {\n // Commander throws for help/version, ignore those\n if (\n err.name === 'CommanderError' &&\n ['commander.helpDisplayed', 'commander.version'].includes(\n (err as { code?: string }).code || ''\n )\n ) {\n process.exit(0);\n }\n\n console.error(`Error: ${err.message}`);\n } else {\n console.error(`Error: ${err}`);\n }\n process.exit(1);\n }\n}\n\nmain();\n","import { Command } from 'commander';\nimport open from 'open';\nimport { getAPIAddr } from '../lib/config.js';\nimport { isLoggedIn } from '../lib/auth.js';\nimport { APIClient, APIError } from '../lib/api-client.js';\nimport { connectToSession } from '../lib/websocket.js';\nimport { uploadWorkspace, buildUploadURL } from '../lib/workspace.js';\n\nexport const newCommand = new Command('new')\n .description('Start a new remote agent session')\n .option('--agent <name>', 'Agent to use: claude or codex', 'claude')\n .option('--no-upload', \"Don't upload current directory\")\n .option('--no-sync-back', 'Disable sync-back')\n .option(\n '--enable-prompts',\n 'Enable permission prompts (by default, all permissions are auto-approved)',\n false\n )\n .action(async function (this: Command) {\n const opts = this.opts();\n const apiAddr = getAPIAddr(this.optsWithGlobals().api);\n\n if (!isLoggedIn()) {\n console.error(\"Not logged in. Please run 'catty login' first.\");\n process.exit(1);\n }\n\n const client = new APIClient(apiAddr);\n\n console.log('Creating session...');\n\n // Determine command arguments based on agent and prompts setting\n let cmdArgs: string[];\n switch (opts.agent) {\n case 'claude':\n if (opts.enablePrompts) {\n // User wants prompts - don't skip permissions\n cmdArgs = ['claude-wrapper'];\n } else {\n // Default: auto-approve all permissions\n cmdArgs = ['claude-wrapper', '--dangerously-skip-permissions'];\n }\n break;\n case 'codex':\n cmdArgs = ['codex'];\n break;\n default:\n console.error(\n `Unknown agent: ${opts.agent} (must be 'claude' or 'codex')`\n );\n process.exit(1);\n }\n\n let session;\n try {\n session = await client.createSession({\n agent: opts.agent,\n cmd: cmdArgs,\n region: 'iad',\n ttl_sec: 7200,\n });\n } catch (err) {\n if (err instanceof APIError && err.isQuotaExceeded()) {\n await handleQuotaExceeded(client);\n return;\n }\n throw err;\n }\n\n console.log(`Session created: ${session.label}`);\n console.log(` Reconnect with: catty connect ${session.label}`);\n\n // Upload workspace\n if (opts.upload !== false) {\n console.log('Uploading workspace...');\n const uploadURL = buildUploadURL(session.connect_url);\n\n await uploadWorkspace(\n uploadURL,\n session.connect_token,\n session.headers['fly-force-instance-id']\n );\n console.log('Workspace uploaded.');\n }\n\n console.log(`Connecting to ${session.connect_url}...`);\n\n await connectToSession({\n connectURL: session.connect_url,\n connectToken: session.connect_token,\n headers: session.headers,\n syncBack: opts.syncBack !== false,\n });\n });\n\nasync function handleQuotaExceeded(client: APIClient): Promise<void> {\n console.error('');\n console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');\n console.error(' Free tier quota exceeded (1M tokens/month)');\n console.error(' Upgrade to Pro for unlimited usage.');\n console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');\n console.error('');\n\n try {\n const checkoutURL = await client.createCheckoutSession();\n console.error('Opening upgrade page in your browser...');\n await open(checkoutURL);\n } catch (err) {\n console.error(`Failed to create checkout session: ${err}`);\n console.error('Please visit https://catty.dev to upgrade.');\n }\n}\n","export const DEFAULT_API_ADDR = 'https://api.catty.dev';\nexport const CREDENTIALS_DIR = '.catty';\nexport const CREDENTIALS_FILE = 'credentials.json';\nexport const MAX_UPLOAD_SIZE = 100 * 1024 * 1024; // 100MB\n\n// Timeouts\nexport const API_TIMEOUT_MS = 120_000; // 120 seconds for API requests (machine creation can be slow)\nexport const WS_WRITE_TIMEOUT_MS = 10_000; // 10 seconds for WebSocket writes\nexport const WS_READ_TIMEOUT_MS = 60_000; // 60 seconds (must be > 25s ping interval)\nexport const SYNC_BACK_ACK_TIMEOUT_MS = 2_000; // Warn if no sync-back ack after 2s\n\n// WebSocket close codes\nexport const WS_POLICY_VIOLATION = 1008; // Connection replaced by new one\n\n// Helper to get API address (checks flag, env var, or default)\nexport function getAPIAddr(cliOption?: string): string {\n if (cliOption) return cliOption;\n if (process.env.CATTY_API_ADDR) return process.env.CATTY_API_ADDR;\n return DEFAULT_API_ADDR;\n}\n\n// Sleep helper for polling\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import { homedir } from 'os';\nimport { join } from 'path';\nimport {\n readFileSync,\n writeFileSync,\n mkdirSync,\n unlinkSync,\n existsSync,\n} from 'fs';\nimport { CREDENTIALS_DIR, CREDENTIALS_FILE } from './config.js';\nimport type { Credentials } from '../types/index.js';\n\nexport function getCredentialsDir(): string {\n return join(homedir(), CREDENTIALS_DIR);\n}\n\nexport function getCredentialsPath(): string {\n return join(getCredentialsDir(), CREDENTIALS_FILE);\n}\n\nexport function loadCredentials(): Credentials | null {\n const path = getCredentialsPath();\n try {\n const content = readFileSync(path, 'utf-8');\n return JSON.parse(content) as Credentials;\n } catch {\n return null;\n }\n}\n\nexport function saveCredentials(creds: Credentials): void {\n const dir = getCredentialsDir();\n const path = getCredentialsPath();\n\n // Create directory with 0700 permissions\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n\n // Write file with 0600 permissions\n writeFileSync(path, JSON.stringify(creds, null, 2), { mode: 0o600 });\n}\n\nexport function deleteCredentials(): void {\n const path = getCredentialsPath();\n if (existsSync(path)) {\n unlinkSync(path);\n }\n}\n\nexport function isLoggedIn(): boolean {\n const creds = loadCredentials();\n if (!creds) return false;\n\n // Check if token exists\n if (!creds.access_token) return false;\n\n // If there's an expiry and no refresh token, check if expired\n if (creds.expires_at && !creds.refresh_token) {\n const expiresAt = new Date(creds.expires_at);\n if (expiresAt <= new Date()) {\n return false;\n }\n }\n\n // If we have a refresh token, we can refresh even if access token expired\n return true;\n}\n\nexport function getAccessToken(): string | null {\n const creds = loadCredentials();\n return creds?.access_token || null;\n}\n\nexport function getRefreshToken(): string | null {\n const creds = loadCredentials();\n return creds?.refresh_token || null;\n}\n","import {\n DEFAULT_API_ADDR,\n API_TIMEOUT_MS,\n} from './config.js';\nimport {\n getAccessToken,\n getRefreshToken,\n loadCredentials,\n saveCredentials,\n} from './auth.js';\nimport type {\n CreateSessionRequest,\n CreateSessionResponse,\n SessionInfo,\n APIErrorResponse,\n} from '../types/index.js';\n\nexport class APIError extends Error {\n constructor(\n public statusCode: number,\n public errorCode: string,\n message: string,\n public upgradeURL?: string\n ) {\n super(message);\n this.name = 'APIError';\n }\n\n isQuotaExceeded(): boolean {\n return this.statusCode === 402 && this.errorCode === 'quota_exceeded';\n }\n}\n\nexport class APIClient {\n private baseURL: string;\n private authToken: string | null;\n\n constructor(baseURL?: string) {\n this.baseURL = baseURL || process.env.CATTY_API_ADDR || DEFAULT_API_ADDR;\n this.authToken = getAccessToken();\n }\n\n private async doRequest(\n method: string,\n path: string,\n body?: unknown\n ): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), API_TIMEOUT_MS);\n\n try {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (this.authToken) {\n headers['Authorization'] = `Bearer ${this.authToken}`;\n }\n\n const response = await fetch(`${this.baseURL}${path}`, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n return response;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private async doRequestWithRefresh(\n method: string,\n path: string,\n body?: unknown\n ): Promise<Response> {\n let response = await this.doRequest(method, path, body);\n\n if (response.status === 401) {\n const refreshed = await this.refreshAuthToken();\n if (refreshed) {\n response = await this.doRequest(method, path, body);\n }\n }\n\n return response;\n }\n\n private async refreshAuthToken(): Promise<boolean> {\n const refreshToken = getRefreshToken();\n if (!refreshToken) return false;\n\n try {\n const response = await this.doRequest('POST', '/v1/auth/refresh', {\n refresh_token: refreshToken,\n });\n\n if (!response.ok) return false;\n\n const data = await response.json();\n if (!data.access_token) return false;\n\n // Update stored credentials\n const creds = loadCredentials();\n if (creds) {\n creds.access_token = data.access_token;\n if (data.refresh_token) {\n creds.refresh_token = data.refresh_token;\n }\n if (data.expires_in) {\n creds.expires_at = new Date(\n Date.now() + (data.expires_in - 30) * 1000\n ).toISOString();\n }\n saveCredentials(creds);\n this.authToken = data.access_token;\n }\n\n return true;\n } catch {\n return false;\n }\n }\n\n private async handleResponse<T>(response: Response): Promise<T> {\n if (!response.ok) {\n let errorData: APIErrorResponse;\n try {\n errorData = await response.json();\n } catch {\n errorData = { error: response.statusText };\n }\n\n throw new APIError(\n response.status,\n errorData.code || '',\n errorData.error || response.statusText,\n errorData.upgrade_url\n );\n }\n\n return response.json() as Promise<T>;\n }\n\n async createSession(req: CreateSessionRequest): Promise<CreateSessionResponse> {\n const response = await this.doRequestWithRefresh('POST', '/v1/sessions', req);\n return this.handleResponse<CreateSessionResponse>(response);\n }\n\n async listSessions(): Promise<SessionInfo[]> {\n const response = await this.doRequestWithRefresh('GET', '/v1/sessions');\n return this.handleResponse<SessionInfo[]>(response);\n }\n\n async getSession(idOrLabel: string, live?: boolean): Promise<SessionInfo> {\n const path = live\n ? `/v1/sessions/${idOrLabel}?live=true`\n : `/v1/sessions/${idOrLabel}`;\n const response = await this.doRequestWithRefresh('GET', path);\n return this.handleResponse<SessionInfo>(response);\n }\n\n async stopSession(idOrLabel: string, del?: boolean): Promise<void> {\n const path = del\n ? `/v1/sessions/${idOrLabel}?delete=true`\n : `/v1/sessions/${idOrLabel}`;\n const response = await this.doRequestWithRefresh('DELETE', path);\n\n if (!response.ok) {\n let errorData: APIErrorResponse;\n try {\n errorData = await response.json();\n } catch {\n errorData = { error: response.statusText };\n }\n throw new APIError(\n response.status,\n errorData.code || '',\n errorData.error || response.statusText\n );\n }\n }\n\n async createCheckoutSession(): Promise<string> {\n const response = await this.doRequestWithRefresh('POST', '/v1/checkout');\n const data = await this.handleResponse<{ url: string }>(response);\n return data.url;\n }\n}\n","import WebSocket from 'ws';\nimport { Terminal } from './terminal.js';\nimport {\n SYNC_BACK_ACK_TIMEOUT_MS,\n WS_POLICY_VIOLATION,\n} from './config.js';\nimport {\n parseMessage,\n createResizeMessage,\n createPongMessage,\n createSyncBackMessage,\n type Message,\n type ExitMessage,\n type ErrorMessage,\n type FileChangeMessage,\n} from '../protocol/messages.js';\nimport { applyRemoteFileChange } from './syncback.js';\n\nexport interface WebSocketConnectOptions {\n connectURL: string;\n connectToken: string;\n headers: Record<string, string>;\n syncBack: boolean;\n onExit?: (code: number) => void;\n}\n\nexport async function connectToSession(\n opts: WebSocketConnectOptions\n): Promise<void> {\n const terminal = new Terminal();\n\n if (!terminal.isTerminal()) {\n throw new Error('stdin is not a terminal');\n }\n\n const ws = new WebSocket(opts.connectURL, {\n headers: {\n ...opts.headers,\n Authorization: `Bearer ${opts.connectToken}`,\n },\n });\n\n return new Promise((resolve, reject) => {\n let syncBackAcked = false;\n let exitCode = 0;\n\n const handleResize = () => {\n const { cols, rows } = terminal.getSize();\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(createResizeMessage(cols, rows));\n }\n };\n\n const cleanup = () => {\n terminal.restore();\n terminal.offResize(handleResize);\n process.stdin.off('data', handleStdinData);\n };\n\n const handleStdinData = (data: Buffer) => {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(data); // Binary\n }\n };\n\n ws.on('open', () => {\n // Enable sync-back if requested\n if (opts.syncBack) {\n ws.send(createSyncBackMessage(true));\n\n // Warn if no ack after timeout\n setTimeout(() => {\n if (!syncBackAcked) {\n process.stderr.write(\n '\\r\\n(sync-back) No ack from executor yet — this machine may be running an older catty-exec image without sync-back.\\r\\n'\n );\n }\n }, SYNC_BACK_ACK_TIMEOUT_MS);\n }\n\n // Enter raw mode\n terminal.makeRaw();\n\n // Send initial size\n const { cols, rows } = terminal.getSize();\n ws.send(createResizeMessage(cols, rows));\n\n // Handle resize\n terminal.onResize(handleResize);\n\n // Relay stdin -> WebSocket\n process.stdin.on('data', handleStdinData);\n });\n\n // Relay WebSocket -> stdout\n ws.on('message', (data: WebSocket.RawData, isBinary: boolean) => {\n if (isBinary) {\n process.stdout.write(data as Buffer);\n } else {\n try {\n const msg = parseMessage(data.toString());\n handleControlMessage(msg);\n } catch {\n // Ignore parse errors\n }\n }\n });\n\n function handleControlMessage(msg: Message) {\n switch (msg.type) {\n case 'exit': {\n const exitMsg = msg as ExitMessage;\n exitCode = exitMsg.code;\n opts.onExit?.(exitMsg.code);\n process.stderr.write(`\\r\\nProcess exited with code ${exitMsg.code}\\r\\n`);\n cleanup();\n ws.close();\n resolve();\n break;\n }\n case 'error': {\n const errorMsg = msg as ErrorMessage;\n process.stderr.write(`\\r\\nError: ${errorMsg.message}\\r\\n`);\n break;\n }\n case 'ping':\n ws.send(createPongMessage());\n break;\n case 'file_change':\n applyRemoteFileChange(msg as FileChangeMessage);\n break;\n case 'sync_back_ack':\n syncBackAcked = true;\n break;\n }\n }\n\n ws.on('close', (code: number) => {\n cleanup();\n // Code 1008 (WS_POLICY_VIOLATION) = connection replaced by new one\n // This is a clean termination, not an error\n if (code === WS_POLICY_VIOLATION) {\n resolve();\n } else {\n resolve();\n }\n });\n\n ws.on('error', (err: Error) => {\n cleanup();\n reject(err);\n });\n\n // Handle process exit\n process.on('exit', () => {\n cleanup();\n if (ws.readyState === WebSocket.OPEN) {\n ws.close();\n }\n });\n });\n}\n","export class Terminal {\n private wasRaw = false;\n private cleanupDone = false;\n\n isTerminal(): boolean {\n return process.stdin.isTTY === true;\n }\n\n makeRaw(): void {\n if (!this.isTerminal()) return;\n if (this.wasRaw) return;\n\n process.stdin.setRawMode(true);\n process.stdin.resume();\n this.wasRaw = true;\n\n // Ensure terminal is restored on process exit\n const cleanup = () => this.restore();\n\n process.on('exit', cleanup);\n\n process.on('SIGINT', () => {\n cleanup();\n process.exit(130); // 128 + SIGINT(2)\n });\n\n process.on('SIGTERM', () => {\n cleanup();\n process.exit(143); // 128 + SIGTERM(15)\n });\n }\n\n restore(): void {\n if (this.cleanupDone) return;\n if (this.wasRaw && process.stdin.isTTY) {\n try {\n process.stdin.setRawMode(false);\n } catch {\n // Ignore errors during cleanup\n }\n this.wasRaw = false;\n }\n this.cleanupDone = true;\n }\n\n getSize(): { cols: number; rows: number } {\n return {\n cols: process.stdout.columns || 80,\n rows: process.stdout.rows || 24,\n };\n }\n\n onResize(callback: () => void): void {\n process.stdout.on('resize', callback);\n }\n\n offResize(callback: () => void): void {\n process.stdout.off('resize', callback);\n }\n}\n","export const MessageType = {\n RESIZE: 'resize',\n SIGNAL: 'signal',\n PING: 'ping',\n PONG: 'pong',\n READY: 'ready',\n EXIT: 'exit',\n ERROR: 'error',\n SYNC_BACK: 'sync_back',\n SYNC_BACK_ACK: 'sync_back_ack',\n FILE_CHANGE: 'file_change',\n} as const;\n\nexport interface BaseMessage {\n type: string;\n}\n\nexport interface ResizeMessage {\n type: 'resize';\n cols: number;\n rows: number;\n}\n\nexport interface SignalMessage {\n type: 'signal';\n name: string;\n}\n\nexport interface PingMessage {\n type: 'ping';\n}\n\nexport interface PongMessage {\n type: 'pong';\n}\n\nexport interface ReadyMessage {\n type: 'ready';\n}\n\nexport interface ExitMessage {\n type: 'exit';\n code: number;\n signal: string | null;\n}\n\nexport interface ErrorMessage {\n type: 'error';\n message: string;\n}\n\nexport interface SyncBackMessage {\n type: 'sync_back';\n enabled: boolean;\n}\n\nexport interface SyncBackAckMessage {\n type: 'sync_back_ack';\n enabled: boolean;\n workspace_dir?: string;\n interval_ms?: number;\n}\n\nexport interface FileChangeMessage {\n type: 'file_change';\n action: 'write' | 'delete';\n path: string;\n content?: string; // base64 encoded\n mode?: number;\n}\n\nexport type Message =\n | ResizeMessage\n | SignalMessage\n | PingMessage\n | PongMessage\n | ReadyMessage\n | ExitMessage\n | ErrorMessage\n | SyncBackMessage\n | SyncBackAckMessage\n | FileChangeMessage\n | BaseMessage;\n\nexport function parseMessage(data: string): Message {\n const base = JSON.parse(data) as BaseMessage;\n\n switch (base.type) {\n case MessageType.RESIZE:\n return JSON.parse(data) as ResizeMessage;\n case MessageType.SIGNAL:\n return JSON.parse(data) as SignalMessage;\n case MessageType.PING:\n return { type: 'ping' } as PingMessage;\n case MessageType.PONG:\n return { type: 'pong' } as PongMessage;\n case MessageType.READY:\n return { type: 'ready' } as ReadyMessage;\n case MessageType.EXIT:\n return JSON.parse(data) as ExitMessage;\n case MessageType.ERROR:\n return JSON.parse(data) as ErrorMessage;\n case MessageType.SYNC_BACK:\n return JSON.parse(data) as SyncBackMessage;\n case MessageType.SYNC_BACK_ACK:\n return JSON.parse(data) as SyncBackAckMessage;\n case MessageType.FILE_CHANGE:\n return JSON.parse(data) as FileChangeMessage;\n default:\n return base;\n }\n}\n\nexport function createResizeMessage(cols: number, rows: number): string {\n return JSON.stringify({ type: MessageType.RESIZE, cols, rows });\n}\n\nexport function createSignalMessage(name: string): string {\n return JSON.stringify({ type: MessageType.SIGNAL, name });\n}\n\nexport function createPingMessage(): string {\n return JSON.stringify({ type: MessageType.PING });\n}\n\nexport function createPongMessage(): string {\n return JSON.stringify({ type: MessageType.PONG });\n}\n\nexport function createSyncBackMessage(enabled: boolean): string {\n return JSON.stringify({ type: MessageType.SYNC_BACK, enabled });\n}\n","import { writeFileSync, unlinkSync, mkdirSync, renameSync } from 'fs';\nimport { join, dirname, normalize, isAbsolute, sep } from 'path';\nimport type { FileChangeMessage } from '../protocol/messages.js';\n\n/**\n * Apply a remote file change to the local filesystem.\n * Best-effort: errors are logged but don't break the terminal.\n */\nexport function applyRemoteFileChange(msg: FileChangeMessage): void {\n // Validate path (no absolute, no traversal)\n const rel = normalize(msg.path.replace(/^\\.\\//, ''));\n\n if (!rel || rel === '.') return;\n\n if (isAbsolute(rel)) {\n console.error(`sync-back rejected absolute path: ${rel}`);\n return;\n }\n\n if (rel === '..' || rel.startsWith('..' + sep)) {\n console.error(`sync-back rejected traversal path: ${rel}`);\n return;\n }\n\n const cwd = process.cwd();\n const destPath = join(cwd, rel);\n\n // Ensure destPath is within cwd (resolve any remaining symlinks/tricks)\n const resolvedCwd = normalize(cwd);\n const resolvedDest = normalize(destPath);\n\n if (!resolvedDest.startsWith(resolvedCwd + sep) && resolvedDest !== resolvedCwd) {\n console.error(`sync-back rejected path outside base: ${destPath}`);\n return;\n }\n\n try {\n if (msg.action === 'delete') {\n unlinkSync(destPath);\n } else if (msg.action === 'write') {\n const content = Buffer.from(msg.content || '', 'base64');\n mkdirSync(dirname(destPath), { recursive: true });\n\n // Atomic write via temp file\n const tmpPath = join(dirname(destPath), `.catty-sync-${Date.now()}`);\n writeFileSync(tmpPath, content, { mode: msg.mode || 0o644 });\n renameSync(tmpPath, destPath);\n }\n } catch {\n // Best-effort, don't break terminal\n }\n}\n","import archiver from 'archiver';\nimport ignore, { type Ignore } from 'ignore';\nimport { createReadStream, readFileSync, readdirSync, statSync } from 'fs';\nimport { join, relative } from 'path';\nimport { MAX_UPLOAD_SIZE } from './config.js';\n\nconst DEFAULT_IGNORES = [\n '.git',\n '.git/**',\n 'node_modules',\n 'node_modules/**',\n '__pycache__',\n '__pycache__/**',\n '.venv',\n '.venv/**',\n 'venv',\n 'venv/**',\n '.env',\n '*.pyc',\n '.DS_Store',\n '*.log',\n];\n\nexport async function createWorkspaceZip(dir: string): Promise<Buffer> {\n const ig = ignore().add(DEFAULT_IGNORES);\n\n // Load .gitignore if exists\n try {\n const gitignore = readFileSync(join(dir, '.gitignore'), 'utf-8');\n ig.add(gitignore);\n } catch {\n // No .gitignore, use defaults only\n }\n\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n const archive = archiver('zip', { zlib: { level: 9 } });\n\n archive.on('data', (chunk: Buffer) => chunks.push(chunk));\n archive.on('end', () => resolve(Buffer.concat(chunks)));\n archive.on('error', reject);\n\n // Walk directory and add files\n walkDir(dir, dir, ig, archive);\n archive.finalize();\n });\n}\n\nfunction walkDir(\n baseDir: string,\n currentDir: string,\n ig: Ignore,\n archive: archiver.Archiver\n): void {\n let entries: string[];\n try {\n entries = readdirSync(currentDir);\n } catch {\n return;\n }\n\n for (const entry of entries) {\n const fullPath = join(currentDir, entry);\n const relativePath = relative(baseDir, fullPath);\n\n // Check if ignored\n if (ig.ignores(relativePath)) {\n continue;\n }\n\n let stat;\n try {\n stat = statSync(fullPath);\n } catch {\n continue;\n }\n\n if (stat.isDirectory()) {\n // Also check directory with trailing slash\n if (ig.ignores(relativePath + '/')) {\n continue;\n }\n walkDir(baseDir, fullPath, ig, archive);\n } else if (stat.isFile()) {\n archive.file(fullPath, { name: relativePath });\n }\n }\n}\n\nexport async function uploadWorkspace(\n uploadURL: string,\n token: string,\n machineID: string\n): Promise<void> {\n const cwd = process.cwd();\n const zipData = await createWorkspaceZip(cwd);\n\n if (zipData.length > MAX_UPLOAD_SIZE) {\n throw new Error(\n `Workspace too large (${zipData.length} bytes, max ${MAX_UPLOAD_SIZE})`\n );\n }\n\n const response = await fetch(uploadURL, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/zip',\n 'fly-force-instance-id': machineID,\n },\n body: zipData,\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`Upload failed: ${response.status} - ${text}`);\n }\n}\n\n/**\n * Build upload URL from connect URL.\n * Converts wss://app.fly.dev/connect to https://app.fly.dev/upload\n */\nexport function buildUploadURL(connectURL: string): string {\n return connectURL\n .replace('wss://', 'https://')\n .replace('ws://', 'http://')\n .replace('/connect', '/upload');\n}\n","import { Command } from 'commander';\nimport { getAPIAddr } from '../lib/config.js';\nimport { isLoggedIn } from '../lib/auth.js';\nimport { APIClient } from '../lib/api-client.js';\nimport { connectToSession } from '../lib/websocket.js';\n\nexport const connectCommand = new Command('connect')\n .description('Reconnect to an existing session')\n .argument('<label>', 'Session label (e.g., brave-tiger-1234)')\n .option('--sync-back', 'Sync remote file changes back to local', true)\n .option('--no-sync-back', 'Disable sync-back')\n .action(async function (this: Command, label: string) {\n const opts = this.opts();\n const apiAddr = getAPIAddr(this.optsWithGlobals().api);\n\n if (!isLoggedIn()) {\n console.error(\"Not logged in. Please run 'catty login' first.\");\n process.exit(1);\n }\n\n const client = new APIClient(apiAddr);\n\n console.log(`Looking up session ${label}...`);\n const session = await client.getSession(label, true);\n\n if (session.status === 'stopped') {\n throw new Error(`Session ${session.label} is stopped`);\n }\n if (session.machine_state && session.machine_state !== 'started') {\n throw new Error(`Machine is not running (state: ${session.machine_state})`);\n }\n\n console.log(`Reconnecting to ${session.label}...`);\n\n await connectToSession({\n connectURL: session.connect_url,\n connectToken: session.connect_token!,\n headers: { 'fly-force-instance-id': session.machine_id },\n syncBack: opts.syncBack,\n });\n });\n","import { Command } from 'commander';\nimport { getAPIAddr } from '../lib/config.js';\nimport { APIClient } from '../lib/api-client.js';\n\nexport const listCommand = new Command('list')\n .aliases(['ls'])\n .description('List all sessions')\n .action(async function (this: Command) {\n const apiAddr = getAPIAddr(this.optsWithGlobals().api);\n const client = new APIClient(apiAddr);\n\n const sessions = await client.listSessions();\n\n if (sessions.length === 0) {\n console.log('No sessions found');\n return;\n }\n\n // Simple table output\n const header = 'LABEL STATUS REGION CREATED';\n console.log(header);\n\n for (const s of sessions) {\n const age = formatAge(new Date(s.created_at));\n const row = [\n s.label.padEnd(22),\n s.status.padEnd(9),\n s.region.padEnd(7),\n age,\n ].join(' ');\n console.log(row);\n }\n });\n\n/**\n * Human-readable time ago formatting\n */\nfunction formatAge(date: Date): string {\n const seconds = Math.floor((Date.now() - date.getTime()) / 1000);\n\n if (seconds < 60) return `${seconds}s ago`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n return `${days}d ago`;\n}\n","import { Command } from 'commander';\nimport { getAPIAddr } from '../lib/config.js';\nimport { APIClient } from '../lib/api-client.js';\n\nexport const stopCommand = new Command('stop')\n .description('Stop a session')\n .argument('<label>', 'Session ID or label')\n .option('--delete', 'Delete the machine after stopping', false)\n .action(async function (this: Command, label: string) {\n const opts = this.opts();\n const apiAddr = getAPIAddr(this.optsWithGlobals().api);\n const client = new APIClient(apiAddr);\n\n await client.stopSession(label, opts.delete);\n\n if (opts.delete) {\n console.log(`Session ${label} stopped and deleted`);\n } else {\n console.log(`Session ${label} stopped`);\n }\n });\n","import { Command } from 'commander';\nimport { getAPIAddr } from '../lib/config.js';\nimport { APIClient } from '../lib/api-client.js';\n\nexport const stopAllCommand = new Command('stop-all-sessions-dangerously')\n .description('Stop and delete ALL sessions')\n .option('--yes-i-mean-it', 'Confirm you want to stop all sessions', false)\n .action(async function (this: Command) {\n const opts = this.opts();\n const apiAddr = getAPIAddr(this.optsWithGlobals().api);\n\n if (!opts.yesIMeanIt) {\n throw new Error('Must pass --yes-i-mean-it to confirm');\n }\n\n const client = new APIClient(apiAddr);\n const sessions = await client.listSessions();\n\n if (sessions.length === 0) {\n console.log('No sessions to stop');\n return;\n }\n\n console.log(`Stopping ${sessions.length} sessions...`);\n\n for (const s of sessions) {\n process.stdout.write(` Stopping ${s.session_id}... `);\n try {\n await client.stopSession(s.session_id, true);\n console.log('done');\n } catch (err) {\n console.log(`ERROR: ${err}`);\n }\n }\n });\n","import { Command } from 'commander';\nimport open from 'open';\nimport { getAPIAddr, sleep } from '../lib/config.js';\nimport { isLoggedIn, loadCredentials, saveCredentials } from '../lib/auth.js';\nimport type { DeviceAuthResponse, TokenResponse } from '../types/index.js';\n\nexport const loginCommand = new Command('login')\n .description('Log in to Catty')\n .action(async function (this: Command) {\n const apiAddr = getAPIAddr(this.optsWithGlobals().api);\n\n if (isLoggedIn()) {\n const creds = loadCredentials();\n console.log(`Already logged in as ${creds?.email}`);\n console.log(\"Run 'catty logout' to log out first\");\n return;\n }\n\n console.log('Starting login...');\n\n // Step 1: Start device auth flow\n const authResp = await fetch(`${apiAddr}/v1/auth/device`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: '{}',\n });\n\n if (!authResp.ok) {\n throw new Error(`Failed to start auth: ${authResp.statusText}`);\n }\n\n const auth: DeviceAuthResponse = await authResp.json();\n\n // Step 2: Show code and open browser\n console.log('\\nYour confirmation code:\\n');\n console.log(` ${auth.user_code}\\n`);\n console.log(`Opening ${auth.verification_uri_complete}\\n`);\n\n await open(auth.verification_uri_complete);\n console.log('Waiting for authentication...');\n\n // Step 3: Poll for token\n const interval = (auth.interval || 5) * 1000;\n const deadline = Date.now() + auth.expires_in * 1000;\n\n while (Date.now() < deadline) {\n await sleep(interval);\n\n const tokenResp = await fetch(`${apiAddr}/v1/auth/device/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ device_code: auth.device_code }),\n });\n\n const token: TokenResponse = await tokenResp.json();\n\n if (token.pending) continue;\n if (token.error) throw new Error(token.error);\n\n if (token.access_token) {\n saveCredentials({\n access_token: token.access_token,\n refresh_token: token.refresh_token,\n user_id: token.user?.id || '',\n email: token.user?.email || '',\n expires_at: token.expires_in\n ? new Date(Date.now() + (token.expires_in - 30) * 1000).toISOString()\n : undefined,\n });\n console.log(`\\nLogged in as ${token.user?.email}`);\n console.log(\"You can now run 'catty new' to start a session\");\n return;\n }\n }\n\n throw new Error('Authentication timed out');\n });\n","import { Command } from 'commander';\nimport { isLoggedIn, loadCredentials, deleteCredentials } from '../lib/auth.js';\n\nexport const logoutCommand = new Command('logout')\n .description('Log out of Catty')\n .action(async () => {\n if (!isLoggedIn()) {\n console.log('Not logged in');\n return;\n }\n\n const creds = loadCredentials();\n const email = creds?.email || '';\n\n deleteCredentials();\n\n if (email) {\n console.log(`Logged out from ${email}`);\n } else {\n console.log('Logged out');\n }\n });\n","import { Command } from 'commander';\n\n// VERSION is replaced at build time by tsup\ndeclare const __VERSION__: string;\n\nexport const versionCommand = new Command('version')\n .description('Print the version number')\n .action(() => {\n console.log(typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'dev');\n });\n","import { Command } from 'commander';\nimport { checkForUpdate, runUpdate } from '../lib/version-checker.js';\n\ndeclare const __VERSION__: string;\n\nexport const updateCommand = new Command('update')\n .description('Update catty to the latest version')\n .action(async () => {\n try {\n const currentVersion = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'dev';\n\n if (currentVersion === 'dev') {\n console.log('Cannot update in development mode.');\n return;\n }\n\n console.log('Checking for updates...');\n const { updateAvailable, latestVersion } = await checkForUpdate();\n\n if (!updateAvailable || !latestVersion) {\n console.log(`You are already using the latest version (${currentVersion}).`);\n return;\n }\n\n // When manually running update command, always update regardless of declined status\n await runUpdate(currentVersion, latestVersion);\n } catch (err) {\n if (err instanceof Error) {\n console.error(`Error: ${err.message}`);\n }\n process.exit(1);\n }\n });\n","import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { createInterface } from 'readline';\nimport { spawn } from 'child_process';\nimport { CREDENTIALS_DIR } from './config.js';\n\ndeclare const __VERSION__: string;\n\ninterface VersionCache {\n latestVersion: string;\n lastChecked: number;\n declinedVersion?: string;\n declinedAt?: number;\n}\n\nconst VERSION_CHECK_INTERVAL = 15 * 60 * 1000; // 15 minutes\nconst DECLINED_VERSION_REMINDER_INTERVAL = 2 * 24 * 60 * 60 * 1000; // 2 days\nconst NPM_REGISTRY_URL = 'https://registry.npmjs.org/@diggerhq/catty/latest';\n\nfunction getVersionCachePath(): string {\n return join(homedir(), CREDENTIALS_DIR, 'version-cache.json');\n}\n\nfunction getCachedVersion(): VersionCache | null {\n try {\n const cachePath = getVersionCachePath();\n if (!existsSync(cachePath)) {\n return null;\n }\n const data = readFileSync(cachePath, 'utf-8');\n return JSON.parse(data);\n } catch {\n return null;\n }\n}\n\nfunction setCachedVersion(version: string, declined?: boolean): void {\n try {\n const cachePath = getVersionCachePath();\n const cacheDir = join(homedir(), CREDENTIALS_DIR);\n if (!existsSync(cacheDir)) {\n mkdirSync(cacheDir, { recursive: true });\n }\n\n // Preserve existing declined info if not updating it\n const existing = getCachedVersion();\n const cache: VersionCache = {\n latestVersion: version,\n lastChecked: Date.now(),\n declinedVersion: declined ? version : existing?.declinedVersion,\n declinedAt: declined ? Date.now() : existing?.declinedAt,\n };\n\n writeFileSync(cachePath, JSON.stringify(cache, null, 2));\n } catch {\n // Silently fail if we can't write cache\n }\n}\n\nasync function fetchLatestVersion(): Promise<string | null> {\n try {\n const response = await fetch(NPM_REGISTRY_URL, {\n signal: AbortSignal.timeout(5000), // 5 second timeout\n });\n if (!response.ok) {\n return null;\n }\n const data = await response.json();\n return data.version || null;\n } catch {\n return null;\n }\n}\n\nfunction compareVersions(current: string, latest: string): boolean {\n const currentParts = current.split('.').map(Number);\n const latestParts = latest.split('.').map(Number);\n\n for (let i = 0; i < 3; i++) {\n const curr = currentParts[i] || 0;\n const lat = latestParts[i] || 0;\n if (lat > curr) return true;\n if (lat < curr) return false;\n }\n return false;\n}\n\nexport async function checkForUpdate(): Promise<{\n updateAvailable: boolean;\n currentVersion: string;\n latestVersion: string | null;\n shouldPrompt: boolean;\n}> {\n const currentVersion =\n typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'dev';\n\n // Don't check in dev mode\n if (currentVersion === 'dev') {\n return {\n updateAvailable: false,\n currentVersion,\n latestVersion: null,\n shouldPrompt: false,\n };\n }\n\n // Check cache first\n const cached = getCachedVersion();\n const now = Date.now();\n\n let latestVersion: string | null = null;\n\n if (cached && now - cached.lastChecked < VERSION_CHECK_INTERVAL) {\n // Use cached version\n latestVersion = cached.latestVersion;\n } else {\n // Fetch latest version\n latestVersion = await fetchLatestVersion();\n if (latestVersion) {\n setCachedVersion(latestVersion);\n }\n }\n\n if (!latestVersion) {\n return {\n updateAvailable: false,\n currentVersion,\n latestVersion: null,\n shouldPrompt: false,\n };\n }\n\n const updateAvailable = compareVersions(currentVersion, latestVersion);\n\n // Check if user previously declined this version\n let shouldPrompt = updateAvailable;\n if (updateAvailable && cached?.declinedVersion === latestVersion && cached.declinedAt) {\n // User declined this version - only prompt again after the reminder interval\n const timeSinceDeclined = now - cached.declinedAt;\n shouldPrompt = timeSinceDeclined >= DECLINED_VERSION_REMINDER_INTERVAL;\n }\n\n return {\n updateAvailable,\n currentVersion,\n latestVersion,\n shouldPrompt,\n };\n}\n\nexport function printUpdateAvailable(\n currentVersion: string,\n latestVersion: string\n): void {\n console.log('');\n console.log(`\\x1b[33mUpdate available:\\x1b[0m \\x1b[2m${currentVersion}\\x1b[0m → \\x1b[32m${latestVersion}\\x1b[0m`);\n}\n\nexport async function promptForUpdate(): Promise<boolean> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise((resolve) => {\n rl.question('Would you like to update? (Y/n): ', (answer) => {\n rl.close();\n const normalized = answer.trim().toLowerCase();\n // Default to yes if empty or starts with 'y'\n resolve(normalized === '' || normalized === 'y' || normalized === 'yes');\n });\n });\n}\n\nexport function recordDeclinedUpdate(version: string): void {\n setCachedVersion(version, true);\n}\n\nfunction detectPackageManager(): string {\n // Check if npm is available\n const userAgent = process.env.npm_config_user_agent || '';\n\n if (userAgent.includes('yarn')) {\n return 'yarn';\n } else if (userAgent.includes('pnpm')) {\n return 'pnpm';\n } else if (userAgent.includes('bun')) {\n return 'bun';\n }\n\n return 'npm';\n}\n\nexport async function runUpdate(\n currentVersion: string,\n latestVersion: string\n): Promise<void> {\n console.log(`\\nUpdating from ${currentVersion} to ${latestVersion}...\\n`);\n\n const packageManager = detectPackageManager();\n let command: string;\n let args: string[];\n\n switch (packageManager) {\n case 'yarn':\n command = 'yarn';\n args = ['global', 'add', '@diggerhq/catty@latest'];\n break;\n case 'pnpm':\n command = 'pnpm';\n args = ['add', '-g', '@diggerhq/catty@latest'];\n break;\n case 'bun':\n command = 'bun';\n args = ['install', '-g', '@diggerhq/catty@latest'];\n break;\n default:\n command = 'npm';\n args = ['install', '-g', '@diggerhq/catty@latest'];\n }\n\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, {\n stdio: 'inherit',\n shell: process.platform === 'win32',\n });\n\n child.on('close', (code) => {\n if (code === 0) {\n console.log(\n `\\n\\x1b[32m✓\\x1b[0m Successfully updated to version ${latestVersion}`\n );\n resolve();\n } else {\n console.error(\n `\\n\\x1b[31m✗\\x1b[0m Update failed with exit code ${code}`\n );\n reject(new Error(`Update process exited with code ${code}`));\n }\n });\n\n child.on('error', (err) => {\n console.error(\n `\\n\\x1b[31m✗\\x1b[0m Failed to run update command: ${err.message}`\n );\n reject(err);\n });\n });\n}\n"],"mappings":"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","names":["program","Command","open","DEFAULT_API_ADDR","CREDENTIALS_DIR","CREDENTIALS_FILE","getAPIAddr","cliOption","DEFAULT_API_ADDR","sleep","ms","resolve","homedir","join","readFileSync","writeFileSync","mkdirSync","unlinkSync","existsSync","getCredentialsDir","join","homedir","CREDENTIALS_DIR","getCredentialsPath","CREDENTIALS_FILE","loadCredentials","path","content","readFileSync","saveCredentials","creds","dir","mkdirSync","writeFileSync","deleteCredentials","existsSync","unlinkSync","isLoggedIn","getAccessToken","getRefreshToken","APIError","statusCode","errorCode","message","upgradeURL","APIClient","baseURL","DEFAULT_API_ADDR","getAccessToken","method","path","body","controller","timeoutId","headers","response","refreshToken","getRefreshToken","data","creds","loadCredentials","saveCredentials","errorData","req","idOrLabel","live","del","WebSocket","Terminal","cleanup","callback","MessageType","parseMessage","data","base","createResizeMessage","cols","rows","createPongMessage","MessageType","createSyncBackMessage","enabled","writeFileSync","unlinkSync","mkdirSync","renameSync","join","dirname","normalize","isAbsolute","sep","applyRemoteFileChange","msg","rel","cwd","destPath","resolvedCwd","resolvedDest","content","tmpPath","connectToSession","opts","terminal","Terminal","ws","WebSocket","resolve","reject","syncBackAcked","exitCode","handleResize","cols","rows","createResizeMessage","cleanup","handleStdinData","data","createSyncBackMessage","isBinary","msg","parseMessage","handleControlMessage","exitMsg","errorMsg","createPongMessage","applyRemoteFileChange","code","err","archiver","ignore","readFileSync","readdirSync","statSync","join","relative","DEFAULT_IGNORES","createWorkspaceZip","dir","ig","ignore","gitignore","readFileSync","join","resolve","reject","chunks","archive","archiver","chunk","walkDir","baseDir","currentDir","entries","readdirSync","entry","fullPath","relativePath","relative","stat","statSync","uploadWorkspace","uploadURL","token","machineID","cwd","zipData","response","text","buildUploadURL","connectURL","newCommand","Command","opts","apiAddr","getAPIAddr","isLoggedIn","client","APIClient","cmdArgs","session","err","APIError","handleQuotaExceeded","uploadURL","buildUploadURL","uploadWorkspace","connectToSession","checkoutURL","open","Command","connectCommand","Command","label","opts","apiAddr","getAPIAddr","isLoggedIn","client","APIClient","session","connectToSession","Command","listCommand","Command","apiAddr","getAPIAddr","sessions","APIClient","s","age","formatAge","row","date","seconds","minutes","hours","Command","stopCommand","Command","label","opts","apiAddr","getAPIAddr","APIClient","Command","stopAllCommand","Command","opts","apiAddr","getAPIAddr","client","APIClient","sessions","s","err","Command","open","loginCommand","Command","apiAddr","getAPIAddr","isLoggedIn","creds","loadCredentials","authResp","auth","open","interval","deadline","sleep","token","saveCredentials","Command","logoutCommand","Command","isLoggedIn","email","loadCredentials","deleteCredentials","Command","versionCommand","Command","readFileSync","writeFileSync","existsSync","mkdirSync","join","homedir","createInterface","spawn","VERSION_CHECK_INTERVAL","DECLINED_VERSION_REMINDER_INTERVAL","NPM_REGISTRY_URL","getVersionCachePath","join","homedir","CREDENTIALS_DIR","getCachedVersion","cachePath","existsSync","data","readFileSync","setCachedVersion","version","declined","cacheDir","mkdirSync","existing","cache","writeFileSync","fetchLatestVersion","response","compareVersions","current","latest","currentParts","latestParts","i","curr","lat","checkForUpdate","currentVersion","cached","now","latestVersion","updateAvailable","shouldPrompt","printUpdateAvailable","promptForUpdate","rl","createInterface","resolve","answer","normalized","recordDeclinedUpdate","detectPackageManager","userAgent","runUpdate","packageManager","command","args","reject","child","spawn","code","err","updateCommand","Command","currentVersion","updateAvailable","latestVersion","checkForUpdate","runUpdate","err","version","program","newCommand","connectCommand","listCommand","stopCommand","stopAllCommand","loginCommand","logoutCommand","versionCommand","updateCommand","main","command","updateAvailable","currentVersion","latestVersion","shouldPrompt","checkForUpdate","printUpdateAvailable","promptForUpdate","runUpdate","recordDeclinedUpdate","err"]}
|