@base44-preview/cli 0.0.24-pr.146.e5ee77a → 0.0.25-pr.104.089a20e
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/README.md +2 -0
- package/dist/index.js +364 -11
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -53,6 +53,8 @@ The CLI will guide you through project setup. For step-by-step tutorials, see th
|
|
|
53
53
|
| [`entities push`](https://docs.base44.com/developers/references/cli/commands/entities-push) | Push local entity schemas to Base44 |
|
|
54
54
|
| [`functions deploy`](https://docs.base44.com/developers/references/cli/commands/functions-deploy) | Deploy local functions to Base44 |
|
|
55
55
|
| [`site deploy`](https://docs.base44.com/developers/references/cli/commands/site-deploy) | Deploy built site files to Base44 hosting |
|
|
56
|
+
| `connectors add [type]` | Connect an OAuth integration |
|
|
57
|
+
| `connectors remove [type]` | Disconnect an integration |
|
|
56
58
|
|
|
57
59
|
|
|
58
60
|
<!--| [`eject`](https://docs.base44.com/developers/references/cli/commands/eject) | Create a Base44 backend project from an existing Base44 app | -->
|
package/dist/index.js
CHANGED
|
@@ -16889,6 +16889,11 @@ async function writeAgents(agentsDir, remoteAgents) {
|
|
|
16889
16889
|
//#endregion
|
|
16890
16890
|
//#region src/core/resources/agent/api.ts
|
|
16891
16891
|
async function pushAgents(agents) {
|
|
16892
|
+
if (agents.length === 0) return {
|
|
16893
|
+
created: [],
|
|
16894
|
+
updated: [],
|
|
16895
|
+
deleted: []
|
|
16896
|
+
};
|
|
16892
16897
|
const response = await getAppClient().put("agent-configs", {
|
|
16893
16898
|
json: agents,
|
|
16894
16899
|
throwHttpErrors: false
|
|
@@ -30612,25 +30617,27 @@ async function createArchive(pathToArchive, targetArchivePath) {
|
|
|
30612
30617
|
* Checks if there are any resources to deploy in the project.
|
|
30613
30618
|
*
|
|
30614
30619
|
* @param projectData - The project configuration and resources
|
|
30615
|
-
* @returns true if there are entities, functions, or a configured site to deploy
|
|
30620
|
+
* @returns true if there are entities, functions, agents, or a configured site to deploy
|
|
30616
30621
|
*/
|
|
30617
30622
|
function hasResourcesToDeploy(projectData) {
|
|
30618
|
-
const { project, entities, functions } = projectData;
|
|
30623
|
+
const { project, entities, functions, agents } = projectData;
|
|
30619
30624
|
const hasSite = Boolean(project.site?.outputDirectory);
|
|
30620
30625
|
const hasEntities = entities.length > 0;
|
|
30621
30626
|
const hasFunctions = functions.length > 0;
|
|
30622
|
-
|
|
30627
|
+
const hasAgents = agents.length > 0;
|
|
30628
|
+
return hasEntities || hasFunctions || hasAgents || hasSite;
|
|
30623
30629
|
}
|
|
30624
30630
|
/**
|
|
30625
|
-
* Deploys all project resources (entities, functions, and site) to Base44.
|
|
30631
|
+
* Deploys all project resources (entities, functions, agents, and site) to Base44.
|
|
30626
30632
|
*
|
|
30627
30633
|
* @param projectData - The project configuration and resources to deploy
|
|
30628
30634
|
* @returns The deployment result including app URL if site was deployed
|
|
30629
30635
|
*/
|
|
30630
30636
|
async function deployAll(projectData) {
|
|
30631
|
-
const { project, entities, functions } = projectData;
|
|
30637
|
+
const { project, entities, functions, agents } = projectData;
|
|
30632
30638
|
await entityResource.push(entities);
|
|
30633
30639
|
await functionResource.push(functions);
|
|
30640
|
+
await agentResource.push(agents);
|
|
30634
30641
|
if (project.site?.outputDirectory) {
|
|
30635
30642
|
const { appUrl } = await deploySite(resolve(project.root, project.site.outputDirectory));
|
|
30636
30643
|
return { appUrl };
|
|
@@ -30642,11 +30649,31 @@ async function deployAll(projectData) {
|
|
|
30642
30649
|
//#region src/core/project/app-config.ts
|
|
30643
30650
|
let cache = null;
|
|
30644
30651
|
/**
|
|
30652
|
+
* Load app config from BASE44_CLI_TEST_OVERRIDES env var.
|
|
30653
|
+
* @returns true if override was applied, false otherwise
|
|
30654
|
+
*/
|
|
30655
|
+
function loadFromTestOverrides() {
|
|
30656
|
+
const overrides = process.env.BASE44_CLI_TEST_OVERRIDES;
|
|
30657
|
+
if (!overrides) return false;
|
|
30658
|
+
try {
|
|
30659
|
+
const data = JSON.parse(overrides);
|
|
30660
|
+
if (data.appConfig?.id && data.appConfig?.projectRoot) {
|
|
30661
|
+
cache = {
|
|
30662
|
+
id: data.appConfig.id,
|
|
30663
|
+
projectRoot: data.appConfig.projectRoot
|
|
30664
|
+
};
|
|
30665
|
+
return true;
|
|
30666
|
+
}
|
|
30667
|
+
} catch {}
|
|
30668
|
+
return false;
|
|
30669
|
+
}
|
|
30670
|
+
/**
|
|
30645
30671
|
* Initialize app config by reading from .app.jsonc.
|
|
30646
30672
|
* Must be called before using getAppConfig().
|
|
30647
30673
|
* @throws Error if no project found or .app.jsonc missing
|
|
30648
30674
|
*/
|
|
30649
30675
|
async function initAppConfig() {
|
|
30676
|
+
if (loadFromTestOverrides()) return;
|
|
30650
30677
|
if (cache) return;
|
|
30651
30678
|
const projectRoot = await findProjectRoot();
|
|
30652
30679
|
if (!projectRoot) throw new Error("No Base44 project found. Run this command from a project directory with a config.jsonc file.");
|
|
@@ -31278,7 +31305,10 @@ const theme = {
|
|
|
31278
31305
|
base44OrangeBackground: source_default.bgHex("#E86B3C"),
|
|
31279
31306
|
shinyOrange: source_default.hex("#FFD700"),
|
|
31280
31307
|
links: source_default.hex("#00D4FF"),
|
|
31281
|
-
white: source_default.white
|
|
31308
|
+
white: source_default.white,
|
|
31309
|
+
success: source_default.green,
|
|
31310
|
+
warning: source_default.yellow,
|
|
31311
|
+
error: source_default.red
|
|
31282
31312
|
},
|
|
31283
31313
|
styles: {
|
|
31284
31314
|
header: source_default.dim,
|
|
@@ -31595,7 +31625,8 @@ const logoutCommand = new Command("logout").description("Logout from current dev
|
|
|
31595
31625
|
async function pushEntitiesAction() {
|
|
31596
31626
|
const { entities } = await readProjectConfig();
|
|
31597
31627
|
if (entities.length === 0) return { outroMessage: "No entities found in project" };
|
|
31598
|
-
|
|
31628
|
+
const entityNames = entities.map((e$1) => e$1.name).join(", ");
|
|
31629
|
+
M.info(`Found ${entities.length} entities to push: ${entityNames}`);
|
|
31599
31630
|
const result = await runTask("Pushing entities to Base44", async () => {
|
|
31600
31631
|
return await pushEntities(entities);
|
|
31601
31632
|
}, {
|
|
@@ -39058,7 +39089,7 @@ var open_default = open;
|
|
|
39058
39089
|
//#region src/cli/commands/project/dashboard.ts
|
|
39059
39090
|
async function openDashboard() {
|
|
39060
39091
|
const dashboardUrl = getDashboardUrl();
|
|
39061
|
-
await open_default(dashboardUrl);
|
|
39092
|
+
if (!process.env.CI) await open_default(dashboardUrl);
|
|
39062
39093
|
return { outroMessage: `Dashboard opened at ${dashboardUrl}` };
|
|
39063
39094
|
}
|
|
39064
39095
|
const dashboardCommand = new Command("dashboard").description("Open the app dashboard in your browser").action(async () => {
|
|
@@ -39070,10 +39101,11 @@ const dashboardCommand = new Command("dashboard").description("Open the app dash
|
|
|
39070
39101
|
async function deployAction$1(options) {
|
|
39071
39102
|
const projectData = await readProjectConfig();
|
|
39072
39103
|
if (!hasResourcesToDeploy(projectData)) return { outroMessage: "No resources found to deploy" };
|
|
39073
|
-
const { project, entities, functions } = projectData;
|
|
39104
|
+
const { project, entities, functions, agents } = projectData;
|
|
39074
39105
|
const summaryLines = [];
|
|
39075
39106
|
if (entities.length > 0) summaryLines.push(` - ${entities.length} ${entities.length === 1 ? "entity" : "entities"}`);
|
|
39076
39107
|
if (functions.length > 0) summaryLines.push(` - ${functions.length} ${functions.length === 1 ? "function" : "functions"}`);
|
|
39108
|
+
if (agents.length > 0) summaryLines.push(` - ${agents.length} ${agents.length === 1 ? "agent" : "agents"}`);
|
|
39077
39109
|
if (project.site?.outputDirectory) summaryLines.push(` - Site from ${project.site.outputDirectory}`);
|
|
39078
39110
|
if (!options.yes) {
|
|
39079
39111
|
M.warn(`This will update your Base44 app with:\n${summaryLines.join("\n")}`);
|
|
@@ -39090,7 +39122,7 @@ async function deployAction$1(options) {
|
|
|
39090
39122
|
if (result.appUrl) M.message(`${theme.styles.header("App URL")}: ${theme.colors.links(result.appUrl)}`);
|
|
39091
39123
|
return { outroMessage: "App deployed successfully" };
|
|
39092
39124
|
}
|
|
39093
|
-
const deployCommand = new Command("deploy").description("Deploy all project resources (entities, functions, and site)").option("-y, --yes", "Skip confirmation prompt").action(async (options) => {
|
|
39125
|
+
const deployCommand = new Command("deploy").description("Deploy all project resources (entities, functions, agents, and site)").option("-y, --yes", "Skip confirmation prompt").action(async (options) => {
|
|
39094
39126
|
await runCommand(() => deployAction$1(options), { requireAuth: true });
|
|
39095
39127
|
});
|
|
39096
39128
|
|
|
@@ -39235,9 +39267,329 @@ const siteDeployCommand = new Command("site").description("Manage site deploymen
|
|
|
39235
39267
|
await runCommand(() => deployAction(options), { requireAuth: true });
|
|
39236
39268
|
}));
|
|
39237
39269
|
|
|
39270
|
+
//#endregion
|
|
39271
|
+
//#region src/core/connectors/schema.ts
|
|
39272
|
+
/**
|
|
39273
|
+
* Response from POST /api/apps/{app_id}/external-auth/initiate
|
|
39274
|
+
*/
|
|
39275
|
+
const InitiateResponseSchema = object({
|
|
39276
|
+
redirect_url: string().nullish(),
|
|
39277
|
+
connection_id: string().nullish(),
|
|
39278
|
+
already_authorized: boolean().nullish(),
|
|
39279
|
+
other_user_email: string().nullish(),
|
|
39280
|
+
error: string().nullish()
|
|
39281
|
+
}).transform((data) => ({
|
|
39282
|
+
redirectUrl: data.redirect_url,
|
|
39283
|
+
connectionId: data.connection_id,
|
|
39284
|
+
alreadyAuthorized: data.already_authorized,
|
|
39285
|
+
otherUserEmail: data.other_user_email,
|
|
39286
|
+
error: data.error
|
|
39287
|
+
}));
|
|
39288
|
+
/**
|
|
39289
|
+
* Response from GET /api/apps/{app_id}/external-auth/status
|
|
39290
|
+
*/
|
|
39291
|
+
const StatusResponseSchema = object({
|
|
39292
|
+
status: _enum([
|
|
39293
|
+
"ACTIVE",
|
|
39294
|
+
"PENDING",
|
|
39295
|
+
"FAILED"
|
|
39296
|
+
]),
|
|
39297
|
+
account_email: string().nullish(),
|
|
39298
|
+
error: string().nullish()
|
|
39299
|
+
}).transform((data) => ({
|
|
39300
|
+
status: data.status,
|
|
39301
|
+
accountEmail: data.account_email,
|
|
39302
|
+
error: data.error
|
|
39303
|
+
}));
|
|
39304
|
+
/**
|
|
39305
|
+
* A connected integration from the list endpoint
|
|
39306
|
+
*/
|
|
39307
|
+
const ConnectorSchema = object({
|
|
39308
|
+
integration_type: string(),
|
|
39309
|
+
status: string(),
|
|
39310
|
+
connected_at: string().nullish(),
|
|
39311
|
+
account_info: object({
|
|
39312
|
+
email: string().nullish(),
|
|
39313
|
+
name: string().nullish()
|
|
39314
|
+
}).nullish()
|
|
39315
|
+
}).transform((data) => ({
|
|
39316
|
+
integrationType: data.integration_type,
|
|
39317
|
+
status: data.status,
|
|
39318
|
+
connectedAt: data.connected_at,
|
|
39319
|
+
accountInfo: data.account_info
|
|
39320
|
+
}));
|
|
39321
|
+
/**
|
|
39322
|
+
* Response from GET /api/apps/{app_id}/external-auth/list
|
|
39323
|
+
*/
|
|
39324
|
+
const ListResponseSchema = object({ integrations: array(ConnectorSchema) });
|
|
39325
|
+
|
|
39326
|
+
//#endregion
|
|
39327
|
+
//#region src/core/connectors/api.ts
|
|
39328
|
+
/**
|
|
39329
|
+
* Initiates OAuth flow for a connector integration.
|
|
39330
|
+
* Returns a redirect URL to open in the browser.
|
|
39331
|
+
*/
|
|
39332
|
+
async function initiateOAuth(integrationType, scopes = null) {
|
|
39333
|
+
const json = await (await getAppClient().post("external-auth/initiate", { json: {
|
|
39334
|
+
integration_type: integrationType,
|
|
39335
|
+
scopes
|
|
39336
|
+
} })).json();
|
|
39337
|
+
return InitiateResponseSchema.parse(json);
|
|
39338
|
+
}
|
|
39339
|
+
/**
|
|
39340
|
+
* Checks the status of an OAuth connection attempt.
|
|
39341
|
+
*/
|
|
39342
|
+
async function checkOAuthStatus(integrationType, connectionId) {
|
|
39343
|
+
const json = await (await getAppClient().get("external-auth/status", { searchParams: {
|
|
39344
|
+
integration_type: integrationType,
|
|
39345
|
+
connection_id: connectionId
|
|
39346
|
+
} })).json();
|
|
39347
|
+
return StatusResponseSchema.parse(json);
|
|
39348
|
+
}
|
|
39349
|
+
/**
|
|
39350
|
+
* Lists all connected integrations for the current app.
|
|
39351
|
+
*/
|
|
39352
|
+
async function listConnectors() {
|
|
39353
|
+
const json = await (await getAppClient().get("external-auth/list")).json();
|
|
39354
|
+
return ListResponseSchema.parse(json).integrations;
|
|
39355
|
+
}
|
|
39356
|
+
/**
|
|
39357
|
+
* Disconnects (soft delete) a connector integration.
|
|
39358
|
+
*/
|
|
39359
|
+
async function disconnectConnector(integrationType) {
|
|
39360
|
+
await getAppClient().delete(`external-auth/integrations/${integrationType}`);
|
|
39361
|
+
}
|
|
39362
|
+
/**
|
|
39363
|
+
* Removes (hard delete) a connector integration.
|
|
39364
|
+
* This permanently removes the connector and cannot be undone.
|
|
39365
|
+
*/
|
|
39366
|
+
async function removeConnector(integrationType) {
|
|
39367
|
+
await getAppClient().delete(`external-auth/integrations/${integrationType}/remove`);
|
|
39368
|
+
}
|
|
39369
|
+
|
|
39370
|
+
//#endregion
|
|
39371
|
+
//#region src/core/connectors/consts.ts
|
|
39372
|
+
/**
|
|
39373
|
+
* Supported OAuth connector integrations.
|
|
39374
|
+
* Based on apper/backend/app/external_auth/models/constants.py
|
|
39375
|
+
*/
|
|
39376
|
+
const SUPPORTED_INTEGRATIONS = [
|
|
39377
|
+
"googlecalendar",
|
|
39378
|
+
"googledrive",
|
|
39379
|
+
"gmail",
|
|
39380
|
+
"googlesheets",
|
|
39381
|
+
"googledocs",
|
|
39382
|
+
"googleslides",
|
|
39383
|
+
"slack",
|
|
39384
|
+
"notion",
|
|
39385
|
+
"salesforce",
|
|
39386
|
+
"hubspot",
|
|
39387
|
+
"linkedin",
|
|
39388
|
+
"tiktok"
|
|
39389
|
+
];
|
|
39390
|
+
/**
|
|
39391
|
+
* Display names for integrations (for CLI output)
|
|
39392
|
+
*/
|
|
39393
|
+
const INTEGRATION_DISPLAY_NAMES = {
|
|
39394
|
+
googlecalendar: "Google Calendar",
|
|
39395
|
+
googledrive: "Google Drive",
|
|
39396
|
+
gmail: "Gmail",
|
|
39397
|
+
googlesheets: "Google Sheets",
|
|
39398
|
+
googledocs: "Google Docs",
|
|
39399
|
+
googleslides: "Google Slides",
|
|
39400
|
+
slack: "Slack",
|
|
39401
|
+
notion: "Notion",
|
|
39402
|
+
salesforce: "Salesforce",
|
|
39403
|
+
hubspot: "HubSpot",
|
|
39404
|
+
linkedin: "LinkedIn",
|
|
39405
|
+
tiktok: "TikTok"
|
|
39406
|
+
};
|
|
39407
|
+
function isValidIntegration(type) {
|
|
39408
|
+
return SUPPORTED_INTEGRATIONS.includes(type);
|
|
39409
|
+
}
|
|
39410
|
+
function getIntegrationDisplayName(type) {
|
|
39411
|
+
return INTEGRATION_DISPLAY_NAMES[type] ?? type;
|
|
39412
|
+
}
|
|
39413
|
+
|
|
39414
|
+
//#endregion
|
|
39415
|
+
//#region src/cli/commands/connectors/utils.ts
|
|
39416
|
+
const OAUTH_POLL_INTERVAL_MS = 2e3;
|
|
39417
|
+
const OAUTH_POLL_TIMEOUT_MS = 300 * 1e3;
|
|
39418
|
+
/**
|
|
39419
|
+
* Polls for OAuth completion status.
|
|
39420
|
+
* Returns when status becomes ACTIVE or FAILED, or times out.
|
|
39421
|
+
*/
|
|
39422
|
+
async function waitForOAuthCompletion(integrationType, connectionId, options) {
|
|
39423
|
+
let accountEmail;
|
|
39424
|
+
let error;
|
|
39425
|
+
try {
|
|
39426
|
+
await pWaitFor(async () => {
|
|
39427
|
+
const status = await checkOAuthStatus(integrationType, connectionId);
|
|
39428
|
+
if (status.status === "ACTIVE") {
|
|
39429
|
+
accountEmail = status.accountEmail ?? void 0;
|
|
39430
|
+
return true;
|
|
39431
|
+
}
|
|
39432
|
+
if (status.status === "FAILED") {
|
|
39433
|
+
error = status.error || "Authorization failed";
|
|
39434
|
+
throw new Error(error);
|
|
39435
|
+
}
|
|
39436
|
+
options?.onPending?.();
|
|
39437
|
+
return false;
|
|
39438
|
+
}, {
|
|
39439
|
+
interval: OAUTH_POLL_INTERVAL_MS,
|
|
39440
|
+
timeout: OAUTH_POLL_TIMEOUT_MS
|
|
39441
|
+
});
|
|
39442
|
+
return {
|
|
39443
|
+
success: true,
|
|
39444
|
+
accountEmail
|
|
39445
|
+
};
|
|
39446
|
+
} catch (err) {
|
|
39447
|
+
if (err instanceof Error && err.message.includes("timed out")) return {
|
|
39448
|
+
success: false,
|
|
39449
|
+
error: "Authorization timed out. Please try again."
|
|
39450
|
+
};
|
|
39451
|
+
return {
|
|
39452
|
+
success: false,
|
|
39453
|
+
error: error || (err instanceof Error ? err.message : "Unknown error")
|
|
39454
|
+
};
|
|
39455
|
+
}
|
|
39456
|
+
}
|
|
39457
|
+
/**
|
|
39458
|
+
* Asserts that a string is a valid integration type, throwing if not.
|
|
39459
|
+
*/
|
|
39460
|
+
function assertValidIntegrationType(type, supportedIntegrations) {
|
|
39461
|
+
if (!supportedIntegrations.includes(type)) {
|
|
39462
|
+
const supportedList = supportedIntegrations.join(", ");
|
|
39463
|
+
throw new Error(`Unsupported connector: ${type}\nSupported connectors: ${supportedList}`);
|
|
39464
|
+
}
|
|
39465
|
+
}
|
|
39466
|
+
|
|
39467
|
+
//#endregion
|
|
39468
|
+
//#region src/cli/commands/connectors/add.ts
|
|
39469
|
+
async function promptForIntegrationType() {
|
|
39470
|
+
const selected = await ve({
|
|
39471
|
+
message: "Select an integration to connect:",
|
|
39472
|
+
options: Object.entries(INTEGRATION_DISPLAY_NAMES).map(([type, displayName]) => ({
|
|
39473
|
+
value: type,
|
|
39474
|
+
label: displayName
|
|
39475
|
+
}))
|
|
39476
|
+
});
|
|
39477
|
+
if (pD(selected)) {
|
|
39478
|
+
xe("Operation cancelled.");
|
|
39479
|
+
process.exit(0);
|
|
39480
|
+
}
|
|
39481
|
+
return selected;
|
|
39482
|
+
}
|
|
39483
|
+
async function pollForOAuthCompletion(integrationType, connectionId) {
|
|
39484
|
+
return await runTask("Waiting for authorization...", async () => {
|
|
39485
|
+
return await waitForOAuthCompletion(integrationType, connectionId);
|
|
39486
|
+
}, {
|
|
39487
|
+
successMessage: "Authorization completed!",
|
|
39488
|
+
errorMessage: "Authorization failed"
|
|
39489
|
+
});
|
|
39490
|
+
}
|
|
39491
|
+
async function addConnector(integrationType) {
|
|
39492
|
+
let selectedType;
|
|
39493
|
+
if (integrationType) {
|
|
39494
|
+
assertValidIntegrationType(integrationType, SUPPORTED_INTEGRATIONS);
|
|
39495
|
+
selectedType = integrationType;
|
|
39496
|
+
} else selectedType = await promptForIntegrationType();
|
|
39497
|
+
const displayName = getIntegrationDisplayName(selectedType);
|
|
39498
|
+
const initiateResponse = await runTask(`Initiating ${displayName} connection...`, async () => {
|
|
39499
|
+
return await initiateOAuth(selectedType);
|
|
39500
|
+
}, {
|
|
39501
|
+
successMessage: `${displayName} OAuth initiated`,
|
|
39502
|
+
errorMessage: `Failed to initiate ${displayName} connection`
|
|
39503
|
+
});
|
|
39504
|
+
if (initiateResponse.alreadyAuthorized) return { outroMessage: `Already connected to ${theme.styles.bold(displayName)}` };
|
|
39505
|
+
if (initiateResponse.error === "different_user" && initiateResponse.otherUserEmail) throw new Error(`This app is already connected to ${displayName} by ${initiateResponse.otherUserEmail}`);
|
|
39506
|
+
if (!initiateResponse.redirectUrl || !initiateResponse.connectionId) throw new Error("Invalid response from server: missing redirect URL or connection ID");
|
|
39507
|
+
M.info(`Please authorize ${displayName} at:\n${theme.colors.links(initiateResponse.redirectUrl)}`);
|
|
39508
|
+
const result = await pollForOAuthCompletion(selectedType, initiateResponse.connectionId);
|
|
39509
|
+
if (!result.success) throw new Error(result.error || "Authorization failed");
|
|
39510
|
+
const accountInfo = result.accountEmail ? ` as ${theme.styles.bold(result.accountEmail)}` : "";
|
|
39511
|
+
return { outroMessage: `Successfully connected to ${theme.styles.bold(displayName)}${accountInfo}` };
|
|
39512
|
+
}
|
|
39513
|
+
const connectorsAddCommand = new Command("add").argument("[type]", "Integration type (e.g., slack, notion, googlecalendar)").description("Connect an OAuth integration").action(async (type) => {
|
|
39514
|
+
await runCommand(() => addConnector(type), {
|
|
39515
|
+
requireAuth: true,
|
|
39516
|
+
requireAppConfig: true
|
|
39517
|
+
});
|
|
39518
|
+
});
|
|
39519
|
+
|
|
39520
|
+
//#endregion
|
|
39521
|
+
//#region src/cli/commands/connectors/remove.ts
|
|
39522
|
+
function mapBackendConnectors(connectors) {
|
|
39523
|
+
return connectors.filter((c$1) => isValidIntegration(c$1.integrationType)).map((c$1) => ({
|
|
39524
|
+
type: c$1.integrationType,
|
|
39525
|
+
displayName: getIntegrationDisplayName(c$1.integrationType),
|
|
39526
|
+
accountEmail: (c$1.accountInfo?.email || c$1.accountInfo?.name) ?? void 0
|
|
39527
|
+
}));
|
|
39528
|
+
}
|
|
39529
|
+
function validateConnectorType(type, connectors) {
|
|
39530
|
+
if (!isValidIntegration(type)) throw new Error(`Invalid connector type: ${type}`);
|
|
39531
|
+
const connector = connectors.find((c$1) => c$1.type === type);
|
|
39532
|
+
if (!connector) throw new Error(`No ${getIntegrationDisplayName(type)} connector found`);
|
|
39533
|
+
return connector;
|
|
39534
|
+
}
|
|
39535
|
+
async function promptForConnectorToRemove(connectors) {
|
|
39536
|
+
const selected = await ve({
|
|
39537
|
+
message: "Select a connector to remove:",
|
|
39538
|
+
options: connectors.map((c$1) => {
|
|
39539
|
+
let label = c$1.displayName;
|
|
39540
|
+
if (c$1.accountEmail) label += ` (${c$1.accountEmail})`;
|
|
39541
|
+
return {
|
|
39542
|
+
value: c$1.type,
|
|
39543
|
+
label
|
|
39544
|
+
};
|
|
39545
|
+
})
|
|
39546
|
+
});
|
|
39547
|
+
if (pD(selected)) {
|
|
39548
|
+
xe("Operation cancelled.");
|
|
39549
|
+
process.exit(0);
|
|
39550
|
+
}
|
|
39551
|
+
return connectors.find((c$1) => c$1.type === selected);
|
|
39552
|
+
}
|
|
39553
|
+
async function removeConnectorCommand(integrationType, options = {}) {
|
|
39554
|
+
const isHardDelete = options.hard === true;
|
|
39555
|
+
const connectors = mapBackendConnectors(await runTask("Fetching connectors...", async () => listConnectors(), {
|
|
39556
|
+
successMessage: "Connectors loaded",
|
|
39557
|
+
errorMessage: "Failed to fetch connectors"
|
|
39558
|
+
}));
|
|
39559
|
+
if (connectors.length === 0) return { outroMessage: "No connectors to remove" };
|
|
39560
|
+
const selectedConnector = integrationType ? validateConnectorType(integrationType, connectors) : await promptForConnectorToRemove(connectors);
|
|
39561
|
+
const displayName = selectedConnector.displayName;
|
|
39562
|
+
const accountInfo = selectedConnector.accountEmail ? ` (${selectedConnector.accountEmail})` : "";
|
|
39563
|
+
if (!options.yes) {
|
|
39564
|
+
const shouldRemove = await ye({ message: `${isHardDelete ? "Permanently remove" : "Remove"} ${displayName}${accountInfo}?` });
|
|
39565
|
+
if (pD(shouldRemove) || !shouldRemove) {
|
|
39566
|
+
xe("Operation cancelled.");
|
|
39567
|
+
process.exit(0);
|
|
39568
|
+
}
|
|
39569
|
+
}
|
|
39570
|
+
await runTask(`Removing ${displayName}...`, async () => {
|
|
39571
|
+
if (isHardDelete) await removeConnector(selectedConnector.type);
|
|
39572
|
+
else await disconnectConnector(selectedConnector.type);
|
|
39573
|
+
}, {
|
|
39574
|
+
successMessage: `${displayName} removed`,
|
|
39575
|
+
errorMessage: `Failed to remove ${displayName}`
|
|
39576
|
+
});
|
|
39577
|
+
return { outroMessage: `Successfully removed ${theme.styles.bold(displayName)}` };
|
|
39578
|
+
}
|
|
39579
|
+
const connectorsRemoveCommand = new Command("remove").argument("[type]", "Integration type to remove (e.g., slack, notion)").option("--hard", "Permanently remove the connector (cannot be undone)").option("-y, --yes", "Skip confirmation prompt").description("Remove an OAuth integration").action(async (type, options) => {
|
|
39580
|
+
await runCommand(() => removeConnectorCommand(type, options), {
|
|
39581
|
+
requireAuth: true,
|
|
39582
|
+
requireAppConfig: true
|
|
39583
|
+
});
|
|
39584
|
+
});
|
|
39585
|
+
|
|
39586
|
+
//#endregion
|
|
39587
|
+
//#region src/cli/commands/connectors/index.ts
|
|
39588
|
+
const connectorsCommand = new Command("connectors").description("Manage OAuth connectors").addCommand(connectorsAddCommand).addCommand(connectorsRemoveCommand);
|
|
39589
|
+
|
|
39238
39590
|
//#endregion
|
|
39239
39591
|
//#region package.json
|
|
39240
|
-
var version = "0.0.
|
|
39592
|
+
var version = "0.0.25";
|
|
39241
39593
|
|
|
39242
39594
|
//#endregion
|
|
39243
39595
|
//#region src/cli/program.ts
|
|
@@ -39255,6 +39607,7 @@ program.addCommand(entitiesPushCommand);
|
|
|
39255
39607
|
program.addCommand(agentsCommand);
|
|
39256
39608
|
program.addCommand(functionsDeployCommand);
|
|
39257
39609
|
program.addCommand(siteDeployCommand);
|
|
39610
|
+
program.addCommand(connectorsCommand);
|
|
39258
39611
|
|
|
39259
39612
|
//#endregion
|
|
39260
39613
|
export { CLIExitError, program };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@base44-preview/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.25-pr.104.089a20e",
|
|
4
4
|
"description": "Base44 CLI - Unified interface for managing Base44 applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"dev": "./bin/dev.js",
|
|
17
17
|
"start": "./bin/run.js",
|
|
18
18
|
"clean": "rm -rf dist",
|
|
19
|
-
"lint": "eslint src",
|
|
19
|
+
"lint": "eslint src tests",
|
|
20
20
|
"test": "vitest run",
|
|
21
21
|
"test:watch": "vitest"
|
|
22
22
|
},
|
|
@@ -52,9 +52,12 @@
|
|
|
52
52
|
"json5": "^2.2.3",
|
|
53
53
|
"ky": "^1.14.2",
|
|
54
54
|
"lodash.kebabcase": "^4.1.1",
|
|
55
|
+
"msw": "^2.12.7",
|
|
55
56
|
"open": "^11.0.0",
|
|
56
57
|
"p-wait-for": "^6.0.0",
|
|
58
|
+
"strip-ansi": "^7.1.2",
|
|
57
59
|
"tar": "^7.5.4",
|
|
60
|
+
"tmp-promise": "^3.0.3",
|
|
58
61
|
"tsdown": "^0.12.4",
|
|
59
62
|
"tsx": "^4.19.2",
|
|
60
63
|
"typescript": "^5.7.2",
|
|
@@ -64,5 +67,8 @@
|
|
|
64
67
|
},
|
|
65
68
|
"engines": {
|
|
66
69
|
"node": ">=20.19.0"
|
|
70
|
+
},
|
|
71
|
+
"optionalDependencies": {
|
|
72
|
+
"@rollup/rollup-linux-x64-gnu": "^4.56.0"
|
|
67
73
|
}
|
|
68
74
|
}
|