@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.
- package/dist/cli/index.js +415 -87
- 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,
|
|
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
|
|
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
|
-
|
|
38824
|
-
|
|
38825
|
-
|
|
38826
|
-
|
|
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("-
|
|
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