spindb 0.37.2 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/bin.js +9 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/commands/attach.js +102 -0
- package/dist/cli/commands/attach.js.map +1 -0
- package/dist/cli/commands/backup.js +197 -0
- package/dist/cli/commands/backup.js.map +1 -0
- package/dist/cli/commands/backups.js +190 -0
- package/dist/cli/commands/backups.js.map +1 -0
- package/dist/cli/commands/clone.js +119 -0
- package/dist/cli/commands/clone.js.map +1 -0
- package/dist/cli/commands/config.js +276 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/connect.js +559 -0
- package/dist/cli/commands/connect.js.map +1 -0
- package/dist/cli/commands/create.js +952 -0
- package/dist/cli/commands/create.js.map +1 -0
- package/dist/cli/commands/databases.js +485 -0
- package/dist/cli/commands/databases.js.map +1 -0
- package/dist/cli/commands/delete.js +106 -0
- package/dist/cli/commands/delete.js.map +1 -0
- package/dist/cli/commands/deps.js +238 -0
- package/dist/cli/commands/deps.js.map +1 -0
- package/dist/cli/commands/detach.js +81 -0
- package/dist/cli/commands/detach.js.map +1 -0
- package/dist/cli/commands/doctor.js +567 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/duckdb.js +207 -0
- package/dist/cli/commands/duckdb.js.map +1 -0
- package/dist/cli/commands/edit.js +524 -0
- package/dist/cli/commands/edit.js.map +1 -0
- package/dist/cli/commands/engines.js +1414 -0
- package/dist/cli/commands/engines.js.map +1 -0
- package/dist/cli/commands/export.js +383 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/info.js +270 -0
- package/dist/cli/commands/info.js.map +1 -0
- package/dist/cli/commands/list.js +215 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/logs.js +81 -0
- package/dist/cli/commands/logs.js.map +1 -0
- package/dist/cli/commands/menu/backup-handlers.js +1202 -0
- package/dist/cli/commands/menu/backup-handlers.js.map +1 -0
- package/dist/cli/commands/menu/container-handlers.js +1788 -0
- package/dist/cli/commands/menu/container-handlers.js.map +1 -0
- package/dist/cli/commands/menu/engine-handlers.js +235 -0
- package/dist/cli/commands/menu/engine-handlers.js.map +1 -0
- package/dist/cli/commands/menu/index.js +266 -0
- package/dist/cli/commands/menu/index.js.map +1 -0
- package/dist/cli/commands/menu/settings-handlers.js +320 -0
- package/dist/cli/commands/menu/settings-handlers.js.map +1 -0
- package/dist/cli/commands/menu/shared.js +13 -0
- package/dist/cli/commands/menu/shared.js.map +1 -0
- package/dist/cli/commands/menu/shell-handlers.js +1573 -0
- package/dist/cli/commands/menu/shell-handlers.js.map +1 -0
- package/dist/cli/commands/menu/sql-handlers.js +185 -0
- package/dist/cli/commands/menu/sql-handlers.js.map +1 -0
- package/dist/cli/commands/menu/update-handlers.js +322 -0
- package/dist/cli/commands/menu/update-handlers.js.map +1 -0
- package/dist/cli/commands/menu/validators.js +9 -0
- package/dist/cli/commands/menu/validators.js.map +1 -0
- package/dist/cli/commands/ports.js +166 -0
- package/dist/cli/commands/ports.js.map +1 -0
- package/dist/cli/commands/pull.js +166 -0
- package/dist/cli/commands/pull.js.map +1 -0
- package/dist/cli/commands/query.js +180 -0
- package/dist/cli/commands/query.js.map +1 -0
- package/dist/cli/commands/restore.js +428 -0
- package/dist/cli/commands/restore.js.map +1 -0
- package/dist/cli/commands/run.js +115 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/self-update.js +99 -0
- package/dist/cli/commands/self-update.js.map +1 -0
- package/dist/cli/commands/sqlite.js +207 -0
- package/dist/cli/commands/sqlite.js.map +1 -0
- package/dist/cli/commands/start.js +196 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/stop.js +182 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/commands/url.js +88 -0
- package/dist/cli/commands/url.js.map +1 -0
- package/dist/cli/commands/users.js +189 -0
- package/dist/cli/commands/users.js.map +1 -0
- package/dist/cli/commands/version.js +52 -0
- package/dist/cli/commands/version.js.map +1 -0
- package/dist/cli/commands/which.js +258 -0
- package/dist/cli/commands/which.js.map +1 -0
- package/dist/cli/constants.js +212 -0
- package/dist/cli/constants.js.map +1 -0
- package/dist/cli/helpers.js +1120 -0
- package/dist/cli/helpers.js.map +1 -0
- package/dist/cli/index.js +146 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/ui/prompts.js +1002 -0
- package/dist/cli/ui/prompts.js.map +1 -0
- package/dist/cli/ui/spinner.js +74 -0
- package/dist/cli/ui/spinner.js.map +1 -0
- package/dist/cli/ui/theme.js +99 -0
- package/dist/cli/ui/theme.js.map +1 -0
- package/dist/cli/utils/file-follower.js +79 -0
- package/dist/cli/utils/file-follower.js.map +1 -0
- package/dist/config/backup-formats.js +363 -0
- package/dist/config/backup-formats.js.map +1 -0
- package/dist/config/defaults.js +25 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/engine-defaults.js +303 -0
- package/dist/config/engine-defaults.js.map +1 -0
- package/dist/config/engines-registry.js +103 -0
- package/dist/config/engines-registry.js.map +1 -0
- package/dist/config/os-dependencies.js +767 -0
- package/dist/config/os-dependencies.js.map +1 -0
- package/dist/config/paths.js +156 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/version.js +3 -0
- package/dist/config/version.js.map +1 -0
- package/dist/core/backup-restore.js +219 -0
- package/dist/core/backup-restore.js.map +1 -0
- package/dist/core/base-binary-manager.js +403 -0
- package/dist/core/base-binary-manager.js.map +1 -0
- package/dist/core/base-document-binary-manager.js +364 -0
- package/dist/core/base-document-binary-manager.js.map +1 -0
- package/dist/core/base-embedded-binary-manager.js +364 -0
- package/dist/core/base-embedded-binary-manager.js.map +1 -0
- package/dist/core/base-server-binary-manager.js +368 -0
- package/dist/core/base-server-binary-manager.js.map +1 -0
- package/dist/core/config-manager.js +495 -0
- package/dist/core/config-manager.js.map +1 -0
- package/dist/core/container-manager.js +609 -0
- package/dist/core/container-manager.js.map +1 -0
- package/dist/core/credential-generator.js +67 -0
- package/dist/core/credential-generator.js.map +1 -0
- package/dist/core/credential-manager.js +211 -0
- package/dist/core/credential-manager.js.map +1 -0
- package/dist/core/dblab-utils.js +105 -0
- package/dist/core/dblab-utils.js.map +1 -0
- package/dist/core/dependency-manager.js +359 -0
- package/dist/core/dependency-manager.js.map +1 -0
- package/dist/core/docker-exporter.js +1077 -0
- package/dist/core/docker-exporter.js.map +1 -0
- package/dist/core/error-handler.js +295 -0
- package/dist/core/error-handler.js.map +1 -0
- package/dist/core/fs-error-utils.js +74 -0
- package/dist/core/fs-error-utils.js.map +1 -0
- package/dist/core/homebrew-version-manager.js +280 -0
- package/dist/core/homebrew-version-manager.js.map +1 -0
- package/dist/core/hostdb-client.js +252 -0
- package/dist/core/hostdb-client.js.map +1 -0
- package/dist/core/hostdb-metadata.js +243 -0
- package/dist/core/hostdb-metadata.js.map +1 -0
- package/dist/core/hostdb-releases-factory.js +161 -0
- package/dist/core/hostdb-releases-factory.js.map +1 -0
- package/dist/core/library-env.js +88 -0
- package/dist/core/library-env.js.map +1 -0
- package/dist/core/pgweb-utils.js +53 -0
- package/dist/core/pgweb-utils.js.map +1 -0
- package/dist/core/platform-service.js +632 -0
- package/dist/core/platform-service.js.map +1 -0
- package/dist/core/port-manager.js +136 -0
- package/dist/core/port-manager.js.map +1 -0
- package/dist/core/process-manager.js +445 -0
- package/dist/core/process-manager.js.map +1 -0
- package/dist/core/pull-manager.js +418 -0
- package/dist/core/pull-manager.js.map +1 -0
- package/dist/core/query-parser.js +449 -0
- package/dist/core/query-parser.js.map +1 -0
- package/dist/core/spawn-utils.js +90 -0
- package/dist/core/spawn-utils.js.map +1 -0
- package/dist/core/start-with-retry.js +90 -0
- package/dist/core/start-with-retry.js.map +1 -0
- package/dist/core/test-cleanup.js +85 -0
- package/dist/core/test-cleanup.js.map +1 -0
- package/dist/core/tls-generator.js +84 -0
- package/dist/core/tls-generator.js.map +1 -0
- package/dist/core/transaction-manager.js +139 -0
- package/dist/core/transaction-manager.js.map +1 -0
- package/dist/core/update-manager.js +241 -0
- package/dist/core/update-manager.js.map +1 -0
- package/dist/core/version-migration.js +260 -0
- package/dist/core/version-migration.js.map +1 -0
- package/dist/core/version-utils.js +91 -0
- package/dist/core/version-utils.js.map +1 -0
- package/dist/engines/base-engine.js +179 -0
- package/dist/engines/base-engine.js.map +1 -0
- package/dist/engines/clickhouse/backup.js +289 -0
- package/dist/engines/clickhouse/backup.js.map +1 -0
- package/dist/engines/clickhouse/binary-manager.js +145 -0
- package/dist/engines/clickhouse/binary-manager.js.map +1 -0
- package/dist/engines/clickhouse/binary-urls.js +100 -0
- package/dist/engines/clickhouse/binary-urls.js.map +1 -0
- package/dist/engines/clickhouse/cli-utils.js +143 -0
- package/dist/engines/clickhouse/cli-utils.js.map +1 -0
- package/dist/engines/clickhouse/hostdb-releases.js +24 -0
- package/dist/engines/clickhouse/hostdb-releases.js.map +1 -0
- package/dist/engines/clickhouse/index.js +1077 -0
- package/dist/engines/clickhouse/index.js.map +1 -0
- package/dist/engines/clickhouse/restore.js +335 -0
- package/dist/engines/clickhouse/restore.js.map +1 -0
- package/dist/engines/clickhouse/version-maps.js +83 -0
- package/dist/engines/clickhouse/version-maps.js.map +1 -0
- package/dist/engines/clickhouse/version-validator.js +133 -0
- package/dist/engines/clickhouse/version-validator.js.map +1 -0
- package/dist/engines/cockroachdb/backup.js +261 -0
- package/dist/engines/cockroachdb/backup.js.map +1 -0
- package/dist/engines/cockroachdb/binary-manager.js +33 -0
- package/dist/engines/cockroachdb/binary-manager.js.map +1 -0
- package/dist/engines/cockroachdb/binary-urls.js +33 -0
- package/dist/engines/cockroachdb/binary-urls.js.map +1 -0
- package/dist/engines/cockroachdb/cli-utils.js +338 -0
- package/dist/engines/cockroachdb/cli-utils.js.map +1 -0
- package/dist/engines/cockroachdb/hostdb-releases.js +21 -0
- package/dist/engines/cockroachdb/hostdb-releases.js.map +1 -0
- package/dist/engines/cockroachdb/index.js +1016 -0
- package/dist/engines/cockroachdb/index.js.map +1 -0
- package/dist/engines/cockroachdb/restore.js +323 -0
- package/dist/engines/cockroachdb/restore.js.map +1 -0
- package/dist/engines/cockroachdb/version-maps.js +37 -0
- package/dist/engines/cockroachdb/version-maps.js.map +1 -0
- package/dist/engines/couchdb/api-client.js +64 -0
- package/dist/engines/couchdb/api-client.js.map +1 -0
- package/dist/engines/couchdb/backup.js +90 -0
- package/dist/engines/couchdb/backup.js.map +1 -0
- package/dist/engines/couchdb/binary-manager.js +62 -0
- package/dist/engines/couchdb/binary-manager.js.map +1 -0
- package/dist/engines/couchdb/binary-urls.js +92 -0
- package/dist/engines/couchdb/binary-urls.js.map +1 -0
- package/dist/engines/couchdb/hostdb-releases.js +21 -0
- package/dist/engines/couchdb/hostdb-releases.js.map +1 -0
- package/dist/engines/couchdb/index.js +1043 -0
- package/dist/engines/couchdb/index.js.map +1 -0
- package/dist/engines/couchdb/restore.js +198 -0
- package/dist/engines/couchdb/restore.js.map +1 -0
- package/dist/engines/couchdb/version-maps.js +67 -0
- package/dist/engines/couchdb/version-maps.js.map +1 -0
- package/dist/engines/couchdb/version-validator.js +88 -0
- package/dist/engines/couchdb/version-validator.js.map +1 -0
- package/dist/engines/duckdb/binary-manager.js +33 -0
- package/dist/engines/duckdb/binary-manager.js.map +1 -0
- package/{engines/duckdb/binary-urls.ts → dist/engines/duckdb/binary-urls.js} +11 -16
- package/dist/engines/duckdb/binary-urls.js.map +1 -0
- package/dist/engines/duckdb/hostdb-releases.js +21 -0
- package/dist/engines/duckdb/hostdb-releases.js.map +1 -0
- package/dist/engines/duckdb/index.js +594 -0
- package/dist/engines/duckdb/index.js.map +1 -0
- package/dist/engines/duckdb/registry.js +265 -0
- package/dist/engines/duckdb/registry.js.map +1 -0
- package/dist/engines/duckdb/scanner.js +12 -0
- package/dist/engines/duckdb/scanner.js.map +1 -0
- package/dist/engines/duckdb/version-maps.js +67 -0
- package/dist/engines/duckdb/version-maps.js.map +1 -0
- package/dist/engines/duckdb/version-validator.js +62 -0
- package/dist/engines/duckdb/version-validator.js.map +1 -0
- package/dist/engines/ferretdb/backup.js +170 -0
- package/dist/engines/ferretdb/backup.js.map +1 -0
- package/dist/engines/ferretdb/binary-manager.js +765 -0
- package/dist/engines/ferretdb/binary-manager.js.map +1 -0
- package/dist/engines/ferretdb/binary-urls.js +135 -0
- package/dist/engines/ferretdb/binary-urls.js.map +1 -0
- package/dist/engines/ferretdb/index.js +1517 -0
- package/dist/engines/ferretdb/index.js.map +1 -0
- package/dist/engines/ferretdb/restore.js +310 -0
- package/dist/engines/ferretdb/restore.js.map +1 -0
- package/{engines/ferretdb/version-maps.ts → dist/engines/ferretdb/version-maps.js} +62 -79
- package/dist/engines/ferretdb/version-maps.js.map +1 -0
- package/dist/engines/file-based-utils.js +184 -0
- package/dist/engines/file-based-utils.js.map +1 -0
- package/dist/engines/index.js +124 -0
- package/dist/engines/index.js.map +1 -0
- package/dist/engines/influxdb/api-client.js +54 -0
- package/dist/engines/influxdb/api-client.js.map +1 -0
- package/dist/engines/influxdb/backup.js +119 -0
- package/dist/engines/influxdb/backup.js.map +1 -0
- package/dist/engines/influxdb/binary-manager.js +87 -0
- package/dist/engines/influxdb/binary-manager.js.map +1 -0
- package/dist/engines/influxdb/binary-urls.js +56 -0
- package/dist/engines/influxdb/binary-urls.js.map +1 -0
- package/dist/engines/influxdb/hostdb-releases.js +21 -0
- package/dist/engines/influxdb/hostdb-releases.js.map +1 -0
- package/dist/engines/influxdb/index.js +962 -0
- package/dist/engines/influxdb/index.js.map +1 -0
- package/dist/engines/influxdb/restore.js +329 -0
- package/dist/engines/influxdb/restore.js.map +1 -0
- package/dist/engines/influxdb/version-maps.js +64 -0
- package/dist/engines/influxdb/version-maps.js.map +1 -0
- package/dist/engines/influxdb/version-validator.js +109 -0
- package/dist/engines/influxdb/version-validator.js.map +1 -0
- package/dist/engines/mariadb/backup.js +178 -0
- package/dist/engines/mariadb/backup.js.map +1 -0
- package/dist/engines/mariadb/binary-manager.js +33 -0
- package/dist/engines/mariadb/binary-manager.js.map +1 -0
- package/{engines/mariadb/binary-urls.ts → dist/engines/mariadb/binary-urls.js} +38 -55
- package/dist/engines/mariadb/binary-urls.js.map +1 -0
- package/dist/engines/mariadb/hostdb-releases.js +21 -0
- package/dist/engines/mariadb/hostdb-releases.js.map +1 -0
- package/dist/engines/mariadb/index.js +1011 -0
- package/dist/engines/mariadb/index.js.map +1 -0
- package/dist/engines/mariadb/restore.js +322 -0
- package/dist/engines/mariadb/restore.js.map +1 -0
- package/dist/engines/mariadb/version-maps.js +63 -0
- package/dist/engines/mariadb/version-maps.js.map +1 -0
- package/dist/engines/mariadb/version-validator.js +143 -0
- package/dist/engines/mariadb/version-validator.js.map +1 -0
- package/dist/engines/meilisearch/api-client.js +50 -0
- package/dist/engines/meilisearch/api-client.js.map +1 -0
- package/dist/engines/meilisearch/backup.js +167 -0
- package/dist/engines/meilisearch/backup.js.map +1 -0
- package/dist/engines/meilisearch/binary-manager.js +31 -0
- package/dist/engines/meilisearch/binary-manager.js.map +1 -0
- package/dist/engines/meilisearch/binary-urls.js +56 -0
- package/dist/engines/meilisearch/binary-urls.js.map +1 -0
- package/dist/engines/meilisearch/hostdb-releases.js +21 -0
- package/dist/engines/meilisearch/hostdb-releases.js.map +1 -0
- package/dist/engines/meilisearch/index.js +992 -0
- package/dist/engines/meilisearch/index.js.map +1 -0
- package/dist/engines/meilisearch/restore.js +167 -0
- package/dist/engines/meilisearch/restore.js.map +1 -0
- package/dist/engines/meilisearch/version-maps.js +67 -0
- package/dist/engines/meilisearch/version-maps.js.map +1 -0
- package/dist/engines/meilisearch/version-validator.js +109 -0
- package/dist/engines/meilisearch/version-validator.js.map +1 -0
- package/dist/engines/mongodb/backup.js +109 -0
- package/dist/engines/mongodb/backup.js.map +1 -0
- package/dist/engines/mongodb/binary-manager.js +36 -0
- package/dist/engines/mongodb/binary-manager.js.map +1 -0
- package/dist/engines/mongodb/binary-urls.js +46 -0
- package/dist/engines/mongodb/binary-urls.js.map +1 -0
- package/dist/engines/mongodb/cli-utils.js +131 -0
- package/dist/engines/mongodb/cli-utils.js.map +1 -0
- package/dist/engines/mongodb/hostdb-releases.js +77 -0
- package/dist/engines/mongodb/hostdb-releases.js.map +1 -0
- package/dist/engines/mongodb/index.js +873 -0
- package/dist/engines/mongodb/index.js.map +1 -0
- package/dist/engines/mongodb/restore.js +276 -0
- package/dist/engines/mongodb/restore.js.map +1 -0
- package/dist/engines/mongodb/version-maps.js +79 -0
- package/dist/engines/mongodb/version-maps.js.map +1 -0
- package/dist/engines/mongodb/version-validator.js +133 -0
- package/dist/engines/mongodb/version-validator.js.map +1 -0
- package/dist/engines/mysql/backup.js +210 -0
- package/dist/engines/mysql/backup.js.map +1 -0
- package/dist/engines/mysql/binary-detection.js +325 -0
- package/dist/engines/mysql/binary-detection.js.map +1 -0
- package/dist/engines/mysql/binary-manager.js +30 -0
- package/dist/engines/mysql/binary-manager.js.map +1 -0
- package/dist/engines/mysql/binary-urls.js +87 -0
- package/dist/engines/mysql/binary-urls.js.map +1 -0
- package/{engines/mysql/hostdb-releases.ts → dist/engines/mysql/hostdb-releases.js} +20 -23
- package/dist/engines/mysql/hostdb-releases.js.map +1 -0
- package/dist/engines/mysql/index.js +1066 -0
- package/dist/engines/mysql/index.js.map +1 -0
- package/dist/engines/mysql/restore.js +361 -0
- package/dist/engines/mysql/restore.js.map +1 -0
- package/dist/engines/mysql/version-maps.js +79 -0
- package/dist/engines/mysql/version-maps.js.map +1 -0
- package/dist/engines/mysql/version-validator.js +266 -0
- package/dist/engines/mysql/version-validator.js.map +1 -0
- package/dist/engines/postgresql/backup.js +118 -0
- package/dist/engines/postgresql/backup.js.map +1 -0
- package/dist/engines/postgresql/binary-manager.js +85 -0
- package/dist/engines/postgresql/binary-manager.js.map +1 -0
- package/dist/engines/postgresql/binary-urls.js +80 -0
- package/dist/engines/postgresql/binary-urls.js.map +1 -0
- package/dist/engines/postgresql/hostdb-releases.js +21 -0
- package/dist/engines/postgresql/hostdb-releases.js.map +1 -0
- package/dist/engines/postgresql/index.js +852 -0
- package/dist/engines/postgresql/index.js.map +1 -0
- package/dist/engines/postgresql/remote-version.js +109 -0
- package/dist/engines/postgresql/remote-version.js.map +1 -0
- package/dist/engines/postgresql/restore.js +254 -0
- package/dist/engines/postgresql/restore.js.map +1 -0
- package/dist/engines/postgresql/version-maps.js +73 -0
- package/dist/engines/postgresql/version-maps.js.map +1 -0
- package/dist/engines/postgresql/version-validator.js +286 -0
- package/dist/engines/postgresql/version-validator.js.map +1 -0
- package/dist/engines/qdrant/api-client.js +50 -0
- package/dist/engines/qdrant/api-client.js.map +1 -0
- package/dist/engines/qdrant/backup.js +115 -0
- package/dist/engines/qdrant/backup.js.map +1 -0
- package/dist/engines/qdrant/binary-manager.js +31 -0
- package/dist/engines/qdrant/binary-manager.js.map +1 -0
- package/dist/engines/qdrant/binary-urls.js +92 -0
- package/dist/engines/qdrant/binary-urls.js.map +1 -0
- package/dist/engines/qdrant/cli-utils.js +39 -0
- package/dist/engines/qdrant/cli-utils.js.map +1 -0
- package/dist/engines/qdrant/hostdb-releases.js +21 -0
- package/dist/engines/qdrant/hostdb-releases.js.map +1 -0
- package/dist/engines/qdrant/index.js +1002 -0
- package/dist/engines/qdrant/index.js.map +1 -0
- package/dist/engines/qdrant/restore.js +154 -0
- package/dist/engines/qdrant/restore.js.map +1 -0
- package/dist/engines/qdrant/version-maps.js +67 -0
- package/dist/engines/qdrant/version-maps.js.map +1 -0
- package/dist/engines/qdrant/version-validator.js +109 -0
- package/dist/engines/qdrant/version-validator.js.map +1 -0
- package/dist/engines/questdb/backup.js +191 -0
- package/dist/engines/questdb/backup.js.map +1 -0
- package/dist/engines/questdb/binary-manager.js +247 -0
- package/dist/engines/questdb/binary-manager.js.map +1 -0
- package/dist/engines/questdb/binary-urls.js +27 -0
- package/dist/engines/questdb/binary-urls.js.map +1 -0
- package/dist/engines/questdb/hostdb-releases.js +21 -0
- package/dist/engines/questdb/hostdb-releases.js.map +1 -0
- package/dist/engines/questdb/index.js +814 -0
- package/dist/engines/questdb/index.js.map +1 -0
- package/dist/engines/questdb/restore.js +202 -0
- package/dist/engines/questdb/restore.js.map +1 -0
- package/dist/engines/questdb/version-maps.js +33 -0
- package/dist/engines/questdb/version-maps.js.map +1 -0
- package/dist/engines/questdb/version-validator.js +99 -0
- package/dist/engines/questdb/version-validator.js.map +1 -0
- package/dist/engines/redis/backup.js +292 -0
- package/dist/engines/redis/backup.js.map +1 -0
- package/dist/engines/redis/binary-manager.js +32 -0
- package/dist/engines/redis/binary-manager.js.map +1 -0
- package/dist/engines/redis/binary-urls.js +96 -0
- package/dist/engines/redis/binary-urls.js.map +1 -0
- package/dist/engines/redis/cli-utils.js +38 -0
- package/dist/engines/redis/cli-utils.js.map +1 -0
- package/dist/engines/redis/hostdb-releases.js +21 -0
- package/dist/engines/redis/hostdb-releases.js.map +1 -0
- package/dist/engines/redis/index.js +1263 -0
- package/dist/engines/redis/index.js.map +1 -0
- package/dist/engines/redis/restore.js +338 -0
- package/dist/engines/redis/restore.js.map +1 -0
- package/dist/engines/redis/version-maps.js +70 -0
- package/dist/engines/redis/version-maps.js.map +1 -0
- package/dist/engines/redis/version-validator.js +109 -0
- package/dist/engines/redis/version-validator.js.map +1 -0
- package/dist/engines/sqlite/binary-manager.js +39 -0
- package/dist/engines/sqlite/binary-manager.js.map +1 -0
- package/{engines/sqlite/binary-urls.ts → dist/engines/sqlite/binary-urls.js} +11 -16
- package/dist/engines/sqlite/binary-urls.js.map +1 -0
- package/dist/engines/sqlite/hostdb-releases.js +21 -0
- package/dist/engines/sqlite/hostdb-releases.js.map +1 -0
- package/dist/engines/sqlite/index.js +493 -0
- package/dist/engines/sqlite/index.js.map +1 -0
- package/dist/engines/sqlite/registry.js +163 -0
- package/dist/engines/sqlite/registry.js.map +1 -0
- package/dist/engines/sqlite/scanner.js +12 -0
- package/dist/engines/sqlite/scanner.js.map +1 -0
- package/dist/engines/sqlite/version-maps.js +57 -0
- package/dist/engines/sqlite/version-maps.js.map +1 -0
- package/dist/engines/surrealdb/backup.js +97 -0
- package/dist/engines/surrealdb/backup.js.map +1 -0
- package/dist/engines/surrealdb/binary-manager.js +33 -0
- package/dist/engines/surrealdb/binary-manager.js.map +1 -0
- package/dist/engines/surrealdb/binary-urls.js +33 -0
- package/dist/engines/surrealdb/binary-urls.js.map +1 -0
- package/dist/engines/surrealdb/cli-utils.js +147 -0
- package/dist/engines/surrealdb/cli-utils.js.map +1 -0
- package/dist/engines/surrealdb/hostdb-releases.js +21 -0
- package/dist/engines/surrealdb/hostdb-releases.js.map +1 -0
- package/dist/engines/surrealdb/index.js +1022 -0
- package/dist/engines/surrealdb/index.js.map +1 -0
- package/dist/engines/surrealdb/restore.js +224 -0
- package/dist/engines/surrealdb/restore.js.map +1 -0
- package/dist/engines/surrealdb/version-maps.js +36 -0
- package/dist/engines/surrealdb/version-maps.js.map +1 -0
- package/dist/engines/tigerbeetle/backup.js +36 -0
- package/dist/engines/tigerbeetle/backup.js.map +1 -0
- package/dist/engines/tigerbeetle/binary-manager.js +72 -0
- package/dist/engines/tigerbeetle/binary-manager.js.map +1 -0
- package/dist/engines/tigerbeetle/binary-urls.js +49 -0
- package/dist/engines/tigerbeetle/binary-urls.js.map +1 -0
- package/dist/engines/tigerbeetle/hostdb-releases.js +21 -0
- package/dist/engines/tigerbeetle/hostdb-releases.js.map +1 -0
- package/dist/engines/tigerbeetle/index.js +559 -0
- package/dist/engines/tigerbeetle/index.js.map +1 -0
- package/dist/engines/tigerbeetle/restore.js +91 -0
- package/dist/engines/tigerbeetle/restore.js.map +1 -0
- package/{engines/tigerbeetle/version-maps.ts → dist/engines/tigerbeetle/version-maps.js} +22 -31
- package/dist/engines/tigerbeetle/version-maps.js.map +1 -0
- package/dist/engines/tigerbeetle/version-validator.js +108 -0
- package/dist/engines/tigerbeetle/version-validator.js.map +1 -0
- package/dist/engines/typedb/backup.js +129 -0
- package/dist/engines/typedb/backup.js.map +1 -0
- package/dist/engines/typedb/binary-manager.js +151 -0
- package/dist/engines/typedb/binary-manager.js.map +1 -0
- package/dist/engines/typedb/binary-urls.js +33 -0
- package/dist/engines/typedb/binary-urls.js.map +1 -0
- package/dist/engines/typedb/cli-utils.js +163 -0
- package/dist/engines/typedb/cli-utils.js.map +1 -0
- package/dist/engines/typedb/hostdb-releases.js +21 -0
- package/dist/engines/typedb/hostdb-releases.js.map +1 -0
- package/dist/engines/typedb/index.js +1003 -0
- package/dist/engines/typedb/index.js.map +1 -0
- package/dist/engines/typedb/restore.js +279 -0
- package/dist/engines/typedb/restore.js.map +1 -0
- package/dist/engines/typedb/version-maps.js +40 -0
- package/dist/engines/typedb/version-maps.js.map +1 -0
- package/dist/engines/typedb/version-validator.js +103 -0
- package/dist/engines/typedb/version-validator.js.map +1 -0
- package/dist/engines/valkey/backup.js +292 -0
- package/dist/engines/valkey/backup.js.map +1 -0
- package/dist/engines/valkey/binary-manager.js +33 -0
- package/dist/engines/valkey/binary-manager.js.map +1 -0
- package/dist/engines/valkey/binary-urls.js +98 -0
- package/dist/engines/valkey/binary-urls.js.map +1 -0
- package/dist/engines/valkey/cli-utils.js +38 -0
- package/dist/engines/valkey/cli-utils.js.map +1 -0
- package/dist/engines/valkey/hostdb-releases.js +21 -0
- package/dist/engines/valkey/hostdb-releases.js.map +1 -0
- package/dist/engines/valkey/index.js +1257 -0
- package/dist/engines/valkey/index.js.map +1 -0
- package/dist/engines/valkey/restore.js +340 -0
- package/dist/engines/valkey/restore.js.map +1 -0
- package/dist/engines/valkey/version-maps.js +70 -0
- package/dist/engines/valkey/version-maps.js.map +1 -0
- package/dist/engines/valkey/version-validator.js +112 -0
- package/dist/engines/valkey/version-validator.js.map +1 -0
- package/dist/engines/weaviate/api-client.js +50 -0
- package/dist/engines/weaviate/api-client.js.map +1 -0
- package/dist/engines/weaviate/backup.js +95 -0
- package/dist/engines/weaviate/backup.js.map +1 -0
- package/dist/engines/weaviate/binary-manager.js +58 -0
- package/dist/engines/weaviate/binary-manager.js.map +1 -0
- package/dist/engines/weaviate/binary-urls.js +92 -0
- package/dist/engines/weaviate/binary-urls.js.map +1 -0
- package/dist/engines/weaviate/cli-utils.js +39 -0
- package/dist/engines/weaviate/cli-utils.js.map +1 -0
- package/dist/engines/weaviate/hostdb-releases.js +21 -0
- package/dist/engines/weaviate/hostdb-releases.js.map +1 -0
- package/dist/engines/weaviate/index.js +871 -0
- package/dist/engines/weaviate/index.js.map +1 -0
- package/dist/engines/weaviate/restore.js +185 -0
- package/dist/engines/weaviate/restore.js.map +1 -0
- package/dist/engines/weaviate/version-maps.js +67 -0
- package/dist/engines/weaviate/version-maps.js.map +1 -0
- package/dist/engines/weaviate/version-validator.js +109 -0
- package/dist/engines/weaviate/version-validator.js.map +1 -0
- package/dist/types/index.js +102 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +12 -9
- package/bin/cli.js +0 -68
- package/cli/bin.ts +0 -10
- package/cli/commands/attach.ts +0 -139
- package/cli/commands/backup.ts +0 -290
- package/cli/commands/backups.ts +0 -247
- package/cli/commands/clone.ts +0 -159
- package/cli/commands/config.ts +0 -367
- package/cli/commands/connect.ts +0 -684
- package/cli/commands/create.ts +0 -1201
- package/cli/commands/databases.ts +0 -630
- package/cli/commands/delete.ts +0 -133
- package/cli/commands/deps.ts +0 -342
- package/cli/commands/detach.ts +0 -107
- package/cli/commands/doctor.ts +0 -689
- package/cli/commands/duckdb.ts +0 -273
- package/cli/commands/edit.ts +0 -683
- package/cli/commands/engines.ts +0 -1914
- package/cli/commands/export.ts +0 -544
- package/cli/commands/info.ts +0 -340
- package/cli/commands/list.ts +0 -284
- package/cli/commands/logs.ts +0 -102
- package/cli/commands/menu/backup-handlers.ts +0 -1571
- package/cli/commands/menu/container-handlers.ts +0 -2288
- package/cli/commands/menu/engine-handlers.ts +0 -355
- package/cli/commands/menu/index.ts +0 -342
- package/cli/commands/menu/settings-handlers.ts +0 -365
- package/cli/commands/menu/shared.ts +0 -23
- package/cli/commands/menu/shell-handlers.ts +0 -1811
- package/cli/commands/menu/sql-handlers.ts +0 -231
- package/cli/commands/menu/update-handlers.ts +0 -378
- package/cli/commands/menu/validators.ts +0 -8
- package/cli/commands/ports.ts +0 -211
- package/cli/commands/pull.ts +0 -223
- package/cli/commands/query.ts +0 -241
- package/cli/commands/restore.ts +0 -587
- package/cli/commands/run.ts +0 -178
- package/cli/commands/self-update.ts +0 -121
- package/cli/commands/sqlite.ts +0 -273
- package/cli/commands/start.ts +0 -218
- package/cli/commands/stop.ts +0 -241
- package/cli/commands/url.ts +0 -104
- package/cli/commands/users.ts +0 -264
- package/cli/commands/version.ts +0 -55
- package/cli/commands/which.ts +0 -290
- package/cli/constants.ts +0 -233
- package/cli/helpers.ts +0 -1593
- package/cli/index.ts +0 -162
- package/cli/ui/prompts.ts +0 -1525
- package/cli/ui/spinner.ts +0 -88
- package/cli/ui/theme.ts +0 -128
- package/cli/utils/file-follower.ts +0 -93
- package/config/backup-formats.ts +0 -446
- package/config/defaults.ts +0 -56
- package/config/engine-defaults.ts +0 -336
- package/config/engines-registry.ts +0 -150
- package/config/engines.schema.json +0 -135
- package/config/os-dependencies.ts +0 -888
- package/config/paths.ts +0 -200
- package/core/backup-restore.ts +0 -330
- package/core/base-binary-manager.ts +0 -562
- package/core/base-document-binary-manager.ts +0 -523
- package/core/base-embedded-binary-manager.ts +0 -547
- package/core/base-server-binary-manager.ts +0 -523
- package/core/config-manager.ts +0 -652
- package/core/container-manager.ts +0 -787
- package/core/credential-generator.ts +0 -93
- package/core/credential-manager.ts +0 -259
- package/core/dblab-utils.ts +0 -113
- package/core/dependency-manager.ts +0 -512
- package/core/docker-exporter.ts +0 -1345
- package/core/error-handler.ts +0 -419
- package/core/fs-error-utils.ts +0 -82
- package/core/homebrew-version-manager.ts +0 -352
- package/core/hostdb-client.ts +0 -344
- package/core/hostdb-metadata.ts +0 -350
- package/core/hostdb-releases-factory.ts +0 -237
- package/core/library-env.ts +0 -118
- package/core/pgweb-utils.ts +0 -62
- package/core/platform-service.ts +0 -829
- package/core/port-manager.ts +0 -165
- package/core/process-manager.ts +0 -576
- package/core/pull-manager.ts +0 -511
- package/core/query-parser.ts +0 -514
- package/core/spawn-utils.ts +0 -122
- package/core/start-with-retry.ts +0 -130
- package/core/test-cleanup.ts +0 -108
- package/core/tls-generator.ts +0 -116
- package/core/transaction-manager.ts +0 -158
- package/core/update-manager.ts +0 -308
- package/core/version-migration.ts +0 -346
- package/core/version-utils.ts +0 -104
- package/engines/base-engine.ts +0 -340
- package/engines/clickhouse/README.md +0 -231
- package/engines/clickhouse/backup.ts +0 -398
- package/engines/clickhouse/binary-manager.ts +0 -201
- package/engines/clickhouse/binary-urls.ts +0 -125
- package/engines/clickhouse/cli-utils.ts +0 -176
- package/engines/clickhouse/hostdb-releases.ts +0 -30
- package/engines/clickhouse/index.ts +0 -1345
- package/engines/clickhouse/restore.ts +0 -466
- package/engines/clickhouse/version-maps.ts +0 -95
- package/engines/clickhouse/version-validator.ts +0 -154
- package/engines/cockroachdb/README.md +0 -170
- package/engines/cockroachdb/backup.ts +0 -376
- package/engines/cockroachdb/binary-manager.ts +0 -45
- package/engines/cockroachdb/binary-urls.ts +0 -40
- package/engines/cockroachdb/cli-utils.ts +0 -384
- package/engines/cockroachdb/hostdb-releases.ts +0 -26
- package/engines/cockroachdb/index.ts +0 -1276
- package/engines/cockroachdb/restore.ts +0 -455
- package/engines/cockroachdb/version-maps.ts +0 -42
- package/engines/couchdb/README.md +0 -257
- package/engines/couchdb/api-client.ts +0 -81
- package/engines/couchdb/backup.ts +0 -137
- package/engines/couchdb/binary-manager.ts +0 -86
- package/engines/couchdb/binary-urls.ts +0 -115
- package/engines/couchdb/hostdb-releases.ts +0 -23
- package/engines/couchdb/index.ts +0 -1429
- package/engines/couchdb/restore.ts +0 -290
- package/engines/couchdb/version-maps.ts +0 -78
- package/engines/couchdb/version-validator.ts +0 -111
- package/engines/duckdb/README.md +0 -154
- package/engines/duckdb/binary-manager.ts +0 -45
- package/engines/duckdb/hostdb-releases.ts +0 -23
- package/engines/duckdb/index.ts +0 -749
- package/engines/duckdb/registry.ts +0 -303
- package/engines/duckdb/scanner.ts +0 -22
- package/engines/duckdb/version-maps.ts +0 -78
- package/engines/duckdb/version-validator.ts +0 -78
- package/engines/ferretdb/README.md +0 -262
- package/engines/ferretdb/backup.ts +0 -173
- package/engines/ferretdb/binary-manager.ts +0 -1095
- package/engines/ferretdb/binary-urls.ts +0 -183
- package/engines/ferretdb/index.ts +0 -1907
- package/engines/ferretdb/restore.ts +0 -357
- package/engines/file-based-utils.ts +0 -262
- package/engines/index.ts +0 -131
- package/engines/influxdb/README.md +0 -180
- package/engines/influxdb/api-client.ts +0 -64
- package/engines/influxdb/backup.ts +0 -160
- package/engines/influxdb/binary-manager.ts +0 -110
- package/engines/influxdb/binary-urls.ts +0 -69
- package/engines/influxdb/hostdb-releases.ts +0 -23
- package/engines/influxdb/index.ts +0 -1272
- package/engines/influxdb/restore.ts +0 -417
- package/engines/influxdb/version-maps.ts +0 -75
- package/engines/influxdb/version-validator.ts +0 -128
- package/engines/mariadb/README.md +0 -141
- package/engines/mariadb/backup.ts +0 -233
- package/engines/mariadb/binary-manager.ts +0 -45
- package/engines/mariadb/hostdb-releases.ts +0 -23
- package/engines/mariadb/index.ts +0 -1300
- package/engines/mariadb/restore.ts +0 -447
- package/engines/mariadb/version-maps.ts +0 -72
- package/engines/mariadb/version-validator.ts +0 -181
- package/engines/meilisearch/README.md +0 -255
- package/engines/meilisearch/api-client.ts +0 -61
- package/engines/meilisearch/backup.ts +0 -233
- package/engines/meilisearch/binary-manager.ts +0 -43
- package/engines/meilisearch/binary-urls.ts +0 -69
- package/engines/meilisearch/hostdb-releases.ts +0 -26
- package/engines/meilisearch/index.ts +0 -1292
- package/engines/meilisearch/restore.ts +0 -219
- package/engines/meilisearch/version-maps.ts +0 -78
- package/engines/meilisearch/version-validator.ts +0 -128
- package/engines/mongodb/README.md +0 -162
- package/engines/mongodb/backup.ts +0 -127
- package/engines/mongodb/binary-manager.ts +0 -48
- package/engines/mongodb/binary-urls.ts +0 -63
- package/engines/mongodb/cli-utils.ts +0 -171
- package/engines/mongodb/hostdb-releases.ts +0 -91
- package/engines/mongodb/index.ts +0 -1118
- package/engines/mongodb/restore.ts +0 -361
- package/engines/mongodb/version-maps.ts +0 -91
- package/engines/mongodb/version-validator.ts +0 -160
- package/engines/mysql/README.md +0 -142
- package/engines/mysql/backup.ts +0 -270
- package/engines/mysql/binary-detection.ts +0 -408
- package/engines/mysql/binary-manager.ts +0 -42
- package/engines/mysql/binary-urls.ts +0 -104
- package/engines/mysql/index.ts +0 -1361
- package/engines/mysql/restore.ts +0 -500
- package/engines/mysql/version-maps.ts +0 -91
- package/engines/mysql/version-validator.ts +0 -369
- package/engines/postgresql/README.md +0 -158
- package/engines/postgresql/backup.ts +0 -151
- package/engines/postgresql/binary-manager.ts +0 -114
- package/engines/postgresql/binary-urls.ts +0 -99
- package/engines/postgresql/hostdb-releases.ts +0 -26
- package/engines/postgresql/index.ts +0 -1143
- package/engines/postgresql/remote-version.ts +0 -161
- package/engines/postgresql/restore.ts +0 -342
- package/engines/postgresql/version-maps.ts +0 -83
- package/engines/postgresql/version-validator.ts +0 -413
- package/engines/qdrant/README.md +0 -222
- package/engines/qdrant/api-client.ts +0 -61
- package/engines/qdrant/backup.ts +0 -165
- package/engines/qdrant/binary-manager.ts +0 -43
- package/engines/qdrant/binary-urls.ts +0 -115
- package/engines/qdrant/cli-utils.ts +0 -43
- package/engines/qdrant/hostdb-releases.ts +0 -23
- package/engines/qdrant/index.ts +0 -1312
- package/engines/qdrant/restore.ts +0 -203
- package/engines/qdrant/version-maps.ts +0 -78
- package/engines/qdrant/version-validator.ts +0 -128
- package/engines/questdb/README.md +0 -334
- package/engines/questdb/backup.ts +0 -220
- package/engines/questdb/binary-manager.ts +0 -310
- package/engines/questdb/binary-urls.ts +0 -34
- package/engines/questdb/hostdb-releases.ts +0 -23
- package/engines/questdb/index.ts +0 -1023
- package/engines/questdb/restore.ts +0 -260
- package/engines/questdb/version-maps.ts +0 -37
- package/engines/questdb/version-validator.ts +0 -121
- package/engines/redis/README.md +0 -173
- package/engines/redis/backup.ts +0 -389
- package/engines/redis/binary-manager.ts +0 -44
- package/engines/redis/binary-urls.ts +0 -117
- package/engines/redis/cli-utils.ts +0 -42
- package/engines/redis/hostdb-releases.ts +0 -23
- package/engines/redis/index.ts +0 -1583
- package/engines/redis/restore.ts +0 -443
- package/engines/redis/version-maps.ts +0 -81
- package/engines/redis/version-validator.ts +0 -131
- package/engines/sqlite/README.md +0 -162
- package/engines/sqlite/binary-manager.ts +0 -52
- package/engines/sqlite/hostdb-releases.ts +0 -23
- package/engines/sqlite/index.ts +0 -641
- package/engines/sqlite/registry.ts +0 -198
- package/engines/sqlite/scanner.ts +0 -22
- package/engines/sqlite/version-maps.ts +0 -64
- package/engines/surrealdb/README.md +0 -218
- package/engines/surrealdb/backup.ts +0 -131
- package/engines/surrealdb/binary-manager.ts +0 -45
- package/engines/surrealdb/binary-urls.ts +0 -40
- package/engines/surrealdb/cli-utils.ts +0 -173
- package/engines/surrealdb/hostdb-releases.ts +0 -23
- package/engines/surrealdb/index.ts +0 -1246
- package/engines/surrealdb/restore.ts +0 -302
- package/engines/surrealdb/version-maps.ts +0 -41
- package/engines/tigerbeetle/README.md +0 -61
- package/engines/tigerbeetle/backup.ts +0 -49
- package/engines/tigerbeetle/binary-manager.ts +0 -95
- package/engines/tigerbeetle/binary-urls.ts +0 -62
- package/engines/tigerbeetle/hostdb-releases.ts +0 -26
- package/engines/tigerbeetle/index.ts +0 -746
- package/engines/tigerbeetle/restore.ts +0 -130
- package/engines/tigerbeetle/version-validator.ts +0 -126
- package/engines/typedb/backup.ts +0 -167
- package/engines/typedb/binary-manager.ts +0 -200
- package/engines/typedb/binary-urls.ts +0 -40
- package/engines/typedb/cli-utils.ts +0 -210
- package/engines/typedb/hostdb-releases.ts +0 -23
- package/engines/typedb/index.ts +0 -1275
- package/engines/typedb/restore.ts +0 -377
- package/engines/typedb/version-maps.ts +0 -48
- package/engines/typedb/version-validator.ts +0 -127
- package/engines/valkey/README.md +0 -219
- package/engines/valkey/backup.ts +0 -389
- package/engines/valkey/binary-manager.ts +0 -45
- package/engines/valkey/binary-urls.ts +0 -122
- package/engines/valkey/cli-utils.ts +0 -42
- package/engines/valkey/hostdb-releases.ts +0 -23
- package/engines/valkey/index.ts +0 -1585
- package/engines/valkey/restore.ts +0 -446
- package/engines/valkey/version-maps.ts +0 -81
- package/engines/valkey/version-validator.ts +0 -131
- package/engines/weaviate/README.md +0 -302
- package/engines/weaviate/api-client.ts +0 -61
- package/engines/weaviate/backup.ts +0 -145
- package/engines/weaviate/binary-manager.ts +0 -80
- package/engines/weaviate/binary-urls.ts +0 -115
- package/engines/weaviate/cli-utils.ts +0 -43
- package/engines/weaviate/hostdb-releases.ts +0 -23
- package/engines/weaviate/index.ts +0 -1139
- package/engines/weaviate/restore.ts +0 -235
- package/engines/weaviate/version-maps.ts +0 -78
- package/engines/weaviate/version-validator.ts +0 -128
- package/types/index.ts +0 -624
- /package/{config → dist/config}/engines.json +0 -0
package/engines/qdrant/index.ts
DELETED
|
@@ -1,1312 +0,0 @@
|
|
|
1
|
-
import { spawn, type SpawnOptions } from 'child_process'
|
|
2
|
-
import { createWriteStream, existsSync } from 'fs'
|
|
3
|
-
import { chmod, mkdir, writeFile, readFile, unlink } from 'fs/promises'
|
|
4
|
-
import { join } from 'path'
|
|
5
|
-
import { Readable } from 'stream'
|
|
6
|
-
import { pipeline } from 'stream/promises'
|
|
7
|
-
import { BaseEngine } from '../base-engine'
|
|
8
|
-
import { paths } from '../../config/paths'
|
|
9
|
-
import { getEngineDefaults } from '../../config/defaults'
|
|
10
|
-
import { platformService, isWindows } from '../../core/platform-service'
|
|
11
|
-
import { configManager } from '../../core/config-manager'
|
|
12
|
-
import {
|
|
13
|
-
logDebug,
|
|
14
|
-
logWarning,
|
|
15
|
-
assertValidUsername,
|
|
16
|
-
} from '../../core/error-handler'
|
|
17
|
-
import { processManager } from '../../core/process-manager'
|
|
18
|
-
import { portManager } from '../../core/port-manager'
|
|
19
|
-
import { qdrantBinaryManager } from './binary-manager'
|
|
20
|
-
import { getBinaryUrl } from './binary-urls'
|
|
21
|
-
import {
|
|
22
|
-
normalizeVersion,
|
|
23
|
-
SUPPORTED_MAJOR_VERSIONS,
|
|
24
|
-
QDRANT_VERSION_MAP,
|
|
25
|
-
} from './version-maps'
|
|
26
|
-
import { fetchAvailableVersions as fetchHostdbVersions } from './hostdb-releases'
|
|
27
|
-
import {
|
|
28
|
-
detectBackupFormat as detectBackupFormatImpl,
|
|
29
|
-
restoreBackup,
|
|
30
|
-
} from './restore'
|
|
31
|
-
import { createBackup } from './backup'
|
|
32
|
-
import { qdrantApiRequest } from './api-client'
|
|
33
|
-
import {
|
|
34
|
-
type Platform,
|
|
35
|
-
type Arch,
|
|
36
|
-
type ContainerConfig,
|
|
37
|
-
type ProgressCallback,
|
|
38
|
-
type BackupFormat,
|
|
39
|
-
type BackupOptions,
|
|
40
|
-
type BackupResult,
|
|
41
|
-
type RestoreResult,
|
|
42
|
-
type DumpResult,
|
|
43
|
-
type StatusResult,
|
|
44
|
-
type QueryResult,
|
|
45
|
-
type QueryOptions,
|
|
46
|
-
type CreateUserOptions,
|
|
47
|
-
type UserCredentials,
|
|
48
|
-
} from '../../types'
|
|
49
|
-
import { parseRESTAPIResult } from '../../core/query-parser'
|
|
50
|
-
|
|
51
|
-
const ENGINE = 'qdrant'
|
|
52
|
-
const engineDef = getEngineDefaults(ENGINE)
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Generate Qdrant configuration YAML content
|
|
56
|
-
*/
|
|
57
|
-
function generateQdrantConfig(options: {
|
|
58
|
-
port: number
|
|
59
|
-
grpcPort: number
|
|
60
|
-
dataDir: string
|
|
61
|
-
snapshotsDir: string
|
|
62
|
-
}): string {
|
|
63
|
-
// Qdrant config uses forward slashes even on Windows
|
|
64
|
-
const normalizePathForQdrant = (p: string) => p.replace(/\\/g, '/')
|
|
65
|
-
|
|
66
|
-
return `# SpinDB generated Qdrant configuration
|
|
67
|
-
service:
|
|
68
|
-
host: 127.0.0.1
|
|
69
|
-
http_port: ${options.port}
|
|
70
|
-
grpc_port: ${options.grpcPort}
|
|
71
|
-
|
|
72
|
-
storage:
|
|
73
|
-
storage_path: ${normalizePathForQdrant(options.dataDir)}
|
|
74
|
-
snapshots_path: ${normalizePathForQdrant(options.snapshotsDir)}
|
|
75
|
-
|
|
76
|
-
log_level: INFO
|
|
77
|
-
`
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Parse a Qdrant connection string
|
|
82
|
-
* Supported formats:
|
|
83
|
-
* - http://host:port
|
|
84
|
-
* - https://host:port
|
|
85
|
-
* - qdrant://host:port (converted to http)
|
|
86
|
-
* - http://host:port?api_key=KEY (for API key auth)
|
|
87
|
-
*/
|
|
88
|
-
function parseQdrantConnectionString(connectionString: string): {
|
|
89
|
-
baseUrl: string
|
|
90
|
-
headers: Record<string, string>
|
|
91
|
-
} {
|
|
92
|
-
let url: URL
|
|
93
|
-
let scheme = 'http'
|
|
94
|
-
|
|
95
|
-
// Handle qdrant:// scheme by converting to http://
|
|
96
|
-
let normalized = connectionString.trim()
|
|
97
|
-
if (normalized.startsWith('qdrant://')) {
|
|
98
|
-
normalized = normalized.replace('qdrant://', 'http://')
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Ensure scheme is present
|
|
102
|
-
if (!normalized.startsWith('http://') && !normalized.startsWith('https://')) {
|
|
103
|
-
normalized = `http://${normalized}`
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
url = new URL(normalized)
|
|
108
|
-
scheme = url.protocol.replace(':', '')
|
|
109
|
-
} catch {
|
|
110
|
-
throw new Error(
|
|
111
|
-
`Invalid Qdrant connection string: ${connectionString}\n` +
|
|
112
|
-
'Expected format: http://host:port or qdrant://host:port',
|
|
113
|
-
)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Extract API key if provided
|
|
117
|
-
const apiKey = url.searchParams.get('api_key')
|
|
118
|
-
|
|
119
|
-
const headers: Record<string, string> = {
|
|
120
|
-
'Content-Type': 'application/json',
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (apiKey) {
|
|
124
|
-
headers['api-key'] = apiKey
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Construct base URL without query params
|
|
128
|
-
// Qdrant REST API uses port 6333 regardless of http/https
|
|
129
|
-
const port = url.port || '6333'
|
|
130
|
-
const baseUrl = `${scheme}://${url.hostname}:${port}`
|
|
131
|
-
|
|
132
|
-
return { baseUrl, headers }
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Make an HTTP request to a remote Qdrant server
|
|
137
|
-
*/
|
|
138
|
-
async function remoteQdrantRequest(
|
|
139
|
-
baseUrl: string,
|
|
140
|
-
method: string,
|
|
141
|
-
path: string,
|
|
142
|
-
headers: Record<string, string>,
|
|
143
|
-
body?: Record<string, unknown>,
|
|
144
|
-
timeoutMs = 30000,
|
|
145
|
-
): Promise<{ status: number; data: unknown }> {
|
|
146
|
-
const url = `${baseUrl}${path}`
|
|
147
|
-
|
|
148
|
-
const controller = new AbortController()
|
|
149
|
-
const timeoutId = setTimeout(() => controller.abort(), timeoutMs)
|
|
150
|
-
|
|
151
|
-
const options: RequestInit = {
|
|
152
|
-
method,
|
|
153
|
-
headers,
|
|
154
|
-
signal: controller.signal,
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (body) {
|
|
158
|
-
options.body = JSON.stringify(body)
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
try {
|
|
162
|
-
const response = await fetch(url, options)
|
|
163
|
-
|
|
164
|
-
// Try to parse as JSON, fall back to text
|
|
165
|
-
let data: unknown
|
|
166
|
-
const contentType = response.headers.get('content-type') || ''
|
|
167
|
-
if (contentType.includes('application/json')) {
|
|
168
|
-
data = await response.json()
|
|
169
|
-
} else {
|
|
170
|
-
data = await response.text()
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return { status: response.status, data }
|
|
174
|
-
} catch (error) {
|
|
175
|
-
if (error instanceof Error && error.name === 'AbortError') {
|
|
176
|
-
throw new Error(
|
|
177
|
-
`Remote Qdrant request timed out after ${timeoutMs / 1000}s: ${method} ${path}`,
|
|
178
|
-
)
|
|
179
|
-
}
|
|
180
|
-
throw error
|
|
181
|
-
} finally {
|
|
182
|
-
clearTimeout(timeoutId)
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export class QdrantEngine extends BaseEngine {
|
|
187
|
-
name = ENGINE
|
|
188
|
-
displayName = 'Qdrant'
|
|
189
|
-
defaultPort = engineDef.defaultPort
|
|
190
|
-
supportedVersions = SUPPORTED_MAJOR_VERSIONS
|
|
191
|
-
|
|
192
|
-
// Get platform info for binary operations
|
|
193
|
-
getPlatformInfo(): { platform: Platform; arch: Arch } {
|
|
194
|
-
return platformService.getPlatformInfo()
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Fetch available versions from hostdb (dynamically or from cache/fallback)
|
|
198
|
-
async fetchAvailableVersions(): Promise<Record<string, string[]>> {
|
|
199
|
-
return fetchHostdbVersions()
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Get binary download URL from hostdb
|
|
203
|
-
getBinaryUrl(version: string, platform: Platform, arch: Arch): string {
|
|
204
|
-
return getBinaryUrl(version, platform, arch)
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Resolves version string to full version (e.g., '1' -> '1.16.3')
|
|
208
|
-
resolveFullVersion(version: string): string {
|
|
209
|
-
// Check if already a full version (has at least two dots)
|
|
210
|
-
if (/^\d+\.\d+\.\d+$/.test(version)) {
|
|
211
|
-
return version
|
|
212
|
-
}
|
|
213
|
-
// It's a major version, resolve using version map
|
|
214
|
-
return QDRANT_VERSION_MAP[version] || `${version}.0.0`
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Get the path where binaries for a version would be installed
|
|
218
|
-
getBinaryPath(version: string): string {
|
|
219
|
-
const fullVersion = this.resolveFullVersion(version)
|
|
220
|
-
const { platform: p, arch: a } = this.getPlatformInfo()
|
|
221
|
-
return paths.getBinaryPath({
|
|
222
|
-
engine: 'qdrant',
|
|
223
|
-
version: fullVersion,
|
|
224
|
-
platform: p,
|
|
225
|
-
arch: a,
|
|
226
|
-
})
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Verify that Qdrant binaries are available
|
|
230
|
-
async verifyBinary(binPath: string): Promise<boolean> {
|
|
231
|
-
const ext = platformService.getExecutableExtension()
|
|
232
|
-
const serverPath = join(binPath, 'bin', `qdrant${ext}`)
|
|
233
|
-
return existsSync(serverPath)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Check if a specific Qdrant version is installed (downloaded)
|
|
237
|
-
async isBinaryInstalled(version: string): Promise<boolean> {
|
|
238
|
-
const { platform, arch } = this.getPlatformInfo()
|
|
239
|
-
return qdrantBinaryManager.isInstalled(version, platform, arch)
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Ensure Qdrant binaries are available for a specific version
|
|
244
|
-
* Downloads from hostdb if not already installed
|
|
245
|
-
* Returns the path to the bin directory
|
|
246
|
-
*/
|
|
247
|
-
async ensureBinaries(
|
|
248
|
-
version: string,
|
|
249
|
-
onProgress?: ProgressCallback,
|
|
250
|
-
): Promise<string> {
|
|
251
|
-
const { platform, arch } = this.getPlatformInfo()
|
|
252
|
-
|
|
253
|
-
const binPath = await qdrantBinaryManager.ensureInstalled(
|
|
254
|
-
version,
|
|
255
|
-
platform,
|
|
256
|
-
arch,
|
|
257
|
-
onProgress,
|
|
258
|
-
)
|
|
259
|
-
|
|
260
|
-
// Register binaries in config
|
|
261
|
-
const ext = platformService.getExecutableExtension()
|
|
262
|
-
const tools = ['qdrant'] as const
|
|
263
|
-
|
|
264
|
-
for (const tool of tools) {
|
|
265
|
-
const toolPath = join(binPath, 'bin', `${tool}${ext}`)
|
|
266
|
-
if (existsSync(toolPath)) {
|
|
267
|
-
await configManager.setBinaryPath(tool, toolPath, 'bundled')
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return binPath
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Initialize a new Qdrant data directory
|
|
276
|
-
* Creates the directory and generates config.yaml
|
|
277
|
-
*/
|
|
278
|
-
async initDataDir(
|
|
279
|
-
containerName: string,
|
|
280
|
-
_version: string,
|
|
281
|
-
options: Record<string, unknown> = {},
|
|
282
|
-
): Promise<string> {
|
|
283
|
-
const dataDir = paths.getContainerDataPath(containerName, {
|
|
284
|
-
engine: ENGINE,
|
|
285
|
-
})
|
|
286
|
-
const containerDir = paths.getContainerPath(containerName, {
|
|
287
|
-
engine: ENGINE,
|
|
288
|
-
})
|
|
289
|
-
const port = (options.port as number) || engineDef.defaultPort
|
|
290
|
-
const grpcPort = port + 1 // gRPC port is typically HTTP port + 1
|
|
291
|
-
|
|
292
|
-
// Create data directory if it doesn't exist
|
|
293
|
-
if (!existsSync(dataDir)) {
|
|
294
|
-
await mkdir(dataDir, { recursive: true })
|
|
295
|
-
logDebug(`Created Qdrant data directory: ${dataDir}`)
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Create snapshots directory
|
|
299
|
-
const snapshotsDir = join(dataDir, 'snapshots')
|
|
300
|
-
if (!existsSync(snapshotsDir)) {
|
|
301
|
-
await mkdir(snapshotsDir, { recursive: true })
|
|
302
|
-
logDebug(`Created Qdrant snapshots directory: ${snapshotsDir}`)
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// Generate config.yaml
|
|
306
|
-
const configPath = join(containerDir, 'config.yaml')
|
|
307
|
-
const configContent = generateQdrantConfig({
|
|
308
|
-
port,
|
|
309
|
-
grpcPort,
|
|
310
|
-
dataDir,
|
|
311
|
-
snapshotsDir,
|
|
312
|
-
})
|
|
313
|
-
await writeFile(configPath, configContent)
|
|
314
|
-
logDebug(`Generated Qdrant config: ${configPath}`)
|
|
315
|
-
|
|
316
|
-
return dataDir
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// Get the path to qdrant server for a version
|
|
320
|
-
async getQdrantServerPath(version: string): Promise<string> {
|
|
321
|
-
const { platform, arch } = this.getPlatformInfo()
|
|
322
|
-
const fullVersion = normalizeVersion(version)
|
|
323
|
-
const binPath = paths.getBinaryPath({
|
|
324
|
-
engine: 'qdrant',
|
|
325
|
-
version: fullVersion,
|
|
326
|
-
platform,
|
|
327
|
-
arch,
|
|
328
|
-
})
|
|
329
|
-
const ext = platformService.getExecutableExtension()
|
|
330
|
-
const serverPath = join(binPath, 'bin', `qdrant${ext}`)
|
|
331
|
-
if (existsSync(serverPath)) {
|
|
332
|
-
return serverPath
|
|
333
|
-
}
|
|
334
|
-
throw new Error(
|
|
335
|
-
`Qdrant ${version} is not installed. Run: spindb engines download qdrant ${version}`,
|
|
336
|
-
)
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Get the path to qdrant binary
|
|
340
|
-
async getQdrantPath(version?: string): Promise<string> {
|
|
341
|
-
// Check config cache first
|
|
342
|
-
const cached = await configManager.getBinaryPath('qdrant')
|
|
343
|
-
if (cached && existsSync(cached)) {
|
|
344
|
-
return cached
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// If version provided, use downloaded binary
|
|
348
|
-
if (version) {
|
|
349
|
-
const { platform, arch } = this.getPlatformInfo()
|
|
350
|
-
const fullVersion = normalizeVersion(version)
|
|
351
|
-
const binPath = paths.getBinaryPath({
|
|
352
|
-
engine: 'qdrant',
|
|
353
|
-
version: fullVersion,
|
|
354
|
-
platform,
|
|
355
|
-
arch,
|
|
356
|
-
})
|
|
357
|
-
const ext = platformService.getExecutableExtension()
|
|
358
|
-
const qdrantPath = join(binPath, 'bin', `qdrant${ext}`)
|
|
359
|
-
if (existsSync(qdrantPath)) {
|
|
360
|
-
return qdrantPath
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
throw new Error(
|
|
365
|
-
'qdrant not found. Run: spindb engines download qdrant <version>',
|
|
366
|
-
)
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Start Qdrant server
|
|
371
|
-
* CLI wrapper: qdrant --config-path /path/to/config.yaml
|
|
372
|
-
*/
|
|
373
|
-
async start(
|
|
374
|
-
container: ContainerConfig,
|
|
375
|
-
onProgress?: ProgressCallback,
|
|
376
|
-
): Promise<{ port: number; connectionString: string }> {
|
|
377
|
-
const { name, port, version, binaryPath } = container
|
|
378
|
-
|
|
379
|
-
// Check if already running (idempotent behavior)
|
|
380
|
-
const alreadyRunning = await processManager.isRunning(name, {
|
|
381
|
-
engine: ENGINE,
|
|
382
|
-
})
|
|
383
|
-
if (alreadyRunning) {
|
|
384
|
-
return {
|
|
385
|
-
port,
|
|
386
|
-
connectionString: this.getConnectionString(container),
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// Use stored binary path if available (from container creation)
|
|
391
|
-
let qdrantServer: string | null = null
|
|
392
|
-
|
|
393
|
-
if (binaryPath && existsSync(binaryPath)) {
|
|
394
|
-
const ext = platformService.getExecutableExtension()
|
|
395
|
-
const serverPath = join(binaryPath, 'bin', `qdrant${ext}`)
|
|
396
|
-
if (existsSync(serverPath)) {
|
|
397
|
-
qdrantServer = serverPath
|
|
398
|
-
logDebug(`Using stored binary path: ${qdrantServer}`)
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// If we didn't find the binary above, fall back to normal path
|
|
403
|
-
if (!qdrantServer) {
|
|
404
|
-
try {
|
|
405
|
-
qdrantServer = await this.getQdrantServerPath(version)
|
|
406
|
-
} catch (error) {
|
|
407
|
-
const originalMessage =
|
|
408
|
-
error instanceof Error ? error.message : String(error)
|
|
409
|
-
throw new Error(
|
|
410
|
-
`Qdrant ${version} is not installed. Run: spindb engines download qdrant ${version}\n` +
|
|
411
|
-
` Original error: ${originalMessage}`,
|
|
412
|
-
)
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
logDebug(`Using qdrant for version ${version}: ${qdrantServer}`)
|
|
417
|
-
|
|
418
|
-
const containerDir = paths.getContainerPath(name, { engine: ENGINE })
|
|
419
|
-
const configPath = join(containerDir, 'config.yaml')
|
|
420
|
-
const dataDir = paths.getContainerDataPath(name, { engine: ENGINE })
|
|
421
|
-
const snapshotsDir = join(dataDir, 'snapshots')
|
|
422
|
-
const logFile = paths.getContainerLogPath(name, { engine: ENGINE })
|
|
423
|
-
const pidFile = join(containerDir, 'qdrant.pid')
|
|
424
|
-
const grpcPort = port + 1
|
|
425
|
-
|
|
426
|
-
// Check if gRPC port is available (Qdrant uses HTTP port + 1 for gRPC)
|
|
427
|
-
// On Windows, wait longer for ports to be released (TIME_WAIT state can persist)
|
|
428
|
-
// Windows can hold ports for 30+ seconds after process termination
|
|
429
|
-
const portWaitTimeout = isWindows() ? 60000 : 0
|
|
430
|
-
const portCheckStart = Date.now()
|
|
431
|
-
const portCheckInterval = 1000
|
|
432
|
-
|
|
433
|
-
while (!(await portManager.isPortAvailable(grpcPort))) {
|
|
434
|
-
if (Date.now() - portCheckStart >= portWaitTimeout) {
|
|
435
|
-
throw new Error(
|
|
436
|
-
`gRPC port ${grpcPort} is already in use. ` +
|
|
437
|
-
`Qdrant requires both HTTP port ${port} and gRPC port ${grpcPort} to be available.`,
|
|
438
|
-
)
|
|
439
|
-
}
|
|
440
|
-
logDebug(`Waiting for gRPC port ${grpcPort} to become available...`)
|
|
441
|
-
await new Promise((resolve) => setTimeout(resolve, portCheckInterval))
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// Also check HTTP port on Windows
|
|
445
|
-
if (isWindows()) {
|
|
446
|
-
while (!(await portManager.isPortAvailable(port))) {
|
|
447
|
-
if (Date.now() - portCheckStart >= portWaitTimeout) {
|
|
448
|
-
throw new Error(
|
|
449
|
-
`HTTP port ${port} is already in use. ` +
|
|
450
|
-
`Qdrant requires both HTTP port ${port} and gRPC port ${grpcPort} to be available.`,
|
|
451
|
-
)
|
|
452
|
-
}
|
|
453
|
-
logDebug(`Waiting for HTTP port ${port} to become available...`)
|
|
454
|
-
await new Promise((resolve) => setTimeout(resolve, portCheckInterval))
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// Ensure snapshots directory exists
|
|
459
|
-
if (!existsSync(snapshotsDir)) {
|
|
460
|
-
await mkdir(snapshotsDir, { recursive: true })
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
// Regenerate config with current port (in case it changed)
|
|
464
|
-
const configContent = generateQdrantConfig({
|
|
465
|
-
port,
|
|
466
|
-
grpcPort,
|
|
467
|
-
dataDir,
|
|
468
|
-
snapshotsDir,
|
|
469
|
-
})
|
|
470
|
-
await writeFile(configPath, configContent)
|
|
471
|
-
|
|
472
|
-
onProgress?.({ stage: 'starting', message: 'Starting Qdrant...' })
|
|
473
|
-
|
|
474
|
-
logDebug(`Starting qdrant with config: ${configPath}`)
|
|
475
|
-
|
|
476
|
-
/**
|
|
477
|
-
* Check log file for startup errors
|
|
478
|
-
*/
|
|
479
|
-
const checkLogForError = async (): Promise<string | null> => {
|
|
480
|
-
try {
|
|
481
|
-
const logContent = await readFile(logFile, 'utf-8')
|
|
482
|
-
const recentLog = logContent.slice(-2000) // Last 2KB
|
|
483
|
-
|
|
484
|
-
if (
|
|
485
|
-
recentLog.includes('Address already in use') ||
|
|
486
|
-
recentLog.includes('bind: Address already in use')
|
|
487
|
-
) {
|
|
488
|
-
return `Port ${port} is already in use`
|
|
489
|
-
}
|
|
490
|
-
if (recentLog.includes('Failed to bind')) {
|
|
491
|
-
return `Port ${port} is already in use`
|
|
492
|
-
}
|
|
493
|
-
} catch {
|
|
494
|
-
// Log file might not exist yet
|
|
495
|
-
}
|
|
496
|
-
return null
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// Qdrant runs in foreground, so we need to spawn detached
|
|
500
|
-
// Set cwd to container directory so any files Qdrant creates stay there
|
|
501
|
-
const args = ['--config-path', configPath]
|
|
502
|
-
|
|
503
|
-
// On non-Windows, use 'ignore' for stdio to allow Node.js process to exit
|
|
504
|
-
// (piped streams keep the event loop alive even after unref)
|
|
505
|
-
// On Windows, use 'pipe' to capture stderr for better error messages
|
|
506
|
-
if (isWindows()) {
|
|
507
|
-
return new Promise((resolve, reject) => {
|
|
508
|
-
const spawnOpts: SpawnOptions = {
|
|
509
|
-
cwd: containerDir,
|
|
510
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
511
|
-
detached: true,
|
|
512
|
-
windowsHide: true,
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
const proc = spawn(qdrantServer, args, spawnOpts)
|
|
516
|
-
let settled = false
|
|
517
|
-
let stderrOutput = ''
|
|
518
|
-
let stdoutOutput = ''
|
|
519
|
-
|
|
520
|
-
proc.on('error', (err) => {
|
|
521
|
-
if (settled) return
|
|
522
|
-
settled = true
|
|
523
|
-
reject(new Error(`Failed to spawn Qdrant server: ${err.message}`))
|
|
524
|
-
})
|
|
525
|
-
|
|
526
|
-
proc.on('exit', (code, signal) => {
|
|
527
|
-
if (settled) return
|
|
528
|
-
settled = true
|
|
529
|
-
const reason = signal ? `signal ${signal}` : `code ${code}`
|
|
530
|
-
reject(
|
|
531
|
-
new Error(
|
|
532
|
-
`Qdrant process exited unexpectedly (${reason}).\n` +
|
|
533
|
-
`Stderr: ${stderrOutput || '(none)'}\n` +
|
|
534
|
-
`Stdout: ${stdoutOutput || '(none)'}`,
|
|
535
|
-
),
|
|
536
|
-
)
|
|
537
|
-
})
|
|
538
|
-
|
|
539
|
-
proc.stdout?.on('data', (data: Buffer) => {
|
|
540
|
-
const str = data.toString()
|
|
541
|
-
stdoutOutput += str
|
|
542
|
-
logDebug(`qdrant stdout: ${str}`)
|
|
543
|
-
})
|
|
544
|
-
proc.stderr?.on('data', (data: Buffer) => {
|
|
545
|
-
const str = data.toString()
|
|
546
|
-
stderrOutput += str
|
|
547
|
-
logDebug(`qdrant stderr: ${str}`)
|
|
548
|
-
})
|
|
549
|
-
|
|
550
|
-
proc.unref()
|
|
551
|
-
|
|
552
|
-
setTimeout(async () => {
|
|
553
|
-
if (settled) return
|
|
554
|
-
|
|
555
|
-
if (!proc.pid) {
|
|
556
|
-
settled = true
|
|
557
|
-
reject(new Error('Qdrant server process failed to start (no PID)'))
|
|
558
|
-
return
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
try {
|
|
562
|
-
await writeFile(pidFile, String(proc.pid))
|
|
563
|
-
} catch {
|
|
564
|
-
// Non-fatal
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
const ready = await this.waitForReady(port)
|
|
568
|
-
if (settled) return
|
|
569
|
-
|
|
570
|
-
if (ready) {
|
|
571
|
-
settled = true
|
|
572
|
-
resolve({
|
|
573
|
-
port,
|
|
574
|
-
connectionString: this.getConnectionString(container),
|
|
575
|
-
})
|
|
576
|
-
} else {
|
|
577
|
-
settled = true
|
|
578
|
-
|
|
579
|
-
// Clean up the orphaned detached process before rejecting
|
|
580
|
-
if (proc.pid && platformService.isProcessRunning(proc.pid)) {
|
|
581
|
-
try {
|
|
582
|
-
await platformService.terminateProcess(proc.pid, true)
|
|
583
|
-
} catch {
|
|
584
|
-
// Ignore cleanup errors - best effort
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
const portError = await checkLogForError()
|
|
589
|
-
|
|
590
|
-
const errorDetails = [
|
|
591
|
-
portError || 'Qdrant failed to start within timeout.',
|
|
592
|
-
`Binary: ${qdrantServer}`,
|
|
593
|
-
`Config: ${configPath}`,
|
|
594
|
-
`Log file: ${logFile}`,
|
|
595
|
-
stderrOutput ? `Stderr:\n${stderrOutput}` : '',
|
|
596
|
-
stdoutOutput ? `Stdout:\n${stdoutOutput}` : '',
|
|
597
|
-
]
|
|
598
|
-
.filter(Boolean)
|
|
599
|
-
.join('\n')
|
|
600
|
-
|
|
601
|
-
reject(new Error(errorDetails))
|
|
602
|
-
}
|
|
603
|
-
}, 500)
|
|
604
|
-
})
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
// macOS/Linux: spawn with ignored stdio so Node.js can exit cleanly
|
|
608
|
-
const proc = spawn(qdrantServer, args, {
|
|
609
|
-
cwd: containerDir,
|
|
610
|
-
stdio: ['ignore', 'ignore', 'ignore'],
|
|
611
|
-
detached: true,
|
|
612
|
-
})
|
|
613
|
-
proc.unref()
|
|
614
|
-
|
|
615
|
-
if (!proc.pid) {
|
|
616
|
-
throw new Error('Qdrant server process failed to start (no PID)')
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
try {
|
|
620
|
-
await writeFile(pidFile, String(proc.pid))
|
|
621
|
-
} catch {
|
|
622
|
-
// Non-fatal
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
// Wait for Qdrant to be ready
|
|
626
|
-
const ready = await this.waitForReady(port)
|
|
627
|
-
|
|
628
|
-
if (ready) {
|
|
629
|
-
return {
|
|
630
|
-
port,
|
|
631
|
-
connectionString: this.getConnectionString(container),
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
const portError = await checkLogForError()
|
|
636
|
-
|
|
637
|
-
const errorDetails = [
|
|
638
|
-
portError || 'Qdrant failed to start within timeout.',
|
|
639
|
-
`Binary: ${qdrantServer}`,
|
|
640
|
-
`Config: ${configPath}`,
|
|
641
|
-
`Log file: ${logFile}`,
|
|
642
|
-
]
|
|
643
|
-
.filter(Boolean)
|
|
644
|
-
.join('\n')
|
|
645
|
-
|
|
646
|
-
throw new Error(errorDetails)
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
// Wait for Qdrant to be ready to accept connections
|
|
650
|
-
private async waitForReady(
|
|
651
|
-
port: number,
|
|
652
|
-
timeoutMs = 30000,
|
|
653
|
-
): Promise<boolean> {
|
|
654
|
-
const startTime = Date.now()
|
|
655
|
-
const checkInterval = 500
|
|
656
|
-
|
|
657
|
-
while (Date.now() - startTime < timeoutMs) {
|
|
658
|
-
try {
|
|
659
|
-
// Use REST API health check
|
|
660
|
-
const response = await qdrantApiRequest(port, 'GET', '/healthz')
|
|
661
|
-
if (response.status === 200) {
|
|
662
|
-
logDebug(`Qdrant ready on port ${port}`)
|
|
663
|
-
return true
|
|
664
|
-
}
|
|
665
|
-
} catch {
|
|
666
|
-
// Connection failed, wait and retry
|
|
667
|
-
}
|
|
668
|
-
await new Promise((resolve) => setTimeout(resolve, checkInterval))
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
logDebug(`Qdrant did not become ready within ${timeoutMs}ms`)
|
|
672
|
-
return false
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
/**
|
|
676
|
-
* Stop Qdrant server
|
|
677
|
-
* Uses process termination
|
|
678
|
-
*/
|
|
679
|
-
async stop(container: ContainerConfig): Promise<void> {
|
|
680
|
-
const { name, port } = container
|
|
681
|
-
const containerDir = paths.getContainerPath(name, { engine: ENGINE })
|
|
682
|
-
const pidFile = join(containerDir, 'qdrant.pid')
|
|
683
|
-
const grpcPort = port + 1
|
|
684
|
-
|
|
685
|
-
logDebug(`Stopping Qdrant container "${name}" on port ${port}`)
|
|
686
|
-
|
|
687
|
-
// Get PID and terminate
|
|
688
|
-
let pid: number | null = null
|
|
689
|
-
|
|
690
|
-
if (existsSync(pidFile)) {
|
|
691
|
-
try {
|
|
692
|
-
const content = await readFile(pidFile, 'utf8')
|
|
693
|
-
pid = parseInt(content.trim(), 10)
|
|
694
|
-
} catch {
|
|
695
|
-
// Ignore
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
// Kill process if running
|
|
700
|
-
// On Windows, use force kill immediately (graceful shutdown often doesn't release resources)
|
|
701
|
-
if (pid && platformService.isProcessRunning(pid)) {
|
|
702
|
-
logDebug(`Killing Qdrant process ${pid}`)
|
|
703
|
-
try {
|
|
704
|
-
// On Windows, skip graceful termination - it often doesn't release file handles
|
|
705
|
-
if (isWindows()) {
|
|
706
|
-
await platformService.terminateProcess(pid, true)
|
|
707
|
-
} else {
|
|
708
|
-
await platformService.terminateProcess(pid, false)
|
|
709
|
-
await new Promise((resolve) => setTimeout(resolve, 2000))
|
|
710
|
-
|
|
711
|
-
if (platformService.isProcessRunning(pid)) {
|
|
712
|
-
logWarning(`Graceful termination failed, force killing ${pid}`)
|
|
713
|
-
await platformService.terminateProcess(pid, true)
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
} catch (error) {
|
|
717
|
-
logDebug(`Process termination error: ${error}`)
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
// Wait for process to fully terminate
|
|
722
|
-
// Windows needs longer due to file handle release
|
|
723
|
-
// Linux/macOS need a brief wait after SIGKILL before checking ports
|
|
724
|
-
const terminationWait = isWindows() ? 3000 : 1000
|
|
725
|
-
await new Promise((resolve) => setTimeout(resolve, terminationWait))
|
|
726
|
-
|
|
727
|
-
// Kill any processes still listening on the ports
|
|
728
|
-
// This handles cases where the PID file is stale, child processes exist,
|
|
729
|
-
// or the main process termination didn't work
|
|
730
|
-
const portPids = await platformService.findProcessByPort(port)
|
|
731
|
-
const grpcPids = await platformService.findProcessByPort(grpcPort)
|
|
732
|
-
const allPids = [...new Set([...portPids, ...grpcPids])]
|
|
733
|
-
for (const portPid of allPids) {
|
|
734
|
-
if (platformService.isProcessRunning(portPid)) {
|
|
735
|
-
logDebug(`Killing process ${portPid} still on port ${port}/${grpcPort}`)
|
|
736
|
-
try {
|
|
737
|
-
await platformService.terminateProcess(portPid, true)
|
|
738
|
-
} catch {
|
|
739
|
-
// Ignore
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
// On Windows, wait again after killing port processes
|
|
745
|
-
if (isWindows() && allPids.length > 0) {
|
|
746
|
-
await new Promise((resolve) => setTimeout(resolve, 2000))
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
// Cleanup PID file
|
|
750
|
-
if (existsSync(pidFile)) {
|
|
751
|
-
try {
|
|
752
|
-
await unlink(pidFile)
|
|
753
|
-
} catch {
|
|
754
|
-
// Ignore
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
// On Windows, wait for ports to be released
|
|
759
|
-
// Windows holds onto ports longer after process termination (TIME_WAIT state)
|
|
760
|
-
// Can take 30+ seconds in some cases
|
|
761
|
-
if (isWindows()) {
|
|
762
|
-
logDebug(`Waiting for ports ${port} and ${grpcPort} to be released...`)
|
|
763
|
-
const portWaitStart = Date.now()
|
|
764
|
-
const portWaitTimeout = 30000 // 30 seconds max
|
|
765
|
-
const checkInterval = 500
|
|
766
|
-
|
|
767
|
-
while (Date.now() - portWaitStart < portWaitTimeout) {
|
|
768
|
-
const httpAvailable = await portManager.isPortAvailable(port)
|
|
769
|
-
const grpcAvailable = await portManager.isPortAvailable(grpcPort)
|
|
770
|
-
|
|
771
|
-
if (httpAvailable && grpcAvailable) {
|
|
772
|
-
logDebug('Ports released successfully')
|
|
773
|
-
break
|
|
774
|
-
}
|
|
775
|
-
await new Promise((resolve) => setTimeout(resolve, checkInterval))
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
logDebug('Qdrant stopped')
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
// Get Qdrant server status
|
|
783
|
-
async status(container: ContainerConfig): Promise<StatusResult> {
|
|
784
|
-
const { name, port } = container
|
|
785
|
-
const containerDir = paths.getContainerPath(name, { engine: ENGINE })
|
|
786
|
-
const pidFile = join(containerDir, 'qdrant.pid')
|
|
787
|
-
|
|
788
|
-
// Try health check via REST API
|
|
789
|
-
try {
|
|
790
|
-
const response = await qdrantApiRequest(port, 'GET', '/healthz')
|
|
791
|
-
if (response.status === 200) {
|
|
792
|
-
return { running: true, message: 'Qdrant is running' }
|
|
793
|
-
}
|
|
794
|
-
} catch {
|
|
795
|
-
// Not responding, check PID
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
// Check PID file
|
|
799
|
-
if (existsSync(pidFile)) {
|
|
800
|
-
try {
|
|
801
|
-
const content = await readFile(pidFile, 'utf8')
|
|
802
|
-
const pid = parseInt(content.trim(), 10)
|
|
803
|
-
if (!isNaN(pid) && pid > 0 && platformService.isProcessRunning(pid)) {
|
|
804
|
-
return {
|
|
805
|
-
running: true,
|
|
806
|
-
message: `Qdrant is running (PID: ${pid})`,
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
} catch {
|
|
810
|
-
// Ignore
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
return { running: false, message: 'Qdrant is not running' }
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
// Detect backup format
|
|
818
|
-
async detectBackupFormat(filePath: string): Promise<BackupFormat> {
|
|
819
|
-
return detectBackupFormatImpl(filePath)
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
/**
|
|
823
|
-
* Restore a backup
|
|
824
|
-
* IMPORTANT: Qdrant must be stopped before restore
|
|
825
|
-
*/
|
|
826
|
-
async restore(
|
|
827
|
-
container: ContainerConfig,
|
|
828
|
-
backupPath: string,
|
|
829
|
-
_options: { database?: string; flush?: boolean } = {},
|
|
830
|
-
): Promise<RestoreResult> {
|
|
831
|
-
const { name } = container
|
|
832
|
-
|
|
833
|
-
// Check if container is running - Qdrant must be stopped for snapshot restore
|
|
834
|
-
const statusResult = await this.status(container)
|
|
835
|
-
if (statusResult.running) {
|
|
836
|
-
throw new Error(
|
|
837
|
-
`Qdrant container "${name}" must be stopped before restore. ` +
|
|
838
|
-
`Run: spindb stop ${name}`,
|
|
839
|
-
)
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
const dataDir = paths.getContainerDataPath(name, { engine: ENGINE })
|
|
843
|
-
|
|
844
|
-
return restoreBackup(backupPath, {
|
|
845
|
-
containerName: name,
|
|
846
|
-
dataDir,
|
|
847
|
-
})
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
/**
|
|
851
|
-
* Get connection string
|
|
852
|
-
* Format: http://127.0.0.1:PORT
|
|
853
|
-
*/
|
|
854
|
-
getConnectionString(container: ContainerConfig, _database?: string): string {
|
|
855
|
-
const { port } = container
|
|
856
|
-
return `http://127.0.0.1:${port}`
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
// Open HTTP API (Qdrant uses REST API, no interactive shell)
|
|
860
|
-
async connect(container: ContainerConfig, _database?: string): Promise<void> {
|
|
861
|
-
const { port } = container
|
|
862
|
-
const url = `http://127.0.0.1:${port}/dashboard`
|
|
863
|
-
|
|
864
|
-
console.log(`Qdrant REST API available at: http://127.0.0.1:${port}`)
|
|
865
|
-
console.log(`Qdrant Dashboard: ${url}`)
|
|
866
|
-
console.log(`gRPC endpoint: http://127.0.0.1:${port + 1}`)
|
|
867
|
-
console.log('')
|
|
868
|
-
console.log('Example commands:')
|
|
869
|
-
console.log(` curl http://127.0.0.1:${port}/collections`)
|
|
870
|
-
console.log(` curl http://127.0.0.1:${port}/healthz`)
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
/**
|
|
874
|
-
* Create a new collection
|
|
875
|
-
* Qdrant uses collections instead of traditional databases
|
|
876
|
-
*/
|
|
877
|
-
async createDatabase(
|
|
878
|
-
container: ContainerConfig,
|
|
879
|
-
database: string,
|
|
880
|
-
): Promise<void> {
|
|
881
|
-
const { port } = container
|
|
882
|
-
|
|
883
|
-
// Create a collection with default vector config
|
|
884
|
-
// Users will need to configure proper vector dimensions for their use case
|
|
885
|
-
const response = await qdrantApiRequest(
|
|
886
|
-
port,
|
|
887
|
-
'PUT',
|
|
888
|
-
`/collections/${database}`,
|
|
889
|
-
{
|
|
890
|
-
vectors: {
|
|
891
|
-
size: 128, // Default vector size, user should update for their needs
|
|
892
|
-
distance: 'Cosine',
|
|
893
|
-
},
|
|
894
|
-
},
|
|
895
|
-
)
|
|
896
|
-
|
|
897
|
-
if (response.status !== 200) {
|
|
898
|
-
throw new Error(
|
|
899
|
-
`Failed to create collection: ${JSON.stringify(response.data)}`,
|
|
900
|
-
)
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
logDebug(`Created Qdrant collection: ${database}`)
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
/**
|
|
907
|
-
* Drop a collection
|
|
908
|
-
*/
|
|
909
|
-
async dropDatabase(
|
|
910
|
-
container: ContainerConfig,
|
|
911
|
-
database: string,
|
|
912
|
-
): Promise<void> {
|
|
913
|
-
const { port } = container
|
|
914
|
-
|
|
915
|
-
const response = await qdrantApiRequest(
|
|
916
|
-
port,
|
|
917
|
-
'DELETE',
|
|
918
|
-
`/collections/${database}`,
|
|
919
|
-
)
|
|
920
|
-
|
|
921
|
-
if (response.status !== 200) {
|
|
922
|
-
throw new Error(
|
|
923
|
-
`Failed to delete collection: ${JSON.stringify(response.data)}`,
|
|
924
|
-
)
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
logDebug(`Deleted Qdrant collection: ${database}`)
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
/**
|
|
931
|
-
* Get the storage size of the Qdrant instance
|
|
932
|
-
*/
|
|
933
|
-
async getDatabaseSize(container: ContainerConfig): Promise<number | null> {
|
|
934
|
-
const { port } = container
|
|
935
|
-
|
|
936
|
-
try {
|
|
937
|
-
// Make API call to verify connectivity, but Qdrant doesn't expose storage size
|
|
938
|
-
await qdrantApiRequest(port, 'GET', '/telemetry')
|
|
939
|
-
// Qdrant doesn't expose direct storage size in telemetry
|
|
940
|
-
// Return null as we can't determine exact size
|
|
941
|
-
return null
|
|
942
|
-
} catch {
|
|
943
|
-
return null
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
/**
|
|
948
|
-
* Dump from a remote Qdrant connection
|
|
949
|
-
* Uses Qdrant's REST API to create and download a full snapshot
|
|
950
|
-
*
|
|
951
|
-
* Connection string format: http://host:port or qdrant://host:port
|
|
952
|
-
* For API key auth: http://host:port?api_key=YOUR_KEY
|
|
953
|
-
*/
|
|
954
|
-
async dumpFromConnectionString(
|
|
955
|
-
connectionString: string,
|
|
956
|
-
outputPath: string,
|
|
957
|
-
): Promise<DumpResult> {
|
|
958
|
-
// Parse connection string
|
|
959
|
-
const { baseUrl, headers } = parseQdrantConnectionString(connectionString)
|
|
960
|
-
|
|
961
|
-
logDebug(`Connecting to remote Qdrant at ${baseUrl}`)
|
|
962
|
-
|
|
963
|
-
// Check connectivity and get collection count
|
|
964
|
-
const collectionsResponse = await remoteQdrantRequest(
|
|
965
|
-
baseUrl,
|
|
966
|
-
'GET',
|
|
967
|
-
'/collections',
|
|
968
|
-
headers,
|
|
969
|
-
)
|
|
970
|
-
if (collectionsResponse.status !== 200) {
|
|
971
|
-
throw new Error(
|
|
972
|
-
`Failed to connect to Qdrant at ${baseUrl}: ${JSON.stringify(collectionsResponse.data)}`,
|
|
973
|
-
)
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
const collectionsData = collectionsResponse.data as {
|
|
977
|
-
result?: { collections?: Array<{ name: string }> }
|
|
978
|
-
}
|
|
979
|
-
const collectionCount = collectionsData.result?.collections?.length ?? 0
|
|
980
|
-
|
|
981
|
-
logDebug(`Found ${collectionCount} collections on remote server`)
|
|
982
|
-
|
|
983
|
-
// Create a full snapshot on the remote server
|
|
984
|
-
logDebug('Creating snapshot on remote server...')
|
|
985
|
-
const snapshotResponse = await remoteQdrantRequest(
|
|
986
|
-
baseUrl,
|
|
987
|
-
'POST',
|
|
988
|
-
'/snapshots',
|
|
989
|
-
headers,
|
|
990
|
-
)
|
|
991
|
-
|
|
992
|
-
if (snapshotResponse.status !== 200) {
|
|
993
|
-
throw new Error(
|
|
994
|
-
`Failed to create snapshot on remote Qdrant: ${JSON.stringify(snapshotResponse.data)}`,
|
|
995
|
-
)
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
const snapshotData = snapshotResponse.data as { result?: { name?: string } }
|
|
999
|
-
const snapshotName = snapshotData.result?.name
|
|
1000
|
-
|
|
1001
|
-
if (!snapshotName) {
|
|
1002
|
-
throw new Error(
|
|
1003
|
-
'Qdrant snapshot creation failed: no snapshot name returned',
|
|
1004
|
-
)
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
logDebug(`Remote snapshot created: ${snapshotName}`)
|
|
1008
|
-
|
|
1009
|
-
// Download the snapshot with timeout (5 minutes for large snapshots)
|
|
1010
|
-
const snapshotUrl = `${baseUrl}/snapshots/${snapshotName}`
|
|
1011
|
-
logDebug(`Downloading snapshot from ${snapshotUrl}...`)
|
|
1012
|
-
|
|
1013
|
-
const controller = new AbortController()
|
|
1014
|
-
const timeoutId = setTimeout(() => controller.abort(), 5 * 60 * 1000)
|
|
1015
|
-
|
|
1016
|
-
let downloadResponse: Response
|
|
1017
|
-
try {
|
|
1018
|
-
downloadResponse = await fetch(snapshotUrl, {
|
|
1019
|
-
headers,
|
|
1020
|
-
signal: controller.signal,
|
|
1021
|
-
})
|
|
1022
|
-
} catch (fetchError) {
|
|
1023
|
-
clearTimeout(timeoutId)
|
|
1024
|
-
// Clean up the snapshot we created
|
|
1025
|
-
await fetch(`${snapshotUrl}`, { method: 'DELETE', headers }).catch(
|
|
1026
|
-
() => {},
|
|
1027
|
-
)
|
|
1028
|
-
const err = fetchError as Error
|
|
1029
|
-
if (err.name === 'AbortError') {
|
|
1030
|
-
throw new Error('Snapshot download timed out after 5 minutes')
|
|
1031
|
-
}
|
|
1032
|
-
throw fetchError
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
if (!downloadResponse.ok) {
|
|
1036
|
-
clearTimeout(timeoutId)
|
|
1037
|
-
// Clean up the snapshot we created
|
|
1038
|
-
await fetch(`${snapshotUrl}`, { method: 'DELETE', headers }).catch(
|
|
1039
|
-
() => {},
|
|
1040
|
-
)
|
|
1041
|
-
throw new Error(
|
|
1042
|
-
`Failed to download snapshot: ${downloadResponse.status} ${downloadResponse.statusText}`,
|
|
1043
|
-
)
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
// Stream to output path instead of buffering in memory
|
|
1047
|
-
if (!downloadResponse.body) {
|
|
1048
|
-
clearTimeout(timeoutId)
|
|
1049
|
-
throw new Error('Download failed: response has no body')
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
const fileStream = createWriteStream(outputPath)
|
|
1053
|
-
try {
|
|
1054
|
-
const nodeStream = Readable.fromWeb(downloadResponse.body)
|
|
1055
|
-
await pipeline(nodeStream, fileStream)
|
|
1056
|
-
clearTimeout(timeoutId)
|
|
1057
|
-
} catch (streamError) {
|
|
1058
|
-
clearTimeout(timeoutId)
|
|
1059
|
-
fileStream.destroy()
|
|
1060
|
-
// Remove partial output file
|
|
1061
|
-
await unlink(outputPath).catch(() => {})
|
|
1062
|
-
// Clean up the snapshot on remote server
|
|
1063
|
-
await fetch(`${snapshotUrl}`, { method: 'DELETE', headers }).catch(
|
|
1064
|
-
() => {},
|
|
1065
|
-
)
|
|
1066
|
-
throw streamError
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
logDebug(`Snapshot downloaded to ${outputPath}`)
|
|
1070
|
-
|
|
1071
|
-
// Clean up snapshot on remote server (courtesy cleanup)
|
|
1072
|
-
await fetch(`${snapshotUrl}`, { method: 'DELETE', headers }).catch(
|
|
1073
|
-
(err) => {
|
|
1074
|
-
logDebug(`Could not delete remote snapshot (non-fatal): ${err}`)
|
|
1075
|
-
},
|
|
1076
|
-
)
|
|
1077
|
-
|
|
1078
|
-
return {
|
|
1079
|
-
filePath: outputPath,
|
|
1080
|
-
warnings:
|
|
1081
|
-
collectionCount === 0
|
|
1082
|
-
? ['Remote Qdrant instance has no collections']
|
|
1083
|
-
: undefined,
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
// Create a backup
|
|
1088
|
-
async backup(
|
|
1089
|
-
container: ContainerConfig,
|
|
1090
|
-
outputPath: string,
|
|
1091
|
-
options: BackupOptions,
|
|
1092
|
-
): Promise<BackupResult> {
|
|
1093
|
-
return createBackup(container, outputPath, options)
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
// Run a command - Qdrant uses REST API, not command files
|
|
1097
|
-
async runScript(
|
|
1098
|
-
container: ContainerConfig,
|
|
1099
|
-
options: { file?: string; sql?: string; database?: string },
|
|
1100
|
-
): Promise<void> {
|
|
1101
|
-
const { port } = container
|
|
1102
|
-
|
|
1103
|
-
if (options.file) {
|
|
1104
|
-
throw new Error(
|
|
1105
|
-
'Qdrant does not support command files. Use the REST API directly.\n' +
|
|
1106
|
-
`Example: curl -X POST http://127.0.0.1:${port}/collections`,
|
|
1107
|
-
)
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
if (options.sql) {
|
|
1111
|
-
// Try to interpret as a simple command (e.g., "LIST COLLECTIONS")
|
|
1112
|
-
const command = options.sql.trim().toUpperCase()
|
|
1113
|
-
|
|
1114
|
-
if (command === 'LIST COLLECTIONS' || command === 'SHOW COLLECTIONS') {
|
|
1115
|
-
const response = await qdrantApiRequest(port, 'GET', '/collections')
|
|
1116
|
-
console.log(JSON.stringify(response.data, null, 2))
|
|
1117
|
-
return
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
throw new Error(
|
|
1121
|
-
'Qdrant uses REST API for operations. Use curl or the Qdrant client libraries.\n' +
|
|
1122
|
-
`API endpoint: http://127.0.0.1:${port}`,
|
|
1123
|
-
)
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
throw new Error('Either file or sql option must be provided')
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
/**
|
|
1130
|
-
* Execute a query via REST API
|
|
1131
|
-
*
|
|
1132
|
-
* Query format: METHOD /path [JSON body]
|
|
1133
|
-
* Examples:
|
|
1134
|
-
* GET /collections
|
|
1135
|
-
* POST /collections/my_collection/points/search {"vector": [0.1, 0.2], "limit": 10}
|
|
1136
|
-
*/
|
|
1137
|
-
async executeQuery(
|
|
1138
|
-
container: ContainerConfig,
|
|
1139
|
-
query: string,
|
|
1140
|
-
options?: QueryOptions,
|
|
1141
|
-
): Promise<QueryResult> {
|
|
1142
|
-
const { port } = container
|
|
1143
|
-
|
|
1144
|
-
// Parse the query string: METHOD /path [body]
|
|
1145
|
-
const trimmed = query.trim()
|
|
1146
|
-
const spaceIdx = trimmed.indexOf(' ')
|
|
1147
|
-
|
|
1148
|
-
if (spaceIdx === -1) {
|
|
1149
|
-
throw new Error(
|
|
1150
|
-
'Invalid query format. Expected: METHOD /path [body]\n' +
|
|
1151
|
-
'Example: GET /collections',
|
|
1152
|
-
)
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
const method = (options?.method ||
|
|
1156
|
-
trimmed.substring(0, spaceIdx).toUpperCase()) as
|
|
1157
|
-
| 'GET'
|
|
1158
|
-
| 'POST'
|
|
1159
|
-
| 'PUT'
|
|
1160
|
-
| 'DELETE'
|
|
1161
|
-
const rest = trimmed.substring(spaceIdx + 1).trim()
|
|
1162
|
-
|
|
1163
|
-
// Extract path and optional JSON body
|
|
1164
|
-
let path: string
|
|
1165
|
-
let body: Record<string, unknown> | undefined = options?.body
|
|
1166
|
-
|
|
1167
|
-
const bodyStart = rest.indexOf('{')
|
|
1168
|
-
if (bodyStart !== -1) {
|
|
1169
|
-
// Always extract path without the JSON blob
|
|
1170
|
-
path = rest.substring(0, bodyStart).trim()
|
|
1171
|
-
if (options?.body) {
|
|
1172
|
-
// Both inline JSON and options.body provided - error
|
|
1173
|
-
throw new Error(
|
|
1174
|
-
'Cannot specify both inline JSON body in query and options.body. Use one or the other.',
|
|
1175
|
-
)
|
|
1176
|
-
}
|
|
1177
|
-
try {
|
|
1178
|
-
body = JSON.parse(rest.substring(bodyStart)) as Record<string, unknown>
|
|
1179
|
-
} catch {
|
|
1180
|
-
throw new Error('Invalid JSON body in query')
|
|
1181
|
-
}
|
|
1182
|
-
} else {
|
|
1183
|
-
path = rest
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
// Ensure path starts with /
|
|
1187
|
-
if (!path.startsWith('/')) {
|
|
1188
|
-
path = '/' + path
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
const response = await qdrantApiRequest(port, method, path, body)
|
|
1192
|
-
|
|
1193
|
-
if (response.status >= 400) {
|
|
1194
|
-
throw new Error(
|
|
1195
|
-
`Qdrant API error (${response.status}): ${JSON.stringify(response.data)}`,
|
|
1196
|
-
)
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
return parseRESTAPIResult(JSON.stringify(response.data))
|
|
1200
|
-
}
|
|
1201
|
-
|
|
1202
|
-
/**
|
|
1203
|
-
* List databases for Qdrant.
|
|
1204
|
-
* Qdrant uses collections, not databases. Returns the configured database.
|
|
1205
|
-
*/
|
|
1206
|
-
async listDatabases(container: ContainerConfig): Promise<string[]> {
|
|
1207
|
-
// Qdrant uses collections, not databases
|
|
1208
|
-
// Return the container's configured database
|
|
1209
|
-
return [container.database]
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
/**
|
|
1213
|
-
* Create/update the global API key for Qdrant.
|
|
1214
|
-
*
|
|
1215
|
-
* Qdrant supports only a single global API key (set in config.yaml).
|
|
1216
|
-
* Calling createUser multiple times will overwrite the previous key.
|
|
1217
|
-
* The caller-provided username is stored in the credential file for
|
|
1218
|
-
* bookkeeping but has no effect on Qdrant itself — authentication
|
|
1219
|
-
* is solely via the api-key header.
|
|
1220
|
-
*/
|
|
1221
|
-
async createUser(
|
|
1222
|
-
container: ContainerConfig,
|
|
1223
|
-
options: CreateUserOptions,
|
|
1224
|
-
): Promise<UserCredentials> {
|
|
1225
|
-
const { username, password } = options
|
|
1226
|
-
assertValidUsername(username)
|
|
1227
|
-
const { port, name } = container
|
|
1228
|
-
|
|
1229
|
-
// Qdrant uses a single global API key in config.yaml.
|
|
1230
|
-
// Read current config, set/replace api_key, write back, and restart.
|
|
1231
|
-
const containerDir = paths.getContainerPath(name, { engine: ENGINE })
|
|
1232
|
-
const configPath = join(containerDir, 'config.yaml')
|
|
1233
|
-
|
|
1234
|
-
const currentConfig = await readFile(configPath, 'utf-8')
|
|
1235
|
-
|
|
1236
|
-
// Parse YAML config line-by-line to find service section and api_key.
|
|
1237
|
-
// Assumes 2-space indentation, no inline comments on api_key lines, and simple scalar values.
|
|
1238
|
-
// This is a lightweight string-edit approach; use a YAML parser if those cases must be supported.
|
|
1239
|
-
const lines = currentConfig.split('\n')
|
|
1240
|
-
const serviceIdx = lines.findIndex((l) => /^service:/.test(l))
|
|
1241
|
-
|
|
1242
|
-
if (serviceIdx < 0) {
|
|
1243
|
-
throw new Error('Could not find service section in Qdrant config')
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
// Scan the service section for existing api_key and last property line
|
|
1247
|
-
let apiKeyIdx = -1
|
|
1248
|
-
let lastServicePropIdx = serviceIdx
|
|
1249
|
-
for (let i = serviceIdx + 1; i < lines.length; i++) {
|
|
1250
|
-
if (/^\s+\S/.test(lines[i])) {
|
|
1251
|
-
lastServicePropIdx = i
|
|
1252
|
-
if (/^\s+api_key:/.test(lines[i])) {
|
|
1253
|
-
apiKeyIdx = i
|
|
1254
|
-
}
|
|
1255
|
-
} else if (/^\S/.test(lines[i]) && lines[i].trim() !== '') {
|
|
1256
|
-
break // Next top-level section
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
const yamlSafePassword = JSON.stringify(password)
|
|
1261
|
-
if (apiKeyIdx >= 0) {
|
|
1262
|
-
lines[apiKeyIdx] = ` api_key: ${yamlSafePassword}`
|
|
1263
|
-
} else {
|
|
1264
|
-
lines.splice(lastServicePropIdx + 1, 0, ` api_key: ${yamlSafePassword}`)
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
const updatedConfig = lines.join('\n')
|
|
1268
|
-
|
|
1269
|
-
// Validate the modified config is structurally sound before writing
|
|
1270
|
-
// Check that service section and api_key line are present
|
|
1271
|
-
const updatedLines = updatedConfig.split('\n')
|
|
1272
|
-
const hasService = updatedLines.some((l) => /^service:/.test(l))
|
|
1273
|
-
const hasApiKey = updatedLines.some((l) => /^\s+api_key:/.test(l))
|
|
1274
|
-
if (!hasService || !hasApiKey) {
|
|
1275
|
-
throw new Error(
|
|
1276
|
-
'Failed to update Qdrant config: modified YAML is structurally invalid. ' +
|
|
1277
|
-
'The service section or api_key entry is missing after modification.',
|
|
1278
|
-
)
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
// Only restart if the container is currently running
|
|
1282
|
-
const statusResult = await this.status(container)
|
|
1283
|
-
if (statusResult.running) {
|
|
1284
|
-
logWarning(
|
|
1285
|
-
`Restarting Qdrant container "${name}" to apply API key change. ` +
|
|
1286
|
-
'Active client connections will be disconnected.',
|
|
1287
|
-
)
|
|
1288
|
-
await this.stop(container)
|
|
1289
|
-
await writeFile(configPath, updatedConfig)
|
|
1290
|
-
await chmod(configPath, 0o600)
|
|
1291
|
-
await this.start(container)
|
|
1292
|
-
} else {
|
|
1293
|
-
await writeFile(configPath, updatedConfig)
|
|
1294
|
-
await chmod(configPath, 0o600)
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
logDebug(`Configured Qdrant global API key (credential label: ${username})`)
|
|
1298
|
-
|
|
1299
|
-
const connectionString = `http://127.0.0.1:${port}`
|
|
1300
|
-
|
|
1301
|
-
return {
|
|
1302
|
-
username,
|
|
1303
|
-
password: '',
|
|
1304
|
-
connectionString,
|
|
1305
|
-
engine: container.engine,
|
|
1306
|
-
container: container.name,
|
|
1307
|
-
apiKey: password,
|
|
1308
|
-
}
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
export const qdrantEngine = new QdrantEngine()
|