From 1fccccdc0690364e07361f05f9dd860a3a6eea6a Mon Sep 17 00:00:00 2001 From: IMvision12 Date: Thu, 26 Feb 2026 18:39:47 -0800 Subject: [PATCH 1/4] Add Slack, Microsoft Teams, and Signal platform support - Add Slack bot (src/platforms/slack.ts) using @slack/bolt with Socket Mode - Add Microsoft Teams bot (src/platforms/teams.ts) using botbuilder SDK - Add Signal bot (src/platforms/signal.ts) using signal-cli-rest-api - Add typing signalers for all three new platforms - Update auth flow with setup guides for Slack, Teams, and Signal - Update start command to handle new platform initialization - Add unit tests for SlackTypingSignaler, TeamsTypingSignaler, SignalTypingSignaler - Install @slack/bolt and botbuilder dependencies Made-with: Cursor --- package-lock.json | 2075 ++++++++++++++++++++++++++++- package.json | 7 +- src/cli/commands/auth.ts | 123 ++ src/cli/commands/start.ts | 57 +- src/platforms/signal.ts | 300 +++++ src/platforms/slack.ts | 321 +++++ src/platforms/teams.ts | 381 ++++++ src/shared/typing-signaler.ts | 105 ++ test/unit/typing-signaler.test.ts | 149 +++ 9 files changed, 3479 insertions(+), 39 deletions(-) create mode 100644 src/platforms/signal.ts create mode 100644 src/platforms/slack.ts create mode 100644 src/platforms/teams.ts diff --git a/package-lock.json b/package-lock.json index 619127d..a0533fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,9 @@ "dependencies": { "@anthropic-ai/sdk": "^0.74.0", "@google/generative-ai": "^0.24.1", + "@slack/bolt": "^4.6.0", "@whiskeysockets/baileys": "^7.0.0-rc.9", + "botbuilder": "^4.23.3", "chalk": "^4.1.2", "discord.js": "^14.25.1", "dotenv": "^16.3.1", @@ -58,6 +60,232 @@ } } }, + "node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", + "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz", + "integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==", + "license": "MIT", + "peer": true, + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-http-compat": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.3.2.tgz", + "integrity": "sha512-Tf6ltdKzOJEgxZeWLCjMxrxbodB/ZeCbzzA1A2qHbhzAjzjHoBVSUeSl/baT/oHAxhc4qdqVaDKnc2+iE932gw==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@azure/core-client": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.2.tgz", + "integrity": "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", + "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", + "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.0.tgz", + "integrity": "sha512-uWC0fssc+hs1TGGVkkghiaFkkS7NkTxfnCH+Hdg+yTehTpMcehpok4PgUKKdyCH+9ldu6FhiHRv84Ntqj1vVcw==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^4.2.0", + "@azure/msal-node": "^3.5.0", + "open": "^10.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/identity/node_modules/@azure/msal-common": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.15.0.tgz", + "integrity": "sha512-/n+bN0AKlVa+AOcETkJSKj38+bvFs78BaP4rNtv3MJCmPH0YrHiskMRe74OhyZ5DZjGISlFyxqvf9/4QVEi2tw==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/identity/node_modules/@azure/msal-node": { + "version": "3.8.8", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.8.8.tgz", + "integrity": "sha512-+f1VrJH1iI517t4zgmuhqORja0bL6LDQXfBqkjuMmfTYXTQQnh1EvwwxO3UbKLT05N0obF72SRHFrC1RBDv5Gg==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "15.15.0", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@azure/identity/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@azure/logger": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", + "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", + "license": "MIT", + "dependencies": { + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.29.0.tgz", + "integrity": "sha512-/f3eHkSNUTl6DLQHm+bKecjBKcRQxbd/XLx8lvSYp8Nl/HRyPuIPOijt9Dt0sH50/SxOwQ62RnFCmFlGK+bR/w==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "15.15.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-browser/node_modules/@azure/msal-common": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.15.0.tgz", + "integrity": "sha512-/n+bN0AKlVa+AOcETkJSKj38+bvFs78BaP4rNtv3MJCmPH0YrHiskMRe74OhyZ5DZjGISlFyxqvf9/4QVEi2tw==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "14.16.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.1.tgz", + "integrity": "sha512-nyxsA6NA4SVKh5YyRpbSXiMr7oQbwark7JU9LMeg6tJYTSPyAGkdx61wPT4gyxZfxlSxMMEyAsWaubBlNyIa1w==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.16.3.tgz", + "integrity": "sha512-CO+SE4weOsfJf+C5LM8argzvotrXw252/ZU6SM2Tz63fEblhH1uuVaaO4ISYFuN4Q6BhTo7I3qIdi8ydUQCqhw==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "14.16.1", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@azure/msal-node/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@babel/runtime": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", @@ -2312,6 +2540,147 @@ "npm": ">=7.0.0" } }, + "node_modules/@slack/bolt": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@slack/bolt/-/bolt-4.6.0.tgz", + "integrity": "sha512-xPgfUs2+OXSugz54Ky07pA890+Qydk22SYToi8uGpXeHSt1JWwFJkRyd/9Vlg5I1AdfdpGXExDpwnbuN9Q/2dQ==", + "license": "MIT", + "dependencies": { + "@slack/logger": "^4.0.0", + "@slack/oauth": "^3.0.4", + "@slack/socket-mode": "^2.0.5", + "@slack/types": "^2.18.0", + "@slack/web-api": "^7.12.0", + "axios": "^1.12.0", + "express": "^5.0.0", + "path-to-regexp": "^8.1.0", + "raw-body": "^3", + "tsscmp": "^1.0.6" + }, + "engines": { + "node": ">=18", + "npm": ">=8.6.0" + }, + "peerDependencies": { + "@types/express": "^5.0.0" + } + }, + "node_modules/@slack/logger": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-4.0.0.tgz", + "integrity": "sha512-Wz7QYfPAlG/DR+DfABddUZeNgoeY7d1J39OCR2jR+v7VBsB8ezulDK5szTnDDPDwLH5IWhLvXIHlCFZV7MSKgA==", + "license": "MIT", + "dependencies": { + "@types/node": ">=18.0.0" + }, + "engines": { + "node": ">= 18", + "npm": ">= 8.6.0" + } + }, + "node_modules/@slack/oauth": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@slack/oauth/-/oauth-3.0.4.tgz", + "integrity": "sha512-+8H0g7mbrHndEUbYCP7uYyBCbwqmm3E6Mo3nfsDvZZW74zKk1ochfH/fWSvGInYNCVvaBUbg3RZBbTp0j8yJCg==", + "license": "MIT", + "dependencies": { + "@slack/logger": "^4", + "@slack/web-api": "^7.10.0", + "@types/jsonwebtoken": "^9", + "@types/node": ">=18", + "jsonwebtoken": "^9" + }, + "engines": { + "node": ">=18", + "npm": ">=8.6.0" + } + }, + "node_modules/@slack/socket-mode": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@slack/socket-mode/-/socket-mode-2.0.5.tgz", + "integrity": "sha512-VaapvmrAifeFLAFaDPfGhEwwunTKsI6bQhYzxRXw7BSujZUae5sANO76WqlVsLXuhVtCVrBWPiS2snAQR2RHJQ==", + "license": "MIT", + "dependencies": { + "@slack/logger": "^4", + "@slack/web-api": "^7.10.0", + "@types/node": ">=18", + "@types/ws": "^8", + "eventemitter3": "^5", + "ws": "^8" + }, + "engines": { + "node": ">= 18", + "npm": ">= 8.6.0" + } + }, + "node_modules/@slack/types": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/@slack/types/-/types-2.20.0.tgz", + "integrity": "sha512-PVF6P6nxzDMrzPC8fSCsnwaI+kF8YfEpxf3MqXmdyjyWTYsZQURpkK7WWUWvP5QpH55pB7zyYL9Qem/xSgc5VA==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0", + "npm": ">= 6.12.0" + } + }, + "node_modules/@slack/web-api": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-7.14.1.tgz", + "integrity": "sha512-RoygyteJeFswxDPJjUMESn9dldWVMD2xUcHHd9DenVavSfVC6FeVnSdDerOO7m8LLvw4Q132nQM4hX8JiF7dng==", + "license": "MIT", + "dependencies": { + "@slack/logger": "^4.0.0", + "@slack/types": "^2.20.0", + "@types/node": ">=18.0.0", + "@types/retry": "0.12.0", + "axios": "^1.13.5", + "eventemitter3": "^5.0.1", + "form-data": "^4.0.4", + "is-electron": "2.2.2", + "is-stream": "^2", + "p-queue": "^6", + "p-retry": "^4", + "retry": "^0.13.1" + }, + "engines": { + "node": ">= 18", + "npm": ">= 8.6.0" + } + }, + "node_modules/@slack/web-api/node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@slack/web-api/node_modules/p-queue/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/@slack/web-api/node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -2348,6 +2717,16 @@ "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", "license": "MIT" }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -2359,6 +2738,15 @@ "assertion-error": "^2.0.1" } }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/deep-eql": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", @@ -2373,12 +2761,58 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "license": "MIT" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, "node_modules/@types/long": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", "license": "MIT" }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "20.19.33", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.33.tgz", @@ -2395,6 +2829,43 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, "node_modules/@types/tinycolor2": { "version": "1.4.6", "resolved": "https://registry.npmjs.org/@types/tinycolor2/-/tinycolor2-1.4.6.tgz", @@ -2410,6 +2881,20 @@ "@types/node": "*" } }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.3.tgz", + "integrity": "sha512-91fp6CAAJSRtH5ja95T1FHSKa8aPW9/Zw6cta81jlZTUw/+Vq8jM/AfF/14h2b71wwR84JUTW/3Y8QPhDAawFA==", + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@vitest/expect": { "version": "4.0.18", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", @@ -2591,6 +3076,34 @@ "node": ">=6.5" } }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/adaptivecards": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/adaptivecards/-/adaptivecards-1.2.3.tgz", + "integrity": "sha512-amQ5OSW3OpIkrxVKLjxVBPk/T49yuOtnqs1z5ZPfZr0+OpTovzmiHbyoAGDIsu5SNYHwOZFp/3LGOnRaALFa/g==", + "license": "MIT" + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -2625,6 +3138,12 @@ "tslib": "^2.4.0" } }, + "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/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", @@ -2634,6 +3153,17 @@ "node": ">=8.0.0" } }, + "node_modules/axios": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", + "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -2654,6 +3184,15 @@ ], "license": "MIT" }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -2665,12 +3204,223 @@ "readable-stream": "^3.4.0" } }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "license": "ISC" }, + "node_modules/botbuilder": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/botbuilder/-/botbuilder-4.23.3.tgz", + "integrity": "sha512-1gDIQHHYosYBHGXMjvZEJDrcp3NGy3lzHBml5wn9PFqVuIk/cbsCDZs3KJ3g+aH/GGh4CH/ij9iQ2KbQYHAYKA==", + "license": "MIT", + "dependencies": { + "@azure/core-rest-pipeline": "^1.18.1", + "@azure/msal-node": "^2.13.1", + "axios": "^1.8.2", + "botbuilder-core": "4.23.3", + "botbuilder-stdlib": "4.23.3-internal", + "botframework-connector": "4.23.3", + "botframework-schema": "4.23.3", + "botframework-streaming": "4.23.3", + "dayjs": "^1.11.13", + "filenamify": "^6.0.0", + "fs-extra": "^11.2.0", + "htmlparser2": "^9.0.1", + "uuid": "^10.0.0", + "zod": "^3.23.8" + } + }, + "node_modules/botbuilder-core": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/botbuilder-core/-/botbuilder-core-4.23.3.tgz", + "integrity": "sha512-48iW739I24piBH683b/Unvlu1fSzjB69ViOwZ0PbTkN2yW5cTvHJWlW7bXntO8GSqJfssgPaVthKfyaCW457ig==", + "license": "MIT", + "dependencies": { + "botbuilder-dialogs-adaptive-runtime-core": "4.23.3-preview", + "botbuilder-stdlib": "4.23.3-internal", + "botframework-connector": "4.23.3", + "botframework-schema": "4.23.3", + "uuid": "^10.0.0", + "zod": "^3.23.8" + } + }, + "node_modules/botbuilder-dialogs-adaptive-runtime-core": { + "version": "4.23.3-preview", + "resolved": "https://registry.npmjs.org/botbuilder-dialogs-adaptive-runtime-core/-/botbuilder-dialogs-adaptive-runtime-core-4.23.3-preview.tgz", + "integrity": "sha512-EssyvqK9MobX3gbnUe/jjhLuxpCEeyQeQsyUFMJ236U6vzSQdrAxNH7Jc5DyZw2KKelBdK1xPBdwTYQNM5S0Qw==", + "license": "MIT", + "dependencies": { + "dependency-graph": "^1.0.0" + } + }, + "node_modules/botbuilder-stdlib": { + "version": "4.23.3-internal", + "resolved": "https://registry.npmjs.org/botbuilder-stdlib/-/botbuilder-stdlib-4.23.3-internal.tgz", + "integrity": "sha512-fwvIHnKU8sXo1gTww+m/k8wnuM5ktVBAV/3vWJ+ou40zapy1HYjWQuu6sVCRFgMUngpKwhdmoOQsTXsp58SNtA==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-http-compat": "^2.1.2", + "@azure/core-rest-pipeline": "^1.18.1", + "@azure/core-tracing": "^1.2.0" + } + }, + "node_modules/botbuilder/node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/botframework-connector": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/botframework-connector/-/botframework-connector-4.23.3.tgz", + "integrity": "sha512-sChwCFJr3xhcMCYChaOxJoE8/YgdjOPWzGwz5JAxZDwhbQonwYX5O/6Z9EA+wB3TCFNEh642SGeC/rOitaTnGQ==", + "license": "MIT", + "dependencies": { + "@azure/core-rest-pipeline": "^1.18.1", + "@azure/identity": "^4.4.1", + "@azure/msal-node": "^2.13.1", + "@types/jsonwebtoken": "9.0.6", + "axios": "^1.8.2", + "base64url": "^3.0.0", + "botbuilder-stdlib": "4.23.3-internal", + "botframework-schema": "4.23.3", + "buffer": "^6.0.3", + "cross-fetch": "^4.0.0", + "https-proxy-agent": "^7.0.5", + "jsonwebtoken": "^9.0.2", + "node-fetch": "^2.7.0", + "openssl-wrapper": "^0.3.4", + "rsa-pem-from-mod-exp": "^0.8.6", + "zod": "^3.23.8" + } + }, + "node_modules/botframework-connector/node_modules/@types/jsonwebtoken": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz", + "integrity": "sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/botframework-connector/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/botframework-schema": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/botframework-schema/-/botframework-schema-4.23.3.tgz", + "integrity": "sha512-/W0uWxZ3fuPLAImZRLnPTbs49Z2xMpJSIzIBxSfvwO0aqv9GsM3bTk3zlNdJ1xr40SshQ7WiH2H1hgjBALwYJw==", + "license": "MIT", + "dependencies": { + "adaptivecards": "1.2.3", + "uuid": "^10.0.0", + "zod": "^3.23.8" + } + }, + "node_modules/botframework-streaming": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/botframework-streaming/-/botframework-streaming-4.23.3.tgz", + "integrity": "sha512-GMtciQGfZXtAW6syUqFpFJQ2vDyVbpxL3T1DqFzq/GmmkAu7KTZ1zvo7PTww6+IT1kMW0lmL/XZJVq3Rhg4PQA==", + "license": "MIT", + "dependencies": { + "@types/ws": "^6.0.3", + "uuid": "^10.0.0", + "ws": "^7.5.10" + } + }, + "node_modules/botframework-streaming/node_modules/@types/ws": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-6.0.4.tgz", + "integrity": "sha512-PpPrX7SZW9re6+Ha8ojZG4Se8AZXgf0GK6zmfqEuCsY49LFDNXO3SByp44X3dFEqtB73lkCDAdUazhAjVPiNwg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/botframework-streaming/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -2711,12 +3461,42 @@ "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", "license": "MIT" }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", "license": "MIT" }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/cacheable": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.3.2.tgz", @@ -2730,6 +3510,35 @@ "qified": "^0.6.0" } }, + "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/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", @@ -2819,6 +3628,31 @@ "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/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", @@ -2828,6 +3662,33 @@ "node": ">= 0.6" } }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cross-fetch": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", + "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, "node_modules/css-select": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", @@ -2862,6 +3723,12 @@ "integrity": "sha512-axn2UMEnkhyDUPWOwVKBMVIzSQy2ejH2xRGy1wq81dqRwApXfIzfbE3hIX0ZRFBIihf/KDqK158DLwESu4AK1w==", "license": "MIT" }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -2903,6 +3770,73 @@ "node": ">=4.0.0" } }, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dependency-graph": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz", + "integrity": "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -3015,6 +3949,44 @@ "url": "https://dotenvx.com" } }, + "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/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", @@ -3036,6 +4008,24 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "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-module-lexer": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", @@ -3043,18 +4033,45 @@ "dev": true, "license": "MIT" }, - "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, + "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", - "bin": { - "esbuild": "bin/esbuild" + "dependencies": { + "es-errors": "^1.3.0" }, "engines": { - "node": ">=18" + "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", @@ -3085,6 +4102,12 @@ "@esbuild/win32-x64": "0.27.3" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -3095,6 +4118,15 @@ "@types/estree": "^1.0.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -3129,6 +4161,49 @@ "node": ">=12.0.0" } }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3171,12 +4246,149 @@ "url": "https://github.com/sindresorhus/file-type?sponsor=1" } }, + "node_modules/filename-reserved-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", + "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/filenamify": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-6.0.0.tgz", + "integrity": "sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==", + "license": "MIT", + "dependencies": { + "filename-reserved-regex": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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/form-data/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/form-data/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/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "license": "MIT" }, + "node_modules/fs-extra": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz", + "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3192,12 +4404,76 @@ "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-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/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", "license": "MIT" }, + "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/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, "node_modules/gradient-string": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/gradient-string/-/gradient-string-3.0.0.tgz", @@ -3232,6 +4508,33 @@ "node": ">=8" } }, + "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/hashery": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.4.0.tgz", @@ -3244,6 +4547,18 @@ "node": ">=20" } }, + "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/hookified": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", @@ -3269,6 +4584,68 @@ "entities": "^4.4.0" } }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -3301,6 +4678,87 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC" }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-electron": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.2.tgz", + "integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==", + "license": "MIT" + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/json-schema-to-ts": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", @@ -3314,6 +4772,61 @@ "node": ">=16" } }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keytar": { "version": "7.9.0", "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", @@ -3403,6 +4916,48 @@ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "license": "MIT" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/lodash.snakecase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", @@ -3427,17 +4982,63 @@ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "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/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, "engines": { - "node": ">= 0.8" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/mimic-response": { @@ -3538,6 +5139,15 @@ "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", "license": "MIT" }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/node-abi": { "version": "3.87.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", @@ -3588,6 +5198,18 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "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/obug": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", @@ -3608,6 +5230,18 @@ "node": ">=14.0.0" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3617,6 +5251,24 @@ "wrappy": "1" } }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/openai": { "version": "6.19.0", "resolved": "https://registry.npmjs.org/openai/-/openai-6.19.0.tgz", @@ -3638,6 +5290,12 @@ } } }, + "node_modules/openssl-wrapper": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/openssl-wrapper/-/openssl-wrapper-0.3.4.tgz", + "integrity": "sha512-iITsrx6Ho8V3/2OVtmZzzX8wQaKAaFXEJQdzoPUZDtyf5jWFlqo+h+OhGT4TATQ47f9ACKHua8nw7Qoy85aeKQ==", + "license": "MIT" + }, "node_modules/oxfmt": { "version": "0.34.0", "resolved": "https://registry.npmjs.org/oxfmt/-/oxfmt-0.34.0.tgz", @@ -3723,6 +5381,15 @@ } } }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/p-queue": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.1.0.tgz", @@ -3751,6 +5418,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/p-timeout": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-4.1.0.tgz", @@ -3797,6 +5477,25 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -3958,6 +5657,25 @@ "node": ">=12.0.0" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/pump": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", @@ -3994,6 +5712,21 @@ "qrcode-terminal": "bin/qrcode-terminal.js" } }, + "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/querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", @@ -4009,6 +5742,30 @@ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", "license": "MIT" }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -4047,6 +5804,15 @@ "node": ">= 12.13.0" } }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/rollup": { "version": "4.59.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", @@ -4092,6 +5858,40 @@ "fsevents": "~2.3.2" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/rsa-pem-from-mod-exp": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/rsa-pem-from-mod-exp/-/rsa-pem-from-mod-exp-0.8.6.tgz", + "integrity": "sha512-c5ouQkOvGHF1qomUUDJGFcXsomeSO2gbEs6hVhMAtlkE1CuaZase/WzoaKFG/EZQuNmq6pw/EMCeEnDvOgCJYQ==", + "license": "MIT" + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -4130,6 +5930,12 @@ "node": ">=10" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/sandwich-stream": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz", @@ -4151,6 +5957,57 @@ "node": ">=10" } }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, "node_modules/sharp": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", @@ -4196,6 +6053,78 @@ "@img/sharp-win32-x64": "0.34.5" } }, + "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/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", @@ -4283,6 +6212,15 @@ "dev": true, "license": "MIT" }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/std-env": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", @@ -4465,6 +6403,15 @@ "node": ">=14.0.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/token-types": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", @@ -4507,6 +6454,15 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "license": "MIT", + "engines": { + "node": ">=0.6.x" + } + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -4519,6 +6475,20 @@ "node": "*" } }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -4560,6 +6530,24 @@ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/url": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", @@ -4576,6 +6564,28 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/vite": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", @@ -4795,6 +6805,31 @@ "optional": true } } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 15dd8e0..dc249c4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "txtcode", "version": "0.1.0", - "description": "Remote IDE control via WhatsApp/Telegram/Discord - Code from anywhere using AI-powered messaging", + "description": "Remote IDE control via WhatsApp/Telegram/Discord/Slack/Teams/Signal - Code from anywhere using AI-powered messaging", "keywords": [ "ai", "claude", @@ -11,10 +11,13 @@ "gemini", "ide", "messaging-ide", + "microsoft-teams", "mobile-coding", "openai", "remote", "remote-development", + "signal", + "slack", "telegram", "text-based-coding", "whatsapp" @@ -41,7 +44,9 @@ "dependencies": { "@anthropic-ai/sdk": "^0.74.0", "@google/generative-ai": "^0.24.1", + "@slack/bolt": "^4.6.0", "@whiskeysockets/baileys": "^7.0.0-rc.9", + "botbuilder": "^4.23.3", "chalk": "^4.1.2", "discord.js": "^14.25.1", "dotenv": "^16.3.1", diff --git a/src/cli/commands/auth.ts b/src/cli/commands/auth.ts index 79bf442..fa3d25e 100644 --- a/src/cli/commands/auth.ts +++ b/src/cli/commands/auth.ts @@ -555,11 +555,22 @@ export async function authCommand() { { name: "WhatsApp", value: "whatsapp" }, { name: "Telegram", value: "telegram" }, { name: "Discord", value: "discord" }, + { name: "Slack", value: "slack" }, + { name: "Microsoft Teams", value: "teams" }, + { name: "Signal", value: "signal" }, ], }); let telegramToken = ""; let discordToken = ""; + let slackBotToken = ""; + let slackAppToken = ""; + let slackSigningSecret = ""; + let teamsAppId = ""; + let teamsAppPassword = ""; + let teamsTenantId = ""; + let signalPhoneNumber = ""; + let signalCliRestUrl = ""; // Complete messaging platform auth immediately if (platform === "telegram") { @@ -598,6 +609,102 @@ export async function authCommand() { console.log(); console.log(chalk.green("Discord bot configured")); console.log(); + } else if (platform === "slack") { + console.log(); + console.log(chalk.cyan("Slack Bot Setup")); + console.log(); + console.log(chalk.gray("1. Go to https://api.slack.com/apps and create a new app")); + console.log(chalk.gray("2. Enable Socket Mode (Settings → Socket Mode)")); + console.log(chalk.gray("3. Add Bot Token Scopes: chat:write, channels:history, groups:history, im:history, mpim:history")); + console.log(chalk.gray("4. Install the app to your workspace")); + console.log(chalk.gray("5. Subscribe to bot events: message.channels, message.groups, message.im, message.mpim")); + console.log(); + + slackBotToken = await showCenteredInput({ + message: "Enter Slack Bot Token (xoxb-...):", + password: true, + validate: (input) => input.length > 0 || "Bot token is required", + }); + console.log(); + + slackAppToken = await showCenteredInput({ + message: "Enter Slack App-Level Token (xapp-...):", + password: true, + validate: (input) => input.length > 0 || "App token is required", + }); + console.log(); + + slackSigningSecret = await showCenteredInput({ + message: "Enter Slack Signing Secret:", + password: true, + validate: (input) => input.length > 0 || "Signing secret is required", + }); + console.log(); + console.log(chalk.green("Slack bot configured")); + console.log(); + } else if (platform === "teams") { + console.log(); + console.log(chalk.cyan("Microsoft Teams Bot Setup")); + console.log(); + console.log(chalk.gray("1. Go to https://dev.teams.microsoft.com/bots")); + console.log(chalk.gray("2. Create a new Bot registration")); + console.log(chalk.gray("3. Copy the App ID and generate a client secret")); + console.log(chalk.gray("4. Set the messaging endpoint to https:///api/messages")); + console.log(); + + teamsAppId = await showCenteredInput({ + message: "Enter Teams App (Bot) ID:", + password: false, + validate: (input) => input.length > 0 || "App ID is required", + }); + console.log(); + + teamsAppPassword = await showCenteredInput({ + message: "Enter Teams App Password (Client Secret):", + password: true, + validate: (input) => input.length > 0 || "App password is required", + }); + console.log(); + + teamsTenantId = await showCenteredInput({ + message: "Enter Azure Tenant ID:", + password: false, + validate: (input) => input.length > 0 || "Tenant ID is required", + }); + console.log(); + console.log(chalk.green("Microsoft Teams bot configured")); + console.log(); + } else if (platform === "signal") { + console.log(); + console.log(chalk.cyan("Signal Bot Setup")); + console.log(); + console.log(chalk.gray("Signal requires signal-cli-rest-api running as a companion service.")); + console.log(); + console.log(chalk.gray("Setup:")); + console.log(chalk.gray(" 1. Run signal-cli-rest-api via Docker:")); + console.log(chalk.white(" docker run -p 8080:8080 bbernhard/signal-cli-rest-api")); + console.log(chalk.gray(" 2. Register/link your phone number with signal-cli")); + console.log(chalk.gray(" 3. Provide the phone number and API URL below")); + console.log(); + + signalPhoneNumber = await showCenteredInput({ + message: "Enter Signal phone number (e.g. +1234567890):", + password: false, + validate: (input) => input.startsWith("+") || "Phone number must start with +", + }); + console.log(); + + signalCliRestUrl = await showCenteredInput({ + message: "Enter signal-cli-rest-api URL (default: http://localhost:8080):", + password: false, + validate: () => true, + }); + if (!signalCliRestUrl.trim()) { + signalCliRestUrl = "http://localhost:8080"; + } + console.log(); + console.log(chalk.green("Signal bot configured")); + console.log(); } else { console.log(); console.log(chalk.cyan("WhatsApp Setup")); @@ -633,6 +740,8 @@ export async function authCommand() { console.log(chalk.cyan("Recommended alternatives:")); console.log(chalk.white(" • Telegram - More stable and reliable")); console.log(chalk.white(" • Discord - Also very stable")); + console.log(chalk.white(" • Slack - Great for workspace integration")); + console.log(chalk.white(" • Signal - Privacy-focused alternative")); console.log(); console.log(chalk.gray("Would you like to restart and choose a different platform?")); } else { @@ -677,6 +786,20 @@ export async function authCommand() { if (discordToken) { await setBotToken("discord", discordToken); } + if (slackBotToken) { + await setBotToken("slack-bot", slackBotToken); + await setBotToken("slack-app", slackAppToken); + await setBotToken("slack-signing", slackSigningSecret); + } + if (teamsAppId) { + await setBotToken("teams-app-id", teamsAppId); + await setBotToken("teams-app-password", teamsAppPassword); + await setBotToken("teams-tenant-id", teamsTenantId); + } + if (signalPhoneNumber) { + await setBotToken("signal-phone", signalPhoneNumber); + await setBotToken("signal-api-url", signalCliRestUrl); + } } catch { console.log(chalk.red("\n[ERROR] Failed to store credentials in keychain")); console.log(chalk.yellow("Falling back to encrypted file storage...\n")); diff --git a/src/cli/commands/start.ts b/src/cli/commands/start.ts index 69a6e01..80f8016 100644 --- a/src/cli/commands/start.ts +++ b/src/cli/commands/start.ts @@ -1,6 +1,9 @@ import chalk from "chalk"; import { AgentCore } from "../../core/agent"; import { DiscordBot } from "../../platforms/discord"; +import { SignalBot } from "../../platforms/signal"; +import { SlackBot } from "../../platforms/slack"; +import { TeamsBot } from "../../platforms/teams"; import { TelegramBot } from "../../platforms/telegram"; import { WhatsAppBot } from "../../platforms/whatsapp"; import { logger } from "../../shared/logger"; @@ -9,6 +12,19 @@ import { getApiKey, getBotToken } from "../../utils/keychain"; import { centerLog } from "../tui"; import { loadConfig } from "./auth"; +async function loadPlatformToken(name: string, keychainKey: string): Promise { + const token = (await getBotToken(keychainKey)) || ""; + if (!token) { + console.log(); + centerLog(chalk.red(`[ERROR] Failed to retrieve ${name} token from keychain`)); + console.log(); + centerLog(chalk.yellow("Please run authentication")); + console.log(); + process.exit(1); + } + return token; +} + export async function startCommand(_options: { daemon?: boolean }) { const rawConfig = loadConfig(); @@ -41,25 +57,21 @@ export async function startCommand(_options: { daemon?: boolean }) { let discordToken = ""; if (config.platform === "telegram") { - telegramToken = (await getBotToken("telegram")) || ""; - if (!telegramToken) { - console.log(); - centerLog(chalk.red("[ERROR] Failed to retrieve Telegram token from keychain")); - console.log(); - centerLog(chalk.yellow("Please run authentication")); - console.log(); - process.exit(1); - } + telegramToken = await loadPlatformToken("Telegram", "telegram"); } else if (config.platform === "discord") { - discordToken = (await getBotToken("discord")) || ""; - if (!discordToken) { - console.log(); - centerLog(chalk.red("[ERROR] Failed to retrieve Discord token from keychain")); - console.log(); - centerLog(chalk.yellow("Please run authentication")); - console.log(); - process.exit(1); - } + discordToken = await loadPlatformToken("Discord", "discord"); + } else if (config.platform === "slack") { + process.env.SLACK_BOT_TOKEN = await loadPlatformToken("Slack Bot", "slack-bot"); + process.env.SLACK_APP_TOKEN = await loadPlatformToken("Slack App", "slack-app"); + process.env.SLACK_SIGNING_SECRET = await loadPlatformToken("Slack Signing Secret", "slack-signing"); + } else if (config.platform === "teams") { + process.env.TEAMS_APP_ID = await loadPlatformToken("Teams App ID", "teams-app-id"); + process.env.TEAMS_APP_PASSWORD = await loadPlatformToken("Teams App Password", "teams-app-password"); + process.env.TEAMS_TENANT_ID = await loadPlatformToken("Teams Tenant ID", "teams-tenant-id"); + } else if (config.platform === "signal") { + process.env.SIGNAL_PHONE_NUMBER = await loadPlatformToken("Signal Phone Number", "signal-phone"); + const signalApiUrl = (await getBotToken("signal-api-url")) || "http://localhost:8080"; + process.env.SIGNAL_CLI_REST_URL = signalApiUrl; } process.env.PLATFORM = config.platform; @@ -87,6 +99,15 @@ export async function startCommand(_options: { daemon?: boolean }) { } else if (config.platform === "discord") { const bot = new DiscordBot(agent); await bot.start(); + } else if (config.platform === "slack") { + const bot = new SlackBot(agent); + await bot.start(); + } else if (config.platform === "teams") { + const bot = new TeamsBot(agent); + await bot.start(); + } else if (config.platform === "signal") { + const bot = new SignalBot(agent); + await bot.start(); } else { logger.error("Invalid platform specified"); process.exit(1); diff --git a/src/platforms/signal.ts b/src/platforms/signal.ts new file mode 100644 index 0000000..9173ff6 --- /dev/null +++ b/src/platforms/signal.ts @@ -0,0 +1,300 @@ +import { AgentCore } from "../core/agent"; +import { logger } from "../shared/logger"; +import { SignalTypingSignaler } from "../shared/typing-signaler"; +import http from "http"; + +const MAX_SIGNAL_LENGTH = 4096; + +interface SignalMessage { + envelope: { + source: string; + sourceNumber?: string; + sourceName?: string; + timestamp: number; + dataMessage?: { + message: string; + timestamp: number; + }; + }; +} + +interface ActiveRequest { + heartbeatInterval: NodeJS.Timeout | null; + aborted: boolean; +} + +export class SignalBot { + private agent: AgentCore; + private signalCliUrl: string; + private registeredNumber: string; + private activeRequests: Map = new Map(); + + constructor(agent: AgentCore) { + this.agent = agent; + this.signalCliUrl = process.env.SIGNAL_CLI_REST_URL || "http://localhost:8080"; + this.registeredNumber = process.env.SIGNAL_PHONE_NUMBER || ""; + + if (!this.registeredNumber) { + throw new Error("SIGNAL_PHONE_NUMBER not set in config"); + } + } + + private cleanupRequest(userId: string) { + const active = this.activeRequests.get(userId); + if (!active) { + return; + } + active.aborted = true; + if (active.heartbeatInterval) { + clearInterval(active.heartbeatInterval); + } + this.activeRequests.delete(userId); + } + + private async sendMessage(recipient: string, message: string): Promise { + const url = `${this.signalCliUrl}/v2/send`; + const body = JSON.stringify({ + message, + number: this.registeredNumber, + recipients: [recipient], + }); + + const response = await fetch(url, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body, + }); + + if (!response.ok) { + throw new Error(`Signal API error: ${response.status} ${response.statusText}`); + } + } + + private async sendTypingIndicator(recipient: string): Promise { + try { + const url = `${this.signalCliUrl}/v1/typing-indicator/${this.registeredNumber}`; + await fetch(url, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ recipient }), + }); + } catch { + // Typing indicator is best-effort + } + } + + private async sendLongMessage(recipient: string, text: string): Promise { + if (text.length <= MAX_SIGNAL_LENGTH) { + await this.sendMessage(recipient, text); + return; + } + const parts = splitMessage(text, MAX_SIGNAL_LENGTH); + for (const part of parts) { + await this.sendMessage(recipient, part); + } + } + + private async handleIncomingMessage(signalMsg: SignalMessage) { + const dataMessage = signalMsg.envelope.dataMessage; + if (!dataMessage?.message) { + return; + } + + const from = signalMsg.envelope.source || signalMsg.envelope.sourceNumber || ""; + const text = dataMessage.message; + + if (!from || !text) { + return; + } + + logger.debug(`Incoming Signal message from ${from}: ${text}`); + + if (!this.agent.shouldStream(from, text)) { + this.cleanupRequest(from); + + const response = await this.agent.processMessage({ + from, + text, + timestamp: new Date(dataMessage.timestamp), + }); + await this.sendLongMessage(from, response); + return; + } + + const prev = this.activeRequests.get(from); + if (prev) { + this.cleanupRequest(from); + await this.sendMessage(from, "Previous command cancelled."); + } + + const active: ActiveRequest = { + heartbeatInterval: null, + aborted: false, + }; + this.activeRequests.set(from, active); + + const typingSignaler = new SignalTypingSignaler( + this.signalCliUrl, + this.registeredNumber, + from, + ); + + active.heartbeatInterval = setInterval(async () => { + if (active.aborted) { + return; + } + await typingSignaler.signalTyping(); + }, 3000); + + try { + const response = await this.agent.processMessage( + { + from, + text, + timestamp: new Date(dataMessage.timestamp), + }, + async (_chunk: string) => { + if (!active.aborted) { + await typingSignaler.signalTyping(); + } + }, + ); + + if (active.aborted) { + return; + } + + if (active.heartbeatInterval) { + clearInterval(active.heartbeatInterval); + } + this.activeRequests.delete(from); + await typingSignaler.stopTyping(); + + await this.sendLongMessage(from, response); + } catch (error) { + if (active.aborted) { + return; + } + + if (active.heartbeatInterval) { + clearInterval(active.heartbeatInterval); + } + this.activeRequests.delete(from); + await typingSignaler.stopTyping(); + + const isAbort = error instanceof Error && error.message.includes("aborted"); + if (isAbort) { + return; + } + + const errMsg = `Error: ${error instanceof Error ? error.message : "Unknown error"}`; + await this.sendMessage(from, errMsg); + } + } + + async start() { + const webhookPort = parseInt(process.env.SIGNAL_WEBHOOK_PORT || "3979", 10); + + logger.info("Starting Signal bot..."); + logger.info(`Signal CLI REST API: ${this.signalCliUrl}`); + logger.info(`Registered number: ${this.registeredNumber}`); + + // Verify connectivity to signal-cli-rest-api + try { + const healthCheck = await fetch(`${this.signalCliUrl}/v1/about`); + if (!healthCheck.ok) { + throw new Error(`signal-cli-rest-api returned ${healthCheck.status}`); + } + logger.info("Connected to signal-cli-rest-api"); + } catch (error) { + throw new Error( + `Cannot connect to signal-cli-rest-api at ${this.signalCliUrl}. ` + + `Make sure signal-cli-rest-api is running. ` + + `Error: ${error instanceof Error ? error.message : String(error)}`, + ); + } + + // Register webhook for incoming messages + try { + await fetch(`${this.signalCliUrl}/v1/receive/${this.registeredNumber}`, { + method: "GET", + }); + } catch { + // Initial receive call to flush pending messages + } + + const server = http.createServer(async (req, res) => { + if (req.url === "/api/signal/webhook" && req.method === "POST") { + let body = ""; + req.on("data", (chunk) => { + body += chunk; + }); + req.on("end", async () => { + try { + const message = JSON.parse(body) as SignalMessage; + await this.handleIncomingMessage(message); + } catch (error) { + logger.error("Failed to process Signal webhook", error); + } + res.writeHead(200); + res.end("OK"); + }); + } else { + res.writeHead(200); + res.end("TxtCode Signal Bot is running."); + } + }); + + server.listen(webhookPort, () => { + logger.info(`Signal webhook listening on port ${webhookPort}`); + logger.info("Configure signal-cli-rest-api webhook to:"); + logger.info(` http://localhost:${webhookPort}/api/signal/webhook`); + }); + + // Also poll for messages as a fallback + this.startPolling(); + } + + private startPolling() { + const pollInterval = parseInt(process.env.SIGNAL_POLL_INTERVAL || "2000", 10); + + setInterval(async () => { + try { + const url = `${this.signalCliUrl}/v1/receive/${this.registeredNumber}`; + const response = await fetch(url); + if (!response.ok) { + return; + } + + const messages = (await response.json()) as SignalMessage[]; + for (const msg of messages) { + try { + await this.handleIncomingMessage(msg); + } catch (error) { + logger.error("Error processing polled Signal message", error); + } + } + } catch { + // Polling failure is non-critical + } + }, pollInterval); + } +} + +function splitMessage(text: string, max: number): string[] { + const parts: string[] = []; + let remaining = text; + while (remaining.length > 0) { + if (remaining.length <= max) { + parts.push(remaining); + break; + } + let breakAt = remaining.lastIndexOf("\n", max); + if (breakAt < max / 2) { + breakAt = max; + } + parts.push(remaining.slice(0, breakAt)); + remaining = remaining.slice(breakAt); + } + return parts; +} diff --git a/src/platforms/slack.ts b/src/platforms/slack.ts new file mode 100644 index 0000000..ff342da --- /dev/null +++ b/src/platforms/slack.ts @@ -0,0 +1,321 @@ +import { App } from "@slack/bolt"; +import { AgentCore } from "../core/agent"; +import { BlockReplyPipeline } from "../shared/block-reply-pipeline"; +import { logger } from "../shared/logger"; +import type { StreamChunk } from "../shared/streaming-types"; +import { SlackTypingSignaler } from "../shared/typing-signaler"; + +const MAX_SLACK_LENGTH = 4000; + +interface ActiveRequest { + heartbeatInterval: NodeJS.Timeout | null; + progressMessageTs: string | null; + channel: string; + aborted: boolean; +} + +export class SlackBot { + private app: App; + private agent: AgentCore; + private activeRequests: Map = new Map(); + + constructor(agent: AgentCore) { + this.agent = agent; + + const token = process.env.SLACK_BOT_TOKEN; + const appToken = process.env.SLACK_APP_TOKEN; + const signingSecret = process.env.SLACK_SIGNING_SECRET; + + if (!token) { + throw new Error("SLACK_BOT_TOKEN not set in config"); + } + if (!appToken) { + throw new Error("SLACK_APP_TOKEN not set in config"); + } + if (!signingSecret) { + throw new Error("SLACK_SIGNING_SECRET not set in config"); + } + + this.app = new App({ + token, + appToken, + signingSecret, + socketMode: true, + }); + + this.setupHandlers(); + } + + private cleanupRequest(userId: string) { + const active = this.activeRequests.get(userId); + if (!active) { + return; + } + active.aborted = true; + if (active.heartbeatInterval) { + clearInterval(active.heartbeatInterval); + } + this.activeRequests.delete(userId); + } + + private setupHandlers() { + this.app.message(async ({ message, client }) => { + const msg = message as unknown as Record; + if (msg.subtype || msg.bot_id) { + return; + } + + const from = msg.user as string | undefined; + const text = (msg.text as string) || ""; + const channel = msg.channel as string; + + if (!from || !text) { + return; + } + + logger.debug(`Incoming Slack message from ${from}: ${text}`); + + if (!this.agent.shouldStream(from, text)) { + const prev = this.activeRequests.get(from); + this.cleanupRequest(from); + + if (prev?.progressMessageTs) { + try { + await client.chat.update({ + channel: prev.channel, + ts: prev.progressMessageTs, + text: "Cancelled.", + }); + } catch { + // ignore + } + } + + const response = await this.agent.processMessage({ from, text, timestamp: new Date() }); + try { + await this.sendLongMessage(client, channel, response, msg.ts as string); + } catch (error) { + logger.error("Failed to send Slack message", error); + } + return; + } + + const prev = this.activeRequests.get(from); + if (prev) { + this.cleanupRequest(from); + if (prev.progressMessageTs) { + try { + await client.chat.update({ + channel: prev.channel, + ts: prev.progressMessageTs, + text: "Cancelled (new request received).", + }); + } catch { + // ignore + } + } + } + + const active: ActiveRequest = { + heartbeatInterval: null, + progressMessageTs: null, + channel, + aborted: false, + }; + this.activeRequests.set(from, active); + + let lastEditText = ""; + const taskStartTime = Date.now(); + + try { + const result = await client.chat.postMessage({ + channel, + text: "Working on your request...", + thread_ts: msg.ts as string, + }); + active.progressMessageTs = result.ts || null; + lastEditText = "Working on your request..."; + } catch (error) { + logger.debug(`Failed to send initial message: ${error}`); + } + + const typingSignaler = new SlackTypingSignaler(); + + const pipeline = new BlockReplyPipeline({ + chunking: { + minChars: 200, + maxChars: 800, + breakPreference: "paragraph", + flushOnParagraph: true, + }, + typingSignaler, + onChunk: async (chunk: StreamChunk) => { + if (active.aborted || !active.progressMessageTs) { + return; + } + const preview = truncate(chunk.text, MAX_SLACK_LENGTH - 50); + if (preview === lastEditText) { + return; + } + try { + await client.chat.update({ + channel, + ts: active.progressMessageTs, + text: preview, + }); + lastEditText = preview; + } catch { + // ignore + } + }, + }); + + active.heartbeatInterval = setInterval(async () => { + if (active.aborted || !active.progressMessageTs) { + return; + } + const elapsed = Math.floor((Date.now() - taskStartTime) / 1000); + const heartbeatMsg = `Still working... (${elapsed}s)`; + if (heartbeatMsg === lastEditText) { + return; + } + try { + await client.chat.update({ + channel, + ts: active.progressMessageTs, + text: heartbeatMsg, + }); + lastEditText = heartbeatMsg; + } catch { + // ignore + } + }, 25000); + + try { + const response = await this.agent.processMessage( + { from, text, timestamp: new Date() }, + async (chunk: string) => { + if (!active.aborted) { + await pipeline.processText(chunk); + } + }, + ); + + if (active.aborted) { + return; + } + + if (active.heartbeatInterval) { + clearInterval(active.heartbeatInterval); + } + this.activeRequests.delete(from); + await pipeline.flush(); + + const finalText = truncate(response, MAX_SLACK_LENGTH); + + try { + if (active.progressMessageTs) { + if (finalText !== lastEditText) { + await client.chat.update({ + channel, + ts: active.progressMessageTs, + text: finalText, + }); + } + } else { + await this.sendLongMessage(client, channel, response, msg.ts as string); + } + } catch { + try { + const fallback = "Task completed, but the output was too long to display."; + if (active.progressMessageTs) { + await client.chat.update({ + channel, + ts: active.progressMessageTs, + text: fallback, + }); + } else { + await client.chat.postMessage({ channel, text: fallback, thread_ts: msg.ts as string }); + } + } catch (fallbackError) { + logger.error("Failed to send fallback message", fallbackError); + } + } + } catch (error) { + if (active.aborted) { + return; + } + + if (active.heartbeatInterval) { + clearInterval(active.heartbeatInterval); + } + this.activeRequests.delete(from); + + const isAbort = error instanceof Error && error.message.includes("aborted"); + if (isAbort) { + return; + } + + const errMsg = `Error: ${error instanceof Error ? error.message : "Unknown error"}`; + if (active.progressMessageTs) { + try { + await client.chat.update({ + channel, + ts: active.progressMessageTs, + text: errMsg, + }); + } catch { + // ignore + } + } + } + }); + } + + private async sendLongMessage( + client: InstanceType["client"], + channel: string, + text: string, + threadTs?: string, + ): Promise { + if (text.length <= MAX_SLACK_LENGTH) { + await client.chat.postMessage({ channel, text, thread_ts: threadTs }); + return; + } + const parts = splitMessage(text, MAX_SLACK_LENGTH); + for (const part of parts) { + await client.chat.postMessage({ channel, text: part, thread_ts: threadTs }); + } + } + + async start() { + logger.info("Starting Slack bot..."); + await this.app.start(); + logger.info("Slack bot is running (Socket Mode)!"); + } +} + +function truncate(text: string, max: number): string { + if (text.length <= max) { + return text; + } + return text.slice(0, max - 20) + "\n\n... (truncated)"; +} + +function splitMessage(text: string, max: number): string[] { + const parts: string[] = []; + let remaining = text; + while (remaining.length > 0) { + if (remaining.length <= max) { + parts.push(remaining); + break; + } + let breakAt = remaining.lastIndexOf("\n", max); + if (breakAt < max / 2) { + breakAt = max; + } + parts.push(remaining.slice(0, breakAt)); + remaining = remaining.slice(breakAt); + } + return parts; +} diff --git a/src/platforms/teams.ts b/src/platforms/teams.ts new file mode 100644 index 0000000..b75a508 --- /dev/null +++ b/src/platforms/teams.ts @@ -0,0 +1,381 @@ +import { + ActivityTypes, + CloudAdapter, + ConfigurationBotFrameworkAuthentication, + type ConfigurationBotFrameworkAuthenticationOptions, + type TurnContext, +} from "botbuilder"; +import { AgentCore } from "../core/agent"; +import { BlockReplyPipeline } from "../shared/block-reply-pipeline"; +import { logger } from "../shared/logger"; +import type { StreamChunk } from "../shared/streaming-types"; +import { TeamsTypingSignaler } from "../shared/typing-signaler"; +import http from "http"; + +const MAX_TEAMS_LENGTH = 4096; + +interface ActiveRequest { + heartbeatInterval: NodeJS.Timeout | null; + activityId: string | null; + aborted: boolean; +} + +function adaptRequest(req: http.IncomingMessage): Record { + return new Promise>((resolve) => { + let body = ""; + req.on("data", (chunk: Buffer) => { + body += chunk.toString(); + }); + req.on("end", () => { + let parsed: unknown = {}; + try { + parsed = JSON.parse(body); + } catch { + // not JSON + } + resolve({ + body: parsed, + headers: req.headers, + method: req.method, + on: req.on.bind(req), + }); + }); + }) as unknown as Record; +} + +function adaptResponse(res: http.ServerResponse): Record { + return { + socket: res.socket, + header(name: string, value: unknown) { + res.setHeader(name, String(value)); + return this; + }, + send(body: unknown) { + if (typeof body === "object") { + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify(body)); + } else { + res.end(String(body ?? "")); + } + return this; + }, + status(code: number) { + res.statusCode = code; + return this; + }, + end(...args: unknown[]) { + res.end(...(args as Parameters)); + return this; + }, + }; +} + +export class TeamsBot { + private adapter: CloudAdapter; + private agent: AgentCore; + private activeRequests: Map = new Map(); + + constructor(agent: AgentCore) { + this.agent = agent; + + const authConfig: ConfigurationBotFrameworkAuthenticationOptions = { + MicrosoftAppId: process.env.TEAMS_APP_ID || "", + MicrosoftAppTenantId: process.env.TEAMS_TENANT_ID || "", + }; + + const botFrameworkAuth = new ConfigurationBotFrameworkAuthentication(authConfig); + this.adapter = new CloudAdapter(botFrameworkAuth); + + this.adapter.onTurnError = async (context: TurnContext, error: Error) => { + logger.error("Teams adapter error", error); + try { + await context.sendActivity("An error occurred processing your request."); + } catch { + // ignore + } + }; + } + + private cleanupRequest(userId: string) { + const active = this.activeRequests.get(userId); + if (!active) { + return; + } + active.aborted = true; + if (active.heartbeatInterval) { + clearInterval(active.heartbeatInterval); + } + this.activeRequests.delete(userId); + } + + private async handleMessage(context: TurnContext) { + if (context.activity.type !== ActivityTypes.Message) { + return; + } + + const from = context.activity.from?.id || ""; + const text = (context.activity.text || "").replace(/.*?<\/at>/g, "").trim(); + + if (!from || !text) { + return; + } + + logger.debug(`Incoming Teams message from ${from}: ${text}`); + + if (!this.agent.shouldStream(from, text)) { + const prev = this.activeRequests.get(from); + this.cleanupRequest(from); + + if (prev?.activityId) { + try { + await context.updateActivity({ + id: prev.activityId, + type: ActivityTypes.Message, + text: "Cancelled.", + }); + } catch { + // ignore + } + } + + const response = await this.agent.processMessage({ from, text, timestamp: new Date() }); + try { + await this.sendLongMessage(context, response); + } catch (error) { + logger.error("Failed to send Teams message", error); + } + return; + } + + const prev = this.activeRequests.get(from); + if (prev) { + this.cleanupRequest(from); + if (prev.activityId) { + try { + await context.updateActivity({ + id: prev.activityId, + type: ActivityTypes.Message, + text: "Cancelled (new request received).", + }); + } catch { + // ignore + } + } + } + + const active: ActiveRequest = { + heartbeatInterval: null, + activityId: null, + aborted: false, + }; + this.activeRequests.set(from, active); + + let lastEditText = ""; + const taskStartTime = Date.now(); + + try { + const reply = await context.sendActivity("Working on your request..."); + active.activityId = reply?.id || null; + lastEditText = "Working on your request..."; + } catch (error) { + logger.debug(`Failed to send initial message: ${error}`); + } + + const typingSignaler = new TeamsTypingSignaler(context); + + const pipeline = new BlockReplyPipeline({ + chunking: { + minChars: 200, + maxChars: 800, + breakPreference: "paragraph", + flushOnParagraph: true, + }, + typingSignaler, + onChunk: async (chunk: StreamChunk) => { + if (active.aborted || !active.activityId) { + return; + } + const preview = truncate(chunk.text, MAX_TEAMS_LENGTH - 50); + if (preview === lastEditText) { + return; + } + try { + await context.updateActivity({ + id: active.activityId, + type: ActivityTypes.Message, + text: preview, + }); + lastEditText = preview; + } catch { + // ignore + } + }, + }); + + active.heartbeatInterval = setInterval(async () => { + if (active.aborted || !active.activityId) { + return; + } + const elapsed = Math.floor((Date.now() - taskStartTime) / 1000); + const heartbeatMsg = `Still working... (${elapsed}s)`; + if (heartbeatMsg === lastEditText) { + return; + } + try { + await context.updateActivity({ + id: active.activityId, + type: ActivityTypes.Message, + text: heartbeatMsg, + }); + lastEditText = heartbeatMsg; + } catch { + // ignore + } + }, 25000); + + try { + const response = await this.agent.processMessage( + { from, text, timestamp: new Date() }, + async (chunk: string) => { + if (!active.aborted) { + await pipeline.processText(chunk); + } + }, + ); + + if (active.aborted) { + return; + } + + if (active.heartbeatInterval) { + clearInterval(active.heartbeatInterval); + } + this.activeRequests.delete(from); + await pipeline.flush(); + + const finalText = truncate(response, MAX_TEAMS_LENGTH); + + try { + if (active.activityId) { + if (finalText !== lastEditText) { + await context.updateActivity({ + id: active.activityId, + type: ActivityTypes.Message, + text: finalText, + }); + } + } else { + await this.sendLongMessage(context, response); + } + } catch { + try { + const fallback = "Task completed, but the output was too long to display."; + if (active.activityId) { + await context.updateActivity({ + id: active.activityId, + type: ActivityTypes.Message, + text: fallback, + }); + } else { + await context.sendActivity(fallback); + } + } catch (fallbackError) { + logger.error("Failed to send fallback message", fallbackError); + } + } + } catch (error) { + if (active.aborted) { + return; + } + + if (active.heartbeatInterval) { + clearInterval(active.heartbeatInterval); + } + this.activeRequests.delete(from); + await typingSignaler.stopTyping(); + + const isAbort = error instanceof Error && error.message.includes("aborted"); + if (isAbort) { + return; + } + + const errMsg = `Error: ${error instanceof Error ? error.message : "Unknown error"}`; + if (active.activityId) { + try { + await context.updateActivity({ + id: active.activityId, + type: ActivityTypes.Message, + text: errMsg, + }); + } catch { + // ignore + } + } + } + } + + private async sendLongMessage(context: TurnContext, text: string): Promise { + if (text.length <= MAX_TEAMS_LENGTH) { + await context.sendActivity(text); + return; + } + const parts = splitMessage(text, MAX_TEAMS_LENGTH); + for (const part of parts) { + await context.sendActivity(part); + } + } + + async start() { + const port = parseInt(process.env.TEAMS_PORT || "3978", 10); + + logger.info("Starting Microsoft Teams bot..."); + + const server = http.createServer(async (req, res) => { + if (req.url === "/api/messages" && req.method === "POST") { + const adaptedReq = await (adaptRequest(req) as unknown as Promise>); + const adaptedRes = adaptResponse(res); + await this.adapter.process( + adaptedReq as never, + adaptedRes as never, + async (context) => { + await this.handleMessage(context); + }, + ); + } else { + res.writeHead(200); + res.end("TxtCode Teams Bot is running."); + } + }); + + server.listen(port, () => { + logger.info(`Teams bot listening on port ${port}`); + logger.info("Ensure your Bot Framework messaging endpoint is set to:"); + logger.info(` https:///api/messages`); + }); + } +} + +function truncate(text: string, max: number): string { + if (text.length <= max) { + return text; + } + return text.slice(0, max - 20) + "\n\n... (truncated)"; +} + +function splitMessage(text: string, max: number): string[] { + const parts: string[] = []; + let remaining = text; + while (remaining.length > 0) { + if (remaining.length <= max) { + parts.push(remaining); + break; + } + let breakAt = remaining.lastIndexOf("\n", max); + if (breakAt < max / 2) { + breakAt = max; + } + parts.push(remaining.slice(0, breakAt)); + remaining = remaining.slice(breakAt); + } + return parts; +} diff --git a/src/shared/typing-signaler.ts b/src/shared/typing-signaler.ts index c49f1b7..c62a907 100644 --- a/src/shared/typing-signaler.ts +++ b/src/shared/typing-signaler.ts @@ -111,6 +111,111 @@ export class TelegramTypingSignaler implements TypingSignaler { } } +/** + * Slack typing signaler + * Slack doesn't have a native typing indicator API for bots, + * so this is a no-op. Message edits provide the "live" feel. + */ +export class SlackTypingSignaler implements TypingSignaler { + async signalTyping(): Promise { + // Slack bots cannot send typing indicators + } + + async signalTextDelta(_text: string): Promise { + // No-op + } + + async stopTyping(): Promise { + // No-op + } +} + +/** + * Microsoft Teams typing signaler + */ +export class TeamsTypingSignaler implements TypingSignaler { + private context: unknown; + private lastTypingSignal: number = 0; + + constructor(context: unknown) { + this.context = context; + } + + async signalTyping(): Promise { + const now = Date.now(); + // Teams typing indicator lasts ~3 seconds, refresh every 2 seconds + if (now - this.lastTypingSignal > 2000) { + try { + const ctx = this.context as { + sendActivity: (activity: { type: string }) => Promise; + }; + await ctx.sendActivity({ type: "typing" }); + this.lastTypingSignal = now; + } catch { + // Ignore typing errors + } + } + } + + async signalTextDelta(_text: string): Promise { + await this.signalTyping(); + } + + async stopTyping(): Promise { + // Teams typing stops automatically + } +} + +/** + * Signal typing signaler (via signal-cli-rest-api) + */ +export class SignalTypingSignaler implements TypingSignaler { + private signalCliUrl: string; + private registeredNumber: string; + private recipient: string; + private lastTypingSignal: number = 0; + + constructor(signalCliUrl: string, registeredNumber: string, recipient: string) { + this.signalCliUrl = signalCliUrl; + this.registeredNumber = registeredNumber; + this.recipient = recipient; + } + + async signalTyping(): Promise { + const now = Date.now(); + if (now - this.lastTypingSignal > 3000) { + try { + const url = `${this.signalCliUrl}/v1/typing-indicator/${this.registeredNumber}`; + await fetch(url, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ recipient: this.recipient }), + }); + this.lastTypingSignal = now; + } catch { + // Ignore typing errors + } + } + } + + async signalTextDelta(_text: string): Promise { + await this.signalTyping(); + } + + async stopTyping(): Promise { + try { + const url = `${this.signalCliUrl}/v1/typing-indicator/${this.registeredNumber}`; + await fetch(url, { + method: "DELETE", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ recipient: this.recipient }), + }); + } catch { + // Ignore errors + } + } +} + /** * WhatsApp typing signaler */ diff --git a/test/unit/typing-signaler.test.ts b/test/unit/typing-signaler.test.ts index 9904320..f1b999b 100644 --- a/test/unit/typing-signaler.test.ts +++ b/test/unit/typing-signaler.test.ts @@ -3,6 +3,9 @@ import { NoOpTypingSignaler, DiscordTypingSignaler, TelegramTypingSignaler, + SlackTypingSignaler, + TeamsTypingSignaler, + SignalTypingSignaler, WhatsAppTypingSignaler, } from "../../src/shared/typing-signaler"; @@ -87,6 +90,152 @@ describe("TelegramTypingSignaler", () => { }); }); +describe("SlackTypingSignaler", () => { + it("all methods resolve without error (no-op)", async () => { + const signaler = new SlackTypingSignaler(); + await signaler.signalTyping(); + await signaler.signalTextDelta("hello"); + await signaler.stopTyping(); + }); +}); + +describe("TeamsTypingSignaler", () => { + it("sends typing activity on signalTyping", async () => { + const context = { sendActivity: vi.fn().mockResolvedValue(undefined) }; + const signaler = new TeamsTypingSignaler(context); + + await signaler.signalTyping(); + expect(context.sendActivity).toHaveBeenCalledWith({ type: "typing" }); + }); + + it("throttles to 2s interval", async () => { + const context = { sendActivity: vi.fn().mockResolvedValue(undefined) }; + const signaler = new TeamsTypingSignaler(context); + + await signaler.signalTyping(); + await signaler.signalTyping(); + + expect(context.sendActivity).toHaveBeenCalledTimes(1); + }); + + it("ignores errors from sendActivity", async () => { + const context = { sendActivity: vi.fn().mockRejectedValue(new Error("fail")) }; + const signaler = new TeamsTypingSignaler(context); + + await expect(signaler.signalTyping()).resolves.not.toThrow(); + }); + + it("signalTextDelta delegates to signalTyping", async () => { + const context = { sendActivity: vi.fn().mockResolvedValue(undefined) }; + const signaler = new TeamsTypingSignaler(context); + + await signaler.signalTextDelta("hello"); + expect(context.sendActivity).toHaveBeenCalledWith({ type: "typing" }); + }); + + it("stopTyping resolves without error", async () => { + const context = { sendActivity: vi.fn().mockResolvedValue(undefined) }; + const signaler = new TeamsTypingSignaler(context); + await signaler.stopTyping(); + }); +}); + +describe("SignalTypingSignaler", () => { + it("calls fetch with PUT on signalTyping", async () => { + const mockFetch = vi.fn().mockResolvedValue({ ok: true }); + vi.stubGlobal("fetch", mockFetch); + + const signaler = new SignalTypingSignaler( + "http://localhost:8080", + "+1234567890", + "+0987654321", + ); + + await signaler.signalTyping(); + expect(mockFetch).toHaveBeenCalledWith( + "http://localhost:8080/v1/typing-indicator/+1234567890", + expect.objectContaining({ + method: "PUT", + body: JSON.stringify({ recipient: "+0987654321" }), + }), + ); + + vi.unstubAllGlobals(); + }); + + it("throttles to 3s interval", async () => { + const mockFetch = vi.fn().mockResolvedValue({ ok: true }); + vi.stubGlobal("fetch", mockFetch); + + const signaler = new SignalTypingSignaler( + "http://localhost:8080", + "+1234567890", + "+0987654321", + ); + + await signaler.signalTyping(); + await signaler.signalTyping(); + + expect(mockFetch).toHaveBeenCalledTimes(1); + + vi.unstubAllGlobals(); + }); + + it("calls fetch with DELETE on stopTyping", async () => { + const mockFetch = vi.fn().mockResolvedValue({ ok: true }); + vi.stubGlobal("fetch", mockFetch); + + const signaler = new SignalTypingSignaler( + "http://localhost:8080", + "+1234567890", + "+0987654321", + ); + + await signaler.stopTyping(); + expect(mockFetch).toHaveBeenCalledWith( + "http://localhost:8080/v1/typing-indicator/+1234567890", + expect.objectContaining({ + method: "DELETE", + body: JSON.stringify({ recipient: "+0987654321" }), + }), + ); + + vi.unstubAllGlobals(); + }); + + it("ignores errors from fetch", async () => { + const mockFetch = vi.fn().mockRejectedValue(new Error("network error")); + vi.stubGlobal("fetch", mockFetch); + + const signaler = new SignalTypingSignaler( + "http://localhost:8080", + "+1234567890", + "+0987654321", + ); + + await expect(signaler.signalTyping()).resolves.not.toThrow(); + await expect(signaler.stopTyping()).resolves.not.toThrow(); + + vi.unstubAllGlobals(); + }); + + it("signalTextDelta delegates to signalTyping", async () => { + const mockFetch = vi.fn().mockResolvedValue({ ok: true }); + vi.stubGlobal("fetch", mockFetch); + + const signaler = new SignalTypingSignaler( + "http://localhost:8080", + "+1234567890", + "+0987654321", + ); + + await signaler.signalTextDelta("hello"); + expect(mockFetch).toHaveBeenCalled(); + + vi.unstubAllGlobals(); + }); +}); + describe("WhatsAppTypingSignaler", () => { it("sends composing presence update", async () => { const sock = { sendPresenceUpdate: vi.fn().mockResolvedValue(undefined) }; From f3357a1f104d9ee9babc9c6fcb38b50ea31b9dbf Mon Sep 17 00:00:00 2001 From: IMvision12 Date: Thu, 26 Feb 2026 18:41:06 -0800 Subject: [PATCH 2/4] Format --- src/cli/commands/auth.ts | 12 ++++++++++-- src/cli/commands/start.ts | 15 ++++++++++++--- src/platforms/signal.ts | 8 ++------ src/platforms/slack.ts | 6 +++++- src/platforms/teams.ts | 12 ++++-------- 5 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/cli/commands/auth.ts b/src/cli/commands/auth.ts index fa3d25e..bab8529 100644 --- a/src/cli/commands/auth.ts +++ b/src/cli/commands/auth.ts @@ -615,9 +615,17 @@ export async function authCommand() { console.log(); console.log(chalk.gray("1. Go to https://api.slack.com/apps and create a new app")); console.log(chalk.gray("2. Enable Socket Mode (Settings → Socket Mode)")); - console.log(chalk.gray("3. Add Bot Token Scopes: chat:write, channels:history, groups:history, im:history, mpim:history")); + console.log( + chalk.gray( + "3. Add Bot Token Scopes: chat:write, channels:history, groups:history, im:history, mpim:history", + ), + ); console.log(chalk.gray("4. Install the app to your workspace")); - console.log(chalk.gray("5. Subscribe to bot events: message.channels, message.groups, message.im, message.mpim")); + console.log( + chalk.gray( + "5. Subscribe to bot events: message.channels, message.groups, message.im, message.mpim", + ), + ); console.log(); slackBotToken = await showCenteredInput({ diff --git a/src/cli/commands/start.ts b/src/cli/commands/start.ts index 80f8016..08daad0 100644 --- a/src/cli/commands/start.ts +++ b/src/cli/commands/start.ts @@ -63,13 +63,22 @@ export async function startCommand(_options: { daemon?: boolean }) { } else if (config.platform === "slack") { process.env.SLACK_BOT_TOKEN = await loadPlatformToken("Slack Bot", "slack-bot"); process.env.SLACK_APP_TOKEN = await loadPlatformToken("Slack App", "slack-app"); - process.env.SLACK_SIGNING_SECRET = await loadPlatformToken("Slack Signing Secret", "slack-signing"); + process.env.SLACK_SIGNING_SECRET = await loadPlatformToken( + "Slack Signing Secret", + "slack-signing", + ); } else if (config.platform === "teams") { process.env.TEAMS_APP_ID = await loadPlatformToken("Teams App ID", "teams-app-id"); - process.env.TEAMS_APP_PASSWORD = await loadPlatformToken("Teams App Password", "teams-app-password"); + process.env.TEAMS_APP_PASSWORD = await loadPlatformToken( + "Teams App Password", + "teams-app-password", + ); process.env.TEAMS_TENANT_ID = await loadPlatformToken("Teams Tenant ID", "teams-tenant-id"); } else if (config.platform === "signal") { - process.env.SIGNAL_PHONE_NUMBER = await loadPlatformToken("Signal Phone Number", "signal-phone"); + process.env.SIGNAL_PHONE_NUMBER = await loadPlatformToken( + "Signal Phone Number", + "signal-phone", + ); const signalApiUrl = (await getBotToken("signal-api-url")) || "http://localhost:8080"; process.env.SIGNAL_CLI_REST_URL = signalApiUrl; } diff --git a/src/platforms/signal.ts b/src/platforms/signal.ts index 9173ff6..63f912e 100644 --- a/src/platforms/signal.ts +++ b/src/platforms/signal.ts @@ -1,7 +1,7 @@ +import http from "http"; import { AgentCore } from "../core/agent"; import { logger } from "../shared/logger"; import { SignalTypingSignaler } from "../shared/typing-signaler"; -import http from "http"; const MAX_SIGNAL_LENGTH = 4096; @@ -133,11 +133,7 @@ export class SignalBot { }; this.activeRequests.set(from, active); - const typingSignaler = new SignalTypingSignaler( - this.signalCliUrl, - this.registeredNumber, - from, - ); + const typingSignaler = new SignalTypingSignaler(this.signalCliUrl, this.registeredNumber, from); active.heartbeatInterval = setInterval(async () => { if (active.aborted) { diff --git a/src/platforms/slack.ts b/src/platforms/slack.ts index ff342da..bbe94e5 100644 --- a/src/platforms/slack.ts +++ b/src/platforms/slack.ts @@ -235,7 +235,11 @@ export class SlackBot { text: fallback, }); } else { - await client.chat.postMessage({ channel, text: fallback, thread_ts: msg.ts as string }); + await client.chat.postMessage({ + channel, + text: fallback, + thread_ts: msg.ts as string, + }); } } catch (fallbackError) { logger.error("Failed to send fallback message", fallbackError); diff --git a/src/platforms/teams.ts b/src/platforms/teams.ts index b75a508..65d1de2 100644 --- a/src/platforms/teams.ts +++ b/src/platforms/teams.ts @@ -1,3 +1,4 @@ +import http from "http"; import { ActivityTypes, CloudAdapter, @@ -10,7 +11,6 @@ import { BlockReplyPipeline } from "../shared/block-reply-pipeline"; import { logger } from "../shared/logger"; import type { StreamChunk } from "../shared/streaming-types"; import { TeamsTypingSignaler } from "../shared/typing-signaler"; -import http from "http"; const MAX_TEAMS_LENGTH = 4096; @@ -334,13 +334,9 @@ export class TeamsBot { if (req.url === "/api/messages" && req.method === "POST") { const adaptedReq = await (adaptRequest(req) as unknown as Promise>); const adaptedRes = adaptResponse(res); - await this.adapter.process( - adaptedReq as never, - adaptedRes as never, - async (context) => { - await this.handleMessage(context); - }, - ); + await this.adapter.process(adaptedReq as never, adaptedRes as never, async (context) => { + await this.handleMessage(context); + }); } else { res.writeHead(200); res.end("TxtCode Teams Bot is running."); From 0f5250fdce0d4205fbe72fdffea22c223483007a Mon Sep 17 00:00:00 2001 From: IMvision12 Date: Thu, 26 Feb 2026 18:42:52 -0800 Subject: [PATCH 3/4] Fix oxlint false-positive on Slack chat.postMessage calls Made-with: Cursor --- src/platforms/slack.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/platforms/slack.ts b/src/platforms/slack.ts index bbe94e5..57da926 100644 --- a/src/platforms/slack.ts +++ b/src/platforms/slack.ts @@ -128,12 +128,12 @@ export class SlackBot { const taskStartTime = Date.now(); try { - const result = await client.chat.postMessage({ + const ts = await this.postSlackMessage(client, { channel, text: "Working on your request...", thread_ts: msg.ts as string, }); - active.progressMessageTs = result.ts || null; + active.progressMessageTs = ts || null; lastEditText = "Working on your request..."; } catch (error) { logger.debug(`Failed to send initial message: ${error}`); @@ -235,7 +235,7 @@ export class SlackBot { text: fallback, }); } else { - await client.chat.postMessage({ + await this.postSlackMessage(client, { channel, text: fallback, thread_ts: msg.ts as string, @@ -276,6 +276,16 @@ export class SlackBot { }); } + private async postSlackMessage( + client: InstanceType["client"], + opts: { channel: string; text: string; thread_ts?: string }, + ): Promise { + // Wrapper to avoid oxlint false-positive (unicorn/require-post-message-target-origin) + // which confuses Slack's chat.postMessage with window.postMessage + const result = await client.chat.postMessage(opts); // eslint-disable-line unicorn/require-post-message-target-origin + return result.ts; + } + private async sendLongMessage( client: InstanceType["client"], channel: string, @@ -283,12 +293,12 @@ export class SlackBot { threadTs?: string, ): Promise { if (text.length <= MAX_SLACK_LENGTH) { - await client.chat.postMessage({ channel, text, thread_ts: threadTs }); + await this.postSlackMessage(client, { channel, text, thread_ts: threadTs }); return; } const parts = splitMessage(text, MAX_SLACK_LENGTH); for (const part of parts) { - await client.chat.postMessage({ channel, text: part, thread_ts: threadTs }); + await this.postSlackMessage(client, { channel, text: part, thread_ts: threadTs }); } } From 24e980aae9bf8e16f9a71174da5f12d2187f86fc Mon Sep 17 00:00:00 2001 From: IMvision12 Date: Thu, 26 Feb 2026 18:43:47 -0800 Subject: [PATCH 4/4] Format --- src/platforms/signal.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platforms/signal.ts b/src/platforms/signal.ts index 63f912e..4802fab 100644 --- a/src/platforms/signal.ts +++ b/src/platforms/signal.ts @@ -207,6 +207,7 @@ export class SignalBot { `Cannot connect to signal-cli-rest-api at ${this.signalCliUrl}. ` + `Make sure signal-cli-rest-api is running. ` + `Error: ${error instanceof Error ? error.message : String(error)}`, + { cause: error }, ); }