@base44-preview/cli 0.0.15-pr.97.e56bc81 → 0.0.15-pr.99.e192f83

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.
Files changed (2) hide show
  1. package/dist/cli/index.js +415 -87
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -7900,6 +7900,19 @@ var AuthValidationError = class extends Error {
7900
7900
  this.name = "AuthValidationError";
7901
7901
  }
7902
7902
  };
7903
+ var ConnectorApiError = class extends Error {
7904
+ constructor(message, cause) {
7905
+ super(message);
7906
+ this.cause = cause;
7907
+ this.name = "ConnectorApiError";
7908
+ }
7909
+ };
7910
+ var ConnectorValidationError = class extends Error {
7911
+ constructor(message) {
7912
+ super(message);
7913
+ this.name = "ConnectorValidationError";
7914
+ }
7915
+ };
7903
7916
 
7904
7917
  //#endregion
7905
7918
  //#region src/core/consts.ts
@@ -16706,12 +16719,6 @@ const ProjectConfigSchema = object({
16706
16719
  });
16707
16720
  const AppConfigSchema = object({ id: string().min(1, "id cannot be empty") });
16708
16721
  const CreateProjectResponseSchema = looseObject({ id: string() });
16709
- const AppSchema = looseObject({
16710
- id: string(),
16711
- name: string(),
16712
- has_source_code: boolean()
16713
- });
16714
- const AppsResponseSchema = array(AppSchema);
16715
16722
 
16716
16723
  //#endregion
16717
16724
  //#region src/core/project/config.ts
@@ -16796,13 +16803,6 @@ async function createProject(projectName, description) {
16796
16803
  } });
16797
16804
  return { projectId: CreateProjectResponseSchema.parse(await response.json()).id };
16798
16805
  }
16799
- async function fetchApps() {
16800
- const response = await base44Client.get("api/apps");
16801
- return AppsResponseSchema.parse(await response.json());
16802
- }
16803
- async function fetchLinkableApps() {
16804
- return (await fetchApps()).filter((app) => !app.has_source_code);
16805
- }
16806
16806
 
16807
16807
  //#endregion
16808
16808
  //#region node_modules/ejs/lib/utils.js
@@ -31005,7 +31005,10 @@ const theme = {
31005
31005
  base44OrangeBackground: source_default.bgHex("#E86B3C"),
31006
31006
  shinyOrange: source_default.hex("#FFD700"),
31007
31007
  links: source_default.hex("#00D4FF"),
31008
- white: source_default.white
31008
+ white: source_default.white,
31009
+ success: source_default.green,
31010
+ warning: source_default.yellow,
31011
+ error: source_default.red
31009
31012
  },
