feat: add mdtodo web-probe commands

This commit is contained in:
Codex
2026-06-26 12:11:31 +00:00
parent ff9dd29faa
commit 0fcdd12c83
8 changed files with 375 additions and 20 deletions
@@ -367,6 +367,9 @@ async function processCommand(command) {
case "selectMdtodoFile": return selectMdtodoFile(command);
case "selectMdtodoTask": return selectMdtodoTask(command);
case "expandMdtodoTask": return expandMdtodoTask(command);
case "openMdtodoReportPreview": return openMdtodoReportPreview(command);
case "toggleMdtodoReportFullscreen": return toggleMdtodoReportFullscreen(command);
case "editMdtodoTaskInline": return editMdtodoTaskInline(command);
case "editMdtodoTaskTitle": return editMdtodoTaskTitle(command);
case "editMdtodoTaskBody": return editMdtodoTaskBody(command);
case "toggleMdtodoTaskStatus": return toggleMdtodoTaskStatus(command);
@@ -1117,6 +1120,14 @@ function sessionIdFromAgentSessionPayload(payload) {
return match ? match[0] : null;
}
function traceIdFromAgentChatPayload(payload) {
const direct = payload?.traceId ?? payload?.turn?.traceId ?? payload?.message?.traceId ?? payload?.data?.traceId ?? payload?.data?.turn?.traceId ?? payload?.data?.message?.traceId;
const directText = String(direct || "").trim();
if (/^(?:trc_[A-Za-z0-9_-]+|[a-f0-9]{16,64})$/u.test(directText)) return directText;
const match = JSON.stringify(payload ?? "").match(/\b(?:trc_[A-Za-z0-9_-]+|[a-f0-9]{16,64})\b/u);
return match ? match[0] : null;
}
async function workbenchSessionSnapshot(targetPage = page) {
return targetPage.evaluate(() => {
const activeTab = document.querySelector(".session-tab[data-active='true'], .session-tab[aria-selected='true']");
@@ -1939,7 +1950,7 @@ async function selectMdtodoFile(command) {
return clickProjectItemByAttr({
type: "selectMdtodoFile",
attr: "data-file-ref",
value: command.fileRef || command.value || command.text || "",
value: command.fileRef || command.filename || command.value || command.text || "",
fallbackSelector: '[data-testid="mdtodo-file-list"] [data-file-ref], [data-file-ref]',
selectTestId: "mdtodo-file-select"
});
@@ -2145,7 +2156,9 @@ async function saveMdtodoTaskWithButton(command, type, testId, fields) {
const beforeUrl = currentPageUrl();
const beforeProject = await projectManagementCommandSnapshot();
const selection = await selectTaskIfCommandTargetsOne(command);
const inlineEditors = [];
for (const field of fields) {
if (field.openByDblClickTestId) inlineEditors.push(await ensureMdtodoInlineEditor(field.testId, field.openByDblClickTestId));
if (field.kind === "fill") await fillMdtodoField(field.testId, field.value);
if (field.kind === "select") {
const locator = page.locator('[data-testid="' + cssEscape(field.testId) + '"]').first();
@@ -2153,26 +2166,142 @@ async function saveMdtodoTaskWithButton(command, type, testId, fields) {
await selectHtmlOptionByValueOrLabel(locator, field.value);
}
}
const save = await clickProjectButtonAndMaybeWait(testId, /^\/v1\/project-management\/mdtodo\/tasks/u);
return { beforeUrl, afterUrl: currentPageUrl(), type, selection, save, beforeProject, afterProject: await projectManagementCommandSnapshot(), pageId, valuesRedacted: true };
const save = await clickProjectButtonAndMaybeWaitAny(Array.isArray(testId) ? testId : [testId], /^\/v1\/project-management\/mdtodo\/tasks/u);
return { beforeUrl, afterUrl: currentPageUrl(), type, selection, inlineEditors, save, beforeProject, afterProject: await projectManagementCommandSnapshot(), pageId, valuesRedacted: true };
}
async function clickProjectButtonAndMaybeWaitAny(testIds, pathPattern) {
const candidates = (testIds || []).filter(Boolean);
for (const testId of candidates) {
const button = page.locator('[data-testid="' + cssEscape(testId) + '"]').first();
if (await visibleLocator(button)) return clickProjectButtonAndMaybeWait(testId, pathPattern);
}
if (candidates.length === 0) throw new Error("clickProjectButtonAndMaybeWaitAny requires at least one data-testid");
return clickProjectButtonAndMaybeWait(candidates[0], pathPattern);
}
async function ensureMdtodoInlineEditor(editorTestId, readTestId) {
const editor = page.locator('[data-testid="' + cssEscape(editorTestId) + '"]').first();
if (await visibleLocator(editor)) return { editorTestId, readTestId, opened: false };
const read = page.locator('[data-testid="' + cssEscape(readTestId) + '"]').first();
await read.waitFor({ state: "visible", timeout: 10000 });
await read.dblclick();
await editor.waitFor({ state: "visible", timeout: 10000 });
return { editorTestId, readTestId, opened: true };
}
async function editMdtodoTaskTitle(command) {
const title = commandValue(command, ["title", "text", "value"]);
if (!title) throw new Error("editMdtodoTaskTitle requires --title or --text");
return saveMdtodoTaskWithButton(command, "editMdtodoTaskTitle", "mdtodo-edit-save", [{ kind: "fill", testId: "mdtodo-edit-title", value: title }]);
return saveMdtodoTaskWithButton(command, "editMdtodoTaskTitle", "mdtodo-edit-save", [{ kind: "fill", testId: "mdtodo-edit-title", value: title, openByDblClickTestId: "mdtodo-title-read" }]);
}
async function editMdtodoTaskBody(command) {
const body = commandValue(command, ["text", "body", "value"]);
if (!body) throw new Error("editMdtodoTaskBody requires --text or --text-stdin");
return saveMdtodoTaskWithButton(command, "editMdtodoTaskBody", "mdtodo-edit-body-save", [{ kind: "fill", testId: "mdtodo-edit-body", value: body }]);
return saveMdtodoTaskWithButton(command, "editMdtodoTaskBody", "mdtodo-edit-body-save", [{ kind: "fill", testId: "mdtodo-edit-body", value: body, openByDblClickTestId: "mdtodo-body-rendered" }]);
}
async function toggleMdtodoTaskStatus(command) {
const status = commandValue(command, ["status", "value", "text"]);
if (!status) throw new Error("toggleMdtodoTaskStatus requires --status");
return saveMdtodoTaskWithButton(command, "toggleMdtodoTaskStatus", "mdtodo-edit-save", [{ kind: "select", testId: "mdtodo-edit-status", value: status }]);
return saveMdtodoTaskWithButton(command, "toggleMdtodoTaskStatus", ["mdtodo-status-save", "mdtodo-edit-save"], [{ kind: "select", testId: "mdtodo-edit-status", value: status }]);
}
async function editMdtodoTaskInline(command) {
const field = commandValue(command, ["field", "value"]).toLowerCase();
if (field === "title") {
const title = commandValue(command, ["title", "text"]);
if (!title) throw new Error("editMdtodoTaskInline --field title requires --title or --text");
return saveMdtodoTaskWithButton(command, "editMdtodoTaskInline", "mdtodo-edit-save", [{ kind: "fill", testId: "mdtodo-edit-title", value: title, openByDblClickTestId: "mdtodo-title-read" }]);
}
if (field === "body" || field === "content") {
const body = commandValue(command, ["body", "text"]);
if (!body) throw new Error("editMdtodoTaskInline --field body requires --body, --text, or --text-stdin");
return saveMdtodoTaskWithButton(command, "editMdtodoTaskInline", "mdtodo-edit-body-save", [{ kind: "fill", testId: "mdtodo-edit-body", value: body, openByDblClickTestId: "mdtodo-body-rendered" }]);
}
if (field === "status") {
const status = commandValue(command, ["status", "text"]);
if (!status) throw new Error("editMdtodoTaskInline --field status requires --status or --text");
return saveMdtodoTaskWithButton(command, "editMdtodoTaskInline", ["mdtodo-status-save", "mdtodo-edit-save"], [{ kind: "select", testId: "mdtodo-edit-status", value: status }]);
}
throw new Error("editMdtodoTaskInline requires --field title, body, or status");
}
async function openMdtodoReportPreview(command) {
ensureProjectManagementCommand("openMdtodoReportPreview");
const beforeUrl = currentPageUrl();
const beforeProject = await projectManagementCommandSnapshot();
const selection = await selectTaskIfCommandTargetsOne(command);
const linkText = commandValue(command, ["link", "value", "text"]);
const links = page.locator('[data-testid="mdtodo-report-link"]');
await links.first().waitFor({ state: "visible", timeout: 15000 });
const count = await links.count();
let index = 0;
if (linkText) {
for (let i = 0; i < count; i += 1) {
const item = links.nth(i);
const text = await item.textContent().catch(() => "");
if (String(text || "").includes(linkText)) {
index = i;
break;
}
}
}
const link = links.nth(index);
const linkStateRaw = await link.evaluate((element, selectedIndex) => ({
index: selectedIndex,
disabled: Boolean(element.disabled) || element.getAttribute("aria-disabled") === "true",
text: String(element.textContent || "").replace(/\s+/gu, " ").trim().slice(0, 240),
valuesRedacted: true
}), index).catch((error) => ({ index, error: errorSummary(error), valuesRedacted: true }));
const linkState = {
...linkStateRaw,
textHash: linkStateRaw.text ? sha256Text(linkStateRaw.text) : null,
textPreview: linkStateRaw.text ? truncate(linkStateRaw.text, 120) : null,
text: undefined,
valuesRedacted: true
};
if (linkState.disabled === true) {
const error = new Error("openMdtodoReportPreview selected report link is disabled");
error.details = { beforeUrl, linkState, beforeProject, valuesRedacted: true };
throw error;
}
const responsePromise = page.waitForResponse((response) => {
try {
const request = response.request();
return request.method().toUpperCase() === "GET" && new URL(response.url()).pathname === "/v1/project-management/mdtodo/report-preview";
} catch {
return false;
}
}, { timeout: 30000 }).then((response) => ({ observed: true, status: response.status(), path: new URL(response.url()).pathname })).catch((error) => ({ observed: false, waitError: errorSummary(error) }));
await link.click();
const response = await responsePromise;
await page.locator('[data-testid="mdtodo-report-preview"], [data-testid="mdtodo-report-error"]').first().waitFor({ state: "visible", timeout: 10000 }).catch(() => null);
return { beforeUrl, afterUrl: currentPageUrl(), type: "openMdtodoReportPreview", selection, linkState, response, beforeProject, afterProject: await projectManagementCommandSnapshot(), pageId, valuesRedacted: true };
}
async function toggleMdtodoReportFullscreen(command) {
ensureProjectManagementCommand("toggleMdtodoReportFullscreen");
const beforeUrl = currentPageUrl();
const beforeProject = await projectManagementCommandSnapshot();
const desired = commandValue(command, ["value", "text"]).toLowerCase();
const dialog = page.locator('[data-testid="mdtodo-report-fullscreen-dialog"]').first();
const initiallyOpen = await visibleLocator(dialog);
let action = "open";
if (desired === "close" || desired === "off") action = "close";
else if (desired === "toggle") action = initiallyOpen ? "close" : "open";
if (action === "close") {
const closeButton = page.locator('[data-testid="mdtodo-report-fullscreen-dialog"] [aria-label="关闭报告"], [aria-label="关闭报告"]').first();
await closeButton.waitFor({ state: "visible", timeout: 10000 });
await closeButton.click();
} else if (!initiallyOpen) {
const button = page.locator('[data-testid="mdtodo-report-fullscreen"]').first();
await button.waitFor({ state: "visible", timeout: 10000 });
await button.click();
}
await page.waitForTimeout(500);
return { beforeUrl, afterUrl: currentPageUrl(), type: "toggleMdtodoReportFullscreen", action, initiallyOpen, fullscreenOpen: await visibleLocator(dialog), beforeProject, afterProject: await projectManagementCommandSnapshot(), pageId, valuesRedacted: true };
}
async function fillNewTaskDraft(command) {
@@ -2253,6 +2382,36 @@ async function launchWorkbenchFromTask(command) {
return false;
}
}, { timeout: 45000 }).catch((error) => ({ waitError: errorSummary(error) }));
const chatResponsePromise = page.waitForResponse((response) => {
const request = response.request();
if (request.method().toUpperCase() !== "POST") return false;
try {
return new URL(response.url()).pathname === "/v1/agent/chat";
} catch {
return false;
}
}, { timeout: 30000 }).then(async (response) => {
let chatPayload = null;
let chatPayloadError = null;
try {
chatPayload = await response.json();
} catch (error) {
chatPayloadError = errorSummary(error);
}
const headers = response.headers();
return {
observed: true,
status: response.status(),
statusText: response.statusText(),
path: new URL(response.url()).pathname,
sessionId: sessionIdFromAgentSessionPayload(chatPayload),
traceId: traceIdFromAgentChatPayload(chatPayload),
otelTraceId: typeof headers["x-hwlab-otel-trace-id"] === "string" ? headers["x-hwlab-otel-trace-id"] : null,
responseParsed: chatPayload !== null,
responseParseError: chatPayloadError,
valuesRedacted: true
};
}).catch((error) => ({ observed: false, waitError: errorSummary(error), valuesRedacted: true }));
await button.click();
const launchResponse = await launchResponsePromise;
if (launchResponse?.waitError) {
@@ -2281,6 +2440,9 @@ async function launchWorkbenchFromTask(command) {
if (workbenchUrl) {
await page.waitForFunction((expectedPath) => window.location.pathname === expectedPath, workbenchUrl, { timeout: 20000 }).catch(() => null);
}
const chat = await chatResponsePromise;
await page.waitForTimeout(1500);
const workbenchSnapshot = await workbenchSessionSnapshot();
return {
beforeUrl,
afterUrl: currentPageUrl(),
@@ -2291,6 +2453,13 @@ async function launchWorkbenchFromTask(command) {
sessionId,
workbenchUrl,
otelTraceId: launchTraceHeader,
chatObserved: chat?.observed === true,
chatStatus: chat?.status ?? null,
chatSessionId: chat?.sessionId ?? null,
chatTraceId: chat?.traceId ?? null,
chatOtelTraceId: chat?.otelTraceId ?? null,
chat,
workbenchSnapshot,
selectedTask: opaqueIdSummary(projectBeforeClick.selectedTaskRefRaw),
projectBeforeClick: sanitizeProjectCommandSnapshot(projectBeforeClick),
buttonState,
@@ -2321,7 +2490,17 @@ async function projectManagementCommandSnapshot(options = {}) {
const fileSelect = document.querySelector('[data-testid="mdtodo-file-select"]');
const sourceOptionCount = sourceSelect ? Array.from(sourceSelect.options || []).filter((option) => option.value).length : 0;
const fileOptionCount = fileSelect ? Array.from(fileSelect.options || []).filter((option) => option.value).length : 0;
const fileOptions = fileSelect ? Array.from(fileSelect.options || []).filter((option) => option.value).map((option) => ({
value: option.value,
label: text(option),
selected: option.selected === true
})) : [];
const selectedFileOption = fileOptions.find((option) => option.selected) || null;
const launch = document.querySelector('[data-testid="mdtodo-workbench-launch"], [data-action="launch-workbench"]');
const bodyRendered = document.querySelector('[data-testid="mdtodo-body-rendered"]');
const reportPreview = document.querySelector('[data-testid="mdtodo-report-preview"]');
const reportFullscreen = document.querySelector('[data-testid="mdtodo-report-fullscreen-dialog"]');
const reportLinks = Array.from(document.querySelectorAll('[data-testid="mdtodo-report-link"]')).filter(visible);
return {
path: window.location.pathname,
pageKind: visible(document.querySelector('[data-testid="project-management-mdtodo"]')) ? "project-management-mdtodo" : visible(document.querySelector('[data-testid="project-management-root"]')) ? "project-management-root" : null,
@@ -2330,6 +2509,8 @@ async function projectManagementCommandSnapshot(options = {}) {
taskCount: Array.from(document.querySelectorAll('[data-task-ref]')).filter(visible).length,
selectedSourceIdRaw: selectedSource?.getAttribute("data-source-id") || sourceSelect?.value || null,
selectedFileRefRaw: selectedFile?.getAttribute("data-file-ref") || fileSelect?.value || null,
selectedFileLabel: selectedFile ? text(selectedFile) : selectedFileOption?.label || null,
fileOptionLabels: fileOptions.map((option) => option.label).filter(Boolean).slice(0, 20),
selectedTaskRefRaw: selectedTask?.getAttribute("data-task-ref") || null,
selectedTaskId: selectedTask?.getAttribute("data-task-id") || selectedTask?.getAttribute("data-rxx-id") || null,
selectedTaskStatus: selectedTask?.getAttribute("data-task-status") || null,
@@ -2337,7 +2518,13 @@ async function projectManagementCommandSnapshot(options = {}) {
fileSelectVisible: visible(fileSelect),
sourceConfigVisible: visible(document.querySelector('[data-testid="mdtodo-source-form-hwpod"], [data-testid="mdtodo-source-config-dialog"], [role="dialog"]')),
taskEditorVisible: visible(document.querySelector('[data-testid="mdtodo-edit-title"], [data-testid="mdtodo-edit-body"]')),
taskBodyVisible: visible(bodyRendered),
taskBodyText: visible(bodyRendered) ? text(bodyRendered) : "",
newTaskDraftVisible: visible(document.querySelector('[data-testid="mdtodo-new-title"], [data-testid="mdtodo-new-body"]')),
reportLinkCount: reportLinks.length,
reportPreviewVisible: visible(reportPreview),
reportPreviewText: visible(reportPreview) ? text(reportPreview) : "",
reportFullscreenVisible: visible(reportFullscreen),
launchButtonVisible: visible(launch),
launchButtonEnabled: visible(launch) && !launch.disabled && launch.getAttribute("aria-disabled") !== "true",
launchButtonText: text(launch),
@@ -2352,6 +2539,16 @@ async function projectManagementCommandSnapshot(options = {}) {
function sanitizeProjectCommandSnapshot(value) {
if (!value || typeof value !== "object") return value;
const textDigest = (raw, limit = 160) => {
const text = String(raw || "");
return { textHash: sha256Text(text), textPreview: truncate(text, limit), textBytes: Buffer.byteLength(text), valuesRedacted: true };
};
const fileLabelLooksDirect = (label) => /^[^/\\]+\.md$/iu.test(String(label || "").trim());
const suspiciousFileLabel = (label) => {
const text = String(label || "").trim();
return Boolean(text && (!fileLabelLooksDirect(text) || /(?:details\/|_Task_Report|_log_|\/)/iu.test(text)));
};
const fileLabels = Array.isArray(value.fileOptionLabels) ? value.fileOptionLabels.map((item) => String(item || "")).filter(Boolean) : [];
return {
...value,
selectedSourceId: opaqueIdSummary(value.selectedSourceIdRaw),
@@ -2360,6 +2557,15 @@ function sanitizeProjectCommandSnapshot(value) {
selectedSourceIdRaw: undefined,
selectedFileRefRaw: undefined,
selectedTaskRefRaw: undefined,
selectedFileLabel: value.selectedFileLabel ? textDigest(value.selectedFileLabel, 120) : null,
selectedFileLabelLooksDirect: value.selectedFileLabel ? fileLabelLooksDirect(value.selectedFileLabel) : null,
fileOptionLabelSamples: fileLabels.slice(0, 8).map((item) => textDigest(item, 120)),
fileOptionSuspiciousLabelCount: fileLabels.filter(suspiciousFileLabel).length,
fileOptionLabels: undefined,
taskBodyText: undefined,
taskBody: value.taskBodyText ? textDigest(value.taskBodyText, 200) : null,
reportPreviewText: undefined,
reportPreview: value.reportPreviewText ? textDigest(value.reportPreviewText, 200) : null,
launchButtonTextHash: value.launchButtonText ? sha256Text(value.launchButtonText) : null,
launchButtonTextPreview: value.launchButtonText ? truncate(value.launchButtonText, 80) : null,
launchButtonText: undefined,
@@ -2758,6 +2964,12 @@ async function sampleOnePage(targetPage, { reason, groupSeq, pageRole, targetPag
const fileSelect = document.querySelector('[data-testid="mdtodo-file-select"]');
const sourceOptionCount = sourceSelect ? Array.from(sourceSelect.options || []).filter((option) => option.value).length : 0;
const fileOptionCount = fileSelect ? Array.from(fileSelect.options || []).filter((option) => option.value).length : 0;
const fileOptions = fileSelect ? Array.from(fileSelect.options || []).filter((option) => option.value).map((option) => ({
value: option.value,
label: trim(option.textContent || option.label || "", 180),
selected: option.selected === true,
})) : [];
const selectedFileOption = fileOptions.find((option) => option.selected) || null;
const taskItems = Array.from(document.querySelectorAll('[data-testid="mdtodo-task-tree"] [data-task-ref], [data-task-ref]')).filter(visible);
const taskCandidates = Array.from(document.querySelectorAll('[data-testid="mdtodo-task-tree"] li, [data-testid="mdtodo-task-tree"] [role="treeitem"], [data-testid="mdtodo-task-tree"] [role="listitem"]')).filter(visible);
const selectedSource = document.querySelector('[data-source-id][data-selected="true"], [data-source-id][aria-selected="true"], [data-source-id].selected, [data-source-id].is-selected');
@@ -2769,6 +2981,10 @@ async function sampleOnePage(targetPage, { reason, groupSeq, pageRole, targetPag
statusCounts[status] = (statusCounts[status] || 0) + 1;
}
const launch = document.querySelector('[data-testid="mdtodo-workbench-launch"], [data-action="launch-workbench"]');
const bodyRendered = document.querySelector('[data-testid="mdtodo-body-rendered"]');
const reportPreview = document.querySelector('[data-testid="mdtodo-report-preview"]');
const reportFullscreen = document.querySelector('[data-testid="mdtodo-report-fullscreen-dialog"]');
const reportLinks = Array.from(document.querySelectorAll('[data-testid="mdtodo-report-link"]')).filter(visible);
const blockers = Array.from(document.querySelectorAll('[data-testid="mdtodo-workbench-launch-blocker"], [data-testid="mdtodo-workbench-launch-error"], [role="alert"]')).filter(visible).slice(0, 12).map((element, index) => ({
index,
testId: element.getAttribute("data-testid"),
@@ -2787,14 +3003,22 @@ async function sampleOnePage(targetPage, { reason, groupSeq, pageRole, targetPag
taskRefMissingCount: Math.max(0, taskCandidates.length - taskItems.length),
selectedSourceId: opaqueDomId(selectedSource?.getAttribute("data-source-id") || sourceSelect?.value),
selectedFileRef: opaqueDomId(selectedFile?.getAttribute("data-file-ref") || fileSelect?.value),
selectedFileLabel: selectedFile ? trim(selectedFile.textContent || "", 180) : selectedFileOption?.label || null,
fileOptionLabels: fileOptions.map((option) => option.label).filter(Boolean).slice(0, 24),
selectedTaskRef: opaqueDomId(selectedTask?.getAttribute("data-task-ref")),
selectedTaskStatus: selectedTask?.getAttribute("data-task-status") || null,
sourceSelectVisible: visible(sourceSelect),
fileSelectVisible: visible(fileSelect),
sourceConfigVisible: visible(document.querySelector('[data-testid="mdtodo-source-form-hwpod"], [data-testid="mdtodo-source-config-dialog"], [role="dialog"]')),
taskEditorVisible: visible(document.querySelector('[data-testid="mdtodo-edit-title"], [data-testid="mdtodo-edit-body"]')),
taskBodyVisible: visible(bodyRendered),
taskBodyText: visible(bodyRendered) ? trim(bodyRendered.textContent || "", 500) : "",
newTaskDraftVisible: visible(document.querySelector('[data-testid="mdtodo-new-title"], [data-testid="mdtodo-new-body"]')),
taskStatusCounts: statusCounts,
reportLinkCount: reportLinks.length,
reportPreviewVisible: visible(reportPreview),
reportPreviewText: visible(reportPreview) ? trim(reportPreview.textContent || "", 500) : "",
reportFullscreenVisible: visible(reportFullscreen),
launchButtonVisible: visible(launch),
launchButtonEnabled: visible(launch) && !launch.disabled && launch.getAttribute("aria-disabled") !== "true",
launchButtonText: trim(launch?.textContent || "", 120),
@@ -2981,6 +3205,12 @@ function digestProjectManagement(value) {
const text = String(raw || "");
return { textHash: sha256Text(text), textPreview: truncate(text, limit), textBytes: Buffer.byteLength(text), valuesRedacted: true };
};
const fileLabelLooksDirect = (label) => /^[^/\\]+\.md$/iu.test(String(label || "").trim());
const suspiciousFileLabel = (label) => {
const text = String(label || "").trim();
return Boolean(text && (!fileLabelLooksDirect(text) || /(?:details\/|_Task_Report|_log_|\/)/iu.test(text)));
};
const fileLabels = Array.isArray(value.fileOptionLabels) ? value.fileOptionLabels.map((item) => String(item || "")).filter(Boolean) : [];
return {
pageKind: value.pageKind ?? null,
configuredPath: value.configuredPath === true,
@@ -2992,14 +3222,24 @@ function digestProjectManagement(value) {
taskRefMissingCount: Number.isFinite(Number(value.taskRefMissingCount)) ? Number(value.taskRefMissingCount) : 0,
selectedSourceId: opaque(value.selectedSourceId),
selectedFileRef: opaque(value.selectedFileRef),
selectedFileLabel: value.selectedFileLabel ? textDigest(value.selectedFileLabel, 120) : null,
selectedFileLabelLooksDirect: value.selectedFileLabel ? fileLabelLooksDirect(value.selectedFileLabel) : null,
fileOptionLabelSamples: fileLabels.slice(0, 10).map((item) => textDigest(item, 120)),
fileOptionSuspiciousLabelCount: fileLabels.filter(suspiciousFileLabel).length,
selectedTaskRef: opaque(value.selectedTaskRef),
selectedTaskStatus: value.selectedTaskStatus ?? null,
sourceSelectVisible: value.sourceSelectVisible === true,
fileSelectVisible: value.fileSelectVisible === true,
sourceConfigVisible: value.sourceConfigVisible === true,
taskEditorVisible: value.taskEditorVisible === true,
taskBodyVisible: value.taskBodyVisible === true,
taskBody: value.taskBodyText ? textDigest(value.taskBodyText, 200) : null,
newTaskDraftVisible: value.newTaskDraftVisible === true,
taskStatusCounts: value.taskStatusCounts && typeof value.taskStatusCounts === "object" ? value.taskStatusCounts : {},
reportLinkCount: Number.isFinite(Number(value.reportLinkCount)) ? Number(value.reportLinkCount) : 0,
reportPreviewVisible: value.reportPreviewVisible === true,
reportPreview: value.reportPreviewText ? textDigest(value.reportPreviewText, 200) : null,
reportFullscreenVisible: value.reportFullscreenVisible === true,
launchButtonVisible: value.launchButtonVisible === true,
launchButtonEnabled: value.launchButtonEnabled === true,
launchButtonText: value.launchButtonText ? textDigest(value.launchButtonText, 120) : null,
@@ -3111,8 +3351,11 @@ function commandInputSummary(command) {
blocking: command.blocking === true ? true : command.blocking === false ? false : null,
sourceId: opaque(command.sourceId),
fileRef: opaque(command.fileRef),
filename: command.filename ? truncate(command.filename, 200) : null,
taskRef: opaque(command.taskRef),
taskId: command.taskId || null,
field: command.field || null,
link: command.link ? truncate(command.link, 200) : null,
titleHash: command.title ? sha256Text(command.title) : null,
titleBytes: command.title ? Buffer.byteLength(command.title) : null,
bodyHash: command.body ? sha256Text(command.body) : null,