Skip to content

Conversation

@Paveltarno
Copy link
Collaborator

@Paveltarno Paveltarno commented Feb 4, 2026

Note

Description

This PR adds OAuth connector support to the Base44 CLI, allowing users to define connector configurations in connectors/*.jsonc files and push them to their Base44 apps. Users can configure 12 supported OAuth providers (Google Calendar, Slack, Notion, etc.) with custom scopes, and the CLI handles the complete OAuth flow with browser-based authorization.

Related Issue

Fixes #184

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Other (please describe):

Changes Made

  • Added new connector resource type following the existing Resource pattern
  • Implemented base44 connectors push command to sync local connector definitions with Base44
  • Added support for 12 OAuth providers: Google Calendar, Google Drive, Gmail, Google Sheets, Google Docs, Google Slides, Slack, Notion, Salesforce, HubSpot, LinkedIn, TikTok
  • Implemented browser-based OAuth flow with automatic tab opening and status polling
  • Added Zod schemas for connector validation with provider-specific scope documentation
  • Added comprehensive test coverage (122 lines in connectors_push.spec.ts, 506 lines in connectors.spec.ts)
  • Updated AGENTS.md documentation with new connector resource structure
  • Added API mock methods to test harness for connector endpoints
  • Created test fixtures for various scenarios (valid connectors, duplicates, invalid configs)

Testing

  • I have tested these changes locally
  • I have added/updated tests as needed
  • All tests pass (npm test)

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (if applicable)
  • My changes generate no new warnings
  • I have updated AGENTS.md if I made architectural changes

Additional Notes

The implementation follows the established Resource pattern used by entities, functions, and agents. Connectors are read from connectors/*.jsonc files, validated against provider-specific Zod schemas, and synced with the backend via the pushConnectors() function. The OAuth flow handles the complete authorization cycle including browser launch, polling for completion, and status reporting with a comprehensive summary of results.


🤖 Generated by Claude | 2026-02-11 16:25 UTC

@github-actions
Copy link
Contributor

github-actions bot commented Feb 4, 2026

🚀 Package Preview Available!


Install this PR's preview build with npm:

npm i @base44-preview/cli@0.0.31-pr.189.ff1c40d

Prefer not to change any import paths? Install using npm alias so your code still imports base44:

npm i "base44@npm:@base44-preview/cli@0.0.31-pr.189.ff1c40d"

Or add it to your package.json dependencies:

{
  "dependencies": {
    "base44": "npm:@base44-preview/cli@0.0.31-pr.189.ff1c40d"
  }
}

Preview published to npm registry — try new features instantly!

@kfirstri kfirstri moved this from Backlog to In progress in CLI Development Feb 5, 2026
@Paveltarno Paveltarno force-pushed the pavelta-connectors-1 branch from c3543de to c1bb2d1 Compare February 8, 2026 15:53
@Paveltarno Paveltarno changed the title feat(connectors): add OAuth connector resource schemas and file parsing feat(connectors): add OAuth connector resource Feb 9, 2026
@base44 base44 deleted a comment from claude bot Feb 10, 2026
@claude
Copy link

claude bot commented Feb 10, 2026


Code review

No issues found. Checked for bugs and CLAUDE.md compliance.


"hubspot",
"linkedin",
"tiktok",
] as const;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be annoying that we have to create a new connectorSchema, update the ConnectorResourceSchema and add the name to KnownIntegrationTypes, Maybe we can enforce this somehow with typescript.. right now the KnownIntegrationTypes is not enforced, meaning there can be typos / missing stuff from this list which will cause issues

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I agree, talked with Neta about this, we should start syncing the server schema into the CLI but this will happen at a later stage, for now this is mainly a hint for the agent

error: z.string().nullable().optional(),
error_message: z.string().nullable().optional(),
other_user_email: z.string().nullable().optional(),
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use .tranform to transform the response the camelCase (there's examples for this in other resources)


export type RemoveConnectorResponse = z.infer<
typeof RemoveConnectorResponseSchema
>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

related to the entire file: I know we have this issue in many places - can we not export stuff that are not used anywhere? Go over this file and just check what is actually used and export only that

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed, how about we add Knip?


let finalStatus: ConnectorOAuthStatus = "PENDING";

await pWaitFor(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

polling should not happen in the core/ folder, Core/ should only expose api / schema/ config stuff.
Polling and using pWaitFor should happen on the CLI side - you can see an example in login-flow.ts

return result.data;
}

export async function setConnector(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe we can give this a better name? enableConnector? updateConnector? i feel set is not clear to me

connector.type,
connector.scopes ?? []
);
results.push(setResponseToResult(connector.type, response));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's because i don't like "setConnector" name, but this name is also not clear to me - maybe something like "getConnectorSyncResult"?

const upstream = await listConnectors();
const localTypes = new Set(connectors.map((c) => c.type));

for (const connector of connectors) {
Copy link
Collaborator

@kfirstri kfirstri Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think basic flow comments / splitting into function might help in this function.

// 1. Update local connector to remote
// 2. Delete remote connector that are not in local project

Comment on lines +45 to +60
const types = new Set<string>();
for (const connector of connectors) {
if (types.has(connector.type)) {
throw new InvalidInputError(
`Duplicate connector type "${connector.type}"`,
{
hints: [
{
message: `Remove duplicate connectors with type "${connector.type}" - only one connector per type is allowed`,
},
],
}
);
}
types.add(connector.type);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extract to another function called something like assertNoDuplicateConnectors

export function getConnectorsPushCommand(context: CLIContext): Command {
return new Command("push")
.description(
"Push local connectors to Base44 (syncs scopes and removes unlisted)"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this description is really clear, "removed unlisted"?

const needsOAuth = results.filter(isPendingOAuth);
let outroMessage = "Connectors pushed to Base44";

if (needsOAuth.length > 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we flip and exit early?

if (needsOauth.length === 0) {
    return { outroMessage };
}

let outroMessage = "Connectors pushed to Base44";

if (needsOAuth.length > 0) {
log.info("");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we really need an extra space?

log.info(`\nOpening browser for ${connector.type}...`);

const oauthResult = await runTask(
`Waiting for ${connector.type} authorization...`,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to add some ticks on the name of the connector

Suggested change
`Waiting for ${connector.type} authorization...`,
`Waiting for '${connector.type}' authorization...`,

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same for the earlier log

const oauthResult = await runTask(
`Waiting for ${connector.type} authorization...`,
async () => {
return await runOAuthFlow({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrote it inside this function but i will mention it here too - the pwaitFor and polling logic should be implemented here, and just call the correct API from the core folder.

successMessage: `${connector.type} authorization complete`,
errorMessage: `${connector.type} authorization failed`,
}
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider adding try/catch - if one fails, do we want to entire process to fail or just keep going? i think keep going makes sense..

Copy link
Collaborator

@kfirstri kfirstri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some comments, main issue i got is the pWaitFor call - it should be implemented inside CLI and not in core (like login-flow.ts)

Paveltarno and others added 3 commits February 11, 2026 18:24
Add connector resource module supporting 12 OAuth providers:
googlecalendar, googledrive, gmail, googlesheets, googledocs,
googleslides, slack, notion, salesforce, hubspot, linkedin, tiktok.

- Zod discriminated union schema with type discriminator per provider
- JSDoc links to official OAuth scope documentation for each provider
- JSONC file reading with validation (filename must match type field)
- API response schemas for upstream connector state
- Unit tests with fixtures for valid, invalid, and mismatched connectors

Part of: #184

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add API client methods for OAuth connector operations:
- listConnectors: list all connectors for current app
- syncConnector: sync connector with exact scope matching
- getOAuthStatus: poll OAuth authorization status
- removeConnector: remove a connector integration

Also update response schemas and clean up verbose comments.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat(connectors): implement push logic for syncing connectors

Add pushConnectors function that:
- Syncs all local connectors via /sync endpoint
- Removes upstream-only connectors not in local config
- Returns typed results (synced, removed, needs_oauth, error)

Includes unit tests covering all scenarios.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(connectors): add OAuth flow handling with browser redirect and polling (#192)

* feat(connectors): add OAuth flow handling with browser redirect and polling

Add runOAuthFlow function that:
- Opens OAuth redirect URL in browser
- Polls getOAuthStatus until ACTIVE or FAILED
- Returns PENDING on timeout (5 minutes)

Uses p-wait-for TimeoutError for robust timeout detection.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* connectors: base44 connectors push (#194)

* final connector work sofi 1

* scopes

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Paveltarno and others added 10 commits February 11, 2026 18:24
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Change provider field from closed enum to flexible union that accepts both
known providers (googlecalendar, notion, slack, etc.) and any arbitrary
provider string. This enables users to configure custom OAuth providers
without waiting for first-class Base44 support.

Schema changes:
- Add GenericConnectorSchema for arbitrary provider types
- Update ConnectorResourceSchema to union of specific + generic schemas
- Update IntegrationTypeSchema to accept known enum OR any non-empty string
- Only reject empty strings

Test coverage:
- Verify known providers continue to work
- Verify arbitrary providers are accepted
- Verify empty strings are rejected
- All 137 tests passing

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@claude
Copy link

claude bot commented Feb 11, 2026

README check ran. 7 issue(s) found and applied: (1) Added missing eject command, (2) Updated create description to match code, (3) Updated deploy description to match code, (4) Updated link description to match code, (5) Updated logout description to match code, (6) Updated whoami description to match code, (7) Added missing connectors push command. README.md has been updated in this branch.

@claude
Copy link

claude bot commented Feb 11, 2026

Note: The README fix commit (1bffec2) was created but could not be pushed due to detached HEAD state in the CI environment. To apply the fix, either: (1) Fetch and cherry-pick commit 1bffec2, or (2) Apply the changes shown in the diff above manually. The required changes are: add eject and connectors push commands, update descriptions for create, deploy, link, logout, whoami, agents pull/push, and entities push to match the code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Ready

Development

Successfully merging this pull request may close these issues.

Add OAuth connectors as a CLI resource

2 participants