Skip to content

Commit 096c110

Browse files
feat: add monitor.activity endpoint and update examples
- Add ApiMonitorTickStatus, ApiMonitorTickEntry, ApiMonitorActivityResponse types - Add monitor.activity(id, params?) method for paginated tick history - Update monitor examples to poll activity and display diffs - Track seen ticks to avoid duplicate output - Use throw for proper TypeScript type narrowing Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 2eba148 commit 096c110

5 files changed

Lines changed: 156 additions & 21 deletions

File tree

examples/monitor/monitor_basic.ts

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,62 @@
11
import { ScrapeGraphAI } from "scrapegraph-js";
22

3-
// reads SGAI_API_KEY from env, or pass explicitly: ScrapeGraphAI({ apiKey: "..." })
43
const sgai = ScrapeGraphAI();
54

65
const res = await sgai.monitor.create({
7-
url: "https://example.com",
8-
name: "Example Monitor",
9-
interval: "0 * * * *",
10-
formats: [{ type: "markdown" }],
6+
url: "https://time.is/",
7+
name: "Time Monitor",
8+
interval: "*/10 * * * *",
9+
formats: [
10+
{
11+
type: "json",
12+
prompt: "Extract the current time",
13+
schema: {
14+
type: "object",
15+
properties: {
16+
time: { type: "string" },
17+
},
18+
required: ["time"],
19+
},
20+
},
21+
],
1122
});
1223

