@base44-preview/cli 0.1.5-pr.568.1f028a6 → 0.1.5-pr.570.f3b8004

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.
@@ -9,6 +9,8 @@
9
9
  * - BASE44_APP_ID: App identifier from .app.jsonc
10
10
  * - BASE44_ACCESS_TOKEN: User's access token
11
11
  * - BASE44_APP_BASE_URL: App's published URL / subdomain (used for function calls)
12
+ * - BASE44_PRIVILEGED: When "true", adds the X-Bypass-RLS header (bypass RLS)
13
+ * - BASE44_DATA_ENV: When set, adds the X-Data-Env header (target data environment)
12
14
  */
13
15
 
14
16
  export {};
@@ -17,6 +19,8 @@ const scriptPath = Deno.env.get("SCRIPT_PATH");
17
19
  const appId = Deno.env.get("BASE44_APP_ID");
18
20
  const accessToken = Deno.env.get("BASE44_ACCESS_TOKEN");
19
21
  const appBaseUrl = Deno.env.get("BASE44_APP_BASE_URL");
22
+ const isPrivileged = Deno.env.get("BASE44_PRIVILEGED") === "true";
23
+ const dataEnv = Deno.env.get("BASE44_DATA_ENV");
20
24
 
21
25
  if (!scriptPath) {
22
26
  console.error("SCRIPT_PATH environment variable is required");
@@ -35,10 +39,20 @@ if (!appBaseUrl) {
35
39
 
36
40
  import { createClient } from "npm:@base44/sdk";
37
41
 
42
+ const customHeaders: Record<string, string> = {};
43
+ if (isPrivileged) customHeaders["X-Bypass-RLS"] = "true";
44
+ // QUESTION(@netanelgilad): backend get_data_environment_from_request honors only
45
+ // "dev" and "prod" on X-Data-Env ("share" is host-derived, unknown values fall
46
+ // through to prod). It applies to entity CRUD, but for backend-function invocations
47
+ // a caller-supplied "dev" is dropped by trusted_request_data_env (prod/no-signal
48
+ // pass through). Intended scope for --data-env?
49
+ if (dataEnv) customHeaders["X-Data-Env"] = dataEnv;
50
+
38
51
  const base44 = createClient({
39
52
  appId,
40
53
  token: accessToken,
41
54
  serverUrl: appBaseUrl,
55
+ headers: customHeaders,
42
56
  });
43
57
 
44
58
  (globalThis as any).base44 = base44;
package/dist/cli/index.js CHANGED
@@ -218287,6 +218287,7 @@ var PROJECT_CONFIG_PATTERNS = [
218287
218287
  `config.${CONFIG_FILE_EXTENSION_GLOB}`
218288
218288
  ];
218289
218289
  var BASE44_APP_ID_ENV_VAR = "BASE44_APP_ID";
218290
+ var DEFAULT_DEV_SERVER_PORT = 4400;
218290
218291
  var TYPES_OUTPUT_SUBDIR = ".types";
218291
218292
  var TYPES_FILENAME = "types.d.ts";
218292
218293
  var AUTH_CLIENT_ID = "base44_cli";
@@ -236069,13 +236070,11 @@ var CreateProjectResponseSchema = exports_external.looseObject({
236069
236070
  var AppDetailSchema = exports_external.looseObject({
236070
236071
  id: exports_external.string(),
236071
236072
  name: exports_external.string().optional(),
236072
- organization_id: exports_external.string().nullish(),
236073
- is_managed_source_code: exports_external.boolean().optional()
236073
+ organization_id: exports_external.string().nullish()
236074
236074
  }).transform((data) => ({
236075
236075
  id: data.id,
236076
236076
  name: data.name,
236077
- organizationId: data.organization_id ?? undefined,
236078
- isManagedSourceCode: data.is_managed_source_code
236077
+ organizationId: data.organization_id ?? undefined
236079
236078
  }));
236080
236079
  var ProjectSchema = exports_external.object({
236081
236080
  id: exports_external.string(),
@@ -241896,15 +241895,14 @@ async function setAppVisibility(visibility) {
241896
241895
  throw await ApiError.fromHttpError(error48, "updating app visibility");
241897
241896
  }
241898
241897
  }
241899
- async function listProjects(options = {}) {
241898
+ async function listProjects() {
241900
241899
  let response;
241901
241900
  try {
241902
241901
  response = await base44Client.get("api/apps", {
241903
241902
  searchParams: {
241904
241903
  sort: "-updated_date",
241905
241904
  fields: "id,name,user_description,is_managed_source_code",
241906
- limit: 50,
241907
- ...options.workspaceId ? { workspace_id: options.workspaceId } : {}
241905
+ limit: 50
241908
241906
  }
241909
241907
  });
241910
241908
  } catch (error48) {
@@ -241920,9 +241918,7 @@ async function getApp(appId) {
241920
241918
  let response;
241921
241919
  try {
241922
241920
  response = await base44Client.get(`api/apps/${appId}`, {
241923
- searchParams: {
241924
- fields: "id,name,organization_id,is_managed_source_code"
241925
- }
241921
+ searchParams: { fields: "id,name,organization_id" }
241926
241922
  });
241927
241923
  } catch (error48) {
241928
241924
  throw await ApiError.fromHttpError(error48, "fetching app");
@@ -253471,7 +253467,7 @@ function workspaceLabel(workspace2) {
253471
253467
  const suffix = workspace2.isPersonal ? "personal" : workspace2.userRole ?? "member";
253472
253468
  return `${workspace2.name} (${suffix})`;
253473
253469
  }
253474
- async function resolveWorkspaceId(ctx, flagWorkspaceId, isInteractive, options = {}) {
253470
+ async function resolveWorkspaceId(ctx, flagWorkspaceId, isInteractive) {
253475
253471
  if (flagWorkspaceId) {
253476
253472
  return flagWorkspaceId;
253477
253473
  }
@@ -253482,13 +253478,13 @@ async function resolveWorkspaceId(ctx, flagWorkspaceId, isInteractive, options =
253482
253478
  if (workspaces.length <= 1) {
253483
253479
  return;
253484
253480
  }
253485
- const promptOptions = workspaces.map((w) => ({
253481
+ const options = workspaces.map((w) => ({
253486
253482
  value: w.id,
253487
253483
  label: workspaceLabel(w)
253488
253484
  }));
253489
253485
  const selected = await Je({
253490
- message: options.promptMessage ?? "Which workspace should this app belong to?",
253491
- options: promptOptions,
253486
+ message: "Which workspace should this app belong to?",
253487
+ options,
253492
253488
  initialValue: workspaces[0].id
253493
253489
  });
253494
253490
  if (Ct(selected)) {
@@ -253821,40 +253817,6 @@ async function promptForExistingProject(linkableProjects) {
253821
253817
  }
253822
253818
  return selectedProject;
253823
253819
  }
253824
- async function resolveExplicitAppId(ctx, appId) {
253825
- const app = await ctx.runTask("Validating app...", () => getApp(appId), {
253826
- errorMessage: "Could not validate app"
253827
- }).catch((error48) => {
253828
- if (error48 instanceof ApiError && (error48.statusCode === 404 || error48.statusCode === 403)) {
253829
- throw new InvalidInputError(`App "${appId}" not found, or you don't have access to it.`, {
253830
- hints: [
253831
- { message: "Check the app ID is correct" },
253832
- {
253833
- message: "Run 'base44 link' without --app-id to browse apps by workspace"
253834
- }
253835
- ]
253836
- });
253837
- }
253838
- throw error48;
253839
- });
253840
- if (app.isManagedSourceCode) {
253841
- throw new InvalidInputError(`App "${appId}" is a managed-source app and can't be linked with the CLI.`);
253842
- }
253843
- return appId;
253844
- }
253845
- async function chooseProjectInteractively(ctx, options) {
253846
- const workspaceId = await resolveWorkspaceId(ctx, options.workspace ?? options.org, !ctx.isNonInteractive, { promptMessage: "Which workspace is the app in?" });
253847
- const projects = await ctx.runTask("Fetching projects...", () => listProjects({ workspaceId }), {
253848
- successMessage: "Projects fetched",
253849
- errorMessage: "Failed to fetch projects"
253850
- });
253851
- const linkableProjects = projects.filter((p) => p.isManagedSourceCode !== true);
253852
- if (!linkableProjects.length) {
253853
- return;
253854
- }
253855
- const selectedProject = await promptForExistingProject(linkableProjects);
253856
- return selectedProject.id;
253857
- }
253858
253820
  async function link(ctx, options, command2) {
253859
253821
  const { log, runTask: runTask2, isNonInteractive } = ctx;
253860
253822
  const appId = readExplicitAppId(command2).value;
@@ -253878,10 +253840,32 @@ async function link(ctx, options, command2) {
253878
253840
  let finalAppId;
253879
253841
  const action = appId ? "choose" : options.create ? "create" : await promptForLinkAction();
253880
253842
  if (action === "choose") {
253881
- const linkedAppId = appId ? await resolveExplicitAppId(ctx, appId) : await chooseProjectInteractively(ctx, options);
253882
- if (!linkedAppId) {
253843
+ const projects = await runTask2("Fetching projects...", async () => listProjects(), {
253844
+ successMessage: "Projects fetched",
253845
+ errorMessage: "Failed to fetch projects"
253846
+ });
253847
+ const linkableProjects = projects.filter((p) => p.isManagedSourceCode !== true);
253848
+ if (!linkableProjects.length) {
253883
253849
  return { outroMessage: "No projects available for linking" };
253884
253850
  }
253851
+ let linkedAppId;
253852
+ if (appId) {
253853
+ const project2 = linkableProjects.find((p) => p.id === appId);
253854
+ if (!project2) {
253855
+ throw new InvalidInputError(`App with ID "${appId}" not found or not available for linking.`, {
253856
+ hints: [
253857
+ { message: "Check the app ID is correct" },
253858
+ {
253859
+ message: "Use 'base44 link' without --app-id to see available projects"
253860
+ }
253861
+ ]
253862
+ });
253863
+ }
253864
+ linkedAppId = appId;
253865
+ } else {
253866
+ const selectedProject = await promptForExistingProject(linkableProjects);
253867
+ linkedAppId = selectedProject.id;
253868
+ }
253885
253869
  await runTask2("Linking project...", async () => {
253886
253870
  await writeAppConfig(projectRoot.root, linkedAppId);
253887
253871
  setAppContext({ id: linkedAppId, projectRoot: projectRoot.root });
@@ -253910,7 +253894,7 @@ async function link(ctx, options, command2) {
253910
253894
  function getLinkCommand() {
253911
253895
  return new Base44Command("link", {
253912
253896
  requireAppContext: false
253913
- }).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").option("-w, --workspace <id>", "Workspace (organization) ID to scope the app picker to (or create the app in, with --create). Defaults to your personal workspace").addOption(new Option("--org <id>", "Alias for --workspace").hideHelp()).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);
253897
+ }).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").option("-w, --workspace <id>", "Workspace (organization) ID to create the app in when using --create (defaults to your personal workspace)").addOption(new Option("--org <id>", "Alias for --workspace").hideHelp()).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);
253914
253898
  }
253915
253899
 
253916
253900
  // src/cli/commands/project/logs.ts
@@ -258474,17 +258458,14 @@ function getDevCommand() {
258474
258458
  import { spawn as spawn4 } from "node:child_process";
258475
258459
  import { copyFileSync, writeFileSync as writeFileSync2 } from "node:fs";
258476
258460
  async function runScript(options8) {
258477
- const { appId, code: code2 } = options8;
258461
+ const { appId, code: code2, local, privileged, dataEnv } = options8;
258478
258462
  verifyDenoInstalled("to run scripts with exec");
258479
258463
  const cleanupFns = [];
258480
258464
  const tempScript = await $file({ postfix: ".ts" });
258481
258465
  cleanupFns.push(tempScript.cleanup);
258482
258466
  writeFileSync2(tempScript.path, code2, "utf-8");
258483
258467
  const scriptPath = `file://${tempScript.path}`;
258484
- const [appUserToken, appBaseUrl] = await Promise.all([
258485
- getAppUserToken(),
258486
- getSiteUrl()
258487
- ]);
258468
+ const [appUserToken, appBaseUrl] = local ? [local.token, local.serverUrl] : await Promise.all([getAppUserToken(), getSiteUrl()]);
258488
258469
  const tempWrapper = await $file({ postfix: ".ts" });
258489
258470
  cleanupFns.push(tempWrapper.cleanup);
258490
258471
  copyFileSync(getExecWrapperPath(), tempWrapper.path);
@@ -258496,7 +258477,9 @@ async function runScript(options8) {
258496
258477
  SCRIPT_PATH: scriptPath,
258497
258478
  BASE44_APP_ID: appId,
258498
258479
  BASE44_ACCESS_TOKEN: appUserToken,
258499
- BASE44_APP_BASE_URL: appBaseUrl
258480
+ BASE44_APP_BASE_URL: appBaseUrl,
258481
+ ...privileged ? { BASE44_PRIVILEGED: "true" } : {},
258482
+ ...dataEnv ? { BASE44_DATA_ENV: dataEnv } : {}
258500
258483
  },
258501
258484
  stdio: "inherit"
258502
258485
  });
@@ -258523,10 +258506,28 @@ function readStdin2() {
258523
258506
  process.stdin.on("error", reject);
258524
258507
  });
258525
258508
  }
258526
- async function execAction({
258527
- app,
258528
- isNonInteractive
258529
- }) {
258509
+ function parsePort(value) {
258510
+ const port = Number(value);
258511
+ if (!Number.isInteger(port) || port <= 0 || port > 65535) {
258512
+ throw new InvalidInputError(`Invalid --port value: "${value}".`, {
258513
+ hints: [{ message: "Pass a port number, e.g. --port 4400" }]
258514
+ });
258515
+ }
258516
+ return port;
258517
+ }
258518
+ async function resolveLocalTarget(port) {
258519
+ const { email: email3 } = await readAuth();
258520
+ return {
258521
+ serverUrl: `http://localhost:${port}`,
258522
+ token: createJwtToken(email3)
258523
+ };
258524
+ }
258525
+ async function execAction({ app, isNonInteractive }, options8) {
258526
+ if (options8.port !== undefined && !options8.local) {
258527
+ throw new InvalidInputError("--port can only be used with --local.", {
258528
+ hints: [{ message: "Usage: base44 exec --local --port <port>" }]
258529
+ });
258530
+ }
258530
258531
  const noInputError = new InvalidInputError("No input provided. Pipe a script to stdin.", {
258531
258532
  hints: [
258532
258533
  { message: "File: cat ./script.ts | base44 exec" },
@@ -258542,22 +258543,33 @@ async function execAction({
258542
258543
  if (!code2.trim()) {
258543
258544
  throw noInputError;
258544
258545
  }
258545
- const { exitCode } = await runScript({ appId: app.id, code: code2 });
258546
+ const local = options8.local ? await resolveLocalTarget(options8.port !== undefined ? parsePort(options8.port) : DEFAULT_DEV_SERVER_PORT) : undefined;
258547
+ const { exitCode } = await runScript({
258548
+ appId: app.id,
258549
+ code: code2,
258550
+ local,
258551
+ privileged: options8.privileged,
258552
+ dataEnv: options8.dataEnv
258553
+ });
258546
258554
  if (exitCode !== 0) {
258547
258555
  process.exitCode = exitCode;
258548
258556
  }
258549
258557
  return {};
258550
258558
  }
258551
258559
  function getExecCommand() {
258552
- return new Base44Command("exec").description("Run a script with the Base44 SDK pre-authenticated as the current user").addHelpText("after", `
258560
+ return new Base44Command("exec").description("Run a script with the Base44 SDK pre-authenticated as the current user").option("--local", "Run against the local `base44 dev` server instead of the deployed app").option("--port <number>", `Port the local dev server is on (with --local; defaults to ${DEFAULT_DEV_SERVER_PORT})`).option("--privileged", "Run with admin privileges (bypass RLS). Requires app owner/editor role.").option("--data-env <environment>", "Data environment to run against (e.g. dev, prod)").addHelpText("after", `
258553
258561
  Examples:
258554
258562
  Run a script file:
258555
258563
  $ cat ./script.ts | base44 exec
258556
258564
 
258557
258565
  Inline script:
258558
- $ echo "const users = await base44.entities.User.list()" | base44 exec`).action(async (ctx) => {
258559
- return await execAction(ctx);
258560
- });
258566
+ $ echo "const users = await base44.entities.User.list()" | base44 exec
258567
+
258568
+ Against the local dev server (base44 dev must be running):
258569
+ $ echo "await base44.entities.Task.create({ title: 'seed' })" | base44 exec --local
258570
+
258571
+ With privileged access (bypass RLS):
258572
+ $ echo "const all = await base44.entities.Task.list()" | base44 exec --privileged`).action(execAction);
258561
258573
  }
258562
258574
 
258563
258575
  // src/cli/commands/project/eject.ts
@@ -262951,4 +262963,4 @@ export {
262951
262963
  CLIExitError
262952
262964
  };
262953
262965
 
262954
- //# debugId=66DC83EDAF91D49064756E2164756E21
262966
+ //# debugId=6F90C592BC20AEC764756E2164756E21