twenty 0.2.0 → 2.46.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.
Files changed (183) hide show
  1. package/CONTRIBUTING.md +166 -0
  2. package/LICENSE +21 -0
  3. package/README.md +64 -57
  4. package/dist/app-template/.nvmrc +1 -0
  5. package/dist/app-template/.oxlintrc.json +37 -0
  6. package/dist/app-template/AGENTS.md +70 -0
  7. package/dist/app-template/CHANGELOG.md +7 -0
  8. package/dist/app-template/README.md +34 -0
  9. package/dist/app-template/SETUP.md +47 -0
  10. package/dist/app-template/github/workflows/cd.yml +42 -0
  11. package/dist/app-template/github/workflows/ci.yml +60 -0
  12. package/dist/app-template/github/workflows/publish.yml +52 -0
  13. package/dist/app-template/gitignore +43 -0
  14. package/dist/app-template/locales/en.json +1 -0
  15. package/dist/app-template/package.json +36 -0
  16. package/dist/app-template/public/logo.svg +7 -0
  17. package/dist/app-template/src/__tests__/application-config.test.ts +14 -0
  18. package/dist/app-template/src/__tests__/global-setup.ts +56 -0
  19. package/dist/app-template/src/__tests__/run-twenty.ts +66 -0
  20. package/dist/app-template/src/__tests__/schema.integration-test.ts +46 -0
  21. package/dist/app-template/src/application-config.ts +13 -0
  22. package/dist/app-template/src/constants/universal-identifiers.ts +12 -0
  23. package/dist/app-template/src/default-role.ts +16 -0
  24. package/dist/app-template/src/front-components/main-page.tsx +287 -0
  25. package/dist/app-template/src/logic-functions/health-check.ts +11 -0
  26. package/dist/app-template/src/navigation-menu-items/main-page.navigation-menu-item.ts +19 -0
  27. package/dist/app-template/src/page-layouts/main-page.page-layout.ts +42 -0
  28. package/dist/app-template/tsconfig.json +37 -0
  29. package/dist/app-template/tsconfig.spec.json +9 -0
  30. package/dist/app-template/vitest.config.ts +31 -0
  31. package/dist/app-template/vitest.unit.config.ts +14 -0
  32. package/dist/app-template/yarn.lock +3374 -0
  33. package/dist/app-template/yarnrc.yml +1 -0
  34. package/dist/app-worker.cjs +1 -0
  35. package/dist/chunks/application-DuIJE2qG.cjs +5 -0
  36. package/dist/chunks/assert-pull-paths-BuQ1ZRvQ.cjs +1 -0
  37. package/dist/chunks/build-CX6FBrbu.cjs +1 -0
  38. package/dist/chunks/build-pull-entities-zwArrVMk.cjs +5 -0
  39. package/dist/chunks/build-source-manifest-CLKH5vzc.cjs +2 -0
  40. package/dist/chunks/build-source-snapshot-CbGsy1-v.cjs +206 -0
  41. package/dist/chunks/check-node-requirement-mqwNPktb.cjs +1 -0
  42. package/dist/chunks/cli-error-CSsayo0u.cjs +1 -0
  43. package/dist/chunks/cli-version.constant-aSp8WZDh.cjs +1 -0
  44. package/dist/chunks/collect-watch-inputs-B9jWG4W0.cjs +1 -0
  45. package/dist/chunks/compile-application-translations-B59jYVWc.cjs +1 -0
  46. package/dist/chunks/config-file-mode.constant-DLBzKKUl.cjs +1 -0
  47. package/dist/chunks/confirm-in-terminal-BxEM5lxE.cjs +1 -0
  48. package/dist/chunks/constants-CL6mrTMT-B748cNNG.cjs +23 -0
  49. package/dist/chunks/constants-Cf5-CwMu.cjs +1 -0
  50. package/dist/chunks/convert-to-label-BPBLtc_O.cjs +1 -0
  51. package/dist/chunks/create-app-not-installed-error-Cc8YZqI7.cjs +1 -0
  52. package/dist/chunks/create-cancelled-error-DgrNfOv2.cjs +1 -0
  53. package/dist/chunks/create-http-status-error-D3gy3DzB.cjs +1 -0
  54. package/dist/chunks/create-metadata-client-ChjDalSi.cjs +6 -0
  55. package/dist/chunks/create-pull-base-write-rxjcKtD-.cjs +1 -0
  56. package/dist/chunks/create-rest-client-eAfOFfDA.cjs +58 -0
  57. package/dist/chunks/create-tooling-failure-DLI5FlQP.cjs +1 -0
  58. package/dist/chunks/credential-kind-labels.constant-CKdoQF8o.cjs +1 -0
  59. package/dist/chunks/extract-manifest-from-file-j-Y9wNas.cjs +1 -0
  60. package/dist/chunks/fetch-metadata-inspection-Dp034TpK.cjs +1 -0
  61. package/dist/chunks/fetch-signed-in-identity-BhvaL3zp.cjs +1 -0
  62. package/dist/chunks/fetch-workspace-id-DJStmf_V.cjs +1 -0
  63. package/dist/chunks/find-remote-D48r20g0.cjs +1 -0
  64. package/dist/chunks/format-app-duration-gu2oRLB4.cjs +1 -0
  65. package/dist/chunks/format-body-VcKL4WXt.cjs +1 -0
  66. package/dist/chunks/format-data-value-B6rIbpJv.cjs +1 -0
  67. package/dist/chunks/format-details-COEwKz_M.cjs +2 -0
  68. package/dist/chunks/format-metadata-fields-DJnTg1R9.cjs +2 -0
  69. package/dist/chunks/format-metadata-owner-yUeocnwY.cjs +1 -0
  70. package/dist/chunks/format-table-4drtI5sP.cjs +2 -0
  71. package/dist/chunks/fs-utils-Bl1fQcaJ.cjs +2 -0
  72. package/dist/chunks/generate-application-client-CCVLjxTj.cjs +1 -0
  73. package/dist/chunks/get-app-plan-summary-B0uwHXbO.cjs +8 -0
  74. package/dist/chunks/get-config-path-C-D-QHuX.cjs +1 -0
  75. package/dist/chunks/get-credential-kind-JI9heHe2.cjs +1 -0
  76. package/dist/chunks/get-function-input-schema-GNk3NRLJ-Bfph1o9z.cjs +1 -0
  77. package/dist/chunks/get-tooling-error-context-C69TBSej.cjs +1 -0
  78. package/dist/chunks/global-flags.constant-8GfW0sNU.cjs +1 -0
  79. package/dist/chunks/has-yarn-plug-and-play-X3ay5Xl0.cjs +1 -0
  80. package/dist/chunks/is-access-token-expiring-gICPXsbO.cjs +1 -0
  81. package/dist/chunks/is-application-not-found-error-mNeGbxmT.cjs +1 -0
  82. package/dist/chunks/is-exported-manifest-CriNOWvx.cjs +1 -0
  83. package/dist/chunks/is-inside-directory-t3ixLQDr.cjs +1 -0
  84. package/dist/chunks/is-interaction-allowed-ycbAFMIj.cjs +1 -0
  85. package/dist/chunks/is-same-universal-identifier-BCBib3RP.cjs +1 -0
  86. package/dist/chunks/is-supported-locale-BAjnKnKj.cjs +20 -0
  87. package/dist/chunks/kebab-case-D0usAzG-.cjs +1 -0
  88. package/dist/chunks/list-application-source-files-jEau5Rie.cjs +1 -0
  89. package/dist/chunks/metadata-D9sQOTRK.cjs +1 -0
  90. package/dist/chunks/metadata-page.constant-BkG4ndrO.cjs +1 -0
  91. package/dist/chunks/oauth-timing.constant-BClMQcKn.cjs +1 -0
  92. package/dist/chunks/open-browser-LPuVspT7.cjs +1 -0
  93. package/dist/chunks/package-CdYv4LEx.cjs +1 -0
  94. package/dist/chunks/parse-api-url-BllxRdpE.cjs +1 -0
  95. package/dist/chunks/parse-app-export-BkfflyUY.cjs +1 -0
  96. package/dist/chunks/parse-graphql-response-CtP9KTS2.cjs +1 -0
  97. package/dist/chunks/parse-json-input-CvMTKAWw.cjs +1 -0
  98. package/dist/chunks/print-typescript-value-DhElcFzI.cjs +3 -0
  99. package/dist/chunks/pull-source-eoiJwFb9.cjs +1 -0
  100. package/dist/chunks/read-app-identity-SeleQ_D6.cjs +1 -0
  101. package/dist/chunks/read-application-identity-BFg_p84F.cjs +1 -0
  102. package/dist/chunks/read-command-values-uSJqBG_E.cjs +1 -0
  103. package/dist/chunks/read-config-DUJ27809.cjs +1 -0
  104. package/dist/chunks/read-input-value-Cxgh_r2g.cjs +1 -0
  105. package/dist/chunks/read-source-identity-C8LG1v6U.cjs +1 -0
  106. package/dist/chunks/read-watch-input-stamp-BxeOb0vI.cjs +1 -0
  107. package/dist/chunks/record-pull-base-BbxvlnU8.cjs +1 -0
  108. package/dist/chunks/record-typecheck-config-inputs-BDqkKCrj.cjs +1 -0
  109. package/dist/chunks/request-oauth-tokens-C4KKlTQT.cjs +1 -0
  110. package/dist/chunks/request-timeout-milliseconds.constant-DtyQHrll.cjs +1 -0
  111. package/dist/chunks/require-approval-fhegB1ap.cjs +1 -0
  112. package/dist/chunks/resolve-app-project-D5ZXIpsG.cjs +1 -0
  113. package/dist/chunks/resolve-inside-sdk-p-16gKb4.cjs +1 -0
  114. package/dist/chunks/resolve-metadata-object-QoDi-Aik.cjs +1 -0
  115. package/dist/chunks/resolve-project-typescript-CDY8BbmP.cjs +1 -0
  116. package/dist/chunks/resolve-request-url-jC5kYwkn.cjs +1 -0
  117. package/dist/chunks/resolve-source-sdk-mK-f4q55.cjs +1 -0
  118. package/dist/chunks/result-item-limit.constant-CDrOhiWD.cjs +1 -0
  119. package/dist/chunks/run-api-graphql-command-Biy0WqXa.cjs +1 -0
  120. package/dist/chunks/run-api-rest-command-D9kdQceV.cjs +1 -0
  121. package/dist/chunks/run-app-add-command-DcTQg7Si.cjs +163 -0
  122. package/dist/chunks/run-app-apply-command-DwLPPJ-Y.cjs +2 -0
  123. package/dist/chunks/run-app-build-command-BRYb0one.cjs +2 -0
  124. package/dist/chunks/run-app-dev-command-CVeh9Gab.cjs +2 -0
  125. package/dist/chunks/run-app-exec-command-DKLNLbL5.cjs +3 -0
  126. package/dist/chunks/run-app-init-command-CJj8_spC.cjs +2 -0
  127. package/dist/chunks/run-app-logs-command-Dusb8K-W.cjs +9 -0
  128. package/dist/chunks/run-app-operation-BWrY9YZK.cjs +1 -0
  129. package/dist/chunks/run-app-plan-command-C0_3e332.cjs +1 -0
  130. package/dist/chunks/run-app-pull-command-DcZqs8gR.cjs +3 -0
  131. package/dist/chunks/run-app-typecheck-command-C8LqpOej.cjs +1 -0
  132. package/dist/chunks/run-app-uninstall-command-ncWS_k-p.cjs +2 -0
  133. package/dist/chunks/run-auth-login-command-_zDgRcth.cjs +1 -0
  134. package/dist/chunks/run-auth-logout-command-BO6O7GVi.cjs +2 -0
  135. package/dist/chunks/run-auth-status-command-Cj-vqoHg.cjs +1 -0
  136. package/dist/chunks/run-auth-token-command-D4geJ-rx.cjs +1 -0
  137. package/dist/chunks/run-cli-c9TOR0zj.cjs +23 -0
  138. package/dist/chunks/run-commands-command-C6gvMpQ_.cjs +2 -0
  139. package/dist/chunks/run-data-get-command-jDEU4EG1.cjs +1 -0
  140. package/dist/chunks/run-data-list-command-EnCdbuzc.cjs +1 -0
  141. package/dist/chunks/run-doctor-command-DnJ6io-m.cjs +2 -0
  142. package/dist/chunks/run-metadata-field-describe-command-vJngpw2S.cjs +3 -0
  143. package/dist/chunks/run-metadata-field-list-command-CR4w7fI6.cjs +3 -0
  144. package/dist/chunks/run-metadata-object-describe-command-CHP8K-W2.cjs +3 -0
  145. package/dist/chunks/run-metadata-object-list-command-BqXMcAp6.cjs +2 -0
  146. package/dist/chunks/run-open-command-DKl8acw0.cjs +1 -0
  147. package/dist/chunks/run-remote-list-command-DeAzr5Mn.cjs +1 -0
  148. package/dist/chunks/run-remote-remove-command-D0C5sWjP.cjs +2 -0
  149. package/dist/chunks/run-remote-rename-command-Byq9WRHd.cjs +1 -0
  150. package/dist/chunks/run-remote-use-command-B6VVDlMG.cjs +1 -0
  151. package/dist/chunks/run-version-command-BmSk8PUV.cjs +1 -0
  152. package/dist/chunks/select-target-C1VZjf-A.cjs +1 -0
  153. package/dist/chunks/sign-in-to-remote-again-CW8HPSTP.cjs +1 -0
  154. package/dist/chunks/sign-in-with-browser-C3kC6uP2.cjs +1 -0
  155. package/dist/chunks/style-BR52fK4V.cjs +1 -0
  156. package/dist/chunks/target-environment-variable.constant-D7z8AihP.cjs +1 -0
  157. package/dist/chunks/to-cli-error-BBYf_wqo.cjs +1 -0
  158. package/dist/chunks/to-public-target-CIAwoOKL.cjs +1 -0
  159. package/dist/chunks/to-worker-output-diagnostics-lprh9b0l.cjs +2 -0
  160. package/dist/chunks/typecheck-application-DGrovgX9.cjs +2 -0
  161. package/dist/chunks/undici-D3mKdfwl.cjs +95 -0
  162. package/dist/chunks/update-config-BkKBEfAp.cjs +1 -0
  163. package/dist/chunks/utils-B8EXJeaX-DK3gCslL.cjs +33 -0
  164. package/dist/chunks/validate-app-path-Dau-PC0a.cjs +1 -0
  165. package/dist/chunks/validate-remote-name-w-qnr43w.cjs +1 -0
  166. package/dist/chunks/write-config-atomically-DRWTukU-.cjs +1 -0
  167. package/dist/cli.cjs +2 -0
  168. package/docs/commands.md +564 -0
  169. package/package.json +41 -18
  170. package/src/app/README.md +43 -0
  171. package/src/app/add/README.md +69 -0
  172. package/src/app/bundles/README.md +85 -0
  173. package/src/app/client/README.md +62 -0
  174. package/src/app/dev/README.md +35 -0
  175. package/src/app/exec/README.md +48 -0
  176. package/src/app/function-logs/README.md +44 -0
  177. package/src/app/manifest/README.md +74 -0
  178. package/src/app/pull/README.md +28 -0
  179. package/src/app/source/README.md +33 -0
  180. package/src/app/typecheck/README.md +67 -0
  181. package/.npmignore +0 -2
  182. package/bin/twenty +0 -2
  183. package/index.js +0 -118
