Compare commits

..
Author SHA1 Message Date
sonarly-bot 66c777f121 fix(server): prevent released query runner in calendar import
https://sonarly.com/issue/39517?type=bug

Calendar event import fails for affected channels with an internal DB lifecycle error, leaving sync in FAILED state. The failure started right after v2.7.2 and is tied to the calendar worker path.

Fix: I fixed the root cause by restoring workspace ORM context around token persistence in `ConnectedAccountRefreshTokensService`.

What changed:
1) `connected-account-refresh-tokens.service.ts`
- Reintroduced:
  - `GlobalWorkspaceOrmManager`
  - `buildSystemAuthContext`
- Re-added `globalWorkspaceOrmManager` constructor dependency.
- Wrapped `connectedAccountRepository.update(...)` in:
  - `const authContext = buildSystemAuthContext(workspaceId);`
  - `await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { ... }, authContext);`

Why this addresses the bug:
- The regression removed explicit workspace context before repository update in the refresh flow used by calendar import workers.
- Reintroducing `executeInWorkspaceContext` ensures repository operations execute with a valid workspace-bound ORM lifecycle/query runner, preventing the released-query-runner failure in this path.

2) `connected-account-refresh-tokens.service.spec.ts`
- Added `GlobalWorkspaceOrmManager` mock provider.
- Added retrieval of the mock from the testing module.
- Updated refresh-path tests to assert `executeInWorkspaceContext` is called for token refresh persistence.

