chore: v2.8.71 — remote clients (company hub), artifacts shared with chosen people, folders and viewable copies, fixes
Remote clients (docs/plans/remote-clients/README.md, B1–B3 for the Claude pool): - server/hub/ holds the hub-only code (devices, remote sessions, upload ingest, LAN gateway), required only in server mode; hub-boundary.test.js keeps local code from importing it. - A local install enrolls with `agenthub remote login` or Settings → AI tools → Company hub; the "Company account" AI Proxy (reserved name agenthub.company) sends a session through the hub's gateway, which adds a pool account's token — no credential reaches the machine. Only those sessions are uploaded, hourly and at their end; secret-shaped strings are redacted on the client. - Gateway refuses: unknown device (401), a message without a session id, an unregistered session under AGENTHUB_REMOTE_GATE=block, a user over quota (EQUOTA), a purged session (410). Device tokens 90 days, conversations 30. - New alert rule type remote_unmonitored: a company-account session its device never registered; a late registration clears the flag instead. - The hub imports a session once per upload round (chunks carry `more`), not once per chunk. Artifacts: - Share an artifact with chosen people (artifact_shares, PATCH sharedWith, GET /api/artifacts/people) besides everyone; a "Shared with you" tab. Only the owner or an admin sees or edits who it is shared with; audited. - Folders: each account's own arrangement (agenthub.artifacts.folders pref). - A claude.ai page keeps a copy viewable without the publishing account: its HTML file (and supporting files) snapshotted at publish, or the copy Claude saves for a Doc / Slides / Design in artifacts-export/ (a synchronous PostToolUse hint hook, scripts/artifact-export-hint.js, admin switch), attached to the page at Stop. Viewed in HtmlFrame's sandbox, Markdown, or pdf.js (pdfjs-dist 6.4.299, legacy build, loaded only for a PDF). Fixes: - tmux: a cached /proc/<pid>/exe of an exited server falls back to PATH's tmux (stop the last pane, start again within 10 s failed with ENOENT). - shelves: a shelf restores beside changes in other files. - chat: a finished reply ends the turn even with no hook at all. - Settings → AI tools: profile list display tweaks.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* @file Tests for PdfView: pdf.js (mocked — jsdom has no canvas) draws one
|
||||
* canvas per page, scripting off, and a PDF that cannot be read says so.
|
||||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import "../../i18n";
|
||||
|
||||
const getDocument = vi.fn();
|
||||
vi.mock("pdfjs-dist/legacy/build/pdf.mjs", () => ({
|
||||
GlobalWorkerOptions: { workerSrc: "" },
|
||||
getDocument: (...a: unknown[]) => getDocument(...a),
|
||||
}));
|
||||
|
||||
import { PdfView } from "../PdfView";
|
||||
|
||||
const realFetch = globalThis.fetch;
|
||||
beforeEach(() => {
|
||||
getDocument.mockReset();
|
||||
globalThis.fetch = vi.fn(
|
||||
async () => new Response(new Uint8Array([37, 80, 68, 70]))
|
||||
) as typeof fetch;
|
||||
});
|
||||
afterEach(() => {
|
||||
globalThis.fetch = realFetch;
|
||||
});
|
||||
|
||||
describe("PdfView", () => {
|
||||
it("draws every page on its own canvas, with XFA off", async () => {
|
||||
const render1 = vi.fn(() => ({ promise: Promise.resolve() }));
|
||||
getDocument.mockReturnValue({
|
||||
destroy: vi.fn(),
|
||||
promise: Promise.resolve({
|
||||
numPages: 3,
|
||||
getPage: async () => ({
|
||||
getViewport: ({ scale }: { scale: number }) => ({
|
||||
width: 600 * scale,
|
||||
height: 800 * scale,
|
||||
}),
|
||||
render: render1,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
const { container } = render(<PdfView url="/api/artifacts/1/raw?path=a.pdf" title="a.pdf" />);
|
||||
await waitFor(() => expect(container.querySelectorAll("canvas")).toHaveLength(3));
|
||||
expect(render1).toHaveBeenCalledTimes(3);
|
||||
expect(getDocument.mock.calls[0]![0]).toMatchObject({ enableXfa: false });
|
||||
expect(screen.getByLabelText("Page 2")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("says so when the file cannot be read", async () => {
|
||||
getDocument.mockReturnValue({ destroy: vi.fn(), promise: Promise.reject(new Error("bad")) });
|
||||
render(<PdfView url="/x" title="x.pdf" />);
|
||||
expect(await screen.findByText(/could not be shown/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user