From bbe563abe9d1e498bab155a26ab96ccc8e0243a4 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 8 May 2026 11:09:14 +0000 Subject: [PATCH] feat: Command Line SDK update for version 20.2.0 --- CHANGELOG.md | 6 + README.md | 8 +- cli.ts | 2 + .../databases/create-big-int-attribute.md | 7 + .../databases/update-big-int-attribute.md | 8 + docs/examples/functions/create-variable.md | 1 + docs/examples/functions/list-variables.md | 3 +- docs/examples/functions/update-variable.md | 3 +- docs/examples/presences/delete.md | 4 + docs/examples/presences/get-usage.md | 3 + docs/examples/presences/get.md | 4 + docs/examples/presences/list.md | 4 + docs/examples/presences/update-presence.md | 5 + docs/examples/presences/upsert.md | 6 + docs/examples/project/create-ephemeral-key.md | 2 +- .../examples/project/get-o-auth-2-provider.md | 2 +- .../project/list-o-auth-2-providers.md | 3 +- docs/examples/proxy/update-rule-status.md | 4 + .../proxy/update-rule-verification.md | 4 - docs/examples/sites/create-variable.md | 1 + docs/examples/sites/list-variables.md | 3 +- docs/examples/sites/update-variable.md | 3 +- .../tablesdb/create-big-int-column.md | 7 + .../tablesdb/update-big-int-column.md | 8 + install.ps1 | 4 +- install.sh | 2 +- lib/commands/generic.ts | 149 +++--------------- lib/commands/services/databases.ts | 45 ++++++ lib/commands/services/functions.ts | 25 ++- lib/commands/services/presences.ts | 136 ++++++++++++++++ lib/commands/services/project.ts | 50 +++--- lib/commands/services/proxy.ts | 29 ++-- lib/commands/services/sites.ts | 25 ++- lib/commands/services/tables-db.ts | 45 ++++++ lib/config.ts | 27 +--- lib/constants.ts | 2 +- lib/questions.ts | 31 ++-- lib/utils.ts | 2 +- package-lock.json | 4 +- package.json | 2 +- scoop/appwrite.config.json | 6 +- 41 files changed, 455 insertions(+), 230 deletions(-) create mode 100644 docs/examples/databases/create-big-int-attribute.md create mode 100644 docs/examples/databases/update-big-int-attribute.md create mode 100644 docs/examples/presences/delete.md create mode 100644 docs/examples/presences/get-usage.md create mode 100644 docs/examples/presences/get.md create mode 100644 docs/examples/presences/list.md create mode 100644 docs/examples/presences/update-presence.md create mode 100644 docs/examples/presences/upsert.md create mode 100644 docs/examples/proxy/update-rule-status.md delete mode 100644 docs/examples/proxy/update-rule-verification.md create mode 100644 docs/examples/tablesdb/create-big-int-column.md create mode 100644 docs/examples/tablesdb/update-big-int-column.md create mode 100644 lib/commands/services/presences.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 79a2a713..5c4e311f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 20.2.0 + +* Added: Introduced `bigint` create/update APIs for legacy Databases attributes +* Added: Introduced `bigint` create/update APIs for `TablesDB` columns +* Updated: Extended key-list query filters with `key`, `resourceType`, `resourceId`, and `secret` + ## 20.1.0 * Added `--switch` and `--new` flags on `appwrite login` to explicitly manage multiple saved accounts diff --git a/README.md b/README.md index 500e4e61..b5e102ef 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Appwrite Command Line SDK ![License](https://img.shields.io/github/license/appwrite/sdk-for-cli.svg?style=flat-square) -![Version](https://img.shields.io/badge/api%20version-1.9.3-blue.svg?style=flat-square) +![Version](https://img.shields.io/badge/api%20version-1.9.4-blue.svg?style=flat-square) [![Build Status](https://img.shields.io/travis/com/appwrite/sdk-generator?style=flat-square)](https://travis-ci.com/appwrite/sdk-generator) [![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite) [![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord) -**This SDK is compatible with Appwrite server version 1.9.x. For older versions, please check [previous releases](https://github.com/appwrite/sdk-for-cli/releases).** +**This SDK is compatible with Appwrite server version latest. For older versions, please check [previous releases](https://github.com/appwrite/sdk-for-cli/releases).** Appwrite is an open-source backend as a service server that abstracts and simplifies complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way. Use the Command Line SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs) @@ -29,7 +29,7 @@ Once the installation is complete, you can verify the install using ```sh $ appwrite -v -20.1.0 +20.2.0 ``` ### Install using prebuilt binaries @@ -83,7 +83,7 @@ $ scoop install https://raw.githubusercontent.com/appwrite/sdk-for-cli/master/sc Once the installation completes, you can verify your install using ``` $ appwrite -v -20.1.0 +20.2.0 ``` ## Getting Started diff --git a/cli.ts b/cli.ts index 293f5336..bc6dd17a 100644 --- a/cli.ts +++ b/cli.ts @@ -45,6 +45,7 @@ import { locale } from './lib/commands/services/locale.js'; import { messaging } from './lib/commands/services/messaging.js'; import { migrations } from './lib/commands/services/migrations.js'; import { organizations } from './lib/commands/services/organizations.js'; +import { presences } from './lib/commands/services/presences.js'; import { project } from './lib/commands/services/project.js'; import { projects } from './lib/commands/services/projects.js'; import { proxy } from './lib/commands/services/proxy.js'; @@ -215,6 +216,7 @@ if (process.argv.includes('-v') || process.argv.includes('--version')) { .addCommand(messaging) .addCommand(migrations) .addCommand(organizations) + .addCommand(presences) .addCommand(project) .addCommand(projects) .addCommand(proxy) diff --git a/docs/examples/databases/create-big-int-attribute.md b/docs/examples/databases/create-big-int-attribute.md new file mode 100644 index 00000000..4ac556d6 --- /dev/null +++ b/docs/examples/databases/create-big-int-attribute.md @@ -0,0 +1,7 @@ +```bash +appwrite databases create-big-int-attribute \ + --database-id \ + --collection-id \ + --key '' \ + --required false +``` diff --git a/docs/examples/databases/update-big-int-attribute.md b/docs/examples/databases/update-big-int-attribute.md new file mode 100644 index 00000000..fdb8fe60 --- /dev/null +++ b/docs/examples/databases/update-big-int-attribute.md @@ -0,0 +1,8 @@ +```bash +appwrite databases update-big-int-attribute \ + --database-id \ + --collection-id \ + --key '' \ + --required false \ + --default null +``` diff --git a/docs/examples/functions/create-variable.md b/docs/examples/functions/create-variable.md index 1b6fe426..57d8344f 100644 --- a/docs/examples/functions/create-variable.md +++ b/docs/examples/functions/create-variable.md @@ -1,6 +1,7 @@ ```bash appwrite functions create-variable \ --function-id \ + --variable-id \ --key \ --value ``` diff --git a/docs/examples/functions/list-variables.md b/docs/examples/functions/list-variables.md index 5639a066..2f9db0e4 100644 --- a/docs/examples/functions/list-variables.md +++ b/docs/examples/functions/list-variables.md @@ -1,4 +1,5 @@ ```bash appwrite functions list-variables \ - --function-id + --function-id \ + --limit 25 ``` diff --git a/docs/examples/functions/update-variable.md b/docs/examples/functions/update-variable.md index 7c7e7583..b8bd3dbf 100644 --- a/docs/examples/functions/update-variable.md +++ b/docs/examples/functions/update-variable.md @@ -1,6 +1,5 @@ ```bash appwrite functions update-variable \ --function-id \ - --variable-id \ - --key + --variable-id ``` diff --git a/docs/examples/presences/delete.md b/docs/examples/presences/delete.md new file mode 100644 index 00000000..c64c0006 --- /dev/null +++ b/docs/examples/presences/delete.md @@ -0,0 +1,4 @@ +```bash +appwrite presences delete \ + --presence-id +``` diff --git a/docs/examples/presences/get-usage.md b/docs/examples/presences/get-usage.md new file mode 100644 index 00000000..65daeca2 --- /dev/null +++ b/docs/examples/presences/get-usage.md @@ -0,0 +1,3 @@ +```bash +appwrite presences get-usage +``` diff --git a/docs/examples/presences/get.md b/docs/examples/presences/get.md new file mode 100644 index 00000000..86a1b5d7 --- /dev/null +++ b/docs/examples/presences/get.md @@ -0,0 +1,4 @@ +```bash +appwrite presences get \ + --presence-id +``` diff --git a/docs/examples/presences/list.md b/docs/examples/presences/list.md new file mode 100644 index 00000000..83c77666 --- /dev/null +++ b/docs/examples/presences/list.md @@ -0,0 +1,4 @@ +```bash +appwrite presences list \ + --limit 25 +``` diff --git a/docs/examples/presences/update-presence.md b/docs/examples/presences/update-presence.md new file mode 100644 index 00000000..800fd721 --- /dev/null +++ b/docs/examples/presences/update-presence.md @@ -0,0 +1,5 @@ +```bash +appwrite presences update-presence \ + --presence-id \ + --user-id +``` diff --git a/docs/examples/presences/upsert.md b/docs/examples/presences/upsert.md new file mode 100644 index 00000000..4261cc0c --- /dev/null +++ b/docs/examples/presences/upsert.md @@ -0,0 +1,6 @@ +```bash +appwrite presences upsert \ + --presence-id \ + --user-id \ + --status +``` diff --git a/docs/examples/project/create-ephemeral-key.md b/docs/examples/project/create-ephemeral-key.md index d80508a3..d4ab3852 100644 --- a/docs/examples/project/create-ephemeral-key.md +++ b/docs/examples/project/create-ephemeral-key.md @@ -1,5 +1,5 @@ ```bash appwrite project create-ephemeral-key \ --scopes one two three \ - --duration 1 + --duration 600 ``` diff --git a/docs/examples/project/get-o-auth-2-provider.md b/docs/examples/project/get-o-auth-2-provider.md index ba47af11..1c54d95c 100644 --- a/docs/examples/project/get-o-auth-2-provider.md +++ b/docs/examples/project/get-o-auth-2-provider.md @@ -1,4 +1,4 @@ ```bash appwrite project get-o-auth-2-provider \ - --provider + --provider-id amazon ``` diff --git a/docs/examples/project/list-o-auth-2-providers.md b/docs/examples/project/list-o-auth-2-providers.md index df16be41..fe4d21d1 100644 --- a/docs/examples/project/list-o-auth-2-providers.md +++ b/docs/examples/project/list-o-auth-2-providers.md @@ -1,3 +1,4 @@ ```bash -appwrite project list-o-auth-2-providers +appwrite project list-o-auth-2-providers \ + --limit 25 ``` diff --git a/docs/examples/proxy/update-rule-status.md b/docs/examples/proxy/update-rule-status.md new file mode 100644 index 00000000..4c9b24e3 --- /dev/null +++ b/docs/examples/proxy/update-rule-status.md @@ -0,0 +1,4 @@ +```bash +appwrite proxy update-rule-status \ + --rule-id +``` diff --git a/docs/examples/proxy/update-rule-verification.md b/docs/examples/proxy/update-rule-verification.md deleted file mode 100644 index 3e5734d2..00000000 --- a/docs/examples/proxy/update-rule-verification.md +++ /dev/null @@ -1,4 +0,0 @@ -```bash -appwrite proxy update-rule-verification \ - --rule-id -``` diff --git a/docs/examples/sites/create-variable.md b/docs/examples/sites/create-variable.md index 100ac21e..21d7c65c 100644 --- a/docs/examples/sites/create-variable.md +++ b/docs/examples/sites/create-variable.md @@ -1,6 +1,7 @@ ```bash appwrite sites create-variable \ --site-id \ + --variable-id \ --key \ --value ``` diff --git a/docs/examples/sites/list-variables.md b/docs/examples/sites/list-variables.md index 67861820..49f6b833 100644 --- a/docs/examples/sites/list-variables.md +++ b/docs/examples/sites/list-variables.md @@ -1,4 +1,5 @@ ```bash appwrite sites list-variables \ - --site-id + --site-id \ + --limit 25 ``` diff --git a/docs/examples/sites/update-variable.md b/docs/examples/sites/update-variable.md index 06fe2cd7..e8a2d4b7 100644 --- a/docs/examples/sites/update-variable.md +++ b/docs/examples/sites/update-variable.md @@ -1,6 +1,5 @@ ```bash appwrite sites update-variable \ --site-id \ - --variable-id \ - --key + --variable-id ``` diff --git a/docs/examples/tablesdb/create-big-int-column.md b/docs/examples/tablesdb/create-big-int-column.md new file mode 100644 index 00000000..49167996 --- /dev/null +++ b/docs/examples/tablesdb/create-big-int-column.md @@ -0,0 +1,7 @@ +```bash +appwrite tables-db create-big-int-column \ + --database-id \ + --table-id \ + --key '' \ + --required false +``` diff --git a/docs/examples/tablesdb/update-big-int-column.md b/docs/examples/tablesdb/update-big-int-column.md new file mode 100644 index 00000000..267ec29d --- /dev/null +++ b/docs/examples/tablesdb/update-big-int-column.md @@ -0,0 +1,8 @@ +```bash +appwrite tables-db update-big-int-column \ + --database-id \ + --table-id \ + --key '' \ + --required false \ + --default null +``` diff --git a/install.ps1 b/install.ps1 index 25204a16..54ea2a90 100644 --- a/install.ps1 +++ b/install.ps1 @@ -13,8 +13,8 @@ # You can use "View source" of this page to see the full script. # REPO -$GITHUB_x64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/20.1.0/appwrite-cli-win-x64.exe" -$GITHUB_arm64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/20.1.0/appwrite-cli-win-arm64.exe" +$GITHUB_x64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/20.2.0/appwrite-cli-win-x64.exe" +$GITHUB_arm64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/20.2.0/appwrite-cli-win-arm64.exe" $APPWRITE_BINARY_NAME = "appwrite.exe" diff --git a/install.sh b/install.sh index 49b9fbb4..6bd9885a 100644 --- a/install.sh +++ b/install.sh @@ -120,7 +120,7 @@ verifyMacOSCodeSignature() { downloadBinary() { echo "[2/5] Downloading executable for $OS ($ARCH) ..." - GITHUB_LATEST_VERSION="20.1.0" + GITHUB_LATEST_VERSION="20.2.0" GITHUB_FILE="appwrite-cli-${OS}-${ARCH}" GITHUB_URL="https://github.com/$GITHUB_REPOSITORY_NAME/releases/download/$GITHUB_LATEST_VERSION/$GITHUB_FILE" diff --git a/lib/commands/generic.ts b/lib/commands/generic.ts index 7941e134..730133de 100644 --- a/lib/commands/generic.ts +++ b/lib/commands/generic.ts @@ -2,11 +2,7 @@ import inquirer from "inquirer"; import { Command } from "commander"; import { Client } from "@appwrite.io/console"; import { sdkForConsole } from "../sdks.js"; -import { - globalConfig, - localConfig, - normalizeCloudConsoleEndpoint, -} from "../config.js"; +import { globalConfig, localConfig } from "../config.js"; import { EXECUTABLE_NAME } from "../constants.js"; import { actionRunner, @@ -20,20 +16,14 @@ import { drawTable, cliConfig, } from "../parser.js"; -import { isCloudHostname } from "../utils.js"; import ID from "../id.js"; import { questionsLogin, questionsLogout, questionsListFactors, questionsMFAChallenge, - questionsSwitchAccount, } from "../questions.js"; -import { - Account, - Client as ConsoleClient, - type Models, -} from "@appwrite.io/console"; +import { Account, Client as ConsoleClient } from "@appwrite.io/console"; import ClientLegacy from "../client.js"; const DEFAULT_ENDPOINT = "https://cloud.appwrite.io/v1"; @@ -47,56 +37,6 @@ const isMfaRequiredError = (err: unknown): err is AppwriteError => (err as AppwriteError)?.type === "user_more_factors_required" || (err as AppwriteError)?.response === "user_more_factors_required"; -const isGuestUnauthorizedError = (err: unknown): err is AppwriteError => - (err as AppwriteError)?.type === "general_unauthorized_scope" || - (err as AppwriteError)?.response === "general_unauthorized_scope"; - -const isRegionalCloudEndpoint = (endpoint: string): boolean => { - try { - const hostname = new URL(endpoint).hostname; - return isCloudHostname(hostname) && hostname !== "cloud.appwrite.io"; - } catch (_error) { - return false; - } -}; - -const restoreCurrentSession = (sessionId: string): void => { - globalConfig.setCurrentSession( - globalConfig.getSessionIds().includes(sessionId) ? sessionId : "", - ); -}; - -const removeCurrentSession = (): void => { - const current = globalConfig.getCurrentSession(); - globalConfig.setCurrentSession(""); - globalConfig.removeSession(current); -}; - -const getCurrentAccount = async (): Promise => { - if (globalConfig.getEndpoint() === "" || globalConfig.getCookie() === "") { - return null; - } - - const endpoint = normalizeCloudConsoleEndpoint(globalConfig.getEndpoint()); - if (endpoint !== globalConfig.getEndpoint()) { - globalConfig.setEndpoint(endpoint); - } - - const client = await sdkForConsole(false); - const accountClient = new Account(client); - - try { - const account = await accountClient.get(); - globalConfig.setEmail(account.email); - return account; - } catch (err) { - if (isGuestUnauthorizedError(err)) { - removeCurrentSession(); - } - return null; - } -}; - const createLegacyConsoleClient = (endpoint: string): ClientLegacy => { const legacyClient = new ClientLegacy(); legacyClient.setEndpoint(endpoint); @@ -175,9 +115,7 @@ const getSessionAccountKey = (sessionId: string): string | undefined => { | { email?: string; endpoint?: string } | undefined; if (!session) return undefined; - return `${session.email ?? ""}|${normalizeCloudConsoleEndpoint( - session.endpoint ?? "", - )}`; + return `${session.email ?? ""}|${session.endpoint ?? ""}`; }; /** @@ -227,62 +165,33 @@ export const loginCommand = async ({ endpoint, mfa, code, - switch: switchAccount, - new: newAccount, }: { email?: string; password?: string; endpoint?: string; mfa?: string; code?: string; - switch?: boolean; - new?: boolean; }): Promise => { - let oldCurrent = globalConfig.getCurrentSession(); - - if (switchAccount && newAccount) { - throw new Error("Use either --switch or --new, not both."); - } + const oldCurrent = globalConfig.getCurrentSession(); - if (endpoint && isRegionalCloudEndpoint(endpoint)) { - throw new Error( - `Cloud login uses ${DEFAULT_ENDPOINT}. Regional Cloud endpoints are for project API calls, not account login.`, - ); - } - - const configEndpoint = normalizeCloudConsoleEndpoint( - (endpoint ?? globalConfig.getEndpoint()) || DEFAULT_ENDPOINT, - ); + const configEndpoint = + (endpoint ?? globalConfig.getEndpoint()) || DEFAULT_ENDPOINT; if (globalConfig.getCurrentSession() !== "") { - const account = await getCurrentAccount(); - oldCurrent = globalConfig.getCurrentSession(); + log("You are currently signed in as " + globalConfig.getEmail()); - if (account) { - if (!email && !password && !endpoint && !switchAccount && !newAccount) { - success("Already logged in as " + account.email); - hint(`Use '${EXECUTABLE_NAME} login --new' to add another account`); - return; - } + if (globalConfig.getSessions().length === 1) { + hint("You can sign in and manage multiple accounts with Appwrite CLI"); } } - let answers; - if (switchAccount) { - if (!globalConfig.getSessions().some((session) => session.email)) { - throw new Error( - `No signed-in accounts found. Run '${EXECUTABLE_NAME} login' to sign in.`, - ); - } - answers = await inquirer.prompt(questionsSwitchAccount); - } else if (email && password) { - answers = { email, password }; - } else { - answers = await inquirer.prompt(questionsLogin); - } + const answers = + email && password + ? { email, password } + : await inquirer.prompt(questionsLogin); if (!answers.method) { - answers.method = switchAccount ? "select" : "login"; + answers.method = "login"; } if (answers.method === "select") { @@ -292,21 +201,7 @@ export const loginCommand = async ({ throw Error("Session ID not found"); } - if (accountId === oldCurrent) { - const account = await getCurrentAccount(); - if (account) { - success(`Already using ${account.email}`); - return; - } - throw new Error( - `Selected account session is no longer valid. Run '${EXECUTABLE_NAME} login --switch' again.`, - ); - } - globalConfig.setCurrentSession(accountId); - globalConfig.setEndpoint( - normalizeCloudConsoleEndpoint(globalConfig.getEndpoint()), - ); const client = await sdkForConsole(false); const accountClient = new Account(client); @@ -318,10 +213,6 @@ export const loginCommand = async ({ await accountClient.get(); } catch (err) { if (!isMfaRequiredError(err)) { - if (isGuestUnauthorizedError(err)) { - globalConfig.removeSession(accountId); - } - restoreCurrentSession(oldCurrent); throw err; } @@ -415,8 +306,14 @@ export const whoami = new Command("whoami") return; } - const account = await getCurrentAccount(); - if (!account) { + const client = await sdkForConsole(false); + const accountClient = new Account(client); + + let account; + + try { + account = await accountClient.get(); + } catch (_) { error("No user is signed in. To sign in, run 'appwrite login'"); return; } @@ -461,8 +358,6 @@ export const login = new Command("login") `Multi-factor authentication login factor: totp, email, phone or recoveryCode`, ) .option(`--code [code]`, `Multi-factor code`) - .option(`--switch`, `Switch to another signed-in account`) - .option(`--new`, `Sign in to another account`) .configureHelp({ helpWidth: process.stdout.columns || 80, }) diff --git a/lib/commands/services/databases.ts b/lib/commands/services/databases.ts index a62f7afb..71f33abc 100644 --- a/lib/commands/services/databases.ts +++ b/lib/commands/services/databases.ts @@ -367,6 +367,51 @@ const databasesListAttributesCommand = databases ); +const databasesCreateBigIntAttributeCommand = databases + .command(`create-big-int-attribute`) + .description(`Create a bigint attribute. Optionally, minimum and maximum values can be provided. +`) + .requiredOption(`--database-id `, `Database ID.`) + .requiredOption(`--collection-id `, `Collection ID.`) + .requiredOption(`--key `, `Attribute Key.`) + .requiredOption(`--required `, `Is attribute required?`, parseBool) + .option(`--min `, `Minimum value`, parseInteger) + .option(`--max `, `Maximum value`, parseInteger) + .option(`--xdefault `, `Default value. Cannot be set when attribute is required.`, parseInteger) + .option( + `--array [value]`, + `Is attribute an array?`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ databaseId, collectionId, key, required, min, max, xdefault, array }) => + parse(await (await getDatabasesClient()).createBigIntAttribute(databaseId, collectionId, key, required, min, max, xdefault, array)), + ), + ); + + +const databasesUpdateBigIntAttributeCommand = databases + .command(`update-big-int-attribute`) + .description(`Update a bigint attribute. Changing the \`default\` value will not update already existing documents. +`) + .requiredOption(`--database-id `, `Database ID.`) + .requiredOption(`--collection-id `, `Collection ID.`) + .requiredOption(`--key `, `Attribute Key.`) + .requiredOption(`--required `, `Is attribute required?`, parseBool) + .requiredOption(`--xdefault `, `Default value. Cannot be set when attribute is required.`, parseInteger) + .option(`--min `, `Minimum value`, parseInteger) + .option(`--max `, `Maximum value`, parseInteger) + .option(`--new-key `, `New Attribute Key.`) + .action( + actionRunner( + async ({ databaseId, collectionId, key, required, xdefault, min, max, newKey }) => + parse(await (await getDatabasesClient()).updateBigIntAttribute(databaseId, collectionId, key, required, xdefault, min, max, newKey)), + ), + ); + + const databasesCreateBooleanAttributeCommand = databases .command(`create-boolean-attribute`) .description(`Create a boolean attribute. diff --git a/lib/commands/services/functions.ts b/lib/commands/services/functions.ts index db803b47..f5f2d2f4 100644 --- a/lib/commands/services/functions.ts +++ b/lib/commands/services/functions.ts @@ -513,10 +513,24 @@ const functionsListVariablesCommand = functions .command(`list-variables`) .description(`Get a list of all variables of a specific function.`) .requiredOption(`--function-id `, `Function unique ID.`) + .option(`--queries [queries...]`, `Raw Appwrite JSON query strings (legacy). Use this for advanced queries or automation; for common filtering, sorting, and pagination prefer --where, --sort-asc, --sort-desc, --limit, and --offset. When mixed, raw --queries are sent before generated flag queries. Array of query strings generated using the Query class provided by the SDK. Learn more about queries (https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: key, resourceType, resourceId, secret`) + .option( + `--total [value]`, + `When set to false, the total count returned will be 0 and will not be calculated.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .option(`--where `, `Filter using a simple comparison expression. Repeat for multiple filters. Supports field=value, field!=value, field>value, field>=value, field collectQueryValue(parseWhereQuery(value), previous)) + .option(`--sort-asc `, `Sort results by an attribute in ascending order. Repeat for multiple sort fields.`, (value: string, previous: string[] | undefined) => collectQueryValue(value, previous)) + .option(`--sort-desc `, `Sort results by an attribute in descending order. Repeat for multiple sort fields.`, (value: string, previous: string[] | undefined) => collectQueryValue(value, previous)) + .option(`--limit `, `Maximum number of results to return.`, parseInteger) + .option(`--offset `, `Number of results to skip.`, parseInteger) + .option(`--cursor-after `, `Return results after this cursor ID.`) + .option(`--cursor-before `, `Return results before this cursor ID.`) .action( actionRunner( - async ({ functionId }) => - parse(await (await getFunctionsClient()).listVariables(functionId)), + async ({ functionId, queries, total, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }) => + parse(await (await getFunctionsClient()).listVariables(functionId, buildQueries({ queries, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }), total)), ), ); @@ -525,6 +539,7 @@ const functionsCreateVariableCommand = functions .command(`create-variable`) .description(`Create a new function environment variable. These variables can be accessed in the function at runtime as environment variables.`) .requiredOption(`--function-id `, `Function unique ID.`) + .requiredOption(`--variable-id `, `Variable ID. Choose a custom ID or generate a random ID with \`ID.unique()\`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars.`) .requiredOption(`--key `, `Variable key. Max length: 255 chars.`) .requiredOption(`--value `, `Variable value. Max length: 8192 chars.`) .option( @@ -535,8 +550,8 @@ const functionsCreateVariableCommand = functions ) .action( actionRunner( - async ({ functionId, key, value, secret }) => - parse(await (await getFunctionsClient()).createVariable(functionId, key, value, secret)), + async ({ functionId, variableId, key, value, secret }) => + parse(await (await getFunctionsClient()).createVariable(functionId, variableId, key, value, secret)), ), ); @@ -559,7 +574,7 @@ const functionsUpdateVariableCommand = functions .description(`Update variable by its unique ID.`) .requiredOption(`--function-id `, `Function unique ID.`) .requiredOption(`--variable-id `, `Variable unique ID.`) - .requiredOption(`--key `, `Variable key. Max length: 255 chars.`) + .option(`--key `, `Variable key. Max length: 255 chars.`) .option(`--value `, `Variable value. Max length: 8192 chars.`) .option( `--secret [value]`, diff --git a/lib/commands/services/presences.ts b/lib/commands/services/presences.ts new file mode 100644 index 00000000..48b7346a --- /dev/null +++ b/lib/commands/services/presences.ts @@ -0,0 +1,136 @@ +import { Command } from "commander"; +import { + buildQueries, + collectQueryValue, + parseWhereQuery, +} from "../utils/query.js"; +import { sdkForProject } from "../../sdks.js"; +import { + actionRunner, + commandDescriptions, + success, + parse, + parseBool, + parseInteger, + parseJsonObject, +} from "../../parser.js"; +import { Presences } from "@appwrite.io/console"; + +let presencesClient: Presences | null = null; + +const getPresencesClient = async (): Promise => { + if (!presencesClient) { + const sdkClient = await sdkForProject(); + presencesClient = new Presences(sdkClient); + } + return presencesClient; +}; + +export const presences = new Command("presences") + .description(commandDescriptions["presences"] ?? "") + .configureHelp({ + helpWidth: process.stdout.columns || 80, + }); + +const presencesListCommand = presences + .command(`list`) + .description(`List presence logs.`) + .option(`--queries [queries...]`, `Raw Appwrite JSON query strings (legacy). Use this for advanced queries or automation; for common filtering, sorting, and pagination prefer --where, --sort-asc, --sort-desc, --limit, and --offset. When mixed, raw --queries are sent before generated flag queries. Array of query strings generated using the Query class provided by the SDK.`) + .option( + `--total [value]`, + `When set to false, the total count returned will be 0 and will not be calculated.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .option(`--ttl `, `TTL (seconds) for caching list responses. Responses are stored in an in-memory key-value cache, keyed per project, collection, schema version (attributes and indexes), caller authorization roles, and the exact query — so users with different permissions never share cached entries. Schema changes invalidate cached entries automatically; document writes do not, so choose a TTL you are comfortable serving as stale data. Set to 0 to disable caching. Must be between 0 and 86400 (24 hours).`, parseInteger) + .option(`--where `, `Filter using a simple comparison expression. Repeat for multiple filters. Supports field=value, field!=value, field>value, field>=value, field collectQueryValue(parseWhereQuery(value), previous)) + .option(`--sort-asc `, `Sort results by an attribute in ascending order. Repeat for multiple sort fields.`, (value: string, previous: string[] | undefined) => collectQueryValue(value, previous)) + .option(`--sort-desc `, `Sort results by an attribute in descending order. Repeat for multiple sort fields.`, (value: string, previous: string[] | undefined) => collectQueryValue(value, previous)) + .option(`--limit `, `Maximum number of results to return.`, parseInteger) + .option(`--offset `, `Number of results to skip.`, parseInteger) + .option(`--cursor-after `, `Return results after this cursor ID.`) + .option(`--cursor-before `, `Return results before this cursor ID.`) + .action( + actionRunner( + async ({ queries, total, ttl, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }) => + parse(await (await getPresencesClient()).list(buildQueries({ queries, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }), total, ttl)), + ), + ); + + +const presencesGetUsageCommand = presences + .command(`get-usage`) + .description(`Get presence usage metrics and statistics, including the current total of online users and historical online user counts for the selected time range.`) + .option(`--range `, `Date range.`) + .action( + actionRunner( + async ({ range }) => + parse(await (await getPresencesClient()).getUsage(range)), + ), + ); + + +const presencesGetCommand = presences + .command(`get`) + .description(`Get a presence log by its unique ID.`) + .requiredOption(`--presence-id `, `Presence unique ID.`) + .action( + actionRunner( + async ({ presenceId }) => + parse(await (await getPresencesClient()).get(presenceId)), + ), + ); + + +const presencesUpsertCommand = presences + .command(`upsert`) + .description(`Create or update a presence log by its unique ID.`) + .requiredOption(`--presence-id `, `Presence unique ID.`) + .requiredOption(`--user-id `, `User ID.`) + .requiredOption(`--status `, `Presence status.`) + .option(`--permissions [permissions...]`, `An array of permissions strings. By default, only the current user is granted all permissions. Learn more about permissions (https://appwrite.io/docs/permissions).`) + .option(`--expires-at `, `Presence expiry datetime.`) + .option(`--metadata `, `Presence metadata object.`) + .action( + actionRunner( + async ({ presenceId, userId, status, permissions, expiresAt, metadata }) => + parse(await (await getPresencesClient()).upsert(presenceId, userId, status, permissions, expiresAt, parseJsonObject(metadata, "--metadata"))), + ), + ); + + +const presencesUpdatePresenceCommand = presences + .command(`update-presence`) + .description(`Update a presence log by its unique ID.`) + .requiredOption(`--presence-id `, `Presence unique ID.`) + .requiredOption(`--user-id `, `User ID.`) + .option(`--status `, `Presence status.`) + .option(`--expires-at `, `Presence expiry datetime.`) + .option(`--metadata `, `Presence metadata object.`) + .option(`--permissions [permissions...]`, `An array of permissions strings. By default, only the current user is granted all permissions. Learn more about permissions (https://appwrite.io/docs/permissions).`) + .option( + `--purge [value]`, + `When true, purge cached responses used by list presences endpoint.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ presenceId, userId, status, expiresAt, metadata, permissions, purge }) => + parse(await (await getPresencesClient()).updatePresence(presenceId, userId, status, expiresAt, parseJsonObject(metadata, "--metadata"), permissions, purge)), + ), + ); + + +const presencesDeleteCommand = presences + .command(`delete`) + .description(`Delete a presence log by its unique ID.`) + .requiredOption(`--presence-id `, `Presence unique ID.`) + .action( + actionRunner( + async ({ presenceId }) => + parse(await (await getPresencesClient()).delete(presenceId)), + ), + ); + + diff --git a/lib/commands/services/project.ts b/lib/commands/services/project.ts index b30e5580..6622b7a9 100644 --- a/lib/commands/services/project.ts +++ b/lib/commands/services/project.ts @@ -271,9 +271,31 @@ const projectDeleteMockPhoneCommand = project const projectListOAuth2ProvidersCommand = project .command(`list-o-auth-2-providers`) .description(`Get a list of all OAuth2 providers supported by the server, along with the project's configuration for each. Credential fields are write-only and always returned empty.`) + .option(`--queries [queries...]`, `Raw Appwrite JSON query strings (legacy). Use this for advanced queries or automation; for common pagination prefer --limit and --offset. When mixed, raw --queries are sent before generated flag queries. Array of query strings generated using the Query class provided by the SDK. Learn more about queries (https://appwrite.io/docs/queries). Only supported methods are limit and offset`) + .option( + `--total [value]`, + `When set to false, the total count returned will be 0 and will not be calculated.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .option(`--limit `, `Maximum number of results to return.`, parseInteger) + .option(`--offset `, `Number of results to skip.`, parseInteger) + .action( + actionRunner( + async ({ queries, total, limit, offset }) => + parse(await (await getProjectClient()).listOAuth2Providers(buildQueries({ queries, limit, offset }), total)), + ), + ); + + +const projectGetOAuth2ProviderCommand = project + .command(`get-o-auth-2-provider`) + .description(`Get a single OAuth2 provider configuration. Credential fields (client secret, p8 file, key/team IDs) are write-only and always returned empty.`) + .requiredOption(`--provider-id `, `OAuth2 provider key. For example: github, google, apple.`) .action( actionRunner( - async () => parse(await (await getProjectClient()).listOAuth2Providers()), + async ({ providerId }) => + parse(await (await getProjectClient()).getOAuth2Provider(providerId)), ), ); @@ -629,8 +651,8 @@ const projectUpdateOAuth2GitlabCommand = project const projectUpdateOAuth2GoogleCommand = project .command(`update-o-auth-2-google`) .description(`Update the project OAuth2 Google configuration.`) - .option(`--client-id `, `'Client ID' of Google OAuth2 app. For example: 120000000095-92ifjb00000000000000000000g7ijfb.apps.googleusercontent.com`) - .option(`--client-secret `, `'Client Secret' of Google OAuth2 app. For example: GOCSPX-2k8gsR0000000000000000VNahJj`) + .option(`--client-id `, `'Client ID' of Google OAuth2 app. For example: your-google-client-id.apps.googleusercontent.com`) + .option(`--client-secret `, `'Client Secret' of Google OAuth2 app. For example: your-google-client-secret`) .option( `--enabled [value]`, `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, @@ -689,7 +711,7 @@ const projectUpdateOAuth2LinkedinCommand = project .command(`update-o-auth-2-linkedin`) .description(`Update the project OAuth2 Linkedin configuration.`) .option(`--client-id `, `'Client ID' of Linkedin OAuth2 app. For example: 770000000000dv`) - .option(`--primary-client-secret `, `'Primary Client Secret or Secondary Client Secret' of Linkedin OAuth2 app. For example: WPL_AP1.2Bf0000000000000./HtlYw==`) + .option(`--primary-client-secret `, `'Primary Client Secret or Secondary Client Secret' of Linkedin OAuth2 app. For example: your-linkedin-client-secret`) .option( `--enabled [value]`, `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, @@ -1091,18 +1113,6 @@ const projectUpdateOAuth2ZoomCommand = project ); -const projectGetOAuth2ProviderCommand = project - .command(`get-o-auth-2-provider`) - .description(`Get a single OAuth2 provider configuration. Credential fields (client secret, p8 file, key/team IDs) are write-only and always returned empty.`) - .requiredOption(`--provider `, `OAuth2 provider key. For example: github, google, apple.`) - .action( - actionRunner( - async ({ provider }) => - parse(await (await getProjectClient()).getOAuth2Provider(provider)), - ), - ); - - const projectListPlatformsCommand = project .command(`list-platforms`) .description(`Get a list of all platforms in the project. This endpoint returns an array of all platforms and their configurations.`) @@ -1621,7 +1631,7 @@ const projectListVariablesCommand = project const projectCreateVariableCommand = project .command(`create-variable`) .description(`Create a new project environment variable. These variables can be accessed by all functions and sites in the project.`) - .requiredOption(`--variable-id `, `Variable ID. Choose a custom ID or generate a random ID with \`ID.unique()\`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars.`) + .requiredOption(`--variable-id `, `Variable unique ID. Choose a custom ID or generate a random ID with \`ID.unique()\`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars.`) .requiredOption(`--key `, `Variable key. Max length: 255 chars.`) .requiredOption(`--value `, `Variable value. Max length: 8192 chars.`) .option( @@ -1641,7 +1651,7 @@ const projectCreateVariableCommand = project const projectGetVariableCommand = project .command(`get-variable`) .description(`Get a variable by its unique ID. `) - .requiredOption(`--variable-id `, `Variable ID.`) + .requiredOption(`--variable-id `, `Variable unique ID.`) .action( actionRunner( async ({ variableId }) => @@ -1653,7 +1663,7 @@ const projectGetVariableCommand = project const projectUpdateVariableCommand = project .command(`update-variable`) .description(`Update variable by its unique ID.`) - .requiredOption(`--variable-id `, `Variable ID.`) + .requiredOption(`--variable-id `, `Variable unique ID.`) .option(`--key `, `Variable key. Max length: 255 chars.`) .option(`--value `, `Variable value. Max length: 8192 chars.`) .option( @@ -1673,7 +1683,7 @@ const projectUpdateVariableCommand = project const projectDeleteVariableCommand = project .command(`delete-variable`) .description(`Delete a variable by its unique ID. `) - .requiredOption(`--variable-id `, `Variable ID.`) + .requiredOption(`--variable-id `, `Variable unique ID.`) .action( actionRunner( async ({ variableId }) => diff --git a/lib/commands/services/proxy.ts b/lib/commands/services/proxy.ts index dc9542bb..1d5afe0a 100644 --- a/lib/commands/services/proxy.ts +++ b/lib/commands/services/proxy.ts @@ -35,7 +35,6 @@ const proxyListRulesCommand = proxy .command(`list-rules`) .description(`Get a list of all the proxy rules. You can use the query params to filter your results.`) .option(`--queries [queries...]`, `Raw Appwrite JSON query strings (legacy). Use this for advanced queries or automation; for common filtering, sorting, and pagination prefer --where, --sort-asc, --sort-desc, --limit, and --offset. When mixed, raw --queries are sent before generated flag queries. Array of query strings generated using the Query class provided by the SDK. Learn more about queries (https://appwrite.io/docs/databases#querying-documents). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: domain, type, trigger, deploymentResourceType, deploymentResourceId, deploymentId, deploymentVcsProviderBranch`) - .option(`--search `, `Search term to filter your list results. Max length: 256 chars.`) .option( `--total [value]`, `When set to false, the total count returned will be 0 and will not be calculated.`, @@ -51,15 +50,17 @@ const proxyListRulesCommand = proxy .option(`--cursor-before `, `Return results before this cursor ID.`) .action( actionRunner( - async ({ queries, search, total, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }) => - parse(await (await getProxyClient()).listRules(buildQueries({ queries, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }), search, total)), + async ({ queries, total, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }) => + parse(await (await getProxyClient()).listRules(buildQueries({ queries, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }), total)), ), ); const proxyCreateAPIRuleCommand = proxy .command(`create-api-rule`) - .description(`Create a new proxy rule for serving Appwrite's API on custom domain.`) + .description(`Create a new proxy rule for serving Appwrite's API on custom domain. + +Rule ID is automatically generated as MD5 hash of a rule domain for performance purposes.`) .requiredOption(`--domain `, `Domain name.`) .action( actionRunner( @@ -71,7 +72,9 @@ const proxyCreateAPIRuleCommand = proxy const proxyCreateFunctionRuleCommand = proxy .command(`create-function-rule`) - .description(`Create a new proxy rule for executing Appwrite Function on custom domain.`) + .description(`Create a new proxy rule for executing Appwrite Function on custom domain. + +Rule ID is automatically generated as MD5 hash of a rule domain for performance purposes.`) .requiredOption(`--domain `, `Domain name.`) .requiredOption(`--function-id `, `ID of function to be executed.`) .option(`--branch `, `Name of VCS branch to deploy changes automatically`) @@ -85,7 +88,9 @@ const proxyCreateFunctionRuleCommand = proxy const proxyCreateRedirectRuleCommand = proxy .command(`create-redirect-rule`) - .description(`Create a new proxy rule for to redirect from custom domain to another domain.`) + .description(`Create a new proxy rule for to redirect from custom domain to another domain. + +Rule ID is automatically generated as MD5 hash of a rule domain for performance purposes.`) .requiredOption(`--domain `, `Domain name.`) .requiredOption(`--url `, `Target URL of redirection`) .requiredOption(`--status-code `, `Status code of redirection`) @@ -101,7 +106,9 @@ const proxyCreateRedirectRuleCommand = proxy const proxyCreateSiteRuleCommand = proxy .command(`create-site-rule`) - .description(`Create a new proxy rule for serving Appwrite Site on custom domain.`) + .description(`Create a new proxy rule for serving Appwrite Site on custom domain. + +Rule ID is automatically generated as MD5 hash of a rule domain for performance purposes.`) .requiredOption(`--domain `, `Domain name.`) .requiredOption(`--site-id `, `ID of site to be executed.`) .option(`--branch `, `Name of VCS branch to deploy changes automatically`) @@ -137,14 +144,14 @@ const proxyDeleteRuleCommand = proxy ); -const proxyUpdateRuleVerificationCommand = proxy - .command(`update-rule-verification`) - .description(`Retry getting verification process of a proxy rule. This endpoint triggers domain verification by checking DNS records (CNAME) against the configured target domain. If verification is successful, a TLS certificate will be automatically provisioned for the domain.`) +const proxyUpdateRuleStatusCommand = proxy + .command(`update-rule-status`) + .description(`If not succeeded yet, retry verification process of a proxy rule domain. This endpoint triggers domain verification by checking DNS records. If verification is successful, a TLS certificate will be automatically provisioned for the domain asynchronously in the background.`) .requiredOption(`--rule-id `, `Rule ID.`) .action( actionRunner( async ({ ruleId }) => - parse(await (await getProxyClient()).updateRuleVerification(ruleId)), + parse(await (await getProxyClient()).updateRuleStatus(ruleId)), ), ); diff --git a/lib/commands/services/sites.ts b/lib/commands/services/sites.ts index 68bb68b8..a802767d 100644 --- a/lib/commands/services/sites.ts +++ b/lib/commands/services/sites.ts @@ -486,10 +486,24 @@ const sitesListVariablesCommand = sites .command(`list-variables`) .description(`Get a list of all variables of a specific site.`) .requiredOption(`--site-id `, `Site unique ID.`) + .option(`--queries [queries...]`, `Raw Appwrite JSON query strings (legacy). Use this for advanced queries or automation; for common filtering, sorting, and pagination prefer --where, --sort-asc, --sort-desc, --limit, and --offset. When mixed, raw --queries are sent before generated flag queries. Array of query strings generated using the Query class provided by the SDK. Learn more about queries (https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: key, resourceType, resourceId, secret`) + .option( + `--total [value]`, + `When set to false, the total count returned will be 0 and will not be calculated.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .option(`--where `, `Filter using a simple comparison expression. Repeat for multiple filters. Supports field=value, field!=value, field>value, field>=value, field collectQueryValue(parseWhereQuery(value), previous)) + .option(`--sort-asc `, `Sort results by an attribute in ascending order. Repeat for multiple sort fields.`, (value: string, previous: string[] | undefined) => collectQueryValue(value, previous)) + .option(`--sort-desc `, `Sort results by an attribute in descending order. Repeat for multiple sort fields.`, (value: string, previous: string[] | undefined) => collectQueryValue(value, previous)) + .option(`--limit `, `Maximum number of results to return.`, parseInteger) + .option(`--offset `, `Number of results to skip.`, parseInteger) + .option(`--cursor-after `, `Return results after this cursor ID.`) + .option(`--cursor-before `, `Return results before this cursor ID.`) .action( actionRunner( - async ({ siteId }) => - parse(await (await getSitesClient()).listVariables(siteId)), + async ({ siteId, queries, total, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }) => + parse(await (await getSitesClient()).listVariables(siteId, buildQueries({ queries, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }), total)), ), ); @@ -498,6 +512,7 @@ const sitesCreateVariableCommand = sites .command(`create-variable`) .description(`Create a new site variable. These variables can be accessed during build and runtime (server-side rendering) as environment variables.`) .requiredOption(`--site-id `, `Site unique ID.`) + .requiredOption(`--variable-id `, `Variable ID. Choose a custom ID or generate a random ID with \`ID.unique()\`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars.`) .requiredOption(`--key `, `Variable key. Max length: 255 chars.`) .requiredOption(`--value `, `Variable value. Max length: 8192 chars.`) .option( @@ -508,8 +523,8 @@ const sitesCreateVariableCommand = sites ) .action( actionRunner( - async ({ siteId, key, value, secret }) => - parse(await (await getSitesClient()).createVariable(siteId, key, value, secret)), + async ({ siteId, variableId, key, value, secret }) => + parse(await (await getSitesClient()).createVariable(siteId, variableId, key, value, secret)), ), ); @@ -532,7 +547,7 @@ const sitesUpdateVariableCommand = sites .description(`Update variable by its unique ID.`) .requiredOption(`--site-id `, `Site unique ID.`) .requiredOption(`--variable-id `, `Variable unique ID.`) - .requiredOption(`--key `, `Variable key. Max length: 255 chars.`) + .option(`--key `, `Variable key. Max length: 255 chars.`) .option(`--value `, `Variable value. Max length: 8192 chars.`) .option( `--secret [value]`, diff --git a/lib/commands/services/tables-db.ts b/lib/commands/services/tables-db.ts index eae8e72a..f6207acd 100644 --- a/lib/commands/services/tables-db.ts +++ b/lib/commands/services/tables-db.ts @@ -367,6 +367,51 @@ const tablesDBListColumnsCommand = tablesDB ); +const tablesDBCreateBigIntColumnCommand = tablesDB + .command(`create-big-int-column`) + .description(`Create a bigint column. Optionally, minimum and maximum values can be provided. +`) + .requiredOption(`--database-id `, `Database ID.`) + .requiredOption(`--table-id `, `Table ID.`) + .requiredOption(`--key `, `Column Key.`) + .requiredOption(`--required `, `Is column required?`, parseBool) + .option(`--min `, `Minimum value`, parseInteger) + .option(`--max `, `Maximum value`, parseInteger) + .option(`--xdefault `, `Default value. Cannot be set when column is required.`, parseInteger) + .option( + `--array [value]`, + `Is column an array?`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ databaseId, tableId, key, required, min, max, xdefault, array }) => + parse(await (await getTablesDBClient()).createBigIntColumn(databaseId, tableId, key, required, min, max, xdefault, array)), + ), + ); + + +const tablesDBUpdateBigIntColumnCommand = tablesDB + .command(`update-big-int-column`) + .description(`Update a bigint column. Changing the \`default\` value will not update already existing rows. +`) + .requiredOption(`--database-id `, `Database ID.`) + .requiredOption(`--table-id `, `Table ID.`) + .requiredOption(`--key `, `Column Key.`) + .requiredOption(`--required `, `Is column required?`, parseBool) + .requiredOption(`--xdefault `, `Default value. Cannot be set when column is required.`, parseInteger) + .option(`--min `, `Minimum value`, parseInteger) + .option(`--max `, `Maximum value`, parseInteger) + .option(`--new-key `, `New Column Key.`) + .action( + actionRunner( + async ({ databaseId, tableId, key, required, xdefault, min, max, newKey }) => + parse(await (await getTablesDBClient()).updateBigIntColumn(databaseId, tableId, key, required, xdefault, min, max, newKey)), + ), + ); + + const tablesDBCreateBooleanColumnCommand = tablesDB .command(`create-boolean-column`) .description(`Create a boolean column. diff --git a/lib/config.ts b/lib/config.ts index 4789c074..c3d7e74f 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -103,22 +103,6 @@ function isRecord(value: unknown): value is Record { return value !== null && typeof value === "object" && !Array.isArray(value); } -export function normalizeCloudConsoleEndpoint(endpoint: string): string { - try { - const url = new URL(endpoint); - if ( - url.hostname === "cloud.appwrite.io" || - url.hostname.endsWith(".cloud.appwrite.io") - ) { - return "https://cloud.appwrite.io/v1"; - } - } catch (_error) { - return endpoint; - } - - return endpoint; -} - function ensureDirectoryForFile(filePath: string): void { const dir = _path.dirname(filePath); if (!fs.existsSync(dir)) { @@ -1217,17 +1201,10 @@ class Global extends Config { sessions.forEach((sessionId) => { const sessionData = (this.data as any)[sessionId]; const email = sessionData[Global.PREFERENCE_EMAIL] ?? ""; - const endpoint = normalizeCloudConsoleEndpoint( - sessionData[Global.PREFERENCE_ENDPOINT] ?? "", - ); + const endpoint = sessionData[Global.PREFERENCE_ENDPOINT] ?? ""; const key = `${email}|${endpoint}`; - const existingSession = sessionMap.get(key); - if ( - !existingSession || - sessionId === current || - existingSession.id !== current - ) { + if (sessionId === current || !sessionMap.has(key)) { sessionMap.set(key, { id: sessionId, endpoint, diff --git a/lib/constants.ts b/lib/constants.ts index 6b6d9935..5302c744 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -1,7 +1,7 @@ // SDK export const SDK_TITLE = 'Appwrite'; export const SDK_TITLE_LOWER = 'appwrite'; -export const SDK_VERSION = '20.1.0'; +export const SDK_VERSION = '20.2.0'; export const SDK_NAME = 'Command Line'; export const SDK_PLATFORM = 'console'; export const SDK_LANGUAGE = 'cli'; diff --git a/lib/questions.ts b/lib/questions.ts index bea21526..3f7a5183 100644 --- a/lib/questions.ts +++ b/lib/questions.ts @@ -822,6 +822,16 @@ export const questionsPullCollection: Question[] = [ ]; export const questionsLogin: Question[] = [ + { + type: "list", + name: "method", + message: "What would you like to do?", + choices: [ + { name: "Login to an account", value: "login" }, + { name: "Switch to an account", value: "select" }, + ], + when: () => globalConfig.getSessions().length >= 2, + }, { type: "input", name: "email", @@ -832,6 +842,7 @@ export const questionsLogin: Question[] = [ } return true; }, + when: (answers: Answers) => answers.method !== "select", }, { type: "password", @@ -844,36 +855,38 @@ export const questionsLogin: Question[] = [ } return true; }, + when: (answers: Answers) => answers.method !== "select", }, -]; - -export const questionsSwitchAccount: Question[] = [ { type: "list", name: "accountId", - message: "Select account:", + message: "Select an account to use", choices() { const sessions = globalConfig.getSessions(); const current = globalConfig.getCurrentSession(); const data: Choice[] = []; + const longestEmail = sessions.reduce((prev: any, current: any) => + prev && (prev.email ?? "").length > (current.email ?? "").length + ? prev + : current, + ).email.length; + sessions.forEach((session: any) => { if (session.email) { data.push({ current: current === session.id, value: session.id, - short: session.email, - name: - session.endpoint === DEFAULT_ENDPOINT - ? session.email - : `${session.email} (${session.endpoint})`, + short: `${session.email} (${session.endpoint})`, + name: `${session.email.padEnd(longestEmail)} ${current === session.id ? chalk.green.bold("current") : " ".repeat(6)} ${session.endpoint}`, }); } }); return data.sort((a, b) => Number(b.current) - Number(a.current)); }, + when: (answers: Answers) => answers.method === "select", }, ]; diff --git a/lib/utils.ts b/lib/utils.ts index fd307162..3e5e727d 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -317,7 +317,7 @@ const getHomebrewLatestVersion = async ( } }; -export const isCloudHostname = (hostname: string): boolean => +const isCloudHostname = (hostname: string): boolean => hostname === "cloud.appwrite.io" || hostname.endsWith(".cloud.appwrite.io"); export const getConsoleBaseUrl = (endpoint: string): string => { diff --git a/package-lock.json b/package-lock.json index f2a94c3c..0aa42480 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "appwrite-cli", - "version": "20.1.0", + "version": "20.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "appwrite-cli", - "version": "20.1.0", + "version": "20.2.0", "license": "BSD-3-Clause", "dependencies": { "@appwrite.io/console": "12.0.0", diff --git a/package.json b/package.json index 08eea694..c6c3b4c0 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "type": "module", "homepage": "https://appwrite.io/support", "description": "Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API", - "version": "20.1.0", + "version": "20.2.0", "license": "BSD-3-Clause", "main": "dist/index.cjs", "module": "dist/index.js", diff --git a/scoop/appwrite.config.json b/scoop/appwrite.config.json index 25d0cd84..ba3dab4f 100644 --- a/scoop/appwrite.config.json +++ b/scoop/appwrite.config.json @@ -1,12 +1,12 @@ { "$schema": "https://raw.githubusercontent.com/ScoopInstaller/Scoop/master/schema.json", - "version": "20.1.0", + "version": "20.2.0", "description": "The Appwrite CLI is a command-line application that allows you to interact with Appwrite and perform server-side tasks using your terminal.", "homepage": "https://github.com/appwrite/sdk-for-cli", "license": "BSD-3-Clause", "architecture": { "64bit": { - "url": "https://github.com/appwrite/sdk-for-cli/releases/download/20.1.0/appwrite-cli-win-x64.exe", + "url": "https://github.com/appwrite/sdk-for-cli/releases/download/20.2.0/appwrite-cli-win-x64.exe", "bin": [ [ "appwrite-cli-win-x64.exe", @@ -15,7 +15,7 @@ ] }, "arm64": { - "url": "https://github.com/appwrite/sdk-for-cli/releases/download/20.1.0/appwrite-cli-win-arm64.exe", + "url": "https://github.com/appwrite/sdk-for-cli/releases/download/20.2.0/appwrite-cli-win-arm64.exe", "bin": [ [ "appwrite-cli-win-arm64.exe",