@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 @@
1
+ import"../../chunk-t5vr9nd8.js";import{y,x}from"../../chunk-yrsb6hws.js";var k=["requested","nearest","default","source"],{failure:r,boatError:S}=y("sandboxer","Sandboxer","SANDBOXER_API_KEY",!0),g="https://sandboxer.com/api/v1";function j(d={}){let l=d.requestTimeoutMs??90000,u=new Map,c=d.region??process.env.SANDBOXER_REGION;if(c!==void 0&&!/^[a-z][a-z0-9-]{0,31}$/.test(c))throw r("Invalid Sandboxer region","invalid_argument");let h=new Map;function m(e){if(typeof e!=="object"||e===null||!("id"in e)||typeof e.id!=="string"||!("region"in e))return;let n=typeof e.region==="string"?e.region:null,o="regionSource"in e?k.find((t)=>t===e.regionSource)??null:null;h.set(e.id,{region:n,regionSource:o})}async function p(e,n="GET",o,t=l){let s=d.apiKey??process.env.SANDBOXER_API_KEY;if(!s)throw r("Sandboxer API key is missing. Authentication is required. Set SANDBOXER_API_KEY or pass apiKey.","auth");try{let i=await fetch(`${d.baseUrl??g}${e}`,{method:n,headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},...o===void 0?{}:{body:JSON.stringify(o)},signal:AbortSignal.timeout(t)});if(!i.ok){let a=await i.json().catch(()=>{return}),I=typeof a==="object"&&a!==null&&"code"in a?a.code:void 0;throw{response:i,apiCode:I}}return await i.json()}catch(i){throw S(i)}}async function f(e,n){let o=performance.now()+l;while(performance.now()<o){if(n(await p(e,"GET",void 0,Math.max(1,Math.ceil(o-performance.now())))))return;await new Promise((t)=>setTimeout(t,Math.min(250,Math.max(1,Math.ceil(o-performance.now())))))}throw r("Sandboxer operation did not finish before its deadline","timeout")}async function w(e,n){if(!/^[a-z0-9][a-z0-9-]{0,62}$/.test(n))throw r("Invalid snapshot name","invalid_argument",e);try{return await p("/named-snapshots","POST",{sandboxId:e,name:n}),await f(`/named-snapshots/${encodeURIComponent(n)}`,(o)=>{if(typeof o!=="object"||o===null||!("snapshot"in o))throw r("Invalid snapshot response");let t=o.snapshot;if(typeof t!=="object"||t===null||!("status"in t)||!("name"in t)||t.name!==n||!("sourceSandboxId"in t)||t.sourceSandboxId!==e)throw r("Invalid snapshot metadata");if(t.status==="failed")throw r("Sandboxer snapshot failed","provider",e);return t.status==="ready"}),{id:n}}catch(o){throw r(`Sandboxer snapshot ${n} did not complete; inspect this name before retrying`,S(o,e).code,e)}}let b=x(d,{name:"sandboxer",label:"Sandboxer",keyEnv:"SANDBOXER_API_KEY",baseUrl:g,createFrom:!0,pausedStates:["archived","stopped"],fetch:async(e,n)=>{let o=new Headers(n?.headers),t=new URL(typeof e==="string"?e:e instanceof URL?e.href:e.url).pathname;if(n?.method==="DELETE"&&/\/sandboxes\/[^/]+$/.test(t))o.set("X-Sandboxer-Confirm-Delete",decodeURIComponent(t.slice(t.lastIndexOf("/")+1)));let s=n?.body;if(c!==void 0&&n?.method==="POST"&&t.endsWith("/sandboxes")){if(typeof s!=="string")throw r("Sandboxer create body is not JSON","provider");let a=JSON.parse(s);if(typeof a!=="object"||a===null)throw r("Sandboxer create body is not an object","provider");s=JSON.stringify({...a,region:c})}let i=await fetch(e,{...n,headers:o,body:s});if(i.ok&&n?.method!=="DELETE"&&/\/sandboxes(\/[^/]+)?$/.test(t)){let a=await i.clone().json().catch(()=>{return});if(typeof a==="object"&&a!==null){if("sandbox"in a)m(a.sandbox);if("sandboxes"in a&&Array.isArray(a.sandboxes))a.sandboxes.forEach(m)}}return i},downloadMaxBytes:52428800,destroy:async(e,n)=>{let o=u.get(n);if(!o){let{operation:t}=await e.deleteSandbox({sandboxId:n,xAsciiConfirmDelete:n});if(t.targetId!==n||t.kind!=="sandbox")throw r("Invalid deletion operation","outcome_unknown",n);if(t.status==="completed")return;o=t.id,u.set(n,o)}await f(`/deletion-operations/${encodeURIComponent(o)}`,(t)=>{if(typeof t!=="object"||t===null||!("operation"in t))throw r("Invalid deletion response");let s=t.operation;if(typeof s!=="object"||s===null||!("targetId"in s)||s.targetId!==n||!("status"in s))throw r("Invalid deletion metadata");return s.status==="completed"}),u.delete(n)},download:async(e,n,o)=>new Uint8Array(await(await e.artifact({sandboxId:n,path:o})).arrayBuffer()),preview:async(e)=>{let n=new URL(e),o=n.searchParams.get("_token");if(o===null)return{url:e};if(!/^[A-Za-z0-9._~-]+$/.test(o))throw r("Invalid preview token");return n.searchParams.delete("_token"),{url:n.toString(),headers:{Cookie:`__sbx_auth=${o}`}}}});return{...b,capabilities:new Set(["size","pause","snapshot"]),async pause(e){await p(`/sandboxes/${encodeURIComponent(e)}/stop`,"POST"),await f(`/sandboxes/${encodeURIComponent(e)}`,(n)=>{if(typeof n!=="object"||n===null||!("sandbox"in n))throw r("Invalid stop response");let o=n.sandbox;if(typeof o!=="object"||o===null||!("id"in o)||o.id!==e||!("stop"in o))throw r("Invalid stop metadata");let t=o.stop;if(typeof t!=="object"||t===null||!("status"in t))return!1;return t.status==="completed"})},async resume(e){await p(`/sandboxes/${encodeURIComponent(e)}/resume`,"POST"),await f(`/sandboxes/${encodeURIComponent(e)}`,(n)=>{if(typeof n!=="object"||n===null||!("sandbox"in n))throw r("Invalid resume response");let o=n.sandbox;if(typeof o!=="object"||o===null||!("id"in o)||o.id!==e||!("state"in o))throw r("Invalid resume metadata");return o.state==="ready"}),await b.connect(e)},snapshot:(e)=>w(e,`sdk-${crypto.randomUUID()}`),native:(e)=>({...b.native(e),region:null,regionSource:null,...h.get(e),createSnapshot:(n)=>w(e,n)})}}export{j as sandboxer};
@@ -0,0 +1,11 @@
1
+ export declare function bootstrapCommand(options: {
2
+ env?: Record<string, string>;
3
+ installPython?: boolean;
4
+ execution?: boolean;
5
+ }): {
6
+ command: string;
7
+ env: {
8
+ SANDBOX_SDK_BOOTSTRAP: string;
9
+ };
10
+ };
11
+ export declare function checkBootstrap(exitCode: number, id: string): void;
@@ -0,0 +1,3 @@
1
+ import { SandboxError, type ErrorCode } from "../../errors.js";
2
+ export declare function failure(message: string, code?: ErrorCode, sandboxId?: string): SandboxError;
3
+ export declare function superserveError(error: unknown, sandboxId?: string): SandboxError;
@@ -0,0 +1,13 @@
1
+ import type { ExecInput, ExecResult } from "../../provider.js";
2
+ export declare const supervisorPath = "/var/lib/sandbox-sdk-exec-v2.py";
3
+ export declare const supervisor = "import os,sys,marshal,_signal as signal,select,time,fcntl\ndef stdin_file():\n try: return os.fdopen(os.memfd_create('sandbox-sdk-stdin'),'w+b')\n except (AttributeError,OSError):\n import tempfile\n return tempfile.TemporaryFile()\nclass Process:\n def __init__(self,v,env,stdin,session=False):\n if not all(hasattr(os,name) for name in ['posix_spawn','waitstatus_to_exitcode']):\n self.legacy(v,env,stdin,session); return\n directory=os.open('.',os.O_RDONLY)\n r,w=os.pipe(); e,x=os.pipe()\n try:\n os.chdir(v['cwd'])\n error=FileNotFoundError(2,os.strerror(2),'sh')\n signals=[getattr(signal,s) for s in ['SIGPIPE','SIGXFZ','SIGXFSZ'] if hasattr(signal,s)]\n for path in os.get_exec_path(env):\n try:\n self.pid=os.posix_spawn(os.path.join(path,'sh'),['sh','-c',v['command']],env,file_actions=[(os.POSIX_SPAWN_DUP2,stdin.fileno(),0),(os.POSIX_SPAWN_DUP2,w,1),(os.POSIX_SPAWN_DUP2,x,2)],setsid=session,setsigdef=signals)\n break\n except (FileNotFoundError,NotADirectoryError): pass\n except OSError as failure:\n if failure.errno==38: raise NotImplementedError()\n error=failure\n else: raise error\n except NotImplementedError:\n os.close(r); os.close(e)\n self.legacy(v,env,stdin,session); return\n except BaseException:\n os.close(r); os.close(e); raise\n finally:\n os.fchdir(directory); os.close(directory); os.close(w); os.close(x)\n self.stdout=os.fdopen(r,'rb',buffering=0); self.stderr=os.fdopen(e,'rb',buffering=0)\n self.code=None\n def legacy(self,v,env,stdin,session):\n import subprocess\n self.child=subprocess.Popen(['sh','-c',v['command']],cwd=v['cwd'],env=env,stdin=stdin,stdout=subprocess.PIPE,stderr=subprocess.PIPE,start_new_session=session)\n self.pid=self.child.pid; self.stdout=self.child.stdout; self.stderr=self.child.stderr\n def poll(self):\n if hasattr(self,'child'): return self.child.poll()\n if self.code is None:\n pid,status=os.waitpid(self.pid,os.WNOHANG)\n if pid: self.code=os.waitstatus_to_exitcode(status)\n return self.code\n def wait(self):\n if hasattr(self,'child'): return self.child.wait()\n if self.code is None: self.code=os.waitstatus_to_exitcode(os.waitpid(self.pid,0)[1])\n return self.code\n\nroot=sys.argv[1]\nv=marshal.loads(bytes.fromhex(sys.argv[2])) if len(sys.argv)>2 else marshal.loads(bytes.fromhex(sys.stdin.read()))\nif 'envKeys' in v: v['env']={k:os.environ[k] for k in v['envKeys']}\nos.makedirs(root,mode=0o700,exist_ok=True)\n# Keep small completed responses until their request deadline has passed.\n# Sweep expired responses inline, so no ack RPC or background daemon is needed.\nfor entry in os.scandir(os.path.dirname(root)):\n if not entry.name.startswith('run-') or entry.path==root: continue\n try:\n with open(entry.path+'/expires') as f: expired=time.time()>float(f.read())\n if expired:\n import shutil\n shutil.rmtree(entry.path,ignore_errors=True)\n except (FileNotFoundError,NotADirectoryError): pass\na=v.get('activityPath')\ntimed=False\ncode=127\ntry:\n if a:\n os.makedirs(os.path.dirname(a),exist_ok=True)\n with open(a,'w') as f: f.write('1')\n try:\n with open('/var/lib/sandbox-sdk-env.json') as f:\n import json\n saved=json.load(f)\n except FileNotFoundError: saved={}\n if os.environ.get('SANDBOX_SDK_BOOTSTRAP') not in (None,'{}'):\n bootstrap=os.environ.pop('SANDBOX_SDK_BOOTSTRAP')\n if bootstrap=='{\"env\":{}}': initial={}\n else:\n import json\n initial=json.loads(bootstrap).get('env')\n if initial is not None: saved=initial\n if initial is not None and (initial or os.path.exists('/var/lib/sandbox-sdk-env.json')):\n import json\n os.makedirs(os.path.dirname('/var/lib/sandbox-sdk-env.json'),exist_ok=True)\n tmp='/var/lib/sandbox-sdk-env.json.'+str(os.getpid())\n fd=os.open(tmp,os.O_WRONLY|os.O_CREAT|os.O_EXCL,0o600)\n with os.fdopen(fd,'w') as f: json.dump(saved,f)\n os.replace(tmp,'/var/lib/sandbox-sdk-env.json')\n env={**os.environ,**saved,**v['env']}\n with stdin_file() as f, open(root+'/out','wb',buffering=0) as out:\n f.write(v.get('stdin','').encode()); f.seek(0)\n with open(root+'/lock','w') as lock:\n fcntl.flock(lock,fcntl.LOCK_EX)\n if os.path.exists(root+'/cancelled'): timed=True; p=None\n else:\n try: p=Process(v,env,f,True)\n except OSError as e:\n out.write(('[\"stderr\",\"'+str(e).encode().hex()+'\"]\\n').encode()); p=None\n if p:\n with open(root+'/pid','w') as m: m.write(str(p.pid))\n if p:\n stdout=p.stdout.fileno(); streams={stdout:'stdout',p.stderr.fileno():'stderr'}\n end=time.monotonic()+v['timeoutMs']/1000\n while streams or p.poll() is None:\n if not timed and time.monotonic()>=end:\n timed=True\n try: os.killpg(p.pid,signal.SIGKILL)\n except ProcessLookupError: pass\n for fd in select.select(list(streams),[],[],.01)[0]:\n data=os.read(fd,8192)\n if data: out.write(('[\"'+streams[fd]+'\",\"'+data.hex()+'\"]\\n').encode())\n else: del streams[fd]; (p.stdout if fd==stdout else p.stderr).close()\n code=p.wait()\n timed=timed or os.path.exists(root+'/cancelled')\n result='{\"exitCode\":%s,\"timedOut\":%s}'%('null' if timed else str(code if code>=0 else 128-code),str(timed).lower())\n with open(root+'/exit','w') as f: f.write(result)\n size=os.path.getsize(root+'/out')\n with open(root+'/out','rb') as f:\n chunks=[line.decode().strip() for _,line in zip(range(3),f)]\n offset=f.tell()\n if offset==size and not timed:\n with open(root+'/expires','w') as f: f.write(str(time.time()+v['timeoutMs']/1000+5))\n if timed and not p:\n import shutil\n shutil.rmtree(root)\n print('{\"result\":%s,\"chunks\":[%s],\"offset\":%d,\"size\":%d}'%(result,','.join(chunks),offset,size))\nfinally:\n if a:\n try: os.unlink(a)\n except FileNotFoundError: pass\n";
4
+ export interface ExecutionGuest {
5
+ exec(command: string, stdin: string, timeoutMs: number, signal?: AbortSignal): Promise<string>;
6
+ run?(command: string, env: Record<string, string>, timeoutMs: number, signal?: AbortSignal): Promise<string>;
7
+ raw(command: string): Promise<string>;
8
+ read(path: string, range: {
9
+ offset: number;
10
+ length: number;
11
+ }, signal?: AbortSignal): Promise<Uint8Array>;
12
+ }
13
+ export declare function execute(g: ExecutionGuest, input: ExecInput): Promise<ExecResult>;
@@ -0,0 +1,23 @@
1
+ /** @superserve/sdk 0.11.4; https://docs.superserve.ai/sdk-reference/sandbox */
2
+ import type { Sandbox } from "@superserve/sdk";
3
+ import type { Provider } from "../../provider.js";
4
+ export interface SuperserveOptions {
5
+ /** Defaults to SUPERSERVE_API_KEY; required on first use. */
6
+ apiKey?: string;
7
+ /** Defaults to SUPERSERVE_BASE_URL, then https://api.superserve.ai. */
8
+ baseUrl?: string;
9
+ /** Native template name or UUID. Defaults to superserve/node-22.
10
+ * Must include git, tar, and nohup. Python 3 is required unless installPython is enabled.
11
+ * Template-owned resources cannot be overridden by the SDK size option.
12
+ */
13
+ template?: string;
14
+ /** Allow apt to install missing Python 3, pip and venv on first use.
15
+ * Defaults to false: a template without Python fails with unsupported on first use.
16
+ * The public default superserve/node-22 currently requires this opt-in.
17
+ */
18
+ installPython?: boolean;
19
+ /** Native request deadline. Default: 90 s. */
20
+ requestTimeoutMs?: number;
21
+ }
22
+ export type SuperserveProvider = Provider<Sandbox, "pause" | "snapshot">;
23
+ export declare function superserve(options?: SuperserveOptions): SuperserveProvider;
@@ -0,0 +1,126 @@
1
+ import{m}from"../../chunk-z9rjzn28.js";import{a,n,d,h}from"../../chunk-exv1kf89.js";import{o}from"../../chunk-yed8mvs7.js";import{e}from"../../chunk-gc8zdxyb.js";function f(i,u="provider",p){return new e(u==="auth"?i:`${i}. Check superserve configuration and retry.`,{code:u,provider:"superserve",sandboxId:p})}function K(i,u){if(i instanceof e)return i;let p=typeof i==="object"&&i!==null&&"statusCode"in i?i.statusCode:void 0,b={400:"invalid_argument",401:"auth",403:"auth",404:"not_found",408:"timeout",409:"unavailable",422:"invalid_argument",429:"quota",500:"unavailable",502:"unavailable",503:"unavailable",504:"timeout"},S={AuthenticationError:"auth",NotFoundError:"not_found",ValidationError:"invalid_argument",TimeoutError:"timeout",AbortError:"aborted",ConflictError:"unavailable"};return f("Superserve request failed",typeof p==="number"?b[p]??"provider":i instanceof Error?S[i.name]??"provider":"provider",u)}var U="/var/lib/sandbox-sdk-exec-v2.py",B=`import os,sys,marshal,_signal as signal,select,time,fcntl
2
+ ${d}
3
+ root=sys.argv[1]
4
+ v=marshal.loads(bytes.fromhex(sys.argv[2])) if len(sys.argv)>2 else marshal.loads(bytes.fromhex(sys.stdin.read()))
5
+ if 'envKeys' in v: v['env']={k:os.environ[k] for k in v['envKeys']}
6
+ os.makedirs(root,mode=0o700,exist_ok=True)
7
+ # Keep small completed responses until their request deadline has passed.
8
+ # Sweep expired responses inline, so no ack RPC or background daemon is needed.
9
+ for entry in os.scandir(os.path.dirname(root)):
10
+ if not entry.name.startswith('run-') or entry.path==root: continue
11
+ try:
12
+ with open(entry.path+'/expires') as f: expired=time.time()>float(f.read())
13
+ if expired:
14
+ import shutil
15
+ shutil.rmtree(entry.path,ignore_errors=True)
16
+ except (FileNotFoundError,NotADirectoryError): pass
17
+ a=v.get('activityPath')
18
+ timed=False
19
+ code=127
20
+ try:
21
+ if a:
22
+ os.makedirs(os.path.dirname(a),exist_ok=True)
23
+ with open(a,'w') as f: f.write('1')
24
+ try:
25
+ with open('/var/lib/sandbox-sdk-env.json') as f:
26
+ import json
27
+ saved=json.load(f)
28
+ except FileNotFoundError: saved={}
29
+ if os.environ.get('SANDBOX_SDK_BOOTSTRAP') not in (None,'{}'):
30
+ bootstrap=os.environ.pop('SANDBOX_SDK_BOOTSTRAP')
31
+ if bootstrap=='{"env":{}}': initial={}
32
+ else:
33
+ import json
34
+ initial=json.loads(bootstrap).get('env')
35
+ if initial is not None: saved=initial
36
+ if initial is not None and (initial or os.path.exists('/var/lib/sandbox-sdk-env.json')):
37
+ import json
38
+ os.makedirs(os.path.dirname('/var/lib/sandbox-sdk-env.json'),exist_ok=True)
39
+ tmp='/var/lib/sandbox-sdk-env.json.'+str(os.getpid())
40
+ fd=os.open(tmp,os.O_WRONLY|os.O_CREAT|os.O_EXCL,0o600)
41
+ with os.fdopen(fd,'w') as f: json.dump(saved,f)
42
+ os.replace(tmp,'/var/lib/sandbox-sdk-env.json')
43
+ env={**os.environ,**saved,**v['env']}
44
+ with stdin_file() as f, open(root+'/out','wb',buffering=0) as out:
45
+ f.write(v.get('stdin','').encode()); f.seek(0)
46
+ with open(root+'/lock','w') as lock:
47
+ fcntl.flock(lock,fcntl.LOCK_EX)
48
+ if os.path.exists(root+'/cancelled'): timed=True; p=None
49
+ else:
50
+ try: p=Process(v,env,f,True)
51
+ except OSError as e:
52
+ out.write(('["stderr","'+str(e).encode().hex()+'"]\\n').encode()); p=None
53
+ if p:
54
+ with open(root+'/pid','w') as m: m.write(str(p.pid))
55
+ if p:
56
+ stdout=p.stdout.fileno(); streams={stdout:'stdout',p.stderr.fileno():'stderr'}
57
+ end=time.monotonic()+v['timeoutMs']/1000
58
+ while streams or p.poll() is None:
59
+ if not timed and time.monotonic()>=end:
60
+ timed=True
61
+ try: os.killpg(p.pid,signal.SIGKILL)
62
+ except ProcessLookupError: pass
63
+ for fd in select.select(list(streams),[],[],.01)[0]:
64
+ data=os.read(fd,8192)
65
+ if data: out.write(('["'+streams[fd]+'","'+data.hex()+'"]\\n').encode())
66
+ else: del streams[fd]; (p.stdout if fd==stdout else p.stderr).close()
67
+ code=p.wait()
68
+ timed=timed or os.path.exists(root+'/cancelled')
69
+ result='{"exitCode":%s,"timedOut":%s}'%('null' if timed else str(code if code>=0 else 128-code),str(timed).lower())
70
+ with open(root+'/exit','w') as f: f.write(result)
71
+ size=os.path.getsize(root+'/out')
72
+ with open(root+'/out','rb') as f:
73
+ chunks=[line.decode().strip() for _,line in zip(range(3),f)]
74
+ offset=f.tell()
75
+ if offset==size and not timed:
76
+ with open(root+'/expires','w') as f: f.write(str(time.time()+v['timeoutMs']/1000+5))
77
+ if timed and not p:
78
+ import shutil
79
+ shutil.rmtree(root)
80
+ print('{"result":%s,"chunks":[%s],"offset":%d,"size":%d}'%(result,','.join(chunks),offset,size))
81
+ finally:
82
+ if a:
83
+ try: os.unlink(a)
84
+ except FileNotFoundError: pass
85
+ `;async function G(i,u){if(u.signal?.aborted)return{exitCode:null,timedOut:!0};let p=`/tmp/.sandbox-sdk/run-${crypto.randomUUID()}`,b=new AbortController,S=setTimeout(()=>b.abort(),u.timeoutMs),P=u.signal?AbortSignal.any([u.signal,b.signal]):b.signal,E=0,v={stdout:new TextDecoder,stderr:new TextDecoder};async function k(){let x=await i.raw(n(`import os,sys,fcntl,signal,shutil,time,json
86
+ root=sys.argv[1]
87
+ os.makedirs(root,mode=0o700,exist_ok=True)
88
+ with open(root+'/lock','w') as lock:
89
+ fcntl.flock(lock,fcntl.LOCK_EX)
90
+ with open(root+'/cancelled','w') as f: f.write('1')
91
+ try:
92
+ with open(root+'/pid') as f: pid=int(f.read())
93
+ except FileNotFoundError: pid=None
94
+ if pid and not os.path.exists(root+'/exit'):
95
+ try: os.killpg(pid,signal.SIGKILL)
96
+ except ProcessLookupError: pass
97
+ # Wait for reaping and pipe drainage before recovering the bounded output tail.
98
+ end=time.monotonic()+2
99
+ while pid and not os.path.exists(root+'/exit') and time.monotonic()<end: time.sleep(.01)
100
+ if pid and not os.path.exists(root+'/exit'): sys.exit(1)
101
+ chunks=[]
102
+ start=int(sys.argv[2])
103
+ try:
104
+ with open(root+'/out','rb') as f:
105
+ size=os.fstat(f.fileno()).st_size
106
+ delivered=int(sys.argv[2])
107
+ start=max(delivered,size-65536); f.seek(start)
108
+ if start>delivered: f.readline(); start=f.tell()
109
+ chunks=[json.loads(line) for line in f]
110
+ except FileNotFoundError: pass
111
+ print(json.dumps(dict(chunks=chunks,offset=start)))
112
+ # Retain a tombstone if a delayed supervisor can still arrive.
113
+ if pid: shutil.rmtree(root,ignore_errors=True)`,p,String(E))),s=JSON.parse(x);if(!s||typeof s!=="object"||!("chunks"in s)||!Array.isArray(s.chunks)||s.chunks.length>65536||!("offset"in s)||typeof s.offset!=="number"||!Number.isSafeInteger(s.offset)||s.offset<E)throw f("Invalid cancellation output");if(s.offset!==E)z(v,u);for(let N of s.chunks)q(N,v,u);z(v,u)}let R=!1;async function T(){try{await k()}catch{throw f("Process cancellation could not be confirmed","outcome_unknown")}return R=!1,{exitCode:null,timedOut:!0}}try{let x;try{let w={command:u.command,cwd:u.cwd,timeoutMs:u.timeoutMs,activityPath:u.activityPath},t=m({...w,envKeys:Object.keys(u.env)}).toString("hex"),r=`python3 -S -c ${a(B)} ${a(p)} ${a(t)}`;if(i.run&&u.stdin===void 0&&Buffer.byteLength(r)<98304)x=await i.run(r,u.env,u.timeoutMs+5000,P);else x=await i.exec(`python3 -S -c ${a(B)} ${a(p)}`,m({...w,env:u.env,stdin:u.stdin??""}).toString("hex"),u.timeoutMs+5000,P)}catch(w){if(w instanceof e&&w.code==="unsupported")throw w;try{await k()}catch{throw f("Process cancellation could not be confirmed","outcome_unknown")}if(P.aborted)return{exitCode:null,timedOut:!0};throw w}let s=JSON.parse(x);if(!s||typeof s!=="object"||!("result"in s)||!s.result||typeof s.result!=="object"||!("exitCode"in s.result)||s.result.exitCode!==null&&typeof s.result.exitCode!=="number"||!("timedOut"in s.result)||typeof s.result.timedOut!=="boolean"||!("chunks"in s)||!Array.isArray(s.chunks)||s.chunks.length>3||!("offset"in s)||typeof s.offset!=="number"||!Number.isSafeInteger(s.offset)||s.offset<0||!("size"in s)||typeof s.size!=="number"||!Number.isSafeInteger(s.size)||s.size<s.offset)throw f("Invalid command response");let N=(w)=>q(w,v,u);if(R=s.offset<s.size||s.result.timedOut,P.aborted)return await T();s.chunks.forEach(N),E=s.offset;let A=s.offset,I="";while(A<s.size){if(P.aborted)return await T();let w;try{w=await i.read(p+"/out",{offset:A,length:Math.min(65536,s.size-A)},P)}catch(r){if(P.aborted)return await T();throw r}if(P.aborted)return await T();if(!w.length)throw f("Incomplete output read");A+=w.length,I+=new TextDecoder().decode(w);let t=I.split(`
114
+ `);I=t.pop()??"",t.forEach((r)=>{N(JSON.parse(r)),E+=Buffer.byteLength(r)+1})}if(I)throw f("Incomplete output record");return z(v,u),{exitCode:s.result.exitCode,timedOut:s.result.timedOut}}finally{if(clearTimeout(S),R)await i.raw(`rm -rf -- ${a(p)}`).catch(()=>{})}}function q(i,u,p){if(!Array.isArray(i)||i[0]!=="stdout"&&i[0]!=="stderr"||typeof i[1]!=="string"||i[1].length>21848)throw f("Invalid output record");let b=i[0]==="stdout"?"stdout":"stderr",S=u[b].decode(Buffer.from(i[1],"hex"),{stream:!0});if(S)p.onChunk({stream:b,text:S})}function z(i,u){for(let p of["stdout","stderr"]){let b=i[p].decode();if(b)u.onChunk({stream:p,text:b})}}var Y=n(`import os,json,shutil,sys,tempfile
115
+ v=json.loads(os.environ.pop('SANDBOX_SDK_BOOTSTRAP'))
116
+ if any(shutil.which(tool) is None for tool in ('git','tar','nohup')): sys.exit(79)
117
+ os.makedirs('/workspace',exist_ok=True)
118
+ os.makedirs('/tmp/.sandbox-sdk',exist_ok=True)
119
+ os.makedirs(os.path.dirname('${U}'),exist_ok=True)
120
+ if v.get('env') is not None:
121
+ with tempfile.NamedTemporaryFile(mode='w',dir=os.path.dirname('/var/lib/sandbox-sdk-env.json'),delete=False) as f:
122
+ json.dump(v['env'],f)
123
+ os.replace(f.name,'/var/lib/sandbox-sdk-env.json')
124
+ if v.get('supervisor') is not None:
125
+ with tempfile.NamedTemporaryFile(mode='w',dir=os.path.dirname('/var/lib/sandbox-sdk-env.json'),delete=False) as f: f.write(v['supervisor'])
126
+ os.replace(f.name,'${U}')`);function X(i){let u=i.installPython===!0?"export DEBIAN_FRONTEND=noninteractive; apt-get update -qq && apt-get install -y -qq python3 python3-venv python3-pip >/dev/null || exit 1":"exit 78";if(i.execution)return{command:`if ! command -v python3 >/dev/null 2>&1; then ${u}; fi; command -v git tar nohup >/dev/null || exit 79; mkdir -p /workspace /tmp/.sandbox-sdk`,env:{SANDBOX_SDK_BOOTSTRAP:JSON.stringify({env:i.env})}};return{command:`if ! command -v python3 >/dev/null 2>&1; then ${u}; fi; ${Y}`,env:{SANDBOX_SDK_BOOTSTRAP:JSON.stringify({env:i.env,supervisor:i.execution?B:void 0})}}}function L(i,u){if(i===78)throw f("Template lacks python3; use a template with Python 3 or explicitly set installPython: true to allow apt installation","unsupported",u);if(i===79)throw f("Template must include git, tar and nohup","unsupported",u)}function pe(i={}){let u=new Map,p=new Map,b=new Set,S=new Set,P=new Map,E=()=>{let t=i.apiKey??process.env.SUPERSERVE_API_KEY;if(!t?.trim())throw f("Superserve API key is missing. Authentication is required. Set SUPERSERVE_API_KEY or pass apiKey.","auth");return{apiKey:t,baseUrl:i.baseUrl}},v=i.requestTimeoutMs??90000;async function k(t,r){try{return await r()}catch(c){throw K(c,t)}}let R;async function T(){return E(),R??=o("@superserve/sdk","superserve",()=>import("@superserve/sdk"))}async function x(t){let r=u.get(t);if(!r)r=await(await T()).Sandbox.connect(t,{...E(),signal:AbortSignal.timeout(v)}),u.set(t,r);return r}function s(t){if(t.metadata["sandbox-sdk"]!=="1")throw f("Not an SDK sandbox","not_found",t.id);let r={};for(let[c,l]of Object.entries(t.metadata))if(c.startsWith("label:"))r[c.slice(6)]=l;return{id:t.id,key:t.metadata["sdk-key"],labels:r,createdAt:t.createdAt.getTime(),state:t.status==="active"?"running":["paused","pausing"].includes(t.status)?"paused":"stopped"}}function N(t,r,c){if(c?S.has(t):b.has(t))return{command:r,env:{}};let l=X({env:p.get(t),installPython:i.installPython,execution:c});return{command:`${l.command} && ${r}`,env:l.env}}async function A(t,r,c,l=!1){let y=N(t,r,l),g=await(await x(t)).commands.run(y.command,{...c,env:{...c.env,...y.env}});if(L(g.exitCode,t),g.exitCode===0){if(b.add(t),l)S.add(t)}return g}async function I(t){let r=await x(t);return{name:"superserve",raw:async(c)=>{let l=await A(t,c,{timeoutMs:v});if(l.exitCode!==0)throw f("Guest operation failed","provider",t);return l.stdout},upload:async(c,l)=>r.files.write(c,l,{timeoutMs:v}),read:async(c,l)=>{try{if(l){let y=await A(t,n("import sys,base64; f=open(sys.argv[1],'rb'); f.seek(int(sys.argv[2])); print(base64.b64encode(f.read(int(sys.argv[3]))).decode())",c,String(l.offset),String(l.length)),{timeoutMs:v});if(y.exitCode!==0)throw f("File range read failed","file_not_found",t);return new Uint8Array(Buffer.from(y.stdout.trim(),"base64"))}return await r.files.read(c,{timeoutMs:v})}catch(y){if(K(y).code==="not_found")throw f("File does not exist","file_not_found",t);throw y}}}}let w={name:"superserve",keyMode:"best-effort",inlineActivity:!0,capabilities:new Set(["pause","snapshot"]),create:(t)=>k(void 0,async()=>{if(t.size!==void 0||t.idleTimeoutMs!==void 0||t.timeoutMs!==void 0||t.network&&t.network!=="all")throw f("Template resources cannot be overridden; SDK idle/lifetime limits and network restrictions are not implemented","unsupported");if(!Number.isSafeInteger(v)||v<=0)throw f("requestTimeoutMs must be a positive integer","invalid_argument");if(t.signal?.aborted)throw f("Creation aborted","aborted");let r={"sandbox-sdk":"1",...t.key!==void 0?{"sdk-key":t.key}:{}};for(let[g,_]of Object.entries(t.labels))r[`label:${g}`]=_;let c=Date.now(),l,y=await T();try{l=await y.Sandbox.create({...E(),name:`sbxsdk-${crypto.randomUUID()}`,metadata:r,envVars:t.env,previewAccess:"private",...t.from?{fromSnapshot:t.from}:{fromTemplate:i.template??"superserve/node-22"},signal:AbortSignal.timeout(v)})}catch(g){let _=K(g);if(["timeout","unavailable","aborted"].includes(_.code))throw f("Creation outcome unknown; inspect SDK-labelled sandboxes before retrying","outcome_unknown");throw _}u.set(l.id,l);try{if(t.signal?.aborted)throw f("Creation aborted","aborted");if(p.set(l.id,{...t.env}),t.from){if((await l.getInfo()).timeoutSeconds!==void 0)await l.update({timeoutSeconds:null})}return{id:l.id,key:t.key,labels:{...t.labels,"sandbox-sdk":"1"},createdAt:c,state:"running"}}catch(g){throw await w.destroy(l.id),g}}),list:(t)=>k(void 0,async()=>(await(await T()).Sandbox.list({...E(),metadata:{"sandbox-sdk":"1"},signal:AbortSignal.timeout(v)})).filter((c)=>c.status!=="deleted").map(s).filter((c)=>Object.entries(t).every(([l,y])=>c.labels[l]===y))),async find(t){let r=(await w.list({})).find((c)=>c.key===t);return r?w.connect(r.id):void 0},connect:(t)=>k(t,async()=>s(await(await x(t)).getInfo())),destroy(t){let r=P.get(t);if(!r)r=k(t,async()=>{await(await T()).Sandbox.killById(t,{...E(),signal:AbortSignal.timeout(v)}),u.delete(t),p.delete(t),S.delete(t),b.delete(t)}),P.set(t,r),r.finally(()=>P.delete(t)).catch(()=>{});return r},async close(t){u.delete(t),p.delete(t),S.delete(t),b.delete(t)},exec:(t,r)=>k(t,async()=>{let c=await x(t),l=await I(t);return G({raw:l.raw,read:async(y,g)=>{let _=await l.raw(n("import sys,base64; f=open(sys.argv[1],'rb'); f.seek(int(sys.argv[2])); print(base64.b64encode(f.read(int(sys.argv[3]))).decode())",y,String(g.offset),String(g.length)));return new Uint8Array(Buffer.from(_.trim(),"base64"))},run:async(y,g,_,O)=>{if(O?.aborted)throw f("Command interrupted","aborted",t);let M=()=>{},j=new Promise((D,C)=>{M=()=>C(f("Command interrupted","aborted",t))});O?.addEventListener("abort",M,{once:!0});try{let D=A(t,y,{env:g,timeoutMs:_},!0),C=await Promise.race([D,j]);if(C.exitCode!==0||C.truncated)throw f("Supervisor failed","provider",t);return C.stdout}finally{O?.removeEventListener("abort",M)}},exec:async(y,g,_,O)=>{let M=N(t,y,!0),j=await c.commands.spawn(M.command,{env:M.env,startupSignal:O,timeoutMs:_}),D=()=>{},C=!1;try{j.stdin.write(g),j.stdin.close();let J=new Promise((H,W)=>{D=()=>{W(f("Command interrupted","aborted",t))}});if(O?.addEventListener("abort",D,{once:!0}),O?.aborted)D();let V=j.wait().finally(()=>j.close());C=!0;let F=await Promise.race([V,J]);if(L(F.exitCode,t),F.exitCode!==0)throw f("Supervisor failed","provider",t);return S.add(t),b.add(t),F.stdout}finally{if(O?.removeEventListener("abort",D),!C)await j.close()}}},r)}),files:h(I,k),previewUrl:(t,r)=>k(t,async()=>{let c=await x(t);await c.publishPreviewPort(r,{access:"private"});let l=await c.getPreviewToken(r);return{url:c.getPreviewUrl(r),headers:{[l.header]:l.token},...l.expiresAt?{expiresAt:l.expiresAt.getTime()}:{}}}),pause:(t)=>k(t,async()=>{await(await x(t)).pause({wait:!0,timeoutMs:v})}),resume:(t)=>k(t,async()=>{await(await x(t)).resume({timeoutMs:v})}),snapshot:(t)=>k(t,async()=>{let r=`sbxsdk-${crypto.randomUUID()}`;try{return{id:(await(await x(t)).snapshot({name:r,idempotencyKey:r,timeoutMs:v})).id}}catch{throw f(`Snapshot outcome unknown; recover snapshot named ${r} from source ${t}`,"outcome_unknown",t)}}),native(t){let r=u.get(t);if(!r)throw f("Connect the sandbox first","not_found",t);return r}};return w}export{pe as superserve};
@@ -0,0 +1,2 @@
1
+ /** Race as well as abort: an unresponsive transport must not hold the caller. */
2
+ export declare function deadline<T>(action: (signal: AbortSignal) => PromiseLike<T>, timeoutMs: number, signal?: AbortSignal): Promise<T>;
@@ -0,0 +1,10 @@
1
+ import { SandboxError, type ErrorCode } from "../../errors.js";
2
+ export type TenkiOperation = "request" | "create" | "list" | "connect" | "destroy" | "exec" | "files" | "preview" | "pause" | "resume" | "snapshot";
3
+ export declare function tenkiError(error: unknown, sandboxId?: string, operation?: TenkiOperation, secrets?: string[]): SandboxError;
4
+ export type CreateStage = "waitReady" | "validate" | "prepare";
5
+ export declare function createError(error: unknown, context: {
6
+ id: string;
7
+ state: string;
8
+ stage: CreateStage;
9
+ }, secrets: string[], cleanupError?: unknown): SandboxError;
10
+ export declare function failure(message: string, code?: ErrorCode): SandboxError;
@@ -0,0 +1,4 @@
1
+ import type { Session } from "@tenkicloud/sandbox";
2
+ import type { ExecInput } from "../../provider.js";
3
+ export declare function raw(box: Session, argv: string[], stdin?: Uint8Array, timeoutMs?: number, signal?: AbortSignal): Promise<string>;
4
+ export declare function execute(box: Session, input: ExecInput, workspaceRoot?: (root: string, stable: boolean) => void): Promise<import("../../provider.js").ExecResult>;
@@ -0,0 +1,3 @@
1
+ import type { Session } from "@tenkicloud/sandbox";
2
+ import type { ProviderFiles } from "../../provider.js";
3
+ export declare function files(get: (id: string) => Promise<Session>, safe: <T>(id: string, action: () => Promise<T>) => Promise<T>, workspaceRoot: (box: Session) => string | undefined, invalidateWorkspace: (box: Session) => void): ProviderFiles;
@@ -0,0 +1,28 @@
1
+ /** Official API sources (checked against @tenkicloud/sandbox 1.4.1 types):
2
+ * https://tenki.cloud/docs/sandbox/sdk.md
3
+ * https://tenki.cloud/docs/sandbox/sessions.md
4
+ * https://tenki.cloud/docs/sandbox/snapshots.md
5
+ * https://tenki.cloud/docs/sandbox/base-image.md
6
+ * https://www.npmjs.com/package/@tenkicloud/sandbox
7
+ */
8
+ import type { Session } from "@tenkicloud/sandbox";
9
+ import type { Provider } from "../../provider.js";
10
+ export interface TenkiOptions {
11
+ /** Defaults to TENKI_AUTH_TOKEN, then TENKI_API_KEY; required on first use. */
12
+ apiKey?: string;
13
+ /** @deprecated Use apiKey. Removed in 1.0.0. */
14
+ authToken?: string;
15
+ /** Defaults to TENKI_API_ENDPOINT, then TENKI_API_URL, then https://api.tenki.cloud. */
16
+ baseUrl?: string;
17
+ /** Base image; default: the Tenki service default image. */
18
+ image?: string;
19
+ /** Control-plane deadline. Default: 30 s. */
20
+ requestTimeoutMs?: number;
21
+ /** Readiness budget, 1–900000 ms. Default: 180 s; snapshots: 300 s. */
22
+ waitTimeoutMs?: number;
23
+ /** Resume RPC deadline, separate from readiness. Default: 180 seconds; maximum: 15 minutes. */
24
+ resumeTimeoutMs?: number;
25
+ }
26
+ export type TenkiCapability = "pause" | "snapshot" | "size" | "network-policy";
27
+ export type TenkiProvider = Provider<Session, TenkiCapability>;
28
+ export declare function tenki(options?: TenkiOptions): TenkiProvider;
@@ -0,0 +1,172 @@
1
+ import{m}from"../../chunk-z9rjzn28.js";import{d}from"../../chunk-exv1kf89.js";import{o}from"../../chunk-yed8mvs7.js";import{e}from"../../chunk-gc8zdxyb.js";var K={MissingAuthTokenError:"auth",InvalidAuthTokenError:"auth",UnauthorizedError:"auth",PermissionDeniedError:"auth",SessionNotFoundError:"not_found",SessionExpiredError:"not_found",SessionTerminatedError:"not_found",SnapshotNotFoundError:"not_found",FileNotFoundError:"file_not_found",QuotaExceededError:"quota",PortLimitExceededError:"quota",RateLimitedError:"quota",CapacityUnavailableError:"unavailable",SSHUnavailableError:"unavailable",DataPlaneNotReadyError:"unavailable",CommandTimeoutError:"timeout",SnapshotWaitTimeoutError:"timeout",PrimitiveTimeoutError:"timeout",InvalidResourceConfigError:"invalid_argument",InvalidStateError:"invalid_argument",InboundDisabledError:"unsupported",CapabilityUnavailableError:"unsupported"};function G(t,s){let f={},h=new Set;for(let k=0;k<4&&t&&typeof t==="object"&&!h.has(t);k++){if(h.add(t),t instanceof Error&&(Object.hasOwn(K,t.name)||["ResumeFailedError","PauseFailedError","WaitReadyFailedError","StreamClosedError"].includes(t.name)))f.nativeName??=t.name;let x="status"in t?t.status:("statusCode"in t)?t.statusCode:void 0;if(typeof x==="number"&&Number.isInteger(x)&&x>=100&&x<=599)f.status??=x;let u="code"in t?t.code:void 0;if(typeof u==="number"&&Number.isInteger(u)&&u>=0&&u<=16||typeof u==="string"&&/^[A-Z][A-Z0-9_]{0,63}$/.test(u)&&!s.some((p)=>u.includes(p)))f.providerCode??=u;let T="metadata"in t?t.metadata:void 0,c="requestId"in t?t.requestId:T instanceof Headers?T.get("x-request-id")??T.get("request-id"):void 0;if(typeof c==="string"&&/^[A-Za-z0-9_-]{1,128}$/.test(c)&&!s.some((p)=>c.includes(p)))f.requestId??=c;t="cause"in t?t.cause:void 0}return f}function A(t,s,f="request",h=[]){if(t instanceof e)return t;let k=t instanceof Error?t.name:"",u=Object.hasOwn(K,k)?K[k]??"provider":"provider",T=[...h,process.env.TENKI_API_KEY??"",process.env.TENKI_AUTH_TOKEN??""].filter(Boolean),c={operation:f,...G(t,T)},p=Object.assign(Error(JSON.stringify(c)),c);return new e(`Tenki request failed (${u}). The provider could not complete the operation. Check credentials, quotas, and session state.`,{code:u,provider:"tenki",sandboxId:s,cause:p,retryable:["quota","unavailable","timeout"].includes(u)})}function B(t,s,f,h){let k=A(t,s.id,"create",f),x=[...f,process.env.TENKI_API_KEY??"",process.env.TENKI_AUTH_TOKEN??""].filter(Boolean),u={operation:"create",admittedByThisCall:!0,stage:s.stage,.../^[0-9a-f-]{36}$/i.test(s.id)&&!x.some((T)=>s.id.includes(T))?{sandboxId:s.id}:{},.../^[A-Z_]{1,32}$/.test(s.state)?{state:s.state}:{},...G(t,x),...h!==void 0?{cleanupCode:A(h).code}:{}};return new e(k.message,{code:k.code,provider:"tenki",sandboxId:s.id,retryable:k.retryable,hint:k.hint,cause:Object.assign(Error(JSON.stringify(u)),u)})}function l(t,s="provider"){return new e(s==="auth"?t:`${t}. Tenki could not honor the request. Check the provider options.`,{code:s,provider:"tenki"})}async function N(t,s,f){let h=new AbortController,k=()=>{},x=new Promise((T,c)=>{k=()=>{let p=l(f?.aborted?"Request aborted":"Request deadline exceeded",f?.aborted?"aborted":"timeout");h.abort(p),c(p)}}),u=setTimeout(k,s);f?.addEventListener("abort",k,{once:!0});try{if(f?.aborted)k();return await Promise.race([x,Promise.resolve().then(()=>(h.signal.throwIfAborted(),t(h.signal)))])}finally{clearTimeout(u),f?.removeEventListener("abort",k)}}import{setTimeout as ee}from"node:timers/promises";async function H(t,s,f){let h=Date.now()+s,k=()=>t.state==="RUNNING";if(f?.aborted)throw l("Creation aborted","aborted");if(k()&&typeof t.dataPlaneRPC==="function"&&t.dataPlaneRPC())return;try{await N((u)=>t.waitReady(s,u),Math.min(5000,s/2),f);return}catch(u){if(f?.aborted)throw l("Creation aborted","aborted");if(A(u).code!=="timeout")throw u}if(k())return;let x;while(Date.now()<h){if(f?.aborted)throw l("Creation aborted","aborted");try{if(await N(()=>t.refresh(),Math.min(1e4,h-Date.now()),f),f?.aborted)throw l("Creation aborted","aborted");if(k())return;if(["TERMINATED","TERMINATING"].includes(t.state))throw l("Session terminated before readiness","not_found")}catch(T){let c=A(T).code;if(["auth","not_found","invalid_argument","aborted"].includes(c))throw T;x=T}let u=h-Date.now();if(u>0)try{await ee(Math.min(1000,u),void 0,{signal:f})}catch{throw l("Creation aborted","aborted")}}throw new e("Tenki session did not become ready within its readiness budget.",{code:"timeout",provider:"tenki",sandboxId:t.id,cause:x})}var U=`workspace=globals().get('v',{}).get('workspace','/workspace')
2
+ home=os.path.join(os.path.dirname(workspace),'home','tenki')
3
+ tmp=os.path.join(os.path.dirname(workspace),'tmp')
4
+ os.makedirs(home+'/.sandbox-sdk',exist_ok=True)
5
+ os.makedirs(tmp,exist_ok=True)
6
+ import fcntl
7
+ with open(home+'/.sandbox-sdk/setup.lock','w') as setup_lock:
8
+ fcntl.flock(setup_lock,fcntl.LOCK_EX)
9
+ if not os.path.exists(home+'/.sandbox-sdk/prepared'):
10
+ if not os.path.exists(workspace):
11
+ os.makedirs(home+'/workspace',exist_ok=True)
12
+ try: os.symlink(home+'/workspace',workspace)
13
+ except FileExistsError: pass
14
+ elif not os.path.islink(workspace) and not os.path.exists(home+'/workspace'):
15
+ os.rename(workspace,home+'/workspace'); os.symlink(home+'/workspace',workspace)
16
+ with open(home+'/.sandbox-sdk/prepared','w'): pass
17
+ if not os.path.exists(tmp+'/.sandbox-sdk'):
18
+ try: os.symlink(home+'/.sandbox-sdk',tmp+'/.sandbox-sdk')
19
+ except FileExistsError: pass
20
+ `;var te=`import os,sys,marshal,_signal as signal,select,time,pwd
21
+ ${d}
22
+ v=marshal.loads(bytes.fromhex(sys.stdin.read()))
23
+ ${U}
24
+ def processes():
25
+ ids=set()
26
+ for e in os.scandir('/proc'):
27
+ if not e.name.isdecimal(): continue
28
+ try: ids.add((e.name,e.inode()))
29
+ except FileNotFoundError: continue
30
+ if len(ids)>4096: return None
31
+ return ids
32
+ before=processes()
33
+ def stable():
34
+ after=processes()
35
+ return before is not None and after is not None and after.issubset(before)
36
+ env={**os.environ,**v['env'],'PWD':v['cwd']}
37
+ if os.geteuid()==0 and 'SUDO_UID' not in env:
38
+ try: env['SUDO_UID']=str(pwd.getpwnam('tenki').pw_uid)
39
+ except KeyError: pass
40
+ with stdin_file() as f:
41
+ f.write(v['stdin'].encode()); f.seek(0)
42
+ p=Process(v,env,f,True)
43
+ try:
44
+ with open(v['marker'],'w') as m: m.write(str(p.pid))
45
+ if v.get('activityPath'):
46
+ os.makedirs(os.path.dirname(v['activityPath']),exist_ok=True)
47
+ with open(v['activityPath'],'w') as a: a.write('1')
48
+ stdout=p.stdout.fileno(); streams={stdout:'stdout',p.stderr.fileno():'stderr'}
49
+ end=time.monotonic()+v['timeoutMs']/1000
50
+ timed=False
51
+ while streams or p.poll() is None:
52
+ if not timed and time.monotonic()>=end:
53
+ timed=True
54
+ try: os.killpg(p.pid,signal.SIGKILL)
55
+ except ProcessLookupError: pass
56
+ for fd in select.select(list(streams),[],[],.01)[0]:
57
+ data=os.read(fd,8192)
58
+ if data: print('["'+streams[fd]+'","'+data.hex()+'"]',flush=True)
59
+ else: del streams[fd]; (p.stdout if fd==stdout else p.stderr).close()
60
+ code=p.wait()
61
+ print('{"exitCode":%s,"timedOut":%s,"workspaceStable":%s,"workspaceRootHex":"%s"}'%('null' if timed else str(code if code>=0 else 128-code),str(timed).lower(),str(stable()).lower(),os.path.realpath(v['workspace']).encode().hex()),flush=True)
62
+ except BaseException:
63
+ try: os.killpg(p.pid,signal.SIGKILL)
64
+ except ProcessLookupError: pass
65
+ raise
66
+ finally:
67
+ for path in [v['marker'],v.get('activityPath')]:
68
+ if path:
69
+ try: os.unlink(path)
70
+ except FileNotFoundError: pass
71
+ `;async function Z(t,s,f){if(s.signal?.aborted)return{exitCode:null,timedOut:!0};let h=`/home/tenki/.sandbox-sdk-run-${crypto.randomUUID()}`,k=Buffer.from(m({command:s.command,cwd:s.cwd,env:s.env,stdin:s.stdin??"",timeoutMs:s.timeoutMs,marker:h,activityPath:s.activityPath,workspace:"/workspace"}).toString("hex")),x=new AbortController,u,T=!1,c=!1,p={stdout:new TextDecoder("utf-8",{ignoreBOM:!0}),stderr:new TextDecoder("utf-8",{ignoreBOM:!0})},b=new TextDecoder,y="";function S(I){let w=JSON.parse(I);if(Array.isArray(w)&&w.length===2&&(w[0]==="stdout"||w[0]==="stderr")&&typeof w[1]==="string"&&w[1].length<=21848){let P=w[0]==="stdout"?"stdout":"stderr",C=p[P].decode(Buffer.from(w[1],"hex"),{stream:!0});if(C)s.onChunk({stream:P,text:C})}else if(w&&typeof w==="object"&&"exitCode"in w&&(w.exitCode===null||typeof w.exitCode==="number"&&Number.isInteger(w.exitCode))&&"timedOut"in w&&typeof w.timedOut==="boolean"){if("workspaceRootHex"in w&&typeof w.workspaceRootHex==="string")f?.(Buffer.from(w.workspaceRootHex,"hex").toString(),"workspaceStable"in w&&w.workspaceStable===!0);u={exitCode:w.exitCode,timedOut:w.timedOut}}else throw l("Invalid command output record")}let _=(async()=>{for(let I=0;I<2;I++){let w=new AbortController,P=AbortSignal.any([x.signal,w.signal]),C,n=!1;async function*i(){P.throwIfAborted(),yield{frame:{payload:{case:"start",value:{sessionId:t.id,cmd:["python3","-S","-c",te],privileged:!0,streamStdin:!0}}}},P.throwIfAborted(),c=!0,yield{frame:{payload:{case:"stdin",value:k}}},yield{frame:{payload:{case:"stdinClose",value:!0}}},await new Promise((r)=>{if(P.aborted)r();else P.addEventListener("abort",()=>r(),{once:!0})})}try{P.throwIfAborted(),C=await t.requireDataPlane(!1),P.throwIfAborted();for await(let r of C.run(i(),{signal:P})){let a=r.frame?.payload;if(!a)continue;if(a.case!=="keepalive")n=!0;if(a.case==="started")t.markDataPlaneServing(C);else if(a.case==="stdout"){y+=b.decode(a.value,{stream:!0});let E;while((E=y.indexOf(`
72
+ `))!==-1)S(y.slice(0,E)),y=y.slice(E+1);if(y.length>24000)throw l("Oversized command output record")}else if(a.case==="exit"){if(a.value.exitCode!==0||!u||y)throw l("Command supervisor failed");T=!0;for(let E of["stdout","stderr"]){let v=p[E].decode();if(v)s.onChunk({stream:E,text:v})}return u}}throw l("Command stream closed before exit")}catch(r){if(w.abort(),x.signal.aborted)throw r;let a=r&&typeof r==="object"&&"code"in r?r.code:void 0,E=r instanceof Error?r.message:"",v=a===16||A(r).code==="auth",R=a===5&&/\bsession not found\b/i.test(E)&&C!==void 0&&t.dataPlaneProvisioningPending(C),g=v||R||a===12&&/\b404\b/.test(E)||(a===10||a===13||a===14)&&/REFUSED_STREAM/i.test(E);if(!n&&(g||!c)){if(c=!1,v){if(I===0&&await t.reauthOnUnauthenticated(r))continue;throw l("Command authorization rejected before launch","auth")}if(g||a===14){if(I===0){if(R&&C)await t.refreshHintedDataPlane(C);continue}throw l("Command transport rejected before launch","unavailable")}}if(c)throw l("Command stream failed after submission; execution outcome is unknown","outcome_unknown");throw r}finally{w.abort()}}throw l("Command recovery exhausted","unavailable")})();_.catch(()=>{});try{return await N(()=>_,s.timeoutMs+1000,s.signal)}catch(I){if(c&&!T)try{await O(t,["python3","-c",`import os,sys,time,signal
73
+ for _ in range(100):
74
+ try:
75
+ with open(sys.argv[1]) as f: pid=int(f.read())
76
+ os.killpg(pid,signal.SIGKILL)
77
+ break
78
+ except ProcessLookupError: break
79
+ except (FileNotFoundError,ValueError): time.sleep(.01)
80
+ else: raise RuntimeError('PID unavailable')`,h],void 0,1500)}catch{if(!T)throw l("Process cancellation could not be confirmed; execution outcome is unknown","outcome_unknown")}if(s.signal?.aborted||A(I).code==="timeout")return{exitCode:null,timedOut:!0};throw I}finally{x.abort()}}var oe=`import os,sys,json,base64,signal,selectors,time,pwd
81
+ ${d}
82
+ v=json.load(sys.stdin)
83
+ ${U}
84
+ def processes():
85
+ ids=set()
86
+ for e in os.scandir('/proc'):
87
+ if not e.name.isdecimal(): continue
88
+ try: ids.add((e.name,e.inode()))
89
+ except FileNotFoundError: continue
90
+ if len(ids)>4096: return None
91
+ return ids
92
+ before=processes()
93
+ def stable():
94
+ after=processes()
95
+ return before is not None and after is not None and after.issubset(before)
96
+ env={**os.environ,**v['env'],'PWD':v['cwd']}
97
+ if os.geteuid()==0 and 'SUDO_UID' not in env:
98
+ try: env['SUDO_UID']=str(pwd.getpwnam('tenki').pw_uid)
99
+ except KeyError: pass
100
+ try:
101
+ with stdin_file() as f, open(v['marker']+'.out','wb',buffering=0) as out:
102
+ f.write(base64.b64decode(v['stdin'])); f.seek(0)
103
+ p=Process(v,env,f,True)
104
+ with open(v['marker'],'w') as m: m.write(str(p.pid))
105
+ if v.get('activityPath'):
106
+ os.makedirs(os.path.dirname(v['activityPath']),exist_ok=True)
107
+ with open(v['activityPath'],'w') as a: a.write('1')
108
+ sel=selectors.DefaultSelector()
109
+ sel.register(p.stdout,selectors.EVENT_READ,'stdout'); sel.register(p.stderr,selectors.EVENT_READ,'stderr')
110
+ end=time.monotonic()+v['timeoutMs']/1000
111
+ timed=False
112
+ while sel.get_map() or p.poll() is None:
113
+ if not timed and time.monotonic()>=end:
114
+ timed=True
115
+ try: os.killpg(p.pid,signal.SIGKILL)
116
+ except ProcessLookupError: pass
117
+ for key,_ in sel.select(.01):
118
+ data=os.read(key.fd,16384)
119
+ if data: out.write((json.dumps([key.data,base64.b64encode(data).decode()])+'\\n').encode())
120
+ else: sel.unregister(key.fileobj); key.fileobj.close()
121
+ code=p.wait()
122
+ with open(v['marker']+'.exit','w') as m: json.dump({'exitCode':None if timed else (code if code>=0 else 128-code),'timedOut':timed,'workspaceStable':stable(),'workspaceRoot':os.path.realpath(v['workspace'])},m)
123
+ finally:
124
+ if v.get('activityPath'):
125
+ try: os.unlink(v['activityPath'])
126
+ except FileNotFoundError: pass
127
+ `,re=`import sys,json
128
+ pos=int(sys.argv[2]); chunks=[]
129
+ try:
130
+ with open(sys.argv[1],'rb') as f:
131
+ f.seek(pos)
132
+ for _ in range(3):
133
+ line=f.readline(24000)
134
+ if not line.endswith(b'\\n'): break
135
+ chunks.append(json.loads(line)); pos=f.tell()
136
+ except FileNotFoundError: pass
137
+ print(json.dumps({'offset':pos,'chunks':chunks}))
138
+ `;async function O(t,s,f,h=30000,k){let x=new ReadableStream({start(T){if(f)T.enqueue(f);T.close()}}),u=await N((T)=>t.run(s,{privileged:!0,stdin:x,timeoutMs:h,signal:T}),h,k);if(u.exitCode!==0||u.timedOut)throw l("Guest operation failed",u.timedOut?"timeout":"provider");return new TextDecoder("utf-8",{ignoreBOM:!0}).decode(u.stdout)}async function J(t,s,f){if(typeof t.requireDataPlane==="function")return Z(t,s,f);if(s.signal?.aborted)return{exitCode:null,timedOut:!0};let h=`/home/tenki/.sandbox-sdk-run-${crypto.randomUUID()}`,k=Buffer.from(JSON.stringify({command:s.command,cwd:s.cwd,env:s.env,stdin:Buffer.from(s.stdin??"").toString("base64"),timeoutMs:s.timeoutMs,marker:h,activityPath:s.activityPath,workspace:"/workspace"})),x=!1,u=()=>{},T=new Promise((r)=>{u=()=>{x=!0,r("cancelled")}});s.signal?.addEventListener("abort",u,{once:!0});let c=setTimeout(u,s.timeoutMs),p=new ReadableStream({start(r){r.enqueue(k),r.close()}}),b=new AbortController,y=t.run(["python3","-S","-c",oe],{privileged:!0,stdin:p,signal:b.signal}),S=[];async function _(r,a){let E=new TextDecoder("utf-8",{ignoreBOM:!0}),v=r.getReader();S.push(v);try{while(!0){let{value:g,done:D}=await v.read();if(D)break;let M=E.decode(g,{stream:!0});if(M)s.onChunk({stream:a,text:M})}}finally{v.releaseLock()}let R=E.decode();if(R)s.onChunk({stream:a,text:R})}let I=!1,w=new AbortController,P=Promise.resolve(y).finally(()=>{I=!0});async function C(){let r=0,a={stdout:new TextDecoder("utf-8",{ignoreBOM:!0}),stderr:new TextDecoder("utf-8",{ignoreBOM:!0})};while(!w.signal.aborted){let E=I,v=JSON.parse(await O(t,["python3","-c",re,`${h}.out`,String(r)],void 0,30000,w.signal));if(!v||typeof v!=="object"||!("offset"in v)||typeof v.offset!=="number"||!Number.isSafeInteger(v.offset)||v.offset<r||!("chunks"in v)||!Array.isArray(v.chunks)||v.chunks.length>3)throw l("Invalid output page");let R=r;r=v.offset;for(let g of v.chunks){if(!Array.isArray(g)||g.length!==2||g[0]!=="stdout"&&g[0]!=="stderr"||typeof g[1]!=="string"||g[1].length>21848)throw l("Invalid output chunk");let D=g[0]==="stdout"?"stdout":"stderr",M=a[D].decode(Buffer.from(g[1],"base64"),{stream:!0});if(M&&!w.signal.aborted)s.onChunk({stream:D,text:M})}if(r===R){if(E)break;await new Promise((g)=>setTimeout(g,10))}}for(let E of["stdout","stderr"]){let v=a[E].decode();if(v&&!w.signal.aborted)s.onChunk({stream:E,text:v})}}let n=Promise.all([P,_(y.stdout,"stdout"),_(y.stderr,"stderr"),C()]).then(async()=>{let r=JSON.parse(await O(t,["cat",`${h}.exit`],void 0,30000,w.signal));if(!r||typeof r!=="object"||!("exitCode"in r)||!("timedOut"in r)||r.exitCode!==null&&typeof r.exitCode!=="number"||typeof r.timedOut!=="boolean")throw l("Invalid command result");if("workspaceRoot"in r&&typeof r.workspaceRoot==="string")f?.(r.workspaceRoot,"workspaceStable"in r&&r.workspaceStable===!0);return{exitCode:r.exitCode,timedOut:r.timedOut}});n.catch(()=>{});async function i(){await O(t,["python3","-c",`import os,sys,time,signal
139
+ p=sys.argv[1]
140
+ for _ in range(3000):
141
+ try:
142
+ with open(p) as f: pid=int(f.read())
143
+ break
144
+ except (FileNotFoundError,ValueError): time.sleep(.01)
145
+ else: raise RuntimeError('PID unavailable')
146
+ try: os.killpg(pid,signal.SIGKILL)
147
+ except ProcessLookupError: pass`,h],void 0,1000)}try{if(s.signal?.aborted)u();let r=x?"cancelled":await Promise.race([n,T]);if(r==="cancelled"){try{await i()}catch{throw await N(()=>t.close(),1000).catch(()=>{}),l("Process cancellation could not be confirmed; VM termination was requested","outcome_unknown")}await N(()=>n,250).catch(()=>{}),b.abort();for(let a of S)a.cancel().catch(()=>{});return{exitCode:null,timedOut:!0}}return r}catch(r){try{await i()}catch{await N(()=>t.close(),1000).catch(()=>{})}throw r}finally{w.abort(),b.abort();for(let r of S)try{r.cancel().catch(()=>{})}catch{}clearTimeout(c),s.signal?.removeEventListener("abort",u),await O(t,["rm","-f",h,`${h}.exit`,`${h}.out`],void 0,250).catch(()=>{})}}import{posix as q}from"node:path";var ne=`import os,sys,json,stat
148
+ ${U}
149
+ op,p=sys.argv[1:3]
150
+ def info(p):
151
+ s=os.lstat(p)
152
+ return dict(path=p,type='symlink' if stat.S_ISLNK(s.st_mode) else 'directory' if stat.S_ISDIR(s.st_mode) else 'file',size=s.st_size,modifiedAt=s.st_mtime*1000)
153
+ try:
154
+ if op=='read':
155
+ import base64
156
+ with open(p,'rb') as f: print(base64.b64encode(f.read()).decode())
157
+ elif op=='write':
158
+ os.makedirs(os.path.dirname(p),exist_ok=True)
159
+ with open(p,'wb') as f: f.write(sys.stdin.buffer.read())
160
+ elif op=='mkdir': os.makedirs(p,exist_ok=True)
161
+ elif op=='stat': print(json.dumps(info(p)))
162
+ elif op=='list': print(json.dumps([info(os.path.join(p,n)) for n in os.listdir(p)]))
163
+ elif op=='remove':
164
+ if os.path.isdir(p) and not os.path.islink(p):
165
+ if sys.argv[3]=='true':
166
+ import shutil
167
+ shutil.rmtree(p)
168
+ else: os.rmdir(p)
169
+ else: os.unlink(p)
170
+ except FileNotFoundError: print('SDK_FILE_NOT_FOUND')
171
+ except OSError: print('SDK_FILE_ERROR')
172
+ `;function W(t){if(!t||typeof t!=="object"||!("path"in t)||typeof t.path!=="string"||!("type"in t)||!["file","directory","symlink"].includes(String(t.type))||!("size"in t)||typeof t.size!=="number"||!("modifiedAt"in t)||typeof t.modifiedAt!=="number")throw l("Invalid file metadata");let s=t.type;if(s!=="file"&&s!=="directory"&&s!=="symlink")throw l("Invalid file type");return{path:t.path,type:s,size:t.size,modifiedAt:t.modifiedAt}}var se=134217728;function V(t,s){let f=s.modifiedUnixNs/1000000000n,h=s.modifiedUnixNs-f*1000000000n;return{path:t,type:s.isSymlink||s.mode&se?"symlink":s.isDir?"directory":"file",size:Number(s.size),modifiedAt:(Number(f)+Number(h)*0.000000001)*1000}}function Y(t,s,f,h){let k=(c,p,b,y,S)=>s(c,async()=>{let _=await O(await t(c),["python3","-c",ne,p,b,String(S)],y);if(_.trim()==="SDK_FILE_NOT_FOUND")throw l("File does not exist","file_not_found");if(_.trim()==="SDK_FILE_ERROR")throw l("File operation failed; check permissions and recursive removal","invalid_argument");return _});function x(c,p){let b=q.normalize(p);if(p!==b)return;if(b==="/workspace"||b.startsWith("/workspace/")){let y=f(c);if(!y)return;let S=q.join(y,b.slice(10));return S===y||S.startsWith(`${y}/`)?S:void 0}return b.startsWith("/home/tenki/")?b:void 0}async function u(c,p,b){let y=Promise.resolve().then(p);try{return await N(()=>y,30000)}catch(S){let _=A(S).code;if(c==="write"&&_==="timeout")throw await y.catch(()=>{}),l("File write exceeded its deadline; the native write was drained and may have completed","outcome_unknown");if(_==="auth"||_==="invalid_argument"||_==="unsupported")return b();throw S}}let T=(c,p,b,y)=>s(c,async()=>{let S=await t(c),_=x(S,p);if(!_)return y();try{return await N(()=>b(S,_),30000)}catch(I){if(A(I).code==="file_not_found")throw l("File does not exist","file_not_found");return y()}});return{writeCreatesParents:!0,read:(c,p)=>s(c,async()=>{let b=x(await t(c),p),y=async()=>new Uint8Array(Buffer.from(await k(c,"read",p),"base64"));return b?u("read",async()=>(await t(c)).readFile(b),y):y()}),write:(c,p,b)=>s(c,async()=>{let y=x(await t(c),p),S=async()=>{await k(c,"write",p,b)};if(y)await u("write",async()=>(await t(c)).writeFile(y,b),S);else await S()}),mkdir:async(c,p)=>{if(["/","/workspace"].includes(q.normalize(p)))h(await t(c));try{await k(c,"mkdir",p)}finally{if(["/","/workspace"].includes(q.normalize(p)))h(await t(c))}},remove:async(c,p,b)=>{h(await t(c));try{await k(c,"remove",p,void 0,b.recursive)}finally{h(await t(c))}},stat:(c,p)=>T(c,p,async(b,y)=>V(p,await b.fs.stat(y)),async()=>W(JSON.parse(await k(c,"stat",p)))),list:(c,p)=>T(c,p,async(b,y)=>(await b.fs.list(y,{includeHidden:!0})).map((S)=>V(q.join(p,q.basename(S.path)),S)),async()=>{let b=JSON.parse(await k(c,"list",p));if(!Array.isArray(b))throw l("Invalid file listing");return b.map(W)})}}var j="sandbox-sdk-key",Q="sandbox-sdk-created-at",ie={small:{cpu:1,memoryMb:1024,diskGb:10},medium:{cpu:2,memoryMb:4096,diskGb:20},large:{cpu:4,memoryMb:8192,diskGb:40}};function ae(t){if(t===void 0)return{};let s=typeof t==="string"?ie[t]:t,f=s?.diskGb??5;if(!s||!Number.isInteger(s.cpu)||s.cpu<1||s.cpu>128||!Number.isInteger(s.memoryMb)||s.memoryMb<128||s.memoryMb>524288||s.memoryMb%2||!Number.isInteger(f)||f<5||f>100)throw l("Size requires 1–128 CPUs, even memoryMb from 128–524288, and 5–100 diskGb","unsupported");return{cpuCores:s.cpu,memoryMb:s.memoryMb,diskSizeGb:f}}function L(t){let s=Number(t.metadata[Q]);if(!Number.isFinite(s)||s<=0||t.metadata["sandbox-sdk"]!=="1")throw l("Session was not created by Sandbox SDK or its metadata is missing","not_found");return{id:t.id,key:t.metadata[j],labels:{...t.metadata},createdAt:s,state:t.state==="RUNNING"?"running":["PAUSED","PAUSING","RESUMING"].includes(t.state)?"paused":"stopped"}}var de=["sh","-c","set -e; mkdir -p /home/tenki/.sandbox-sdk /tmp; exec 9>/home/tenki/.sandbox-sdk/setup.lock; flock -x 9; if [ ! -e /workspace ]; then mkdir -p /home/tenki/workspace; ln -s /home/tenki/workspace /workspace; elif [ ! -L /workspace ] && [ ! -e /home/tenki/workspace ]; then mv /workspace /home/tenki/workspace; ln -s /home/tenki/workspace /workspace; fi; flock -u 9; if [ ! -e /tmp/.sandbox-sdk ]; then ln -s /home/tenki/.sandbox-sdk /tmp/.sandbox-sdk; fi; test -L /tmp/.sandbox-sdk; command -v python3 git tar nohup setsid >/dev/null; readlink -f /workspace"];function Ge(t={}){let s,f=new Map,h=new Map,k=new WeakMap,x=new Map;function u(n){let i=x.get(n.id);if(!i)i={active:0,revision:0,overlap:!1,untracked:!1},x.set(n.id,i);return i}function T(n){k.delete(n),u(n).revision++}function c(n,i){let r=u(n);if(!r.active&&!r.untracked&&(i==="/home/tenki"||i.startsWith("/home/tenki/")))k.set(n,i)}async function p(n){T(n);let i=u(n).revision,r=(await O(n,de)).replace(/\n$/,"");if(i===u(n).revision)c(n,r)}let b=t.requestTimeoutMs??30000,y=t.resumeTimeoutMs??180000,S=(n,i)=>N(n,b,i),_=(n,i,r)=>N(n,i,r);async function I(){return s??=(async()=>{if(!Number.isSafeInteger(b)||b<=0)throw l("requestTimeoutMs must be a positive integer","invalid_argument");if(!Number.isSafeInteger(y)||y<=0||y>900000)throw l("resumeTimeoutMs must be an integer from 1 to 900000","invalid_argument");if(t.waitTimeoutMs!==void 0&&(!Number.isSafeInteger(t.waitTimeoutMs)||t.waitTimeoutMs<=0||t.waitTimeoutMs>900000))throw l("waitTimeoutMs must be an integer from 1 to 900000","invalid_argument");let n=t.apiKey??t.authToken??(process.env.TENKI_AUTH_TOKEN?.trim()||process.env.TENKI_API_KEY?.trim());if(!n)throw l("Tenki API key is missing. Authentication is required. Set TENKI_API_KEY or TENKI_AUTH_TOKEN or pass apiKey.","auth");let{TenkiSandbox:i}=await o("@tenkicloud/sandbox","tenki",()=>import("@tenkicloud/sandbox"));return new i({authToken:n,baseUrl:t.baseUrl})})()}async function w(n,i,r){try{return await r()}catch(a){throw A(a,i,n,[t.apiKey??t.authToken??""])}}async function P(n){let i=f.get(n);if(i)return i;await C.connect(n);let r=f.get(n);if(!r)throw l("Session is detached","not_found");return r}let C={name:"tenki",keyMode:"best-effort",inlineActivity:!0,processRecordRoot:"/home/tenki/.sandbox-sdk/procs",capabilities:new Set(["pause","snapshot","size","network-policy"]),create:(n)=>w("create",void 0,async()=>{if(n.idleTimeoutMs!==void 0)throw l("Tenki no longer auto-pauses on idle; idleTimeoutMs is unavailable","unsupported");if(n.timeoutMs!==void 0&&(!Number.isSafeInteger(n.timeoutMs)||n.timeoutMs<=0))throw l("timeoutMs must be a positive integer","invalid_argument");if(n.signal?.aborted)throw l("Creation aborted","aborted");let i=ae(n.size),r=typeof n.network==="object"?n.network.allow:void 0;if(r?.some((M)=>M.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(M)))throw l("Network allow entries must be DNS hostnames or leading wildcard domains","unsupported");let a={...n.labels,"sandbox-sdk":"1",[Q]:String(Date.now())};if(delete a[j],n.key!==void 0)a[j]=n.key;let E={...i,metadata:a,env:n.env,sticky:!1,maxDurationMs:n.timeoutMs,allowInbound:!0,allowOutbound:n.network!=="none"&&r?.length!==0,allowDomains:r,...n.from?{snapshotId:n.from}:{image:t.image},waitReady:!1},R=(await I()).create(E),g;try{g=await S(()=>R,n.signal)}catch(M){throw R.then((F)=>S(()=>F.close())).catch(()=>{}),M}f.set(g.id,g);let D="waitReady";try{let M=t.waitTimeoutMs??180000;if(await H(g,M,n.signal),D="validate",n.signal?.aborted)throw l("Creation aborted","aborted");if(i.cpuCores!==void 0&&(g.cpuCores!==i.cpuCores||g.memoryMb!==i.memoryMb||g.diskSizeGb!==i.diskSizeGb))throw l("Tenki returned different resources than requested","unsupported");if(g.outboundEnabled!==E.allowOutbound||r?.length&&(r.some((F)=>!g.egress.allowDomains.includes(F))||g.egress.allowDomains.some((F)=>!r.includes(F))||g.egress.allowCidrs.length))throw l("Tenki did not confirm the requested network policy","unsupported");return L(g)}catch(M){let F={id:g.id,state:g.state,stage:D},z;try{await C.destroy(g.id)}catch(X){z=X}throw B(n.signal?.aborted?l("Creation aborted","aborted"):M,F,[t.apiKey??t.authToken??""],z)}}),list:(n)=>w("list",void 0,async()=>(await S(async()=>(await I()).list())).filter((i)=>i.state!=="TERMINATED"&&i.state!=="TERMINATING"&&Object.entries({...n,"sandbox-sdk":"1"}).every(([r,a])=>i.metadata[r]===a)).flatMap((i)=>{try{return[L(i)]}catch(r){if(A(r).code!=="not_found")throw r;return[]}})),async find(n){let i=(await C.list({[j]:n}))[0];return i?C.connect(i.id):void 0},connect:(n)=>w("connect",n,async()=>{let i=await S(async()=>(await I()).get(n)),r=L(i);if(["TERMINATED","TERMINATING"].includes(i.state))throw l("Session has terminated","not_found");if(r.state==="running")await p(i);return f.set(n,i),r}),destroy(n){let i=h.get(n);if(i)return i;let r=w("destroy",n,async()=>{let a=!1;try{await S(async()=>(await P(n)).close()),a=!0}catch(E){if(A(E).code!=="not_found")throw E;a=!0}finally{if(f.delete(n),a)x.delete(n)}}).finally(()=>h.delete(n));return h.set(n,r),r},async close(n){f.delete(n)},exec:(n,i)=>w("exec",n,async()=>{let r=await P(n);T(r);let a=u(r),E=a.revision;if(a.active++,a.active>1)a.overlap=!0;let v;try{return await J(r,i,(R,g)=>{if(!g)a.untracked=!0;v=R})}finally{if(v===void 0)a.untracked=!0;if(a.active--,!a.overlap&&E===a.revision&&v!==void 0)c(r,v);if(!a.active)a.overlap=!1}}),files:Y(P,(n,i)=>w("files",n,i),(n)=>{let i=u(n);return i.active||i.untracked?void 0:k.get(n)},T),previewUrl:(n,i)=>w("preview",n,async()=>{let r=await P(n),a=await S(()=>r.exposePort(i));return{url:a.previewUrl,expiresAt:a.expiresAt?.getTime()}}),pause:(n)=>w("pause",n,async()=>{let i=await P(n),r=t.waitTimeoutMs??180000;await N(()=>i.pause(),r),await _((a)=>i.waitPaused(r,a),r)}),resume:(n)=>w("resume",n,async()=>{let i=await P(n);await N(()=>i.resume(),y);let r=t.waitTimeoutMs??180000;await _((a)=>i.waitResumed(r,a),r),await p(i)}),snapshot:(n)=>w("snapshot",n,async()=>{let i=await I(),r=await S(()=>i.createSnapshot(n,{async:!0})),a=t.waitTimeoutMs??300000;return{id:(await N((v)=>i.waitSnapshotReady(r.id,a,v),a)).id}}),native(n){let i=f.get(n);if(!i)throw l("Session is detached; connect first","not_found");return T(i),u(i).untracked=!0,i}};return C}export{Ge as tenki};
@@ -0,0 +1,7 @@
1
+ import type { Session } from "@tenkicloud/sandbox";
2
+ /** SDK 1.4.1 waits for stream EOF even after RUNNING, and does not recover a
3
+ * silent stream. Reserve the rest of the original budget for bounded refreshes.
4
+ * Successful streams keep their existing path and make no additional RPCs.
5
+ */
6
+ export declare function waitReady(value: Session, timeoutMs: number, signal?: AbortSignal): Promise<void>;
7
+ export declare const workspaceSetup = "workspace=globals().get('v',{}).get('workspace','/workspace')\nhome=os.path.join(os.path.dirname(workspace),'home','tenki')\ntmp=os.path.join(os.path.dirname(workspace),'tmp')\nos.makedirs(home+'/.sandbox-sdk',exist_ok=True)\nos.makedirs(tmp,exist_ok=True)\nimport fcntl\nwith open(home+'/.sandbox-sdk/setup.lock','w') as setup_lock:\n fcntl.flock(setup_lock,fcntl.LOCK_EX)\n if not os.path.exists(home+'/.sandbox-sdk/prepared'):\n if not os.path.exists(workspace):\n os.makedirs(home+'/workspace',exist_ok=True)\n try: os.symlink(home+'/workspace',workspace)\n except FileExistsError: pass\n elif not os.path.islink(workspace) and not os.path.exists(home+'/workspace'):\n os.rename(workspace,home+'/workspace'); os.symlink(home+'/workspace',workspace)\n with open(home+'/.sandbox-sdk/prepared','w'): pass\nif not os.path.exists(tmp+'/.sandbox-sdk'):\n try: os.symlink(home+'/.sandbox-sdk',tmp+'/.sandbox-sdk')\n except FileExistsError: pass\n";
@@ -0,0 +1,3 @@
1
+ import type { Session } from "@tenkicloud/sandbox";
2
+ import type { ExecInput, ExecResult } from "../../provider.js";
3
+ export declare function executeStream(box: Session, input: ExecInput, workspaceRoot?: (root: string, stable: boolean) => void): Promise<ExecResult>;
@@ -0,0 +1,3 @@
1
+ import type { Box } from "@upstash/box";
2
+ import type { ExecInput, ExecResult } from "../../provider.js";
3
+ export declare function bufferedExecute(box: Box, input: ExecInput, prepare?: string): Promise<ExecResult | undefined>;
@@ -0,0 +1,3 @@
1
+ import { SandboxError } from "../../errors.js";
2
+ export declare function upstashError(error: unknown, sandboxId?: string, file?: boolean, operation?: string, redact?: (text: string) => string): SandboxError;
3
+ export declare function unsupported(message: string): never;
@@ -0,0 +1,18 @@
1
+ import type { Box } from "@upstash/box";
2
+ import type { ExecInput, ExecResult } from "../../provider.js";
3
+ /** Session signals target the whole process tree; wait acknowledges termination. */
4
+ export declare function execute(box: Box, input: ExecInput, prepare?: string, prepared?: () => void): Promise<ExecResult>;
5
+ /** Durable work uses command(), never a connection-owned live session. Core owns
6
+ * process logs/specs; this private record only tracks the native wrapper's exit.
7
+ * `halt` stops a launch whose PID never arrived; it receives the private run directory. */
8
+ export declare function startProcess(box: Box, input: Omit<ExecInput, "timeoutMs">, halt: (dir: string) => Promise<unknown>, prepare?: string): Promise<{
9
+ pid: number;
10
+ wait: () => Promise<{
11
+ exitCode: number;
12
+ signal: null;
13
+ }>;
14
+ detach(): void;
15
+ stop({ graceMs }: {
16
+ graceMs: number;
17
+ }): Promise<void>;
18
+ }>;
@@ -0,0 +1,15 @@
1
+ import type { Box } from "@upstash/box";
2
+ export declare function readFile(box: Box, path: string, range?: {
3
+ offset: number;
4
+ length: number;
5
+ }): Promise<Uint8Array<ArrayBuffer>>;
6
+ export declare function writeFile(box: Box, path: string, data: Uint8Array, prepare?: string): Promise<void>;
7
+ export type ListedEntry = {
8
+ name: string;
9
+ type: "file" | "directory" | "symlink" | "other";
10
+ size: number;
11
+ modifiedAt: number;
12
+ };
13
+ /** Returns undefined when the guest cannot answer (no python3, truncated output),
14
+ * so the caller can use the native list-then-stat path instead. */
15
+ export declare function listDirectory(box: Box, path: string, prepare?: string): Promise<ListedEntry[] | undefined>;
@@ -0,0 +1,38 @@
1
+ import type { Box, BoxConfig } from "@upstash/box";
2
+ import type { Provider } from "../../provider.js";
3
+ type CommonOptions = {
4
+ /** Defaults to UPSTASH_BOX_API_KEY; required on first use. */
5
+ apiKey?: BoxConfig["apiKey"];
6
+ /** Defaults to UPSTASH_BOX_BASE_URL, then https://us-east-1.box.upstash.com. */
7
+ baseUrl?: BoxConfig["baseUrl"];
8
+ /** Runtime; default: the Upstash service default. */
9
+ runtime?: BoxConfig["runtime"];
10
+ /** Native telemetry; default: true, overridden by UPSTASH_DISABLE_TELEMETRY. */
11
+ enableTelemetry?: BoxConfig["enableTelemetry"];
12
+ /** Native SDK operation timeout, not a sandbox lifetime. Default: 600 s; snapshot listing: 30 s. */
13
+ requestTimeoutMs?: number;
14
+ /** Public previews. Default: false (previews require a bearer token). */
15
+ public?: boolean;
16
+ };
17
+ /** Durable Box: pauses when idle and keeps its files until destroyed. */
18
+ export type UpstashBoxOptions = CommonOptions & {
19
+ /** Durable mode. Default: false. */
20
+ ephemeral?: false;
21
+ };
22
+ /** Ephemeral Box: ready on create, cannot pause, and Upstash deletes it after its TTL. */
23
+ export type UpstashEphemeralOptions = CommonOptions & {
24
+ /** Select ephemeral mode; required for ephemeral options. Default: false. */
25
+ ephemeral: true;
26
+ /** Seconds before Upstash deletes the box: a leak backstop, not a deadline. Defaults to 3600; at most 259200. */
27
+ ttlSeconds?: number;
28
+ };
29
+ export type UpstashOptions = UpstashBoxOptions | UpstashEphemeralOptions;
30
+ export type UpstashCapability = "pause" | "snapshot" | "size" | "network-policy";
31
+ export type UpstashEphemeralCapability = Exclude<UpstashCapability, "pause">;
32
+ export type UpstashProvider = Provider<Box, UpstashCapability>;
33
+ export type UpstashEphemeralProvider = Provider<Box, UpstashEphemeralCapability>;
34
+ export declare function upstash(options: UpstashEphemeralOptions): UpstashEphemeralProvider;
35
+ export declare function upstash(options?: UpstashBoxOptions): UpstashProvider;
36
+ /** Mode unknown at compile time: pause is not promised. */
37
+ export declare function upstash(options: UpstashOptions): UpstashEphemeralProvider;
38
+ export {};