源码快照 @ 2026-09-02 (source-only, 剔除非源码素材)
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
# Desktop App Policies
|
||||
|
||||
Desktop app policy config is loaded from OpenWork Cloud through `GET /v1/me/desktop-config` and exposed inside the desktop app through `DesktopConfigProvider`.
|
||||
|
||||
App code should read policy state through the hooks in `apps/app/src/react-app/domains/cloud/desktop-config-provider.tsx`. Do not read the provider's internal refs directly; those are only used to compare and apply newly loaded config safely.
|
||||
|
||||
## Policy Keys
|
||||
|
||||
The canonical policy catalog lives in `packages/types/src/den/desktop-policies.ts` as `desktopPolicyDefinitions`. Add or change policy items there first so the API, Den web, and desktop app share the same IDs and copy.
|
||||
|
||||
Do not duplicate the list of policy IDs in app docs or feature code unless a feature is intentionally checking a specific policy. Import from the shared package when you need the full catalog.
|
||||
|
||||
`allowedDesktopVersions` is part of the desktop config response but is not a boolean policy item in `desktopPolicyDefinitions`.
|
||||
|
||||
For boolean policy keys, `false` means the feature is restricted or disabled. `true` or `undefined` means the app should not block the feature locally.
|
||||
|
||||
## Organization Prompt Suggestions
|
||||
|
||||
Desktop policy documents can also include `onboardingPrompts` and `onboardingPromptDescriptions`. The shared schema requires 2 or 3 prompts, caps each prompt at 500 characters, and caps each description at 120 characters.
|
||||
|
||||
In the desktop app, `onboardingPromptDescriptions` become prompt-card titles. `onboardingPrompts` become the visible card descriptions and the text inserted into the composer when a user clicks a card; the click does not auto-send.
|
||||
|
||||
Boolean policy keys are OR-unioned across matching policies by `calculateEffectiveDesktopPolicy()`. Prompt suggestions are different: `selectEffectiveOnboardingPromptConfig()` picks one matching prompt config by highest `priority`, then earliest `createdAt`, then policy `id`, falling back to the default policy only when no targeted prompt config applies.
|
||||
|
||||
## Preferred Check
|
||||
|
||||
Use `useCheckDesktopRestriction()` when gating app behavior.
|
||||
|
||||
```tsx
|
||||
import { useCheckDesktopRestriction } from "../domains/cloud/desktop-config-provider";
|
||||
|
||||
function Example() {
|
||||
const checkDesktopRestriction = useCheckDesktopRestriction();
|
||||
const zenModelsRestricted = checkDesktopRestriction({
|
||||
restriction: "allowZenModel",
|
||||
});
|
||||
|
||||
return zenModelsRestricted ? null : <ZenModelPicker />;
|
||||
}
|
||||
```
|
||||
|
||||
`checkDesktopRestriction()` returns `true` when the feature is restricted.
|
||||
|
||||
## Single Policy Hook
|
||||
|
||||
Use `useDesktopRestriction()` when a component only needs one policy value.
|
||||
|
||||
```tsx
|
||||
import { useDesktopRestriction } from "../domains/cloud/desktop-config-provider";
|
||||
|
||||
function AddWorkspaceButton() {
|
||||
const multipleWorkspacesRestricted = useDesktopRestriction(
|
||||
"allowMultipleWorkspaces",
|
||||
);
|
||||
|
||||
return (
|
||||
<button disabled={multipleWorkspacesRestricted}>
|
||||
Add workspace
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Raw Config
|
||||
|
||||
Use `useDesktopConfig()` when you need the raw config, loading state, or manual refresh function.
|
||||
|
||||
```tsx
|
||||
import { useDesktopConfig } from "../domains/cloud/desktop-config-provider";
|
||||
|
||||
function DesktopPolicyDebug() {
|
||||
const desktopConfig = useDesktopConfig();
|
||||
|
||||
return (
|
||||
<pre>
|
||||
{JSON.stringify({
|
||||
loading: desktopConfig.loading,
|
||||
config: desktopConfig.config,
|
||||
}, null, 2)}
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Use `useOrgRestrictions()` only when you need the raw config object without `loading`, `refresh`, or `checkRestriction`.
|
||||
|
||||
```tsx
|
||||
import { useOrgRestrictions } from "../domains/cloud/desktop-config-provider";
|
||||
|
||||
function Example() {
|
||||
const config = useOrgRestrictions();
|
||||
const customProvidersRestricted = config.allowCustomProviders === false;
|
||||
|
||||
return customProvidersRestricted ? <RestrictedNotice /> : <ProviderForm />;
|
||||
}
|
||||
```
|
||||
|
||||
## Helper Functions
|
||||
|
||||
For model/provider-specific gates, use the helpers in `apps/app/src/app/cloud/desktop-app-restrictions.ts`.
|
||||
|
||||
```tsx
|
||||
import { isDesktopModelBlocked } from "../../app/cloud/desktop-app-restrictions";
|
||||
import { useCheckDesktopRestriction } from "../domains/cloud/desktop-config-provider";
|
||||
|
||||
function ModelOption({ model }: { model: ModelRef }) {
|
||||
const checkDesktopRestriction = useCheckDesktopRestriction();
|
||||
const blocked = isDesktopModelBlocked({
|
||||
model,
|
||||
checkRestriction: checkDesktopRestriction,
|
||||
});
|
||||
|
||||
return <ModelRow model={model} disabled={blocked} />;
|
||||
}
|
||||
```
|
||||
|
||||
## Loading And Refresh
|
||||
|
||||
The provider loads cached config first, then fetches the latest config from Cloud. It refreshes on sign-in/session changes, Den settings changes, and a one-hour interval.
|
||||
|
||||
Manual refresh is available through `useDesktopConfig().refresh()`:
|
||||
|
||||
```tsx
|
||||
const desktopConfig = useDesktopConfig();
|
||||
|
||||
await desktopConfig.refresh();
|
||||
```
|
||||
|
||||
## Rule Of Thumb
|
||||
|
||||
Use these APIs in this order:
|
||||
|
||||
1. `useCheckDesktopRestriction()` for most feature gates.
|
||||
2. `useDesktopRestriction(key)` for one-off component checks.
|
||||
3. `useDesktopConfig()` when you need loading/refresh/raw config.
|
||||
4. `useOrgRestrictions()` only for raw config reads.
|
||||
Reference in New Issue
Block a user