الانتقال إلى المحتوى الرئيسي
التطبيقات حاليًا في مرحلة الاختبار الألفا. الميزة تعمل لكنها لا تزال قيد التطور.

استخدم موارد SDK (الأنواع والتكوين)

يوفّر twenty-sdk كتلَ بناءٍ مضبوطة الأنواع ودوال مساعدة تستخدمها داخل تطبيقك. فيما يلي الأجزاء الأساسية التي ستتعامل معها غالبًا.

دوال مساعدة

يوفّر SDK دوالًا مساعدة لتعريف كيانات تطبيقك. كما هو موضح في اكتشاف الكيانات، يجب استخدام export default define<Entity>({...}) كي يتم اكتشاف كياناتك:
دالةالغرض
defineApplicationتهيئة بيانات التعريف للتطبيق (مطلوب، واحد لكل تطبيق)
defineObjectتعريف كائنات مخصصة مع حقول
defineLogicFunctionتعريف وظائف منطقية مع معالجات
definePreInstallLogicFunctionتعريف دالة منطقية لما قبل التثبيت (واحدة لكل تطبيق)
definePostInstallLogicFunctionتعريف دالة منطقية لما بعد التثبيت (واحدة لكل تطبيق)
defineFrontComponentعرِّف مكوّنات أمامية لواجهة مستخدم مخصّصة
defineRoleتهيئة صلاحيات الدور والوصول إلى الكائنات
defineFieldوسّع الكائنات الموجودة بحقول إضافية
defineViewتعريف العروض المحفوظة للكائنات
defineNavigationMenuItemتعريف روابط التنقل في الشريط الجانبي
defineSkillعرّف مهارات وكيل الذكاء الاصطناعي
تتحقق هذه الدوال من تكوينك وقت البناء وتوفّر إكمالًا تلقائيًا في بيئة التطوير وأمان الأنواع.

تعريف الكائنات

تصف الكائنات المخصصة كلًا من المخطط والسلوك للسجلات في مساحة عملك. استخدم defineObject() لتعريف كائنات مع تحقق مدمج:
// src/app/postCard.object.ts
import { defineObject, FieldType } from 'twenty-sdk';

enum PostCardStatus {
  DRAFT = 'DRAFT',
  SENT = 'SENT',
  DELIVERED = 'DELIVERED',
  RETURNED = 'RETURNED',
}

