1428 lines
68 KiB
Plaintext
1428 lines
68 KiB
Plaintext
---
|
||
title: Aplikace Twenty
|
||
description: Vytvářejte a spravujte přizpůsobení Twenty jako kód.
|
||
---
|
||
|
||
<Warning>
|
||
Aplikace jsou aktuálně v alfa testování. Tato funkce je funkční, ale stále se vyvíjí.
|
||
</Warning>
|
||
|
||
## Co jsou aplikace?
|
||
|
||
Aplikace vám umožňují vytvářet a spravovat přizpůsobení Twenty **jako kód**. Místo konfigurace všeho přes uživatelské rozhraní definujete v kódu svůj datový model a logické funkce — což zrychluje vývoj, údržbu i nasazování do více pracovních prostorů.
|
||
|
||
**Co můžete dělat už dnes:**
|
||
|
||
* Definujte vlastní objekty a pole jako kód (spravovaný datový model)
|
||
* Vytvářejte logické funkce s vlastními spouštěči
|
||
* Definujte dovednosti a agenty AI!
|
||
* Nasazujte stejnou aplikaci do více pracovních prostorů
|
||
|
||
## Předpoklady
|
||
|
||
* Node.js 24+ a Yarn 4
|
||
* Docker (pro místní vývojový server Twenty)
|
||
|
||
## Začínáme
|
||
|
||
Vytvořte novou aplikaci pomocí oficiálního generátoru kostry. Může vám automaticky spustit místní instanci Twenty:
|
||
|
||
```bash filename="Terminal"
|
||
# Vygenerujte kostru nové aplikace — CLI nabídne spuštění místního serveru Twenty
|
||
npx create-twenty-app@latest my-twenty-app
|
||
cd my-twenty-app
|
||
|
||
# Spusťte vývojový režim: automaticky synchronizuje místní změny s vaším pracovním prostorem
|
||
yarn twenty dev
|
||
```
|
||
|
||
### Správa místního serveru
|
||
|
||
SDK obsahuje příkazy ke správě místního vývojového serveru Twenty (all-in-one obraz Dockeru s PostgreSQL, Redisem, serverem a workerem):
|
||
|
||
```bash filename="Terminal"
|
||
# Spusťte místní server (v případě potřeby stáhne obraz)
|
||
yarn twenty server start
|
||
|
||
# Zkontrolujte stav serveru
|
||
yarn twenty server status
|
||
|
||
# Streamujte logy serveru
|
||
yarn twenty server logs
|
||
|
||
# Zastavte server
|
||
yarn twenty server stop
|
||
|
||
# Resetujte všechna data a začněte znovu
|
||
yarn twenty server reset
|
||
```
|
||
|
||
Lokální server je předem naplněn pracovním prostorem a uživatelem (`tim@apple.dev` / `tim@apple.dev`), takže můžete začít vyvíjet okamžitě bez jakéhokoli ručního nastavení.
|
||
|
||
### Ověření
|
||
|
||
Připojte svou aplikaci k lokálnímu serveru pomocí OAuth:
|
||
|
||
```bash filename="Terminal"
|
||
# Ověřte se pomocí OAuth (otevře prohlížeč)
|
||
yarn twenty remote add --local
|
||
```
|
||
|
||
Nástroj pro generování kostry podporuje dva režimy pro řízení toho, které ukázkové soubory jsou zahrnuty:
|
||
|
||
```bash filename="Terminal"
|
||
# Výchozí (úplný): všechny příklady (objekt, pole, logická funkce, front-endová komponenta, zobrazení, položka navigační nabídky, dovednost, agent)
|
||
npx create-twenty-app@latest my-app
|
||
|
||
# Minimální: pouze základní soubory (application-config.ts a default-role.ts)
|
||
npx create-twenty-app@latest my-app --minimal
|
||
```
|
||
|
||
### Jak používat lokální instanci Twenty
|
||
|
||
Pokud již lokálně provozujete instanci Twenty, můžete se k ní připojit místo použití Dockeru. Zadejte port, na kterém váš lokální server naslouchá (výchozí: `3000`):
|
||
|
||
```bash filename="Terminal"
|
||
# Během vytváření kostry
|
||
npx create-twenty-app@latest my-app --port 3000
|
||
|
||
# Nebo po vytvoření kostry
|
||
yarn twenty remote add --local --port 3000
|
||
```
|
||
|
||
Odtud můžete:
|
||
|
||
```bash filename="Terminal"
|
||
# Přidejte do vaší aplikace novou entitu (s průvodcem)
|
||
yarn twenty entity:add
|
||
|
||
# Sledujte logy funkcí vaší aplikace
|
||
yarn twenty function:logs
|
||
|
||
# Spusťte funkci podle názvu
|
||
yarn twenty function:execute -n my-function -p '{"name": "test"}'
|
||
|
||
# Spusťte předinstalační funkci
|
||
yarn twenty function:execute --preInstall
|
||
|
||
# Spusťte postinstalační funkci
|
||
yarn twenty function:execute --postInstall
|
||
|
||
# Sestavte aplikaci pro distribuci
|
||
yarn twenty build
|
||
|
||
# Publikujte aplikaci na npm nebo na server Twenty
|
||
yarn twenty publish
|
||
|
||
# Odinstalujte aplikaci z aktuálního pracovního prostoru
|
||
yarn twenty uninstall
|
||
|
||
# Zobrazte nápovědu k příkazům
|
||
yarn twenty help},{
|
||
```
|
||
|
||
Viz také: referenční stránky CLI pro [create-twenty-app](https://www.npmjs.com/package/create-twenty-app) a [twenty-sdk CLI](https://www.npmjs.com/package/twenty-sdk).
|
||
|
||
## Struktura projektu (vytvořená scaffolderem)
|
||
|
||
Když spustíte `npx create-twenty-app@latest my-twenty-app`, scaffolder:
|
||
|
||
* Zkopíruje minimální základní aplikaci do `my-twenty-app/`
|
||
* Přidá lokální závislost `twenty-sdk` a konfiguraci pro Yarn 4
|
||
* Vytvoří konfigurační soubory a skripty napojené na `twenty` CLI
|
||
* Vygeneruje základní soubory (konfigurace aplikace, výchozí role funkcí, předinstalační a postinstalační funkce) a k nim ukázkové soubory podle zvoleného režimu generování kostry
|
||
|
||
Čerstvě vygenerovaná aplikace s výchozím režimem `--exhaustive` vypadá takto:
|
||
|
||
```text filename="my-twenty-app/"
|
||
my-twenty-app/
|
||
package.json
|
||
yarn.lock
|
||
.gitignore
|
||
.nvmrc
|
||
.yarnrc.yml
|
||
.yarn/
|
||
install-state.gz
|
||
.oxlintrc.json
|
||
tsconfig.json
|
||
README.md
|
||
public/ # Složka s veřejnými prostředky (obrázky, písma apod.)
|
||
src/
|
||
├── application-config.ts # Povinné – hlavní konfigurace aplikace
|
||
├── roles/
|
||
│ └── default-role.ts # Výchozí role pro logické funkce
|
||
├── objects/
|
||
│ └── example-object.ts # Ukázková definice vlastního objektu
|
||
├── fields/
|
||
│ └── example-field.ts # Ukázková samostatná definice pole
|
||
├── logic-functions/
|
||
│ ├── hello-world.ts # Ukázková logická funkce
|
||
│ ├── pre-install.ts # Předinstalační logická funkce
|
||
│ └── post-install.ts # Postinstalační logická funkce
|
||
├── front-components/
|
||
│ └── hello-world.tsx # Ukázková front-endová komponenta
|
||
├── views/
|
||
│ └── example-view.ts # Ukázková definice uloženého zobrazení
|
||
├── navigation-menu-items/
|
||
│ └── example-navigation-menu-item.ts # Ukázkový odkaz postranní navigace
|
||
├── skills/
|
||
│ └── example-skill.ts # Ukázková definice dovednosti agenta AI
|
||
└── agents/
|
||
└── example-agent.ts # Ukázková definice agenta AI
|
||
```
|
||
|
||
S volbou `--minimal` se vytvoří pouze základní soubory (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts` a `logic-functions/post-install.ts`).
|
||
|
||
V kostce:
|
||
|
||
* **package.json**: Deklaruje název aplikace, verzi, engines (Node 24+, Yarn 4) a přidává `twenty-sdk` plus skript `twenty`, který deleguje na lokální `twenty` CLI. Spusťte `yarn twenty help` pro výpis všech dostupných příkazů.
|
||
* **.gitignore**: Ignoruje běžné artefakty jako `node_modules`, `.yarn`, `generated/` (typovaný klient), `dist/`, `build/`, složky s coverage, logy a soubory `.env*`.
|
||
* **yarn.lock**, **.yarnrc.yml**, **.yarn/**: Zamykají a konfigurují nástrojový řetězec Yarn 4 používaný projektem.
|
||
* **.nvmrc**: Fixuje verzi Node.js požadovanou projektem.
|
||
* **.oxlintrc.json** and **tsconfig.json**: Provide linting and TypeScript configuration for your app's TypeScript sources.
|
||
* **README.md**: Krátké README v kořeni aplikace se základními pokyny.
|
||
* **public/**: Složka pro ukládání veřejných prostředků (obrázky, písma, statické soubory), které bude vaše aplikace poskytovat. Soubory umístěné zde se během synchronizace nahrají a jsou za běhu dostupné.
|
||
* **src/**: Hlavní místo, kde definujete svou aplikaci jako kód
|
||
|
||
### Detekce entit
|
||
|
||
SDK detekuje entity analýzou vašich souborů TypeScript a hledá volání **`export default define<Entity>({...})`**. Každý typ entity má odpovídající pomocnou funkci exportovanou z `twenty-sdk`:
|
||
|
||
| Pomocná funkce | Typ entity |
|
||
| ---------------------------------- | --------------------------------------------------------- |
|
||
| `defineObject()` | Definice vlastních objektů |
|
||
| `defineLogicFunction()` | Definice logických funkcí |
|
||
| `definePreInstallLogicFunction()` | Předinstalační logická funkce (spouští se před instalací) |
|
||
| `definePostInstallLogicFunction()` | Postinstalační logická funkce (spouští se po instalaci) |
|
||
| `defineFrontComponent()` | Definice frontendových komponent |
|
||
| `defineRole()` | Definice rolí |
|
||
| `defineField()` | Rozšíření polí u existujících objektů |
|
||
| `defineView()` | Definice uložených zobrazení |
|
||
| `defineNavigationMenuItem()` | Definice položek navigační nabídky |
|
||
| `defineSkill()` | Definice dovedností agenta AI |
|
||
| `defineAgent()` | Definice agentů AI |
|
||
|
||
<Note>
|
||
**Pojmenování souborů je flexibilní.** Detekce entit je založená na AST — SDK prochází vaše zdrojové soubory a hledá vzor `export default define<Entity>({...})`. Soubory a složky můžete organizovat, jak chcete. Seskupování podle typu entity (např. `logic-functions/`, `roles/`) je pouze konvence pro organizaci kódu, nikoli požadavek.
|
||
</Note>
|
||
|
||
Příklad detekované entity:
|
||
|
||
```typescript
|
||
// This file can be named anything and placed anywhere in src/
|
||
import { defineObject, FieldType } from 'twenty-sdk';
|
||
|
||
export default defineObject({
|
||
universalIdentifier: '...',
|
||
nameSingular: 'postCard',
|
||
// ... rest of config
|
||
});
|
||
```
|
||
|
||
Pozdější příkazy přidají další soubory a složky:
|
||
|
||
* `yarn twenty dev` automaticky vygeneruje dva typované API klienty v `node_modules/twenty-sdk/clients`: `CoreApiClient` (pro data pracovního prostoru přes `/graphql`) a `MetadataApiClient` (pro konfiguraci pracovního prostoru a nahrávání souborů přes `/metadata`).
|
||
* `yarn twenty entity:add` přidá soubory s definicemi entit do `src/` pro vaše vlastní objekty, funkce, frontové komponenty, role, dovednosti a další.
|
||
|
||
## Ověření
|
||
|
||
Při prvním spuštění `yarn twenty auth:login` budete vyzváni k zadání:
|
||
|
||
* URL API (výchozí je http://localhost:3000 nebo váš aktuální profil pracovního prostoru)
|
||
* Klíč API
|
||
|
||
Vaše přihlašovací údaje se ukládají pro jednotlivé uživatele do `~/.twenty/config.json`. Můžete spravovat více profilů a přepínat mezi nimi.
|
||
|
||
### Správa pracovních prostorů
|
||
|
||
```bash filename="Terminal"
|
||
# Login interactively (recommended)
|
||
yarn twenty auth:login
|
||
|
||
# Login to a specific workspace profile
|
||
yarn twenty auth:login --workspace my-custom-workspace
|
||
|
||
# List all configured workspaces
|
||
yarn twenty auth:list
|
||
|
||
# Switch the default workspace (interactive)
|
||
yarn twenty auth:switch
|
||
|
||
# Switch to a specific workspace
|
||
yarn twenty auth:switch production
|
||
|
||
# Check current authentication status
|
||
yarn twenty auth:status
|
||
```
|
||
|
||
Jakmile přepnete pracovní prostor pomocí `yarn twenty auth:switch`, všechny následující příkazy budou tento pracovní prostor používat jako výchozí. Můžete jej stále dočasně přepsat pomocí `--workspace <name>`.
|
||
|
||
## Používejte zdroje SDK (typy a konfiguraci)
|
||
|
||
twenty-sdk poskytuje typované stavební bloky a pomocné funkce, které používáte ve své aplikaci. Níže jsou klíčové části, se kterými budete nejčastěji pracovat.
|
||
|
||
### Pomocné funkce
|
||
|
||
SDK poskytuje pomocné funkce pro definování entit vaší aplikace. Jak je popsáno v [Detekce entit](#entity-detection), musíte použít `export default define<Entity>({...})`, aby byly vaše entity detekovány:
|
||
|
||
| Funkce | Účel |
|
||
| ---------------------------------- | ----------------------------------------------------------------- |
|
||
| `defineApplication()` | Nakonfigurujte metadata aplikace (povinné, jedno na aplikaci) |
|
||
| `defineObject()` | Definice vlastních objektů s poli |
|
||
| `defineLogicFunction()` | Definice logických funkcí s obslužnými funkcemi |
|
||
| `definePreInstallLogicFunction()` | Definujte předinstalační logickou funkci (jedna na aplikaci) |
|
||
| `definePostInstallLogicFunction()` | Definujte postinstalační logickou funkci (jedna na aplikaci) |
|
||
| `defineFrontComponent()` | Definujte frontendové komponenty pro vlastní uživatelské rozhraní |
|
||
| `defineRole()` | Konfigurace oprávnění rolí a přístupu k objektům |
|
||
| `defineField()` | Rozšiřte existující objekty o další pole |
|
||
| `defineView()` | Definujte uložená zobrazení pro objekty |
|
||
| `defineNavigationMenuItem()` | Definujte odkazy postranní navigace |
|
||
| `defineSkill()` | Definuje dovednosti agenta AI |
|
||
| `defineAgent()` | Definujte AI agenty pomocí systémových promptů |
|
||
|
||
Tyto funkce validují vaši konfiguraci v době sestavení a poskytují automatické doplňování v IDE a typovou bezpečnost.
|
||
|
||
### Definování objektů
|
||
|
||
Vlastní objekty popisují jak schéma, tak chování záznamů ve vašem pracovním prostoru. K definování objektů s vestavěnou validací použijte `defineObject()`:
|
||
|
||
```typescript
|
||
// src/app/postCard.object.ts
|
||
import { defineObject, FieldType } from 'twenty-sdk';
|
||
|
||
enum PostCardStatus {
|
||
DRAFT = 'DRAFT',
|
||
SENT = 'SENT',
|
||
DELIVERED = 'DELIVERED',
|
||
RETURNED = 'RETURNED',
|
||
}
|
||
|
||
export default defineObject({
|
||
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
|
||
nameSingular: 'postCard',
|
||
namePlural: 'postCards',
|
||
labelSingular: 'Post Card',
|
||
labelPlural: 'Post Cards',
|
||
description: 'A post card object',
|
||
icon: 'IconMail',
|
||
fields: [
|
||
{
|
||
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
|
||
name: 'content',
|
||
type: FieldType.TEXT,
|
||
label: 'Content',
|
||
description: "Postcard's content",
|
||
icon: 'IconAbc',
|
||
},
|
||
{
|
||
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
|
||
name: 'recipientName',
|
||
type: FieldType.FULL_NAME,
|
||
label: 'Recipient name',
|
||
icon: 'IconUser',
|
||
},
|
||
{
|
||
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
|
||
name: 'recipientAddress',
|
||
type: FieldType.ADDRESS,
|
||
label: 'Recipient address',
|
||
icon: 'IconHome',
|
||
},
|
||
{
|
||
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
|
||
name: 'status',
|
||
type: FieldType.SELECT,
|
||
label: 'Status',
|
||
icon: 'IconSend',
|
||
defaultValue: `'${PostCardStatus.DRAFT}'`,
|
||
options: [
|
||
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
|
||
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
|
||
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
|
||
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
|
||
],
|
||
},
|
||
{
|
||
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
|
||
name: 'deliveredAt',
|
||
type: FieldType.DATE_TIME,
|
||
label: 'Delivered at',
|
||
icon: 'IconCheck',
|
||
isNullable: true,
|
||
defaultValue: null,
|
||
},
|
||
],
|
||
});
|
||
```
|
||
|
||
Hlavní body:
|
||
|
||
* Použijte `defineObject()` pro vestavěnou validaci a lepší podporu v IDE.
|
||
* Hodnota `universalIdentifier` musí být jedinečná a stabilní napříč nasazeními.
|
||
* Každé pole vyžaduje `name`, `type`, `label` a svůj vlastní stabilní `universalIdentifier`.
|
||
* Pole `fields` je volitelné — objekty můžete definovat i bez vlastních polí.
|
||
* Nové objekty můžete vygenerovat pomocí `yarn twenty entity:add`, který vás provede pojmenováním, poli a vztahy.
|
||
|
||
<Note>
|
||
**Základní pole jsou vytvořena automaticky.** Když definujete vlastní objekt, Twenty automaticky přidá standardní pole
|
||
jako `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` a `deletedAt`.
|
||
Nemusíte je definovat v poli `fields` — přidejte pouze svá vlastní pole.
|
||
Výchozí pole můžete přepsat definováním pole se stejným názvem v poli `fields`,
|
||
ale to se nedoporučuje.
|
||
</Note>
|
||
|
||
### Definování polí u existujících objektů
|
||
|
||
Použijte `defineField()` k přidání vlastních polí k existujícím objektům — jak ke standardním objektům (např. `company`, `person`, `opportunity`), tak k vlastním objektům definovaným jinými aplikacemi. Každé pole je ve svém vlastním souboru a odkazuje na cílový objekt pomocí jeho `universalIdentifier`.
|
||
|
||
Chcete-li odkazovat na standardní objekty, importujte `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS` z `twenty-sdk`. Tato konstanta poskytuje stabilní identifikátory pro všechny vestavěné objekty a jejich pole:
|
||
|
||
```typescript
|
||
// src/fields/apollo-total-funding.field.ts
|
||
import {
|
||
defineField,
|
||
FieldType,
|
||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
|
||
} from 'twenty-sdk';
|
||
|
||
export default defineField({
|
||
universalIdentifier: 'c90ae72d-4ddf-4f22-882f-eef98c91e40e',
|
||
objectUniversalIdentifier:
|
||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
|
||
type: FieldType.CURRENCY,
|
||
name: 'apolloTotalFunding',
|
||
label: 'Total Funding',
|
||
description: 'Total funding raised by the company',
|
||
icon: 'IconCash',
|
||
});
|
||
```
|
||
|
||
Hlavní body:
|
||
|
||
* `objectUniversalIdentifier` určuje, ke kterému objektu má Twenty pole připojit. Použijte `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.<objectName>.universalIdentifier` pro standardní objekty.
|
||
* Každé pole vyžaduje svůj vlastní stabilní `universalIdentifier`, `name`, `type`, `label` a cílový `objectUniversalIdentifier`.
|
||
* Nová pole můžete vygenerovat pomocí `yarn twenty entity:add` a zvolit možnost pole.
|
||
* `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS` je pro pohodlí exportován také jako `STANDARD_OBJECT` — obojí odkazuje na stejnou konstantu.
|
||
|
||
Mezi dostupné standardní objekty patří: `attachment`, `blocklist`, `calendarChannel`, `calendarEvent`, `calendarEventParticipant`, `company`, `connectedAccount`, `dashboard`, `favorite`, `favoriteFolder`, `message`, `messageChannel`, `messageParticipant`, `messageThread`, `note`, `noteTarget`, `opportunity`, `person`, `task`, `taskTarget`, `timelineActivity`, `workflow`, `workflowAutomatedTrigger`, `workflowRun`, `workflowVersion` a `workspaceMember`.
|
||
|
||
Každý standardní objekt také zpřístupňuje identifikátory svých polí. Například chcete-li odkázat na konkrétní pole u standardního objektu v oprávněních pro role:
|
||
|
||
```typescript
|
||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.name.universalIdentifier
|
||
```
|
||
|
||
#### Vztahová pole u existujících objektů
|
||
|
||
Můžete také definovat vztahová pole, která propojí existující objekty s vašimi vlastními objekty:
|
||
|
||
```typescript
|
||
// src/fields/people-on-call-recording.field.ts
|
||
import { defineField, FieldType, RelationType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk';
|
||
import { CALL_RECORDING_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/objects/call-recording';
|
||
import { CALL_RECORDING_ON_PERSON_ID } from 'src/fields/call-recording-on-person.field';
|
||
|
||
export default defineField({
|
||
universalIdentifier: '4a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d',
|
||
objectUniversalIdentifier:
|
||
CALL_RECORDING_OBJECT_UNIVERSAL_IDENTIFIER,
|
||
type: FieldType.RELATION,
|
||
name: 'person',
|
||
label: 'Person',
|
||
relationTargetObjectMetadataUniversalIdentifier:
|
||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
|
||
relationTargetFieldMetadataUniversalIdentifier:
|
||
CALL_RECORDING_ON_PERSON_ID,
|
||
relationType: RelationType.MANY_TO_ONE,
|
||
});
|
||
```
|
||
|
||
### Konfigurace aplikace (application-config.ts)
|
||
|
||
Každá aplikace má jeden soubor `application-config.ts`, který popisuje:
|
||
|
||
* **Identitu aplikace**: identifikátory, zobrazovaný název a popis.
|
||
* **Jak běží její funkce**: kterou roli používají pro oprávnění.
|
||
* **(Volitelné) proměnné**: dvojice klíč–hodnota zpřístupněné vašim funkcím jako proměnné prostředí.
|
||
* **(Volitelná) předinstalační funkce**: logická funkce, která se spouští před instalací aplikace.
|
||
* **(Volitelná) postinstalační funkce**: logická funkce, která se spouští po instalaci aplikace.
|
||
|
||
Use `defineApplication()` to define your application configuration:
|
||
|
||
```typescript
|
||
// src/application-config.ts
|
||
import { defineApplication } from 'twenty-sdk';
|
||
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role';
|
||
|
||
export default defineApplication({
|
||
universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
|
||
displayName: 'My Twenty App',
|
||
description: 'My first Twenty app',
|
||
icon: 'IconWorld',
|
||
applicationVariables: {
|
||
DEFAULT_RECIPIENT_NAME: {
|
||
universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
|
||
description: 'Default recipient name for postcards',
|
||
value: 'Jane Doe',
|
||
isSecret: false,
|
||
},
|
||
},
|
||
defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
||
});
|
||
```
|
||
|
||
Poznámky:
|
||
|
||
* Pole `universalIdentifier` jsou deterministická ID, která vlastníte; vygenerujte je jednou a udržujte je stabilní napříč synchronizacemi.
|
||
* `applicationVariables` se stanou proměnnými prostředí pro vaše funkce (například `DEFAULT_RECIPIENT_NAME` je dostupné jako `process.env.DEFAULT_RECIPIENT_NAME`).
|
||
* `defaultRoleUniversalIdentifier` se musí shodovat se souborem role (viz níže).
|
||
* Předinstalační a postinstalační funkce jsou při sestavování manifestu automaticky detekovány. Viz [Předinstalační funkce](#pre-install-functions) a [Postinstalační funkce](#post-install-functions).
|
||
|
||
#### Role a oprávnění
|
||
|
||
Aplikace mohou definovat role, které zapouzdřují oprávnění k objektům a akcím ve vašem pracovním prostoru. Pole `defaultRoleUniversalIdentifier` v `application-config.ts` určuje výchozí roli používanou logickými funkcemi vaší aplikace.
|
||
|
||
* Běhový klíč API vložený jako `TWENTY_API_KEY` je odvozen z této výchozí role funkcí.
|
||
* Typovaný klient bude omezen oprávněními udělenými této roli.
|
||
* Dodržujte princip nejmenších oprávnění: vytvořte vyhrazenou roli pouze s oprávněními, která vaše funkce potřebují, a poté odkazujte na její univerzální identifikátor.
|
||
|
||
##### Výchozí role funkce (\*.role.ts)
|
||
|
||
Když vygenerujete novou aplikaci, CLI také vytvoří výchozí soubor role. K definování rolí s vestavěnou validací použijte `defineRole()`:
|
||
|
||
```typescript
|
||
// src/roles/default-role.ts
|
||
import { defineRole, PermissionFlag } from 'twenty-sdk';
|
||
|
||
export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER =
|
||
'b648f87b-1d26-4961-b974-0908fd991061';
|
||
|
||
export default defineRole({
|
||
universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
||
label: 'Default function role',
|
||
description: 'Default role for function Twenty client',
|
||
canReadAllObjectRecords: false,
|
||
canUpdateAllObjectRecords: false,
|
||
canSoftDeleteAllObjectRecords: false,
|
||
canDestroyAllObjectRecords: false,
|
||
canUpdateAllSettings: false,
|
||
canBeAssignedToAgents: false,
|
||
canBeAssignedToUsers: false,
|
||
canBeAssignedToApiKeys: false,
|
||
objectPermissions: [
|
||
{
|
||
objectUniversalIdentifier: '9f9882af-170c-4879-b013-f9628b77c050',
|
||
canReadObjectRecords: true,
|
||
canUpdateObjectRecords: true,
|
||
canSoftDeleteObjectRecords: false,
|
||
canDestroyObjectRecords: false,
|
||
},
|
||
],
|
||
fieldPermissions: [
|
||
{
|
||
objectUniversalIdentifier: '9f9882af-170c-4879-b013-f9628b77c050',
|
||
fieldUniversalIdentifier: 'b2c37dc0-8ae7-470e-96cd-1476b47dfaff',
|
||
canReadFieldValue: false,
|
||
canUpdateFieldValue: false,
|
||
},
|
||
],
|
||
permissionFlags: [PermissionFlag.APPLICATIONS],
|
||
});
|
||
```
|
||
|
||
Na `universalIdentifier` této role se poté odkazuje v `application-config.ts` jako na `defaultRoleUniversalIdentifier`. Jinými slovy:
|
||
|
||
* **\*.role.ts** definuje, co může výchozí role funkce dělat.
|
||
* **application-config.ts** ukazuje na tuto roli, aby vaše funkce zdědily její oprávnění.
|
||
|
||
Poznámky:
|
||
|
||
* Začněte rolí vytvořenou scaffolderem a postupně ji omezujte podle principu nejmenších oprávnění.
|
||
* Nahraďte `objectPermissions` a `fieldPermissions` objekty/poli, která vaše funkce potřebují.
|
||
* `permissionFlags` řídí přístup k schopnostem na úrovni platformy. Držte je na minimu; přidávejte pouze to, co potřebujete.
|
||
* Podívejte se na funkční příklad v aplikaci Hello World: [`packages/twenty-apps/hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts).
|
||
|
||
### Konfigurace logických funkcí a vstupní bod
|
||
|
||
Každý soubor funkce používá `defineLogicFunction()` k exportu konfigurace s obslužnou funkcí (handlerem) a volitelnými spouštěči.
|
||
|
||
```typescript
|
||
// src/app/createPostCard.logic-function.ts
|
||
import { defineLogicFunction } from 'twenty-sdk';
|
||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||
import { CoreApiClient, type Person } from 'twenty-client-sdk/core';
|
||
|
||
const handler = async (params: RoutePayload) => {
|
||
const client = new CoreApiClient();
|
||
const name = 'name' in params.queryStringParameters
|
||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||
: 'Hello world';
|
||
|
||
const result = await client.mutation({
|
||
createPostCard: {
|
||
__args: { data: { name } },
|
||
id: true,
|
||
name: true,
|
||
},
|
||
});
|
||
return result;
|
||
};
|
||
|
||
export default defineLogicFunction({
|
||
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
|
||
name: 'create-new-post-card',
|
||
timeoutSeconds: 2,
|
||
handler,
|
||
triggers: [
|
||
// Public HTTP route trigger '/s/post-card/create'
|
||
{
|
||
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
|
||
type: 'route',
|
||
path: '/post-card/create',
|
||
httpMethod: 'GET',
|
||
isAuthRequired: false,
|
||
},
|
||
// Cron trigger (CRON pattern)
|
||
// {
|
||
// universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2',
|
||
// type: 'cron',
|
||
// pattern: '0 0 1 1 *',
|
||
// },
|
||
// Database event trigger
|
||
// {
|
||
// universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
|
||
// type: 'databaseEvent',
|
||
// eventName: 'person.updated',
|
||
// updatedFields: ['name'],
|
||
// },
|
||
],
|
||
});
|
||
```
|
||
|
||
Běžné typy spouštěčů:
|
||
|
||
* **route**: Zpřístupní vaši funkci na HTTP cestě a metodě **pod koncovým bodem `/s/`**:
|
||
|
||
> např. `path: '/post-card/create',` -> volání na `<APP_URL>/s/post-card/create`
|
||
|
||
* **cron**: Spouští vaši funkci podle plánu pomocí výrazu CRON.
|
||
* **databaseEvent**: Spouští se při událostech životního cyklu objektů v pracovním prostoru. Když je operace události `updated`, lze konkrétní sledovaná pole určit v poli `updatedFields`. Pokud zůstane nedefinované nebo prázdné, spustí funkci jakákoli aktualizace.
|
||
|
||
> např. `person.updated`
|
||
|
||
Poznámky:
|
||
|
||
* Pole `triggers` je volitelné. Funkce bez spouštěčů lze použít jako pomocné funkce volané jinými funkcemi.
|
||
* V jedné funkci můžete kombinovat více typů spouštěčů.
|
||
|
||
### Předinstalační funkce
|
||
|
||
Předinstalační funkce je logická funkce, která se automaticky spouští před instalací vaší aplikace v pracovním prostoru. To je užitečné pro validační úlohy, kontrolu předpokladů nebo přípravu stavu pracovního prostoru před zahájením hlavní instalace.
|
||
|
||
Když vygenerujete kostru nové aplikace pomocí `create-twenty-app`, vytvoří se pro vás předinstalační funkce v `src/logic-functions/pre-install.ts`:
|
||
|
||
```typescript
|
||
// src/logic-functions/pre-install.ts
|
||
import { definePreInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';
|
||
|
||
const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
|
||
console.log('Pre install logic function executed successfully!', payload.previousVersion);
|
||
};
|
||
|
||
export default definePreInstallLogicFunction({
|
||
universalIdentifier: '<generated-uuid>',
|
||
name: 'pre-install',
|
||
description: 'Runs before installation to prepare the application.',
|
||
timeoutSeconds: 300,
|
||
handler,
|
||
});
|
||
```
|
||
|
||
Předinstalační funkci můžete také kdykoli spustit ručně pomocí CLI:
|
||
|
||
```bash filename="Terminal"
|
||
yarn twenty function:execute --preInstall
|
||
```
|
||
|
||
Hlavní body:
|
||
|
||
* Předinstalační funkce používají `definePreInstallLogicFunction()` — specializovanou variantu, která vynechává nastavení spouštěčů (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `isTool`).
|
||
* Obslužná funkce (handler) obdrží `InstallLogicFunctionPayload` s `{ previousVersion: string }` — verzi aplikace, která byla dříve nainstalována (nebo prázdný řetězec při čisté instalaci).
|
||
* Na jednu aplikaci je povolena pouze jedna předinstalační funkce. Sestavení manifestu skončí chybou, pokud je zjištěna více než jedna.
|
||
* Identifikátor `universalIdentifier` funkce se během sestavení automaticky nastaví v manifestu aplikace jako `preInstallLogicFunctionUniversalIdentifier` — není potřeba jej uvádět v `defineApplication()`.
|
||
* Výchozí časový limit je nastaven na 300 sekund (5 minut), aby umožnil delší přípravné úlohy.
|
||
* Předinstalační funkce nepotřebují spouštěče — platforma je vyvolává před instalací nebo je lze spustit ručně pomocí `function:execute --preInstall`.
|
||
|
||
### Postinstalační funkce
|
||
|
||
Postinstalační funkce je logická funkce, která se automaticky spouští po instalaci vaší aplikace do pracovního prostoru. To je užitečné pro jednorázové úlohy nastavení, jako je naplnění výchozími daty, vytvoření počátečních záznamů nebo konfigurace nastavení pracovního prostoru.
|
||
|
||
Když vygenerujete kostru nové aplikace pomocí `create-twenty-app`, vytvoří se pro vás postinstalační funkce v `src/logic-functions/post-install.ts`:
|
||
|
||
```typescript
|
||
// src/logic-functions/post-install.ts
|
||
import { definePostInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';
|
||
|
||
const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
|
||
console.log('Post install logic function executed successfully!', payload.previousVersion);
|
||
};
|
||
|
||
export default definePostInstallLogicFunction({
|
||
universalIdentifier: '<generated-uuid>',
|
||
name: 'post-install',
|
||
description: 'Runs after installation to set up the application.',
|
||
timeoutSeconds: 300,
|
||
handler,
|
||
});
|
||
```
|
||
|
||
Postinstalační funkci můžete také kdykoli spustit ručně pomocí CLI:
|
||
|
||
```bash filename="Terminal"
|
||
yarn twenty function:execute --postInstall
|
||
```
|
||
|
||
Hlavní body:
|
||
|
||
* Postinstalační funkce používají `definePostInstallLogicFunction()` — specializovanou variantu, která vynechává nastavení spouštěčů (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `isTool`).
|
||
* Obslužná funkce (handler) obdrží `InstallLogicFunctionPayload` s `{ previousVersion: string }` — verzi aplikace, která byla dříve nainstalována (nebo prázdný řetězec při čisté instalaci).
|
||
* Na jednu aplikaci je povolena pouze jedna postinstalační funkce. Sestavení manifestu skončí chybou, pokud je zjištěna více než jedna.
|
||
* Identifikátor `universalIdentifier` funkce se během sestavení automaticky nastaví v manifestu aplikace jako `postInstallLogicFunctionUniversalIdentifier` — není potřeba jej uvádět v `defineApplication()`.
|
||
* Výchozí časový limit je nastaven na 300 sekund (5 minut), aby umožnil delší úlohy nastavení, jako je naplnění daty.
|
||
* Postinstalační funkce nepotřebují spouštěče — jsou spouštěny platformou během instalace nebo ručně pomocí `function:execute --postInstall`.
|
||
|
||
### Payload spouštěče trasy
|
||
|
||
<Warning>
|
||
**Zpětně nekompatibilní změna (v1.16, leden 2026):** Formát payloadu spouštěče trasy se změnil. Před verzí v1.16 byly parametry dotazu, parametry cesty a tělo odesílány přímo jako payload. Od verze v1.16 jsou zanořeny uvnitř strukturovaného objektu `RoutePayload`.
|
||
|
||
**Před v1.16:**
|
||
```typescript
|
||
const handler = async (params) => {
|
||
const { param1, param2 } = params; // Direct access
|
||
};
|
||
```
|
||
|
||
**Po v1.16:**
|
||
```typescript
|
||
const handler = async (event: RoutePayload) => {
|
||
const { param1, param2 } = event.body; // Access via .body
|
||
const { queryParam } = event.queryStringParameters;
|
||
const { id } = event.pathParameters;
|
||
};
|
||
```
|
||
|
||
**Jak migrovat existující funkce:** Aktualizujte svůj handler tak, aby destrukturoval z `event.body`, `event.queryStringParameters` nebo `event.pathParameters` místo přímo z objektu params.
|
||
</Warning>
|
||
|
||
Když spouštěč trasy vyvolá vaši logickou funkci, ta obdrží objekt `RoutePayload`, který odpovídá formátu AWS HTTP API v2. Importujte typ z `twenty-sdk`:
|
||
|
||
```typescript
|
||
import { defineLogicFunction, type RoutePayload } from 'twenty-sdk';
|
||
|
||
const handler = async (event: RoutePayload) => {
|
||
// Access request data
|
||
const { headers, queryStringParameters, pathParameters, body } = event;
|
||
|
||
// HTTP method and path are available in requestContext
|
||
const { method, path } = event.requestContext.http;
|
||
|
||
return { message: 'Success' };
|
||
};
|
||
```
|
||
|
||
Typ `RoutePayload` má následující strukturu:
|
||
|
||
| Vlastnost | Typ | Popis |
|
||
| ---------------------------- | ------------------------------------- | --------------------------------------------------------------------------------- |
|
||
| `headers` | `Record<string, string \| undefined>` | Záhlaví HTTP (pouze ta uvedená v `forwardedRequestHeaders`) |
|
||
| `queryStringParameters` | `Record<string, string \| undefined>` | Parametry query stringu (více hodnot spojených čárkami) |
|
||
| `pathParameters` | `Record<string, string \| undefined>` | Parametry cesty extrahované ze vzoru trasy (např. `/users/:id` → `{ id: '123' }`) |
|
||
| `text zprávy` | `object \| null` | Parsované tělo požadavku (JSON) |
|
||
| `isBase64Encoded` | `booleovská hodnota` | Zda je tělo kódováno base64 |
|
||
| `requestContext.http.method` | `string` | Metoda HTTP (GET, POST, PUT, PATCH, DELETE) |
|
||
| `requestContext.http.path` | `string` | Nezpracovaná cesta požadavku |
|
||
|
||
### Přeposílání záhlaví HTTP
|
||
|
||
Ve výchozím nastavení se záhlaví HTTP z příchozích požadavků z bezpečnostních důvodů do vaší logické funkce **ne** předávají. Chcete-li zpřístupnit konkrétní záhlaví, výslovně je uveďte v poli `forwardedRequestHeaders`:
|
||
|
||
```typescript
|
||
export default defineLogicFunction({
|
||
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
|
||
name: 'webhook-handler',
|
||
handler,
|
||
triggers: [
|
||
{
|
||
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
|
||
type: 'route',
|
||
path: '/webhook',
|
||
httpMethod: 'POST',
|
||
isAuthRequired: false,
|
||
forwardedRequestHeaders: ['x-webhook-signature', 'content-type'],
|
||
},
|
||
],
|
||
});
|
||
```
|
||
|
||
Ve vašem handleru k nim poté můžete přistupovat:
|
||
|
||
```typescript
|
||
const handler = async (event: RoutePayload) => {
|
||
const signature = event.headers['x-webhook-signature'];
|
||
const contentType = event.headers['content-type'];
|
||
|
||
// Validate webhook signature...
|
||
return { received: true };
|
||
};
|
||
```
|
||
|
||
<Note>
|
||
Názvy záhlaví jsou normalizovány na malá písmena. Přistupujte k nim pomocí klíčů s malými písmeny (například `event.headers['content-type']`).
|
||
</Note>
|
||
|
||
Nové funkce můžete vytvářet dvěma způsoby:
|
||
|
||
* **Vygenerované**: Spusťte `yarn twenty entity:add` a zvolte možnost přidat novou logickou funkci. Tím se vygeneruje startovací soubor s obslužnou funkcí a konfigurací.
|
||
* **Ruční**: Vytvořte nový soubor `*.logic-function.ts` a použijte `defineLogicFunction()` podle stejného vzoru.
|
||
|
||
### Označení logické funkce jako nástroje
|
||
|
||
Logické funkce lze zpřístupnit jako **nástroje** pro agenty AI a pracovní postupy. Když je funkce označena jako nástroj, stane se dohledatelnou funkcemi AI produktu Twenty a lze ji vybrat jako krok v automatizacích pracovních postupů.
|
||
|
||
Chcete-li označit logickou funkci jako nástroj, nastavte `isTool: true` a poskytněte `toolInputSchema` popisující očekávané vstupní parametry pomocí [JSON Schema](https://json-schema.org/):
|
||
|
||
```typescript
|
||
// src/logic-functions/enrich-company.logic-function.ts
|
||
import { defineLogicFunction } from 'twenty-sdk';
|
||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||
|
||
const handler = async (params: { companyName: string; domain?: string }) => {
|
||
const client = new CoreApiClient();
|
||
|
||
const result = await client.mutation({
|
||
createTask: {
|
||
__args: {
|
||
data: {
|
||
title: `Enrich data for ${params.companyName}`,
|
||
body: `Domain: ${params.domain ?? 'unknown'}`,
|
||
},
|
||
},
|
||
id: true,
|
||
},
|
||
});
|
||
|
||
return { taskId: result.createTask.id };
|
||
};
|
||
|
||
export default defineLogicFunction({
|
||
universalIdentifier: 'f47ac10b-58cc-4372-a567-0e02b2c3d479',
|
||
name: 'enrich-company',
|
||
description: 'Enrich a company record with external data',
|
||
timeoutSeconds: 10,
|
||
handler,
|
||
isTool: true,
|
||
toolInputSchema: {
|
||
type: 'object',
|
||
properties: {
|
||
companyName: {
|
||
type: 'string',
|
||
description: 'The name of the company to enrich',
|
||
},
|
||
domain: {
|
||
type: 'string',
|
||
description: 'The company website domain (optional)',
|
||
},
|
||
},
|
||
required: ['companyName'],
|
||
},
|
||
});
|
||
```
|
||
|
||
Hlavní body:
|
||
|
||
* **`isTool`** (`boolean`, výchozí: `false`): Když je nastaveno na `true`, funkce je zaregistrována jako nástroj a zpřístupní se agentům AI a automatizacím pracovních postupů.
|
||
* **`toolInputSchema`** (`object`, volitelné): Objekt JSON Schema, který popisuje parametry, jež vaše funkce přijímá. Agenti AI používají toto schéma k pochopení toho, jaké vstupy nástroj očekává, a k ověřování volání. Pokud je vynecháno, schéma má výchozí podobu `{ type: 'object', properties: {} }` (žádné parametry).
|
||
* Funkce s `isTool: false` (nebo není nastaveno) **nejsou** zpřístupněny jako nástroje. Stále je lze spouštět přímo nebo volat z jiných funkcí, ale neobjeví se ve vyhledávání nástrojů.
|
||
* **Pojmenování nástrojů**: Když je funkce zpřístupněna jako nástroj, její název se automaticky normalizuje na `logic_function_<name>` (převedeno na malá písmena, nealfanumerické znaky jsou nahrazeny podtržítky). Například `enrich-company` se změní na `logic_function_enrich_company`.
|
||
* Můžete kombinovat `isTool` se spouštěči — funkce může být zároveň nástrojem (volatelným agenty AI) i spouštěna událostmi (cron, databázové události, routes).
|
||
|
||
<Note>
|
||
**Napište kvalitní `description`.** Agenti AI se spoléhají na pole funkce `description` při rozhodování, kdy nástroj použít. Buďte konkrétní ohledně toho, co nástroj dělá a kdy se má volat.
|
||
</Note>
|
||
|
||
### Frontendové komponenty
|
||
|
||
Frontendové komponenty vám umožňují vytvářet vlastní React komponenty, které se vykreslují v rozhraní Twenty. K definování komponent s vestavěnou validací použijte `defineFrontComponent()`:
|
||
|
||
```typescript
|
||
// src/front-components/my-widget.tsx
|
||
import { defineFrontComponent } from 'twenty-sdk';
|
||
|
||
const MyWidget = () => {
|
||
return (
|
||
<div style={{ padding: '20px', fontFamily: 'sans-serif' }}>
|
||
<h1>My Custom Widget</h1>
|
||
<p>This is a custom front component for Twenty.</p>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default defineFrontComponent({
|
||
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||
name: 'my-widget',
|
||
description: 'A custom widget component',
|
||
component: MyWidget,
|
||
});
|
||
```
|
||
|
||
Hlavní body:
|
||
|
||
* Frontendové komponenty jsou React komponenty, které se vykreslují v izolovaných kontextech v rámci Twenty.
|
||
* Pole `component` odkazuje na vaši React komponentu.
|
||
* Komponenty se během `yarn twenty dev` automaticky sestaví a synchronizují.
|
||
|
||
Nové frontendové komponenty můžete vytvořit dvěma způsoby:
|
||
|
||
* **Vygenerované**: Spusťte `yarn twenty entity:add` a zvolte možnost přidat novou frontendovou komponentu.
|
||
* **Ruční**: Vytvořte nový soubor `.tsx` a použijte `defineFrontComponent()`, podle stejného vzoru.
|
||
|
||
#### Kde lze použít front komponenty
|
||
|
||
Front komponenty se mohou vykreslovat na dvou místech v rámci Twenty:
|
||
|
||
* **Postranní panel** — Ne-headless front komponenty se otevírají v pravém postranním panelu. Toto je výchozí chování, když je front komponenta vyvolána z menu příkazů.
|
||
* **Widgety (nástěnky a stránky záznamů)** — Front komponenty lze vkládat jako widgety do rozložení stránek. Při konfiguraci nástěnky nebo rozložení stránky záznamu mohou uživatelé přidat widget front komponenty.
|
||
|
||
#### Headless vs. ne-headless
|
||
|
||
Front komponenty existují ve dvou režimech vykreslování řízených volbou `isHeadless`:
|
||
|
||
**Ne-headless (výchozí)** — Komponenta vykreslí viditelné uživatelské rozhraní. Po vyvolání z menu příkazů se otevře v postranním panelu. Toto je výchozí chování, když je `isHeadless` `false` nebo když tato volba není uvedena.
|
||
|
||
**Headless** — Komponenta se neviditelně inicializuje na pozadí. Neotevírá postranní panel. Headless komponenty jsou určené pro akce, které provedou logiku a poté se odpojí — například spuštění asynchronního úkolu, navigaci na stránku nebo zobrazení potvrzovacího modálního okna. Přirozeně se hodí ke komponentám SDK Command popsaným níže.
|
||
|
||
```typescript
|
||
export default defineFrontComponent({
|
||
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||
name: 'my-action',
|
||
description: 'Runs an action without opening the side panel',
|
||
component: MyAction,
|
||
isHeadless: true,
|
||
command: {
|
||
universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f12345678901',
|
||
label: 'Run my action',
|
||
},
|
||
});
|
||
```
|
||
|
||
#### Přidávání položek menu příkazů
|
||
|
||
Aby se front komponenta zobrazila jako položka v menu příkazů Twenty, přidejte vlastnost `command` k `defineFrontComponent()`. Když uživatelé otevřou menu příkazů (Cmd+K / Ctrl+K), položka se zobrazí a po kliknutí spustí front komponentu.
|
||
|
||
Objekt `command` přijímá následující pole:
|
||
|
||
| Pole | Typ | Popis |
|
||
| --------------------------------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
||
| `universalIdentifier` | `string` (povinné) | Jedinečné ID položky menu příkazů |
|
||
| `štítek` | `string` (povinné) | Text zobrazený v menu příkazů |
|
||
| `ikona` | `string` (nepovinné) | Název ikony (např. `'IconSparkles'`) |
|
||
| `isPinned` | `boolean` (nepovinné) | Zda je příkaz připnutý nahoře v menu |
|
||
| `availabilityType` | `'GLOBAL' \| 'RECORD_SELECTION'` (nepovinné) | `GLOBAL` zobrazuje příkaz všude; `RECORD_SELECTION` jej zobrazuje pouze v kontextech záznamů |
|
||
| `availabilityObjectUniversalIdentifier` | `string` (nepovinné) | Omezí příkaz na konkrétní typ objektu (např. Person) |
|
||
|
||
Zde je příklad z aplikace pro nahrávání hovorů, který přidává příkaz omezený na záznamy typu Person:
|
||
|
||
```typescript
|
||
import { defineFrontComponent } from 'twenty-sdk';
|
||
|
||
export default defineFrontComponent({
|
||
universalIdentifier: 'c3d4e5f6-a7b8-9012-cdef-123456789012',
|
||
name: 'Summarize Person Call Recordings',
|
||
description: 'Generates a summary of call recordings for a person',
|
||
component: SummarizePersonRecordings,
|
||
command: {
|
||
universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-234567890123',
|
||
label: 'Summarize call recordings',
|
||
icon: 'IconSparkles',
|
||
isPinned: false,
|
||
availabilityType: 'RECORD_SELECTION',
|
||
availabilityObjectUniversalIdentifier:
|
||
'20202020-e674-48e5-a542-72570eee7213',
|
||
},
|
||
});
|
||
```
|
||
|
||
Když se příkaz synchronizuje, objeví se v menu příkazů. Pokud je front komponenta ne-headless, otevře se postranní panel s komponentou vykreslenou uvnitř. Pokud je headless, komponenta se inicializuje na pozadí a provede svou logiku.
|
||
|
||
#### Komponenty SDK Command
|
||
|
||
Balíček `twenty-sdk` poskytuje čtyři pomocné komponenty Command navržené pro headless front komponenty. Každá komponenta při připojení provede akci, chyby zpracuje zobrazením oznámení ve snackbaru a po dokončení automaticky odpojí front komponentu.
|
||
|
||
Importujte je z `twenty-sdk/command`:
|
||
|
||
* **`Command`** — Spustí asynchronní callback přes prop `execute`.
|
||
* **`CommandLink`** — Naviguje na cestu v aplikaci. Props: `to`, `params`, `queryParams`, `options`.
|
||
* **`CommandModal`** — Otevře potvrzovací modální okno. Pokud uživatel potvrdí, provede callback `execute`. Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`.
|
||
* **`CommandOpenSidePanelPage`** — Otevře konkrétní stránku postranního panelu. Props: `page`, `pageTitle`, `pageIcon`.
|
||
|
||
Zde je kompletní příklad headless front komponenty, která pomocí `Command` spouští akci z menu příkazů:
|
||
|
||
```typescript
|
||
// src/front-components/run-action.tsx
|
||
import { defineFrontComponent } from 'twenty-sdk';
|
||
import { Command } from 'twenty-sdk/command';
|
||
import { CoreApiClient } from 'twenty-sdk/clients';
|
||
|
||
const RunAction = () => {
|
||
const execute = async () => {
|
||
const client = new CoreApiClient();
|
||
|
||
await client.mutation({
|
||
createTask: {
|
||
__args: { data: { title: 'Created by my app' } },
|
||
id: true,
|
||
},
|
||
});
|
||
};
|
||
|
||
return <Command execute={execute} />;
|
||
};
|
||
|
||
export default defineFrontComponent({
|
||
universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
|
||
name: 'run-action',
|
||
description: 'Creates a task from the command menu',
|
||
component: RunAction,
|
||
isHeadless: true,
|
||
command: {
|
||
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
|
||
label: 'Run my action',
|
||
icon: 'IconPlayerPlay',
|
||
},
|
||
});
|
||
```
|
||
|
||
A příklad s použitím `CommandModal` k vyžádání potvrzení před provedením:
|
||
|
||
```typescript
|
||
// src/front-components/delete-draft.tsx
|
||
import { defineFrontComponent } from 'twenty-sdk';
|
||
import { CommandModal } from 'twenty-sdk/command';
|
||
|
||
const DeleteDraft = () => {
|
||
const execute = async () => {
|
||
// perform the deletion
|
||
};
|
||
|
||
return (
|
||
<CommandModal
|
||
title="Delete draft?"
|
||
subtitle="This action cannot be undone."
|
||
execute={execute}
|
||
confirmButtonText="Delete"
|
||
confirmButtonAccent="danger"
|
||
/>
|
||
);
|
||
};
|
||
|
||
export default defineFrontComponent({
|
||
universalIdentifier: 'a7b8c9d0-e1f2-3456-abcd-567890123456',
|
||
name: 'delete-draft',
|
||
description: 'Deletes a draft with confirmation',
|
||
component: DeleteDraft,
|
||
isHeadless: true,
|
||
command: {
|
||
universalIdentifier: 'b8c9d0e1-f2a3-4567-bcde-678901234567',
|
||
label: 'Delete draft',
|
||
icon: 'IconTrash',
|
||
},
|
||
});
|
||
```
|
||
|
||
#### Kontext provádění
|
||
|
||
Každá front komponenta získá kontext provádění, který poskytuje informace o tom, kde a jak běží. K hodnotám kontextu přistupujte pomocí hooků z `twenty-sdk`:
|
||
|
||
| Hook | Návratový typ | Popis |
|
||
| ----------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| `useFrontComponentId()` | `string` | Jedinečné ID aktuální instance front komponenty |
|
||
| `useRecordId()` | `string \| null` | ID aktuálního záznamu, když komponenta běží v kontextu záznamu (např. widget na stránce záznamu nebo příkaz omezený na záznam). V opačném případě vrací `null`. |
|
||
| `useUserId()` | `string \| null` | ID aktuálního uživatele |
|
||
|
||
```typescript
|
||
import { useRecordId, useUserId } from 'twenty-sdk';
|
||
|
||
const MyWidget = () => {
|
||
const recordId = useRecordId();
|
||
const userId = useUserId();
|
||
|
||
return (
|
||
<div>
|
||
<p>Record: {recordId ?? 'none'}</p>
|
||
<p>User: {userId ?? 'anonymous'}</p>
|
||
</div>
|
||
);
|
||
};
|
||
```
|
||
|
||
Kontext je reaktivní — pokud se okolní záznam změní, hooky automaticky vrátí aktualizované hodnoty.
|
||
|
||
#### Funkce hostitelského API
|
||
|
||
Front komponenty běží v izolovaném sandboxu, ale mohou interagovat s UI Twenty prostřednictvím sady funkcí poskytovaných hostitelem. Importujte je přímo z `twenty-sdk`:
|
||
|
||
```typescript
|
||
import {
|
||
navigate,
|
||
closeSidePanel,
|
||
enqueueSnackbar,
|
||
unmountFrontComponent,
|
||
openSidePanelPage,
|
||
openCommandConfirmationModal,
|
||
} from 'twenty-sdk';
|
||
```
|
||
|
||
| Funkce | Signatura | Popis |
|
||
| ------------------------------ | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| `navigovat` | `(to, params?, queryParams?, options?) => Promise<void>` | Přejde na typovanou cestu aplikace v rámci Twenty |
|
||
| `closeSidePanel` | `() => Promise<void>` | Zavře postranní panel |
|
||
| `enqueueSnackbar` | `(params) => Promise<void>` | Zobrazí oznámení ve snackbaru. Parametry: `message`, `variant` (`'error'`, `'success'`, `'info'`, `'warning'`), volitelně `duration`, `detailedMessage`, `dedupeKey` |
|
||
| `unmountFrontComponent` | `() => Promise<void>` | Odpojí aktuální front komponentu (používají headless komponenty k úklidu po vykonání) |
|
||
| `openSidePanelPage` | `(params) => Promise<void>` | Otevře stránku v postranním panelu. Parametry: `page`, `pageTitle`, `pageIcon`, `shouldResetSearchState` |
|
||
| `openCommandConfirmationModal` | `(params) => Promise<'confirm' \| 'cancel'>` | Zobrazí potvrzovací modální okno a počká na reakci uživatele. Parametry: `title`, `subtitle`, `confirmButtonText`, `confirmButtonAccent` (`'default'`, `'blue'`, `'danger'`) |
|
||
|
||
Zde je příklad, který používá hostitelské API k zobrazení snackbaru a zavření postranního panelu po dokončení akce:
|
||
|
||
```typescript
|
||
import { defineFrontComponent, useRecordId } from 'twenty-sdk';
|
||
import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk';
|
||
import { CoreApiClient } from 'twenty-sdk/clients';
|
||
|
||
const ArchiveRecord = () => {
|
||
const recordId = useRecordId();
|
||
|
||
const handleArchive = async () => {
|
||
const client = new CoreApiClient();
|
||
|
||
await client.mutation({
|
||
updateTask: {
|
||
__args: { id: recordId, data: { status: 'ARCHIVED' } },
|
||
id: true,
|
||
},
|
||
});
|
||
|
||
await enqueueSnackbar({
|
||
message: 'Record archived',
|
||
variant: 'success',
|
||
});
|
||
|
||
await closeSidePanel();
|
||
};
|
||
|
||
return (
|
||
<div style={{ padding: '20px' }}>
|
||
<p>Archive this record?</p>
|
||
<button onClick={handleArchive}>Archive</button>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default defineFrontComponent({
|
||
universalIdentifier: 'c9d0e1f2-a3b4-5678-cdef-789012345678',
|
||
name: 'archive-record',
|
||
description: 'Archives the current record',
|
||
component: ArchiveRecord,
|
||
});
|
||
```
|
||
|
||
### Dovednosti
|
||
|
||
Dovednosti definují znovupoužitelné pokyny a schopnosti, které mohou agenti AI používat ve vašem pracovním prostoru. K definování dovedností s vestavěnou validací použijte `defineSkill()`:
|
||
|
||
```typescript
|
||
// src/skills/example-skill.ts
|
||
import { defineSkill } from 'twenty-sdk';
|
||
|
||
export default defineSkill({
|
||
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||
name: 'sales-outreach',
|
||
label: 'Sales Outreach',
|
||
description: 'Guides the AI agent through a structured sales outreach process',
|
||
icon: 'IconBrain',
|
||
content: `You are a sales outreach assistant. When reaching out to a prospect:
|
||
1. Research the company and recent news
|
||
2. Identify the prospect's role and likely pain points
|
||
3. Draft a personalized message referencing specific details
|
||
4. Keep the tone professional but conversational`,
|
||
});
|
||
```
|
||
|
||
Hlavní body:
|
||
|
||
* `name` je jedinečný identifikátor dovednosti (doporučuje se kebab-case).
|
||
* `label` je uživatelsky čitelný název zobrazovaný v UI.
|
||
* `content` obsahuje pokyny dovednosti — je to text, který agent AI používá.
|
||
* `icon` (volitelné) nastavuje ikonu zobrazovanou v UI.
|
||
* `description` (volitelné) poskytuje doplňující kontext o účelu dovednosti.
|
||
|
||
Nové dovednosti můžete vytvářet dvěma způsoby:
|
||
|
||
* **Vygenerované**: Spusťte `yarn twenty entity:add` a zvolte možnost přidat novou dovednost.
|
||
* **Ruční**: Vytvořte nový soubor a použijte `defineSkill()` podle stejného vzoru.
|
||
|
||
### Agenti
|
||
|
||
Agenti jsou AI agenti se systémovými prompty, kteří mohou fungovat ve vašem pracovním prostoru. K definování agentů s vestavěnou validací použijte `defineAgent()`:
|
||
|
||
```typescript
|
||
// src/agents/example-agent.ts
|
||
import { defineAgent } from 'twenty-sdk';
|
||
|
||
export default defineAgent({
|
||
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||
name: 'sales-assistant',
|
||
label: 'Sales Assistant',
|
||
description: 'An AI agent that helps with sales tasks',
|
||
icon: 'IconRobot',
|
||
prompt: `You are a sales assistant. Help users with:
|
||
1. Researching prospects and companies
|
||
2. Drafting personalized outreach messages
|
||
3. Tracking follow-ups and next steps
|
||
4. Analyzing deal pipeline and suggesting actions`,
|
||
});
|
||
```
|
||
|
||
Hlavní body:
|
||
|
||
* `name` je jedinečný identifikátor agenta (doporučuje se kebab-case).
|
||
* `label` je uživatelsky čitelný název zobrazovaný v UI.
|
||
* `prompt` obsahuje systémový prompt — jde o instrukční text, který určuje chování agenta.
|
||
* `icon` (volitelné) nastavuje ikonu zobrazovanou v UI.
|
||
* `description` (volitelné) poskytuje doplňující kontext o účelu agenta.
|
||
|
||
Nové agenty můžete vytvářet dvěma způsoby:
|
||
|
||
* **Vygenerované**: Spusťte `yarn twenty entity:add` a zvolte možnost přidat nového agenta.
|
||
* **Ruční**: Vytvořte nový soubor a použijte `defineAgent()` podle stejného vzoru.
|
||
|
||
### Generované typované klienty
|
||
|
||
Dva typované klienty jsou automaticky vygenerovány pomocí `yarn twenty dev` a uloženy do `node_modules/twenty-sdk/clients` podle schématu vašeho pracovního prostoru:
|
||
|
||
* **`CoreApiClient`** — provádí dotazy na endpoint `/graphql` za účelem získání dat pracovního prostoru
|
||
* **`MetadataApiClient`** — odesílá dotazy na endpoint `/metadata` pro konfiguraci pracovního prostoru a nahrávání souborů
|
||
|
||
```typescript
|
||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
|
||
|
||
const client = new CoreApiClient();
|
||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||
|
||
const metadataClient = new MetadataApiClient();
|
||
const { currentWorkspace } = await metadataClient.query({ currentWorkspace: { id: true } });
|
||
```
|
||
|
||
`CoreApiClient` se automaticky znovu generuje pomocí `yarn twenty dev` kdykoli se změní vaše objekty nebo pole. `MetadataApiClient` je v SDK k dispozici již předem sestavený.
|
||
|
||
#### Běhové přihlašovací údaje v logických funkcích
|
||
|
||
Když vaše funkce běží na Twenty, platforma před spuštěním kódu vloží přihlašovací údaje jako proměnné prostředí:
|
||
|
||
* `TWENTY_API_URL`: Základní URL Twenty API, na které vaše aplikace cílí.
|
||
* `TWENTY_API_KEY`: Krátkodobý klíč s rozsahem omezeným na výchozí roli funkce vaší aplikace.
|
||
|
||
Poznámky:
|
||
|
||
* Není nutné předávat URL ani klíč API vygenerovanému klientovi. Za běhu čte `TWENTY_API_URL` a `TWENTY_API_KEY` z process.env.
|
||
* Oprávnění klíče API jsou určena rolí odkazovanou ve vašem `application-config.ts` prostřednictvím `defaultRoleUniversalIdentifier`. Toto je výchozí role používaná logickými funkcemi vaší aplikace.
|
||
* Aplikace mohou definovat role podle principu nejmenších oprávnění. Udělte pouze oprávnění, která vaše funkce potřebují, a poté nastavte `defaultRoleUniversalIdentifier` na univerzální identifikátor této role.
|
||
|
||
#### Nahrávání souborů
|
||
|
||
`MetadataApiClient` obsahuje metodu `uploadFile` pro připojování souborů k polím typu souboru u objektů ve vašem pracovním prostoru. Protože standardní klienti GraphQL nativně nepodporují nahrávání souborů pomocí multipart, klient poskytuje tuto speciální metodu, která interně implementuje [specifikaci multipart požadavků GraphQL](https://github.com/jaydenseric/graphql-multipart-request-spec).
|
||
|
||
```typescript
|
||
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
|
||
import * as fs from 'fs';
|
||
|
||
const metadataClient = new MetadataApiClient();
|
||
|
||
const fileBuffer = fs.readFileSync('./invoice.pdf');
|
||
|
||
const uploadedFile = await metadataClient.uploadFile(
|
||
fileBuffer, // file contents as a Buffer
|
||
'invoice.pdf', // filename
|
||
'application/pdf', // MIME type (defaults to 'application/octet-stream')
|
||
'58a0a314-d7ea-4865-9850-7fb84e72f30b', // field universal identifier
|
||
);
|
||
|
||
console.log(uploadedFile);
|
||
// { id: '...', path: '...', size: 12345, createdAt: '...', url: 'https://...' }
|
||
```
|
||
|
||
Signatura metody:
|
||
|
||
```typescript
|
||
uploadFile(
|
||
fileBuffer: Buffer,
|
||
filename: string,
|
||
contentType: string,
|
||
fieldMetadataUniversalIdentifier: string,
|
||
): Promise<{ id: string; path: string; size: number; createdAt: string; url: string }>
|
||
```
|
||
|
||
| Parametr | Typ | Popis |
|
||
| ---------------------------------- | -------- | --------------------------------------------------------------------------- |
|
||
| `fileBuffer` | `Buffer` | Surový obsah souboru |
|
||
| `filename` | `string` | Název souboru (používá se pro ukládání a zobrazení) |
|
||
| `contentType` | `string` | Typ MIME souboru (pokud je vynechán, výchozí je `application/octet-stream`) |
|
||
| `fieldMetadataUniversalIdentifier` | `string` | `universalIdentifier` pole typu souboru ve vašem objektu |
|
||
|
||
Hlavní body:
|
||
|
||
* Metoda `uploadFile` je k dispozici v `MetadataApiClient`, protože mutaci nahrávání obsluhuje endpoint `/metadata`.
|
||
* Používá `universalIdentifier` pole (nikoli jeho ID specifické pro pracovní prostor), takže váš kód pro nahrávání funguje ve všech pracovních prostorech, kde je vaše aplikace nainstalována — v souladu s tím, jak aplikace odkazují na pole všude jinde.
|
||
* Vrácená hodnota `url` je podepsaná adresa URL, kterou můžete použít k přístupu k nahranému souboru.
|
||
|
||
### Příklad Hello World
|
||
|
||
Prozkoumejte minimalistický end-to-end příklad, který demonstruje objekty, logické funkce, frontendové komponenty a více spouštěčů [zde](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/hello-world):
|
||
|
||
## Sestavení vaší aplikace
|
||
|
||
Jakmile vyvinete svou aplikaci pomocí `app:dev`, použijte `app:build` k jejímu zkompilování do distribučního balíčku.
|
||
|
||
```bash filename="Terminal"
|
||
# Sestavte aplikaci (výstup se uloží do .twenty/output/)
|
||
yarn twenty build
|
||
|
||
# Sestavte a vytvořte tarball (.tgz) pro distribuci
|
||
yarn twenty build --tarball
|
||
```
|
||
|
||
Proces sestavení:
|
||
|
||
1. **Parsuje a ověřuje manifest** — čte všechny entity `defineX()` z vašich zdrojových souborů a ověřuje strukturu manifestu.
|
||
2. **Kompiluje logické funkce a frontendové komponenty** — slučuje zdrojové soubory TypeScriptu do ESM souborů `.mjs` pomocí esbuild.
|
||
3. **Generuje kontrolní součty** — vypočítá MD5 hashe pro každý sestavený soubor, které jsou v manifestu uloženy jako `builtHandlerChecksum` / `builtComponentChecksum`.
|
||
4. **Vygeneruje typovaného klienta API** — prozkoumá schéma GraphQL a vygeneruje typované klienty `CoreApiClient` a `MetadataApiClient`.
|
||
5. **Spustí kontrolu typů TypeScriptu** — spustí `tsc --noEmit`, aby zachytil chyby typů před publikováním.
|
||
6. **Znovu sestaví s vygenerovaným klientem** — provede druhý průchod kompilace, aby byly zahrnuty typy vygenerovaného klienta.
|
||
7. **Volitelně vytvoří tarball** — pokud je předán `--tarball`, spustí `npm pack` a vytvoří soubor `.tgz` připravený k distribuci.
|
||
|
||
Výstup sestavení v `.twenty/output/` obsahuje:
|
||
|
||
```text
|
||
.twenty/output/
|
||
├── manifest.json # Manifest with checksums for all built files
|
||
├── package.json # Copied from app root
|
||
├── yarn.lock # Copied from app root
|
||
├── src/
|
||
│ ├── logic-functions/ # Compiled .mjs logic function files
|
||
│ └── front-components/ # Compiled .mjs front component files
|
||
├── public/ # Static assets (if any)
|
||
└── my-app-1.0.0.tgz # Only with --tarball flag
|
||
```
|
||
|
||
| Možnost | Popis |
|
||
| ----------- | ----------------------------------------------------- |
|
||
| `[appPath]` | Cesta k adresáři aplikace (výchozí: aktuální adresář) |
|
||
| `--tarball` | Také zabalí výstup do tarballu `.tgz` |
|
||
|
||
## Publikování vaší aplikace
|
||
|
||
Použijte `app:publish` k distribuci své aplikace — buď do registru npm, nebo přímo na server Twenty.
|
||
|
||
### Publikovat na npm (výchozí)
|
||
|
||
```bash filename="Terminal"
|
||
# Publikujte na npm (vyžaduje přihlášení k npm)
|
||
yarn twenty publish
|
||
|
||
# Publikujte s dist-tagem (např. beta, next)
|
||
yarn twenty publish --tag beta
|
||
```
|
||
|
||
Tímto se aplikace sestaví a spustí se `npm publish` z adresáře `.twenty/output/`. Publikovaný balíček pak může být nainstalován z tržiště Twenty jakýmkoli pracovním prostorem.
|
||
|
||
### Publikovat na server Twenty
|
||
|
||
```bash filename="Terminal"
|
||
# Publikujte přímo na server Twenty
|
||
yarn twenty publish --server https://app.twenty.com
|
||
```
|
||
|
||
Tímto se aplikace sestaví s tarballem, nahraje se na server pomocí GraphQL mutace `uploadAppTarball` a v jednom kroku se spustí instalace. To je užitečné pro soukromá nasazení nebo testování proti konkrétnímu serveru.
|
||
|
||
| Možnost | Popis |
|
||
| ----------------- | ------------------------------------------------------------------ |
|
||
| `[appPath]` | Cesta k adresáři aplikace (výchozí: aktuální adresář) |
|
||
| `--server <url>` | Publikovat na server Twenty místo npm |
|
||
| `--token <token>` | Autentizační token pro cílový server |
|
||
| `--tag <tag>` | npm dist-tag (např. `beta`, `next`) — pouze pro publikování na npm |
|
||
|
||
## Registrace aplikace
|
||
|
||
Než může být aplikace nainstalována v pracovním prostoru, musí být **zaregistrována**. Registrace je záznam metadat, který popisuje, odkud aplikace pochází a jak ji autentizovat. Ve většině případů to CLI zpracuje automaticky.
|
||
|
||
### Typy zdrojů
|
||
|
||
Každá registrace má **typ zdroje**, který určuje, jak se při instalaci získávají soubory aplikace:
|
||
|
||
| Typ zdroje | Jak se získávají soubory | Typický případ použití |
|
||
| ---------- | -------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
|
||
| `LOCAL` | Soubory jsou průběžně synchronizovány nástrojem CLI watcher v reálném čase — instalace se přeskočí | Vývoj s `app:dev` |
|
||
| `NPM` | Získáváno z registru npm prostřednictvím pole `sourcePackage` | Publikované aplikace na npm |
|
||
| `TARBALL` | Extrahováno z nahraného souboru `.tgz` uloženého na serveru | Soukromé aplikace publikované pomocí `--server` |
|
||
|
||
### Jak probíhá registrace
|
||
|
||
* **`app:dev`** — při prvním spuštění vývojového režimu pro pracovní prostor automaticky vytvoří registraci `LOCAL`.
|
||
* **`app:publish --server`** — nahraje tarball a vytvoří (nebo aktualizuje) registraci `TARBALL` a poté nainstaluje aplikaci.
|
||
* **tržiště npm** — registrace `NPM` se vytvářejí, když jsou aplikace synchronizovány z registru npm do katalogu tržiště Twenty.
|
||
* **GraphQL API** — registrace můžete vytvářet také programově pomocí mutace `createApplicationRegistration`.
|
||
|
||
### Registrace vs instalace
|
||
|
||
**Registrace** a **instalace** jsou odlišné pojmy:
|
||
|
||
* **Registrace** (`ApplicationRegistration`) je globální záznam metadat popisující aplikaci: její název, typ zdroje, přihlašovací údaje OAuth a stav zařazení na tržišti. Existuje nezávisle na jakémkoli pracovním prostoru.
|
||
* **Instalace** (`Application`) je instancí na úrovni pracovního prostoru. Když uživatel nainstaluje aplikaci, Twenty načte balíček ze zdroje uvedeného v registraci, zapíše sestavené soubory do úložiště a synchronizuje manifest (vytváření objektů, polí, logických funkcí atd.) v daném pracovním prostoru.
|
||
|
||
Jedna registrace může být nainstalována v mnoha pracovních prostorech. Každý pracovní prostor získá svou vlastní kopii souborů a datového modelu aplikace.
|
||
|
||
### Přihlašovací údaje OAuth
|
||
|
||
Každá registrace obsahuje přihlašovací údaje OAuth (`oAuthClientId` a `oAuthClientSecret`) vygenerované při vytvoření. Tyto údaje aplikace používá k autentizaci požadavků na API jménem uživatelů. Tajný klíč klienta je při vytvoření vrácen **pouze jednou** — uložte jej bezpečně. Později jej můžete rotovat prostřednictvím mutace `rotateApplicationRegistrationClientSecret`.
|
||
|
||
## Ruční nastavení (bez scaffolderu)
|
||
|
||
Ačkoli pro nejlepší začátky doporučujeme použít `create-twenty-app`, projekt můžete nastavit i ručně. Neinstalujte CLI globálně. Místo toho přidejte `twenty-sdk` jako lokální závislost a přidejte jeden skript do souboru package.json:
|
||
|
||
```bash filename="Terminal"
|
||
yarn add -D twenty-sdk
|
||
```
|
||
|
||
Poté přidejte skript `twenty`:
|
||
|
||
```json filename="package.json"
|
||
{
|
||
"scripts": {
|
||
"twenty": "twenty"
|
||
}
|
||
}
|
||
```
|
||
|
||
Nyní můžete spouštět všechny příkazy přes `yarn twenty <command>`, např. `yarn twenty dev`, `yarn twenty help` atd.
|
||
|
||
## Řešení potíží
|
||
|
||
* Chyby ověření: spusťte `yarn twenty auth:login` a ujistěte se, že váš klíč API má požadovaná oprávnění.
|
||
* Nelze se připojit k serveru: ověřte URL API a že je server Twenty dosažitelný.
|
||
* Typy nebo klient chybí nebo jsou zastaralé: restartujte `yarn twenty dev` — automaticky generuje typovaného klienta.
|
||
* Režim vývoje se nesynchronizuje: ujistěte se, že běží `yarn twenty dev` a že vaše prostředí změny neignoruje.
|
||
|
||
Kanál podpory na Discordu: https://discord.com/channels/1130383047699738754/1130386664812982322
|