@lightcone-ai/daemon 0.15.23 → 0.15.25
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/package.json
CHANGED
|
@@ -157,6 +157,26 @@ async function scrollToTop(page) {
|
|
|
157
157
|
});
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
function scalePhaseY(phase, zoom) {
|
|
161
|
+
if (!phase || typeof phase !== 'object') return phase;
|
|
162
|
+
const scale = (v) => (Number.isFinite(Number(v)) ? Math.round(Number(v) * zoom) : v);
|
|
163
|
+
const scaledVisualAction = phase.visual_action && typeof phase.visual_action === 'object'
|
|
164
|
+
? {
|
|
165
|
+
...phase.visual_action,
|
|
166
|
+
...(phase.visual_action.target_y != null ? { target_y: scale(phase.visual_action.target_y) } : {}),
|
|
167
|
+
...(phase.visual_action.to_y != null ? { to_y: scale(phase.visual_action.to_y) } : {}),
|
|
168
|
+
...(phase.visual_action.from_y != null ? { from_y: scale(phase.visual_action.from_y) } : {}),
|
|
169
|
+
}
|
|
170
|
+
: phase.visual_action;
|
|
171
|
+
return {
|
|
172
|
+
...phase,
|
|
173
|
+
...(phase.target_y != null ? { target_y: scale(phase.target_y) } : {}),
|
|
174
|
+
...(phase.to_y != null ? { to_y: scale(phase.to_y) } : {}),
|
|
175
|
+
...(phase.from_y != null ? { from_y: scale(phase.from_y) } : {}),
|
|
176
|
+
...(scaledVisualAction !== phase.visual_action ? { visual_action: scaledVisualAction } : {}),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
160
180
|
export async function recordUrlNarration({
|
|
161
181
|
plan,
|
|
162
182
|
output_path,
|
|
@@ -167,6 +187,7 @@ export async function recordUrlNarration({
|
|
|
167
187
|
viewport = DEFAULT_VIEWPORT,
|
|
168
188
|
fps = DEFAULT_FPS,
|
|
169
189
|
settle_ms = 4000,
|
|
190
|
+
page_zoom = 1.0,
|
|
170
191
|
displayPool = defaultDisplayPool,
|
|
171
192
|
ffmpegDurationBufferSec = 8,
|
|
172
193
|
startupProbeMs = 1200,
|
|
@@ -174,7 +195,9 @@ export async function recordUrlNarration({
|
|
|
174
195
|
xvfbStopTimeoutMs = 5000,
|
|
175
196
|
postPlanTailMs = 600,
|
|
176
197
|
} = {}) {
|
|
177
|
-
const
|
|
198
|
+
const zoom = Number.isFinite(Number(page_zoom)) && Number(page_zoom) > 0 ? Number(page_zoom) : 1.0;
|
|
199
|
+
const rawPhases = normalizePlanPhases(plan);
|
|
200
|
+
const phases = zoom !== 1.0 ? rawPhases.map(p => scalePhaseY(p, zoom)) : rawPhases;
|
|
178
201
|
const executablePlan = {
|
|
179
202
|
...(plan && typeof plan === 'object' ? plan : {}),
|
|
180
203
|
phases,
|
|
@@ -220,6 +243,13 @@ export async function recordUrlNarration({
|
|
|
220
243
|
settleMs: settle_ms,
|
|
221
244
|
});
|
|
222
245
|
|
|
246
|
+
if (zoom !== 1.0) {
|
|
247
|
+
await browserSession.page.evaluate((z) => {
|
|
248
|
+
document.documentElement.style.zoom = String(z);
|
|
249
|
+
}, zoom);
|
|
250
|
+
await browserSession.page.waitForTimeout(300);
|
|
251
|
+
}
|
|
252
|
+
|
|
223
253
|
const estimatedDurationMs = estimatePlanDurationMs(executablePlan);
|
|
224
254
|
const estimatedDurationSec = Math.max(
|
|
225
255
|
5,
|
|
@@ -204,6 +204,55 @@ async function extractStructure(page, { binHeight, minBinChars }) {
|
|
|
204
204
|
return rangeFromMatches(matches, root.scrollHeight);
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
+
function trimNoiseFromBottom(range, pageHeight, detectionStrategy) {
|
|
208
|
+
// Site-specific selectors already point to the exact content element — trust them.
|
|
209
|
+
if (detectionStrategy && detectionStrategy.startsWith('site:')) return range;
|
|
210
|
+
|
|
211
|
+
const [coreTop, rawBottom] = range;
|
|
212
|
+
const minContentHeight = 600; // always keep at least 600px of core content
|
|
213
|
+
|
|
214
|
+
// Selectors that reliably identify non-article noise below the body text.
|
|
215
|
+
const noiseSelectors = [
|
|
216
|
+
// Comment sections
|
|
217
|
+
'[class*="comment-list"]', '[class*="comment-wrap"]', '[class*="commentList"]',
|
|
218
|
+
'[id*="comment"]', '[class*="discuss"]', '[class*="reply-list"]',
|
|
219
|
+
// Related / recommended articles
|
|
220
|
+
'[class*="related-read"]', '[class*="related-article"]', '[class*="relatedArticle"]',
|
|
221
|
+
'[class*="recommend"]', '[class*="more-article"]', '[class*="further-reading"]',
|
|
222
|
+
// Article footer / bottom toolbars
|
|
223
|
+
'[class*="article-footer"]', '[class*="post-footer"]', '[class*="article-bottom"]',
|
|
224
|
+
// Social sharing bars that sit below the body
|
|
225
|
+
'[class*="share-bar"]', '[class*="share-wrap"]', '[class*="article-share"]',
|
|
226
|
+
// Generic page footer inside the core element
|
|
227
|
+
'footer',
|
|
228
|
+
];
|
|
229
|
+
|
|
230
|
+
const root = document.scrollingElement || document.documentElement;
|
|
231
|
+
let trimBottom = rawBottom;
|
|
232
|
+
|
|
233
|
+
for (const sel of noiseSelectors) {
|
|
234
|
+
try {
|
|
235
|
+
const elements = document.querySelectorAll(sel);
|
|
236
|
+
for (const el of elements) {
|
|
237
|
+
if (!isVisibleEnough(el, 40)) continue;
|
|
238
|
+
const rect = el.getBoundingClientRect();
|
|
239
|
+
const elTop = Math.round(rect.top + root.scrollTop);
|
|
240
|
+
if (
|
|
241
|
+
elTop > coreTop + minContentHeight
|
|
242
|
+
&& elTop < trimBottom
|
|
243
|
+
&& rect.height > 60
|
|
244
|
+
) {
|
|
245
|
+
trimBottom = elTop;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
} catch {
|
|
249
|
+
// ignore selector errors for individual sites
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return [coreTop, trimBottom];
|
|
254
|
+
}
|
|
255
|
+
|
|
207
256
|
function collectTextBins({ range, coreElement }) {
|
|
208
257
|
const [rangeTop, rangeBottom] = range;
|
|
209
258
|
const root = document.scrollingElement || document.documentElement;
|
|
@@ -353,6 +402,10 @@ async function extractStructure(page, { binHeight, minBinChars }) {
|
|
|
353
402
|
strategy = `${strategy}:range_fixed`;
|
|
354
403
|
}
|
|
355
404
|
|
|
405
|
+
// Trim coreBottom to exclude footer noise (comments, related articles, recommendations)
|
|
406
|
+
// that commonly appear below the article body.
|
|
407
|
+
coreRange = trimNoiseFromBottom(coreRange, totalHeight, strategy);
|
|
408
|
+
|
|
356
409
|
const bins = collectTextBins({ range: coreRange, coreElement });
|
|
357
410
|
const focusRange = (() => {
|
|
358
411
|
const span = coreRange[1] - coreRange[0];
|
package/src/chat-bridge.js
CHANGED
|
@@ -1480,6 +1480,7 @@ server.tool('record_url_narration',
|
|
|
1480
1480
|
}).optional().describe('Default 1080x1920 (mobile portrait). Override only if the plan requires a different shape.'),
|
|
1481
1481
|
fps: z.number().optional().describe('Default 30. Do not change unless needed.'),
|
|
1482
1482
|
settle_ms: z.number().optional().describe('Default 4000. Settle wait after navigation before recording starts.'),
|
|
1483
|
+
page_zoom: z.number().optional().describe('Browser zoom factor applied before recording. Default 1.0 (no zoom). Use e.g. 1.1 to zoom in 10% so text appears larger. Plan Y coordinates are automatically scaled.'),
|
|
1483
1484
|
},
|
|
1484
1485
|
async (args) => {
|
|
1485
1486
|
if (isBlockedCvmaxEditorVideoTool('record_url_narration')) {
|
package/src/mcp-config.js
CHANGED
|
@@ -111,7 +111,7 @@ export function buildSkillMcpServers({
|
|
|
111
111
|
const resolvedArgs = (mc.args ?? []).map(arg => resolveSkillArg(arg, config));
|
|
112
112
|
const resolvedEnv = {};
|
|
113
113
|
for (const envKey of (mc.env ?? [])) {
|
|
114
|
-
resolvedEnv[envKey] = agentEnv[envKey]
|
|
114
|
+
resolvedEnv[envKey] = agentEnv[envKey] || process.env[envKey] || '';
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
if (mcpServers[mc.server]) {
|