mockpit 1.3.0 → 2.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 +61 -45
- package/bin/demoData.js +3 -3
- package/bin/mockpit.js +327 -52
- package/dist/mcp/server.js +35 -0
- package/dist/server/apiViews.d.ts +308 -23
- package/dist/server/apiViews.js +60 -19
- package/dist/server/app.d.ts +4 -1
- package/dist/server/app.js +608 -119
- package/dist/server/codeWorker.js +2 -1
- package/dist/server/decisions.d.ts +5 -5
- package/dist/server/decisions.js +10 -9
- package/dist/server/designGuide.d.ts +6 -2
- package/dist/server/designGuide.js +77 -43
- package/dist/server/embeds.d.ts +1 -0
- package/dist/server/embeds.js +30 -4
- package/dist/server/feedbackBatch.d.ts +6 -6
- package/dist/server/feedbackBatch.js +18 -11
- package/dist/server/guidance.d.ts +23 -0
- package/dist/server/guidance.js +71 -0
- package/dist/server/index.js +10 -1
- package/dist/server/mcpHttp.d.ts +3 -0
- package/dist/server/mcpHttp.js +32 -0
- package/dist/server/mcpSpec.d.ts +2 -1
- package/dist/server/mcpSpec.js +74 -24
- package/dist/server/postSurfaces.js +3 -0
- package/dist/server/run.js +6 -1
- package/dist/server/runApi.d.ts +3 -3
- package/dist/server/runApi.js +16 -12
- package/dist/server/sqlStore.d.ts +10 -3
- package/dist/server/sqlStore.js +230 -16
- package/dist/server/types.d.ts +46 -17
- package/dist/server/types.js +34 -11
- package/extensions/mockpit.js +179 -16
- package/guide/packs/design-craft.md +35 -0
- package/guide/topics/asks.md +60 -56
- package/guide/topics/feedback.md +104 -74
- package/guide/topics/html.md +5 -11
- package/guide/topics/http.md +6 -3
- package/guide/topics/knobs.md +2 -2
- package/guide/topics/scripts.md +2 -2
- package/guide/topics/surfaces.md +28 -10
- package/package.json +1 -4
- package/skills/launch-video/SKILL.md +1 -1
- package/skills/mockpit/SKILL.md +18 -10
- package/viewer/dist/index.html +8 -7
package/dist/server/apiViews.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// one mock 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 { draftedCount, inUseVersion, isSandboxedSurfaceKind, isLive, momentsOf, openCount, SURFACE_CONTENT_FIELDS, variantStatus, } from "./types.js";
|
|
6
6
|
export const surfaceRef = (surface, index) => ({
|
|
7
7
|
id: surface.id,
|
|
8
8
|
kind: surface.kind,
|
|
@@ -25,6 +25,27 @@ export const hydratedSurfaceView = (surface, index) => {
|
|
|
25
25
|
delete view[field];
|
|
26
26
|
return view;
|
|
27
27
|
};
|
|
28
|
+
// The still overviews show for a version: its html page's poster.
|
|
29
|
+
const pagePoster = (surfaces) => {
|
|
30
|
+
const page = surfaces.find((s) => s.kind === "html");
|
|
31
|
+
return page?.kind === "html" ? page.poster : undefined;
|
|
32
|
+
};
|
|
33
|
+
const posterOf = (surfaces) => {
|
|
34
|
+
const poster = pagePoster(surfaces);
|
|
35
|
+
return poster ? { poster } : {};
|
|
36
|
+
};
|
|
37
|
+
// The poster of one version of a variant: what Home shows for it.
|
|
38
|
+
const posterAt = (post, version) => pagePoster((version === post.version
|
|
39
|
+
? post.surfaces
|
|
40
|
+
: post.history.find((h) => h.version === version)?.surfaces) ?? []);
|
|
41
|
+
// The in-use version's poster, when Home shows an older version than the
|
|
42
|
+
// newest.
|
|
43
|
+
const inUsePoster = (p) => {
|
|
44
|
+
if (p.acceptedVersion === undefined || p.acceptedVersion === p.version)
|
|
45
|
+
return {};
|
|
46
|
+
const poster = posterAt(p, p.acceptedVersion);
|
|
47
|
+
return poster ? { inUsePoster: poster } : {};
|
|
48
|
+
};
|
|
28
49
|
// One past version without its bodies: enough to list versions or pick one to
|
|
29
50
|
// fetch, at a bounded cost.
|
|
30
51
|
export const historyMetaView = (version) => ({
|
|
@@ -35,12 +56,13 @@ export const historyMetaView = (version) => ({
|
|
|
35
56
|
...(version.prompt ? { prompt: version.prompt } : {}),
|
|
36
57
|
...(version.author === undefined ? {} : { author: version.author }),
|
|
37
58
|
surfaceKinds: version.surfaces.map((s) => s.kind),
|
|
59
|
+
...posterOf(version.surfaces),
|
|
38
60
|
});
|
|
39
61
|
export const variantView = (post, opts = {}) => ({
|
|
40
62
|
postId: post.id,
|
|
41
63
|
state: post.state,
|
|
42
64
|
variant: post.variant,
|
|
43
|
-
status: post.status,
|
|
65
|
+
status: variantStatus(post.status),
|
|
44
66
|
version: post.version,
|
|
45
67
|
title: post.title,
|
|
46
68
|
sessionId: post.sessionId,
|
|
@@ -49,11 +71,11 @@ export const variantView = (post, opts = {}) => ({
|
|
|
49
71
|
...(post.from === undefined ? {} : { from: post.from }),
|
|
50
72
|
...(post.prompt ? { prompt: post.prompt } : {}),
|
|
51
73
|
...(post.author === undefined ? {} : { author: post.author }),
|
|
52
|
-
// A
|
|
74
|
+
// A part with fills is a select knob on its part (fills.ts); declared knobs win.
|
|
53
75
|
...(slotsInSurfaces(post.surfaces).length || post.knobs
|
|
54
76
|
? { knobs: { ...slotKnobs(slotsInSurfaces(post.surfaces)), ...post.knobs } }
|
|
55
77
|
: {}),
|
|
56
|
-
...(slotsInSurfaces(post.surfaces).length ? {
|
|
78
|
+
...(slotsInSurfaces(post.surfaces).length ? { fills: slotsInSurfaces(post.surfaces) } : {}),
|
|
57
79
|
...(post.note ? { note: post.note } : {}),
|
|
58
80
|
// Embedded components as they render now (server/embeds.ts); `publishedWith`
|
|
59
81
|
// is what this version rendered with when it was published.
|
|
@@ -63,9 +85,9 @@ export const variantView = (post, opts = {}) => ({
|
|
|
63
85
|
...(post.embeds.length ? { publishedWith: post.embeds } : {}),
|
|
64
86
|
...(post.forkedFrom ? { forkedFrom: post.forkedFrom } : {}),
|
|
65
87
|
...(post.starred ? { starred: true } : {}),
|
|
66
|
-
...(post.acceptedVersion === undefined ? {} : {
|
|
88
|
+
...(post.acceptedVersion === undefined ? {} : { inUseVersion: post.acceptedVersion }),
|
|
67
89
|
// The part names this variant's own markup marks, so the viewer can tell
|
|
68
|
-
// which
|
|
90
|
+
// which variants render a part (Borrow) without the html bodies.
|
|
69
91
|
parts: partsInSurfaces(post.surfaces).map((p) => p.name),
|
|
70
92
|
surfaces: post.surfaces.map(opts.body ? fullSurfaceView : hydratedSurfaceView),
|
|
71
93
|
...(opts.history
|
|
@@ -101,6 +123,12 @@ export function partsByState(mock, posts) {
|
|
|
101
123
|
.map((p) => partsInSurfaces(p.surfaces))),
|
|
102
124
|
}));
|
|
103
125
|
}
|
|
126
|
+
// Where a mock sits in the product and the code that implements it, only once
|
|
127
|
+
// declared, so the views of a project that names neither cost nothing.
|
|
128
|
+
export const placeOf = (mock) => ({
|
|
129
|
+
...(mock.area ? { area: mock.area } : {}),
|
|
130
|
+
...(mock.source ? { source: mock.source } : {}),
|
|
131
|
+
});
|
|
104
132
|
// What Home shows for a mock: the first state's chosen (else first) variant.
|
|
105
133
|
function thumbnail(mock, posts) {
|
|
106
134
|
const first = stateOrder(mock)[0];
|
|
@@ -112,8 +140,13 @@ function thumbnail(mock, posts) {
|
|
|
112
140
|
if (!pick)
|
|
113
141
|
return null;
|
|
114
142
|
const surface = pick.surfaces.findIndex((s) => isSandboxedSurfaceKind(s.kind));
|
|
115
|
-
|
|
143
|
+
const version = inUseVersion(pick);
|
|
144
|
+
const poster = posterAt(pick, version);
|
|
145
|
+
return { postId: pick.id, surface: Math.max(surface, 0), version, ...(poster ? { poster } : {}) };
|
|
116
146
|
}
|
|
147
|
+
// A mock's situations, only once it has any, so the views of mocks without
|
|
148
|
+
// them are unchanged.
|
|
149
|
+
const situationsOf = (mock) => Object.keys(mock.situations).length ? { situations: mock.situations } : {};
|
|
117
150
|
export const mockSummaryView = (mock, posts) => {
|
|
118
151
|
const drafted = draftedCount(mock, posts);
|
|
119
152
|
return {
|
|
@@ -122,14 +155,16 @@ export const mockSummaryView = (mock, posts) => {
|
|
|
122
155
|
slug: mock.slug,
|
|
123
156
|
title: mock.title,
|
|
124
157
|
kind: mock.kind,
|
|
158
|
+
...placeOf(mock),
|
|
125
159
|
states: mock.states,
|
|
126
|
-
|
|
160
|
+
...situationsOf(mock),
|
|
161
|
+
stateCount: Math.max(momentsOf(mock.states, mock.situations).length, 1),
|
|
127
162
|
variants: posts.length,
|
|
128
163
|
open: openCount(mock, posts),
|
|
129
164
|
// Only once the user has started one, so a list with no drafts costs nothing.
|
|
130
165
|
...(drafted === null ? {} : { drafted }),
|
|
131
166
|
sessionId: mock.sessionId,
|
|
132
|
-
|
|
167
|
+
closedAt: mock.closedAt,
|
|
133
168
|
createdAt: mock.createdAt,
|
|
134
169
|
updatedAt: mock.updatedAt,
|
|
135
170
|
thumbnail: thumbnail(mock, posts),
|
|
@@ -143,13 +178,15 @@ export const mockDetailView = (mock, posts, opts = {}) => ({
|
|
|
143
178
|
slug: mock.slug,
|
|
144
179
|
title: mock.title,
|
|
145
180
|
kind: mock.kind,
|
|
181
|
+
...placeOf(mock),
|
|
146
182
|
states: mock.states,
|
|
183
|
+
...situationsOf(mock),
|
|
147
184
|
asks: mock.asks,
|
|
148
185
|
open: openCount(mock, posts),
|
|
149
186
|
knobs: mock.knobs,
|
|
150
187
|
widths: mock.widths,
|
|
151
188
|
sessionId: mock.sessionId,
|
|
152
|
-
|
|
189
|
+
closedAt: mock.closedAt,
|
|
153
190
|
createdAt: mock.createdAt,
|
|
154
191
|
updatedAt: mock.updatedAt,
|
|
155
192
|
variants: posts.map((p) => variantView(p, opts)),
|
|
@@ -164,35 +201,39 @@ export const sessionRowView = (session, postCount) => ({
|
|
|
164
201
|
postCount,
|
|
165
202
|
});
|
|
166
203
|
// 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 (`
|
|
168
|
-
// `
|
|
204
|
+
// variants per state with status and lineage, and the mocks it embeds (`embeds`);
|
|
205
|
+
// `embeddedIn` is filled in once every node is known.
|
|
169
206
|
export const treeNodeView = (mock, posts, extra) => ({
|
|
170
207
|
id: mock.id,
|
|
171
208
|
slug: mock.slug,
|
|
172
209
|
title: mock.title,
|
|
173
210
|
kind: mock.kind,
|
|
211
|
+
...placeOf(mock),
|
|
174
212
|
open: openCount(mock, posts),
|
|
175
|
-
|
|
213
|
+
// Home shows a mock's states, never their situations.
|
|
214
|
+
states: mock.states.length ? momentsOf(mock.states, mock.situations) : [null],
|
|
176
215
|
widths: mock.widths,
|
|
177
216
|
createdAt: mock.createdAt,
|
|
178
217
|
updatedAt: mock.updatedAt,
|
|
179
|
-
|
|
218
|
+
closedAt: mock.closedAt,
|
|
180
219
|
settled: extra.settled,
|
|
181
|
-
|
|
182
|
-
|
|
220
|
+
embeds: extra.embeds,
|
|
221
|
+
embeddedIn: [],
|
|
183
222
|
inUse: inUseByState(mock, posts),
|
|
184
223
|
variants: posts.map((p) => ({
|
|
185
224
|
postId: p.id,
|
|
186
225
|
state: p.state,
|
|
187
226
|
variant: p.variant,
|
|
188
|
-
status: p.status,
|
|
227
|
+
status: variantStatus(p.status),
|
|
189
228
|
version: p.version,
|
|
190
229
|
updatedAt: p.updatedAt,
|
|
191
230
|
surface: p.surfaces.findIndex((x) => isSandboxedSurfaceKind(x.kind)),
|
|
231
|
+
...posterOf(p.surfaces),
|
|
232
|
+
...inUsePoster(p),
|
|
192
233
|
...(p.note ? { note: p.note } : {}),
|
|
193
234
|
...(p.forkedFrom ? { forkedFrom: p.forkedFrom } : {}),
|
|
194
235
|
...(p.starred ? { starred: true } : {}),
|
|
195
|
-
...(p.acceptedVersion === undefined ? {} : {
|
|
196
|
-
...(extra.
|
|
236
|
+
...(p.acceptedVersion === undefined ? {} : { inUseVersion: p.acceptedVersion }),
|
|
237
|
+
...(extra.embedViews.get(p.id) ? { embedsKey: extra.embedViews.get(p.id).key } : {}),
|
|
197
238
|
})),
|
|
198
239
|
});
|
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;
|
|
@@ -53,4 +56,4 @@ export interface FlowContext {
|
|
|
53
56
|
request?: Request;
|
|
54
57
|
signal?: AbortSignal;
|
|
55
58
|
}
|
|
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, "/">;
|
|
59
|
+
export declare function createApp({ store, viewerHtml, setupText, topics, packs, userRules, authenticate, authToken, basePath, publicRead, screenshots, version, upgradeCommand, renderRev, fetchLatestRelease, onEvent, maxHoldConnections, executor, runLimits, log, }: AppOptions): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|