twenty 0.3.0 → 2.48.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 (198) hide show
  1. package/CONTRIBUTING.md +174 -0
  2. package/LICENSE +21 -0
  3. package/README.md +86 -3
  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-VJqniwXy.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-BBNc4zkt.cjs +5 -0
  39. package/dist/chunks/build-source-manifest-BMjw7BvT.cjs +2 -0
  40. package/dist/chunks/build-source-snapshot-BbQlyo_m.cjs +206 -0
  41. package/dist/chunks/check-node-requirement-CGBKvEqH.cjs +1 -0
  42. package/dist/chunks/cli-error-CSsayo0u.cjs +1 -0
  43. package/dist/chunks/cli-node-version-range.constant-D7fE8edk.cjs +1 -0
  44. package/dist/chunks/cli-version.constant-BJdY4dXs.cjs +1 -0
  45. package/dist/chunks/collect-translatable-strings-C3TWaO1V.cjs +1 -0
  46. package/dist/chunks/collect-watch-inputs-Cexr5YYZ.cjs +1 -0
  47. package/dist/chunks/compile-application-translations-Dhfvrs__.cjs +1 -0
  48. package/dist/chunks/config-file-mode.constant-DLBzKKUl.cjs +1 -0
  49. package/dist/chunks/confirm-in-terminal-BxEM5lxE.cjs +1 -0
  50. package/dist/chunks/constants-Cf5-CwMu.cjs +1 -0
  51. package/dist/chunks/constants-Du9J-8Ug-CD8GTrut.cjs +23 -0
  52. package/dist/chunks/convert-to-label-D24GvvpW.cjs +1 -0
  53. package/dist/chunks/create-app-not-installed-error-DY-iuPPx.cjs +1 -0
  54. package/dist/chunks/create-cancelled-error-DgrNfOv2.cjs +1 -0
  55. package/dist/chunks/create-http-status-error-Dd3336c8.cjs +1 -0
  56. package/dist/chunks/create-metadata-client-DEBMHFDo.cjs +6 -0
  57. package/dist/chunks/create-pull-base-write-Cl1jfQ6l.cjs +1 -0
  58. package/dist/chunks/create-rest-client-r3OA3fUu.cjs +60 -0
  59. package/dist/chunks/create-tooling-failure-B2FmNwR2.cjs +1 -0
  60. package/dist/chunks/credential-kind-labels.constant-CKdoQF8o.cjs +1 -0
  61. package/dist/chunks/extract-manifest-from-file-D3w7Ampa.cjs +1 -0
  62. package/dist/chunks/extract-source-translations-BLQ-XFuN.cjs +1 -0
  63. package/dist/chunks/fetch-metadata-inspection-BwJjaHCB.cjs +1 -0
  64. package/dist/chunks/fetch-signed-in-identity-CWFlsW7e.cjs +1 -0
  65. package/dist/chunks/fetch-workspace-id-DJFdCITG.cjs +1 -0
  66. package/dist/chunks/find-remote-D48r20g0.cjs +1 -0
  67. package/dist/chunks/format-app-duration-gu2oRLB4.cjs +1 -0
  68. package/dist/chunks/format-body-CJyi3Ukr.cjs +1 -0
  69. package/dist/chunks/format-data-value-C5itQm1i.cjs +1 -0
  70. package/dist/chunks/format-details-COEwKz_M.cjs +2 -0
  71. package/dist/chunks/format-display-path-gEepVJDg.cjs +1 -0
  72. package/dist/chunks/format-metadata-fields-BozY419E.cjs +2 -0
  73. package/dist/chunks/format-metadata-owner-C6CksDIK.cjs +1 -0
  74. package/dist/chunks/format-progress-line-C5nYDVHB.cjs +1 -0
  75. package/dist/chunks/format-table-4drtI5sP.cjs +2 -0
  76. package/dist/chunks/fs-utils-Bl1fQcaJ.cjs +2 -0
  77. package/dist/chunks/generate-application-client-BApuJWQO.cjs +1 -0
  78. package/dist/chunks/get-app-plan-summary-F_MjZOGe.cjs +8 -0
  79. package/dist/chunks/get-client-generation-skip-reason-B5f_gcok.cjs +1 -0
  80. package/dist/chunks/get-client-sdk-package-path-2U3kDCRq.cjs +1 -0
  81. package/dist/chunks/get-config-path-C-D-QHuX.cjs +1 -0
  82. package/dist/chunks/get-credential-kind-JI9heHe2.cjs +1 -0
  83. package/dist/chunks/get-function-input-schema-GNk3NRLJ-oQ5Evald.cjs +1 -0
  84. package/dist/chunks/get-tooling-error-context-C69TBSej.cjs +1 -0
  85. package/dist/chunks/global-flags.constant-BPHQAhn7.cjs +1 -0
  86. package/dist/chunks/has-yarn-plug-and-play-X3ay5Xl0.cjs +1 -0
  87. package/dist/chunks/is-access-token-expiring-DIimejBr.cjs +1 -0
  88. package/dist/chunks/is-application-not-found-error-D0BAcKyX.cjs +1 -0
  89. package/dist/chunks/is-exported-manifest-RF_O2ibz.cjs +1 -0
  90. package/dist/chunks/is-inside-directory-DHVCQE5C.cjs +1 -0
  91. package/dist/chunks/is-interaction-allowed-CF-ko1KI.cjs +1 -0
  92. package/dist/chunks/is-locale-catalog-Dzj6m_Xt.cjs +1 -0
  93. package/dist/chunks/is-same-universal-identifier-BCBib3RP.cjs +1 -0
  94. package/dist/chunks/is-supported-locale-C9iuFQ39.cjs +1 -0
  95. package/dist/chunks/kebab-case-D0usAzG-.cjs +1 -0
  96. package/dist/chunks/list-application-source-files-jEau5Rie.cjs +1 -0
  97. package/dist/chunks/locale-catalog-format-BT6ommA1.cjs +20 -0
  98. package/dist/chunks/metadata-BtOyR6x0.cjs +1 -0
  99. package/dist/chunks/metadata-page.constant-CnR03vnh.cjs +1 -0
  100. package/dist/chunks/oauth-timing.constant-BClMQcKn.cjs +1 -0
  101. package/dist/chunks/open-browser-LPuVspT7.cjs +1 -0
  102. package/dist/chunks/parse-api-url-C9MmGtLY.cjs +1 -0
  103. package/dist/chunks/parse-app-export-BT6nM7Ki.cjs +1 -0
  104. package/dist/chunks/parse-extraction-locale-CM7sARBo.cjs +1 -0
  105. package/dist/chunks/parse-graphql-response-DbYu3YlQ.cjs +1 -0
  106. package/dist/chunks/parse-json-input-DaXTn_6C.cjs +1 -0
  107. package/dist/chunks/print-typescript-value-BoxeFXca.cjs +3 -0
  108. package/dist/chunks/pull-source-DNswr_80.cjs +1 -0
  109. package/dist/chunks/read-app-identity-CtO3wiSx.cjs +1 -0
  110. package/dist/chunks/read-application-identifier-B51ax_Q6.cjs +1 -0
  111. package/dist/chunks/read-application-identity-Bx8B9Bel.cjs +1 -0
  112. package/dist/chunks/read-command-values-uSJqBG_E.cjs +1 -0
  113. package/dist/chunks/read-config-LKYhSPOb.cjs +1 -0
  114. package/dist/chunks/read-input-value-Cxgh_r2g.cjs +1 -0
  115. package/dist/chunks/read-json-object-BONMjSVo.cjs +1 -0
  116. package/dist/chunks/read-snapshot-file-ClIpemjz.cjs +1 -0
  117. package/dist/chunks/read-source-identity-C4eKC14c.cjs +1 -0
  118. package/dist/chunks/read-watch-input-stamp-BxeOb0vI.cjs +1 -0
  119. package/dist/chunks/record-pull-base-CdTOndSf.cjs +1 -0
  120. package/dist/chunks/record-typecheck-config-inputs-DcCY-_Z1.cjs +1 -0
  121. package/dist/chunks/request-oauth-tokens-CS79pVAp.cjs +1 -0
  122. package/dist/chunks/request-timeout-milliseconds.constant-DtyQHrll.cjs +1 -0
  123. package/dist/chunks/require-approval-D0FfoHhF.cjs +1 -0
  124. package/dist/chunks/resolve-app-project-BQnFOdLH.cjs +1 -0
  125. package/dist/chunks/resolve-command-target-CKYtQQPx.cjs +1 -0
  126. package/dist/chunks/resolve-inside-sdk-Cc_y-RWT.cjs +1 -0
  127. package/dist/chunks/resolve-metadata-object-CRWbF9Ce.cjs +1 -0
  128. package/dist/chunks/resolve-project-typescript-CZW_p97L.cjs +1 -0
  129. package/dist/chunks/resolve-request-url-BrqiqInn.cjs +1 -0
  130. package/dist/chunks/resolve-source-sdk-CGEJC-UH.cjs +1 -0
  131. package/dist/chunks/result-item-limit.constant-CDrOhiWD.cjs +1 -0
  132. package/dist/chunks/run-api-graphql-command-KlIMjpOY.cjs +1 -0
  133. package/dist/chunks/run-api-rest-command-W4gCU562.cjs +1 -0
  134. package/dist/chunks/run-app-add-command-C6rIt02R.cjs +163 -0
  135. package/dist/chunks/run-app-apply-command-BckNNni9.cjs +2 -0
  136. package/dist/chunks/run-app-build-command-DW0r-w2C.cjs +2 -0
  137. package/dist/chunks/run-app-dev-command-C3U-3OiV.cjs +2 -0
  138. package/dist/chunks/run-app-exec-command-DNeCk5qs.cjs +3 -0
  139. package/dist/chunks/run-app-generate-client-command-HAgBNVFU.cjs +2 -0
  140. package/dist/chunks/run-app-init-command-dTbY28oS.cjs +2 -0
  141. package/dist/chunks/run-app-install-command-BX30OsrQ.cjs +1 -0
  142. package/dist/chunks/run-app-logs-command-CdAXR-XY.cjs +9 -0
  143. package/dist/chunks/run-app-operation--X_q0SAn.cjs +1 -0
  144. package/dist/chunks/run-app-plan-command-Bm1eXl7V.cjs +1 -0
  145. package/dist/chunks/run-app-publish-command-Cdb3OlI2.cjs +3 -0
  146. package/dist/chunks/run-app-pull-command-DljZP4Ey.cjs +3 -0
  147. package/dist/chunks/run-app-translations-extract-command-Cc053lZ6.cjs +2 -0
  148. package/dist/chunks/run-app-typecheck-command-B7o20_BR.cjs +1 -0
  149. package/dist/chunks/run-app-uninstall-command-DJsWZtoI.cjs +2 -0
  150. package/dist/chunks/run-auth-login-command-21pHzpvT.cjs +1 -0
  151. package/dist/chunks/run-auth-logout-command-DWOafJmL.cjs +2 -0
  152. package/dist/chunks/run-auth-status-command-BBAcouvs.cjs +1 -0
  153. package/dist/chunks/run-auth-token-command-D4geJ-rx.cjs +1 -0
  154. package/dist/chunks/run-cli-D1VWVXar.cjs +23 -0
  155. package/dist/chunks/run-commands-command-6x3V48LL.cjs +2 -0
  156. package/dist/chunks/run-data-get-command-DrgvC-zi.cjs +1 -0
  157. package/dist/chunks/run-data-list-command-Cs8GAHbD.cjs +1 -0
  158. package/dist/chunks/run-doctor-command-BM4pkNY0.cjs +2 -0
  159. package/dist/chunks/run-metadata-field-describe-command-CrmzNFh8.cjs +3 -0
  160. package/dist/chunks/run-metadata-field-list-command-B2CbERvZ.cjs +3 -0
  161. package/dist/chunks/run-metadata-object-describe-command-PLt5ySLJ.cjs +3 -0
  162. package/dist/chunks/run-metadata-object-list-command-BcnWKmgh.cjs +2 -0
  163. package/dist/chunks/run-open-command-CzMO5wI4.cjs +1 -0
  164. package/dist/chunks/run-remote-list-command-37zf0rSp.cjs +1 -0
  165. package/dist/chunks/run-remote-remove-command-B-XUqYBY.cjs +2 -0
  166. package/dist/chunks/run-remote-rename-command-Bmg3nZfB.cjs +1 -0
  167. package/dist/chunks/run-remote-use-command-r2NUlgWw.cjs +1 -0
  168. package/dist/chunks/run-version-command-DfDcA927.cjs +1 -0
  169. package/dist/chunks/select-target-CEy96CC9.cjs +1 -0
  170. package/dist/chunks/sign-in-to-remote-again-9cHGpXda.cjs +1 -0
  171. package/dist/chunks/sign-in-with-browser-StQ_Fkku.cjs +1 -0
  172. package/dist/chunks/style-BR52fK4V.cjs +1 -0
  173. package/dist/chunks/target-environment-variable.constant-D7z8AihP.cjs +1 -0
  174. package/dist/chunks/to-cli-error-BBYf_wqo.cjs +1 -0
  175. package/dist/chunks/to-public-target-T4zHrdLQ.cjs +1 -0
  176. package/dist/chunks/to-worker-output-diagnostics-BeJKIsDG.cjs +2 -0
  177. package/dist/chunks/typecheck-application-DLdoLIDx.cjs +2 -0
  178. package/dist/chunks/undici-D3mKdfwl.cjs +95 -0
  179. package/dist/chunks/update-config-CNOHxgkF.cjs +1 -0
  180. package/dist/chunks/utils-BEfQWzTA-z2Y1wZJq.cjs +35 -0
  181. package/dist/chunks/validate-app-path-Dau-PC0a.cjs +1 -0
  182. package/dist/chunks/validate-remote-name-w-qnr43w.cjs +1 -0
  183. package/dist/chunks/with-phase-details-Dof8Ao9R.cjs +1 -0
  184. package/dist/chunks/write-config-atomically-gr2Ldzm8.cjs +1 -0
  185. package/dist/cli.cjs +2 -0
  186. package/docs/commands.md +652 -0
  187. package/package.json +39 -3
  188. package/src/app/README.md +43 -0
  189. package/src/app/add/README.md +69 -0
  190. package/src/app/bundles/README.md +85 -0
  191. package/src/app/client/README.md +62 -0
  192. package/src/app/dev/README.md +35 -0
  193. package/src/app/exec/README.md +48 -0
  194. package/src/app/function-logs/README.md +44 -0
  195. package/src/app/manifest/README.md +74 -0
  196. package/src/app/pull/README.md +28 -0
  197. package/src/app/source/README.md +33 -0
  198. package/src/app/typecheck/README.md +67 -0
