Envs had three scopes, general, workspace and computer. A bot had no
scope of its own, so the only way to give one worker a key was to set it
on a computer it happened to share.
The envs table gets a bot_id, and resolution now layers bot over
computer over workspace over general, so a bot's own key wins wherever it
runs. Every computer call site passes the run's bot_id, and grabRunEnvs
does the same for the {{env.KEY}} a custom tool or an MCP server's headers
read.
A bot's and a workspace's settings page can now edit their own envs, in
place, through the same save-env and delete-env routes: sending `scoped`
makes them answer with just that scope instead of the whole install's
overview. Super admins only, like the Environment page.
43 lines
1.2 KiB
TypeScript
43 lines
1.2 KiB
TypeScript
import { useState } from "react";
|
|
import type { EnvsMutationResult, ScopedEnvsPayload } from "@/src/types";
|
|
import EnvsGroup from "./envs-group";
|
|
|
|
type Props = {
|
|
/**
|
|
* A bot's own envs, or a workspace's. `null` when the viewer can't
|
|
* manage them, so the group isn't rendered at all.
|
|
*/
|
|
initial?: ScopedEnvsPayload | null;
|
|
};
|
|
|
|
/**
|
|
* The envs set on one bot or one workspace, on its settings page. Super
|
|
* admins only, like the Environment page.
|
|
*/
|
|
export default function ScopedEnvsGroup({ initial }: Props) {
|
|
const [data, setData] = useState<ScopedEnvsPayload | null>(initial || null);
|
|
const [status, setStatus] = useState<{
|
|
error?: boolean;
|
|
success?: boolean;
|
|
msg?: string;
|
|
}>();
|
|
|
|
if (!data?.can_manage) return null;
|
|
|
|
const changed = (result: EnvsMutationResult, msg: string) => {
|
|
if (result.scoped) setData(result.scoped);
|
|
setStatus({ success: true, msg });
|
|
};
|
|
|
|
return (
|
|
<EnvsGroup
|
|
scope={data.scope}
|
|
envs={data.envs}
|
|
scoped
|
|
status={status}
|
|
onChanged={changed}
|
|
onError={(msg) => setStatus({ error: true, msg })}
|
|
/>
|
|
);
|
|
}
|