Compare commits

..
Author SHA1 Message Date
Sonarly Claude Code 8cbe31bf34 Logic function creation crashes on unvalidated JSON source input
https://sonarly.com/issue/11005?type=bug

The `createOneLogicFunction` mutation crashes with a TypeError when the `source` field contains unexpected JSON property names (e.g. `code` instead of `sourceHandlerCode`), because the `source` field uses `graphqlTypeJson` with no nested validation.

Fix: Added `@ValidateNested()` and `@Type(() => LogicFunctionSourceInput)` decorators to the `source` field in `CreateLogicFunctionFromSourceInput`, matching the pattern already used in `UpdateLogicFunctionFromSourceInput`.

**What this fixes:** When a client sends a `source` object with incorrect field names (e.g. `{ code: "..." }` instead of `{ sourceHandlerCode: "...", toolInputSchema: {...}, handlerName: "main" }`), the `ResolverValidationPipe` now validates the nested object's fields using the `LogicFunctionSourceInput` class validators. The request is rejected with a clear `UserInputError` (e.g. "sourceHandlerCode must be a string") instead of crashing deep in `FileStorageService.writeFile()` with `TypeError: Cannot read properties of undefined (reading 'length')`.

**How it works:**
1. `ResolverValidationPipe` calls `plainToInstance(CreateLogicFunctionFromSourceInput, value)`
2. `@Type(() => LogicFunctionSourceInput)` tells `class-transformer` to instantiate the `source` property as a `LogicFunctionSourceInput` instance
3. `@ValidateNested()` tells `class-validator` to validate the nested instance
4. `LogicFunctionSourceInput` has `@IsString()` on `sourceHandlerCode` (non-nullable), so missing/wrong fields are caught
5. `@IsOptional()` ensures that omitting `source` entirely is still valid

This also prevents the secondary issue of orphaned files on S3 — since the request is rejected before `writeFile()` is called, no partial uploads occur.
2026-03-08 01:06:29 +00:00
Sonarly Claude Code 4313ff8e0e UpdateWorkspace fails when removing auto-detected logo before workspace activation
https://sonarly.com/issue/5651?type=bug

When a user signs up with a work email and tries to remove the auto-detected logo on the workspace creation page, `deleteCorePicture` fails because the Twenty Standard Application hasn't been created yet (only created during workspace activation).

