mockpit 1.2.0 → 1.3.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 (51) hide show
  1. package/README.md +13 -8
  2. package/bin/mockpit.js +116 -16
  3. package/dist/mcp/server.js +10 -7
  4. package/dist/server/apiViews.d.ts +118 -17
  5. package/dist/server/apiViews.js +66 -8
  6. package/dist/server/app.d.ts +2 -1
  7. package/dist/server/app.js +917 -144
  8. package/dist/server/decisions.d.ts +23 -0
  9. package/dist/server/decisions.js +99 -0
  10. package/dist/server/designGuide.js +45 -10
  11. package/dist/server/embeds.d.ts +25 -0
  12. package/dist/server/embeds.js +118 -0
  13. package/dist/server/eventLog.js +2 -1
  14. package/dist/server/feedbackBatch.d.ts +12 -1
  15. package/dist/server/feedbackBatch.js +20 -3
  16. package/dist/server/fills.d.ts +41 -0
  17. package/dist/server/fills.js +328 -0
  18. package/dist/server/index.js +15 -1
  19. package/dist/server/knobs.d.ts +6 -2
  20. package/dist/server/knobs.js +11 -1
  21. package/dist/server/mcpHttp.d.ts +3 -0
  22. package/dist/server/mcpHttp.js +3 -0
  23. package/dist/server/mcpSpec.d.ts +13 -13
  24. package/dist/server/mcpSpec.js +73 -8
  25. package/dist/server/postSurfaces.js +3 -12
  26. package/dist/server/richRender.js +3 -1
  27. package/dist/server/run.js +4 -0
  28. package/dist/server/runApi.d.ts +1 -1
  29. package/dist/server/runApi.js +4 -3
  30. package/dist/server/sqlStore.d.ts +89 -4
  31. package/dist/server/sqlStore.js +149 -23
  32. package/dist/server/surfacePage.d.ts +6 -2
  33. package/dist/server/surfacePage.js +142 -10
  34. package/dist/server/themes.d.ts +2 -0
  35. package/dist/server/themes.js +23 -3
  36. package/dist/server/types.d.ts +73 -11
  37. package/dist/server/types.js +42 -2
  38. package/dist/server/viewerRequest.d.ts +2 -0
  39. package/dist/server/viewerRequest.js +14 -0
  40. package/guide/topics/asks.md +50 -3
  41. package/guide/topics/feedback.md +89 -11
  42. package/guide/topics/html.md +5 -1
  43. package/guide/topics/http.md +2 -1
  44. package/guide/topics/knobs.md +2 -1
  45. package/guide/topics/surfaces.md +24 -8
  46. package/package.json +4 -13
  47. package/skills/launch-video/SKILL.md +1 -1
  48. package/skills/mockpit/SKILL.md +12 -4
  49. package/viewer/dist/index.html +8 -8
  50. package/dist/server/slots.d.ts +0 -14
  51. package/dist/server/slots.js +0 -71
