From 09db054d0898d8b5a3156314fe65f9ec36a7e32c Mon Sep 17 00:00:00 2001 From: Naing Linn Khant Date: Thu, 2 Oct 2025 02:09:26 +0700 Subject: [PATCH 01/10] chore: update sdk and rename to stores --- packages/cli/README.md | 4 +- packages/cli/package.json | 4 +- packages/cli/src/bin/mxbai.ts | 4 +- .../cli/src/commands/vector-store/create.ts | 20 +-- .../cli/src/commands/vector-store/delete.ts | 19 +-- .../src/commands/vector-store/files/delete.ts | 13 +- .../src/commands/vector-store/files/get.ts | 16 +- .../src/commands/vector-store/files/list.ts | 13 +- packages/cli/src/commands/vector-store/get.ts | 40 +++-- .../cli/src/commands/vector-store/index.ts | 2 +- .../cli/src/commands/vector-store/list.ts | 18 +-- packages/cli/src/commands/vector-store/qa.ts | 15 +- .../cli/src/commands/vector-store/search.ts | 37 +++-- .../cli/src/commands/vector-store/sync.ts | 19 +-- .../cli/src/commands/vector-store/update.ts | 56 +++---- .../cli/src/commands/vector-store/upload.ts | 27 ++-- packages/cli/src/index.ts | 2 +- packages/cli/src/utils/completion-cache.ts | 2 +- packages/cli/src/utils/config.ts | 2 +- packages/cli/src/utils/manifest.ts | 13 +- packages/cli/src/utils/sync-state.ts | 11 +- packages/cli/src/utils/sync.ts | 10 +- packages/cli/src/utils/upload.ts | 30 ++-- packages/cli/src/utils/vector-store.ts | 36 ++--- .../cli/tests/commands/completion.test.ts | 4 +- .../commands/vector-store/create.test.ts | 36 ++--- .../commands/vector-store/delete.test.ts | 37 +++-- .../tests/commands/vector-store/files.test.ts | 98 ++++++------ .../tests/commands/vector-store/get.test.ts | 59 ++++---- .../tests/commands/vector-store/list.test.ts | 54 +++---- .../tests/commands/vector-store/qa.test.ts | 111 +++++++------- .../commands/vector-store/search.test.ts | 115 +++++++------- .../tests/commands/vector-store/sync.test.ts | 17 +-- .../commands/vector-store/update.test.ts | 89 +++++------ .../commands/vector-store/upload.test.ts | 42 +++--- packages/cli/tests/helpers/test-utils.ts | 4 +- .../cli/tests/utils/completion-cache.test.ts | 12 +- packages/cli/tests/utils/config.test.ts | 10 +- packages/cli/tests/utils/sync.test.ts | 14 +- packages/cli/tests/utils/upload.test.ts | 56 +++---- packages/cli/tests/utils/vector-store.test.ts | 141 ++++++++---------- pnpm-lock.yaml | 10 +- 42 files changed, 610 insertions(+), 712 deletions(-) diff --git a/packages/cli/README.md b/packages/cli/README.md index 14044de..2b6863e 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -531,8 +531,8 @@ tests/ ```typescript import { createMyCommand } from './my-command'; - // Add to vectorStoreCommand - vectorStoreCommand.addCommand(createMyCommand()); + // Add to storeCommand + storeCommand.addCommand(createMyCommand()); ``` 3. **Add tests** in `tests/commands/vector-store/my-command.test.ts`: diff --git a/packages/cli/package.json b/packages/cli/package.json index a567f6a..a8a61a9 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -13,7 +13,7 @@ "keywords": [ "mixedbread", "cli", - "vector-store", + "store", "embeddings", "command-line" ], @@ -49,7 +49,7 @@ "setup-cli": "node ./scripts/setup-cli.js" }, "dependencies": { - "@mixedbread/sdk": "^0.26.0", + "@mixedbread/sdk": "^0.31.1", "@pnpm/tabtab": "^0.5.4", "chalk": "^5.3.0", "cli-table3": "^0.6.5", diff --git a/packages/cli/src/bin/mxbai.ts b/packages/cli/src/bin/mxbai.ts index fd403be..02f3104 100644 --- a/packages/cli/src/bin/mxbai.ts +++ b/packages/cli/src/bin/mxbai.ts @@ -32,7 +32,7 @@ import { createCompletionServerCommand, } from "../commands/completion"; import { createConfigCommand } from "../commands/config"; -import { createVectorStoreCommand } from "../commands/vector-store"; +import { createStoreCommand } from "../commands/vector-store"; import { setupGlobalOptions } from "../utils/global-options"; const program = new Command(); @@ -46,7 +46,7 @@ program setupGlobalOptions(program); // Add commands -program.addCommand(createVectorStoreCommand()); +program.addCommand(createStoreCommand()); program.addCommand(createConfigCommand()); program.addCommand(createCompletionCommand()); program.addCommand(createCompletionServerCommand()); diff --git a/packages/cli/src/commands/vector-store/create.ts b/packages/cli/src/commands/vector-store/create.ts index 66a6048..7fbd4d8 100644 --- a/packages/cli/src/commands/vector-store/create.ts +++ b/packages/cli/src/commands/vector-store/create.ts @@ -17,7 +17,7 @@ import { import { validateMetadata } from "../../utils/metadata"; import { formatOutput } from "../../utils/output"; -const CreateVectorStoreSchema = extendGlobalOptions({ +const CreateStoreSchema = extendGlobalOptions({ name: z.string().min(1, { error: '"name" is required' }), description: z.string().optional(), expiresAfter: z.coerce @@ -51,7 +51,7 @@ export function createCreateCommand(): Command { const mergedOptions = mergeCommandOptions(command, options); const client = createClient(mergedOptions); - const parsedOptions = parseOptions(CreateVectorStoreSchema, { + const parsedOptions = parseOptions(CreateStoreSchema, { ...mergedOptions, name, }); @@ -60,7 +60,7 @@ export function createCreateCommand(): Command { spinner = ora("Creating store...").start(); - const vectorStore = await client.vectorStores.create({ + const store = await client.stores.create({ name: parsedOptions.name, description: parsedOptions.description, expires_after: parsedOptions.expiresAfter @@ -76,14 +76,14 @@ export function createCreateCommand(): Command { formatOutput( { - id: vectorStore.id, - name: vectorStore.name, - description: vectorStore.description, - expires_after: vectorStore.expires_after, + id: store.id, + name: store.name, + description: store.description, + expires_after: store.expires_after, metadata: parsedOptions.format === "table" - ? JSON.stringify(vectorStore.metadata, null, 2) - : vectorStore.metadata, + ? JSON.stringify(store.metadata, null, 2) + : store.metadata, }, parsedOptions.format ); @@ -91,7 +91,7 @@ export function createCreateCommand(): Command { // Update completion cache with the new store const keyName = getCurrentKeyName(); if (keyName) { - updateCacheAfterCreate(keyName, vectorStore.name); + updateCacheAfterCreate(keyName, store.name); } } catch (error) { spinner?.fail("Failed to create store"); diff --git a/packages/cli/src/commands/vector-store/delete.ts b/packages/cli/src/commands/vector-store/delete.ts index 19f733b..3886a66 100644 --- a/packages/cli/src/commands/vector-store/delete.ts +++ b/packages/cli/src/commands/vector-store/delete.ts @@ -15,9 +15,9 @@ import { mergeCommandOptions, parseOptions, } from "../../utils/global-options"; -import { resolveVectorStore } from "../../utils/vector-store"; +import { resolveStore } from "../../utils/vector-store"; -const DeleteVectorStoreSchema = extendGlobalOptions({ +const DeleteStoreSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), yes: z.boolean().optional(), }); @@ -41,16 +41,13 @@ export function createDeleteCommand(): Command { try { const mergedOptions = mergeCommandOptions(command, options); - const parsedOptions = parseOptions(DeleteVectorStoreSchema, { + const parsedOptions = parseOptions(DeleteStoreSchema, { ...mergedOptions, nameOrId, }); const client = createClient(parsedOptions); - const vectorStore = await resolveVectorStore( - client, - parsedOptions.nameOrId - ); + const store = await resolveStore(client, parsedOptions.nameOrId); // Confirmation prompt unless --yes is used if (!parsedOptions.yes) { @@ -58,7 +55,7 @@ export function createDeleteCommand(): Command { { type: "confirm", name: "confirmed", - message: `Are you sure you want to delete store "${vectorStore.name}" (${vectorStore.id})? This action cannot be undone.`, + message: `Are you sure you want to delete store "${store.name}" (${store.id})? This action cannot be undone.`, default: false, }, ]); @@ -71,14 +68,14 @@ export function createDeleteCommand(): Command { spinner = ora("Deleting store...").start(); - await client.vectorStores.delete(vectorStore.id); + await client.stores.delete(store.id); - spinner.succeed(`Store "${vectorStore.name}" deleted successfully`); + spinner.succeed(`Store "${store.name}" deleted successfully`); // Update completion cache by removing the deleted store const keyName = getCurrentKeyName(); if (keyName) { - updateCacheAfterDelete(keyName, vectorStore.name); + updateCacheAfterDelete(keyName, store.name); } } catch (error) { spinner?.fail("Failed to delete store"); diff --git a/packages/cli/src/commands/vector-store/files/delete.ts b/packages/cli/src/commands/vector-store/files/delete.ts index 7d39e0e..d2d8747 100644 --- a/packages/cli/src/commands/vector-store/files/delete.ts +++ b/packages/cli/src/commands/vector-store/files/delete.ts @@ -11,7 +11,7 @@ import { mergeCommandOptions, parseOptions, } from "../../../utils/global-options"; -import { resolveVectorStore } from "../../../utils/vector-store"; +import { resolveStore } from "../../../utils/vector-store"; const DeleteFileSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), @@ -42,10 +42,7 @@ export function createDeleteCommand(): Command { }); const client = createClient(parsedOptions); - const vectorStore = await resolveVectorStore( - client, - parsedOptions.nameOrId - ); + const store = await resolveStore(client, parsedOptions.nameOrId); // Confirmation prompt unless --yes is used if (!parsedOptions.yes) { @@ -53,7 +50,7 @@ export function createDeleteCommand(): Command { { type: "confirm", name: "confirmed", - message: `Are you sure you want to delete file "${parsedOptions.fileId}" from store "${vectorStore.name}" (${vectorStore.id})? This action cannot be undone.`, + message: `Are you sure you want to delete file "${parsedOptions.fileId}" from store "${store.name}" (${store.id})? This action cannot be undone.`, default: false, }, ]); @@ -66,8 +63,8 @@ export function createDeleteCommand(): Command { spinner = ora("Deleting file...").start(); - await client.vectorStores.files.delete(parsedOptions.fileId, { - vector_store_identifier: vectorStore.id, + await client.stores.files.delete(parsedOptions.fileId, { + store_identifier: store.id, }); spinner.succeed(`File ${parsedOptions.fileId} deleted successfully`); diff --git a/packages/cli/src/commands/vector-store/files/get.ts b/packages/cli/src/commands/vector-store/files/get.ts index ca74f91..6ee82bb 100644 --- a/packages/cli/src/commands/vector-store/files/get.ts +++ b/packages/cli/src/commands/vector-store/files/get.ts @@ -11,7 +11,7 @@ import { parseOptions, } from "../../../utils/global-options"; import { formatBytes, formatOutput } from "../../../utils/output"; -import { resolveVectorStore } from "../../../utils/vector-store"; +import { resolveStore } from "../../../utils/vector-store"; const GetFileSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), @@ -41,17 +41,11 @@ export function createGetCommand(): Command { const client = createClient(parsedOptions); spinner = ora("Loading file details...").start(); - const vectorStore = await resolveVectorStore( - client, - parsedOptions.nameOrId - ); + const store = await resolveStore(client, parsedOptions.nameOrId); - const file = await client.vectorStores.files.retrieve( - parsedOptions.fileId, - { - vector_store_identifier: vectorStore.id, - } - ); + const file = await client.stores.files.retrieve(parsedOptions.fileId, { + store_identifier: store.id, + }); spinner.succeed("File details loaded"); diff --git a/packages/cli/src/commands/vector-store/files/list.ts b/packages/cli/src/commands/vector-store/files/list.ts index 7f9bf02..c413e9c 100644 --- a/packages/cli/src/commands/vector-store/files/list.ts +++ b/packages/cli/src/commands/vector-store/files/list.ts @@ -1,4 +1,4 @@ -import type { VectorStoreFile } from "@mixedbread/sdk/resources/vector-stores"; +import type { StoreFile } from "@mixedbread/sdk/resources/stores"; import chalk from "chalk"; import { Command } from "commander"; import ora, { type Ora } from "ora"; @@ -15,7 +15,7 @@ import { formatCountWithSuffix, formatOutput, } from "../../../utils/output"; -import { resolveVectorStore } from "../../../utils/vector-store"; +import { resolveStore } from "../../../utils/vector-store"; import type { FilesOptions } from "."; const ListFilesSchema = extendGlobalOptions({ @@ -59,12 +59,9 @@ export function createListCommand(): Command { const client = createClient(parsedOptions); spinner = ora("Loading files...").start(); - const vectorStore = await resolveVectorStore( - client, - parsedOptions.nameOrId - ); + const store = await resolveStore(client, parsedOptions.nameOrId); - const response = await client.vectorStores.files.list(vectorStore.id, { + const response = await client.stores.files.list(store.id, { limit: parsedOptions.limit || 10, }); @@ -73,7 +70,7 @@ export function createListCommand(): Command { // Apply status filter if (parsedOptions.status && parsedOptions.status !== "all") { files = files.filter( - (file: VectorStoreFile) => file.status === parsedOptions.status + (file: StoreFile) => file.status === parsedOptions.status ); } diff --git a/packages/cli/src/commands/vector-store/get.ts b/packages/cli/src/commands/vector-store/get.ts index 09a6465..21855f1 100644 --- a/packages/cli/src/commands/vector-store/get.ts +++ b/packages/cli/src/commands/vector-store/get.ts @@ -11,9 +11,9 @@ import { parseOptions, } from "../../utils/global-options"; import { formatBytes, formatOutput } from "../../utils/output"; -import { resolveVectorStore } from "../../utils/vector-store"; +import { resolveStore } from "../../utils/vector-store"; -const GetVectorStoreSchema = extendGlobalOptions({ +const GetStoreSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), }); @@ -32,42 +32,38 @@ export function createGetCommand(): Command { try { const mergedOptions = mergeCommandOptions(command, options); - const parsedOptions = parseOptions(GetVectorStoreSchema, { + const parsedOptions = parseOptions(GetStoreSchema, { ...mergedOptions, nameOrId, }); const client = createClient(parsedOptions); spinner = ora("Loading store details...").start(); - const vectorStore = await resolveVectorStore( - client, - parsedOptions.nameOrId - ); + const store = await resolveStore(client, parsedOptions.nameOrId); spinner.succeed("Store details loaded"); const formattedData = { - name: vectorStore.name, - id: vectorStore.id, - description: vectorStore.description || "N/A", + name: store.name, + id: store.id, + description: store.description || "N/A", status: - vectorStore.expires_at && - new Date(vectorStore.expires_at) < new Date() + store.expires_at && new Date(store.expires_at) < new Date() ? "expired" : "active", - "total files": vectorStore.file_counts?.total || 0, - "completed files": vectorStore.file_counts?.completed || 0, - "processing files": vectorStore.file_counts?.in_progress || 0, - "failed files": vectorStore.file_counts?.failed || 0, - usage: formatBytes(vectorStore.usage_bytes || 0), - "created at": new Date(vectorStore.created_at).toLocaleString(), - "expires at": vectorStore.expires_at - ? new Date(vectorStore.expires_at).toLocaleString() + "total files": store.file_counts?.total || 0, + "completed files": store.file_counts?.completed || 0, + "processing files": store.file_counts?.in_progress || 0, + "failed files": store.file_counts?.failed || 0, + usage: formatBytes(store.usage_bytes || 0), + "created at": new Date(store.created_at).toLocaleString(), + "expires at": store.expires_at + ? new Date(store.expires_at).toLocaleString() : "Never", metadata: parsedOptions.format === "table" - ? JSON.stringify(vectorStore.metadata, null, 2) - : vectorStore.metadata, + ? JSON.stringify(store.metadata, null, 2) + : store.metadata, }; formatOutput(formattedData, parsedOptions.format); diff --git a/packages/cli/src/commands/vector-store/index.ts b/packages/cli/src/commands/vector-store/index.ts index de37427..c0c4a1d 100644 --- a/packages/cli/src/commands/vector-store/index.ts +++ b/packages/cli/src/commands/vector-store/index.ts @@ -10,7 +10,7 @@ import { createSyncCommand } from "./sync"; import { createUpdateCommand } from "./update"; import { createUploadCommand } from "./upload"; -export function createVectorStoreCommand(): Command { +export function createStoreCommand(): Command { const storeCommand = new Command("store").description("Manage stores"); // Add subcommands diff --git a/packages/cli/src/commands/vector-store/list.ts b/packages/cli/src/commands/vector-store/list.ts index 66d27ed..93e526f 100644 --- a/packages/cli/src/commands/vector-store/list.ts +++ b/packages/cli/src/commands/vector-store/list.ts @@ -20,7 +20,7 @@ import { formatOutput, } from "../../utils/output"; -const ListVectorStoreSchema = extendGlobalOptions({ +const ListStoreSchema = extendGlobalOptions({ filter: z.string().optional(), limit: z.coerce .number({ error: '"limit" must be a number' }) @@ -49,33 +49,33 @@ export function createListCommand(): Command { try { const mergedOptions = mergeCommandOptions(command, options); const parsedOptions = parseOptions( - ListVectorStoreSchema, + ListStoreSchema, mergedOptions as Record ); const client = createClient(parsedOptions); spinner = ora("Loading stores...").start(); - const response = await client.vectorStores.list({ + const response = await client.stores.list({ limit: parsedOptions.limit || 10, }); - let vectorStores = response.data; + let stores = response.data; // Apply filter if provided if (parsedOptions.filter) { const filterPattern = parsedOptions.filter.toLowerCase(); - vectorStores = vectorStores.filter((store) => + stores = stores.filter((store) => store.name.toLowerCase().includes(filterPattern) ); } - if (vectorStores.length === 0) { + if (stores.length === 0) { spinner.info("No stores found."); return; } // Format data for output - const formattedData = vectorStores.map((store) => ({ + const formattedData = stores.map((store) => ({ name: store.name, id: store.id, status: @@ -87,9 +87,7 @@ export function createListCommand(): Command { created: new Date(store.created_at).toLocaleDateString(), })); - spinner.succeed( - `Found ${formatCountWithSuffix(vectorStores.length, "store")}` - ); + spinner.succeed(`Found ${formatCountWithSuffix(stores.length, "store")}`); formatOutput(formattedData, parsedOptions.format); // Update completion cache with the fetched stores diff --git a/packages/cli/src/commands/vector-store/qa.ts b/packages/cli/src/commands/vector-store/qa.ts index 6e966d1..1a32fae 100644 --- a/packages/cli/src/commands/vector-store/qa.ts +++ b/packages/cli/src/commands/vector-store/qa.ts @@ -12,9 +12,9 @@ import { parseOptions, } from "../../utils/global-options"; import { formatOutput } from "../../utils/output"; -import { resolveVectorStore } from "../../utils/vector-store"; +import { resolveStore } from "../../utils/vector-store"; -const QAVectorStoreSchema = extendGlobalOptions({ +const QAStoreSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), question: z.string().min(1, { error: '"question" is required' }), topK: z.coerce @@ -58,7 +58,7 @@ export function createQACommand(): Command { try { const mergedOptions = mergeCommandOptions(command, options); - const parsedOptions = parseOptions(QAVectorStoreSchema, { + const parsedOptions = parseOptions(QAStoreSchema, { ...mergedOptions, nameOrId, question, @@ -66,18 +66,15 @@ export function createQACommand(): Command { const client = createClient(parsedOptions); spinner = ora("Processing question...").start(); - const vectorStore = await resolveVectorStore( - client, - parsedOptions.nameOrId - ); + const store = await resolveStore(client, parsedOptions.nameOrId); const config = loadConfig(); // Get default values from config const topK = parsedOptions.topK || config.defaults?.search?.top_k || 10; - const response = await client.vectorStores.questionAnswering({ + const response = await client.stores.questionAnswering({ query: parsedOptions.question, - vector_store_identifiers: [vectorStore.id], + store_identifiers: [store.id], top_k: topK, search_options: { score_threshold: parsedOptions.threshold diff --git a/packages/cli/src/commands/vector-store/search.ts b/packages/cli/src/commands/vector-store/search.ts index 80002fc..37f9a15 100644 --- a/packages/cli/src/commands/vector-store/search.ts +++ b/packages/cli/src/commands/vector-store/search.ts @@ -13,9 +13,9 @@ import { parseOptions, } from "../../utils/global-options"; import { formatCountWithSuffix, formatOutput } from "../../utils/output"; -import { resolveVectorStore } from "../../utils/vector-store"; +import { resolveStore } from "../../utils/vector-store"; -const SearchVectorStoreSchema = extendGlobalOptions({ +const SearchStoreSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), query: z.string().min(1, { error: '"query" is required' }), topK: z.coerce @@ -34,17 +34,17 @@ const SearchVectorStoreSchema = extendGlobalOptions({ fileSearch: z.boolean().optional(), }); -type ParsedSearchOptions = z.infer & { - vectorStoreIdentifier: string; +type ParsedSearchOptions = z.infer & { + storeIdentifier: string; }; -async function searchVectorStoreFiles( +async function searchStoreFiles( client: Mixedbread, parsedOptions: ParsedSearchOptions ) { - return await client.vectorStores.files.search({ + return await client.stores.files.search({ query: parsedOptions.query, - vector_store_identifiers: [parsedOptions.vectorStoreIdentifier], + store_identifiers: [parsedOptions.storeIdentifier], top_k: parsedOptions.topK, search_options: { return_metadata: parsedOptions.returnMetadata, @@ -54,13 +54,13 @@ async function searchVectorStoreFiles( }); } -async function searchVectorStoreChunks( +async function searchStoreChunks( client: Mixedbread, parsedOptions: ParsedSearchOptions ) { - return await client.vectorStores.search({ + return await client.stores.search({ query: parsedOptions.query, - vector_store_identifiers: [parsedOptions.vectorStoreIdentifier], + store_identifiers: [parsedOptions.storeIdentifier], top_k: parsedOptions.topK, search_options: { return_metadata: parsedOptions.returnMetadata, @@ -97,7 +97,7 @@ export function createSearchCommand(): Command { try { const mergedOptions = mergeCommandOptions(command, options); - const parsedOptions = parseOptions(SearchVectorStoreSchema, { + const parsedOptions = parseOptions(SearchStoreSchema, { ...mergedOptions, nameOrId, query, @@ -105,10 +105,7 @@ export function createSearchCommand(): Command { const client = createClient(parsedOptions); spinner = ora("Searching store...").start(); - const vectorStore = await resolveVectorStore( - client, - parsedOptions.nameOrId - ); + const store = await resolveStore(client, parsedOptions.nameOrId); const config = loadConfig(); // Get default values from config @@ -117,15 +114,15 @@ export function createSearchCommand(): Command { parsedOptions.rerank ?? config.defaults?.search?.rerank ?? false; const results = parsedOptions.fileSearch - ? await searchVectorStoreFiles(client, { + ? await searchStoreFiles(client, { ...parsedOptions, - vectorStoreIdentifier: vectorStore.id, + storeIdentifier: store.id, topK, rerank, }) - : await searchVectorStoreChunks(client, { + : await searchStoreChunks(client, { ...parsedOptions, - vectorStoreIdentifier: vectorStore.id, + storeIdentifier: store.id, topK, rerank, }); @@ -148,7 +145,7 @@ export function createSearchCommand(): Command { const output: Record = { filename: result.filename, score: result.score.toFixed(2), - vector_store_id: result.vector_store_id, + store_id: result.store_id, }; if (!parsedOptions.fileSearch) { diff --git a/packages/cli/src/commands/vector-store/sync.ts b/packages/cli/src/commands/vector-store/sync.ts index 399503d..143b219 100644 --- a/packages/cli/src/commands/vector-store/sync.ts +++ b/packages/cli/src/commands/vector-store/sync.ts @@ -1,4 +1,4 @@ -import type { FileCreateParams } from "@mixedbread/sdk/resources/vector-stores"; +import type { FileCreateParams } from "@mixedbread/sdk/resources/stores"; import chalk from "chalk"; import { Command } from "commander"; import ora from "ora"; @@ -21,9 +21,9 @@ import { formatChangeSummary, } from "../../utils/sync"; import { getSyncedFiles } from "../../utils/sync-state"; -import { resolveVectorStore } from "../../utils/vector-store"; +import { resolveStore } from "../../utils/vector-store"; -const SyncVectorStoreSchema = extendGlobalOptions({ +const SyncStoreSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), patterns: z .array(z.string()) @@ -86,7 +86,7 @@ export function createSyncCommand(): Command { async (nameOrId: string, patterns: string[], options: SyncOptions) => { try { const mergedOptions = mergeCommandOptions(command, options); - const parsedOptions = parseOptions(SyncVectorStoreSchema, { + const parsedOptions = parseOptions(SyncStoreSchema, { ...mergedOptions, nameOrId, patterns, @@ -100,11 +100,8 @@ export function createSyncCommand(): Command { const resolveSpinner = ora( `Looking up store "${parsedOptions.nameOrId}"...` ).start(); - const vectorStore = await resolveVectorStore( - client, - parsedOptions.nameOrId - ); - resolveSpinner.succeed(`Found store: ${vectorStore.name}`); + const store = await resolveStore(client, parsedOptions.nameOrId); + resolveSpinner.succeed(`Found store: ${store.name}`); // Parse metadata if provided const additionalMetadata = validateMetadata(parsedOptions.metadata); @@ -114,7 +111,7 @@ export function createSyncCommand(): Command { const spinner = ora("Loading existing files from store...").start(); - const syncedFiles = await getSyncedFiles(client, vectorStore.id); + const syncedFiles = await getSyncedFiles(client, store.id); spinner.succeed( `Found ${formatCountWithSuffix(syncedFiles.size, "existing file")} in store` @@ -218,7 +215,7 @@ export function createSyncCommand(): Command { // Execute changes const syncResults = await executeSyncChanges( client, - vectorStore.id, + store.id, analysis, { strategy: parsedOptions.strategy, diff --git a/packages/cli/src/commands/vector-store/update.ts b/packages/cli/src/commands/vector-store/update.ts index 4aabde7..55a8f96 100644 --- a/packages/cli/src/commands/vector-store/update.ts +++ b/packages/cli/src/commands/vector-store/update.ts @@ -1,4 +1,4 @@ -import type { VectorStoreUpdateParams } from "@mixedbread/sdk/resources/index"; +import type { StoreUpdateParams } from "@mixedbread/sdk/resources/index"; import chalk from "chalk"; import { Command } from "commander"; import ora, { type Ora } from "ora"; @@ -17,9 +17,9 @@ import { } from "../../utils/global-options"; import { validateMetadata } from "../../utils/metadata"; import { formatOutput } from "../../utils/output"; -import { resolveVectorStore } from "../../utils/vector-store"; +import { resolveStore } from "../../utils/vector-store"; -const UpdateVectorStoreSchema = extendGlobalOptions({ +const UpdateStoreSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), name: z.string().optional(), description: z.string().optional(), @@ -58,21 +58,18 @@ export function createUpdateCommand(): Command { try { const mergedOptions = mergeCommandOptions(command, options); - const parsedOptions = parseOptions(UpdateVectorStoreSchema, { + const parsedOptions = parseOptions(UpdateStoreSchema, { ...mergedOptions, nameOrId, }); const client = createClient(parsedOptions); - const vectorStore = await resolveVectorStore( - client, - parsedOptions.nameOrId - ); + const store = await resolveStore(client, parsedOptions.nameOrId); // Parse metadata if provided const metadata = validateMetadata(parsedOptions.metadata); - const updateData: VectorStoreUpdateParams = {}; + const updateData: StoreUpdateParams = {}; if (parsedOptions.name) updateData.name = parsedOptions.name; if (parsedOptions.description !== undefined) updateData.description = parsedOptions.description; @@ -93,43 +90,36 @@ export function createUpdateCommand(): Command { spinner = ora("Updating store...").start(); - const updatedVectorStore = await client.vectorStores.update( - vectorStore.id, - updateData - ); + const updatedStore = await client.stores.update(store.id, updateData); - spinner.succeed(`Store "${vectorStore.name}" updated successfully`); + spinner.succeed(`Store "${store.name}" updated successfully`); formatOutput( { - id: updatedVectorStore.id, - name: updatedVectorStore.name, - description: updatedVectorStore.description, - expires_after: updatedVectorStore.expires_after, + id: updatedStore.id, + name: updatedStore.name, + description: updatedStore.description, + expires_after: updatedStore.expires_after, metadata: parsedOptions.format === "table" - ? JSON.stringify(updatedVectorStore.metadata, null, 2) - : updatedVectorStore.metadata, - file_counts: updatedVectorStore.file_counts, - status: updatedVectorStore.status, - created_at: updatedVectorStore.created_at, - updated_at: updatedVectorStore.updated_at, - last_active_at: updatedVectorStore.last_active_at, - usage_bytes: updatedVectorStore.usage_bytes, - expires_at: updatedVectorStore.expires_at, + ? JSON.stringify(updatedStore.metadata, null, 2) + : updatedStore.metadata, + file_counts: updatedStore.file_counts, + status: updatedStore.status, + created_at: updatedStore.created_at, + updated_at: updatedStore.updated_at, + last_active_at: updatedStore.last_active_at, + usage_bytes: updatedStore.usage_bytes, + expires_at: updatedStore.expires_at, }, parsedOptions.format ); // Update completion cache if the name was changed - if (vectorStore.name !== updatedVectorStore.name) { + if (store.name !== updatedStore.name) { const keyName = getCurrentKeyName(); if (keyName) { - updateCacheAfterUpdate( - keyName, - vectorStore.name, - updatedVectorStore.name - ); + updateCacheAfterUpdate(keyName, store.name, updatedStore.name); } } } catch (error) { diff --git a/packages/cli/src/commands/vector-store/upload.ts b/packages/cli/src/commands/vector-store/upload.ts index a2aa4ad..e4b1fd8 100644 --- a/packages/cli/src/commands/vector-store/upload.ts +++ b/packages/cli/src/commands/vector-store/upload.ts @@ -1,5 +1,5 @@ import { statSync } from "node:fs"; -import type { FileCreateParams } from "@mixedbread/sdk/resources/vector-stores"; +import type { FileCreateParams } from "@mixedbread/sdk/resources/stores"; import chalk from "chalk"; import { Command } from "commander"; import { glob } from "glob"; @@ -18,12 +18,9 @@ import { uploadFromManifest } from "../../utils/manifest"; import { validateMetadata } from "../../utils/metadata"; import { formatBytes, formatCountWithSuffix } from "../../utils/output"; import { type FileToUpload, uploadFilesInBatch } from "../../utils/upload"; -import { - getVectorStoreFiles, - resolveVectorStore, -} from "../../utils/vector-store"; +import { getStoreFiles, resolveStore } from "../../utils/vector-store"; -const UploadVectorStoreSchema = extendGlobalOptions({ +const UploadStoreSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), patterns: z.array(z.string()).optional(), strategy: z @@ -83,7 +80,7 @@ export function createUploadCommand(): Command { try { const mergedOptions = mergeCommandOptions(command, options); - const parsedOptions = parseOptions(UploadVectorStoreSchema, { + const parsedOptions = parseOptions(UploadStoreSchema, { ...mergedOptions, nameOrId, patterns, @@ -91,10 +88,7 @@ export function createUploadCommand(): Command { const client = createClient(parsedOptions); const spinner = ora("Initializing upload...").start(); - const vectorStore = await resolveVectorStore( - client, - parsedOptions.nameOrId - ); + const store = await resolveStore(client, parsedOptions.nameOrId); const config = loadConfig(); spinner.succeed("Upload initialized"); @@ -103,7 +97,7 @@ export function createUploadCommand(): Command { if (parsedOptions.manifest) { return await uploadFromManifest( client, - vectorStore.id, + store.id, parsedOptions.manifest, parsedOptions ); @@ -186,12 +180,9 @@ export function createUploadCommand(): Command { if (parsedOptions.unique) { const spinner = ora("Checking for existing files...").start(); try { - const vectorStoreFiles = await getVectorStoreFiles( - client, - vectorStore.id - ); + const storeFiles = await getStoreFiles(client, store.id); existingFiles = new Map( - vectorStoreFiles + storeFiles .filter((f) => { const filePath = typeof f.metadata === "object" && @@ -224,7 +215,7 @@ export function createUploadCommand(): Command { })); // Upload files with progress tracking - await uploadFilesInBatch(client, vectorStore.id, filesToUpload, { + await uploadFilesInBatch(client, store.id, filesToUpload, { unique: parsedOptions.unique || false, existingFiles, parallel, diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 68ef3c3..283d775 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,5 +1,5 @@ // Main CLI exports -export { createVectorStoreCommand } from "./commands/vector-store"; +export { createStoreCommand } from "./commands/vector-store"; export { createClient } from "./utils/client"; export { getApiKey, loadConfig, saveConfig } from "./utils/config"; export { formatOutput } from "./utils/output"; diff --git a/packages/cli/src/utils/completion-cache.ts b/packages/cli/src/utils/completion-cache.ts index f2d7427..9bc1c87 100644 --- a/packages/cli/src/utils/completion-cache.ts +++ b/packages/cli/src/utils/completion-cache.ts @@ -94,7 +94,7 @@ export async function refreshCacheForKey( client: Mixedbread ): Promise { try { - const response = await client.vectorStores.list({ + const response = await client.stores.list({ limit: MAX_STORES, }); diff --git a/packages/cli/src/utils/config.ts b/packages/cli/src/utils/config.ts index f79371e..cb7108f 100644 --- a/packages/cli/src/utils/config.ts +++ b/packages/cli/src/utils/config.ts @@ -268,7 +268,7 @@ export function getBaseURL(options?: { baseUrl?: string }): string { ); } -export function resolveVectorStoreName(nameOrAlias: string): string { +export function resolveStoreName(nameOrAlias: string): string { const config = loadConfig(); return config.aliases?.[nameOrAlias] || nameOrAlias; } diff --git a/packages/cli/src/utils/manifest.ts b/packages/cli/src/utils/manifest.ts index 32ff679..feb022e 100644 --- a/packages/cli/src/utils/manifest.ts +++ b/packages/cli/src/utils/manifest.ts @@ -10,7 +10,7 @@ import { loadConfig } from "./config"; import { validateMetadata } from "./metadata"; import { formatBytes, formatCountWithSuffix } from "./output"; import { type FileToUpload, uploadFilesInBatch } from "./upload"; -import { getVectorStoreFiles } from "./vector-store"; +import { getStoreFiles } from "./vector-store"; // Manifest file schema const ManifestFileEntrySchema = z.object({ @@ -38,7 +38,7 @@ type ManifestFile = z.infer; export async function uploadFromManifest( client: Mixedbread, - vectorStoreIdentifier: string, + storeIdentifier: string, manifestPath: string, options: UploadOptions ) { @@ -174,12 +174,9 @@ export async function uploadFromManifest( if (options.unique) { const spinner = ora("Checking for existing files...").start(); try { - const vectorStoreFiles = await getVectorStoreFiles( - client, - vectorStoreIdentifier - ); + const storeFiles = await getStoreFiles(client, storeIdentifier); existingFiles = new Map( - vectorStoreFiles + storeFiles .filter((f) => uniqueFiles.some((file) => { const filePath = @@ -203,7 +200,7 @@ export async function uploadFromManifest( } // Upload files - await uploadFilesInBatch(client, vectorStoreIdentifier, uniqueFiles, { + await uploadFilesInBatch(client, storeIdentifier, uniqueFiles, { unique: options.unique || false, existingFiles, parallel: options.parallel ?? config.defaults.upload.parallel ?? 100, diff --git a/packages/cli/src/utils/sync-state.ts b/packages/cli/src/utils/sync-state.ts index 78bc1b2..bdccd66 100644 --- a/packages/cli/src/utils/sync-state.ts +++ b/packages/cli/src/utils/sync-state.ts @@ -1,5 +1,5 @@ import type Mixedbread from "@mixedbread/sdk"; -import { getVectorStoreFiles } from "./vector-store"; +import { getStoreFiles } from "./vector-store"; export interface FileSyncMetadata { file_path: string; @@ -15,7 +15,7 @@ export interface FileSyncMetadata { */ export async function getSyncedFiles( client: Mixedbread, - vectorStoreIdentifier: string + storeIdentifier: string ): Promise> { const fileMap = new Map< string, @@ -23,12 +23,9 @@ export async function getSyncedFiles( >(); try { - const vectorStoreFiles = await getVectorStoreFiles( - client, - vectorStoreIdentifier - ); + const storeFiles = await getStoreFiles(client, storeIdentifier); - for (const file of vectorStoreFiles) { + for (const file of storeFiles) { // Check if file has sync metadata const metadata = file.metadata as FileSyncMetadata; if (metadata && metadata.synced === true && metadata.file_path) { diff --git a/packages/cli/src/utils/sync.ts b/packages/cli/src/utils/sync.ts index 647ae77..6246e59 100644 --- a/packages/cli/src/utils/sync.ts +++ b/packages/cli/src/utils/sync.ts @@ -2,7 +2,7 @@ import { statSync } from "node:fs"; import fs from "node:fs/promises"; import path from "node:path"; import type Mixedbread from "@mixedbread/sdk"; -import type { FileCreateParams } from "@mixedbread/sdk/resources/vector-stores"; +import type { FileCreateParams } from "@mixedbread/sdk/resources/stores"; import chalk from "chalk"; import { glob } from "glob"; import ora from "ora"; @@ -242,7 +242,7 @@ export function formatChangeSummary(analysis: SyncAnalysis): string { export async function executeSyncChanges( client: Mixedbread, - vectorStoreIdentifier: string, + storeIdentifier: string, analysis: SyncAnalysis, options: { strategy?: FileCreateParams.Experimental["parsing_strategy"]; @@ -285,8 +285,8 @@ export async function executeSyncChanges( `Deleting ${path.relative(process.cwd(), file.path)}` ).start(); try { - await client.vectorStores.files.delete(file.fileId!, { - vector_store_identifier: vectorStoreIdentifier, + await client.stores.files.delete(file.fileId!, { + store_identifier: storeIdentifier, }); completed++; deleteSpinner.succeed( @@ -364,7 +364,7 @@ export async function executeSyncChanges( } // Upload file - await uploadFile(client, vectorStoreIdentifier, file.path, { + await uploadFile(client, storeIdentifier, file.path, { metadata: finalMetadata, strategy: options.strategy, contextualization: options.contextualization, diff --git a/packages/cli/src/utils/upload.ts b/packages/cli/src/utils/upload.ts index e5c3d7a..00c55e8 100644 --- a/packages/cli/src/utils/upload.ts +++ b/packages/cli/src/utils/upload.ts @@ -1,7 +1,7 @@ import { readFileSync, statSync } from "node:fs"; import { basename, relative } from "node:path"; import type Mixedbread from "@mixedbread/sdk"; -import type { FileCreateParams } from "@mixedbread/sdk/resources/vector-stores"; +import type { FileCreateParams } from "@mixedbread/sdk/resources/stores"; import chalk from "chalk"; import { lookup } from "mime-types"; import ora from "ora"; @@ -64,7 +64,7 @@ function fixMimeTypes(file: File): File { */ export async function uploadFile( client: Mixedbread, - vectorStoreIdentifier: string, + storeIdentifier: string, filePath: string, options: UploadFileOptions = {} ): Promise { @@ -79,7 +79,7 @@ export async function uploadFile( ); // Upload the file - await client.vectorStores.files.upload(vectorStoreIdentifier, file, { + await client.stores.files.upload(storeIdentifier, file, { metadata, experimental: { parsing_strategy: strategy, @@ -93,7 +93,7 @@ export async function uploadFile( */ export async function uploadFilesInBatch( client: Mixedbread, - vectorStoreIdentifier: string, + storeIdentifier: string, files: FileToUpload[], options: { unique: boolean; @@ -141,8 +141,8 @@ export async function uploadFilesInBatch( const relativePath = relative(process.cwd(), file.path); if (unique && existingFiles.has(relativePath)) { const existingFileId = existingFiles.get(relativePath); - await client.vectorStores.files.delete(existingFileId, { - vector_store_identifier: vectorStoreIdentifier, + await client.stores.files.delete(existingFileId, { + store_identifier: storeIdentifier, }); } @@ -171,17 +171,13 @@ export async function uploadFilesInBatch( }) ); - await client.vectorStores.files.upload( - vectorStoreIdentifier, - fileToUpload, - { - metadata: fileMetadata, - experimental: { - parsing_strategy: file.strategy, - contextualization: file.contextualization, - }, - } - ); + await client.stores.files.upload(storeIdentifier, fileToUpload, { + metadata: fileMetadata, + experimental: { + parsing_strategy: file.strategy, + contextualization: file.contextualization, + }, + }); if (unique && existingFiles.has(relativePath)) { results.updated++; diff --git a/packages/cli/src/utils/vector-store.ts b/packages/cli/src/utils/vector-store.ts index 9cbb69a..38f39ae 100644 --- a/packages/cli/src/utils/vector-store.ts +++ b/packages/cli/src/utils/vector-store.ts @@ -1,30 +1,30 @@ import type { Mixedbread } from "@mixedbread/sdk"; import type { FileListParams, - VectorStore, - VectorStoreFile, -} from "@mixedbread/sdk/resources/vector-stores"; + Store, + StoreFile, +} from "@mixedbread/sdk/resources/stores"; import chalk from "chalk"; import inquirer from "inquirer"; -import { resolveVectorStoreName } from "./config"; +import { resolveStoreName } from "./config"; -export async function resolveVectorStore( +export async function resolveStore( client: Mixedbread, nameOrId: string, interactive = false -): Promise { +): Promise { // First check if it's an alias - const resolved = resolveVectorStoreName(nameOrId); + const resolved = resolveStoreName(nameOrId); try { - return await client.vectorStores.retrieve(resolved); + return await client.stores.retrieve(resolved); } catch (_error) { // If not found by identifier, fall through to fuzzy search } - const vectorStores = await client.vectorStores.list({ limit: 100 }); + const stores = await client.stores.list({ limit: 100 }); - const fuzzyMatches = vectorStores.data.filter((store) => + const fuzzyMatches = stores.data.filter((store) => store.name.toLowerCase().includes(resolved.toLowerCase()) ); @@ -63,18 +63,18 @@ export async function resolveVectorStore( } } -export async function getVectorStoreFiles( +export async function getStoreFiles( client: Mixedbread, - vectorStoreIdentifier: string -): Promise { - const vectorStoreFiles = []; + storeIdentifier: string +): Promise { + const storeFiles = []; const fileListParams: FileListParams = { limit: 100, }; while (true) { - const response = await client.vectorStores.files.list( - vectorStoreIdentifier, + const response = await client.stores.files.list( + storeIdentifier, fileListParams ); if (response.data.length === 0) { @@ -82,8 +82,8 @@ export async function getVectorStoreFiles( } fileListParams.after = response.pagination.last_cursor; - vectorStoreFiles.push(...response.data); + storeFiles.push(...response.data); } - return vectorStoreFiles; + return storeFiles; } diff --git a/packages/cli/tests/commands/completion.test.ts b/packages/cli/tests/commands/completion.test.ts index b665e88..a092ead 100644 --- a/packages/cli/tests/commands/completion.test.ts +++ b/packages/cli/tests/commands/completion.test.ts @@ -505,7 +505,7 @@ describe("Completion Commands", () => { }); describe("store completions", () => { - const vectorStoreCommands = [ + const storeCommands = [ "create", "delete", "get", @@ -535,7 +535,7 @@ describe("Completion Commands", () => { await parseCommand(command, []); expect(mockLog).toHaveBeenCalledWith( - vectorStoreCommands, + storeCommands, "zsh", console.log ); diff --git a/packages/cli/tests/commands/vector-store/create.test.ts b/packages/cli/tests/commands/vector-store/create.test.ts index 65fd776..cc52559 100644 --- a/packages/cli/tests/commands/vector-store/create.test.ts +++ b/packages/cli/tests/commands/vector-store/create.test.ts @@ -27,8 +27,8 @@ const mockFormatOutput = outputUtils.formatOutput as jest.MockedFunction< describe("Store Create Command", () => { let command: Command; let mockClient: { - vectorStores: { - create: jest.MockedFunction; + stores: { + create: jest.MockedFunction; }; }; @@ -37,7 +37,7 @@ describe("Store Create Command", () => { // Setup mock client mockClient = { - vectorStores: { + stores: { create: jest.fn(), }, }; @@ -63,11 +63,11 @@ describe("Store Create Command", () => { updated_at: "2021-01-01T00:00:00Z", }; - mockClient.vectorStores.create.mockResolvedValue(mockResponse); + mockClient.stores.create.mockResolvedValue(mockResponse); await command.parseAsync(["node", "create", "test-store"]); - expect(mockClient.vectorStores.create).toHaveBeenCalledWith({ + expect(mockClient.stores.create).toHaveBeenCalledWith({ name: "test-store", description: undefined, expires_after: undefined, @@ -102,7 +102,7 @@ describe("Store Create Command", () => { updated_at: "2021-01-01T00:00:00Z", }; - mockClient.vectorStores.create.mockResolvedValue(mockResponse); + mockClient.stores.create.mockResolvedValue(mockResponse); await command.parseAsync([ "node", @@ -112,7 +112,7 @@ describe("Store Create Command", () => { "Test description", ]); - expect(mockClient.vectorStores.create).toHaveBeenCalledWith({ + expect(mockClient.stores.create).toHaveBeenCalledWith({ name: "test-store", description: "Test description", expires_after: undefined, @@ -134,7 +134,7 @@ describe("Store Create Command", () => { updated_at: "2021-01-01T00:00:00Z", }; - mockClient.vectorStores.create.mockResolvedValue(mockResponse); + mockClient.stores.create.mockResolvedValue(mockResponse); await command.parseAsync([ "node", @@ -144,7 +144,7 @@ describe("Store Create Command", () => { "30", ]); - expect(mockClient.vectorStores.create).toHaveBeenCalledWith({ + expect(mockClient.stores.create).toHaveBeenCalledWith({ name: "temp-store", description: undefined, expires_after: { @@ -171,7 +171,7 @@ describe("Store Create Command", () => { updated_at: "2021-01-01T00:00:00Z", }; - mockClient.vectorStores.create.mockResolvedValue(mockResponse); + mockClient.stores.create.mockResolvedValue(mockResponse); await command.parseAsync([ "node", @@ -181,7 +181,7 @@ describe("Store Create Command", () => { '{"project":"website","team":"engineering"}', ]); - expect(mockClient.vectorStores.create).toHaveBeenCalledWith({ + expect(mockClient.stores.create).toHaveBeenCalledWith({ name: "test-store", description: undefined, expires_after: undefined, @@ -228,7 +228,7 @@ describe("Store Create Command", () => { updated_at: "2021-01-01T00:00:00Z", }; - mockClient.vectorStores.create.mockResolvedValue(mockResponse); + mockClient.stores.create.mockResolvedValue(mockResponse); await command.parseAsync([ "node", @@ -238,7 +238,7 @@ describe("Store Create Command", () => { JSON.stringify(complexMetadata), ]); - expect(mockClient.vectorStores.create).toHaveBeenCalledWith({ + expect(mockClient.stores.create).toHaveBeenCalledWith({ name: "test-store", description: undefined, expires_after: undefined, @@ -259,7 +259,7 @@ describe("Store Create Command", () => { updated_at: "2021-01-01T00:00:00Z", }; - mockClient.vectorStores.create.mockResolvedValue(mockResponse); + mockClient.stores.create.mockResolvedValue(mockResponse); await command.parseAsync([ "node", @@ -283,7 +283,7 @@ describe("Store Create Command", () => { updated_at: "2021-01-01T00:00:00Z", }; - mockClient.vectorStores.create.mockResolvedValue(mockResponse); + mockClient.stores.create.mockResolvedValue(mockResponse); await command.parseAsync([ "node", @@ -300,7 +300,7 @@ describe("Store Create Command", () => { describe("Error handling", () => { it("should handle API errors", async () => { const error = new Error("API Error: Unauthorized"); - mockClient.vectorStores.create.mockRejectedValue(error); + mockClient.stores.create.mockRejectedValue(error); await command.parseAsync(["node", "create", "test-store"]); @@ -313,7 +313,7 @@ describe("Store Create Command", () => { it("should handle network errors", async () => { const error = new Error("Network error"); - mockClient.vectorStores.create.mockRejectedValue(error); + mockClient.stores.create.mockRejectedValue(error); await command.parseAsync(["node", "create", "test-store"]); @@ -325,7 +325,7 @@ describe("Store Create Command", () => { }); it("should handle non-Error rejections", async () => { - mockClient.vectorStores.create.mockRejectedValue("Unknown error"); + mockClient.stores.create.mockRejectedValue("Unknown error"); await command.parseAsync(["node", "create", "test-store"]); diff --git a/packages/cli/tests/commands/vector-store/delete.test.ts b/packages/cli/tests/commands/vector-store/delete.test.ts index 2841c0f..8e60a1c 100644 --- a/packages/cli/tests/commands/vector-store/delete.test.ts +++ b/packages/cli/tests/commands/vector-store/delete.test.ts @@ -10,7 +10,7 @@ import type Mixedbread from "@mixedbread/sdk"; import type { Command } from "commander"; import { createDeleteCommand } from "../../../src/commands/vector-store/delete"; import * as clientUtils from "../../../src/utils/client"; -import * as vectorStoreUtils from "../../../src/utils/vector-store"; +import * as storeUtils from "../../../src/utils/vector-store"; // Mock dependencies jest.mock("../../../src/utils/client"); @@ -20,16 +20,15 @@ jest.mock("../../../src/utils/vector-store"); const mockCreateClient = clientUtils.createClient as jest.MockedFunction< typeof clientUtils.createClient >; -const mockResolveVectorStore = - vectorStoreUtils.resolveVectorStore as jest.MockedFunction< - typeof vectorStoreUtils.resolveVectorStore - >; +const mockResolveStore = storeUtils.resolveStore as jest.MockedFunction< + typeof storeUtils.resolveStore +>; describe("Delete Command", () => { let command: Command; let mockClient: { - vectorStores: { - delete: jest.MockedFunction; + stores: { + delete: jest.MockedFunction; }; }; @@ -37,14 +36,14 @@ describe("Delete Command", () => { command = createDeleteCommand(); mockClient = { - vectorStores: { + stores: { delete: jest.fn(), }, }; // Setup default mocks mockCreateClient.mockReturnValue(mockClient as unknown as Mixedbread); - mockResolveVectorStore.mockResolvedValue({ + mockResolveStore.mockResolvedValue({ id: "550e8400-e29b-41d4-a716-446655440040", name: "test-store", created_at: "2021-01-01", @@ -58,20 +57,20 @@ describe("Delete Command", () => { describe("Basic deletion", () => { it("should delete store with yes flag", async () => { - mockClient.vectorStores.delete.mockResolvedValue({ + mockClient.stores.delete.mockResolvedValue({ id: "550e8400-e29b-41d4-a716-446655440040", deleted: true, }); await command.parseAsync(["node", "delete", "test-store", "--yes"]); - expect(mockResolveVectorStore).toHaveBeenCalledWith( + expect(mockResolveStore).toHaveBeenCalledWith( expect.objectContaining({ - vectorStores: expect.any(Object), + stores: expect.any(Object), }), "test-store" ); - expect(mockClient.vectorStores.delete).toHaveBeenCalledWith( + expect(mockClient.stores.delete).toHaveBeenCalledWith( "550e8400-e29b-41d4-a716-446655440040" ); expect(console.log).toHaveBeenCalledWith( @@ -81,14 +80,14 @@ describe("Delete Command", () => { }); it("should skip confirmation when yes flag is used", async () => { - mockClient.vectorStores.delete.mockResolvedValue({ + mockClient.stores.delete.mockResolvedValue({ id: "550e8400-e29b-41d4-a716-446655440040", deleted: true, }); await command.parseAsync(["node", "delete", "test-store", "--yes"]); - expect(mockClient.vectorStores.delete).toHaveBeenCalledWith( + expect(mockClient.stores.delete).toHaveBeenCalledWith( "550e8400-e29b-41d4-a716-446655440040" ); }); @@ -97,7 +96,7 @@ describe("Delete Command", () => { describe("Error handling", () => { it("should handle API errors", async () => { const error = new Error("API Error: Unauthorized"); - mockClient.vectorStores.delete.mockRejectedValue(error); + mockClient.stores.delete.mockRejectedValue(error); await command.parseAsync(["node", "delete", "test-store", "--yes"]); @@ -110,7 +109,7 @@ describe("Delete Command", () => { it("should handle store resolution errors", async () => { const error = new Error("Store not found"); - mockResolveVectorStore.mockRejectedValue(error); + mockResolveStore.mockRejectedValue(error); await command.parseAsync([ "node", @@ -127,7 +126,7 @@ describe("Delete Command", () => { }); it("should handle non-Error rejections", async () => { - mockClient.vectorStores.delete.mockRejectedValue("Unknown error"); + mockClient.stores.delete.mockRejectedValue("Unknown error"); await command.parseAsync(["node", "delete", "test-store", "--yes"]); @@ -141,7 +140,7 @@ describe("Delete Command", () => { describe("Global options", () => { it("should support API key option", async () => { - mockClient.vectorStores.delete.mockResolvedValue({ + mockClient.stores.delete.mockResolvedValue({ id: "550e8400-e29b-41d4-a716-446655440040", deleted: true, }); diff --git a/packages/cli/tests/commands/vector-store/files.test.ts b/packages/cli/tests/commands/vector-store/files.test.ts index 7021d41..f5d0747 100644 --- a/packages/cli/tests/commands/vector-store/files.test.ts +++ b/packages/cli/tests/commands/vector-store/files.test.ts @@ -7,13 +7,13 @@ import { jest, } from "@jest/globals"; import type Mixedbread from "@mixedbread/sdk"; -import type { VectorStoreFile } from "@mixedbread/sdk/resources/vector-stores"; -import type { FileListResponse } from "@mixedbread/sdk/resources/vector-stores/files"; +import type { StoreFile } from "@mixedbread/sdk/resources/stores"; +import type { FileListResponse } from "@mixedbread/sdk/resources/stores/files"; import type { Command } from "commander"; import { createFilesCommand } from "../../../src/commands/vector-store/files"; import * as clientUtils from "../../../src/utils/client"; import * as outputUtils from "../../../src/utils/output"; -import * as vectorStoreUtils from "../../../src/utils/vector-store"; +import * as storeUtils from "../../../src/utils/vector-store"; // Mock dependencies jest.mock("../../../src/utils/client"); @@ -27,16 +27,15 @@ jest.mock("../../../src/utils/output", () => ({ const mockCreateClient = clientUtils.createClient as jest.MockedFunction< typeof clientUtils.createClient >; -const mockResolveVectorStore = - vectorStoreUtils.resolveVectorStore as jest.MockedFunction< - typeof vectorStoreUtils.resolveVectorStore - >; +const mockResolveStore = storeUtils.resolveStore as jest.MockedFunction< + typeof storeUtils.resolveStore +>; const mockFormatOutput = outputUtils.formatOutput as jest.MockedFunction< typeof outputUtils.formatOutput >; const createMockCursor = ( - data: VectorStoreFile[], + data: StoreFile[], pagination: FileListResponse.Pagination ): FileListResponse => { return { @@ -48,15 +47,13 @@ const createMockCursor = ( describe("Files Command", () => { let command: Command; let mockClient: { - vectorStores: { + stores: { files: { - list: jest.MockedFunction; + list: jest.MockedFunction; retrieve: jest.MockedFunction< - Mixedbread["vectorStores"]["files"]["retrieve"] - >; - delete: jest.MockedFunction< - Mixedbread["vectorStores"]["files"]["delete"] + Mixedbread["stores"]["files"]["retrieve"] >; + delete: jest.MockedFunction; }; }; }; @@ -64,7 +61,7 @@ describe("Files Command", () => { beforeEach(() => { command = createFilesCommand(); mockClient = { - vectorStores: { + stores: { files: { list: jest.fn(), retrieve: jest.fn(), @@ -75,7 +72,7 @@ describe("Files Command", () => { // Setup default mocks mockCreateClient.mockReturnValue(mockClient as unknown as Mixedbread); - mockResolveVectorStore.mockResolvedValue({ + mockResolveStore.mockResolvedValue({ id: "550e8400-e29b-41d4-a716-446655440070", name: "test-store", created_at: "2021-01-01", @@ -96,7 +93,7 @@ describe("Files Command", () => { status: "completed" as const, usage_bytes: 1048576, created_at: "2024-01-01T00:00:00Z", - vector_store_id: "550e8400-e29b-41d4-a716-446655440070", + store_id: "550e8400-e29b-41d4-a716-446655440070", }, { id: "file_2", @@ -104,12 +101,12 @@ describe("Files Command", () => { status: "in_progress" as const, usage_bytes: 2048, created_at: "2024-01-02T00:00:00Z", - vector_store_id: "550e8400-e29b-41d4-a716-446655440070", + store_id: "550e8400-e29b-41d4-a716-446655440070", }, ]; it("should list all files by default", async () => { - mockClient.vectorStores.files.list.mockResolvedValue( + mockClient.stores.files.list.mockResolvedValue( createMockCursor(mockFiles, { first_cursor: "123", last_cursor: "456", @@ -119,13 +116,13 @@ describe("Files Command", () => { await command.parseAsync(["node", "files", "list", "test-store"]); - expect(mockResolveVectorStore).toHaveBeenCalledWith( + expect(mockResolveStore).toHaveBeenCalledWith( expect.objectContaining({ - vectorStores: expect.any(Object), + stores: expect.any(Object), }), "test-store" ); - expect(mockClient.vectorStores.files.list).toHaveBeenCalledWith( + expect(mockClient.stores.files.list).toHaveBeenCalledWith( "550e8400-e29b-41d4-a716-446655440070", { limit: 10 } ); @@ -151,7 +148,7 @@ describe("Files Command", () => { }); it("should filter files by status", async () => { - mockClient.vectorStores.files.list.mockResolvedValue( + mockClient.stores.files.list.mockResolvedValue( createMockCursor(mockFiles, { first_cursor: "123", last_cursor: "456", @@ -177,7 +174,7 @@ describe("Files Command", () => { }); it("should handle custom limit", async () => { - mockClient.vectorStores.files.list.mockResolvedValue( + mockClient.stores.files.list.mockResolvedValue( createMockCursor(mockFiles, { first_cursor: "123", last_cursor: "456", @@ -194,14 +191,14 @@ describe("Files Command", () => { "50", ]); - expect(mockClient.vectorStores.files.list).toHaveBeenCalledWith( + expect(mockClient.stores.files.list).toHaveBeenCalledWith( "550e8400-e29b-41d4-a716-446655440070", { limit: 50 } ); }); it("should handle empty results", async () => { - mockClient.vectorStores.files.list.mockResolvedValue( + mockClient.stores.files.list.mockResolvedValue( createMockCursor([], { first_cursor: null, last_cursor: null, @@ -218,7 +215,7 @@ describe("Files Command", () => { }); it("should support output formatting", async () => { - mockClient.vectorStores.files.list.mockResolvedValue( + mockClient.stores.files.list.mockResolvedValue( createMockCursor(mockFiles, { first_cursor: "123", last_cursor: "456", @@ -283,11 +280,11 @@ describe("Files Command", () => { usage_bytes: 2097152, created_at: "2024-01-01T12:00:00Z", metadata: { author: "John Doe", version: "1.0" }, - vector_store_id: "550e8400-e29b-41d4-a716-446655440070", + store_id: "550e8400-e29b-41d4-a716-446655440070", }; it("should get file details", async () => { - mockClient.vectorStores.files.retrieve.mockResolvedValue(mockFile); + mockClient.stores.files.retrieve.mockResolvedValue(mockFile); await command.parseAsync([ "node", @@ -297,16 +294,16 @@ describe("Files Command", () => { "file_123", ]); - expect(mockResolveVectorStore).toHaveBeenCalledWith( + expect(mockResolveStore).toHaveBeenCalledWith( expect.objectContaining({ - vectorStores: expect.any(Object), + stores: expect.any(Object), }), "test-store" ); - expect(mockClient.vectorStores.files.retrieve).toHaveBeenCalledWith( + expect(mockClient.stores.files.retrieve).toHaveBeenCalledWith( "file_123", { - vector_store_identifier: "550e8400-e29b-41d4-a716-446655440070", + store_identifier: "550e8400-e29b-41d4-a716-446655440070", } ); expect(mockFormatOutput).toHaveBeenCalledWith( @@ -326,11 +323,9 @@ describe("Files Command", () => { const fileWithoutMetadata = { ...mockFile, metadata: {}, - vector_store_id: "550e8400-e29b-41d4-a716-446655440070", + store_id: "550e8400-e29b-41d4-a716-446655440070", }; - mockClient.vectorStores.files.retrieve.mockResolvedValue( - fileWithoutMetadata - ); + mockClient.stores.files.retrieve.mockResolvedValue(fileWithoutMetadata); await command.parseAsync([ "node", @@ -345,7 +340,7 @@ describe("Files Command", () => { }); it("should support output formatting", async () => { - mockClient.vectorStores.files.retrieve.mockResolvedValue(mockFile); + mockClient.stores.files.retrieve.mockResolvedValue(mockFile); await command.parseAsync([ "node", @@ -362,7 +357,7 @@ describe("Files Command", () => { it("should handle API errors", async () => { const error = new Error("File not found"); - mockClient.vectorStores.files.retrieve.mockRejectedValue(error); + mockClient.stores.files.retrieve.mockRejectedValue(error); await command.parseAsync([ "node", @@ -382,7 +377,7 @@ describe("Files Command", () => { describe("Delete file subcommand", () => { it("should delete file with yes flag", async () => { - mockClient.vectorStores.files.delete.mockResolvedValue({ + mockClient.stores.files.delete.mockResolvedValue({ id: "file_123", }); @@ -395,18 +390,15 @@ describe("Files Command", () => { "--yes", ]); - expect(mockResolveVectorStore).toHaveBeenCalledWith( + expect(mockResolveStore).toHaveBeenCalledWith( expect.objectContaining({ - vectorStores: expect.any(Object), + stores: expect.any(Object), }), "test-store" ); - expect(mockClient.vectorStores.files.delete).toHaveBeenCalledWith( - "file_123", - { - vector_store_identifier: "550e8400-e29b-41d4-a716-446655440070", - } - ); + expect(mockClient.stores.files.delete).toHaveBeenCalledWith("file_123", { + store_identifier: "550e8400-e29b-41d4-a716-446655440070", + }); expect(console.log).toHaveBeenCalledWith( expect.any(String), expect.stringContaining("File file_123 deleted successfully") @@ -415,7 +407,7 @@ describe("Files Command", () => { it("should handle API errors", async () => { const error = new Error("API Error: Unauthorized"); - mockClient.vectorStores.files.delete.mockRejectedValue(error); + mockClient.stores.files.delete.mockRejectedValue(error); await command.parseAsync([ "node", @@ -434,7 +426,7 @@ describe("Files Command", () => { }); it("should handle non-Error rejections", async () => { - mockClient.vectorStores.files.delete.mockRejectedValue("Unknown error"); + mockClient.stores.files.delete.mockRejectedValue("Unknown error"); await command.parseAsync([ "node", @@ -500,12 +492,12 @@ describe("Files Command", () => { status: "completed" as const, usage_bytes: 1024, created_at: "2024-01-01T00:00:00Z", - vector_store_id: "550e8400-e29b-41d4-a716-446655440070", + store_id: "550e8400-e29b-41d4-a716-446655440070", }, ]; it("should support API key option", async () => { - mockClient.vectorStores.files.list.mockResolvedValue( + mockClient.stores.files.list.mockResolvedValue( createMockCursor(mockFiles, { first_cursor: "123", last_cursor: "456", @@ -533,7 +525,7 @@ describe("Files Command", () => { describe("Error handling", () => { it("should handle store resolution errors", async () => { const error = new Error("Store not found"); - mockResolveVectorStore.mockRejectedValue(error); + mockResolveStore.mockRejectedValue(error); await command.parseAsync(["node", "files", "list", "nonexistent-store"]); diff --git a/packages/cli/tests/commands/vector-store/get.test.ts b/packages/cli/tests/commands/vector-store/get.test.ts index a3deff5..f8c0721 100644 --- a/packages/cli/tests/commands/vector-store/get.test.ts +++ b/packages/cli/tests/commands/vector-store/get.test.ts @@ -11,7 +11,7 @@ import type { Command } from "commander"; import { createGetCommand } from "../../../src/commands/vector-store/get"; import * as clientUtils from "../../../src/utils/client"; import * as outputUtils from "../../../src/utils/output"; -import * as vectorStoreUtils from "../../../src/utils/vector-store"; +import * as storeUtils from "../../../src/utils/vector-store"; // Mock dependencies jest.mock("../../../src/utils/client"); @@ -25,10 +25,9 @@ jest.mock("../../../src/utils/output", () => ({ const mockCreateClient = clientUtils.createClient as jest.MockedFunction< typeof clientUtils.createClient >; -const mockResolveVectorStore = - vectorStoreUtils.resolveVectorStore as jest.MockedFunction< - typeof vectorStoreUtils.resolveVectorStore - >; +const mockResolveStore = storeUtils.resolveStore as jest.MockedFunction< + typeof storeUtils.resolveStore +>; const mockFormatOutput = outputUtils.formatOutput as jest.MockedFunction< typeof outputUtils.formatOutput >; @@ -36,8 +35,8 @@ const mockFormatOutput = outputUtils.formatOutput as jest.MockedFunction< describe("Store Get Command", () => { let command: Command; let mockClient: { - vectorStores: { - retrieve: jest.MockedFunction; + stores: { + retrieve: jest.MockedFunction; }; }; @@ -46,14 +45,14 @@ describe("Store Get Command", () => { // Setup mock client mockClient = { - vectorStores: { + stores: { retrieve: jest.fn(), }, }; // Setup default mocks mockCreateClient.mockReturnValue(mockClient as unknown as Mixedbread); - mockResolveVectorStore.mockResolvedValue({ + mockResolveStore.mockResolvedValue({ id: "550e8400-e29b-41d4-a716-446655440050", name: "test-store", created_at: "2021-01-01", @@ -68,7 +67,7 @@ describe("Store Get Command", () => { describe("Basic retrieval", () => { it("should get store details", async () => { - mockResolveVectorStore.mockResolvedValue({ + mockResolveStore.mockResolvedValue({ id: "550e8400-e29b-41d4-a716-446655440050", name: "test-store", description: "A test store", @@ -82,9 +81,9 @@ describe("Store Get Command", () => { await command.parseAsync(["node", "get", "test-store"]); - expect(mockResolveVectorStore).toHaveBeenCalledWith( + expect(mockResolveStore).toHaveBeenCalledWith( expect.objectContaining({ - vectorStores: expect.any(Object), + stores: expect.any(Object), }), "test-store" ); @@ -110,7 +109,7 @@ describe("Store Get Command", () => { const expiredDate = new Date(); expiredDate.setDate(expiredDate.getDate() - 1); // Yesterday - mockResolveVectorStore.mockResolvedValue({ + mockResolveStore.mockResolvedValue({ id: "550e8400-e29b-41d4-a716-446655440050", name: "test-store", description: "An expired store", @@ -129,7 +128,7 @@ describe("Store Get Command", () => { }); it("should handle missing optional fields", async () => { - mockResolveVectorStore.mockResolvedValue({ + mockResolveStore.mockResolvedValue({ id: "550e8400-e29b-41d4-a716-446655440050", name: "test-store", created_at: "2024-01-01T00:00:00Z", @@ -156,7 +155,7 @@ describe("Store Get Command", () => { }); describe("Output formatting", () => { - const mockVectorStore = { + const mockStore = { id: "550e8400-e29b-41d4-a716-446655440050", name: "test-store", description: "A test store", @@ -170,10 +169,10 @@ describe("Store Get Command", () => { it("should format as table by default", async () => { ( - vectorStoreUtils.resolveVectorStore as jest.MockedFunction< - typeof vectorStoreUtils.resolveVectorStore + storeUtils.resolveStore as jest.MockedFunction< + typeof storeUtils.resolveStore > - ).mockResolvedValue(mockVectorStore); + ).mockResolvedValue(mockStore); await command.parseAsync(["node", "get", "test-store"]); @@ -185,10 +184,10 @@ describe("Store Get Command", () => { it("should format as JSON when specified", async () => { ( - vectorStoreUtils.resolveVectorStore as jest.MockedFunction< - typeof vectorStoreUtils.resolveVectorStore + storeUtils.resolveStore as jest.MockedFunction< + typeof storeUtils.resolveStore > - ).mockResolvedValue(mockVectorStore); + ).mockResolvedValue(mockStore); await command.parseAsync([ "node", @@ -203,10 +202,10 @@ describe("Store Get Command", () => { it("should format as CSV when specified", async () => { ( - vectorStoreUtils.resolveVectorStore as jest.MockedFunction< - typeof vectorStoreUtils.resolveVectorStore + storeUtils.resolveStore as jest.MockedFunction< + typeof storeUtils.resolveStore > - ).mockResolvedValue(mockVectorStore); + ).mockResolvedValue(mockStore); await command.parseAsync([ "node", @@ -223,7 +222,7 @@ describe("Store Get Command", () => { describe("Error handling", () => { it("should handle store resolution errors", async () => { const error = new Error("Store not found"); - mockResolveVectorStore.mockRejectedValue(error); + mockResolveStore.mockRejectedValue(error); await command.parseAsync(["node", "get", "nonexistent-store"]); @@ -235,7 +234,7 @@ describe("Store Get Command", () => { }); it("should handle non-Error rejections", async () => { - mockResolveVectorStore.mockRejectedValue("Unknown error"); + mockResolveStore.mockRejectedValue("Unknown error"); await command.parseAsync(["node", "get", "test-store"]); @@ -248,7 +247,7 @@ describe("Store Get Command", () => { }); describe("Global options", () => { - const mockVectorStore = { + const mockStore = { id: "550e8400-e29b-41d4-a716-446655440050", name: "test-store", description: "A test store", @@ -262,10 +261,10 @@ describe("Store Get Command", () => { it("should support API key option", async () => { ( - vectorStoreUtils.resolveVectorStore as jest.MockedFunction< - typeof vectorStoreUtils.resolveVectorStore + storeUtils.resolveStore as jest.MockedFunction< + typeof storeUtils.resolveStore > - ).mockResolvedValue(mockVectorStore); + ).mockResolvedValue(mockStore); await command.parseAsync([ "node", diff --git a/packages/cli/tests/commands/vector-store/list.test.ts b/packages/cli/tests/commands/vector-store/list.test.ts index a0c3d0b..0758409 100644 --- a/packages/cli/tests/commands/vector-store/list.test.ts +++ b/packages/cli/tests/commands/vector-store/list.test.ts @@ -8,7 +8,7 @@ import { } from "@jest/globals"; import type Mixedbread from "@mixedbread/sdk"; import type { CursorResponse } from "@mixedbread/sdk/core/pagination.mjs"; -import type { VectorStore } from "@mixedbread/sdk/resources/index.mjs"; +import type { Store } from "@mixedbread/sdk/resources/index.mjs"; import type { Command } from "commander"; import { createListCommand } from "../../../src/commands/vector-store/list"; import * as clientUtils from "../../../src/utils/client"; @@ -33,21 +33,21 @@ const mockFormatOutput = outputUtils.formatOutput as jest.MockedFunction< // Since the Cursor class has private fields, we need to use type assertion // but we avoid 'any' by being specific about what we're mocking const createMockCursor = ( - data: VectorStore[], + data: Store[], pagination: CursorResponse.Pagination -): CursorResponse => { +): CursorResponse => { return { data, pagination, - } as unknown as CursorResponse; + } as unknown as CursorResponse; }; describe("Store List Command", () => { let command: Command; let mockClient: { - vectorStores: { + stores: { list: jest.MockedFunction< - (options: { limit?: number }) => Promise> + (options: { limit?: number }) => Promise> >; }; }; @@ -57,7 +57,7 @@ describe("Store List Command", () => { // Setup mock client mockClient = { - vectorStores: { + stores: { list: jest.fn(), }, }; @@ -94,7 +94,7 @@ describe("Store List Command", () => { }, ]; - mockClient.vectorStores.list.mockResolvedValue( + mockClient.stores.list.mockResolvedValue( createMockCursor(mockData, { first_cursor: "123", last_cursor: "456", @@ -105,7 +105,7 @@ describe("Store List Command", () => { await command.parseAsync(["node", "list"]); - expect(mockClient.vectorStores.list).toHaveBeenCalledWith({ + expect(mockClient.stores.list).toHaveBeenCalledWith({ limit: 10, }); @@ -155,7 +155,7 @@ describe("Store List Command", () => { }, ]; - mockClient.vectorStores.list.mockResolvedValue( + mockClient.stores.list.mockResolvedValue( createMockCursor(mockData, { first_cursor: "123", last_cursor: "456", @@ -174,7 +174,7 @@ describe("Store List Command", () => { }); it("should handle empty results", async () => { - mockClient.vectorStores.list.mockResolvedValue( + mockClient.stores.list.mockResolvedValue( createMockCursor([], { first_cursor: "123", last_cursor: "456", @@ -192,7 +192,7 @@ describe("Store List Command", () => { describe("Pagination", () => { it("should handle custom limit", async () => { - mockClient.vectorStores.list.mockResolvedValue( + mockClient.stores.list.mockResolvedValue( createMockCursor([], { first_cursor: "123", last_cursor: "456", @@ -203,7 +203,7 @@ describe("Store List Command", () => { await command.parseAsync(["node", "list", "--limit", "50"]); - expect(mockClient.vectorStores.list).toHaveBeenCalledWith({ + expect(mockClient.stores.list).toHaveBeenCalledWith({ limit: 50, }); }); @@ -233,7 +233,7 @@ describe("Store List Command", () => { ]; it("should format as table by default", async () => { - mockClient.vectorStores.list.mockResolvedValue( + mockClient.stores.list.mockResolvedValue( createMockCursor(mockData, { first_cursor: "123", last_cursor: "456", @@ -251,7 +251,7 @@ describe("Store List Command", () => { }); it("should format as JSON when specified", async () => { - mockClient.vectorStores.list.mockResolvedValue( + mockClient.stores.list.mockResolvedValue( createMockCursor(mockData, { first_cursor: "123", last_cursor: "456", @@ -266,7 +266,7 @@ describe("Store List Command", () => { }); it("should format as CSV when specified", async () => { - mockClient.vectorStores.list.mockResolvedValue( + mockClient.stores.list.mockResolvedValue( createMockCursor(mockData, { first_cursor: "123", last_cursor: "456", @@ -284,7 +284,7 @@ describe("Store List Command", () => { describe("Error handling", () => { it("should handle API errors", async () => { const error = new Error("API Error: Rate limit exceeded"); - mockClient.vectorStores.list.mockRejectedValue(error); + mockClient.stores.list.mockRejectedValue(error); await command.parseAsync(["node", "list"]); @@ -297,7 +297,7 @@ describe("Store List Command", () => { it("should handle network errors", async () => { const error = new Error("ECONNREFUSED"); - mockClient.vectorStores.list.mockRejectedValue(error); + mockClient.stores.list.mockRejectedValue(error); await command.parseAsync(["node", "list"]); @@ -309,7 +309,7 @@ describe("Store List Command", () => { }); it("should handle non-Error rejections", async () => { - mockClient.vectorStores.list.mockRejectedValue("Unknown error"); + mockClient.stores.list.mockRejectedValue("Unknown error"); await command.parseAsync(["node", "list"]); @@ -323,7 +323,7 @@ describe("Store List Command", () => { describe("API key handling", () => { it("should use API key from command line", async () => { - mockClient.vectorStores.list.mockResolvedValue( + mockClient.stores.list.mockResolvedValue( createMockCursor([], { first_cursor: "123", last_cursor: "456", @@ -342,7 +342,7 @@ describe("Store List Command", () => { }); it("should work without explicit API key (uses env/config)", async () => { - mockClient.vectorStores.list.mockResolvedValue( + mockClient.stores.list.mockResolvedValue( createMockCursor([], { first_cursor: "123", last_cursor: "456", @@ -359,7 +359,7 @@ describe("Store List Command", () => { describe("Base URL handling", () => { it("should pass base URL from command line to createClient", async () => { - mockClient.vectorStores.list.mockResolvedValue( + mockClient.stores.list.mockResolvedValue( createMockCursor([], { first_cursor: "123", last_cursor: "456", @@ -383,7 +383,7 @@ describe("Store List Command", () => { }); it("should validate base URL is a valid URL", async () => { - mockClient.vectorStores.list.mockResolvedValue( + mockClient.stores.list.mockResolvedValue( createMockCursor([], { first_cursor: "123", last_cursor: "456", @@ -407,7 +407,7 @@ describe("Store List Command", () => { }); it("should work with base URL and API key together", async () => { - mockClient.vectorStores.list.mockResolvedValue( + mockClient.stores.list.mockResolvedValue( createMockCursor([], { first_cursor: "123", last_cursor: "456", @@ -437,7 +437,7 @@ describe("Store List Command", () => { const originalEnv = process.env.MXBAI_BASE_URL; process.env.MXBAI_BASE_URL = "https://env-api.example.com"; - mockClient.vectorStores.list.mockResolvedValue( + mockClient.stores.list.mockResolvedValue( createMockCursor([], { first_cursor: "123", last_cursor: "456", @@ -481,7 +481,7 @@ describe("Store List Command", () => { }, ]; - mockClient.vectorStores.list.mockResolvedValue( + mockClient.stores.list.mockResolvedValue( createMockCursor(mockData, { first_cursor: "123", last_cursor: "456", @@ -517,7 +517,7 @@ describe("Store List Command", () => { }, ]; - mockClient.vectorStores.list.mockResolvedValue( + mockClient.stores.list.mockResolvedValue( createMockCursor(mockData, { first_cursor: "123", last_cursor: "456", diff --git a/packages/cli/tests/commands/vector-store/qa.test.ts b/packages/cli/tests/commands/vector-store/qa.test.ts index 435e33c..d4f9a78 100644 --- a/packages/cli/tests/commands/vector-store/qa.test.ts +++ b/packages/cli/tests/commands/vector-store/qa.test.ts @@ -7,13 +7,13 @@ import { jest, } from "@jest/globals"; import type Mixedbread from "@mixedbread/sdk"; -import type { VectorStoreQuestionAnsweringResponse } from "@mixedbread/sdk/resources"; +import type { StoreQuestionAnsweringResponse } from "@mixedbread/sdk/resources"; import type { Command } from "commander"; import { createQACommand } from "../../../src/commands/vector-store/qa"; import * as clientUtils from "../../../src/utils/client"; import * as configUtils from "../../../src/utils/config"; import * as outputUtils from "../../../src/utils/output"; -import * as vectorStoreUtils from "../../../src/utils/vector-store"; +import * as storeUtils from "../../../src/utils/vector-store"; // Mock dependencies jest.mock("../../../src/utils/client"); @@ -25,10 +25,9 @@ jest.mock("../../../src/utils/output", () => ({ jest.mock("../../../src/utils/config"); // Explicit mock definitions -const mockResolveVectorStore = - vectorStoreUtils.resolveVectorStore as jest.MockedFunction< - typeof vectorStoreUtils.resolveVectorStore - >; +const mockResolveStore = storeUtils.resolveStore as jest.MockedFunction< + typeof storeUtils.resolveStore +>; const mockCreateClient = clientUtils.createClient as jest.MockedFunction< typeof clientUtils.createClient >; @@ -42,9 +41,9 @@ const mockFormatOutput = outputUtils.formatOutput as jest.MockedFunction< describe("QA Command", () => { let command: Command; let mockClient: { - vectorStores: { + stores: { questionAnswering: jest.MockedFunction< - Mixedbread["vectorStores"]["questionAnswering"] + Mixedbread["stores"]["questionAnswering"] >; }; }; @@ -52,14 +51,14 @@ describe("QA Command", () => { beforeEach(() => { command = createQACommand(); mockClient = { - vectorStores: { + stores: { questionAnswering: jest.fn(), }, }; // Setup default mocks mockCreateClient.mockReturnValue(mockClient as unknown as Mixedbread); - mockResolveVectorStore.mockResolvedValue({ + mockResolveStore.mockResolvedValue({ id: "550e8400-e29b-41d4-a716-446655440090", name: "test-store", created_at: "2021-01-01", @@ -81,7 +80,7 @@ describe("QA Command", () => { }); describe("Basic question answering", () => { - const mockQAResponse: VectorStoreQuestionAnsweringResponse = { + const mockQAResponse: StoreQuestionAnsweringResponse = { answer: "Machine learning is a subset of artificial intelligence that focuses on algorithms that can learn from data.", sources: [ @@ -91,7 +90,7 @@ describe("QA Command", () => { chunk_index: 2, metadata: { chapter: "Introduction" }, file_id: "123", - vector_store_id: "456", + store_id: "456", type: "text", text: "Machine learning is a subset of artificial intelligence that focuses on algorithms that can learn from data.", }, @@ -101,7 +100,7 @@ describe("QA Command", () => { chunk_index: 0, metadata: { section: "Definitions" }, file_id: "123", - vector_store_id: "456", + store_id: "456", type: "text", text: "Artificial intelligence (AI) is the simulation of human intelligence in machines that are programmed to think like humans and mimic their actions.", }, @@ -109,9 +108,7 @@ describe("QA Command", () => { }; it("should ask question with default options", async () => { - mockClient.vectorStores.questionAnswering.mockResolvedValue( - mockQAResponse - ); + mockClient.stores.questionAnswering.mockResolvedValue(mockQAResponse); await command.parseAsync([ "node", @@ -120,15 +117,15 @@ describe("QA Command", () => { "What is machine learning?", ]); - expect(mockResolveVectorStore).toHaveBeenCalledWith( + expect(mockResolveStore).toHaveBeenCalledWith( expect.objectContaining({ - vectorStores: expect.any(Object), + stores: expect.any(Object), }), "test-store" ); - expect(mockClient.vectorStores.questionAnswering).toHaveBeenCalledWith({ + expect(mockClient.stores.questionAnswering).toHaveBeenCalledWith({ query: "What is machine learning?", - vector_store_identifiers: ["550e8400-e29b-41d4-a716-446655440090"], + store_identifiers: ["550e8400-e29b-41d4-a716-446655440090"], top_k: 5, search_options: { score_threshold: undefined, @@ -162,9 +159,7 @@ describe("QA Command", () => { }); it("should ask question with custom top-k", async () => { - mockClient.vectorStores.questionAnswering.mockResolvedValue( - mockQAResponse - ); + mockClient.stores.questionAnswering.mockResolvedValue(mockQAResponse); await command.parseAsync([ "node", @@ -175,7 +170,7 @@ describe("QA Command", () => { "15", ]); - expect(mockClient.vectorStores.questionAnswering).toHaveBeenCalledWith( + expect(mockClient.stores.questionAnswering).toHaveBeenCalledWith( expect.objectContaining({ top_k: 15, }) @@ -183,9 +178,7 @@ describe("QA Command", () => { }); it("should ask question with threshold", async () => { - mockClient.vectorStores.questionAnswering.mockResolvedValue( - mockQAResponse - ); + mockClient.stores.questionAnswering.mockResolvedValue(mockQAResponse); await command.parseAsync([ "node", @@ -196,7 +189,7 @@ describe("QA Command", () => { "0.8", ]); - expect(mockClient.vectorStores.questionAnswering).toHaveBeenCalledWith( + expect(mockClient.stores.questionAnswering).toHaveBeenCalledWith( expect.objectContaining({ search_options: expect.objectContaining({ score_threshold: 0.8, @@ -206,9 +199,7 @@ describe("QA Command", () => { }); it("should ask question with return metadata enabled", async () => { - mockClient.vectorStores.questionAnswering.mockResolvedValue( - mockQAResponse - ); + mockClient.stores.questionAnswering.mockResolvedValue(mockQAResponse); await command.parseAsync([ "node", @@ -218,7 +209,7 @@ describe("QA Command", () => { "--return-metadata", ]); - expect(mockClient.vectorStores.questionAnswering).toHaveBeenCalledWith( + expect(mockClient.stores.questionAnswering).toHaveBeenCalledWith( expect.objectContaining({ search_options: expect.objectContaining({ return_metadata: true, @@ -237,7 +228,7 @@ describe("QA Command", () => { sources: [], }; - mockClient.vectorStores.questionAnswering.mockResolvedValue( + mockClient.stores.questionAnswering.mockResolvedValue( responseWithoutSources ); @@ -263,7 +254,7 @@ describe("QA Command", () => { answer: "The answer to your question.", }; - mockClient.vectorStores.questionAnswering.mockResolvedValue( + mockClient.stores.questionAnswering.mockResolvedValue( responseWithUndefinedSources ); @@ -288,7 +279,7 @@ describe("QA Command", () => { }); describe("Output formatting", () => { - const mockResponse: VectorStoreQuestionAnsweringResponse = { + const mockResponse: StoreQuestionAnsweringResponse = { answer: "Test answer", sources: [ { @@ -297,7 +288,7 @@ describe("QA Command", () => { chunk_index: 1, metadata: { key: "value" }, file_id: "123", - vector_store_id: "456", + store_id: "456", type: "text", text: "Test text", }, @@ -305,7 +296,7 @@ describe("QA Command", () => { }; it("should format sources as table by default", async () => { - mockClient.vectorStores.questionAnswering.mockResolvedValue(mockResponse); + mockClient.stores.questionAnswering.mockResolvedValue(mockResponse); await command.parseAsync(["node", "qa", "test-store", "question"]); @@ -316,7 +307,7 @@ describe("QA Command", () => { }); it("should format sources as JSON when specified", async () => { - mockClient.vectorStores.questionAnswering.mockResolvedValue(mockResponse); + mockClient.stores.questionAnswering.mockResolvedValue(mockResponse); await command.parseAsync([ "node", @@ -331,7 +322,7 @@ describe("QA Command", () => { }); it("should format sources as CSV when specified", async () => { - mockClient.vectorStores.questionAnswering.mockResolvedValue(mockResponse); + mockClient.stores.questionAnswering.mockResolvedValue(mockResponse); await command.parseAsync([ "node", @@ -346,7 +337,7 @@ describe("QA Command", () => { }); it("should format metadata correctly for table output", async () => { - mockClient.vectorStores.questionAnswering.mockResolvedValue(mockResponse); + mockClient.stores.questionAnswering.mockResolvedValue(mockResponse); await command.parseAsync([ "node", @@ -362,7 +353,7 @@ describe("QA Command", () => { }); it("should format metadata as object for non-table output", async () => { - mockClient.vectorStores.questionAnswering.mockResolvedValue(mockResponse); + mockClient.stores.questionAnswering.mockResolvedValue(mockResponse); await command.parseAsync([ "node", @@ -380,7 +371,7 @@ describe("QA Command", () => { }); it("should not include metadata in output when not requested", async () => { - mockClient.vectorStores.questionAnswering.mockResolvedValue(mockResponse); + mockClient.stores.questionAnswering.mockResolvedValue(mockResponse); await command.parseAsync(["node", "qa", "test-store", "question"]); @@ -501,7 +492,7 @@ describe("QA Command", () => { describe("Error handling", () => { it("should handle QA API errors", async () => { const error = new Error("API Error: Service unavailable"); - mockClient.vectorStores.questionAnswering.mockRejectedValue(error); + mockClient.stores.questionAnswering.mockRejectedValue(error); await command.parseAsync(["node", "qa", "test-store", "question"]); @@ -514,7 +505,7 @@ describe("QA Command", () => { it("should handle store resolution errors", async () => { const error = new Error("Store not found"); - mockResolveVectorStore.mockRejectedValue(error); + mockResolveStore.mockRejectedValue(error); await command.parseAsync(["node", "qa", "nonexistent-store", "question"]); @@ -526,9 +517,7 @@ describe("QA Command", () => { }); it("should handle non-Error rejections", async () => { - mockClient.vectorStores.questionAnswering.mockRejectedValue( - "Unknown error" - ); + mockClient.stores.questionAnswering.mockRejectedValue("Unknown error"); await command.parseAsync(["node", "qa", "test-store", "question"]); @@ -541,7 +530,7 @@ describe("QA Command", () => { }); describe("Global options", () => { - const mockResponse: VectorStoreQuestionAnsweringResponse = { + const mockResponse: StoreQuestionAnsweringResponse = { answer: "Test answer", sources: [ { @@ -550,7 +539,7 @@ describe("QA Command", () => { chunk_index: 1, metadata: {}, file_id: "123", - vector_store_id: "456", + store_id: "456", type: "text", text: "Test text", }, @@ -558,7 +547,7 @@ describe("QA Command", () => { }; it("should support API key option", async () => { - mockClient.vectorStores.questionAnswering.mockResolvedValue(mockResponse); + mockClient.stores.questionAnswering.mockResolvedValue(mockResponse); await command.parseAsync([ "node", @@ -593,11 +582,11 @@ describe("QA Command", () => { }, }); - mockClient.vectorStores.questionAnswering.mockResolvedValue(mockResponse); + mockClient.stores.questionAnswering.mockResolvedValue(mockResponse); await command.parseAsync(["node", "qa", "test-store", "question"]); - expect(mockClient.vectorStores.questionAnswering).toHaveBeenCalledWith( + expect(mockClient.stores.questionAnswering).toHaveBeenCalledWith( expect.objectContaining({ top_k: 12, }) @@ -614,7 +603,7 @@ describe("QA Command", () => { }, }); - mockClient.vectorStores.questionAnswering.mockResolvedValue(mockResponse); + mockClient.stores.questionAnswering.mockResolvedValue(mockResponse); await command.parseAsync([ "node", @@ -625,7 +614,7 @@ describe("QA Command", () => { "25", ]); - expect(mockClient.vectorStores.questionAnswering).toHaveBeenCalledWith( + expect(mockClient.stores.questionAnswering).toHaveBeenCalledWith( expect.objectContaining({ top_k: 25, }) @@ -637,11 +626,11 @@ describe("QA Command", () => { version: "1.0", }); - mockClient.vectorStores.questionAnswering.mockResolvedValue(mockResponse); + mockClient.stores.questionAnswering.mockResolvedValue(mockResponse); await command.parseAsync(["node", "qa", "test-store", "question"]); - expect(mockClient.vectorStores.questionAnswering).toHaveBeenCalledWith( + expect(mockClient.stores.questionAnswering).toHaveBeenCalledWith( expect.objectContaining({ top_k: 10, // Fallback default }) @@ -651,7 +640,7 @@ describe("QA Command", () => { describe("Complex scenarios", () => { it("should handle all options together", async () => { - const mockResponse: VectorStoreQuestionAnsweringResponse = { + const mockResponse: StoreQuestionAnsweringResponse = { answer: "Complex answer with all options", sources: [ { @@ -660,14 +649,14 @@ describe("QA Command", () => { chunk_index: 5, metadata: { complexity: "high", topic: "advanced" }, file_id: "123", - vector_store_id: "456", + store_id: "456", type: "text", text: "Test text", }, ], }; - mockClient.vectorStores.questionAnswering.mockResolvedValue(mockResponse); + mockClient.stores.questionAnswering.mockResolvedValue(mockResponse); await command.parseAsync([ "node", @@ -683,9 +672,9 @@ describe("QA Command", () => { "json", ]); - expect(mockClient.vectorStores.questionAnswering).toHaveBeenCalledWith({ + expect(mockClient.stores.questionAnswering).toHaveBeenCalledWith({ query: "Complex question?", - vector_store_identifiers: ["550e8400-e29b-41d4-a716-446655440090"], + store_identifiers: ["550e8400-e29b-41d4-a716-446655440090"], top_k: 20, search_options: { score_threshold: 0.75, diff --git a/packages/cli/tests/commands/vector-store/search.test.ts b/packages/cli/tests/commands/vector-store/search.test.ts index fc311dd..3bf0d83 100644 --- a/packages/cli/tests/commands/vector-store/search.test.ts +++ b/packages/cli/tests/commands/vector-store/search.test.ts @@ -7,14 +7,14 @@ import { jest, } from "@jest/globals"; import type Mixedbread from "@mixedbread/sdk"; -import type { VectorStoreSearchResponse } from "@mixedbread/sdk/resources/index.mjs"; -import type { FileSearchResponse } from "@mixedbread/sdk/resources/vector-stores.mjs"; +import type { StoreSearchResponse } from "@mixedbread/sdk/resources/index.mjs"; +import type { FileSearchResponse } from "@mixedbread/sdk/resources/stores.mjs"; import type { Command } from "commander"; import { createSearchCommand } from "../../../src/commands/vector-store/search"; import * as clientUtils from "../../../src/utils/client"; import * as configUtils from "../../../src/utils/config"; import * as outputUtils from "../../../src/utils/output"; -import * as vectorStoreUtils from "../../../src/utils/vector-store"; +import * as storeUtils from "../../../src/utils/vector-store"; // Mock dependencies jest.mock("../../../src/utils/client"); @@ -29,10 +29,9 @@ jest.mock("../../../src/utils/config"); const mockCreateClient = clientUtils.createClient as jest.MockedFunction< typeof clientUtils.createClient >; -const mockResolveVectorStore = - vectorStoreUtils.resolveVectorStore as jest.MockedFunction< - typeof vectorStoreUtils.resolveVectorStore - >; +const mockResolveStore = storeUtils.resolveStore as jest.MockedFunction< + typeof storeUtils.resolveStore +>; const mockLoadConfig = configUtils.loadConfig as jest.MockedFunction< typeof configUtils.loadConfig >; @@ -43,20 +42,18 @@ const mockFormatOutput = outputUtils.formatOutput as jest.MockedFunction< describe("Store Search Command", () => { let command: Command; let mockClient: { - vectorStores: { + stores: { files: { - search: jest.MockedFunction< - Mixedbread["vectorStores"]["files"]["search"] - >; + search: jest.MockedFunction; }; - search: jest.MockedFunction; + search: jest.MockedFunction; }; }; beforeEach(() => { command = createSearchCommand(); mockClient = { - vectorStores: { + stores: { files: { search: jest.fn(), }, @@ -66,7 +63,7 @@ describe("Store Search Command", () => { // Setup default mocks mockCreateClient.mockReturnValue(mockClient as unknown as Mixedbread); - mockResolveVectorStore.mockResolvedValue({ + mockResolveStore.mockResolvedValue({ id: "550e8400-e29b-41d4-a716-446655440080", name: "test-store", created_at: "2021-01-01", @@ -89,12 +86,12 @@ describe("Store Search Command", () => { }); describe("Basic chunk search (default)", () => { - const mockChunkResults: VectorStoreSearchResponse = { + const mockChunkResults: StoreSearchResponse = { data: [ { filename: "document1.pdf", score: 0.95, - vector_store_id: "550e8400-e29b-41d4-a716-446655440080", + store_id: "550e8400-e29b-41d4-a716-446655440080", chunk_index: 0, metadata: { author: "John Doe" }, type: "text", @@ -105,12 +102,12 @@ describe("Store Search Command", () => { }; it("should search chunks in store with default options", async () => { - const mockChunkResults: VectorStoreSearchResponse = { + const mockChunkResults: StoreSearchResponse = { data: [ { filename: "document1.pdf", score: 0.95, - vector_store_id: "550e8400-e29b-41d4-a716-446655440080", + store_id: "550e8400-e29b-41d4-a716-446655440080", chunk_index: 0, metadata: { page: 1 }, type: "text", @@ -120,7 +117,7 @@ describe("Store Search Command", () => { { filename: "document2.txt", score: 0.87, - vector_store_id: "550e8400-e29b-41d4-a716-446655440080", + store_id: "550e8400-e29b-41d4-a716-446655440080", chunk_index: 1, metadata: { category: "manual" }, type: "text", @@ -129,7 +126,7 @@ describe("Store Search Command", () => { }, ], }; - mockClient.vectorStores.search.mockResolvedValue(mockChunkResults); + mockClient.stores.search.mockResolvedValue(mockChunkResults); await command.parseAsync([ "node", @@ -138,15 +135,15 @@ describe("Store Search Command", () => { "machine learning", ]); - expect(mockResolveVectorStore).toHaveBeenCalledWith( + expect(mockResolveStore).toHaveBeenCalledWith( expect.objectContaining({ - vectorStores: expect.any(Object), + stores: expect.any(Object), }), "test-store" ); - expect(mockClient.vectorStores.search).toHaveBeenCalledWith({ + expect(mockClient.stores.search).toHaveBeenCalledWith({ query: "machine learning", - vector_store_identifiers: ["550e8400-e29b-41d4-a716-446655440080"], + store_identifiers: ["550e8400-e29b-41d4-a716-446655440080"], top_k: 5, search_options: { return_metadata: undefined, @@ -165,13 +162,13 @@ describe("Store Search Command", () => { expect.objectContaining({ filename: "document1.pdf", score: "0.95", - vector_store_id: "550e8400-e29b-41d4-a716-446655440080", + store_id: "550e8400-e29b-41d4-a716-446655440080", chunk_index: 0, }), expect.objectContaining({ filename: "document2.txt", score: "0.87", - vector_store_id: "550e8400-e29b-41d4-a716-446655440080", + store_id: "550e8400-e29b-41d4-a716-446655440080", chunk_index: 1, }), ]), @@ -180,7 +177,7 @@ describe("Store Search Command", () => { }); it("should search with custom top-k", async () => { - mockClient.vectorStores.search.mockResolvedValue(mockChunkResults); + mockClient.stores.search.mockResolvedValue(mockChunkResults); await command.parseAsync([ "node", @@ -191,7 +188,7 @@ describe("Store Search Command", () => { "20", ]); - expect(mockClient.vectorStores.search).toHaveBeenCalledWith( + expect(mockClient.stores.search).toHaveBeenCalledWith( expect.objectContaining({ top_k: 20, }) @@ -199,7 +196,7 @@ describe("Store Search Command", () => { }); it("should search with threshold", async () => { - mockClient.vectorStores.search.mockResolvedValue(mockChunkResults); + mockClient.stores.search.mockResolvedValue(mockChunkResults); await command.parseAsync([ "node", @@ -210,7 +207,7 @@ describe("Store Search Command", () => { "0.8", ]); - expect(mockClient.vectorStores.search).toHaveBeenCalledWith( + expect(mockClient.stores.search).toHaveBeenCalledWith( expect.objectContaining({ search_options: expect.objectContaining({ score_threshold: 0.8, @@ -220,7 +217,7 @@ describe("Store Search Command", () => { }); it("should search with return metadata enabled", async () => { - mockClient.vectorStores.search.mockResolvedValue(mockChunkResults); + mockClient.stores.search.mockResolvedValue(mockChunkResults); await command.parseAsync([ "node", @@ -230,7 +227,7 @@ describe("Store Search Command", () => { "--return-metadata", ]); - expect(mockClient.vectorStores.search).toHaveBeenCalledWith( + expect(mockClient.stores.search).toHaveBeenCalledWith( expect.objectContaining({ search_options: expect.objectContaining({ return_metadata: true, @@ -243,7 +240,7 @@ describe("Store Search Command", () => { }); it("should search with reranking enabled", async () => { - mockClient.vectorStores.search.mockResolvedValue(mockChunkResults); + mockClient.stores.search.mockResolvedValue(mockChunkResults); await command.parseAsync([ "node", @@ -253,7 +250,7 @@ describe("Store Search Command", () => { "--rerank", ]); - expect(mockClient.vectorStores.search).toHaveBeenCalledWith( + expect(mockClient.stores.search).toHaveBeenCalledWith( expect.objectContaining({ search_options: expect.objectContaining({ rerank: true, @@ -263,7 +260,7 @@ describe("Store Search Command", () => { }); it("should handle empty search results", async () => { - mockClient.vectorStores.search.mockResolvedValue({ data: [] }); + mockClient.stores.search.mockResolvedValue({ data: [] }); await command.parseAsync([ "node", @@ -286,7 +283,7 @@ describe("Store Search Command", () => { { filename: "document1.pdf", score: 0.95, - vector_store_id: "550e8400-e29b-41d4-a716-446655440080", + store_id: "550e8400-e29b-41d4-a716-446655440080", metadata: { author: "John Doe" }, id: "123", created_at: "2021-01-01", @@ -295,7 +292,7 @@ describe("Store Search Command", () => { { filename: "document2.txt", score: 0.87, - vector_store_id: "550e8400-e29b-41d4-a716-446655440080", + store_id: "550e8400-e29b-41d4-a716-446655440080", metadata: { category: "manual" }, id: "456", created_at: "2021-01-01", @@ -303,7 +300,7 @@ describe("Store Search Command", () => { }, ], }; - mockClient.vectorStores.files.search.mockResolvedValue(mockFileResults); + mockClient.stores.files.search.mockResolvedValue(mockFileResults); await command.parseAsync([ "node", @@ -313,9 +310,9 @@ describe("Store Search Command", () => { "--file-search", ]); - expect(mockClient.vectorStores.files.search).toHaveBeenCalledWith({ + expect(mockClient.stores.files.search).toHaveBeenCalledWith({ query: "query", - vector_store_identifiers: ["550e8400-e29b-41d4-a716-446655440080"], + store_identifiers: ["550e8400-e29b-41d4-a716-446655440080"], top_k: 5, search_options: { return_metadata: undefined, @@ -335,7 +332,7 @@ describe("Store Search Command", () => { { filename: "document1.pdf", score: 0.95, - vector_store_id: "550e8400-e29b-41d4-a716-446655440080", + store_id: "550e8400-e29b-41d4-a716-446655440080", metadata: { author: "John Doe" }, id: "123", created_at: "2021-01-01", @@ -343,7 +340,7 @@ describe("Store Search Command", () => { }, ], }; - mockClient.vectorStores.files.search.mockResolvedValue(mockFileResults); + mockClient.stores.files.search.mockResolvedValue(mockFileResults); await command.parseAsync([ "node", @@ -359,9 +356,9 @@ describe("Store Search Command", () => { "--rerank", ]); - expect(mockClient.vectorStores.files.search).toHaveBeenCalledWith({ + expect(mockClient.stores.files.search).toHaveBeenCalledWith({ query: "query", - vector_store_identifiers: ["550e8400-e29b-41d4-a716-446655440080"], + store_identifiers: ["550e8400-e29b-41d4-a716-446655440080"], top_k: 15, search_options: { return_metadata: true, @@ -373,12 +370,12 @@ describe("Store Search Command", () => { }); describe("Output formatting", () => { - const mockResults: VectorStoreSearchResponse = { + const mockResults: StoreSearchResponse = { data: [ { filename: "test.pdf", score: 0.9, - vector_store_id: "550e8400-e29b-41d4-a716-446655440080", + store_id: "550e8400-e29b-41d4-a716-446655440080", chunk_index: 0, metadata: { key: "value" }, type: "text", @@ -389,7 +386,7 @@ describe("Store Search Command", () => { }; it("should format as table by default", async () => { - mockClient.vectorStores.search.mockResolvedValue(mockResults); + mockClient.stores.search.mockResolvedValue(mockResults); await command.parseAsync(["node", "search", "test-store", "query"]); @@ -400,7 +397,7 @@ describe("Store Search Command", () => { }); it("should format as JSON when specified", async () => { - mockClient.vectorStores.search.mockResolvedValue(mockResults); + mockClient.stores.search.mockResolvedValue(mockResults); await command.parseAsync([ "node", @@ -415,7 +412,7 @@ describe("Store Search Command", () => { }); it("should format as CSV when specified", async () => { - mockClient.vectorStores.search.mockResolvedValue(mockResults); + mockClient.stores.search.mockResolvedValue(mockResults); await command.parseAsync([ "node", @@ -542,7 +539,7 @@ describe("Store Search Command", () => { describe("Error handling", () => { it("should handle search API errors", async () => { const error = new Error("API Error: Rate limit exceeded"); - mockClient.vectorStores.search.mockRejectedValue(error); + mockClient.stores.search.mockRejectedValue(error); await command.parseAsync(["node", "search", "test-store", "query"]); @@ -555,7 +552,7 @@ describe("Store Search Command", () => { it("should handle store resolution errors", async () => { const error = new Error("Store not found"); - mockResolveVectorStore.mockRejectedValue(error); + mockResolveStore.mockRejectedValue(error); await command.parseAsync([ "node", @@ -572,7 +569,7 @@ describe("Store Search Command", () => { }); it("should handle non-Error rejections", async () => { - mockClient.vectorStores.search.mockRejectedValue("Unknown error"); + mockClient.stores.search.mockRejectedValue("Unknown error"); await command.parseAsync(["node", "search", "test-store", "query"]); @@ -585,12 +582,12 @@ describe("Store Search Command", () => { }); describe("Global options", () => { - const mockResults: VectorStoreSearchResponse = { + const mockResults: StoreSearchResponse = { data: [ { filename: "test.pdf", score: 0.9, - vector_store_id: "550e8400-e29b-41d4-a716-446655440080", + store_id: "550e8400-e29b-41d4-a716-446655440080", chunk_index: 0, metadata: {}, type: "text", @@ -601,7 +598,7 @@ describe("Store Search Command", () => { }; it("should support API key option", async () => { - mockClient.vectorStores.search.mockResolvedValue(mockResults); + mockClient.stores.search.mockResolvedValue(mockResults); await command.parseAsync([ "node", @@ -632,11 +629,11 @@ describe("Store Search Command", () => { version: "1.0.0", }); - mockClient.vectorStores.search.mockResolvedValue({ data: [] }); + mockClient.stores.search.mockResolvedValue({ data: [] }); await command.parseAsync(["node", "search", "test-store", "query"]); - expect(mockClient.vectorStores.search).toHaveBeenCalledWith( + expect(mockClient.stores.search).toHaveBeenCalledWith( expect.objectContaining({ top_k: 15, search_options: expect.objectContaining({ @@ -657,7 +654,7 @@ describe("Store Search Command", () => { version: "1.0.0", }); - mockClient.vectorStores.search.mockResolvedValue({ data: [] }); + mockClient.stores.search.mockResolvedValue({ data: [] }); await command.parseAsync([ "node", @@ -668,7 +665,7 @@ describe("Store Search Command", () => { "25", ]); - expect(mockClient.vectorStores.search).toHaveBeenCalledWith( + expect(mockClient.stores.search).toHaveBeenCalledWith( expect.objectContaining({ top_k: 25, search_options: expect.objectContaining({ diff --git a/packages/cli/tests/commands/vector-store/sync.test.ts b/packages/cli/tests/commands/vector-store/sync.test.ts index 91671b7..29847cc 100644 --- a/packages/cli/tests/commands/vector-store/sync.test.ts +++ b/packages/cli/tests/commands/vector-store/sync.test.ts @@ -13,7 +13,7 @@ import { createSyncCommand } from "../../../src/commands/vector-store/sync"; import * as clientUtils from "../../../src/utils/client"; import * as syncUtils from "../../../src/utils/sync"; import * as syncStateUtils from "../../../src/utils/sync-state"; -import * as vectorStoreUtils from "../../../src/utils/vector-store"; +import * as storeUtils from "../../../src/utils/vector-store"; // Mock dependencies jest.mock("../../../src/utils/client"); @@ -35,16 +35,15 @@ const mockExecuteSyncChanges = const mockGetSyncedFiles = syncStateUtils.getSyncedFiles as jest.MockedFunction< typeof syncStateUtils.getSyncedFiles >; -const mockResolveVectorStore = - vectorStoreUtils.resolveVectorStore as jest.MockedFunction< - typeof vectorStoreUtils.resolveVectorStore - >; +const mockResolveStore = storeUtils.resolveStore as jest.MockedFunction< + typeof storeUtils.resolveStore +>; describe("Store Sync Command", () => { let command: Command; let mockClient: { - vectorStores: { - create: jest.MockedFunction; + stores: { + create: jest.MockedFunction; }; }; @@ -53,14 +52,14 @@ describe("Store Sync Command", () => { // Setup mock client mockClient = { - vectorStores: { + stores: { create: jest.fn(), }, }; // Setup default mocks mockCreateClient.mockReturnValue(mockClient as unknown as Mixedbread); - mockResolveVectorStore.mockResolvedValue({ + mockResolveStore.mockResolvedValue({ id: "550e8400-e29b-41d4-a716-446655440040", name: "test-store", created_at: "2021-01-01", diff --git a/packages/cli/tests/commands/vector-store/update.test.ts b/packages/cli/tests/commands/vector-store/update.test.ts index 3cf1e3d..13d404f 100644 --- a/packages/cli/tests/commands/vector-store/update.test.ts +++ b/packages/cli/tests/commands/vector-store/update.test.ts @@ -11,7 +11,7 @@ import type { Command } from "commander"; import { createUpdateCommand } from "../../../src/commands/vector-store/update"; import * as clientUtils from "../../../src/utils/client"; import * as outputUtils from "../../../src/utils/output"; -import * as vectorStoreUtils from "../../../src/utils/vector-store"; +import * as storeUtils from "../../../src/utils/vector-store"; // Mock dependencies jest.mock("../../../src/utils/client"); @@ -25,10 +25,9 @@ jest.mock("../../../src/utils/output", () => ({ const mockCreateClient = clientUtils.createClient as jest.MockedFunction< typeof clientUtils.createClient >; -const mockResolveVectorStore = - vectorStoreUtils.resolveVectorStore as jest.MockedFunction< - typeof vectorStoreUtils.resolveVectorStore - >; +const mockResolveStore = storeUtils.resolveStore as jest.MockedFunction< + typeof storeUtils.resolveStore +>; const mockFormatOutput = outputUtils.formatOutput as jest.MockedFunction< typeof outputUtils.formatOutput >; @@ -36,8 +35,8 @@ const mockFormatOutput = outputUtils.formatOutput as jest.MockedFunction< describe("Store Update Command", () => { let command: Command; let mockClient: { - vectorStores: { - update: jest.MockedFunction; + stores: { + update: jest.MockedFunction; }; }; @@ -46,14 +45,14 @@ describe("Store Update Command", () => { // Setup mock client mockClient = { - vectorStores: { + stores: { update: jest.fn(), }, }; // Setup default mocks mockCreateClient.mockReturnValue(mockClient as unknown as Mixedbread); - mockResolveVectorStore.mockResolvedValue({ + mockResolveStore.mockResolvedValue({ id: "550e8400-e29b-41d4-a716-446655440060", name: "test-store", created_at: "2021-01-01", @@ -68,7 +67,7 @@ describe("Store Update Command", () => { describe("Basic updates", () => { it("should update store name", async () => { - const updatedVectorStore = { + const updatedStore = { id: "550e8400-e29b-41d4-a716-446655440060", name: "updated-store", description: null, @@ -78,7 +77,7 @@ describe("Store Update Command", () => { updated_at: "2021-01-01T00:00:00Z", }; - mockClient.vectorStores.update.mockResolvedValue(updatedVectorStore); + mockClient.stores.update.mockResolvedValue(updatedStore); await command.parseAsync([ "node", @@ -88,13 +87,13 @@ describe("Store Update Command", () => { "updated-store", ]); - expect(mockResolveVectorStore).toHaveBeenCalledWith( + expect(mockResolveStore).toHaveBeenCalledWith( expect.objectContaining({ - vectorStores: expect.any(Object), + stores: expect.any(Object), }), "test-store" ); - expect(mockClient.vectorStores.update).toHaveBeenCalledWith( + expect(mockClient.stores.update).toHaveBeenCalledWith( "550e8400-e29b-41d4-a716-446655440060", { name: "updated-store", @@ -104,14 +103,11 @@ describe("Store Update Command", () => { expect.any(String), expect.stringContaining('Store "test-store" updated successfully') ); - expect(mockFormatOutput).toHaveBeenCalledWith( - updatedVectorStore, - undefined - ); + expect(mockFormatOutput).toHaveBeenCalledWith(updatedStore, undefined); }); it("should update store description", async () => { - const updatedVectorStore = { + const updatedStore = { id: "550e8400-e29b-41d4-a716-446655440060", name: "test-store", description: "Updated description", @@ -121,7 +117,7 @@ describe("Store Update Command", () => { updated_at: "2021-01-01T00:00:00Z", }; - mockClient.vectorStores.update.mockResolvedValue(updatedVectorStore); + mockClient.stores.update.mockResolvedValue(updatedStore); await command.parseAsync([ "node", @@ -131,7 +127,7 @@ describe("Store Update Command", () => { "Updated description", ]); - expect(mockClient.vectorStores.update).toHaveBeenCalledWith( + expect(mockClient.stores.update).toHaveBeenCalledWith( "550e8400-e29b-41d4-a716-446655440060", { description: "Updated description", @@ -140,7 +136,7 @@ describe("Store Update Command", () => { }); it("should update store expiration", async () => { - const updatedVectorStore = { + const updatedStore = { id: "550e8400-e29b-41d4-a716-446655440060", name: "test-store", description: null, @@ -150,7 +146,7 @@ describe("Store Update Command", () => { updated_at: "2021-01-01T00:00:00Z", }; - mockClient.vectorStores.update.mockResolvedValue(updatedVectorStore); + mockClient.stores.update.mockResolvedValue(updatedStore); await command.parseAsync([ "node", @@ -160,7 +156,7 @@ describe("Store Update Command", () => { "30", ]); - expect(mockClient.vectorStores.update).toHaveBeenCalledWith( + expect(mockClient.stores.update).toHaveBeenCalledWith( "550e8400-e29b-41d4-a716-446655440060", { expires_after: { @@ -173,7 +169,7 @@ describe("Store Update Command", () => { it("should update store metadata", async () => { const metadata = { key: "value", version: "2.0" }; - const updatedVectorStore = { + const updatedStore = { id: "550e8400-e29b-41d4-a716-446655440060", name: "test-store", description: null, @@ -183,7 +179,7 @@ describe("Store Update Command", () => { updated_at: "2021-01-01T00:00:00Z", }; - mockClient.vectorStores.update.mockResolvedValue(updatedVectorStore); + mockClient.stores.update.mockResolvedValue(updatedStore); await command.parseAsync([ "node", @@ -193,7 +189,7 @@ describe("Store Update Command", () => { JSON.stringify(metadata), ]); - expect(mockClient.vectorStores.update).toHaveBeenCalledWith( + expect(mockClient.stores.update).toHaveBeenCalledWith( "550e8400-e29b-41d4-a716-446655440060", { metadata, @@ -203,7 +199,7 @@ describe("Store Update Command", () => { it("should update multiple fields at once", async () => { const metadata = { updated: true }; - const updatedVectorStore = { + const updatedStore = { id: "550e8400-e29b-41d4-a716-446655440060", name: "new-name", description: "New description", @@ -213,7 +209,7 @@ describe("Store Update Command", () => { updated_at: "2021-01-01T00:00:00Z", }; - mockClient.vectorStores.update.mockResolvedValue(updatedVectorStore); + mockClient.stores.update.mockResolvedValue(updatedStore); await command.parseAsync([ "node", @@ -229,7 +225,7 @@ describe("Store Update Command", () => { JSON.stringify(metadata), ]); - expect(mockClient.vectorStores.update).toHaveBeenCalledWith( + expect(mockClient.stores.update).toHaveBeenCalledWith( "550e8400-e29b-41d4-a716-446655440060", { name: "new-name", @@ -268,7 +264,7 @@ describe("Store Update Command", () => { numbers: [1, 2, 3], }; - const updatedVectorStore = { + const updatedStore = { id: "550e8400-e29b-41d4-a716-446655440060", name: "test-store", description: null, @@ -278,7 +274,7 @@ describe("Store Update Command", () => { updated_at: "2021-01-01T00:00:00Z", }; - mockClient.vectorStores.update.mockResolvedValue(updatedVectorStore); + mockClient.stores.update.mockResolvedValue(updatedStore); await command.parseAsync([ "node", @@ -288,7 +284,7 @@ describe("Store Update Command", () => { JSON.stringify(complexMetadata), ]); - expect(mockClient.vectorStores.update).toHaveBeenCalledWith( + expect(mockClient.stores.update).toHaveBeenCalledWith( "550e8400-e29b-41d4-a716-446655440060", { metadata: complexMetadata, @@ -298,7 +294,7 @@ describe("Store Update Command", () => { }); describe("Output formatting", () => { - const updatedVectorStore = { + const updatedStore = { id: "550e8400-e29b-41d4-a716-446655440060", name: "test-store", description: "Updated", @@ -309,7 +305,7 @@ describe("Store Update Command", () => { }; it("should format as table by default", async () => { - mockClient.vectorStores.update.mockResolvedValue(updatedVectorStore); + mockClient.stores.update.mockResolvedValue(updatedStore); await command.parseAsync([ "node", @@ -319,14 +315,11 @@ describe("Store Update Command", () => { "Updated", ]); - expect(mockFormatOutput).toHaveBeenCalledWith( - updatedVectorStore, - undefined - ); + expect(mockFormatOutput).toHaveBeenCalledWith(updatedStore, undefined); }); it("should format as JSON when specified", async () => { - mockClient.vectorStores.update.mockResolvedValue(updatedVectorStore); + mockClient.stores.update.mockResolvedValue(updatedStore); await command.parseAsync([ "node", @@ -338,11 +331,11 @@ describe("Store Update Command", () => { "json", ]); - expect(mockFormatOutput).toHaveBeenCalledWith(updatedVectorStore, "json"); + expect(mockFormatOutput).toHaveBeenCalledWith(updatedStore, "json"); }); it("should format as CSV when specified", async () => { - mockClient.vectorStores.update.mockResolvedValue(updatedVectorStore); + mockClient.stores.update.mockResolvedValue(updatedStore); await command.parseAsync([ "node", @@ -354,7 +347,7 @@ describe("Store Update Command", () => { "csv", ]); - expect(mockFormatOutput).toHaveBeenCalledWith(updatedVectorStore, "csv"); + expect(mockFormatOutput).toHaveBeenCalledWith(updatedStore, "csv"); }); }); @@ -417,7 +410,7 @@ describe("Store Update Command", () => { describe("Error handling", () => { it("should handle API errors", async () => { const error = new Error("API Error: Unauthorized"); - mockClient.vectorStores.update.mockRejectedValue(error); + mockClient.stores.update.mockRejectedValue(error); await command.parseAsync([ "node", @@ -436,7 +429,7 @@ describe("Store Update Command", () => { it("should handle store resolution errors", async () => { const error = new Error("Store not found"); - mockResolveVectorStore.mockRejectedValue(error); + mockResolveStore.mockRejectedValue(error); await command.parseAsync([ "node", @@ -454,7 +447,7 @@ describe("Store Update Command", () => { }); it("should handle non-Error rejections", async () => { - mockClient.vectorStores.update.mockRejectedValue("Unknown error"); + mockClient.stores.update.mockRejectedValue("Unknown error"); await command.parseAsync([ "node", @@ -473,7 +466,7 @@ describe("Store Update Command", () => { }); describe("Global options", () => { - const updatedVectorStore = { + const updatedStore = { id: "550e8400-e29b-41d4-a716-446655440060", name: "updated-store", description: null, @@ -484,7 +477,7 @@ describe("Store Update Command", () => { }; it("should support API key option", async () => { - mockClient.vectorStores.update.mockResolvedValue(updatedVectorStore); + mockClient.stores.update.mockResolvedValue(updatedStore); await command.parseAsync([ "node", diff --git a/packages/cli/tests/commands/vector-store/upload.test.ts b/packages/cli/tests/commands/vector-store/upload.test.ts index 8df67d4..8ecc2b1 100644 --- a/packages/cli/tests/commands/vector-store/upload.test.ts +++ b/packages/cli/tests/commands/vector-store/upload.test.ts @@ -14,11 +14,11 @@ import { createUploadCommand } from "../../../src/commands/vector-store/upload"; import * as clientUtils from "../../../src/utils/client"; import * as configUtils from "../../../src/utils/config"; import * as uploadUtils from "../../../src/utils/upload"; -import * as vectorStoreUtils from "../../../src/utils/vector-store"; +import * as storeUtils from "../../../src/utils/vector-store"; import { createMockConfig, - createMockVectorStore, + createMockStore, type FlexibleMock, } from "../../helpers/test-utils"; @@ -34,14 +34,12 @@ jest.mock("glob"); const mockCreateClient = clientUtils.createClient as jest.MockedFunction< typeof clientUtils.createClient >; -const mockResolveVectorStore = - vectorStoreUtils.resolveVectorStore as jest.MockedFunction< - typeof vectorStoreUtils.resolveVectorStore - >; -const mockGetVectorStoreFiles = - vectorStoreUtils.getVectorStoreFiles as jest.MockedFunction< - typeof vectorStoreUtils.getVectorStoreFiles - >; +const mockResolveStore = storeUtils.resolveStore as jest.MockedFunction< + typeof storeUtils.resolveStore +>; +const mockGetStoreFiles = storeUtils.getStoreFiles as jest.MockedFunction< + typeof storeUtils.getStoreFiles +>; const mockLoadConfig = configUtils.loadConfig as jest.MockedFunction< typeof configUtils.loadConfig >; @@ -53,7 +51,7 @@ const mockUploadFilesInBatch = describe("Store Upload Command", () => { let command: Command; let mockClient: { - vectorStores: { + stores: { files: { list: FlexibleMock; upload: FlexibleMock; @@ -67,7 +65,7 @@ describe("Store Upload Command", () => { // Setup mock client mockClient = { - vectorStores: { + stores: { files: { upload: jest.fn(), list: jest.fn(), @@ -78,8 +76,8 @@ describe("Store Upload Command", () => { // Setup default mocks mockCreateClient.mockReturnValue(mockClient as unknown as Mixedbread); - mockResolveVectorStore.mockResolvedValue( - createMockVectorStore({ + mockResolveStore.mockResolvedValue( + createMockStore({ id: "550e8400-e29b-41d4-a716-446655440130", name: "test-store", }) @@ -353,16 +351,16 @@ describe("Store Upload Command", () => { "test.md", ]); - mockGetVectorStoreFiles.mockResolvedValue([ + mockGetStoreFiles.mockResolvedValue([ { id: "existing_file_id", - vector_store_id: "550e8400-e29b-41d4-a716-446655440130", + store_id: "550e8400-e29b-41d4-a716-446655440130", created_at: "2021-02-18T12:00:00Z", metadata: { file_path: "test.md" }, }, ]); - mockClient.vectorStores.files.delete.mockResolvedValue({}); + mockClient.stores.files.delete.mockResolvedValue({}); await command.parseAsync([ "node", @@ -372,7 +370,7 @@ describe("Store Upload Command", () => { "--unique", ]); - expect(mockGetVectorStoreFiles).toHaveBeenCalledWith( + expect(mockGetStoreFiles).toHaveBeenCalledWith( expect.any(Object), "550e8400-e29b-41d4-a716-446655440130" ); @@ -395,7 +393,7 @@ describe("Store Upload Command", () => { (glob as unknown as jest.MockedFunction).mockResolvedValue([ "test.md", ]); - mockGetVectorStoreFiles.mockRejectedValue(new Error("List failed")); + mockGetStoreFiles.mockRejectedValue(new Error("List failed")); await command.parseAsync([ "node", @@ -488,7 +486,7 @@ describe("Store Upload Command", () => { }); it("should handle store resolution errors", async () => { - mockResolveVectorStore.mockRejectedValue(new Error("Store not found")); + mockResolveStore.mockRejectedValue(new Error("Store not found")); await command.parseAsync(["node", "upload", "invalid-store", "*.md"]); @@ -609,10 +607,10 @@ describe("Store Upload Command", () => { "empty.md", ]); - mockGetVectorStoreFiles.mockResolvedValue([ + mockGetStoreFiles.mockResolvedValue([ { id: "existing_file_id", - vector_store_id: "550e8400-e29b-41d4-a716-446655440130", + store_id: "550e8400-e29b-41d4-a716-446655440130", created_at: "2021-02-18T12:00:00Z", metadata: { file_path: "existing.md" }, }, diff --git a/packages/cli/tests/helpers/test-utils.ts b/packages/cli/tests/helpers/test-utils.ts index 6459e7c..e32cbc9 100644 --- a/packages/cli/tests/helpers/test-utils.ts +++ b/packages/cli/tests/helpers/test-utils.ts @@ -78,7 +78,7 @@ export function mockConsole() { */ export function createMockClient() { return { - vectorStores: { + stores: { create: jest.fn(), retrieve: jest.fn(), update: jest.fn(), @@ -226,7 +226,7 @@ export const createMockConsole = () => { }; // Helper to create mock store data with required fields -export const createMockVectorStore = (overrides: Record = {}) => ({ +export const createMockStore = (overrides: Record = {}) => ({ id: "550e8400-e29b-41d4-a716-446655440000", name: "test-store", created_at: "2021-01-01T00:00:00Z", diff --git a/packages/cli/tests/utils/completion-cache.test.ts b/packages/cli/tests/utils/completion-cache.test.ts index 9d6e105..85856d0 100644 --- a/packages/cli/tests/utils/completion-cache.test.ts +++ b/packages/cli/tests/utils/completion-cache.test.ts @@ -187,7 +187,7 @@ describe("Completion Cache", () => { ], }); const mockClient = { - vectorStores: { + stores: { list: mockList, }, } as unknown as Mixedbread; @@ -219,7 +219,7 @@ describe("Completion Cache", () => { data: [{ id: "store1", name: "work-store" }], }); const mockClient = { - vectorStores: { + stores: { list: mockList, }, } as unknown as Mixedbread; @@ -249,7 +249,7 @@ describe("Completion Cache", () => { >() .mockRejectedValue(new Error("API Error")); const mockClient = { - vectorStores: { + stores: { list: mockList, }, } as unknown as Mixedbread; @@ -273,7 +273,7 @@ describe("Completion Cache", () => { data: [{ id: "store1", name: "store1" }], }); const mockClient = { - vectorStores: { + stores: { list: mockList, }, } as unknown as Mixedbread; @@ -312,7 +312,7 @@ describe("Completion Cache", () => { data: [{ id: "store2", name: "personal-store" }], }); const mockClient = { - vectorStores: { + stores: { list: mockList, }, } as unknown as Mixedbread; @@ -525,7 +525,7 @@ describe("Completion Cache", () => { data: [{ id: "store1", name: "store1" }], }); const mockClient = { - vectorStores: { + stores: { list: mockList, }, } as unknown as Mixedbread; diff --git a/packages/cli/tests/utils/config.test.ts b/packages/cli/tests/utils/config.test.ts index c62031b..d33f74a 100644 --- a/packages/cli/tests/utils/config.test.ts +++ b/packages/cli/tests/utils/config.test.ts @@ -8,7 +8,7 @@ import { getBaseURL, loadConfig, parseConfigValue, - resolveVectorStoreName, + resolveStoreName, saveConfig, } from "../../src/utils/config"; import { getTestConfigDir } from "../helpers/test-utils"; @@ -407,7 +407,7 @@ describe("Config Utils", () => { }); }); - describe("resolveVectorStoreName", () => { + describe("resolveStoreName", () => { it("should return alias value if exists", () => { mockFs({ [configFile]: JSON.stringify({ @@ -416,7 +416,7 @@ describe("Config Utils", () => { }), }); - const resolved = resolveVectorStoreName("docs"); + const resolved = resolveStoreName("docs"); expect(resolved).toBe("store_abc123"); }); @@ -429,7 +429,7 @@ describe("Config Utils", () => { }), }); - const resolved = resolveVectorStoreName("store_direct123"); + const resolved = resolveStoreName("store_direct123"); expect(resolved).toBe("store_direct123"); }); @@ -439,7 +439,7 @@ describe("Config Utils", () => { [configFile]: JSON.stringify({ version: "1.0" }), }); - const resolved = resolveVectorStoreName("test"); + const resolved = resolveStoreName("test"); expect(resolved).toBe("test"); }); diff --git a/packages/cli/tests/utils/sync.test.ts b/packages/cli/tests/utils/sync.test.ts index a9da918..b280a38 100644 --- a/packages/cli/tests/utils/sync.test.ts +++ b/packages/cli/tests/utils/sync.test.ts @@ -62,7 +62,7 @@ const mockNormalizeGitPatterns = describe("Sync Utils", () => { let mockClient: { - vectorStores: { + stores: { files: { upload: FlexibleMock; delete: FlexibleMock; @@ -318,7 +318,7 @@ describe("Sync Utils", () => { totalSize: 27, }; - mockClient.vectorStores.files.delete.mockResolvedValue({}); + mockClient.stores.files.delete.mockResolvedValue({}); mockUploadFile.mockResolvedValue(); const result = await executeSyncChanges( @@ -336,9 +336,9 @@ describe("Sync Utils", () => { expect(result.uploads.failed).toHaveLength(0); expect(result.deletions.successful).toHaveLength(1); expect(result.deletions.failed).toHaveLength(0); - expect(mockClient.vectorStores.files.delete).toHaveBeenCalledWith( + expect(mockClient.stores.files.delete).toHaveBeenCalledWith( "modified-file-id", - { vector_store_identifier: "test-store" } + { store_identifier: "test-store" } ); expect(mockUploadFile).toHaveBeenCalledTimes(2); }); @@ -428,7 +428,7 @@ describe("Sync Utils", () => { totalSize: 31, }; - mockClient.vectorStores.files.delete.mockResolvedValue({}); + mockClient.stores.files.delete.mockResolvedValue({}); mockUploadFile.mockResolvedValue(); const result = await executeSyncChanges( @@ -446,9 +446,9 @@ describe("Sync Utils", () => { expect(result.uploads.failed).toHaveLength(1); // Empty file expect(result.uploads.failed[0].skipped).toBe(true); expect(result.deletions.successful).toHaveLength(1); - expect(mockClient.vectorStores.files.delete).toHaveBeenCalledWith( + expect(mockClient.stores.files.delete).toHaveBeenCalledWith( "modified-file-id", - { vector_store_identifier: "test-store" } + { store_identifier: "test-store" } ); expect(mockUploadFile).toHaveBeenCalledTimes(2); }); diff --git a/packages/cli/tests/utils/upload.test.ts b/packages/cli/tests/utils/upload.test.ts index 2bf5541..9eb8fae 100644 --- a/packages/cli/tests/utils/upload.test.ts +++ b/packages/cli/tests/utils/upload.test.ts @@ -18,7 +18,7 @@ const originalConsoleWarn = console.warn; describe("Upload Utils", () => { let mockClient: { - vectorStores: { + stores: { files: { upload: FlexibleMock; delete: FlexibleMock; @@ -45,7 +45,7 @@ describe("Upload Utils", () => { "test.txt": "Hello world", }); - mockClient.vectorStores.files.upload.mockResolvedValue({}); + mockClient.stores.files.upload.mockResolvedValue({}); await uploadFile( mockClient as unknown as Mixedbread, @@ -53,7 +53,7 @@ describe("Upload Utils", () => { "test.txt" ); - expect(mockClient.vectorStores.files.upload).toHaveBeenCalledWith( + expect(mockClient.stores.files.upload).toHaveBeenCalledWith( "test-store", expect.any(File), expect.objectContaining({ @@ -78,7 +78,7 @@ describe("Upload Utils", () => { ); // uploadFile doesn't have empty file checking, so it will upload - expect(mockClient.vectorStores.files.upload).toHaveBeenCalledWith( + expect(mockClient.stores.files.upload).toHaveBeenCalledWith( "test-store", expect.any(File), expect.objectContaining({ @@ -96,7 +96,7 @@ describe("Upload Utils", () => { "test.md": "# Test Document", }); - mockClient.vectorStores.files.upload.mockResolvedValue({}); + mockClient.stores.files.upload.mockResolvedValue({}); await uploadFile( mockClient as unknown as Mixedbread, @@ -109,7 +109,7 @@ describe("Upload Utils", () => { } ); - expect(mockClient.vectorStores.files.upload).toHaveBeenCalledWith( + expect(mockClient.stores.files.upload).toHaveBeenCalledWith( "test-store", expect.any(File), expect.objectContaining({ @@ -128,7 +128,7 @@ describe("Upload Utils", () => { "test.ts": "const hello = 'world';", }); - mockClient.vectorStores.files.upload.mockResolvedValue({}); + mockClient.stores.files.upload.mockResolvedValue({}); await uploadFile( mockClient as unknown as Mixedbread, @@ -136,7 +136,7 @@ describe("Upload Utils", () => { "test.ts" ); - expect(mockClient.vectorStores.files.upload).toHaveBeenCalledWith( + expect(mockClient.stores.files.upload).toHaveBeenCalledWith( "test-store", expect.objectContaining({ name: "test.ts", @@ -151,7 +151,7 @@ describe("Upload Utils", () => { "script.py": "print('Hello, World!')", }); - mockClient.vectorStores.files.upload.mockResolvedValue({}); + mockClient.stores.files.upload.mockResolvedValue({}); await uploadFile( mockClient as unknown as Mixedbread, @@ -159,7 +159,7 @@ describe("Upload Utils", () => { "script.py" ); - expect(mockClient.vectorStores.files.upload).toHaveBeenCalledWith( + expect(mockClient.stores.files.upload).toHaveBeenCalledWith( "test-store", expect.objectContaining({ name: "script.py", @@ -174,7 +174,7 @@ describe("Upload Utils", () => { "content.mdx": "# Hello MDX\n\n", }); - mockClient.vectorStores.files.upload.mockResolvedValue({}); + mockClient.stores.files.upload.mockResolvedValue({}); await uploadFile( mockClient as unknown as Mixedbread, @@ -182,7 +182,7 @@ describe("Upload Utils", () => { "content.mdx" ); - expect(mockClient.vectorStores.files.upload).toHaveBeenCalledWith( + expect(mockClient.stores.files.upload).toHaveBeenCalledWith( "test-store", expect.objectContaining({ name: "content.mdx", @@ -200,7 +200,7 @@ describe("Upload Utils", () => { "file2.txt": "Content 2", }); - mockClient.vectorStores.files.upload.mockResolvedValue({}); + mockClient.stores.files.upload.mockResolvedValue({}); const files = [ { @@ -235,7 +235,7 @@ describe("Upload Utils", () => { failed: 0, successfulSize: 18, // "Content 1" + "Content 2" = 9 + 9 = 18 }); - expect(mockClient.vectorStores.files.upload).toHaveBeenCalledTimes(2); + expect(mockClient.stores.files.upload).toHaveBeenCalledTimes(2); expect(mockConsoleWarn).not.toHaveBeenCalled(); }); @@ -246,7 +246,7 @@ describe("Upload Utils", () => { "empty2.txt": "", }); - mockClient.vectorStores.files.upload.mockResolvedValue({}); + mockClient.stores.files.upload.mockResolvedValue({}); const files = [ { @@ -287,7 +287,7 @@ describe("Upload Utils", () => { failed: 0, successfulSize: 12, // Only "Real content" = 12 bytes }); - expect(mockClient.vectorStores.files.upload).toHaveBeenCalledTimes(1); + expect(mockClient.stores.files.upload).toHaveBeenCalledTimes(1); expect(mockConsoleWarn).not.toHaveBeenCalled(); // Warning is shown via spinner.warn }); @@ -299,7 +299,7 @@ describe("Upload Utils", () => { }); // Mock upload to fail for failure.txt - mockClient.vectorStores.files.upload + mockClient.stores.files.upload .mockResolvedValueOnce({}) // success.txt .mockRejectedValueOnce(new Error("Upload failed")); // failure.txt @@ -342,7 +342,7 @@ describe("Upload Utils", () => { failed: 1, successfulSize: 15, // Only "Success content" = 15 bytes }); - expect(mockClient.vectorStores.files.upload).toHaveBeenCalledTimes(2); + expect(mockClient.stores.files.upload).toHaveBeenCalledTimes(2); }); it("should handle unique flag with empty files", async () => { @@ -351,8 +351,8 @@ describe("Upload Utils", () => { "empty.txt": "", }); - mockClient.vectorStores.files.upload.mockResolvedValue({}); - mockClient.vectorStores.files.delete.mockResolvedValue({}); + mockClient.stores.files.upload.mockResolvedValue({}); + mockClient.stores.files.delete.mockResolvedValue({}); const existingFiles = new Map([["existing.txt", "existing-file-id"]]); @@ -389,11 +389,11 @@ describe("Upload Utils", () => { failed: 0, successfulSize: 16, // "Existing content" = 16 bytes }); - expect(mockClient.vectorStores.files.delete).toHaveBeenCalledWith( + expect(mockClient.stores.files.delete).toHaveBeenCalledWith( "existing-file-id", - { vector_store_identifier: "test-store" } + { store_identifier: "test-store" } ); - expect(mockClient.vectorStores.files.upload).toHaveBeenCalledTimes(1); + expect(mockClient.stores.files.upload).toHaveBeenCalledTimes(1); }); it("should fix mime types for TypeScript, Python, and MDX files in batch", async () => { @@ -404,7 +404,7 @@ describe("Upload Utils", () => { "readme.md": "# Regular Markdown", }); - mockClient.vectorStores.files.upload.mockResolvedValue({}); + mockClient.stores.files.upload.mockResolvedValue({}); const files = [ { @@ -445,7 +445,7 @@ describe("Upload Utils", () => { ); // Verify TypeScript file mime type was fixed - expect(mockClient.vectorStores.files.upload).toHaveBeenCalledWith( + expect(mockClient.stores.files.upload).toHaveBeenCalledWith( "test-store", expect.objectContaining({ name: "app.ts", @@ -455,7 +455,7 @@ describe("Upload Utils", () => { ); // Verify Python file mime type was fixed - expect(mockClient.vectorStores.files.upload).toHaveBeenCalledWith( + expect(mockClient.stores.files.upload).toHaveBeenCalledWith( "test-store", expect.objectContaining({ name: "utils.py", @@ -465,7 +465,7 @@ describe("Upload Utils", () => { ); // Verify MDX file mime type was fixed - expect(mockClient.vectorStores.files.upload).toHaveBeenCalledWith( + expect(mockClient.stores.files.upload).toHaveBeenCalledWith( "test-store", expect.objectContaining({ name: "page.mdx", @@ -475,7 +475,7 @@ describe("Upload Utils", () => { ); // Verify regular markdown file kept its original mime type - expect(mockClient.vectorStores.files.upload).toHaveBeenCalledWith( + expect(mockClient.stores.files.upload).toHaveBeenCalledWith( "test-store", expect.objectContaining({ name: "readme.md", diff --git a/packages/cli/tests/utils/vector-store.test.ts b/packages/cli/tests/utils/vector-store.test.ts index 1a66b2d..6c25f4d 100644 --- a/packages/cli/tests/utils/vector-store.test.ts +++ b/packages/cli/tests/utils/vector-store.test.ts @@ -9,15 +9,15 @@ import { } from "@jest/globals"; import type { Mixedbread } from "@mixedbread/sdk"; import * as configUtils from "../../src/utils/config"; -import { resolveVectorStore } from "../../src/utils/vector-store"; +import { resolveStore } from "../../src/utils/vector-store"; // Mock config utils jest.mock("../../src/utils/config"); describe("Store Utils", () => { - describe("resolveVectorStore", () => { + describe("resolveStore", () => { let mockClient: { - vectorStores: { + stores: { list: jest.MockedFunction<() => Promise<{ data: unknown[] }>>; retrieve: jest.MockedFunction<(id: string) => Promise>; }; @@ -25,15 +25,15 @@ describe("Store Utils", () => { beforeEach(() => { mockClient = { - vectorStores: { + stores: { list: jest.fn(), retrieve: jest.fn(), }, }; ( - configUtils.resolveVectorStoreName as jest.MockedFunction< - typeof configUtils.resolveVectorStoreName + configUtils.resolveStoreName as jest.MockedFunction< + typeof configUtils.resolveStoreName > ).mockImplementation((name) => name); }); @@ -43,80 +43,78 @@ describe("Store Utils", () => { }); it("should resolve store ID directly", async () => { - const mockVectorStore = { + const mockStore = { id: "550e8400-e29b-41d4-a716-446655440010", name: "test-store", }; - mockClient.vectorStores.retrieve.mockResolvedValue(mockVectorStore); - mockClient.vectorStores.list.mockResolvedValue({ data: [] }); + mockClient.stores.retrieve.mockResolvedValue(mockStore); + mockClient.stores.list.mockResolvedValue({ data: [] }); - const result = await resolveVectorStore( + const result = await resolveStore( mockClient as unknown as Mixedbread, "550e8400-e29b-41d4-a716-446655440010" ); - expect(result).toEqual(mockVectorStore); - expect(mockClient.vectorStores.retrieve).toHaveBeenCalledWith( + expect(result).toEqual(mockStore); + expect(mockClient.stores.retrieve).toHaveBeenCalledWith( "550e8400-e29b-41d4-a716-446655440010" ); - expect(mockClient.vectorStores.list).not.toHaveBeenCalled(); + expect(mockClient.stores.list).not.toHaveBeenCalled(); }); it("should resolve store by name", async () => { - const mockVectorStore = { + const mockStore = { id: "550e8400-e29b-41d4-a716-446655440011", name: "my-store", }; // Names are valid identifiers, so retrieve should succeed - mockClient.vectorStores.retrieve.mockResolvedValue(mockVectorStore); + mockClient.stores.retrieve.mockResolvedValue(mockStore); - const result = await resolveVectorStore( + const result = await resolveStore( mockClient as unknown as Mixedbread, "my-store" ); - expect(result).toEqual(mockVectorStore); - expect(mockClient.vectorStores.retrieve).toHaveBeenCalledWith("my-store"); - expect(mockClient.vectorStores.list).not.toHaveBeenCalled(); + expect(result).toEqual(mockStore); + expect(mockClient.stores.retrieve).toHaveBeenCalledWith("my-store"); + expect(mockClient.stores.list).not.toHaveBeenCalled(); }); it("should resolve using alias", async () => { - const mockVectorStore = { + const mockStore = { id: "550e8400-e29b-41d4-a716-446655440001", name: "aliased-store", }; ( - configUtils.resolveVectorStoreName as jest.MockedFunction< - typeof configUtils.resolveVectorStoreName + configUtils.resolveStoreName as jest.MockedFunction< + typeof configUtils.resolveStoreName > ).mockReturnValue("550e8400-e29b-41d4-a716-446655440001"); - mockClient.vectorStores.retrieve.mockResolvedValue(mockVectorStore); - mockClient.vectorStores.list.mockResolvedValue({ data: [] }); + mockClient.stores.retrieve.mockResolvedValue(mockStore); + mockClient.stores.list.mockResolvedValue({ data: [] }); - const result = await resolveVectorStore( + const result = await resolveStore( mockClient as unknown as Mixedbread, "myalias" ); - expect(configUtils.resolveVectorStoreName).toHaveBeenCalledWith( - "myalias" - ); - expect(result).toEqual(mockVectorStore); - expect(mockClient.vectorStores.retrieve).toHaveBeenCalledWith( + expect(configUtils.resolveStoreName).toHaveBeenCalledWith("myalias"); + expect(result).toEqual(mockStore); + expect(mockClient.stores.retrieve).toHaveBeenCalledWith( "550e8400-e29b-41d4-a716-446655440001" ); }); it("should handle store not found by ID", async () => { - mockClient.vectorStores.retrieve.mockRejectedValue( + mockClient.stores.retrieve.mockRejectedValue( new Error("Store not found") ); - mockClient.vectorStores.list.mockResolvedValue({ data: [] }); + mockClient.stores.list.mockResolvedValue({ data: [] }); - await resolveVectorStore( + await resolveStore( mockClient as unknown as Mixedbread, "550e8400-e29b-41d4-a716-446655440002" ); @@ -132,18 +130,16 @@ describe("Store Utils", () => { it("should handle store not found by name", async () => { // Mock retrieve to fail first - mockClient.vectorStores.retrieve.mockRejectedValue( - new Error("Not found") - ); + mockClient.stores.retrieve.mockRejectedValue(new Error("Not found")); - mockClient.vectorStores.list.mockResolvedValue({ + mockClient.stores.list.mockResolvedValue({ data: [ { id: "550e8400-e29b-41d4-a716-446655440014", name: "other-store" }, { id: "550e8400-e29b-41d4-a716-446655440015", name: "another-store" }, ], }); - await resolveVectorStore( + await resolveStore( mockClient as unknown as Mixedbread, "nonexistent-store" ); @@ -156,16 +152,11 @@ describe("Store Utils", () => { it("should handle empty store list", async () => { // Mock retrieve to fail first - mockClient.vectorStores.retrieve.mockRejectedValue( - new Error("Not found") - ); + mockClient.stores.retrieve.mockRejectedValue(new Error("Not found")); - mockClient.vectorStores.list.mockResolvedValue({ data: [] }); + mockClient.stores.list.mockResolvedValue({ data: [] }); - await resolveVectorStore( - mockClient as unknown as Mixedbread, - "any-store" - ); + await resolveStore(mockClient as unknown as Mixedbread, "any-store"); expect(console.error).toHaveBeenCalledWith( expect.any(String), @@ -175,93 +166,87 @@ describe("Store Utils", () => { it("should handle API errors when listing", async () => { // Mock retrieve to fail first - mockClient.vectorStores.retrieve.mockRejectedValue( - new Error("Not found") - ); - mockClient.vectorStores.list.mockRejectedValue( + mockClient.stores.retrieve.mockRejectedValue(new Error("Not found")); + mockClient.stores.list.mockRejectedValue( new Error("API Error: Unauthorized") ); await expect( - resolveVectorStore(mockClient as unknown as Mixedbread, "some-store") + resolveStore(mockClient as unknown as Mixedbread, "some-store") ).rejects.toThrow("API Error: Unauthorized"); }); it("should handle case-sensitive name matching", async () => { - const mockVectorStore = { + const mockStore = { id: "550e8400-e29b-41d4-a716-446655440017", name: "mystore", }; // Names are valid identifiers, so retrieve should succeed - mockClient.vectorStores.retrieve.mockResolvedValue(mockVectorStore); + mockClient.stores.retrieve.mockResolvedValue(mockStore); - const result = await resolveVectorStore( + const result = await resolveStore( mockClient as unknown as Mixedbread, "mystore" ); - expect(result).toEqual(mockVectorStore); - expect(mockClient.vectorStores.retrieve).toHaveBeenCalledWith("mystore"); - expect(mockClient.vectorStores.list).not.toHaveBeenCalled(); + expect(result).toEqual(mockStore); + expect(mockClient.stores.retrieve).toHaveBeenCalledWith("mystore"); + expect(mockClient.stores.list).not.toHaveBeenCalled(); }); it("should handle special characters in names", async () => { - const mockVectorStore = { + const mockStore = { id: "550e8400-e29b-41d4-a716-446655440018", name: "my-store_v2.0", }; // Names with special characters are still valid identifiers - mockClient.vectorStores.retrieve.mockResolvedValue(mockVectorStore); + mockClient.stores.retrieve.mockResolvedValue(mockStore); - const result = await resolveVectorStore( + const result = await resolveStore( mockClient as unknown as Mixedbread, "my-store_v2.0" ); - expect(result).toEqual(mockVectorStore); - expect(mockClient.vectorStores.retrieve).toHaveBeenCalledWith( - "my-store_v2.0" - ); - expect(mockClient.vectorStores.list).not.toHaveBeenCalled(); + expect(result).toEqual(mockStore); + expect(mockClient.stores.retrieve).toHaveBeenCalledWith("my-store_v2.0"); + expect(mockClient.stores.list).not.toHaveBeenCalled(); }); it("should check if input looks like UUID before searching by name", async () => { // Input that looks like UUID should try retrieve first, then fall through to name search - mockClient.vectorStores.retrieve.mockRejectedValue( - new Error("Not found") - ); - mockClient.vectorStores.list.mockResolvedValue({ data: [] }); + mockClient.stores.retrieve.mockRejectedValue(new Error("Not found")); + mockClient.stores.list.mockResolvedValue({ data: [] }); - await resolveVectorStore( + await resolveStore( mockClient as unknown as Mixedbread, "550e8400-e29b-41d4-a716-446655440003" ); - expect(mockClient.vectorStores.retrieve).toHaveBeenCalledWith( + expect(mockClient.stores.retrieve).toHaveBeenCalledWith( "550e8400-e29b-41d4-a716-446655440003" ); - expect(mockClient.vectorStores.list).toHaveBeenCalled(); + expect(mockClient.stores.list).toHaveBeenCalled(); }); it("should search by name for non-ID inputs", async () => { - const mockVectorStore = { + const mockStore = { id: "550e8400-e29b-41d4-a716-446655440004", name: "test", }; // Names are valid identifiers, so retrieve should succeed - mockClient.vectorStores.retrieve.mockResolvedValue(mockVectorStore); + mockClient.stores.retrieve.mockResolvedValue(mockStore); - const result = await resolveVectorStore( + const result = await resolveStore( mockClient as unknown as Mixedbread, "test" ); - expect(result).toEqual(mockVectorStore); - expect(mockClient.vectorStores.retrieve).toHaveBeenCalledWith("test"); - expect(mockClient.vectorStores.list).not.toHaveBeenCalled(); + expect(result).toEqual(mockStore); + expect(mockClient.stores.retrieve).toHaveBeenCalledWith("test"); + expect(mockClient.stores.list).not.toHaveBeenCalled(); }); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0a4692a..464e27f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,8 +27,8 @@ importers: packages/cli: dependencies: '@mixedbread/sdk': - specifier: ^0.26.0 - version: 0.26.0 + specifier: ^0.31.1 + version: 0.31.1 '@pnpm/tabtab': specifier: ^0.5.4 version: 0.5.4 @@ -794,6 +794,10 @@ packages: resolution: {integrity: sha512-B0Un0thpz4UKN69QVmgdVsCIgiz+jR4V+nxuCOgdm/P0Iie1NctqY092VQ/iobY/FSMzhHJofXij086y/9E5rg==} hasBin: true + '@mixedbread/sdk@0.31.1': + resolution: {integrity: sha512-cPbArdpdseUlPXAXyofaRpHw+5ePkQU68IsS//cnx9fBaFfBRTrQSUWDRkGoxaJ6cnNoVdGx/DuBO/Cuu5yj7g==} + hasBin: true + '@next/env@15.5.2': resolution: {integrity: sha512-Qe06ew4zt12LeO6N7j8/nULSOe3fMXE4dM6xgpBQNvdzyK1sv5y4oAP3bq4LamrvGCZtmRYnW8URFCeX5nFgGg==} @@ -4283,6 +4287,8 @@ snapshots: '@mixedbread/sdk@0.26.0': {} + '@mixedbread/sdk@0.31.1': {} + '@next/env@15.5.2': {} '@next/swc-darwin-arm64@15.5.2': From d6698259f8dd84ba0723ba0b7a4082f106023059 Mon Sep 17 00:00:00 2001 From: Naing Linn Khant Date: Thu, 2 Oct 2025 02:11:43 +0700 Subject: [PATCH 02/10] chore: update file/folder names --- packages/cli/README.md | 16 ++++++++-------- packages/cli/src/bin/mxbai.ts | 2 +- .../commands/{vector-store => store}/create.ts | 0 .../commands/{vector-store => store}/delete.ts | 2 +- .../{vector-store => store}/files/delete.ts | 2 +- .../{vector-store => store}/files/get.ts | 2 +- .../{vector-store => store}/files/index.ts | 0 .../{vector-store => store}/files/list.ts | 2 +- .../src/commands/{vector-store => store}/get.ts | 2 +- .../commands/{vector-store => store}/index.ts | 0 .../src/commands/{vector-store => store}/list.ts | 0 .../src/commands/{vector-store => store}/qa.ts | 2 +- .../commands/{vector-store => store}/search.ts | 2 +- .../src/commands/{vector-store => store}/sync.ts | 2 +- .../commands/{vector-store => store}/update.ts | 2 +- .../commands/{vector-store => store}/upload.ts | 2 +- packages/cli/src/index.ts | 2 +- packages/cli/src/utils/manifest.ts | 4 ++-- .../cli/src/utils/{vector-store.ts => store.ts} | 0 packages/cli/src/utils/sync-state.ts | 2 +- .../{vector-store => store}/create.test.ts | 2 +- .../{vector-store => store}/delete.test.ts | 6 +++--- .../{vector-store => store}/files.test.ts | 6 +++--- .../commands/{vector-store => store}/get.test.ts | 6 +++--- .../{vector-store => store}/list.test.ts | 2 +- .../commands/{vector-store => store}/qa.test.ts | 6 +++--- .../{vector-store => store}/search.test.ts | 6 +++--- .../{vector-store => store}/sync.test.ts | 6 +++--- .../{vector-store => store}/update.test.ts | 6 +++--- .../{vector-store => store}/upload.test.ts | 6 +++--- packages/cli/tests/utils/vector-store.test.ts | 2 +- 31 files changed, 50 insertions(+), 50 deletions(-) rename packages/cli/src/commands/{vector-store => store}/create.ts (100%) rename packages/cli/src/commands/{vector-store => store}/delete.ts (97%) rename packages/cli/src/commands/{vector-store => store}/files/delete.ts (97%) rename packages/cli/src/commands/{vector-store => store}/files/get.ts (97%) rename packages/cli/src/commands/{vector-store => store}/files/index.ts (100%) rename packages/cli/src/commands/{vector-store => store}/files/list.ts (98%) rename packages/cli/src/commands/{vector-store => store}/get.ts (97%) rename packages/cli/src/commands/{vector-store => store}/index.ts (100%) rename packages/cli/src/commands/{vector-store => store}/list.ts (100%) rename packages/cli/src/commands/{vector-store => store}/qa.ts (98%) rename packages/cli/src/commands/{vector-store => store}/search.ts (98%) rename packages/cli/src/commands/{vector-store => store}/sync.ts (99%) rename packages/cli/src/commands/{vector-store => store}/update.ts (98%) rename packages/cli/src/commands/{vector-store => store}/upload.ts (99%) rename packages/cli/src/utils/{vector-store.ts => store.ts} (100%) rename packages/cli/tests/commands/{vector-store => store}/create.test.ts (99%) rename packages/cli/tests/commands/{vector-store => store}/delete.test.ts (96%) rename packages/cli/tests/commands/{vector-store => store}/files.test.ts (98%) rename packages/cli/tests/commands/{vector-store => store}/get.test.ts (97%) rename packages/cli/tests/commands/{vector-store => store}/list.test.ts (99%) rename packages/cli/tests/commands/{vector-store => store}/qa.test.ts (99%) rename packages/cli/tests/commands/{vector-store => store}/search.test.ts (98%) rename packages/cli/tests/commands/{vector-store => store}/sync.test.ts (96%) rename packages/cli/tests/commands/{vector-store => store}/update.test.ts (98%) rename packages/cli/tests/commands/{vector-store => store}/upload.test.ts (99%) diff --git a/packages/cli/README.md b/packages/cli/README.md index 2b6863e..daf8f92 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -443,7 +443,7 @@ This CLI is built on top of the `@mixedbread/sdk` and provides a convenient comm pnpm test # Run specific test file - pnpm test tests/commands/vector-store/upload.test.ts + pnpm test tests/commands/store/upload.test.ts # Run tests in watch mode pnpm test --watch @@ -467,7 +467,7 @@ src/ │ │ ├── get.ts # Get config values │ │ ├── set.ts # Set config values │ │ └── index.ts -│ └── vector-store/ # Store commands +│ └── store/ # Store commands │ ├── files/ # File management subcommands │ │ ├── delete.ts │ │ ├── get.ts @@ -496,7 +496,7 @@ src/ │ ├── sync-state.ts # Sync state management │ ├── sync.ts # Sync logic │ ├── upload.ts # Upload utilities -│ └── vector-store.ts # Store helpers +│ └── store.ts # Store helpers └── index.ts # Package exports tests/ @@ -507,10 +507,10 @@ tests/ #### Adding New Commands -1. **Create command file** in `src/commands/vector-store/`: +1. **Create command file** in `src/commands/store/`: ```typescript - // src/commands/vector-store/my-command.ts + // src/commands/store/my-command.ts import { Command } from 'commander'; import { addGlobalOptions } from '../../utils/global-options'; @@ -526,7 +526,7 @@ tests/ } ``` -2. **Register command** in `src/commands/vector-store/index.ts`: +2. **Register command** in `src/commands/store/index.ts`: ```typescript import { createMyCommand } from './my-command'; @@ -535,10 +535,10 @@ tests/ storeCommand.addCommand(createMyCommand()); ``` -3. **Add tests** in `tests/commands/vector-store/my-command.test.ts`: +3. **Add tests** in `tests/commands/store/my-command.test.ts`: ```typescript - import { createMyCommand } from '../../../src/commands/vector-store/my-command'; + import { createMyCommand } from '../../../src/commands/store/my-command'; describe('My Command', () => { it('should work correctly', async () => { diff --git a/packages/cli/src/bin/mxbai.ts b/packages/cli/src/bin/mxbai.ts index 02f3104..099cb37 100644 --- a/packages/cli/src/bin/mxbai.ts +++ b/packages/cli/src/bin/mxbai.ts @@ -32,7 +32,7 @@ import { createCompletionServerCommand, } from "../commands/completion"; import { createConfigCommand } from "../commands/config"; -import { createStoreCommand } from "../commands/vector-store"; +import { createStoreCommand } from "../commands/store"; import { setupGlobalOptions } from "../utils/global-options"; const program = new Command(); diff --git a/packages/cli/src/commands/vector-store/create.ts b/packages/cli/src/commands/store/create.ts similarity index 100% rename from packages/cli/src/commands/vector-store/create.ts rename to packages/cli/src/commands/store/create.ts diff --git a/packages/cli/src/commands/vector-store/delete.ts b/packages/cli/src/commands/store/delete.ts similarity index 97% rename from packages/cli/src/commands/vector-store/delete.ts rename to packages/cli/src/commands/store/delete.ts index 3886a66..9cc6994 100644 --- a/packages/cli/src/commands/vector-store/delete.ts +++ b/packages/cli/src/commands/store/delete.ts @@ -15,7 +15,7 @@ import { mergeCommandOptions, parseOptions, } from "../../utils/global-options"; -import { resolveStore } from "../../utils/vector-store"; +import { resolveStore } from "../../utils/store"; const DeleteStoreSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), diff --git a/packages/cli/src/commands/vector-store/files/delete.ts b/packages/cli/src/commands/store/files/delete.ts similarity index 97% rename from packages/cli/src/commands/vector-store/files/delete.ts rename to packages/cli/src/commands/store/files/delete.ts index d2d8747..24841b7 100644 --- a/packages/cli/src/commands/vector-store/files/delete.ts +++ b/packages/cli/src/commands/store/files/delete.ts @@ -11,7 +11,7 @@ import { mergeCommandOptions, parseOptions, } from "../../../utils/global-options"; -import { resolveStore } from "../../../utils/vector-store"; +import { resolveStore } from "../../../utils/store"; const DeleteFileSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), diff --git a/packages/cli/src/commands/vector-store/files/get.ts b/packages/cli/src/commands/store/files/get.ts similarity index 97% rename from packages/cli/src/commands/vector-store/files/get.ts rename to packages/cli/src/commands/store/files/get.ts index 6ee82bb..5d1c671 100644 --- a/packages/cli/src/commands/vector-store/files/get.ts +++ b/packages/cli/src/commands/store/files/get.ts @@ -11,7 +11,7 @@ import { parseOptions, } from "../../../utils/global-options"; import { formatBytes, formatOutput } from "../../../utils/output"; -import { resolveStore } from "../../../utils/vector-store"; +import { resolveStore } from "../../../utils/store"; const GetFileSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), diff --git a/packages/cli/src/commands/vector-store/files/index.ts b/packages/cli/src/commands/store/files/index.ts similarity index 100% rename from packages/cli/src/commands/vector-store/files/index.ts rename to packages/cli/src/commands/store/files/index.ts diff --git a/packages/cli/src/commands/vector-store/files/list.ts b/packages/cli/src/commands/store/files/list.ts similarity index 98% rename from packages/cli/src/commands/vector-store/files/list.ts rename to packages/cli/src/commands/store/files/list.ts index c413e9c..bf01b6b 100644 --- a/packages/cli/src/commands/vector-store/files/list.ts +++ b/packages/cli/src/commands/store/files/list.ts @@ -15,7 +15,7 @@ import { formatCountWithSuffix, formatOutput, } from "../../../utils/output"; -import { resolveStore } from "../../../utils/vector-store"; +import { resolveStore } from "../../../utils/store"; import type { FilesOptions } from "."; const ListFilesSchema = extendGlobalOptions({ diff --git a/packages/cli/src/commands/vector-store/get.ts b/packages/cli/src/commands/store/get.ts similarity index 97% rename from packages/cli/src/commands/vector-store/get.ts rename to packages/cli/src/commands/store/get.ts index 21855f1..ccdc3b1 100644 --- a/packages/cli/src/commands/vector-store/get.ts +++ b/packages/cli/src/commands/store/get.ts @@ -11,7 +11,7 @@ import { parseOptions, } from "../../utils/global-options"; import { formatBytes, formatOutput } from "../../utils/output"; -import { resolveStore } from "../../utils/vector-store"; +import { resolveStore } from "../../utils/store"; const GetStoreSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), diff --git a/packages/cli/src/commands/vector-store/index.ts b/packages/cli/src/commands/store/index.ts similarity index 100% rename from packages/cli/src/commands/vector-store/index.ts rename to packages/cli/src/commands/store/index.ts diff --git a/packages/cli/src/commands/vector-store/list.ts b/packages/cli/src/commands/store/list.ts similarity index 100% rename from packages/cli/src/commands/vector-store/list.ts rename to packages/cli/src/commands/store/list.ts diff --git a/packages/cli/src/commands/vector-store/qa.ts b/packages/cli/src/commands/store/qa.ts similarity index 98% rename from packages/cli/src/commands/vector-store/qa.ts rename to packages/cli/src/commands/store/qa.ts index 1a32fae..eeb9f9d 100644 --- a/packages/cli/src/commands/vector-store/qa.ts +++ b/packages/cli/src/commands/store/qa.ts @@ -12,7 +12,7 @@ import { parseOptions, } from "../../utils/global-options"; import { formatOutput } from "../../utils/output"; -import { resolveStore } from "../../utils/vector-store"; +import { resolveStore } from "../../utils/store"; const QAStoreSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), diff --git a/packages/cli/src/commands/vector-store/search.ts b/packages/cli/src/commands/store/search.ts similarity index 98% rename from packages/cli/src/commands/vector-store/search.ts rename to packages/cli/src/commands/store/search.ts index 37f9a15..6698b76 100644 --- a/packages/cli/src/commands/vector-store/search.ts +++ b/packages/cli/src/commands/store/search.ts @@ -13,7 +13,7 @@ import { parseOptions, } from "../../utils/global-options"; import { formatCountWithSuffix, formatOutput } from "../../utils/output"; -import { resolveStore } from "../../utils/vector-store"; +import { resolveStore } from "../../utils/store"; const SearchStoreSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), diff --git a/packages/cli/src/commands/vector-store/sync.ts b/packages/cli/src/commands/store/sync.ts similarity index 99% rename from packages/cli/src/commands/vector-store/sync.ts rename to packages/cli/src/commands/store/sync.ts index 143b219..63dfd7f 100644 --- a/packages/cli/src/commands/vector-store/sync.ts +++ b/packages/cli/src/commands/store/sync.ts @@ -14,6 +14,7 @@ import { } from "../../utils/global-options"; import { validateMetadata } from "../../utils/metadata"; import { formatBytes, formatCountWithSuffix } from "../../utils/output"; +import { resolveStore } from "../../utils/store"; import { analyzeChanges, displaySyncResultsSummary, @@ -21,7 +22,6 @@ import { formatChangeSummary, } from "../../utils/sync"; import { getSyncedFiles } from "../../utils/sync-state"; -import { resolveStore } from "../../utils/vector-store"; const SyncStoreSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), diff --git a/packages/cli/src/commands/vector-store/update.ts b/packages/cli/src/commands/store/update.ts similarity index 98% rename from packages/cli/src/commands/vector-store/update.ts rename to packages/cli/src/commands/store/update.ts index 55a8f96..e73fc61 100644 --- a/packages/cli/src/commands/vector-store/update.ts +++ b/packages/cli/src/commands/store/update.ts @@ -17,7 +17,7 @@ import { } from "../../utils/global-options"; import { validateMetadata } from "../../utils/metadata"; import { formatOutput } from "../../utils/output"; -import { resolveStore } from "../../utils/vector-store"; +import { resolveStore } from "../../utils/store"; const UpdateStoreSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), diff --git a/packages/cli/src/commands/vector-store/upload.ts b/packages/cli/src/commands/store/upload.ts similarity index 99% rename from packages/cli/src/commands/vector-store/upload.ts rename to packages/cli/src/commands/store/upload.ts index e4b1fd8..1e675bb 100644 --- a/packages/cli/src/commands/vector-store/upload.ts +++ b/packages/cli/src/commands/store/upload.ts @@ -17,8 +17,8 @@ import { import { uploadFromManifest } from "../../utils/manifest"; import { validateMetadata } from "../../utils/metadata"; import { formatBytes, formatCountWithSuffix } from "../../utils/output"; +import { getStoreFiles, resolveStore } from "../../utils/store"; import { type FileToUpload, uploadFilesInBatch } from "../../utils/upload"; -import { getStoreFiles, resolveStore } from "../../utils/vector-store"; const UploadStoreSchema = extendGlobalOptions({ nameOrId: z.string().min(1, { error: '"name-or-id" is required' }), diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 283d775..c9e1428 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,5 +1,5 @@ // Main CLI exports -export { createStoreCommand } from "./commands/vector-store"; +export { createStoreCommand } from "./commands/store"; export { createClient } from "./utils/client"; export { getApiKey, loadConfig, saveConfig } from "./utils/config"; export { formatOutput } from "./utils/output"; diff --git a/packages/cli/src/utils/manifest.ts b/packages/cli/src/utils/manifest.ts index feb022e..83ebcd3 100644 --- a/packages/cli/src/utils/manifest.ts +++ b/packages/cli/src/utils/manifest.ts @@ -5,12 +5,12 @@ import { glob } from "glob"; import ora from "ora"; import { parse } from "yaml"; import { z } from "zod"; -import type { UploadOptions } from "../commands/vector-store/upload"; +import type { UploadOptions } from "../commands/store/upload"; import { loadConfig } from "./config"; import { validateMetadata } from "./metadata"; import { formatBytes, formatCountWithSuffix } from "./output"; +import { getStoreFiles } from "./store"; import { type FileToUpload, uploadFilesInBatch } from "./upload"; -import { getStoreFiles } from "./vector-store"; // Manifest file schema const ManifestFileEntrySchema = z.object({ diff --git a/packages/cli/src/utils/vector-store.ts b/packages/cli/src/utils/store.ts similarity index 100% rename from packages/cli/src/utils/vector-store.ts rename to packages/cli/src/utils/store.ts diff --git a/packages/cli/src/utils/sync-state.ts b/packages/cli/src/utils/sync-state.ts index bdccd66..fa248b4 100644 --- a/packages/cli/src/utils/sync-state.ts +++ b/packages/cli/src/utils/sync-state.ts @@ -1,5 +1,5 @@ import type Mixedbread from "@mixedbread/sdk"; -import { getStoreFiles } from "./vector-store"; +import { getStoreFiles } from "./store"; export interface FileSyncMetadata { file_path: string; diff --git a/packages/cli/tests/commands/vector-store/create.test.ts b/packages/cli/tests/commands/store/create.test.ts similarity index 99% rename from packages/cli/tests/commands/vector-store/create.test.ts rename to packages/cli/tests/commands/store/create.test.ts index cc52559..3c10462 100644 --- a/packages/cli/tests/commands/vector-store/create.test.ts +++ b/packages/cli/tests/commands/store/create.test.ts @@ -8,7 +8,7 @@ import { } from "@jest/globals"; import type Mixedbread from "@mixedbread/sdk"; import type { Command } from "commander"; -import { createCreateCommand } from "../../../src/commands/vector-store/create"; +import { createCreateCommand } from "../../../src/commands/store/create"; import * as clientUtils from "../../../src/utils/client"; import * as outputUtils from "../../../src/utils/output"; diff --git a/packages/cli/tests/commands/vector-store/delete.test.ts b/packages/cli/tests/commands/store/delete.test.ts similarity index 96% rename from packages/cli/tests/commands/vector-store/delete.test.ts rename to packages/cli/tests/commands/store/delete.test.ts index 8e60a1c..26afe0d 100644 --- a/packages/cli/tests/commands/vector-store/delete.test.ts +++ b/packages/cli/tests/commands/store/delete.test.ts @@ -8,13 +8,13 @@ import { } from "@jest/globals"; import type Mixedbread from "@mixedbread/sdk"; import type { Command } from "commander"; -import { createDeleteCommand } from "../../../src/commands/vector-store/delete"; +import { createDeleteCommand } from "../../../src/commands/store/delete"; import * as clientUtils from "../../../src/utils/client"; -import * as storeUtils from "../../../src/utils/vector-store"; +import * as storeUtils from "../../../src/utils/store"; // Mock dependencies jest.mock("../../../src/utils/client"); -jest.mock("../../../src/utils/vector-store"); +jest.mock("../../../src/utils/store"); // Explicit mock definitions const mockCreateClient = clientUtils.createClient as jest.MockedFunction< diff --git a/packages/cli/tests/commands/vector-store/files.test.ts b/packages/cli/tests/commands/store/files.test.ts similarity index 98% rename from packages/cli/tests/commands/vector-store/files.test.ts rename to packages/cli/tests/commands/store/files.test.ts index f5d0747..bd78439 100644 --- a/packages/cli/tests/commands/vector-store/files.test.ts +++ b/packages/cli/tests/commands/store/files.test.ts @@ -10,14 +10,14 @@ import type Mixedbread from "@mixedbread/sdk"; import type { StoreFile } from "@mixedbread/sdk/resources/stores"; import type { FileListResponse } from "@mixedbread/sdk/resources/stores/files"; import type { Command } from "commander"; -import { createFilesCommand } from "../../../src/commands/vector-store/files"; +import { createFilesCommand } from "../../../src/commands/store/files"; import * as clientUtils from "../../../src/utils/client"; import * as outputUtils from "../../../src/utils/output"; -import * as storeUtils from "../../../src/utils/vector-store"; +import * as storeUtils from "../../../src/utils/store"; // Mock dependencies jest.mock("../../../src/utils/client"); -jest.mock("../../../src/utils/vector-store"); +jest.mock("../../../src/utils/store"); jest.mock("../../../src/utils/output", () => ({ ...(jest.requireActual("../../../src/utils/output") as object), formatOutput: jest.fn(), diff --git a/packages/cli/tests/commands/vector-store/get.test.ts b/packages/cli/tests/commands/store/get.test.ts similarity index 97% rename from packages/cli/tests/commands/vector-store/get.test.ts rename to packages/cli/tests/commands/store/get.test.ts index f8c0721..c74c0d7 100644 --- a/packages/cli/tests/commands/vector-store/get.test.ts +++ b/packages/cli/tests/commands/store/get.test.ts @@ -8,14 +8,14 @@ import { } from "@jest/globals"; import type Mixedbread from "@mixedbread/sdk"; import type { Command } from "commander"; -import { createGetCommand } from "../../../src/commands/vector-store/get"; +import { createGetCommand } from "../../../src/commands/store/get"; import * as clientUtils from "../../../src/utils/client"; import * as outputUtils from "../../../src/utils/output"; -import * as storeUtils from "../../../src/utils/vector-store"; +import * as storeUtils from "../../../src/utils/store"; // Mock dependencies jest.mock("../../../src/utils/client"); -jest.mock("../../../src/utils/vector-store"); +jest.mock("../../../src/utils/store"); jest.mock("../../../src/utils/output", () => ({ ...(jest.requireActual("../../../src/utils/output") as object), formatOutput: jest.fn(), diff --git a/packages/cli/tests/commands/vector-store/list.test.ts b/packages/cli/tests/commands/store/list.test.ts similarity index 99% rename from packages/cli/tests/commands/vector-store/list.test.ts rename to packages/cli/tests/commands/store/list.test.ts index 0758409..ea8f479 100644 --- a/packages/cli/tests/commands/vector-store/list.test.ts +++ b/packages/cli/tests/commands/store/list.test.ts @@ -10,7 +10,7 @@ import type Mixedbread from "@mixedbread/sdk"; import type { CursorResponse } from "@mixedbread/sdk/core/pagination.mjs"; import type { Store } from "@mixedbread/sdk/resources/index.mjs"; import type { Command } from "commander"; -import { createListCommand } from "../../../src/commands/vector-store/list"; +import { createListCommand } from "../../../src/commands/store/list"; import * as clientUtils from "../../../src/utils/client"; import * as outputUtils from "../../../src/utils/output"; diff --git a/packages/cli/tests/commands/vector-store/qa.test.ts b/packages/cli/tests/commands/store/qa.test.ts similarity index 99% rename from packages/cli/tests/commands/vector-store/qa.test.ts rename to packages/cli/tests/commands/store/qa.test.ts index d4f9a78..7e16029 100644 --- a/packages/cli/tests/commands/vector-store/qa.test.ts +++ b/packages/cli/tests/commands/store/qa.test.ts @@ -9,15 +9,15 @@ import { import type Mixedbread from "@mixedbread/sdk"; import type { StoreQuestionAnsweringResponse } from "@mixedbread/sdk/resources"; import type { Command } from "commander"; -import { createQACommand } from "../../../src/commands/vector-store/qa"; +import { createQACommand } from "../../../src/commands/store/qa"; import * as clientUtils from "../../../src/utils/client"; import * as configUtils from "../../../src/utils/config"; import * as outputUtils from "../../../src/utils/output"; -import * as storeUtils from "../../../src/utils/vector-store"; +import * as storeUtils from "../../../src/utils/store"; // Mock dependencies jest.mock("../../../src/utils/client"); -jest.mock("../../../src/utils/vector-store"); +jest.mock("../../../src/utils/store"); jest.mock("../../../src/utils/output", () => ({ ...(jest.requireActual("../../../src/utils/output") as object), formatOutput: jest.fn(), diff --git a/packages/cli/tests/commands/vector-store/search.test.ts b/packages/cli/tests/commands/store/search.test.ts similarity index 98% rename from packages/cli/tests/commands/vector-store/search.test.ts rename to packages/cli/tests/commands/store/search.test.ts index 3bf0d83..5f99262 100644 --- a/packages/cli/tests/commands/vector-store/search.test.ts +++ b/packages/cli/tests/commands/store/search.test.ts @@ -10,15 +10,15 @@ import type Mixedbread from "@mixedbread/sdk"; import type { StoreSearchResponse } from "@mixedbread/sdk/resources/index.mjs"; import type { FileSearchResponse } from "@mixedbread/sdk/resources/stores.mjs"; import type { Command } from "commander"; -import { createSearchCommand } from "../../../src/commands/vector-store/search"; +import { createSearchCommand } from "../../../src/commands/store/search"; import * as clientUtils from "../../../src/utils/client"; import * as configUtils from "../../../src/utils/config"; import * as outputUtils from "../../../src/utils/output"; -import * as storeUtils from "../../../src/utils/vector-store"; +import * as storeUtils from "../../../src/utils/store"; // Mock dependencies jest.mock("../../../src/utils/client"); -jest.mock("../../../src/utils/vector-store"); +jest.mock("../../../src/utils/store"); jest.mock("../../../src/utils/output", () => ({ ...(jest.requireActual("../../../src/utils/output") as object), formatOutput: jest.fn(), diff --git a/packages/cli/tests/commands/vector-store/sync.test.ts b/packages/cli/tests/commands/store/sync.test.ts similarity index 96% rename from packages/cli/tests/commands/vector-store/sync.test.ts rename to packages/cli/tests/commands/store/sync.test.ts index 29847cc..772c237 100644 --- a/packages/cli/tests/commands/vector-store/sync.test.ts +++ b/packages/cli/tests/commands/store/sync.test.ts @@ -9,17 +9,17 @@ import { import type Mixedbread from "@mixedbread/sdk"; import type { Command } from "commander"; import mockFs from "mock-fs"; -import { createSyncCommand } from "../../../src/commands/vector-store/sync"; +import { createSyncCommand } from "../../../src/commands/store/sync"; import * as clientUtils from "../../../src/utils/client"; +import * as storeUtils from "../../../src/utils/store"; import * as syncUtils from "../../../src/utils/sync"; import * as syncStateUtils from "../../../src/utils/sync-state"; -import * as storeUtils from "../../../src/utils/vector-store"; // Mock dependencies jest.mock("../../../src/utils/client"); jest.mock("../../../src/utils/sync"); jest.mock("../../../src/utils/sync-state"); -jest.mock("../../../src/utils/vector-store"); +jest.mock("../../../src/utils/store"); // Explicit mock definitions const mockCreateClient = clientUtils.createClient as jest.MockedFunction< diff --git a/packages/cli/tests/commands/vector-store/update.test.ts b/packages/cli/tests/commands/store/update.test.ts similarity index 98% rename from packages/cli/tests/commands/vector-store/update.test.ts rename to packages/cli/tests/commands/store/update.test.ts index 13d404f..bea0ba5 100644 --- a/packages/cli/tests/commands/vector-store/update.test.ts +++ b/packages/cli/tests/commands/store/update.test.ts @@ -8,14 +8,14 @@ import { } from "@jest/globals"; import type Mixedbread from "@mixedbread/sdk"; import type { Command } from "commander"; -import { createUpdateCommand } from "../../../src/commands/vector-store/update"; +import { createUpdateCommand } from "../../../src/commands/store/update"; import * as clientUtils from "../../../src/utils/client"; import * as outputUtils from "../../../src/utils/output"; -import * as storeUtils from "../../../src/utils/vector-store"; +import * as storeUtils from "../../../src/utils/store"; // Mock dependencies jest.mock("../../../src/utils/client"); -jest.mock("../../../src/utils/vector-store"); +jest.mock("../../../src/utils/store"); jest.mock("../../../src/utils/output", () => ({ ...(jest.requireActual("../../../src/utils/output") as object), formatOutput: jest.fn(), diff --git a/packages/cli/tests/commands/vector-store/upload.test.ts b/packages/cli/tests/commands/store/upload.test.ts similarity index 99% rename from packages/cli/tests/commands/vector-store/upload.test.ts rename to packages/cli/tests/commands/store/upload.test.ts index 8ecc2b1..dcf2e53 100644 --- a/packages/cli/tests/commands/vector-store/upload.test.ts +++ b/packages/cli/tests/commands/store/upload.test.ts @@ -10,11 +10,11 @@ import type Mixedbread from "@mixedbread/sdk"; import type { Command } from "commander"; import { glob } from "glob"; import mockFs from "mock-fs"; -import { createUploadCommand } from "../../../src/commands/vector-store/upload"; +import { createUploadCommand } from "../../../src/commands/store/upload"; import * as clientUtils from "../../../src/utils/client"; import * as configUtils from "../../../src/utils/config"; +import * as storeUtils from "../../../src/utils/store"; import * as uploadUtils from "../../../src/utils/upload"; -import * as storeUtils from "../../../src/utils/vector-store"; import { createMockConfig, @@ -24,7 +24,7 @@ import { // Mock dependencies jest.mock("../../../src/utils/client"); -jest.mock("../../../src/utils/vector-store"); +jest.mock("../../../src/utils/store"); jest.mock("../../../src/utils/config"); jest.mock("../../../src/utils/upload"); jest.mock("glob"); diff --git a/packages/cli/tests/utils/vector-store.test.ts b/packages/cli/tests/utils/vector-store.test.ts index 6c25f4d..b2db558 100644 --- a/packages/cli/tests/utils/vector-store.test.ts +++ b/packages/cli/tests/utils/vector-store.test.ts @@ -9,7 +9,7 @@ import { } from "@jest/globals"; import type { Mixedbread } from "@mixedbread/sdk"; import * as configUtils from "../../src/utils/config"; -import { resolveStore } from "../../src/utils/vector-store"; +import { resolveStore } from "../../src/utils/store"; // Mock config utils jest.mock("../../src/utils/config"); From ff44c19d72a26c686997ba27015f851ed01a8dcf Mon Sep 17 00:00:00 2001 From: Naing Linn Khant Date: Thu, 2 Oct 2025 02:32:07 +0700 Subject: [PATCH 03/10] chore: add changeset and update changelog --- .changeset/free-places-visit.md | 5 +++++ packages/cli/CHANGELOG.md | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 .changeset/free-places-visit.md diff --git a/.changeset/free-places-visit.md b/.changeset/free-places-visit.md new file mode 100644 index 0000000..d821bd6 --- /dev/null +++ b/.changeset/free-places-visit.md @@ -0,0 +1,5 @@ +--- +"@mixedbread/cli": patch +--- + +Updated mixedbread SDK version diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 6d43b0c..bcf7b71 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -4,7 +4,23 @@ ### Major Changes -- aa54a04: Update `vs` to `store` in all commands +- aa54a04: Renamed command structure from `vs` to `store` for clarity + + ## Breaking Changes + + All `vs` commands have been renamed to `store`: + + - `mxbai vs` → `mxbai store` + - `mxbai vs create` → `mxbai store create` + - `mxbai vs list` → `mxbai store list` + - `mxbai vs get` → `mxbai store get` + - `mxbai vs update` → `mxbai store update` + - `mxbai vs delete` → `mxbai store delete` + - `mxbai vs files` → `mxbai store files` + - `mxbai vs sync` → `mxbai store sync` + - `mxbai vs upload` → `mxbai store upload` + - `mxbai vs search` → `mxbai store search` + - `mxbai vs qa` → `mxbai store qa` ## 1.2.1 From 0c2dae01d9bdb33ef8cf9801311479e893b54e58 Mon Sep 17 00:00:00 2001 From: Naing Linn Khant Date: Thu, 2 Oct 2025 02:44:34 +0700 Subject: [PATCH 04/10] docs: update changelog --- packages/cli/CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index bcf7b71..e280fe1 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -6,7 +6,7 @@ - aa54a04: Renamed command structure from `vs` to `store` for clarity - ## Breaking Changes + ### Breaking Changes All `vs` commands have been renamed to `store`: @@ -46,7 +46,7 @@ - **Multi-key support**: Completions work seamlessly with multiple API keys, showing stores for the current default key - **New command**: `mxbai completion refresh` to manually refresh the completion cache - Breaking changes: + ### Breaking changes - Removed undocumented `vector-store` alias (use `vs` instead) @@ -62,15 +62,15 @@ - 617f988: Added force upload option and standardize CLI flags (breaking changes) - ## Breaking Changes + ### Breaking Changes - ### Sync Command Changes + #### Sync Command Changes - **REMOVED**: `--ci` flag - use `--yes/-y` instead for non-interactive mode - **CHANGED**: `--force/-f` behavior - now forces re-upload of all files (ignoring change detection) - **NEW**: `--yes/-y` flag - skips confirmation prompts (replaces old `--force` behavior) - ### Flag Standardization Across Commands + #### Flag Standardization Across Commands All confirmation-skipping flags have been standardized from `--force` to `--yes/-y`: From 36beec355d133b8eed4dbea6ed0270391ec224e2 Mon Sep 17 00:00:00 2001 From: Naing Linn Khant Date: Fri, 3 Oct 2025 19:31:58 +0700 Subject: [PATCH 05/10] feat: improve unknown command error message --- packages/cli/src/bin/mxbai.ts | 43 ++++++----- packages/cli/src/commands/config/index.ts | 12 ++- .../cli/src/commands/store/files/index.ts | 11 ++- packages/cli/src/commands/store/index.ts | 12 ++- packages/cli/src/utils/command-suggestions.ts | 77 +++++++++++++++++++ 5 files changed, 127 insertions(+), 28 deletions(-) create mode 100644 packages/cli/src/utils/command-suggestions.ts diff --git a/packages/cli/src/bin/mxbai.ts b/packages/cli/src/bin/mxbai.ts index 099cb37..08bbad2 100644 --- a/packages/cli/src/bin/mxbai.ts +++ b/packages/cli/src/bin/mxbai.ts @@ -4,6 +4,14 @@ import { readFileSync } from "node:fs"; import { join } from "node:path"; import chalk from "chalk"; import { Command } from "commander"; +import { + createCompletionCommand, + createCompletionServerCommand, +} from "../commands/completion"; +import { createConfigCommand } from "../commands/config"; +import { createStoreCommand } from "../commands/store"; +import { formatUnknownCommandError } from "../utils/command-suggestions"; +import { setupGlobalOptions } from "../utils/global-options"; // Find package.json relative to the compiled file location // In the published package, from bin/mxbai.js, package.json is one level up @@ -27,35 +35,24 @@ try { } } -import { - createCompletionCommand, - createCompletionServerCommand, -} from "../commands/completion"; -import { createConfigCommand } from "../commands/config"; -import { createStoreCommand } from "../commands/store"; -import { setupGlobalOptions } from "../utils/global-options"; - const program = new Command(); program .name("mxbai") .description("CLI tool for managing the Mixedbread platform.") - .version(version) - .allowExcessArguments(false); + .version(version); setupGlobalOptions(program); +// Configure command handling +program.showHelpAfterError(); + // Add commands program.addCommand(createStoreCommand()); program.addCommand(createConfigCommand()); program.addCommand(createCompletionCommand()); program.addCommand(createCompletionServerCommand()); -// Show help without error exit code when no command provided -program.action(() => { - program.help(); -}); - // Global error handling program.on("error", (error: Error) => { console.error(chalk.red("\n✗"), error.message); @@ -67,16 +64,22 @@ program.on("error", (error: Error) => { // Handle unknown commands program.on("command:*", () => { - console.error( - chalk.red("\n✗"), - `Unknown command: ${program.args.join(" ")}\n` - ); - program.help(); + const unknownCommand = program.args[0]; + const availableCommands = program.commands + .map((cmd) => cmd.name()) + .filter((name) => name !== "completion-server"); + console.error(formatUnknownCommandError(unknownCommand, availableCommands)); + process.exit(1); }); // Parse arguments async function main() { try { + // Show help if no arguments provided + if (process.argv.length === 2) { + program.help(); + } + await program.parseAsync(process.argv); } catch (error) { if (error instanceof Error) { diff --git a/packages/cli/src/commands/config/index.ts b/packages/cli/src/commands/config/index.ts index 0b75812..96fdba7 100644 --- a/packages/cli/src/commands/config/index.ts +++ b/packages/cli/src/commands/config/index.ts @@ -1,4 +1,5 @@ import { Command } from "commander"; +import { formatUnknownCommandError } from "../../utils/command-suggestions"; import { createGetCommand } from "./get"; import { createKeysCommand } from "./keys"; import { createSetCommand } from "./set"; @@ -8,13 +9,18 @@ export function createConfigCommand(): Command { "Manage CLI configuration" ); + configCommand.showHelpAfterError(); + configCommand.addCommand(createSetCommand()); configCommand.addCommand(createGetCommand()); configCommand.addCommand(createKeysCommand()); - // Show help without error exit code when no subcommand provided - configCommand.action(() => { - configCommand.help(); + // Handle unknown subcommands + configCommand.on("command:*", () => { + const unknownCommand = configCommand.args[0]; + const availableCommands = configCommand.commands.map((cmd) => cmd.name()); + console.error(formatUnknownCommandError(unknownCommand, availableCommands)); + process.exit(1); }); return configCommand; diff --git a/packages/cli/src/commands/store/files/index.ts b/packages/cli/src/commands/store/files/index.ts index dace415..f907c4a 100644 --- a/packages/cli/src/commands/store/files/index.ts +++ b/packages/cli/src/commands/store/files/index.ts @@ -1,4 +1,5 @@ import { Command } from "commander"; +import { formatUnknownCommandError } from "../../../utils/command-suggestions"; import type { GlobalOptions } from "../../../utils/global-options"; import { createDeleteCommand } from "./delete"; import { createGetCommand } from "./get"; @@ -14,13 +15,19 @@ export function createFilesCommand(): Command { "Manage files in stores" ); + filesCommand.showHelpAfterError(); + // Add subcommands filesCommand.addCommand(createListCommand()); filesCommand.addCommand(createGetCommand()); filesCommand.addCommand(createDeleteCommand()); - filesCommand.action(() => { - filesCommand.help(); + // Handle unknown subcommands + filesCommand.on("command:*", () => { + const unknownCommand = filesCommand.args[0]; + const availableCommands = filesCommand.commands.map((cmd) => cmd.name()); + console.error(formatUnknownCommandError(unknownCommand, availableCommands)); + process.exit(1); }); return filesCommand; diff --git a/packages/cli/src/commands/store/index.ts b/packages/cli/src/commands/store/index.ts index c0c4a1d..52ce711 100644 --- a/packages/cli/src/commands/store/index.ts +++ b/packages/cli/src/commands/store/index.ts @@ -1,4 +1,5 @@ import { Command } from "commander"; +import { formatUnknownCommandError } from "../../utils/command-suggestions"; import { createCreateCommand } from "./create"; import { createDeleteCommand } from "./delete"; import { createFilesCommand } from "./files"; @@ -13,6 +14,8 @@ import { createUploadCommand } from "./upload"; export function createStoreCommand(): Command { const storeCommand = new Command("store").description("Manage stores"); + storeCommand.showHelpAfterError(); + // Add subcommands storeCommand.addCommand(createListCommand()); storeCommand.addCommand(createCreateCommand()); @@ -25,9 +28,12 @@ export function createStoreCommand(): Command { storeCommand.addCommand(createQACommand()); storeCommand.addCommand(createSyncCommand()); - // Show help without error exit code when no subcommand provided - storeCommand.action(() => { - storeCommand.help(); + // Handle unknown subcommands + storeCommand.on("command:*", () => { + const unknownCommand = storeCommand.args[0]; + const availableCommands = storeCommand.commands.map((cmd) => cmd.name()); + console.error(formatUnknownCommandError(unknownCommand, availableCommands)); + process.exit(1); }); return storeCommand; diff --git a/packages/cli/src/utils/command-suggestions.ts b/packages/cli/src/utils/command-suggestions.ts new file mode 100644 index 0000000..2b53931 --- /dev/null +++ b/packages/cli/src/utils/command-suggestions.ts @@ -0,0 +1,77 @@ +import chalk from "chalk"; + +function levenshteinDistance(a: string, b: string): number { + const matrix: number[][] = []; + + for (let i = 0; i <= b.length; i++) { + matrix[i] = [i]; + } + + for (let j = 0; j <= a.length; j++) { + matrix[0][j] = j; + } + + for (let i = 1; i <= b.length; i++) { + for (let j = 1; j <= a.length; j++) { + if (b.charAt(i - 1) === a.charAt(j - 1)) { + matrix[i][j] = matrix[i - 1][j - 1]; + } else { + matrix[i][j] = Math.min( + matrix[i - 1][j - 1] + 1, + matrix[i][j - 1] + 1, + matrix[i - 1][j] + 1 + ); + } + } + } + + return matrix[b.length][a.length]; +} + +export function findSimilarCommands( + input: string, + availableCommands: string[], + threshold = 3 +): string[] { + const suggestions = availableCommands + .map((cmd) => ({ + command: cmd, + distance: levenshteinDistance(input.toLowerCase(), cmd.toLowerCase()), + })) + .filter((item) => item.distance <= threshold) + .sort((a, b) => a.distance - b.distance) + .map((item) => item.command); + + return suggestions; +} + +export function formatUnknownCommandError( + unknownCommand: string, + availableCommands: string[] +): string { + let message = `${chalk.red("\n✗")} Unknown command: ${chalk.yellow(unknownCommand)}`; + + if (unknownCommand === "vs") { + message += `\n\n${chalk.yellow("⚠")} ${chalk.green("vs")} command was deprecated and renamed to ${chalk.green("store")} since ${chalk.bold("v2.0.0")}.`; + message += `\n\nSee: https://github.com/mixedbread-ai/openbread/blob/main/packages/cli/CHANGELOG.md`; + return message; + } + + const suggestions = findSimilarCommands(unknownCommand, availableCommands); + + if (suggestions.length > 0) { + message += `\n\n${chalk.cyan("Did you mean one of these?")}`; + for (const suggestion of suggestions) { + message += `\n ${chalk.green(suggestion)}`; + } + } else { + message += `\n\n${chalk.cyan("Available commands:")}`; + for (const cmd of availableCommands) { + message += `\n ${chalk.green(cmd)}`; + } + } + + message += `\n\n${chalk.gray("Run 'mxbai --help' for more information.")}`; + + return message; +} From 11f43e83813d7b6150cbb84ab504048d1116b4b4 Mon Sep 17 00:00:00 2001 From: Naing Linn Khant Date: Fri, 3 Oct 2025 21:06:22 +0700 Subject: [PATCH 06/10] feat: notify users when a newer version is available --- packages/cli/src/bin/mxbai.ts | 3 + packages/cli/src/utils/update-checker.ts | 137 +++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 packages/cli/src/utils/update-checker.ts diff --git a/packages/cli/src/bin/mxbai.ts b/packages/cli/src/bin/mxbai.ts index 08bbad2..cfbcf59 100644 --- a/packages/cli/src/bin/mxbai.ts +++ b/packages/cli/src/bin/mxbai.ts @@ -12,6 +12,7 @@ import { createConfigCommand } from "../commands/config"; import { createStoreCommand } from "../commands/store"; import { formatUnknownCommandError } from "../utils/command-suggestions"; import { setupGlobalOptions } from "../utils/global-options"; +import { checkForUpdates } from "../utils/update-checker"; // Find package.json relative to the compiled file location // In the published package, from bin/mxbai.js, package.json is one level up @@ -75,6 +76,8 @@ program.on("command:*", () => { // Parse arguments async function main() { try { + await checkForUpdates(version); + // Show help if no arguments provided if (process.argv.length === 2) { program.help(); diff --git a/packages/cli/src/utils/update-checker.ts b/packages/cli/src/utils/update-checker.ts new file mode 100644 index 0000000..4a5919d --- /dev/null +++ b/packages/cli/src/utils/update-checker.ts @@ -0,0 +1,137 @@ +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { homedir } from "node:os"; +import { join } from "node:path"; +import chalk from "chalk"; + +interface UpdateCheckCache { + lastCheck: number; + latestVersion: string; +} + +const CACHE_DIR = join(homedir(), ".config", "mxbai"); +const CACHE_FILE = join(CACHE_DIR, "update-check.json"); +const CHECK_INTERVAL = 24 * 60 * 60 * 1000; // 24 hours +const REGISTRY_URL = "https://registry.npmjs.org/@mixedbread/cli/latest"; +const TIMEOUT = 2000; + +/** + * Simple semantic version comparison + * Returns true if versionA is less than versionB + */ +function isVersionLessThan(versionA: string, versionB: string): boolean { + const parseVersion = (v: string) => { + const cleaned = v.replace(/^v/, ""); + const parts = cleaned.split(/[.-]/).map((p) => parseInt(p, 10) || 0); + return parts; + }; + + const a = parseVersion(versionA); + const b = parseVersion(versionB); + + for (let i = 0; i < Math.max(a.length, b.length); i++) { + const aVal = a[i] || 0; + const bVal = b[i] || 0; + if (aVal < bVal) return true; + if (aVal > bVal) return false; + } + + return false; +} + +async function fetchLatestVersion(): Promise { + const response = await fetch(REGISTRY_URL, { + headers: { Accept: "application/json" }, + signal: AbortSignal.timeout(TIMEOUT), + }); + + const json = await response.json(); + return json.version; +} + +function readCache(): UpdateCheckCache | null { + if (!existsSync(CACHE_FILE)) return null; + const data = readFileSync(CACHE_FILE, "utf-8"); + return JSON.parse(data); +} + +function writeCache(cache: UpdateCheckCache): void { + if (!existsSync(CACHE_DIR)) { + mkdirSync(CACHE_DIR, { recursive: true }); + } + writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2)); +} + +function formatUpdateBanner( + currentVersion: string, + latestVersion: string +): string { + const contentWidth = 92; // Width of actual content + const horizontalPadding = " "; // 2 spaces on each side + + const pad = (text: string) => { + const stripped = stripAnsi(text); + const padding = contentWidth - stripped.length; + return text + " ".repeat(Math.max(0, padding)); + }; + + const totalWidth = contentWidth + horizontalPadding.length * 2; + const border = "─".repeat(totalWidth); + + return [ + `╭${border}╮`, + `│${horizontalPadding}${pad("")}${horizontalPadding}│`, + `│${horizontalPadding}${pad(chalk.bold(`Update available: ${chalk.red(currentVersion)} → ${chalk.green(latestVersion)}`))}${horizontalPadding}│`, + `│${horizontalPadding}${pad("")}${horizontalPadding}│`, + `│${horizontalPadding}${pad(`Run: ${chalk.cyan("npm install -g @mixedbread/cli@latest")}`)}${horizontalPadding}│`, + `│${horizontalPadding}${pad("")}${horizontalPadding}│`, + `│${horizontalPadding}${pad(`Changelog: ${chalk.gray("https://github.com/mixedbread-ai/openbread/blob/main/packages/cli/CHANGELOG.md")}`)}${horizontalPadding}│`, + `│${horizontalPadding}${pad("")}${horizontalPadding}│`, + `╰${border}╯`, + ].join("\n"); +} + +/** + * Strip ANSI escape codes for length calculation + * Handles all ANSI escape sequences including color codes + */ +function stripAnsi(str: string): string { + // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional escape sequence for ANSI codes + return str.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, ""); +} + +/** + * Check for updates and display notification if available + * This runs asynchronously and doesn't block command execution + */ +export async function checkForUpdates(currentVersion: string): Promise { + // Skip in CI or non-TTY environments + if (process.env.CI || !process.stdout.isTTY) { + return; + } + + // Skip if user explicitly disabled checks + if (process.env.MXBAI_NO_UPDATE_CHECK === "true") { + return; + } + + try { + const cache = readCache(); + const now = Date.now(); + + // Check if we need to fetch fresh data + let latestVersion: string; + if (!cache || now - cache.lastCheck > CHECK_INTERVAL) { + latestVersion = await fetchLatestVersion(); + writeCache({ lastCheck: now, latestVersion }); + } else { + latestVersion = cache.latestVersion; + } + + if (isVersionLessThan(currentVersion, latestVersion)) { + console.log(formatUpdateBanner(currentVersion, latestVersion)); + console.log(); // Add spacing after banner + } + } catch { + // Silently fail - update checks should never break the CLI + } +} From 00af10a0cd217a0a7046fd53dcb712ead0a519f1 Mon Sep 17 00:00:00 2001 From: Naing Linn Khant Date: Fri, 3 Oct 2025 21:12:35 +0700 Subject: [PATCH 07/10] test: add tests for command suggestions --- .../tests/utils/command-suggestions.test.ts | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 packages/cli/tests/utils/command-suggestions.test.ts diff --git a/packages/cli/tests/utils/command-suggestions.test.ts b/packages/cli/tests/utils/command-suggestions.test.ts new file mode 100644 index 0000000..fb3e82a --- /dev/null +++ b/packages/cli/tests/utils/command-suggestions.test.ts @@ -0,0 +1,67 @@ +import { describe, expect, it } from "@jest/globals"; +import { + findSimilarCommands, + formatUnknownCommandError, +} from "../../src/utils/command-suggestions"; + +describe("Command Suggestions", () => { + describe("findSimilarCommands", () => { + const commands = ["store", "config", "completion", "list", "files"]; + + it("should find commands within threshold", () => { + expect(findSimilarCommands("stor", commands)).toEqual(["store"]); + expect(findSimilarCommands("confg", commands)).toEqual(["config"]); + expect(findSimilarCommands("lis", commands)).toContain("list"); + }); + + it("should return empty for commands beyond threshold", () => { + expect(findSimilarCommands("xyz", commands)).toEqual([]); + expect(findSimilarCommands("abcdefg", commands)).toEqual([]); + }); + + it("should sort by distance (closest first)", () => { + const result = findSimilarCommands("lst", commands); + expect(result[0]).toBe("list"); // distance 1 + }); + + it("should return multiple matches when applicable", () => { + const result = findSimilarCommands("st", commands); + expect(result.length).toBeGreaterThan(0); + expect(result).toContain("store"); + }); + + it("should use custom threshold", () => { + expect(findSimilarCommands("str", commands, 1)).toEqual([]); + expect(findSimilarCommands("store", commands, 0)).toEqual(["store"]); + }); + }); + + describe("formatUnknownCommandError", () => { + const commands = ["store", "config", "completion"]; + + it("should show deprecation message for 'vs' command", () => { + const result = formatUnknownCommandError("vs", commands); + expect(result).toContain("deprecated"); + expect(result).toContain("store"); + expect(result).toContain("v2.0.0"); + expect(result).toContain("CHANGELOG.md"); + }); + + it("should show suggestions for close matches", () => { + const result = formatUnknownCommandError("stor", commands); + expect(result).toContain("Unknown command"); + expect(result).toContain("Did you mean one of these?"); + expect(result).toContain("store"); + expect(result).toContain("--help"); + }); + + it("should list all commands when no close matches", () => { + const result = formatUnknownCommandError("xyz", commands); + expect(result).toContain("Unknown command"); + expect(result).toContain("Available commands:"); + expect(result).toContain("store"); + expect(result).toContain("config"); + expect(result).toContain("completion"); + }); + }); +}); From 3e1c80203b21de89315601da6d1fdd2047a94b99 Mon Sep 17 00:00:00 2001 From: Naing Linn Khant Date: Sat, 4 Oct 2025 00:58:47 +0700 Subject: [PATCH 08/10] chore: remove MXBAI_NO_UPDATE_CHECK --- packages/cli/src/utils/update-checker.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/cli/src/utils/update-checker.ts b/packages/cli/src/utils/update-checker.ts index 4a5919d..f617c97 100644 --- a/packages/cli/src/utils/update-checker.ts +++ b/packages/cli/src/utils/update-checker.ts @@ -109,11 +109,6 @@ export async function checkForUpdates(currentVersion: string): Promise { return; } - // Skip if user explicitly disabled checks - if (process.env.MXBAI_NO_UPDATE_CHECK === "true") { - return; - } - try { const cache = readCache(); const now = Date.now(); From be0bf04d53acbd99fd5402c4eafa13c44e6596cf Mon Sep 17 00:00:00 2001 From: Naing Linn Khant Date: Sat, 4 Oct 2025 01:19:35 +0700 Subject: [PATCH 09/10] fix: add adjustments --- packages/cli/src/utils/update-checker.ts | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/utils/update-checker.ts b/packages/cli/src/utils/update-checker.ts index f617c97..d69d409 100644 --- a/packages/cli/src/utils/update-checker.ts +++ b/packages/cli/src/utils/update-checker.ts @@ -17,25 +17,25 @@ const TIMEOUT = 2000; /** * Simple semantic version comparison * Returns true if versionA is less than versionB + * Ignores pre-release versions (only compares major.minor.patch) */ function isVersionLessThan(versionA: string, versionB: string): boolean { const parseVersion = (v: string) => { const cleaned = v.replace(/^v/, ""); - const parts = cleaned.split(/[.-]/).map((p) => parseInt(p, 10) || 0); - return parts; + const mainVersion = cleaned.split("-")[0]; + const [major, minor, patch] = mainVersion + .split(".") + .map((p) => parseInt(p, 10) || 0); + return { major, minor, patch }; }; const a = parseVersion(versionA); const b = parseVersion(versionB); - for (let i = 0; i < Math.max(a.length, b.length); i++) { - const aVal = a[i] || 0; - const bVal = b[i] || 0; - if (aVal < bVal) return true; - if (aVal > bVal) return false; - } - - return false; + // Compare major.minor.patch + if (a.major !== b.major) return a.major < b.major; + if (a.minor !== b.minor) return a.minor < b.minor; + return a.patch < b.patch; } async function fetchLatestVersion(): Promise { @@ -44,6 +44,10 @@ async function fetchLatestVersion(): Promise { signal: AbortSignal.timeout(TIMEOUT), }); + if (!response.ok) { + throw new Error(`Failed to fetch latest version: ${response.statusText}`); + } + const json = await response.json(); return json.version; } From c5cf811a533a6ed5100f9bde649168bb315a5794 Mon Sep 17 00:00:00 2001 From: Naing Linn Khant Date: Sat, 4 Oct 2025 01:27:55 +0700 Subject: [PATCH 10/10] chore(release): add changeset --- .changeset/soft-glasses-thank.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/soft-glasses-thank.md diff --git a/.changeset/soft-glasses-thank.md b/.changeset/soft-glasses-thank.md new file mode 100644 index 0000000..2e8ad0c --- /dev/null +++ b/.changeset/soft-glasses-thank.md @@ -0,0 +1,6 @@ +--- +"@mixedbread/cli": minor +--- + +- Improve unkown command error message +- Notify users when a newer version is released