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.
- package/README.md +66 -202
- package/bin/demoData.js +5 -5
- package/bin/initDesign.js +6 -6
- package/bin/mockpit.js +687 -192
- package/bin/poster.js +377 -0
- package/dist/mcp/server.js +110 -19
- package/dist/server/answerAdditions.d.ts +7 -0
- package/dist/server/answerAdditions.js +76 -0
- package/dist/server/apiViews.d.ts +563 -41
- package/dist/server/apiViews.js +223 -74
- package/dist/server/app.d.ts +10 -2
- package/dist/server/app.js +3284 -782
- package/dist/server/appIcon.js +15 -5
- package/dist/server/appIconPng.js +3 -3
- package/dist/server/askNudges.js +1 -1
- package/dist/server/cdnInline.d.ts +8 -0
- package/dist/server/cdnInline.js +638 -0
- package/dist/server/codeWorker.js +2 -1
- package/dist/server/decisionList.d.ts +45 -0
- package/dist/server/decisionList.js +302 -0
- package/dist/server/decisions.d.ts +8 -8
- package/dist/server/decisions.js +18 -17
- package/dist/server/designGuide.d.ts +10 -6
- package/dist/server/designGuide.js +173 -78
- package/dist/server/embeds.d.ts +4 -1
- package/dist/server/embeds.js +57 -6
- package/dist/server/eventLog.d.ts +2 -2
- package/dist/server/eventLog.js +11 -11
- package/dist/server/events.d.ts +9 -6
- package/dist/server/feedbackBatch.d.ts +38 -16
- package/dist/server/feedbackBatch.js +121 -50
- package/dist/server/fills.d.ts +4 -0
- package/dist/server/fills.js +55 -1
- package/dist/server/guidance.d.ts +23 -0
- package/dist/server/guidance.js +71 -0
- package/dist/server/handoff.d.ts +4 -1
- package/dist/server/handoff.js +8 -2
- package/dist/server/icons.js +1 -1
- package/dist/server/index.js +49 -11
- package/dist/server/jump.d.ts +21 -0
- package/dist/server/jump.js +175 -0
- package/dist/server/knobs.d.ts +3 -3
- package/dist/server/knobs.js +6 -6
- package/dist/server/mcpHttp.d.ts +8 -1
- package/dist/server/mcpHttp.js +44 -7
- package/dist/server/mcpSpec.d.ts +2 -1
- package/dist/server/mcpSpec.js +172 -50
- package/dist/server/parts.js +9 -1
- package/dist/server/postSurfaces.js +3 -0
- package/dist/server/run.d.ts +1 -1
- package/dist/server/run.js +31 -17
- package/dist/server/runApi.d.ts +3 -3
- package/dist/server/runApi.js +34 -24
- package/dist/server/sqlStore.d.ts +117 -94
- package/dist/server/sqlStore.js +1089 -141
- package/dist/server/standings.d.ts +25 -0
- package/dist/server/standings.js +113 -0
- package/dist/server/surfacePage.d.ts +6 -9
- package/dist/server/surfacePage.js +361 -86
- package/dist/server/types.d.ts +252 -69
- package/dist/server/types.js +87 -69
- package/extensions/mockpit.js +245 -65
- package/guide/AGENT_SETUP.md +13 -1
- package/guide/packs/design-craft.md +34 -0
- package/guide/topics/asks.md +117 -69
- package/guide/topics/feedback.md +156 -119
- package/guide/topics/html.md +19 -32
- package/guide/topics/http.md +43 -40
- package/guide/topics/knobs.md +7 -10
- package/guide/topics/scripts.md +24 -35
- package/guide/topics/surfaces.md +50 -41
- package/opencode/mockpit.js +120 -0
- package/opencode/package.json +8 -0
- package/package.json +4 -6
- package/skills/launch-video/SKILL.md +12 -12
- package/skills/mockpit/SKILL.md +35 -23
- package/viewer/dist/index.html +9 -8
package/dist/server/apiViews.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// Response shapes shared by every tier (REST, both MCP transports, the CLI), so
|
|
2
|
-
// one
|
|
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 {
|
|
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
|
-
|
|
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 ? {
|
|
57
|
-
...(post.
|
|
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.
|
|
66
|
-
...(
|
|
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
|
|
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 = (
|
|
89
|
-
// What the user put in use, per state, in the
|
|
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 = (
|
|
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(
|
|
97
|
-
return stateOrder(
|
|
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
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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
|
-
|
|
118
|
-
|
|
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:
|
|
121
|
-
project:
|
|
122
|
-
slug:
|
|
123
|
-
title:
|
|
124
|
-
kind:
|
|
125
|
-
|
|
126
|
-
|
|
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(
|
|
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:
|
|
132
|
-
|
|
133
|
-
createdAt:
|
|
134
|
-
updatedAt:
|
|
135
|
-
thumbnail: thumbnail(
|
|
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
|
|
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
|
|
141
|
-
id:
|
|
142
|
-
project:
|
|
143
|
-
slug:
|
|
144
|
-
title:
|
|
145
|
-
kind:
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
|
167
|
-
// variants per state with status and lineage, and the
|
|
168
|
-
// `
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
182
|
-
|
|
183
|
-
inUse: inUseByState(
|
|
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.
|
|
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.
|
|
195
|
-
...(p
|
|
196
|
-
...(
|
|
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
|
});
|
package/dist/server/app.d.ts
CHANGED
|
@@ -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
|
-
|
|
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, "/">;
|