@@ -0,0 +1,328 @@
1
+ // Slots: a part with alternative fills, declared in the agent's own markup.
2
+ //
3
+ // <section data-part="entry" data-fill="dots">…</section>
4
+ // <section data-part="entry" data-fill="skeleton">…</section>
5
+ //
6
+ // Elements sharing a `data-part` name with two or more distinct `data-fill`
7
+ // values make one slot. The first fill in document order is the default unless
8
+ // one carries `data-fill-default`. A fill may hold further slots: the scan
9
+ // records, for each slot, the fill it sits in, so the viewer can ask about it
10
+ // only while that fill is chosen.
11
+ //
12
+ // String-only and runtime-agnostic, like parts.ts: identity is read from the
13
+ // markup, never from a DOM. The scan is a tolerant tag walker, not a parser:
14
+ // it tracks open elements to know containment and each fill's extent, skips
15
+ // comments and raw-text elements, and treats an unclosed element as ending at
16
+ // the next close of an outer one.
17
+ const VOID = new Set([
18
+ "area",
19
+ "base",
20
+ "br",
21
+ "col",
22
+ "embed",
23
+ "hr",
24
+ "img",
25
+ "input",
26
+ "link",
27
+ "meta",
28
+ "param",
29
+ "source",
30
+ "track",
31
+ "wbr",
32
+ ]);
33
+ const RAW = new Set(["script", "style", "textarea", "title"]);
34
+ const TOKEN = /<!--[\s\S]*?-->|<\/([A-Za-z][\w:-]*)\s*>|<([A-Za-z][\w:-]*)((?:\s[^<>]*?)?)(\/?)>/g;
35
+ // Longest names first, and each must end at the name's end: `data-fill`
36
+ // must not match the start of `data-fill-label`.
37
+ const ATTR = /\s(data-part-label|data-part|data-fill-label|data-fill-default|data-fill)(?=[\s=/>]|$)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
38
+ // A slot's knob path is its part name, so the name must be a valid path segment.
39
+ const PATH_SEGMENT = /^[A-Za-z_][\w-]{0,63}$/;
40
+ const MAX_SLOTS = 100;
41
+ const decode = (s) => s
42
+ .replace(/&quot;/g, '"')
43
+ .replace(/&#39;|&apos;/g, "'")
44
+ .replace(/&lt;/g, "<")
45
+ .replace(/&gt;/g, ">")
46
+ .replace(/&amp;/g, "&")
47
+ .trim()
48
+ .slice(0, 200);
49
+ function attributes(raw) {
50
+ const out = {};
51
+ ATTR.lastIndex = 0;
52
+ let m;
53
+ while ((m = ATTR.exec(raw)))
54
+ out[m[1]] = decode(m[2] ?? m[3] ?? m[4] ?? "");
55
+ return out;
56
+ }
57
+ export function scanFills(html) {
58
+ const { slots, ranges } = scanAll(html);
59
+ const real = slots.filter((s) => s.fills.length >= 2);
60
+ const isSlot = new Set(real.map((s) => s.part));
61
+ return { slots: real, ranges: ranges.filter((r) => isSlot.has(r.part)) };
62
+ }
63
+ // Every part carrying `data-fill`, a lone fill included: a splice may add the
64
+ // second fill that makes it a slot.
65
+ function scanAll(html) {
66
+ const ranges = [];
67
+ // Per part, in document order: each fill element and the fill it sits in.
68
+ const seen = new Map();
69
+ const stack = [];
70
+ const close = (i, end) => {
71
+ for (let j = stack.length - 1; j >= i; j--) {
72
+ const f = stack[j].fill;
73
+ if (f)
74
+ f.end = end;
75
+ }
76
+ stack.length = i;
77
+ };
78
+ TOKEN.lastIndex = 0;
79
+ let m;
80
+ while ((m = TOKEN.exec(html))) {
81
+ if (m[0].startsWith("<!--"))
82
+ continue;
83
+ if (m[1]) {
84
+ const tag = m[1].toLowerCase();
85
+ for (let i = stack.length - 1; i >= 0; i--) {
86
+ if (stack[i].tag === tag) {
87
+ close(i, TOKEN.lastIndex);
88
+ break;
89
+ }
90
+ }
91
+ continue;
92
+ }
93
+ const tag = m[2].toLowerCase();
94
+ const attrs = attributes(m[3] ?? "");
95
+ let fill = null;
96
+ const part = attrs["data-part"];
97
+ const name = attrs["data-fill"];
98
+ if (part && name && PATH_SEGMENT.test(part)) {
99
+ fill = { part, fill: name, start: m.index, end: html.length, tagEnd: TOKEN.lastIndex };
100
+ ranges.push(fill);
101
+ let slot = seen.get(part);
102
+ if (!slot && seen.size < MAX_SLOTS) {
103
+ const outer = [...stack].reverse().find((o) => o.fill)?.fill ?? null;
104
+ slot = {
105
+ fills: [],
106
+ defaults: [],
107
+ parent: outer ? { part: outer.part, fill: outer.fill } : null,
108
+ };
109
+ seen.set(part, slot);
110
+ }
111
+ if (slot) {
112
+ if (attrs["data-part-label"] && !slot.label)
113
+ slot.label = attrs["data-part-label"];
114
+ if (!slot.fills.some((f) => f.name === name)) {
115
+ slot.fills.push({
116
+ name,
117
+ ...(attrs["data-fill-label"] ? { label: attrs["data-fill-label"] } : {}),
118
+ });
119
+ }
120
+ if ("data-fill-default" in attrs)
121
+ slot.defaults.push(name);
122
+ }
123
+ }
124
+ if (VOID.has(tag) || m[4] === "/") {
125
+ if (fill)
126
+ fill.end = TOKEN.lastIndex;
127
+ continue;
128
+ }
129
+ if (RAW.has(tag)) {
130
+ // Skip to the matching close: markup inside script or style is text.
131
+ const endTag = new RegExp(`</${tag}\\s*>`, "gi");
132
+ endTag.lastIndex = TOKEN.lastIndex;
133
+ const e = endTag.exec(html);
134
+ TOKEN.lastIndex = e ? endTag.lastIndex : html.length;
135
+ if (fill)
136
+ fill.end = TOKEN.lastIndex;
137
+ continue;
138
+ }
139
+ stack.push({ tag, fill });
140
+ }
141
+ const slots = [];
142
+ for (const [part, s] of seen) {
143
+ slots.push({
144
+ part,
145
+ ...(s.label ? { label: s.label } : {}),
146
+ fills: s.fills,
147
+ default: s.defaults[0] ?? s.fills[0].name,
148
+ parent: s.parent,
149
+ });
150
+ }
151
+ return { slots, ranges };
152
+ }
153
+ // Each slot is a select knob at its part's path: the fills are its options and
154
+ // the default fill is its value. A knob the agent declares at the same path
155
+ // wins (to relabel the options), so these are merged under declared knobs.
156
+ export function slotKnobs(slots) {
157
+ const out = {};
158
+ for (const s of slots) {
159
+ const config = {
160
+ type: "select",
161
+ options: s.fills.map((f) => (f.label ? { value: f.name, label: f.label } : f.name)),
162
+ value: s.default,
163
+ };
164
+ out[s.part] = config;
165
+ }
166
+ return out;
167
+ }
168
+ const cssString = (s) => `"${s.replace(/[\\"]/g, (c) => `\\${c}`).replace(/[\n\r<>]/g, (c) => `\\${c.charCodeAt(0).toString(16)} `)}"`;
169
+ // Shows only the chosen fill of each slot, keyed on the `data-k-<part>`
170
+ // attribute the knob runtime keeps on <html>. The chosen fill is never styled:
171
+ // only the others are hidden, so a fill keeps whatever display the agent gave it.
172
+ export function slotCss(slots) {
173
+ const rules = [];
174
+ for (const s of slots) {
175
+ const attr = `data-k-${s.part}`;
176
+ for (const f of s.fills) {
177
+ const el = `[data-part=${cssString(s.part)}][data-fill=${cssString(f.name)}]`;
178
+ const chosen = `[${attr}=${cssString(f.name)}]`;
179
+ rules.push(f.name === s.default
180
+ ? `html[${attr}]:not(${chosen}) ${el}{display:none!important}`
181
+ : `html:not(${chosen}) ${el}{display:none!important}`);
182
+ }
183
+ }
184
+ return rules.join("\n");
185
+ }
186
+ const FILL_ATTRS = /\s(data-fill-label|data-fill-default|data-fill)(?=[\s=/>]|$)(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?/g;
187
+ // The screen as chosen: every slot reduced to its chosen fill, the others cut
188
+ // out with whatever they contained, and the fill attributes dropped from the
189
+ // fills that stay. `chosen` maps a slot's part to a fill; a slot it does not
190
+ // name keeps its default.
191
+ export function composeFills(html, chosen) {
192
+ const { slots, ranges } = scanFills(html);
193
+ const pick = new Map(slots.map((s) => [s.part, chosen[s.part] ?? s.default]));
194
+ const cut = ranges.filter((r) => pick.get(r.part) !== r.fill);
195
+ const kept = ranges.filter((r) => pick.get(r.part) === r.fill);
196
+ const edits = [];
197
+ for (const r of cut) {
198
+ if (cut.some((o) => o !== r && o.start <= r.start && r.end <= o.end))
199
+ continue;
200
+ edits.push({ start: r.start, end: r.end, text: "" });
201
+ }
202
+ for (const r of kept) {
203
+ if (cut.some((o) => o.start <= r.start && r.end <= o.end))
204
+ continue;
205
+ edits.push({
206
+ start: r.start,
207
+ end: r.tagEnd,
208
+ text: html.slice(r.start, r.tagEnd).replace(FILL_ATTRS, ""),
209
+ });
210
+ }
211
+ edits.sort((a, b) => b.start - a.start);
212
+ let out = html;
213
+ for (const e of edits)
214
+ out = out.slice(0, e.start) + e.text + out.slice(e.end);
215
+ return out;
216
+ }
217
+ // A post's slots across its html surfaces (the first surface that declares a
218
+ // part wins), cached per surfaces array: a stored post never mutates it.
219
+ const bySurfaces = new WeakMap();
220
+ export function slotsInSurfaces(surfaces) {
221
+ const hit = bySurfaces.get(surfaces);
222
+ if (hit)
223
+ return hit;
224
+ const out = [];
225
+ for (const sf of surfaces) {
226
+ if (sf.kind !== "html")
227
+ continue;
228
+ for (const slot of scanFills(sf.html).slots) {
229
+ if (!out.some((o) => o.part === slot.part))
230
+ out.push(slot);
231
+ }
232
+ }
233
+ bySurfaces.set(surfaces, out);
234
+ return out;
235
+ }
236
+ const attrValue = (s) => s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
237
+ const FIRST_TAG = /^(?:\s|<!--[\s\S]*?-->)*<([A-Za-z][\w:-]*)((?:\s[^<>]*?)?)(\/?)>/;
238
+ // The replacement must be one element marked as that fill: the marks are added
239
+ // when missing and a contradicting one is refused, so a splice cannot quietly
240
+ // land in another slot.
241
+ function markFill(html, part, fill) {
242
+ const m = FIRST_TAG.exec(html);
243
+ if (!m)
244
+ return { error: `fill "${part}#${fill}" must be html starting with its element` };
245
+ const attrs = attributes(m[2] ?? "");
246
+ if (attrs["data-part"] !== undefined && attrs["data-part"] !== part) {
247
+ return { error: `fill "${part}#${fill}" is marked data-part="${attrs["data-part"]}"` };
248
+ }
249
+ if (attrs["data-fill"] !== undefined && attrs["data-fill"] !== fill) {
250
+ return { error: `fill "${part}#${fill}" is marked data-fill="${attrs["data-fill"]}"` };
251
+ }
252
+ const add = (attrs["data-part"] === undefined ? ` data-part="${attrValue(part)}"` : "") +
253
+ (attrs["data-fill"] === undefined ? ` data-fill="${attrValue(fill)}"` : "");
254
+ const at = m.index + m[0].indexOf("<") + 1 + m[1].length;
255
+ return html.slice(0, at) + add + html.slice(at);
256
+ }
257
+ // Replace one fill of a slot, or add it after the slot's last fill, keyed
258
+ // "part#fill". Like spliceParts, every target is located in the original
259
+ // markup and overlapping edits are refused.
260
+ export function spliceFills(surfaces, edits) {
261
+ const docs = surfaces.flatMap((s, i) => (s.kind === "html" ? [{ i, surface: s }] : []));
262
+ if (docs.length === 0) {
263
+ return { ok: false, error: "fills needs an html surface; this variant has none" };
264
+ }
265
+ const scans = docs.map((d) => scanAll(d.surface.html).ranges);
266
+ const plan = new Map();
267
+ const targets = Object.keys(edits);
268
+ for (const [n, target] of targets.entries()) {
269
+ const at = target.indexOf("#");
270
+ const part = at > 0 ? target.slice(0, at) : "";
271
+ const fill = at > 0 ? target.slice(at + 1).trim() : "";
272
+ if (!PATH_SEGMENT.test(part) || !fill) {
273
+ return {
274
+ ok: false,
275
+ error: `fills keys are "part#fill", e.g. "entry#skeleton" (got "${target}")`,
276
+ };
277
+ }
278
+ const where = scans.flatMap((rs, d) => (rs.some((r) => r.part === part) ? [d] : []));
279
+ if (where.length === 0) {
280
+ return {
281
+ ok: false,
282
+ error: `no fills of "${part}"; mark its alternatives data-part="${part}" data-fill="<name>" with html or parts first`,
283
+ };
284
+ }
285
+ if (where.length > 1) {
286
+ return {
287
+ ok: false,
288
+ error: `fills of "${part}" are in several html surfaces; revise with surfaces`,
289
+ };
290
+ }
291
+ const d = where[0];
292
+ const own = scans[d].filter((r) => r.part === part);
293
+ const same = own.filter((r) => r.fill === fill);
294
+ if (same.length > 1) {
295
+ return {
296
+ ok: false,
297
+ error: `fill "${target}" appears ${same.length} times; revise with html or parts`,
298
+ };
299
+ }
300
+ const text = markFill(edits[target], part, fill);
301
+ if (typeof text !== "string")
302
+ return { ok: false, error: text.error };
303
+ const edit = same.length
304
+ ? { start: same[0].start, end: same[0].end, text, n }
305
+ : (() => {
306
+ const end = Math.max(...own.map((r) => r.end));
307
+ return { start: end, end, text: `\n${text}`, n };
308
+ })();
309
+ const list = plan.get(d) ?? [];
310
+ const clash = list.find((e) => e.start < edit.end && edit.start < e.end);
311
+ if (clash) {
312
+ return { ok: false, error: `fills "${targets[clash.n]}" and "${target}" overlap; send one` };
313
+ }
314
+ list.push(edit);
315
+ plan.set(d, list);
316
+ }
317
+ const next = [...surfaces];
318
+ for (const [d, list] of plan) {
319
+ const { i, surface } = docs[d];
320
+ let html = surface.html;
321
+ // From the end, and same-place additions last-listed first, so they land in listed order.
322
+ for (const e of list.toSorted((a, b) => b.start - a.start || b.n - a.n)) {
323
+ html = html.slice(0, e.start) + e.text + html.slice(e.end);
324
+ }
325
+ next[i] = { ...surface, html };
326
+ }
327
+ return { ok: true, surfaces: next, applied: targets };
328
+ }
@@ -1,5 +1,6 @@
1
1
  import { serve } from "@hono/node-server";
2
- import { readFile } from "node:fs/promises";
2
+ import { createHash } from "node:crypto";
3
+ import { readdir, readFile } from "node:fs/promises";
3
4
  import { homedir } from "node:os";
4
5
  import { basename, dirname, join } from "node:path";
5
6
  import { fileURLToPath } from "node:url";
@@ -25,6 +26,18 @@ const [viewerHtml, setupText, topicTexts, pkgJson] = await Promise.all([
25
26
  readFile(join(root, "package.json"), "utf8"),
26
27
  ]);
27
28
  const topics = Object.fromEntries(GUIDE_TOPICS.map((id, i) => [id, topicTexts[i]]));
29
+ // What renders surface documents: this server's own files and the locked
30
+ // library versions. Hashed whole rather than picked apart, so an edit in dev or
31
+ // an upgrade both reach browsers holding an old document; a change elsewhere
32
+ // in the server only costs one re-fetch per document.
33
+ const serverDir = dirname(fileURLToPath(import.meta.url));
34
+ const renderHash = createHash("sha256");
35
+ for (const name of (await readdir(serverDir)).sort()) {
36
+ if (/\.(ts|js)$/.test(name))
37
+ renderHash.update(await readFile(join(serverDir, name)));
38
+ }
39
+ renderHash.update(await readFile(join(root, "package-lock.json")).catch(() => pkgJson));
40
+ const renderRev = renderHash.digest("hex").slice(0, 10);
28
41
  const pr = process.env.MOCKPIT_PUBLIC_READ;
29
42
  const publicRead = pr === "session" || pr === "full" ? pr : undefined;
30
43
  // Storage: SQLite via node:sqlite, the same SqlStore the Cloudflare Durable
@@ -55,6 +68,7 @@ const app = createApp({
55
68
  // notice); set it to the empty string to disable the update check
56
69
  version: process.env.MOCKPIT_VERSION ?? JSON.parse(pkgJson).version,
57
70
  upgradeCommand: "npm install -g mockpit",
71
+ renderRev,
58
72
  // `run` scripts execute in QuickJS inside a worker thread per run.
59
73
  executor: createNodeExecutor(),
60
74
  log: logPath ? createFileLog(logPath) : undefined,
@@ -10,6 +10,10 @@ export declare function explicitKnob(config: KnobConfig): ExplicitKnob;
10
10
  export declare function checkKnobConfig(path: string, raw: unknown): Checked<KnobConfig>;
11
11
  export declare function checkKnobs(raw: unknown): Checked<Knobs>;
12
12
  export declare function checkKnobValue(path: string, config: KnobConfig, value: unknown): Checked<KnobValue>;
13
- export declare function knobFor(path: string, mock: Pick<Mock, "knobs">, posts: Pick<Post, "knobs">[]): KnobConfig | undefined;
14
- export declare function checkKnobValues(raw: unknown, mock: Pick<Mock, "knobs">, posts: Pick<Post, "knobs">[], what: string): Checked<Record<string, KnobValue>>;
13
+ export declare function knobFor(path: string, mock: Pick<Mock, "knobs">, posts: (Pick<Post, "knobs"> & {
14
+ surfaces?: Post["surfaces"];
15
+ })[]): KnobConfig | undefined;
16
+ export declare function checkKnobValues(raw: unknown, mock: Pick<Mock, "knobs">, posts: (Pick<Post, "knobs"> & {
17
+ surfaces?: Post["surfaces"];
18
+ })[], what: string): Checked<Record<string, KnobValue>>;
15
19
  export declare function discreteChoices(config: KnobConfig): number | null;
@@ -1,6 +1,7 @@
1
1
  // Knob schema and value validation (plan Q3: values are checked against the
2
2
  // declared schema before they are stored or used). Runtime-agnostic. The config
3
3
  // shape mirrors tunekit's usePane config; see the knob types in types.ts.
4
+ import { slotKnobs, slotsInSurfaces } from "./fills.js";
4
5
  const MAX_KNOBS = 100;
5
6
  const MAX_OPTIONS = 100;
6
7
  const MAX_STRING = 500;
@@ -292,13 +293,22 @@ export function checkKnobValue(path, config, value) {
292
293
  }
293
294
  }
294
295
  }
295
- // The declared knob for a path: the mock's own, else a variant's override.
296
+ // The declared knob for a path: the mock's own, else a variant's override,
297
+ // else the knob a variant's slot implies (fills.ts), so an option can `set` a
298
+ // slot's fill without the agent declaring anything.
296
299
  export function knobFor(path, mock, posts) {
297
300
  if (Object.hasOwn(mock.knobs, path))
298
301
  return mock.knobs[path];
299
302
  for (const p of posts)
300
303
  if (p.knobs && Object.hasOwn(p.knobs, path))
301
304
  return p.knobs[path];
305
+ for (const p of posts) {
306
+ if (!p.surfaces)
307
+ continue;
308
+ const implied = slotKnobs(slotsInSurfaces(p.surfaces));
309
+ if (Object.hasOwn(implied, path))
310
+ return implied[path];
311
+ }
302
312
  return undefined;
303
313
  }
304
314
  export function checkKnobValues(raw, mock, posts, what) {
@@ -8,6 +8,9 @@ export interface McpFlows {
8
8
  list: Flow<[query: {
9
9
  project?: string;
10
10
  }]>;
11
+ tree: Flow<[query: {
12
+ project?: string;
13
+ }]>;
11
14
  get: Flow<[ref: unknown, query: {
12
15
  project?: unknown;
13
16
  body?: boolean;
@@ -35,6 +35,9 @@ export function registerMcp(app, deps) {
35
35
  case "ask":
36
36
  return unwrap(await flows.ask(args.mock, args, ctx));
37
37
  case "read":
38
+ if (args.mock === undefined && args.tree === true) {
39
+ return unwrap(await flows.tree({ project: args.project }));
40
+ }
38
41
  if (args.mock === undefined)
39
42
  return unwrap(await flows.list({ project: args.project }));
40
43
  return unwrap(await flows.get(args.mock, {
@@ -53,7 +53,7 @@ type Param = {
53
53
  d?: string;
54
54
  req?: boolean;
55
55
  } | {
56
- t: "surfaces" | "asks" | "object";
56
+ t: "surfaces" | "asks" | "object" | "integers";
57
57
  d?: string;
58
58
  req?: boolean;
59
59
  };
@@ -131,9 +131,9 @@ export declare const STDIO_MCP_TOOLS: ({
131
131
  _meta: {
132
132
  "anthropic/alwaysLoad": boolean;
133
133
  };
134
- outputSchema?: z.ZodObject<Readonly<{
135
- [k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
136
- }>, z.core.$loose> | undefined;
134
+ outputSchema?: z.ZodObject<{
135
+ [x: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
136
+ }, z.core.$loose> | undefined;
137
137
  name: string;
138
138
  description: string;
139
139
  inputSchema: Readonly<{
@@ -141,9 +141,9 @@ export declare const STDIO_MCP_TOOLS: ({
141
141
  }>;
142
142
  } | {
143
143
  _meta?: undefined;
144
- outputSchema?: z.ZodObject<Readonly<{
145
- [k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
146
- }>, z.core.$loose> | undefined;
144
+ outputSchema?: z.ZodObject<{
145
+ [x: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
146
+ }, z.core.$loose> | undefined;
147
147
  name: string;
148
148
  description: string;
149
149
  inputSchema: Readonly<{
@@ -154,9 +154,9 @@ export declare const STDIO_RUN_TOOLS: ({
154
154
  _meta: {
155
155
  "anthropic/alwaysLoad": boolean;
156
156
  };
157
- outputSchema?: z.ZodObject<Readonly<{
158
- [k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
159
- }>, z.core.$loose> | undefined;
157
+ outputSchema?: z.ZodObject<{
158
+ [x: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
159
+ }, z.core.$loose> | undefined;
160
160
  name: string;
161
161
  description: string;
162
162
  inputSchema: Readonly<{
@@ -164,9 +164,9 @@ export declare const STDIO_RUN_TOOLS: ({
164
164
  }>;
165
165
  } | {
166
166
  _meta?: undefined;
167
- outputSchema?: z.ZodObject<Readonly<{
168
- [k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
169
- }>, z.core.$loose> | undefined;
167
+ outputSchema?: z.ZodObject<{
168
+ [x: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
169
+ }, z.core.$loose> | undefined;
170
170
  name: string;
171
171
  description: string;
172
172
  inputSchema: Readonly<{