mirror of
https://github.com/logseq/logseq.git
synced 2026-05-23 12:14:06 +00:00
fix: e2b terminal access && git push
This commit is contained in:
172
deps/workers/src/logseq/agents/runtime_provider.cljs
vendored
172
deps/workers/src/logseq/agents/runtime_provider.cljs
vendored
@@ -123,6 +123,7 @@
|
||||
(str "http://127.0.0.1:" port path))
|
||||
|
||||
(def ^:private default-repo-base-dir "/workspace")
|
||||
(def ^:private e2b-repo-base-dir "/home/user/workspace")
|
||||
(def ^:private vercel-repo-base-dir "/vercel/sandbox")
|
||||
(def ^:private cloudflare-local-host "http://localhost")
|
||||
(def ^:private cloudflare-snapshot-ttl-seconds (* 30 24 60 60))
|
||||
@@ -372,19 +373,22 @@
|
||||
|
||||
(defn- repo-base-dir
|
||||
[provider]
|
||||
(if (= "vercel" (normalize-provider provider))
|
||||
vercel-repo-base-dir
|
||||
(case (normalize-provider provider)
|
||||
"e2b" e2b-repo-base-dir
|
||||
"vercel" vercel-repo-base-dir
|
||||
default-repo-base-dir))
|
||||
|
||||
(defn- get-repo-dir
|
||||
([session-id]
|
||||
(get-repo-dir session-id nil nil))
|
||||
([session-id task provider]
|
||||
(if (= "vercel" (normalize-provider provider))
|
||||
(str vercel-repo-base-dir "/" (task-repo-name task))
|
||||
(let [session-id (some-> session-id str)]
|
||||
(when (string? session-id)
|
||||
(str default-repo-base-dir "/" (sanitize-name session-id)))))))
|
||||
(case (normalize-provider provider)
|
||||
"e2b" (str e2b-repo-base-dir "/" (task-repo-name task))
|
||||
"vercel" (str vercel-repo-base-dir "/" (task-repo-name task))
|
||||
(let [session-id (some-> session-id str)
|
||||
base-dir (repo-base-dir provider)]
|
||||
(when (and (string? session-id) (string? base-dir))
|
||||
(str base-dir "/" (sanitize-name session-id)))))))
|
||||
|
||||
(defn- repo-cd-command
|
||||
([session-id]
|
||||
@@ -965,7 +969,7 @@
|
||||
|
||||
(defn- e2b-sandbox-timeout-ms
|
||||
[^js env]
|
||||
(parse-int (env-str env "E2B_SANDBOX_TIMEOUT_MS") (* 30 60 1000)))
|
||||
(parse-int (env-str env "E2B_SANDBOX_TIMEOUT_MS") (* 5 60 1000)))
|
||||
|
||||
(defn- e2b-api-opts
|
||||
[^js env]
|
||||
@@ -990,13 +994,30 @@
|
||||
(or (aget sandbox "sandboxId")
|
||||
(aget sandbox "id")))
|
||||
|
||||
(defn- e2b-command-error-data
|
||||
[command error]
|
||||
(let [stdout (or (some-> error (aget "stdout")) "")
|
||||
stderr (or (some-> error (aget "stderr")) "")
|
||||
exit-code (or (some-> error (aget "exitCode"))
|
||||
(some-> error (aget "exit_code")))
|
||||
name (some-> error (aget "name"))
|
||||
message (or (some-> error (aget "message"))
|
||||
(str error))]
|
||||
(cond-> {:reason :e2b-command-failed
|
||||
:command command
|
||||
:message message
|
||||
:stdout stdout
|
||||
:stderr stderr}
|
||||
(string? name) (assoc :error-name name)
|
||||
(number? exit-code) (assoc :exit-code exit-code))))
|
||||
|
||||
(defn- e2b-sandbox-host
|
||||
[sandbox port]
|
||||
(let [get-host (js-method sandbox "getHost")]
|
||||
(when-not (fn? get-host)
|
||||
(throw (ex-info "e2b sandbox missing getHost method"
|
||||
{:reason :missing-e2b-get-host})))
|
||||
(.call get-host sandbox port)))
|
||||
(sandbox/normalize-base-url (.call get-host sandbox port))))
|
||||
|
||||
(defn <e2b-create-sandbox!
|
||||
[^js env template opts]
|
||||
@@ -1032,6 +1053,15 @@
|
||||
(->promise (.call kill sandbox-class sandbox-id opts))
|
||||
(p/resolved nil))))
|
||||
|
||||
(defn <e2b-pause-sandbox!
|
||||
[^js env sandbox-id]
|
||||
(let [sandbox-class (e2b-sandbox-class)
|
||||
pause (js-method sandbox-class "pause")
|
||||
opts (clj->js (e2b-api-opts env))]
|
||||
(if (and (string? sandbox-id) (fn? pause))
|
||||
(->promise (.call pause sandbox-class sandbox-id opts))
|
||||
(p/resolved nil))))
|
||||
|
||||
(defn <e2b-run-shell!
|
||||
[sandbox command & [opts]]
|
||||
(let [commands (when sandbox (aget sandbox "commands"))
|
||||
@@ -1048,7 +1078,12 @@
|
||||
{:reason :missing-e2b-run-command})))
|
||||
(if (true? (:background opts))
|
||||
(->promise (.call run-command commands command (clj->js params)))
|
||||
(p/let [result (->promise (.call run-command commands command (clj->js params)))
|
||||
(p/let [result (-> (.call run-command commands command (clj->js params))
|
||||
->promise
|
||||
(p/catch (fn [error]
|
||||
(throw (ex-info "e2b sandbox command failed"
|
||||
(e2b-command-error-data command error)
|
||||
error)))))
|
||||
stdout (or (aget result "stdout") "")
|
||||
stderr (or (aget result "stderr") "")
|
||||
exit-code (or (aget result "exitCode")
|
||||
@@ -1219,7 +1254,7 @@
|
||||
port (e2b-agent-port env runtime)
|
||||
sandbox-id (:sandbox-id runtime)]
|
||||
(if (string? cached)
|
||||
(p/resolved cached)
|
||||
(p/resolved (sandbox/normalize-base-url cached))
|
||||
(p/let [sandbox (<e2b-connect-sandbox! env sandbox-id)]
|
||||
(e2b-sandbox-host sandbox port)))))
|
||||
|
||||
@@ -1283,28 +1318,93 @@
|
||||
true)))
|
||||
|
||||
(defn- <e2b-open-terminal!
|
||||
[^js env runtime request]
|
||||
(let [session-id (:session-id runtime)]
|
||||
[^js env runtime request {:keys [cols rows]}]
|
||||
(let [sandbox-id (:sandbox-id runtime)
|
||||
session-id (:session-id runtime)
|
||||
cwd (or (:backup-dir runtime)
|
||||
(get-repo-dir session-id nil "e2b"))]
|
||||
(when-not (string? sandbox-id)
|
||||
(throw (ex-info "missing sandbox-id on runtime" {:runtime runtime})))
|
||||
(when-not (string? session-id)
|
||||
(throw (ex-info "missing runtime session-id on runtime"
|
||||
{:runtime runtime})))
|
||||
(let [agent-token (e2b-agent-token env runtime)]
|
||||
(p/let [base-url (<e2b-runtime-base-url! env runtime)
|
||||
req-url (platform/request-url request)
|
||||
terminal-url (str (sandbox/session-url base-url session-id) "/terminal" (.-search req-url))
|
||||
headers (js/Headers. (.-headers request))
|
||||
_ (when (string? agent-token)
|
||||
(.set headers "authorization" (str "Bearer " agent-token)))
|
||||
req (js/Request. terminal-url
|
||||
#js {:method (.-method request)
|
||||
:headers headers})
|
||||
resp (js/fetch req)
|
||||
status (.-status resp)]
|
||||
(if (or (= status 101) (<= 200 status 299))
|
||||
resp
|
||||
(throw (ex-info "e2b open-terminal failed"
|
||||
{:status status
|
||||
:session-id session-id})))))))
|
||||
(when-not (= "websocket"
|
||||
(some-> request .-headers (.get "Upgrade") str string/lower-case))
|
||||
(throw (ex-info "e2b terminal requires websocket upgrade"
|
||||
{:reason :unsupported-terminal
|
||||
:session-id session-id})))
|
||||
(p/let [sandbox (<e2b-connect-sandbox! env sandbox-id)]
|
||||
(let [pty (some-> sandbox (aget "pty"))
|
||||
create-pty (js-method pty "create")]
|
||||
(when-not (fn? create-pty)
|
||||
(throw (ex-info "e2b sandbox missing pty.create"
|
||||
{:reason :unsupported-terminal
|
||||
:sandbox-id sandbox-id
|
||||
:session-id session-id})))
|
||||
(let [pair (js/WebSocketPair.)
|
||||
client (aget pair 0)
|
||||
server (aget pair 1)]
|
||||
(.accept server)
|
||||
(set! (.-binaryType server) "arraybuffer")
|
||||
(-> (->promise
|
||||
(.call create-pty pty
|
||||
(clj->js (cond-> {:cols (or cols 120)
|
||||
:rows (or rows 40)
|
||||
:cwd cwd
|
||||
:onData (fn [payload]
|
||||
(.send server payload))}
|
||||
(number? cols) (assoc :cols cols)
|
||||
(number? rows) (assoc :rows rows)))))
|
||||
(p/then
|
||||
(fn [handle]
|
||||
(let [pid (aget handle "pid")
|
||||
send-input (js-method pty "sendInput")
|
||||
resize (js-method pty "resize")
|
||||
kill-handle (js-method handle "kill")
|
||||
close-handler (fn []
|
||||
(when (fn? kill-handle)
|
||||
(-> (->promise (.call kill-handle handle))
|
||||
(p/catch (fn [_] nil)))))]
|
||||
(.addEventListener
|
||||
server
|
||||
"message"
|
||||
(fn [event]
|
||||
(let [payload (.-data event)]
|
||||
(cond
|
||||
(string? payload)
|
||||
(let [parsed (parse-json-safe payload)]
|
||||
(if (= "resize" (:type parsed))
|
||||
(when (and (number? pid) (fn? resize))
|
||||
(-> (->promise (.call resize pty pid
|
||||
(clj->js {:cols (:cols parsed)
|
||||
:rows (:rows parsed)})))
|
||||
(p/catch (fn [_] nil))))
|
||||
(when (and (number? pid) (fn? send-input))
|
||||
(-> (->promise (.call send-input pty pid payload))
|
||||
(p/catch (fn [_] nil))))))
|
||||
|
||||
(instance? js/ArrayBuffer payload)
|
||||
(when (and (number? pid) (fn? send-input))
|
||||
(-> (->promise (.call send-input pty pid (js/Uint8Array. payload)))
|
||||
(p/catch (fn [_] nil))))
|
||||
|
||||
(instance? js/Uint8Array payload)
|
||||
(when (and (number? pid) (fn? send-input))
|
||||
(-> (->promise (.call send-input pty pid payload))
|
||||
(p/catch (fn [_] nil))))
|
||||
|
||||
:else nil))))
|
||||
(.addEventListener server "close" close-handler)
|
||||
(.addEventListener server "error" (fn [_] (close-handler)))
|
||||
(.send server (js/JSON.stringify #js {:type "ready"}))
|
||||
(js/Response. nil #js {:status 101
|
||||
:webSocket client}))))
|
||||
(p/catch
|
||||
(fn [error]
|
||||
(try
|
||||
(.close server 1011 "terminal init failed")
|
||||
(catch :default _ nil))
|
||||
(throw error)))))))))
|
||||
|
||||
(defn- vercel-agent-token [^js env runtime]
|
||||
(or (:agent-token runtime)
|
||||
@@ -2546,14 +2646,16 @@
|
||||
(fn [error]
|
||||
(log/error :agent/e2b-repo-clone-failed
|
||||
{:session-id session-id
|
||||
:error (str error)})
|
||||
:error (str error)
|
||||
:error-data (ex-data error)})
|
||||
nil)))
|
||||
_ (p/catch
|
||||
(<e2b-run-project-init-setup! sandbox session-id task)
|
||||
(fn [error]
|
||||
(log/error :agent/e2b-project-init-setup-failed
|
||||
{:session-id session-id
|
||||
:error (str error)})
|
||||
:error (str error)
|
||||
:error-data (ex-data error)})
|
||||
nil))
|
||||
base-url (e2b-sandbox-host sandbox port)
|
||||
response (sandbox/<create-session base-url agent-token session-id payload)
|
||||
@@ -2583,8 +2685,8 @@
|
||||
(p/let [base-url (<e2b-runtime-base-url! env runtime)]
|
||||
(sandbox/<send-message base-url agent-token (:session-id runtime) message))))
|
||||
|
||||
(<open-terminal! [_ runtime request _opts]
|
||||
(<e2b-open-terminal! env runtime request))
|
||||
(<open-terminal! [_ runtime request opts]
|
||||
(<e2b-open-terminal! env runtime request opts))
|
||||
|
||||
(<snapshot-runtime! [_ runtime opts]
|
||||
(let [session-id (:session-id runtime)
|
||||
@@ -2666,7 +2768,7 @@
|
||||
(if-not (string? sandbox-id)
|
||||
(p/resolved nil)
|
||||
(p/catch
|
||||
(<e2b-kill-sandbox! env sandbox-id)
|
||||
(<e2b-pause-sandbox! env sandbox-id)
|
||||
(fn [_] nil))))))
|
||||
|
||||
(defrecord VercelProvider [env]
|
||||
|
||||
11
deps/workers/src/logseq/agents/sandbox.cljs
vendored
11
deps/workers/src/logseq/agents/sandbox.cljs
vendored
@@ -3,8 +3,17 @@
|
||||
[logseq.sync.platform.core :as platform]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(def ^:private absolute-url-re #"^[A-Za-z][A-Za-z0-9+.-]*://")
|
||||
(def ^:private local-host-re #"^(localhost|127(?:\.\d{1,3}){3}|\[::1\])(?::\d+)?(?:/.*)?$")
|
||||
|
||||
(defn normalize-base-url [base]
|
||||
(string/replace (or base "") #"/+$" ""))
|
||||
(let [base (some-> base str string/trim not-empty)
|
||||
base (some-> base (string/replace #"/+$" ""))]
|
||||
(cond
|
||||
(not (string? base)) ""
|
||||
(re-find absolute-url-re base) base
|
||||
(re-find local-host-re base) (str "http://" base)
|
||||
:else (str "https://" base))))
|
||||
|
||||
(defn sessions-base-url [base]
|
||||
(str (normalize-base-url base) "/v1/sessions"))
|
||||
|
||||
@@ -31,7 +31,8 @@
|
||||
env #js {"E2B_API_KEY" "e2b-key"
|
||||
"SANDBOX_AGENT_TOKEN" "agent-token"}
|
||||
provider (runtime-provider/create-provider env "e2b")
|
||||
task {:agent {:provider "codex"}}
|
||||
task {:agent {:provider "codex"}
|
||||
:project {:repo-url "https://github.com/logseq/agent-test"}}
|
||||
sandbox-class (runtime-provider/e2b-sandbox-class)
|
||||
original-create (aget sandbox-class "create")
|
||||
original-fetch js/fetch
|
||||
@@ -77,6 +78,12 @@
|
||||
(is (= "https://e2b-agent.local" (:base-url runtime)))
|
||||
(is (= "sess-e2b-1" (:session-id runtime)))
|
||||
(is (= 2468 (:sandbox-port runtime)))
|
||||
(is (some #(and (= :command (:type %))
|
||||
(string/includes? (:cmd %) "mkdir -p '/home/user/workspace'"))
|
||||
@calls))
|
||||
(is (some #(and (= :command (:type %))
|
||||
(string/includes? (:cmd %) "'/home/user/workspace/agent-test'"))
|
||||
@calls))
|
||||
(is (some #(= {:type :host :port 2468} %) @calls))
|
||||
(done)))
|
||||
(.catch (fn [error]
|
||||
@@ -84,6 +91,95 @@
|
||||
(is false (str "unexpected error: " error))
|
||||
(done)))))))
|
||||
|
||||
(deftest e2b-provider-provision-normalizes-bare-host-test
|
||||
(async done
|
||||
(let [env #js {"E2B_API_KEY" "e2b-key"
|
||||
"SANDBOX_AGENT_TOKEN" "agent-token"}
|
||||
provider (runtime-provider/create-provider env "e2b")
|
||||
task {:agent {:provider "codex"}
|
||||
:project {:repo-url "https://github.com/logseq/agent-test"}}
|
||||
sandbox-class (runtime-provider/e2b-sandbox-class)
|
||||
original-create (aget sandbox-class "create")
|
||||
original-fetch js/fetch
|
||||
restore! (fn []
|
||||
(aset sandbox-class "create" original-create)
|
||||
(set! js/fetch original-fetch))]
|
||||
(aset sandbox-class "create"
|
||||
(fn [& _args]
|
||||
(js/Promise.resolve
|
||||
#js {:sandboxId "e2b-sbx-2"
|
||||
:getHost (fn [_port]
|
||||
"2468-if9ct9du77wx2o6oorw10.e2b.app")
|
||||
:commands
|
||||
#js {:run (fn [cmd _opts]
|
||||
(if (string/includes? cmd "/v1/health")
|
||||
(js/Promise.resolve #js {:stdout "__HEALTH_OK__"
|
||||
:stderr ""
|
||||
:exitCode 0})
|
||||
(js/Promise.resolve #js {:stdout ""
|
||||
:stderr ""
|
||||
:exitCode 0})))}})))
|
||||
(set! js/fetch
|
||||
(fn [request]
|
||||
(is (= "https://2468-if9ct9du77wx2o6oorw10.e2b.app/v1/sessions/sess-e2b-2"
|
||||
(.-url request)))
|
||||
(js/Promise.resolve
|
||||
(js/Response.
|
||||
(js/JSON.stringify #js {:ok true})
|
||||
#js {:status 200
|
||||
:headers #js {"content-type" "application/json"}}))))
|
||||
(-> (runtime-provider/<provision-runtime! provider "sess-e2b-2" task)
|
||||
(.then (fn [runtime]
|
||||
(restore!)
|
||||
(is (= "https://2468-if9ct9du77wx2o6oorw10.e2b.app"
|
||||
(:base-url runtime)))
|
||||
(is (= "/home/user/workspace/agent-test"
|
||||
(:backup-dir runtime)))
|
||||
(done)))
|
||||
(.catch (fn [error]
|
||||
(restore!)
|
||||
(is false (str "unexpected error: " error))
|
||||
(done)))))))
|
||||
|
||||
(deftest e2b-run-shell-wraps-commandexiterror-test
|
||||
(async done
|
||||
(let [env #js {"E2B_API_KEY" "e2b-key"}
|
||||
provider (runtime-provider/create-provider env "e2b")
|
||||
sandbox-class (runtime-provider/e2b-sandbox-class)
|
||||
original-connect (aget sandbox-class "connect")
|
||||
restore! (fn []
|
||||
(aset sandbox-class "connect" original-connect))]
|
||||
(aset sandbox-class "connect"
|
||||
(fn [_sandbox-id _opts]
|
||||
(js/Promise.resolve
|
||||
#js {:sandboxId "e2b-sbx-3"
|
||||
:commands
|
||||
#js {:run (fn [_cmd _opts]
|
||||
(js/Promise.reject
|
||||
#js {:name "CommandExitError"
|
||||
:message "CommandExitError: exit status 1"
|
||||
:stdout ""
|
||||
:stderr "fatal: repository not found"
|
||||
:exitCode 1}))}})))
|
||||
(-> (runtime-provider/<snapshot-runtime! provider
|
||||
{:provider "e2b"
|
||||
:sandbox-id "e2b-sbx-3"
|
||||
:session-id "sess-e2b-error"
|
||||
:backup-dir "/home/user/workspace/agent-test"}
|
||||
{:task {:project {:repo-url "https://github.com/logseq/agent-test"
|
||||
:base-branch "main"}}})
|
||||
(.then (fn [_result]
|
||||
(restore!)
|
||||
(is false "expected command failure")
|
||||
(done)))
|
||||
(.catch (fn [error]
|
||||
(restore!)
|
||||
(is (= :e2b-command-failed (:reason (ex-data error))))
|
||||
(is (= 1 (:exit-code (ex-data error))))
|
||||
(is (= "fatal: repository not found" (:stderr (ex-data error))))
|
||||
(is (string/includes? (:message (ex-data error)) "exit status 1"))
|
||||
(done)))))))
|
||||
|
||||
(deftest e2b-provider-open-terminal-test
|
||||
(async done
|
||||
(let [env #js {"E2B_API_KEY" "e2b-key"
|
||||
@@ -92,31 +188,83 @@
|
||||
runtime {:provider "e2b"
|
||||
:sandbox-id "e2b-sbx-1"
|
||||
:base-url "https://e2b-agent.local"
|
||||
:session-id "sess-term-1"}
|
||||
:session-id "sess-term-1"
|
||||
:backup-dir "/home/user/workspace/agent-test"}
|
||||
request (js/Request. "https://api.logseq.local/sessions/sess-term-1/terminal?cols=120&rows=40"
|
||||
#js {:method "GET"})
|
||||
calls (atom {})
|
||||
original-fetch js/fetch]
|
||||
(set! js/fetch
|
||||
(fn [req]
|
||||
(reset! calls {:url (.-url req)
|
||||
:method (.-method req)
|
||||
:auth (.get (.-headers req) "authorization")})
|
||||
(js/Promise.resolve (js/Response. "ok" #js {:status 200}))))
|
||||
sandbox-class (runtime-provider/e2b-sandbox-class)
|
||||
original-connect (aget sandbox-class "connect")
|
||||
original-websocket-pair js/WebSocketPair
|
||||
client-socket #js {}
|
||||
server-socket #js {:accept (fn [] (swap! calls assoc :accepted true))
|
||||
:send (fn [payload] (swap! calls assoc :ready payload))
|
||||
:addEventListener (fn [_type _listener] nil)
|
||||
:close (fn [& _args] nil)}
|
||||
restore! (fn []
|
||||
(aset sandbox-class "connect" original-connect)
|
||||
(set! js/WebSocketPair original-websocket-pair))]
|
||||
(set! js/WebSocketPair
|
||||
(fn []
|
||||
(clj->js [client-socket server-socket])))
|
||||
(aset sandbox-class "connect"
|
||||
(fn [_sandbox-id _opts]
|
||||
(js/Promise.resolve
|
||||
#js {:sandboxId "e2b-sbx-1"
|
||||
:pty
|
||||
#js {:create (fn [opts]
|
||||
(swap! calls assoc :pty-opts (js->clj opts :keywordize-keys true))
|
||||
(js/Promise.resolve
|
||||
#js {:pid 42
|
||||
:kill (fn []
|
||||
(swap! calls update :kills (fnil inc 0))
|
||||
(js/Promise.resolve true))}))}})))
|
||||
(-> (runtime-provider/<open-terminal! provider runtime request {:cols 120 :rows 40})
|
||||
(.then (fn [resp]
|
||||
(set! js/fetch original-fetch)
|
||||
(is (= 200 (.-status resp)))
|
||||
(is (= "GET" (:method @calls)))
|
||||
(is (= "Bearer agent-token" (:auth @calls)))
|
||||
(is (= "https://e2b-agent.local/v1/sessions/sess-term-1/terminal?cols=120&rows=40"
|
||||
(:url @calls)))
|
||||
(restore!)
|
||||
(is (= 101 (.-status resp)))
|
||||
(is (= client-socket (aget resp "webSocket")))
|
||||
(is (true? (:accepted @calls)))
|
||||
(is (= 120 (get-in @calls [:pty-opts :cols])))
|
||||
(is (= 40 (get-in @calls [:pty-opts :rows])))
|
||||
(is (= "/home/user/workspace/agent-test" (get-in @calls [:pty-opts :cwd])))
|
||||
(is (= "{\"type\":\"ready\"}" (:ready @calls)))
|
||||
(done)))
|
||||
(.catch (fn [error]
|
||||
(set! js/fetch original-fetch)
|
||||
(restore!)
|
||||
(is false (str "unexpected error: " error))
|
||||
(done)))))))
|
||||
|
||||
(deftest e2b-provider-terminate-pauses-sandbox-test
|
||||
(async done
|
||||
(let [env #js {"E2B_API_KEY" "e2b-key"}
|
||||
provider (runtime-provider/create-provider env "e2b")
|
||||
sandbox-class (runtime-provider/e2b-sandbox-class)
|
||||
original-pause (aget sandbox-class "pause")
|
||||
original-kill (aget sandbox-class "kill")
|
||||
calls (atom [])
|
||||
restore! (fn []
|
||||
(aset sandbox-class "pause" original-pause)
|
||||
(aset sandbox-class "kill" original-kill))]
|
||||
(aset sandbox-class "pause"
|
||||
(fn [sandbox-id _opts]
|
||||
(swap! calls conj {:type :pause :sandbox-id sandbox-id})
|
||||
(js/Promise.resolve true)))
|
||||
(aset sandbox-class "kill"
|
||||
(fn [sandbox-id _opts]
|
||||
(swap! calls conj {:type :kill :sandbox-id sandbox-id})
|
||||
(js/Promise.resolve true)))
|
||||
(-> (runtime-provider/<terminate-runtime! provider {:provider "e2b"
|
||||
:sandbox-id "e2b-sbx-pause"})
|
||||
(.then (fn [_]
|
||||
(restore!)
|
||||
(is (= [{:type :pause :sandbox-id "e2b-sbx-pause"}] @calls))
|
||||
(done)))
|
||||
(.catch (fn [error]
|
||||
(restore!)
|
||||
(is false (str "unexpected terminate error: " error))
|
||||
(done)))))))
|
||||
|
||||
(deftest e2b-provider-snapshot-runtime-test
|
||||
(async done
|
||||
(let [env #js {"E2B_API_KEY" "e2b-key"}
|
||||
@@ -124,8 +272,8 @@
|
||||
runtime {:provider "e2b"
|
||||
:sandbox-id "e2b-sbx-1"
|
||||
:session-id "sess-e2b-snapshot"
|
||||
:backup-dir "/workspace/sess-e2b-snapshot"}
|
||||
task {:project {:repo-url "https://github.com/example/repo"
|
||||
:backup-dir "/home/user/workspace/agent-test"}
|
||||
task {:project {:repo-url "https://github.com/logseq/agent-test"
|
||||
:base-branch "main"}}
|
||||
sandbox-class (runtime-provider/e2b-sandbox-class)
|
||||
original-connect (aget sandbox-class "connect")
|
||||
@@ -142,7 +290,7 @@
|
||||
(restore!)
|
||||
(is (= "e2b" (:provider result)))
|
||||
(is (= "e2b-snap-1" (:snapshot-id result)))
|
||||
(is (= "/workspace/sess-e2b-snapshot" (:backup-dir result)))
|
||||
(is (= "/home/user/workspace/agent-test" (:backup-dir result)))
|
||||
(done)))
|
||||
(.catch (fn [error]
|
||||
(restore!)
|
||||
@@ -155,7 +303,7 @@
|
||||
provider (runtime-provider/create-provider env "e2b")
|
||||
runtime {:provider "e2b"
|
||||
:sandbox-id "e2b-sbx-1"
|
||||
:backup-dir "/workspace/sess-e2b-bundle"
|
||||
:backup-dir "/home/user/workspace/agent-test"
|
||||
:session-id "sess-e2b-bundle"}
|
||||
task {:project {:base-branch "main"}}
|
||||
sandbox-class (runtime-provider/e2b-sandbox-class)
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
(testing "normalizes sandbox base urls"
|
||||
(is (= "https://sandbox.example" (sandbox/normalize-base-url "https://sandbox.example")))
|
||||
(is (= "https://sandbox.example" (sandbox/normalize-base-url "https://sandbox.example/")))
|
||||
(is (= "http://localhost:8787" (sandbox/normalize-base-url "http://localhost:8787//")))))
|
||||
(is (= "http://localhost:8787" (sandbox/normalize-base-url "http://localhost:8787//")))
|
||||
(is (= "https://2468-sbx.e2b.app" (sandbox/normalize-base-url "2468-sbx.e2b.app")))
|
||||
(is (= "https://2468-sbx.e2b.app" (sandbox/normalize-base-url " 2468-sbx.e2b.app/ ")))
|
||||
(is (= "http://localhost:8787" (sandbox/normalize-base-url "localhost:8787")))))
|
||||
|
||||
(deftest session-endpoint-test
|
||||
(testing "builds sandbox session endpoints"
|
||||
|
||||
@@ -643,6 +643,7 @@
|
||||
session-created? (or session-started?
|
||||
(agent-handler/task-session-created? block))
|
||||
terminal-enabled? (agent-handler/session-terminal-enabled? session)
|
||||
snapshot-enabled? (agent-handler/session-snapshot-enabled? session)
|
||||
session-chat-messages (->> session-messages
|
||||
(map message->chat-message)
|
||||
(remove nil?))
|
||||
@@ -1020,7 +1021,7 @@
|
||||
:class "h-7 px-2 text-xs"
|
||||
:on-click (fn [_] (close-terminal!))}
|
||||
"Disconnect"))])
|
||||
(when-not pr-created?
|
||||
(when (and (not pr-created?) snapshot-enabled?)
|
||||
(shui/button
|
||||
{:size :sm
|
||||
:variant :outline
|
||||
|
||||
@@ -354,7 +354,11 @@
|
||||
(some-> provider str string/trim string/lower-case not-empty))
|
||||
|
||||
(defn- runtime-provider-terminal-enabled? [provider]
|
||||
(= "cloudflare" (normalize-runtime-provider provider)))
|
||||
(contains? #{"cloudflare" "e2b"}
|
||||
(normalize-runtime-provider provider)))
|
||||
|
||||
(defn- runtime-provider-snapshot-enabled? [provider]
|
||||
(not= "e2b" (normalize-runtime-provider provider)))
|
||||
|
||||
(defn- event-runtime-provider [event]
|
||||
(when (= "session.provisioned" (:type event))
|
||||
@@ -381,6 +385,15 @@
|
||||
first))]
|
||||
(runtime-provider-terminal-enabled? provider)))
|
||||
|
||||
(defn session-snapshot-enabled?
|
||||
[session]
|
||||
(let [provider (or (normalize-runtime-provider (:runtime-provider session))
|
||||
(some->> (:events session)
|
||||
reverse
|
||||
(keep event-runtime-provider)
|
||||
first))]
|
||||
(runtime-provider-snapshot-enabled? provider)))
|
||||
|
||||
(defn- status->label [status-ident]
|
||||
(some-> (db/entity status-ident) :block/title))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user