package/package.json CHANGED
@@ -1,28 +1,51 @@
1
1
  {
2
2
  "name": "twenty",
3
- "version": "0.2.0",
4
- "description": "Locates the city/region of a given IP address",
5
- "main": "index.js",
3
+ "version": "2.46.0",
4
+ "description": "The Twenty command line.",
5
+ "homepage": "https://twenty.com",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "https://github.com/scottwrobinson/twentyjs.git"
8
+ "url": "git+https://github.com/twentyhq/twenty.git",
9
+ "directory": "packages/twenty-cli"
9
10
  },
10
- "keywords": [
11
- "cli",
12
- "location",
13
- "ipinfo",
14
- "tools"
11
+ "bugs": {
12
+ "url": "https://github.com/twentyhq/twenty/issues"
13
+ },
14
+ "license": "MIT",
15
+ "bin": "dist/cli.cjs",
16
+ "files": [
17
+ "dist",
18
+ "docs/commands.md",
19
+ "CONTRIBUTING.md",
20
+ "src/app/**/README.md"
15
21
  ],
16
22
  "scripts": {
17
- "test": "echo \"Error: no test specified\" && exit 1"
23
+ "build": "npx rimraf dist && npx vite build",
24
+ "prepublish": "node scripts/check-publish.mjs && node scripts/generate-template-lock.mjs"
18
25
  },
19
- "author": "Scott Robinson <s.w.robinson@gmail.com> (http://stackabuse.com)",
20
- "license": "MIT",
21
- "dependencies": {
22
- "request": "^2.67.0",
23
- "yargs": "^4.2.0"
26
+ "engines": {
27
+ "node": "^24.5.0"
28
+ },
29
+ "devDependencies": {
30
+ "@sniptt/guards": "^0.2.0",
31
+ "@types/lodash.startcase": "^4",
32
+ "@types/node": "^24.0.0",
33
+ "@types/semver": "^7.5.8",
34
+ "cjs-module-lexer": "^2.2.0",
35
+ "commander": "^14.0.3",
36
+ "lodash.startcase": "^4.4.0",
37
+ "semver": "7.6.3",
38
+ "twenty-client-sdk": "2.46.0",
39
+ "twenty-shared": "0.0.0",
40
+ "undici": "^7.29.0",
41
+ "uuid": "^13.0.2",
42
+ "vite": "^8.0.0",
43
+ "vitest": "^4.1.0"
24
44
  },
25
- "bin": {
26
- "twenty": "./bin/twenty"
45
+ "dependencies": {
46
+ "chokidar": "^3.6.0",
47
+ "esbuild": "^0.28.1",
48
+ "tinyglobby": "^0.2.15",
49
+ "typescript": "^5.9.3"
27
50
  }
28
- }
51
+ }
@@ -0,0 +1,43 @@
1
+ # Application tooling
2
+
3
+ See [Contributing](../../CONTRIBUTING.md) for package setup and tests, and the
4
+ [command reference](../../docs/commands.md) for user-visible behavior.
5
+
6
+ The `twenty` CLI owns filesystem operations, source loading, build, typecheck,
7
+ watching, pull and deployment. Apps use `twenty-sdk` to define application
8
+ metadata and runtime behavior. They do not import or depend on the CLI, which
9
+ is normally installed globally.
10
+
11
+ | Module | Responsibility |
12
+ | ---------------------------------------- | -------------------------------------------------------------------- |
13
+ | `project` | Locate the app and resolve its installed SDK and Node requirements |
14
+ | `deployment` | Preview metadata changes, approve them, upload snapshots and sync |
15
+ | [source](source/README.md) | Discover and evaluate trusted app definitions in a disposable worker |
16
+ | [manifest](manifest/README.md) | Construct and validate metadata, compile translations |
17
+ | [bundles](bundles/README.md) | Build artifacts and hold immutable snapshots for consumers |
18
+ | [typecheck](typecheck/README.md) | Check source and configuration with the app's TypeScript compiler |
19
+ | [client](client/README.md) | Invoke the app's installed client SDK generator |
20
+ | [pull](pull/README.md) | Reconcile remote metadata with source and a target-bound baseline |
21
+ | [add](add/README.md) | Create starter definitions without replacing existing files |
22
+ | [dev](dev/README.md) | Watch build inputs and schedule builds and remote apply |
23
+ | [exec](exec/README.md) | Execute an installed logic function |
24
+ | [function-logs](function-logs/README.md) | Stream application function logs |
25
+
26
+ The worker isolates process exits, captures bounded output, filters inherited
27
+ CLI credentials and supports cancellation. It runs trusted developer code and
28
+ is not a security sandbox. Configurations containing functions stay inside the
29
+ worker; only serializable requests, results and diagnostics cross IPC.
30
+
31
+ Snapshots retain files until their consumers finish. Uploads verify containment,
32
+ size and checksums. Pull and scaffolding stage writes before exposing them to
33
+ file watchers. Keep these guarantees when simplifying implementation details.
34
+
35
+ ## Package boundaries
36
+
37
+ The CLI imports the app's public SDK authoring exports and invokes its installed
38
+ client SDK generator. The [typecheck documentation](typecheck/README.md) describes
39
+ compiler resolution and project requirements.
40
+
41
+ Parity tests compare generated source, manifests and bundles with the repository
42
+ SDK. Command failure and cancellation tests inject tooling fixtures into a bundle
43
+ of the production worker; real-app tests exercise its compiler and bundler.
@@ -0,0 +1,69 @@
1
+ # App definition scaffolding
2
+
3
+ The templates create object, field, logic-function and front-component
4
+ definitions. Filenames and logic-function/front-component names use kebab-case.
5
+ Object and field metadata names keep the entered spelling; display labels use
6
+ the same helper as app init.
7
+
8
+ ## Contract
9
+
10
+ - `twenty app add [entity]` accepts `object`, `field`, `logic-function` and
11
+ `front-component`. A human terminal can supply omitted values interactively.
12
+ JSON, CI, redirected stdin and `--no-input` never prompt; missing required or
13
+ inapplicable options exit 2. Labels default to the name; fields default to
14
+ `TEXT`.
15
+ - `--path` selects an app, like the other CLI app commands. Files go under the
16
+ selected app's `src/objects`, `src/fields`, `src/logic-functions` or
17
+ `src/front-components`. Custom output directories are not supported.
18
+ - Parent and relation endpoint identifiers must be valid universal UUIDs. No
19
+ workspace lookup verifies ownership or existence. Relations require reviewing
20
+ both endpoints; this generator creates only the requested standalone field.
21
+ - Objects accept `--create-view`, `--create-navigation-menu-item` and
22
+ `--create-page-layout`. Each flag opts into that definition; page layouts also
23
+ create the fields view used by their Fields widget. The list view and fields
24
+ view reuse the object's name field. Fields views also include the SDK's
25
+ generated audit fields, resolved using the application's universal identifier.
26
+ With no creation flags, human interactive mode offers all three together,
27
+ defaulting to No. Explicit creation flags bypass that question. Noninteractive
28
+ modes never ask and create only explicitly selected companions. All prompts
29
+ complete before any files are written, including on cancellation.
30
+ - Node's `randomUUID` creates version 4 identifiers. The CLI TypeScript printer
31
+ escapes authored strings, including quotes, backslashes and line breaks.
32
+ - App discovery, SDK compatibility and application identity reuse the CLI's
33
+ worker boundary. Identity loading evaluates trusted app code in a disposable
34
+ worker; it is not a security sandbox. The command makes no workspace requests
35
+ and does not require authentication.
36
+ - Destination paths reuse pull's containment and symlink checks. All destinations
37
+ are checked before writing and all files are staged beside their destinations
38
+ before exclusive linking, including when source directories are mounted on
39
+ different filesystems.
40
+ Existing or concurrently created files are never replaced. Failure or
41
+ cancellation before all links complete rolls back the links already created;
42
+ files replaced by another process are preserved. Incomplete rollback reports
43
+ remaining app-relative paths. Completion wins over late cancellation. Cleanup failure
44
+ after success warns and reports the remaining temporary directory. Empty
45
+ parent directories can remain after failure or cancellation. Exclusive atomic
46
+ creation requires a filesystem supporting hard links; unsupported filesystems
47
+ fail instead of falling back to a potentially partial destination write.
48
+ - Path validation rejects existing symbolic links. It cannot prevent another
49
+ local process from replacing a parent directory between validation and writing.
50
+ - JSON reports app-relative `createdPaths`. There is no overwrite mode and no
51
+ editing or registration of existing definitions. Standalone view, layout and
52
+ navigation item generators are not supported.
53
+
54
+ These are starter definitions, not a deployment validator. Review type-specific
55
+ settings, relation endpoints, handlers and components, then build and plan the
56
+ app. Successful generation does not imply the server will accept the metadata.
57
+
58
+ ## Verification
59
+
60
+ `add-template-parity.spec.ts` compares generated source with the repository SDK,
61
+ normalizing random UUIDs while preserving their reuse. It covers every field type
62
+ and both runtime templates. Additional tests cover escaped strings, invalid
63
+ identifiers, destination conflicts and cancellation.
64
+
65
+ View and navigation templates are deliberately limited to object companions:
66
+ fully populated fields and an object-target navigation item, without the SDK
67
+ CLI's placeholder and unrelated view/link/folder variants. Generated identifiers
68
+ and references are checked by the app-add command tests; record page layouts
69
+ also retain SDK source parity.
@@ -0,0 +1,85 @@
1
+ # Bundles and snapshots
2
+
3
+ The CLI owns application bundling and immutable snapshot lifecycle. The internal
4
+ `bundleSnapshot` worker request builds and holds or releases a snapshot under
5
+ `.twenty/cli/snapshots/build-*`. The [typechecker](../typecheck/README.md) uses the
6
+ app's own TypeScript compiler. Build, plan, apply and uninstall use this pipeline.
7
+ Worker requests are internal, not a public API or a way to bypass build checks.
8
+
9
+ See the [app tooling overview](../README.md) for package ownership.
10
+
11
+ ## Contract
12
+
13
+ Logic functions use an ESM/CJS banner, external modules and define stubs.
14
+ Front components use JSX wrappers, remote-DOM transformation, optional
15
+ preact aliases, bundled CSS injection, static shared-dependency export discovery
16
+ and live-binding shims. The app's translation catalogs are embedded in the bundles.
17
+ Dependency discovery does not execute package initialization code. CommonJS
18
+ named exports must be statically detectable; packages that compute export names
19
+ at runtime can be consumed through their default export.
20
+
21
+ The bundler collects source and dependency files, public and generated assets,
22
+ and the README, and updates the manifest's artifact checksums. Snapshot copies
23
+ dereference symlinks. Each upload artifact records its role, byte size and
24
+ SHA-256 hash. README and source maps are included in the snapshot but are not
25
+ upload artifacts. The content hash combines the sorted artifact paths, roles and
26
+ hashes with the exact manifest bytes.
27
+
28
+ Each build has its own directory and lease. Release removes only a snapshot held
29
+ by that worker. Failed and cooperatively cancelled builds remove their temporary
30
+ directory. Forced worker termination can leave a snapshot behind. Cleanup only
31
+ removes snapshots owned by the current build.
32
+
33
+ ## Implementation
34
+
35
+ - Source loading, manifest generation, translations and filesystem helpers are
36
+ shared with the other CLI app operations. The [dev session](../dev/README.md)
37
+ observes the build's inputs and retains immutable copies for remote apply.
38
+ - The define stub reads `twenty-sdk/define` from the app's installed SDK. Factories
39
+ become no-op validators, plain-data exports retain their values, and other
40
+ exports use proxy stubs. Runtime constants match the SDK the app uses.
41
+ - `sharp` is optional and resolved from the app. It is not a CLI dependency.
42
+ When unavailable, cover generation warns and the build continues. Vite embeds
43
+ the backdrop PNG as a data URI so it works in the standalone package.
44
+ - `compileApplication` runs the CLI typecheck after bundling, using the compiler
45
+ resolved from the app.
46
+ - Workers run from the selected app directory, so invoking the CLI from a
47
+ parent or nested directory produces the same bundle paths, bytes and hashes.
48
+ SDK parity references run from the app root as well.
49
+ - Upload validation accepts only snapshots inside the selected app's
50
+ `.twenty/cli/snapshots`, with path containment and per-file hash checks.
51
+ - The internal worker reuses the source SDK gate, credential filtering, output
52
+ capture, cancellation and held-snapshot release flow, and stops esbuild after
53
+ building. Public app commands share this worker lifecycle and its
54
+ `ToolingResult` contract.
55
+
56
+ ## Verification
57
+
58
+ After building shared, SDK and UI dependencies, from `packages/twenty-cli`:
59
+
60
+ ```bash
61
+ node ../../node_modules/vitest/vitest.mjs run --config vitest.config.ts --maxWorkers=1 src/app/snapshots src/app/bundles src/app/__tests__/resolve-snapshot-directory.spec.ts src/commands/app/__tests__/app-real-sdk.spec.ts
62
+ ```
63
+
64
+ The parity suite builds all five repository fixtures and a fresh CLI-created app
65
+ through the SDK reference and the production CLI worker. The app has a copy of
66
+ the real authoring SDK without its build/CLI exports or implementation files.
67
+ Successful builds preserve manifest configuration and artifact paths/roles.
68
+ Non-frontend files are compared byte for byte, except for `manifest.json` and
69
+ source maps. Frontend bundle bytes and checksums are allowed to differ: dedicated tests verify preserved
70
+ string literals, resolved CSS assets and live shared-dependency bindings. Every
71
+ CLI artifact's size/checksum and the snapshot content hash are checked against
72
+ the actual bytes. Tests also verify the manifest references for function, component
73
+ and shared-dependency bundles, and compare the serialized manifest with the
74
+ worker's manifest result. The invalid fixture fails in both
75
+ pipelines. The fresh CLI template's test setup typechecks with an authoring-only SDK.
76
+
77
+ Source-map comparisons for unchanged bundles resolve paths against each map's
78
+ directory and exclude the define stub's generator banner from `sourcesContent`.
79
+
80
+ Additional tests cover optional covers, project SDK constants, baked translations
81
+ and CSS, README selection, immutable symlink copies, concurrent snapshots,
82
+ release ownership, failed/cancelled builds and failed snapshot consumers. The
83
+ build/apply contract runs CLI snapshots against a local HTTP fixture, including
84
+ uploads, sync, pull-base recording, release and client generation. It does not contact
85
+ a live Twenty workspace.
@@ -0,0 +1,62 @@
1
+ # Application client generation
2
+
3
+ The CLI calls `replaceCoreClient` from the app's installed
4
+ `twenty-client-sdk/generate`. See the [app tooling overview](../README.md) for
5
+ package boundaries. The package manifest is read through `readJsonObject`;
6
+ missing or malformed JSON and incorrect package names produce an expected-package
7
+ `CLIENT_GENERATION_FAILED` error.
8
+
9
+ Public `app apply` invokes the generator through the internal
10
+ `generateSourceClient` worker request.
11
+
12
+ ## Package and output ownership
13
+
14
+ The app must have `node_modules/twenty-client-sdk/package.json` with the expected
15
+ package name. Resolution does not search workspace ancestors, avoiding writes
16
+ to a hoisted-only installation shared by other apps. Package-manager symlinks
17
+ are followed, so a linked installation can still be shared.
18
+
19
+ Resolve `twenty-client-sdk/generate` from this package and require the entry to
20
+ belong to the same real package directory. It must export a callable
21
+ `replaceCoreClient`. This keeps the generator and the client it writes into on
22
+ the same installed version, with no fallback to the CLI's own client SDK.
23
+
24
+ The output layout is `dist/core/generated`, `dist/core.mjs` and
25
+ `dist/core.cjs` inside that installation. Source files and the metadata client
26
+ remain unchanged. Neither apps nor SDK packages depend on the CLI. The client
27
+ SDK's generator dependencies remain part of the app's installed client package.
28
+
29
+ ## Failure and cancellation
30
+
31
+ The worker requires a non-empty schema and an absolute app path. Failures use
32
+ `CLIENT_GENERATION_FAILED` / `CANCELLED` results. Missing or incompatible local
33
+ packages fail before invoking the generator. The generator rejects
34
+ invalid schemas without replacing the installed bundles.
35
+
36
+ Generation is not transactional. A generator failure can leave partial changes;
37
+ the wrapper reports that failure even if cancellation was requested meanwhile.
38
+ The generator has no cancellation parameter, so the wrapper waits for it to
39
+ settle and then checks the signal. Forced worker termination can interrupt file
40
+ writes. The operation does not roll back partial writes.
41
+
42
+ ## Verification
43
+
44
+ From `packages/twenty-cli`, after building the shared, SDK and client SDK:
45
+
46
+ ```sh
47
+ node ../../node_modules/vitest/vitest.mjs run --config vitest.config.ts src/app/client src/commands/app/__tests__/app-real-sdk.spec.ts --maxWorkers=1
48
+ ```
49
+
50
+ Parity runs the SDK reference and the production CLI worker against
51
+ the same schema and app path, comparing every generated source file and both
52
+ compiled bundles byte for byte. Reusing the path keeps esbuild's source-path
53
+ comments identical without normalizing file contents. The app has its own client
54
+ SDK and no application SDK. The generated CommonJS client is loaded to verify
55
+ its `CoreApiClient` export.
56
+
57
+ Other cases cover a missing or hoisted-only package, invalid manifests and
58
+ schemas, missing exports, a non-callable API, selection of the app's generator,
59
+ pre-cancellation, in-flight cancellation and preserved failures/partial writes.
60
+ The local HTTP apply contract exercises CLI client
61
+ generation, along with snapshot upload, synchronization, pull-base recording
62
+ and release. No live workspace is contacted.
@@ -0,0 +1,35 @@
1
+ # App development session
2
+
3
+ The parent owns observation and two bounded scheduling lanes: one build worker
4
+ and one remote apply. `runDevLoop` keeps only the latest pending snapshot. Every
5
+ snapshot is released once, including superseded builds and cancellation; the
6
+ active apply retains its files until its reads settle. The skip key is the last
7
+ acknowledged remote content hash, cleared after a failed apply.
8
+
9
+ `buildDevSnapshot` copies declared, size/SHA-256-verified artifacts into a fresh
10
+ `dev-*` directory while the compiler's snapshot lease is held. The worker then
11
+ releases its own snapshot and exits, so retained builds do not retain compiler
12
+ heaps. At most three snapshot directories exist for an active session: in-flight
13
+ apply, compiler source, and its copy. No hard links or mutable output tree.
14
+
15
+ Input collection is private worker metadata, absent on ordinary build requests.
16
+ It observes esbuild loads/resolution parents, static-file copies and TypeScript
17
+ reads. Missing TypeScript package-scope probes cannot expand watching into
18
+ unrelated ancestor directories. Inputs have stamps captured before reads; after
19
+ attaching the watcher, changed stamps invalidate the result. A successful graph
20
+ replaces the previous graph; a failure watches the last successful graph plus
21
+ only that attempt's inputs. External directory observation is shallow.
22
+
23
+ `applyAppBuild` handles remote operations. Dev supplies a
24
+ revision-specific approval signal and a check immediately before the first
25
+ write. Once writes start, a newer build does not cancel those writes. Client
26
+ generation pauses compilation and invalidates pending builds; its successful
27
+ input key plus output stamps prevent generation loops. A failure after client
28
+ writes start ends the session. Normal compilation failures keep watching;
29
+ abnormal worker exits and watcher failures require restart.
30
+
31
+ There is no incremental compiler cache or package-manager dependency watcher.
32
+ Arbitrary app-code filesystem reads, concurrent independent dev processes,
33
+ and SIGKILL recovery are outside this
34
+ session's guarantees. See the [command reference](../../../docs/commands.md#develop-an-app) for
35
+ user-visible limits.
@@ -0,0 +1,48 @@
1
+ # CLI app function execution
2
+
3
+ The command selects one deployed function from the
4
+ local app's identifiers, executes it once with a payload, and reports status,
5
+ duration, data, error and logs. Name matching is exact. Hooks select their
6
+ function identifier; executing an uninstall hook does not uninstall the app.
7
+ A non-SUCCESS result exits with failure. It executes deployed code, so local
8
+ edits must be synced with apply or dev first.
9
+
10
+ ## Contract
11
+
12
+ - Flags follow the space-separated command surface and kebab-case convention:
13
+ `app exec --name`, `--universal-identifier`, `--post-install`, `--pre-install`
14
+ or `--uninstall-hook`. Exactly one selector is required.
15
+ - The CLI keeps no saved build manifest. It uses the `buildManifest`
16
+ worker to load definitions with the app's authoring SDK, without bundling or
17
+ typechecking. Worker output becomes diagnostics; credentials are not passed to it.
18
+ - Function selection checks both the local manifest identifiers and the remote
19
+ installed application's ID. The `findOneApplication` lookup prevents
20
+ copied or stale identifiers from selecting another installed app's function.
21
+ This needs `APPLICATIONS` in addition to execution's `WORKFLOWS` permission.
22
+ - The current server requires a user-bound token for execution. Browser-login
23
+ OAuth remotes are supported; API-key targets fail before loading app source.
24
+ The server still decides which functions that user can run: workspace custom
25
+ app functions, functions exposed as workflow actions, or functions from an
26
+ app registered by this workspace. The CLI does not bypass those checks.
27
+ - Payloads use the JSON-object input reader: inline JSON, `@file` or
28
+ `-` for stdin, default `{}`. Non-object values are rejected.
29
+ - Human output escapes terminal controls. JSON success includes function/app
30
+ identity, status, `durationMilliseconds`, data, logs, error and diagnostics.
31
+ A completed non-SUCCESS result exits 1 with `EXECUTION_FAILED` and retains
32
+ that information in `error.details`, with `outcome: "completed"`. A missing
33
+ function exits 4; ambiguous selection exits 6; bad input exits 2.
34
+ - The execution request deadline is the deployed function's `timeoutSeconds`
35
+ plus the transport's 60-second allowance. Its HTTP dispatcher header/body
36
+ deadlines use the same value, avoiding Node's default five-minute cutoff.
37
+ Other commands use their own deadlines and lazy transport loading.
38
+ Proxies and the server can still close a request earlier.
39
+ - Execution is never retried. Acknowledged authentication, permission,
40
+ not-found and input/validation rejections have `outcome: "not-started"`.
41
+ Transport failure, cancellation, internal server error or an invalid response
42
+ after submission have `outcome: "unknown"`: execution may still be running.
43
+ Ctrl+C exits 130 and cannot stop the server-side function or undo its effects.
44
+
45
+ Command tests use the public command runner and real HTTP metadata transport,
46
+ with controlled worker results. They cover selection, application binding,
47
+ payloads, returned errors, rejection/unknown outcomes, cancellation and no
48
+ replay. These tests do not establish live workspace authorization or execution.
@@ -0,0 +1,44 @@
1
+ # CLI app function logs
2
+
3
+ The command subscribes to POST `/metadata`,
4
+ `logicFunctionLogs(input: LogicFunctionLogsInput!)`, application universal
5
+ identifier and optional exact function name or universal identifier. It receives
6
+ future completed-execution output, not history or per-line execution progress.
7
+ Names can repeat within one app; name selection is a filter matching all of them.
8
+
9
+ ## Contract
10
+
11
+ - `app logs` uses the CLI app-identity worker. Only the app definition is needed;
12
+ no function bundles, full manifest build or generated client is required. Source
13
+ diagnostics use CLI output, and target credentials stay outside the worker.
14
+ - The CLI target supplies OAuth or API-key credentials, environment proxy
15
+ routing and cancellation. Server authorization requires `WORKFLOWS`,
16
+ workspace scoping and per-payload application access checks. There is no
17
+ execution mutation, app registration or workspace audit-log query.
18
+ - The selection requests nullable `name` and `universalIdentifier` fields. Server
19
+ schemas rejecting only these fields trigger one explicit, warned fallback to
20
+ `{ logs }`, before any record has been emitted. Unrelated validation,
21
+ authorization, network and execution errors are never retried. An explicit
22
+ filter provides that identity if the server cannot; unknown values stay null.
23
+ - A pull-driven reader uses the GraphQL `next`/`complete` SSE protocol,
24
+ awaits output and reads the next
25
+ frame only when the consumer is ready. It handles comments, multiline data,
26
+ split UTF-8 and LF/CRLF/CR framing. Each frame is capped at 16 MiB; the session
27
+ has no total byte cap. Reader locks and HTTP requests are released on exit.
28
+ - Setup has a 60-second deadline. After setup there is no whole-session timer;
29
+ the HTTP body's 60-second idle deadline detects missing heartbeats. Yoga sends
30
+ comments every 12 seconds in production. Undici suspends its body deadline
31
+ while the body is paused for backpressure and refreshes it on resume.
32
+ - There is no automatic reconnect or replay. EOF without `complete`, broken
33
+ JSON or records, redirects, oversized frames and network failures stop the
34
+ command. Errors retain `recordCount`; network failures explain the gap.
35
+ Explicit server completion exits 0, Ctrl+C exits 130. Human text escapes
36
+ terminal control characters; NDJSON emits start, connection progress, records
37
+ and a final result/error. A compatibility fallback may establish two
38
+ connections and therefore emit two connection progress events.
39
+
40
+ Tests cover the public command with a real local HTTP transport, duplicate-name
41
+ filters, compatibility failures, proxy routing, per-frame bounds, output
42
+ backpressure, idle heartbeats, stalled connections, completion and cancellation.
43
+ These tests verify protocol behavior, not live workspace authorization or
44
+ execution.
@@ -0,0 +1,74 @@
1
+ # Manifest generation
2
+
3
+ The manifest builder constructs and validates metadata and compiles translations
4
+ for build, plan and apply. The worker evaluates definitions through
5
+ [app/source](../source/README.md); bundling and final artifact checksums belong to
6
+ [app/bundles](../bundles/README.md).
7
+
8
+ See the [app tooling overview](../README.md) for package ownership.
9
+
10
+ ## Contract
11
+
12
+ The builder discovers definitions and assets, derives default object fields and
13
+ permission identifiers, infers logic-function input schemas, resolves lifecycle
14
+ hooks, reads the README and validates entity relationships, identifiers, role
15
+ permissions and conditional availability. Manifests are sorted and carry
16
+ checksum placeholders that the bundler replaces with final artifact checksums.
17
+
18
+ `package.json`'s `engines.twenty` sets
19
+ `application.requiredServerVersionRange`, with surrounding whitespace trimmed;
20
+ an absent or empty value becomes `null`. No CLI version is substituted. The
21
+ builder reports package dependency warnings and validates empty lockfiles.
22
+
23
+ Translation compilation reuses the locale catalog helpers shared with
24
+ pull. It preserves the distinction between no locale directory (`undefined`)
25
+ and an empty one (`{}`), context-dependent message IDs, authored-over-compiled
26
+ precedence, orphan compiled translations, collision handling and skipped-file
27
+ warnings. It reads catalogs without rewriting them.
28
+
29
+ Select options without an `id` receive a deterministic ID from their label and
30
+ field identifier. Keep an explicit `id` when changing an option's label or value,
31
+ so existing view filters continue to reference the same option. Duplicate option
32
+ IDs fail validation. Pulled options already carry their server IDs.
33
+
34
+ ## Implementation
35
+
36
+ - Definition discovery, detection and evaluation reuse `app/source`, including
37
+ its source and ignore globs and validation-result shape check.
38
+ - Source and public-asset discovery is sorted so warning, error and entity file
39
+ order is deterministic.
40
+ - The loader result accepts a compile-time config type, with the same runtime
41
+ shape check. Config types describe the authoring values consumed by the builder.
42
+ Handler parameters use `never` and return `unknown`; front components expose
43
+ only the `name` consumed here. The builder never calls those callbacks.
44
+ - The version helper requires an explicit app path; the process working directory
45
+ cannot choose another app's version.
46
+ - Filesystem reads use Node and the CLI `pathExists` helper. The JSON reader
47
+ propagates parse errors.
48
+ - The worker resolves the app's authoring SDK with the source compatibility check,
49
+ returns structured errors and warnings, checks cancellation between phases and
50
+ stops esbuild before exiting. It uses credential filtering,
51
+ bounded output capture and cancellation/termination behavior. It does not hold
52
+ a snapshot or write build state.
53
+
54
+ ## Verification
55
+
56
+ From `packages/twenty-cli`, after building the repository SDK, shared and UI
57
+ packages:
58
+
59
+ ```bash
60
+ node ../../node_modules/vitest/vitest.mjs run --config vitest.config.ts --maxWorkers=1 src/app/manifest/__tests__/manifest-parity.spec.ts
61
+ ```
62
+
63
+ The suite bundles the SDK implementation as a test-only reference. It
64
+ compares manifests, entity file paths, errors and warnings for every repository
65
+ fixture and a freshly initialized app. It also exercises the production CLI
66
+ worker using a copy of the real SDK's authoring entry points without `./build`,
67
+ `./cli` or their implementation files.
68
+
69
+ The SDK reference's discovery is sorted to match the CLI's deterministic input
70
+ order. Comparisons remove only JSON-omitted `undefined` properties, matching the
71
+ worker IPC boundary; arrays and diagnostics are not reordered. The invalid-app
72
+ fixture throws `Invalid UUID` in both implementations. Additional cases cover
73
+ duplicate identifiers, empty lockfiles, translations, source output and credential filtering.
74
+ Unit tests cover individual validation and translation rules.
@@ -0,0 +1,28 @@
1
+ # CLI application pull
2
+
3
+ Pull reconciles the server's application export with local definitions and a
4
+ target-bound baseline. It preserves unsupported and local-only entities, reports
5
+ overwritten local edits, and commits source changes and the new baseline as one
6
+ transaction. Source generation and translation behavior are covered by parity
7
+ and reconciliation tests.
8
+
9
+ See the [app tooling overview](../README.md) for package boundaries.
10
+
11
+ ## Contract
12
+
13
+ - Source scanning stays inside the disposable CLI worker so configs containing functions are never serialized through IPC. Only the server export and report cross that boundary. Selected credentials are removed from the worker environment. App source is trusted developer code, not sandboxed code.
14
+ - The wire/export and baseline boundary check identity and envelope fields, not every metadata collection. `toPullManifest` is the single explicit assertion into the typed writers, preserving their trust in the server schema. Missing/null object and field collections are consumed with `?? []`, like the other collections. Malformed entity contents can still make reconciliation fail before writing. We do not duplicate the server's schema.
15
+ - The baseline file is an envelope whose `manifest` member is an `ExportedManifest`. Reconciliation walks arrays generically without maintaining a second collection list. Unrecognized collections and non-array metadata survive; protected or unreconciled definitions retain their prior baseline entries.
16
+ - `preparePullEntities` resolves the source layout once for current and baseline entities. Planning, overwrite reporting and nested-definition protection consume these prepared entities. A definition can leave a parent only when another retained or written file contains it, or its remote deletion is confirmed.
17
+ - Fields already declared with `defineField` stay in their existing files, even when the server exports them inside an object. Object generation excludes those fields; baseline comparisons use the same source layout. Pull also removes duplicate inline copies left in object files, while retaining the standalone definitions.
18
+ - Before destination writes, `assertPullSdkExports` checks the actual imports emitted by the writer against the app's SDK. It checks export presence, not every config shape; run typecheck to validate the generated definitions against the installed SDK.
19
+ - Temporary files and the baseline live under `.twenty/cli`. Apply and pull share `createPullBaseWrite` as the serializer. The baseline is bound to the API URL, workspace UUID and application UUID. Pull stages it with mode 0600 and writes it after the source files.
20
+ - The baseline can carry `sourceFingerprints`, a SHA-256 per app-relative source and locale file. Apply captures them before building. Pull reports an overwritten local change when a file it rewrites or deletes no longer matches its fingerprint. Without a fingerprint it compares the file with the writer's rendering of the baseline, which is reliable only for source produced by that writer. Pull carries fingerprints forward, updating the files it writes and dropping the ones it deletes.
21
+ - Ordinary I/O failures restore originals. Cancellation is checked before committing the staged plan, not halfway through it. An acknowledged successful commit wins over cancellation. The parent can forcibly kill an unresponsive worker after its grace period, so an unacknowledged result has outcome `unknown`, never a claim that rollback succeeded. Failed rollback retains backups and reports their directory; failure to clean up after a completed commit has outcome `pulled`.
22
+ - Human reporting uses CLI terminal escaping, machine reporting contains the full coverage and path lists, and gaps warn without stopping successful writes. Unknown coverage status strings remain visible. Pull does not prompt for approval or require a clean Git checkout; remote changes overwrite conflicting local edits and are included in the report.
23
+
24
+ Translation string extraction uses the CLI's TypeScript parser. Traversal, static-string rules, whitespace normalization and deduplication are checked against the SDK reference by parity tests. It does not depend on ts-morph or load parser tooling for help and command discovery.
25
+
26
+ Pull accepts metadata exports with `files: []` and refuses nonempty source/dependency-file exports. It does not restore package files, generate the typed client or write logic-function/front-component source. Export responses have a 16 MiB cap. Pull does not modify the workspace.
27
+
28
+ Tests cover writers, planning and reconciliation. Command tests build the actual Vite worker, exercise an authoring-only SDK, local mock HTTP, credentials/output isolation, SDK export refusal and forced termination after the first destination write. The repository SDK tests cover canonical generated definitions, nested coverage, translation preservation, target mismatch, overwrite reporting and rollback.
@@ -0,0 +1,33 @@
1
+ # App source loading
2
+
3
+ This CLI-owned source loader powers `twenty app pull` and the [manifest builder](../manifest/README.md) used by public build, plan and apply commands.
4
+
5
+ Package boundaries are documented in the
6
+ [app tooling overview](../README.md).
7
+
8
+ ## Boundary
9
+
10
+ `readAppIdentity` checks the installed SDK without importing it, then evaluates the application's definition in a disposable CLI worker. Source is trusted executable developer code. The child contains exits, captures bounded output and can be killed on cancellation; it is not a filesystem or network sandbox. Selected workspace credentials are removed from its environment by the worker launcher.
11
+
12
+ `scanProjectSourceFiles` stays inside the worker. Its configs may contain functions and React components, so reconciliation runs in that same worker rather than send configs over JSON IPC. Only the application UUID, display name and diagnostics cross the identity boundary.
13
+
14
+ ## Compatibility
15
+
16
+ - The app supplies its own `twenty-sdk`, version `>=1.23.0`, with resolvable `twenty-sdk/define` and `twenty-sdk/front-component` entry points. The installed SDK's Node requirement is checked.
17
+ - There is no upper SDK version cap. Every evaluated definition must return the public `ValidationResult` shape: boolean `success`, object `config`, string-array `errors`, and optional string-array `warnings`. An incompatible shape fails with `SDK_SOURCE_UNSUPPORTED`. This validates the loader contract, not every future SDK behavior.
18
+ - The CLI owns esbuild, its TypeScript parser and tinyglobby as runtime dependencies. These load only for source operations. The app does not need to install TypeScript for source loading. Build and typecheck use the project's compiler separately.
19
+ - The pull writer separately checks the authoring exports its generated files use; this loader floor does not establish that every SDK since 1.23 supports all current manifest collections.
20
+
21
+ ## Source contract
22
+
23
+ Source discovery covers root and nested `.ts`/`.tsx` files, excluding declarations, `node_modules`, `dist` and `.twenty`. Only a direct top-level `export default defineX(...)` is classified as a definition. Aliases, namespace calls and variable re-exports remain unsupported. Helpers reserve their paths without being evaluated on their own; imported helpers run as dependencies of a definition.
24
+
25
+ Definitions are bundled using the app's tsconfig and imports, then evaluated with a require rooted at the app. React resolves from the app. UI and generated-client imports use extraction stubs; these are not working UI or client implementations. CSS imports are ignored and conditional-availability expressions are transformed for extraction.
26
+
27
+ An ordinary extraction failure marks a scanned definition unreadable, retaining its path so reconciliation can avoid overwriting it. SDK validation errors leave the config available for reporting. Reading identity evaluates only application definitions, rejects duplicate or invalid application UUIDs, and returns `null` when no application is declared.
28
+
29
+ The loader checks SDK compatibility, validation-result shape and cancellation between files. Discovery is sorted: the loader, the identity reader and the scanner share `listApplicationSourceFiles`, so results do not depend on filesystem timing. Detection uses the function name rather than its import origin: a local helper named `defineObject`, for example, must be renamed if it returns an incompatible shape. Such a mismatch fails the scan. Source rewriting and reconciliation live in the adjacent `pull` module.
30
+
31
+ ## Verification
32
+
33
+ Run the source-loader, SDK-resolution and worker tests with the CLI Vitest configuration. The repository-SDK fixture requires its existing SDK/UI/shared artifacts to be built, as do the other CLI app tests. Worker tests use an authoring-only SDK without a project compiler, exercise output and credential isolation, and cover source exits and cancellation.