From 4fedfdfd11aa8ca7fff6f7aed5ae2b313888f878 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sun, 22 Feb 2026 08:53:28 +0100 Subject: [PATCH] feat(cli): add consistent get/list/new subcommands for all item types (#8047) * feat(cli): add consistent get/list/new subcommands for all item types Make the CLI consistent so every item type (script, flow, app, resource, resource-type, variable, schedule, folder, trigger) supports get/list/new subcommands, enabling the CLI to be used as a full API client in bash scripts with jq piping. - Add --json flag to all list commands for machine-readable output - Register explicit "list" subcommand alongside default action - Add "get [--json]" subcommand to fetch single items from API - Rename "bootstrap" to "new" for script/flow, keep "bootstrap" as alias - Add "new" subcommand for resource, resource-type, variable, schedule, folder, and trigger to create local template YAML files - Update cli-commands skill documentation for wmill init - Add integration tests for all new commands Co-Authored-By: Claude Opus 4.6 * all * feat: install wmill CLI in Docker images and use it for bash variable/resource access - Install windmill-cli via bun in all Dockerfiles that include bun - DockerfileCli: switch from node:slim to oven/bun:slim - CLI: auto-configure from WM_WORKSPACE/WM_TOKEN/BASE_INTERNAL_URL env vars as last-resort fallback when no workspace is configured - Frontend: replace curl-based bash snippets with wmill variable/resource get - Add backend integration tests for wmill CLI in bash scripts Co-Authored-By: Claude Opus 4.6 * fix(ci): install windmill-cli in backend test workflow Ensures wmill is available on PATH for bash integration tests that use `wmill variable get` and `wmill resource get`. Co-Authored-By: Claude Opus 4.6 * refactor(cli): replace @std/* Deno dependencies with Node.js equivalents Replace @std/log with a lightweight custom logger (core/log.ts), @std/path with node:path, and @std/yaml with the yaml npm package. Also fix process hang on exit, add --node option to install_dev.sh, and add missing hasRequiredPermissions to NpmProvider. Co-Authored-By: Claude Opus 4.6 * all * all * all * refactor(cli): replace @ayonli/jsext and @std/encoding with lightweight alternatives Replace @ayonli/jsext (8.4MB) with tar-stream (32kB) for tar creation, replace @std/encoding with Node.js Buffer.toString("hex"), and fix @windmill-labs/shared-utils to use direct npm instead of JSR mirror. Also resolve merge conflicts in sync.ts and fix pre-existing type errors. Co-Authored-By: Claude Opus 4.6 * fix(cli): use singleQuote YAML output and pass yamlOptions in gitsync pull The yaml library defaults to double quotes, but the codebase (and tests) expect single-quoted strings. Add singleQuote: true to yamlOptions and pass yamlOptions to gitsync-settings pull writeFile calls. Co-Authored-By: Claude Opus 4.6 * all * all * fix(cli): address code review feedback - Install CLI from source in backend tests instead of npm - Fix script bootstrap catch block to re-throw "File already exists" - Add type-safe local variable after trigger kind validation - Use created_by instead of policy.on_behalf_of for app get output - Note --kind is recommended for faster trigger lookup in help text - Document node symlink purpose in Dockerfiles Co-Authored-By: Claude Opus 4.6 * fix(ci): use /usr/bin for wmill wrapper to ensure it's in PATH Co-Authored-By: Claude Opus 4.6 * fix(ci): install wmill to ~/.local/bin to avoid permission issues Co-Authored-By: Claude Opus 4.6 * ci(backend): switch to Blacksmith runner and add cargo caching - Switch from ubicloud-standard-16 to blacksmith-16vcpu-ubuntu-2404 for faster NVMe-backed builds - Add stickydisk for cargo target directory (persistent NVMe cache across runs) - Add cache for cargo registry and git dependencies - Upgrade DuckDB FFI cache from actions/cache@v3 to useblacksmith/cache@v1 - Enable CARGO_INCREMENTAL=1 to benefit from persistent target cache Co-Authored-By: Claude Opus 4.6 * fix ci --------- Co-authored-by: Claude Opus 4.6 --- .github/DockerfileBackendTests | 4 + .github/workflows/backend-test.yml | 29 +- Dockerfile | 4 + backend/tests/fixtures/wmill_cli_test.sql | 10 + backend/tests/worker.rs | 74 + cli/bun.lock | 47 +- cli/install_dev.sh | 34 +- cli/package-lock.json | 1498 +++++++++++++++++ cli/package.json | 13 +- cli/src/commands/app/app.ts | 46 +- cli/src/commands/app/app_metadata.ts | 6 +- cli/src/commands/app/bundle.ts | 2 +- cli/src/commands/app/dev.ts | 4 +- cli/src/commands/app/generate_agents.ts | 2 +- cli/src/commands/app/lint.ts | 2 +- cli/src/commands/app/new.ts | 4 +- cli/src/commands/app/raw_apps.ts | 6 +- cli/src/commands/dependencies/dependencies.ts | 2 +- cli/src/commands/dev/dev.ts | 4 +- cli/src/commands/flow/flow.ts | 64 +- cli/src/commands/flow/flow_metadata.ts | 8 +- cli/src/commands/folder/folder.ts | 86 +- cli/src/commands/gitsync-settings/pull.ts | 11 +- cli/src/commands/gitsync-settings/push.ts | 2 +- cli/src/commands/gitsync-settings/utils.ts | 2 +- cli/src/commands/hub/hub.ts | 2 +- cli/src/commands/init/init.ts | 4 +- cli/src/commands/instance/instance.ts | 6 +- cli/src/commands/jobs/jobs.ts | 2 +- cli/src/commands/lint/lint.ts | 6 +- cli/src/commands/queues/queues.ts | 4 +- .../commands/resource-type/resource-type.ts | 60 +- cli/src/commands/resource/resource.ts | 77 +- cli/src/commands/schedule/schedule.ts | 82 +- cli/src/commands/script/script.ts | 161 +- cli/src/commands/sync/global.ts | 2 +- cli/src/commands/sync/pull.ts | 2 +- cli/src/commands/sync/push.ts | 2 +- cli/src/commands/sync/sync.ts | 29 +- cli/src/commands/trigger/trigger.ts | 247 ++- cli/src/commands/user/user.ts | 4 +- cli/src/commands/variable/variable.ts | 91 +- .../commands/worker-groups/worker-groups.ts | 2 +- cli/src/commands/workers/workers.ts | 2 +- cli/src/commands/workspace/fork.ts | 2 +- cli/src/commands/workspace/workspace.ts | 4 +- cli/src/core/auth.ts | 2 +- cli/src/core/branch-profiles.ts | 2 +- cli/src/core/conf.ts | 6 +- cli/src/core/context.ts | 40 +- cli/src/core/log.ts | 24 + cli/src/core/login.ts | 2 +- cli/src/core/settings.ts | 4 +- cli/src/guidance/skills.ts | 92 +- cli/src/main.ts | 25 +- cli/src/types.ts | 8 +- cli/src/utils/codebase.ts | 2 +- cli/src/utils/git.ts | 2 +- cli/src/utils/metadata.ts | 6 +- cli/src/utils/resource_folders.ts | 4 +- cli/src/utils/tar.ts | 22 + cli/src/utils/upgrade.ts | 4 + cli/src/utils/utils.ts | 7 +- cli/src/utils/yaml.ts | 2 +- cli/test/lint_command.test.ts | 2 +- cli/test/lint_locks.test.ts | 2 +- cli/test/list_get_new_commands.test.ts | 639 +++++++ cli/test/lock_cache.test.ts | 4 +- cli/test/mixed_case_paths.test.ts | 2 +- cli/test/raw_app_sync.test.ts | 2 +- cli/test/sync_pull_push.test.ts | 4 +- cli/test/tar_creation.test.ts | 140 ++ cli/test/wmill_lock.test.ts | 4 +- cli/test/workspace_deps_filter.test.ts | 2 +- .../src/parse/parse-schema.ts | 4 +- docker/DockerfileCli | 8 +- docker/DockerfileSlim | 5 + docker/DockerfileSlimEe | 5 + frontend/package-lock.json | 46 +- frontend/src/lib/components/EditorBar.svelte | 6 +- 80 files changed, 3465 insertions(+), 419 deletions(-) create mode 100644 backend/tests/fixtures/wmill_cli_test.sql create mode 100644 cli/package-lock.json create mode 100644 cli/src/core/log.ts create mode 100644 cli/src/utils/tar.ts create mode 100644 cli/test/list_get_new_commands.test.ts create mode 100644 cli/test/tar_creation.test.ts diff --git a/.github/DockerfileBackendTests b/.github/DockerfileBackendTests index 8783bb241c..b204856823 100644 --- a/.github/DockerfileBackendTests +++ b/.github/DockerfileBackendTests @@ -44,6 +44,10 @@ RUN /usr/local/bin/python3 -m pip install pip-tools # Bun COPY --from=oven/bun:1.3.8 /usr/local/bin/bun /usr/bin/bun +# Install windmill CLI +RUN bun install -g windmill-cli \ + && ln -s $(bun pm bin -g)/wmill /usr/bin/wmill + ARG TARGETPLATFORM # Deno diff --git a/.github/workflows/backend-test.yml b/.github/workflows/backend-test.yml index 48bd1e11ec..f3a7d1e883 100644 --- a/.github/workflows/backend-test.yml +++ b/.github/workflows/backend-test.yml @@ -19,7 +19,7 @@ defaults: jobs: cargo_test: - runs-on: ubicloud-standard-16 + runs-on: blacksmith-16vcpu-ubuntu-2404 services: postgres: image: postgres @@ -70,6 +70,16 @@ jobs: with: ruby-version: "3.3" bundler-cache: false + - name: Install windmill CLI from source + run: | + cd $GITHUB_WORKSPACE/cli + bash gen_wm_client.sh + bun install + mkdir -p "$HOME/.local/bin" + printf '#!/bin/sh\nexec bun run "%s/cli/src/main.ts" "$@"\n' "$GITHUB_WORKSPACE" > "$HOME/.local/bin/wmill" + chmod +x "$HOME/.local/bin/wmill" + echo "$HOME/.local/bin" >> $GITHUB_PATH + working-directory: / - name: Install PowerShell, mold and clang run: | sudo apt-get update && sudo apt-get install -y powershell mold clang libcurl4-openssl-dev @@ -78,6 +88,20 @@ jobs: with: cache: false toolchain: 1.93.0 + - name: Cache cargo target directory + uses: useblacksmith/stickydisk@v1 + with: + key: cargo-target + path: ./backend/target + - name: Cache cargo registry + uses: useblacksmith/cache@v1 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: cargo-registry-${{ hashFiles('backend/Cargo.lock') }} + restore-keys: | + cargo-registry- - name: Read EE repo commit hash run: | echo "ee_repo_ref=$(cat ./ee-repo-ref.txt)" >> "$GITHUB_ENV" @@ -205,7 +229,7 @@ jobs: fi echo "Verified: Package requires authentication for @windmill-test/private-pkg" - name: Cache DuckDB FFI module build - uses: actions/cache@v3 + uses: useblacksmith/cache@v1 with: path: ./backend/windmill-duckdb-ffi-internal/target key: ${{ runner.os }}-duckdb-ffi-${{ hashFiles('./backend/windmill-duckdb-ffi-internal/src/**/*.rs', './backend/windmill-duckdb-ffi-internal/Cargo.toml', './backend/windmill-duckdb-ffi-internal/Cargo.lock') }} @@ -221,6 +245,7 @@ jobs: RUST_LOG_STYLE: never CARGO_NET_GIT_FETCH_WITH_CLI: true CARGO_BUILD_JOBS: 12 + CARGO_INCREMENTAL: 1 WMDEBUG_FORCE_V0_WORKSPACE_DEPENDENCIES: 1 WMDEBUG_FORCE_RUNNABLE_SETTINGS_V0: 1 WMDEBUG_FORCE_NO_LEGACY_DEBOUNCING_COMPAT: 1 diff --git a/Dockerfile b/Dockerfile index 16647165db..3b9588a697 100644 --- a/Dockerfile +++ b/Dockerfile @@ -258,6 +258,10 @@ COPY --from=denoland/deno:2.2.1 --chmod=755 /usr/bin/deno /usr/bin/deno COPY --from=oven/bun:1.3.8 /usr/local/bin/bun /usr/bin/bun +# Install windmill CLI +RUN bun install -g windmill-cli \ + && ln -s $(bun pm bin -g)/wmill /usr/bin/wmill + COPY --from=php:8.3.7-cli /usr/local/bin/php /usr/bin/php COPY --from=composer:2.7.6 /usr/bin/composer /usr/bin/composer diff --git a/backend/tests/fixtures/wmill_cli_test.sql b/backend/tests/fixtures/wmill_cli_test.sql new file mode 100644 index 0000000000..e4ac24734b --- /dev/null +++ b/backend/tests/fixtures/wmill_cli_test.sql @@ -0,0 +1,10 @@ +-- Fixture for testing wmill CLI variable/resource get from bash scripts + +INSERT INTO variable (workspace_id, path, value, is_secret, description, extra_perms) +VALUES ('test-workspace', 'u/test-user/test_var', 'hello from variable', false, 'A test variable', '{"u/test-user": true}'); + +INSERT INTO resource_type (workspace_id, name, schema, description, created_by) +VALUES ('test-workspace', 'test_object', '{}', 'Test object type', 'test-user'); + +INSERT INTO resource (workspace_id, path, value, description, resource_type, extra_perms, created_by) +VALUES ('test-workspace', 'u/test-user/test_res', '{"host": "localhost", "port": 5432}', 'A test resource', 'test_object', '{"u/test-user": true}', 'test-user'); diff --git a/backend/tests/worker.rs b/backend/tests/worker.rs index 647647604f..9548986a5a 100644 --- a/backend/tests/worker.rs +++ b/backend/tests/worker.rs @@ -993,6 +993,80 @@ echo "hello $msg" Ok(()) } +#[sqlx::test(fixtures("base", "wmill_cli_test"))] +async fn test_bash_wmill_variable_get(db: Pool) -> anyhow::Result<()> { + initialize_tracing().await; + let server = ApiServer::start(db.clone()).await?; + let port = server.addr.port(); + + // The bash script uses wmill CLI to get the variable value. + // The worker sets WM_TOKEN, WM_WORKSPACE, and BASE_INTERNAL_URL as env vars, + // and the CLI auto-configures from them when no workspace is explicitly set. + // We point WMILL_CONFIG_DIR to a clean temp dir so no local active workspace interferes. + let content = r#" +export WMILL_CONFIG_DIR=$(mktemp -d) +result=$(wmill variable get "u/test-user/test_var" --json | jq -r .value) +echo "$result" +"# + .to_owned(); + + let job = RunJob::from(JobPayload::Code(RawCode { + hash: None, + content, + path: None, + lock: None, + language: ScriptLang::Bash, + cache_ttl: None, + cache_ignore_s3_path: None, + dedicated_worker: None, + concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default() + .into(), + debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(), + })) + .run_until_complete(&db, false, port) + .await; + assert_eq!(job.json_result(), Some(json!("hello from variable"))); + Ok(()) +} + +#[sqlx::test(fixtures("base", "wmill_cli_test"))] +async fn test_bash_wmill_resource_get(db: Pool) -> anyhow::Result<()> { + initialize_tracing().await; + let server = ApiServer::start(db.clone()).await?; + let port = server.addr.port(); + + // The bash script uses wmill CLI to get the resource value. + // We point WMILL_CONFIG_DIR to a clean temp dir so no local active workspace interferes. + let content = r#" +export WMILL_CONFIG_DIR=$(mktemp -d) +result=$(wmill resource get "u/test-user/test_res" --json | jq -c .value) +echo "$result" +"# + .to_owned(); + + let job = RunJob::from(JobPayload::Code(RawCode { + hash: None, + content, + path: None, + lock: None, + language: ScriptLang::Bash, + cache_ttl: None, + cache_ignore_s3_path: None, + dedicated_worker: None, + concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default() + .into(), + debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(), + })) + .run_until_complete(&db, false, port) + .await; + // Bash echo outputs are returned as strings, so the JSON is a string value + assert_eq!( + job.json_result(), + Some(json!("{\"host\":\"localhost\",\"port\":5432}")) + ); + Ok(()) +} + #[cfg(feature = "nu")] #[sqlx::test(fixtures("base"))] async fn test_nu_job(db: Pool) -> anyhow::Result<()> { diff --git a/cli/bun.lock b/cli/bun.lock index 951ba47c7c..7fea1c929b 100644 --- a/cli/bun.lock +++ b/cli/bun.lock @@ -5,16 +5,11 @@ "": { "name": "windmill-cli-dev", "dependencies": { - "@ayonli/jsext": "^1.9.0", "@cliffy/ansi": "npm:@jsr/cliffy__ansi@1.0.0", "@cliffy/command": "npm:@jsr/cliffy__command@1.0.0", "@cliffy/prompt": "npm:@jsr/cliffy__prompt@1.0.0", "@cliffy/table": "npm:@jsr/cliffy__table@1.0.0", - "@std/encoding": "npm:@jsr/std__encoding@1.0.10", - "@std/log": "npm:@jsr/std__log@0.224.14", - "@std/path": "npm:@jsr/std__path@1.1.4", - "@std/yaml": "npm:@jsr/std__yaml@1.0.10", - "@windmill-labs/shared-utils": "npm:@jsr/windmill-labs__shared-utils@1.0.12", + "@windmill-labs/shared-utils": "^1.0.12", "diff": "^5.2.0", "esbuild": "0.24.2", "get-port": "7.1.0", @@ -22,6 +17,7 @@ "minimatch": "^10.0.0", "open": "^10.0.0", "svelte": "^5.45.2", + "tar-stream": "^3.1.7", "windmill-parser-wasm-csharp": "*", "windmill-parser-wasm-go": "*", "windmill-parser-wasm-java": "*", @@ -40,14 +36,13 @@ "devDependencies": { "@types/diff": "^5.2.3", "@types/node": "^22.0.0", + "@types/tar-stream": "^3.1.4", "@types/ws": "^8.5.0", "typescript": "^5.7.0", }, }, }, "packages": { - "@ayonli/jsext": ["@ayonli/jsext@1.9.0", "", { "dependencies": { "iconv-lite": "^0.6.3", "sudo-prompt": "^9.2.1", "ws": "^8.17.0", "zod": "^3.23.8" } }, "sha512-hIu6lQhoLr5e26lmt+vzopuZffaAyb623r4+8HlN/rhXgm2ywHslzk7UHiATdfDbfPjBARkB6cfXjVEi3aav6g=="], - "@cliffy/ansi": ["@jsr/cliffy__ansi@1.0.0", "https://npm.jsr.io/~/11/@jsr/cliffy__ansi/1.0.0.tgz", { "dependencies": { "@jsr/cliffy__internal": "1.0.0", "@jsr/std__encoding": "^1.0.10", "@jsr/std__fmt": "^1.0.9", "@jsr/std__io": "~0.225.3" } }, "sha512-JesgTdgR0aW1mZv96VqvRHr2efzr4MgDFMnoT+hkhaiCpmyBz33sHM5peAoMJUbGVfEfQAsysIXvvgoFYoveYg=="], "@cliffy/command": ["@jsr/cliffy__command@1.0.0", "https://npm.jsr.io/~/11/@jsr/cliffy__command/1.0.0.tgz", { "dependencies": { "@jsr/cliffy__flags": "1.0.0", "@jsr/cliffy__internal": "1.0.0", "@jsr/cliffy__table": "1.0.0", "@jsr/std__fmt": "^1.0.9", "@jsr/std__semver": "^1.0.8", "@jsr/std__text": "^1.0.17" } }, "sha512-oObplVtu1tvpkhgpuPDHZidx9g3axVOfRMQGmw7ZSGxp0+vZIJGiEtpcSvlN0XfuEhOG8neqfVBSSE9txrKanw=="], @@ -134,8 +129,6 @@ "@jsr/std__fmt": ["@jsr/std__fmt@1.0.9", "https://npm.jsr.io/~/11/@jsr/std__fmt/1.0.9.tgz", {}, "sha512-YFJJMozmORj2K91c5J9opWeh0VUwrd+Mwb7Pr0FkVCAKVLu2UhT4LyvJqWiyUT+eF+MdfqQ9F7RtQj4bXn9Smw=="], - "@jsr/std__fs": ["@jsr/std__fs@1.0.21", "https://npm.jsr.io/~/11/@jsr/std__fs/1.0.21.tgz", { "dependencies": { "@jsr/std__internal": "^1.0.12", "@jsr/std__path": "^1.1.4" } }, "sha512-k/agrcKGm6KD89ci3AEyRmu3wRWf9JZNliOF4ZUxagTHiySmxjiKU3Lk+d2ksRtwEi7oWlLGS0AVM9Lciwc/xg=="], - "@jsr/std__internal": ["@jsr/std__internal@1.0.12", "https://npm.jsr.io/~/11/@jsr/std__internal/1.0.12.tgz", {}, "sha512-6xReMW9p+paJgqoFRpOE2nogJFvzPfaLHLIlyADYjKMUcwDyjKZxryIbgcU+gxiTygn8yCjld1HoI0ET4/iZeA=="], "@jsr/std__io": ["@jsr/std__io@0.225.3", "https://npm.jsr.io/~/11/@jsr/std__io/0.225.3.tgz", { "dependencies": { "@jsr/std__bytes": "^1.0.6" } }, "sha512-IDXY253ipW6FV34CJVxO+3ubfvSEEzw9N2W303KnLe9K/Y9+v/ID1dQYf9VsCCOFMpFtCmOLqzIZsRqv6yQnWw=="], @@ -148,14 +141,6 @@ "@jsr/std__text": ["@jsr/std__text@1.0.17", "https://npm.jsr.io/~/11/@jsr/std__text/1.0.17.tgz", { "dependencies": { "@jsr/std__regexp": "^1.0.1" } }, "sha512-oZsihl1bcTy1Ixzven8rin8kjChj1zDJWqgpS0oSMGCJDzyB365gtIfAvcMmji+M+FcIWo3goDXfHcFYt+k/kg=="], - "@std/encoding": ["@jsr/std__encoding@1.0.10", "https://npm.jsr.io/~/11/@jsr/std__encoding/1.0.10.tgz", {}, "sha512-WK2njnDTyKefroRNk2Ooq7GStp6Y0ccAvr4To+Z/zecRAGe7+OSvH9DbiaHpAKwEi2KQbmpWMOYsdNt+TsdmSw=="], - - "@std/log": ["@jsr/std__log@0.224.14", "https://npm.jsr.io/~/11/@jsr/std__log/0.224.14.tgz", { "dependencies": { "@jsr/std__fmt": "^1.0.5", "@jsr/std__fs": "^1.0.11", "@jsr/std__io": "^0.225.2" } }, "sha512-EHT7E0plakyzk/gxMrwqUf3YGCCxN3Is25QrEh7toYA7qwj46R4qY7cIaDEKy8QqI5JHOFHwWXOClcPK6goIoQ=="], - - "@std/path": ["@jsr/std__path@1.1.4", "https://npm.jsr.io/~/11/@jsr/std__path/1.1.4.tgz", { "dependencies": { "@jsr/std__internal": "^1.0.12" } }, "sha512-SK4u9H6NVTfolhPdlvdYXfNFefy1W04AEHWJydryYbk+xqzNiVmr5o7TLJLJFqwHXuwMRhwrn+mcYeUfS0YFaA=="], - - "@std/yaml": ["@jsr/std__yaml@1.0.10", "https://npm.jsr.io/~/11/@jsr/std__yaml/1.0.10.tgz", {}, "sha512-1WIM023Kvi48pvPE3UO5YcieambLgywUooLhAkkaObIcMB77F/YP2ILdl+vNfik+vElkl9znmuST9AZo8mbCpA=="], - "@stoplight/ordered-object-literal": ["@stoplight/ordered-object-literal@1.0.5", "", {}, "sha512-COTiuCU5bgMUtbIFBuyyh2/yVVzlr5Om0v5utQDgBCuQUOPgU1DwoffkTfg4UBQOvByi5foF4w4T+H9CoRe5wg=="], "@stoplight/types": ["@stoplight/types@14.1.1", "", { "dependencies": { "@types/json-schema": "^7.0.4", "utility-types": "^3.10.0" } }, "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g=="], @@ -174,11 +159,13 @@ "@types/node": ["@types/node@22.19.11", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w=="], + "@types/tar-stream": ["@types/tar-stream@3.1.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-921gW0+g29mCJX0fRvqeHzBlE/XclDaAG0Ousy1LCghsOhvaKacDeRGEVzQP9IPfKn8Vysy7FEXAIxycpc/CMg=="], + "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], - "@windmill-labs/shared-utils": ["@jsr/windmill-labs__shared-utils@1.0.12", "https://npm.jsr.io/~/11/@jsr/windmill-labs__shared-utils/1.0.12.tgz", {}, "sha512-bJOacyfxxNPwNTzA4AxCB5iGFop0h3mCgs+E9j3ZaJYDo1soblY16CebnQ56EPy/M3V344X/QoOFBORyRo1Mnw=="], + "@windmill-labs/shared-utils": ["@windmill-labs/shared-utils@1.0.12", "", {}, "sha512-n68uEYv2B5q2Pp8J9syMS3qPZbppFEfeM7HIBEUfU5lGqi3hwnv4mPvgRUyb6K9im3frXC4gzdIdZdlrDpudXQ=="], "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], @@ -188,8 +175,12 @@ "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], + "b4a": ["b4a@1.8.0", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg=="], + "balanced-match": ["balanced-match@4.0.3", "", {}, "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g=="], + "bare-events": ["bare-events@2.8.2", "", { "peerDependencies": { "bare-abort-controller": "*" }, "optionalPeers": ["bare-abort-controller"] }, "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ=="], + "brace-expansion": ["brace-expansion@5.0.2", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw=="], "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], @@ -214,14 +205,16 @@ "esrap": ["esrap@2.2.3", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-8fOS+GIGCQZl/ZIlhl59htOlms6U8NvX6ZYgYHpRU/b6tVSh3uHkOHZikl3D4cMbYM0JlpBe+p/BkZEi8J9XIQ=="], + "events-universal": ["events-universal@1.0.1", "", { "dependencies": { "bare-events": "^2.7.0" } }, "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw=="], + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + "fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="], + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], "get-port": ["get-port@7.1.0", "", {}, "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw=="], - "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], - "immediate": ["immediate@3.0.6", "", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="], "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], @@ -262,16 +255,18 @@ "safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], - "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - "set-immediate-shim": ["set-immediate-shim@1.0.1", "", {}, "sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ=="], + "streamx": ["streamx@2.23.0", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg=="], + "string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], - "sudo-prompt": ["sudo-prompt@9.2.1", "", {}, "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw=="], - "svelte": ["svelte@5.53.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.6.3", "esm-env": "^1.2.1", "esrap": "^2.2.2", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-WzxFHZhhD23Qzu7JCYdvm1rxvRSzdt9HtHO8TScMBX51bLRFTcJmATVqjqXG+6Ln6hrViGCo9DzwOhAasxwC/w=="], + "tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="], + + "text-decoder": ["text-decoder@1.2.7", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ=="], + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], @@ -313,7 +308,5 @@ "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], "zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], - - "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], } } diff --git a/cli/install_dev.sh b/cli/install_dev.sh index b552a47352..ebfa99bd69 100755 --- a/cli/install_dev.sh +++ b/cli/install_dev.sh @@ -2,10 +2,19 @@ set -e -if [ -z "$1" ]; then +# Parse options +USE_NODE=false +name="" +for arg in "$@"; do + case "$arg" in + --node|-node|---node) USE_NODE=true ;; + -*) echo "Unknown option: $arg"; echo "Usage: $0 [name] [--node]"; exit 1 ;; + *) [ -z "$name" ] && name="$arg" ;; + esac +done + +if [ -z "$name" ]; then name="wmill-dev" -else - name="$1" fi SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" @@ -18,10 +27,25 @@ bun install INSTALL_DIR="$HOME/.local/bin" mkdir -p "$INSTALL_DIR" -cat > "$INSTALL_DIR/$name" < "$INSTALL_DIR/$name" < "$INSTALL_DIR/$name" <=14.18" + } + }, + "node_modules/@cliffy/ansi": { + "name": "@jsr/cliffy__ansi", + "version": "1.0.0", + "resolved": "https://npm.jsr.io/~/11/@jsr/cliffy__ansi/1.0.0.tgz", + "integrity": "sha512-JesgTdgR0aW1mZv96VqvRHr2efzr4MgDFMnoT+hkhaiCpmyBz33sHM5peAoMJUbGVfEfQAsysIXvvgoFYoveYg==", + "dependencies": { + "@jsr/cliffy__internal": "1.0.0", + "@jsr/std__encoding": "^1.0.10", + "@jsr/std__fmt": "^1.0.9", + "@jsr/std__io": "~0.225.3" + } + }, + "node_modules/@cliffy/command": { + "name": "@jsr/cliffy__command", + "version": "1.0.0", + "resolved": "https://npm.jsr.io/~/11/@jsr/cliffy__command/1.0.0.tgz", + "integrity": "sha512-oObplVtu1tvpkhgpuPDHZidx9g3axVOfRMQGmw7ZSGxp0+vZIJGiEtpcSvlN0XfuEhOG8neqfVBSSE9txrKanw==", + "dependencies": { + "@jsr/cliffy__flags": "1.0.0", + "@jsr/cliffy__internal": "1.0.0", + "@jsr/cliffy__table": "1.0.0", + "@jsr/std__fmt": "^1.0.9", + "@jsr/std__semver": "^1.0.8", + "@jsr/std__text": "^1.0.17" + } + }, + "node_modules/@cliffy/prompt": { + "name": "@jsr/cliffy__prompt", + "version": "1.0.0", + "resolved": "https://npm.jsr.io/~/11/@jsr/cliffy__prompt/1.0.0.tgz", + "integrity": "sha512-JDuHcCAjScV0IUj389brneF6AzJyyP0pK8mymsrGN5/PGQfqK8zr96QpFlo1wmo8BY/3JQAdNfy6NZkPCJ6VWA==", + "dependencies": { + "@jsr/cliffy__ansi": "1.0.0", + "@jsr/cliffy__internal": "1.0.0", + "@jsr/cliffy__keycode": "1.0.0", + "@jsr/std__assert": "^1.0.18", + "@jsr/std__fmt": "^1.0.9", + "@jsr/std__io": "~0.225.3", + "@jsr/std__path": "^1.1.4", + "@jsr/std__text": "^1.0.17" + } + }, + "node_modules/@cliffy/table": { + "name": "@jsr/cliffy__table", + "version": "1.0.0", + "resolved": "https://npm.jsr.io/~/11/@jsr/cliffy__table/1.0.0.tgz", + "integrity": "sha512-VoLxH0DjofHWPWKUc5N+oCwXB6O6e+carnhp23yJTa7qokBb+SCrTIABEgQdIe/p0bxgmZhz17xt2efaAxXvbQ==", + "dependencies": { + "@jsr/std__fmt": "^1.0.9" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", + "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jsr/cliffy__ansi": { + "version": "1.0.0", + "resolved": "https://npm.jsr.io/~/11/@jsr/cliffy__ansi/1.0.0.tgz", + "integrity": "sha512-JesgTdgR0aW1mZv96VqvRHr2efzr4MgDFMnoT+hkhaiCpmyBz33sHM5peAoMJUbGVfEfQAsysIXvvgoFYoveYg==", + "dependencies": { + "@jsr/cliffy__internal": "1.0.0", + "@jsr/std__encoding": "^1.0.10", + "@jsr/std__fmt": "^1.0.9", + "@jsr/std__io": "~0.225.3" + } + }, + "node_modules/@jsr/cliffy__flags": { + "version": "1.0.0", + "resolved": "https://npm.jsr.io/~/11/@jsr/cliffy__flags/1.0.0.tgz", + "integrity": "sha512-j/v3J8MWu0tkYyisZ2w1HxELxxL/qg6vey9+fRkbTJ+S9J0GeLUn2joouikG7aXpULKCXHTjJ9XH9gQx+F3npw==", + "dependencies": { + "@jsr/cliffy__internal": "1.0.0", + "@jsr/std__text": "^1.0.17" + } + }, + "node_modules/@jsr/cliffy__internal": { + "version": "1.0.0", + "resolved": "https://npm.jsr.io/~/11/@jsr/cliffy__internal/1.0.0.tgz", + "integrity": "sha512-YPkbccbuu+kE55k+nia5jJx5Tu/IolBDXZTAgEA+YRGOzq8I1VkXajwykFXvSbXeVee3zQBU7y0HajVDB7ujQA==", + "dependencies": { + "@jsr/std__fmt": "^1.0.9" + } + }, + "node_modules/@jsr/cliffy__keycode": { + "version": "1.0.0", + "resolved": "https://npm.jsr.io/~/11/@jsr/cliffy__keycode/1.0.0.tgz", + "integrity": "sha512-1ot+y8oZheBTpfgCazWjSOAK2Y2nOQD7NwMuiSAkcRuc1t7VizQZfDpZtBx97NlkYWgjn6ylArt2xyhiyLKRhA==" + }, + "node_modules/@jsr/cliffy__table": { + "version": "1.0.0", + "resolved": "https://npm.jsr.io/~/11/@jsr/cliffy__table/1.0.0.tgz", + "integrity": "sha512-VoLxH0DjofHWPWKUc5N+oCwXB6O6e+carnhp23yJTa7qokBb+SCrTIABEgQdIe/p0bxgmZhz17xt2efaAxXvbQ==", + "dependencies": { + "@jsr/std__fmt": "^1.0.9" + } + }, + "node_modules/@jsr/std__assert": { + "version": "1.0.19", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__assert/1.0.19.tgz", + "integrity": "sha512-pEj6RPkGbqlgRmyKwATp4cUs6+ijxtdrv3bq8v1d2I2CEcMEyPaO8cVKro61wGRDH4cNg8Zx6haztvK/9m7gkA==", + "dependencies": { + "@jsr/std__internal": "^1.0.12" + } + }, + "node_modules/@jsr/std__bytes": { + "version": "1.0.6", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__bytes/1.0.6.tgz", + "integrity": "sha512-St6yKggjFGhxS52IFLJWvkchRFbAKg2Xh8UxA4S1EGz7GJ2Ui+ssDDldj/w2c8vCxvl6qgR0HaYbKeFJNqujmA==" + }, + "node_modules/@jsr/std__encoding": { + "version": "1.0.10", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__encoding/1.0.10.tgz", + "integrity": "sha512-WK2njnDTyKefroRNk2Ooq7GStp6Y0ccAvr4To+Z/zecRAGe7+OSvH9DbiaHpAKwEi2KQbmpWMOYsdNt+TsdmSw==" + }, + "node_modules/@jsr/std__fmt": { + "version": "1.0.9", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__fmt/1.0.9.tgz", + "integrity": "sha512-YFJJMozmORj2K91c5J9opWeh0VUwrd+Mwb7Pr0FkVCAKVLu2UhT4LyvJqWiyUT+eF+MdfqQ9F7RtQj4bXn9Smw==" + }, + "node_modules/@jsr/std__fs": { + "version": "1.0.23", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__fs/1.0.23.tgz", + "integrity": "sha512-e8jspB3M44E5YhWiLCTqibBBTwVmxQaHN06WvFa/elAKm5E/LfAe8Hj5XGNC8P7a0MIPASlNJsnF1bgO/g+aqg==", + "dependencies": { + "@jsr/std__internal": "^1.0.12", + "@jsr/std__path": "^1.1.4" + } + }, + "node_modules/@jsr/std__internal": { + "version": "1.0.12", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__internal/1.0.12.tgz", + "integrity": "sha512-6xReMW9p+paJgqoFRpOE2nogJFvzPfaLHLIlyADYjKMUcwDyjKZxryIbgcU+gxiTygn8yCjld1HoI0ET4/iZeA==" + }, + "node_modules/@jsr/std__io": { + "version": "0.225.3", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__io/0.225.3.tgz", + "integrity": "sha512-IDXY253ipW6FV34CJVxO+3ubfvSEEzw9N2W303KnLe9K/Y9+v/ID1dQYf9VsCCOFMpFtCmOLqzIZsRqv6yQnWw==", + "dependencies": { + "@jsr/std__bytes": "^1.0.6" + } + }, + "node_modules/@jsr/std__path": { + "version": "1.1.4", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__path/1.1.4.tgz", + "integrity": "sha512-SK4u9H6NVTfolhPdlvdYXfNFefy1W04AEHWJydryYbk+xqzNiVmr5o7TLJLJFqwHXuwMRhwrn+mcYeUfS0YFaA==", + "dependencies": { + "@jsr/std__internal": "^1.0.12" + } + }, + "node_modules/@jsr/std__regexp": { + "version": "1.0.1", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__regexp/1.0.1.tgz", + "integrity": "sha512-AnGeP//DHpPvhCWjI5dR4o013JhCQioD8yMF8drD7PWb0X4kvmO35hbZi+NZhfSolz4Ts2cpPzJY+DUpi2XE9A==" + }, + "node_modules/@jsr/std__semver": { + "version": "1.0.8", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__semver/1.0.8.tgz", + "integrity": "sha512-YhkykPU2Majz66e+rQbP0okYc7kKv+U32aguLPCXZZAL+vEVmBA+khHjPHhLBpWR073gzU3WHqGRgB7a/aXCjg==" + }, + "node_modules/@jsr/std__text": { + "version": "1.0.17", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__text/1.0.17.tgz", + "integrity": "sha512-oZsihl1bcTy1Ixzven8rin8kjChj1zDJWqgpS0oSMGCJDzyB365gtIfAvcMmji+M+FcIWo3goDXfHcFYt+k/kg==", + "dependencies": { + "@jsr/std__regexp": "^1.0.1" + } + }, + "node_modules/@std/encoding": { + "name": "@jsr/std__encoding", + "version": "1.0.10", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__encoding/1.0.10.tgz", + "integrity": "sha512-WK2njnDTyKefroRNk2Ooq7GStp6Y0ccAvr4To+Z/zecRAGe7+OSvH9DbiaHpAKwEi2KQbmpWMOYsdNt+TsdmSw==" + }, + "node_modules/@std/log": { + "name": "@jsr/std__log", + "version": "0.224.14", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__log/0.224.14.tgz", + "integrity": "sha512-EHT7E0plakyzk/gxMrwqUf3YGCCxN3Is25QrEh7toYA7qwj46R4qY7cIaDEKy8QqI5JHOFHwWXOClcPK6goIoQ==", + "dependencies": { + "@jsr/std__fmt": "^1.0.5", + "@jsr/std__fs": "^1.0.11", + "@jsr/std__io": "^0.225.2" + } + }, + "node_modules/@std/path": { + "name": "@jsr/std__path", + "version": "1.1.4", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__path/1.1.4.tgz", + "integrity": "sha512-SK4u9H6NVTfolhPdlvdYXfNFefy1W04AEHWJydryYbk+xqzNiVmr5o7TLJLJFqwHXuwMRhwrn+mcYeUfS0YFaA==", + "dependencies": { + "@jsr/std__internal": "^1.0.12" + } + }, + "node_modules/@std/yaml": { + "name": "@jsr/std__yaml", + "version": "1.0.10", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__yaml/1.0.10.tgz", + "integrity": "sha512-1WIM023Kvi48pvPE3UO5YcieambLgywUooLhAkkaObIcMB77F/YP2ILdl+vNfik+vElkl9znmuST9AZo8mbCpA==" + }, + "node_modules/@stoplight/ordered-object-literal": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.5.tgz", + "integrity": "sha512-COTiuCU5bgMUtbIFBuyyh2/yVVzlr5Om0v5utQDgBCuQUOPgU1DwoffkTfg4UBQOvByi5foF4w4T+H9CoRe5wg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/@stoplight/types": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.1.1.tgz", + "integrity": "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g==", + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.4", + "utility-types": "^3.10.0" + }, + "engines": { + "node": "^12.20 || >=14.13" + } + }, + "node_modules/@stoplight/yaml": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.3.0.tgz", + "integrity": "sha512-JZlVFE6/dYpP9tQmV0/ADfn32L9uFarHWxfcRhReKUnljz1ZiUM5zpX+PH8h5CJs6lao3TuFqnPm9IJJCEkE2w==", + "license": "Apache-2.0", + "dependencies": { + "@stoplight/ordered-object-literal": "^1.0.5", + "@stoplight/types": "^14.1.1", + "@stoplight/yaml-ast-parser": "0.0.50", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=10.8" + } + }, + "node_modules/@stoplight/yaml-ast-parser": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@stoplight/yaml-ast-parser/-/yaml-ast-parser-0.0.50.tgz", + "integrity": "sha512-Pb6M8TDO9DtSVla9yXSTAxmo9GVEouq5P40DWXdOie69bXogZTkgvopCq+yEvTMA0F6PEvdJmbtTV3ccIp11VQ==", + "license": "Apache-2.0" + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz", + "integrity": "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@types/diff": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/diff/-/diff-5.2.3.tgz", + "integrity": "sha512-K0Oqlrq3kQMaO2RhfrNQX5trmt+XLyom88zS0u84nnIcLvFnRUMRRHmrGny5GSM+kNO9IZLARsdQHDzkhAgmrQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", + "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@windmill-labs/shared-utils": { + "name": "@jsr/windmill-labs__shared-utils", + "version": "1.0.12", + "resolved": "https://npm.jsr.io/~/11/@jsr/windmill-labs__shared-utils/1.0.12.tgz", + "integrity": "sha512-bJOacyfxxNPwNTzA4AxCB5iGFop0h3mCgs+E9j3ZaJYDo1soblY16CebnQ56EPy/M3V344X/QoOFBORyRo1Mnw==" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/balanced-match": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.3.tgz", + "integrity": "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz", + "integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/devalue": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.3.tgz", + "integrity": "sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==", + "license": "MIT" + }, + "node_modules/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/esbuild": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" + } + }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "license": "MIT" + }, + "node_modules/esrap": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.3.tgz", + "integrity": "sha512-8fOS+GIGCQZl/ZIlhl59htOlms6U8NvX6ZYgYHpRU/b6tVSh3uHkOHZikl3D4cMbYM0JlpBe+p/BkZEi8J9XIQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/get-port": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/jszip": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.8.0.tgz", + "integrity": "sha512-cnpQrXvFSLdsR9KR5/x7zdf6c3m8IhZfZzSblFEHSqBaVwD2nvJ4CuCKLyvKvwBgZm08CgfSoiTBQLm5WW9hGw==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minimatch": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", + "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/sudo-prompt": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz", + "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT" + }, + "node_modules/svelte": { + "version": "5.53.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.53.2.tgz", + "integrity": "sha512-yGONuIrcl/BMmqbm6/52Q/NYzfkta7uVlos5NSzGTfNJTTFtPPzra6rAQoQIwAqupeM3s9uuTf5PvioeiCdg9g==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/estree": "^1.0.5", + "@types/trusted-types": "^2.0.7", + "acorn": "^8.12.1", + "aria-query": "^5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "devalue": "^5.6.3", + "esm-env": "^1.2.1", + "esrap": "^2.2.2", + "is-reference": "^3.0.3", + "locate-character": "^3.0.0", + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utility-types": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/windmill-parser-wasm-csharp": { + "version": "1.510.1", + "resolved": "https://registry.npmjs.org/windmill-parser-wasm-csharp/-/windmill-parser-wasm-csharp-1.510.1.tgz", + "integrity": "sha512-qm09YmnbeYHLwYn1jUnObVzPhYO9NZKMlIO7nlo7zPJBXqksgG5fK/KCtwGw9rChrnz+DsvM9wP5FhrwRLMtwQ==" + }, + "node_modules/windmill-parser-wasm-go": { + "version": "1.510.1", + "resolved": "https://registry.npmjs.org/windmill-parser-wasm-go/-/windmill-parser-wasm-go-1.510.1.tgz", + "integrity": "sha512-HOkk6LXK0wrwvkn+zjm3Gxo90HmyL6TYqmLo2yp8fZuppy7GOngT27zwYeBtwONiPyvDKskzoqPQoEfd8VuUsQ==" + }, + "node_modules/windmill-parser-wasm-java": { + "version": "1.510.1", + "resolved": "https://registry.npmjs.org/windmill-parser-wasm-java/-/windmill-parser-wasm-java-1.510.1.tgz", + "integrity": "sha512-Zle+JZT/ZwUArUVacUudYlS+CaHp2lSnkqD/IhWaRUG+gcv26VbERnrrHPonqXbVMS+eA9ElfXrFM5j0ukaXUw==" + }, + "node_modules/windmill-parser-wasm-nu": { + "version": "1.510.1", + "resolved": "https://registry.npmjs.org/windmill-parser-wasm-nu/-/windmill-parser-wasm-nu-1.510.1.tgz", + "integrity": "sha512-AJLFiUy6af+LpUe7CddDo4+JOmw3c0K/1iOWh8NdTwXcLDj90lL6089mdsVo1apyloLgrTbcuFDzZMXVGBgtCg==" + }, + "node_modules/windmill-parser-wasm-php": { + "version": "1.574.1", + "resolved": "https://registry.npmjs.org/windmill-parser-wasm-php/-/windmill-parser-wasm-php-1.574.1.tgz", + "integrity": "sha512-COyid6B1RYs+bpzUCInsA4HY/WZkpDLfkQ90+AqU/TVTpzYSbAC2JCbIwy0cRElBvlhI4bQ+9Wg6hSQKMpEkpA==" + }, + "node_modules/windmill-parser-wasm-py": { + "version": "1.628.3", + "resolved": "https://registry.npmjs.org/windmill-parser-wasm-py/-/windmill-parser-wasm-py-1.628.3.tgz", + "integrity": "sha512-TlluqknZpg8cZ+A3m6JFLPseY2PpKtDsxdj26fAnCUzKPtse8TxQR+n0dwC80rfW5TwdWSulvNGRDgcNuf7CTw==" + }, + "node_modules/windmill-parser-wasm-regex": { + "version": "1.639.0", + "resolved": "https://registry.npmjs.org/windmill-parser-wasm-regex/-/windmill-parser-wasm-regex-1.639.0.tgz", + "integrity": "sha512-qvYM4sYxB6M0xrqwBljS2fWqOMk6rp++60TRltJnzZDzVaWQrKjTGwNMmfepGAIWy1OGVKp0SCVERhe2P+O6tQ==" + }, + "node_modules/windmill-parser-wasm-ruby": { + "version": "1.526.1", + "resolved": "https://registry.npmjs.org/windmill-parser-wasm-ruby/-/windmill-parser-wasm-ruby-1.526.1.tgz", + "integrity": "sha512-rMBQA8s21wmL2kA5ztRs/ZgVA3ckxe9/NLjxl3iQPL0CX6DlvfaUH0O+AnhpXXDMyBs1Y1SZIhcnbnvsHZ3R8g==" + }, + "node_modules/windmill-parser-wasm-rust": { + "version": "1.558.1", + "resolved": "https://registry.npmjs.org/windmill-parser-wasm-rust/-/windmill-parser-wasm-rust-1.558.1.tgz", + "integrity": "sha512-21S7lm1KF8zO1187rbq14hzPHII2RdM2+D44MoAh1F6VoaScj+Puq0z5B1O/hwn/95R/a9jBlL2D8jbkXtlD1A==" + }, + "node_modules/windmill-parser-wasm-ts": { + "version": "1.623.1", + "resolved": "https://registry.npmjs.org/windmill-parser-wasm-ts/-/windmill-parser-wasm-ts-1.623.1.tgz", + "integrity": "sha512-FBwi/zXxjhZcCvi04oFdNivazru1ynIqSbafHSArfaaBWesBO3nye9UO/WXUlWZm5a7BExbU+3R/eVJrGaornw==" + }, + "node_modules/windmill-parser-wasm-yaml": { + "version": "1.593.0", + "resolved": "https://registry.npmjs.org/windmill-parser-wasm-yaml/-/windmill-parser-wasm-yaml-1.593.0.tgz", + "integrity": "sha512-Gyx4aR2jsJYuDrD3mCNTmz7LWOQQXPw5yKNCC1xRgUOPfjsD/tINAFfsBLwVOSmlQQcFZO+wHm4KtDtXOcnGVw==" + }, + "node_modules/windmill-yaml-validator": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/windmill-yaml-validator/-/windmill-yaml-validator-1.1.1.tgz", + "integrity": "sha512-CVgAwEoBdJhF39q2N012QffhlGPRIyIWd8gj7NnfG+/lMWgH2k5CBLtKIt6cPF8Bxz+6DGC3st1ARSsecDtbTg==", + "license": "Apache 2.0", + "dependencies": { + "@stoplight/yaml": "^4.3.0", + "ajv": "^8.17.1" + } + }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/zimmerframe": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", + "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", + "license": "MIT" + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/cli/package.json b/cli/package.json index 2c8df16d77..a07d43a1a4 100644 --- a/cli/package.json +++ b/cli/package.json @@ -13,23 +13,19 @@ "gen-client": "./gen_wm_client.sh && ./windmill-utils-internal/gen_wm_client.sh" }, "dependencies": { - "@ayonli/jsext": "^1.9.0", "@cliffy/ansi": "npm:@jsr/cliffy__ansi@1.0.0", "@cliffy/command": "npm:@jsr/cliffy__command@1.0.0", "@cliffy/prompt": "npm:@jsr/cliffy__prompt@1.0.0", "@cliffy/table": "npm:@jsr/cliffy__table@1.0.0", - "@std/encoding": "npm:@jsr/std__encoding@1.0.10", - "@std/log": "npm:@jsr/std__log@0.224.14", - "@std/path": "npm:@jsr/std__path@1.1.4", - "@std/yaml": "npm:@jsr/std__yaml@1.0.10", - "@windmill-labs/shared-utils": "npm:@jsr/windmill-labs__shared-utils@1.0.12", + "@windmill-labs/shared-utils": "^1.0.12", "diff": "^5.2.0", "esbuild": "0.24.2", - "svelte": "^5.45.2", "get-port": "7.1.0", "jszip": "3.8.0", "minimatch": "^10.0.0", "open": "^10.0.0", + "svelte": "^5.45.2", + "tar-stream": "^3.1.7", "windmill-parser-wasm-csharp": "*", "windmill-parser-wasm-go": "*", "windmill-parser-wasm-java": "*", @@ -47,8 +43,9 @@ }, "devDependencies": { "@types/diff": "^5.2.3", - "@types/ws": "^8.5.0", "@types/node": "^22.0.0", + "@types/tar-stream": "^3.1.4", + "@types/ws": "^8.5.0", "typescript": "^5.7.0" } } diff --git a/cli/src/commands/app/app.ts b/cli/src/commands/app/app.ts index febd55e918..3aa8305542 100644 --- a/cli/src/commands/app/app.ts +++ b/cli/src/commands/app/app.ts @@ -3,8 +3,8 @@ import { resolveWorkspace, validatePath } from "../../core/context.ts"; import { Command } from "@cliffy/command"; import { Table } from "@cliffy/table"; import { colors } from "@cliffy/ansi/colors"; -import * as log from "@std/log"; -import { SEPARATOR as SEP } from "@std/path"; +import * as log from "../../core/log.ts"; +import { sep as SEP } from "node:path"; import * as windmillUtils from "@windmill-labs/shared-utils"; import { yamlParseFile } from "../../utils/yaml.ts"; import * as wmill from "../../../gen/services.gen.ts"; @@ -185,7 +185,7 @@ export async function generatingPolicy( } } -async function list(opts: GlobalOptions & { includeDraftOnly?: boolean }) { +async function list(opts: GlobalOptions & { includeDraftOnly?: boolean; json?: boolean }) { const workspace = await resolveWorkspace(opts); await requireLogin(opts); @@ -206,12 +206,32 @@ async function list(opts: GlobalOptions & { includeDraftOnly?: boolean }) { } } - new Table() - .header(["path", "summary"]) - .padding(2) - .border(true) - .body(total.map((x) => [x.path, x.summary])) - .render(); + if (opts.json) { + console.log(JSON.stringify(total)); + } else { + new Table() + .header(["path", "summary"]) + .padding(2) + .border(true) + .body(total.map((x) => [x.path, x.summary])) + .render(); + } +} + +async function get(opts: GlobalOptions & { json?: boolean }, path: string) { + const workspace = await resolveWorkspace(opts); + await requireLogin(opts); + const a = await wmill.getAppByPath({ + workspace: workspace.workspaceId, + path, + }); + if (opts.json) { + console.log(JSON.stringify(a)); + } else { + console.log(colors.bold("Path:") + " " + a.path); + console.log(colors.bold("Summary:") + " " + (a.summary ?? "")); + console.log(colors.bold("Created by:") + " " + (a.created_by ?? "")); + } } async function push(opts: GlobalOptions, filePath: string, remotePath: string) { @@ -227,7 +247,15 @@ async function push(opts: GlobalOptions, filePath: string, remotePath: string) { const command = new Command() .description("app related commands") + .option("--json", "Output as JSON (for piping to jq)") .action(list as any) + .command("list", "list all apps") + .option("--json", "Output as JSON (for piping to jq)") + .action(list as any) + .command("get", "get an app's details") + .arguments("") + .option("--json", "Output as JSON (for piping to jq)") + .action(get as any) .command("push", "push a local app ") .arguments(" ") .action(push as any) diff --git a/cli/src/commands/app/app_metadata.ts b/cli/src/commands/app/app_metadata.ts index be0decfa32..3a28770225 100644 --- a/cli/src/commands/app/app_metadata.ts +++ b/cli/src/commands/app/app_metadata.ts @@ -1,10 +1,10 @@ import path from "node:path"; import { readFile, mkdir } from "node:fs/promises"; import { colors } from "@cliffy/ansi/colors"; -import * as log from "@std/log"; -import { SEPARATOR as SEP } from "@std/path"; +import * as log from "../../core/log.ts"; +import { sep as SEP } from "node:path"; import { yamlParseFile } from "../../utils/yaml.ts"; -import { stringify as yamlStringify } from "@std/yaml"; +import { stringify as yamlStringify } from "yaml"; import { GlobalOptions } from "../../types.ts"; import { checkifMetadataUptodate, diff --git a/cli/src/commands/app/bundle.ts b/cli/src/commands/app/bundle.ts index d610d743f2..998546d364 100644 --- a/cli/src/commands/app/bundle.ts +++ b/cli/src/commands/app/bundle.ts @@ -2,7 +2,7 @@ import * as fs from "node:fs"; import * as path from "node:path"; import process from "node:process"; import { spawn } from "node:child_process"; -import * as log from "@std/log"; +import * as log from "../../core/log.ts"; import { colors } from "@cliffy/ansi/colors"; import * as windmillUtils from "@windmill-labs/shared-utils"; export interface BundleOptions { diff --git a/cli/src/commands/app/dev.ts b/cli/src/commands/app/dev.ts index 7cc106baa4..ad704ee8dd 100644 --- a/cli/src/commands/app/dev.ts +++ b/cli/src/commands/app/dev.ts @@ -1,7 +1,7 @@ import { Command } from "@cliffy/command"; import { colors } from "@cliffy/ansi/colors"; -import * as log from "@std/log"; -import { SEPARATOR as SEP } from "@std/path"; +import * as log from "../../core/log.ts"; +import { sep as SEP } from "node:path"; import * as windmillUtils from "@windmill-labs/shared-utils"; import { yamlParseFile } from "../../utils/yaml.ts"; import * as getPort from "get-port"; diff --git a/cli/src/commands/app/generate_agents.ts b/cli/src/commands/app/generate_agents.ts index f8e34a34d9..eec86a8b19 100644 --- a/cli/src/commands/app/generate_agents.ts +++ b/cli/src/commands/app/generate_agents.ts @@ -5,7 +5,7 @@ import process from "node:process"; import { Command } from "@cliffy/command"; import { colors } from "@cliffy/ansi/colors"; -import * as log from "@std/log"; +import * as log from "../../core/log.ts"; import { yamlParseFile } from "../../utils/yaml.ts"; import { GlobalOptions } from "../../types.ts"; import { resolveWorkspace } from "../../core/context.ts"; diff --git a/cli/src/commands/app/lint.ts b/cli/src/commands/app/lint.ts index 932f714185..12014cc7d5 100644 --- a/cli/src/commands/app/lint.ts +++ b/cli/src/commands/app/lint.ts @@ -3,7 +3,7 @@ import * as path from "node:path"; import process from "node:process"; import { Command } from "@cliffy/command"; import { colors } from "@cliffy/ansi/colors"; -import * as log from "@std/log"; +import * as log from "../../core/log.ts"; import { yamlParseFile } from "../../utils/yaml.ts"; import { GlobalOptions } from "../../types.ts"; import { createBundle } from "./bundle.ts"; diff --git a/cli/src/commands/app/new.ts b/cli/src/commands/app/new.ts index fbbc2b4c36..0ae5065251 100644 --- a/cli/src/commands/app/new.ts +++ b/cli/src/commands/app/new.ts @@ -4,8 +4,8 @@ import { colors } from "@cliffy/ansi/colors"; import { Confirm } from "@cliffy/prompt/confirm"; import { Input } from "@cliffy/prompt/input"; import { Select } from "@cliffy/prompt/select"; -import * as log from "@std/log"; -import { stringify as yamlStringify } from "@std/yaml"; +import * as log from "../../core/log.ts"; +import { stringify as yamlStringify } from "yaml"; import { GlobalOptions } from "../../types.ts"; import { generateAgentsDocumentation, generateDatatablesDocumentation, yamlOptions } from "../sync/sync.ts"; import { resolveWorkspace } from "../../core/context.ts"; diff --git a/cli/src/commands/app/raw_apps.ts b/cli/src/commands/app/raw_apps.ts index 5c8189284a..6e71a3bf30 100644 --- a/cli/src/commands/app/raw_apps.ts +++ b/cli/src/commands/app/raw_apps.ts @@ -1,11 +1,11 @@ import { requireLogin } from "../../core/auth.ts"; import { resolveWorkspace, validatePath } from "../../core/context.ts"; import { colors } from "@cliffy/ansi/colors"; -import * as log from "@std/log"; -import { SEPARATOR as SEP } from "@std/path"; +import * as log from "../../core/log.ts"; +import { sep as SEP } from "node:path"; import * as windmillUtils from "@windmill-labs/shared-utils"; import { yamlParseFile } from "../../utils/yaml.ts"; -import { stringify as yamlStringify } from "@std/yaml"; +import { stringify as yamlStringify } from "yaml"; import * as wmill from "../../../gen/services.gen.ts"; import { Policy } from "../../../gen/types.gen.ts"; import path from "node:path"; diff --git a/cli/src/commands/dependencies/dependencies.ts b/cli/src/commands/dependencies/dependencies.ts index aa556974df..9cbcdf86df 100644 --- a/cli/src/commands/dependencies/dependencies.ts +++ b/cli/src/commands/dependencies/dependencies.ts @@ -3,7 +3,7 @@ import { resolveWorkspace } from "../../core/context.ts"; import { GlobalOptions } from "../../types.ts"; import { colors } from "@cliffy/ansi/colors"; import { Command } from "@cliffy/command"; -import * as log from "@std/log"; +import * as log from "../../core/log.ts"; import * as wmill from "../../../gen/services.gen.ts"; import fs from "node:fs"; import { workspaceDependenciesPathToLanguageAndFilename } from "../../utils/metadata.ts"; diff --git a/cli/src/commands/dev/dev.ts b/cli/src/commands/dev/dev.ts index e0a47f2c69..b2d86cc6ed 100644 --- a/cli/src/commands/dev/dev.ts +++ b/cli/src/commands/dev/dev.ts @@ -1,6 +1,6 @@ import { Command } from "@cliffy/command"; -import * as log from "@std/log"; -import { SEPARATOR as SEP } from "@std/path"; +import * as log from "../../core/log.ts"; +import { sep as SEP } from "node:path"; import { yamlParseFile } from "../../utils/yaml.ts"; import { WebSocket, WebSocketServer } from "ws"; diff --git a/cli/src/commands/flow/flow.ts b/cli/src/commands/flow/flow.ts index 218dc69e8c..eff742ee68 100644 --- a/cli/src/commands/flow/flow.ts +++ b/cli/src/commands/flow/flow.ts @@ -3,9 +3,9 @@ import { colors } from "@cliffy/ansi/colors"; import { Command } from "@cliffy/command"; import { Confirm } from "@cliffy/prompt/confirm"; import { Table } from "@cliffy/table"; -import * as log from "@std/log"; -import { SEPARATOR as SEP } from "@std/path"; -import { stringify as yamlStringify } from "@std/yaml"; +import * as log from "../../core/log.ts"; +import { sep as SEP } from "node:path"; +import { stringify as yamlStringify } from "yaml"; import { yamlParseFile } from "../../utils/yaml.ts"; import * as wmill from "../../../gen/services.gen.ts"; import { readFile } from "node:fs/promises"; @@ -113,7 +113,7 @@ async function push(opts: Options, filePath: string, remotePath: string) { } async function list( - opts: GlobalOptions & { showArchived?: boolean; includeDraftOnly?: boolean } + opts: GlobalOptions & { showArchived?: boolean; includeDraftOnly?: boolean; json?: boolean } ) { const workspace = await resolveWorkspace(opts); await requireLogin(opts); @@ -136,13 +136,35 @@ async function list( } } - new Table() - .header(["path", "summary", "edited by"]) - .padding(2) - .border(true) - .body(total.map((x) => [x.path, x.summary, x.edited_by])) - .render(); + if (opts.json) { + console.log(JSON.stringify(total)); + } else { + new Table() + .header(["path", "summary", "edited by"]) + .padding(2) + .border(true) + .body(total.map((x) => [x.path, x.summary, x.edited_by])) + .render(); + } } +async function get(opts: GlobalOptions & { json?: boolean }, path: string) { + const workspace = await resolveWorkspace(opts); + await requireLogin(opts); + const f = await wmill.getFlowByPath({ + workspace: workspace.workspaceId, + path, + }); + if (opts.json) { + console.log(JSON.stringify(f)); + } else { + console.log(colors.bold("Path:") + " " + f.path); + console.log(colors.bold("Summary:") + " " + (f.summary ?? "")); + console.log(colors.bold("Description:") + " " + (f.description ?? "")); + console.log(colors.bold("Edited by:") + " " + (f.edited_by ?? "")); + console.log(colors.bold("Edited at:") + " " + (f.edited_at ?? "")); + } +} + async function run( opts: GlobalOptions & { data?: string; @@ -375,8 +397,17 @@ export function bootstrap( const command = new Command() .description("flow related commands") - .option("--show-archived", "Enable archived scripts in output") + .option("--show-archived", "Enable archived flows in output") + .option("--json", "Output as JSON (for piping to jq)") .action(list as any) + .command("list", "list all flows") + .option("--show-archived", "Enable archived flows in output") + .option("--json", "Output as JSON (for piping to jq)") + .action(list as any) + .command("get", "get a flow's details") + .arguments("") + .option("--json", "Output as JSON (for piping to jq)") + .action(get as any) .command( "push", "push a local flow spec. This overrides any remote versions." @@ -423,10 +454,15 @@ const command = new Command() "Comma separated patterns to specify which file to NOT take into account." ) .action(generateLocks as any) - .command("bootstrap", "create a new empty flow") + .command("new", "create a new empty flow") .arguments("") - .option("--summary ", "script summary") - .option("--description ", "script description") + .option("--summary ", "flow summary") + .option("--description ", "flow description") + .action(bootstrap as any) + .command("bootstrap", "create a new empty flow (alias for new)") + .arguments("") + .option("--summary ", "flow summary") + .option("--description ", "flow description") .action(bootstrap as any); export default command; diff --git a/cli/src/commands/flow/flow_metadata.ts b/cli/src/commands/flow/flow_metadata.ts index 34771be346..cb2e5336e6 100644 --- a/cli/src/commands/flow/flow_metadata.ts +++ b/cli/src/commands/flow/flow_metadata.ts @@ -1,8 +1,8 @@ import { colors } from "@cliffy/ansi/colors"; -import * as log from "@std/log"; -import * as path from "@std/path"; -import { SEPARATOR as SEP } from "@std/path"; -import { stringify as yamlStringify } from "@std/yaml"; +import * as log from "../../core/log.ts"; +import * as path from "node:path"; +import { sep as SEP } from "node:path"; +import { stringify as yamlStringify } from "yaml"; import { yamlParseFile } from "../../utils/yaml.ts"; import { readFile } from "node:fs/promises"; import { GlobalOptions } from "../../types.ts"; diff --git a/cli/src/commands/folder/folder.ts b/cli/src/commands/folder/folder.ts index 1073b2626a..e3967d8ce3 100644 --- a/cli/src/commands/folder/folder.ts +++ b/cli/src/commands/folder/folder.ts @@ -1,10 +1,11 @@ -import { stat } from "node:fs/promises"; +import { stat, writeFile, mkdir } from "node:fs/promises"; +import { stringify as yamlStringify } from "yaml"; import { colors } from "@cliffy/ansi/colors"; import { Command } from "@cliffy/command"; import { Table } from "@cliffy/table"; -import * as log from "@std/log"; -import { SEPARATOR as SEP } from "@std/path"; +import * as log from "../../core/log.ts"; +import { sep as SEP } from "node:path"; import * as wmill from "../../../gen/services.gen.ts"; import { requireLogin } from "../../core/auth.ts"; @@ -18,7 +19,7 @@ export interface FolderFile { display_name: string | undefined; } -async function list(opts: GlobalOptions) { +async function list(opts: GlobalOptions & { json?: boolean }) { const workspace = await resolveWorkspace(opts); await requireLogin(opts); @@ -26,18 +27,60 @@ async function list(opts: GlobalOptions) { workspace: workspace.workspaceId, }); - new Table() - .header(["Name", "Owners", "Extra Perms"]) - .padding(2) - .border(true) - .body( - folders.map((x) => [ - x.name, - x.owners?.join(",") ?? "-", - JSON.stringify(x.extra_perms ?? {}), - ]) - ) - .render(); + if (opts.json) { + console.log(JSON.stringify(folders)); + } else { + new Table() + .header(["Name", "Owners", "Extra Perms"]) + .padding(2) + .border(true) + .body( + folders.map((x) => [ + x.name, + x.owners?.join(",") ?? "-", + JSON.stringify(x.extra_perms ?? {}), + ]) + ) + .render(); + } +} + +async function newFolder(opts: GlobalOptions, name: string) { + const dirPath = `f${SEP}${name}`; + const filePath = `${dirPath}${SEP}folder.meta.yaml`; + try { + await stat(filePath); + throw new Error("File already exists: " + filePath); + } catch (e: any) { + if (e.message?.startsWith("File already exists")) throw e; + } + const template: Omit = { + owners: [], + extra_perms: {}, + }; + await mkdir(dirPath, { recursive: true }); + await writeFile(filePath, yamlStringify(template as Record), { + flag: "wx", + encoding: "utf-8", + }); + log.info(colors.green(`Created ${filePath}`)); +} + +async function get(opts: GlobalOptions & { json?: boolean }, name: string) { + const workspace = await resolveWorkspace(opts); + await requireLogin(opts); + const f = await wmill.getFolder({ + workspace: workspace.workspaceId, + name, + }); + if (opts.json) { + console.log(JSON.stringify(f)); + } else { + console.log(colors.bold("Name:") + " " + f.name); + console.log(colors.bold("Summary:") + " " + (f.summary ?? "")); + console.log(colors.bold("Owners:") + " " + (f.owners?.join(", ") ?? "-")); + console.log(colors.bold("Extra Perms:") + " " + JSON.stringify(f.extra_perms ?? {})); + } } export async function pushFolder( @@ -126,7 +169,18 @@ async function push(opts: GlobalOptions, filePath: string, remotePath: string) { const command = new Command() .description("folder related commands") + .option("--json", "Output as JSON (for piping to jq)") .action(list as any) + .command("list", "list all folders") + .option("--json", "Output as JSON (for piping to jq)") + .action(list as any) + .command("get", "get a folder's details") + .arguments("") + .option("--json", "Output as JSON (for piping to jq)") + .action(get as any) + .command("new", "create a new folder locally") + .arguments("") + .action(newFolder as any) .command( "push", "push a local folder spec. This overrides any remote versions." diff --git a/cli/src/commands/gitsync-settings/pull.ts b/cli/src/commands/gitsync-settings/pull.ts index 7c58a45bbe..bea37743a4 100644 --- a/cli/src/commands/gitsync-settings/pull.ts +++ b/cli/src/commands/gitsync-settings/pull.ts @@ -1,12 +1,13 @@ import { writeFile } from "node:fs/promises"; import { colors } from "@cliffy/ansi/colors"; -import * as log from "@std/log"; -import { stringify as yamlStringify } from "@std/yaml"; +import * as log from "../../core/log.ts"; +import { stringify as yamlStringify } from "yaml"; import { GlobalOptions } from "../../types.ts"; import { requireLogin } from "../../core/auth.ts"; import { resolveWorkspace } from "../../core/context.ts"; import * as wmill from "../../../gen/services.gen.ts"; import { SyncOptions, readConfigFile, getEffectiveSettings, DEFAULT_SYNC_OPTIONS, getWmillYamlPath } from "../../core/conf.ts"; +import { yamlOptions } from "../sync/sync.ts"; import { deepEqual } from "../../utils/utils.ts"; import { getCurrentGitBranch, isGitRepository } from "../../utils/git.ts"; @@ -176,7 +177,7 @@ export async function pullGitSyncSettings( } // Write the new configuration - await writeFile("wmill.yaml", yamlStringify(updatedConfig), "utf-8"); + await writeFile("wmill.yaml", yamlStringify(updatedConfig, yamlOptions), "utf-8"); if (opts.jsonOutput) { console.log( @@ -372,7 +373,7 @@ export async function pullGitSyncSettings( } // Write updated configuration - await writeFile("wmill.yaml", yamlStringify(updatedConfig), "utf-8"); + await writeFile("wmill.yaml", yamlStringify(updatedConfig, yamlOptions), "utf-8"); if (opts.jsonOutput) { console.log( @@ -449,7 +450,7 @@ export async function pullGitSyncSettings( } // Write updated configuration - await writeFile("wmill.yaml", yamlStringify(updatedConfig), "utf-8"); + await writeFile("wmill.yaml", yamlStringify(updatedConfig, yamlOptions), "utf-8"); if (opts.jsonOutput) { console.log( diff --git a/cli/src/commands/gitsync-settings/push.ts b/cli/src/commands/gitsync-settings/push.ts index 62a3b2781a..0392aa8722 100644 --- a/cli/src/commands/gitsync-settings/push.ts +++ b/cli/src/commands/gitsync-settings/push.ts @@ -1,7 +1,7 @@ import process from "node:process"; import { colors } from "@cliffy/ansi/colors"; -import * as log from "@std/log"; +import * as log from "../../core/log.ts"; import { Confirm } from "@cliffy/prompt/confirm"; import { GlobalOptions } from "../../types.ts"; import { requireLogin } from "../../core/auth.ts"; diff --git a/cli/src/commands/gitsync-settings/utils.ts b/cli/src/commands/gitsync-settings/utils.ts index e2acd5e506..8d255d9f92 100644 --- a/cli/src/commands/gitsync-settings/utils.ts +++ b/cli/src/commands/gitsync-settings/utils.ts @@ -1,5 +1,5 @@ import { colors } from "@cliffy/ansi/colors"; -import * as log from "@std/log"; +import * as log from "../../core/log.ts"; import { deepEqual, selectRepository } from "../../utils/utils.ts"; import { SyncOptions, getEffectiveSettings, DEFAULT_SYNC_OPTIONS } from "../../core/conf.ts"; import { GitSyncRepository, GIT_SYNC_FIELDS } from "./types.ts"; diff --git a/cli/src/commands/hub/hub.ts b/cli/src/commands/hub/hub.ts index 51aa59d133..83ec15a5e3 100644 --- a/cli/src/commands/hub/hub.ts +++ b/cli/src/commands/hub/hub.ts @@ -1,5 +1,5 @@ import { Command } from "@cliffy/command"; -import * as log from "@std/log"; +import * as log from "../../core/log.ts"; import * as wmill from "../../../gen/services.gen.ts"; import { requireLogin } from "../../core/auth.ts"; diff --git a/cli/src/commands/init/init.ts b/cli/src/commands/init/init.ts index 81eadbe732..807f2fb31b 100644 --- a/cli/src/commands/init/init.ts +++ b/cli/src/commands/init/init.ts @@ -2,8 +2,8 @@ import { stat, writeFile, rm, mkdir } from "node:fs/promises"; import { colors } from "@cliffy/ansi/colors"; import { Command } from "@cliffy/command"; import { Confirm } from "@cliffy/prompt/confirm"; -import * as log from "@std/log"; -import { stringify as yamlStringify } from "@std/yaml"; +import * as log from "../../core/log.ts"; +import { stringify as yamlStringify } from "yaml"; import { GlobalOptions } from "../../types.ts"; import { readLockfile } from "../../utils/metadata.ts"; import { getActiveWorkspaceOrFallback } from "../workspace/workspace.ts"; diff --git a/cli/src/commands/instance/instance.ts b/cli/src/commands/instance/instance.ts index c6b848d379..6b22d49b27 100644 --- a/cli/src/commands/instance/instance.ts +++ b/cli/src/commands/instance/instance.ts @@ -6,9 +6,9 @@ import { Confirm } from "@cliffy/prompt/confirm"; import { Input } from "@cliffy/prompt/input"; import { Select } from "@cliffy/prompt/select"; import { Table } from "@cliffy/table"; -import * as log from "@std/log"; -import * as path from "@std/path"; -import { stringify as yamlStringify } from "@std/yaml"; +import * as log from "../../core/log.ts"; +import * as path from "node:path"; +import { stringify as yamlStringify } from "yaml"; import { setClient } from "../../core/client.ts"; import { yamlParseFile } from "../../utils/yaml.ts"; import * as wmill from "../../../gen/services.gen.ts"; diff --git a/cli/src/commands/jobs/jobs.ts b/cli/src/commands/jobs/jobs.ts index 083426479d..17a58d11f2 100644 --- a/cli/src/commands/jobs/jobs.ts +++ b/cli/src/commands/jobs/jobs.ts @@ -4,7 +4,7 @@ import { resolveWorkspace } from "../../core/context.ts"; import { Command } from "@cliffy/command"; import { colors } from "@cliffy/ansi/colors"; import { Confirm } from "@cliffy/prompt/confirm"; -import * as log from "@std/log"; +import * as log from "../../core/log.ts"; import { mergeConfigWithConfigFile } from "../../core/conf.ts"; import * as fs from "node:fs/promises"; import * as wmill from "../../../gen/services.gen.ts"; diff --git a/cli/src/commands/lint/lint.ts b/cli/src/commands/lint/lint.ts index 6ce244c7c0..62a491eee1 100644 --- a/cli/src/commands/lint/lint.ts +++ b/cli/src/commands/lint/lint.ts @@ -3,9 +3,9 @@ import process from "node:process"; import { colors } from "@cliffy/ansi/colors"; import { Command } from "@cliffy/command"; -import * as log from "@std/log"; -import * as path from "@std/path"; -import { SEPARATOR as SEP } from "@std/path"; +import * as log from "../../core/log.ts"; +import * as path from "node:path"; +import { sep as SEP } from "node:path"; import { yamlParseFile } from "../../utils/yaml.ts"; import { GlobalOptions } from "../../types.ts"; import { mergeConfigWithConfigFile } from "../../core/conf.ts"; diff --git a/cli/src/commands/queues/queues.ts b/cli/src/commands/queues/queues.ts index 4f9201a8ab..3f8a18772c 100644 --- a/cli/src/commands/queues/queues.ts +++ b/cli/src/commands/queues/queues.ts @@ -1,6 +1,6 @@ import { Command } from "@cliffy/command"; import { Table } from "@cliffy/table"; -import * as log from "@std/log"; +import * as log from "../../core/log.ts"; import * as wmill from "../../../gen/services.gen.ts"; import { pickInstance } from "../instance/instance.ts"; @@ -124,7 +124,7 @@ async function displayQueues(opts: GlobalOptions, workspace?: string) { table.body(body).render(); } catch (error) { - log.error("Failed to fetch queue metrics:", error); + log.error(`Failed to fetch queue metrics: ${error}`); } } else { log.info("No active instance found"); diff --git a/cli/src/commands/resource-type/resource-type.ts b/cli/src/commands/resource-type/resource-type.ts index 2a4a785ab2..2058c80923 100644 --- a/cli/src/commands/resource-type/resource-type.ts +++ b/cli/src/commands/resource-type/resource-type.ts @@ -1,7 +1,8 @@ import { writeFileSync } from "node:fs"; -import { stat } from "node:fs/promises"; +import { stat, writeFile } from "node:fs/promises"; import path from "node:path"; import process from "node:process"; +import { stringify as yamlStringify } from "yaml"; import { GlobalOptions, @@ -14,7 +15,7 @@ import { resolveWorkspace } from "../../core/context.ts"; import { colors } from "@cliffy/ansi/colors"; import { Command } from "@cliffy/command"; import { Table } from "@cliffy/table"; -import * as log from "@std/log"; +import * as log from "../../core/log.ts"; import * as wmill from "../../../gen/services.gen.ts"; import { ResourceType } from "../../../gen/types.gen.ts"; import { compileResourceTypeToTsType } from "../../utils/resource_types.ts"; @@ -85,14 +86,16 @@ async function push(opts: PushOptions, filePath: string, name: string) { log.info(colors.bold.underline.green("Resource pushed")); } -async function list(opts: GlobalOptions & { schema?: boolean }) { +async function list(opts: GlobalOptions & { schema?: boolean; json?: boolean }) { const workspace = await resolveWorkspace(opts); await requireLogin(opts); const res = await wmill.listResourceType({ workspace: workspace.workspaceId, }); - if (opts.schema) { + if (opts.json) { + console.log(JSON.stringify(res)); + } else if (opts.schema) { new Table() .header(["Workspace", "Name", "Schema"]) .padding(2) @@ -115,6 +118,44 @@ async function list(opts: GlobalOptions & { schema?: boolean }) { } } +async function newResourceType(opts: GlobalOptions, name: string) { + const filePath = name + ".resource-type.yaml"; + try { + await stat(filePath); + throw new Error("File already exists: " + filePath); + } catch (e: any) { + if (e.message?.startsWith("File already exists")) throw e; + } + const template: ResourceTypeFile = { + schema: {}, + description: "", + }; + await writeFile(filePath, yamlStringify(template as Record), { + flag: "wx", + encoding: "utf-8", + }); + log.info(colors.green(`Created ${filePath}`)); +} + +async function get(opts: GlobalOptions & { json?: boolean }, path: string) { + const workspace = await resolveWorkspace(opts); + await requireLogin(opts); + const rt = await wmill.getResourceType({ + workspace: workspace.workspaceId, + path, + }); + if (opts.json) { + console.log(JSON.stringify(rt)); + } else { + console.log(colors.bold("Name:") + " " + rt.name); + console.log(colors.bold("Description:") + " " + (rt.description ?? "")); + console.log(colors.bold("Workspace:") + " " + (rt.workspace_id ?? "Global")); + if (rt.schema) { + console.log(colors.bold("Schema:") + " " + JSON.stringify(rt.schema, null, 2)); + } + } +} + export async function generateRTNamespace(opts: GlobalOptions) { const workspace = await resolveWorkspace(opts); await requireLogin(opts); @@ -146,10 +187,19 @@ export async function generateRTNamespace(opts: GlobalOptions) { const command = new Command() .description("resource type related commands") - .action(() => log.info("2 actions available, list and push.")) + .option("--json", "Output as JSON (for piping to jq)") + .action(list as any) .command("list", "list all resource types") .option("--schema", "Show schema in the output") + .option("--json", "Output as JSON (for piping to jq)") .action(list as any) + .command("get", "get a resource type's details") + .arguments("") + .option("--json", "Output as JSON (for piping to jq)") + .action(get as any) + .command("new", "create a new resource type locally") + .arguments("") + .action(newResourceType as any) .command( "push", "push a local resource spec. This overrides any remote versions." diff --git a/cli/src/commands/resource/resource.ts b/cli/src/commands/resource/resource.ts index 1d16aac782..400130f9b8 100644 --- a/cli/src/commands/resource/resource.ts +++ b/cli/src/commands/resource/resource.ts @@ -1,4 +1,5 @@ -import { stat } from "node:fs/promises"; +import { stat, writeFile } from "node:fs/promises"; +import { stringify as yamlStringify } from "yaml"; import { GlobalOptions, @@ -11,8 +12,8 @@ import { resolveWorkspace, validatePath } from "../../core/context.ts"; import { Command } from "@cliffy/command"; import { Table } from "@cliffy/table"; import { colors } from "@cliffy/ansi/colors"; -import * as log from "@std/log"; -import { SEPARATOR as SEP } from "@std/path"; +import * as log from "../../core/log.ts"; +import { sep as SEP } from "node:path"; import * as wmill from "../../../gen/services.gen.ts"; import { Resource } from "../../../gen/types.gen.ts"; import { readInlinePathSync } from "../../utils/utils.ts"; @@ -131,7 +132,7 @@ async function push(opts: PushOptions, filePath: string, remotePath: string) { log.info(colors.bold.underline.green(`Resource ${remotePath} pushed`)); } -async function list(opts: GlobalOptions) { +async function list(opts: GlobalOptions & { json?: boolean }) { const workspace = await resolveWorkspace(opts); await requireLogin(opts); let page = 0; @@ -150,17 +151,73 @@ async function list(opts: GlobalOptions) { } } - new Table() - .header(["Path", "Resource Type"]) - .padding(2) - .border(true) - .body(total.map((x) => [x.path, x.resource_type])) - .render(); + if (opts.json) { + console.log(JSON.stringify(total)); + } else { + new Table() + .header(["Path", "Resource Type"]) + .padding(2) + .border(true) + .body(total.map((x) => [x.path, x.resource_type])) + .render(); + } +} + +async function newResource(opts: GlobalOptions, path: string) { + if (!validatePath(path)) { + return; + } + const filePath = path + ".resource.yaml"; + try { + await stat(filePath); + throw new Error("File already exists: " + filePath); + } catch (e: any) { + if (e.message?.startsWith("File already exists")) throw e; + // file doesn't exist, proceed + } + const template: ResourceFile = { + value: {}, + resource_type: "", + description: "", + }; + await writeFile(filePath, yamlStringify(template as Record), { + flag: "wx", + encoding: "utf-8", + }); + log.info(colors.green(`Created ${filePath}`)); +} + +async function get(opts: GlobalOptions & { json?: boolean }, path: string) { + const workspace = await resolveWorkspace(opts); + await requireLogin(opts); + const r = await wmill.getResource({ + workspace: workspace.workspaceId, + path, + }); + if (opts.json) { + console.log(JSON.stringify(r)); + } else { + console.log(colors.bold("Path:") + " " + r.path); + console.log(colors.bold("Resource Type:") + " " + (r.resource_type ?? "")); + console.log(colors.bold("Description:") + " " + (r.description ?? "")); + console.log(colors.bold("Value:") + " " + JSON.stringify(r.value, null, 2)); + } } const command = new Command() .description("resource related commands") + .option("--json", "Output as JSON (for piping to jq)") .action(list as any) + .command("list", "list all resources") + .option("--json", "Output as JSON (for piping to jq)") + .action(list as any) + .command("get", "get a resource's details") + .arguments("") + .option("--json", "Output as JSON (for piping to jq)") + .action(get as any) + .command("new", "create a new resource locally") + .arguments("") + .action(newResource as any) .command( "push", "push a local resource spec. This overrides any remote versions." diff --git a/cli/src/commands/schedule/schedule.ts b/cli/src/commands/schedule/schedule.ts index bd5192de66..c8582c5315 100644 --- a/cli/src/commands/schedule/schedule.ts +++ b/cli/src/commands/schedule/schedule.ts @@ -1,10 +1,11 @@ -import { stat } from "node:fs/promises"; +import { stat, writeFile } from "node:fs/promises"; +import { stringify as yamlStringify } from "yaml"; import { Command } from "@cliffy/command"; import { Table } from "@cliffy/table"; import { colors } from "@cliffy/ansi/colors"; -import * as log from "@std/log"; -import { SEPARATOR as SEP } from "@std/path"; +import * as log from "../../core/log.ts"; +import { sep as SEP } from "node:path"; import { requireLogin } from "../../core/auth.ts"; import { resolveWorkspace, validatePath } from "../../core/context.ts"; import * as wmill from "../../../gen/services.gen.ts"; @@ -27,7 +28,7 @@ export interface ScheduleFile { enabled: boolean; } -async function list(opts: GlobalOptions) { +async function list(opts: GlobalOptions & { json?: boolean }) { const workspace = await resolveWorkspace(opts); await requireLogin(opts); @@ -35,12 +36,62 @@ async function list(opts: GlobalOptions) { workspace: workspace.workspaceId, }); - new Table() - .header(["Path", "Schedule"]) - .padding(2) - .border(true) - .body(schedules.map((x) => [x.path, x.schedule])) - .render(); + if (opts.json) { + console.log(JSON.stringify(schedules)); + } else { + new Table() + .header(["Path", "Schedule"]) + .padding(2) + .border(true) + .body(schedules.map((x) => [x.path, x.schedule])) + .render(); + } +} + +async function newSchedule(opts: GlobalOptions, path: string) { + if (!validatePath(path)) { + return; + } + const filePath = path + ".schedule.yaml"; + try { + await stat(filePath); + throw new Error("File already exists: " + filePath); + } catch (e: any) { + if (e.message?.startsWith("File already exists")) throw e; + } + const template: ScheduleFile = { + schedule: "0 */6 * * *", + on_failure: "", + script_path: "", + args: {}, + timezone: "Etc/UTC", + is_flow: false, + enabled: false, + }; + await writeFile(filePath, yamlStringify(template as Record), { + flag: "wx", + encoding: "utf-8", + }); + log.info(colors.green(`Created ${filePath}`)); +} + +async function get(opts: GlobalOptions & { json?: boolean }, path: string) { + const workspace = await resolveWorkspace(opts); + await requireLogin(opts); + const s = await wmill.getSchedule({ + workspace: workspace.workspaceId, + path, + }); + if (opts.json) { + console.log(JSON.stringify(s)); + } else { + console.log(colors.bold("Path:") + " " + s.path); + console.log(colors.bold("Schedule:") + " " + s.schedule); + console.log(colors.bold("Timezone:") + " " + (s.timezone ?? "")); + console.log(colors.bold("Script Path:") + " " + (s.script_path ?? "")); + console.log(colors.bold("Is Flow:") + " " + (s.is_flow ? "true" : "false")); + console.log(colors.bold("Enabled:") + " " + (s.enabled ? "true" : "false")); + } } export async function pushSchedule( @@ -137,7 +188,18 @@ async function push(opts: GlobalOptions, filePath: string, remotePath: string) { const command = new Command() .description("schedule related commands") + .option("--json", "Output as JSON (for piping to jq)") .action(list as any) + .command("list", "list all schedules") + .option("--json", "Output as JSON (for piping to jq)") + .action(list as any) + .command("get", "get a schedule's details") + .arguments("") + .option("--json", "Output as JSON (for piping to jq)") + .action(get as any) + .command("new", "create a new schedule locally") + .arguments("") + .action(newSchedule as any) .command( "push", "push a local schedule spec. This overrides any remote versions." diff --git a/cli/src/commands/script/script.ts b/cli/src/commands/script/script.ts index ff91b11fb5..9c6f094b41 100644 --- a/cli/src/commands/script/script.ts +++ b/cli/src/commands/script/script.ts @@ -7,9 +7,9 @@ import { colors } from "@cliffy/ansi/colors"; import { Command } from "@cliffy/command"; import { Confirm } from "@cliffy/prompt/confirm"; import { Table } from "@cliffy/table"; -import * as log from "@std/log"; -import { SEPARATOR as SEP } from "@std/path"; -import { stringify as yamlStringify } from "@std/yaml"; +import * as log from "../../core/log.ts"; +import { sep as SEP } from "node:path"; +import { stringify as yamlStringify } from "yaml"; import { deepEqual } from "../../utils/utils.ts"; import * as wmill from "../../../gen/services.gen.ts"; import * as specificItems from "../../core/specific_items.ts"; @@ -48,7 +48,7 @@ import { } from "../../core/conf.ts"; import { SyncCodebase, listSyncCodebases } from "../../utils/codebase.ts"; import fs from "node:fs"; -import { type Tarball } from "@ayonli/jsext/archive"; +import { createTarBlob, type TarEntry } from "../../utils/tar.ts"; import { execSync } from "node:child_process"; import { NewScript, Script } from "../../../gen/types.gen.ts"; @@ -246,7 +246,7 @@ export async function handleFile( const codebase = language == "bun" ? findCodebase(path, codebases) : undefined; - let bundleContent: string | Tarball | undefined = undefined; + let bundleContent: string | Blob | undefined = undefined; let forceTar = false; if (codebase) { @@ -292,7 +292,6 @@ export async function handleFile( ); } if (outputFiles.length > 1) { - const archiveNpm = await import("@ayonli/jsext/archive"); log.info( `Found multiple output files for ${path}, creating a tarball... ${outputFiles .map((file) => file.path) @@ -300,54 +299,49 @@ export async function handleFile( ); forceTar = true; const startTime = performance.now(); - const tarball = new archiveNpm.Tarball(); const mainPath = path.split(SEP).pop()?.split(".")[0] + ".js"; - const content = + const mainContent = outputFiles.find((file) => file.path == "/" + mainPath)?.text ?? ""; - log.info(`Main content: ${content.length}chars`); - tarball.append(new File([content], "main.js", { type: "text/plain" })); + log.info(`Main content: ${mainContent.length}chars`); + const entries: TarEntry[] = [ + { name: "main.js", content: mainContent }, + ]; for (const file of outputFiles) { if (file.path == "/" + mainPath) { continue; } log.info(`Adding file: ${file.path.substring(1)}`); - - const fil = new File([file.contents as any], file.path.substring(1)); - tarball.append(fil); + entries.push({ name: file.path.substring(1), content: file.contents }); } + bundleContent = await createTarBlob(entries); const endTime = performance.now(); log.info( `Finished creating tarball for ${path}: ${( - tarball.size / 1024 + bundleContent.size / 1024 ).toFixed(0)}kB (${(endTime - startTime).toFixed(0)}ms)` ); - bundleContent = tarball; } else { if (Array.isArray(codebase.assets) && codebase.assets.length > 0) { - const archiveNpm = await import("@ayonli/jsext/archive"); log.info( `Using the following asset configuration for ${path}: ${JSON.stringify( codebase.assets )}` ); const startTime = performance.now(); - const tarball = new archiveNpm.Tarball(); - tarball.append( - new File([bundleContent], "main.js", { type: "text/plain" }) - ); + const entries: TarEntry[] = [ + { name: "main.js", content: bundleContent }, + ]; for (const asset of codebase.assets) { const data = fs.readFileSync(asset.from); - const blob = new Blob([data], { type: "text/plain" }); - const file = new File([blob], asset.to); - tarball.append(file); + entries.push({ name: asset.to, content: data }); } + bundleContent = await createTarBlob(entries); const endTime = performance.now(); log.info( `Finished creating tarball for ${path}: ${( - tarball.size / 1024 + bundleContent.size / 1024 ).toFixed(0)}kB (${(endTime - startTime).toFixed(0)}ms)` ); - bundleContent = tarball; } } } @@ -512,31 +506,8 @@ export async function handleFile( return false; } -async function streamToBlob(stream: ReadableStream): Promise { - // Create a reader from the stream - const reader = stream.getReader(); - const chunks = []; - - // Read the data from the stream - while (true) { - const { done, value } = await reader.read(); - - if (done) { - // If stream is finished, break the loop - break; - } - - // Push the chunk to the array - chunks.push(value); - } - - - const blob = new Blob(chunks as any); - return blob; -} - async function createScript( - bundleContent: string | Tarball | undefined, + bundleContent: string | Blob | undefined, workspaceId: string, body: NewScript, workspace: Workspace @@ -563,7 +534,7 @@ async function createScript( "file", typeof bundleContent == "string" ? bundleContent - : await streamToBlob(bundleContent.stream()) + : bundleContent ); const url = @@ -726,6 +697,7 @@ async function list( showArchived?: boolean; includeWithoutMain?: boolean; includeDraftOnly?: boolean; + json?: boolean; } ) { const workspace = await resolveWorkspace(opts); @@ -750,12 +722,16 @@ async function list( } } - new Table() - .header(["path", "summary", "language", "created by"]) - .padding(2) - .border(true) - .body(total.map((x) => [x.path, x.summary, x.language, x.created_by])) - .render(); + if (opts.json) { + console.log(JSON.stringify(total)); + } else { + new Table() + .header(["path", "summary", "language", "created by"]) + .padding(2) + .border(true) + .body(total.map((x) => [x.path, x.summary, x.language, x.created_by])) + .render(); + } } export async function resolve(input: string): Promise> { @@ -916,6 +892,26 @@ async function show(opts: GlobalOptions, path: string) { log.info(s.content); } +async function get(opts: GlobalOptions & { json?: boolean }, path: string) { + const workspace = await resolveWorkspace(opts); + await requireLogin(opts); + const s = await wmill.getScriptByPath({ + workspace: workspace.workspaceId, + path, + }); + if (opts.json) { + console.log(JSON.stringify(s)); + } else { + console.log(colors.bold("Path:") + " " + s.path); + console.log(colors.bold("Summary:") + " " + (s.summary ?? "")); + console.log(colors.bold("Description:") + " " + (s.description ?? "")); + console.log(colors.bold("Language:") + " " + s.language); + console.log(colors.bold("Kind:") + " " + (s.kind ?? "script")); + console.log(colors.bold("Created by:") + " " + (s.created_by ?? "")); + console.log(colors.bold("Created at:") + " " + (s.created_at ?? "")); + } +} + async function bootstrap( opts: GlobalOptions & { summary: string; description: string }, scriptPath: string, @@ -941,10 +937,15 @@ async function bootstrap( try { await stat(scriptCodeFileFullPath); + throw new Error("File already exists: " + scriptCodeFileFullPath); + } catch (e: any) { + if (e.message?.startsWith("File already exists")) throw e; + } + try { await stat(scriptMetadataFileFullPath); - throw new Error("File already exists in repository"); - } catch { - // file does not exist, we can continue + throw new Error("File already exists: " + scriptMetadataFileFullPath); + } catch (e: any) { + if (e.message?.startsWith("File already exists")) throw e; } const scriptMetadata = defaultScriptMetadata(); @@ -1155,38 +1156,34 @@ async function preview( // Handle multiple output files (create tarball) if (out.outputFiles.length > 1) { - const archiveNpm = await import("@ayonli/jsext/archive"); if (!opts.silent) { log.info(`Creating tarball for multiple output files...`); } - const tarball = new archiveNpm.Tarball(); const mainPath = filePath.split(SEP).pop()?.split(".")[0] + ".js"; const mainContent = out.outputFiles.find((file: OutputFile) => file.path == "/" + mainPath)?.text ?? ""; - tarball.append(new File([mainContent], "main.js", { type: "text/plain" })); + const entries: TarEntry[] = [ + { name: "main.js", content: mainContent }, + ]; for (const file of out.outputFiles) { if (file.path == "/" + mainPath) continue; - - const fil = new File([file.contents as any], file.path.substring(1)); - tarball.append(fil); + entries.push({ name: file.path.substring(1), content: file.contents }); } - bundledContent = await streamToBlob(tarball.stream()); + bundledContent = await createTarBlob(entries); isTar = true; } else if (Array.isArray(codebase.assets) && codebase.assets.length > 0) { // Handle assets - const archiveNpm = await import("@ayonli/jsext/archive"); if (!opts.silent) { log.info(`Adding assets to tarball...`); } - const tarball = new archiveNpm.Tarball(); - tarball.append(new File([bundledContent], "main.js", { type: "text/plain" })); + const entries: TarEntry[] = [ + { name: "main.js", content: bundledContent }, + ]; for (const asset of codebase.assets) { const data = fs.readFileSync(asset.from); - const blob = new Blob([data], { type: "text/plain" }); - const file = new File([blob], asset.to); - tarball.append(file); + entries.push({ name: asset.to, content: data }); } - bundledContent = await streamToBlob(tarball.stream()); + bundledContent = await createTarBlob(entries); isTar = true; } @@ -1290,6 +1287,11 @@ async function preview( const command = new Command() .description("script related commands") .option("--show-archived", "Enable archived scripts in output") + .option("--json", "Output as JSON (for piping to jq)") + .action(list as any) + .command("list", "list all scripts") + .option("--show-archived", "Enable archived scripts in output") + .option("--json", "Output as JSON (for piping to jq)") .action(list as any) .command( "push", @@ -1297,7 +1299,11 @@ const command = new Command() ) .arguments("") .action(push as any) - .command("show", "show a scripts content") + .command("get", "get a script's details") + .arguments("") + .option("--json", "Output as JSON (for piping to jq)") + .action(get as any) + .command("show", "show a script's content (alias for get)") .arguments("") .action(show as any) .command("run", "run a script by path") @@ -1325,7 +1331,12 @@ const command = new Command() "Do not output anything other than the final output. Useful for scripting." ) .action(preview as any) - .command("bootstrap", "create a new script") + .command("new", "create a new script") + .arguments(" ") + .option("--summary ", "script summary") + .option("--description ", "script description") + .action(bootstrap as any) + .command("bootstrap", "create a new script (alias for new)") .arguments(" ") .option("--summary ", "script summary") .option("--description ", "script description") diff --git a/cli/src/commands/sync/global.ts b/cli/src/commands/sync/global.ts index 859dab98b8..b30848942a 100644 --- a/cli/src/commands/sync/global.ts +++ b/cli/src/commands/sync/global.ts @@ -1,5 +1,5 @@ import { colors } from "@cliffy/ansi/colors"; -import * as log from "@std/log"; +import * as log from "../../core/log.ts"; let GLOBAL_VERSIONS: { remoteMajor: number | undefined; diff --git a/cli/src/commands/sync/pull.ts b/cli/src/commands/sync/pull.ts index 54ddfdfa15..2d72bc35cf 100644 --- a/cli/src/commands/sync/pull.ts +++ b/cli/src/commands/sync/pull.ts @@ -1,7 +1,7 @@ import { GlobalOptions } from "../../types.ts"; import { colors } from "@cliffy/ansi/colors"; import { Command } from "@cliffy/command"; -import * as log from "@std/log"; +import * as log from "../../core/log.ts"; import JSZip from "jszip"; import { Workspace } from "../workspace/workspace.ts"; import { getHeaders } from "../../utils/utils.ts"; diff --git a/cli/src/commands/sync/push.ts b/cli/src/commands/sync/push.ts index a62150d59e..e95fa048be 100644 --- a/cli/src/commands/sync/push.ts +++ b/cli/src/commands/sync/push.ts @@ -1,6 +1,6 @@ import { colors } from "@cliffy/ansi/colors"; import { Command } from "@cliffy/command"; -import * as log from "@std/log"; +import * as log from "../../core/log.ts"; import { GlobalOptions } from "../../types.ts"; function stub(_opts: GlobalOptions, _dir?: string) { diff --git a/cli/src/commands/sync/sync.ts b/cli/src/commands/sync/sync.ts index ca5c4ce6d2..bf2ecf7826 100644 --- a/cli/src/commands/sync/sync.ts +++ b/cli/src/commands/sync/sync.ts @@ -4,10 +4,10 @@ import { readFile, writeFile, readdir, stat, rm, copyFile, mkdir } from "node:fs import { colors } from "@cliffy/ansi/colors"; import { Command } from "@cliffy/command"; import { Confirm } from "@cliffy/prompt/confirm"; -import * as log from "@std/log"; -import * as path from "@std/path"; -import { SEPARATOR as SEP } from "@std/path"; -import { stringify as yamlStringify } from "@std/yaml"; +import * as log from "../../core/log.ts"; +import * as path from "node:path"; +import { sep as SEP } from "node:path"; +import { stringify as yamlStringify, type DocumentOptions, type SchemaOptions, type CreateNodeOptions, type ToStringOptions } from "yaml"; import JSZip from "jszip"; import { minimatch } from "minimatch"; import { yamlParseContent } from "../../utils/yaml.ts"; @@ -276,13 +276,12 @@ function prioritizeName(name: string): string { return name; } -export const yamlOptions = { - sortKeys: (a: any, b: any) => { - return prioritizeName(a).localeCompare(prioritizeName(b)); +export const yamlOptions: DocumentOptions & SchemaOptions & CreateNodeOptions & ToStringOptions = { + sortMapEntries: (a, b) => { + return prioritizeName(String(a.key)).localeCompare(prioritizeName(String(b.key))); }, - noCompatMode: true, - noRefs: true, - skipInvalid: true, + aliasDuplicateObjects: false, + singleQuote: true, }; export interface InlineScript { @@ -1338,17 +1337,19 @@ async function compareDynFSElement( continue; } if (!ignoreCodebaseChanges) { + const beforeCodebase = before?.codebase; + const afterCodebase = after?.codebase; if (before?.codebase != undefined) { delete before.codebase; m2[k] = yamlStringify(before, yamlOptions); } if (after?.codebase != undefined) { - if (before.codebase != after.codebase) { - codebaseChanges[k] = after.codebase; - } delete after.codebase; v = yamlStringify(after, yamlOptions); } + if (beforeCodebase != afterCodebase) { + codebaseChanges[k] = afterCodebase ?? beforeCodebase ?? ""; + } } if (skipMetadata) { continue; @@ -2214,7 +2215,7 @@ export async function push( `\nPush aborted: ${lockIssues.length} script(s) missing locks.`, ), ); - Deno.exit(1); + process.exit(1); } log.info(colors.green("All scripts have valid locks.")); } diff --git a/cli/src/commands/trigger/trigger.ts b/cli/src/commands/trigger/trigger.ts index 5e4c8e234a..be645a83fa 100644 --- a/cli/src/commands/trigger/trigger.ts +++ b/cli/src/commands/trigger/trigger.ts @@ -1,4 +1,5 @@ -import { stat } from "node:fs/promises"; +import { stat, writeFile } from "node:fs/promises"; +import { stringify as yamlStringify } from "yaml"; import * as wmill from "../../../gen/services.gen.ts"; import { @@ -18,8 +19,8 @@ import { import { Command } from "@cliffy/command"; import { Table } from "@cliffy/table"; import { colors } from "@cliffy/ansi/colors"; -import * as log from "@std/log"; -import { SEPARATOR as SEP } from "@std/path"; +import * as log from "../../core/log.ts"; +import { sep as SEP } from "node:path"; import { GlobalOptions, isSuperset, @@ -295,37 +296,192 @@ export async function pushNativeTrigger( } } -async function list(opts: GlobalOptions) { +const triggerTemplates: Record> = { + http: { + script_path: "", + is_flow: false, + route_path: "", + http_method: "get", + is_async: false, + requires_auth: true, + }, + websocket: { + script_path: "", + is_flow: false, + url: "", + enabled: false, + }, + kafka: { + script_path: "", + is_flow: false, + kafka_resource_path: "", + group_id: "", + topics: [], + enabled: false, + }, + nats: { + script_path: "", + is_flow: false, + nats_resource_path: "", + subjects: [], + enabled: false, + }, + postgres: { + script_path: "", + is_flow: false, + postgres_resource_path: "", + publication_name: "", + replication_slot_name: "", + enabled: false, + }, + mqtt: { + script_path: "", + is_flow: false, + mqtt_resource_path: "", + topics: [], + subscribe_qos: 0, + enabled: false, + }, + sqs: { + script_path: "", + is_flow: false, + sqs_resource_path: "", + queue_url: "", + enabled: false, + }, + gcp: { + script_path: "", + is_flow: false, + gcp_resource_path: "", + subscription_id: "", + topic_id: "", + enabled: false, + }, + email: { + script_path: "", + is_flow: false, + enabled: false, + }, +}; + +async function newTrigger(opts: GlobalOptions & { kind: string }, path: string) { + if (!validatePath(path)) { + return; + } + if (!opts.kind) { + throw new Error("--kind is required. Valid kinds: " + TRIGGER_TYPES.join(", ")); + } + if (!checkIfValidTrigger(opts.kind)) { + throw new Error("Invalid trigger kind: " + opts.kind + ". Valid kinds: " + TRIGGER_TYPES.join(", ")); + } + const kind: TriggerType = opts.kind; + const filePath = `${path}.${kind}_trigger.yaml`; + try { + await stat(filePath); + throw new Error("File already exists: " + filePath); + } catch (e: any) { + if (e.message?.startsWith("File already exists")) throw e; + } + const template = triggerTemplates[kind]; + await writeFile(filePath, yamlStringify(template), { + flag: "wx", + encoding: "utf-8", + }); + log.info(colors.green(`Created ${filePath}`)); +} + +async function get(opts: GlobalOptions & { json?: boolean; kind?: string }, path: string) { const workspace = await resolveWorkspace(opts); await requireLogin(opts); - const httpTriggers = await wmill.listHttpTriggers({ - workspace: workspace.workspaceId, - }); - const websocketTriggers = await wmill.listWebsocketTriggers({ - workspace: workspace.workspaceId, - }); - const kafkaTriggers = await wmill.listKafkaTriggers({ - workspace: workspace.workspaceId, - }); - const natsTriggers = await wmill.listNatsTriggers({ - workspace: workspace.workspaceId, - }); - const postgresTriggers = await wmill.listPostgresTriggers({ - workspace: workspace.workspaceId, - }); - const mqttTriggers = await wmill.listMqttTriggers({ - workspace: workspace.workspaceId, - }); - const sqsTriggers = await wmill.listSqsTriggers({ - workspace: workspace.workspaceId, - }); - const gcpTriggers = await wmill.listGcpTriggers({ - workspace: workspace.workspaceId, - }); - const emailTriggers = await wmill.listEmailTriggers({ - workspace: workspace.workspaceId, - }); + if (opts.kind) { + if (!checkIfValidTrigger(opts.kind)) { + throw new Error("Invalid trigger kind: " + opts.kind + ". Valid kinds: " + TRIGGER_TYPES.join(", ")); + } + const trigger = await getTrigger(opts.kind, workspace.workspaceId, path); + if (opts.json) { + console.log(JSON.stringify(trigger)); + } else { + console.log(colors.bold("Path:") + " " + (trigger as any).path); + console.log(colors.bold("Kind:") + " " + opts.kind); + console.log(colors.bold("Enabled:") + " " + ((trigger as any).enabled ?? "-")); + console.log(colors.bold("Script Path:") + " " + ((trigger as any).script_path ?? "")); + console.log(colors.bold("Is Flow:") + " " + ((trigger as any).is_flow ? "true" : "false")); + } + return; + } + + // Try all trigger types and collect matches + const matches: { kind: string; trigger: any }[] = []; + for (const kind of TRIGGER_TYPES) { + try { + const trigger = await getTrigger(kind, workspace.workspaceId, path); + matches.push({ kind, trigger }); + } catch { + // not found for this kind + } + } + + if (matches.length === 0) { + throw new Error("No trigger found at path: " + path); + } + + if (matches.length === 1) { + const { kind, trigger } = matches[0]; + if (opts.json) { + console.log(JSON.stringify(trigger)); + } else { + console.log(colors.bold("Path:") + " " + trigger.path); + console.log(colors.bold("Kind:") + " " + kind); + console.log(colors.bold("Enabled:") + " " + (trigger.enabled ?? "-")); + console.log(colors.bold("Script Path:") + " " + (trigger.script_path ?? "")); + console.log(colors.bold("Is Flow:") + " " + (trigger.is_flow ? "true" : "false")); + } + return; + } + + // Multiple matches — ask user to specify --kind + console.log("Multiple triggers found at path " + path + ":"); + for (const m of matches) { + console.log(" - " + m.kind); + } + console.log("Please specify --kind to select one."); +} + +async function listOrEmpty(fn: () => Promise): Promise { + try { + return await fn(); + } catch { + return []; + } +} + +async function list(opts: GlobalOptions & { json?: boolean }) { + const workspace = await resolveWorkspace(opts); + await requireLogin(opts); + + const ws = workspace.workspaceId; + const [ + httpTriggers, + websocketTriggers, + kafkaTriggers, + natsTriggers, + postgresTriggers, + mqttTriggers, + sqsTriggers, + gcpTriggers, + emailTriggers, + ] = await Promise.all([ + listOrEmpty(() => wmill.listHttpTriggers({ workspace: ws })), + listOrEmpty(() => wmill.listWebsocketTriggers({ workspace: ws })), + listOrEmpty(() => wmill.listKafkaTriggers({ workspace: ws })), + listOrEmpty(() => wmill.listNatsTriggers({ workspace: ws })), + listOrEmpty(() => wmill.listPostgresTriggers({ workspace: ws })), + listOrEmpty(() => wmill.listMqttTriggers({ workspace: ws })), + listOrEmpty(() => wmill.listSqsTriggers({ workspace: ws })), + listOrEmpty(() => wmill.listGcpTriggers({ workspace: ws })), + listOrEmpty(() => wmill.listEmailTriggers({ workspace: ws })), + ]); const triggers = [ ...httpTriggers.map((x) => ({ path: x.path, kind: "http" })), ...websocketTriggers.map((x) => ({ path: x.path, kind: "websocket" })), @@ -338,12 +494,16 @@ async function list(opts: GlobalOptions) { ...emailTriggers.map((x) => ({ path: x.path, kind: "email" })), ]; - new Table() - .header(["Path", "Kind"]) - .padding(2) - .border(true) - .body(triggers.map((x) => [x.path, x.kind])) - .render(); + if (opts.json) { + console.log(JSON.stringify(triggers)); + } else { + new Table() + .header(["Path", "Kind"]) + .padding(2) + .border(true) + .body(triggers.map((x) => [x.path, x.kind])) + .render(); + } } function checkIfValidTrigger(kind: string | undefined): kind is TriggerType { @@ -401,7 +561,20 @@ async function push(opts: GlobalOptions, filePath: string, remotePath: string) { const command = new Command() .description("trigger related commands") + .option("--json", "Output as JSON (for piping to jq)") .action(list as any) + .command("list", "list all triggers") + .option("--json", "Output as JSON (for piping to jq)") + .action(list as any) + .command("get", "get a trigger's details") + .arguments("") + .option("--json", "Output as JSON (for piping to jq)") + .option("--kind ", "Trigger kind (http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, email). Recommended for faster lookup") + .action(get as any) + .command("new", "create a new trigger locally") + .arguments("") + .option("--kind ", "Trigger kind (required: http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, email)") + .action(newTrigger as any) .command( "push", "push a local trigger spec. This overrides any remote versions." diff --git a/cli/src/commands/user/user.ts b/cli/src/commands/user/user.ts index f5d8891c9c..207958ecce 100644 --- a/cli/src/commands/user/user.ts +++ b/cli/src/commands/user/user.ts @@ -11,8 +11,8 @@ import { compareInstanceObjects, InstanceSyncOptions } from "../instance/instanc import { colors } from "@cliffy/ansi/colors"; import { Command } from "@cliffy/command"; import { Table } from "@cliffy/table"; -import * as log from "@std/log"; -import { stringify as yamlStringify } from "@std/yaml"; +import * as log from "../../core/log.ts"; +import { stringify as yamlStringify } from "yaml"; import { yamlParseFile } from "../../utils/yaml.ts"; import * as wmill from "../../../gen/services.gen.ts"; import { diff --git a/cli/src/commands/variable/variable.ts b/cli/src/commands/variable/variable.ts index 1fc831bf87..21b7a69eba 100644 --- a/cli/src/commands/variable/variable.ts +++ b/cli/src/commands/variable/variable.ts @@ -1,4 +1,5 @@ -import { stat } from "node:fs/promises"; +import { stat, writeFile } from "node:fs/promises"; +import { stringify as yamlStringify } from "yaml"; import { requireLogin } from "../../core/auth.ts"; import { resolveWorkspace, validatePath } from "../../core/context.ts"; @@ -12,13 +13,13 @@ import { Command } from "@cliffy/command"; import { Table } from "@cliffy/table"; import { colors } from "@cliffy/ansi/colors"; import { Confirm } from "@cliffy/prompt/confirm"; -import * as log from "@std/log"; -import { SEPARATOR as SEP } from "@std/path"; +import * as log from "../../core/log.ts"; +import { sep as SEP } from "node:path"; import * as wmill from "../../../gen/services.gen.ts"; import { ListableVariable } from "../../../gen/types.gen.ts"; -async function list(opts: GlobalOptions) { +async function list(opts: GlobalOptions & { json?: boolean }) { const workspace = await resolveWorkspace(opts); await requireLogin(opts); @@ -26,19 +27,64 @@ async function list(opts: GlobalOptions) { workspace: workspace.workspaceId, }); - new Table() - .header(["Path", "Is Secret", "Account", "Value"]) - .padding(2) - .border(true) - .body( - variables.map((x) => [ - x.path, - x.is_secret ? "true" : "false", - x.account ?? "-", - x.value ?? "-", - ]) - ) - .render(); + if (opts.json) { + console.log(JSON.stringify(variables)); + } else { + new Table() + .header(["Path", "Is Secret", "Account", "Value"]) + .padding(2) + .border(true) + .body( + variables.map((x) => [ + x.path, + x.is_secret ? "true" : "false", + x.account ?? "-", + x.value ?? "-", + ]) + ) + .render(); + } +} + +async function newVariable(opts: GlobalOptions, path: string) { + if (!validatePath(path)) { + return; + } + const filePath = path + ".variable.yaml"; + try { + await stat(filePath); + throw new Error("File already exists: " + filePath); + } catch (e: any) { + if (e.message?.startsWith("File already exists")) throw e; + } + const template: VariableFile = { + value: "", + is_secret: false, + description: "", + }; + await writeFile(filePath, yamlStringify(template as Record), { + flag: "wx", + encoding: "utf-8", + }); + log.info(colors.green(`Created ${filePath}`)); +} + +async function get(opts: GlobalOptions & { json?: boolean }, path: string) { + const workspace = await resolveWorkspace(opts); + await requireLogin(opts); + const v = await wmill.getVariable({ + workspace: workspace.workspaceId, + path, + }); + if (opts.json) { + console.log(JSON.stringify(v)); + } else { + console.log(colors.bold("Path:") + " " + v.path); + console.log(colors.bold("Value:") + " " + (v.value ?? "-")); + console.log(colors.bold("Is Secret:") + " " + (v.is_secret ? "true" : "false")); + console.log(colors.bold("Description:") + " " + (v.description ?? "")); + console.log(colors.bold("Account:") + " " + (v.account ?? "-")); + } } export interface VariableFile { @@ -178,7 +224,18 @@ async function add( const command = new Command() .description("variable related commands") + .option("--json", "Output as JSON (for piping to jq)") .action(list as any) + .command("list", "list all variables") + .option("--json", "Output as JSON (for piping to jq)") + .action(list as any) + .command("get", "get a variable's details") + .arguments("") + .option("--json", "Output as JSON (for piping to jq)") + .action(get as any) + .command("new", "create a new variable locally") + .arguments("") + .action(newVariable as any) .command( "push", "Push a local variable spec. This overrides any remote versions." diff --git a/cli/src/commands/worker-groups/worker-groups.ts b/cli/src/commands/worker-groups/worker-groups.ts index a49769b706..30c14249ad 100644 --- a/cli/src/commands/worker-groups/worker-groups.ts +++ b/cli/src/commands/worker-groups/worker-groups.ts @@ -1,7 +1,7 @@ import { Command } from "@cliffy/command"; import { Table } from "@cliffy/table"; import { Confirm } from "@cliffy/prompt/confirm"; -import * as log from "@std/log"; +import * as log from "../../core/log.ts"; import { setClient } from "../../core/client.ts"; import { allInstances, getActiveInstance, InstanceSyncOptions, pickInstance } from "../instance/instance.ts"; import * as wmill from "../../../gen/services.gen.ts"; diff --git a/cli/src/commands/workers/workers.ts b/cli/src/commands/workers/workers.ts index 70decb109a..7ea5d074ab 100644 --- a/cli/src/commands/workers/workers.ts +++ b/cli/src/commands/workers/workers.ts @@ -1,6 +1,6 @@ import { Command } from "@cliffy/command"; import { Table } from "@cliffy/table"; -import * as log from "@std/log"; +import * as log from "../../core/log.ts"; import * as wmill from "../../../gen/services.gen.ts"; import { pickInstance } from "../instance/instance.ts"; diff --git a/cli/src/commands/workspace/fork.ts b/cli/src/commands/workspace/fork.ts index 19091d6b95..619f29fa2c 100644 --- a/cli/src/commands/workspace/fork.ts +++ b/cli/src/commands/workspace/fork.ts @@ -1,7 +1,7 @@ import { GlobalOptions } from "../../types.ts"; import { colors } from "@cliffy/ansi/colors"; import { Input } from "@cliffy/prompt/input"; -import * as log from "@std/log"; +import * as log from "../../core/log.ts"; import { setClient } from "../../core/client.ts"; import { allWorkspaces, list, removeWorkspace } from "./workspace.ts"; import * as wmill from "../../../gen/services.gen.ts"; diff --git a/cli/src/commands/workspace/workspace.ts b/cli/src/commands/workspace/workspace.ts index b7464e7cfd..b70469073c 100644 --- a/cli/src/commands/workspace/workspace.ts +++ b/cli/src/commands/workspace/workspace.ts @@ -11,7 +11,7 @@ import { Command } from "@cliffy/command"; import { Confirm } from "@cliffy/prompt/confirm"; import { Input } from "@cliffy/prompt/input"; import { Table } from "@cliffy/table"; -import * as log from "@std/log"; +import * as log from "../../core/log.ts"; import { setClient } from "../../core/client.ts"; import { requireLogin } from "../../core/auth.ts"; import { createWorkspaceFork, deleteWorkspaceFork } from "./fork.ts"; @@ -518,7 +518,7 @@ async function bind( } // Write back the updated config - const { stringify: yamlStringify } = await import("@std/yaml"); + const { stringify: yamlStringify } = await import("yaml"); try { await writeFile("wmill.yaml", yamlStringify(config), "utf-8"); } catch (error) { diff --git a/cli/src/core/auth.ts b/cli/src/core/auth.ts index 311fe16ea7..0be7571320 100644 --- a/cli/src/core/auth.ts +++ b/cli/src/core/auth.ts @@ -1,5 +1,5 @@ import { colors } from "@cliffy/ansi/colors"; -import * as log from "@std/log"; +import * as log from "./log.ts"; import { setClient } from "./client.ts"; import * as wmill from "../../gen/services.gen.ts"; import { GlobalUserInfo } from "../../gen/types.gen.ts"; diff --git a/cli/src/core/branch-profiles.ts b/cli/src/core/branch-profiles.ts index 8b1c52f9d5..80c72b0705 100644 --- a/cli/src/core/branch-profiles.ts +++ b/cli/src/core/branch-profiles.ts @@ -1,4 +1,4 @@ -import * as log from "@std/log"; +import * as log from "./log.ts"; import { readFile, writeFile } from "node:fs/promises"; import { getStore } from "./store.ts"; diff --git a/cli/src/core/conf.ts b/cli/src/core/conf.ts index 775f71befb..22acd536f0 100644 --- a/cli/src/core/conf.ts +++ b/cli/src/core/conf.ts @@ -1,7 +1,7 @@ -import * as log from "@std/log"; +import * as log from "./log.ts"; import { yamlParseFile } from "../utils/yaml.ts"; import { Confirm } from "@cliffy/prompt/confirm"; -import { stringify as yamlStringify } from "@std/yaml"; +import { stringify as yamlStringify } from "yaml"; import { getCurrentGitBranch, getOriginalBranchForWorkspaceForks, @@ -196,7 +196,7 @@ export async function readConfigFile(): Promise { if (!wmillYamlPath) { log.warn( - "No wmill.yaml found. Use 'wmill init' to bootstrap it. Using 'bun' as default typescript runtime." + "No wmill.yaml found. Use 'wmill init' to bootstrap it." ); return {}; } diff --git a/cli/src/core/context.ts b/cli/src/core/context.ts index 0e71628599..86307646bf 100644 --- a/cli/src/core/context.ts +++ b/cli/src/core/context.ts @@ -1,5 +1,5 @@ import { colors } from "@cliffy/ansi/colors"; -import * as log from "@std/log"; +import * as log from "./log.ts"; import { Select } from "@cliffy/prompt/select"; import { Confirm } from "@cliffy/prompt/confirm"; import { Input } from "@cliffy/prompt/input"; @@ -459,11 +459,12 @@ export async function resolveWorkspace( // forked workspace, that we detect through the branch name (only when not using branchOverride) const res = await tryResolveWorkspace(opts); if (!res.isError) { + const workspace = (res as { isError: false; value: Workspace }).value; if (branchOverride || !branch || !branch.startsWith(WM_FORK_PREFIX)) { - return res.value; + return workspace; } else { log.info( - `Found an active workspace \`${res.value.name}\` but the branch name indicates this is a forked workspace. Ignoring active workspace and trying to resolve the correct workspace from the branch name \`${branch}\`` + `Found an active workspace \`${workspace.name}\` but the branch name indicates this is a forked workspace. Ignoring active workspace and trying to resolve the correct workspace from the branch name \`${branch}\`` ); } } @@ -486,13 +487,41 @@ export async function resolveWorkspace( } } - // Fall back to active workspace (lowest priority) + // Fall back to active workspace const activeWorkspace = await getActiveWorkspace(opts); if (activeWorkspace) { (opts as any).__secret_workspace = activeWorkspace; return activeWorkspace; } + // Last resort: auto-configure from Windmill environment variables + // (set by the worker for bash/script execution) + const envWorkspace = process.env["WM_WORKSPACE"]; + const envToken = process.env["WM_TOKEN"]; + const envBaseUrl = + process.env["BASE_INTERNAL_URL"] ?? process.env["BASE_URL"]; + + if (envWorkspace && envToken && envBaseUrl) { + let normalizedBaseUrl: string; + try { + normalizedBaseUrl = new URL(envBaseUrl).toString(); + } catch { + log.info(colors.red(`Invalid BASE_INTERNAL_URL: ${envBaseUrl}`)); + return process.exit(-1); + } + log.debug( + `Using workspace from environment variables: ${envWorkspace} on ${normalizedBaseUrl}` + ); + const ws: Workspace = { + name: envWorkspace, + workspaceId: envWorkspace, + remote: normalizedBaseUrl, + token: envToken, + }; + (opts as any).__secret_workspace = ws; + return ws; + } + // If everything failed, show error log.info(colors.red.bold("No workspace given and no default set.")); return process.exit(-1); @@ -532,7 +561,8 @@ export async function tryResolveVersion( const workspaceRes = await tryResolveWorkspace(opts); if (workspaceRes.isError) return undefined; - const version = await fetchVersion(workspaceRes.value.remote); + const workspace = (workspaceRes as { isError: false; value: Workspace }).value; + const version = await fetchVersion(workspace.remote); try { return Number.parseInt( diff --git a/cli/src/core/log.ts b/cli/src/core/log.ts new file mode 100644 index 0000000000..d7bed9a0d4 --- /dev/null +++ b/cli/src/core/log.ts @@ -0,0 +1,24 @@ +let logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR" = "INFO"; + +const levels = { DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3 }; + +export function setup(level: "DEBUG" | "INFO" | "WARN" | "ERROR") { + logLevel = level; +} + +export function debug(msg: unknown) { + if (levels[logLevel] <= levels.DEBUG) + console.log(`\x1b[90m${String(msg)}\x1b[39m`); +} + +export function info(msg: unknown) { + console.log(`\x1b[34m${String(msg)}\x1b[39m`); +} + +export function warn(msg: unknown) { + console.log(`\x1b[33m${String(msg)}\x1b[39m`); +} + +export function error(msg: unknown) { + console.log(`\x1b[31m${String(msg)}\x1b[39m`); +} diff --git a/cli/src/core/login.ts b/cli/src/core/login.ts index c492347c0f..516b3fb4c8 100644 --- a/cli/src/core/login.ts +++ b/cli/src/core/login.ts @@ -1,7 +1,7 @@ import { GlobalOptions } from "../types.ts"; import { colors } from "@cliffy/ansi/colors"; import * as getPort from "get-port"; -import * as log from "@std/log"; +import * as log from "./log.ts"; import * as open from "open"; import { Secret } from "@cliffy/prompt/secret"; import { Select } from "@cliffy/prompt/select"; diff --git a/cli/src/core/settings.ts b/cli/src/core/settings.ts index 7075ba2edc..3ffddc4837 100644 --- a/cli/src/core/settings.ts +++ b/cli/src/core/settings.ts @@ -2,9 +2,9 @@ import process from "node:process"; import { writeFile } from "node:fs/promises"; import { colors } from "@cliffy/ansi/colors"; import { Confirm } from "@cliffy/prompt/confirm"; -import * as log from "@std/log"; +import * as log from "./log.ts"; import { yamlParseFile } from "../utils/yaml.ts"; -import { stringify as yamlStringify } from "@std/yaml"; +import { stringify as yamlStringify } from "yaml"; import * as wmill from "../../gen/services.gen.ts"; import { AIConfig, Config, GlobalSetting } from "../../gen/types.gen.ts"; import { compareInstanceObjects, InstanceSyncOptions } from "../commands/instance/instance.ts"; diff --git a/cli/src/guidance/skills.ts b/cli/src/guidance/skills.ts index 19f455fadb..88f8cbb842 100644 --- a/cli/src/guidance/skills.ts +++ b/cli/src/guidance/skills.ts @@ -4557,9 +4557,16 @@ Current version: 1.624.0 app related commands +**Options:** +- \`--json\` - Output as JSON (for piping to jq) + **Subcommands:** -- \`app push \` - push a local app +- \`app list\` - list all apps + - \`--json\` - Output as JSON (for piping to jq) +- \`app get \` - get an app's details + - \`--json\` - Output as JSON (for piping to jq) +- \`app push \` - push a local app - \`app dev [app_folder:string]\` - Start a development server for building apps with live reload and hot module replacement - \`--port \` - Port to run the dev server on (will find next available port if occupied) - \`--host \` - Host to bind the dev server to @@ -4596,10 +4603,16 @@ Launch a dev server that will spawn a webserver with HMR flow related commands **Options:** -- \`--show-archived\` - Enable archived scripts in output +- \`--show-archived\` - Enable archived flows in output +- \`--json\` - Output as JSON (for piping to jq) **Subcommands:** +- \`flow list\` - list all flows + - \`--show-archived\` - Enable archived flows in output + - \`--json\` - Output as JSON (for piping to jq) +- \`flow get \` - get a flow's details + - \`--json\` - Output as JSON (for piping to jq) - \`flow push \` - push a local flow spec. This overrides any remote versions. - \`flow run \` - run a flow by path. - \`-d --data \` - Inputs specified as a JSON string or a file using @ or stdin using @-. @@ -4611,16 +4624,27 @@ flow related commands - \`--yes\` - Skip confirmation prompt - \`-i --includes \` - Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string) - \`-e --excludes \` - Comma separated patterns to specify which file to NOT take into account. -- \`flow bootstrap \` - create a new empty flow - - \`--summary \` - script summary - - \`--description \` - script description +- \`flow new \` - create a new empty flow + - \`--summary \` - flow summary + - \`--description \` - flow description +- \`flow bootstrap \` - create a new empty flow (alias for new) + - \`--summary \` - flow summary + - \`--description \` - flow description ### folder folder related commands +**Options:** +- \`--json\` - Output as JSON (for piping to jq) + **Subcommands:** +- \`folder list\` - list all folders + - \`--json\` - Output as JSON (for piping to jq) +- \`folder get \` - get a folder's details + - \`--json\` - Output as JSON (for piping to jq) +- \`folder new \` - create a new folder locally - \`folder push \` - push a local folder spec. This overrides any remote versions. ### gitsync-settings @@ -4731,18 +4755,33 @@ List all queues with their metrics resource related commands +**Options:** +- \`--json\` - Output as JSON (for piping to jq) + **Subcommands:** +- \`resource list\` - list all resources + - \`--json\` - Output as JSON (for piping to jq) +- \`resource get \` - get a resource's details + - \`--json\` - Output as JSON (for piping to jq) +- \`resource new \` - create a new resource locally - \`resource push \` - push a local resource spec. This overrides any remote versions. ### resource-type resource type related commands +**Options:** +- \`--json\` - Output as JSON (for piping to jq) + **Subcommands:** - \`resource-type list\` - list all resource types - \`--schema\` - Show schema in the output + - \`--json\` - Output as JSON (for piping to jq) +- \`resource-type get \` - get a resource type's details + - \`--json\` - Output as JSON (for piping to jq) +- \`resource-type new \` - create a new resource type locally - \`resource-type push \` - push a local resource spec. This overrides any remote versions. - \`resource-type generate-namespace\` - Create a TypeScript definition file with the RT namespace generated from the resource types @@ -4750,8 +4789,16 @@ resource type related commands schedule related commands +**Options:** +- \`--json\` - Output as JSON (for piping to jq) + **Subcommands:** +- \`schedule list\` - list all schedules + - \`--json\` - Output as JSON (for piping to jq) +- \`schedule get \` - get a schedule's details + - \`--json\` - Output as JSON (for piping to jq) +- \`schedule new \` - create a new schedule locally - \`schedule push \` - push a local schedule spec. This overrides any remote versions. ### script @@ -4760,21 +4807,30 @@ script related commands **Options:** - \`--show-archived\` - Enable archived scripts in output +- \`--json\` - Output as JSON (for piping to jq) **Subcommands:** -- \`script push \` - push a local script spec. This overrides any remote versions. Use the script file (.ts, .js, .py, .sh -- \`script show \` - show a scripts content +- \`script list\` - list all scripts + - \`--show-archived\` - Enable archived scripts in output + - \`--json\` - Output as JSON (for piping to jq) +- \`script get \` - get a script's details + - \`--json\` - Output as JSON (for piping to jq) +- \`script show \` - show a script's content (alias for get) +- \`script push \` - push a local script spec. This overrides any remote versions. Use the script file (.ts, .js, .py, .sh) - \`script run \` - run a script by path - \`-d --data \` - Inputs specified as a JSON string or a file using @ or stdin using @-. - \`-s --silent\` - Do not output anything other then the final output. Useful for scripting. - \`script preview \` - preview a local script without deploying it. Supports both regular and codebase scripts. - \`-d --data \` - Inputs specified as a JSON string or a file using @ or stdin using @-. - \`-s --silent\` - Do not output anything other than the final output. Useful for scripting. -- \`script bootstrap \` - create a new script +- \`script new \` - create a new script - \`--summary \` - script summary - \`--description \` - script description -- \`script generate-metadata [script:file]\` - re-generate the metadata file updating the lock and the script schema (for flows, use \`wmill flow generate-locks\` +- \`script bootstrap \` - create a new script (alias for new) + - \`--summary \` - script summary + - \`--description \` - script description +- \`script generate-metadata [script:file]\` - re-generate the metadata file updating the lock and the script schema (for flows, use \`wmill flow generate-locks\`) - \`--yes\` - Skip confirmation prompt - \`--dry-run\` - Perform a dry run without making changes - \`--lock-only\` - re-generate only the lock @@ -4852,8 +4908,18 @@ sync local with a remote workspaces or the opposite (push or pull) trigger related commands +**Options:** +- \`--json\` - Output as JSON (for piping to jq) + **Subcommands:** +- \`trigger list\` - list all triggers + - \`--json\` - Output as JSON (for piping to jq) +- \`trigger get \` - get a trigger's details + - \`--json\` - Output as JSON (for piping to jq) + - \`--kind \` - Trigger kind (http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, email) +- \`trigger new \` - create a new trigger locally + - \`--kind \` - Trigger kind (required: http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, email) - \`trigger push \` - push a local trigger spec. This overrides any remote versions. ### user @@ -4875,8 +4941,16 @@ user related commands variable related commands +**Options:** +- \`--json\` - Output as JSON (for piping to jq) + **Subcommands:** +- \`variable list\` - list all variables + - \`--json\` - Output as JSON (for piping to jq) +- \`variable get \` - get a variable's details + - \`--json\` - Output as JSON (for piping to jq) +- \`variable new \` - create a new variable locally - \`variable push \` - Push a local variable spec. This overrides any remote versions. - \`--plain-secrets\` - Push secrets as plain text - \`variable add \` - Create a new variable on the remote. This will update the variable if it already exists. diff --git a/cli/src/main.ts b/cli/src/main.ts index e7ace077c6..cfd366081a 100644 --- a/cli/src/main.ts +++ b/cli/src/main.ts @@ -1,7 +1,7 @@ import { Command } from "@cliffy/command"; import { CompletionsCommand } from "@cliffy/command/completions"; import { UpgradeCommand } from "@cliffy/command/upgrade"; -import * as log from "@std/log"; +import * as log from "./core/log.ts"; import { realpathSync } from "node:fs"; import { fileURLToPath } from "node:url"; @@ -28,7 +28,7 @@ import lint from "./commands/lint/lint.ts"; import dev from "./commands/dev/dev.ts"; import { GlobalOptions } from "./types.ts"; import { OpenAPI } from "../gen/index.ts"; -import { getHeaders, getIsWin } from "./utils/utils.ts"; +import { getHeaders } from "./utils/utils.ts"; import { setShowDiffs } from "./core/conf.ts"; import { NpmProvider } from "./utils/upgrade.ts"; import { pull as hubPull } from "./commands/hub/hub.ts"; @@ -187,21 +187,7 @@ async function main() { // const NO_COLORS = args.includes("--no-colors"); setShowDiffs(args.includes("--show-diffs")); - const isWin = await getIsWin(); - log.setup({ - handlers: { - console: new log.ConsoleHandler(LOG_LEVEL, { - formatter: ({ msg }) => msg, - useColors: isWin ? false : true, - }), - }, - loggers: { - default: { - level: LOG_LEVEL, - handlers: ["console"], - }, - }, - }); + log.setup(LOG_LEVEL); log.debug("Debug logging enabled. CLI build against " + VERSION); const extraHeaders = getHeaders(); @@ -235,7 +221,10 @@ function isMain() { } } if (isMain()) { - main(); + main().then(() => { + // Destroy stdin so interactive prompts (Cliffy) don't keep the event loop alive + process.stdin.destroy(); + }); } export default command; diff --git a/cli/src/types.ts b/cli/src/types.ts index 7115f743ff..382f7f82af 100644 --- a/cli/src/types.ts +++ b/cli/src/types.ts @@ -1,9 +1,9 @@ import { colors } from "@cliffy/ansi/colors"; import * as Diff from "diff"; -import * as log from "@std/log"; -import * as path from "@std/path"; -import { SEPARATOR as SEP } from "@std/path"; -import { stringify as yamlStringify } from "@std/yaml"; +import * as log from "./core/log.ts"; +import * as path from "node:path"; +import { sep as SEP } from "node:path"; +import { stringify as yamlStringify } from "yaml"; import { yamlParseContent } from "./utils/yaml.ts"; import { readFileSync } from "node:fs"; import { pushApp } from "./commands/app/app.ts"; diff --git a/cli/src/utils/codebase.ts b/cli/src/utils/codebase.ts index 2fdad891d6..e665d060b2 100644 --- a/cli/src/utils/codebase.ts +++ b/cli/src/utils/codebase.ts @@ -1,5 +1,5 @@ import { Codebase, SyncOptions } from "../core/conf.ts"; -import * as log from "@std/log"; +import * as log from "../core/log.ts"; import { digestDir } from "./utils.ts"; export type SyncCodebase = Codebase & { diff --git a/cli/src/utils/git.ts b/cli/src/utils/git.ts index c402a5906a..05e37240dd 100644 --- a/cli/src/utils/git.ts +++ b/cli/src/utils/git.ts @@ -1,4 +1,4 @@ -import * as log from "@std/log"; +import * as log from "../core/log.ts"; import { execSync } from "node:child_process"; import { WM_FORK_PREFIX } from "../core/constants.ts"; diff --git a/cli/src/utils/metadata.ts b/cli/src/utils/metadata.ts index 7ddba177b3..0e10798725 100644 --- a/cli/src/utils/metadata.ts +++ b/cli/src/utils/metadata.ts @@ -1,8 +1,8 @@ import { GlobalOptions } from "../types.ts"; -import { SEPARATOR as SEP } from "@std/path"; +import { sep as SEP } from "node:path"; import { colors } from "@cliffy/ansi/colors"; -import * as log from "@std/log"; -import { stringify as yamlStringify } from "@std/yaml"; +import * as log from "../core/log.ts"; +import { stringify as yamlStringify } from "yaml"; import { yamlParseFile } from "./yaml.ts"; import { readFile, writeFile, stat, rm, readdir } from "node:fs/promises"; import { readFileSync } from "node:fs"; diff --git a/cli/src/utils/resource_folders.ts b/cli/src/utils/resource_folders.ts index 898edb305c..e24835ab35 100644 --- a/cli/src/utils/resource_folders.ts +++ b/cli/src/utils/resource_folders.ts @@ -8,8 +8,8 @@ * (.flow, .app, .raw_app) or dunder-prefixed names (__flow, __app, __raw_app). */ -import * as log from "@std/log"; -import { SEPARATOR as SEP } from "@std/path"; +import * as log from "../core/log.ts"; +import { sep as SEP } from "node:path"; import { yamlParseFile } from "./yaml.ts"; import * as fs from "node:fs"; import * as path from "node:path"; diff --git a/cli/src/utils/tar.ts b/cli/src/utils/tar.ts new file mode 100644 index 0000000000..d5149acb56 --- /dev/null +++ b/cli/src/utils/tar.ts @@ -0,0 +1,22 @@ +import { pack } from "tar-stream"; + +export interface TarEntry { + name: string; + content: Buffer | Uint8Array | string; +} + +export function createTarBlob(entries: TarEntry[]): Promise { + return new Promise((resolve, reject) => { + const p = pack(); + const chunks: Uint8Array[] = []; + + p.on("data", (chunk: Buffer) => chunks.push(new Uint8Array(chunk))); + p.on("end", () => resolve(new Blob(chunks as BlobPart[]))); + p.on("error", reject); + + for (const entry of entries) { + p.entry({ name: entry.name }, Buffer.from(entry.content)); + } + p.finalize(); + }); +} diff --git a/cli/src/utils/upgrade.ts b/cli/src/utils/upgrade.ts index 709d59eb7f..44ab0741fd 100644 --- a/cli/src/utils/upgrade.ts +++ b/cli/src/utils/upgrade.ts @@ -53,6 +53,10 @@ export class NpmProvider extends Provider { getRegistryUrl(name: string, version: string): string { return `npm:${this.packageName ?? name}@${version}`; } + + async hasRequiredPermissions(): Promise { + return true; + } } type NpmApiPackageMetadata = { diff --git a/cli/src/utils/utils.ts b/cli/src/utils/utils.ts index 8448ef9b9a..02a8c1a643 100644 --- a/cli/src/utils/utils.ts +++ b/cli/src/utils/utils.ts @@ -3,9 +3,8 @@ // @ts-nocheck This file is copied from a JS project, so it's not type-safe. import { colors } from "@cliffy/ansi/colors"; -import { encodeHex } from "@std/encoding"; -import * as log from "@std/log"; -import { SEPARATOR as SEP } from "@std/path"; +import * as log from "../core/log.ts"; +import { sep as SEP } from "node:path"; import crypto from "node:crypto"; import { readFileSync, writeFileSync } from "node:fs"; import { readdir, readFile } from "node:fs/promises"; @@ -128,7 +127,7 @@ export async function generateHashFromBuffer( content: BufferSource ): Promise { const hashBuffer = await crypto.subtle.digest("SHA-256", content); - return encodeHex(hashBuffer); + return Buffer.from(hashBuffer).toString("hex"); } export function readInlinePathSync(path: string): string { diff --git a/cli/src/utils/yaml.ts b/cli/src/utils/yaml.ts index f8621ba2b6..9ad247c1fd 100644 --- a/cli/src/utils/yaml.ts +++ b/cli/src/utils/yaml.ts @@ -1,4 +1,4 @@ -import { parse as yamlParse, type ParseOptions } from "@std/yaml"; +import { parse as yamlParse, type ParseOptions } from "yaml"; import { readFile } from "node:fs/promises"; export async function yamlParseFile(path: string, options: ParseOptions = {}) { diff --git a/cli/test/lint_command.test.ts b/cli/test/lint_command.test.ts index 6b3d91244e..d0f4226568 100644 --- a/cli/test/lint_command.test.ts +++ b/cli/test/lint_command.test.ts @@ -1,7 +1,7 @@ import { expect, test } from "bun:test"; import { mkdtemp, rm, mkdir, writeFile } from "node:fs/promises"; import os from "node:os"; -import * as path from "@std/path"; +import * as path from "node:path"; import { formatValidationError, runLint, diff --git a/cli/test/lint_locks.test.ts b/cli/test/lint_locks.test.ts index 742e2318df..6ec4363e82 100644 --- a/cli/test/lint_locks.test.ts +++ b/cli/test/lint_locks.test.ts @@ -1,7 +1,7 @@ import { expect, test, describe } from "bun:test"; import { mkdtemp, rm, mkdir, writeFile } from "node:fs/promises"; import os from "node:os"; -import * as path from "@std/path"; +import * as path from "node:path"; import { checkMissingLocks, runLint } from "../src/commands/lint/lint.ts"; async function withTempDir( diff --git a/cli/test/list_get_new_commands.test.ts b/cli/test/list_get_new_commands.test.ts new file mode 100644 index 0000000000..8df67cab9b --- /dev/null +++ b/cli/test/list_get_new_commands.test.ts @@ -0,0 +1,639 @@ +/** + * Integration tests for the new list/get/new CLI commands. + * + * Tests: + * - `list --json` for all item types + * - `get ` and `get --json` for all item types + * - `new` (bootstrap) for script, flow, resource, resource-type, variable, schedule, folder, trigger + * - `bootstrap` alias for script and flow + */ + +import { expect, test, describe } from "bun:test"; +import { writeFile, mkdir, stat, readFile } from "node:fs/promises"; +import { join } from "node:path"; +import { withTestBackend, type TestBackend } from "./test_backend.ts"; +import { addWorkspace } from "../workspace.ts"; + +async function setupWorkspaceProfile(backend: TestBackend): Promise { + await addWorkspace( + { + remote: backend.baseUrl, + workspaceId: backend.workspace, + name: "localhost_test", + token: backend.token!, + }, + { force: true, configDir: backend.testConfigDir } + ); +} + +async function createRemoteScript( + backend: TestBackend, + scriptPath: string, + content: string = 'export async function main() { return "hello"; }' +): Promise { + const resp = await backend.apiRequest!( + `/api/w/${backend.workspace}/scripts/create`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + path: scriptPath, + content, + language: "bun", + summary: "Test script summary", + description: "Test script description", + schema: { + $schema: "https://json-schema.org/draft/2020-12/schema", + type: "object", + properties: {}, + required: [], + }, + }), + } + ); + expect(resp.status).toBeLessThan(300); + await resp.text(); +} + +// ============================================================================= +// list --json +// ============================================================================= + +describe("list --json flag", () => { + test("script list --json outputs valid JSON", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const uniqueId = Date.now(); + const scriptPath = `f/test/list_json_script_${uniqueId}`; + await createRemoteScript(backend, scriptPath); + + const result = await backend.runCLICommand( + ["script", "list", "--json"], + tempDir + ); + + expect(result.code).toEqual(0); + const parsed = JSON.parse(result.stdout); + expect(Array.isArray(parsed)).toBe(true); + expect(parsed.some((s: any) => s.path === scriptPath)).toBe(true); + }); + }); + + test("flow list --json outputs valid JSON", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const result = await backend.runCLICommand( + ["flow", "list", "--json"], + tempDir + ); + + expect(result.code).toEqual(0); + const parsed = JSON.parse(result.stdout); + expect(Array.isArray(parsed)).toBe(true); + }); + }); + + test("resource list --json outputs valid JSON", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const result = await backend.runCLICommand( + ["resource", "list", "--json"], + tempDir + ); + + expect(result.code).toEqual(0); + const parsed = JSON.parse(result.stdout); + expect(Array.isArray(parsed)).toBe(true); + // seedTestData creates f/test/my_resource + expect(parsed.some((r: any) => r.path === "f/test/my_resource")).toBe( + true + ); + }); + }); + + test("variable list --json outputs valid JSON", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const result = await backend.runCLICommand( + ["variable", "list", "--json"], + tempDir + ); + + expect(result.code).toEqual(0); + const parsed = JSON.parse(result.stdout); + expect(Array.isArray(parsed)).toBe(true); + }); + }); + + test("folder list --json outputs valid JSON", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const result = await backend.runCLICommand( + ["folder", "list", "--json"], + tempDir + ); + + expect(result.code).toEqual(0); + const parsed = JSON.parse(result.stdout); + expect(Array.isArray(parsed)).toBe(true); + expect(parsed.some((f: any) => f.name === "test")).toBe(true); + }); + }); + + test("schedule list --json outputs valid JSON", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const result = await backend.runCLICommand( + ["schedule", "list", "--json"], + tempDir + ); + + expect(result.code).toEqual(0); + const parsed = JSON.parse(result.stdout); + expect(Array.isArray(parsed)).toBe(true); + }); + }); + + test("resource-type list --json outputs valid JSON", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const result = await backend.runCLICommand( + ["resource-type", "list", "--json"], + tempDir + ); + + expect(result.code).toEqual(0); + const parsed = JSON.parse(result.stdout); + expect(Array.isArray(parsed)).toBe(true); + }); + }); + + test("trigger list --json outputs valid JSON", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const result = await backend.runCLICommand( + ["trigger", "list", "--json"], + tempDir + ); + + expect(result.code).toEqual(0); + const parsed = JSON.parse(result.stdout); + expect(Array.isArray(parsed)).toBe(true); + }); + }); + + test("app list --json outputs valid JSON", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const result = await backend.runCLICommand( + ["app", "list", "--json"], + tempDir + ); + + expect(result.code).toEqual(0); + const parsed = JSON.parse(result.stdout); + expect(Array.isArray(parsed)).toBe(true); + }); + }); + + test("default action with --json works (e.g. wmill script --json)", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const result = await backend.runCLICommand( + ["script", "--json"], + tempDir + ); + + expect(result.code).toEqual(0); + const parsed = JSON.parse(result.stdout); + expect(Array.isArray(parsed)).toBe(true); + }); + }); +}); + +// ============================================================================= +// get and get --json +// ============================================================================= + +describe("get command", () => { + test("script get pretty-prints details", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const uniqueId = Date.now(); + const scriptPath = `f/test/get_script_${uniqueId}`; + await createRemoteScript(backend, scriptPath); + + const result = await backend.runCLICommand( + ["script", "get", scriptPath], + tempDir + ); + + expect(result.code).toEqual(0); + const output = result.stdout; + expect(output).toContain("Path:"); + expect(output).toContain(scriptPath); + expect(output).toContain("Summary:"); + expect(output).toContain("Language:"); + expect(output).toContain("bun"); + }); + }); + + test("script get --json outputs valid JSON", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const uniqueId = Date.now(); + const scriptPath = `f/test/get_json_script_${uniqueId}`; + await createRemoteScript(backend, scriptPath); + + const result = await backend.runCLICommand( + ["script", "get", scriptPath, "--json"], + tempDir + ); + + expect(result.code).toEqual(0); + const parsed = JSON.parse(result.stdout); + expect(parsed.path).toBe(scriptPath); + expect(parsed.language).toBe("bun"); + expect(parsed.summary).toBe("Test script summary"); + }); + }); + + test("resource get --json outputs valid JSON", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const result = await backend.runCLICommand( + ["resource", "get", "f/test/my_resource", "--json"], + tempDir + ); + + expect(result.code).toEqual(0); + const parsed = JSON.parse(result.stdout); + expect(parsed.path).toBe("f/test/my_resource"); + expect(parsed.resource_type).toBe("any"); + }); + }); + + test("resource get pretty-prints details", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const result = await backend.runCLICommand( + ["resource", "get", "f/test/my_resource"], + tempDir + ); + + expect(result.code).toEqual(0); + const output = result.stdout; + expect(output).toContain("Path:"); + expect(output).toContain("f/test/my_resource"); + expect(output).toContain("Resource Type:"); + }); + }); + + test("variable get --json outputs valid JSON", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const result = await backend.runCLICommand( + ["variable", "get", "f/test/my_variable", "--json"], + tempDir + ); + + expect(result.code).toEqual(0); + const parsed = JSON.parse(result.stdout); + expect(parsed.path).toBe("f/test/my_variable"); + }); + }); + + test("folder get --json outputs valid JSON", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const result = await backend.runCLICommand( + ["folder", "get", "test", "--json"], + tempDir + ); + + expect(result.code).toEqual(0); + const parsed = JSON.parse(result.stdout); + expect(parsed.name).toBe("test"); + }); + }); + + test("folder get pretty-prints details", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const result = await backend.runCLICommand( + ["folder", "get", "test"], + tempDir + ); + + expect(result.code).toEqual(0); + const output = result.stdout; + expect(output).toContain("Name:"); + expect(output).toContain("test"); + }); + }); +}); + +// ============================================================================= +// new command +// ============================================================================= + +describe("new command", () => { + test("script new creates files (same as bootstrap)", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + await writeFile( + join(tempDir, "wmill.yaml"), + `defaultTs: bun\nincludes:\n - "**"\nexcludes: []\n`, + "utf-8" + ); + await mkdir(join(tempDir, "f", "test"), { recursive: true }); + + const result = await backend.runCLICommand( + ["script", "new", "f/test/new_cmd_script", "bun", "--summary", "Test new"], + tempDir + ); + + expect(result.code).toEqual(0); + + const codeStat = await stat(join(tempDir, "f/test/new_cmd_script.ts")); + expect(codeStat.isFile()).toBe(true); + + const metaStat = await stat( + join(tempDir, "f/test/new_cmd_script.script.yaml") + ); + expect(metaStat.isFile()).toBe(true); + + const metaContent = await readFile( + join(tempDir, "f/test/new_cmd_script.script.yaml"), + "utf-8" + ); + expect(metaContent).toContain("Test new"); + }); + }); + + test("script bootstrap still works as alias", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + await writeFile( + join(tempDir, "wmill.yaml"), + `defaultTs: bun\nincludes:\n - "**"\nexcludes: []\n`, + "utf-8" + ); + await mkdir(join(tempDir, "f", "test"), { recursive: true }); + + const result = await backend.runCLICommand( + ["script", "bootstrap", "f/test/alias_script", "bun"], + tempDir + ); + + expect(result.code).toEqual(0); + + const codeStat = await stat(join(tempDir, "f/test/alias_script.ts")); + expect(codeStat.isFile()).toBe(true); + }); + }); + + test("flow new creates flow directory and flow.yaml", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + await mkdir(join(tempDir, "f", "test"), { recursive: true }); + + const result = await backend.runCLICommand( + ["flow", "new", "f/test/new_flow", "--summary", "My flow"], + tempDir + ); + + expect(result.code).toEqual(0); + + const flowYamlStat = await stat( + join(tempDir, "f/test/new_flow.flow/flow.yaml") + ); + expect(flowYamlStat.isFile()).toBe(true); + + const flowContent = await readFile( + join(tempDir, "f/test/new_flow.flow/flow.yaml"), + "utf-8" + ); + expect(flowContent).toContain("My flow"); + }); + }); + + test("flow bootstrap still works as alias", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + await mkdir(join(tempDir, "f", "test"), { recursive: true }); + + const result = await backend.runCLICommand( + ["flow", "bootstrap", "f/test/alias_flow"], + tempDir + ); + + expect(result.code).toEqual(0); + + const flowYamlStat = await stat( + join(tempDir, "f/test/alias_flow.flow/flow.yaml") + ); + expect(flowYamlStat.isFile()).toBe(true); + }); + }); + + test("resource new creates resource yaml template", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + await mkdir(join(tempDir, "f", "test"), { recursive: true }); + + const result = await backend.runCLICommand( + ["resource", "new", "f/test/new_resource"], + tempDir + ); + + expect(result.code).toEqual(0); + + const filePath = join(tempDir, "f/test/new_resource.resource.yaml"); + const fileStat = await stat(filePath); + expect(fileStat.isFile()).toBe(true); + + const content = await readFile(filePath, "utf-8"); + expect(content).toContain("resource_type"); + expect(content).toContain("value"); + }); + }); + + test("resource-type new creates resource-type yaml template", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const result = await backend.runCLICommand( + ["resource-type", "new", "my_custom_type"], + tempDir + ); + + expect(result.code).toEqual(0); + + const filePath = join(tempDir, "my_custom_type.resource-type.yaml"); + const fileStat = await stat(filePath); + expect(fileStat.isFile()).toBe(true); + + const content = await readFile(filePath, "utf-8"); + expect(content).toContain("schema"); + expect(content).toContain("description"); + }); + }); + + test("variable new creates variable yaml template", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + await mkdir(join(tempDir, "f", "test"), { recursive: true }); + + const result = await backend.runCLICommand( + ["variable", "new", "f/test/new_var"], + tempDir + ); + + expect(result.code).toEqual(0); + + const filePath = join(tempDir, "f/test/new_var.variable.yaml"); + const fileStat = await stat(filePath); + expect(fileStat.isFile()).toBe(true); + + const content = await readFile(filePath, "utf-8"); + expect(content).toContain("is_secret"); + expect(content).toContain("value"); + }); + }); + + test("schedule new creates schedule yaml template", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + await mkdir(join(tempDir, "f", "test"), { recursive: true }); + + const result = await backend.runCLICommand( + ["schedule", "new", "f/test/new_sched"], + tempDir + ); + + expect(result.code).toEqual(0); + + const filePath = join(tempDir, "f/test/new_sched.schedule.yaml"); + const fileStat = await stat(filePath); + expect(fileStat.isFile()).toBe(true); + + const content = await readFile(filePath, "utf-8"); + expect(content).toContain("schedule"); + expect(content).toContain("script_path"); + expect(content).toContain("timezone"); + }); + }); + + test("folder new creates folder.meta.yaml in f//", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + const result = await backend.runCLICommand( + ["folder", "new", "new_folder"], + tempDir + ); + + expect(result.code).toEqual(0); + + const filePath = join(tempDir, "f/new_folder/folder.meta.yaml"); + const fileStat = await stat(filePath); + expect(fileStat.isFile()).toBe(true); + + const content = await readFile(filePath, "utf-8"); + expect(content).toContain("owners"); + expect(content).toContain("extra_perms"); + }); + }); + + test("trigger new --kind http creates http trigger yaml template", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + await mkdir(join(tempDir, "f", "test"), { recursive: true }); + + const result = await backend.runCLICommand( + ["trigger", "new", "f/test/new_trigger", "--kind", "http"], + tempDir + ); + + expect(result.code).toEqual(0); + + const filePath = join( + tempDir, + "f/test/new_trigger.http_trigger.yaml" + ); + const fileStat = await stat(filePath); + expect(fileStat.isFile()).toBe(true); + + const content = await readFile(filePath, "utf-8"); + expect(content).toContain("script_path"); + expect(content).toContain("route_path"); + }); + }); + + test("trigger new without --kind fails with error", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + await mkdir(join(tempDir, "f", "test"), { recursive: true }); + + const result = await backend.runCLICommand( + ["trigger", "new", "f/test/fail_trigger"], + tempDir + ); + + expect(result.code).not.toEqual(0); + }); + }); + + test("trigger new --kind kafka creates kafka trigger yaml template", async () => { + await withTestBackend(async (backend, tempDir) => { + await setupWorkspaceProfile(backend); + + await mkdir(join(tempDir, "f", "test"), { recursive: true }); + + const result = await backend.runCLICommand( + ["trigger", "new", "f/test/kafka_trigger", "--kind", "kafka"], + tempDir + ); + + expect(result.code).toEqual(0); + + const filePath = join( + tempDir, + "f/test/kafka_trigger.kafka_trigger.yaml" + ); + const fileStat = await stat(filePath); + expect(fileStat.isFile()).toBe(true); + + const content = await readFile(filePath, "utf-8"); + expect(content).toContain("kafka_resource_path"); + expect(content).toContain("topics"); + }); + }); +}); diff --git a/cli/test/lock_cache.test.ts b/cli/test/lock_cache.test.ts index db1632bb27..9f30b67d90 100644 --- a/cli/test/lock_cache.test.ts +++ b/cli/test/lock_cache.test.ts @@ -11,7 +11,7 @@ */ import { expect, test } from "bun:test"; -import { encodeHex } from "@std/encoding"; +import { Buffer } from "node:buffer"; // --------------------------------------------------------------------------- // Mirrors extractWorkspaceDepsAnnotation + computeLockCacheKey from @@ -110,7 +110,7 @@ async function computeLockCacheKey( .join(";"); const content = `${language}|${annotationStr}|${depsStr}`; const buf = new TextEncoder().encode(content); - return encodeHex(await crypto.subtle.digest("SHA-256", buf)); + return Buffer.from(await crypto.subtle.digest("SHA-256", buf)).toString("hex"); } // --------------------------------------------------------------------------- diff --git a/cli/test/mixed_case_paths.test.ts b/cli/test/mixed_case_paths.test.ts index d0518cb511..ffbb3f21d4 100644 --- a/cli/test/mixed_case_paths.test.ts +++ b/cli/test/mixed_case_paths.test.ts @@ -13,7 +13,7 @@ */ import { expect, test } from "bun:test"; -import * as path from "@std/path"; +import * as path from "node:path"; import { writeFile, readFile, stat } from "node:fs/promises"; import { withTestBackend } from "./test_backend.ts"; import { addWorkspace } from "../workspace.ts"; diff --git a/cli/test/raw_app_sync.test.ts b/cli/test/raw_app_sync.test.ts index e9151b45fc..f77b4b045b 100644 --- a/cli/test/raw_app_sync.test.ts +++ b/cli/test/raw_app_sync.test.ts @@ -1,7 +1,7 @@ import { expect, test } from "bun:test"; import { withTestBackend } from "./test_backend.ts"; import { addWorkspace } from "../workspace.ts"; -import * as path from "@std/path"; +import * as path from "node:path"; import { writeFile, readFile, stat, rm, mkdir } from "node:fs/promises"; // ============================================================================= diff --git a/cli/test/sync_pull_push.test.ts b/cli/test/sync_pull_push.test.ts index 7abe61edab..2b062eb157 100644 --- a/cli/test/sync_pull_push.test.ts +++ b/cli/test/sync_pull_push.test.ts @@ -6,8 +6,8 @@ */ import { expect, test, describe } from "bun:test"; -import * as path from "@std/path"; -import { SEPARATOR as SEP } from "@std/path"; +import * as path from "node:path"; +import { sep as SEP } from "node:path"; import { writeFile, readFile, readdir, rm, mkdir, mkdtemp } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; diff --git a/cli/test/tar_creation.test.ts b/cli/test/tar_creation.test.ts new file mode 100644 index 0000000000..accbbb3829 --- /dev/null +++ b/cli/test/tar_creation.test.ts @@ -0,0 +1,140 @@ +/** + * Unit tests for the tar creation utility. + * These tests require no backend — they test standalone tar logic. + */ + +import { expect, test, describe } from "bun:test"; +import { createTarBlob, type TarEntry } from "../src/utils/tar.ts"; +import { extract, type Headers } from "tar-stream"; +import { Readable } from "node:stream"; + +/** Extract all entries from a tarball Blob into a map of name -> content string */ +async function extractTar( + blob: Blob +): Promise> { + const result = new Map(); + const ex = extract(); + const buffer = Buffer.from(await blob.arrayBuffer()); + + return new Promise((resolve, reject) => { + ex.on("entry", (header, stream, next) => { + const chunks: Buffer[] = []; + stream.on("data", (chunk: Buffer) => chunks.push(chunk)); + stream.on("end", () => { + result.set(header.name, { + content: Buffer.concat(chunks).toString("utf-8"), + header, + }); + next(); + }); + stream.on("error", reject); + stream.resume(); + }); + ex.on("finish", () => resolve(result)); + ex.on("error", reject); + + Readable.from(buffer).pipe(ex); + }); +} + +describe("createTarBlob", () => { + test("single file tarball", async () => { + const entries: TarEntry[] = [ + { name: "main.js", content: 'console.log("hello");' }, + ]; + + const blob = await createTarBlob(entries); + const extracted = await extractTar(blob); + + expect(extracted.size).toBe(1); + expect(extracted.has("main.js")).toBe(true); + expect(extracted.get("main.js")!.content).toBe('console.log("hello");'); + }); + + test("multiple output files", async () => { + const entries: TarEntry[] = [ + { name: "main.js", content: 'import "./chunk-abc.js";' }, + { name: "chunk-abc.js", content: "export const x = 42;" }, + { name: "chunk-def.js", content: "export const y = 99;" }, + ]; + + const blob = await createTarBlob(entries); + const extracted = await extractTar(blob); + + expect(extracted.size).toBe(3); + expect(extracted.get("main.js")!.content).toBe( + 'import "./chunk-abc.js";' + ); + expect(extracted.get("chunk-abc.js")!.content).toBe( + "export const x = 42;" + ); + expect(extracted.get("chunk-def.js")!.content).toBe( + "export const y = 99;" + ); + }); + + test("single file with assets", async () => { + const entries: TarEntry[] = [ + { name: "main.js", content: "const data = require('./data.json');" }, + { name: "data.json", content: '{"key":"value"}' }, + ]; + + const blob = await createTarBlob(entries); + const extracted = await extractTar(blob); + + expect(extracted.size).toBe(2); + expect(extracted.has("main.js")).toBe(true); + expect(extracted.has("data.json")).toBe(true); + expect(extracted.get("data.json")!.content).toBe('{"key":"value"}'); + }); + + test("produces a valid Blob", async () => { + const entries: TarEntry[] = [ + { name: "main.js", content: "module.exports = {};" }, + ]; + + const blob = await createTarBlob(entries); + + expect(blob).toBeInstanceOf(Blob); + expect(blob.size).toBeGreaterThan(0); + // Tar blocks are 512-byte aligned + expect(blob.size % 512).toBe(0); + }); + + test("file naming — entries have exact names given", async () => { + const entries: TarEntry[] = [ + { name: "main.js", content: "entry point" }, + { name: "lib/utils.js", content: "utils" }, + ]; + + const blob = await createTarBlob(entries); + const extracted = await extractTar(blob); + + // Names should be exactly as provided (no leading slash) + expect(extracted.has("main.js")).toBe(true); + expect(extracted.has("lib/utils.js")).toBe(true); + }); + + test("handles Buffer content", async () => { + const entries: TarEntry[] = [ + { name: "main.js", content: Buffer.from("buffer content") }, + ]; + + const blob = await createTarBlob(entries); + const extracted = await extractTar(blob); + + expect(extracted.get("main.js")!.content).toBe("buffer content"); + }); + + test("handles Uint8Array content", async () => { + const content = new TextEncoder().encode("uint8 content"); + const entries: TarEntry[] = [ + { name: "main.js", content }, + ]; + + const blob = await createTarBlob(entries); + const extracted = await extractTar(blob); + + expect(extracted.get("main.js")!.content).toBe("uint8 content"); + }); +}); diff --git a/cli/test/wmill_lock.test.ts b/cli/test/wmill_lock.test.ts index 7760166fd4..22c6c52ea0 100644 --- a/cli/test/wmill_lock.test.ts +++ b/cli/test/wmill_lock.test.ts @@ -7,7 +7,7 @@ */ import { expect, test } from "bun:test"; -import * as path from "@std/path"; +import * as path from "node:path"; import { mkdtemp, rm, writeFile } from "node:fs/promises"; import os from "node:os"; import { @@ -18,7 +18,7 @@ import { clearGlobalLock, } from "../src/utils/metadata.ts"; import { generateHash } from "../src/utils/utils.ts"; -import { stringify as yamlStringify } from "@std/yaml"; +import { stringify as yamlStringify } from "yaml"; import { yamlParseFile } from "../src/utils/yaml.ts"; // ============================================================================= diff --git a/cli/test/workspace_deps_filter.test.ts b/cli/test/workspace_deps_filter.test.ts index 52fa6a7b76..c184e9a524 100644 --- a/cli/test/workspace_deps_filter.test.ts +++ b/cli/test/workspace_deps_filter.test.ts @@ -14,7 +14,7 @@ import { expect, test } from "bun:test"; import { withTestBackend } from "./test_backend.ts"; import { addWorkspace } from "../workspace.ts"; import { writeFile, mkdir } from "node:fs/promises"; -import { stringify as stringifyYaml } from "@std/yaml"; +import { stringify as stringifyYaml } from "yaml"; // Import hash generation utilities from CLI import { generateHash } from "../src/utils/utils.ts"; diff --git a/cli/windmill-utils-internal/src/parse/parse-schema.ts b/cli/windmill-utils-internal/src/parse/parse-schema.ts index 1887ef2d40..6e94c0ff77 100644 --- a/cli/windmill-utils-internal/src/parse/parse-schema.ts +++ b/cli/windmill-utils-internal/src/parse/parse-schema.ts @@ -228,7 +228,7 @@ export function argSigToJsonSchemaType( if (oldS.items && typeof oldS.items === "object") { ITEMS_PRESERVED_FIELDS.forEach((field) => { if (oldS.items && oldS.items[field] !== undefined) { - newS.items![field] = oldS.items[field]; + (newS.items as any)[field] = oldS.items[field]; } }); } @@ -241,7 +241,7 @@ export function argSigToJsonSchemaType( if (oldS.items && typeof oldS.items === "object") { ITEMS_PRESERVED_FIELDS.forEach((field) => { if (oldS.items && oldS.items[field] !== undefined) { - newS.items![field] = oldS.items[field]; + (newS.items as any)[field] = oldS.items[field]; } }); } diff --git a/docker/DockerfileCli b/docker/DockerfileCli index a3e5d80cb2..546ce49924 100644 --- a/docker/DockerfileCli +++ b/docker/DockerfileCli @@ -1,5 +1,7 @@ -FROM node:slim +FROM oven/bun:slim -RUN npm install -g windmill-cli +RUN bun install -g windmill-cli -ENTRYPOINT [ "wmill" ] \ No newline at end of file +RUN ln -s $(bun pm bin -g)/wmill /usr/bin/wmill + +ENTRYPOINT [ "wmill" ] diff --git a/docker/DockerfileSlim b/docker/DockerfileSlim index 155746cd64..d20067e862 100644 --- a/docker/DockerfileSlim +++ b/docker/DockerfileSlim @@ -56,6 +56,11 @@ RUN mkdir -p /tmp/windmill/cache && \ COPY --from=oven/bun:1.3.8 /usr/local/bin/bun /usr/bin/bun +# Install windmill CLI (node symlink needed for bun install) +RUN ln -s /usr/bin/bun /usr/bin/node \ + && bun install -g windmill-cli \ + && ln -s $(bun pm bin -g)/wmill /usr/bin/wmill + # add the docker client to call docker from a worker if enabled COPY --from=docker:dind /usr/local/bin/docker /usr/local/bin/ diff --git a/docker/DockerfileSlimEe b/docker/DockerfileSlimEe index 53fc9ae51d..8510ce59ad 100644 --- a/docker/DockerfileSlimEe +++ b/docker/DockerfileSlimEe @@ -56,6 +56,11 @@ RUN mkdir -p /tmp/windmill/cache && \ COPY --from=oven/bun:1.3.8 /usr/local/bin/bun /usr/bin/bun +# Install windmill CLI (node symlink needed for bun install) +RUN ln -s /usr/bin/bun /usr/bin/node \ + && bun install -g windmill-cli \ + && ln -s $(bun pm bin -g)/wmill /usr/bin/wmill + # add the docker client to call docker from a worker if enabled COPY --from=docker:dind /usr/local/bin/docker /usr/local/bin/ diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1c6e10c9e9..ca78b11190 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -835,7 +835,6 @@ "version": "1.7.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -847,7 +846,6 @@ "version": "1.7.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -858,7 +856,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1348,7 +1345,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.0.tgz", "integrity": "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1503,7 +1499,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1520,7 +1515,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1537,7 +1531,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1554,7 +1547,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1571,7 +1563,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1588,7 +1579,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1605,7 +1595,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1622,7 +1611,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1639,7 +1627,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1656,7 +1643,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1673,7 +1659,6 @@ "cpu": [ "wasm32" ], - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1690,7 +1675,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1707,7 +1691,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2313,7 +2296,6 @@ "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -7193,7 +7175,7 @@ "version": "1.21.7", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "dev": true, + "devOptional": true, "license": "MIT", "bin": { "jiti": "bin/jiti.js" @@ -7692,7 +7674,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7713,7 +7694,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7734,7 +7714,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7755,7 +7734,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7776,7 +7754,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7797,7 +7774,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7818,7 +7794,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7839,7 +7814,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7860,7 +7834,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7881,7 +7854,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7902,7 +7874,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -12529,21 +12500,6 @@ } } }, - "node_modules/svelte-check/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/svelte-eslint-parser": { "version": "0.43.0", "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.43.0.tgz", diff --git a/frontend/src/lib/components/EditorBar.svelte b/frontend/src/lib/components/EditorBar.svelte index 9807ed877c..3dfc25e127 100644 --- a/frontend/src/lib/components/EditorBar.svelte +++ b/frontend/src/lib/components/EditorBar.svelte @@ -543,8 +543,7 @@ } editor.insertAtCursor(`v, _ := wmill.GetVariable("${path}")`) } else if (lang == 'bash') { - editor.insertAtCursor(`curl -s -H "Authorization: Bearer $WM_TOKEN" \\ - "$BASE_INTERNAL_URL/api/w/$WM_WORKSPACE/variables/get_value/${path}" | jq -r .`) + editor.insertAtCursor(`wmill variable get ${path} --json | jq -r .value`) } else if (lang == 'powershell') { editor.insertAtCursor(`$Headers = @{\n"Authorization" = "Bearer $Env:WM_TOKEN"`) editor.arrowDown() @@ -620,8 +619,7 @@ string ${windmillPathToCamelCaseName(path)} = await client.GetStringAsync(uri); } editor.insertAtCursor(`r, _ := wmill.GetResource("${path}")`) } else if (lang == 'bash') { - editor.insertAtCursor(`curl -s -H "Authorization: Bearer $WM_TOKEN" \\ - "$BASE_INTERNAL_URL/api/w/$WM_WORKSPACE/resources/get_value_interpolated/${path}" | jq`) + editor.insertAtCursor(`wmill resource get ${path} --json | jq .value`) } else if (lang == 'powershell') { editor.insertAtCursor(`$Headers = @{\n"Authorization" = "Bearer $Env:WM_TOKEN"`) editor.arrowDown()