mockpit 1.3.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +66 -202
  2. package/bin/demoData.js +5 -5
  3. package/bin/initDesign.js +6 -6
  4. package/bin/mockpit.js +687 -192
  5. package/bin/poster.js +377 -0
  6. package/dist/mcp/server.js +110 -19
  7. package/dist/server/answerAdditions.d.ts +7 -0
  8. package/dist/server/answerAdditions.js +76 -0
  9. package/dist/server/apiViews.d.ts +563 -41
  10. package/dist/server/apiViews.js +223 -74
  11. package/dist/server/app.d.ts +10 -2
  12. package/dist/server/app.js +3284 -782
  13. package/dist/server/appIcon.js +15 -5
  14. package/dist/server/appIconPng.js +3 -3
  15. package/dist/server/askNudges.js +1 -1
  16. package/dist/server/cdnInline.d.ts +8 -0
  17. package/dist/server/cdnInline.js +638 -0
  18. package/dist/server/codeWorker.js +2 -1
  19. package/dist/server/decisionList.d.ts +45 -0
  20. package/dist/server/decisionList.js +302 -0
  21. package/dist/server/decisions.d.ts +8 -8
  22. package/dist/server/decisions.js +18 -17
  23. package/dist/server/designGuide.d.ts +10 -6
  24. package/dist/server/designGuide.js +173 -78
  25. package/dist/server/embeds.d.ts +4 -1
  26. package/dist/server/embeds.js +57 -6
  27. package/dist/server/eventLog.d.ts +2 -2
  28. package/dist/server/eventLog.js +11 -11
  29. package/dist/server/events.d.ts +9 -6
  30. package/dist/server/feedbackBatch.d.ts +38 -16
  31. package/dist/server/feedbackBatch.js +121 -50
  32. package/dist/server/fills.d.ts +4 -0
  33. package/dist/server/fills.js +55 -1
  34. package/dist/server/guidance.d.ts +23 -0
  35. package/dist/server/guidance.js +71 -0
  36. package/dist/server/handoff.d.ts +4 -1
  37. package/dist/server/handoff.js +8 -2
  38. package/dist/server/icons.js +1 -1
  39. package/dist/server/index.js +49 -11
  40. package/dist/server/jump.d.ts +21 -0
  41. package/dist/server/jump.js +175 -0
  42. package/dist/server/knobs.d.ts +3 -3
  43. package/dist/server/knobs.js +6 -6
  44. package/dist/server/mcpHttp.d.ts +8 -1
  45. package/dist/server/mcpHttp.js +44 -7
  46. package/dist/server/mcpSpec.d.ts +2 -1
  47. package/dist/server/mcpSpec.js +172 -50
  48. package/dist/server/parts.js +9 -1
  49. package/dist/server/postSurfaces.js +3 -0
  50. package/dist/server/run.d.ts +1 -1
  51. package/dist/server/run.js +31 -17
  52. package/dist/server/runApi.d.ts +3 -3
  53. package/dist/server/runApi.js +34 -24
  54. package/dist/server/sqlStore.d.ts +117 -94
  55. package/dist/server/sqlStore.js +1089 -141
  56. package/dist/server/standings.d.ts +25 -0
  57. package/dist/server/standings.js +113 -0
  58. package/dist/server/surfacePage.d.ts +6 -9
  59. package/dist/server/surfacePage.js +361 -86
  60. package/dist/server/types.d.ts +252 -69
  61. package/dist/server/types.js +87 -69
  62. package/extensions/mockpit.js +245 -65
  63. package/guide/AGENT_SETUP.md +13 -1
  64. package/guide/packs/design-craft.md +34 -0
  65. package/guide/topics/asks.md +117 -69
  66. package/guide/topics/feedback.md +156 -119
  67. package/guide/topics/html.md +19 -32
  68. package/guide/topics/http.md +43 -40
  69. package/guide/topics/knobs.md +7 -10
  70. package/guide/topics/scripts.md +24 -35
  71. package/guide/topics/surfaces.md +50 -41
  72. package/opencode/mockpit.js +120 -0
  73. package/opencode/package.json +8 -0
  74. package/package.json +4 -6
  75. package/skills/launch-video/SKILL.md +12 -12
  76. package/skills/mockpit/SKILL.md +35 -23
  77. package/viewer/dist/index.html +9 -8
