@typhons/cli 0.2.9 → 0.3.0
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/cli.js +3 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import f from"fs";import
|
|
3
|
-
`);
|
|
2
|
+
import f from"fs";import l from"path";import{fileURLToPath as se}from"url";import{execSync as A,execFileSync as ie}from"child_process";import O from"fs";import E from"path";function G(o){let t=[E.join(o,".devcontainer","devcontainer.json"),E.join(o,".devcontainer.json")];for(let e of t)if(O.existsSync(e))return e;return null}function Q(o){let t=O.readFileSync(o,"utf8");return t=t.replace(/"(?:[^"\\]|\\.)*"|\/\/.*$|\/\*[\s\S]*?\*\//gm,e=>e.startsWith('"')?e:""),t=t.replace(/,\s*([}\]])/g,"$1"),JSON.parse(t)}function j(o){let t=G(o);if(!t)return{jsonPath:null,jsonPathRel:null,workspaceFolder:`/workspaces/${E.basename(o)}`,remoteUser:"root",postCreateCommand:null,postStartCommand:null,service:"app",forwardPorts:[],dockerComposeFile:null};let e=Q(t),s=e.postCreateCommand?typeof e.postCreateCommand=="string"?e.postCreateCommand:Array.isArray(e.postCreateCommand)?e.postCreateCommand.join(" "):Object.values(e.postCreateCommand).join(" && "):null,p=e.postStartCommand?typeof e.postStartCommand=="string"?e.postStartCommand:Array.isArray(e.postStartCommand)?e.postStartCommand.join(" "):Object.values(e.postStartCommand).join(" && "):null;return{jsonPath:t,jsonPathRel:E.relative(o,t),workspaceFolder:e.workspaceFolder||`/workspaces/${E.basename(o)}`,remoteUser:e.remoteUser||"root",postCreateCommand:s,postStartCommand:p,service:e.service||"app",forwardPorts:Array.isArray(e.forwardPorts)?e.forwardPorts.map(Number).filter(g=>g>0):[],dockerComposeFile:e.dockerComposeFile||null}}import _ from"fs";import V from"os";import B from"path";import{execSync as X}from"child_process";import{fileURLToPath as Z}from"url";function I(){throw new Error("Direct template builds are not available in the published CLI. Remove --direct-build flag to use server-proxied builds instead.")}var Se=B.dirname(Z(import.meta.url));function T(o){console.error(`[${new Date().toISOString().slice(11,19)}] ${o}`)}function H(o){return`'${String(o).replace(/'/g,`'"'"'`)}'`}function F(o,t=[]){let e=B.join(V.tmpdir(),`e2b-repo-${Date.now()}.tar.gz`);T("Creating repo tarball...");let s=["node_modules",".codex-worktrees",...t].map(p=>`--exclude=${H(p)}`).join(" ");return X(`tar czf ${H(e)} ${s} .`,{cwd:o,maxBuffer:500*1024*1024,env:{...process.env,COPYFILE_DISABLE:"1"}}),e}async function M(o){let t=F(o.repoDir,o.ignorePatterns);try{return await I({tarballPath:t,mode:"raw",tag:o.tag,remoteUser:o.remoteUser,workspace:o.workspace,script:o.script,startupScript:o.startupScript,sshPublicKey:o.sshPublicKey,cpuCount:o.cpuCount,memoryMB:o.memoryMB,repoBasename:B.basename(o.repoDir),noRepo:o.noRepo,image:o.image},T)}finally{try{_.unlinkSync(t)}catch{}}}async function L(o){let t=F(o.repoDir,o.ignorePatterns);try{return await I({tarballPath:t,mode:"devcontainer",tag:o.tag,remoteUser:o.remoteUser,workspace:o.workspace,postCreateCommand:o.postCreateCommand,postStartCommand:o.postStartCommand,composeService:o.composeService,sshPublicKey:o.sshPublicKey,cpuCount:o.cpuCount,memoryMB:o.memoryMB,repoBasename:B.basename(o.repoDir)},T)}finally{try{_.unlinkSync(t)}catch{}}}async function N(o){let t=F(o.repoDir,o.ignorePatterns),e=B.resolve(o.repoDir,o.dockerfile),s=_.readFileSync(e,"utf8");try{return await I({tarballPath:t,mode:"dockerfile",tag:o.tag,remoteUser:o.remoteUser,workspace:o.workspace,script:o.script,startupScript:o.startupScript,dockerfileContent:s,sshPublicKey:o.sshPublicKey,cpuCount:o.cpuCount,memoryMB:o.memoryMB,repoBasename:B.basename(o.repoDir)},T)}finally{try{_.unlinkSync(t)}catch{}}}import K from"fs";import ee from"os";import oe from"path";import{execSync as re}from"child_process";function $(o){console.error(`[${new Date().toISOString().slice(11,19)}] ${o}`)}function Y(o){return`'${String(o).replace(/'/g,`'"'"'`)}'`}function te(o,t=[]){let e=oe.join(ee.tmpdir(),`repo-upload-${Date.now()}.tar.gz`),s=["node_modules",".codex-worktrees",...t].map(p=>`--exclude=${Y(p)}`).join(" ");return re(`tar czf ${Y(e)} ${s} .`,{cwd:o,maxBuffer:500*1024*1024,env:{...process.env,COPYFILE_DISABLE:"1"}}),e}async function x({repoDir:o,serverUrl:t,apiKey:e,config:s}){$("Creating repo tarball for upload...");let p=te(o,s.ignorePatterns),g=K.statSync(p).size;$(`Tarball size: ${(g/1024/1024).toFixed(1)} MB`);try{let y;if(g>20971520){$("Tarball exceeds 20MB, using GCS upload path...");let d=await fetch(`${t}/api/builds/upload-url`,{headers:{Authorization:`Bearer ${e}`}});if(!d.ok){let m=new Error(`Failed to get upload URL: HTTP ${d.status}`);throw m.statusCode=d.status,m}let{uploadUrl:a,gcsPath:n}=await d.json();$("Uploading tarball to cloud storage...");let c=await fetch(a,{method:"PUT",headers:{"Content-Type":"application/gzip","Content-Length":String(g)},body:K.createReadStream(p),duplex:"half"});if(!c.ok)throw new Error(`GCS upload failed: HTTP ${c.status}`);$("Upload complete, starting build...");let{FormData:r}=globalThis,i=new r;i.append("gcsPath",n),i.append("config",JSON.stringify(s)),y=await fetch(`${t}/api/builds`,{method:"POST",headers:{Authorization:`Bearer ${e}`},body:i})}else{let{FormData:d,Blob:a}=globalThis,n=new d,c=K.readFileSync(p);n.append("tarball",new a([c]),"repo.tar.gz"),n.append("config",JSON.stringify(s)),$(`Uploading to ${t}/api/builds ...`),y=await fetch(`${t}/api/builds`,{method:"POST",headers:{Authorization:`Bearer ${e}`},body:n})}if(!y.ok&&!y.headers.get("content-type")?.includes("text/event-stream")){let d=await y.text(),a;try{a=JSON.parse(d).error}catch{a=d}let n=new Error(`Server build failed (HTTP ${y.status}): ${a}`);throw n.statusCode=y.status,n}let R=y.body.getReader(),w=new TextDecoder,v="",u=null;for(;;){let{done:d,value:a}=await R.read();if(d)break;v+=w.decode(a,{stream:!0});let n=v.split(`
|
|
3
|
+
`);v=n.pop()||"";for(let c of n)if(c.startsWith("data: ")){let r=c.slice(6);try{let i=JSON.parse(r);if(i.type==="log")$(i.message);else if(i.type==="done")u={snapshotId:i.snapshotId,templateId:i.templateId,buildId:i.buildId};else if(i.type==="error")throw new Error(`Server build error: ${i.error}`)}catch(i){if(i.message?.startsWith("Server build error:"))throw i}}}if(!u)throw new Error("Server build ended without returning a result");return u}finally{try{K.unlinkSync(p)}catch{}}}var q=l.dirname(se(import.meta.url));if(!process.argv.includes("--no-update")&&process.env.TYPHONS_NO_UPDATE!=="1")try{let o=l.join(q,"..","package.json"),t=JSON.parse(f.readFileSync(o,"utf8")),e=t.version,s=A(`npm view ${t.name} version 2>/dev/null`,{timeout:1e4}).toString().trim();if(s&&s!==e){console.log(`Updating ${t.name}: ${e} \u2192 ${s}...`),A(`npm install -g ${t.name}@latest`,{timeout:12e4,stdio:"inherit"});let p=[...process.argv.slice(1),"--no-update"];try{ie(process.argv[0],p,{stdio:"inherit"})}catch(g){process.exit(g.status||1)}process.exit(0)}}catch{}var ne=process.argv.filter(o=>o!=="--no-update");function W(o){if(!f.existsSync(o))return{};let t={};for(let e of f.readFileSync(o,"utf8").split(/\r?\n/)){if(!e||/^\s*#/.test(e))continue;let s=e.match(/^\s*([^=]+?)\s*=\s*(.*)\s*$/);if(!s)continue;let p=s[2];(p.startsWith('"')&&p.endsWith('"')||p.startsWith("'")&&p.endsWith("'"))&&(p=p.slice(1,-1)),t[s[1]]=p}return t}async function J(o){if(!process.stdin.isTTY)return null;let t=`${o}/cli/auth`;console.log(""),console.log("Authenticate with the server to continue."),console.log(""),console.log(" Open this URL in your browser and authorize:"),console.log(` ${t}`),console.log("");try{process.platform==="darwin"?A(`open "${t}"`,{stdio:"ignore"}):A(`xdg-open "${t}" 2>/dev/null || true`,{stdio:"ignore"})}catch{}let s=(await import("readline")).createInterface({input:process.stdin,output:process.stdout}),p=await new Promise(y=>s.question("Paste your API key here: ",y));s.close();let g=p.trim();if(!g)return null;let P=l.join(process.env.HOME||"",".config","typhons");return f.mkdirSync(P,{recursive:!0}),f.writeFileSync(l.join(P,"api-key"),g,{mode:384}),console.log(`API key saved to ${l.join(P,"api-key")}`),g}function z(){console.log(`Usage: typhons build [options] <repo-dir>
|
|
4
4
|
|
|
5
5
|
Build a typhon image for a repo. The repo will be copied over
|
|
6
6
|
|
|
@@ -23,4 +23,4 @@ Options:
|
|
|
23
23
|
--workspace PATH Workspace (repo) path inside dev server (default: /home/user/<repo-basename>)
|
|
24
24
|
--no-repo Don't copy the repo into the dev server
|
|
25
25
|
-h, --help Show this help
|
|
26
|
-
`),process.exit(0)}async function ae(){let o=ne.slice(2);(o.length===0||o[0]==="--help"||o[0]==="-h")&&z();let t=o[0];t!=="build"&&(console.error(`Unknown command: ${t}`),console.error("Available commands: build"),process.exit(1));let e={tag:new Date().toISOString().replace(/[-:T]/g,"").slice(0,8)+"-"+new Date().toISOString().replace(/[-:T]/g,"").slice(8,14),ports:"",apiKey:process.env.BUILD_API_KEY||"",serverUrl:process.env.BUILD_SERVER_URL||"https://www.typhons.dev",directBuild:!1,devcontainer:!1,script:"",dockerfile:"",image:"",startupScript:"",sshKey:"",workspace:"",remoteUser:"",repoDir:"",cpuCount:0,memoryMB:0,noRepo:!1,ignorePatterns:[],ignoreFiles:[]},s=o.slice(1);for(let r=0;r<s.length;r++)switch(s[r]){case"-t":case"--tag":e.tag=s[++r];break;case"-p":case"--ports":e.ports=s[++r];break;case"--api-key":e.apiKey=s[++r];break;case"--server-url":e.serverUrl=s[++r];break;case"--direct-build":e.directBuild=!0;break;case"--devcontainer":e.devcontainer=!0;break;case"--script":e.script=s[++r];break;case"--dockerfile":console.error("Error: --dockerfile is not yet supported. Use --image instead."),process.exit(1);case"--image":e.image=s[++r];break;case"--startup-script":e.startupScript=s[++r];break;case"--ssh-key":e.sshKey=s[++r];break;case"--workspace":e.workspace=s[++r];break;case"--user":case"--remote-user":e.remoteUser=s[++r];break;case"--cpu":e.cpuCount=parseInt(s[++r],10);break;case"--memory":e.memoryMB=Math.round(parseFloat(s[++r])*1024);break;case"--no-repo":e.noRepo=!0;break;case"--ignore":e.ignorePatterns.push(...s[++r].split(","));break;case"--ignore-file":e.ignoreFiles.push(s[++r]);break;case"-h":case"--help":z();break;default:s[r].startsWith("-")&&(console.error(`Unknown option: ${s[r]}`),process.exit(1)),e.repoDir=s[r]}e.repoDir||(console.error("Error: repo directory is required"),process.exit(1)),e.repoDir=c.resolve(e.repoDir);for(let r of e.ignoreFiles){let i=c.resolve(r);f.existsSync(i)||(console.error(`Error: ignore file not found: ${i}`),process.exit(1));let m=f.readFileSync(i,"utf8").split(/\r?\n/);for(let S of m){let g=S.trim();!g||g.startsWith("#")||e.ignorePatterns.push(g)}}if(!e.script&&!e.dockerfile&&!e.image&&!e.devcontainer){let r=j(e.repoDir);if(r.jsonPath)if(process.stdin.isTTY){let m=(await import("readline")).createInterface({input:process.stdin,output:process.stdout});console.log(`.devcontainer detected: ${r.jsonPath}`),console.log(" 1) Use devcontainer (recommended) (pass --devcontainer to skip this prompt)"),console.log(" 2) Raw build (just copy repo, no package install)");let S=await new Promise(C=>m.question("Select build mode [1]: ",C));m.close(),S.trim()==="2"?console.log(""):e.devcontainer=!0}else e.devcontainer=!0;else console.log("No .devcontainer found, making a default build with just the repo and a base image. Pass --script, --startup-script, and/or --image to install dependencies")}let p=c.join(q,".."),h=W(c.join(p,".env")),P=W(c.join(e.repoDir,".env")),y=process.env.HOME||"",R=(f.existsSync(c.join(y,".config","typhons","api-key"))?f.readFileSync(c.join(y,".config","typhons","api-key"),"utf8").trim():"")||(f.existsSync(c.join(y,".config","remote-claude","api-key"))?f.readFileSync(c.join(y,".config","remote-claude","api-key"),"utf8").trim():"");e.apiKey||(e.apiKey=R||h.BUILD_API_KEY||P.BUILD_API_KEY||"");let w=process.env.E2B_API_KEY||process.env.E2B_API_TOKEN||h.E2B_API_KEY||h.E2B_API_TOKEN||"";w&&(process.env.E2B_API_KEY=w);let b=!w&&!e.directBuild&&e.apiKey;if(!w&&!b)if(e.directBuild&&(console.error("Error: E2B_API_KEY or E2B_API_TOKEN is required for --direct-build"),process.exit(1)),!e.apiKey&&process.stdin.isTTY){console.log("No API key found.");let r=await J(e.serverUrl);r||(console.error("No key provided."),process.exit(1)),e.apiKey=r}else e.apiKey||(console.error("Error: E2B_API_KEY or BUILD_API_KEY is required for template builds."),console.error(" Set E2B_API_KEY for direct builds, or BUILD_API_KEY to build via the server."),console.error(" Run without --direct-build to authenticate interactively."),process.exit(1));b=!w&&!e.directBuild&&e.apiKey,e.directBuild&&!w&&(console.error("Error: E2B_API_KEY or E2B_API_TOKEN is required for --direct-build"),process.exit(1));let u="";if(e.sshKey){let r=c.resolve(e.sshKey);f.existsSync(r)||(console.error(`Error: SSH key file not found: ${r}`),process.exit(1)),u=f.readFileSync(r,"utf8").trim()}else u=process.env.SSH_PUBLIC_KEY||h.SSH_PUBLIC_KEY||P.SSH_PUBLIC_KEY||"";if(u){let r=u.split(" "),i=r.length>=3?r[2]:(r[1]||"").slice(0,20)+"...";console.log(`SSH key: ${r[0]} ${i}`)}if(!u){process.stdin.isTTY||(console.error("Error: SSH public key is required. Set SSH_PUBLIC_KEY env var or pass --ssh-key <path>."),process.exit(1));let r=process.env.HOME||"",i=c.join(r,".ssh"),m=[];if(f.existsSync(i))for(let v of f.readdirSync(i).sort())v.endsWith(".pub")&&m.push(v);let g=(await import("readline")).createInterface({input:process.stdin,output:process.stdout}),C=v=>new Promise(D=>g.question(v,D));console.log("SSH public key is required for dev server access."),m.length>0&&console.log("Found keys in ~/.ssh:"),m.forEach((v,D)=>console.log(` ${D+1}) ${v}`)),console.log(" p) Paste a public key"),console.log(" q) Exit");let k=(await C(m.length>0?"Select a key [1]: ":"Select an option: ")).trim();if(k==="q"||k==="Q")g.close(),process.exit(0);else if(k==="p"||k==="P"||m.length===0&&k!=="q")u=(await C("Paste your SSH public key: ")).trim(),(!u||!u.startsWith("ssh-"))&&(console.error("Error: Invalid SSH public key (should start with ssh-ed25519, ssh-rsa, etc.)"),g.close(),process.exit(1));else{let v=k===""?1:parseInt(k,10);if(v>0&&v<=m.length){let D=c.join(i,m[v-1]);u=f.readFileSync(D,"utf8").trim(),console.log(`Using ${m[v-1]}`)}else console.error("Invalid selection."),g.close(),process.exit(1)}g.close()}let d,a,n,l;if(e.dockerfile){let r="",i="";try{let S=f.readFileSync(c.resolve(e.repoDir,e.dockerfile),"utf8");if(!e.remoteUser){let g=S.match(/^\s*USER\s+(\S+)/gmi);g&&(r=g[g.length-1].match(/USER\s+(\S+)/i)[1])}if(!e.startupScript){let g=S.match(/^\s*CMD\s+(.+)$/mi),C=S.match(/^\s*ENTRYPOINT\s+(.+)$/mi),x=g?.[1]||C?.[1]||"";if(x)try{let k=JSON.parse(x);Array.isArray(k)&&(i=k.join(" "))}catch{i=x.trim()}}}catch{}n=e.remoteUser||r||"user",i&&!e.startupScript&&(e.startupScript=i);let m=n==="root"?"/root":`/home/${n}`;if(l=e.workspace||`${m}/${c.basename(e.repoDir)}`,a=e.ports||"",console.log(`==> Building E2B template (Dockerfile mode): ${e.tag}`),console.log(` Dockerfile: ${e.dockerfile}`),console.log(` Workspace: ${l}`),console.log(` Remote user: ${n}`),e.startupScript&&console.log(` Startup script: ${e.startupScript} ${i?"(from Dockerfile CMD)":""}`),a&&console.log(` Ports: ${a}`),console.log(""),b){console.log("==> Building template via server...");let S=f.readFileSync(c.resolve(e.repoDir,e.dockerfile),"utf8");d=await U({repoDir:e.repoDir,serverUrl:e.serverUrl,apiKey:e.apiKey,config:{mode:"dockerfile",tag:e.tag,remoteUser:n,workspace:l,dockerfileContent:S,script:e.script||void 0,startupScript:e.startupScript,ports:a,sshPublicKey:u,cpuCount:e.cpuCount||void 0,memoryMB:e.memoryMB||void 0,repoBasename:c.basename(e.repoDir),ignorePatterns:e.ignorePatterns}})}else console.log("==> Building template from Dockerfile..."),d=await L({repoDir:e.repoDir,workspace:l,tag:e.tag,remoteUser:n,dockerfile:e.dockerfile,script:e.script||void 0,startupScript:e.startupScript,ports:a,sshPublicKey:u,cpuCount:e.cpuCount||void 0,memoryMB:e.memoryMB||void 0,ignorePatterns:e.ignorePatterns})}else if(e.script||e.image){n=e.remoteUser||"user";let r=n==="root"?"/root":`/home/${n}`;l=e.workspace||`${r}/${c.basename(e.repoDir)}`,a=e.ports||"",console.log(`==> Building E2B template (${e.image?"image":"raw"} mode): ${e.tag}`),e.image&&console.log(` Image: ${e.image}`),console.log(` Workspace: ${l}`),console.log(` Remote user: ${n}`),e.script&&console.log(` Script: ${e.script}`),e.startupScript&&console.log(` Startup script: ${e.startupScript}`),a&&console.log(` Ports: ${a}`),console.log(""),b?(console.log("==> Building template via server..."),d=await U({repoDir:e.repoDir,serverUrl:e.serverUrl,apiKey:e.apiKey,config:{mode:"raw",tag:e.tag,remoteUser:n,workspace:l,image:e.image||void 0,script:e.script,startupScript:e.startupScript,ports:a,sshPublicKey:u,cpuCount:e.cpuCount||void 0,memoryMB:e.memoryMB||void 0,repoBasename:c.basename(e.repoDir),ignorePatterns:e.ignorePatterns}})):(console.log("==> Building template..."),d=await M({repoDir:e.repoDir,workspace:l,tag:e.tag,remoteUser:n,image:e.image||void 0,script:e.script,startupScript:e.startupScript,ports:a,sshPublicKey:u,cpuCount:e.cpuCount||void 0,memoryMB:e.memoryMB||void 0,noRepo:e.noRepo,ignorePatterns:e.ignorePatterns}))}else if(e.devcontainer){let r=j(e.repoDir);r.jsonPath?console.log(`==> Found devcontainer config: ${r.jsonPath}`):(console.error("Error: --devcontainer specified but no .devcontainer config found in "+e.repoDir),process.exit(1)),n=e.remoteUser||r.remoteUser,l=e.workspace||r.workspaceFolder,a=e.ports||r.forwardPorts.join(","),console.log(`==> Building E2B template: ${e.tag}`),console.log(` Workspace: ${l}`),console.log(` Remote user: ${n}`),console.log(` Service: ${r.service}`),a&&console.log(` Ports: ${a}`),r.postCreateCommand&&console.log(` postCreateCommand: ${r.postCreateCommand}`),r.postStartCommand&&console.log(` postStartCommand: ${r.postStartCommand}`),console.log(""),b?(console.log("==> Building template via server..."),d=await U({repoDir:e.repoDir,serverUrl:e.serverUrl,apiKey:e.apiKey,config:{mode:"devcontainer",tag:e.tag,remoteUser:n,workspace:l,postCreateCommand:r.postCreateCommand,postStartCommand:r.postStartCommand,composeService:r.service,sshPublicKey:u,cpuCount:e.cpuCount||void 0,memoryMB:e.memoryMB||void 0,repoBasename:c.basename(e.repoDir),ignorePatterns:e.ignorePatterns}})):(console.log("==> Building template..."),d=await N({repoDir:e.repoDir,workspace:l,tag:e.tag,remoteUser:n,postCreateCommand:r.postCreateCommand,postStartCommand:r.postStartCommand,composeService:r.service,sshPublicKey:u,cpuCount:e.cpuCount||void 0,memoryMB:e.memoryMB||void 0,ignorePatterns:e.ignorePatterns}))}else{n=e.remoteUser||"user";let r=n==="root"?"/root":`/home/${n}`;l=e.workspace||`${r}/${c.basename(e.repoDir)}`,a=e.ports||"",console.log(`==> Building E2B template: ${e.tag}`),console.log(` Workspace: ${l}`),console.log(` Remote user: ${n}`),a&&console.log(` Ports: ${a}`),console.log(""),b?(console.log("==> Building template via server..."),d=await U({repoDir:e.repoDir,serverUrl:e.serverUrl,apiKey:e.apiKey,config:{mode:"raw",tag:e.tag,remoteUser:n,workspace:l,sshPublicKey:u,cpuCount:e.cpuCount||void 0,memoryMB:e.memoryMB||void 0,repoBasename:c.basename(e.repoDir),ignorePatterns:e.ignorePatterns}})):(console.log("==> Building template..."),d=await M({repoDir:e.repoDir,workspace:l,tag:e.tag,remoteUser:n,ports:a,sshPublicKey:u,cpuCount:e.cpuCount||void 0,memoryMB:e.memoryMB||void 0,ignorePatterns:e.ignorePatterns}))}if(console.log(""),console.log(`==> Snapshot ID: ${d.snapshotId}`),e.apiKey){console.log("==> Registering image with server...");let r=a?a.split(",").map(Number).filter(i=>i>0):[];try{let i=await fetch(`${e.serverUrl}/api/images`,{method:"POST",headers:{Authorization:`Bearer ${e.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({tag:e.tag,image:d.snapshotId,type:e.dockerfile?"e2b-dockerfile":e.script?"e2b-raw":"e2b-devcontainer",build_method:"template",ports:r,cmd:[],remote_user:n,workspace_folder:l,built_at:new Date().toISOString()})});i.ok?console.log("==> Registered successfully"):console.log(`WARNING: Failed to register image with server (HTTP ${i.status})`)}catch(i){console.log(`WARNING: Failed to register image with server: ${i.message}`)}}console.log(""),console.log("==> Done!"),console.log(` Template tag: ${e.tag}`),console.log(` Snapshot ID: ${d.snapshotId}`),console.log(` Remote user: ${n}`),console.log(` Workspace: ${l}`),a&&console.log(` Ports: ${a}`)}ae().catch(async o=>{o.statusCode===401&&process.stdin.isTTY&&(console.error("Authentication failed. Your API key may be invalid or expired."),await J(process.env.BUILD_SERVER_URL||"https://www.typhons.dev")&&console.log("API key updated. Please re-run your build command."),process.exit(1)),console.error("Error:",o.message),process.exit(1)});
|
|
26
|
+
`),process.exit(0)}async function ae(){let o=ne.slice(2);(o.length===0||o[0]==="--help"||o[0]==="-h")&&z();let t=o[0];t!=="build"&&(console.error(`Unknown command: ${t}`),console.error("Available commands: build"),process.exit(1));let e={tag:new Date().toISOString().replace(/[-:T]/g,"").slice(0,8)+"-"+new Date().toISOString().replace(/[-:T]/g,"").slice(8,14),ports:"",apiKey:process.env.BUILD_API_KEY||"",serverUrl:process.env.BUILD_SERVER_URL||"https://www.typhons.dev",directBuild:!1,devcontainer:!1,script:"",dockerfile:"",image:"",startupScript:"",sshKey:"",workspace:"",remoteUser:"",repoDir:"",cpuCount:0,memoryMB:0,noRepo:!1,ignorePatterns:[],ignoreFiles:[]},s=o.slice(1);for(let r=0;r<s.length;r++)switch(s[r]){case"-t":case"--tag":e.tag=s[++r];break;case"-p":case"--ports":e.ports=s[++r];break;case"--api-key":e.apiKey=s[++r];break;case"--server-url":e.serverUrl=s[++r];break;case"--direct-build":e.directBuild=!0;break;case"--devcontainer":e.devcontainer=!0;break;case"--script":e.script=s[++r];break;case"--dockerfile":console.error("Error: --dockerfile is not yet supported. Use --image instead."),process.exit(1);case"--image":e.image=s[++r];break;case"--startup-script":e.startupScript=s[++r];break;case"--ssh-key":e.sshKey=s[++r];break;case"--workspace":e.workspace=s[++r];break;case"--user":case"--remote-user":e.remoteUser=s[++r];break;case"--cpu":e.cpuCount=parseInt(s[++r],10);break;case"--memory":e.memoryMB=Math.round(parseFloat(s[++r])*1024);break;case"--no-repo":e.noRepo=!0;break;case"--ignore":e.ignorePatterns.push(...s[++r].split(","));break;case"--ignore-file":e.ignoreFiles.push(s[++r]);break;case"-h":case"--help":z();break;default:s[r].startsWith("-")&&(console.error(`Unknown option: ${s[r]}`),process.exit(1)),e.repoDir=s[r]}e.repoDir||(console.error("Error: repo directory is required"),process.exit(1)),e.repoDir=l.resolve(e.repoDir);for(let r of e.ignoreFiles){let i=l.resolve(r);f.existsSync(i)||(console.error(`Error: ignore file not found: ${i}`),process.exit(1));let m=f.readFileSync(i,"utf8").split(/\r?\n/);for(let b of m){let h=b.trim();!h||h.startsWith("#")||e.ignorePatterns.push(h)}}if(!e.script&&!e.dockerfile&&!e.image&&!e.devcontainer){let r=j(e.repoDir);if(r.jsonPath)if(process.stdin.isTTY){let m=(await import("readline")).createInterface({input:process.stdin,output:process.stdout});console.log(`.devcontainer detected: ${r.jsonPath}`),console.log(" 1) Use devcontainer (recommended) (pass --devcontainer to skip this prompt)"),console.log(" 2) Raw build (just copy repo, no package install)");let b=await new Promise(C=>m.question("Select build mode [1]: ",C));m.close(),b.trim()==="2"?console.log(""):e.devcontainer=!0}else e.devcontainer=!0;else console.log("No .devcontainer found, making a default build with just the repo and a base image. Pass --script, --startup-script, and/or --image to install dependencies")}let p=l.join(q,".."),g=W(l.join(p,".env")),P=W(l.join(e.repoDir,".env")),y=process.env.HOME||"",R=(f.existsSync(l.join(y,".config","typhons","api-key"))?f.readFileSync(l.join(y,".config","typhons","api-key"),"utf8").trim():"")||(f.existsSync(l.join(y,".config","remote-claude","api-key"))?f.readFileSync(l.join(y,".config","remote-claude","api-key"),"utf8").trim():"");e.apiKey||(e.apiKey=R||g.BUILD_API_KEY||P.BUILD_API_KEY||"");let w=process.env.E2B_API_KEY||process.env.E2B_API_TOKEN||g.E2B_API_KEY||g.E2B_API_TOKEN||"";w&&(process.env.E2B_API_KEY=w);let v=!w&&!e.directBuild&&e.apiKey;if(!w&&!v)if(e.directBuild&&(console.error("Error: E2B_API_KEY or E2B_API_TOKEN is required for --direct-build"),process.exit(1)),!e.apiKey&&process.stdin.isTTY){console.log("No API key found.");let r=await J(e.serverUrl);r||(console.error("No key provided."),process.exit(1)),e.apiKey=r}else e.apiKey||(console.error("Error: E2B_API_KEY or BUILD_API_KEY is required for template builds."),console.error(" Set E2B_API_KEY for direct builds, or BUILD_API_KEY to build via the server."),console.error(" Run without --direct-build to authenticate interactively."),process.exit(1));v=!w&&!e.directBuild&&e.apiKey,e.directBuild&&!w&&(console.error("Error: E2B_API_KEY or E2B_API_TOKEN is required for --direct-build"),process.exit(1));let u="";if(e.sshKey){let r=l.resolve(e.sshKey);f.existsSync(r)||(console.error(`Error: SSH key file not found: ${r}`),process.exit(1)),u=f.readFileSync(r,"utf8").trim()}else u=process.env.SSH_PUBLIC_KEY||g.SSH_PUBLIC_KEY||P.SSH_PUBLIC_KEY||"";if(u){let r=u.split(" "),i=r.length>=3?r[2]:(r[1]||"").slice(0,20)+"...";console.log(`SSH key: ${r[0]} ${i}`)}if(!u){process.stdin.isTTY||(console.error("Error: SSH public key is required. Set SSH_PUBLIC_KEY env var or pass --ssh-key <path>."),process.exit(1));let r=process.env.HOME||"",i=l.join(r,".ssh"),m=[];if(f.existsSync(i))for(let S of f.readdirSync(i).sort())S.endsWith(".pub")&&m.push(S);let h=(await import("readline")).createInterface({input:process.stdin,output:process.stdout}),C=S=>new Promise(D=>h.question(S,D));console.log("SSH public key is required for dev server access."),m.length>0&&console.log("Found keys in ~/.ssh:"),m.forEach((S,D)=>console.log(` ${D+1}) ${S}`)),console.log(" p) Paste a public key"),console.log(" q) Exit");let k=(await C(m.length>0?"Select a key [1]: ":"Select an option: ")).trim();if(k==="q"||k==="Q")h.close(),process.exit(0);else if(k==="p"||k==="P"||m.length===0&&k!=="q")u=(await C("Paste your SSH public key: ")).trim(),(!u||!u.startsWith("ssh-"))&&(console.error("Error: Invalid SSH public key (should start with ssh-ed25519, ssh-rsa, etc.)"),h.close(),process.exit(1));else{let S=k===""?1:parseInt(k,10);if(S>0&&S<=m.length){let D=l.join(i,m[S-1]);u=f.readFileSync(D,"utf8").trim(),console.log(`Using ${m[S-1]}`)}else console.error("Invalid selection."),h.close(),process.exit(1)}h.close()}let d,a,n,c;if(e.dockerfile){let r="",i="";try{let b=f.readFileSync(l.resolve(e.repoDir,e.dockerfile),"utf8");if(!e.remoteUser){let h=b.match(/^\s*USER\s+(\S+)/gmi);h&&(r=h[h.length-1].match(/USER\s+(\S+)/i)[1])}if(!e.startupScript){let h=b.match(/^\s*CMD\s+(.+)$/mi),C=b.match(/^\s*ENTRYPOINT\s+(.+)$/mi),U=h?.[1]||C?.[1]||"";if(U)try{let k=JSON.parse(U);Array.isArray(k)&&(i=k.join(" "))}catch{i=U.trim()}}}catch{}n=e.remoteUser||r||"user",i&&!e.startupScript&&(e.startupScript=i);let m=n==="root"?"/root":`/home/${n}`;if(c=e.workspace||`${m}/${l.basename(e.repoDir)}`,a=e.ports||"",console.log(`==> Building E2B template (Dockerfile mode): ${e.tag}`),console.log(` Dockerfile: ${e.dockerfile}`),console.log(` Workspace: ${c}`),console.log(` Remote user: ${n}`),e.startupScript&&console.log(` Startup script: ${e.startupScript} ${i?"(from Dockerfile CMD)":""}`),a&&console.log(` Ports: ${a}`),console.log(""),v){console.log("==> Building template via server...");let b=f.readFileSync(l.resolve(e.repoDir,e.dockerfile),"utf8");d=await x({repoDir:e.repoDir,serverUrl:e.serverUrl,apiKey:e.apiKey,config:{mode:"dockerfile",tag:e.tag,remoteUser:n,workspace:c,dockerfileContent:b,script:e.script||void 0,startupScript:e.startupScript,ports:a,sshPublicKey:u,cpuCount:e.cpuCount||void 0,memoryMB:e.memoryMB||void 0,repoBasename:l.basename(e.repoDir),ignorePatterns:e.ignorePatterns}})}else console.log("==> Building template from Dockerfile..."),d=await N({repoDir:e.repoDir,workspace:c,tag:e.tag,remoteUser:n,dockerfile:e.dockerfile,script:e.script||void 0,startupScript:e.startupScript,ports:a,sshPublicKey:u,cpuCount:e.cpuCount||void 0,memoryMB:e.memoryMB||void 0,ignorePatterns:e.ignorePatterns})}else if(e.script||e.image){n=e.remoteUser||"user";let r=n==="root"?"/root":`/home/${n}`;c=e.workspace||`${r}/${l.basename(e.repoDir)}`,a=e.ports||"",console.log(`==> Building E2B template (${e.image?"image":"raw"} mode): ${e.tag}`),e.image&&console.log(` Image: ${e.image}`),console.log(` Workspace: ${c}`),console.log(` Remote user: ${n}`),e.script&&console.log(` Script: ${e.script}`),e.startupScript&&console.log(` Startup script: ${e.startupScript}`),a&&console.log(` Ports: ${a}`),console.log(""),v?(console.log("==> Building template via server..."),d=await x({repoDir:e.repoDir,serverUrl:e.serverUrl,apiKey:e.apiKey,config:{mode:"raw",tag:e.tag,remoteUser:n,workspace:c,image:e.image||void 0,script:e.script,startupScript:e.startupScript,ports:a,sshPublicKey:u,cpuCount:e.cpuCount||void 0,memoryMB:e.memoryMB||void 0,repoBasename:l.basename(e.repoDir),ignorePatterns:e.ignorePatterns}})):(console.log("==> Building template..."),d=await M({repoDir:e.repoDir,workspace:c,tag:e.tag,remoteUser:n,image:e.image||void 0,script:e.script,startupScript:e.startupScript,ports:a,sshPublicKey:u,cpuCount:e.cpuCount||void 0,memoryMB:e.memoryMB||void 0,noRepo:e.noRepo,ignorePatterns:e.ignorePatterns}))}else if(e.devcontainer){let r=j(e.repoDir);r.jsonPath?console.log(`==> Found devcontainer config: ${r.jsonPath}`):(console.error("Error: --devcontainer specified but no .devcontainer config found in "+e.repoDir),process.exit(1)),n=e.remoteUser||r.remoteUser,c=e.workspace||r.workspaceFolder,a=e.ports||r.forwardPorts.join(","),console.log(`==> Building E2B template: ${e.tag}`),console.log(` Workspace: ${c}`),console.log(` Remote user: ${n}`),console.log(` Service: ${r.service}`),a&&console.log(` Ports: ${a}`),r.postCreateCommand&&console.log(` postCreateCommand: ${r.postCreateCommand}`),r.postStartCommand&&console.log(` postStartCommand: ${r.postStartCommand}`),console.log(""),v?(console.log("==> Building template via server..."),d=await x({repoDir:e.repoDir,serverUrl:e.serverUrl,apiKey:e.apiKey,config:{mode:"devcontainer",tag:e.tag,remoteUser:n,workspace:c,postCreateCommand:r.postCreateCommand,postStartCommand:r.postStartCommand,composeService:r.service,sshPublicKey:u,cpuCount:e.cpuCount||void 0,memoryMB:e.memoryMB||void 0,repoBasename:l.basename(e.repoDir),ignorePatterns:e.ignorePatterns}})):(console.log("==> Building template..."),d=await L({repoDir:e.repoDir,workspace:c,tag:e.tag,remoteUser:n,postCreateCommand:r.postCreateCommand,postStartCommand:r.postStartCommand,composeService:r.service,sshPublicKey:u,cpuCount:e.cpuCount||void 0,memoryMB:e.memoryMB||void 0,ignorePatterns:e.ignorePatterns}))}else{n=e.remoteUser||"user";let r=n==="root"?"/root":`/home/${n}`;c=e.workspace||`${r}/${l.basename(e.repoDir)}`,a=e.ports||"",console.log(`==> Building E2B template: ${e.tag}`),console.log(` Workspace: ${c}`),console.log(` Remote user: ${n}`),a&&console.log(` Ports: ${a}`),console.log(""),v?(console.log("==> Building template via server..."),d=await x({repoDir:e.repoDir,serverUrl:e.serverUrl,apiKey:e.apiKey,config:{mode:"raw",tag:e.tag,remoteUser:n,workspace:c,sshPublicKey:u,cpuCount:e.cpuCount||void 0,memoryMB:e.memoryMB||void 0,repoBasename:l.basename(e.repoDir),ignorePatterns:e.ignorePatterns}})):(console.log("==> Building template..."),d=await M({repoDir:e.repoDir,workspace:c,tag:e.tag,remoteUser:n,ports:a,sshPublicKey:u,cpuCount:e.cpuCount||void 0,memoryMB:e.memoryMB||void 0,ignorePatterns:e.ignorePatterns}))}if(console.log(""),console.log(`==> Snapshot ID: ${d.snapshotId}`),e.apiKey){console.log("==> Registering image with server...");let r=a?a.split(",").map(Number).filter(i=>i>0):[];try{let i=await fetch(`${e.serverUrl}/api/images`,{method:"POST",headers:{Authorization:`Bearer ${e.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({tag:e.tag,image:d.snapshotId,type:e.dockerfile?"e2b-dockerfile":e.script?"e2b-raw":"e2b-devcontainer",build_method:"template",ports:r,cmd:[],remote_user:n,workspace_folder:c,built_at:new Date().toISOString()})});i.ok?console.log("==> Registered successfully"):console.log(`WARNING: Failed to register image with server (HTTP ${i.status})`)}catch(i){console.log(`WARNING: Failed to register image with server: ${i.message}`)}}console.log(""),console.log("==> Done!"),console.log(` Template tag: ${e.tag}`),console.log(` Snapshot ID: ${d.snapshotId}`),console.log(` Remote user: ${n}`),console.log(` Workspace: ${c}`),a&&console.log(` Ports: ${a}`)}ae().catch(async o=>{o.statusCode===401&&process.stdin.isTTY&&(console.error("Authentication failed. Your API key may be invalid or expired."),await J(process.env.BUILD_SERVER_URL||"https://www.typhons.dev")&&console.log("API key updated. Please re-run your build command."),process.exit(1)),console.error("Error:",o.message),process.exit(1)});
|