13-
if (res.status === "success") {
14-
console.log("Monitor created:", res.data?.cronId);
15-
console.log("Status:", res.data?.status);
16-
console.log("Interval:", res.data?.interval);
17-
} else {
18-
console.error("Failed:", res.error);
24+
if (res.status !== "success" || !res.data) {
25+
throw new Error(`Failed to create monitor: ${res.error}`);
26+
}
27+
28+
const { cronId: monitorId, interval } = res.data;
29+
console.log(`Monitor created: ${monitorId}`);
30+
console.log(`Interval: ${interval}`);
31+
console.log("\nPolling for activity (Ctrl+C to stop)...\n");
32+
33+
function cleanup() {
34+
console.log("\nStopping monitor...");
35+
sgai.monitor.delete(monitorId).then(() => {
36+
console.log("Monitor deleted");
37+
process.exit(0);
38+
});
39+
}
40+
41+
process.on("SIGINT", cleanup);
42+
43+
const seenIds = new Set<string>();
44+
45+
while (true) {
46+
const activity = await sgai.monitor.activity(monitorId);
47+
if (activity.status === "success" && activity.data) {
48+
for (const tick of activity.data.ticks) {
49+
if (seenIds.has(tick.id)) continue;
50+
seenIds.add(tick.id);
51+
52+
const changes = tick.changed ? "CHANGED" : "no change";
53+
console.log(`[${tick.createdAt}] ${tick.status} - ${changes} (${tick.elapsedMs}ms)`);
54+
if (tick.diffs && Object.keys(tick.diffs).length > 0) {
55+
console.log(" Diffs:", JSON.stringify(tick.diffs, null, 2));
56+
} else if (tick.changed) {
57+
console.log(" (no diffs data)");
58+
}
59+
}
60+
}
61+
await new Promise((r) => setTimeout(r, 30000));
1962
}
Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,66 @@
11
import { ScrapeGraphAI } from "scrapegraph-js";
22

3-
// reads SGAI_API_KEY from env, or pass explicitly: ScrapeGraphAI({ apiKey: "..." })
43
const sgai = ScrapeGraphAI();
54

65
const res = await sgai.monitor.create({
7-
url: "https://example.com/prices",
8-
name: "Price Monitor",
9-
interval: "0 */6 * * *",
6+
url: "https://time.is/",
7+
name: "Time Monitor with Webhook",
8+
interval: "*/10 * * * *",
109
formats: [
1110
{ type: "markdown" },
12-
{ type: "json", prompt: "Extract all product prices" },
11+
{
12+
type: "json",
13+
prompt: "Extract the current time and timezone",
14+
schema: {
15+
type: "object",
16+
properties: {
17+
time: { type: "string" },
18+
timezone: { type: "string" },
19+
},
20+
required: ["time"],
21+
},
22+
},
1323
],
1424
webhookUrl: "https://your-server.com/webhook",
1525
});
1626

17-
if (res.status === "success") {
18-
console.log("Monitor created:", res.data?.cronId);
19-
console.log("Will notify:", res.data?.config.webhookUrl);
20-
} else {
21-
console.error("Failed:", res.error);
27+
if (res.status !== "success" || !res.data) {
28+
throw new Error(`Failed to create monitor: ${res.error}`);
29+
}
30+
31+
const { cronId: monitorId, interval, config } = res.data;
32+
console.log(`Monitor created: ${monitorId}`);
33+
console.log(`Interval: ${interval}`);
34+
console.log(`Webhook: ${config.webhookUrl}`);
35+
console.log("\nPolling for activity (Ctrl+C to stop)...\n");
36+
37+
function cleanup() {
38+
console.log("\nStopping monitor...");
39+
sgai.monitor.delete(monitorId).then(() => {
40+
console.log("Monitor deleted");
41+
process.exit(0);
42+
});
43+
}
44+
45+
process.on("SIGINT", cleanup);
46+
47+
const seenIds = new Set<string>();
48+
49+
while (true) {
50+
const activity = await sgai.monitor.activity(monitorId);
51+
if (activity.status === "success" && activity.data) {
52+
for (const tick of activity.data.ticks) {
53+
if (seenIds.has(tick.id)) continue;
54+
seenIds.add(tick.id);
55+
56+
const changes = tick.changed ? "CHANGED" : "no change";
57+
console.log(`[${tick.createdAt}] ${tick.status} - ${changes} (${tick.elapsedMs}ms)`);
58+
if (tick.diffs && Object.keys(tick.diffs).length > 0) {
59+
console.log(" Diffs:", JSON.stringify(tick.diffs, null, 2));
60+
} else if (tick.changed) {
61+
console.log(" (no diffs data - first tick establishes baseline)");
62+
}
63+
}
64+
}
65+
await new Promise((r) => setTimeout(r, 30000));
2266
}

src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ export type {
3737
ApiMonitorResponse,
3838
ApiMonitorResult,
3939
ApiMonitorDiffs,
40+
ApiMonitorActivityParams,
41+
ApiMonitorActivityResponse,
42+
ApiMonitorTickEntry,
43+
ApiMonitorTickStatus,
4044
ApiHistoryFilter,
4145
ApiHistoryEntry,
4246
ApiHistoryPage,

src/scrapegraphai.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import type {
99
ApiHistoryEntry,
1010
ApiHistoryFilter,
1111
ApiHistoryPage,
12+
ApiMonitorActivityParams,
13+
ApiMonitorActivityResponse,
1214
ApiMonitorCreateInput,
1315
ApiMonitorResponse,
1416
ApiMonitorUpdateInput,
@@ -342,6 +344,24 @@ export const monitor = {
342344
return fail(err);
343345
}
344346
},
347+
348+
async activity(
349+
apiKey: string,
350+
id: string,
351+
params?: ApiMonitorActivityParams,
352+
): Promise<ApiResult<ApiMonitorActivityResponse>> {
353+
try {
354+
const qs = new URLSearchParams();
355+
if (params?.limit) qs.set("limit", String(params.limit));
356+
if (params?.cursor) qs.set("cursor", params.cursor);
357+
const query = qs.toString();
358+
const path = query ? `/monitor/${id}/activity?${query}` : `/monitor/${id}/activity`;
359+
const { data, elapsedMs } = await request<ApiMonitorActivityResponse>("GET", path, apiKey);
360+
return ok(data, elapsedMs);
361+
} catch (err) {
362+
return fail(err);
363+
}
364+
},
345365
};
346366

347367
export interface ScrapeGraphAIInput {
@@ -381,6 +401,8 @@ export function ScrapeGraphAI(opts?: ScrapeGraphAIInput) {
381401
delete: (id: string) => monitor.delete(key, id),
382402
pause: (id: string) => monitor.pause(key, id),
383403
resume: (id: string) => monitor.resume(key, id),
404+
activity: (id: string, params?: ApiMonitorActivityParams) =>
405+
monitor.activity(key, id, params),
384406
},
385407
};
386408
}

src/types.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,28 @@ export interface ApiMonitorResponse {
298298
updatedAt: string;
299299
}
300300

301+
export type ApiMonitorTickStatus = "completed" | "failed" | "paused" | "running";
302+
303+
export interface ApiMonitorTickEntry {
304+
id: string;
305+
status: ApiMonitorTickStatus;
306+
createdAt: string;
307+
elapsedMs: number;
308+
changed: boolean;
309+
diffs: ApiMonitorDiffs;
310+
error?: string;
311+
}
312+
313+
export interface ApiMonitorActivityResponse {
314+
ticks: ApiMonitorTickEntry[];
315+
nextCursor: string | null;
316+
}
317+
318+
export interface ApiMonitorActivityParams {
319+
limit?: number;
320+
cursor?: string;
321+
}
322+
301323
export type ApiHistoryService = "scrape" | "extract" | "search" | "monitor" | "crawl";
302324
export type ApiHistoryStatus = "completed" | "failed" | "running" | "paused" | "deleted";
303325

0 commit comments

Comments
 (0)