From c6e554faa3725aab4416b569c1ca6e573a7b2167 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 12:21:22 +0000 Subject: [PATCH 1/4] feat: add dashboard command to open app dashboard in browser - Add new 'dashboard' command under project commands - Opens browser to app dashboard page using platform-specific command - Throws error if BASE44_CLIENT_ID is not configured - Logs 'Opening dashboard...' message before opening - Falls back to displaying URL if browser opening fails Resolves #80 Co-authored-by: Kfir Stri --- src/cli/commands/project/dashboard.ts | 53 +++++++++++++++++++++++++++ src/cli/index.ts | 2 + 2 files changed, 55 insertions(+) create mode 100644 src/cli/commands/project/dashboard.ts diff --git a/src/cli/commands/project/dashboard.ts b/src/cli/commands/project/dashboard.ts new file mode 100644 index 000000000..5b92ef387 --- /dev/null +++ b/src/cli/commands/project/dashboard.ts @@ -0,0 +1,53 @@ +import { Command } from "commander"; +import { log } from "@clack/prompts"; +import { execa } from "execa"; +import { getBase44ApiUrl, getBase44ClientId, loadProjectEnv } from "@core/config.js"; +import { runCommand, theme } from "../../utils/index.js"; +import type { RunCommandResult } from "../../utils/runCommand.js"; + +async function openDashboard(): Promise { + // Load project environment to get the project ID + await loadProjectEnv(); + + const projectId = getBase44ClientId(); + + if (!projectId) { + throw new Error( + "App not configured. BASE44_CLIENT_ID environment variable is required. Set it in your .env.local file." + ); + } + + const dashboardUrl = `${getBase44ApiUrl()}/apps/${projectId}/editor/preview`; + + log.info(theme.colors.base44Orange("Opening dashboard...")); + + // Determine the command to open the browser based on platform + const platform = process.platform; + let openCommand: string; + + if (platform === "darwin") { + openCommand = "open"; + } else if (platform === "win32") { + openCommand = "start"; + } else { + // Linux and other Unix-like systems + openCommand = "xdg-open"; + } + + try { + await execa(openCommand, [dashboardUrl], { shell: true }); + } catch (error) { + // If the command fails, just log the URL for the user to open manually + log.warn("Could not open browser automatically"); + log.message(`${theme.styles.header("Dashboard")}: ${theme.colors.links(dashboardUrl)}`); + return {}; + } + + return { outroMessage: `Dashboard opened at ${dashboardUrl}` }; +} + +export const dashboardCommand = new Command("dashboard") + .description("Open the app dashboard in your browser") + .action(async () => { + await runCommand(openDashboard); + }); diff --git a/src/cli/index.ts b/src/cli/index.ts index 1527ffc50..308499bda 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -7,6 +7,7 @@ import { logoutCommand } from "./commands/auth/logout.js"; import { entitiesPushCommand } from "./commands/entities/push.js"; import { functionsDeployCommand } from "./commands/functions/deploy.js"; import { createCommand } from "./commands/project/create.js"; +import { dashboardCommand } from "./commands/project/dashboard.js"; import { siteDeployCommand } from "./commands/site/deploy.js"; import packageJson from "../../package.json"; @@ -26,6 +27,7 @@ program.addCommand(logoutCommand); // Register project commands program.addCommand(createCommand); +program.addCommand(dashboardCommand); // Register entities commands program.addCommand(entitiesPushCommand); From ae793ccb548e93bdc2019e3e7f2bed84138af0b1 Mon Sep 17 00:00:00 2001 From: Kfir Strikovsky Date: Wed, 21 Jan 2026 15:43:25 +0200 Subject: [PATCH 2/4] added dashboard command --- package-lock.json | 188 ++++++++++++++++++++++++++ package.json | 3 +- src/cli/commands/project/dashboard.ts | 27 +--- 3 files changed, 193 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index 965228396..f281143c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "json5": "^2.2.3", "ky": "^1.14.2", "lodash.kebabcase": "^4.1.1", + "open": "^11.0.0", "p-wait-for": "^6.0.0", "tar": "^7.5.3", "tsdown": "^0.12.4", @@ -2417,6 +2418,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -2748,6 +2765,36 @@ "dev": true, "license": "MIT" }, + "node_modules/default-browser": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.4.0.tgz", + "integrity": "sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -2766,6 +2813,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-properties": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", @@ -4362,6 +4422,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -4421,6 +4497,38 @@ "node": ">=0.10.0" } }, + "node_modules/is-in-ssh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", + "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -4671,6 +4779,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", @@ -5137,6 +5261,27 @@ ], "license": "MIT" }, + "node_modules/open": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.0.tgz", + "integrity": "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.4.0", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.1.0", + "wsl-utils": "^0.3.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -5347,6 +5492,19 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/powershell-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", + "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5671,6 +5829,19 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -6851,6 +7022,23 @@ "node": ">=0.10.0" } }, + "node_modules/wsl-utils": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz", + "integrity": "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/yallist": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", diff --git a/package.json b/package.json index 0fac38b8f..7666c677e 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,8 @@ "typescript": "^5.7.2", "typescript-eslint": "^8.52.0", "vitest": "^4.0.16", - "zod": "^4.3.5" + "zod": "^4.3.5", + "open": "^11.0.0" }, "engines": { "node": ">=20.19.0" diff --git a/src/cli/commands/project/dashboard.ts b/src/cli/commands/project/dashboard.ts index 5b92ef387..0f98dc10d 100644 --- a/src/cli/commands/project/dashboard.ts +++ b/src/cli/commands/project/dashboard.ts @@ -1,6 +1,7 @@ import { Command } from "commander"; import { log } from "@clack/prompts"; import { execa } from "execa"; +import open from 'open'; import { getBase44ApiUrl, getBase44ClientId, loadProjectEnv } from "@core/config.js"; import { runCommand, theme } from "../../utils/index.js"; import type { RunCommandResult } from "../../utils/runCommand.js"; @@ -17,31 +18,9 @@ async function openDashboard(): Promise { ); } - const dashboardUrl = `${getBase44ApiUrl()}/apps/${projectId}/editor/preview`; + const dashboardUrl = `${getBase44ApiUrl()}/apps/${projectId}/editor/workspace/overview`; - log.info(theme.colors.base44Orange("Opening dashboard...")); - - // Determine the command to open the browser based on platform - const platform = process.platform; - let openCommand: string; - - if (platform === "darwin") { - openCommand = "open"; - } else if (platform === "win32") { - openCommand = "start"; - } else { - // Linux and other Unix-like systems - openCommand = "xdg-open"; - } - - try { - await execa(openCommand, [dashboardUrl], { shell: true }); - } catch (error) { - // If the command fails, just log the URL for the user to open manually - log.warn("Could not open browser automatically"); - log.message(`${theme.styles.header("Dashboard")}: ${theme.colors.links(dashboardUrl)}`); - return {}; - } + await open(dashboardUrl); return { outroMessage: `Dashboard opened at ${dashboardUrl}` }; } From 69d1efc32dc195e71493ed4fddbe2c07b5ca8edb Mon Sep 17 00:00:00 2001 From: Kfir Strikovsky Date: Wed, 21 Jan 2026 15:47:32 +0200 Subject: [PATCH 3/4] remove imports and require auth --- src/cli/commands/project/dashboard.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/cli/commands/project/dashboard.ts b/src/cli/commands/project/dashboard.ts index 0f98dc10d..949b37c8f 100644 --- a/src/cli/commands/project/dashboard.ts +++ b/src/cli/commands/project/dashboard.ts @@ -1,9 +1,7 @@ import { Command } from "commander"; -import { log } from "@clack/prompts"; -import { execa } from "execa"; import open from 'open'; import { getBase44ApiUrl, getBase44ClientId, loadProjectEnv } from "@core/config.js"; -import { runCommand, theme } from "../../utils/index.js"; +import { runCommand } from "../../utils/index.js"; import type { RunCommandResult } from "../../utils/runCommand.js"; async function openDashboard(): Promise { @@ -28,5 +26,5 @@ async function openDashboard(): Promise { export const dashboardCommand = new Command("dashboard") .description("Open the app dashboard in your browser") .action(async () => { - await runCommand(openDashboard); + await runCommand(openDashboard, { requireAuth: true }); }); From b4382e877d484ff8c560a1faa4cd0b3c6dc986b6 Mon Sep 17 00:00:00 2001 From: Kfir Strikovsky Date: Wed, 21 Jan 2026 15:52:25 +0200 Subject: [PATCH 4/4] lint issue --- src/cli/commands/project/dashboard.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/commands/project/dashboard.ts b/src/cli/commands/project/dashboard.ts index 949b37c8f..8b58d8406 100644 --- a/src/cli/commands/project/dashboard.ts +++ b/src/cli/commands/project/dashboard.ts @@ -1,5 +1,5 @@ import { Command } from "commander"; -import open from 'open'; +import open from "open"; import { getBase44ApiUrl, getBase44ClientId, loadProjectEnv } from "@core/config.js"; import { runCommand } from "../../utils/index.js"; import type { RunCommandResult } from "../../utils/runCommand.js";