@nebulr-group/bridge-cli 0.6.0-beta.2 → 0.6.0-beta.4
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 +8 -2
- package/dist/commands/flag.command.d.ts.map +1 -1
- package/dist/commands/flag.command.js +19 -6
- package/dist/commands/flag.command.js.map +1 -1
- package/dist/commands/guide.command.d.ts +6 -0
- package/dist/commands/guide.command.d.ts.map +1 -1
- package/dist/commands/guide.command.js +101 -1
- package/dist/commands/guide.command.js.map +1 -1
- package/dist/commands/journeys.d.ts +2 -1
- package/dist/commands/journeys.d.ts.map +1 -1
- package/dist/commands/journeys.js +15 -1
- package/dist/commands/journeys.js.map +1 -1
- package/dist/commands/plan.command.d.ts +39 -0
- package/dist/commands/plan.command.d.ts.map +1 -1
- package/dist/commands/plan.command.js +134 -10
- package/dist/commands/plan.command.js.map +1 -1
- package/dist/commands/role.command.d.ts.map +1 -1
- package/dist/commands/role.command.js +2 -1
- package/dist/commands/role.command.js.map +1 -1
- package/dist/commands/tenant.command.d.ts.map +1 -1
- package/dist/commands/tenant.command.js +12 -5
- package/dist/commands/tenant.command.js.map +1 -1
- package/dist/commands/token.command.d.ts.map +1 -1
- package/dist/commands/token.command.js +6 -0
- package/dist/commands/token.command.js.map +1 -1
- package/dist/commands/user.command.d.ts.map +1 -1
- package/dist/commands/user.command.js +6 -0
- package/dist/commands/user.command.js.map +1 -1
- package/dist/flag-coherence.d.ts +42 -0
- package/dist/flag-coherence.d.ts.map +1 -0
- package/dist/flag-coherence.js +103 -0
- package/dist/flag-coherence.js.map +1 -0
- package/dist/preview.d.ts +9 -0
- package/dist/preview.d.ts.map +1 -0
- package/dist/preview.js +63 -0
- package/dist/preview.js.map +1 -0
- package/dist/prompts/auth-master-integration-prompt.md +7 -5
- package/dist/prompts/billing/master.md +35 -18
- package/dist/prompts/decisions/feature-control.md +105 -0
- package/dist/prompts/decisions/going-live.md +59 -0
- package/dist/prompts/decisions/login.md +68 -0
- package/dist/prompts/decisions/look-and-feel.md +62 -0
- package/dist/prompts/decisions/payments.md +81 -0
- package/dist/prompts/decisions/roles.md +72 -0
- package/dist/prompts/decisions/teams.md +56 -0
- package/dist/prompts/flags/master.md +8 -8
- package/dist/prompts/integration-success.md +4 -5
- package/dist/prompts/mcp-tools.json +77 -0
- package/dist/prompts/mechanisms.md +284 -0
- package/dist/prompts/orientation.md +40 -0
- package/dist/shape.d.ts +25 -0
- package/dist/shape.d.ts.map +1 -0
- package/dist/shape.js +74 -0
- package/dist/shape.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Look and feel — decision guide
|
|
2
|
+
|
|
3
|
+
How Bridge's pages and emails look to the developer's users: the sign-in pages, the subscription pages, and the emails Bridge sends in the app's name.
|
|
4
|
+
|
|
5
|
+
> Other decision guides: `bridge guide decision <name>` (MCP: resource `bridge://guides/decisions/<name>`). The customisation levels in full, with every design token: `bridge guide mechanisms`.
|
|
6
|
+
|
|
7
|
+
## Two places pages can live
|
|
8
|
+
|
|
9
|
+
- **Hosted:** Bridge serves sign-in on its own address. Styled with the app's **branding** (colours, font, corner radius), which Bridge stores.
|
|
10
|
+
- **In the app:** the same pages render inside the developer's own layout, on their address. Styled with CSS design tokens (`--bridge-*`) in the app's own stylesheet.
|
|
11
|
+
|
|
12
|
+
Both use the same single route file; one setting switches sign-in into the app (`loginRoute: '/auth/login'` in SvelteKit's `bridgeBootstrap()`). Subscription pages always render in the app.
|
|
13
|
+
|
|
14
|
+
## Start from what is there
|
|
15
|
+
|
|
16
|
+
| Read | MCP | CLI |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| Hosted-page branding | `get_branding` | `bridge branding get` |
|
|
19
|
+
| App name, and the email sender | `get_app` | `bridge app get` |
|
|
20
|
+
|
|
21
|
+
## Ask the developer
|
|
22
|
+
|
|
23
|
+
Product questions. Ask, wait, never guess.
|
|
24
|
+
|
|
25
|
+
1. **Should sign-in look like part of the app, on the app's own address, or is Bridge's hosted page fine?**
|
|
26
|
+
2. **What are the brand colours and font?** Ask for exact values or point at their existing stylesheet; do not pick colours for them.
|
|
27
|
+
3. **What name should users see** on sign-in pages and in emails?
|
|
28
|
+
4. **Which address and sender name should emails come from?** (Invitations, password resets, magic links.)
|
|
29
|
+
5. **Beyond colours, should sign-in pages say anything of their own?** A different heading, a frame around the form, a page they design themselves.
|
|
30
|
+
|
|
31
|
+
## Decide yourself
|
|
32
|
+
|
|
33
|
+
Climb only as far as the answers require, and say which level you stopped at.
|
|
34
|
+
|
|
35
|
+
- **Level 0, nothing:** in-app pages already sit inside the app's layout, so its header and navigation surround them. Reason: often this alone is enough.
|
|
36
|
+
- **Level 1, tokens:** set `--bridge-primary`, `--bridge-border-radius` and friends on `:root`. Reason: tokens are the supported contract; ordinary CSS already wins over Bridge's defaults, so there is never a need for `!important`.
|
|
37
|
+
- **Level 2, frame and heading:** pass the `frame` and `heading` snippets to the sign-in routes component. Reason: it changes every page at once and keeps each page's own sub-steps.
|
|
38
|
+
- **Level 3, take over one page:** create that page's own route file (e.g. `src/routes/auth/login/+page.svelte`); every other page keeps working.
|
|
39
|
+
- **Level 4, headless:** build everything on `getBridgeAuth()`. Only when the developer asks for it.
|
|
40
|
+
- **Never copy Bridge's components into the app to restyle them.** Reason: the copy stops receiving fixes.
|
|
41
|
+
- **When both hosted pages and in-app pages are used, give them the same colours.** Reason: a person moving between them should not notice.
|
|
42
|
+
- **A new sender address must be confirmed.** Bridge emails that address a link; until it is clicked, mail keeps coming from Bridge's default sender. Tell the developer to look for it.
|
|
43
|
+
|
|
44
|
+
## Do it
|
|
45
|
+
|
|
46
|
+
| Step | MCP | CLI |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| Hosted-page colours, font, corners | `update_branding` | `bridge branding update --primary-btn-bg '#1a1a2e' --font-family Inter` |
|
|
49
|
+
| Name users see | `update_app` (`name`) | `bridge app update --name "Acme"` |
|
|
50
|
+
| Email sender | `setup_communication` | `bridge setup communication --from-address no-reply@acme.com --from-name Acme` |
|
|
51
|
+
| In-app pages, tokens, snippets | `get_integration_guide` (topic `branding`, then `auth`) | `bridge guide svelte branding`, `bridge guide mechanisms` |
|
|
52
|
+
|
|
53
|
+
## Prove it
|
|
54
|
+
|
|
55
|
+
1. Open the sign-in page and the subscription page in the running app (or the hosted page): colours and font match what the developer gave.
|
|
56
|
+
2. Trigger a password reset for a test user: the email arrives from the agreed sender, in the app's name, and its link opens the app, not a Bridge page.
|
|
57
|
+
|
|
58
|
+
## Where this connects
|
|
59
|
+
|
|
60
|
+
- **Login:** hosted or in-app sign-in is the same switch.
|
|
61
|
+
- **Payments:** the subscription and plan-choice pages use the same tokens.
|
|
62
|
+
- **Going live:** a verified sender address before real users arrive.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Payments and plans — decision guide
|
|
2
|
+
|
|
3
|
+
What customers pay, what each plan includes, and what happens when they reach a limit. A **plan** belongs to a workspace, not to a person. It carries the price, the trial and the limits ("3 projects on Free, 50 on Pro"). Payments run through the developer's own Stripe account.
|
|
4
|
+
|
|
5
|
+
> Other decision guides: `bridge guide decision <name>` (MCP: resource `bridge://guides/decisions/<name>`). The full model behind limits and upgrade prompts: `bridge guide mechanisms`.
|
|
6
|
+
|
|
7
|
+
## Start from what is there
|
|
8
|
+
|
|
9
|
+
| Read | MCP | CLI |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| Plans with prices and limits | `list_plans` | `bridge plan list` |
|
|
12
|
+
| Every metric the plans already limit, with its kind | `list_plan_quotas` | `bridge plan quota list` |
|
|
13
|
+
| Whether Stripe is connected and its webhook works | `get_stripe_status` | `bridge stripe status` |
|
|
14
|
+
| Whether new customers must pick a plan first (`paymentsAutoRedirect`) | `get_app` | `bridge app get` |
|
|
15
|
+
|
|
16
|
+
## Ask the developer
|
|
17
|
+
|
|
18
|
+
Product questions. Ask, wait, never guess. Then show the answer back as tables (plans, prices, limits) and create nothing until the developer confirms them.
|
|
19
|
+
|
|
20
|
+
1. **Which plans, and what are they called?** Is there a free one?
|
|
21
|
+
2. **What does each paid plan cost, in which currency, and monthly, yearly or both?**
|
|
22
|
+
3. **Is there a free trial, and how many days?**
|
|
23
|
+
4. **What does each plan limit, and how much?** In their words: "2 tickets on Free, 100 on Pro", "10 seats", "1,000 exports a month".
|
|
24
|
+
5. **For each limit: at the limit, block, or keep going and charge per extra unit?** If they charge, how much per unit?
|
|
25
|
+
6. **Which features are only on some plans?** ("Analytics on Pro only.")
|
|
26
|
+
7. **Do they want a welcome page for new customers** where they choose a plan? Offer it; create it only if they say yes.
|
|
27
|
+
|
|
28
|
+
## Decide yourself
|
|
29
|
+
|
|
30
|
+
- **Plan keys short and lower-case: `free`, `pro`, `team`.** Reason: the key is permanent and is what code, flag rules and tokens use; the name can change any time.
|
|
31
|
+
- **Monthly and yearly prices of one tier are one plan with two prices,** never `pro-monthly` and `pro-yearly`. Reason: the subscription page then shows one plan with a monthly/yearly switch.
|
|
32
|
+
- **A free plan gets a price of 0.** Reason: a plan with no price cannot be assigned to a workspace.
|
|
33
|
+
- **Counter or gauge?** If deleting it frees room, it's a gauge and your app counts it; if it happened, it's a counter and Bridge counts it. Exports, AI completions and emails are counters (reset each billing period). Tickets, projects and stored files are gauges (never reset). Decide from that sentence and say which you chose.
|
|
34
|
+
- **Seats are the built-in `users` metric, a gauge.** Reason: Bridge counts workspace members itself.
|
|
35
|
+
- **Reuse metric names the app already has** (`list_plan_quotas`) before inventing new ones. Reason: two names for one thing split the count.
|
|
36
|
+
- **Connect Stripe with test keys first; live keys only when going live.** Reason: nobody is charged while you build. Use `setup_payments` / `bridge setup payments`: it stores the keys and switches payments on in one step.
|
|
37
|
+
- **Connect Stripe before adding paid prices.** Reason: each price is created in Stripe as it is saved.
|
|
38
|
+
- **Leave "customers must pick a plan" on** (it is on by default) unless the developer says otherwise. Reason: a new workspace then always has a plan, so every limit and feature check has something to read.
|
|
39
|
+
- **One plan, one call:** `apply_plan` / `bridge plan apply` sets name, trial, prices and limits together. Reason: a plan is never left half set up.
|
|
40
|
+
|
|
41
|
+
## How a hard limit is enforced, end to end
|
|
42
|
+
|
|
43
|
+
Tell the developer this plainly; it is where integrations go wrong.
|
|
44
|
+
|
|
45
|
+
1. **The plan holds the number.** A limit is a quota on the plan: metric, limit, `hard`, and `counter` or `gauge`.
|
|
46
|
+
2. **The backend enforces it, and only the backend.** In NestJS one decorator on the handler that creates the thing: `@RequireQuota('exports')`. Before the handler runs, a workspace at its limit gets `402` with `{ code: 'QUOTA_EXCEEDED', metric, used, limit, fix }` and the handler never runs. After a `2xx` answer, Bridge records one use. Nothing else to wire: no usage call, no counter table. If Bridge cannot answer, the request is refused with `503`, never let through.
|
|
47
|
+
3. **Gauges send the app's own count.** `@RequireQuota('tickets', { current })` on create and `@SyncQuota('tickets', { current })` on delete, where `current` returns the app's count from its own database. There is no decrement.
|
|
48
|
+
4. **Seats need no count at all.** `@RequireQuota('users')` on the invite handler checks the seat limit.
|
|
49
|
+
5. **The frontend only explains the refusal.** With no code at all, the upgrade dialog opens on a `402`, names the limit, and links to the subscription page. A member who cannot manage billing is told to ask the owner. Disabling a button before the click (`<QuotaGate>`) or showing "8 of 10" (`useQuota`) is optional.
|
|
50
|
+
6. **A frontend alone cannot enforce a limit.** Anyone can call the API directly. An app with no backend can report usage from the browser, but that is self-reported: Bridge counts what the client says.
|
|
51
|
+
7. **`metered` never blocks.** Past the included amount it bills per unit through Stripe.
|
|
52
|
+
8. **A plan feature is a hard limit nothing counts.** `analytics` with limit 1 on `pro` makes `@RequireEntitlement('analytics')` and `<Entitled to="analytics">` true on Pro and false elsewhere. Never put `@RequireEntitlement` and `@RequireQuota` on the same name: at the cap the entitlement answers `403` first and the upgrade dialog never opens.
|
|
53
|
+
9. **The plan-choice page** (`/subscription/plan`) appears only when the app has plans and "customers must pick a plan" is on. Turn it off with `update_app` (`paymentsAutoRedirect: false`) / `bridge app update --payments-auto-redirect false`.
|
|
54
|
+
|
|
55
|
+
Whether a paid feature is a plan feature or a flag with a plan rule is decided in the **feature-control** guide.
|
|
56
|
+
|
|
57
|
+
## Do it
|
|
58
|
+
|
|
59
|
+
| Step | MCP | CLI |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| Connect Stripe (keys from the developer) | `setup_payments` | `bridge setup payments --stripe-key sk_test_… --stripe-public-key pk_test_…` |
|
|
62
|
+
| Set up or reshape one plan in one call | `apply_plan` | `bridge plan apply --spec @plan.json` |
|
|
63
|
+
| Or step by step: create a plan with its first price | `create_plan` | `bridge plan create --key pro --name Pro --amount 29 --interval month` |
|
|
64
|
+
| Add another price | `set_plan_price` | `bridge plan price set pro --amount 290 --interval year` |
|
|
65
|
+
| Add a limit | `set_plan_quota` | `bridge plan quota set pro --metric tickets --limit 100 --policy hard --kind gauge` |
|
|
66
|
+
| Turn off the forced plan choice | `update_app` | `bridge app update --payments-auto-redirect false` |
|
|
67
|
+
| Code for the framework | `get_integration_guide` (topic `billing`) | `bridge guide billing --framework svelte`, `bridge guide billing --framework nestjs` |
|
|
68
|
+
|
|
69
|
+
## Prove it
|
|
70
|
+
|
|
71
|
+
1. `list_plans` / `bridge plan list` matches the tables the developer confirmed; a plan with monthly and yearly pricing is one plan.
|
|
72
|
+
2. `get_stripe_status` / `bridge stripe status`: connected, webhook working.
|
|
73
|
+
3. At the limit, send the decorated request with curl: it answers `402 QUOTA_EXCEEDED`. In the app, the upgrade dialog opens and names the limit.
|
|
74
|
+
4. A paid plan opens Stripe Checkout and returns to the subscription page with the new plan.
|
|
75
|
+
|
|
76
|
+
## Where this connects
|
|
77
|
+
|
|
78
|
+
- **Feature control:** features that depend on the plan.
|
|
79
|
+
- **Teams:** seats, and the plan belonging to the workspace.
|
|
80
|
+
- **Roles:** who may change the plan.
|
|
81
|
+
- **Going live:** live Stripe keys.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Roles and permissions — decision guide
|
|
2
|
+
|
|
3
|
+
Who may do what inside a customer's workspace. A **role** (Owner, Admin, Editor…) is a named bundle of **privileges** (`USER_WRITE`, `INVOICE_APPROVE`…). Every signed-in person has one role per workspace, and their token carries its role key and privileges, so the backend can check them on every request.
|
|
4
|
+
|
|
5
|
+
> Other decision guides: `bridge guide decision <name>` (MCP: resource `bridge://guides/decisions/<name>`). How the pieces work underneath: `bridge guide mechanisms`.
|
|
6
|
+
|
|
7
|
+
## Which tool answers which question
|
|
8
|
+
|
|
9
|
+
Three different questions, three different tools. Keep them apart.
|
|
10
|
+
|
|
11
|
+
| The question | Answered by | Guide |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| Who, inside a workspace, may do this? | a **role** and its privileges | this one |
|
|
14
|
+
| Is this feature on for them at all? | a **flag** | feature-control |
|
|
15
|
+
| How much do they get (projects, seats, exports)? | the **plan** | payments |
|
|
16
|
+
|
|
17
|
+
A role never stands in for a plan ("Pro users are Admins") and a plan never stands in for a role.
|
|
18
|
+
|
|
19
|
+
## Start from what is there
|
|
20
|
+
|
|
21
|
+
| Read | MCP | CLI |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| Roles, their privileges, and which is the default | `list_roles` | `bridge role list` |
|
|
24
|
+
| One role in full | `get_role` | — |
|
|
25
|
+
| Privileges that exist | `list_roles` (the `privileges` map) | `bridge privilege list` |
|
|
26
|
+
|
|
27
|
+
A new app has two roles: **Owner** (`OWNER`, everything, including permanent deletes) and **Admin** (`ADMIN`, manage people, read workspace settings). **Owner is the default role**, which means anyone invited without a role becomes an Owner.
|
|
28
|
+
|
|
29
|
+
## Ask the developer
|
|
30
|
+
|
|
31
|
+
Product questions. Ask, wait, never guess.
|
|
32
|
+
|
|
33
|
+
1. **What kinds of people use one customer's workspace?** For example: the person who pays, people who manage the team, people who do the work, people who only look.
|
|
34
|
+
2. **For each kind, what may they do that others may not?** Ask in the app's own verbs: approve an invoice, delete a project, invite a colleague, change billing.
|
|
35
|
+
3. **Which role should a newly invited person get?** Owner is rarely the right answer.
|
|
36
|
+
4. **Who may manage the subscription and billing?**
|
|
37
|
+
|
|
38
|
+
## Decide yourself
|
|
39
|
+
|
|
40
|
+
- **Name privileges after actions, in capitals: `PROJECT_DELETE`, `INVOICE_APPROVE`.** Reason: they read like the existing ones (`USER_READ`, `TENANT_WRITE`) and say exactly what they allow.
|
|
41
|
+
- **Role keys in capitals too: `EDITOR`, `VIEWER`.** Reason: the token carries the key exactly as written, and a flag rule on `user.role` compares it case for case.
|
|
42
|
+
- **In code, check a privilege, not a role name.** Reason: roles get reshaped as the product grows; the privilege on a handler stays true.
|
|
43
|
+
- **Keep Owner as the one role with everything.** Never strip it: it is the only role that can delete, and every workspace needs someone who can.
|
|
44
|
+
- **Make the lowest sensible role the default** by creating it with the default setting on. Reason: a forgotten role on an invitation then gives too little, not too much.
|
|
45
|
+
- **When changing a role's privileges, send the full list.** Reason: the update replaces the list, and anything left out is revoked.
|
|
46
|
+
- **Enforce on the backend; hide buttons in the frontend only as a courtesy.** Reason: anyone can call the API directly.
|
|
47
|
+
|
|
48
|
+
## Do it
|
|
49
|
+
|
|
50
|
+
| Step | MCP | CLI |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| Add a privilege | — | `bridge privilege create --key PROJECT_DELETE --description …` |
|
|
53
|
+
| Create a role | `create_role` | `bridge role create --key EDITOR --name Editor --privileges PROJECT_READ,PROJECT_WRITE` |
|
|
54
|
+
| Create it as the new default | `create_role` (`isDefault: true`) | `bridge role create --key MEMBER --name Member --privileges … --is-default` |
|
|
55
|
+
| Change a role's privileges | `update_role` (full list) | `bridge role update --key EDITOR --privileges …` (full list) |
|
|
56
|
+
| Give a person a role | `update_user` | `bridge user update --email … --role EDITOR --tenant-id …` |
|
|
57
|
+
| Code for the framework | `get_integration_guide` (topic `team`) | `bridge guide nestjs` (backend checks), `bridge guide svelte team` |
|
|
58
|
+
|
|
59
|
+
On the NestJS backend the check is a decorator on the handler: `@RequirePrivilege('PROJECT_DELETE')`, or `@RequireRole('ADMIN')` when the developer really means a role.
|
|
60
|
+
|
|
61
|
+
Setting the default on one role clears it from the previous default.
|
|
62
|
+
|
|
63
|
+
## Prove it
|
|
64
|
+
|
|
65
|
+
1. `list_roles` / `bridge role list` shows each role with exactly the privileges agreed, and the default where the developer wanted it.
|
|
66
|
+
2. Call a protected backend route as a user whose role lacks the privilege: it must answer 403. Then with one who has it: it must succeed.
|
|
67
|
+
|
|
68
|
+
## Where this connects
|
|
69
|
+
|
|
70
|
+
- **Teams:** the role an invited person gets.
|
|
71
|
+
- **Feature control:** a flag can target `user.role`, but "may this person do it" is still a privilege check on the backend.
|
|
72
|
+
- **Payments:** who may change the plan.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Teams and workspaces — decision guide
|
|
2
|
+
|
|
3
|
+
How customers are grouped in the developer's app. In Bridge every signed-in person belongs to a **workspace** (the API and the CLI call it a tenant). A workspace is one customer: it holds the people, their roles, and the plan the customer pays for.
|
|
4
|
+
|
|
5
|
+
> Other decision guides: `bridge guide decision <name>` (MCP: resource `bridge://guides/decisions/<name>`). How the pieces work underneath: `bridge guide mechanisms`.
|
|
6
|
+
|
|
7
|
+
## Start from what is there
|
|
8
|
+
|
|
9
|
+
| Read | MCP | CLI |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| Whether people can create their own workspace (`tenantSelfSignup`) | `get_app` | `bridge app get` |
|
|
12
|
+
| Existing workspaces | `list_tenants` | `bridge tenant list` |
|
|
13
|
+
| The people in one workspace | `list_users` | `bridge user list --tenant-id <id>` |
|
|
14
|
+
| Roles an invited person can get | `list_roles` | `bridge role list` |
|
|
15
|
+
|
|
16
|
+
## Ask the developer
|
|
17
|
+
|
|
18
|
+
Product questions. Ask, wait, never guess.
|
|
19
|
+
|
|
20
|
+
1. **Is the app for single people or for teams?** With teams, a customer invites colleagues into a shared workspace. For single people, each person simply has a workspace of their own and never sees the word.
|
|
21
|
+
2. **How does someone get in the first time?** Either they sign up and get a new workspace of their own, or they can only join by invitation from someone already inside.
|
|
22
|
+
3. **When someone is invited, which role should they get?** See the **roles** guide: on a new app the default role is Owner, so an invitee without a role becomes an Owner.
|
|
23
|
+
4. **Does the number of people per workspace depend on the plan?** ("1 user on Free, 10 on Pro.") If yes, that is a seat limit: see the **payments** guide.
|
|
24
|
+
5. **Can one person belong to several workspaces?** Bridge supports it and shows a workspace picker at sign-in; the question is whether the developer wants to offer it in their product.
|
|
25
|
+
|
|
26
|
+
## Decide yourself
|
|
27
|
+
|
|
28
|
+
- **Self-signup on for a product people try on their own; off for invitation-only products.** Take it from the answer to question 2 and set `tenantSelfSignup` to match. Reason: with it off, a stranger who signs up has nowhere to land.
|
|
29
|
+
- **Use Bridge's team panel for inviting and managing people** (`TeamManagementPanel` in SvelteKit) rather than a page of your own. Reason: it already handles invitations, role changes and removal against the right permissions.
|
|
30
|
+
- **Seats are the built-in `users` limit.** Reason: Bridge counts workspace members itself, so there is nothing for the app to count or report.
|
|
31
|
+
- **Never create workspaces from the app's own code for sign-ups.** Reason: Bridge creates one at signup when self-signup is on; creating them elsewhere makes duplicates.
|
|
32
|
+
- **Create workspaces by hand only for setup and support** (an existing customer you are migrating, or a demo).
|
|
33
|
+
|
|
34
|
+
## Do it
|
|
35
|
+
|
|
36
|
+
| Step | MCP | CLI |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| Allow or stop self-signup | `update_app` (`tenantSelfSignup`) | `bridge app update --tenant-self-signup true` |
|
|
39
|
+
| Create a workspace by hand | `create_tenant` | `bridge tenant create --name … --owner-email …` |
|
|
40
|
+
| Invite someone into a workspace | `invite_user` | `bridge user invite --email … --role … --tenant-id …` |
|
|
41
|
+
| Change someone's role | `update_user` | `bridge user update --email … --role … --tenant-id …` |
|
|
42
|
+
| Limit seats per plan | `set_plan_quota` (metric `users`, kind `gauge`) | `bridge plan quota set pro --metric users --limit 10 --policy hard --kind gauge` |
|
|
43
|
+
| Code for the framework | `get_integration_guide` (topic `team`) | `bridge guide svelte team` |
|
|
44
|
+
|
|
45
|
+
Pass a role key on every invitation you make on the developer's behalf; leaving it out gives the app's default role.
|
|
46
|
+
|
|
47
|
+
## Prove it
|
|
48
|
+
|
|
49
|
+
1. Create a test user (`create_test_user` / `bridge test-user create`) and sign in (`verify_login` / `bridge test-user verify`): the response names the workspace.
|
|
50
|
+
2. Invite a second address into that workspace and check it appears in `list_users` / `bridge user list` with the role you meant.
|
|
51
|
+
|
|
52
|
+
## Where this connects
|
|
53
|
+
|
|
54
|
+
- **Roles:** what each person in a workspace may do.
|
|
55
|
+
- **Payments:** a plan belongs to the workspace, not to a person; seats are a plan limit.
|
|
56
|
+
- **Login:** whether sign-up is open at all.
|
|
@@ -19,7 +19,7 @@ Each plugin repo owns its own framework-specific prompt under `mcp/feature-flags
|
|
|
19
19
|
|
|
20
20
|
`bridge guide <framework> flags` is an equivalent route — both call the same plugin-repo prompt.
|
|
21
21
|
|
|
22
|
-
If the CLI returns `HTTP 404 — file may not exist yet in the plugin repo`, the plugin hasn't published its
|
|
22
|
+
If the CLI returns `HTTP 404 — file may not exist yet in the plugin repo`, the plugin hasn't published its flags guide yet. Tell the user that's the blocker and fall back to `@nebulr-group/bridge-auth-core` directly (it's framework-agnostic and stable). Do NOT improvise an inline integration in this master prompt — the plugin guide is the canonical handover.
|
|
23
23
|
|
|
24
24
|
## Step 0 — Authenticate
|
|
25
25
|
|
|
@@ -63,7 +63,7 @@ Track as you work:
|
|
|
63
63
|
|
|
64
64
|
## Mental model (1-minute version)
|
|
65
65
|
|
|
66
|
-
A flag
|
|
66
|
+
A flag has:
|
|
67
67
|
|
|
68
68
|
| Field | Possible values |
|
|
69
69
|
|---|---|
|
|
@@ -75,7 +75,7 @@ A flag in has:
|
|
|
75
75
|
|
|
76
76
|
Branches are first-match-wins; conditions within a branch AND together; `rolloutPct` is rule-level (0-100) and requires `identity` on the eval context.
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
Every plugin wraps the same evaluation, and the return type is inferred from the default value, so no casts. On SvelteKit it is `useFlag(key, defaultValue)` in script and `<FeatureFlag key defaultValue>` in markup, both from `@nebulr-group/bridge-svelte/flags`; a whole route is gated by a `featureFlag` rule in `bridgeBootstrap()`.
|
|
79
79
|
|
|
80
80
|
## Step 1 — Discover projects
|
|
81
81
|
|
|
@@ -95,7 +95,7 @@ Scan the current directory and its immediate subdirectories for `package.json` f
|
|
|
95
95
|
- `@nestjs/core` → **NestJS** (backend)
|
|
96
96
|
- `express` (without `@nestjs/core`) → **Express** (backend)
|
|
97
97
|
|
|
98
|
-
3. **Existing Bridge plugin?** Check for `@nebulr-group/bridge-<framework>` — if present, the project may already have auth or other Bridge surfaces. Flags can co-exist with auth in the same plugin; the per-framework guide will tell you whether
|
|
98
|
+
3. **Existing Bridge plugin?** Check for `@nebulr-group/bridge-<framework>` — if present, the project may already have auth or other Bridge surfaces. Flags can co-exist with auth in the same plugin; the per-framework guide will tell you whether its bootstrap is already in place. On SvelteKit, an app that already calls `bridgeBootstrap()` and renders `<BridgeBootstrap>` has flags running — there is nothing to install or initialise.
|
|
99
99
|
|
|
100
100
|
4. **Existing flag system?** Look for `launchdarkly-*`, `@unleash/*`, `posthog-js`, `growthbook-*` — warn the user that Bridge flags will run in parallel unless they migrate.
|
|
101
101
|
|
|
@@ -167,8 +167,8 @@ Where `<name>` is one of: `svelte`, `react`, `nextjs`, `angular`, `nestjs`, `exp
|
|
|
167
167
|
|
|
168
168
|
**Every per-framework guide covers this contract:**
|
|
169
169
|
|
|
170
|
-
- **Install** — the right package + version + import path (
|
|
171
|
-
- **Bootstrap** — provider / module / factory setup,
|
|
170
|
+
- **Install** — the right package + version + import path (when the plugin is already installed for auth, nothing more to install)
|
|
171
|
+
- **Bootstrap** — provider / module / factory setup where the framework needs one (SvelteKit with Bridge Auth needs none: `<BridgeBootstrap>` starts flags, reading the app id from `.env`)
|
|
172
172
|
- **Eval context** — idiomatic placement for `identity` and `attributes`, including how to wire the `identitySource` chosen in Step 2
|
|
173
173
|
- **Reading a flag** — the framework wrapper (`useFlag`, `FeatureFlag`, `signal`, decorator) and the synchronous `evaluateFlag` for non-reactive contexts
|
|
174
174
|
- **Advanced attribute wiring** — the unified `bridge.attributes` write surface and realtime event subscription via `bridge.events.handle`
|
|
@@ -177,14 +177,14 @@ Where `<name>` is one of: `svelte`, `react`, `nextjs`, `angular`, `nestjs`, `exp
|
|
|
177
177
|
|
|
178
178
|
**Pass these values from this master to the per-framework prompt:**
|
|
179
179
|
- `appId` — from Step 3
|
|
180
|
-
- `baseUrl` —
|
|
180
|
+
- `baseUrl` — production by default; a stage, local or self-hosted app must set it (SvelteKit reads `VITE_BRIDGE_API_BASE_URL` itself)
|
|
181
181
|
- `packageManager` — from Step 1
|
|
182
182
|
- `mode` — `frontend` (browser projects) or `backend` (NestJS, Express, Next.js server)
|
|
183
183
|
- `identitySource` — from Step 2
|
|
184
184
|
|
|
185
185
|
**Order:** Frontend first, then backend.
|
|
186
186
|
|
|
187
|
-
If the CLI returns `HTTP 404`, the plugin hasn't published its
|
|
187
|
+
If the CLI returns `HTTP 404`, the plugin hasn't published its flags guide yet. Tell the user that's the blocker and fall back to `@nebulr-group/bridge-auth-core` directly — `new BridgeFlags({ appId, baseUrl, mode })` + `bridge.setContext({...})` + `bridge.flag(...)`. The SDK shape is identical across frameworks; only the bindings differ.
|
|
188
188
|
|
|
189
189
|
## Step 5 — Verify
|
|
190
190
|
|
|
@@ -94,14 +94,13 @@ PLACEHOLDER: what-i-changed
|
|
|
94
94
|
|
|
95
95
|
Frontend (my-saas-app):
|
|
96
96
|
✅ @nebulr-group/bridge-svelte installed
|
|
97
|
-
✅ src/routes/+layout.ts
|
|
98
|
-
✅ src/routes/+layout.svelte
|
|
99
|
-
✅
|
|
97
|
+
✅ src/routes/+layout.ts — export const load = bridgeBootstrap({ rules })
|
|
98
|
+
✅ src/routes/+layout.svelte — app wrapped in <BridgeBootstrap>
|
|
99
|
+
✅ src/routes/auth/[...bridge]/+page.svelte — <BridgeAuthRoutes /> serves every sign-in page
|
|
100
100
|
✅ .env configured with VITE_BRIDGE_APP_ID=abc123
|
|
101
101
|
✅ Bridge app configured for http://localhost:5175
|
|
102
102
|
|
|
103
103
|
Backend (my-saas-api):
|
|
104
104
|
✅ @nebulr-group/bridge-nestjs installed
|
|
105
|
-
✅ BridgeModule
|
|
106
|
-
✅ Global JWT guard applied
|
|
105
|
+
✅ AppModule — BridgeModule.forRoot({ guard: { global: true } }), every route needs a signed-in user
|
|
107
106
|
✅ .env configured with BRIDGE_APP_ID=abc123
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": [
|
|
3
|
+
"The names of the Bridge MCP server's tools, and nothing else. The bundled guides are tested against this list: a guide that names a tool not in it fails src/__tests__/guide-decisions.test.ts.",
|
|
4
|
+
"Source: the `bridgeTools` registry in bridge-api, microservices/account/nebulr-api/mcp/tools/register.ts.",
|
|
5
|
+
"Regenerate from the thebridge-platform root when a tool is added, renamed or removed: grep -hoE \"^ {2,4}name: '[a-z]+(_[a-z]+)+',\" bridge-api/microservices/account/nebulr-api/mcp/tools/*.ts | sed -E \"s/.*'(.*)'.*/\\1/\" | sort -u",
|
|
6
|
+
"The count must equal the number of entries in `bridgeTools` (66 at the source below). The twin of this check in bridge-api reads the live registry instead of this copy."
|
|
7
|
+
],
|
|
8
|
+
"source": "bridge-api feature/mcp-journey 689899e",
|
|
9
|
+
"tools": [
|
|
10
|
+
"add_redirect_uri",
|
|
11
|
+
"apply_plan",
|
|
12
|
+
"clear_flag_schedule",
|
|
13
|
+
"connect_stripe",
|
|
14
|
+
"create_api_token",
|
|
15
|
+
"create_app",
|
|
16
|
+
"create_feature_flag",
|
|
17
|
+
"create_plan",
|
|
18
|
+
"create_role",
|
|
19
|
+
"create_tenant",
|
|
20
|
+
"create_test_user",
|
|
21
|
+
"debug_auth_issue",
|
|
22
|
+
"delete_feature_flag",
|
|
23
|
+
"delete_tenant",
|
|
24
|
+
"diagnose_integration",
|
|
25
|
+
"evaluate_feature_flag",
|
|
26
|
+
"export_feature_flags",
|
|
27
|
+
"get_app",
|
|
28
|
+
"get_auth_config",
|
|
29
|
+
"get_branding",
|
|
30
|
+
"get_environment_info",
|
|
31
|
+
"get_flag",
|
|
32
|
+
"get_integration_guide",
|
|
33
|
+
"get_plan",
|
|
34
|
+
"get_role",
|
|
35
|
+
"get_server_info",
|
|
36
|
+
"get_setup_status",
|
|
37
|
+
"get_stripe_status",
|
|
38
|
+
"get_tenant",
|
|
39
|
+
"get_user",
|
|
40
|
+
"import_feature_flags",
|
|
41
|
+
"invite_user",
|
|
42
|
+
"list_api_tokens",
|
|
43
|
+
"list_apps",
|
|
44
|
+
"list_events",
|
|
45
|
+
"list_feature_flags",
|
|
46
|
+
"list_plan_quotas",
|
|
47
|
+
"list_plans",
|
|
48
|
+
"list_redirect_uris",
|
|
49
|
+
"list_roles",
|
|
50
|
+
"list_tenants",
|
|
51
|
+
"list_users",
|
|
52
|
+
"remove_plan_price",
|
|
53
|
+
"remove_plan_quota",
|
|
54
|
+
"remove_redirect_uri",
|
|
55
|
+
"remove_user",
|
|
56
|
+
"revoke_api_token",
|
|
57
|
+
"scaffold_flags_integration",
|
|
58
|
+
"set_flag_schedule",
|
|
59
|
+
"set_plan_price",
|
|
60
|
+
"set_plan_quota",
|
|
61
|
+
"setup_communication",
|
|
62
|
+
"setup_payments",
|
|
63
|
+
"setup_sso",
|
|
64
|
+
"toggle_feature_flag",
|
|
65
|
+
"update_app",
|
|
66
|
+
"update_auth_methods",
|
|
67
|
+
"update_branding",
|
|
68
|
+
"update_feature_flag",
|
|
69
|
+
"update_password_policy",
|
|
70
|
+
"update_plan",
|
|
71
|
+
"update_role",
|
|
72
|
+
"update_tenant",
|
|
73
|
+
"update_user",
|
|
74
|
+
"use_app",
|
|
75
|
+
"verify_login"
|
|
76
|
+
]
|
|
77
|
+
}
|