Authored by Sonarly by autonomous analysis (run 45019).
2026-05-21 17:40:16 +00:00
Charles BochetandGitHub edf2bfbf76 feat(server): rotate connectedAccount.connectionParameters via secret-encryption:rotate (#20807)
## Summary

Adds a new `connected-account-connection-parameters` site to the
`secret-encryption:rotate` CLI introduced in #20613, so the nested
password envelopes inside `connectedAccount.connectionParameters` (IMAP
/ SMTP / CALDAV — encrypted at-rest in #20673) are re-encrypted under
the current `ENCRYPTION_KEY` alongside every other at-rest secret site.
Without this, rotating `ENCRYPTION_KEY` on a 2.7+ instance would
silently leave IMAP / SMTP / CalDav passwords on the old key id.

### Why a new handler

A dedicated handler is required (rather than reusing
`ColumnRotationSiteHandler`) because the envelope lives at
`connectionParameters->'<PROTOCOL>'->>'password'`, not in the whole
column, and up to three independent envelopes may need rotating per row.
The handler:

- Uses the same cursor-based, idempotent, online pattern as the existing
handlers, with a SQL predicate that skips rows where every non-null
protocol password is already on the current key id.
- Threads \`workspaceId\` into HKDF, matching how
\`EncryptConnectionParametersSlowInstanceCommand\` backfilled.
- Rebuilds only the protocols whose passwords are not yet current, so a
partial mid-row failure cannot cause unnecessary re-encryption on
resume.
- Guards the UPDATE with jsonb-level deep equality (\`IS NOT DISTINCT
FROM CAST(:json AS jsonb)\`) so optimistic concurrency is unaffected by
Postgres's internal jsonb key ordering vs. JSON.stringify ordering.
- Refuses to rotate plaintext passwords (counted as \`errors\`) —
operators must finish the 2.7 slow instance command
(\`EncryptConnectionParametersSlowInstanceCommand\`) before running
rotation.

### Sites covered (now)

| Site | Location | Scope |
| --- | --- | --- |
| \`connected-account-access-token\` | \`connectedAccount.accessToken\`
| workspace |
| \`connected-account-refresh-token\` |
\`connectedAccount.refreshToken\` | workspace |
| **\`connected-account-connection-parameters\`** (new) |
\`connectedAccount.connectionParameters.{IMAP,SMTP,CALDAV}.password\` |
workspace |
| \`application-variable\` | \`applicationVariable.value\` (isSecret) |
workspace |
| \`application-registration-variable\` |
\`applicationRegistrationVariable.encryptedValue\` | instance |
| \`signing-key-private-key\` | \`signingKey.privateKey\` | instance |
| \`totp-secret\` | \`twoFactorAuthenticationMethod.secret\` | workspace
|
| \`sensitive-config-storage\` | \`keyValuePair.value\` (sensitive
STRING configs) | instance |
2026-05-21 19:31:52 +02:00
64241ed8ae i18n - translations (#20822)
Created by Github action

---------

Co-authored-by: github-actions <[email protected]>
2026-05-21 19:26:28 +02:00
EtienneandGitHub 0edd8d400c fix(billing) - fix orphaned stripe subs (#20814)
Fix sentry issues
https://twenty-v7.sentry.io/issues/7203797925/?environment=prod&project=4507072499810304&query=is%3Aunresolved%20assigned%3Ame&referrer=issue-stream

An orphaned sub is a not "canceled" stripe sub with no matching
workspace

- Clean all orphaned sub (script not included in this PR)
- Ensure to soft delete > cancel stripe sub > check for not active sub >
hard delete in every workspace deletion flow

Bonus : 
- Remove dead code
- Update doc on RLS (to improve AI chat knowledge)
2026-05-21 17:02:54 +00:00
Paul RastoinandGitHub 3c91f3f276 fix(server): encrypt token post refresh (#20819)
# Introduction
Jobs were refreshing token and returning them as plain text, resulting
to underlying code flow failure as expecting encrypted tokens

## Next
We should define a strong typescript signature to avoid such things to
happen again, or least have an explicit naming
2026-05-21 18:53:03 +02:00
0bfcd9a701 fix(api-keys): refresh API keys list after key creation (#20806)
New API keys are created successfully but the API keys table can keep
showing a stale pre-create result, so users think the key vanished. This
blocks key management from the expected UI flow.

Fix: Updated the API key creation mutation to explicitly synchronize
Apollo cache for the API keys list:

- In `SettingsDevelopersApiKeysNew.tsx`, imported `GetApiKeysDocument`.
- Changed `useMutation(CreateApiKeyDocument)` to:
  - `refetchQueries: [GetApiKeysDocument]`
  - `awaitRefetchQueries: true`

Why: the list page (`SettingsApiKeysTable`) reads from
`GetApiKeysDocument`, and creation previously did not invalidate/refetch
that query. With this change, successful creation refreshes the list
query so the new key appears when the user returns to APIs & Webhooks.

Validation attempted:
- `npx nx lint:diff-with-main twenty-front` → failed due missing Nx
modules in this environment.
- `npx nx typecheck twenty-front` → failed due missing Nx modules in
this environment.

Authored by Sonarly by autonomous analysis (run 44851).

Co-authored-by: sonarly-bot <[email protected]>
2026-05-21 16:40:48 +00:00
869f8af4f0 Fix workflow cron trigger cache stuck without TTL (#20812)
## Problem

The cron-trigger cache key (`module:workflow:workflow-cron-triggers`)
can get stuck without a TTL, silently halting **all** cron-triggered
workflows for a whole tenant until the key is manually deleted from
Redis.

Repro path:

1. Cache miss → DB-scan branch runs.
2. Inner loop writes triggers via `hashSet` (creates the key, **no TTL
yet**).
3. Worker crashes / OOMs / gets killed by a deploy between any `hashSet`
and the trailing `expire(1h)` call.
4. Key now exists with TTL = `-1` and a partial set of fields.
5. Next tick: `hashGetValues` returns those fields →
`cachedValues.length > 0` → **cache-hit branch** → `expire` is never
called.
6. Key has no TTL, so it never auto-expires. The DB-scan branch never
runs again. New / missing triggers are never picked up. Workflows go
silent.

Observed in production: cache key with `TTL: no limit` and 121 fields.
Deleting the key restored normal behaviour (next tick rebuilt with TTL
~3600).

## Fix

Set the TTL right after first value is added 

## Monitoring

Added a "Cache miss" log count in workflow dashboard, counted among the
last 6 hours. Turns green if >= 5
<img width="1627" height="721" alt="Screenshot 2026-05-21 at 16 46 13"
src="https://github.com/user-attachments/assets/8262dd5f-fbbd-43c9-aede-c0ce5d6a0f59"
/>

---------

Co-authored-by: Cursor <[email protected]>
2026-05-21 16:03:09 +00:00
EtienneandGitHub 2fcf3e3c2b fix(ai-chat) - fix browser context injection (#20809)
Move the browsing context out of the system prompt and injecting it
directly into the last user message instead.
Previously (before this PR), browsing context change, update system
prompt then break whole conversation history ... and caching. Now,
browsing context is sent with last message only if changed.

"Benchmark" this PR vs main : 
- same conv with 3-4 turns - 60% -> 85% cache ratio || 0.31 credits ->
0.13
2026-05-21 15:55:07 +00:00
Abdullah.andGitHub 8826d12a18 [Website] Host customer story hero images locally and fix multi-segment redirects (#20790)
Customer story page shows the following error, which I believe leads to
an internal server on the individual customer story pages.

<img width="636" height="75" alt="image"
src="https://github.com/user-attachments/assets/fc9ede75-fd3b-4538-8211-8182f4a99b9b"
/>

This PR replaces remote URLs of those images with local copies to avoid
a 404 issue. Will test once deployed on dev to confirm if the error is
resolved, but locally, I do not see console errors any longer after this
change.

There is some duplicated copy that I found upon audit which can be made
DRY, but I will resolve it in a separate PR to keep this PR
single-responsibility.
2026-05-21 14:10:24 +00:00
Paul RastoinandGitHub abf5902ab5 Connected account deprecation system build (#20810) 2026-05-21 16:14:56 +02:00
a78c4c9fe8 chore: bump version to 2.8.0 (#20813)
## Summary

- Moves current version to previous versions array
- Sets TWENTY_CURRENT_VERSION to the new version
- Updates TWENTY_NEXT_VERSIONS with the next minor version

## Checklist

- [ ] Verify version constants are correct

Co-authored-by: Github Action Deploy <[email protected]>
2026-05-21 16:10:08 +02:00
a9ff1a9d3c Fix server variable not shown (#20799)
## Before

<img width="1502" height="675" alt="image"
src="https://github.com/user-attachments/assets/b64b24b4-11a5-4f6b-a3aa-c77108f22e9d"
/>

## After

<img width="1262" height="593" alt="image"
src="https://github.com/user-attachments/assets/42d662b4-2ec6-4ad4-9d19-58f25f52475c"
/>

---------

Co-authored-by: ehconitin <[email protected]>
2026-05-21 13:43:05 +00:00
Paul RastoinandGitHub 570c57563a Upload application file resolver exception management and integration coverage (#20803)
# Introduction
Earlier and better exception handling of the upload application file
resolver
+ coverage
2026-05-21 12:45:54 +00:00
Charles BochetandGitHub 4b8c722b41 fix(docker): pin patched curl/nghttp2/postgresql18-client apk versions (#20805)
## Summary

- ECR Inspector still flags `prod-twenty` for the High-severity CVEs
that PR #20603 was meant to fix (8x `postgresql18-18.3-r0`,
`nghttp2-1.68.0-r0`, `curl-8.17.0-r1`, plus the related Medium `curl`
CVE).
- Root cause: PR #20603 pinned the `node:24.15.0-alpine3.23` digest to
invalidate the buildx GHA cache once, but the cache layer was first
repopulated (on the PR branch) before Alpine 3.23 published `18.4-r0` /
`1.69.0-r0` / `8.19.0-r0`. Every build since — including today's prod
v2.6.2 — hits `#26 [twenty-server 2/19] RUN apk add --no-cache curl jq
postgresql-client / #26 CACHED` and ships the stale packages.
- Pinning minimum versions in the `apk add` spec changes the RUN text →
forces a new buildx cache key → apk re-resolves against the current
Alpine mirror. apk also refuses to install anything below the floor, so
the image can't silently regress if a stale layer ever matches the key
again.
2026-05-21 12:42:19 +00:00
WeikoandGitHub d13cc7c349 Drop legacy rolePermissionFlag.flag column + fallback logic (#20730)
## Summary
- **New fast migration**
`2-7-instance-command-fast-1779600000000-finalize-role-permission-flag-cutover.ts`:
  - `DROP CONSTRAINT IDX_ROLE_PERMISSION_FLAG_FLAG_ROLE_ID_UNIQUE`
  - `ALTER COLUMN permissionFlagId SET NOT NULL`
  - `DROP COLUMN flag`
- `down()` repopulates `flag` from the catalog via `permissionFlagId`
and restores the old unique.
- **Entity**: `RolePermissionFlagEntity` hides the `flag` column by
using the new decorator + drops old `@Unique` decorator;
`permissionFlagId` and the `permissionFlag` relation become
non-nullable.
- **Deletes** the synthesizer
`synthesize-flat-permission-flag-from-flag.util.ts` and every fallback
branch that used it

(`from-role-permission-flag-entity-to-flat-role-permission-flag.util.ts`,
`from-flat-role-permission-flag-to-role-permission-flag-dto.util.ts`,
`permissions.service.ts`,
`workspace-roles-permissions-cache.service.ts`,
`fromRoleEntityToRoleDto.util.ts`,
`flat-role-permission-flag-validator.service.ts`,
`role-permission-flag.service.ts:getEffectiveUniversalIdentifier`).
- **Write path**: ~~drops `flag` from `CreateRolePermissionFlagInput`,
the create util, and the application-manifest converter.~~
- **Metadata configs**: ~~removes `flag` from
`all-entity-properties-configuration-by-metadata-name.constant.ts`
(rolePermissionFlag block)~~ and flips `permissionFlag.isNullable` to
`false` in `all-many-to-one-metadata-relations.constant.ts`.

### Why the `flag` field stays declared in the entity

The decorator (`@WasRemovedInUpgrade`) is the right tool for the
lifecycle marker, but it's a **reflect-metadata** runtime decorator —
TypeScript can't see it at compile time. So while
the adapter
([`UpgradeAwareEntityMetadataAdapter`](packages/twenty-server/src/engine/twenty-orm/upgrade-aware/upgrade-aware-entity-metadata.adapter.ts))
now correctly flips
`isSelect`/`isInsert`/`isUpdate` to `false` once the drop migration's
cursor is crossed, the *static* TypeScript types derived from
`RolePermissionFlagEntity`
(`UniversalFlatRolePermissionFlag`, `FlatRolePermissionFlag`,
`MetadataEntityPropertyConfiguration<'rolePermissionFlag'>`, etc.) still
see `flag` as a required scalar property — because the entity declares
it.

That means every producer of one of those derived types must include
`flag`:
-
`from-create-role-permission-flag-input-to-flat-role-permission-flag-to-create.util.ts`
plumbs it through.
- `from-permission-flag-to-universal-flat-role-permission-flag.util.ts`
(the application-manifest converter) sets `flag: permissionFlag.flag`.
- `all-entity-properties-configuration-by-metadata-name.constant.ts` has
a `flag` entry under `rolePermissionFlag`.
- `CreateRolePermissionFlagInput` keeps the `flag` field.
- `RolePermissionFlagService.upsertPermissionFlags` passes `flag:
permissionFlag.key as PermissionFlagType` to the create util.

Explored phantom-brand approach (`RemovedInUpgrade<T>` wrapper on the
field type, key-filter mapped type applied inside `ScalarFlatEntity` /
`UniversalFlatEntityFrom`) but previous commands could have `flag ===
undefined` (downcast from the brand since we can't compare with
UpgradeMigrationName like we do with a decorator). That's a
**silent-read** failure mode: compiles fine, comparisons against `flag`
silently always-false, no error surfaces. Probably worth too much risk
for what's a small amount of plumbing?

The eventual full deletion of `flag` (entity field included) is a future
cleanup once we drop cross-upgrade support for versions ≤ 2.6

Note: Not sure if this PR (and even the decorator) is really needed in
the end, seems we need to keep a lot of code in place to handle legacy.
Maybe a simple noop [At]Deprecated is enough @charlesBochet (and a
migration to set the column nullable if that was not the case before +
remove associated constraints)
2026-05-21 11:56:06 +00:00
d192d2d492 i18n - translations (#20804)
Created by Github action

---------

Co-authored-by: github-actions <[email protected]>
2026-05-21 13:50:56 +02:00
7ea652ddf8 i18n - translations (#20802)
Created by Github action

Co-authored-by: github-actions <[email protected]>
2026-05-21 13:43:27 +02:00
Paul RastoinandGitHub 9b9c97a049 Deprecate and backfill delete ConnectedAccount twenty standard object (#20752)
# Introduction
Following connected account permissions refactor and encryption
Removing the old workspace schema twenty standard application
connectedAccount objects and related standard fields and index

- a lot of deadcode
- instance command backfill cleaning the connected account object from
workspaces
2026-05-21 13:42:09 +02:00
f1b2be041a i18n - translations (#20801)
Created by Github action

Co-authored-by: github-actions <[email protected]>
2026-05-21 13:35:35 +02:00
martmullandGitHub 2963fa9324 Navigate to installed page after app install (#20797)
as title
2026-05-21 11:19:18 +00:00
Charles BochetandGitHub a6a08439f7 chore(deps): bump @xmldom/xmldom to 0.8.13 (security) (#20798)
## Summary
- Re-resolves the transitive `@xmldom/xmldom` dependency to `0.8.13` to
fix four high-severity Dependabot alerts.
- yarn.lock-only change: all four upstream consumers
(`@node-saml/node-saml`, `plist`, `xml-crypto`, `xml-encryption`) accept
`^0.8.x`, so the previous `0.8.10` / `0.8.11` entries collapse onto a
single `0.8.13` resolution. No `package.json` change needed.

## Alerts fixed
-  XML node injection through unvalidated comment serialization (high)
- XML node injection through unvalidated processing instruction
serialization (high)
- XML injection through unvalidated DocumentType serialization (high)
- Uncontrolled recursion in XML serialization leads to DoS (high)

All four advisories are patched in `0.8.13`, the latest release in the
`0.8.x` line.
2026-05-21 11:17:41 +00:00
383edd5871 fix(email): resolve reply account from thread channel (#20755)
## Summary

Fixes the reply account resolution path so email replies use the
connected account associated with the thread's message channel instead
of blindly selecting the first connected account.

This targets the failure mode reported in #20658 where replying can
surface `SMTP is not configured for connected account` even though the
thread's actual IMAP/SMTP account has SMTP configured.

## Changes

- Query `myMessageChannels` alongside `myConnectedAccounts` in
`useEmailThread`
- Resolve the latest message's `messageChannelId` to its
`connectedAccountId`
- Return the matching connected account for reply context instead of
`myConnectedAccounts[0]`
- Add a regression test proving the hook chooses the thread channel's
account when the first account is different

## Validation

- `git diff --check`

I could not run the full frontend test locally in this workspace because
dependency installation failed with `ENOSPC: no space left on device`
while Yarn was cloning a dependency into the Windows temp/cache path.
The code change is intentionally narrow and covered by the added hook
regression test.

---------

Co-authored-by: neo773 <[email protected]>
2026-05-21 10:26:59 +00:00
16ad50ea46 Set website default port to 3002 (#20795)
## Summary

- Set `twenty-website`'s `dev` script to run Next.js on port `3002` by
default.
- Set `twenty-website`'s `start` script to use the same default port.

## Why

`twenty-website` previously inherited Next.js' default port `3000`,
which is also Twenty's backend/server default. The main Twenty frontend
already defaults to `3001`, so using `3002` for the website avoids local
port collisions when running the website next to the app server and
frontend.

This keeps the local convention sequential:

- `3000`: Twenty backend/server
- `3001`: Twenty frontend app
- `3002`: Twenty website

## Validation

- Parsed `packages/twenty-website/package.json` successfully with Node.
- Ran `git diff --check` for the changed package file.
- Verified Next.js supports the `--port` option for `next dev`.

---------

Co-authored-by: Abdullah <[email protected]>
2026-05-21 09:34:56 +00:00
martmullandGitHub 74fa26f9e5 Fix must wait 3 days to create app in twenty-apps (#20794)
as title
2026-05-21 09:29:47 +00:00
7ca2efeb96 i18n - translations (#20796)
Created by Github action

---------

Co-authored-by: github-actions <[email protected]>
2026-05-21 11:42:46 +02:00
WeikoandGitHub 224376233b fix(front): focus new Field widget and open side panel on add (#20777)
## Context
In the record-page edit mode, the "Add widget" section has three menu
items:
`Fields group`, `Field`, and `More widgets`. `Fields group` creates the
widget, focuses it, and opens its settings side panel. `Field` only
added the widget to the draft — no focus, no panel — which left the user
without visible confirmation or a way to immediately edit the new
widget.

## Change
Updated `useCreateRecordPageFieldWidget` to mirror
`useCreateRecordPageFieldsWidget`:
After appending the new widget to the draft, it now sets
`pageLayoutEditingWidgetIdComponentState` to the new widget's id and
navigates the side panel to `SidePanelPages.RecordPageFieldSettings`
(with `focusTitleInput: true`, `resetNavigationStack: true`).
This matches the behavior of clicking an existing field widget in
`useOpenWidgetSettingsInSidePanel` (`WidgetType.FIELD` branch).
2026-05-21 09:22:21 +00:00
Paul RastoinandGitHub a3c92311e3 Application file storage service (#20793)
# Introduction
Fix unsafe resource path join with expected prefix at file storage
directly
Add early paths transversal detections in metadata validators
2026-05-21 09:21:44 +00:00
1ed347bc17 chore: sync AI model catalog from models.dev (#20791)
Automated daily sync of `ai-providers.json` from
[models.dev](https://models.dev).

This PR updates pricing, context windows, and model availability based
on the latest data.
New models meeting inclusion criteria (tool calling, pricing data,
context limits) are added automatically.
Deprecated models are detected based on cost-efficiency within the same
model family.

**Please review before merging** — verify no critical models were
incorrectly deprecated.

Co-authored-by: FelixMalfait <[email protected]>
2026-05-21 09:14:02 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b792f7654b chore(deps): bump tinyglobby from 0.2.15 to 0.2.16 (#20788)
Bumps [tinyglobby](https://github.com/SuperchupuDev/tinyglobby) from
0.2.15 to 0.2.16.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/SuperchupuDev/tinyglobby/releases">tinyglobby's
releases</a>.</em></p>
<blockquote>
<h2>0.2.16</h2>
<h2>Fixed</h2>
<ul>
<li>Upgraded <code>picomatch</code> to 4.0.4, mitigating any potential
exposure to <a
href="https://github.com/micromatch/picomatch/security/advisories/GHSA-c2c7-rcm5-vvqj">CVE-2026-33671</a>
and <a
href="https://github.com/micromatch/picomatch/security/advisories/GHSA-3v7f-55p6-f55p">CVE-2026-33672</a></li>
</ul>
<h2>Changed</h2>
<ul>
<li>Overhauled and optimized most internals by <a
href="https://github.com/Torathion"><code>@​Torathion</code></a></li>
<li>Ignore patterns are no longer compiled twice by <a
href="https://github.com/webpro"><code>@​webpro</code></a></li>
</ul>
<p>Consider <a
href="https://github.com/sponsors/SuperchupuDev">sponsoring</a> if you'd
like to support the development of this project and the goal of reaching
a lighter and faster ecosystem</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/SuperchupuDev/tinyglobby/blob/main/CHANGELOG.md">tinyglobby's
changelog</a>.</em></p>
<blockquote>
<h3><a
href="https://github.com/SuperchupuDev/tinyglobby/compare/0.2.15...0.2.16">0.2.16</a></h3>
<h4>Fixed</h4>
<ul>
<li>Upgraded <code>picomatch</code> to 4.0.4, mitigating any potential
exposure to
<a
href="https://github.com/micromatch/picomatch/security/advisories/GHSA-c2c7-rcm5-vvqj">CVE-2026-33671</a>
and <a
href="https://github.com/micromatch/picomatch/security/advisories/GHSA-3v7f-55p6-f55p">CVE-2026-33672</a></li>
</ul>
<h4>Changed</h4>
<ul>
<li>Overhauled and optimized most internals by <a
href="https://github.com/Torathion">Torathion</a></li>
<li>Ignore patterns are no longer compiled twice by <a
href="https://github.com/webpro">webpro</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/SuperchupuDev/tinyglobby/commit/577920259c91f5603fab3dbfa599a83bbb14a27a"><code>5779202</code></a>
release 0.2.16</li>
<li><a
href="https://github.com/SuperchupuDev/tinyglobby/commit/071954f97f4d2b573ecd92aecb48220867b3c776"><code>071954f</code></a>
bump deps once more</li>
<li><a
href="https://github.com/SuperchupuDev/tinyglobby/commit/e541dde000158e69b44e8b5a03789d136d88ae0d"><code>e541dde</code></a>
do not import the whole <code>fs</code> module</li>
<li><a
href="https://github.com/SuperchupuDev/tinyglobby/commit/2381b766d3447e078b3112e533ada07621f60526"><code>2381b76</code></a>
fix root being too broad</li>
<li><a
href="https://github.com/SuperchupuDev/tinyglobby/commit/0addeb9a78cab5fd93ac6dad217f75930cfbfda2"><code>0addeb9</code></a>
chore(deps): update all non-major dependencies (<a
href="https://redirect.github.com/SuperchupuDev/tinyglobby/issues/191">#191</a>)</li>
<li><a
href="https://github.com/SuperchupuDev/tinyglobby/commit/91ac26cc3bda60378d188565ce4f05e884aa4f31"><code>91ac26c</code></a>
chore(deps): update pnpm/action-setup action to v5 (<a
href="https://redirect.github.com/SuperchupuDev/tinyglobby/issues/192">#192</a>)</li>
<li><a
href="https://github.com/SuperchupuDev/tinyglobby/commit/c50558e944bf71dbfeb392f8693fada87f520a70"><code>c50558e</code></a>
upgrade picomatch (and everything else)</li>
<li><a
href="https://github.com/SuperchupuDev/tinyglobby/commit/618517544e95f8167f1722902558dd78dcd34fe6"><code>6185175</code></a>
chore(deps): update dependency picomatch to v4.0.4 [security] (<a
href="https://redirect.github.com/SuperchupuDev/tinyglobby/issues/193">#193</a>)</li>
<li><a
href="https://github.com/SuperchupuDev/tinyglobby/commit/49c2b9356c4977f1f531de651f90b420a06c64f6"><code>49c2b93</code></a>
enable pnpm <code>trustPolicy</code></li>
<li><a
href="https://github.com/SuperchupuDev/tinyglobby/commit/bc825c476a5a429e58e06d11364cd68707b4cdd1"><code>bc825c4</code></a>
chore(deps): update all non-major dependencies (<a
href="https://redirect.github.com/SuperchupuDev/tinyglobby/issues/181">#181</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/SuperchupuDev/tinyglobby/compare/0.2.15...0.2.16">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tinyglobby&package-manager=npm_and_yarn&previous-version=0.2.15&new-version=0.2.16)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-21 05:47:58 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
42ad3cbd1a chore(deps): bump linkify-react from 4.3.2 to 4.3.3 (#20789)
Bumps
[linkify-react](https://github.com/nfrasser/linkifyjs/tree/HEAD/packages/linkify-react)
from 4.3.2 to 4.3.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/nfrasser/linkifyjs/releases">linkify-react's
releases</a>.</em></p>
<blockquote>
<h2>v4.3.3</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix parsing bugs with some special encoded URLs</li>
<li>Parsed emails should not include port numbers</li>
<li>Exact version requirement for interfaces and plugins to avoid
incompatibility issues with older versions of linkify core</li>
<li>Support for jQuery 4</li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/nfrasser/linkifyjs/compare/v4.3.2...v4.3.3">https://github.com/nfrasser/linkifyjs/compare/v4.3.2...v4.3.3</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/nfrasser/linkifyjs/blob/main/CHANGELOG.md">linkify-react's
changelog</a>.</em></p>
<blockquote>
<h2>v4.3.3</h2>
<ul>
<li>Fix parsing bugs with some special encoded URLs</li>
<li>Parsed emails should not include port numbers</li>
<li>Exact version requirement for interfaces and plugins to avoid
incompatibility issues with older versions of linkify core</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/nfrasser/linkifyjs/commit/7fffcc6b48f7dbf8e98fca493e1c997a659fe651"><code>7fffcc6</code></a>
v4.3.3</li>
<li><a
href="https://github.com/nfrasser/linkifyjs/commit/2cb8352d78c7449cd8c7ee489a647d3422640a25"><code>2cb8352</code></a>
Update dependencies (<a
href="https://github.com/nfrasser/linkifyjs/tree/HEAD/packages/linkify-react/issues/529">#529</a>)</li>
<li>See full diff in <a
href="https://github.com/nfrasser/linkifyjs/commits/v4.3.3/packages/linkify-react">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for linkify-react since your current version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=linkify-react&package-manager=npm_and_yarn&previous-version=4.3.2&new-version=4.3.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-21 05:47:35 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e1378dd4cf chore(deps): bump @azure/msal-node from 3.8.4 to 3.8.10 (#20787)
Bumps
[@azure/msal-node](https://github.com/AzureAD/microsoft-authentication-library-for-js)
from 3.8.4 to 3.8.10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/AzureAD/microsoft-authentication-library-for-js/releases">@​azure/msal-node's
releases</a>.</em></p>
<blockquote>
<h2><code>@​azure/msal-node</code> v3.8.10</h2>
<h2>3.8.10</h2>
<p>Wed, 18 Mar 2026 20:48:29 GMT</p>
<h3>Patches</h3>
<ul>
<li>Bump <code>@​azure/msal-common</code> to v15.17.0 (beachball)</li>
<li>Bump eslint-config-msal to v0.0.0 (beachball)</li>
<li>Bump rollup-msal to v0.0.0 (beachball)</li>
</ul>
<h2><code>@​azure/msal-node</code> v3.8.9</h2>
<h2>3.8.9</h2>
<p>Fri, 13 Mar 2026 04:32:07 GMT</p>
<h3>Patches</h3>
<ul>
<li>Bump <code>@​azure/msal-common</code> to v15.16.1 (beachball)</li>
<li>Bump eslint-config-msal to v0.0.0 (beachball)</li>
<li>Bump rollup-msal to v0.0.0 (beachball)</li>
</ul>
<h2><code>@​azure/msal-node</code> v3.8.8</h2>
<h2>3.8.8</h2>
<p>Mon, 23 Feb 2026 16:28:24 GMT</p>
<h3>Patches</h3>
<ul>
<li>Bump <code>@​azure/msal-common</code> to v15.15.0 (beachball)</li>
<li>Bump eslint-config-msal to v0.0.0 (beachball)</li>
<li>Bump rollup-msal to v0.0.0 (beachball)</li>
</ul>
<h2><code>@​azure/msal-node</code> v3.8.7</h2>
<h2>3.8.7</h2>
<p>Tue, 10 Feb 2026 22:19:29 GMT</p>
<h3>Patches</h3>
<ul>
<li>Bump <code>@​azure/msal-common</code> to v15.14.2 (beachball)</li>
<li>Bump eslint-config-msal to v0.0.0 (beachball)</li>
<li>Bump rollup-msal to v0.0.0 (beachball)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/AzureAD/microsoft-authentication-library-for-js/commit/74c792ec34cd83a3470c4d878b403af0fa2884f0"><code>74c792e</code></a>
[v4] Add missing client capabilities in platform broker flows (<a
href="https://redirect.github.com/AzureAD/microsoft-authentication-library-for-js/issues/8429">#8429</a>)</li>
<li><a
href="https://github.com/AzureAD/microsoft-authentication-library-for-js/commit/e096fc85e22fcb941646b3f5d9f264b90dffd0ad"><code>e096fc8</code></a>
[v4] Post-release PR (<a
href="https://redirect.github.com/AzureAD/microsoft-authentication-library-for-js/issues/8425">#8425</a>)</li>
<li><a
href="https://github.com/AzureAD/microsoft-authentication-library-for-js/commit/23cd31c1fbcfa569dd9135830f5168ec71678a19"><code>23cd31c</code></a>
[v4] Add support for client data telemetry with CLI_DATA parameter (<a
href="https://redirect.github.com/AzureAD/microsoft-authentication-library-for-js/issues/8378">#8378</a>)</li>
<li><a
href="https://github.com/AzureAD/microsoft-authentication-library-for-js/commit/eb893e565a4f7de5b9c957b37bb4f975302f1713"><code>eb893e5</code></a>
Track online/offline status change (<a
href="https://redirect.github.com/AzureAD/microsoft-authentication-library-for-js/issues/8410">#8410</a>)</li>
<li><a
href="https://github.com/AzureAD/microsoft-authentication-library-for-js/commit/d221f4e6dbc439e771a90f1648c8f5db09b4d88b"><code>d221f4e</code></a>
[v4] Respect claims of the brokered application (<a
href="https://redirect.github.com/AzureAD/microsoft-authentication-library-for-js/issues/8409">#8409</a>)</li>
<li><a
href="https://github.com/AzureAD/microsoft-authentication-library-for-js/commit/73df97e5c505010f6b9681749df28a328e4e3894"><code>73df97e</code></a>
Common partial release resolution (<a
href="https://redirect.github.com/AzureAD/microsoft-authentication-library-for-js/issues/8397">#8397</a>)</li>
<li><a
href="https://github.com/AzureAD/microsoft-authentication-library-for-js/commit/ee5e7abd5d9a6cfb6ada9b3ac36e15a81f7ba3a8"><code>ee5e7ab</code></a>
monitor_window_timeout telemetry (<a
href="https://redirect.github.com/AzureAD/microsoft-authentication-library-for-js/issues/8380">#8380</a>)</li>
<li><a
href="https://github.com/AzureAD/microsoft-authentication-library-for-js/commit/2be7888051cc12e7447487e84c826159dad3ed42"><code>2be7888</code></a>
Rename dev to v4-lts changes (<a
href="https://redirect.github.com/AzureAD/microsoft-authentication-library-for-js/issues/8364">#8364</a>)</li>
<li><a
href="https://github.com/AzureAD/microsoft-authentication-library-for-js/commit/4486b7e4bfedc414f7f5f1a7786be30484318206"><code>4486b7e</code></a>
Fix JSON object conversion in PlatformDOMRequest v4 (<a
href="https://redirect.github.com/AzureAD/microsoft-authentication-library-for-js/issues/8350">#8350</a>)</li>
<li><a
href="https://github.com/AzureAD/microsoft-authentication-library-for-js/commit/3e5d58b71e232b5978edcf0bd4c05f5948b8a747"><code>3e5d58b</code></a>
Post-release PR (<a
href="https://redirect.github.com/AzureAD/microsoft-authentication-library-for-js/issues/8354">#8354</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/AzureAD/microsoft-authentication-library-for-js/compare/msal-node-v3.8.4...msal-node-v3.8.10">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@azure/msal-node&package-manager=npm_and_yarn&previous-version=3.8.4&new-version=3.8.10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-21 05:06:59 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
5e1b23a28b chore(deps): bump @recallai/desktop-sdk from 2.0.8 to 2.0.15 (#20785)
Bumps @recallai/desktop-sdk from 2.0.8 to 2.0.15.


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@recallai/desktop-sdk&package-manager=npm_and_yarn&previous-version=2.0.8&new-version=2.0.15)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-21 05:04:13 +00:00
7aef406696 i18n - translations (#20782)
Created by Github action

---------

Co-authored-by: github-actions <[email protected]>
2026-05-20 21:33:20 +02:00
923c3beead fix(auth): clarify error when joining a non-active workspace (#20769)
## Summary

When an existing user accepts an invite into a workspace whose
`activationStatus` is not `ACTIVE` (e.g. `SUSPENDED`, `INACTIVE`,
`PENDING_CREATION`), the throw in
`throwIfWorkspaceIsNotReadyForSignInUp` returns:

> User is not part of the workspace

The message describes the symptom (they aren't a member yet) instead of
the cause (the workspace can't accept new members), which makes invitees
assume their invite is broken when the real issue is the target
workspace's state.

The sibling branch a few lines above — for brand-new users hitting the
same non-ACTIVE workspace — already returns `"Workspace is not ready to
welcome new members"`. This PR reuses the same message in the
existing-user branch so both paths give a consistent, accurate
explanation.

Single file, two string literals.

## Test plan

- [ ] Sign in via Google with an existing Twenty account, accepting an
invite to a `SUSPENDED` workspace → confirm the new message is shown
instead of "User is not part of the workspace".
- [ ] Confirm the happy path (sign-in to an `ACTIVE` workspace via
invite) is unchanged — early-return on `ACTIVE` is untouched.

---------

Co-authored-by: Charles Bochet <[email protected]>
2026-05-20 19:16:11 +00:00
Paul RastoinandGitHub 31842f7714 Ci server custom jest reporter (#20765)
# Introduction
Only display failing unit tests trace in the ci-server server-test jobs
So it's possible to identify what unit test are failing without having
to re run them locally
2026-05-20 17:58:27 +00:00
Charles BochetandGitHub 9988f98577 feat(server): idempotent CLI to rotate ENCRYPTION_KEY across enc:v2 rows (#20613)
## Summary
Adds the \`secret-encryption:rotate\` CLI command, which re-encrypts
every at-rest secret stored in an \`enc:v2:\` envelope under the current
\`ENCRYPTION_KEY\`. The command is **online** and **resumable**: a SQL
filter skips rows already on the current keyId, so interrupting it
(Ctrl-C, container restart, …) and re-running picks up where it left off
without re-rotating earlier rows.

### Sites covered (one handler each)
| Site | Table.column | Scope |
| --- | --- | --- |
| \`connected-account-tokens\` | \`connectedAccount.{accessToken,
refreshToken}\` | workspace |
| \`application-variable\` | \`applicationVariable.value\` (isSecret
only) | workspace |
| \`application-registration-variable\` |
\`applicationRegistrationVariable.encryptedValue\` | instance |
| \`signing-key-private-keys\` | \`signingKey.privateKey\` | instance |
| \`sensitive-config-storage\` | \`keyValuePair.value\` (isSensitive +
STRING configs) | instance |
| \`totp-secrets\` | \`twoFactorAuthenticationMethod.secret\` |
workspace |

Each handler:
- Filters at SQL level on \`value LIKE 'enc:v2:%' AND value NOT LIKE
'enc:v2:<primaryKeyId>:%'\` to enforce idempotency without re-decrypting
already-rotated rows.
- Uses cursor-based batching (default **200**, capped **5000**).
- Threads \`workspaceId\` into HKDF for workspace-scoped sites; runs
instance-scoped for the rest.

### CLI flags
| Flag | Description |
| --- | --- |
| \`-s, --site <site>\` | Limit to a single site. |
| \`-b, --batch-size <n>\` | Override per-batch row count. |
| \`-d, --dry-run\` | Decrypt + re-encrypt in memory, skip the
\`UPDATE\`. |

The runner logs progress via Nest \`Logger\` (per-site start,
completion, final summary) and exits non-zero when any site reports
\`errors > 0\`. \`FALLBACK_ENCRYPTION_KEY\` must be set to the previous
\`ENCRYPTION_KEY\` during rotation; the runner warns when it is unset.

Operator documentation lives in #20611 (docs PR).
2026-05-20 17:51:29 +00:00
3c458ce4ca i18n - docs translations (#20778)
Created by Github action

Co-authored-by: github-actions <[email protected]>
2026-05-20 19:28:51 +02:00
b869107a22 fix(messaging): preserve all gmail to/cc/bcc recipients as participants (#20491)
As title but I also refactored it a little to match our current file and
code conventions since the code was very old

Reported by a cloud customer

---------

Co-authored-by: martmull <[email protected]>
2026-05-20 15:40:29 +00:00
EtienneandGitHub 4c4dc4cb21 fix(ai-chat)-preference models import (#20776) 2026-05-20 15:20:09 +00:00
martmullGitHubcubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
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
WeikoandGitHub 6e5e7963b5 fix(server): map PermissionsException to proper HTTP status on REST API (#20739)
## Summary

`PermissionsException` thrown by `SettingsPermissionGuard` (and other
permission code paths) was bubbling up through every typed REST
exception filter and landing in the global `UnhandledExceptionFilter`,
which falls back to **500** for anything that isn't an `HttpException`.
So a forbidden user (e.g. an API key whose role doesn't have
`DATA_MODEL`) calling `GET /rest/metadata/objects` got:
```
HTTP/1.1 500 Internal Server Error "Entity performing the request does not have permission"
```


GraphQL already had the right plumbing via
`permissionGraphqlApiExceptionHandler` (`ForbiddenError` → 403,
`UserInputError` → 400, `NotFoundError` → 404). This PR mirrors it on
the REST side.

## What

- New util `permissionRestApiExceptionCodeToHttpStatus` mapping every
`PermissionsExceptionCode` → HTTP status, with `assertUnreachable` to
force explicit handling of future codes.
- New filter `PermissionsRestApiExceptionFilter`
(`@Catch(PermissionsException)`) that delegates to
`HttpExceptionHandlerService.handleError(...)` with the resolved status.
- Wired `PermissionsRestApiExceptionFilter` (placed first, so the typed
filter wins over any sibling catch-all) into `@UseFilters(...)` of every
REST controller that uses `SettingsPermissionGuard` or whose service can
throw `PermissionsException`:
  - `object-metadata`, `field-metadata`, `webhook`, `api-key`
- `view`, `view-sort`, `view-group`, `view-filter`, `view-filter-group`,
`view-field`
  - `page-layout`, `page-layout-widget`, `page-layout-tab`
  - `front-component`, `ai-generate-text`
- Unit tests covering 403 / 400 / 404 / 500 mappings.

## Mapping

| Code | Status |
|------|--------|
| `PERMISSION_DENIED`, `NO_AUTHENTICATION_CONTEXT`,
`ROLE_LABEL_ALREADY_EXISTS`, `CANNOT_UNASSIGN_LAST_ADMIN`,
`CANNOT_UPDATE_SELF_ROLE`, `CANNOT_DELETE_LAST_ADMIN_USER`,
`ROLE_NOT_EDITABLE`, `CANNOT_ADD_OBJECT_PERMISSION_ON_SYSTEM_OBJECT`,
`CANNOT_ADD_FIELD_PERMISSION_ON_SYSTEM_OBJECT` | **403** |
| `INVALID_ARG`, `INVALID_SETTING`,
`CANNOT_GIVE_WRITING_PERMISSION_ON_NON_READABLE_OBJECT`,
`CANNOT_GIVE_WRITING_PERMISSION_WITHOUT_READING_PERMISSION`,
`ONLY_FIELD_RESTRICTION_ALLOWED`,
`FIELD_RESTRICTION_ONLY_ALLOWED_ON_READABLE_OBJECT`,
`FIELD_RESTRICTION_ON_UPDATE_ONLY_ALLOWED_ON_UPDATABLE_OBJECT`,
`EMPTY_FIELD_PERMISSION_NOT_ALLOWED`,
`ROLE_MUST_HAVE_AT_LEAST_ONE_TARGET`, `ROLE_CANNOT_BE_ASSIGNED_TO_USERS`
| **400** |
| `ROLE_NOT_FOUND`, `OBJECT_METADATA_NOT_FOUND`,
`FIELD_METADATA_NOT_FOUND`, `FIELD_PERMISSION_NOT_FOUND`,
`PERMISSION_NOT_FOUND` | **404** |
| All remaining "internal" codes (rethrown as-is in GraphQL) | **500** |

## Before
<img width="507" height="216" alt="Screenshot 2026-05-19 at 19 26 07"
src="https://github.com/user-attachments/assets/21d633aa-7ee8-4923-94e4-7ad57258a29e"
/>

## After
<img width="610" height="385" alt="Screenshot 2026-05-19 at 19 26 01"
src="https://github.com/user-attachments/assets/0103b7ee-7df7-4aef-999a-73c22901afd2"
/>
2026-05-20 13:02:58 +00:00
458 changed files with 10260 additions and 10953 deletions
+1 -1
View File
@@ -50,4 +50,4 @@ runs:
- name: Deploy
shell: bash
working-directory: ${{ inputs.app-path }}
run: yarn twenty deploy --remote target
run: yarn twenty app:publish --private --remote target
@@ -50,4 +50,4 @@ runs:
- name: Install
shell: bash
working-directory: ${{ inputs.app-path }}
run: yarn twenty install --remote target
run: yarn twenty app:install --remote target
@@ -105,7 +105,7 @@ jobs:
create-twenty-app --version
mkdir -p /tmp/e2e-test-workspace
cd /tmp/e2e-test-workspace
create-twenty-app test-app --display-name "Test scaffolded app" --description "E2E test scaffolded app" --workspace-url http://localhost:3000
create-twenty-app test-app --display-name "Test scaffolded app" --description "E2E test scaffolded app" --url http://localhost:3000
- name: Install scaffolded app dependencies
run: |
@@ -153,7 +153,7 @@ jobs:
- name: Authenticate with twenty-server
run: |
cd /tmp/e2e-test-workspace/test-app
npx --no-install twenty remote add --api-key ${{ env.TWENTY_API_KEY }} --api-url ${{ env.TWENTY_API_URL }}
npx --no-install twenty remote:add --api-key ${{ env.TWENTY_API_KEY }} --url ${{ env.TWENTY_API_URL }}
- name: Run scaffolded app integration test (deploys, installs, and verifies the app)
run: |
+4
View File
@@ -10,4 +10,8 @@ nodeLinker: node-modules
npmMinimalAgeGate: 3d
npmPreapprovedPackages:
- twenty-sdk
- twenty-client-sdk
yarnPath: .yarn/releases/yarn-4.13.0.cjs
+1 -1
View File
@@ -63,7 +63,7 @@ export default defineObject({
Then ship it to your workspace:
```bash
npx twenty deploy
npx twenty app:publish --private
```
See the [app development guide](https://docs.twenty.com/developers/extend/apps/getting-started) for objects, views, agents, and logic functions.
+3 -3
View File
@@ -35,7 +35,7 @@ The scaffolder will:
| `--name <name>` | Set the app name |
| `--display-name <displayName>` | Set the display name |
| `--description <description>` | Set the description |
| `--workspace-url <url>` | Twenty workspace URL (default: `http://localhost:2020`) |
| `--url <url>` | Twenty workspace URL (default: `http://localhost:2020`) |
| `--authentication-method <method>` | `oauth` or `apiKey` (default: `apiKey` for local, `oauth` for remote) |
## Documentation
@@ -48,8 +48,8 @@ Full documentation is available at **[docs.twenty.com/developers/extend/apps](ht
## Troubleshooting
- Server not starting: check Docker is running (`docker info`), then try `yarn twenty server logs`.
- Auth not working: run `yarn twenty remote add --local` to re-authenticate.
- Server not starting: check Docker is running (`docker info`), then try `yarn twenty docker:logs`.
- Auth not working: run `yarn twenty remote:add --local` to re-authenticate.
- Types not generated: ensure `yarn twenty dev` is running — it auto-generates the typed client.
## Contributing
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "create-twenty-app",
"version": "2.6.0",
"version": "2.7.0",
"description": "Command-line interface to create Twenty application",
"main": "dist/cli.cjs",
"bin": "dist/cli.cjs",
+6 -14
View File
@@ -18,11 +18,8 @@ const program = new Command(packageJson.name)
.option('-n, --name <name>', 'Application name')
.option('-d, --display-name <displayName>', 'Application display name')
.option('--description <description>', 'Application description')
.option(
'--workspace-url <workspaceUrl>',
'Twenty workspace URL (default: http://localhost:2020)',
)
.option('--api-url <apiUrl>', '[deprecated: use --workspace-url]')
.option('--url <url>', 'Twenty server URL (default: http://localhost:2020)')
.option('--api-url <apiUrl>', '[deprecated: use --url]')
.option(
'--authentication-method <method>',
'Authentication method: oauth or apiKey (default: apiKey for local, oauth for remote)',
@@ -35,7 +32,7 @@ const program = new Command(packageJson.name)
name?: string;
displayName?: string;
description?: string;
workspaceUrl?: string;
url?: string;
apiUrl?: string;
authenticationMethod?: AuthenticationMethod;
},
@@ -68,23 +65,18 @@ const program = new Command(packageJson.name)
if (options?.apiUrl) {
console.warn(
chalk.yellow(
'Warning: --api-url is deprecated. Use --workspace-url instead.',
),
chalk.yellow('Warning: --api-url is deprecated. Use --url instead.'),
);
}
const workspaceUrl = (options?.workspaceUrl ?? options?.apiUrl)?.replace(
/\/+$/,
'',
);
const serverUrl = (options?.url ?? options?.apiUrl)?.replace(/\/+$/, '');
await new CreateAppCommand().execute({
directory,
name: options?.name,
displayName: options?.displayName,
description: options?.description,
workspaceUrl,
serverUrl,
authenticationMethod: options?.authenticationMethod,
});
},
@@ -49,19 +49,19 @@
## Best practice
It's highly recommended to create new app entities using `yarn twenty add`. These are the options:
It's highly recommended to create new app entities using `yarn twenty dev:add`. These are the options:
| Entity type | Command | Generated file |
| -------------------- | ------------------------------------ | ------------------------------------- |
| Object | `yarn twenty add object` | `src/objects/<name>.ts` |
| Field | `yarn twenty add field` | `src/fields/<name>.ts` |
| Logic function | `yarn twenty add logicFunction` | `src/logic-functions/<name>.ts` |
| Front component | `yarn twenty add frontComponent` | `src/front-components/<name>.tsx` |
| Role | `yarn twenty add role` | `src/roles/<name>.ts` |
| Skill | `yarn twenty add skill` | `src/skills/<name>.ts` |
| Agent | `yarn twenty add agent` | `src/agents/<name>.ts` |
| View | `yarn twenty add view` | `src/views/<name>.ts` |
| Navigation menu item | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/<name>.ts` |
| Page layout | `yarn twenty add pageLayout` | `src/page-layouts/<name>.ts` |
| Entity type | Command | Generated file |
| -------------------- | ---------------------------------------- | ------------------------------------- |
| Object | `yarn twenty dev:add object` | `src/objects/<name>.ts` |
| Field | `yarn twenty dev:add field` | `src/fields/<name>.ts` |
| Logic function | `yarn twenty dev:add logicFunction` | `src/logic-functions/<name>.ts` |
| Front component | `yarn twenty dev:add frontComponent` | `src/front-components/<name>.tsx` |
| Role | `yarn twenty dev:add role` | `src/roles/<name>.ts` |
| Skill | `yarn twenty dev:add skill` | `src/skills/<name>.ts` |
| Agent | `yarn twenty dev:add agent` | `src/agents/<name>.ts` |
| View | `yarn twenty dev:add view` | `src/views/<name>.ts` |
| Navigation menu item | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/<name>.ts` |
| Page layout | `yarn twenty dev:add pageLayout` | `src/page-layouts/<name>.ts` |
This helps automatically generate required IDs etc.
@@ -11,8 +11,8 @@ Run `yarn twenty help` to list all available commands.
## Useful Commands
- `yarn twenty dev` - Start the development server and sync your app
- `yarn twenty server status` - Check the local Twenty server status
- `yarn twenty server start` - Start the local Twenty server
- `yarn twenty docker:status` - Check the local Twenty server status
- `yarn twenty docker:start` - Start the local Twenty server
- `yarn test` - Run integration tests
## Learn More
@@ -14,7 +14,7 @@ function validateEnv(): { apiUrl: string; apiKey: string } {
if (!apiUrl || !apiKey) {
throw new Error(
'TWENTY_API_URL and TWENTY_API_KEY must be set.\n' +
'Start a local server: yarn twenty server start\n' +
'Start a local server: yarn twenty docker:start\n' +
'Or set them in vitest env config.',
);
}
@@ -33,7 +33,7 @@ type CreateAppOptions = {
name?: string;
displayName?: string;
description?: string;
workspaceUrl?: string;
serverUrl?: string;
authenticationMethod?: AuthenticationMethod;
};
@@ -45,9 +45,9 @@ export class CreateAppCommand {
const { appName, appDisplayName, appDirectory, appDescription } =
this.getAppInfos(options);
const workspaceUrl = options.workspaceUrl ?? DEV_API_URL;
const serverUrl = options.serverUrl ?? DEV_API_URL;
const skipLocalInstance = workspaceUrl !== DEV_API_URL;
const skipLocalInstance = serverUrl !== DEV_API_URL;
if (!skipLocalInstance && !isDockerInstalled()) {
console.log(chalk.yellow('\n' + getDockerInstallInstructions() + '\n'));
@@ -118,7 +118,7 @@ export class CreateAppCommand {
console.log('');
let authSucceeded = false;
let resolvedWorkspaceUrl = workspaceUrl;
let resolvedServerUrl = serverUrl;
let serverReady = skipLocalInstance;
if (!skipLocalInstance) {
@@ -126,7 +126,7 @@ export class CreateAppCommand {
const serverResult = await this.ensureDockerServer(dockerPullPromise);
if (isDefined(serverResult.url)) {
resolvedWorkspaceUrl = serverResult.url;
resolvedServerUrl = serverResult.url;
serverReady = true;
}
}
@@ -134,18 +134,16 @@ export class CreateAppCommand {
if (serverReady) {
this.logNextStep('Authenticating');
authSucceeded = await this.tryExistingAuth(resolvedWorkspaceUrl);
authSucceeded = await this.tryExistingAuth(resolvedServerUrl);
if (authSucceeded) {
this.logDetail('Reusing existing credentials');
} else if (authenticationMethod === 'oauth') {
this.logDetail('Starting OAuth flow');
authSucceeded =
await this.authenticateWithOAuth(resolvedWorkspaceUrl);
authSucceeded = await this.authenticateWithOAuth(resolvedServerUrl);
} else {
this.logDetail('Using development API key');
authSucceeded =
await this.authenticateWithDevKey(resolvedWorkspaceUrl);
authSucceeded = await this.authenticateWithDevKey(resolvedServerUrl);
}
}
@@ -165,10 +163,10 @@ export class CreateAppCommand {
}
if (syncSucceeded) {
await this.openMainPage(appDirectory, resolvedWorkspaceUrl);
await this.openMainPage(appDirectory, resolvedServerUrl);
}
this.logSuccess(appDirectory, resolvedWorkspaceUrl, authSucceeded);
this.logSuccess(appDirectory, resolvedServerUrl, authSucceeded);
} catch (error) {
console.error(
chalk.red('\nCreate application failed:'),
@@ -315,7 +313,7 @@ export class CreateAppCommand {
private async openMainPage(
appDirectory: string,
workspaceUrl: string,
serverUrl: string,
): Promise<void> {
try {
const configService = new ConfigService();
@@ -328,7 +326,7 @@ export class CreateAppCommand {
const [universalIdentifier, frontUrl] = await Promise.all([
this.readMainPageLayoutUniversalIdentifier(appDirectory),
this.resolveWorkspaceFrontUrl(workspaceUrl, token),
this.resolveWorkspaceFrontUrl(serverUrl, token),
]);
if (!universalIdentifier || !frontUrl) {
@@ -336,7 +334,7 @@ export class CreateAppCommand {
}
const pageLayoutId = await this.resolvePageLayoutId(
workspaceUrl,
serverUrl,
universalIdentifier,
token,
);
@@ -355,12 +353,12 @@ export class CreateAppCommand {
}
private async resolveWorkspaceFrontUrl(
workspaceUrl: string,
serverUrl: string,
token: string,
): Promise<string | null> {
const query = `{ currentWorkspace { workspaceUrls { subdomainUrl customUrl } } }`;
const response = await fetch(`${workspaceUrl}/metadata`, {
const response = await fetch(`${serverUrl}/metadata`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -410,13 +408,13 @@ export class CreateAppCommand {
}
private async resolvePageLayoutId(
workspaceUrl: string,
serverUrl: string,
universalIdentifier: string,
token: string,
): Promise<string | null> {
const query = `{ getPageLayouts { id universalIdentifier } }`;
const response = await fetch(`${workspaceUrl}/metadata`, {
const response = await fetch(`${serverUrl}/metadata`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -503,7 +501,7 @@ export class CreateAppCommand {
});
}
private async tryExistingAuth(workspaceUrl: string): Promise<boolean> {
private async tryExistingAuth(serverUrl: string): Promise<boolean> {
try {
const configService = new ConfigService();
const remoteNames = await configService.getRemotes();
@@ -511,7 +509,7 @@ export class CreateAppCommand {
for (const remoteName of remoteNames) {
const remoteConfig = await configService.getConfigForRemote(remoteName);
if (remoteConfig.apiUrl !== workspaceUrl) {
if (remoteConfig.apiUrl !== serverUrl) {
continue;
}
@@ -521,7 +519,7 @@ export class CreateAppCommand {
continue;
}
const response = await fetch(`${workspaceUrl}/metadata`, {
const response = await fetch(`${serverUrl}/metadata`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -555,11 +553,11 @@ export class CreateAppCommand {
}
}
private async authenticateWithDevKey(workspaceUrl: string): Promise<boolean> {
private async authenticateWithDevKey(serverUrl: string): Promise<boolean> {
try {
const result = await authLogin({
apiKey: DEV_API_KEY,
apiUrl: workspaceUrl,
apiUrl: serverUrl,
remote: 'local',
});
@@ -574,7 +572,7 @@ export class CreateAppCommand {
console.log(
chalk.yellow(
' Authentication failed. Run `yarn twenty remote add --local` manually.',
' Authentication failed. Run `yarn twenty remote:add --local` manually.',
),
);
@@ -582,7 +580,7 @@ export class CreateAppCommand {
} catch {
console.log(
chalk.yellow(
' Authentication failed. Run `yarn twenty remote add --local` manually.',
' Authentication failed. Run `yarn twenty remote:add --local` manually.',
),
);
@@ -598,21 +596,21 @@ export class CreateAppCommand {
}
}
private async authenticateWithOAuth(workspaceUrl: string): Promise<boolean> {
private async authenticateWithOAuth(serverUrl: string): Promise<boolean> {
try {
const remoteName = this.deriveRemoteName(workspaceUrl);
const remoteName = this.deriveRemoteName(serverUrl);
ConfigService.setActiveRemote(remoteName);
this.logDetail('Opening browser for OAuth...');
const result = await authLoginOAuth({ apiUrl: workspaceUrl });
const result = await authLoginOAuth({ apiUrl: serverUrl });
if (result.success) {
const configService = new ConfigService();
await configService.setDefaultRemote(remoteName);
this.logDetail(`Authenticated via OAuth to ${workspaceUrl}`);
this.logDetail(`Authenticated via OAuth to ${serverUrl}`);
return true;
}
@@ -620,7 +618,7 @@ export class CreateAppCommand {
console.log(
chalk.yellow(
` OAuth failed: ${result.error.message}\n` +
` Run \`yarn twenty remote add --api-url ${workspaceUrl}\` manually.`,
` Run \`yarn twenty remote:add --url ${serverUrl}\` manually.`,
),
);
@@ -628,7 +626,7 @@ export class CreateAppCommand {
} catch {
console.log(
chalk.yellow(
` Authentication failed. Run \`yarn twenty remote add --api-url ${workspaceUrl}\` manually.`,
` Authentication failed. Run \`yarn twenty remote:add --url ${serverUrl}\` manually.`,
),
);
@@ -638,7 +636,7 @@ export class CreateAppCommand {
private logSuccess(
appDirectory: string,
workspaceUrl: string,
serverUrl: string,
authSucceeded: boolean,
): void {
const dirName = basename(appDirectory);
@@ -656,9 +654,7 @@ export class CreateAppCommand {
if (!authSucceeded) {
console.log(chalk.white(` ${stepNumber}. Connect to a Twenty instance`));
console.log(
chalk.cyan(
' yarn twenty remote add --api-url <your-instance-url>\n',
),
chalk.cyan(' yarn twenty remote:add --url <your-instance-url>\n'),
);
stepNumber++;
}
@@ -668,7 +664,7 @@ export class CreateAppCommand {
stepNumber++;
console.log(chalk.white(` ${stepNumber}. Open your twenty instance`));
console.log(chalk.cyan(` ${workspaceUrl}\n`));
console.log(chalk.cyan(` ${serverUrl}\n`));
console.log(
chalk.gray(
@@ -28,6 +28,6 @@ export const getDockerInstallInstructions = (): string => {
' Then run this command again.',
'',
' Alternatively, connect to an existing Twenty instance:',
' npx create-twenty-app@latest my-twenty-app --workspace-url <your-instance-url>',
' npx create-twenty-app@latest my-twenty-app --url <your-instance-url>',
].join('\n');
};
@@ -93,7 +93,7 @@ yarn install
# Register your local Twenty server as a remote (interactive prompt).
# When asked for the URL use http://localhost:2021 and paste an API key
# from Settings -> Developers in the Twenty UI.
yarn twenty remote add
yarn twenty remote:add
# Build, install, and watch for changes.
yarn twenty dev
@@ -107,8 +107,8 @@ watching `src/`. Edit any file and the change is re-synced within seconds.
```bash
cd packages/twenty-apps/community/github-connector
yarn install
yarn twenty remote add # same prompts as above
yarn twenty install # builds and installs once
yarn twenty remote:add # same prompts as above
yarn twenty app:install # builds and installs once
```
## Configure authentication
@@ -5,7 +5,7 @@ This is a [Twenty](https://twenty.com) application project bootstrapped with [`c
First, authenticate to your workspace:
```bash
yarn twenty remote add --api-url http://localhost:2020 --as local
yarn twenty remote:add --api-url http://localhost:2020 --as local
```
Then, start development mode to sync your app and watch for changes:
@@ -22,18 +22,18 @@ Run `yarn twenty help` to list all available commands. Common commands:
```bash
# Remotes & Authentication
yarn twenty remote add --api-url http://localhost:2020 --as local # Authenticate with Twenty
yarn twenty remote status # Check auth status
yarn twenty remote switch # Switch default remote
yarn twenty remote list # List all configured remotes
yarn twenty remote remove <name> # Remove a remote
yarn twenty remote:add --api-url http://localhost:2020 --as local # Authenticate with Twenty
yarn twenty remote:status # Check auth status
yarn twenty remote:use # Set default remote
yarn twenty remote:list # List all configured remotes
yarn twenty remote:remove <name> # Remove a remote
# Application
yarn twenty dev # Start dev mode (watch, build, sync, and auto-generate typed client)
yarn twenty add # Add a new entity (object, field, function, front-component, role, view, navigation-menu-item)
yarn twenty logs # Stream function logs
yarn twenty exec # Execute a function with JSON payload
yarn twenty uninstall # Uninstall app from workspace
yarn twenty dev # Start dev mode (watch, build, sync, and auto-generate typed client)
yarn twenty dev:add # Scaffold a new entity (object, field, function, front-component, role, view, navigation-menu-item)
yarn twenty dev:function:logs # Stream function logs
yarn twenty dev:function:exec # Execute a function with JSON payload
yarn twenty app:uninstall # Uninstall app from workspace
```
## Integration Tests
@@ -13,7 +13,7 @@ beforeAll(async () => {
if (!apiUrl || !token) {
throw new Error(
'TWENTY_API_URL and TWENTY_API_KEY must be set.\n' +
'Start a local server: yarn twenty server start\n' +
'Start a local server: yarn twenty docker:start\n' +
'Or set them in vitest env config.',
);
}
@@ -14,7 +14,7 @@ function validateEnv(): { apiUrl: string; apiKey: string } {
if (!apiUrl || !apiKey) {
throw new Error(
'TWENTY_API_URL and TWENTY_API_KEY must be set.\n' +
'Start a local server: yarn twenty server start\n' +
'Start a local server: yarn twenty docker:start\n' +
'Or set them in vitest env config.',
);
}
@@ -10,8 +10,8 @@
"packageManager": "[email protected]",
"scripts": {
"dev": "twenty dev",
"exec": "twenty exec",
"uninstall": "twenty uninstall",
"exec": "twenty dev:fn-exec",
"uninstall": "twenty app:uninstall",
"lint": "oxlint -c .oxlintrc.json .",
"lint:fix": "oxlint --fix -c .oxlintrc.json ."
},
@@ -5,7 +5,7 @@ This is a [Twenty](https://twenty.com) application project bootstrapped with [`c
First, authenticate to your workspace:
```bash
yarn twenty remote add --api-url http://localhost:2020 --as local
yarn twenty remote:add --api-url http://localhost:2020 --as local
```
Then, start development mode to sync your app and watch for changes:
@@ -22,18 +22,18 @@ Run `yarn twenty help` to list all available commands. Common commands:
```bash
# Remotes & Authentication
yarn twenty remote add --api-url http://localhost:2020 --as local # Authenticate with Twenty
yarn twenty remote status # Check auth status
yarn twenty remote switch # Switch default remote
yarn twenty remote list # List all configured remotes
yarn twenty remote remove <name> # Remove a remote
yarn twenty remote:add --api-url http://localhost:2020 --as local # Authenticate with Twenty
yarn twenty remote:status # Check auth status
yarn twenty remote:use # Set default remote
yarn twenty remote:list # List all configured remotes
yarn twenty remote:remove <name> # Remove a remote
# Application
yarn twenty dev # Start dev mode (watch, build, sync, and auto-generate typed client)
yarn twenty add # Add a new entity (object, field, function, front-component, role, view, navigation-menu-item)
yarn twenty logs # Stream function logs
yarn twenty exec # Execute a function with JSON payload
yarn twenty uninstall # Uninstall app from workspace
yarn twenty dev # Start dev mode (watch, build, sync, and auto-generate typed client)
yarn twenty dev:add # Scaffold a new entity (object, field, function, front-component, role, view, navigation-menu-item)
yarn twenty dev:function:logs # Stream function logs
yarn twenty dev:function:exec # Execute a function with JSON payload
yarn twenty app:uninstall # Uninstall app from workspace
```
## LLMs instructions
@@ -13,7 +13,7 @@ beforeAll(async () => {
if (!apiUrl || !token) {
throw new Error(
'TWENTY_API_URL and TWENTY_API_KEY must be set.\n' +
'Start a local server: yarn twenty server start\n' +
'Start a local server: yarn twenty docker:start\n' +
'Or set them in vitest env config.',
);
}
@@ -14,7 +14,7 @@ function validateEnv(): { apiUrl: string; apiKey: string } {
if (!apiUrl || !apiKey) {
throw new Error(
'TWENTY_API_URL and TWENTY_API_KEY must be set.\n' +
'Start a local server: yarn twenty server start\n' +
'Start a local server: yarn twenty docker:start\n' +
'Or set them in vitest env config.',
);
}
@@ -64,15 +64,15 @@ cd packages/twenty-apps/internal/twenty-linear
# For day-to-day development (publish + install + watch in one):
yarn twenty dev
# Manual publish flow (deploy registers the app, install activates it):
yarn twenty deploy
yarn twenty install
# Manual publish flow (publish registers the app, install activates it):
yarn twenty app:publish --private
yarn twenty app:install
```
`twenty dev` is recommended for iteration — it publishes, installs, and
watches for changes in one command. Use `twenty deploy` + `twenty install`
when you want to control each step separately (e.g. deploying to a
production server without auto-installing).
watches for changes in one command. Use `twenty app:publish --private` +
`twenty app:install` when you want to control each step separately (e.g.
deploying to a production server without auto-installing).
This serves as the reference implementation for Twenty's
`defineConnectionProvider({ type: 'oauth' })` flow — useful as a template
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "twenty-client-sdk",
"version": "2.6.0",
"version": "2.7.0",
"sideEffects": false,
"license": "AGPL-3.0",
"scripts": {
@@ -1,2 +1,2 @@
// Stub — overwritten by `twenty build` or `twenty dev`
// Stub — overwritten by `twenty dev:build` or `twenty dev`
export type CoreSchema = {};
@@ -1764,6 +1764,18 @@ type WorkspaceUrls {
subdomainUrl: String!
}
type ApplicationRegistrationVariableDTO {
id: UUID!
key: String!
value: String
description: String!
isSecret: Boolean!
isRequired: Boolean!
isFilled: Boolean!
createdAt: DateTime!
updatedAt: DateTime!
}
type BillingTrialPeriod {
duration: Float!
isCreditCardRequired: Boolean!
@@ -1968,18 +1980,6 @@ type RotateClientSecret {
clientSecret: String!
}
type ApplicationRegistrationVariableDTO {
id: UUID!
key: String!
value: String
description: String!
isSecret: Boolean!
isRequired: Boolean!
isFilled: Boolean!
createdAt: DateTime!
updatedAt: DateTime!
}
type Relation {
type: RelationType!
sourceObjectMetadata: Object!
@@ -3315,7 +3315,7 @@ type Mutation {
deleteEmailingDomain(id: String!): Boolean!
verifyEmailingDomain(id: String!): EmailingDomain!
createOneAppToken(input: CreateOneAppTokenInput!): AppToken!
installMarketplaceApp(universalIdentifier: String!, version: String): Boolean!
installMarketplaceApp(universalIdentifier: String!, version: String): Application!
syncMarketplaceCatalog: Boolean!
createDevelopmentApplication(universalIdentifier: String!, name: String!): DevelopmentApplication!
generateApplicationToken(applicationId: UUID!): ApplicationTokenPair!
@@ -1401,6 +1401,19 @@ export interface WorkspaceUrls {
__typename: 'WorkspaceUrls'
}
export interface ApplicationRegistrationVariableDTO {
id: Scalars['UUID']
key: Scalars['String']
value?: Scalars['String']
description: Scalars['String']
isSecret: Scalars['Boolean']
isRequired: Scalars['Boolean']
isFilled: Scalars['Boolean']
createdAt: Scalars['DateTime']
updatedAt: Scalars['DateTime']
__typename: 'ApplicationRegistrationVariableDTO'
}
export interface BillingTrialPeriod {
duration: Scalars['Float']
isCreditCardRequired: Scalars['Boolean']
@@ -1609,19 +1622,6 @@ export interface RotateClientSecret {
__typename: 'RotateClientSecret'
}
export interface ApplicationRegistrationVariableDTO {
id: Scalars['UUID']
key: Scalars['String']
value?: Scalars['String']
description: Scalars['String']
isSecret: Scalars['Boolean']
isRequired: Scalars['Boolean']
isFilled: Scalars['Boolean']
createdAt: Scalars['DateTime']
updatedAt: Scalars['DateTime']
__typename: 'ApplicationRegistrationVariableDTO'
}
export interface Relation {
type: RelationType
sourceObjectMetadata: Object
@@ -2848,7 +2848,7 @@ export interface Mutation {
deleteEmailingDomain: Scalars['Boolean']
verifyEmailingDomain: EmailingDomain
createOneAppToken: AppToken
installMarketplaceApp: Scalars['Boolean']
installMarketplaceApp: Application
syncMarketplaceCatalog: Scalars['Boolean']
createDevelopmentApplication: DevelopmentApplication
generateApplicationToken: ApplicationTokenPair
@@ -4332,6 +4332,20 @@ export interface WorkspaceUrlsGenqlSelection{
__scalar?: boolean | number
}
export interface ApplicationRegistrationVariableDTOGenqlSelection{
id?: boolean | number
key?: boolean | number
value?: boolean | number
description?: boolean | number
isSecret?: boolean | number
isRequired?: boolean | number
isFilled?: boolean | number
createdAt?: boolean | number
updatedAt?: boolean | number
__typename?: boolean | number
__scalar?: boolean | number
}
export interface BillingTrialPeriodGenqlSelection{
duration?: boolean | number
isCreditCardRequired?: boolean | number
@@ -4553,20 +4567,6 @@ export interface RotateClientSecretGenqlSelection{
__scalar?: boolean | number
}
export interface ApplicationRegistrationVariableDTOGenqlSelection{
id?: boolean | number
key?: boolean | number
value?: boolean | number
description?: boolean | number
isSecret?: boolean | number
isRequired?: boolean | number
isFilled?: boolean | number
createdAt?: boolean | number
updatedAt?: boolean | number
__typename?: boolean | number
__scalar?: boolean | number
}
export interface RelationGenqlSelection{
type?: boolean | number
sourceObjectMetadata?: ObjectGenqlSelection
@@ -5900,7 +5900,7 @@ export interface MutationGenqlSelection{
deleteEmailingDomain?: { __args: {id: Scalars['String']} }
verifyEmailingDomain?: (EmailingDomainGenqlSelection & { __args: {id: Scalars['String']} })
createOneAppToken?: (AppTokenGenqlSelection & { __args: {input: CreateOneAppTokenInput} })
installMarketplaceApp?: { __args: {universalIdentifier: Scalars['String'], version?: (Scalars['String'] | null)} }
installMarketplaceApp?: (ApplicationGenqlSelection & { __args: {universalIdentifier: Scalars['String'], version?: (Scalars['String'] | null)} })
syncMarketplaceCatalog?: boolean | number
createDevelopmentApplication?: (DevelopmentApplicationGenqlSelection & { __args: {universalIdentifier: Scalars['String'], name: Scalars['String']} })
generateApplicationToken?: (ApplicationTokenPairGenqlSelection & { __args: {applicationId: Scalars['UUID']} })
@@ -7183,6 +7183,14 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null
const ApplicationRegistrationVariableDTO_possibleTypes: string[] = ['ApplicationRegistrationVariableDTO']
export const isApplicationRegistrationVariableDTO = (obj?: { __typename?: any } | null): obj is ApplicationRegistrationVariableDTO => {
if (!obj?.__typename) throw new Error('__typename is missing in "isApplicationRegistrationVariableDTO"')
return ApplicationRegistrationVariableDTO_possibleTypes.includes(obj.__typename)
}
const BillingTrialPeriod_possibleTypes: string[] = ['BillingTrialPeriod']
export const isBillingTrialPeriod = (obj?: { __typename?: any } | null): obj is BillingTrialPeriod => {
if (!obj?.__typename) throw new Error('__typename is missing in "isBillingTrialPeriod"')
@@ -7367,14 +7375,6 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null
const ApplicationRegistrationVariableDTO_possibleTypes: string[] = ['ApplicationRegistrationVariableDTO']
export const isApplicationRegistrationVariableDTO = (obj?: { __typename?: any } | null): obj is ApplicationRegistrationVariableDTO => {
if (!obj?.__typename) throw new Error('__typename is missing in "isApplicationRegistrationVariableDTO"')
return ApplicationRegistrationVariableDTO_possibleTypes.includes(obj.__typename)
}
const Relation_possibleTypes: string[] = ['Relation']
export const isRelation = (obj?: { __typename?: any } | null): obj is Relation => {
if (!obj?.__typename) throw new Error('__typename is missing in "isRelation"')
@@ -51,11 +51,11 @@ export default {
163,
167,
169,
173,
174,
181,
184,
187,
175,
182,
185,
188,
201,
226,
262,
@@ -3434,6 +3434,38 @@ export default {
1
]
},
"ApplicationRegistrationVariableDTO": {
"id": [
3
],
"key": [
1
],
"value": [
1
],
"description": [
1
],
"isSecret": [
6
],
"isRequired": [
6
],
"isFilled": [
6
],
"createdAt": [
4
],
"updatedAt": [
4
],
"__typename": [
1
]
},
"BillingTrialPeriod": {
"duration": [
11
@@ -3453,10 +3485,10 @@ export default {
1
],
"type": [
173
174
],
"status": [
174
175
],
"issuer": [
1
@@ -3469,7 +3501,7 @@ export default {
"SSOIdentityProviderStatus": {},
"AuthProviders": {
"sso": [
172
173
],
"google": [
6
@@ -3506,10 +3538,10 @@ export default {
3
],
"authProviders": [
175
176
],
"authBypassProviders": [
176
177
],
"logo": [
1
@@ -3557,7 +3589,7 @@ export default {
1
],
"modelFamily": [
181
182
],
"modelFamilyLabel": [
1
@@ -3572,7 +3604,7 @@ export default {
11
],
"nativeCapabilities": [
179
180
],
"isDeprecated": [
6
@@ -3608,7 +3640,7 @@ export default {
1
],
"trialPeriods": [
171
172
],
"__typename": [
1
@@ -3616,7 +3648,7 @@ export default {
},
"Support": {
"supportDriver": [
184
185
],
"supportFrontChatId": [
1
@@ -3642,7 +3674,7 @@ export default {
},
"Captcha": {
"provider": [
187
188
],
"siteKey": [
1
@@ -3679,7 +3711,7 @@ export default {
169
],
"metadata": [
189
190
],
"__typename": [
1
@@ -3704,13 +3736,13 @@ export default {
1
],
"authProviders": [
175
176
],
"billing": [
182
183
],
"aiModels": [
180
181
],
"signInPrefilled": [
6
@@ -3731,25 +3763,25 @@ export default {
6
],
"support": [
183
184
],
"isAttachmentPreviewEnabled": [
6
],
"sentry": [
185
],
"captcha": [
186
],
"captcha": [
187
],
"api": [
188
189
],
"canManageFeatureFlags": [
6
],
"publicFeatureFlags": [
190
191
],
"isMicrosoftMessagingEnabled": [
6
@@ -3788,7 +3820,7 @@ export default {
6
],
"maintenance": [
191
192
],
"__typename": [
1
@@ -3827,7 +3859,7 @@ export default {
1
],
"versionDistribution": [
194
195
],
"__typename": [
1
@@ -3872,38 +3904,6 @@ export default {
1
]
},
"ApplicationRegistrationVariableDTO": {
"id": [
3
],
"key": [
1
],
"value": [
1
],
"description": [
1
],
"isSecret": [
6
],
"isRequired": [
6
],
"isFilled": [
6
],
"createdAt": [
4
],
"updatedAt": [
4
],
"__typename": [
1
]
},
"Relation": {
"type": [
201
@@ -4095,7 +4095,7 @@ export default {
3
],
"type": [
173
174
],
"issuer": [
1
@@ -4104,7 +4104,7 @@ export default {
1
],
"status": [
174
175
],
"__typename": [
1
@@ -4123,7 +4123,7 @@ export default {
},
"FindAvailableSSOIDP": {
"type": [
173
174
],
"id": [
3
@@ -4135,7 +4135,7 @@ export default {
1
],
"status": [
174
175
],
"workspace": [
218
@@ -4149,7 +4149,7 @@ export default {
3
],
"type": [
173
174
],
"issuer": [
1
@@ -4158,7 +4158,7 @@ export default {
1
],
"status": [
174
175
],
"__typename": [
1
@@ -4166,7 +4166,7 @@ export default {
},
"SSOConnection": {
"type": [
173
174
],
"id": [
3
@@ -4178,7 +4178,7 @@ export default {
1
],
"status": [
174
175
],
"__typename": [
1
@@ -4529,13 +4529,13 @@ export default {
},
"UsageAnalytics": {
"usageByUser": [
193
194
],
"usageByOperationType": [
193
194
],
"usageByModel": [
193
194
],
"timeSeries": [
251
@@ -6293,7 +6293,7 @@ export default {
}
],
"findApplicationRegistrationByClientId": [
197,
198,
{
"clientId": [
1,
@@ -6323,7 +6323,7 @@ export default {
}
],
"findApplicationRegistrationStats": [
195,
196,
{
"id": [
1,
@@ -6332,7 +6332,7 @@ export default {
}
],
"findApplicationRegistrationVariables": [
199,
171,
{
"applicationRegistrationId": [
1,
@@ -6356,7 +6356,7 @@ export default {
75
],
"getPublicWorkspaceDataByDomain": [
177,
178,
{
"origin": [
1
@@ -6364,7 +6364,7 @@ export default {
}
],
"getPublicWorkspaceDataById": [
178,
179,
{
"id": [
3,
@@ -8197,7 +8197,7 @@ export default {
}
],
"createApplicationRegistration": [
196,
197,
{
"input": [
443,
@@ -8224,7 +8224,7 @@ export default {
}
],
"rotateApplicationRegistrationClientSecret": [
198,
199,
{
"id": [
1,
@@ -8602,7 +8602,7 @@ export default {
}
],
"installMarketplaceApp": [
6,
58,
{
"universalIdentifier": [
1,
@@ -11010,7 +11010,7 @@ export default {
3
],
"status": [
174
175
],
"__typename": [
1
+5 -1
View File
@@ -86,7 +86,11 @@ RUN if [ -d /app/packages/twenty-front/build ]; then \
FROM node:24.15.0-alpine3.23@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f AS twenty-server
RUN apk add --no-cache curl jq postgresql-client
RUN apk add --no-cache \
'curl>=8.19.0-r0' \
'nghttp2-libs>=1.69.0-r0' \
'postgresql18-client>=18.4-r0' \
jq
COPY ./packages/twenty-docker/twenty/entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
@@ -45,7 +45,7 @@ export default definePostInstallLogicFunction({
You can also manually execute the post-install function at any time using the CLI:
```bash filename="Terminal"
yarn twenty exec --postInstall
yarn twenty dev:function:exec --postInstall
```
Key points:
@@ -60,7 +60,7 @@ Key points:
- Only one post-install function is allowed per application. The manifest build will error if more than one is detected.
- The function's `universalIdentifier`, `shouldRunOnVersionUpgrade`, and `shouldRunSynchronously` are automatically attached to the application manifest under the `postInstallLogicFunction` field during the build — you do not need to reference them in [`defineApplication()`](/developers/extend/apps/config/application).
- The default timeout is set to 300 seconds (5 minutes) to allow for longer setup tasks like data seeding.
- **Not executed in dev mode**: when an app is registered locally (via `yarn twenty dev`), the server skips the install flow entirely and syncs files directly through the CLI watcher — so post-install never runs in dev mode, regardless of `shouldRunSynchronously`. Use `yarn twenty exec --postInstall` to trigger it manually against a running workspace.
- **Not executed in dev mode**: when an app is registered locally (via `yarn twenty dev`), the server skips the install flow entirely and syncs files directly through the CLI watcher — so post-install never runs in dev mode, regardless of `shouldRunSynchronously`. Use `yarn twenty dev:function:exec --postInstall` to trigger it manually against a running workspace.
</Accordion>
<Accordion title="definePreInstallLogicFunction" description="Runs before the workspace metadata migration is applied">
@@ -87,7 +87,7 @@ export default definePreInstallLogicFunction({
You can also manually execute the pre-install function at any time using the CLI:
```bash filename="Terminal"
yarn twenty exec --preInstall
yarn twenty dev:function:exec --preInstall
```
Key points:
@@ -96,7 +96,7 @@ Key points:
- **When the hook runs**: positioned just before the workspace metadata migration (`synchronizeFromManifest`). Before executing, the server runs a purely additive "pared-down sync" that registers the **new** version's pre-install function in the workspace metadata — nothing else is touched — and then executes it. Because this sync is additive-only, the previous version's objects, fields, and data are still intact when your handler runs: you can safely read and back up pre-migration state.
- **Execution model**: pre-install is executed **synchronously** and **blocks the install**. If the handler throws, the install is aborted before any schema changes are applied — the workspace stays on the previous version in a consistent state. This is intentional: pre-install is your last chance to refuse a risky upgrade.
- As with post-install, only one pre-install function is allowed per application. It is attached to the application manifest under `preInstallLogicFunction` automatically during the build.
- **Not executed in dev mode**: same as post-install — the install flow is skipped entirely for locally-registered apps, so pre-install never runs under `yarn twenty dev`. Use `yarn twenty exec --preInstall` to trigger it manually.
- **Not executed in dev mode**: same as post-install — the install flow is skipped entirely for locally-registered apps, so pre-install never runs under `yarn twenty dev`. Use `yarn twenty dev:function:exec --preInstall` to trigger it manually.
</Accordion>
<Accordion title="Pre-install vs post-install: when to use which" description="Choosing the right install hook">
@@ -13,7 +13,7 @@ Files placed in `public/` are:
- **Available in logic functions** — reference asset URLs in emails, API responses, or any server-side logic.
- **Used for marketplace metadata** — the `logoUrl` and `screenshots` fields in `defineApplication()` reference files from this folder (e.g., `public/logo.png`). These are displayed in the marketplace when your app is published.
- **Auto-synced in dev mode** — when you add, update, or delete a file in `public/`, it is synced to the server automatically. No restart needed.
- **Included in builds** — `yarn twenty build` bundles all public assets into the distribution output.
- **Included in builds** — `yarn twenty dev:build` bundles all public assets into the distribution output.
## Accessing public assets with `getPublicAssetUrl`
@@ -80,7 +80,7 @@ export default defineObject({
- Each field requires a `name`, `type`, `label`, and its own stable `universalIdentifier`.
- The `fields` array is optional — you can define objects without custom fields.
- Inline fields defined here do **not** need an `objectUniversalIdentifier` — it's inherited from the parent object. Use [`defineField()`](/developers/extend/apps/data/extending-objects) to add fields to objects you don't own.
- You can scaffold new objects with `yarn twenty add object`, which guides you through naming, fields, and relationships. See [Architecture → Scaffolding entities](/developers/extend/apps/getting-started/scaffolding).
- You can scaffold new objects with `yarn twenty dev:add object`, which guides you through naming, fields, and relationships. See [Architecture → Scaffolding entities](/developers/extend/apps/getting-started/scaffolding).
<Note>
**Base fields are added automatically.** When you define a custom object, Twenty creates standard fields like `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy`, and `deletedAt` for you. You don't need to declare them in your `fields` array — only your custom fields. You can override a default field by declaring one with the same name, but this is rarely a good idea.
@@ -65,7 +65,7 @@ your-app/
│ npx create-twenty-app → yarn twenty dev (live sync) │
├─────────────────────────────────────────────────────────┤
│ Build & Deploy │
│ yarn twenty build → yarn twenty deploy
│ yarn twenty dev:build → yarn twenty app:publish
├─────────────────────────────────────────────────────────┤
│ Install flow │
│ upload → [pre-install] → metadata migration → │
@@ -77,7 +77,7 @@ your-app/
```
- **`yarn twenty dev`** — watches your source files and live-syncs changes to a connected Twenty server. The typed API client is regenerated automatically when the schema changes.
- **`yarn twenty build`** — compiles TypeScript, bundles logic functions and front components with esbuild, and produces a manifest.
- **`yarn twenty dev:build`** — compiles TypeScript, bundles logic functions and front components with esbuild, and produces a manifest.
- **Pre/post-install hooks** — optional functions that run during installation. See [Install Hooks](/developers/extend/apps/config/install-hooks) for details.
## Next steps
@@ -6,44 +6,44 @@ icon: "server"
## Managing the local server
Use `yarn twenty server` to control the local Twenty container:
Use `yarn twenty docker:*` to control the local Twenty container:
| Command | What it does |
|---------|--------------|
| `yarn twenty server start` | Start the server (pulls the image if needed) |
| `yarn twenty server start --port 3030` | Start on a custom port |
| `yarn twenty server stop` | Stop the server (preserves data) |
| `yarn twenty server status` | Show URL, version, and login credentials |
| `yarn twenty server logs` | Stream server logs |
| `yarn twenty server reset` | Wipe data and start fresh |
| `yarn twenty server upgrade` | Pull the latest `twenty-app-dev` image |
| `yarn twenty server upgrade 2.2.0` | Upgrade to a specific version |
| `yarn twenty docker:start` | Start the server (pulls the image if needed) |
| `yarn twenty docker:start --port 3030` | Start on a custom port |
| `yarn twenty docker:stop` | Stop the server (preserves data) |
| `yarn twenty docker:status` | Show URL, version, and login credentials |
| `yarn twenty docker:logs` | Stream server logs |
| `yarn twenty docker:reset` | Wipe data and start fresh |
| `yarn twenty docker:upgrade` | Pull the latest `twenty-app-dev` image |
| `yarn twenty docker:upgrade 2.2.0` | Upgrade to a specific version |
Data persists across restarts in two Docker volumes (`twenty-app-dev-data` for PostgreSQL, `twenty-app-dev-storage` for files). Use `reset` to wipe everything.
## Upgrading the server image
`yarn twenty server upgrade` pulls the latest image, compares digests, and only recreates the container if anything actually changed. Volumes are preserved — only the container is replaced. If a new image was pulled and the container was running, the upgrade automatically starts a new container; run `yarn twenty server start` afterward to wait for it to become healthy.
`yarn twenty docker:upgrade` pulls the latest image, compares digests, and only recreates the container if anything actually changed. Volumes are preserved — only the container is replaced. If a new image was pulled and the container was running, the upgrade automatically starts a new container; run `yarn twenty docker:start` afterward to wait for it to become healthy.
```bash filename="Terminal"
yarn twenty server upgrade # Latest
yarn twenty server upgrade 2.2.0 # Specific version
yarn twenty docker:upgrade # Latest
yarn twenty docker:upgrade 2.2.0 # Specific version
```
Verify the running version with `yarn twenty server status` (it shows the `APP_VERSION` baked into the container).
Verify the running version with `yarn twenty docker:status` (it shows the `APP_VERSION` baked into the container).
## Running a parallel test instance
Pass `--test` to any `server` command to manage a second, fully isolated instance — useful for integration tests or experiments without touching your main dev data:
Pass `--test` to any `docker:*` command to manage a second, fully isolated instance — useful for integration tests or experiments without touching your main dev data:
| Command | What it does |
|---------|--------------|
| `yarn twenty server start --test` | Start the test instance (defaults to port 2021) |
| `yarn twenty server stop --test` | Stop it |
| `yarn twenty server status --test` | Show its status |
| `yarn twenty server logs --test` | Stream its logs |
| `yarn twenty server reset --test` | Wipe its data |
| `yarn twenty server upgrade --test` | Upgrade its image |
| `yarn twenty docker:start --test` | Start the test instance (defaults to port 2021) |
| `yarn twenty docker:stop --test` | Stop it |
| `yarn twenty docker:status --test` | Show its status |
| `yarn twenty docker:logs --test` | Stream its logs |
| `yarn twenty docker:reset --test` | Wipe its data |
| `yarn twenty docker:upgrade --test` | Upgrade its image |
The test instance has its own container (`twenty-app-dev-test`), volumes (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`), and config — it runs alongside your main instance without conflicts. Combine `--test` with `--port` to override 2021.
@@ -65,7 +65,7 @@ Add the script to `package.json`:
}
```
You can now run `yarn twenty dev`, `yarn twenty server start`, and the rest.
You can now run `yarn twenty dev`, `yarn twenty docker:start`, and the rest.
<Note>
Don't install `twenty-sdk` globally — pin it per project so each app uses its own version.
@@ -43,7 +43,7 @@ The scaffolder offers to start one for you:
> **Would you like to set up a local Twenty instance?**
- **Yes (recommended)** — pulls the `twentycrm/twenty-app-dev` Docker image and starts it on port `2020`. Make sure Docker is running first.
- **No** — choose this if you already have a Twenty server you want to connect to. You can wire it up later with `yarn twenty remote add`.
- **No** — choose this if you already have a Twenty server you want to connect to. You can wire it up later with `yarn twenty remote:add`.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/developers/extends/apps/start-instance.png" alt="Should start local instance?" />
@@ -73,7 +73,7 @@ Your terminal will confirm everything is set up.
**After this phase:** you have a running Twenty server at [http://localhost:2020](http://localhost:2020) with your CLI authorized to sync to it.
<Note>
If Docker isn't installed or running, the scaffolder will tell you the right start command for your OS. Once Docker is up, you can resume with `yarn twenty server start` — no need to re-scaffold.
If Docker isn't installed or running, the scaffolder will tell you the right start command for your OS. Once Docker is up, you can resume with `yarn twenty docker:start` — no need to re-scaffold.
</Note>
---
@@ -1,13 +1,13 @@
---
title: Scaffolding
description: Generate entity files interactively with yarn twenty add — objects, fields, views, logic functions, and more.
description: Generate entity files interactively with yarn twenty dev:add — objects, fields, views, logic functions, and more.
icon: "wand-magic-sparkles"
---
Instead of creating entity files by hand, use the interactive scaffolder:
```bash filename="Terminal"
yarn twenty add
yarn twenty dev:add
```
It prompts you to pick an entity type and walks you through the required fields, then writes a ready-to-use file with a stable `universalIdentifier` and the correct `defineEntity()` call.
@@ -15,29 +15,29 @@ It prompts you to pick an entity type and walks you through the required fields,
You can also pass the entity type directly to skip the first prompt:
```bash filename="Terminal"
yarn twenty add object
yarn twenty add logicFunction
yarn twenty add frontComponent
yarn twenty dev:add object
yarn twenty dev:add logicFunction
yarn twenty dev:add frontComponent
```
## Available entity types
| Entity type | Command | Generated file |
|-------------|---------|----------------|
| Object | `yarn twenty add object` | `src/objects/<name>.ts` |
| Field | `yarn twenty add field` | `src/fields/<name>.ts` |
| Logic function | `yarn twenty add logicFunction` | `src/logic-functions/<name>.ts` |
| Front component | `yarn twenty add frontComponent` | `src/front-components/<name>.tsx` |
| Role | `yarn twenty add role` | `src/roles/<name>.ts` |
| Skill | `yarn twenty add skill` | `src/skills/<name>.ts` |
| Agent | `yarn twenty add agent` | `src/agents/<name>.ts` |
| View | `yarn twenty add view` | `src/views/<name>.ts` |
| Navigation menu item | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/<name>.ts` |
| Page layout | `yarn twenty add pageLayout` | `src/page-layouts/<name>.ts` |
| Object | `yarn twenty dev:add object` | `src/objects/<name>.ts` |
| Field | `yarn twenty dev:add field` | `src/fields/<name>.ts` |
| Logic function | `yarn twenty dev:add logicFunction` | `src/logic-functions/<name>.ts` |
| Front component | `yarn twenty dev:add frontComponent` | `src/front-components/<name>.tsx` |
| Role | `yarn twenty dev:add role` | `src/roles/<name>.ts` |
| Skill | `yarn twenty dev:add skill` | `src/skills/<name>.ts` |
| Agent | `yarn twenty dev:add agent` | `src/agents/<name>.ts` |
| View | `yarn twenty dev:add view` | `src/views/<name>.ts` |
| Navigation menu item | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/<name>.ts` |
| Page layout | `yarn twenty dev:add pageLayout` | `src/page-layouts/<name>.ts` |
## What the scaffolder generates
Each entity type has its own template. For example, `yarn twenty add object` asks for:
Each entity type has its own template. For example, `yarn twenty dev:add object` asks for:
1. **Name (singular)** — e.g., `invoice`
2. **Name (plural)** — e.g., `invoices`
@@ -54,5 +54,5 @@ The `field` entity type is more detailed: it asks for the field name, label, typ
Use the `--path` flag to place the generated file in a custom location:
```bash filename="Terminal"
yarn twenty add logicFunction --path src/custom-folder
yarn twenty dev:add logicFunction --path src/custom-folder
```
@@ -4,7 +4,7 @@ description: Common first-run issues — Docker, Node version, Yarn, dependencie
icon: "wrench"
---
- **Docker errors** — Make sure Docker Desktop (or the daemon) is running before `yarn twenty server start`. The error message will show the right start command for your OS.
- **Docker errors** — Make sure Docker Desktop (or the daemon) is running before `yarn twenty docker:start`. The error message will show the right start command for your OS.
- **Wrong Node version** — Need 24+. Check with `node -v`.
- **Yarn 4 missing** — Run `corepack enable`.
- **Dependencies broken** — `rm -rf node_modules && yarn install`.
@@ -61,17 +61,17 @@ Available trigger types:
You can also manually execute a function using the CLI:
```bash filename="Terminal"
yarn twenty exec -n create-new-post-card -p '{"key": "value"}'
yarn twenty dev:function:exec -n create-new-post-card -p '{"key": "value"}'
```
```bash filename="Terminal"
yarn twenty exec -y e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
```
You can watch logs with:
```bash filename="Terminal"
yarn twenty logs
yarn twenty dev:function:logs
```
</Note>
@@ -341,7 +341,7 @@ The `twenty-client-sdk` package provides two typed GraphQL clients for interacti
<AccordionGroup>
<Accordion title="CoreApiClient" description="Query and mutate workspace data (records, objects)">
`CoreApiClient` is the main client for querying and mutating workspace data. It is **generated from your workspace schema** during `yarn twenty dev` or `yarn twenty build`, so it is fully typed to match your objects and fields.
`CoreApiClient` is the main client for querying and mutating workspace data. It is **generated from your workspace schema** during `yarn twenty dev` or `yarn twenty dev:build`, so it is fully typed to match your objects and fields.
```ts
import { CoreApiClient } from 'twenty-client-sdk/core';
@@ -381,7 +381,7 @@ const { createCompany } = await client.mutation({
The client uses a selection-set syntax: pass `true` to include a field, use `__args` for arguments, and nest objects for relations. You get full autocompletion and type checking based on your workspace schema.
<Note>
**CoreApiClient is generated at dev/build time.** If you use it without running `yarn twenty dev` or `yarn twenty build` first, it throws an error. The generation happens automatically — the CLI introspects your workspace's GraphQL schema and generates a typed client using `@genql/cli`.
**CoreApiClient is generated at dev/build time.** If you use it without running `yarn twenty dev` or `yarn twenty dev:build` first, it throws an error. The generation happens automatically — the CLI introspects your workspace's GraphQL schema and generates a typed client using `@genql/cli`.
</Note>
#### Using CoreSchema for type annotations
@@ -4,54 +4,54 @@ description: yarn twenty commands for executing functions, streaming logs, manag
icon: "terminal"
---
Beyond `dev`, `build`, `add`, and `typecheck`, the `yarn twenty` CLI provides commands for executing functions, viewing logs, and managing app installations.
Beyond `dev`, `dev:build`, `dev:add`, and `dev:typecheck`, the `yarn twenty` CLI provides commands for executing functions, viewing logs, and managing app installations.
## Executing functions (`yarn twenty exec`)
## Executing functions (`yarn twenty dev:function:exec`)
Run a logic function manually without triggering it via HTTP, cron, or database event:
```bash filename="Terminal"
# Execute by function name
yarn twenty exec -n create-new-post-card
yarn twenty dev:function:exec -n create-new-post-card
# Execute by universalIdentifier
yarn twenty exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
# Pass a JSON payload
yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}'
yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}'
# Execute the post-install function
yarn twenty exec --postInstall
yarn twenty dev:function:exec --postInstall
```
## Viewing function logs (`yarn twenty logs`)
## Viewing function logs (`yarn twenty dev:function:logs`)
Stream execution logs for your app's logic functions:
```bash filename="Terminal"
# Stream all function logs
yarn twenty logs
yarn twenty dev:function:logs
# Filter by function name
yarn twenty logs -n create-new-post-card
yarn twenty dev:function:logs -n create-new-post-card
# Filter by universalIdentifier
yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
yarn twenty dev:function:logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
```
<Note>
This is different from `yarn twenty server logs`, which shows the Docker container logs. `yarn twenty logs` shows your app's function execution logs from the Twenty server.
This is different from `yarn twenty docker:logs`, which shows the Docker container logs. `yarn twenty dev:function:logs` shows your app's function execution logs from the Twenty server.
</Note>
## Uninstalling an app (`yarn twenty uninstall`)
## Uninstalling an app (`yarn twenty app:uninstall`)
Remove your app from the active workspace:
```bash filename="Terminal"
yarn twenty uninstall
yarn twenty app:uninstall
# Skip the confirmation prompt
yarn twenty uninstall --yes
yarn twenty app:uninstall --yes
```
## Managing remotes
@@ -60,19 +60,19 @@ A **remote** is a Twenty server that your app connects to. During setup, the sca
```bash filename="Terminal"
# Add a new remote (opens a browser for OAuth login)
yarn twenty remote add
yarn twenty remote:add
# Connect to a local Twenty server (auto-detects port 2020 or 3000)
yarn twenty remote add --local
yarn twenty remote:add --local
# Add a remote non-interactively (useful for CI)
yarn twenty remote add --api-url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote
yarn twenty remote:add --url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote
# List all configured remotes
yarn twenty remote list
yarn twenty remote:list
# Switch the active remote
yarn twenty remote switch <name>
# Set the active remote
yarn twenty remote:use <name>
```
Your credentials are stored in `~/.twenty/config.json`.
@@ -9,9 +9,9 @@ The **operations layer** is everything you do *to* your app rather than *with* i
```text
develop ─▶ test ─▶ build ─▶ deploy / publish
─────── ──── ───── ─────────────────
yarn yarn yarn yarn twenty deploy (tarball → one server)
yarn yarn yarn yarn twenty app:publish --private (tarball → one server)
twenty test twenty
dev build yarn twenty publish (npm → marketplace)
dev dev:build yarn twenty app:publish (npm → marketplace)
```
## In this section
@@ -18,10 +18,10 @@ Both paths start from the same **build** step.
Run the build command to compile your app and generate a distribution-ready `manifest.json`:
```bash filename="Terminal"
yarn twenty build
yarn twenty dev:build
```
This compiles TypeScript sources, transpiles logic functions and front components, and writes everything to `.twenty/output/`. Add `--tarball` to also produce a `.tgz` package for manual distribution or the deploy command.
This compiles TypeScript sources, transpiles logic functions and front components, and writes everything to `.twenty/output/`. Add `--tarball` to also produce a `.tgz` package for manual distribution or the publish command.
## Deploying to a server (tarball)
@@ -34,7 +34,7 @@ Before deploying, you need a configured remote pointing to the target server. Re
Add a remote:
```bash filename="Terminal"
yarn twenty remote add --api-url https://your-twenty-server.com --as production
yarn twenty remote:add --url https://your-twenty-server.com --as production
```
### Deploying
@@ -42,9 +42,9 @@ yarn twenty remote add --api-url https://your-twenty-server.com --as production
Build and upload your app to the server in one step:
```bash filename="Terminal"
yarn twenty deploy
yarn twenty app:publish --private
# To deploy to a specific remote:
# yarn twenty deploy --remote production
# yarn twenty app:publish --private --remote production
```
### Sharing a deployed app
@@ -68,7 +68,7 @@ When updating an already deployed tarball app, the server requires the `version`
To release an update:
1. Bump the `version` field in your `package.json` (e.g. `1.2.3` → `1.2.4`, `1.3.0`, or `2.0.0`)
2. Run `yarn twenty deploy` (or `yarn twenty deploy --remote production`)
2. Run `yarn twenty app:publish --private` (or `yarn twenty app:publish --private --remote production`)
3. Workspaces that have the app installed will see the upgrade available in their settings
<Note>
@@ -121,7 +121,7 @@ Runs integration tests on every push to `main` and every pull request.
**What it does:**
1. Checks out your app's source.
2. Spawns an isolated Twenty test instance using the `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` composite action (the CI equivalent of `yarn twenty server start --test`).
2. Spawns an isolated Twenty test instance using the `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` composite action (the CI equivalent of `yarn twenty docker:start --test`).
3. Enables Corepack, sets up Node.js from your `.nvmrc`, and installs dependencies with `yarn install --immutable`.
4. Runs `yarn test`, passing `TWENTY_API_URL` and `TWENTY_API_KEY` from the spawned instance so your tests can talk to a real server.
@@ -139,7 +139,7 @@ Deploys your app to a configured Twenty server on every push to `main`, and opti
**What it does:**
1. Checks out the PR head (for labeled PRs) or the pushed commit.
2. Runs `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — the CI equivalent of `yarn twenty deploy`.
2. Runs `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — the CI equivalent of `yarn twenty app:publish --private`.
3. Runs `twentyhq/twenty/.github/actions/install-twenty-app@main` so the newly deployed version is installed into the target workspace.
**Required configuration:**
@@ -208,13 +208,13 @@ Screenshots of any aspect ratio are displayed in full and are never cropped, but
### Publish
```bash filename="Terminal"
yarn twenty publish
yarn twenty app:publish
```
To publish under a specific dist-tag (e.g., `beta` or `next`):
```bash filename="Terminal"
yarn twenty publish --tag beta
yarn twenty app:publish --tag beta
```
### How marketplace discovery works
@@ -224,9 +224,9 @@ The Twenty server syncs its marketplace catalog from the npm registry **every ho
You can trigger the sync immediately instead of waiting:
```bash filename="Terminal"
yarn twenty server catalog-sync
yarn twenty dev:catalog-sync
# To target a specific remote:
# yarn twenty server catalog-sync --remote production
# yarn twenty dev:catalog-sync --remote production
```
The metadata shown in the marketplace comes from your `defineApplication()` config — fields like `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl`, and `termsUrl`.
@@ -259,12 +259,12 @@ jobs:
node-version: "24"
registry-url: https://registry.npmjs.org
- run: yarn install --immutable
- run: npx twenty build
- run: npx twenty dev:build
- run: npm publish --provenance --access public
working-directory: .twenty/output
```
For other CI systems (GitLab CI, CircleCI, etc.), the same three commands apply: `yarn install`, `yarn twenty build`, then `npm publish` from `.twenty/output`.
For other CI systems (GitLab CI, CircleCI, etc.), the same three commands apply: `yarn install`, `yarn twenty dev:build`, then `npm publish` from `.twenty/output`.
<Note>
**npm provenance** is optional but recommended. Publishing with `--provenance` adds a trust badge to your npm listing, letting users verify the package was built from a specific commit in a public CI pipeline. See the [npm provenance docs](https://docs.npmjs.com/generating-provenance-statements) for setup instructions.
@@ -281,7 +281,7 @@ Go to the **Settings > Applications** page in Twenty, where both marketplace and
You can also install apps from the command line:
```bash filename="Terminal"
yarn twenty install
yarn twenty app:install
```
<Note>
@@ -290,5 +290,5 @@ The server enforces semver versioning on install, mirroring the rules on deploy:
- Installing the same version that is already installed in your workspace is rejected with an `APP_ALREADY_INSTALLED` error.
- Installing a lower version than the one currently installed is rejected with a `CANNOT_DOWNGRADE_APPLICATION` error.
To install a newer version, deploy or publish it first, then re-run `yarn twenty install`.
To install a newer version, deploy or publish it first, then re-run `yarn twenty app:install`.
</Note>
@@ -235,7 +235,7 @@ yarn test:watch
You can also run type checking on your app without running tests:
```bash filename="Terminal"
yarn twenty typecheck
yarn twenty dev:typecheck
```
This runs `tsc --noEmit` and reports any type errors.
@@ -35,7 +35,7 @@ Everything is detected via AST analysis at build time — no config files, no re
## The developer experience
You write your app as a TypeScript project on your machine. The CLI watches your source files and live-syncs them to a running Twenty server — edit a file, see the change in the UI within a second. The typed API client regenerates automatically when the schema changes. When you're ready, `yarn twenty deploy` pushes to a production server, or `yarn twenty publish` lists your app on npm and the Twenty marketplace.
You write your app as a TypeScript project on your machine. The CLI watches your source files and live-syncs them to a running Twenty server — edit a file, see the change in the UI within a second. The typed API client regenerates automatically when the schema changes. When you're ready, `yarn twenty app:publish --private` pushes to a production server, or `yarn twenty app:publish` lists your app on npm and the Twenty marketplace.
<Card title="Build your first app" icon="arrow-right" href="/developers/extend/apps/getting-started">
Three-phase walkthrough — scaffold, run a local server, sync your changes.
@@ -45,7 +45,7 @@ export default definePostInstallLogicFunction({
Sie können die Post-Installationsfunktion auch jederzeit manuell über die CLI ausführen:
```bash filename="Terminal"
yarn twenty exec --postInstall
yarn twenty dev:function:exec --postInstall
```
Hauptpunkte:
@@ -60,7 +60,7 @@ Hauptpunkte:
* Pro Anwendung ist nur eine Post-Installationsfunktion zulässig. Der Manifest-Build schlägt fehl, wenn mehr als eine erkannt wird.
* Die `universalIdentifier`, `shouldRunOnVersionUpgrade` und `shouldRunSynchronously` der Funktion werden während des Builds automatisch dem Anwendungsmanifest unter dem Feld `postInstallLogicFunction` hinzugefügt  Sie müssen sie in [`defineApplication()`](/l/de/developers/extend/apps/config/application) nicht referenzieren.
* Das standardmäßige Timeout ist auf 300 Sekunden (5 Minuten) festgelegt, um längere Einrichtungsvorgänge wie Daten-Seeding zu ermöglichen.
* **Nicht im Dev-Modus ausgeführt**: Wenn eine App lokal registriert ist (über `yarn twenty dev`), überspringt der Server den Installationsablauf vollständig und synchronisiert Dateien direkt über den CLI-Watcher — daher läuft Post-Install im Dev-Modus nie, unabhängig von `shouldRunSynchronously`. Verwenden Sie `yarn twenty exec --postInstall`, um es manuell gegen einen laufenden Arbeitsbereich auszulösen.
* **Nicht im Dev-Modus ausgeführt**: Wenn eine App lokal registriert ist (über `yarn twenty dev`), überspringt der Server den Installationsablauf vollständig und synchronisiert Dateien direkt über den CLI-Watcher — daher läuft Post-Install im Dev-Modus nie, unabhängig von `shouldRunSynchronously`. Verwenden Sie `yarn twenty dev:function:exec --postInstall`, um es manuell gegen einen laufenden Arbeitsbereich auszulösen.
</Accordion>
<Accordion title="definePreInstallLogicFunction" description="Wird ausgeführt, bevor die Metadatenmigration des Arbeitsbereichs angewendet wird">
@@ -87,7 +87,7 @@ export default definePreInstallLogicFunction({
Sie können die Pre-Installationsfunktion auch jederzeit manuell über die CLI ausführen:
```bash filename="Terminal"
yarn twenty exec --preInstall
yarn twenty dev:function:exec --preInstall
```
Hauptpunkte:
@@ -96,7 +96,7 @@ Hauptpunkte:
* **Wann der Hook ausgeführt wird**: positioniert direkt vor der Metadatenmigration des Arbeitsbereichs (`synchronizeFromManifest`). Vor der Ausführung führt der Server einen rein additiven "pared-down sync" durch, der die Pre-Install-Funktion der **neuen** Version in den Metadaten des Arbeitsbereichs registriert — sonst wird nichts angefasst — und führt sie dann aus. Da dieser Sync nur additiv ist, sind die Objekte, Felder und Daten der vorherigen Version noch intakt, wenn Ihr Handler läuft: Sie können den Zustand vor der Migration gefahrlos lesen und sichern.
* **Ausführungsmodell**: Pre-Install wird **synchron** ausgeführt und **blockiert die Installation**. Wenn der Handler einen Fehler wirft, wird die Installation abgebrochen, bevor Schemaänderungen angewendet werden — der Arbeitsbereich verbleibt in der vorherigen Version in einem konsistenten Zustand. Das ist beabsichtigt: Pre-Install ist Ihre letzte Chance, ein riskantes Upgrade abzulehnen.
* Wie bei Post-Install ist pro Anwendung nur eine Pre-Installationsfunktion zulässig. Sie wird während des Builds automatisch dem Anwendungsmanifest unter `preInstallLogicFunction` hinzugefügt.
* **Nicht im Dev-Modus ausgeführt**: wie bei Post-Install — der Installationsablauf wird für lokal registrierte Apps vollständig übersprungen, daher läuft Pre-Install unter `yarn twenty dev` nie. Verwenden Sie `yarn twenty exec --preInstall`, um es manuell auszulösen.
* **Nicht im Dev-Modus ausgeführt**: wie bei Post-Install — der Installationsablauf wird für lokal registrierte Apps vollständig übersprungen, daher läuft Pre-Install unter `yarn twenty dev` nie. Verwenden Sie `yarn twenty dev:function:exec --preInstall`, um es manuell auszulösen.
</Accordion>
<Accordion title="Pre-Install vs. Post-Install: wann was verwenden" description="Den richtigen Installations-Hook wählen">
@@ -13,7 +13,7 @@ Für Dateien im Verzeichnis `public/` gilt:
* **In Logikfunktionen verfügbar** — referenzieren Sie Asset-URLs in E-Mails, API-Antworten oder in beliebiger serverseitiger Logik.
* **Für Marketplace-Metadaten verwendet** — die Felder `logoUrl` und `screenshots` in `defineApplication()` referenzieren Dateien aus diesem Ordner (z. B. `public/logo.png`). Diese werden im Marketplace angezeigt, wenn Ihre App veröffentlicht wird.
* **Im Dev-Modus automatisch synchronisiert** — wenn Sie in `public/` eine Datei hinzufügen, aktualisieren oder löschen, wird sie automatisch mit dem Server synchronisiert. Kein Neustart erforderlich.
* **In Builds enthalten** — `yarn twenty build` bündelt alle öffentlichen Assets in der Distributionsausgabe.
* **In Builds enthalten** — `yarn twenty dev:build` bündelt alle öffentlichen Assets in der Distributionsausgabe.
## Zugriff auf öffentliche Assets mit `getPublicAssetUrl`
@@ -80,7 +80,7 @@ export default defineObject({
* Jedes Feld benötigt `name`, `type`, `label` und einen eigenen stabilen `universalIdentifier`.
* Das Array `fields` ist optional — Sie können Objekte ohne benutzerdefinierte Felder definieren.
* Inline definierte Felder benötigen **kein** `objectUniversalIdentifier` er wird vom übergeordneten Objekt geerbt. Verwenden Sie [`defineField()`](/l/de/developers/extend/apps/data/extending-objects), um Objekten Felder hinzuzufügen, die Ihnen nicht gehören.
* Sie können mit `yarn twenty add object` neue Objekte erzeugen; der Assistent führt Sie durch Benennung, Felder und Beziehungen. Siehe [Architektur → Gerüste für Entitäten](/l/de/developers/extend/apps/getting-started/scaffolding).
* Sie können mit `yarn twenty dev:add object` neue Objekte erzeugen; der Assistent führt Sie durch Benennung, Felder und Beziehungen. Siehe [Architektur → Gerüste für Entitäten](/l/de/developers/extend/apps/getting-started/scaffolding).
<Note>
**Basisfelder werden automatisch hinzugefügt.** Wenn Sie ein benutzerdefiniertes Objekt definieren, erstellt Twenty Standardfelder wie `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` und `deletedAt` für Sie. Sie müssen diese nicht in Ihrem `fields`-Array deklarieren nur Ihre benutzerdefinierten Felder. Sie können ein Standardfeld überschreiben, indem Sie eines mit demselben Namen deklarieren, aber das ist nur selten eine gute Idee.
@@ -65,7 +65,7 @@ your-app/
│ npx create-twenty-app → yarn twenty dev (live sync) │
├─────────────────────────────────────────────────────────┤
│ Build & Deploy │
│ yarn twenty build → yarn twenty deploy
│ yarn twenty dev:build → yarn twenty app:publish
├─────────────────────────────────────────────────────────┤
│ Install flow │
│ upload → [pre-install] → metadata migration → │
@@ -77,7 +77,7 @@ your-app/
```
* **`yarn twenty dev`** — überwacht Ihre Quelldateien und synchronisiert Änderungen in Echtzeit mit einem verbundenen Twenty-Server. Der typisierte API-Client wird automatisch neu erzeugt, wenn sich das Schema ändert.
* **`yarn twenty build`** — kompiliert TypeScript, bündelt Logikfunktionen und Frontend-Komponenten mit esbuild und erzeugt ein Manifest.
* **`yarn twenty dev:build`** — kompiliert TypeScript, bündelt Logikfunktionen und Frontend-Komponenten mit esbuild und erzeugt ein Manifest.
* **Pre/Post-Install-Hooks** — optionale Funktionen, die während der Installation ausgeführt werden. Details finden Sie unter [Install Hooks](/l/de/developers/extend/apps/config/install-hooks).
## Nächste Schritte
@@ -6,44 +6,44 @@ icon: server
## Lokalen Server verwalten
Verwenden Sie `yarn twenty server`, um den lokalen Twenty-Container zu steuern:
Verwenden Sie `yarn twenty docker:*`, um den lokalen Twenty-Container zu steuern:
| Befehl | Was es tut |
| -------------------------------------- | --------------------------------------------------- |
| `yarn twenty server start` | Server starten (lädt das Image bei Bedarf herunter) |
| `yarn twenty server start --port 3030` | Auf einem benutzerdefinierten Port starten |
| `yarn twenty server stop` | Server stoppen (Daten bleiben erhalten) |
| `yarn twenty server status` | URL, Version und Anmeldedaten anzeigen |
| `yarn twenty server logs` | Serverprotokolle streamen |
| `yarn twenty server reset` | Alle Daten löschen und neu starten |
| `yarn twenty server upgrade` | Das neueste `twenty-app-dev`-Image herunterladen |
| `yarn twenty server upgrade 2.2.0` | Auf eine bestimmte Version aktualisieren |
| `yarn twenty docker:start` | Server starten (lädt das Image bei Bedarf herunter) |
| `yarn twenty docker:start --port 3030` | Auf einem benutzerdefinierten Port starten |
| `yarn twenty docker:stop` | Server stoppen (Daten bleiben erhalten) |
| `yarn twenty docker:status` | URL, Version und Anmeldedaten anzeigen |
| `yarn twenty docker:logs` | Serverprotokolle streamen |
| `yarn twenty docker:reset` | Alle Daten löschen und neu starten |
| `yarn twenty docker:upgrade` | Das neueste `twenty-app-dev`-Image herunterladen |
| `yarn twenty docker:upgrade 2.2.0` | Auf eine bestimmte Version aktualisieren |
Daten bleiben über Neustarts hinweg in zwei Docker-Volumes bestehen (`twenty-app-dev-data` für PostgreSQL, `twenty-app-dev-storage` für Dateien). Verwenden Sie `reset`, um alles zu löschen.
## Aktualisieren des Server-Images
`yarn twenty server upgrade` lädt das neueste Image herunter, vergleicht die Digests und erstellt den Container nur neu, wenn sich tatsächlich etwas geändert hat. Die Volumes bleiben erhalten — nur der Container wird ersetzt. Wenn ein neues Image heruntergeladen wurde und der Container lief, startet das Upgrade automatisch einen neuen Container; führen Sie anschließend `yarn twenty server start` aus, um zu warten, bis er betriebsbereit ist.
`yarn twenty docker:upgrade` lädt das neueste Image herunter, vergleicht die Digests und erstellt den Container nur neu, wenn sich tatsächlich etwas geändert hat. Die Volumes bleiben erhalten — nur der Container wird ersetzt. Wenn ein neues Image heruntergeladen wurde und der Container lief, startet das Upgrade automatisch einen neuen Container; führen Sie anschließend `yarn twenty docker:start` aus, um zu warten, bis er betriebsbereit ist.
```bash filename="Terminal"
yarn twenty server upgrade # Latest
yarn twenty server upgrade 2.2.0 # Specific version
yarn twenty docker:upgrade # Latest
yarn twenty docker:upgrade 2.2.0 # Specific version
```
Überprüfen Sie die laufende Version mit `yarn twenty server status` (dies zeigt die im Container enthaltene `APP_VERSION` an).
Überprüfen Sie die laufende Version mit `yarn twenty docker:status` (dies zeigt die im Container enthaltene `APP_VERSION` an).
## Eine parallele Testinstanz ausführen
Übergeben Sie `--test` an jeden `server`-Befehl, um eine zweite, vollständig isolierte Instanz zu verwalten — nützlich für Integrationstests oder Experimente, ohne Ihre Hauptentwicklungsdaten anzutasten:
Übergeben Sie `--test` an jeden `docker:*`-Befehl, um eine zweite, vollständig isolierte Instanz zu verwalten — nützlich für Integrationstests oder Experimente, ohne Ihre Hauptentwicklungsdaten anzutasten:
| Befehl | Was es tut |
| ----------------------------------- | ------------------------------------------------- |
| `yarn twenty server start --test` | Die Testinstanz starten (standardmäßig Port 2021) |
| `yarn twenty server stop --test` | Anhalten |
| `yarn twenty server status --test` | Status anzeigen |
| `yarn twenty server logs --test` | Protokolle streamen |
| `yarn twenty server reset --test` | Daten löschen |
| `yarn twenty server upgrade --test` | Image aktualisieren |
| `yarn twenty docker:start --test` | Die Testinstanz starten (standardmäßig Port 2021) |
| `yarn twenty docker:stop --test` | Anhalten |
| `yarn twenty docker:status --test` | Status anzeigen |
| `yarn twenty docker:logs --test` | Protokolle streamen |
| `yarn twenty docker:reset --test` | Daten löschen |
| `yarn twenty docker:upgrade --test` | Image aktualisieren |
Die Testinstanz hat ihren eigenen Container (`twenty-app-dev-test`), eigene Volumes (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) und eine eigene Konfiguration — sie läuft parallel zu Ihrer Hauptinstanz ohne Konflikte. Kombinieren Sie `--test` mit `--port`, um den Port 2021 zu überschreiben.
@@ -65,7 +65,7 @@ Fügen Sie der `package.json` das Skript hinzu:
}
```
Sie können jetzt `yarn twenty dev`, `yarn twenty server start` und den Rest ausführen.
Sie können jetzt `yarn twenty dev`, `yarn twenty docker:start` und den Rest ausführen.
<Note>
Installieren Sie `twenty-sdk` nicht global — fixieren Sie es pro Projekt, damit jede App ihre eigene Version verwendet.
@@ -43,7 +43,7 @@ Das Scaffolding-Tool bietet an, einen für Sie zu starten:
> **Möchten Sie eine lokale Twenty-Instanz einrichten?**
* **Ja (empfohlen)** — lädt das Docker-Image `twentycrm/twenty-app-dev` herunter und startet es auf Port `2020`. Stellen Sie sicher, dass Docker läuft.
* **Nein** — wählen Sie dies, wenn Sie bereits einen Twenty-Server haben, mit dem Sie sich verbinden möchten. Sie können die Verbindung später mit `yarn twenty remote add` herstellen.
* **Nein** — wählen Sie dies, wenn Sie bereits einen Twenty-Server haben, mit dem Sie sich verbinden möchten. Sie können die Verbindung später mit `yarn twenty remote:add` herstellen.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/developers/extends/apps/start-instance.png" alt="Soll die lokale Instanz gestartet werden?" />
@@ -73,7 +73,7 @@ Ihr Terminal bestätigt, dass alles eingerichtet ist.
**Nach dieser Phase:** Sie haben einen laufenden Twenty-Server unter [http://localhost:2020](http://localhost:2020), und Ihre CLI ist autorisiert, mit ihm zu synchronisieren.
<Note>
Wenn Docker nicht installiert ist oder nicht läuft, zeigt das Scaffolding-Tool den richtigen Startbefehl für Ihr Betriebssystem an. Sobald Docker läuft, können Sie mit `yarn twenty server start` fortfahren — ein erneutes Scaffolding ist nicht nötig.
Wenn Docker nicht installiert ist oder nicht läuft, zeigt das Scaffolding-Tool den richtigen Startbefehl für Ihr Betriebssystem an. Sobald Docker läuft, können Sie mit `yarn twenty docker:start` fortfahren — ein erneutes Scaffolding ist nicht nötig.
</Note>
---
@@ -1,13 +1,13 @@
---
title: Scaffolding
description: Generieren Sie Entitätsdateien interaktiv mit yarn twenty add — Objekte, Felder, Ansichten, Logikfunktionen und mehr.
description: Generieren Sie Entitätsdateien interaktiv mit yarn twenty dev:add — Objekte, Felder, Ansichten, Logikfunktionen und mehr.
icon: wand-magic-sparkles
---
Anstatt Entitätsdateien manuell zu erstellen, können Sie den interaktiven Scaffolder verwenden:
```bash filename="Terminal"
yarn twenty add
yarn twenty dev:add
```
Er fordert Sie auf, einen Entitätstyp auszuwählen, führt Sie durch die erforderlichen Felder und schreibt anschließend eine einsatzbereite Datei mit einem stabilen `universalIdentifier` und dem korrekten `defineEntity()`-Aufruf.
@@ -15,29 +15,29 @@ Er fordert Sie auf, einen Entitätstyp auszuwählen, führt Sie durch die erford
Sie können den Entitätstyp auch direkt übergeben, um die erste Eingabeaufforderung zu überspringen:
```bash filename="Terminal"
yarn twenty add object
yarn twenty add logicFunction
yarn twenty add frontComponent
yarn twenty dev:add object
yarn twenty dev:add logicFunction
yarn twenty dev:add frontComponent
```
## Verfügbare Entitätstypen
| Entitätstyp | Befehl | Generierte Datei |
| ---------------------- | ------------------------------------ | ------------------------------------------------------- |
| Objekt | `yarn twenty add object` | `src/objects/\<name>.ts` |
| Feld | `yarn twenty add field` | `src/fields/\<name>.ts` |
| Logikfunktion | `yarn twenty add logicFunction` | `src/logic-functions/\<name>.ts` |
| Frontend-Komponente | `yarn twenty add frontComponent` | `src/front-components/\<name>.tsx` |
| Rolle | `yarn twenty add role` | `src/roles/\<name>.ts` |
| Skill | `yarn twenty add skill` | `src/skills/\<name>.ts` |
| Agent | `yarn twenty add agent` | `src/agents/\<name>.ts` |
| Ansicht | `yarn twenty add view` | `src/views/\<name>.ts` |
| Navigationsmenüeintrag | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\<name>.ts` |
| Seitenlayout | `yarn twenty add pageLayout` | `src/page-layouts/\<name>.ts` |
| Entitätstyp | Befehl | Generierte Datei |
| ---------------------- | ---------------------------------------- | ------------------------------------------------------- |
| Objekt | `yarn twenty dev:add object` | `src/objects/\<name>.ts` |
| Feld | `yarn twenty dev:add field` | `src/fields/\<name>.ts` |
| Logikfunktion | `yarn twenty dev:add logicFunction` | `src/logic-functions/\<name>.ts` |
| Frontend-Komponente | `yarn twenty dev:add frontComponent` | `src/front-components/\<name>.tsx` |
| Rolle | `yarn twenty dev:add role` | `src/roles/\<name>.ts` |
| Skill | `yarn twenty dev:add skill` | `src/skills/\<name>.ts` |
| Agent | `yarn twenty dev:add agent` | `src/agents/\<name>.ts` |
| Ansicht | `yarn twenty dev:add view` | `src/views/\<name>.ts` |
| Navigationsmenüeintrag | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\<name>.ts` |
| Seitenlayout | `yarn twenty dev:add pageLayout` | `src/page-layouts/\<name>.ts` |
## Was der Scaffolder generiert
Jeder Entitätstyp hat seine eigene Vorlage. Zum Beispiel fragt `yarn twenty add object` nach:
Jeder Entitätstyp hat seine eigene Vorlage. Zum Beispiel fragt `yarn twenty dev:add object` nach:
1. **Name (Singular)** — z. B. `invoice`
2. **Name (Plural)** — z. B. `invoices`
@@ -54,5 +54,5 @@ Der Entitätstyp `field` ist detaillierter: Er fragt nach Feldname, Label, Typ (
Verwenden Sie den Schalter `--path`, um die generierte Datei an einem benutzerdefinierten Ort abzulegen:
```bash filename="Terminal"
yarn twenty add logicFunction --path src/custom-folder
yarn twenty dev:add logicFunction --path src/custom-folder
```
@@ -4,7 +4,7 @@ description: Häufige Probleme beim ersten Start — Docker, Node-Version, Yarn
icon: Schraubenschlüssel
---
* **Docker-Fehler** — Stellen Sie sicher, dass Docker Desktop (oder der Daemon) läuft, bevor Sie `yarn twenty server start` ausführen. Die Fehlermeldung zeigt den richtigen Startbefehl für Ihr Betriebssystem an.
* **Docker-Fehler** — Stellen Sie sicher, dass Docker Desktop (oder der Daemon) läuft, bevor Sie `yarn twenty docker:start` ausführen. Die Fehlermeldung zeigt den richtigen Startbefehl für Ihr Betriebssystem an.
* **Falsche Node-Version** — 24+ erforderlich. Prüfen Sie mit `node -v`.
* **Yarn 4 fehlt** — Führen Sie `corepack enable` aus.
* **Abhängigkeiten defekt** — `rm -rf node_modules && yarn install`.
@@ -61,17 +61,17 @@ Verfügbare Trigger-Typen:
Sie können eine Funktion auch manuell über die CLI ausführen:
```bash filename="Terminal"
yarn twenty exec -n create-new-post-card -p '{"key": "value"}'
yarn twenty dev:function:exec -n create-new-post-card -p '{"key": "value"}'
```
```bash filename="Terminal"
yarn twenty exec -y e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
```
Sie können Protokolle mit folgendem Befehl ansehen:
```bash filename="Terminal"
yarn twenty logs
yarn twenty dev:function:logs
```
</Note>
@@ -340,7 +340,7 @@ Das Paket `twenty-client-sdk` stellt zwei typisierte GraphQL-Clients bereit, um
<AccordionGroup>
<Accordion title="CoreApiClient" description="Arbeitsbereichsdaten (Datensätze, Objekte) abfragen und ändern">
Der `CoreApiClient` ist der Haupt-Client zum Abfragen und Ändern von Arbeitsbereichsdaten. Er wird während `yarn twenty dev` oder `yarn twenty build` **aus Ihrem Arbeitsbereichsschema generiert** und ist daher vollständig typisiert, passend zu Ihren Objekten und Feldern.
Der `CoreApiClient` ist der Haupt-Client zum Abfragen und Ändern von Arbeitsbereichsdaten. Er wird während `yarn twenty dev` oder `yarn twenty dev:build` **aus Ihrem Arbeitsbereichsschema generiert** und ist daher vollständig typisiert, passend zu Ihren Objekten und Feldern.
```ts
import { CoreApiClient } from 'twenty-client-sdk/core';
@@ -380,7 +380,7 @@ const { createCompany } = await client.mutation({
Der Client verwendet eine Selection-Set-Syntax: Übergeben Sie `true`, um ein Feld einzuschließen, verwenden Sie `__args` für Argumente, und verschachteln Sie Objekte für Relationen. Sie erhalten vollständige Autovervollständigung und Typprüfung basierend auf Ihrem Arbeitsbereichsschema.
<Note>
**Der CoreApiClient wird zur Entwicklungs-/Build-Zeit generiert.** Wenn Sie ihn verwenden, ohne zuvor `yarn twenty dev` oder `yarn twenty build` ausgeführt zu haben, wird ein Fehler ausgelöst. Die Generierung erfolgt automatisch — die CLI inspiziert das GraphQL-Schema Ihres Arbeitsbereichs und erzeugt mit `@genql/cli` einen typisierten Client.
**Der CoreApiClient wird zur Entwicklungs-/Build-Zeit generiert.** Wenn Sie ihn verwenden, ohne zuvor `yarn twenty dev` oder `yarn twenty dev:build` ausgeführt zu haben, wird ein Fehler ausgelöst. Die Generierung erfolgt automatisch — die CLI inspiziert das GraphQL-Schema Ihres Arbeitsbereichs und erzeugt mit `@genql/cli` einen typisierten Client.
</Note>
#### Verwendung von CoreSchema für Typannotationen
@@ -4,54 +4,54 @@ description: yarn twenty Befehle zum Ausführen von Funktionen, Streamen von Log
icon: Terminal
---
Zusätzlich zu `dev`, `build`, `add` und `typecheck` bietet die `yarn twenty` CLI Befehle zum Ausführen von Funktionen, Anzeigen von Logs und Verwalten von App-Installationen.
Zusätzlich zu `dev`, `dev:build`, `dev:add` und `dev:typecheck` bietet die `yarn twenty` CLI Befehle zum Ausführen von Funktionen, Anzeigen von Logs und Verwalten von App-Installationen.
## Funktionen ausführen (`yarn twenty exec`)
## Funktionen ausführen (`yarn twenty dev:function:exec`)
Eine Logikfunktion manuell ausführen, ohne sie über HTTP, Cron oder ein Datenbankereignis auszulösen:
```bash filename="Terminal"
# Execute by function name
yarn twenty exec -n create-new-post-card
yarn twenty dev:function:exec -n create-new-post-card
# Execute by universalIdentifier
yarn twenty exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
# Pass a JSON payload
yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}'
yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}'
# Execute the post-install function
yarn twenty exec --postInstall
yarn twenty dev:function:exec --postInstall
```
## Funktionsprotokolle ansehen (`yarn twenty logs`)
## Funktionsprotokolle ansehen (`yarn twenty dev:function:logs`)
Ausführungsprotokolle für die Logikfunktionen Ihrer App streamen:
```bash filename="Terminal"
# Stream all function logs
yarn twenty logs
yarn twenty dev:function:logs
# Filter by function name
yarn twenty logs -n create-new-post-card
yarn twenty dev:function:logs -n create-new-post-card
# Filter by universalIdentifier
yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
yarn twenty dev:function:logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
```
<Note>
Dies unterscheidet sich von `yarn twenty server logs`, das die Docker-Container-Logs anzeigt. `yarn twenty logs` zeigt die Funktionsausführungsprotokolle Ihrer App vom Twenty-Server.
Dies unterscheidet sich von `yarn twenty docker:logs`, das die Docker-Container-Logs anzeigt. `yarn twenty dev:function:logs` zeigt die Funktionsausführungsprotokolle Ihrer App vom Twenty-Server.
</Note>
## Eine App deinstallieren (`yarn twenty uninstall`)
## Eine App deinstallieren (`yarn twenty app:uninstall`)
Entfernen Sie Ihre App aus dem aktiven Arbeitsbereich:
```bash filename="Terminal"
yarn twenty uninstall
yarn twenty app:uninstall
# Skip the confirmation prompt
yarn twenty uninstall --yes
yarn twenty app:uninstall --yes
```
## Remotes verwalten
@@ -60,19 +60,19 @@ Ein **Remote** ist ein Twenty-Server, mit dem sich Ihre App verbindet. Während
```bash filename="Terminal"
# Add a new remote (opens a browser for OAuth login)
yarn twenty remote add
yarn twenty remote:add
# Connect to a local Twenty server (auto-detects port 2020 or 3000)
yarn twenty remote add --local
yarn twenty remote:add --local
# Add a remote non-interactively (useful for CI)
yarn twenty remote add --api-url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote
yarn twenty remote:add --url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote
# List all configured remotes
yarn twenty remote list
yarn twenty remote:list
# Switch the active remote
yarn twenty remote switch <name>
# Set the active remote
yarn twenty remote:use <name>
```
Ihre Anmeldedaten werden in `~/.twenty/config.json` gespeichert.
@@ -9,9 +9,9 @@ Die **Operationsschicht** umfasst alles, was Sie *an* Ihrer App tun, statt *mit*
```text
develop ─▶ test ─▶ build ─▶ deploy / publish
─────── ──── ───── ─────────────────
yarn yarn yarn yarn twenty deploy (tarball → one server)
yarn yarn yarn yarn twenty app:publish --private (tarball → one server)
twenty test twenty
dev build yarn twenty publish (npm → marketplace)
dev dev:build yarn twenty app:publish (npm → marketplace)
```
## In diesem Abschnitt
@@ -18,10 +18,10 @@ Beide Pfade beginnen mit demselben **Build**-Schritt.
Führen Sie den Build-Befehl aus, um Ihre App zu kompilieren und eine distributionsfertige `manifest.json` zu erzeugen:
```bash filename="Terminal"
yarn twenty build
yarn twenty dev:build
```
Dabei werden TypeScript-Quelltexte kompiliert, Logikfunktionen und Frontend-Komponenten transpiliert und alles in `.twenty/output/` geschrieben. Fügen Sie `--tarball` hinzu, um zusätzlich ein `.tgz`-Paket für die manuelle Verteilung oder den Deploy-Befehl zu erzeugen.
Dabei werden TypeScript-Quelltexte kompiliert, Logikfunktionen und Frontend-Komponenten transpiliert und alles in `.twenty/output/` geschrieben. Fügen Sie `--tarball` hinzu, um zusätzlich ein `.tgz`-Paket für die manuelle Verteilung oder den Publish-Befehl zu erzeugen.
## Bereitstellung auf einem Server (Tarball)
@@ -34,7 +34,7 @@ Bevor Sie bereitstellen, benötigen Sie ein konfiguriertes Remote, das auf den Z
Ein Remote hinzufügen:
```bash filename="Terminal"
yarn twenty remote add --api-url https://your-twenty-server.com --as production
yarn twenty remote:add --url https://your-twenty-server.com --as production
```
### Bereitstellen
@@ -42,9 +42,9 @@ yarn twenty remote add --api-url https://your-twenty-server.com --as production
Bauen und laden Sie Ihre App in einem Schritt auf den Server hoch:
```bash filename="Terminal"
yarn twenty deploy
yarn twenty app:publish --private
# To deploy to a specific remote:
# yarn twenty deploy --remote production
# yarn twenty app:publish --private --remote production
```
### Eine bereitgestellte App freigeben
@@ -68,7 +68,7 @@ Beim Aktualisieren einer bereits bereitgestellten Tarball-App verlangt der Serve
So veröffentlichen Sie ein Update:
1. Erhöhen Sie das Feld `version` in Ihrer `package.json` (z. B. `1.2.3` → `1.2.4`, `1.3.0` oder `2.0.0`).
2. Führen Sie `yarn twenty deploy` aus (oder `yarn twenty deploy --remote production`)
2. Führen Sie `yarn twenty app:publish --private` aus (oder `yarn twenty app:publish --private --remote production`)
3. Arbeitsbereiche, die die App installiert haben, sehen in ihren Einstellungen, dass ein Upgrade verfügbar ist.
<Note>
@@ -121,7 +121,7 @@ Führt Ihre Integrationstests bei jedem Push auf `main` und bei Pull Requests au
**Was sie macht:**
1. Checkt den Quellcode Ihrer App aus.
2. Startet eine isolierte Twenty-Testinstanz mithilfe der Composite-Action `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (das CI-Äquivalent zu `yarn twenty server start --test`).
2. Startet eine isolierte Twenty-Testinstanz mithilfe der Composite-Action `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (das CI-Äquivalent zu `yarn twenty docker:start --test`).
3. Aktiviert Corepack, richtet Node.js anhand Ihrer `.nvmrc` ein und installiert Abhängigkeiten mit `yarn install --immutable`.
4. Führt `yarn test` aus und übergibt `TWENTY_API_URL` und `TWENTY_API_KEY` aus der gestarteten Instanz, damit Ihre Tests mit einem echten Server kommunizieren können.
@@ -139,7 +139,7 @@ Stellt Ihre App bei jedem Push auf `main` auf einem konfigurierten Twenty-Server
**Was sie macht:**
1. Checkt den PR-Head (bei PRs mit Label) oder den gepushten Commit aus.
2. Führt `twentyhq/twenty/.github/actions/deploy-twenty-app@main` aus — das CI-Äquivalent zu `yarn twenty deploy`.
2. Führt `twentyhq/twenty/.github/actions/deploy-twenty-app@main` aus — das CI-Äquivalent zu `yarn twenty app:publish --private`.
3. Führt `twentyhq/twenty/.github/actions/install-twenty-app@main` aus, damit die neu bereitgestellte Version in den Ziel-Workspace installiert wird.
**Erforderliche Konfiguration:**
@@ -208,13 +208,13 @@ Screenshots mit beliebigem Seitenverhältnis werden vollständig angezeigt und n
### Veröffentlichen
```bash filename="Terminal"
yarn twenty publish
yarn twenty app:publish
```
Um unter einem bestimmten dist-tag zu veröffentlichen (z. B. `beta` oder `next`):
```bash filename="Terminal"
yarn twenty publish --tag beta
yarn twenty app:publish --tag beta
```
### So funktioniert die Marktplatz-Erkennung
@@ -224,9 +224,9 @@ Der Twenty-Server synchronisiert seinen Marktplatzkatalog **stündlich** aus der
Sie können die Synchronisierung sofort auslösen, anstatt zu warten:
```bash filename="Terminal"
yarn twenty server catalog-sync
yarn twenty dev:catalog-sync
# To target a specific remote:
# yarn twenty server catalog-sync --remote production
# yarn twenty dev:catalog-sync --remote production
```
Die im Marktplatz angezeigten Metadaten stammen aus Ihrer `defineApplication()`-Konfiguration — Felder wie `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` und `termsUrl`.
@@ -259,12 +259,12 @@ jobs:
node-version: "24"
registry-url: https://registry.npmjs.org
- run: yarn install --immutable
- run: npx twenty build
- run: npx twenty dev:build
- run: npm publish --provenance --access public
working-directory: .twenty/output
```
Für andere CI-Systeme (GitLab CI, CircleCI usw.) gelten die gleichen drei Befehle: `yarn install`, `yarn twenty build` und anschließend `npm publish` aus `.twenty/output`.
Für andere CI-Systeme (GitLab CI, CircleCI usw.) gelten die gleichen drei Befehle: `yarn install`, `yarn twenty dev:build` und anschließend `npm publish` aus `.twenty/output`.
<Note>
**npm-Provenance** ist optional, wird jedoch empfohlen. Das Veröffentlichen mit `--provenance` fügt Ihrem npm-Eintrag ein Vertrauensabzeichen hinzu, sodass Nutzer überprüfen können, dass das Paket aus einem bestimmten Commit in einer öffentlichen CI-Pipeline gebaut wurde. Siehe die [npm-Provenance-Dokumentation](https://docs.npmjs.com/generating-provenance-statements) für Einrichtungshinweise.
@@ -281,7 +281,7 @@ Gehen Sie zur Seite **Einstellungen > Anwendungen** in Twenty, auf der sowohl Ma
Sie können Apps auch über die Befehlszeile installieren:
```bash filename="Terminal"
yarn twenty install
yarn twenty app:install
```
<Note>
@@ -290,5 +290,5 @@ Der Server erzwingt bei der Installation semver-Versionierung und spiegelt damit
* Die Installation derselben Version, die in Ihrem Arbeitsbereich bereits installiert ist, wird mit einem `APP_ALREADY_INSTALLED`-Fehler abgelehnt.
* Die Installation einer niedrigeren Version als die aktuell installierte wird mit einem `CANNOT_DOWNGRADE_APPLICATION`-Fehler abgelehnt.
Um eine neuere Version zu installieren, stellen Sie sie zuerst bereit oder veröffentlichen Sie sie und führen Sie dann `yarn twenty install` erneut aus.
Um eine neuere Version zu installieren, stellen Sie sie zuerst bereit oder veröffentlichen Sie sie und führen Sie dann `yarn twenty app:install` erneut aus.
</Note>
@@ -235,7 +235,7 @@ yarn test:watch
Sie können die Typprüfung Ihrer App auch ohne Tests ausführen:
```bash filename="Terminal"
yarn twenty typecheck
yarn twenty dev:typecheck
```
Dies führt `tsc --noEmit` aus und meldet etwaige Typfehler.
@@ -45,7 +45,7 @@ export default definePostInstallLogicFunction({
Você também pode executar manualmente a função de pós-instalação a qualquer momento usando a CLI:
```bash filename="Terminal"
yarn twenty exec --postInstall
yarn twenty dev:function:exec --postInstall
```
Pontos-chave:
@@ -60,7 +60,7 @@ Pontos-chave:
* É permitida apenas uma função de pós-instalação por app. A geração do manifesto apresentará erro se mais de uma for detectada.
* O `universalIdentifier`, `shouldRunOnVersionUpgrade` e `shouldRunSynchronously` da função são anexados automaticamente ao manifesto do aplicativo no campo `postInstallLogicFunction` durante o build — você não precisa referenciá-los em [`defineApplication()`](/l/pt/developers/extend/apps/config/application).
* O tempo limite padrão é definido como 300 segundos (5 minutos) para permitir tarefas de configuração mais longas, como o pré-carregamento de dados.
* **Não executado no modo de desenvolvimento**: quando um app é registrado localmente (via `yarn twenty dev`), o servidor pula completamente o fluxo de instalação e sincroniza arquivos diretamente pelo watcher da CLI — portanto, a pós-instalação nunca é executada no modo de desenvolvimento, independentemente de `shouldRunSynchronously`. Use `yarn twenty exec --postInstall` para acioná-lo manualmente em um workspace em execução.
* **Não executado no modo de desenvolvimento**: quando um app é registrado localmente (via `yarn twenty dev`), o servidor pula completamente o fluxo de instalação e sincroniza arquivos diretamente pelo watcher da CLI — portanto, a pós-instalação nunca é executada no modo de desenvolvimento, independentemente de `shouldRunSynchronously`. Use `yarn twenty dev:function:exec --postInstall` para acioná-lo manualmente em um workspace em execução.
</Accordion>
<Accordion title="definePreInstallLogicFunction" description="É executada antes que a migração de metadados do workspace seja aplicada">
@@ -87,7 +87,7 @@ export default definePreInstallLogicFunction({
Você também pode executar manualmente a função de pré-instalação a qualquer momento usando a CLI:
```bash filename="Terminal"
yarn twenty exec --preInstall
yarn twenty dev:function:exec --preInstall
```
Pontos-chave:
@@ -96,7 +96,7 @@ Pontos-chave:
* **Quando o hook é executado**: posicionado imediatamente antes da migração de metadados do workspace (`synchronizeFromManifest`). Antes de executar, o servidor realiza uma "sincronização simplificada" puramente aditiva que registra a função de pré-instalação da **nova** versão nos metadados do workspace — nada mais é alterado — e então a executa. Como essa sincronização é apenas aditiva, os objetos, campos e dados da versão anterior ainda estão intactos quando seu manipulador é executado: você pode ler e fazer backup com segurança do estado pré-migração.
* **Modelo de execução**: a pré-instalação é executada **de forma síncrona** e **bloqueia a instalação**. Se o manipulador lançar uma exceção, a instalação é abortada antes que quaisquer alterações de esquema sejam aplicadas — o workspace permanece na versão anterior em um estado consistente. Isto é intencional: a pré-instalação é sua última chance de recusar uma atualização arriscada.
* Assim como na pós-instalação, é permitida apenas uma função de pré-instalação por app. Ela é anexada ao manifesto do aplicativo sob `preInstallLogicFunction` automaticamente durante o build.
* **Não é executada no modo de desenvolvimento**: igual à pós-instalação — o fluxo de instalação é totalmente ignorado para apps registrados localmente, portanto a pré-instalação nunca é executada com `yarn twenty dev`. Use `yarn twenty exec --preInstall` para acioná-lo manualmente.
* **Não é executada no modo de desenvolvimento**: igual à pós-instalação — o fluxo de instalação é totalmente ignorado para apps registrados localmente, portanto a pré-instalação nunca é executada com `yarn twenty dev`. Use `yarn twenty dev:function:exec --preInstall` para acioná-lo manualmente.
</Accordion>
<Accordion title="Pré-instalação vs pós-instalação: quando usar cada um" description="Escolhendo o hook de instalação correto">
@@ -13,7 +13,7 @@ Arquivos colocados em `public/` são:
* **Disponíveis em funções lógicas** — referencie URLs de recursos em e-mails, respostas de API ou qualquer lógica no lado do servidor.
* **Usados para metadados do marketplace** — os campos `logoUrl` e `screenshots` em `defineApplication()` referenciam arquivos desta pasta (por exemplo, `public/logo.png`). Eles são exibidos no marketplace quando seu app é publicado.
* **Sincronizados automaticamente no modo de desenvolvimento** — quando você adiciona, atualiza ou exclui um arquivo em `public/`, ele é sincronizado automaticamente com o servidor. Não é necessário reiniciar.
* **Incluídos nas compilações** — `yarn twenty build` agrupa todos os recursos públicos na saída de distribuição.
* **Incluídos nas compilações** — `yarn twenty dev:build` agrupa todos os recursos públicos na saída de distribuição.
## Acessando recursos públicos com `getPublicAssetUrl`
@@ -80,7 +80,7 @@ export default defineObject({
* Cada campo requer `name`, `type`, `label` e seu próprio `universalIdentifier` estável.
* O array `fields` é opcional — você pode definir objetos sem campos personalizados.
* Campos inline definidos aqui **não** precisam de `objectUniversalIdentifier` — ele é herdado do objeto pai. Use [`defineField()`](/l/pt/developers/extend/apps/data/extending-objects) para adicionar campos a objetos que não pertencem a você.
* Você pode criar novos objetos com `yarn twenty add object`, que orienta você sobre nomeação, campos e relacionamentos. Veja [Arquitetura → Scaffolding de entidades](/l/pt/developers/extend/apps/getting-started/scaffolding).
* Você pode criar novos objetos com `yarn twenty dev:add object`, que orienta você na definição de nomes, campos e relacionamentos. Veja [Arquitetura → Scaffolding de entidades](/l/pt/developers/extend/apps/getting-started/scaffolding).
<Note>
**Os campos base são adicionados automaticamente.** Quando você define um objeto personalizado, o Twenty cria campos padrão como `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` e `deletedAt` para você. Você não precisa declará‑los no seu array `fields` — apenas seus campos personalizados. Você pode substituir um campo padrão declarando um com o mesmo nome, mas isso raramente é uma boa ideia.
@@ -65,7 +65,7 @@ your-app/
│ npx create-twenty-app → yarn twenty dev (live sync) │
├─────────────────────────────────────────────────────────┤
│ Build & Deploy │
│ yarn twenty build → yarn twenty deploy
│ yarn twenty dev:build → yarn twenty app:publish
├─────────────────────────────────────────────────────────┤
│ Install flow │
│ upload → [pre-install] → metadata migration → │
@@ -77,7 +77,7 @@ your-app/
```
* **`yarn twenty dev`** — observa seus arquivos-fonte e sincroniza ao vivo as alterações com um servidor Twenty conectado. O cliente de API tipado é regenerado automaticamente quando o esquema muda.
* **`yarn twenty build`** — compila TypeScript, empacota funções de lógica e componentes de front-end com o esbuild e produz um manifesto.
* **`yarn twenty dev:build`** — compila TypeScript, empacota funções de lógica e componentes de front-end com o esbuild e produz um manifesto.
* **Hooks de pré/pós-instalação** — funções opcionais que são executadas durante a instalação. Veja [Hooks de instalação](/l/pt/developers/extend/apps/config/install-hooks) para detalhes.
## Próximos passos
@@ -6,44 +6,44 @@ icon: server
## Gerenciando o servidor local
Use `yarn twenty server` para controlar o contêiner Twenty local:
Use `yarn twenty docker:*` para controlar o contêiner Twenty local:
| Comando | O que faz |
| -------------------------------------- | ------------------------------------------------ |
| `yarn twenty server start` | Inicia o servidor (baixa a imagem se necessário) |
| `yarn twenty server start --port 3030` | Inicia em uma porta personalizada |
| `yarn twenty server stop` | Interrompe o servidor (preserva os dados) |
| `yarn twenty server status` | Mostra a URL, a versão e as credenciais de login |
| `yarn twenty server logs` | Transmite os logs do servidor |
| `yarn twenty server reset` | Apaga os dados e começa do zero |
| `yarn twenty server upgrade` | Baixa a imagem mais recente `twenty-app-dev` |
| `yarn twenty server upgrade 2.2.0` | Atualiza para uma versão específica |
| `yarn twenty docker:start` | Inicia o servidor (baixa a imagem se necessário) |
| `yarn twenty docker:start --port 3030` | Inicia em uma porta personalizada |
| `yarn twenty docker:stop` | Interrompe o servidor (preserva os dados) |
| `yarn twenty docker:status` | Mostra a URL, a versão e as credenciais de login |
| `yarn twenty docker:logs` | Transmite os logs do servidor |
| `yarn twenty docker:reset` | Apaga os dados e começa do zero |
| `yarn twenty docker:upgrade` | Baixa a imagem mais recente `twenty-app-dev` |
| `yarn twenty docker:upgrade 2.2.0` | Atualiza para uma versão específica |
Os dados são persistidos entre reinicializações em dois volumes do Docker (`twenty-app-dev-data` para PostgreSQL, `twenty-app-dev-storage` para arquivos). Use `reset` para apagar tudo.
## Atualizando a imagem do servidor
`yarn twenty server upgrade` baixa a imagem mais recente, compara os digests e só recria o contêiner se algo realmente tiver mudado. Os volumes são preservados — apenas o contêiner é substituído. Se uma nova imagem foi baixada e o contêiner estava em execução, a atualização inicia automaticamente um novo contêiner; execute `yarn twenty server start` depois para aguardar até que ele fique saudável.
`yarn twenty docker:upgrade` baixa a imagem mais recente, compara os digests e só recria o contêiner se algo realmente tiver mudado. Os volumes são preservados — apenas o contêiner é substituído. Se uma nova imagem foi baixada e o contêiner estava em execução, a atualização inicia automaticamente um novo contêiner; execute `yarn twenty docker:start` depois para aguardar até que ele fique saudável.
```bash filename="Terminal"
yarn twenty server upgrade # Latest
yarn twenty server upgrade 2.2.0 # Specific version
yarn twenty docker:upgrade # Latest
yarn twenty docker:upgrade 2.2.0 # Specific version
```
Verifique a versão em execução com `yarn twenty server status` (ele mostra o `APP_VERSION` incorporado ao contêiner).
Verifique a versão em execução com `yarn twenty docker:status` (ele mostra o `APP_VERSION` incorporado ao contêiner).
## Executando uma instância de teste paralela
Passe `--test` para qualquer comando de `server` para gerenciar uma segunda instância totalmente isolada — útil para testes de integração ou para experimentar sem tocar nos seus dados principais de desenvolvimento:
Passe `--test` para qualquer comando `docker:*` para gerenciar uma segunda instância totalmente isolada — útil para testes de integração ou para experimentar sem tocar nos seus dados principais de desenvolvimento:
| Comando | O que faz |
| ----------------------------------- | ------------------------------------------------ |
| `yarn twenty server start --test` | Inicia a instância de teste (padrão: porta 2021) |
| `yarn twenty server stop --test` | Para |
| `yarn twenty server status --test` | Mostra seu status |
| `yarn twenty server logs --test` | Transmite seus logs |
| `yarn twenty server reset --test` | Apaga seus dados |
| `yarn twenty server upgrade --test` | Atualiza sua imagem |
| `yarn twenty docker:start --test` | Inicia a instância de teste (padrão: porta 2021) |
| `yarn twenty docker:stop --test` | Para |
| `yarn twenty docker:status --test` | Mostra seu status |
| `yarn twenty docker:logs --test` | Transmite seus logs |
| `yarn twenty docker:reset --test` | Apaga seus dados |
| `yarn twenty docker:upgrade --test` | Atualiza sua imagem |
A instância de teste tem seu próprio contêiner (`twenty-app-dev-test`), volumes (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) e configuração — ela é executada junto com sua instância principal sem conflitos. Combine `--test` com `--port` para substituir 2021.
@@ -65,7 +65,7 @@ Adicione o script ao `package.json`:
}
```
Agora você pode executar `yarn twenty dev`, `yarn twenty server start` e o restante.
Agora você pode executar `yarn twenty dev`, `yarn twenty docker:start` e o restante.
<Note>
Não instale `twenty-sdk` globalmente — fixe-o por projeto, para que cada aplicativo use sua própria versão.
@@ -43,7 +43,7 @@ A ferramenta de scaffolding oferece iniciar um para você:
> **Você gostaria de configurar uma instância local do Twenty?**
* **Sim (recomendado)** — baixa a imagem Docker `twentycrm/twenty-app-dev` e a inicia na porta `2020`. Certifique-se de que o Docker esteja em execução primeiro.
* **Não** — escolha isto se você já tiver um servidor Twenty ao qual deseja se conectar. Você pode conectá-lo depois com `yarn twenty remote add`.
* **Não** — escolha isto se você já tiver um servidor Twenty ao qual deseja se conectar. Você pode conectá-lo depois com `yarn twenty remote:add`.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/developers/extends/apps/start-instance.png" alt="Deve iniciar instância local?" />
@@ -73,7 +73,7 @@ Seu terminal confirmará que tudo está configurado.
**Após esta fase:** você tem um servidor Twenty em execução em [http://localhost:2020](http://localhost:2020) com sua CLI autorizada a sincronizar com ele.
<Note>
Se o Docker não estiver instalado ou em execução, a ferramenta de scaffolding informará o comando de inicialização correto para o seu sistema operacional. Quando o Docker estiver ativo, você pode retomar com `yarn twenty server start` — sem necessidade de recriar o scaffolding.
Se o Docker não estiver instalado ou em execução, a ferramenta de scaffolding informará o comando de inicialização correto para o seu sistema operacional. Quando o Docker estiver ativo, você pode retomar com `yarn twenty docker:start` — sem necessidade de recriar o scaffolding.
</Note>
---
@@ -1,13 +1,13 @@
---
title: Scaffolding
description: Gere arquivos de entidade de forma interativa com `yarn twenty add` — objetos, campos, visualizações, funções de lógica e mais.
description: Gere arquivos de entidade de forma interativa com `yarn twenty dev:add` — objetos, campos, visualizações, funções de lógica e mais.
icon: wand-magic-sparkles
---
Em vez de criar arquivos de entidade manualmente, use o scaffolder interativo:
```bash filename="Terminal"
yarn twenty add
yarn twenty dev:add
```
Ele solicita que você escolha um tipo de entidade e orienta você pelos campos obrigatórios, depois grava um arquivo pronto para uso com um `universalIdentifier` estável e a chamada correta de `defineEntity()`.
@@ -15,29 +15,29 @@ Ele solicita que você escolha um tipo de entidade e orienta você pelos campos
Você também pode passar o tipo de entidade diretamente para pular o primeiro prompt:
```bash filename="Terminal"
yarn twenty add object
yarn twenty add logicFunction
yarn twenty add frontComponent
yarn twenty dev:add object
yarn twenty dev:add logicFunction
yarn twenty dev:add frontComponent
```
## Tipos de entidade disponíveis
| Tipo de entidade | Comando | Arquivo gerado |
| ------------------------- | ------------------------------------ | ------------------------------------------------------- |
| Objeto | `yarn twenty add object` | `src/objects/\<name>.ts` |
| Campo | `yarn twenty add field` | `src/fields/\<name>.ts` |
| Função lógica | `yarn twenty add logicFunction` | `src/logic-functions/\<name>.ts` |
| Componente de front-end | `yarn twenty add frontComponent` | `src/front-components/\<name>.tsx` |
| Função | `yarn twenty add role` | `src/roles/\<name>.ts` |
| Habilidade | `yarn twenty add skill` | `src/skills/\<name>.ts` |
| Agente | `yarn twenty add agent` | `src/agents/\<name>.ts` |
| Vista | `yarn twenty add view` | `src/views/\<name>.ts` |
| Item do menu de navegação | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\<name>.ts` |
| Layout da página | `yarn twenty add pageLayout` | `src/page-layouts/\<name>.ts` |
| Tipo de entidade | Comando | Arquivo gerado |
| ------------------------- | ---------------------------------------- | ------------------------------------------------------- |
| Objeto | `yarn twenty dev:add object` | `src/objects/\<name>.ts` |
| Campo | `yarn twenty dev:add field` | `src/fields/\<name>.ts` |
| Função lógica | `yarn twenty dev:add logicFunction` | `src/logic-functions/\<name>.ts` |
| Componente de front-end | `yarn twenty dev:add frontComponent` | `src/front-components/\<name>.tsx` |
| Função | `yarn twenty dev:add role` | `src/roles/\<name>.ts` |
| Habilidade | `yarn twenty dev:add skill` | `src/skills/\<name>.ts` |
| Agente | `yarn twenty dev:add agent` | `src/agents/\<name>.ts` |
| Vista | `yarn twenty dev:add view` | `src/views/\<name>.ts` |
| Item do menu de navegação | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\<name>.ts` |
| Layout da página | `yarn twenty dev:add pageLayout` | `src/page-layouts/\<name>.ts` |
## O que o scaffolder gera
Cada tipo de entidade tem seu próprio modelo. Por exemplo, `yarn twenty add object` solicita:
Cada tipo de entidade tem seu próprio modelo. Por exemplo, `yarn twenty dev:add object` solicita:
1. **Nome (singular)** — por exemplo, `invoice`
2. **Nome (plural)** — por exemplo, `invoices`
@@ -54,5 +54,5 @@ O tipo de entidade `field` é mais detalhado: ele solicita o nome do campo, rót
Use a opção `--path` para colocar o arquivo gerado em um local personalizado:
```bash filename="Terminal"
yarn twenty add logicFunction --path src/custom-folder
yarn twenty dev:add logicFunction --path src/custom-folder
```
@@ -4,7 +4,7 @@ description: Problemas comuns na primeira execução — Docker, versão do Node
icon: wrench
---
* **Erros do Docker** — Certifique-se de que o Docker Desktop (ou o daemon) esteja em execução antes de `yarn twenty server start`. A mensagem de erro mostrará o comando de inicialização correto para o seu sistema operacional.
* **Erros do Docker** — Certifique-se de que o Docker Desktop (ou o daemon) esteja em execução antes de `yarn twenty docker:start`. A mensagem de erro mostrará o comando de inicialização correto para o seu sistema operacional.
* **Versão errada do Node** — É necessário 24 ou superior. Verifique com `node -v`.
* **Falta o Yarn 4** — Execute `corepack enable`.
* **Dependências com problemas** — `rm -rf node_modules && yarn install`.
@@ -61,17 +61,17 @@ Tipos de gatilho disponíveis:
Você também pode executar manualmente uma função usando a CLI:
```bash filename="Terminal"
yarn twenty exec -n create-new-post-card -p '{"key": "value"}'
yarn twenty dev:function:exec -n create-new-post-card -p '{"key": "value"}'
```
```bash filename="Terminal"
yarn twenty exec -y e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
```
Você pode acompanhar os logs com:
```bash filename="Terminal"
yarn twenty logs
yarn twenty dev:function:logs
```
</Note>
@@ -340,7 +340,7 @@ O pacote `twenty-client-sdk` fornece dois clientes GraphQL tipados para interagi
<AccordionGroup>
<Accordion title="CoreApiClient" description="Consultar e modificar dados do espaço de trabalho (registros, objetos)">
`CoreApiClient` é o cliente principal para consultar e mutar dados do espaço de trabalho. Ele é **gerado a partir do schema do seu espaço de trabalho** durante `yarn twenty dev` ou `yarn twenty build`, então é totalmente tipado para corresponder aos seus objetos e campos.
`CoreApiClient` é o cliente principal para consultar e mutar dados do espaço de trabalho. Ele é **gerado a partir do schema do seu espaço de trabalho** durante `yarn twenty dev` ou `yarn twenty dev:build`, então é totalmente tipado para corresponder aos seus objetos e campos.
```ts
import { CoreApiClient } from 'twenty-client-sdk/core';
@@ -380,7 +380,7 @@ const { createCompany } = await client.mutation({
O cliente usa uma sintaxe de selection-set: passe `true` para incluir um campo, use `__args` para argumentos e aninhe objetos para relações. Você tem preenchimento automático e verificação de tipos completos com base no schema do seu espaço de trabalho.
<Note>
**CoreApiClient é gerado em tempo de dev/build.** Se você usá-lo sem executar primeiro `yarn twenty dev` ou `yarn twenty build`, ele lançará um erro. A geração ocorre automaticamente — a CLI analisa o schema GraphQL do seu espaço de trabalho e gera um cliente tipado usando `@genql/cli`.
**CoreApiClient é gerado em tempo de dev/build.** Se você usá-lo sem executar primeiro `yarn twenty dev` ou `yarn twenty dev:build`, ele lançará um erro. A geração ocorre automaticamente — a CLI analisa o schema GraphQL do seu espaço de trabalho e gera um cliente tipado usando `@genql/cli`.
</Note>
#### Usando CoreSchema para anotações de tipo
@@ -4,54 +4,54 @@ description: comandos `yarn twenty` para executar funções, transmitir logs, ge
icon: terminal
---
Além de `dev`, `build`, `add` e `typecheck`, a CLI `yarn twenty` fornece comandos para executar funções, visualizar logs e gerenciar instalações de aplicativos.
Além de `dev`, `dev:build`, `dev:add` e `dev:typecheck`, a CLI `yarn twenty` fornece comandos para executar funções, visualizar logs e gerenciar instalações de aplicativos.
## Executando funções (`yarn twenty exec`)
## Executando funções (`yarn twenty dev:function:exec`)
Execute manualmente uma função de lógica sem acioná-la via HTTP, cron ou evento de banco de dados:
```bash filename="Terminal"
# Execute by function name
yarn twenty exec -n create-new-post-card
yarn twenty dev:function:exec -n create-new-post-card
# Execute by universalIdentifier
yarn twenty exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
# Pass a JSON payload
yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}'
yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}'
# Execute the post-install function
yarn twenty exec --postInstall
yarn twenty dev:function:exec --postInstall
```
## Visualizando logs de funções (`yarn twenty logs`)
## Visualizando logs de funções (`yarn twenty dev:function:logs`)
Transmita os logs de execução das funções de lógica do seu aplicativo:
```bash filename="Terminal"
# Stream all function logs
yarn twenty logs
yarn twenty dev:function:logs
# Filter by function name
yarn twenty logs -n create-new-post-card
yarn twenty dev:function:logs -n create-new-post-card
# Filter by universalIdentifier
yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
yarn twenty dev:function:logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
```
<Note>
Isso é diferente de `yarn twenty server logs`, que mostra os logs do contêiner Docker. `yarn twenty logs` mostra os logs de execução de funções do seu aplicativo a partir do servidor Twenty.
Isso é diferente de `yarn twenty docker:logs`, que mostra os logs do contêiner Docker. `yarn twenty dev:function:logs` mostra os logs de execução de funções do seu aplicativo a partir do servidor Twenty.
</Note>
## Desinstalando um aplicativo (`yarn twenty uninstall`)
## Desinstalando um aplicativo (`yarn twenty app:uninstall`)
Remova seu aplicativo do espaço de trabalho ativo:
```bash filename="Terminal"
yarn twenty uninstall
yarn twenty app:uninstall
# Skip the confirmation prompt
yarn twenty uninstall --yes
yarn twenty app:uninstall --yes
```
## Gerenciando remotos
@@ -60,19 +60,19 @@ Um **remoto** é um servidor Twenty ao qual seu aplicativo se conecta. Durante a
```bash filename="Terminal"
# Add a new remote (opens a browser for OAuth login)
yarn twenty remote add
yarn twenty remote:add
# Connect to a local Twenty server (auto-detects port 2020 or 3000)
yarn twenty remote add --local
yarn twenty remote:add --local
# Add a remote non-interactively (useful for CI)
yarn twenty remote add --api-url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote
yarn twenty remote:add --url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote
# List all configured remotes
yarn twenty remote list
yarn twenty remote:list
# Switch the active remote
yarn twenty remote switch <name>
# Set the active remote
yarn twenty remote:use <name>
```
Suas credenciais são armazenadas em `~/.twenty/config.json`.
@@ -9,9 +9,9 @@ A **camada de operações** é tudo o que você faz *para* o seu app em vez de *
```text
develop ─▶ test ─▶ build ─▶ deploy / publish
─────── ──── ───── ─────────────────
yarn yarn yarn yarn twenty deploy (tarball → one server)
yarn yarn yarn yarn twenty app:publish --private (tarball → one server)
twenty test twenty
dev build yarn twenty publish (npm → marketplace)
dev dev:build yarn twenty app:publish (npm → marketplace)
```
## Nesta secção
@@ -18,10 +18,10 @@ Ambos os caminhos começam na mesma etapa de **build**.
Execute o comando build para compilar seu app e gerar um `manifest.json` pronto para distribuição:
```bash filename="Terminal"
yarn twenty build
yarn twenty dev:build
```
Isso compila seu código-fonte em TypeScript, transpila funções de lógica e componentes de front-end e grava tudo em `.twenty/output/`. Adicione `--tarball` para também gerar um pacote `.tgz` para distribuição manual ou para o comando de deploy.
Isso compila seu código-fonte em TypeScript, transpila funções de lógica e componentes de front-end e grava tudo em `.twenty/output/`. Adicione `--tarball` para também gerar um pacote `.tgz` para distribuição manual ou para o comando de publish.
## Implantando em um servidor (tarball)
@@ -34,7 +34,7 @@ Antes de implantar, você precisa de um remote configurado apontando para o serv
Adicionar um remote:
```bash filename="Terminal"
yarn twenty remote add --api-url https://your-twenty-server.com --as production
yarn twenty remote:add --url https://your-twenty-server.com --as production
```
### Implantando
@@ -42,9 +42,9 @@ yarn twenty remote add --api-url https://your-twenty-server.com --as production
Compile e envie seu aplicativo para o servidor em uma única etapa:
```bash filename="Terminal"
yarn twenty deploy
yarn twenty app:publish --private
# To deploy to a specific remote:
# yarn twenty deploy --remote production
# yarn twenty app:publish --private --remote production
```
### Compartilhando um aplicativo implantado
@@ -68,7 +68,7 @@ Ao atualizar um aplicativo empacotado como tarball já implantado, o servidor ex
Para lançar uma atualização:
1. Atualize o campo `version` no seu `package.json` (por exemplo, `1.2.3` → `1.2.4`, `1.3.0` ou `2.0.0`)
2. Execute `yarn twenty deploy` (ou `yarn twenty deploy --remote production`)
2. Execute `yarn twenty app:publish --private` (ou `yarn twenty app:publish --private --remote production`)
3. Os espaços de trabalho que têm o aplicativo instalado verão a atualização disponível em suas configurações
<Note>
@@ -121,7 +121,7 @@ Executa testes de integração a cada push para `main` e a cada pull request.
**O que faz:**
1. Faz checkout do código-fonte do seu app.
2. Inicia uma instância de teste do Twenty isolada usando a ação composta `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (o equivalente em CI de `yarn twenty server start --test`).
2. Inicia uma instância de teste do Twenty isolada usando a ação composta `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (o equivalente em CI de `yarn twenty docker:start --test`).
3. Habilita o Corepack, configura o Node.js a partir do seu `.nvmrc` e instala as dependências com `yarn install --immutable`.
4. Executa `yarn test`, passando `TWENTY_API_URL` e `TWENTY_API_KEY` da instância iniciada para que seus testes possam se comunicar com um servidor real.
@@ -139,7 +139,7 @@ Faz o deploy do seu app para um servidor Twenty configurado a cada push para `ma
**O que faz:**
1. Faz checkout do head do PR (para PRs rotulados) ou do commit enviado.
2. Executa `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — o equivalente em CI de `yarn twenty deploy`.
2. Executa `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — o equivalente em CI de `yarn twenty app:publish --private`.
3. Executa `twentyhq/twenty/.github/actions/install-twenty-app@main` para que a versão recém-implantada seja instalada no espaço de trabalho de destino.
**Configuração obrigatória:**
@@ -208,13 +208,13 @@ Capturas de tela de qualquer proporção são exibidas por completo e nunca são
### Publicar
```bash filename="Terminal"
yarn twenty publish
yarn twenty app:publish
```
Para publicar sob uma dist-tag específica (por exemplo, `beta` ou `next`):
```bash filename="Terminal"
yarn twenty publish --tag beta
yarn twenty app:publish --tag beta
```
### Como funciona a descoberta no marketplace
@@ -224,9 +224,9 @@ O servidor Twenty sincroniza seu catálogo do marketplace a partir do registro d
Você pode acionar a sincronização imediatamente em vez de esperar:
```bash filename="Terminal"
yarn twenty server catalog-sync
yarn twenty dev:catalog-sync
# To target a specific remote:
# yarn twenty server catalog-sync --remote production
# yarn twenty dev:catalog-sync --remote production
```
Os metadados exibidos no marketplace vêm da sua configuração `defineApplication()` — campos como `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` e `termsUrl`.
@@ -259,12 +259,12 @@ jobs:
node-version: "24"
registry-url: https://registry.npmjs.org
- run: yarn install --immutable
- run: npx twenty build
- run: npx twenty dev:build
- run: npm publish --provenance --access public
working-directory: .twenty/output
```
Para outros sistemas de CI (GitLab CI, CircleCI etc.), aplicam-se os mesmos três comandos: `yarn install`, `yarn twenty build` e, em seguida, `npm publish` a partir de `.twenty/output`.
Para outros sistemas de CI (GitLab CI, CircleCI etc.), aplicam-se os mesmos três comandos: `yarn install`, `yarn twenty dev:build` e, em seguida, `npm publish` a partir de `.twenty/output`.
<Note>
**Proveniência do npm** é opcional, mas recomendada. Publicar com `--provenance` adiciona um selo de confiança à sua listagem no npm, permitindo que os usuários verifiquem que o pacote foi construído a partir de um commit específico em um pipeline de CI público. Consulte a [documentação de proveniência do npm](https://docs.npmjs.com/generating-provenance-statements) para instruções de configuração.
@@ -281,7 +281,7 @@ Vá para a página **Configurações > Aplicações** no Twenty, onde é possív
Você também pode instalar apps pela linha de comando:
```bash filename="Terminal"
yarn twenty install
yarn twenty app:install
```
<Note>
@@ -290,5 +290,5 @@ O servidor impõe o versionamento semver na instalação, espelhando as regras d
* Instalar a mesma versão que já está instalada no seu espaço de trabalho é rejeitado com um erro `APP_ALREADY_INSTALLED`.
* Instalar uma versão inferior à atualmente instalada é rejeitado com um erro `CANNOT_DOWNGRADE_APPLICATION`.
Para instalar uma versão mais recente, implante ou publique-a primeiro e, em seguida, execute novamente `yarn twenty install`.
Para instalar uma versão mais recente, implante ou publique-a primeiro e, em seguida, execute novamente `yarn twenty app:install`.
</Note>
@@ -235,7 +235,7 @@ yarn test:watch
Você também pode executar a verificação de tipos no seu aplicativo sem executar os testes:
```bash filename="Terminal"
yarn twenty typecheck
yarn twenty dev:typecheck
```
Isso executa `tsc --noEmit` e informa quaisquer erros de tipo.
@@ -45,7 +45,7 @@ export default definePostInstallLogicFunction({
Puteți, de asemenea, să executați manual funcția post-instalare oricând folosind CLI:
```bash filename="Terminal"
yarn twenty exec --postInstall
yarn twenty dev:function:exec --postInstall
```
Puncte cheie:
@@ -60,7 +60,7 @@ Puncte cheie:
* Este permisă o singură funcție de post-instalare per aplicație. Construirea manifestului va genera o eroare dacă este detectată mai mult de una.
* `universalIdentifier`, `shouldRunOnVersionUpgrade` și `shouldRunSynchronously` ale funcției sunt atașate automat la manifestul aplicației în câmpul `postInstallLogicFunction` în timpul build-ului — nu este nevoie să le referiți în [`defineApplication()`](/l/ro/developers/extend/apps/config/application).
* Timpul de expirare implicit este setat la 300 de secunde (5 minute) pentru a permite sarcini de configurare mai lungi, cum ar fi popularea datelor.
* **Nu se execută în modul dev**: când o aplicație este înregistrată local (prin `yarn twenty dev`), serverul sare complet peste fluxul de instalare și sincronizează fișierele direct prin watcher-ul CLI — astfel încât post-install nu rulează niciodată în modul dev, indiferent de `shouldRunSynchronously`. Folosiți `yarn twenty exec --postInstall` pentru a-l declanșa manual într-un workspace care rulează.
* **Nu se execută în modul dev**: când o aplicație este înregistrată local (prin `yarn twenty dev`), serverul sare complet peste fluxul de instalare și sincronizează fișierele direct prin watcher-ul CLI — astfel încât post-install nu rulează niciodată în modul dev, indiferent de `shouldRunSynchronously`. Folosiți `yarn twenty dev:function:exec --postInstall` pentru a-l declanșa manual într-un workspace care rulează.
</Accordion>
<Accordion title="definePreInstallLogicFunction" description="Rulează înainte ca migrarea metadatelor workspace-ului să fie aplicată">
@@ -87,7 +87,7 @@ export default definePreInstallLogicFunction({
Puteți, de asemenea, să executați manual funcția de pre-instalare oricând folosind CLI:
```bash filename="Terminal"
yarn twenty exec --preInstall
yarn twenty dev:function:exec --preInstall
```
Puncte cheie:
@@ -96,7 +96,7 @@ Puncte cheie:
* **Când rulează hook-ul**: poziționat chiar înainte de migrarea metadatelor workspace-ului (`synchronizeFromManifest`). Înainte de execuție, serverul rulează un "sync redus", pur aditiv, care înregistrează funcția de pre-instalare a versiunii **noi** în metadatele workspace-ului — nimic altceva nu este atins — și apoi o execută. Deoarece acest sync este doar aditiv, obiectele, câmpurile și datele versiunii precedente sunt încă intacte când rulează handlerul dvs.: puteți citi și face backup în siguranță stării pre-migrare.
* **Model de execuție**: pre-install este executat **sincron** și **blochează instalarea**. Dacă handlerul aruncă o eroare, instalarea este întreruptă înainte ca orice modificări de schemă să fie aplicate — workspace-ul rămâne la versiunea anterioară într-o stare consistentă. Acest lucru este intenționat: pre-install este ultima dvs. șansă de a refuza o actualizare riscantă.
* La fel ca la post-install, este permisă o singură funcție de pre-instalare per aplicație. Este atașată automat la manifestul aplicației sub `preInstallLogicFunction` în timpul build-ului.
* **Nu se execută în modul dev**: la fel ca post-install — fluxul de instalare este sărit complet pentru aplicațiile înregistrate local, astfel încât pre-install nu rulează niciodată sub `yarn twenty dev`. Folosiți `yarn twenty exec --preInstall` pentru a-l declanșa manual.
* **Nu se execută în modul dev**: la fel ca post-install — fluxul de instalare este sărit complet pentru aplicațiile înregistrate local, astfel încât pre-install nu rulează niciodată sub `yarn twenty dev`. Folosiți `yarn twenty dev:function:exec --preInstall` pentru a-l declanșa manual.
</Accordion>
<Accordion title="Pre-install vs post-install: când să folosiți fiecare" description="Alegerea hook-ului de instalare potrivit">
@@ -13,7 +13,7 @@ Fișierele plasate în `public/` sunt:
* **Disponibile în funcțiile logice** — referiți URL-urile resurselor în e-mailuri, răspunsuri API sau orice logică pe server.
* **Utilizate pentru metadatele marketplace-ului** — câmpurile `logoUrl` și `screenshots` din `defineApplication()` fac referire la fișiere din acest folder (de ex., `public/logo.png`). Acestea sunt afișate în marketplace când aplicația este publicată.
* **Sincronizate automat în modul de dezvoltare** — când adăugați, actualizați sau ștergeți un fișier în `public/`, acesta este sincronizat automat cu serverul. Nu este nevoie de repornire.
* **Incluse în build-uri** — `yarn twenty build` împachetează toate resursele publice în outputul de distribuție.
* **Incluse în build-uri** — `yarn twenty dev:build` împachetează toate resursele publice în outputul de distribuție.
## Accesarea resurselor publice cu `getPublicAssetUrl`
@@ -80,7 +80,7 @@ export default defineObject({
* Fiecare câmp necesită un `name`, un `type`, un `label` și propriul `universalIdentifier` stabil.
* Tabloul `fields` este opțional — puteți defini obiecte fără câmpuri personalizate.
* Câmpurile inline definite aici **nu** au nevoie de `objectUniversalIdentifier` — este moștenit de la obiectul părinte. Folosiți [`defineField()`](/l/ro/developers/extend/apps/data/extending-objects) pentru a adăuga câmpuri la obiecte care nu vă aparțin.
* Puteți genera obiecte noi cu `yarn twenty add object`, care vă ghidează prin denumire, câmpuri și relații. Consultați [Arhitectură → Generarea entităților](/l/ro/developers/extend/apps/getting-started/scaffolding).
* Puteți genera obiecte noi cu `yarn twenty dev:add object`, care vă ghidează prin denumire, câmpuri și relații. Consultați [Arhitectură → Generarea entităților](/l/ro/developers/extend/apps/getting-started/scaffolding).
<Note>
**Câmpurile de bază sunt adăugate automat.** Când definiți un obiect personalizat, Twenty creează pentru dvs. câmpuri standard precum `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` și `deletedAt`. Nu trebuie să le declarați în tabloul `fields` — doar câmpurile dvs. personalizate. Puteți suprascrie un câmp implicit declarând unul cu același nume, dar acest lucru este rareori o idee bună.
@@ -65,7 +65,7 @@ your-app/
│ npx create-twenty-app → yarn twenty dev (live sync) │
├─────────────────────────────────────────────────────────┤
│ Build & Deploy │
│ yarn twenty build → yarn twenty deploy
│ yarn twenty dev:build → yarn twenty app:publish
├─────────────────────────────────────────────────────────┤
│ Install flow │
│ upload → [pre-install] → metadata migration → │
@@ -77,7 +77,7 @@ your-app/
```
* **`yarn twenty dev`** — monitorizează fișierele sursă și sincronizează în timp real modificările către un server Twenty conectat. Clientul API tipizat este regenerat automat atunci când schema se schimbă.
* **`yarn twenty build`** — compilează TypeScript, împachetează funcțiile logice și componentele front-end cu esbuild și produce un manifest.
* **`yarn twenty dev:build`** — compilează TypeScript, împachetează funcțiile logice și componentele front-end cu esbuild și produce un manifest.
* **Hook-uri pre/post-instalare** — funcții opționale care rulează în timpul instalării. Vezi [Install Hooks](/l/ro/developers/extend/apps/config/install-hooks) pentru detalii.
## Pașii următori
@@ -6,44 +6,44 @@ icon: server
## Gestionarea serverului local
Folosiți `yarn twenty server` pentru a controla containerul Twenty local:
Folosiți `yarn twenty docker:*` pentru a controla containerul Twenty local:
| Comandă | Ce face |
| -------------------------------------- | ------------------------------------------------------------ |
| `yarn twenty server start` | Pornește serverul (descarcă imaginea dacă este necesar) |
| `yarn twenty server start --port 3030` | Pornește pe un port personalizat |
| `yarn twenty server stop` | Oprește serverul (păstrează datele) |
| `yarn twenty server status` | Afișează URL-ul, versiunea și credențialele de autentificare |
| `yarn twenty server logs` | Transmite în flux jurnalele serverului |
| `yarn twenty server reset` | Șterge datele și pornește de la zero |
| `yarn twenty server upgrade` | Descarcă cea mai recentă imagine `twenty-app-dev` |
| `yarn twenty server upgrade 2.2.0` | Actualizează la o versiune specifică |
| `yarn twenty docker:start` | Pornește serverul (descarcă imaginea dacă este necesar) |
| `yarn twenty docker:start --port 3030` | Pornește pe un port personalizat |
| `yarn twenty docker:stop` | Oprește serverul (păstrează datele) |
| `yarn twenty docker:status` | Afișează URL-ul, versiunea și credențialele de autentificare |
| `yarn twenty docker:logs` | Transmite în flux jurnalele serverului |
| `yarn twenty docker:reset` | Șterge datele și pornește de la zero |
| `yarn twenty docker:upgrade` | Descarcă cea mai recentă imagine `twenty-app-dev` |
| `yarn twenty docker:upgrade 2.2.0` | Actualizează la o versiune specifică |
Datele persistă între reporniri în două volume Docker (`twenty-app-dev-data` pentru PostgreSQL, `twenty-app-dev-storage` pentru fișiere). Folosiți `reset` pentru a șterge totul.
## Actualizarea imaginii serverului
`yarn twenty server upgrade` descarcă cea mai recentă imagine, compară digest-urile și recreează containerul doar dacă s-a schimbat ceva. Volumele de date sunt păstrate — doar containerul este înlocuit. Dacă a fost descărcată o imagine nouă și containerul rula, actualizarea pornește automat un container nou; rulați apoi `yarn twenty server start` pentru a aștepta până când devine funcțional.
`yarn twenty docker:upgrade` descarcă cea mai recentă imagine, compară digest-urile și recreează containerul doar dacă s-a schimbat ceva. Volumele de date sunt păstrate — doar containerul este înlocuit. Dacă a fost descărcată o imagine nouă și containerul rula, actualizarea pornește automat un container nou; rulați apoi `yarn twenty docker:start` pentru a aștepta până când devine funcțional.
```bash filename="Terminal"
yarn twenty server upgrade # Latest
yarn twenty server upgrade 2.2.0 # Specific version
yarn twenty docker:upgrade # Latest
yarn twenty docker:upgrade 2.2.0 # Specific version
```
Puteți verifica versiunea care rulează cu `yarn twenty server status` (aceasta afișează `APP_VERSION` încorporat în container).
Puteți verifica versiunea care rulează cu `yarn twenty docker:status` (aceasta afișează `APP_VERSION` încorporat în container).
## Rularea unei instanțe de test în paralel
Adăugați `--test` la orice comandă `server` pentru a gestiona o a doua instanță, complet izolată — utilă pentru teste de integrare sau pentru a experimenta fără a atinge datele principale de dezvoltare:
Adăugați `--test` la orice comandă `docker:*` pentru a gestiona o a doua instanță, complet izolată — utilă pentru teste de integrare sau pentru a experimenta fără a atinge datele principale de dezvoltare:
| Comandă | Ce face |
| ----------------------------------- | --------------------------------------------------- |
| `yarn twenty server start --test` | Pornește instanța de test (implicit pe portul 2021) |
| `yarn twenty server stop --test` | Oprește-o |
| `yarn twenty server status --test` | Afișează-i starea |
| `yarn twenty server logs --test` | Transmite în flux jurnalele sale |
| `yarn twenty server reset --test` | Șterge-i datele |
| `yarn twenty server upgrade --test` | Actualizează-i imaginea |
| `yarn twenty docker:start --test` | Pornește instanța de test (implicit pe portul 2021) |
| `yarn twenty docker:stop --test` | Oprește-o |
| `yarn twenty docker:status --test` | Afișează-i starea |
| `yarn twenty docker:logs --test` | Transmite în flux jurnalele sale |
| `yarn twenty docker:reset --test` | Șterge-i datele |
| `yarn twenty docker:upgrade --test` | Actualizează-i imaginea |
Instanța de test are propriul container (`twenty-app-dev-test`), propriile volume (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) și propria configurație — rulează alături de instanța principală, fără conflicte. Combinați `--test` cu `--port` pentru a înlocui portul 2021.
@@ -65,7 +65,7 @@ Adăugați scriptul în `package.json`:
}
```
Acum puteți rula `yarn twenty dev`, `yarn twenty server start` și restul.
Acum puteți rula `yarn twenty dev`, `yarn twenty docker:start` și restul.
<Note>
Nu instalați `twenty-sdk` global — fixați-l per proiect astfel încât fiecare aplicație să folosească propria versiune.
@@ -43,7 +43,7 @@ Generatorul de schelet vă propune să pornească unul pentru dvs.:
> **Doriți să configurați o instanță Twenty locală?**
* **Yes (recomandat)** — descarcă imaginea Docker `twentycrm/twenty-app-dev` și o pornește pe portul `2020`. Asigurați-vă mai întâi că Docker rulează.
* **No** — alegeți această opțiune dacă aveți deja un server Twenty la care doriți să vă conectați. Îl puteți conecta ulterior cu `yarn twenty remote add`.
* **No** — alegeți această opțiune dacă aveți deja un server Twenty la care doriți să vă conectați. Îl puteți conecta ulterior cu `yarn twenty remote:add`.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/developers/extends/apps/start-instance.png" alt="Porniți instanța locală?" />
@@ -73,7 +73,7 @@ Terminalul va confirma că totul este configurat.
**După această fază:** aveți un server Twenty care rulează la [http://localhost:2020](http://localhost:2020), iar CLI-ul dvs. este autorizat să sincronizeze cu acesta.
<Note>
Dacă Docker nu este instalat sau nu rulează, generatorul de schelet vă va indica comanda corectă de pornire pentru sistemul dvs. de operare. După ce Docker rulează, puteți continua cu `yarn twenty server start` — nu este nevoie să recreați scheletul.
Dacă Docker nu este instalat sau nu rulează, generatorul de schelet vă va indica comanda corectă de pornire pentru sistemul dvs. de operare. După ce Docker rulează, puteți continua cu `yarn twenty docker:start` — nu este nevoie să recreați scheletul.
</Note>
---
@@ -1,13 +1,13 @@
---
title: Generare automată
description: Generați fișiere de entități în mod interactiv cu yarn twenty add — obiecte, câmpuri, vizualizări, funcții logice și altele.
description: Generați fișiere de entități în mod interactiv cu yarn twenty dev:add — obiecte, câmpuri, vizualizări, funcții logice și altele.
icon: wand-magic-sparkles
---
În loc să creați manual fișiere de entități, folosiți generatorul interactiv (scaffolder):
```bash filename="Terminal"
yarn twenty add
yarn twenty dev:add
```
Vă cere să alegeți un tip de entitate și vă ghidează prin câmpurile necesare, apoi scrie un fișier gata de utilizare cu un `universalIdentifier` stabil și apelul corect `defineEntity()`.
@@ -15,29 +15,29 @@ Vă cere să alegeți un tip de entitate și vă ghidează prin câmpurile neces
Puteți de asemenea să transmiteți direct tipul de entitate pentru a sări peste primul prompt:
```bash filename="Terminal"
yarn twenty add object
yarn twenty add logicFunction
yarn twenty add frontComponent
yarn twenty dev:add object
yarn twenty dev:add logicFunction
yarn twenty dev:add frontComponent
```
## Tipuri de entități disponibile
| Tipul entității | Comandă | Fișier generat |
| ---------------------------- | ------------------------------------ | ------------------------------------------------------- |
| Obiect | `yarn twenty add object` | `src/objects/\<name>.ts` |
| Câmp | `yarn twenty add field` | `src/fields/\<name>.ts` |
| Funcție logică | `yarn twenty add logicFunction` | `src/logic-functions/\<name>.ts` |
| Componentă frontend | `yarn twenty add frontComponent` | `src/front-components/\<name>.tsx` |
| Rol | `yarn twenty add role` | `src/roles/\<name>.ts` |
| Abilitate | `yarn twenty add skill` | `src/skills/\<name>.ts` |
| Agent | `yarn twenty add agent` | `src/agents/\<name>.ts` |
| Vizualizare | `yarn twenty add view` | `src/views/\<name>.ts` |
| Element de meniu de navigare | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\<name>.ts` |
| Machetă de pagină | `yarn twenty add pageLayout` | `src/page-layouts/\<name>.ts` |
| Tipul entității | Comandă | Fișier generat |
| ---------------------------- | ---------------------------------------- | ------------------------------------------------------- |
| Obiect | `yarn twenty dev:add object` | `src/objects/\<name>.ts` |
| Câmp | `yarn twenty dev:add field` | `src/fields/\<name>.ts` |
| Funcție logică | `yarn twenty dev:add logicFunction` | `src/logic-functions/\<name>.ts` |
| Componentă frontend | `yarn twenty dev:add frontComponent` | `src/front-components/\<name>.tsx` |
| Rol | `yarn twenty dev:add role` | `src/roles/\<name>.ts` |
| Abilitate | `yarn twenty dev:add skill` | `src/skills/\<name>.ts` |
| Agent | `yarn twenty dev:add agent` | `src/agents/\<name>.ts` |
| Vizualizare | `yarn twenty dev:add view` | `src/views/\<name>.ts` |
| Element de meniu de navigare | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\<name>.ts` |
| Machetă de pagină | `yarn twenty dev:add pageLayout` | `src/page-layouts/\<name>.ts` |
## Ce generează scaffolder-ul
Fiecare tip de entitate are propriul său șablon. De exemplu, `yarn twenty add object` solicită:
Fiecare tip de entitate are propriul său șablon. De exemplu, `yarn twenty dev:add object` solicită:
1. **Nume (singular)** — de ex., `invoice`
2. **Nume (plural)** — de ex., `invoices`
@@ -54,5 +54,5 @@ Tipul de entitate `field` este mai detaliat: solicită numele câmpului, etichet
Utilizați opțiunea `--path` pentru a plasa fișierul generat într-o locație personalizată:
```bash filename="Terminal"
yarn twenty add logicFunction --path src/custom-folder
yarn twenty dev:add logicFunction --path src/custom-folder
```
@@ -4,7 +4,7 @@ description: Probleme comune la prima rulare — Docker, versiunea Node, Yarn, d
icon: wrench
---
* **Erori Docker** — Asigurați-vă că Docker Desktop (sau daemonul) rulează înainte de `yarn twenty server start`. Mesajul de eroare va afișa comanda corectă de pornire pentru sistemul dvs. de operare.
* **Erori Docker** — Asigurați-vă că Docker Desktop (sau daemonul) rulează înainte de `yarn twenty docker:start`. Mesajul de eroare va afișa comanda corectă de pornire pentru sistemul dvs. de operare.
* **Versiune Node greșită** — Aveți nevoie de 24+. Verificați cu `node -v`.
* **Lipsește Yarn 4** — Rulați `corepack enable`.
* **Dependențe nefuncționale** — `rm -rf node_modules && yarn install`.
@@ -61,17 +61,17 @@ Tipuri de declanșatoare disponibile:
Puteți, de asemenea, să executați manual o funcție folosind CLI:
```bash filename="Terminal"
yarn twenty exec -n create-new-post-card -p '{"key": "value"}'
yarn twenty dev:function:exec -n create-new-post-card -p '{"key": "value"}'
```
```bash filename="Terminal"
yarn twenty exec -y e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
```
Puteți urmări jurnalele cu:
```bash filename="Terminal"
yarn twenty logs
yarn twenty dev:function:logs
```
</Note>
@@ -341,7 +341,7 @@ Pachetul `twenty-client-sdk` oferă doi clienți GraphQL tipați pentru a intera
<AccordionGroup>
<Accordion title="CoreApiClient" description="Interogați și modificați datele spațiului de lucru (înregistrări, obiecte)">
`CoreApiClient` este clientul principal pentru interogarea și modificarea datelor din spațiul de lucru. Este generat din schema spațiului de lucru în timpul `yarn twenty dev` sau `yarn twenty build`, astfel încât este complet tipizat pentru a corespunde obiectelor și câmpurilor dvs.
`CoreApiClient` este clientul principal pentru interogarea și modificarea datelor din spațiul de lucru. Este **generat din schema spațiului de lucru** în timpul `yarn twenty dev` sau `yarn twenty dev:build`, astfel încât este complet tipizat pentru a corespunde obiectelor și câmpurilor dvs.
```ts
import { CoreApiClient } from 'twenty-client-sdk/core';
@@ -381,7 +381,7 @@ const { createCompany } = await client.mutation({
Clientul folosește o sintaxă de tip selection-set: transmiteți `true` pentru a include un câmp, folosiți `__args` pentru argumente și imbricați obiecte pentru relații. Obțineți autocompletare și verificare completă a tipurilor, pe baza schemei spațiului dvs. de lucru.
<Note>
**CoreApiClient este generat în timpul dev/build.** Dacă îl utilizați fără a rula mai întâi `yarn twenty dev` sau `yarn twenty build`, va arunca o eroare. Generarea are loc automat — CLI inspectează schema GraphQL a spațiului dvs. de lucru și generează un client tipizat folosind `@genql/cli`.
**CoreApiClient este generat în timpul dev/build.** Dacă îl utilizați fără a rula mai întâi `yarn twenty dev` sau `yarn twenty dev:build`, va arunca o eroare. Generarea are loc automat — CLI inspectează schema GraphQL a spațiului dvs. de lucru și generează un client tipizat folosind `@genql/cli`.
</Note>
#### Folosirea CoreSchema pentru adnotări de tip
@@ -4,54 +4,54 @@ description: comenzi `yarn twenty` pentru executarea funcțiilor, transmiterea
icon: terminal
---
Dincolo de `dev`, `build`, `add` și `typecheck`, `yarn twenty` CLI oferă comenzi pentru executarea funcțiilor, vizualizarea jurnalelor și gestionarea instalărilor de aplicații.
Dincolo de `dev`, `dev:build`, `dev:add` și `dev:typecheck`, `yarn twenty` CLI oferă comenzi pentru executarea funcțiilor, vizualizarea jurnalelor și gestionarea instalărilor de aplicații.
## Executarea funcțiilor (`yarn twenty exec`)
## Executarea funcțiilor (`yarn twenty dev:function:exec`)
Rulați manual o funcție logică fără a o declanșa prin HTTP, cron sau eveniment de bază de date:
```bash filename="Terminal"
# Execute by function name
yarn twenty exec -n create-new-post-card
yarn twenty dev:function:exec -n create-new-post-card
# Execute by universalIdentifier
yarn twenty exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
# Pass a JSON payload
yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}'
yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}'
# Execute the post-install function
yarn twenty exec --postInstall
yarn twenty dev:function:exec --postInstall
```
## Vizualizarea jurnalelor funcțiilor (`yarn twenty logs`)
## Vizualizarea jurnalelor funcțiilor (`yarn twenty dev:function:logs`)
Transmiteți în flux jurnalele de execuție pentru funcțiile logice ale aplicației:
```bash filename="Terminal"
# Stream all function logs
yarn twenty logs
yarn twenty dev:function:logs
# Filter by function name
yarn twenty logs -n create-new-post-card
yarn twenty dev:function:logs -n create-new-post-card
# Filter by universalIdentifier
yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
yarn twenty dev:function:logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
```
<Note>
Acest lucru este diferit de `yarn twenty server logs`, care afișează jurnalele containerului Docker. `yarn twenty logs` afișează jurnalele de execuție ale funcțiilor aplicației de pe serverul Twenty.
Acest lucru este diferit de `yarn twenty docker:logs`, care afișează jurnalele containerului Docker. `yarn twenty dev:function:logs` afișează jurnalele de execuție ale funcțiilor aplicației de pe serverul Twenty.
</Note>
## Dezinstalarea unei aplicații (`yarn twenty uninstall`)
## Dezinstalarea unei aplicații (`yarn twenty app:uninstall`)
Eliminați aplicația din spațiul de lucru activ:
```bash filename="Terminal"
yarn twenty uninstall
yarn twenty app:uninstall
# Skip the confirmation prompt
yarn twenty uninstall --yes
yarn twenty app:uninstall --yes
```
## Gestionarea remote-urilor
@@ -60,19 +60,19 @@ Un „remote” este un server Twenty la care se conectează aplicația. În tim
```bash filename="Terminal"
# Add a new remote (opens a browser for OAuth login)
yarn twenty remote add
yarn twenty remote:add
# Connect to a local Twenty server (auto-detects port 2020 or 3000)
yarn twenty remote add --local
yarn twenty remote:add --local
# Add a remote non-interactively (useful for CI)
yarn twenty remote add --api-url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote
yarn twenty remote:add --url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote
# List all configured remotes
yarn twenty remote list
yarn twenty remote:list
# Switch the active remote
yarn twenty remote switch <name>
# Set the active remote
yarn twenty remote:use <name>
```
Acreditările dvs. sunt stocate în `~/.twenty/config.json`.
@@ -9,9 +9,9 @@ icon: rocket
```text
develop ─▶ test ─▶ build ─▶ deploy / publish
─────── ──── ───── ─────────────────
yarn yarn yarn yarn twenty deploy (tarball → one server)
yarn yarn yarn yarn twenty app:publish --private (tarball → one server)
twenty test twenty
dev build yarn twenty publish (npm → marketplace)
dev dev:build yarn twenty app:publish (npm → marketplace)
```
## În această secțiune
@@ -18,10 +18,10 @@ Ambele căi pornesc din aceeași etapă de **build**.
Rulează comanda `build` pentru a compila aplicația și a genera un `manifest.json` pregătit pentru distribuire:
```bash filename="Terminal"
yarn twenty build
yarn twenty dev:build
```
Aceasta compilează sursele TypeScript, transpilează funcțiile de logică și componentele de front-end și scrie totul în `.twenty/output/`. Adaugă `--tarball` pentru a produce și un pachet `.tgz` pentru distribuire manuală sau pentru comanda de deploy.
Aceasta compilează sursele TypeScript, transpilează funcțiile de logică și componentele de front-end și scrie totul în `.twenty/output/`. Adaugă `--tarball` pentru a produce și un pachet `.tgz` pentru distribuire manuală sau pentru comanda de publish.
## Implementare pe un server (tarball)
@@ -34,7 +34,7 @@ Pentru aplicațiile pe care nu le dorești disponibile public — instrumente pr
Adaugă un remote:
```bash filename="Terminal"
yarn twenty remote add --api-url https://your-twenty-server.com --as production
yarn twenty remote:add --url https://your-twenty-server.com --as production
```
### Implementare
@@ -42,9 +42,9 @@ yarn twenty remote add --api-url https://your-twenty-server.com --as production
Construiește și încarcă aplicația ta pe server într-un singur pas:
```bash filename="Terminal"
yarn twenty deploy
yarn twenty app:publish --private
# To deploy to a specific remote:
# yarn twenty deploy --remote production
# yarn twenty app:publish --private --remote production
```
### Partajarea unei aplicații implementate
@@ -68,7 +68,7 @@ Când actualizezi o aplicație tarball deja implementată, serverul solicită ca
Pentru a lansa o actualizare:
1. Incrementează câmpul `version` din `package.json` (de ex. `1.2.3` → `1.2.4`, `1.3.0` sau `2.0.0`)
2. Rulează `yarn twenty deploy` (sau `yarn twenty deploy --remote production`)
2. Rulează `yarn twenty app:publish --private` (sau `yarn twenty app:publish --private --remote production`)
3. Spațiile de lucru care au aplicația instalată vor vedea actualizarea disponibilă în setările lor
<Note>
@@ -121,7 +121,7 @@ Rulează testele de integrare la fiecare push pe `main` și la fiecare pull requ
**Ce face:**
1. Preia codul sursă al aplicației.
2. Pornește o instanță de test Twenty izolată folosind acțiunea compozită `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (echivalentul din CI al `yarn twenty server start --test`).
2. Pornește o instanță de test Twenty izolată folosind acțiunea compozită `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (echivalentul din CI al `yarn twenty docker:start --test`).
3. Activează Corepack, configurează Node.js pe baza fișierului `.nvmrc` și instalează dependențele cu `yarn install --immutable`.
4. Rulează `yarn test`, transmitând `TWENTY_API_URL` și `TWENTY_API_KEY` din instanța pornită, astfel încât testele să poată comunica cu un server real.
@@ -139,7 +139,7 @@ Implementează aplicația pe un server Twenty configurat la fiecare push pe `mai
**Ce face:**
1. Preia head-ul PR-ului (pentru PR-urile etichetate) sau commitul împins.
2. Rulează `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — echivalentul din CI al `yarn twenty deploy`.
2. Rulează `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — echivalentul din CI al `yarn twenty app:publish --private`.
3. Rulează `twentyhq/twenty/.github/actions/install-twenty-app@main` astfel încât versiunea nou implementată să fie instalată în spațiul de lucru țintă.
**Configurare necesară:**
@@ -208,13 +208,13 @@ Capturile de ecran cu orice raport de aspect sunt afișate integral și nu sunt
### Publicare
```bash filename="Terminal"
yarn twenty publish
yarn twenty app:publish
```
Pentru a publica sub un dist-tag specific (de ex., `beta` sau `next`):
```bash filename="Terminal"
yarn twenty publish --tag beta
yarn twenty app:publish --tag beta
```
### Cum funcționează descoperirea în marketplace
@@ -224,9 +224,9 @@ Serverul Twenty sincronizează catalogul marketplace-ului din registrul npm **la
Poți declanșa sincronizarea imediat, în loc să aștepți:
```bash filename="Terminal"
yarn twenty server catalog-sync
yarn twenty dev:catalog-sync
# To target a specific remote:
# yarn twenty server catalog-sync --remote production
# yarn twenty dev:catalog-sync --remote production
```
Metadatele afișate în marketplace provin din configurația `defineApplication()` — câmpuri precum `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` și `termsUrl`.
@@ -259,12 +259,12 @@ jobs:
node-version: "24"
registry-url: https://registry.npmjs.org
- run: yarn install --immutable
- run: npx twenty build
- run: npx twenty dev:build
- run: npm publish --provenance --access public
working-directory: .twenty/output
```
Pentru alte sisteme CI (GitLab CI, CircleCI etc.), se aplică aceleași trei comenzi: `yarn install`, `yarn twenty build`, apoi `npm publish` din `.twenty/output`.
Pentru alte sisteme CI (GitLab CI, CircleCI etc.), se aplică aceleași trei comenzi: `yarn install`, `yarn twenty dev:build`, apoi `npm publish` din `.twenty/output`.
<Note>
**npm provenance** este opțională, dar recomandată. Publicarea cu `--provenance` adaugă un badge de încredere la listarea ta în npm, permițând utilizatorilor să verifice că pachetul a fost construit dintr-un commit specific într-un pipeline CI public. Vezi [documentația npm provenance](https://docs.npmjs.com/generating-provenance-statements) pentru instrucțiuni de configurare.
@@ -281,7 +281,7 @@ Mergi la pagina **Setări > Aplicații** din Twenty, unde pot fi parcurse și in
Poți instala aplicații și din linia de comandă:
```bash filename="Terminal"
yarn twenty install
yarn twenty app:install
```
<Note>
@@ -290,5 +290,5 @@ Serverul impune versionarea semver la instalare, reflectând regulile de la depl
* Instalarea aceleiași versiuni care este deja instalată în spațiul tău de lucru este respinsă cu o eroare `APP_ALREADY_INSTALLED`.
* Instalarea unei versiuni mai mici decât cea instalată în prezent este respinsă cu o eroare `CANNOT_DOWNGRADE_APPLICATION`.
Pentru a instala o versiune mai nouă, fă mai întâi deploy sau public-o, apoi rulează din nou `yarn twenty install`.
Pentru a instala o versiune mai nouă, fă mai întâi deploy sau public-o, apoi rulează din nou `yarn twenty app:install`.
</Note>
@@ -235,7 +235,7 @@ yarn test:watch
Puteți rula și verificarea tipurilor pe aplicație fără a rula testele:
```bash filename="Terminal"
yarn twenty typecheck
yarn twenty dev:typecheck
```
Aceasta rulează `tsc --noEmit` și raportează orice erori de tip.
@@ -45,7 +45,7 @@ export default definePostInstallLogicFunction({
Вы также можете вручную выполнить постустановочную функцию в любое время с помощью CLI:
```bash filename="Terminal"
yarn twenty exec --postInstall
yarn twenty dev:function:exec --postInstall
```
Основные моменты:
@@ -60,7 +60,7 @@ yarn twenty exec --postInstall
* Для каждого приложения допускается только одна послеустановочная функция. Сборка манифеста завершится ошибкой, если будет обнаружено более одной такой функции.
* Параметры функции `universalIdentifier`, `shouldRunOnVersionUpgrade` и `shouldRunSynchronously` автоматически добавляются в манифест приложения в поле `postInstallLogicFunction` во время сборки — вам не нужно указывать их в [`defineApplication()`](/l/ru/developers/extend/apps/config/application).
* Тайм-аут по умолчанию установлен на 300 секунд (5 минут), чтобы позволить выполнять более длительные задачи настройки, такие как инициализация данных.
* **Не выполняется в режиме разработки**: когда приложение зарегистрировано локально (через `yarn twenty dev`), сервер полностью пропускает процесс установки и синхронизирует файлы напрямую через наблюдатель CLI — поэтому post-install никогда не запускается в режиме разработки, независимо от `shouldRunSynchronously`. Используйте `yarn twenty exec --postInstall`, чтобы запустить это вручную для запущенного рабочего пространства.
* **Не выполняется в режиме разработки**: когда приложение зарегистрировано локально (через `yarn twenty dev`), сервер полностью пропускает процесс установки и синхронизирует файлы напрямую через наблюдатель CLI — поэтому post-install никогда не запускается в режиме разработки, независимо от `shouldRunSynchronously`. Используйте `yarn twenty dev:function:exec --postInstall`, чтобы запустить это вручную для запущенного рабочего пространства.
</Accordion>
<Accordion title="definePreInstallLogicFunction" description="Выполняется до применения миграции метаданных рабочего пространства">
@@ -87,7 +87,7 @@ export default definePreInstallLogicFunction({
Вы также можете вручную выполнить предустановочную функцию в любое время с помощью CLI:
```bash filename="Terminal"
yarn twenty exec --preInstall
yarn twenty dev:function:exec --preInstall
```
Основные моменты:
@@ -96,7 +96,7 @@ yarn twenty exec --preInstall
* **Когда запускается хук**: выполняется непосредственно перед миграцией метаданных рабочего пространства (`synchronizeFromManifest`). Перед выполнением сервер запускает чисто добавочную «урезанную синхронизацию», которая регистрирует в метаданных рабочего пространства pre-install функцию **новой** версии — ничего больше не затрагивается — а затем выполняет её. Поскольку эта синхронизация только добавляет, объекты, поля и данные предыдущей версии остаются нетронутыми к моменту запуска вашего обработчика: вы можете безопасно читать и сохранять состояние до миграции.
* **Модель выполнения**: pre-install выполняется **синхронно** и **блокирует установку**. Если обработчик генерирует исключение, установка прерывается до применения каких-либо изменений схемы — рабочее пространство остаётся на предыдущей версии в согласованном состоянии. Это сделано намеренно: pre-install — ваш последний шанс отказать в рискованном обновлении.
* Как и в случае с post-install, для каждого приложения допускается только одна предустановочная функция. Она автоматически добавляется в манифест приложения в поле `preInstallLogicFunction` во время сборки.
* **Не выполняется в режиме разработки**: как и post-install, процесс установки полностью пропускается для локально зарегистрированных приложений, поэтому pre-install никогда не запускается при `yarn twenty dev`. Используйте `yarn twenty exec --preInstall`, чтобы запустить это вручную.
* **Не выполняется в режиме разработки**: как и post-install, процесс установки полностью пропускается для локально зарегистрированных приложений, поэтому pre-install никогда не запускается при `yarn twenty dev`. Используйте `yarn twenty dev:function:exec --preInstall`, чтобы запустить это вручную.
</Accordion>
<Accordion title="Pre-install и post-install: когда что использовать" description="Выбор подходящего хука установки">
@@ -13,7 +13,7 @@ icon: folder-open
* **Доступными в логических функциях** — используйте URL ресурсов в письмах, ответах API или любой серверной логике.
* **Используются для метаданных маркетплейса** — поля `logoUrl` и `screenshots` в `defineApplication()` ссылаются на файлы из этой папки (например, `public/logo.png`). Они отображаются в маркетплейсе при публикации вашего приложения.
* **Автосинхронизация в режиме разработки** — когда вы добавляете, обновляете или удаляете файл в `public/`, он автоматически синхронизируется с сервером. Перезапуск не требуется.
* **Включены в сборки** — `yarn twenty build` упаковывает все публичные ресурсы в выходной дистрибутив.
* **Включены в сборки** — `yarn twenty dev:build` упаковывает все публичные ресурсы в выходной дистрибутив.
## Доступ к публичным ресурсам с помощью `getPublicAssetUrl`
@@ -80,7 +80,7 @@ export default defineObject({
* Каждому полю требуются `name`, `type`, `label` и собственный стабильный `universalIdentifier`.
* Массив `fields` необязателен — вы можете определять объекты без пользовательских полей.
* Встроенным полям, определённым здесь, **не** нужен `objectUniversalIdentifier` — он наследуется от родительского объекта. Используйте [`defineField()`](/l/ru/developers/extend/apps/data/extending-objects), чтобы добавить поля к объектам, которые вам не принадлежат.
* Вы можете сгенерировать новые объекты с помощью `yarn twenty add object`, который проведёт вас через выбор именования, полей и связей. См. [Architecture → Scaffolding entities](/l/ru/developers/extend/apps/getting-started/scaffolding).
* Вы можете сгенерировать заготовки новых объектов с помощью `yarn twenty dev:add object`, который проведёт вас через настройку названий, полей и связей. См. [Architecture → Scaffolding entities](/l/ru/developers/extend/apps/getting-started/scaffolding).
<Note>
**Базовые поля добавляются автоматически.** Когда вы определяете пользовательский объект, Twenty создаёт для вас стандартные поля, такие как `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` и `deletedAt`. Вам не нужно объявлять их в массиве `fields` — только ваши пользовательские поля. Вы можете переопределить базовое поле, объявив поле с тем же именем, но это редко бывает хорошей идеей.
@@ -65,7 +65,7 @@ your-app/
│ npx create-twenty-app → yarn twenty dev (live sync) │
├─────────────────────────────────────────────────────────┤
│ Build & Deploy │
│ yarn twenty build → yarn twenty deploy
│ yarn twenty dev:build → yarn twenty app:publish
├─────────────────────────────────────────────────────────┤
│ Install flow │
│ upload → [pre-install] → metadata migration → │
@@ -77,7 +77,7 @@ your-app/
```
* **`yarn twenty dev`** — следит за исходными файлами и синхронизирует изменения в реальном времени с подключённым сервером Twenty. Типизированный клиент API автоматически пересоздаётся при изменении схемы.
* **`yarn twenty build`** — компилирует TypeScript, упаковывает логические функции и фронтенд-компоненты с помощью esbuild и формирует манифест.
* **`yarn twenty dev:build`** — компилирует TypeScript, упаковывает логические функции и фронтенд-компоненты с помощью esbuild и формирует манифест.
* **Хуки до/после установки** — необязательные функции, которые выполняются во время установки. См. раздел [Install Hooks](/l/ru/developers/extend/apps/config/install-hooks) для подробностей.
## Следующие шаги
@@ -6,44 +6,44 @@ icon: server
## Управление локальным сервером
Используйте `yarn twenty server` для управления локальным контейнером Twenty:
Используйте `yarn twenty docker:*` для управления локальным контейнером Twenty:
| Команда | Что делает |
| -------------------------------------- | ---------------------------------------------------- |
| `yarn twenty server start` | Запустить сервер (при необходимости скачивает образ) |
| `yarn twenty server start --port 3030` | Запустить на пользовательском порту |
| `yarn twenty server stop` | Остановить сервер (данные сохраняются) |
| `yarn twenty server status` | Показать URL, версию и учётные данные для входа |
| `yarn twenty server logs` | Потоковый вывод журналов сервера |
| `yarn twenty server reset` | Стереть данные и начать заново |
| `yarn twenty server upgrade` | Скачать последний образ `twenty-app-dev` |
| `yarn twenty server upgrade 2.2.0` | Обновить до конкретной версии |
| `yarn twenty docker:start` | Запустить сервер (при необходимости скачивает образ) |
| `yarn twenty docker:start --port 3030` | Запустить на пользовательском порту |
| `yarn twenty docker:stop` | Остановить сервер (данные сохраняются) |
| `yarn twenty docker:status` | Показать URL, версию и учётные данные для входа |
| `yarn twenty docker:logs` | Потоковый вывод журналов сервера |
| `yarn twenty docker:reset` | Стереть данные и начать заново |
| `yarn twenty docker:upgrade` | Скачать последний образ `twenty-app-dev` |
| `yarn twenty docker:upgrade 2.2.0` | Обновить до конкретной версии |
Данные сохраняются между перезапусками в двух томах Docker (`twenty-app-dev-data` для PostgreSQL, `twenty-app-dev-storage` для файлов). Используйте `reset`, чтобы стереть всё.
## Обновление образа сервера
`yarn twenty server upgrade` скачивает последний образ, сравнивает дайджесты и пересоздаёт контейнер только если действительно что-то изменилось. Тома сохраняются — заменяется только контейнер. Если был скачан новый образ и контейнер работал, при обновлении автоматически запускается новый контейнер; затем выполните `yarn twenty server start`, чтобы дождаться его готовности.
`yarn twenty docker:upgrade` скачивает последний образ, сравнивает дайджесты и пересоздаёт контейнер только если действительно что-то изменилось. Тома сохраняются — заменяется только контейнер. Если был скачан новый образ и контейнер работал, при обновлении автоматически запускается новый контейнер; затем выполните `yarn twenty docker:start`, чтобы дождаться его готовности.
```bash filename="Terminal"
yarn twenty server upgrade # Latest
yarn twenty server upgrade 2.2.0 # Specific version
yarn twenty docker:upgrade # Latest
yarn twenty docker:upgrade 2.2.0 # Specific version
```
Проверьте запущенную версию с помощью `yarn twenty server status` (эта команда показывает `APP_VERSION`, встроенную в контейнер).
Проверьте запущенную версию с помощью `yarn twenty docker:status` (эта команда показывает `APP_VERSION`, встроенную в контейнер).
## Запуск параллельного тестового экземпляра
Передайте `--test` любой команде `server`, чтобы управлять вторым, полностью изолированным экземпляром — это полезно для запуска интеграционных тестов или экспериментов, не затрагивая ваши основные данные разработки.
Передайте `--test` любой команде `docker:*`, чтобы управлять вторым, полностью изолированным экземпляром — это полезно для запуска интеграционных тестов или экспериментов, не затрагивая ваши основные данные разработки:
| Команда | Что делает |
| ----------------------------------- | ------------------------------------------------------- |
| `yarn twenty server start --test` | Запустить тестовый экземпляр (по умолчанию — порт 2021) |
| `yarn twenty server stop --test` | Остановить его |
| `yarn twenty server status --test` | Показать его статус |
| `yarn twenty server logs --test` | Транслировать его журналы |
| `yarn twenty server reset --test` | Стереть его данные |
| `yarn twenty server upgrade --test` | Обновить его образ |
| `yarn twenty docker:start --test` | Запустить тестовый экземпляр (по умолчанию — порт 2021) |
| `yarn twenty docker:stop --test` | Остановить его |
| `yarn twenty docker:status --test` | Показать его статус |
| `yarn twenty docker:logs --test` | Транслировать его журналы |
| `yarn twenty docker:reset --test` | Стереть его данные |
| `yarn twenty docker:upgrade --test` | Обновить его образ |
Тестовый экземпляр запускается в собственном контейнере Docker (`twenty-app-dev-test`) с выделенными томами (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) и собственной конфигурацией, поэтому он может работать параллельно с вашим основным экземпляром без конфликтов. Совместите `--test` с `--port`, чтобы переопределить значение по умолчанию (2021).
@@ -65,7 +65,7 @@ yarn add twenty-sdk twenty-client-sdk
}
```
Теперь вы можете запускать `yarn twenty dev`, `yarn twenty server start` и остальные команды.
Теперь вы можете запускать `yarn twenty dev`, `yarn twenty docker:start` и остальные команды.
<Note>
Не устанавливайте `twenty-sdk` глобально — фиксируйте версию в каждом проекте, чтобы каждое приложение использовало свою собственную версию.
@@ -43,7 +43,7 @@ npx create-twenty-app@latest my-twenty-app
> **Хотите настроить локальный экземпляр Twenty?**
* **Да (рекомендуется)** — скачивает Docker-образ `twentycrm/twenty-app-dev` и запускает его на порту `2020`. Сначала убедитесь, что Docker запущен.
* **Нет** — выберите это, если у вас уже есть сервер Twenty, к которому вы хотите подключиться. Позже вы можете подключить его с помощью `yarn twenty remote add`.
* **Нет** — выберите это, если у вас уже есть сервер Twenty, к которому вы хотите подключиться. Позже вы можете подключить его с помощью `yarn twenty remote:add`.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/developers/extends/apps/start-instance.png" alt="Запустить локальный экземпляр?" />
@@ -73,7 +73,7 @@ npx create-twenty-app@latest my-twenty-app
**После этой фазы:** у вас запущен сервер Twenty на [http://localhost:2020](http://localhost:2020), а ваш CLI авторизован для синхронизации с ним.
<Note>
Если Docker не установлен или не запущен, генератор каркаса подскажет правильную команду запуска для вашей ОС. Когда Docker будет запущен, вы можете продолжить с `yarn twenty server start` — заново генерировать каркас не нужно.
Если Docker не установлен или не запущен, генератор каркаса подскажет правильную команду запуска для вашей ОС. Когда Docker будет запущен, вы можете продолжить с `yarn twenty docker:start` — заново генерировать каркас не нужно.
</Note>
---
@@ -1,13 +1,13 @@
---
title: Создание каркаса
description: Интерактивно генерируйте файлы сущностей с помощью yarn twenty add — объекты, поля, представления, логические функции и многое другое.
description: Интерактивно генерируйте файлы сущностей с помощью yarn twenty dev:add — объекты, поля, представления, логические функции и многое другое.
icon: wand-magic-sparkles
---
Вместо ручного создания файлов сущностей используйте интерактивный генератор:
```bash filename="Terminal"
yarn twenty add
yarn twenty dev:add
```
Он предлагает выбрать тип сущности и пошагово проводит через обязательные поля, а затем записывает готовый к использованию файл со стабильным `universalIdentifier` и корректным вызовом `defineEntity()`.
@@ -15,29 +15,29 @@ yarn twenty add
Вы также можете передать тип сущности напрямую, чтобы пропустить первый запрос:
```bash filename="Terminal"
yarn twenty add object
yarn twenty add logicFunction
yarn twenty add frontComponent
yarn twenty dev:add object
yarn twenty dev:add logicFunction
yarn twenty dev:add frontComponent
```
## Доступные типы сущностей
| Тип сущности | Команда | Сгенерированный файл |
| -------------------- | ------------------------------------ | ------------------------------------------------------- |
| Объект | `yarn twenty add object` | `src/objects/\<name>.ts` |
| Поле | `yarn twenty add field` | `src/fields/\<name>.ts` |
| Логическая функция | `yarn twenty add logicFunction` | `src/logic-functions/\<name>.ts` |
| Компонент фронтенда | `yarn twenty add frontComponent` | `src/front-components/\<name>.tsx` |
| Роль | `yarn twenty add role` | `src/roles/\<name>.ts` |
| Навык | `yarn twenty add skill` | `src/skills/\<name>.ts` |
| Агент | `yarn twenty add agent` | `src/agents/\<name>.ts` |
| Представление | `yarn twenty add view` | `src/views/\<name>.ts` |
| Пункт меню навигации | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\<name>.ts` |
| Макет страницы | `yarn twenty add pageLayout` | `src/page-layouts/\<name>.ts` |
| Тип сущности | Команда | Сгенерированный файл |
| -------------------- | ---------------------------------------- | ------------------------------------------------------- |
| Объект | `yarn twenty dev:add object` | `src/objects/\<name>.ts` |
| Поле | `yarn twenty dev:add field` | `src/fields/\<name>.ts` |
| Логическая функция | `yarn twenty dev:add logicFunction` | `src/logic-functions/\<name>.ts` |
| Компонент фронтенда | `yarn twenty dev:add frontComponent` | `src/front-components/\<name>.tsx` |
| Роль | `yarn twenty dev:add role` | `src/roles/\<name>.ts` |
| Навык | `yarn twenty dev:add skill` | `src/skills/\<name>.ts` |
| Агент | `yarn twenty dev:add agent` | `src/agents/\<name>.ts` |
| Представление | `yarn twenty dev:add view` | `src/views/\<name>.ts` |
| Пункт меню навигации | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\<name>.ts` |
| Макет страницы | `yarn twenty dev:add pageLayout` | `src/page-layouts/\<name>.ts` |
## Что генерирует скэффолдер
У каждого типа сущности есть свой шаблон. Например, `yarn twenty add object` запрашивает:
У каждого типа сущности есть свой шаблон. Например, `yarn twenty dev:add object` запрашивает:
1. **Имя (единственное число)** — например, `invoice`
2. **Имя (множественное число)** — например, `invoices`
@@ -54,5 +54,5 @@ yarn twenty add frontComponent
Используйте флаг `--path`, чтобы поместить сгенерированный файл в пользовательское расположение:
```bash filename="Terminal"
yarn twenty add logicFunction --path src/custom-folder
yarn twenty dev:add logicFunction --path src/custom-folder
```
@@ -4,7 +4,7 @@ description: Распространенные проблемы при перво
icon: wrench
---
* **Ошибки Docker** — убедитесь, что Docker Desktop (или демон) запущен перед выполнением `yarn twenty server start`. Сообщение об ошибке укажет правильную команду запуска для вашей ОС.
* **Ошибки Docker** — убедитесь, что Docker Desktop (или демон) запущен перед выполнением `yarn twenty docker:start`. Сообщение об ошибке укажет правильную команду запуска для вашей ОС.
* **Неподходящая версия Node** — нужна 24+. Проверьте с помощью `node -v`.
* **Отсутствует Yarn 4** — выполните `corepack enable`.
* **Зависимости повреждены** — `rm -rf node_modules && yarn install`.
@@ -61,17 +61,17 @@ export default defineLogicFunction({
Вы также можете вручную выполнить функцию с помощью CLI:
```bash filename="Terminal"
yarn twenty exec -n create-new-post-card -p '{"key": "value"}'
yarn twenty dev:function:exec -n create-new-post-card -p '{"key": "value"}'
```
```bash filename="Terminal"
yarn twenty exec -y e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
```
Вы можете просматривать логи с помощью:
```bash filename="Terminal"
yarn twenty logs
yarn twenty dev:function:logs
```
</Note>
@@ -340,7 +340,7 @@ export default defineLogicFunction({
<AccordionGroup>
<Accordion title="CoreApiClient" description="Запрос и изменение данных рабочего пространства (записи, объекты)">
`CoreApiClient` — основной клиент для запросов и изменений данных рабочего пространства. Он **генерируется из схемы вашего рабочего пространства** во время `yarn twenty dev` или `yarn twenty build`, поэтому полностью типизирован в соответствии с вашими объектами и полями.
`CoreApiClient` — основной клиент для запросов и изменений данных рабочего пространства. Он **генерируется из схемы вашего рабочего пространства** во время `yarn twenty dev` или `yarn twenty dev:build`, поэтому полностью типизирован в соответствии с вашими объектами и полями.
```ts
import { CoreApiClient } from 'twenty-client-sdk/core';
@@ -380,7 +380,7 @@ const { createCompany } = await client.mutation({
Клиент использует синтаксис selection-set: передайте `true`, чтобы включить поле, используйте `__args` для аргументов и вкладывайте объекты для отношений. Вы получаете полное автодополнение и проверку типов на основе схемы вашего рабочего пространства.
<Note>
**CoreApiClient генерируется на этапе dev/build.** Если вы используете его, не запустив сначала `yarn twenty dev` или `yarn twenty build`, он выбросит ошибку. Генерация происходит автоматически — CLI анализирует GraphQL-схему вашего рабочего пространства и создает типизированный клиент с помощью `@genql/cli`.
**CoreApiClient генерируется на этапе dev/build.** Если вы используете его, не запустив сначала `yarn twenty dev` или `yarn twenty dev:build`, он выбросит ошибку. Генерация происходит автоматически — CLI анализирует GraphQL-схему вашего рабочего пространства и создает типизированный клиент с помощью `@genql/cli`.
</Note>
#### Использование CoreSchema для аннотаций типов
@@ -4,54 +4,54 @@ description: Команды `yarn twenty` для выполнения функц
icon: terminal
---
Помимо `dev`, `build`, `add` и `typecheck`, CLI `yarn twenty` предоставляет команды для выполнения функций, просмотра логов и управления установками приложений.
Помимо `dev`, `dev:build`, `dev:add` и `dev:typecheck`, CLI `yarn twenty` предоставляет команды для выполнения функций, просмотра логов и управления установками приложений.
## Выполнение функций (`yarn twenty exec`)
## Выполнение функций (`yarn twenty dev:function:exec`)
Запустите логическую функцию вручную, не вызывая её через HTTP, cron или событие базы данных:
```bash filename="Terminal"
# Execute by function name
yarn twenty exec -n create-new-post-card
yarn twenty dev:function:exec -n create-new-post-card
# Execute by universalIdentifier
yarn twenty exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
# Pass a JSON payload
yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}'
yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}'
# Execute the post-install function
yarn twenty exec --postInstall
yarn twenty dev:function:exec --postInstall
```
## Просмотр логов функций (`yarn twenty logs`)
## Просмотр логов функций (`yarn twenty dev:function:logs`)
Потоковая передача журналов выполнения логических функций вашего приложения:
```bash filename="Terminal"
# Stream all function logs
yarn twenty logs
yarn twenty dev:function:logs
# Filter by function name
yarn twenty logs -n create-new-post-card
yarn twenty dev:function:logs -n create-new-post-card
# Filter by universalIdentifier
yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
yarn twenty dev:function:logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
```
<Note>
Это отличается от `yarn twenty server logs`, который показывает логи контейнера Docker. `yarn twenty logs` показывает журналы выполнения функций вашего приложения с сервера Twenty.
Это отличается от `yarn twenty docker:logs`, который показывает логи контейнера Docker. `yarn twenty dev:function:logs` показывает журналы выполнения функций вашего приложения с сервера Twenty.
</Note>
## Удаление приложения (`yarn twenty uninstall`)
## Удаление приложения (`yarn twenty app:uninstall`)
Удалите свое приложение из активного рабочего пространства:
```bash filename="Terminal"
yarn twenty uninstall
yarn twenty app:uninstall
# Skip the confirmation prompt
yarn twenty uninstall --yes
yarn twenty app:uninstall --yes
```
## Управление удалёнными серверами
@@ -60,19 +60,19 @@ yarn twenty uninstall --yes
```bash filename="Terminal"
# Add a new remote (opens a browser for OAuth login)
yarn twenty remote add
yarn twenty remote:add
# Connect to a local Twenty server (auto-detects port 2020 or 3000)
yarn twenty remote add --local
yarn twenty remote:add --local
# Add a remote non-interactively (useful for CI)
yarn twenty remote add --api-url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote
yarn twenty remote:add --url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote
# List all configured remotes
yarn twenty remote list
yarn twenty remote:list
# Switch the active remote
yarn twenty remote switch <name>
# Set the active remote
yarn twenty remote:use <name>
```
Ваши учётные данные хранятся в `~/.twenty/config.json`.
@@ -9,9 +9,9 @@ icon: rocket
```text
develop ─▶ test ─▶ build ─▶ deploy / publish
─────── ──── ───── ─────────────────
yarn yarn yarn yarn twenty deploy (tarball → one server)
yarn yarn yarn yarn twenty app:publish --private (tarball → one server)
twenty test twenty
dev build yarn twenty publish (npm → marketplace)
dev dev:build yarn twenty app:publish (npm → marketplace)
```
## В этом разделе
@@ -18,10 +18,10 @@ description: Распространяйте своё приложение Twenty
Выполните команду сборки, чтобы скомпилировать приложение и сгенерировать готовый к распространению `manifest.json`:
```bash filename="Terminal"
yarn twenty build
yarn twenty dev:build
```
Это компилирует исходные файлы TypeScript, транспилирует функции логики и фронтенд-компоненты и записывает всё в `.twenty/output/`. Добавьте `--tarball`, чтобы также создать пакет `.tgz` для ручного распространения или для команды deploy.
Это компилирует исходные файлы TypeScript, транспилирует функции логики и фронтенд-компоненты и записывает всё в `.twenty/output/`. Добавьте `--tarball`, чтобы также создать пакет `.tgz` для ручного распространения или для команды publish.
## Развертывание на сервер (tarball)
@@ -34,7 +34,7 @@ yarn twenty build
Добавьте remote:
```bash filename="Terminal"
yarn twenty remote add --api-url https://your-twenty-server.com --as production
yarn twenty remote:add --url https://your-twenty-server.com --as production
```
### Развертывание
@@ -42,9 +42,9 @@ yarn twenty remote add --api-url https://your-twenty-server.com --as production
Соберите и загрузите ваше приложение на сервер в одном шаге:
```bash filename="Terminal"
yarn twenty deploy
yarn twenty app:publish --private
# To deploy to a specific remote:
# yarn twenty deploy --remote production
# yarn twenty app:publish --private --remote production
```
### Общий доступ к развернутому приложению
@@ -68,7 +68,7 @@ yarn twenty deploy
Чтобы выпустить обновление:
1. Увеличьте значение поля `version` в вашем `package.json` (например: `1.2.3` → `1.2.4`, `1.3.0` или `2.0.0`).
2. Выполните `yarn twenty deploy` (или `yarn twenty deploy --remote production`)
2. Выполните `yarn twenty app:publish --private` (или `yarn twenty app:publish --private --remote production`)
3. Рабочие пространства, в которых установлено приложение, увидят доступное обновление в своих настройках
<Note>
@@ -121,7 +121,7 @@ yarn twenty deploy
**Что делает:**
1. Извлекает исходный код вашего приложения.
2. Запускает изолированный тестовый экземпляр Twenty с помощью составного действия `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (эквивалент для CI `yarn twenty server start --test`).
2. Запускает изолированный тестовый экземпляр Twenty с помощью составного действия `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (эквивалент для CI `yarn twenty docker:start --test`).
3. Включает Corepack, настраивает Node.js на основе вашего `.nvmrc` и устанавливает зависимости с помощью `yarn install --immutable`.
4. Запускает `yarn test`, передавая `TWENTY_API_URL` и `TWENTY_API_KEY` из запущенного экземпляра, чтобы ваши тесты могли взаимодействовать с реальным сервером.
@@ -139,7 +139,7 @@ yarn twenty deploy
**Что делает:**
1. Извлекает head-коммит PR (для PR с меткой) или запушенный коммит.
2. Запускает `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — эквивалент для CI `yarn twenty deploy`.
2. Запускает `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — эквивалент для CI `yarn twenty app:publish --private`.
3. Запускает `twentyhq/twenty/.github/actions/install-twenty-app@main`, чтобы новая развернутая версия была установлена в целевое рабочее пространство.
**Обязательная конфигурация:**
@@ -208,13 +208,13 @@ export default defineApplication({
### Публикация
```bash filename="Terminal"
yarn twenty publish
yarn twenty app:publish
```
Чтобы опубликовать с определённым dist-tag (например, `beta` или `next`):
```bash filename="Terminal"
yarn twenty publish --tag beta
yarn twenty app:publish --tag beta
```
### Как работает обнаружение приложений в маркетплейсе
@@ -224,9 +224,9 @@ yarn twenty publish --tag beta
Вы можете запустить синхронизацию немедленно, вместо ожидания:
```bash filename="Terminal"
yarn twenty server catalog-sync
yarn twenty dev:catalog-sync
# To target a specific remote:
# yarn twenty server catalog-sync --remote production
# yarn twenty dev:catalog-sync --remote production
```
Метаданные, отображаемые в маркетплейсе, берутся из конфигурации `defineApplication()` — из таких полей, как `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` и `termsUrl`.
@@ -259,12 +259,12 @@ jobs:
node-version: "24"
registry-url: https://registry.npmjs.org
- run: yarn install --immutable
- run: npx twenty build
- run: npx twenty dev:build
- run: npm publish --provenance --access public
working-directory: .twenty/output
```
Для других CI-систем (GitLab CI, CircleCI и др.) применимы те же три команды: `yarn install`, `yarn twenty build`, затем `npm publish` из `.twenty/output`.
Для других CI-систем (GitLab CI, CircleCI и др.) применимы те же три команды: `yarn install`, `yarn twenty dev:build`, затем `npm publish` из `.twenty/output`.
<Note>
**npm provenance** — опционально, но рекомендуется. Публикация с флагом `--provenance` добавляет к вашему пакету в npm значок доверия, позволяя пользователям проверить, что пакет был собран из конкретного коммита в общедоступном конвейере CI. См. инструкции по настройке в [документации по npm provenance](https://docs.npmjs.com/generating-provenance-statements).
@@ -281,7 +281,7 @@ jobs:
Вы также можете устанавливать приложения из командной строки:
```bash filename="Terminal"
yarn twenty install
yarn twenty app:install
```
<Note>
@@ -290,5 +290,5 @@ yarn twenty install
* Установка той же версии, которая уже установлена в вашем рабочем пространстве, отклоняется с ошибкой `APP_ALREADY_INSTALLED`.
* Установка версии ниже текущей отклоняется с ошибкой `CANNOT_DOWNGRADE_APPLICATION`.
Чтобы установить более новую версию, сначала разверните или опубликуйте её, затем снова выполните `yarn twenty install`.
Чтобы установить более новую версию, сначала разверните или опубликуйте её, затем снова выполните `yarn twenty app:install`.
</Note>
@@ -235,7 +235,7 @@ yarn test:watch
Вы также можете запустить проверку типов для своего приложения без запуска тестов:
```bash filename="Terminal"
yarn twenty typecheck
yarn twenty dev:typecheck
```
Это запускает `tsc --noEmit` и сообщает о любых ошибках типов.
@@ -45,7 +45,7 @@ export default definePostInstallLogicFunction({
Ayrıca kurulum sonrası işlevi istediğiniz zaman CLI kullanarak manuel olarak çalıştırabilirsiniz:
```bash filename="Terminal"
yarn twenty exec --postInstall
yarn twenty dev:function:exec --postInstall
```
Önemli noktalar:
@@ -60,7 +60,7 @@ yarn twenty exec --postInstall
* Uygulama başına yalnızca bir kurulum sonrası işlevine izin verilir. Birden fazla tespit edilirse manifest oluşturma hataya düşer.
* İşlevin `universalIdentifier`, `shouldRunOnVersionUpgrade` ve `shouldRunSynchronously` değerleri, derleme sırasında uygulama manifestine `postInstallLogicFunction` alanı altında otomatik olarak eklenir — bunlara [`defineApplication()`](/l/tr/developers/extend/apps/config/application) içinde atıfta bulunmanıza gerek yoktur.
* Varsayılan zaman aşımı, veri tohumlama gibi daha uzun kurulum görevlerine izin vermek için 300 saniye (5 dakika) olarak ayarlanmıştır.
* **Geliştirme modunda çalıştırılmaz**: bir uygulama yerel olarak kaydedildiğinde (`yarn twenty dev` aracılığıyla), sunucu kurulum akışını tamamen atlar ve dosyaları doğrudan CLI watcher üzerinden eşitler — bu nedenle, `shouldRunSynchronously` ne olursa olsun, kurulum sonrası geliştirme modunda hiç çalışmaz. Çalışan bir çalışma alanında bunu elle tetiklemek için `yarn twenty exec --postInstall` kullanın.
* **Geliştirme modunda çalıştırılmaz**: bir uygulama yerel olarak kaydedildiğinde (`yarn twenty dev` aracılığıyla), sunucu kurulum akışını tamamen atlar ve dosyaları doğrudan CLI watcher üzerinden eşitler — bu nedenle, `shouldRunSynchronously` ne olursa olsun, kurulum sonrası geliştirme modunda hiç çalışmaz. Çalışan bir çalışma alanında bunu elle tetiklemek için `yarn twenty dev:function:exec --postInstall` kullanın.
</Accordion>
<Accordion title="definePreInstallLogicFunction" description="Çalışma alanı üstveri (metadata) geçişi uygulanmadan önce çalışır">
@@ -87,7 +87,7 @@ export default definePreInstallLogicFunction({
Ayrıca kurulum öncesi işlevi istediğiniz zaman CLI kullanarak manuel olarak çalıştırabilirsiniz:
```bash filename="Terminal"
yarn twenty exec --preInstall
yarn twenty dev:function:exec --preInstall
```
Önemli noktalar:
@@ -96,7 +96,7 @@ yarn twenty exec --preInstall
* **Kanca ne zaman çalışır**: çalışma alanı üstveri (metadata) geçişinden hemen önce konumlandırılır (`synchronizeFromManifest`). Çalıştırmadan önce, sunucu yalnızca ekleyici bir "indirgenmiş eşitleme" yürütür; bu, çalışma alanı üstverisinde **yeni** sürümün kurulum öncesi işlevini kaydeder — başka hiçbir şeye dokunulmaz — ve ardından bunu yürütür. Bu eşitleme yalnızca ekleyici olduğundan, işleyiciniz çalıştığında önceki sürümün nesneleri, alanları ve verileri hâlâ sağlamdır: geçiş öncesi durumu güvenle okuyabilir ve yedekleyebilirsiniz.
* **Yürütme modeli**: kurulum öncesi **senkron** olarak yürütülür ve **kurulumu bloklar**. İşleyici bir hata fırlatırsa, herhangi bir şema değişikliği uygulanmadan önce kurulum iptal edilir — çalışma alanı, tutarlı bir durumda önceki sürümde kalır. Bu kasıtlıdır: kurulum öncesi, riskli bir yükseltmeyi reddetmek için son şansınızdır.
* Kurulum sonrası ile aynı şekilde, uygulama başına yalnızca bir kurulum öncesi işlevine izin verilir. Derleme sırasında uygulama manifestine `preInstallLogicFunction` altında otomatik olarak eklenir.
* **Geliştirme modunda çalıştırılmaz**: kurulum sonrasında olduğu gibi — yerel olarak kaydedilen uygulamalarda kurulum akışı tamamen atlanır, bu nedenle `yarn twenty dev` altında kurulum öncesi hiç çalışmaz. Bunu elle tetiklemek için `yarn twenty exec --preInstall` kullanın.
* **Geliştirme modunda çalıştırılmaz**: kurulum sonrasında olduğu gibi — yerel olarak kaydedilen uygulamalarda kurulum akışı tamamen atlanır, bu nedenle `yarn twenty dev` altında kurulum öncesi hiç çalışmaz. Bunu elle tetiklemek için `yarn twenty dev:function:exec --preInstall` kullanın.
</Accordion>
<Accordion title="Kurulum öncesi vs kurulum sonrası: hangisini ne zaman kullanmalı" description="Doğru kurulum kancasını seçme">
@@ -13,7 +13,7 @@ Uygulamanızın kökündeki `public/` klasörü, statik dosyaları barındırır
* **Mantık işlevlerinde kullanılabilir** — e-postalarda, API yanıtlarında veya herhangi bir sunucu tarafı mantıkta varlık URL'lerine referans verin.
* **Pazar yeri üst verileri için kullanılır** — `defineApplication()` içindeki `logoUrl` ve `screenshots` alanları bu klasördeki dosyalara referans verir (örn. `public/logo.png`). Bunlar, uygulamanız yayımlandığında pazar yerinde görüntülenir.
* **Geliştirme modunda otomatik senkronize edilir** — `public/` içinde bir dosya eklediğinizde, güncellediğinizde veya sildiğinizde otomatik olarak sunucuya senkronize edilir. Yeniden başlatma gerekmez.
* **Derlemelere dahil edilir** — `yarn twenty build`, tüm genel varlıkları dağıtım çıktısına paketler.
* **Derlemelere dahil edilir** — `yarn twenty dev:build`, tüm genel varlıkları dağıtım çıktısına paketler.
## `getPublicAssetUrl` ile genel varlıklara erişme
@@ -80,7 +80,7 @@ export default defineObject({
* Her alan bir `name`, `type`, `label` ve kendi kararlı `universalIdentifier` değerini gerektirir.
* `fields` dizisi isteğe bağlıdır — özel alanlar olmadan da nesneler tanımlayabilirsiniz.
* Burada tanımlanan satır içi alanların bir `objectUniversalIdentifier`'a **ihtiyacı yoktur** — üst nesneden devralınır. Sahibi olmadığınız nesnelere alan eklemek için [`defineField()`](/l/tr/developers/extend/apps/data/extending-objects) kullanın.
* `yarn twenty add object` kullanarak, adlandırma, alanlar ve ilişkiler konusunda sizi yönlendirerek yeni nesneler oluşturabilirsiniz. [Mimari → Varlık iskeleti oluşturma](/l/tr/developers/extend/apps/getting-started/scaffolding) bölümüne bakın.
* `yarn twenty dev:add object` kullanarak, adlandırma, alanlar ve ilişkiler konusunda sizi yönlendirerek yeni nesneler oluşturabilirsiniz. [Mimari → Varlık iskeleti oluşturma](/l/tr/developers/extend/apps/getting-started/scaffolding) bölümüne bakın.
<Note>
**Temel alanlar otomatik olarak eklenir.** Özel bir nesne tanımladığınızda Twenty, sizin için `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` ve `deletedAt` gibi standart alanlar oluşturur. Bunları `fields` dizinizde bildirmenize gerek yok — yalnızca özel alanlarınızı ekleyin. Aynı ada sahip bir alan bildirerek varsayılan bir alanı geçersiz kılabilirsiniz, ancak bu nadiren iyi bir fikirdir.
@@ -65,7 +65,7 @@ your-app/
│ npx create-twenty-app → yarn twenty dev (live sync) │
├─────────────────────────────────────────────────────────┤
│ Build & Deploy │
│ yarn twenty build → yarn twenty deploy
│ yarn twenty dev:build → yarn twenty app:publish
├─────────────────────────────────────────────────────────┤
│ Install flow │
│ upload → [pre-install] → metadata migration → │
@@ -77,7 +77,7 @@ your-app/
```
* **`yarn twenty dev`** — kaynak dosyalarınızı izler ve bağlı bir Twenty sunucusuna değişiklikleri canlı olarak senkronize eder. Şema değiştiğinde tipli API istemcisi otomatik olarak yeniden oluşturulur.
* **`yarn twenty build`** — TypeScript'i derler, mantık işlevlerini ve ön uç bileşenlerini esbuild ile paketler ve bir manifest üretir.
* **`yarn twenty dev:build`** — TypeScript'i derler, mantık işlevlerini ve ön uç bileşenlerini esbuild ile paketler ve bir manifest üretir.
* **Kurulum öncesi/sonrası kancaları** — kurulum sırasında çalışan isteğe bağlı işlevler. Ayrıntılar için [Kurulum Kancaları](/l/tr/developers/extend/apps/config/install-hooks) bölümüne bakın.
## Sonraki adımlar
@@ -6,44 +6,44 @@ icon: server
## Yerel sunucuyu yönetme
Yerel Twenty konteynerini kontrol etmek için `yarn twenty server` kullanın:
Yerel Twenty konteynerini kontrol etmek için `yarn twenty docker:*` kullanın:
| Komut | Ne yapar |
| -------------------------------------- | --------------------------------------------------------- |
| `yarn twenty server start` | Sunucuyu başlatır (gerekirse imajı çeker) |
| `yarn twenty server start --port 3030` | Özel bir portta başlatır |
| `yarn twenty server stop` | Sunucuyu durdurur (verileri korur) |
| `yarn twenty server status` | URL'yi, sürümü ve oturum açma kimlik bilgilerini gösterir |
| `yarn twenty server logs` | Sunucu günlüklerini akış olarak gösterir |
| `yarn twenty server reset` | Verileri siler ve sıfırdan başlatır |
| `yarn twenty server upgrade` | En güncel `twenty-app-dev` imajını çeker |
| `yarn twenty server upgrade 2.2.0` | Belirli bir sürüme yükseltir |
| `yarn twenty docker:start` | Sunucuyu başlatır (gerekirse imajı çeker) |
| `yarn twenty docker:start --port 3030` | Özel bir portta başlatır |
| `yarn twenty docker:stop` | Sunucuyu durdurur (verileri korur) |
| `yarn twenty docker:status` | URL'yi, sürümü ve oturum açma kimlik bilgilerini gösterir |
| `yarn twenty docker:logs` | Sunucu günlüklerini akış olarak gösterir |
| `yarn twenty docker:reset` | Verileri siler ve sıfırdan başlatır |
| `yarn twenty docker:upgrade` | En güncel `twenty-app-dev` imajını çeker |
| `yarn twenty docker:upgrade 2.2.0` | Belirli bir sürüme yükseltir |
Veriler, yeniden başlatmalar arasında iki Docker biriminde kalıcıdır (PostgreSQL için `twenty-app-dev-data`, dosyalar için `twenty-app-dev-storage`). Her şeyi silmek için `reset` kullanın.
## Sunucu imajını yükseltme
`yarn twenty server upgrade`, en güncel imajı çeker, özetleri karşılaştırır ve yalnızca gerçekten bir şey değiştiyse konteyneri yeniden oluşturur. Birimler korunur — yalnızca konteyner değiştirilir. Yeni bir imaj çekildiyse ve konteyner çalışıyorsa, yükseltme otomatik olarak yeni bir konteyner başlatır; sağlıklı hale gelmesini beklemek için ardından `yarn twenty server start` çalıştırın.
`yarn twenty docker:upgrade`, en güncel imajı çeker, özetleri karşılaştırır ve yalnızca gerçekten bir şey değiştiyse konteyneri yeniden oluşturur. Birimler korunur — yalnızca konteyner değiştirilir. Yeni bir imaj çekildiyse ve konteyner çalışıyorsa, yükseltme otomatik olarak yeni bir konteyner başlatır; sağlıklı hale gelmesini beklemek için ardından `yarn twenty docker:start` çalıştırın.
```bash filename="Terminal"
yarn twenty server upgrade # Latest
yarn twenty server upgrade 2.2.0 # Specific version
yarn twenty docker:upgrade # Latest
yarn twenty docker:upgrade 2.2.0 # Specific version
```
`yarn twenty server status` ile çalışan sürümü doğrulayabilirsiniz (konteynere gömülü `APP_VERSION` değerini gösterir).
Çalışan sürümü `yarn twenty docker:status` ile doğrulayabilirsiniz (konteynere gömülü `APP_VERSION` değerini gösterir).
## Paralel bir test örneği çalıştırma
`server` komutlarının herhangi birine `--test` parametresini vererek ikinci, tamamen yalıtılmış bir örneği yönetin — entegrasyon testlerini çalıştırmak veya ana geliştirme verilerinize dokunmadan denemeler yapmak için kullanışlıdır:
Ana geliştirme verilerinize dokunmadan entegrasyon testlerini veya denemeleri yürütmek için, herhangi bir `docker:*` komutuna `--test` parametresini vererek ikinci, tamamen yalıtılmış bir örneği yönetebilirsiniz:
| Komut | Ne yapar |
| ----------------------------------- | ------------------------------------------------------------- |
| `yarn twenty server start --test` | Test örneğini başlatır (varsayılan bağlantı noktası 2021'dir) |
| `yarn twenty server stop --test` | Durdurur |
| `yarn twenty server status --test` | Durumunu gösterir |
| `yarn twenty server logs --test` | Günlüklerini akış olarak gösterir |
| `yarn twenty server reset --test` | Verilerini siler |
| `yarn twenty server upgrade --test` | İmajını yükseltir |
| `yarn twenty docker:start --test` | Test örneğini başlatır (varsayılan bağlantı noktası 2021'dir) |
| `yarn twenty docker:stop --test` | Durdurur |
| `yarn twenty docker:status --test` | Durumunu gösterir |
| `yarn twenty docker:logs --test` | Günlüklerini akış olarak gösterir |
| `yarn twenty docker:reset --test` | Verilerini siler |
| `yarn twenty docker:upgrade --test` | İmajını yükseltir |
Test örneği, kendine ait bir Docker konteynerinde (`twenty-app-dev-test`), ayrılmış birimlerle (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) ve yapılandırmayla çalışır; böylece ana örneğinizle çakışma olmadan paralel olarak çalışabilir. Varsayılan 2021'i geçersiz kılmak için `--test` ile `--port`'u birlikte kullanın.
@@ -65,7 +65,7 @@ yarn add twenty-sdk twenty-client-sdk
}
```
Artık `yarn twenty dev`, `yarn twenty server start` ve diğerlerini çalıştırabilirsiniz.
Artık `yarn twenty dev`, `yarn twenty docker:start` ve diğerlerini çalıştırabilirsiniz.
<Note>
`twenty-sdk`'yi global olarak kurmayın — her uygulama kendi sürümünü kullansın diye proje bazında sabitleyin.
@@ -43,7 +43,7 @@ Uygulamanızın eşitleme yapacağı bir Twenty sunucusuna ihtiyacı vardır. Su
> **Yerel bir Twenty örneği kurmak ister misiniz?**
* **Evet (önerilir)** — `twentycrm/twenty-app-dev` Docker imajını çeker ve `2020` portunda başlatır. Önce Docker'ın çalıştığından emin olun.
* **Hayır** — Zaten bağlanmak istediğiniz bir Twenty sunucunuz varsa bunu seçin. Bunu daha sonra `yarn twenty remote add` ile bağlayabilirsiniz.
* **Hayır** — Zaten bağlanmak istediğiniz bir Twenty sunucunuz varsa bunu seçin. Bunu daha sonra `yarn twenty remote:add` ile bağlayabilirsiniz.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/developers/extends/apps/start-instance.png" alt="Yerel örnek başlatılsın mı?" />
@@ -73,7 +73,7 @@ Terminaliniz her şeyin kurulduğunu onaylayacaktır.
**Bu aşamadan sonra:** [http://localhost:2020](http://localhost:2020) adresinde çalışan bir Twenty sunucunuz ve ona eşitleme yapmak için yetkilendirilmiş bir CLI'ınız olur.
<Note>
Docker yüklü değilse veya çalışmıyorsa, iskelet oluşturucu işletim sisteminiz için doğru başlatma komutunu söyleyecektir. Docker çalışır duruma geldiğinde, `yarn twenty server start` ile devam edebilirsiniz — yeniden iskelet oluşturmaya gerek yok.
Docker yüklü değilse veya çalışmıyorsa, iskelet oluşturucu işletim sisteminiz için doğru başlatma komutunu söyleyecektir. Docker çalışır duruma geldiğinde, `yarn twenty docker:start` ile devam edebilirsiniz — yeniden iskelet oluşturmaya gerek yok.
</Note>
---

Some files were not shown because too many files have changed in this diff Show More