mirror of
https://github.com/logseq/logseq.git
synced 2026-05-23 12:14:06 +00:00
e2b sandbox
This commit is contained in:
22
deps/workers/README.md
vendored
22
deps/workers/README.md
vendored
@@ -39,7 +39,7 @@ Local split note:
|
||||
`wrangler dev -c wrangler.toml -c wrangler.agents.toml --port 8787`
|
||||
and `/sessions*` will be forwarded via `AGENTS_SERVICE`.
|
||||
- If you run workers separately, call sessions on the agents port.
|
||||
- `worker/wrangler.agents.toml` sets `AGENT_RUNTIME_PROVIDER=cloudflare` by default.
|
||||
- `worker/wrangler.agents.toml` sets `AGENT_RUNTIME_PROVIDER=e2b` by default.
|
||||
- On localhost, `/sessions*` forwarding retries during agents startup (up to ~30s) to avoid transient `503`.
|
||||
|
||||
Production routing note:
|
||||
@@ -164,12 +164,20 @@ Create session pinned to local runner:
|
||||
### Runtime Provider
|
||||
|
||||
Agent runtime is selected by `AGENT_RUNTIME_PROVIDER`:
|
||||
- `e2b`: provisions/manages E2B sandboxes via the `e2b` SDK, then runs `sandbox-agent` inside the sandbox.
|
||||
- `sprites`: provisions a Sprite and runs `sandbox-agent` inside it.
|
||||
- `local-dev`: uses `SANDBOX_AGENT_URL` directly.
|
||||
- `local-runner`: selects a registered user runner endpoint from `AGENTS_DB` and runs through that endpoint.
|
||||
- `vercel`: provisions/manages Vercel sandboxes via `@vercel/sandbox`, then runs `sandbox-agent` inside the sandbox.
|
||||
- `cloudflare`: provisions a sandbox first, then connects to the sandbox-hosted `sandbox-agent`.
|
||||
- Agents worker default is `cloudflare` (set in `worker/wrangler.agents.toml`).
|
||||
- Agents worker default is `e2b` (set in `worker/wrangler.agents.toml`).
|
||||
|
||||
E2B runtime flow:
|
||||
- create or restore sandbox from template/snapshot
|
||||
- bootstrap `sandbox-agent` in sandbox
|
||||
- create runtime session via `sandbox-agent` HTTP API
|
||||
- stream events/messages through sandbox host URL
|
||||
- support runtime snapshot persistence and browser terminal open
|
||||
|
||||
For `cloudflare`, bind and export `Sandbox` in the agents worker and configure the container image in
|
||||
`worker/wrangler.agents.toml` (`[[containers]] class_name = "Sandbox"`).
|
||||
@@ -195,7 +203,7 @@ Cloudflare runtime flow:
|
||||
| DB_SYNC_STATIC_USER_ID | Static user id for local dev |
|
||||
| DB_SYNC_STATIC_EMAIL | Static user email for local dev |
|
||||
| DB_SYNC_STATIC_USERNAME | Static username for local dev |
|
||||
| AGENT_RUNTIME_PROVIDER | Runtime backend (`sprites`, `local-dev`, `local-runner`, `vercel`, `cloudflare`) |
|
||||
| AGENT_RUNTIME_PROVIDER | Runtime backend (`e2b`, `sprites`, `local-dev`, `local-runner`, `vercel`, `cloudflare`) |
|
||||
| SENTRY_DSN | Sentry DSN |
|
||||
| SENTRY_RELEASE | Release identifier for Sentry events and sourcemaps |
|
||||
| SENTRY_ENVIRONMENT | Sentry environment name (prod, staging, etc.) |
|
||||
@@ -205,6 +213,14 @@ Cloudflare runtime flow:
|
||||
| COGNITO_JWKS_URL | Cognito JWKS URL |
|
||||
| SANDBOX_AGENT_URL | sandbox-agent base URL for agent sessions |
|
||||
| SANDBOX_AGENT_TOKEN | Optional bearer token for sandbox-agent |
|
||||
| E2B_API_KEY | E2B API key for runtime provisioning |
|
||||
| E2B_DOMAIN | Optional E2B API domain override |
|
||||
| E2B_TEMPLATE | Optional E2B template name/id used for new sandboxes |
|
||||
| E2B_REPO_CLONE_COMMAND | Optional repo clone command template for E2B runtime |
|
||||
| E2B_SANDBOX_AGENT_PORT | sandbox-agent port inside E2B sandbox (default `2468`) |
|
||||
| E2B_SANDBOX_TIMEOUT_MS | E2B sandbox timeout in milliseconds (default `1800000`) |
|
||||
| E2B_HEALTH_RETRIES | E2B sandbox health check retry count |
|
||||
| E2B_HEALTH_INTERVAL_MS | E2B sandbox health check retry interval (ms) |
|
||||
| SPRITE_TOKEN | Sprites API token (default runtime auth) |
|
||||
| SPRITES_TOKEN | Alias for `SPRITE_TOKEN` |
|
||||
| SPRITES_API_URL | Sprites API base URL override |
|
||||
|
||||
@@ -27,3 +27,4 @@ Milestones are tracked as separate files in this folder:
|
||||
- `21-m21-store-snapshots-metadata-in-d1.md`
|
||||
- `22-m22-persist-workspace-git-bundles.md`
|
||||
- `23-m23-local-runner-via-tunnel.md`
|
||||
- `24-m24-e2b-sandbox-runtime-default.md`
|
||||
|
||||
97
deps/workers/docs/milestones/agents/24-m24-e2b-sandbox-runtime-default.md
vendored
Normal file
97
deps/workers/docs/milestones/agents/24-m24-e2b-sandbox-runtime-default.md
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
# M24: E2B Sandbox Runtime Provider (Default Runtime)
|
||||
|
||||
Status: Proposed
|
||||
Target: Add a first-class `e2b` runtime provider and make `e2b` the default runtime for new agent sessions.
|
||||
|
||||
## Goal
|
||||
Support `AGENT_RUNTIME_PROVIDER=e2b` end-to-end so sessions can provision E2B sandboxes, persist/restore workspace state, use template-based startup, and support browser terminal access.
|
||||
|
||||
## Why M24
|
||||
- E2B supports sandbox persistence and resume semantics that align with long-running agent sessions and interruption recovery.
|
||||
- E2B supports template-based environments, which reduces startup setup time and improves reproducibility.
|
||||
- E2B exposes PTY APIs for interactive terminals, which can be used to provide browser terminal capabilities.
|
||||
- Moving the default runtime to `e2b` gives a single hosted default while preserving explicit fallback providers.
|
||||
|
||||
## Inputs
|
||||
- E2B docs root: `https://e2b.dev/docs`
|
||||
- E2B quickstart (`E2B_API_KEY`, sandbox creation): `https://e2b.dev/docs/quickstart`
|
||||
- E2B sandbox persistence: `https://e2b.dev/docs/sandbox/persistence`
|
||||
- E2B interactive PTY terminal docs: `https://e2b.dev/docs/sandbox/pty`
|
||||
- E2B template quickstart: `https://e2b.dev/docs/template/quickstart`
|
||||
- E2B JS SDK sandbox reference (`Sandbox.create/connect/getHost/createSnapshot`, `commands`, `pty`): `https://e2b.dev/docs/sdk-reference/js-sdk/v2.0.1/sandbox`
|
||||
|
||||
## Scope
|
||||
1) Add `E2BProvider` under `src/logseq/agents/runtime_provider.cljs` implementing runtime lifecycle:
|
||||
- `<provision-runtime!`
|
||||
- `<open-events-stream!`
|
||||
- `<send-message!`
|
||||
- `<open-terminal!`
|
||||
- `<snapshot-runtime!`
|
||||
- `<terminate-runtime!`
|
||||
2) Add E2B snapshot persistence flow:
|
||||
- create snapshots from runtime
|
||||
- restore from `:sandbox-checkpoint` on reprovision
|
||||
- persist E2B snapshot metadata in runtime/session state
|
||||
3) Add E2B template support:
|
||||
- choose template from env/config (`E2B_TEMPLATE`)
|
||||
- allow explicit template overrides where appropriate
|
||||
4) Keep workspace bundle and git push parity for E2B runtime.
|
||||
5) Make `e2b` the default runtime provider:
|
||||
- when `AGENT_RUNTIME_PROVIDER` is unset
|
||||
- in `worker/wrangler.agents.toml` local/staging/prod defaults
|
||||
6) Add config/env plumbing and docs for E2B credentials/options.
|
||||
|
||||
## Out of Scope
|
||||
- Removing existing providers (`cloudflare`, `vercel`, `sprites`, `local-dev`, `local-runner`).
|
||||
- Cross-provider snapshot migration.
|
||||
- New user-facing UI for selecting templates/snapshots.
|
||||
|
||||
## Workstreams
|
||||
|
||||
### WS1: E2B Provider Implementation
|
||||
- Add provider kind normalization for `e2b`.
|
||||
- Add provider dispatch and provider ID handling for `e2b`.
|
||||
- Provision E2B sandbox via SDK, bootstrap `sandbox-agent`, clone repo, create runtime session.
|
||||
- Persist runtime metadata (`:provider`, `:sandbox-id`, `:sandbox-name`, `:sandbox-port`, `:base-url`, `:session-id`, `:snapshot-id`).
|
||||
|
||||
### WS2: Persistence + Recovery
|
||||
- Implement E2B snapshot create via SDK.
|
||||
- Restore from task checkpoint when provider is `e2b`.
|
||||
- Keep unsupported-provider behavior explicit for providers without persistence parity.
|
||||
|
||||
### WS3: Browser Terminal Support
|
||||
- Implement provider terminal open for E2B runtime.
|
||||
- Ensure terminal lifecycle events are emitted via existing DO flow.
|
||||
- Align timeout and reconnect behavior with E2B PTY/session semantics.
|
||||
|
||||
### WS4: Config + Defaults
|
||||
- Add E2B envs to node config/server passthrough and README:
|
||||
- `E2B_API_KEY`
|
||||
- `E2B_DOMAIN` (optional)
|
||||
- `E2B_TEMPLATE` (optional)
|
||||
- `E2B_SANDBOX_TIMEOUT_MS` (optional)
|
||||
- `E2B_SANDBOX_AGENT_PORT` (optional, default `2468`)
|
||||
- Set default runtime to `e2b` in provider fallback and wrangler agents config.
|
||||
|
||||
### WS5: Tests + Validation
|
||||
- Add runtime provider tests in `test/logseq/agents/runtime_provider_test.cljs` for:
|
||||
- provider selection/default dispatch to `e2b`
|
||||
- E2B provision/session wiring
|
||||
- E2B snapshot create/restore behavior
|
||||
- E2B terminal behavior
|
||||
- Verify existing provider tests still pass.
|
||||
|
||||
## Exit Criteria
|
||||
1) New sessions default to `e2b` when `AGENT_RUNTIME_PROVIDER` is unset.
|
||||
2) `AGENT_RUNTIME_PROVIDER=e2b` supports create/message/events end-to-end.
|
||||
3) E2B snapshots can be created and restored from checkpoint metadata.
|
||||
4) Browser terminal open works for E2B runtime sessions.
|
||||
5) Existing providers remain functional when explicitly selected.
|
||||
|
||||
## Validation
|
||||
- Add/update tests in:
|
||||
- `test/logseq/agents/runtime_provider_test.cljs`
|
||||
- `test/logseq/agents/do_test.cljs` (if runtime behavior contract changes require DO-level assertions)
|
||||
- Run targeted tests for changed namespaces.
|
||||
- Run full lint/test pass before rollout (`bb dev:lint-and-test` from repo root).
|
||||
|
||||
1
deps/workers/package.json
vendored
1
deps/workers/package.json
vendored
@@ -31,6 +31,7 @@
|
||||
"@sentry/node": "^10.38.0",
|
||||
"@vercel/sandbox": "1.7.1",
|
||||
"better-sqlite3": "^12.6.2",
|
||||
"e2b": "2.14.0",
|
||||
"shadow-cljs": "^3.3.4",
|
||||
"ws": "^8.18.3"
|
||||
},
|
||||
|
||||
7
deps/workers/shadow-cljs.edn
vendored
7
deps/workers/shadow-cljs.edn
vendored
@@ -43,4 +43,9 @@
|
||||
:output-to "worker/dist/worker-split-test.js"
|
||||
:ns-regexp "logseq\\.(sync\\.worker-test|agents\\.dispatch-test)$"
|
||||
:devtools {:enabled false}
|
||||
:main logseq.sync.worker-split-test-runner/main}}}
|
||||
:main logseq.sync.worker-split-test-runner/main}
|
||||
:agents-e2b-runtime-provider-test {:target :node-test
|
||||
:output-to "worker/dist/agents-e2b-runtime-provider-test.js"
|
||||
:ns-regexp "logseq\\.agents\\.e2b-runtime-provider-test$"
|
||||
:devtools {:enabled false}
|
||||
:main logseq.agents.e2b-runtime-provider-test-runner/main}}}
|
||||
|
||||
541
deps/workers/src/logseq/agents/runtime_provider.cljs
vendored
541
deps/workers/src/logseq/agents/runtime_provider.cljs
vendored
@@ -1,10 +1,12 @@
|
||||
(ns logseq.agents.runtime-provider
|
||||
(:require ["@cloudflare/sandbox" :as cf-sandbox]
|
||||
["@vercel/sandbox" :as vercel-sandbox]
|
||||
["e2b" :as e2b]
|
||||
[clojure.string :as string]
|
||||
[lambdaisland.glogi :as log]
|
||||
[logseq.agents.sandbox :as sandbox]
|
||||
[logseq.agents.source-control :as source-control]
|
||||
[logseq.sync.platform.core :as platform]
|
||||
[promesa.core :as p]))
|
||||
|
||||
;; -----------------------
|
||||
@@ -35,7 +37,7 @@
|
||||
(when-not (string/blank? normalized) normalized))))
|
||||
|
||||
(def ^:private supported-provider-kinds
|
||||
#{"sprites" "local-dev" "local-runner" "cloudflare" "vercel"})
|
||||
#{"e2b" "sprites" "local-dev" "local-runner" "cloudflare" "vercel"})
|
||||
|
||||
(defn- known-provider-kind [value]
|
||||
(let [provider (normalize-provider value)]
|
||||
@@ -44,7 +46,7 @@
|
||||
|
||||
(defn provider-kind [^js env]
|
||||
(or (known-provider-kind (env-str env "AGENT_RUNTIME_PROVIDER"))
|
||||
"sprites"))
|
||||
"e2b"))
|
||||
|
||||
(defn runtime-provider-kind [^js env runtime]
|
||||
(or (known-provider-kind (:provider runtime))
|
||||
@@ -133,6 +135,10 @@
|
||||
[]
|
||||
nil)
|
||||
|
||||
(defn clear-e2b-snapshot-cache!
|
||||
[]
|
||||
nil)
|
||||
|
||||
(defn- cloudflare-agent-port [^js env]
|
||||
(parse-int (env-str env "CLOUDFLARE_SANDBOX_AGENT_PORT") 2468))
|
||||
|
||||
@@ -553,6 +559,7 @@
|
||||
repo-dir (get-repo-dir session-id task provider)
|
||||
base-dir (repo-base-dir provider)
|
||||
override-key (case provider
|
||||
"e2b" "E2B_REPO_CLONE_COMMAND"
|
||||
"cloudflare" "CLOUDFLARE_REPO_CLONE_COMMAND"
|
||||
"vercel" "VERCEL_REPO_CLONE_COMMAND"
|
||||
"SPRITES_REPO_CLONE_COMMAND")
|
||||
@@ -924,6 +931,381 @@
|
||||
(string? access-client-id) (assoc "CF-Access-Client-Id" access-client-id)
|
||||
(string? access-client-secret) (assoc "CF-Access-Client-Secret" access-client-secret))))
|
||||
|
||||
(defn- e2b-api-key
|
||||
[^js env]
|
||||
(env-str env "E2B_API_KEY"))
|
||||
|
||||
(defn- e2b-domain
|
||||
[^js env]
|
||||
(env-str env "E2B_DOMAIN"))
|
||||
|
||||
(defn- e2b-template
|
||||
[^js env task runtime]
|
||||
(or (:template runtime)
|
||||
(some-> (get-in task [:runtime :template]) str string/trim not-empty)
|
||||
(env-str env "E2B_TEMPLATE")))
|
||||
|
||||
(defn- e2b-agent-token
|
||||
[^js env runtime]
|
||||
(or (:agent-token runtime)
|
||||
(env-str env "SANDBOX_AGENT_TOKEN")))
|
||||
|
||||
(defn- e2b-agent-port
|
||||
[^js env runtime]
|
||||
(or (:sandbox-port runtime)
|
||||
(parse-int (env-str env "E2B_SANDBOX_AGENT_PORT") 2468)))
|
||||
|
||||
(defn- e2b-health-retries
|
||||
[^js env]
|
||||
(parse-int (env-str env "E2B_HEALTH_RETRIES") 30))
|
||||
|
||||
(defn- e2b-health-interval-ms
|
||||
[^js env]
|
||||
(parse-int (env-str env "E2B_HEALTH_INTERVAL_MS") 300))
|
||||
|
||||
(defn- e2b-sandbox-timeout-ms
|
||||
[^js env]
|
||||
(parse-int (env-str env "E2B_SANDBOX_TIMEOUT_MS") (* 30 60 1000)))
|
||||
|
||||
(defn- e2b-api-opts
|
||||
[^js env]
|
||||
(let [api-key (e2b-api-key env)
|
||||
domain (e2b-domain env)]
|
||||
(when-not (string? api-key)
|
||||
(throw (ex-info "missing E2B_API_KEY for e2b runtime provider"
|
||||
{:reason :missing-e2b-api-key})))
|
||||
(cond-> {:apiKey api-key}
|
||||
(string? domain) (assoc :domain domain))))
|
||||
|
||||
(defn e2b-sandbox-class
|
||||
[]
|
||||
(let [sandbox-class (aget e2b "Sandbox")]
|
||||
(when-not sandbox-class
|
||||
(throw (ex-info "missing e2b Sandbox export"
|
||||
{:reason :missing-e2b-sdk})))
|
||||
sandbox-class))
|
||||
|
||||
(defn- e2b-sandbox-id
|
||||
[sandbox]
|
||||
(or (aget sandbox "sandboxId")
|
||||
(aget sandbox "id")))
|
||||
|
||||
(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)))
|
||||
|
||||
(defn <e2b-create-sandbox!
|
||||
[^js env template opts]
|
||||
(let [sandbox-class (e2b-sandbox-class)
|
||||
create (js-method sandbox-class "create")
|
||||
params (clj->js (merge (e2b-api-opts env) opts))]
|
||||
(when-not (fn? create)
|
||||
(throw (ex-info "e2b sdk missing Sandbox.create"
|
||||
{:reason :missing-e2b-create})))
|
||||
(if (string? template)
|
||||
(->promise (.call create sandbox-class template params))
|
||||
(->promise (.call create sandbox-class params)))))
|
||||
|
||||
(defn <e2b-connect-sandbox!
|
||||
[^js env sandbox-id]
|
||||
(let [sandbox-class (e2b-sandbox-class)
|
||||
connect (js-method sandbox-class "connect")
|
||||
opts (clj->js (e2b-api-opts env))]
|
||||
(when-not (string? sandbox-id)
|
||||
(throw (ex-info "missing sandbox-id on runtime"
|
||||
{:reason :missing-sandbox-id})))
|
||||
(when-not (fn? connect)
|
||||
(throw (ex-info "e2b sdk missing Sandbox.connect"
|
||||
{:reason :missing-e2b-connect})))
|
||||
(->promise (.call connect sandbox-class sandbox-id opts))))
|
||||
|
||||
(defn <e2b-kill-sandbox!
|
||||
[^js env sandbox-id]
|
||||
(let [sandbox-class (e2b-sandbox-class)
|
||||
kill (js-method sandbox-class "kill")
|
||||
opts (clj->js (e2b-api-opts env))]
|
||||
(if (and (string? sandbox-id) (fn? kill))
|
||||
(->promise (.call kill sandbox-class sandbox-id opts))
|
||||
(p/resolved nil))))
|
||||
|
||||
(defn <e2b-run-shell!
|
||||
[sandbox command & [opts]]
|
||||
(let [commands (when sandbox (aget sandbox "commands"))
|
||||
run-command (js-method commands "run")
|
||||
params (cond-> {}
|
||||
(string? (:cwd opts)) (assoc :cwd (:cwd opts))
|
||||
(map? (:env opts)) (assoc :envs (:env opts))
|
||||
(number? (:timeout-ms opts)) (assoc :timeoutMs (:timeout-ms opts))
|
||||
(true? (:background opts)) (assoc :background true)
|
||||
(fn? (:on-stdout opts)) (assoc :onStdout (:on-stdout opts))
|
||||
(fn? (:on-stderr opts)) (assoc :onStderr (:on-stderr opts)))]
|
||||
(when-not (fn? run-command)
|
||||
(throw (ex-info "e2b sandbox missing commands.run"
|
||||
{: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)))
|
||||
stdout (or (aget result "stdout") "")
|
||||
stderr (or (aget result "stderr") "")
|
||||
exit-code (or (aget result "exitCode")
|
||||
(aget result "exit_code"))]
|
||||
(when (and (number? exit-code) (not (zero? exit-code)))
|
||||
(throw (ex-info "e2b sandbox command failed"
|
||||
{:reason :e2b-command-failed
|
||||
:command command
|
||||
:exit-code exit-code
|
||||
:stdout stdout
|
||||
:stderr stderr})))
|
||||
{:stdout stdout
|
||||
:stderr stderr
|
||||
:exit-code exit-code}))))
|
||||
|
||||
(defn- e2b-health-command
|
||||
[port agent-token]
|
||||
(str "if curl -fsS "
|
||||
(curl-auth-arg agent-token)
|
||||
" "
|
||||
cloudflare-local-host
|
||||
":"
|
||||
port
|
||||
"/v1/health >/dev/null; then echo __HEALTH_OK__; else echo __HEALTH_FAIL__; fi"))
|
||||
|
||||
(defn- <e2b-health-once!
|
||||
[sandbox port agent-token]
|
||||
(-> (<e2b-run-shell! sandbox (e2b-health-command port agent-token))
|
||||
(p/then (fn [{:keys [stdout stderr]}]
|
||||
(let [output (str stdout "\n" stderr)]
|
||||
(string/includes? output "__HEALTH_OK__"))))
|
||||
(p/catch (fn [_] false))))
|
||||
|
||||
(defn- <e2b-health!
|
||||
[^js env sandbox port agent-token]
|
||||
(let [retries (e2b-health-retries env)
|
||||
interval-ms (e2b-health-interval-ms env)]
|
||||
(letfn [(step [left]
|
||||
(if (<= left 0)
|
||||
(throw (ex-info "sandbox-agent health check timed out in e2b sandbox"
|
||||
{:port port}))
|
||||
(p/let [healthy? (<e2b-health-once! sandbox port agent-token)]
|
||||
(if healthy?
|
||||
true
|
||||
(p/let [_ (p/delay interval-ms)]
|
||||
(step (dec left)))))))]
|
||||
(step retries))))
|
||||
|
||||
(declare normalize-agent-env-map cloudflare-env-pass-through)
|
||||
|
||||
(defn- <e2b-agent-env-vars!
|
||||
[^js env task]
|
||||
(let [base (reduce (fn [acc k]
|
||||
(if-let [v (env-str env k)]
|
||||
(assoc acc k v)
|
||||
acc))
|
||||
{}
|
||||
cloudflare-env-pass-through)
|
||||
task-env (normalize-agent-env-map (get-in task [:agent :env]))
|
||||
agent-id (:agent (session-payload task))
|
||||
api-token (some-> (get-in task [:agent :api-token]) str string/trim not-empty)]
|
||||
(p/let [github-token (<github-installation-token-for-task! env task)]
|
||||
(cond-> (merge base task-env)
|
||||
(and (string? api-token) (= "codex" agent-id)) (assoc "OPENAI_API_KEY" api-token)
|
||||
(and (string? api-token) (= "claude" agent-id)) (assoc "ANTHROPIC_API_KEY" api-token)
|
||||
(string? github-token) (assoc-github-installation-token github-token)))))
|
||||
|
||||
(defn- e2b-create-opts
|
||||
[^js env session-id env-vars]
|
||||
(let [timeout-ms (e2b-sandbox-timeout-ms env)
|
||||
metadata (cond-> {"session-id" (or session-id "")
|
||||
"runtime-provider" "e2b"}
|
||||
(string? session-id) (assoc "runtime-session-id" session-id))]
|
||||
(cond-> {:timeoutMs timeout-ms
|
||||
:lifecycle {:onTimeout "pause"
|
||||
:autoResume true}}
|
||||
(seq env-vars) (assoc :envs env-vars)
|
||||
(string? session-id) (assoc :metadata metadata))))
|
||||
|
||||
(defn- e2b-server-command
|
||||
[^js env task session-id port agent-token env-vars]
|
||||
(let [auth-json (get-in task [:agent :auth-json])
|
||||
write-auth (or (auth-json-write-command auth-json) "")
|
||||
repo-cd (or (repo-cd-command session-id task "e2b") "")
|
||||
token-exports (shell-export-command env-vars)
|
||||
github-auth-setup (github-auth-setup-command (get env-vars "GITHUB_TOKEN"))]
|
||||
(str (sandbox-agent-install-command env)
|
||||
token-exports
|
||||
github-auth-setup
|
||||
write-auth
|
||||
(when (string? repo-cd) (str repo-cd "; "))
|
||||
"nohup sandbox-agent server "
|
||||
(if (string? agent-token)
|
||||
(str "--token '" (escape-shell-single agent-token) "'")
|
||||
"--no-token")
|
||||
" --host 0.0.0.0 --port " port
|
||||
" --no-telemetry >/tmp/sandbox-agent.log 2>&1 &")))
|
||||
|
||||
(defn- <e2b-clone-repo!
|
||||
[^js env sandbox session-id task]
|
||||
(when-let [cmd (repo-clone-command env session-id task "e2b")]
|
||||
(<e2b-run-shell! sandbox cmd)))
|
||||
|
||||
(defn- <e2b-run-project-init-setup!
|
||||
[sandbox session-id task]
|
||||
(when-let [cmd (project-init-setup-command session-id task "e2b")]
|
||||
(<e2b-run-shell! sandbox cmd)))
|
||||
|
||||
(defn- <e2b-ensure-running!
|
||||
[^js env sandbox session-id task port agent-token env-vars]
|
||||
(p/let [healthy? (<e2b-health-once! sandbox port agent-token)]
|
||||
(if healthy?
|
||||
true
|
||||
(p/let [bootstrap-cmd (e2b-server-command env task session-id port agent-token env-vars)
|
||||
_ (<e2b-run-shell! sandbox bootstrap-cmd)
|
||||
_ (<e2b-health! env sandbox port agent-token)]
|
||||
true))))
|
||||
|
||||
(defn <e2b-create-snapshot!
|
||||
[sandbox]
|
||||
(let [create-snapshot (js-method sandbox "createSnapshot")]
|
||||
(when-not (fn? create-snapshot)
|
||||
(throw (ex-info "e2b runtime does not support snapshots"
|
||||
{:reason :unsupported-snapshot
|
||||
:provider "e2b"})))
|
||||
(p/let [snapshot (->promise (.call create-snapshot sandbox))
|
||||
snapshot-id (or (aget snapshot "snapshotId")
|
||||
(aget snapshot "snapshotID"))]
|
||||
(if (string? snapshot-id)
|
||||
{:snapshot-id snapshot-id}
|
||||
(throw (ex-info "e2b snapshot create returned invalid id"
|
||||
{:reason :invalid-snapshot-id
|
||||
:snapshot snapshot}))))))
|
||||
|
||||
(defn- <e2b-create-sandbox-from-checkpoint!
|
||||
[^js env checkpoint create-opts]
|
||||
(let [snapshot-id (:snapshot-id checkpoint)]
|
||||
(if-not (string? snapshot-id)
|
||||
(p/resolved nil)
|
||||
(-> (<e2b-create-sandbox! env snapshot-id create-opts)
|
||||
(p/then (fn [sandbox]
|
||||
(log/debug :agent/e2b-snapshot-restored
|
||||
{:snapshot-id snapshot-id
|
||||
:source "task-checkpoint"})
|
||||
{:sandbox sandbox
|
||||
:snapshot-id snapshot-id
|
||||
:restored? true}))
|
||||
(p/catch (fn [error]
|
||||
(log/error :agent/e2b-snapshot-restore-failed
|
||||
{:snapshot-id snapshot-id
|
||||
:source "task-checkpoint"
|
||||
:error (str error)})
|
||||
nil))))))
|
||||
|
||||
(defn- <e2b-create-sandbox-for-restore!
|
||||
[^js env template checkpoint create-opts]
|
||||
(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}))))
|
||||
|
||||
(defn- <e2b-runtime-base-url!
|
||||
[^js env runtime]
|
||||
(let [cached (:base-url runtime)
|
||||
port (e2b-agent-port env runtime)
|
||||
sandbox-id (:sandbox-id runtime)]
|
||||
(if (string? cached)
|
||||
(p/resolved cached)
|
||||
(p/let [sandbox (<e2b-connect-sandbox! env sandbox-id)]
|
||||
(e2b-sandbox-host sandbox port)))))
|
||||
|
||||
(defn- <e2b-export-workspace-bundle!
|
||||
[^js env runtime opts]
|
||||
(let [sandbox-id (:sandbox-id runtime)
|
||||
task (:task opts)
|
||||
session-id (:session-id runtime)
|
||||
repo-dir (or (:backup-dir runtime)
|
||||
(get-repo-dir session-id task "e2b"))
|
||||
base-branch (or (some-> (get-in task [:project :base-branch]) source-control/sanitize-branch-name)
|
||||
"main")
|
||||
head-branch (some-> (:head-branch opts) source-control/sanitize-branch-name)]
|
||||
(when-not (string? sandbox-id)
|
||||
(throw (ex-info "missing sandbox-id on runtime"
|
||||
{:reason :missing-sandbox-id
|
||||
:runtime runtime})))
|
||||
(when-not (string? repo-dir)
|
||||
(throw (ex-info "missing repo dir for workspace bundle export"
|
||||
{:reason :missing-repo-dir
|
||||
:session-id session-id})))
|
||||
(p/let [sandbox (<e2b-connect-sandbox! env sandbox-id)
|
||||
result (<e2b-run-shell! sandbox (export-workspace-bundle-command repo-dir base-branch head-branch))]
|
||||
(or (parse-exported-workspace-bundle (:stdout result))
|
||||
(throw (ex-info "invalid workspace bundle export output"
|
||||
{:reason :invalid-bundle-export-output
|
||||
:provider "e2b"}))))))
|
||||
|
||||
(defn- <e2b-apply-workspace-bundle!
|
||||
[^js env runtime opts]
|
||||
(let [sandbox-id (:sandbox-id runtime)
|
||||
task (:task opts)
|
||||
session-id (:session-id runtime)
|
||||
repo-dir (or (:backup-dir runtime)
|
||||
(get-repo-dir session-id task "e2b"))
|
||||
head-sha (some-> (:head-sha opts) str string/trim not-empty)
|
||||
bundle-base64 (some-> (:bundle-base64 opts) str string/trim not-empty)
|
||||
head-branch (some-> (:head-branch opts) source-control/sanitize-branch-name)]
|
||||
(when-not (string? sandbox-id)
|
||||
(throw (ex-info "missing sandbox-id on runtime"
|
||||
{:reason :missing-sandbox-id
|
||||
:runtime runtime})))
|
||||
(when-not (string? repo-dir)
|
||||
(throw (ex-info "missing repo dir for workspace bundle apply"
|
||||
{:reason :missing-repo-dir
|
||||
:session-id session-id})))
|
||||
(when-not (string? head-sha)
|
||||
(throw (ex-info "missing bundle head sha"
|
||||
{:reason :missing-bundle-head-sha
|
||||
:provider "e2b"})))
|
||||
(when-not (string? bundle-base64)
|
||||
(throw (ex-info "missing bundle payload"
|
||||
{:reason :missing-bundle-payload
|
||||
:provider "e2b"})))
|
||||
(p/let [sandbox (<e2b-connect-sandbox! env sandbox-id)
|
||||
command (apply-workspace-bundle-command repo-dir
|
||||
{:head-sha head-sha
|
||||
:head-branch head-branch
|
||||
:bundle-base64 bundle-base64})
|
||||
_ (<e2b-run-shell! sandbox command)]
|
||||
true)))
|
||||
|
||||
(defn- <e2b-open-terminal!
|
||||
[^js env runtime request]
|
||||
(let [session-id (:session-id 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})))))))
|
||||
|
||||
(defn- vercel-agent-token [^js env runtime]
|
||||
(or (:agent-token runtime)
|
||||
(env-str env "SANDBOX_AGENT_TOKEN")))
|
||||
@@ -2139,6 +2521,154 @@
|
||||
{:headers headers})
|
||||
(fn [_] nil)))))))
|
||||
|
||||
(defrecord E2BProvider [env]
|
||||
RuntimeProvider
|
||||
|
||||
(<provision-runtime! [_ session-id task]
|
||||
(let [agent-token (e2b-agent-token env nil)
|
||||
port (e2b-agent-port env nil)
|
||||
payload (session-payload task)
|
||||
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)]
|
||||
(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)
|
||||
_ (<e2b-ensure-running! env sandbox session-id task port agent-token env-vars)
|
||||
_ (when-not restored?
|
||||
(p/catch
|
||||
(<e2b-clone-repo! env sandbox session-id task)
|
||||
(fn [error]
|
||||
(log/error :agent/e2b-repo-clone-failed
|
||||
{:session-id session-id
|
||||
:error (str 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)})
|
||||
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)]
|
||||
(when-not (string? sandbox-id)
|
||||
(throw (ex-info "e2b sandbox missing sandboxId"
|
||||
{:reason :missing-sandbox-id})))
|
||||
{:provider "e2b"
|
||||
:sandbox-id sandbox-id
|
||||
:sandbox-name sandbox-id
|
||||
:sandbox-port port
|
||||
:base-url base-url
|
||||
:agent-token agent-token
|
||||
:session-id (:session-id response)
|
||||
:backup-key backup-key
|
||||
:backup-dir repo-dir
|
||||
:template template
|
||||
:snapshot-id snapshot-id})))
|
||||
|
||||
(<open-events-stream! [_ runtime]
|
||||
(let [agent-token (e2b-agent-token env runtime)]
|
||||
(p/let [base-url (<e2b-runtime-base-url! env runtime)]
|
||||
(sandbox/<open-events-stream base-url agent-token (:session-id runtime)))))
|
||||
|
||||
(<send-message! [_ runtime message]
|
||||
(let [agent-token (e2b-agent-token env runtime)]
|
||||
(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))
|
||||
|
||||
(<snapshot-runtime! [_ runtime opts]
|
||||
(let [session-id (:session-id runtime)
|
||||
sandbox-id (:sandbox-id runtime)
|
||||
backup-dir (or (:backup-dir runtime)
|
||||
(get-repo-dir session-id (:task opts) "e2b"))
|
||||
task (:task opts)
|
||||
backup-key (repo-backup-key task)]
|
||||
(when-not (string? sandbox-id)
|
||||
(throw (ex-info "missing sandbox-id on runtime"
|
||||
{:reason :missing-sandbox-id
|
||||
:runtime runtime})))
|
||||
(p/let [sandbox (<e2b-connect-sandbox! env sandbox-id)
|
||||
result (<e2b-create-snapshot! sandbox)]
|
||||
(cond-> result
|
||||
(string? backup-key) (assoc :backup-key backup-key)
|
||||
(string? backup-dir) (assoc :backup-dir backup-dir)
|
||||
:always (assoc :provider "e2b")))))
|
||||
|
||||
(<export-workspace-bundle! [_ runtime opts]
|
||||
(<e2b-export-workspace-bundle! env runtime opts))
|
||||
|
||||
(<apply-workspace-bundle! [_ runtime opts]
|
||||
(<e2b-apply-workspace-bundle! env runtime opts))
|
||||
|
||||
(<push-branch! [_ runtime opts]
|
||||
(let [sandbox-id (:sandbox-id runtime)
|
||||
session-id (:session-id opts)
|
||||
repo-url (:repo-url opts)
|
||||
head-branch (:head-branch opts)
|
||||
force? (true? (:force opts))
|
||||
repo-dir (or (:backup-dir runtime)
|
||||
(get-repo-dir session-id (:task opts) "e2b"))
|
||||
remote-url (or (source-control/push-remote-url repo-url (:push-token opts))
|
||||
repo-url)]
|
||||
(when-not (string? sandbox-id)
|
||||
(throw (ex-info "missing sandbox-id on runtime" {:runtime runtime})))
|
||||
(when-not (string? repo-dir)
|
||||
(throw (ex-info "missing repo dir for push"
|
||||
{:reason :missing-repo-dir
|
||||
:session-id session-id})))
|
||||
(when-not (string? remote-url)
|
||||
(throw (ex-info "missing remote url for push"
|
||||
{:reason :missing-remote-url
|
||||
:repo-url repo-url})))
|
||||
(when-not (string? head-branch)
|
||||
(throw (ex-info "missing head branch for push"
|
||||
{:reason :missing-branch})))
|
||||
(let [script (push-command {:repo-dir repo-dir
|
||||
:remote-url remote-url
|
||||
:head-branch head-branch
|
||||
:commit-message (:commit-message opts)
|
||||
:force force?})
|
||||
env-vars (assoc-github-installation-token {}
|
||||
(some-> (:push-token opts) str string/trim not-empty))
|
||||
run-opts (when (seq env-vars)
|
||||
{:env env-vars})]
|
||||
(-> (p/let [sandbox (<e2b-connect-sandbox! env sandbox-id)
|
||||
_ (<e2b-run-shell! sandbox script run-opts)]
|
||||
{:head-branch head-branch
|
||||
:repo-url repo-url
|
||||
:force force?
|
||||
:remote "origin"})
|
||||
(p/catch (fn [error]
|
||||
(p/rejected
|
||||
(if-let [data (ex-data error)]
|
||||
(ex-info (ex-message error)
|
||||
(assoc data
|
||||
:provider "e2b"
|
||||
:reason (or (:reason data)
|
||||
(classify-push-error error))))
|
||||
(ex-info "git push failed"
|
||||
{:provider "e2b"
|
||||
:reason (classify-push-error error)
|
||||
:error (str error)})))))))))
|
||||
|
||||
(<terminate-runtime! [_ runtime]
|
||||
(let [sandbox-id (:sandbox-id runtime)]
|
||||
(if-not (string? sandbox-id)
|
||||
(p/resolved nil)
|
||||
(p/catch
|
||||
(<e2b-kill-sandbox! env sandbox-id)
|
||||
(fn [_] nil))))))
|
||||
|
||||
(defrecord VercelProvider [env]
|
||||
RuntimeProvider
|
||||
|
||||
@@ -2470,6 +3000,7 @@
|
||||
|
||||
(defn provider-id [provider]
|
||||
(cond
|
||||
(instance? E2BProvider provider) "e2b"
|
||||
(instance? SpritesProvider provider) "sprites"
|
||||
(instance? LocalDevProvider provider) "local-dev"
|
||||
(instance? LocalRunnerProvider provider) "local-runner"
|
||||
@@ -2482,11 +3013,12 @@
|
||||
{:requested kind
|
||||
:resolved (known-provider-kind kind)})
|
||||
(case (known-provider-kind kind)
|
||||
"e2b" (->E2BProvider env)
|
||||
"local-dev" (->LocalDevProvider env)
|
||||
"local-runner" (->LocalRunnerProvider env)
|
||||
"vercel" (->VercelProvider env)
|
||||
"cloudflare" (->CloudflareProvider env)
|
||||
(->SpritesProvider env)))
|
||||
(->E2BProvider env)))
|
||||
|
||||
(defn resolve-provider
|
||||
[^js env runtime]
|
||||
@@ -2495,4 +3027,5 @@
|
||||
(defn runtime-terminal-supported?
|
||||
[runtime]
|
||||
(let [provider (known-provider-kind (:provider runtime))]
|
||||
(= "cloudflare" provider)))
|
||||
(or (= "cloudflare" provider)
|
||||
(= "e2b" provider))))
|
||||
|
||||
11
deps/workers/src/logseq/sync/node/config.cljs
vendored
11
deps/workers/src/logseq/sync/node/config.cljs
vendored
@@ -24,6 +24,14 @@
|
||||
:agent-runtime-provider (env-value env "AGENT_RUNTIME_PROVIDER")
|
||||
:sandbox-agent-url (env-value env "SANDBOX_AGENT_URL")
|
||||
:sandbox-agent-token (env-value env "SANDBOX_AGENT_TOKEN")
|
||||
:e2b-api-key (env-value env "E2B_API_KEY")
|
||||
:e2b-domain (env-value env "E2B_DOMAIN")
|
||||
:e2b-template (env-value env "E2B_TEMPLATE")
|
||||
:e2b-repo-clone-command (env-value env "E2B_REPO_CLONE_COMMAND")
|
||||
:e2b-sandbox-agent-port (env-value env "E2B_SANDBOX_AGENT_PORT")
|
||||
:e2b-sandbox-timeout-ms (env-value env "E2B_SANDBOX_TIMEOUT_MS")
|
||||
:e2b-health-retries (env-value env "E2B_HEALTH_RETRIES")
|
||||
:e2b-health-interval-ms (env-value env "E2B_HEALTH_INTERVAL_MS")
|
||||
:sprite-token (env-value env "SPRITE_TOKEN")
|
||||
:sprites-token (env-value env "SPRITES_TOKEN")
|
||||
:sprites-api-url (env-value env "SPRITES_API_URL")
|
||||
@@ -72,6 +80,9 @@
|
||||
[:port :base-url :data-dir :storage-driver :assets-driver
|
||||
:auth-driver :auth-token :static-user-id :static-email :static-username
|
||||
:agent-runtime-provider :sandbox-agent-url :sandbox-agent-token
|
||||
:e2b-api-key :e2b-domain :e2b-template :e2b-repo-clone-command
|
||||
:e2b-sandbox-agent-port :e2b-sandbox-timeout-ms
|
||||
:e2b-health-retries :e2b-health-interval-ms
|
||||
:sprite-token :sprites-token :sprites-api-url :sprites-timeout-ms
|
||||
:sprites-name-prefix :sprites-ram-mb :sprites-cpus :sprites-region
|
||||
:sprites-storage-gb :sprites-bootstrap-command :sprites-repo-clone-command
|
||||
|
||||
@@ -33,6 +33,14 @@
|
||||
(aset "AGENT_RUNTIME_PROVIDER" (:agent-runtime-provider cfg))
|
||||
(aset "SANDBOX_AGENT_URL" (:sandbox-agent-url cfg))
|
||||
(aset "SANDBOX_AGENT_TOKEN" (:sandbox-agent-token cfg))
|
||||
(aset "E2B_API_KEY" (:e2b-api-key cfg))
|
||||
(aset "E2B_DOMAIN" (:e2b-domain cfg))
|
||||
(aset "E2B_TEMPLATE" (:e2b-template cfg))
|
||||
(aset "E2B_REPO_CLONE_COMMAND" (:e2b-repo-clone-command cfg))
|
||||
(aset "E2B_SANDBOX_AGENT_PORT" (:e2b-sandbox-agent-port cfg))
|
||||
(aset "E2B_SANDBOX_TIMEOUT_MS" (:e2b-sandbox-timeout-ms cfg))
|
||||
(aset "E2B_HEALTH_RETRIES" (:e2b-health-retries cfg))
|
||||
(aset "E2B_HEALTH_INTERVAL_MS" (:e2b-health-interval-ms cfg))
|
||||
(aset "SPRITE_TOKEN" (:sprite-token cfg))
|
||||
(aset "SPRITES_TOKEN" (:sprites-token cfg))
|
||||
(aset "SPRITES_API_URL" (:sprites-api-url cfg))
|
||||
|
||||
193
deps/workers/test/logseq/agents/e2b_runtime_provider_test.cljs
vendored
Normal file
193
deps/workers/test/logseq/agents/e2b_runtime_provider_test.cljs
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
(ns logseq.agents.e2b-runtime-provider-test
|
||||
(:require [cljs.test :refer [async deftest is testing]]
|
||||
[clojure.string :as string]
|
||||
[logseq.agents.runtime-provider :as runtime-provider]))
|
||||
|
||||
(deftest e2b-default-provider-kind-test
|
||||
(testing "e2b is the default and supports explicit normalization"
|
||||
(is (= "e2b" (runtime-provider/provider-kind #js {})))
|
||||
(is (= "e2b" (runtime-provider/provider-kind #js {"AGENT_RUNTIME_PROVIDER" "E2B"})))
|
||||
(is (= "e2b" (runtime-provider/provider-kind #js {"AGENT_RUNTIME_PROVIDER" "unknown"})))))
|
||||
|
||||
(deftest e2b-provider-dispatch-test
|
||||
(testing "create-provider and resolve-provider dispatch e2b"
|
||||
(let [env #js {"AGENT_RUNTIME_PROVIDER" "e2b"}]
|
||||
(is (= "e2b"
|
||||
(runtime-provider/provider-id (runtime-provider/create-provider env "e2b"))))
|
||||
(is (= "e2b"
|
||||
(runtime-provider/provider-id (runtime-provider/create-provider env "unknown"))))
|
||||
(is (= "e2b"
|
||||
(runtime-provider/provider-id (runtime-provider/resolve-provider env {:provider "e2b"}))))
|
||||
(is (= "e2b"
|
||||
(runtime-provider/provider-id (runtime-provider/resolve-provider env nil)))))))
|
||||
|
||||
(deftest e2b-terminal-supported-test
|
||||
(testing "e2b runtime supports browser terminal"
|
||||
(is (true? (runtime-provider/runtime-terminal-supported? {:provider "e2b"})))))
|
||||
|
||||
(deftest e2b-provider-provision-test
|
||||
(async done
|
||||
(let [calls (atom [])
|
||||
env #js {"E2B_API_KEY" "e2b-key"
|
||||
"SANDBOX_AGENT_TOKEN" "agent-token"}
|
||||
provider (runtime-provider/create-provider env "e2b")
|
||||
task {:agent {:provider "codex"}}
|
||||
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]
|
||||
(let [opts (js->clj (last args) :keywordize-keys true)]
|
||||
(is (= "e2b-key" (:apiKey opts)))
|
||||
(is (= "pause" (get-in opts [:lifecycle :onTimeout])))
|
||||
(js/Promise.resolve
|
||||
#js {:sandboxId "e2b-sbx-1"
|
||||
:getHost (fn [port]
|
||||
(swap! calls conj {:type :host :port port})
|
||||
"https://e2b-agent.local")
|
||||
:commands
|
||||
#js {:run (fn [cmd _opts]
|
||||
(swap! calls conj {:type :command :cmd cmd})
|
||||
(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 (= "POST" (.-method request)))
|
||||
(is (= "https://e2b-agent.local/v1/sessions/sess-e2b-1" (.-url request)))
|
||||
(is (= "Bearer agent-token"
|
||||
(.get (.-headers request) "authorization")))
|
||||
(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-1" task)
|
||||
(.then (fn [runtime]
|
||||
(restore!)
|
||||
(is (= "e2b" (:provider runtime)))
|
||||
(is (= "e2b-sbx-1" (:sandbox-id runtime)))
|
||||
(is (= "https://e2b-agent.local" (:base-url runtime)))
|
||||
(is (= "sess-e2b-1" (:session-id runtime)))
|
||||
(is (= 2468 (:sandbox-port runtime)))
|
||||
(is (some #(= {:type :host :port 2468} %) @calls))
|
||||
(done)))
|
||||
(.catch (fn [error]
|
||||
(restore!)
|
||||
(is false (str "unexpected error: " error))
|
||||
(done)))))))
|
||||
|
||||
(deftest e2b-provider-open-terminal-test
|
||||
(async done
|
||||
(let [env #js {"E2B_API_KEY" "e2b-key"
|
||||
"SANDBOX_AGENT_TOKEN" "agent-token"}
|
||||
provider (runtime-provider/create-provider env "e2b")
|
||||
runtime {:provider "e2b"
|
||||
:sandbox-id "e2b-sbx-1"
|
||||
:base-url "https://e2b-agent.local"
|
||||
:session-id "sess-term-1"}
|
||||
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}))))
|
||||
(-> (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)))
|
||||
(done)))
|
||||
(.catch (fn [error]
|
||||
(set! js/fetch original-fetch)
|
||||
(is false (str "unexpected error: " error))
|
||||
(done)))))))
|
||||
|
||||
(deftest e2b-provider-snapshot-runtime-test
|
||||
(async done
|
||||
(let [env #js {"E2B_API_KEY" "e2b-key"}
|
||||
provider (runtime-provider/create-provider env "e2b")
|
||||
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"
|
||||
:base-branch "main"}}
|
||||
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-1"
|
||||
:createSnapshot (fn []
|
||||
(js/Promise.resolve #js {:snapshotId "e2b-snap-1"}))})))
|
||||
(-> (runtime-provider/<snapshot-runtime! provider runtime {:task task})
|
||||
(.then (fn [result]
|
||||
(restore!)
|
||||
(is (= "e2b" (:provider result)))
|
||||
(is (= "e2b-snap-1" (:snapshot-id result)))
|
||||
(is (= "/workspace/sess-e2b-snapshot" (:backup-dir result)))
|
||||
(done)))
|
||||
(.catch (fn [error]
|
||||
(restore!)
|
||||
(is false (str "unexpected error: " error))
|
||||
(done)))))))
|
||||
|
||||
(deftest e2b-provider-export-workspace-bundle-test
|
||||
(async done
|
||||
(let [env #js {"E2B_API_KEY" "e2b-key"}
|
||||
provider (runtime-provider/create-provider env "e2b")
|
||||
runtime {:provider "e2b"
|
||||
:sandbox-id "e2b-sbx-1"
|
||||
:backup-dir "/workspace/sess-e2b-bundle"
|
||||
:session-id "sess-e2b-bundle"}
|
||||
task {:project {:base-branch "main"}}
|
||||
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-1"
|
||||
:commands
|
||||
#js {:run (fn [_cmd _opts]
|
||||
(js/Promise.resolve
|
||||
#js {:stdout (str "__BUNDLE_HEAD__:abc123\n"
|
||||
"__BUNDLE_BASE__:base123\n"
|
||||
"__BUNDLE_BYTES__:16\n"
|
||||
"__BUNDLE_SHA256__:sha256-123\n"
|
||||
"__BUNDLE_BRANCH__:feat/m24\n"
|
||||
"__BUNDLE_DATA__:ZmFrZS1idW5kbGUtZGF0YQ==\n")
|
||||
:stderr ""
|
||||
:exitCode 0}))}})))
|
||||
(-> (runtime-provider/<export-workspace-bundle! provider runtime {:task task})
|
||||
(.then (fn [result]
|
||||
(restore!)
|
||||
(is (= "abc123" (:head-sha result)))
|
||||
(is (= "base123" (:base-sha result)))
|
||||
(is (= 16 (:byte-size result)))
|
||||
(is (= "sha256-123" (:checksum result)))
|
||||
(is (= "feat/m24" (:head-branch result)))
|
||||
(is (= "ZmFrZS1idW5kbGUtZGF0YQ==" (:bundle-base64 result)))
|
||||
(done)))
|
||||
(.catch (fn [error]
|
||||
(restore!)
|
||||
(is false (str "unexpected error: " error))
|
||||
(done)))))))
|
||||
21
deps/workers/test/logseq/agents/e2b_runtime_provider_test_runner.cljs
vendored
Normal file
21
deps/workers/test/logseq/agents/e2b_runtime_provider_test_runner.cljs
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
(ns logseq.agents.e2b-runtime-provider-test-runner
|
||||
(:require [cljs.test :as ct]
|
||||
[logseq.agents.e2b-runtime-provider-test]
|
||||
[shadow.test :as st]
|
||||
[shadow.test.env :as env]))
|
||||
|
||||
(derive ::node ::ct/default)
|
||||
|
||||
(defmethod ct/report [::node :end-run-tests] [m]
|
||||
(if (ct/successful? m)
|
||||
(js/process.exit 0)
|
||||
(js/process.exit 1)))
|
||||
|
||||
(defn ^:dev/after-load reset-test-data! []
|
||||
(when-let [test-data (env/get-test-data)]
|
||||
(env/reset-test-data! test-data)))
|
||||
|
||||
(defn main [& _args]
|
||||
(reset-test-data!)
|
||||
(let [test-env (ct/empty-env ::node)]
|
||||
(st/run-all-tests test-env nil)))
|
||||
@@ -18,13 +18,14 @@
|
||||
|
||||
(deftest provider-kind-test
|
||||
(testing "normalizes configured runtime provider"
|
||||
(is (= "sprites" (runtime-provider/provider-kind #js {})))
|
||||
(is (= "e2b" (runtime-provider/provider-kind #js {})))
|
||||
(is (= "e2b" (runtime-provider/provider-kind #js {"AGENT_RUNTIME_PROVIDER" "E2B"})))
|
||||
(is (= "sprites" (runtime-provider/provider-kind #js {"AGENT_RUNTIME_PROVIDER" "SPRITES"})))
|
||||
(is (= "local-dev" (runtime-provider/provider-kind #js {"AGENT_RUNTIME_PROVIDER" "LOCAL-DEV"})))
|
||||
(is (= "local-runner" (runtime-provider/provider-kind #js {"AGENT_RUNTIME_PROVIDER" "LOCAL-RUNNER"})))
|
||||
(is (= "vercel" (runtime-provider/provider-kind #js {"AGENT_RUNTIME_PROVIDER" "VERCEL"})))
|
||||
(is (= "cloudflare" (runtime-provider/provider-kind #js {"AGENT_RUNTIME_PROVIDER" "CLOUDFLARE"})))
|
||||
(is (= "sprites" (runtime-provider/provider-kind #js {"AGENT_RUNTIME_PROVIDER" "unknown"})))))
|
||||
(is (= "e2b" (runtime-provider/provider-kind #js {"AGENT_RUNTIME_PROVIDER" "unknown"})))))
|
||||
|
||||
(deftest fill-template-test
|
||||
(testing "fills sandbox id placeholders"
|
||||
@@ -38,6 +39,8 @@
|
||||
(let [env #js {"AGENT_RUNTIME_PROVIDER" "cloudflare"}]
|
||||
(is (= "local-dev"
|
||||
(runtime-provider/runtime-provider-kind env {:provider "local-dev"})))
|
||||
(is (= "e2b"
|
||||
(runtime-provider/runtime-provider-kind env {:provider "e2b"})))
|
||||
(is (= "sprites"
|
||||
(runtime-provider/runtime-provider-kind env {:provider "sprites"})))
|
||||
(is (= "vercel"
|
||||
@@ -49,7 +52,9 @@
|
||||
|
||||
(deftest provider-dispatch-test
|
||||
(testing "create-provider and resolve-provider dispatch supported providers"
|
||||
(let [env #js {"AGENT_RUNTIME_PROVIDER" "cloudflare"}]
|
||||
(let [env #js {"AGENT_RUNTIME_PROVIDER" "e2b"}]
|
||||
(is (= "e2b"
|
||||
(runtime-provider/provider-id (runtime-provider/create-provider env "e2b"))))
|
||||
(is (= "sprites"
|
||||
(runtime-provider/provider-id (runtime-provider/create-provider env "sprites"))))
|
||||
(is (= "local-dev"
|
||||
@@ -60,15 +65,24 @@
|
||||
(runtime-provider/provider-id (runtime-provider/create-provider env "vercel"))))
|
||||
(is (= "cloudflare"
|
||||
(runtime-provider/provider-id (runtime-provider/create-provider env "cloudflare"))))
|
||||
(is (= "sprites"
|
||||
(is (= "e2b"
|
||||
(runtime-provider/provider-id (runtime-provider/create-provider env "unknown"))))
|
||||
(is (= "e2b"
|
||||
(runtime-provider/provider-id (runtime-provider/resolve-provider env {:provider "e2b"}))))
|
||||
(is (= "local-dev"
|
||||
(runtime-provider/provider-id (runtime-provider/resolve-provider env {:provider "local-dev"}))))
|
||||
(is (= "local-runner"
|
||||
(runtime-provider/provider-id (runtime-provider/resolve-provider env {:provider "local-runner"}))))
|
||||
(is (= "cloudflare"
|
||||
(is (= "e2b"
|
||||
(runtime-provider/provider-id (runtime-provider/resolve-provider env nil)))))))
|
||||
|
||||
(deftest runtime-terminal-supported-test
|
||||
(testing "cloudflare and e2b runtimes support browser terminal"
|
||||
(is (true? (runtime-provider/runtime-terminal-supported? {:provider "cloudflare"})))
|
||||
(is (true? (runtime-provider/runtime-terminal-supported? {:provider "e2b"})))
|
||||
(is (false? (runtime-provider/runtime-terminal-supported? {:provider "vercel"})))
|
||||
(is (false? (runtime-provider/runtime-terminal-supported? {:provider "sprites"})))))
|
||||
|
||||
(deftest local-runner-provider-provision-test
|
||||
(async done
|
||||
(let [env #js {}
|
||||
|
||||
6
deps/workers/worker/wrangler.agents.toml
vendored
6
deps/workers/worker/wrangler.agents.toml
vendored
@@ -48,7 +48,7 @@ new_sqlite_classes = [ "Sandbox" ]
|
||||
COGNITO_JWKS_URL = "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_dtagLnju8/.well-known/jwks.json"
|
||||
COGNITO_ISSUER = "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_dtagLnju8"
|
||||
COGNITO_CLIENT_ID = "69cs1lgme7p8kbgld8n5kseii6"
|
||||
AGENT_RUNTIME_PROVIDER = "vercel"
|
||||
AGENT_RUNTIME_PROVIDER = "e2b"
|
||||
CLOUDFLARE_ACCOUNT_ID = "2553ea8236c11ea0f88de28fce1cbfee"
|
||||
BACKUP_BUCKET_NAME = "logseq-sync-assets-prod"
|
||||
|
||||
@@ -65,7 +65,7 @@ max_instances = 100
|
||||
COGNITO_JWKS_URL = "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_dtagLnju8/.well-known/jwks.json"
|
||||
COGNITO_ISSUER = "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_dtagLnju8"
|
||||
COGNITO_CLIENT_ID = "69cs1lgme7p8kbgld8n5kseii6"
|
||||
AGENT_RUNTIME_PROVIDER = "vercel"
|
||||
AGENT_RUNTIME_PROVIDER = "e2b"
|
||||
CLOUDFLARE_ACCOUNT_ID = "2553ea8236c11ea0f88de28fce1cbfee"
|
||||
BACKUP_BUCKET_NAME = "logseq-sync-assets-dev"
|
||||
SENTRY_DSN = "https://dc09d27243b9492bbe15e0dd279ad7de@o416451.ingest.us.sentry.io/5311485"
|
||||
@@ -108,7 +108,7 @@ name = "logseq-agents-prod"
|
||||
COGNITO_JWKS_URL = "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_dtagLnju8/.well-known/jwks.json"
|
||||
COGNITO_ISSUER = "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_dtagLnju8"
|
||||
COGNITO_CLIENT_ID = "69cs1lgme7p8kbgld8n5kseii6"
|
||||
AGENT_RUNTIME_PROVIDER = "vercel"
|
||||
AGENT_RUNTIME_PROVIDER = "e2b"
|
||||
CLOUDFLARE_ACCOUNT_ID = "2553ea8236c11ea0f88de28fce1cbfee"
|
||||
BACKUP_BUCKET_NAME = "logseq-sync-assets-prod"
|
||||
SENTRY_DSN = "https://dc09d27243b9492bbe15e0dd279ad7de@o416451.ingest.us.sentry.io/5311485"
|
||||
|
||||
218
deps/workers/yarn.lock
vendored
218
deps/workers/yarn.lock
vendored
@@ -16,6 +16,11 @@
|
||||
debug "^4.4.1"
|
||||
module-details-from-path "^1.0.4"
|
||||
|
||||
"@bufbuild/protobuf@^2.6.2":
|
||||
version "2.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@bufbuild/protobuf/-/protobuf-2.11.0.tgz#3ec3985c9074b23aea337957225fe15a0e845f8e"
|
||||
integrity sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==
|
||||
|
||||
"@cloudflare/containers@^0.0.30":
|
||||
version "0.0.30"
|
||||
resolved "https://registry.yarnpkg.com/@cloudflare/containers/-/containers-0.0.30.tgz#c825edd70712cfee6c578f2280e19991cbcfca94"
|
||||
@@ -29,11 +34,33 @@
|
||||
"@cloudflare/containers" "^0.0.30"
|
||||
aws4fetch "^1.0.20"
|
||||
|
||||
"@connectrpc/connect-web@2.0.0-rc.3":
|
||||
version "2.0.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@connectrpc/connect-web/-/connect-web-2.0.0-rc.3.tgz#4dd9c18b8105cb7395077808c4967a9eafdffe76"
|
||||
integrity sha512-w88P8Lsn5CCsA7MFRl2e6oLY4J/5toiNtJns/YJrlyQaWOy3RO8pDgkz+iIkG98RPMhj2thuBvsd3Cn4DKKCkw==
|
||||
|
||||
"@connectrpc/connect@2.0.0-rc.3":
|
||||
version "2.0.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@connectrpc/connect/-/connect-2.0.0-rc.3.tgz#f75cd1bd132dd983ff4f16432aaf5309ee673662"
|
||||
integrity sha512-ARBt64yEyKbanyRETTjcjJuHr2YXorzQo0etyS5+P6oSeW8xEuzajA9g+zDnMcj1hlX2dQE93foIWQGfpru7gQ==
|
||||
|
||||
"@fly/sprites@^0.0.1":
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@fly/sprites/-/sprites-0.0.1.tgz#c308cd6cc073714cd81512580bbd4de08c11d641"
|
||||
integrity sha512-1s+dIVi/pTMP4Aj4Mkg+4LoZ/+a0Kp6l9piPRxvpgEKm11b/eRiZgJwVytwAHeI/vtg2fuwcFExjtXOEfny/TA==
|
||||
|
||||
"@isaacs/cliui@^9.0.0":
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-9.0.0.tgz#4d0a3f127058043bf2e7ee169eaf30ed901302f3"
|
||||
integrity sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==
|
||||
|
||||
"@isaacs/fs-minipass@^4.0.0":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz#2d59ae3ab4b38fb4270bfa23d30f8e2e86c7fe32"
|
||||
integrity sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==
|
||||
dependencies:
|
||||
minipass "^7.0.4"
|
||||
|
||||
"@opentelemetry/api-logs@0.207.0":
|
||||
version "0.207.0"
|
||||
resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.207.0.tgz#ae991c51eedda55af037a3e6fc1ebdb12b289f49"
|
||||
@@ -550,6 +577,11 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
balanced-match@^4.0.2:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a"
|
||||
integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==
|
||||
|
||||
bare-events@^2.7.0:
|
||||
version "2.8.2"
|
||||
resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.8.2.tgz#7b3e10bd8e1fc80daf38bb516921678f566ab89f"
|
||||
@@ -591,6 +623,13 @@ brace-expansion@^2.0.1:
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
|
||||
brace-expansion@^5.0.2:
|
||||
version "5.0.4"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.4.tgz#614daaecd0a688f660bbbc909a8748c3d80d4336"
|
||||
integrity sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==
|
||||
dependencies:
|
||||
balanced-match "^4.0.2"
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
|
||||
@@ -612,16 +651,40 @@ buffer@^6.0.3:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.2.1"
|
||||
|
||||
chalk@^5.3.0:
|
||||
version "5.6.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.2.tgz#b1238b6e23ea337af71c7f8a295db5af0c158aea"
|
||||
integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==
|
||||
|
||||
chownr@^1.1.1:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
|
||||
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
|
||||
|
||||
chownr@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4"
|
||||
integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==
|
||||
|
||||
cjs-module-lexer@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz#b3ca5101843389259ade7d88c77bd06ce55849ca"
|
||||
integrity sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==
|
||||
|
||||
compare-versions@^6.1.0:
|
||||
version "6.1.1"
|
||||
resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-6.1.1.tgz#7af3cc1099ba37d244b3145a9af5201b629148a9"
|
||||
integrity sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==
|
||||
|
||||
cross-spawn@^7.0.6:
|
||||
version "7.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
|
||||
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
|
||||
dependencies:
|
||||
path-key "^3.1.0"
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
debug@^4.3.5, debug@^4.4.1:
|
||||
version "4.4.3"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
|
||||
@@ -646,6 +709,30 @@ detect-libc@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad"
|
||||
integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==
|
||||
|
||||
dockerfile-ast@^0.7.1:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/dockerfile-ast/-/dockerfile-ast-0.7.1.tgz#16f2be0434bfc940c9eaf00da97099fe5d2c050f"
|
||||
integrity sha512-oX/A4I0EhSkGqrFv0YuvPkBUSYp1XiY8O8zAKc8Djglx8ocz+JfOr8gP0ryRMC2myqvDLagmnZaU9ot1vG2ijw==
|
||||
dependencies:
|
||||
vscode-languageserver-textdocument "^1.0.8"
|
||||
vscode-languageserver-types "^3.17.3"
|
||||
|
||||
e2b@2.14.0:
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/e2b/-/e2b-2.14.0.tgz#40f163315fa73d2e6fc11d23c09f2dfa902041e3"
|
||||
integrity sha512-jNb8mFedmrUcTMCgoQT/JqG1IcCbxQvv137Tr1z2VlUZ6AMtYD/atQpExJ8/+481tcrtQCtCScuqbK+elKVDig==
|
||||
dependencies:
|
||||
"@bufbuild/protobuf" "^2.6.2"
|
||||
"@connectrpc/connect" "2.0.0-rc.3"
|
||||
"@connectrpc/connect-web" "2.0.0-rc.3"
|
||||
chalk "^5.3.0"
|
||||
compare-versions "^6.1.0"
|
||||
dockerfile-ast "^0.7.1"
|
||||
glob "^11.1.0"
|
||||
openapi-fetch "^0.14.1"
|
||||
platform "^1.3.6"
|
||||
tar "^7.5.9"
|
||||
|
||||
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
||||
version "1.4.5"
|
||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c"
|
||||
@@ -675,6 +762,14 @@ file-uri-to-path@1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
|
||||
integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
|
||||
|
||||
foreground-child@^3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f"
|
||||
integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==
|
||||
dependencies:
|
||||
cross-spawn "^7.0.6"
|
||||
signal-exit "^4.0.1"
|
||||
|
||||
forwarded-parse@2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/forwarded-parse/-/forwarded-parse-2.1.2.tgz#08511eddaaa2ddfd56ba11138eee7df117a09325"
|
||||
@@ -690,6 +785,18 @@ github-from-package@0.0.0:
|
||||
resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
|
||||
integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==
|
||||
|
||||
glob@^11.1.0:
|
||||
version "11.1.0"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-11.1.0.tgz#4f826576e4eb99c7dad383793d2f9f08f67e50a6"
|
||||
integrity sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==
|
||||
dependencies:
|
||||
foreground-child "^3.3.1"
|
||||
jackspeak "^4.1.1"
|
||||
minimatch "^10.1.1"
|
||||
minipass "^7.1.2"
|
||||
package-json-from-dist "^1.0.0"
|
||||
path-scurry "^2.0.0"
|
||||
|
||||
ieee754@^1.1.13, ieee754@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
@@ -725,16 +832,35 @@ isexe@^3.1.1:
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.1.tgz#4a407e2bd78ddfb14bea0c27c6f7072dde775f0d"
|
||||
integrity sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==
|
||||
|
||||
jackspeak@^4.1.1:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.2.3.tgz#27ef80f33b93412037c3bea4f8eddf80e1931483"
|
||||
integrity sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==
|
||||
dependencies:
|
||||
"@isaacs/cliui" "^9.0.0"
|
||||
|
||||
jsonlines@0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonlines/-/jsonlines-0.1.1.tgz#4fcd246dc5d0e38691907c44ab002f782d1d94cc"
|
||||
integrity sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA==
|
||||
|
||||
lru-cache@^11.0.0:
|
||||
version "11.2.6"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.6.tgz#356bf8a29e88a7a2945507b31f6429a65a192c58"
|
||||
integrity sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==
|
||||
|
||||
mimic-response@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
|
||||
integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
|
||||
|
||||
minimatch@^10.1.1:
|
||||
version "10.2.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.4.tgz#465b3accbd0218b8281f5301e27cedc697f96fde"
|
||||
integrity sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==
|
||||
dependencies:
|
||||
brace-expansion "^5.0.2"
|
||||
|
||||
minimatch@^9.0.0:
|
||||
version "9.0.5"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5"
|
||||
@@ -747,6 +873,18 @@ minimist@^1.2.0, minimist@^1.2.3:
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||
|
||||
minipass@^7.0.4, minipass@^7.1.2:
|
||||
version "7.1.3"
|
||||
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.3.tgz#79389b4eb1bb2d003a9bba87d492f2bd37bdc65b"
|
||||
integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==
|
||||
|
||||
minizlib@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-3.1.0.tgz#6ad76c3a8f10227c9b51d1c9ac8e30b27f5a251c"
|
||||
integrity sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==
|
||||
dependencies:
|
||||
minipass "^7.1.2"
|
||||
|
||||
mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
|
||||
@@ -781,11 +919,41 @@ once@^1.3.1, once@^1.4.0:
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
openapi-fetch@^0.14.1:
|
||||
version "0.14.1"
|
||||
resolved "https://registry.yarnpkg.com/openapi-fetch/-/openapi-fetch-0.14.1.tgz#6ae3e8a6f6398f457e22fec4197c7fa402e6ba06"
|
||||
integrity sha512-l7RarRHxlEZYjMLd/PR0slfMVse2/vvIAGm75/F7J6MlQ8/b9uUQmUF2kCPrQhJqMXSxmYWObVgeYXbFYzZR+A==
|
||||
dependencies:
|
||||
openapi-typescript-helpers "^0.0.15"
|
||||
|
||||
openapi-typescript-helpers@^0.0.15:
|
||||
version "0.0.15"
|
||||
resolved "https://registry.yarnpkg.com/openapi-typescript-helpers/-/openapi-typescript-helpers-0.0.15.tgz#96ffa762a5e01ef66a661b163d5f1109ed1967ed"
|
||||
integrity sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==
|
||||
|
||||
os-paths@^4.0.1:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/os-paths/-/os-paths-4.4.0.tgz#2908b5bcb60cbfe3afb869292281a2a6b2f77ebe"
|
||||
integrity sha512-wrAwOeXp1RRMFfQY8Sy7VaGVmPocaLwSFOYCGKSyo8qmJ+/yaafCl5BCA1IQZWqFSRBrKDYFeR9d/VyQzfH/jg==
|
||||
|
||||
package-json-from-dist@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505"
|
||||
integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==
|
||||
|
||||
path-key@^3.1.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
|
||||
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
||||
|
||||
path-scurry@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.2.tgz#6be0d0ee02a10d9e0de7a98bae65e182c9061f85"
|
||||
integrity sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==
|
||||
dependencies:
|
||||
lru-cache "^11.0.0"
|
||||
minipass "^7.1.2"
|
||||
|
||||
pg-int8@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c"
|
||||
@@ -812,6 +980,11 @@ picocolors@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
||||
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
||||
|
||||
platform@^1.3.6:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7"
|
||||
integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==
|
||||
|
||||
postgres-array@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e"
|
||||
@@ -940,6 +1113,23 @@ shadow-cljs@^3.3.4:
|
||||
which "^5.0.0"
|
||||
ws "^8.18.1"
|
||||
|
||||
shebang-command@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
|
||||
integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
|
||||
dependencies:
|
||||
shebang-regex "^3.0.0"
|
||||
|
||||
shebang-regex@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
|
||||
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
|
||||
|
||||
signal-exit@^4.0.1:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
|
||||
integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
|
||||
|
||||
simple-concat@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
|
||||
@@ -1018,6 +1208,17 @@ tar-stream@^2.1.4:
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^3.1.1"
|
||||
|
||||
tar@^7.5.9:
|
||||
version "7.5.10"
|
||||
resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.10.tgz#2281541123f5507db38bc6eb22619f4bbaef73ad"
|
||||
integrity sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw==
|
||||
dependencies:
|
||||
"@isaacs/fs-minipass" "^4.0.0"
|
||||
chownr "^3.0.0"
|
||||
minipass "^7.1.2"
|
||||
minizlib "^3.1.0"
|
||||
yallist "^5.0.0"
|
||||
|
||||
text-decoder@^1.1.0:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.7.tgz#5d073a9a74b9c0a9d28dfadcab96b604af57d8ba"
|
||||
@@ -1052,7 +1253,17 @@ util-deprecate@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||
|
||||
which@^2.0.2:
|
||||
vscode-languageserver-textdocument@^1.0.8:
|
||||
version "1.0.12"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz#457ee04271ab38998a093c68c2342f53f6e4a631"
|
||||
integrity sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==
|
||||
|
||||
vscode-languageserver-types@^3.17.3:
|
||||
version "3.17.5"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz#3273676f0cf2eab40b3f44d085acbb7f08a39d8a"
|
||||
integrity sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==
|
||||
|
||||
which@^2.0.1, which@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
||||
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
|
||||
@@ -1095,6 +1306,11 @@ xtend@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||
|
||||
yallist@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-5.0.0.tgz#00e2de443639ed0d78fd87de0d27469fbcffb533"
|
||||
integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==
|
||||
|
||||
zod@3.24.4:
|
||||
version "3.24.4"
|
||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.4.tgz#e2e2cca5faaa012d76e527d0d36622e0a90c315f"
|
||||
|
||||
Reference in New Issue
Block a user