@base44-preview/cli 0.0.55-pr.545.7bded43 → 0.0.55-pr.545.7e19dbc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -218285,6 +218285,7 @@ var PROJECT_CONFIG_PATTERNS = [
218285
218285
  `${PROJECT_SUBDIR}/config.${CONFIG_FILE_EXTENSION_GLOB}`,
218286
218286
  `config.${CONFIG_FILE_EXTENSION_GLOB}`
218287
218287
  ];
218288
+ var BASE44_APP_ID_ENV_VAR = "BASE44_APP_ID";
218288
218289
  var TYPES_OUTPUT_SUBDIR = ".types";
218289
218290
  var TYPES_FILENAME = "types.d.ts";
218290
218291
  var AUTH_CLIENT_ID = "base44_cli";
@@ -233980,7 +233981,7 @@ async function parseEnvFile(filePath) {
233980
233981
  }
233981
233982
  var STRIPE_ENV_PREFIX = "BASE44_PROJECTS_";
233982
233983
  var BASE44_ENV_KEYS = [
233983
- "BASE44_APP_ID",
233984
+ BASE44_APP_ID_ENV_VAR,
233984
233985
  "BASE44_ACCESS_TOKEN",
233985
233986
  "BASE44_REFRESH_TOKEN",
233986
233987
  "BASE44_API_URL"
@@ -234017,7 +234018,7 @@ function normalizeBase44Env() {
234017
234018
  loadProjectEnvFiles();
234018
234019
 
234019
234020
  // src/cli/index.ts
234020
- import { dirname as dirname23, join as join29 } from "node:path";
234021
+ import { dirname as dirname23, join as join28 } from "node:path";
234021
234022
  import { fileURLToPath as fileURLToPath6 } from "node:url";
234022
234023
 
234023
234024
  // ../../node_modules/@clack/core/dist/index.mjs
@@ -241694,18 +241695,35 @@ function loadFromTestOverrides() {
241694
241695
  }
241695
241696
  return null;
241696
241697
  }
241697
- async function initAppContext() {
241698
+ async function initAppContext(options = {}) {
241698
241699
  const testConfig = loadFromTestOverrides();
241699
241700
  if (testConfig) {
241700
241701
  cache2 = testConfig;
241701
241702
  return cache2;
241702
241703
  }
241704
+ const projectRoot = findProjectRoot();
241705
+ if (options.appId !== undefined) {
241706
+ const id = options.appId.trim();
241707
+ if (!id) {
241708
+ throw new InvalidInputError("App id cannot be empty.");
241709
+ }
241710
+ cache2 = { id };
241711
+ return cache2;
241712
+ }
241703
241713
  if (cache2) {
241704
241714
  return cache2;
241705
241715
  }
241706
- const projectRoot = findProjectRoot();
241707
241716
  if (!projectRoot) {
241708
- throw new ConfigNotFoundError("No Base44 project found. Run this command from a project directory with a config.jsonc file.");
241717
+ throw new ConfigNotFoundError(`No Base44 app ID found. Pass --app-id, set ${BASE44_APP_ID_ENV_VAR}, or run this command from a linked project with base44/.app.jsonc.`, {
241718
+ hints: [
241719
+ { message: "Pass an app ID explicitly with --app-id <id>" },
241720
+ { message: `Set ${BASE44_APP_ID_ENV_VAR} in your environment` },
241721
+ {
241722
+ message: "Run from a linked Base44 project with base44/.app.jsonc, or run 'base44 link'",
241723
+ command: "base44 link"
241724
+ }
241725
+ ]
241726
+ });
241709
241727
  }
241710
241728
  const config3 = await readAppConfig(projectRoot.root);
241711
241729
  const appConfigPath = await findAppConfigPath(projectRoot.root);
@@ -244433,8 +244451,8 @@ async function ensureAuth(ctx) {
244433
244451
  });
244434
244452
  } catch {}
244435
244453
  }
