Files
twenty/packages/twenty-sdk/package.json
T
Félix MalfaitandGitHub 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

151 lines
3.8 KiB
JSON

{
"name": "twenty-sdk",
"version": "0.6.3",
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/sdk/index.d.ts",
"bin": {
"twenty": "dist/cli.cjs"
},
"files": [
"dist",
"generated",
"README.md",
"package.json"
],
"scripts": {
"build": "npx rimraf dist && npx vite build"
},
"keywords": [
"twenty",
"cli",
"sdk",
"crm",
"application",
"development"
],
"exports": {
".": {
"types": "./dist/sdk/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./ui": {
"types": "./dist/ui/index.d.ts",
"import": "./dist/ui/index.mjs",
"require": "./dist/ui/index.cjs"
},
"./cli": {
"types": "./dist/cli/public-operations/index.d.ts",
"import": "./dist/operations.mjs",
"require": "./dist/operations.cjs"
},
"./front-component-renderer": {
"types": "./dist/front-component-renderer/index.d.ts",
"import": "./dist/front-component-renderer/index.mjs",
"require": "./dist/front-component-renderer/index.cjs"
},
"./generated": {
"types": "./generated/index.ts",
"import": "./generated/index.ts",
"require": "./generated/index.ts"
},
"./generated/core": {
"types": "./generated/core/index.ts",
"import": "./generated/core/index.ts",
"require": "./generated/core/index.ts"
},
"./generated/metadata": {
"types": "./generated/metadata/index.ts",
"import": "./generated/metadata/index.ts",
"require": "./generated/metadata/index.ts"
}
},
"license": "AGPL-3.0",
"dependencies": {
"@chakra-ui/react": "^3.33.0",
"@emotion/react": "^11.14.0",
"@genql/cli": "^3.0.3",
"@genql/runtime": "^2.10.0",
"@quilted/threads": "^4.0.1",
"@remote-dom/core": "^1.10.1",
"@remote-dom/react": "^1.2.2",
"@sniptt/guards": "^0.2.0",
"archiver": "^7.0.1",
"axios": "^1.13.5",
"chalk": "^5.3.0",
"chokidar": "^4.0.0",
"commander": "^12.0.0",
"dotenv": "^16.4.0",
"esbuild": "^0.25.0",
"fast-glob": "^3.3.0",
"form-data": "^4.0.5",
"fs-extra": "^11.2.0",
"graphql": "^16.8.1",
"graphql-sse": "^2.5.4",
"ink": "^5.1.1",
"inquirer": "^10.0.0",
"jsonc-parser": "^3.2.0",
"lodash.camelcase": "^4.3.0",
"lodash.kebabcase": "^4.1.1",
"preact": "^10.28.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^5.9.2",
"uuid": "^13.0.0",
"vite": "^7.0.0",
"vite-tsconfig-paths": "^4.2.1",
"zod": "^4.1.11"
},
"devDependencies": {
"@emotion/styled": "^11.14.0",
"@mui/material": "^7.3.8",
"@prettier/sync": "^0.5.2",
"@storybook/addon-vitest": "^10.2.13",
"@storybook/react-vite": "^10.2.13",
"@types/archiver": "^6.0.0",
"@types/fs-extra": "^11.0.0",
"@types/inquirer": "^9.0.0",
"@types/lodash.camelcase": "^4.3.7",
"@types/lodash.kebabcase": "^4.1.9",
"@types/node": "^24.0.0",
"@types/react": "18.2.66",
"@types/react-dom": "18.2.22",
"@vitest/browser-playwright": "^4.0.18",
"playwright": "^1.56.1",
"storybook": "^10.2.13",
"ts-morph": "^25.0.0",
"tsx": "^4.7.0",
"twenty-shared": "workspace:*",
"twenty-ui": "workspace:*",
"vite-plugin-dts": "^4.5.4",
"wait-on": "^7.2.0"
},
"engines": {
"node": "^24.5.0",
"yarn": "^4.0.2"
},
"typesVersions": {
"*": {
"cli": [
"dist/cli/public-operations/index.d.ts"
],
"ui": [
"dist/ui/index.d.ts"
],
"front-component-renderer": [
"dist/front-component-renderer/index.d.ts"
],
"generated": [
"generated/index.ts"
],
"generated/core": [
"generated/core/index.ts"
],
"generated/metadata": [
"generated/metadata/index.ts"
]
}
}
}