Media tools and card actions send prompts, images, video and audio to the
deployment owner's ComfyUI and transcription servers. Like the embeddings
server behind built-in memory, those self-hosted endpoints are recipients in
the AI data-sharing disclosure: they appear with the model uses ("ComfyUI
(<origin>)", "Transcription (<origin>)", keyed by the full endpoint so a new
server asks again), and starting a card job asks for model consent on clients
that run the foreground check.
Also adds unit tests for the media settings store: URL normalization and
refusals, encryption, the key-free view, one decryption per saved version, key
retention per server and removing empty settings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
65 lines
2.5 KiB
TypeScript
65 lines
2.5 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { aiRecipient } from "./ai-consent.js";
|
|
|
|
afterEach(() => vi.unstubAllGlobals());
|
|
|
|
describe("mobile AI recipient disclosure", () => {
|
|
it("identifies both gateways without querying or changing their routing", () => {
|
|
const fetch = vi.fn();
|
|
vi.stubGlobal("fetch", fetch);
|
|
for (const provider of ["openrouter", "vercel-ai-gateway"]) {
|
|
const recipient = aiRecipient({ provider, use: "model", modelId: "any/model" });
|
|
expect(recipient?.detail).toContain("forwards requests to model providers");
|
|
expect(recipient?.privacyUrl).toBeTruthy();
|
|
expect(recipient).not.toHaveProperty("payloadFields");
|
|
}
|
|
expect(fetch).not.toHaveBeenCalled();
|
|
});
|
|
it("supports custom endpoints without exposing secrets or inventing a provider policy", () => {
|
|
const recipient = aiRecipient({
|
|
provider: "openai-compatible",
|
|
use: "model",
|
|
baseUrl: "https://user:password@example.com/private?token=secret",
|
|
})!;
|
|
expect(recipient.name).toContain("https://example.com");
|
|
expect(JSON.stringify(recipient)).not.toMatch(/password|private|token|secret/);
|
|
expect(recipient.privacyUrl).toBeUndefined();
|
|
expect(
|
|
aiRecipient({
|
|
provider: "openai-compatible",
|
|
use: "model",
|
|
baseUrl: "https://example.com/other",
|
|
})?.key,
|
|
).not.toBe(recipient.key);
|
|
});
|
|
it("names the embeddings server that built-in memory sends text to", () => {
|
|
const recipient = aiRecipient({
|
|
provider: "builtin",
|
|
use: "memory",
|
|
baseUrl: "http://192.168.1.20:8080/v1",
|
|
})!;
|
|
expect(recipient.name).toBe("Embeddings (http://192.168.1.20:8080)");
|
|
expect(recipient.privacyUrl).toBeUndefined();
|
|
});
|
|
it("names the media servers that media jobs send files to", () => {
|
|
expect(
|
|
aiRecipient({ provider: "comfyui", use: "model", baseUrl: "http://127.0.0.1:8188" })?.name,
|
|
).toBe("ComfyUI (http://127.0.0.1:8188)");
|
|
expect(
|
|
aiRecipient({
|
|
provider: "media-transcription",
|
|
use: "model",
|
|
baseUrl: "http://127.0.0.1:8080/v1",
|
|
})?.name,
|
|
).toBe("Transcription (http://127.0.0.1:8080)");
|
|
});
|
|
it("supports local models without a hosted dependency", () => {
|
|
const fetch = vi.fn();
|
|
vi.stubGlobal("fetch", fetch);
|
|
expect(
|
|
aiRecipient({ provider: "local", use: "model", baseUrl: "http://127.0.0.1:11434/v1" })?.name,
|
|
).toContain("http://127.0.0.1:11434");
|
|
expect(fetch).not.toHaveBeenCalled();
|
|
});
|
|
});
|