@@ -1,8 +1,9 @@
1
1
  // Response shapes shared by every tier (REST, both MCP transports, the CLI), so
2
- // one mock reads the same however an agent asks for it.
2
+ // one design reads the same however an agent asks for it.
3
3
  import { slotKnobs, slotsInSurfaces } from "./fills.js";
4
4
  import { mergeParts, partsInSurfaces } from "./parts.js";
5
- import { draftedCount, inUseVersion, isSandboxedSurfaceKind, isLive, openCount, SURFACE_CONTENT_FIELDS, } from "./types.js";
5
+ import { isKeptVariant, keptVariants } from "./standings.js";
6
+ import { changedTo, currentVersion, draftedCount, inUseVersion, isPlanned, isSandboxedSurfaceKind, isLive, openCount, recommendedVariant, SURFACE_CONTENT_FIELDS, variantStatus, } from "./types.js";
6
7
  export const surfaceRef = (surface, index) => ({
7
8
  id: surface.id,
8
9
  kind: surface.kind,
@@ -25,6 +26,72 @@ export const hydratedSurfaceView = (surface, index) => {
25
26
  delete view[field];
26
27
  return view;
27
28
  };
29
+ // A still mockpit took shows the page in the mode it was taken in. In the other mode it
30
+ // would be the one picture in the wrong colours among live pages, so it is left out and the
31
+ // page runs as it did before posters. Applied to posts before any view reads them, so every
32
+ // view agrees. Posts come from the store's cache: copied where changed, never written to.
33
+ const otherMode = (s, mode) => {
34
+ const taken = s.posterMode;
35
+ return s.kind === "html" && taken !== undefined && taken !== mode;
36
+ };
37
+ const surfacesFor = (surfaces, mode) => surfaces.some((s) => otherMode(s, mode))
38
+ ? surfaces.map((s) => {
39
+ if (!otherMode(s, mode))
40
+ return s;
41
+ const { poster: _poster, posterMode: _mode, ...rest } = s;
42
+ return rest;
43
+ })
44
+ : surfaces;
45
+ export const postersFor = (posts, mode) => posts.map((p) => {
46
+ const surfaces = surfacesFor(p.surfaces, mode);
47
+ const history = p.history.map((h) => {
48
+ const own = surfacesFor(h.surfaces, mode);
49
+ return own === h.surfaces ? h : { ...h, surfaces: own };
50
+ });
51
+ return surfaces === p.surfaces && history.every((h, i) => h === p.history[i])
52
+ ? p
53
+ : { ...p, surfaces, history };
54
+ });
55
+ // The still overviews show for a version: its html page's poster.
56
+ const pagePoster = (surfaces) => surfaces.find((s) => s.kind === "html")?.poster;
57
+ const posterOf = (surfaces) => {
58
+ const poster = pagePoster(surfaces);
59
+ return poster ? { poster } : {};
60
+ };
61
+ // The poster of one version of a variant: what Home shows for it.
62
+ const posterAt = (post, version) => pagePoster((version === post.version
63
+ ? post.surfaces
64
+ : post.history.find((h) => h.version === version)?.surfaces) ?? []);
65
+ // The in-use version's poster, when Home shows an older version than the
66
+ // newest.
67
+ const inUsePoster = (p) => {
68
+ if (p.acceptedVersion === undefined || p.acceptedVersion === p.version)
69
+ return {};
70
+ const poster = posterAt(p, p.acceptedVersion);
71
+ return poster ? { inUsePoster: poster } : {};
72
+ };
73
+ // The version Home shows when nothing is in use and the newest was hidden.
74
+ const coverVersion = (p) => {
75
+ const at = currentVersion(p);
76
+ if (p.status === "accepted" || at === p.version)
77
+ return {};
78
+ const poster = posterAt(p, at);
79
+ return { coverVersion: at, ...(poster ? { coverPoster: poster } : {}) };
80
+ };
81
+ // A page that draws on a canvas may hold a WebGL context, of which a browser
82
+ // keeps about 16 and drops the oldest past that, so overviews ration these
83
+ // frames and no others. Three.js and the like make their canvas from script.
84
+ // Asked once per version: a read lists every version, with bodies up to 1 MB.
85
+ const CANVAS_PAGE = /<canvas[\s>]|webgl|three(?:\.module|\.min|\.js|@)|['"]three['"]|pixi|babylon/i;
86
+ const canvasPages = new WeakMap();
87
+ const drawsOnCanvas = (surfaces) => {
88
+ let hit = canvasPages.get(surfaces);
89
+ if (hit === undefined) {
90
+ hit = surfaces.some((s) => s.kind === "html" && CANVAS_PAGE.test(s.html));
91
+ canvasPages.set(surfaces, hit);
92
+ }
93
+ return hit;
94
+ };
28
95
  // One past version without its bodies: enough to list versions or pick one to
29
96
  // fetch, at a bounded cost.
30
97
  export const historyMetaView = (version) => ({
@@ -34,13 +101,16 @@ export const historyMetaView = (version) => ({
34
101
  ...(version.from === undefined ? {} : { from: version.from }),
35
102
  ...(version.prompt ? { prompt: version.prompt } : {}),
36
103
  ...(version.author === undefined ? {} : { author: version.author }),
104
+ ...(version.amendedAt ? { amendedAt: version.amendedAt } : {}),
37
105
  surfaceKinds: version.surfaces.map((s) => s.kind),
106
+ ...posterOf(version.surfaces),
107
+ ...(drawsOnCanvas(version.surfaces) ? { canvas: true } : {}),
38
108
  });
39
109
  export const variantView = (post, opts = {}) => ({
40
110
  postId: post.id,
41
111
  state: post.state,
42
112
  variant: post.variant,
43
- status: post.status,
113
+ status: variantStatus(post.status),
44
114
  version: post.version,
45
115
  title: post.title,
46
116
  sessionId: post.sessionId,
@@ -49,12 +119,15 @@ export const variantView = (post, opts = {}) => ({
49
119
  ...(post.from === undefined ? {} : { from: post.from }),
50
120
  ...(post.prompt ? { prompt: post.prompt } : {}),
51
121
  ...(post.author === undefined ? {} : { author: post.author }),
52
- // A slot is a select knob on its part (fills.ts); declared knobs win.
122
+ ...(post.amendedAt ? { amendedAt: post.amendedAt } : {}),
123
+ // Frame URLs carry it (`w`): an amended version keeps its number.
124
+ ...(post.rewrites ? { rewrites: post.rewrites } : {}),
125
+ // A part with fills is a select knob on its part (fills.ts); declared knobs win.
53
126
  ...(slotsInSurfaces(post.surfaces).length || post.knobs
54
127
  ? { knobs: { ...slotKnobs(slotsInSurfaces(post.surfaces)), ...post.knobs } }
55
128
  : {}),
56
- ...(slotsInSurfaces(post.surfaces).length ? { slots: slotsInSurfaces(post.surfaces) } : {}),
57
- ...(post.note ? { note: post.note } : {}),
129
+ ...(slotsInSurfaces(post.surfaces).length ? { fills: slotsInSurfaces(post.surfaces) } : {}),
130
+ ...(post.description ? { description: post.description } : {}),
58
131
  // Embedded components as they render now (server/embeds.ts); `publishedWith`
59
132
  // is what this version rendered with when it was published.
60
133
  ...(opts.embeds?.get(post.id)
@@ -62,10 +135,13 @@ export const variantView = (post, opts = {}) => ({
62
135
  : {}),
63
136
  ...(post.embeds.length ? { publishedWith: post.embeds } : {}),
64
137
  ...(post.forkedFrom ? { forkedFrom: post.forkedFrom } : {}),
65
- ...(post.starred ? { starred: true } : {}),
66
- ...(post.acceptedVersion === undefined ? {} : { acceptedVersion: post.acceptedVersion }),
138
+ ...(post.copiedFrom ? { copiedFrom: post.copiedFrom } : {}),
139
+ ...(opts.kept && isKeptVariant(opts.kept, post) ? { kept: true } : {}),
140
+ ...(post.acceptedVersion === undefined ? {} : { inUseVersion: post.acceptedVersion }),
141
+ ...(changedTo(post) === undefined ? {} : { changedTo: changedTo(post) }),
142
+ ...(post.archiveReason ? { archiveReason: post.archiveReason } : {}),
67
143
  // The part names this variant's own markup marks, so the viewer can tell
68
- // which looks render a part (Mix) without the html bodies.
144
+ // which variants render a part (Borrow) without the html bodies.
69
145
  parts: partsInSurfaces(post.surfaces).map((p) => p.name),
70
146
  surfaces: post.surfaces.map(opts.body ? fullSurfaceView : hydratedSurfaceView),
71
147
  ...(opts.history
@@ -79,82 +155,124 @@ export const variantView = (post, opts = {}) => ({
79
155
  ...(post.from === undefined ? {} : { from: post.from }),
80
156
  ...(post.prompt === undefined ? {} : { prompt: post.prompt }),
81
157
  ...(post.author === undefined ? {} : { author: post.author }),
158
+ ...(post.amendedAt ? { amendedAt: post.amendedAt } : {}),
82
159
  }),
83
160
  ...[...post.history].sort((a, b) => b.version - a.version).map(historyMetaView),
84
161
  ].map((row) => post.hiddenVersions?.includes(row.version) ? { ...row, hidden: true } : row),
85
162
  }
86
163
  : {}),
87
164
  });
88
- const stateOrder = (mock) => (mock.states.length ? mock.states : [null]);
89
- // What the user put in use, per state, in the mock's order; a state with
165
+ const stateOrder = (design) => (design.states.length ? design.states : [null]);
166
+ // What the user put in use, per state, in the design's order; a state with
90
167
  // nothing accepted is absent. Export and embeds resolve to exactly these.
91
- export const inUseByState = (mock, posts) => stateOrder(mock).flatMap((state) => {
168
+ export const inUseByState = (design, posts) => stateOrder(design).flatMap((state) => {
92
169
  const p = posts.find((x) => x.state === state && x.status === "accepted");
93
170
  return p ? [{ state, variant: p.variant, version: inUseVersion(p) }] : [];
94
171
  });
95
172
  // The parts the agent marked, per state, across the variants still in play.
96
- export function partsByState(mock, posts) {
97
- return stateOrder(mock).map((state) => ({
173
+ export function partsByState(design, posts) {
174
+ return stateOrder(design).map((state) => ({
98
175
  state,
99
176
  parts: mergeParts(posts
100
177
  .filter((p) => p.state === state && isLive(p.status))
101
178
  .map((p) => partsInSurfaces(p.surfaces))),
102
179
  }));
103
180
  }
104
- // What Home shows for a mock: the first state's chosen (else first) variant.
105
- function thumbnail(mock, posts) {
106
- const first = stateOrder(mock)[0];
181
+ // Where a design sits in the product and the code that implements it, only once
182
+ // declared, so the views of a project that names neither cost nothing.
183
+ export const placeOf = (design) => ({
184
+ ...(design.area ? { area: design.area } : {}),
185
+ ...(design.source ? { source: design.source } : {}),
186
+ });
187
+ // What a design's first state looks like in a list: what's in use, else what the agent
188
+ // recommends, else its first. Same rule as viewer/src/logic.ts screenPicture.
189
+ function thumbnail(design, posts) {
190
+ const first = stateOrder(design)[0];
107
191
  const inState = posts.filter((p) => p.state === first);
192
+ const recommended = recommendedVariant(design.asks, first);
108
193
  const pick = inState.find((p) => p.status === "accepted") ??
194
+ inState.find((p) => p.variant === recommended && isLive(p.status)) ??
109
195
  inState.find((p) => p.status === "open") ??
110
196
  inState[0] ??
111
197
  posts[0];
112
198
  if (!pick)
113
199
  return null;
114
200
  const surface = pick.surfaces.findIndex((s) => isSandboxedSurfaceKind(s.kind));
115
- return { postId: pick.id, surface: Math.max(surface, 0), version: inUseVersion(pick) };
201
+ const version = currentVersion(pick);
202
+ const poster = posterAt(pick, version);
203
+ return {
204
+ postId: pick.id,
205
+ surface: Math.max(surface, 0),
206
+ version,
207
+ ...(poster ? { poster } : {}),
208
+ ...(pick.rewrites ? { rewrites: pick.rewrites } : {}),
209
+ };
116
210
  }
117
- export const mockSummaryView = (mock, posts) => {
118
- const drafted = draftedCount(mock, posts);
211
+ // Only a plan says so, so the views of everything drawn are unchanged.
212
+ const plannedOf = (design, variants) => isPlanned(design, variants) ? { planned: true } : {};
213
+ // Anything the user has yet to send. A stored draft can be an emptied one, which
214
+ // waits on nobody.
215
+ export const draftHolds = (draft) => !!draft &&
216
+ (draft.comments.length > 0 ||
217
+ Object.values(draft.answers).some((a) => !Array.isArray(a) || a.length > 0) ||
218
+ [draft.borrowed, draft.tuned, draft.others ?? {}, draft.askComments ?? {}].some((rec) => Object.keys(rec).length > 0));
219
+ export const designSummaryView = (design, posts) => {
220
+ const drafted = draftedCount(design);
119
221
  return {
120
- id: mock.id,
121
- project: mock.project,
122
- slug: mock.slug,
123
- title: mock.title,
124
- kind: mock.kind,
125
- states: mock.states,
126
- stateCount: Math.max(mock.states.length, 1),
222
+ id: design.id,
223
+ project: design.project,
224
+ slug: design.slug,
225
+ title: design.title,
226
+ kind: design.kind,
227
+ ...placeOf(design),
228
+ ...plannedOf(design, posts.length),
229
+ // Text comes with a read of the design; a list only says there is some to read.
230
+ ...(design.notes ? { hasNotes: true } : {}),
231
+ states: design.states,
232
+ stateCount: Math.max(design.states.length, 1),
127
233
  variants: posts.length,
128
- open: openCount(mock, posts),
234
+ open: openCount(design),
129
235
  // Only once the user has started one, so a list with no drafts costs nothing.
130
236
  ...(drafted === null ? {} : { drafted }),
131
- sessionId: mock.sessionId,
132
- resolvedAt: mock.resolvedAt,
133
- createdAt: mock.createdAt,
134
- updatedAt: mock.updatedAt,
135
- thumbnail: thumbnail(mock, posts),
237
+ sessionId: design.sessionId,
238
+ archivedAt: design.archivedAt,
239
+ createdAt: design.createdAt,
240
+ updatedAt: design.updatedAt,
241
+ thumbnail: thumbnail(design, posts),
136
242
  };
137
243
  };
138
- // One mock as anyone outside the viewer's own draft sees it: the draft is the
244
+ // One design as anyone outside the viewer's own draft sees it: the draft is the
139
245
  // user's unsent work and never leaves through this view.
140
- export const mockDetailView = (mock, posts, opts = {}) => ({
141
- id: mock.id,
142
- project: mock.project,
143
- slug: mock.slug,
144
- title: mock.title,
145
- kind: mock.kind,
146
- states: mock.states,
147
- asks: mock.asks,
148
- open: openCount(mock, posts),
149
- knobs: mock.knobs,
150
- widths: mock.widths,
151
- sessionId: mock.sessionId,
152
- resolvedAt: mock.resolvedAt,
153
- createdAt: mock.createdAt,
154
- updatedAt: mock.updatedAt,
155
- variants: posts.map((p) => variantView(p, opts)),
156
- inUse: inUseByState(mock, posts),
157
- parts: partsByState(mock, posts),
246
+ export const designDetailView = (design, posts, opts = {}) => ({
247
+ id: design.id,
248
+ project: design.project,
249
+ slug: design.slug,
250
+ title: design.title,
251
+ kind: design.kind,
252
+ ...placeOf(design),
253
+ ...(design.shortlist ? { shortlist: true } : {}),
254
+ // No agent has written to this shortlist yet: who its replies reach for now.
255
+ ...(opts.repliesGoTo ? { repliesGoTo: opts.repliesGoTo } : {}),
256
+ ...plannedOf(design, posts.length),
257
+ states: design.states,
258
+ ...(design.notes ? { notes: design.notes } : {}),
259
+ ...(Object.keys(design.stateOutlines).length ? { stateOutlines: design.stateOutlines } : {}),
260
+ ...(design.answerAdditions.length ? { answerAdditions: design.answerAdditions } : {}),
261
+ asks: design.asks,
262
+ // By question id: the answers the user kept for later and hid, with why.
263
+ ...(Object.keys(design.standings).length ? { standings: design.standings } : {}),
264
+ ...(design.sets.length ? { sets: design.sets } : {}),
265
+ open: openCount(design),
266
+ knobs: design.knobs,
267
+ widths: design.widths,
268
+ sessionId: design.sessionId,
269
+ archivedAt: design.archivedAt,
270
+ ...(design.archiveReason ? { archiveReason: design.archiveReason } : {}),
271
+ createdAt: design.createdAt,
272
+ updatedAt: design.updatedAt,
273
+ variants: ((kept) => posts.map((p) => variantView(p, { ...opts, kept })))(keptVariants(design, posts)),
274
+ inUse: inUseByState(design, posts),
275
+ parts: partsByState(design, posts),
158
276
  // Every knob value the user has sent (answers' sets and tuning, later replies
159
277
  // winning), never drafts. Later questions render on top of it.
160
278
  settled: opts.settled ?? {},
@@ -163,36 +281,67 @@ export const sessionRowView = (session, postCount) => ({
163
281
  ...session,
164
282
  postCount,
165
283
  });
166
- // One mock as a node of the project tree (GET /api/projects/:name/tree): its
167
- // variants per state with status and lineage, and the mocks it embeds (`uses`);
168
- // `usedIn` is filled in once every node is known.
169
- export const treeNodeView = (mock, posts, extra) => ({
170
- id: mock.id,
171
- slug: mock.slug,
172
- title: mock.title,
173
- kind: mock.kind,
174
- open: openCount(mock, posts),
175
- states: mock.states.length ? mock.states : [null],
176
- widths: mock.widths,
177
- createdAt: mock.createdAt,
178
- updatedAt: mock.updatedAt,
179
- resolvedAt: mock.resolvedAt,
284
+ // One design as a node of the project tree (GET /api/projects/:name/tree): its
285
+ // variants per state with status and lineage, and the designs it embeds (`embeds`);
286
+ // `embeddedIn` is filled in once every node is known.
287
+ // When it left the shelf: archived by hand, or with the last of its variants archived.
288
+ // Designs archived before the date was kept fall back on the design's last change.
289
+ function leftOf(design, posts) {
290
+ if (design.archivedAt)
291
+ return { leftAt: design.archivedAt };
292
+ if (!posts.length || posts.some((p) => isLive(p.status)))
293
+ return {};
294
+ const last = posts.reduce((t, p) => (p.archivedAt && p.archivedAt > t ? p.archivedAt : t), "");
295
+ return { leftAt: last || design.updatedAt };
296
+ }
297
+ export const treeNodeView = (design, posts, extra) => ({
298
+ id: design.id,
299
+ slug: design.slug,
300
+ title: design.title,
301
+ kind: design.kind,
302
+ ...placeOf(design),
303
+ ...(design.shortlist ? { shortlist: true } : {}),
304
+ ...plannedOf(design, posts.length),
305
+ open: openCount(design),
306
+ // Any question ever asked, answered or not: it tells an idea from a document.
307
+ ...(design.asks.length ? { asked: design.asks.length } : {}),
308
+ // Later than every variant: the user has spoken and it is the agent's turn.
309
+ ...(extra.lastReplyAt ? { lastReplyAt: extra.lastReplyAt } : {}),
310
+ ...(extra.lastSend ? { lastSend: extra.lastSend } : {}),
311
+ ...(extra.drafting ? { drafting: true } : {}),
312
+ states: stateOrder(design),
313
+ widths: design.widths,
314
+ createdAt: design.createdAt,
315
+ updatedAt: design.updatedAt,
316
+ archivedAt: design.archivedAt,
317
+ ...leftOf(design, posts),
318
+ ...(design.archiveReason ? { archiveReason: design.archiveReason } : {}),
180
319
  settled: extra.settled,
181
- uses: extra.uses,
182
- usedIn: [],
183
- inUse: inUseByState(mock, posts),
320
+ embeds: extra.embeds,
321
+ embeddedIn: [],
322
+ inUse: inUseByState(design, posts),
323
+ // What the agent recommends where nothing is in use: a cover shows it.
324
+ recommended: stateOrder(design).flatMap((state) => {
325
+ const variant = recommendedVariant(design.asks, state);
326
+ return variant ? [{ state, variant }] : [];
327
+ }),
184
328
  variants: posts.map((p) => ({
185
329
  postId: p.id,
186
330
  state: p.state,
187
331
  variant: p.variant,
188
- status: p.status,
332
+ status: variantStatus(p.status),
189
333
  version: p.version,
190
334
  updatedAt: p.updatedAt,
335
+ ...(p.rewrites ? { rewrites: p.rewrites } : {}),
191
336
  surface: p.surfaces.findIndex((x) => isSandboxedSurfaceKind(x.kind)),
192
- ...(p.note ? { note: p.note } : {}),
337
+ ...posterOf(p.surfaces),
338
+ ...inUsePoster(p),
339
+ ...coverVersion(p),
340
+ ...(p.description && !extra.noDescriptions ? { description: p.description } : {}),
193
341
  ...(p.forkedFrom ? { forkedFrom: p.forkedFrom } : {}),
194
- ...(p.starred ? { starred: true } : {}),
195
- ...(p.acceptedVersion === undefined ? {} : { acceptedVersion: p.acceptedVersion }),
196
- ...(extra.embeds.get(p.id) ? { embedsKey: extra.embeds.get(p.id).key } : {}),
342
+ ...(p.acceptedVersion === undefined ? {} : { inUseVersion: p.acceptedVersion }),
343
+ ...(changedTo(p) === undefined ? {} : { changedTo: changedTo(p) }),
344
+ ...(p.archiveReason ? { archiveReason: p.archiveReason } : {}),
345
+ ...(extra.embedViews.get(p.id) ? { embedsKey: extra.embedViews.get(p.id).key } : {}),
197
346
  })),
198
347
  });
@@ -1,6 +1,7 @@
1
1
  import { Hono } from "hono";
2
2
  import { type FeedEvent } from "./events.ts";
3
3
  import { type GuideTopic } from "./designGuide.ts";
4
+ import { type GuidePack } from "./guidance.ts";
4
5
  import { type Executor, type RunLimits } from "./run.ts";
5
6
  import { type LogSink } from "./eventLog.ts";
6
7
  import { type Store } from "./types.ts";
@@ -14,6 +15,8 @@ export interface AppOptions {
14
15
  viewerHtml: string;
15
16
  setupText: string;
16
17
  topics?: Partial<Record<GuideTopic, string>>;
18
+ packs?: Partial<Record<GuidePack, string>>;
19
+ userRules?: () => Promise<string | null>;
17
20
  authenticate?: AuthenticateHook;
18
21
  authToken?: string;
19
22
  basePath?: BasePathHook;
@@ -27,6 +30,10 @@ export interface AppOptions {
27
30
  maxHoldConnections?: number;
28
31
  executor?: Executor;
29
32
  runLimits?: Partial<RunLimits>;
33
+ networkHosts?: () => {
34
+ localOnly: boolean;
35
+ hosts: string[];
36
+ };
30
37
  log?: LogSink;
31
38
  }
32
39
  export interface LatestRelease {
@@ -35,12 +42,13 @@ export interface LatestRelease {
35
42
  }
36
43
  export interface CommentWait {
37
44
  sessionId?: string;
38
- mockId?: string;
45
+ designId?: string;
39
46
  postId?: string;
40
47
  author?: string;
41
48
  afterSeq?: number;
42
49
  waitSeconds: number;
43
50
  agent?: boolean;
51
+ peek?: boolean;
44
52
  }
45
53
  export interface FlowResult {
46
54
  status: number;
@@ -53,4 +61,4 @@ export interface FlowContext {
53
61
  request?: Request;
54
62
  signal?: AbortSignal;
55
63
  }
56
- export declare function createApp({ store, viewerHtml, setupText, topics, authenticate, authToken, basePath, publicRead, screenshots, version, upgradeCommand, renderRev, fetchLatestRelease, onEvent, maxHoldConnections, executor, runLimits, log, }: AppOptions): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
64
+ export declare function createApp({ store, viewerHtml, setupText, topics, packs, userRules, authenticate, authToken, basePath, publicRead, screenshots, version, upgradeCommand, renderRev, fetchLatestRelease, onEvent, maxHoldConnections, executor, runLimits, networkHosts, log, }: AppOptions): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;