|
1 | | -import { fetchPersonalEnvironment, fetchWorkspaceEnvironment } from '@/lib/environment/api' |
| 1 | +import { fetchWorkspaceEnvironment } from '@/lib/environment/api' |
2 | 2 | import { getQueryClient } from '@/app/_shell/providers/get-query-client' |
3 | | -import { |
4 | | - environmentKeys, |
5 | | - PERSONAL_ENVIRONMENT_STALE_TIME, |
6 | | - WORKSPACE_ENVIRONMENT_STALE_TIME, |
7 | | -} from '@/hooks/queries/environment' |
| 3 | +import { environmentKeys, WORKSPACE_ENVIRONMENT_STALE_TIME } from '@/hooks/queries/environment' |
8 | 4 | import { getSandboxListQueryOptions, type SandboxListResponse } from '@/hooks/queries/sandboxes' |
9 | 5 | import { getWorkflowListQueryOptions } from '@/hooks/queries/utils/workflow-list-query' |
10 | 6 | import { useWorkflowRegistry } from '@/stores/workflows/registry/store' |
@@ -43,30 +39,33 @@ export async function fetchWorkspaceWorkflowOptions(options?: { |
43 | 39 | * Loads the active workspace's secret NAMES for the Function block's secret-scope |
44 | 40 | * picker. Names only — values stay server-side and are injected at execution, the |
45 | 41 | * same discipline the copilot's workspace context uses. |
| 42 | + * |
| 43 | + * Both halves come from the single workspace-environment response, which is the |
| 44 | + * client-side mirror of `getEffectiveDecryptedEnv` — the resolver the executor |
| 45 | + * injects from. Its `personal` slice is NOT the caller's raw personal variables: |
| 46 | + * under credential filtering it also carries personal secrets other members have |
| 47 | + * shared into the workspace, so reading `/api/environment` instead would hide |
| 48 | + * names the code can genuinely resolve. This is the same pair the `{{VAR}}` |
| 49 | + * autocomplete lists, so the picker and the editor never disagree. |
| 50 | + * |
| 51 | + * Values are masked to `''` for non-admin viewers; only the keys are used here. |
46 | 52 | */ |
47 | 53 | export async function fetchWorkspaceSecretNameOptions(): Promise<SubBlockOption[]> { |
48 | 54 | const workspaceId = useWorkflowRegistry.getState().hydration.workspaceId |
49 | 55 | if (!workspaceId) return [] |
50 | 56 |
|
51 | | - const [workspace, personal] = await Promise.all([ |
52 | | - getQueryClient().fetchQuery({ |
53 | | - queryKey: environmentKeys.workspace(workspaceId), |
54 | | - queryFn: ({ signal }: { signal?: AbortSignal }) => |
55 | | - fetchWorkspaceEnvironment(workspaceId, signal), |
56 | | - staleTime: WORKSPACE_ENVIRONMENT_STALE_TIME, |
57 | | - }), |
58 | | - getQueryClient().fetchQuery({ |
59 | | - queryKey: environmentKeys.personal(), |
60 | | - queryFn: ({ signal }: { signal?: AbortSignal }) => fetchPersonalEnvironment(signal), |
61 | | - staleTime: PERSONAL_ENVIRONMENT_STALE_TIME, |
62 | | - }), |
63 | | - ]) |
| 57 | + const environment = await getQueryClient().fetchQuery({ |
| 58 | + queryKey: environmentKeys.workspace(workspaceId), |
| 59 | + queryFn: ({ signal }: { signal?: AbortSignal }) => |
| 60 | + fetchWorkspaceEnvironment(workspaceId, signal), |
| 61 | + staleTime: WORKSPACE_ENVIRONMENT_STALE_TIME, |
| 62 | + }) |
64 | 63 |
|
65 | | - // Personal variables shadow workspace ones at execution, so both are offered |
66 | | - // under one de-duplicated list of names. |
| 64 | + // Workspace entries shadow personal ones at execution (`getEffectiveDecryptedEnv` |
| 65 | + // spreads workspace last), but a name-only list just de-duplicates the union. |
67 | 66 | const names = new Set<string>([ |
68 | | - ...Object.keys(workspace?.workspace?.variables ?? {}), |
69 | | - ...Object.keys(personal ?? {}), |
| 67 | + ...Object.keys(environment?.workspace ?? {}), |
| 68 | + ...Object.keys(environment?.personal ?? {}), |
70 | 69 | ]) |
71 | 70 | return [...names].sort().map((name) => ({ id: name, label: name })) |
72 | 71 | } |
|
0 commit comments