From eae8cd3cc6f8ba11217bd4052e58fa81b0e92e8a Mon Sep 17 00:00:00 2001 From: A Ibrahim Date: Wed, 22 Apr 2026 11:12:06 +0200 Subject: [PATCH 1/3] chore: replace playground shell-loop with shared ReplSession - Remove custom configure/mount/flush handlers from playground - Use ReplSession from @tigrisdata/agent-shell/repl - Implement ReplIO for xterm.js with prompt support - Remove credentials.ts (session manages state) - Update welcome message with login and configure options Co-Authored-By: Claude Opus 4.6 (1M context) --- playground/package-lock.json | 431 +++++++++++++++++----------------- playground/package.json | 2 +- playground/src/credentials.ts | 11 - playground/src/shell-loop.ts | 209 +++++------------ playground/src/welcome.ts | 9 +- 5 files changed, 286 insertions(+), 376 deletions(-) delete mode 100644 playground/src/credentials.ts diff --git a/playground/package-lock.json b/playground/package-lock.json index 8dcee9c..975678c 100644 --- a/playground/package-lock.json +++ b/playground/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "agent-shell-playground", "dependencies": { - "@tigrisdata/agent-shell": "^0.1.3", + "@tigrisdata/agent-shell": "^0.3.0", "@xterm/addon-fit": "^0.10.0", "@xterm/xterm": "^5.5.0" }, @@ -104,34 +104,34 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.1032.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1032.0.tgz", - "integrity": "sha512-A1wjVhV3IgsZ5td2l4AWgK03EjZ+ldwbiorxuO1hPf7RHJtSdr6oq/gKzyUwP7Tm7ma/M2xS/tplg5C8XB8RWg==", + "version": "3.1034.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1034.0.tgz", + "integrity": "sha512-r91IZKPKuRlpCBsEmz9qnWrYxuHD0jsQv1p9UGNasFpcuPo1OnfwIB2ClXtqdXKYUvubXCwn7KBObTVnnvYvAA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.974.1", - "@aws-sdk/credential-provider-node": "^3.972.32", + "@aws-sdk/core": "^3.974.3", + "@aws-sdk/credential-provider-node": "^3.972.34", "@aws-sdk/middleware-bucket-endpoint": "^3.972.10", "@aws-sdk/middleware-expect-continue": "^3.972.10", - "@aws-sdk/middleware-flexible-checksums": "^3.974.9", + "@aws-sdk/middleware-flexible-checksums": "^3.974.11", "@aws-sdk/middleware-host-header": "^3.972.10", "@aws-sdk/middleware-location-constraint": "^3.972.10", "@aws-sdk/middleware-logger": "^3.972.10", "@aws-sdk/middleware-recursion-detection": "^3.972.11", - "@aws-sdk/middleware-sdk-s3": "^3.972.30", + "@aws-sdk/middleware-sdk-s3": "^3.972.32", "@aws-sdk/middleware-ssec": "^3.972.10", - "@aws-sdk/middleware-user-agent": "^3.972.31", - "@aws-sdk/region-config-resolver": "^3.972.12", - "@aws-sdk/signature-v4-multi-region": "^3.996.18", + "@aws-sdk/middleware-user-agent": "^3.972.33", + "@aws-sdk/region-config-resolver": "^3.972.13", + "@aws-sdk/signature-v4-multi-region": "^3.996.20", "@aws-sdk/types": "^3.973.8", - "@aws-sdk/util-endpoints": "^3.996.7", + "@aws-sdk/util-endpoints": "^3.996.8", "@aws-sdk/util-user-agent-browser": "^3.972.10", - "@aws-sdk/util-user-agent-node": "^3.973.17", - "@smithy/config-resolver": "^4.4.16", - "@smithy/core": "^3.23.15", + "@aws-sdk/util-user-agent-node": "^3.973.19", + "@smithy/config-resolver": "^4.4.17", + "@smithy/core": "^3.23.16", "@smithy/eventstream-serde-browser": "^4.2.14", "@smithy/eventstream-serde-config-resolver": "^4.3.14", "@smithy/eventstream-serde-node": "^4.2.14", @@ -142,25 +142,25 @@ "@smithy/invalid-dependency": "^4.2.14", "@smithy/md5-js": "^4.2.14", "@smithy/middleware-content-length": "^4.2.14", - "@smithy/middleware-endpoint": "^4.4.30", - "@smithy/middleware-retry": "^4.5.3", - "@smithy/middleware-serde": "^4.2.18", + "@smithy/middleware-endpoint": "^4.4.31", + "@smithy/middleware-retry": "^4.5.4", + "@smithy/middleware-serde": "^4.2.19", "@smithy/middleware-stack": "^4.2.14", "@smithy/node-config-provider": "^4.3.14", - "@smithy/node-http-handler": "^4.5.3", + "@smithy/node-http-handler": "^4.6.0", "@smithy/protocol-http": "^5.3.14", - "@smithy/smithy-client": "^4.12.11", + "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "@smithy/url-parser": "^4.2.14", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", - "@smithy/util-defaults-mode-browser": "^4.3.47", - "@smithy/util-defaults-mode-node": "^4.2.52", - "@smithy/util-endpoints": "^3.4.1", + "@smithy/util-defaults-mode-browser": "^4.3.48", + "@smithy/util-defaults-mode-node": "^4.2.53", + "@smithy/util-endpoints": "^3.4.2", "@smithy/util-middleware": "^4.2.14", - "@smithy/util-retry": "^4.3.2", - "@smithy/util-stream": "^4.5.23", + "@smithy/util-retry": "^4.3.3", + "@smithy/util-stream": "^4.5.24", "@smithy/util-utf8": "^4.2.2", "@smithy/util-waiter": "^4.2.16", "tslib": "^2.6.2" @@ -183,22 +183,23 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.974.1", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.1.tgz", - "integrity": "sha512-gy/gffKz0zaHDaqRiLCdIvgHmaAL/HXuAtMcBP7euYSFx4BsbsdlfmUBJag+Gqe62z6/XuloKyQyaiH+kS3Vrg==", + "version": "3.974.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.3.tgz", + "integrity": "sha512-W3aJJm2clu8OmsrwMOMnfof13O6LGnbknnZIQeSRbxjqKah2nVvkjbUBBZVhWrt08KC69H7WsINTdrxC/2SXQw==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.8", "@aws-sdk/xml-builder": "^3.972.18", - "@smithy/core": "^3.23.15", + "@smithy/core": "^3.23.16", "@smithy/node-config-provider": "^4.3.14", "@smithy/property-provider": "^4.2.14", "@smithy/protocol-http": "^5.3.14", "@smithy/signature-v4": "^5.3.14", - "@smithy/smithy-client": "^4.12.11", + "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-middleware": "^4.2.14", + "@smithy/util-retry": "^4.3.3", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, @@ -233,12 +234,12 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.27", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.27.tgz", - "integrity": "sha512-xfUt2CUZDC+Tf16A6roD1b4pk/nrXdkoLY3TEhv198AXDtBo5xUJP1zd0e8SmuKLN4PpIBX96OizZbmMlcI6oQ==", + "version": "3.972.29", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.29.tgz", + "integrity": "sha512-rf+AlUxgTeSzQ/4zoS0D+Bt7XvgpY48PnWG8Yg/N9fdMgyK2Jaqa+6tLZp4MYMIMHkGrfAxnbSeb2YLMGFMg6g==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.1", + "@aws-sdk/core": "^3.974.3", "@aws-sdk/types": "^3.973.8", "@smithy/property-provider": "^4.2.14", "@smithy/types": "^4.14.1", @@ -249,20 +250,20 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.29", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.29.tgz", - "integrity": "sha512-hjNeYb6oLyHgMihra83ie0J/T2y9om3cy1qC90h9DRgvYXEoN4BCFf8bHguZjKhXunnv7YkmZRuYL5Mkk77eCA==", + "version": "3.972.31", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.31.tgz", + "integrity": "sha512-TR2/lQ3qKFj2EOrsiASzemsNEz2uzZ/SUBf48+U4Cr9a/FZlHfH/hwAeBJNBp1gMyJNxROJZhT3dn1cO+jnYfQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.1", + "@aws-sdk/core": "^3.974.3", "@aws-sdk/types": "^3.973.8", "@smithy/fetch-http-handler": "^5.3.17", - "@smithy/node-http-handler": "^4.5.3", + "@smithy/node-http-handler": "^4.6.0", "@smithy/property-provider": "^4.2.14", "@smithy/protocol-http": "^5.3.14", - "@smithy/smithy-client": "^4.12.11", + "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", - "@smithy/util-stream": "^4.5.23", + "@smithy/util-stream": "^4.5.24", "tslib": "^2.6.2" }, "engines": { @@ -270,19 +271,19 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.972.31", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.31.tgz", - "integrity": "sha512-PuQ7e8WYzAPpzvFcajxf8c0LqSzakVHVlKw8M0oubk8Kf347YOCCqT1seQrHs5AdZuIh2RD9LX4O+Xa5ImEBfQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.1", - "@aws-sdk/credential-provider-env": "^3.972.27", - "@aws-sdk/credential-provider-http": "^3.972.29", - "@aws-sdk/credential-provider-login": "^3.972.31", - "@aws-sdk/credential-provider-process": "^3.972.27", - "@aws-sdk/credential-provider-sso": "^3.972.31", - "@aws-sdk/credential-provider-web-identity": "^3.972.31", - "@aws-sdk/nested-clients": "^3.996.21", + "version": "3.972.33", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.33.tgz", + "integrity": "sha512-UwdbJbOrgnOxZbshaNZ4DzX35h5wQd33MNYTGzWhN3ORG9lG9KQbDX6l6tDJSAdaGTktJoZPSritmUoW1rYkRA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.3", + "@aws-sdk/credential-provider-env": "^3.972.29", + "@aws-sdk/credential-provider-http": "^3.972.31", + "@aws-sdk/credential-provider-login": "^3.972.33", + "@aws-sdk/credential-provider-process": "^3.972.29", + "@aws-sdk/credential-provider-sso": "^3.972.33", + "@aws-sdk/credential-provider-web-identity": "^3.972.33", + "@aws-sdk/nested-clients": "^3.997.1", "@aws-sdk/types": "^3.973.8", "@smithy/credential-provider-imds": "^4.2.14", "@smithy/property-provider": "^4.2.14", @@ -295,13 +296,13 @@ } }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.31", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.31.tgz", - "integrity": "sha512-bBmWDmtSpmLOZR6a0kmowBcVL1hiL8Vlap/RXeMpFd7JbWl87YcwqL6T9LH/0oBVEZXu1dUZAtojgSuZgMO5xw==", + "version": "3.972.33", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.33.tgz", + "integrity": "sha512-WyZuPVoDM1HGNl41eVg8HSSXIB+FGkuuK63GhDbh4TMdfWU03AciWvF/QqOVWvJtWVYaLddANJ+aUklVr2ieuw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.1", - "@aws-sdk/nested-clients": "^3.996.21", + "@aws-sdk/core": "^3.974.3", + "@aws-sdk/nested-clients": "^3.997.1", "@aws-sdk/types": "^3.973.8", "@smithy/property-provider": "^4.2.14", "@smithy/protocol-http": "^5.3.14", @@ -314,17 +315,17 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.32", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.32.tgz", - "integrity": "sha512-9aj0x9hGYUondBZSD0XkksAdHhOKttFw4BWpLCeggeg40qSJxGrAP++g0GCm0VqWc1WtC/NRFiAVzPCy56vmog==", + "version": "3.972.34", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.34.tgz", + "integrity": "sha512-sPcisURibKU4x0PCWJkWF1KJYm49Cph9dCn/PAnG5FU0wq5Id3g2v7RuEWAtNlKv1Af4gUJYBVGOeNpSEEx41A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.27", - "@aws-sdk/credential-provider-http": "^3.972.29", - "@aws-sdk/credential-provider-ini": "^3.972.31", - "@aws-sdk/credential-provider-process": "^3.972.27", - "@aws-sdk/credential-provider-sso": "^3.972.31", - "@aws-sdk/credential-provider-web-identity": "^3.972.31", + "@aws-sdk/credential-provider-env": "^3.972.29", + "@aws-sdk/credential-provider-http": "^3.972.31", + "@aws-sdk/credential-provider-ini": "^3.972.33", + "@aws-sdk/credential-provider-process": "^3.972.29", + "@aws-sdk/credential-provider-sso": "^3.972.33", + "@aws-sdk/credential-provider-web-identity": "^3.972.33", "@aws-sdk/types": "^3.973.8", "@smithy/credential-provider-imds": "^4.2.14", "@smithy/property-provider": "^4.2.14", @@ -337,12 +338,12 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.27", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.27.tgz", - "integrity": "sha512-1CZvfb1WzudWWIFAVQkd1OI/T1RxPcSvNWzNsb2BMBVsBJzBtB8dV5f2nymHVU4UqwxipdVt/DAbgdDRf33JDg==", + "version": "3.972.29", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.29.tgz", + "integrity": "sha512-DURisqWS3bUgiwMXTmzymVNGlcRW0FnbPZ3SZknhmxnCXm3n9idkTJ6T+Uir359KRKtJNFLRViskk8HsSVLi1w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.1", + "@aws-sdk/core": "^3.974.3", "@aws-sdk/types": "^3.973.8", "@smithy/property-provider": "^4.2.14", "@smithy/shared-ini-file-loader": "^4.4.9", @@ -354,14 +355,14 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.972.31", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.31.tgz", - "integrity": "sha512-x8Mx18S48XMl9bEEpYwmXDTvjWGPIfDadReN37Lc099/DUrlL4Zs9T9rwwggo6DkKS1aev6v+MTUx7JTa87TZQ==", + "version": "3.972.33", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.33.tgz", + "integrity": "sha512-9y9obU4IQWru9f+NiiscUeyCe5ZmQav4FKEb1qfUNrik/C3BzBGUnHQWyPEyXjOX9cb+vx1TYx0qZBtinKdzTA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.1", - "@aws-sdk/nested-clients": "^3.996.21", - "@aws-sdk/token-providers": "3.1032.0", + "@aws-sdk/core": "^3.974.3", + "@aws-sdk/nested-clients": "^3.997.1", + "@aws-sdk/token-providers": "3.1034.0", "@aws-sdk/types": "^3.973.8", "@smithy/property-provider": "^4.2.14", "@smithy/shared-ini-file-loader": "^4.4.9", @@ -373,13 +374,13 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.31", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.31.tgz", - "integrity": "sha512-zfuNMIkGfjYsHis9qytYf74Bcmq6Ji9Xwf4w53baRCI/b2otTwZv3SW1uRiJ5Di7999QzRGhHZ96+eUeo3gSOA==", + "version": "3.972.33", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.33.tgz", + "integrity": "sha512-RazhlN0YAkna2T2p2v4YuuRlVBVRNo8V0SL+9JePTWDndEUAeOBAjYeQfAMbtDyCh120+zA0Op6V0jS4dw2+iw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.1", - "@aws-sdk/nested-clients": "^3.996.21", + "@aws-sdk/core": "^3.974.3", + "@aws-sdk/nested-clients": "^3.997.1", "@aws-sdk/types": "^3.973.8", "@smithy/property-provider": "^4.2.14", "@smithy/shared-ini-file-loader": "^4.4.9", @@ -391,14 +392,14 @@ } }, "node_modules/@aws-sdk/lib-storage": { - "version": "3.1032.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.1032.0.tgz", - "integrity": "sha512-jXXKbrRWYvLlCCO8suiOiFrkcsO/zVYjdPZpVnDLSO6Nled7VvxwRkjnM1/l5CnOHAW6VGhR3nrU/+LmqeGQYg==", + "version": "3.1034.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.1034.0.tgz", + "integrity": "sha512-2iWFXxrDb0Ni6ZtToE89PUWFGTqo+0lji0E4Jngf8w4xA3aCBx4CY4PcHQ6/LsQfm8GNsgCsx0W503C3Hn9PyQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-endpoint": "^4.4.30", + "@smithy/middleware-endpoint": "^4.4.31", "@smithy/protocol-http": "^5.3.14", - "@smithy/smithy-client": "^4.12.11", + "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "buffer": "5.6.0", "events": "3.3.0", @@ -409,7 +410,7 @@ "node": ">=20.0.0" }, "peerDependencies": { - "@aws-sdk/client-s3": "^3.1032.0" + "@aws-sdk/client-s3": "^3.1034.0" } }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { @@ -446,15 +447,15 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.974.9", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.9.tgz", - "integrity": "sha512-ye6xVuMEQ5NCT+yQOryGYsuCXnOwu7iGFGzV+qpXZOWtqXIAAaFostapxj6RCubw36rekVwmdB2lcspFuyNfYQ==", + "version": "3.974.11", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.11.tgz", + "integrity": "sha512-jTrJFs4SMs9xjih45+QHtU79piovA6CAlofMt4jeknN5ef9zsVEHDtuwCnEe/3eANWewa9fd6Tvc54xEPpQ3RA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "^3.974.1", + "@aws-sdk/core": "^3.974.3", "@aws-sdk/crc64-nvme": "^3.972.7", "@aws-sdk/types": "^3.973.8", "@smithy/is-array-buffer": "^4.2.2", @@ -462,7 +463,7 @@ "@smithy/protocol-http": "^5.3.14", "@smithy/types": "^4.14.1", "@smithy/util-middleware": "^4.2.14", - "@smithy/util-stream": "^4.5.23", + "@smithy/util-stream": "^4.5.24", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, @@ -543,23 +544,23 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.972.30", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.30.tgz", - "integrity": "sha512-hoQRxjJu4tt3gEOQin21rJKotClJC+x7AmCh9ylRct1DJeaNI/BRlFxMbuhJe54bG6xANPagSs0my8K30QyV9g==", + "version": "3.972.32", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.32.tgz", + "integrity": "sha512-dc2O2x0V5pGJhmdQYQveUIFtMZsur7GrGuSgoKM4oQJuEcfvwnJ3sj+ip6WnxR5l6TrX5zkl4KgcgswOy3wAzQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.1", + "@aws-sdk/core": "^3.974.3", "@aws-sdk/types": "^3.973.8", "@aws-sdk/util-arn-parser": "^3.972.3", - "@smithy/core": "^3.23.15", + "@smithy/core": "^3.23.16", "@smithy/node-config-provider": "^4.3.14", "@smithy/protocol-http": "^5.3.14", "@smithy/signature-v4": "^5.3.14", - "@smithy/smithy-client": "^4.12.11", + "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "@smithy/util-config-provider": "^4.2.2", "@smithy/util-middleware": "^4.2.14", - "@smithy/util-stream": "^4.5.23", + "@smithy/util-stream": "^4.5.24", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, @@ -595,18 +596,18 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.972.31", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.31.tgz", - "integrity": "sha512-L+hXN2HDomlIsWSHW5DVD7ppccCeRnlHXZ5uHG34ePTjF5bm0I1fmrJLbUGiW97xRXWryit5cjdP4Sx2FwiGog==", + "version": "3.972.33", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.33.tgz", + "integrity": "sha512-mqtT3Fo7xanWMk2SbAcKLGGI/q1GHWNrExBj7cnWP2W2mkTMheXB4ntJvwPZ1UxPrQobrsv2dWFXmaOJeSOiDg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.1", + "@aws-sdk/core": "^3.974.3", "@aws-sdk/types": "^3.973.8", - "@aws-sdk/util-endpoints": "^3.996.7", - "@smithy/core": "^3.23.15", + "@aws-sdk/util-endpoints": "^3.996.8", + "@smithy/core": "^3.23.16", "@smithy/protocol-http": "^5.3.14", "@smithy/types": "^4.14.1", - "@smithy/util-retry": "^4.3.2", + "@smithy/util-retry": "^4.3.3", "tslib": "^2.6.2" }, "engines": { @@ -614,47 +615,48 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.996.21", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.21.tgz", - "integrity": "sha512-Me3d/ua2lb2G0bQfFmvCeQQp3+nN6GSPqMxDmi/IQlQ8CrlpQ5C0JJHpz2AnOUkEFI0lBNrAL3Vnt29l44ndkA==", + "version": "3.997.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.1.tgz", + "integrity": "sha512-Afc9hc2WZs3X4Jb8dnxyuYiZsLoWRO51roTCRf497gPnAKN2WRdXANu1vaVCTzwnDMOYFXb/cYv4ZSjxqAqcKA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.974.1", + "@aws-sdk/core": "^3.974.3", "@aws-sdk/middleware-host-header": "^3.972.10", "@aws-sdk/middleware-logger": "^3.972.10", "@aws-sdk/middleware-recursion-detection": "^3.972.11", - "@aws-sdk/middleware-user-agent": "^3.972.31", - "@aws-sdk/region-config-resolver": "^3.972.12", + "@aws-sdk/middleware-user-agent": "^3.972.33", + "@aws-sdk/region-config-resolver": "^3.972.13", + "@aws-sdk/signature-v4-multi-region": "^3.996.20", "@aws-sdk/types": "^3.973.8", - "@aws-sdk/util-endpoints": "^3.996.7", + "@aws-sdk/util-endpoints": "^3.996.8", "@aws-sdk/util-user-agent-browser": "^3.972.10", - "@aws-sdk/util-user-agent-node": "^3.973.17", - "@smithy/config-resolver": "^4.4.16", - "@smithy/core": "^3.23.15", + "@aws-sdk/util-user-agent-node": "^3.973.19", + "@smithy/config-resolver": "^4.4.17", + "@smithy/core": "^3.23.16", "@smithy/fetch-http-handler": "^5.3.17", "@smithy/hash-node": "^4.2.14", "@smithy/invalid-dependency": "^4.2.14", "@smithy/middleware-content-length": "^4.2.14", - "@smithy/middleware-endpoint": "^4.4.30", - "@smithy/middleware-retry": "^4.5.3", - "@smithy/middleware-serde": "^4.2.18", + "@smithy/middleware-endpoint": "^4.4.31", + "@smithy/middleware-retry": "^4.5.4", + "@smithy/middleware-serde": "^4.2.19", "@smithy/middleware-stack": "^4.2.14", "@smithy/node-config-provider": "^4.3.14", - "@smithy/node-http-handler": "^4.5.3", + "@smithy/node-http-handler": "^4.6.0", "@smithy/protocol-http": "^5.3.14", - "@smithy/smithy-client": "^4.12.11", + "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "@smithy/url-parser": "^4.2.14", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", - "@smithy/util-defaults-mode-browser": "^4.3.47", - "@smithy/util-defaults-mode-node": "^4.2.52", - "@smithy/util-endpoints": "^3.4.1", + "@smithy/util-defaults-mode-browser": "^4.3.48", + "@smithy/util-defaults-mode-node": "^4.2.53", + "@smithy/util-endpoints": "^3.4.2", "@smithy/util-middleware": "^4.2.14", - "@smithy/util-retry": "^4.3.2", + "@smithy/util-retry": "^4.3.3", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, @@ -676,13 +678,13 @@ } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.972.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.12.tgz", - "integrity": "sha512-QQI43Mxd53nBij0pm8HXC+t4IOC6gnhhZfzxE0OATQyO6QfPV4e+aTIRRuAJKA6Nig/cR8eLwPryqYTX9ZrjAQ==", + "version": "3.972.13", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.13.tgz", + "integrity": "sha512-CvJ2ZIjK/jVD/lbOpowBVElJyC1YxLTIJ13yM0AEo0t2v7swOzGjSA6lJGH+DwZXQhcjUjoYwc8bVYCX5MDr1A==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.8", - "@smithy/config-resolver": "^4.4.16", + "@smithy/config-resolver": "^4.4.17", "@smithy/node-config-provider": "^4.3.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" @@ -692,17 +694,17 @@ } }, "node_modules/@aws-sdk/s3-request-presigner": { - "version": "3.1032.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.1032.0.tgz", - "integrity": "sha512-LFaI5JQhiOmJDjKK02ir9oERU9AmxdyEvzv332oPDzAzWeNH06sZ1WsF3xRBBE5tbEH2jIc79N8EqDCY0s5kKQ==", + "version": "3.1034.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.1034.0.tgz", + "integrity": "sha512-YFU/ipfcNSNBzP4vqjsAL9oXuLwUqudQiGBeleVMkcnBdyvWYTQ30aj7iyA1jxDQCVvxBSB/QKdoRge89WcUvw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/signature-v4-multi-region": "^3.996.18", + "@aws-sdk/signature-v4-multi-region": "^3.996.20", "@aws-sdk/types": "^3.973.8", "@aws-sdk/util-format-url": "^3.972.10", - "@smithy/middleware-endpoint": "^4.4.30", + "@smithy/middleware-endpoint": "^4.4.31", "@smithy/protocol-http": "^5.3.14", - "@smithy/smithy-client": "^4.12.11", + "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, @@ -711,12 +713,12 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.18", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.18.tgz", - "integrity": "sha512-4KT8UXRmvNAP5zKq9UI1MIwbnmSChZncBt89RKu/skMqZSSWGkBZTAJsZ+no+txfmF3kVaUFv31CTBZkQ5BJpQ==", + "version": "3.996.20", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.20.tgz", + "integrity": "sha512-MEj6DhEcaO8RgVtFCJ+xpCQnZC3Iesr09avdY75qkMQfckQULu447IegK7Rs1MCGerVBfKnJQ4q+pQq9hI5lng==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "^3.972.30", + "@aws-sdk/middleware-sdk-s3": "^3.972.32", "@aws-sdk/types": "^3.973.8", "@smithy/protocol-http": "^5.3.14", "@smithy/signature-v4": "^5.3.14", @@ -728,13 +730,13 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.1032.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1032.0.tgz", - "integrity": "sha512-n+PU8Z+gll7p3wDrH+Wo6fkt8sPrVnq30YYM6Ryga95oJlEneNMEbDHj0iqjMX3V7gaGdJo/hJWyPo4lscP+mA==", + "version": "3.1034.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1034.0.tgz", + "integrity": "sha512-8E+KGcD4ET0H9FXJ2/ZWbfFnQNYEkTZZYJxAs1lkdJlve1AYuqaydInIFfvNgoz5GbYtzbK8/ugsSMu5wPm6kA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.1", - "@aws-sdk/nested-clients": "^3.996.21", + "@aws-sdk/core": "^3.974.3", + "@aws-sdk/nested-clients": "^3.997.1", "@aws-sdk/types": "^3.973.8", "@smithy/property-provider": "^4.2.14", "@smithy/shared-ini-file-loader": "^4.4.9", @@ -771,15 +773,15 @@ } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.996.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.7.tgz", - "integrity": "sha512-ty4LQxN1QC+YhUP28NfEgZDEGXkyqOQy+BDriBozqHsrYO4JMgiPhfizqOGF7P+euBTZ5Ez6SKlLAMCLo8tzmw==", + "version": "3.996.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.8.tgz", + "integrity": "sha512-oOZHcRDihk5iEe5V25NVWg45b3qEA8OpHWVdU/XQh8Zj4heVPAJqWvMphQnU7LkufmUo10EpvFPZuQMiFLJK3g==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.8", "@smithy/types": "^4.14.1", "@smithy/url-parser": "^4.2.14", - "@smithy/util-endpoints": "^3.4.1", + "@smithy/util-endpoints": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -826,12 +828,12 @@ } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.973.17", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.17.tgz", - "integrity": "sha512-utF5qjjbuJQuU9VdCkWl7L87sr93cApsrD+uxGfUnlafX8iyEzJrb7EZnufjThURZVTOtelRMXrblWxpefElUg==", + "version": "3.973.19", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.19.tgz", + "integrity": "sha512-ZAfHjpzdbrzkAftC139JoYGfXzDh5HY+AxRzw8pGJ8cULsf+l721sKAMK8mV5NvRETaW/BwghSwQhGgoNgrxMw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "^3.972.31", + "@aws-sdk/middleware-user-agent": "^3.972.33", "@aws-sdk/types": "^3.973.8", "@smithy/node-config-provider": "^4.3.14", "@smithy/types": "^4.14.1", @@ -1764,15 +1766,15 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.4.16", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.16.tgz", - "integrity": "sha512-GFlGPNLZKrGfqWpqVb31z7hvYCA9ZscfX1buYnvvMGcRYsQQnhH+4uN6mWWflcD5jB4OXP/LBrdpukEdjl41tg==", + "version": "4.4.17", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.17.tgz", + "integrity": "sha512-TzDZcAnhTyAHbXVxWZo7/tEcrIeFq20IBk8So3OLOetWpR8EwY/yEqBMBFaJMeyEiREDq4NfEl+qO3OAUD+vbQ==", "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^4.3.14", "@smithy/types": "^4.14.1", "@smithy/util-config-provider": "^4.2.2", - "@smithy/util-endpoints": "^3.4.1", + "@smithy/util-endpoints": "^3.4.2", "@smithy/util-middleware": "^4.2.14", "tslib": "^2.6.2" }, @@ -1781,9 +1783,9 @@ } }, "node_modules/@smithy/core": { - "version": "3.23.15", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.15.tgz", - "integrity": "sha512-E7GVCgsQttzfujEZb6Qep005wWf4xiL4x06apFEtzQMWYBPggZh/0cnOxPficw5cuK/YjjkehKoIN4YUaSh0UQ==", + "version": "3.23.16", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.16.tgz", + "integrity": "sha512-JStomOrINQA1VqNEopLsgcdgwd42au7mykKqVr30XFw89wLt9sDxJDi4djVPRwQmmzyTGy/uOvTc2ultMpFi1w==", "license": "Apache-2.0", "dependencies": { "@smithy/protocol-http": "^5.3.14", @@ -1792,7 +1794,7 @@ "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-middleware": "^4.2.14", - "@smithy/util-stream": "^4.5.23", + "@smithy/util-stream": "^4.5.24", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" @@ -2053,13 +2055,13 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.4.30", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.30.tgz", - "integrity": "sha512-qS2XqhKeXmdZ4nEQ4cOxIczSP/Y91wPAHYuRwmWDCh975B7/57uxsm5d6sisnUThn2u2FwzMdJNM7AbO1YPsPg==", + "version": "4.4.31", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.31.tgz", + "integrity": "sha512-KJPdCIN2kOE2aGmqZd7eUTr4WQwOGgtLWgUkswGJggs7rBcQYQjcZMEDa3C0DwbOiXS9L8/wDoQHkfxBYLfiLw==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.23.15", - "@smithy/middleware-serde": "^4.2.18", + "@smithy/core": "^3.23.16", + "@smithy/middleware-serde": "^4.2.19", "@smithy/node-config-provider": "^4.3.14", "@smithy/shared-ini-file-loader": "^4.4.9", "@smithy/types": "^4.14.1", @@ -2072,19 +2074,19 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.5.3.tgz", - "integrity": "sha512-TE8dJNi6JuxzGSxMCVd3i9IEWDndCl3bmluLsBNDWok8olgj65OfkndMhl9SZ7m14c+C5SQn/PcUmrDl57rSFw==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.5.4.tgz", + "integrity": "sha512-/z7nIFK+ZRW3Ie/l3NEVGdy34LvmEOzBrtBAvgWZ/4PrKX0xP3kWm8pkfcwUk523SqxZhdbQP9JSXgjF77Uhpw==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.23.15", + "@smithy/core": "^3.23.16", "@smithy/node-config-provider": "^4.3.14", "@smithy/protocol-http": "^5.3.14", - "@smithy/service-error-classification": "^4.2.14", - "@smithy/smithy-client": "^4.12.11", + "@smithy/service-error-classification": "^4.3.0", + "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "@smithy/util-middleware": "^4.2.14", - "@smithy/util-retry": "^4.3.2", + "@smithy/util-retry": "^4.3.3", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" }, @@ -2093,12 +2095,12 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.2.18", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.18.tgz", - "integrity": "sha512-M6CSgnp3v4tYz9ynj2JHbA60woBZcGqEwNjTKjBsNHPV26R1ZX52+0wW8WsZU18q45jD0tw2wL22S17Ze9LpEw==", + "version": "4.2.19", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.19.tgz", + "integrity": "sha512-Q6y+W9h3iYVMCKWDoVge+OC1LKFqbEKaq8SIWG2X2bWJRpd/6dDLyICcNLT6PbjH3Rr6bmg/SeDB25XFOFfeEw==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.23.15", + "@smithy/core": "^3.23.16", "@smithy/protocol-http": "^5.3.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" @@ -2136,9 +2138,9 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.5.3.tgz", - "integrity": "sha512-lc5jFL++x17sPhIwMWJ3YOnqmSjw/2Po6VLDlUIXvxVWRuJwRXnJ4jOBBLB0cfI5BB5ehIl02Fxr1PDvk/kxDw==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.6.0.tgz", + "integrity": "sha512-P734cAoTFtuGfWa/R3jgBnGlURt2w9bYEBwQNMKf58sRM9RShirB2mKwLsVP+jlG/wxpCu8abv8NxdUts8tdLA==", "license": "Apache-2.0", "dependencies": { "@smithy/protocol-http": "^5.3.14", @@ -2204,9 +2206,9 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.2.14", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.14.tgz", - "integrity": "sha512-vVimoUnGxlx4eLLQbZImdOZFOe+Zh+5ACntv8VxZuGP72LdWu5GV3oEmCahSEReBgRJoWjypFkrehSj7BWx1HQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.3.0.tgz", + "integrity": "sha512-9jKsBYQRPR0xBLgc2415RsA5PIcP2sis4oBdN9s0D13cg1B1284mNTjx9Yc+BEERXzuPm5ObktI96OxsKh8E9A==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.14.1" @@ -2261,17 +2263,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.12.11", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.11.tgz", - "integrity": "sha512-wzz/Wa1CH/Tlhxh0s4DQPEcXSxSVfJ59AZcUh9Gu0c6JTlKuwGf4o/3P2TExv0VbtPFt8odIBG+eQGK2+vTECg==", + "version": "4.12.12", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.12.tgz", + "integrity": "sha512-daO7SJn4eM6ArbmrEs+/BTbH7af8AEbSL3OMQdcRvvn8tuUcR5rU2n6DgxIV53aXMS42uwK8NgKKCh5XgqYOPQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.23.15", - "@smithy/middleware-endpoint": "^4.4.30", + "@smithy/core": "^3.23.16", + "@smithy/middleware-endpoint": "^4.4.31", "@smithy/middleware-stack": "^4.2.14", "@smithy/protocol-http": "^5.3.14", "@smithy/types": "^4.14.1", - "@smithy/util-stream": "^4.5.23", + "@smithy/util-stream": "^4.5.24", "tslib": "^2.6.2" }, "engines": { @@ -2381,13 +2383,13 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.47", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.47.tgz", - "integrity": "sha512-zlIuXai3/SHjQUQ8y3g/woLvrH573SK2wNjcDaHu5e9VOcC0JwM1MI0Sq0GZJyN3BwSUneIhpjZ18nsiz5AtQw==", + "version": "4.3.48", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.48.tgz", + "integrity": "sha512-hxVRVPYaRDWa6YQdse1aWX1qrksmLsvNyGBKdc32q4jFzSjxYVNWfstknAfR228TnzS4tzgswXRuYIbhXBuXFQ==", "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^4.2.14", - "@smithy/smithy-client": "^4.12.11", + "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, @@ -2396,16 +2398,16 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.52", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.52.tgz", - "integrity": "sha512-cQBz8g68Vnw1W2meXlkb3D/hXJU+Taiyj9P8qLJtjREEV9/Td65xi4A/H1sRQ8EIgX5qbZbvdYPKygKLholZ3w==", + "version": "4.2.53", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.53.tgz", + "integrity": "sha512-ybgCk+9JdBq8pYC8Y6U5fjyS8e4sboyAShetxPNL0rRBtaVl56GSFAxsolVBIea1tXR4LPIzL8i6xqmcf0+DCQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.4.16", + "@smithy/config-resolver": "^4.4.17", "@smithy/credential-provider-imds": "^4.2.14", "@smithy/node-config-provider": "^4.3.14", "@smithy/property-provider": "^4.2.14", - "@smithy/smithy-client": "^4.12.11", + "@smithy/smithy-client": "^4.12.12", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, @@ -2414,9 +2416,9 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.4.1.tgz", - "integrity": "sha512-wMxNDZJrgS5mQV9oxCs4TWl5767VMgOfqfZ3JHyCkMtGC2ykW9iPqMvFur695Otcc5yxLG8OKO/80tsQBxrhXg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.4.2.tgz", + "integrity": "sha512-a55Tr+3OKld4TTtnT+RhKOQHyPxm3j/xL4OR83WBUhLJaKDS9dnJ7arRMOp3t31dcLhApwG9bgvrRXBHlLdIkg==", "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^4.3.14", @@ -2453,12 +2455,12 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.3.2.tgz", - "integrity": "sha512-2+KTsJEwTi63NUv4uR9IQ+IFT1yu6Rf6JuoBK2WKaaJ/TRvOiOVGcXAsEqX/TQN2thR9yII21kPUJq1UV/WI2A==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.3.3.tgz", + "integrity": "sha512-idjUvd4M9Jj6rXkhqw4H4reHoweuK4ZxYWyOrEp4N2rOF5VtaOlQGLDQJva/8WanNXk9ScQtsAb7o5UHGvFm4A==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.2.14", + "@smithy/service-error-classification": "^4.3.0", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, @@ -2467,13 +2469,13 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.5.23", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.23.tgz", - "integrity": "sha512-N6on1+ngJ3RznZOnDWNveIwnTSlqxNnXuNAh7ez889ZZaRdXoNRTXKgmYOLe6dB0gCmAVtuRScE1hymQFl4hpg==", + "version": "4.5.24", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.24.tgz", + "integrity": "sha512-na5vv2mBSDzXewLEEoWGI7LQQkfpmFEomBsmOpzLFjqGctm0iMwXY5lAwesY9pIaErkccW0qzEOUcYP+WKneXg==", "license": "Apache-2.0", "dependencies": { "@smithy/fetch-http-handler": "^5.3.17", - "@smithy/node-http-handler": "^4.5.3", + "@smithy/node-http-handler": "^4.6.0", "@smithy/types": "^4.14.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-buffer-from": "^4.2.2", @@ -2574,14 +2576,17 @@ } }, "node_modules/@tigrisdata/agent-shell": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@tigrisdata/agent-shell/-/agent-shell-0.1.3.tgz", - "integrity": "sha512-5rtM6RnLJvpysfYBCRwi3Ivl9KYQRkyGOZK4JTAxrlRMzCDChqwat8qQ+uChywZ1QJYx60f6DoZInZS4k8llKQ==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tigrisdata/agent-shell/-/agent-shell-0.3.0.tgz", + "integrity": "sha512-vM+nMyYdM04ruihomnQl9MuEsrxKpnO9pcu9kYRSfgcJWrxzP0xfir43TBAAWcRxe+XSvpraTIPy6f5gZAzE/w==", "license": "MIT", "dependencies": { "@tigrisdata/storage": "^3.1.0", "just-bash": "^2.14.2" }, + "bin": { + "tigris-shell": "dist/cli.js" + }, "engines": { "node": ">=20.0.0" } diff --git a/playground/package.json b/playground/package.json index d0772fb..098a96c 100644 --- a/playground/package.json +++ b/playground/package.json @@ -8,7 +8,7 @@ "preview": "vite preview" }, "dependencies": { - "@tigrisdata/agent-shell": "^0.1.3", + "@tigrisdata/agent-shell": "^0.3.0", "@xterm/addon-fit": "^0.10.0", "@xterm/xterm": "^5.5.0" }, diff --git a/playground/src/credentials.ts b/playground/src/credentials.ts deleted file mode 100644 index 0c91296..0000000 --- a/playground/src/credentials.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { TigrisConfig } from "@tigrisdata/agent-shell"; - -let stored: TigrisConfig | null = null; - -export function setCredentials(config: TigrisConfig) { - stored = config; -} - -export function getCredentials(): TigrisConfig | null { - return stored; -} diff --git a/playground/src/shell-loop.ts b/playground/src/shell-loop.ts index 418d4f1..8b3dd81 100644 --- a/playground/src/shell-loop.ts +++ b/playground/src/shell-loop.ts @@ -1,27 +1,27 @@ -import type { TigrisConfig } from "@tigrisdata/agent-shell"; -import { TigrisShell } from "@tigrisdata/agent-shell"; +import type { ReplIO } from "@tigrisdata/agent-shell/repl"; +import { ReplSession } from "@tigrisdata/agent-shell/repl"; import type { Terminal } from "@xterm/xterm"; -import { getCredentials, setCredentials } from "./credentials.js"; const PROMPT = "\x1b[32m$ \x1b[0m"; -const YELLOW = "\x1b[33m"; -const RED = "\x1b[31m"; -const GREEN = "\x1b[32m"; -const DIM = "\x1b[2m"; -const RESET = "\x1b[0m"; +/** + * Thin xterm.js adapter over ReplSession. + * Handles keyboard input, history, and line editing. + * Delegates all command execution to the shared REPL layer. + */ export class ShellLoop { private currentLine = ""; private cursorPos = 0; private history: string[] = []; private historyIndex = -1; - private shell: TigrisShell | null = null; private terminal: Terminal; + private session: ReplSession; private busy = false; - private cwd: string | undefined; + private pendingPromptResolve: ((value: string) => void) | null = null; constructor(terminal: Terminal) { this.terminal = terminal; + this.session = new ReplSession(); } start() { @@ -29,6 +29,20 @@ export class ShellLoop { this.terminal.onData((data) => this.handleInput(data)); } + private get io(): ReplIO { + return { + write: (text: string) => { + this.terminal.write(text.replace(/\r?\n/g, "\r\n")); + }, + prompt: (message: string) => { + this.terminal.write(message.replace(/\r?\n/g, "\r\n")); + return new Promise((resolve) => { + this.pendingPromptResolve = resolve; + }); + }, + }; + } + private prompt() { this.terminal.write(`\r\n${PROMPT}`); this.currentLine = ""; @@ -36,18 +50,33 @@ export class ShellLoop { } private async handleInput(data: string) { - // Block input while a command is executing - if (this.busy) return; + if (this.busy && !this.pendingPromptResolve) return; if (data === "\r") { this.terminal.write("\r\n"); const line = this.currentLine.trim(); + this.currentLine = ""; + this.cursorPos = 0; + + // If a prompt is waiting for input, resolve it + if (this.pendingPromptResolve) { + const resolve = this.pendingPromptResolve; + this.pendingPromptResolve = null; + resolve(line); + return; + } if (line) { this.history.push(line); this.historyIndex = this.history.length; this.busy = true; - await this.execute(line); + + if (line === "clear") { + this.terminal.clear(); + } else { + await this.session.handle(line, this.io); + } + this.busy = false; } @@ -67,6 +96,11 @@ export class ShellLoop { if (data === "\x03") { this.terminal.write("^C"); + if (this.pendingPromptResolve) { + const resolve = this.pendingPromptResolve; + this.pendingPromptResolve = null; + resolve(""); + } this.prompt(); return; } @@ -77,23 +111,27 @@ export class ShellLoop { return; } - if (data === "\x1b[A") { - if (this.historyIndex > 0) { - this.historyIndex--; - this.setLine(this.history[this.historyIndex] ?? ""); + // Arrow keys only when not in prompt mode + if (!this.pendingPromptResolve) { + if (data === "\x1b[A") { + if (this.historyIndex > 0) { + this.historyIndex--; + this.setLine(this.history[this.historyIndex] ?? ""); + } + return; } - return; - } - if (data === "\x1b[B") { - if (this.historyIndex < this.history.length - 1) { - this.historyIndex++; - this.setLine(this.history[this.historyIndex] ?? ""); - } else { - this.historyIndex = this.history.length; - this.setLine(""); + if (data === "\x1b[B") { + if (this.historyIndex < this.history.length - 1) { + this.historyIndex++; + this.setLine(this.history[this.historyIndex] ?? ""); + } else { + this.historyIndex = this.history.length; + this.setLine(""); + } + return; } - return; } + if (data === "\x1b[C") { if (this.cursorPos < this.currentLine.length) { this.cursorPos++; @@ -128,121 +166,4 @@ export class ShellLoop { this.terminal.write(`\x1b[${back}D`); } } - - private async execute(command: string) { - if (command === "clear") { - this.terminal.clear(); - return; - } - - const firstToken = command.split(/\s+/)[0]; - - if (firstToken === "configure") { - this.handleConfigure(command); - return; - } - - if (!this.shell) { - this.writeOutput(`${RED}Not configured. Run 'configure' first.${RESET}\r\n`); - return; - } - - if (command === "flush") { - await this.handleFlush(); - return; - } - - try { - const result = await this.shell.engine.exec(command, { - ...(this.cwd !== undefined && { cwd: this.cwd }), - }); - - // Track cwd changes (e.g. cd) across exec calls - if (result.env?.PWD) { - this.cwd = result.env.PWD; - } - - if (result.stdout) { - this.writeOutput(result.stdout); - } - if (result.stderr) { - this.writeOutput(`${RED}${result.stderr}${RESET}`); - } - } catch (err) { - const message = err instanceof Error ? err.message : String(err); - this.writeOutput(`${RED}Error: ${message}${RESET}\r\n`); - } - } - - private handleConfigure(command: string) { - const args = command.split(/\s+/); - let bucket: string | undefined; - let accessKeyId: string | undefined; - let secretAccessKey: string | undefined; - - for (let i = 1; i < args.length; i++) { - if (args[i] === "--bucket" && args[i + 1]) { - bucket = args[i + 1]; - i++; - } else if (args[i] === "--key" && args[i + 1]) { - accessKeyId = args[i + 1]; - i++; - } else if (args[i] === "--secret" && args[i + 1]) { - secretAccessKey = args[i + 1]; - i++; - } - } - - if (!bucket || !accessKeyId || !secretAccessKey) { - const creds = getCredentials(); - if (creds) { - this.writeOutput(`${GREEN}Connected to bucket: ${creds.bucket}${RESET}\r\n`); - this.writeOutput(`${DIM}Access key: ${creds.accessKeyId.slice(0, 8)}...${RESET}\r\n`); - } else { - this.writeOutput( - "Usage: configure --bucket --key --secret \r\n", - ); - } - return; - } - - const config: TigrisConfig = { bucket, accessKeyId, secretAccessKey }; - setCredentials(config); - - this.shell = new TigrisShell(config, { - env: { BUCKET: bucket }, - }); - this.cwd = undefined; - - this.writeOutput(`${GREEN}Connected to bucket: ${bucket}${RESET}\r\n`); - this.writeOutput("\r\n"); - this.writeOutput( - `${YELLOW}WARNING: Credentials are stored in browser memory only.${RESET}\r\n`, - ); - this.writeOutput(`${YELLOW}They will be lost when you close or refresh this tab.${RESET}\r\n`); - this.writeOutput("\r\n"); - this.writeOutput(`${DIM}Commands available: flush, presign, snapshot, fork${RESET}\r\n`); - this.writeOutput(`${DIM}Use $BUCKET in commands, e.g.: snapshot $BUCKET --list${RESET}\r\n`); - } - - private async handleFlush() { - if (!this.shell) { - this.writeOutput(`${RED}Not configured. Run 'configure' first.${RESET}\r\n`); - return; - } - - try { - await this.shell.flush(); - const creds = getCredentials(); - this.writeOutput(`${GREEN}Flushed to bucket: ${creds?.bucket}${RESET}\r\n`); - } catch (err) { - const message = err instanceof Error ? err.message : String(err); - this.writeOutput(`${RED}Flush failed: ${message}${RESET}\r\n`); - } - } - - private writeOutput(text: string) { - const lines = text.replace(/\r?\n/g, "\r\n"); - this.terminal.write(lines); - } } diff --git a/playground/src/welcome.ts b/playground/src/welcome.ts index c48ef99..7a200a4 100644 --- a/playground/src/welcome.ts +++ b/playground/src/welcome.ts @@ -20,12 +20,7 @@ export function showWelcome(terminal: Terminal) { ); terminal.writeln(""); terminal.writeln(`${DIM}Connect to Tigris:${RESET}`); - terminal.writeln( - `${DIM} configure --bucket --key --secret ${RESET}`, - ); + terminal.writeln(`${DIM} configure --key --secret Set credentials${RESET}`); terminal.writeln(""); - terminal.writeln( - `${DIM}After configuring, all bash commands are available. Files are stored${RESET}`, - ); - terminal.writeln(`${DIM}in memory until you run flush.${RESET}`); + terminal.writeln(`${DIM}Type 'help' for all commands.${RESET}`); } From 61a0609c4cc97f4fe67ab4d615081470f84abb9b Mon Sep 17 00:00:00 2001 From: A Ibrahim Date: Wed, 22 Apr 2026 12:27:44 +0200 Subject: [PATCH 2/3] fix: move device flow to cli, make REPL auth-agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - src/repl/auth.ts: slimmed to types only (LoginFn, LoginResult, Organization) - src/cli/auth.ts: device flow implementation moved here (CLI-specific) - ReplSession accepts loginFn via constructor — no hardcoded auth - CLI passes deviceLogin, playground passes browserLogin Co-Authored-By: Claude Opus 4.6 (1M context) --- src/cli.ts | 3 +- src/cli/auth.ts | 138 ++++++++++++++++++++++++++++++++++++++++ src/repl/auth.ts | 152 +------------------------------------------- src/repl/index.ts | 4 +- src/repl/session.ts | 27 +++++--- 5 files changed, 163 insertions(+), 161 deletions(-) create mode 100644 src/cli/auth.ts diff --git a/src/cli.ts b/src/cli.ts index bd15d9e..ae174d0 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,6 +1,7 @@ #!/usr/bin/env node import * as readline from "node:readline"; +import { deviceLogin } from "./cli/auth.js"; import type { ReplIO } from "./repl/index.js"; import { ReplSession } from "./repl/index.js"; @@ -34,7 +35,7 @@ function parseArgs(args: string[]): { async function main() { const args = parseArgs(process.argv.slice(2)); - const session = new ReplSession(); + const session = new ReplSession({ loginFn: deviceLogin }); const rl = readline.createInterface({ input: process.stdin, diff --git a/src/cli/auth.ts b/src/cli/auth.ts new file mode 100644 index 0000000..db370b5 --- /dev/null +++ b/src/cli/auth.ts @@ -0,0 +1,138 @@ +import type { LoginResult, Organization } from "../repl/auth.js"; +import type { ReplIO } from "../repl/io.js"; + +const AUTH0_DOMAIN = "https://auth.storage.tigrisdata.io"; +const AUTH0_CLIENT_ID = "FKXunmhaaBZOYXjNYLIU8Fi2jIqpT7DR"; +const AUTH0_AUDIENCE = "https://tigris-os-api"; +const AUTH0_SCOPES = "openid profile email offline_access"; +const CLAIMS_NAMESPACE = "https://tigris"; +const DEFAULT_POLL_INTERVAL = 5; + +interface DeviceCodeResponse { + device_code: string; + user_code: string; + verification_uri: string; + verification_uri_complete: string; + expires_in: number; + interval: number; +} + +interface TokenResponse { + access_token: string; + refresh_token?: string; + id_token?: string; + expires_in: number; + token_type: string; +} + +async function requestDeviceCode(): Promise { + const response = await fetch(`${AUTH0_DOMAIN}/oauth/device/code`, { + method: "POST", + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams({ + client_id: AUTH0_CLIENT_ID, + audience: AUTH0_AUDIENCE, + scope: AUTH0_SCOPES, + }), + }); + + if (!response.ok) { + const text = await response.text(); + throw new Error(`Device code request failed: ${text}`); + } + + return response.json() as Promise; +} + +async function pollForToken(deviceCode: string, interval: number): Promise { + let pollInterval = Math.max(interval, DEFAULT_POLL_INTERVAL) * 1000; + + for (;;) { + await new Promise((resolve) => setTimeout(resolve, pollInterval)); + + const response = await fetch(`${AUTH0_DOMAIN}/oauth/token`, { + method: "POST", + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams({ + client_id: AUTH0_CLIENT_ID, + device_code: deviceCode, + grant_type: "urn:ietf:params:oauth:grant-type:device_code", + }), + }); + + const data = (await response.json()) as TokenResponse & { error?: string }; + + if (data.error === "authorization_pending") { + continue; + } + if (data.error === "slow_down") { + pollInterval += 5000; // RFC 8628 §3.5: permanently increase by 5s + continue; + } + if (data.error) { + throw new Error(`Authorization failed: ${data.error}`); + } + + return data; + } +} + +function extractEmail(idToken: string): string { + const parts = idToken.split("."); + if (parts.length !== 3 || !parts[1]) { + return "unknown"; + } + + try { + const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/"); + const payload = JSON.parse(atob(base64)); + return payload.email ?? payload.name ?? "unknown"; + } catch { + return "unknown"; + } +} + +async function fetchOrganizations(accessToken: string): Promise { + const response = await fetch(`${AUTH0_DOMAIN}/userinfo`, { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + + if (!response.ok) { + return []; + } + + const data = (await response.json()) as Record; + const claims = data[CLAIMS_NAMESPACE] as { ns?: Organization[] } | undefined; + + return claims?.ns ?? []; +} + +/** + * OAuth Device Flow login for CLI (Node.js). + */ +export async function deviceLogin(io: ReplIO): Promise { + io.write("Logging in to Tigris...\n"); + + const deviceCode = await requestDeviceCode(); + + io.write("\nOpen this URL in your browser:\n"); + io.write(` ${deviceCode.verification_uri_complete}\n\n`); + io.write(`Or go to ${deviceCode.verification_uri} and enter code: ${deviceCode.user_code}\n\n`); + io.write("Waiting for authorization..."); + + const tokens = await pollForToken(deviceCode.device_code, deviceCode.interval); + + io.write(" done!\n\n"); + + const email = tokens.id_token ? extractEmail(tokens.id_token) : "unknown"; + io.write(`Logged in as ${email}\n`); + + const organizations = await fetchOrganizations(tokens.access_token); + + return { + accessToken: tokens.access_token, + ...(tokens.refresh_token !== undefined && { refreshToken: tokens.refresh_token }), + email, + organizations, + }; +} diff --git a/src/repl/auth.ts b/src/repl/auth.ts index 5b726c8..cfe17ba 100644 --- a/src/repl/auth.ts +++ b/src/repl/auth.ts @@ -1,29 +1,5 @@ import type { ReplIO } from "./io.js"; -const AUTH0_DOMAIN = "https://auth.storage.tigrisdata.io"; -const AUTH0_CLIENT_ID = "FKXunmhaaBZOYXjNYLIU8Fi2jIqpT7DR"; -const AUTH0_AUDIENCE = "https://tigris-os-api"; -const AUTH0_SCOPES = "openid profile email offline_access"; -const CLAIMS_NAMESPACE = "https://tigris"; -const DEFAULT_POLL_INTERVAL = 5; - -interface DeviceCodeResponse { - device_code: string; - user_code: string; - verification_uri: string; - verification_uri_complete: string; - expires_in: number; - interval: number; -} - -interface TokenResponse { - access_token: string; - refresh_token?: string; - id_token?: string; - expires_in: number; - token_type: string; -} - export interface Organization { id: string; name: string; @@ -36,129 +12,5 @@ export interface LoginResult { organizations: Organization[]; } -/** - * Start the OAuth Device Authorization Flow. - * Returns device code info for display to the user. - */ -async function requestDeviceCode(): Promise { - const response = await fetch(`${AUTH0_DOMAIN}/oauth/device/code`, { - method: "POST", - headers: { "Content-Type": "application/x-www-form-urlencoded" }, - body: new URLSearchParams({ - client_id: AUTH0_CLIENT_ID, - audience: AUTH0_AUDIENCE, - scope: AUTH0_SCOPES, - }), - }); - - if (!response.ok) { - const text = await response.text(); - throw new Error(`Device code request failed: ${text}`); - } - - return response.json() as Promise; -} - -/** - * Poll the token endpoint until the user authorizes or the code expires. - */ -async function pollForToken(deviceCode: string, interval: number): Promise { - let pollInterval = Math.max(interval, DEFAULT_POLL_INTERVAL) * 1000; - - for (;;) { - await new Promise((resolve) => setTimeout(resolve, pollInterval)); - - const response = await fetch(`${AUTH0_DOMAIN}/oauth/token`, { - method: "POST", - headers: { "Content-Type": "application/x-www-form-urlencoded" }, - body: new URLSearchParams({ - client_id: AUTH0_CLIENT_ID, - device_code: deviceCode, - grant_type: "urn:ietf:params:oauth:grant-type:device_code", - }), - }); - - const data = (await response.json()) as TokenResponse & { error?: string }; - - if (data.error === "authorization_pending") { - continue; - } - if (data.error === "slow_down") { - pollInterval += 5000; // RFC 8628 §3.5: permanently increase by 5s - continue; - } - if (data.error) { - throw new Error(`Authorization failed: ${data.error}`); - } - - return data; - } -} - -/** - * Extract email from ID token (base64-decode the payload, no verification needed). - */ -function extractEmail(idToken: string): string { - const parts = idToken.split("."); - if (parts.length !== 3 || !parts[1]) { - return "unknown"; - } - - try { - // Handle base64url encoding - const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/"); - const payload = JSON.parse(atob(base64)); - return payload.email ?? payload.name ?? "unknown"; - } catch { - return "unknown"; - } -} - -/** - * Fetch organizations from the userinfo endpoint. - */ -async function fetchOrganizations(accessToken: string): Promise { - const response = await fetch(`${AUTH0_DOMAIN}/userinfo`, { - headers: { Authorization: `Bearer ${accessToken}` }, - }); - - if (!response.ok) { - return []; - } - - const data = (await response.json()) as Record; - const claims = data[CLAIMS_NAMESPACE] as { ns?: Organization[] } | undefined; - - return claims?.ns ?? []; -} - -/** - * Run the full device authorization flow. - * Shows URL + code, waits for auth, fetches orgs. - */ -export async function deviceLogin(io: ReplIO): Promise { - io.write("Logging in to Tigris...\n"); - - const deviceCode = await requestDeviceCode(); - - io.write(`\nOpen this URL in your browser:\n`); - io.write(` ${deviceCode.verification_uri_complete}\n\n`); - io.write(`Or go to ${deviceCode.verification_uri} and enter code: ${deviceCode.user_code}\n\n`); - io.write("Waiting for authorization..."); - - const tokens = await pollForToken(deviceCode.device_code, deviceCode.interval); - - io.write(" done!\n\n"); - - const email = tokens.id_token ? extractEmail(tokens.id_token) : "unknown"; - io.write(`Logged in as ${email}\n`); - - const organizations = await fetchOrganizations(tokens.access_token); - - return { - accessToken: tokens.access_token, - ...(tokens.refresh_token !== undefined && { refreshToken: tokens.refresh_token }), - email, - organizations, - }; -} +/** Login function signature — device flow (CLI) or Auth0 SDK (browser). */ +export type LoginFn = (io: ReplIO) => Promise; diff --git a/src/repl/index.ts b/src/repl/index.ts index f3a5d69..be4a8ff 100644 --- a/src/repl/index.ts +++ b/src/repl/index.ts @@ -1,4 +1,4 @@ -export type { LoginResult, Organization } from "./auth.js"; -export { deviceLogin } from "./auth.js"; +export type { LoginFn, LoginResult, Organization } from "./auth.js"; export type { ReplIO } from "./io.js"; +export type { ReplSessionOptions } from "./session.js"; export { ReplSession } from "./session.js"; diff --git a/src/repl/session.ts b/src/repl/session.ts index dc9c1d1..6de7b98 100644 --- a/src/repl/session.ts +++ b/src/repl/session.ts @@ -2,9 +2,14 @@ import { listBuckets } from "@tigrisdata/storage"; import type { BashExecResult } from "just-bash"; import { TigrisShell } from "../shell.js"; import type { TigrisConfig } from "../types.js"; -import { deviceLogin } from "./auth.js"; +import type { LoginFn } from "./auth.js"; import type { ReplIO } from "./io.js"; +export interface ReplSessionOptions { + /** Login function — device flow for CLI, Auth0 SDK for browser. */ + loginFn?: LoginFn; +} + /** * REPL session — owns the TigrisShell lifecycle and handles * built-in commands (configure, mount, unmount, flush, etc). @@ -17,6 +22,11 @@ export class ReplSession { private authMethod: "access-key" | "oauth" | null = null; private email: string | undefined; private cwd: string | undefined; + private loginFn: LoginFn | undefined; + + constructor(options?: ReplSessionOptions) { + this.loginFn = options?.loginFn; + } /** Handle a command line. Returns true if handled, false to pass to bash. */ async handle(line: string, io: ReplIO): Promise { @@ -158,8 +168,14 @@ export class ReplSession { } private async handleLogin(io: ReplIO): Promise { + if (!this.loginFn) { + io.write("login: no login method configured.\n"); + io.write("Use 'configure' with access keys instead.\n"); + return; + } + try { - const result = await deviceLogin(io); + const result = await this.loginFn(io); if (result.organizations.length === 0) { io.write("No organizations found.\n"); @@ -200,12 +216,7 @@ export class ReplSession { await this.listAndMountBuckets(newConfig, newShell, "oauth", io); } catch (err) { const message = err instanceof Error ? err.message : String(err); - if (message.includes("fetch") || message.includes("CORS") || message.includes("network")) { - io.write("login: OAuth login is not supported in the browser.\n"); - io.write("Use 'configure' with access keys instead.\n"); - } else { - io.write(`login: ${message}\n`); - } + io.write(`login: ${message}\n`); } } From 435419f5af78a29c59c1a9c0fe3a3532ea5184dc Mon Sep 17 00:00:00 2001 From: A Ibrahim Date: Wed, 22 Apr 2026 12:44:00 +0200 Subject: [PATCH 3/3] chore: add Auth0 SPA login for playground, fix prompt redraw - playground/src/auth.ts: browser login via @auth0/auth0-spa-js - ShellLoop passes browserLogin to ReplSession - Fix prompt redraw showing $ instead of "Enter number: " during org selection Co-Authored-By: Claude Opus 4.6 (1M context) --- playground/src/auth.ts | 61 ++++++++++++++++++++++++++++++++++++ playground/src/shell-loop.ts | 19 ++++++++--- 2 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 playground/src/auth.ts diff --git a/playground/src/auth.ts b/playground/src/auth.ts new file mode 100644 index 0000000..5c89729 --- /dev/null +++ b/playground/src/auth.ts @@ -0,0 +1,61 @@ +import { createAuth0Client } from "@auth0/auth0-spa-js"; +import type { LoginFn, Organization } from "@tigrisdata/agent-shell/repl"; + +const AUTH0_DOMAIN = "auth.storage.tigrisdata.io"; +const AUTH0_CLIENT_ID = "FKXunmhaaBZOYXjNYLIU8Fi2jIqpT7DR"; +const AUTH0_AUDIENCE = "https://tigris-os-api"; +const CLAIMS_NAMESPACE = "https://tigris"; + +/** + * Browser login using Auth0 SPA SDK (Authorization Code + PKCE). + * Opens a popup for authentication. + */ +export const browserLogin: LoginFn = async (io) => { + io.write("Logging in to Tigris...\n"); + + const auth0 = await createAuth0Client({ + domain: AUTH0_DOMAIN, + clientId: AUTH0_CLIENT_ID, + authorizationParams: { + audience: AUTH0_AUDIENCE, + scope: "openid profile email offline_access", + redirect_uri: window.location.origin, + }, + }); + + await auth0.loginWithPopup({ + authorizationParams: { + audience: AUTH0_AUDIENCE, + scope: "openid profile email offline_access", + }, + }); + + const accessToken = await auth0.getTokenSilently({ + authorizationParams: { + audience: AUTH0_AUDIENCE, + }, + }); + + const user = await auth0.getUser(); + const email = user?.email ?? user?.name ?? "unknown"; + + io.write(`Logged in as ${email}\n`); + + // Fetch organizations from userinfo + const userInfoResponse = await fetch(`https://${AUTH0_DOMAIN}/userinfo`, { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + + let organizations: Organization[] = []; + if (userInfoResponse.ok) { + const data = (await userInfoResponse.json()) as Record; + const claims = data[CLAIMS_NAMESPACE] as { ns?: Organization[] } | undefined; + organizations = claims?.ns ?? []; + } + + return { + accessToken, + email, + organizations, + }; +}; diff --git a/playground/src/shell-loop.ts b/playground/src/shell-loop.ts index 8b3dd81..227fad3 100644 --- a/playground/src/shell-loop.ts +++ b/playground/src/shell-loop.ts @@ -1,6 +1,7 @@ import type { ReplIO } from "@tigrisdata/agent-shell/repl"; import { ReplSession } from "@tigrisdata/agent-shell/repl"; import type { Terminal } from "@xterm/xterm"; +import { browserLogin } from "./auth.js"; const PROMPT = "\x1b[32m$ \x1b[0m"; @@ -18,10 +19,11 @@ export class ShellLoop { private session: ReplSession; private busy = false; private pendingPromptResolve: ((value: string) => void) | null = null; + private pendingPromptText = ""; constructor(terminal: Terminal) { this.terminal = terminal; - this.session = new ReplSession(); + this.session = new ReplSession({ loginFn: browserLogin }); } start() { @@ -36,6 +38,9 @@ export class ShellLoop { }, prompt: (message: string) => { this.terminal.write(message.replace(/\r?\n/g, "\r\n")); + // Store only the last line for redrawing (strip leading newlines) + const lines = message.split(/\r?\n/); + this.pendingPromptText = lines[lines.length - 1] ?? ""; return new Promise((resolve) => { this.pendingPromptResolve = resolve; }); @@ -62,6 +67,7 @@ export class ShellLoop { if (this.pendingPromptResolve) { const resolve = this.pendingPromptResolve; this.pendingPromptResolve = null; + this.pendingPromptText = ""; resolve(line); return; } @@ -95,11 +101,14 @@ export class ShellLoop { } if (data === "\x03") { - this.terminal.write("^C"); + this.terminal.write("^C\r\n"); if (this.pendingPromptResolve) { const resolve = this.pendingPromptResolve; this.pendingPromptResolve = null; + this.pendingPromptText = ""; resolve(""); + // Don't prompt here — the session flow will resume and prompt when done + return; } this.prompt(); return; @@ -107,7 +116,8 @@ export class ShellLoop { if (data === "\x0c") { this.terminal.clear(); - this.terminal.write(PROMPT + this.currentLine); + const prefix = this.pendingPromptResolve ? this.pendingPromptText : PROMPT; + this.terminal.write(prefix + this.currentLine); return; } @@ -160,7 +170,8 @@ export class ShellLoop { } private redrawLine() { - this.terminal.write(`\r\x1b[K${PROMPT}${this.currentLine}`); + const prefix = this.pendingPromptResolve ? this.pendingPromptText : PROMPT; + this.terminal.write(`\r\x1b[K${prefix}${this.currentLine}`); const back = this.currentLine.length - this.cursorPos; if (back > 0) { this.terminal.write(`\x1b[${back}D`);