From 181f24cfccd83648449cdbba05b75159bf3c7181 Mon Sep 17 00:00:00 2001 From: ezl-keygraph Date: Sun, 15 Mar 2026 00:45:36 +0530 Subject: [PATCH] refactor: migrate to Turborepo + pnpm + Biome monorepo Restructure into apps/worker, apps/cli, packages/mcp-server with Turborepo task orchestration, pnpm workspaces, Biome linting/formatting, and tsdown CLI bundling. Key changes: - src/ -> apps/worker/src/, cli/ -> apps/cli/, mcp-server/ -> packages/mcp-server/ - prompts/ and configs/ moved into apps/worker/ - npm replaced with pnpm, package-lock.json replaced with pnpm-lock.yaml - Dockerfile updated for pnpm-based builds - CLI logs command rewritten with chokidar for cross-platform reliability - Router health checking added for auto-detected router mode - Centralized path resolution via apps/worker/src/paths.ts --- .dockerignore | 11 +- .github/workflows/release.yml | 36 +- .gitignore | 1 + .npmrc | 2 + .releaserc.json | 22 +- CLAUDE.md | 118 +- Dockerfile | 37 +- {cli => apps/cli}/.npmignore | 0 {cli => apps/cli}/infra/compose.yml | 0 {cli => apps/cli}/infra/router-config.json | 4 +- {cli => apps/cli}/package.json | 15 +- {cli => apps/cli}/src/commands/build.ts | 2 +- apps/cli/src/commands/logs.ts | 106 + {cli => apps/cli}/src/commands/setup.ts | 20 +- {cli => apps/cli}/src/commands/start.ts | 24 +- {cli => apps/cli}/src/commands/status.ts | 0 {cli => apps/cli}/src/commands/stop.ts | 2 +- {cli => apps/cli}/src/commands/uninstall.ts | 4 +- {cli => apps/cli}/src/commands/update.ts | 0 {cli => apps/cli}/src/commands/workspaces.ts | 24 +- {cli => apps/cli}/src/config/resolver.ts | 18 +- {cli => apps/cli}/src/config/writer.ts | 0 {cli => apps/cli}/src/docker.ts | 85 +- {cli => apps/cli}/src/env.ts | 9 +- {cli => apps/cli}/src/home.ts | 6 +- {cli => apps/cli}/src/index.ts | 79 +- {cli => apps/cli}/src/mode.ts | 0 {cli => apps/cli}/src/paths.ts | 0 {cli => apps/cli}/src/splash.ts | 0 {cli => apps/cli}/tsconfig.json | 2 +- apps/cli/tsdown.config.ts | 11 + .../worker/configs}/config-schema.json | 8 +- .../worker/configs}/example-config.yaml | 0 apps/worker/package.json | 27 + .../worker/prompts}/exploit-auth.txt | 0 .../worker/prompts}/exploit-authz.txt | 0 .../worker/prompts}/exploit-injection.txt | 0 .../worker/prompts}/exploit-ssrf.txt | 0 .../worker/prompts}/exploit-xss.txt | 0 .../pipeline-testing/exploit-auth.txt | 0 .../pipeline-testing/exploit-authz.txt | 0 .../pipeline-testing/exploit-injection.txt | 0 .../pipeline-testing/exploit-ssrf.txt | 0 .../prompts}/pipeline-testing/exploit-xss.txt | 0 .../pipeline-testing/pre-recon-code.txt | 0 .../prompts}/pipeline-testing/recon.txt | 0 .../pipeline-testing/report-executive.txt | 0 .../prompts}/pipeline-testing/vuln-auth.txt | 0 .../prompts}/pipeline-testing/vuln-authz.txt | 0 .../pipeline-testing/vuln-injection.txt | 0 .../prompts}/pipeline-testing/vuln-ssrf.txt | 0 .../prompts}/pipeline-testing/vuln-xss.txt | 0 .../worker/prompts}/pre-recon-code.txt | 0 {prompts => apps/worker/prompts}/recon.txt | 0 .../worker/prompts}/report-executive.txt | 0 .../worker/prompts}/shared/_exploit-scope.txt | 0 .../worker/prompts}/shared/_rules.txt | 0 .../worker/prompts}/shared/_target.txt | 0 .../worker/prompts}/shared/_vuln-scope.txt | 0 .../prompts}/shared/login-instructions.txt | 0 .../worker/prompts}/vuln-auth.txt | 0 .../worker/prompts}/vuln-authz.txt | 0 .../worker/prompts}/vuln-injection.txt | 0 .../worker/prompts}/vuln-ssrf.txt | 0 {prompts => apps/worker/prompts}/vuln-xss.txt | 0 {src => apps/worker/src}/ai/audit-logger.ts | 0 .../worker/src}/ai/claude-executor.ts | 84 +- .../worker/src}/ai/message-handlers.ts | 73 +- {src => apps/worker/src}/ai/models.ts | 0 .../worker/src}/ai/output-formatters.ts | 20 +- .../worker/src}/ai/progress-manager.ts | 5 +- {src => apps/worker/src}/ai/router-utils.ts | 1 - {src => apps/worker/src}/ai/types.ts | 2 - .../worker/src}/audit/audit-session.ts | 44 +- {src => apps/worker/src}/audit/index.ts | 0 {src => apps/worker/src}/audit/log-stream.ts | 6 +- {src => apps/worker/src}/audit/logger.ts | 12 +- .../worker/src}/audit/metrics-tracker.ts | 42 +- {src => apps/worker/src}/audit/utils.ts | 22 +- .../worker/src}/audit/workflow-logger.ts | 55 +- {src => apps/worker/src}/config-parser.ts | 77 +- apps/worker/src/paths.ts | 30 + .../worker/src}/progress-indicator.ts | 2 +- .../worker/src}/services/agent-execution.ts | 74 +- .../worker/src}/services/config-loader.ts | 16 +- .../worker/src}/services/container.ts | 5 +- .../worker/src}/services/error-handling.ts | 63 +- .../src}/services/exploitation-checker.ts | 10 +- .../worker/src}/services/git-manager.ts | 70 +- {src => apps/worker/src}/services/index.ts | 11 +- .../worker/src}/services/preflight.ts | 159 +- .../worker/src}/services/prompt-manager.ts | 89 +- .../worker/src}/services/queue-validation.ts | 56 +- .../worker/src}/services/reporting.ts | 30 +- {src => apps/worker/src}/session-manager.ts | 18 +- .../worker/src}/temporal/activities.ts | 90 +- .../worker/src}/temporal/activity-logger.ts | 0 {src => apps/worker/src}/temporal/shared.ts | 3 +- .../worker/src}/temporal/summary-mapper.ts | 12 +- {src => apps/worker/src}/temporal/worker.ts | 57 +- .../worker/src}/temporal/workflow-errors.ts | 12 +- .../worker/src}/temporal/workflows.ts | 87 +- .../worker/src}/temporal/workspaces.ts | 33 +- .../worker/src}/types/activity-logger.ts | 0 {src => apps/worker/src}/types/agents.ts | 9 +- {src => apps/worker/src}/types/audit.ts | 0 {src => apps/worker/src}/types/config.ts | 8 +- {src => apps/worker/src}/types/errors.ts | 0 {src => apps/worker/src}/types/index.ts | 6 +- {src => apps/worker/src}/types/metrics.ts | 0 {src => apps/worker/src}/types/result.ts | 0 .../worker/src}/utils/billing-detection.ts | 6 +- {src => apps/worker/src}/utils/concurrency.ts | 2 +- {src => apps/worker/src}/utils/file-io.ts | 2 +- {src => apps/worker/src}/utils/formatting.ts | 0 {src => apps/worker/src}/utils/functional.ts | 5 +- {src => apps/worker/src}/utils/metrics.ts | 0 apps/worker/tsconfig.json | 6 + biome.json | 30 + cli/src/commands/logs.ts | 75 - configs/router-config.json | 33 - docker-compose.yml | 2 +- package-lock.json | 2391 ---------------- package.json | 41 +- .../mcp-server}/package.json | 9 +- .../mcp-server}/src/index.ts | 4 +- .../mcp-server}/src/tools/generate-totp.ts | 10 +- .../mcp-server}/src/tools/save-deliverable.ts | 86 +- .../mcp-server}/src/types/deliverables.ts | 0 .../mcp-server}/src/types/index.ts | 0 .../mcp-server}/src/types/tool-responses.ts | 6 +- .../mcp-server}/src/utils/error-formatter.ts | 6 +- .../mcp-server}/src/utils/file-operations.ts | 4 +- .../src/validation/queue-validator.ts | 0 .../src/validation/totp-validator.ts | 0 .../mcp-server}/tsconfig.json | 2 +- pnpm-lock.yaml | 2515 +++++++++++++++++ pnpm-workspace.yaml | 6 + shannon | 381 +-- tsconfig.json | 9 +- turbo.json | 17 + 141 files changed, 3717 insertions(+), 3997 deletions(-) create mode 100644 .npmrc rename {cli => apps/cli}/.npmignore (100%) rename {cli => apps/cli}/infra/compose.yml (100%) rename {cli => apps/cli}/infra/router-config.json (91%) rename {cli => apps/cli}/package.json (75%) rename {cli => apps/cli}/src/commands/build.ts (100%) create mode 100644 apps/cli/src/commands/logs.ts rename {cli => apps/cli}/src/commands/setup.ts (95%) rename {cli => apps/cli}/src/commands/start.ts (91%) rename {cli => apps/cli}/src/commands/status.ts (100%) rename {cli => apps/cli}/src/commands/stop.ts (89%) rename {cli => apps/cli}/src/commands/uninstall.ts (95%) rename {cli => apps/cli}/src/commands/update.ts (100%) rename {cli => apps/cli}/src/commands/workspaces.ts (63%) rename {cli => apps/cli}/src/config/resolver.ts (96%) rename {cli => apps/cli}/src/config/writer.ts (100%) rename {cli => apps/cli}/src/docker.ts (79%) rename {cli => apps/cli}/src/env.ts (95%) rename {cli => apps/cli}/src/home.ts (94%) rename {cli => apps/cli}/src/index.ts (86%) rename {cli => apps/cli}/src/mode.ts (100%) rename {cli => apps/cli}/src/paths.ts (100%) rename {cli => apps/cli}/src/splash.ts (100%) rename {cli => apps/cli}/tsconfig.json (77%) create mode 100644 apps/cli/tsdown.config.ts rename {configs => apps/worker/configs}/config-schema.json (97%) rename {configs => apps/worker/configs}/example-config.yaml (100%) create mode 100644 apps/worker/package.json rename {prompts => apps/worker/prompts}/exploit-auth.txt (100%) rename {prompts => apps/worker/prompts}/exploit-authz.txt (100%) rename {prompts => apps/worker/prompts}/exploit-injection.txt (100%) rename {prompts => apps/worker/prompts}/exploit-ssrf.txt (100%) rename {prompts => apps/worker/prompts}/exploit-xss.txt (100%) rename {prompts => apps/worker/prompts}/pipeline-testing/exploit-auth.txt (100%) rename {prompts => apps/worker/prompts}/pipeline-testing/exploit-authz.txt (100%) rename {prompts => apps/worker/prompts}/pipeline-testing/exploit-injection.txt (100%) rename {prompts => apps/worker/prompts}/pipeline-testing/exploit-ssrf.txt (100%) rename {prompts => apps/worker/prompts}/pipeline-testing/exploit-xss.txt (100%) rename {prompts => apps/worker/prompts}/pipeline-testing/pre-recon-code.txt (100%) rename {prompts => apps/worker/prompts}/pipeline-testing/recon.txt (100%) rename {prompts => apps/worker/prompts}/pipeline-testing/report-executive.txt (100%) rename {prompts => apps/worker/prompts}/pipeline-testing/vuln-auth.txt (100%) rename {prompts => apps/worker/prompts}/pipeline-testing/vuln-authz.txt (100%) rename {prompts => apps/worker/prompts}/pipeline-testing/vuln-injection.txt (100%) rename {prompts => apps/worker/prompts}/pipeline-testing/vuln-ssrf.txt (100%) rename {prompts => apps/worker/prompts}/pipeline-testing/vuln-xss.txt (100%) rename {prompts => apps/worker/prompts}/pre-recon-code.txt (100%) rename {prompts => apps/worker/prompts}/recon.txt (100%) rename {prompts => apps/worker/prompts}/report-executive.txt (100%) rename {prompts => apps/worker/prompts}/shared/_exploit-scope.txt (100%) rename {prompts => apps/worker/prompts}/shared/_rules.txt (100%) rename {prompts => apps/worker/prompts}/shared/_target.txt (100%) rename {prompts => apps/worker/prompts}/shared/_vuln-scope.txt (100%) rename {prompts => apps/worker/prompts}/shared/login-instructions.txt (100%) rename {prompts => apps/worker/prompts}/vuln-auth.txt (100%) rename {prompts => apps/worker/prompts}/vuln-authz.txt (100%) rename {prompts => apps/worker/prompts}/vuln-injection.txt (100%) rename {prompts => apps/worker/prompts}/vuln-ssrf.txt (100%) rename {prompts => apps/worker/prompts}/vuln-xss.txt (100%) rename {src => apps/worker/src}/ai/audit-logger.ts (100%) rename {src => apps/worker/src}/ai/claude-executor.ts (90%) rename {src => apps/worker/src}/ai/message-handlers.ts (90%) rename {src => apps/worker/src}/ai/models.ts (100%) rename {src => apps/worker/src}/ai/output-formatters.ts (96%) rename {src => apps/worker/src}/ai/progress-manager.ts (93%) rename {src => apps/worker/src}/ai/router-utils.ts (99%) rename {src => apps/worker/src}/ai/types.ts (99%) rename {src => apps/worker/src}/audit/audit-session.ts (91%) rename {src => apps/worker/src}/audit/index.ts (100%) rename {src => apps/worker/src}/audit/log-stream.ts (97%) rename {src => apps/worker/src}/audit/logger.ts (93%) rename {src => apps/worker/src}/audit/metrics-tracker.ts (91%) rename {src => apps/worker/src}/audit/utils.ts (89%) rename {src => apps/worker/src}/audit/workflow-logger.ts (86%) rename {src => apps/worker/src}/config-parser.ts (91%) create mode 100644 apps/worker/src/paths.ts rename {src => apps/worker/src}/progress-indicator.ts (94%) rename {src => apps/worker/src}/services/agent-execution.ts (89%) rename {src => apps/worker/src}/services/config-loader.ts (89%) rename {src => apps/worker/src}/services/container.ts (97%) rename {src => apps/worker/src}/services/error-handling.ts (85%) rename {src => apps/worker/src}/services/exploitation-checker.ts (94%) rename {src => apps/worker/src}/services/git-manager.ts (86%) rename {src => apps/worker/src}/services/index.ts (99%) rename {src => apps/worker/src}/services/preflight.ts (73%) rename {src => apps/worker/src}/services/prompt-manager.ts (78%) rename {src => apps/worker/src}/services/queue-validation.ts (87%) rename {src => apps/worker/src}/services/reporting.ts (90%) rename {src => apps/worker/src}/session-manager.ts (95%) rename {src => apps/worker/src}/temporal/activities.ts (93%) rename {src => apps/worker/src}/temporal/activity-logger.ts (100%) rename {src => apps/worker/src}/temporal/shared.ts (99%) rename {src => apps/worker/src}/temporal/summary-mapper.ts (82%) rename {src => apps/worker/src}/temporal/worker.ts (91%) rename {src => apps/worker/src}/temporal/workflow-errors.ts (88%) rename {src => apps/worker/src}/temporal/workflows.ts (91%) rename {src => apps/worker/src}/temporal/workspaces.ts (82%) rename {src => apps/worker/src}/types/activity-logger.ts (100%) rename {src => apps/worker/src}/types/agents.ts (91%) rename {src => apps/worker/src}/types/audit.ts (100%) rename {src => apps/worker/src}/types/config.ts (92%) rename {src => apps/worker/src}/types/errors.ts (100%) rename {src => apps/worker/src}/types/index.ts (100%) rename {src => apps/worker/src}/types/metrics.ts (100%) rename {src => apps/worker/src}/types/result.ts (100%) rename {src => apps/worker/src}/utils/billing-detection.ts (96%) rename {src => apps/worker/src}/utils/concurrency.ts (98%) rename {src => apps/worker/src}/utils/file-io.ts (98%) rename {src => apps/worker/src}/utils/formatting.ts (100%) rename {src => apps/worker/src}/utils/functional.ts (86%) rename {src => apps/worker/src}/utils/metrics.ts (100%) create mode 100644 apps/worker/tsconfig.json create mode 100644 biome.json delete mode 100644 cli/src/commands/logs.ts delete mode 100644 configs/router-config.json delete mode 100644 package-lock.json rename {mcp-server => packages/mcp-server}/package.json (54%) rename {mcp-server => packages/mcp-server}/src/index.ts (100%) rename {mcp-server => packages/mcp-server}/src/tools/generate-totp.ts (93%) rename {mcp-server => packages/mcp-server}/src/tools/save-deliverable.ts (65%) rename {mcp-server => packages/mcp-server}/src/types/deliverables.ts (100%) rename {mcp-server => packages/mcp-server}/src/types/index.ts (100%) rename {mcp-server => packages/mcp-server}/src/types/tool-responses.ts (92%) rename {mcp-server => packages/mcp-server}/src/utils/error-formatter.ts (92%) rename {mcp-server => packages/mcp-server}/src/utils/file-operations.ts (93%) rename {mcp-server => packages/mcp-server}/src/validation/queue-validator.ts (100%) rename {mcp-server => packages/mcp-server}/src/validation/totp-validator.ts (100%) rename {mcp-server => packages/mcp-server}/tsconfig.json (77%) create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml create mode 100644 turbo.json diff --git a/.dockerignore b/.dockerignore index d076197..848de13 100644 --- a/.dockerignore +++ b/.dockerignore @@ -47,11 +47,12 @@ ehthumbs.db Thumbs.db # CLI package (runs on host, not in container) -# Keep cli/package.json so npm workspaces resolve and npm ci works with the root lockfile -cli/src/ -cli/dist/ -cli/infra/ -cli/tsconfig.json +# Keep apps/cli/package.json so pnpm workspaces resolve +apps/cli/src/ +apps/cli/dist/ +apps/cli/infra/ +apps/cli/tsconfig.json +apps/cli/tsdown.config.ts # Docker files (avoid recursive copying) Dockerfile* diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5377a3a..b133cc1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,16 +26,17 @@ jobs: with: fetch-depth: 0 + - name: Install pnpm + uses: pnpm/action-setup@v4 + - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: 24 + cache: 'pnpm' - name: Install dependencies - run: npm clean-install - - - name: Verify dependency signatures - run: npm audit signatures + run: pnpm install --frozen-lockfile - name: Probe semantic-release id: probe @@ -108,34 +109,35 @@ jobs: --certificate-identity https://github.com/${{ github.repository }}/.github/workflows/release.yml@${{ github.ref }} \ keygraph/shannon@${{ steps.build.outputs.digest }} + - name: Install pnpm + uses: pnpm/action-setup@v4 + - name: Configure npm registry uses: actions/setup-node@v6 with: node-version: 24 registry-url: https://registry.npmjs.org + cache: 'pnpm' - name: Install dependencies - run: npm clean-install - - - name: Verify dependency signatures - run: npm audit signatures + run: pnpm install --frozen-lockfile - name: Set CLI package version - run: npm version "${{ needs.preflight.outputs.version }}" --workspace cli --no-git-tag-version --allow-same-version + run: cd apps/cli && npm version "${{ needs.preflight.outputs.version }}" --no-git-tag-version --allow-same-version - - name: Sync lockfile with bumped versions - run: npm i --package-lock-only + - name: Sync lockfile with bumped version + run: pnpm install --lockfile-only - name: Build CLI - run: npm run build:cli + run: pnpm --filter @keygraph/shannon run build - name: Publish npm package - working-directory: cli + working-directory: apps/cli run: | if npm view "@keygraph/shannon@${{ needs.preflight.outputs.version }}" version 2>/dev/null; then echo "Version already published, skipping" else - npm publish --access public + pnpm publish --access public --no-git-checks fi release: @@ -151,13 +153,17 @@ jobs: with: fetch-depth: 0 + - name: Install pnpm + uses: pnpm/action-setup@v4 + - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: 24 + cache: 'pnpm' - name: Install dependencies - run: npm clean-install + run: pnpm install --frozen-lockfile - name: Create GitHub release env: diff --git a/.gitignore b/.gitignore index 3ba3126..c24f52e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ workspaces/ credentials/ dist/ repos/ +.turbo/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..4c2f52b --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +auto-install-peers=true +strict-peer-dependencies=false diff --git a/.releaserc.json b/.releaserc.json index d874d78..6e598cb 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -3,13 +3,19 @@ "plugins": [ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", - ["@semantic-release/npm", { - "npmPublish": false - }], - ["@semantic-release/github", { - "successCommentCondition": false, - "failCommentCondition": false, - "releasedLabels": false - }] + [ + "@semantic-release/npm", + { + "npmPublish": false + } + ], + [ + "@semantic-release/github", + { + "successCommentCondition": false, + "failCommentCondition": false, + "releasedLabels": false + } + ] ] } diff --git a/CLAUDE.md b/CLAUDE.md index 7ca6ce9..0857df9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,13 +13,13 @@ Shannon supports two CLI modes, auto-detected based on the current working direc | | **npx** (`npx @keygraph/shannon`) | **Local** (`./shannon`) | |---|---|---| | **Install** | Zero-install via npm | Clone the repo | -| **Image** | Pulled from Docker Hub (`keygraph/shannon:`) | Built locally (`shannon-worker`) | +| **Image** | Pulled from Docker Hub (`keygraph/shannon:latest`) | Built locally (`shannon-worker`) | | **State** | `~/.shannon/` | Project directory | | **Credentials** | `~/.shannon/config.toml` (via `shn setup`) or env vars | `./.env` | | **Config** | `~/.shannon/config.toml` (via `shn setup`) | N/A | -| **Prompts** | Bundled in Docker image | Mounted from `./prompts/` (live-editable) | +| **Prompts** | Bundled in Docker image | Mounted from `./apps/worker/prompts/` (live-editable) | -Mode detection: `./shannon` sets `SHANNON_LOCAL=1` env var; `cli/src/mode.ts` checks this to select local vs npx mode. +Mode auto-detection: local mode activates when env var `SHANNON_LOCAL=1` is set by the `./shannon` entry point (`apps/cli/src/mode.ts`). Otherwise npx mode. ### npx Quick Start @@ -45,7 +45,7 @@ echo "ANTHROPIC_API_KEY=your-key" > .env # Run ./shannon start -u -r my-repo -./shannon start -u -r my-repo -c ./configs/my-config.yaml +./shannon start -u -r my-repo -c ./apps/worker/configs/my-config.yaml ./shannon start -u -r /any/path/to/repo ``` @@ -75,55 +75,68 @@ npx @keygraph/shannon update # npx mode: pull latest image npx @keygraph/shannon uninstall # npx mode: remove ~/.shannon/ (confirms first) # Build TypeScript (development) -npm run build:all # Build core + CLI + MCP server +pnpm run build # Build all packages via Turborepo +pnpm run check # Type-check all packages +pnpm biome # Biome lint + format + import sorting check +pnpm biome:fix # Auto-fix lint, format, and import sorting ``` -TypeScript compiler options are shared via `tsconfig.base.json` at the root. All three packages extend it, overriding only `rootDir` and `outDir`. Shared devDependencies (`typescript`, `@types/node`) are hoisted to the root workspace. +**Monorepo tooling:** pnpm workspaces, Turborepo for task orchestration, Biome for linting/formatting. TypeScript compiler options shared via `tsconfig.base.json` at the root. All packages extend it, overriding only `rootDir` and `outDir`. Shared devDependencies (`typescript`, `@types/node`, `turbo`, `@biomejs/biome`) are hoisted to the root workspace. **Options:** `-c ` (YAML config), `-o ` (output directory), `-w ` (named workspace; auto-resumes if exists), `--pipeline-testing` (minimal prompts, 10s retries), `--router` (multi-model routing via [claude-code-router](https://github.com/musistudio/claude-code-router)) ## Architecture -### CLI Package (`cli/`) -Published as `@keygraph/shannon` on npm. Contains only Docker orchestration logic — no Temporal SDK, business logic, or prompts. +### Monorepo Layout -- `cli/src/index.ts` — CLI dispatcher (`setup`, `start`, `stop`, `logs`, `workspaces`, `status`, `build`, `update`, `uninstall`, `info`) -- `cli/src/mode.ts` — Auto-detection: local mode if `Dockerfile` + `docker-compose.yml` + `prompts/` exist in CWD -- `cli/src/docker.ts` — Compose lifecycle, image pull/build, ephemeral `docker run` worker spawning -- `cli/src/home.ts` — State directory management (`~/.shannon/` for npx, `./` for local) -- `cli/src/env.ts` — `.env` loading, TOML fallback (npx only) via `cli/src/config/resolver.ts`, credential validation, env flag building -- `cli/src/config/resolver.ts` — Cascading config (npx only): env vars → `~/.shannon/config.toml` (parsed with `smol-toml`) -- `cli/src/config/writer.ts` — TOML serialization and secure file persistence (0o600) -- `cli/src/commands/setup.ts` — Interactive TUI wizard (`@clack/prompts`) for provider credential setup (npx only) -- `cli/src/paths.ts` — Repo/config path resolution (bare name → `./repos/`, or any absolute/relative path) -- `cli/src/commands/` — Command handlers -- `cli/infra/compose.yml` — Bundled Temporal + router compose file for npx mode -- `shannon` — Node.js entry point (`#!/usr/bin/env node`) that delegates to `cli/dist/index.js` +``` +apps/cli/ — @keygraph/shannon (published to npm, bundled with tsdown) +apps/worker/ — @shannon/worker (private, Temporal worker + pipeline logic) +packages/mcp-server/ — @shannon/mcp-server (private, MCP tool server) +``` + +### CLI Package (`apps/cli/`) +Published as `@keygraph/shannon` on npm. Contains only Docker orchestration logic — no Temporal SDK, business logic, or prompts. Bundled with tsdown for single-file ESM output. + +- `apps/cli/src/index.ts` — CLI dispatcher (`setup`, `start`, `stop`, `logs`, `workspaces`, `status`, `build`, `update`, `uninstall`, `info`) +- `apps/cli/src/mode.ts` — Auto-detection: local mode if `SHANNON_LOCAL=1` env var is set +- `apps/cli/src/docker.ts` — Compose lifecycle, image pull/build, ephemeral `docker run` worker spawning +- `apps/cli/src/home.ts` — State directory management (`~/.shannon/` for npx, `./` for local) +- `apps/cli/src/env.ts` — `.env` loading, TOML fallback (npx only) via `apps/cli/src/config/resolver.ts`, credential validation, env flag building +- `apps/cli/src/config/resolver.ts` — Cascading config (npx only): env vars → `~/.shannon/config.toml` (parsed with `smol-toml`) +- `apps/cli/src/config/writer.ts` — TOML serialization and secure file persistence (0o600) +- `apps/cli/src/commands/setup.ts` — Interactive TUI wizard (`@clack/prompts`) for provider credential setup (npx only) +- `apps/cli/src/paths.ts` — Repo/config path resolution (bare name → `./repos/`, or any absolute/relative path) +- `apps/cli/src/commands/` — Command handlers +- `apps/cli/infra/compose.yml` — Bundled Temporal + router compose file for npx mode +- `apps/cli/tsdown.config.ts` — tsdown bundler config +- `shannon` — Node.js entry point (`#!/usr/bin/env node`) that delegates to `apps/cli/dist/index.mjs` ### Docker Architecture Infra (Temporal + router) runs via `docker-compose.yml`. Workers are ephemeral `docker run --rm` containers, one per scan, each with a unique task queue and isolated volume mounts. - `docker-compose.yml` — Infra only: `shannon-temporal` (port 7233/8233) and `shannon-router` (port 3456, optional via profile). Network: `shannon-net` -- `Dockerfile` — 2-stage build (builder + Chainguard Wolfi runtime). Entrypoint: `CMD ["node", "dist/temporal/worker.js"]` +- `Dockerfile` — 2-stage build (builder + Chainguard Wolfi runtime). Uses pnpm. Entrypoint: `CMD ["node", "apps/worker/dist/temporal/worker.js"]` - No `docker-compose.docker.yml` — host gateway handled via `--add-host` flag in CLI -### Core Modules -- `src/session-manager.ts` — Agent definitions (`AGENTS` record). Agent types in `src/types/agents.ts` -- `src/config-parser.ts` — YAML config parsing with JSON Schema validation -- `src/ai/claude-executor.ts` — Claude Agent SDK integration with retry logic -- `src/services/` — Business logic layer (Temporal-agnostic). Activities delegate here. Key: `agent-execution.ts`, `error-handling.ts`, `container.ts` -- `src/types/` — Consolidated types: `Result`, `ErrorCode`, `AgentName`, `ActivityLogger`, etc. -- `src/utils/` — Shared utilities (file I/O, formatting, concurrency) +### Worker Package (`apps/worker/`) +- `apps/worker/src/paths.ts` — Centralized path constants (`PROMPTS_DIR`, `CONFIGS_DIR`, `WORKSPACES_DIR`) +- `apps/worker/src/session-manager.ts` — Agent definitions (`AGENTS` record). Agent types in `apps/worker/src/types/agents.ts` +- `apps/worker/src/config-parser.ts` — YAML config parsing with JSON Schema validation +- `apps/worker/src/ai/claude-executor.ts` — Claude Agent SDK integration with retry logic +- `apps/worker/src/services/` — Business logic layer (Temporal-agnostic). Activities delegate here. Key: `agent-execution.ts`, `error-handling.ts`, `container.ts` +- `apps/worker/src/types/` — Consolidated types: `Result`, `ErrorCode`, `AgentName`, `ActivityLogger`, etc. +- `apps/worker/src/utils/` — Shared utilities (file I/O, formatting, concurrency) ### Temporal Orchestration Durable workflow orchestration with crash recovery, queryable progress, intelligent retry, and parallel execution (5 concurrent agents in vuln/exploit phases). -- `src/temporal/workflows.ts` — Main workflow (`pentestPipelineWorkflow`) -- `src/temporal/activities.ts` — Thin wrappers — heartbeat loop, error classification, container lifecycle. Business logic delegated to `src/services/` -- `src/temporal/activity-logger.ts` — `TemporalActivityLogger` implementation of `ActivityLogger` interface -- `src/temporal/summary-mapper.ts` — Maps `PipelineSummary` to `WorkflowSummary` -- `src/temporal/worker.ts` — Combined worker + client entry point (per-invocation task queue, submits workflow, waits for result) -- `src/temporal/shared.ts` — Types, interfaces, query definitions +- `apps/worker/src/temporal/workflows.ts` — Main workflow (`pentestPipelineWorkflow`) +- `apps/worker/src/temporal/activities.ts` — Thin wrappers — heartbeat loop, error classification, container lifecycle. Business logic delegated to `apps/worker/src/services/` +- `apps/worker/src/temporal/activity-logger.ts` — `TemporalActivityLogger` implementation of `ActivityLogger` interface +- `apps/worker/src/temporal/summary-mapper.ts` — Maps `PipelineSummary` to `WorkflowSummary` +- `apps/worker/src/temporal/worker.ts` — Combined worker + client entry point (per-invocation task queue, submits workflow, waits for result) +- `apps/worker/src/temporal/shared.ts` — Types, interfaces, query definitions ### Five-Phase Pipeline 1. **Pre-Recon** (`pre-recon`) — External scans (nmap, subfinder, whatweb) + source code analysis @@ -133,24 +146,24 @@ Durable workflow orchestration with crash recovery, queryable progress, intellig 5. **Reporting** (`report`) — Executive-level security report ### Supporting Systems -- **Configuration** — YAML configs in `configs/` with JSON Schema validation (`config-schema.json`). Supports auth settings, MFA/TOTP, and per-app testing parameters. Credential resolution — local mode: env vars → `./.env`; npx mode: env vars → `~/.shannon/config.toml` (via `shn setup`) -- **Prompts** — Per-phase templates in `prompts/` with variable substitution (`{{TARGET_URL}}`, `{{CONFIG_CONTEXT}}`). Shared partials in `prompts/shared/` via `src/services/prompt-manager.ts` -- **SDK Integration** — Uses `@anthropic-ai/claude-agent-sdk` with `maxTurns: 10_000` and `bypassPermissions` mode. Playwright MCP for browser automation, TOTP generation via MCP tool. Login flow template at `prompts/shared/login-instructions.txt` supports form, SSO, API, and basic auth -- **Audit System** — Crash-safe append-only logging in `workspaces/{hostname}_{sessionId}/`. Tracks session metrics, per-agent logs, prompts, and deliverables. WorkflowLogger (`audit/workflow-logger.ts`) provides unified human-readable per-workflow logs, backed by LogStream (`audit/log-stream.ts`) shared stream primitive +- **Configuration** — YAML configs in `apps/worker/configs/` with JSON Schema validation (`config-schema.json`). Supports auth settings, MFA/TOTP, and per-app testing parameters. Credential resolution — local mode: env vars → `./.env`; npx mode: env vars → `~/.shannon/config.toml` (via `shn setup`) +- **Prompts** — Per-phase templates in `apps/worker/prompts/` with variable substitution (`{{TARGET_URL}}`, `{{CONFIG_CONTEXT}}`). Shared partials in `apps/worker/prompts/shared/` via `apps/worker/src/services/prompt-manager.ts` +- **SDK Integration** — Uses `@anthropic-ai/claude-agent-sdk` with `maxTurns: 10_000` and `bypassPermissions` mode. Playwright MCP for browser automation, TOTP generation via MCP tool. Login flow template at `apps/worker/prompts/shared/login-instructions.txt` supports form, SSO, API, and basic auth +- **Audit System** — Crash-safe append-only logging in `workspaces/{hostname}_{sessionId}/`. Tracks session metrics, per-agent logs, prompts, and deliverables. WorkflowLogger (`apps/worker/src/audit/workflow-logger.ts`) provides unified human-readable per-workflow logs, backed by LogStream (`apps/worker/src/audit/log-stream.ts`) shared stream primitive - **Deliverables** — Saved to `deliverables/` in the target repo via the `save_deliverable` MCP tool -- **Workspaces & Resume** — Named workspaces via `-w ` or auto-named from URL+timestamp. Resume detects completed agents via `session.json`. `loadResumeState()` in `src/temporal/activities.ts` validates deliverable existence, restores git checkpoints, and cleans up incomplete deliverables. Workspace listing via `src/temporal/workspaces.ts` +- **Workspaces & Resume** — Named workspaces via `-w ` or auto-named from URL+timestamp. Resume detects completed agents via `session.json`. `loadResumeState()` in `apps/worker/src/temporal/activities.ts` validates deliverable existence, restores git checkpoints, and cleans up incomplete deliverables. Workspace listing via `apps/worker/src/temporal/workspaces.ts` ## Development Notes ### Adding a New Agent -1. Define agent in `src/session-manager.ts` (add to `AGENTS` record). `ALL_AGENTS`/`AgentName` types live in `src/types/agents.ts` -2. Create prompt template in `prompts/` (e.g., `vuln-newtype.txt`) -3. Two-layer pattern: add a thin activity wrapper in `src/temporal/activities.ts` (heartbeat + error classification). `AgentExecutionService` in `src/services/agent-execution.ts` handles the agent lifecycle automatically via the `AGENTS` registry -4. Register activity in `src/temporal/workflows.ts` within the appropriate phase +1. Define agent in `apps/worker/src/session-manager.ts` (add to `AGENTS` record). `ALL_AGENTS`/`AgentName` types live in `apps/worker/src/types/agents.ts` +2. Create prompt template in `apps/worker/prompts/` (e.g., `vuln-newtype.txt`) +3. Two-layer pattern: add a thin activity wrapper in `apps/worker/src/temporal/activities.ts` (heartbeat + error classification). `AgentExecutionService` in `apps/worker/src/services/agent-execution.ts` handles the agent lifecycle automatically via the `AGENTS` registry +4. Register activity in `apps/worker/src/temporal/workflows.ts` within the appropriate phase ### Modifying Prompts - Variable substitution: `{{TARGET_URL}}`, `{{CONFIG_CONTEXT}}`, `{{LOGIN_INSTRUCTIONS}}` -- Shared partials in `prompts/shared/` included via `src/services/prompt-manager.ts` +- Shared partials in `apps/worker/prompts/shared/` included via `apps/worker/src/services/prompt-manager.ts` - Test with `--pipeline-testing` for fast iteration ### Key Design Patterns @@ -158,8 +171,8 @@ Durable workflow orchestration with crash recovery, queryable progress, intellig - **Progressive Analysis** — Each phase builds on previous results - **SDK-First** — Claude Agent SDK handles autonomous analysis - **Modular Error Handling** — `ErrorCode` enum, `Result` for explicit error propagation, automatic retry (3 attempts per agent) -- **Services Boundary** — Activities are thin Temporal wrappers; `src/services/` owns business logic, accepts `ActivityLogger`, returns `Result`. No Temporal imports in services -- **DI Container** — Per-workflow in `src/services/container.ts`. `AuditSession` excluded (parallel safety) +- **Services Boundary** — Activities are thin Temporal wrappers; `apps/worker/src/services/` owns business logic, accepts `ActivityLogger`, returns `Result`. No Temporal imports in services +- **DI Container** — Per-workflow in `apps/worker/src/services/container.ts`. `AuditSession` excluded (parallel safety) - **Ephemeral Workers** — Each scan runs in its own `docker run --rm` container with a per-invocation task queue. Temporal routes activities by queue name, so per-scan queues ensure activities never land on a worker with the wrong repo mounted ### Security @@ -167,6 +180,9 @@ Defensive security tool only. Use only on systems you own or have explicit permi ## Code Style Guidelines +### Formatting +Biome handles formatting and linting. Run `pnpm biome:fix` to auto-fix. Config in `biome.json`: single quotes, semicolons, trailing commas, 2-space indent, 120 char line width. + ### Clarity Over Brevity - Optimize for readability, not line count — three clear lines beat one dense expression - Use descriptive names that convey intent @@ -209,15 +225,15 @@ Comments must be **timeless** — no references to this conversation, refactorin ## Key Files -**CLI:** `shannon` (entry point), `cli/src/index.ts` (dispatcher), `cli/src/docker.ts` (orchestration), `cli/src/mode.ts` (auto-detection) +**CLI:** `shannon` (entry point), `apps/cli/src/index.ts` (dispatcher), `apps/cli/src/docker.ts` (orchestration), `apps/cli/src/mode.ts` (auto-detection) -**Entry Points:** `src/temporal/workflows.ts`, `src/temporal/activities.ts`, `src/temporal/worker.ts` +**Entry Points:** `apps/worker/src/temporal/workflows.ts`, `apps/worker/src/temporal/activities.ts`, `apps/worker/src/temporal/worker.ts` -**Core Logic:** `src/session-manager.ts`, `src/ai/claude-executor.ts`, `src/config-parser.ts`, `src/services/`, `src/audit/` +**Core Logic:** `apps/worker/src/session-manager.ts`, `apps/worker/src/ai/claude-executor.ts`, `apps/worker/src/config-parser.ts`, `apps/worker/src/services/`, `apps/worker/src/audit/` -**Config:** `docker-compose.yml`, `cli/infra/compose.yml`, `configs/`, `prompts/`, `tsconfig.base.json` (shared compiler options) +**Config:** `docker-compose.yml`, `apps/cli/infra/compose.yml`, `apps/worker/configs/`, `apps/worker/prompts/`, `tsconfig.base.json` (shared compiler options), `turbo.json`, `biome.json` -**CI/CD:** `.github/workflows/release.yml` (semantic-release: npm publish + Docker push + GitHub release), `.github/workflows/rollback.yml` (rollback npm and Docker latest tags) +**CI/CD:** `.github/workflows/release.yml` (Docker Hub push + npm publish + GitHub release, manual dispatch) ## Troubleshooting diff --git a/Dockerfile b/Dockerfile index dacff6e..6b82217 100644 --- a/Dockerfile +++ b/Dockerfile @@ -50,16 +50,27 @@ RUN git clone --depth 1 https://github.com/urbanadventurer/WhatWeb.git /opt/what # Install Python-based tools RUN pip3 install --no-cache-dir schemathesis +# Install pnpm +RUN npm install -g pnpm@10 + # Build Node.js application in builder to avoid QEMU emulation failures in CI WORKDIR /app -COPY package*.json ./ -COPY cli/package.json ./cli/ -COPY mcp-server/package*.json ./mcp-server/ -RUN npm ci && npm cache clean --force + +# Copy workspace manifests for install layer caching +COPY package.json pnpm-workspace.yaml pnpm-lock.yaml .npmrc ./ +COPY apps/worker/package.json ./apps/worker/ +COPY apps/cli/package.json ./apps/cli/ +COPY packages/mcp-server/package.json ./packages/mcp-server/ + +RUN pnpm install --frozen-lockfile + COPY . . -RUN cd mcp-server && npm run build && cd .. && npm run build -RUN npm prune --production && \ - cd mcp-server && npm prune --production + +# Build mcp-server (dependency) then worker. CLI not needed in Docker +RUN pnpm --filter @shannon/mcp-server run build && \ + pnpm --filter @shannon/worker run build + +RUN pnpm prune --prod # Runtime stage - Minimal production image FROM cgr.dev/chainguard/wolfi-base:latest AS runtime @@ -119,13 +130,17 @@ RUN addgroup -g 1001 pentest && \ # Set working directory WORKDIR /app -# Copy built application from builder -COPY --from=builder /app /app +# Copy only what the worker needs (skip CLI source, infra, tsdown artifacts) +COPY --from=builder /app/package.json /app/pnpm-workspace.yaml /app/pnpm-lock.yaml /app/.npmrc /app/ +COPY --from=builder /app/node_modules /app/node_modules +COPY --from=builder /app/apps/worker /app/apps/worker +COPY --from=builder /app/apps/cli/package.json /app/apps/cli/package.json +COPY --from=builder /app/packages /app/packages RUN npm install -g @anthropic-ai/claude-code # Create directories for session data and ensure proper permissions -RUN mkdir -p /app/sessions /app/deliverables /app/repos /app/configs /app/workspaces && \ +RUN mkdir -p /app/sessions /app/deliverables /app/repos /app/workspaces && \ mkdir -p /tmp/.cache /tmp/.config /tmp/.npm && \ chmod 777 /app && \ chmod 777 /tmp/.cache && \ @@ -152,4 +167,4 @@ RUN git config --global user.email "agent@localhost" && \ git config --global user.name "Pentest Agent" && \ git config --global --add safe.directory '*' -CMD ["node", "dist/temporal/worker.js"] +CMD ["node", "apps/worker/dist/temporal/worker.js"] diff --git a/cli/.npmignore b/apps/cli/.npmignore similarity index 100% rename from cli/.npmignore rename to apps/cli/.npmignore diff --git a/cli/infra/compose.yml b/apps/cli/infra/compose.yml similarity index 100% rename from cli/infra/compose.yml rename to apps/cli/infra/compose.yml diff --git a/cli/infra/router-config.json b/apps/cli/infra/router-config.json similarity index 91% rename from cli/infra/router-config.json rename to apps/cli/infra/router-config.json index cf57b1e..9cc0422 100644 --- a/cli/infra/router-config.json +++ b/apps/cli/infra/router-config.json @@ -19,9 +19,7 @@ "name": "openrouter", "api_base_url": "https://openrouter.ai/api/v1/chat/completions", "api_key": "$OPENROUTER_API_KEY", - "models": [ - "google/gemini-3-flash-preview" - ], + "models": ["google/gemini-3-flash-preview"], "transformer": { "use": ["openrouter"] } diff --git a/cli/package.json b/apps/cli/package.json similarity index 75% rename from cli/package.json rename to apps/cli/package.json index 7583dca..9110bb9 100644 --- a/cli/package.json +++ b/apps/cli/package.json @@ -3,20 +3,22 @@ "version": "0.0.0", "description": "Shannon - Autonomous white-box AI pentester for web applications and APIs by Keygraph", "type": "module", - "main": "dist/index.js", + "main": "dist/index.mjs", "bin": { - "shannon": "dist/index.js" + "shannon": "dist/index.mjs" }, "files": [ "dist", "infra" ], "scripts": { - "build": "tsc", - "dev": "tsc --watch" + "build": "tsdown", + "check": "tsc --noEmit", + "clean": "rm -rf dist" }, "dependencies": { "@clack/prompts": "^1.1.0", + "chokidar": "^5.0.0", "dotenv": "^17.3.1", "smol-toml": "^1.6.0" }, @@ -37,9 +39,12 @@ "repository": { "type": "git", "url": "git+https://github.com/KeygraphHQ/shannon.git", - "directory": "cli" + "directory": "apps/cli" }, "engines": { "node": ">=18" + }, + "devDependencies": { + "tsdown": "^0.21.2" } } diff --git a/cli/src/commands/build.ts b/apps/cli/src/commands/build.ts similarity index 100% rename from cli/src/commands/build.ts rename to apps/cli/src/commands/build.ts index dfbb8bd..66bf878 100644 --- a/cli/src/commands/build.ts +++ b/apps/cli/src/commands/build.ts @@ -3,8 +3,8 @@ * Only available in local mode (running from cloned repository). */ -import { isLocal } from '../mode.js'; import { buildImage } from '../docker.js'; +import { isLocal } from '../mode.js'; export function build(noCache: boolean): void { if (!isLocal()) { diff --git a/apps/cli/src/commands/logs.ts b/apps/cli/src/commands/logs.ts new file mode 100644 index 0000000..4bee242 --- /dev/null +++ b/apps/cli/src/commands/logs.ts @@ -0,0 +1,106 @@ +/** + * `shannon logs` command — tail a workspace's workflow log. + * + * Uses chokidar for reliable cross-platform file watching and + * bounded synchronous reads to prevent duplicate output. + */ + +import fs from 'node:fs'; +import path from 'node:path'; +import { watch } from 'chokidar'; +import { getWorkspacesDir } from '../home.js'; + +// Match the exact line the worker writes — anchored to prevent false positives from agent output +const COMPLETION_PATTERN = /^Workflow (COMPLETED|FAILED)$/m; + +/** Read a byte range from a file and return it as a UTF-8 string. */ +function readRange(filePath: string, start: number, end: number): string { + const length = end - start; + const buffer = Buffer.alloc(length); + const fd = fs.openSync(filePath, 'r'); + try { + fs.readSync(fd, buffer, 0, length, start); + } finally { + fs.closeSync(fd); + } + return buffer.toString('utf-8'); +} + +/** Resolve a workspace ID to its workflow.log path, or exit with an error. */ +function resolveLogFile(workspaceId: string): string { + const workspacesDir = getWorkspacesDir(); + + // 1. Direct match + const directPath = path.join(workspacesDir, workspaceId, 'workflow.log'); + if (fs.existsSync(directPath)) return directPath; + + // 2. Resume workflow ID (e.g. workspace_resume_123) + const resumeBase = workspaceId.replace(/_resume_\d+$/, ''); + if (resumeBase !== workspaceId) { + const resumePath = path.join(workspacesDir, resumeBase, 'workflow.log'); + if (fs.existsSync(resumePath)) return resumePath; + } + + // 3. Named workspace ID (e.g. workspace_shannon-123) + const namedBase = workspaceId.replace(/_shannon-\d+$/, ''); + if (namedBase !== workspaceId) { + const namedPath = path.join(workspacesDir, namedBase, 'workflow.log'); + if (fs.existsSync(namedPath)) return namedPath; + } + + console.error(`ERROR: Workflow log not found for: ${workspaceId}`); + console.error(''); + console.error('Possible causes:'); + console.error(" - Workflow hasn't started yet"); + console.error(' - Workspace ID is incorrect'); + console.error(''); + console.error('Check the Temporal Web UI at http://localhost:8233 for workflow details'); + process.exit(1); +} + +export function logs(workspaceId: string): void { + const logFile = resolveLogFile(workspaceId); + let position = 0; + + /** + * Output any new content appended since the last read. + * Returns true when the workflow completion marker is detected. + */ + function flush(): boolean { + try { + const { size } = fs.statSync(logFile); + if (size <= position) return false; + + const data = readRange(logFile, position, size); + process.stdout.write(data); + position = size; + + return COMPLETION_PATTERN.test(data); + } catch { + // File deleted or unreadable — treat as done + return true; + } + } + + console.log(`Tailing workflow log: ${logFile}`); + + // 1. Output existing content + if (flush()) { + process.exit(0); + } + + // 2. Watch for appended content via chokidar + const watcher = watch(logFile, { persistent: true }); + + const shutdown = (): void => { + watcher.close().finally(() => process.exit(0)); + // Safety net — force exit if watcher.close() stalls + setTimeout(() => process.exit(0), 1000).unref(); + }; + + watcher.on('change', () => { + if (flush()) shutdown(); + }); + + process.on('SIGINT', shutdown); +} diff --git a/cli/src/commands/setup.ts b/apps/cli/src/commands/setup.ts similarity index 95% rename from cli/src/commands/setup.ts rename to apps/cli/src/commands/setup.ts index da57fc7..8716a25 100644 --- a/cli/src/commands/setup.ts +++ b/apps/cli/src/commands/setup.ts @@ -6,8 +6,8 @@ */ import fs from 'node:fs'; -import path from 'node:path'; import os from 'node:os'; +import path from 'node:path'; import * as p from '@clack/prompts'; import { type ShannonConfig, saveConfig } from '../config/writer.js'; @@ -42,10 +42,14 @@ export async function setup(): Promise { async function setupProvider(provider: Provider): Promise { switch (provider) { - case 'anthropic': return setupAnthropic(); - case 'bedrock': return setupBedrock(); - case 'vertex': return setupVertex(); - case 'router': return setupRouter(); + case 'anthropic': + return setupAnthropic(); + case 'bedrock': + return setupBedrock(); + case 'vertex': + return setupVertex(); + case 'router': + return setupRouter(); } } @@ -190,7 +194,7 @@ async function setupRouter(): Promise { if (p.isCancel(routerProvider)) return cancelAndExit(); const apiKey = await promptSecret( - routerProvider === 'openai' ? 'Enter your OpenAI API key' : 'Enter your OpenRouter API key' + routerProvider === 'openai' ? 'Enter your OpenAI API key' : 'Enter your OpenRouter API key', ); let defaultModel: string; @@ -207,9 +211,7 @@ async function setupRouter(): Promise { } else { const model = await p.select({ message: 'Default model', - options: [ - { value: 'google/gemini-3-flash-preview' as const, label: 'Google Gemini 3 Flash Preview' }, - ], + options: [{ value: 'google/gemini-3-flash-preview' as const, label: 'Google Gemini 3 Flash Preview' }], }); if (p.isCancel(model)) return cancelAndExit(); defaultModel = `openrouter,${model}`; diff --git a/cli/src/commands/start.ts b/apps/cli/src/commands/start.ts similarity index 91% rename from cli/src/commands/start.ts rename to apps/cli/src/commands/start.ts index 85a1cf4..cd8aa4e 100644 --- a/cli/src/commands/start.ts +++ b/apps/cli/src/commands/start.ts @@ -5,14 +5,14 @@ * and npx mode (Docker Hub pull, ~/.shannon/). */ +import { execFileSync } from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; -import { execFileSync } from 'node:child_process'; +import { ensureImage, ensureInfra, randomSuffix, spawnWorker } from '../docker.js'; +import { buildEnvFlags, isRouterConfigured, loadEnv, validateCredentials } from '../env.js'; +import { getCredentialsDir, getCredentialsPath, getWorkspacesDir, initHome } from '../home.js'; import { isLocal } from '../mode.js'; -import { initHome, getWorkspacesDir, getCredentialsPath, getCredentialsDir } from '../home.js'; -import { loadEnv, buildEnvFlags, validateCredentials, isRouterConfigured } from '../env.js'; -import { resolveRepo, resolveConfig, ensureDeliverables } from '../paths.js'; -import { ensureInfra, ensureImage, spawnWorker, randomSuffix } from '../docker.js'; +import { ensureDeliverables, resolveConfig, resolveRepo } from '../paths.js'; import { displaySplash } from '../splash.js'; export interface StartArgs { @@ -83,7 +83,7 @@ export function start(args: StartArgs): void { } // 11. Resolve prompts directory (local mode only) - const promptsDir = isLocal() ? path.resolve('prompts') : undefined; + const promptsDir = isLocal() ? path.resolve('apps/worker/prompts') : undefined; // 12. Display splash screen displaySplash(isLocal() ? undefined : args.version); @@ -165,8 +165,14 @@ export function start(args: StartArgs): void { } }; - process.on('SIGINT', () => { cleanup(); process.exit(0); }); - process.on('SIGTERM', () => { cleanup(); process.exit(0); }); + process.on('SIGINT', () => { + cleanup(); + process.exit(0); + }); + process.on('SIGTERM', () => { + cleanup(); + process.exit(0); + }); process.on('exit', cleanup); } @@ -176,7 +182,7 @@ function printInfo( workspace: string, workflowId: string, repoPath: string, - workspacesDir: string + workspacesDir: string, ): void { const logsCmd = isLocal() ? `./shannon logs ${workspace}` : `npx @keygraph/shannon logs ${workspace}`; const reportsPath = path.join(workspacesDir, workspace); diff --git a/cli/src/commands/status.ts b/apps/cli/src/commands/status.ts similarity index 100% rename from cli/src/commands/status.ts rename to apps/cli/src/commands/status.ts diff --git a/cli/src/commands/stop.ts b/apps/cli/src/commands/stop.ts similarity index 89% rename from cli/src/commands/stop.ts rename to apps/cli/src/commands/stop.ts index 4460333..f123013 100644 --- a/cli/src/commands/stop.ts +++ b/apps/cli/src/commands/stop.ts @@ -3,7 +3,7 @@ */ import * as p from '@clack/prompts'; -import { stopWorkers, stopInfra } from '../docker.js'; +import { stopInfra, stopWorkers } from '../docker.js'; export async function stop(clean: boolean): Promise { if (clean) { diff --git a/cli/src/commands/uninstall.ts b/apps/cli/src/commands/uninstall.ts similarity index 95% rename from cli/src/commands/uninstall.ts rename to apps/cli/src/commands/uninstall.ts index 26aa9a2..e65564e 100644 --- a/cli/src/commands/uninstall.ts +++ b/apps/cli/src/commands/uninstall.ts @@ -3,10 +3,10 @@ */ import fs from 'node:fs'; -import path from 'node:path'; import os from 'node:os'; +import path from 'node:path'; import * as p from '@clack/prompts'; -import { stopWorkers, stopInfra } from '../docker.js'; +import { stopInfra, stopWorkers } from '../docker.js'; const SHANNON_HOME = path.join(os.homedir(), '.shannon'); diff --git a/cli/src/commands/update.ts b/apps/cli/src/commands/update.ts similarity index 100% rename from cli/src/commands/update.ts rename to apps/cli/src/commands/update.ts diff --git a/cli/src/commands/workspaces.ts b/apps/cli/src/commands/workspaces.ts similarity index 63% rename from cli/src/commands/workspaces.ts rename to apps/cli/src/commands/workspaces.ts index b5e5eb7..e9c8b49 100644 --- a/cli/src/commands/workspaces.ts +++ b/apps/cli/src/commands/workspaces.ts @@ -3,21 +3,29 @@ */ import { execFileSync } from 'node:child_process'; -import { getWorkspacesDir } from '../home.js'; import { getWorkerImage } from '../docker.js'; +import { getWorkspacesDir } from '../home.js'; export function workspaces(version: string): void { const workspacesDir = getWorkspacesDir(); const image = getWorkerImage(version); try { - execFileSync('docker', [ - 'run', '--rm', - '-v', `${workspacesDir}:/app/workspaces`, - '-e', 'WORKSPACES_DIR=/app/workspaces', - image, - 'node', 'dist/temporal/workspaces.js', - ], { stdio: 'inherit' }); + execFileSync( + 'docker', + [ + 'run', + '--rm', + '-v', + `${workspacesDir}:/app/workspaces`, + '-e', + 'WORKSPACES_DIR=/app/workspaces', + image, + 'node', + 'apps/worker/dist/temporal/workspaces.js', + ], + { stdio: 'inherit' }, + ); } catch { console.error('ERROR: Failed to list workspaces. Is the Docker image available?'); console.error(` Run: docker pull ${image}`); diff --git a/cli/src/config/resolver.ts b/apps/cli/src/config/resolver.ts similarity index 96% rename from cli/src/config/resolver.ts rename to apps/cli/src/config/resolver.ts index e1a889a..a6715cd 100644 --- a/cli/src/config/resolver.ts +++ b/apps/cli/src/config/resolver.ts @@ -118,11 +118,7 @@ function buildSchema(): Map> { } /** Check that a provider section has all required fields and dependencies. */ -function validateProviderFields( - config: TOMLConfig, - provider: string, - errors: string[], -): void { +function validateProviderFields(config: TOMLConfig, provider: string, errors: string[]): void { const section = config[provider] as Record | undefined; if (!section) return; const keys = Object.keys(section); @@ -171,11 +167,7 @@ function validateProviderFields( } /** Bedrock and Vertex require a [models] section with all three tiers. */ -function validateModelTiers( - config: TOMLConfig, - provider: string, - errors: string[], -): void { +function validateModelTiers(config: TOMLConfig, provider: string, errors: string[]): void { const models = config.models as Record | undefined; if (!models || typeof models !== 'object') { errors.push(`[${provider}] requires a [models] section with small, medium, and large`); @@ -222,9 +214,7 @@ function validateConfig(config: TOMLConfig): string[] { } if (typeof value !== expectedType) { - errors.push( - `[${section}].${key} must be ${expectedType}, got ${typeof value}` - ); + errors.push(`[${section}].${key} must be ${expectedType}, got ${typeof value}`); continue; } @@ -243,7 +233,7 @@ function validateConfig(config: TOMLConfig): string[] { }); if (present.length > 1) { errors.push( - `Multiple providers configured: [${present.join('], [')}]. Only one provider section is allowed at a time` + `Multiple providers configured: [${present.join('], [')}]. Only one provider section is allowed at a time`, ); } diff --git a/cli/src/config/writer.ts b/apps/cli/src/config/writer.ts similarity index 100% rename from cli/src/config/writer.ts rename to apps/cli/src/config/writer.ts diff --git a/cli/src/docker.ts b/apps/cli/src/docker.ts similarity index 79% rename from cli/src/docker.ts rename to apps/cli/src/docker.ts index 4905431..f411995 100644 --- a/cli/src/docker.ts +++ b/apps/cli/src/docker.ts @@ -5,10 +5,10 @@ * NPX mode: pulls from Docker Hub, uses bundled compose.yml. */ -import { execFileSync, spawn, type ChildProcess } from 'node:child_process'; -import path from 'node:path'; -import os from 'node:os'; +import { type ChildProcess, execFileSync, spawn } from 'node:child_process'; import crypto from 'node:crypto'; +import os from 'node:os'; +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { getMode } from './mode.js'; @@ -56,37 +56,77 @@ function runOutput(cmd: string, args: string[]): string { */ export function isTemporalReady(): boolean { const output = runOutput('docker', [ - 'exec', 'shannon-temporal', - 'temporal', 'operator', 'cluster', 'health', '--address', 'localhost:7233', + 'exec', + 'shannon-temporal', + 'temporal', + 'operator', + 'cluster', + 'health', + '--address', + 'localhost:7233', ]); return output.includes('SERVING'); } +/** Check if the router container is running and healthy. */ +function isRouterReady(): boolean { + const status = runOutput('docker', ['inspect', '--format', '{{.State.Health.Status}}', 'shannon-router']); + return status === 'healthy'; +} + /** * Ensure Temporal (and optionally router) are running via compose. + * If Temporal is already up but router is needed and missing, starts router only. */ export function ensureInfra(useRouter: boolean): void { - if (isTemporalReady()) { + const temporalReady = isTemporalReady(); + const routerNeeded = useRouter && !isRouterReady(); + + if (temporalReady && !routerNeeded) { return; } const composeFile = getComposeFile(); - console.log('Starting Shannon infrastructure...'); const composeArgs = ['compose', '-f', composeFile]; if (useRouter) composeArgs.push('--profile', 'router'); composeArgs.push('up', '-d'); + + if (temporalReady && routerNeeded) { + console.log('Starting router...'); + } else { + console.log('Starting Shannon infrastructure...'); + } execFileSync('docker', composeArgs, { stdio: 'inherit' }); - console.log('Waiting for Temporal to be ready...'); - for (let i = 0; i < 30; i++) { - if (isTemporalReady()) { - console.log('Temporal is ready!'); - return; + // Wait for Temporal if it wasn't already running + if (!temporalReady) { + console.log('Waiting for Temporal to be ready...'); + for (let i = 0; i < 30; i++) { + if (isTemporalReady()) { + console.log('Temporal is ready!'); + break; + } + if (i === 29) { + console.error('Timeout waiting for Temporal'); + process.exit(1); + } + execFileSync('sleep', ['2']); } - execFileSync('sleep', ['2']); } - console.error('Timeout waiting for Temporal'); - process.exit(1); + + // Wait for router if needed + if (routerNeeded) { + console.log('Waiting for router to be ready...'); + for (let i = 0; i < 15; i++) { + if (isRouterReady()) { + console.log('Router is ready!'); + return; + } + execFileSync('sleep', ['2']); + } + console.error('Timeout waiting for router'); + process.exit(1); + } } /** @@ -173,7 +213,7 @@ export function spawnWorker(opts: WorkerOptions): ChildProcess { // Local mode: mount prompts for live editing if (opts.promptsDir) { - args.push('-v', `${opts.promptsDir}:/app/prompts:ro`); + args.push('-v', `${opts.promptsDir}:/app/apps/worker/prompts:ro`); } if (opts.config) { @@ -202,7 +242,7 @@ export function spawnWorker(opts: WorkerOptions): ChildProcess { args.push(getWorkerImage(opts.version)); // Worker command - args.push('node', 'dist/temporal/worker.js', opts.url, opts.repo.containerPath); + args.push('node', 'apps/worker/dist/temporal/worker.js', opts.url, opts.repo.containerPath); args.push('--task-queue', opts.taskQueue); if (opts.config) { args.push('--config', opts.config.containerPath); @@ -262,9 +302,7 @@ export function pullImage(version: string): void { * Remove old keygraph/shannon images that don't match the current version. */ function pruneOldImages(currentVersion: string): void { - const output = runOutput('docker', [ - 'images', NPX_IMAGE_REPO, '--format', '{{.Tag}}', - ]); + const output = runOutput('docker', ['images', NPX_IMAGE_REPO, '--format', '{{.Tag}}']); if (!output) return; const currentTag = currentVersion; @@ -279,7 +317,10 @@ function pruneOldImages(currentVersion: string): void { */ export function listRunningWorkers(): string { return runOutput('docker', [ - 'ps', '--filter', 'name=shannon-worker-', - '--format', 'table {{.Names}}\t{{.Status}}\t{{.RunningFor}}', + 'ps', + '--filter', + 'name=shannon-worker-', + '--format', + 'table {{.Names}}\t{{.Status}}\t{{.RunningFor}}', ]); } diff --git a/cli/src/env.ts b/apps/cli/src/env.ts similarity index 95% rename from cli/src/env.ts rename to apps/cli/src/env.ts index c14d1b0..a0ce8e1 100644 --- a/cli/src/env.ts +++ b/apps/cli/src/env.ts @@ -6,8 +6,8 @@ */ import dotenv from 'dotenv'; -import { getMode } from './mode.js'; import { resolveConfig } from './config/resolver.js'; +import { getMode } from './mode.js'; /** Environment variables forwarded to worker containers. */ const FORWARD_VARS = [ @@ -148,9 +148,10 @@ export function validateCredentials(): CredentialValidation { return { valid: true, mode: 'router' }; } - const hint = getMode() === 'local' - ? `No credentials found. Set ANTHROPIC_API_KEY in .env or export it.` - : `Authentication not configured. Export variables or run 'npx @keygraph/shannon setup'.`; + const hint = + getMode() === 'local' + ? `No credentials found. Set ANTHROPIC_API_KEY in .env or export it.` + : `Authentication not configured. Export variables or run 'npx @keygraph/shannon setup'.`; return { valid: false, mode: 'api-key', diff --git a/cli/src/home.ts b/apps/cli/src/home.ts similarity index 94% rename from cli/src/home.ts rename to apps/cli/src/home.ts index 3f0d172..913a93f 100644 --- a/cli/src/home.ts +++ b/apps/cli/src/home.ts @@ -6,8 +6,8 @@ */ import fs from 'node:fs'; -import path from 'node:path'; import os from 'node:os'; +import path from 'node:path'; import { getMode } from './mode.js'; const SHANNON_HOME = path.join(os.homedir(), '.shannon'); @@ -17,9 +17,7 @@ export function getConfigFile(): string { } export function getWorkspacesDir(): string { - return getMode() === 'local' - ? path.resolve('workspaces') - : path.join(SHANNON_HOME, 'workspaces'); + return getMode() === 'local' ? path.resolve('workspaces') : path.join(SHANNON_HOME, 'workspaces'); } /** diff --git a/cli/src/index.ts b/apps/cli/src/index.ts similarity index 86% rename from cli/src/index.ts rename to apps/cli/src/index.ts index 0074923..bceae3a 100644 --- a/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env node - /** * Shannon CLI — AI Penetration Testing Framework * @@ -14,16 +12,16 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { getMode } from './mode.js'; -import { start } from './commands/start.js'; -import { stop } from './commands/stop.js'; -import { logs } from './commands/logs.js'; -import { workspaces } from './commands/workspaces.js'; -import { status } from './commands/status.js'; -import { update } from './commands/update.js'; import { build } from './commands/build.js'; +import { logs } from './commands/logs.js'; import { setup } from './commands/setup.js'; +import { start } from './commands/start.js'; +import { status } from './commands/status.js'; +import { stop } from './commands/stop.js'; import { uninstall } from './commands/uninstall.js'; +import { update } from './commands/update.js'; +import { workspaces } from './commands/workspaces.js'; +import { getMode } from './mode.js'; import { displaySplash } from './splash.js'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -45,16 +43,24 @@ function showHelp(): void { console.log(` Shannon - AI Penetration Testing Framework -Usage:${mode === 'local' ? '' : ` - ${prefix} setup Configure credentials`} +Usage:${ + mode === 'local' + ? '' + : ` + ${prefix} setup Configure credentials` + } ${prefix} start --url --repo [options] Start a pentest scan ${prefix} stop [--clean] Stop all containers ${prefix} workspaces List all workspaces ${prefix} logs Tail workflow log - ${prefix} status Show running workers${mode === 'local' ? ` - ${prefix} build [--no-cache] Build worker image` : ` + ${prefix} status Show running workers${ + mode === 'local' + ? ` + ${prefix} build [--no-cache] Build worker image` + : ` ${prefix} update Pull latest image - ${prefix} uninstall Remove ~/.shannon/ and all data`} + ${prefix} uninstall Remove ~/.shannon/ and all data` + } ${prefix} info Show splash screen ${prefix} help Show this help @@ -72,9 +78,13 @@ Examples: ${prefix} start -u https://example.com -r /path/to/repo -c config.yaml -w q1-audit ${prefix} logs q1-audit ${prefix} stop --clean -${mode === 'local' ? ` -State directory: ./workspaces/` : ` -State directory: ~/.shannon/`} +${ + mode === 'local' + ? ` +State directory: ./workspaces/` + : ` +State directory: ~/.shannon/` +} Monitor workflows at http://localhost:8233 `); } @@ -105,23 +115,38 @@ function parseStartArgs(argv: string[]): ParsedStartArgs { switch (arg) { case '-u': case '--url': - if (next && !next.startsWith('-')) { url = next; i++; } + if (next && !next.startsWith('-')) { + url = next; + i++; + } break; case '-r': case '--repo': - if (next && !next.startsWith('-')) { repo = next; i++; } + if (next && !next.startsWith('-')) { + repo = next; + i++; + } break; case '-c': case '--config': - if (next && !next.startsWith('-')) { config = next; i++; } + if (next && !next.startsWith('-')) { + config = next; + i++; + } break; case '-w': case '--workspace': - if (next && !next.startsWith('-')) { workspace = next; i++; } + if (next && !next.startsWith('-')) { + workspace = next; + i++; + } break; case '-o': case '--output': - if (next && !next.startsWith('-')) { output = next; i++; } + if (next && !next.startsWith('-')) { + output = next; + i++; + } break; case '--pipeline-testing': pipelineTesting = true; @@ -142,7 +167,15 @@ function parseStartArgs(argv: string[]): ParsedStartArgs { process.exit(1); } - return { url, repo, pipelineTesting, router, ...(config && { config }), ...(workspace && { workspace }), ...(output && { output }) }; + return { + url, + repo, + pipelineTesting, + router, + ...(config && { config }), + ...(workspace && { workspace }), + ...(output && { output }), + }; } // === Main Dispatch === diff --git a/cli/src/mode.ts b/apps/cli/src/mode.ts similarity index 100% rename from cli/src/mode.ts rename to apps/cli/src/mode.ts diff --git a/cli/src/paths.ts b/apps/cli/src/paths.ts similarity index 100% rename from cli/src/paths.ts rename to apps/cli/src/paths.ts diff --git a/cli/src/splash.ts b/apps/cli/src/splash.ts similarity index 100% rename from cli/src/splash.ts rename to apps/cli/src/splash.ts diff --git a/cli/tsconfig.json b/apps/cli/tsconfig.json similarity index 77% rename from cli/tsconfig.json rename to apps/cli/tsconfig.json index 68dd138..452b099 100644 --- a/cli/tsconfig.json +++ b/apps/cli/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "rootDir": "./src", "outDir": "./dist" diff --git a/apps/cli/tsdown.config.ts b/apps/cli/tsdown.config.ts new file mode 100644 index 0000000..cf8a84f --- /dev/null +++ b/apps/cli/tsdown.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'tsdown'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: 'esm', + target: 'node18', + outDir: 'dist', + clean: true, + deps: { neverBundle: ['@clack/prompts', 'dotenv', 'smol-toml'] }, + banner: { js: '#!/usr/bin/env node' }, +}); diff --git a/configs/config-schema.json b/apps/worker/configs/config-schema.json similarity index 97% rename from configs/config-schema.json rename to apps/worker/configs/config-schema.json index b9621c2..dd38eac 100644 --- a/configs/config-schema.json +++ b/apps/worker/configs/config-schema.json @@ -124,11 +124,7 @@ "deprecated": true } }, - "anyOf": [ - {"required": ["authentication"]}, - {"required": ["rules"]}, - {"required": ["authentication", "rules"]} - ], + "anyOf": [{ "required": ["authentication"] }, { "required": ["rules"] }, { "required": ["authentication", "rules"] }], "additionalProperties": false, "$defs": { "rule": { @@ -157,4 +153,4 @@ "additionalProperties": false } } -} \ No newline at end of file +} diff --git a/configs/example-config.yaml b/apps/worker/configs/example-config.yaml similarity index 100% rename from configs/example-config.yaml rename to apps/worker/configs/example-config.yaml diff --git a/apps/worker/package.json b/apps/worker/package.json new file mode 100644 index 0000000..37544b4 --- /dev/null +++ b/apps/worker/package.json @@ -0,0 +1,27 @@ +{ + "name": "@shannon/worker", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "tsc", + "check": "tsc --noEmit", + "clean": "rm -rf dist" + }, + "dependencies": { + "@anthropic-ai/claude-agent-sdk": "catalog:", + "@shannon/mcp-server": "workspace:*", + "@temporalio/activity": "^1.11.0", + "@temporalio/client": "^1.11.0", + "@temporalio/worker": "^1.11.0", + "@temporalio/workflow": "^1.11.0", + "ajv": "^8.12.0", + "ajv-formats": "^2.1.1", + "dotenv": "^16.4.5", + "js-yaml": "^4.1.0", + "zx": "^8.0.0" + }, + "devDependencies": { + "@types/js-yaml": "^4.0.9" + } +} diff --git a/prompts/exploit-auth.txt b/apps/worker/prompts/exploit-auth.txt similarity index 100% rename from prompts/exploit-auth.txt rename to apps/worker/prompts/exploit-auth.txt diff --git a/prompts/exploit-authz.txt b/apps/worker/prompts/exploit-authz.txt similarity index 100% rename from prompts/exploit-authz.txt rename to apps/worker/prompts/exploit-authz.txt diff --git a/prompts/exploit-injection.txt b/apps/worker/prompts/exploit-injection.txt similarity index 100% rename from prompts/exploit-injection.txt rename to apps/worker/prompts/exploit-injection.txt diff --git a/prompts/exploit-ssrf.txt b/apps/worker/prompts/exploit-ssrf.txt similarity index 100% rename from prompts/exploit-ssrf.txt rename to apps/worker/prompts/exploit-ssrf.txt diff --git a/prompts/exploit-xss.txt b/apps/worker/prompts/exploit-xss.txt similarity index 100% rename from prompts/exploit-xss.txt rename to apps/worker/prompts/exploit-xss.txt diff --git a/prompts/pipeline-testing/exploit-auth.txt b/apps/worker/prompts/pipeline-testing/exploit-auth.txt similarity index 100% rename from prompts/pipeline-testing/exploit-auth.txt rename to apps/worker/prompts/pipeline-testing/exploit-auth.txt diff --git a/prompts/pipeline-testing/exploit-authz.txt b/apps/worker/prompts/pipeline-testing/exploit-authz.txt similarity index 100% rename from prompts/pipeline-testing/exploit-authz.txt rename to apps/worker/prompts/pipeline-testing/exploit-authz.txt diff --git a/prompts/pipeline-testing/exploit-injection.txt b/apps/worker/prompts/pipeline-testing/exploit-injection.txt similarity index 100% rename from prompts/pipeline-testing/exploit-injection.txt rename to apps/worker/prompts/pipeline-testing/exploit-injection.txt diff --git a/prompts/pipeline-testing/exploit-ssrf.txt b/apps/worker/prompts/pipeline-testing/exploit-ssrf.txt similarity index 100% rename from prompts/pipeline-testing/exploit-ssrf.txt rename to apps/worker/prompts/pipeline-testing/exploit-ssrf.txt diff --git a/prompts/pipeline-testing/exploit-xss.txt b/apps/worker/prompts/pipeline-testing/exploit-xss.txt similarity index 100% rename from prompts/pipeline-testing/exploit-xss.txt rename to apps/worker/prompts/pipeline-testing/exploit-xss.txt diff --git a/prompts/pipeline-testing/pre-recon-code.txt b/apps/worker/prompts/pipeline-testing/pre-recon-code.txt similarity index 100% rename from prompts/pipeline-testing/pre-recon-code.txt rename to apps/worker/prompts/pipeline-testing/pre-recon-code.txt diff --git a/prompts/pipeline-testing/recon.txt b/apps/worker/prompts/pipeline-testing/recon.txt similarity index 100% rename from prompts/pipeline-testing/recon.txt rename to apps/worker/prompts/pipeline-testing/recon.txt diff --git a/prompts/pipeline-testing/report-executive.txt b/apps/worker/prompts/pipeline-testing/report-executive.txt similarity index 100% rename from prompts/pipeline-testing/report-executive.txt rename to apps/worker/prompts/pipeline-testing/report-executive.txt diff --git a/prompts/pipeline-testing/vuln-auth.txt b/apps/worker/prompts/pipeline-testing/vuln-auth.txt similarity index 100% rename from prompts/pipeline-testing/vuln-auth.txt rename to apps/worker/prompts/pipeline-testing/vuln-auth.txt diff --git a/prompts/pipeline-testing/vuln-authz.txt b/apps/worker/prompts/pipeline-testing/vuln-authz.txt similarity index 100% rename from prompts/pipeline-testing/vuln-authz.txt rename to apps/worker/prompts/pipeline-testing/vuln-authz.txt diff --git a/prompts/pipeline-testing/vuln-injection.txt b/apps/worker/prompts/pipeline-testing/vuln-injection.txt similarity index 100% rename from prompts/pipeline-testing/vuln-injection.txt rename to apps/worker/prompts/pipeline-testing/vuln-injection.txt diff --git a/prompts/pipeline-testing/vuln-ssrf.txt b/apps/worker/prompts/pipeline-testing/vuln-ssrf.txt similarity index 100% rename from prompts/pipeline-testing/vuln-ssrf.txt rename to apps/worker/prompts/pipeline-testing/vuln-ssrf.txt diff --git a/prompts/pipeline-testing/vuln-xss.txt b/apps/worker/prompts/pipeline-testing/vuln-xss.txt similarity index 100% rename from prompts/pipeline-testing/vuln-xss.txt rename to apps/worker/prompts/pipeline-testing/vuln-xss.txt diff --git a/prompts/pre-recon-code.txt b/apps/worker/prompts/pre-recon-code.txt similarity index 100% rename from prompts/pre-recon-code.txt rename to apps/worker/prompts/pre-recon-code.txt diff --git a/prompts/recon.txt b/apps/worker/prompts/recon.txt similarity index 100% rename from prompts/recon.txt rename to apps/worker/prompts/recon.txt diff --git a/prompts/report-executive.txt b/apps/worker/prompts/report-executive.txt similarity index 100% rename from prompts/report-executive.txt rename to apps/worker/prompts/report-executive.txt diff --git a/prompts/shared/_exploit-scope.txt b/apps/worker/prompts/shared/_exploit-scope.txt similarity index 100% rename from prompts/shared/_exploit-scope.txt rename to apps/worker/prompts/shared/_exploit-scope.txt diff --git a/prompts/shared/_rules.txt b/apps/worker/prompts/shared/_rules.txt similarity index 100% rename from prompts/shared/_rules.txt rename to apps/worker/prompts/shared/_rules.txt diff --git a/prompts/shared/_target.txt b/apps/worker/prompts/shared/_target.txt similarity index 100% rename from prompts/shared/_target.txt rename to apps/worker/prompts/shared/_target.txt diff --git a/prompts/shared/_vuln-scope.txt b/apps/worker/prompts/shared/_vuln-scope.txt similarity index 100% rename from prompts/shared/_vuln-scope.txt rename to apps/worker/prompts/shared/_vuln-scope.txt diff --git a/prompts/shared/login-instructions.txt b/apps/worker/prompts/shared/login-instructions.txt similarity index 100% rename from prompts/shared/login-instructions.txt rename to apps/worker/prompts/shared/login-instructions.txt diff --git a/prompts/vuln-auth.txt b/apps/worker/prompts/vuln-auth.txt similarity index 100% rename from prompts/vuln-auth.txt rename to apps/worker/prompts/vuln-auth.txt diff --git a/prompts/vuln-authz.txt b/apps/worker/prompts/vuln-authz.txt similarity index 100% rename from prompts/vuln-authz.txt rename to apps/worker/prompts/vuln-authz.txt diff --git a/prompts/vuln-injection.txt b/apps/worker/prompts/vuln-injection.txt similarity index 100% rename from prompts/vuln-injection.txt rename to apps/worker/prompts/vuln-injection.txt diff --git a/prompts/vuln-ssrf.txt b/apps/worker/prompts/vuln-ssrf.txt similarity index 100% rename from prompts/vuln-ssrf.txt rename to apps/worker/prompts/vuln-ssrf.txt diff --git a/prompts/vuln-xss.txt b/apps/worker/prompts/vuln-xss.txt similarity index 100% rename from prompts/vuln-xss.txt rename to apps/worker/prompts/vuln-xss.txt diff --git a/src/ai/audit-logger.ts b/apps/worker/src/ai/audit-logger.ts similarity index 100% rename from src/ai/audit-logger.ts rename to apps/worker/src/ai/audit-logger.ts diff --git a/src/ai/claude-executor.ts b/apps/worker/src/ai/claude-executor.ts similarity index 90% rename from src/ai/claude-executor.ts rename to apps/worker/src/ai/claude-executor.ts index 3f7fd0c..1612589 100644 --- a/src/ai/claude-executor.ts +++ b/apps/worker/src/ai/claude-executor.ts @@ -6,26 +6,23 @@ // Production Claude agent execution with retry, git checkpoints, and audit logging -import { fs, path } from 'zx'; import { query } from '@anthropic-ai/claude-agent-sdk'; - +import { createShannonHelperServer } from '@shannon/mcp-server'; +import { fs, path } from 'zx'; +import type { AuditSession } from '../audit/index.js'; import { isRetryableError, PentestError } from '../services/error-handling.js'; -import { isSpendingCapBehavior } from '../utils/billing-detection.js'; -import { Timer } from '../utils/metrics.js'; -import { formatTimestamp } from '../utils/formatting.js'; -import { AGENT_VALIDATORS, MCP_AGENT_MAPPING } from '../session-manager.js'; -import { AuditSession } from '../audit/index.js'; -import { createShannonHelperServer } from '../../mcp-server/dist/index.js'; -import { AGENTS } from '../session-manager.js'; -import type { AgentName } from '../types/index.js'; - -import { dispatchMessage } from './message-handlers.js'; -import { detectExecutionContext, formatErrorOutput, formatCompletionMessage } from './output-formatters.js'; -import { createProgressManager } from './progress-manager.js'; -import { createAuditLogger } from './audit-logger.js'; -import { getActualModelName } from './router-utils.js'; -import { resolveModel, type ModelTier } from './models.js'; +import { AGENT_VALIDATORS, AGENTS, MCP_AGENT_MAPPING } from '../session-manager.js'; import type { ActivityLogger } from '../types/activity-logger.js'; +import type { AgentName } from '../types/index.js'; +import { isSpendingCapBehavior } from '../utils/billing-detection.js'; +import { formatTimestamp } from '../utils/formatting.js'; +import { Timer } from '../utils/metrics.js'; +import { createAuditLogger } from './audit-logger.js'; +import { dispatchMessage } from './message-handlers.js'; +import { type ModelTier, resolveModel } from './models.js'; +import { detectExecutionContext, formatCompletionMessage, formatErrorOutput } from './output-formatters.js'; +import { createProgressManager } from './progress-manager.js'; +import { getActualModelName } from './router-utils.js'; declare global { var SHANNON_DISABLE_LOADER: boolean | undefined; @@ -59,7 +56,7 @@ type McpServer = ReturnType | StdioMcpServer; function buildMcpServers( sourceDir: string, agentName: string | null, - logger: ActivityLogger + logger: ActivityLogger, ): Record { // 1. Create the shannon-helper server (always present) const shannonHelperServer = createShannonHelperServer(sourceDir); @@ -81,11 +78,7 @@ function buildMcpServers( // 3. Configure Playwright MCP args with Docker/local browser handling const isDocker = process.env.SHANNON_DOCKER === 'true'; - const mcpArgs: string[] = [ - '@playwright/mcp@0.0.68', - '--isolated', - '--user-data-dir', userDataDir, - ]; + const mcpArgs: string[] = ['@playwright/mcp@0.0.68', '--isolated', '--user-data-dir', userDataDir]; if (isDocker) { mcpArgs.push('--executable-path', '/usr/bin/chromium-browser'); @@ -139,7 +132,7 @@ async function writeErrorLog( err: Error & { code?: string; status?: number }, sourceDir: string, fullPrompt: string, - duration: number + duration: number, ): Promise { try { const errorLog = { @@ -150,17 +143,17 @@ async function writeErrorLog( message: err.message, code: err.code, status: err.status, - stack: err.stack + stack: err.stack, }, context: { sourceDir, - prompt: fullPrompt.slice(0, 200) + '...', - retryable: isRetryableError(err) + prompt: `${fullPrompt.slice(0, 200)}...`, + retryable: isRetryableError(err), }, - duration + duration, }; const logPath = path.join(sourceDir, 'error.log'); - await fs.appendFile(logPath, JSON.stringify(errorLog) + '\n'); + await fs.appendFile(logPath, `${JSON.stringify(errorLog)}\n`); } catch { // Best-effort error log writing - don't propagate failures } @@ -170,7 +163,7 @@ export async function validateAgentOutput( result: ClaudePromptResult, agentName: string | null, sourceDir: string, - logger: ActivityLogger + logger: ActivityLogger, ): Promise { logger.info(`Validating ${agentName} agent output`); @@ -202,7 +195,6 @@ export async function validateAgentOutput( } return validationResult; - } catch (error) { const errMsg = error instanceof Error ? error.message : String(error); logger.error(`Validation failed with error: ${errMsg}`); @@ -220,7 +212,7 @@ export async function runClaudePrompt( agentName: string | null = null, auditSession: AuditSession | null = null, logger: ActivityLogger, - modelTier: ModelTier = 'medium' + modelTier: ModelTier = 'medium', ): Promise { // 1. Initialize timing and prompt const timer = new Timer(`agent-${description.toLowerCase().replace(/\s+/g, '-')}`); @@ -230,7 +222,7 @@ export async function runClaudePrompt( const execContext = detectExecutionContext(description); const progress = createProgressManager( { description, useCleanOutput: execContext.useCleanOutput }, - global.SHANNON_DISABLE_LOADER ?? false + global.SHANNON_DISABLE_LOADER ?? false, ); const auditLogger = createAuditLogger(auditSession); @@ -293,7 +285,7 @@ export async function runClaudePrompt( fullPrompt, options, { execContext, description, progress, auditLogger, logger }, - timer + timer, ); turnCount = messageLoopResult.turnCount; @@ -309,7 +301,7 @@ export async function runClaudePrompt( throw new PentestError( `Spending cap likely reached (turns=${turnCount}, cost=$0): ${result?.slice(0, 100)}`, 'billing', - true // Retryable - Temporal will use 5-30 min backoff + true, // Retryable - Temporal will use 5-30 min backoff ); } @@ -330,9 +322,8 @@ export async function runClaudePrompt( cost: totalCost, model, partialCost: totalCost, - apiErrorDetected + apiErrorDetected, }; - } catch (error) { // 9. Handle errors — log, write error file, return failure const duration = timer.stop(); @@ -347,16 +338,15 @@ export async function runClaudePrompt( return { error: err.message, errorType: err.constructor.name, - prompt: fullPrompt.slice(0, 100) + '...', + prompt: `${fullPrompt.slice(0, 100)}...`, success: false, duration, cost: totalCost, - retryable: isRetryableError(err) + retryable: isRetryableError(err), }; } } - interface MessageLoopResult { turnCount: number; result: string | null; @@ -377,7 +367,7 @@ async function processMessageStream( fullPrompt: string, options: NonNullable[0]['options']>, deps: MessageLoopDeps, - timer: Timer + timer: Timer, ): Promise { const { execContext, description, progress, auditLogger, logger } = deps; const HEARTBEAT_INTERVAL = 30000; @@ -402,11 +392,13 @@ async function processMessageStream( turnCount++; } - const dispatchResult = await dispatchMessage( - message as { type: string; subtype?: string }, - turnCount, - { execContext, description, progress, auditLogger, logger } - ); + const dispatchResult = await dispatchMessage(message as { type: string; subtype?: string }, turnCount, { + execContext, + description, + progress, + auditLogger, + logger, + }); if (dispatchResult.type === 'throw') { throw dispatchResult.error; diff --git a/src/ai/message-handlers.ts b/apps/worker/src/ai/message-handlers.ts similarity index 90% rename from src/ai/message-handlers.ts rename to apps/worker/src/ai/message-handlers.ts index 78068ad..7ff725c 100644 --- a/src/ai/message-handlers.ts +++ b/apps/worker/src/ai/message-handlers.ts @@ -4,35 +4,35 @@ // it under the terms of the GNU Affero General Public License version 3 // as published by the Free Software Foundation. +import type { SDKAssistantMessageError } from '@anthropic-ai/claude-agent-sdk'; import { PentestError } from '../services/error-handling.js'; +import type { ActivityLogger } from '../types/activity-logger.js'; import { ErrorCode } from '../types/errors.js'; import { matchesBillingTextPattern } from '../utils/billing-detection.js'; -import { filterJsonToolCalls } from './output-formatters.js'; import { formatTimestamp } from '../utils/formatting.js'; -import { getActualModelName } from './router-utils.js'; -import type { ActivityLogger } from '../types/activity-logger.js'; +import type { AuditLogger } from './audit-logger.js'; import { + filterJsonToolCalls, formatAssistantOutput, formatResultOutput, - formatToolUseOutput, formatToolResultOutput, + formatToolUseOutput, } from './output-formatters.js'; -import type { AuditLogger } from './audit-logger.js'; import type { ProgressManager } from './progress-manager.js'; -import type { SDKAssistantMessageError } from '@anthropic-ai/claude-agent-sdk'; +import { getActualModelName } from './router-utils.js'; import type { - AssistantMessage, - ResultMessage, - ToolUseMessage, - ToolResultMessage, - AssistantResult, - ResultData, - ToolUseData, - ToolResultData, ApiErrorDetection, + AssistantMessage, + AssistantResult, ContentBlock, - SystemInitMessage, ExecutionContext, + ResultData, + ResultMessage, + SystemInitMessage, + ToolResultData, + ToolResultMessage, + ToolUseData, + ToolUseMessage, } from './types.js'; // Handles both array and string content formats from SDK @@ -40,9 +40,7 @@ function extractMessageContent(message: AssistantMessage): string { const messageContent = message.message; if (Array.isArray(messageContent.content)) { - return messageContent.content - .map((c: ContentBlock) => c.text || JSON.stringify(c)) - .join('\n'); + return messageContent.content.map((c: ContentBlock) => c.text || JSON.stringify(c)).join('\n'); } return String(messageContent.content); @@ -81,7 +79,7 @@ function detectApiError(content: string): ApiErrorDetection { 'billing', true, // RETRYABLE - Temporal will use 5-30 min backoff {}, - ErrorCode.SPENDING_CAP_REACHED + ErrorCode.SPENDING_CAP_REACHED, ), }; } @@ -104,10 +102,7 @@ function detectApiError(content: string): ApiErrorDetection { } // Maps SDK structured error types to our error handling. -function handleStructuredError( - errorType: SDKAssistantMessageError, - content: string -): ApiErrorDetection { +function handleStructuredError(errorType: SDKAssistantMessageError, content: string): ApiErrorDetection { switch (errorType) { case 'billing_error': return { @@ -117,7 +112,7 @@ function handleStructuredError( 'billing', true, // Retryable with backoff {}, - ErrorCode.INSUFFICIENT_CREDITS + ErrorCode.INSUFFICIENT_CREDITS, ), }; case 'rate_limit': @@ -128,7 +123,7 @@ function handleStructuredError( 'network', true, // Retryable with backoff {}, - ErrorCode.API_RATE_LIMITED + ErrorCode.API_RATE_LIMITED, ), }; case 'authentication_failed': @@ -137,7 +132,7 @@ function handleStructuredError( shouldThrow: new PentestError( `Authentication failed: ${content.slice(0, 100)}`, 'config', - false // Not retryable - needs API key fix + false, // Not retryable - needs API key fix ), }; case 'server_error': @@ -146,7 +141,7 @@ function handleStructuredError( shouldThrow: new PentestError( `Server error (structured): ${content.slice(0, 100)}`, 'network', - true // Retryable + true, // Retryable ), }; case 'invalid_request': @@ -155,7 +150,7 @@ function handleStructuredError( shouldThrow: new PentestError( `Invalid request: ${content.slice(0, 100)}`, 'config', - false // Not retryable - needs code fix + false, // Not retryable - needs code fix ), }; case 'max_output_tokens': @@ -164,19 +159,15 @@ function handleStructuredError( shouldThrow: new PentestError( `Max output tokens reached: ${content.slice(0, 100)}`, 'billing', - true // Retryable - may succeed with different content + true, // Retryable - may succeed with different content ), }; - case 'unknown': default: return { detected: true }; } } -function handleAssistantMessage( - message: AssistantMessage, - turnCount: number -): AssistantResult { +function handleAssistantMessage(message: AssistantMessage, turnCount: number): AssistantResult { const content = extractMessageContent(message); const cleanedContent = filterJsonToolCalls(content); @@ -246,8 +237,7 @@ function handleToolUseMessage(message: ToolUseMessage): ToolUseData { // Truncates long results for display (500 char limit), preserves full content for logging function handleToolResultMessage(message: ToolResultMessage): ToolResultData { const content = message.content; - const contentStr = - typeof content === 'string' ? content : JSON.stringify(content, null, 2); + const contentStr = typeof content === 'string' ? content : JSON.stringify(content, null, 2); const displayContent = contentStr.length > 500 @@ -284,7 +274,7 @@ export interface MessageDispatchDeps { export async function dispatchMessage( message: { type: string; subtype?: string }, turnCount: number, - deps: MessageDispatchDeps + deps: MessageDispatchDeps, ): Promise { const { execContext, description, progress, auditLogger, logger } = deps; @@ -298,12 +288,7 @@ export async function dispatchMessage( if (assistantResult.cleanedContent.trim()) { progress.stop(); - outputLines(formatAssistantOutput( - assistantResult.cleanedContent, - execContext, - turnCount, - description - )); + outputLines(formatAssistantOutput(assistantResult.cleanedContent, execContext, turnCount, description)); progress.start(); } @@ -324,7 +309,7 @@ export async function dispatchMessage( if (!execContext.useCleanOutput) { logger.info(`Model: ${actualModel}, Permission: ${initMsg.permissionMode}`); if (initMsg.mcp_servers && initMsg.mcp_servers.length > 0) { - const mcpStatus = initMsg.mcp_servers.map(s => `${s.name}(${s.status})`).join(', '); + const mcpStatus = initMsg.mcp_servers.map((s) => `${s.name}(${s.status})`).join(', '); logger.info(`MCP: ${mcpStatus}`); } } diff --git a/src/ai/models.ts b/apps/worker/src/ai/models.ts similarity index 100% rename from src/ai/models.ts rename to apps/worker/src/ai/models.ts diff --git a/src/ai/output-formatters.ts b/apps/worker/src/ai/output-formatters.ts similarity index 96% rename from src/ai/output-formatters.ts rename to apps/worker/src/ai/output-formatters.ts index 34735f2..1e44280 100644 --- a/src/ai/output-formatters.ts +++ b/apps/worker/src/ai/output-formatters.ts @@ -4,8 +4,8 @@ // it under the terms of the GNU Affero General Public License version 3 // as published by the Free Software Foundation. -import { extractAgentType, formatDuration } from '../utils/formatting.js'; import { AGENTS } from '../session-manager.js'; +import { extractAgentType, formatDuration } from '../utils/formatting.js'; import type { ExecutionContext, ResultData } from './types.js'; interface ToolCallInput { @@ -247,11 +247,7 @@ export function filterJsonToolCalls(content: string | null | undefined): string if (browserAction) { processedLines.push(browserAction); } - continue; } - - // Hide all other tool calls (Read, Write, Grep, etc.) - continue; } catch { // If JSON parsing fails, treat as regular text processedLines.push(line); @@ -266,8 +262,7 @@ export function filterJsonToolCalls(content: string | null | undefined): string } export function detectExecutionContext(description: string): ExecutionContext { - const isParallelExecution = - description.includes('vuln agent') || description.includes('exploit agent'); + const isParallelExecution = description.includes('vuln agent') || description.includes('exploit agent'); const useCleanOutput = description.includes('Pre-recon agent') || @@ -287,7 +282,7 @@ export function formatAssistantOutput( cleanedContent: string, context: ExecutionContext, turnCount: number, - description: string + description: string, ): string[] { if (!cleanedContent.trim()) { return []; @@ -341,7 +336,7 @@ export function formatErrorOutput( description: string, duration: number, sourceDir: string, - isRetryable: boolean + isRetryable: boolean, ): string[] { const lines: string[] = []; @@ -374,7 +369,7 @@ export function formatCompletionMessage( context: ExecutionContext, description: string, turnCount: number, - duration: number + duration: number, ): string { if (context.isParallelExecution) { const prefix = getAgentPrefix(description); @@ -388,10 +383,7 @@ export function formatCompletionMessage( return ` Claude Code completed: ${description} (${turnCount} turns) in ${formatDuration(duration)}`; } -export function formatToolUseOutput( - toolName: string, - input: Record | undefined -): string[] { +export function formatToolUseOutput(toolName: string, input: Record | undefined): string[] { const lines: string[] = []; lines.push(`\n Using Tool: ${toolName}`); diff --git a/src/ai/progress-manager.ts b/apps/worker/src/ai/progress-manager.ts similarity index 93% rename from src/ai/progress-manager.ts rename to apps/worker/src/ai/progress-manager.ts index ceee32d..0d4e63e 100644 --- a/src/ai/progress-manager.ts +++ b/apps/worker/src/ai/progress-manager.ts @@ -63,10 +63,7 @@ class NullProgressManager implements ProgressManager { } // Returns no-op when disabled -export function createProgressManager( - context: ProgressContext, - disableLoader: boolean -): ProgressManager { +export function createProgressManager(context: ProgressContext, disableLoader: boolean): ProgressManager { if (!context.useCleanOutput || disableLoader) { return new NullProgressManager(); } diff --git a/src/ai/router-utils.ts b/apps/worker/src/ai/router-utils.ts similarity index 99% rename from src/ai/router-utils.ts rename to apps/worker/src/ai/router-utils.ts index df3d14b..b41185c 100644 --- a/src/ai/router-utils.ts +++ b/apps/worker/src/ai/router-utils.ts @@ -25,4 +25,3 @@ export function getActualModelName(sdkReportedModel?: string): string | undefine // Fall back to SDK-reported model return sdkReportedModel; } - diff --git a/src/ai/types.ts b/apps/worker/src/ai/types.ts similarity index 99% rename from src/ai/types.ts rename to apps/worker/src/ai/types.ts index ee98bdf..d504ab9 100644 --- a/src/ai/types.ts +++ b/apps/worker/src/ai/types.ts @@ -53,7 +53,6 @@ export interface ContentBlock { text?: string; } - export interface AssistantMessage { type: 'assistant'; error?: SDKAssistantMessageError; @@ -99,4 +98,3 @@ export interface SystemInitMessage { export interface UserMessage { type: 'user'; } - diff --git a/src/audit/audit-session.ts b/apps/worker/src/audit/audit-session.ts similarity index 91% rename from src/audit/audit-session.ts rename to apps/worker/src/audit/audit-session.ts index b571e65..e434477 100644 --- a/src/audit/audit-session.ts +++ b/apps/worker/src/audit/audit-session.ts @@ -11,15 +11,15 @@ * crash-safe audit logging. */ -import { AgentLogger } from './logger.js'; -import { WorkflowLogger, type AgentLogDetails, type WorkflowSummary } from './workflow-logger.js'; -import { MetricsTracker } from './metrics-tracker.js'; -import { initializeAuditStructure, type SessionMetadata } from './utils.js'; -import { formatTimestamp } from '../utils/formatting.js'; -import { SessionMutex } from '../utils/concurrency.js'; -import type { AgentEndResult } from '../types/index.js'; import { PentestError } from '../services/error-handling.js'; import { ErrorCode } from '../types/errors.js'; +import type { AgentEndResult } from '../types/index.js'; +import { SessionMutex } from '../utils/concurrency.js'; +import { formatTimestamp } from '../utils/formatting.js'; +import { AgentLogger } from './logger.js'; +import { MetricsTracker } from './metrics-tracker.js'; +import { initializeAuditStructure, type SessionMetadata } from './utils.js'; +import { type AgentLogDetails, WorkflowLogger, type WorkflowSummary } from './workflow-logger.js'; // Global mutex instance const sessionMutex = new SessionMutex(); @@ -47,7 +47,7 @@ export class AuditSession { 'config', false, { field: 'sessionMetadata.id' }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } if (!this.sessionMetadata.webUrl) { @@ -56,7 +56,7 @@ export class AuditSession { 'config', false, { field: 'sessionMetadata.webUrl' }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } @@ -100,11 +100,7 @@ export class AuditSession { /** * Start agent execution */ - async startAgent( - agentName: string, - promptContent: string, - attemptNumber: number = 1 - ): Promise { + async startAgent(agentName: string, promptContent: string, attemptNumber: number = 1): Promise { await this.ensureInitialized(); // 1. Save prompt snapshot (only on first attempt) @@ -140,7 +136,7 @@ export class AuditSession { 'validation', false, {}, - ErrorCode.AGENT_EXECUTION_FAILED + ErrorCode.AGENT_EXECUTION_FAILED, ); } @@ -152,18 +148,10 @@ export class AuditSession { const agentName = this.currentAgentName || 'unknown'; switch (eventType) { case 'tool_start': - await this.workflowLogger.logToolStart( - agentName, - String(data.toolName || ''), - data.parameters - ); + await this.workflowLogger.logToolStart(agentName, String(data.toolName || ''), data.parameters); break; case 'llm_response': - await this.workflowLogger.logLlmResponse( - agentName, - Number(data.turn || 0), - String(data.content || '') - ); + await this.workflowLogger.logLlmResponse(agentName, Number(data.turn || 0), String(data.content || '')); break; // tool_end and error events are intentionally not logged to workflow log // to reduce noise - the agent completion message captures the outcome @@ -266,11 +254,7 @@ export class AuditSession { * @param terminatedWorkflows - IDs of workflows that were terminated * @param checkpointHash - Git checkpoint hash that was restored */ - async addResumeAttempt( - workflowId: string, - terminatedWorkflows: string[], - checkpointHash?: string - ): Promise { + async addResumeAttempt(workflowId: string, terminatedWorkflows: string[], checkpointHash?: string): Promise { await this.ensureInitialized(); const unlock = await sessionMutex.lock(this.sessionId); diff --git a/src/audit/index.ts b/apps/worker/src/audit/index.ts similarity index 100% rename from src/audit/index.ts rename to apps/worker/src/audit/index.ts diff --git a/src/audit/log-stream.ts b/apps/worker/src/audit/log-stream.ts similarity index 97% rename from src/audit/log-stream.ts rename to apps/worker/src/audit/log-stream.ts index b97bef5..dc1d1b9 100644 --- a/src/audit/log-stream.ts +++ b/apps/worker/src/audit/log-stream.ts @@ -12,8 +12,8 @@ * and proper cleanup. */ -import fs from 'fs'; -import path from 'path'; +import fs from 'node:fs'; +import path from 'node:path'; import { ensureDirectory } from '../utils/file-io.js'; /** @@ -103,7 +103,7 @@ export class LogStream { } return new Promise((resolve) => { - this.stream!.end(() => { + this.stream?.end(() => { this._isOpen = false; this.stream = null; resolve(); diff --git a/src/audit/logger.ts b/apps/worker/src/audit/logger.ts similarity index 93% rename from src/audit/logger.ts rename to apps/worker/src/audit/logger.ts index d616bfa..43f860d 100644 --- a/src/audit/logger.ts +++ b/apps/worker/src/audit/logger.ts @@ -11,14 +11,10 @@ * Uses LogStream for stream management with backpressure handling. */ -import { - generateLogPath, - generatePromptPath, - type SessionMetadata, -} from './utils.js'; import { atomicWrite } from '../utils/file-io.js'; import { formatTimestamp } from '../utils/formatting.js'; import { LogStream } from './log-stream.js'; +import { generateLogPath, generatePromptPath, type SessionMetadata } from './utils.js'; interface LogEvent { type: string; @@ -103,11 +99,7 @@ export class AgentLogger { * Save prompt snapshot to prompts directory * Static method - doesn't require logger instance */ - static async savePrompt( - sessionMetadata: SessionMetadata, - agentName: string, - promptContent: string - ): Promise { + static async savePrompt(sessionMetadata: SessionMetadata, agentName: string, promptContent: string): Promise { const promptPath = generatePromptPath(sessionMetadata, agentName); // Create header with metadata diff --git a/src/audit/metrics-tracker.ts b/apps/worker/src/audit/metrics-tracker.ts similarity index 91% rename from src/audit/metrics-tracker.ts rename to apps/worker/src/audit/metrics-tracker.ts index 5853860..f88a6c1 100644 --- a/src/audit/metrics-tracker.ts +++ b/apps/worker/src/audit/metrics-tracker.ts @@ -11,16 +11,13 @@ * Tracks attempt-level data for complete forensic trail. */ -import { - generateSessionJsonPath, - type SessionMetadata, -} from './utils.js'; -import { atomicWrite, readJson, fileExists } from '../utils/file-io.js'; -import { formatTimestamp, calculatePercentage } from '../utils/formatting.js'; -import { AGENT_PHASE_MAP, type PhaseName } from '../session-manager.js'; import { PentestError } from '../services/error-handling.js'; +import { AGENT_PHASE_MAP, type PhaseName } from '../session-manager.js'; import { ErrorCode } from '../types/errors.js'; -import type { AgentName, AgentEndResult } from '../types/index.js'; +import type { AgentEndResult, AgentName } from '../types/index.js'; +import { atomicWrite, fileExists, readJson } from '../utils/file-io.js'; +import { calculatePercentage, formatTimestamp } from '../utils/formatting.js'; +import { generateSessionJsonPath, type SessionMetadata } from './utils.js'; interface AttemptData { attempt_number: number; @@ -166,7 +163,7 @@ export class MetricsTracker { 'validation', false, {}, - ErrorCode.AGENT_EXECUTION_FAILED + ErrorCode.AGENT_EXECUTION_FAILED, ); } @@ -254,18 +251,14 @@ export class MetricsTracker { * @param terminatedWorkflows - IDs of workflows that were terminated * @param checkpointHash - Git checkpoint hash that was restored */ - async addResumeAttempt( - workflowId: string, - terminatedWorkflows: string[], - checkpointHash?: string - ): Promise { + async addResumeAttempt(workflowId: string, terminatedWorkflows: string[], checkpointHash?: string): Promise { if (!this.data) { throw new PentestError( 'MetricsTracker not initialized', 'validation', false, {}, - ErrorCode.AGENT_EXECUTION_FAILED + ErrorCode.AGENT_EXECUTION_FAILED, ); } @@ -307,15 +300,10 @@ export class MetricsTracker { const agents = this.data.metrics.agents; // Only count successful agents - const successfulAgents = Object.entries(agents).filter( - ([, data]) => data.status === 'success' - ); + const successfulAgents = Object.entries(agents).filter(([, data]) => data.status === 'success'); // Calculate total duration and cost - const totalDuration = successfulAgents.reduce( - (sum, [, data]) => sum + data.final_duration_ms, - 0 - ); + const totalDuration = successfulAgents.reduce((sum, [, data]) => sum + data.final_duration_ms, 0); const totalCost = successfulAgents.reduce((sum, [, data]) => sum + data.total_cost_usd, 0); @@ -329,15 +317,13 @@ export class MetricsTracker { /** * Calculate phase-level metrics */ - private calculatePhaseMetrics( - successfulAgents: Array<[string, AgentAuditMetrics]> - ): Record { + private calculatePhaseMetrics(successfulAgents: Array<[string, AgentAuditMetrics]>): Record { const phases: Record = { 'pre-recon': [], - 'recon': [], + recon: [], 'vulnerability-analysis': [], - 'exploitation': [], - 'reporting': [], + exploitation: [], + reporting: [], }; // Group agents by phase using imported AGENT_PHASE_MAP diff --git a/src/audit/utils.ts b/apps/worker/src/audit/utils.ts similarity index 89% rename from src/audit/utils.ts rename to apps/worker/src/audit/utils.ts index bd51c87..2715e57 100644 --- a/src/audit/utils.ts +++ b/apps/worker/src/audit/utils.ts @@ -11,22 +11,15 @@ * All functions are pure and crash-safe. */ -import fs from 'fs/promises'; -import path from 'path'; -import { fileURLToPath } from 'url'; - +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { WORKSPACES_DIR } from '../paths.js'; import { ensureDirectory } from '../utils/file-io.js'; export type { SessionMetadata } from '../types/audit.js'; + import type { SessionMetadata } from '../types/audit.js'; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -// Get Shannon repository root -const SHANNON_ROOT = path.resolve(__dirname, '..', '..'); -const WORKSPACES_DIR = path.join(SHANNON_ROOT, 'workspaces'); - /** * Extract and sanitize hostname from URL for use in identifiers */ @@ -59,7 +52,7 @@ export function generateLogPath( sessionMetadata: SessionMetadata, agentName: string, timestamp: number, - attemptNumber: number + attemptNumber: number, ): string { const auditPath = generateAuditPath(sessionMetadata); const filename = `${timestamp}_${agentName}_attempt-${attemptNumber}.log`; @@ -110,10 +103,7 @@ export async function initializeAuditStructure(sessionMetadata: SessionMetadata) * Copy deliverable files from repo to workspaces for self-contained audit trail. * No-ops if source directory doesn't exist. Idempotent and parallel-safe. */ -export async function copyDeliverablesToAudit( - sessionMetadata: SessionMetadata, - repoPath: string -): Promise { +export async function copyDeliverablesToAudit(sessionMetadata: SessionMetadata, repoPath: string): Promise { const sourceDir = path.join(repoPath, 'deliverables'); const destDir = path.join(generateAuditPath(sessionMetadata), 'deliverables'); diff --git a/src/audit/workflow-logger.ts b/apps/worker/src/audit/workflow-logger.ts similarity index 86% rename from src/audit/workflow-logger.ts rename to apps/worker/src/audit/workflow-logger.ts index 2790fe4..7c84f53 100644 --- a/src/audit/workflow-logger.ts +++ b/apps/worker/src/audit/workflow-logger.ts @@ -11,10 +11,10 @@ * Optimized for `tail -f` viewing during concurrent workflow execution. */ -import fs from 'fs/promises'; -import { generateWorkflowLogPath, type SessionMetadata } from './utils.js'; +import fs from 'node:fs/promises'; import { formatDuration, formatTimestamp } from '../utils/formatting.js'; import { LogStream } from './log-stream.js'; +import { generateWorkflowLogPath, type SessionMetadata } from './utils.js'; export interface AgentLogDetails { attemptNumber?: number; @@ -147,11 +147,7 @@ export class WorkflowLogger { /** * Log an agent event */ - async logAgent( - agentName: string, - event: 'start' | 'end', - details?: AgentLogDetails - ): Promise { + async logAgent(agentName: string, event: 'start' | 'end', details?: AgentLogDetails): Promise { await this.ensureInitialized(); let message: string; @@ -160,7 +156,7 @@ export class WorkflowLogger { const attempt = details?.attemptNumber ?? 1; message = `${agentName}: Starting (attempt ${attempt})`; } else { - const parts: string[] = [agentName + ':']; + const parts: string[] = [`${agentName}:`]; if (details?.success === false) { parts.push('Failed'); @@ -213,7 +209,7 @@ export class WorkflowLogger { */ private truncate(str: string, maxLen: number): string { if (str.length <= maxLen) return str; - return str.slice(0, maxLen - 3) + '...'; + return `${str.slice(0, maxLen - 3)}...`; } /** @@ -327,11 +323,9 @@ export class WorkflowLogger { const label = 'Error: '; const indent = ' '.repeat(label.length); - const lines = segments.map((segment, i) => - i === 0 ? `${label}${segment.trim()}` : `${indent}${segment.trim()}` - ); + const lines = segments.map((segment, i) => (i === 0 ? `${label}${segment.trim()}` : `${indent}${segment.trim()}`)); - return lines.join('\n') + '\n'; + return `${lines.join('\n')}\n`; } /** @@ -342,35 +336,40 @@ export class WorkflowLogger { const status = summary.status === 'completed' ? 'COMPLETED' : 'FAILED'; - await this.logStream.write('\n'); - await this.logStream.write(`================================================================================\n`); - await this.logStream.write(`Workflow ${status}\n`); - await this.logStream.write(`────────────────────────────────────────\n`); - await this.logStream.write(`Workflow ID: ${this.workflowId ?? this.sessionMetadata.id}\n`); - await this.logStream.write(`Status: ${summary.status}\n`); - await this.logStream.write(`Duration: ${formatDuration(summary.totalDurationMs)}\n`); - await this.logStream.write(`Total Cost: $${summary.totalCostUsd.toFixed(4)}\n`); - await this.logStream.write(`Agents: ${summary.completedAgents.length} completed\n`); + const lines: string[] = [ + '', + '================================================================================', + `Workflow ${status}`, + '────────────────────────────────────────', + `Workflow ID: ${this.workflowId ?? this.sessionMetadata.id}`, + `Status: ${summary.status}`, + `Duration: ${formatDuration(summary.totalDurationMs)}`, + `Total Cost: $${summary.totalCostUsd.toFixed(4)}`, + `Agents: ${summary.completedAgents.length} completed`, + ]; if (summary.error) { - await this.logStream.write(this.formatErrorBlock(summary.error)); + lines.push(this.formatErrorBlock(summary.error).trimEnd()); } - await this.logStream.write(`\n`); - await this.logStream.write(`Agent Breakdown:\n`); + lines.push(''); + lines.push('Agent Breakdown:'); for (const agentName of summary.completedAgents) { const metrics = summary.agentMetrics[agentName]; if (metrics) { const duration = formatDuration(metrics.durationMs); const cost = metrics.costUsd !== null ? `$${metrics.costUsd.toFixed(4)}` : 'N/A'; - await this.logStream.write(` - ${agentName} (${duration}, ${cost})\n`); + lines.push(` - ${agentName} (${duration}, ${cost})`); } else { - await this.logStream.write(` - ${agentName}\n`); + lines.push(` - ${agentName}`); } } - await this.logStream.write(`================================================================================\n`); + lines.push('================================================================================'); + + // Single atomic write to prevent interleaved/duplicate output in log tailers + await this.logStream.write(`${lines.join('\n')}\n`); } /** diff --git a/src/config-parser.ts b/apps/worker/src/config-parser.ts similarity index 91% rename from src/config-parser.ts rename to apps/worker/src/config-parser.ts index 484ad37..acbf5be 100644 --- a/src/config-parser.ts +++ b/apps/worker/src/config-parser.ts @@ -4,19 +4,14 @@ // it under the terms of the GNU Affero General Public License version 3 // as published by the Free Software Foundation. -import { createRequire } from 'module'; -import { fs } from 'zx'; -import yaml from 'js-yaml'; -import { Ajv, type ValidateFunction, type ErrorObject } from 'ajv'; +import { createRequire } from 'node:module'; +import { Ajv, type ErrorObject, type ValidateFunction } from 'ajv'; import type { FormatsPlugin } from 'ajv-formats'; +import yaml from 'js-yaml'; +import { fs } from 'zx'; import { PentestError } from './services/error-handling.js'; +import type { Authentication, Config, DistributedConfig, Rule } from './types/config.js'; import { ErrorCode } from './types/errors.js'; -import type { - Config, - Rule, - Authentication, - DistributedConfig, -} from './types/config.js'; // Handle ESM/CJS interop for ajv-formats using require const require = createRequire(import.meta.url); @@ -35,12 +30,10 @@ try { validateSchema = ajv.compile(configSchema); } catch (error) { const errMsg = error instanceof Error ? error.message : String(error); - throw new PentestError( - `Failed to load configuration schema: ${errMsg}`, - 'config', - false, - { schemaPath: '../configs/config-schema.json', originalError: errMsg } - ); + throw new PentestError(`Failed to load configuration schema: ${errMsg}`, 'config', false, { + schemaPath: '../configs/config-schema.json', + originalError: errMsg, + }); } const DANGEROUS_PATTERNS: RegExp[] = [ @@ -185,7 +178,7 @@ export const parseConfig = async (configPath: string): Promise => { 'config', false, { configPath }, - ErrorCode.CONFIG_NOT_FOUND + ErrorCode.CONFIG_NOT_FOUND, ); } @@ -198,7 +191,7 @@ export const parseConfig = async (configPath: string): Promise => { 'config', false, { configPath, fileSize: stats.size, maxFileSize }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } @@ -211,7 +204,7 @@ export const parseConfig = async (configPath: string): Promise => { 'config', false, { configPath }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } @@ -230,7 +223,7 @@ export const parseConfig = async (configPath: string): Promise => { 'config', false, { configPath, originalError: errMsg }, - ErrorCode.CONFIG_PARSE_ERROR + ErrorCode.CONFIG_PARSE_ERROR, ); } @@ -241,7 +234,7 @@ export const parseConfig = async (configPath: string): Promise => { 'config', false, { configPath }, - ErrorCode.CONFIG_PARSE_ERROR + ErrorCode.CONFIG_PARSE_ERROR, ); } @@ -260,7 +253,7 @@ export const parseConfig = async (configPath: string): Promise => { 'config', false, { configPath, originalError: errMsg }, - ErrorCode.CONFIG_PARSE_ERROR + ErrorCode.CONFIG_PARSE_ERROR, ); } }; @@ -272,7 +265,7 @@ const validateConfig = (config: Config): void => { 'config', false, {}, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } @@ -282,7 +275,7 @@ const validateConfig = (config: Config): void => { 'config', false, {}, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } @@ -295,7 +288,7 @@ const validateConfig = (config: Config): void => { 'config', false, { validationErrors: errorMessages }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } @@ -303,12 +296,10 @@ const validateConfig = (config: Config): void => { if (!config.rules && !config.authentication) { console.warn( - '⚠️ Configuration file contains no rules or authentication. The pentest will run without any scoping restrictions or login capabilities.' + '⚠️ Configuration file contains no rules or authentication. The pentest will run without any scoping restrictions or login capabilities.', ); } else if (config.rules && !config.rules.avoid && !config.rules.focus) { - console.warn( - '⚠️ Configuration file contains no rules. The pentest will run without any scoping restrictions.' - ); + console.warn('⚠️ Configuration file contains no rules. The pentest will run without any scoping restrictions.'); } }; @@ -325,7 +316,7 @@ const performSecurityValidation = (config: Config): void => { 'config', false, { field: 'login_url', pattern: pattern.source }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } } @@ -339,7 +330,7 @@ const performSecurityValidation = (config: Config): void => { 'config', false, { field: 'credentials.username', pattern: pattern.source }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } if (pattern.test(auth.credentials.password)) { @@ -348,7 +339,7 @@ const performSecurityValidation = (config: Config): void => { 'config', false, { field: 'credentials.password', pattern: pattern.source }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } } @@ -363,7 +354,7 @@ const performSecurityValidation = (config: Config): void => { 'config', false, { field: `login_flow[${index}]`, pattern: pattern.source }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } } @@ -392,7 +383,7 @@ const validateRulesSecurity = (rules: Rule[] | undefined, ruleType: string): voi 'config', false, { field: `rules.${ruleType}[${index}].url_path`, pattern: pattern.source }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } if (pattern.test(rule.description)) { @@ -401,7 +392,7 @@ const validateRulesSecurity = (rules: Rule[] | undefined, ruleType: string): voi 'config', false, { field: `rules.${ruleType}[${index}].description`, pattern: pattern.source }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } } @@ -421,7 +412,7 @@ const validateRuleTypeSpecific = (rule: Rule, ruleType: string, index: number): 'config', false, { field, ruleType: rule.type }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } break; @@ -435,7 +426,7 @@ const validateRuleTypeSpecific = (rule: Rule, ruleType: string, index: number): 'config', false, { field, ruleType: rule.type }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } // Must contain at least one dot for domains @@ -445,7 +436,7 @@ const validateRuleTypeSpecific = (rule: Rule, ruleType: string, index: number): 'config', false, { field, ruleType: rule.type }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } break; @@ -458,7 +449,7 @@ const validateRuleTypeSpecific = (rule: Rule, ruleType: string, index: number): 'config', false, { field, ruleType: rule.type, allowedMethods }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } break; @@ -471,7 +462,7 @@ const validateRuleTypeSpecific = (rule: Rule, ruleType: string, index: number): 'config', false, { field, ruleType: rule.type }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } break; @@ -483,7 +474,7 @@ const validateRuleTypeSpecific = (rule: Rule, ruleType: string, index: number): 'config', false, { field, ruleType: rule.type }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } break; @@ -500,7 +491,7 @@ const checkForDuplicates = (rules: Rule[], ruleType: string): void => { 'config', false, { field: `rules.${ruleType}[${index}]`, ruleType: rule.type, urlPath: rule.url_path }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } seen.add(key); @@ -518,7 +509,7 @@ const checkForConflicts = (avoidRules: Rule[] = [], focusRules: Rule[] = []): vo 'config', false, { field: `rules.focus[${index}]`, urlPath: rule.url_path }, - ErrorCode.CONFIG_VALIDATION_FAILED + ErrorCode.CONFIG_VALIDATION_FAILED, ); } }); diff --git a/apps/worker/src/paths.ts b/apps/worker/src/paths.ts new file mode 100644 index 0000000..dc9b71a --- /dev/null +++ b/apps/worker/src/paths.ts @@ -0,0 +1,30 @@ +/** Centralized path constants for the worker package */ + +import fs from 'node:fs'; +import path from 'node:path'; + +/** Worker package root (apps/worker/) resolved from compiled dist/ files */ +const WORKER_ROOT = path.resolve(import.meta.dirname, '..'); + +export const PROMPTS_DIR = path.join(WORKER_ROOT, 'prompts'); +export const CONFIGS_DIR = path.join(WORKER_ROOT, 'configs'); + +/** + * Repository root — walk up from WORKER_ROOT looking for pnpm-workspace.yaml. + * Falls back to two levels up (apps/worker/ → repo root) if not found. + */ +function findRepoRoot(): string { + let dir = WORKER_ROOT; + for (let i = 0; i < 5; i++) { + if (fs.existsSync(path.join(dir, 'pnpm-workspace.yaml'))) { + return dir; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return path.resolve(WORKER_ROOT, '..', '..'); +} + +const REPO_ROOT = findRepoRoot(); +export const WORKSPACES_DIR = path.join(REPO_ROOT, 'workspaces'); diff --git a/src/progress-indicator.ts b/apps/worker/src/progress-indicator.ts similarity index 94% rename from src/progress-indicator.ts rename to apps/worker/src/progress-indicator.ts index 4ecaa0a..961f7e8 100644 --- a/src/progress-indicator.ts +++ b/apps/worker/src/progress-indicator.ts @@ -37,7 +37,7 @@ export class ProgressIndicator { } // Clear the spinner line - process.stdout.write('\r' + ' '.repeat(this.message.length + 5) + '\r'); + process.stdout.write(`\r${' '.repeat(this.message.length + 5)}\r`); this.isRunning = false; } diff --git a/src/services/agent-execution.ts b/apps/worker/src/services/agent-execution.ts similarity index 89% rename from src/services/agent-execution.ts rename to apps/worker/src/services/agent-execution.ts index f0f01df..987f93b 100644 --- a/src/services/agent-execution.ts +++ b/apps/worker/src/services/agent-execution.ts @@ -21,29 +21,20 @@ * No Temporal dependencies - pure domain logic. */ -import type { ActivityLogger } from '../types/activity-logger.js'; -import { Result, ok, err, isErr } from '../types/result.js'; -import { ErrorCode, type PentestErrorType } from '../types/errors.js'; -import { PentestError } from './error-handling.js'; -import { isSpendingCapBehavior } from '../utils/billing-detection.js'; +import { type ClaudePromptResult, runClaudePrompt, validateAgentOutput } from '../ai/claude-executor.js'; +import type { AuditSession } from '../audit/index.js'; import { AGENTS } from '../session-manager.js'; -import { loadPrompt } from './prompt-manager.js'; -import { - runClaudePrompt, - validateAgentOutput, - type ClaudePromptResult, -} from '../ai/claude-executor.js'; -import { - createGitCheckpoint, - commitGitSuccess, - rollbackGitWorkspace, - getGitCommitHash, -} from './git-manager.js'; -import { AuditSession } from '../audit/index.js'; -import type { AgentEndResult } from '../types/audit.js'; +import type { ActivityLogger } from '../types/activity-logger.js'; import type { AgentName } from '../types/agents.js'; -import type { ConfigLoaderService } from './config-loader.js'; +import type { AgentEndResult } from '../types/audit.js'; +import { ErrorCode, type PentestErrorType } from '../types/errors.js'; import type { AgentMetrics } from '../types/metrics.js'; +import { err, isErr, ok, type Result } from '../types/result.js'; +import { isSpendingCapBehavior } from '../utils/billing-detection.js'; +import type { ConfigLoaderService } from './config-loader.js'; +import { PentestError } from './error-handling.js'; +import { commitGitSuccess, createGitCheckpoint, getGitCommitHash, rollbackGitWorkspace } from './git-manager.js'; +import { loadPrompt } from './prompt-manager.js'; /** * Input for agent execution. @@ -94,7 +85,7 @@ export class AgentExecutionService { agentName: AgentName, input: AgentExecutionInput, auditSession: AuditSession, - logger: ActivityLogger + logger: ActivityLogger, ): Promise> { const { webUrl, repoPath, configPath, pipelineTestingMode = false, attemptNumber } = input; @@ -109,13 +100,7 @@ export class AgentExecutionService { const promptTemplate = AGENTS[agentName].promptTemplate; let prompt: string; try { - prompt = await loadPrompt( - promptTemplate, - { webUrl, repoPath }, - distributedConfig, - pipelineTestingMode, - logger - ); + prompt = await loadPrompt(promptTemplate, { webUrl, repoPath }, distributedConfig, pipelineTestingMode, logger); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return err( @@ -124,8 +109,8 @@ export class AgentExecutionService { 'prompt', false, { agentName, promptTemplate, originalError: errorMessage }, - ErrorCode.PROMPT_LOAD_FAILED - ) + ErrorCode.PROMPT_LOAD_FAILED, + ), ); } @@ -140,8 +125,8 @@ export class AgentExecutionService { 'filesystem', false, { agentName, repoPath, originalError: errorMessage }, - ErrorCode.GIT_CHECKPOINT_FAILED - ) + ErrorCode.GIT_CHECKPOINT_FAILED, + ), ); } @@ -157,7 +142,7 @@ export class AgentExecutionService { agentName, auditSession, logger, - AGENTS[agentName].modelTier + AGENTS[agentName].modelTier, ); // 6. Spending cap check - defense-in-depth @@ -165,7 +150,8 @@ export class AgentExecutionService { const resultText = result.result || ''; if (isSpendingCapBehavior(result.turns ?? 0, result.cost || 0, resultText)) { return this.failAgent(agentName, repoPath, auditSession, logger, { - attemptNumber, result, + attemptNumber, + result, rollbackReason: 'spending cap detected', errorMessage: `Spending cap likely reached: ${resultText.slice(0, 100)}`, errorCode: ErrorCode.SPENDING_CAP_REACHED, @@ -179,7 +165,8 @@ export class AgentExecutionService { // 7. Handle execution failure if (!result.success) { return this.failAgent(agentName, repoPath, auditSession, logger, { - attemptNumber, result, + attemptNumber, + result, rollbackReason: 'execution failure', errorMessage: result.error || 'Agent execution failed', errorCode: ErrorCode.AGENT_EXECUTION_FAILED, @@ -193,7 +180,8 @@ export class AgentExecutionService { const validationPassed = await validateAgentOutput(result, agentName, repoPath, logger); if (!validationPassed) { return this.failAgent(agentName, repoPath, auditSession, logger, { - attemptNumber, result, + attemptNumber, + result, rollbackReason: 'validation failure', errorMessage: `Agent ${agentName} failed output validation`, errorCode: ErrorCode.OUTPUT_VALIDATION_FAILED, @@ -225,7 +213,7 @@ export class AgentExecutionService { repoPath: string, auditSession: AuditSession, logger: ActivityLogger, - opts: FailAgentOpts + opts: FailAgentOpts, ): Promise> { await rollbackGitWorkspace(repoPath, opts.rollbackReason, logger); @@ -239,15 +227,7 @@ export class AgentExecutionService { }; await auditSession.endAgent(agentName, endResult); - return err( - new PentestError( - opts.errorMessage, - opts.category, - opts.retryable, - opts.context, - opts.errorCode - ) - ); + return err(new PentestError(opts.errorMessage, opts.category, opts.retryable, opts.context, opts.errorCode)); } /** @@ -267,7 +247,7 @@ export class AgentExecutionService { agentName: AgentName, input: AgentExecutionInput, auditSession: AuditSession, - logger: ActivityLogger + logger: ActivityLogger, ): Promise { const result = await this.execute(agentName, input, auditSession, logger); if (isErr(result)) { diff --git a/src/services/config-loader.ts b/apps/worker/src/services/config-loader.ts similarity index 89% rename from src/services/config-loader.ts rename to apps/worker/src/services/config-loader.ts index 506603b..709a8b8 100644 --- a/src/services/config-loader.ts +++ b/apps/worker/src/services/config-loader.ts @@ -11,11 +11,11 @@ * Pure service with no Temporal dependencies. */ -import { parseConfig, distributeConfig } from '../config-parser.js'; -import { PentestError } from './error-handling.js'; -import { Result, ok, err } from '../types/result.js'; -import { ErrorCode } from '../types/errors.js'; +import { distributeConfig, parseConfig } from '../config-parser.js'; import type { DistributedConfig } from '../types/config.js'; +import { ErrorCode } from '../types/errors.js'; +import { err, ok, type Result } from '../types/result.js'; +import { PentestError } from './error-handling.js'; /** * Service for loading and distributing configuration files. @@ -52,8 +52,8 @@ export class ConfigLoaderService { 'config', false, { configPath, originalError: errorMessage }, - errorCode - ) + errorCode, + ), ); } } @@ -64,9 +64,7 @@ export class ConfigLoaderService { * @param configPath - Optional path to the YAML configuration file * @returns Result containing DistributedConfig (or null) on success, PentestError on failure */ - async loadOptional( - configPath: string | undefined - ): Promise> { + async loadOptional(configPath: string | undefined): Promise> { if (!configPath) { return ok(null); } diff --git a/src/services/container.ts b/apps/worker/src/services/container.ts similarity index 97% rename from src/services/container.ts rename to apps/worker/src/services/container.ts index f0573aa..b494ebe 100644 --- a/src/services/container.ts +++ b/apps/worker/src/services/container.ts @@ -75,10 +75,7 @@ const containers = new Map(); * @param sessionMetadata - Session metadata for audit paths * @returns Container instance for the workflow */ -export function getOrCreateContainer( - workflowId: string, - sessionMetadata: SessionMetadata -): Container { +export function getOrCreateContainer(workflowId: string, sessionMetadata: SessionMetadata): Container { let container = containers.get(workflowId); if (!container) { diff --git a/src/services/error-handling.ts b/apps/worker/src/services/error-handling.ts similarity index 85% rename from src/services/error-handling.ts rename to apps/worker/src/services/error-handling.ts index 1c9c75c..6c002c2 100644 --- a/src/services/error-handling.ts +++ b/apps/worker/src/services/error-handling.ts @@ -4,16 +4,8 @@ // it under the terms of the GNU Affero General Public License version 3 // as published by the Free Software Foundation. -import { - ErrorCode, - type PentestErrorType, - type PentestErrorContext, - type PromptErrorResult, -} from '../types/errors.js'; -import { - matchesBillingApiPattern, - matchesBillingTextPattern, -} from '../utils/billing-detection.js'; +import { ErrorCode, type PentestErrorContext, type PentestErrorType, type PromptErrorResult } from '../types/errors.js'; +import { matchesBillingApiPattern, matchesBillingTextPattern } from '../utils/billing-detection.js'; export class PentestError extends Error { override name = 'PentestError' as const; @@ -29,7 +21,7 @@ export class PentestError extends Error { type: PentestErrorType, retryable: boolean = false, context: PentestErrorContext = {}, - code?: ErrorCode + code?: ErrorCode, ) { super(message); this.type = type; @@ -42,18 +34,13 @@ export class PentestError extends Error { } } -export function handlePromptError( - promptName: string, - error: Error -): PromptErrorResult { +export function handlePromptError(promptName: string, error: Error): PromptErrorResult { return { success: false, - error: new PentestError( - `Failed to load prompt '${promptName}': ${error.message}`, - 'prompt', - false, - { promptName, originalError: error.message } - ), + error: new PentestError(`Failed to load prompt '${promptName}': ${error.message}`, 'prompt', false, { + promptName, + originalError: error.message, + }), }; } @@ -111,10 +98,7 @@ export function isRetryableError(error: Error): boolean { * Classifies errors by ErrorCode for reliable, code-based classification. * Used when error is a PentestError with a specific ErrorCode. */ -function classifyByErrorCode( - code: ErrorCode, - retryableFromError: boolean -): { type: string; retryable: boolean } { +function classifyByErrorCode(code: ErrorCode, retryableFromError: boolean): { type: string; retryable: boolean } { switch (code) { // Billing errors - retryable (wait for cap reset or credits added) case ErrorCode.SPENDING_CAP_REACHED: @@ -206,49 +190,30 @@ export function classifyErrorForTemporal(error: unknown): { type: string; retrya } // Permission (403) - access won't be granted - if ( - message.includes('permission') || - message.includes('forbidden') || - message.includes('403') - ) { + if (message.includes('permission') || message.includes('forbidden') || message.includes('403')) { return { type: 'PermissionError', retryable: false }; } // === OUTPUT VALIDATION ERRORS (Retryable) === // Agent didn't produce expected deliverables - retry may succeed // IMPORTANT: Must come BEFORE generic 'validation' check below - if ( - message.includes('failed output validation') || - message.includes('output validation failed') - ) { + if (message.includes('failed output validation') || message.includes('output validation failed')) { return { type: 'OutputValidationError', retryable: true }; } // Invalid Request (400) - malformed request is permanent // Note: Checked AFTER billing and AFTER output validation - if ( - message.includes('invalid_request_error') || - message.includes('malformed') || - message.includes('validation') - ) { + if (message.includes('invalid_request_error') || message.includes('malformed') || message.includes('validation')) { return { type: 'InvalidRequestError', retryable: false }; } // Request Too Large (413) - won't fit no matter how many retries - if ( - message.includes('request_too_large') || - message.includes('too large') || - message.includes('413') - ) { + if (message.includes('request_too_large') || message.includes('too large') || message.includes('413')) { return { type: 'RequestTooLargeError', retryable: false }; } // Configuration errors - missing files need manual fix - if ( - message.includes('enoent') || - message.includes('no such file') || - message.includes('cli not installed') - ) { + if (message.includes('enoent') || message.includes('no such file') || message.includes('cli not installed')) { return { type: 'ConfigurationError', retryable: false }; } diff --git a/src/services/exploitation-checker.ts b/apps/worker/src/services/exploitation-checker.ts similarity index 94% rename from src/services/exploitation-checker.ts rename to apps/worker/src/services/exploitation-checker.ts index 2ecea91..3786243 100644 --- a/src/services/exploitation-checker.ts +++ b/apps/worker/src/services/exploitation-checker.ts @@ -13,13 +13,9 @@ * No Temporal dependencies - this is pure business logic. */ -import { - validateQueueSafe, - type VulnType, - type ExploitationDecision, -} from './queue-validation.js'; -import { isOk } from '../types/result.js'; import type { ActivityLogger } from '../types/activity-logger.js'; +import { isOk } from '../types/result.js'; +import { type ExploitationDecision, type VulnType, validateQueueSafe } from './queue-validation.js'; /** * Service for checking exploitation queue decisions. @@ -46,7 +42,7 @@ export class ExploitationCheckerService { if (isOk(result)) { const decision = result.value; logger.info( - `${vulnType}: ${decision.shouldExploit ? `${decision.vulnerabilityCount} vulnerabilities found` : 'no vulnerabilities, skipping exploitation'}` + `${vulnType}: ${decision.shouldExploit ? `${decision.vulnerabilityCount} vulnerabilities found` : 'no vulnerabilities, skipping exploitation'}`, ); return decision; } diff --git a/src/services/git-manager.ts b/apps/worker/src/services/git-manager.ts similarity index 86% rename from src/services/git-manager.ts rename to apps/worker/src/services/git-manager.ts index 1f68ab1..de8ebd1 100644 --- a/src/services/git-manager.ts +++ b/apps/worker/src/services/git-manager.ts @@ -5,9 +5,9 @@ // as published by the Free Software Foundation. import { $ } from 'zx'; -import { PentestError } from './error-handling.js'; -import { ErrorCode } from '../types/errors.js'; import type { ActivityLogger } from '../types/activity-logger.js'; +import { ErrorCode } from '../types/errors.js'; +import { PentestError } from './error-handling.js'; /** * Check if a directory is a git repository. @@ -31,15 +31,8 @@ interface GitOperationResult { /** * Get list of changed files from git status --porcelain output */ -async function getChangedFiles( - sourceDir: string, - operationDescription: string -): Promise { - const status = await executeGitCommandWithRetry( - ['git', 'status', '--porcelain'], - sourceDir, - operationDescription - ); +async function getChangedFiles(sourceDir: string, operationDescription: string): Promise { + const status = await executeGitCommandWithRetry(['git', 'status', '--porcelain'], sourceDir, operationDescription); return status.stdout .trim() .split('\n') @@ -55,14 +48,15 @@ function logChangeSummary( messageWithoutChanges: string, logger: ActivityLogger, level: 'info' | 'warn' = 'info', - maxToShow: number = 5 + maxToShow: number = 5, ): void { if (changes.length > 0) { const msg = messageWithChanges.replace('{count}', String(changes.length)); - const fileList = changes.slice(0, maxToShow).map((c) => ` ${c}`).join(', '); - const suffix = changes.length > maxToShow - ? ` ... and ${changes.length - maxToShow} more files` - : ''; + const fileList = changes + .slice(0, maxToShow) + .map((c) => ` ${c}`) + .join(', '); + const suffix = changes.length > maxToShow ? ` ... and ${changes.length - maxToShow} more files` : ''; logger[level](`${msg} ${fileList}${suffix}`); } else { logger[level](messageWithoutChanges); @@ -101,7 +95,7 @@ class GitSemaphore { if (!this.running && this.queue.length > 0) { this.running = true; const resolve = this.queue.shift(); - resolve!(); + resolve?.(); } } } @@ -125,7 +119,7 @@ export async function executeGitCommandWithRetry( commandArgs: string[], sourceDir: string, description: string, - maxRetries: number = 5 + maxRetries: number = 5, ): Promise<{ stdout: string; stderr: string }> { await gitSemaphore.acquire(); @@ -139,11 +133,11 @@ export async function executeGitCommandWithRetry( const errMsg = error instanceof Error ? error.message : String(error); if (isGitLockError(errMsg) && attempt < maxRetries) { - const delay = Math.pow(2, attempt - 1) * 1000; + const delay = 2 ** (attempt - 1) * 1000; // executeGitCommandWithRetry is also called outside activity context // (e.g., from resume logic), so we use console.warn as a fallback here console.warn( - `Git lock conflict during ${description} (attempt ${attempt}/${maxRetries}). Retrying in ${delay}ms...` + `Git lock conflict during ${description} (attempt ${attempt}/${maxRetries}). Retrying in ${delay}ms...`, ); await new Promise((resolve) => setTimeout(resolve, delay)); continue; @@ -157,7 +151,7 @@ export async function executeGitCommandWithRetry( 'filesystem', true, // Retryable - transient git lock issues { maxRetries, description }, - ErrorCode.GIT_CHECKPOINT_FAILED + ErrorCode.GIT_CHECKPOINT_FAILED, ); } finally { gitSemaphore.release(); @@ -168,7 +162,7 @@ export async function executeGitCommandWithRetry( export async function rollbackGitWorkspace( sourceDir: string, reason: string = 'retry preparation', - logger: ActivityLogger + logger: ActivityLogger, ): Promise { // Skip git operations if not a git repository if (!(await isGitRepository(sourceDir))) { @@ -180,16 +174,8 @@ export async function rollbackGitWorkspace( try { const changes = await getChangedFiles(sourceDir, 'status check for rollback'); - await executeGitCommandWithRetry( - ['git', 'reset', '--hard', 'HEAD'], - sourceDir, - 'hard reset for rollback' - ); - await executeGitCommandWithRetry( - ['git', 'clean', '-fd'], - sourceDir, - 'cleaning untracked files for rollback' - ); + await executeGitCommandWithRetry(['git', 'reset', '--hard', 'HEAD'], sourceDir, 'hard reset for rollback'); + await executeGitCommandWithRetry(['git', 'clean', '-fd'], sourceDir, 'cleaning untracked files for rollback'); logChangeSummary( changes, @@ -197,7 +183,7 @@ export async function rollbackGitWorkspace( 'Rollback completed - no changes to remove', logger, 'info', - 3 + 3, ); return { success: true }; } catch (error) { @@ -210,7 +196,7 @@ export async function rollbackGitWorkspace( 'filesystem', false, // Non-retryable - rollback is best-effort cleanup { sourceDir, reason }, - ErrorCode.GIT_ROLLBACK_FAILED + ErrorCode.GIT_ROLLBACK_FAILED, ), }; } @@ -221,7 +207,7 @@ export async function createGitCheckpoint( sourceDir: string, description: string, attempt: number, - logger: ActivityLogger + logger: ActivityLogger, ): Promise { // Skip git operations if not a git repository if (!(await isGitRepository(sourceDir))) { @@ -248,7 +234,7 @@ export async function createGitCheckpoint( await executeGitCommandWithRetry( ['git', 'commit', '-m', `📍 Checkpoint: ${description} (attempt ${attempt})`, '--allow-empty'], sourceDir, - 'creating commit' + 'creating commit', ); // 4. Log result @@ -268,7 +254,7 @@ export async function createGitCheckpoint( export async function commitGitSuccess( sourceDir: string, description: string, - logger: ActivityLogger + logger: ActivityLogger, ): Promise { // Skip git operations if not a git repository if (!(await isGitRepository(sourceDir))) { @@ -280,22 +266,18 @@ export async function commitGitSuccess( try { const changes = await getChangedFiles(sourceDir, 'status check for success commit'); - await executeGitCommandWithRetry( - ['git', 'add', '-A'], - sourceDir, - 'staging changes for success commit' - ); + await executeGitCommandWithRetry(['git', 'add', '-A'], sourceDir, 'staging changes for success commit'); await executeGitCommandWithRetry( ['git', 'commit', '-m', `✅ ${description}: completed successfully`, '--allow-empty'], sourceDir, - 'creating success commit' + 'creating success commit', ); logChangeSummary( changes, 'Success commit created with {count} file changes:', 'Empty success commit created (agent made no file changes)', - logger + logger, ); return { success: true }; } catch (error) { diff --git a/src/services/index.ts b/apps/worker/src/services/index.ts similarity index 99% rename from src/services/index.ts rename to apps/worker/src/services/index.ts index 29c489f..e7c41b8 100644 --- a/src/services/index.ts +++ b/apps/worker/src/services/index.ts @@ -11,13 +11,12 @@ * Services are pure domain logic with no Temporal dependencies. */ -export { Container, getOrCreateContainer, removeContainer } from './container.js'; -export type { ContainerDependencies } from './container.js'; +export type { AgentExecutionInput } from './agent-execution.js'; +export { AgentExecutionService } from './agent-execution.js'; export { ConfigLoaderService } from './config-loader.js'; +export type { ContainerDependencies } from './container.js'; +export { Container, getOrCreateContainer, removeContainer } from './container.js'; export { ExploitationCheckerService } from './exploitation-checker.js'; -export { AgentExecutionService } from './agent-execution.js'; -export type { AgentExecutionInput } from './agent-execution.js'; - -export { assembleFinalReport, injectModelIntoReport } from './reporting.js'; export { loadPrompt } from './prompt-manager.js'; +export { assembleFinalReport, injectModelIntoReport } from './reporting.js'; diff --git a/src/services/preflight.ts b/apps/worker/src/services/preflight.ts similarity index 73% rename from src/services/preflight.ts rename to apps/worker/src/services/preflight.ts index e1b6dbe..ece222a 100644 --- a/src/services/preflight.ts +++ b/apps/worker/src/services/preflight.ts @@ -17,22 +17,19 @@ * 3. Credentials validate via Claude Agent SDK query (API key, OAuth, Bedrock, Vertex AI, or router mode) */ -import fs from 'fs/promises'; -import { query } from '@anthropic-ai/claude-agent-sdk'; +import fs from 'node:fs/promises'; import type { SDKAssistantMessageError } from '@anthropic-ai/claude-agent-sdk'; -import { PentestError, isRetryableError } from './error-handling.js'; -import { ErrorCode } from '../types/errors.js'; -import { type Result, ok, err } from '../types/result.js'; -import { parseConfig } from '../config-parser.js'; +import { query } from '@anthropic-ai/claude-agent-sdk'; import { resolveModel } from '../ai/models.js'; +import { parseConfig } from '../config-parser.js'; import type { ActivityLogger } from '../types/activity-logger.js'; +import { ErrorCode } from '../types/errors.js'; +import { err, ok, type Result } from '../types/result.js'; +import { isRetryableError, PentestError } from './error-handling.js'; // === Repository Validation === -async function validateRepo( - repoPath: string, - logger: ActivityLogger -): Promise> { +async function validateRepo(repoPath: string, logger: ActivityLogger): Promise> { logger.info('Checking repository path...', { repoPath }); // 1. Check repo directory exists @@ -45,8 +42,8 @@ async function validateRepo( 'config', false, { repoPath }, - ErrorCode.REPO_NOT_FOUND - ) + ErrorCode.REPO_NOT_FOUND, + ), ); } } catch { @@ -56,8 +53,8 @@ async function validateRepo( 'config', false, { repoPath }, - ErrorCode.REPO_NOT_FOUND - ) + ErrorCode.REPO_NOT_FOUND, + ), ); } @@ -71,8 +68,8 @@ async function validateRepo( 'config', false, { repoPath }, - ErrorCode.REPO_NOT_FOUND - ) + ErrorCode.REPO_NOT_FOUND, + ), ); } } catch { @@ -82,8 +79,8 @@ async function validateRepo( 'config', false, { repoPath }, - ErrorCode.REPO_NOT_FOUND - ) + ErrorCode.REPO_NOT_FOUND, + ), ); } @@ -93,10 +90,7 @@ async function validateRepo( // === Config Validation === -async function validateConfig( - configPath: string, - logger: ActivityLogger -): Promise> { +async function validateConfig(configPath: string, logger: ActivityLogger): Promise> { logger.info('Validating configuration file...', { configPath }); try { @@ -114,8 +108,8 @@ async function validateConfig( 'config', false, { configPath }, - ErrorCode.CONFIG_VALIDATION_FAILED - ) + ErrorCode.CONFIG_VALIDATION_FAILED, + ), ); } } @@ -123,43 +117,60 @@ async function validateConfig( // === Credential Validation === /** Map SDK error type to a human-readable preflight PentestError. */ -function classifySdkError( - sdkError: SDKAssistantMessageError, - authType: string -): Result { +function classifySdkError(sdkError: SDKAssistantMessageError, authType: string): Result { switch (sdkError) { case 'authentication_failed': - return err(new PentestError( - `Invalid ${authType}. Check your credentials in .env and try again.`, - 'config', false, { authType, sdkError }, ErrorCode.AUTH_FAILED - )); + return err( + new PentestError( + `Invalid ${authType}. Check your credentials in .env and try again.`, + 'config', + false, + { authType, sdkError }, + ErrorCode.AUTH_FAILED, + ), + ); case 'billing_error': - return err(new PentestError( - `Anthropic account has a billing issue. Add credits or check your billing dashboard.`, - 'billing', true, { authType, sdkError }, ErrorCode.BILLING_ERROR - )); + return err( + new PentestError( + `Anthropic account has a billing issue. Add credits or check your billing dashboard.`, + 'billing', + true, + { authType, sdkError }, + ErrorCode.BILLING_ERROR, + ), + ); case 'rate_limit': - return err(new PentestError( - `Anthropic rate limit or spending cap reached. Wait a few minutes and try again.`, - 'billing', true, { authType, sdkError }, ErrorCode.BILLING_ERROR - )); + return err( + new PentestError( + `Anthropic rate limit or spending cap reached. Wait a few minutes and try again.`, + 'billing', + true, + { authType, sdkError }, + ErrorCode.BILLING_ERROR, + ), + ); case 'server_error': - return err(new PentestError( - `Anthropic API is temporarily unavailable. Try again shortly.`, - 'network', true, { authType, sdkError } - )); + return err( + new PentestError(`Anthropic API is temporarily unavailable. Try again shortly.`, 'network', true, { + authType, + sdkError, + }), + ); default: - return err(new PentestError( - `${authType} validation failed unexpectedly. Check your credentials in .env.`, - 'config', false, { authType, sdkError }, ErrorCode.AUTH_FAILED - )); + return err( + new PentestError( + `${authType} validation failed unexpectedly. Check your credentials in .env.`, + 'config', + false, + { authType, sdkError }, + ErrorCode.AUTH_FAILED, + ), + ); } } /** Validate credentials via a minimal Claude Agent SDK query. */ -async function validateCredentials( - logger: ActivityLogger -): Promise> { +async function validateCredentials(logger: ActivityLogger): Promise> { // 1. Custom base URL — validate endpoint is reachable via SDK query if (process.env.ANTHROPIC_BASE_URL) { const baseUrl = process.env.ANTHROPIC_BASE_URL; @@ -193,8 +204,14 @@ async function validateCredentials( // 2. Bedrock mode — validate required AWS credentials are present if (process.env.CLAUDE_CODE_USE_BEDROCK === '1') { - const required = ['AWS_REGION', 'AWS_BEARER_TOKEN_BEDROCK', 'ANTHROPIC_SMALL_MODEL', 'ANTHROPIC_MEDIUM_MODEL', 'ANTHROPIC_LARGE_MODEL']; - const missing = required.filter(v => !process.env[v]); + const required = [ + 'AWS_REGION', + 'AWS_BEARER_TOKEN_BEDROCK', + 'ANTHROPIC_SMALL_MODEL', + 'ANTHROPIC_MEDIUM_MODEL', + 'ANTHROPIC_LARGE_MODEL', + ]; + const missing = required.filter((v) => !process.env[v]); if (missing.length > 0) { return err( new PentestError( @@ -202,8 +219,8 @@ async function validateCredentials( 'config', false, { missing }, - ErrorCode.AUTH_FAILED - ) + ErrorCode.AUTH_FAILED, + ), ); } logger.info('Bedrock credentials OK'); @@ -212,8 +229,14 @@ async function validateCredentials( // 3. Vertex AI mode — validate required GCP credentials are present if (process.env.CLAUDE_CODE_USE_VERTEX === '1') { - const required = ['CLOUD_ML_REGION', 'ANTHROPIC_VERTEX_PROJECT_ID', 'ANTHROPIC_SMALL_MODEL', 'ANTHROPIC_MEDIUM_MODEL', 'ANTHROPIC_LARGE_MODEL']; - const missing = required.filter(v => !process.env[v]); + const required = [ + 'CLOUD_ML_REGION', + 'ANTHROPIC_VERTEX_PROJECT_ID', + 'ANTHROPIC_SMALL_MODEL', + 'ANTHROPIC_MEDIUM_MODEL', + 'ANTHROPIC_LARGE_MODEL', + ]; + const missing = required.filter((v) => !process.env[v]); if (missing.length > 0) { return err( new PentestError( @@ -221,8 +244,8 @@ async function validateCredentials( 'config', false, { missing }, - ErrorCode.AUTH_FAILED - ) + ErrorCode.AUTH_FAILED, + ), ); } // Validate service account credentials file is accessible @@ -234,8 +257,8 @@ async function validateCredentials( 'config', false, {}, - ErrorCode.AUTH_FAILED - ) + ErrorCode.AUTH_FAILED, + ), ); } try { @@ -247,8 +270,8 @@ async function validateCredentials( 'config', false, { credPath }, - ErrorCode.AUTH_FAILED - ) + ErrorCode.AUTH_FAILED, + ), ); } logger.info('Vertex AI credentials OK'); @@ -263,8 +286,8 @@ async function validateCredentials( 'config', false, {}, - ErrorCode.AUTH_FAILED - ) + ErrorCode.AUTH_FAILED, + ), ); } @@ -296,8 +319,8 @@ async function validateCredentials( retryable ? 'network' : 'config', retryable, { authType }, - retryable ? undefined : ErrorCode.AUTH_FAILED - ) + retryable ? undefined : ErrorCode.AUTH_FAILED, + ), ); } } @@ -316,7 +339,7 @@ async function validateCredentials( export async function runPreflightChecks( repoPath: string, configPath: string | undefined, - logger: ActivityLogger + logger: ActivityLogger, ): Promise> { // 1. Repository check (free — filesystem only) const repoResult = await validateRepo(repoPath, logger); diff --git a/src/services/prompt-manager.ts b/apps/worker/src/services/prompt-manager.ts similarity index 78% rename from src/services/prompt-manager.ts rename to apps/worker/src/services/prompt-manager.ts index b852c73..027e918 100644 --- a/src/services/prompt-manager.ts +++ b/apps/worker/src/services/prompt-manager.ts @@ -5,10 +5,11 @@ // as published by the Free Software Foundation. import { fs, path } from 'zx'; -import { PentestError, handlePromptError } from './error-handling.js'; +import { PROMPTS_DIR } from '../paths.js'; import { MCP_AGENT_MAPPING } from '../session-manager.js'; -import type { Authentication, DistributedConfig } from '../types/config.js'; import type { ActivityLogger } from '../types/activity-logger.js'; +import type { Authentication, DistributedConfig } from '../types/config.js'; +import { handlePromptError, PentestError } from './error-handling.js'; interface PromptVariables { webUrl: string; @@ -25,15 +26,10 @@ interface IncludeReplacement { async function buildLoginInstructions(authentication: Authentication, logger: ActivityLogger): Promise { try { // 1. Load the login instructions template - const loginInstructionsPath = path.join(import.meta.dirname, '..', '..', 'prompts', 'shared', 'login-instructions.txt'); + const loginInstructionsPath = path.join(PROMPTS_DIR, 'shared', 'login-instructions.txt'); - if (!await fs.pathExists(loginInstructionsPath)) { - throw new PentestError( - 'Login instructions template not found', - 'filesystem', - false, - { loginInstructionsPath } - ); + if (!(await fs.pathExists(loginInstructionsPath))) { + throw new PentestError('Login instructions template not found', 'filesystem', false, { loginInstructionsPath }); } const fullTemplate = await fs.readFile(loginInstructionsPath, 'utf8'); @@ -57,9 +53,7 @@ async function buildLoginInstructions(authentication: Authentication, logger: Ac logger.warn('Section markers not found, using full login instructions template'); loginInstructions = fullTemplate; } else { - loginInstructions = [commonSection, authSection, verificationSection] - .filter(section => section) - .join('\n\n'); + loginInstructions = [commonSection, authSection, verificationSection].filter((section) => section).join('\n\n'); } // 4. Interpolate login flow and credential placeholders @@ -73,7 +67,10 @@ async function buildLoginInstructions(authentication: Authentication, logger: Ac userInstructions = userInstructions.replace(/\$password/g, authentication.credentials.password); } if (authentication.credentials.totp_secret) { - userInstructions = userInstructions.replace(/\$totp/g, `generated TOTP code using secret "${authentication.credentials.totp_secret}"`); + userInstructions = userInstructions.replace( + /\$totp/g, + `generated TOTP code using secret "${authentication.credentials.totp_secret}"`, + ); } } @@ -90,12 +87,10 @@ async function buildLoginInstructions(authentication: Authentication, logger: Ac throw error; } const errMsg = error instanceof Error ? error.message : String(error); - throw new PentestError( - `Failed to build login instructions: ${errMsg}`, - 'config', - false, - { authentication, originalError: errMsg } - ); + throw new PentestError(`Failed to build login instructions: ${errMsg}`, 'config', false, { + authentication, + originalError: errMsg, + }); } } @@ -112,7 +107,7 @@ async function processIncludes(content: string, baseDir: string): Promise { try { if (!template || typeof template !== 'string') { - throw new PentestError( - 'Template must be a non-empty string', - 'validation', - false, - { templateType: typeof template, templateLength: template?.length } - ); + throw new PentestError('Template must be a non-empty string', 'validation', false, { + templateType: typeof template, + templateLength: template?.length, + }); } if (!variables || !variables.webUrl || !variables.repoPath) { - throw new PentestError( - 'Variables must include webUrl and repoPath', - 'validation', - false, - { variables: Object.keys(variables || {}) } - ); + throw new PentestError('Variables must include webUrl and repoPath', 'validation', false, { + variables: Object.keys(variables || {}), + }); } let result = template @@ -170,12 +160,10 @@ async function interpolateVariables( const cleanRulesSection = '\nNo specific rules or focus areas provided for this test.\n'; result = result.replace(/[\s\S]*?<\/rules>/g, cleanRulesSection); } else { - const avoidRules = hasAvoidRules ? config.avoid!.map(r => `- ${r.description}`).join('\n') : 'None'; - const focusRules = hasFocusRules ? config.focus!.map(r => `- ${r.description}`).join('\n') : 'None'; + const avoidRules = hasAvoidRules ? config.avoid?.map((r) => `- ${r.description}`).join('\n') : 'None'; + const focusRules = hasFocusRules ? config.focus?.map((r) => `- ${r.description}`).join('\n') : 'None'; - result = result - .replace(/{{RULES_AVOID}}/g, avoidRules) - .replace(/{{RULES_FOCUS}}/g, focusRules); + result = result.replace(/{{RULES_AVOID}}/g, avoidRules).replace(/{{RULES_FOCUS}}/g, focusRules); } // Extract and inject login instructions from config @@ -204,12 +192,7 @@ async function interpolateVariables( throw error; } const errMsg = error instanceof Error ? error.message : String(error); - throw new PentestError( - `Variable interpolation failed: ${errMsg}`, - 'prompt', - false, - { originalError: errMsg } - ); + throw new PentestError(`Variable interpolation failed: ${errMsg}`, 'prompt', false, { originalError: errMsg }); } } @@ -219,25 +202,19 @@ export async function loadPrompt( variables: PromptVariables, config: DistributedConfig | null = null, pipelineTestingMode: boolean = false, - logger: ActivityLogger + logger: ActivityLogger, ): Promise { try { // 1. Resolve prompt file path - const baseDir = pipelineTestingMode ? 'prompts/pipeline-testing' : 'prompts'; - const promptsDir = path.join(import.meta.dirname, '..', '..', baseDir); + const promptsDir = pipelineTestingMode ? path.join(PROMPTS_DIR, 'pipeline-testing') : PROMPTS_DIR; const promptPath = path.join(promptsDir, `${promptName}.txt`); if (pipelineTestingMode) { logger.info(`Using pipeline testing prompt: ${promptPath}`); } - if (!await fs.pathExists(promptPath)) { - throw new PentestError( - `Prompt file not found: ${promptPath}`, - 'prompt', - false, - { promptName, promptPath } - ); + if (!(await fs.pathExists(promptPath))) { + throw new PentestError(`Prompt file not found: ${promptPath}`, 'prompt', false, { promptName, promptPath }); } // 2. Assign MCP server based on agent name diff --git a/src/services/queue-validation.ts b/apps/worker/src/services/queue-validation.ts similarity index 87% rename from src/services/queue-validation.ts rename to apps/worker/src/services/queue-validation.ts index dde8666..86a5a04 100644 --- a/src/services/queue-validation.ts +++ b/apps/worker/src/services/queue-validation.ts @@ -5,13 +5,13 @@ // as published by the Free Software Foundation. import { fs, path } from 'zx'; -import { PentestError } from './error-handling.js'; +import type { ExploitationDecision, VulnType } from '../types/agents.js'; import { ErrorCode } from '../types/errors.js'; -import { type Result, ok, err } from '../types/result.js'; +import { err, ok, type Result } from '../types/result.js'; import { asyncPipe } from '../utils/functional.js'; -import type { VulnType, ExploitationDecision } from '../types/agents.js'; +import { PentestError } from './error-handling.js'; -export type { VulnType, ExploitationDecision } from '../types/agents.js'; +export type { ExploitationDecision, VulnType } from '../types/agents.js'; interface VulnTypeConfigItem { deliverable: string; @@ -63,7 +63,6 @@ interface QueueValidationResult { error: string | null; } - /** * Result type for safe validation - explicit error handling. */ @@ -97,7 +96,7 @@ const VULN_TYPE_CONFIG: VulnTypeConfig = Object.freeze({ function createValidationRule( predicate: (existence: FileExistence) => boolean, errorMessage: ErrorMessageResolver, - retryable: boolean = true + retryable: boolean = true, ): ValidationRule { return Object.freeze({ predicate, errorMessage, retryable }); } @@ -106,7 +105,7 @@ function createValidationRule( const fileExistenceRules: readonly ValidationRule[] = Object.freeze([ createValidationRule( ({ deliverableExists, queueExists }) => deliverableExists && queueExists, - getExistenceErrorMessage + getExistenceErrorMessage, ), ]); @@ -124,19 +123,11 @@ function getExistenceErrorMessage(existence: FileExistence): string { } // Pure function to create file paths -const createPaths = ( - vulnType: VulnType, - sourceDir: string -): PathsBase | PathsWithError => { +const createPaths = (vulnType: VulnType, sourceDir: string): PathsBase | PathsWithError => { const config = VULN_TYPE_CONFIG[vulnType]; if (!config) { return { - error: new PentestError( - `Unknown vulnerability type: ${vulnType}`, - 'validation', - false, - { vulnType } - ), + error: new PentestError(`Unknown vulnerability type: ${vulnType}`, 'validation', false, { vulnType }), }; } @@ -149,9 +140,7 @@ const createPaths = ( }; // Pure function to check file existence -const checkFileExistence = async ( - paths: PathsBase | PathsWithError -): Promise => { +const checkFileExistence = async (paths: PathsBase | PathsWithError): Promise => { if ('error' in paths) return paths; const [deliverableExists, queueExists] = await Promise.all([ @@ -167,7 +156,7 @@ const checkFileExistence = async ( // Validates deliverable/queue symmetry - both must exist or neither const validateExistenceRules = ( - pathsWithExistence: PathsWithExistence | PathsWithError + pathsWithExistence: PathsWithExistence | PathsWithError, ): PathsWithExistence | PathsWithError => { if ('error' in pathsWithExistence) return pathsWithExistence; @@ -178,9 +167,7 @@ const validateExistenceRules = ( if (failedRule) { const message = - typeof failedRule.errorMessage === 'function' - ? failedRule.errorMessage(existence) - : failedRule.errorMessage; + typeof failedRule.errorMessage === 'function' ? failedRule.errorMessage(existence) : failedRule.errorMessage; return { error: new PentestError( @@ -193,7 +180,7 @@ const validateExistenceRules = ( queuePath: pathsWithExistence.queue, existence, }, - ErrorCode.DELIVERABLE_NOT_FOUND + ErrorCode.DELIVERABLE_NOT_FOUND, ), }; } @@ -227,7 +214,7 @@ const validateQueueStructure = (content: string): QueueValidationResult => { // Queue parse failures are retryable - agent can fix malformed JSON on retry const validateQueueContent = async ( - pathsWithExistence: PathsWithExistence | PathsWithError + pathsWithExistence: PathsWithExistence | PathsWithError, ): Promise => { if ('error' in pathsWithExistence) return pathsWithExistence; @@ -249,7 +236,7 @@ const validateQueueContent = async ( queuePath: pathsWithExistence.queue, originalError: queueValidation.error, queueStructure: queueValidation.data ? Object.keys(queueValidation.data) : [], - } + }, ), }; } @@ -268,16 +255,14 @@ const validateQueueContent = async ( vulnType: pathsWithExistence.vulnType, queuePath: pathsWithExistence.queue, originalError: readError instanceof Error ? readError.message : String(readError), - } + }, ), }; } }; // Final decision: skip if queue says no vulns, proceed if vulns found, error otherwise -const determineExploitationDecision = ( - validatedData: PathsWithQueue | PathsWithError -): ExploitationDecision => { +const determineExploitationDecision = (validatedData: PathsWithQueue | PathsWithError): ExploitationDecision => { if ('error' in validatedData) { throw validatedData.error; } @@ -297,14 +282,14 @@ const determineExploitationDecision = ( // Main functional validation pipeline export async function validateQueueAndDeliverable( vulnType: VulnType, - sourceDir: string + sourceDir: string, ): Promise { return asyncPipe( createPaths(vulnType, sourceDir), checkFileExistence, validateExistenceRules, validateQueueContent, - determineExploitationDecision + determineExploitationDecision, ); } @@ -312,10 +297,7 @@ export async function validateQueueAndDeliverable( * Safely validate queue and deliverable files. * Returns Result for explicit error handling. */ -export async function validateQueueSafe( - vulnType: VulnType, - sourceDir: string -): Promise { +export async function validateQueueSafe(vulnType: VulnType, sourceDir: string): Promise { try { const result = await validateQueueAndDeliverable(vulnType, sourceDir); return ok(result); diff --git a/src/services/reporting.ts b/apps/worker/src/services/reporting.ts similarity index 90% rename from src/services/reporting.ts rename to apps/worker/src/services/reporting.ts index bc04fe1..c138927 100644 --- a/src/services/reporting.ts +++ b/apps/worker/src/services/reporting.ts @@ -5,9 +5,9 @@ // as published by the Free Software Foundation. import { fs, path } from 'zx'; -import { PentestError } from './error-handling.js'; -import { ErrorCode } from '../types/errors.js'; import type { ActivityLogger } from '../types/activity-logger.js'; +import { ErrorCode } from '../types/errors.js'; +import { PentestError } from './error-handling.js'; interface DeliverableFile { name: string; @@ -22,7 +22,7 @@ export async function assembleFinalReport(sourceDir: string, logger: ActivityLog { name: 'XSS', path: 'xss_exploitation_evidence.md', required: false }, { name: 'Authentication', path: 'auth_exploitation_evidence.md', required: false }, { name: 'SSRF', path: 'ssrf_exploitation_evidence.md', required: false }, - { name: 'Authorization', path: 'authz_exploitation_evidence.md', required: false } + { name: 'Authorization', path: 'authz_exploitation_evidence.md', required: false }, ]; const sections: string[] = []; @@ -40,7 +40,7 @@ export async function assembleFinalReport(sourceDir: string, logger: ActivityLog 'filesystem', false, { deliverableFile: file.path, sourceDir }, - ErrorCode.DELIVERABLE_NOT_FOUND + ErrorCode.DELIVERABLE_NOT_FOUND, ); } else { logger.info(`No ${file.name} deliverable found`); @@ -65,12 +65,10 @@ export async function assembleFinalReport(sourceDir: string, logger: ActivityLog logger.info(`Final report assembled at ${finalReportPath}`); } catch (error) { const err = error as Error; - throw new PentestError( - `Failed to write final report: ${err.message}`, - 'filesystem', - false, - { finalReportPath, originalError: err.message } - ); + throw new PentestError(`Failed to write final report: ${err.message}`, 'filesystem', false, { + finalReportPath, + originalError: err.message, + }); } return finalContent; @@ -84,7 +82,7 @@ export async function assembleFinalReport(sourceDir: string, logger: ActivityLog export async function injectModelIntoReport( repoPath: string, outputPath: string, - logger: ActivityLogger + logger: ActivityLogger, ): Promise { // 1. Read session.json to get model information const sessionJsonPath = path.join(outputPath, 'session.json'); @@ -136,20 +134,14 @@ export async function injectModelIntoReport( if (match) { // Inject model line after Assessment Date const modelLine = `- Model: ${modelStr}`; - reportContent = reportContent.replace( - assessmentDatePattern, - `$1\n${modelLine}` - ); + reportContent = reportContent.replace(assessmentDatePattern, `$1\n${modelLine}`); logger.info('Model info injected into Executive Summary'); } else { // If no Assessment Date line found, try to add after Executive Summary header const execSummaryPattern = /^## Executive Summary$/m; if (reportContent.match(execSummaryPattern)) { // Add model as first item in Executive Summary - reportContent = reportContent.replace( - execSummaryPattern, - `## Executive Summary\n- Model: ${modelStr}` - ); + reportContent = reportContent.replace(execSummaryPattern, `## Executive Summary\n- Model: ${modelStr}`); logger.info('Model info added to Executive Summary header'); } else { logger.warn('Could not find Executive Summary section'); diff --git a/src/session-manager.ts b/apps/worker/src/session-manager.ts similarity index 95% rename from src/session-manager.ts rename to apps/worker/src/session-manager.ts index a17cc7d..b87eafd 100644 --- a/src/session-manager.ts +++ b/apps/worker/src/session-manager.ts @@ -4,10 +4,10 @@ // it under the terms of the GNU Affero General Public License version 3 // as published by the Free Software Foundation. -import { path, fs } from 'zx'; +import { fs, path } from 'zx'; import { validateQueueAndDeliverable } from './services/queue-validation.js'; -import type { AgentName, AgentDefinition, PlaywrightAgent, AgentValidator, VulnType } from './types/index.js'; import type { ActivityLogger } from './types/activity-logger.js'; +import type { AgentDefinition, AgentName, AgentValidator, PlaywrightAgent, VulnType } from './types/index.js'; // Agent definitions according to PRD // NOTE: deliverableFilename values must match mcp-server/src/types/deliverables.ts:DELIVERABLE_FILENAMES @@ -20,7 +20,7 @@ export const AGENTS: Readonly> = Object.freez deliverableFilename: 'code_analysis_deliverable.md', modelTier: 'large', }, - 'recon': { + recon: { name: 'recon', displayName: 'Recon agent', prerequisites: ['pre-recon'], @@ -97,7 +97,7 @@ export const AGENTS: Readonly> = Object.freez promptTemplate: 'exploit-authz', deliverableFilename: 'authz_exploitation_evidence.md', }, - 'report': { + report: { name: 'report', displayName: 'Report agent', prerequisites: ['injection-exploit', 'xss-exploit', 'auth-exploit', 'ssrf-exploit', 'authz-exploit'], @@ -113,7 +113,7 @@ export type PhaseName = 'pre-recon' | 'recon' | 'vulnerability-analysis' | 'expl // Map agents to their corresponding phases (single source of truth) export const AGENT_PHASE_MAP: Readonly> = Object.freeze({ 'pre-recon': 'pre-recon', - 'recon': 'recon', + recon: 'recon', 'injection-vuln': 'vulnerability-analysis', 'xss-vuln': 'vulnerability-analysis', 'auth-vuln': 'vulnerability-analysis', @@ -124,7 +124,7 @@ export const AGENT_PHASE_MAP: Readonly> = Object.fr 'auth-exploit': 'exploitation', 'authz-exploit': 'exploitation', 'ssrf-exploit': 'exploitation', - 'report': 'reporting', + report: 'reporting', }); // Factory function for vulnerability queue validators @@ -210,11 +210,7 @@ export const AGENT_VALIDATORS: Record = Object.freeze // Executive report agent report: async (sourceDir: string, logger: ActivityLogger): Promise => { - const reportFile = path.join( - sourceDir, - 'deliverables', - 'comprehensive_security_assessment_report.md' - ); + const reportFile = path.join(sourceDir, 'deliverables', 'comprehensive_security_assessment_report.md'); const reportExists = await fs.pathExists(reportFile); diff --git a/src/temporal/activities.ts b/apps/worker/src/temporal/activities.ts similarity index 93% rename from src/temporal/activities.ts rename to apps/worker/src/temporal/activities.ts index 1f6dc1a..1d89193 100644 --- a/src/temporal/activities.ts +++ b/apps/worker/src/temporal/activities.ts @@ -15,29 +15,28 @@ * Business logic is delegated to services in src/services/. */ -import { heartbeat, ApplicationFailure, Context } from '@temporalio/activity'; -import path from 'path'; -import fs from 'fs/promises'; - -import { classifyErrorForTemporal, PentestError } from '../services/error-handling.js'; -import { ErrorCode } from '../types/errors.js'; -import { getOrCreateContainer, getContainer, removeContainer } from '../services/container.js'; -import { ExploitationCheckerService } from '../services/exploitation-checker.js'; -import type { VulnType, ExploitationDecision } from '../services/queue-validation.js'; +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { ApplicationFailure, Context, heartbeat } from '@temporalio/activity'; import { AuditSession } from '../audit/index.js'; -import type { WorkflowSummary } from '../audit/workflow-logger.js'; -import type { AgentName } from '../types/agents.js'; -import { ALL_AGENTS } from '../types/agents.js'; -import type { AgentMetrics, ResumeState } from './shared.js'; +import type { ResumeAttempt } from '../audit/metrics-tracker.js'; import { copyDeliverablesToAudit, type SessionMetadata } from '../audit/utils.js'; -import { readJson, fileExists } from '../utils/file-io.js'; +import type { WorkflowSummary } from '../audit/workflow-logger.js'; +import { getContainer, getOrCreateContainer, removeContainer } from '../services/container.js'; +import { classifyErrorForTemporal, PentestError } from '../services/error-handling.js'; +import { ExploitationCheckerService } from '../services/exploitation-checker.js'; +import { executeGitCommandWithRetry } from '../services/git-manager.js'; +import { runPreflightChecks } from '../services/preflight.js'; +import type { ExploitationDecision, VulnType } from '../services/queue-validation.js'; import { assembleFinalReport, injectModelIntoReport } from '../services/reporting.js'; import { AGENTS } from '../session-manager.js'; -import { executeGitCommandWithRetry } from '../services/git-manager.js'; -import type { ResumeAttempt } from '../audit/metrics-tracker.js'; -import { createActivityLogger } from './activity-logger.js'; -import { runPreflightChecks } from '../services/preflight.js'; +import type { AgentName } from '../types/agents.js'; +import { ALL_AGENTS } from '../types/agents.js'; +import { ErrorCode } from '../types/errors.js'; import { isErr } from '../types/result.js'; +import { fileExists, readJson } from '../utils/file-io.js'; +import { createActivityLogger } from './activity-logger.js'; +import type { AgentMetrics, ResumeState } from './shared.js'; // Max lengths to prevent Temporal protobuf buffer overflow const MAX_ERROR_MESSAGE_LENGTH = 2000; @@ -68,7 +67,7 @@ function truncateErrorMessage(message: string): string { if (message.length <= MAX_ERROR_MESSAGE_LENGTH) { return message; } - return message.slice(0, MAX_ERROR_MESSAGE_LENGTH - 20) + '\n[truncated]'; + return `${message.slice(0, MAX_ERROR_MESSAGE_LENGTH - 20)}\n[truncated]`; } /** @@ -76,7 +75,7 @@ function truncateErrorMessage(message: string): string { */ function truncateStackTrace(failure: ApplicationFailure): void { if (failure.stack && failure.stack.length > MAX_STACK_TRACE_LENGTH) { - failure.stack = failure.stack.slice(0, MAX_STACK_TRACE_LENGTH) + '\n[stack truncated]'; + failure.stack = `${failure.stack.slice(0, MAX_STACK_TRACE_LENGTH)}\n[stack truncated]`; } } @@ -102,10 +101,7 @@ function buildSessionMetadata(input: ActivityInput): SessionMetadata { * 3. Service-based agent execution * 4. Error classification for Temporal retry */ -async function runAgentActivity( - agentName: AgentName, - input: ActivityInput -): Promise { +async function runAgentActivity(agentName: AgentName, input: ActivityInput): Promise { const { repoPath, configPath, pipelineTestingMode = false, workflowId, webUrl } = input; const startTime = Date.now(); const attemptNumber = Context.current().info.attempt; @@ -140,7 +136,7 @@ async function runAgentActivity( attemptNumber, }, auditSession, - logger + logger, ); // 4. Return metrics @@ -167,7 +163,7 @@ async function runAgentActivity( throw ApplicationFailure.nonRetryable( `Agent ${agentName} failed output validation after ${attemptNumber} attempts`, 'OutputValidationError', - [{ agentName, attemptNumber, elapsed: Date.now() - startTime }] + [{ agentName, attemptNumber, elapsed: Date.now() - startTime }], ); } @@ -335,9 +331,7 @@ export async function assembleReportActivity(input: ActivityInput): Promise { const { repoPath, sessionId, outputPath } = input; const logger = createActivityLogger(); - const effectiveOutputPath = outputPath - ? path.join(outputPath, sessionId) - : path.join('./workspaces', sessionId); + const effectiveOutputPath = outputPath ? path.join(outputPath, sessionId) : path.join('./workspaces', sessionId); try { await injectModelIntoReport(repoPath, effectiveOutputPath, logger); } catch (error) { @@ -352,10 +346,7 @@ export async function injectReportMetadataActivity(input: ActivityInput): Promis * Uses existing container if available (from prior agent runs), * otherwise creates service directly (stateless, no dependencies). */ -export async function checkExploitationQueue( - input: ActivityInput, - vulnType: VulnType -): Promise { +export async function checkExploitationQueue(input: ActivityInput, vulnType: VulnType): Promise { const { repoPath, workflowId } = input; const logger = createActivityLogger(); @@ -391,7 +382,7 @@ interface SessionJson { export async function loadResumeState( workspaceName: string, expectedUrl: string, - expectedRepoPath: string + expectedRepoPath: string, ): Promise { // 1. Validate workspace exists const sessionPath = path.join('./workspaces', workspaceName, 'session.json'); @@ -400,7 +391,7 @@ export async function loadResumeState( if (!exists) { throw ApplicationFailure.nonRetryable( `Workspace not found: ${workspaceName}\nExpected path: ${sessionPath}`, - 'WorkspaceNotFoundError' + 'WorkspaceNotFoundError', ); } @@ -412,14 +403,14 @@ export async function loadResumeState( const errorMsg = error instanceof Error ? error.message : String(error); throw ApplicationFailure.nonRetryable( `Corrupted session.json in workspace ${workspaceName}: ${errorMsg}`, - 'CorruptedSessionError' + 'CorruptedSessionError', ); } if (session.session.webUrl !== expectedUrl) { throw ApplicationFailure.nonRetryable( `URL mismatch with workspace\n Workspace URL: ${session.session.webUrl}\n Provided URL: ${expectedUrl}`, - 'URLMismatchError' + 'URLMismatchError', ); } @@ -463,7 +454,7 @@ export async function loadResumeState( `but their deliverable files are missing from disk. ` + `Start a fresh run instead.` : `No agents completed successfully. Start a fresh run instead.`), - 'NoCheckpointsError' + 'NoCheckpointsError', ); } @@ -497,7 +488,7 @@ async function findLatestCommit(repoPath: string, commitHashes: string[]): Promi 'filesystem', false, // Non-retryable - corrupt workspace state { phase: 'resume' }, - ErrorCode.GIT_CHECKPOINT_FAILED + ErrorCode.GIT_CHECKPOINT_FAILED, ); } return hash; @@ -506,7 +497,7 @@ async function findLatestCommit(repoPath: string, commitHashes: string[]): Promi const result = await executeGitCommandWithRetry( ['git', 'rev-list', '--max-count=1', ...commitHashes], repoPath, - 'find latest commit' + 'find latest commit', ); return result.stdout.trim(); @@ -518,7 +509,7 @@ async function findLatestCommit(repoPath: string, commitHashes: string[]): Promi export async function restoreGitCheckpoint( repoPath: string, checkpointHash: string, - incompleteAgents: AgentName[] + incompleteAgents: AgentName[], ): Promise { const logger = createActivityLogger(); logger.info(`Restoring git workspace to ${checkpointHash}...`); @@ -526,13 +517,9 @@ export async function restoreGitCheckpoint( await executeGitCommandWithRetry( ['git', 'reset', '--hard', checkpointHash], repoPath, - 'reset to checkpoint for resume' - ); - await executeGitCommandWithRetry( - ['git', 'clean', '-fd'], - repoPath, - 'clean untracked files for resume' + 'reset to checkpoint for resume', ); + await executeGitCommandWithRetry(['git', 'clean', '-fd'], repoPath, 'clean untracked files for resume'); for (const agentName of incompleteAgents) { const deliverableFilename = AGENTS[agentName].deliverableFilename; @@ -559,7 +546,7 @@ export async function recordResumeAttempt( terminatedWorkflows: string[], checkpointHash: string, previousWorkflowId: string, - completedAgents: string[] + completedAgents: string[], ): Promise { const sessionMetadata = buildSessionMetadata(input); const auditSession = new AuditSession(sessionMetadata); @@ -583,7 +570,7 @@ export async function recordResumeAttempt( export async function logPhaseTransition( input: ActivityInput, phase: string, - event: 'start' | 'complete' + event: 'start' | 'complete', ): Promise { const sessionMetadata = buildSessionMetadata(input); const auditSession = new AuditSession(sessionMetadata); @@ -600,10 +587,7 @@ export async function logPhaseTransition( * Log workflow completion with full summary. * Cleans up container when done. */ -export async function logWorkflowComplete( - input: ActivityInput, - summary: WorkflowSummary -): Promise { +export async function logWorkflowComplete(input: ActivityInput, summary: WorkflowSummary): Promise { const { repoPath, workflowId } = input; const sessionMetadata = buildSessionMetadata(input); diff --git a/src/temporal/activity-logger.ts b/apps/worker/src/temporal/activity-logger.ts similarity index 100% rename from src/temporal/activity-logger.ts rename to apps/worker/src/temporal/activity-logger.ts diff --git a/src/temporal/shared.ts b/apps/worker/src/temporal/shared.ts similarity index 99% rename from src/temporal/shared.ts rename to apps/worker/src/temporal/shared.ts index c0c8d20..c7615f1 100644 --- a/src/temporal/shared.ts +++ b/apps/worker/src/temporal/shared.ts @@ -1,8 +1,9 @@ import { defineQuery } from '@temporalio/workflow'; export type { AgentMetrics } from '../types/metrics.js'; -import type { AgentMetrics } from '../types/metrics.js'; + import type { PipelineConfig } from '../types/config.js'; +import type { AgentMetrics } from '../types/metrics.js'; export interface PipelineInput { webUrl: string; diff --git a/src/temporal/summary-mapper.ts b/apps/worker/src/temporal/summary-mapper.ts similarity index 82% rename from src/temporal/summary-mapper.ts rename to apps/worker/src/temporal/summary-mapper.ts index e160026..7b44160 100644 --- a/src/temporal/summary-mapper.ts +++ b/apps/worker/src/temporal/summary-mapper.ts @@ -9,8 +9,8 @@ * Pure function with no side effects. */ -import type { PipelineState } from './shared.js'; import type { WorkflowSummary } from '../audit/workflow-logger.js'; +import type { PipelineState } from './shared.js'; /** * Maps PipelineState to WorkflowSummary. @@ -19,10 +19,7 @@ import type { WorkflowSummary } from '../audit/workflow-logger.js'; * safely imported into Temporal workflows. The caller must ensure * state.summary is set before calling (via computeSummary). */ -export function toWorkflowSummary( - state: PipelineState, - status: 'completed' | 'failed' -): WorkflowSummary { +export function toWorkflowSummary(state: PipelineState, status: 'completed' | 'failed'): WorkflowSummary { // state.summary must be computed before calling this mapper const summary = state.summary; if (!summary) { @@ -35,10 +32,7 @@ export function toWorkflowSummary( totalCostUsd: summary.totalCostUsd, completedAgents: state.completedAgents, agentMetrics: Object.fromEntries( - Object.entries(state.agentMetrics).map(([name, m]) => [ - name, - { durationMs: m.durationMs, costUsd: m.costUsd }, - ]) + Object.entries(state.agentMetrics).map(([name, m]) => [name, { durationMs: m.durationMs, costUsd: m.costUsd }]), ), ...(state.error && { error: state.error }), }; diff --git a/src/temporal/worker.ts b/apps/worker/src/temporal/worker.ts similarity index 91% rename from src/temporal/worker.ts rename to apps/worker/src/temporal/worker.ts index d33f558..b8c5a93 100644 --- a/src/temporal/worker.ts +++ b/apps/worker/src/temporal/worker.ts @@ -1,4 +1,5 @@ #!/usr/bin/env node + // Copyright (C) 2025 Keygraph, Inc. // // This program is free software: you can redistribute it and/or modify @@ -26,18 +27,18 @@ * TEMPORAL_ADDRESS - Temporal server address (default: localhost:7233) */ -import { NativeConnection, Worker, bundleWorkflowCode } from '@temporalio/worker'; -import { Connection, Client, WorkflowNotFoundError, type WorkflowHandle } from '@temporalio/client'; -import { fileURLToPath } from 'node:url'; -import path from 'node:path'; import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { Client, Connection, type WorkflowHandle, WorkflowNotFoundError } from '@temporalio/client'; +import { bundleWorkflowCode, NativeConnection, Worker } from '@temporalio/worker'; import dotenv from 'dotenv'; -import * as activities from './activities.js'; import { sanitizeHostname } from '../audit/utils.js'; -import { readJson, fileExists } from '../utils/file-io.js'; import { parseConfig } from '../config-parser.js'; import type { PipelineConfig } from '../types/config.js'; -import type { PipelineInput, PipelineState, PipelineProgress } from './shared.js'; +import { fileExists, readJson } from '../utils/file-io.js'; +import * as activities from './activities.js'; +import type { PipelineInput, PipelineProgress, PipelineState } from './shared.js'; dotenv.config(); @@ -61,9 +62,7 @@ function showUsage(): void { console.log('\nShannon Worker'); console.log('Combined worker + client for pentest pipeline\n'); console.log('Usage:'); - console.log( - ' node dist/temporal/worker.js --task-queue [options]\n' - ); + console.log(' node dist/temporal/worker.js --task-queue [options]\n'); console.log('Options:'); console.log(' --task-queue Task queue name (required)'); console.log(' --config Configuration file path'); @@ -135,7 +134,10 @@ function parseCliArgs(argv: string[]): CliArgs { } return { - webUrl, repoPath, taskQueue, pipelineTestingMode, + webUrl, + repoPath, + taskQueue, + pipelineTestingMode, ...(configPath && { configPath }), ...(outputPath && { outputPath }), ...(resumeFromWorkspace && { resumeFromWorkspace }), @@ -167,17 +169,11 @@ interface WorkspaceResolution { terminatedWorkflows: string[]; } -async function terminateExistingWorkflows( - client: Client, - workspaceName: string -): Promise { +async function terminateExistingWorkflows(client: Client, workspaceName: string): Promise { const sessionPath = path.join('./workspaces', workspaceName, 'session.json'); if (!(await fileExists(sessionPath))) { - throw new Error( - `Workspace not found: ${workspaceName}\n` + - `Expected path: ${sessionPath}` - ); + throw new Error(`Workspace not found: ${workspaceName}\n` + `Expected path: ${sessionPath}`); } const session = await readJson(sessionPath); @@ -214,10 +210,7 @@ async function terminateExistingWorkflows( return terminated; } -async function resolveWorkspace( - client: Client, - args: CliArgs -): Promise { +async function resolveWorkspace(client: Client, args: CliArgs): Promise { if (!args.resumeFromWorkspace) { const hostname = sanitizeHostname(args.webUrl); const workflowId = `${hostname}_shannon-${Date.now()}`; @@ -269,9 +262,7 @@ async function resolveWorkspace( // If the workspace name already looks like a CLI-generated ID // (ends with _shannon-), use it directly to avoid double _shannon- suffixes - const workflowId = /_shannon-\d+$/.test(workspace) - ? workspace - : `${workspace}_shannon-${Date.now()}`; + const workflowId = /_shannon-\d+$/.test(workspace) ? workspace : `${workspace}_shannon-${Date.now()}`; return { workflowId, @@ -304,7 +295,9 @@ async function loadPipelineConfig(configPath: string | undefined): Promise Promise>, - workspace: WorkspaceResolution + workspace: WorkspaceResolution, ): Promise { const progressInterval = setInterval(async () => { try { const progress = await handle.query(PROGRESS_QUERY); const elapsed = Math.floor(progress.elapsedMs / 1000); console.log( - `[${elapsed}s] Phase: ${progress.currentPhase || 'unknown'} | Agent: ${progress.currentAgent || 'none'} | Completed: ${progress.completedAgents.length}/13` + `[${elapsed}s] Phase: ${progress.currentPhase || 'unknown'} | Agent: ${progress.currentAgent || 'none'} | Completed: ${progress.completedAgents.length}/13`, ); } catch { // Workflow may have completed @@ -350,9 +343,7 @@ async function waitForWorkflowResult( if (workspace.isResume) { try { - const session = await readJson( - path.join('./workspaces', workspace.sessionId, 'session.json') - ); + const session = await readJson(path.join('./workspaces', workspace.sessionId, 'session.json')); console.log(`Cumulative cost: $${session.metrics.total_cost_usd.toFixed(4)}`); } catch { // Non-fatal @@ -437,7 +428,7 @@ async function run(): Promise { taskQueue: args.taskQueue, workflowId: workspace.workflowId, args: [input], - } + }, ); // 7. Wait for workflow result diff --git a/src/temporal/workflow-errors.ts b/apps/worker/src/temporal/workflow-errors.ts similarity index 88% rename from src/temporal/workflow-errors.ts rename to apps/worker/src/temporal/workflow-errors.ts index 3062080..ceb1670 100644 --- a/src/temporal/workflow-errors.ts +++ b/apps/worker/src/temporal/workflow-errors.ts @@ -11,11 +11,9 @@ /** Maps Temporal error type strings to actionable remediation hints. */ const REMEDIATION_HINTS: Record = { - AuthenticationError: - 'Verify ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN in .env is valid and not expired.', + AuthenticationError: 'Verify ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN in .env is valid and not expired.', ConfigurationError: 'Check your CONFIG file path and contents.', - BillingError: - 'Check your Anthropic billing dashboard. Add credits or wait for spending cap reset.', + BillingError: 'Check your Anthropic billing dashboard. Add credits or wait for spending cap reset.', GitError: 'Check repository path and git state.', InvalidTargetError: 'Verify the target URL is correct and accessible.', PermissionError: 'Check file and network permissions.', @@ -59,11 +57,7 @@ function unwrapActivityError(error: unknown): { * Format a structured error string from workflow catch context. * Segments are delimited by | for multi-line rendering by WorkflowLogger. */ -export function formatWorkflowError( - error: unknown, - currentPhase: string | null, - currentAgent: string | null -): string { +export function formatWorkflowError(error: unknown, currentPhase: string | null, currentAgent: string | null): string { const unwrapped = unwrapActivityError(error); // Phase context (first segment) diff --git a/src/temporal/workflows.ts b/apps/worker/src/temporal/workflows.ts similarity index 91% rename from src/temporal/workflows.ts rename to apps/worker/src/temporal/workflows.ts index 82bcb2c..be2eabe 100644 --- a/src/temporal/workflows.ts +++ b/apps/worker/src/temporal/workflows.ts @@ -23,26 +23,21 @@ * - Graceful failure handling: pipelines continue if one fails */ -import { - log, - proxyActivities, - setHandler, - workflowInfo, -} from '@temporalio/workflow'; +import { log, proxyActivities, setHandler, workflowInfo } from '@temporalio/workflow'; +import type { AgentName, VulnType } from '../types/agents.js'; +import { ALL_AGENTS } from '../types/agents.js'; import type * as activities from './activities.js'; import type { ActivityInput } from './activities.js'; import { + type AgentMetrics, getProgress, type PipelineInput, - type PipelineState, type PipelineProgress, + type PipelineState, type PipelineSummary, - type VulnExploitPipelineResult, - type AgentMetrics, type ResumeState, + type VulnExploitPipelineResult, } from './shared.js'; -import type { AgentName, VulnType } from '../types/agents.js'; -import { ALL_AGENTS } from '../types/agents.js'; import { toWorkflowSummary } from './summary-mapper.js'; import { formatWorkflowError } from './workflow-errors.js'; @@ -132,9 +127,7 @@ function computeSummary(state: PipelineState): PipelineSummary { }; } -export async function pentestPipelineWorkflow( - input: PipelineInput -): Promise { +export async function pentestPipelineWorkflow(input: PipelineInput): Promise { const { workflowId } = workflowInfo(); // Select activity proxy based on mode: testing (fast), subscription (extended), or default @@ -158,11 +151,14 @@ export async function pentestPipelineWorkflow( summary: null, }; - setHandler(getProgress, (): PipelineProgress => ({ - ...state, - workflowId, - elapsedMs: Date.now() - state.startTime, - })); + setHandler( + getProgress, + (): PipelineProgress => ({ + ...state, + workflowId, + elapsedMs: Date.now() - state.startTime, + }), + ); // Build ActivityInput with required workflowId for audit correlation // Activities require workflowId (non-optional), PipelineInput has it optional @@ -186,22 +182,14 @@ export async function pentestPipelineWorkflow( if (input.resumeFromWorkspace) { // 1. Load resume state (validates workspace, cross-checks deliverables) - resumeState = await a.loadResumeState( - input.resumeFromWorkspace, - input.webUrl, - input.repoPath - ); + resumeState = await a.loadResumeState(input.resumeFromWorkspace, input.webUrl, input.repoPath); // 2. Restore git workspace and clean up incomplete deliverables const incompleteAgents = ALL_AGENTS.filter( - (agentName) => !resumeState!.completedAgents.includes(agentName) + (agentName) => !resumeState?.completedAgents.includes(agentName), ) as AgentName[]; - await a.restoreGitCheckpoint( - input.repoPath, - resumeState.checkpointHash, - incompleteAgents - ); + await a.restoreGitCheckpoint(input.repoPath, resumeState.checkpointHash, incompleteAgents); // 3. Short-circuit if all agents already completed if (resumeState.completedAgents.length === ALL_AGENTS.length) { @@ -218,7 +206,7 @@ export async function pentestPipelineWorkflow( input.terminatedWorkflows || [], resumeState.checkpointHash, resumeState.originalWorkflowId, - resumeState.completedAgents + resumeState.completedAgents, ); log.info('Resume state loaded and workspace restored'); @@ -232,7 +220,7 @@ export async function pentestPipelineWorkflow( async function runSequentialPhase( phaseName: string, agentName: AgentName, - runAgent: (input: ActivityInput) => Promise + runAgent: (input: ActivityInput) => Promise, ): Promise { if (!shouldSkip(agentName)) { state.currentPhase = phaseName; @@ -295,9 +283,7 @@ export async function pentestPipelineWorkflow( } // Aggregate results from settled pipeline promises into workflow state - function aggregatePipelineResults( - results: PromiseSettledResult[] - ): void { + function aggregatePipelineResults(results: PromiseSettledResult[]): void { const failedPipelines: string[] = []; for (const result of results) { @@ -320,10 +306,7 @@ export async function pentestPipelineWorkflow( state.completedAgents.push(exploitAgentName); } } else { - const errorMsg = - result.reason instanceof Error - ? result.reason.message - : String(result.reason); + const errorMsg = result.reason instanceof Error ? result.reason.message : String(result.reason); failedPipelines.push(errorMsg); } } @@ -340,16 +323,24 @@ export async function pentestPipelineWorkflow( // NOTE: Results are in completion order, not input order. Callers must key on value fields, not index. async function runWithConcurrencyLimit( thunks: Array<() => Promise>, - limit: number + limit: number, ): Promise[]> { const results: PromiseSettledResult[] = []; const inFlight = new Set>(); for (const thunk of thunks) { - const slot = thunk().then( - (value) => { results.push({ status: 'fulfilled', value }); }, - (reason: unknown) => { results.push({ status: 'rejected', reason }); } - ).finally(() => { inFlight.delete(slot); }); + const slot = thunk() + .then( + (value) => { + results.push({ status: 'fulfilled', value }); + }, + (reason: unknown) => { + results.push({ status: 'rejected', reason }); + }, + ) + .finally(() => { + inFlight.delete(slot); + }); inFlight.add(slot); @@ -389,7 +380,7 @@ export async function pentestPipelineWorkflow( async function runVulnExploitPipeline( vulnType: VulnType, runVulnAgent: () => Promise, - runExploitAgent: () => Promise + runExploitAgent: () => Promise, ): Promise { const vulnAgentName = `${vulnType}-vuln`; const exploitAgentName = `${vulnType}-exploit`; @@ -434,9 +425,7 @@ export async function pentestPipelineWorkflow( for (const config of pipelineConfigs) { if (!shouldSkip(config.vulnAgent) || !shouldSkip(config.exploitAgent)) { - pipelineThunks.push( - () => runVulnExploitPipeline(config.vulnType, config.runVuln, config.runExploit) - ); + pipelineThunks.push(() => runVulnExploitPipeline(config.vulnType, config.runVuln, config.runExploit)); } else { log.info(`Skipping entire ${config.vulnType} pipeline (both agents complete)`); state.completedAgents.push(config.vulnAgent, config.exploitAgent); @@ -460,7 +449,7 @@ export async function pentestPipelineWorkflow( await a.assembleReportActivity(activityInput); // Then run the report agent to add executive summary and clean up - state.agentMetrics['report'] = await a.runReportAgent(activityInput); + state.agentMetrics.report = await a.runReportAgent(activityInput); state.completedAgents.push('report'); // Inject model metadata into the final report diff --git a/src/temporal/workspaces.ts b/apps/worker/src/temporal/workspaces.ts similarity index 82% rename from src/temporal/workspaces.ts rename to apps/worker/src/temporal/workspaces.ts index 28f6e37..ab3f39f 100644 --- a/src/temporal/workspaces.ts +++ b/apps/worker/src/temporal/workspaces.ts @@ -18,8 +18,9 @@ * WORKSPACES_DIR - Override workspaces directory (default: ./workspaces) */ -import fs from 'fs/promises'; -import path from 'path'; +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { WORKSPACES_DIR as DEFAULT_WORKSPACES_DIR } from '../paths.js'; interface SessionJson { session: { @@ -63,11 +64,11 @@ function getStatusDisplay(status: string): string { function truncate(str: string, maxLen: number): string { if (str.length <= maxLen) return str; - return str.slice(0, maxLen - 1) + '\u2026'; + return `${str.slice(0, maxLen - 1)}\u2026`; } async function listWorkspaces(): Promise { - const workspacesDir = process.env.WORKSPACES_DIR || './workspaces'; + const workspacesDir = process.env.WORKSPACES_DIR || DEFAULT_WORKSPACES_DIR; let entries: string[]; try { @@ -120,13 +121,13 @@ async function listWorkspaces(): Promise { // Header console.log( ' ' + - 'WORKSPACE'.padEnd(nameWidth) + - 'URL'.padEnd(urlWidth) + - 'STATUS'.padEnd(statusWidth) + - 'DURATION'.padEnd(durationWidth) + - 'COST'.padEnd(costWidth) + 'WORKSPACE'.padEnd(nameWidth) + + 'URL'.padEnd(urlWidth) + + 'STATUS'.padEnd(statusWidth) + + 'DURATION'.padEnd(durationWidth) + + 'COST'.padEnd(costWidth), ); - console.log(' ' + '\u2500'.repeat(nameWidth + urlWidth + statusWidth + durationWidth + costWidth)); + console.log(` ${'\u2500'.repeat(nameWidth + urlWidth + statusWidth + durationWidth + costWidth)}`); let resumableCount = 0; @@ -146,12 +147,12 @@ async function listWorkspaces(): Promise { console.log( ' ' + - truncate(ws.name, nameWidth - 2).padEnd(nameWidth) + - truncate(ws.url, urlWidth - 2).padEnd(urlWidth) + - getStatusDisplay(ws.status).padEnd(statusWidth) + - duration.padEnd(durationWidth) + - cost.padEnd(costWidth) + - resumeTag + truncate(ws.name, nameWidth - 2).padEnd(nameWidth) + + truncate(ws.url, urlWidth - 2).padEnd(urlWidth) + + getStatusDisplay(ws.status).padEnd(statusWidth) + + duration.padEnd(durationWidth) + + cost.padEnd(costWidth) + + resumeTag, ); } diff --git a/src/types/activity-logger.ts b/apps/worker/src/types/activity-logger.ts similarity index 100% rename from src/types/activity-logger.ts rename to apps/worker/src/types/activity-logger.ts diff --git a/src/types/agents.ts b/apps/worker/src/types/agents.ts similarity index 91% rename from src/types/agents.ts rename to apps/worker/src/types/agents.ts index 41b5873..1fd916a 100644 --- a/src/types/agents.ts +++ b/apps/worker/src/types/agents.ts @@ -32,7 +32,7 @@ export const ALL_AGENTS = [ * Agent name type derived from ALL_AGENTS. * This ensures type safety and prevents drift between type and array. */ -export type AgentName = typeof ALL_AGENTS[number]; +export type AgentName = (typeof ALL_AGENTS)[number]; export type PlaywrightAgent = | 'playwright-agent1' @@ -45,12 +45,7 @@ import type { ActivityLogger } from './activity-logger.js'; export type AgentValidator = (sourceDir: string, logger: ActivityLogger) => Promise; -export type AgentStatus = - | 'pending' - | 'in_progress' - | 'completed' - | 'failed' - | 'rolled-back'; +export type AgentStatus = 'pending' | 'in_progress' | 'completed' | 'failed' | 'rolled-back'; export interface AgentDefinition { name: AgentName; diff --git a/src/types/audit.ts b/apps/worker/src/types/audit.ts similarity index 100% rename from src/types/audit.ts rename to apps/worker/src/types/audit.ts diff --git a/src/types/config.ts b/apps/worker/src/types/config.ts similarity index 92% rename from src/types/config.ts rename to apps/worker/src/types/config.ts index 6f2fb99..9e4e170 100644 --- a/src/types/config.ts +++ b/apps/worker/src/types/config.ts @@ -8,13 +8,7 @@ * Configuration type definitions */ -export type RuleType = - | 'path' - | 'subdomain' - | 'domain' - | 'method' - | 'header' - | 'parameter'; +export type RuleType = 'path' | 'subdomain' | 'domain' | 'method' | 'header' | 'parameter'; export interface Rule { description: string; diff --git a/src/types/errors.ts b/apps/worker/src/types/errors.ts similarity index 100% rename from src/types/errors.ts rename to apps/worker/src/types/errors.ts diff --git a/src/types/index.ts b/apps/worker/src/types/index.ts similarity index 100% rename from src/types/index.ts rename to apps/worker/src/types/index.ts index 8cf4cd4..81d3dd1 100644 --- a/src/types/index.ts +++ b/apps/worker/src/types/index.ts @@ -9,9 +9,9 @@ */ export * from './activity-logger.js'; -export * from './errors.js'; -export * from './config.js'; export * from './agents.js'; export * from './audit.js'; -export * from './result.js'; +export * from './config.js'; +export * from './errors.js'; export * from './metrics.js'; +export * from './result.js'; diff --git a/src/types/metrics.ts b/apps/worker/src/types/metrics.ts similarity index 100% rename from src/types/metrics.ts rename to apps/worker/src/types/metrics.ts diff --git a/src/types/result.ts b/apps/worker/src/types/result.ts similarity index 100% rename from src/types/result.ts rename to apps/worker/src/types/result.ts diff --git a/src/utils/billing-detection.ts b/apps/worker/src/utils/billing-detection.ts similarity index 96% rename from src/utils/billing-detection.ts rename to apps/worker/src/utils/billing-detection.ts index a5258f9..6f25c72 100644 --- a/src/utils/billing-detection.ts +++ b/apps/worker/src/utils/billing-detection.ts @@ -81,11 +81,7 @@ export function matchesBillingApiPattern(message: string): boolean { * @param resultText - The result text from the agent * @returns true if this looks like a spending cap hit */ -export function isSpendingCapBehavior( - turns: number, - cost: number, - resultText: string -): boolean { +export function isSpendingCapBehavior(turns: number, cost: number, resultText: string): boolean { // Only check if turns <= 2 AND cost is exactly 0 if (turns > 2 || cost !== 0) { return false; diff --git a/src/utils/concurrency.ts b/apps/worker/src/utils/concurrency.ts similarity index 98% rename from src/utils/concurrency.ts rename to apps/worker/src/utils/concurrency.ts index 1edf03b..c3f308e 100644 --- a/src/utils/concurrency.ts +++ b/apps/worker/src/utils/concurrency.ts @@ -51,7 +51,7 @@ export class SessionMutex { // Return unlock function return () => { this.locks.delete(sessionId); - resolve!(); + resolve?.(); }; } } diff --git a/src/utils/file-io.ts b/apps/worker/src/utils/file-io.ts similarity index 98% rename from src/utils/file-io.ts rename to apps/worker/src/utils/file-io.ts index 0f35c83..f77459a 100644 --- a/src/utils/file-io.ts +++ b/apps/worker/src/utils/file-io.ts @@ -11,7 +11,7 @@ * directory creation, and JSON file handling. */ -import fs from 'fs/promises'; +import fs from 'node:fs/promises'; /** * Ensure directory exists (idempotent, race-safe) diff --git a/src/utils/formatting.ts b/apps/worker/src/utils/formatting.ts similarity index 100% rename from src/utils/formatting.ts rename to apps/worker/src/utils/formatting.ts diff --git a/src/utils/functional.ts b/apps/worker/src/utils/functional.ts similarity index 86% rename from src/utils/functional.ts rename to apps/worker/src/utils/functional.ts index ee1dac7..3019ea5 100644 --- a/src/utils/functional.ts +++ b/apps/worker/src/utils/functional.ts @@ -17,10 +17,7 @@ type PipelineFunction = (x: any) => any | Promise; * Async pipeline that passes result through a series of functions. * Clearer than reduce-based pipe and easier to debug. */ -export async function asyncPipe( - initial: unknown, - ...fns: PipelineFunction[] -): Promise { +export async function asyncPipe(initial: unknown, ...fns: PipelineFunction[]): Promise { let result = initial; for (const fn of fns) { result = await fn(result); diff --git a/src/utils/metrics.ts b/apps/worker/src/utils/metrics.ts similarity index 100% rename from src/utils/metrics.ts rename to apps/worker/src/utils/metrics.ts diff --git a/apps/worker/tsconfig.json b/apps/worker/tsconfig.json new file mode 100644 index 0000000..d18fa3c --- /dev/null +++ b/apps/worker/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { "rootDir": "./src", "outDir": "./dist" }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..0383e5d --- /dev/null +++ b/biome.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.4.7/schema.json", + "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 120 + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "style": { + "noNonNullAssertion": "warn", + "useConst": "error", + "noParameterAssign": "off" + }, + "suspicious": { "noExplicitAny": "warn" }, + "correctness": { + "noUnusedVariables": "off", + "noUnusedImports": "error" + } + } + }, + "javascript": { + "formatter": { "quoteStyle": "single", "semicolons": "always", "trailingCommas": "all" } + }, + "files": { "includes": ["apps/**/*.ts", "packages/**/*.ts", "*.json", "apps/**/*.json", "packages/**/*.json"] } +} diff --git a/cli/src/commands/logs.ts b/cli/src/commands/logs.ts deleted file mode 100644 index 1e8bdd2..0000000 --- a/cli/src/commands/logs.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * `shannon logs` command — tail a workspace's workflow log. - */ - -import fs from 'node:fs'; -import path from 'node:path'; -import { getWorkspacesDir } from '../home.js'; - -export function logs(workspaceId: string): void { - const workspacesDir = getWorkspacesDir(); - let logFile = ''; - - // 1. Direct match - const directPath = path.join(workspacesDir, workspaceId, 'workflow.log'); - if (fs.existsSync(directPath)) { - logFile = directPath; - } - - // 2. Resume workflow ID (e.g. workspace_resume_123) - if (!logFile) { - const base = workspaceId.replace(/_resume_\d+$/, ''); - if (base !== workspaceId) { - const resumePath = path.join(workspacesDir, base, 'workflow.log'); - if (fs.existsSync(resumePath)) { - logFile = resumePath; - } - } - } - - // 3. Named workspace ID (e.g. workspace_shannon-123) - if (!logFile) { - const base = workspaceId.replace(/_shannon-\d+$/, ''); - if (base !== workspaceId) { - const namedPath = path.join(workspacesDir, base, 'workflow.log'); - if (fs.existsSync(namedPath)) { - logFile = namedPath; - } - } - } - - if (!logFile) { - console.error(`ERROR: Workflow log not found for: ${workspaceId}`); - console.error(''); - console.error('Possible causes:'); - console.error(' - Workflow hasn\'t started yet'); - console.error(' - Workspace ID is incorrect'); - console.error(''); - console.error('Check the Temporal Web UI at http://localhost:8233 for workflow details'); - process.exit(1); - } - - console.log(`Tailing workflow log: ${logFile}`); - - // Stream existing content, then watch for new lines - const stream = fs.createReadStream(logFile, { encoding: 'utf-8' }); - stream.pipe(process.stdout); - - stream.on('end', () => { - // Switch to watching for appended content - let position = fs.statSync(logFile).size; - const watcher = fs.watch(logFile, () => { - const stat = fs.statSync(logFile); - if (stat.size <= position) return; - - const chunk = fs.createReadStream(logFile, { start: position, encoding: 'utf-8' }); - chunk.pipe(process.stdout, { end: false }); - position = stat.size; - }); - - process.on('SIGINT', () => { - watcher.close(); - process.exit(0); - }); - }); -} diff --git a/configs/router-config.json b/configs/router-config.json deleted file mode 100644 index cf57b1e..0000000 --- a/configs/router-config.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "HOST": "0.0.0.0", - "APIKEY": "shannon-router-key", - "LOG": true, - "LOG_LEVEL": "info", - "NON_INTERACTIVE_MODE": true, - "API_TIMEOUT_MS": 600000, - "Providers": [ - { - "name": "openai", - "api_base_url": "https://api.openai.com/v1/chat/completions", - "api_key": "$OPENAI_API_KEY", - "models": ["gpt-5.2", "gpt-5-mini"], - "transformer": { - "use": [["maxcompletiontokens", { "max_completion_tokens": 16384 }]] - } - }, - { - "name": "openrouter", - "api_base_url": "https://openrouter.ai/api/v1/chat/completions", - "api_key": "$OPENROUTER_API_KEY", - "models": [ - "google/gemini-3-flash-preview" - ], - "transformer": { - "use": ["openrouter"] - } - } - ], - "Router": { - "default": "$ROUTER_DEFAULT" - } -} diff --git a/docker-compose.yml b/docker-compose.yml index 951e05c..6c0e5a4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,7 +34,7 @@ services: ports: - "127.0.0.1:3456:3456" volumes: - - ./configs/router-config.json:/config/router-config.json:ro + - ./apps/cli/infra/router-config.json:/config/router-config.json:ro environment: - HOST=0.0.0.0 - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-} diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 645bf4d..0000000 --- a/package-lock.json +++ /dev/null @@ -1,2391 +0,0 @@ -{ - "name": "shannon", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "shannon", - "version": "0.0.0", - "workspaces": [ - "cli", - "mcp-server" - ], - "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.2.38", - "@temporalio/activity": "^1.11.0", - "@temporalio/client": "^1.11.0", - "@temporalio/worker": "^1.11.0", - "@temporalio/workflow": "^1.11.0", - "ajv": "^8.12.0", - "ajv-formats": "^2.1.1", - "dotenv": "^16.4.5", - "js-yaml": "^4.1.0", - "zx": "^8.0.0" - }, - "devDependencies": { - "@types/js-yaml": "^4.0.9", - "@types/node": "^25.0.3", - "typescript": "^5.9.3" - } - }, - "cli": { - "name": "@keygraph/shannon", - "version": "0.0.0", - "license": "AGPL-3.0-only", - "dependencies": { - "@clack/prompts": "^1.1.0", - "dotenv": "^17.3.1", - "smol-toml": "^1.6.0" - }, - "bin": { - "shannon": "dist/index.js" - }, - "engines": { - "node": ">=18" - } - }, - "cli/node_modules/dotenv": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", - "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "mcp-server": { - "name": "@shannon/mcp-server", - "version": "1.0.0", - "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.2.38", - "zod": "^4.3.6" - } - }, - "node_modules/@anthropic-ai/claude-agent-sdk": { - "version": "0.2.38", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.2.38.tgz", - "integrity": "sha512-U1vpf3rlSkw1qUlzC6CBibBA30ouQnla9JnuqYFLQ2zBb1U2NUCXIElrnV7RwWrI5e9ZKCHgR+1uaCwROONo7w==", - "license": "SEE LICENSE IN README.md", - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "^0.33.5", - "@img/sharp-darwin-x64": "^0.33.5", - "@img/sharp-linux-arm": "^0.33.5", - "@img/sharp-linux-arm64": "^0.33.5", - "@img/sharp-linux-x64": "^0.33.5", - "@img/sharp-linuxmusl-arm64": "^0.33.5", - "@img/sharp-linuxmusl-x64": "^0.33.5", - "@img/sharp-win32-x64": "^0.33.5" - }, - "peerDependencies": { - "zod": "^4.0.0" - } - }, - "node_modules/@clack/core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.1.0.tgz", - "integrity": "sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA==", - "license": "MIT", - "dependencies": { - "sisteransi": "^1.0.5" - } - }, - "node_modules/@clack/prompts": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.1.0.tgz", - "integrity": "sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g==", - "license": "MIT", - "dependencies": { - "@clack/core": "1.1.0", - "sisteransi": "^1.0.5" - } - }, - "node_modules/@grpc/grpc-js": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", - "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==", - "license": "Apache-2.0", - "dependencies": { - "@grpc/proto-loader": "^0.8.0", - "@js-sdsl/ordered-map": "^4.4.2" - }, - "engines": { - "node": ">=12.10.0" - } - }, - "node_modules/@grpc/proto-loader": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", - "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", - "license": "Apache-2.0", - "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.5.3", - "yargs": "^17.7.2" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", - "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", - "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", - "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", - "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", - "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", - "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", - "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", - "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", - "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.5" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", - "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", - "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", - "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", - "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", - "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "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/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/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "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/@js-sdsl/ordered-map": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", - "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/@jsonjoy.com/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/buffers": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", - "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/codegen": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz", - "integrity": "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/json-pack": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz", - "integrity": "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/base64": "^1.1.2", - "@jsonjoy.com/buffers": "^1.2.0", - "@jsonjoy.com/codegen": "^1.0.0", - "@jsonjoy.com/json-pointer": "^1.0.2", - "@jsonjoy.com/util": "^1.9.0", - "hyperdyperid": "^1.2.0", - "thingies": "^2.5.0", - "tree-dump": "^1.1.0" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/json-pointer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz", - "integrity": "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/codegen": "^1.0.0", - "@jsonjoy.com/util": "^1.9.0" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/util": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.9.0.tgz", - "integrity": "sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/buffers": "^1.0.0", - "@jsonjoy.com/codegen": "^1.0.0" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@keygraph/shannon": { - "resolved": "cli", - "link": true - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "license": "BSD-3-Clause" - }, - "node_modules/@shannon/mcp-server": { - "resolved": "mcp-server", - "link": true - }, - "node_modules/@swc/core": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.8.tgz", - "integrity": "sha512-T8keoJjXaSUoVBCIjgL6wAnhADIb09GOELzKg10CjNg+vLX48P93SME6jTfte9MZIm5m+Il57H3rTSk/0kzDUw==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.25" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-darwin-arm64": "1.15.8", - "@swc/core-darwin-x64": "1.15.8", - "@swc/core-linux-arm-gnueabihf": "1.15.8", - "@swc/core-linux-arm64-gnu": "1.15.8", - "@swc/core-linux-arm64-musl": "1.15.8", - "@swc/core-linux-x64-gnu": "1.15.8", - "@swc/core-linux-x64-musl": "1.15.8", - "@swc/core-win32-arm64-msvc": "1.15.8", - "@swc/core-win32-ia32-msvc": "1.15.8", - "@swc/core-win32-x64-msvc": "1.15.8" - }, - "peerDependencies": { - "@swc/helpers": ">=0.5.17" - }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } - } - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.8.tgz", - "integrity": "sha512-M9cK5GwyWWRkRGwwCbREuj6r8jKdES/haCZ3Xckgkl8MUQJZA3XB7IXXK1IXRNeLjg6m7cnoMICpXv1v1hlJOg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.8.tgz", - "integrity": "sha512-j47DasuOvXl80sKJHSi2X25l44CMc3VDhlJwA7oewC1nV1VsSzwX+KOwE5tLnfORvVJJyeiXgJORNYg4jeIjYQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.8.tgz", - "integrity": "sha512-siAzDENu2rUbwr9+fayWa26r5A9fol1iORG53HWxQL1J8ym4k7xt9eME0dMPXlYZDytK5r9sW8zEA10F2U3Xwg==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.8.tgz", - "integrity": "sha512-o+1y5u6k2FfPYbTRUPvurwzNt5qd0NTumCTFscCNuBksycloXY16J8L+SMW5QRX59n4Hp9EmFa3vpvNHRVv1+Q==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.8.tgz", - "integrity": "sha512-koiCqL09EwOP1S2RShCI7NbsQuG6r2brTqUYE7pV7kZm9O17wZ0LSz22m6gVibpwEnw8jI3IE1yYsQTVpluALw==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.8.tgz", - "integrity": "sha512-4p6lOMU3bC+Vd5ARtKJ/FxpIC5G8v3XLoPEZ5s7mLR8h7411HWC/LmTXDHcrSXRC55zvAVia1eldy6zDLz8iFQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.8.tgz", - "integrity": "sha512-z3XBnbrZAL+6xDGAhJoN4lOueIxC/8rGrJ9tg+fEaeqLEuAtHSW2QHDHxDwkxZMjuF/pZ6MUTjHjbp8wLbuRLA==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.8.tgz", - "integrity": "sha512-djQPJ9Rh9vP8GTS/Df3hcc6XP6xnG5c8qsngWId/BLA9oX6C7UzCPAn74BG/wGb9a6j4w3RINuoaieJB3t+7iQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.8.tgz", - "integrity": "sha512-/wfAgxORg2VBaUoFdytcVBVCgf1isWZIEXB9MZEUty4wwK93M/PxAkjifOho9RN3WrM3inPLabICRCEgdHpKKQ==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.8.tgz", - "integrity": "sha512-GpMePrh9Sl4d61o4KAHOOv5is5+zt6BEXCOCgs/H0FLGeii7j9bWDE8ExvKFy2GRRZVNR1ugsnzaGWHKM6kuzA==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "license": "Apache-2.0" - }, - "node_modules/@swc/types": { - "version": "0.1.25", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", - "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", - "license": "Apache-2.0", - "dependencies": { - "@swc/counter": "^0.1.3" - } - }, - "node_modules/@temporalio/activity": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@temporalio/activity/-/activity-1.14.1.tgz", - "integrity": "sha512-wG2fTNgomhcKOzPY7mqhKqe8scawm4BvUYdgX1HJouHmVNRgtZurf2xQWJZQOTxWrsXfdoYqzohZLzxlNtcC5A==", - "license": "MIT", - "dependencies": { - "@temporalio/client": "1.14.1", - "@temporalio/common": "1.14.1", - "abort-controller": "^3.0.0" - }, - "engines": { - "node": ">= 18.0.0" - } - }, - "node_modules/@temporalio/client": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@temporalio/client/-/client-1.14.1.tgz", - "integrity": "sha512-AfWSA0LYzBvDLFiFgrPWqTGGq1NGnF3d4xKnxf0PGxSmv5SLb/aqQ9lzHg4DJ5UNkHO4M/NwzdxzzoaR1J5F8Q==", - "license": "MIT", - "dependencies": { - "@grpc/grpc-js": "^1.12.4", - "@temporalio/common": "1.14.1", - "@temporalio/proto": "1.14.1", - "abort-controller": "^3.0.0", - "long": "^5.2.3", - "uuid": "^11.1.0" - }, - "engines": { - "node": ">= 18.0.0" - } - }, - "node_modules/@temporalio/common": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@temporalio/common/-/common-1.14.1.tgz", - "integrity": "sha512-y49wOm3AIEKZufIQ/QU5JhTSaHJIEkiUt5bGB0/uSzCg8P4g8Cz0XoVPSbDwuCix533O9cOKcliYq7Gzjt/sIA==", - "license": "MIT", - "dependencies": { - "@temporalio/proto": "1.14.1", - "long": "^5.2.3", - "ms": "3.0.0-canary.1", - "nexus-rpc": "^0.0.1", - "proto3-json-serializer": "^2.0.0" - }, - "engines": { - "node": ">= 18.0.0" - } - }, - "node_modules/@temporalio/core-bridge": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@temporalio/core-bridge/-/core-bridge-1.14.1.tgz", - "integrity": "sha512-mrXXIFK5yNvsSZsTejLnL64JMuMliQjFKktSGITm2Ci7cWZ/ZTOVN6u+hCsUKfadYYv83jSuOC9Xe3z3RK273w==", - "license": "MIT", - "dependencies": { - "@grpc/grpc-js": "^1.12.4", - "@temporalio/common": "1.14.1" - }, - "engines": { - "node": ">= 18.0.0" - } - }, - "node_modules/@temporalio/nexus": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@temporalio/nexus/-/nexus-1.14.1.tgz", - "integrity": "sha512-51oTeJ8nntAMF8boFSlzVdHlyC7y/LaLQPZMjEEOV2pi8O9yOI7GZvYDIAHhY8Z8AcDVgbXb8x0BbkjkwNiUiQ==", - "license": "MIT", - "dependencies": { - "@temporalio/client": "1.14.1", - "@temporalio/common": "1.14.1", - "@temporalio/proto": "1.14.1", - "long": "^5.2.3", - "nexus-rpc": "^0.0.1" - }, - "engines": { - "node": ">= 18.0.0" - } - }, - "node_modules/@temporalio/proto": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@temporalio/proto/-/proto-1.14.1.tgz", - "integrity": "sha512-mCsUommDPXbXbBu60p1g4jpSqVb+GNR67yR0uKTU8ARb4qVZQo7SQnOUaneoxDERDXuR/yIjVCektMm+7Myb+A==", - "license": "MIT", - "dependencies": { - "long": "^5.2.3", - "protobufjs": "^7.2.5" - }, - "engines": { - "node": ">= 18.0.0" - } - }, - "node_modules/@temporalio/worker": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@temporalio/worker/-/worker-1.14.1.tgz", - "integrity": "sha512-wFfN5gc03eq1bYAuJNsG9a1iWBG6hL9zAfYbxiJdshPhpHa82BtHGvXD447oT2BX3zqI+Jf2b0m/N0wgkW6wyQ==", - "license": "MIT", - "dependencies": { - "@grpc/grpc-js": "^1.12.4", - "@swc/core": "^1.3.102", - "@temporalio/activity": "1.14.1", - "@temporalio/client": "1.14.1", - "@temporalio/common": "1.14.1", - "@temporalio/core-bridge": "1.14.1", - "@temporalio/nexus": "1.14.1", - "@temporalio/proto": "1.14.1", - "@temporalio/workflow": "1.14.1", - "abort-controller": "^3.0.0", - "heap-js": "^2.6.0", - "memfs": "^4.6.0", - "nexus-rpc": "^0.0.1", - "proto3-json-serializer": "^2.0.0", - "protobufjs": "^7.2.5", - "rxjs": "^7.8.1", - "source-map": "^0.7.4", - "source-map-loader": "^4.0.2", - "supports-color": "^8.1.1", - "swc-loader": "^0.2.3", - "unionfs": "^4.5.1", - "webpack": "^5.94.0" - }, - "engines": { - "node": ">= 18.0.0" - } - }, - "node_modules/@temporalio/workflow": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@temporalio/workflow/-/workflow-1.14.1.tgz", - "integrity": "sha512-MzshcoRo8zjQYa9WHrv3XC8LVvpRNSVaW3kOSTmHuTYDh/7be48WODOgs5yUpbnkpsw6rjVCDCgtB/K02cQwDg==", - "license": "MIT", - "dependencies": { - "@temporalio/common": "1.14.1", - "@temporalio/proto": "1.14.1", - "nexus-rpc": "^0.0.1" - }, - "engines": { - "node": ">= 18.0.0" - } - }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "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/js-yaml": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", - "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", - "dev": true, - "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": "25.0.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz", - "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==", - "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "license": "Apache-2.0" - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-phases": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", - "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", - "license": "MIT", - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "acorn": "^8.14.0" - } - }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "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/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.9.14", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.14.tgz", - "integrity": "sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==", - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001764", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz", - "integrity": "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.267", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", - "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/enhanced-resolve": { - "version": "5.18.4", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", - "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", - "license": "MIT" - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "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/fs-monkey": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", - "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==", - "license": "Unlicense" - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/glob-to-regex.js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz", - "integrity": "sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "license": "BSD-2-Clause" - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/heap-js": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/heap-js/-/heap-js-2.7.1.tgz", - "integrity": "sha512-EQfezRg0NCZGNlhlDR3Evrw1FVL2G3LhU7EgPoxufQKruNBSYA8MiRPHeWbU+36o+Fhel0wMwM+sLEiBAlNLJA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/hyperdyperid": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", - "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", - "license": "MIT", - "engines": { - "node": ">=10.18" - } - }, - "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/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "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/loader-runner": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", - "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", - "license": "MIT", - "engines": { - "node": ">=6.11.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "license": "MIT" - }, - "node_modules/long": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "license": "Apache-2.0" - }, - "node_modules/memfs": { - "version": "4.51.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.51.1.tgz", - "integrity": "sha512-Eyt3XrufitN2ZL9c/uIRMyDwXanLI88h/L3MoWqNY747ha3dMR9dWqp8cRT5ntjZ0U1TNuq4U91ZXK0sMBjYOQ==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/json-pack": "^1.11.0", - "@jsonjoy.com/util": "^1.9.0", - "glob-to-regex.js": "^1.0.1", - "thingies": "^2.5.0", - "tree-dump": "^1.0.3", - "tslib": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "license": "MIT" - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "3.0.0-canary.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-3.0.0-canary.1.tgz", - "integrity": "sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==", - "license": "MIT", - "engines": { - "node": ">=12.13" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "license": "MIT" - }, - "node_modules/nexus-rpc": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/nexus-rpc/-/nexus-rpc-0.0.1.tgz", - "integrity": "sha512-hAWn8Hh2eewpB5McXR5EW81R3pR/ziuGhKCF3wFyUVCklanPqrIgMNr7jKCbzXeNVad0nUDfWpFRqh2u+zxQtw==", - "license": "MIT", - "engines": { - "node": ">= 18.0.0" - } - }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/proto3-json-serializer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", - "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", - "license": "Apache-2.0", - "dependencies": { - "protobufjs": "^7.2.5" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/protobufjs": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", - "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "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/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "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/schema-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", - "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "license": "MIT" - }, - "node_modules/smol-toml": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.0.tgz", - "integrity": "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 18" - }, - "funding": { - "url": "https://github.com/sponsors/cyyynthia" - } - }, - "node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-loader": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.2.tgz", - "integrity": "sha512-oYwAqCuL0OZhBoSgmdrLa7mv9MjommVMiQIWgcztf+eS4+8BfcUee6nenFnDhKOhzAVnk5gpZdfnz1iiBv+5sg==", - "license": "MIT", - "dependencies": { - "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.72.1" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/swc-loader": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.2.6.tgz", - "integrity": "sha512-9Zi9UP2YmDpgmQVbyOPJClY0dwf58JDyDMQ7uRc4krmc72twNI2fvlBWHLqVekBpPc7h5NJkGVT1zNDxFrqhvg==", - "license": "MIT", - "dependencies": { - "@swc/counter": "^0.1.3" - }, - "peerDependencies": { - "@swc/core": "^1.2.147", - "webpack": ">=2" - } - }, - "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser": { - "version": "5.44.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz", - "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.15.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.16", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", - "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT" - }, - "node_modules/thingies": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.5.0.tgz", - "integrity": "sha512-s+2Bwztg6PhWUD7XMfeYm5qliDdSiZm7M7n8KjTkIsm3l/2lgVRc2/Gx/v+ZX8lT4FMA+i8aQvhcWylldc+ZNw==", - "license": "MIT", - "engines": { - "node": ">=10.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "^2" - } - }, - "node_modules/tree-dump": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz", - "integrity": "sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "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": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "license": "MIT" - }, - "node_modules/unionfs": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/unionfs/-/unionfs-4.6.0.tgz", - "integrity": "sha512-fJAy3gTHjFi5S3TP5EGdjs/OUMFFvI/ady3T8qVuZfkv8Qi8prV/Q8BuFEgODJslhZTT2z2qdD2lGdee9qjEnA==", - "dependencies": { - "fs-monkey": "^1.0.0" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/watchpack": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.0.tgz", - "integrity": "sha512-e6vZvY6xboSwLz2GD36c16+O/2Z6fKvIf4pOXptw2rY9MVwE/TXc6RGqxD3I3x0a28lwBY7DE+76uTPSsBrrCA==", - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack": { - "version": "5.104.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.104.1.tgz", - "integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==", - "license": "MIT", - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.8", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.15.0", - "acorn-import-phases": "^1.0.3", - "browserslist": "^4.28.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.4", - "es-module-lexer": "^2.0.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.3.1", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.3", - "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.16", - "watchpack": "^2.4.4", - "webpack-sources": "^3.3.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-sources": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", - "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zx": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/zx/-/zx-8.8.1.tgz", - "integrity": "sha512-qvsKBnvWHstHKCluKPlEgI/D3+mdiQyMoSSeFR8IX/aXzWIas5A297KxKgPJhuPXdrR6ma0Jzx43+GQ/8sqbrw==", - "license": "Apache-2.0", - "bin": { - "zx": "build/cli.js" - }, - "engines": { - "node": ">= 12.17.0" - } - } - } -} diff --git a/package.json b/package.json index c2f3e00..41c6bcd 100644 --- a/package.json +++ b/package.json @@ -3,35 +3,26 @@ "version": "0.0.0", "private": true, "type": "module", - "workspaces": [ - "cli", - "mcp-server" - ], + "packageManager": "pnpm@10.12.1", "scripts": { - "build": "tsc", - "build:cli": "npm run build -w cli", - "build:mcp": "npm run build -w mcp-server", - "build:all": "npm run build:mcp && npm run build && npm run build:cli", - "temporal:server": "docker compose -f docker/docker-compose.temporal.yml up temporal -d", - "temporal:server:stop": "docker compose -f docker/docker-compose.temporal.yml down", - "temporal:worker": "node dist/temporal/worker.js", - "temporal:start": "node dist/temporal/worker.js" - }, - "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.2.38", - "@temporalio/activity": "^1.11.0", - "@temporalio/client": "^1.11.0", - "@temporalio/worker": "^1.11.0", - "@temporalio/workflow": "^1.11.0", - "ajv": "^8.12.0", - "ajv-formats": "^2.1.1", - "dotenv": "^16.4.5", - "js-yaml": "^4.1.0", - "zx": "^8.0.0" + "build": "turbo run build", + "check": "turbo run check", + "biome": "biome check .", + "biome:fix": "biome check --write .", + "clean": "turbo run clean", + "temporal:worker": "node apps/worker/dist/temporal/worker.js", + "temporal:start": "node apps/worker/dist/temporal/worker.js" }, "devDependencies": { - "@types/js-yaml": "^4.0.9", + "@biomejs/biome": "^2.0.0", "@types/node": "^25.0.3", + "turbo": "^2.5.0", "typescript": "^5.9.3" + }, + "pnpm": { + "onlyBuiltDependencies": [ + "@swc/core", + "protobufjs" + ] } } diff --git a/mcp-server/package.json b/packages/mcp-server/package.json similarity index 54% rename from mcp-server/package.json rename to packages/mcp-server/package.json index eb832dd..8c698e9 100644 --- a/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -3,12 +3,19 @@ "version": "1.0.0", "type": "module", "main": "./dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + } + }, "scripts": { "build": "tsc", + "check": "tsc --noEmit", "clean": "rm -rf dist" }, "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.2.38", + "@anthropic-ai/claude-agent-sdk": "catalog:", "zod": "^4.3.6" } } diff --git a/mcp-server/src/index.ts b/packages/mcp-server/src/index.ts similarity index 100% rename from mcp-server/src/index.ts rename to packages/mcp-server/src/index.ts index 0844e96..635f6c0 100644 --- a/mcp-server/src/index.ts +++ b/packages/mcp-server/src/index.ts @@ -17,8 +17,8 @@ */ import { createSdkMcpServer } from '@anthropic-ai/claude-agent-sdk'; -import { createSaveDeliverableTool } from './tools/save-deliverable.js'; import { generateTotpTool } from './tools/generate-totp.js'; +import { createSaveDeliverableTool } from './tools/save-deliverable.js'; /** * Create Shannon Helper MCP Server with target directory context @@ -38,9 +38,9 @@ export function createShannonHelperServer(targetDir: string): ReturnType; @@ -49,43 +58,41 @@ function isPathContained(basePath: string, targetPath: string): boolean { * Resolve deliverable content from either inline content or a file path. * Returns the content string on success, or a ToolResult error on failure. */ -function resolveContent( - args: SaveDeliverableInput, - targetDir: string, -): string | ToolResult { +function resolveContent(args: SaveDeliverableInput, targetDir: string): string | ToolResult { if (args.content) { return args.content; } if (!args.file_path) { - return createToolResult(createValidationError( - 'Either "content" or "file_path" must be provided', - true, - { deliverableType: args.deliverable_type }, - )); + return createToolResult( + createValidationError('Either "content" or "file_path" must be provided', true, { + deliverableType: args.deliverable_type, + }), + ); } - const resolvedPath = path.isAbsolute(args.file_path) - ? args.file_path - : path.resolve(targetDir, args.file_path); + const resolvedPath = path.isAbsolute(args.file_path) ? args.file_path : path.resolve(targetDir, args.file_path); // Security: Prevent path traversal outside targetDir if (!isPathContained(targetDir, resolvedPath)) { - return createToolResult(createValidationError( - `Path "${args.file_path}" resolves outside allowed directory`, - false, - { deliverableType: args.deliverable_type, allowedBase: targetDir }, - )); + return createToolResult( + createValidationError(`Path "${args.file_path}" resolves outside allowed directory`, false, { + deliverableType: args.deliverable_type, + allowedBase: targetDir, + }), + ); } try { return fs.readFileSync(resolvedPath, 'utf-8'); } catch (readError) { - return createToolResult(createValidationError( - `Failed to read file at ${resolvedPath}: ${readError instanceof Error ? readError.message : String(readError)}`, - true, - { deliverableType: args.deliverable_type, filePath: resolvedPath }, - )); + return createToolResult( + createValidationError( + `Failed to read file at ${resolvedPath}: ${readError instanceof Error ? readError.message : String(readError)}`, + true, + { deliverableType: args.deliverable_type, filePath: resolvedPath }, + ), + ); } } @@ -109,11 +116,12 @@ function createSaveDeliverableHandler(targetDir: string) { if (isQueueType(deliverable_type)) { const queueValidation = validateQueueJson(content); if (!queueValidation.valid) { - return createToolResult(createValidationError( - queueValidation.message ?? 'Invalid queue JSON', - true, - { deliverableType: deliverable_type, expectedFormat: '{"vulnerabilities": [...]}' }, - )); + return createToolResult( + createValidationError(queueValidation.message ?? 'Invalid queue JSON', true, { + deliverableType: deliverable_type, + expectedFormat: '{"vulnerabilities": [...]}', + }), + ); } } @@ -130,11 +138,7 @@ function createSaveDeliverableHandler(targetDir: string) { return createToolResult(successResponse); } catch (error) { - return createToolResult(createGenericError( - error, - false, - { deliverableType: args.deliverable_type }, - )); + return createToolResult(createGenericError(error, false, { deliverableType: args.deliverable_type })); } }; } @@ -150,6 +154,6 @@ export function createSaveDeliverableTool(targetDir: string) { 'save_deliverable', 'Saves deliverable files with automatic validation. Queue files must have {"vulnerabilities": [...]} structure. For large reports, write the file to disk first then pass file_path instead of inline content to avoid output token limits.', SaveDeliverableInputSchema.shape, - createSaveDeliverableHandler(targetDir) + createSaveDeliverableHandler(targetDir), ); } diff --git a/mcp-server/src/types/deliverables.ts b/packages/mcp-server/src/types/deliverables.ts similarity index 100% rename from mcp-server/src/types/deliverables.ts rename to packages/mcp-server/src/types/deliverables.ts diff --git a/mcp-server/src/types/index.ts b/packages/mcp-server/src/types/index.ts similarity index 100% rename from mcp-server/src/types/index.ts rename to packages/mcp-server/src/types/index.ts diff --git a/mcp-server/src/types/tool-responses.ts b/packages/mcp-server/src/types/tool-responses.ts similarity index 92% rename from mcp-server/src/types/tool-responses.ts rename to packages/mcp-server/src/types/tool-responses.ts index 960d18f..80273ac 100644 --- a/mcp-server/src/types/tool-responses.ts +++ b/packages/mcp-server/src/types/tool-responses.ts @@ -40,11 +40,7 @@ export interface GenerateTotpResponse { expiresIn: number; // seconds until expiration } -export type ToolResponse = - | ErrorResponse - | SuccessResponse - | SaveDeliverableResponse - | GenerateTotpResponse; +export type ToolResponse = ErrorResponse | SuccessResponse | SaveDeliverableResponse | GenerateTotpResponse; export interface ToolResultContent { type: string; diff --git a/mcp-server/src/utils/error-formatter.ts b/packages/mcp-server/src/utils/error-formatter.ts similarity index 92% rename from mcp-server/src/utils/error-formatter.ts rename to packages/mcp-server/src/utils/error-formatter.ts index 5669abe..7b7b554 100644 --- a/mcp-server/src/utils/error-formatter.ts +++ b/packages/mcp-server/src/utils/error-formatter.ts @@ -18,7 +18,7 @@ import type { ErrorResponse } from '../types/tool-responses.js'; export function createValidationError( message: string, retryable: boolean = true, - context?: Record + context?: Record, ): ErrorResponse { return { status: 'error', @@ -35,7 +35,7 @@ export function createValidationError( export function createCryptoError( message: string, retryable: boolean = false, - context?: Record + context?: Record, ): ErrorResponse { return { status: 'error', @@ -52,7 +52,7 @@ export function createCryptoError( export function createGenericError( error: unknown, retryable: boolean = false, - context?: Record + context?: Record, ): ErrorResponse { const message = error instanceof Error ? error.message : String(error); const errorType = error instanceof Error ? error.constructor.name : 'UnknownError'; diff --git a/mcp-server/src/utils/file-operations.ts b/packages/mcp-server/src/utils/file-operations.ts similarity index 93% rename from mcp-server/src/utils/file-operations.ts rename to packages/mcp-server/src/utils/file-operations.ts index 150c38d..e468007 100644 --- a/mcp-server/src/utils/file-operations.ts +++ b/packages/mcp-server/src/utils/file-operations.ts @@ -11,8 +11,8 @@ * Ported from tools/save_deliverable.js (lines 117-130). */ -import { writeFileSync, mkdirSync } from 'fs'; -import { join } from 'path'; +import { mkdirSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; /** * Save deliverable file to deliverables/ directory diff --git a/mcp-server/src/validation/queue-validator.ts b/packages/mcp-server/src/validation/queue-validator.ts similarity index 100% rename from mcp-server/src/validation/queue-validator.ts rename to packages/mcp-server/src/validation/queue-validator.ts diff --git a/mcp-server/src/validation/totp-validator.ts b/packages/mcp-server/src/validation/totp-validator.ts similarity index 100% rename from mcp-server/src/validation/totp-validator.ts rename to packages/mcp-server/src/validation/totp-validator.ts diff --git a/mcp-server/tsconfig.json b/packages/mcp-server/tsconfig.json similarity index 77% rename from mcp-server/tsconfig.json rename to packages/mcp-server/tsconfig.json index 68dd138..452b099 100644 --- a/mcp-server/tsconfig.json +++ b/packages/mcp-server/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "rootDir": "./src", "outDir": "./dist" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..4a1fd8c --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2515 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +catalogs: + default: + '@anthropic-ai/claude-agent-sdk': + specifier: ^0.2.38 + version: 0.2.76 + +importers: + + .: + devDependencies: + '@biomejs/biome': + specifier: ^2.0.0 + version: 2.4.7 + '@types/node': + specifier: ^25.0.3 + version: 25.5.0 + turbo: + specifier: ^2.5.0 + version: 2.8.17 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + + apps/cli: + dependencies: + '@clack/prompts': + specifier: ^1.1.0 + version: 1.1.0 + chokidar: + specifier: ^5.0.0 + version: 5.0.0 + dotenv: + specifier: ^17.3.1 + version: 17.3.1 + smol-toml: + specifier: ^1.6.0 + version: 1.6.0 + devDependencies: + tsdown: + specifier: ^0.21.2 + version: 0.21.2(typescript@5.9.3) + + apps/worker: + dependencies: + '@anthropic-ai/claude-agent-sdk': + specifier: 'catalog:' + version: 0.2.76(zod@4.3.6) + '@shannon/mcp-server': + specifier: workspace:* + version: link:../../packages/mcp-server + '@temporalio/activity': + specifier: ^1.11.0 + version: 1.15.0 + '@temporalio/client': + specifier: ^1.11.0 + version: 1.15.0 + '@temporalio/worker': + specifier: ^1.11.0 + version: 1.15.0(tslib@2.8.1) + '@temporalio/workflow': + specifier: ^1.11.0 + version: 1.15.0 + ajv: + specifier: ^8.12.0 + version: 8.18.0 + ajv-formats: + specifier: ^2.1.1 + version: 2.1.1(ajv@8.18.0) + dotenv: + specifier: ^16.4.5 + version: 16.6.1 + js-yaml: + specifier: ^4.1.0 + version: 4.1.1 + zx: + specifier: ^8.0.0 + version: 8.8.5 + devDependencies: + '@types/js-yaml': + specifier: ^4.0.9 + version: 4.0.9 + + packages/mcp-server: + dependencies: + '@anthropic-ai/claude-agent-sdk': + specifier: 'catalog:' + version: 0.2.76(zod@4.3.6) + zod: + specifier: ^4.3.6 + version: 4.3.6 + +packages: + + '@anthropic-ai/claude-agent-sdk@0.2.76': + resolution: {integrity: sha512-HZxvnT8ZWkzCnQygaYCA0dl8RSUzuVbxE1YG4ecy6vh4nQbTT36CxUxBy+QVdR12pPQluncC0mCOLhI2918Eaw==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^4.0.0 + + '@babel/generator@8.0.0-rc.2': + resolution: {integrity: sha512-oCQ1IKPwkzCeJzAPb7Fv8rQ9k5+1sG8mf2uoHiMInPYvkRfrDJxbTIbH51U+jstlkghus0vAi3EBvkfvEsYNLQ==} + engines: {node: ^20.19.0 || >=22.12.0} + + '@babel/helper-string-parser@8.0.0-rc.2': + resolution: {integrity: sha512-noLx87RwlBEMrTzncWd/FvTxoJ9+ycHNg0n8yyYydIoDsLZuxknKgWRJUqcrVkNrJ74uGyhWQzQaS3q8xfGAhQ==} + engines: {node: ^20.19.0 || >=22.12.0} + + '@babel/helper-validator-identifier@8.0.0-rc.2': + resolution: {integrity: sha512-xExUBkuXWJjVuIbO7z6q7/BA9bgfJDEhVL0ggrggLMbg0IzCUWGT1hZGE8qUH7Il7/RD/a6cZ3AAFrrlp1LF/A==} + engines: {node: ^20.19.0 || >=22.12.0} + + '@babel/parser@8.0.0-rc.2': + resolution: {integrity: sha512-29AhEtcq4x8Dp3T72qvUMZHx0OMXCj4Jy/TEReQa+KWLln524Cj1fWb3QFi0l/xSpptQBR6y9RNEXuxpFvwiUQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + '@babel/types@8.0.0-rc.2': + resolution: {integrity: sha512-91gAaWRznDwSX4E2tZ1YjBuIfnQVOFDCQ2r0Toby0gu4XEbyF623kXLMA8d4ZbCu+fINcrudkmEcwSUHgDDkNw==} + engines: {node: ^20.19.0 || >=22.12.0} + + '@biomejs/biome@2.4.7': + resolution: {integrity: sha512-vXrgcmNGZ4lpdwZSpMf1hWw1aWS6B+SyeSYKTLrNsiUsAdSRN0J4d/7mF3ogJFbIwFFSOL3wT92Zzxia/d5/ng==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.4.7': + resolution: {integrity: sha512-Oo0cF5mHzmvDmTXw8XSjhCia8K6YrZnk7aCS54+/HxyMdZMruMO3nfpDsrlar/EQWe41r1qrwKiCa2QDYHDzWA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.4.7': + resolution: {integrity: sha512-I+cOG3sd/7HdFtvDSnF9QQPrWguUH7zrkIMMykM3PtfWU9soTcS2yRb9Myq6MHmzbeCT08D1UmY+BaiMl5CcoQ==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.4.7': + resolution: {integrity: sha512-I2NvM9KPb09jWml93O2/5WMfNR7Lee5Latag1JThDRMURVhPX74p9UDnyTw3Ae6cE1DgXfw7sqQgX7rkvpc0vw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@2.4.7': + resolution: {integrity: sha512-om6FugwmibzfP/6ALj5WRDVSND4H2G9X0nkI1HZpp2ySf9lW2j0X68oQSaHEnls6666oy4KDsc5RFjT4m0kV0w==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@2.4.7': + resolution: {integrity: sha512-00kx4YrBMU8374zd2wHuRV5wseh0rom5HqRND+vDldJPrWwQw+mzd/d8byI9hPx926CG+vWzq6AeiT7Yi5y59g==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@2.4.7': + resolution: {integrity: sha512-bV8/uo2Tj+gumnk4sUdkerWyCPRabaZdv88IpbmDWARQQoA/Q0YaqPz1a+LSEDIL7OfrnPi9Hq1Llz4ZIGyIQQ==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@2.4.7': + resolution: {integrity: sha512-hOUHBMlFCvDhu3WCq6vaBoG0dp0LkWxSEnEEsxxXvOa9TfT6ZBnbh72A/xBM7CBYB7WgwqboetzFEVDnMxelyw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.4.7': + resolution: {integrity: sha512-qEpGjSkPC3qX4ycbMUthXvi9CkRq7kZpkqMY1OyhmYlYLnANnooDQ7hDerM8+0NJ+DZKVnsIc07h30XOpt7LtQ==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@clack/core@1.1.0': + resolution: {integrity: sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA==} + + '@clack/prompts@1.1.0': + resolution: {integrity: sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g==} + + '@emnapi/core@1.9.0': + resolution: {integrity: sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==} + + '@emnapi/runtime@1.9.0': + resolution: {integrity: sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==} + + '@emnapi/wasi-threads@1.2.0': + resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==} + + '@grpc/grpc-js@1.14.3': + resolution: {integrity: sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==} + engines: {node: '>=12.10.0'} + + '@grpc/proto-loader@0.8.0': + resolution: {integrity: sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==} + engines: {node: '>=6'} + hasBin: true + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + + '@jsonjoy.com/base64@1.1.2': + resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/base64@17.67.0': + resolution: {integrity: sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/buffers@1.2.1': + resolution: {integrity: sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/buffers@17.67.0': + resolution: {integrity: sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/codegen@1.0.0': + resolution: {integrity: sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/codegen@17.67.0': + resolution: {integrity: sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-core@4.56.11': + resolution: {integrity: sha512-wThHjzUp01ImIjfCwhs+UnFkeGPFAymwLEkOtenHewaKe2pTP12p6r1UuwikA9NEvNf9Vlck92r8fb8n/MWM5w==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-fsa@4.56.11': + resolution: {integrity: sha512-ZYlF3XbMayyp97xEN8ZvYutU99PCHjM64mMZvnCseXkCJXJDVLAwlF8Q/7q/xiWQRsv3pQBj1WXHd9eEyYcaCQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-builtins@4.56.11': + resolution: {integrity: sha512-CNmt3a0zMCIhniFLXtzPWuUxXFU+U+2VyQiIrgt/rRVeEJNrMQUABaRbVxR0Ouw1LyR9RjaEkPM6nYpED+y43A==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-to-fsa@4.56.11': + resolution: {integrity: sha512-5OzGdvJDgZVo+xXWEYo72u81zpOWlxlbG4d4nL+hSiW+LKlua/dldNgPrpWxtvhgyntmdFQad2UTxFyGjJAGhA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-utils@4.56.11': + resolution: {integrity: sha512-JADOZFDA3wRfsuxkT0+MYc4F9hJO2PYDaY66kRTG6NqGX3+bqmKu66YFYAbII/tEmQWPZeHoClUB23rtQM9UPg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node@4.56.11': + resolution: {integrity: sha512-D65YrnP6wRuZyEWoSFnBJSr5zARVpVBGctnhie4rCsMuGXNzX7IHKaOt85/Aj7SSoG1N2+/xlNjWmkLvZ2H3Tg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-print@4.56.11': + resolution: {integrity: sha512-rnaKRgCRIn8JGTjxhS0JPE38YM3Pj/H7SW4/tglhIPbfKEkky7dpPayNKV2qy25SZSL15oFVgH/62dMZ/z7cyA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-snapshot@4.56.11': + resolution: {integrity: sha512-IIldPX+cIRQuUol9fQzSS3hqyECxVpYMJQMqdU3dCKZFRzEl1rkIkw4P6y7Oh493sI7YdxZlKr/yWdzEWZ1wGQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@1.21.0': + resolution: {integrity: sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@17.67.0': + resolution: {integrity: sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pointer@1.0.2': + resolution: {integrity: sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pointer@17.67.0': + resolution: {integrity: sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@1.9.0': + resolution: {integrity: sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@17.67.0': + resolution: {integrity: sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@napi-rs/wasm-runtime@1.1.1': + resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} + + '@oxc-project/types@0.115.0': + resolution: {integrity: sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@quansync/fs@1.0.0': + resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} + + '@rolldown/binding-android-arm64@1.0.0-rc.9': + resolution: {integrity: sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.0-rc.9': + resolution: {integrity: sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-rc.9': + resolution: {integrity: sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0-rc.9': + resolution: {integrity: sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.9': + resolution: {integrity: sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.9': + resolution: {integrity: sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.9': + resolution: {integrity: sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.9': + resolution: {integrity: sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.9': + resolution: {integrity: sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.9': + resolution: {integrity: sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.9': + resolution: {integrity: sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.9': + resolution: {integrity: sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.9': + resolution: {integrity: sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.9': + resolution: {integrity: sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.9': + resolution: {integrity: sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.0-rc.9': + resolution: {integrity: sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==} + + '@swc/core-darwin-arm64@1.15.18': + resolution: {integrity: sha512-+mIv7uBuSaywN3C9LNuWaX1jJJ3SKfiJuE6Lr3bd+/1Iv8oMU7oLBjYMluX1UrEPzwN2qCdY6Io0yVicABoCwQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-x64@1.15.18': + resolution: {integrity: sha512-wZle0eaQhnzxWX5V/2kEOI6Z9vl/lTFEC6V4EWcn+5pDjhemCpQv9e/TDJ0GIoiClX8EDWRvuZwh+Z3dhL1NAg==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-linux-arm-gnueabihf@1.15.18': + resolution: {integrity: sha512-ao61HGXVqrJFHAcPtF4/DegmwEkVCo4HApnotLU8ognfmU8x589z7+tcf3hU+qBiU1WOXV5fQX6W9Nzs6hjxDw==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.15.18': + resolution: {integrity: sha512-3xnctOBLIq3kj8PxOCgPrGjBLP/kNOddr6f5gukYt/1IZxsITQaU9TDyjeX6jG+FiCIHjCuWuffsyQDL5Ew1bg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-arm64-musl@1.15.18': + resolution: {integrity: sha512-0a+Lix+FSSHBSBOA0XznCcHo5/1nA6oLLjcnocvzXeqtdjnPb+SvchItHI+lfeiuj1sClYPDvPMLSLyXFaiIKw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-x64-gnu@1.15.18': + resolution: {integrity: sha512-wG9J8vReUlpaHz4KOD/5UE1AUgirimU4UFT9oZmupUDEofxJKYb1mTA/DrMj0s78bkBiNI+7Fo2EgPuvOJfuAA==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-linux-x64-musl@1.15.18': + resolution: {integrity: sha512-4nwbVvCphKzicwNWRmvD5iBaZj8JYsRGa4xOxJmOyHlMDpsvvJ2OR2cODlvWyGFH6BYL1MfIAK3qph3hp0Az6g==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-win32-arm64-msvc@1.15.18': + resolution: {integrity: sha512-zk0RYO+LjiBCat2RTMHzAWaMky0cra9loH4oRrLKLLNuL+jarxKLFDA8xTZWEkCPLjUTwlRN7d28eDLLMgtUcQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.15.18': + resolution: {integrity: sha512-yVuTrZ0RccD5+PEkpcLOBAuPbYBXS6rslENvIXfvJGXSdX5QGi1ehC4BjAMl5FkKLiam4kJECUI0l7Hq7T1vwg==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.15.18': + resolution: {integrity: sha512-7NRmE4hmUQNCbYU3Hn9Tz57mK9Qq4c97ZS+YlamlK6qG9Fb5g/BB3gPDe0iLlJkns/sYv2VWSkm8c3NmbEGjbg==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.15.18': + resolution: {integrity: sha512-z87aF9GphWp//fnkRsqvtY+inMVPgYW3zSlXH1kJFvRT5H/wiAn+G32qW5l3oEk63KSF1x3Ov0BfHCObAmT8RA==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '>=0.5.17' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/types@0.1.25': + resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} + + '@temporalio/activity@1.15.0': + resolution: {integrity: sha512-kKEIrHMTsANiEpDVZd+v3OT6kU20UtcvAK7iibJNzQnoZUGC7XnqdKQlBuGYBxgpJzD9ppGgskXRczW+XU5Kng==} + engines: {node: '>= 20.0.0'} + + '@temporalio/client@1.15.0': + resolution: {integrity: sha512-SxTGqRIa2+Vy4P9+06ZpUf4u7ZZmOXfx/kr9XvNqAApLxTMKjTQIg5OH5Wt4JLUtIR7dFkuHIyhewdRyG+hSsQ==} + engines: {node: '>= 20.0.0'} + + '@temporalio/common@1.15.0': + resolution: {integrity: sha512-tBfC3fdOExsNoS5krkMUXnaMtCRKj05Jts4+TH+cgHpbys68nslFvUQLqwPIw2x6155Divb9MF219a/75itbTg==} + engines: {node: '>= 20.0.0'} + + '@temporalio/core-bridge@1.15.0': + resolution: {integrity: sha512-Qdrs5zju5MiOwmERCWzQ6uHZwn1JaQk/ppS2UHbxqZndjbAEFPDU9KQqFLkxWAicHMy7+LGPnA4DpVOANGlTZA==} + engines: {node: '>= 20.0.0'} + + '@temporalio/nexus@1.15.0': + resolution: {integrity: sha512-E6CdIjskkbK2aObxcb76Z4V3o1D3QDxEtsxmuHX5D7HEABuYGdV+oeOzDyxMlfeY9GyIM9Nvky4XCiSz2h2XRA==} + engines: {node: '>= 20.0.0'} + + '@temporalio/proto@1.15.0': + resolution: {integrity: sha512-Awy4Fjzyba7Pg/CVZjjQ3x2CWkDL1qELyTZWcLlyjXq8bX694JVfBsmiMmF6tHn5/ySOIxDTcc0MSScZ0oKX5A==} + engines: {node: '>= 20.0.0'} + + '@temporalio/worker@1.15.0': + resolution: {integrity: sha512-9e0AWP2OxYFgeztMdkoWYbDVqmNubreRkG7/frVKFT3xHdIOrFQ2W6Yomv61q3oKMXTIrpvjClHtiTjAUr70uA==} + engines: {node: '>= 20.0.0'} + + '@temporalio/workflow@1.15.0': + resolution: {integrity: sha512-VaMhVtlA0hLLM/pna26vFSdn5W1Arq2+ccgItdbdRdZUa0X8eW2B7sl/PcUYQOWc4aMUGnvPATKUGUoFKyCxSg==} + engines: {node: '>= 20.0.0'} + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/js-yaml@4.0.9': + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + + '@types/jsesc@2.5.1': + resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@25.5.0': + resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==} + + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansis@4.2.0: + resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} + engines: {node: '>=14'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + ast-kit@3.0.0-beta.1: + resolution: {integrity: sha512-trmleAnZ2PxN/loHWVhhx1qeOHSRXq4TDsBBxq3GqeJitfk3+jTQ+v/C1km/KYq9M7wKqCewMh+/NAvVH7m+bw==} + engines: {node: '>=20.19.0'} + + baseline-browser-mapping@2.10.8: + resolution: {integrity: sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + birpc@4.0.0: + resolution: {integrity: sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==} + + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} + + caniuse-lite@1.0.30001778: + resolution: {integrity: sha512-PN7uxFL+ExFJO61aVmP1aIEG4i9whQd4eoSCebav62UwDyp5OHh06zN4jqKSMePVgxHifCw1QJxdRkA1Pisekg==} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + dotenv@17.3.1: + resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==} + engines: {node: '>=12'} + + dts-resolver@2.1.3: + resolution: {integrity: sha512-bihc7jPC90VrosXNzK0LTE2cuLP6jr0Ro8jk+kMugHReJVLIpHz/xadeq3MhuwyO4TD4OA3L1Q8pBBFRc08Tsw==} + engines: {node: '>=20.19.0'} + peerDependencies: + oxc-resolver: '>=11.0.0' + peerDependenciesMeta: + oxc-resolver: + optional: true + + electron-to-chromium@1.5.313: + resolution: {integrity: sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + empathic@2.0.0: + resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} + engines: {node: '>=14'} + + enhanced-resolve@5.20.0: + resolution: {integrity: sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==} + engines: {node: '>=10.13.0'} + + es-module-lexer@2.0.0: + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fs-monkey@1.1.0: + resolution: {integrity: sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-tsconfig@4.13.6: + resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} + + glob-to-regex.js@1.2.0: + resolution: {integrity: sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + heap-js@2.7.1: + resolution: {integrity: sha512-EQfezRg0NCZGNlhlDR3Evrw1FVL2G3LhU7EgPoxufQKruNBSYA8MiRPHeWbU+36o+Fhel0wMwM+sLEiBAlNLJA==} + engines: {node: '>=10.0.0'} + + hookable@6.0.1: + resolution: {integrity: sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw==} + + hyperdyperid@1.2.0: + resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} + engines: {node: '>=10.18'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + import-without-cache@0.2.5: + resolution: {integrity: sha512-B6Lc2s6yApwnD2/pMzFh/d5AVjdsDXjgkeJ766FmFuJELIGHNycKRj+l3A39yZPM4CchqNCB4RITEAYB1KUM6A==} + engines: {node: '>=20.19.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + loader-runner@4.3.1: + resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} + engines: {node: '>=6.11.5'} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + memfs@4.56.11: + resolution: {integrity: sha512-/GodtwVeKVIHZKLUSr2ZdOxKBC5hHki4JNCU22DoCGPEHr5o2PD5U721zvESKyWwCfTfavFl9WZYgA13OAYK0g==} + peerDependencies: + tslib: '2' + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + ms@3.0.0-canary.1: + resolution: {integrity: sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==} + engines: {node: '>=12.13'} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + nexus-rpc@0.0.1: + resolution: {integrity: sha512-hAWn8Hh2eewpB5McXR5EW81R3pR/ziuGhKCF3wFyUVCklanPqrIgMNr7jKCbzXeNVad0nUDfWpFRqh2u+zxQtw==} + engines: {node: '>= 18.0.0'} + + node-releases@2.0.36: + resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} + + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + proto3-json-serializer@2.0.2: + resolution: {integrity: sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==} + engines: {node: '>=14.0.0'} + + protobufjs@7.5.4: + resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} + engines: {node: '>=12.0.0'} + + quansync@1.0.0: + resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} + + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + rolldown-plugin-dts@0.22.5: + resolution: {integrity: sha512-M/HXfM4cboo+jONx9Z0X+CUf3B5tCi7ni+kR5fUW50Fp9AlZk0oVLesibGWgCXDKFp5lpgQ9yhKoImUFjl3VZw==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@ts-macro/tsc': ^0.3.6 + '@typescript/native-preview': '>=7.0.0-dev.20250601.1' + rolldown: ^1.0.0-rc.3 + typescript: ^5.0.0 || ^6.0.0-beta + vue-tsc: ~3.2.0 + peerDependenciesMeta: + '@ts-macro/tsc': + optional: true + '@typescript/native-preview': + optional: true + typescript: + optional: true + vue-tsc: + optional: true + + rolldown@1.0.0-rc.9: + resolution: {integrity: sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + schema-utils@4.3.3: + resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} + engines: {node: '>= 10.13.0'} + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + smol-toml@1.6.0: + resolution: {integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==} + engines: {node: '>= 18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-loader@4.0.2: + resolution: {integrity: sha512-oYwAqCuL0OZhBoSgmdrLa7mv9MjommVMiQIWgcztf+eS4+8BfcUee6nenFnDhKOhzAVnk5gpZdfnz1iiBv+5sg==} + engines: {node: '>= 14.15.0'} + peerDependencies: + webpack: ^5.72.1 + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + swc-loader@0.2.7: + resolution: {integrity: sha512-nwYWw3Fh9ame3Rtm7StS9SBLpHRRnYcK7bnpF3UKZmesAK0gw2/ADvlURFAINmPvKtDLzp+GBiP9yLoEjg6S9w==} + peerDependencies: + '@swc/core': ^1.2.147 + webpack: '>=2' + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + terser-webpack-plugin@5.4.0: + resolution: {integrity: sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + + terser@5.46.0: + resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==} + engines: {node: '>=10'} + hasBin: true + + thingies@2.5.0: + resolution: {integrity: sha512-s+2Bwztg6PhWUD7XMfeYm5qliDdSiZm7M7n8KjTkIsm3l/2lgVRc2/Gx/v+ZX8lT4FMA+i8aQvhcWylldc+ZNw==} + engines: {node: '>=10.18'} + peerDependencies: + tslib: ^2 + + tinyexec@1.0.4: + resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} + engines: {node: '>=18'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tree-dump@1.1.0: + resolution: {integrity: sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + tsdown@0.21.2: + resolution: {integrity: sha512-pP8eAcd1XAWjl5gjosuJs0BAuVoheUe3V8VDHx31QK7YOgXjcCMsBSyFWO3CMh/CSUkjRUzR96JtGH3WJFTExQ==} + engines: {node: '>=20.19.0'} + hasBin: true + peerDependencies: + '@arethetypeswrong/core': ^0.18.1 + '@tsdown/css': 0.21.2 + '@tsdown/exe': 0.21.2 + '@vitejs/devtools': '*' + publint: ^0.3.0 + typescript: ^5.0.0 + unplugin-unused: ^0.5.0 + peerDependenciesMeta: + '@arethetypeswrong/core': + optional: true + '@tsdown/css': + optional: true + '@tsdown/exe': + optional: true + '@vitejs/devtools': + optional: true + publint: + optional: true + typescript: + optional: true + unplugin-unused: + optional: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + turbo-darwin-64@2.8.17: + resolution: {integrity: sha512-ZFkv2hv7zHpAPEXBF6ouRRXshllOavYc+jjcrYyVHvxVTTwJWsBZwJ/gpPzmOKGvkSjsEyDO5V6aqqtZzwVF+Q==} + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@2.8.17: + resolution: {integrity: sha512-5DXqhQUt24ycEryXDfMNKEkW5TBHs+QmU23a2qxXwwFDaJsWcPo2obEhBxxdEPOv7qmotjad+09RGeWCcJ9JDw==} + cpu: [arm64] + os: [darwin] + + turbo-linux-64@2.8.17: + resolution: {integrity: sha512-KLUbz6w7F73D/Ihh51hVagrKR0/CTsPEbRkvXLXvoND014XJ4BCrQUqSxlQ4/hu+nqp1v5WlM85/h3ldeyujuA==} + cpu: [x64] + os: [linux] + + turbo-linux-arm64@2.8.17: + resolution: {integrity: sha512-pJK67XcNJH40lTAjFu7s/rUlobgVXyB3A3lDoq+/JccB3hf+SysmkpR4Itlc93s8LEaFAI4mamhFuTV17Z6wOg==} + cpu: [arm64] + os: [linux] + + turbo-windows-64@2.8.17: + resolution: {integrity: sha512-EijeQ6zszDMmGZLP2vT2RXTs/GVi9rM0zv2/G4rNu2SSRSGFapgZdxgW4b5zUYLVaSkzmkpWlGfPfj76SW9yUg==} + cpu: [x64] + os: [win32] + + turbo-windows-arm64@2.8.17: + resolution: {integrity: sha512-crpfeMPkfECd4V1PQ/hMoiyVcOy04+bWedu/if89S15WhOalHZ2BYUi6DOJhZrszY+mTT99OwpOsj4wNfb/GHQ==} + cpu: [arm64] + os: [win32] + + turbo@2.8.17: + resolution: {integrity: sha512-YwPsNSqU2f/RXU/+Kcb7cPkPZARxom4+me7LKEdN5jsvy2tpfze3zDZ4EiGrJnvOm9Avu9rK0aaYsP7qZ3iz7A==} + hasBin: true + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + unconfig-core@7.5.0: + resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} + + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + + unionfs@4.6.0: + resolution: {integrity: sha512-fJAy3gTHjFi5S3TP5EGdjs/OUMFFvI/ady3T8qVuZfkv8Qi8prV/Q8BuFEgODJslhZTT2z2qdD2lGdee9qjEnA==} + + unrun@0.2.32: + resolution: {integrity: sha512-opd3z6791rf281JdByf0RdRQrpcc7WyzqittqIXodM/5meNWdTwrVxeyzbaCp4/Rgls/um14oUaif1gomO8YGg==} + engines: {node: '>=20.19.0'} + hasBin: true + peerDependencies: + synckit: ^0.11.11 + peerDependenciesMeta: + synckit: + optional: true + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uuid@11.1.0: + resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + hasBin: true + + watchpack@2.5.1: + resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} + engines: {node: '>=10.13.0'} + + webpack-sources@3.3.4: + resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==} + engines: {node: '>=10.13.0'} + + webpack@5.105.4: + resolution: {integrity: sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + + zx@8.8.5: + resolution: {integrity: sha512-SNgDF5L0gfN7FwVOdEFguY3orU5AkfFZm9B5YSHog/UDHv+lvmd82ZAsOenOkQixigwH2+yyH198AwNdKhj+RA==} + engines: {node: '>= 12.17.0'} + hasBin: true + +snapshots: + + '@anthropic-ai/claude-agent-sdk@0.2.76(zod@4.3.6)': + dependencies: + zod: 4.3.6 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + + '@babel/generator@8.0.0-rc.2': + dependencies: + '@babel/parser': 8.0.0-rc.2 + '@babel/types': 8.0.0-rc.2 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@types/jsesc': 2.5.1 + jsesc: 3.1.0 + + '@babel/helper-string-parser@8.0.0-rc.2': {} + + '@babel/helper-validator-identifier@8.0.0-rc.2': {} + + '@babel/parser@8.0.0-rc.2': + dependencies: + '@babel/types': 8.0.0-rc.2 + + '@babel/types@8.0.0-rc.2': + dependencies: + '@babel/helper-string-parser': 8.0.0-rc.2 + '@babel/helper-validator-identifier': 8.0.0-rc.2 + + '@biomejs/biome@2.4.7': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.4.7 + '@biomejs/cli-darwin-x64': 2.4.7 + '@biomejs/cli-linux-arm64': 2.4.7 + '@biomejs/cli-linux-arm64-musl': 2.4.7 + '@biomejs/cli-linux-x64': 2.4.7 + '@biomejs/cli-linux-x64-musl': 2.4.7 + '@biomejs/cli-win32-arm64': 2.4.7 + '@biomejs/cli-win32-x64': 2.4.7 + + '@biomejs/cli-darwin-arm64@2.4.7': + optional: true + + '@biomejs/cli-darwin-x64@2.4.7': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.4.7': + optional: true + + '@biomejs/cli-linux-arm64@2.4.7': + optional: true + + '@biomejs/cli-linux-x64-musl@2.4.7': + optional: true + + '@biomejs/cli-linux-x64@2.4.7': + optional: true + + '@biomejs/cli-win32-arm64@2.4.7': + optional: true + + '@biomejs/cli-win32-x64@2.4.7': + optional: true + + '@clack/core@1.1.0': + dependencies: + sisteransi: 1.0.5 + + '@clack/prompts@1.1.0': + dependencies: + '@clack/core': 1.1.0 + sisteransi: 1.0.5 + + '@emnapi/core@1.9.0': + dependencies: + '@emnapi/wasi-threads': 1.2.0 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.9.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@grpc/grpc-js@1.14.3': + dependencies: + '@grpc/proto-loader': 0.8.0 + '@js-sdsl/ordered-map': 4.4.2 + + '@grpc/proto-loader@0.8.0': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.5.4 + yargs: 17.7.2 + + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@js-sdsl/ordered-map@4.4.2': {} + + '@jsonjoy.com/base64@1.1.2(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/base64@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/buffers@1.2.1(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/buffers@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/codegen@1.0.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/codegen@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/fs-core@4.56.11(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-builtins': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.56.11(tslib@2.8.1) + thingies: 2.5.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-fsa@4.56.11(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-core': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.56.11(tslib@2.8.1) + thingies: 2.5.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-builtins@4.56.11(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-to-fsa@4.56.11(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-fsa': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.56.11(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-utils@4.56.11(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-builtins': 4.56.11(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node@4.56.11(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-core': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.56.11(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + thingies: 2.5.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-print@4.56.11(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-utils': 4.56.11(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-snapshot@4.56.11(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/json-pack': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pack@1.21.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 1.1.2(tslib@2.8.1) + '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/json-pointer': 1.0.2(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 2.5.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pack@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/json-pointer': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 2.5.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pointer@1.0.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pointer@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/util@1.9.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/util@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@napi-rs/wasm-runtime@1.1.1': + dependencies: + '@emnapi/core': 1.9.0 + '@emnapi/runtime': 1.9.0 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@oxc-project/types@0.115.0': {} + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@quansync/fs@1.0.0': + dependencies: + quansync: 1.0.0 + + '@rolldown/binding-android-arm64@1.0.0-rc.9': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0-rc.9': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-rc.9': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-rc.9': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.9': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.9': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.9': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.9': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.9': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.9': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.9': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.9': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.9': + dependencies: + '@napi-rs/wasm-runtime': 1.1.1 + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.9': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.9': + optional: true + + '@rolldown/pluginutils@1.0.0-rc.9': {} + + '@swc/core-darwin-arm64@1.15.18': + optional: true + + '@swc/core-darwin-x64@1.15.18': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.15.18': + optional: true + + '@swc/core-linux-arm64-gnu@1.15.18': + optional: true + + '@swc/core-linux-arm64-musl@1.15.18': + optional: true + + '@swc/core-linux-x64-gnu@1.15.18': + optional: true + + '@swc/core-linux-x64-musl@1.15.18': + optional: true + + '@swc/core-win32-arm64-msvc@1.15.18': + optional: true + + '@swc/core-win32-ia32-msvc@1.15.18': + optional: true + + '@swc/core-win32-x64-msvc@1.15.18': + optional: true + + '@swc/core@1.15.18': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.25 + optionalDependencies: + '@swc/core-darwin-arm64': 1.15.18 + '@swc/core-darwin-x64': 1.15.18 + '@swc/core-linux-arm-gnueabihf': 1.15.18 + '@swc/core-linux-arm64-gnu': 1.15.18 + '@swc/core-linux-arm64-musl': 1.15.18 + '@swc/core-linux-x64-gnu': 1.15.18 + '@swc/core-linux-x64-musl': 1.15.18 + '@swc/core-win32-arm64-msvc': 1.15.18 + '@swc/core-win32-ia32-msvc': 1.15.18 + '@swc/core-win32-x64-msvc': 1.15.18 + + '@swc/counter@0.1.3': {} + + '@swc/types@0.1.25': + dependencies: + '@swc/counter': 0.1.3 + + '@temporalio/activity@1.15.0': + dependencies: + '@temporalio/client': 1.15.0 + '@temporalio/common': 1.15.0 + abort-controller: 3.0.0 + + '@temporalio/client@1.15.0': + dependencies: + '@grpc/grpc-js': 1.14.3 + '@temporalio/common': 1.15.0 + '@temporalio/proto': 1.15.0 + abort-controller: 3.0.0 + long: 5.3.2 + uuid: 11.1.0 + + '@temporalio/common@1.15.0': + dependencies: + '@temporalio/proto': 1.15.0 + long: 5.3.2 + ms: 3.0.0-canary.1 + nexus-rpc: 0.0.1 + proto3-json-serializer: 2.0.2 + + '@temporalio/core-bridge@1.15.0': + dependencies: + '@grpc/grpc-js': 1.14.3 + '@temporalio/common': 1.15.0 + + '@temporalio/nexus@1.15.0': + dependencies: + '@temporalio/client': 1.15.0 + '@temporalio/common': 1.15.0 + '@temporalio/proto': 1.15.0 + long: 5.3.2 + nexus-rpc: 0.0.1 + + '@temporalio/proto@1.15.0': + dependencies: + long: 5.3.2 + protobufjs: 7.5.4 + + '@temporalio/worker@1.15.0(tslib@2.8.1)': + dependencies: + '@grpc/grpc-js': 1.14.3 + '@swc/core': 1.15.18 + '@temporalio/activity': 1.15.0 + '@temporalio/client': 1.15.0 + '@temporalio/common': 1.15.0 + '@temporalio/core-bridge': 1.15.0 + '@temporalio/nexus': 1.15.0 + '@temporalio/proto': 1.15.0 + '@temporalio/workflow': 1.15.0 + abort-controller: 3.0.0 + heap-js: 2.7.1 + memfs: 4.56.11(tslib@2.8.1) + nexus-rpc: 0.0.1 + proto3-json-serializer: 2.0.2 + protobufjs: 7.5.4 + rxjs: 7.8.2 + source-map: 0.7.6 + source-map-loader: 4.0.2(webpack@5.105.4(@swc/core@1.15.18)) + supports-color: 8.1.1 + swc-loader: 0.2.7(@swc/core@1.15.18)(webpack@5.105.4(@swc/core@1.15.18)) + unionfs: 4.6.0 + webpack: 5.105.4(@swc/core@1.15.18) + transitivePeerDependencies: + - '@swc/helpers' + - esbuild + - tslib + - uglify-js + - webpack-cli + + '@temporalio/workflow@1.15.0': + dependencies: + '@temporalio/common': 1.15.0 + '@temporalio/proto': 1.15.0 + nexus-rpc: 0.0.1 + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.8 + + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + + '@types/estree@1.0.8': {} + + '@types/js-yaml@4.0.9': {} + + '@types/jsesc@2.5.1': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@25.5.0': + dependencies: + undici-types: 7.18.2 + + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + acorn-import-phases@1.0.4(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + ajv-formats@2.1.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + + ajv-keywords@5.1.0(ajv@8.18.0): + dependencies: + ajv: 8.18.0 + fast-deep-equal: 3.1.3 + + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansis@4.2.0: {} + + argparse@2.0.1: {} + + ast-kit@3.0.0-beta.1: + dependencies: + '@babel/parser': 8.0.0-rc.2 + estree-walker: 3.0.3 + pathe: 2.0.3 + + baseline-browser-mapping@2.10.8: {} + + birpc@4.0.0: {} + + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.10.8 + caniuse-lite: 1.0.30001778 + electron-to-chromium: 1.5.313 + node-releases: 2.0.36 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + + buffer-from@1.1.2: {} + + cac@7.0.0: {} + + caniuse-lite@1.0.30001778: {} + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + + chrome-trace-event@1.0.4: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@2.20.3: {} + + defu@6.1.4: {} + + dotenv@16.6.1: {} + + dotenv@17.3.1: {} + + dts-resolver@2.1.3: {} + + electron-to-chromium@1.5.313: {} + + emoji-regex@8.0.0: {} + + empathic@2.0.0: {} + + enhanced-resolve@5.20.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + es-module-lexer@2.0.0: {} + + escalade@3.2.0: {} + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + event-target-shim@5.0.1: {} + + events@3.3.0: {} + + fast-deep-equal@3.1.3: {} + + fast-uri@3.1.0: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fs-monkey@1.1.0: {} + + get-caller-file@2.0.5: {} + + get-tsconfig@4.13.6: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-to-regex.js@1.2.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + + glob-to-regexp@0.4.1: {} + + graceful-fs@4.2.11: {} + + has-flag@4.0.0: {} + + heap-js@2.7.1: {} + + hookable@6.0.1: {} + + hyperdyperid@1.2.0: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + import-without-cache@0.2.5: {} + + is-fullwidth-code-point@3.0.0: {} + + jest-worker@27.5.1: + dependencies: + '@types/node': 25.5.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@1.0.0: {} + + loader-runner@4.3.1: {} + + lodash.camelcase@4.3.0: {} + + long@5.3.2: {} + + memfs@4.56.11(tslib@2.8.1): + dependencies: + '@jsonjoy.com/fs-core': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/fs-fsa': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/fs-node': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/fs-node-to-fsa': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.56.11(tslib@2.8.1) + '@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + thingies: 2.5.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + merge-stream@2.0.0: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + ms@3.0.0-canary.1: {} + + neo-async@2.6.2: {} + + nexus-rpc@0.0.1: {} + + node-releases@2.0.36: {} + + obug@2.1.1: {} + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.3: {} + + proto3-json-serializer@2.0.2: + dependencies: + protobufjs: 7.5.4 + + protobufjs@7.5.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 25.5.0 + long: 5.3.2 + + quansync@1.0.0: {} + + readdirp@5.0.0: {} + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + resolve-pkg-maps@1.0.0: {} + + rolldown-plugin-dts@0.22.5(rolldown@1.0.0-rc.9)(typescript@5.9.3): + dependencies: + '@babel/generator': 8.0.0-rc.2 + '@babel/helper-validator-identifier': 8.0.0-rc.2 + '@babel/parser': 8.0.0-rc.2 + '@babel/types': 8.0.0-rc.2 + ast-kit: 3.0.0-beta.1 + birpc: 4.0.0 + dts-resolver: 2.1.3 + get-tsconfig: 4.13.6 + obug: 2.1.1 + rolldown: 1.0.0-rc.9 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - oxc-resolver + + rolldown@1.0.0-rc.9: + dependencies: + '@oxc-project/types': 0.115.0 + '@rolldown/pluginutils': 1.0.0-rc.9 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-rc.9 + '@rolldown/binding-darwin-arm64': 1.0.0-rc.9 + '@rolldown/binding-darwin-x64': 1.0.0-rc.9 + '@rolldown/binding-freebsd-x64': 1.0.0-rc.9 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.9 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.9 + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.9 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.9 + '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.9 + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.9 + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.9 + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.9 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.9 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.9 + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.9 + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safer-buffer@2.1.2: {} + + schema-utils@4.3.3: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.18.0 + ajv-formats: 2.1.1(ajv@8.18.0) + ajv-keywords: 5.1.0(ajv@8.18.0) + + semver@7.7.4: {} + + sisteransi@1.0.5: {} + + smol-toml@1.6.0: {} + + source-map-js@1.2.1: {} + + source-map-loader@4.0.2(webpack@5.105.4(@swc/core@1.15.18)): + dependencies: + iconv-lite: 0.6.3 + source-map-js: 1.2.1 + webpack: 5.105.4(@swc/core@1.15.18) + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + source-map@0.7.6: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + swc-loader@0.2.7(@swc/core@1.15.18)(webpack@5.105.4(@swc/core@1.15.18)): + dependencies: + '@swc/core': 1.15.18 + '@swc/counter': 0.1.3 + webpack: 5.105.4(@swc/core@1.15.18) + + tapable@2.3.0: {} + + terser-webpack-plugin@5.4.0(@swc/core@1.15.18)(webpack@5.105.4(@swc/core@1.15.18)): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + terser: 5.46.0 + webpack: 5.105.4(@swc/core@1.15.18) + optionalDependencies: + '@swc/core': 1.15.18 + + terser@5.46.0: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.16.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + thingies@2.5.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + + tinyexec@1.0.4: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tree-dump@1.1.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + + tree-kill@1.2.2: {} + + tsdown@0.21.2(typescript@5.9.3): + dependencies: + ansis: 4.2.0 + cac: 7.0.0 + defu: 6.1.4 + empathic: 2.0.0 + hookable: 6.0.1 + import-without-cache: 0.2.5 + obug: 2.1.1 + picomatch: 4.0.3 + rolldown: 1.0.0-rc.9 + rolldown-plugin-dts: 0.22.5(rolldown@1.0.0-rc.9)(typescript@5.9.3) + semver: 7.7.4 + tinyexec: 1.0.4 + tinyglobby: 0.2.15 + tree-kill: 1.2.2 + unconfig-core: 7.5.0 + unrun: 0.2.32 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@ts-macro/tsc' + - '@typescript/native-preview' + - oxc-resolver + - synckit + - vue-tsc + + tslib@2.8.1: {} + + turbo-darwin-64@2.8.17: + optional: true + + turbo-darwin-arm64@2.8.17: + optional: true + + turbo-linux-64@2.8.17: + optional: true + + turbo-linux-arm64@2.8.17: + optional: true + + turbo-windows-64@2.8.17: + optional: true + + turbo-windows-arm64@2.8.17: + optional: true + + turbo@2.8.17: + optionalDependencies: + turbo-darwin-64: 2.8.17 + turbo-darwin-arm64: 2.8.17 + turbo-linux-64: 2.8.17 + turbo-linux-arm64: 2.8.17 + turbo-windows-64: 2.8.17 + turbo-windows-arm64: 2.8.17 + + typescript@5.9.3: {} + + unconfig-core@7.5.0: + dependencies: + '@quansync/fs': 1.0.0 + quansync: 1.0.0 + + undici-types@7.18.2: {} + + unionfs@4.6.0: + dependencies: + fs-monkey: 1.1.0 + + unrun@0.2.32: + dependencies: + rolldown: 1.0.0-rc.9 + + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + + uuid@11.1.0: {} + + watchpack@2.5.1: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + + webpack-sources@3.3.4: {} + + webpack@5.105.4(@swc/core@1.15.18): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.16.0 + acorn-import-phases: 1.0.4(acorn@8.16.0) + browserslist: 4.28.1 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.20.0 + es-module-lexer: 2.0.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.4.0(@swc/core@1.15.18)(webpack@5.105.4(@swc/core@1.15.18)) + watchpack: 2.5.1 + webpack-sources: 3.3.4 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + y18n@5.0.8: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + zod@4.3.6: {} + + zx@8.8.5: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..6b85677 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,6 @@ +packages: + - "apps/*" + - "packages/*" + +catalog: + "@anthropic-ai/claude-agent-sdk": ^0.2.38 diff --git a/shannon b/shannon index 8655cdc..6a3efae 100755 --- a/shannon +++ b/shannon @@ -1,378 +1,3 @@ -#!/bin/bash -# Shannon CLI - AI Penetration Testing Framework - -set -e - -# Prevent MSYS from converting Unix paths (e.g. /repos/my-repo) to Windows paths -case "$OSTYPE" in - msys*) export MSYS_NO_PATHCONV=1 ;; -esac - -# Detect Podman vs Docker and set compose files accordingly -# Podman doesn't support host-gateway, so we only include the Docker override for actual Docker -COMPOSE_BASE="docker-compose.yml" -if command -v podman &>/dev/null; then - # Podman detected (either native or via Docker Desktop shim) - use base config only - COMPOSE_OVERRIDE="" -else - # Docker detected - include extra_hosts override for Linux localhost access - COMPOSE_OVERRIDE="-f docker-compose.docker.yml" -fi -COMPOSE_FILE="$COMPOSE_BASE" - -# Load .env if present -if [ -f .env ]; then - set -a - source .env - set +a -fi - -show_help() { - cat << 'EOF' - - ███████╗██╗ ██╗ █████╗ ███╗ ██╗███╗ ██╗ ██████╗ ███╗ ██╗ - ██╔════╝██║ ██║██╔══██╗████╗ ██║████╗ ██║██╔═══██╗████╗ ██║ - ███████╗███████║███████║██╔██╗ ██║██╔██╗ ██║██║ ██║██╔██╗ ██║ - ╚════██║██╔══██║██╔══██║██║╚██╗██║██║╚██╗██║██║ ██║██║╚██╗██║ - ███████║██║ ██║██║ ██║██║ ╚████║██║ ╚████║╚██████╔╝██║ ╚████║ - ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═══╝ - - AI Penetration Testing Framework - -Usage: - ./shannon start URL= REPO= Start a pentest workflow - ./shannon workspaces List all workspaces - ./shannon logs ID= Tail logs for a specific workflow - ./shannon stop Stop all containers - ./shannon help Show this help message - -Options for 'start': - REPO= Folder name under ./repos/ (e.g. REPO=repo-name) - CONFIG= Configuration file (YAML) - OUTPUT= Output directory for reports (default: ./audit-logs/) - WORKSPACE= Named workspace (auto-resumes if exists, creates if new) - PIPELINE_TESTING=true Use minimal prompts for fast testing - ROUTER=true Route requests through claude-code-router (multi-model support) - -Options for 'stop': - CLEAN=true Remove all data including volumes - -Examples: - ./shannon start URL=https://example.com REPO=repo-name - ./shannon start URL=https://example.com REPO=repo-name WORKSPACE=q1-audit - ./shannon start URL=https://example.com REPO=repo-name CONFIG=./config.yaml - ./shannon start URL=https://example.com REPO=repo-name OUTPUT=./my-reports - ./shannon workspaces - ./shannon logs ID=example.com_shannon-1234567890 - ./shannon stop CLEAN=true - -Monitor workflows at http://localhost:8233 -EOF -} - -# Parse KEY=value arguments into variables -parse_args() { - for arg in "$@"; do - case "$arg" in - URL=*) URL="${arg#URL=}" ;; - REPO=*) REPO="${arg#REPO=}" ;; - CONFIG=*) CONFIG="${arg#CONFIG=}" ;; - OUTPUT=*) OUTPUT="${arg#OUTPUT=}" ;; - ID=*) ID="${arg#ID=}" ;; - CLEAN=*) CLEAN="${arg#CLEAN=}" ;; - PIPELINE_TESTING=*) PIPELINE_TESTING="${arg#PIPELINE_TESTING=}" ;; - REBUILD=*) REBUILD="${arg#REBUILD=}" ;; - ROUTER=*) ROUTER="${arg#ROUTER=}" ;; - WORKSPACE=*) WORKSPACE="${arg#WORKSPACE=}" ;; - esac - done -} - -# Check if Temporal is running and healthy -is_temporal_ready() { - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE exec -T temporal \ - temporal operator cluster health --address localhost:7233 2>/dev/null | grep -q "SERVING" -} - -# Ensure containers are running with correct mounts -ensure_containers() { - # If custom OUTPUT_DIR is set, always refresh worker to ensure correct volume mount - # Docker compose will only recreate if the mount actually changed - if [ -n "$OUTPUT_DIR" ]; then - echo "Ensuring worker has correct output mount..." - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE up -d worker 2>/dev/null || true - fi - - # Quick check: if Temporal is already healthy, we're good - if is_temporal_ready; then - return 0 - fi - - # Need to start containers - echo "Starting Shannon containers..." - if [ "$REBUILD" = "true" ]; then - # Force rebuild without cache (use when code changes aren't being picked up) - echo "Rebuilding with --no-cache..." - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE build --no-cache worker - fi - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE up -d --build - - # Wait for Temporal to be ready - echo "Waiting for Temporal to be ready..." - for i in $(seq 1 30); do - if is_temporal_ready; then - echo "Temporal is ready!" - return 0 - fi - if [ "$i" -eq 30 ]; then - echo "Timeout waiting for Temporal" - exit 1 - fi - sleep 2 - done -} - -cmd_start() { - parse_args "$@" - - # Validate required vars - if [ -z "$URL" ] || [ -z "$REPO" ]; then - echo "ERROR: URL and REPO are required" - echo "Usage: ./shannon start URL= REPO=" - exit 1 - fi - - # Check for API key (Bedrock, Vertex, router, and custom base URL modes can bypass this) - if [ -z "$ANTHROPIC_API_KEY" ] && [ -z "$CLAUDE_CODE_OAUTH_TOKEN" ]; then - if [ -n "$ANTHROPIC_BASE_URL" ] && [ -n "$ANTHROPIC_AUTH_TOKEN" ]; then - # Custom base URL mode — use auth token as API key for SDK initialization - echo "Using custom base URL: $ANTHROPIC_BASE_URL" - elif [ "$CLAUDE_CODE_USE_BEDROCK" = "1" ]; then - # Bedrock mode — validate required AWS credentials - MISSING="" - [ -z "$AWS_REGION" ] && MISSING="$MISSING AWS_REGION" - [ -z "$AWS_BEARER_TOKEN_BEDROCK" ] && MISSING="$MISSING AWS_BEARER_TOKEN_BEDROCK" - [ -z "$ANTHROPIC_SMALL_MODEL" ] && MISSING="$MISSING ANTHROPIC_SMALL_MODEL" - [ -z "$ANTHROPIC_MEDIUM_MODEL" ] && MISSING="$MISSING ANTHROPIC_MEDIUM_MODEL" - [ -z "$ANTHROPIC_LARGE_MODEL" ] && MISSING="$MISSING ANTHROPIC_LARGE_MODEL" - if [ -n "$MISSING" ]; then - echo "ERROR: Bedrock mode requires the following env vars in .env:$MISSING" - exit 1 - fi - elif [ "$CLAUDE_CODE_USE_VERTEX" = "1" ]; then - # Vertex AI mode — validate required GCP credentials - MISSING="" - [ -z "$CLOUD_ML_REGION" ] && MISSING="$MISSING CLOUD_ML_REGION" - [ -z "$ANTHROPIC_VERTEX_PROJECT_ID" ] && MISSING="$MISSING ANTHROPIC_VERTEX_PROJECT_ID" - [ -z "$ANTHROPIC_SMALL_MODEL" ] && MISSING="$MISSING ANTHROPIC_SMALL_MODEL" - [ -z "$ANTHROPIC_MEDIUM_MODEL" ] && MISSING="$MISSING ANTHROPIC_MEDIUM_MODEL" - [ -z "$ANTHROPIC_LARGE_MODEL" ] && MISSING="$MISSING ANTHROPIC_LARGE_MODEL" - if [ -n "$MISSING" ]; then - echo "ERROR: Vertex AI mode requires the following env vars in .env:$MISSING" - exit 1 - fi - # Validate service account key file (must be inside ./credentials/ for Docker mount) - if [ -z "$GOOGLE_APPLICATION_CREDENTIALS" ]; then - echo "ERROR: Vertex AI mode requires GOOGLE_APPLICATION_CREDENTIALS in .env" - echo " Place your service account key in ./credentials/ and set:" - echo " GOOGLE_APPLICATION_CREDENTIALS=./credentials/gcp-sa-key.json" - exit 1 - fi - if [ ! -f "$GOOGLE_APPLICATION_CREDENTIALS" ]; then - echo "ERROR: Service account key file not found: $GOOGLE_APPLICATION_CREDENTIALS" - echo " Download a key from the GCP Console (IAM > Service Accounts > Keys)" - exit 1 - fi - elif [ "$ROUTER" = "true" ] && { [ -n "$OPENAI_API_KEY" ] || [ -n "$OPENROUTER_API_KEY" ]; }; then - # Router mode with alternative provider - set a placeholder for SDK init - export ANTHROPIC_API_KEY="router-mode" - else - echo "ERROR: Set ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN in .env" - echo " (or use CLAUDE_CODE_USE_BEDROCK=1 for AWS Bedrock," - echo " CLAUDE_CODE_USE_VERTEX=1 for Google Vertex AI," - echo " or ROUTER=true with OPENAI_API_KEY or OPENROUTER_API_KEY)" - exit 1 - fi - fi - - # Determine container path for REPO - # - If REPO is already a container path (/benchmarks/*, /repos/*), use as-is - # - Otherwise, treat as a folder name under ./repos/ (mounted at /repos in container) - case "$REPO" in - /benchmarks/*|/repos/*) - CONTAINER_REPO="$REPO" - ;; - *) - if [ ! -d "./repos/$REPO" ]; then - echo "ERROR: Repository not found at ./repos/$REPO" - echo "" - echo "Place your target repository under the ./repos/ directory" - exit 1 - fi - CONTAINER_REPO="/repos/$REPO" - ;; - esac - - # Handle custom OUTPUT directory - # Export OUTPUT_DIR for docker-compose volume mount BEFORE starting containers - if [ -n "$OUTPUT" ]; then - # Create output directory with write permissions for container user (UID 1001) - mkdir -p "$OUTPUT" - chmod 777 "$OUTPUT" - export OUTPUT_DIR="$OUTPUT" - fi - - # Handle ROUTER flag - start claude-code-router for multi-model support - if [ "$ROUTER" = "true" ]; then - # Check if router is already running - if docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE --profile router ps router 2>/dev/null | grep -q "running"; then - echo "Router already running, skipping startup..." - else - echo "Starting claude-code-router..." - - # Check for provider API keys - if [ -z "$OPENAI_API_KEY" ] && [ -z "$OPENROUTER_API_KEY" ]; then - echo "WARNING: No provider API key set (OPENAI_API_KEY or OPENROUTER_API_KEY). Router may not work." - fi - - # Start router with profile - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE --profile router up -d router - - # Give router a few seconds to start (health check disabled for now - TODO: debug later) - echo "Waiting for router to start..." - sleep 5 - fi - - # Set ANTHROPIC_BASE_URL to route through router - export ANTHROPIC_BASE_URL="http://router:3456" - # Set auth token to match router's APIKEY - export ANTHROPIC_AUTH_TOKEN="shannon-router-key" - fi - - # Ensure audit-logs directory exists with write permissions for container user (UID 1001) - mkdir -p ./audit-logs ./credentials - chmod 777 ./audit-logs - - # Ensure repo deliverables directory is writable by container user (UID 1001) - if [ -d "./repos/$REPO" ]; then - mkdir -p "./repos/$REPO/deliverables" - chmod 777 "./repos/$REPO/deliverables" - fi - - # Ensure containers are running (starts them if needed) - ensure_containers - - # Build optional args - ARGS="" - [ -n "$CONFIG" ] && ARGS="$ARGS --config $CONFIG" - - # Pass container path for output (where OUTPUT_DIR is mounted) - # Also pass display path so client can show the host path to user - if [ -n "$OUTPUT" ]; then - ARGS="$ARGS --output /app/output --display-output $OUTPUT" - fi - - [ "$PIPELINE_TESTING" = "true" ] && ARGS="$ARGS --pipeline-testing" - [ -n "$WORKSPACE" ] && ARGS="$ARGS --workspace $WORKSPACE" - - # Run the client to submit workflow - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE exec -T worker \ - node dist/temporal/client.js "$URL" "$CONTAINER_REPO" $ARGS -} - -cmd_logs() { - parse_args "$@" - - if [ -z "$ID" ]; then - echo "ERROR: ID is required" - echo "Usage: ./shannon logs ID=" - exit 1 - fi - - # Auto-discover the workflow log file - # 1. Check default location first - # 2. Search common output directories - # 3. Fall back to find command - WORKFLOW_LOG="" - - if [ -f "./audit-logs/${ID}/workflow.log" ]; then - WORKFLOW_LOG="./audit-logs/${ID}/workflow.log" - else - # For resume workflow IDs (e.g. workspace_resume_123), check the original workspace - WORKSPACE_ID="${ID%%_resume_*}" - if [ "$WORKSPACE_ID" != "$ID" ] && [ -f "./audit-logs/${WORKSPACE_ID}/workflow.log" ]; then - WORKFLOW_LOG="./audit-logs/${WORKSPACE_ID}/workflow.log" - fi - - # For named workspace IDs (e.g. workspace_shannon-123), check the workspace name - if [ -z "$WORKFLOW_LOG" ]; then - WORKSPACE_ID="${ID%%_shannon-*}" - if [ "$WORKSPACE_ID" != "$ID" ] && [ -f "./audit-logs/${WORKSPACE_ID}/workflow.log" ]; then - WORKFLOW_LOG="./audit-logs/${WORKSPACE_ID}/workflow.log" - fi - fi - - if [ -z "$WORKFLOW_LOG" ]; then - # Search for the workflow directory (handles custom OUTPUT paths) - FOUND=$(find . -maxdepth 3 -path "*/${ID}/workflow.log" -type f 2>/dev/null | head -1) - if [ -n "$FOUND" ]; then - WORKFLOW_LOG="$FOUND" - fi - fi - fi - - if [ -n "$WORKFLOW_LOG" ]; then - echo "Tailing workflow log: $WORKFLOW_LOG" - tail -f "$WORKFLOW_LOG" - else - echo "ERROR: Workflow log not found for ID: $ID" - echo "" - echo "Possible causes:" - echo " - Workflow hasn't started yet" - echo " - Workflow ID is incorrect" - echo "" - echo "Check the Temporal Web UI at http://localhost:8233 for workflow details" - exit 1 - fi -} - -cmd_workspaces() { - # Ensure containers are running (need worker to execute node) - ensure_containers - - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE exec -T worker \ - node dist/temporal/workspaces.js -} - -cmd_stop() { - parse_args "$@" - - if [ "$CLEAN" = "true" ]; then - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE --profile router down -v - else - docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE --profile router down - fi -} - -# Main command dispatch -case "${1:-help}" in - start) - shift - cmd_start "$@" - ;; - logs) - shift - cmd_logs "$@" - ;; - workspaces) - shift - cmd_workspaces - ;; - stop) - shift - cmd_stop "$@" - ;; - help|--help|-h|*) - show_help - ;; -esac +#!/usr/bin/env node +process.env.SHANNON_LOCAL = '1'; +import('./apps/cli/dist/index.mjs'); diff --git a/tsconfig.json b/tsconfig.json index 5bf6a7e..63581e2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,4 @@ { - "extends": "./tsconfig.base.json", - "compilerOptions": { - "rootDir": "./src", - "outDir": "./dist" - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "mcp-server"] + "files": [], + "references": [{ "path": "apps/worker" }, { "path": "apps/cli" }, { "path": "packages/mcp-server" }] } diff --git a/turbo.json b/turbo.json new file mode 100644 index 0000000..43b1979 --- /dev/null +++ b/turbo.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://turbo.build/schema.json", + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**"], + "inputs": ["src/**/*.ts", "tsconfig.json", "package.json"] + }, + "check": { + "dependsOn": ["^build"], + "inputs": ["src/**/*.ts", "tsconfig.json"] + }, + "clean": { + "cache": false + } + } +}