9001078cb2
## I have read the CONTRIBUTING.md file. YES ## What kind of change does this PR introduce? Fix (CLI) — `twenty deploy` now detects an expired/invalid API key on the active remote and offers an interactive re-auth flow (TTY only). In non-TTY contexts the behavior is unchanged: a clear error and a non-zero exit. Fixes #20197 ## What is the current behavior? After a workspace DB reset, key revocation, or workspace deletion, both `twenty deploy` and (effectively) `twenty dev` fail with: ``` Upload failed: Token has expired. ``` The message is technically correct but gives the user no way forward. They have to know to mint a new key from **Settings → Developers** and re-run `twenty remote add --local --api-key <NEW_KEY>`. This came up while testing PR #20181 and is the same friction on any DB reset, key revocation, or workspace deletion. ## What is the new behavior? Two changes, layered: ### (1) Better error message + remediation hint When the upload returns a 401 or its message matches a token-expired pattern (`/token has expired|unauthori[sz]ed|invalid api key/i`), `appDeploy` now prints: ``` Your API key for remote "local" is no longer valid (the workspace may have been reset, or the key was revoked). Re-authenticate with: twenty remote:add --as local --api-key <NEW_KEY> Generate a new key at: <SERVER_URL>/settings/developers ``` ### (2) Interactive re-auth prompt (TTY only) If the process is attached to a TTY, after the hint is printed the user is prompted: ``` Re-authenticate now? (Y/n) ``` - **Yes** → re-validate the token (it may have been refreshed externally), and if still invalid, instruct the user to re-run `remote:add`. The original `appDeploy` is then retried once. - **No** → the original `DEPLOY_FAILED` error is surfaced (same code, better message). - **Non-TTY (CI, scripts, redirects)** → the prompt is suppressed entirely. The user gets the hint and a non-zero exit, preserving scriptable behavior. **No change** to existing CI scripts. ## Acceptance criteria | Scenario | Before | After | |---|---|---| | Happy path deploy | ✅ works | ✅ works (no change) | | Deploy with expired key (TTY) | generic error, exit 1 | hint + prompt, retry on Y, error on N | | Deploy with expired key (CI / no-TTY) | generic error, exit 1 | hint + exit 1 (no prompt, scriptable) | | Deploy with unrelated error (e.g. 500) | generic error, exit 1 | unchanged (no false positive on the matcher) | ## Reproduction 1. Spin up Twenty, mint an API key, run `twenty deploy` — confirm the happy path. 2. Reset the DB (`core.appToken` cleared) and re-run `twenty deploy` — confirm the new hint + prompt fire and the retry succeeds. 3. Repeat step 2 in a non-TTY context (e.g. `twenty deploy < /dev/null` or via `script -qc ''`) — confirm the prompt is suppressed and the scriptable exit-1 behavior is preserved. ## Implementation notes - **`FileApi.uploadAppTarball`** now tags 401 responses with an `isAuthError: true` flag on the failing `ApiResponse`. The existing `error` string is still populated so callers that don't check the flag continue to work — **additive, no breaking change**. - **`FailingApiResponse<TError>`** gained an optional `isAuthError?: boolean` field. The other `ApiResponse` call sites in the SDK don't need to set it. - **`@/cli/utilities/auth/reauth-helper.ts`** is new. It owns: - `isTokenExpiredMessage(...)` — pure matcher, easy to unit-test, used as a backstop if a non-401 message still says "expired" (GraphQL returns 200 with errors in some cases). - `promptForReauthentication(remoteName)` — TTY-gated `inquirer.confirm` prompt that re-validates the token and either returns `'reauthenticated'`, `'declined'`, or `'non-interactive'`. - **`@/cli/operations/deploy.ts`** is the single call site that wires the helper. The helper is structured so it can be reused from the dev orchestrator's upload step (a follow-up) without changes. - **New unit test** at `__tests__/reauth-helper.test.ts` covers the matcher: positive cases, negative cases, case-insensitivity, and nullish input. ## Out of scope (per the issue) - Long-lived dev tokens for `--local` remotes. - Web-based OAuth login flow for the CLI (the existing `authenticate(...)` flow in `remote.ts` is fine; the prompt here just tells the user to re-run it). ## Files changed ``` packages/twenty-sdk/src/cli/operations/deploy.ts | 33 ++++++++ packages/twenty-sdk/src/cli/utilities/api/api-response-type.ts | 1 + packages/twenty-sdk/src/cli/utilities/api/file-api.ts | 8 +++ packages/twenty-sdk/src/cli/utilities/auth/__tests__/reauth-helper.test.ts | 34 ++++++++++ packages/twenty-sdk/src/cli/utilities/auth/reauth-helper.ts | 61 ++++++++++++++++++ 5 files changed, 137 insertions(+) ``` Happy to address feedback and split this into two PRs (hint-only first, prompt-on-top) if the maintainers prefer a smaller first cut. --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: martmull <martmull@hotmail.fr>
59 lines
3.1 KiB
Markdown
59 lines
3.1 KiB
Markdown
<div align="center">
|
|
<a href="https://twenty.com">
|
|
<picture>
|
|
<img alt="Twenty logo" src="https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-website/public/images/core/logo.svg" height="128">
|
|
</picture>
|
|
</a>
|
|
<h1>Create Twenty App</h1>
|
|
|
|
<a href="https://www.npmjs.com/package/create-twenty-app"><img alt="NPM version" src="https://img.shields.io/npm/v/create-twenty-app.svg?style=for-the-badge&labelColor=000000"></a>
|
|
<a href="https://github.com/twentyhq/twenty/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/npm/l/next.svg?style=for-the-badge&labelColor=000000"></a>
|
|
<a href="https://discord.gg/cx5n4Jzs57"><img alt="Join the community on Discord" src="https://img.shields.io/badge/Join%20the%20community-blueviolet.svg?style=for-the-badge&logo=Twenty&labelColor=000000&logoWidth=20"></a>
|
|
|
|
</div>
|
|
|
|
The official scaffolding CLI for building apps on top of [Twenty CRM](https://twenty.com). Sets up a ready-to-run project with [twenty-sdk](https://www.npmjs.com/package/twenty-sdk).
|
|
|
|
## Quick start
|
|
|
|
```bash
|
|
npx create-twenty-app@latest my-twenty-app
|
|
cd my-twenty-app
|
|
yarn twenty dev
|
|
```
|
|
|
|
The scaffolder will:
|
|
|
|
1. Create a new project with TypeScript, linting, tests, and a preconfigured `twenty` CLI
|
|
2. Start a local Twenty server via Docker (pulls the latest image automatically)
|
|
3. Authenticate with the development API key
|
|
|
|
## Options
|
|
|
|
| Flag | Description |
|
|
| ---------------------------------- | --------------------------------------------------------------------- |
|
|
| `--name <name>` | Set the app name |
|
|
| `--display-name <displayName>` | Set the display name |
|
|
| `--description <description>` | Set the description |
|
|
| `--url <url>` | Twenty workspace URL (default: `http://localhost:2020`) |
|
|
| `--authentication-method <method>` | `oauth` or `apiKey` (default: `apiKey` for local, `oauth` for remote) |
|
|
|
|
## Documentation
|
|
|
|
Full documentation is available at **[docs.twenty.com/developers/extend/apps](https://docs.twenty.com/developers/extend/apps/getting-started/quick-start)**:
|
|
|
|
- [Quick Start](https://docs.twenty.com/developers/extend/apps/getting-started/quick-start) — scaffold, run a local server, sync your code
|
|
- [Concepts](https://docs.twenty.com/developers/extend/apps/getting-started/concepts) — how apps work: entity model, sandboxing, lifecycle
|
|
- [Operations](https://docs.twenty.com/developers/extend/apps/operations/overview) — CLI, testing, CI, deploy and publish
|
|
|
|
## Troubleshooting
|
|
|
|
- Server not starting: check Docker is running (`docker info`), then try `yarn twenty docker:logs`.
|
|
- Auth not working: run `yarn twenty remote:add` to re-authenticate.
|
|
- Types not generated: ensure `yarn twenty dev` is running — it auto-generates the typed client.
|
|
|
|
## Contributing
|
|
|
|
- See our [GitHub](https://github.com/twentyhq/twenty)
|
|
- Join our [Discord](https://discord.gg/cx5n4Jzs57)
|