twenty-sdk 2.55.0 → 2.57.0
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 +6 -0
- package/dist/billing/index.cjs.map +1 -1
- package/dist/billing/index.mjs.map +1 -1
- package/dist/define/index.cjs +6 -6
- package/dist/define/index.cjs.map +1 -1
- package/dist/define/index.d.ts +523 -353
- package/dist/define/index.mjs +3526 -3380
- package/dist/define/index.mjs.map +1 -1
- package/dist/front-component/index.cjs +34 -24
- package/dist/front-component/index.cjs.map +1 -1
- package/dist/front-component/index.d.ts +2 -0
- package/dist/front-component/index.mjs +49782 -4870
- package/dist/front-component/index.mjs.map +1 -1
- package/dist/logic-function/index.cjs +104 -23
- package/dist/logic-function/index.cjs.map +1 -1
- package/dist/logic-function/index.d.ts +197 -30
- package/dist/logic-function/index.mjs +379 -121
- package/dist/logic-function/index.mjs.map +1 -1
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.mjs.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,10 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **`INBOX_ASSISTANT_MENTION` now writes a canonical assistant reference instead of plain `@Ada` text.** The inbox keeps the saved label and assistant identity when rendering the message. Existing plain text is unchanged. This does not change assistant participation or reply selection for application messages.
|
|
12
|
+
|
|
9
13
|
### Breaking Changes
|
|
10
14
|
|
|
11
15
|
- **`MetadataApiClient.uploadFile` in `twenty-client-sdk` takes one object and no longer accepts a content type.** The file now goes straight to Twenty's file storage (the API issues an upload target, the client sends the bytes there, then the API confirms the upload) instead of being streamed through the GraphQL API, and the MIME type is detected from the bytes on completion. Update every call:
|
|
@@ -32,6 +36,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
|
32
36
|
|
|
33
37
|
### Added
|
|
34
38
|
|
|
39
|
+
- **Message attachments in `twenty-sdk/logic-function`.** `uploadMessageAttachment({ filename, content })` uploads a file for a message and returns `{ fileId, label }`, which `ingestMessages` takes in a message's `attachments`. Twenty stores each file as an attachment record on the message. Attachments are added only while the message has none, so a later delivery can add them but never replaces them, and each `IngestedMessage` now reports `hasAttachments` so a redelivery can skip downloading them again. An uploaded file that no message ends up holding is kept, so upload only for messages without attachments. Uploading needs the `UPLOAD_FILE` permission flag on the app's role. `ingestMessages` itself now reads `hasAttachments`, so every call needs a Twenty server with message attachments, not only uploads.
|
|
35
40
|
- **`sendInboxMessage` in `twenty-sdk/front-component`.** A front component can post in members' inbox without a logic function in between. The message is still from your app; the call carries the component's token, so the member looking at the page needs the AI permission too, and an expired token is refreshed once before the call is retried. Open the conversation with `navigate`:
|
|
36
41
|
|
|
37
42
|
```tsx
|
|
@@ -103,6 +108,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
|
103
108
|
|
|
104
109
|
### Changed
|
|
105
110
|
|
|
111
|
+
- **`uploadMessageAttachment` sends `content` without copying it.** The bytes go to storage as they are, so uploading a file no longer holds a second copy of it in memory. `MetadataApiClient.uploadFile` in `twenty-client-sdk` now takes any `Uint8Array` as `fileBuffer`; a `Buffer` still works.
|
|
106
112
|
- **`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.
|
|
107
113
|
- **`twenty app deploy` sends the tarball straight to file storage.** The CLI reserves an upload with `createFileUpload`, PUTs the archive to storage and registers it with `completeAppTarballUpload`, so the tarball no longer transits through the API process. Deploying to a server that predates these mutations fails instead of falling back to the multipart `uploadAppTarball` mutation.
|
|
108
114
|
- **`twenty-client-sdk` should now be a dev dependency too.** Although app code imports it (`CoreApiClient`, `MetadataApiClient`, `RestApiClient`), Twenty provides it at runtime — logic functions get it from a generated SDK layer and front components resolve it from server-served modules — so the installed copy is only needed for typechecking and the deploy-time build. Newly scaffolded apps now place it under `devDependencies`. Moving it is recommended (not required: the server already strips it from the deployed runtime), and keeps the installed app leaner:
|
|
@@ -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 = \"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 // Platform billing category; omitted, the charge lands in the dedicated APP_OPERATION category\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\n// acts as whoever triggered the run, like the client SDK's default runAs\nexport const getCallerAccessToken = (): string | undefined =>\n process.env[DEFAULT_APP_ACCESS_TOKEN_NAME] ??\n process.env[DEFAULT_APP_APPLICATION_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 // Platform billing category; omitted, the charge lands in the dedicated APP_OPERATION category\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 = \"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 // Platform billing category; omitted, the charge lands in the dedicated APP_OPERATION category\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\n// acts as whoever triggered the run, like the client SDK's default runAs\nexport const getCallerAccessToken = (): string | undefined =>\n process.env[DEFAULT_APP_ACCESS_TOKEN_NAME] ??\n process.env[DEFAULT_APP_APPLICATION_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 // Platform billing category; omitted, the charge lands in the dedicated APP_OPERATION category\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"}
|