feat: add mdtodo web-probe commands
This commit is contained in:
@@ -48,6 +48,10 @@ export function hwlabNodeWebProbeHelp(): Record<string, unknown> {
|
||||
"bun scripts/cli.ts web-probe observe start --node D601 --lane v03 --target-path /workbench --sample-interval-ms 5000",
|
||||
"bun scripts/cli.ts web-probe observe start --node D601 --lane v03 --target-path /projects/mdtodo --sample-interval-ms 5000",
|
||||
"bun scripts/cli.ts web-probe observe command webobs-xxxx --type sendPrompt --text 'ping'",
|
||||
"bun scripts/cli.ts web-probe observe command webobs-xxxx --type selectMdtodoFile --filename 20260609_频率判断_用户反馈.md",
|
||||
"bun scripts/cli.ts web-probe observe command webobs-xxxx --type openMdtodoReportPreview --task R1 --link R1.1",
|
||||
"bun scripts/cli.ts web-probe observe command webobs-xxxx --type toggleMdtodoReportFullscreen --text toggle",
|
||||
"bun scripts/cli.ts web-probe observe command webobs-xxxx --type editMdtodoTaskInline --task R1 --field body --text 'body updated through web-probe command'",
|
||||
"bun scripts/cli.ts web-probe observe command webobs-xxxx --type launchWorkbenchFromMdtodo --task R1.1",
|
||||
"bun scripts/cli.ts web-probe observe status webobs-xxxx",
|
||||
"bun scripts/cli.ts web-probe observe collect webobs-xxxx --view turn-summary",
|
||||
|
||||
@@ -1019,6 +1019,17 @@ function buildProjectManagementReport(samples, control, network, pagePerformance
|
||||
const launchEnabledSamples = projectSamples.filter((sample) => sample.projectManagement?.launchButtonEnabled === true);
|
||||
const launchVisibleSamples = projectSamples.filter((sample) => sample.projectManagement?.launchButtonVisible === true);
|
||||
const mdtodoSamples = projectSamples.filter((sample) => sample.projectManagement?.pageKind === "project-management-mdtodo");
|
||||
const selectedFileLabelBadSamples = projectSamples.filter((sample) => sample.projectManagement?.selectedFileLabel && sample.projectManagement?.selectedFileLabelLooksDirect === false);
|
||||
const suspiciousFileLabelSamples = projectSamples.filter((sample) => Number(sample.projectManagement?.fileOptionSuspiciousLabelCount ?? 0) > 0);
|
||||
const bodyVisibleSamples = selectedTaskSamples.filter((sample) => sample.projectManagement?.taskBodyVisible === true && Number(sample.projectManagement?.taskBody?.textBytes ?? 0) > 0);
|
||||
const reportLinkSamples = projectSamples.filter((sample) => Number(sample.projectManagement?.reportLinkCount ?? 0) > 0);
|
||||
const reportPreviewSamples = projectSamples.filter((sample) => sample.projectManagement?.reportPreviewVisible === true && Number(sample.projectManagement?.reportPreview?.textBytes ?? 0) > 0);
|
||||
const reportFullscreenSamples = projectSamples.filter((sample) => sample.projectManagement?.reportFullscreenVisible === true);
|
||||
const previewCommands = commandRows.filter((item) => item.type === "openMdtodoReportPreview" || item.type === "toggleMdtodoReportFullscreen");
|
||||
const launchNonEmpty = launchSuccess.filter((item) => item.chatObserved === true && (Number(item.workbenchMessageCount ?? 0) > 0 || Number(item.workbenchTraceRowCount ?? 0) > 0));
|
||||
const launchEmpty = launchSuccess.filter((item) => item.chatObserved !== true || (Number(item.workbenchMessageCount ?? 0) === 0 && Number(item.workbenchTraceRowCount ?? 0) === 0));
|
||||
const minMdtodoTaskCount = minNumber(mdtodoSamples.map((sample) => sample.projectManagement?.taskCount));
|
||||
const maxMdtodoTaskCount = maxNumber(mdtodoSamples.map((sample) => sample.projectManagement?.taskCount));
|
||||
return {
|
||||
enabled,
|
||||
config: config || null,
|
||||
@@ -1041,8 +1052,18 @@ function buildProjectManagementReport(samples, control, network, pagePerformance
|
||||
taskRefMissingMax: maxNumber(projectSamples.map((sample) => sample.projectManagement?.taskRefMissingCount)),
|
||||
latestSelectedTaskRefHash: latestProject?.selectedTaskRef?.hash ?? null,
|
||||
latestSelectedTaskRefPreview: latestProject?.selectedTaskRef?.preview ?? null,
|
||||
latestSelectedFileLabelPreview: latestProject?.selectedFileLabel?.textPreview ?? null,
|
||||
latestSelectedFileLabelLooksDirect: latestProject?.selectedFileLabelLooksDirect ?? null,
|
||||
selectedFileLabelBadSampleCount: selectedFileLabelBadSamples.length,
|
||||
fileOptionSuspiciousLabelSampleCount: suspiciousFileLabelSamples.length,
|
||||
maxFileOptionSuspiciousLabelCount: maxNumber(projectSamples.map((sample) => sample.projectManagement?.fileOptionSuspiciousLabelCount)),
|
||||
latestSelectedTaskStatus: latestProject?.selectedTaskStatus ?? null,
|
||||
latestTaskStatusCounts,
|
||||
selectedTaskBodyVisibleSamples: bodyVisibleSamples.length,
|
||||
reportLinkVisibleSamples: reportLinkSamples.length,
|
||||
maxReportLinkCount: maxNumber(projectSamples.map((sample) => sample.projectManagement?.reportLinkCount)),
|
||||
reportPreviewVisibleSamples: reportPreviewSamples.length,
|
||||
reportFullscreenVisibleSamples: reportFullscreenSamples.length,
|
||||
launchButtonVisibleSamples: launchVisibleSamples.length,
|
||||
launchButtonEnabledSamples: launchEnabledSamples.length,
|
||||
launchButtonDisabledSamples: Math.max(0, launchVisibleSamples.length - launchEnabledSamples.length),
|
||||
@@ -1054,7 +1075,12 @@ function buildProjectManagementReport(samples, control, network, pagePerformance
|
||||
launchCommandCount: launchCommands.length,
|
||||
launchSuccessCount: launchSuccess.length,
|
||||
launchFailureCount: launchFailed.length,
|
||||
launchNonEmptyCount: launchNonEmpty.length,
|
||||
launchEmptyCount: launchEmpty.length,
|
||||
launchWithOtelTraceHeaderCount: launchSuccess.filter((item) => item.otelTraceId).length,
|
||||
reportPreviewCommandCount: previewCommands.length,
|
||||
mdtodoTaskCountMin: minMdtodoTaskCount,
|
||||
mdtodoTaskCountMax: maxMdtodoTaskCount,
|
||||
projectApiEventCount: projectApiEvents.length,
|
||||
projectApiResponseCount: projectApiResponses.length,
|
||||
projectApiFailureCount: projectApiFailures.length,
|
||||
@@ -1075,7 +1101,16 @@ function buildProjectManagementReport(samples, control, network, pagePerformance
|
||||
taskCount: sample.projectManagement?.taskCount ?? null,
|
||||
taskRefMissingCount: sample.projectManagement?.taskRefMissingCount ?? null,
|
||||
selectedTaskRefHash: sample.projectManagement?.selectedTaskRef?.hash ?? null,
|
||||
selectedFileLabelPreview: sample.projectManagement?.selectedFileLabel?.textPreview ?? null,
|
||||
selectedFileLabelLooksDirect: sample.projectManagement?.selectedFileLabelLooksDirect ?? null,
|
||||
fileOptionSuspiciousLabelCount: sample.projectManagement?.fileOptionSuspiciousLabelCount ?? 0,
|
||||
selectedTaskStatus: sample.projectManagement?.selectedTaskStatus ?? null,
|
||||
taskBodyVisible: sample.projectManagement?.taskBodyVisible === true,
|
||||
taskBodyBytes: sample.projectManagement?.taskBody?.textBytes ?? 0,
|
||||
reportLinkCount: sample.projectManagement?.reportLinkCount ?? 0,
|
||||
reportPreviewVisible: sample.projectManagement?.reportPreviewVisible === true,
|
||||
reportPreviewBytes: sample.projectManagement?.reportPreview?.textBytes ?? 0,
|
||||
reportFullscreenVisible: sample.projectManagement?.reportFullscreenVisible === true,
|
||||
launchButtonVisible: sample.projectManagement?.launchButtonVisible === true,
|
||||
launchButtonEnabled: sample.projectManagement?.launchButtonEnabled === true,
|
||||
blockerCount: sample.projectManagement?.blockerCount ?? 0,
|
||||
@@ -1106,6 +1141,11 @@ function compactProjectManagementForOutput(report) {
|
||||
sessionId: item?.sessionId ?? null,
|
||||
workbenchUrl: item?.workbenchUrl ?? null,
|
||||
otelTraceId: item?.otelTraceId ?? null,
|
||||
chatObserved: item?.chatObserved ?? null,
|
||||
chatStatus: item?.chatStatus ?? null,
|
||||
chatTraceId: item?.chatTraceId ?? null,
|
||||
workbenchMessageCount: item?.workbenchMessageCount ?? null,
|
||||
workbenchTraceRowCount: item?.workbenchTraceRowCount ?? null,
|
||||
contractVersion: item?.contractVersion ?? null,
|
||||
selectedTaskRefHash: item?.selectedTaskRefHash ?? null,
|
||||
errorMessageHash: item?.errorMessageHash ?? null,
|
||||
@@ -1161,7 +1201,7 @@ function compactProjectManagementForOutput(report) {
|
||||
|
||||
function projectManagementCommandRows(control, config) {
|
||||
const allowed = new Set(config?.commandAllowlist || []);
|
||||
const mdtodoCommandTypes = new Set(["gotoProjectMdtodo", "openMdtodoSourceConfig", "configureMdtodoHwpodSource", "probeMdtodoSource", "reindexMdtodoSource", "expandMdtodoTask", "editMdtodoTaskTitle", "editMdtodoTaskBody", "toggleMdtodoTaskStatus", "addMdtodoRootTask", "addMdtodoSubTask", "continueMdtodoTask", "deleteMdtodoTask", "launchWorkbenchFromMdtodo"]);
|
||||
const mdtodoCommandTypes = new Set(["gotoProjectMdtodo", "openMdtodoSourceConfig", "configureMdtodoHwpodSource", "probeMdtodoSource", "reindexMdtodoSource", "expandMdtodoTask", "openMdtodoReportPreview", "toggleMdtodoReportFullscreen", "editMdtodoTaskInline", "editMdtodoTaskTitle", "editMdtodoTaskBody", "toggleMdtodoTaskStatus", "addMdtodoRootTask", "addMdtodoSubTask", "continueMdtodoTask", "deleteMdtodoTask", "launchWorkbenchFromMdtodo"]);
|
||||
return (control || [])
|
||||
.filter((item) => allowed.has(item?.type) || mdtodoCommandTypes.has(item?.type) || String(item?.type || "").startsWith("selectMdtodo") || item?.type === "selectProjectSource" || item?.type === "launchWorkbenchFromTask")
|
||||
.filter((item) => item.phase === "completed" || item.phase === "failed")
|
||||
@@ -1178,6 +1218,14 @@ function projectManagementCommandRows(control, config) {
|
||||
sessionId: detail.sessionId ?? error.details?.sessionId ?? null,
|
||||
workbenchUrl: detail.workbenchUrl ?? error.details?.workbenchUrl ?? null,
|
||||
otelTraceId: detail.otelTraceId ?? error.details?.otelTraceId ?? null,
|
||||
chatObserved: detail.chatObserved ?? error.details?.chatObserved ?? null,
|
||||
chatStatus: detail.chatStatus ?? error.details?.chatStatus ?? null,
|
||||
chatSessionId: detail.chatSessionId ?? error.details?.chatSessionId ?? null,
|
||||
chatTraceId: detail.chatTraceId ?? error.details?.chatTraceId ?? null,
|
||||
chatOtelTraceId: detail.chatOtelTraceId ?? error.details?.chatOtelTraceId ?? null,
|
||||
workbenchMessageCount: detail.workbenchSnapshot?.messageCount ?? error.details?.workbenchSnapshot?.messageCount ?? null,
|
||||
workbenchTraceRowCount: detail.workbenchSnapshot?.traceRowCount ?? error.details?.workbenchSnapshot?.traceRowCount ?? null,
|
||||
workbenchComposerReady: detail.workbenchSnapshot?.composerReady ?? error.details?.workbenchSnapshot?.composerReady ?? null,
|
||||
contractVersion: detail.contractVersion ?? error.details?.contractVersion ?? null,
|
||||
selectedTaskRefHash: detail.selectedTask?.hash ?? detail.projectBeforeClick?.selectedTaskRef?.hash ?? null,
|
||||
errorName: error.name ?? null,
|
||||
@@ -1238,6 +1286,28 @@ function buildProjectManagementFindings(report) {
|
||||
if (Number(summary.mdtodoSampleCount ?? 0) > 0 && Number(summary.latestTaskCount ?? 0) > 0 && Number(summary.launchButtonEnabledSamples ?? 0) === 0) {
|
||||
findings.push({ id: "workbench-launch-button-unavailable", severity: "red", summary: "mdtodo tasks were sampled but the Workbench launch button was never enabled", count: summary.mdtodoSampleCount, latest: report.latest, valuesRedacted: true });
|
||||
}
|
||||
if (Number(summary.selectedFileLabelBadSampleCount ?? 0) > 0 || summary.latestSelectedFileLabelLooksDirect === false) {
|
||||
findings.push({ id: "mdtodo-file-label-not-filename", severity: "red", summary: "MDTODO file dropdown selected label is not a direct markdown filename", count: summary.selectedFileLabelBadSampleCount, latestSelectedFileLabelPreview: summary.latestSelectedFileLabelPreview, samples: report.samples.filter((item) => item.selectedFileLabelLooksDirect === false).slice(0, 12), valuesRedacted: true });
|
||||
}
|
||||
if (Number(summary.maxFileOptionSuspiciousLabelCount ?? 0) > 0) {
|
||||
findings.push({ id: "mdtodo-nondirect-files-visible", severity: "red", summary: "MDTODO file dropdown includes non-direct or report-like markdown labels; docs/MDTODO discovery must be direct files only", count: summary.maxFileOptionSuspiciousLabelCount, samples: report.samples.filter((item) => Number(item.fileOptionSuspiciousLabelCount ?? 0) > 0).slice(0, 12), valuesRedacted: true });
|
||||
}
|
||||
if (Number(summary.selectedTaskSampleCount ?? 0) > 0 && Number(summary.selectedTaskBodyVisibleSamples ?? 0) === 0) {
|
||||
findings.push({ id: "mdtodo-task-body-not-visible", severity: "red", summary: "selected MDTODO task was sampled but no rendered task body was visible", count: summary.selectedTaskSampleCount, samples: report.samples.filter((item) => item.selectedTaskRefHash).slice(-12), valuesRedacted: true });
|
||||
}
|
||||
if (Number(summary.maxReportLinkCount ?? 0) > 0 && Number(summary.reportPreviewVisibleSamples ?? 0) === 0) {
|
||||
const severity = Number(summary.reportPreviewCommandCount ?? 0) > 0 ? "red" : "amber";
|
||||
findings.push({ id: "mdtodo-report-preview-missing", severity, summary: "MDTODO report links were visible but no markdown report preview was sampled", count: summary.maxReportLinkCount, previewCommandCount: summary.reportPreviewCommandCount, samples: report.samples.filter((item) => Number(item.reportLinkCount ?? 0) > 0).slice(-12), valuesRedacted: true });
|
||||
}
|
||||
if (Number(summary.reportPreviewCommandCount ?? 0) > 0 && Number(summary.reportFullscreenVisibleSamples ?? 0) === 0 && report.commands.some((item) => item.type === "toggleMdtodoReportFullscreen" && item.phase === "completed")) {
|
||||
findings.push({ id: "mdtodo-report-fullscreen-missing", severity: "red", summary: "toggleMdtodoReportFullscreen command completed but fullscreen report dialog was never sampled", count: summary.reportPreviewCommandCount, commands: report.commands.filter((item) => item.type === "toggleMdtodoReportFullscreen").slice(-8), valuesRedacted: true });
|
||||
}
|
||||
if (Number(summary.launchEmptyCount ?? 0) > 0) {
|
||||
findings.push({ id: "mdtodo-workbench-launch-empty", severity: "red", summary: "MDTODO Workbench launch created a session without observing agent chat or visible message/trace content", count: summary.launchEmptyCount, commands: report.launchCommands.filter((item) => item.chatObserved !== true || (Number(item.workbenchMessageCount ?? 0) === 0 && Number(item.workbenchTraceRowCount ?? 0) === 0)).slice(0, 12), valuesRedacted: true });
|
||||
}
|
||||
if (Number(summary.mdtodoTaskCountMin ?? 0) > 0 && Number(summary.mdtodoTaskCountMax ?? 0) > 0 && (Number(summary.mdtodoTaskCountMax) - Number(summary.mdtodoTaskCountMin) >= 10 || Number(summary.mdtodoTaskCountMax) / Math.max(1, Number(summary.mdtodoTaskCountMin)) >= 2)) {
|
||||
findings.push({ id: "mdtodo-task-count-diverged", severity: "amber", summary: "MDTODO task count varied sharply during observation; compare control commands and observer samples for projection divergence", minTaskCount: summary.mdtodoTaskCountMin, maxTaskCount: summary.mdtodoTaskCountMax, samples: report.samples.slice(-20), valuesRedacted: true });
|
||||
}
|
||||
if (Number(summary.projectApiFailureCount ?? 0) > 0 || Number(summary.projectApiRequestFailedCount ?? 0) > 0) {
|
||||
findings.push({ id: "project-management-api-failed", severity: "amber", summary: "natural project-management or Workbench launch API requests failed during observation", count: Number(summary.projectApiFailureCount ?? 0) + Number(summary.projectApiRequestFailedCount ?? 0), groups: report.projectApiByPath.slice(0, 12), valuesRedacted: true });
|
||||
}
|
||||
@@ -1264,6 +1334,11 @@ function maxNumber(values) {
|
||||
return numeric.length > 0 ? Math.max(...numeric) : 0;
|
||||
}
|
||||
|
||||
function minNumber(values) {
|
||||
const numeric = (values || []).map((value) => Number(value)).filter(Number.isFinite);
|
||||
return numeric.length > 0 ? Math.min(...numeric) : 0;
|
||||
}
|
||||
|
||||
function buildSessionInvariantFindings(control, manifest = {}) {
|
||||
const findings = [];
|
||||
for (const row of control || []) {
|
||||
|
||||
@@ -532,24 +532,24 @@ function projectSummaryFromSamples(){
|
||||
const projectSamples=samples.filter((sample)=>sample?.projectManagement&&typeof sample.projectManagement==='object');
|
||||
const latest=projectSamples[projectSamples.length-1]||null;
|
||||
const latestProject=latest?.projectManagement||null;
|
||||
const projectCommandTypes=new Set(['gotoProjectMdtodo','openMdtodoSourceConfig','configureMdtodoHwpodSource','probeMdtodoSource','reindexMdtodoSource','selectProjectSource','selectMdtodoSource','selectMdtodoFile','selectMdtodoTask','expandMdtodoTask','editMdtodoTaskTitle','editMdtodoTaskBody','toggleMdtodoTaskStatus','addMdtodoRootTask','addMdtodoSubTask','continueMdtodoTask','deleteMdtodoTask','launchWorkbenchFromTask','launchWorkbenchFromMdtodo']);
|
||||
const mutationTypes=new Set(['configureMdtodoHwpodSource','probeMdtodoSource','reindexMdtodoSource','editMdtodoTaskTitle','editMdtodoTaskBody','toggleMdtodoTaskStatus','addMdtodoRootTask','addMdtodoSubTask','continueMdtodoTask','deleteMdtodoTask']);
|
||||
const projectCommandTypes=new Set(['gotoProjectMdtodo','openMdtodoSourceConfig','configureMdtodoHwpodSource','probeMdtodoSource','reindexMdtodoSource','selectProjectSource','selectMdtodoSource','selectMdtodoFile','selectMdtodoTask','expandMdtodoTask','openMdtodoReportPreview','toggleMdtodoReportFullscreen','editMdtodoTaskInline','editMdtodoTaskTitle','editMdtodoTaskBody','toggleMdtodoTaskStatus','addMdtodoRootTask','addMdtodoSubTask','continueMdtodoTask','deleteMdtodoTask','launchWorkbenchFromTask','launchWorkbenchFromMdtodo']);
|
||||
const mutationTypes=new Set(['configureMdtodoHwpodSource','probeMdtodoSource','reindexMdtodoSource','editMdtodoTaskInline','editMdtodoTaskTitle','editMdtodoTaskBody','toggleMdtodoTaskStatus','addMdtodoRootTask','addMdtodoSubTask','continueMdtodoTask','deleteMdtodoTask']);
|
||||
const commandRows=control.filter((item)=>projectCommandTypes.has(item.type)&&(item.phase==='completed'||item.phase==='failed')).map((item)=>{
|
||||
const detail=item.detail&&typeof item.detail==='object'?item.detail:{};
|
||||
const error=detail.error&&typeof detail.error==='object'?detail.error:{};
|
||||
return {ts:item.ts||null,phase:item.phase||null,type:item.type||null,commandId:item.commandId||null,afterPath:pathOnly(item.afterUrl),selectedTaskHash:detail.selectedTask?.hash??detail.projectBeforeClick?.selectedTaskRef?.hash??detail.afterProject?.selectedTaskRef?.hash??null,status:detail.launchStatus??error.details?.launchStatus??null,message:error.message?short(error.message,160):null,valuesRedacted:true};
|
||||
return {ts:item.ts||null,phase:item.phase||null,type:item.type||null,commandId:item.commandId||null,afterPath:pathOnly(item.afterUrl),selectedTaskHash:detail.selectedTask?.hash??detail.projectBeforeClick?.selectedTaskRef?.hash??detail.afterProject?.selectedTaskRef?.hash??null,status:detail.launchStatus??error.details?.launchStatus??null,chatObserved:detail.chatObserved??error.details?.chatObserved??null,workbenchMessageCount:detail.workbenchSnapshot?.messageCount??error.details?.workbenchSnapshot?.messageCount??null,workbenchTraceRowCount:detail.workbenchSnapshot?.traceRowCount??error.details?.workbenchSnapshot?.traceRowCount??null,message:error.message?short(error.message,160):null,valuesRedacted:true};
|
||||
});
|
||||
const mutations=commandRows.filter((item)=>mutationTypes.has(item.type));
|
||||
const launches=control.filter((item)=>(item.type==='launchWorkbenchFromTask'||item.type==='launchWorkbenchFromMdtodo')&&(item.phase==='completed'||item.phase==='failed')).map((item)=>{
|
||||
const detail=item.detail&&typeof item.detail==='object'?item.detail:{};
|
||||
const error=detail.error&&typeof detail.error==='object'?detail.error:{};
|
||||
return {ts:item.ts||null,phase:item.phase||null,commandId:item.commandId||null,status:detail.launchStatus??error.details?.launchStatus??null,sessionId:detail.sessionId??error.details?.sessionId??null,workbenchUrl:detail.workbenchUrl??error.details?.workbenchUrl??null,otelTraceId:detail.otelTraceId??error.details?.otelTraceId??null,taskHash:detail.selectedTask?.hash??detail.projectBeforeClick?.selectedTaskRef?.hash??null,message:error.message?short(error.message,160):null,valuesRedacted:true};
|
||||
return {ts:item.ts||null,phase:item.phase||null,commandId:item.commandId||null,status:detail.launchStatus??error.details?.launchStatus??null,sessionId:detail.sessionId??error.details?.sessionId??null,workbenchUrl:detail.workbenchUrl??error.details?.workbenchUrl??null,otelTraceId:detail.otelTraceId??error.details?.otelTraceId??null,chatObserved:detail.chatObserved??error.details?.chatObserved??null,chatStatus:detail.chatStatus??error.details?.chatStatus??null,chatTraceId:detail.chatTraceId??error.details?.chatTraceId??null,workbenchMessageCount:detail.workbenchSnapshot?.messageCount??error.details?.workbenchSnapshot?.messageCount??null,workbenchTraceRowCount:detail.workbenchSnapshot?.traceRowCount??error.details?.workbenchSnapshot?.traceRowCount??null,taskHash:detail.selectedTask?.hash??detail.projectBeforeClick?.selectedTaskRef?.hash??null,message:error.message?short(error.message,160):null,valuesRedacted:true};
|
||||
});
|
||||
const findings=Array.isArray(report.findings)?report.findings.filter((item)=>String(item?.id||item?.kind||'').match(/project-management|mdtodo|workbench-launch/u)).slice(0,20):[];
|
||||
const summary=report.projectManagement?.summary||{};
|
||||
const mdtodoSampleCount=projectSamples.filter((sample)=>sample.projectManagement?.pageKind==='project-management-mdtodo').length;
|
||||
const derived={enabled:summary.enabled===true||projectSamples.length>0,projectSampleCount:Math.max(Number(summary.projectSampleCount??0),projectSamples.length),mdtodoSampleCount:Math.max(Number(summary.mdtodoSampleCount??0),mdtodoSampleCount),latestPageKind:summary.latestPageKind??latestProject?.pageKind??null,latestPath:summary.latestPath??latest?.path??null,latestSeq:summary.latestSeq??latest?.seq??null,latestTs:summary.latestTs??latest?.ts??null,latestSourceCount:summary.latestSourceCount??latestProject?.sourceCount??null,latestFileCount:summary.latestFileCount??latestProject?.fileCount??null,latestTaskCount:summary.latestTaskCount??latestProject?.taskCount??null,latestSelectedTaskRefHash:summary.latestSelectedTaskRefHash??latestProject?.selectedTaskRef?.hash??null,latestSelectedTaskRefPreview:summary.latestSelectedTaskRefPreview??latestProject?.selectedTaskRef?.preview??null,projectCommandCount:commandRows.length,mutationCommandCount:mutations.length,mutationFailureCount:mutations.filter((item)=>item.phase==='failed').length,launchCommandCount:summary.launchCommandCount??launches.length,launchSuccessCount:summary.launchSuccessCount??launches.filter((item)=>Number(item.status)>=200&&Number(item.status)<300).length,launchFailureCount:summary.launchFailureCount??launches.filter((item)=>item.phase==='failed'||Number(item.status)>=400).length,launchWithOtelTraceHeaderCount:summary.launchWithOtelTraceHeaderCount??launches.filter((item)=>item.otelTraceId).length,projectApiResponseCount:summary.projectApiResponseCount??null,projectApiFailureCount:summary.projectApiFailureCount??null,projectApiRequestFailedCount:summary.projectApiRequestFailedCount??null,projectApiSlowPathCount:summary.projectApiSlowPathCount??null,valuesRedacted:true};
|
||||
return {summary:derived,commands:commandRows.slice(-24),mutations:mutations.slice(-16),launches:launches.slice(-12),findings,sampleRows:projectSamples.slice(-12).map((sample)=>({seq:sample.seq??null,ts:sample.ts??null,pageRole:sample.pageRole??null,path:sample.path??null,pageKind:sample.projectManagement?.pageKind??null,sourceCount:sample.projectManagement?.sourceCount??null,fileCount:sample.projectManagement?.fileCount??null,taskCount:sample.projectManagement?.taskCount??null,selectedTaskRefHash:sample.projectManagement?.selectedTaskRef?.hash??null,selectedTaskStatus:sample.projectManagement?.selectedTaskStatus??null,launchButtonEnabled:sample.projectManagement?.launchButtonEnabled===true,workbenchLinkCount:sample.projectManagement?.workbenchLinkCount??0,valuesRedacted:true})),valuesRedacted:true};
|
||||
const derived={enabled:summary.enabled===true||projectSamples.length>0,projectSampleCount:Math.max(Number(summary.projectSampleCount??0),projectSamples.length),mdtodoSampleCount:Math.max(Number(summary.mdtodoSampleCount??0),mdtodoSampleCount),latestPageKind:summary.latestPageKind??latestProject?.pageKind??null,latestPath:summary.latestPath??latest?.path??null,latestSeq:summary.latestSeq??latest?.seq??null,latestTs:summary.latestTs??latest?.ts??null,latestSourceCount:summary.latestSourceCount??latestProject?.sourceCount??null,latestFileCount:summary.latestFileCount??latestProject?.fileCount??null,latestTaskCount:summary.latestTaskCount??latestProject?.taskCount??null,latestSelectedTaskRefHash:summary.latestSelectedTaskRefHash??latestProject?.selectedTaskRef?.hash??null,latestSelectedTaskRefPreview:summary.latestSelectedTaskRefPreview??latestProject?.selectedTaskRef?.preview??null,latestSelectedFileLabelPreview:summary.latestSelectedFileLabelPreview??latestProject?.selectedFileLabel?.textPreview??null,latestSelectedFileLabelLooksDirect:summary.latestSelectedFileLabelLooksDirect??latestProject?.selectedFileLabelLooksDirect??null,selectedTaskBodyVisibleSamples:summary.selectedTaskBodyVisibleSamples??projectSamples.filter((sample)=>sample.projectManagement?.taskBodyVisible===true).length,reportLinkVisibleSamples:summary.reportLinkVisibleSamples??projectSamples.filter((sample)=>Number(sample.projectManagement?.reportLinkCount??0)>0).length,reportPreviewVisibleSamples:summary.reportPreviewVisibleSamples??projectSamples.filter((sample)=>sample.projectManagement?.reportPreviewVisible===true).length,reportFullscreenVisibleSamples:summary.reportFullscreenVisibleSamples??projectSamples.filter((sample)=>sample.projectManagement?.reportFullscreenVisible===true).length,projectCommandCount:commandRows.length,mutationCommandCount:mutations.length,mutationFailureCount:mutations.filter((item)=>item.phase==='failed').length,launchCommandCount:summary.launchCommandCount??launches.length,launchSuccessCount:summary.launchSuccessCount??launches.filter((item)=>Number(item.status)>=200&&Number(item.status)<300).length,launchFailureCount:summary.launchFailureCount??launches.filter((item)=>item.phase==='failed'||Number(item.status)>=400).length,launchNonEmptyCount:summary.launchNonEmptyCount??launches.filter((item)=>item.chatObserved===true&&(Number(item.workbenchMessageCount??0)>0||Number(item.workbenchTraceRowCount??0)>0)).length,launchEmptyCount:summary.launchEmptyCount??launches.filter((item)=>item.chatObserved!==true||(Number(item.workbenchMessageCount??0)===0&&Number(item.workbenchTraceRowCount??0)===0)).length,launchWithOtelTraceHeaderCount:summary.launchWithOtelTraceHeaderCount??launches.filter((item)=>item.otelTraceId).length,projectApiResponseCount:summary.projectApiResponseCount??null,projectApiFailureCount:summary.projectApiFailureCount??null,projectApiRequestFailedCount:summary.projectApiRequestFailedCount??null,projectApiSlowPathCount:summary.projectApiSlowPathCount??null,valuesRedacted:true};
|
||||
return {summary:derived,commands:commandRows.slice(-24),mutations:mutations.slice(-16),launches:launches.slice(-12),findings,sampleRows:projectSamples.slice(-12).map((sample)=>({seq:sample.seq??null,ts:sample.ts??null,pageRole:sample.pageRole??null,path:sample.path??null,pageKind:sample.projectManagement?.pageKind??null,sourceCount:sample.projectManagement?.sourceCount??null,fileCount:sample.projectManagement?.fileCount??null,taskCount:sample.projectManagement?.taskCount??null,selectedFileLabelPreview:sample.projectManagement?.selectedFileLabel?.textPreview??null,selectedFileLabelLooksDirect:sample.projectManagement?.selectedFileLabelLooksDirect??null,selectedTaskRefHash:sample.projectManagement?.selectedTaskRef?.hash??null,selectedTaskStatus:sample.projectManagement?.selectedTaskStatus??null,taskBodyVisible:sample.projectManagement?.taskBodyVisible===true,taskBodyBytes:sample.projectManagement?.taskBody?.textBytes??0,reportLinkCount:sample.projectManagement?.reportLinkCount??0,reportPreviewVisible:sample.projectManagement?.reportPreviewVisible===true,reportPreviewBytes:sample.projectManagement?.reportPreview?.textBytes??0,reportFullscreenVisible:sample.projectManagement?.reportFullscreenVisible===true,launchButtonEnabled:sample.projectManagement?.launchButtonEnabled===true,workbenchLinkCount:sample.projectManagement?.workbenchLinkCount??0,valuesRedacted:true})),valuesRedacted:true};
|
||||
}
|
||||
function targetNodeFromStateDir(){
|
||||
const parts=String(dir||'').split(/[\\\\/]+/u);
|
||||
@@ -558,11 +558,11 @@ function targetNodeFromStateDir(){
|
||||
}
|
||||
function renderProjectSummary(project){
|
||||
const s=project.summary||{};
|
||||
const lines=['Project MDTODO observer '+(manifest.jobId||'-'),'=======================================================','enabled='+String(s.enabled===true)+' samples='+String(s.projectSampleCount??0)+' mdtodo='+String(s.mdtodoSampleCount??0)+' latest='+String(s.latestPageKind||'-')+' path='+String(s.latestPath||'-'),'counts source='+String(s.latestSourceCount??'-')+' file='+String(s.latestFileCount??'-')+' task='+String(s.latestTaskCount??'-')+' selectedTask='+String(s.latestSelectedTaskRefHash||'-'),'commands='+String(s.projectCommandCount??0)+' mutations='+String(s.mutationCommandCount??0)+' mutationFailures='+String(s.mutationFailureCount??0),'launch commands='+String(s.launchCommandCount??0)+' success='+String(s.launchSuccessCount??0)+' failure='+String(s.launchFailureCount??0)+' otelTraceHeader='+String(s.launchWithOtelTraceHeaderCount??0),'api responses='+String(s.projectApiResponseCount??'-')+' failures='+String(s.projectApiFailureCount??'-')+'/'+String(s.projectApiRequestFailedCount??'-')+' slowPaths='+String(s.projectApiSlowPathCount??'-'),'','Recent samples'];
|
||||
for(const row of project.sampleRows.slice(-12)) lines.push('#'+String(row.seq??'-')+' '+String(row.ts||'-')+' '+String(row.pageRole||'-')+' '+String(row.pageKind||'-')+' src='+String(row.sourceCount??'-')+' files='+String(row.fileCount??'-')+' tasks='+String(row.taskCount??'-')+' selected='+String(row.selectedTaskRefHash||'-')+' launch='+String(row.launchButtonEnabled===true)+' links='+String(row.workbenchLinkCount??0));
|
||||
const lines=['Project MDTODO observer '+(manifest.jobId||'-'),'=======================================================','enabled='+String(s.enabled===true)+' samples='+String(s.projectSampleCount??0)+' mdtodo='+String(s.mdtodoSampleCount??0)+' latest='+String(s.latestPageKind||'-')+' path='+String(s.latestPath||'-'),'counts source='+String(s.latestSourceCount??'-')+' file='+String(s.latestFileCount??'-')+' task='+String(s.latestTaskCount??'-')+' selectedTask='+String(s.latestSelectedTaskRefHash||'-'),'fileLabel='+short(s.latestSelectedFileLabelPreview||'-',80)+' direct='+String(s.latestSelectedFileLabelLooksDirect??'-')+' bodyVisibleSamples='+String(s.selectedTaskBodyVisibleSamples??'-')+' reportPreviewSamples='+String(s.reportPreviewVisibleSamples??'-')+' reportFullscreenSamples='+String(s.reportFullscreenVisibleSamples??'-'),'commands='+String(s.projectCommandCount??0)+' mutations='+String(s.mutationCommandCount??0)+' mutationFailures='+String(s.mutationFailureCount??0),'launch commands='+String(s.launchCommandCount??0)+' success='+String(s.launchSuccessCount??0)+' failure='+String(s.launchFailureCount??0)+' nonEmpty='+String(s.launchNonEmptyCount??'-')+' empty='+String(s.launchEmptyCount??'-')+' otelTraceHeader='+String(s.launchWithOtelTraceHeaderCount??0),'api responses='+String(s.projectApiResponseCount??'-')+' failures='+String(s.projectApiFailureCount??'-')+'/'+String(s.projectApiRequestFailedCount??'-')+' slowPaths='+String(s.projectApiSlowPathCount??'-'),'','Recent samples'];
|
||||
for(const row of project.sampleRows.slice(-12)) lines.push('#'+String(row.seq??'-')+' '+String(row.ts||'-')+' '+String(row.pageRole||'-')+' '+String(row.pageKind||'-')+' src='+String(row.sourceCount??'-')+' files='+String(row.fileCount??'-')+' tasks='+String(row.taskCount??'-')+' fileLabel='+short(row.selectedFileLabelPreview||'-',50)+' body='+String(row.taskBodyVisible===true)+' report='+String(row.reportPreviewVisible===true)+' fullscreen='+String(row.reportFullscreenVisible===true)+' selected='+String(row.selectedTaskRefHash||'-')+' launch='+String(row.launchButtonEnabled===true)+' links='+String(row.workbenchLinkCount??0));
|
||||
lines.push('','Launches');
|
||||
if(project.launches.length===0) lines.push('-');
|
||||
for(const item of project.launches.slice(-12)) lines.push(String(item.ts||'-')+' '+String(item.phase||'-')+' status='+String(item.status??'-')+' session='+String(item.sessionId||'-')+' otel='+String(item.otelTraceId||'-')+' task='+String(item.taskHash||'-'));
|
||||
for(const item of project.launches.slice(-12)) lines.push(String(item.ts||'-')+' '+String(item.phase||'-')+' status='+String(item.status??'-')+' session='+String(item.sessionId||'-')+' chat='+String(item.chatObserved===true)+' chatStatus='+String(item.chatStatus??'-')+' msg='+String(item.workbenchMessageCount??'-')+' traceRows='+String(item.workbenchTraceRowCount??'-')+' otel='+String(item.otelTraceId||'-')+' task='+String(item.taskHash||'-'));
|
||||
lines.push('','MDTODO commands');
|
||||
if(project.commands.length===0) lines.push('-');
|
||||
for(const item of project.commands.slice(-24)) lines.push(String(item.ts||'-')+' '+String(item.phase||'-')+' '+String(item.type||'-')+' path='+String(item.afterPath||'-')+' task='+String(item.selectedTaskHash||'-')+' status='+String(item.status??'-')+(item.message?' message='+short(item.message,120):''));
|
||||
@@ -584,11 +584,11 @@ const rows=turnSummaryRows();
|
||||
if(view==='project-summary'||view==='project-mdtodo-summary'){
|
||||
const project=projectSummaryFromSamples();
|
||||
const projectSummary={...project.summary,latestSelectedTaskRefPreview:short(project.summary?.latestSelectedTaskRefPreview,80)};
|
||||
const projectSampleRows=project.sampleRows.slice(-4).map((item)=>({seq:item.seq??null,ts:short(item.ts,24),pageRole:short(item.pageRole,18),path:short(item.path,36),pageKind:short(item.pageKind,28),sourceCount:item.sourceCount??null,fileCount:item.fileCount??null,taskCount:item.taskCount??null,selectedTaskRefHash:short(item.selectedTaskRefHash,24),selectedTaskStatus:short(item.selectedTaskStatus,20),launchButtonEnabled:item.launchButtonEnabled===true,workbenchLinkCount:item.workbenchLinkCount??0,valuesRedacted:true}));
|
||||
const projectSampleRows=project.sampleRows.slice(-4).map((item)=>({seq:item.seq??null,ts:short(item.ts,24),pageRole:short(item.pageRole,18),path:short(item.path,36),pageKind:short(item.pageKind,28),sourceCount:item.sourceCount??null,fileCount:item.fileCount??null,taskCount:item.taskCount??null,selectedFileLabelPreview:short(item.selectedFileLabelPreview,80),selectedFileLabelLooksDirect:item.selectedFileLabelLooksDirect??null,selectedTaskRefHash:short(item.selectedTaskRefHash,24),selectedTaskStatus:short(item.selectedTaskStatus,20),taskBodyVisible:item.taskBodyVisible===true,taskBodyBytes:item.taskBodyBytes??0,reportLinkCount:item.reportLinkCount??0,reportPreviewVisible:item.reportPreviewVisible===true,reportPreviewBytes:item.reportPreviewBytes??0,reportFullscreenVisible:item.reportFullscreenVisible===true,launchButtonEnabled:item.launchButtonEnabled===true,workbenchLinkCount:item.workbenchLinkCount??0,valuesRedacted:true}));
|
||||
const compactCommand=(item)=>({ts:short(item.ts,24),phase:item.phase??null,type:short(item.type,28),commandId:short(item.commandId,24),afterPath:short(item.afterPath,36),selectedTaskHash:short(item.selectedTaskHash,24),status:item.status??null,message:item.message?short(item.message,80):null,valuesRedacted:true});
|
||||
const projectCommands=project.commands.slice(-14).map(compactCommand);
|
||||
const projectMutations=project.mutations.slice(-10).map(compactCommand);
|
||||
const projectLaunches=project.launches.slice(-4).map((item)=>({ts:short(item.ts,24),phase:item.phase??null,commandId:short(item.commandId,24),status:item.status??null,sessionId:short(item.sessionId,28),workbenchUrl:short(item.workbenchUrl,52),otelTraceId:short(item.otelTraceId,28),taskHash:short(item.taskHash,24),message:item.message?short(item.message,80):null,valuesRedacted:true}));
|
||||
const projectLaunches=project.launches.slice(-4).map((item)=>({ts:short(item.ts,24),phase:item.phase??null,commandId:short(item.commandId,24),status:item.status??null,sessionId:short(item.sessionId,28),workbenchUrl:short(item.workbenchUrl,52),otelTraceId:short(item.otelTraceId,28),chatObserved:item.chatObserved??null,chatStatus:item.chatStatus??null,chatTraceId:short(item.chatTraceId,28),workbenchMessageCount:item.workbenchMessageCount??null,workbenchTraceRowCount:item.workbenchTraceRowCount??null,taskHash:short(item.taskHash,24),message:item.message?short(item.message,80):null,valuesRedacted:true}));
|
||||
const projectFindings=project.findings.slice(0,4).map((item)=>({severity:item.severity??item.level??null,id:short(item.id??item.kind??item.code,48),count:item.count??item.sampleCount??null,summary:short(item.summary??item.message,96),valuesRedacted:true}));
|
||||
console.log(JSON.stringify({ok:true,command:'web-probe-observe collect',view,stateDir:dir,summary:projectSummary,sampleRowCount:project.sampleRows.length,commandCount:project.commands.length,mutationCount:project.mutations.length,launchCount:project.launches.length,findingCount:project.findings.length,sampleRows:projectSampleRows,commands:projectCommands,mutations:projectMutations,launches:projectLaunches,findings:projectFindings,sourceFiles:['samples.jsonl','control.jsonl','analysis/report.json'],valuesRedacted:true}));
|
||||
process.exit(0);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -130,10 +130,13 @@ export type NodeWebProbeObserveCommandType =
|
||||
| "selectMdtodoFile"
|
||||
| "selectMdtodoTask"
|
||||
| "expandMdtodoTask"
|
||||
| "openMdtodoReportPreview"
|
||||
| "toggleMdtodoReportFullscreen"
|
||||
| "openMdtodoSourceConfig"
|
||||
| "configureMdtodoHwpodSource"
|
||||
| "probeMdtodoSource"
|
||||
| "reindexMdtodoSource"
|
||||
| "editMdtodoTaskInline"
|
||||
| "editMdtodoTaskTitle"
|
||||
| "editMdtodoTaskBody"
|
||||
| "toggleMdtodoTaskStatus"
|
||||
@@ -198,8 +201,11 @@ export interface NodeWebProbeObserveOptions {
|
||||
commandBlocking: boolean | null;
|
||||
commandSourceId: string | null;
|
||||
commandFileRef: string | null;
|
||||
commandFilename: string | null;
|
||||
commandTaskRef: string | null;
|
||||
commandTaskId: string | null;
|
||||
commandField: string | null;
|
||||
commandLink: string | null;
|
||||
commandTitle: string | null;
|
||||
commandBody: string | null;
|
||||
commandStatus: string | null;
|
||||
|
||||
@@ -209,9 +209,12 @@ export function parseNodeWebProbeObserveOptions(
|
||||
"--finding-id",
|
||||
"--source-id",
|
||||
"--file-ref",
|
||||
"--filename",
|
||||
"--task-ref",
|
||||
"--task",
|
||||
"--task-id",
|
||||
"--field",
|
||||
"--link",
|
||||
"--title",
|
||||
"--body",
|
||||
"--status",
|
||||
@@ -270,8 +273,11 @@ export function parseNodeWebProbeObserveOptions(
|
||||
const commandText = commandTextFromStdin ? readFileSync(0, "utf8") : commandTextOption;
|
||||
const commandSourceId = optionValue(args, "--source-id") ?? null;
|
||||
const commandFileRef = optionValue(args, "--file-ref") ?? null;
|
||||
const commandFilename = optionValue(args, "--filename") ?? null;
|
||||
const commandTaskRef = optionValue(args, "--task-ref") ?? null;
|
||||
const commandTaskId = optionValue(args, "--task-id") ?? optionValue(args, "--task") ?? null;
|
||||
const commandField = optionValue(args, "--field") ?? null;
|
||||
const commandLink = optionValue(args, "--link") ?? null;
|
||||
const commandTitle = optionValue(args, "--title") ?? null;
|
||||
const commandBody = optionValue(args, "--body") ?? null;
|
||||
const commandStatus = optionValue(args, "--status") ?? null;
|
||||
@@ -296,8 +302,11 @@ export function parseNodeWebProbeObserveOptions(
|
||||
["--finding-id", commandFindingId],
|
||||
["--source-id", commandSourceId],
|
||||
["--file-ref", commandFileRef],
|
||||
["--filename", commandFilename],
|
||||
["--task-ref", commandTaskRef],
|
||||
["--task/--task-id", commandTaskId],
|
||||
["--field", commandField],
|
||||
["--link", commandLink],
|
||||
["--title", commandTitle],
|
||||
["--body", commandBody],
|
||||
["--status", commandStatus],
|
||||
@@ -359,8 +368,11 @@ export function parseNodeWebProbeObserveOptions(
|
||||
commandBlocking,
|
||||
commandSourceId,
|
||||
commandFileRef,
|
||||
commandFilename,
|
||||
commandTaskRef,
|
||||
commandTaskId,
|
||||
commandField,
|
||||
commandLink,
|
||||
commandTitle,
|
||||
commandBody,
|
||||
commandStatus,
|
||||
@@ -391,10 +403,13 @@ export function parseNodeWebProbeObserveCommandType(value: string): NodeWebProbe
|
||||
|| value === "selectMdtodoFile"
|
||||
|| value === "selectMdtodoTask"
|
||||
|| value === "expandMdtodoTask"
|
||||
|| value === "openMdtodoReportPreview"
|
||||
|| value === "toggleMdtodoReportFullscreen"
|
||||
|| value === "openMdtodoSourceConfig"
|
||||
|| value === "configureMdtodoHwpodSource"
|
||||
|| value === "probeMdtodoSource"
|
||||
|| value === "reindexMdtodoSource"
|
||||
|| value === "editMdtodoTaskInline"
|
||||
|| value === "editMdtodoTaskTitle"
|
||||
|| value === "editMdtodoTaskBody"
|
||||
|| value === "toggleMdtodoTaskStatus"
|
||||
@@ -408,7 +423,7 @@ export function parseNodeWebProbeObserveCommandType(value: string): NodeWebProbe
|
||||
|| value === "mark"
|
||||
|| value === "stop"
|
||||
) return value;
|
||||
throw new Error(`web-probe observe command --type must be login, preflight, goto, gotoProjectMdtodo, newSession, sendPrompt, steer, cancel, selectProvider, clickSession, refreshCurrentSession, switchAwayAndBack, assertSessionInvariant, selectProjectSource, selectMdtodoSource, selectMdtodoFile, selectMdtodoTask, expandMdtodoTask, openMdtodoSourceConfig, configureMdtodoHwpodSource, probeMdtodoSource, reindexMdtodoSource, editMdtodoTaskTitle, editMdtodoTaskBody, toggleMdtodoTaskStatus, addMdtodoRootTask, addMdtodoSubTask, continueMdtodoTask, deleteMdtodoTask, launchWorkbenchFromTask, launchWorkbenchFromMdtodo, screenshot, mark, or stop; got ${value}`);
|
||||
throw new Error(`web-probe observe command --type must be login, preflight, goto, gotoProjectMdtodo, newSession, sendPrompt, steer, cancel, selectProvider, clickSession, refreshCurrentSession, switchAwayAndBack, assertSessionInvariant, selectProjectSource, selectMdtodoSource, selectMdtodoFile, selectMdtodoTask, expandMdtodoTask, openMdtodoReportPreview, toggleMdtodoReportFullscreen, openMdtodoSourceConfig, configureMdtodoHwpodSource, probeMdtodoSource, reindexMdtodoSource, editMdtodoTaskInline, editMdtodoTaskTitle, editMdtodoTaskBody, toggleMdtodoTaskStatus, addMdtodoRootTask, addMdtodoSubTask, continueMdtodoTask, deleteMdtodoTask, launchWorkbenchFromTask, launchWorkbenchFromMdtodo, screenshot, mark, or stop; got ${value}`);
|
||||
}
|
||||
|
||||
export function parseWebProbeBrowserProxyMode(value: string | undefined): WebProbeBrowserProxyMode {
|
||||
@@ -1369,8 +1384,11 @@ export function runNodeWebProbeObserveCommand(options: NodeWebProbeObserveOption
|
||||
blocking: options.commandBlocking,
|
||||
sourceId: options.commandSourceId,
|
||||
fileRef: options.commandFileRef,
|
||||
filename: options.commandFilename,
|
||||
taskRef: options.commandTaskRef,
|
||||
taskId: options.commandTaskId,
|
||||
field: options.commandField,
|
||||
link: options.commandLink,
|
||||
title: options.commandTitle,
|
||||
body: options.commandBody,
|
||||
status: options.commandStatus,
|
||||
|
||||
Reference in New Issue
Block a user