31010
31013
  styles: {
31011
31014
  header: source_default.dim,
@@ -38756,33 +38759,20 @@ const deployCommand = new Command("deploy").description("Deploy all project reso
38756
38759
  //#endregion
38757
38760
  //#region src/cli/commands/project/link.ts
38758
38761
  function validateNonInteractiveFlags(command) {
38759
- const { create: create$1, existing, name: name$1 } = command.opts();
38760
- if (create$1 && existing) command.error("--create and --existing cannot be used together");
38762
+ const { create: create$1, name: name$1 } = command.opts();
38761
38763
  if (create$1 && !name$1) command.error("--name is required when using --create");
38762
38764
  }
38763
- async function promptForLinkAction(linkableApps) {
38765
+ async function promptForProjectDetails() {
38764
38766
  const actionOptions = [{
38765
38767
  value: "create",
38766
38768
  label: "Create a new project",
38767
38769
  hint: "Create a new Base44 project and link it"
38768
38770
  }];
38769
- if (linkableApps.length > 0) actionOptions.push({
38770
- value: "choose",
38771
- label: "Link an existing project",
38772
- hint: `Choose from ${linkableApps.length} available project${linkableApps.length === 1 ? "" : "s"}`
38773
- });
38774
- const action = await ve({
38775
- message: "How would you like to link this project?",
38776
- options: actionOptions
38777
- });
38778
- if (pD(action)) {
38779
- xe("Operation cancelled.");
38780
- process.exit(0);
38781
- }
38782
- return action;
38783
- }
38784
- async function promptForNewProjectDetails() {
38785
38771
  const result = await Ce({
38772
+ action: () => ve({
38773
+ message: "How would you like to link this project?",
38774
+ options: actionOptions
38775
+ }),
38786
38776
  name: () => {
38787
38777
  return he({
38788
38778
  message: "What is the name of your project?",
@@ -38802,63 +38792,14 @@ async function promptForNewProjectDetails() {
38802
38792
  description: result.description ? result.description.trim() : void 0
38803
38793
  };
38804
38794
  }
38805
- async function promptForExistingApp(linkableApps) {
38806
- const selectedApp = await ve({
38807
- message: "Choose a project to link",
38808
- options: linkableApps.map((app) => ({
38809
- value: app,
38810
- label: app.name
38811
- }))
38812
- });
38813
- if (pD(selectedApp)) {
38814
- xe("Operation cancelled.");
38815
- process.exit(0);
38816
- }
38817
- return selectedApp;
38818
- }
38819
38795
  async function link(options) {
38820
38796
  const projectRoot = await findProjectRoot();
38821
38797
  if (!projectRoot) throw new Error("No Base44 project found. Run this command from a project directory with a config.jsonc file.");
38822
38798
  if (await appConfigExists(projectRoot.root)) throw new Error("Project is already linked. An .app.jsonc file with the appId already exists.");
38823
- if (options.existing) {
38824
- await writeAppConfig(projectRoot.root, options.existing);
38825
- setAppConfig({
38826
- id: options.existing,
38827
- projectRoot: projectRoot.root
38828
- });
38829
- M.message(`${theme.styles.header("Dashboard")}: ${theme.colors.links(getDashboardUrl(options.existing))}`);
38830
- return { outroMessage: "Project linked" };
38831
- }
38832
- if (options.create) {
38833
- const { projectId: projectId$1 } = await runTask("Creating project on Base44...", async () => {
38834
- return await createProject(options.name.trim(), options.description?.trim());
38835
- }, {
38836
- successMessage: "Project created successfully",
38837
- errorMessage: "Failed to create project"
38838
- });
38839
- await writeAppConfig(projectRoot.root, projectId$1);
38840
- setAppConfig({
38841
- id: projectId$1,
38842
- projectRoot: projectRoot.root
38843
- });
38844
- M.message(`${theme.styles.header("Dashboard")}: ${theme.colors.links(getDashboardUrl(projectId$1))}`);
38845
- return { outroMessage: "Project linked" };
38846
- }
38847
- const linkableApps = await runTask("Fetching your projects...", async () => fetchLinkableApps(), {
38848
- successMessage: `Found ${theme.colors.base44Orange("projects")} available for linking`,
38849
- errorMessage: "Failed to fetch projects"
38850
- });
38851
- if (await promptForLinkAction(linkableApps) === "choose") {
38852
- const selectedApp = await promptForExistingApp(linkableApps);
38853
- await writeAppConfig(projectRoot.root, selectedApp.id);
38854
- setAppConfig({
38855
- id: selectedApp.id,
38856
- projectRoot: projectRoot.root
38857
- });
38858
- M.message(`${theme.styles.header("Dashboard")}: ${theme.colors.links(getDashboardUrl(selectedApp.id))}`);
38859
- return { outroMessage: "Project linked" };
38860
- }
38861
- const { name: name$1, description } = await promptForNewProjectDetails();
38799
+ const { name: name$1, description } = options.create ? {
38800
+ name: options.name.trim(),
38801
+ description: options.description?.trim()
38802
+ } : await promptForProjectDetails();
38862
38803
  const { projectId } = await runTask("Creating project on Base44...", async () => {
38863
38804
  return await createProject(name$1, description);
38864
38805
  }, {
@@ -38873,7 +38814,7 @@ async function link(options) {
38873
38814
  M.message(`${theme.styles.header("Dashboard")}: ${theme.colors.links(getDashboardUrl(projectId))}`);
38874
38815
  return { outroMessage: "Project linked" };
38875
38816
  }
38876
- const linkCommand = new Command("link").description("Link a local project to a Base44 project").option("-c, --create", "Create a new project (skip selection prompt)").option("-e, --existing <id>", "Link to an existing project by ID (skip selection prompt)").option("-n, --name <name>", "Project name (required when --create is used)").option("-d, --description <description>", "Project description").hook("preAction", validateNonInteractiveFlags).action(async (options) => {
38817
+ const linkCommand = new Command("link").description("Link a local project to a Base44 project").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").hook("preAction", validateNonInteractiveFlags).action(async (options) => {
38877
38818
  await runCommand(() => link(options), {
38878
38819
  requireAuth: true,
38879
38820
  requireAppConfig: false
@@ -38901,6 +38842,390 @@ const siteDeployCommand = new Command("site").description("Manage site deploymen
38901
38842
  await runCommand(() => deployAction(options), { requireAuth: true });
38902
38843
  }));
38903
38844
 
38845
+ //#endregion
38846
+ //#region src/core/connectors/schema.ts
38847
+ /**
38848
+ * Response from POST /api/apps/{app_id}/external-auth/initiate
38849
+ */
38850
+ const InitiateResponseSchema = object({
38851
+ redirect_url: string().optional(),
38852
+ connection_id: string().optional(),
38853
+ already_authorized: boolean().optional(),
38854
+ other_user_email: string().optional(),
38855
+ error: string().optional()
38856
+ });
38857
+ /**
38858
+ * Response from GET /api/apps/{app_id}/external-auth/status
38859
+ */
38860
+ const StatusResponseSchema = object({
38861
+ status: _enum([
38862
+ "ACTIVE",
38863
+ "PENDING",
38864
+ "FAILED"
38865
+ ]),
38866
+ account_email: string().optional(),
38867
+ error: string().optional()
38868
+ });
38869
+ /**
38870
+ * A connected integration from the list endpoint
38871
+ */
38872
+ const ConnectorSchema = object({
38873
+ integration_type: string(),
38874
+ status: string(),
38875
+ connected_at: string().optional(),
38876
+ account_info: object({
38877
+ email: string().optional(),
38878
+ name: string().optional()
38879
+ }).optional()
38880
+ }).transform((data) => ({
38881
+ integrationType: data.integration_type,
38882
+ status: data.status,
38883
+ connectedAt: data.connected_at,
38884
+ accountInfo: data.account_info
38885
+ }));
38886
+ /**
38887
+ * Response from GET /api/apps/{app_id}/external-auth/list
38888
+ */
38889
+ const ListResponseSchema = object({ integrations: array(ConnectorSchema) });
38890
+ /**
38891
+ * Generic API error response
38892
+ */
38893
+ const ApiErrorSchema = object({
38894
+ error: string(),
38895
+ detail: string().optional()
38896
+ });
38897
+
38898
+ //#endregion
38899
+ //#region src/core/connectors/api.ts
38900
+ /**
38901
+ * Initiates OAuth flow for a connector integration.
38902
+ * Returns a redirect URL to open in the browser.
38903
+ */
38904
+ async function initiateOAuth(integrationType, scopes = null) {
38905
+ const response = await getAppClient().post("external-auth/initiate", {
38906
+ json: {
38907
+ integration_type: integrationType,
38908
+ scopes
38909
+ },
38910
+ throwHttpErrors: false
38911
+ });
38912
+ const json = await response.json();
38913
+ if (!response.ok) {
38914
+ const errorResult = ApiErrorSchema.safeParse(json);
38915
+ if (errorResult.success) throw new ConnectorApiError(errorResult.data.error);
38916
+ throw new ConnectorApiError(`Failed to initiate OAuth: ${response.status} ${response.statusText}`);
38917
+ }
38918
+ const result = InitiateResponseSchema.safeParse(json);
38919
+ if (!result.success) throw new ConnectorValidationError(`Invalid initiate response from server: ${result.error.message}`);
38920
+ return result.data;
38921
+ }
38922
+ /**
38923
+ * Checks the status of an OAuth connection attempt.
38924
+ */
38925
+ async function checkOAuthStatus(integrationType, connectionId) {
38926
+ const response = await getAppClient().get("external-auth/status", {
38927
+ searchParams: {
38928
+ integration_type: integrationType,
38929
+ connection_id: connectionId
38930
+ },
38931
+ throwHttpErrors: false
38932
+ });
38933
+ const json = await response.json();
38934
+ if (!response.ok) {
38935
+ const errorResult = ApiErrorSchema.safeParse(json);
38936
+ if (errorResult.success) throw new ConnectorApiError(errorResult.data.error);
38937
+ throw new ConnectorApiError(`Failed to check OAuth status: ${response.status} ${response.statusText}`);
38938
+ }
38939
+ const result = StatusResponseSchema.safeParse(json);
38940
+ if (!result.success) throw new ConnectorValidationError(`Invalid status response from server: ${result.error.message}`);
38941
+ return result.data;
38942
+ }
38943
+ /**
38944
+ * Lists all connected integrations for the current app.
38945
+ */
38946
+ async function listConnectors() {
38947
+ const response = await getAppClient().get("external-auth/list", { throwHttpErrors: false });
38948
+ const json = await response.json();
38949
+ if (!response.ok) {
38950
+ const errorResult = ApiErrorSchema.safeParse(json);
38951
+ if (errorResult.success) throw new ConnectorApiError(errorResult.data.error);
38952
+ throw new ConnectorApiError(`Failed to list connectors: ${response.status} ${response.statusText}`);
38953
+ }
38954
+ const result = ListResponseSchema.safeParse(json);
38955
+ if (!result.success) throw new ConnectorValidationError(`Invalid list response from server: ${result.error.message}`);
38956
+ return result.data.integrations;
38957
+ }
38958
+ /**
38959
+ * Disconnects (soft delete) a connector integration.
38960
+ */
38961
+ async function disconnectConnector(integrationType) {
38962
+ const response = await getAppClient().delete(`external-auth/integrations/${integrationType}`, { throwHttpErrors: false });
38963
+ if (!response.ok) {
38964
+ const json = await response.json();
38965
+ const errorResult = ApiErrorSchema.safeParse(json);
38966
+ if (errorResult.success) throw new ConnectorApiError(errorResult.data.error);
38967
+ throw new ConnectorApiError(`Failed to disconnect connector: ${response.status} ${response.statusText}`);
38968
+ }
38969
+ }
38970
+
38971
+ //#endregion
38972
+ //#region src/core/connectors/constants.ts
38973
+ /**
38974
+ * Supported OAuth connector integrations.
38975
+ * Based on apper/backend/app/external_auth/models/constants.py
38976
+ */
38977
+ const SUPPORTED_INTEGRATIONS = [
38978
+ "googlecalendar",
38979
+ "googledrive",
38980
+ "gmail",
38981
+ "googlesheets",
38982
+ "googledocs",
38983
+ "googleslides",
38984
+ "slack",
38985
+ "notion",
38986
+ "salesforce",
38987
+ "hubspot",
38988
+ "linkedin",
38989
+ "tiktok"
38990
+ ];
38991
+ /**
38992
+ * Display names for integrations (for CLI output)
38993
+ */
38994
+ const INTEGRATION_DISPLAY_NAMES = {
38995
+ googlecalendar: "Google Calendar",
38996
+ googledrive: "Google Drive",
38997
+ gmail: "Gmail",
38998
+ googlesheets: "Google Sheets",
38999
+ googledocs: "Google Docs",
39000
+ googleslides: "Google Slides",
39001
+ slack: "Slack",
39002
+ notion: "Notion",
39003
+ salesforce: "Salesforce",
39004
+ hubspot: "HubSpot",
39005
+ linkedin: "LinkedIn",
39006
+ tiktok: "TikTok"
39007
+ };
39008
+ function isValidIntegration(type) {
39009
+ return SUPPORTED_INTEGRATIONS.includes(type);
39010
+ }
39011
+ function getIntegrationDisplayName(type) {
39012
+ if (isValidIntegration(type)) return INTEGRATION_DISPLAY_NAMES[type];
39013
+ return type;
39014
+ }
39015
+
39016
+ //#endregion
39017
+ //#region src/cli/commands/connectors/add.ts
39018
+ const POLL_INTERVAL_MS = 2e3;
39019
+ const POLL_TIMEOUT_MS = 300 * 1e3;
39020
+ async function promptForIntegrationType() {
39021
+ const selected = await ve({
39022
+ message: "Select an integration to connect:",
39023
+ options: SUPPORTED_INTEGRATIONS.map((type) => ({
39024
+ value: type,
39025
+ label: getIntegrationDisplayName(type)
39026
+ }))
39027
+ });
39028
+ if (pD(selected)) return null;
39029
+ return selected;
39030
+ }
39031
+ async function waitForOAuthCompletion(integrationType, connectionId) {
39032
+ let accountEmail;
39033
+ let error;
39034
+ try {
39035
+ await runTask("Waiting for authorization...", async (updateMessage) => {
39036
+ await pWaitFor(async () => {
39037
+ const status = await checkOAuthStatus(integrationType, connectionId);
39038
+ if (status.status === "ACTIVE") {
39039
+ accountEmail = status.account_email;
39040
+ return true;
39041
+ }
39042
+ if (status.status === "FAILED") {
39043
+ error = status.error || "Authorization failed";
39044
+ throw new Error(error);
39045
+ }
39046
+ updateMessage("Waiting for authorization in browser...");
39047
+ return false;
39048
+ }, {
39049
+ interval: POLL_INTERVAL_MS,
39050
+ timeout: POLL_TIMEOUT_MS
39051
+ });
39052
+ }, {
39053
+ successMessage: "Authorization completed!",
39054
+ errorMessage: "Authorization failed"
39055
+ });
39056
+ return {
39057
+ success: true,
39058
+ accountEmail
39059
+ };
39060
+ } catch (err) {
39061
+ if (err instanceof Error && err.message.includes("timed out")) return {
39062
+ success: false,
39063
+ error: "Authorization timed out. Please try again."
39064
+ };
39065
+ return {
39066
+ success: false,
39067
+ error: error || (err instanceof Error ? err.message : "Unknown error")
39068
+ };
39069
+ }
39070
+ }
39071
+ async function addConnector(integrationType) {
39072
+ let selectedType;
39073
+ if (!integrationType) {
39074
+ const prompted = await promptForIntegrationType();
39075
+ if (!prompted) return { outroMessage: "Cancelled" };
39076
+ selectedType = prompted;
39077
+ } else {
39078
+ if (!isValidIntegration(integrationType)) {
39079
+ const supportedList = SUPPORTED_INTEGRATIONS.join(", ");
39080
+ throw new Error(`Unsupported connector: ${integrationType}\nSupported connectors: ${supportedList}`);
39081
+ }
39082
+ selectedType = integrationType;
39083
+ }
39084
+ const displayName = getIntegrationDisplayName(selectedType);
39085
+ const initiateResponse = await runTask(`Initiating ${displayName} connection...`, async () => {
39086
+ return await initiateOAuth(selectedType);
39087
+ }, {
39088
+ successMessage: `${displayName} OAuth initiated`,
39089
+ errorMessage: `Failed to initiate ${displayName} connection`
39090
+ });
39091
+ if (initiateResponse.already_authorized) return { outroMessage: `Already connected to ${theme.styles.bold(displayName)}` };
39092
+ if (initiateResponse.error === "different_user" && initiateResponse.other_user_email) throw new Error(`This app is already connected to ${displayName} by ${initiateResponse.other_user_email}`);
39093
+ if (!initiateResponse.redirect_url || !initiateResponse.connection_id) throw new Error("Invalid response from server: missing redirect URL or connection ID");
39094
+ M.info(`Opening browser for ${displayName} authorization...`);
39095
+ await open_default(initiateResponse.redirect_url);
39096
+ const result = await waitForOAuthCompletion(selectedType, initiateResponse.connection_id);
39097
+ if (!result.success) throw new Error(result.error || "Authorization failed");
39098
+ const accountInfo = result.accountEmail ? ` as ${theme.styles.bold(result.accountEmail)}` : "";
39099
+ return { outroMessage: `Successfully connected to ${theme.styles.bold(displayName)}${accountInfo}` };
39100
+ }
39101
+ const connectorsAddCommand = new Command("connectors:add").argument("[type]", "Integration type (e.g., slack, notion, googlecalendar)").description("Connect an OAuth integration").action(async (type) => {
39102
+ await runCommand(() => addConnector(type), {
39103
+ requireAuth: true,
39104
+ requireAppConfig: true
39105
+ });
39106
+ });
39107
+
39108
+ //#endregion
39109
+ //#region src/cli/commands/connectors/list.ts
39110
+ function formatDate(dateString) {
39111
+ if (!dateString) return "-";
39112
+ try {
39113
+ return new Date(dateString).toLocaleDateString("en-US", {
39114
+ year: "numeric",
39115
+ month: "short",
39116
+ day: "numeric"
39117
+ });
39118
+ } catch {
39119
+ return dateString;
39120
+ }
39121
+ }
39122
+ function formatStatus(status) {
39123
+ const normalized = status.toLowerCase();
39124
+ if (normalized === "active" || normalized === "connected") return theme.colors.success("● active");
39125
+ if (normalized === "expired") return theme.colors.warning("● expired");
39126
+ if (normalized === "failed" || normalized === "disconnected") return theme.colors.error("● disconnected");
39127
+ return status;
39128
+ }
39129
+ async function listConnectorsCommand() {
39130
+ const connectors = await runTask("Fetching connectors...", async () => {
39131
+ return await listConnectors();
39132
+ }, {
39133
+ successMessage: "Connectors loaded",
39134
+ errorMessage: "Failed to fetch connectors"
39135
+ });
39136
+ if (connectors.length === 0) {
39137
+ M.info("No connectors configured for this app.");
39138
+ M.info(`Run ${theme.styles.bold("base44 connectors:add")} to connect an integration.`);
39139
+ return { outroMessage: "" };
39140
+ }
39141
+ console.log();
39142
+ console.log(theme.styles.bold("Connected Integrations:"));
39143
+ console.log();
39144
+ const headers = [
39145
+ "Type",
39146
+ "Account",
39147
+ "Status",
39148
+ "Connected"
39149
+ ];
39150
+ const colWidths = [
39151
+ 20,
39152
+ 30,
39153
+ 15,
39154
+ 15
39155
+ ];
39156
+ const headerRow = headers.map((h$2, i$1) => h$2.padEnd(colWidths[i$1])).join(" ");
39157
+ console.log(theme.styles.dim(headerRow));
39158
+ console.log(theme.styles.dim("─".repeat(headerRow.length)));
39159
+ for (const connector of connectors) {
39160
+ const type = getIntegrationDisplayName(connector.integrationType).padEnd(colWidths[0]);
39161
+ const account = (connector.accountInfo?.email || connector.accountInfo?.name || "-").padEnd(colWidths[1]);
39162
+ const status = formatStatus(connector.status);
39163
+ const connected = formatDate(connector.connectedAt).padEnd(colWidths[3]);
39164
+ console.log(`${type} ${account} ${status.padEnd(colWidths[2] + 10)} ${connected}`);
39165
+ }
39166
+ console.log();
39167
+ return { outroMessage: `${connectors.length} connector${connectors.length === 1 ? "" : "s"} configured` };
39168
+ }
39169
+ const connectorsListCommand = new Command("connectors:list").description("List all connected OAuth integrations").action(async () => {
39170
+ await runCommand(listConnectorsCommand, {
39171
+ requireAuth: true,
39172
+ requireAppConfig: true
39173
+ });
39174
+ });
39175
+
39176
+ //#endregion
39177
+ //#region src/cli/commands/connectors/remove.ts
39178
+ async function promptForConnectorToRemove(connectors) {
39179
+ const selected = await ve({
39180
+ message: "Select a connector to remove:",
39181
+ options: connectors.map((c$1) => ({
39182
+ value: c$1.integrationType,
39183
+ label: `${getIntegrationDisplayName(c$1.integrationType)}${c$1.accountInfo?.email ? ` (${c$1.accountInfo.email})` : ""}`
39184
+ }))
39185
+ });
39186
+ if (pD(selected)) return null;
39187
+ return selected;
39188
+ }
39189
+ async function removeConnectorCommand(integrationType) {
39190
+ const connectors = await runTask("Fetching connectors...", async () => {
39191
+ return await listConnectors();
39192
+ }, {
39193
+ successMessage: "Connectors loaded",
39194
+ errorMessage: "Failed to fetch connectors"
39195
+ });
39196
+ if (connectors.length === 0) return { outroMessage: "No connectors to remove" };
39197
+ let selectedType;
39198
+ if (!integrationType) {
39199
+ const prompted = await promptForConnectorToRemove(connectors);
39200
+ if (!prompted) return { outroMessage: "Cancelled" };
39201
+ selectedType = prompted;
39202
+ } else {
39203
+ if (!isValidIntegration(integrationType)) throw new Error(`Invalid connector type: ${integrationType}`);
39204
+ if (!connectors.some((c$1) => c$1.integrationType === integrationType)) throw new Error(`No ${getIntegrationDisplayName(integrationType)} connector found for this app`);
39205
+ selectedType = integrationType;
39206
+ }
39207
+ const displayName = getIntegrationDisplayName(selectedType);
39208
+ const connector = connectors.find((c$1) => c$1.integrationType === selectedType);
39209
+ const shouldRemove = await ye({
39210
+ message: `Disconnect ${displayName}${connector?.accountInfo?.email ? ` (${connector.accountInfo.email})` : ""}?`,
39211
+ initialValue: false
39212
+ });
39213
+ if (pD(shouldRemove) || !shouldRemove) return { outroMessage: "Cancelled" };
39214
+ await runTask(`Disconnecting ${displayName}...`, async () => {
39215
+ await disconnectConnector(selectedType);
39216
+ }, {
39217
+ successMessage: `${displayName} disconnected`,
39218
+ errorMessage: `Failed to disconnect ${displayName}`
39219
+ });
39220
+ return { outroMessage: `Successfully disconnected ${theme.styles.bold(displayName)}` };
39221
+ }
39222
+ const connectorsRemoveCommand = new Command("connectors:remove").argument("[type]", "Integration type to remove (e.g., slack, notion)").description("Disconnect an OAuth integration").action(async (type) => {
39223
+ await runCommand(() => removeConnectorCommand(type), {
39224
+ requireAuth: true,
39225
+ requireAppConfig: true
39226
+ });
39227
+ });
39228
+
38904
39229
  //#endregion
38905
39230
  //#region package.json
38906
39231
  var version = "0.0.15";
@@ -38920,6 +39245,9 @@ program.addCommand(linkCommand);
38920
39245
  program.addCommand(entitiesPushCommand);
38921
39246
  program.addCommand(functionsDeployCommand);
38922
39247
  program.addCommand(siteDeployCommand);
39248
+ program.addCommand(connectorsAddCommand);
39249
+ program.addCommand(connectorsListCommand);
39250
+ program.addCommand(connectorsRemoveCommand);
38923
39251
  program.parse();
38924
39252
 
38925
39253
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@base44-preview/cli",
3
- "version": "0.0.15-pr.97.e56bc81",
3
+ "version": "0.0.15-pr.99.e192f83",
4
4
  "description": "Base44 CLI - Unified interface for managing Base44 applications",
5
5
  "type": "module",
6
6
  "main": "./dist/cli/index.js",