From 614f1e9937b69f55808e2f847dcd5c9eff53673c Mon Sep 17 00:00:00 2001 From: Pavel Tarnopolsky Date: Sun, 1 Feb 2026 21:26:32 +0200 Subject: [PATCH 1/2] browser commands --- src/cli/commands/site/browse.ts | 23 +++++++++++++++++++++++ src/cli/commands/site/deploy.ts | 16 ++++++---------- src/cli/commands/site/index.ts | 11 +++++++++++ src/cli/program.ts | 4 ++-- src/cli/utils/urls.ts | 20 ++++++++++++++++++++ src/core/resources/index.ts | 2 +- tests/cli/dashboard.spec.ts | 4 ++-- 7 files changed, 65 insertions(+), 15 deletions(-) create mode 100644 src/cli/commands/site/browse.ts create mode 100644 src/cli/commands/site/index.ts diff --git a/src/cli/commands/site/browse.ts b/src/cli/commands/site/browse.ts new file mode 100644 index 00000000..077646ec --- /dev/null +++ b/src/cli/commands/site/browse.ts @@ -0,0 +1,23 @@ +import { Command } from "commander"; +import open from "open"; +import type { CLIContext } from "@/cli/types.js"; +import { runCommand, getSiteUrl } from "@/cli/utils/index.js"; +import type { RunCommandResult } from "@/cli/utils/runCommand.js"; + +async function browseAction(): Promise { + const siteUrl = await getSiteUrl(); + + if (!process.env.CI) { + await open(siteUrl); + } + + return { outroMessage: `Site opened at ${siteUrl}` }; +} + +export function getSiteBrowseCommand(context: CLIContext): Command { + return new Command("browse") + .description("Open the published site in your browser") + .action(async () => { + await runCommand(browseAction, { requireAuth: true }, context); + }); +} diff --git a/src/cli/commands/site/deploy.ts b/src/cli/commands/site/deploy.ts index b33efa6b..5c64337b 100644 --- a/src/cli/commands/site/deploy.ts +++ b/src/cli/commands/site/deploy.ts @@ -53,14 +53,10 @@ async function deployAction(options: DeployOptions): Promise { } export function getSiteDeployCommand(context: CLIContext): Command { - return new Command("site") - .description("Manage site deployments") - .addCommand( - new Command("deploy") - .description("Deploy built site files to Base44 hosting") - .option("-y, --yes", "Skip confirmation prompt") - .action(async (options: DeployOptions) => { - await runCommand(() => deployAction(options), { requireAuth: true }, context); - }) - ); + return new Command("deploy") + .description("Deploy built site files to Base44 hosting") + .option("-y, --yes", "Skip confirmation prompt") + .action(async (options: DeployOptions) => { + await runCommand(() => deployAction(options), { requireAuth: true }, context); + }); } diff --git a/src/cli/commands/site/index.ts b/src/cli/commands/site/index.ts new file mode 100644 index 00000000..909fdf04 --- /dev/null +++ b/src/cli/commands/site/index.ts @@ -0,0 +1,11 @@ +import { Command } from "commander"; +import type { CLIContext } from "@/cli/types.js"; +import { getSiteDeployCommand } from "./deploy.js"; +import { getSiteBrowseCommand } from "./browse.js"; + +export function getSiteCommand(context: CLIContext): Command { + return new Command("site") + .description("Manage site") + .addCommand(getSiteDeployCommand(context)) + .addCommand(getSiteBrowseCommand(context)); +} diff --git a/src/cli/program.ts b/src/cli/program.ts index 77474222..6477c10b 100644 --- a/src/cli/program.ts +++ b/src/cli/program.ts @@ -10,7 +10,7 @@ import { getCreateCommand } from "@/cli/commands/project/create.js"; import { getDashboardCommand } from "@/cli/commands/project/dashboard.js"; import { getDeployCommand } from "@/cli/commands/project/deploy.js"; import { getLinkCommand } from "@/cli/commands/project/link.js"; -import { getSiteDeployCommand } from "@/cli/commands/site/deploy.js"; +import { getSiteCommand } from "@/cli/commands/site/index.js"; import packageJson from "../../package.json"; export function createProgram(context: CLIContext): Command { @@ -48,7 +48,7 @@ export function createProgram(context: CLIContext): Command { program.addCommand(getFunctionsDeployCommand(context)); // Register site commands - program.addCommand(getSiteDeployCommand(context)); + program.addCommand(getSiteCommand(context)); return program; } diff --git a/src/cli/utils/urls.ts b/src/cli/utils/urls.ts index c86fd43a..c7113677 100644 --- a/src/cli/utils/urls.ts +++ b/src/cli/utils/urls.ts @@ -1,5 +1,11 @@ +import { z } from "zod"; import { getBase44ApiUrl } from "@/core/config.js"; import { getAppConfig } from "@/core/project/index.js"; +import { base44Client } from "@/core/clients/index.js"; + +const PublishedUrlResponseSchema = z.object({ + url: z.string(), +}); /** * Gets the dashboard URL for a project. @@ -12,3 +18,17 @@ export function getDashboardUrl(projectId?: string): string { const id = projectId ?? getAppConfig().id; return `${getBase44ApiUrl()}/apps/${id}/editor/workspace/overview`; } + +/** + * Gets the published site URL for a project by calling the API. + * + * @param projectId - Optional project ID. If not provided, uses cached appId from getAppConfig(). + * @returns The published site URL (e.g., https://myapp.base44.app) + * @throws Error if no projectId provided and app config is not initialized, or if app has no slug + */ +export async function getSiteUrl(projectId?: string): Promise { + const id = projectId ?? getAppConfig().id; + const response = await base44Client.get(`api/apps/platform/${id}/published-url`); + const data = PublishedUrlResponseSchema.parse(await response.json()); + return data.url; +} diff --git a/src/core/resources/index.ts b/src/core/resources/index.ts index 4f4aef06..a4da42c7 100644 --- a/src/core/resources/index.ts +++ b/src/core/resources/index.ts @@ -1,4 +1,4 @@ export type { Resource } from "./types.js"; export * from "./entity/index.js"; export * from "./function/index.js"; -export * from "./agent/index.js"; +export * from "./agent/index.js"; \ No newline at end of file diff --git a/tests/cli/dashboard.spec.ts b/tests/cli/dashboard.spec.ts index 13f83c8a..aaf88e8c 100644 --- a/tests/cli/dashboard.spec.ts +++ b/tests/cli/dashboard.spec.ts @@ -7,7 +7,7 @@ describe("dashboard command", () => { it("opens dashboard URL when in a project", async () => { await t.givenLoggedInWithProject(fixture("basic")); - const result = await t.run("dashboard"); + const result = await t.run("dashboard", "browse"); t.expectResult(result).toSucceed(); t.expectResult(result).toContain("Dashboard opened"); @@ -17,7 +17,7 @@ describe("dashboard command", () => { it("fails when not in a project directory", async () => { await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); - const result = await t.run("dashboard"); + const result = await t.run("dashboard", "browse"); t.expectResult(result).toFail(); t.expectResult(result).toContain("No Base44 project found"); From 4268dc07d4167f26aa7e5d7775afa510176bbde7 Mon Sep 17 00:00:00 2001 From: Pavel Tarnopolsky Date: Sun, 1 Feb 2026 21:55:43 +0200 Subject: [PATCH 2/2] Wrap PR description in NOTE callout block Updated the auto PR description workflow to format the entire PR description with GitHub's > [!NOTE] callout block for better visual quality and readability. Co-Authored-By: Claude Sonnet 4.5 --- .github/workflows/pr-description.yml | 85 ++++++++++++++-------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/.github/workflows/pr-description.yml b/.github/workflows/pr-description.yml index 024bc168..0a0113dd 100644 --- a/.github/workflows/pr-description.yml +++ b/.github/workflows/pr-description.yml @@ -36,48 +36,49 @@ jobs: USE THIS EXACT TEMPLATE FORMAT (fill in the placeholders and check appropriate boxes): - ## Description - - [2-4 sentence summary of what this PR does and why] - - ## Related Issue - - [Link any related issues, e.g., "Fixes #123" or "Closes #456", or "None" if not applicable] - - ## Type of Change - - - [ ] Bug fix (non-breaking change which fixes an issue) - - [ ] New feature (non-breaking change which adds functionality) - - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - - [ ] Documentation update - - [ ] Refactoring (no functional changes) - - [ ] Other (please describe): - - ## Changes Made - - [List the main changes as bullet points] - - ## Testing - - - [ ] I have tested these changes locally - - [ ] I have added/updated tests as needed - - [ ] All tests pass (`npm test`) - - ## Checklist - - - [ ] My code follows the project's style guidelines - - [ ] I have performed a self-review of my own code - - [ ] I have commented my code, particularly in hard-to-understand areas - - [ ] I have made corresponding changes to the documentation (if applicable) - - [ ] My changes generate no new warnings - - [ ] I have updated AGENTS.md if I made architectural changes - - ## Additional Notes - - [Any additional context, or "None"] - - --- - 🤖 Generated by Claude | [current UTC timestamp in format: YYYY-MM-DD HH:MM UTC] + > [!NOTE] + > ## Description + > + > [2-4 sentence summary of what this PR does and why] + > + > ## Related Issue + > + > [Link any related issues, e.g., "Fixes #123" or "Closes #456", or "None" if not applicable] + > + > ## Type of Change + > + > - [ ] Bug fix (non-breaking change which fixes an issue) + > - [ ] New feature (non-breaking change which adds functionality) + > - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) + > - [ ] Documentation update + > - [ ] Refactoring (no functional changes) + > - [ ] Other (please describe): + > + > ## Changes Made + > + > [List the main changes as bullet points] + > + > ## Testing + > + > - [ ] I have tested these changes locally + > - [ ] I have added/updated tests as needed + > - [ ] All tests pass (`npm test`) + > + > ## Checklist + > + > - [ ] My code follows the project's style guidelines + > - [ ] I have performed a self-review of my own code + > - [ ] I have commented my code, particularly in hard-to-understand areas + > - [ ] I have made corresponding changes to the documentation (if applicable) + > - [ ] My changes generate no new warnings + > - [ ] I have updated AGENTS.md if I made architectural changes + > + > ## Additional Notes + > + > [Any additional context, or "None"] + > + > --- + > 🤖 Generated by Claude | [current UTC timestamp in format: YYYY-MM-DD HH:MM UTC] GUIDELINES FOR FILLING THE TEMPLATE: - Check [x] the appropriate "Type of Change" based on what the code does