twenty-sdk 2.53.0 → 2.54.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -54,6 +54,18 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
54
54
  const text = `${await inboxMention({ workspaceMemberId })}, the report is ready.`;
55
55
  ```
56
56
 
57
+ - **`attachRecordToInboxThread` and `detachRecordFromInboxThread` in `twenty-sdk/logic-function` and `twenty-sdk/front-component`.** Link a conversation to a person, company, opportunity or custom object record, or remove the link. Both are idempotent and run like the default API clients: a member must be able to edit the conversation, and your application can act on a conversation it wrote in or one its default role was granted. The record must be readable with the same access.
58
+
59
+ ```ts
60
+ import { attachRecordToInboxThread } from 'twenty-sdk/logic-function';
61
+
62
+ await attachRecordToInboxThread({
63
+ threadId,
64
+ objectNameSingular: 'company',
65
+ recordId: company.id,
66
+ });
67
+ ```
68
+
57
69
  - **`useSelectedRecordsFilter` in `twenty-sdk/front-component`.** Returns a record filter matching exactly the records the user selected, or `null` when there is no selection context. Under Select all, `useSelectedRecordIds()` is empty because the selection is defined by the view's filters, search and unticked rows; query the records with this filter, or pass it to a logic function. Twenty servers that predate it return `null`.
58
70
 
59
71
  ```tsx
@@ -81,10 +93,14 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
81
93
  const objectMetadata = useSelectedObjectMetadata();
