martmull
f20d04eb6e
feat(app-dev): surface metadata diff in dev sync and name failing migration actions ( #21249 )
...
Split out of #21240 .
- Render the applied metadata changes (created/updated/deleted +
identifiers) in the dev sync output instead of a bare `✓ Synced`.
- Include the failing entity's `universalIdentifier` in
`WorkspaceMigrationRunnerException` messages so conflicts are
diagnosable.
<img width="637" height="114" alt="image"
src="https://github.com/user-attachments/assets/61422a16-370c-4e9b-a2f6-c29ce17f3b1b "
/>
<img width="497" height="104" alt="image"
src="https://github.com/user-attachments/assets/d493c398-da29-49c9-ac5e-aa0f26cd7389 "
/>
<img width="593" height="127" alt="image"
src="https://github.com/user-attachments/assets/15e26edc-c0e4-4427-bd34-909040e970c9 "
/>
---------
Co-authored-by: Charles Bochet <charles@twenty.com >
2026-06-05 16:35:50 +02:00
nitin
59d69e2f5c
fix(sdk): link dev UI to workspace application detail page ( #20849 )
...
sdk handle auth of one workspace per session -- but server could be
configured as multi or single -- hence for multi get subdomain -- and
for single the localhost fallback!
also: link includes applicationId so it opens the app detail page
directly (not the list)
## QA
multi workspace flag on -
<img width="2996" height="1712" alt="CleanShot 2026-05-22 at 18 21
31@2x"
src="https://github.com/user-attachments/assets/8499b9f3-b22e-45e2-8b97-4b27fadc3c94 "
/>
multi workspace flag off -
<img width="3012" height="1734" alt="CleanShot 2026-05-22 at 18 14
37@2x"
src="https://github.com/user-attachments/assets/3af2f492-5e2d-4a4b-8251-c3343d79ae9e "
/>
2026-05-22 14:19:27 +00:00
martmull
9a7f50fcb7
Fix breaking change in install app command ( #20825 )
...
add backward compatibility for twenty-sdk install command
2026-05-21 20:58:34 +00:00
martmull
2963fa9324
Navigate to installed page after app install ( #20797 )
...
as title
2026-05-21 11:19:18 +00:00
martmull
237a943947
Update twenty sdk commands ( #20735 )
...
Performs twenty-sdk cli command migration:
Summary
``` ┌─────┬──────────────────────────┬────────────────────────────┬───────────────────────┐
│ # │ Old command │ New command │ Status │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 1 │ twenty dev [appPath] │ twenty dev [appPath] │ Unchanged (now also │
│ │ │ │ DEFAULT) │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 2 │ twenty dev --once │ twenty dev --once │ Unchanged │
│ │ [appPath] │ [appPath] │ │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 3 │ twenty dev --watch │ twenty dev [appPath] │ --watch flag removed │
│ │ [appPath] │ │ (was default) │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 4 │ twenty dev --verbose │ twenty dev --verbose │ Unchanged │
│ │ [appPath] │ [appPath] │ │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 5 │ twenty dev --debug │ twenty dev --debug │ Unchanged │
│ │ [appPath] │ [appPath] │ │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 6 │ twenty dev --debounceMs │ twenty dev --debounceMs │ Unchanged │
│ │ <ms> [appPath] │ <ms> [appPath] │ │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 7 │ twenty build [appPath] │ twenty dev:build [appPath] │ Deprecated → colon │
│ │ │ │ command │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 8 │ twenty build --tarball │ twenty dev:build --tarball │ Deprecated → colon │
│ │ [appPath] │ [appPath] │ command │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 9 │ twenty typecheck │ twenty dev:typecheck │ Deprecated → colon │
│ │ [appPath] │ [appPath] │ command │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 10 │ twenty logs [appPath] │ twenty dev:fn-logs │ Deprecated → colon │
│ │ │ [appPath] │ command │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 11 │ twenty logs -n <name> │ twenty dev:fn-logs -n │ Deprecated → colon │
│ │ [appPath] │ <name> [appPath] │ command │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 12 │ twenty logs -u <id> │ twenty dev:fn-logs -u <id> │ Deprecated → colon │
│ │ [appPath] │ [appPath] │ command │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 13 │ twenty exec [appPath] │ twenty dev:fn-exec │ Deprecated → colon │
│ │ │ [appPath] │ command │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 14 │ twenty exec -n <name> │ twenty dev:fn-exec -n │ Deprecated → colon │
│ │ [appPath] │ <name> [appPath] │ command │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 15 │ twenty exec -u <id> │ twenty dev:fn-exec -u <id> │ Deprecated → colon │
│ │ [appPath] │ [appPath] │ command │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 16 │ twenty exec -p <json> │ twenty dev:fn-exec -p │ Deprecated → colon │
│ │ [appPath] │ <json> [appPath] │ command │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 17 │ twenty exec │ twenty dev:fn-exec │ Deprecated → colon │
│ │ --postInstall [appPath] │ --postInstall [appPath] │ command │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 18 │ twenty exec --preInstall │ twenty dev:fn-exec │ Deprecated → colon │
│ │ [appPath] │ --preInstall [appPath] │ command │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 19 │ twenty add [entityType] │ twenty dev:add │ Deprecated → colon │
│ │ │ [entityType] │ command │
├─────┼──────────────────────────┼────────────────────────────┼───────────────────────┤
│ 20 │ twenty add --path <path> │ twenty dev:add --path │ Deprecated → colon │
│ │ [entityType] │ <path> [entityType] │ command │
└─────┴──────────────────────────┴────────────────────────────┴───────────────────────┘
App lifecycle commands
┌─────┬────────────────────────┬────────────────────────────┬─────────────────────────┐
│ # │ Old command │ New command │ Status │
├─────┼────────────────────────┼────────────────────────────┼─────────────────────────┤
│ 21 │ twenty publish │ twenty app:publish │ Deprecated → colon │
│ │ [appPath] │ [appPath] │ command │
├─────┼────────────────────────┼────────────────────────────┼─────────────────────────┤
│ 22 │ twenty publish --tag │ twenty app:publish --tag │ Deprecated → colon │
│ │ <tag> [appPath] │ <tag> [appPath] │ command │
├─────┼────────────────────────┼────────────────────────────┼─────────────────────────┤
│ 23 │ twenty deploy │ twenty app:publish │ Deprecated → colon │
│ │ [appPath] │ --private [appPath] │ command + --private │
├─────┼────────────────────────┼────────────────────────────┼─────────────────────────┤
│ 24 │ twenty install │ twenty app:install │ Deprecated → colon │
│ │ [appPath] │ [appPath] │ command │
├─────┼────────────────────────┼────────────────────────────┼─────────────────────────┤
│ 25 │ twenty uninstall │ twenty app:uninstall │ Deprecated → colon │
│ │ [appPath] │ [appPath] │ command │
├─────┼────────────────────────┼────────────────────────────┼─────────────────────────┤
│ 26 │ twenty uninstall -y │ twenty app:uninstall -y │ Deprecated → colon │
│ │ [appPath] │ [appPath] │ command │
└─────┴────────────────────────┴────────────────────────────┴─────────────────────────┘
Server commands
┌─────┬─────────────────────────┬─────────────────────────────┬──────────────────────┐
│ # │ Old command │ New command │ Status │
├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
│ 27 │ twenty server start │ twenty docker:start │ Deprecated → colon │
│ │ │ │ syntax │
├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
│ 28 │ twenty server start -p │ twenty docker:start -p │ Deprecated → colon │
│ │ <port> │ <port> │ syntax │
├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
│ 29 │ twenty server start │ twenty docker:start --test │ Deprecated → colon │
│ │ --test │ │ syntax │
├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
│ 30 │ twenty server stop │ twenty docker:stop │ Deprecated → colon │
│ │ │ │ syntax │
├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
│ 31 │ twenty server stop │ twenty docker:stop --test │ Deprecated → colon │
│ │ --test │ │ syntax │
├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
│ 32 │ twenty server status │ twenty docker:status │ Deprecated → colon │
│ │ │ │ syntax │
├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
│ 33 │ twenty server status │ twenty docker:status --test │ Deprecated → colon │
│ │ --test │ │ syntax │
├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
│ 34 │ twenty server logs │ twenty docker:logs │ Deprecated → colon │
│ │ │ │ syntax │
├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
│ 35 │ twenty server logs -n │ twenty docker:logs -n │ Deprecated → colon │
│ │ <lines> │ <lines> │ syntax │
├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
│ 36 │ twenty server logs │ twenty docker:logs --test │ Deprecated → colon │
│ │ --test │ │ syntax │
├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
│ 37 │ twenty server reset │ twenty docker:reset │ Deprecated → colon │
│ │ │ │ syntax │
├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
│ 38 │ twenty server reset │ twenty docker:reset --test │ Deprecated → colon │
│ │ --test │ │ syntax │
├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
│ 39 │ twenty server upgrade │ twenty docker:upgrade │ Deprecated → colon │
│ │ [version] │ [version] │ syntax │
├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
│ 40 │ twenty server upgrade │ twenty docker:upgrade │ Deprecated → colon │
│ │ --test [version] │ --test [version] │ syntax │
├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
│ 41 │ twenty server │ twenty app:catalog-sync │ Deprecated → colon │
│ │ catalog-sync │ │ syntax │
├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
│ 42 │ twenty server │ twenty app:catalog-sync │ Deprecated → colon │
│ │ catalog-sync -r <name> │ -r <name> │ syntax │
├─────┼─────────────────────────┼─────────────────────────────┼──────────────────────┤
│ 43 │ twenty catalog-sync │ (removed) │ Removed (was already │
│ │ │ │ deprecated) │
└─────┴─────────────────────────┴─────────────────────────────┴──────────────────────┘
Remote commands
┌─────┬────────────────────────┬──────────────────────────┬──────────────────────────┐
│ # │ Old command │ New command │ Status │
├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
│ 44 │ twenty remote add │ twenty remote:add │ Deprecated → colon │
│ │ │ │ syntax │
├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
│ 45 │ twenty remote add --as │ twenty remote:add --as │ Deprecated → colon │
│ │ <name> │ <name> │ syntax │
├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
│ 46 │ twenty remote add │ twenty remote:add │ Deprecated → colon │
│ │ --api-key <key> │ --api-key <key> │ syntax │
├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
│ 47 │ twenty remote add │ twenty remote:add │ Deprecated → colon │
│ │ --api-url <url> │ --api-url <url> │ syntax │
├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
│ 48 │ twenty remote add │ twenty remote:add │ Deprecated → colon │
│ │ --local │ --local │ syntax │
├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
│ 49 │ twenty remote add │ twenty remote:add --test │ Deprecated → colon │
│ │ --test │ │ syntax │
├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
│ 50 │ twenty remote list │ twenty remote:list │ Deprecated → colon │
│ │ │ │ syntax │
├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
│ 51 │ twenty remote switch │ twenty remote:use [name] │ Deprecated → colon │
│ │ [name] │ │ syntax + renamed │
├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
│ 52 │ twenty remote status │ twenty remote:status │ Deprecated → colon │
│ │ │ │ syntax │
├─────┼────────────────────────┼──────────────────────────┼──────────────────────────┤
│ 53 │ twenty remote remove │ twenty remote:remove │ Deprecated → colon │
│ │ <name> │ <name> │ syntax │
└─────┴────────────────────────┴──────────────────────────┴──────────────────────────┘
```
---------
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-05-20 15:12:39 +00:00
Charles Bochet
76ea0f37ed
Surface structured validation errors during application install ( #19787 )
...
## Summary
- Add `WorkspaceMigrationGraphqlApiExceptionInterceptor` to
`MarketplaceResolver` and `ApplicationInstallResolver` so validation
failures during app install return `METADATA_VALIDATION_FAILED` with
structured `extensions.errors` instead of generic
`INTERNAL_SERVER_ERROR`
- Update SDK `installTarballApp()` to pass the full GraphQL error object
(including extensions) through the install flow
- Add `formatInstallValidationErrors` utility to format structured
validation errors for CLI output
- Add integration test verifying structured error responses for invalid
navigation menu items and view fields
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-17 11:29:33 +00:00
Charles Bochet
53065f241f
Exchange clientSecret for tokens after app registration + bump canary ( #19582 )
...
## Summary
- **Fix `createApplicationRegistration` flow**: The server's
`createApplicationRegistration` mutation returns a `clientSecret`, not
`accessToken`/`refreshToken` directly. The SDK now correctly requests
`clientSecret` and immediately performs an OAuth `client_credentials`
exchange to obtain `appAccessToken` and `appRefreshToken`, then stores
them in config.
- **New `exchangeCredentialsForTokens` helper**: Shared by both `dev`
and `dev --once` flows. Takes `clientId` + `clientSecret`, calls
`/oauth/token` with `client_credentials` grant, and persists the
resulting tokens.
- **Bump `twenty-sdk`, `twenty-client-sdk`, `create-twenty-app` to
`1.22.0-canary.2`**
## Context
The `1.22.0-canary.1` SDK release expected
`createApplicationRegistration` to return `accessToken`/`refreshToken`
directly, but the `v1.22.0` server returns `clientSecret`. This caused
`yarn twenty dev` and `yarn twenty dev --once` to fail with "No
registration found" errors.
2026-04-11 12:26:18 +02:00
Charles Bochet
c26c0b9d71
Use app's own OAuth credentials for CoreApiClient generation ( #19563 )
...
## Summary
- **SDK (`dev` & `dev --once`)**: After app registration, the CLI now
obtains an `APPLICATION_ACCESS` token via `client_credentials` grant
using the app's own `clientId`/`clientSecret`, and uses that token for
CoreApiClient schema introspection — instead of the user's
`config.accessToken` which returns the full unscoped schema.
- **Config**: `oauthClientSecret` is now persisted alongside
`oauthClientId` in `~/.twenty/config.json` when creating a new app
registration, so subsequent `dev`/`dev --once` runs can obtain fresh app
tokens without re-registration.
- **CI action**: `spawn-twenty-app-dev-test` now outputs a proper
`API_KEY` JWT (signed with the seeded dev workspace secret) instead of
the previous hardcoded `ACCESS` token — giving consumers a real API key
rather than a user session token.
## Motivation
When developing Twenty apps, `yarn twenty dev` was using the CLI user's
OAuth token for GraphQL schema introspection during CoreApiClient
generation. This token (type `ACCESS`) has no `applicationId` claim, so
the server returns the **full workspace schema** — including all objects
— rather than the scoped schema the app should see at runtime (filtered
by `applicationId`).
This caused a discrepancy: the generated CoreApiClient contained fields
the app couldn't actually query at runtime with its `APPLICATION_ACCESS`
token.
By switching to `client_credentials` grant, the SDK now introspects with
the same token type the app will use in production, ensuring the
generated client accurately reflects the app's runtime capabilities.
2026-04-11 11:24:28 +02:00
Charles Bochet
15eb3e7edc
feat(sdk): use config file as single source of truth, remove env var fallbacks ( #19409 )
...
## Summary
- **Config as source of truth**: `~/.twenty/config.json` is now the
single source of truth for SDK authentication — env var fallbacks have
been removed from the config resolution chain.
- **Test instance support**: `twenty server start --test` spins up a
dedicated Docker instance on port 2021 with its own config
(`config.test.json`), so integration tests don't interfere with the dev
environment.
- **API key auth for marketplace**: Removed `UserAuthGuard` from
`MarketplaceResolver` so API key tokens (workspace-scoped) can call
`installMarketplaceApp`.
- **CI for example apps**: Added monorepo CI workflows for `hello-world`
and `postcard` example apps to catch regressions.
- **Simplified CI**: All `ci-create-app-e2e` and example app workflows
now use a shared `spawn-twenty-app-dev-test` action (Docker-based)
instead of building the server from source. Consolidated auth env vars
to `TWENTY_API_URL` + `TWENTY_API_KEY`.
- **Template publishing fix**: `create-twenty-app` template now
correctly preserves `.github/` and `.gitignore` through npm publish
(stored without leading dot, renamed after copy).
## Test plan
- [x] CI SDK (lint, typecheck, unit, integration, e2e) — all green
- [x] CI Example App Hello World — green
- [x] CI Example App Postcard — green
- [x] CI Create App E2E minimal — green
- [x] CI Front, CI Server, CI Shared — green
2026-04-08 06:49:10 +02:00
martmull
8985dfbc5d
Improve apps ( #19120 )
...
fixes
https://discord.com/channels/1130383047699738754/1488094970241089586
2026-03-30 15:03:23 +00:00
martmull
fe1377f18b
Provide applicatiion assets ( #18973 )
...
- improve backend
- improve frontend
<img width="1293" height="824" alt="image"
src="https://github.com/user-attachments/assets/7a4633f1-85cd-4126-b058-dbeae6ba2218 "
/>
2026-03-30 10:53:31 +02:00
Etienne
9698996771
fix useless auth in sdk gql codegen command ( #18805 )
2026-03-20 17:22:41 +01:00
martmull
731e297147
Twenty sdk cli oauth ( #18638 )
...
<img width="1418" height="804" alt="image"
src="https://github.com/user-attachments/assets/de6c8222-6496-4a71-bc21-7e5e1269d5cb "
/>
---------
Co-authored-by: Félix Malfait <felix.malfait@gmail.com >
Co-authored-by: Félix Malfait <felix@twenty.com >
2026-03-17 11:43:17 +01:00
Félix Malfait
66d93c4d28
Fix app:dev CLI by removing deleted createOneApplication mutation ( #18460 )
...
## Summary
- The `createOneApplication` GraphQL mutation was removed from the
server during the application architecture refactor (#18432 ), but the
SDK CLI (`app:dev`, `app:build --sync`) still called it, causing
failures.
- Simplified the SDK to use `syncApplication` (which now internally
creates the `ApplicationEntity` via `ensureApplicationExists`) instead
of a separate create step.
- On first run (clean install), the orchestrator now runs an initial
sync before initializing the file uploader, so file uploads can proceed
(they require the `ApplicationEntity` to exist).
## Test plan
- [x] Typecheck passes for both `twenty-sdk` and `twenty-server`
- [x] `app:dev` tested locally with existing app (finds app, uploads,
syncs)
- [x] `app:dev` tested locally after `app:uninstall` (creates app via
sync, uploads, syncs)
- [x] SDK unit tests pass (23/26 files, 3 pre-existing failures
unrelated)
Made with [Cursor](https://cursor.com )
2026-03-06 18:37:54 +01:00
Félix Malfait
0e89c96170
feat: add npm and tarball app distribution with upgrade mechanism ( #18358 )
...
## Summary
- **npm + tarball app distribution**: Apps can be installed from the npm
registry (public or private) or uploaded as `.tar.gz` tarballs, with
`AppRegistrationSourceType` tracking the origin
- **Upgrade mechanism**: `AppUpgradeService` checks for newer versions,
supports rollback for npm-sourced apps, and a cron job runs every 6
hours to update `latestAvailableVersion` on registrations
- **Security hardening**: Tarball extraction uses path traversal
protection, and `enableScripts: false` in `.yarnrc.yml` disables all
lifecycle scripts during `yarn install` to prevent RCE
- **Frontend**: "Install from npm" and "Upload tarball" modals, upgrade
button on app detail page, blue "Update" badge on installed apps table
when a newer version is available
- **Marketplace catalog sync**: Hourly cron job syncs a hardcoded
catalog index into `ApplicationRegistration` entities
- **Integration tests**: Coverage for install, upgrade, tarball upload,
and catalog sync flows
## Backend changes
| Area | Files |
|------|-------|
| Entity & migration | `ApplicationRegistrationEntity` (sourceType,
sourcePackage, latestAvailableVersion), `ApplicationEntity`
(applicationRegistrationId), migration |
| Services | `AppPackageResolverService`, `ApplicationInstallService`,
`AppUpgradeService`, `MarketplaceCatalogSyncService` |
| Cron jobs | `MarketplaceCatalogSyncCronJob` (hourly),
`AppVersionCheckCronJob` (every 6h) |
| REST endpoint | `AppRegistrationUploadController` — tarball upload
with secure extraction |
| Resolver | `MarketplaceResolver` — simplified `installMarketplaceApp`
(removed redundant `sourcePackage` arg) |
| Security | `.yarnrc.yml` — `enableScripts: false` to block postinstall
RCE |
## Frontend changes
| Area | Files |
|------|-------|
| Modals | `SettingsInstallNpmAppModal`, `SettingsUploadTarballModal`,
`SettingsAppModalLayout` |
| Hooks | `useUploadAppTarball`, `useInstallMarketplaceApp` (cleaned up)
|
| Upgrade UI | `SettingsApplicationVersionContainer`,
`SettingsApplicationDetailAboutTab` |
| Badge | `SettingsApplicationTableRow` — blue "Update" tag,
`SettingsApplicationsInstalledTab` — fetches registrations for version
comparison |
| Styling | Migrated to Linaria (matching main) |
## Test plan
- [ ] Install an app from npm via the "Install from npm" modal
- [ ] Upload a `.tar.gz` tarball via the "Upload tarball" modal
- [ ] Verify upgrade badge appears when `latestAvailableVersion >
version`
- [ ] Verify upgrade flow from app detail page
- [ ] Run integration tests: `app-distribution.integration-spec.ts`,
`marketplace-catalog-sync.integration-spec.ts`
- [ ] Verify `enableScripts: false` blocks postinstall scripts during
yarn install
Made with [Cursor](https://cursor.com )
2026-03-05 10:34:08 +01:00
Félix Malfait
012d819557
OAuth Client — Unified ApplicationRegistration, OAuth server, and frontend ( #18267 )
...
## Summary
Consolidates three separate PRs (#18260 , #18261 , #18262 ) into a single
unified branch with all review feedback addressed:
### New features
- **ApplicationRegistration entity** — server-level registration for
OAuth apps with encrypted server variables
- **OAuth 2.0 server** — authorization code, client credentials, refresh
token grants with PKCE support
- **OAuth discovery endpoint** —
`.well-known/oauth-authorization-server` metadata
- **Frontend UI** — app registration details page with credential
management, redirect URI editing, and server variable configuration
- **CLI integration** — `twenty dev` auto-registers apps and stores
OAuth credentials locally
- **Authorize consent screen** — OAuth consent page at `/authorize`
showing requested scopes
### Review feedback addressed
**Renames (PR #18260 ):**
- `appRegistration` → `applicationRegistration` (entity, tables, files,
imports, GraphQL types)
- `appRegistrationVariable` → `applicationRegistrationVariable`
- `clientId` → `oAuthClientId`, `clientSecretHash` →
`oAuthClientSecretHash`, `redirectUris` → `oAuthRedirectUris`, `scopes`
→ `oAuthScopes`
**Security fixes (PR #18261 ):**
- Fixed redirect URI validation bypass when `oAuthRedirectUris` is an
empty array
- Fixed workspace isolation in `clientCredentialsGrant` — now uses
`find()` with explicit handling for multiple installations
- Added error logging in refresh token `catch` block instead of silently
swallowing
**Code quality (PR #18262 ):**
- Split `VersionDistributionEntry` into its own file (one export per
file)
- Split GraphQL queries and mutations into individual files with a
shared fragment
- Removed unused `OAuth` entry from `AuthProviderEnum`
- Added loading state to `handleRotateSecret`
- Removed 27 narration-style comments from test files
- Added proper guards (`PublicEndpointGuard`, `NoPermissionGuard`) to
controllers and resolvers
## Test plan
- [ ] Verify `twenty dev` registers an app and stores OAuth credentials
- [ ] Test OAuth authorization code flow end-to-end (authorize → token →
API call)
- [ ] Test client credentials grant
- [ ] Verify redirect URI validation rejects requests when no URIs are
registered
- [ ] Verify app registration detail page renders correctly
- [ ] Test secret rotation with loading state
- [ ] Verify server variable editing and saving
- [ ] Run `npx nx database:reset twenty-server` to validate migration
Closes #18260 , #18261 , #18262
Made with [Cursor](https://cursor.com )
---------
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
2026-02-28 14:07:49 +01:00
Weiko
0af980a783
introduce metadata api client to twenty sdk ( #18233 )
...
Logic function: hello-world.ts
```typescript
import { CoreApiClient } from 'twenty-sdk/generated/core';
import { MetadataApiClient } from 'twenty-sdk/generated/metadata';
const handler = async () => {
const coreClient = new CoreApiClient();
const metadataClient = new MetadataApiClient();
// Query the core /graphql endpoint — fetch some people
const coreResult = await coreClient.query({
people: {
edges: {
node: {
id: true,
name: {
firstName: true,
lastName: true,
},
},
},
},
});
// Query the metadata /metadata endpoint — fetch current workspace
const metadataResult = await metadataClient.query({
currentWorkspace: {
id: true,
displayName: true,
},
});
return {
coreResponse: coreResult,
metadataResponse: metadataResult,
};
};
```
With route trigger should now produce:
<img width="582" height="238" alt="Screenshot 2026-02-25 at 17 14 29"
src="https://github.com/user-attachments/assets/8c597113-7552-4d32-845a-352083d84ac7 "
/>
```json
{
"coreResponse": {
"people": {
"edges": [
{
"node": {
"id": "20202020-b000-4485-94de-70c2a98daef2",
"name": {
"firstName": "Jeffery",
"lastName": "Griffin"
}
}
},
{
"node": {
"id": "20202020-b003-415a-9051-133248495f7f",
"name": {
"firstName": "Terry",
"lastName": "Melendez"
}
}
},
{
"node": {
"id": "20202020-b00e-4bc1-87c8-00aeb49c10f8",
"name": {
"firstName": "Lee",
"lastName": "Jones"
}
}
},
{
"node": {
"id": "20202020-b012-44c1-9fdc-90f110962d07",
"name": {
"firstName": "Sarah",
"lastName": "Hernandez"
}
}
},
]
}
},
...
"metadataResponse": {
"currentWorkspace": {
"id": "20202020-1c25-4d02-bf25-6aeccf7ea419",
"displayName": "Apple"
}
}
}
2026-02-25 21:42:05 +01:00
martmull
b84a8588be
Fix function logs ( #18215 )
...
as title
2026-02-25 10:36:19 +01:00
Charles Bochet
0d4fe4575b
Various SDK improvements ( #18115 )
...
## Summary
- **Refactor frontend metadata loading architecture**: Split the
monolithic `EagerMetadataLoadEffect` into focused provider effects
(`UserMetadataProviderEffect`, `ObjectMetadataProviderEffect`,
`ViewMetadataProviderEffect`) orchestrated by `MetadataProviderEffects`.
Replaced `UserProvider` + `ObjectMetadataItemsProvider` with a single
`MetadataGater` that gates rendering on `isAppMetadataReadyState`. The
metadata store now validates view-object consistency before promoting
views, and `updateDraft` skips no-op updates via deep equality checks.
- **SDK CLI improvements**: Added `app:typecheck` command, improved
error handling in API sync (extracts GraphQL error messages), added
`serializeError` utility for human-readable error output, added `error`
file status to dev mode orchestrator with UI support, and fixed
ClickHouse migration/seed commands to use `transpile-only`.
2026-02-23 19:57:02 +01:00
Charles Bochet
c0cc0689d6
Add Client Api generation ( #17961 )
...
## Add API client generation to SDK dev mode and refactor orchestrator
into step-based pipeline
### Why
The SDK dev mode lacked typed API client generation, forcing developers
to work without auto-generated GraphQL types when building applications.
Additionally, the orchestrator was a monolithic class that mixed watcher
management, token handling, and sync logic — making it difficult to
extend with new steps like client generation.
### How
- **Refactored the orchestrator** into a step-based pipeline with
dedicated classes: `CheckServer`, `EnsureValidTokens`,
`ResolveApplication`, `BuildManifest`, `UploadFiles`,
`GenerateApiClient`, `SyncApplication`, and `StartWatchers`. Each step
has typed input/output/status, managed by a new `OrchestratorState`
class.
- **Added `GenerateApiClientOrchestratorStep`** that detects
object/field schema changes and regenerates a typed GraphQL client (via
`@genql/cli`) into `node_modules/twenty-sdk/generated` for seamless
imports.
- **Replaced `checkApplicationExist`** with `findOneApplication` on both
server resolver and SDK API service, returning the entity data instead
of a boolean.
- **Added application token pair mutations**
(`generateApplicationToken`, `renewApplicationToken`) to the API
service, with the server now returning `ApplicationTokenPairDTO`
containing both access and refresh tokens.
- **Restructured the dev UI** into `dev/ui/components/` with dedicated
panel, section, and event log components.
- **Simplified `AppDevCommand`** from ~180 lines of watcher management
down to ~40 lines that delegate entirely to the orchestrator.
2026-02-17 18:45:52 +01:00
martmull
c3d565f266
Add default fields on object manifest ( #17977 )
...
as title
<img width="830" height="227" alt="image"
src="https://github.com/user-attachments/assets/8cbf4a14-5d1d-496f-a146-f31079e6e602 "
/>
2026-02-16 16:50:46 +01:00
martmull
a4ed043d43
Logic function refactorization ( #17861 )
...
As title
2026-02-12 11:40:49 +01:00
martmull
9162685b2e
Reorganize logic function files ( #17766 )
...
reorganize according to
<img width="1243" height="725" alt="Pasted Graphic"
src="https://github.com/user-attachments/assets/ba65dd10-8eec-4b13-ad49-9726edd3b79c "
/>
Not working yet
2026-02-09 12:36:39 +01:00
martmull
65678ed99f
Upload files instead ofsources ( #17608 )
2026-02-03 12:11:10 +01:00
martmull
f46da3eefd
Update manifest structure ( #17547 )
...
Move all sync entities in an `entities` key. Rename functions to
logicFunctions
```json
{
application: {
...
},
entities: {
objects: [],
logicFunctions: [],
...
}
}
```
2026-01-30 16:26:45 +01:00
martmull
3412992e99
2162 Add asset watcher in twenty-sdk dev mode ( #17513 )
...
- assets are pushed in .twenty/output
- assets are uploaded in FileFolder.Assets
- not handled yet by the sync-manifest endpoint
2026-01-29 09:08:44 +00:00
Charles Bochet
da6f1bbef3
Rename serverlessFunction to logicFunction ( #17494 )
...
## Summary
Rename "Serverless Function" to "Logic Function" across the codebase for
clearer naming.
### Environment Variable Changes
| Old | New |
|-----|-----|
| `SERVERLESS_TYPE` | `LOGIC_FUNCTION_TYPE` |
| `SERVERLESS_LAMBDA_REGION` | `LOGIC_FUNCTION_LAMBDA_REGION` |
| `SERVERLESS_LAMBDA_ROLE` | `LOGIC_FUNCTION_LAMBDA_ROLE` |
| `SERVERLESS_LAMBDA_SUBHOSTING_URL` |
`LOGIC_FUNCTION_LAMBDA_SUBHOSTING_URL` |
| `SERVERLESS_LAMBDA_ACCESS_KEY_ID` |
`LOGIC_FUNCTION_LAMBDA_ACCESS_KEY_ID` |
| `SERVERLESS_LAMBDA_SECRET_ACCESS_KEY` |
`LOGIC_FUNCTION_LAMBDA_SECRET_ACCESS_KEY` |
### Breaking Changes
- Environment variables must be updated in production deployments
- Database migration renames `serverlessFunction` → `logicFunction`
tables
2026-01-28 01:42:19 +01:00
martmull
f4ca69a474
Implement dev mode nice UI ( #17471 )
...
Implement a nice terminal UI for dev mode using INK
<img width="1512" height="721" alt="image"
src="https://github.com/user-attachments/assets/79a71f37-1b31-4761-9e8d-718ef029ceb8 "
/>
2026-01-27 15:34:28 +00:00
martmull
41d470e687
Implement sync in dev mode ( #17405 )
...
implement orchestrator to sync application in dev mode
Log example when starting dev mode, delete and add back functions
```bash
👩💻 Workspace - default
[init] 🚀 Starting Twenty Application Development Mode
[init] 📁 App Path: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/.gitignore
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/.nvmrc
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/.yarnrc.yml
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/README.md
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/eslint.config.mjs
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/package.json
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/tsconfig.json
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/yarn.lock
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/.yarn/install-state.gz
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/ooo.front-component.tsx
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/tata.object.ts
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/app/application.config.ts
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/app/default-function.role.ts
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/app/hello-world-2.function.ts
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/app/hello-world-3.function.ts
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/app/hello-world.front-component.tsx
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/app/hello-world.function.ts
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/app/myObject.object.ts
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/utils/toto.ts
[dev-mode] Building manifest...
[dev-mode] Successfully built manifest
[dev-mode] Syncing...
[dev-mode] ✓ Successfully built src/ooo.front-component.tsx
[dev-mode] Uploading .twenty/output/src/ooo.front-component.mjs...
[dev-mode] ✓ Successfully built src/app/hello-world-2.function.ts
[dev-mode] Uploading .twenty/output/src/app/hello-world-2.function.mjs...
[dev-mode] ✓ Successfully built src/app/hello-world.front-component.tsx
[dev-mode] Uploading .twenty/output/src/app/hello-world.front-component.mjs...
[dev-mode] ✓ Successfully built src/app/hello-world-3.function.ts
[dev-mode] Uploading .twenty/output/src/app/hello-world-3.function.mjs...
[dev-mode] ✓ Successfully built src/app/hello-world.function.ts
[dev-mode] Uploading .twenty/output/src/app/hello-world.function.mjs...
[dev-mode] Successfully uploaded .twenty/output/src/ooo.front-component.mjs
[dev-mode] Successfully uploaded .twenty/output/src/app/hello-world-3.function.mjs
[dev-mode] Successfully uploaded .twenty/output/src/app/hello-world.front-component.mjs
[dev-mode] Successfully uploaded .twenty/output/src/app/hello-world.function.mjs
[dev-mode] Successfully uploaded .twenty/output/src/app/hello-world-2.function.mjs
[dev-mode] ✓ Synced
[dev-mode] ✓ Successfully built src/app/hello-world-2.function.ts
[dev-mode] Uploading .twenty/output/src/app/hello-world-2.function.mjs...
[dev-mode] Successfully uploaded .twenty/output/src/app/hello-world-2.function.mjs
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/app/hello-world-2.function.ts
[dev-mode] Building manifest...
[dev-mode] Successfully built manifest
[dev-mode] Syncing...
[dev-mode] ✓ Synced
[dev-mode] Build failed:
[dev-mode] Could not resolve "/Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/app/hello-world.function.ts"
[dev-mode] Could not resolve "/Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/app/hello-world-3.function.ts"
[dev-mode] Could not resolve "/Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/app/hello-world-2.function.ts"
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/app/hello-world.function.ts
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/app/hello-world-2.function.ts
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/app/hello-world-3.function.ts
[dev-mode] Building manifest...
[dev-mode] ⚠ No functions defined
[dev-mode] Successfully built manifest
[dev-mode] Syncing...
🗑️ Removed src/app/hello-world-2.function.mjs
🗑️ Removed src/app/hello-world-3.function.mjs
🗑️ Removed src/app/hello-world.function.mjs
[dev-mode] ✓ Synced
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/app/hello-world-2.function.ts
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/app/hello-world-3.function.ts
[dev-mode] File changed: /Users/martinmuller/Desktop/twenty/packages/twenty-apps/toto/src/app/hello-world.function.ts
[dev-mode] Building manifest...
[dev-mode] Successfully built manifest
[dev-mode] Syncing...
[dev-mode] ✓ Successfully built src/app/hello-world-2.function.ts
[dev-mode] Uploading .twenty/output/src/app/hello-world-2.function.mjs...
[dev-mode] ✓ Successfully built src/app/hello-world-3.function.ts
[dev-mode] Uploading .twenty/output/src/app/hello-world-3.function.mjs...
[dev-mode] ✓ Successfully built src/app/hello-world.function.ts
[dev-mode] Uploading .twenty/output/src/app/hello-world.function.mjs...
[dev-mode] Successfully uploaded .twenty/output/src/app/hello-world.function.mjs
[dev-mode] Successfully uploaded .twenty/output/src/app/hello-world-2.function.mjs
[dev-mode] Successfully uploaded .twenty/output/src/app/hello-world-3.function.mjs
[dev-mode] ✓ Synced
```
2026-01-26 19:32:13 +00:00
martmull
0091ef5f6c
Sync built files ( #17379 )
...
as title, upload built files to local storage
---------
Co-authored-by: Charles Bochet <charles@twenty.com >
2026-01-23 13:43:53 +01:00
Charles Bochet
d74d74da4c
Improvements on SDK watcher ( #17291 )
...
# Improve cross-entity duplicate detection in manifest validation
- Refactored findDuplicates to receive the full manifest, enabling
cross-entity duplicate checks
-ObjectExtensionEntityBuilder now validates that extension field IDs
don't conflict with object field IDs
- Renamed DuplicateId type to EntityIdWithLocation for clarity
- Updated all entity builders to use the new signature
2026-01-21 15:31:38 +01:00
Charles Bochet
351e2030ff
Rework watcher ( #17284 )
...
Heavy Refactoring of the watcher, sorry about this one, I'll keep
iterating on it.
In a nutshell:
- app-dev.ts is maintaining 3 watchers in parallel: manifest, function
and frontComponent
2026-01-20 17:25:44 +01:00
martmull
02a181a49f
Add endpoint to upload application file ( #17270 )
...
As title
2026-01-20 14:10:34 +00:00
martmull
801878cb2b
2091 extensibility twenty sdk add command twenty app function logs and twenty app function test ( #17278 )
...
add `function:logs` and `function:execute` cli commands
2026-01-20 14:10:01 +00:00
Charles Bochet
e61b92132c
Rework folder structure ( #17230 )
...
<img width="700" height="771" alt="image"
src="https://github.com/user-attachments/assets/b784fbe6-f976-4c9a-84c1-3d90a9010665 "
/>
Second one :)
2026-01-19 14:08:05 +00:00