Fix: **Problem**: `deleteCorePicture` called `findWorkspaceTwentyStandardAndCustomApplicationOrThrow` which requires **both** the Twenty Standard Application and the custom application to exist. However, `deleteCorePicture` only needs the custom application's `universalIdentifier` for the file storage delete. The standard application doesn't exist yet for workspaces in `PENDING_CREATION` state (it's only created during workspace activation).

**Fix**: Added a new `findWorkspaceCustomFlatApplicationOrThrow` method to `ApplicationService` that only looks up the workspace's custom application from the cache — without requiring the standard application. Changed `deleteCorePicture` to use this new method instead.

The custom application IS created during sign-up (in `sign-in-up.service.ts`), so it exists for workspaces in any activation state. This fixes the error for users who try to remove their auto-detected logo on the workspace creation page before activating.
2026-03-07 22:22:03 +00:00
Sonarly Claude Code 1ad1685d53 Gmail "Precondition check failed" error not recognized as service-disabled
https://sonarly.com/issue/5372?type=bug

The `isServiceNotEnabledError()` method in v1.18.1 only matches Gmail API errors containing "service not enabled" but not the "Precondition check failed." variant, causing the Google account connection flow to fail entirely for users whose Google Workspace has Gmail disabled.

Fix: **Simplified `isServiceNotEnabledError` to match on `reason` field only, removing fragile message substring matching.**

The root cause was that the `isServiceNotEnabledError()` method relied on substring matching against Google API error messages (`"service not enabled"`, `"precondition check failed"`). This is fragile — Google can return different message variants for the same condition.

The previous fix (commit `e7f958c9cf`, not yet deployed) added a second substring check for `"precondition check failed"`. This change goes further by removing ALL message substring checks and relying solely on `firstError.reason === 'failedPrecondition'`, which is the structured/stable field in the Google API error response.

This handles:
- `"Mail service not enabled"` (original case)
- `"Precondition check failed."` (the Sentry error case)
- Any future `failedPrecondition` message variants

The existing test suite validates all three scenarios pass correctly.
2026-03-07 15:52:37 +00:00
Sonarly Claude Code bf6c1e2cc0 chore: additional changes for FK violation during calendar event association INS 2026-03-07 15:11:40 +00:00
Sonarly Claude Code 6ac2f09a53 chore: improve monitoring for FK violation during calendar event association INS
**Problem**: Commit `d4303469ff5` replaced all PostgreSQL error messages with the generic string `"Data validation error."` in `computeTwentyORMException`. This made it impossible to distinguish between different types of database errors (FK violations, check violations, not-null violations, etc.) in Sentry alerts and logs. The error code is the only diagnostic signal, but it was only stored in the `PostgresException.code` property — not visible in the error message that appears in Sentry.

**Fix**: Changed the generic message from `"Data validation error."` to `"Data validation error (code: ${errorCode})."` which includes the specific PostgreSQL error code (e.g., `23503` for FK violations, `23505` for unique violations). This preserves the sanitization intent (no SQL, table names, or column names are exposed) while providing enough diagnostic information to identify the specific database constraint that was violated. For example, the Sentry error will now show `"Unknown error ... : Data validation error (code: 23503)."` instead of the uninformative `"Data validation error."`.
2026-03-07 15:11:38 +00:00
Sonarly Claude Code 5843d1d4cd FK violation during calendar event association INSERT due to concurrent cleanup race
https://sonarly.com/issue/3806?type=bug

A race condition between concurrent calendar sync jobs causes a foreign key violation when inserting `calendarChannelEventAssociation` records. A concurrent hard-delete by the calendar event cleaner removes referenced `calendarEvent` rows between the find and insert within the same transaction, causing a PostgreSQL FK violation (23503) wrapped as a generic "Data validation error."

Fix: **Problem**: When a PostgreSQL foreign key violation (code `23503`) occurs during `calendarChannelEventAssociation` INSERT — caused by a race condition with the concurrent calendar event cleaner hard-deleting referenced events — the `PostgresException` with code `'23503'` doesn't match any recognized exception code in the calendar error handler's switch statement. It falls to the `default` case, which permanently marks the calendar channel as `FAILED_UNKNOWN`, stopping sync entirely.

**Fix**: Added `POSTGRESQL_ERROR_CODES.FOREIGN_KEY_VIOLATION` (`'23503'`) as a case in the `handleDriverException` switch statement, routing it to `handleTemporaryException`. This treats FK violations as transient errors (which they are — caused by a concurrent cleanup race), enabling retry with exponential backoff via the existing `throttleFailureCount` mechanism. On retry, the `find()` query will see the current database state and the race condition is unlikely to recur.

This follows the existing pattern where `TwentyORMExceptionCode.QUERY_READ_TIMEOUT` is also routed to `handleTemporaryException`.
2026-03-07 15:11:38 +00:00
Sonarly Claude Code 1b7e41f650 Empty filter object crashes query parser when workspace member state is not loaded
https://sonarly.com/issue/7575?type=bug

When `currentWorkspaceMember` is null/undefined during page initialization, the frontend sends `{ accountOwnerId: {} }` as a filter (because `JSON.stringify` drops the `undefined` `eq` value), which crashes the server-side query parser that assumes filter objects always contain at least one operator entry.

Fix: ## Changes

### Backend: Defensive guard at crash site (1 file)

**`graphql-query-filter-field.parser.ts`** — Added a guard before the destructuring on line 72 that checks `filterEntries.length !== 1` and throws a proper `GraphqlQueryRunnerException` instead of letting an unhandled `TypeError` crash the process. This is consistent with the validation pattern already established in the newer `validateAndTransformOperatorAndValue` utility.

```typescript
// Before: crashes with TypeError when filterValue is {}
const [[operator, value]] = Object.entries(filterValue);

// After: explicit validation with descriptive error
const filterEntries = Object.entries(filterValue);
if (filterEntries.length !== 1) {
  throw new GraphqlQueryRunnerException(
    `Filter for field "${key}" must have exactly one operator, received ${filterEntries.length}`,
    GraphqlQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
    { userFriendlyMessage: msg`Invalid filter value for field "${key}"` },
  );
}
const [[operator, value]] = filterEntries;
```

### Frontend: Prevent query with invalid filter (3 files)

**`SettingsAccounts.tsx`**, **`SettingsAccountsMessageChannelsContainer.tsx`**, **`SettingsAccountsCalendarChannelsContainer.tsx`** — Added `skip: !currentWorkspaceMember?.id` to each `useFindManyRecords` call. This prevents the GraphQL query from firing when `currentWorkspaceMember` is null/undefined (e.g., during initial page load), which would otherwise serialize `{ eq: undefined }` as `{}` after JSON stringification and send an empty filter object to the server.
2026-03-07 12:08:04 +00:00
Sonarly Claude Code 0df60256ba chore: improve monitoring for Race condition: debounced save fires while workflo
Applied the same `isReady` guard to `useUpdateWorkflowVersionTrigger`, which has the identical race condition pattern — it's called from debounced trigger save callbacks that can fire during transient workflow data loading states. Without this fix, the same "Failed to get updatable workflow version" error would generate noisy Sentry events from trigger editing flows as well.

The code fix itself is the primary monitoring improvement: by preventing the throw during expected transient states, it eliminates the noisy Sentry error events (currently tagged `handled: yes, mechanism: generic`) that provide no actionable signal. The remaining throw in `getUpdatableWorkflowVersion()` still fires for genuine configuration errors (e.g., missing `workflowVisualizerWorkflowId`), preserving alerting for real bugs.
2026-03-07 10:15:04 +00:00
Sonarly Claude Code de31a2822c Race condition: debounced save fires while workflow data is transiently undefined during refetch
https://sonarly.com/issue/5150?type=bug

The `useGetUpdatableWorkflowVersionOrThrow` hook throws "Failed to get updatable workflow version" when a debounced save callback fires during a transient window where `useWorkflowWithCurrentVersion` returns `undefined` — typically caused by an SSE-triggered workflow refetch that changes the version ID and causes a cache miss on the second sequential Apollo query.

Fix: Added an `isReady` boolean to the return value of `useGetUpdatableWorkflowVersionOrThrow` that callers can check synchronously before calling the async `getUpdatableWorkflowVersion()`. This flag is `true` only when both `workflowVisualizerWorkflowId` and `workflow` are defined (i.e., not in a transient loading/refetch state).

Updated `useUpdateStep` and `useUpdateWorkflowVersionTrigger` — the two hooks called from debounced save callbacks — to check `isReady` and return early when the workflow data is temporarily unavailable. This prevents the error from being thrown during the transient window when `useWorkflowWithCurrentVersion` returns `undefined` (e.g., after an SSE-triggered refetch changes the version ID and the second Apollo query has a cache miss).

The `isReady` flag is captured in the same React render cycle as the `workflow` value, so they're always consistent in the closure. When the debounced callback fires, `useDebouncedCallback`'s ref points to the latest callback which captures the latest `isReady` value. If `isReady` is `false`, the save is silently skipped. Once the data loads and the component re-renders with `isReady = true`, the next debounced save succeeds.
2026-03-07 10:15:03 +00:00
573 changed files with 17528 additions and 46953 deletions
+1 -1
View File
@@ -65,7 +65,7 @@ jobs:
key: ${{ env.STORYBOOK_BUILD_CACHE_KEY_FOR_SAVE_ACTION }}
front-sb-test:
timeout-minutes: 30
runs-on: ubuntu-latest
runs-on: ubuntu-latest-4-cores
needs: front-sb-build
strategy:
fail-fast: false
+1 -1
View File
@@ -51,7 +51,7 @@ jobs:
tasks: ${{ matrix.task }}
sdk-e2e-test:
timeout-minutes: 30
runs-on: ubuntu-latest
runs-on: ubuntu-latest-4-cores
needs: [changed-files-check, sdk-test]
if: needs.changed-files-check.outputs.any_changed == 'true'
services:
+8 -25
View File
@@ -25,7 +25,6 @@ jobs:
packages/twenty-server/**
packages/twenty-front/src/generated/**
packages/twenty-front/src/generated-metadata/**
packages/twenty-sdk/src/clients/generated/metadata/**
packages/twenty-emails/**
packages/twenty-shared/**
@@ -33,7 +32,7 @@ jobs:
needs: changed-files-check
if: needs.changed-files-check.outputs.any_changed == 'true'
timeout-minutes: 30
runs-on: ubuntu-latest
runs-on: ubuntu-latest-4-cores
steps:
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@v4
@@ -61,7 +60,7 @@ jobs:
needs: changed-files-check
if: needs.changed-files-check.outputs.any_changed == 'true'
timeout-minutes: 30
runs-on: ubuntu-latest
runs-on: ubuntu-latest-4-cores
steps:
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@v4
@@ -80,7 +79,7 @@ jobs:
server-validation:
needs: server-build
timeout-minutes: 30
runs-on: ubuntu-latest
runs-on: ubuntu-latest-4-cores
services:
postgres:
image: twentycrm/twenty-postgres-spilo
@@ -159,10 +158,8 @@ jobs:
exit 1
fi
- name: Check for Pending Code Generation
- name: GraphQL / Check for Pending Generation
run: |
HAS_ERRORS=false
npx nx run twenty-front:graphql:generate
npx nx run twenty-front:graphql:generate --configuration=metadata
@@ -174,29 +171,15 @@ jobs:
git diff -- packages/twenty-front/src/generated packages/twenty-front/src/generated-metadata
echo "==================================================="
echo ""
HAS_ERRORS=true
fi
npx nx run twenty-sdk:generate-metadata-client
if ! git diff --quiet -- packages/twenty-sdk/src/clients/generated/metadata; then
echo "::error::SDK metadata client changes detected. Please run 'npx nx run twenty-sdk:generate-metadata-client' and commit the changes."
echo "Please run 'npx nx run twenty-front:graphql:generate' and 'npx nx run twenty-front:graphql:generate --configuration=metadata' and commit the changes."
echo ""
echo "The following SDK metadata client changes were detected:"
echo "==================================================="
git diff -- packages/twenty-sdk/src/clients/generated/metadata
echo "==================================================="
echo ""
HAS_ERRORS=true
fi
if [ "$HAS_ERRORS" = true ]; then
exit 1
fi
server-test:
needs: server-build
timeout-minutes: 30
runs-on: ubuntu-latest
runs-on: ubuntu-latest-4-cores
steps:
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@v4
@@ -218,7 +201,7 @@ jobs:
server-integration-test:
timeout-minutes: 30
runs-on: ubuntu-latest
runs-on: ubuntu-latest-4-cores
needs: server-build
strategy:
fail-fast: false
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
needs: changed-files-check
if: needs.changed-files-check.outputs.any_changed == 'true'
timeout-minutes: 30
runs-on: ubuntu-latest
runs-on: ubuntu-latest-4-cores
services:
postgres:
image: twentycrm/twenty-postgres-spilo
+2 -2
View File
@@ -43,7 +43,7 @@ yarn twenty auth:login
yarn twenty entity:add
# Start dev mode: watches, builds, and syncs local changes to your workspace
# (also auto-generates typed CoreApiClient MetadataApiClient ships pre-built with the SDK — both available via `twenty-sdk/clients`)
# (also auto-generates typed API clients — CoreApiClient and MetadataApiClient — in node_modules/twenty-sdk/generated)
yarn twenty app:dev
# Watch your application's function logs
@@ -107,7 +107,7 @@ npx create-twenty-app@latest my-app -m
- Use `yarn twenty auth:login` to authenticate with your Twenty workspace.
- Explore the generated project and add your first entity with `yarn twenty entity:add` (logic functions, front components, objects, roles, views, navigation menu items, skills).
- Use `yarn twenty app:dev` while you iterate — it watches, builds, and syncs changes to your workspace in real time.
- `CoreApiClient` (for workspace data via `/graphql`) is auto-generated by `yarn twenty app:dev`. `MetadataApiClient` (for workspace configuration and file uploads via `/metadata`) ships pre-built with the SDK. Both are available via `import { CoreApiClient, MetadataApiClient } from 'twenty-sdk/clients'`.
- Two typed API clients are autogenerated by `yarn twenty app:dev` and stored in `node_modules/twenty-sdk/generated`: `CoreApiClient` (for workspace data via `/graphql`) and `MetadataApiClient` (for workspace configuration and file uploads via `/metadata`).
## Publish your application
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "create-twenty-app",
"version": "0.7.0-canary.0",
"version": "0.6.4",
"description": "Command-line interface to create Twenty application",
"main": "dist/cli.cjs",
"bin": "dist/cli.cjs",
@@ -113,7 +113,6 @@ export class CreateAppCommand {
includeExampleView: false,
includeExampleNavigationMenuItem: false,
includeExampleSkill: false,
includeExampleAgent: false,
includeExampleIntegrationTest: false,
};
}
@@ -127,7 +126,6 @@ export class CreateAppCommand {
includeExampleNavigationMenuItem: true,
includeExampleSkill: true,
includeExampleIntegrationTest: true,
includeExampleAgent: true,
};
}
@@ -8,6 +8,5 @@ export type ExampleOptions = {
includeExampleView: boolean;
includeExampleNavigationMenuItem: boolean;
includeExampleSkill: boolean;
includeExampleAgent: boolean;
includeExampleIntegrationTest: boolean;
};
@@ -25,7 +25,6 @@ const ALL_EXAMPLES: ExampleOptions = {
includeExampleView: true,
includeExampleNavigationMenuItem: true,
includeExampleSkill: true,
includeExampleAgent: true,
includeExampleIntegrationTest: true,
};
@@ -42,7 +41,6 @@ const NO_EXAMPLES: ExampleOptions = {
includeExampleObject: false,
includeExampleField: false,
includeExampleSkill: false,
includeExampleAgent: false,
includeExampleLogicFunction: false,
includeExampleFrontComponent: false,
includeExampleView: false,
@@ -478,7 +476,6 @@ describe('copyBaseApplicationProject', () => {
includeExampleObject: false,
includeExampleField: false,
includeExampleSkill: false,
includeExampleAgent: false,
includeExampleLogicFunction: false,
includeExampleFrontComponent: true,
includeExampleView: false,
@@ -516,7 +513,6 @@ describe('copyBaseApplicationProject', () => {
exampleOptions: {
includeExampleObject: false,
includeExampleSkill: false,
includeExampleAgent: false,
includeExampleField: false,
includeExampleLogicFunction: true,
includeExampleFrontComponent: false,
@@ -47,17 +47,18 @@ describe('scaffoldIntegrationTest', () => {
const content = await fs.readFile(testPath, 'utf8');
expect(content).toContain(
"import { appBuild, appUninstall } from 'twenty-sdk/cli'",
"import { appGenerateClient, appUninstall } from 'twenty-sdk/cli'",
);
expect(content).toContain(
"import { MetadataApiClient } from 'twenty-sdk/clients'",
"import { MetadataApiClient } from 'twenty-sdk/generated'",
);
expect(content).toContain(
"import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application-config'",
);
expect(content).toContain('appBuild');
expect(content).toContain('TWENTY_TEST_API_KEY');
expect(content).toContain('assertServerIsReachable');
expect(content).toContain('appGenerateClient');
expect(content).toContain('appUninstall');
expect(content).toContain('new MetadataApiClient()');
expect(content).toContain('findManyApplications');
expect(content).toContain('APPLICATION_UNIVERSAL_IDENTIFIER');
});
@@ -83,8 +84,7 @@ describe('scaffoldIntegrationTest', () => {
expect(content).toContain('.twenty-sdk-test');
expect(content).toContain('config.json');
expect(content).toContain('process.env.TWENTY_API_URL');
expect(content).toContain('process.env.TWENTY_API_KEY');
expect(content).toContain('assertServerIsReachable');
expect(content).toContain('process.env.TWENTY_TEST_API_KEY');
});
});
@@ -101,8 +101,7 @@ describe('scaffoldIntegrationTest', () => {
const content = await fs.readFile(vitestConfigPath, 'utf8');
expect(content).toContain('TWENTY_API_KEY');
expect(content).not.toContain('TWENTY_TEST_API_KEY');
expect(content).toContain('TWENTY_TEST_API_KEY');
expect(content).toContain('TWENTY_API_URL');
expect(content).toContain('setup-test.ts');
expect(content).toContain('tsconfig.spec.json');
@@ -103,14 +103,6 @@ export const copyBaseApplicationProject = async ({
});
}
if (exampleOptions.includeExampleAgent) {
await createExampleAgent({
appDirectory: sourceFolderPath,
fileFolder: 'agents',
fileName: 'example-agent.ts',
});
}
if (exampleOptions.includeExampleIntegrationTest) {
await scaffoldIntegrationTest({
appDirectory,
@@ -527,36 +519,6 @@ export default defineSkill({
await fs.writeFile(join(appDirectory, fileFolder ?? '', fileName), content);
};
const createExampleAgent = async ({
appDirectory,
fileFolder,
fileName,
}: {
appDirectory: string;
fileFolder?: string;
fileName: string;
}) => {
const universalIdentifier = v4();
const content = `import { defineAgent } from 'twenty-sdk';
export const EXAMPLE_AGENT_UNIVERSAL_IDENTIFIER =
'${universalIdentifier}';
export default defineAgent({
universalIdentifier: EXAMPLE_AGENT_UNIVERSAL_IDENTIFIER,
name: 'example-agent',
label: 'Example Agent',
description: 'A sample AI agent for your application',
icon: 'IconRobot',
prompt: 'You are a helpful assistant. Help users with their questions and tasks.',
});
`;
await fs.ensureDir(join(appDirectory, fileFolder ?? ''));
await fs.writeFile(join(appDirectory, fileFolder ?? '', fileName), content);
};
const createApplicationConfig = async ({
displayName,
description,
@@ -45,7 +45,7 @@ export default defineConfig({
setupFiles: ['src/__tests__/setup-test.ts'],
env: {
TWENTY_API_URL: 'http://localhost:3000',
TWENTY_API_KEY:
TWENTY_TEST_API_KEY:
'${SEED_API_KEY}',
},
},
@@ -93,36 +93,16 @@ import * as os from 'os';
import * as path from 'path';
import { beforeAll } from 'vitest';
const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:3000';
const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test');
const assertServerIsReachable = async () => {
let response: Response;
try {
response = await fetch(\`\${TWENTY_API_URL}/healthz\`);
} catch {
throw new Error(
\`Twenty server is not reachable at \${TWENTY_API_URL}. \` +
'Make sure the server is running before executing integration tests.',
);
}
if (!response.ok) {
throw new Error(\`Server at \${TWENTY_API_URL} returned \${response.status}\`);
}
};
beforeAll(async () => {
await assertServerIsReachable();
beforeAll(() => {
fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true });
const configFile = {
profiles: {
default: {
apiUrl: process.env.TWENTY_API_URL,
apiKey: process.env.TWENTY_API_KEY,
apiKey: process.env.TWENTY_TEST_API_KEY,
},
},
};
@@ -148,24 +128,44 @@ const createIntegrationTest = async ({
fileName: string;
}) => {
const content = `import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application-config';
import { appBuild, appUninstall } from 'twenty-sdk/cli';
import { MetadataApiClient } from 'twenty-sdk/clients';
import { appGenerateClient, appUninstall } from 'twenty-sdk/cli';
import { MetadataApiClient } from 'twenty-sdk/generated';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
const APP_PATH = process.cwd();
const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:3000';
const assertServerIsReachable = async () => {
let response: Response;
try {
response = await fetch(\`\${TWENTY_API_URL}/healthz\`);
} catch {
throw new Error(
\`Twenty server is not reachable at \${TWENTY_API_URL}. \` +
'Make sure the server is running before executing integration tests.',
);
}
if (!response.ok) {
throw new Error(\`Server at \${TWENTY_API_URL} returned \${response.status}\`);
}
};
describe('App installation', () => {
let appInstalled = false;
beforeAll(async () => {
const buildResult = await appBuild({
await assertServerIsReachable();
const generateResult = await appGenerateClient({
appPath: APP_PATH,
onProgress: (message: string) => console.log(\`[build] \${message}\`),
onProgress: (message: string) => console.log(\`[generate-client] \${message}\`),
});
if (!buildResult.success) {
if (!generateResult.success) {
throw new Error(
\`Build failed: \${buildResult.error?.message ?? 'Unknown error'}\`,
\`Client generation failed: \${generateResult.error?.message ?? 'Unknown error'}\`,
);
}
@@ -187,7 +187,20 @@ describe('App installation', () => {
});
it('should find the installed app in the applications list', async () => {
const metadataClient = new MetadataApiClient();
const apiKey = process.env.TWENTY_TEST_API_KEY;
if (!apiKey) {
throw new Error(
'No API key found. Set TWENTY_TEST_API_KEY in your vitest config env.',
);
}
const metadataClient = new MetadataApiClient({
url: \`\${TWENTY_API_URL}/metadata\`,
headers: {
Authorization: \`Bearer \${apiKey}\`,
},
});
const result = await metadataClient.query({
findManyApplications: {
+29 -10
View File
@@ -1,19 +1,38 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["typescript"],
"plugins": ["typescript", "import", "unicorn"],
"categories": {
"correctness": "off"
},
"ignorePatterns": ["node_modules", "dist"],
"ignorePatterns": ["node_modules"],
"rules": {
"func-style": ["error", "declaration", { "allowArrowFunctions": true }],
"no-console": ["warn", { "allow": ["group", "groupCollapsed", "groupEnd"] }],
"no-control-regex": "off",
"no-debugger": "error",
"no-duplicate-imports": "error",
"no-undef": "off",
"no-unused-vars": "off",
"typescript/no-unused-vars": [
"warn",
{
"argsIgnorePattern": "^_"
}
],
"typescript/no-explicit-any": "off"
"no-redeclare": "off",
"import/no-duplicates": "error",
"typescript/no-redeclare": "error",
"typescript/ban-ts-comment": "error",
"typescript/consistent-type-imports": ["error", {
"prefer": "type-imports",
"fixStyle": "inline-type-imports"
}],
"typescript/explicit-function-return-type": "off",
"typescript/explicit-module-boundary-types": "off",
"typescript/no-empty-object-type": ["error", {
"allowInterfaces": "with-single-extends"
}],
"typescript/no-empty-function": "off",
"typescript/no-explicit-any": "off",
"typescript/no-unused-vars": ["warn", {
"vars": "all",
"varsIgnorePattern": "^_",
"args": "after-used",
"argsIgnorePattern": "^_"
}]
}
}
-1
View File
@@ -4,7 +4,6 @@
- Rich app example: https://github.com/twentyhq/twenty/tree/main/packages/twenty-sdk/src/cli/__tests__/apps/rich-app
## UUID requirement
- All generated UUIDs must be valid UUID v4.
## Common Pitfalls
@@ -1,6 +1,6 @@
{
"name": "hello-world",
"version": "0.1.0",
"name": "@twentyhq/hello-world",
"version": "0.2.2",
"license": "MIT",
"engines": {
"node": "^24.5.0",
@@ -20,7 +20,7 @@
"@types/react": "^18.2.0",
"oxlint": "^0.16.0",
"react": "^18.2.0",
"twenty-sdk": "0.6.4",
"twenty-sdk": "0.6.3",
"typescript": "^5.9.3",
"vite-tsconfig-paths": "^4.2.1",
"vitest": "^3.1.1"
@@ -1,22 +1,43 @@
import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application-config';
import { appBuild, appUninstall } from 'twenty-sdk/cli';
import { MetadataApiClient } from 'twenty-sdk/clients';
import { appGenerateClient, appUninstall } from 'twenty-sdk/cli';
import { MetadataApiClient } from 'twenty-sdk/generated';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
const APP_PATH = process.cwd();
const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:3000';
const assertServerIsReachable = async () => {
let response: Response;
try {
response = await fetch(`${TWENTY_API_URL}/healthz`);
} catch {
throw new Error(
`Twenty server is not reachable at ${TWENTY_API_URL}. ` +
'Make sure the server is running before executing integration tests.',
);
}
if (!response.ok) {
throw new Error(`Server at ${TWENTY_API_URL} returned ${response.status}`);
}
};
describe('App installation', () => {
let appInstalled = false;
beforeAll(async () => {
const buildResult = await appBuild({
await assertServerIsReachable();
const generateResult = await appGenerateClient({
appPath: APP_PATH,
onProgress: (message: string) => console.log(`[build] ${message}`),
onProgress: (message: string) =>
console.log(`[generate-client] ${message}`),
});
if (!buildResult.success) {
if (!generateResult.success) {
throw new Error(
`Build failed: ${buildResult.error?.message ?? 'Unknown error'}`,
`Client generation failed: ${generateResult.error?.message ?? 'Unknown error'}`,
);
}
@@ -38,7 +59,20 @@ describe('App installation', () => {
});
it('should find the installed app in the applications list', async () => {
const metadataClient = new MetadataApiClient();
const apiKey = process.env.TWENTY_TEST_API_KEY;
if (!apiKey) {
throw new Error(
'No API key found. Set TWENTY_TEST_API_KEY in your vitest config env.',
);
}
const metadataClient = new MetadataApiClient({
url: `${TWENTY_API_URL}/metadata`,
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
const result = await metadataClient.query({
findManyApplications: {
@@ -3,36 +3,16 @@ import * as os from 'os';
import * as path from 'path';
import { beforeAll } from 'vitest';
const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:3000';
const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test');
const assertServerIsReachable = async () => {
let response: Response;
try {
response = await fetch(`${TWENTY_API_URL}/healthz`);
} catch {
throw new Error(
`Twenty server is not reachable at ${TWENTY_API_URL}. ` +
'Make sure the server is running before executing integration tests.',
);
}
if (!response.ok) {
throw new Error(`Server at ${TWENTY_API_URL} returned ${response.status}`);
}
};
beforeAll(async () => {
await assertServerIsReachable();
beforeAll(() => {
fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true });
const configFile = {
profiles: {
default: {
apiUrl: process.env.TWENTY_API_URL,
apiKey: process.env.TWENTY_API_KEY,
apiKey: process.env.TWENTY_TEST_API_KEY,
},
},
};
@@ -2,7 +2,7 @@ import { defineApplication } from 'twenty-sdk';
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role';
export const APPLICATION_UNIVERSAL_IDENTIFIER =
'6563e091-9f5b-4026-a3ea-7e3b3d09e218';
'1badae7c-8a42-4dea-b4b8-3c56e77c2f9a';
export default defineApplication({
universalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
@@ -3,7 +3,7 @@ import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/objects/example-object'
export default defineField({
objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER,
universalIdentifier: '770d32c2-cf12-4ab2-b66d-73f92dc239b5',
universalIdentifier: '2c503a0d-36c9-49ec-b82f-4fafe0eb6f47',
type: FieldType.NUMBER,
name: 'priority',
label: 'Priority',
@@ -10,7 +10,7 @@ export const HelloWorld = () => {
};
export default defineFrontComponent({
universalIdentifier: 'd371f098-5b2c-42f0-898d-94459f1ee337',
universalIdentifier: '26c17445-fbfb-4b34-99d6-f461e734ca97',
name: 'hello-world-front-component',
description: 'A sample front component',
component: HelloWorld,
@@ -5,7 +5,7 @@ const handler = async (): Promise<{ message: string }> => {
};
export default defineLogicFunction({
universalIdentifier: '2baa26eb-9aaf-4856-a4f4-30d6fd6480ee',
universalIdentifier: '4f0b7137-1399-4e50-ac00-3c3bb2555c38',
name: 'hello-world-logic-function',
description: 'A simple logic function',
timeoutSeconds: 5,
@@ -5,7 +5,7 @@ const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
};
export default definePostInstallLogicFunction({
universalIdentifier: '7a3f4684-51db-494d-833b-a747a3b90507',
universalIdentifier: 'c1410017-8536-42aa-a188-4bfc5a1c3dae',
name: 'post-install',
description: 'Runs after installation to set up the application.',
timeoutSeconds: 300,
@@ -5,7 +5,7 @@ const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
};
export default definePreInstallLogicFunction({
universalIdentifier: '1272ffdb-8e2f-492c-ab37-66c2b97e9c23',
universalIdentifier: '68d005d4-1110-4fa0-8227-71e06d6b9f30',
name: 'pre-install',
description: 'Runs before installation to prepare the application.',
timeoutSeconds: 300,
@@ -1,11 +1,14 @@
import { defineNavigationMenuItem } from 'twenty-sdk';
import { EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER } from 'src/views/example-view';
export default defineNavigationMenuItem({
universalIdentifier: '10f90627-e9c2-44b7-9742-bed77e3d1b17',
universalIdentifier: '574a895f-1511-4b38-9d28-d6b8436738ff',
name: 'example-navigation-menu-item',
icon: 'IconList',
color: 'blue',
position: 0,
viewUniversalIdentifier: EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER,
// Link to a view:
// viewUniversalIdentifier: '...',
// Or link to an object:
// targetObjectUniversalIdentifier: '...',
// Or link to an external URL:
// link: 'https://example.com',
});
@@ -1,10 +1,10 @@
import { defineObject, FieldType } from 'twenty-sdk';
export const EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER =
'dfd43356-39b3-4b55-b4a7-279bec689928';
'b75cfe84-18ce-47da-812a-53e25ee094af';
export const NAME_FIELD_UNIVERSAL_IDENTIFIER =
'd2d7f6cd-33f6-456f-bf00-17adeca926ba';
'6ab9c690-06ce-455e-a2c9-8067a9747f96';
export default defineObject({
universalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER,
@@ -1,7 +1,7 @@
import { defineRole } from 'twenty-sdk';
export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER =
'9238bc7b-d38f-4a1c-9d19-31ab7bc67a2f';
'f14afc30-f2fa-4f70-9b12-903c5f852225';
export default defineRole({
universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
@@ -1,7 +1,7 @@
import { defineSkill } from 'twenty-sdk';
export const EXAMPLE_SKILL_UNIVERSAL_IDENTIFIER =
'd0940029-9d3c-40be-903a-52d65393028f';
'4f00dd76-c07b-4d55-a43a-7f17e7f6440a';
export default defineSkill({
universalIdentifier: EXAMPLE_SKILL_UNIVERSAL_IDENTIFIER,
@@ -1,22 +1,10 @@
import { defineView, ViewKey } from 'twenty-sdk';
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER, NAME_FIELD_UNIVERSAL_IDENTIFIER } from 'src/objects/example-object';
export const EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER = 'e004df40-29f3-47ba-b39d-d3a5c444367a';
import { defineView } from 'twenty-sdk';
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/objects/example-object';
export default defineView({
universalIdentifier: EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER,
name: 'All example items',
universalIdentifier: 'e574b32c-c058-492a-8a5c-780b844a8735',
name: 'example-view',
objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER,
icon: 'IconList',
key: ViewKey.INDEX,
position: 0,
fields: [
{
universalIdentifier: '496c40c2-5766-419c-93bf-20fdad3f34bb',
fieldMetadataUniversalIdentifier: NAME_FIELD_UNIVERSAL_IDENTIFIER,
position: 0,
isVisible: true,
size: 200,
},
],
});
@@ -15,7 +15,7 @@ export default defineConfig({
setupFiles: ['src/__tests__/setup-test.ts'],
env: {
TWENTY_API_URL: 'http://localhost:3000',
TWENTY_API_KEY:
TWENTY_TEST_API_KEY:
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC1lNmI1LTQ2ODAtOGEzMi1iODIwOTczNzE1NmIiLCJ1c2VySWQiOiIyMDIwMjAyMC1lNmI1LTQ2ODAtOGEzMi1iODIwOTczNzE1NmIiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtNDYzZi00MzViLTgyOGMtMTA3ZTAwN2EyNzExIiwidXNlcldvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMWU3Yy00M2Q5LWE1ZGItNjg1YjUwNjlkODE2IiwidHlwZSI6IkFDQ0VTUyIsImF1dGhQcm92aWRlciI6InBhc3N3b3JkIiwiaWF0IjoxNzUxMjgxNzA0LCJleHAiOjIwNjY4NTc3MDR9.HMGqCsVlOAPVUBhKSGlD1X86VoHKt4LIUtET3CGIdik',
},
},
File diff suppressed because it is too large Load Diff
@@ -14,7 +14,7 @@ Apps let you build and manage Twenty customizations **as code**. Instead of conf
**What you can do today:**
- Define custom objects and fields as code (managed data model)
- Build logic functions with custom triggers
- Define skills and agents for AI
- Define skills for AI agents
- Deploy the same app across multiple workspaces
## Prerequisites
@@ -38,7 +38,7 @@ yarn twenty app:dev
The scaffolder supports two modes for controlling which example files are included:
```bash filename="Terminal"
# Default (exhaustive): all examples (object, field, logic function, front component, view, navigation menu item, skill, agent)
# Default (exhaustive): all examples (object, field, logic function, front component, view, navigation menu item, skill)
npx create-twenty-app@latest my-app
# Minimal: only core files (application-config.ts and default-role.ts)
@@ -114,10 +114,8 @@ my-twenty-app/
│ └── example-view.ts # Example saved view definition
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
── skills/
└── example-skill.ts # Example AI agent skill definition
└── agents/
└── example-agent.ts # Example AI agent definition
── skills/
└── example-skill.ts # Example AI agent skill definition
```
With `--minimal`, only the core files are created (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts`, and `logic-functions/post-install.ts`).
@@ -149,7 +147,6 @@ The SDK detects entities by parsing your TypeScript files for **`export default
| `defineView()` | Saved view definitions |
| `defineNavigationMenuItem()` | Navigation menu item definitions |
| `defineSkill()` | AI agent skill definitions |
| `defineAgent()` | AI agent definitions |
<Note>
**File naming is flexible.** Entity detection is AST-based — the SDK scans your source files for the `export default define<Entity>({...})` pattern. You can organize your files and folders however you like. Grouping by entity type (e.g., `logic-functions/`, `roles/`) is just a convention for code organization, not a requirement.
@@ -169,7 +166,7 @@ export default defineObject({
Later commands will add more files and folders:
- `yarn twenty app:dev` will auto-generate two typed API clients in `node_modules/twenty-sdk/clients`: `CoreApiClient` (for workspace data via `/graphql`) and `MetadataApiClient` (for workspace configuration and file uploads via `/metadata`).
- `yarn twenty app:dev` will auto-generate two typed API clients in `node_modules/twenty-sdk/generated`: `CoreApiClient` (for workspace data via `/graphql`) and `MetadataApiClient` (for workspace configuration and file uploads via `/metadata`).
- `yarn twenty entity:add` will add entity definition files under `src/` for your custom objects, functions, front components, roles, skills, and more.
## Authentication
@@ -226,7 +223,6 @@ The SDK provides helper functions for defining your app entities. As described i
| `defineView()` | Define saved views for objects |
| `defineNavigationMenuItem()` | Define sidebar navigation links |
| `defineSkill()` | Define AI agent skills |
| `defineAgent()` | Define AI agents with system prompts |
These functions validate your configuration at build time and provide IDE autocompletion and type safety.
@@ -431,7 +427,7 @@ Each function file uses `defineLogicFunction()` to export a configuration with a
// 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-sdk/clients';
import { CoreApiClient, type Person } from 'twenty-sdk/generated';
const handler = async (params: RoutePayload) => {
const client = new CoreApiClient();
@@ -669,7 +665,7 @@ To mark a logic function as a tool, set `isTool: true` and provide a `toolInputS
```typescript
// src/logic-functions/enrich-company.logic-function.ts
import { defineLogicFunction } from 'twenty-sdk';
import { CoreApiClient } from 'twenty-sdk/clients';
import { CoreApiClient } from 'twenty-sdk/generated';
const handler = async (params: { companyName: string; domain?: string }) => {
const client = new CoreApiClient();
@@ -794,50 +790,15 @@ You can create new skills in two ways:
- **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new skill.
- **Manual**: Create a new file and use `defineSkill()`, following the same pattern.
### Agents
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```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`,
});
```
Key points:
- `name` is a unique identifier string for the agent (kebab-case recommended).
- `label` is the human-readable display name shown in the UI.
- `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
- `icon` (optional) sets the icon displayed in the UI.
- `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
- **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
- **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### Generated typed clients
Two typed clients are auto-generated by `yarn twenty app:dev` and stored in `node_modules/twenty-sdk/clients` based on your workspace schema:
Two typed clients are auto-generated by `yarn twenty app:dev` and stored in `node_modules/twenty-sdk/generated` based on your workspace schema:
- **`CoreApiClient`** — queries the `/graphql` endpoint for workspace data
- **`MetadataApiClient`** — queries the `/metadata` endpoint for workspace configuration and file uploads
```typescript
import { CoreApiClient } from 'twenty-sdk/clients';
import { MetadataApiClient } from 'twenty-sdk/clients';
import { CoreApiClient, MetadataApiClient } from 'twenty-sdk/generated';
const client = new CoreApiClient();
const { me } = await client.query({ me: { id: true, displayName: true } });
@@ -846,7 +807,7 @@ const metadataClient = new MetadataApiClient();
const { currentWorkspace } = await metadataClient.query({ currentWorkspace: { id: true } });
```
`CoreApiClient` is re-generated automatically by `yarn twenty app:dev` whenever your objects or fields change. `MetadataApiClient` ships pre-built with the SDK.
Both clients are re-generated automatically by `yarn twenty app:dev` whenever your objects or fields change.
#### Runtime credentials in logic functions
@@ -862,10 +823,10 @@ Notes:
#### Uploading files
The `MetadataApiClient` includes an `uploadFile` method for attaching files to file-type fields on your workspace objects. Because standard GraphQL clients do not support multipart file uploads natively, the client provides this dedicated method that implements the [GraphQL multipart request specification](https://github.com/jaydenseric/graphql-multipart-request-spec) under the hood.
The generated `MetadataApiClient` includes an `uploadFile` method for attaching files to file-type fields on your workspace objects. Because standard GraphQL clients do not support multipart file uploads natively, the client provides this dedicated method that implements the [GraphQL multipart request specification](https://github.com/jaydenseric/graphql-multipart-request-spec) under the hood.
```typescript
import { MetadataApiClient } from 'twenty-sdk/clients';
import { MetadataApiClient } from 'twenty-sdk/generated';
import * as fs from 'fs';
const metadataClient = new MetadataApiClient();
@@ -15,7 +15,7 @@ description: أنشئ وأدِر تخصيصات Twenty على هيئة كود.
* عرِّف كائنات وحقولًا مخصصة على شكل كود (نموذج بيانات مُدار)
* أنشئ وظائف منطقية مع مشغلات مخصصة
* تعريف المهارات والوكلاء للذكاء الاصطناعي
* حدد المهارات لوكلاء الذكاء الاصطناعي
* انشر التطبيق نفسه عبر مساحات عمل متعددة
## المتطلبات الأساسية
@@ -39,7 +39,7 @@ yarn twenty app:dev
يدعم المُنشئ وضعين للتحكم في ملفات الأمثلة التي سيتم تضمينها:
```bash filename="Terminal"
# الافتراضي (شامل): جميع الأمثلة (كائن، حقل، دالة منطقية، مكوّن الواجهة الأمامية، عرض، عنصر قائمة التنقل، مهارة، وكيل)
# الافتراضي (شامل): جميع الأمثلة (كائن، حقل، دالة منطقية، مكوّن الواجهة الأمامية، عرض، عنصر قائمة التنقل، مهارة)
npx create-twenty-app@latest my-app
# الأدنى: الملفات الأساسية فقط (application-config.ts و default-role.ts)
@@ -115,10 +115,8 @@ my-twenty-app/
│ └── example-view.ts # تعريف عرض محفوظ — مثال
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # رابط تنقّل في الشريط الجانبي — مثال
── skills/
└── example-skill.ts # تعريف مهارة لوكيل الذكاء الاصطناعي — مثال
└── agents/
└── example-agent.ts # تعريف وكيل الذكاء الاصطناعي — مثال
── skills/
└── example-skill.ts # تعريف مهارة لوكيل الذكاء الاصطناعي — مثال},{
```
مع `--minimal`، سيتم إنشاء الملفات الأساسية فقط (`application-config.ts`، `roles/default-role.ts`، `logic-functions/pre-install.ts`، و`logic-functions/post-install.ts`).
@@ -150,7 +148,6 @@ my-twenty-app/
| `defineView()` | تعريفات العروض المحفوظة |
| `defineNavigationMenuItem()` | تعريفات عناصر قائمة التنقل |
| `defineSkill()` | تعريفات مهارات وكيل الذكاء الاصطناعي |
| `defineAgent()` | تعريفات وكلاء الذكاء الاصطناعي |
<Note>
**تسمية الملفات مرنة.** يعتمد اكتشاف الكيانات على بنية الشجرة المجردة (AST) — إذ يقوم SDK بفحص ملفات المصدر لديك بحثًا عن النمط `export default define<Entity>({...})`. يمكنك تنظيم ملفاتك ومجلداتك كيفما تشاء. التجميع حسب نوع الكيان (مثلًا، `logic-functions/` و`roles/`) هو مجرد عرف لتنظيم الشيفرة، وليس مطلبًا إلزاميًا.
@@ -228,7 +225,6 @@ yarn twenty auth:status
| `defineView()` | تعريف العروض المحفوظة للكائنات |
| `defineNavigationMenuItem()` | تعريف روابط التنقل في الشريط الجانبي |
| `defineSkill()` | عرّف مهارات وكيل الذكاء الاصطناعي |
| `defineAgent()` | Define AI agents with system prompts |
تتحقق هذه الدوال من تكوينك وقت البناء وتوفّر إكمالًا تلقائيًا في بيئة التطوير وأمان الأنواع.
@@ -806,41 +802,6 @@ export default defineSkill({
* **مُنشأ بالقالب**: شغِّل `yarn twenty entity:add` واختر خيار إضافة مهارة جديدة.
* **يدوي**: أنشئ ملفًا جديدًا واستخدم `defineSkill()` مع اتباع النمط نفسه.
### Agents
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```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`,
});
```
النقاط الرئيسية:
* `name` is a unique identifier string for the agent (kebab-case recommended).
* `label` هو اسم العرض المقروء للبشر الظاهر في واجهة المستخدم.
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (اختياري) يحدّد الأيقونة المعروضة في واجهة المستخدم.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### عملاء مُولَّدون مضبوطو الأنواع
يتم توليد عميلين مضبوطي الأنواع تلقائيًا بواسطة `yarn twenty app:dev` وتخزينهما في `node_modules/twenty-sdk/generated` استنادًا إلى مخطط مساحة العمل لديك:
@@ -15,7 +15,7 @@ Aplikace vám umožňují vytvářet a spravovat přizpůsobení Twenty **jako k
* 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!
* Definujte dovednosti agentů AI
* Nasazujte stejnou aplikaci do více pracovních prostorů
## Předpoklady
@@ -39,7 +39,7 @@ yarn twenty app:dev
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)
# Výchozí (úplný): všechny příklady (objekt, pole, logická funkce, front-endová komponenta, zobrazení, položka navigační nabídky, dovednost)
npx create-twenty-app@latest my-app
# Minimální: pouze základní soubory (application-config.ts a default-role.ts)
@@ -115,10 +115,8 @@ my-twenty-app/
│ └── 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
── skills/
└── example-skill.ts # Ukázková definice dovednosti 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`).
@@ -150,7 +148,6 @@ SDK detekuje entity analýzou vašich souborů TypeScript a hledá volání **`e
| `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.
@@ -228,7 +225,6 @@ SDK poskytuje pomocné funkce pro definování entit vaší aplikace. Jak je pop
| `defineView()` | Definujte uložená zobrazení pro objekty |
| `defineNavigationMenuItem()` | Definujte odkazy postranní navigace |
| `defineSkill()` | Definuje dovednosti agenta AI |
| `defineAgent()` | Define AI agents with system prompts |
Tyto funkce validují vaši konfiguraci v době sestavení a poskytují automatické doplňování v IDE a typovou bezpečnost.
@@ -806,41 +802,6 @@ 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
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```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` is a unique identifier string for the agent (kebab-case recommended).
* `label` je uživatelsky čitelný název zobrazovaný v UI.
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (volitelné) nastavuje ikonu zobrazovanou v UI.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### Generované typované klienty
Dva typované klienty jsou automaticky generovány pomocí `yarn twenty app:dev` a ukládají se do `node_modules/twenty-sdk/generated` podle schématu vašeho pracovního prostoru:
@@ -15,7 +15,7 @@ Mit Apps können Sie Twenty-Anpassungen **als Code** erstellen und verwalten. An
* Benutzerdefinierte Objekte und Felder als Code definieren (verwaltetes Datenmodell)
* Logikfunktionen mit benutzerdefinierten Triggern erstellen
* Skills und Agenten für KI definieren
* Fähigkeiten für KI-Agenten definieren
* Dieselbe App in mehreren Workspaces bereitstellen
## Voraussetzungen
@@ -39,7 +39,7 @@ yarn twenty app:dev
Das Scaffolding-Tool unterstützt zwei Modi, um zu steuern, welche Beispieldateien enthalten sind:
```bash filename="Terminal"
# Standard (umfassend): alle Beispiele (Objekt, Feld, Logikfunktion, Frontend-Komponente, View, Navigationsmenüeintrag, Skill, Agent)
# Standard (umfassend): alle Beispiele (Objekt, Feld, Logikfunktion, Frontend-Komponente, View, Navigationsmenüeintrag, Skill)
npx create-twenty-app@latest my-app
# Minimal: nur Kerndateien (application-config.ts und default-role.ts)
@@ -96,29 +96,27 @@ my-twenty-app/
.oxlintrc.json
tsconfig.json
README.md
public/ # Ordner für öffentliche Assets (Bilder, Schriftarten usw.)
public/ # Public assets folder (images, fonts, etc.)
src/
├── application-config.ts # Erforderlich - Hauptkonfiguration der Anwendung
├── application-config.ts # Required - main application configuration
├── roles/
│ └── default-role.ts # Standardrolle für Logikfunktionen
│ └── default-role.ts # Default role for logic functions
├── objects/
│ └── example-object.ts # Beispiel für eine benutzerdefinierte Objektdefinition
│ └── example-object.ts # Example custom object definition
├── fields/
│ └── example-field.ts # Beispiel für eine eigenständige Felddefinition
│ └── example-field.ts # Example standalone field definition
├── logic-functions/
│ ├── hello-world.ts # Beispiel für eine Logikfunktion
│ ├── pre-install.ts # Pre-Installations-Logikfunktion
│ └── post-install.ts # Post-Installations-Logikfunktion
│ ├── hello-world.ts # Example logic function
│ ├── pre-install.ts # Pre-install logic function
│ └── post-install.ts # Post-install logic function
├── front-components/
│ └── hello-world.tsx # Beispiel für eine Frontend-Komponente
│ └── hello-world.tsx # Example front component
├── views/
│ └── example-view.ts # Beispiel für eine gespeicherte View-Definition
│ └── example-view.ts # Example saved view definition
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Beispiel für einen Navigationslink in der Seitenleiste
── skills/
└── example-skill.ts # Beispiel für eine Skill-Definition eines KI-Agenten
└── agents/
└── example-agent.ts # Beispiel für eine KI-Agenten-Definition
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
── skills/
└── example-skill.ts # Example AI agent skill definition
```
Mit `--minimal` werden nur die Kerndateien erstellt (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts` und `logic-functions/post-install.ts`).
@@ -150,7 +148,6 @@ Das SDK erkennt Entitäten, indem es Ihre TypeScript-Dateien nach Aufrufen von *
| `defineView()` | Gespeicherte View-Definitionen |
| `defineNavigationMenuItem()` | Definitionen von Navigationsmenüeinträgen |
| `defineSkill()` | Skill-Definitionen für KI-Agenten |
| `defineAgent()` | KI-Agenten-Definitionen |
<Note>
**Dateibenennung ist flexibel.** Die Entitätserkennung ist AST-basiert — das SDK durchsucht Ihre Quelldateien nach dem Muster `export default define<Entity>({...})`. Sie können Ihre Dateien und Ordner nach Belieben organisieren. Die Gruppierung nach Entitätstyp (z. B. `logic-functions/`, `roles/`) ist lediglich eine Konvention zur Codeorganisation, keine Voraussetzung.
@@ -228,7 +225,6 @@ Das SDK stellt Hilfsfunktionen bereit, um die Entitäten Ihrer App zu definieren
| `defineView()` | Gespeicherte Views für Objekte definieren |
| `defineNavigationMenuItem()` | Seitenleisten-Navigationslinks definieren |
| `defineSkill()` | Definieren Sie Skills für KI-Agenten |
| `defineAgent()` | Define AI agents with system prompts |
Diese Funktionen validieren Ihre Konfiguration zur Build-Zeit und bieten IDE-Autovervollständigung sowie Typsicherheit.
@@ -806,41 +802,6 @@ Sie können neue Skills auf zwei Arten erstellen:
* **Generiert**: Führen Sie `yarn twenty entity:add` aus und wählen Sie die Option zum Hinzufügen eines neuen Skills.
* **Manuell**: Erstellen Sie eine neue Datei und verwenden Sie `defineSkill()` nach demselben Muster.
### Agenten
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```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`,
});
```
Hauptpunkte:
* `name` is a unique identifier string for the agent (kebab-case recommended).
* `label` ist der menschenlesbare Anzeigename, der in der UI angezeigt wird.
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (optional) legt das in der UI angezeigte Symbol fest.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### Generierte typisierte Clients
Zwei typisierte Clients werden von `yarn twenty app:dev` automatisch generiert und basierend auf Ihrem Arbeitsbereichs-Schema in `node_modules/twenty-sdk/generated` gespeichert:
@@ -15,7 +15,7 @@ Le app ti consentono di creare e gestire le personalizzazioni di Twenty **come c
* Definisci oggetti e campi personalizzati come codice (modello dati gestito)
* Crea funzioni logiche con trigger personalizzati
* Definire skill e agenti per l'IA
* Definire le abilità per gli agenti IA
* Distribuisci la stessa app su più spazi di lavoro
## Prerequisiti
@@ -39,7 +39,7 @@ yarn twenty app:dev
Lo strumento di scaffolding supporta due modalità per controllare quali file di esempio vengono inclusi:
```bash filename="Terminal"
# Predefinita (esaustiva): tutti gli esempi (oggetto, campo, funzione logica, componente front-end, vista, voce del menu di navigazione, skill, agente)
# Predefinita (esaustiva): tutti gli esempi (oggetto, campo, funzione logica, componente front-end, vista, voce del menu di navigazione, skill)
npx create-twenty-app@latest my-app
# Minimale: solo i file principali (application-config.ts e default-role.ts)
@@ -96,29 +96,27 @@ my-twenty-app/
.oxlintrc.json
tsconfig.json
README.md
public/ # Cartella delle risorse pubbliche (immagini, font, ecc.)
public/ # Public assets folder (images, fonts, etc.)
src/
├── application-config.ts # Obbligatorio - configurazione principale dell'applicazione
├── application-config.ts # Required - main application configuration
├── roles/
│ └── default-role.ts # Ruolo predefinito per le funzioni logiche
│ └── default-role.ts # Default role for logic functions
├── objects/
│ └── example-object.ts # Definizione di oggetto personalizzato di esempio
│ └── example-object.ts # Example custom object definition
├── fields/
│ └── example-field.ts # Definizione di campo autonomo di esempio
│ └── example-field.ts # Example standalone field definition
├── logic-functions/
│ ├── hello-world.ts # Funzione logica di esempio
│ ├── pre-install.ts # Funzione logica di pre-installazione
│ └── post-install.ts # Funzione logica di post-installazione
│ ├── hello-world.ts # Example logic function
│ ├── pre-install.ts # Pre-install logic function
│ └── post-install.ts # Post-install logic function
├── front-components/
│ └── hello-world.tsx # Componente front-end di esempio
│ └── hello-world.tsx # Example front component
├── views/
│ └── example-view.ts # Definizione di vista salvata di esempio
│ └── example-view.ts # Example saved view definition
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Link di navigazione della barra laterale di esempio
── skills/
└── example-skill.ts # Definizione di skill per agente IA di esempio
└── agents/
└── example-agent.ts # Definizione di agente IA di esempio
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
── skills/
└── example-skill.ts # Example AI agent skill definition
```
Con `--minimal`, vengono creati solo i file principali (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts` e `logic-functions/post-install.ts`).
@@ -150,7 +148,6 @@ L'SDK rileva le entità analizzando i tuoi file TypeScript alla ricerca di chiam
| `defineView()` | Definizioni di viste salvate |
| `defineNavigationMenuItem()` | Definizioni delle voci del menu di navigazione |
| `defineSkill()` | AI agent skill definitions |
| `defineAgent()` | Definizioni di agenti IA |
<Note>
**La denominazione dei file è flessibile.** Il rilevamento delle entità è basato sull'AST — l'SDK esegue la scansione dei file sorgente alla ricerca del pattern `export default define<Entity>({...})`. Puoi organizzare file e cartelle come preferisci. Raggruppare per tipo di entità (ad es., `logic-functions/`, `roles/`) è solo una convenzione per l'organizzazione del codice, non un requisito.
@@ -228,7 +225,6 @@ L'SDK fornisce funzioni helper per definire le entità della tua app. Come descr
| `defineView()` | Definisce viste salvate per gli oggetti |
| `defineNavigationMenuItem()` | Definisce i link di navigazione della barra laterale |
| `defineSkill()` | Define AI agent skills |
| `defineAgent()` | Define AI agents with system prompts |
Queste funzioni convalidano la configurazione in fase di build e offrono il completamento automatico nell'IDE e la sicurezza dei tipi.
@@ -806,41 +802,6 @@ You can create new skills in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new skill.
* **Manual**: Create a new file and use `defineSkill()`, following the same pattern.
### Agenti
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```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`,
});
```
Punti chiave:
* `name` is a unique identifier string for the agent (kebab-case recommended).
* `label` is the human-readable display name shown in the UI.
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (optional) sets the icon displayed in the UI.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### Client tipizzati generati
Due client tipizzati sono generati automaticamente da `yarn twenty app:dev` e salvati in `node_modules/twenty-sdk/generated` in base allo schema della tua area di lavoro:
@@ -15,7 +15,7 @@ Os aplicativos permitem criar e gerenciar personalizações do Twenty **como có
* Defina objetos e campos personalizados como código (modelo de dados gerenciado)
* Crie funções de lógica com gatilhos personalizados
* Defina habilidades e agentes de IA
* Definir habilidades para agentes de IA
* Implemente o mesmo aplicativo em vários espaços de trabalho
## Pré-requisitos
@@ -39,7 +39,7 @@ yarn twenty app:dev
O gerador de estrutura oferece suporte a dois modos para controlar quais arquivos de exemplo são incluídos:
```bash filename="Terminal"
# Padrão (exhaustivo): todos os exemplos (objeto, campo, função de lógica, componente de front-end, visualização, item do menu de navegação, habilidade, agente)
# Padrão (exhaustivo): todos os exemplos (objeto, campo, função de lógica, componente de front-end, visualização, item do menu de navegação, habilidade)
npx create-twenty-app@latest my-app
# Mínimo: apenas arquivos principais (application-config.ts e default-role.ts)
@@ -96,29 +96,27 @@ my-twenty-app/
.oxlintrc.json
tsconfig.json
README.md
public/ # Public assets folder (images, fonts, etc.)
public/ # Pasta de recursos públicos (imagens, fontes, etc.)
src/
├── application-config.ts # Required - main application configuration
├── application-config.ts # Obrigatório - configuração principal da aplicação
├── roles/
│ └── default-role.ts # Default role for logic functions
│ └── default-role.ts # Papel padrão para funções de lógica
├── objects/
│ └── example-object.ts # Example custom object definition
│ └── example-object.ts # Exemplo de definição de objeto personalizado
├── fields/
│ └── example-field.ts # Example standalone field definition
│ └── example-field.ts # Exemplo de definição de campo independente
├── logic-functions/
│ ├── hello-world.ts # Example logic function
│ ├── pre-install.ts # Pre-install logic function
│ └── post-install.ts # Post-install logic function
│ ├── hello-world.ts # Exemplo de função de lógica
│ ├── pre-install.ts # Função de lógica de pré-instalação
│ └── post-install.ts # Função de lógica de pós-instalação
├── front-components/
│ └── hello-world.tsx # Example front component
│ └── hello-world.tsx # Exemplo de componente de front-end
├── views/
│ └── example-view.ts # Example saved view definition
│ └── example-view.ts # Exemplo de definição de visualização salva
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
── skills/
└── example-skill.ts # Example AI agent skill definition
└── agents/
└── example-agent.ts # Example AI agent definition
│ └── example-navigation-menu-item.ts # Exemplo de link de navegação da barra lateral
── skills/
└── example-skill.ts # Exemplo de definição de habilidade de agente de IA
```
With `--minimal`, only the core files are created (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts`, and `logic-functions/post-install.ts`).
@@ -150,7 +148,6 @@ O SDK detecta entidades analisando seus arquivos TypeScript em busca de chamadas
| `defineView()` | Definições de visualizações salvas |
| `defineNavigationMenuItem()` | Definições de itens do menu de navegação |
| `defineSkill()` | Definições de habilidades de agente de IA |
| `defineAgent()` | Definições de agentes de IA |
<Note>
**A nomeação de arquivos é flexível.** A detecção de entidades é baseada em AST — o SDK varre seus arquivos fonte em busca do padrão `export default define<Entity>({...})`. Você pode organizar seus arquivos e pastas como quiser. Agrupar por tipo de entidade (por exemplo, `logic-functions/`, `roles/`) é apenas uma convenção para organização do código, não um requisito.
@@ -228,7 +225,6 @@ O SDK fornece funções utilitárias para definir as entidades do seu app. Confo
| `defineView()` | Define visualizações salvas para objetos |
| `defineNavigationMenuItem()` | Define links de navegação da barra lateral |
| `defineSkill()` | Define habilidades de agente de IA |
| `defineAgent()` | Define AI agents with system prompts |
Essas funções validam sua configuração em tempo de compilação e oferecem autocompletar na IDE e segurança de tipos.
@@ -807,41 +803,6 @@ Você pode criar novas habilidades de duas formas:
* **Gerado automaticamente**: Execute `yarn twenty entity:add` e escolha a opção para adicionar uma nova habilidade.
* **Manual**: Crie um novo arquivo e use `defineSkill()`, seguindo o mesmo padrão.
### Agentes
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```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`,
});
```
Pontos-chave:
* `name` is a unique identifier string for the agent (kebab-case recommended).
* `label` é o nome de exibição legível por humanos mostrado na UI.
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (opcional) define o ícone exibido na UI.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### Clientes tipados gerados
Dois clientes tipados são gerados automaticamente pelo `yarn twenty app:dev` e armazenados em `node_modules/twenty-sdk/generated` com base no esquema do seu espaço de trabalho:
@@ -15,7 +15,7 @@ Aplicațiile vă permit să construiți și să gestionați personalizările Twe
* Definiți obiecte și câmpuri personalizate sub formă de cod (model de date gestionat)
* Creați funcții de logică cu declanșatoare personalizate
* Definiți abilități și agenți pentru AI
* Definiți abilități pentru agenți de IA
* Implementați aceeași aplicație în mai multe spații de lucru
## Cerințe
@@ -39,7 +39,7 @@ yarn twenty app:dev
Generatorul de schelet acceptă două moduri pentru a controla ce fișiere de exemplu sunt incluse:
```bash filename="Terminal"
# Implicit (exhaustiv): toate exemplele (obiect, câmp, funcție logică, componentă de interfață, vizualizare, element de meniu de navigare, abilitate, agent)
# Implicit (exhaustiv): toate exemplele (obiect, câmp, funcție logică, componentă de interfață, vizualizare, element de meniu de navigare, abilitate)
npx create-twenty-app@latest my-app
# Minimal: doar fișierele de bază (application-config.ts și default-role.ts)
@@ -96,29 +96,27 @@ my-twenty-app/
.oxlintrc.json
tsconfig.json
README.md
public/ # Director pentru resurse publice (imagini, fonturi etc.)
public/ # Public assets folder (images, fonts, etc.)
src/
├── application-config.ts # Obligatoriu - configurația principală a aplicației
├── application-config.ts # Required - main application configuration
├── roles/
│ └── default-role.ts # Rol implicit pentru funcțiile logice
│ └── default-role.ts # Default role for logic functions
├── objects/
│ └── example-object.ts # Exemplu de definiție a unui obiect personalizat
│ └── example-object.ts # Example custom object definition
├── fields/
│ └── example-field.ts # Exemplu de definiție de câmp independent
│ └── example-field.ts # Example standalone field definition
├── logic-functions/
│ ├── hello-world.ts # Exemplu de funcție logică
│ ├── pre-install.ts # Funcție logică de pre-instalare
│ └── post-install.ts # Funcție logică post-instalare
│ ├── hello-world.ts # Example logic function
│ ├── pre-install.ts # Pre-install logic function
│ └── post-install.ts # Post-install logic function
├── front-components/
│ └── hello-world.tsx # Exemplu de componentă de interfață
│ └── hello-world.tsx # Example front component
├── views/
│ └── example-view.ts # Exemplu de definiție a unei vizualizări salvate
│ └── example-view.ts # Example saved view definition
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Exemplu de link de navigare în bara laterală
── skills/
└── example-skill.ts # Exemplu de definiție a unei abilități a agentului AI
└── agents/
└── example-agent.ts # Exemplu de definiție a unui agent AI
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
── skills/
└── example-skill.ts # Example AI agent skill definition
```
Cu `--minimal`, sunt create doar fișierele de bază (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts` și `logic-functions/post-install.ts`).
@@ -150,7 +148,6 @@ SDK-ul detectează entitățile analizând fișierele TypeScript pentru apeluri
| `defineView()` | Definiții pentru vizualizări salvate |
| `defineNavigationMenuItem()` | Definiții pentru elemente de meniu de navigare |
| `defineSkill()` | Definiții ale abilităților agentului AI |
| `defineAgent()` | Definiții ale agenților AI |
<Note>
**Denumirea fișierelor este flexibilă.** Detectarea entităților se bazează pe AST — SDK-ul scanează fișierele sursă pentru tiparul `export default define<Entity>({...})`. Puteți organiza fișierele și folderele cum doriți. Gruparea după tipul de entitate (de exemplu, `logic-functions/`, `roles/`) este doar o convenție pentru organizarea codului, nu o cerință.
@@ -228,7 +225,6 @@ SDK-ul oferă funcții ajutătoare pentru definirea entităților aplicației. D
| `defineView()` | Definește vizualizări salvate pentru obiecte |
| `defineNavigationMenuItem()` | Definește linkuri de navigare în bara laterală |
| `defineSkill()` | Definiți abilități pentru agentul AI |
| `defineAgent()` | Define AI agents with system prompts |
Aceste funcții validează configurația în timpul build-ului și oferă completare automată în IDE și siguranța tipurilor.
@@ -806,41 +802,6 @@ Puteți crea abilități noi în două moduri:
* **Generat**: Rulați `yarn twenty entity:add` și alegeți opțiunea de a adăuga o abilitate nouă.
* **Manual**: Creați un fișier nou și folosiți `defineSkill()`, urmând același model.
### Agenți
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```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`,
});
```
Puncte cheie:
* `name` is a unique identifier string for the agent (kebab-case recommended).
* `label` este numele lizibil afișat în interfața cu utilizatorul (UI).
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (opțional) setează pictograma afișată în UI.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### Generated typed clients
Two typed clients are auto-generated by `yarn twenty app:dev` and stored in `node_modules/twenty-sdk/generated` based on your workspace schema:
@@ -15,7 +15,7 @@ description: Создавайте и управляйте настройками
* Определяйте пользовательские объекты и поля в виде кода (управляемая модель данных)
* Создавайте логические функции с пользовательскими триггерами
* Определите навыки и агентов для ИИ
* Определите навыки для ИИ-агентов
* Развёртывайте одно и то же приложение в нескольких рабочих пространствах
## Требования
@@ -39,7 +39,7 @@ yarn twenty app:dev
Генератор каркаса поддерживает два режима для управления тем, какие файлы-примеры включаются:
```bash filename="Terminal"
# По умолчанию (полный набор): все примеры (объект, поле, логическая функция, фронтенд-компонент, представление, пункт меню навигации, навык, агент)
# По умолчанию (полный набор): все примеры (объект, поле, логическая функция, фронтенд-компонент, представление, пункт меню навигации, навык)
npx create-twenty-app@latest my-app
# Минимальный: только основные файлы (application-config.ts и default-role.ts)
@@ -96,29 +96,27 @@ my-twenty-app/
.oxlintrc.json
tsconfig.json
README.md
public/ # Папка публичных ресурсов (изображения, шрифты и т. д.)
public/ # Public assets folder (images, fonts, etc.)
src/
├── application-config.ts # Обязательный — основная конфигурация приложения
├── application-config.ts # Required - main application configuration
├── roles/
│ └── default-role.ts # Роль по умолчанию для логических функций
│ └── default-role.ts # Default role for logic functions
├── objects/
│ └── example-object.ts # Пример определения пользовательского объекта
│ └── example-object.ts # Example custom object definition
├── fields/
│ └── example-field.ts # Пример определения отдельного поля
│ └── example-field.ts # Example standalone field definition
├── logic-functions/
│ ├── hello-world.ts # Пример логической функции
│ ├── pre-install.ts # Предустановочная логическая функция
│ └── post-install.ts # Послеустановочная логическая функция
│ ├── hello-world.ts # Example logic function
│ ├── pre-install.ts # Pre-install logic function
│ └── post-install.ts # Post-install logic function
├── front-components/
│ └── hello-world.tsx # Пример фронтенд-компонента
│ └── hello-world.tsx # Example front component
├── views/
│ └── example-view.ts # Пример определения сохранённого представления
│ └── example-view.ts # Example saved view definition
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Пример ссылки боковой панели навигации
── skills/
└── example-skill.ts # Пример определения навыка агента ИИ
└── agents/
└── example-agent.ts # Пример определения агента ИИ
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
── skills/
└── example-skill.ts # Example AI agent skill definition
```
С `--minimal` создаются только основные файлы (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts` и `logic-functions/post-install.ts`).
@@ -150,7 +148,6 @@ SDK обнаруживает сущности, разбирая ваши фай
| `defineView()` | Определения сохранённых представлений |
| `defineNavigationMenuItem()` | Определения пунктов меню навигации |
| `defineSkill()` | Определения навыков агента ИИ |
| `defineAgent()` | Определения агентов ИИ |
<Note>
**Имена файлов заданы гибко.** Обнаружение сущностей основано на AST — SDK сканирует ваши исходные файлы в поисках шаблона `export default define<Entity>({...})`. Вы можете организовывать файлы и папки как угодно. Группировка по типу сущности (например, `logic-functions/`, `roles/`) — это лишь соглашение для организации кода, а не требование.
@@ -228,7 +225,6 @@ SDK предоставляет вспомогательные функции д
| `defineView()` | Определяйте сохранённые представления для объектов |
| `defineNavigationMenuItem()` | Определяйте ссылки боковой панели навигации |
| `defineSkill()` | Определение навыков агента ИИ |
| `defineAgent()` | Define AI agents with system prompts |
Эти функции проверяют вашу конфигурацию на этапе сборки и обеспечивают автодополнение в IDE и безопасность типов.
@@ -806,41 +802,6 @@ export default defineSkill({
* **Сгенерировано**: Запустите `yarn twenty entity:add` и выберите опцию добавления нового навыка.
* **Вручную**: Создайте новый файл и используйте `defineSkill()`, следуя тому же шаблону.
### Агенты
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```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`,
});
```
Основные моменты:
* `name` is a unique identifier string for the agent (kebab-case recommended).
* `label` — читаемое человеком отображаемое имя, показываемое в UI.
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (необязательно) задаёт значок, отображаемый в UI.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### Сгенерированные типизированные клиенты
Два типизированных клиента автоматически генерируются с помощью `yarn twenty app:dev` и сохраняются в `node_modules/twenty-sdk/generated` на основе схемы вашего рабочего пространства:
@@ -15,7 +15,7 @@ Uygulamalar, Twenty özelleştirmelerini **kod olarak** oluşturup yönetmenizi
* Özel nesneleri ve alanları kod olarak tanımlayın (yönetilen veri modeli)
* Özel tetikleyicilerle mantık fonksiyonları oluşturun
* Define skills and agents for AI
* Yapay zekâ ajanları için becerileri tanımlayın
* Aynı uygulamayı birden çok çalışma alanına dağıtın
## Ön Gereksinimler
@@ -39,10 +39,10 @@ yarn twenty app:dev
İskelet oluşturucu, hangi örnek dosyaların dahil edileceğini kontrol etmek için iki modu destekler:
```bash filename="Terminal"
# Default (exhaustive): all examples (object, field, logic function, front component, view, navigation menu item, skill, agent)
# Varsayılan (kapsamlı): tüm örnekler (nesne, alan, mantık fonksiyonu, ön bileşen, görünüm, gezinme menüsü öğesi, yetenek)
npx create-twenty-app@latest my-app
# Minimal: only core files (application-config.ts and default-role.ts)
# Minimal: yalnızca çekirdek dosyalar (application-config.ts ve default-role.ts)
npx create-twenty-app@latest my-app --minimal
```
@@ -96,29 +96,27 @@ my-twenty-app/
.oxlintrc.json
tsconfig.json
README.md
public/ # Public assets folder (images, fonts, etc.)
public/ # Genel varlıklar klasörü (görseller, yazı tipleri vb.)
src/
├── application-config.ts # Required - main application configuration
├── application-config.ts # Gerekli - ana uygulama yapılandırması
├── roles/
│ └── default-role.ts # Default role for logic functions
│ └── default-role.ts # Mantık fonksiyonları için varsayılan rol
├── objects/
│ └── example-object.ts # Example custom object definition
│ └── example-object.ts # Örnek özel nesne tanımı
├── fields/
│ └── example-field.ts # Example standalone field definition
│ └── example-field.ts # Örnek bağımsız alan tanımı
├── logic-functions/
│ ├── hello-world.ts # Example logic function
│ ├── pre-install.ts # Pre-install logic function
│ └── post-install.ts # Post-install logic function
│ ├── hello-world.ts # Örnek mantık fonksiyonu
│ ├── pre-install.ts # Kurulum öncesi mantık fonksiyonu
│ └── post-install.ts # Kurulum sonrası mantık fonksiyonu
├── front-components/
│ └── hello-world.tsx # Example front component
│ └── hello-world.tsx # Örnek ön bileşen
├── views/
│ └── example-view.ts # Example saved view definition
│ └── example-view.ts # Örnek kaydedilmiş görünüm tanımı
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
── skills/
└── example-skill.ts # Example AI agent skill definition
└── agents/
└── example-agent.ts # Example AI agent definition
│ └── example-navigation-menu-item.ts # Örnek kenar çubuğu gezinme bağlantısı
── skills/
└── example-skill.ts # Örnek yapay zekâ ajanı yetenek tanımı
```
`--minimal` ile yalnızca çekirdek dosyalar oluşturulur (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts` ve `logic-functions/post-install.ts`).
@@ -150,7 +148,6 @@ SDK, TypeScript dosyalarınızı **`export default define<Entity>({...})`** ça
| `defineView()` | Kaydedilmiş görünüm tanımları |
| `defineNavigationMenuItem()` | Gezinme menüsü öğesi tanımları |
| `defineSkill()` | Yapay zekâ ajanı yetenek tanımları |
| `defineAgent()` | AI agent definitions |
<Note>
**Dosya adlandırma esnektir.** Varlık algılama AST tabanlıdır — SDK, kaynak dosyalarınızı `export default define<Entity>({...})` desenini bulmak için tarar. Dosyalarınızı ve klasörlerinizi dilediğiniz gibi düzenleyebilirsiniz. Varlık türüne göre gruplama (örn. `logic-functions/`, `roles/`) bir gereklilik değil, yalnızca kod organizasyonu için bir gelenektir.
@@ -228,7 +225,6 @@ SDK, uygulama varlıklarınızı tanımlamak için yardımcı fonksiyonlar sağl
| `defineView()` | Nesneler için kaydedilmiş görünümler tanımlayın |
| `defineNavigationMenuItem()` | Kenar çubuğu gezinme bağlantılarını tanımlayın |
| `defineSkill()` | Yapay zekâ ajanı yeteneklerini tanımlayın |
| `defineAgent()` | Define AI agents with system prompts |
Bu fonksiyonlar, derleme zamanında yapılandırmanızı doğrular ve IDE otomatik tamamlama ile tür güvenliği sağlar.
@@ -806,41 +802,6 @@ Yeni yetenekleri iki şekilde oluşturabilirsiniz:
* **Şablondan**: `yarn twenty entity:add` komutunu çalıştırın ve yeni bir yetenek ekleme seçeneğini seçin.
* **Manuel**: Yeni bir dosya oluşturun ve aynı deseni izleyerek `defineSkill()` kullanın.
### Temsilciler
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```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`,
});
```
Önemli noktalar:
* `name` is a unique identifier string for the agent (kebab-case recommended).
* `label`, UI'de gösterilen, insan tarafından okunabilir addır.
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (isteğe bağlı), UI'de gösterilen simgeyi ayarlar.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### Oluşturulan tiplendirilmiş istemciler
Çalışma alanı şemanıza göre `yarn twenty app:dev` tarafından iki tiplendirilmiş istemci otomatik olarak oluşturulur ve `node_modules/twenty-sdk/generated` içine kaydedilir:
@@ -15,7 +15,7 @@ description: 以代码的形式构建并管理 Twenty 自定义项。
* 以代码定义自定义对象和字段(受管理的数据模型)
* 构建带有自定义触发器的逻辑函数
* Define skills and agents for AI
* 为 AI 智能体定义技能
* 将同一个应用部署到多个工作空间
## 先决条件
@@ -39,10 +39,10 @@ yarn twenty app:dev
脚手架工具支持两种模式,用于控制包含哪些示例文件:
```bash filename="Terminal"
# Default (exhaustive): all examples (object, field, logic function, front component, view, navigation menu item, skill, agent)
# 默认(完整):所有示例(对象、字段、逻辑函数、前端组件、视图、导航菜单项、技能)
npx create-twenty-app@latest my-app
# Minimal: only core files (application-config.ts and default-role.ts)
# 最小化:仅核心文件(application-config.ts default-role.ts
npx create-twenty-app@latest my-app --minimal
```
@@ -115,10 +115,8 @@ my-twenty-app/
│ └── example-view.ts # Example saved view definition
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
── skills/
└── example-skill.ts # Example AI agent skill definition
└── agents/
└── example-agent.ts # Example AI agent definition
── skills/
└── example-skill.ts # Example AI agent skill definition
```
使用 `--minimal` 时,只会创建核心文件(`application-config.ts`、`roles/default-role.ts`、`logic-functions/pre-install.ts` 和 `logic-functions/post-install.ts`)。
@@ -150,7 +148,6 @@ my-twenty-app/
| `defineView()` | Saved view definitions |
| `defineNavigationMenuItem()` | Navigation menu item definitions |
| `defineSkill()` | AI agent skill definitions |
| `defineAgent()` | AI agent definitions |
<Note>
**文件命名是灵活的。** 实体检测基于 AST — SDK 会扫描你的源文件以查找 `export default define<Entity>({...})` 模式。 你可以按照自己的喜好组织文件和文件夹。 按实体类型分组(例如 `logic-functions/`、`roles/`)只是代码组织的一种约定,并非必需。
@@ -215,20 +212,19 @@ twenty-sdk 提供你在应用中使用的类型化构件和辅助函数。 以
该 SDK 提供辅助函数用于定义你的应用实体。 如 [实体检测](#entity-detection) 中所述,你必须使用 `export default define<Entity>({...})` 才能让你的实体被检测到:
| 函数 | 目的 |
| ---------------------------------- | ------------------------------------ |
| `defineApplication()` | 配置应用元数据(必需,每个应用一个) |
| `defineObject()` | 定义带字段的自定义对象 |
| `defineLogicFunction()` | 定义带处理程序的逻辑函数 |
| `definePreInstallLogicFunction()` | 定义一个安装前逻辑函数(每个应用一个) |
| `definePostInstallLogicFunction()` | 定义一个安装后逻辑函数(每个应用一个) |
| `defineFrontComponent()` | 为自定义 UI 定义前端组件 |
| `defineRole()` | 配置角色权限和对象访问 |
| `defineField()` | 为现有对象扩展额外字段 |
| `defineView()` | Define saved views for objects |
| `defineNavigationMenuItem()` | Define sidebar navigation links |
| `defineSkill()` | Define AI agent skills |
| `defineAgent()` | Define AI agents with system prompts |
| 函数 | 目的 |
| ---------------------------------- | ------------------------------- |
| `defineApplication()` | 配置应用元数据(必需,每个应用一个) |
| `defineObject()` | 定义带字段的自定义对象 |
| `defineLogicFunction()` | 定义带处理程序的逻辑函数 |
| `definePreInstallLogicFunction()` | 定义一个安装前逻辑函数(每个应用一个) |
| `definePostInstallLogicFunction()` | 定义一个安装后逻辑函数(每个应用一个) |
| `defineFrontComponent()` | 为自定义 UI 定义前端组件 |
| `defineRole()` | 配置角色权限和对象访问 |
| `defineField()` | 为现有对象扩展额外字段 |
| `defineView()` | Define saved views for objects |
| `defineNavigationMenuItem()` | Define sidebar navigation links |
| `defineSkill()` | Define AI agent skills |
这些函数会在构建时校验你的配置,并提供 IDE 自动补全和类型安全。
@@ -806,41 +802,6 @@ You can create new skills in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new skill.
* **Manual**: Create a new file and use `defineSkill()`, following the same pattern.
### 代理
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```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`,
});
```
关键点:
* `name` is a unique identifier string for the agent (kebab-case recommended).
* `label` is the human-readable display name shown in the UI.
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (optional) sets the icon displayed in the UI.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### 生成的类型化客户端
两个类型化客户端由 `yarn twenty app:dev` 自动生成(基于你的工作区架构),并存放在 `node_modules/twenty-sdk/generated`
+2 -2
View File
@@ -61,8 +61,8 @@ const jestConfig = {
extensionsToTreatAsEsm: ['.ts', '.tsx'],
coverageThreshold: {
global: {
statements: 49.3,
lines: 47.9,
statements: 49.5,
lines: 48,
functions: 39.5,
},
},
-1
View File
@@ -38,7 +38,6 @@
"@calcom/embed-react": "^1.5.3",
"@cyntler/react-doc-viewer": "^1.17.0",
"@dagrejs/dagre": "^1.1.8",
"@dnd-kit/react": "^0.3.2",
"@floating-ui/react": "^0.24.3",
"@graphiql/plugin-explorer": "^1.0.2",
"@graphiql/react": "^0.23.0",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

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