82
94
  ```
83
95
 
96
+ - **`SystemPermissionFlag.USER_APPS` and `SystemPermissionFlag.WORKSPACE_APPS` in `twenty-sdk/define`.** The SDK names of the User Apps and Workspace Apps permission flags, with the same universal identifiers as `CONNECTED_ACCOUNTS` and `APPLICATIONS`. They are left out of `Object.keys`, `Object.values` and `Object.entries`, which list the same 26 names as before, so code that enumerates or filters the flags behaves as before. Only the SDK names change: the API keeps reporting these flags as `CONNECTED_ACCOUNTS` and `APPLICATIONS`, for example in `currentUserWorkspace.permissionFlags` and in command menu expressions.
97
+
84
98
  ### Deprecated
85
99
 
86
100
  - **`enqueueJob` in `twenty-sdk/logic-function`.** Use `enqueueJobs` with a one-element `payloads` list instead. `enqueueJob` keeps working for now and will be removed in a future major version.
87
101
 
102
+ - **`SystemPermissionFlag.CONNECTED_ACCOUNTS` and `SystemPermissionFlag.APPLICATIONS` in `twenty-sdk/define`.** Use `USER_APPS` and `WORKSPACE_APPS` instead. The old names keep working and stay the ones `Object.keys` and `Object.entries` return. In logic functions and front components the new names resolve only when bundled with the `twenty` CLI 2.54 or later.
103
+
88
104
  ### Changed
89
105
 
90
106
  - **`sendInboxMessage` runs like the default API clients.** It now sends through `MetadataApiClient`, so in a logic function it acts as the person who triggered the run, who then needs the AI permission, and as your application only when nobody did; it used to always use the application's access. A GraphQL error now rejects with the server's message instead of `sendInboxMessage() failed: ...`. Its input and result are unchanged.
@@ -102,6 +118,12 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
102
118
 
103
119
  `twenty build` now also emits a warning when `twenty-client-sdk` is still listed under `dependencies`.
104
120
 
121
+ - **`defineRole` and `defineApplicationRole` keep each permission flag once in `permissionFlagUniversalIdentifiers`.** A flag listed twice, for example under its deprecated and new `SystemPermissionFlag` names, is sent once. Syncing a role with a repeated flag used to fail on the server.
122
+
123
+ - **`twenty app pull` names permission flags after the app's installed `twenty-sdk`.** Role files get `SystemPermissionFlag.USER_APPS` and `SystemPermissionFlag.WORKSPACE_APPS` with `twenty-sdk` 2.54 or later, and `CONNECTED_ACCOUNTS` and `APPLICATIONS` with earlier versions. A flag the installed `SystemPermissionFlag` lacks is written as its universal identifier string.
124
+
125
+ - **`twenty app build` and `twenty app dev` keep non-enumerable `twenty-sdk/define` constants in logic function and front component bundles,** so `SystemPermissionFlag.USER_APPS` and `WORKSPACE_APPS` resolve at runtime. Agent role errors also name system permission flags after the app's installed `twenty-sdk`.
126
+
105
127
  ## [2.8.0]
106
128
 
107
129
  ### Breaking Changes
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../../../twenty-shared/dist/application-runtime.mjs","../../src/sdk/utils/get-application-access-token.ts","../../src/sdk/billing/charge-credits.ts","../../src/sdk/billing/get-credit-availability.ts"],"sourcesContent":["//#region src/application-runtime/constants/ConnectionScopeByLegacyVisibility.ts\nvar e = {\n\tuser: \"USER\",\n\tworkspace: \"WORKSPACE\"\n}, t = \"TWENTY_API_KEY\", n = \"TWENTY_API_URL\", r = \"TWENTY_APP_ACCESS_TOKEN\", i = \"TWENTY_APP_APPLICATION_ACCESS_TOKEN\", a = \"TWENTY_FUNCTIONS_URL\", o = {\n\tUSER: \"user\",\n\tWORKSPACE: \"workspace\"\n}, s = [\n\t\"workspace-suspended\",\n\t\"no-subscription\",\n\t\"no-credits\"\n];\n//#endregion\nexport { e as CONNECTION_SCOPE_BY_LEGACY_VISIBILITY, s as CREDIT_UNAVAILABLE_REASONS, t as DEFAULT_API_KEY_NAME, n as DEFAULT_API_URL_NAME, r as DEFAULT_APP_ACCESS_TOKEN_NAME, i as DEFAULT_APP_APPLICATION_ACCESS_TOKEN_NAME, a as DEFAULT_FUNCTIONS_URL_NAME, o as LEGACY_CONNECTION_VISIBILITY_BY_SCOPE };\n","import {\n DEFAULT_APP_ACCESS_TOKEN_NAME,\n DEFAULT_APP_APPLICATION_ACCESS_TOKEN_NAME,\n} from 'twenty-shared/application-runtime';\n\n// These reach the application's own resources, so prefer the application token when the runtime injects one\nexport const getApplicationAccessToken = (): string | undefined =>\n process.env[DEFAULT_APP_APPLICATION_ACCESS_TOKEN_NAME] ??\n process.env[DEFAULT_APP_ACCESS_TOKEN_NAME];\n","import { type UsageOperationTypeValue } from 'twenty-shared/application';\nimport { DEFAULT_API_URL_NAME } from 'twenty-shared/application-runtime';\n\nimport { getApplicationAccessToken } from '@/sdk/utils/get-application-access-token';\n\nconst BILLING_CHARGE_TIMEOUT_MS = 5_000;\n\nexport type ChargeCreditsParams = {\n creditsUsedMicro: number;\n quantity?: number;\n // Who the spend belongs to when no user triggered the run (webhook, cron); ignored otherwise\n userWorkspaceId?: string;\n} & (\n | {\n // An operation declared in the manifest's `billing.operations`; the platform resolves its category and label\n operation: string;\n operationType?: never;\n resourceContext?: never;\n }\n | {\n // For applications that declare no billable operations: names the platform billing category directly\n operationType: UsageOperationTypeValue;\n operation?: never;\n resourceContext?: string;\n }\n);\n\n// No-ops without an API URL or access token, and never throws: a billing error must not fail a tool\nexport const chargeCredits = async ({\n creditsUsedMicro,\n quantity = 1,\n operation,\n operationType,\n resourceContext,\n userWorkspaceId,\n}: ChargeCreditsParams): Promise<void> => {\n const apiUrl = process.env[DEFAULT_API_URL_NAME];\n const token = getApplicationAccessToken();\n\n if (!apiUrl || !token) {\n return;\n }\n\n try {\n const response = await fetch(\n `${apiUrl.replace(/\\/$/, '')}/app/billing/charge`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n creditsUsedMicro,\n quantity,\n operation,\n operationType,\n resourceContext,\n userWorkspaceId,\n }),\n signal: AbortSignal.timeout(BILLING_CHARGE_TIMEOUT_MS),\n },\n );\n\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n\n console.error(\n `chargeCredits: ${response.status} ${response.statusText}: ${body}`,\n );\n }\n } catch (error) {\n console.error(\n `chargeCredits: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n};\n","import {\n CREDIT_UNAVAILABLE_REASONS,\n type CreditAvailability,\n type CreditUnavailableReason,\n DEFAULT_API_URL_NAME,\n} from 'twenty-shared/application-runtime';\n\nimport { getApplicationAccessToken } from '@/sdk/utils/get-application-access-token';\n\nconst CREDIT_AVAILABILITY_TIMEOUT_MS = 5_000;\n\n// A fresh object per call, since app code could mutate a shared constant\nconst available = (): CreditAvailability => ({ hasAvailableCredits: true });\n\nconst parseCreditAvailability = (body: unknown): CreditAvailability | null => {\n if (typeof body !== 'object' || body === null) {\n return null;\n }\n\n const { hasAvailableCredits, reason } = body as {\n hasAvailableCredits?: unknown;\n reason?: unknown;\n };\n\n if (hasAvailableCredits === true) {\n return { hasAvailableCredits: true };\n }\n\n if (\n hasAvailableCredits === false &&\n CREDIT_UNAVAILABLE_REASONS.includes(reason as CreditUnavailableReason)\n ) {\n return {\n hasAvailableCredits: false,\n reason: reason as CreditUnavailableReason,\n };\n }\n\n return null;\n};\n\n// Fails open on timeouts or bad responses: a hint before expensive work, never an authorization decision\nexport const getCreditAvailability = async (): Promise<CreditAvailability> => {\n const apiUrl = process.env[DEFAULT_API_URL_NAME];\n const token = getApplicationAccessToken();\n\n if (!apiUrl || !token) {\n return available();\n }\n\n try {\n const response = await fetch(\n `${apiUrl.replace(/\\/$/, '')}/app/billing/credits`,\n {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/json',\n },\n signal: AbortSignal.timeout(CREDIT_AVAILABILITY_TIMEOUT_MS),\n },\n );\n\n if (!response.ok) {\n console.error(\n `getCreditAvailability: ${response.status} ${response.statusText}`,\n );\n\n return available();\n }\n\n const creditAvailability = parseCreditAvailability(await response.json());\n\n if (creditAvailability === null) {\n console.error('getCreditAvailability: unexpected response shape');\n\n return available();\n }\n\n return creditAvailability;\n } catch (error) {\n console.error(\n `getCreditAvailability: ${error instanceof Error ? error.message : String(error)}`,\n );\n\n return available();\n }\n};\n"],"mappings":"uEAIyB,EAAI,iBAG1B,EAAI,CACN,sBACA,kBACA,YACD,ECLa,MACX,QAAQ,IAAA,qCACR,QAAQ,IAAA,wBCHJ,EAA4B,IAuBrB,EAAgB,MAAO,CAClC,mBACA,WAAW,EACX,YACA,gBACA,kBACA,qBACwC,CACxC,IAAM,EAAS,QAAQ,IAAI,GACrB,EAAQ,EAA0B,EAEpC,MAAC,GAAU,CAAC,GAIhB,GAAI,CACF,IAAM,EAAW,MAAM,MACrB,GAAG,EAAO,QAAQ,MAAO,EAAE,EAAE,qBAC7B,CACE,OAAQ,OACR,QAAS,CACP,cAAe,UAAU,IACzB,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CACnB,mBACA,WACA,YACA,gBACA,kBACA,iBACF,CAAC,EACD,OAAQ,YAAY,QAAQ,CAAyB,CACvD,CACF,EAEA,GAAI,CAAC,EAAS,GAAI,CAChB,IAAM,EAAO,MAAM,EAAS,KAAK,CAAC,CAAC,UAAY,EAAE,EAEjD,QAAQ,MACN,kBAAkB,EAAS,OAAO,GAAG,EAAS,WAAW,IAAI,GAC/D,CACF,CACF,OAAS,EAAO,CACd,QAAQ,MACN,kBAAkB,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,GACzE,CACF,CACF,ECnEM,EAAiC,IAGjC,OAAuC,CAAE,oBAAqB,EAAK,GAEnE,EAA2B,GAA6C,CAC5E,GAAI,OAAO,GAAS,WAAY,EAC9B,OAAO,KAGT,GAAM,CAAE,sBAAqB,UAAW,EAmBxC,OAdI,IAAwB,GACnB,CAAE,oBAAqB,EAAK,EAInC,IAAwB,IACxB,EAA2B,SAAS,CAAiC,EAE9D,CACL,oBAAqB,GACb,QACV,EAGK,IACT,EAGa,EAAwB,SAAyC,CAC5E,IAAM,EAAS,QAAQ,IAAI,GACrB,EAAQ,EAA0B,EAExC,GAAI,CAAC,GAAU,CAAC,EACd,OAAO,EAAU,EAGnB,GAAI,CACF,IAAM,EAAW,MAAM,MACrB,GAAG,EAAO,QAAQ,MAAO,EAAE,EAAE,sBAC7B,CACE,OAAQ,MACR,QAAS,CACP,cAAe,UAAU,IACzB,OAAQ,kBACV,EACA,OAAQ,YAAY,QAAQ,CAA8B,CAC5D,CACF,EAEA,GAAI,CAAC,EAAS,GAKZ,OAJA,QAAQ,MACN,0BAA0B,EAAS,OAAO,GAAG,EAAS,YACxD,EAEO,EAAU,EAGnB,IAAM,EAAqB,EAAwB,MAAM,EAAS,KAAK,CAAC,EAQxE,OANI,IAAuB,MACzB,QAAQ,MAAM,kDAAkD,EAEzD,EAAU,GAGZ,CACT,OAAS,EAAO,CAKd,OAJA,QAAQ,MACN,0BAA0B,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,GACjF,EAEO,EAAU,CACnB,CACF"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../../twenty-shared/dist/application-runtime.mjs","../../src/sdk/utils/get-application-access-token.ts","../../src/sdk/billing/charge-credits.ts","../../src/sdk/billing/get-credit-availability.ts"],"sourcesContent":["//#region src/application-runtime/constants/ConnectionScopeByLegacyVisibility.ts\nvar e = {\n\tuser: \"USER\",\n\tworkspace: \"WORKSPACE\"\n}, t = \"TWENTY_API_KEY\", n = \"TWENTY_API_URL\", r = \"TWENTY_APP_ACCESS_TOKEN\", i = \"TWENTY_APP_APPLICATION_ACCESS_TOKEN\", a = \"TWENTY_FUNCTIONS_URL\", o = \"TWENTY_SERVER_ROUTES_URL\", s = {\n\tUSER: \"user\",\n\tWORKSPACE: \"workspace\"\n}, c = [\n\t\"workspace-suspended\",\n\t\"no-subscription\",\n\t\"no-credits\"\n];\n//#endregion\nexport { e as CONNECTION_SCOPE_BY_LEGACY_VISIBILITY, c as CREDIT_UNAVAILABLE_REASONS, t as DEFAULT_API_KEY_NAME, n as DEFAULT_API_URL_NAME, r as DEFAULT_APP_ACCESS_TOKEN_NAME, i as DEFAULT_APP_APPLICATION_ACCESS_TOKEN_NAME, a as DEFAULT_FUNCTIONS_URL_NAME, o as DEFAULT_SERVER_ROUTES_URL_NAME, s as LEGACY_CONNECTION_VISIBILITY_BY_SCOPE };\n","import {\n DEFAULT_APP_ACCESS_TOKEN_NAME,\n DEFAULT_APP_APPLICATION_ACCESS_TOKEN_NAME,\n} from 'twenty-shared/application-runtime';\n\n// These reach the application's own resources, so prefer the application token when the runtime injects one\nexport const getApplicationAccessToken = (): string | undefined =>\n process.env[DEFAULT_APP_APPLICATION_ACCESS_TOKEN_NAME] ??\n process.env[DEFAULT_APP_ACCESS_TOKEN_NAME];\n","import { type UsageOperationTypeValue } from 'twenty-shared/application';\nimport { DEFAULT_API_URL_NAME } from 'twenty-shared/application-runtime';\n\nimport { getApplicationAccessToken } from '@/sdk/utils/get-application-access-token';\n\nconst BILLING_CHARGE_TIMEOUT_MS = 5_000;\n\nexport type ChargeCreditsParams = {\n creditsUsedMicro: number;\n quantity?: number;\n // Who the spend belongs to when no user triggered the run (webhook, cron); ignored otherwise\n userWorkspaceId?: string;\n} & (\n | {\n // An operation declared in the manifest's `billing.operations`; the platform resolves its category and label\n operation: string;\n operationType?: never;\n resourceContext?: never;\n }\n | {\n // For applications that declare no billable operations: names the platform billing category directly\n operationType: UsageOperationTypeValue;\n operation?: never;\n resourceContext?: string;\n }\n);\n\n// No-ops without an API URL or access token, and never throws: a billing error must not fail a tool\nexport const chargeCredits = async ({\n creditsUsedMicro,\n quantity = 1,\n operation,\n operationType,\n resourceContext,\n userWorkspaceId,\n}: ChargeCreditsParams): Promise<void> => {\n const apiUrl = process.env[DEFAULT_API_URL_NAME];\n const token = getApplicationAccessToken();\n\n if (!apiUrl || !token) {\n return;\n }\n\n try {\n const response = await fetch(\n `${apiUrl.replace(/\\/$/, '')}/app/billing/charge`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n creditsUsedMicro,\n quantity,\n operation,\n operationType,\n resourceContext,\n userWorkspaceId,\n }),\n signal: AbortSignal.timeout(BILLING_CHARGE_TIMEOUT_MS),\n },\n );\n\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n\n console.error(\n `chargeCredits: ${response.status} ${response.statusText}: ${body}`,\n );\n }\n } catch (error) {\n console.error(\n `chargeCredits: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n};\n","import {\n CREDIT_UNAVAILABLE_REASONS,\n type CreditAvailability,\n type CreditUnavailableReason,\n DEFAULT_API_URL_NAME,\n} from 'twenty-shared/application-runtime';\n\nimport { getApplicationAccessToken } from '@/sdk/utils/get-application-access-token';\n\nconst CREDIT_AVAILABILITY_TIMEOUT_MS = 5_000;\n\n// A fresh object per call, since app code could mutate a shared constant\nconst available = (): CreditAvailability => ({ hasAvailableCredits: true });\n\nconst parseCreditAvailability = (body: unknown): CreditAvailability | null => {\n if (typeof body !== 'object' || body === null) {\n return null;\n }\n\n const { hasAvailableCredits, reason } = body as {\n hasAvailableCredits?: unknown;\n reason?: unknown;\n };\n\n if (hasAvailableCredits === true) {\n return { hasAvailableCredits: true };\n }\n\n if (\n hasAvailableCredits === false &&\n CREDIT_UNAVAILABLE_REASONS.includes(reason as CreditUnavailableReason)\n ) {\n return {\n hasAvailableCredits: false,\n reason: reason as CreditUnavailableReason,\n };\n }\n\n return null;\n};\n\n// Fails open on timeouts or bad responses: a hint before expensive work, never an authorization decision\nexport const getCreditAvailability = async (): Promise<CreditAvailability> => {\n const apiUrl = process.env[DEFAULT_API_URL_NAME];\n const token = getApplicationAccessToken();\n\n if (!apiUrl || !token) {\n return available();\n }\n\n try {\n const response = await fetch(\n `${apiUrl.replace(/\\/$/, '')}/app/billing/credits`,\n {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/json',\n },\n signal: AbortSignal.timeout(CREDIT_AVAILABILITY_TIMEOUT_MS),\n },\n );\n\n if (!response.ok) {\n console.error(\n `getCreditAvailability: ${response.status} ${response.statusText}`,\n );\n\n return available();\n }\n\n const creditAvailability = parseCreditAvailability(await response.json());\n\n if (creditAvailability === null) {\n console.error('getCreditAvailability: unexpected response shape');\n\n return available();\n }\n\n return creditAvailability;\n } catch (error) {\n console.error(\n `getCreditAvailability: ${error instanceof Error ? error.message : String(error)}`,\n );\n\n return available();\n }\n};\n"],"mappings":"uEAIyB,EAAI,iBAG1B,EAAI,CACN,sBACA,kBACA,YACD,ECLa,MACX,QAAQ,IAAA,qCACR,QAAQ,IAAA,wBCHJ,EAA4B,IAuBrB,EAAgB,MAAO,CAClC,mBACA,WAAW,EACX,YACA,gBACA,kBACA,qBACwC,CACxC,IAAM,EAAS,QAAQ,IAAI,GACrB,EAAQ,EAA0B,EAEpC,MAAC,GAAU,CAAC,GAIhB,GAAI,CACF,IAAM,EAAW,MAAM,MACrB,GAAG,EAAO,QAAQ,MAAO,EAAE,EAAE,qBAC7B,CACE,OAAQ,OACR,QAAS,CACP,cAAe,UAAU,IACzB,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CACnB,mBACA,WACA,YACA,gBACA,kBACA,iBACF,CAAC,EACD,OAAQ,YAAY,QAAQ,CAAyB,CACvD,CACF,EAEA,GAAI,CAAC,EAAS,GAAI,CAChB,IAAM,EAAO,MAAM,EAAS,KAAK,CAAC,CAAC,UAAY,EAAE,EAEjD,QAAQ,MACN,kBAAkB,EAAS,OAAO,GAAG,EAAS,WAAW,IAAI,GAC/D,CACF,CACF,OAAS,EAAO,CACd,QAAQ,MACN,kBAAkB,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,GACzE,CACF,CACF,ECnEM,EAAiC,IAGjC,OAAuC,CAAE,oBAAqB,EAAK,GAEnE,EAA2B,GAA6C,CAC5E,GAAI,OAAO,GAAS,WAAY,EAC9B,OAAO,KAGT,GAAM,CAAE,sBAAqB,UAAW,EAmBxC,OAdI,IAAwB,GACnB,CAAE,oBAAqB,EAAK,EAInC,IAAwB,IACxB,EAA2B,SAAS,CAAiC,EAE9D,CACL,oBAAqB,GACb,QACV,EAGK,IACT,EAGa,EAAwB,SAAyC,CAC5E,IAAM,EAAS,QAAQ,IAAI,GACrB,EAAQ,EAA0B,EAExC,GAAI,CAAC,GAAU,CAAC,EACd,OAAO,EAAU,EAGnB,GAAI,CACF,IAAM,EAAW,MAAM,MACrB,GAAG,EAAO,QAAQ,MAAO,EAAE,EAAE,sBAC7B,CACE,OAAQ,MACR,QAAS,CACP,cAAe,UAAU,IACzB,OAAQ,kBACV,EACA,OAAQ,YAAY,QAAQ,CAA8B,CAC5D,CACF,EAEA,GAAI,CAAC,EAAS,GAKZ,OAJA,QAAQ,MACN,0BAA0B,EAAS,OAAO,GAAG,EAAS,YACxD,EAEO,EAAU,EAGnB,IAAM,EAAqB,EAAwB,MAAM,EAAS,KAAK,CAAC,EAQxE,OANI,IAAuB,MACzB,QAAQ,MAAM,kDAAkD,EAEzD,EAAU,GAGZ,CACT,OAAS,EAAO,CAKd,OAJA,QAAQ,MACN,0BAA0B,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,GACjF,EAEO,EAAU,CACnB,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../twenty-shared/dist/application-runtime.mjs","../../src/sdk/utils/get-application-access-token.ts","../../src/sdk/billing/charge-credits.ts","../../src/sdk/billing/get-credit-availability.ts"],"sourcesContent":["//#region src/application-runtime/constants/ConnectionScopeByLegacyVisibility.ts\nvar e = {\n\tuser: \"USER\",\n\tworkspace: \"WORKSPACE\"\n}, t = \"TWENTY_API_KEY\", n = \"TWENTY_API_URL\", r = \"TWENTY_APP_ACCESS_TOKEN\", i = \"TWENTY_APP_APPLICATION_ACCESS_TOKEN\", a = \"TWENTY_FUNCTIONS_URL\", o = {\n\tUSER: \"user\",\n\tWORKSPACE: \"workspace\"\n}, s = [\n\t\"workspace-suspended\",\n\t\"no-subscription\",\n\t\"no-credits\"\n];\n//#endregion\nexport { e as CONNECTION_SCOPE_BY_LEGACY_VISIBILITY, s as CREDIT_UNAVAILABLE_REASONS, t as DEFAULT_API_KEY_NAME, n as DEFAULT_API_URL_NAME, r as DEFAULT_APP_ACCESS_TOKEN_NAME, i as DEFAULT_APP_APPLICATION_ACCESS_TOKEN_NAME, a as DEFAULT_FUNCTIONS_URL_NAME, o as LEGACY_CONNECTION_VISIBILITY_BY_SCOPE };\n","import {\n DEFAULT_APP_ACCESS_TOKEN_NAME,\n DEFAULT_APP_APPLICATION_ACCESS_TOKEN_NAME,\n} from 'twenty-shared/application-runtime';\n\n// These reach the application's own resources, so prefer the application token when the runtime injects one\nexport const getApplicationAccessToken = (): string | undefined =>\n process.env[DEFAULT_APP_APPLICATION_ACCESS_TOKEN_NAME] ??\n process.env[DEFAULT_APP_ACCESS_TOKEN_NAME];\n","import { type UsageOperationTypeValue } from 'twenty-shared/application';\nimport { DEFAULT_API_URL_NAME } from 'twenty-shared/application-runtime';\n\nimport { getApplicationAccessToken } from '@/sdk/utils/get-application-access-token';\n\nconst BILLING_CHARGE_TIMEOUT_MS = 5_000;\n\nexport type ChargeCreditsParams = {\n creditsUsedMicro: number;\n quantity?: number;\n // Who the spend belongs to when no user triggered the run (webhook, cron); ignored otherwise\n userWorkspaceId?: string;\n} & (\n | {\n // An operation declared in the manifest's `billing.operations`; the platform resolves its category and label\n operation: string;\n operationType?: never;\n resourceContext?: never;\n }\n | {\n // For applications that declare no billable operations: names the platform billing category directly\n operationType: UsageOperationTypeValue;\n operation?: never;\n resourceContext?: string;\n }\n);\n\n// No-ops without an API URL or access token, and never throws: a billing error must not fail a tool\nexport const chargeCredits = async ({\n creditsUsedMicro,\n quantity = 1,\n operation,\n operationType,\n resourceContext,\n userWorkspaceId,\n}: ChargeCreditsParams): Promise<void> => {\n const apiUrl = process.env[DEFAULT_API_URL_NAME];\n const token = getApplicationAccessToken();\n\n if (!apiUrl || !token) {\n return;\n }\n\n try {\n const response = await fetch(\n `${apiUrl.replace(/\\/$/, '')}/app/billing/charge`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n creditsUsedMicro,\n quantity,\n operation,\n operationType,\n resourceContext,\n userWorkspaceId,\n }),\n signal: AbortSignal.timeout(BILLING_CHARGE_TIMEOUT_MS),\n },\n );\n\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n\n console.error(\n `chargeCredits: ${response.status} ${response.statusText}: ${body}`,\n );\n }\n } catch (error) {\n console.error(\n `chargeCredits: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n};\n","import {\n CREDIT_UNAVAILABLE_REASONS,\n type CreditAvailability,\n type CreditUnavailableReason,\n DEFAULT_API_URL_NAME,\n} from 'twenty-shared/application-runtime';\n\nimport { getApplicationAccessToken } from '@/sdk/utils/get-application-access-token';\n\nconst CREDIT_AVAILABILITY_TIMEOUT_MS = 5_000;\n\n// A fresh object per call, since app code could mutate a shared constant\nconst available = (): CreditAvailability => ({ hasAvailableCredits: true });\n\nconst parseCreditAvailability = (body: unknown): CreditAvailability | null => {\n if (typeof body !== 'object' || body === null) {\n return null;\n }\n\n const { hasAvailableCredits, reason } = body as {\n hasAvailableCredits?: unknown;\n reason?: unknown;\n };\n\n if (hasAvailableCredits === true) {\n return { hasAvailableCredits: true };\n }\n\n if (\n hasAvailableCredits === false &&\n CREDIT_UNAVAILABLE_REASONS.includes(reason as CreditUnavailableReason)\n ) {\n return {\n hasAvailableCredits: false,\n reason: reason as CreditUnavailableReason,\n };\n }\n\n return null;\n};\n\n// Fails open on timeouts or bad responses: a hint before expensive work, never an authorization decision\nexport const getCreditAvailability = async (): Promise<CreditAvailability> => {\n const apiUrl = process.env[DEFAULT_API_URL_NAME];\n const token = getApplicationAccessToken();\n\n if (!apiUrl || !token) {\n return available();\n }\n\n try {\n const response = await fetch(\n `${apiUrl.replace(/\\/$/, '')}/app/billing/credits`,\n {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/json',\n },\n signal: AbortSignal.timeout(CREDIT_AVAILABILITY_TIMEOUT_MS),\n },\n );\n\n if (!response.ok) {\n console.error(\n `getCreditAvailability: ${response.status} ${response.statusText}`,\n );\n\n return available();\n }\n\n const creditAvailability = parseCreditAvailability(await response.json());\n\n if (creditAvailability === null) {\n console.error('getCreditAvailability: unexpected response shape');\n\n return available();\n }\n\n return creditAvailability;\n } catch (error) {\n console.error(\n `getCreditAvailability: ${error instanceof Error ? error.message : String(error)}`,\n );\n\n return available();\n }\n};\n"],"mappings":";IAIyB,IAAI,kBAG1B,IAAI;CACN;CACA;CACA;AACD,GCLa,UACX,QAAQ,IAAA,uCACR,QAAQ,IAAA,yBCHJ,IAA4B,KAuBrB,IAAgB,OAAO,EAClC,qBACA,cAAW,GACX,cACA,kBACA,oBACA,yBACwC;CACxC,IAAM,IAAS,QAAQ,IAAI,IACrB,IAAQ,EAA0B;CAEpC,OAAC,KAAU,CAAC,IAIhB,IAAI;EACF,IAAM,IAAW,MAAM,MACrB,GAAG,EAAO,QAAQ,OAAO,EAAE,EAAE,sBAC7B;GACE,QAAQ;GACR,SAAS;IACP,eAAe,UAAU;IACzB,gBAAgB;GAClB;GACA,MAAM,KAAK,UAAU;IACnB;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,QAAQ,YAAY,QAAQ,CAAyB;EACvD,CACF;EAEA,IAAI,CAAC,EAAS,IAAI;GAChB,IAAM,IAAO,MAAM,EAAS,KAAK,CAAC,CAAC,YAAY,EAAE;GAEjD,QAAQ,MACN,kBAAkB,EAAS,OAAO,GAAG,EAAS,WAAW,IAAI,GAC/D;EACF;CACF,SAAS,GAAO;EACd,QAAQ,MACN,kBAAkB,aAAiB,QAAQ,EAAM,UAAU,OAAO,CAAK,GACzE;CACF;AACF,GCnEM,IAAiC,KAGjC,WAAuC,EAAE,qBAAqB,GAAK,IAEnE,KAA2B,MAA6C;CAC5E,IAAI,OAAO,KAAS,aAAY,GAC9B,OAAO;CAGT,IAAM,EAAE,wBAAqB,cAAW;CAmBxC,OAdI,MAAwB,KACnB,EAAE,qBAAqB,GAAK,IAInC,MAAwB,MACxB,EAA2B,SAAS,CAAiC,IAE9D;EACL,qBAAqB;EACb;CACV,IAGK;AACT,GAGa,IAAwB,YAAyC;CAC5E,IAAM,IAAS,QAAQ,IAAI,IACrB,IAAQ,EAA0B;CAExC,IAAI,CAAC,KAAU,CAAC,GACd,OAAO,EAAU;CAGnB,IAAI;EACF,IAAM,IAAW,MAAM,MACrB,GAAG,EAAO,QAAQ,OAAO,EAAE,EAAE,uBAC7B;GACE,QAAQ;GACR,SAAS;IACP,eAAe,UAAU;IACzB,QAAQ;GACV;GACA,QAAQ,YAAY,QAAQ,CAA8B;EAC5D,CACF;EAEA,IAAI,CAAC,EAAS,IAKZ,OAJA,QAAQ,MACN,0BAA0B,EAAS,OAAO,GAAG,EAAS,YACxD,GAEO,EAAU;EAGnB,IAAM,IAAqB,EAAwB,MAAM,EAAS,KAAK,CAAC;EAQxE,OANI,MAAuB,QACzB,QAAQ,MAAM,kDAAkD,GAEzD,EAAU,KAGZ;CACT,SAAS,GAAO;EAKd,OAJA,QAAQ,MACN,0BAA0B,aAAiB,QAAQ,EAAM,UAAU,OAAO,CAAK,GACjF,GAEO,EAAU;CACnB;AACF"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../twenty-shared/dist/application-runtime.mjs","../../src/sdk/utils/get-application-access-token.ts","../../src/sdk/billing/charge-credits.ts","../../src/sdk/billing/get-credit-availability.ts"],"sourcesContent":["//#region src/application-runtime/constants/ConnectionScopeByLegacyVisibility.ts\nvar e = {\n\tuser: \"USER\",\n\tworkspace: \"WORKSPACE\"\n}, t = \"TWENTY_API_KEY\", n = \"TWENTY_API_URL\", r = \"TWENTY_APP_ACCESS_TOKEN\", i = \"TWENTY_APP_APPLICATION_ACCESS_TOKEN\", a = \"TWENTY_FUNCTIONS_URL\", o = \"TWENTY_SERVER_ROUTES_URL\", s = {\n\tUSER: \"user\",\n\tWORKSPACE: \"workspace\"\n}, c = [\n\t\"workspace-suspended\",\n\t\"no-subscription\",\n\t\"no-credits\"\n];\n//#endregion\nexport { e as CONNECTION_SCOPE_BY_LEGACY_VISIBILITY, c as CREDIT_UNAVAILABLE_REASONS, t as DEFAULT_API_KEY_NAME, n as DEFAULT_API_URL_NAME, r as DEFAULT_APP_ACCESS_TOKEN_NAME, i as DEFAULT_APP_APPLICATION_ACCESS_TOKEN_NAME, a as DEFAULT_FUNCTIONS_URL_NAME, o as DEFAULT_SERVER_ROUTES_URL_NAME, s as LEGACY_CONNECTION_VISIBILITY_BY_SCOPE };\n","import {\n DEFAULT_APP_ACCESS_TOKEN_NAME,\n DEFAULT_APP_APPLICATION_ACCESS_TOKEN_NAME,\n} from 'twenty-shared/application-runtime';\n\n// These reach the application's own resources, so prefer the application token when the runtime injects one\nexport const getApplicationAccessToken = (): string | undefined =>\n process.env[DEFAULT_APP_APPLICATION_ACCESS_TOKEN_NAME] ??\n process.env[DEFAULT_APP_ACCESS_TOKEN_NAME];\n","import { type UsageOperationTypeValue } from 'twenty-shared/application';\nimport { DEFAULT_API_URL_NAME } from 'twenty-shared/application-runtime';\n\nimport { getApplicationAccessToken } from '@/sdk/utils/get-application-access-token';\n\nconst BILLING_CHARGE_TIMEOUT_MS = 5_000;\n\nexport type ChargeCreditsParams = {\n creditsUsedMicro: number;\n quantity?: number;\n // Who the spend belongs to when no user triggered the run (webhook, cron); ignored otherwise\n userWorkspaceId?: string;\n} & (\n | {\n // An operation declared in the manifest's `billing.operations`; the platform resolves its category and label\n operation: string;\n operationType?: never;\n resourceContext?: never;\n }\n | {\n // For applications that declare no billable operations: names the platform billing category directly\n operationType: UsageOperationTypeValue;\n operation?: never;\n resourceContext?: string;\n }\n);\n\n// No-ops without an API URL or access token, and never throws: a billing error must not fail a tool\nexport const chargeCredits = async ({\n creditsUsedMicro,\n quantity = 1,\n operation,\n operationType,\n resourceContext,\n userWorkspaceId,\n}: ChargeCreditsParams): Promise<void> => {\n const apiUrl = process.env[DEFAULT_API_URL_NAME];\n const token = getApplicationAccessToken();\n\n if (!apiUrl || !token) {\n return;\n }\n\n try {\n const response = await fetch(\n `${apiUrl.replace(/\\/$/, '')}/app/billing/charge`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n creditsUsedMicro,\n quantity,\n operation,\n operationType,\n resourceContext,\n userWorkspaceId,\n }),\n signal: AbortSignal.timeout(BILLING_CHARGE_TIMEOUT_MS),\n },\n );\n\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n\n console.error(\n `chargeCredits: ${response.status} ${response.statusText}: ${body}`,\n );\n }\n } catch (error) {\n console.error(\n `chargeCredits: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n};\n","import {\n CREDIT_UNAVAILABLE_REASONS,\n type CreditAvailability,\n type CreditUnavailableReason,\n DEFAULT_API_URL_NAME,\n} from 'twenty-shared/application-runtime';\n\nimport { getApplicationAccessToken } from '@/sdk/utils/get-application-access-token';\n\nconst CREDIT_AVAILABILITY_TIMEOUT_MS = 5_000;\n\n// A fresh object per call, since app code could mutate a shared constant\nconst available = (): CreditAvailability => ({ hasAvailableCredits: true });\n\nconst parseCreditAvailability = (body: unknown): CreditAvailability | null => {\n if (typeof body !== 'object' || body === null) {\n return null;\n }\n\n const { hasAvailableCredits, reason } = body as {\n hasAvailableCredits?: unknown;\n reason?: unknown;\n };\n\n if (hasAvailableCredits === true) {\n return { hasAvailableCredits: true };\n }\n\n if (\n hasAvailableCredits === false &&\n CREDIT_UNAVAILABLE_REASONS.includes(reason as CreditUnavailableReason)\n ) {\n return {\n hasAvailableCredits: false,\n reason: reason as CreditUnavailableReason,\n };\n }\n\n return null;\n};\n\n// Fails open on timeouts or bad responses: a hint before expensive work, never an authorization decision\nexport const getCreditAvailability = async (): Promise<CreditAvailability> => {\n const apiUrl = process.env[DEFAULT_API_URL_NAME];\n const token = getApplicationAccessToken();\n\n if (!apiUrl || !token) {\n return available();\n }\n\n try {\n const response = await fetch(\n `${apiUrl.replace(/\\/$/, '')}/app/billing/credits`,\n {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/json',\n },\n signal: AbortSignal.timeout(CREDIT_AVAILABILITY_TIMEOUT_MS),\n },\n );\n\n if (!response.ok) {\n console.error(\n `getCreditAvailability: ${response.status} ${response.statusText}`,\n );\n\n return available();\n }\n\n const creditAvailability = parseCreditAvailability(await response.json());\n\n if (creditAvailability === null) {\n console.error('getCreditAvailability: unexpected response shape');\n\n return available();\n }\n\n return creditAvailability;\n } catch (error) {\n console.error(\n `getCreditAvailability: ${error instanceof Error ? error.message : String(error)}`,\n );\n\n return available();\n }\n};\n"],"mappings":";IAIyB,IAAI,kBAG1B,IAAI;CACN;CACA;CACA;AACD,GCLa,UACX,QAAQ,IAAA,uCACR,QAAQ,IAAA,yBCHJ,IAA4B,KAuBrB,IAAgB,OAAO,EAClC,qBACA,cAAW,GACX,cACA,kBACA,oBACA,yBACwC;CACxC,IAAM,IAAS,QAAQ,IAAI,IACrB,IAAQ,EAA0B;CAEpC,OAAC,KAAU,CAAC,IAIhB,IAAI;EACF,IAAM,IAAW,MAAM,MACrB,GAAG,EAAO,QAAQ,OAAO,EAAE,EAAE,sBAC7B;GACE,QAAQ;GACR,SAAS;IACP,eAAe,UAAU;IACzB,gBAAgB;GAClB;GACA,MAAM,KAAK,UAAU;IACnB;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,QAAQ,YAAY,QAAQ,CAAyB;EACvD,CACF;EAEA,IAAI,CAAC,EAAS,IAAI;GAChB,IAAM,IAAO,MAAM,EAAS,KAAK,CAAC,CAAC,YAAY,EAAE;GAEjD,QAAQ,MACN,kBAAkB,EAAS,OAAO,GAAG,EAAS,WAAW,IAAI,GAC/D;EACF;CACF,SAAS,GAAO;EACd,QAAQ,MACN,kBAAkB,aAAiB,QAAQ,EAAM,UAAU,OAAO,CAAK,GACzE;CACF;AACF,GCnEM,IAAiC,KAGjC,WAAuC,EAAE,qBAAqB,GAAK,IAEnE,KAA2B,MAA6C;CAC5E,IAAI,OAAO,KAAS,aAAY,GAC9B,OAAO;CAGT,IAAM,EAAE,wBAAqB,cAAW;CAmBxC,OAdI,MAAwB,KACnB,EAAE,qBAAqB,GAAK,IAInC,MAAwB,MACxB,EAA2B,SAAS,CAAiC,IAE9D;EACL,qBAAqB;EACb;CACV,IAGK;AACT,GAGa,IAAwB,YAAyC;CAC5E,IAAM,IAAS,QAAQ,IAAI,IACrB,IAAQ,EAA0B;CAExC,IAAI,CAAC,KAAU,CAAC,GACd,OAAO,EAAU;CAGnB,IAAI;EACF,IAAM,IAAW,MAAM,MACrB,GAAG,EAAO,QAAQ,OAAO,EAAE,EAAE,uBAC7B;GACE,QAAQ;GACR,SAAS;IACP,eAAe,UAAU;IACzB,QAAQ;GACV;GACA,QAAQ,YAAY,QAAQ,CAA8B;EAC5D,CACF;EAEA,IAAI,CAAC,EAAS,IAKZ,OAJA,QAAQ,MACN,0BAA0B,EAAS,OAAO,GAAG,EAAS,YACxD,GAEO,EAAU;EAGnB,IAAM,IAAqB,EAAwB,MAAM,EAAS,KAAK,CAAC;EAQxE,OANI,MAAuB,QACzB,QAAQ,MAAM,kDAAkD,GAEzD,EAAU,KAGZ;CACT,SAAS,GAAO;EAKd,OAJA,QAAQ,MACN,0BAA0B,aAAiB,QAAQ,EAAM,UAAU,OAAO,CAAK,GACjF,GAEO,EAAU;CACnB;AACF"}