244436
- async function ensureAppContext(ctx) {
244437
- const appContext = await initAppContext();
244454
+ async function ensureAppContext(ctx, options = {}) {
244455
+ const appContext = await initAppContext(options);
244438
244456
  ctx.app = appContext;
244439
244457
  ctx.errorReporter.setContext({ appId: appContext.id });
244440
244458
  }
@@ -251100,7 +251118,8 @@ class Base44Command extends Command {
251100
251118
  await ensureAuth(this.context);
251101
251119
  }
251102
251120
  if (this._commandOptions.requireAppContext) {
251103
- await ensureAppContext(this.context);
251121
+ const { appId } = this.optsWithGlobals();
251122
+ await ensureAppContext(this.context, { appId });
251104
251123
  }
251105
251124
  const result = await fn(this.context, ...args) ?? {};
251106
251125
  if (!quiet) {
@@ -253069,7 +253088,11 @@ function printProjectSummary({ name: name2, dashboardUrl, appUrl }, log) {
253069
253088
  }
253070
253089
 
253071
253090
  // src/cli/commands/project/create.ts
253072
- function validateNonInteractiveFlags(command2) {
253091
+ function validateCreateOptions(command2) {
253092
+ const { appId } = command2.optsWithGlobals();
253093
+ if (appId !== undefined) {
253094
+ command2.error(`base44 create cannot be used with --app-id or ${BASE44_APP_ID_ENV_VAR}.`);
253095
+ }
253073
253096
  const { path: path17 } = command2.opts();
253074
253097
  if (path17 && !command2.args.length) {
253075
253098
  command2.error("--path requires a project name argument. Usage: base44 create <name> --path <path>");
@@ -253186,7 +253209,7 @@ function getCreateCommand() {
253186
253209
  Examples:
253187
253210
  $ base44 create my-app Creates a base44 project at ./my-app
253188
253211
  $ base44 create my-todo-app --template backend-and-client Creates a base44 backend-and-client project at ./my-todo-app
253189
- $ base44 create my-app --path ./projects/my-app --deploy Creates a base44 project at ./project/my-app and deploys it`).hook("preAction", validateNonInteractiveFlags).action(createAction);
253212
+ $ base44 create my-app --path ./projects/my-app --deploy Creates a base44 project at ./project/my-app and deploys it`).hook("preAction", validateCreateOptions).action(createAction);
253190
253213
  }
253191
253214
 
253192
253215
  // src/cli/commands/project/deploy.ts
@@ -253282,11 +253305,32 @@ function printStripeResult(r, log) {
253282
253305
  log.info(` Connectors dashboard: ${theme.colors.links(getConnectorsUrl())}`);
253283
253306
  }
253284
253307
 
253308
+ // src/cli/commands/project/app-id-options.ts
253309
+ function readExplicitAppId(command2) {
253310
+ const { appId } = command2.optsWithGlobals();
253311
+ const { projectId } = command2.opts();
253312
+ const explicitAppId = command2.getOptionValueSourceWithGlobals("appId") === "cli" ? appId : undefined;
253313
+ const legacyProjectId = command2.getOptionValueSource("projectId") === "cli" ? projectId : undefined;
253314
+ return {
253315
+ appId: explicitAppId,
253316
+ legacyProjectId,
253317
+ value: explicitAppId ?? legacyProjectId
253318
+ };
253319
+ }
253320
+
253285
253321
  // src/cli/commands/project/link.ts
253286
- function validateNonInteractiveFlags2(command2) {
253287
- const { create: create4, name: name2, projectId } = command2.opts();
253288
- if (create4 && projectId) {
253289
- command2.error("--create and --projectId cannot be used together");
253322
+ function validateNonInteractiveFlags(command2) {
253323
+ const { create: create4, name: name2 } = command2.opts();
253324
+ const {
253325
+ appId,
253326
+ legacyProjectId,
253327
+ value: selectedAppId
253328
+ } = readExplicitAppId(command2);
253329
+ if (appId && legacyProjectId) {
253330
+ command2.error("--app-id and --project-id cannot be used together");
253331
+ }
253332
+ if (create4 && selectedAppId) {
253333
+ command2.error("--create and --app-id cannot be used together");
253290
253334
  }
253291
253335
  if (create4 && !name2) {
253292
253336
  command2.error("--name is required when using --create");
@@ -253355,11 +253399,12 @@ async function promptForExistingProject(linkableProjects) {
253355
253399
  }
253356
253400
  return selectedProject;
253357
253401
  }
253358
- async function link(ctx, options) {
253402
+ async function link(ctx, options, command2) {
253359
253403
  const { log, runTask: runTask2, isNonInteractive } = ctx;
253360
- const skipPrompts = !!options.create || !!options.projectId;
253404
+ const appId = readExplicitAppId(command2).value;
253405
+ const skipPrompts = !!options.create || !!appId;
253361
253406
  if (!skipPrompts && isNonInteractive) {
253362
- throw new InvalidInputError("--create with --name, or --projectId, is required in non-interactive mode");
253407
+ throw new InvalidInputError("--create with --name, or --app-id, is required in non-interactive mode");
253363
253408
  }
253364
253409
  const projectRoot = findProjectRoot();
253365
253410
  if (!projectRoot) {
@@ -253374,8 +253419,8 @@ async function link(ctx, options) {
253374
253419
  ]
253375
253420
  });
253376
253421
  }
253377
- let finalProjectId;
253378
- const action = options.projectId ? "choose" : options.create ? "create" : await promptForLinkAction();
253422
+ let finalAppId;
253423
+ const action = appId ? "choose" : options.create ? "create" : await promptForLinkAction();
253379
253424
  if (action === "choose") {
253380
253425
  const projects = await runTask2("Fetching projects...", async () => listProjects(), {
253381
253426
  successMessage: "Projects fetched",
@@ -253385,32 +253430,32 @@ async function link(ctx, options) {
253385
253430
  if (!linkableProjects.length) {
253386
253431
  return { outroMessage: "No projects available for linking" };
253387
253432
  }
253388
- let projectId;
253389
- if (options.projectId) {
253390
- const project2 = linkableProjects.find((p) => p.id === options.projectId);
253433
+ let linkedAppId;
253434
+ if (appId) {
253435
+ const project2 = linkableProjects.find((p) => p.id === appId);
253391
253436
  if (!project2) {
253392
- throw new InvalidInputError(`Project with ID "${options.projectId}" not found or not available for linking.`, {
253437
+ throw new InvalidInputError(`App with ID "${appId}" not found or not available for linking.`, {
253393
253438
  hints: [
253394
- { message: "Check the project ID is correct" },
253439
+ { message: "Check the app ID is correct" },
253395
253440
  {
253396
- message: "Use 'base44 link' without --projectId to see available projects"
253441
+ message: "Use 'base44 link' without --app-id to see available projects"
253397
253442
  }
253398
253443
  ]
253399
253444
  });
253400
253445
  }
253401
- projectId = options.projectId;
253446
+ linkedAppId = appId;
253402
253447
  } else {
253403
253448
  const selectedProject = await promptForExistingProject(linkableProjects);
253404
- projectId = selectedProject.id;
253449
+ linkedAppId = selectedProject.id;
253405
253450
  }
253406
253451
  await runTask2("Linking project...", async () => {
253407
- await writeAppConfig(projectRoot.root, projectId);
253408
- setAppContext({ id: projectId, projectRoot: projectRoot.root });
253452
+ await writeAppConfig(projectRoot.root, linkedAppId);
253453
+ setAppContext({ id: linkedAppId, projectRoot: projectRoot.root });
253409
253454
  }, {
253410
253455
  successMessage: "Project linked successfully",
253411
253456
  errorMessage: "Failed to link project"
253412
253457
  });
253413
- finalProjectId = projectId;
253458
+ finalAppId = linkedAppId;
253414
253459
  }
253415
253460
  if (action === "create") {
253416
253461
  const { name: name2, description } = options.create ? { name: options.name.trim(), description: options.description?.trim() } : await promptForNewProjectDetails();
@@ -253422,13 +253467,15 @@ async function link(ctx, options) {
253422
253467
  });
253423
253468
  await writeAppConfig(projectRoot.root, projectId);
253424
253469
  setAppContext({ id: projectId, projectRoot: projectRoot.root });
253425
- finalProjectId = projectId;
253470
+ finalAppId = projectId;
253426
253471
  }
253427
- log.message(`${theme.styles.header("Dashboard")}: ${theme.colors.links(getDashboardUrl(finalProjectId))}`);
253472
+ log.message(`${theme.styles.header("Dashboard")}: ${theme.colors.links(getDashboardUrl(finalAppId))}`);
253428
253473
  return { outroMessage: "Project linked" };
253429
253474
  }
253430
253475
  function getLinkCommand() {
253431
- return new Base44Command("link", { requireAppContext: false }).description("Link a local project to a Base44 project (create new or link existing)").option("-c, --create", "Create a new project (skip selection prompt)").option("-n, --name <name>", "Project name (required when --create is used)").option("-d, --description <description>", "Project description").option("-p, --projectId <id>", "Project ID to link to an existing project (skips selection prompt)").hook("preAction", validateNonInteractiveFlags2).action(link);
253476
+ return new Base44Command("link", {
253477
+ requireAppContext: false
253478
+ }).description("Link a local project to a Base44 project (create new or link existing)").configureHelp({ showGlobalOptions: true }).option("-c, --create", "Create a new project (skip selection prompt)").option("-n, --name <name>", "Project name (required when --create is used)").option("-d, --description <description>", "Project description").addOption(new Option("-p, --project-id <id>", "Project ID to link to an existing project").hideHelp()).addOption(new Option("--projectId <id>", "Project ID to link to an existing project").hideHelp()).hook("preAction", validateNonInteractiveFlags).action(link);
253432
253479
  }
253433
253480
 
253434
253481
  // src/cli/commands/project/logs.ts
@@ -253518,8 +253565,12 @@ async function fetchLogsForFunctions(functionNames, options, availableFunctionNa
253518
253565
  }
253519
253566
  return allEntries;
253520
253567
  }
253521
- async function getAllFunctionNames() {
253522
- const { functions } = await readProjectConfig();
253568
+ async function getProjectFunctionNames(projectRoot) {
253569
+ const { functions } = await readProjectConfig(projectRoot);
253570
+ return functions.map((fn) => fn.name);
253571
+ }
253572
+ async function getRemoteFunctionNames() {
253573
+ const { functions } = await listDeployedFunctions();
253523
253574
  return functions.map((fn) => fn.name);
253524
253575
  }
253525
253576
  function validateLimit(limit) {
@@ -253530,15 +253581,18 @@ function validateLimit(limit) {
253530
253581
  throw new InvalidInputError(`Invalid limit: "${limit}". Must be a number between 1 and 1000.`);
253531
253582
  }
253532
253583
  }
253533
- async function logsAction(_ctx, options) {
253584
+ async function logsAction(ctx, options) {
253534
253585
  validateLimit(options.limit);
253535
253586
  const specifiedFunctions = parseFunctionNames(options.function);
253536
- const allProjectFunctions = await getAllFunctionNames();
253537
- const functionNames = specifiedFunctions.length > 0 ? specifiedFunctions : allProjectFunctions;
253587
+ const localProjectRoot = ctx.app?.projectRoot;
253588
+ const availableFunctionNames = localProjectRoot ? await getProjectFunctionNames(localProjectRoot) : await getRemoteFunctionNames();
253589
+ const functionNames = specifiedFunctions.length > 0 ? specifiedFunctions : availableFunctionNames;
253538
253590
  if (functionNames.length === 0) {
253539
- return { outroMessage: "No functions found in this project." };
253591
+ return {
253592
+ outroMessage: localProjectRoot ? "No functions found in this project." : "No functions found in this app."
253593
+ };
253540
253594
  }
253541
- let entries = await fetchLogsForFunctions(functionNames, options, allProjectFunctions);
253595
+ let entries = await fetchLogsForFunctions(functionNames, options, availableFunctionNames);
253542
253596
  const limit = options.limit ? Number.parseInt(options.limit, 10) : undefined;
253543
253597
  if (limit !== undefined && entries.length > limit) {
253544
253598
  entries = entries.slice(0, limit);
@@ -253554,17 +253608,20 @@ function getLogsCommand() {
253554
253608
  // src/cli/commands/project/scaffold.ts
253555
253609
  import { basename as basename5, resolve as resolve6 } from "node:path";
253556
253610
  function resolveAppId(options) {
253557
- const appId = options.appId ?? process.env.BASE44_APP_ID;
253611
+ const appId = options.appId;
253558
253612
  if (!appId) {
253559
253613
  throw new InvalidInputError("No app ID found. `base44 scaffold` sets up a local project for an existing Base44 app.", {
253560
- hints: [{ message: "Pass it explicitly with --app-id <id>" }]
253614
+ hints: [
253615
+ { message: "Pass it explicitly with --app-id <id>" },
253616
+ { message: `Set ${BASE44_APP_ID_ENV_VAR} in your environment` }
253617
+ ]
253561
253618
  });
253562
253619
  }
253563
253620
  return appId;
253564
253621
  }
253565
- async function scaffoldAction(ctx, name2, options) {
253622
+ async function scaffoldAction(ctx, name2, options, command2) {
253566
253623
  const { log, runTask: runTask2 } = ctx;
253567
- const appId = resolveAppId(options);
253624
+ const appId = resolveAppId(command2.optsWithGlobals());
253568
253625
  const resolvedPath = resolve6("./");
253569
253626
  const projectName = (name2 ?? basename5(resolvedPath)).trim();
253570
253627
  const template2 = await getTemplateById("backend-only");
@@ -253596,7 +253653,7 @@ function getScaffoldCommand() {
253596
253653
  return new Base44Command("scaffold", {
253597
253654
  requireAppContext: false,
253598
253655
  fullBanner: true
253599
- }).description("Scaffold a local project for an existing Base44 app").addArgument(new Argument("name", "Project name").argOptional()).option("--app-id <id>", "Existing Base44 app ID").option("--no-skills", "Skip AI agent skills installation").addHelpText("after", `
253656
+ }).description("Scaffold a local project for an existing Base44 app").addArgument(new Argument("name", "Project name").argOptional()).option("--no-skills", "Skip AI agent skills installation").addHelpText("after", `
253600
253657
  Examples:
253601
253658
  $ base44 scaffold --app-id app_123 Scaffolds the current dir for the given app
253602
253659
  $ base44 scaffold my-app --app-id app_123 Scaffolds the current dir, named "my-app"`).action(scaffoldAction);
@@ -253896,9 +253953,6 @@ function getTypesCommand() {
253896
253953
  return new Command("types").description("Manage TypeScript type generation").addCommand(getTypesGenerateCommand());
253897
253954
  }
253898
253955
 
253899
- // src/cli/commands/dev.ts
253900
- import { join as join28 } from "node:path";
253901
-
253902
253956
  // src/cli/dev/dev-server/main.ts
253903
253957
  var import_cors = __toESM(require_lib4(), 1);
253904
253958
  var import_express6 = __toESM(require_express(), 1);
@@ -255436,6 +255490,7 @@ class ServeRunner {
255436
255490
  logger;
255437
255491
  child;
255438
255492
  stopping = false;
255493
+ stopPromise;
255439
255494
  exitListeners = [];
255440
255495
  constructor(options8) {
255441
255496
  this.command = options8.command;
@@ -255447,12 +255502,14 @@ class ServeRunner {
255447
255502
  if (this.child) {
255448
255503
  return;
255449
255504
  }
255505
+ const stdin2 = process21.platform === "win32" ? "ignore" : "inherit";
255450
255506
  const child = spawn3(this.command, {
255451
255507
  cwd: this.cwd,
255452
255508
  shell: true,
255453
255509
  detached: process21.platform !== "win32",
255454
255510
  env: { ...process21.env, ...this.env },
255455
- stdio: ["inherit", "pipe", "pipe"]
255511
+ stdio: [stdin2, "pipe", "pipe"],
255512
+ windowsHide: true
255456
255513
  });
255457
255514
  this.child = child;
255458
255515
  this.setupHandlers(child);
@@ -255461,6 +255518,13 @@ class ServeRunner {
255461
255518
  this.exitListeners.push(listener);
255462
255519
  }
255463
255520
  async stop() {
255521
+ if (this.stopPromise) {
255522
+ return this.stopPromise;
255523
+ }
255524
+ this.stopPromise = this.stopChild();
255525
+ return this.stopPromise;
255526
+ }
255527
+ async stopChild() {
255464
255528
  const child = this.child;
255465
255529
  if (!child || child.exitCode !== null) {
255466
255530
  return;
@@ -255468,7 +255532,14 @@ class ServeRunner {
255468
255532
  this.stopping = true;
255469
255533
  const exited = new Promise((resolve10) => child.once("exit", () => resolve10()));
255470
255534
  if (process21.platform === "win32" && child.pid) {
255471
- spawn3("taskkill", ["/pid", String(child.pid), "/T", "/F"]);
255535
+ const taskkill = spawn3("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
255536
+ stdio: "ignore",
255537
+ windowsHide: true
255538
+ });
255539
+ await new Promise((resolve10) => {
255540
+ taskkill.once("exit", () => resolve10());
255541
+ taskkill.once("error", () => resolve10());
255542
+ });
255472
255543
  } else if (child.pid) {
255473
255544
  try {
255474
255545
  process21.kill(-child.pid, "SIGTERM");
@@ -257216,7 +257287,7 @@ async function createDevServer(options8) {
257216
257287
  port: process.env.IS_TEST === "true" ? undefined : DEFAULT_PORT
257217
257288
  });
257218
257289
  const baseUrl = `http://localhost:${port}`;
257219
- const { functions, entities, project: project2 } = await options8.loadResources();
257290
+ const { functions, entities, project: project2, siteUrl } = await options8.loadResources();
257220
257291
  const app = import_express6.default();
257221
257292
  const remoteProxy = import_http_proxy_middleware2.createProxyMiddleware({
257222
257293
  target: BASE44_APP_URL,
@@ -257239,12 +257310,12 @@ async function createDevServer(options8) {
257239
257310
  const functionRoutes = createFunctionRouter(functionManager, devLogger);
257240
257311
  app.use("/api/apps/:appId/functions", functionRoutes);
257241
257312
  if (functionManager.getFunctionNames().length > 0) {
257242
- options8.log.info(`Loaded functions: ${functionManager.getFunctionNames().join(", ")}`);
257313
+ devLogger.log(`Loaded functions: ${functionManager.getFunctionNames().join(", ")}`);
257243
257314
  }
257244
257315
  const db2 = new Database;
257245
257316
  await db2.load(entities);
257246
257317
  if (db2.getCollectionNames().length > 0) {
257247
- options8.log.info(`Loaded entities: ${db2.getCollectionNames().join(", ")}`);
257318
+ devLogger.log(`Loaded entities: ${db2.getCollectionNames().join(", ")}`);
257248
257319
  }
257249
257320
  let emitEntityEvent = () => {};
257250
257321
  const entityRoutes = await createEntityRoutes(db2, devLogger, (...args) => emitEntityEvent(...args));
@@ -257276,6 +257347,12 @@ async function createDevServer(options8) {
257276
257347
  const customIntegrationRoutes = createCustomIntegrationRoutes(remoteProxy, devLogger);
257277
257348
  app.use("/api/apps/:appId/integrations/custom", customIntegrationRoutes);
257278
257349
  app.use((req, res, next) => {
257350
+ if (siteUrl && (req.path === "/login" || req.path.startsWith("/login/"))) {
257351
+ const targetUrl = new URL(req.originalUrl, siteUrl);
257352
+ devLogger.warn(`"${req.originalUrl}" requires hosted login, redirecting to ${targetUrl.toString()}`);
257353
+ res.redirect(targetUrl.toString());
257354
+ return;
257355
+ }
257279
257356
  if (!req.originalUrl.endsWith("analytics/track/batch")) {
257280
257357
  devLogger.warn(`"${req.originalUrl}" is not supported in local development, passing call to production`);
257281
257358
  }
@@ -257332,70 +257409,97 @@ async function createDevServer(options8) {
257332
257409
  }
257333
257410
  });
257334
257411
  await base44ConfigWatcher.start();
257412
+ const serveCommand = project2.site?.serveCommand;
257335
257413
  let serveRunner;
257336
- if (options8.serve && project2.site?.serveCommand) {
257414
+ if (options8.appId && serveCommand) {
257337
257415
  serveRunner = new ServeRunner({
257338
- command: project2.site.serveCommand,
257416
+ command: serveCommand,
257339
257417
  cwd: project2.root,
257340
257418
  env: {
257341
- VITE_BASE44_APP_ID: options8.serve.appId,
257419
+ VITE_BASE44_APP_ID: options8.appId,
257342
257420
  VITE_BASE44_APP_BASE_URL: baseUrl
257343
257421
  },
257344
257422
  logger: createDevLogger("frontend", theme.colors.base44Orange)
257345
257423
  });
257346
257424
  }
257347
- const shutdown = async () => {
257425
+ const handleShutdownError = (error48) => {
257426
+ const errorMessage = error48 instanceof Error ? error48.message : String(error48);
257427
+ devLogger.error(`Failed to shut down dev server: ${errorMessage}`);
257428
+ };
257429
+ const closeServerIfRunning = async () => {
257430
+ if (!server.listening) {
257431
+ return;
257432
+ }
257433
+ await new Promise((resolve12, reject) => {
257434
+ server.close((error48) => {
257435
+ if (error48) {
257436
+ reject(error48);
257437
+ return;
257438
+ }
257439
+ resolve12();
257440
+ });
257441
+ });
257442
+ };
257443
+ const runShutdown = async () => {
257348
257444
  base44ConfigWatcher.close();
257349
- io6.close();
257445
+ await io6.close();
257350
257446
  await functionManager.stopAll();
257351
257447
  await serveRunner?.stop();
257352
- server.close();
257448
+ await closeServerIfRunning();
257449
+ };
257450
+ let shutdownPromise;
257451
+ const shutdown = () => {
257452
+ shutdownPromise ??= runShutdown().catch(handleShutdownError);
257453
+ return shutdownPromise;
257353
257454
  };
257354
257455
  process.on("SIGINT", shutdown);
257355
257456
  process.on("SIGTERM", shutdown);
257356
257457
  serveRunner?.onExit(() => {
257357
257458
  shutdown().finally(() => process.exit(1));
257358
257459
  });
257359
- serveRunner?.start();
257360
- return { port, server };
257460
+ if (serveRunner) {
257461
+ devLogger.log(`Backend running on ${baseUrl}`);
257462
+ serveRunner.start();
257463
+ }
257464
+ return { port, server, isServingFrontend: serveRunner !== undefined };
257361
257465
  }
257362
257466
 
257363
257467
  // src/cli/commands/dev.ts
257364
257468
  function localServerUrl(port) {
257365
257469
  return `http://localhost:${port}`;
257366
257470
  }
257367
- async function writeEnvLocal(projectRoot, port, appId, log) {
257368
- const envLocalPath = join28(projectRoot, ".env.local");
257369
- await writeFile(envLocalPath, `VITE_BASE44_APP_ID=${appId}
257370
- VITE_BASE44_APP_BASE_URL=${localServerUrl(port)}
257371
- `);
257372
- log.info("Wrote .env.local with app ID and dev server URL");
257471
+ function validateDevOptions(command2) {
257472
+ const { appId } = command2.optsWithGlobals();
257473
+ if (appId !== undefined) {
257474
+ command2.error(`base44 dev cannot be used with --app-id or ${BASE44_APP_ID_ENV_VAR}.`);
257475
+ }
257373
257476
  }
257374
- async function devAction({ log }, options8) {
257477
+ async function devAction(ctx, options8) {
257478
+ const { log, app } = ctx;
257479
+ if (!app?.projectRoot) {
257480
+ throw new ConfigInvalidError("base44 dev requires a linked local project. Run it from a project with base44/.app.jsonc.");
257481
+ }
257375
257482
  const port = options8.port ? Number(options8.port) : undefined;
257376
- const serveEnabled = options8.serve !== false;
257377
- let projectRoot;
257378
- const appId = serveEnabled || options8.writeEnv ? (await initAppContext()).id : undefined;
257379
- const { port: resolvedPort } = await createDevServer({
257483
+ const appId = app.id;
257484
+ const siteUrlPromise = getSiteUrl().catch(() => {
257485
+ return;
257486
+ });
257487
+ const { port: resolvedPort, isServingFrontend } = await createDevServer({
257380
257488
  log,
257381
257489
  port,
257490
+ appId,
257382
257491
  denoWrapperPath: getDenoWrapperPath(),
257383
- serve: serveEnabled && appId ? { appId } : undefined,
257384
257492
  loadResources: async () => {
257385
257493
  const { functions, entities, project: project2 } = await readProjectConfig();
257386
- projectRoot = project2.root;
257387
- return { functions, entities, project: project2 };
257494
+ const siteUrl = await siteUrlPromise;
257495
+ return { functions, entities, project: project2, siteUrl };
257388
257496
  }
257389
257497
  });
257390
- if (options8.writeEnv && projectRoot && appId) {
257391
- await writeEnvLocal(projectRoot, resolvedPort, appId, log);
257392
- }
257393
- return {
257394
- outroMessage: `Dev server is available at ${theme.colors.links(localServerUrl(resolvedPort))}`
257395
- };
257498
+ const outroMessage = isServingFrontend ? "Open your app using the frontend dev server URL" : `Dev server is available at ${theme.colors.links(localServerUrl(resolvedPort))}`;
257499
+ return { outroMessage };
257396
257500
  }
257397
257501
  function getDevCommand() {
257398
- return new Base44Command("dev").description("Start the development server").option("-p, --port <number>", "Port for the development server").option("--no-serve", "Do not start the frontend dev server (serveCommand)").option("--write-env", "Write the app ID and dev server URL to .env.local").action(devAction);
257502
+ return new Base44Command("dev").description("Start the development server").option("-p, --port <number>", "Port for the development server").hook("preAction", validateDevOptions).action(devAction);
257399
257503
  }
257400
257504
 
257401
257505
  // src/core/exec/run-script.ts
@@ -257451,7 +257555,10 @@ function readStdin2() {
257451
257555
  process.stdin.on("error", reject);
257452
257556
  });
257453
257557
  }
257454
- async function execAction(isNonInteractive) {
257558
+ async function execAction({
257559
+ app,
257560
+ isNonInteractive
257561
+ }) {
257455
257562
  const noInputError = new InvalidInputError("No input provided. Pipe a script to stdin.", {
257456
257563
  hints: [
257457
257564
  { message: "File: cat ./script.ts | base44 exec" },
@@ -257467,7 +257574,7 @@ async function execAction(isNonInteractive) {
257467
257574
  if (!code2.trim()) {
257468
257575
  throw noInputError;
257469
257576
  }
257470
- const { exitCode } = await runScript({ appId: getAppContext().id, code: code2 });
257577
+ const { exitCode } = await runScript({ appId: app.id, code: code2 });
257471
257578
  if (exitCode !== 0) {
257472
257579
  process.exitCode = exitCode;
257473
257580
  }
@@ -257480,18 +257587,26 @@ Examples:
257480
257587
  $ cat ./script.ts | base44 exec
257481
257588
 
257482
257589
  Inline script:
257483
- $ echo "const users = await base44.entities.User.list()" | base44 exec`).action(async ({ isNonInteractive }) => {
257484
- return await execAction(isNonInteractive);
257590
+ $ echo "const users = await base44.entities.User.list()" | base44 exec`).action(async (ctx) => {
257591
+ return await execAction(ctx);
257485
257592
  });
257486
257593
  }
257487
257594
 
257488
257595
  // src/cli/commands/project/eject.ts
257489
257596
  import { resolve as resolve12 } from "node:path";
257490
257597
  var import_kebabCase2 = __toESM(require_kebabCase(), 1);
257491
- async function eject(ctx, options8) {
257598
+ async function eject(ctx, options8, command2) {
257492
257599
  const { log, runTask: runTask2, isNonInteractive } = ctx;
257493
- if (isNonInteractive && !options8.projectId) {
257494
- throw new InvalidInputError("--project-id is required in non-interactive mode");
257600
+ const {
257601
+ appId,
257602
+ legacyProjectId,
257603
+ value: selectedAppId
257604
+ } = readExplicitAppId(command2);
257605
+ if (appId && legacyProjectId) {
257606
+ throw new InvalidInputError("--app-id and --project-id cannot be used together");
257607
+ }
257608
+ if (isNonInteractive && !selectedAppId) {
257609
+ throw new InvalidInputError("--app-id is required in non-interactive mode");
257495
257610
  }
257496
257611
  if (isNonInteractive && !options8.path) {
257497
257612
  throw new InvalidInputError("--path is required in non-interactive mode");
@@ -257499,13 +257614,13 @@ async function eject(ctx, options8) {
257499
257614
  const projects = await listProjects();
257500
257615
  const ejectableProjects = projects.filter((p4) => p4.isManagedSourceCode !== false);
257501
257616
  let selectedProject;
257502
- if (options8.projectId) {
257503
- const foundProject = ejectableProjects.find((p4) => p4.id === options8.projectId);
257617
+ if (selectedAppId) {
257618
+ const foundProject = ejectableProjects.find((p4) => p4.id === selectedAppId);
257504
257619
  if (!foundProject) {
257505
- throw new InvalidInputError(`Project with ID "${options8.projectId}" not found or not ejectable`, {
257620
+ throw new InvalidInputError(`App with ID "${selectedAppId}" not found or not ejectable`, {
257506
257621
  hints: [
257507
257622
  {
257508
- message: "Run 'base44 eject' without --project-id to see available projects"
257623
+ message: "Run 'base44 eject' without --app-id to see available projects"
257509
257624
  }
257510
257625
  ]
257511
257626
  });
@@ -257581,13 +257696,15 @@ async function eject(ctx, options8) {
257581
257696
  return { outroMessage: "Your new project is set and ready to use" };
257582
257697
  }
257583
257698
  function getEjectCommand() {
257584
- return new Base44Command("eject", { requireAppContext: false }).description("Download the code for an existing Base44 project").option("-p, --path <path>", "Path where to write the project").option("--project-id <id>", "Project ID to eject (skips interactive selection)").option("-y, --yes", "Skip confirmation prompts").action(eject);
257699
+ return new Base44Command("eject", {
257700
+ requireAppContext: false
257701
+ }).description("Download the code for an existing Base44 project").configureHelp({ showGlobalOptions: true }).option("-p, --path <path>", "Path where to write the project").option("-y, --yes", "Skip confirmation prompts").addOption(new Option("--project-id <id>", "Project ID to eject (skips interactive selection)").hideHelp()).action(eject);
257585
257702
  }
257586
257703
 
257587
257704
  // src/cli/program.ts
257588
257705
  function createProgram(context) {
257589
257706
  const program2 = new Command;
257590
- program2.name("base44").description("Base44 CLI - Unified interface for managing Base44 applications").version(package_default.version);
257707
+ program2.name("base44").description("Base44 CLI - Unified interface for managing Base44 applications").version(package_default.version).addOption(new Option("--app-id <id>", "Base44 app ID to use").env(BASE44_APP_ID_ENV_VAR));
257591
257708
  program2.configureHelp({
257592
257709
  sortSubcommands: true
257593
257710
  });
@@ -257614,7 +257731,7 @@ function createProgram(context) {
257614
257731
  program2.addCommand(getSiteCommand());
257615
257732
  program2.addCommand(getTypesCommand());
257616
257733
  program2.addCommand(getExecCommand());
257617
- program2.addCommand(getDevCommand(), { hidden: true });
257734
+ program2.addCommand(getDevCommand());
257618
257735
  program2.addCommand(getLogsCommand());
257619
257736
  return program2;
257620
257737
  }
@@ -261824,7 +261941,7 @@ function addCommandInfoToErrorReporter(program2, errorReporter) {
261824
261941
  // src/cli/index.ts
261825
261942
  var __dirname4 = dirname23(fileURLToPath6(import.meta.url));
261826
261943
  async function runCLI(options8) {
261827
- ensureNpmAssets(join29(__dirname4, "../assets"));
261944
+ ensureNpmAssets(join28(__dirname4, "../assets"));
261828
261945
  const errorReporter = new ErrorReporter;
261829
261946
  errorReporter.registerProcessErrorHandlers();
261830
261947
  const isNonInteractive = !process.stdin.isTTY || !process.stdout.isTTY;
@@ -261861,4 +261978,4 @@ export {
261861
261978
  CLIExitError
261862
261979
  };
261863
261980
 
261864
- //# debugId=A582415BD965E2EA64756E2164756E21
261981
+ //# debugId=A8360A6A73B7381F64756E2164756E21