diff --git a/src/builders/workflow-builder.ts b/src/builders/workflow-builder.ts index ca6d1d8..dcf6a74 100644 --- a/src/builders/workflow-builder.ts +++ b/src/builders/workflow-builder.ts @@ -189,6 +189,12 @@ export abstract class WorkflowBuilder { return this; } + /** Enable or disable comparison with the previous successful run. */ + monitorChanges(enabled: boolean = true): this { + this.meta.monitor = enabled; + return this; + } + /** * Add a new step to the workflow */ diff --git a/src/client/maxun-client.ts b/src/client/maxun-client.ts index 9eabd22..3f3909b 100644 --- a/src/client/maxun-client.ts +++ b/src/client/maxun-client.ts @@ -19,6 +19,7 @@ import { MaxunError, WorkflowFile, ExecutionOptions, + RunDiffResult, CrawlOptions, SearchOptions, LlmOptions, @@ -143,12 +144,14 @@ export class Client { const httpsAgent = new https.Agent({ keepAlive: false }); const robotTypeValue = (workflowFile.meta as any)?.robotType || (workflowFile.meta as any)?.type; + const { monitor, ...publicMeta } = workflowFile.meta || {} as any; const payload = { ...workflowFile, meta: { - ...workflowFile.meta, + ...publicMeta, type: robotTypeValue, robotType: robotTypeValue, + ...(monitor !== undefined ? { compareRuns: monitor } : {}), } }; @@ -167,7 +170,17 @@ export class Client { * Update an existing robot */ async updateRobot(robotId: string, updates: Partial): Promise { - const response = await this.axios.put>(`/robots/${robotId}`, updates); + const { monitor, ...publicMeta } = updates.meta || {} as any; + const payload = updates.meta + ? { + ...updates, + meta: { + ...publicMeta, + ...(monitor !== undefined ? { compareRuns: monitor } : {}), + }, + } + : updates; + const response = await this.axios.put>(`/robots/${robotId}`, payload); if (!response.data.data) { throw new MaxunError(`Failed to update robot ${robotId}`); } @@ -253,6 +266,18 @@ export class Client { return response.data.data; } + /** Get the detailed monitoring diff for a completed run. */ + async getRunDiff(robotId: string, runId: string, format?: string): Promise { + const response = await this.axios.get>( + `/robots/${robotId}/runs/${runId}/diff`, + { params: format ? { format } : undefined } + ); + if (!response.data.data) { + throw new MaxunError(`Monitoring diff for run ${runId} was not found`, 404); + } + return response.data.data; + } + /** * Abort a running or queued run */ @@ -329,6 +354,7 @@ export class Client { llmApiKey?: string; llmBaseUrl?: string; robotName?: string; + monitor?: boolean; }): Promise { const response = await this.axios.post>( '/extract/llm', @@ -340,6 +366,7 @@ export class Client { ...(options.llmApiKey ? { llmApiKey: options.llmApiKey } : {}), ...(options.llmBaseUrl ? { llmBaseUrl: options.llmBaseUrl } : {}), robotName: options.robotName, + ...(options.monitor !== undefined ? { compareRuns: options.monitor } : {}), }, { timeout: 300000, diff --git a/src/extract.ts b/src/extract.ts index 903b7b2..c9278f6 100644 --- a/src/extract.ts +++ b/src/extract.ts @@ -99,6 +99,7 @@ export class Extract { llmApiKey?: string; llmBaseUrl?: string; robotName?: string; + monitor?: boolean; }): Promise { const robotData = await this.client.extractWithLLM(options); const robot = await this.client.getRobot(robotData.robotId); diff --git a/src/robot/robot.ts b/src/robot/robot.ts index 5ca9359..6a627f3 100644 --- a/src/robot/robot.ts +++ b/src/robot/robot.ts @@ -2,7 +2,7 @@ * Robot class - represents a saved workflow that can be executed */ -import { RunResult, RobotData, ScheduleConfig, WebhookConfig, ExecutionOptions, Run, MaxunError } from '../types'; +import { RunResult, RunDiffResult, RobotData, ScheduleConfig, WebhookConfig, ExecutionOptions, Run, MaxunError } from '../types'; import { Client } from '../client/maxun-client'; export class Robot { @@ -56,6 +56,11 @@ export class Robot { return await this.client.getRun(this.id, runId); } + /** Get the detailed monitoring diff for a completed run. */ + async getRunDiff(runId: string, format?: string): Promise { + return await this.client.getRunDiff(this.id, runId, format); + } + /** * Get the latest run */ diff --git a/src/scrape.ts b/src/scrape.ts index 667593d..0516243 100644 --- a/src/scrape.ts +++ b/src/scrape.ts @@ -27,6 +27,8 @@ export interface ScrapeOptions extends LlmOptions { * Adds 2 extra credits per run on top of the base 1 scrape credit. */ smartQueries?: string; + /** Monitor this robot for changes between successful runs. */ + monitor?: boolean; } export class Scrape { @@ -55,6 +57,7 @@ export class Scrape { robotType: 'scrape', url, formats: options?.formats || ['markdown'], + ...(options?.monitor !== undefined ? { monitor: options.monitor } : {}), ...(options?.smartQueries ? { smartQueries: options.smartQueries } : {}), ...buildLlmPayload(options || {}), } as any, diff --git a/src/types/index.ts b/src/types/index.ts index f07ac7b..c14a00c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -29,6 +29,7 @@ export interface RobotMeta extends LlmOptions { formats?: Format[]; subscriptionLevel?: number; smartQueries?: string; + monitor?: boolean; } export interface Where { @@ -99,6 +100,7 @@ export interface Run { runId: string; startedAt: string; finishedAt: string | null; + hasChanges?: boolean; serializableOutput?: { scrapeSchema?: Record; scrapeList?: Record[]; @@ -155,6 +157,27 @@ export interface RunResult { screenshots?: Array; status: RunStatus; runId: string; + hasChanges?: boolean; + changedFormats?: string[]; +} + +export interface RunDiffChange { + value: string; + added: boolean; + removed: boolean; +} + +export interface RunFormatDiff { + format: string; + changes: RunDiffChange[]; +} + +export interface RunDiffResult { + runId: string; + previousRunId: string | null; + hasChanges: boolean; + changedFormats: string[]; + diffs: RunFormatDiff[]; } export interface ExecutionOptions {