@mindstudio-ai/remy 0.1.308 → 0.1.310
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/dist/headless.js +281 -188
- package/dist/index.js +300 -204
- package/dist/prompt/compiled/interfaces.md +2 -0
- package/dist/prompt/compiled/platform.md +1 -1
- package/dist/prompt/compiled/sdk-actions.md +4 -0
- package/dist/prompt/skills/agentInterfaces.md +7 -1
- package/dist/prompt/skills/realtimeEvents.md +80 -0
- package/dist/prompt/static/intake.md +1 -1
- package/dist/subagents/designExpert/skills/chatExperience.md +15 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4459,6 +4459,7 @@ async function renderHtmlViaSidecar(opts) {
|
|
|
4459
4459
|
html: opts.html,
|
|
4460
4460
|
width: opts.width,
|
|
4461
4461
|
height: opts.height,
|
|
4462
|
+
...opts.autoHeight ? { autoHeight: true } : {},
|
|
4462
4463
|
...opts.transparent ? { transparent: true } : {},
|
|
4463
4464
|
...opts.scale != null ? { scale: opts.scale } : {}
|
|
4464
4465
|
},
|
|
@@ -5624,7 +5625,7 @@ The first-party SDK (@mindstudio-ai/agent) provides access to 200+ AI models (Op
|
|
|
5624
5625
|
## What Remy apps are NOT good for
|
|
5625
5626
|
|
|
5626
5627
|
- Native mobile apps (iOS/Android). Mobile-responsive web apps are fine.
|
|
5627
|
-
-
|
|
5628
|
+
- Fast-twitch multiplayer and live co-editing (shared cursors, 60fps sync) \u2014 client\u2192server is always a method invoke, so sub-100ms bidirectional interaction isn't a fit.
|
|
5628
5629
|
</platform_brief>`;
|
|
5629
5630
|
}
|
|
5630
5631
|
var init_context = __esm({
|
|
@@ -6045,58 +6046,293 @@ var init_scrapeWebUrl = __esm({
|
|
|
6045
6046
|
}
|
|
6046
6047
|
});
|
|
6047
6048
|
|
|
6048
|
-
// src/subagents/designExpert/tools/
|
|
6049
|
-
var
|
|
6050
|
-
__export(
|
|
6049
|
+
// src/subagents/designExpert/tools/images/renderImage.ts
|
|
6050
|
+
var renderImage_exports = {};
|
|
6051
|
+
__export(renderImage_exports, {
|
|
6052
|
+
RENDER_ANALYZE_PROMPT: () => RENDER_ANALYZE_PROMPT,
|
|
6051
6053
|
definition: () => definition3,
|
|
6052
6054
|
execute: () => execute3
|
|
6053
6055
|
});
|
|
6056
|
+
import { mkdir, unlink, writeFile } from "fs/promises";
|
|
6057
|
+
import { tmpdir } from "os";
|
|
6058
|
+
import { dirname, join, resolve as resolve2, sep } from "path";
|
|
6059
|
+
import { randomUUID } from "crypto";
|
|
6054
6060
|
async function execute3(input, onLog, context) {
|
|
6055
|
-
const
|
|
6056
|
-
const
|
|
6057
|
-
const
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6061
|
+
const html = typeof input.html === "string" ? input.html : "";
|
|
6062
|
+
const width = Math.round(Number(input.width));
|
|
6063
|
+
const height = Math.round(Number(input.height));
|
|
6064
|
+
if (!html || !Number.isFinite(width) || !Number.isFinite(height) || width < MIN_DIMENSION || width > MAX_DIMENSION || height < MIN_DIMENSION || height > MAX_DIMENSION) {
|
|
6065
|
+
return `Error: renderImage requires an html string plus width/height between ${MIN_DIMENSION} and ${MAX_DIMENSION}.`;
|
|
6066
|
+
}
|
|
6067
|
+
const release = await acquireBrowserLock();
|
|
6068
|
+
let rendered;
|
|
6069
|
+
try {
|
|
6070
|
+
onLog?.("Rendering document in the sandbox browser...");
|
|
6071
|
+
rendered = await renderHtmlViaSidecar({
|
|
6072
|
+
html,
|
|
6073
|
+
width,
|
|
6074
|
+
height,
|
|
6075
|
+
transparent: input.transparentBackground === true,
|
|
6076
|
+
scale: typeof input.scale === "number" ? input.scale : void 0
|
|
6077
|
+
});
|
|
6078
|
+
} catch (err) {
|
|
6079
|
+
return `Error: render failed: ${err?.message ?? err}`;
|
|
6080
|
+
} finally {
|
|
6081
|
+
release();
|
|
6082
|
+
}
|
|
6083
|
+
let bytes;
|
|
6084
|
+
try {
|
|
6085
|
+
const res = await fetch(rendered.url);
|
|
6086
|
+
if (!res.ok) {
|
|
6087
|
+
throw new Error(`fetch returned ${res.status}`);
|
|
6088
|
+
}
|
|
6089
|
+
bytes = Buffer.from(await res.arrayBuffer());
|
|
6090
|
+
} catch (err) {
|
|
6091
|
+
return `Error: rendered but could not download the capture (${err?.message ?? err}). Temporary URL: ${rendered.url}`;
|
|
6092
|
+
}
|
|
6093
|
+
let savedPath;
|
|
6094
|
+
if (typeof input.savePath === "string" && input.savePath) {
|
|
6095
|
+
const absolute = resolve2(PROJECT_ROOT, input.savePath);
|
|
6096
|
+
if (!absolute.startsWith(PROJECT_ROOT + sep)) {
|
|
6097
|
+
return "Error: savePath must resolve inside the project.";
|
|
6098
|
+
}
|
|
6099
|
+
await mkdir(dirname(absolute), { recursive: true });
|
|
6100
|
+
await writeFile(absolute, bytes);
|
|
6101
|
+
savedPath = input.savePath;
|
|
6102
|
+
}
|
|
6103
|
+
onLog?.("Hosting the capture...");
|
|
6104
|
+
const tmpPath = join(tmpdir(), `render-${randomUUID()}.png`);
|
|
6105
|
+
let url = rendered.url;
|
|
6106
|
+
let temporary = true;
|
|
6107
|
+
try {
|
|
6108
|
+
await writeFile(tmpPath, bytes);
|
|
6109
|
+
const upload = await runMindstudioCliResult(["upload", tmpPath], {
|
|
6110
|
+
timeout: 6e4,
|
|
6111
|
+
onLog,
|
|
6112
|
+
caller: "designExpert"
|
|
6113
|
+
});
|
|
6114
|
+
const match = upload.ok ? upload.value.match(/https:\/\/\S+/g) : null;
|
|
6115
|
+
if (match?.length) {
|
|
6116
|
+
url = match[match.length - 1];
|
|
6117
|
+
temporary = false;
|
|
6118
|
+
}
|
|
6119
|
+
} finally {
|
|
6120
|
+
await unlink(tmpPath).catch(() => {
|
|
6121
|
+
});
|
|
6122
|
+
}
|
|
6123
|
+
const analysis = await analyzeImage({
|
|
6124
|
+
prompt: RENDER_ANALYZE_PROMPT,
|
|
6125
|
+
image: url,
|
|
6126
|
+
onLog,
|
|
6127
|
+
model: resolveModel("imageAnalysis", context?.models, context?.model)
|
|
6128
|
+
}).then((r) => r.analysis).catch((err) => `Could not review this image: ${err.message}`);
|
|
6129
|
+
return JSON.stringify({
|
|
6130
|
+
images: [
|
|
6072
6131
|
{
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6132
|
+
url,
|
|
6133
|
+
...temporary ? {
|
|
6134
|
+
note: "Durable hosting failed \u2014 this URL is dev-session scratch storage and may expire. Do not use it for app metadata; retry if a durable URL is needed."
|
|
6135
|
+
} : {},
|
|
6136
|
+
...savedPath ? { savedPath } : {},
|
|
6137
|
+
analysis,
|
|
6138
|
+
width: rendered.width,
|
|
6139
|
+
height: rendered.height
|
|
6140
|
+
}
|
|
6141
|
+
]
|
|
6142
|
+
});
|
|
6143
|
+
}
|
|
6144
|
+
var MIN_DIMENSION, MAX_DIMENSION, RENDER_ANALYZE_PROMPT, definition3;
|
|
6145
|
+
var init_renderImage = __esm({
|
|
6146
|
+
"src/subagents/designExpert/tools/images/renderImage.ts"() {
|
|
6147
|
+
"use strict";
|
|
6148
|
+
init_screenshot();
|
|
6149
|
+
init_browserLock();
|
|
6150
|
+
init_runMindstudioCli();
|
|
6151
|
+
init_analyzeImage();
|
|
6152
|
+
init_surfaces();
|
|
6153
|
+
init_projectRoot();
|
|
6154
|
+
MIN_DIMENSION = 16;
|
|
6155
|
+
MAX_DIMENSION = 4096;
|
|
6156
|
+
RENDER_ANALYZE_PROMPT = 'You are reviewing a browser-rendered graphic (composed from HTML/CSS by a designer) for fidelity. Report: whether the composition fills the full canvas or leaves unintended gaps at any edge, any clipped or overflowing text, whether custom webfonts appear to have loaded (distinctive letterforms vs generic fallback serif/sans), any misalignment or uneven spacing, any unintended scrollbars or default-styling artifacts, and \u2014 if the background is transparent \u2014 any fringing or stray opaque pixels at the edges. Then briefly describe the overall composition and how polished it looks. Be concise and practical. Respond only with your analysis as Markdown (starting with the title "Render Review") and absolutely no other text. Do not use emojis - use unicode if you need symbols.';
|
|
6157
|
+
definition3 = {
|
|
6158
|
+
name: "renderImage",
|
|
6159
|
+
description: "Render a self-contained HTML document in a real browser and capture it as a hosted PNG at exact pixel dimensions, with a fidelity review included. Deterministic \u2014 exact hex colors, real loaded webfonts, precise geometry \u2014 unlike generateImages, which is an image model. Use for token-exact graphics: Open Graph share cards, wordmarks, flat/geometric icon tiles, badges, any composition where letterforms and spacing carry the design. Compose with HTML/CSS (link webfonts from CDNs \u2014 the renderer waits for them to load); inline existing SVG markup when needed, but never hand-write new SVG path data.",
|
|
6160
|
+
inputSchema: {
|
|
6161
|
+
type: "object",
|
|
6162
|
+
properties: {
|
|
6163
|
+
html: {
|
|
6164
|
+
type: "string",
|
|
6165
|
+
description: "A complete, self-contained HTML document sized to fill the viewport (style html/body to the full dimensions with margin 0). External webfonts and images from CDNs are fine \u2014 loading is awaited before capture."
|
|
6166
|
+
},
|
|
6167
|
+
width: {
|
|
6168
|
+
type: "number",
|
|
6169
|
+
description: "Viewport width in CSS pixels (e.g. 1200 for an OG card, 512 for an icon tile). Range: 16-4096."
|
|
6170
|
+
},
|
|
6171
|
+
height: {
|
|
6172
|
+
type: "number",
|
|
6173
|
+
description: "Viewport height in CSS pixels. Range: 16-4096."
|
|
6174
|
+
},
|
|
6175
|
+
scale: {
|
|
6176
|
+
type: "number",
|
|
6177
|
+
description: "Device scale factor, 1-3. Output pixels = css \xD7 scale. Use 2 for crisp icon masters (e.g. a 512\xD7512 document captured at 1024\xD71024)."
|
|
6178
|
+
},
|
|
6179
|
+
transparentBackground: {
|
|
6180
|
+
type: "boolean",
|
|
6181
|
+
description: "Capture with true alpha: leave the document background transparent (no background on html/body) and the PNG keeps it. No background-removal model involved."
|
|
6182
|
+
},
|
|
6183
|
+
savePath: {
|
|
6184
|
+
type: "string",
|
|
6185
|
+
description: "Optional project-relative path to also save the PNG into the app (e.g. 'dist/interfaces/web/public/og-image.png' so the deployed site self-hosts it)."
|
|
6186
|
+
}
|
|
6187
|
+
},
|
|
6188
|
+
required: ["html", "width", "height"]
|
|
6077
6189
|
}
|
|
6190
|
+
};
|
|
6191
|
+
}
|
|
6192
|
+
});
|
|
6193
|
+
|
|
6194
|
+
// src/subagents/designExpert/tools/analyzeDesign.ts
|
|
6195
|
+
var analyzeDesign_exports = {};
|
|
6196
|
+
__export(analyzeDesign_exports, {
|
|
6197
|
+
definition: () => definition4,
|
|
6198
|
+
execute: () => execute4
|
|
6199
|
+
});
|
|
6200
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
6201
|
+
import { extname as extname2, join as join2 } from "path";
|
|
6202
|
+
function splitFrontmatter(raw) {
|
|
6203
|
+
const match = raw.match(/^---\n([\s\S]*?)\n---\n?/);
|
|
6204
|
+
if (!match) {
|
|
6205
|
+
return { html: raw };
|
|
6206
|
+
}
|
|
6207
|
+
const fields = {};
|
|
6208
|
+
for (const line of match[1].split("\n")) {
|
|
6209
|
+
const idx = line.indexOf(":");
|
|
6210
|
+
if (idx > 0) {
|
|
6211
|
+
fields[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
|
|
6212
|
+
}
|
|
6213
|
+
}
|
|
6214
|
+
return {
|
|
6215
|
+
name: fields.name,
|
|
6216
|
+
description: fields.description,
|
|
6217
|
+
html: raw.slice(match[0].length)
|
|
6218
|
+
};
|
|
6219
|
+
}
|
|
6220
|
+
async function execute4(input, onLog, context) {
|
|
6221
|
+
const url = String(input.url ?? "").trim();
|
|
6222
|
+
if (!url) {
|
|
6223
|
+
return "Error: url is required.";
|
|
6224
|
+
}
|
|
6225
|
+
const customPrompt = typeof input.prompt === "string" && input.prompt.trim() ? input.prompt : void 0;
|
|
6226
|
+
if (!isFetchableUrl(url)) {
|
|
6227
|
+
if (HTML_EXTS.has(extname2(url).toLowerCase())) {
|
|
6228
|
+
return analyzeLocalHtml(url, customPrompt, onLog, context);
|
|
6229
|
+
}
|
|
6230
|
+
return analyze(
|
|
6231
|
+
url,
|
|
6232
|
+
customPrompt ?? DESIGN_REFERENCE_PROMPT,
|
|
6233
|
+
onLog,
|
|
6234
|
+
context
|
|
6235
|
+
);
|
|
6236
|
+
}
|
|
6237
|
+
if (IMAGE_EXT_RE.test(url)) {
|
|
6238
|
+
return analyze(
|
|
6239
|
+
url,
|
|
6240
|
+
customPrompt ?? DESIGN_REFERENCE_PROMPT,
|
|
6241
|
+
onLog,
|
|
6242
|
+
context
|
|
6078
6243
|
);
|
|
6079
|
-
|
|
6080
|
-
|
|
6244
|
+
}
|
|
6245
|
+
const ss = await runMindstudioCliResult(
|
|
6246
|
+
[
|
|
6247
|
+
"screenshot-url",
|
|
6248
|
+
"--url",
|
|
6249
|
+
url,
|
|
6250
|
+
"--mode",
|
|
6251
|
+
"viewport",
|
|
6252
|
+
"--width",
|
|
6253
|
+
"1440",
|
|
6254
|
+
"--delay",
|
|
6255
|
+
"2000"
|
|
6256
|
+
],
|
|
6257
|
+
{
|
|
6258
|
+
outputKey: "screenshotUrl",
|
|
6259
|
+
timeout: 12e4,
|
|
6260
|
+
onLog,
|
|
6261
|
+
caller: "designExpert"
|
|
6081
6262
|
}
|
|
6082
|
-
|
|
6263
|
+
);
|
|
6264
|
+
if (!ss.ok) {
|
|
6265
|
+
return `Could not screenshot ${url}: ${ss.value}`;
|
|
6083
6266
|
}
|
|
6267
|
+
return analyze(
|
|
6268
|
+
ss.value,
|
|
6269
|
+
customPrompt ?? DESIGN_REFERENCE_PROMPT,
|
|
6270
|
+
onLog,
|
|
6271
|
+
context
|
|
6272
|
+
);
|
|
6273
|
+
}
|
|
6274
|
+
async function analyze(image, prompt, onLog, context, extra) {
|
|
6084
6275
|
const analyzed = await analyzeImage({
|
|
6085
|
-
prompt
|
|
6276
|
+
prompt,
|
|
6086
6277
|
image,
|
|
6087
6278
|
apiConfig: context?.apiConfig,
|
|
6088
6279
|
onLog,
|
|
6089
6280
|
model: resolveModel("imageAnalysis", context?.models, context?.model)
|
|
6090
6281
|
});
|
|
6091
|
-
return JSON.stringify({
|
|
6282
|
+
return JSON.stringify({
|
|
6283
|
+
url: analyzed.url,
|
|
6284
|
+
analysis: analyzed.analysis,
|
|
6285
|
+
...extra
|
|
6286
|
+
});
|
|
6092
6287
|
}
|
|
6093
|
-
|
|
6288
|
+
async function analyzeLocalHtml(path14, customPrompt, onLog, context) {
|
|
6289
|
+
let raw;
|
|
6290
|
+
try {
|
|
6291
|
+
raw = await readFile2(join2(PROJECT_ROOT, path14), "utf8");
|
|
6292
|
+
} catch {
|
|
6293
|
+
return `No file at "${path14}". Paths resolve from the project root; list the directory to check the name.`;
|
|
6294
|
+
}
|
|
6295
|
+
const { name, description, html } = splitFrontmatter(raw);
|
|
6296
|
+
if (!html.trim()) {
|
|
6297
|
+
return `"${path14}" has no HTML to render.`;
|
|
6298
|
+
}
|
|
6299
|
+
onLog?.(`Rendering ${path14} in the sandbox browser...`);
|
|
6300
|
+
const release = await acquireBrowserLock();
|
|
6301
|
+
let rendered;
|
|
6302
|
+
try {
|
|
6303
|
+
rendered = await renderHtmlViaSidecar({
|
|
6304
|
+
html,
|
|
6305
|
+
width: HTML_RENDER_WIDTH,
|
|
6306
|
+
height: HTML_RENDER_START_HEIGHT,
|
|
6307
|
+
autoHeight: true
|
|
6308
|
+
});
|
|
6309
|
+
} catch (err) {
|
|
6310
|
+
return `Could not render ${path14}: ${err?.message ?? err}`;
|
|
6311
|
+
} finally {
|
|
6312
|
+
release();
|
|
6313
|
+
}
|
|
6314
|
+
const basePrompt = customPrompt ?? RENDER_ANALYZE_PROMPT;
|
|
6315
|
+
const subject = [name, description].filter(Boolean).join(" \u2014 ");
|
|
6316
|
+
const prompt = subject ? `The document rendered below is described by its author as: ${subject}
|
|
6317
|
+
|
|
6318
|
+
${basePrompt}` : basePrompt;
|
|
6319
|
+
return analyze(rendered.url, prompt, onLog, context, {
|
|
6320
|
+
renderedWidth: rendered.width,
|
|
6321
|
+
renderedHeight: rendered.height
|
|
6322
|
+
});
|
|
6323
|
+
}
|
|
6324
|
+
var DESIGN_REFERENCE_PROMPT, definition4, IMAGE_EXT_RE, HTML_EXTS, HTML_RENDER_WIDTH, HTML_RENDER_START_HEIGHT;
|
|
6094
6325
|
var init_analyzeDesign = __esm({
|
|
6095
6326
|
"src/subagents/designExpert/tools/analyzeDesign.ts"() {
|
|
6096
6327
|
"use strict";
|
|
6097
6328
|
init_runMindstudioCli();
|
|
6098
6329
|
init_analyzeImage();
|
|
6099
6330
|
init_surfaces();
|
|
6331
|
+
init_uploadImage();
|
|
6332
|
+
init_screenshot();
|
|
6333
|
+
init_browserLock();
|
|
6334
|
+
init_projectRoot();
|
|
6335
|
+
init_renderImage();
|
|
6100
6336
|
DESIGN_REFERENCE_PROMPT = `
|
|
6101
6337
|
You are analyzing a screenshot of a real website or app for a designer's personal technique/inspiration reference notes.
|
|
6102
6338
|
|
|
@@ -6120,15 +6356,15 @@ Identify the specific design moves that make this page interesting and unique, d
|
|
|
6120
6356
|
|
|
6121
6357
|
Respond only with your analysis as Markdown and absolutely no other text. Do not use emojis - use unicode if you need symbols.
|
|
6122
6358
|
`;
|
|
6123
|
-
|
|
6359
|
+
definition4 = {
|
|
6124
6360
|
name: "analyzeDesign",
|
|
6125
|
-
description: "Analyze the visual design of a website, an image URL, or an
|
|
6361
|
+
description: "Analyze the visual design of a website, an image (URL or file on disk), or an HTML document on disk. Websites are screenshotted first; an HTML file is rendered in a real browser first and the render is analyzed, which is how you look at a wireframe you authored. Provides static image analysis only, will not capture animations or video. With no prompt, a website or image gets a full design reference analysis (mood, color, typography, layout, distinctiveness) and a rendered HTML document gets a fidelity review (clipping, overflow, webfont loading, alignment). Provide a custom prompt to ask a specific design question instead. Use a bulleted list to ask many questions at once.",
|
|
6126
6362
|
inputSchema: {
|
|
6127
6363
|
type: "object",
|
|
6128
6364
|
properties: {
|
|
6129
6365
|
url: {
|
|
6130
6366
|
type: "string",
|
|
6131
|
-
description: "What to analyze: a website URL (will be screenshotted), an image URL,
|
|
6367
|
+
description: "What to analyze: a website URL (will be screenshotted), an image URL, the path of an image file on disk (e.g. a reference the user uploaded, under src/.user-uploads/), or the path of an HTML file on disk (rendered in a browser, then analyzed). Local files are hosted automatically and their URL comes back in the result."
|
|
6132
6368
|
},
|
|
6133
6369
|
prompt: {
|
|
6134
6370
|
type: "string",
|
|
@@ -6138,16 +6374,20 @@ Respond only with your analysis as Markdown and absolutely no other text. Do not
|
|
|
6138
6374
|
required: ["url"]
|
|
6139
6375
|
}
|
|
6140
6376
|
};
|
|
6377
|
+
IMAGE_EXT_RE = /\.(png|jpe?g|webp|gif|svg|avif)(\?|$)/i;
|
|
6378
|
+
HTML_EXTS = /* @__PURE__ */ new Set([".html", ".htm"]);
|
|
6379
|
+
HTML_RENDER_WIDTH = 1280;
|
|
6380
|
+
HTML_RENDER_START_HEIGHT = 1400;
|
|
6141
6381
|
}
|
|
6142
6382
|
});
|
|
6143
6383
|
|
|
6144
6384
|
// src/subagents/designExpert/tools/analyzeImage.ts
|
|
6145
6385
|
var analyzeImage_exports = {};
|
|
6146
6386
|
__export(analyzeImage_exports, {
|
|
6147
|
-
definition: () =>
|
|
6148
|
-
execute: () =>
|
|
6387
|
+
definition: () => definition5,
|
|
6388
|
+
execute: () => execute5
|
|
6149
6389
|
});
|
|
6150
|
-
async function
|
|
6390
|
+
async function execute5(input, onLog, context) {
|
|
6151
6391
|
const prompt = buildScreenshotAnalysisPrompt({
|
|
6152
6392
|
prompt: input.prompt
|
|
6153
6393
|
});
|
|
@@ -6160,14 +6400,14 @@ async function execute4(input, onLog, context) {
|
|
|
6160
6400
|
});
|
|
6161
6401
|
return JSON.stringify({ url, analysis });
|
|
6162
6402
|
}
|
|
6163
|
-
var
|
|
6403
|
+
var definition5;
|
|
6164
6404
|
var init_analyzeImage2 = __esm({
|
|
6165
6405
|
"src/subagents/designExpert/tools/analyzeImage.ts"() {
|
|
6166
6406
|
"use strict";
|
|
6167
6407
|
init_analyzeImage();
|
|
6168
6408
|
init_screenshot();
|
|
6169
6409
|
init_surfaces();
|
|
6170
|
-
|
|
6410
|
+
definition5 = {
|
|
6171
6411
|
name: "analyzeImage",
|
|
6172
6412
|
description: "Analyze an image using a vision model. Provides static image analysis only, will not capture animations or video. Returns an objective description of what is visible \u2014 shapes, colors, layout, text, artifacts. Use for factual inventory of image contents, not for subjective design judgment - the vision model providing the analysis has no sense of design. You are the design expert - use the analysis tool for factual inventory, then apply your own expertise for quality and suitability assessments. Optionally provide specific questions about what you're looking for. Use a bulleted list to ask many questions at once. If you are analyzing a screenshot of the app preview, you can reuse the same screenshot URL multiple times to ask multiple questions.",
|
|
6173
6413
|
inputSchema: {
|
|
@@ -6435,10 +6675,10 @@ var init_imageGenerator = __esm({
|
|
|
6435
6675
|
// src/subagents/designExpert/tools/images/generateImages.ts
|
|
6436
6676
|
var generateImages_exports = {};
|
|
6437
6677
|
__export(generateImages_exports, {
|
|
6438
|
-
definition: () =>
|
|
6439
|
-
execute: () =>
|
|
6678
|
+
definition: () => definition6,
|
|
6679
|
+
execute: () => execute6
|
|
6440
6680
|
});
|
|
6441
|
-
async function
|
|
6681
|
+
async function execute6(input, onLog, context) {
|
|
6442
6682
|
return generateImageAssets({
|
|
6443
6683
|
prompts: input.prompts,
|
|
6444
6684
|
width: input.width,
|
|
@@ -6465,13 +6705,13 @@ async function execute5(input, onLog, context) {
|
|
|
6465
6705
|
)
|
|
6466
6706
|
});
|
|
6467
6707
|
}
|
|
6468
|
-
var
|
|
6708
|
+
var definition6;
|
|
6469
6709
|
var init_generateImages = __esm({
|
|
6470
6710
|
"src/subagents/designExpert/tools/images/generateImages.ts"() {
|
|
6471
6711
|
"use strict";
|
|
6472
6712
|
init_imageGenerator();
|
|
6473
6713
|
init_surfaces();
|
|
6474
|
-
|
|
6714
|
+
definition6 = {
|
|
6475
6715
|
name: "generateImages",
|
|
6476
6716
|
description: "Generate images. Returns CDN URLs with a quality analysis for each image. Produces high-quality results for everything from photorealistic images and abstract/creative visuals. Pass multiple prompts to generate in parallel. No need to analyze images separately after generating \u2014 the analysis is included.",
|
|
6477
6717
|
inputSchema: {
|
|
@@ -6510,10 +6750,10 @@ var init_generateImages = __esm({
|
|
|
6510
6750
|
// src/subagents/designExpert/tools/images/editImages.ts
|
|
6511
6751
|
var editImages_exports = {};
|
|
6512
6752
|
__export(editImages_exports, {
|
|
6513
|
-
definition: () =>
|
|
6514
|
-
execute: () =>
|
|
6753
|
+
definition: () => definition7,
|
|
6754
|
+
execute: () => execute7
|
|
6515
6755
|
});
|
|
6516
|
-
async function
|
|
6756
|
+
async function execute7(input, onLog, context) {
|
|
6517
6757
|
return generateImageAssets({
|
|
6518
6758
|
prompts: input.prompts,
|
|
6519
6759
|
sourceImages: input.sourceImages,
|
|
@@ -6540,13 +6780,13 @@ async function execute6(input, onLog, context) {
|
|
|
6540
6780
|
)
|
|
6541
6781
|
});
|
|
6542
6782
|
}
|
|
6543
|
-
var
|
|
6783
|
+
var definition7;
|
|
6544
6784
|
var init_editImages = __esm({
|
|
6545
6785
|
"src/subagents/designExpert/tools/images/editImages.ts"() {
|
|
6546
6786
|
"use strict";
|
|
6547
6787
|
init_imageGenerator();
|
|
6548
6788
|
init_surfaces();
|
|
6549
|
-
|
|
6789
|
+
definition7 = {
|
|
6550
6790
|
name: "editImages",
|
|
6551
6791
|
description: "Edit or transform existing images. Provide one or more source image URLs as reference and a prompt describing the desired edit. Use for compositing, style transfer, subject transformation, blending multiple references, or incorporating one or more references into something new. Returns CDN URLs with analysis.",
|
|
6552
6792
|
inputSchema: {
|
|
@@ -6585,150 +6825,6 @@ var init_editImages = __esm({
|
|
|
6585
6825
|
}
|
|
6586
6826
|
});
|
|
6587
6827
|
|
|
6588
|
-
// src/subagents/designExpert/tools/images/renderImage.ts
|
|
6589
|
-
var renderImage_exports = {};
|
|
6590
|
-
__export(renderImage_exports, {
|
|
6591
|
-
definition: () => definition7,
|
|
6592
|
-
execute: () => execute7
|
|
6593
|
-
});
|
|
6594
|
-
import { mkdir, unlink, writeFile } from "fs/promises";
|
|
6595
|
-
import { tmpdir } from "os";
|
|
6596
|
-
import { dirname, join, resolve as resolve2, sep } from "path";
|
|
6597
|
-
import { randomUUID } from "crypto";
|
|
6598
|
-
async function execute7(input, onLog, context) {
|
|
6599
|
-
const html = typeof input.html === "string" ? input.html : "";
|
|
6600
|
-
const width = Math.round(Number(input.width));
|
|
6601
|
-
const height = Math.round(Number(input.height));
|
|
6602
|
-
if (!html || !Number.isFinite(width) || !Number.isFinite(height) || width < MIN_DIMENSION || width > MAX_DIMENSION || height < MIN_DIMENSION || height > MAX_DIMENSION) {
|
|
6603
|
-
return `Error: renderImage requires an html string plus width/height between ${MIN_DIMENSION} and ${MAX_DIMENSION}.`;
|
|
6604
|
-
}
|
|
6605
|
-
const release = await acquireBrowserLock();
|
|
6606
|
-
let rendered;
|
|
6607
|
-
try {
|
|
6608
|
-
onLog?.("Rendering document in the sandbox browser...");
|
|
6609
|
-
rendered = await renderHtmlViaSidecar({
|
|
6610
|
-
html,
|
|
6611
|
-
width,
|
|
6612
|
-
height,
|
|
6613
|
-
transparent: input.transparentBackground === true,
|
|
6614
|
-
scale: typeof input.scale === "number" ? input.scale : void 0
|
|
6615
|
-
});
|
|
6616
|
-
} catch (err) {
|
|
6617
|
-
return `Error: render failed: ${err?.message ?? err}`;
|
|
6618
|
-
} finally {
|
|
6619
|
-
release();
|
|
6620
|
-
}
|
|
6621
|
-
let bytes;
|
|
6622
|
-
try {
|
|
6623
|
-
const res = await fetch(rendered.url);
|
|
6624
|
-
if (!res.ok) {
|
|
6625
|
-
throw new Error(`fetch returned ${res.status}`);
|
|
6626
|
-
}
|
|
6627
|
-
bytes = Buffer.from(await res.arrayBuffer());
|
|
6628
|
-
} catch (err) {
|
|
6629
|
-
return `Error: rendered but could not download the capture (${err?.message ?? err}). Temporary URL: ${rendered.url}`;
|
|
6630
|
-
}
|
|
6631
|
-
let savedPath;
|
|
6632
|
-
if (typeof input.savePath === "string" && input.savePath) {
|
|
6633
|
-
const absolute = resolve2(PROJECT_ROOT, input.savePath);
|
|
6634
|
-
if (!absolute.startsWith(PROJECT_ROOT + sep)) {
|
|
6635
|
-
return "Error: savePath must resolve inside the project.";
|
|
6636
|
-
}
|
|
6637
|
-
await mkdir(dirname(absolute), { recursive: true });
|
|
6638
|
-
await writeFile(absolute, bytes);
|
|
6639
|
-
savedPath = input.savePath;
|
|
6640
|
-
}
|
|
6641
|
-
onLog?.("Hosting the capture...");
|
|
6642
|
-
const tmpPath = join(tmpdir(), `render-${randomUUID()}.png`);
|
|
6643
|
-
let url = rendered.url;
|
|
6644
|
-
let temporary = true;
|
|
6645
|
-
try {
|
|
6646
|
-
await writeFile(tmpPath, bytes);
|
|
6647
|
-
const upload = await runMindstudioCliResult(["upload", tmpPath], {
|
|
6648
|
-
timeout: 6e4,
|
|
6649
|
-
onLog,
|
|
6650
|
-
caller: "designExpert"
|
|
6651
|
-
});
|
|
6652
|
-
const match = upload.ok ? upload.value.match(/https:\/\/\S+/g) : null;
|
|
6653
|
-
if (match?.length) {
|
|
6654
|
-
url = match[match.length - 1];
|
|
6655
|
-
temporary = false;
|
|
6656
|
-
}
|
|
6657
|
-
} finally {
|
|
6658
|
-
await unlink(tmpPath).catch(() => {
|
|
6659
|
-
});
|
|
6660
|
-
}
|
|
6661
|
-
const analysis = await analyzeImage({
|
|
6662
|
-
prompt: RENDER_ANALYZE_PROMPT,
|
|
6663
|
-
image: url,
|
|
6664
|
-
onLog,
|
|
6665
|
-
model: resolveModel("imageAnalysis", context?.models, context?.model)
|
|
6666
|
-
}).then((r) => r.analysis).catch((err) => `Could not review this image: ${err.message}`);
|
|
6667
|
-
return JSON.stringify({
|
|
6668
|
-
images: [
|
|
6669
|
-
{
|
|
6670
|
-
url,
|
|
6671
|
-
...temporary ? {
|
|
6672
|
-
note: "Durable hosting failed \u2014 this URL is dev-session scratch storage and may expire. Do not use it for app metadata; retry if a durable URL is needed."
|
|
6673
|
-
} : {},
|
|
6674
|
-
...savedPath ? { savedPath } : {},
|
|
6675
|
-
analysis,
|
|
6676
|
-
width: rendered.width,
|
|
6677
|
-
height: rendered.height
|
|
6678
|
-
}
|
|
6679
|
-
]
|
|
6680
|
-
});
|
|
6681
|
-
}
|
|
6682
|
-
var MIN_DIMENSION, MAX_DIMENSION, RENDER_ANALYZE_PROMPT, definition7;
|
|
6683
|
-
var init_renderImage = __esm({
|
|
6684
|
-
"src/subagents/designExpert/tools/images/renderImage.ts"() {
|
|
6685
|
-
"use strict";
|
|
6686
|
-
init_screenshot();
|
|
6687
|
-
init_browserLock();
|
|
6688
|
-
init_runMindstudioCli();
|
|
6689
|
-
init_analyzeImage();
|
|
6690
|
-
init_surfaces();
|
|
6691
|
-
init_projectRoot();
|
|
6692
|
-
MIN_DIMENSION = 16;
|
|
6693
|
-
MAX_DIMENSION = 4096;
|
|
6694
|
-
RENDER_ANALYZE_PROMPT = 'You are reviewing a browser-rendered graphic (composed from HTML/CSS by a designer) for fidelity. Report: whether the composition fills the full canvas or leaves unintended gaps at any edge, any clipped or overflowing text, whether custom webfonts appear to have loaded (distinctive letterforms vs generic fallback serif/sans), any misalignment or uneven spacing, any unintended scrollbars or default-styling artifacts, and \u2014 if the background is transparent \u2014 any fringing or stray opaque pixels at the edges. Then briefly describe the overall composition and how polished it looks. Be concise and practical. Respond only with your analysis as Markdown (starting with the title "Render Review") and absolutely no other text. Do not use emojis - use unicode if you need symbols.';
|
|
6695
|
-
definition7 = {
|
|
6696
|
-
name: "renderImage",
|
|
6697
|
-
description: "Render a self-contained HTML document in a real browser and capture it as a hosted PNG at exact pixel dimensions, with a fidelity review included. Deterministic \u2014 exact hex colors, real loaded webfonts, precise geometry \u2014 unlike generateImages, which is an image model. Use for token-exact graphics: Open Graph share cards, wordmarks, flat/geometric icon tiles, badges, any composition where letterforms and spacing carry the design. Compose with HTML/CSS (link webfonts from CDNs \u2014 the renderer waits for them to load); inline existing SVG markup when needed, but never hand-write new SVG path data.",
|
|
6698
|
-
inputSchema: {
|
|
6699
|
-
type: "object",
|
|
6700
|
-
properties: {
|
|
6701
|
-
html: {
|
|
6702
|
-
type: "string",
|
|
6703
|
-
description: "A complete, self-contained HTML document sized to fill the viewport (style html/body to the full dimensions with margin 0). External webfonts and images from CDNs are fine \u2014 loading is awaited before capture."
|
|
6704
|
-
},
|
|
6705
|
-
width: {
|
|
6706
|
-
type: "number",
|
|
6707
|
-
description: "Viewport width in CSS pixels (e.g. 1200 for an OG card, 512 for an icon tile). Range: 16-4096."
|
|
6708
|
-
},
|
|
6709
|
-
height: {
|
|
6710
|
-
type: "number",
|
|
6711
|
-
description: "Viewport height in CSS pixels. Range: 16-4096."
|
|
6712
|
-
},
|
|
6713
|
-
scale: {
|
|
6714
|
-
type: "number",
|
|
6715
|
-
description: "Device scale factor, 1-3. Output pixels = css \xD7 scale. Use 2 for crisp icon masters (e.g. a 512\xD7512 document captured at 1024\xD71024)."
|
|
6716
|
-
},
|
|
6717
|
-
transparentBackground: {
|
|
6718
|
-
type: "boolean",
|
|
6719
|
-
description: "Capture with true alpha: leave the document background transparent (no background on html/body) and the PNG keeps it. No background-removal model involved."
|
|
6720
|
-
},
|
|
6721
|
-
savePath: {
|
|
6722
|
-
type: "string",
|
|
6723
|
-
description: "Optional project-relative path to also save the PNG into the app (e.g. 'dist/interfaces/web/public/og-image.png' so the deployed site self-hosts it)."
|
|
6724
|
-
}
|
|
6725
|
-
},
|
|
6726
|
-
required: ["html", "width", "height"]
|
|
6727
|
-
}
|
|
6728
|
-
};
|
|
6729
|
-
}
|
|
6730
|
-
});
|
|
6731
|
-
|
|
6732
6828
|
// src/subagents/copyEditor/tools.ts
|
|
6733
6829
|
var COPY_EDITOR_TOOLS;
|
|
6734
6830
|
var init_tools3 = __esm({
|
|
@@ -6911,7 +7007,7 @@ __export(createWireframe_exports, {
|
|
|
6911
7007
|
execute: () => execute10
|
|
6912
7008
|
});
|
|
6913
7009
|
import { mkdir as mkdir2, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
6914
|
-
import { join as
|
|
7010
|
+
import { join as join3 } from "path";
|
|
6915
7011
|
function singleLine(value) {
|
|
6916
7012
|
return value.replace(/\s+/g, " ").trim();
|
|
6917
7013
|
}
|
|
@@ -7000,12 +7096,12 @@ async function execute10(input, onLog, context) {
|
|
|
7000
7096
|
html.trim(),
|
|
7001
7097
|
""
|
|
7002
7098
|
].join("\n");
|
|
7003
|
-
await mkdir2(
|
|
7004
|
-
const existed = await stat2(
|
|
7099
|
+
await mkdir2(join3(PROJECT_ROOT, WIREFRAMES_DIR), { recursive: true });
|
|
7100
|
+
const existed = await stat2(join3(PROJECT_ROOT, relPath)).then(
|
|
7005
7101
|
() => true,
|
|
7006
7102
|
() => false
|
|
7007
7103
|
);
|
|
7008
|
-
await writeFile2(
|
|
7104
|
+
await writeFile2(join3(PROJECT_ROOT, relPath), content, "utf8");
|
|
7009
7105
|
onLog?.(`Wrote ${relPath}, mirroring for preview...`);
|
|
7010
7106
|
const mirror = await uploadMirror(context, slug, content);
|
|
7011
7107
|
if (!mirror.ok) {
|
|
@@ -7441,7 +7537,7 @@ The guidance about specifying layouts in prose, writing implementation notes, an
|
|
|
7441
7537
|
|
|
7442
7538
|
// src/subagents/designExpert/validateWireframeRefs.ts
|
|
7443
7539
|
import { stat as stat3 } from "fs/promises";
|
|
7444
|
-
import { join as
|
|
7540
|
+
import { join as join4 } from "path";
|
|
7445
7541
|
async function validateWireframeRefs(text) {
|
|
7446
7542
|
const refs = [...new Set(text.match(WIREFRAME_REF_RE) ?? [])];
|
|
7447
7543
|
if (refs.length === 0) {
|
|
@@ -7449,7 +7545,7 @@ async function validateWireframeRefs(text) {
|
|
|
7449
7545
|
}
|
|
7450
7546
|
const missing = [];
|
|
7451
7547
|
for (const ref of refs) {
|
|
7452
|
-
const exists = await stat3(
|
|
7548
|
+
const exists = await stat3(join4(PROJECT_ROOT, ref)).then(
|
|
7453
7549
|
(s) => s.isFile(),
|
|
7454
7550
|
() => false
|
|
7455
7551
|
);
|
|
@@ -10267,7 +10363,7 @@ var init_config = __esm({
|
|
|
10267
10363
|
// src/headless/attachments.ts
|
|
10268
10364
|
import { mkdirSync, existsSync } from "fs";
|
|
10269
10365
|
import { writeFile as writeFile3 } from "fs/promises";
|
|
10270
|
-
import { basename as basename2, join as
|
|
10366
|
+
import { basename as basename2, join as join5, extname as extname3 } from "path";
|
|
10271
10367
|
function filenameFromUrl(url) {
|
|
10272
10368
|
try {
|
|
10273
10369
|
const pathname = new URL(url).pathname;
|
|
@@ -10278,11 +10374,11 @@ function filenameFromUrl(url) {
|
|
|
10278
10374
|
}
|
|
10279
10375
|
}
|
|
10280
10376
|
function resolveUniqueFilename(name, claimed) {
|
|
10281
|
-
const isFree = (candidate) => !claimed.has(candidate) && !existsSync(
|
|
10377
|
+
const isFree = (candidate) => !claimed.has(candidate) && !existsSync(join5(UPLOADS_DIR, candidate));
|
|
10282
10378
|
if (isFree(name)) {
|
|
10283
10379
|
return name;
|
|
10284
10380
|
}
|
|
10285
|
-
const ext =
|
|
10381
|
+
const ext = extname3(name);
|
|
10286
10382
|
const base = name.slice(0, name.length - ext.length);
|
|
10287
10383
|
let counter = 1;
|
|
10288
10384
|
while (!isFree(`${base}-${counter}${ext}`)) {
|
|
@@ -10292,7 +10388,7 @@ function resolveUniqueFilename(name, claimed) {
|
|
|
10292
10388
|
}
|
|
10293
10389
|
function isImageAttachment(att) {
|
|
10294
10390
|
const name = att.filename || filenameFromUrl(att.url);
|
|
10295
|
-
return IMAGE_EXTENSIONS.has(
|
|
10391
|
+
return IMAGE_EXTENSIONS.has(extname3(name).toLowerCase());
|
|
10296
10392
|
}
|
|
10297
10393
|
async function persistAttachments(attachments) {
|
|
10298
10394
|
const nonVoice = attachments.filter((a) => !a.isVoice);
|
|
@@ -10312,7 +10408,7 @@ async function persistAttachments(attachments) {
|
|
|
10312
10408
|
const results = await Promise.allSettled(
|
|
10313
10409
|
nonVoice.map(async (att, i) => {
|
|
10314
10410
|
const name = names[i];
|
|
10315
|
-
const localPath =
|
|
10411
|
+
const localPath = join5(UPLOADS_DIR, name);
|
|
10316
10412
|
const res = await fetch(att.url, {
|
|
10317
10413
|
signal: AbortSignal.timeout(3e4)
|
|
10318
10414
|
});
|