diff --git a/.gitmodules b/.gitmodules index e2ed8a7..ce7c96c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,15 @@ [submodule "instructions"] path = instructions url = git@gitea.dsrptlab.com:dsrptlab/instructions.git +[submodule "frontend"] + path = frontend + url = git@gitea.dsrptlab.com:clientflow/frontend.git + branch = main +[submodule "backend"] + path = backend + url = git@gitea.dsrptlab.com:clientflow/backend.git + branch = main +[submodule "backend_worker"] + path = backend_worker + url = git@gitea.dsrptlab.com:clientflow/backend_worker.git + branch = main diff --git a/backend b/backend new file mode 160000 index 0000000..42e9dc7 --- /dev/null +++ b/backend @@ -0,0 +1 @@ +Subproject commit 42e9dc7bcbbc5933620045f83368e51307dc4ce1 diff --git a/backend/Dockerfile b/backend/Dockerfile deleted file mode 100644 index b13cb8f..0000000 --- a/backend/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM node:22-alpine - -WORKDIR /app - -RUN apk add --no-cache curl jq - -COPY package*.json ./ -COPY prisma ./prisma -RUN npm ci -RUN DATABASE_URL="postgresql://postgres:postgres@127.0.0.1:5432/postgres?schema=public" npx prisma generate - -COPY . . - -CMD ["sh", "-lc", ". /app/scripts/load-vault-env.sh && npm run start"] diff --git a/backend/README.md b/backend/README.md deleted file mode 100644 index 2f18267..0000000 --- a/backend/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# backend - -Core CRM/omni-домен с единственной Prisma-базой. - -## Назначение - -- принимает входящие telegram-события через GraphQL mutation `ingestTelegramInbound`; -- создает исходящую задачу через GraphQL mutation `requestTelegramOutbound` (в `telegram_backend`, далее в Hatchet); -- принимает отчет о доставке через GraphQL mutation `reportTelegramOutbound`. -- выполняет sync календарных предзаписей через GraphQL mutation `syncCalendarPredueTimeline`. - -## API - -- `GET /health` -- `POST /graphql` - -## GraphQL auth - -Если задан `BACKEND_GRAPHQL_SHARED_SECRET`, запросы на `/graphql` должны содержать заголовок: - -- `x-graphql-secret: ` - -## Переменные окружения - -- `PORT` (default: `8090`) -- `MAX_BODY_SIZE_BYTES` (default: `2097152`) -- `BACKEND_GRAPHQL_SHARED_SECRET` (optional) -- `TELEGRAM_BACKEND_GRAPHQL_URL` (required для `requestTelegramOutbound`) -- `TELEGRAM_BACKEND_GRAPHQL_SHARED_SECRET` (optional) -- `DEFAULT_TEAM_ID` (optional fallback для inbound маршрутизации) -- `TIMELINE_EVENT_PREDUE_MINUTES` (default: `30`) -- `TIMELINE_EVENT_LOOKBACK_MINUTES` (default: `180`) -- `TIMELINE_EVENT_LOOKAHEAD_MINUTES` (default: `1440`) -- `TIMELINE_SCHEDULER_LOCK_KEY` (default: `603001`) - -## Prisma policy - -- Источник схемы: `frontend/prisma/schema.prisma`. -- Локальная копия в `backend/prisma/schema.prisma` обновляется только через `scripts/prisma-sync.sh`. -- Миграции/`db push` выполняются только в `frontend`. diff --git a/backend/package-lock.json b/backend/package-lock.json deleted file mode 100644 index e453069..0000000 --- a/backend/package-lock.json +++ /dev/null @@ -1,1025 +0,0 @@ -{ - "name": "crm-backend", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "crm-backend", - "dependencies": { - "@prisma/client": "^6.16.1", - "graphql": "^16.13.1" - }, - "devDependencies": { - "@types/node": "^22.13.9", - "prisma": "^6.16.1", - "tsx": "^4.20.5", - "typescript": "^5.9.2" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", - "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", - "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", - "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", - "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", - "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", - "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", - "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", - "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", - "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", - "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", - "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", - "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", - "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", - "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", - "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", - "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", - "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", - "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", - "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", - "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", - "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", - "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", - "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", - "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", - "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", - "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@prisma/client": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.16.1.tgz", - "integrity": "sha512-QaBCOY29lLAxEFFJgBPyW3WInCW52fJeQTmWx/h6YsP5u0bwuqP51aP0uhqFvhK9DaZPwvai/M4tSDYLVE9vRg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "peerDependencies": { - "prisma": "*", - "typescript": ">=5.1.0" - }, - "peerDependenciesMeta": { - "prisma": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/@prisma/config": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.16.1.tgz", - "integrity": "sha512-sz3uxRPNL62QrJ0EYiujCFkIGZ3hg+9hgC1Ae1HjoYuj0BxCqHua4JNijYvYCrh9LlofZDZcRBX3tHBfLvAngA==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "c12": "3.1.0", - "deepmerge-ts": "7.1.5", - "effect": "3.16.12", - "empathic": "2.0.0" - } - }, - "node_modules/@prisma/debug": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.16.1.tgz", - "integrity": "sha512-RWv/VisW5vJE4cDRTuAHeVedtGoItXTnhuLHsSlJ9202QKz60uiXWywBlVcqXVq8bFeIZoCoWH+R1duZJPwqLw==", - "devOptional": true, - "license": "Apache-2.0" - }, - "node_modules/@prisma/engines": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.16.1.tgz", - "integrity": "sha512-EOnEM5HlosPudBqbI+jipmaW/vQEaF0bKBo4gVkGabasINHR6RpC6h44fKZEqx4GD8CvH+einD2+b49DQrwrAg==", - "devOptional": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.16.1", - "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", - "@prisma/fetch-engine": "6.16.1", - "@prisma/get-platform": "6.16.1" - } - }, - "node_modules/@prisma/engines-version": { - "version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43.tgz", - "integrity": "sha512-ThvlDaKIVrnrv97ujNFDYiQbeMQpLa0O86HFA2mNoip4mtFqM7U5GSz2ie1i2xByZtvPztJlNRgPsXGeM/kqAA==", - "devOptional": true, - "license": "Apache-2.0" - }, - "node_modules/@prisma/fetch-engine": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.16.1.tgz", - "integrity": "sha512-fl/PKQ8da5YTayw86WD3O9OmKJEM43gD3vANy2hS5S1CnfW2oPXk+Q03+gUWqcKK306QqhjjIHRFuTZ31WaosQ==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.16.1", - "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", - "@prisma/get-platform": "6.16.1" - } - }, - "node_modules/@prisma/get-platform": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.16.1.tgz", - "integrity": "sha512-kUfg4vagBG7dnaGRcGd1c0ytQFcDj2SUABiuveIpL3bthFdTLI6PJeLEia6Q8Dgh+WhPdo0N2q0Fzjk63XTyaA==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.16.1" - } - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.19.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", - "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/c12": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", - "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "chokidar": "^4.0.3", - "confbox": "^0.2.2", - "defu": "^6.1.4", - "dotenv": "^16.6.1", - "exsolve": "^1.0.7", - "giget": "^2.0.0", - "jiti": "^2.4.2", - "ohash": "^2.0.11", - "pathe": "^2.0.3", - "perfect-debounce": "^1.0.0", - "pkg-types": "^2.2.0", - "rc9": "^2.1.2" - }, - "peerDependencies": { - "magicast": "^0.3.5" - }, - "peerDependenciesMeta": { - "magicast": { - "optional": true - } - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/citty": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", - "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "consola": "^3.2.3" - } - }, - "node_modules/confbox": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", - "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/deepmerge-ts": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", - "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", - "devOptional": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/defu": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", - "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/destr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", - "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "devOptional": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/effect": { - "version": "3.16.12", - "resolved": "https://registry.npmjs.org/effect/-/effect-3.16.12.tgz", - "integrity": "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.0.0", - "fast-check": "^3.23.1" - } - }, - "node_modules/empathic": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", - "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/esbuild": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", - "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.3", - "@esbuild/android-arm": "0.27.3", - "@esbuild/android-arm64": "0.27.3", - "@esbuild/android-x64": "0.27.3", - "@esbuild/darwin-arm64": "0.27.3", - "@esbuild/darwin-x64": "0.27.3", - "@esbuild/freebsd-arm64": "0.27.3", - "@esbuild/freebsd-x64": "0.27.3", - "@esbuild/linux-arm": "0.27.3", - "@esbuild/linux-arm64": "0.27.3", - "@esbuild/linux-ia32": "0.27.3", - "@esbuild/linux-loong64": "0.27.3", - "@esbuild/linux-mips64el": "0.27.3", - "@esbuild/linux-ppc64": "0.27.3", - "@esbuild/linux-riscv64": "0.27.3", - "@esbuild/linux-s390x": "0.27.3", - "@esbuild/linux-x64": "0.27.3", - "@esbuild/netbsd-arm64": "0.27.3", - "@esbuild/netbsd-x64": "0.27.3", - "@esbuild/openbsd-arm64": "0.27.3", - "@esbuild/openbsd-x64": "0.27.3", - "@esbuild/openharmony-arm64": "0.27.3", - "@esbuild/sunos-x64": "0.27.3", - "@esbuild/win32-arm64": "0.27.3", - "@esbuild/win32-ia32": "0.27.3", - "@esbuild/win32-x64": "0.27.3" - } - }, - "node_modules/exsolve": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", - "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/fast-check": { - "version": "3.23.2", - "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", - "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", - "devOptional": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT", - "dependencies": { - "pure-rand": "^6.1.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-tsconfig": { - "version": "4.13.6", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", - "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/giget": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", - "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "citty": "^0.1.6", - "consola": "^3.4.0", - "defu": "^6.1.4", - "node-fetch-native": "^1.6.6", - "nypm": "^0.6.0", - "pathe": "^2.0.3" - }, - "bin": { - "giget": "dist/cli.mjs" - } - }, - "node_modules/graphql": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.13.1.tgz", - "integrity": "sha512-gGgrVCoDKlIZ8fIqXBBb0pPKqDgki0Z/FSKNiQzSGj2uEYHr1tq5wmBegGwJx6QB5S5cM0khSBpi/JFHMCvsmQ==", - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" - } - }, - "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "devOptional": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/node-fetch-native": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", - "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/nypm": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz", - "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "citty": "^0.2.0", - "pathe": "^2.0.3", - "tinyexec": "^1.0.2" - }, - "bin": { - "nypm": "dist/cli.mjs" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/nypm/node_modules/citty": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.1.tgz", - "integrity": "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/ohash": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", - "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/perfect-debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", - "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/pkg-types": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", - "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "confbox": "^0.2.2", - "exsolve": "^1.0.7", - "pathe": "^2.0.3" - } - }, - "node_modules/prisma": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.16.1.tgz", - "integrity": "sha512-MFkMU0eaDDKAT4R/By2IA9oQmwLTxokqv2wegAErr9Rf+oIe7W2sYpE/Uxq0H2DliIR7vnV63PkC1bEwUtl98w==", - "devOptional": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/config": "6.16.1", - "@prisma/engines": "6.16.1" - }, - "bin": { - "prisma": "build/index.js" - }, - "engines": { - "node": ">=18.18" - }, - "peerDependencies": { - "typescript": ">=5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "devOptional": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/rc9": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", - "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "defu": "^6.1.4", - "destr": "^2.0.3" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/tinyexec": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", - "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", - "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.27.0", - "get-tsconfig": "^4.7.5" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - } - } -} diff --git a/backend/package.json b/backend/package.json deleted file mode 100644 index 1d457b6..0000000 --- a/backend/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "crm-backend", - "private": true, - "type": "module", - "scripts": { - "start": "tsx src/index.ts", - "typecheck": "tsc --noEmit" - }, - "devDependencies": { - "@types/node": "^22.13.9", - "prisma": "^6.16.1", - "tsx": "^4.20.5", - "typescript": "^5.9.2" - }, - "dependencies": { - "@prisma/client": "^6.16.1", - "graphql": "^16.13.1" - } -} diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma deleted file mode 100644 index ea2ef0b..0000000 --- a/backend/prisma/schema.prisma +++ /dev/null @@ -1,483 +0,0 @@ -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} - -enum TeamRole { - OWNER - MEMBER -} - -enum MessageDirection { - IN - OUT -} - -enum MessageChannel { - TELEGRAM - WHATSAPP - INSTAGRAM - PHONE - EMAIL - INTERNAL -} - -enum ContactMessageKind { - MESSAGE - CALL -} - -enum ChatRole { - USER - ASSISTANT - SYSTEM -} - -enum OmniMessageStatus { - PENDING - SENT - FAILED - DELIVERED - READ -} - -enum FeedCardDecision { - PENDING - ACCEPTED - REJECTED -} - -enum WorkspaceDocumentType { - Regulation - Playbook - Policy - Template -} - -enum ClientTimelineContentType { - CALENDAR_EVENT - DOCUMENT - RECOMMENDATION -} - -model Team { - id String @id @default(cuid()) - name String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - members TeamMember[] - contacts Contact[] - calendarEvents CalendarEvent[] - deals Deal[] - aiConversations AiConversation[] - aiMessages AiMessage[] - - omniThreads OmniThread[] - omniMessages OmniMessage[] - omniIdentities OmniContactIdentity[] - telegramBusinessConnections TelegramBusinessConnection[] - - feedCards FeedCard[] - contactPins ContactPin[] - documents WorkspaceDocument[] - clientTimelineEntries ClientTimelineEntry[] - contactInboxes ContactInbox[] - contactInboxPreferences ContactInboxPreference[] - contactThreadReads ContactThreadRead[] -} - -model User { - id String @id @default(cuid()) - phone String @unique - passwordHash String - email String? @unique - name String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - memberships TeamMember[] - aiConversations AiConversation[] @relation("ConversationCreator") - aiMessages AiMessage[] @relation("ChatAuthor") - contactInboxPreferences ContactInboxPreference[] - contactThreadReads ContactThreadRead[] -} - -model TeamMember { - id String @id @default(cuid()) - teamId String - userId String - role TeamRole @default(MEMBER) - createdAt DateTime @default(now()) - - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - - @@unique([teamId, userId]) - @@index([userId]) -} - -model Contact { - id String @id @default(cuid()) - teamId String - name String - avatarUrl String? - email String? - phone String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - note ContactNote? - messages ContactMessage[] - events CalendarEvent[] - deals Deal[] - feedCards FeedCard[] - pins ContactPin[] - - omniThreads OmniThread[] - omniMessages OmniMessage[] - omniIdentities OmniContactIdentity[] - contactInboxes ContactInbox[] - clientTimelineEntries ClientTimelineEntry[] - contactThreadReads ContactThreadRead[] - - @@index([teamId, updatedAt]) -} - -model ContactThreadRead { - id String @id @default(cuid()) - teamId String - userId String - contactId String - readAt DateTime @default(now()) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) - - @@unique([userId, contactId]) - @@index([teamId, userId]) -} - -model ContactNote { - id String @id @default(cuid()) - contactId String @unique - content String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) -} - -model ContactMessage { - id String @id @default(cuid()) - contactId String - contactInboxId String? - kind ContactMessageKind @default(MESSAGE) - direction MessageDirection - channel MessageChannel - content String - audioUrl String? - durationSec Int? - waveformJson Json? - transcriptJson Json? - occurredAt DateTime @default(now()) - createdAt DateTime @default(now()) - - contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) - contactInbox ContactInbox? @relation(fields: [contactInboxId], references: [id], onDelete: SetNull) - - @@index([contactId, occurredAt]) - @@index([contactInboxId, occurredAt]) -} - -model ContactInbox { - id String @id @default(cuid()) - teamId String - contactId String - channel MessageChannel - sourceExternalId String - title String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) - messages ContactMessage[] - preferences ContactInboxPreference[] - - @@unique([teamId, channel, sourceExternalId]) - @@index([contactId, updatedAt]) - @@index([teamId, updatedAt]) -} - -model ContactInboxPreference { - id String @id @default(cuid()) - teamId String - userId String - contactInboxId String - isHidden Boolean @default(false) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - contactInbox ContactInbox @relation(fields: [contactInboxId], references: [id], onDelete: Cascade) - - @@unique([userId, contactInboxId]) - @@index([teamId, userId, isHidden]) -} - -model OmniContactIdentity { - id String @id @default(cuid()) - teamId String - contactId String - channel MessageChannel - externalId String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) - - @@unique([teamId, channel, externalId]) - @@index([contactId]) - @@index([teamId, updatedAt]) -} - -model OmniThread { - id String @id @default(cuid()) - teamId String - contactId String - channel MessageChannel - externalChatId String - businessConnectionId String? - title String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) - messages OmniMessage[] - - @@unique([teamId, channel, externalChatId, businessConnectionId]) - @@index([teamId, updatedAt]) - @@index([contactId, updatedAt]) -} - -model OmniMessage { - id String @id @default(cuid()) - teamId String - contactId String - threadId String - direction MessageDirection - channel MessageChannel - status OmniMessageStatus @default(PENDING) - text String - providerMessageId String? - providerUpdateId String? - rawJson Json? - occurredAt DateTime @default(now()) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) - thread OmniThread @relation(fields: [threadId], references: [id], onDelete: Cascade) - - @@unique([threadId, providerMessageId]) - @@index([teamId, occurredAt]) - @@index([threadId, occurredAt]) -} - -model TelegramBusinessConnection { - id String @id @default(cuid()) - teamId String - businessConnectionId String - isEnabled Boolean? - canReply Boolean? - rawJson Json? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - - @@unique([teamId, businessConnectionId]) - @@index([teamId, updatedAt]) -} - -model CalendarEvent { - id String @id @default(cuid()) - teamId String - contactId String? - title String - startsAt DateTime - endsAt DateTime? - note String? - isArchived Boolean @default(false) - archiveNote String? - archivedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - contact Contact? @relation(fields: [contactId], references: [id], onDelete: SetNull) - - @@index([startsAt]) - @@index([contactId, startsAt]) - @@index([teamId, startsAt]) -} - -model Deal { - id String @id @default(cuid()) - teamId String - contactId String - title String - stage String - amount Int? - paidAmount Int? - nextStep String? - summary String? - currentStepId String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) - steps DealStep[] - - @@index([teamId, updatedAt]) - @@index([contactId, updatedAt]) - @@index([currentStepId]) -} - -model DealStep { - id String @id @default(cuid()) - dealId String - title String - description String? - status String @default("todo") - dueAt DateTime? - order Int @default(0) - completedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - deal Deal @relation(fields: [dealId], references: [id], onDelete: Cascade) - - @@index([dealId, order]) - @@index([status, dueAt]) -} - -model AiConversation { - id String @id @default(cuid()) - teamId String - createdByUserId String - title String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - createdByUser User @relation("ConversationCreator", fields: [createdByUserId], references: [id], onDelete: Cascade) - messages AiMessage[] - - @@index([teamId, updatedAt]) - @@index([createdByUserId]) - @@map("ChatConversation") -} - -model AiMessage { - id String @id @default(cuid()) - teamId String - conversationId String - authorUserId String? - role ChatRole - text String - planJson Json? - createdAt DateTime @default(now()) - - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - conversation AiConversation @relation(fields: [conversationId], references: [id], onDelete: Cascade) - authorUser User? @relation("ChatAuthor", fields: [authorUserId], references: [id], onDelete: SetNull) - - @@index([createdAt]) - @@index([teamId, createdAt]) - @@index([conversationId, createdAt]) - @@map("ChatMessage") -} - -model FeedCard { - id String @id @default(cuid()) - teamId String - contactId String? - happenedAt DateTime - text String - proposalJson Json - decision FeedCardDecision @default(PENDING) - decisionNote String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - contact Contact? @relation(fields: [contactId], references: [id], onDelete: SetNull) - - @@index([teamId, happenedAt]) - @@index([contactId, happenedAt]) -} - -model ContactPin { - id String @id @default(cuid()) - teamId String - contactId String - text String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) - - @@index([teamId, updatedAt]) - @@index([contactId, updatedAt]) -} - -model WorkspaceDocument { - id String @id @default(cuid()) - teamId String - title String - type WorkspaceDocumentType - owner String - scope String - summary String - body String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - - @@index([teamId, updatedAt]) -} - -model ClientTimelineEntry { - id String @id @default(cuid()) - teamId String - contactId String - contentType ClientTimelineContentType - contentId String - datetime DateTime @default(now()) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) - contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) - - @@unique([teamId, contentType, contentId]) - @@index([teamId, contactId, datetime]) - @@index([contactId, datetime]) -} diff --git a/backend/scripts/load-vault-env.sh b/backend/scripts/load-vault-env.sh deleted file mode 100755 index 086c1e8..0000000 --- a/backend/scripts/load-vault-env.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/sh -set -eu - -log() { - printf '%s\n' "$*" >&2 -} - -VAULT_ENABLED="${VAULT_ENABLED:-auto}" -if [ "$VAULT_ENABLED" = "false" ] || [ "$VAULT_ENABLED" = "0" ]; then - exit 0 -fi - -if [ -z "${VAULT_ADDR:-}" ] || [ -z "${VAULT_TOKEN:-}" ]; then - if [ "$VAULT_ENABLED" = "true" ] || [ "$VAULT_ENABLED" = "1" ]; then - log "Vault bootstrap is required but VAULT_ADDR or VAULT_TOKEN is missing." - exit 1 - fi - exit 0 -fi - -if ! command -v curl >/dev/null 2>&1 || ! command -v jq >/dev/null 2>&1; then - log "Vault bootstrap requires curl and jq." - exit 1 -fi - -VAULT_KV_MOUNT="${VAULT_KV_MOUNT:-secret}" - -load_secret_path() { - path="$1" - source_name="$2" - if [ -z "$path" ]; then - return 0 - fi - - url="${VAULT_ADDR%/}/v1/${VAULT_KV_MOUNT}/data/${path}" - response="$(curl -fsS -H "X-Vault-Token: $VAULT_TOKEN" "$url")" || { - log "Failed to load Vault path ${VAULT_KV_MOUNT}/${path}." - return 1 - } - - encoded_items="$(printf '%s' "$response" | jq -r '.data.data // {} | to_entries[]? | @base64')" - if [ -z "$encoded_items" ]; then - return 0 - fi - - old_ifs="${IFS}" - IFS=' -' - for encoded_item in $encoded_items; do - key="$(printf '%s' "$encoded_item" | base64 -d | jq -r '.key')" - value="$(printf '%s' "$encoded_item" | base64 -d | jq -r '.value | tostring')" - export "$key=$value" - done - IFS="${old_ifs}" - - log "Loaded Vault ${source_name} secrets from ${VAULT_KV_MOUNT}/${path}." -} - -load_secret_path "${VAULT_SHARED_PATH:-}" "shared" -load_secret_path "${VAULT_PROJECT_PATH:-}" "project" diff --git a/backend/src/index.ts b/backend/src/index.ts deleted file mode 100644 index 2b0702b..0000000 --- a/backend/src/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { startServer } from "./server"; -import { prisma } from "./utils/prisma"; - -const server = startServer(); - -async function shutdown(signal: string) { - console.log(`[backend] shutting down by ${signal}`); - - try { - await new Promise((resolve, reject) => { - server.close((error) => { - if (error) { - reject(error); - return; - } - resolve(); - }); - }); - } catch { - // ignore shutdown errors - } - - try { - await prisma.$disconnect(); - } catch { - // ignore shutdown errors - } - - process.exit(0); -} - -process.on("SIGINT", () => { - void shutdown("SIGINT"); -}); - -process.on("SIGTERM", () => { - void shutdown("SIGTERM"); -}); diff --git a/backend/src/server.ts b/backend/src/server.ts deleted file mode 100644 index 95618d1..0000000 --- a/backend/src/server.ts +++ /dev/null @@ -1,265 +0,0 @@ -import { createServer, type IncomingMessage, type ServerResponse } from "node:http"; -import { buildSchema, graphql } from "graphql"; -import { - ingestTelegramInbound, - reportTelegramOutbound, - requestTelegramOutbound, - syncCalendarPredueTimeline, - type TelegramInboundEnvelope, - type TelegramOutboundReport, - type TelegramOutboundRequest, -} from "./service"; - -const PORT = Number(process.env.PORT || 8090); -const MAX_BODY_SIZE_BYTES = Number(process.env.MAX_BODY_SIZE_BYTES || 2 * 1024 * 1024); -const GRAPHQL_SHARED_SECRET = String(process.env.BACKEND_GRAPHQL_SHARED_SECRET || "").trim(); - -const schema = buildSchema(` - type Query { - health: Health! - } - - type Health { - ok: Boolean! - service: String! - now: String! - } - - type MutationResult { - ok: Boolean! - message: String! - runId: String - omniMessageId: String - } - - type SchedulerSyncResult { - ok: Boolean! - message: String! - now: String! - scanned: Int! - updated: Int! - skippedBeforeWindow: Int! - skippedLocked: Boolean! - preDueMinutes: Int! - lookbackMinutes: Int! - lookaheadMinutes: Int! - lockKey: Int! - } - - input TelegramInboundInput { - version: Int! - idempotencyKey: String! - provider: String! - channel: String! - direction: String! - providerEventId: String! - providerMessageId: String - eventType: String! - occurredAt: String! - receivedAt: String! - payloadRawJson: String! - payloadNormalizedJson: String! - } - - input TelegramOutboundReportInput { - omniMessageId: String! - status: String! - providerMessageId: String - error: String - responseJson: String - } - - input TelegramOutboundTaskInput { - omniMessageId: String! - chatId: String! - text: String! - businessConnectionId: String - } - - type Mutation { - ingestTelegramInbound(input: TelegramInboundInput!): MutationResult! - reportTelegramOutbound(input: TelegramOutboundReportInput!): MutationResult! - requestTelegramOutbound(input: TelegramOutboundTaskInput!): MutationResult! - syncCalendarPredueTimeline: SchedulerSyncResult! - } -`); - -function writeJson(res: ServerResponse, statusCode: number, body: unknown) { - res.statusCode = statusCode; - res.setHeader("content-type", "application/json; charset=utf-8"); - res.end(JSON.stringify(body)); -} - -function isGraphqlAuthorized(req: IncomingMessage) { - if (!GRAPHQL_SHARED_SECRET) return true; - const incoming = String(req.headers["x-graphql-secret"] || "").trim(); - return incoming !== "" && incoming === GRAPHQL_SHARED_SECRET; -} - -async function readJsonBody(req: IncomingMessage): Promise { - const chunks: Buffer[] = []; - let total = 0; - - for await (const chunk of req) { - const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk); - total += buf.length; - if (total > MAX_BODY_SIZE_BYTES) { - throw new Error(`payload_too_large:${MAX_BODY_SIZE_BYTES}`); - } - chunks.push(buf); - } - - const raw = Buffer.concat(chunks).toString("utf8"); - if (!raw) return {}; - return JSON.parse(raw); -} - -function parseJsonField(raw: string, fieldName: string): T { - try { - return JSON.parse(raw) as T; - } catch { - throw new Error(`${fieldName} must be valid JSON string`); - } -} - -const root = { - health: () => ({ - ok: true, - service: "backend", - now: new Date().toISOString(), - }), - - ingestTelegramInbound: async ({ input }: { input: any }) => { - const envelope: TelegramInboundEnvelope = { - version: Number(input.version ?? 1), - idempotencyKey: String(input.idempotencyKey ?? ""), - provider: String(input.provider ?? ""), - channel: String(input.channel ?? ""), - direction: String(input.direction ?? "IN") === "OUT" ? "OUT" : "IN", - providerEventId: String(input.providerEventId ?? ""), - providerMessageId: input.providerMessageId != null ? String(input.providerMessageId) : null, - eventType: String(input.eventType ?? ""), - occurredAt: String(input.occurredAt ?? new Date().toISOString()), - receivedAt: String(input.receivedAt ?? new Date().toISOString()), - payloadRaw: parseJsonField(input.payloadRawJson, "payloadRawJson"), - payloadNormalized: parseJsonField(input.payloadNormalizedJson, "payloadNormalizedJson"), - }; - - const result = await ingestTelegramInbound(envelope); - return { - ok: result.ok, - message: result.message, - omniMessageId: (result as any).omniMessageId ?? null, - runId: null, - }; - }, - - reportTelegramOutbound: async ({ input }: { input: any }) => { - const payload: TelegramOutboundReport = { - omniMessageId: String(input.omniMessageId ?? ""), - status: String(input.status ?? "FAILED"), - providerMessageId: input.providerMessageId != null ? String(input.providerMessageId) : null, - error: input.error != null ? String(input.error) : null, - responseJson: input.responseJson != null ? String(input.responseJson) : null, - }; - - const result = await reportTelegramOutbound(payload); - return { - ok: result.ok, - message: result.message, - runId: null, - omniMessageId: null, - }; - }, - - requestTelegramOutbound: async ({ input }: { input: any }) => { - const payload: TelegramOutboundRequest = { - omniMessageId: String(input.omniMessageId ?? ""), - chatId: String(input.chatId ?? ""), - text: String(input.text ?? ""), - businessConnectionId: input.businessConnectionId != null ? String(input.businessConnectionId) : null, - }; - - const result = await requestTelegramOutbound(payload); - return { - ok: result.ok, - message: result.message, - runId: result.runId ?? null, - omniMessageId: null, - }; - }, - - syncCalendarPredueTimeline: async () => { - const result = await syncCalendarPredueTimeline(); - return { - ok: result.ok, - message: result.message, - now: result.now, - scanned: result.scanned, - updated: result.updated, - skippedBeforeWindow: result.skippedBeforeWindow, - skippedLocked: result.skippedLocked, - preDueMinutes: result.preDueMinutes, - lookbackMinutes: result.lookbackMinutes, - lookaheadMinutes: result.lookaheadMinutes, - lockKey: result.lockKey, - }; - }, -}; - -export function startServer() { - const server = createServer(async (req, res) => { - if (!req.url || !req.method) { - writeJson(res, 404, { ok: false, error: "not_found" }); - return; - } - - if (req.url === "/health" && req.method === "GET") { - writeJson(res, 200, { - ok: true, - service: "backend", - now: new Date().toISOString(), - }); - return; - } - - if (req.url === "/graphql" && req.method === "POST") { - if (!isGraphqlAuthorized(req)) { - writeJson(res, 401, { errors: [{ message: "unauthorized" }] }); - return; - } - - try { - const body = (await readJsonBody(req)) as { - query?: string; - variables?: Record; - operationName?: string; - }; - - const result = await graphql({ - schema, - source: String(body.query || ""), - rootValue: root, - variableValues: body.variables || {}, - operationName: body.operationName, - }); - - writeJson(res, 200, result); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - const statusCode = message.startsWith("payload_too_large:") ? 413 : 400; - writeJson(res, statusCode, { errors: [{ message }] }); - } - - return; - } - - writeJson(res, 404, { ok: false, error: "not_found" }); - }); - - server.listen(PORT, "0.0.0.0", () => { - console.log(`[backend] listening on :${PORT}`); - }); - - return server; -} diff --git a/backend/src/service.ts b/backend/src/service.ts deleted file mode 100644 index cf18269..0000000 --- a/backend/src/service.ts +++ /dev/null @@ -1,637 +0,0 @@ -type MessageDirection = "IN" | "OUT"; -type OmniMessageStatus = "PENDING" | "SENT" | "FAILED" | "DELIVERED" | "READ"; -import { prisma } from "./utils/prisma"; - -export type TelegramInboundEnvelope = { - version: number; - idempotencyKey: string; - provider: string; - channel: string; - direction: "IN" | "OUT"; - providerEventId: string; - providerMessageId: string | null; - eventType: string; - occurredAt: string; - receivedAt: string; - payloadRaw: unknown; - payloadNormalized: { - threadExternalId: string | null; - contactExternalId: string | null; - text: string | null; - businessConnectionId: string | null; - [key: string]: unknown; - }; -}; - -export type TelegramOutboundReport = { - omniMessageId: string; - status: string; - providerMessageId?: string | null; - error?: string | null; - responseJson?: string | null; -}; - -export type TelegramOutboundRequest = { - omniMessageId: string; - chatId: string; - text: string; - businessConnectionId?: string | null; -}; - -export type CalendarPredueSyncResult = { - ok: boolean; - message: string; - now: string; - scanned: number; - updated: number; - skippedBeforeWindow: number; - skippedLocked: boolean; - preDueMinutes: number; - lookbackMinutes: number; - lookaheadMinutes: number; - lockKey: number; -}; - -function asString(value: unknown) { - if (typeof value !== "string") return null; - const v = value.trim(); - return v || null; -} - -function parseDate(value: string) { - const d = new Date(value); - if (Number.isNaN(d.getTime())) return new Date(); - return d; -} - -function normalizeDirection(value: string): MessageDirection { - return value === "OUT" ? "OUT" : "IN"; -} - -function readIntEnv(name: string, defaultValue: number) { - const raw = asString(process.env[name]); - if (!raw) return defaultValue; - const parsed = Number.parseInt(raw, 10); - return Number.isFinite(parsed) ? parsed : defaultValue; -} - -async function resolveTeamId(envelope: TelegramInboundEnvelope) { - const n = envelope.payloadNormalized; - const bcId = asString(n.businessConnectionId); - - if (bcId) { - const linked = await prisma.telegramBusinessConnection.findFirst({ - where: { businessConnectionId: bcId }, - orderBy: { updatedAt: "desc" }, - select: { teamId: true }, - }); - if (linked?.teamId) return linked.teamId; - } - - const externalContactId = asString(n.contactExternalId) ?? asString(n.threadExternalId); - if (externalContactId) { - const linked = await prisma.telegramBusinessConnection.findFirst({ - where: { businessConnectionId: `link:${externalContactId}` }, - orderBy: { updatedAt: "desc" }, - select: { teamId: true }, - }); - if (linked?.teamId) return linked.teamId; - } - - const fallback = asString(process.env.DEFAULT_TEAM_ID); - if (fallback) return fallback; - - const firstTeam = await prisma.team.findFirst({ - orderBy: { createdAt: "asc" }, - select: { id: true }, - }); - - return firstTeam?.id ?? null; -} - -async function resolveContact(input: { - teamId: string; - externalContactId: string; - displayName: string; - avatarUrl: string | null; -}) { - const existing = await prisma.omniContactIdentity.findFirst({ - where: { - teamId: input.teamId, - channel: "TELEGRAM", - externalId: input.externalContactId, - }, - select: { contactId: true }, - }); - - if (existing?.contactId) { - return existing.contactId; - } - - const contact = await prisma.contact.create({ - data: { - teamId: input.teamId, - name: input.displayName, - avatarUrl: input.avatarUrl, - }, - select: { id: true }, - }); - - try { - await prisma.omniContactIdentity.create({ - data: { - teamId: input.teamId, - contactId: contact.id, - channel: "TELEGRAM", - externalId: input.externalContactId, - }, - }); - return contact.id; - } catch { - const concurrent = await prisma.omniContactIdentity.findFirst({ - where: { - teamId: input.teamId, - channel: "TELEGRAM", - externalId: input.externalContactId, - }, - select: { contactId: true }, - }); - if (concurrent?.contactId) { - await prisma.contact.delete({ where: { id: contact.id } }).catch(() => undefined); - return concurrent.contactId; - } - - throw new Error("failed to create telegram contact identity"); - } -} - -async function upsertThread(input: { - teamId: string; - contactId: string; - externalChatId: string; - businessConnectionId: string | null; - title: string | null; -}) { - const existing = await prisma.omniThread.findFirst({ - where: { - teamId: input.teamId, - channel: "TELEGRAM", - externalChatId: input.externalChatId, - businessConnectionId: input.businessConnectionId, - }, - select: { id: true }, - }); - - if (existing) { - await prisma.omniThread.update({ - where: { id: existing.id }, - data: { - contactId: input.contactId, - ...(input.title ? { title: input.title } : {}), - }, - select: { id: true }, - }); - return existing.id; - } - - try { - const created = await prisma.omniThread.create({ - data: { - teamId: input.teamId, - contactId: input.contactId, - channel: "TELEGRAM", - externalChatId: input.externalChatId, - businessConnectionId: input.businessConnectionId, - title: input.title, - }, - select: { id: true }, - }); - return created.id; - } catch { - const concurrent = await prisma.omniThread.findFirst({ - where: { - teamId: input.teamId, - channel: "TELEGRAM", - externalChatId: input.externalChatId, - businessConnectionId: input.businessConnectionId, - }, - select: { id: true }, - }); - - if (concurrent?.id) return concurrent.id; - throw new Error("failed to upsert telegram thread"); - } -} - -async function upsertContactInbox(input: { - teamId: string; - contactId: string; - sourceExternalId: string; - title: string | null; -}) { - const inbox = await prisma.contactInbox.upsert({ - where: { - teamId_channel_sourceExternalId: { - teamId: input.teamId, - channel: "TELEGRAM", - sourceExternalId: input.sourceExternalId, - }, - }, - create: { - teamId: input.teamId, - contactId: input.contactId, - channel: "TELEGRAM", - sourceExternalId: input.sourceExternalId, - title: input.title, - }, - update: { - contactId: input.contactId, - ...(input.title ? { title: input.title } : {}), - }, - select: { id: true }, - }); - - return inbox.id; -} - -async function markRead(teamId: string, externalChatId: string) { - const thread = await prisma.omniThread.findFirst({ - where: { - teamId, - channel: "TELEGRAM", - externalChatId, - }, - select: { contactId: true }, - }); - if (!thread) return; - - const members = await prisma.teamMember.findMany({ - where: { teamId }, - select: { userId: true }, - }); - - const readAt = new Date(); - await Promise.all( - members.map((member: { userId: string }) => - prisma.contactThreadRead.upsert({ - where: { - userId_contactId: { - userId: member.userId, - contactId: thread.contactId, - }, - }, - create: { - teamId, - userId: member.userId, - contactId: thread.contactId, - readAt, - }, - update: { readAt }, - }), - ), - ); -} - -export async function ingestTelegramInbound(envelope: TelegramInboundEnvelope) { - if (envelope.channel !== "TELEGRAM") { - return { ok: true, message: "skip_non_telegram" }; - } - - const teamId = await resolveTeamId(envelope); - if (!teamId) { - throw new Error("team_not_resolved"); - } - - const n = envelope.payloadNormalized; - const externalChatId = asString(n.threadExternalId) ?? asString(n.contactExternalId); - if (!externalChatId) { - throw new Error("thread_external_id_required"); - } - - if (envelope.eventType === "read_business_message") { - await markRead(teamId, externalChatId); - return { ok: true, message: "read_marked" }; - } - - const externalContactId = asString(n.contactExternalId) ?? externalChatId; - const businessConnectionId = asString(n.businessConnectionId); - const text = asString(n.text) ?? "[no text]"; - const occurredAt = parseDate(envelope.occurredAt); - const direction = normalizeDirection(envelope.direction); - - const contactFirstName = asString(n.contactFirstName); - const contactLastName = asString(n.contactLastName); - const contactUsername = asString(n.contactUsername); - const fallbackName = `Telegram ${externalContactId}`; - const displayName = - [contactFirstName, contactLastName].filter(Boolean).join(" ") || - (contactUsername ? `@${contactUsername.replace(/^@/, "")}` : null) || - fallbackName; - - const contactId = await resolveContact({ - teamId, - externalContactId, - displayName, - avatarUrl: asString(n.contactAvatarUrl), - }); - - const threadId = await upsertThread({ - teamId, - contactId, - externalChatId, - businessConnectionId, - title: asString(n.chatTitle), - }); - - const contactInboxId = await upsertContactInbox({ - teamId, - contactId, - sourceExternalId: externalChatId, - title: asString(n.chatTitle), - }); - - const rawEnvelope: Record = { - version: envelope.version, - source: "backend.graphql.ingestTelegramInbound", - provider: envelope.provider, - channel: envelope.channel, - direction, - providerEventId: envelope.providerEventId, - receivedAt: envelope.receivedAt, - occurredAt: occurredAt.toISOString(), - payloadNormalized: n, - payloadRaw: envelope.payloadRaw ?? null, - }; - - let omniMessageId: string; - if (envelope.providerMessageId) { - const message = await prisma.omniMessage.upsert({ - where: { - threadId_providerMessageId: { - threadId, - providerMessageId: envelope.providerMessageId, - }, - }, - create: { - teamId, - contactId, - threadId, - direction, - channel: "TELEGRAM", - status: "DELIVERED", - text, - providerMessageId: envelope.providerMessageId, - providerUpdateId: envelope.providerEventId, - rawJson: rawEnvelope, - occurredAt, - }, - update: { - text, - providerUpdateId: envelope.providerEventId, - rawJson: rawEnvelope, - occurredAt, - }, - select: { id: true }, - }); - omniMessageId = message.id; - } else { - const message = await prisma.omniMessage.create({ - data: { - teamId, - contactId, - threadId, - direction, - channel: "TELEGRAM", - status: "DELIVERED", - text, - providerMessageId: null, - providerUpdateId: envelope.providerEventId, - rawJson: rawEnvelope, - occurredAt, - }, - select: { id: true }, - }); - omniMessageId = message.id; - } - - await prisma.contactMessage.create({ - data: { - contactId, - contactInboxId, - kind: "MESSAGE", - direction, - channel: "TELEGRAM", - content: text, - occurredAt, - }, - }); - - return { ok: true, message: "inbound_ingested", omniMessageId }; -} - -export async function reportTelegramOutbound(input: TelegramOutboundReport) { - const statusRaw = input.status.trim().toUpperCase(); - const status: OmniMessageStatus = - statusRaw === "SENT" || - statusRaw === "FAILED" || - statusRaw === "DELIVERED" || - statusRaw === "READ" || - statusRaw === "PENDING" - ? (statusRaw as OmniMessageStatus) - : "FAILED"; - - const existing = await prisma.omniMessage.findUnique({ - where: { id: input.omniMessageId }, - select: { rawJson: true }, - }); - - const raw = (existing?.rawJson && typeof existing.rawJson === "object" && !Array.isArray(existing.rawJson) - ? (existing.rawJson as Record) - : {}) as Record; - - await prisma.omniMessage.update({ - where: { id: input.omniMessageId }, - data: { - status, - ...(input.providerMessageId ? { providerMessageId: input.providerMessageId } : {}), - rawJson: { - ...raw, - telegramWorker: { - reportedAt: new Date().toISOString(), - status, - error: input.error ?? null, - response: (() => { - if (!input.responseJson) return null; - try { - return JSON.parse(input.responseJson); - } catch { - return input.responseJson; - } - })(), - }, - }, - }, - }); - - return { ok: true, message: "outbound_reported" }; -} - -async function callTelegramBackendGraphql(query: string, variables: Record) { - const url = asString(process.env.TELEGRAM_BACKEND_GRAPHQL_URL); - if (!url) { - throw new Error("TELEGRAM_BACKEND_GRAPHQL_URL is required"); - } - - const headers: Record = { - "content-type": "application/json", - }; - - const secret = asString(process.env.TELEGRAM_BACKEND_GRAPHQL_SHARED_SECRET); - if (secret) { - headers["x-graphql-secret"] = secret; - } - - const response = await fetch(url, { - method: "POST", - headers, - body: JSON.stringify({ query, variables }), - }); - - const payload = (await response.json()) as { data?: T; errors?: Array<{ message?: string }> }; - if (!response.ok || payload.errors?.length) { - const errorMessage = payload.errors?.map((e) => e.message).filter(Boolean).join("; ") || `HTTP ${response.status}`; - throw new Error(errorMessage); - } - - return payload.data as T; -} - -export async function requestTelegramOutbound(input: TelegramOutboundRequest) { - type Out = { - enqueueTelegramOutbound: { - ok: boolean; - message: string; - runId?: string | null; - }; - }; - - const query = `mutation Enqueue($input: TelegramOutboundTaskInput!) { - enqueueTelegramOutbound(input: $input) { - ok - message - runId - } - }`; - - const data = await callTelegramBackendGraphql(query, { input }); - const result = data.enqueueTelegramOutbound; - if (!result?.ok) { - throw new Error(result?.message || "enqueue failed"); - } - - return { ok: true, message: "outbound_enqueued", runId: result.runId ?? null }; -} - -export async function syncCalendarPredueTimeline(): Promise { - const preDueMinutes = Math.max(1, readIntEnv("TIMELINE_EVENT_PREDUE_MINUTES", 30)); - const lookbackMinutes = Math.max(preDueMinutes, readIntEnv("TIMELINE_EVENT_LOOKBACK_MINUTES", 180)); - const lookaheadMinutes = Math.max(preDueMinutes, readIntEnv("TIMELINE_EVENT_LOOKAHEAD_MINUTES", 1440)); - const lockKey = readIntEnv("TIMELINE_SCHEDULER_LOCK_KEY", 603001); - - const now = new Date(); - const rangeStart = new Date(now.getTime() - lookbackMinutes * 60_000); - const rangeEnd = new Date(now.getTime() + lookaheadMinutes * 60_000); - - const lockRows = await prisma.$queryRaw>` - SELECT pg_try_advisory_lock(${lockKey}) AS locked - `; - const locked = Boolean(lockRows?.[0]?.locked); - - if (!locked) { - return { - ok: true, - message: "lock_busy_skip", - now: now.toISOString(), - scanned: 0, - updated: 0, - skippedBeforeWindow: 0, - skippedLocked: true, - preDueMinutes, - lookbackMinutes, - lookaheadMinutes, - lockKey, - }; - } - - try { - const events = await prisma.calendarEvent.findMany({ - where: { - isArchived: false, - contactId: { not: null }, - startsAt: { - gte: rangeStart, - lte: rangeEnd, - }, - }, - orderBy: { startsAt: "asc" }, - select: { - id: true, - teamId: true, - contactId: true, - startsAt: true, - }, - }); - - let updated = 0; - let skippedBeforeWindow = 0; - - for (const event of events) { - if (!event.contactId) continue; - - const preDueAt = new Date(event.startsAt.getTime() - preDueMinutes * 60_000); - if (now < preDueAt) { - skippedBeforeWindow += 1; - continue; - } - - await prisma.clientTimelineEntry.upsert({ - where: { - teamId_contentType_contentId: { - teamId: event.teamId, - contentType: "CALENDAR_EVENT", - contentId: event.id, - }, - }, - create: { - teamId: event.teamId, - contactId: event.contactId, - contentType: "CALENDAR_EVENT", - contentId: event.id, - datetime: preDueAt, - }, - update: { - contactId: event.contactId, - datetime: preDueAt, - }, - }); - - updated += 1; - } - - return { - ok: true, - message: "calendar_predue_synced", - now: now.toISOString(), - scanned: events.length, - updated, - skippedBeforeWindow, - skippedLocked: false, - preDueMinutes, - lookbackMinutes, - lookaheadMinutes, - lockKey, - }; - } finally { - await prisma.$queryRaw`SELECT pg_advisory_unlock(${lockKey})`; - } -} diff --git a/backend/src/utils/prisma.ts b/backend/src/utils/prisma.ts deleted file mode 100644 index 27c97b7..0000000 --- a/backend/src/utils/prisma.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { PrismaClient } from "@prisma/client"; - -declare global { - // eslint-disable-next-line no-var - var __omniChatPrisma: PrismaClient | undefined; -} - -export const prisma = - globalThis.__omniChatPrisma ?? - new PrismaClient({ - log: ["error", "warn"], - }); - -if (process.env.NODE_ENV !== "production") { - globalThis.__omniChatPrisma = prisma; -} diff --git a/backend/tsconfig.json b/backend/tsconfig.json deleted file mode 100644 index 5541a4b..0000000 --- a/backend/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "Bundler", - "strict": true, - "skipLibCheck": true, - "esModuleInterop": true, - "types": ["node"], - "resolveJsonModule": true, - "verbatimModuleSyntax": true - }, - "include": ["src/**/*.ts"] -} diff --git a/backend_worker b/backend_worker new file mode 160000 index 0000000..6536179 --- /dev/null +++ b/backend_worker @@ -0,0 +1 @@ +Subproject commit 653617983a5fda9abb2e38d6cb75de0aa4490e0c diff --git a/backend_worker/Dockerfile b/backend_worker/Dockerfile deleted file mode 100644 index 5a94641..0000000 --- a/backend_worker/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM node:22-alpine - -WORKDIR /app - -RUN apk add --no-cache curl jq - -COPY package*.json ./ -RUN npm ci - -COPY src ./src -COPY scripts ./scripts -COPY tsconfig.json ./tsconfig.json - -ENV NODE_ENV=production - -CMD ["sh", "-lc", ". /app/scripts/load-vault-env.sh && npm run start"] diff --git a/backend_worker/README.md b/backend_worker/README.md deleted file mode 100644 index 07e1126..0000000 --- a/backend_worker/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# backend_worker - -Hatchet worker для периодических backend-задач. - -## Назначение - -- запускает cron workflow `backend-calendar-timeline-scheduler`; -- вызывает `backend` GraphQL mutation `syncCalendarPredueTimeline`; -- заменяет legacy `schedulers/` сервис для предзаписи календарных событий в `ClientTimelineEntry`. - -## Переменные окружения - -- `BACKEND_GRAPHQL_URL` (required) -- `BACKEND_GRAPHQL_SHARED_SECRET` (optional) -- `BACKEND_TIMELINE_SYNC_CRON` (default: `* * * * *`) -- `HATCHET_CLIENT_TOKEN` (required) -- `HATCHET_CLIENT_TLS_STRATEGY` (optional, например `none` для self-host без TLS) -- `HATCHET_CLIENT_HOST_PORT` (optional, например `hatchet-engine:7070`) -- `HATCHET_CLIENT_API_URL` (optional) - -## Скрипты - -- `npm run start` — запуск Hatchet worker. -- `npm run typecheck` — проверка TypeScript. diff --git a/backend_worker/package-lock.json b/backend_worker/package-lock.json deleted file mode 100644 index d23c4fe..0000000 --- a/backend_worker/package-lock.json +++ /dev/null @@ -1,1456 +0,0 @@ -{ - "name": "crm-backend-worker", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "crm-backend-worker", - "dependencies": { - "@hatchet-dev/typescript-sdk": "^1.15.2" - }, - "devDependencies": { - "@types/node": "^22.13.9", - "tsx": "^4.20.5", - "typescript": "^5.9.2" - } - }, - "node_modules/@bufbuild/protobuf": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.11.0.tgz", - "integrity": "sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==", - "license": "(Apache-2.0 AND BSD-3-Clause)" - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", - "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", - "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", - "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", - "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", - "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", - "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", - "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", - "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", - "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", - "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", - "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", - "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", - "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", - "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", - "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", - "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", - "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", - "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", - "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", - "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", - "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", - "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", - "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", - "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", - "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", - "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@grpc/grpc-js": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", - "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==", - "license": "Apache-2.0", - "dependencies": { - "@grpc/proto-loader": "^0.8.0", - "@js-sdsl/ordered-map": "^4.4.2" - }, - "engines": { - "node": ">=12.10.0" - } - }, - "node_modules/@grpc/proto-loader": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", - "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", - "license": "Apache-2.0", - "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.5.3", - "yargs": "^17.7.2" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@hatchet-dev/typescript-sdk": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/@hatchet-dev/typescript-sdk/-/typescript-sdk-1.15.2.tgz", - "integrity": "sha512-y9PY8m7thGBoIjJdmM+0x6F8qT8u+ZdrmzOQK3jp/TpCspR8PtsKmVzLc4zQiRBtzzSw0GVJbbA9xZG6zZlz3A==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@bufbuild/protobuf": "^2.2.5", - "@types/qs": "^6.9.18", - "abort-controller-x": "^0.4.3", - "axios": "^1.13.5", - "long": "^5.3.1", - "nice-grpc": "^2.1.12", - "nice-grpc-common": "^2.0.2", - "protobufjs": "^7.4.0", - "qs": "^6.14.2", - "semver": "^7.7.1", - "yaml": "^2.7.1", - "zod": "^3.24.2", - "zod-to-json-schema": "^3.24.1" - }, - "optionalDependencies": { - "prom-client": "^15.1.3" - } - }, - "node_modules/@js-sdsl/ordered-map": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", - "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": ">=8.0.0" - } - }, - "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/@types/node": { - "version": "22.19.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", - "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/qs": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", - "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", - "license": "MIT" - }, - "node_modules/abort-controller-x": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/abort-controller-x/-/abort-controller-x-0.4.3.tgz", - "integrity": "sha512-VtUwTNU8fpMwvWGn4xE93ywbogTYsuT+AUxAXOeelbXuQVIwNmC5YLeho9sH4vZ4ITW8414TTAOG1nW6uIVHCA==", - "license": "MIT" - }, - "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/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/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", - "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.11", - "form-data": "^4.0.5", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/bintrees": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", - "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==", - "license": "MIT", - "optional": true - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", - "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.3", - "@esbuild/android-arm": "0.27.3", - "@esbuild/android-arm64": "0.27.3", - "@esbuild/android-x64": "0.27.3", - "@esbuild/darwin-arm64": "0.27.3", - "@esbuild/darwin-x64": "0.27.3", - "@esbuild/freebsd-arm64": "0.27.3", - "@esbuild/freebsd-x64": "0.27.3", - "@esbuild/linux-arm": "0.27.3", - "@esbuild/linux-arm64": "0.27.3", - "@esbuild/linux-ia32": "0.27.3", - "@esbuild/linux-loong64": "0.27.3", - "@esbuild/linux-mips64el": "0.27.3", - "@esbuild/linux-ppc64": "0.27.3", - "@esbuild/linux-riscv64": "0.27.3", - "@esbuild/linux-s390x": "0.27.3", - "@esbuild/linux-x64": "0.27.3", - "@esbuild/netbsd-arm64": "0.27.3", - "@esbuild/netbsd-x64": "0.27.3", - "@esbuild/openbsd-arm64": "0.27.3", - "@esbuild/openbsd-x64": "0.27.3", - "@esbuild/openharmony-arm64": "0.27.3", - "@esbuild/sunos-x64": "0.27.3", - "@esbuild/win32-arm64": "0.27.3", - "@esbuild/win32-ia32": "0.27.3", - "@esbuild/win32-x64": "0.27.3" - } - }, - "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/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-tsconfig": { - "version": "4.13.6", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", - "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "license": "MIT" - }, - "node_modules/long": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "license": "Apache-2.0" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/nice-grpc": { - "version": "2.1.14", - "resolved": "https://registry.npmjs.org/nice-grpc/-/nice-grpc-2.1.14.tgz", - "integrity": "sha512-GK9pKNxlvnU5FAdaw7i2FFuR9CqBspcE+if2tqnKXBcE0R8525wj4BZvfcwj7FjvqbssqKxRHt2nwedalbJlww==", - "license": "MIT", - "dependencies": { - "@grpc/grpc-js": "^1.14.0", - "abort-controller-x": "^0.4.0", - "nice-grpc-common": "^2.0.2" - } - }, - "node_modules/nice-grpc-common": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/nice-grpc-common/-/nice-grpc-common-2.0.2.tgz", - "integrity": "sha512-7RNWbls5kAL1QVUOXvBsv1uO0wPQK3lHv+cY1gwkTzirnG1Nop4cBJZubpgziNbaVc/bl9QJcyvsf/NQxa3rjQ==", - "license": "MIT", - "dependencies": { - "ts-error": "^1.0.6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/prom-client": { - "version": "15.1.3", - "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-15.1.3.tgz", - "integrity": "sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@opentelemetry/api": "^1.4.0", - "tdigest": "^0.1.1" - }, - "engines": { - "node": "^16 || ^18 || >=20" - } - }, - "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-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/qs": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", - "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tdigest": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", - "integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==", - "license": "MIT", - "optional": true, - "dependencies": { - "bintrees": "1.0.2" - } - }, - "node_modules/ts-error": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/ts-error/-/ts-error-1.0.6.tgz", - "integrity": "sha512-tLJxacIQUM82IR7JO1UUkKlYuUTmoY9HBJAmNWFzheSlDS5SPMcNIepejHJa4BpPQLAcbRhRf3GDJzyj6rbKvA==", - "license": "MIT" - }, - "node_modules/tsx": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", - "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.27.0", - "get-tsconfig": "^4.7.5" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-to-json-schema": { - "version": "3.25.1", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", - "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.25 || ^4" - } - } - } -} diff --git a/backend_worker/package.json b/backend_worker/package.json deleted file mode 100644 index 4f06dfa..0000000 --- a/backend_worker/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "crm-backend-worker", - "private": true, - "type": "module", - "scripts": { - "start": "tsx src/hatchet/worker.ts", - "typecheck": "tsc --noEmit" - }, - "dependencies": { - "@hatchet-dev/typescript-sdk": "^1.15.2" - }, - "devDependencies": { - "@types/node": "^22.13.9", - "tsx": "^4.20.5", - "typescript": "^5.9.2" - } -} diff --git a/backend_worker/scripts/load-vault-env.sh b/backend_worker/scripts/load-vault-env.sh deleted file mode 100755 index 086c1e8..0000000 --- a/backend_worker/scripts/load-vault-env.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/sh -set -eu - -log() { - printf '%s\n' "$*" >&2 -} - -VAULT_ENABLED="${VAULT_ENABLED:-auto}" -if [ "$VAULT_ENABLED" = "false" ] || [ "$VAULT_ENABLED" = "0" ]; then - exit 0 -fi - -if [ -z "${VAULT_ADDR:-}" ] || [ -z "${VAULT_TOKEN:-}" ]; then - if [ "$VAULT_ENABLED" = "true" ] || [ "$VAULT_ENABLED" = "1" ]; then - log "Vault bootstrap is required but VAULT_ADDR or VAULT_TOKEN is missing." - exit 1 - fi - exit 0 -fi - -if ! command -v curl >/dev/null 2>&1 || ! command -v jq >/dev/null 2>&1; then - log "Vault bootstrap requires curl and jq." - exit 1 -fi - -VAULT_KV_MOUNT="${VAULT_KV_MOUNT:-secret}" - -load_secret_path() { - path="$1" - source_name="$2" - if [ -z "$path" ]; then - return 0 - fi - - url="${VAULT_ADDR%/}/v1/${VAULT_KV_MOUNT}/data/${path}" - response="$(curl -fsS -H "X-Vault-Token: $VAULT_TOKEN" "$url")" || { - log "Failed to load Vault path ${VAULT_KV_MOUNT}/${path}." - return 1 - } - - encoded_items="$(printf '%s' "$response" | jq -r '.data.data // {} | to_entries[]? | @base64')" - if [ -z "$encoded_items" ]; then - return 0 - fi - - old_ifs="${IFS}" - IFS=' -' - for encoded_item in $encoded_items; do - key="$(printf '%s' "$encoded_item" | base64 -d | jq -r '.key')" - value="$(printf '%s' "$encoded_item" | base64 -d | jq -r '.value | tostring')" - export "$key=$value" - done - IFS="${old_ifs}" - - log "Loaded Vault ${source_name} secrets from ${VAULT_KV_MOUNT}/${path}." -} - -load_secret_path "${VAULT_SHARED_PATH:-}" "shared" -load_secret_path "${VAULT_PROJECT_PATH:-}" "project" diff --git a/backend_worker/src/hatchet/client.ts b/backend_worker/src/hatchet/client.ts deleted file mode 100644 index 128a422..0000000 --- a/backend_worker/src/hatchet/client.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { HatchetClient } from "@hatchet-dev/typescript-sdk/v1"; - -export const hatchet = HatchetClient.init(); diff --git a/backend_worker/src/hatchet/worker.ts b/backend_worker/src/hatchet/worker.ts deleted file mode 100644 index c8d2466..0000000 --- a/backend_worker/src/hatchet/worker.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { hatchet } from "./client"; -import { backendCalendarTimelineScheduler } from "./workflow"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - -async function main() { - const worker = await hatchet.worker("backend-worker", { - workflows: [backendCalendarTimelineScheduler], - }); - - await worker.start(); -} - -const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url); - -if (isMain) { - main().catch((error) => { - const message = error instanceof Error ? error.stack || error.message : String(error); - console.error(`[backend_worker/hatchet] worker failed: ${message}`); - process.exitCode = 1; - }); -} diff --git a/backend_worker/src/hatchet/workflow.ts b/backend_worker/src/hatchet/workflow.ts deleted file mode 100644 index f7b18c4..0000000 --- a/backend_worker/src/hatchet/workflow.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { hatchet } from "./client"; - -type SyncCalendarPredueResult = { - syncCalendarPredueTimeline: { - ok: boolean; - message: string; - now: string; - scanned: number; - updated: number; - skippedBeforeWindow: number; - skippedLocked: boolean; - preDueMinutes: number; - lookbackMinutes: number; - lookaheadMinutes: number; - lockKey: number; - }; -}; - -type GraphqlResponse = { - data?: T; - errors?: Array<{ message?: string }>; -}; - -function asString(value: unknown) { - if (typeof value !== "string") return null; - const v = value.trim(); - return v || null; -} - -function requiredEnv(name: string) { - const value = asString(process.env[name]); - if (!value) { - throw new Error(`${name} is required`); - } - return value; -} - -async function callBackendSyncMutation() { - const url = requiredEnv("BACKEND_GRAPHQL_URL"); - const secret = asString(process.env.BACKEND_GRAPHQL_SHARED_SECRET); - - const headers: Record = { - "content-type": "application/json", - }; - - if (secret) { - headers["x-graphql-secret"] = secret; - } - - const query = `mutation SyncCalendarPredueTimeline { - syncCalendarPredueTimeline { - ok - message - now - scanned - updated - skippedBeforeWindow - skippedLocked - preDueMinutes - lookbackMinutes - lookaheadMinutes - lockKey - } - }`; - - const response = await fetch(url, { - method: "POST", - headers, - body: JSON.stringify({ - operationName: "SyncCalendarPredueTimeline", - query, - variables: {}, - }), - }); - - const payload = (await response.json()) as GraphqlResponse; - if (!response.ok || payload.errors?.length) { - const message = payload.errors?.map((error) => error.message).filter(Boolean).join("; ") || `HTTP ${response.status}`; - throw new Error(message); - } - - const result = payload.data?.syncCalendarPredueTimeline; - if (!result?.ok) { - throw new Error(result?.message || "syncCalendarPredueTimeline failed"); - } - - return result; -} - -const BACKEND_WORKER_CRON = asString(process.env.BACKEND_TIMELINE_SYNC_CRON) || "* * * * *"; - -export const backendCalendarTimelineScheduler = hatchet.workflow({ - name: "backend-calendar-timeline-scheduler", - on: { - cron: BACKEND_WORKER_CRON, - }, -}); - -backendCalendarTimelineScheduler.task({ - name: "sync-calendar-predue-timeline-in-backend", - retries: 6, - backoff: { - factor: 2, - maxSeconds: 60, - }, - fn: async (_, ctx) => { - const result = await callBackendSyncMutation(); - - await ctx.logger.info("backend timeline predue sync completed", { - scanned: result.scanned, - updated: result.updated, - skippedBeforeWindow: result.skippedBeforeWindow, - skippedLocked: result.skippedLocked, - now: result.now, - }); - - return result; - }, -}); diff --git a/backend_worker/tsconfig.json b/backend_worker/tsconfig.json deleted file mode 100644 index 5541a4b..0000000 --- a/backend_worker/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "Bundler", - "strict": true, - "skipLibCheck": true, - "esModuleInterop": true, - "types": ["node"], - "resolveJsonModule": true, - "verbatimModuleSyntax": true - }, - "include": ["src/**/*.ts"] -} diff --git a/frontend b/frontend new file mode 160000 index 0000000..e111852 --- /dev/null +++ b/frontend @@ -0,0 +1 @@ +Subproject commit e11185259fd67a9d1abd7c04b7814652bf068435 diff --git a/frontend/.dockerignore b/frontend/.dockerignore deleted file mode 100644 index dd158b4..0000000 --- a/frontend/.dockerignore +++ /dev/null @@ -1,9 +0,0 @@ -.git -.gitignore -node_modules -.nuxt -.output -.data -npm-debug.log* -dist -coverage diff --git a/frontend/.env.example b/frontend/.env.example deleted file mode 100644 index 91db93d..0000000 --- a/frontend/.env.example +++ /dev/null @@ -1,35 +0,0 @@ -DATABASE_URL="postgresql://postgres:postgres@localhost:5432/clientsflow?schema=public" -REDIS_URL="redis://localhost:6379" - -# Agent (LangGraph + OpenRouter) -OPENROUTER_API_KEY="" -OPENROUTER_BASE_URL="https://openrouter.ai/api/v1" -OPENROUTER_MODEL="openai/gpt-4o-mini" -# Optional headers for OpenRouter ranking/analytics -OPENROUTER_HTTP_REFERER="" -OPENROUTER_X_TITLE="clientsflow" -# Enable reasoning payload for models that support it: 1 or 0 -OPENROUTER_REASONING_ENABLED="0" - -# Langfuse local tracing (optional) -LANGFUSE_ENABLED="true" -LANGFUSE_BASE_URL="http://localhost:3001" -LANGFUSE_PUBLIC_KEY="pk-lf-local" -LANGFUSE_SECRET_KEY="sk-lf-local" - -# Optional fallback (OpenAI-compatible) -OPENAI_API_KEY="" -OPENAI_MODEL="gpt-4o-mini" -# "langgraph" (default) or "rule" -CF_AGENT_MODE="langgraph" -CF_WHISPER_MODEL="Xenova/whisper-small" -CF_WHISPER_LANGUAGE="ru" - -TELEGRAM_BOT_TOKEN="" -TELEGRAM_WEBHOOK_SECRET="" -TELEGRAM_DEFAULT_TEAM_ID="demo-team" - -# Frontend GraphQL endpoint for Apollo client runtime -GRAPHQL_HTTP_ENDPOINT="http://localhost:3000/api/graphql" -# Remote GraphQL schema URL for codegen (used by `pnpm codegen` / `npm run codegen`) -GRAPHQL_SCHEMA_URL="" diff --git a/frontend/.storybook/main.ts b/frontend/.storybook/main.ts deleted file mode 100644 index cdd6186..0000000 --- a/frontend/.storybook/main.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { StorybookConfig } from "@storybook/vue3-vite"; - -const config: StorybookConfig = { - stories: ["../components/**/*.stories.@(ts|tsx)"], - addons: ["@storybook/addon-essentials", "@storybook/addon-interactions"], - framework: { - name: "@storybook/vue3-vite", - options: {}, - }, -}; - -export default config; diff --git a/frontend/.storybook/preview.ts b/frontend/.storybook/preview.ts deleted file mode 100644 index 92dc76f..0000000 --- a/frontend/.storybook/preview.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { Preview } from "@storybook/vue3-vite"; -import "../assets/css/main.css"; - -const preview: Preview = { - parameters: { - actions: { argTypesRegex: "^on[A-Z].*" }, - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/i, - }, - }, - }, -}; - -export default preview; diff --git a/frontend/Dockerfile b/frontend/Dockerfile deleted file mode 100644 index 13c5266..0000000 --- a/frontend/Dockerfile +++ /dev/null @@ -1,33 +0,0 @@ -FROM node:22-bookworm-slim - -WORKDIR /app/frontend - -RUN apt-get update -y \ - && apt-get install -y --no-install-recommends openssl ca-certificates curl jq \ - && rm -rf /var/lib/apt/lists/* - -COPY package*.json ./ -RUN npm ci --ignore-scripts --legacy-peer-deps -RUN set -eux; \ - arch="$(dpkg --print-architecture)"; \ - if [ "$arch" = "amd64" ]; then \ - npm rebuild sharp --platform=linux --arch=x64 || npm install --no-save sharp --platform=linux --arch=x64; \ - elif [ "$arch" = "arm64" ]; then \ - npm rebuild sharp --platform=linux --arch=arm64 || npm install --no-save sharp --platform=linux --arch=arm64; \ - else \ - npm rebuild sharp || true; \ - fi - -COPY . . - -# Build server bundle at image build time. -RUN npm run postinstall && npm run build - -ENV NODE_ENV=production -ENV NITRO_HOST=0.0.0.0 -ENV NITRO_PORT=3000 - -EXPOSE 3000 - -# Keep schema in sync, then start Nitro production server. -CMD ["sh", "-lc", ". /app/frontend/scripts/load-vault-env.sh && npx prisma db push && node .output/server/index.mjs"] diff --git a/frontend/app/app.vue b/frontend/app/app.vue deleted file mode 100644 index f8eacfa..0000000 --- a/frontend/app/app.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/frontend/app/assets/css/main.css b/frontend/app/assets/css/main.css deleted file mode 100644 index 0dee3e4..0000000 --- a/frontend/app/assets/css/main.css +++ /dev/null @@ -1,16 +0,0 @@ -@import "tailwindcss"; -@plugin "daisyui"; - -:root { - --color-accent: #1e6bff; -} - -body { - min-height: 100vh; - background: - radial-gradient(circle at 100% 0%, rgba(30, 107, 255, 0.08), transparent 40%), - radial-gradient(circle at 0% 100%, rgba(30, 107, 255, 0.08), transparent 40%), - #f5f7fb; - color: #111827; -} - diff --git a/frontend/app/components/ContactCollaborativeEditor.client.stories.ts b/frontend/app/components/ContactCollaborativeEditor.client.stories.ts deleted file mode 100644 index da18023..0000000 --- a/frontend/app/components/ContactCollaborativeEditor.client.stories.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/vue3"; -import ContactCollaborativeEditor from "./ContactCollaborativeEditor.client.vue"; - -const meta: Meta = { - title: "Components/ContactCollaborativeEditor", - component: ContactCollaborativeEditor, - args: { - modelValue: "

Client summary draft...

", - room: "storybook-contact-editor-room", - placeholder: "Type here...", - plain: false, - }, -}; - -export default meta; - -type Story = StoryObj; - -export const Default: Story = {}; diff --git a/frontend/app/components/ContactCollaborativeEditor.client.vue b/frontend/app/components/ContactCollaborativeEditor.client.vue deleted file mode 100644 index af1ad41..0000000 --- a/frontend/app/components/ContactCollaborativeEditor.client.vue +++ /dev/null @@ -1,238 +0,0 @@ - - - - - diff --git a/frontend/app/components/workspace/CrmWorkspaceApp.vue b/frontend/app/components/workspace/CrmWorkspaceApp.vue deleted file mode 100644 index 9455361..0000000 --- a/frontend/app/components/workspace/CrmWorkspaceApp.vue +++ /dev/null @@ -1,2673 +0,0 @@ - - -