twenty-bells 1.0.1 → 1.0.2
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/README.md
CHANGED
|
@@ -56,12 +56,9 @@ Updating after a `git pull` is the same `yarn twenty apply`. To remove the app,
|
|
|
56
56
|
|
|
57
57
|
## Permissions
|
|
58
58
|
|
|
59
|
-
The panel calls the API under the **application's** role, not under the permissions of the person who opened it.
|
|
59
|
+
The panel calls the API under the **application's** role, not under the permissions of the person who opened it, so it decides relevance itself: by deal owner, company account owner, task assignee and task author. **It always does.** On the Organization plan Twenty also scopes the response server-side, which can only narrow it further — so the two compose, and neither is skipped on account of the other.
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
- on other plans the predicates sync but stay inert, and the panel filters client-side: by deal owner, company account owner and task assignee.
|
|
63
|
-
|
|
64
|
-
Which mode is live is detected at runtime: the panel asks the server for records it should have withheld and reads the answer. When Twenty does the scoping itself, the header carries an **Access: server** badge; otherwise the panel filters on its own and says nothing.
|
|
61
|
+
An earlier version tried to be clever here. It probed one object, and where the answer looked like the server was scoping the data it turned its own filter off for everything — including objects the server was not scoping at all. A task with no assignee, set by somebody else, then showed up in a personal feed. Guessing at someone else's enforcement is not worth the request it costs.
|
|
65
62
|
|
|
66
63
|
**The client-side filter is not a security boundary.** Until row-level permissions are enforced, the server hands the whole workspace to the browser. Install the app where that is acceptable.
|
|
67
64
|
|
package/manifest.json
CHANGED
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"public/gallery-3-buzz.png"
|
|
16
16
|
],
|
|
17
17
|
"defaultRoleUniversalIdentifier": "c42e0f90-a77f-41cb-af0e-60273784d211",
|
|
18
|
-
"aboutDescription": "# The Bell\n\nAn activity feed inside Twenty. A bell button in the top-right corner opens a side panel showing what happened in the CRM, what your team is talking about, and what is overdue on you personally.\n\nРусская версия: [README.ru.md](https://github.com/frasimah/twenty_bells/blob/main/README.ru.md).\n\n## What it does\n\n- **Change feed.** Records created and edited, read from the standard `timelineActivity` — custom objects included. Field changes render as a diff, and `SELECT` values keep the same coloured labels the record page uses.\n- **Documents.** Twenty emits no timeline event for an attachment, so the panel reads files separately and files each one into the card of the record it hangs on — a deal, a contact, a custom object. Clicking the file name opens the attachment; clicking anywhere else on the card opens the record.\n- **Buzz.** Team notes as posts with their comments underneath. Twenty has no comment object — amending a note's body is the mechanism — so each thread is reconstructed from the `note.updated` diffs, and every reply says who wrote it and when.\n- **Tasks.** Overdue first, oldest first, then everything else by due date. Done tasks are dropped. If a task belongs to a deal, the deal is on the row. The badge counts what is assigned to you and not done — asked of the server, so it is the real number rather than the length of the loaded page.\n- **Grouping.** Several events on one record collapse into a single line with a counter that expands in place. A burst of identical events — an import, a bulk edit — becomes one row like `29 × document · added`.\n- **Read / unread.** The marker is personal, stored per `userId` in the app's own object.\n- **Opens the record.** A click opens the object in Twenty's side panel without leaving the page.\n\n## What it costs to keep open\n\nNothing at all until the bell is clicked. The panel is a side panel and Twenty loads it on demand — the renderer is not even fetched before that, and closing the panel unmounts the whole thing.\n\nOpen, it polls once a minute, and every tick asks one cheap question first: the id of the newest event and the size of the window, one row without relations, about 2 KB. Unchanged means the tab already holds the truth and nothing else is fetched. Five minutes without a click, a keypress or a scroll inside the panel and it stops asking altogether, saying so in a line at the top; the next touch resumes it and refreshes at once.\n\nOn a workspace of some 1,800 tasks and as many notes that comes to roughly 10 KB a minute while somebody is actually reading it.\n\nThe trade is latency: a new event surfaces within a minute rather than instantly.\n\n## Requirements\n\n- Twenty server **2.20.0 or later** (declared as `engines.twenty`). CI installs the app and runs the integration tests against both ends of that range on every push, so the floor is a tested promise rather than a guess. 2.19 is genuinely out: it requires a custom object to spell out its own system fields, which the server has filled in itself since 2.20.\n- Node 24 and Yarn 4 for local development\n\n## Install into your Twenty\n\n```bash\ngit clone https://github.com/frasimah/twenty_bells.git\ncd twenty_bells\ncorepack enable\nyarn install\n```\n\nYou need an API key for the target instance: in Twenty open **Settings → APIs → Create API key** and copy the value — it is shown once.\n\n```bash\nyarn twenty remote:add --as my-crm --url https://your-twenty-server.com --api-key <YOUR_KEY>\nyarn twenty remote:use my-crm\nyarn twenty apply\n```\n\n`--url` is the **server** address, not the frontend, if the two are on different ports. The key is stored in `~/.twenty/config.json` and never reaches the repository.\n\n`apply` prints the plan first, then syncs the application, its role, the technical `feedReadState` object, the front component and the command-menu button. Reload the CRM tab afterwards — the bell appears in the top-right corner.\n\nNo instance of your own? `yarn twenty docker:start` brings one up on `http://localhost:2020` (`tim@apple.dev` / `tim@apple.dev`), and `remote:add --local` finds it by itself.\n\nUpdating after a `git pull` is the same `yarn twenty apply`. To remove the app, `yarn twenty app:uninstall`.\n\n## Permissions\n\nThe panel calls the API under the **application's** role, not under the permissions of the person who opened it
|
|
18
|
+
"aboutDescription": "# The Bell\n\nAn activity feed inside Twenty. A bell button in the top-right corner opens a side panel showing what happened in the CRM, what your team is talking about, and what is overdue on you personally.\n\nРусская версия: [README.ru.md](https://github.com/frasimah/twenty_bells/blob/main/README.ru.md).\n\n## What it does\n\n- **Change feed.** Records created and edited, read from the standard `timelineActivity` — custom objects included. Field changes render as a diff, and `SELECT` values keep the same coloured labels the record page uses.\n- **Documents.** Twenty emits no timeline event for an attachment, so the panel reads files separately and files each one into the card of the record it hangs on — a deal, a contact, a custom object. Clicking the file name opens the attachment; clicking anywhere else on the card opens the record.\n- **Buzz.** Team notes as posts with their comments underneath. Twenty has no comment object — amending a note's body is the mechanism — so each thread is reconstructed from the `note.updated` diffs, and every reply says who wrote it and when.\n- **Tasks.** Overdue first, oldest first, then everything else by due date. Done tasks are dropped. If a task belongs to a deal, the deal is on the row. The badge counts what is assigned to you and not done — asked of the server, so it is the real number rather than the length of the loaded page.\n- **Grouping.** Several events on one record collapse into a single line with a counter that expands in place. A burst of identical events — an import, a bulk edit — becomes one row like `29 × document · added`.\n- **Read / unread.** The marker is personal, stored per `userId` in the app's own object.\n- **Opens the record.** A click opens the object in Twenty's side panel without leaving the page.\n\n## What it costs to keep open\n\nNothing at all until the bell is clicked. The panel is a side panel and Twenty loads it on demand — the renderer is not even fetched before that, and closing the panel unmounts the whole thing.\n\nOpen, it polls once a minute, and every tick asks one cheap question first: the id of the newest event and the size of the window, one row without relations, about 2 KB. Unchanged means the tab already holds the truth and nothing else is fetched. Five minutes without a click, a keypress or a scroll inside the panel and it stops asking altogether, saying so in a line at the top; the next touch resumes it and refreshes at once.\n\nOn a workspace of some 1,800 tasks and as many notes that comes to roughly 10 KB a minute while somebody is actually reading it.\n\nThe trade is latency: a new event surfaces within a minute rather than instantly.\n\n## Requirements\n\n- Twenty server **2.20.0 or later** (declared as `engines.twenty`). CI installs the app and runs the integration tests against both ends of that range on every push, so the floor is a tested promise rather than a guess. 2.19 is genuinely out: it requires a custom object to spell out its own system fields, which the server has filled in itself since 2.20.\n- Node 24 and Yarn 4 for local development\n\n## Install into your Twenty\n\n```bash\ngit clone https://github.com/frasimah/twenty_bells.git\ncd twenty_bells\ncorepack enable\nyarn install\n```\n\nYou need an API key for the target instance: in Twenty open **Settings → APIs → Create API key** and copy the value — it is shown once.\n\n```bash\nyarn twenty remote:add --as my-crm --url https://your-twenty-server.com --api-key <YOUR_KEY>\nyarn twenty remote:use my-crm\nyarn twenty apply\n```\n\n`--url` is the **server** address, not the frontend, if the two are on different ports. The key is stored in `~/.twenty/config.json` and never reaches the repository.\n\n`apply` prints the plan first, then syncs the application, its role, the technical `feedReadState` object, the front component and the command-menu button. Reload the CRM tab afterwards — the bell appears in the top-right corner.\n\nNo instance of your own? `yarn twenty docker:start` brings one up on `http://localhost:2020` (`tim@apple.dev` / `tim@apple.dev`), and `remote:add --local` finds it by itself.\n\nUpdating after a `git pull` is the same `yarn twenty apply`. To remove the app, `yarn twenty app:uninstall`.\n\n## Permissions\n\nThe panel calls the API under the **application's** role, not under the permissions of the person who opened it, so it decides relevance itself: by deal owner, company account owner, task assignee and task author. **It always does.** On the Organization plan Twenty also scopes the response server-side, which can only narrow it further — so the two compose, and neither is skipped on account of the other.\n\nAn earlier version tried to be clever here. It probed one object, and where the answer looked like the server was scoping the data it turned its own filter off for everything — including objects the server was not scoping at all. A task with no assignee, set by somebody else, then showed up in a personal feed. Guessing at someone else's enforcement is not worth the request it costs.\n\n**The client-side filter is not a security boundary.** Until row-level permissions are enforced, the server hands the whole workspace to the browser. Install the app where that is acceptable.\n\n## Settings\n\nThe app deliberately ships **no Settings tab**. What would be there — poll interval, page size — lives in `src/front-components/activity-feed.tsx` as constants; change one and run `yarn twenty apply`.\n\n> The reason is not minimalism. The host injects application-variable values into a front component still **encrypted** — `enc:v2:<workspace>:<blob>` — and neither `twenty-sdk` nor `twenty-client-sdk` decrypts them. Verified by printing the raw payload inside the panel: `getApplicationVariable('SHOW_ATTACHMENTS')` returned the ciphertext, not `true`, and the panel read it as \"off\" and hid every file. Switches an admin can move that change nothing are worse than no switches, so they are gone until Twenty decrypts them.\n>\n> This cannot be diagnosed from outside either: the metadata API refuses to read the values back — an API key gets `Cannot return null for non-nullable field Application.applicationVariables`, the application token is denied by permissions. Writing (`updateOneApplicationVariable`) does work.\n\nApplication variables are workspace-wide anyway; the SDK has no per-user ones. Anything personal lives in the app's `feedReadState` object.\n\n## What the feed cannot show\n\nLimits of the source, not of the app:\n\n- **deletions** — Twenty writes no event for them at all;\n- **emails and meetings** — `message` and `calendarEvent` have no relation to `timelineActivity`;\n- **history of deleted records** — the event survives but its link breaks; those rows are marked \"record deleted\".\n\n## Development\n\n```bash\nyarn lint # oxlint\nyarn typecheck # tsgo\nyarn test:unit # vitest, no server needed\nyarn test # integration tests against a real instance\n```\n\nCI runs all of it on every push against a Twenty instance spawned for the job. CD is manual (`workflow_dispatch`): the scaffold's deploy-on-push targeted `http://localhost:2020`, which from a GitHub runner is the runner itself.\n\n## Publishing\n\n`Publish` (`.github/workflows/publish.yml`) publishes the package to npm with provenance via [npm trusted publishing](https://docs.npmjs.com/trusted-publishers), which is also how app ownership is claimed in a Twenty marketplace.\n\n1. On npmjs.com register this repository and `publish.yml` as a trusted publisher of the package.\n2. Bump `version` in `package.json`, then push a tag (`git tag v1.0.1 && git push --tags`) or run the workflow from the Actions tab.\n\nnpm accepts provenance only from **public** repositories. To publish from a private one, set `TWENTY_APP_PUBLISH_DISABLE_PROVENANCE: 'true'` in the publish step — at the cost of the trust badge and of the marketplace ownership claim.\n\nThe marketplace catalog syncs from npm hourly; `yarn twenty dev:catalog-sync` triggers it immediately.\n\n## Learn more\n\n- [Twenty Apps documentation](https://docs.twenty.com/developers/extend/apps/getting-started/quick-start)\n- [twenty-sdk CLI reference](https://www.npmjs.com/package/twenty-sdk)\n- Changes are listed in [CHANGELOG.md](https://github.com/frasimah/twenty_bells/blob/main/CHANGELOG.md).\n",
|
|
19
19
|
"yarnLockChecksum": "5fdce6f28140aad07baeb584f638c172",
|
|
20
|
-
"packageJsonChecksum": "
|
|
20
|
+
"packageJsonChecksum": "cbba653acc0c7236afe2f665eebd9606",
|
|
21
21
|
"requiredServerVersionRange": ">=2.20.0"
|
|
22
22
|
},
|
|
23
23
|
"objects": [
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"componentName": "ActivityFeed",
|
|
121
121
|
"sourceComponentPath": "src/front-components/activity-feed.tsx",
|
|
122
122
|
"builtComponentPath": "src/front-components/activity-feed.mjs",
|
|
123
|
-
"builtComponentChecksum": "
|
|
123
|
+
"builtComponentChecksum": "03284197dcb7114b875f18893f5b713ff1280c02946cf2145a479b554ffa17e2",
|
|
124
124
|
"isHeadless": false,
|
|
125
125
|
"usesSdkClient": false
|
|
126
126
|
}
|
|
@@ -171,7 +171,6 @@
|
|
|
171
171
|
"ru-RU": {
|
|
172
172
|
"SDBm/z": "ещё 1 комментарий",
|
|
173
173
|
"/9AFM1": "ещё 1 событие по этой записи",
|
|
174
|
-
"BNg5+K": "Права: сервер",
|
|
175
174
|
"MMAUm4": "На вас и не завершено: {count}",
|
|
176
175
|
"4YE4Qc": "По сроку",
|
|
177
176
|
"H86f9p": "Свернуть",
|
|
@@ -202,7 +201,6 @@
|
|
|
202
201
|
"eBGNxQ": "Персональная отметка прочтения для ленты",
|
|
203
202
|
"OZdaTZ": "Контакт",
|
|
204
203
|
"w80YWM": "Запись",
|
|
205
|
-
"AxtbvG": "Row-level permissions включены: доступ ограничивает сам Twenty, панель ничего не доотфильтровывает.",
|
|
206
204
|
"+Cg9pL": "Просмотрено {loaded} из {total} обновлений рабочего пространства за последние {days} дней",
|
|
207
205
|
"fMPkxb": "Показать ещё",
|
|
208
206
|
"6edDJh": "Показать записи",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "twenty-bells",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "The Bell — an activity feed for Twenty: what changed in the workspace, team notes with their comments, documents, and the tasks that are overdue.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|