@opencoredev/sandbox-sdk 0.2.0 → 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/CHANGELOG.md +32 -11
  2. package/README.md +350 -25
  3. package/dist/ai.d.ts +5 -0
  4. package/dist/ai.js +1 -0
  5. package/dist/chunk-59t3rqja.js +2 -0
  6. package/dist/chunk-cwx6cc31.js +2 -0
  7. package/dist/chunk-exv1kf89.js +60 -0
  8. package/dist/chunk-gc8zdxyb.js +2 -0
  9. package/dist/chunk-hjzhc3qg.js +40 -0
  10. package/dist/chunk-md6fmprc.js +1 -0
  11. package/dist/chunk-t5vr9nd8.js +8 -0
  12. package/dist/chunk-v6y7eqxw.js +27 -0
  13. package/dist/chunk-yed8mvs7.js +2 -0
  14. package/dist/chunk-yrsb6hws.js +5 -0
  15. package/dist/chunk-z9rjzn28.js +2 -0
  16. package/dist/errors.d.ts +60 -0
  17. package/dist/files.d.ts +57 -0
  18. package/dist/git.d.ts +28 -0
  19. package/dist/handle.d.ts +4 -0
  20. package/dist/index.d.ts +17 -0
  21. package/dist/index.js +1 -0
  22. package/dist/keys.d.ts +5 -0
  23. package/dist/logs.d.ts +8 -0
  24. package/dist/mastra.d.ts +43 -0
  25. package/dist/mastra.js +1 -0
  26. package/dist/memory.d.ts +34 -0
  27. package/dist/memory.js +11 -0
  28. package/dist/openai.d.ts +39 -0
  29. package/dist/openai.js +1 -0
  30. package/dist/peer.d.ts +5 -0
  31. package/dist/processes.d.ts +71 -0
  32. package/dist/provider.d.ts +138 -0
  33. package/dist/provider.js +0 -0
  34. package/dist/providers/agentos/errors.d.ts +4 -0
  35. package/dist/providers/agentos/index.d.ts +11 -0
  36. package/dist/providers/agentos/index.js +2 -0
  37. package/dist/providers/boat/index.d.ts +23 -0
  38. package/dist/providers/boat/index.js +1 -0
  39. package/dist/providers/boat-api/errors.d.ts +6 -0
  40. package/dist/providers/boat-api/exec.d.ts +11 -0
  41. package/dist/providers/boat-api/index.d.ts +22 -0
  42. package/dist/providers/boxd/errors.d.ts +3 -0
  43. package/dist/providers/boxd/execute.d.ts +3 -0
  44. package/dist/providers/boxd/guest.d.ts +14 -0
  45. package/dist/providers/boxd/index.d.ts +23 -0
  46. package/dist/providers/boxd/index.js +85 -0
  47. package/dist/providers/boxd/transfers.d.ts +19 -0
  48. package/dist/providers/daytona/buffered.d.ts +3 -0
  49. package/dist/providers/daytona/errors.d.ts +3 -0
  50. package/dist/providers/daytona/exec.d.ts +14 -0
  51. package/dist/providers/daytona/index.d.ts +24 -0
  52. package/dist/providers/daytona/index.js +88 -0
  53. package/dist/providers/e2b/errors.d.ts +3 -0
  54. package/dist/providers/e2b/index.d.ts +15 -0
  55. package/dist/providers/e2b/index.js +1 -0
  56. package/dist/providers/freestyle/deadline.d.ts +4 -0
  57. package/dist/providers/freestyle/errors.d.ts +16 -0
  58. package/dist/providers/freestyle/execute.d.ts +12 -0
  59. package/dist/providers/freestyle/index.d.ts +14 -0
  60. package/dist/providers/freestyle/index.js +133 -0
  61. package/dist/providers/freestyle/wire.d.ts +10 -0
  62. package/dist/providers/local/docker.d.ts +32 -0
  63. package/dist/providers/local/index.d.ts +17 -0
  64. package/dist/providers/local/index.js +2 -0
  65. package/dist/providers/railway/errors.d.ts +4 -0
  66. package/dist/providers/railway/index.d.ts +24 -0
  67. package/dist/providers/railway/index.js +1 -0
  68. package/dist/providers/sandboxer/index.d.ts +33 -0
  69. package/dist/providers/sandboxer/index.js +1 -0
  70. package/dist/providers/superserve/bootstrap.d.ts +11 -0
  71. package/dist/providers/superserve/errors.d.ts +3 -0
  72. package/dist/providers/superserve/execute.d.ts +13 -0
  73. package/dist/providers/superserve/index.d.ts +23 -0
  74. package/dist/providers/superserve/index.js +126 -0
  75. package/dist/providers/tenki/deadline.d.ts +2 -0
  76. package/dist/providers/tenki/errors.d.ts +10 -0
  77. package/dist/providers/tenki/exec.d.ts +4 -0
  78. package/dist/providers/tenki/files.d.ts +3 -0
  79. package/dist/providers/tenki/index.d.ts +28 -0
  80. package/dist/providers/tenki/index.js +172 -0
  81. package/dist/providers/tenki/ready.d.ts +7 -0
  82. package/dist/providers/tenki/stream.d.ts +3 -0
  83. package/dist/providers/upstash/buffered.d.ts +3 -0
  84. package/dist/providers/upstash/errors.d.ts +3 -0
  85. package/dist/providers/upstash/exec.d.ts +18 -0
  86. package/dist/providers/upstash/files.d.ts +15 -0
  87. package/dist/providers/upstash/index.d.ts +38 -0
  88. package/dist/providers/upstash/index.js +111 -0
  89. package/dist/providers/vercel/errors.d.ts +3 -0
  90. package/dist/providers/vercel/index.d.ts +14 -0
  91. package/dist/providers/vercel/index.js +1 -0
  92. package/dist/providers/vercel/provider.d.ts +10 -0
  93. package/dist/providers/vercel/transport.d.ts +40 -0
  94. package/dist/run.d.ts +41 -0
  95. package/dist/testing.d.ts +17 -0
  96. package/dist/testing.js +6 -0
  97. package/dist/tool-security.d.ts +9 -0
  98. package/dist/tools.d.ts +117 -0
  99. package/dist/tools.js +1 -0
  100. package/dist/types.d.ts +189 -0
  101. package/package.json +164 -106
  102. package/dist/agentos-CbrZW7R0.mjs +0 -390
  103. package/dist/agentos-CbrZW7R0.mjs.map +0 -1
  104. package/dist/ai/harness.d.mts +0 -16
  105. package/dist/ai/harness.mjs +0 -69
  106. package/dist/ai/harness.mjs.map +0 -1
  107. package/dist/ai/index.d.mts +0 -54
  108. package/dist/ai/index.mjs +0 -4
  109. package/dist/ai-DeFa8yPk.mjs +0 -243
  110. package/dist/ai-DeFa8yPk.mjs.map +0 -1
  111. package/dist/capabilities-BWvOVFbH.d.mts +0 -15
  112. package/dist/capabilities-CaSNd6J1.mjs +0 -125
  113. package/dist/capabilities-CaSNd6J1.mjs.map +0 -1
  114. package/dist/capabilities-xs9kaHrK.mjs +0 -28
  115. package/dist/capabilities-xs9kaHrK.mjs.map +0 -1
  116. package/dist/errors-BuF9bYOQ.mjs +0 -68
  117. package/dist/errors-BuF9bYOQ.mjs.map +0 -1
  118. package/dist/eve/index.d.mts +0 -19
  119. package/dist/eve/index.mjs +0 -155
  120. package/dist/eve/index.mjs.map +0 -1
  121. package/dist/index-CkTwqD0X.d.mts +0 -25
  122. package/dist/index-Dh9xob2s.d.mts +0 -31
  123. package/dist/index.d.mts +0 -5
  124. package/dist/index.mjs +0 -40
  125. package/dist/index.mjs.map +0 -1
  126. package/dist/managed-provider-BsZGVQ4I.mjs +0 -148
  127. package/dist/managed-provider-BsZGVQ4I.mjs.map +0 -1
  128. package/dist/mastra/index.d.mts +0 -118
  129. package/dist/mastra/index.mjs +0 -662
  130. package/dist/mastra/index.mjs.map +0 -1
  131. package/dist/metadata.d.mts +0 -34
  132. package/dist/metadata.mjs +0 -135
  133. package/dist/metadata.mjs.map +0 -1
  134. package/dist/provider-DOBErkBO.d.mts +0 -91
  135. package/dist/provider-utils-CfB9nvJx.mjs +0 -57
  136. package/dist/provider-utils-CfB9nvJx.mjs.map +0 -1
  137. package/dist/providers/agentos/index.d.mts +0 -7
  138. package/dist/providers/agentos/index.mjs +0 -9
  139. package/dist/providers/box/index.d.mts +0 -34
  140. package/dist/providers/box/index.mjs +0 -348
  141. package/dist/providers/box/index.mjs.map +0 -1
  142. package/dist/providers/daytona/index.d.mts +0 -21
  143. package/dist/providers/daytona/index.mjs +0 -206
  144. package/dist/providers/daytona/index.mjs.map +0 -1
  145. package/dist/providers/e2b/index.d.mts +0 -17
  146. package/dist/providers/e2b/index.mjs +0 -169
  147. package/dist/providers/e2b/index.mjs.map +0 -1
  148. package/dist/providers/local/index.d.mts +0 -22
  149. package/dist/providers/local/index.mjs +0 -22
  150. package/dist/providers/local/index.mjs.map +0 -1
  151. package/dist/providers/railway/index.d.mts +0 -29
  152. package/dist/providers/railway/index.mjs +0 -211
  153. package/dist/providers/railway/index.mjs.map +0 -1
  154. package/dist/providers/upstash/index.d.mts +0 -17
  155. package/dist/providers/upstash/index.mjs +0 -211
  156. package/dist/providers/upstash/index.mjs.map +0 -1
  157. package/dist/providers/vercel/index.d.mts +0 -28
  158. package/dist/providers/vercel/index.mjs +0 -186
  159. package/dist/providers/vercel/index.mjs.map +0 -1
  160. package/dist/sandbox-DdEqoXJF.d.mts +0 -26
  161. package/dist/sandbox-DgtY5hvt.mjs +0 -112
  162. package/dist/sandbox-DgtY5hvt.mjs.map +0 -1
  163. package/dist/testing/index.d.mts +0 -27
  164. package/dist/testing/index.mjs +0 -173
  165. package/dist/testing/index.mjs.map +0 -1
  166. package/dist/types-Bv6I2U1c.d.mts +0 -103
