Bots get list_hooks, pause_hook and delete_hook, which act directly since they only take away. A new hook, or a new name, brief or filter, or switching a paused one back on, goes through propose_hook: it stores a hook_proposals row and puts a [hook-proposal:ID] card in the reply, which the user it was proposed to confirms or rejects. The card is read from what Confirm saves, with the bot's reason labelled as its own; confirming checks everything again and saves through the Hooks modal's helpers. A new hook's URL is shown on the card once, to the user only, never in the outcome the bot is told. What calls a hook, its computers and its signing are never proposed. Only chat replies propose, and a hook call's own run gets no hook tools, so a caller's payload can't reach them. Co-Authored-By: Claude Opus 5.5 <[email protected]>
85 lines
3.0 KiB
TypeScript
85 lines
3.0 KiB
TypeScript
import type { HookProposalAction } from "@/src/dict/bot-hook-dict";
|
|
import type { HookProposalData } from "@/src/schemas/bot-hook-schema";
|
|
import type { BotHookWithNames, ExtensionProposalDetail } from "@/src/types";
|
|
|
|
type Params = {
|
|
action: HookProposalAction;
|
|
data: HookProposalData;
|
|
/**
|
|
* The hook it changes, as it is now.
|
|
*/
|
|
current?: BotHookWithNames | null;
|
|
/**
|
|
* Where a new hook runs: the workspace's name, none for the bot's own
|
|
* chat.
|
|
*/
|
|
workspace_name?: string | null;
|
|
/**
|
|
* Computer names by id.
|
|
*/
|
|
computers: Map<number, string>;
|
|
};
|
|
|
|
/**
|
|
* What a hook proposal would save, line by line for its card, read from
|
|
* the data Confirm saves (never from what the bot said): for a new hook
|
|
* what calls it, where it runs, on which computer and its whole brief;
|
|
* for a change each field as it is and as it would be.
|
|
*/
|
|
export default function describeHookProposalDetails({
|
|
action,
|
|
data,
|
|
current,
|
|
workspace_name,
|
|
computers,
|
|
}: Params): ExtensionProposalDetail[] {
|
|
const computer = (id?: number | null) => (id ? `${computers.get(Number(id)) || "Computer"} (#${id})` : "");
|
|
|
|
if (action == "create") {
|
|
const watching = data.source == "browser_notifications";
|
|
|
|
return [
|
|
{ label: "Name", value: data.title || "" },
|
|
{
|
|
label: "Called by",
|
|
value: watching
|
|
? `Browser notifications on ${computer(data.watch_computer_id)}`
|
|
: "Anyone with its URL (shown to you once, when you confirm)",
|
|
},
|
|
...(watching ? [{ label: "Takes", value: data.watch_filter ? `Only notifications containing "${data.watch_filter}"` : "Every notification" }] : []),
|
|
{ label: "Runs in", value: workspace_name ? `The workspace ${workspace_name}` : "The bot's own chat with you" },
|
|
{
|
|
label: "Runs on",
|
|
value: computer(data.computer_id || (watching ? data.watch_computer_id : null)) || "The bot's current computer",
|
|
},
|
|
{ label: "Brief", value: data.instructions || "", block: true },
|
|
];
|
|
}
|
|
|
|
const lines: ExtensionProposalDetail[] = [
|
|
{ label: "Hook", value: `#${current?.id} ${current?.title || ""}`.trim() },
|
|
];
|
|
|
|
if (data.title !== undefined) {
|
|
lines.push({ label: "Name now", value: current?.title || "" }, { label: "New name", value: data.title });
|
|
}
|
|
|
|
if (data.instructions !== undefined) {
|
|
lines.push(
|
|
{ label: "Brief now", value: current?.instructions || "", block: true },
|
|
{ label: "New brief", value: data.instructions, block: true },
|
|
);
|
|
}
|
|
|
|
if (data.watch_filter !== undefined) {
|
|
lines.push(
|
|
{ label: "Filter now", value: current?.watch_filter || "None: every notification" },
|
|
{ label: "New filter", value: data.watch_filter || "None: every notification" },
|
|
);
|
|
}
|
|
|
|
if (data.enabled) lines.push({ label: "Switch on", value: "It's paused now; calls would run again" });
|
|
|
|
return lines;
|
|
}
|