mirror of
https://github.com/logseq/logseq.git
synced 2026-05-23 12:14:06 +00:00
e2b template
This commit is contained in:
1
deps/db/src/logseq/db/frontend/class.cljs
vendored
1
deps/db/src/logseq/db/frontend/class.cljs
vendored
@@ -45,6 +45,7 @@
|
||||
:logseq.class/Project
|
||||
{:title "Project"
|
||||
:schema {:properties [:logseq.property/git-repo
|
||||
:logseq.property/project.docker-file
|
||||
:logseq.property/project-sandbox-init-setup]
|
||||
:required-properties [:logseq.property/git-repo]}}
|
||||
|
||||
|
||||
6
deps/db/src/logseq/db/frontend/property.cljs
vendored
6
deps/db/src/logseq/db/frontend/property.cljs
vendored
@@ -449,6 +449,12 @@
|
||||
:public? true
|
||||
:view-context :page}
|
||||
:properties {:logseq.property/description "Runs after sandbox startup is ready. Store setup commands in a code block, e.g. `yarn install`."}}
|
||||
:logseq.property/project.docker-file
|
||||
{:title "Project Dockerfile"
|
||||
:schema {:type :default
|
||||
:public? true
|
||||
:view-context :page}
|
||||
:properties {:logseq.property/description "Store Dockerfile content in a code block. Used to build the sandbox image before startup."}}
|
||||
:logseq.property/pr
|
||||
{:title "PR"
|
||||
:schema {:type :url
|
||||
|
||||
2
deps/db/src/logseq/db/frontend/schema.cljs
vendored
2
deps/db/src/logseq/db/frontend/schema.cljs
vendored
@@ -30,7 +30,7 @@
|
||||
(map (juxt :major :minor)
|
||||
[(parse-schema-version x) (parse-schema-version y)])))
|
||||
|
||||
(def version (parse-schema-version "65.28"))
|
||||
(def version (parse-schema-version "65.29"))
|
||||
|
||||
(defn major-version
|
||||
"Return a number.
|
||||
|
||||
@@ -63,3 +63,13 @@
|
||||
(testing "logseq property namespace"
|
||||
(is (db-property/logseq-property? :logseq.property.reaction/emoji-id))
|
||||
(is (db-property/logseq-property? :logseq.property.reaction/target)))))
|
||||
|
||||
(deftest project-docker-file-built-in-property
|
||||
(let [props db-property/built-in-properties]
|
||||
(is (contains? props :logseq.property/project.docker-file))
|
||||
(is (= "Project Dockerfile"
|
||||
(get-in props [:logseq.property/project.docker-file :title])))
|
||||
(is (= :default
|
||||
(get-in props [:logseq.property/project.docker-file :schema :type])))
|
||||
(is (= true
|
||||
(get-in props [:logseq.property/project.docker-file :schema :public?])))))
|
||||
|
||||
41
deps/workers/src/logseq/agents/do.cljs
vendored
41
deps/workers/src/logseq/agents/do.cljs
vendored
@@ -132,6 +132,30 @@
|
||||
checkpoint
|
||||
(merge checkpoint (checkpoint-bundle-fields bundle))))
|
||||
|
||||
(def ^:private checkpoint-bundle-ks
|
||||
[:bundle-id
|
||||
:bundle-seq
|
||||
:bundle-object-key
|
||||
:bundle-byte-size
|
||||
:bundle-checksum
|
||||
:bundle-head-sha
|
||||
:bundle-base-sha
|
||||
:bundle-head-branch])
|
||||
|
||||
(defn- strip-checkpoint-bundle
|
||||
[checkpoint]
|
||||
(if (map? checkpoint)
|
||||
(apply dissoc checkpoint checkpoint-bundle-ks)
|
||||
checkpoint))
|
||||
|
||||
(defn- runtime-provider-id
|
||||
[runtime]
|
||||
(some-> (:provider runtime) str string/lower-case))
|
||||
|
||||
(defn- workspace-bundle-required?
|
||||
[runtime]
|
||||
(not= "e2b" (runtime-provider-id runtime)))
|
||||
|
||||
(defn- runtime-checkpoint-payload
|
||||
[runtime result reason]
|
||||
(let [snapshot-id (runtime-snapshot-id result)
|
||||
@@ -423,10 +447,12 @@
|
||||
(string? reason) (assoc :reason reason))
|
||||
:ts (common/now-ms)})]
|
||||
(if (map? checkpoint)
|
||||
(p/let [checkpoint (<checkpoint-workspace-bundle! self current-session checkpoint (cond-> {:by by
|
||||
:reason reason}
|
||||
(string? head-branch)
|
||||
(assoc :head-branch head-branch)))
|
||||
(p/let [checkpoint (if (workspace-bundle-required? (:runtime current-session))
|
||||
(<checkpoint-workspace-bundle! self current-session checkpoint (cond-> {:by by
|
||||
:reason reason}
|
||||
(string? head-branch)
|
||||
(assoc :head-branch head-branch)))
|
||||
(p/resolved (strip-checkpoint-bundle checkpoint)))
|
||||
_ (<persist-session-checkpoint! self (:id current-session) checkpoint)
|
||||
_ (<append-event! self {:type "sandbox.checkpoint.succeeded"
|
||||
:data (checkpoint-event-data checkpoint
|
||||
@@ -1113,9 +1139,12 @@
|
||||
nil
|
||||
|
||||
:else
|
||||
(p/let [restored-bundle (<restore-workspace-bundle! self session-id task runtime)]
|
||||
(p/let [restored-bundle (if (workspace-bundle-required? runtime)
|
||||
(<restore-workspace-bundle! self session-id task runtime)
|
||||
(p/resolved nil))]
|
||||
(let [base-checkpoint (or (runtime-checkpoint-payload runtime runtime "provisioned")
|
||||
(checkpoint-payload-with-reason d1-checkpoint "provisioned"))
|
||||
(some-> (checkpoint-payload-with-reason d1-checkpoint "provisioned")
|
||||
strip-checkpoint-bundle))
|
||||
runtime-checkpoint (merge-checkpoint-bundle base-checkpoint restored-bundle)
|
||||
session (-> session
|
||||
(assoc :runtime runtime)
|
||||
|
||||
@@ -949,6 +949,19 @@
|
||||
(some-> (get-in task [:runtime :template]) str string/trim not-empty)
|
||||
(env-str env "E2B_TEMPLATE")))
|
||||
|
||||
(defn- task-graph-id
|
||||
[task]
|
||||
(some-> (get-in task [:project :graph-id]) str string/trim not-empty))
|
||||
|
||||
(defn- project-docker-file
|
||||
[task]
|
||||
(some-> (get-in task [:project :docker-file]) str string/trim not-empty))
|
||||
|
||||
(defn- e2b-template-name
|
||||
[task]
|
||||
(when-let [graph-id (task-graph-id task)]
|
||||
(sanitize-name (str "logseq-" graph-id "-" (task-repo-name task)))))
|
||||
|
||||
(defn- e2b-agent-token
|
||||
[^js env runtime]
|
||||
(or (:agent-token runtime)
|
||||
@@ -1163,6 +1176,41 @@
|
||||
(seq env-vars) (assoc :envs env-vars)
|
||||
(string? session-id) (assoc :metadata metadata))))
|
||||
|
||||
(defn- e2b-template-fn
|
||||
[]
|
||||
(aget e2b "Template"))
|
||||
|
||||
(defn- e2b-template-missing-error?
|
||||
[error]
|
||||
(let [message (some-> error str)]
|
||||
(boolean (and (string? message)
|
||||
(string/starts-with? message "Error: 404:")))))
|
||||
|
||||
(defn- <e2b-existing-template!
|
||||
[^js env template-name]
|
||||
(let [template-fn (e2b-template-fn)
|
||||
get-tags (js-method template-fn "getTags")]
|
||||
(when-not (fn? template-fn)
|
||||
(throw (ex-info "e2b sdk missing Template"
|
||||
{:reason :missing-e2b-template-sdk})))
|
||||
(when-not (fn? get-tags)
|
||||
(throw (ex-info "e2b sdk missing Template.getTags"
|
||||
{:reason :missing-e2b-template-get-tags})))
|
||||
(-> (->promise (.call get-tags template-fn template-name (clj->js (e2b-api-opts env))))
|
||||
(p/then (fn [_tags] template-name))
|
||||
(p/catch (fn [error]
|
||||
(if (e2b-template-missing-error? error)
|
||||
nil
|
||||
(throw error)))))))
|
||||
|
||||
(defn- <e2b-resolve-template!
|
||||
[^js env task runtime]
|
||||
(if (project-docker-file task)
|
||||
(if-let [template-name (e2b-template-name task)]
|
||||
(<e2b-existing-template! env template-name)
|
||||
(p/resolved nil))
|
||||
(p/resolved (e2b-template env task runtime))))
|
||||
|
||||
(defn- e2b-server-command
|
||||
[^js env task session-id port agent-token env-vars]
|
||||
(let [auth-json (get-in task [:agent :auth-json])
|
||||
@@ -1239,14 +1287,11 @@
|
||||
nil))))))
|
||||
|
||||
(defn- <e2b-create-sandbox-for-restore!
|
||||
[^js env template checkpoint create-opts]
|
||||
[^js env checkpoint create-opts create-fn]
|
||||
(p/let [checkpoint-restore (<e2b-create-sandbox-from-checkpoint! env checkpoint create-opts)]
|
||||
(if (map? checkpoint-restore)
|
||||
checkpoint-restore
|
||||
(p/let [sandbox (<e2b-create-sandbox! env template create-opts)]
|
||||
{:sandbox sandbox
|
||||
:snapshot-id nil
|
||||
:restored? false}))))
|
||||
(create-fn))))
|
||||
|
||||
(defn- <e2b-runtime-base-url!
|
||||
[^js env runtime]
|
||||
@@ -2631,14 +2676,19 @@
|
||||
repo-dir (get-repo-dir session-id task "e2b")
|
||||
checkpoint (task-sandbox-checkpoint task)
|
||||
backup-key (or (:backup-key checkpoint)
|
||||
(repo-backup-key task))
|
||||
template (e2b-template env task nil)]
|
||||
(repo-backup-key task))]
|
||||
(p/let [env-vars (<e2b-agent-env-vars! env task)
|
||||
create-opts (e2b-create-opts env session-id env-vars)
|
||||
{:keys [sandbox snapshot-id restored?]} (<e2b-create-sandbox-for-restore! env
|
||||
template
|
||||
checkpoint
|
||||
create-opts)
|
||||
{:keys [sandbox snapshot-id restored? template]} (<e2b-create-sandbox-for-restore! env
|
||||
checkpoint
|
||||
create-opts
|
||||
(fn []
|
||||
(p/let [template (<e2b-resolve-template! env task nil)
|
||||
sandbox (<e2b-create-sandbox! env template create-opts)]
|
||||
{:sandbox sandbox
|
||||
:snapshot-id nil
|
||||
:restored? false
|
||||
:template template})))
|
||||
_ (<e2b-ensure-running! env sandbox session-id task port agent-token env-vars)
|
||||
_ (when-not restored?
|
||||
(p/catch
|
||||
@@ -2649,14 +2699,6 @@
|
||||
: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-data (ex-data error)})
|
||||
nil))
|
||||
base-url (e2b-sandbox-host sandbox port)
|
||||
response (sandbox/<create-session base-url agent-token session-id payload)
|
||||
sandbox-id (e2b-sandbox-id sandbox)]
|
||||
|
||||
@@ -234,6 +234,8 @@
|
||||
[:title :string]
|
||||
[:repo-url :string]
|
||||
[:base-branch {:optional true} :string]
|
||||
[:graph-id {:optional true} :string]
|
||||
[:docker-file {:optional true} :string]
|
||||
[:sandbox-init-setup {:optional true} :string]]]
|
||||
[:agent [:or :string
|
||||
[:map
|
||||
|
||||
141
deps/workers/test/logseq/agents/do_test.cljs
vendored
141
deps/workers/test/logseq/agents/do_test.cljs
vendored
@@ -742,6 +742,67 @@
|
||||
(is false (str "unexpected checkpoint bundle error: " error))
|
||||
(done))))))))
|
||||
|
||||
(deftest checkpoint-existing-snapshot-skips-workspace-bundle-for-e2b-test
|
||||
(testing "e2b checkpoint refresh keeps snapshot metadata without persisting workspace bundle"
|
||||
(async done
|
||||
(let [env #js {"AGENT_RUNTIME_PROVIDER" "e2b"}
|
||||
self (make-self env)
|
||||
export-calls (atom 0)
|
||||
put-calls (atom 0)
|
||||
upsert-calls (atom 0)
|
||||
session {:id "sess-checkpoint-e2b"
|
||||
:status "running"
|
||||
:task {:id "sess-checkpoint-e2b"
|
||||
:project {:repo-url "https://github.com/logseq/logseq"
|
||||
:base-branch "main"}
|
||||
:sandbox-checkpoint {:provider "e2b"
|
||||
:snapshot-id "e2b-snapshot-1"
|
||||
:bundle-id "old-bundle"
|
||||
:bundle-object-key "workspace-bundles/old.bundle.b64"}}
|
||||
:runtime {:provider "e2b"
|
||||
:session-id "sess-checkpoint-e2b"
|
||||
:sandbox-id "sbx-checkpoint-e2b"
|
||||
:snapshot-id "e2b-snapshot-1"
|
||||
:backup-key "github/logseq/logseq#main"
|
||||
:backup-dir "/home/user/workspace/logseq"}
|
||||
:audit {}
|
||||
:created-at 0
|
||||
:updated-at 0}]
|
||||
(-> (.put (.-storage self) "session" (clj->js session))
|
||||
(.then (fn [_]
|
||||
(with-redefs [checkpoint-store/<upsert-checkpoint-for-task! (fn [_env _task checkpoint]
|
||||
(js/Promise.resolve checkpoint))
|
||||
runtime-provider/<export-workspace-bundle! (fn [_provider _runtime _opts]
|
||||
(swap! export-calls inc)
|
||||
(js/Promise.resolve nil))
|
||||
workspace-bundle-r2/<put-bundle-base64! (fn [_env _object-key _bundle-base64 _metadata]
|
||||
(swap! put-calls inc)
|
||||
(js/Promise.resolve nil))
|
||||
workspace-bundle-store/<upsert-bundle-for-task! (fn [_env _task _bundle]
|
||||
(swap! upsert-calls inc)
|
||||
(js/Promise.resolve nil))]
|
||||
(#'agent-do/<checkpoint-existing-snapshot! self
|
||||
session
|
||||
{:by "system"
|
||||
:reason "pr-ready"}))))
|
||||
(.then (fn [ok?]
|
||||
(is (true? ok?))
|
||||
(is (zero? @export-calls))
|
||||
(is (zero? @put-calls))
|
||||
(is (zero? @upsert-calls))
|
||||
(.then (.get (.-storage self) "session")
|
||||
(fn [session-js]
|
||||
(let [stored (js->clj session-js :keywordize-keys true)
|
||||
checkpoint (get-in stored [:task :sandbox-checkpoint])]
|
||||
(is (= "e2b-snapshot-1" (:snapshot-id checkpoint)))
|
||||
(is (= "e2b" (:provider checkpoint)))
|
||||
(is (nil? (:bundle-id checkpoint)))
|
||||
(is (nil? (:bundle-object-key checkpoint)))
|
||||
(done))))))
|
||||
(.catch (fn [error]
|
||||
(is false (str "unexpected e2b checkpoint error: " error))
|
||||
(done))))))))
|
||||
|
||||
(deftest provision-runtime-restores-workspace-bundle-test
|
||||
(testing "provision runtime applies latest workspace bundle and stores bundle metadata in checkpoint"
|
||||
(async done
|
||||
@@ -814,6 +875,86 @@
|
||||
(is false (str "unexpected restore bundle error: " error))
|
||||
(done))))))))
|
||||
|
||||
(deftest provision-runtime-skips-workspace-bundle-restore-for-e2b-test
|
||||
(testing "e2b provision should not restore workspace bundle metadata from R2"
|
||||
(async done
|
||||
(let [env #js {"AGENT_RUNTIME_PROVIDER" "e2b"
|
||||
"AGENTS_DB" #js {}}
|
||||
self (make-self env)
|
||||
task {:id "sess-restore-e2b"
|
||||
:agent "codex"
|
||||
:project {:repo-url "https://github.com/logseq/logseq"
|
||||
:base-branch "main"}}
|
||||
runtime {:provider "e2b"
|
||||
:session-id "runtime-restore-e2b"
|
||||
:sandbox-id "sbx-restore-e2b"
|
||||
:snapshot-id "e2b-snapshot-restored"}
|
||||
get-bundle-calls (atom 0)
|
||||
apply-calls (atom 0)
|
||||
provider (reify runtime-provider/RuntimeProvider
|
||||
(<provision-runtime! [_ _session-id _task]
|
||||
(js/Promise.resolve runtime))
|
||||
(<open-events-stream! [_ _runtime]
|
||||
(js/Promise.resolve nil))
|
||||
(<send-message! [_ _runtime _message]
|
||||
(js/Promise.resolve true))
|
||||
(<open-terminal! [_ _runtime _request _opts]
|
||||
(js/Promise.resolve nil))
|
||||
(<snapshot-runtime! [_ _runtime _opts]
|
||||
(js/Promise.resolve nil))
|
||||
(<export-workspace-bundle! [_ _runtime _opts]
|
||||
(js/Promise.resolve nil))
|
||||
(<apply-workspace-bundle! [_ _runtime _opts]
|
||||
(swap! apply-calls inc)
|
||||
(js/Promise.resolve true))
|
||||
(<push-branch! [_ _runtime _opts]
|
||||
(js/Promise.resolve nil))
|
||||
(<terminate-runtime! [_ _runtime]
|
||||
(js/Promise.resolve nil)))]
|
||||
(-> (.put (.-storage self)
|
||||
"session"
|
||||
(clj->js {:id "sess-restore-e2b"
|
||||
:status "running"
|
||||
:task task
|
||||
:audit {}
|
||||
:created-at 0
|
||||
:updated-at 0}))
|
||||
(.then (fn [_]
|
||||
(with-redefs [runtime-provider/resolve-provider (fn [_env _runtime] provider)
|
||||
runtime-provider/provider-id (fn [_provider] "e2b")
|
||||
agent-do/start-runtime-events-stream-background! (fn [& _] nil)
|
||||
common/<d1-all (fn [_db _sql & _args]
|
||||
(js/Promise.resolve
|
||||
#js {:results #js [#js {"provider" "e2b"
|
||||
"snapshot_id" "snapshot-from-d1"
|
||||
"bundle_id" "bundle-restore-1"
|
||||
"bundle_seq" 3
|
||||
"bundle_object_key" "workspace-bundles/github/logseq/logseq/main/bundle-restore-1.bundle.b64"
|
||||
"checkpoint_at" 1000}]}))
|
||||
common/get-sql-rows (fn [result]
|
||||
(aget result "results"))
|
||||
workspace-bundle-store/<load-latest-bundle-for-task! (fn [_env _task _session-id]
|
||||
(js/Promise.resolve {:bundle-id "bundle-restore-1"}))
|
||||
workspace-bundle-r2/<get-bundle-base64! (fn [_env _object-key]
|
||||
(swap! get-bundle-calls inc)
|
||||
(js/Promise.resolve {:bundle-base64 "ZmFrZS1idW5kbGUtZGF0YQ=="}))]
|
||||
(#'agent-do/<provision-runtime! self task "sess-restore-e2b"))))
|
||||
(.then (fn [_]
|
||||
(is (zero? @get-bundle-calls))
|
||||
(is (zero? @apply-calls))
|
||||
(.then (.get (.-storage self) "session")
|
||||
(fn [session-js]
|
||||
(let [stored (js->clj session-js :keywordize-keys true)
|
||||
checkpoint (get-in stored [:task :sandbox-checkpoint])]
|
||||
(is (= "e2b-snapshot-restored" (:snapshot-id checkpoint)))
|
||||
(is (= "e2b" (:provider checkpoint)))
|
||||
(is (nil? (:bundle-id checkpoint)))
|
||||
(is (nil? (:bundle-object-key checkpoint)))
|
||||
(done))))))
|
||||
(.catch (fn [error]
|
||||
(is false (str "unexpected e2b restore provision error: " error))
|
||||
(done))))))))
|
||||
|
||||
(deftest restore-workspace-bundle-skips-when-session-changed-test
|
||||
(testing "bundle apply should be skipped when expected session is no longer current"
|
||||
(async done
|
||||
|
||||
@@ -141,6 +141,146 @@
|
||||
(is false (str "unexpected error: " error))
|
||||
(done)))))))
|
||||
|
||||
(deftest e2b-provider-provision-uses-existing-template-from-project-docker-file-test
|
||||
(async done
|
||||
(let [calls (atom [])
|
||||
env #js {"E2B_API_KEY" "e2b-key"
|
||||
"SANDBOX_AGENT_TOKEN" "agent-token"
|
||||
"E2B_TEMPLATE" "fallback-template"}
|
||||
provider (runtime-provider/create-provider env "e2b")
|
||||
task {:agent {:provider "codex"}
|
||||
:project {:repo-url "https://github.com/logseq/agent-test"
|
||||
:graph-id "graph-123"
|
||||
:docker-file "FROM node:20\nRUN corepack enable"}}
|
||||
e2b-ns (js/require "e2b")
|
||||
original-template (aget e2b-ns "Template")
|
||||
sandbox-class (runtime-provider/e2b-sandbox-class)
|
||||
original-create (aget sandbox-class "create")
|
||||
original-fetch js/fetch
|
||||
restore! (fn []
|
||||
(aset e2b-ns "Template" original-template)
|
||||
(aset sandbox-class "create" original-create)
|
||||
(set! js/fetch original-fetch))
|
||||
template-fn (fn [] #js {})]
|
||||
(aset template-fn "getTags"
|
||||
(fn [name opts]
|
||||
(swap! calls conj {:type :get-tags
|
||||
:name name
|
||||
:opts (js->clj opts :keywordize-keys true)})
|
||||
(js/Promise.resolve #js [])))
|
||||
(aset e2b-ns "Template" template-fn)
|
||||
(aset sandbox-class "create"
|
||||
(fn [& args]
|
||||
(let [template (first args)
|
||||
opts (js->clj (last args) :keywordize-keys true)]
|
||||
(swap! calls conj {:type :create
|
||||
:template template
|
||||
:opts opts})
|
||||
(js/Promise.resolve
|
||||
#js {:sandboxId "e2b-sbx-docker"
|
||||
:getHost (fn [_port]
|
||||
"https://e2b-agent.local")
|
||||
: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]
|
||||
(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-docker" task)
|
||||
(.then (fn [runtime]
|
||||
(restore!)
|
||||
(is (= "e2b" (:provider runtime)))
|
||||
(is (= "logseq-graph-123-agent-test" (:template runtime)))
|
||||
(is (some #(and (= :get-tags (:type %))
|
||||
(= "logseq-graph-123-agent-test" (:name %))
|
||||
(= "e2b-key" (get-in % [:opts :apiKey])))
|
||||
@calls))
|
||||
(is (some #(and (= :create (:type %))
|
||||
(= "logseq-graph-123-agent-test" (:template %)))
|
||||
@calls))
|
||||
(done)))
|
||||
(.catch (fn [error]
|
||||
(restore!)
|
||||
(is false (str "unexpected error: " error))
|
||||
(done)))))))
|
||||
|
||||
(deftest e2b-provider-provision-falls-back-to-default-sandbox-when-template-missing-test
|
||||
(async done
|
||||
(let [calls (atom [])
|
||||
env #js {"E2B_API_KEY" "e2b-key"
|
||||
"SANDBOX_AGENT_TOKEN" "agent-token"
|
||||
"E2B_TEMPLATE" "fallback-template"}
|
||||
provider (runtime-provider/create-provider env "e2b")
|
||||
task {:agent {:provider "codex"}
|
||||
:project {:repo-url "https://github.com/logseq/agent-test"
|
||||
:graph-id "graph-123"
|
||||
:docker-file "FROM node:20\nRUN corepack enable"}}
|
||||
e2b-ns (js/require "e2b")
|
||||
original-template (aget e2b-ns "Template")
|
||||
sandbox-class (runtime-provider/e2b-sandbox-class)
|
||||
original-create (aget sandbox-class "create")
|
||||
original-fetch js/fetch
|
||||
restore! (fn []
|
||||
(aset e2b-ns "Template" original-template)
|
||||
(aset sandbox-class "create" original-create)
|
||||
(set! js/fetch original-fetch))
|
||||
template-fn (fn [] #js {})]
|
||||
(aset template-fn "getTags"
|
||||
(fn [_name _opts]
|
||||
(js/Promise.reject (js/Error. "404: Not Found"))))
|
||||
(aset e2b-ns "Template" template-fn)
|
||||
(aset sandbox-class "create"
|
||||
(fn [& args]
|
||||
(swap! calls conj {:type :create
|
||||
:argc (count args)
|
||||
:first-arg (first args)
|
||||
:last-arg (js->clj (last args) :keywordize-keys true)})
|
||||
(js/Promise.resolve
|
||||
#js {:sandboxId "e2b-sbx-docker-default"
|
||||
:getHost (fn [_port]
|
||||
"https://e2b-agent.local")
|
||||
: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]
|
||||
(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-docker-default" task)
|
||||
(.then (fn [runtime]
|
||||
(restore!)
|
||||
(is (= "e2b" (:provider runtime)))
|
||||
(is (nil? (:template runtime)))
|
||||
(is (some #(and (= :create (:type %))
|
||||
(= 1 (:argc %))
|
||||
(map? (:first-arg %))
|
||||
(= "e2b-key" (get-in % [:first-arg :apiKey])))
|
||||
@calls))
|
||||
(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"}
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
:runner-id "runner-1"
|
||||
:project {:id "project-1"
|
||||
:title "Demo Project"
|
||||
:repo-url "https://github.com/example/repo"}
|
||||
:repo-url "https://github.com/example/repo"
|
||||
:graph-id "graph-1"
|
||||
:docker-file "FROM node:20\nRUN corepack enable"}
|
||||
:agent "codex"}
|
||||
coerced (http/coerce-http-request :sessions/create body)]
|
||||
(is (= body coerced))))
|
||||
@@ -30,7 +32,9 @@
|
||||
:attachments ["https://example.com/a.png" "https://example.com/b.png"]
|
||||
:project {:id "project-1"
|
||||
:title "Demo Project"
|
||||
:repo-url "https://github.com/example/repo"}
|
||||
:repo-url "https://github.com/example/repo"
|
||||
:graph-id "graph-1"
|
||||
:docker-file "FROM node:20\nRUN corepack enable"}
|
||||
:agent {:provider "codex"
|
||||
:api-token "token-123"
|
||||
:auth-json "{\"tokens\":{\"access_token\":\"abc\"}}"}
|
||||
@@ -47,7 +51,9 @@
|
||||
"https://example.com/b.png"]}
|
||||
:project {:id "project-1"
|
||||
:title "Demo Project"
|
||||
:repo-url "https://github.com/example/repo"}
|
||||
:repo-url "https://github.com/example/repo"
|
||||
:graph-id "graph-1"
|
||||
:docker-file "FROM node:20\nRUN corepack enable"}
|
||||
:agent {:provider "codex"
|
||||
:api-token "token-123"
|
||||
:auth-json "{\"tokens\":{\"access_token\":\"abc\"}}"}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
[lambdaisland.glogi :as log]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.shui.ui :as shui]
|
||||
[logseq.sync.malli-schema :as db-sync-schema]
|
||||
[promesa.core :as p]))
|
||||
@@ -105,9 +106,12 @@
|
||||
([project-page {:keys [base-branch]}]
|
||||
(let [repo-url (blank->nil (or (pu/get-block-property-value project-page :logseq.property/git-repo)
|
||||
(:logseq.property/git-repo project-page)))
|
||||
docker-file (blank->nil (or (pu/get-block-property-value project-page :logseq.property/project.docker-file)
|
||||
(:logseq.property/project.docker-file project-page)))
|
||||
sandbox-init-setup (blank->nil (or (pu/get-block-property-value project-page :logseq.property/project-sandbox-init-setup)
|
||||
(:logseq.property/project-sandbox-init-setup project-page)))
|
||||
project-id (some-> (:block/uuid project-page) str)
|
||||
graph-id (some-> (ldb/get-graph-rtc-uuid (db/get-db)) str blank->nil)
|
||||
title (blank->nil (:block/title project-page))
|
||||
base-branch (blank->nil base-branch)]
|
||||
(when (and project-id title repo-url)
|
||||
@@ -115,6 +119,8 @@
|
||||
:title title
|
||||
:repo-url repo-url}
|
||||
(string? base-branch) (assoc :base-branch base-branch)
|
||||
(string? graph-id) (assoc :graph-id graph-id)
|
||||
(string? docker-file) (assoc :docker-file docker-file)
|
||||
(string? sandbox-init-setup) (assoc :sandbox-init-setup sandbox-init-setup))))))
|
||||
|
||||
(defn- block-line-content
|
||||
|
||||
@@ -86,7 +86,8 @@
|
||||
:logseq.property/agent-auth-json]}]
|
||||
["65.25" {:properties [:logseq.property/pr]}]
|
||||
["65.27" {:properties [:logseq.property/agent-session-id]}]
|
||||
["65.28" {:properties [:logseq.property/sandbox-checkpoint]}]])
|
||||
["65.28" {:properties [:logseq.property/sandbox-checkpoint]}]
|
||||
["65.29" {:properties [:logseq.property/project.docker-file]}]])
|
||||
|
||||
(let [[major minor] (last (sort (map (comp (juxt :major :minor) db-schema/parse-schema-version first)
|
||||
schema-version->updates)))]
|
||||
|
||||
@@ -82,3 +82,18 @@
|
||||
(:kv/value (d/entity @conn :logseq.kv/schema-version))))
|
||||
(is (some? property))
|
||||
(is (= :map (:logseq.property/type property)))))
|
||||
|
||||
(deftest migrate-adds-project-docker-file-property-builtin
|
||||
(let [conn (db-test/create-conn)
|
||||
property-ident :logseq.property/project.docker-file
|
||||
_ (d/transact! conn [{:db/ident :logseq.kv/schema-version
|
||||
:kv/value {:major 65 :minor 28}}])
|
||||
existing-eid (d/entid @conn property-ident)
|
||||
_ (when existing-eid
|
||||
(d/transact! conn [[:db/retractEntity existing-eid]]))
|
||||
_ (db-migrate/migrate conn :target-version "65.29")
|
||||
property (d/entity @conn property-ident)]
|
||||
(is (= {:major 65 :minor 29}
|
||||
(:kv/value (d/entity @conn :logseq.kv/schema-version))))
|
||||
(is (some? property))
|
||||
(is (= :default (:logseq.property/type property)))))
|
||||
|
||||
Reference in New Issue
Block a user