Let outside services and computers' browser notifications give bots work
A bot hook (bot_hooks) is a secret URL, POST /api/hooks/<token>, whose calls queue the hook's brief plus the raw body as a task, per user and bot like a routine. Only the token's hash is kept (shown once, rotatable); an optional HMAC-SHA256 signing secret is stored encrypted. Calls are rate limited per address and per hook, recorded in bot_hook_events (pruned daily), deduplicated by delivery id, and a call while the last run still waits joins its brief instead of queueing another. The payload is fenced data and HookRunNote tells the bot never to take instructions from it; failed runs retry like routines. A hook can instead watch a computer's browser notifications: the app installs a small watcher that owns org.freedesktop.Notifications on the sandbox's session bus, so Chrome hands it every notification (it only looks for that service at start-up, so Chrome is restarted once, never mid-task), and it posts each one to its hooks, optionally filtered. Computers reach the app at COMPUTER_APP_URL or the app container's name. Users make hooks in the conversation's Hooks modal and manage them all on /admin/routines; bots never create or change hooks. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import { useState } from "react";
|
||||
import Input from "@moduletrace/twui/form/Input";
|
||||
import Button from "@moduletrace/twui/layout/Button";
|
||||
import Row from "@moduletrace/twui/layout/Row";
|
||||
import Span from "@moduletrace/twui/layout/Span";
|
||||
import Stack from "@moduletrace/twui/layout/Stack";
|
||||
import { Copy } from "lucide-react";
|
||||
import { BotHookPath } from "@/src/dict/bot-hook-dict";
|
||||
import copyText from "@/src/utils/copy-text";
|
||||
|
||||
type Props = {
|
||||
token: string;
|
||||
/**
|
||||
* Whether calls must be signed, so the example says so.
|
||||
*/
|
||||
signed?: boolean;
|
||||
onDone: () => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* A hook's URL, shown once, right after it was made or given a new one:
|
||||
* only a hash of its token is kept, so it can't be shown again. With a
|
||||
* copy button and an example call.
|
||||
*/
|
||||
export default function HookUrlNotice({ token, signed, onDone }: Props) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
const url = `${window.location.origin}${BotHookPath}/${token}`;
|
||||
const example = `curl -X POST '${url}' -H 'Content-Type: application/json' -d '{"event":"test"}'`;
|
||||
|
||||
const copy = async () => {
|
||||
setCopied(await copyText({ text: url }));
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack className="w-full gap-3! items-stretch pt-4">
|
||||
<Span className="text-[13px] leading-[18px] text-ink">
|
||||
Copy this URL now: it is shown only once. Anyone who has it can start this hook, so keep it secret.
|
||||
</Span>
|
||||
<Row className="w-full gap-2! flex-nowrap items-end">
|
||||
<Input
|
||||
id="hook-url"
|
||||
title="Hook URL"
|
||||
label="URL"
|
||||
showLabel
|
||||
readOnly
|
||||
value={url}
|
||||
onFocus={(e) => e.target.select()}
|
||||
wrapperWrapperProps={{ className: "grow min-w-0" }}
|
||||
/>
|
||||
<Button title="Copy the URL" type="button" variant="outlined" color="gray" onClick={() => copy().catch(() => {})}>
|
||||
<Copy size={16} aria-hidden />
|
||||
{copied ? "Copied" : "Copy"}
|
||||
</Button>
|
||||
</Row>
|
||||
<Span className="text-[12px] leading-4 text-ink-secondary">
|
||||
{signed
|
||||
? `Calls must be signed with the secret (HMAC-SHA256 of the body) in the header you set.`
|
||||
: `Send any body (JSON or text, up to 64 KB) with a POST. For example:`}
|
||||
</Span>
|
||||
{signed ? null : (
|
||||
<Span className="block w-full rounded-default bg-sunken px-3 py-2 font-mono text-[12px] leading-4 text-ink break-all">
|
||||
{example}
|
||||
</Span>
|
||||
)}
|
||||
<Row className="w-full justify-end">
|
||||
<Button title="Done" type="button" onClick={onDone}>
|
||||
Done
|
||||
</Button>
|
||||
</Row>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user