From baf23961ea20e821c1c02c2968a8b4e57cfe9330 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Sat, 28 Feb 2026 21:28:28 +0800 Subject: [PATCH] add project sandbox init setup --- deps/db/src/logseq/db/frontend/class.cljs | 3 +- deps/db/src/logseq/db/frontend/property.cljs | 6 + deps/db/src/logseq/db/frontend/schema.cljs | 2 +- .../db/src/logseq/db/sqlite/create_graph.cljs | 2 +- .../src/logseq/agents/runtime_provider.cljs | 36 +++++ .../workers/src/logseq/sync/malli_schema.cljs | 3 +- .../logseq/agents/runtime_provider_test.cljs | 140 ++++++++++++++++++ src/main/frontend/handler/agent.cljs | 4 +- src/main/frontend/worker/db/migrate.cljs | 3 +- src/test/frontend/handler/agent_test.cljs | 18 +++ src/test/frontend/worker/migrate_test.cljs | 13 ++ 11 files changed, 224 insertions(+), 6 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/class.cljs b/deps/db/src/logseq/db/frontend/class.cljs index d4335c3679..fb41f3ad33 100644 --- a/deps/db/src/logseq/db/frontend/class.cljs +++ b/deps/db/src/logseq/db/frontend/class.cljs @@ -44,7 +44,8 @@ :logseq.class/Project {:title "Project" - :schema {:properties [:logseq.property/git-repo] + :schema {:properties [:logseq.property/git-repo + :logseq.property/project-sandbox-init-setup] :required-properties [:logseq.property/git-repo]}} :logseq.class/Agent diff --git a/deps/db/src/logseq/db/frontend/property.cljs b/deps/db/src/logseq/db/frontend/property.cljs index 4722257ea8..82e2a9d905 100644 --- a/deps/db/src/logseq/db/frontend/property.cljs +++ b/deps/db/src/logseq/db/frontend/property.cljs @@ -437,6 +437,12 @@ :public? true :view-context :page} :queryable? true} + :logseq.property/project-sandbox-init-setup + {:title "Project sandbox init setup" + :schema {:type :default + :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/pr {:title "PR" :schema {:type :url diff --git a/deps/db/src/logseq/db/frontend/schema.cljs b/deps/db/src/logseq/db/frontend/schema.cljs index fc4b6baa6e..a5421c67e6 100644 --- a/deps/db/src/logseq/db/frontend/schema.cljs +++ b/deps/db/src/logseq/db/frontend/schema.cljs @@ -30,7 +30,7 @@ (map (juxt :major :minor) [(parse-schema-version x) (parse-schema-version y)]))) -(def version (parse-schema-version "65.25")) +(def version (parse-schema-version "65.26")) (defn major-version "Return a number. diff --git a/deps/db/src/logseq/db/sqlite/create_graph.cljs b/deps/db/src/logseq/db/sqlite/create_graph.cljs index 8db106b36e..46d55c9d69 100644 --- a/deps/db/src/logseq/db/sqlite/create_graph.cljs +++ b/deps/db/src/logseq/db/sqlite/create_graph.cljs @@ -250,7 +250,7 @@ hidden-pages (concat (build-initial-views) (build-favorites-page)) ;; These classes bootstrap our tags and properties as they depend on each other e.g. ;; Root <-> Tag, classes-tx depends on logseq.property.class/extends, properties-tx depends on Property - bootstrap-class? (fn [c] (contains? #{:logseq.class/Root :logseq.class/Property :logseq.class/Tag :logseq.class/Template :logseq.class/Agent :logseq.class/Project} (:db/ident c))) + bootstrap-class? (fn [c] (contains? #{:logseq.class/Root :logseq.class/Property :logseq.class/Tag :logseq.class/Template :logseq.class/Agent :logseq.class/Project :logseq.class/Code-block} (:db/ident c))) bootstrap-classes (filter bootstrap-class? default-classes) bootstrap-class-ids (map #(select-keys % [:db/ident :block/uuid]) bootstrap-classes) classes-tx (concat (map #(dissoc % :db/ident) bootstrap-classes) diff --git a/deps/workers/src/logseq/agents/runtime_provider.cljs b/deps/workers/src/logseq/agents/runtime_provider.cljs index 62aeeaa7d1..c59c343083 100644 --- a/deps/workers/src/logseq/agents/runtime_provider.cljs +++ b/deps/workers/src/logseq/agents/runtime_provider.cljs @@ -416,6 +416,24 @@ (defn- task-repo-url [task] (some-> (get-in task [:project :repo-url]) str string/trim not-empty)) +(defn- unwrap-code-block + [text] + (when (string? text) + (or (some->> text + (re-matches #"(?s)^```[^\n]*\n(.*)\n```$") + second + string/trim + not-empty) + text))) + +(defn- task-project-init-setup + [task] + (some-> (get-in task [:project :sandbox-init-setup]) + str + unwrap-code-block + string/trim + not-empty)) + (defn- session-id str) + repo-dir (get-repo-dir session-id)] + (when (and (string? setup-script) (string? repo-dir)) + (str "set -e; cd '" (escape-shell-single repo-dir) "'; " setup-script)))) + (defn- classify-push-error [error] (let [data (ex-data error) @@ -701,6 +727,10 @@ (when-let [cmd (repo-clone-command env session-id task)] (sprites-exec-post! env sprite-name ["bash" "-lc" cmd]))) +(defn- js (cond + create-session? + {:result {:stdout "{\"ok\":true}\n__HTTP_STATUS__:200" + :stderr ""}} + + health? + {:result {:stdout "__HEALTH_OK__" + :stderr ""}} + + :else + {:result {:stdout "" + :stderr ""}}))) + #js {:status 200 :headers #js {"content-type" "application/json"}}))) + + :else + (js/Promise.resolve + (js/Response. + (js/JSON.stringify #js {:error "unexpected request"}) + #js {:status 500 :headers #js {"content-type" "application/json"}})))))) + (-> (runtime-provider/ (runtime-provider/nil (pu/get-block-property-value project-page :logseq.property/git-repo)) + sandbox-init-setup (blank->nil (pu/get-block-property-value project-page :logseq.property/project-sandbox-init-setup)) project-id (some-> (:block/uuid project-page) str) title (blank->nil (:block/title project-page)) base-branch (blank->nil base-branch)] @@ -59,7 +60,8 @@ (cond-> {:id project-id :title title :repo-url repo-url} - (string? base-branch) (assoc :base-branch base-branch)))))) + (string? base-branch) (assoc :base-branch base-branch) + (string? sandbox-init-setup) (assoc :sandbox-init-setup sandbox-init-setup)))))) (defn- block-line-content [block] diff --git a/src/main/frontend/worker/db/migrate.cljs b/src/main/frontend/worker/db/migrate.cljs index ea9e473ab6..7b57a7f820 100644 --- a/src/main/frontend/worker/db/migrate.cljs +++ b/src/main/frontend/worker/db/migrate.cljs @@ -84,7 +84,8 @@ :logseq.property/agent-api-token :logseq.property/agent-auth-json]}] ["65.24" {:properties [:logseq.property/agent-session-created?]}] - ["65.25" {:properties [:logseq.property/pr]}]]) + ["65.25" {:properties [:logseq.property/pr]}] + ["65.26" {:properties [:logseq.property/project-sandbox-init-setup]}]]) (let [[major minor] (last (sort (map (comp (juxt :major :minor) db-schema/parse-schema-version first) schema-version->updates)))] diff --git a/src/test/frontend/handler/agent_test.cljs b/src/test/frontend/handler/agent_test.cljs index f2c39cd39a..b1f84e1f3e 100644 --- a/src/test/frontend/handler/agent_test.cljs +++ b/src/test/frontend/handler/agent_test.cljs @@ -12,6 +12,24 @@ [frontend.state :as state] [promesa.core :as p])) +(deftest build-session-body-includes-project-sandbox-init-setup-test + (let [project-page {:block/uuid #uuid "11111111-1111-1111-1111-111111111111" + :block/title "Project"} + block {:block/uuid #uuid "22222222-2222-2222-2222-222222222222" + :block/title "Task" + :logseq.property/project project-page + :logseq.property/agent {:block/title "Codex"}}] + (p/with-redefs [pu/get-block-property-value (fn [entity k] + (if (= entity project-page) + (case k + :logseq.property/git-repo "https://github.com/example/repo" + :logseq.property/project-sandbox-init-setup "yarn install" + nil) + (get entity k)))] + (is (= "yarn install" + (get-in (agent-handler/build-session-body block) + [:project :sandbox-init-setup])))))) + (deftest start-session-sends-initial-message-test (async done (let [calls (atom []) diff --git a/src/test/frontend/worker/migrate_test.cljs b/src/test/frontend/worker/migrate_test.cljs index a51d4adc01..71443a0994 100644 --- a/src/test/frontend/worker/migrate_test.cljs +++ b/src/test/frontend/worker/migrate_test.cljs @@ -54,3 +54,16 @@ (is (= {:major 65 :minor 25} (:kv/value (d/entity @conn :logseq.kv/schema-version)))) (is (some? (d/entity @conn property-ident))))) + +(deftest migrate-adds-project-sandbox-init-setup-property-builtin + (let [conn (db-test/create-conn) + property-ident :logseq.property/project-sandbox-init-setup + _ (d/transact! conn [{:db/ident :logseq.kv/schema-version + :kv/value {:major 65 :minor 25}}]) + existing-eid (d/entid @conn property-ident) + _ (when existing-eid + (d/transact! conn [[:db/retractEntity existing-eid]])) + _ (db-migrate/migrate conn :target-version "65.26")] + (is (= {:major 65 :minor 26} + (:kv/value (d/entity @conn :logseq.kv/schema-version)))) + (is (some? (d/entity @conn property-ident)))))