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.
- package/CONTRIBUTING.md +166 -0
- package/LICENSE +21 -0
- package/README.md +64 -57
- package/dist/app-template/.nvmrc +1 -0
- package/dist/app-template/.oxlintrc.json +37 -0
- package/dist/app-template/AGENTS.md +70 -0
- package/dist/app-template/CHANGELOG.md +7 -0
- package/dist/app-template/README.md +34 -0
- package/dist/app-template/SETUP.md +47 -0
- package/dist/app-template/github/workflows/cd.yml +42 -0
- package/dist/app-template/github/workflows/ci.yml +60 -0
- package/dist/app-template/github/workflows/publish.yml +52 -0
- package/dist/app-template/gitignore +43 -0
- package/dist/app-template/locales/en.json +1 -0
- package/dist/app-template/package.json +36 -0
- package/dist/app-template/public/logo.svg +7 -0
- package/dist/app-template/src/__tests__/application-config.test.ts +14 -0
- package/dist/app-template/src/__tests__/global-setup.ts +56 -0
- package/dist/app-template/src/__tests__/run-twenty.ts +66 -0
- package/dist/app-template/src/__tests__/schema.integration-test.ts +46 -0
- package/dist/app-template/src/application-config.ts +13 -0
- package/dist/app-template/src/constants/universal-identifiers.ts +12 -0
- package/dist/app-template/src/default-role.ts +16 -0
- package/dist/app-template/src/front-components/main-page.tsx +287 -0
- package/dist/app-template/src/logic-functions/health-check.ts +11 -0
- package/dist/app-template/src/navigation-menu-items/main-page.navigation-menu-item.ts +19 -0
- package/dist/app-template/src/page-layouts/main-page.page-layout.ts +42 -0
- package/dist/app-template/tsconfig.json +37 -0
- package/dist/app-template/tsconfig.spec.json +9 -0
- package/dist/app-template/vitest.config.ts +31 -0
- package/dist/app-template/vitest.unit.config.ts +14 -0
- package/dist/app-template/yarn.lock +3374 -0
- package/dist/app-template/yarnrc.yml +1 -0
- package/dist/app-worker.cjs +1 -0
- package/dist/chunks/application-DuIJE2qG.cjs +5 -0
- package/dist/chunks/assert-pull-paths-BuQ1ZRvQ.cjs +1 -0
- package/dist/chunks/build-CX6FBrbu.cjs +1 -0
- package/dist/chunks/build-pull-entities-zwArrVMk.cjs +5 -0
- package/dist/chunks/build-source-manifest-CLKH5vzc.cjs +2 -0
- package/dist/chunks/build-source-snapshot-CbGsy1-v.cjs +206 -0
- package/dist/chunks/check-node-requirement-mqwNPktb.cjs +1 -0
- package/dist/chunks/cli-error-CSsayo0u.cjs +1 -0
- package/dist/chunks/cli-version.constant-aSp8WZDh.cjs +1 -0
- package/dist/chunks/collect-watch-inputs-B9jWG4W0.cjs +1 -0
- package/dist/chunks/compile-application-translations-B59jYVWc.cjs +1 -0
- package/dist/chunks/config-file-mode.constant-DLBzKKUl.cjs +1 -0
- package/dist/chunks/confirm-in-terminal-BxEM5lxE.cjs +1 -0
- package/dist/chunks/constants-CL6mrTMT-B748cNNG.cjs +23 -0
- package/dist/chunks/constants-Cf5-CwMu.cjs +1 -0
- package/dist/chunks/convert-to-label-BPBLtc_O.cjs +1 -0
- package/dist/chunks/create-app-not-installed-error-Cc8YZqI7.cjs +1 -0
- package/dist/chunks/create-cancelled-error-DgrNfOv2.cjs +1 -0
- package/dist/chunks/create-http-status-error-D3gy3DzB.cjs +1 -0
- package/dist/chunks/create-metadata-client-ChjDalSi.cjs +6 -0
- package/dist/chunks/create-pull-base-write-rxjcKtD-.cjs +1 -0
- package/dist/chunks/create-rest-client-eAfOFfDA.cjs +58 -0
- package/dist/chunks/create-tooling-failure-DLI5FlQP.cjs +1 -0
- package/dist/chunks/credential-kind-labels.constant-CKdoQF8o.cjs +1 -0
- package/dist/chunks/extract-manifest-from-file-j-Y9wNas.cjs +1 -0
- package/dist/chunks/fetch-metadata-inspection-Dp034TpK.cjs +1 -0
- package/dist/chunks/fetch-signed-in-identity-BhvaL3zp.cjs +1 -0
- package/dist/chunks/fetch-workspace-id-DJStmf_V.cjs +1 -0
- package/dist/chunks/find-remote-D48r20g0.cjs +1 -0
- package/dist/chunks/format-app-duration-gu2oRLB4.cjs +1 -0
- package/dist/chunks/format-body-VcKL4WXt.cjs +1 -0
- package/dist/chunks/format-data-value-B6rIbpJv.cjs +1 -0
- package/dist/chunks/format-details-COEwKz_M.cjs +2 -0
- package/dist/chunks/format-metadata-fields-DJnTg1R9.cjs +2 -0
- package/dist/chunks/format-metadata-owner-yUeocnwY.cjs +1 -0
- package/dist/chunks/format-table-4drtI5sP.cjs +2 -0
- package/dist/chunks/fs-utils-Bl1fQcaJ.cjs +2 -0
- package/dist/chunks/generate-application-client-CCVLjxTj.cjs +1 -0
- package/dist/chunks/get-app-plan-summary-B0uwHXbO.cjs +8 -0
- package/dist/chunks/get-config-path-C-D-QHuX.cjs +1 -0
- package/dist/chunks/get-credential-kind-JI9heHe2.cjs +1 -0
- package/dist/chunks/get-function-input-schema-GNk3NRLJ-Bfph1o9z.cjs +1 -0
- package/dist/chunks/get-tooling-error-context-C69TBSej.cjs +1 -0
- package/dist/chunks/global-flags.constant-8GfW0sNU.cjs +1 -0
- package/dist/chunks/has-yarn-plug-and-play-X3ay5Xl0.cjs +1 -0
- package/dist/chunks/is-access-token-expiring-gICPXsbO.cjs +1 -0
- package/dist/chunks/is-application-not-found-error-mNeGbxmT.cjs +1 -0
- package/dist/chunks/is-exported-manifest-CriNOWvx.cjs +1 -0
- package/dist/chunks/is-inside-directory-t3ixLQDr.cjs +1 -0
- package/dist/chunks/is-interaction-allowed-ycbAFMIj.cjs +1 -0
- package/dist/chunks/is-same-universal-identifier-BCBib3RP.cjs +1 -0
- package/dist/chunks/is-supported-locale-BAjnKnKj.cjs +20 -0
- package/dist/chunks/kebab-case-D0usAzG-.cjs +1 -0
- package/dist/chunks/list-application-source-files-jEau5Rie.cjs +1 -0
- package/dist/chunks/metadata-D9sQOTRK.cjs +1 -0
- package/dist/chunks/metadata-page.constant-BkG4ndrO.cjs +1 -0
- package/dist/chunks/oauth-timing.constant-BClMQcKn.cjs +1 -0
- package/dist/chunks/open-browser-LPuVspT7.cjs +1 -0
- package/dist/chunks/package-CdYv4LEx.cjs +1 -0
- package/dist/chunks/parse-api-url-BllxRdpE.cjs +1 -0
- package/dist/chunks/parse-app-export-BkfflyUY.cjs +1 -0
- package/dist/chunks/parse-graphql-response-CtP9KTS2.cjs +1 -0
- package/dist/chunks/parse-json-input-CvMTKAWw.cjs +1 -0
- package/dist/chunks/print-typescript-value-DhElcFzI.cjs +3 -0
- package/dist/chunks/pull-source-eoiJwFb9.cjs +1 -0
- package/dist/chunks/read-app-identity-SeleQ_D6.cjs +1 -0
- package/dist/chunks/read-application-identity-BFg_p84F.cjs +1 -0
- package/dist/chunks/read-command-values-uSJqBG_E.cjs +1 -0
- package/dist/chunks/read-config-DUJ27809.cjs +1 -0
- package/dist/chunks/read-input-value-Cxgh_r2g.cjs +1 -0
- package/dist/chunks/read-source-identity-C8LG1v6U.cjs +1 -0
- package/dist/chunks/read-watch-input-stamp-BxeOb0vI.cjs +1 -0
- package/dist/chunks/record-pull-base-BbxvlnU8.cjs +1 -0
- package/dist/chunks/record-typecheck-config-inputs-BDqkKCrj.cjs +1 -0
- package/dist/chunks/request-oauth-tokens-C4KKlTQT.cjs +1 -0
- package/dist/chunks/request-timeout-milliseconds.constant-DtyQHrll.cjs +1 -0
- package/dist/chunks/require-approval-fhegB1ap.cjs +1 -0
- package/dist/chunks/resolve-app-project-D5ZXIpsG.cjs +1 -0
- package/dist/chunks/resolve-inside-sdk-p-16gKb4.cjs +1 -0
- package/dist/chunks/resolve-metadata-object-QoDi-Aik.cjs +1 -0
- package/dist/chunks/resolve-project-typescript-CDY8BbmP.cjs +1 -0
- package/dist/chunks/resolve-request-url-jC5kYwkn.cjs +1 -0
- package/dist/chunks/resolve-source-sdk-mK-f4q55.cjs +1 -0
- package/dist/chunks/result-item-limit.constant-CDrOhiWD.cjs +1 -0
- package/dist/chunks/run-api-graphql-command-Biy0WqXa.cjs +1 -0
- package/dist/chunks/run-api-rest-command-D9kdQceV.cjs +1 -0
- package/dist/chunks/run-app-add-command-DcTQg7Si.cjs +163 -0
- package/dist/chunks/run-app-apply-command-DwLPPJ-Y.cjs +2 -0
- package/dist/chunks/run-app-build-command-BRYb0one.cjs +2 -0
- package/dist/chunks/run-app-dev-command-CVeh9Gab.cjs +2 -0
- package/dist/chunks/run-app-exec-command-DKLNLbL5.cjs +3 -0
- package/dist/chunks/run-app-init-command-CJj8_spC.cjs +2 -0
- package/dist/chunks/run-app-logs-command-Dusb8K-W.cjs +9 -0
- package/dist/chunks/run-app-operation-BWrY9YZK.cjs +1 -0
- package/dist/chunks/run-app-plan-command-C0_3e332.cjs +1 -0
- package/dist/chunks/run-app-pull-command-DcZqs8gR.cjs +3 -0
- package/dist/chunks/run-app-typecheck-command-C8LqpOej.cjs +1 -0
- package/dist/chunks/run-app-uninstall-command-ncWS_k-p.cjs +2 -0
- package/dist/chunks/run-auth-login-command-_zDgRcth.cjs +1 -0
- package/dist/chunks/run-auth-logout-command-BO6O7GVi.cjs +2 -0
- package/dist/chunks/run-auth-status-command-Cj-vqoHg.cjs +1 -0
- package/dist/chunks/run-auth-token-command-D4geJ-rx.cjs +1 -0
- package/dist/chunks/run-cli-c9TOR0zj.cjs +23 -0
- package/dist/chunks/run-commands-command-C6gvMpQ_.cjs +2 -0
- package/dist/chunks/run-data-get-command-jDEU4EG1.cjs +1 -0
- package/dist/chunks/run-data-list-command-EnCdbuzc.cjs +1 -0
- package/dist/chunks/run-doctor-command-DnJ6io-m.cjs +2 -0
- package/dist/chunks/run-metadata-field-describe-command-vJngpw2S.cjs +3 -0
- package/dist/chunks/run-metadata-field-list-command-CR4w7fI6.cjs +3 -0
- package/dist/chunks/run-metadata-object-describe-command-CHP8K-W2.cjs +3 -0
- package/dist/chunks/run-metadata-object-list-command-BqXMcAp6.cjs +2 -0
- package/dist/chunks/run-open-command-DKl8acw0.cjs +1 -0
- package/dist/chunks/run-remote-list-command-DeAzr5Mn.cjs +1 -0
- package/dist/chunks/run-remote-remove-command-D0C5sWjP.cjs +2 -0
- package/dist/chunks/run-remote-rename-command-Byq9WRHd.cjs +1 -0
- package/dist/chunks/run-remote-use-command-B6VVDlMG.cjs +1 -0
- package/dist/chunks/run-version-command-BmSk8PUV.cjs +1 -0
- package/dist/chunks/select-target-C1VZjf-A.cjs +1 -0
- package/dist/chunks/sign-in-to-remote-again-CW8HPSTP.cjs +1 -0
- package/dist/chunks/sign-in-with-browser-C3kC6uP2.cjs +1 -0
- package/dist/chunks/style-BR52fK4V.cjs +1 -0
- package/dist/chunks/target-environment-variable.constant-D7z8AihP.cjs +1 -0
- package/dist/chunks/to-cli-error-BBYf_wqo.cjs +1 -0
- package/dist/chunks/to-public-target-CIAwoOKL.cjs +1 -0
- package/dist/chunks/to-worker-output-diagnostics-lprh9b0l.cjs +2 -0
- package/dist/chunks/typecheck-application-DGrovgX9.cjs +2 -0
- package/dist/chunks/undici-D3mKdfwl.cjs +95 -0
- package/dist/chunks/update-config-BkKBEfAp.cjs +1 -0
- package/dist/chunks/utils-B8EXJeaX-DK3gCslL.cjs +33 -0
- package/dist/chunks/validate-app-path-Dau-PC0a.cjs +1 -0
- package/dist/chunks/validate-remote-name-w-qnr43w.cjs +1 -0
- package/dist/chunks/write-config-atomically-DRWTukU-.cjs +1 -0
- package/dist/cli.cjs +2 -0
- package/docs/commands.md +564 -0
- package/package.json +41 -18
- package/src/app/README.md +43 -0
- package/src/app/add/README.md +69 -0
- package/src/app/bundles/README.md +85 -0
- package/src/app/client/README.md +62 -0
- package/src/app/dev/README.md +35 -0
- package/src/app/exec/README.md +48 -0
- package/src/app/function-logs/README.md +44 -0
- package/src/app/manifest/README.md +74 -0
- package/src/app/pull/README.md +28 -0
- package/src/app/source/README.md +33 -0
- package/src/app/typecheck/README.md +67 -0
- package/.npmignore +0 -2
- package/bin/twenty +0 -2
- 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"))});
|
package/docs/commands.md
ADDED
|
@@ -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 |
|