@@ -0,0 +1,652 @@
1
+ # Command reference
2
+
3
+ For available commands and flags, run `twenty commands` or
4
+ `twenty <command> --help`. This reference describes behavior, permissions and
5
+ failure recovery. See the [README](../README.md) to build and start the CLI.
6
+
7
+ - [Connections and authentication](#connections-and-authentication)
8
+ - [Raw API requests](#raw-api-requests)
9
+ - [Network proxies](#network-proxies)
10
+ - [Doctor](#diagnose-your-setup)
11
+ - [Open](#open-the-workspace)
12
+ - [Metadata](#inspect-the-data-model)
13
+ - [Records](#read-records)
14
+ - [App init](#create-an-app) and [add](#add-definitions-to-an-app)
15
+ - [Build and typecheck](#build-and-check-an-app)
16
+ - [Plan](#preview-app-changes), [apply](#apply-an-app) and [dev](#develop-an-app)
17
+ - [Pull](#pull-an-app) and [uninstall](#uninstall-an-app)
18
+ - [Function execution](#execute-an-app-function) and [logs](#watch-app-logs)
19
+ - [Expired browser sessions](#expired-browser-sessions)
20
+ - [Output and exit codes](#output)
21
+
22
+ ## Connections and authentication
23
+
24
+ ```bash
25
+ twenty auth login --url https://acme.twenty.com
26
+ twenty auth status
27
+ ```
28
+
29
+ The connection is named `default` when neither `--name` nor `--remote` is supplied.
30
+ Run `twenty auth login` again to sign in to that saved URL. To keep several
31
+ connections, give each a name:
32
+
33
+ ```bash
34
+ twenty auth login --url https://acme.twenty.com --name dev --use
35
+ twenty auth status --remote dev
36
+ twenty remote list
37
+ twenty remote use dev
38
+ ```
39
+
40
+ A remote is a named workspace connection. Browser login requires an interactive
41
+ terminal and saves a user OAuth session after verifying it with the server.
42
+
43
+ For scripts, pass an API key through standard input:
44
+
45
+ ```bash
46
+ printf '%s' "$TWENTY_API_KEY" | twenty auth login --with-token --url https://acme.twenty.com --name ci
47
+ ```
48
+
49
+ Credentials and remote settings are saved in `~/.twenty/config.json`, with
50
+ owner-only file permissions. Login validates credentials before saving them.
51
+ `--use` makes the remote the default; the first remote also becomes the default.
52
+ Creating the connection named `default` does not switch an existing default
53
+ remote unless you pass `--use`.
54
+ Use `twenty auth login --remote dev` to sign in again to a saved URL. Changing an
55
+ existing remote's URL requires `--replace`.
56
+
57
+ Commands select a workspace in this order:
58
+
59
+ 1. `--remote <name>`, ignoring environment target settings with a warning.
60
+ 2. `TWENTY_REMOTE`, which cannot be combined with either API environment variable.
61
+ 3. `TWENTY_API_URL` and `TWENTY_API_KEY`, which must both be set and are not saved.
62
+ 4. The saved default remote.
63
+
64
+ ```bash
65
+ twenty remote rename dev development
66
+ twenty auth logout --remote development
67
+ twenty remote remove development
68
+ ```
69
+
70
+ Logout forgets saved credentials but keeps the remote. Removing a remote deletes
71
+ both. Neither command revokes the server-side credential. `twenty auth token`
72
+ prints the selected credential for use in another tool; its output is a secret,
73
+ including in JSON mode.
74
+
75
+ `--no-input` disables prompts and browser opening. JSON/NDJSON output, redirected
76
+ stdin and CI also disable interaction. Pass required values and explicit
77
+ command-specific approvals, such as `app apply --create` or `--yes`, in scripts.
78
+ Disabling prompts does not approve an operation.
79
+
80
+ ## Raw API requests
81
+
82
+ ```bash
83
+ twenty api rest '/rest/companies?limit=5'
84
+ twenty api graphql --query '{ companies(first: 2) { edges { node { name } } } }'
85
+ twenty api graphql --metadata --query '{ currentWorkspace { displayName } }'
86
+ ```
87
+
88
+ Both commands use the selected connection. REST paths stay inside its API URL;
89
+ credentials are not sent to an arbitrary host. REST
90
+ uses GET by default. `--body` accepts JSON inline, `@file.json`, or `-` for stdin
91
+ and requires an explicit `--method`.
92
+
93
+ ```bash
94
+ twenty api rest /rest/companies --method POST --body '{"name":"Example"}'
95
+ twenty api graphql --query @query.graphql --variables @variables.json
96
+ ```
97
+
98
+ GraphQL uses the core endpoint by default and the metadata endpoint with
99
+ `--metadata`. `--query` accepts a document inline, `@file.graphql` or `-`;
100
+ `--variables` accepts a JSON object through the same input forms. Only one option
101
+ can read stdin. Mutations and REST writes execute directly, without a
102
+ confirmation step. Server permissions still apply.
103
+
104
+ Human output displays the response body. In JSON mode, REST returns status,
105
+ selected response headers and body in `data`; GraphQL returns its data there.
106
+ HTTP and GraphQL errors use the standard error envelope. Responses are bounded
107
+ by the transport's 16 MiB limit; raw requests do not paginate automatically.
108
+
109
+ ## Network proxies
110
+
111
+ API requests, browser sign-in requests and app uploads honor `HTTP_PROXY`,
112
+ `HTTPS_PROXY` and `NO_PROXY` (also their lowercase equivalents, which take
113
+ precedence). `HTTP_PROXY` also covers HTTPS when `HTTPS_PROXY` is unset.
114
+ Use `NO_PROXY=localhost,127.0.0.1` to bypass a proxy for local development,
115
+ or `NO_PROXY=*` to bypass it for all hosts. Proxies must support HTTP CONNECT.
116
+ For a corporate certificate authority, set `NODE_EXTRA_CA_CERTS` before starting
117
+ the CLI. TLS certificate verification stays enabled.
118
+
119
+ ## Diagnose your setup
120
+
121
+ ```bash
122
+ twenty doctor
123
+ twenty doctor --remote staging --path ./my-app --json
124
+ twenty doctor --offline
125
+ ```
126
+
127
+ `doctor` checks the running CLI and Node version, executable ownership and precedence on PATH, configuration, the selected connection, and an app's installed SDK authoring exports. It works without an app or saved remote; unavailable optional checks are marked `skipped`. Use `--path` to require a specific app. PATH inspection does not execute binaries or inspect shell aliases, functions or command caches; package-manager wrappers whose owner cannot be determined are reported as unknown.
128
+
129
+ By default it sends one read-only request to the selected workspace's metadata endpoint to check access. This does not prove access to every object or app operation. `--offline` disables network requests. Doctor never imports app or SDK code, builds an app, installs packages, repairs files, or refreshes credentials. An expired OAuth access token skips the network check: it produces a warning when a refresh token is saved (renewal remains unverified), or a failure when there is none. Renew through `twenty auth status` for the same remote, or sign in again.
130
+
131
+ Each check has an `id`, `status` (`pass`, `warning`, `fail`, or `skipped`), message, and optional code, hint and details. Failed checks exit 1 with `DOCTOR_FAILED`; JSON includes the checklist in `error.details.checks`. Otherwise exit 0, with the same checklist in `data.checks`, including warnings and skipped checks. Human output includes the full checklist in either case. Ctrl+C exits 130. Output omits credentials, server response bodies and user identity information, but includes local paths and the selected API URL.
132
+
133
+ ## Open the workspace
134
+
135
+ ```bash
136
+ twenty open
137
+ twenty open settings/applications
138
+ twenty open --remote staging --url-only --json
139
+ ```
140
+
141
+ `open` asks the selected workspace for its web address, which is its custom domain when one is enabled and its subdomain otherwise. It then opens that address, or a page inside it, in your default browser. The address carries no credentials, so sign in to the workspace in the browser if needed. A browser only opens in an interactive terminal: with `--no-input`, JSON output, redirected stdin or in CI, the command stops with `USAGE` (exit 2) unless `--url-only` is set, which prints the address instead. A page is a path inside the workspace; one that would lead to another site is refused with `USAGE`.
142
+
143
+ If the workspace returns no web address, `open` uses the frontend origin from
144
+ the server's OAuth discovery document. It drops the authorization path, query,
145
+ fragment and any URL credentials. Invalid addresses and failed metadata
146
+ authentication still stop the command.
147
+
148
+ ## Inspect the data model
149
+
150
+ ```bash
151
+ twenty metadata object list
152
+ twenty metadata object describe companies
153
+ twenty metadata field list companies --all
154
+ twenty metadata field describe companies tier --json
155
+ ```
156
+
157
+ Object arguments accept exact, case-sensitive singular or plural API names. Field arguments accept exact API names. Labels are never matched, and ambiguous names fail with `AMBIGUOUS_RESOURCE` (exit 2). Missing names return `NOT_FOUND` (exit 4).
158
+
159
+ Object describe shows object properties. Use field list to inspect its fields; system fields are hidden unless `--all` is present. Field describe can inspect a named system field directly. Each field reports its own owner, independently of its object. If application names cannot be read, Custom remains identifiable and other owners are reported as unknown with a warning; ownership is not a permission check.
160
+
161
+ Inspection reads every metadata page before resolving names, so a match on an early page cannot hide an ambiguity on a later one. Each object/field traversal is capped at 10,000 items or 16 MiB of node data; exceeding either limit fails instead of returning incomplete metadata. Requests use the selected connection and fetch live metadata on every invocation. Human output shows labels, constraints, options and relationships; JSON also preserves IDs, universal identifiers, settings and relationship endpoints.
162
+
163
+ ## Read records
164
+
165
+ ```bash
166
+ twenty data list companies --limit 20
167
+ twenty data list companies --filter 'employees[gte]:5000' --order-by 'employees[DescNullsLast]'
168
+ twenty data get people <id> --json
169
+ twenty data list people --all --format ndjson
170
+ ```
171
+
172
+ These commands resolve the same exact singular/plural API names as metadata inspection, including custom objects, and reuse the selected connection. They require access to object metadata as well as the records. `data get` reads one record with one level of related records; it does not recursively expand or paginate relations.
173
+
174
+ `data list` reads one page by default, with a default `--limit` of 50 and a maximum of 200. `--cursor` passes the opaque REST `starting_after` cursor. `--filter` and `--order-by` pass REST expressions unchanged; server ordering and its ID tie-breaker are preserved. Every result includes page information, and human output shows a next-page command. Pagination is not a database snapshot; concurrent writes can change results or totals during traversal.
175
+
176
+ `--fields name,employees` selects human table columns only. Tables shorten cells longer than 60 characters and display absent values as `-`. JSON and NDJSON retain full records, including composite values and relation identifiers.
177
+
178
+ `--all` reads every remaining page, including when starting from `--cursor`. Human/JSON results are limited to 10,000 records and 16 MiB of serialized payload; human tables are also bounded. Exceeding a limit returns `RESULT_LIMIT_EXCEEDED` (exit 2), with no partial success. Every HTTP response is separately limited to 16 MiB, including in NDJSON mode.
179
+
180
+ Use explicit `--format ndjson` for larger traversals. It emits numbered `start`, `record`, page `progress`, and terminal `result`/`error` events, reading one page at a time and waiting for stdout when the reader is slow. `resumeCursor` advances only after a complete page has been written. After a partial page or a failure, resume with the last reported cursor and the same filter/order; records from the partial page may repeat. With no completed page, use the original cursor or restart without one. A stream without a terminal event is incomplete. Without `--all`, NDJSON still reads only one page.
181
+
182
+ ## Create an app
183
+
184
+ ```bash
185
+ twenty app init my-app
186
+ twenty app init billing --path ./apps/billing --display-name Billing --json
187
+ ```
188
+
189
+ `app init` creates a new app from the template bundled with the CLI, with fresh universal identifiers and `twenty-client-sdk`, `twenty-sdk` and `twenty-ui` pinned to the exact version the CLI was built with. It needs no installed SDK, saved remote or network access, and it only writes files: it does not install dependencies, create a Git repository, start a server, sign in or sync anything. A published CLI also writes a `yarn.lock`, so the first `yarn install` uses exact, integrity-checked versions. The next steps it prints, and returns as `data.nextSteps` in JSON, cover the rest, and name the remote when you pass `--remote`.
190
+
191
+ The name must be a valid npm package name, otherwise the command fails with `INVALID_APP_NAME` (exit 2). The app is created in `./<name>` unless `--path` says otherwise; `--display-name` and `--description` set what Twenty shows. The target must not exist yet or be an empty directory; anything else fails with `APP_PATH_UNAVAILABLE` (exit 6) and nothing is written. The template is rendered in a hidden sibling directory and moved into place only after every placeholder was filled, so a failure or Ctrl+C does not leave a half-created app behind. An existing empty directory is filled with create-only writes, so a file that appears there meanwhile is never overwritten.
192
+
193
+ New apps declare `engines.twenty` as `>=` the template release version. The server
194
+ checks this range against the workspace's completed upgrade version on apply.
195
+ Upgrade the workspace before deploying a newer template, including during local
196
+ development. Adjust the range only after testing the app against older versions.
197
+
198
+ The integration tests of a new app run against a workspace through this CLI.
199
+ `src/__tests__/global-setup.ts` uninstalls any previous copy, runs
200
+ `twenty app apply --create` before the tests and `twenty app uninstall --yes`
201
+ after them, through `src/__tests__/run-twenty.ts`. That helper runs `twenty` with
202
+ `--json`, returns its result, and passes its progress through. It skips the
203
+ binaries a package manager puts first on `PATH` inside `yarn test`, so the
204
+ globally installed CLI runs; set `TWENTY_CLI` to use another executable.
205
+ Credentials come from `TWENTY_API_URL` and `TWENTY_API_KEY`,
206
+ which the template's Vitest config sets.
207
+
208
+ ## Add definitions to an app
209
+
210
+ ```bash
211
+ twenty app add
212
+ twenty app add object --name invoice --name-plural invoices --no-input
213
+ twenty app add object --name invoice --name-plural invoices --create-view --create-navigation-menu-item --create-page-layout --no-input
214
+ twenty app add field --name amount --type NUMBER --object <object-universal-identifier> --json
215
+ twenty app add logic-function --name send-invoice --no-input
216
+ twenty app add front-component --name invoice-panel --no-input
217
+ ```
218
+
219
+ `app add` creates definitions from the CLI's templates.
220
+ It discovers the containing app, or accepts `--path <app-directory>`. No sign-in
221
+ or workspace connection is needed. Use `object`, `field`, `logic-function` or
222
+ `front-component`.
223
+
224
+ Objects can also create optional UI definitions:
225
+
226
+ - `--create-view`: a table view containing the object's name field.
227
+ - `--create-navigation-menu-item`: a navigation item pointing to the object.
228
+ - `--create-page-layout`: a record page layout with Fields and Timeline tabs,
229
+ plus the fields view required by its Fields widget.
230
+
231
+ Combine these flags to select what to generate without a companion prompt.
232
+ Without any creation flags, a human interactive terminal asks whether to create
233
+ all three, defaulting to No. Press Enter or answer No to create only the object.
234
+ With `--no-input`, `--json`, redirected stdin or CI, companions are created only
235
+ when explicitly requested by flags. These flags are valid only for objects.
236
+ View, navigation and layout files go under `src/views`,
237
+ `src/navigation-menu-items` and `src/page-layouts`.
238
+
239
+ Files are created under `src/objects`, `src/fields`, `src/logic-functions` and
240
+ `src/front-components`, with kebab-case filenames and fresh universal UUIDs.
241
+ Existing files are never replaced, including filename collisions after case
242
+ normalization. Symlinked destination paths are refused. Both cases exit 6.
243
+ JSON returns app-relative paths in `data.createdPaths`.
244
+
245
+ Omitted values are prompted only in a human interactive terminal. With `--json`,
246
+ `--no-input`, redirected stdin or CI, required values must be supplied explicitly
247
+ or the command exits 2 before creating files. Labels default to the name; fields
248
+ default to `TEXT`. `--object` requires the parent's universal identifier, not its
249
+ workspace database ID. Relations additionally require `--target-object` and
250
+ `--target-field`; `--relation-type` defaults to `ONE_TO_MANY` and `--on-delete`
251
+ to `CASCADE`. The command does not create the reverse field or
252
+ check that these identifiers exist in a workspace.
253
+
254
+ Review the generated definition and its type-specific settings, then run
255
+ `twenty app build` and `twenty app plan`. Generation does not install or apply
256
+ anything. Cancellation before the file is created exits 130; existing app files
257
+ stay untouched, although empty parent directories can remain.
258
+
259
+ ## Build and check an app
260
+
261
+ ```bash
262
+ twenty app build
263
+ twenty app typecheck
264
+ twenty app build --path ./apps/billing --json
265
+ ```
266
+
267
+ These commands run inside an app project: the nearest folder, from the current one upwards, whose `package.json` depends on `twenty-sdk`. Pass `--path` to choose another app. In a folder that contains several apps, `--path` is required, and the error lists them.
268
+
269
+ The CLI owns manifest generation, bundling and typechecking. It uses the app's
270
+ installed `twenty-sdk` authoring exports (`define` and `front-component`, SDK
271
+ `>=1.23.0`). An app without an installed SDK fails with `SDK_NOT_INSTALLED`;
272
+ missing or unsupported authoring
273
+ exports fail with `SDK_SOURCE_UNSUPPORTED`. A Node version below every version the SDK's
274
+ `engines.node` range accepts fails with `NODE_VERSION_UNSUPPORTED`; any other
275
+ Node version outside that range continues with a `NODE_VERSION_UNTESTED`
276
+ warning, which `doctor` also reports. The CLI never installs dependencies or
277
+ substitutes another SDK. Yarn Plug'n'Play is not supported; use
278
+ `nodeLinker: node-modules`.
279
+
280
+ Typechecking uses `typescript` installed in the app or its workspace, never the
281
+ global CLI's parser dependency. Missing TypeScript fails with
282
+ `TYPESCRIPT_NOT_INSTALLED`. Configuration errors and unbuilt project references
283
+ fail with `TYPECHECK_FAILED`. For example, a project with an unbuilt reference
284
+ to `tsconfig.spec.json` can report `TS6305`: remove an unintended reference, or
285
+ build the referenced project before checking the app. Changing compiler versions
286
+ can also change diagnostics.
287
+
288
+ Builds run in a separate worker process. That process does not receive the CLI's
289
+ connections or credentials: `TWENTY_API_URL`, `TWENTY_API_KEY`, `TWENTY_REMOTE`
290
+ and every `TWENTY_*` token, key, secret or password variable are removed from its
291
+ environment. App and SDK output becomes `PROJECT_OUTPUT` diagnostics; an app
292
+ that exits the process fails with `WORKER_FAILED`. This separates output and
293
+ process state; it is not a sandbox for untrusted code.
294
+
295
+ `app build` produces a temporary snapshot under `.twenty/cli/snapshots`, reports
296
+ its files, upload roles, sizes, SHA-256 checksums, content hash and manifest, then
297
+ deletes the snapshot. Nothing is uploaded or kept. `app typecheck` checks the
298
+ project without writing files. Build and type errors exit 1 with diagnostics in
299
+ `error.details.diagnostics`. Worker failures report the underlying code in
300
+ `error.details.toolingErrorCode`. Worker process failures use
301
+ `error.details.workerErrorCode`. Ctrl+C cancels the worker and exits 130; a
302
+ worker that does not stop within a few seconds is killed. The JSON `sdk` object
303
+ identifies the app's installed authoring SDK version.
304
+
305
+ The CLI version determines build rules, including derived permission identifiers.
306
+ Different tooling versions can produce different manifests. Run `twenty app plan`
307
+ and inspect the changes before applying an app with a different tooling version.
308
+
309
+ ## Preview app changes
310
+
311
+ ```bash
312
+ twenty app plan --remote dev
313
+ twenty app plan --path ./apps/billing --no-delete --json
314
+ ```
315
+
316
+ `app plan` builds once with the CLI pipeline, releases the temporary snapshot, then requests the server's metadata preview with `dryRun: true`. It uses the usual connection selection and requires the server's `APPLICATIONS` permission. The server enforces authorization, ownership and manifest/version compatibility. No registration, installation, upload or metadata synchronization is performed, and planning never advances a pull base. The local build can generate app artifacts, just as `app build` does.
317
+
318
+ JSON marks plans with `plan: true`. A plan is not a saved approval: remote changes can alter what a later apply does. A missing owned application registration returns `PLAN_UNAVAILABLE` (exit 1). Register the app with `twenty app apply --create`, then plan again.
319
+
320
+ Entities missing from source are included as deletions by default. `--no-delete` passes `inferDeletionFromMissingEntities: false` to the preview. Human output lists every action, identifies object/field deletions that would remove stored data, and shows the opt-out hint. JSON includes action details and counts. Application-variable values, including previous values in updates, are redacted from all plan actions regardless of their `isSecret` setting. A plan exceeding 10,000 actions or the transport's 16 MiB response limit fails instead of displaying an incomplete plan.
321
+
322
+ ## Apply an app
323
+
324
+ ```bash
325
+ twenty app apply --remote dev
326
+ twenty app apply --create --json
327
+ twenty app apply --no-delete
328
+ ```
329
+
330
+ `app apply` builds the app once with the CLI pipeline and keeps that build's snapshot until it finishes, so the files it uploads are the ones it built. It then asks the workspace for a fresh plan, shows it, and applies it: it installs the development app if needed, uploads the snapshot files, synchronizes the manifest, and regenerates the app's typed API client. It needs the server's `APPLICATIONS` and `UPLOAD_FILE` permissions.
331
+
332
+ - **New apps.** An app without a registration needs `--create`, or a yes at the prompt in an interactive terminal. The CLI then registers the app (the server also requires `API_KEYS_AND_WEBHOOKS` for this), installs it, and plans its changes before uploading anything. Without approval it stops with `CREATE_REQUIRED` (exit 2). The registration's client secret is never requested.
333
+ - **Deletions.** Entities missing from source are deleted by default, as in `app plan`; `--no-delete` keeps them and is sent to both the plan and the sync. Object and field deletions permanently delete stored data, so they need `--yes` or a yes at the prompt. Otherwise the command stops with `CONFIRMATION_REQUIRED` (exit 2) before changing anything. `--yes` never changes which entities are deleted.
334
+ - **Uploads.** File bytes go straight to the upload URLs the server returns, without the CLI's credentials. Each file is checked against the build's size and SHA-256 before anything is uploaded.
335
+ - **Pull baseline.** After an acknowledged sync, the CLI fetches the workspace ID and fresh application export, then atomically records `.twenty/cli/pull-base.json`. This baseline lets `app pull` distinguish workspace changes from local edits. It is bound to the normalized API URL, workspace UUID and app UUID. JSON reports `pullBase: "recorded"`, `"failed"` or `"unsupported"`; only a recorded base adds `pullBase` to `completedPhases`. Invalid exports or file-write errors preserve the prior base, warn with `PULL_BASE_NOT_RECORDED`, and allow client generation to continue. A server without the export API reports `"unsupported"` without a warning on every apply. The export is recorded as the server sent it; collections it lacks are read as empty by pull. Cancellation before the base is committed exits 130 with `outcome: "applied"` and `phase: "pullBase"`.
336
+ - **Typed client.** After the sync, the CLI fetches the app's GraphQL schema from the workspace and calls the app's own `twenty-client-sdk/generate` to regenerate the client in the app's own `node_modules/twenty-client-sdk` (`clientGeneration: "generated"`). It is skipped with a `CLIENT_NOT_GENERATED` warning when the app has no `node_modules/twenty-client-sdk` of its own, as in a hoisted workspace. A symlinked client package is followed and its target rewritten, so don't apply two apps that share one client package at the same time.
337
+ - **Failures.** A failed apply reports `details.phase`, `details.completedPhases` and `details.outcome`: `not-started` when the failing step changed nothing, `partial` when some files were uploaded, `unknown` when a request was sent but its effect is not known, such as a failed or interrupted sync, and `applied` when the sync succeeded but baseline recording was cancelled or client generation failed. Earlier steps, like a new registration, stay done. There is no rollback and no automatic retry: run `twenty app plan` to see where the workspace stands, then apply again. When client generation failed, the workspace has the new version but the client files may be incomplete: fix the problem, then run `twenty app generate-client`, which regenerates the client without syncing again. Ctrl+C exits with 130 and reports the step it interrupted.
338
+
339
+ Remote changes made between the plan and the sync can change what the sync does. Remote changes between the acknowledged sync and the export can also enter the saved baseline without appearing in local source; this is not an atomic server snapshot of the sync. Planning, a failed sync, or a sync with an unknown outcome never advances the baseline.
340
+
341
+ ## Develop an app
342
+
343
+ ```bash
344
+ twenty app dev --remote dev
345
+ twenty app dev --create --no-delete
346
+ twenty app dev --path ./apps/billing --format ndjson --no-delete
347
+ ```
348
+
349
+ `app dev` builds and syncs once, then watches for edits. Each attempt uses the
350
+ same CLI build and strict project TypeScript checks as `app apply`. A failed
351
+ build prints diagnostics and keeps watching; it never uploads an incomplete
352
+ revision. It needs the same `APPLICATIONS` and `UPLOAD_FILE` permissions as apply.
353
+
354
+ The CLI watches the app folder and the external local files read by definition
355
+ loading, bundling and typechecking, including linked packages and extended
356
+ TypeScript configs. Relative imports of missing external files watch the nearest
357
+ existing parent, so creating the file can recover a failed build. The last
358
+ successful input graph stays watched after a failure. `.git`, `.twenty`,
359
+ `node_modules`, root `dist`, editor temporary files and `.DS_Store` are ignored.
360
+ Arbitrary filesystem reads inside app code are not discovered. Restart dev after
361
+ installing dependencies or changing the package manager's links.
362
+
363
+ Only one remote apply runs at a time. The compiler can prepare a newer complete
364
+ snapshot while that apply finishes; intermediate edits coalesce into the latest
365
+ revision. Uploads read independent, checksum-verified files, so a later build
366
+ cannot overwrite them. An unchanged content hash skips sync only after that
367
+ snapshot was acknowledged by the workspace. This is eventual convergence after
368
+ edits settle, not an atomic checkout of files being edited concurrently.
369
+
370
+ Registration uses `--create` or a terminal confirmation. Deletion inference is
371
+ on by default, with `--no-delete` to keep missing entities. Object and field
372
+ deletions require a terminal confirmation or `--yes`. Every revision requests a
373
+ fresh plan. An edit cancels a pending confirmation; approval for the
374
+ old revision cannot authorize the new one. A remote failure is reported with the
375
+ same phase/outcome details as apply and is retried only after a new source edit,
376
+ with a new plan. Inspect `twenty app plan` when a request's outcome is unknown.
377
+
378
+ After an acknowledged sync, dev records the pull base and regenerates the app's
379
+ own typed client when its schema, generator package or generated files changed.
380
+ Compilation pauses during generation, then explicitly rebuilds. Unchanged client
381
+ inputs and unchanged build output prevent a generation/sync loop. A schema fetch
382
+ failure keeps the old client and prints a warning. If generation starts writing
383
+ and fails, dev stops because the client files may be incomplete; fix the problem
384
+ and run `twenty app generate-client` before restarting dev.
385
+
386
+ Use human output or `--format ndjson`; `--json` is rejected before target lookup.
387
+ NDJSON uses ordered `progress` events with `data.kind`: `watch-ready`,
388
+ `build-start`, `build-success`, `build-failure`, `build-skipped`, `sync-start`,
389
+ `sync-progress`, `sync-superseded`, `sync-failure` and `sync-success`. Build events
390
+ carry the revision, successful builds also carry the build ID and content hash.
391
+ Output honors backpressure; buffered progress is bounded. Ctrl+C stops watching,
392
+ cancels active requests, releases snapshots after readers settle, and exits 130
393
+ with the interrupted remote phase when available. It does not roll back writes.
394
+
395
+ An abnormal worker exit or a filesystem-watcher failure ends the session; fix
396
+ the cause and restart `twenty app dev`. A killed process can leave temporary
397
+ folders in `.twenty/cli/snapshots`. After stopping all dev/apply sessions for that
398
+ app, abandoned snapshot folders can be removed.
399
+
400
+ ## Regenerate the typed client
401
+
402
+ ```bash
403
+ twenty app generate-client --remote dev
404
+ twenty app generate-client --path ./apps/billing --json
405
+ ```
406
+
407
+ `app generate-client` fetches the app's GraphQL schema from the workspace and regenerates the typed client in the app's own `node_modules/twenty-client-sdk`, the same step `app apply` and `app dev` run after a sync, without building, uploading or syncing. Use it after installing dependencies, after a failed client generation, or to pick up schema changes made in the workspace. It needs no server permission flag.
408
+
409
+ The app must already be installed in the workspace; otherwise the command fails with `APP_NOT_INSTALLED` (exit 4), and `twenty app apply` installs it and generates the client. An app without its own `node_modules/twenty-client-sdk`, as in a hoisted workspace, fails with `CLIENT_SDK_NOT_INSTALLED` instead of the warning `app apply` prints.
410
+
411
+ ## Pull an app
412
+
413
+ ```bash
414
+ twenty app pull --remote dev
415
+ twenty app pull --path ./apps/billing --json
416
+ twenty app pull --universal-identifier <uuid> --verbose
417
+ ```
418
+
419
+ `app pull` reads the app's definition and exports its workspace metadata into the existing project. It uses the selected connection and the server's `APPLICATIONS` permission; the server decides which apps this workspace can export. It does not register an app or change the workspace. When a project has no application definition, provide `--universal-identifier`. A project declaring a different app is refused before writing.
420
+
421
+ The project must have `twenty-sdk` installed, version `>=1.23.0`, with its public `twenty-sdk/define` and `twenty-sdk/front-component` exports. Pull also checks that the SDK provides the authoring APIs required by the generated files. Missing APIs fail with `SDK_SOURCE_UNSUPPORTED`; install a compatible version (2.40.0 or later). Pull does not require a project TypeScript compiler. Run `twenty app typecheck` afterwards to check the generated definitions against your installed SDK.
422
+
423
+ Pull compares workspace metadata and local definitions using a baseline saved
424
+ by apply or a previous pull in `.twenty/cli/pull-base.json`. The baseline belongs
425
+ to one API URL, workspace UUID and application UUID.
426
+
427
+ Pull overwrites local edits to entities that changed on the workspace, without a confirmation prompt. With a matching baseline, unchanged workspace entities keep their local source, and confirmed remote deletions remove their files. Without a matching baseline, local-only definitions stay in place and pull does not infer deletions. Coverage gaps, unreadable definitions and local-only nested entities are preserved and reported; gaps do not require `--force` or an opt-in partial mode. JSON includes `coverage`, `skipped`, `unreadableRelativePaths`, `localOnlyRelativePaths` and `overwrittenLocalChanges`. Without a baseline, overwritten local edits cannot be distinguished from generated source.
428
+
429
+ Before replacing local files, pull stages the new content and backs up the
430
+ originals. It updates the baseline only after writing the files. Ordinary write
431
+ failures restore the previous files and baseline.
432
+
433
+ Failures in reconciliation report `error.details.outcome`: `unchanged` after an acknowledged refusal, cancellation before writing or successful rollback; `unknown` if a worker stops without acknowledging completion or rollback fails; `pulled` if writes and baseline completed but temporary-file cleanup failed. An interrupted worker can leave files partially written; inspect the changes and retained `.twenty/cli/pull-backup-*` files before retrying. A failed rollback also reports `backupDirectory`. Ctrl+C exits 130 unless the worker acknowledges a completed commit, in which case pull returns its result.
434
+
435
+ The current export contains metadata and translations, not application source assets or dependency files. Logic-function/front-component code and unsupported metadata are not regenerated. An export with nonempty `files` is refused before writing. Package files, dependency pins and the installed generated client are left alone; pull never installs dependencies. Each export response is limited to 16 MiB. See the [pull implementation](../src/app/pull/README.md) for reconciliation and compatibility details.
436
+
437
+ ## Install a registered app
438
+
439
+ ```bash
440
+ twenty app install --remote dev
441
+ twenty app install --path ./apps/billing --json
442
+ twenty app install --universal-identifier <id> --remote staging --json
443
+ ```
444
+
445
+ `app install` installs a package already registered on the target server. It reads the local app definition to find its universal identifier, without building or typechecking the app; `--universal-identifier` works without a local project. Identifiers are sent in lowercase. It requires `APPLICATIONS` permission.
446
+
447
+ The server selects the registered package and version. Local source changes are not uploaded. For a private package, run `app publish --private` first; publishing to npm alone does not create a server registration. Installation can run app hooks and update an existing installation. Use `app apply` to sync local source instead.
448
+
449
+ The command waits up to ten minutes for its server installation job to complete, then verifies that the app is installed. While it waits, human output shows the job's progress as an updating percentage bar in an interactive terminal. Redirected output, CI and terminals with `TERM=dumb` print a plain progress line each time the percentage changes. JSON returns `application`, `installed: true` and `jobId`. Failed jobs return `INSTALL_FAILED`; missing or mismatched job status is an error, not a successful installation.
450
+
451
+ Identifier resolution and installation errors include `details.phase`, `details.completedPhases` and `details.outcome`, plus the universal identifier and job identifier when available. Identifier resolution failures report `phase: "identity"` and `outcome: "not-started"`. Ctrl+C or a timeout stops waiting but does not cancel the server job. Run the same command again to retry or resume waiting for an installation that is still running. The command requires the server's `triggerInstallApplication` and `findInstallApplicationJobStatus` APIs, including lookup by `jobId`.
452
+
453
+ ## Uninstall an app
454
+
455
+ ```bash
456
+ twenty app uninstall --remote dev
457
+ twenty app uninstall --yes --json
458
+ twenty app uninstall --universal-identifier <id> --yes
459
+ ```
460
+
461
+ `app uninstall` reads the local app definition to find its universal identifier, without building or typechecking the app. It checks that the app is installed on the target and that the workspace allows uninstalling it, then uninstalls it. `--universal-identifier` works without a local project. It needs the server's `APPLICATIONS` permission.
462
+
463
+ Uninstalling runs the app's uninstall hook and deletes everything the app owns, including its objects, fields and their data. It always needs `--yes`, or a yes at the prompt in an interactive terminal; otherwise it stops with `CONFIRMATION_REQUIRED` (exit 2) before changing anything. An app that is not installed returns `APP_NOT_INSTALLED` (exit 4), and one the workspace does not allow to uninstall returns `APP_NOT_UNINSTALLABLE` (exit 6).
464
+
465
+ The app's registration is kept, so `twenty app apply` can install it again without `--create`. Failures report `details.phase`, `details.completedPhases` and `details.outcome`, as for apply: an uninstall request that fails after the server received it, or is interrupted with Ctrl+C, has an `unknown` outcome. Running the command again reports `APP_NOT_INSTALLED` once the app is gone. `--universal-identifier` accepts any UUID casing and sends the canonical lowercase form.
466
+
467
+ ## Execute an app function
468
+
469
+ ```bash
470
+ twenty app exec --name add-numbers --remote dev --payload '{"a":2,"b":3}'
471
+ twenty app exec --universal-identifier <uuid> --payload @payload.json --json
472
+ printf '{"a":4,"b":7}' | twenty app exec --name add-numbers --remote dev --payload - --json
473
+ ```
474
+
475
+ Run from the app folder or pass `--path`. The CLI reads app definitions to find
476
+ its functions, then executes the selected function's deployed code. Sync local
477
+ changes with `app apply` or `app dev` first. Execution does not build bundles,
478
+ typecheck, sync, register or install the app.
479
+
480
+ Choose exactly one of `--name` (exact deployed name), `--universal-identifier`,
481
+ `--post-install`, `--pre-install` or `--uninstall-hook`. Hook selectors run the
482
+ hook's code and its side effects without installing or uninstalling the app.
483
+ Payloads must be JSON objects, supplied inline, through `@file`, or through `-`
484
+ for stdin; the default is `{}`. The selected function must belong to both the
485
+ local app manifest and the installed app on the target workspace.
486
+
487
+ The current server requires a signed-in user and the `APPLICATIONS` and
488
+ `WORKFLOWS` settings permissions. Sign in with `twenty auth login --url <url>
489
+ --name dev`, then pass `--remote dev`. API keys, including environment API-key
490
+ targets, cannot execute logic functions. The server additionally restricts
491
+ which functions the user may run on demand.
492
+
493
+ Human output shows status, duration, returned data, error and logs. `--json`
494
+ returns those fields in the standard envelope; NDJSON is not supported. A
495
+ function returning a non-SUCCESS status exits 1 with `EXECUTION_FAILED`, keeping
496
+ its data, logs and error in `error.details` with `outcome: "completed"`.
497
+
498
+ There are no automatic retries. The request deadline uses the deployed
499
+ function's timeout plus 60 seconds. A lost response or Ctrl+C can leave the
500
+ function running on the server; the error reports `outcome: "unknown"` and asks
501
+ you to check its effects before retrying. An acknowledged execution rejection
502
+ reports `"not-started"`. Ctrl+C exits 130, without rolling back side effects.
503
+ See the [execution implementation](../src/app/exec/README.md) for compatibility details.
504
+
505
+ ## Watch app logs
506
+
507
+ ```bash
508
+ twenty app logs --remote dev
509
+ twenty app logs --name add-numbers --remote dev
510
+ twenty app logs --universal-identifier <uuid> --format ndjson
511
+ ```
512
+
513
+ Run from the app folder or pass `--path`. This watches newly published execution
514
+ output from every logic function in that app. Output arrives when an execution
515
+ finishes, not line by line while it runs. It does not fetch historical logs,
516
+ watch other apps, or build, sync, register or install anything.
517
+
518
+ Names are not unique: `--name` matches every function with that exact name in the
519
+ app. Use `--universal-identifier` to isolate one function. The two filters are
520
+ mutually exclusive. Human output labels each execution with its function name
521
+ and identifier; NDJSON records contain `applicationUniversalIdentifier`,
522
+ `functionName`, `functionUniversalIdentifier` and `logs`.
523
+
524
+ Some server schemas expose only log text. If the identity fields are unsupported,
525
+ the CLI warns and subscribes once requesting only `logs`. An explicit filter
526
+ supplies that name or identifier; unknown identities are `null` in NDJSON and
527
+ omitted from human labels. With neither filter, those schemas cannot attribute
528
+ mixed output to individual functions.
529
+
530
+ The stream supports human output and `--format ndjson`; finite `--json` is
531
+ rejected. Ctrl+C closes it and exits 130. A server completion exits 0. A dropped
532
+ or stalled connection exits with an error and the emitted record count, with no
533
+ automatic reconnect because missed logs cannot be replayed. Slow output applies
534
+ backpressure; each SSE frame is limited to 16 MiB, with no session-size cap.
535
+ Connection setup and gaps between received body chunks each have a 60-second
536
+ deadline; server heartbeat comments keep quiet subscriptions alive.
537
+
538
+ This uses the existing app-log subscription and its `WORKFLOWS` permission and
539
+ application access checks. API keys and browser-login remotes are supported.
540
+ It does not require the paid audit-log entitlement, ClickHouse history or the
541
+ workspace-wide Settings logs API. See the [log subscription implementation](../src/app/function-logs/README.md).
542
+
543
+ ## Expired browser sessions
544
+
545
+ For commands that use a workspace, interactive OAuth sessions are checked before
546
+ the command starts. If a session is rejected or cannot be refreshed, the CLI
547
+ offers to sign in again once, validates the new session, and saves its credentials
548
+ without changing the remote's URL, default selection or other settings.
549
+
550
+ API keys, JSON/NDJSON output, `--no-input`, redirected stdin and CI never trigger
551
+ this prompt. Permission errors do not trigger sign-in either. If authentication
552
+ fails after a command starts, the CLI stops with the login hint; it does not
553
+ replay requests or restart an app apply. A remote changed during sign-in is left
554
+ as-is, and the command stops with `CONFLICT`.
555
+
556
+ ## Output
557
+
558
+ `twenty version` (or `--version`) reports the running executable's version.
559
+ `twenty version --json` also includes Node, platform and architecture.
560
+ `twenty commands --json` lists command arguments, flags, output modes and
561
+ project/target requirements for scripts that discover the command surface.
562
+
563
+ Commands print readable text by default. With `--json`, a command prints exactly one JSON document on stdout, including when it fails:
564
+
565
+ ```json
566
+ {
567
+ "schemaVersion": 1,
568
+ "ok": true,
569
+ "command": "version",
570
+ "data": {
571
+ "version": "2.46.0",
572
+ "node": "24.9.0",
573
+ "platform": "darwin",
574
+ "arch": "arm64"
575
+ },
576
+ "warnings": []
577
+ }
578
+ ```
579
+
580
+ A failure has `"ok": false` and an `error` object with a stable `code`, a `message`, and sometimes a `hint` and `details`.
581
+
582
+ Exit codes:
583
+
584
+ | Code | Meaning |
585
+ | ---- | ----------------------------------- |
586
+ | 0 | Success |
587
+ | 1 | Failure |
588
+ | 2 | Usage error or missing confirmation |
589
+ | 3 | Authentication or permission denied |
590
+ | 4 | Not found |
591
+ | 5 | Partial failure |
592
+ | 6 | Conflict |
593
+ | 130 | Cancelled |
594
+
595
+ ## Publish an app to npm
596
+
597
+ ```sh
598
+ twenty app publish
599
+ twenty app publish --path ./apps/billing --tag beta --json
600
+ ```
601
+
602
+ Builds and typechecks the app, packs that exact build, then publishes the package with public access. No Twenty remote or workspace authentication is required. Publishing to npm does not sync or install the app in a workspace.
603
+
604
+ Install npm and authenticate with the intended registry first. npm runs from the app directory, so its project/user configuration and environment credentials apply. `--tag` sets the npm dist-tag. GitHub Actions jobs with OIDC request provenance automatically; set `TWENTY_APP_PUBLISH_DISABLE_PROVENANCE=true` to opt out, for example when publishing from a private source repository.
605
+
606
+ Packing and publishing skip npm lifecycle scripts because the CLI already built the app. npm's package-file selection still applies. If `package.json` has a `files` field, it must include `manifest.json` and the generated app files; the command rejects an incomplete package before publication. npm excludes the root `yarn.lock` from packages. Temporary packages and build snapshots are removed after completion or failure.
607
+
608
+ Human mode displays npm output and supports interactive authentication when a terminal is available. JSON mode returns one CLI envelope and does not prompt; configure an automation token or npm’s `npm_config_otp` environment option before running it. Failed npm commands return `PUBLISH_FAILED` with the subprocess exit code and captured output. If publication is interrupted after sending the package, check the registry before retrying, since the version may already have been published.
609
+
610
+ ### Publish to a private Twenty registry
611
+
612
+ ```sh
613
+ twenty app publish --private --remote staging
614
+ twenty app publish --private --path ./apps/billing --json
615
+ ```
616
+
617
+ `--private` publishes the same built package to the selected Twenty server registry instead of npm. It uses the normal saved remote or environment credentials, requests a tarball upload, sends the bytes directly to storage, then registers the uploaded app. npm is still needed for local packing, but npm registry credentials are not used and `--tag` is rejected. The server requires file-upload permission and `MARKETPLACE_APPS` permission on the target workspace.
618
+
619
+ Publishing registers a version; it does not install the app or directly sync the selected workspace. Publishing an update can trigger automatic upgrades in workspaces where the app is already installed with automatic upgrades enabled. The server enforces application ownership, version progression and engine compatibility. JSON results contain `target: "private"`, the selected server under `registry`, the registration and `installed: false`, indicating that this command did not request installation.
620
+
621
+ Failed uploads never trigger registration. If registration fails or is interrupted after sending the request, the app may already be registered. The error includes the phase, upload file ID and an `unknown` outcome; check the server registry before retrying. Temporary packages are cleaned up in either case.
622
+
623
+ ## App translations
624
+
625
+ ```sh
626
+ twenty app translations extract
627
+ twenty app translations extract --locale fr-FR
628
+ twenty app translations extract --path ./apps/billing --json
629
+ ```
630
+
631
+ Use `--path` for an explicit app directory. Without `--path`, the CLI finds the
632
+ app containing the current directory.
633
+
634
+ Extraction runs locally, without a workspace connection. It reads app metadata
635
+ and static `t`, `msg`, and `Trans` strings in front-component source files. It
636
+ refreshes `locales/en.json` with source strings and updates existing target JSON
637
+ catalogs. `--locale` also creates a missing supported target catalog, for example
638
+ `locales/fr-FR.json`. The source locale `en` cannot be a target.
639
+
640
+ Translations for unchanged message/context pairs are preserved. New entries have
641
+ empty translations; obsolete entries are removed. Context groups remain separate.
642
+ Malformed target catalogs fail with `INVALID_INPUT` before writing any catalogs.
643
+ Unsafe catalog or staging paths report `APP_PATH_UNAVAILABLE`. Files under
644
+ `locales/compiled` are left untouched. Temporary files live under `.twenty/cli`.
645
+ Each catalog is replaced atomically; an
646
+ interrupted run may update only some catalogs, so rerun extraction to finish.
647
+
648
+ The command validates app definitions but does not bundle or upload them. Edit
649
+ the target catalogs, then run `twenty app apply` to deploy translations. Build
650
+ and apply already compile translations; no separate compile command is needed.
651
+ JSON output includes the app, source-string count, updated catalog paths and
652
+ diagnostics.