@@ -0,0 +1,111 @@
1
+ import{s,t}from"../../chunk-t5vr9nd8.js";import{d}from"../../chunk-exv1kf89.js";import{o}from"../../chunk-yed8mvs7.js";import{e,l}from"../../chunk-gc8zdxyb.js";import{posix as Q}from"node:path";var oe=`import os,select,_signal as signal,sys,time,fcntl
2
+ ${d}
3
+ command,directory,cwd,limit,activity=sys.argv[1:]
4
+ if activity:
5
+ os.makedirs(os.path.dirname(activity),exist_ok=True)
6
+ os.mkdir(activity,mode=0o700)
7
+ os.makedirs(directory,mode=0o700,exist_ok=True)
8
+ cancel=directory+"/cancel"
9
+ with open(directory+"/lock","w") as lock:
10
+ fcntl.flock(lock,fcntl.LOCK_EX)
11
+ if os.path.exists(cancel):
12
+ print("R:0:1:1")
13
+ sys.exit(0)
14
+ try:
15
+ with stdin_file() as f: p=Process(dict(command=command,cwd=cwd),dict(os.environ,PWD=cwd),f,True)
16
+ except OSError as error:
17
+ print("E:"+str(error).encode().hex())
18
+ print("R:2:0:0")
19
+ os.unlink(directory+"/lock"); os.rmdir(directory)
20
+ sys.exit(0)
21
+ with open(directory+"/pid","w") as f: f.write(str(p.pid))
22
+ streams={p.stdout.fileno():"O",p.stderr.fileno():"E"}
23
+ deadline=time.monotonic()+float(limit)
24
+ timedout=False
25
+ size=0
26
+ spill=None
27
+ try:
28
+ while streams or p.poll() is None:
29
+ if not timedout and (time.monotonic()>=deadline or os.path.exists(cancel)):
30
+ timedout=True
31
+ try: os.killpg(p.pid,signal.SIGKILL)
32
+ except ProcessLookupError: pass
33
+ for fd in select.select(list(streams),[],[],0.02)[0]:
34
+ data=os.read(fd,4096)
35
+ if not data: del streams[fd]; continue
36
+ frame=streams[fd]+":"+data.hex()+"\\n"
37
+ size+=len(frame)
38
+ if size>65536:
39
+ if spill is None: spill=open(directory+"/output","w")
40
+ spill.write(frame)
41
+ else: sys.stdout.write(frame)
42
+ code=p.wait()
43
+ print("R:%d:%d:%d"%(code if code>=0 else 128-code,timedout,spill is not None))
44
+ except BaseException:
45
+ try: os.killpg(p.pid,signal.SIGKILL)
46
+ except ProcessLookupError: pass
47
+ p.wait()
48
+ raise
49
+ finally:
50
+ if activity:
51
+ try: os.rmdir(activity)
52
+ except FileNotFoundError: pass
53
+ if spill is not None: spill.close()
54
+ else:
55
+ for name in ('pid','lock','cancel'):
56
+ try: os.unlink(directory+'/'+name)
57
+ except FileNotFoundError: pass
58
+ os.rmdir(directory)
59
+ `,re=`import os,signal,sys,fcntl,time
60
+ p=sys.argv[1]
61
+ pid=None
62
+ for attempt in range(3):
63
+ try:
64
+ os.makedirs(p,mode=0o700,exist_ok=True)
65
+ with open(p+"/lock","w") as lock:
66
+ fcntl.flock(lock,fcntl.LOCK_EX)
67
+ open(p+"/cancel","w").close()
68
+ try:
69
+ with open(p+"/pid") as f: pid=int(f.read())
70
+ os.killpg(pid,signal.SIGKILL)
71
+ except (FileNotFoundError,ProcessLookupError): pass
72
+ break
73
+ except FileNotFoundError:
74
+ if attempt==2: raise
75
+ if pid:
76
+ end=time.monotonic()+2
77
+ while True:
78
+ try: os.killpg(pid,0)
79
+ except ProcessLookupError: break
80
+ if time.monotonic()>=end: sys.exit(1)
81
+ time.sleep(.01)
82
+ `;async function H(n,i,x){if(i.signal?.aborted)return{exitCode:null,timedOut:!0};let g=`/tmp/.sandbox-sdk/exec/sdk-${crypto.randomUUID()}`,c=Object.entries(s).filter(([r])=>(r in i.env)).map(([r,a])=>t(`${r}=${a}`)).join(" "),f=`${x?`${x} && `:""}env ${c} python3 -S -u -c ${t(oe)} ${t(i.command)} ${t(g)} ${t(i.cwd)} ${t(String(i.timeoutMs/1000))} ''`;if(Buffer.byteLength(f)>=98304)return;let w=Date.now()+i.timeoutMs,B=!1,_=!1,b=!1,I=()=>{},C=new Promise((r)=>{I=()=>r(void 0)}),T,S,y=()=>new e("Command output is incomplete. Upstash did not confirm exact output and exit. Inspect the box before retrying.",{code:"outcome_unknown",provider:"upstash",sandboxId:n.id}),k=()=>{if(B=!0,I(),_)return;T??=n.exec.command(`python3 -c ${t(re)} ${t(g)}`).then((r)=>{if(r.exitCode!==0)throw y()}).catch((r)=>{S=r})},A={stdout:new TextDecoder("utf-8",{ignoreBOM:!0}),stderr:new TextDecoder("utf-8",{ignoreBOM:!0})},E=(r)=>{let a=/^(O|E):([0-9a-f]*)$/.exec(r);if(!a||r.length>8194||Buffer.from(a[2]??"","hex").toString("hex")!==a[2])throw y();let u=a[1]==="O"?"stdout":"stderr",h=A[u].decode(Buffer.from(a[2]??"","hex"),{stream:!0});if(h)i.onChunk({stream:u,text:h})};i.signal?.addEventListener("abort",k,{once:!0});let O=setTimeout(k,Math.max(0,w-Date.now()));try{let r=await n.exec.command(f);if(r.exitCode===127&&r.stderr.includes("python3")&&!B)return;let a=r.stdout.trimEnd().split(`
83
+ `),u=/^R:(\d+):([01]):([01])$/.exec(a.pop()??"");if(r.exitCode!==0||!u)throw y();_=!0,b=u[3]==="1";for(let p of a)if(p)E(p);if(b&&!B){let p=0,m="";while(!B){let v=await Promise.race([n.files.read(`${g}/output`,{offset:p,length:65536,encoding:"base64"}),C]);if(v===void 0)break;let U=Buffer.from(v,"base64");if(B)break;p+=U.length,m+=U.toString("ascii");let P;while((P=m.indexOf(`
84
+ `))>=0)E(m.slice(0,P)),m=m.slice(P+1);if(m.length>8194)throw y();if(U.length<65536){if(m)throw y();break}}}for(let p of["stdout","stderr"]){let m=A[p].decode();if(m)i.onChunk({stream:p,text:m})}if(await T,S!==void 0)throw y();let h=u[2]==="1"||B;return{exitCode:h?null:Number(u[1]),timedOut:h}}catch(r){if(!_){if(k(),await T,S!==void 0)throw y()}throw r}finally{if(clearTimeout(O),i.signal?.removeEventListener("abort",k),await T,_&&(T||b))await n.files.remove(g,{recursive:!0}).catch(()=>{})}}function X(n,i,x=0){let g=n instanceof Error?`${n.name}: ${n.message}`:"Native request failed",c=i(g).replace(/https?:\/\/[^\s"'<>]+/gi,"[URL REDACTED]").replace(/(\bauthorization["']?\s*[:=]\s*)(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[^\r\n;]+)/gi,"$1[REDACTED]").replace(/\bBearer\s+[^\s"',;]+/gi,"Bearer [REDACTED]").replace(/((?:["']?(?:authorization|api[-_]?key|token|password|secret)["']?)\s*[:=]\s*)(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[^\s,;]+)/gi,"$1[REDACTED]").replace(/\p{Cc}+/gu," ").slice(0,1000);return n instanceof Error&&n.cause!==void 0&&x<2?`${c}; caused by ${X(n.cause,i,x+1)}`:c}function D(n,i,x=!1,g=x?"files":"request",c=(f)=>{let w=new l;return w.add([process.env.UPSTASH_BOX_API_KEY??""]),w.text(f)}){if(n instanceof e)return n;let f=n&&typeof n==="object"&&"statusCode"in n?n.statusCode:void 0,w="provider";if(f===401||f===403)w="auth";else if(f===404)w=x?"file_not_found":"not_found";else if(f===410)w="not_found";else if(f===429||f===402)w="quota";else if(f===408||f===504)w="timeout";else if(typeof f==="number"&&f>=500)w="unavailable";else if(f===400||f===422)w="invalid_argument";let B=`${g}${typeof f==="number"?` HTTP ${f}`:""}: ${X(n,c)}`;return new e(`Upstash Box ${g} failed (${w}${typeof f==="number"?`, HTTP ${f}`:""}). The provider could not complete the operation. Check credentials, limits, and sandbox state.`,{code:w,provider:"upstash",sandboxId:i,retryable:["quota","timeout","unavailable"].includes(w),cause:B})}function z(n){throw new e(`${n}. Upstash Box cannot enforce this request. Choose supported settings.`,{code:"unsupported",provider:"upstash"})}import{StringDecoder as Z}from"node:string_decoder";async function G(n,i,x,g){if(i.signal?.aborted)return{exitCode:null,timedOut:!0};let c,f=!1,w=()=>{f=!0,c?.kill("KILL")},B=setTimeout(w,i.timeoutMs);i.signal?.addEventListener("abort",w,{once:!0});let _=`sdk-prepared-${crypto.randomUUID()}
85
+ `,b="",I=x!==void 0,C=new Z("utf8"),T=new Z("utf8");try{if(c=await n.exec.session({argv:x?["sh","-c",`${x} && printf '%s
86
+ ' '${_.trimEnd()}' && cd "$1" && exec sh -c "$2"`,"sh",i.cwd,i.command]:["sh","-c",i.command],cwd:x?"/":i.cwd,env:Object.entries(i.env).map(([A,E])=>`${A}=${E}`),onStdout:(A)=>{let E=C.write(Buffer.from(A));if(I){b+=E;let O=b.indexOf(_);if(O>=0)E=b.slice(0,O)+b.slice(O+_.length),b="",I=!1,g?.();else{let r=Math.max(0,b.length-_.length+1);E=b.slice(0,r),b=b.slice(r)}}if(E)i.onChunk({stream:"stdout",text:E})},onStderr:(A)=>{let E=T.write(Buffer.from(A));if(E)i.onChunk({stream:"stderr",text:E})}}),f||i.signal?.aborted)w();else{if(i.stdin!==void 0)c.write(i.stdin);c.endStdin()}let S=await c.wait();if(S<0&&!f)throw new e("Command session disconnected. Upstash could not confirm an exit code. Inspect the box before retrying.",{code:"unavailable",provider:"upstash",sandboxId:n.id});let y=b+C.end();if(y)i.onChunk({stream:"stdout",text:y});let k=T.end();if(k)i.onChunk({stream:"stderr",text:k});return{exitCode:f?null:S,timedOut:f}}catch(S){throw c?.close(),S}finally{clearTimeout(B),i.signal?.removeEventListener("abort",w),c?.close()}}async function J(n,i,x,g){let c=(y)=>`'${y.replaceAll("'","'\\''")}'`,f=`/tmp/.sandbox-sdk/upstash-runs/${crypto.randomUUID()}`,w=`${f}/env`,B=`${g?`${g} && `:""}chmod 600 ${c(w)} && . ${c(w)} && /bin/rm -f -- ${c(w)} || exit 1; echo $$ > ${c(`${f}/pid`)}; cd ${c(i.cwd)} || exit 1; sh -c ${c(i.command)}; code=$?; printf '%s' "$code" > ${c(`${f}/exit`)}; exit "$code"`,_;try{if((await n.exec.command(`umask 077; mkdir -p /tmp/.sandbox-sdk/upstash-runs && mkdir ${c(f)} && : > ${c(w)}`)).exitCode!==0)throw Error("Durable process environment setup failed");if(await n.files.write({path:w,content:Object.entries(i.env).map(([A,E])=>`export ${c(`${A}=${E}`)}`).join(`
87
+ `)+`
88
+ `}),(await n.exec.command(`nohup setsid sh -c ${c(B)} > /dev/null 2>&1 < /dev/null &`)).exitCode!==0)throw Error("Durable process launch failed");for(let A=0;A<200;A++){try{let E=Number(await n.files.read(`${f}/pid`));if(Number.isSafeInteger(E)&&E>1){_=E;break}}catch(E){if(D(E,n.id,!0).code!=="file_not_found")throw E}await new Promise((E)=>setTimeout(E,25))}if(_===void 0)throw Error("Durable process PID missing")}catch(y){throw await n.files.remove(w).catch(()=>{}),await x(f),y}let b=_,I=!1,C=(async()=>{while(!0){if(I)await new Promise(()=>{});try{let y=Number(await n.files.read(`${f}/exit`));if(!Number.isInteger(y)||y<0||y>255)throw Error("Invalid durable exit code");return{exitCode:y,signal:null}}catch(y){let k=D(y,n.id,!0).code;if(k!=="file_not_found"&&k!=="unavailable"&&k!=="quota"&&k!=="timeout")throw y}await new Promise((y)=>setTimeout(y,50))}})();C.catch(()=>{});let T=async(y)=>{if((await n.exec.command(`kill -${y} -${b} 2>/dev/null || ! kill -0 -${b} 2>/dev/null`)).exitCode!==0)throw new e("Process termination is unconfirmed. The remote process group could not be signalled. Destroy the box before retrying.",{code:"outcome_unknown",provider:"upstash",sandboxId:n.id})},S=!1;return{pid:b,wait:()=>C,detach(){I=!0},async stop({graceMs:y}){if(S)return;await T("TERM"),await new Promise((k)=>setTimeout(k,y)),await T("KILL");try{await n.files.read(`${f}/exit`)}catch(k){if(D(k,n.id,!0).code!=="file_not_found")throw k;await n.files.write({path:`${f}/exit`,content:"137"})}await C,S=!0}}}import{posix as ne}from"node:path";var j=4194304,L=(n)=>`'${n.replaceAll("'","'\\''")}'`;async function K(n,i,x){if(!x)try{return new Uint8Array(Buffer.from(await n.files.read(i,{encoding:"base64"}),"base64"))}catch(w){if(["auth","file_not_found","not_found","quota"].includes(D(w,n.id,!0).code))throw w}let g=[],c=x?.offset??0,f=x?.length??1/0;while(f>0){let w=Math.min(j,f),B=Buffer.from(await n.files.read(i,{offset:c,length:w,encoding:"base64"}),"base64");if(g.push(B),c+=B.length,f-=B.length,B.length<w)break}return new Uint8Array(Buffer.concat(g))}async function Y(n,i,x,g){if(g){let b=await n.exec.session({argv:["sh","-c",`${g} && exec python3 -c "$1" "$2"`,"sh","import os,sys; p=sys.argv[1]; os.makedirs(os.path.dirname(p),exist_ok=True); f=open(p,'wb'); f.write(sys.stdin.buffer.read()); f.close()",i],cwd:"/"});try{if(b.write(x),b.endStdin(),await b.wait()!==0)throw new e("File write failed. Check workspace permissions and Python availability.",{code:"provider",provider:"upstash",sandboxId:n.id});return}finally{b.close()}}let c=async(_,b)=>{let I={path:_,content:Buffer.from(b).toString("base64"),encoding:"base64"};try{await n.files.write(I)}catch(C){if(D(C,n.id,!0).code!=="file_not_found")throw C;await n.files.mkdir(ne.dirname(_),{parents:!0}),await n.files.write(I)}};if(x.length<=j)return c(i,x);let f=`/tmp/.sandbox-sdk/upload-${crypto.randomUUID()}`;if((await n.exec.command(`umask 077; mkdir -p /tmp/.sandbox-sdk && mkdir ${L(f)}`)).exitCode!==0)throw new e("Private upload staging could not be created. Check guest permissions.",{code:"provider",provider:"upstash",sandboxId:n.id});let B=!1;try{let _=0;for(let C=0;C<x.length;C+=j)await c(`${f}/${_++}`,x.subarray(C,C+j));let b=`import os,sys,shutil,tempfile,stat
89
+ p,d,n=sys.argv[1:]; p=os.path.realpath(p); tmp=None
90
+ try:
91
+ os.makedirs(os.path.dirname(p),exist_ok=True)
92
+ mode=stat.S_IMODE(os.stat(p).st_mode) if os.path.exists(p) else 0o644
93
+ with tempfile.NamedTemporaryFile(dir=os.path.dirname(p),prefix='.sandbox-upload-',delete=False) as out:
94
+ tmp=out.name; os.fchmod(out.fileno(),mode)
95
+ for i in range(int(n)):
96
+ with open(d+'/'+str(i),'rb') as part: shutil.copyfileobj(part,out)
97
+ out.flush(); os.fsync(out.fileno())
98
+ os.replace(tmp,p); tmp=None
99
+ finally:
100
+ if tmp is not None: os.unlink(tmp)
101
+ shutil.rmtree(d,ignore_errors=True)`;B=!0;let I;try{I=await n.exec.command(`python3 -c ${L(b)} ${L(i)} ${L(f)} ${_}`)}catch{throw new e(`Large upload outcome is unknown. Inspect ${f} and the destination before retrying; destroy the sandbox to remove any retained staging.`,{code:"outcome_unknown",provider:"upstash",sandboxId:n.id})}if(I.exitCode!==0)throw await n.files.remove(f,{recursive:!0}).catch(()=>{}),new e("Large file assembly failed. Check guest Python and destination permissions.",{code:"provider",provider:"upstash",sandboxId:n.id})}finally{if(!B)await n.files.remove(f,{recursive:!0})}}var ie=`import os,sys,json,stat
102
+ p=sys.argv[1]
103
+ try: names=os.listdir(p)
104
+ except (FileNotFoundError,NotADirectoryError): names=[]
105
+ out=[]
106
+ for n in names:
107
+ try: s=os.lstat(os.path.join(p,n))
108
+ except FileNotFoundError: print('SDK_FILE_NOT_FOUND'); sys.exit(0)
109
+ m=s.st_mode
110
+ out.append([n.encode('utf-8','surrogateescape').decode('utf-8','replace'),'symlink' if stat.S_ISLNK(m) else 'directory' if stat.S_ISDIR(m) else 'file' if stat.S_ISREG(m) else 'other',s.st_size,s.st_mtime_ns//1000000])
111
+ print(json.dumps(out))`;function ae(n){if(!Array.isArray(n))return;let i=[];for(let x of n){if(!Array.isArray(x)||x.length!==4)return;let[g,c,f,w]=x;if(typeof g!=="string"||c!=="file"&&c!=="directory"&&c!=="symlink"&&c!=="other"||typeof f!=="number"||typeof w!=="number")return;i.push({name:g,type:c,size:f,modifiedAt:w})}return i}async function W(n,i,x){if(!i.startsWith("/"))return;let g=await n.exec.command(`${x?`${x} && `:""}python3 -c ${L(ie)} ${L(i)}`);if(g.exitCode!==0)return;let c=g.stdout.trim();if(c==="SDK_FILE_NOT_FOUND")throw Object.assign(Error("Directory entry vanished during listing"),{statusCode:404});try{return ae(JSON.parse(c))}catch{return}}var V=259200,F="sandbox-sdk",M="SANDBOX_SDK_UPSTASH_METADATA",N='if ! mkdir -p /workspace 2>/dev/null || [ ! -w /workspace ]; then sudo -n install -d -o "$(id -u)" -g "$(id -g)" -m 755 /workspace; fi && test -w /workspace',de="/tmp/.sandbox-sdk/upstash.json",q='for d in /tmp/.sandbox-sdk/procs/*; do [ -f "$d/spec.json" ] && [ ! -f "$d/exit" ] && printf 137 > "$d/exit"; done; rm -rf /tmp/.sandbox-sdk/upstash-runs; true';function ce(n){let i;try{i=JSON.parse(n)}catch{throw new e("Invalid SDK metadata",{code:"not_found",provider:"upstash"})}if(!i||typeof i!=="object"||!("version"in i)||i.version!==1||!("labels"in i)||!i.labels||typeof i.labels!=="object"||Array.isArray(i.labels)||"key"in i&&typeof i.key!=="string")throw new e("Invalid SDK metadata",{code:"not_found",provider:"upstash"});let x={};for(let[g,c]of Object.entries(i.labels)){if(typeof c!=="string")throw new e("Invalid SDK label",{code:"not_found",provider:"upstash"});Object.defineProperty(x,g,{value:c,enumerable:!0,writable:!0,configurable:!0})}return{version:1,labels:x,..."key"in i&&typeof i.key==="string"?{key:i.key}:{}}}function ee(n,i){return{id:n.id,key:i.key,labels:i.labels,createdAt:n.created_at*1000,state:n.status==="paused"?"paused":n.status==="running"||n.status==="idle"?"running":"stopped"}}var R=(n)=>("ephemeral"in n)&&n.ephemeral===!0;function te(n,i){if(i.type==="other")z("Special filesystem entries are not supported");return{path:n,type:i.type,size:i.size,modifiedAt:Date.parse(i.mod_time)}}function le(n){if(typeof n!=="object")return{mode:n==="none"?"deny-all":"allow-all"};if(!n.allow.length)return{mode:"deny-all"};if(n.allow.some((i)=>i.length>253||!/^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)*[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(i)))z("Network allow entries must be DNS hostnames");return{mode:"custom",allowedDomains:n.allow}}function Ae(n={}){let i=n.ephemeral?n.ttlSeconds??3600:void 0;if(i!==void 0&&(!Number.isSafeInteger(i)||i<1||i>V))throw new e(`Ephemeral ttlSeconds must be a whole number from 1 to ${V}.`,{code:"invalid_argument",provider:"upstash"});let x=new l,g=new Map,c=new Set,f=new Map,w=new Map,B,_=new Map,b=new Map,I=(r)=>{for(let a of _.get(r)??[])a();_.delete(r)},C=()=>{let r=n.apiKey??process.env.UPSTASH_BOX_API_KEY;if(!r)throw new e("Upstash Box API key is missing. Authentication is required. Set UPSTASH_BOX_API_KEY or pass apiKey.",{code:"auth",provider:"upstash"});return x.add([r]),{apiKey:r,baseUrl:n.baseUrl,enableTelemetry:n.enableTelemetry}},T=async()=>{let r=C(),{Box:a,EphemeralBox:u}=await(B??=o("@upstash/box","upstash",()=>import("@upstash/box")));return{Box:a,EphemeralBox:u,config:r}};async function S(r,a,u=!1,h="request"){try{return await r()}catch(p){throw D(p,a,u,h,(m)=>x.text(m))}}async function y(r){let a=f.get(r);if(a){let v=a();return g.set(r,v),f.delete(r),v}let u=g.get(r);if(u)return u;let{Box:h,config:p}=await T(),m=await h.get(r,{...p,timeout:n.requestTimeoutMs});return g.set(r,m),c.add(r),m}let k=(r,a,u)=>S(async()=>u(await y(r)),r,!0,`files.${a}`),A=async(r)=>{let a=await r.exec.command(`if [ -n "$${M}" ]; then printf %s "$${M}"; else cat ${t(de)}; fi`);if(a.exitCode!==0)throw new e("SDK metadata was not found",{code:"not_found",provider:"upstash",sandboxId:r.id});return ce(a.stdout)};async function E(r){let a=C(),u=(a.baseUrl??process.env.UPSTASH_BOX_BASE_URL??"https://us-east-1.box.upstash.com").replace(/\/$/,""),h=await fetch(`${u}/v2/box/snapshots`,{headers:{Authorization:`Bearer ${a.apiKey}`},signal:AbortSignal.timeout(n.requestTimeoutMs??30000)});if(!h.ok)throw Object.assign(Error("Snapshot metadata lookup failed"),{statusCode:h.status});let p=await h.json();if(!p||typeof p!=="object"||!("snapshots"in p)||p.snapshots!==null&&!Array.isArray(p.snapshots))throw new e("Invalid snapshot metadata response. Upstash did not return a snapshot list. Retry when the service is available.",{code:"provider",provider:"upstash"});for(let m of p.snapshots??[]){let v=m;if(!v||typeof v!=="object"||!("id"in v)||v.id!==r)continue;if("name"in v&&typeof v.name==="string"){let U=/^sdk-v1-(small|medium|large)-/.exec(v.name)?.[1];if(U==="small"||U==="medium"||U==="large")return U}if("box_id"in v&&typeof v.box_id==="string")try{return(await y(v.box_id)).size}catch(U){if(D(U).code!=="not_found")throw U}z("Snapshot size is unknown because its source box is unavailable; pass an explicit size")}throw new e("Snapshot was not found. It may have been deleted. Check the snapshot ID and account.",{code:"not_found",provider:"upstash"})}let O={name:"upstash",keyMode:"best-effort",capabilities:new Set(i===void 0?["pause","snapshot","size","network-policy"]:["snapshot","size","network-policy"]),create:(r)=>S(async()=>{if(r.idleTimeoutMs!==void 0||r.timeoutMs!==void 0)z("Custom idle deadlines and hard lifetimes are not available on durable boxes");if(r.size!==void 0&&typeof r.size!=="string")z("Use small, medium, or large; exact CPU, memory, and disk sizes are unavailable");if(r.env&&M in r.env)throw new e(`${M} is reserved for SDK discovery metadata. Choose another environment variable name.`,{code:"invalid_argument",provider:"upstash"});x.add(Object.values(r.env??{}));let a=le(r.network),{Box:u,EphemeralBox:h,config:p}=await T(),m={version:1,key:r.key,labels:{...r.labels,"sandbox-sdk":"1"}},v={...p,runtime:n.runtime,size:r.size??(r.from===void 0?void 0:await E(r.from)),env:{...r.env,[M]:JSON.stringify(m)},labels:[F],networkPolicy:a,timeout:n.requestTimeoutMs},U=Date.now(),P=i===void 0?r.from===void 0?await u.create({...v,initCommand:q}):await u.fromSnapshot(r.from,{...v,initCommand:q}):r.from===void 0?await h.create({...v,ttl:i}):await h.fromSnapshot(r.from,{...v,ttl:i});try{if(r.signal?.aborted)throw new e("Creation aborted. The caller cancelled. Retry when ready.",{code:"aborted",provider:"upstash"});if(P instanceof u)g.set(P.id,P);else f.set(P.id,()=>new u({id:P.id,status:"running",created_at:U/1000,updated_at:U/1000,size:v.size??"small",labels:[F],network_policy:{mode:a.mode,..."allowedDomains"in a?{allowed_domains:a.allowedDomains}:{}}},{baseUrl:(p.baseUrl??process.env.UPSTASH_BOX_BASE_URL??"https://us-east-1.box.upstash.com").replace(/\/$/,""),headers:{"X-Box-Api-Key":p.apiKey},timeout:n.requestTimeoutMs??600000,debug:!1}));if(i!==void 0&&r.from!==void 0&&(await P.exec.command(q)).exitCode!==0)throw new e("Restored process records could not be invalidated",{code:"provider",provider:"upstash",sandboxId:P.id});return w.set(P.id,{...r.env,...s}),c.add(P.id),{id:P.id,key:m.key,labels:m.labels,createdAt:U,state:"running"}}catch(se){throw await P.delete(),g.delete(P.id),f.delete(P.id),c.delete(P.id),w.delete(P.id),se}},void 0,!1,"create"),list:(r)=>S(async()=>{let{Box:a,config:u}=await T(),h=[];for(let p of await a.list({...u,label:F})){if(!p.labels?.includes(F)||p.status==="deleted"||R(p)!==(i!==void 0))continue;try{let m=await A(await y(p.id));if(Object.entries(r).every(([v,U])=>m.labels[v]===U))h.push(ee(p,m))}catch(m){if(D(m).code!=="not_found")throw m}}return h},void 0,!1,"list"),async find(r){return(await O.list({})).find((a)=>a.key===r)},connect:(r)=>S(async()=>{let{Box:a,config:u}=await T(),h=(await a.list(u)).find((m)=>m.id===r);if(!h)throw Object.assign(Error("Missing box"),{statusCode:404});if(R(h)!==(i!==void 0))throw new e(`Box ${r} is ${R(h)?"an Ephemeral Box":"a durable Box"}. Connect with upstash({ ephemeral: ${R(h)} }).`,{code:"unsupported",provider:"upstash",sandboxId:r});let p=await y(r);return c.add(r),ee(h,await A(p))},r,!1,"connect"),destroy:(r)=>S(async()=>{try{await(await y(r)).delete()}catch(a){if(D(a).code!=="not_found")throw a}finally{I(r),b.delete(r),g.delete(r),w.delete(r),f.delete(r),c.delete(r)}},r,!1,"destroy"),exec:(r,a)=>S(async()=>{let u=await y(r),h=w.get(r)??s;if(a.streaming!==!0&&a.stdin===void 0&&a.timeoutMs+15000<=(n.requestTimeoutMs??600000)&&Object.entries(a.env).every(([m,v])=>h[m]===v)){let m=await H(u,a,c.has(r)?N:void 0);if(m){if(!m.timedOut)c.delete(r);return m}}return await G(u,a,c.has(r)?N:void 0,()=>c.delete(r))},r,!1,"exec"),startProcess:(r,a)=>S(async()=>{let u=await y(r),p=await J(u,a,i===void 0?()=>u.pause():(v)=>u.exec.command(`for p in $(pgrep -f ${t(`[${v[1]}]${v.slice(2)}/`)}); do kill -KILL -$p 2>/dev/null; done; true`),c.has(r)?N:void 0);c.delete(r);let m=_.get(r);if(!m)m=new Set,_.set(r,m);return m.add(p.detach),p.wait().finally(()=>m.delete(p.detach)).catch(()=>{}),{pid:p.pid,wait:()=>S(p.wait,r,!1,"process.wait"),stop:(v)=>S(()=>p.stop(v),r,!1,"process.stop")}},r,!1,"process.start"),async close(r){I(r),g.delete(r),w.delete(r),f.delete(r),c.delete(r)},files:{writeCreatesParents:!0,internal:[".step-git"],read:(r,a)=>k(r,"read",(u)=>K(u,a)),readRange:(r,a,u)=>k(r,"readRange",(h)=>K(h,a,u)),write:(r,a,u)=>k(r,"write",(h)=>Y(h,a,u,c.has(r)?N:void 0).then(()=>{c.delete(r)})),list:(r,a)=>k(r,"list",async(u)=>{let h=await W(u,a,c.has(r)?N:void 0);if(h)return c.delete(r),h.map((p)=>{if(p.type==="other")z("Special filesystem entries are not supported");return{path:Q.join(a,p.name),type:p.type,size:p.size,modifiedAt:p.modifiedAt}});return Promise.all((await u.files.list(a)).map(async(p)=>{let m=Q.join(a,p.name);return te(m,await u.files.stat(m))}))}),stat:(r,a)=>k(r,"stat",async(u)=>{try{return te(a,await u.files.stat(a))}catch(h){if(a!=="/workspace"||!c.has(r)||D(h,r,!0).code!=="file_not_found")throw h;return{path:a,type:"directory",size:0,modifiedAt:Date.now()}}}),remove:(r,a,u)=>k(r,"remove",async(h)=>{try{await h.files.remove(a,u)}catch(p){if(a!=="/workspace"||!c.has(r)||D(p,r,!0).code!=="file_not_found")throw p}if(a==="/workspace")c.delete(r)}),mkdir:(r,a)=>k(r,"mkdir",async(u)=>{if(!c.has(r))return u.files.mkdir(a,{parents:!0});if((await u.exec.command(`${N} && mkdir -p ${t(a)}`)).exitCode!==0)throw new e("Directory creation failed. Check guest permissions.",{code:"provider",provider:"upstash",sandboxId:r});c.delete(r)})},previewUrl:(r,a)=>{let u=b.get(r);if(!u)u=new Map,b.set(r,u);let h=u.get(a);if(h)return h;let p=S(async()=>{let m=await(await y(r)).getPublicURL(a,n.public?void 0:{bearerToken:!0});if(!n.public&&!m.token)throw Error("Protected preview returned no bearer token");return{url:m.url,...m.token?{headers:{Authorization:`Bearer ${m.token}`}}:{}}},r,!1,"preview");return u.set(a,p),p.catch(()=>u.delete(a)),p},...i===void 0?{pause:(r)=>S(async()=>{I(r),await(await y(r)).pause()},r,!1,"pause"),resume:(r)=>S(async()=>{await(await y(r)).resume()},r,!1,"resume")}:{},snapshot:(r)=>S(async()=>{let a=await y(r);return{id:(await a.snapshot({name:`sdk-v1-${a.size}-${crypto.randomUUID()}`})).id}},r,!1,"snapshot"),native(r){let a=f.get(r);if(a){let h=a();return g.set(r,h),f.delete(r),h}let u=g.get(r);if(!u)throw new e("Sandbox is not connected. No native object is cached. Connect first.",{code:"not_found",provider:"upstash",sandboxId:r});return u}};return O}export{Ae as upstash};
@@ -0,0 +1,3 @@
1
+ import { SandboxError } from "../../errors.js";
2
+ export declare function vercelError(error: unknown, sandboxId?: string): SandboxError;
3
+ export declare function unsupported(message: string): never;
@@ -0,0 +1,14 @@
1
+ import type { Sandbox } from "@vercel/sandbox";
2
+ import type { Provider } from "../../provider.js";
3
+ import { type VercelOptions as NativeVercelOptions, type VercelCapability } from "./provider.js";
4
+ export type { VercelCapability } from "./provider.js";
5
+ export interface VercelOptions extends NativeVercelOptions {
6
+ /** Defaults to VERCEL_TOKEN; otherwise the native SDK uses VERCEL_OIDC_TOKEN. */
7
+ token?: string;
8
+ /** Defaults to VERCEL_TEAM_ID; required with token authentication. */
9
+ teamId?: string;
10
+ /** Defaults to VERCEL_PROJECT_ID; required with token authentication. */
11
+ projectId?: string;
12
+ }
13
+ export type VercelProvider = Provider<Sandbox, VercelCapability>;
14
+ export declare function vercel(options?: VercelOptions): VercelProvider;
@@ -0,0 +1 @@
1
+ import{t}from"../../chunk-t5vr9nd8.js";import{o}from"../../chunk-yed8mvs7.js";import{e}from"../../chunk-gc8zdxyb.js";import{createHash as q}from"node:crypto";import{posix as O}from"node:path";import{Readable as R}from"node:stream";function k(i,p){if(i instanceof e)return i;let l=typeof i==="object"&&i!==null&&"response"in i&&i.response instanceof Response?i.response.status:void 0,m=typeof i==="object"&&i!==null&&"code"in i?i.code:void 0,b=l===401||l===403?"auth":l===404?"not_found":l===429?"quota":l!==void 0&&l>=500?"unavailable":m==="ENOENT"?"file_not_found":m==="ENOTEMPTY"||m==="ENOTDIR"||l===400?"invalid_argument":i instanceof Error&&i.name==="AbortError"?"aborted":i instanceof Error&&i.name==="TimeoutError"?"timeout":i instanceof Error&&/credentials|OIDC|token/i.test(i.message)?"auth":"provider";return new e(`Vercel request failed (${b}). The provider could not complete the operation. Check credentials, resource limits, and the sandbox state before retrying.`,{code:b,provider:"vercel",sandboxId:p,retryable:b==="quota"||b==="unavailable"})}function N(i){throw new e(`${i}. Vercel cannot enforce this request. Use the supported Vercel settings or another provider.`,{code:"unsupported",provider:"vercel"})}var L="sandbox-sdk-key",j=(i)=>`sdk-${q("sha256").update(i).digest("hex")}`,M="flock -x /tmp/.sandbox-sdk-workspace-ready.lock sh -c 'if [ ! -f /tmp/.sandbox-sdk/prepared-workspace ]; then sudo mkdir -p /workspace && sudo chmod 777 /workspace && mkdir -p /tmp/.sandbox-sdk && touch /tmp/.sandbox-sdk/prepared-workspace || exit 1; fi' || exit 1",A="stat --printf '%n\\0%f %s %y\\0' --";function U(i){let p=i.split("\x00");if(p.pop()!==""||p.length%2)throw Error("Invalid Vercel file metadata");let l=[];for(let m=0;m<p.length;m+=2){let b=p[m],d=p[m+1]?.match(/^([0-9a-f]+) ([0-9]+) (.+)$/);if(b===void 0||!d)throw Error("Invalid Vercel stat response");let g=Number.parseInt(d[1]??"",16),C=Number(d[2]),_=Date.parse(d[3]??"");if(![g,C,_].every(Number.isFinite))throw Error("Invalid Vercel stat fields");l.push({path:b,type:(g&61440)===40960?"symlink":(g&61440)===16384?"directory":"file",size:C,modifiedAt:_})}return l}var B='for d in /tmp/.sandbox-sdk/procs/*; do [ -f "$d/spec.json" ] && [ ! -f "$d/exit" ] && printf 137 > "$d/exit"; done; rm -rf /tmp/.sandbox-sdk/runs; true';function z(i,p={}){let l=new Map,m=new Set,b,T=()=>b??=i(),d=async(r,n)=>{try{return await n()}catch(a){throw k(a,r)}};function g(){let r=p.token??process.env.VERCEL_TOKEN,n=p.teamId??process.env.VERCEL_TEAM_ID,a=p.projectId??process.env.VERCEL_PROJECT_ID;if(r){if(!n||!a)throw new e("Vercel credentials are incomplete. Token authentication requires teamId and projectId. Set VERCEL_TEAM_ID and VERCEL_PROJECT_ID.",{code:"auth",provider:"vercel"});return{token:r,teamId:n,projectId:a}}return{}}let C=async(r,n=!1)=>{if((await(await r.runCommand({cmd:"sh",args:["-c",B+(n?"; mkdir -p /tmp/.sandbox-sdk && touch /tmp/.sandbox-sdk/prepared-workspace":"")],detached:!0})).wait()).exitCode!==0)throw Error("Could not invalidate restored processes")},_=(r)=>{let n=l.get(r);if(!n)throw new e("Sandbox is not attached. No local native object exists. Connect the sandbox first.",{code:"not_found",provider:"vercel",sandboxId:r});return n};function P(r){let n={...r.tags},a=n[L];return delete n[L],{id:r.name,key:a,labels:n,createdAt:r.createdAt.getTime(),state:r.status==="running"||r.status==="pending"?"running":r.status==="stopped"?"paused":"stopped"}}let S=async(r,n=!1)=>{let a=await T(),s=await a.get({...g(),name:r,resume:n,onResume:C}),c=Date.now()+60000;while(s.tags?.["sandbox-sdk"]==="pending"){if(Date.now()>=c)throw new e("Sandbox initialization is incomplete. The creator has not finished setup. Retry connection later.",{code:"unavailable",provider:"vercel",sandboxId:r,retryable:!0});await new Promise((u)=>setTimeout(u,25)),s=await a.get({...g(),name:r,resume:!1,onResume:C})}return l.set(r,s),s},h=async(r)=>l.get(r)??S(r);function F(r){if(r===void 0)return;if(typeof r==="string")return{vcpus:{small:1,medium:2,large:4}[r]};N("Custom sizes are unavailable; Vercel exposes only vCPUs with 2048 MiB RAM per vCPU and no disk allocation control")}let D=async(r,n)=>{let a=await(await h(r)).fs.lstat(n);return{path:n,type:a.isSymbolicLink()?"symlink":a.isDirectory()?"directory":"file",size:a.size,modifiedAt:a.mtimeMs}};async function V(r,n,a,s){if(m.has(r)||!(a[0]==="/workspace"||a[0]?.startsWith("/workspace/")))return;let u=await(await h(r)).runCommand({cmd:"sh",args:["-c",`${M}; ${n}`,"sh",...a],cwd:"/",env:{LC_ALL:"C"},stdin:s===void 0?!1:R.from([s]),detached:!0}),I="",y="",f=(async()=>{for await(let x of u.logs())if(x.stream==="stdout")I+=x.data;else y+=x.data})(),[E]=await Promise.all([u.wait(),f]);if(E.exitCode!==0){if(/No such file or directory/.test(y))throw k({code:"ENOENT"},r);if(/Not a directory|Directory not empty/.test(y))throw k({code:"ENOTDIR"},r);throw Error("Vercel workspace or file operation failed")}return m.add(r),I}async function K(r,n,a){let s=`/tmp/sandbox-sdk-${crypto.randomUUID()}.pid`,c=`${s}.error`,u=`${m.has(r.name)?"":`(${M}) || { echo setup-error > ${t(c)}; exit 125; }; `}cd ${t(n.cwd)} || { echo cwd-error > ${t(c)}; exit 125; }; exec sh -c ${t(n.command)}`,I=`echo $$ > ${t(s)}; exec timeout --signal=KILL ${a/1000}s sh -c ${t(u)}`,y=await r.runCommand({cmd:"setsid",args:["--wait","sh","-c",I],cwd:"/",env:n.env,stdin:n.stdin===void 0?!1:R.from([n.stdin]),detached:!0}),f;try{for(let w=0;w<200;w++){try{if(f=Number((await r.fs.readFile(s)).toString()),Number.isInteger(f)&&f>0)break}catch(v){if(k(v,r.name).code!=="file_not_found")throw v}await new Promise((v)=>setTimeout(v,5))}if(!f||!Number.isInteger(f))throw await y.kill("SIGKILL"),new e("Command launch could not be confirmed. Remote process ID was unavailable. Reconnect and inspect the sandbox.",{code:"outcome_unknown",provider:"vercel"});await r.fs.rm(s,{force:!0})}catch(w){let v=f&&Number.isInteger(f)&&f>0?String(f):`$(cat ${t(s)})`;if((await(await r.runCommand({cmd:"sh",args:["-c",`pid=${v}; case "$pid" in ''|*[!0-9]*) exit 1;; esac; kill -KILL -"$pid" 2>/dev/null || true`],detached:!0})).wait()).exitCode!==0)throw new e("Command launch cleanup could not be confirmed. The remote PID is unavailable. Inspect or destroy the sandbox.",{code:"outcome_unknown",provider:"vercel",sandboxId:r.name});throw await y.wait(),w}let E=f;return{command:y,pid:f,kill:async(w)=>{await(await r.runCommand({cmd:"sh",args:["-c",`kill -${w} -${E} 2>/dev/null || true`],detached:!0})).wait()},errorPath:c}}return{name:"vercel",keyMode:"exact",capabilities:new Set(["pause","snapshot","network-policy","size"]),native:_,create:(r)=>d(void 0,async()=>{if(r.timeoutMs!==void 0)N("A hard lifetime across persistent sessions is unavailable; Vercel timeout resets on resume");if(r.idleTimeoutMs!==void 0)N("idleTimeoutMs is not exposed by the Vercel SDK");let n={...r.labels,"sandbox-sdk":"1",...r.key===void 0?{}:{[L]:r.key}};if(Object.keys(n).length>5)N("Vercel supports at most five tags, including sandbox-sdk and sandbox-sdk-key");if(new Set(r.ports).size>15)N("Vercel supports at most fifteen declared ports");let a={...g(),name:r.key===void 0?`sdk-${crypto.randomUUID()}`:j(r.key),persistent:!0,env:r.env,tags:n,ports:r.ports,resources:F(r.size),networkPolicy:r.network==="none"?"deny-all":typeof r.network==="object"?{allow:r.network.allow}:"allow-all",signal:r.signal,onResume:C},s={...a,tags:{...n,"sandbox-sdk":"pending"}},c=await(await T()).create(r.from?{...s,source:{type:"snapshot",snapshotId:r.from}}:a);l.set(c.name,c);try{if(!r.from)return P(c);if((await(await c.runCommand({cmd:"sh",args:["-c","mkdir -p /workspace && chmod 777 /workspace"],sudo:!0,detached:!0})).wait()).exitCode!==0)throw Error("Could not initialize /workspace");return await C(c,!0),await c.update({tags:n}),m.add(c.name),P(c)}catch(u){throw await c.delete(),l.delete(c.name),u}}),find:(r)=>d(void 0,async()=>{try{let n=await S(j(r));return n.tags?.["sandbox-sdk"]==="1"&&n.tags[L]===r?P(n):void 0}catch(n){if(k(n).code==="not_found")return;throw n}}),connect:(r)=>d(r,async()=>P(await S(r))),list:(r)=>d(void 0,async()=>{let n=[];for await(let a of await(await T()).list({...g(),tags:{"sandbox-sdk":"1"}}))try{let s=P(await S(a.name));if(s.labels["sandbox-sdk"]==="1"&&Object.entries(r).every(([c,u])=>s.labels[c]===u))n.push(s)}catch(s){if(k(s,a.name).code!=="not_found")throw s}return n}),destroy:(r)=>d(r,async()=>{try{await(await h(r)).delete()}catch(n){if(k(n).code!=="not_found")throw n}l.delete(r),m.delete(r)}),exec:(r,n)=>d(r,async()=>{if(n.signal?.aborted)return{exitCode:null,timedOut:!0};let a=Date.now(),s=await h(r),c=await K(s,n,n.timeoutMs),u=!1,I,y=()=>{u=!0,I??=c.kill("KILL"),I.catch(()=>{})};n.signal?.addEventListener("abort",y,{once:!0});let f=setTimeout(y,Math.max(0,n.timeoutMs-(Date.now()-a)));if(n.signal?.aborted)y();try{let E=(async()=>{for await(let w of c.command.logs())n.onChunk({stream:w.stream,text:w.data})})(),[x]=await Promise.all([c.command.wait(),E]);if(x.exitCode===137&&Date.now()-a>=n.timeoutMs)u=!0;if(await I,!u&&x.exitCode===125){let w;try{w=(await s.fs.readFile(c.errorPath)).toString().trim()}catch(v){if(k(v,r).code!=="file_not_found")throw v}if(w!==void 0)throw await s.fs.rm(c.errorPath,{force:!0}),new e("Command could not start. Workspace initialization or the requested working directory failed. Check the directory and sandbox permissions.",{code:w==="cwd-error"?"invalid_argument":"provider",provider:"vercel",sandboxId:r})}if(!u&&x.exitCode===0)m.add(r);return{exitCode:u?null:x.exitCode,timedOut:u}}catch(E){throw await c.kill("KILL"),await c.command.wait(),E}finally{clearTimeout(f),n.signal?.removeEventListener("abort",y)}}),files:{writeCreatesParents:!0,read:(r,n)=>d(r,async()=>{let a=await V(r,'base64 -- "$1"',[n]);return a===void 0?new Uint8Array(await(await h(r)).fs.readFile(n)):new Uint8Array(Buffer.from(a,"base64"))}),write:(r,n,a)=>d(r,async()=>{if(await V(r,'mkdir -p -- "$(dirname -- "$1")" && cat > "$1"',[n],a)!==void 0)return;let s=await h(r);await s.fs.mkdir(O.dirname(n),{recursive:!0}),await s.fs.writeFile(n,a)}),stat:(r,n)=>d(r,async()=>{let a=await V(r,`${A} "$1"`,[n]);if(a===void 0)return D(r,n);let s=U(a)[0];if(!s)throw Error("Missing Vercel stat response");return s}),list:(r,n)=>d(r,async()=>{let a=await V(r,`if [ ! -d "$1" ]; then if [ -e "$1" ] || [ -L "$1" ]; then echo 'Not a directory' >&2; else echo 'No such file or directory' >&2; fi; exit 1; fi; find -H "$1" -mindepth 1 -maxdepth 1 -exec ${A} {} +`,[n]);if(a===void 0)return Promise.all((await(await h(r)).fs.readdir(n)).map((s)=>D(r,O.join(n,s))));return U(a)}),mkdir:(r,n)=>d(r,async()=>{if(await V(r,'mkdir -p -- "$1"',[n])!==void 0)return;await(await h(r)).fs.mkdir(n,{recursive:!0})}),remove:(r,n,a)=>d(r,async()=>{if(await V(r,'if [ "$2" = true ]; then rm -r -- "$1"; else rm -- "$1"; fi',[n,String(a.recursive)])!==void 0)return;await(await h(r)).fs.rm(n,{recursive:a.recursive})})},previewUrl:(r,n)=>d(r,async()=>{let a=await h(r);if(!a.routes.some((s)=>s.port===n))throw new e(`Port ${n} was not declared at creation. Vercel needs ports in CreateInput. Recreate with ports: [${n}].`,{code:"invalid_argument",provider:"vercel",sandboxId:r});return{url:a.domain(n)}}),close:async(r)=>{l.delete(r),m.delete(r)},pause:(r)=>d(r,async()=>{await(await h(r)).stop()}),resume:(r)=>d(r,async()=>{await S(r,!0)}),snapshot:(r)=>d(r,async()=>({id:(await(await h(r)).snapshot()).snapshotId}))}}function ae(i={}){return z(async()=>{let{Sandbox:p}=await o("@vercel/sandbox","vercel",()=>import("@vercel/sandbox"));return p},i)}export{ae as vercel};
@@ -0,0 +1,10 @@
1
+ import type { Provider } from "../../provider.js";
2
+ import type { NativeSandbox, VercelClient } from "./transport.js";
3
+ export interface VercelOptions {
4
+ token?: string;
5
+ teamId?: string;
6
+ projectId?: string;
7
+ }
8
+ export type VercelCapability = "pause" | "snapshot" | "network-policy" | "size";
9
+ /** Internal injection point used by the offline SDK contract tests. */
10
+ export declare function createVercelProvider<N extends NativeSandbox>(load: () => Promise<VercelClient<N>>, options?: VercelOptions): Provider<N, VercelCapability>;
@@ -0,0 +1,40 @@
1
+ import type { Sandbox, Command } from "@vercel/sandbox";
2
+ import type { Stats } from "node:fs";
3
+ export type NativeCommand = Pick<Command, "cmdId" | "kill"> & {
4
+ wait(): Promise<Pick<Awaited<ReturnType<Command["wait"]>>, "exitCode">>;
5
+ logs(): AsyncIterable<{
6
+ stream: "stdout" | "stderr";
7
+ data: string;
8
+ }>;
9
+ };
10
+ /** SDK-derived transport slice, structurally checked against the real SDK loader. */
11
+ export type NativeSandbox = Pick<Sandbox, "name" | "status" | "tags" | "routes" | "createdAt" | "cwd" | "domain" | "delete" | "update"> & {
12
+ fs: Pick<Sandbox["fs"], "writeFile" | "mkdir" | "rm"> & {
13
+ readFile(path: string): Promise<Buffer>;
14
+ readdir(path: string): Promise<string[]>;
15
+ lstat(path: string): Promise<Stats>;
16
+ };
17
+ stop(): Promise<unknown>;
18
+ snapshot(): Promise<Pick<Awaited<ReturnType<Sandbox["snapshot"]>>, "snapshotId">>;
19
+ runCommand(params: Parameters<Sandbox["runCommand"]>[0] & {
20
+ detached: true;
21
+ }): Promise<NativeCommand>;
22
+ };
23
+ export interface VercelClient<N extends NativeSandbox> {
24
+ create(params: NonNullable<Parameters<typeof Sandbox.create>[0]> & {
25
+ onResume?: (box: N) => Promise<void>;
26
+ }): Promise<N>;
27
+ get(params: Parameters<typeof Sandbox.get>[0] & {
28
+ onResume?: (box: N) => Promise<void>;
29
+ }): Promise<N>;
30
+ list(params: {
31
+ tags: {
32
+ "sandbox-sdk": string;
33
+ };
34
+ token?: string;
35
+ teamId?: string;
36
+ projectId?: string;
37
+ }): Promise<AsyncIterable<{
38
+ name: string;
39
+ }>>;
40
+ }
package/dist/run.d.ts ADDED
@@ -0,0 +1,41 @@
1
+ import type { OutputChunk, Provider } from "./provider.js";
2
+ import { Redactor } from "./errors.js";
3
+ import type { RunHandle, RunOptions } from "./types.js";
4
+ export declare const defaults: {
5
+ CI: string;
6
+ PAGER: string;
7
+ GIT_TERMINAL_PROMPT: string;
8
+ DEBIAN_FRONTEND: string;
9
+ TERM: string;
10
+ NO_COLOR: string;
11
+ };
12
+ export declare const pathOf: (path?: string) => string;
13
+ export declare const quote: (value: unknown) => string;
14
+ export declare const secretRecoveryCommand = "if [ -f /tmp/.sandbox-sdk/secret-names.json ]; then cat /tmp/.sandbox-sdk/secret-names.json || exit 1; printf '\\000'; env -0; fi";
15
+ export declare const isRunActive: (provider: string, id: string) => boolean;
16
+ export declare class BufferCap {
17
+ readonly limit: number;
18
+ private head;
19
+ private tail;
20
+ total: number;
21
+ constructor(limit: number);
22
+ add(text: string): void;
23
+ get truncated(): boolean;
24
+ text(): string;
25
+ }
26
+ /** Bounded independent subscriptions; slow consumers see the most recent 1 MB. */
27
+ export declare class OutputBus {
28
+ private listeners;
29
+ private ended;
30
+ private failure;
31
+ emit(chunk: OutputChunk): void;
32
+ fail(error: unknown): void;
33
+ end(): void;
34
+ stream(): AsyncGenerator<OutputChunk>;
35
+ }
36
+ /** Hold suffixes so secrets split between transport chunks never escape. */
37
+ export declare function chunkRedactor(redactor: Redactor, emit: (chunk: OutputChunk) => void, interleaved?: boolean): {
38
+ push(chunk: OutputChunk): void;
39
+ flush(): void;
40
+ };
41
+ export declare function makeRun(provider: Provider, id: string, redactor: Redactor, baseEnv: Record<string, string>, count: (bytes: number) => void, guard?: () => void): (command: string, initial?: RunOptions) => RunHandle;
@@ -0,0 +1,17 @@
1
+ import type { Provider } from "./provider.js";
2
+ /** Register a provider-independent Vitest suite. Call inside a test module.
3
+ * live must be explicitly true to run a cloud provider. memory skips real git
4
+ * and HTTP probes, since it never executes host programs or opens sockets.
5
+ * Live runs give each case 120 seconds by default, since real providers boot
6
+ * machines and cross the network; pass timeoutMs to change it.
7
+ */
8
+ export declare function runConformance(factory: () => Provider | Promise<Provider>, options: {
9
+ live: boolean;
10
+ ports?: number[];
11
+ timeoutMs?: number;
12
+ unsupported?: {
13
+ processes?: string;
14
+ previews?: string;
15
+ gitPush?: string;
16
+ };
17
+ }): void;
@@ -0,0 +1,6 @@
1
+ import{p,g,S,E}from"./chunk-59t3rqja.js";import{e}from"./chunk-gc8zdxyb.js";import{describe as y,test as n,expect as a,afterEach as x}from"vitest";import{mkdtemp as k,mkdir as B,writeFile as b,readFile as O,rm as j}from"node:fs/promises";import{tmpdir as M}from"node:os";import{join as w}from"node:path";function A(f,c){let v=c.timeoutMs??(c.live?120000:void 0);y("Sandbox SDK conformance",{timeout:v},()=>{let u=[],h=[];async function d(t={}){let i=await f();if(!c.live&&!["memory","local"].includes(i.name))throw new e("Live provider refused. live is false. Enable live explicitly with provider credentials.",{code:"invalid_argument"});let o=await p(i,{ports:c.ports,...t});return u.push(o),{provider:i,box:o}}x(async()=>{await Promise.all(u.splice(0).map((t)=>t.destroy())),await Promise.all(h.splice(0).map((t)=>j(t,{recursive:!0,force:!0})))}),n("create, key lookup, connect and labels",async()=>{let t=await f();if(!c.live&&!["memory","local"].includes(t.name))throw Error("live must be enabled");let i=`conformance-${crypto.randomUUID()}`,o=await p(t,{key:i,labels:{suite:i}});u.push(o);let s=await p(t,{key:i});u.push(s),a(o.created).toBe(!0),a(s.created).toBe(!1),a(s.id).toBe(o.id);let r=await g(t,o.id);u.push(r),a(r.native).toBeDefined(),await a.poll(async()=>(await S(t,{labels:{suite:i}})).map((l)=>l.id),{timeout:15000,interval:500}).toEqual([o.id])}),n("exit results, stdin, resolved cwd and default environment",async()=>{let{box:t}=await d();a(await t.run("false")).toMatchObject({ok:!1,exitCode:1,timedOut:!1}),a(await t.run("exit 7")).toMatchObject({exitCode:7}),a((await t.run("cat",{stdin:`input
2
+ `})).stdout).toBe(`input
3
+ `),a((await t.run("pwd")).stdout).toBe(`/workspace
4
+ `),a((await t.run(`printf '%s' "$CI:$PAGER:$GIT_TERMINAL_PROMPT:$DEBIAN_FRONTEND:$TERM:$NO_COLOR"`)).stdout).toBe("1:cat:0:noninteractive:dumb:1")}),n("timeout and abort kill work before following side effects",async()=>{let{box:t}=await d(),i=await t.run("sleep 1; echo survived > survived",{timeoutMs:20});a(i).toMatchObject({timedOut:!0,exitCode:null,ok:!1});let o=new AbortController,s=t.run("sleep 1; echo survived > aborted",{signal:o.signal}),r=Promise.resolve(s);setTimeout(()=>o.abort(),20),a(await r).toMatchObject({timedOut:!0,exitCode:null}),a(await t.files.exists("survived")).toBe(!1),a(await t.files.exists("aborted")).toBe(!1)}),n("bounded head and tail output, interleaving and streaming",async()=>{let{box:t}=await d(),i=await t.run(`printf '${"a".repeat(1024)}END'`,{maxOutputBytes:64});a(i.truncated).toBe(!0),a(i.stdout.startsWith("a")).toBe(!0),a(i.stdout.endsWith("END")).toBe(!0),a(i.stdout).toContain("bytes cut");let o=t.run("printf A; printf B >&2; printf C"),s=[];for await(let m of o)s.push(m.text);let r=await o;a(r).toMatchObject({stdout:"AC",stderr:"B",output:s.join("")});let l=[];for await(let m of t.run("echo streamed"))l.push(m.text);a(l.join("")).toBe(`streamed
5
+ `)}),n("tag escaping and chain options prevent interpolation execution",async()=>{let{box:t}=await d(),i="x'; echo injected; printf '";a((await t.$`printf '%s' ${"x'; echo injected; printf '"}`.cwd(".").env({X:"ok"}).timeout(c.live?1e4:1000)).stdout).toBe("x'; echo injected; printf '"),a((await t.run(`printf '%s' "$X"`)).stdout).toBe("")}),n("all file primitives, exact edits, glob search, checkpoints",async()=>{let{box:t}=await d({trackChanges:!0});await t.files.write({"app/a.ts":`TODO one
6
+ second`,"app/b.ts":"two two","app/raw":new Uint8Array([0,255])}),a(await t.files.read("app/raw",{as:"bytes"})).toEqual(new Uint8Array([0,255])),a((await t.files.stat("app/a.ts")).type).toBe("file"),a((await t.files.list("app",{recursive:!0,glob:"**/*.ts"})).length).toBe(2),a(await t.files.search("TODO",{path:"app",glob:"*.ts"})).toMatchObject([{line:1,text:"TODO one"}]),await a(t.files.edit("app/a.ts",{find:"missing",replace:"x"})).rejects.toMatchObject({code:"edit_not_found"}),await a(t.files.edit("app/b.ts",{find:"two",replace:"x"})).rejects.toMatchObject({code:"edit_not_unique"});let i=await t.files.mark();await t.files.edit("app/a.ts",{find:"TODO",replace:"DONE"}),await t.files.remove("app/b.ts"),await t.files.write("app/new","new"),a(await t.files.changes({since:i})).toMatchObject({added:["/workspace/app/new"],modified:["/workspace/app/a.ts"],removed:["/workspace/app/b.ts"]}),a((await t.files.changes()).added).toContain("/workspace/app/a.ts"),await t.files.remove("app",{recursive:!0}),a(await t.files.exists("app")).toBe(!1)}),n("changes without since needs trackChanges or an explicit mark",async()=>{let{box:t}=await d();await a(t.files.changes()).rejects.toMatchObject({code:"invalid_argument",message:a.stringContaining("trackChanges: true")}),await t.files.write("late","x");let i=await t.files.mark("start");await t.files.write("late","y"),a((await t.files.changes()).modified).toEqual(["/workspace/late"]),a((await t.files.changes({since:i})).modified).toEqual(["/workspace/late"])}),n("upload/download round trip with ignores",async()=>{let{box:t}=await d(),i=await k(w(M(),"sdk-conformance-"));h.push(i),await B(w(i,"source")),await b(w(i,"source","keep"),"hello"),await b(w(i,"source","ignore"),"no"),await t.files.upload(w(i,"source"),"upload",{ignore:["ignore"]}),a(await t.files.exists("upload/ignore")).toBe(!1),await t.files.download("upload",w(i,"download")),a(await O(w(i,"download","keep"),"utf8")).toBe("hello")}),n("file watch observes changes",async()=>{let{box:t}=await d();await t.files.write("watch/file","a");let i=[],o=t.files.watch("watch",(r)=>i.push(r.type),{recursive:!0,intervalMs:10}),s=0;try{await a.poll(async()=>(await t.files.write("watch/file",`different-${s++}`),i),{timeout:1e4,interval:50}).toContain("modified")}finally{o()}}),n("process logs, cursors, fresh client reattach, stop and exit-before-ready",async(t)=>{if(c.unsupported?.processes){t.skip(c.unsupported.processes);return}let{provider:i,box:o}=await d(),s=await o.start("echo READY; sleep 5",{name:"dev",ready:{log:/READY/},readyTimeoutMs:2000});a(s.ready).toBe(!0);let r=await s.logs();a(r.text).toContain("READY"),a((await s.logs({after:r.nextCursor})).text).toBe("");let l=await g(i,o.id);u.push(l),a((await l.processes()).map((m)=>m.name)).toContain("dev"),a((await l.process("dev").logs()).text).toContain("READY"),await l.process("dev").stop({graceMs:10}),a((await s.wait()).exitCode).not.toBe(null),await a(o.start("exit 3",{name:"early",ready:{log:/never/},readyTimeoutMs:1000})).rejects.toMatchObject({code:"process_exited"})}),n("readiness timeout preserves the process",async(t)=>{if(c.unsupported?.processes){t.skip(c.unsupported.processes);return}let{box:i}=await d();await a(i.start("sleep 5",{name:"unready",ready:{log:/never/},readyTimeoutMs:30})).rejects.toMatchObject({code:"timeout"}),a(i.process("unready").ready).toBe(!1),a((await i.processes())[0]?.status).toBe("running"),await i.process("unready").stop({graceMs:10})}),n("preview and capability gating",async()=>{let{provider:t,box:i}=await d();if(c.unsupported?.previews)await a(i.preview(3000)).rejects.toMatchObject({code:"unsupported"});else a(["http:","https:"]).toContain(new URL((await i.preview(3000)).url).protocol);for(let[o,s]of[["pause","pause"],["snapshot","snapshot"],["terminal","terminal"]])a(s in i).toBe(t.capabilities.has(o));if(!t.capabilities.has("network-policy"))await a(p(t,{network:"none"})).rejects.toMatchObject({code:"unsupported"});if(!t.capabilities.has("size"))await a(p(t,{size:"small"})).rejects.toMatchObject({code:"unsupported"})}),n("close, disposal, destroy and label-scoped reaping",async()=>{let{provider:t,box:i}=await d(),o=i.id;await i.close(),a((await g(t,o)).id).toBe(o),await i.destroy(),await a(g(t,o)).rejects.toMatchObject({code:"not_found"});let s=crypto.randomUUID(),r=await p(t,{key:s,labels:{reap:s}});u.push(r),await r[Symbol.asyncDispose](),a((await t.connect(r.id)).id).toBe(r.id),a(await E(t,{labels:{reap:s},olderThanMs:0})).toBe(1);let l=await p(t);await l[Symbol.asyncDispose](),await a(t.connect(l.id)).rejects.toMatchObject({code:"not_found"})}),n("usage counters",async()=>{let{box:t}=await d();await t.run("echo abc"),a(await t.usage()).toMatchObject({commands:1,bytesOut:4})}),n("local bare git clone, commit and push (live providers only)",async(t)=>{if(c.unsupported?.gitPush){t.skip(c.unsupported.gitPush);return}let{provider:i,box:o}=await d();if(i.name==="memory"){t.skip();return}a((await o.run("git init --bare /tmp/sdk-bare")).ok).toBe(!0),a((await o.git.clone("/tmp/sdk-bare",{dir:"repo"})).ok).toBe(!0),await o.run("git config user.name Conformance; git config user.email test@example.com",{cwd:"repo"}),await o.files.write("repo/test","test"),a((await o.git.commit("repo",{message:"conformance",all:!0})).ok).toBe(!0),a((await o.git.status("repo")).stdout).toBe(""),a((await o.git.push("repo",{branch:"HEAD"})).ok).toBe(!0)}),n("real HTTP preview probe (live Linux providers only)",async(t)=>{if(c.unsupported?.previews){t.skip(c.unsupported.previews);return}let{provider:i,box:o}=await d();if(i.name==="memory"){t.skip();return}let s=await o.start(`node -e 'require("http").createServer((_,r)=>r.end("ok")).listen(3000,"0.0.0.0")'`,{name:"http",ready:{http:{port:3000}}});a(s.ready).toBe(!0),a((await o.preview(3000,{waitMs:2000})).url).toBeTruthy(),await s.stop()})})}export{A as runConformance};
@@ -0,0 +1,9 @@
1
+ import type { Redactor } from "./errors.js";
2
+ import type { OutputChunk } from "./types.js";
3
+ export declare function registerToolRedactor(box: object, redactor: Redactor): void;
4
+ export declare function redactToolText(box: object, text: string): string;
5
+ /** ANSI sequences and secrets can both cross transport boundaries. Normalize before redacting. */
6
+ export declare function toolOutputFilter(box: object, emit: (chunk: OutputChunk) => void): {
7
+ push(chunk: OutputChunk): undefined;
8
+ flush(): void;
9
+ };
@@ -0,0 +1,117 @@
1
+ import { z } from "zod";
2
+ import type { Sandbox, OutputChunk } from "./types.js";
3
+ export declare const toolSchemas: {
4
+ run: z.ZodObject<{
5
+ command: z.ZodString;
6
+ cwd: z.ZodOptional<z.ZodString>;
7
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
8
+ }, z.core.$strict>;
9
+ read_file: z.ZodObject<{
10
+ path: z.ZodString;
11
+ offset: z.ZodOptional<z.ZodNumber>;
12
+ limit: z.ZodOptional<z.ZodNumber>;
13
+ }, z.core.$strict>;
14
+ write_file: z.ZodObject<{
15
+ path: z.ZodString;
16
+ content: z.ZodString;
17
+ }, z.core.$strict>;
18
+ edit_file: z.ZodObject<{
19
+ path: z.ZodString;
20
+ find: z.ZodString;
21
+ replace: z.ZodString;
22
+ }, z.core.$strict>;
23
+ start_process: z.ZodObject<{
24
+ name: z.ZodString;
25
+ command: z.ZodString;
26
+ cwd: z.ZodOptional<z.ZodString>;
27
+ port: z.ZodOptional<z.ZodNumber>;
28
+ }, z.core.$strict>;
29
+ process_logs: z.ZodObject<{
30
+ name: z.ZodString;
31
+ cursor: z.ZodOptional<z.ZodString>;
32
+ }, z.core.$strict>;
33
+ stop_process: z.ZodObject<{
34
+ name: z.ZodString;
35
+ }, z.core.$strict>;
36
+ search: z.ZodObject<{
37
+ query: z.ZodString;
38
+ path: z.ZodOptional<z.ZodString>;
39
+ glob: z.ZodOptional<z.ZodString>;
40
+ }, z.core.$strict>;
41
+ list_files: z.ZodObject<{
42
+ path: z.ZodOptional<z.ZodString>;
43
+ glob: z.ZodOptional<z.ZodString>;
44
+ }, z.core.$strict>;
45
+ changes: z.ZodObject<{}, z.core.$strict>;
46
+ preview: z.ZodObject<{
47
+ port: z.ZodNumber;
48
+ path: z.ZodOptional<z.ZodString>;
49
+ }, z.core.$strict>;
50
+ };
51
+ export type ToolName = keyof typeof toolSchemas;
52
+ export type ToolParameters = z.infer<(typeof toolSchemas)[ToolName]>;
53
+ export type ToolEvent = {
54
+ tool: ToolName;
55
+ kind: "output";
56
+ stream: OutputChunk["stream"];
57
+ text: string;
58
+ };
59
+ export interface ToolLimits {
60
+ maxOutputBytes?: number;
61
+ timeoutMs?: number;
62
+ maxReadBytes?: number;
63
+ maxMatches?: number;
64
+ }
65
+ export interface SandboxToolsOptions {
66
+ /** Resolves file paths and cwd. Shell commands still have the sandbox's full privileges. */
67
+ root?: string;
68
+ allow?: readonly ToolName[];
69
+ deny?: readonly ToolName[];
70
+ beforeRun?: (command: string) => boolean | string | Promise<boolean | string>;
71
+ limits?: Partial<Record<ToolName, ToolLimits>>;
72
+ onEvent?: (event: ToolEvent) => void;
73
+ }
74
+ export interface ToolResult {
75
+ text: string;
76
+ }
77
+ export declare function createSandboxTools(box: Sandbox, options?: SandboxToolsOptions): {
78
+ name: "run" | "read_file" | "write_file" | "edit_file" | "start_process" | "process_logs" | "stop_process" | "search" | "list_files" | "changes" | "preview";
79
+ description: string;
80
+ inputSchema: z.ZodObject<{
81
+ command: z.ZodString;
82
+ cwd: z.ZodOptional<z.ZodString>;
83
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
84
+ }, z.core.$strict> | z.ZodObject<{
85
+ path: z.ZodString;
86
+ offset: z.ZodOptional<z.ZodNumber>;
87
+ limit: z.ZodOptional<z.ZodNumber>;
88
+ }, z.core.$strict> | z.ZodObject<{
89
+ path: z.ZodString;
90
+ content: z.ZodString;
91
+ }, z.core.$strict> | z.ZodObject<{
92
+ path: z.ZodString;
93
+ find: z.ZodString;
94
+ replace: z.ZodString;
95
+ }, z.core.$strict> | z.ZodObject<{
96
+ name: z.ZodString;
97
+ command: z.ZodString;
98
+ cwd: z.ZodOptional<z.ZodString>;
99
+ port: z.ZodOptional<z.ZodNumber>;
100
+ }, z.core.$strict> | z.ZodObject<{
101
+ name: z.ZodString;
102
+ cursor: z.ZodOptional<z.ZodString>;
103
+ }, z.core.$strict> | z.ZodObject<{
104
+ name: z.ZodString;
105
+ }, z.core.$strict> | z.ZodObject<{
106
+ query: z.ZodString;
107
+ path: z.ZodOptional<z.ZodString>;
108
+ glob: z.ZodOptional<z.ZodString>;
109
+ }, z.core.$strict> | z.ZodObject<{
110
+ path: z.ZodOptional<z.ZodString>;
111
+ glob: z.ZodOptional<z.ZodString>;
112
+ }, z.core.$strict> | z.ZodObject<{}, z.core.$strict> | z.ZodObject<{
113
+ port: z.ZodNumber;
114
+ path: z.ZodOptional<z.ZodString>;
115
+ }, z.core.$strict>;
116
+ execute(input: unknown, emit?: ((event: ToolEvent) => void) | undefined, signal?: AbortSignal): Promise<ToolResult>;
117
+ }[];
package/dist/tools.js ADDED
@@ -0,0 +1 @@
1
+ import{c,w}from"./chunk-v6y7eqxw.js";import"./chunk-cwx6cc31.js";import"./chunk-t5vr9nd8.js";export{w as createSandboxTools,c as toolSchemas};