Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/engine-post-wire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"sideshow": minor
---

The engine now uses the canonical `/api/posts` wire and `post-*` SSE events; legacy `/api/surfaces` routes, `surface-*` aliases, and `publish_surface` MCP tools remain as deprecated aliases.
2 changes: 1 addition & 1 deletion e2e/viewer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ test("a surface kind this viewer doesn't know shows a refresh hint, not a broken
// server returns a valid surface, but rewrite the surface kind to one THIS
// viewer build has no Match for. It must degrade to a neutral hint, never
// the diff fallback.
await page.route(/\/api\/surfaces\/[^/?]+(\?|$)/, async (route) => {
await page.route(/\/api\/posts\/[^/?]+(\?|$)/, async (route) => {
const res = await route.fetch();
const surface = await res.json();
if (Array.isArray(surface.surfaces)) {
Expand Down
6 changes: 3 additions & 3 deletions server/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ export function createApp({
title: input.title?.slice(0, MAX_TITLE),
});
if (!surface) return { error: "session not found", status: 404 };
bus.broadcast({ type: "surface-created", id: surface.id, sessionId, version: 1 });
bus.broadcast({ type: "post-created", id: surface.id, sessionId, version: 1 });
return { surface, userFeedback: await collectFeedback(sessionId) };
}

Expand Down Expand Up @@ -444,7 +444,7 @@ export function createApp({
const surface = await store.updatePost(id, { surfaces: patch.parts, title: patch.title });
if (!surface) return { error: "surface not found", status: 404 };
bus.broadcast({
type: "surface-updated",
type: "post-updated",
id: surface.id,
sessionId: surface.sessionId,
version: surface.version,
Expand Down Expand Up @@ -886,7 +886,7 @@ export function createApp({
const surface = await store.getPost(c.req.param("id"));
if (!surface) return c.json({ error: "surface not found" }, 404);
await store.removePost(surface.id);
bus.broadcast({ type: "surface-deleted", id: surface.id, sessionId: surface.sessionId });
bus.broadcast({ type: "post-deleted", id: surface.id, sessionId: surface.sessionId });
return c.json({ ok: true });
};
app.delete("/api/surfaces/:id", remove);
Expand Down
4 changes: 2 additions & 2 deletions server/events.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export type FeedEvent =
| { type: "session-created" | "session-updated" | "session-deleted"; id: string }
| { type: "surface-created" | "surface-updated"; id: string; sessionId: string; version: number }
| { type: "surface-deleted"; id: string; sessionId: string }
| { type: "post-created" | "post-updated"; id: string; sessionId: string; version: number }
| { type: "post-deleted"; id: string; sessionId: string }
| {
type: "comment-created";
id: string;
Expand Down
3 changes: 1 addition & 2 deletions viewer/src/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,7 @@ export function Card(props: { post: Post; standalone?: boolean }) {
aria-label={`Delete "${props.post.title}"`}
onClick={async () => {
if (confirm(`Delete "${props.post.title}"?`)) {
// /api/surfaces/:id is the legacy wire alias for a post.
await api(`/api/surfaces/${props.post.id}`, { method: "DELETE" });
await api(`/api/posts/${props.post.id}`, { method: "DELETE" });
}
}}
>
Expand Down
24 changes: 9 additions & 15 deletions viewer/src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,17 +183,15 @@ export async function bootstrap() {
// Fetch a post and switch into standalone mode. No-op if already showing it.
export async function enterStandalone(id: string) {
if (standalonePost()?.id === id) return;
// /api/surfaces/:id is the legacy wire alias for fetching a post.
const post = await api<Post>(`/api/surfaces/${encodeURIComponent(id)}`).catch(() => null);
const post = await api<Post>(`/api/posts/${encodeURIComponent(id)}`).catch(() => null);
if (post) setStandaloneInternal(post);
}

export async function refreshSessions(targetPostId?: string | null) {
if (isReadonly() && publicReadMode() === "session") {
const route = host().router.get();
if (!route.sessionId && targetPostId) {
// /api/surfaces/:id is the legacy wire alias for fetching a post.
const target = await api<Post>(`/api/surfaces/${encodeURIComponent(targetPostId)}`).catch(
const target = await api<Post>(`/api/posts/${encodeURIComponent(targetPostId)}`).catch(
() => null,
);
if (!target) return;
Expand All @@ -217,8 +215,7 @@ export async function refreshSessions(targetPostId?: string | null) {
await refreshSessionsQuiet();
if (selected() && !sessions.some((s) => s.id === selected())) setSelectedInternal(null);
if (targetPostId) {
// /api/surfaces/:id is the legacy wire alias for fetching a post.
const target = await api<Post>(`/api/surfaces/${encodeURIComponent(targetPostId)}`).catch(
const target = await api<Post>(`/api/posts/${encodeURIComponent(targetPostId)}`).catch(
() => null,
);
if (target && sessions.some((s) => s.id === target.sessionId)) {
Expand Down Expand Up @@ -268,10 +265,9 @@ export async function select(
setCommentsInternal([]);
setTraceStepsInternal([]);
void fetchTrace(id);
// /api/sessions/:id/surfaces and /api/surfaces/:id are the legacy wire aliases.
const metas = await api<{ id: string }[]>(`/api/sessions/${id}/surfaces`).catch(() => []);
const metas = await api<{ id: string }[]>(`/api/sessions/${id}/posts`).catch(() => []);
const details = (
await Promise.all(metas.map((m) => api<Post>(`/api/surfaces/${m.id}`).catch(() => null)))
await Promise.all(metas.map((m) => api<Post>(`/api/posts/${m.id}`).catch(() => null)))
).filter((s) => s !== null);
if (selected() !== id) return; // user switched away mid-load
setPostsInternal(reconcile(details, { key: "id" }));
Expand Down Expand Up @@ -341,8 +337,7 @@ export async function selectAdjacent(delta: 1 | -1) {

// Fetch a post and insert/update it in the open session's stream.
async function upsertPost(id: string, { scroll = true } = {}) {
// /api/surfaces/:id is the legacy wire alias for fetching a post.
const s = await api<Post>(`/api/surfaces/${id}`).catch(() => null);
const s = await api<Post>(`/api/posts/${id}`).catch(() => null);
if (!s || s.sessionId !== selected()) return;
const idx = posts.findIndex((x) => x.id === s.id);
if (idx >= 0) {
Expand Down Expand Up @@ -452,11 +447,11 @@ export function connect() {
applyTheme(e.id);
} else if (e.type.startsWith("session-")) {
await refreshSessions();
} else if (e.type === "surface-created" || e.type === "surface-updated") {
} else if (e.type === "post-created" || e.type === "post-updated") {
if (away && e.sessionId) markUnread(e.sessionId);
if (e.sessionId === selected()) await upsertPost(e.id);
await refreshSessionsQuiet();
} else if (e.type === "surface-deleted") {
} else if (e.type === "post-deleted") {
const idx = posts.findIndex((s) => s.id === e.id);
if (idx >= 0) setPostsInternal(produce((arr) => arr.splice(idx, 1)));
await refreshSessionsQuiet();
Expand All @@ -481,8 +476,7 @@ async function resyncSelected() {
await refreshSessions();
if (!before || selected() !== before) return; // select() rebuilt the stream
void fetchTrace(before);
// /api/sessions/:id/surfaces is the legacy wire alias.
const metas = await api<{ id: string }[]>(`/api/sessions/${before}/surfaces`).catch(() => []);
const metas = await api<{ id: string }[]>(`/api/sessions/${before}/posts`).catch(() => []);
const ids = new Set(metas.map((m) => m.id));
setPostsInternal(
produce((arr) => {
Expand Down
Loading