export default defineObject({
  universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
  nameSingular: 'postCard',
  namePlural: 'postCards',
  labelSingular: 'Post Card',
  labelPlural: 'Post Cards',
  description: 'A post card object',
  icon: 'IconMail',
  fields: [
    {
      universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
      name: 'content',
      type: FieldType.TEXT,
      label: 'Content',
      description: "Postcard's content",
      icon: 'IconAbc',
    },
    {
      universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
      name: 'recipientName',
      type: FieldType.FULL_NAME,
      label: 'Recipient name',
      icon: 'IconUser',
    },
    {
      universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
      name: 'recipientAddress',
      type: FieldType.ADDRESS,
      label: 'Recipient address',
      icon: 'IconHome',
    },
    {
      universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
      name: 'status',
      type: FieldType.SELECT,
      label: 'Status',
      icon: 'IconSend',
      defaultValue: `'${PostCardStatus.DRAFT}'`,
      options: [
        { value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
        { value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
        { value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
        { value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
      ],
    },
    {
      universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
      name: 'deliveredAt',
      type: FieldType.DATE_TIME,
      label: 'Delivered at',
      icon: 'IconCheck',
      isNullable: true,
      defaultValue: null,
    },
  ],
});
النقاط الرئيسية:
  • استخدم defineObject() للحصول على تحقق مدمج ودعم أفضل من IDE.
  • universalIdentifier يجب أن يكون فريدًا وثابتًا عبر عمليات النشر.
  • يتطلب كل حقل name وtype وlabel ومعرّف universalIdentifier ثابتًا خاصًا به.
  • المصفوفة fields اختيارية — يمكنك تعريف كائنات بدون حقول مخصصة.
  • يمكنك إنشاء كائنات جديدة باستخدام yarn twenty entity:add، والذي يرشدك خلال التسمية والحقول والعلاقات.
يتم إنشاء الحقول الأساسية تلقائيًا. عند تعريف كائن مخصص، يضيف Twenty تلقائيًا حقولًا قياسية مثل id وname وcreatedAt وupdatedAt وcreatedBy وupdatedBy وdeletedAt. لا تحتاج إلى تعريف هذه في مصفوفة fields — أضف فقط حقولك المخصصة. يمكنك تجاوز الحقول الافتراضية من خلال تعريف حقل بالاسم نفسه في مصفوفة fields الخاصة بك، لكن هذا غير مستحسن.

تكوين التطبيق (application-config.ts)

كل تطبيق لديه ملف واحد application-config.ts يصف:
  • هوية التطبيق: المعرفات، اسم العرض، والوصف.
  • كيفية تشغيل وظائفه: الدور الذي تستخدمه للأذونات.
  • متغيرات (اختياري): أزواج مفتاح-قيمة تُعرض لوظائفك كمتغيرات بيئة.
  • (اختياري) دالة ما قبل التثبيت: دالة منطقية تعمل قبل تثبيت التطبيق.
  • (اختياري) دالة ما بعد التثبيت: دالة منطقية تعمل بعد تثبيت التطبيق.
استخدم defineApplication() لتعريف تهيئة تطبيقك:
// src/application-config.ts
import { defineApplication } from 'twenty-sdk';
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role';

export default defineApplication({
  universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
  displayName: 'My Twenty App',
  description: 'My first Twenty app',
  icon: 'IconWorld',
  applicationVariables: {
    DEFAULT_RECIPIENT_NAME: {
      universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
      description: 'Default recipient name for postcards',
      value: 'Jane Doe',
      isSecret: false,
    },
  },
  defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
});
الملاحظات:
  • حقول universalIdentifier هي معرّفات حتمية تخصك؛ أنشئها مرة واحدة واحتفظ بها ثابتة عبر عمليات المزامنة.
  • applicationVariables تصبح متغيرات بيئة لوظائفك (على سبيل المثال، DEFAULT_RECIPIENT_NAME متاح كـ process.env.DEFAULT_RECIPIENT_NAME).
  • defaultRoleUniversalIdentifier يجب أن يطابق ملف الدور (انظر أدناه).
  • يتم اكتشاف دوال ما قبل التثبيت وما بعد التثبيت تلقائيًا أثناء إنشاء ملف البيان. راجع دوال ما قبل التثبيت ودوال ما بعد التثبيت.

الأدوار والصلاحيات

يمكن للتطبيقات تعريف أدوار تُغلّف الصلاحيات على كائنات وإجراءات مساحة العمل لديك. يعين الحقل defaultRoleUniversalIdentifier في application-config.ts الدور الافتراضي الذي تستخدمه وظائف المنطق في تطبيقك.
  • مفتاح واجهة البرمجة في وقت التشغيل المحقون باسم TWENTY_API_KEY مستمد من دور الوظيفة الافتراضي هذا.
  • سيُقيَّد العميل مضبوط الأنواع بالأذونات الممنوحة لذلك الدور.
  • اتبع مبدأ أقل الامتياز: أنشئ دورًا مخصصًا بالأذونات التي تحتاجها وظائفك فقط، ثم أشِر إلى معرّفه الشامل.
الدور الافتراضي للوظيفة (*.role.ts)
عند توليد تطبيق جديد بالقالب، ينشئ CLI أيضًا ملف دور افتراضي. استخدم defineRole() لتعريف أدوار مع تحقق مدمج:
// src/roles/default-role.ts
import { defineRole, PermissionFlag } from 'twenty-sdk';

export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER =
  'b648f87b-1d26-4961-b974-0908fd991061';

export default defineRole({
  universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
  label: 'Default function role',
  description: 'Default role for function Twenty client',
  canReadAllObjectRecords: false,
  canUpdateAllObjectRecords: false,
  canSoftDeleteAllObjectRecords: false,
  canDestroyAllObjectRecords: false,
  canUpdateAllSettings: false,
  canBeAssignedToAgents: false,
  canBeAssignedToUsers: false,
  canBeAssignedToApiKeys: false,
  objectPermissions: [
    {
      objectUniversalIdentifier: '9f9882af-170c-4879-b013-f9628b77c050',
      canReadObjectRecords: true,
      canUpdateObjectRecords: true,
      canSoftDeleteObjectRecords: false,
      canDestroyObjectRecords: false,
    },
  ],
  fieldPermissions: [
    {
      objectUniversalIdentifier: '9f9882af-170c-4879-b013-f9628b77c050',
      fieldUniversalIdentifier: 'b2c37dc0-8ae7-470e-96cd-1476b47dfaff',
      canReadFieldValue: false,
      canUpdateFieldValue: false,
    },
  ],
  permissionFlags: [PermissionFlag.APPLICATIONS],
});
يُشار بعد ذلك إلى universalIdentifier لهذا الدور في application-config.ts باسم defaultRoleUniversalIdentifier. بعبارة أخرى:
  • \*.role.ts يحدد ما يمكن أن يفعله الدور الافتراضي للوظيفة.
  • application-config.ts يشير إلى ذلك الدور بحيث ترث وظائفك أذوناته.
الملاحظات:
  • ابدأ من الدور المُنشأ بالقالب، ثم قيّده تدريجيًا باتباع مبدأ أقل الامتياز.
  • استبدل objectPermissions وfieldPermissions بالكائنات/الحقول التي تحتاجها وظائفك.
  • permissionFlags تتحكم في الوصول إلى القدرات على مستوى المنصة. اجعلها في الحد الأدنى؛ أضف فقط ما تحتاجه.
  • اطّلع على مثال عملي في تطبيق Hello World: packages/twenty-apps/hello-world/src/roles/function-role.ts.

تكوين الوظيفة المنطقية ونقطة الدخول

كل ملف وظيفة يستخدم defineLogicFunction() لتصدير تكوين مع معالج ومشغّلات اختيارية.
// 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/generated';

const handler = async (params: RoutePayload) => {
  const client = new CoreApiClient();
  const name = 'name' in params.queryStringParameters
    ? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
    : 'Hello world';

  const result = await client.mutation({
    createPostCard: {
      __args: { data: { name } },
      id: true,
      name: true,
    },
  });
  return result;
};

export default defineLogicFunction({
  universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
  name: 'create-new-post-card',
  timeoutSeconds: 2,
  handler,
  triggers: [
    // Public HTTP route trigger '/s/post-card/create'
    {
      universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
      type: 'route',
      path: '/post-card/create',
      httpMethod: 'GET',
      isAuthRequired: false,
    },
    // Cron trigger (CRON pattern)
    // {
    //   universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2',
    //   type: 'cron',
    //   pattern: '0 0 1 1 *',
    // },
    // Database event trigger
    // {
    //   universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
    //   type: 'databaseEvent',
    //   eventName: 'person.updated',
    //   updatedFields: ['name'],
    // },
  ],
});
أنواع المشغلات الشائعة:
  • route: يعرِض وظيفتك على مسار وطريقة HTTP تحت نقطة النهاية /s/:
مثال: path: '/post-card/create', -> الاستدعاء على <APP_URL>/s/post-card/create
  • cron: يشغّل وظيفتك على جدول باستخدام تعبير CRON.
  • databaseEvent: يعمل على أحداث دورة حياة كائنات مساحة العمل. عندما تكون عملية الحدث هي updated، يمكن تحديد الحقول المحددة المراد الاستماع إليها في مصفوفة updatedFields. إذا تُركت غير معرّفة أو فارغة، فسيؤدي أي تحديث إلى تشغيل الدالة.
مثال: person.updated
الملاحظات:
  • المصفوفة triggers اختيارية. يمكن استخدام الوظائف بدون مشغلات كوظائف مساعدة تُستدعى بواسطة وظائف أخرى.
  • يمكنك مزج أنواع متعددة من المشغلات في وظيفة واحدة.

دوال ما قبل التثبيت

دالة ما قبل التثبيت هي دالة منطقية تعمل تلقائيًا قبل تثبيت تطبيقك على مساحة عمل. يفيد ذلك في مهام التحقق، وفحص المتطلبات المسبقة، أو تجهيز حالة مساحة العمل قبل متابعة التثبيت الرئيسي. عند إنشاء هيكل تطبيق جديد باستخدام create-twenty-app، يتم إنشاء دالة ما قبل التثبيت لك في src/logic-functions/pre-install.ts:
// src/logic-functions/pre-install.ts
import { definePreInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';

const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
  console.log('Pre install logic function executed successfully!', payload.previousVersion);
};

export default definePreInstallLogicFunction({
  universalIdentifier: '<generated-uuid>',
  name: 'pre-install',
  description: 'Runs before installation to prepare the application.',
  timeoutSeconds: 300,
  handler,
});
يمكنك أيضًا تنفيذ دالة ما قبل التثبيت يدويًا في أي وقت باستخدام CLI:
yarn twenty function:execute --preInstall
النقاط الرئيسية:
  • تستخدم دوال ما قبل التثبيت definePreInstallLogicFunction() — وهو إصدار متخصص يستبعد إعدادات المُشغِّل (cronTriggerSettings وdatabaseEventTriggerSettings وhttpRouteTriggerSettings وisTool).
  • يتلقى المُعالج InstallLogicFunctionPayload يحوي { previousVersion: string } — إصدار التطبيق الذي كان مُثبّتًا سابقًا (أو سلسلة فارغة للتثبيتات الجديدة).
  • يُسمح بدالة ما قبل التثبيت واحدة فقط لكل تطبيق. سيُنتج إنشاء ملف البيان خطأً إذا تم اكتشاف أكثر من واحدة.
  • يتم تعيين universalIdentifier للدالة تلقائيًا كـ preInstallLogicFunctionUniversalIdentifier في بيان التطبيق أثناء الإنشاء — لست بحاجة إلى الإشارة إليه في defineApplication().
  • تم ضبط المهلة الافتراضية على 300 ثانية (5 دقائق) للسماح بمهام التحضير الأطول.
  • لا تحتاج دوال ما قبل التثبيت إلى مُشغِّلات — إذ يستدعيها النظام الأساسي قبل التثبيت أو يدويًا عبر function:execute --preInstall.

دوال ما بعد التثبيت

دالة ما بعد التثبيت هي دالة منطقية تعمل تلقائيًا بعد تثبيت تطبيقك على مساحة عمل. هذا مفيد لمهام الإعداد لمرة واحدة مثل تهيئة البيانات الافتراضية، وإنشاء السجلات الأولية، أو تكوين إعدادات مساحة العمل. عند إنشاء هيكل تطبيق جديد باستخدام create-twenty-app، يتم إنشاء دالة ما بعد التثبيت لك في src/logic-functions/post-install.ts:
// src/logic-functions/post-install.ts
import { definePostInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';

const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
  console.log('Post install logic function executed successfully!', payload.previousVersion);
};

export default definePostInstallLogicFunction({
  universalIdentifier: '<generated-uuid>',
  name: 'post-install',
  description: 'Runs after installation to set up the application.',
  timeoutSeconds: 300,
  handler,
});
يمكنك أيضًا تنفيذ دالة ما بعد التثبيت يدويًا في أي وقت باستخدام CLI:
yarn twenty function:execute --postInstall
النقاط الرئيسية:
  • تستخدم دوال ما بعد التثبيت definePostInstallLogicFunction() — وهو إصدار متخصص يستبعد إعدادات المُشغِّل (cronTriggerSettings وdatabaseEventTriggerSettings وhttpRouteTriggerSettings وisTool).
  • يتلقى المُعالج InstallLogicFunctionPayload يحوي { previousVersion: string } — إصدار التطبيق الذي كان مُثبّتًا سابقًا (أو سلسلة فارغة للتثبيتات الجديدة).
  • يُسمح بدالة ما بعد التثبيت واحدة فقط لكل تطبيق. سيُنتج إنشاء ملف البيان خطأً إذا تم اكتشاف أكثر من واحدة.
  • يتم تعيين universalIdentifier للدالة تلقائيًا كـ postInstallLogicFunctionUniversalIdentifier في بيان التطبيق أثناء الإنشاء — لست بحاجة إلى الإشارة إليه في defineApplication().
  • تم تعيين مهلة افتراضية إلى 300 ثانية (5 دقائق) للسماح بمهام الإعداد الأطول مثل تهيئة البيانات.
  • لا تحتاج دوال ما بعد التثبيت إلى مُشغِّلات — حيث يستدعيها النظام الأساسي أثناء التثبيت أو يدويًا عبر function:execute --postInstall.

حمولة مشغل المسار

تغيير غير متوافق (v1.16، يناير 2026): لقد تغير تنسيق حمولة مشغل المسار. قبل v1.16، كانت معلمات الاستعلام، ومعلمات المسار، وجسم الطلب تُرسل مباشرةً كحمولة. بدءًا من v1.16، أصبحت متداخلة داخل كائن منظَّم RoutePayload.قبل v1.16:
const handler = async (params) => {
  const { param1, param2 } = params; // Direct access
};
بعد v1.16:
const handler = async (event: RoutePayload) => {
  const { param1, param2 } = event.body; // Access via .body
  const { queryParam } = event.queryStringParameters;
  const { id } = event.pathParameters;
};
لترحيل الدوال الحالية: حدّث المعالج لديك لفكّ البنية من event.body أو event.queryStringParameters أو event.pathParameters بدلاً من القراءة مباشرةً من كائن params.
عندما يستدعي مشغّل المسار وظيفتك المنطقية، يتلقى كائنًا من النوع RoutePayload يتبع تنسيق AWS HTTP API v2. استورد النوع من twenty-sdk:
import { defineLogicFunction, type RoutePayload } from 'twenty-sdk';

const handler = async (event: RoutePayload) => {
  // Access request data
  const { headers, queryStringParameters, pathParameters, body } = event;

  // HTTP method and path are available in requestContext
  const { method, path } = event.requestContext.http;

  return { message: 'Success' };
};
يحتوي نوع RoutePayload على البنية التالية:
الخاصيةالنوعالوصف
headersRecord<string, string | undefined>رؤوس HTTP (فقط تلك المدرجة في forwardedRequestHeaders)
queryStringParametersRecord<string, string | undefined>معلمات سلسلة الاستعلام (تُضمّ القيم المتعددة باستخدام فواصل)
pathParametersRecord<string, string | undefined>معلمات المسار المستخرجة من نمط المسار (على سبيل المثال، /users/:id{ id: '123' })
المحتوىobject | nullجسم الطلب المُحلَّل (JSON)
isBase64Encodedقيمة منطقيةما إذا كان جسم الطلب مُرمَّزًا بترميز base64
requestContext.http.methodstringطريقة HTTP (GET, POST, PUT, PATCH, DELETE)
requestContext.http.pathstringالمسار الخام للطلب

تمرير رؤوس HTTP

افتراضيًا، لا تُمرَّر رؤوس HTTP من الطلبات الواردة إلى دالتك المنطقية لأسباب أمنية. للوصول إلى رؤوس محددة، قم بإدراجها صراحةً في مصفوفة forwardedRequestHeaders:
export default defineLogicFunction({
  universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
  name: 'webhook-handler',
  handler,
  triggers: [
    {
      universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
      type: 'route',
      path: '/webhook',
      httpMethod: 'POST',
      isAuthRequired: false,
      forwardedRequestHeaders: ['x-webhook-signature', 'content-type'],
    },
  ],
});
في المعالج الخاص بك، يمكنك حينها الوصول إلى هذه الرؤوس:
const handler = async (event: RoutePayload) => {
  const signature = event.headers['x-webhook-signature'];
  const contentType = event.headers['content-type'];

  // Validate webhook signature...
  return { received: true };
};
تُحوَّل أسماء الرؤوس إلى أحرف صغيرة. يمكنك الوصول إليها باستخدام مفاتيح بأحرف صغيرة (على سبيل المثال، event.headers['content-type']).
يمكنك إنشاء وظائف جديدة بطريقتين:
  • مُنشأ بالقالب: شغّل yarn twenty entity:add واختر خيار إضافة دالة منطقية جديدة. يُولّد هذا ملفًا مبدئيًا مع معالج وتكوين.
  • يدوي: أنشئ ملفًا جديدًا *.logic-function.ts واستخدم defineLogicFunction() مع اتباع النمط نفسه.

تمييز دالة منطقية كأداة

يمكن إتاحة الدوال المنطقية بوصفها أدوات لوكلاء الذكاء الاصطناعي وسير العمل. عندما يتم تمييز دالة كأداة، تصبح قابلة للاكتشاف بواسطة ميزات الذكاء الاصطناعي الخاصة بـ Twenty ويمكن اختيارها كخطوة في أتمتة سير العمل. لتمييز دالة منطقية كأداة، عيّن isTool: true وقدّم toolInputSchema يصف معاملات الإدخال المتوقعة باستخدام مخطط JSON:
// src/logic-functions/enrich-company.logic-function.ts
import { defineLogicFunction } from 'twenty-sdk';
import { CoreApiClient } from 'twenty-sdk/generated';

const handler = async (params: { companyName: string; domain?: string }) => {
  const client = new CoreApiClient();

  const result = await client.mutation({
    createTask: {
      __args: {
        data: {
          title: `Enrich data for ${params.companyName}`,
          body: `Domain: ${params.domain ?? 'unknown'}`,
        },
      },
      id: true,
    },
  });

  return { taskId: result.createTask.id };
};

export default defineLogicFunction({
  universalIdentifier: 'f47ac10b-58cc-4372-a567-0e02b2c3d479',
  name: 'enrich-company',
  description: 'Enrich a company record with external data',
  timeoutSeconds: 10,
  handler,
  isTool: true,
  toolInputSchema: {
    type: 'object',
    properties: {
      companyName: {
        type: 'string',
        description: 'The name of the company to enrich',
      },
      domain: {
        type: 'string',
        description: 'The company website domain (optional)',
      },
    },
    required: ['companyName'],
  },
});
النقاط الرئيسية:
  • isTool (boolean, الافتراضي: false): عند ضبطه على true، يتم تسجيل الدالة كأداة وتصبح متاحة لوكلاء الذكاء الاصطناعي ولأتمتة سير العمل.
  • toolInputSchema (object, اختياري): كائن JSON Schema يصف المعلمات التي تقبلها دالتك. يستخدم وكلاء الذكاء الاصطناعي هذا المخطط لفهم المدخلات التي تتوقعها الأداة وللتحقق من صحة الاستدعاءات. إذا تم إغفاله، فالقيمة الافتراضية للمخطط هي { type: 'object', properties: {} } (من دون معلمات).
  • الدوال التي لديها isTool: false (أو غير معيَّنة) غير معروضة كأدوات. لا يزال بالإمكان تنفيذها مباشرةً أو استدعاؤها بواسطة دوال أخرى، لكنها لن تظهر في اكتشاف الأدوات.
  • تسمية الأداة: عند كشفها كأداة، يتم تطبيع اسم الدالة تلقائيًا إلى logic_function_<name> (تحويله إلى أحرف صغيرة، واستبدال المحارف غير الأبجدية الرقمية بشرطات سفلية). على سبيل المثال، enrich-company تصبح logic_function_enrich_company.
  • يمكنك دمج isTool مع المشغِّلات — إذ يمكن للدالة أن تكون أداة (قابلة للاستدعاء من قِبل وكلاء الذكاء الاصطناعي) وأن تُشغَّل بواسطة أحداث (cron، وأحداث قاعدة البيانات، والمسارات) في الوقت نفسه.
اكتب description جيدًا. يعتمد وكلاء الذكاء الاصطناعي على حقل description الخاص بالدالة لتحديد وقت استخدام الأداة. كن محددًا بشأن ما تفعله الأداة ومتى ينبغي استدعاؤها.

المكوّنات الأمامية

تتيح لك المكوّنات الأمامية إنشاء مكوّنات React مخصّصة تُعرَض داخل واجهة مستخدم Twenty. استخدم defineFrontComponent() لتعريف مكوّنات مع تحقّق مدمج:
// src/front-components/my-widget.tsx
import { defineFrontComponent } from 'twenty-sdk';

const MyWidget = () => {
  return (
    <div style={{ padding: '20px', fontFamily: 'sans-serif' }}>
      <h1>My Custom Widget</h1>
      <p>This is a custom front component for Twenty.</p>
    </div>
  );
};

export default defineFrontComponent({
  universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
  name: 'my-widget',
  description: 'A custom widget component',
  component: MyWidget,
});
النقاط الرئيسية:
  • المكوّنات الأمامية هي مكوّنات React تُعرَض ضمن سياقات معزولة داخل Twenty.
  • يشير الحقل component إلى مكوّن React الخاص بك.
  • يتم بناء المكوّنات ومزامنتها تلقائيًا أثناء yarn twenty app:dev.
يمكنك إنشاء مكوّنات أمامية جديدة بطريقتين:
  • مُنشأ بالقالب: شغّل yarn twenty entity:add واختر خيار إضافة مكوّن أمامي جديد.
  • يدوي: أنشئ ملفًا جديدًا .tsx واستخدم defineFrontComponent() مع اتباع النمط نفسه.

المهارات

تُحدِّد المهارات تعليمات وإمكانات قابلة لإعادة الاستخدام يمكن لوكلاء الذكاء الاصطناعي استخدامها داخل مساحة العمل لديك. استخدم defineSkill() لتعريف مهارات مع تحقّق مدمج:
// src/skills/example-skill.ts
import { defineSkill } from 'twenty-sdk';

export default defineSkill({
  universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
  name: 'sales-outreach',
  label: 'Sales Outreach',
  description: 'Guides the AI agent through a structured sales outreach process',
  icon: 'IconBrain',
  content: `You are a sales outreach assistant. When reaching out to a prospect:
1. Research the company and recent news
2. Identify the prospect's role and likely pain points
3. Draft a personalized message referencing specific details
4. Keep the tone professional but conversational`,
});
النقاط الرئيسية:
  • name هي سلسلة معرّف فريدة للمهارة (يُنصَح باستخدام kebab-case).
  • label هو اسم العرض المقروء للبشر الظاهر في واجهة المستخدم.
  • content يحتوي على تعليمات المهارة — وهو النص الذي يستخدمه وكيل الذكاء الاصطناعي.
  • icon (اختياري) يحدّد الأيقونة المعروضة في واجهة المستخدم.
  • description (اختياري) يوفّر سياقًا إضافيًا حول غرض المهارة.
يمكنك إنشاء مهارات جديدة بطريقتين:
  • مُنشأ بالقالب: شغِّل yarn twenty entity:add واختر خيار إضافة مهارة جديدة.
  • يدوي: أنشئ ملفًا جديدًا واستخدم defineSkill() مع اتباع النمط نفسه.

عملاء مُولَّدون مضبوطو الأنواع

يتم توليد عميلين مضبوطي الأنواع تلقائيًا بواسطة yarn twenty app:dev وتخزينهما في node_modules/twenty-sdk/generated استنادًا إلى مخطط مساحة العمل لديك:
  • CoreApiClient — يُجري استعلامات إلى نقطة النهاية /graphql للحصول على بيانات مساحة العمل
  • MetadataApiClient — يستعلم عن نقطة النهاية /metadata لتكوين مساحة العمل وتحميل الملفات.
import { CoreApiClient, MetadataApiClient } from 'twenty-sdk/generated';

const client = new CoreApiClient();
const { me } = await client.query({ me: { id: true, displayName: true } });

const metadataClient = new MetadataApiClient();
const { currentWorkspace } = await metadataClient.query({ currentWorkspace: { id: true } });
يُعاد توليد كلا العميلين تلقائيًا بواسطة yarn twenty app:dev كلما تغيّرت كائناتك أو حقولك.

بيانات الاعتماد وقت التشغيل في الدوال المنطقية

عندما تعمل دالتك على Twenty، يقوم النظام الأساسي بحقن بيانات الاعتماد كمتغيرات بيئة قبل تنفيذ كودك:
  • TWENTY_API_URL: عنوان URL الأساسي لواجهة Twenty البرمجية التي يستهدفها تطبيقك.
  • TWENTY_API_KEY: مفتاح قصير العمر ذو نطاق يقتصر على الدور الافتراضي لوظيفة تطبيقك.
الملاحظات:
  • لا تحتاج إلى تمرير عنوان URL أو مفتاح واجهة برمجة التطبيقات إلى العميل المُولَّد. يقوم بقراءة TWENTY_API_URL وTWENTY_API_KEY من process.env وقت التشغيل.
  • تُحدَّد أذونات مفتاح واجهة برمجة التطبيقات بواسطة الدور المشار إليه في application-config.ts عبر defaultRoleUniversalIdentifier. هذا هو الدور الافتراضي الذي تستخدمه الدوال المنطقية في تطبيقك.
  • يمكن للتطبيقات تعريف أدوار لاتباع مبدأ أقل الامتياز. امنح فقط الأذونات التي تحتاجها وظائفك، ثم وجّه defaultRoleUniversalIdentifier إلى المعرّف الشامل لذلك الدور.

رفع الملفات

يتضمن MetadataApiClient المُولَّد طريقة uploadFile لإرفاق الملفات بالحقول من نوع ملف ضمن كائنات مساحة العمل الخاصة بك. نظرًا لأن عملاء GraphQL القياسيون لا يدعمون تحميل الملفات متعددة الأجزاء افتراضيًا، يوفر العميل هذه الطريقة المخصصة التي تطبق مواصفة طلب GraphQL متعدد الأجزاء في الخلفية.
import { MetadataApiClient } from 'twenty-sdk/generated';
import * as fs from 'fs';

const metadataClient = new MetadataApiClient();

const fileBuffer = fs.readFileSync('./invoice.pdf');

const uploadedFile = await metadataClient.uploadFile(
  fileBuffer,                                         // file contents as a Buffer
  'invoice.pdf',                                      // filename
  'application/pdf',                                  // MIME type (defaults to 'application/octet-stream')
  '58a0a314-d7ea-4865-9850-7fb84e72f30b',            // field universal identifier
);

console.log(uploadedFile);
// { id: '...', path: '...', size: 12345, createdAt: '...', url: 'https://...' }
توقيع الطريقة:
uploadFile(
  fileBuffer: Buffer,
  filename: string,
  contentType: string,
  fieldMetadataUniversalIdentifier: string,
): Promise<{ id: string; path: string; size: number; createdAt: string; url: string }>
المعلمةالنوعالوصف
fileBufferBufferالمحتوى الخام للملف
filenamestringاسم الملف (يُستخدم للتخزين والعرض)
contentTypestringنوع MIME للملف (القيمة الافتراضية هي application/octet-stream إذا لم يتم تحديده)
fieldMetadataUniversalIdentifierstringقيمة universalIdentifier لحقل نوع الملف في كائنك
النقاط الرئيسية:
  • تتوفر طريقة uploadFile على MetadataApiClient لأن عملية الـ mutation الخاصة بالرفع تُعالَج عبر نقطة النهاية /metadata.
  • تستخدم universalIdentifier الخاص بالحقل (وليس المعرّف الخاص بمساحة العمل)، ليعمل كود الرفع لديك عبر أي مساحة عمل مُثبَّت فيها تطبيقك — بما يتماشى مع كيفية إشارة التطبيقات إلى الحقول في كل مكان آخر.
  • العنوان url المُعاد هو عنوان URL موقّع يمكنك استخدامه للوصول إلى الملف المرفوع.

مثال Hello World

استكشف مثالًا بسيطًا شاملًا من البداية إلى النهاية يوضح الكائنات والوظائف المنطقية والمكوّنات الأمامية ومشغّلات متعددة هنا.