@protobase/cli 0.69.0 → 0.69.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.
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "0.69.0";
2
+ var version = "0.69.2";
3
3
  //#endregion
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/cli",
3
- "version": "0.69.0",
3
+ "version": "0.69.2",
4
4
  "description": "The protobase command: scaffold, doctor, dev, build and serve a Protobase project.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -43,13 +43,13 @@
43
43
  "dependencies": {
44
44
  "@better-auth/core": "^1.7.12",
45
45
  "@hono/node-server": "^2.1.3",
46
- "@protobase/config": "0.69.0",
47
- "@protobase/layout": "0.69.0",
48
- "@protobase/query": "0.69.0",
49
- "@protobase/schema": "0.69.0",
50
- "@protobase/server": "0.69.0",
51
- "@protobase/ui": "0.69.0",
52
- "@protobase/workflows": "0.69.0",
46
+ "@protobase/config": "0.69.2",
47
+ "@protobase/layout": "0.69.2",
48
+ "@protobase/query": "0.69.2",
49
+ "@protobase/schema": "0.69.2",
50
+ "@protobase/server": "0.69.2",
51
+ "@protobase/ui": "0.69.2",
52
+ "@protobase/workflows": "0.69.2",
53
53
  "@react-email/render": "^2.1.0",
54
54
  "@scalar/hono-api-reference": "^0.12.10",
55
55
  "@sentry/vite-plugin": "^11.10.0",
@@ -6,6 +6,7 @@ Buttons that change records, ask for input or run your own code, per role.
6
6
 
7
7
  - [Record actions](../reference/ui-config.md#actions-and-views-per-role): Buttons that update fields, delete, open a link, run an AI action or call your own code.
8
8
  - [Actions that ask for input](../reference/ui-config.md#actions-that-ask-for-input): An action opens a dialog for text, numbers, dates, choices or records before it runs.
9
+ - [Actions that send from someone's mailbox](../reference/ui-config.md#actions-that-send-from-someones-mailbox) (beta): An action that sends from the person's own mailbox offers to connect it right in its dialog while none is linked or its link expired.
9
10
  - [Conditional and bulk actions](../reference/ui-config.md#where-an-action-applies): Actions show, or show disabled with a reason, only where a condition holds, and run on several selected rows at once.
10
11
  - [Views per role](../reference/ui-config.md#actions-and-views-per-role): Each role can get its own view of a resource, with other columns, filters, layout and actions.
11
12
  - [Named actions](../reference/layouts.md#named-actions): An action is declared once in a view and used as a button on lists, record pages and composed pages.
@@ -11,7 +11,7 @@ Mail the app sends, the PDF documents it makes, and forms for people without an
11
11
  - [Unsubscribe headers](../reference/email.md#templates): Your emails carry header fields of their own, such as a one-click unsubscribe link for bulk mail.
12
12
  - [Attachments](../reference/email.md#attachments): A message carries files, such as an invoice's PDF, through the app's mail or someone's own mailbox.
13
13
  - [PDF documents](../reference/pdf.md#documents): PDF files such as invoices and quotes are React components with typed data, rendered from jobs, hooks or functions and attached to mail as they are.
14
- - [Mail from your own mailbox](../reference/email.md#from-someones-own-mailbox) (beta): People link their Gmail on the account page, and the app's functions send mail from it, with a clear error when the link is missing or expired.
14
+ - [Mail from your own mailbox](../reference/email.md#from-someones-own-mailbox) (beta): People link their Gmail on the account page, and the app's functions send mail from it; an action offers to connect the mailbox right there when the link is missing or expired.
15
15
  - [Public forms](../reference/forms.md#what-the-visitor-sees): A form at its own address lets people without an account create a record, such as a contact request.
16
16
  - [Bot check](../reference/forms.md#bot-check): Each submission passes a check in the browser, without a third-party service or a puzzle.
17
17
  - [Form rate limit](../reference/forms.md#rate-limit): Each client sends only a set number of submissions per minute.
@@ -24,4 +24,4 @@ Graphs of steps the server runs along their edges, written in a language a model
24
24
  - [Guarded requests for plugins](../reference/workflow-plugins.md#sending-requests) (beta): A plugin's requests reach only public addresses, follow no redirect and read at most 10 MiB.
25
25
  - [Webhook trigger](../reference/workflows.md#appwebhook) (beta): A workflow starts from a signed call to a URL of its own, limited in size and rate.
26
26
  - [Polling triggers](../reference/workflow-plugins.md#polling-and-webhook) (beta): A workflow starts once for each new item a plugin finds on a schedule, or at each time of a cron with `app:schedule`.
27
- - [Connected accounts](../reference/workflows.md#connecting-an-account) (beta): People connect their own accounts at other services through OAuth from the account page, and a workflow's steps and triggers sign in with one only once its owner turned the workflow on, with tokens encrypted and refreshed. Disconnecting deletes the token, and revokes it at a provider that has a revocation endpoint and answers.
27
+ - [Connected accounts](../reference/workflows.md#connecting-an-account) (beta): People connect their own accounts at other services through OAuth from the account page, see which workflows use each one, and MCP clients list them to bind. A workflow's steps and triggers sign in with one only once its owner turned the workflow on, with tokens encrypted and refreshed. Disconnecting deletes the token, and revokes it at a provider that has a revocation endpoint and answers.
@@ -65,4 +65,4 @@ export default defineFunction(async (request, { personalMail }) => {
65
65
  })
66
66
  ```
67
67
 
68
- The message goes out from the linked Gmail, and shows in its Sent folder. When the person linked nothing the call answers `409` with `code: "not_linked"`, and when Google no longer honours the link, as after they removed the app from their Google account, `code: "link_expired"`: point them to Account → Mailbox to connect Gmail again.
68
+ The message goes out from the linked Gmail, and shows in its Sent folder. When the person linked nothing the call answers `409` with `code: "not_linked"`, and when Google no longer honours the link, as after they removed the app from their Google account, `code: "link_expired"`. An action whose handler calls the function offers to connect Gmail right in its dialog then; mark it `mailbox: true` and it offers that before it runs, while nothing is linked ([Actions that send from someone's mailbox](../reference/ui-config.md#actions-that-send-from-someones-mailbox)).
@@ -247,7 +247,7 @@ export default defineFunction(async (request, { personalMail }) => {
247
247
  ```
248
248
 
249
249
  - **Tokens:** the link is the sign-in provider's account Better Auth keeps for the person, with its access and refresh tokens, encrypted: `auth.personalMail` needs `encryptOAuthTokens: true`, and startup stops without it. The tokens never leave the server. An access token that has expired is refreshed before the message goes out, and the new one is stored.
250
- - **When it cannot send:** `send` rejects with a `PersonalMailError` and sends nothing. Its `code` is `not_linked` when the person linked no mailbox, or signed in with Google without allowing it to send mail, and `link_expired` when the provider no longer honours the link, as when they revoked it or its refresh token expired: they connect it again under Account → Mailbox. Thrown on, it answers the function's caller as a `409` problem with that `code` (`urn:protobase:problem:personal-mail-not-linked` or `-link-expired`), so the page can offer to link the mailbox:
250
+ - **When it cannot send:** `send` rejects with a `PersonalMailError` and sends nothing. Its `code` is `not_linked` when the person linked no mailbox, or signed in with Google without allowing it to send mail, and `link_expired` when the provider no longer honours the link, as when they revoked it or its refresh token expired: they connect it again under Account → Mailbox. Thrown on, it answers the function's caller as a `409` problem with that `code` (`urn:protobase:problem:personal-mail-not-linked` or `-link-expired`), and an [action](ui-config.md#actions-that-send-from-someones-mailbox) whose handler gets it offers to connect the mailbox right there:
251
251
 
252
252
  ```json
253
253
  { "type": "urn:protobase:problem:personal-mail-link-expired", "status": 409, "code": "link_expired", "provider": "gmail" }
@@ -257,6 +257,7 @@ export default defineFunction(async (request, { personalMail }) => {
257
257
  - **Which account:** mail goes out through the account the person connected for mail last. Connecting Gmail with another Google account takes the mail scopes off the earlier one in the app's store, and signing in with the earlier one does not take the mail back; connecting it again does. When the chosen account's link fails, sending fails: it never falls back to another account. Of two accounts that both still hold the mail scopes, as when both connected before this rule came in, mail goes through the one linked to the person last, until they connect one again.
258
258
  - **Only the person:** mail goes out from a mailbox only for someone signed in as themselves, or from a [workflow step's `mailbox`](workflow-plugins.md#step-types) in a version that person turned on while signed in as themselves, and only while they may still change the workflow. Staff [signed in as them](auth.md#staff-sign-in), an outside app acting for them, and a job's own code cannot send from it; a request is refused with a `403` problem (`own-sign-in-only`), a step fails with `not_linked`, and nothing is sent. Turning the workflow off, or someone else turning a new version on, ends the first person's consent.
259
259
  - **Providers:** Gmail, through Google sign-in. A provider is added to Protobase behind the same `PersonalMailProvider` shape: its sign-in provider, the scopes linking asks for, and how it sends.
260
+ - **Sign-in is not a mailbox:** under **Mailbox**, the account page says when someone signs in with Google without having allowed it to send mail, so a linked Google sign-in is not taken for a connected mailbox.
260
261
  - <gap>The account page cannot show which account a mailbox is linked through or disconnect it because unlinking a mailbox is not implemented.</gap>
261
262
  - <gap>Mailboxes other than Gmail cannot be linked because no other mail provider is implemented.</gap>
262
263
 
@@ -97,6 +97,7 @@ In an app with [workflows](workflows.md), the endpoint also offers tools to find
97
97
  | `get_workflow` | One workflow with its whole definition and its ETag. |
98
98
  | `validate_workflow` | Checks a `definition`, or a workflow's `id` with an `edit` of its saved definition, as a save checks it, and saves nothing: `{ valid: true }`, or `{ valid: false, problems }`. A record trigger is checked against the resources and fields the person may read, so a resource they cannot read is not a resource of the app. |
99
99
  | `get_workflow_run` | A run's status, input, output, error and cause, with its steps in the order they ran, each with its state, scope, try, status, input, output, assigned variables, logs, error and cause; `state` reads one state's steps, a page of 20 by default. |
100
+ | `list_connections` | The person's own [connected accounts](workflows.md#connecting-an-account), by each connection the app's plugins declare: its key and label, and each account's `id`, scopes and `needs_sign_in`. A definition's `connections` binds an account by its `id`. It returns no token, and no one else's accounts. |
100
101
  | `create_workflow` | Creates a workflow from its `name`, `description` and `definition`, and returns its `id`, `version` and ETag. |
101
102
  | `update_workflow` | Changes a workflow's definition by an `edit` or whole, as `definition`, and its `name` or `description`, and returns its `id`, new `version` and ETag. With the `etag` that `get_workflow` gave, it fails when someone changed the workflow since; an edit without one is saved against the version it was applied to. |
102
103
  | `run_workflow` | Starts a run of a workflow's current version with an `input`, and returns its `run_id` and `status`. With `rerun_of`, a failed run's id, it [re-runs](workflows.md#re-running-a-failed-run) that run from its failed step instead, on its input. |
@@ -1139,6 +1139,16 @@ An empty answer is `null`. Before the handler runs, the dialog refuses a require
1139
1139
 
1140
1140
  A custom input's component gets the answer as `value` (`null` when empty), `onChange`, `invalid` while its answer is refused, the input's `props` and the `input` itself (`ActionInputProps`), as a [field type's](custom-components.md#field-types) editor does. Without that component the button is disabled, with the reason as its tooltip. In `/api/meta`, a user who cannot read the resource of a choice does not get the action.
1141
1141
 
1142
+ ### Actions that send from someone's mailbox
1143
+
1144
+ An `a.action` whose handler sends mail from the signed-in person's [own mailbox](email.md#from-someones-own-mailbox), such as an invoice from their Gmail, says so with `mailbox: true`. While they have linked no mailbox, its dialog offers to connect one instead of running: **Connect Gmail** starts the same sign-in as the account page's and comes back to the page they were on, where the action runs. Someone who signs in with Google without having allowed it to send mail is told that sign-in is no mailbox. The check needs a dialog, so give the action `confirm` or inputs:
1145
+
1146
+ ```ts
1147
+ a.action('send_invoice', { label: 'Send', icon: 'send', mailbox: true, confirm: 'Send the invoice from your own mailbox?' })
1148
+ ```
1149
+
1150
+ Any action whose handler fails with a function's [mailbox problem](email.md#from-someones-own-mailbox) (`not_linked` or `link_expired`) gets the same offer in place of the bare error, worded **Connect Gmail again** for an expired link: in its dialog, or as its error toast's button. A failure that is not about the mailbox shows as it is. <gap>An action that comes back from connecting a mailbox cannot reopen its dialog because keeping an open dialog in the page's address is not implemented; the person presses the button again.</gap>
1151
+
1142
1152
  ### Where an action applies
1143
1153
 
1144
1154
  `when` limits an action to the records it applies to, with the same [AIP-160](https://google.aip.dev/160) conditions a section's [`when`](#form-and-read-view) takes, over the record's own fields: `status = 'running'`, `status != 'stopped' AND locked = false`. On a record it does not hold for, the button is left out, or, with `otherwise: 'disable'`, shown disabled with `reason` as its tooltip. `variant: 'danger'` draws the button in red, as Delete is, and its question's button too:
@@ -223,9 +223,9 @@ A save also refuses a Task that leaves out an argument its step type needs, such
223
223
  Each connected account belongs to the person who connected it, and only that person can turn on a workflow that uses it.
224
224
  :::
225
225
 
226
- A trigger or step type that signs in to another service, such as a mailbox, uses an account that someone connected. Each person connects their own accounts on the **Connections** section of the account page's Profile tab. Connecting takes them to the provider and back. The app keeps the account's tokens on the server, encrypted, and refreshes them when they expire. **Disconnect** always deletes the token, so no step or trigger can use it again. When the connection has a revocation endpoint, it first asks the provider to revoke the token. A provider that does not answer or refuses keeps the token valid on its side, and the answer's `revoked` is false. A trigger that uses the account stops as `needs_sign_in`. Connecting again makes a new account with a new id: bind it in the workflow's `connections` and turn the workflow on again. Revoking at Google ends everything the account allowed the app, the sign-in and a linked mailbox included, and the dialog says so.
226
+ A trigger or step type that signs in to another service, such as a mailbox, uses an account that someone connected. Each person connects their own accounts on the **Connections** section of the account page's Profile tab. Connecting takes them to the provider and back. Under each account, the section links each workflow that uses it: those the person turned on, whose enabled version binds the account, while they may still change them. The app keeps the account's tokens on the server, encrypted, and refreshes them when they expire. **Disconnect** always deletes the token, so no step or trigger can use it again. When the connection has a revocation endpoint, it first asks the provider to revoke the token. A provider that does not answer or refuses keeps the token valid on its side, and the answer's `revoked` is false. A trigger that uses the account stops as `needs_sign_in`. Connecting again makes a new account with a new id: bind it in the workflow's `connections` and turn the workflow on again. Revoking at Google ends everything the account allowed the app, the sign-in and a linked mailbox included, and the dialog says so.
227
227
 
228
- A workflow names the account each of its connections uses in its definition's `connections`, by `<plugin>:<connection>` and the account's id, which the Connections section shows:
228
+ A workflow names the account each of its connections uses in its definition's `connections`, by `<plugin>:<connection>` and the account's id, which the Connections section shows, as does `list_connections` [over MCP](mcp.md#workflows):
229
229
 
230
230
  ```json
231
231
  { "trigger": { "gmail:new_mail": {} }, "connections": { "gmail:google": "<account id>" }, "stateMachine": { ... } }