You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
### OAuth deployment availability (required for integration blocks)
173
173
174
174
A visible tools-category block with OAuth is deployment-gated. Its `oauth-input.serviceId` is
175
-
projected into `apps/sim/lib/integrations/integrations.json`, then resolved through
176
-
`resolveOAuthClientCapabilityId()` in `apps/sim/lib/core/config/env-capabilities.ts`.
175
+
projected into `packages/deployment-config/src/integrations.json`, then resolved through
176
+
`resolveOAuthClientCapabilityId()` in `packages/deployment-config/src/env-capabilities.ts`.
177
177
178
178
When adding or changing an OAuth integration block:
179
179
@@ -184,13 +184,14 @@ When adding or changing an OAuth integration block:
184
184
3. For a new capability, add its required client fields to `OAUTH_CLIENT_CAPABILITIES` and ensure
185
185
every referenced field exists in the env schema in `apps/sim/lib/core/config/env.ts`. Then add
186
186
the matching `text` or `secret` input modes to `OAUTH_CLIENT_SETUP_FIELDS` in
187
-
`scripts/setup/capability-config.ts`. The CLI catalog is exhaustively typed and checked against
188
-
the runtime field list; do not infer secrecy from the field name.
189
-
4. If the canonical OAuth service declares `serviceAccountProviderId`, keep
190
-
`SERVICE_ACCOUNT_METADATA_BY_OAUTH_SERVICE_ID` in
191
-
`apps/sim/lib/integrations/service-account-metadata.ts` aligned. Set
192
-
`deploymentRequirement` only when the service-account path is preview-gated or depends on the
193
-
OAuth client fields; otherwise omit it.
187
+
`packages/sim-setup/src/capability-config.ts`. The CLI catalog is exhaustively typed and checked
188
+
against the runtime field list; do not infer secrecy from the field name.
189
+
4. If the canonical OAuth service declares `serviceAccountProviderId`, run
190
+
`bun run deployment-config:generate`; this regenerates the provider-ID facts in
191
+
`packages/deployment-config/src/service-account-providers.generated.ts`. Never hand-edit that
192
+
generated map. Add `deploymentRequirement` policy in
193
+
`packages/deployment-config/src/service-account-metadata.ts` only when the service-account path
194
+
is preview-gated or depends on the OAuth client fields; otherwise omit it.
194
195
195
196
Missing capability metadata is a runtime configuration error, not a reason to make the integration
196
197
silently available.
@@ -992,16 +993,21 @@ After adding or changing one, run:
992
993
993
994
```bash
994
995
bun run scripts/generate-docs.ts
996
+
bun run deployment-config:generate
995
997
bun run integration-catalog:check
998
+
bun run deployment-config:check
996
999
bun run docs:check
997
1000
```
998
1001
999
1002
The catalog check independently derives deployment metadata from the executable block registry and
1000
-
compares it with the committed `apps/sim/lib/integrations/integrations.json`. `docs:check` re-renders
1001
-
every generated docs artifact in memory and fails on any committed file that differs — it runs in CI
1002
-
via `check:audits`, so commit the full generator output. If the generator also trues up pages an
1003
-
earlier PR left stale, commit that catch-up too; reverting it as "unrelated drift" makes `docs:check`
1004
-
fail.
1003
+
compares it with the committed `packages/deployment-config/src/integrations.json`. The deployment
1004
+
config check verifies the generated service-account facts against the canonical OAuth registry and
1005
+
catalog. `docs:check` re-renders every generated docs artifact in memory and fails on any committed
1006
+
file that differs — it runs in CI via `check:audits`, so commit the full generator output. If the
1007
+
generator also trues up pages an earlier PR left stale, commit that catch-up too; reverting it as
1008
+
"unrelated drift" makes `docs:check` fail. Review the generated diff and keep only intentional
1009
+
changes.
1010
+
1005
1011
## Checklist Before Finishing
1006
1012
1007
1013
-[ ]`integrationType` is set to the correct `IntegrationType` enum value
@@ -1046,3 +1052,34 @@ After creating the block, you MUST validate it against every tool it references:
1046
1052
4.**Verify conditions** — each subBlock should only show for the operations that actually use it
1047
1053
5.**Verify `{Service}BlockMeta` is exported** with at least 7 templates, each having `icon`, `title`, `prompt`, `modules`, `category`, and `tags`
1048
1054
6.**If any tool outputs are still unknown**, explicitly tell the user instead of guessing block outputs
1055
+
1056
+
## Option Lists: `selectorKey` or `options`, never a per-block fetcher
1057
+
1058
+
A sub-block gets its choices from exactly one of two places. There is no third.
1059
+
1060
+
**`selectorKey` — every remote list.** Register the list in `hooks/selectors/providers/<service>/selectors.ts`, add its key to `SelectorKey`, and point the sub-block at it. A selector is parameterized by an explicit `SelectorContext`, so the same definition serves the canvas, the workspace-fork sync modal, and anything added later.
`canonicalParamId: 'oauthCredential'` on the credential sub-block is the line people forget. `buildSelectorContextFromBlock` keys the context on a sub-block's CANONICAL id, so without it `context.oauthCredential` is never set and the picker looks unfixable without reading the store. (A credential field is also recognised by its `oauth-input` TYPE as a fallback, so a block whose shipped param is already named something else does not have to rename it.)
1070
+
1071
+
**`options` — everything else.** A static array, or a pure function of the block's own values for a list that narrows to a sibling's selection. No I/O.
**Never fetch inside `options`, and never reach into the stores from a block definition.** A fetcher that resolves its credential with `readSubBlockValue(blockId, ...)` only works on the canvas — every surface that is not the editor gets an empty list. `fetchOptions`/`fetchOptionById` were removed for exactly this reason.
1081
+
1082
+
Two rules the checks enforce:
1083
+
1084
+
-**A secret never enters a selector's `getQueryKey`.** A query key identifies a resource; a credential authorizes access to it. A credential *id* is fine; a typed password is not (see `imap.mailboxes`).
1085
+
-**A sub-block that `dependsOn` a credential / knowledge-base / table selector must be reconfigurable at fork-sync time** — a `selectorKey`, a canonical pair whose basic member is a selector, or a `short-input`/`long-input`. `bun run check:fork-dependent-coverage` fails otherwise, because a fork sync clears those fields on every push and an unofferable one can never be set anywhere that sticks.
`packages/sim-setup/src/capability-config.ts`. Do not create integration-specific setup logic or
547
+
infer secret fields from naming; the CLI mapping is exhaustively checked against the runtime
548
+
fields.
549
+
4. If the canonical OAuth service has `serviceAccountProviderId`, run
550
+
`bun run deployment-config:generate` to refresh
551
+
`packages/deployment-config/src/service-account-providers.generated.ts`; never hand-edit the
552
+
generated provider-ID map. In `packages/deployment-config/src/service-account-metadata.ts`, use:
551
553
- no `deploymentRequirement` when the service-account path works independently of OAuth client fields;
552
554
-`'oauth-client'` when it requires the same deployment OAuth client fields;
553
555
-`'preview-gated'` when availability is controlled by the service-account preview block.
@@ -560,15 +562,18 @@ a resolvable capability must fail validation.
560
562
Run the documentation generator:
561
563
```bash
562
564
bun run scripts/generate-docs.ts
565
+
bun run deployment-config:generate
563
566
bun run integration-catalog:check
567
+
bun run deployment-config:check
564
568
bun run docs:check
565
569
```
566
570
567
571
This creates `apps/docs/content/docs/en/integrations/{service}.mdx` — one page per service carrying the block's Actions and, if it has one, its Triggers section. Never hand-edit generated pages; the only editable region is the `{/* MANUAL-CONTENT */}` block (see `scripts/README.md`).
568
572
569
-
The same generator refreshes `apps/sim/lib/integrations/integrations.json`. The catalog check then
570
-
derives the deployment-relevant fields from the executable block registry and compares them with the
571
-
committed projection. Review the generated diff and keep only intentional changes.
573
+
The docs generator refreshes `packages/deployment-config/src/integrations.json`, and the deployment
574
+
config generator projects service-account provider IDs from that catalog plus the canonical OAuth
575
+
registry. The checks compare both committed projections with their sources. Review the generated
576
+
diff and keep only intentional changes.
572
577
573
578
## V2 Integration Pattern
574
579
@@ -647,14 +652,16 @@ If creating V2 versions (API-aligned outputs):
647
652
-[ ] Created `index.ts` barrel export
648
653
-[ ] Registered all triggers in `triggers/registry.ts`
649
654
650
-
### Docs
655
+
### Docs and deployment metadata
651
656
-[ ] Ran `bun run scripts/generate-docs.ts`
657
+
-[ ] Ran `bun run deployment-config:generate` for OAuth or service-account changes
652
658
-[ ] Verified docs file created
653
-
-[ ] Reviewed and committed the generated `apps/sim/lib/integrations/integrations.json` change
659
+
-[ ] Reviewed and committed the generated `packages/deployment-config/src/integrations.json` change
654
660
-[ ]`bun run integration-catalog:check` passes
655
661
-[ ]`bun run docs:check` passes — CI fails on stale generated docs, so commit the full generator
656
662
output, including catch-up regeneration for pages another PR left stale (never revert it as
657
663
"unrelated drift")
664
+
-[ ]`bun run deployment-config:check` passes
658
665
659
666
### Final Validation (Required)
660
667
-[ ] Read every tool file and cross-referenced inputs/outputs against the API docs
11.**Never hardcode scopes** - Use `getScopesForService()` in blocks and `getCanonicalScopesForProvider()` in auth.ts
1003
1010
12.**Always add scope descriptions** - New scopes must have entries in `SCOPE_DESCRIPTIONS` within `lib/oauth/utils.ts`
1004
1011
13.**OAuth service IDs need deployment capabilities** - Every visible OAuth integration must resolve through `OAUTH_CLIENT_CAPABILITIES`; shared Google/Microsoft aliases map to their provider capability
1005
-
14.**Keep runtime and presentation separate** - Runtime OAuth fields live in `env-capabilities.ts`; CLI input modes live in the exhaustively checked `scripts/setup/capability-config.ts` mapping
1012
+
14.**Keep runtime and presentation separate** - Runtime OAuth fields live in `packages/deployment-config/src/env-capabilities.ts`; CLI input modes live in the exhaustively checked `packages/sim-setup/src/capability-config.ts` mapping
## Option Lists: `selectorKey` or `options`, never a per-block fetcher
476
+
477
+
A sub-block gets its choices from exactly one of two places. There is no third.
478
+
479
+
**`selectorKey` — every remote list.** Register the list in `hooks/selectors/providers/<service>/selectors.ts`, add its key to `SelectorKey`, and point the sub-block at it. A selector is parameterized by an explicit `SelectorContext`, so the same definition serves the canvas, the workspace-fork sync modal, and anything added later.
`canonicalParamId: 'oauthCredential'` on the credential sub-block is the line people forget. `buildSelectorContextFromBlock` keys the context on a sub-block's CANONICAL id, so without it `context.oauthCredential` is never set and the picker looks unfixable without reading the store. (A credential field is also recognised by its `oauth-input` TYPE as a fallback, so a block whose shipped param is already named something else does not have to rename it.)
489
+
490
+
**`options` — everything else.** A static array, or a pure function of the block's own values for a list that narrows to a sibling's selection. No I/O.
491
+
492
+
```ts
493
+
options: (params) => {
494
+
const model = params?.values.model
495
+
return typeof model === 'string' ? effortsFor(model) : DEFAULT_EFFORTS
496
+
}
497
+
```
498
+
499
+
**Never fetch inside `options`, and never reach into the stores from a block definition.** A fetcher that resolves its credential with `readSubBlockValue(blockId, ...)` only works on the canvas — every surface that is not the editor gets an empty list. `fetchOptions`/`fetchOptionById` were removed for exactly this reason.
500
+
501
+
Two rules the checks enforce:
502
+
503
+
- **A secret never enters a selector's `getQueryKey`.** A query key identifies a resource; a credential authorizes access to it. A credential *id* is fine; a typed password is not (see `imap.mailboxes`).
504
+
- **A sub-block that `dependsOn` a credential / knowledge-base / table selector must be reconfigurable at fork-sync time** — a `selectorKey`, a canonical pair whose basic member is a selector, or a `short-input`/`long-input`. `bun run check:fork-dependent-coverage` fails otherwise, because a fork sync clears those fields on every push and an unofferable one can never be set anywhere that sticks.
0 commit comments