@evolvingmachines/daytona 0.0.51 → 0.0.52-project-sable.20260726.6e95f36
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.cjs +2 -2
- package/dist/index.d.cts +311 -9
- package/dist/index.d.ts +311 -9
- package/dist/index.js +2 -2
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var sdk=require('@daytonaio/sdk');var h={"evolve-all":"evolvingmachines/evolve-all"},S=new Set([".xlsx",".xls",".docx",".doc",".pptx",".ppt",".pdf",".zip",".tar",".gz",".7z",".rar",".png",".jpg",".jpeg",".gif",".webp",".ico",".bmp",".mp3",".wav",".ogg",".flac",".aac",".mp4",".avi",".mov",".mkv",".webm",".woff",".woff2",".ttf",".otf",".eot",".exe",".dll",".so",".dylib",".sqlite",".db",".pickle",".pkl",".parquet"]);function w(r){let e=r.substring(r.lastIndexOf(".")).toLowerCase();return S.has(e)}function v(r){let e=r.lastIndexOf("/");return e>0?r.substring(0,e):"/"}function P(r){let e=r.lastIndexOf("/");return e>=0?r.substring(e+1):r}function g(r,e,t){let s=r;return e&&(s=`cd '${e.replace(/'/g,"'\\''")}' && ${s}`),t&&Object.keys(t).length>0&&(s=`${Object.entries(t).filter(([,a])=>a!=null).map(([a,o])=>`export ${a}='${o.replace(/'/g,"'\\''")}'`).join("; ")}; ${s}`),s}var C=g,f=class{constructor(e){this.sandbox=e;}async run(e,t){let s=t?.timeoutMs?Math.floor(t.timeoutMs/1e3):void 0,n=`run-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;await this.sandbox.process.createSession(n);try{let a=await this.sandbox.process.executeSessionCommand(n,{command:g(e,t?.cwd,t?.envs),runAsync:!1},s),o=a.cmdId;o&&(t?.onStdout||t?.onStderr)&&await this.sandbox.process.getSessionCommandLogs(n,o,t.onStdout||(()=>{}),t.onStderr||(()=>{}));let d=a.stdout??a.output??"",i=a.stderr??"";if(!d&&o&&!t?.onStdout)try{let m=await this.sandbox.process.getSessionCommandLogs(n,o);d=m.stdout??m.output??"",i=m.stderr??i;}catch{}return {exitCode:a.exitCode??0,stdout:d,stderr:i}}finally{try{await this.sandbox.process.deleteSession(n);}catch{}}}async spawn(e,t){let s=`evolve-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;await this.sandbox.process.createSession(s);let n=t?.timeoutMs?Math.floor(t.timeoutMs/1e3):void 0,a=Date.now(),o=t?.timeoutMs,i=(await this.sandbox.process.executeSessionCommand(s,{command:g(e,t?.cwd,t?.envs),runAsync:true},n)).cmdId;i&&(t?.onStdout||t?.onStderr)&&this.sandbox.process.getSessionCommandLogs(s,i,t.onStdout||(()=>{}),t.onStderr||(()=>{})).catch(()=>{});let m=this.sandbox;return {processId:s,wait:async()=>{if(!i)return {exitCode:0,stdout:"",stderr:""};for(;;){if(o&&Date.now()-a>=o){try{await m.process.deleteSession(s);}catch{}return {exitCode:-1,stdout:"",stderr:"operation timed out"}}try{let c=await m.process.getSessionCommand(s,i);if(c.exitCode!==void 0)try{let l=await m.process.getSessionCommandLogs(s,i);return {exitCode:c.exitCode,stdout:l.stdout??l.output??"",stderr:l.stderr??""}}catch{return {exitCode:c.exitCode,stdout:"",stderr:""}}}catch(c){if((c instanceof Error?c.message.toLowerCase():String(c).toLowerCase()).includes("not found"))return {exitCode:-1,stdout:"",stderr:"session terminated"};throw c}await new Promise(c=>setTimeout(c,500));}},kill:async()=>{try{return await m.process.deleteSession(s),!0}catch{return false}}}}async list(){try{return (await this.sandbox.process.listSessions()).map(t=>({processId:t.sessionId||"",cmd:"",args:[],envs:{}}))}catch{return []}}async kill(e){try{return await this.sandbox.process.deleteSession(e),!0}catch{return false}}},p=class{constructor(e){this.sandbox=e;}async read(e){let t=await this.sandbox.fs.downloadFile(e);return w(e)?new Uint8Array(t):t.toString("utf-8")}async write(e,t){let s;if(typeof t=="string")s=Buffer.from(t,"utf-8");else if(Buffer.isBuffer(t))s=t;else if(t instanceof ArrayBuffer)s=Buffer.from(t);else if(t instanceof Uint8Array)s=Buffer.from(t);else throw new Error(`Unsupported content type: ${typeof t}`);await this.sandbox.fs.uploadFile(s,e);}async writeBatch(e){let t=e.map(s=>{let n;if(typeof s.data=="string")n=Buffer.from(s.data,"utf-8");else if(Buffer.isBuffer(s.data))n=s.data;else if(s.data instanceof ArrayBuffer)n=Buffer.from(s.data);else if(s.data instanceof Uint8Array)n=Buffer.from(s.data);else throw new Error(`Unsupported content type: ${typeof s.data}`);return {source:n,destination:s.path}});await this.sandbox.fs.uploadFiles(t);}async makeDir(e){await this.sandbox.fs.createFolder(e,"755");}async exists(e){try{let t=v(e),s=P(e);return (await this.sandbox.fs.listFiles(t)).some(a=>a.name===s)}catch{return false}}async list(e){return (await this.sandbox.fs.listFiles(e)).map(s=>({name:s.name,path:e.endsWith("/")?`${e}${s.name}`:`${e}/${s.name}`,type:s.isDir?"dir":"file"}))}async remove(e){await this.sandbox.fs.deleteFile(e,true);}async rename(e,t){await this.sandbox.fs.moveFiles(e,t);}},u=class{constructor(e){this.sandbox=e;this.commands=new f(e),this.files=new p(e);}commands;files;get sandboxId(){return this.sandbox.id}async getHost(e){return (await this.sandbox.getPreviewLink(e)).url}async isRunning(){return this.sandbox.state==="started"}async getInfo(){return {sandboxId:this.sandbox.id,image:this.sandbox.snapshot||"unknown",name:this.sandbox.name,metadata:this.sandbox.labels||{},startedAt:new Date().toISOString()}}async kill(){await this.sandbox.delete();}async pause(){await this.sandbox.stop();}},x=class{providerType="daytona";name="Daytona";client;defaultTimeoutMs;snapshotName;constructor(e){this.client=new sdk.Daytona({apiKey:e.apiKey,apiUrl:e.apiUrl,target:e.target}),this.defaultTimeoutMs=e.defaultTimeoutMs??36e5,this.snapshotName=e.snapshotName??"evolve-all";}async create(e){let t=e.timeoutMs??this.defaultTimeoutMs,s=Math.max(1,Math.ceil(t/6e4)),n=e.image||this.snapshotName,a;try{let o=await this.client.snapshot.get(n);if(o&&o.state==="active")console.log(`[daytona] Using cached snapshot: ${n}`),a=await this.client.create({snapshot:n,envVars:e.envs,labels:e.metadata,autoStopInterval:s},{timeout:600});else throw new Error("Snapshot not active")}catch{let o=h[n]??n;console.log(`[daytona] Snapshot "${n}" not found, building from image: ${o}`),console.log("[daytona] First run will take a few minutes (this only happens once)...");try{await this.client.snapshot.create({name:n,image:sdk.Image.base(o),resources:{cpu:e.resources?.cpu??4,memory:e.resources?.memory??4,disk:e.resources?.disk??10}},{onLogs:d=>console.log(`[daytona] ${d}`)}),console.log(`[daytona] Snapshot "${n}" ready.`),a=await this.client.create({snapshot:n,envVars:e.envs,labels:e.metadata,autoStopInterval:s},{timeout:600});}catch(d){console.warn(`[daytona] Snapshot creation failed, falling back to direct image: ${d instanceof Error?d.message:d}`),a=await this.client.create({image:o,envVars:e.envs,labels:e.metadata,autoStopInterval:s,resources:{cpu:e.resources?.cpu??4,memory:e.resources?.memory??4,disk:e.resources?.disk??10}},{timeout:600,onSnapshotCreateLogs:i=>console.log(`[daytona] ${i}`)});}}return e.workingDirectory&&await a.fs.createFolder(e.workingDirectory,"755"),new u(a)}async connect(e,t){let s=await this.client.get(e);return s.state!=="started"&&await s.start(),new u(s)}async list(e){let t=e?.limit??100;return (await this.client.list(e?.metadata,1,t)).items.map(n=>({sandboxId:n.id,image:n.snapshot||"unknown",name:n.name,metadata:n.labels||{},startedAt:new Date().toISOString()}))}};function A(r={}){let e=r.apiKey??process.env.DAYTONA_API_KEY;if(!e)throw new Error("Daytona API key required. Set DAYTONA_API_KEY environment variable or pass apiKey in config. Get your key at https://app.daytona.io/dashboard/keys");return new x({...r,apiKey:e})}
|
|
2
|
-
exports.DaytonaProvider=
|
|
1
|
+
'use strict';var sdk=require('@daytonaio/sdk'),promises=require('dns/promises');var F={"evolve-all":"evolvingmachines/evolve-all"},K=10,A=10,H=new Set([".xlsx",".xls",".docx",".doc",".pptx",".ppt",".pdf",".zip",".tar",".gz",".7z",".rar",".png",".jpg",".jpeg",".gif",".webp",".ico",".bmp",".mp3",".wav",".ogg",".flac",".aac",".mp4",".avi",".mov",".mkv",".webm",".woff",".woff2",".ttf",".otf",".eot",".exe",".dll",".so",".dylib",".sqlite",".db",".pickle",".pkl",".parquet"]);function j(s){let e=s.substring(s.lastIndexOf(".")).toLowerCase();return H.has(e)}function z(s){let e=s.lastIndexOf("/");return e>0?s.substring(0,e):"/"}function Y(s){let e=s.lastIndexOf("/");return e>=0?s.substring(e+1):s}var f=class extends Error{reason;destination;constructor(e,t,n){super(t),this.name="DaytonaNetworkPolicyError",this.reason=e,this.destination=n;}},b=class extends Error{image;constructor(e,t){let n=t instanceof Error?t.message:String(t??"unknown error");super(`Failed to pull image "${e}" on Daytona: ${n}. Private registry images (e.g. AWS ECR) require registry credentials pre-registered in the Daytona dashboard (Registries page) before sandbox creation \u2014 Daytona has no per-call image pull secret. Register the registry at https://app.daytona.io, then retry. Also ensure the image is linux/amd64 and pinned to a tag or digest (floating tags like "latest" are rejected by Daytona's snapshot builder).`),this.name="DaytonaImagePullError",this.image=e;}},h=class extends Error{snapshot;constructor(e){super(`Daytona snapshot "${e}" already exists and pins its cpu/memory/disk sizing \u2014 create-from-snapshot cannot resize it, so the requested \`resources\` cannot be enforced. Pre-build a snapshot with the desired sizing under its own name (sizing-addressed), or omit \`resources\` to accept the snapshot's pinned sizing.`),this.name="DaytonaResourcesError",this.snapshot=e;}};function q(s,e){if(!e||e<=0)return s;let t=Buffer.from(s).toString("base64"),n=`/tmp/.evolve-cmd-${Date.now()}-${Math.random().toString(36).slice(2,8)}.sh`;return `sh -c '${`echo ${t} | base64 -d > ${n}; if command -v timeout >/dev/null 2>&1; then timeout -k 10 ${e} bash ${n}; else bash ${n}; fi; rc=$?; rm -f ${n}; exit $rc`}'`}function x(s,e,t,n){let r=s;return e&&(r=`cd '${e.replace(/'/g,"'\\''")}' && ${r}`),t&&Object.keys(t).length>0&&(r=`${Object.entries(t).filter(([,o])=>o!=null).map(([o,a])=>`export ${o}='${String(a).replace(/'/g,"'\\''")}'`).join("; ")}; ${r}`),n==="root"&&(r=`echo ${Buffer.from(r).toString("base64")} | base64 -d | sudo -n bash`),r}var W=s=>promises.resolve4(s);function U(s){let e=/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(?:\/(\d{1,3}))?$/.exec(s);return !(!e||[e[1],e[2],e[3],e[4]].some(t=>Number(t)>255)||e[5]!==void 0&&Number(e[5])>32)}function X(s){return /^\d{1,3}(\.\d{1,3}){3}(\/\d+)?$/.test(s)&&!U(s)}function G(s){return s.startsWith("[")?true:(s.match(/:/g)?.length??0)>=2}function V(s){return /^[^:]+:\d+$/.test(s)}async function M(s,e=W){if(!s||s.outbound==="open"){if(s?.allowedDestinations?.length)throw new Error("network.allowedDestinations is only valid when outbound is blocked");return s?{networkBlockAll:false}:{}}let t=s.allowedDestinations??[];if(t.length===0)return {networkBlockAll:true};let n=[],r=[];for(let o of t)if(U(o))n.push(o.includes("/")?o:`${o}/32`);else {if(X(o))throw new f("invalid-ipv4",`"${o}" is not a valid IPv4 address or CIDR (octets must be 0-255, prefix 0-32). Fix the address or list a hostname instead.`,o);if(G(o))throw new f("ipv6-unsupported",`Daytona's network allowlist supports IPv4 CIDRs only; cannot allow IPv6 destination "${o}".`,o);if(V(o))throw new f("port-unsupported",`Daytona's network allowlist filters hosts and IPs only and cannot match a port; drop the ":<port>" from "${o}" and list just the host or IP.`,o);if(o.includes("*"))throw new f("wildcard-hostname",`Daytona cannot enforce wildcard hostname "${o}": its allowlist is kernel-level IPv4 CIDR filtering with no DNS/domain layer. List concrete hostnames or IPv4 CIDRs instead.`,o);r.push(o);}for(let o of r){let a=[];try{a=await e(o);}catch(i){throw new f("unresolvable-hostname",`Cannot pin hostname "${o}" to stable IPv4 addresses at create time (${i instanceof Error?i.message:String(i)}). Daytona enforces IPv4 CIDRs only; use an IPv4 CIDR for this destination instead.`,o)}if(a.length===0)throw new f("unresolvable-hostname",`Hostname "${o}" resolved to no IPv4 addresses. Daytona enforces IPv4 CIDRs only; use an IPv4 CIDR for this destination instead.`,o);console.warn(`[daytona] Network allowlist: pinning "${o}" to [${a.join(", ")}] (DNS resolved at create time). Daytona enforces IPv4 CIDRs only \u2014 if this host rotates DNS (CDNs and cloud APIs often do), traffic to its new IPs will be BLOCKED for the sandbox's lifetime.`);for(let i of a)n.push(`${i}/32`);}let d=[...new Set(n)];if(d.length>A)throw new f("allowlist-too-large",`Daytona allows at most ${A} entries in its network allowlist; this policy resolved to ${d.length} CIDRs (${d.join(", ")}). Aggregate destinations into broader CIDRs or reduce the list.`);return {networkBlockAll:false,networkAllowList:d.join(",")}}function B(s){let e=s.indexOf("/");if(e<0)return;let t=s.substring(0,e);return t.includes(".")||t.includes(":")||t==="localhost"?t:void 0}function D(s){return {sandboxId:s.id,image:s.snapshot??"",name:s.name,metadata:s.labels??{},startedAt:s.createdAt??""}}function T(s){if(s==="started")return "running";if(s==="stopped"||s==="archived")return "paused"}var S=class extends sdk.Daytona{constructor(t,n){super(t);this.managedToolboxUrl=n;}async getProxyToolboxUrl(){return this.managedToolboxUrl}};function J(s,e){let t=new Uint8Array(new ArrayBuffer(s.length+e.length));return t.set(s,0),t.set(e,s.length),t}function $(s,e,t){e:for(let n=t;n+e.length<=s.length;n++){for(let r=0;r<e.length;r++)if(s[n+r]!==e[r])continue e;return n}return -1}function E(s,e){let t=new TextDecoder("utf-8"),n=new TextDecoder("utf-8"),r=new Uint8Array(0),d="stdout",o=a=>{a.length!==0&&(d==="stdout"?s(t.decode(a,{stream:true})):e(n.decode(a,{stream:true})));};return {push(a){for(r=J(r,a);;){let i=$(r,sdk.STDOUT_PREFIX_BYTES,0),c=$(r,sdk.STDERR_PREFIX_BYTES,0),l=-1,u="stdout",m=0;if(i!==-1&&(c===-1||i<c)?(l=i,u="stdout",m=sdk.STDOUT_PREFIX_BYTES.length):c!==-1&&(l=c,u="stderr",m=sdk.STDERR_PREFIX_BYTES.length),l===-1){let g=Math.max(0,r.length-(sdk.MAX_PREFIX_LEN-1));o(r.subarray(0,g)),r=r.slice(g);return}o(r.subarray(0,l)),d=u,r=r.slice(l+m);}},flush(){o(r),r=new Uint8Array(0);let a=t.decode();a&&s(a);let i=n.decode();i&&e(i);}}}async function L(s,e,t,n,r,d){let o=`${s.toolboxUrl.replace(/\/+$/,"")}/${encodeURIComponent(e)}/process/session/${encodeURIComponent(t)}/command/${encodeURIComponent(n)}/logs?follow=true`,a=await fetch(o,{headers:{Authorization:`Bearer ${s.apiKey}`,accept:"application/octet-stream"}});if(!a.ok)throw new Error(`Daytona managed log follow failed: ${a.status} ${await a.text()}`);if(!a.body)return;let i=E(r,d),c=a.body.getReader();try{for(;;){let{done:l,value:u}=await c.read();if(l)break;u&&i.push(u);}}finally{i.flush(),await c.cancel().catch(()=>{});}}function w(s){let e=s.stdout||"",t=s.stderr||"",n=s.output||"";return !e&&!t&&n?{stdout:n,stderr:""}:{stdout:e,stderr:t}}var v=class{constructor(e,t,n){this.sandbox=e;this.user=t;this.managedStream=n;}followLogs(e,t,n,r){return this.managedStream?L(this.managedStream,this.sandbox.id,e,t,n,r):this.sandbox.process.getSessionCommandLogs(e,t,n,r)}async run(e,t){let n=t?.timeoutMs?Math.floor(t.timeoutMs/1e3):void 0,r=`run-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;await this.sandbox.process.createSession(r);try{let d=await this.sandbox.process.executeSessionCommand(r,{command:x(e,t?.cwd,t?.envs,this.user),runAsync:!1},n),o=d.cmdId;o&&(t?.onStdout||t?.onStderr)&&await this.followLogs(r,o,t.onStdout||(()=>{}),t.onStderr||(()=>{}));let{stdout:a,stderr:i}=w(d);if(!a&&!i&&o&&!t?.onStdout)try{let c=await this.sandbox.process.getSessionCommandLogs(r,o),l=w(c);a=l.stdout,i=l.stderr||i;}catch{}return {exitCode:d.exitCode??0,stdout:a,stderr:i}}finally{try{await this.sandbox.process.deleteSession(r);}catch{}}}async spawn(e,t){let n=`evolve-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;await this.sandbox.process.createSession(n);let r=t?.timeoutMs?Math.floor(t.timeoutMs/1e3):void 0,d=Date.now(),o=t?.timeoutMs,i=(await this.sandbox.process.executeSessionCommand(n,{command:q(x(e,t?.cwd,t?.envs,this.user),r),runAsync:true},r)).cmdId;i&&(t?.onStdout||t?.onStderr)&&this.followLogs(n,i,t.onStdout||(()=>{}),t.onStderr||(()=>{})).catch(()=>{});let c=this.sandbox;return {processId:n,wait:async()=>{if(!i)return {exitCode:0,stdout:"",stderr:""};for(;;){if(o&&Date.now()-d>=o){try{await c.process.deleteSession(n);}catch{}return {exitCode:124,stdout:"",stderr:"operation timed out"}}try{let l=await c.process.getSessionCommand(n,i);if(l.exitCode!==void 0)try{let u=await c.process.getSessionCommandLogs(n,i);return {exitCode:l.exitCode,...w(u)}}catch{return {exitCode:l.exitCode,stdout:"",stderr:""}}}catch(l){let u=l instanceof Error?l.message.toLowerCase():String(l).toLowerCase();if(u.includes("not found")){let m=u.includes("sandbox");try{await c.refreshData(),m=!1;}catch(g){(g instanceof Error?g.message.toLowerCase():String(g).toLowerCase()).includes("not found")&&(m=true);}return {exitCode:-1,stdout:"",stderr:m?"sandbox deleted during run":"session terminated"}}throw l}await new Promise(l=>setTimeout(l,500));}},kill:async()=>{try{return await c.process.deleteSession(n),!0}catch{return false}}}}async list(){try{return (await this.sandbox.process.listSessions()).map(t=>({processId:t.sessionId||"",cmd:"",args:[],envs:{}}))}catch{return []}}async kill(e){try{return await this.sandbox.process.deleteSession(e),!0}catch{return false}}},P=class{constructor(e){this.sandbox=e;}async read(e){let t=await this.sandbox.fs.downloadFile(e);return j(e)?new Uint8Array(t):t.toString("utf-8")}async write(e,t){let n;if(typeof t=="string")n=Buffer.from(t,"utf-8");else if(Buffer.isBuffer(t))n=t;else if(t instanceof ArrayBuffer)n=Buffer.from(t);else if(t instanceof Uint8Array)n=Buffer.from(t);else throw new Error(`Unsupported content type: ${typeof t}`);await this.sandbox.fs.uploadFile(n,e);}async writeBatch(e){let t=e.map(n=>{let r;if(typeof n.data=="string")r=Buffer.from(n.data,"utf-8");else if(Buffer.isBuffer(n.data))r=n.data;else if(n.data instanceof ArrayBuffer)r=Buffer.from(n.data);else if(n.data instanceof Uint8Array)r=Buffer.from(n.data);else throw new Error(`Unsupported content type: ${typeof n.data}`);return {source:r,destination:n.path}});await this.sandbox.fs.uploadFiles(t);}async writeFromPath(e,t){await this.sandbox.fs.uploadFile(t,e);}async makeDir(e){await this.sandbox.fs.createFolder(e,"755");}async exists(e){try{let t=z(e),n=Y(e);return (await this.sandbox.fs.listFiles(t)).some(d=>d.name===n)}catch{return false}}async list(e){return (await this.sandbox.fs.listFiles(e)).map(n=>({name:n.name,path:e.endsWith("/")?`${e}${n.name}`:`${e}/${n.name}`,type:n.isDir?"dir":"file"}))}async remove(e){await this.sandbox.fs.deleteFile(e,true);}async rename(e,t){await this.sandbox.fs.moveFiles(e,t);}},y=class{constructor(e,t,n){this.sandbox=e;this.commands=new v(e,t,n),this.files=new P(e);}commands;files;get sandboxId(){return this.sandbox.id}async getHost(e){return (await this.sandbox.getPreviewLink(e)).url}async isRunning(){try{await this.sandbox.refreshData();}catch{return false}return this.sandbox.state==="started"}async getInfo(){return await this.sandbox.refreshData(),D(this.sandbox)}async kill(){await this.sandbox.delete();}async pause(){await this.sandbox.stop();}},I=class{providerType="daytona";name="Daytona";client;defaultTimeoutMs;snapshotName;sandboxUsers=new Map;managedStream;constructor(e){let t={apiKey:e.apiKey,apiUrl:e.apiUrl,target:e.target};this.client=e.managedToolboxUrl?new S(t,e.managedToolboxUrl):new sdk.Daytona(t),e.managedToolboxUrl&&(this.managedStream={toolboxUrl:e.managedToolboxUrl,apiKey:e.apiKey}),this.defaultTimeoutMs=e.defaultTimeoutMs??36e5,this.snapshotName=e.snapshotName??"evolve-all";}async create(e){let t=await M(e.network),n=e.user,r=n&&n!=="root"?n:void 0,d=e.timeoutMs??this.defaultTimeoutMs,o=Math.max(1,Math.ceil(d/6e4)),a=e.image||this.snapshotName,i={envVars:e.envs,labels:e.metadata,autoStopInterval:o,autoDeleteInterval:K,...r?{user:r}:{},...t},c,l=e.resources!==void 0&&(e.resources.cpu!==void 0||e.resources.memory!==void 0||e.resources.disk!==void 0);if(this.managedStream){if(l)throw new h(a);let u=await this.client.create({snapshot:a,...i},{timeout:600});return e.workingDirectory&&await u.fs.createFolder(e.workingDirectory,"755"),n&&this.sandboxUsers.set(u.id,n),new y(u,n,this.managedStream)}try{let u=await this.client.snapshot.get(a);if(u&&u.state==="active"){if(l)throw new h(a);console.log(`[daytona] Using cached snapshot: ${a}`),c=await this.client.create({snapshot:a,...i},{timeout:600});}else throw new Error("Snapshot not active")}catch(u){if(u instanceof h)throw u;let m=F[a]??a;console.log(`[daytona] Snapshot "${a}" not found, building from image: ${m}`),console.log("[daytona] First run will take a few minutes (this only happens once)...");try{await this.client.snapshot.create({name:a,image:sdk.Image.base(m),resources:{cpu:e.resources?.cpu??4,memory:e.resources?.memory??4,disk:e.resources?.disk??10}},{onLogs:g=>console.log(`[daytona] ${g}`)}),console.log(`[daytona] Snapshot "${a}" ready.`),c=await this.client.create({snapshot:a,...i},{timeout:600});}catch(g){console.warn(`[daytona] Snapshot creation failed, falling back to direct image: ${g instanceof Error?g.message:g}`);try{c=await this.client.create({image:m,...i,resources:{cpu:e.resources?.cpu??4,memory:e.resources?.memory??4,disk:e.resources?.disk??10}},{timeout:600,onSnapshotCreateLogs:p=>console.log(`[daytona] ${p}`)});}catch(p){throw B(m)?new b(m,p):p}}}return e.workingDirectory&&await c.fs.createFolder(e.workingDirectory,"755"),n&&this.sandboxUsers.set(c.id,n),new y(c,n,this.managedStream)}async connect(e,t){let n=await this.client.get(e);return n.state!=="started"&&await n.start(),new y(n,this.sandboxUsers.get(e),this.managedStream)}async list(e){let t=e?.limit??100,r=(await this.client.list(e?.metadata,1,t)).items;return e?.state&&(r=r.filter(d=>{let o=T(d.state);return o!==void 0&&e.state.includes(o)})),r.map(D)}};function ee(s={}){let e=s.apiKey??process.env.DAYTONA_API_KEY;if(!e)throw new Error("Daytona API key required. Set DAYTONA_API_KEY environment variable or pass apiKey in config. Get your key at https://app.daytona.io/dashboard/keys");return new I({...s,apiKey:e})}var te=x,ne=M,se=B,re=D,oe=T,ae=E,ie=L,de=w;
|
|
2
|
+
exports.DAYTONA_AUTO_DELETE_GRACE_MINUTES=K;exports.DAYTONA_MAX_NETWORK_ALLOWLIST=A;exports.DaytonaCommands=v;exports.DaytonaFiles=P;exports.DaytonaImagePullError=b;exports.DaytonaNetworkPolicyError=f;exports.DaytonaProvider=I;exports.DaytonaResourcesError=h;exports._testCreateLogDemuxer=ae;exports._testDaytonaStateToEvolveState=oe;exports._testFollowManagedSessionLogs=ie;exports._testImageRegistryHost=se;exports._testMapNetworkPolicy=ne;exports._testReadCommandStreams=de;exports._testToSandboxInfo=re;exports._testWrapCommand=te;exports.createDaytonaProvider=ee;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Sandbox } from '@daytonaio/sdk';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Daytona Sandbox Provider
|
|
3
5
|
*
|
|
@@ -7,17 +9,140 @@
|
|
|
7
9
|
* - Mirror E2B provider interface for SDK compatibility
|
|
8
10
|
* - Uses public Docker images via IMAGE_MAP
|
|
9
11
|
* - Parallel structure to E2B provider
|
|
12
|
+
*
|
|
13
|
+
* Daytona-specific notes:
|
|
14
|
+
* - Network policy maps to Daytona's networkBlockAll / networkAllowList,
|
|
15
|
+
* enforced by kernel iptables on the runner. The allowlist is IPv4 CIDR
|
|
16
|
+
* ONLY (max 10 entries, DAYTONA_MAX_NETWORK_ALLOWLIST). Hostname
|
|
17
|
+
* destinations are pinned to their IPv4 addresses by a DNS lookup at
|
|
18
|
+
* create time — see the DNS-ROTATION CAVEAT on SandboxCreateOptions.network.
|
|
19
|
+
* Destinations Daytona can never enforce (wildcards, IPv6, unresolvable
|
|
20
|
+
* hostnames, >10 CIDRs) throw DaytonaNetworkPolicyError instead of
|
|
21
|
+
* silently weakening the policy.
|
|
22
|
+
* - The `user` option is a CREATE-TIME OS user (Daytona's osUser field);
|
|
23
|
+
* there is no per-exec user switch — the Daytona daemon runs commands as
|
|
24
|
+
* the container's user (governed by the image's USER directive; default
|
|
25
|
+
* images use "daytona" with passwordless sudo). `user: "root"` keeps the
|
|
26
|
+
* image's default user and elevates every command through a `sudo -n`
|
|
27
|
+
* wrapper instead (mirrors the Modal provider's su wrapper). File
|
|
28
|
+
* operations always go through the Daytona daemon and are NOT elevated.
|
|
29
|
+
* - Private registry images (AWS ECR, GHCR, GCP Artifact Registry, ...)
|
|
30
|
+
* require registry credentials pre-registered in the Daytona dashboard
|
|
31
|
+
* (Registries page) — Daytona has no per-call image pull secret. Pull
|
|
32
|
+
* failures for such images throw DaytonaImagePullError.
|
|
33
|
+
* - getInfo()/list() report the API's real createdAt timestamp (never a
|
|
34
|
+
* fabricated client-side date); Daytona exposes no end timestamp, so
|
|
35
|
+
* endAt is always undefined.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Daytona's hard cap on network allowlist size (validated server-side too).
|
|
40
|
+
* Policies that resolve to more CIDRs throw DaytonaNetworkPolicyError.
|
|
10
41
|
*/
|
|
11
42
|
/**
|
|
12
|
-
*
|
|
43
|
+
* Minutes a STOPPED sandbox is kept before Daytona deletes it. Long enough that
|
|
44
|
+
* a stop nobody ordered leaves something to look at and something to collect
|
|
45
|
+
* from; short enough that it is never a way to hold billable state.
|
|
46
|
+
*/
|
|
47
|
+
declare const DAYTONA_AUTO_DELETE_GRACE_MINUTES = 10;
|
|
48
|
+
declare const DAYTONA_MAX_NETWORK_ALLOWLIST = 10;
|
|
49
|
+
/** Why a network policy cannot be enforced by Daytona. */
|
|
50
|
+
type DaytonaNetworkPolicyReason = "wildcard-hostname" | "ipv6-unsupported" | "port-unsupported" | "invalid-ipv4" | "unresolvable-hostname" | "allowlist-too-large";
|
|
51
|
+
/**
|
|
52
|
+
* Typed error for network policies Daytona cannot enforce.
|
|
13
53
|
*
|
|
14
|
-
* Daytona's
|
|
15
|
-
*
|
|
16
|
-
*
|
|
54
|
+
* Daytona's allowlist is kernel-level IPv4 CIDR filtering only (max 10
|
|
55
|
+
* entries) — no DNS/domain layer. Anything that cannot be pinned to stable
|
|
56
|
+
* IPv4 CIDRs at create time is rejected loudly instead of silently
|
|
57
|
+
* weakening the sandbox's egress policy.
|
|
17
58
|
*/
|
|
18
|
-
declare
|
|
19
|
-
|
|
20
|
-
|
|
59
|
+
declare class DaytonaNetworkPolicyError extends Error {
|
|
60
|
+
readonly reason: DaytonaNetworkPolicyReason;
|
|
61
|
+
/** The offending destination (absent for list-level violations). */
|
|
62
|
+
readonly destination?: string;
|
|
63
|
+
constructor(reason: DaytonaNetworkPolicyReason, message: string, destination?: string);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Typed error for image pull failures on private registries.
|
|
67
|
+
*
|
|
68
|
+
* Daytona has no per-call image pull secret: credentials for private
|
|
69
|
+
* registries (AWS ECR, GHCR, GCP Artifact Registry, private Docker Hub)
|
|
70
|
+
* must be pre-registered in the Daytona dashboard BEFORE creating the
|
|
71
|
+
* sandbox (dashboard → Registries → Add Registry).
|
|
72
|
+
*/
|
|
73
|
+
declare class DaytonaImagePullError extends Error {
|
|
74
|
+
/** The image reference that failed to pull. */
|
|
75
|
+
readonly image: string;
|
|
76
|
+
constructor(image: string, cause?: unknown);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Typed error for create-time sizing requests an EXISTING snapshot cannot
|
|
80
|
+
* honor. Daytona pins cpu/memory/disk on the SNAPSHOT at build time;
|
|
81
|
+
* create-from-snapshot has no resources parameter, so a `resources` request
|
|
82
|
+
* against a cached snapshot would be silently ignored. Per the provider law
|
|
83
|
+
* (reject what you cannot enforce) it is refused loudly instead: pre-build a
|
|
84
|
+
* snapshot with the desired sizing under its own name, or drop `resources`.
|
|
85
|
+
*/
|
|
86
|
+
declare class DaytonaResourcesError extends Error {
|
|
87
|
+
/** The existing snapshot whose pinned sizing cannot be overridden. */
|
|
88
|
+
readonly snapshot: string;
|
|
89
|
+
constructor(snapshot: string);
|
|
90
|
+
}
|
|
91
|
+
declare function wrapCommand(command: string, cwd?: string, envs?: Record<string, string>, user?: string): string;
|
|
92
|
+
/** Daytona create() params derived from Evolve's provider-neutral network policy. */
|
|
93
|
+
interface DaytonaNetworkCreateParams {
|
|
94
|
+
networkBlockAll?: boolean;
|
|
95
|
+
networkAllowList?: string;
|
|
96
|
+
}
|
|
97
|
+
/** Resolves a hostname to its IPv4 addresses (injectable for tests). */
|
|
98
|
+
type HostnameResolver = (hostname: string) => Promise<string[]>;
|
|
99
|
+
/**
|
|
100
|
+
* Map Evolve's provider-neutral network policy onto Daytona create() params.
|
|
101
|
+
*
|
|
102
|
+
* - outbound "open" (or no policy) → no restrictions
|
|
103
|
+
* - outbound "blocked", no allowlist → networkBlockAll: true (kernel DROP of all egress)
|
|
104
|
+
* - outbound "blocked" with allowlist → networkAllowList (comma-separated IPv4
|
|
105
|
+
* CIDRs; bare IPv4 gets /32). Daytona enforces the allowlist with kernel
|
|
106
|
+
* iptables and supports IPv4 CIDRs ONLY, max 10 entries.
|
|
107
|
+
*
|
|
108
|
+
* DNS-ROTATION CAVEAT (load-bearing): hostname destinations are resolved to
|
|
109
|
+
* their IPv4 A records ONCE, at create time, and pinned as /32 CIDRs. If the
|
|
110
|
+
* host rotates DNS afterwards (CDNs and cloud APIs often do), traffic to the
|
|
111
|
+
* new IPs is BLOCKED for the sandbox's lifetime. Prefer stable IPs/CIDRs for
|
|
112
|
+
* anything long-running.
|
|
113
|
+
*
|
|
114
|
+
* Anything that cannot be pinned to stable IPv4 CIDRs throws
|
|
115
|
+
* DaytonaNetworkPolicyError (wildcard hostnames, IPv6, unresolvable
|
|
116
|
+
* hostnames, >10 resolved CIDRs) — never silently weakened.
|
|
117
|
+
*/
|
|
118
|
+
declare function mapNetworkPolicy(network?: SandboxCreateOptions["network"], resolveHostname?: HostnameResolver): Promise<DaytonaNetworkCreateParams>;
|
|
119
|
+
/**
|
|
120
|
+
* Registry host of an image reference, or undefined for Docker Hub images.
|
|
121
|
+
* A reference carries a registry host only when its first path segment
|
|
122
|
+
* contains "." or ":" or is "localhost" (Docker's own heuristic).
|
|
123
|
+
*/
|
|
124
|
+
declare function imageRegistryHost(image: string): string | undefined;
|
|
125
|
+
/** Fields we read off a Daytona SDK sandbox to build a SandboxInfo. */
|
|
126
|
+
interface DaytonaSandboxLike {
|
|
127
|
+
id: string;
|
|
128
|
+
name?: string;
|
|
129
|
+
snapshot?: string;
|
|
130
|
+
labels?: Record<string, string>;
|
|
131
|
+
createdAt?: string;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Build a SandboxInfo from a Daytona sandbox entity. Timestamps come from
|
|
135
|
+
* the API's createdAt (never fabricated client-side; empty string when the
|
|
136
|
+
* API omits it). Daytona exposes no end timestamp, so endAt is always
|
|
137
|
+
* undefined.
|
|
138
|
+
*/
|
|
139
|
+
declare function toSandboxInfo(sandbox: DaytonaSandboxLike): SandboxInfo;
|
|
140
|
+
/**
|
|
141
|
+
* Map a Daytona sandbox state onto Evolve's list() filter states.
|
|
142
|
+
* "started" → running; "stopped"/"archived" → paused (our pause() stops the
|
|
143
|
+
* sandbox); transitional and terminal states match no filter.
|
|
144
|
+
*/
|
|
145
|
+
declare function daytonaStateToEvolveState(state?: string): "running" | "paused" | undefined;
|
|
21
146
|
/** Result of a completed sandbox command */
|
|
22
147
|
interface SandboxCommandResult {
|
|
23
148
|
exitCode: number;
|
|
@@ -46,6 +171,7 @@ interface SandboxInfo {
|
|
|
46
171
|
name?: string;
|
|
47
172
|
metadata: Record<string, string>;
|
|
48
173
|
startedAt: string;
|
|
174
|
+
/** End time (undefined for running sandboxes; Daytona never exposes one) */
|
|
49
175
|
endAt?: string;
|
|
50
176
|
}
|
|
51
177
|
/** File or directory entry info */
|
|
@@ -82,11 +208,47 @@ interface SandboxCreateOptions {
|
|
|
82
208
|
metadata?: Record<string, string>;
|
|
83
209
|
timeoutMs?: number;
|
|
84
210
|
workingDirectory?: string;
|
|
85
|
-
/**
|
|
211
|
+
/**
|
|
212
|
+
* Resource allocation (cpu cores, memory GiB, disk GiB), applied when a
|
|
213
|
+
* SNAPSHOT IS BUILT for the image (Daytona pins sizing on the snapshot).
|
|
214
|
+
* When the named snapshot ALREADY exists it cannot be resized at create —
|
|
215
|
+
* declaring resources then throws DaytonaResourcesError rather than
|
|
216
|
+
* silently ignoring them (pre-build a sizing-addressed snapshot instead).
|
|
217
|
+
*/
|
|
86
218
|
resources?: SandboxResources;
|
|
219
|
+
/**
|
|
220
|
+
* Provider-neutral outbound network policy, enforced by kernel iptables on
|
|
221
|
+
* the Daytona runner. "blocked" with no allowedDestinations drops all
|
|
222
|
+
* egress. With allowedDestinations, Daytona supports IPv4 CIDRs ONLY (max
|
|
223
|
+
* 10): IPs/CIDRs pass through; hostnames are DNS-resolved ONCE at create
|
|
224
|
+
* time and pinned as /32 CIDRs — if the host rotates DNS later (CDNs and
|
|
225
|
+
* cloud APIs often do), its new IPs are BLOCKED for the sandbox's
|
|
226
|
+
* lifetime. Wildcards, IPv6, and unresolvable hostnames throw
|
|
227
|
+
* DaytonaNetworkPolicyError rather than silently weakening the policy.
|
|
228
|
+
* Note: on Daytona orgs below Tier 3, org network policy overrides
|
|
229
|
+
* per-sandbox settings server-side.
|
|
230
|
+
*/
|
|
231
|
+
network?: {
|
|
232
|
+
outbound: "open" | "blocked";
|
|
233
|
+
allowedDestinations?: string[];
|
|
234
|
+
};
|
|
235
|
+
/**
|
|
236
|
+
* OS user for the sandbox, applied at CREATE time (Daytona's osUser field)
|
|
237
|
+
* — Daytona has no per-exec user switch, so the user commands actually run
|
|
238
|
+
* as is governed by the sandbox image (USER directive; default Daytona
|
|
239
|
+
* images use "daytona" with passwordless sudo). A non-root value must
|
|
240
|
+
* exist in the image. Pass "root" to keep the image's default user and
|
|
241
|
+
* elevate every command through a `sudo -n` wrapper instead (requires
|
|
242
|
+
* passwordless sudo in the image; default images have it). File operations
|
|
243
|
+
* go through the Daytona daemon and are NOT elevated.
|
|
244
|
+
*/
|
|
245
|
+
user?: string;
|
|
246
|
+
/** Home directory used by the SDK for agent config paths; not consumed by the provider. */
|
|
247
|
+
homeDir?: string;
|
|
87
248
|
}
|
|
88
249
|
/** Options for listing sandboxes */
|
|
89
250
|
interface SandboxListOptions {
|
|
251
|
+
/** "running" matches Daytona "started"; "paused" matches "stopped"/"archived". */
|
|
90
252
|
state?: ("running" | "paused")[];
|
|
91
253
|
metadata?: Record<string, string>;
|
|
92
254
|
limit?: number;
|
|
@@ -106,6 +268,8 @@ interface SandboxFiles {
|
|
|
106
268
|
path: string;
|
|
107
269
|
data: string | Buffer | ArrayBuffer | Uint8Array;
|
|
108
270
|
}>): Promise<void>;
|
|
271
|
+
/** Upload a local file by path, without buffering it whole */
|
|
272
|
+
writeFromPath(sandboxPath: string, localPath: string): Promise<void>;
|
|
109
273
|
makeDir(path: string): Promise<void>;
|
|
110
274
|
exists(path: string): Promise<boolean>;
|
|
111
275
|
list(path: string): Promise<FileInfo[]>;
|
|
@@ -142,6 +306,20 @@ interface DaytonaConfig {
|
|
|
142
306
|
defaultTimeoutMs?: number;
|
|
143
307
|
/** Daytona snapshot name (default: 'evolve-all'). Create custom snapshots via `cd assets && ./build.sh daytona` */
|
|
144
308
|
snapshotName?: string;
|
|
309
|
+
/**
|
|
310
|
+
* Evolve-managed toolbox base URL. Setting it puts the provider in MANAGED
|
|
311
|
+
* mode, where `apiKey` is an Evolve API key rather than a Daytona one and
|
|
312
|
+
* both planes ride the Dashboard.
|
|
313
|
+
*
|
|
314
|
+
* One field rather than a separate `managed: true` because the two effects
|
|
315
|
+
* have one cause: if the toolbox belongs to the platform, so do the
|
|
316
|
+
* snapshots behind it, so managed creates name an existing platform snapshot
|
|
317
|
+
* and never build one. Resolved by the Evolve SDK; direct/BYO callers leave
|
|
318
|
+
* it unset.
|
|
319
|
+
*
|
|
320
|
+
* @internal
|
|
321
|
+
*/
|
|
322
|
+
managedToolboxUrl?: string;
|
|
145
323
|
}
|
|
146
324
|
interface ResolvedDaytonaConfig {
|
|
147
325
|
apiKey: string;
|
|
@@ -149,6 +327,110 @@ interface ResolvedDaytonaConfig {
|
|
|
149
327
|
target?: string;
|
|
150
328
|
defaultTimeoutMs?: number;
|
|
151
329
|
snapshotName?: string;
|
|
330
|
+
managedToolboxUrl?: string;
|
|
331
|
+
}
|
|
332
|
+
/** What a managed sandbox's streaming log follow needs to reach the Dashboard. */
|
|
333
|
+
interface ManagedStreamContext {
|
|
334
|
+
toolboxUrl: string;
|
|
335
|
+
apiKey: string;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Split Daytona's multiplexed command log into stdout and stderr as the bytes
|
|
339
|
+
* arrive.
|
|
340
|
+
*
|
|
341
|
+
* The wire format is one byte stream with 3-byte markers announcing which
|
|
342
|
+
* stream the following bytes belong to (STDOUT_PREFIX_BYTES /
|
|
343
|
+
* STDERR_PREFIX_BYTES, both exported by the Daytona SDK — this reads their
|
|
344
|
+
* framing, it does not invent one). Two things make a streaming demux
|
|
345
|
+
* different from the SDK's whole-buffer one: a marker can be split across two
|
|
346
|
+
* chunks, so the last MAX_PREFIX_LEN-1 bytes are always held back rather than
|
|
347
|
+
* emitted; and a multi-byte UTF-8 character can be split too, so each stream
|
|
348
|
+
* keeps its own decoder in streaming mode.
|
|
349
|
+
*/
|
|
350
|
+
declare function createLogDemuxer(onStdout: (chunk: string) => void, onStderr: (chunk: string) => void): {
|
|
351
|
+
push(chunk: Uint8Array): void;
|
|
352
|
+
flush(): void;
|
|
353
|
+
};
|
|
354
|
+
/**
|
|
355
|
+
* Follow a session command's logs over plain HTTP.
|
|
356
|
+
*
|
|
357
|
+
* The Daytona SDK follows logs over a WEBSOCKET (Process.js:289 rewrites the
|
|
358
|
+
* toolbox base to ws:// and opens a socket). A managed sandbox's toolbox base
|
|
359
|
+
* is a Next.js route handler, and a Next route handler never sees a websocket
|
|
360
|
+
* upgrade — the codebase's one websocket proxy lives in a separate custom
|
|
361
|
+
* server for exactly that reason. So managed mode cannot use that transport.
|
|
362
|
+
*
|
|
363
|
+
* It does not have to. MEASURED 2026-07-26 against a live sandbox: the same
|
|
364
|
+
* endpoint with `?follow=true` over ordinary HTTP answers 200 with
|
|
365
|
+
* `transfer-encoding: chunked` and `content-type: application/octet-stream`,
|
|
366
|
+
* and chunks arrive as the command produces them — 95 ms, 1045 ms, 2127 ms,
|
|
367
|
+
* 2984 ms, 3976 ms for a command printing one line per second. That is real
|
|
368
|
+
* streaming through a plain response body, which is what the Dashboard route
|
|
369
|
+
* pipes through unbuffered.
|
|
370
|
+
*/
|
|
371
|
+
declare function followManagedSessionLogs(context: ManagedStreamContext, sandboxId: string, sessionId: string, commandId: string, onStdout: (chunk: string) => void, onStderr: (chunk: string) => void): Promise<void>;
|
|
372
|
+
/**
|
|
373
|
+
* Read a command's streams out of whatever shape Daytona returned.
|
|
374
|
+
*
|
|
375
|
+
* Session logs are USUALLY framed with per-stream markers, and the Daytona
|
|
376
|
+
* SDK's demux returns "" for a stream whose marker never appears. An empty
|
|
377
|
+
* string is not "this command printed nothing" — it means "this daemon build
|
|
378
|
+
* did not frame the output" — and `??` does not fall through an empty string,
|
|
379
|
+
* so reading `stdout ?? output` silently reports every such command as silent.
|
|
380
|
+
*
|
|
381
|
+
* Measured 2026-07-26 on a live sandbox booted from ubuntu:22.04: a command
|
|
382
|
+
* printing one line to each stream came back as
|
|
383
|
+
* {output: "hello-managed\noops\n", stdout: null, exitCode: 0} with no marker
|
|
384
|
+
* bytes anywhere in the log body, and the provider reported exit 0 with empty
|
|
385
|
+
* stdout. Unframed output goes to stdout, because that is what the combined
|
|
386
|
+
* `output` field is; a framed response is untouched.
|
|
387
|
+
*/
|
|
388
|
+
declare function readCommandStreams(source: {
|
|
389
|
+
stdout?: string | null;
|
|
390
|
+
stderr?: string | null;
|
|
391
|
+
output?: string | null;
|
|
392
|
+
}): {
|
|
393
|
+
stdout: string;
|
|
394
|
+
stderr: string;
|
|
395
|
+
};
|
|
396
|
+
declare class DaytonaCommands implements SandboxCommands {
|
|
397
|
+
private sandbox;
|
|
398
|
+
private user?;
|
|
399
|
+
private managedStream?;
|
|
400
|
+
constructor(sandbox: Sandbox, user?: string | undefined, managedStream?: ManagedStreamContext | undefined);
|
|
401
|
+
/**
|
|
402
|
+
* Stream a command's output to callbacks. Direct mode uses the Daytona
|
|
403
|
+
* SDK's own follow, which is a websocket; managed mode uses the HTTP
|
|
404
|
+
* chunked follow, because a Dashboard route handler cannot terminate a
|
|
405
|
+
* websocket upgrade (see followManagedSessionLogs).
|
|
406
|
+
*/
|
|
407
|
+
private followLogs;
|
|
408
|
+
run(command: string, options?: SandboxRunOptions): Promise<SandboxCommandResult>;
|
|
409
|
+
spawn(command: string, options?: SandboxSpawnOptions): Promise<SandboxCommandHandle>;
|
|
410
|
+
list(): Promise<ProcessInfo[]>;
|
|
411
|
+
kill(processId: string): Promise<boolean>;
|
|
412
|
+
}
|
|
413
|
+
declare class DaytonaFiles implements SandboxFiles {
|
|
414
|
+
private sandbox;
|
|
415
|
+
constructor(sandbox: Sandbox);
|
|
416
|
+
read(path: string): Promise<string | Uint8Array>;
|
|
417
|
+
write(path: string, content: string | Buffer | ArrayBuffer | Uint8Array): Promise<void>;
|
|
418
|
+
writeBatch(files: Array<{
|
|
419
|
+
path: string;
|
|
420
|
+
data: string | Buffer | ArrayBuffer | Uint8Array;
|
|
421
|
+
}>): Promise<void>;
|
|
422
|
+
/**
|
|
423
|
+
* Upload a local file by PATH. Daytona's own uploadFile has a local-path
|
|
424
|
+
* overload (FileSystem.d.ts: `uploadFile(localPath: string, remotePath:
|
|
425
|
+
* string, timeout?)`), so the bytes never pass through this process's heap —
|
|
426
|
+
* which is what makes a large artifact safe to upload under concurrency.
|
|
427
|
+
*/
|
|
428
|
+
writeFromPath(sandboxPath: string, localPath: string): Promise<void>;
|
|
429
|
+
makeDir(path: string): Promise<void>;
|
|
430
|
+
exists(path: string): Promise<boolean>;
|
|
431
|
+
list(path: string): Promise<FileInfo[]>;
|
|
432
|
+
remove(path: string): Promise<void>;
|
|
433
|
+
rename(oldPath: string, newPath: string): Promise<void>;
|
|
152
434
|
}
|
|
153
435
|
declare class DaytonaProvider implements SandboxProvider {
|
|
154
436
|
readonly providerType: "daytona";
|
|
@@ -156,6 +438,17 @@ declare class DaytonaProvider implements SandboxProvider {
|
|
|
156
438
|
private readonly client;
|
|
157
439
|
private readonly defaultTimeoutMs;
|
|
158
440
|
private readonly snapshotName;
|
|
441
|
+
/**
|
|
442
|
+
* Sandbox user configured at create time, reapplied on connect() so the
|
|
443
|
+
* root sudo wrapper keeps applying. In-memory only: a connect() from a
|
|
444
|
+
* fresh process falls back to the sandbox's create-time OS user without
|
|
445
|
+
* the wrapper — callers reconnecting across processes must recreate the
|
|
446
|
+
* provider and sandbox with the same user, or root-only operations fail
|
|
447
|
+
* loudly.
|
|
448
|
+
*/
|
|
449
|
+
private readonly sandboxUsers;
|
|
450
|
+
/** Set only in Evolve-managed mode; see the EVOLVE-MANAGED MODE section. */
|
|
451
|
+
private readonly managedStream?;
|
|
159
452
|
constructor(config: ResolvedDaytonaConfig);
|
|
160
453
|
create(options: SandboxCreateOptions): Promise<SandboxInstance>;
|
|
161
454
|
connect(sandboxId: string, _timeoutMs?: number): Promise<SandboxInstance>;
|
|
@@ -168,5 +461,14 @@ declare class DaytonaProvider implements SandboxProvider {
|
|
|
168
461
|
* @throws Error if apiKey cannot be resolved
|
|
169
462
|
*/
|
|
170
463
|
declare function createDaytonaProvider(config?: DaytonaConfig): SandboxProvider;
|
|
464
|
+
/** @internal Test-only export for unit testing wrapCommand logic. */
|
|
465
|
+
declare const _testWrapCommand: typeof wrapCommand;
|
|
466
|
+
declare const _testMapNetworkPolicy: typeof mapNetworkPolicy;
|
|
467
|
+
declare const _testImageRegistryHost: typeof imageRegistryHost;
|
|
468
|
+
declare const _testToSandboxInfo: typeof toSandboxInfo;
|
|
469
|
+
declare const _testDaytonaStateToEvolveState: typeof daytonaStateToEvolveState;
|
|
470
|
+
declare const _testCreateLogDemuxer: typeof createLogDemuxer;
|
|
471
|
+
declare const _testFollowManagedSessionLogs: typeof followManagedSessionLogs;
|
|
472
|
+
declare const _testReadCommandStreams: typeof readCommandStreams;
|
|
171
473
|
|
|
172
|
-
export { type DaytonaConfig, DaytonaProvider, type FileInfo, type ProcessInfo, type SandboxCommandHandle, type SandboxCommandResult, type SandboxCommands, type SandboxCreateOptions, type SandboxFiles, type SandboxInfo, type SandboxInstance, type SandboxListOptions, type SandboxProvider, type SandboxResources, type SandboxRunOptions, type SandboxSpawnOptions, _testWrapCommand, createDaytonaProvider };
|
|
474
|
+
export { DAYTONA_AUTO_DELETE_GRACE_MINUTES, DAYTONA_MAX_NETWORK_ALLOWLIST, DaytonaCommands, type DaytonaConfig, DaytonaFiles, DaytonaImagePullError, DaytonaNetworkPolicyError, type DaytonaNetworkPolicyReason, DaytonaProvider, DaytonaResourcesError, type FileInfo, type ProcessInfo, type SandboxCommandHandle, type SandboxCommandResult, type SandboxCommands, type SandboxCreateOptions, type SandboxFiles, type SandboxInfo, type SandboxInstance, type SandboxListOptions, type SandboxProvider, type SandboxResources, type SandboxRunOptions, type SandboxSpawnOptions, _testCreateLogDemuxer, _testDaytonaStateToEvolveState, _testFollowManagedSessionLogs, _testImageRegistryHost, _testMapNetworkPolicy, _testReadCommandStreams, _testToSandboxInfo, _testWrapCommand, createDaytonaProvider };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Sandbox } from '@daytonaio/sdk';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Daytona Sandbox Provider
|
|
3
5
|
*
|
|
@@ -7,17 +9,140 @@
|
|
|
7
9
|
* - Mirror E2B provider interface for SDK compatibility
|
|
8
10
|
* - Uses public Docker images via IMAGE_MAP
|
|
9
11
|
* - Parallel structure to E2B provider
|
|
12
|
+
*
|
|
13
|
+
* Daytona-specific notes:
|
|
14
|
+
* - Network policy maps to Daytona's networkBlockAll / networkAllowList,
|
|
15
|
+
* enforced by kernel iptables on the runner. The allowlist is IPv4 CIDR
|
|
16
|
+
* ONLY (max 10 entries, DAYTONA_MAX_NETWORK_ALLOWLIST). Hostname
|
|
17
|
+
* destinations are pinned to their IPv4 addresses by a DNS lookup at
|
|
18
|
+
* create time — see the DNS-ROTATION CAVEAT on SandboxCreateOptions.network.
|
|
19
|
+
* Destinations Daytona can never enforce (wildcards, IPv6, unresolvable
|
|
20
|
+
* hostnames, >10 CIDRs) throw DaytonaNetworkPolicyError instead of
|
|
21
|
+
* silently weakening the policy.
|
|
22
|
+
* - The `user` option is a CREATE-TIME OS user (Daytona's osUser field);
|
|
23
|
+
* there is no per-exec user switch — the Daytona daemon runs commands as
|
|
24
|
+
* the container's user (governed by the image's USER directive; default
|
|
25
|
+
* images use "daytona" with passwordless sudo). `user: "root"` keeps the
|
|
26
|
+
* image's default user and elevates every command through a `sudo -n`
|
|
27
|
+
* wrapper instead (mirrors the Modal provider's su wrapper). File
|
|
28
|
+
* operations always go through the Daytona daemon and are NOT elevated.
|
|
29
|
+
* - Private registry images (AWS ECR, GHCR, GCP Artifact Registry, ...)
|
|
30
|
+
* require registry credentials pre-registered in the Daytona dashboard
|
|
31
|
+
* (Registries page) — Daytona has no per-call image pull secret. Pull
|
|
32
|
+
* failures for such images throw DaytonaImagePullError.
|
|
33
|
+
* - getInfo()/list() report the API's real createdAt timestamp (never a
|
|
34
|
+
* fabricated client-side date); Daytona exposes no end timestamp, so
|
|
35
|
+
* endAt is always undefined.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Daytona's hard cap on network allowlist size (validated server-side too).
|
|
40
|
+
* Policies that resolve to more CIDRs throw DaytonaNetworkPolicyError.
|
|
10
41
|
*/
|
|
11
42
|
/**
|
|
12
|
-
*
|
|
43
|
+
* Minutes a STOPPED sandbox is kept before Daytona deletes it. Long enough that
|
|
44
|
+
* a stop nobody ordered leaves something to look at and something to collect
|
|
45
|
+
* from; short enough that it is never a way to hold billable state.
|
|
46
|
+
*/
|
|
47
|
+
declare const DAYTONA_AUTO_DELETE_GRACE_MINUTES = 10;
|
|
48
|
+
declare const DAYTONA_MAX_NETWORK_ALLOWLIST = 10;
|
|
49
|
+
/** Why a network policy cannot be enforced by Daytona. */
|
|
50
|
+
type DaytonaNetworkPolicyReason = "wildcard-hostname" | "ipv6-unsupported" | "port-unsupported" | "invalid-ipv4" | "unresolvable-hostname" | "allowlist-too-large";
|
|
51
|
+
/**
|
|
52
|
+
* Typed error for network policies Daytona cannot enforce.
|
|
13
53
|
*
|
|
14
|
-
* Daytona's
|
|
15
|
-
*
|
|
16
|
-
*
|
|
54
|
+
* Daytona's allowlist is kernel-level IPv4 CIDR filtering only (max 10
|
|
55
|
+
* entries) — no DNS/domain layer. Anything that cannot be pinned to stable
|
|
56
|
+
* IPv4 CIDRs at create time is rejected loudly instead of silently
|
|
57
|
+
* weakening the sandbox's egress policy.
|
|
17
58
|
*/
|
|
18
|
-
declare
|
|
19
|
-
|
|
20
|
-
|
|
59
|
+
declare class DaytonaNetworkPolicyError extends Error {
|
|
60
|
+
readonly reason: DaytonaNetworkPolicyReason;
|
|
61
|
+
/** The offending destination (absent for list-level violations). */
|
|
62
|
+
readonly destination?: string;
|
|
63
|
+
constructor(reason: DaytonaNetworkPolicyReason, message: string, destination?: string);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Typed error for image pull failures on private registries.
|
|
67
|
+
*
|
|
68
|
+
* Daytona has no per-call image pull secret: credentials for private
|
|
69
|
+
* registries (AWS ECR, GHCR, GCP Artifact Registry, private Docker Hub)
|
|
70
|
+
* must be pre-registered in the Daytona dashboard BEFORE creating the
|
|
71
|
+
* sandbox (dashboard → Registries → Add Registry).
|
|
72
|
+
*/
|
|
73
|
+
declare class DaytonaImagePullError extends Error {
|
|
74
|
+
/** The image reference that failed to pull. */
|
|
75
|
+
readonly image: string;
|
|
76
|
+
constructor(image: string, cause?: unknown);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Typed error for create-time sizing requests an EXISTING snapshot cannot
|
|
80
|
+
* honor. Daytona pins cpu/memory/disk on the SNAPSHOT at build time;
|
|
81
|
+
* create-from-snapshot has no resources parameter, so a `resources` request
|
|
82
|
+
* against a cached snapshot would be silently ignored. Per the provider law
|
|
83
|
+
* (reject what you cannot enforce) it is refused loudly instead: pre-build a
|
|
84
|
+
* snapshot with the desired sizing under its own name, or drop `resources`.
|
|
85
|
+
*/
|
|
86
|
+
declare class DaytonaResourcesError extends Error {
|
|
87
|
+
/** The existing snapshot whose pinned sizing cannot be overridden. */
|
|
88
|
+
readonly snapshot: string;
|
|
89
|
+
constructor(snapshot: string);
|
|
90
|
+
}
|
|
91
|
+
declare function wrapCommand(command: string, cwd?: string, envs?: Record<string, string>, user?: string): string;
|
|
92
|
+
/** Daytona create() params derived from Evolve's provider-neutral network policy. */
|
|
93
|
+
interface DaytonaNetworkCreateParams {
|
|
94
|
+
networkBlockAll?: boolean;
|
|
95
|
+
networkAllowList?: string;
|
|
96
|
+
}
|
|
97
|
+
/** Resolves a hostname to its IPv4 addresses (injectable for tests). */
|
|
98
|
+
type HostnameResolver = (hostname: string) => Promise<string[]>;
|
|
99
|
+
/**
|
|
100
|
+
* Map Evolve's provider-neutral network policy onto Daytona create() params.
|
|
101
|
+
*
|
|
102
|
+
* - outbound "open" (or no policy) → no restrictions
|
|
103
|
+
* - outbound "blocked", no allowlist → networkBlockAll: true (kernel DROP of all egress)
|
|
104
|
+
* - outbound "blocked" with allowlist → networkAllowList (comma-separated IPv4
|
|
105
|
+
* CIDRs; bare IPv4 gets /32). Daytona enforces the allowlist with kernel
|
|
106
|
+
* iptables and supports IPv4 CIDRs ONLY, max 10 entries.
|
|
107
|
+
*
|
|
108
|
+
* DNS-ROTATION CAVEAT (load-bearing): hostname destinations are resolved to
|
|
109
|
+
* their IPv4 A records ONCE, at create time, and pinned as /32 CIDRs. If the
|
|
110
|
+
* host rotates DNS afterwards (CDNs and cloud APIs often do), traffic to the
|
|
111
|
+
* new IPs is BLOCKED for the sandbox's lifetime. Prefer stable IPs/CIDRs for
|
|
112
|
+
* anything long-running.
|
|
113
|
+
*
|
|
114
|
+
* Anything that cannot be pinned to stable IPv4 CIDRs throws
|
|
115
|
+
* DaytonaNetworkPolicyError (wildcard hostnames, IPv6, unresolvable
|
|
116
|
+
* hostnames, >10 resolved CIDRs) — never silently weakened.
|
|
117
|
+
*/
|
|
118
|
+
declare function mapNetworkPolicy(network?: SandboxCreateOptions["network"], resolveHostname?: HostnameResolver): Promise<DaytonaNetworkCreateParams>;
|
|
119
|
+
/**
|
|
120
|
+
* Registry host of an image reference, or undefined for Docker Hub images.
|
|
121
|
+
* A reference carries a registry host only when its first path segment
|
|
122
|
+
* contains "." or ":" or is "localhost" (Docker's own heuristic).
|
|
123
|
+
*/
|
|
124
|
+
declare function imageRegistryHost(image: string): string | undefined;
|
|
125
|
+
/** Fields we read off a Daytona SDK sandbox to build a SandboxInfo. */
|
|
126
|
+
interface DaytonaSandboxLike {
|
|
127
|
+
id: string;
|
|
128
|
+
name?: string;
|
|
129
|
+
snapshot?: string;
|
|
130
|
+
labels?: Record<string, string>;
|
|
131
|
+
createdAt?: string;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Build a SandboxInfo from a Daytona sandbox entity. Timestamps come from
|
|
135
|
+
* the API's createdAt (never fabricated client-side; empty string when the
|
|
136
|
+
* API omits it). Daytona exposes no end timestamp, so endAt is always
|
|
137
|
+
* undefined.
|
|
138
|
+
*/
|
|
139
|
+
declare function toSandboxInfo(sandbox: DaytonaSandboxLike): SandboxInfo;
|
|
140
|
+
/**
|
|
141
|
+
* Map a Daytona sandbox state onto Evolve's list() filter states.
|
|
142
|
+
* "started" → running; "stopped"/"archived" → paused (our pause() stops the
|
|
143
|
+
* sandbox); transitional and terminal states match no filter.
|
|
144
|
+
*/
|
|
145
|
+
declare function daytonaStateToEvolveState(state?: string): "running" | "paused" | undefined;
|
|
21
146
|
/** Result of a completed sandbox command */
|
|
22
147
|
interface SandboxCommandResult {
|
|
23
148
|
exitCode: number;
|
|
@@ -46,6 +171,7 @@ interface SandboxInfo {
|
|
|
46
171
|
name?: string;
|
|
47
172
|
metadata: Record<string, string>;
|
|
48
173
|
startedAt: string;
|
|
174
|
+
/** End time (undefined for running sandboxes; Daytona never exposes one) */
|
|
49
175
|
endAt?: string;
|
|
50
176
|
}
|
|
51
177
|
/** File or directory entry info */
|
|
@@ -82,11 +208,47 @@ interface SandboxCreateOptions {
|
|
|
82
208
|
metadata?: Record<string, string>;
|
|
83
209
|
timeoutMs?: number;
|
|
84
210
|
workingDirectory?: string;
|
|
85
|
-
/**
|
|
211
|
+
/**
|
|
212
|
+
* Resource allocation (cpu cores, memory GiB, disk GiB), applied when a
|
|
213
|
+
* SNAPSHOT IS BUILT for the image (Daytona pins sizing on the snapshot).
|
|
214
|
+
* When the named snapshot ALREADY exists it cannot be resized at create —
|
|
215
|
+
* declaring resources then throws DaytonaResourcesError rather than
|
|
216
|
+
* silently ignoring them (pre-build a sizing-addressed snapshot instead).
|
|
217
|
+
*/
|
|
86
218
|
resources?: SandboxResources;
|
|
219
|
+
/**
|
|
220
|
+
* Provider-neutral outbound network policy, enforced by kernel iptables on
|
|
221
|
+
* the Daytona runner. "blocked" with no allowedDestinations drops all
|
|
222
|
+
* egress. With allowedDestinations, Daytona supports IPv4 CIDRs ONLY (max
|
|
223
|
+
* 10): IPs/CIDRs pass through; hostnames are DNS-resolved ONCE at create
|
|
224
|
+
* time and pinned as /32 CIDRs — if the host rotates DNS later (CDNs and
|
|
225
|
+
* cloud APIs often do), its new IPs are BLOCKED for the sandbox's
|
|
226
|
+
* lifetime. Wildcards, IPv6, and unresolvable hostnames throw
|
|
227
|
+
* DaytonaNetworkPolicyError rather than silently weakening the policy.
|
|
228
|
+
* Note: on Daytona orgs below Tier 3, org network policy overrides
|
|
229
|
+
* per-sandbox settings server-side.
|
|
230
|
+
*/
|
|
231
|
+
network?: {
|
|
232
|
+
outbound: "open" | "blocked";
|
|
233
|
+
allowedDestinations?: string[];
|
|
234
|
+
};
|
|
235
|
+
/**
|
|
236
|
+
* OS user for the sandbox, applied at CREATE time (Daytona's osUser field)
|
|
237
|
+
* — Daytona has no per-exec user switch, so the user commands actually run
|
|
238
|
+
* as is governed by the sandbox image (USER directive; default Daytona
|
|
239
|
+
* images use "daytona" with passwordless sudo). A non-root value must
|
|
240
|
+
* exist in the image. Pass "root" to keep the image's default user and
|
|
241
|
+
* elevate every command through a `sudo -n` wrapper instead (requires
|
|
242
|
+
* passwordless sudo in the image; default images have it). File operations
|
|
243
|
+
* go through the Daytona daemon and are NOT elevated.
|
|
244
|
+
*/
|
|
245
|
+
user?: string;
|
|
246
|
+
/** Home directory used by the SDK for agent config paths; not consumed by the provider. */
|
|
247
|
+
homeDir?: string;
|
|
87
248
|
}
|
|
88
249
|
/** Options for listing sandboxes */
|
|
89
250
|
interface SandboxListOptions {
|
|
251
|
+
/** "running" matches Daytona "started"; "paused" matches "stopped"/"archived". */
|
|
90
252
|
state?: ("running" | "paused")[];
|
|
91
253
|
metadata?: Record<string, string>;
|
|
92
254
|
limit?: number;
|
|
@@ -106,6 +268,8 @@ interface SandboxFiles {
|
|
|
106
268
|
path: string;
|
|
107
269
|
data: string | Buffer | ArrayBuffer | Uint8Array;
|
|
108
270
|
}>): Promise<void>;
|
|
271
|
+
/** Upload a local file by path, without buffering it whole */
|
|
272
|
+
writeFromPath(sandboxPath: string, localPath: string): Promise<void>;
|
|
109
273
|
makeDir(path: string): Promise<void>;
|
|
110
274
|
exists(path: string): Promise<boolean>;
|
|
111
275
|
list(path: string): Promise<FileInfo[]>;
|
|
@@ -142,6 +306,20 @@ interface DaytonaConfig {
|
|
|
142
306
|
defaultTimeoutMs?: number;
|
|
143
307
|
/** Daytona snapshot name (default: 'evolve-all'). Create custom snapshots via `cd assets && ./build.sh daytona` */
|
|
144
308
|
snapshotName?: string;
|
|
309
|
+
/**
|
|
310
|
+
* Evolve-managed toolbox base URL. Setting it puts the provider in MANAGED
|
|
311
|
+
* mode, where `apiKey` is an Evolve API key rather than a Daytona one and
|
|
312
|
+
* both planes ride the Dashboard.
|
|
313
|
+
*
|
|
314
|
+
* One field rather than a separate `managed: true` because the two effects
|
|
315
|
+
* have one cause: if the toolbox belongs to the platform, so do the
|
|
316
|
+
* snapshots behind it, so managed creates name an existing platform snapshot
|
|
317
|
+
* and never build one. Resolved by the Evolve SDK; direct/BYO callers leave
|
|
318
|
+
* it unset.
|
|
319
|
+
*
|
|
320
|
+
* @internal
|
|
321
|
+
*/
|
|
322
|
+
managedToolboxUrl?: string;
|
|
145
323
|
}
|
|
146
324
|
interface ResolvedDaytonaConfig {
|
|
147
325
|
apiKey: string;
|
|
@@ -149,6 +327,110 @@ interface ResolvedDaytonaConfig {
|
|
|
149
327
|
target?: string;
|
|
150
328
|
defaultTimeoutMs?: number;
|
|
151
329
|
snapshotName?: string;
|
|
330
|
+
managedToolboxUrl?: string;
|
|
331
|
+
}
|
|
332
|
+
/** What a managed sandbox's streaming log follow needs to reach the Dashboard. */
|
|
333
|
+
interface ManagedStreamContext {
|
|
334
|
+
toolboxUrl: string;
|
|
335
|
+
apiKey: string;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Split Daytona's multiplexed command log into stdout and stderr as the bytes
|
|
339
|
+
* arrive.
|
|
340
|
+
*
|
|
341
|
+
* The wire format is one byte stream with 3-byte markers announcing which
|
|
342
|
+
* stream the following bytes belong to (STDOUT_PREFIX_BYTES /
|
|
343
|
+
* STDERR_PREFIX_BYTES, both exported by the Daytona SDK — this reads their
|
|
344
|
+
* framing, it does not invent one). Two things make a streaming demux
|
|
345
|
+
* different from the SDK's whole-buffer one: a marker can be split across two
|
|
346
|
+
* chunks, so the last MAX_PREFIX_LEN-1 bytes are always held back rather than
|
|
347
|
+
* emitted; and a multi-byte UTF-8 character can be split too, so each stream
|
|
348
|
+
* keeps its own decoder in streaming mode.
|
|
349
|
+
*/
|
|
350
|
+
declare function createLogDemuxer(onStdout: (chunk: string) => void, onStderr: (chunk: string) => void): {
|
|
351
|
+
push(chunk: Uint8Array): void;
|
|
352
|
+
flush(): void;
|
|
353
|
+
};
|
|
354
|
+
/**
|
|
355
|
+
* Follow a session command's logs over plain HTTP.
|
|
356
|
+
*
|
|
357
|
+
* The Daytona SDK follows logs over a WEBSOCKET (Process.js:289 rewrites the
|
|
358
|
+
* toolbox base to ws:// and opens a socket). A managed sandbox's toolbox base
|
|
359
|
+
* is a Next.js route handler, and a Next route handler never sees a websocket
|
|
360
|
+
* upgrade — the codebase's one websocket proxy lives in a separate custom
|
|
361
|
+
* server for exactly that reason. So managed mode cannot use that transport.
|
|
362
|
+
*
|
|
363
|
+
* It does not have to. MEASURED 2026-07-26 against a live sandbox: the same
|
|
364
|
+
* endpoint with `?follow=true` over ordinary HTTP answers 200 with
|
|
365
|
+
* `transfer-encoding: chunked` and `content-type: application/octet-stream`,
|
|
366
|
+
* and chunks arrive as the command produces them — 95 ms, 1045 ms, 2127 ms,
|
|
367
|
+
* 2984 ms, 3976 ms for a command printing one line per second. That is real
|
|
368
|
+
* streaming through a plain response body, which is what the Dashboard route
|
|
369
|
+
* pipes through unbuffered.
|
|
370
|
+
*/
|
|
371
|
+
declare function followManagedSessionLogs(context: ManagedStreamContext, sandboxId: string, sessionId: string, commandId: string, onStdout: (chunk: string) => void, onStderr: (chunk: string) => void): Promise<void>;
|
|
372
|
+
/**
|
|
373
|
+
* Read a command's streams out of whatever shape Daytona returned.
|
|
374
|
+
*
|
|
375
|
+
* Session logs are USUALLY framed with per-stream markers, and the Daytona
|
|
376
|
+
* SDK's demux returns "" for a stream whose marker never appears. An empty
|
|
377
|
+
* string is not "this command printed nothing" — it means "this daemon build
|
|
378
|
+
* did not frame the output" — and `??` does not fall through an empty string,
|
|
379
|
+
* so reading `stdout ?? output` silently reports every such command as silent.
|
|
380
|
+
*
|
|
381
|
+
* Measured 2026-07-26 on a live sandbox booted from ubuntu:22.04: a command
|
|
382
|
+
* printing one line to each stream came back as
|
|
383
|
+
* {output: "hello-managed\noops\n", stdout: null, exitCode: 0} with no marker
|
|
384
|
+
* bytes anywhere in the log body, and the provider reported exit 0 with empty
|
|
385
|
+
* stdout. Unframed output goes to stdout, because that is what the combined
|
|
386
|
+
* `output` field is; a framed response is untouched.
|
|
387
|
+
*/
|
|
388
|
+
declare function readCommandStreams(source: {
|
|
389
|
+
stdout?: string | null;
|
|
390
|
+
stderr?: string | null;
|
|
391
|
+
output?: string | null;
|
|
392
|
+
}): {
|
|
393
|
+
stdout: string;
|
|
394
|
+
stderr: string;
|
|
395
|
+
};
|
|
396
|
+
declare class DaytonaCommands implements SandboxCommands {
|
|
397
|
+
private sandbox;
|
|
398
|
+
private user?;
|
|
399
|
+
private managedStream?;
|
|
400
|
+
constructor(sandbox: Sandbox, user?: string | undefined, managedStream?: ManagedStreamContext | undefined);
|
|
401
|
+
/**
|
|
402
|
+
* Stream a command's output to callbacks. Direct mode uses the Daytona
|
|
403
|
+
* SDK's own follow, which is a websocket; managed mode uses the HTTP
|
|
404
|
+
* chunked follow, because a Dashboard route handler cannot terminate a
|
|
405
|
+
* websocket upgrade (see followManagedSessionLogs).
|
|
406
|
+
*/
|
|
407
|
+
private followLogs;
|
|
408
|
+
run(command: string, options?: SandboxRunOptions): Promise<SandboxCommandResult>;
|
|
409
|
+
spawn(command: string, options?: SandboxSpawnOptions): Promise<SandboxCommandHandle>;
|
|
410
|
+
list(): Promise<ProcessInfo[]>;
|
|
411
|
+
kill(processId: string): Promise<boolean>;
|
|
412
|
+
}
|
|
413
|
+
declare class DaytonaFiles implements SandboxFiles {
|
|
414
|
+
private sandbox;
|
|
415
|
+
constructor(sandbox: Sandbox);
|
|
416
|
+
read(path: string): Promise<string | Uint8Array>;
|
|
417
|
+
write(path: string, content: string | Buffer | ArrayBuffer | Uint8Array): Promise<void>;
|
|
418
|
+
writeBatch(files: Array<{
|
|
419
|
+
path: string;
|
|
420
|
+
data: string | Buffer | ArrayBuffer | Uint8Array;
|
|
421
|
+
}>): Promise<void>;
|
|
422
|
+
/**
|
|
423
|
+
* Upload a local file by PATH. Daytona's own uploadFile has a local-path
|
|
424
|
+
* overload (FileSystem.d.ts: `uploadFile(localPath: string, remotePath:
|
|
425
|
+
* string, timeout?)`), so the bytes never pass through this process's heap —
|
|
426
|
+
* which is what makes a large artifact safe to upload under concurrency.
|
|
427
|
+
*/
|
|
428
|
+
writeFromPath(sandboxPath: string, localPath: string): Promise<void>;
|
|
429
|
+
makeDir(path: string): Promise<void>;
|
|
430
|
+
exists(path: string): Promise<boolean>;
|
|
431
|
+
list(path: string): Promise<FileInfo[]>;
|
|
432
|
+
remove(path: string): Promise<void>;
|
|
433
|
+
rename(oldPath: string, newPath: string): Promise<void>;
|
|
152
434
|
}
|
|
153
435
|
declare class DaytonaProvider implements SandboxProvider {
|
|
154
436
|
readonly providerType: "daytona";
|
|
@@ -156,6 +438,17 @@ declare class DaytonaProvider implements SandboxProvider {
|
|
|
156
438
|
private readonly client;
|
|
157
439
|
private readonly defaultTimeoutMs;
|
|
158
440
|
private readonly snapshotName;
|
|
441
|
+
/**
|
|
442
|
+
* Sandbox user configured at create time, reapplied on connect() so the
|
|
443
|
+
* root sudo wrapper keeps applying. In-memory only: a connect() from a
|
|
444
|
+
* fresh process falls back to the sandbox's create-time OS user without
|
|
445
|
+
* the wrapper — callers reconnecting across processes must recreate the
|
|
446
|
+
* provider and sandbox with the same user, or root-only operations fail
|
|
447
|
+
* loudly.
|
|
448
|
+
*/
|
|
449
|
+
private readonly sandboxUsers;
|
|
450
|
+
/** Set only in Evolve-managed mode; see the EVOLVE-MANAGED MODE section. */
|
|
451
|
+
private readonly managedStream?;
|
|
159
452
|
constructor(config: ResolvedDaytonaConfig);
|
|
160
453
|
create(options: SandboxCreateOptions): Promise<SandboxInstance>;
|
|
161
454
|
connect(sandboxId: string, _timeoutMs?: number): Promise<SandboxInstance>;
|
|
@@ -168,5 +461,14 @@ declare class DaytonaProvider implements SandboxProvider {
|
|
|
168
461
|
* @throws Error if apiKey cannot be resolved
|
|
169
462
|
*/
|
|
170
463
|
declare function createDaytonaProvider(config?: DaytonaConfig): SandboxProvider;
|
|
464
|
+
/** @internal Test-only export for unit testing wrapCommand logic. */
|
|
465
|
+
declare const _testWrapCommand: typeof wrapCommand;
|
|
466
|
+
declare const _testMapNetworkPolicy: typeof mapNetworkPolicy;
|
|
467
|
+
declare const _testImageRegistryHost: typeof imageRegistryHost;
|
|
468
|
+
declare const _testToSandboxInfo: typeof toSandboxInfo;
|
|
469
|
+
declare const _testDaytonaStateToEvolveState: typeof daytonaStateToEvolveState;
|
|
470
|
+
declare const _testCreateLogDemuxer: typeof createLogDemuxer;
|
|
471
|
+
declare const _testFollowManagedSessionLogs: typeof followManagedSessionLogs;
|
|
472
|
+
declare const _testReadCommandStreams: typeof readCommandStreams;
|
|
171
473
|
|
|
172
|
-
export { type DaytonaConfig, DaytonaProvider, type FileInfo, type ProcessInfo, type SandboxCommandHandle, type SandboxCommandResult, type SandboxCommands, type SandboxCreateOptions, type SandboxFiles, type SandboxInfo, type SandboxInstance, type SandboxListOptions, type SandboxProvider, type SandboxResources, type SandboxRunOptions, type SandboxSpawnOptions, _testWrapCommand, createDaytonaProvider };
|
|
474
|
+
export { DAYTONA_AUTO_DELETE_GRACE_MINUTES, DAYTONA_MAX_NETWORK_ALLOWLIST, DaytonaCommands, type DaytonaConfig, DaytonaFiles, DaytonaImagePullError, DaytonaNetworkPolicyError, type DaytonaNetworkPolicyReason, DaytonaProvider, DaytonaResourcesError, type FileInfo, type ProcessInfo, type SandboxCommandHandle, type SandboxCommandResult, type SandboxCommands, type SandboxCreateOptions, type SandboxFiles, type SandboxInfo, type SandboxInstance, type SandboxListOptions, type SandboxProvider, type SandboxResources, type SandboxRunOptions, type SandboxSpawnOptions, _testCreateLogDemuxer, _testDaytonaStateToEvolveState, _testFollowManagedSessionLogs, _testImageRegistryHost, _testMapNetworkPolicy, _testReadCommandStreams, _testToSandboxInfo, _testWrapCommand, createDaytonaProvider };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {Daytona,Image}from'@daytonaio/sdk';var h={"evolve-all":"evolvingmachines/evolve-all"},S=new Set([".xlsx",".xls",".docx",".doc",".pptx",".ppt",".pdf",".zip",".tar",".gz",".7z",".rar",".png",".jpg",".jpeg",".gif",".webp",".ico",".bmp",".mp3",".wav",".ogg",".flac",".aac",".mp4",".avi",".mov",".mkv",".webm",".woff",".woff2",".ttf",".otf",".eot",".exe",".dll",".so",".dylib",".sqlite",".db",".pickle",".pkl",".parquet"]);function w(r){let e=r.substring(r.lastIndexOf(".")).toLowerCase();return S.has(e)}function v(r){let e=r.lastIndexOf("/");return e>0?r.substring(0,e):"/"}function P(r){let e=r.lastIndexOf("/");return e>=0?r.substring(e+1):r}function g(r,e,t){let s=r;return e&&(s=`cd '${e.replace(/'/g,"'\\''")}' && ${s}`),t&&Object.keys(t).length>0&&(s=`${Object.entries(t).filter(([,a])=>a!=null).map(([a,o])=>`export ${a}='${o.replace(/'/g,"'\\''")}'`).join("; ")}; ${s}`),s}var C=g,f=class{constructor(e){this.sandbox=e;}async run(e,t){let s=t?.timeoutMs?Math.floor(t.timeoutMs/1e3):void 0,n=`run-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;await this.sandbox.process.createSession(n);try{let a=await this.sandbox.process.executeSessionCommand(n,{command:g(e,t?.cwd,t?.envs),runAsync:!1},s),o=a.cmdId;o&&(t?.onStdout||t?.onStderr)&&await this.sandbox.process.getSessionCommandLogs(n,o,t.onStdout||(()=>{}),t.onStderr||(()=>{}));let d=a.stdout??a.output??"",i=a.stderr??"";if(!d&&o&&!t?.onStdout)try{let m=await this.sandbox.process.getSessionCommandLogs(n,o);d=m.stdout??m.output??"",i=m.stderr??i;}catch{}return {exitCode:a.exitCode??0,stdout:d,stderr:i}}finally{try{await this.sandbox.process.deleteSession(n);}catch{}}}async spawn(e,t){let s=`evolve-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;await this.sandbox.process.createSession(s);let n=t?.timeoutMs?Math.floor(t.timeoutMs/1e3):void 0,a=Date.now(),o=t?.timeoutMs,i=(await this.sandbox.process.executeSessionCommand(s,{command:g(e,t?.cwd,t?.envs),runAsync:true},n)).cmdId;i&&(t?.onStdout||t?.onStderr)&&this.sandbox.process.getSessionCommandLogs(s,i,t.onStdout||(()=>{}),t.onStderr||(()=>{})).catch(()=>{});let m=this.sandbox;return {processId:s,wait:async()=>{if(!i)return {exitCode:0,stdout:"",stderr:""};for(;;){if(o&&Date.now()-a>=o){try{await m.process.deleteSession(s);}catch{}return {exitCode:-1,stdout:"",stderr:"operation timed out"}}try{let c=await m.process.getSessionCommand(s,i);if(c.exitCode!==void 0)try{let l=await m.process.getSessionCommandLogs(s,i);return {exitCode:c.exitCode,stdout:l.stdout??l.output??"",stderr:l.stderr??""}}catch{return {exitCode:c.exitCode,stdout:"",stderr:""}}}catch(c){if((c instanceof Error?c.message.toLowerCase():String(c).toLowerCase()).includes("not found"))return {exitCode:-1,stdout:"",stderr:"session terminated"};throw c}await new Promise(c=>setTimeout(c,500));}},kill:async()=>{try{return await m.process.deleteSession(s),!0}catch{return false}}}}async list(){try{return (await this.sandbox.process.listSessions()).map(t=>({processId:t.sessionId||"",cmd:"",args:[],envs:{}}))}catch{return []}}async kill(e){try{return await this.sandbox.process.deleteSession(e),!0}catch{return false}}},p=class{constructor(e){this.sandbox=e;}async read(e){let t=await this.sandbox.fs.downloadFile(e);return w(e)?new Uint8Array(t):t.toString("utf-8")}async write(e,t){let s;if(typeof t=="string")s=Buffer.from(t,"utf-8");else if(Buffer.isBuffer(t))s=t;else if(t instanceof ArrayBuffer)s=Buffer.from(t);else if(t instanceof Uint8Array)s=Buffer.from(t);else throw new Error(`Unsupported content type: ${typeof t}`);await this.sandbox.fs.uploadFile(s,e);}async writeBatch(e){let t=e.map(s=>{let n;if(typeof s.data=="string")n=Buffer.from(s.data,"utf-8");else if(Buffer.isBuffer(s.data))n=s.data;else if(s.data instanceof ArrayBuffer)n=Buffer.from(s.data);else if(s.data instanceof Uint8Array)n=Buffer.from(s.data);else throw new Error(`Unsupported content type: ${typeof s.data}`);return {source:n,destination:s.path}});await this.sandbox.fs.uploadFiles(t);}async makeDir(e){await this.sandbox.fs.createFolder(e,"755");}async exists(e){try{let t=v(e),s=P(e);return (await this.sandbox.fs.listFiles(t)).some(a=>a.name===s)}catch{return false}}async list(e){return (await this.sandbox.fs.listFiles(e)).map(s=>({name:s.name,path:e.endsWith("/")?`${e}${s.name}`:`${e}/${s.name}`,type:s.isDir?"dir":"file"}))}async remove(e){await this.sandbox.fs.deleteFile(e,true);}async rename(e,t){await this.sandbox.fs.moveFiles(e,t);}},u=class{constructor(e){this.sandbox=e;this.commands=new f(e),this.files=new p(e);}commands;files;get sandboxId(){return this.sandbox.id}async getHost(e){return (await this.sandbox.getPreviewLink(e)).url}async isRunning(){return this.sandbox.state==="started"}async getInfo(){return {sandboxId:this.sandbox.id,image:this.sandbox.snapshot||"unknown",name:this.sandbox.name,metadata:this.sandbox.labels||{},startedAt:new Date().toISOString()}}async kill(){await this.sandbox.delete();}async pause(){await this.sandbox.stop();}},x=class{providerType="daytona";name="Daytona";client;defaultTimeoutMs;snapshotName;constructor(e){this.client=new Daytona({apiKey:e.apiKey,apiUrl:e.apiUrl,target:e.target}),this.defaultTimeoutMs=e.defaultTimeoutMs??36e5,this.snapshotName=e.snapshotName??"evolve-all";}async create(e){let t=e.timeoutMs??this.defaultTimeoutMs,s=Math.max(1,Math.ceil(t/6e4)),n=e.image||this.snapshotName,a;try{let o=await this.client.snapshot.get(n);if(o&&o.state==="active")console.log(`[daytona] Using cached snapshot: ${n}`),a=await this.client.create({snapshot:n,envVars:e.envs,labels:e.metadata,autoStopInterval:s},{timeout:600});else throw new Error("Snapshot not active")}catch{let o=h[n]??n;console.log(`[daytona] Snapshot "${n}" not found, building from image: ${o}`),console.log("[daytona] First run will take a few minutes (this only happens once)...");try{await this.client.snapshot.create({name:n,image:Image.base(o),resources:{cpu:e.resources?.cpu??4,memory:e.resources?.memory??4,disk:e.resources?.disk??10}},{onLogs:d=>console.log(`[daytona] ${d}`)}),console.log(`[daytona] Snapshot "${n}" ready.`),a=await this.client.create({snapshot:n,envVars:e.envs,labels:e.metadata,autoStopInterval:s},{timeout:600});}catch(d){console.warn(`[daytona] Snapshot creation failed, falling back to direct image: ${d instanceof Error?d.message:d}`),a=await this.client.create({image:o,envVars:e.envs,labels:e.metadata,autoStopInterval:s,resources:{cpu:e.resources?.cpu??4,memory:e.resources?.memory??4,disk:e.resources?.disk??10}},{timeout:600,onSnapshotCreateLogs:i=>console.log(`[daytona] ${i}`)});}}return e.workingDirectory&&await a.fs.createFolder(e.workingDirectory,"755"),new u(a)}async connect(e,t){let s=await this.client.get(e);return s.state!=="started"&&await s.start(),new u(s)}async list(e){let t=e?.limit??100;return (await this.client.list(e?.metadata,1,t)).items.map(n=>({sandboxId:n.id,image:n.snapshot||"unknown",name:n.name,metadata:n.labels||{},startedAt:new Date().toISOString()}))}};function A(r={}){let e=r.apiKey??process.env.DAYTONA_API_KEY;if(!e)throw new Error("Daytona API key required. Set DAYTONA_API_KEY environment variable or pass apiKey in config. Get your key at https://app.daytona.io/dashboard/keys");return new x({...r,apiKey:e})}
|
|
2
|
-
export{
|
|
1
|
+
import {STDOUT_PREFIX_BYTES,STDERR_PREFIX_BYTES,MAX_PREFIX_LEN,Daytona,Image}from'@daytonaio/sdk';import {resolve4}from'dns/promises';var F={"evolve-all":"evolvingmachines/evolve-all"},K=10,A=10,H=new Set([".xlsx",".xls",".docx",".doc",".pptx",".ppt",".pdf",".zip",".tar",".gz",".7z",".rar",".png",".jpg",".jpeg",".gif",".webp",".ico",".bmp",".mp3",".wav",".ogg",".flac",".aac",".mp4",".avi",".mov",".mkv",".webm",".woff",".woff2",".ttf",".otf",".eot",".exe",".dll",".so",".dylib",".sqlite",".db",".pickle",".pkl",".parquet"]);function j(s){let e=s.substring(s.lastIndexOf(".")).toLowerCase();return H.has(e)}function z(s){let e=s.lastIndexOf("/");return e>0?s.substring(0,e):"/"}function Y(s){let e=s.lastIndexOf("/");return e>=0?s.substring(e+1):s}var f=class extends Error{reason;destination;constructor(e,t,n){super(t),this.name="DaytonaNetworkPolicyError",this.reason=e,this.destination=n;}},b=class extends Error{image;constructor(e,t){let n=t instanceof Error?t.message:String(t??"unknown error");super(`Failed to pull image "${e}" on Daytona: ${n}. Private registry images (e.g. AWS ECR) require registry credentials pre-registered in the Daytona dashboard (Registries page) before sandbox creation \u2014 Daytona has no per-call image pull secret. Register the registry at https://app.daytona.io, then retry. Also ensure the image is linux/amd64 and pinned to a tag or digest (floating tags like "latest" are rejected by Daytona's snapshot builder).`),this.name="DaytonaImagePullError",this.image=e;}},h=class extends Error{snapshot;constructor(e){super(`Daytona snapshot "${e}" already exists and pins its cpu/memory/disk sizing \u2014 create-from-snapshot cannot resize it, so the requested \`resources\` cannot be enforced. Pre-build a snapshot with the desired sizing under its own name (sizing-addressed), or omit \`resources\` to accept the snapshot's pinned sizing.`),this.name="DaytonaResourcesError",this.snapshot=e;}};function q(s,e){if(!e||e<=0)return s;let t=Buffer.from(s).toString("base64"),n=`/tmp/.evolve-cmd-${Date.now()}-${Math.random().toString(36).slice(2,8)}.sh`;return `sh -c '${`echo ${t} | base64 -d > ${n}; if command -v timeout >/dev/null 2>&1; then timeout -k 10 ${e} bash ${n}; else bash ${n}; fi; rc=$?; rm -f ${n}; exit $rc`}'`}function x(s,e,t,n){let r=s;return e&&(r=`cd '${e.replace(/'/g,"'\\''")}' && ${r}`),t&&Object.keys(t).length>0&&(r=`${Object.entries(t).filter(([,o])=>o!=null).map(([o,a])=>`export ${o}='${String(a).replace(/'/g,"'\\''")}'`).join("; ")}; ${r}`),n==="root"&&(r=`echo ${Buffer.from(r).toString("base64")} | base64 -d | sudo -n bash`),r}var W=s=>resolve4(s);function U(s){let e=/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(?:\/(\d{1,3}))?$/.exec(s);return !(!e||[e[1],e[2],e[3],e[4]].some(t=>Number(t)>255)||e[5]!==void 0&&Number(e[5])>32)}function X(s){return /^\d{1,3}(\.\d{1,3}){3}(\/\d+)?$/.test(s)&&!U(s)}function G(s){return s.startsWith("[")?true:(s.match(/:/g)?.length??0)>=2}function V(s){return /^[^:]+:\d+$/.test(s)}async function M(s,e=W){if(!s||s.outbound==="open"){if(s?.allowedDestinations?.length)throw new Error("network.allowedDestinations is only valid when outbound is blocked");return s?{networkBlockAll:false}:{}}let t=s.allowedDestinations??[];if(t.length===0)return {networkBlockAll:true};let n=[],r=[];for(let o of t)if(U(o))n.push(o.includes("/")?o:`${o}/32`);else {if(X(o))throw new f("invalid-ipv4",`"${o}" is not a valid IPv4 address or CIDR (octets must be 0-255, prefix 0-32). Fix the address or list a hostname instead.`,o);if(G(o))throw new f("ipv6-unsupported",`Daytona's network allowlist supports IPv4 CIDRs only; cannot allow IPv6 destination "${o}".`,o);if(V(o))throw new f("port-unsupported",`Daytona's network allowlist filters hosts and IPs only and cannot match a port; drop the ":<port>" from "${o}" and list just the host or IP.`,o);if(o.includes("*"))throw new f("wildcard-hostname",`Daytona cannot enforce wildcard hostname "${o}": its allowlist is kernel-level IPv4 CIDR filtering with no DNS/domain layer. List concrete hostnames or IPv4 CIDRs instead.`,o);r.push(o);}for(let o of r){let a=[];try{a=await e(o);}catch(i){throw new f("unresolvable-hostname",`Cannot pin hostname "${o}" to stable IPv4 addresses at create time (${i instanceof Error?i.message:String(i)}). Daytona enforces IPv4 CIDRs only; use an IPv4 CIDR for this destination instead.`,o)}if(a.length===0)throw new f("unresolvable-hostname",`Hostname "${o}" resolved to no IPv4 addresses. Daytona enforces IPv4 CIDRs only; use an IPv4 CIDR for this destination instead.`,o);console.warn(`[daytona] Network allowlist: pinning "${o}" to [${a.join(", ")}] (DNS resolved at create time). Daytona enforces IPv4 CIDRs only \u2014 if this host rotates DNS (CDNs and cloud APIs often do), traffic to its new IPs will be BLOCKED for the sandbox's lifetime.`);for(let i of a)n.push(`${i}/32`);}let d=[...new Set(n)];if(d.length>A)throw new f("allowlist-too-large",`Daytona allows at most ${A} entries in its network allowlist; this policy resolved to ${d.length} CIDRs (${d.join(", ")}). Aggregate destinations into broader CIDRs or reduce the list.`);return {networkBlockAll:false,networkAllowList:d.join(",")}}function B(s){let e=s.indexOf("/");if(e<0)return;let t=s.substring(0,e);return t.includes(".")||t.includes(":")||t==="localhost"?t:void 0}function D(s){return {sandboxId:s.id,image:s.snapshot??"",name:s.name,metadata:s.labels??{},startedAt:s.createdAt??""}}function T(s){if(s==="started")return "running";if(s==="stopped"||s==="archived")return "paused"}var S=class extends Daytona{constructor(t,n){super(t);this.managedToolboxUrl=n;}async getProxyToolboxUrl(){return this.managedToolboxUrl}};function J(s,e){let t=new Uint8Array(new ArrayBuffer(s.length+e.length));return t.set(s,0),t.set(e,s.length),t}function $(s,e,t){e:for(let n=t;n+e.length<=s.length;n++){for(let r=0;r<e.length;r++)if(s[n+r]!==e[r])continue e;return n}return -1}function E(s,e){let t=new TextDecoder("utf-8"),n=new TextDecoder("utf-8"),r=new Uint8Array(0),d="stdout",o=a=>{a.length!==0&&(d==="stdout"?s(t.decode(a,{stream:true})):e(n.decode(a,{stream:true})));};return {push(a){for(r=J(r,a);;){let i=$(r,STDOUT_PREFIX_BYTES,0),c=$(r,STDERR_PREFIX_BYTES,0),l=-1,u="stdout",m=0;if(i!==-1&&(c===-1||i<c)?(l=i,u="stdout",m=STDOUT_PREFIX_BYTES.length):c!==-1&&(l=c,u="stderr",m=STDERR_PREFIX_BYTES.length),l===-1){let g=Math.max(0,r.length-(MAX_PREFIX_LEN-1));o(r.subarray(0,g)),r=r.slice(g);return}o(r.subarray(0,l)),d=u,r=r.slice(l+m);}},flush(){o(r),r=new Uint8Array(0);let a=t.decode();a&&s(a);let i=n.decode();i&&e(i);}}}async function L(s,e,t,n,r,d){let o=`${s.toolboxUrl.replace(/\/+$/,"")}/${encodeURIComponent(e)}/process/session/${encodeURIComponent(t)}/command/${encodeURIComponent(n)}/logs?follow=true`,a=await fetch(o,{headers:{Authorization:`Bearer ${s.apiKey}`,accept:"application/octet-stream"}});if(!a.ok)throw new Error(`Daytona managed log follow failed: ${a.status} ${await a.text()}`);if(!a.body)return;let i=E(r,d),c=a.body.getReader();try{for(;;){let{done:l,value:u}=await c.read();if(l)break;u&&i.push(u);}}finally{i.flush(),await c.cancel().catch(()=>{});}}function w(s){let e=s.stdout||"",t=s.stderr||"",n=s.output||"";return !e&&!t&&n?{stdout:n,stderr:""}:{stdout:e,stderr:t}}var v=class{constructor(e,t,n){this.sandbox=e;this.user=t;this.managedStream=n;}followLogs(e,t,n,r){return this.managedStream?L(this.managedStream,this.sandbox.id,e,t,n,r):this.sandbox.process.getSessionCommandLogs(e,t,n,r)}async run(e,t){let n=t?.timeoutMs?Math.floor(t.timeoutMs/1e3):void 0,r=`run-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;await this.sandbox.process.createSession(r);try{let d=await this.sandbox.process.executeSessionCommand(r,{command:x(e,t?.cwd,t?.envs,this.user),runAsync:!1},n),o=d.cmdId;o&&(t?.onStdout||t?.onStderr)&&await this.followLogs(r,o,t.onStdout||(()=>{}),t.onStderr||(()=>{}));let{stdout:a,stderr:i}=w(d);if(!a&&!i&&o&&!t?.onStdout)try{let c=await this.sandbox.process.getSessionCommandLogs(r,o),l=w(c);a=l.stdout,i=l.stderr||i;}catch{}return {exitCode:d.exitCode??0,stdout:a,stderr:i}}finally{try{await this.sandbox.process.deleteSession(r);}catch{}}}async spawn(e,t){let n=`evolve-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;await this.sandbox.process.createSession(n);let r=t?.timeoutMs?Math.floor(t.timeoutMs/1e3):void 0,d=Date.now(),o=t?.timeoutMs,i=(await this.sandbox.process.executeSessionCommand(n,{command:q(x(e,t?.cwd,t?.envs,this.user),r),runAsync:true},r)).cmdId;i&&(t?.onStdout||t?.onStderr)&&this.followLogs(n,i,t.onStdout||(()=>{}),t.onStderr||(()=>{})).catch(()=>{});let c=this.sandbox;return {processId:n,wait:async()=>{if(!i)return {exitCode:0,stdout:"",stderr:""};for(;;){if(o&&Date.now()-d>=o){try{await c.process.deleteSession(n);}catch{}return {exitCode:124,stdout:"",stderr:"operation timed out"}}try{let l=await c.process.getSessionCommand(n,i);if(l.exitCode!==void 0)try{let u=await c.process.getSessionCommandLogs(n,i);return {exitCode:l.exitCode,...w(u)}}catch{return {exitCode:l.exitCode,stdout:"",stderr:""}}}catch(l){let u=l instanceof Error?l.message.toLowerCase():String(l).toLowerCase();if(u.includes("not found")){let m=u.includes("sandbox");try{await c.refreshData(),m=!1;}catch(g){(g instanceof Error?g.message.toLowerCase():String(g).toLowerCase()).includes("not found")&&(m=true);}return {exitCode:-1,stdout:"",stderr:m?"sandbox deleted during run":"session terminated"}}throw l}await new Promise(l=>setTimeout(l,500));}},kill:async()=>{try{return await c.process.deleteSession(n),!0}catch{return false}}}}async list(){try{return (await this.sandbox.process.listSessions()).map(t=>({processId:t.sessionId||"",cmd:"",args:[],envs:{}}))}catch{return []}}async kill(e){try{return await this.sandbox.process.deleteSession(e),!0}catch{return false}}},P=class{constructor(e){this.sandbox=e;}async read(e){let t=await this.sandbox.fs.downloadFile(e);return j(e)?new Uint8Array(t):t.toString("utf-8")}async write(e,t){let n;if(typeof t=="string")n=Buffer.from(t,"utf-8");else if(Buffer.isBuffer(t))n=t;else if(t instanceof ArrayBuffer)n=Buffer.from(t);else if(t instanceof Uint8Array)n=Buffer.from(t);else throw new Error(`Unsupported content type: ${typeof t}`);await this.sandbox.fs.uploadFile(n,e);}async writeBatch(e){let t=e.map(n=>{let r;if(typeof n.data=="string")r=Buffer.from(n.data,"utf-8");else if(Buffer.isBuffer(n.data))r=n.data;else if(n.data instanceof ArrayBuffer)r=Buffer.from(n.data);else if(n.data instanceof Uint8Array)r=Buffer.from(n.data);else throw new Error(`Unsupported content type: ${typeof n.data}`);return {source:r,destination:n.path}});await this.sandbox.fs.uploadFiles(t);}async writeFromPath(e,t){await this.sandbox.fs.uploadFile(t,e);}async makeDir(e){await this.sandbox.fs.createFolder(e,"755");}async exists(e){try{let t=z(e),n=Y(e);return (await this.sandbox.fs.listFiles(t)).some(d=>d.name===n)}catch{return false}}async list(e){return (await this.sandbox.fs.listFiles(e)).map(n=>({name:n.name,path:e.endsWith("/")?`${e}${n.name}`:`${e}/${n.name}`,type:n.isDir?"dir":"file"}))}async remove(e){await this.sandbox.fs.deleteFile(e,true);}async rename(e,t){await this.sandbox.fs.moveFiles(e,t);}},y=class{constructor(e,t,n){this.sandbox=e;this.commands=new v(e,t,n),this.files=new P(e);}commands;files;get sandboxId(){return this.sandbox.id}async getHost(e){return (await this.sandbox.getPreviewLink(e)).url}async isRunning(){try{await this.sandbox.refreshData();}catch{return false}return this.sandbox.state==="started"}async getInfo(){return await this.sandbox.refreshData(),D(this.sandbox)}async kill(){await this.sandbox.delete();}async pause(){await this.sandbox.stop();}},I=class{providerType="daytona";name="Daytona";client;defaultTimeoutMs;snapshotName;sandboxUsers=new Map;managedStream;constructor(e){let t={apiKey:e.apiKey,apiUrl:e.apiUrl,target:e.target};this.client=e.managedToolboxUrl?new S(t,e.managedToolboxUrl):new Daytona(t),e.managedToolboxUrl&&(this.managedStream={toolboxUrl:e.managedToolboxUrl,apiKey:e.apiKey}),this.defaultTimeoutMs=e.defaultTimeoutMs??36e5,this.snapshotName=e.snapshotName??"evolve-all";}async create(e){let t=await M(e.network),n=e.user,r=n&&n!=="root"?n:void 0,d=e.timeoutMs??this.defaultTimeoutMs,o=Math.max(1,Math.ceil(d/6e4)),a=e.image||this.snapshotName,i={envVars:e.envs,labels:e.metadata,autoStopInterval:o,autoDeleteInterval:K,...r?{user:r}:{},...t},c,l=e.resources!==void 0&&(e.resources.cpu!==void 0||e.resources.memory!==void 0||e.resources.disk!==void 0);if(this.managedStream){if(l)throw new h(a);let u=await this.client.create({snapshot:a,...i},{timeout:600});return e.workingDirectory&&await u.fs.createFolder(e.workingDirectory,"755"),n&&this.sandboxUsers.set(u.id,n),new y(u,n,this.managedStream)}try{let u=await this.client.snapshot.get(a);if(u&&u.state==="active"){if(l)throw new h(a);console.log(`[daytona] Using cached snapshot: ${a}`),c=await this.client.create({snapshot:a,...i},{timeout:600});}else throw new Error("Snapshot not active")}catch(u){if(u instanceof h)throw u;let m=F[a]??a;console.log(`[daytona] Snapshot "${a}" not found, building from image: ${m}`),console.log("[daytona] First run will take a few minutes (this only happens once)...");try{await this.client.snapshot.create({name:a,image:Image.base(m),resources:{cpu:e.resources?.cpu??4,memory:e.resources?.memory??4,disk:e.resources?.disk??10}},{onLogs:g=>console.log(`[daytona] ${g}`)}),console.log(`[daytona] Snapshot "${a}" ready.`),c=await this.client.create({snapshot:a,...i},{timeout:600});}catch(g){console.warn(`[daytona] Snapshot creation failed, falling back to direct image: ${g instanceof Error?g.message:g}`);try{c=await this.client.create({image:m,...i,resources:{cpu:e.resources?.cpu??4,memory:e.resources?.memory??4,disk:e.resources?.disk??10}},{timeout:600,onSnapshotCreateLogs:p=>console.log(`[daytona] ${p}`)});}catch(p){throw B(m)?new b(m,p):p}}}return e.workingDirectory&&await c.fs.createFolder(e.workingDirectory,"755"),n&&this.sandboxUsers.set(c.id,n),new y(c,n,this.managedStream)}async connect(e,t){let n=await this.client.get(e);return n.state!=="started"&&await n.start(),new y(n,this.sandboxUsers.get(e),this.managedStream)}async list(e){let t=e?.limit??100,r=(await this.client.list(e?.metadata,1,t)).items;return e?.state&&(r=r.filter(d=>{let o=T(d.state);return o!==void 0&&e.state.includes(o)})),r.map(D)}};function ee(s={}){let e=s.apiKey??process.env.DAYTONA_API_KEY;if(!e)throw new Error("Daytona API key required. Set DAYTONA_API_KEY environment variable or pass apiKey in config. Get your key at https://app.daytona.io/dashboard/keys");return new I({...s,apiKey:e})}var te=x,ne=M,se=B,re=D,oe=T,ae=E,ie=L,de=w;
|
|
2
|
+
export{K as DAYTONA_AUTO_DELETE_GRACE_MINUTES,A as DAYTONA_MAX_NETWORK_ALLOWLIST,v as DaytonaCommands,P as DaytonaFiles,b as DaytonaImagePullError,f as DaytonaNetworkPolicyError,I as DaytonaProvider,h as DaytonaResourcesError,ae as _testCreateLogDemuxer,oe as _testDaytonaStateToEvolveState,ie as _testFollowManagedSessionLogs,se as _testImageRegistryHost,ne as _testMapNetworkPolicy,de as _testReadCommandStreams,re as _testToSandboxInfo,te as _testWrapCommand,ee as createDaytonaProvider};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evolvingmachines/daytona",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.52-project-sable.20260726.6e95f36",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"ai",
|
|
6
6
|
"agents",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"build": "tsup --minify",
|
|
34
34
|
"dev": "tsup --watch",
|
|
35
35
|
"type-check": "tsc --noEmit",
|
|
36
|
-
"test:unit": "tsx tests/unit/daytona-commands.test.ts"
|
|
36
|
+
"test:unit": "tsx tests/unit/daytona-commands.test.ts && tsx tests/unit/daytona-provider.test.ts && tsx tests/unit/daytona-managed.test.ts"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@daytonaio/sdk": "^0.134.0"
|