commit 8391eb0f70f87ff1406fc620dd40fbb84f0a3c53 Author: Jonas Haugesen Date: Mon Apr 6 15:09:41 2026 +0200 panopticon init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..485ea52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +dist/ +state/ +runs/ +*.log diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..9a4632e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,45 @@ +# Panopticon — Automated Project Documentation Registry + +## Overview + +Panopticon is a nightly batch system that maintains up-to-date, LLM-optimized project documentation as pi skill files. It analyzes git diffs since the last run, dispatches parallel LLM workers to update documentation sections, synthesizes the results, and writes pi-compatible skill directories into each project's `.pi/skills/panopticon/` folder. + +## Architecture + +- **CLI entry point:** `src/index.ts` — handles `--full-analysis `, `--project `, `--dry-run` +- **Config:** `src/config.ts` — loads `config.json` with project registry, model config, limits +- **Git:** `src/git.ts` — git operations (pull, diff, log, file tree, churn) +- **Structural:** `src/structural.ts` — regex-based AST extraction, import graph, file hashing +- **Session:** `src/session.ts` — pi SDK session factory for orchestrator/worker/synthesizer roles +- **Orchestrator:** `src/orchestrator.ts` — analyzes diffs, plans work units +- **Worker:** `src/worker.ts` — generates/updates doc sections with read tool access +- **Synthesizer:** `src/synthesizer.ts` — reconciles worker outputs, generates SKILL.md +- **Writer:** `src/writer.ts` — writes skill files to `/.pi/skills/panopticon/` +- **Reporter:** `src/reporter.ts` — generates run reports, detects anomalies +- **Metrics:** `src/metrics.ts` — pushes Prometheus metrics to Victoria Metrics +- **State:** `src/state.ts` — per-project state persistence (last SHA, file hashes) +- **Types:** `src/types.ts` — shared type definitions + +## Key Conventions + +- All pi SDK sessions are created via `createSession()` in `session.ts` +- Workers get read-only tools; orchestrator and synthesizer get no tools +- Prompts live in `prompts/` directory as standalone markdown files +- Models default to Anthropic (claude-sonnet-4-5 for smart, claude-haiku-4-5 for cheap) +- Config is in `config.json` at project root +- State persisted in `state/` directory, run reports in `runs/` + +## Build & Run + +```bash +npm run build # Compile TypeScript +npm start # Run incremental (all projects) +node dist/index.js --full-analysis snow_trail_sdl # Full analysis +node dist/index.js --dry-run # Test without writing +``` + +## Dependencies + +- `@mariozechner/pi-coding-agent` — pi SDK for LLM sessions +- `@mariozechner/pi-ai` — model resolution and streaming +- Node.js 20+, TypeScript 5.7+ diff --git a/config.json b/config.json new file mode 100644 index 0000000..ecfdffc --- /dev/null +++ b/config.json @@ -0,0 +1,36 @@ +{ + "projects": [ + { + "name": "snow_trail", + "path": "/home/jonas/projects/snow_trail", + "language": "rust", + "sourceGlobs": ["src/**/*.rs", "shaders/**/*.wgsl"], + "excludeGlobs": ["target/**", "*.lock"], + "branch": "main" + } + ], + "models": { + "orchestrator": "anthropic/claude-sonnet-4-5", + "worker": "anthropic/claude-haiku-4-5", + "synthesizer": "anthropic/claude-sonnet-4-5" + }, + "thinkingLevels": { + "orchestrator": "medium", + "worker": "off", + "synthesizer": "low" + }, + "metrics": { + "enabled": true, + "victoriaMetricsUrl": "http://localhost:8428", + "jobLabel": "panopticon" + }, + "limits": { + "maxWorkerConcurrency": 4, + "maxDiffSizeBytes": 200000, + "maxFilesPerWorkUnit": 15, + "workerTimeoutSeconds": 120, + "synthesizerTimeoutSeconds": 180 + }, + "stateDir": "./state", + "runsDir": "./runs" +} diff --git a/grafana/dashboard.json b/grafana/dashboard.json new file mode 100644 index 0000000..f3211a7 --- /dev/null +++ b/grafana/dashboard.json @@ -0,0 +1,122 @@ +{ + "annotations": { "list": [] }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "id": null, + "links": [], + "panels": [ + { + "title": "Run Status Timeline", + "type": "state-timeline", + "gridPos": { "h": 6, "w": 12, "x": 0, "y": 0 }, + "targets": [ + { + "expr": "panopticon_run_status", + "legendFormat": "{{project}}" + } + ] + }, + { + "title": "Run Duration", + "type": "timeseries", + "gridPos": { "h": 6, "w": 12, "x": 12, "y": 0 }, + "targets": [ + { + "expr": "panopticon_run_duration_seconds", + "legendFormat": "{{project}}" + } + ] + }, + { + "title": "Token Usage", + "type": "barchart", + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 6 }, + "targets": [ + { + "expr": "sum by (model, direction) (panopticon_tokens_total)", + "legendFormat": "{{model}} {{direction}}" + } + ] + }, + { + "title": "Estimated Cost (30d)", + "type": "stat", + "gridPos": { "h": 4, "w": 6, "x": 12, "y": 6 }, + "targets": [ + { + "expr": "sum(increase(panopticon_estimated_cost_usd[30d]))", + "legendFormat": "Cost" + } + ], + "fieldConfig": { + "defaults": { + "unit": "currencyUSD" + } + } + }, + { + "title": "Error Rate (7d)", + "type": "stat", + "gridPos": { "h": 4, "w": 6, "x": 18, "y": 6 }, + "targets": [ + { + "expr": "sum(increase(panopticon_errors_total[7d]))", + "legendFormat": "Errors" + } + ], + "fieldConfig": { + "defaults": { + "thresholds": { + "steps": [ + { "color": "green", "value": 0 }, + { "color": "yellow", "value": 1 }, + { "color": "red", "value": 5 } + ] + } + } + } + }, + { + "title": "Files Changed", + "type": "timeseries", + "gridPos": { "h": 6, "w": 12, "x": 12, "y": 10 }, + "targets": [ + { + "expr": "panopticon_files_changed", + "legendFormat": "{{project}}" + } + ] + }, + { + "title": "Doc Churn", + "type": "table", + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 14 }, + "targets": [ + { + "expr": "panopticon_doc_lines_changed", + "legendFormat": "{{project}} {{file}}", + "format": "table", + "instant": true + } + ] + }, + { + "title": "Phase Breakdown", + "type": "barchart", + "gridPos": { "h": 8, "w": 12, "x": 12, "y": 14 }, + "targets": [ + { + "expr": "panopticon_phase_duration_seconds", + "legendFormat": "{{project}} {{phase}}" + } + ] + } + ], + "schemaVersion": 39, + "tags": ["panopticon"], + "templating": { "list": [] }, + "time": { "from": "now-30d", "to": "now" }, + "title": "Panopticon", + "uid": "panopticon-main" +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2124514 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3553 @@ +{ + "name": "panopticon", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "panopticon", + "version": "0.1.0", + "dependencies": { + "@mariozechner/pi-ai": "^0.57.0", + "@mariozechner/pi-coding-agent": "^0.57.0" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "typescript": "^5.7.0" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.73.0.tgz", + "integrity": "sha512-URURVzhxXGJDGUGFunIOtBlSl7KWvZiAAKY/ttTkZAkXT9bTPqdk2eK0b8qqSxXpikh3QKPnPYpiyX98zf5ebw==", + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "^3.1.1" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-runtime": { + "version": "3.1023.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1023.0.tgz", + "integrity": "sha512-C0He9qhrClUp6JEk3QjE0WScDN1GSZF8eruP0uoh5kXeQEJLxyfFDrR2TIYnHntlRs/sMwhO82Vu7yGGQM2pfQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/credential-provider-node": "^3.972.29", + "@aws-sdk/eventstream-handler-node": "^3.972.12", + "@aws-sdk/middleware-eventstream": "^3.972.8", + "@aws-sdk/middleware-host-header": "^3.972.8", + "@aws-sdk/middleware-logger": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.9", + "@aws-sdk/middleware-user-agent": "^3.972.28", + "@aws-sdk/middleware-websocket": "^3.972.14", + "@aws-sdk/region-config-resolver": "^3.972.10", + "@aws-sdk/token-providers": "3.1023.0", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@aws-sdk/util-user-agent-browser": "^3.972.8", + "@aws-sdk/util-user-agent-node": "^3.973.14", + "@smithy/config-resolver": "^4.4.13", + "@smithy/core": "^3.23.13", + "@smithy/eventstream-serde-browser": "^4.2.12", + "@smithy/eventstream-serde-config-resolver": "^4.3.12", + "@smithy/eventstream-serde-node": "^4.2.12", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/hash-node": "^4.2.12", + "@smithy/invalid-dependency": "^4.2.12", + "@smithy/middleware-content-length": "^4.2.12", + "@smithy/middleware-endpoint": "^4.4.28", + "@smithy/middleware-retry": "^4.4.46", + "@smithy/middleware-serde": "^4.2.16", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/node-http-handler": "^4.5.1", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.44", + "@smithy/util-defaults-mode-node": "^4.2.48", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.13", + "@smithy/util-stream": "^4.5.21", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.973.26", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.26.tgz", + "integrity": "sha512-A/E6n2W42ruU+sfWk+mMUOyVXbsSgGrY3MJ9/0Az5qUdG67y8I6HYzzoAa+e/lzxxl1uCYmEL6BTMi9ZiZnplQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/xml-builder": "^3.972.16", + "@smithy/core": "^3.23.13", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/signature-v4": "^5.3.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.24", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.24.tgz", + "integrity": "sha512-FWg8uFmT6vQM7VuzELzwVo5bzExGaKHdubn0StjgrcU5FvuLExUe+k06kn/40uKv59rYzhez8eFNM4yYE/Yb/w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.26", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.26.tgz", + "integrity": "sha512-CY4ppZ+qHYqcXqBVi//sdHST1QK3KzOEiLtpLsc9W2k2vfZPKExGaQIsOwcyvjpjUEolotitmd3mUNY56IwDEA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/types": "^3.973.6", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/node-http-handler": "^4.5.1", + "@smithy/property-provider": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "@smithy/util-stream": "^4.5.21", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.28", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.28.tgz", + "integrity": "sha512-wXYvq3+uQcZV7k+bE4yDXCTBdzWTU9x/nMiKBfzInmv6yYK1veMK0AKvRfRBd72nGWYKcL6AxwiPg9z/pYlgpw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/credential-provider-env": "^3.972.24", + "@aws-sdk/credential-provider-http": "^3.972.26", + "@aws-sdk/credential-provider-login": "^3.972.28", + "@aws-sdk/credential-provider-process": "^3.972.24", + "@aws-sdk/credential-provider-sso": "^3.972.28", + "@aws-sdk/credential-provider-web-identity": "^3.972.28", + "@aws-sdk/nested-clients": "^3.996.18", + "@aws-sdk/types": "^3.973.6", + "@smithy/credential-provider-imds": "^4.2.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.28", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.28.tgz", + "integrity": "sha512-ZSTfO6jqUTCysbdBPtEX5OUR//3rbD0lN7jO3sQeS2Gjr/Y+DT6SbIJ0oT2cemNw3UzKu97sNONd1CwNMthuZQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/nested-clients": "^3.996.18", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.29", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.29.tgz", + "integrity": "sha512-clSzDcvndpFJAggLDnDb36sPdlZYyEs5Zm6zgZjjUhwsJgSWiWKwFIXUVBcbruidNyBdbpOv2tNDL9sX8y3/0g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.24", + "@aws-sdk/credential-provider-http": "^3.972.26", + "@aws-sdk/credential-provider-ini": "^3.972.28", + "@aws-sdk/credential-provider-process": "^3.972.24", + "@aws-sdk/credential-provider-sso": "^3.972.28", + "@aws-sdk/credential-provider-web-identity": "^3.972.28", + "@aws-sdk/types": "^3.973.6", + "@smithy/credential-provider-imds": "^4.2.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.24", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.24.tgz", + "integrity": "sha512-Q2k/XLrFXhEztPHqj4SLCNID3hEPdlhh1CDLBpNnM+1L8fq7P+yON9/9M1IGN/dA5W45v44ylERfXtDAlmMNmw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.28", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.28.tgz", + "integrity": "sha512-IoUlmKMLEITFn1SiCTjPfR6KrE799FBo5baWyk/5Ppar2yXZoUdaRqZzJzK6TcJxx450M8m8DbpddRVYlp5R/A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/nested-clients": "^3.996.18", + "@aws-sdk/token-providers": "3.1021.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": { + "version": "3.1021.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1021.0.tgz", + "integrity": "sha512-TKY6h9spUk3OLs5v1oAgW9mAeBE3LAGNBwJokLy96wwmd4W2v/tYlXseProyed9ValDj2u1jK/4Rg1T+1NXyJA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/nested-clients": "^3.996.18", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.28", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.28.tgz", + "integrity": "sha512-d+6h0SD8GGERzKe27v5rOzNGKOl0D+l0bWJdqrxH8WSQzHzjsQFIAPgIeOTUwBHVsKKwtSxc91K/SWax6XgswQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/nested-clients": "^3.996.18", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-handler-node": { + "version": "3.972.12", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.12.tgz", + "integrity": "sha512-ruyc/MNR6e+cUrGCth7fLQ12RXBZDy/bV06tgqB9Z5n/0SN/C0m6bsQEV8FF9zPI6VSAOaRd0rNgmpYVnGawrQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/eventstream-codec": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-eventstream": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.8.tgz", + "integrity": "sha512-r+oP+tbCxgqXVC3pu3MUVePgSY0ILMjA+aEwOosS77m3/DRbtvHrHwqvMcw+cjANMeGzJ+i0ar+n77KXpRA8RQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.8.tgz", + "integrity": "sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.8.tgz", + "integrity": "sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.972.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.9.tgz", + "integrity": "sha512-/Wt5+CT8dpTFQxEJ9iGy/UGrXr7p2wlIOEHvIr/YcHYByzoLjrqkYqXdJjd9UIgWjv7eqV2HnFJen93UTuwfTQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.972.28", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.28.tgz", + "integrity": "sha512-cfWZFlVh7Va9lRay4PN2A9ARFzaBYcA097InT5M2CdRS05ECF5yaz86jET8Wsl2WcyKYEvVr/QNmKtYtafUHtQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@smithy/core": "^3.23.13", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-retry": "^4.2.13", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-websocket": { + "version": "3.972.14", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.14.tgz", + "integrity": "sha512-qnfDlIHjm6DrTYNvWOUbnZdVKgtoKbO/Qzj+C0Wp5Y7VUrsvBRQtGKxD+hc+mRTS4N0kBJ6iZ3+zxm4N1OSyjg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-format-url": "^3.972.8", + "@smithy/eventstream-codec": "^4.2.12", + "@smithy/eventstream-serde-browser": "^4.2.12", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/protocol-http": "^5.3.12", + "@smithy/signature-v4": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.996.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.18.tgz", + "integrity": "sha512-c7ZSIXrESxHKx2Mcopgd8AlzZgoXMr20fkx5ViPWPOLBvmyhw9VwJx/Govg8Ef/IhEon5R9l53Z8fdYSEmp6VA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/middleware-host-header": "^3.972.8", + "@aws-sdk/middleware-logger": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.9", + "@aws-sdk/middleware-user-agent": "^3.972.28", + "@aws-sdk/region-config-resolver": "^3.972.10", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@aws-sdk/util-user-agent-browser": "^3.972.8", + "@aws-sdk/util-user-agent-node": "^3.973.14", + "@smithy/config-resolver": "^4.4.13", + "@smithy/core": "^3.23.13", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/hash-node": "^4.2.12", + "@smithy/invalid-dependency": "^4.2.12", + "@smithy/middleware-content-length": "^4.2.12", + "@smithy/middleware-endpoint": "^4.4.28", + "@smithy/middleware-retry": "^4.4.46", + "@smithy/middleware-serde": "^4.2.16", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/node-http-handler": "^4.5.1", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.44", + "@smithy/util-defaults-mode-node": "^4.2.48", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.13", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.10.tgz", + "integrity": "sha512-1dq9ToC6e070QvnVhhbAs3bb5r6cQ10gTVc6cyRV5uvQe7P138TV2uG2i6+Yok4bAkVAcx5AqkTEBUvWEtBlsQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/config-resolver": "^4.4.13", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.1023.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1023.0.tgz", + "integrity": "sha512-g/t814ec7g+MbazONIdQzb0c8FalVnSKCLc665GLG4QdrviKXHzag7HQmf5wBhCDsUDNAIi77fLeElaZSkylTA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/nested-clients": "^3.996.18", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.973.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.6.tgz", + "integrity": "sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.996.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.5.tgz", + "integrity": "sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-endpoints": "^3.3.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.972.8.tgz", + "integrity": "sha512-J6DS9oocrgxM8xlUTTmQOuwRF6rnAGEujAN9SAzllcrQmwn5iJ58ogxy3SEhD0Q7JZvlA5jvIXBkpQRqEqlE9A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/querystring-builder": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", + "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.8.tgz", + "integrity": "sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.973.14", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.14.tgz", + "integrity": "sha512-vNSB/DYaPOyujVZBg/zUznH9QC142MaTHVmaFlF7uzzfg3CgT9f/l4C0Yi+vU/tbBhxVcXVB90Oohk5+o+ZbWw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "^3.972.28", + "@aws-sdk/types": "^3.973.6", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-config-provider": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.16", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.16.tgz", + "integrity": "sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "fast-xml-parser": "5.5.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", + "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@borewit/text-codec": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@google/genai": { + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.48.0.tgz", + "integrity": "sha512-plonYK4ML2PrxsRD9SeqmFt76eREWkQdPCglOA6aYDzL1AAbE+7PUnT54SvpWGfws13L0AZEqGSpL7+1IPnTxQ==", + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^10.3.0", + "p-retry": "^4.6.2", + "protobufjs": "^7.5.4", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.25.2" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@mariozechner/clipboard": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard/-/clipboard-0.3.2.tgz", + "integrity": "sha512-IHQpksNjo7EAtGuHFU+tbWDp5LarH3HU/8WiB9O70ZEoBPHOg0/6afwSLK0QyNMMmx4Bpi/zl6+DcBXe95nWYA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@mariozechner/clipboard-darwin-arm64": "0.3.2", + "@mariozechner/clipboard-darwin-universal": "0.3.2", + "@mariozechner/clipboard-darwin-x64": "0.3.2", + "@mariozechner/clipboard-linux-arm64-gnu": "0.3.2", + "@mariozechner/clipboard-linux-arm64-musl": "0.3.2", + "@mariozechner/clipboard-linux-riscv64-gnu": "0.3.2", + "@mariozechner/clipboard-linux-x64-gnu": "0.3.2", + "@mariozechner/clipboard-linux-x64-musl": "0.3.2", + "@mariozechner/clipboard-win32-arm64-msvc": "0.3.2", + "@mariozechner/clipboard-win32-x64-msvc": "0.3.2" + } + }, + "node_modules/@mariozechner/clipboard-darwin-arm64": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-arm64/-/clipboard-darwin-arm64-0.3.2.tgz", + "integrity": "sha512-uBf6K7Je1ihsgvmWxA8UCGCeI+nbRVRXoarZdLjl6slz94Zs1tNKFZqx7aCI5O1i3e0B6ja82zZ06BWrl0MCVw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-darwin-universal": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-universal/-/clipboard-darwin-universal-0.3.2.tgz", + "integrity": "sha512-mxSheKTW2U9LsBdXy0SdmdCAE5HqNS9QUmpNHLnfJ+SsbFKALjEZc5oRrVMXxGQSirDvYf5bjmRyT0QYYonnlg==", + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-darwin-x64": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-x64/-/clipboard-darwin-x64-0.3.2.tgz", + "integrity": "sha512-U1BcVEoidvwIp95+HJswSW+xr28EQiHR7rZjH6pn8Sja5yO4Yoe3yCN0Zm8Lo72BbSOK/fTSq0je7CJpaPCspg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-arm64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-arm64-gnu/-/clipboard-linux-arm64-gnu-0.3.2.tgz", + "integrity": "sha512-BsinwG3yWTIjdgNCxsFlip7LkfwPk+ruw/aFCXHUg/fb5XC/Ksp+YMQ7u0LUtiKzIv/7LMXgZInJQH6gxbAaqQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-arm64-musl": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-arm64-musl/-/clipboard-linux-arm64-musl-0.3.2.tgz", + "integrity": "sha512-0/Gi5Xq2V6goXBop19ePoHvXsmJD9SzFlO3S+d6+T2b+BlPcpOu3Oa0wTjl+cZrLAAEzA86aPNBI+VVAFDFPKw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-riscv64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-riscv64-gnu/-/clipboard-linux-riscv64-gnu-0.3.2.tgz", + "integrity": "sha512-2AFFiXB24qf0zOZsxI1GJGb9wQGlOJyN6UwoXqmKS3dpQi/l6ix30IzDDA4c4ZcCcx4D+9HLYXhC1w7Sov8pXA==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-x64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-gnu/-/clipboard-linux-x64-gnu-0.3.2.tgz", + "integrity": "sha512-v6fVnsn7WMGg73Dab8QMwyFce7tzGfgEixKgzLP8f1GJqkJZi5zO4k4FOHzSgUufgLil63gnxvMpjWkgfeQN7A==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-x64-musl": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-musl/-/clipboard-linux-x64-musl-0.3.2.tgz", + "integrity": "sha512-xVUtnoMQ8v2JVyfJLKKXACA6avdnchdbBkTsZs8BgJQo29qwCp5NIHAUO8gbJ40iaEGToW5RlmVk2M9V0HsHEw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-win32-arm64-msvc": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-arm64-msvc/-/clipboard-win32-arm64-msvc-0.3.2.tgz", + "integrity": "sha512-AEgg95TNi8TGgak2wSXZkXKCvAUTjWoU1Pqb0ON7JHrX78p616XUFNTJohtIon3e0w6k0pYPZeCuqRCza/Tqeg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-win32-x64-msvc": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-x64-msvc/-/clipboard-win32-x64-msvc-0.3.2.tgz", + "integrity": "sha512-tGRuYpZwDOD7HBrCpyRuhGnHHSCknELvqwKKUG4JSfSB7JIU7LKRh6zx6fMUOQd8uISK35TjFg5UcNih+vJhFA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/jiti": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@mariozechner/jiti/-/jiti-2.6.5.tgz", + "integrity": "sha512-faGUlTcXka5l7rv0lP3K3vGW/ejRuOS24RR2aSFWREUQqzjgdsuWNo/IiPqL3kWRGt6Ahl2+qcDAwtdeWeuGUw==", + "license": "MIT", + "dependencies": { + "std-env": "^3.10.0", + "yoctocolors": "^2.1.2" + }, + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/@mariozechner/pi-agent-core": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-agent-core/-/pi-agent-core-0.57.1.tgz", + "integrity": "sha512-WXsBbkNWOObFGHkhixaT8GXJpHDd3+fn8QntYF+4R8Sa9WB90ENXWidO6b7vcKX+JX0jjO5dIsQxmzosARJKlg==", + "license": "MIT", + "dependencies": { + "@mariozechner/pi-ai": "^0.57.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@mariozechner/pi-ai": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-ai/-/pi-ai-0.57.1.tgz", + "integrity": "sha512-Bd/J4a3YpdzJVyHLih0vDSdB0QPL4ti0XsAwtHOK/8eVhB0fHM1CpcgIrcBFJ23TMcKXMi0qamz18ERfp8tmgg==", + "license": "MIT", + "dependencies": { + "@anthropic-ai/sdk": "^0.73.0", + "@aws-sdk/client-bedrock-runtime": "^3.983.0", + "@google/genai": "^1.40.0", + "@mistralai/mistralai": "1.14.1", + "@sinclair/typebox": "^0.34.41", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "chalk": "^5.6.2", + "openai": "6.26.0", + "partial-json": "^0.1.7", + "proxy-agent": "^6.5.0", + "undici": "^7.19.1", + "zod-to-json-schema": "^3.24.6" + }, + "bin": { + "pi-ai": "dist/cli.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@mariozechner/pi-coding-agent": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-coding-agent/-/pi-coding-agent-0.57.1.tgz", + "integrity": "sha512-u5MQEduj68rwVIsRsqrWkJYiJCyPph/a6bMoJAQKo1sb+Pc17Y/ojwa+wGssnUMjEB38AQKofWTVe8NFEpSWNw==", + "license": "MIT", + "dependencies": { + "@mariozechner/jiti": "^2.6.2", + "@mariozechner/pi-agent-core": "^0.57.1", + "@mariozechner/pi-ai": "^0.57.1", + "@mariozechner/pi-tui": "^0.57.1", + "@silvia-odwyer/photon-node": "^0.3.4", + "chalk": "^5.5.0", + "cli-highlight": "^2.1.11", + "diff": "^8.0.2", + "extract-zip": "^2.0.1", + "file-type": "^21.1.1", + "glob": "^13.0.1", + "hosted-git-info": "^9.0.2", + "ignore": "^7.0.5", + "marked": "^15.0.12", + "minimatch": "^10.2.3", + "proper-lockfile": "^4.1.2", + "strip-ansi": "^7.1.0", + "undici": "^7.19.1", + "yaml": "^2.8.2" + }, + "bin": { + "pi": "dist/cli.js" + }, + "engines": { + "node": ">=20.6.0" + }, + "optionalDependencies": { + "@mariozechner/clipboard": "^0.3.2" + } + }, + "node_modules/@mariozechner/pi-tui": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-tui/-/pi-tui-0.57.1.tgz", + "integrity": "sha512-cjoRghLbeAHV0tTJeHgZXaryUi5zzBZofeZ7uJun1gztnckLLRjoVeaPTujNlc5BIfyKvFqhh1QWCZng/MXlpg==", + "license": "MIT", + "dependencies": { + "@types/mime-types": "^2.1.4", + "chalk": "^5.5.0", + "get-east-asian-width": "^1.3.0", + "marked": "^15.0.12", + "mime-types": "^3.0.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "optionalDependencies": { + "koffi": "^2.9.0" + } + }, + "node_modules/@mistralai/mistralai": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.14.1.tgz", + "integrity": "sha512-IiLmmZFCCTReQgPAT33r7KQ1nYo5JPdvGkrkZqA8qQ2qB1GHgs5LoP5K2ICyrjnpw2n8oSxMM/VP+liiKcGNlQ==", + "dependencies": { + "ws": "^8.18.0", + "zod": "^3.25.0 || ^4.0.0", + "zod-to-json-schema": "^3.24.1" + } + }, + "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/@silvia-odwyer/photon-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz", + "integrity": "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==", + "license": "Apache-2.0" + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.49", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", + "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", + "license": "MIT" + }, + "node_modules/@smithy/config-resolver": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.13.tgz", + "integrity": "sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.23.13", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.13.tgz", + "integrity": "sha512-J+2TT9D6oGsUVXVEMvz8h2EmdVnkBiy2auCie4aSJMvKlzUtO5hqjEzXhoCUkIMo7gAYjbQcN0g/MMSXEhDs1Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-stream": "^4.5.21", + "@smithy/util-utf8": "^4.2.2", + "@smithy/uuid": "^1.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.12.tgz", + "integrity": "sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.12.tgz", + "integrity": "sha512-FE3bZdEl62ojmy8x4FHqxq2+BuOHlcxiH5vaZ6aqHJr3AIZzwF5jfx8dEiU/X0a8RboyNDjmXjlbr8AdEyLgiA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.13.1", + "@smithy/util-hex-encoding": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.12.tgz", + "integrity": "sha512-XUSuMxlTxV5pp4VpqZf6Sa3vT/Q75FVkLSpSSE3KkWBvAQWeuWt1msTv8fJfgA4/jcJhrbrbMzN1AC/hvPmm5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.3.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.12.tgz", + "integrity": "sha512-7epsAZ3QvfHkngz6RXQYseyZYHlmWXSTPOfPmXkiS+zA6TBNo1awUaMFL9vxyXlGdoELmCZyZe1nQE+imbmV+Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.12.tgz", + "integrity": "sha512-D1pFuExo31854eAvg89KMn9Oab/wEeJR6Buy32B49A9Ogdtx5fwZPqBHUlDzaCDpycTFk2+fSQgX689Qsk7UGA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.12.tgz", + "integrity": "sha512-+yNuTiyBACxOJUTvbsNsSOfH9G9oKbaJE1lNL3YHpGcuucl6rPZMi3nrpehpVOVR2E07YqFFmtwpImtpzlouHQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.15", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.15.tgz", + "integrity": "sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.12", + "@smithy/querystring-builder": "^4.2.12", + "@smithy/types": "^4.13.1", + "@smithy/util-base64": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.12.tgz", + "integrity": "sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.12.tgz", + "integrity": "sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.2.tgz", + "integrity": "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.12.tgz", + "integrity": "sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.4.28", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.28.tgz", + "integrity": "sha512-p1gfYpi91CHcs5cBq982UlGlDrxoYUX6XdHSo91cQ2KFuz6QloHosO7Jc60pJiVmkWrKOV8kFYlGFFbQ2WUKKQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.13", + "@smithy/middleware-serde": "^4.2.16", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-middleware": "^4.2.12", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.4.46", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.46.tgz", + "integrity": "sha512-SpvWNNOPOrKQGUqZbEPO+es+FRXMWvIyzUKUOYdDgdlA6BdZj/R58p4umoQ76c2oJC44PiM7mKizyyex1IJzow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/service-error-classification": "^4.2.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.13", + "@smithy/uuid": "^1.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.2.16", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.16.tgz", + "integrity": "sha512-beqfV+RZ9RSv+sQqor3xroUUYgRFCGRw6niGstPG8zO9LgTl0B0MCucxjmrH/2WwksQN7UUgI7KNANoZv+KALA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.13", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.12.tgz", + "integrity": "sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.3.12", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.12.tgz", + "integrity": "sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.5.1.tgz", + "integrity": "sha512-ejjxdAXjkPIs9lyYyVutOGNOraqUE9v/NjGMKwwFrfOM354wfSD8lmlj8hVwUzQmlLLF4+udhfCX9Exnbmvfzw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.12", + "@smithy/querystring-builder": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.12.tgz", + "integrity": "sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.3.12", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.12.tgz", + "integrity": "sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.12.tgz", + "integrity": "sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "@smithy/util-uri-escape": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.12.tgz", + "integrity": "sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.12.tgz", + "integrity": "sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.7.tgz", + "integrity": "sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.3.12", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.12.tgz", + "integrity": "sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.2", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-uri-escape": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.12.8", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.8.tgz", + "integrity": "sha512-aJaAX7vHe5i66smoSSID7t4rKY08PbD8EBU7DOloixvhOozfYWdcSYE4l6/tjkZ0vBZhGjheWzB2mh31sLgCMA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.13", + "@smithy/middleware-endpoint": "^4.4.28", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-stream": "^4.5.21", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.13.1.tgz", + "integrity": "sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.12.tgz", + "integrity": "sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.2.tgz", + "integrity": "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.2.tgz", + "integrity": "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.3.tgz", + "integrity": "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.2.tgz", + "integrity": "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.2.tgz", + "integrity": "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.3.44", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.44.tgz", + "integrity": "sha512-eZg6XzaCbVr2S5cAErU5eGBDaOVTuTo1I65i4tQcHENRcZ8rMWhQy1DaIYUSLyZjsfXvmCqZrstSMYyGFocvHA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.48", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.48.tgz", + "integrity": "sha512-FqOKTlqSaoV3nzO55pMs5NBnZX8EhoI0DGmn9kbYeXWppgHD6dchyuj2HLqp4INJDJbSrj6OFYJkAh/WhSzZPg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.4.13", + "@smithy/credential-provider-imds": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.3.3.tgz", + "integrity": "sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.2.tgz", + "integrity": "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.12.tgz", + "integrity": "sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.2.13", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.13.tgz", + "integrity": "sha512-qQQsIvL0MGIbUjeSrg0/VlQ3jGNKyM3/2iU3FPNgy01z+Sp4OvcaxbgIoFOTvB61ZoohtutuOvOcgmhbD0katQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.5.21", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.21.tgz", + "integrity": "sha512-KzSg+7KKywLnkoKejRtIBXDmwBfjGvg1U1i/etkC7XSWUyFCoLno1IohV2c74IzQqdhX5y3uE44r/8/wuK+A7Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/node-http-handler": "^4.5.1", + "@smithy/types": "^4.13.1", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.2.tgz", + "integrity": "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.2.tgz", + "integrity": "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/uuid": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.2.tgz", + "integrity": "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "license": "MIT" + }, + "node_modules/@types/mime-types": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", + "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.16.tgz", + "integrity": "sha512-K6csxIjY+9RoDxdP6/wzaJzXaCf4znBz0/y0rrQDsbqmzQ5QFsOjubbsYWZhj6ZCgz3mjlyDZS+EJkhA9jWl9Q==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "license": "MIT" + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "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/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "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/basic-ftp": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz", + "integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/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/cliui/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/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/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "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/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "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/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "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/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "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/fast-xml-builder": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", + "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.1.3" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.5.8", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.8.tgz", + "integrity": "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "fast-xml-builder": "^1.1.4", + "path-expression-matcher": "^1.2.0", + "strnum": "^2.2.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/file-type": { + "version": "21.3.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", + "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/gaxios": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz", + "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "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/get-east-asian-width": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-uri": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/get-uri/node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/google-auth-library": { + "version": "10.6.2", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz", + "integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.1.4", + "gcp-metadata": "8.1.2", + "google-logging-utils": "1.1.3", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "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/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/hosted-git-info": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "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/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "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/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/koffi": { + "version": "2.15.2", + "resolved": "https://registry.npmjs.org/koffi/-/koffi-2.15.2.tgz", + "integrity": "sha512-r9tjJLVRSOhCRWdVyQlF3/Ugzeg13jlzS4czS82MAgLff4W+BcYOW7g8Y62t9O5JYjYOLAjAovAZDNlDfZNu+g==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "funding": { + "url": "https://liberapay.com/Koromix" + } + }, + "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/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/marked": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/openai": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.26.0.tgz", + "integrity": "sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==", + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "license": "MIT" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "license": "MIT", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "license": "MIT" + }, + "node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", + "license": "MIT" + }, + "node_modules/path-expression-matcher": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.2.1.tgz", + "integrity": "sha512-d7gQQmLvAKXKXE2GeP9apIGbMYKz88zWdsn/BN2HRWVQsDFdUY36WSLTY0Jvd4HWi7Fb30gQ62oAOzdgJA6fZw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "license": "MIT" + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "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/proxy-agent": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "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/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "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/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "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", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "license": "MIT" + }, + "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/string-width/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/string-width/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": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strnum": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.2.tgz", + "integrity": "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/strtok3": { + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/token-types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "license": "MIT", + "dependencies": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "license": "MIT" + }, + "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/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.7.tgz", + "integrity": "sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "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/wrap-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/wrap-ansi/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/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/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/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..3692748 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "panopticon", + "version": "0.1.0", + "description": "Automated project documentation registry using pi SDK", + "type": "module", + "main": "dist/index.js", + "scripts": { + "build": "tsc", + "start": "node dist/index.js", + "dev": "tsc --watch" + }, + "dependencies": { + "@mariozechner/pi-coding-agent": "^0.57.0", + "@mariozechner/pi-ai": "^0.57.0" + }, + "devDependencies": { + "typescript": "^5.7.0", + "@types/node": "^22.0.0" + } +} diff --git a/prompts/orchestrator-full.md b/prompts/orchestrator-full.md new file mode 100644 index 0000000..1e6ca17 --- /dev/null +++ b/prompts/orchestrator-full.md @@ -0,0 +1,56 @@ +You are the Panopticon orchestrator performing a full analysis of a codebase. +Your job is to plan the documentation generation from scratch. + +## Input + +You will receive: +1. The complete file tree +2. AST summaries (key types, functions, impls per file) +3. An import/dependency graph +4. The project's CLAUDE.md if it exists +5. Git log and file churn data + +## Output + +Return a JSON object with this structure: + +```json +{ + "skipReason": null, + "updates": [ + { + "target": "structure.md", + "reason": "Full analysis — generating from scratch", + "relevantFiles": ["src/main.rs", "src/lib.rs", "src/rendering/mod.rs"], + "diffContext": "Focus on module boundaries, data flow, and key types" + }, + { + "target": "guide.md", + "reason": "Full analysis — discovering patterns from code", + "relevantFiles": ["CLAUDE.md", "src/main.rs"], + "diffContext": "Focus on coding conventions, architectural patterns, testing patterns" + }, + { + "target": "changelog.md", + "reason": "Full analysis — summarizing recent development", + "relevantFiles": [], + "diffContext": "Use git log and churn data to identify active areas" + } + ] +} +``` + +## Document Scopes (Workers Must Stay in Lane) + +- **structure.md**: Modules, types, data flow, entry points, dependencies. A factual MAP of the codebase. No conventions, no patterns, no "do this / don't do that". +- **guide.md**: Coding conventions, patterns, anti-patterns, testing, build workflow. A GUIDE for writing code. No module catalogs, no type listings, no architecture descriptions. +- **changelog.md**: Recent changes, active areas, stability, open threads. A LOG of what changed. No architecture descriptions, no conventions. + +## Rules + +- For full analysis, ALL three files should always be generated. +- For structure.md: identify the most important 15-20 files that define the module structure. Include entry points, mod.rs files, and key type definitions. +- For guide.md: include CLAUDE.md/AGENTS.md and representative source files that show coding patterns. +- For changelog.md: the worker will use git log data, no specific files needed. +- List relevant files in order of importance for each target. +- Return ONLY the JSON object. No other text. diff --git a/prompts/orchestrator-incremental.md b/prompts/orchestrator-incremental.md new file mode 100644 index 0000000..45bba1f --- /dev/null +++ b/prompts/orchestrator-incremental.md @@ -0,0 +1,47 @@ +You are the Panopticon orchestrator. Your job is to analyze what changed in a +project and decide which documentation sections need updating. + +## Input + +You will receive: +1. A diff summary (files changed, insertions, deletions) +2. A git log of commit messages since the last run +3. The current table of contents of each documentation file +4. The file tree of the project + +## Output + +Return a JSON object with this structure: + +```json +{ + "skipReason": null, + "updates": [ + { + "target": "structure.md", + "reason": "Why this file needs updating", + "relevantFiles": ["src/rendering/pipeline.rs", "src/rendering/dither.rs"], + "diffContext": "Key changes to communicate to the worker" + } + ] +} +``` + +`skipReason` should be null if updates are needed, or a string like "no-meaningful-changes" if the diff is trivial. + +`target` must be one of: "structure.md", "guide.md", "changelog.md" + +## Document Scopes + +- **structure.md**: Modules, types, data flow, entry points, dependencies. Update when module boundaries, key types, or data flow changed. +- **guide.md**: Coding conventions, patterns, anti-patterns, testing. Update when new patterns emerged or existing conventions changed. +- **changelog.md**: Recent changes, active areas, stability. ALWAYS update if there are any code changes. + +## Rules + +- If the diff is purely non-code (README, docs, CI config), set skipReason to "no-meaningful-changes". +- changelog.md should ALWAYS be updated if there are any code changes. +- structure.md only needs updating if module boundaries, key types, or data flow changed. +- guide.md only needs updating if new patterns emerged or existing conventions were violated/changed. +- Be conservative. Unnecessary updates waste tokens and risk doc drift. +- Return ONLY the JSON object. No other text. diff --git a/prompts/synthesizer.md b/prompts/synthesizer.md new file mode 100644 index 0000000..7e91ddb --- /dev/null +++ b/prompts/synthesizer.md @@ -0,0 +1,79 @@ +You are the Panopticon synthesizer. You combine documentation sections produced +by specialist workers into a coherent skill package. + +## Input + +You will receive updated documentation sections from workers and the current +SKILL.md entry point (if it exists). + +## Tasks + +1. Generate or update SKILL.md as a **pure table of contents** — it must NOT + repeat content from the sub-documents. It has a 2-3 sentence project summary, + quick reference block, and links. Nothing else. +2. Verify cross-references between documents are consistent: + - Types mentioned in structure.md should use the same names everywhere + - Patterns described in guide.md should reference real types from structure.md + - Active areas in changelog.md should reference real modules from structure.md +3. Check that documents stay in their lane: + - structure.md should NOT contain conventions, patterns, or "do this" advice + - guide.md should NOT catalog modules/types or describe data flow + - changelog.md should NOT describe architecture or conventions +4. Flag any contradictions, overlap, or scope violations you find. +5. Keep SKILL.md concise: description under 200 characters, body under 40 lines. + +## SKILL.md Format + +The SKILL.md must follow this exact structure: + +```markdown +--- +name: panopticon +description: >- + Auto-generated project overview for . Structure, conventions, + and recent activity. Updated nightly by Panopticon. +--- + +# — Project Overview + +<2-3 sentence summary of the project. What it is and what technologies it uses.> + +## Quick Reference + +- **Language:** +- **Key dependencies:** +- **Build:** `` +- **Test:** `` +- **Entry point:** `` + +## Documentation + +- [Structure](structure.md) — modules, types, data flow, dependencies +- [Guide](guide.md) — conventions, patterns, anti-patterns, testing +- [Changelog](changelog.md) — recent changes, active areas, stability +``` + +**IMPORTANT:** SKILL.md is ONLY a table of contents. Do NOT add "Architecture +Highlights", "Key Conventions", or any other sections that summarize the +sub-documents. The links are sufficient. + +## Output + +Return a JSON object: + +```json +{ + "skill_md": "the complete SKILL.md content", + "fixes": [ + { + "file": "structure.md", + "description": "Fixed reference to renamed type", + "before": "old text", + "after": "new text" + } + ], + "inconsistencies": ["description of any unresolvable issues"] +} +``` + +Return ONLY the JSON object. No other text. diff --git a/prompts/worker-changelog.md b/prompts/worker-changelog.md new file mode 100644 index 0000000..c5c8739 --- /dev/null +++ b/prompts/worker-changelog.md @@ -0,0 +1,69 @@ +You are generating a changelog / recent activity document for a codebase. You +have read access to the source code via the `read` and `bash` tools. + +## Your Scope — ONLY Recent Changes and Development Activity + +This document covers ONLY what has changed recently: +- Which areas of the codebase are actively being modified +- Semantic descriptions of recent changes +- Stability assessment (what hasn't changed) +- Partially complete work / open threads + +## What Does NOT Belong Here + +- **Module descriptions, type catalogs, dependency graphs** → structure.md +- **Coding conventions, patterns, anti-patterns** → guide.md +- **How the architecture works** → structure.md +- **How to write code** → guide.md + +If you find yourself describing what a module does or how the architecture works, +STOP — that belongs in structure.md. You may name modules/areas as locations of +changes, but do not describe their architecture. + +## Instructions + +1. Analyze the git log and file churn data provided. +2. Group changes by area/module, not chronologically. +3. Identify: + - **Active areas:** directories/modules with the most churn + - **Recent changes:** what changed semantically ("Added dithering pass" not "Modified pipeline.rs") + - **Stability assessment:** which parts haven't changed in 30+ days + - **Open threads:** partially complete work based on recent commits +4. Target {min_lines}-{max_lines} lines. Updated every run — older entries age out. + +## Format + +``` +# Changelog + +*Last updated: * + +## Active Areas +| Area | Changes (30d) | Description | +... + +## Recent Changes +### +- +... + +## Stability +| Area | Last Changed | Status | +... + +## Open Threads +- +``` + +## Writing Rules + +- Use semantic descriptions, not commit messages +- Group by area, not by date +- Be specific about what changed and why it matters +- Mark areas as "active", "stable", or "in flux" +- You may use `bash` to run `git log` commands for more detail + +## Output + +Return ONLY the markdown document. No preamble, no commentary, no "here is the +document" or "let me create" — start directly with `# Changelog`. diff --git a/prompts/worker-guide.md b/prompts/worker-guide.md new file mode 100644 index 0000000..33c7b88 --- /dev/null +++ b/prompts/worker-guide.md @@ -0,0 +1,75 @@ +You are generating a development guide for a codebase. You have read access +to the source code via the `read` and `bash` tools. + +## Your Scope — ONLY Conventions, Patterns, and How-To + +This document covers ONLY how to write code in this project: +- Naming conventions +- Formatting rules +- Architectural patterns (with code examples) +- Anti-patterns to avoid (with code examples) +- Testing conventions +- Build and development workflow + +## What Does NOT Belong Here + +- **Module listings, type catalogs, dependency graphs** → these go in structure.md +- **Data flow descriptions** → structure.md +- **Entry points, initialization order** → structure.md +- **What changed recently** → changelog.md +- **Describing what each module does** → structure.md + +If you find yourself listing all the modules or types or describing what each +system does, STOP — that belongs in structure.md, not here. + +You may REFERENCE specific types or modules as examples of a pattern, but do not +catalog them. + +## Instructions + +1. If a CLAUDE.md or AGENTS.md exists, read it first — these contain authoritative + project rules. Summarize and reference them, don't duplicate verbatim. +2. Read representative source files to discover recurring patterns. +3. For each pattern, show a concrete code example from the actual codebase. +4. For each anti-pattern, show what to avoid and why. +5. Target {min_lines}-{max_lines} lines. Dense, prescriptive, no fluff. + +## Format + +``` +# Guide + +## Conventions +### Naming +### Formatting +### Imports + +## Patterns +### + + +## Anti-Patterns +### + + +## Testing +### Structure +### What to Test +### Running Tests + +## References +- See CLAUDE.md for authoritative project rules +``` + +## Writing Rules + +- Be prescriptive: "Do X" not "X is sometimes done" +- Give concrete code examples for each pattern +- Explain WHY a pattern exists, not just WHAT it is +- Reference specific types/modules as examples, don't catalog them +- Write for an LLM reader that will be writing code in this project + +## Output + +Return ONLY the markdown document. No preamble, no commentary, no "here is the +document" or "let me create" — start directly with `# Guide`. diff --git a/prompts/worker-structure.md b/prompts/worker-structure.md new file mode 100644 index 0000000..c111539 --- /dev/null +++ b/prompts/worker-structure.md @@ -0,0 +1,72 @@ +You are generating a structure document for a codebase. You have read access +to the source code via the `read` and `bash` tools. + +## Your Scope — ONLY Factual Structure + +This document covers ONLY the physical and logical layout of the code: +- Modules and what they contain +- Types and their fields +- Data flow through the system +- Entry points and initialization order +- Dependency relationships between modules + +## What Does NOT Belong Here + +- **Coding conventions, style rules, naming rules** → these go in guide.md +- **Patterns, anti-patterns, best practices** → these go in guide.md +- **How to write code in this project** → guide.md +- **What changed recently** → changelog.md +- **Code examples showing "do this / don't do this"** → guide.md + +If you find yourself writing "Do X" or "Avoid Y" or showing good/bad examples, +STOP — that belongs in guide.md, not here. + +## Instructions + +1. Read key files to understand the actual structure. Start with entry points + and work outward. +2. Identify the natural module boundaries from the code structure. +3. For each module/area, describe: + - What it does (1-2 sentences of factual description) + - Key types it defines + - What it depends on and what depends on it +4. Describe the main data flow through the system. +5. List the 10-20 most important types with one-sentence descriptions. +6. Identify entry points and initialization order. +7. Target {min_lines}-{max_lines} lines. Dense, precise, no filler. + +## Format + +``` +# Structure + +## Modules +### +... + +## Data Flow +... + +## Key Types +| Type | Location | Description | +... + +## Entry Points +... + +## Dependencies +... +``` + +## Writing Rules + +- Be precise about names: exact function names, type names, file paths +- State relationships explicitly: "X calls Y", "A depends on B" +- Avoid vague language: "various", "several", "etc." +- This is a **map**, not a **guide** — describe what IS, not what SHOULD BE +- Write for an LLM reader, not a human + +## Output + +Return ONLY the markdown document. No preamble, no commentary, no "here is the +document" or "let me create" — start directly with `# Structure`. diff --git a/prompts/worker-update.md b/prompts/worker-update.md new file mode 100644 index 0000000..4cea6b5 --- /dev/null +++ b/prompts/worker-update.md @@ -0,0 +1,20 @@ +You are updating a section of project documentation. You have read access to the +project's source code via the `read` and `bash` tools. + +## Instructions + +1. Read the relevant source files to understand the changes in context. +2. Follow imports if needed to understand how changes connect to the broader codebase. +3. Update ONLY the sections affected by the changes. Do not rewrite unchanged sections. +4. Preserve the existing structure and heading hierarchy unless it no longer fits. +5. Keep the document between {min_lines} and {max_lines} lines. +6. Write for an LLM reader, not a human: + - Be precise about names (exact function names, type names, file paths) + - State relationships explicitly ("X calls Y", "A depends on B") + - Avoid vague language ("various", "several", "etc.") + - Include concrete examples over abstract descriptions + +## Output + +Return the complete updated markdown file. Do not wrap in code fences. Return +only the document content. diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..d8194d2 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,67 @@ +import { readFileSync, existsSync } from "fs"; +import { resolve } from "path"; +import type { Config, ProjectConfig, ModelsConfig, ThinkingConfig, MetricsConfig, LimitsConfig } from "./types.js"; + +const DEFAULT_LIMITS: LimitsConfig = { + maxWorkerConcurrency: 4, + maxDiffSizeBytes: 200000, + maxFilesPerWorkUnit: 15, + workerTimeoutSeconds: 120, + synthesizerTimeoutSeconds: 180, +}; + +const DEFAULT_METRICS: MetricsConfig = { + enabled: true, + victoriaMetricsUrl: "http://localhost:8428", + jobLabel: "panopticon", +}; + +const DEFAULT_MODELS: ModelsConfig = { + orchestrator: "anthropic/claude-sonnet-4-5", + worker: "anthropic/claude-haiku-4-5", + synthesizer: "anthropic/claude-sonnet-4-5", +}; + +const DEFAULT_THINKING: ThinkingConfig = { + orchestrator: "medium", + worker: "off", + synthesizer: "low", +}; + +export function loadConfig(configPath?: string): Config { + const resolvedPath = configPath ?? resolve(process.cwd(), "config.json"); + + if (!existsSync(resolvedPath)) { + throw new Error(`Config file not found: ${resolvedPath}`); + } + + const raw = JSON.parse(readFileSync(resolvedPath, "utf-8")); + + if (!raw.projects || !Array.isArray(raw.projects) || raw.projects.length === 0) { + throw new Error("Config must have at least one project in 'projects' array"); + } + + for (const p of raw.projects) { + validateProject(p); + } + + return { + projects: raw.projects as ProjectConfig[], + models: { ...DEFAULT_MODELS, ...raw.models }, + thinkingLevels: { ...DEFAULT_THINKING, ...raw.thinkingLevels }, + metrics: { ...DEFAULT_METRICS, ...raw.metrics }, + limits: { ...DEFAULT_LIMITS, ...raw.limits }, + stateDir: raw.stateDir ?? "./state", + runsDir: raw.runsDir ?? "./runs", + }; +} + +function validateProject(p: any): asserts p is ProjectConfig { + if (!p.name || typeof p.name !== "string") throw new Error("Project must have a 'name' string"); + if (!p.path || typeof p.path !== "string") throw new Error(`Project '${p.name}' must have a 'path' string`); + if (!p.language || typeof p.language !== "string") throw new Error(`Project '${p.name}' must have a 'language' string`); + if (!existsSync(p.path)) throw new Error(`Project '${p.name}' path does not exist: ${p.path}`); + p.sourceGlobs = p.sourceGlobs ?? ["src/**/*"]; + p.excludeGlobs = p.excludeGlobs ?? []; + p.branch = p.branch ?? "main"; +} diff --git a/src/git.ts b/src/git.ts new file mode 100644 index 0000000..f013866 --- /dev/null +++ b/src/git.ts @@ -0,0 +1,149 @@ +import { execSync } from "child_process"; +import type { ProjectConfig, GitDiffResult, GitChurnEntry } from "./types.js"; + +function git(projectPath: string, args: string): string { + try { + return execSync(`git ${args}`, { + cwd: projectPath, + encoding: "utf-8", + maxBuffer: 10 * 1024 * 1024, + timeout: 30000, + }).trim(); + } catch (err: any) { + throw new Error(`Git command failed in ${projectPath}: git ${args}\n${err.stderr || err.message}`); + } +} + +export function getCurrentSha(projectPath: string): string { + return git(projectPath, "rev-parse HEAD"); +} + +export function pull(projectPath: string, branch: string): void { + git(projectPath, `pull origin ${branch} --ff-only`); +} + +export function getFileTree(projectPath: string, config: ProjectConfig): string[] { + const allFiles = git(projectPath, "ls-files").split("\n").filter(Boolean); + + // Filter by source globs (simple glob matching) + const filtered = allFiles.filter((file) => { + // Check exclusions first + for (const exc of config.excludeGlobs) { + if (matchGlob(file, exc)) return false; + } + // Check inclusions + for (const inc of config.sourceGlobs) { + if (matchGlob(file, inc)) return true; + } + return false; + }); + + return filtered.sort(); +} + +export function getDiffSince(projectPath: string, lastSha: string, config: ProjectConfig): GitDiffResult { + const currentSha = getCurrentSha(projectPath); + + // Diff stat + const diffStat = git(projectPath, `diff ${lastSha}..${currentSha} --stat`); + + // Full diff (for source files only), truncated + const sourceExtArgs = getSourceExtArgs(config); + let diffContent: string; + try { + diffContent = git(projectPath, `diff ${lastSha}..${currentSha} ${sourceExtArgs}`); + // Truncate if too large + if (Buffer.byteLength(diffContent) > config.excludeGlobs.length) { + // Use maxDiffSizeBytes from caller if needed; for now truncate at 200KB + const maxBytes = 200000; + if (Buffer.byteLength(diffContent) > maxBytes) { + diffContent = diffContent.slice(0, maxBytes) + "\n... [truncated]"; + } + } + } catch { + diffContent = "[diff too large or unavailable]"; + } + + // Commit log + const commitLog = git(projectPath, `log ${lastSha}..${currentSha} --oneline`); + + // Files changed + const filesChanged = git(projectPath, `diff ${lastSha}..${currentSha} --name-only`) + .split("\n") + .filter(Boolean); + + // Stats + let insertions = 0; + let deletions = 0; + try { + const numstat = git(projectPath, `diff ${lastSha}..${currentSha} --numstat`); + for (const line of numstat.split("\n")) { + const parts = line.split("\t"); + if (parts.length >= 2) { + const ins = parseInt(parts[0], 10); + const del = parseInt(parts[1], 10); + if (!isNaN(ins)) insertions += ins; + if (!isNaN(del)) deletions += del; + } + } + } catch { /* ignore */ } + + const commitCount = commitLog ? commitLog.split("\n").length : 0; + + return { diffStat, diffContent, commitLog, filesChanged, insertions, deletions, commitCount }; +} + +export function getGitLog(projectPath: string, days: number): string { + try { + return git(projectPath, `log --oneline --since="${days} days ago"`); + } catch { + return ""; + } +} + +export function getFileChurn(projectPath: string, days: number): GitChurnEntry[] { + try { + const raw = git(projectPath, `log --since="${days} days ago" --pretty=format: --name-only`); + const counts = new Map(); + for (const line of raw.split("\n")) { + const file = line.trim(); + if (file) { + counts.set(file, (counts.get(file) ?? 0) + 1); + } + } + return Array.from(counts.entries()) + .map(([file, count]) => ({ file, count })) + .sort((a, b) => b.count - a.count); + } catch { + return []; + } +} + +export function getDirstat(projectPath: string, lastSha: string): string { + try { + return git(projectPath, `diff ${lastSha}..HEAD --dirstat`); + } catch { + return ""; + } +} + +// Simple glob matching (supports ** and *) +function matchGlob(path: string, glob: string): boolean { + const regex = glob + .replace(/\./g, "\\.") + .replace(/\*\*/g, "{{DOUBLESTAR}}") + .replace(/\*/g, "[^/]*") + .replace(/\{\{DOUBLESTAR\}\}/g, ".*"); + return new RegExp(`^${regex}$`).test(path); +} + +function getSourceExtArgs(config: ProjectConfig): string { + // Build -- '*.ext' args from source globs for git diff filtering + const exts = new Set(); + for (const glob of config.sourceGlobs) { + const match = glob.match(/\*\.(\w+)$/); + if (match) exts.add(match[1]); + } + if (exts.size === 0) return ""; + return "-- " + Array.from(exts).map((e) => `'*.${e}'`).join(" "); +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..58f8e71 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,584 @@ +#!/usr/bin/env node + +/** + * Panopticon — Automated Project Documentation Registry + * + * Usage: + * node dist/index.js # Incremental update (all projects) + * node dist/index.js --full-analysis # Full analysis for one project + * node dist/index.js --project # Incremental update for one project + */ + +import { resolve } from "path"; +import { loadConfig } from "./config.js"; +import { getCurrentSha, pull, getFileTree, getDiffSince } from "./git.js"; +import { gatherStructuralContext, hashFileAtPath } from "./structural.js"; +import { loadState, saveState } from "./state.js"; +import { readAllSkillFiles, readSkillFile, writeSkillFiles, countLinesChanged, cleanSkillDir } from "./writer.js"; +import { runIncrementalOrchestrator, runFullOrchestrator } from "./orchestrator.js"; +import { runIncrementalWorker, runFullAnalysisWorker, runWorkersConcurrently } from "./worker.js"; +import { runSynthesizer } from "./synthesizer.js"; +import { pushMetrics, buildProjectMetrics } from "./metrics.js"; +import { writeProjectReport, writeNightlyReport, detectAnomalies } from "./reporter.js"; +import type { + Config, + ProjectConfig, + ProjectRunReport, + NightlyReport, + OrchestratorResult, + WorkerResult, + PhaseTimings, + SessionMetrics, +} from "./types.js"; +import { join } from "path"; + +// ── CLI Parsing ── + +interface CliArgs { + fullAnalysis: string | null; + project: string | null; + configPath: string | null; + dryRun: boolean; +} + +function parseArgs(): CliArgs { + const args = process.argv.slice(2); + const result: CliArgs = { + fullAnalysis: null, + project: null, + configPath: null, + dryRun: false, + }; + + for (let i = 0; i < args.length; i++) { + switch (args[i]) { + case "--full-analysis": + result.fullAnalysis = args[++i] ?? null; + break; + case "--project": + result.project = args[++i] ?? null; + break; + case "--config": + result.configPath = args[++i] ?? null; + break; + case "--dry-run": + result.dryRun = true; + break; + } + } + + return result; +} + +// ── Main Pipeline ── + +async function main() { + const cliArgs = parseArgs(); + const config = loadConfig(cliArgs.configPath ?? undefined); + const date = new Date().toISOString().split("T")[0]; + + console.log(`Panopticon run: ${date}`); + console.log(`Projects configured: ${config.projects.map((p) => p.name).join(", ")}`); + + if (cliArgs.fullAnalysis) { + // Full analysis mode + const project = config.projects.find((p) => p.name === cliArgs.fullAnalysis); + if (!project) { + console.error(`Project not found: ${cliArgs.fullAnalysis}`); + console.error(`Available: ${config.projects.map((p) => p.name).join(", ")}`); + process.exit(1); + } + + console.log(`\nRunning full analysis for: ${project.name}`); + const report = await runFullAnalysis(project, config, cliArgs.dryRun); + writeProjectReport(resolve(config.runsDir), date, report); + + if (config.metrics.enabled) { + const metrics = buildProjectMetrics(report, config.metrics.jobLabel); + await pushMetrics(config.metrics, metrics); + } + + console.log(`\nFull analysis complete. Status: ${report.status}`); + return; + } + + // Incremental mode (nightly) + const projectsToRun = cliArgs.project + ? config.projects.filter((p) => p.name === cliArgs.project) + : config.projects; + + if (projectsToRun.length === 0) { + console.error(`No matching projects found`); + process.exit(1); + } + + const nightlyStart = Date.now(); + const projectReports: ProjectRunReport[] = []; + + for (const project of projectsToRun) { + console.log(`\nProcessing: ${project.name}`); + const report = await runIncremental(project, config, cliArgs.dryRun); + projectReports.push(report); + console.log(` Status: ${report.status} (${(report.duration / 1000).toFixed(1)}s)`); + } + + // Nightly report + const anomalies = detectAnomalies(projectReports); + const nightlyReport: NightlyReport = { + date, + projectReports, + totalDuration: Date.now() - nightlyStart, + totalCost: projectReports.reduce((sum, r) => sum + r.estimatedCost, 0), + anomalies, + }; + + writeNightlyReport(resolve(config.runsDir), date, nightlyReport); + + // Push metrics for all projects + if (config.metrics.enabled) { + for (const report of projectReports) { + const metrics = buildProjectMetrics(report, config.metrics.jobLabel); + await pushMetrics(config.metrics, metrics); + } + } + + console.log(`\nNightly run complete.`); + console.log(` Duration: ${(nightlyReport.totalDuration / 1000).toFixed(1)}s`); + console.log(` Cost: ~$${nightlyReport.totalCost.toFixed(3)}`); + if (anomalies.length > 0) { + console.log(` Anomalies: ${anomalies.length}`); + for (const a of anomalies) console.log(` - ${a}`); + } +} + +// ── Full Analysis Pipeline ── + +async function runFullAnalysis( + project: ProjectConfig, + config: Config, + dryRun: boolean +): Promise { + const runStart = Date.now(); + const timings: PhaseTimings = { git: 0, structural: 0, orchestrator: 0, workers: 0, synthesizer: 0 }; + const errors: string[] = []; + const stateDir = resolve(config.stateDir); + + // Phase: Git + let gitStart = Date.now(); + let currentSha: string; + try { + currentSha = getCurrentSha(project.path); + } catch (err: any) { + errors.push(`Git error: ${err.message}`); + return makeFailedReport(project.name, 0, errors, timings, Date.now() - runStart); + } + timings.git = Date.now() - gitStart; + + // Phase: Structural extraction + let structStart = Date.now(); + const structural = gatherStructuralContext(project.path, project); + timings.structural = Date.now() - structStart; + + console.log(` Files: ${structural.fileTree.length}, AST entries: ${structural.astSummaries.length}`); + + // Phase: Orchestrator + let orchStart = Date.now(); + const { result: orchResult, metrics: orchMetrics } = await runFullOrchestrator( + project.path, + config, + structural + ); + timings.orchestrator = Date.now() - orchStart; + errors.push(...orchMetrics.errors); + + console.log(` Orchestrator: ${orchResult.updates.length} work units planned`); + + // Phase: Workers + let workerStart = Date.now(); + const workerTasks = orchResult.updates.map((workUnit) => { + return () => + runFullAnalysisWorker( + project.path, + config, + workUnit.target as "structure.md" | "guide.md" | "changelog.md", + structural, + workUnit + ); + }); + + const workerResults = await runWorkersConcurrently(workerTasks, config.limits.maxWorkerConcurrency); + timings.workers = Date.now() - workerStart; + + for (const w of workerResults) { + console.log(` Worker ${w.target}: ${w.status}${w.error ? ` (${w.error})` : ""}`); + if (w.metrics.errors.length > 0) errors.push(...w.metrics.errors); + } + + // Phase: Synthesizer + let synthStart = Date.now(); + const currentSkillMd = readSkillFile(project.path, "SKILL.md"); + const { result: synthResult, metrics: synthMetrics } = await runSynthesizer( + project.path, + config, + project.name, + workerResults, + currentSkillMd + ); + timings.synthesizer = Date.now() - synthStart; + errors.push(...synthMetrics.errors); + + // Write outputs + if (!dryRun) { + const existingDocs = readAllSkillFiles(project.path); + + // Wipe all generated .md files (except SKILL.md) before writing fresh ones. + // This removes stale documents from previous runs (e.g. after file renames). + const removed = cleanSkillDir(project.path); + if (removed.length > 0) { + console.log(` Cleaned stale docs: ${removed.join(", ")}`); + } + + const filesToWrite: Record = {}; + + filesToWrite["SKILL.md"] = synthResult.skillMd; + for (const w of workerResults) { + if (w.status === "success") { + filesToWrite[w.target] = w.content; + } + } + + // Apply synthesizer fixes + for (const fix of synthResult.fixes) { + if (filesToWrite[fix.file] && fix.before && fix.after) { + filesToWrite[fix.file] = filesToWrite[fix.file].replace(fix.before, fix.after); + } + } + + writeSkillFiles(project.path, filesToWrite); + + // Update state + const fileHashes: Record = {}; + for (const file of structural.fileTree) { + try { + fileHashes[file] = hashFileAtPath(join(project.path, file)); + } catch { /* skip */ } + } + + saveState(stateDir, project.name, { + lastSha: currentSha, + lastRunTimestamp: new Date().toISOString(), + lastRunStatus: errors.length === 0 ? "success" : "partial", + fileHashes, + docVersions: Object.fromEntries( + Object.entries(filesToWrite).map(([k, v]) => [k, String(v.length)]) + ), + }); + + // Doc line changes + const docLinesChanged: Record = {}; + for (const [file, content] of Object.entries(filesToWrite)) { + docLinesChanged[file] = countLinesChanged(existingDocs[file] ?? null, content); + } + + const totalTokensIn = + orchMetrics.tokensIn + synthMetrics.tokensIn + workerResults.reduce((s, w) => s + w.metrics.tokensIn, 0); + const totalTokensOut = + orchMetrics.tokensOut + synthMetrics.tokensOut + workerResults.reduce((s, w) => s + w.metrics.tokensOut, 0); + + return { + project: project.name, + date: new Date().toISOString(), + status: errors.length === 0 ? "success" : "partial", + duration: Date.now() - runStart, + commitCount: 0, + filesChanged: structural.fileTree.length, + insertions: 0, + deletions: 0, + orchestratorDecision: orchResult, + workerResults, + synthesizerStatus: synthMetrics.errors.length === 0 ? "success" : "failure", + docLinesChanged, + phaseTimings: timings, + totalTokensIn, + totalTokensOut, + estimatedCost: estimateCost(totalTokensIn, totalTokensOut), + errors, + }; + } + + // Dry run + return makeFailedReport(project.name, 0, ["dry-run"], timings, Date.now() - runStart); +} + +// ── Incremental Pipeline ── + +async function runIncremental( + project: ProjectConfig, + config: Config, + dryRun: boolean +): Promise { + const runStart = Date.now(); + const timings: PhaseTimings = { git: 0, structural: 0, orchestrator: 0, workers: 0, synthesizer: 0 }; + const errors: string[] = []; + const stateDir = resolve(config.stateDir); + + // Load state + const state = loadState(stateDir, project.name); + + // Phase: Git + let gitStart = Date.now(); + let currentSha: string; + try { + // Try to pull (non-fatal if fails for local repos) + try { pull(project.path, project.branch); } catch { /* local repo, no remote */ } + currentSha = getCurrentSha(project.path); + } catch (err: any) { + errors.push(`Git error: ${err.message}`); + timings.git = Date.now() - gitStart; + return makeFailedReport(project.name, 0, errors, timings, Date.now() - runStart); + } + timings.git = Date.now() - gitStart; + + // Check if there are changes + if (state.lastSha && state.lastSha === currentSha) { + console.log(` No changes since last run (${currentSha.slice(0, 8)})`); + return makeSkippedReport(project.name, timings, Date.now() - runStart); + } + + // If no previous state, recommend full analysis + if (!state.lastSha) { + console.log(` No previous state — running full analysis instead`); + return runFullAnalysis(project, config, dryRun); + } + + // Get diff + let diff; + try { + diff = getDiffSince(project.path, state.lastSha, project); + } catch (err: any) { + errors.push(`Diff error: ${err.message}`); + // Fall back to full analysis + console.log(` Diff failed, falling back to full analysis`); + return runFullAnalysis(project, config, dryRun); + } + + if (diff.filesChanged.length === 0) { + console.log(` No file changes detected`); + return makeSkippedReport(project.name, timings, Date.now() - runStart); + } + + console.log(` Changes: ${diff.commitCount} commits, ${diff.filesChanged.length} files (+${diff.insertions}/-${diff.deletions})`); + + // Phase: Structural (lightweight for incremental) + let structStart = Date.now(); + const fileTree = getFileTree(project.path, project); + timings.structural = Date.now() - structStart; + + // Phase: Orchestrator + let orchStart = Date.now(); + const existingDocs = readAllSkillFiles(project.path); + const { result: orchResult, metrics: orchMetrics } = await runIncrementalOrchestrator( + project.path, + config, + diff, + existingDocs, + fileTree + ); + timings.orchestrator = Date.now() - orchStart; + errors.push(...orchMetrics.errors); + + if (orchResult.skipReason) { + console.log(` Orchestrator: skip (${orchResult.skipReason})`); + // Still update state + if (!dryRun) { + saveState(stateDir, project.name, { + ...state, + lastSha: currentSha, + lastRunTimestamp: new Date().toISOString(), + lastRunStatus: "success", + }); + } + return makeSkippedReport(project.name, timings, Date.now() - runStart); + } + + console.log(` Orchestrator: ${orchResult.updates.map((u) => u.target).join(", ")}`); + + // Phase: Workers + let workerStart = Date.now(); + const workerTasks = orchResult.updates.map((workUnit) => { + return () => + runIncrementalWorker( + project.path, + config, + workUnit, + existingDocs[workUnit.target] ?? null, + diff.diffContent + ); + }); + + const workerResults = await runWorkersConcurrently(workerTasks, config.limits.maxWorkerConcurrency); + timings.workers = Date.now() - workerStart; + + for (const w of workerResults) { + console.log(` Worker ${w.target}: ${w.status}${w.error ? ` (${w.error})` : ""}`); + if (w.metrics.errors.length > 0) errors.push(...w.metrics.errors); + } + + // Phase: Synthesizer + let synthStart = Date.now(); + const currentSkillMd = readSkillFile(project.path, "SKILL.md"); + const successfulWorkers = workerResults.filter((w) => w.status === "success"); + + let synthResult; + let synthMetrics: SessionMetrics = { tokensIn: 0, tokensOut: 0, toolCalls: 0, errors: [], durationMs: 0 }; + + if (successfulWorkers.length > 0) { + const synthResponse = await runSynthesizer( + project.path, + config, + project.name, + workerResults, + currentSkillMd + ); + synthResult = synthResponse.result; + synthMetrics = synthResponse.metrics; + errors.push(...synthMetrics.errors); + } + timings.synthesizer = Date.now() - synthStart; + + // Write outputs + if (!dryRun && successfulWorkers.length > 0) { + const filesToWrite: Record = {}; + + if (synthResult) { + filesToWrite["SKILL.md"] = synthResult.skillMd; + // Apply fixes + for (const w of workerResults) { + if (w.status === "success") { + filesToWrite[w.target] = w.content; + } + } + for (const fix of synthResult.fixes) { + if (filesToWrite[fix.file] && fix.before && fix.after) { + filesToWrite[fix.file] = filesToWrite[fix.file].replace(fix.before, fix.after); + } + } + } else { + // No synthesizer — write worker outputs directly + for (const w of workerResults) { + if (w.status === "success") { + filesToWrite[w.target] = w.content; + } + } + } + + writeSkillFiles(project.path, filesToWrite); + + // Update state + saveState(stateDir, project.name, { + ...state, + lastSha: currentSha, + lastRunTimestamp: new Date().toISOString(), + lastRunStatus: errors.length === 0 ? "success" : "partial", + }); + + // Doc line changes + const docLinesChanged: Record = {}; + for (const [file, content] of Object.entries(filesToWrite)) { + docLinesChanged[file] = countLinesChanged(existingDocs[file] ?? null, content); + } + + const totalTokensIn = + orchMetrics.tokensIn + synthMetrics.tokensIn + workerResults.reduce((s, w) => s + w.metrics.tokensIn, 0); + const totalTokensOut = + orchMetrics.tokensOut + synthMetrics.tokensOut + workerResults.reduce((s, w) => s + w.metrics.tokensOut, 0); + + return { + project: project.name, + date: new Date().toISOString(), + status: workerResults.some((w) => w.status === "failure") ? "partial" : "success", + duration: Date.now() - runStart, + commitCount: diff.commitCount, + filesChanged: diff.filesChanged.length, + insertions: diff.insertions, + deletions: diff.deletions, + orchestratorDecision: orchResult, + workerResults, + synthesizerStatus: synthMetrics.errors.length === 0 ? "success" : "failure", + docLinesChanged, + phaseTimings: timings, + totalTokensIn, + totalTokensOut, + estimatedCost: estimateCost(totalTokensIn, totalTokensOut), + errors, + }; + } + + return makeFailedReport(project.name, diff.commitCount, errors, timings, Date.now() - runStart); +} + +// ── Helpers ── + +function estimateCost(tokensIn: number, tokensOut: number): number { + // Rough estimate based on Anthropic pricing + // Sonnet: $3/Minput, $15/Moutput; Haiku: $0.80/Minput, $4/Moutput + // Average it out roughly + return (tokensIn * 2.0 + tokensOut * 10.0) / 1_000_000; +} + +function makeSkippedReport(project: string, timings: PhaseTimings, duration: number): ProjectRunReport { + return { + project, + date: new Date().toISOString(), + status: "skipped", + duration, + commitCount: 0, + filesChanged: 0, + insertions: 0, + deletions: 0, + orchestratorDecision: null, + workerResults: [], + synthesizerStatus: "skipped", + docLinesChanged: {}, + phaseTimings: timings, + totalTokensIn: 0, + totalTokensOut: 0, + estimatedCost: 0, + errors: [], + }; +} + +function makeFailedReport( + project: string, + commitCount: number, + errors: string[], + timings: PhaseTimings, + duration: number +): ProjectRunReport { + return { + project, + date: new Date().toISOString(), + status: "failure", + duration, + commitCount, + filesChanged: 0, + insertions: 0, + deletions: 0, + orchestratorDecision: null, + workerResults: [], + synthesizerStatus: "skipped", + docLinesChanged: {}, + phaseTimings: timings, + totalTokensIn: 0, + totalTokensOut: 0, + estimatedCost: 0, + errors, + }; +} + +// ── Entry Point ── + +main().catch((err) => { + console.error("Fatal error:", err); + process.exit(1); +}); diff --git a/src/metrics.ts b/src/metrics.ts new file mode 100644 index 0000000..81f1e3d --- /dev/null +++ b/src/metrics.ts @@ -0,0 +1,70 @@ +import type { MetricLine, MetricsConfig, ProjectRunReport } from "./types.js"; + +function formatLabels(labels: Record): string { + return Object.entries(labels) + .map(([k, v]) => `${k}="${v}"`) + .join(","); +} + +export async function pushMetrics(config: MetricsConfig, metrics: MetricLine[]): Promise { + if (!config.enabled) return; + + const body = metrics + .map((m) => { + const ts = m.timestamp ?? Date.now(); + return `${m.name}{${formatLabels(m.labels)}} ${m.value} ${ts}`; + }) + .join("\n"); + + try { + const response = await fetch(`${config.victoriaMetricsUrl}/api/v1/import/prometheus`, { + method: "POST", + headers: { "Content-Type": "text/plain" }, + body, + }); + if (!response.ok) { + console.error(`Metrics push failed: ${response.status} ${response.statusText}`); + } + } catch (err: any) { + console.error(`Metrics push error: ${err.message}`); + } +} + +export function buildProjectMetrics(report: ProjectRunReport, jobLabel: string): MetricLine[] { + const labels = { project: report.project, job: jobLabel }; + const ts = Date.now(); + const metrics: MetricLine[] = []; + + const add = (name: string, value: number, extraLabels: Record = {}) => { + metrics.push({ name, labels: { ...labels, ...extraLabels }, value, timestamp: ts }); + }; + + add("panopticon_run_status", report.status === "success" ? 1 : 0); + add("panopticon_run_duration_seconds", report.duration / 1000); + add("panopticon_run_skipped", report.status === "skipped" ? 1 : 0); + add("panopticon_files_changed", report.filesChanged); + add("panopticon_commits_since_last", report.commitCount); + + // Phase timings + for (const [phase, duration] of Object.entries(report.phaseTimings)) { + add("panopticon_phase_duration_seconds", duration / 1000, { phase }); + } + + // Token usage + add("panopticon_tokens_total", report.totalTokensIn, { direction: "input" }); + add("panopticon_tokens_total", report.totalTokensOut, { direction: "output" }); + + // Cost estimate + add("panopticon_estimated_cost_usd", report.estimatedCost); + + // Doc changes + for (const [file, lines] of Object.entries(report.docLinesChanged)) { + add("panopticon_doc_lines_changed", lines, { file }); + } + + // Errors + add("panopticon_errors_total", report.errors.length); + add("panopticon_worker_failures", report.workerResults.filter((w) => w.status === "failure").length); + + return metrics; +} diff --git a/src/orchestrator.ts b/src/orchestrator.ts new file mode 100644 index 0000000..451be5e --- /dev/null +++ b/src/orchestrator.ts @@ -0,0 +1,212 @@ +import { readFileSync } from "fs"; +import { join, dirname } from "path"; +import { fileURLToPath } from "url"; +import { createSession, promptWithTimeout } from "./session.js"; +import type { Config, OrchestratorResult, GitDiffResult, StructuralContext, SessionMetrics } from "./types.js"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const PROMPTS_DIR = join(__dirname, "..", "prompts"); + +function loadPrompt(name: string): string { + return readFileSync(join(PROMPTS_DIR, name), "utf-8"); +} + +/** + * Run the orchestrator for an incremental update. + * Analyzes the diff and decides which doc files need updating. + */ +export async function runIncrementalOrchestrator( + projectPath: string, + config: Config, + diff: GitDiffResult, + existingDocs: Record, + fileTree: string[] +): Promise<{ result: OrchestratorResult; metrics: SessionMetrics }> { + const systemPrompt = loadPrompt("orchestrator-incremental.md"); + + const { session, metrics } = await createSession({ + role: "orchestrator", + projectPath, + config, + systemPrompt, + }); + + const startTime = Date.now(); + + // Build context for the orchestrator + const context = buildIncrementalContext(diff, existingDocs, fileTree); + + try { + const response = await promptWithTimeout( + session, + context, + config.limits.workerTimeoutSeconds * 1000 + ); + + metrics.durationMs = Date.now() - startTime; + + // Parse JSON from response + const result = parseOrchestratorResponse(response); + return { result, metrics }; + } catch (err: any) { + metrics.durationMs = Date.now() - startTime; + metrics.errors.push(`orchestrator: ${err.message}`); + + // Fallback: update all files + console.error(`Orchestrator failed, falling back to full update: ${err.message}`); + return { + result: { + skipReason: null, + updates: [ + { target: "structure.md", reason: "Orchestrator fallback", relevantFiles: [], diffContext: diff.diffStat }, + { target: "guide.md", reason: "Orchestrator fallback", relevantFiles: [], diffContext: diff.diffStat }, + { target: "changelog.md", reason: "Orchestrator fallback", relevantFiles: [], diffContext: diff.commitLog }, + ], + }, + metrics, + }; + } +} + +/** + * Run the orchestrator for a full analysis. + * Plans documentation structure from scratch. + */ +export async function runFullOrchestrator( + projectPath: string, + config: Config, + structural: StructuralContext +): Promise<{ result: OrchestratorResult; metrics: SessionMetrics }> { + const systemPrompt = loadPrompt("orchestrator-full.md"); + + const { session, metrics } = await createSession({ + role: "orchestrator", + projectPath, + config, + systemPrompt, + }); + + const startTime = Date.now(); + const context = buildFullContext(structural); + + try { + const response = await promptWithTimeout( + session, + context, + config.limits.workerTimeoutSeconds * 1000 + ); + + metrics.durationMs = Date.now() - startTime; + const result = parseOrchestratorResponse(response); + return { result, metrics }; + } catch (err: any) { + metrics.durationMs = Date.now() - startTime; + metrics.errors.push(`orchestrator-full: ${err.message}`); + + // Fallback: generate all docs + return { + result: { + skipReason: null, + updates: [ + { target: "structure.md", reason: "Full analysis", relevantFiles: structural.fileTree, diffContext: "" }, + { target: "guide.md", reason: "Full analysis", relevantFiles: structural.fileTree, diffContext: "" }, + { target: "changelog.md", reason: "Full analysis", relevantFiles: structural.fileTree, diffContext: structural.gitLog }, + ], + }, + metrics, + }; + } +} + +function buildIncrementalContext( + diff: GitDiffResult, + existingDocs: Record, + fileTree: string[] +): string { + const parts: string[] = []; + + parts.push("## Diff Summary"); + parts.push(diff.diffStat); + parts.push(""); + parts.push("## Commit Log"); + parts.push(diff.commitLog); + parts.push(""); + parts.push("## Files Changed"); + parts.push(diff.filesChanged.join("\n")); + parts.push(""); + + parts.push("## Current Documentation TOCs"); + for (const [file, content] of Object.entries(existingDocs)) { + parts.push(`### ${file}`); + // Extract headings as TOC + const headings = content.split("\n").filter((l) => l.startsWith("#")); + parts.push(headings.join("\n")); + parts.push(""); + } + + parts.push("## File Tree (first 200 files)"); + parts.push(fileTree.slice(0, 200).join("\n")); + + return parts.join("\n"); +} + +function buildFullContext(structural: StructuralContext): string { + const parts: string[] = []; + + parts.push("## File Tree"); + parts.push(structural.fileTree.slice(0, 300).join("\n")); + parts.push(""); + + parts.push("## AST Summaries"); + for (const entry of structural.astSummaries.slice(0, 50)) { + parts.push(`### ${entry.file}`); + parts.push(entry.summary); + parts.push(""); + } + + parts.push("## Import Graph"); + for (const [mod, deps] of structural.importGraph.modules) { + parts.push(`${mod} → ${deps.join(", ")}`); + } + parts.push(""); + + if (structural.claudeMd) { + parts.push("## CLAUDE.md"); + parts.push(structural.claudeMd.slice(0, 5000)); + parts.push(""); + } + + parts.push("## Git Log (last 90 days)"); + parts.push(structural.gitLog.slice(0, 3000)); + parts.push(""); + + parts.push("## File Churn (last 30 days, top 30)"); + for (const entry of structural.gitChurn.slice(0, 30)) { + parts.push(` ${entry.count}\t${entry.file}`); + } + + return parts.join("\n"); +} + +function parseOrchestratorResponse(response: string): OrchestratorResult { + // Try to find JSON in the response + const jsonMatch = response.match(/\{[\s\S]*\}/); + if (!jsonMatch) { + throw new Error("No JSON found in orchestrator response"); + } + + try { + const parsed = JSON.parse(jsonMatch[0]); + return { + skipReason: parsed.skipReason ?? null, + updates: (parsed.updates ?? []).map((u: any) => ({ + target: u.target, + reason: u.reason ?? "", + relevantFiles: u.relevantFiles ?? [], + diffContext: u.diffContext ?? "", + })), + }; + } catch (err: any) { + throw new Error(`Failed to parse orchestrator JSON: ${err.message}`); + } +} diff --git a/src/reporter.ts b/src/reporter.ts new file mode 100644 index 0000000..73470f0 --- /dev/null +++ b/src/reporter.ts @@ -0,0 +1,157 @@ +import { writeFileSync, mkdirSync } from "fs"; +import { join } from "path"; +import type { ProjectRunReport, NightlyReport } from "./types.js"; + +export function writeProjectReport(runsDir: string, date: string, report: ProjectRunReport): void { + const dir = join(runsDir, date, report.project); + mkdirSync(dir, { recursive: true }); + mkdirSync(join(dir, "sessions"), { recursive: true }); + + const md = renderProjectReport(report); + writeFileSync(join(dir, "report.md"), md); +} + +function renderProjectReport(r: ProjectRunReport): string { + const lines: string[] = []; + + lines.push(`# Panopticon Run Report: ${r.project}`); + lines.push(`Date: ${r.date}`); + lines.push(`Status: ${r.status}`); + lines.push(`Duration: ${(r.duration / 1000).toFixed(1)}s`); + lines.push(""); + + lines.push("## Changes Since Last Run"); + lines.push(`- Commits: ${r.commitCount}`); + lines.push(`- Files changed: ${r.filesChanged}`); + lines.push(`- Insertions: +${r.insertions}, Deletions: -${r.deletions}`); + lines.push(""); + + if (r.orchestratorDecision) { + lines.push("## Orchestrator Decision"); + if (r.orchestratorDecision.skipReason) { + lines.push(`Skip: ${r.orchestratorDecision.skipReason}`); + } else { + for (const u of r.orchestratorDecision.updates) { + lines.push(`- ${u.target}: UPDATE (${u.reason})`); + } + } + lines.push(""); + } + + if (r.workerResults.length > 0) { + lines.push("## Worker Results"); + lines.push("| Worker | Target | Status | Tokens In | Tokens Out | Duration |"); + lines.push("|--------|--------|--------|-----------|------------|----------|"); + for (let i = 0; i < r.workerResults.length; i++) { + const w = r.workerResults[i]; + lines.push( + `| worker-${i} | ${w.target} | ${w.status} | ${w.metrics.tokensIn.toLocaleString()} | ${w.metrics.tokensOut.toLocaleString()} | ${(w.metrics.durationMs / 1000).toFixed(1)}s |` + ); + } + lines.push(""); + } + + lines.push("## Synthesizer Result"); + lines.push(`- Status: ${r.synthesizerStatus}`); + lines.push(""); + + if (Object.keys(r.docLinesChanged).length > 0) { + lines.push("## Doc Changes"); + for (const [file, linesChanged] of Object.entries(r.docLinesChanged)) { + lines.push(`### ${file}`); + lines.push(`- Lines changed: ${linesChanged}`); + lines.push(""); + } + } + + if (r.errors.length > 0) { + lines.push("## Errors"); + for (const err of r.errors) { + lines.push(`- ${err}`); + } + lines.push(""); + } + + return lines.join("\n"); +} + +export function writeNightlyReport(runsDir: string, date: string, report: NightlyReport): void { + mkdirSync(join(runsDir, date), { recursive: true }); + const md = renderNightlyReport(report); + writeFileSync(join(runsDir, date, "report.md"), md); +} + +function renderNightlyReport(r: NightlyReport): string { + const lines: string[] = []; + + lines.push(`# Panopticon Nightly Report — ${r.date}`); + lines.push(""); + + const succeeded = r.projectReports.filter((p) => p.status === "success").length; + const skipped = r.projectReports.filter((p) => p.status === "skipped").length; + const failed = r.projectReports.filter((p) => p.status === "failure").length; + + lines.push("## Summary"); + lines.push(`- Projects processed: ${r.projectReports.length}`); + lines.push(`- Succeeded: ${succeeded}`); + lines.push(`- Skipped (no changes): ${skipped}`); + lines.push(`- Failures: ${failed}`); + lines.push(`- Total duration: ${(r.totalDuration / 1000).toFixed(1)}s`); + lines.push(`- Total cost: ~$${r.totalCost.toFixed(3)}`); + lines.push(""); + + lines.push("## Per Project"); + lines.push("| Project | Status | Changes | Duration | Cost |"); + lines.push("|---------|--------|---------|----------|------|"); + for (const p of r.projectReports) { + const statusIcon = p.status === "success" ? "✅" : p.status === "skipped" ? "⏭" : "❌"; + lines.push( + `| ${p.project} | ${statusIcon} ${p.status} | ${p.filesChanged} files | ${(p.duration / 1000).toFixed(1)}s | $${p.estimatedCost.toFixed(3)} |` + ); + } + lines.push(""); + + if (r.anomalies.length > 0) { + lines.push("## Anomalies"); + for (const a of r.anomalies) { + lines.push(`- ${a}`); + } + } else { + lines.push("## Anomalies"); + lines.push("None detected."); + } + + lines.push(""); + return lines.join("\n"); +} + +/** + * Detect anomalies in the nightly run. + */ +export function detectAnomalies(reports: ProjectRunReport[]): string[] { + const anomalies: string[] = []; + + for (const r of reports) { + // Empty worker output + for (const w of r.workerResults) { + if (w.status === "success" && (!w.content || w.content.trim().length < 50)) { + anomalies.push(`${r.project}: worker for ${w.target} returned near-empty output`); + } + } + + // Large doc size changes (would need previous sizes — flag if >500 lines changed) + for (const [file, lines] of Object.entries(r.docLinesChanged)) { + if (lines > 200) { + anomalies.push(`${r.project}: ${file} had ${lines} lines changed (drastic change)`); + } + } + + // Worker failures + const failedWorkers = r.workerResults.filter((w) => w.status === "failure"); + if (failedWorkers.length > 0) { + anomalies.push(`${r.project}: ${failedWorkers.length} worker(s) failed`); + } + } + + return anomalies; +} diff --git a/src/session.ts b/src/session.ts new file mode 100644 index 0000000..160c1c3 --- /dev/null +++ b/src/session.ts @@ -0,0 +1,153 @@ +/** + * Shared session creation utilities for orchestrator, workers, and synthesizer. + */ +import { + createAgentSession, + DefaultResourceLoader, + SessionManager, + SettingsManager, + AuthStorage, + ModelRegistry, + readOnlyTools, + type AgentSession, + type AgentSessionEvent, +} from "@mariozechner/pi-coding-agent"; +import { getModel } from "@mariozechner/pi-ai"; +import type { ThinkingLevel } from "@mariozechner/pi-agent-core"; +import type { Config, SessionMetrics } from "./types.js"; + +export interface SessionOptions { + role: "orchestrator" | "worker" | "synthesizer"; + projectPath: string; + config: Config; + systemPrompt: string; +} + +/** + * Resolve a model spec like "anthropic/claude-sonnet-4-5" into a Model object. + */ +function resolveModel(spec: string, modelRegistry: ModelRegistry) { + const [provider, modelId] = spec.split("/"); + // Try ModelRegistry first, fall back to getModel + try { + return getModel(provider as any, modelId as any); + } catch { + throw new Error(`Cannot resolve model: ${spec}`); + } +} + +/** + * Create a pi AgentSession for a given role. + */ +export async function createSession(options: SessionOptions): Promise<{ + session: AgentSession; + metrics: SessionMetrics; +}> { + const { role, projectPath, config, systemPrompt } = options; + + const authStorage = AuthStorage.create(); + const modelRegistry = new ModelRegistry(authStorage); + + const modelSpec = config.models[role]; + const model = resolveModel(modelSpec, modelRegistry); + const thinkingLevel: ThinkingLevel = config.thinkingLevels[role]; + + const loader = new DefaultResourceLoader({ + cwd: projectPath, + systemPrompt, + noSkills: true, + noPromptTemplates: true, + noExtensions: true, + noThemes: true, + }); + await loader.reload(); + + const settingsManager = SettingsManager.inMemory({ + retry: { enabled: true, maxRetries: 2 }, + }); + + // Workers get read-only tools; orchestrator and synthesizer get no tools + const tools = role === "worker" ? readOnlyTools : []; + + const { session } = await createAgentSession({ + cwd: projectPath, + model, + thinkingLevel, + tools, + resourceLoader: loader, + sessionManager: SessionManager.inMemory(), + settingsManager, + authStorage, + modelRegistry, + }); + + // Track metrics + const metrics = trackSession(session); + + return { session, metrics }; +} + +/** + * Subscribe to session events and collect metrics. + */ +function trackSession(session: AgentSession): SessionMetrics { + const metrics: SessionMetrics = { + tokensIn: 0, + tokensOut: 0, + toolCalls: 0, + errors: [], + durationMs: 0, + }; + + session.subscribe((event: AgentSessionEvent) => { + if (event.type === "message_end") { + const msg = event.message as any; + if (msg.role === "assistant" && msg.usage) { + metrics.tokensIn += msg.usage.input ?? 0; + metrics.tokensOut += msg.usage.output ?? 0; + } + } + if (event.type === "tool_execution_end") { + metrics.toolCalls++; + if (event.isError) { + metrics.errors.push(`${event.toolName}: ${JSON.stringify(event.result).slice(0, 200)}`); + } + } + }); + + return metrics; +} + +/** + * Extract the final text response from a session after prompt() completes. + */ +export function extractFinalResponse(session: AgentSession): string { + const messages = session.messages; + for (let i = messages.length - 1; i >= 0; i--) { + const msg = messages[i] as any; + if (msg.role === "assistant" && msg.content) { + const textParts = msg.content.filter((c: any) => c.type === "text"); + return textParts.map((c: any) => c.text).join("\n"); + } + } + return ""; +} + +/** + * Run a prompt with a timeout via AbortController. + */ +export async function promptWithTimeout( + session: AgentSession, + prompt: string, + timeoutMs: number +): Promise { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), timeoutMs); + + try { + await session.prompt(prompt); + return extractFinalResponse(session); + } finally { + clearTimeout(timeout); + } +} diff --git a/src/state.ts b/src/state.ts new file mode 100644 index 0000000..7b186f2 --- /dev/null +++ b/src/state.ts @@ -0,0 +1,33 @@ +import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs"; +import { join } from "path"; +import type { ProjectState } from "./types.js"; + +const EMPTY_STATE: ProjectState = { + lastSha: null, + lastRunTimestamp: null, + lastRunStatus: null, + fileHashes: {}, + docVersions: {}, +}; + +export function loadState(stateDir: string, projectName: string): ProjectState { + const statePath = join(stateDir, `${projectName}.json`); + if (!existsSync(statePath)) { + return { ...EMPTY_STATE }; + } + try { + const raw = JSON.parse(readFileSync(statePath, "utf-8")); + return { + ...EMPTY_STATE, + ...raw, + }; + } catch { + return { ...EMPTY_STATE }; + } +} + +export function saveState(stateDir: string, projectName: string, state: ProjectState): void { + mkdirSync(stateDir, { recursive: true }); + const statePath = join(stateDir, `${projectName}.json`); + writeFileSync(statePath, JSON.stringify(state, null, 2) + "\n"); +} diff --git a/src/structural.ts b/src/structural.ts new file mode 100644 index 0000000..037fe13 --- /dev/null +++ b/src/structural.ts @@ -0,0 +1,240 @@ +import { readFileSync, existsSync } from "fs"; +import { join } from "path"; +import { createHash } from "crypto"; +import type { ProjectConfig, AstEntry, ImportGraph, StructuralContext, GitChurnEntry } from "./types.js"; +import { getFileTree, getGitLog, getFileChurn } from "./git.js"; + +export function hashFile(content: string): string { + return createHash("sha256").update(content).digest("hex"); +} + +export function hashFileAtPath(filePath: string): string { + const content = readFileSync(filePath, "utf-8"); + return hashFile(content); +} + +/** + * Extract AST summaries using regex-based parsing. + * Works for Rust, TypeScript, Python. Not perfect but functional. + */ +export function extractAstSummaries(projectPath: string, files: string[], language: string): AstEntry[] { + const entries: AstEntry[] = []; + + for (const file of files) { + const fullPath = join(projectPath, file); + if (!existsSync(fullPath)) continue; + + let content: string; + try { + content = readFileSync(fullPath, "utf-8"); + } catch { + continue; + } + + const summary = extractSummary(content, language); + if (summary) { + entries.push({ file, summary }); + } + } + + return entries; +} + +function extractSummary(content: string, language: string): string { + switch (language) { + case "rust": + return extractRustSummary(content); + case "typescript": + case "javascript": + return extractTsSummary(content); + default: + return extractGenericSummary(content); + } +} + +function extractRustSummary(content: string): string { + const lines: string[] = []; + + // Structs + const structs = content.matchAll(/pub\s+struct\s+(\w+)(?:<[^>]*>)?\s*\{([^}]*)\}/gs); + for (const m of structs) { + const fields = m[2] + .split("\n") + .map((l) => l.trim()) + .filter((l) => l && !l.startsWith("//") && !l.startsWith("#")) + .map((l) => l.replace(/pub\s+/, "").replace(/,\s*$/, "").split(":")[0]?.trim()) + .filter(Boolean); + lines.push(` struct ${m[1]} { ${fields.join(", ")} }`); + } + + // Enums + const enums = content.matchAll(/pub\s+enum\s+(\w+)(?:<[^>]*>)?\s*\{([^}]*)\}/gs); + for (const m of enums) { + const variants = m[2] + .split("\n") + .map((l) => l.trim()) + .filter((l) => l && !l.startsWith("//") && !l.startsWith("#")) + .map((l) => l.replace(/,\s*$/, "").split("(")[0]?.split("{")[0]?.trim()) + .filter(Boolean); + lines.push(` enum ${m[1]} { ${variants.join(", ")} }`); + } + + // Impl blocks + const impls = content.matchAll(/impl(?:<[^>]*>)?\s+(\w+)(?:<[^>]*>)?(?:\s+for\s+(\w+)(?:<[^>]*>)?)?\s*\{/g); + for (const m of impls) { + const implName = m[2] ? `${m[1]} for ${m[2]}` : m[1]; + // Find functions within the impl block (rough) + const startIdx = m.index! + m[0].length; + const block = extractBlock(content, startIdx); + const fns = block.matchAll(/(?:pub\s+)?(?:async\s+)?fn\s+(\w+)\s*(?:<[^>]*>)?\s*\(([^)]*)\)(?:\s*->\s*([^\n{]+))?/g); + const fnNames: string[] = []; + for (const f of fns) { + fnNames.push(f[1]); + } + if (fnNames.length > 0) { + lines.push(` impl ${implName}: ${fnNames.join(", ")}`); + } + } + + // Top-level functions + const topFns = content.matchAll(/^pub\s+(?:async\s+)?fn\s+(\w+)\s*(?:<[^>]*>)?\s*\(([^)]*)\)(?:\s*->\s*([^\n{]+))?/gm); + for (const m of topFns) { + const ret = m[3]?.trim() ?? "()"; + lines.push(` fn ${m[1]}(...) -> ${ret}`); + } + + // Traits + const traits = content.matchAll(/pub\s+trait\s+(\w+)(?:<[^>]*>)?\s*(?::\s*[^{]*)?\{/g); + for (const m of traits) { + lines.push(` trait ${m[1]}`); + } + + return lines.join("\n"); +} + +function extractTsSummary(content: string): string { + const lines: string[] = []; + + // Interfaces and types + const interfaces = content.matchAll(/export\s+(?:interface|type)\s+(\w+)/g); + for (const m of interfaces) { + lines.push(` type ${m[1]}`); + } + + // Classes + const classes = content.matchAll(/export\s+class\s+(\w+)/g); + for (const m of classes) { + lines.push(` class ${m[1]}`); + } + + // Functions + const fns = content.matchAll(/export\s+(?:async\s+)?function\s+(\w+)/g); + for (const m of fns) { + lines.push(` fn ${m[1]}`); + } + + return lines.join("\n"); +} + +function extractGenericSummary(content: string): string { + // Fallback: count lines, extract function-like patterns + const fns = content.matchAll(/(?:def|fn|func|function|sub)\s+(\w+)/g); + const names: string[] = []; + for (const m of fns) { + names.push(m[1]); + } + if (names.length === 0) return ""; + return ` functions: ${names.join(", ")}`; +} + +/** + * Extract a block of code starting from an opening brace position. + * Returns content between the first { and matching }. + */ +function extractBlock(content: string, startIdx: number): string { + let depth = 1; + let i = startIdx; + while (i < content.length && depth > 0) { + if (content[i] === "{") depth++; + else if (content[i] === "}") depth--; + i++; + } + return content.slice(startIdx, i - 1); +} + +/** + * Build an import/dependency graph (Rust-specific, with fallbacks). + */ +export function buildImportGraph(projectPath: string, files: string[], language: string): ImportGraph { + const modules = new Map(); + + if (language === "rust") { + for (const file of files) { + const fullPath = join(projectPath, file); + if (!existsSync(fullPath)) continue; + let content: string; + try { + content = readFileSync(fullPath, "utf-8"); + } catch { + continue; + } + + const deps: string[] = []; + + // use crate:: imports + const uses = content.matchAll(/use\s+crate::(\w+)/g); + for (const m of uses) { + deps.push(m[1]); + } + + // mod declarations + const mods = content.matchAll(/(?:pub\s+)?mod\s+(\w+)\s*;/g); + for (const m of mods) { + deps.push(m[1]); + } + + if (deps.length > 0) { + modules.set(file, [...new Set(deps)]); + } + } + } + + return { modules }; +} + +/** + * Gather all structural context for a project. + */ +export function gatherStructuralContext( + projectPath: string, + config: ProjectConfig +): StructuralContext { + const files = getFileTree(projectPath, config); + const astSummaries = extractAstSummaries(projectPath, files, config.language); + const importGraph = buildImportGraph(projectPath, files, config.language); + const gitLog = getGitLog(projectPath, 90); + const gitChurn = getFileChurn(projectPath, 30); + + // Read CLAUDE.md / AGENTS.md if they exist + let claudeMd: string | null = null; + let agentsMd: string | null = null; + + const claudeMdPath = join(projectPath, "CLAUDE.md"); + if (existsSync(claudeMdPath)) { + claudeMd = readFileSync(claudeMdPath, "utf-8"); + } + const agentsMdPath = join(projectPath, "AGENTS.md"); + if (existsSync(agentsMdPath)) { + agentsMd = readFileSync(agentsMdPath, "utf-8"); + } + + return { + fileTree: files, + astSummaries, + importGraph, + gitLog, + gitChurn, + claudeMd, + agentsMd, + }; +} diff --git a/src/synthesizer.ts b/src/synthesizer.ts new file mode 100644 index 0000000..48e7af0 --- /dev/null +++ b/src/synthesizer.ts @@ -0,0 +1,151 @@ +import { readFileSync } from "fs"; +import { join, dirname } from "path"; +import { fileURLToPath } from "url"; +import { createSession, promptWithTimeout } from "./session.js"; +import type { Config, WorkerResult, SynthesizerResult, SessionMetrics } from "./types.js"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const PROMPTS_DIR = join(__dirname, "..", "prompts"); + +function loadPrompt(name: string): string { + return readFileSync(join(PROMPTS_DIR, name), "utf-8"); +} + +/** + * Run the synthesizer to reconcile worker outputs and generate SKILL.md. + */ +export async function runSynthesizer( + projectPath: string, + config: Config, + projectName: string, + workerResults: WorkerResult[], + currentSkillMd: string | null +): Promise<{ result: SynthesizerResult; metrics: SessionMetrics }> { + const systemPrompt = loadPrompt("synthesizer.md"); + + const { session, metrics } = await createSession({ + role: "synthesizer", + projectPath, + config, + systemPrompt, + }); + + const startTime = Date.now(); + + const userPrompt = buildSynthesizerPrompt(projectName, workerResults, currentSkillMd); + + try { + const response = await promptWithTimeout( + session, + userPrompt, + config.limits.synthesizerTimeoutSeconds * 1000 + ); + + metrics.durationMs = Date.now() - startTime; + + const result = parseSynthesizerResponse(response, projectName); + return { result, metrics }; + } catch (err: any) { + metrics.durationMs = Date.now() - startTime; + metrics.errors.push(`synthesizer: ${err.message}`); + + // Fallback: generate a basic SKILL.md + console.error(`Synthesizer failed, generating basic SKILL.md: ${err.message}`); + return { + result: { + skillMd: generateFallbackSkillMd(projectName, workerResults), + fixes: [], + inconsistencies: [`Synthesizer failed: ${err.message}`], + }, + metrics, + }; + } +} + +function buildSynthesizerPrompt( + projectName: string, + workerResults: WorkerResult[], + currentSkillMd: string | null +): string { + const parts: string[] = []; + + parts.push(`## Project: ${projectName}`); + parts.push(""); + + for (const w of workerResults) { + if (w.status === "success") { + parts.push(`## Updated: ${w.target}`); + parts.push(w.content); + parts.push(""); + } else { + parts.push(`## Failed: ${w.target} (keeping existing)`); + parts.push(`Error: ${w.error}`); + parts.push(""); + } + } + + if (currentSkillMd) { + parts.push("## Current SKILL.md"); + parts.push(currentSkillMd); + } else { + parts.push("## No existing SKILL.md — generate from scratch"); + } + + return parts.join("\n"); +} + +function parseSynthesizerResponse(response: string, projectName: string): SynthesizerResult { + // Try to parse JSON response + const jsonMatch = response.match(/\{[\s\S]*\}/); + if (jsonMatch) { + try { + const parsed = JSON.parse(jsonMatch[0]); + return { + skillMd: parsed.skill_md ?? parsed.skillMd ?? generateFallbackSkillMd(projectName, []), + fixes: (parsed.fixes ?? []).map((f: any) => ({ + file: f.file, + description: f.description ?? "", + before: f.before ?? "", + after: f.after ?? "", + })), + inconsistencies: parsed.inconsistencies ?? [], + }; + } catch { + // JSON parsing failed, treat entire response as SKILL.md + } + } + + // If no valid JSON, treat the response as the SKILL.md content + return { + skillMd: response.trim(), + fixes: [], + inconsistencies: [], + }; +} + +function generateFallbackSkillMd(projectName: string, workerResults: WorkerResult[]): string { + const hasStructure = workerResults.some((w) => w.target === "structure.md" && w.status === "success"); + const hasGuide = workerResults.some((w) => w.target === "guide.md" && w.status === "success"); + const hasChangelog = workerResults.some((w) => w.target === "changelog.md" && w.status === "success"); + + const lines: string[] = []; + lines.push("---"); + lines.push(`name: panopticon`); + lines.push(`description: >-`); + lines.push(` Auto-generated project overview for ${projectName}. Architecture, conventions,`); + lines.push(` and recent activity. Updated nightly by Panopticon.`); + lines.push("---"); + lines.push(""); + lines.push(`# ${projectName} — Project Overview`); + lines.push(""); + lines.push("*Auto-generated by Panopticon*"); + lines.push(""); + lines.push("## Detailed Documentation"); + lines.push(""); + if (hasStructure) lines.push("- [Structure](structure.md) — modules, types, data flow, dependencies"); + if (hasGuide) lines.push("- [Guide](guide.md) — conventions, patterns, anti-patterns, testing"); + if (hasChangelog) lines.push("- [Changelog](changelog.md) — recent changes, active areas, stability"); + lines.push(""); + + return lines.join("\n"); +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..df5fb47 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,192 @@ +import type { ThinkingLevel } from "@mariozechner/pi-agent-core"; + +// ── Config types ── + +export interface ProjectConfig { + name: string; + path: string; + language: string; + sourceGlobs: string[]; + excludeGlobs: string[]; + branch: string; +} + +export interface ModelsConfig { + orchestrator: string; // "anthropic/claude-sonnet-4-5" + worker: string; + synthesizer: string; +} + +export interface ThinkingConfig { + orchestrator: ThinkingLevel; + worker: ThinkingLevel; + synthesizer: ThinkingLevel; +} + +export interface MetricsConfig { + enabled: boolean; + victoriaMetricsUrl: string; + jobLabel: string; +} + +export interface LimitsConfig { + maxWorkerConcurrency: number; + maxDiffSizeBytes: number; + maxFilesPerWorkUnit: number; + workerTimeoutSeconds: number; + synthesizerTimeoutSeconds: number; +} + +export interface Config { + projects: ProjectConfig[]; + models: ModelsConfig; + thinkingLevels: ThinkingConfig; + metrics: MetricsConfig; + limits: LimitsConfig; + stateDir: string; + runsDir: string; +} + +// ── State types ── + +export interface ProjectState { + lastSha: string | null; + lastRunTimestamp: string | null; + lastRunStatus: "success" | "failure" | "partial" | null; + fileHashes: Record; + docVersions: Record; +} + +// ── Orchestrator types ── + +export interface WorkUnit { + target: "structure.md" | "guide.md" | "changelog.md"; + reason: string; + relevantFiles: string[]; + diffContext: string; +} + +export interface OrchestratorResult { + skipReason: string | null; + updates: WorkUnit[]; +} + +// ── Worker types ── + +export interface WorkerResult { + target: string; + content: string; + status: "success" | "failure"; + error?: string; + metrics: SessionMetrics; +} + +// ── Synthesizer types ── + +export interface SynthesizerFix { + file: string; + description: string; + before: string; + after: string; +} + +export interface SynthesizerResult { + skillMd: string; + fixes: SynthesizerFix[]; + inconsistencies: string[]; +} + +// ── Metrics types ── + +export interface SessionMetrics { + tokensIn: number; + tokensOut: number; + toolCalls: number; + errors: string[]; + durationMs: number; +} + +export interface MetricLine { + name: string; + labels: Record; + value: number; + timestamp?: number; +} + +// ── Structural types ── + +export interface FileTreeResult { + files: string[]; +} + +export interface AstEntry { + file: string; + summary: string; +} + +export interface ImportGraph { + modules: Map; +} + +export interface GitDiffResult { + diffStat: string; + diffContent: string; + commitLog: string; + filesChanged: string[]; + insertions: number; + deletions: number; + commitCount: number; +} + +export interface GitChurnEntry { + count: number; + file: string; +} + +export interface StructuralContext { + fileTree: string[]; + astSummaries: AstEntry[]; + importGraph: ImportGraph; + gitLog: string; + gitChurn: GitChurnEntry[]; + claudeMd: string | null; + agentsMd: string | null; +} + +// ── Report types ── + +export interface PhaseTimings { + git: number; + structural: number; + orchestrator: number; + workers: number; + synthesizer: number; +} + +export interface ProjectRunReport { + project: string; + date: string; + status: "success" | "failure" | "partial" | "skipped"; + duration: number; + commitCount: number; + filesChanged: number; + insertions: number; + deletions: number; + orchestratorDecision: OrchestratorResult | null; + workerResults: WorkerResult[]; + synthesizerStatus: "success" | "failure" | "skipped"; + docLinesChanged: Record; + phaseTimings: PhaseTimings; + totalTokensIn: number; + totalTokensOut: number; + estimatedCost: number; + errors: string[]; +} + +export interface NightlyReport { + date: string; + projectReports: ProjectRunReport[]; + totalDuration: number; + totalCost: number; + anomalies: string[]; +} diff --git a/src/worker.ts b/src/worker.ts new file mode 100644 index 0000000..1a3e9af --- /dev/null +++ b/src/worker.ts @@ -0,0 +1,327 @@ +import { readFileSync } from "fs"; +import { join, dirname } from "path"; +import { fileURLToPath } from "url"; +import { createSession, promptWithTimeout } from "./session.js"; +import type { Config, WorkUnit, WorkerResult, StructuralContext, SessionMetrics } from "./types.js"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const PROMPTS_DIR = join(__dirname, "..", "prompts"); + +function loadPrompt(name: string): string { + return readFileSync(join(PROMPTS_DIR, name), "utf-8"); +} + +const LINE_LIMITS: Record = { + "structure.md": { min: 200, max: 500 }, + "guide.md": { min: 100, max: 300 }, + "changelog.md": { min: 100, max: 200 }, +}; + +/** + * Run a worker for an incremental doc update. + */ +export async function runIncrementalWorker( + projectPath: string, + config: Config, + workUnit: WorkUnit, + currentDoc: string | null, + diffContent: string +): Promise { + const promptTemplate = loadPrompt("worker-update.md"); + const limits = LINE_LIMITS[workUnit.target] ?? { min: 100, max: 300 }; + + const systemPrompt = promptTemplate + .replace("{min_lines}", String(limits.min)) + .replace("{max_lines}", String(limits.max)); + + const { session, metrics } = await createSession({ + role: "worker", + projectPath, + config, + systemPrompt, + }); + + const startTime = Date.now(); + + const userPrompt = buildIncrementalWorkerPrompt(workUnit, currentDoc, diffContent); + + try { + const content = await promptWithTimeout( + session, + userPrompt, + config.limits.workerTimeoutSeconds * 1000 + ); + + metrics.durationMs = Date.now() - startTime; + + if (!content || content.trim().length < 20) { + return { + target: workUnit.target, + content: currentDoc ?? "", + status: "failure", + error: "Worker returned empty/near-empty output", + metrics, + }; + } + + return { + target: workUnit.target, + content: cleanMarkdownOutput(content), + status: "success", + metrics, + }; + } catch (err: any) { + metrics.durationMs = Date.now() - startTime; + return { + target: workUnit.target, + content: currentDoc ?? "", + status: "failure", + error: err.message, + metrics, + }; + } +} + +/** + * Run a worker for full analysis of a specific doc section. + */ +export async function runFullAnalysisWorker( + projectPath: string, + config: Config, + target: "structure.md" | "guide.md" | "changelog.md", + structural: StructuralContext, + workUnit: WorkUnit +): Promise { + const promptName = `worker-${target.replace(".md", "")}.md`; + let systemPrompt: string; + try { + systemPrompt = loadPrompt(promptName); + } catch { + // Fall back to generic worker prompt + systemPrompt = loadPrompt("worker-structure.md"); + } + + const limits = LINE_LIMITS[target] ?? { min: 100, max: 300 }; + systemPrompt = systemPrompt + .replace("{min_lines}", String(limits.min)) + .replace("{max_lines}", String(limits.max)); + + const { session, metrics } = await createSession({ + role: "worker", + projectPath, + config, + systemPrompt, + }); + + const startTime = Date.now(); + const userPrompt = buildFullWorkerPrompt(target, structural, workUnit); + + try { + const content = await promptWithTimeout( + session, + userPrompt, + config.limits.workerTimeoutSeconds * 1000 + ); + + metrics.durationMs = Date.now() - startTime; + + if (!content || content.trim().length < 20) { + return { + target, + content: "", + status: "failure", + error: "Worker returned empty/near-empty output", + metrics, + }; + } + + return { + target, + content: cleanMarkdownOutput(content), + status: "success", + metrics, + }; + } catch (err: any) { + metrics.durationMs = Date.now() - startTime; + return { + target, + content: "", + status: "failure", + error: err.message, + metrics, + }; + } +} + +/** + * Run multiple workers concurrently, respecting concurrency limits. + */ +export async function runWorkersConcurrently( + tasks: Array<() => Promise>, + maxConcurrency: number +): Promise { + const results: T[] = []; + const running: Promise[] = []; + + for (const task of tasks) { + const promise = task().then((result) => { + results.push(result); + }); + running.push(promise); + + if (running.length >= maxConcurrency) { + await Promise.race(running); + // Remove completed promises + for (let i = running.length - 1; i >= 0; i--) { + // Check if promise is settled by racing with an immediate resolve + const settled = await Promise.race([ + running[i].then(() => true), + Promise.resolve(false), + ]); + if (settled) running.splice(i, 1); + } + } + } + + await Promise.all(running); + return results; +} + +function buildIncrementalWorkerPrompt( + workUnit: WorkUnit, + currentDoc: string | null, + diffContent: string +): string { + const parts: string[] = []; + + parts.push("## Current Documentation"); + if (currentDoc) { + parts.push(currentDoc); + } else { + parts.push("*No existing documentation for this section.*"); + } + parts.push(""); + + parts.push("## What Changed"); + parts.push(workUnit.reason); + parts.push(""); + + parts.push("## Diff Context"); + parts.push(workUnit.diffContext || diffContent.slice(0, 50000)); + parts.push(""); + + parts.push("## Relevant Files to Examine"); + parts.push("Use the read tool to examine these files for context:"); + for (const f of workUnit.relevantFiles) { + parts.push(`- ${f}`); + } + + return parts.join("\n"); +} + +function buildFullWorkerPrompt( + target: string, + structural: StructuralContext, + workUnit: WorkUnit +): string { + const parts: string[] = []; + + parts.push("## File Tree"); + parts.push(structural.fileTree.slice(0, 300).join("\n")); + parts.push(""); + + if (target === "structure.md") { + parts.push("## AST Summaries"); + for (const entry of structural.astSummaries) { + parts.push(`### ${entry.file}`); + parts.push(entry.summary); + parts.push(""); + } + + parts.push("## Import Graph"); + for (const [mod, deps] of structural.importGraph.modules) { + parts.push(`${mod} → ${deps.join(", ")}`); + } + parts.push(""); + } + + if (target === "guide.md") { + if (structural.claudeMd) { + parts.push("## Existing CLAUDE.md"); + parts.push(structural.claudeMd); + parts.push(""); + } + if (structural.agentsMd) { + parts.push("## Existing AGENTS.md"); + parts.push(structural.agentsMd); + parts.push(""); + } + + parts.push("## AST Summaries (for pattern detection)"); + for (const entry of structural.astSummaries.slice(0, 30)) { + parts.push(`### ${entry.file}`); + parts.push(entry.summary); + parts.push(""); + } + } + + if (target === "changelog.md") { + parts.push("## Git Log (last 90 days)"); + parts.push(structural.gitLog); + parts.push(""); + + parts.push("## File Churn (last 30 days)"); + for (const entry of structural.gitChurn) { + parts.push(` ${entry.count}\t${entry.file}`); + } + parts.push(""); + } + + if (workUnit.relevantFiles.length > 0) { + parts.push("## Key Files to Examine"); + parts.push("Use the read tool to examine these files:"); + for (const f of workUnit.relevantFiles.slice(0, 20)) { + parts.push(`- ${f}`); + } + } + + return parts.join("\n"); +} + +/** + * Clean markdown output: remove wrapping code fences and LLM preamble. + */ +function cleanMarkdownOutput(content: string): string { + let cleaned = content.trim(); + + // Remove ```markdown ... ``` wrapper + if (cleaned.startsWith("```markdown")) { + cleaned = cleaned.slice("```markdown".length); + if (cleaned.endsWith("```")) { + cleaned = cleaned.slice(0, -3); + } + cleaned = cleaned.trim(); + } else if (cleaned.startsWith("```md")) { + cleaned = cleaned.slice("```md".length); + if (cleaned.endsWith("```")) { + cleaned = cleaned.slice(0, -3); + } + cleaned = cleaned.trim(); + } else if (cleaned.startsWith("```")) { + cleaned = cleaned.slice(3); + if (cleaned.endsWith("```")) { + cleaned = cleaned.slice(0, -3); + } + cleaned = cleaned.trim(); + } + + // Strip LLM preamble: any text before the first markdown heading. + // Workers are instructed to start directly with a heading, so anything + // before the first "# " line is conversational fluff. + const firstHeading = cleaned.search(/^# /m); + if (firstHeading > 0) { + cleaned = cleaned.slice(firstHeading).trim(); + } + + return cleaned; +} diff --git a/src/writer.ts b/src/writer.ts new file mode 100644 index 0000000..501d2c5 --- /dev/null +++ b/src/writer.ts @@ -0,0 +1,80 @@ +import { writeFileSync, mkdirSync, existsSync, readFileSync, readdirSync, unlinkSync } from "fs"; +import { join } from "path"; + +const SKILL_DIR = ".pi/skills/panopticon"; + +/** + * Write the complete skill directory for a project. + */ +export function writeSkillFiles( + projectPath: string, + files: Record +): void { + const skillDir = join(projectPath, SKILL_DIR); + mkdirSync(skillDir, { recursive: true }); + + for (const [filename, content] of Object.entries(files)) { + const filePath = join(skillDir, filename); + writeFileSync(filePath, content); + } +} + +/** + * Read an existing skill file if it exists. + */ +export function readSkillFile(projectPath: string, filename: string): string | null { + const filePath = join(projectPath, SKILL_DIR, filename); + if (!existsSync(filePath)) return null; + try { + return readFileSync(filePath, "utf-8"); + } catch { + return null; + } +} + +/** + * Read all existing skill files. + */ +export function readAllSkillFiles(projectPath: string): Record { + const files: Record = {}; + for (const name of ["SKILL.md", "structure.md", "guide.md", "changelog.md"]) { + const content = readSkillFile(projectPath, name); + if (content) { + files[name] = content; + } + } + return files; +} + +/** + * Remove all generated .md files in the skill directory except SKILL.md. + * Used before full analysis to wipe stale documents (e.g. after renames). + */ +export function cleanSkillDir(projectPath: string): string[] { + const skillDir = join(projectPath, SKILL_DIR); + if (!existsSync(skillDir)) return []; + + const removed: string[] = []; + for (const entry of readdirSync(skillDir)) { + if (entry === "SKILL.md") continue; + if (entry.endsWith(".md")) { + unlinkSync(join(skillDir, entry)); + removed.push(entry); + } + } + return removed; +} + +/** + * Count lines changed between old and new content. + */ +export function countLinesChanged(oldContent: string | null, newContent: string): number { + if (!oldContent) return newContent.split("\n").length; + const oldLines = new Set(oldContent.split("\n")); + const newLines = newContent.split("\n"); + let changed = 0; + for (const line of newLines) { + if (!oldLines.has(line)) changed++; + } + return changed; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..183333c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "outDir": "dist", + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": true, + "sourceMap": true + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist"] +}