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
@@ -0,0 +1 @@
1
+ let e=require("node:fs/promises"),t=require("node:path");var n=async n=>(0,t.isAbsolute)(n)&&(await(0,e.stat)(n).catch(()=>null))?.isDirectory()?{success:!0,data:null,diagnostics:[]}:{success:!1,error:{code:`INVALID_APP_PATH`,message:`appPath must be an absolute path to an existing directory.`},diagnostics:[]};Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return n}});
@@ -0,0 +1 @@
1
+ const e=require("./cli-error-CSsayo0u.cjs");var t=/^[A-Za-z0-9][A-Za-z0-9._-]{0,62}$/,n=n=>{if(!t.test(n))throw new e.t({code:`INVALID_REMOTE_NAME`,exitCode:e.n.USAGE,message:`${n} is not a valid remote name.`,hint:`Use letters, digits, dots, dashes and underscores, starting with a letter or digit.`});return n};Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return n}});
@@ -0,0 +1 @@
1
+ const e=require("./build-CX6FBrbu.cjs"),t=require("./constants-CL6mrTMT-B748cNNG.cjs"),n=require("./cli-error-CSsayo0u.cjs"),r=require("./request-timeout-milliseconds.constant-DtyQHrll.cjs"),i=require("./config-file-mode.constant-DLBzKKUl.cjs");let a=require("node:crypto"),o=require("node:fs/promises"),s=require("node:path"),c=require("node:timers/promises");var l=e.t(),u={TIMEOUT_MILLISECONDS:2*r.t+5e3,RETRY_MILLISECONDS:50},d=async e=>{try{let n=JSON.parse(await(0,o.readFile)(e,`utf8`));return t.d(n)&&(0,l.isNumber)(n.pid)&&(0,l.isString)(n.token)?{pid:n.pid,token:n.token}:void 0}catch{return}},f=e=>{try{return process.kill(e,0),!0}catch(e){return n.r(e,`EPERM`)}},p=async e=>{let r=await d(e),i=t.zt(r)&&!f(r.pid),a=t.zt(r)?` (pid ${r.pid})`:``;return new n.t({code:`CONFIG_LOCKED`,message:i?`A twenty command (pid ${r.pid}) stopped without releasing ${e}.`:`Another twenty command${a} is changing ${(0,s.dirname)(e)}.`,hint:i?`Delete ${e} and try again.`:`Try again when it finishes. If no twenty command is running, delete ${e}.`,details:{lockPath:e,ownerPid:r?.pid??null}})},m=async(e,t)=>{try{let n=await(0,o.open)(e,`wx`,i.t.PRIVATE_FILE);return await n.writeFile(JSON.stringify({pid:process.pid,token:t})),await n.close(),!0}catch(e){if(n.r(e,`EEXIST`))return!1;throw e}},h=async({lockPath:e,token:n,signal:r})=>{let i=Date.now()+u.TIMEOUT_MILLISECONDS,a=await m(e,n);for(;!a;){r.throwIfAborted();let o=await d(e);if(t.zt(o)&&!f(o.pid)||Date.now()>i)throw await p(e);await(0,c.setTimeout)(u.RETRY_MILLISECONDS,void 0,{signal:r}),a=await m(e,n)}},g=async(e,t)=>{(await d(e))?.token===t&&await(0,o.rm)(e,{force:!0})},_=async({configPath:e,signal:t,operation:n})=>{let r=`${e}.lock`,c=(0,a.randomUUID)();await(0,o.mkdir)((0,s.dirname)(e),{recursive:!0,mode:i.t.PRIVATE_DIRECTORY}),await h({lockPath:r,token:c,signal:t});try{return await n()}finally{await g(r,c)}},v=async(e,t)=>{let n=`${e}.${process.pid}.${(0,a.randomUUID)()}.tmp`,r=await(0,o.open)(n,`wx`,i.t.PRIVATE_FILE);try{await r.writeFile(`${JSON.stringify(t,null,2)}\n`),await r.sync(),await r.close(),await(0,o.rename)(n,e)}catch(e){throw await r.close().catch(()=>void 0),await(0,o.rm)(n,{force:!0}),e}};Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return v}});
package/dist/cli.cjs ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ const e=require("./chunks/package-CdYv4LEx.cjs"),t=require("./chunks/check-node-requirement-mqwNPktb.cjs");process.stdout.on(`error`,e=>{if(e.code!==`EPIPE`)throw e;process.exit()}),(async({args:n,nodeVersion:r,loadCli:i})=>{let a=t.t({version:r,range:e.t.node})===`satisfied`,o;try{o=await i()}catch(t){if(a)throw t;process.stderr.write(`twenty requires Node.js ${e.t.node}; this is Node.js ${r}. Switch to a supported Node.js version, then try again.\n`),process.exitCode=1;return}await o.runCli(n)})({args:process.argv.slice(2),nodeVersion:process.versions.node,loadCli:()=>Promise.resolve().then(()=>require("./chunks/run-cli-c9TOR0zj.cjs"))});
@@ -0,0 +1,564 @@
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`; an incompatible SDK Node requirement
274
+ fails with `NODE_VERSION_UNSUPPORTED`. The CLI never installs dependencies or
275
+ substitutes another SDK. Yarn Plug'n'Play is not supported; use
276
+ `nodeLinker: node-modules`.
277
+
278
+ Typechecking uses `typescript` installed in the app or its workspace, never the
279
+ global CLI's parser dependency. Missing TypeScript fails with
280
+ `TYPESCRIPT_NOT_INSTALLED`. Configuration errors and unbuilt project references
281
+ fail with `TYPECHECK_FAILED`. For example, a project with an unbuilt reference
282
+ to `tsconfig.spec.json` can report `TS6305`: remove an unintended reference, or
283
+ build the referenced project before checking the app. Changing compiler versions
284
+ can also change diagnostics.
285
+
286
+ Builds run in a separate worker process. That process does not receive the CLI's
287
+ connections or credentials: `TWENTY_API_URL`, `TWENTY_API_KEY`, `TWENTY_REMOTE`
288
+ and every `TWENTY_*` token, key, secret or password variable are removed from its
289
+ environment. App and SDK output becomes `PROJECT_OUTPUT` diagnostics; an app
290
+ that exits the process fails with `WORKER_FAILED`. This separates output and
291
+ process state; it is not a sandbox for untrusted code.
292
+
293
+ `app build` produces a temporary snapshot under `.twenty/cli/snapshots`, reports
294
+ its files, upload roles, sizes, SHA-256 checksums, content hash and manifest, then
295
+ deletes the snapshot. Nothing is uploaded or kept. `app typecheck` checks the
296
+ project without writing files. Build and type errors exit 1 with diagnostics in
297
+ `error.details.diagnostics`. Worker failures report the underlying code in
298
+ `error.details.toolingErrorCode`. Worker process failures use
299
+ `error.details.workerErrorCode`. Ctrl+C cancels the worker and exits 130; a
300
+ worker that does not stop within a few seconds is killed. The JSON `sdk` object
301
+ identifies the app's installed authoring SDK version.
302
+
303
+ The CLI version determines build rules, including derived permission identifiers.
304
+ Different tooling versions can produce different manifests. Run `twenty app plan`
305
+ and inspect the changes before applying an app with a different tooling version.
306
+
307
+ ## Preview app changes
308
+
309
+ ```bash
310
+ twenty app plan --remote dev
311
+ twenty app plan --path ./apps/billing --no-delete --json
312
+ ```
313
+
314
+ `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.
315
+
316
+ 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.
317
+
318
+ 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.
319
+
320
+ ## Apply an app
321
+
322
+ ```bash
323
+ twenty app apply --remote dev
324
+ twenty app apply --create --json
325
+ twenty app apply --no-delete
326
+ ```
327
+
328
+ `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.
329
+
330
+ - **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.
331
+ - **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.
332
+ - **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.
333
+ - **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"`.
334
+ - **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.
335
+ - **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. After `applied`, the workspace has the new version but the client files may be incomplete: fix the problem, then run `twenty app apply` again, which repeats the plan, upload and sync before regenerating the client. Ctrl+C exits with 130 and reports the step it interrupted.
336
+
337
+ 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.
338
+
339
+ ## Develop an app
340
+
341
+ ```bash
342
+ twenty app dev --remote dev
343
+ twenty app dev --create --no-delete
344
+ twenty app dev --path ./apps/billing --format ndjson --no-delete
345
+ ```
346
+
347
+ `app dev` builds and syncs once, then watches for edits. Each attempt uses the
348
+ same CLI build and strict project TypeScript checks as `app apply`. A failed
349
+ build prints diagnostics and keeps watching; it never uploads an incomplete
350
+ revision. It needs the same `APPLICATIONS` and `UPLOAD_FILE` permissions as apply.
351
+
352
+ The CLI watches the app folder and the external local files read by definition
353
+ loading, bundling and typechecking, including linked packages and extended
354
+ TypeScript configs. Relative imports of missing external files watch the nearest
355
+ existing parent, so creating the file can recover a failed build. The last
356
+ successful input graph stays watched after a failure. `.git`, `.twenty`,
357
+ `node_modules`, root `dist`, editor temporary files and `.DS_Store` are ignored.
358
+ Arbitrary filesystem reads inside app code are not discovered. Restart dev after
359
+ installing dependencies or changing the package manager's links.
360
+
361
+ Only one remote apply runs at a time. The compiler can prepare a newer complete
362
+ snapshot while that apply finishes; intermediate edits coalesce into the latest
363
+ revision. Uploads read independent, checksum-verified files, so a later build
364
+ cannot overwrite them. An unchanged content hash skips sync only after that
365
+ snapshot was acknowledged by the workspace. This is eventual convergence after
366
+ edits settle, not an atomic checkout of files being edited concurrently.
367
+
368
+ Registration uses `--create` or a terminal confirmation. Deletion inference is
369
+ on by default, with `--no-delete` to keep missing entities. Object and field
370
+ deletions require a terminal confirmation or `--yes`. Every revision requests a
371
+ fresh plan. An edit cancels a pending confirmation; approval for the
372
+ old revision cannot authorize the new one. A remote failure is reported with the
373
+ same phase/outcome details as apply and is retried only after a new source edit,
374
+ with a new plan. Inspect `twenty app plan` when a request's outcome is unknown.
375
+
376
+ After an acknowledged sync, dev records the pull base and regenerates the app's
377
+ own typed client when its schema, generator package or generated files changed.
378
+ Compilation pauses during generation, then explicitly rebuilds. Unchanged client
379
+ inputs and unchanged build output prevent a generation/sync loop. A schema fetch
380
+ failure keeps the old client and prints a warning. If generation starts writing
381
+ and fails, dev stops because the client files may be incomplete; fix the problem
382
+ and run `twenty app apply` before restarting dev.
383
+
384
+ Use human output or `--format ndjson`; `--json` is rejected before target lookup.
385
+ NDJSON uses ordered `progress` events with `data.kind`: `watch-ready`,
386
+ `build-start`, `build-success`, `build-failure`, `build-skipped`, `sync-start`,
387
+ `sync-progress`, `sync-superseded`, `sync-failure` and `sync-success`. Build events
388
+ carry the revision, successful builds also carry the build ID and content hash.
389
+ Output honors backpressure; buffered progress is bounded. Ctrl+C stops watching,
390
+ cancels active requests, releases snapshots after readers settle, and exits 130
391
+ with the interrupted remote phase when available. It does not roll back writes.
392
+
393
+ An abnormal worker exit or a filesystem-watcher failure ends the session; fix
394
+ the cause and restart `twenty app dev`. A killed process can leave temporary
395
+ folders in `.twenty/cli/snapshots`. After stopping all dev/apply sessions for that
396
+ app, abandoned snapshot folders can be removed.
397
+
398
+ ## Pull an app
399
+
400
+ ```bash
401
+ twenty app pull --remote dev
402
+ twenty app pull --path ./apps/billing --json
403
+ twenty app pull --universal-identifier <uuid> --verbose
404
+ ```
405
+
406
+ `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.
407
+
408
+ 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.
409
+
410
+ Pull compares workspace metadata and local definitions using a baseline saved
411
+ by apply or a previous pull in `.twenty/cli/pull-base.json`. The baseline belongs
412
+ to one API URL, workspace UUID and application UUID.
413
+
414
+ 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.
415
+
416
+ Before replacing local files, pull stages the new content and backs up the
417
+ originals. It updates the baseline only after writing the files. Ordinary write
418
+ failures restore the previous files and baseline.
419
+
420
+ 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.
421
+
422
+ 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.
423
+
424
+ ## Uninstall an app
425
+
426
+ ```bash
427
+ twenty app uninstall --remote dev
428
+ twenty app uninstall --yes --json
429
+ twenty app uninstall --universal-identifier <id> --yes
430
+ ```
431
+
432
+ `app uninstall` builds the app to find its universal identifier, checks that the app is installed on the target and that the workspace allows uninstalling it, then uninstalls it. `--universal-identifier` skips the build, so an app whose source no longer builds can still be uninstalled. It needs the server's `APPLICATIONS` permission.
433
+
434
+ 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).
435
+
436
+ 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.
437
+
438
+ ## Execute an app function
439
+
440
+ ```bash
441
+ twenty app exec --name add-numbers --remote dev --payload '{"a":2,"b":3}'
442
+ twenty app exec --universal-identifier <uuid> --payload @payload.json --json
443
+ printf '{"a":4,"b":7}' | twenty app exec --name add-numbers --remote dev --payload - --json
444
+ ```
445
+
446
+ Run from the app folder or pass `--path`. The CLI reads app definitions to find
447
+ its functions, then executes the selected function's deployed code. Sync local
448
+ changes with `app apply` or `app dev` first. Execution does not build bundles,
449
+ typecheck, sync, register or install the app.
450
+
451
+ Choose exactly one of `--name` (exact deployed name), `--universal-identifier`,
452
+ `--post-install`, `--pre-install` or `--uninstall-hook`. Hook selectors run the
453
+ hook's code and its side effects without installing or uninstalling the app.
454
+ Payloads must be JSON objects, supplied inline, through `@file`, or through `-`
455
+ for stdin; the default is `{}`. The selected function must belong to both the
456
+ local app manifest and the installed app on the target workspace.
457
+
458
+ The current server requires a signed-in user and the `APPLICATIONS` and
459
+ `WORKFLOWS` settings permissions. Sign in with `twenty auth login --url <url>
460
+ --name dev`, then pass `--remote dev`. API keys, including environment API-key
461
+ targets, cannot execute logic functions. The server additionally restricts
462
+ which functions the user may run on demand.
463
+
464
+ Human output shows status, duration, returned data, error and logs. `--json`
465
+ returns those fields in the standard envelope; NDJSON is not supported. A
466
+ function returning a non-SUCCESS status exits 1 with `EXECUTION_FAILED`, keeping
467
+ its data, logs and error in `error.details` with `outcome: "completed"`.
468
+
469
+ There are no automatic retries. The request deadline uses the deployed
470
+ function's timeout plus 60 seconds. A lost response or Ctrl+C can leave the
471
+ function running on the server; the error reports `outcome: "unknown"` and asks
472
+ you to check its effects before retrying. An acknowledged execution rejection
473
+ reports `"not-started"`. Ctrl+C exits 130, without rolling back side effects.
474
+ See the [execution implementation](../src/app/exec/README.md) for compatibility details.
475
+
476
+ ## Watch app logs
477
+
478
+ ```bash
479
+ twenty app logs --remote dev
480
+ twenty app logs --name add-numbers --remote dev
481
+ twenty app logs --universal-identifier <uuid> --format ndjson
482
+ ```
483
+
484
+ Run from the app folder or pass `--path`. This watches newly published execution
485
+ output from every logic function in that app. Output arrives when an execution
486
+ finishes, not line by line while it runs. It does not fetch historical logs,
487
+ watch other apps, or build, sync, register or install anything.
488
+
489
+ Names are not unique: `--name` matches every function with that exact name in the
490
+ app. Use `--universal-identifier` to isolate one function. The two filters are
491
+ mutually exclusive. Human output labels each execution with its function name
492
+ and identifier; NDJSON records contain `applicationUniversalIdentifier`,
493
+ `functionName`, `functionUniversalIdentifier` and `logs`.
494
+
495
+ Some server schemas expose only log text. If the identity fields are unsupported,
496
+ the CLI warns and subscribes once requesting only `logs`. An explicit filter
497
+ supplies that name or identifier; unknown identities are `null` in NDJSON and
498
+ omitted from human labels. With neither filter, those schemas cannot attribute
499
+ mixed output to individual functions.
500
+
501
+ The stream supports human output and `--format ndjson`; finite `--json` is
502
+ rejected. Ctrl+C closes it and exits 130. A server completion exits 0. A dropped
503
+ or stalled connection exits with an error and the emitted record count, with no
504
+ automatic reconnect because missed logs cannot be replayed. Slow output applies
505
+ backpressure; each SSE frame is limited to 16 MiB, with no session-size cap.
506
+ Connection setup and gaps between received body chunks each have a 60-second
507
+ deadline; server heartbeat comments keep quiet subscriptions alive.
508
+
509
+ This uses the existing app-log subscription and its `WORKFLOWS` permission and
510
+ application access checks. API keys and browser-login remotes are supported.
511
+ It does not require the paid audit-log entitlement, ClickHouse history or the
512
+ workspace-wide Settings logs API. See the [log subscription implementation](../src/app/function-logs/README.md).
513
+
514
+ ## Expired browser sessions
515
+
516
+ For commands that use a workspace, interactive OAuth sessions are checked before
517
+ the command starts. If a session is rejected or cannot be refreshed, the CLI
518
+ offers to sign in again once, validates the new session, and saves its credentials
519
+ without changing the remote's URL, default selection or other settings.
520
+
521
+ API keys, JSON/NDJSON output, `--no-input`, redirected stdin and CI never trigger
522
+ this prompt. Permission errors do not trigger sign-in either. If authentication
523
+ fails after a command starts, the CLI stops with the login hint; it does not
524
+ replay requests or restart an app apply. A remote changed during sign-in is left
525
+ as-is, and the command stops with `CONFLICT`.
526
+
527
+ ## Output
528
+
529
+ `twenty version` (or `--version`) reports the running executable's version.
530
+ `twenty version --json` also includes Node, platform and architecture.
531
+ `twenty commands --json` lists command arguments, flags, output modes and
532
+ project/target requirements for scripts that discover the command surface.
533
+
534
+ Commands print readable text by default. With `--json`, a command prints exactly one JSON document on stdout, including when it fails:
535
+
536
+ ```json
537
+ {
538
+ "schemaVersion": 1,
539
+ "ok": true,
540
+ "command": "version",
541
+ "data": {
542
+ "version": "2.46.0",
543
+ "node": "24.9.0",
544
+ "platform": "darwin",
545
+ "arch": "arm64"
546
+ },
547
+ "warnings": []
548
+ }
549
+ ```
550
+
551
+ A failure has `"ok": false` and an `error` object with a stable `code`, a `message`, and sometimes a `hint` and `details`.
552
+
553
+ Exit codes:
554
+
555
+ | Code | Meaning |
556
+ | ---- | ----------------------------------- |
557
+ | 0 | Success |
558
+ | 1 | Failure |
559
+ | 2 | Usage error or missing confirmation |
560
+ | 3 | Authentication or permission denied |
561
+ | 4 | Not found |
562
+ | 5 | Partial failure |
563
+ | 6 | Conflict |
564
+ | 130 | Cancelled |