@commandgarden/cli 2.12.1 → 2.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +58 -28
- package/node_modules/@commandgarden/app/dist/client/assets/{Slides-CHeVcB9K.js → Slides-DWQmJfkp.js} +1 -1
- package/node_modules/@commandgarden/app/dist/client/assets/{index-DR1TjI6k.js → index-C8yqRQk-.js} +37 -37
- package/node_modules/@commandgarden/app/dist/client/assets/index-rHqHbsBw.css +1 -0
- package/node_modules/@commandgarden/app/dist/client/index.html +19 -19
- package/node_modules/@commandgarden/app/dist/server/journal/journal.service.d.ts +2 -0
- package/node_modules/@commandgarden/app/dist/server/journal/journal.service.js +54 -24
- package/node_modules/@commandgarden/app/dist/server/journal/journal.types.d.ts +8 -0
- package/node_modules/@commandgarden/app/dist/server/main.js +1 -1
- package/node_modules/@commandgarden/app/dist/server/routes/audit.js +27 -16
- package/node_modules/@commandgarden/app/dist/server/routes/config.js +8 -2
- package/node_modules/@commandgarden/app/dist/server/routes/connectors.js +37 -26
- package/node_modules/@commandgarden/app/dist/server/routes/run.js +3 -4
- package/node_modules/@commandgarden/app/dist/server/routes/status.js +2 -1
- package/node_modules/@commandgarden/app/dist/server/routes/utils.d.ts +2 -0
- package/node_modules/@commandgarden/app/dist/server/routes/utils.js +4 -0
- package/node_modules/@commandgarden/app/dist/server/store.js +53 -53
- package/node_modules/@commandgarden/app/package.json +42 -42
- package/node_modules/@commandgarden/chrome/dist/content-script.js.map +1 -1
- package/node_modules/@commandgarden/chrome/dist/manifest.json +26 -26
- package/node_modules/@commandgarden/chrome/dist/popup.html +218 -218
- package/node_modules/@commandgarden/chrome/dist/popup.js.map +1 -1
- package/node_modules/@commandgarden/chrome/dist/service-worker.js.map +1 -1
- package/node_modules/@commandgarden/chrome/package.json +26 -26
- package/node_modules/@commandgarden/daemon/connectors/ado-git-commits.eval.js +100 -100
- package/node_modules/@commandgarden/daemon/connectors/ado-git-commits.yaml +71 -71
- package/node_modules/@commandgarden/daemon/connectors/gcs-kb-content.yaml +61 -61
- package/node_modules/@commandgarden/daemon/connectors/gcs-kb-pages.yaml +54 -54
- package/node_modules/@commandgarden/daemon/connectors/jira-my-tickets.eval.js +115 -115
- package/node_modules/@commandgarden/daemon/connectors/jira-my-tickets.yaml +54 -54
- package/node_modules/@commandgarden/daemon/connectors/lib/msal-token.js +20 -20
- package/node_modules/@commandgarden/daemon/connectors/outlook-my-meetings.eval.js +204 -204
- package/node_modules/@commandgarden/daemon/connectors/outlook-my-meetings.yaml +42 -42
- package/node_modules/@commandgarden/daemon/connectors/saba-pending-training.eval.js +94 -94
- package/node_modules/@commandgarden/daemon/connectors/saba-pending-training.yaml +34 -34
- package/node_modules/@commandgarden/daemon/connectors/socket-security-news.yaml +63 -63
- package/node_modules/@commandgarden/daemon/connectors/teams-room-availability.eval.js +437 -437
- package/node_modules/@commandgarden/daemon/connectors/teams-room-availability.yaml +53 -53
- package/node_modules/@commandgarden/daemon/connectors/teams-rooms-availability.eval.js +547 -547
- package/node_modules/@commandgarden/daemon/connectors/teams-rooms-availability.yaml +48 -48
- package/node_modules/@commandgarden/daemon/connectors/timetracking-projects.eval.js +52 -52
- package/node_modules/@commandgarden/daemon/connectors/timetracking-projects.yaml +42 -42
- package/node_modules/@commandgarden/daemon/connectors/timetracking-report.eval.js +81 -81
- package/node_modules/@commandgarden/daemon/connectors/timetracking-report.yaml +52 -52
- package/node_modules/@commandgarden/daemon/connectors/tldrsec-newsletter.eval.js +27 -27
- package/node_modules/@commandgarden/daemon/connectors/tldrsec-newsletter.yaml +38 -38
- package/node_modules/@commandgarden/daemon/connectors/tokenmaster-clients-list.yaml +55 -55
- package/node_modules/@commandgarden/daemon/dist/audit-store.js +8 -8
- package/node_modules/@commandgarden/daemon/dist/auth.d.ts +1 -0
- package/node_modules/@commandgarden/daemon/dist/auth.d.ts.map +1 -1
- package/node_modules/@commandgarden/daemon/dist/auth.js +10 -0
- package/node_modules/@commandgarden/daemon/dist/auth.js.map +1 -1
- package/node_modules/@commandgarden/daemon/dist/main.js +12 -5
- package/node_modules/@commandgarden/daemon/dist/main.js.map +1 -1
- package/node_modules/@commandgarden/daemon/dist/server.d.ts.map +1 -1
- package/node_modules/@commandgarden/daemon/dist/server.js +10 -0
- package/node_modules/@commandgarden/daemon/dist/server.js.map +1 -1
- package/node_modules/@commandgarden/daemon/package.json +32 -32
- package/node_modules/@commandgarden/shared/dist/daemon-client.d.ts +5 -1
- package/node_modules/@commandgarden/shared/dist/daemon-client.d.ts.map +1 -1
- package/node_modules/@commandgarden/shared/dist/daemon-client.js +32 -14
- package/node_modules/@commandgarden/shared/dist/daemon-client.js.map +1 -1
- package/node_modules/@commandgarden/shared/package.json +32 -32
- package/package.json +58 -58
- package/node_modules/@commandgarden/app/dist/client/assets/index-CDD2bVHm.css +0 -1
- package/node_modules/@commandgarden/app/dist/server/daemon-client.test.js +0 -47
- package/node_modules/@commandgarden/app/dist/server/routes/routes.test.js +0 -137
- package/node_modules/@commandgarden/app/dist/server/store.test.js +0 -59
- package/node_modules/@commandgarden/chrome/dist/fetch-interceptor.js +0 -44
- package/node_modules/@commandgarden/daemon/dist/cdp-client.d.ts +0 -24
- package/node_modules/@commandgarden/daemon/dist/cdp-client.d.ts.map +0 -1
- package/node_modules/@commandgarden/daemon/dist/cdp-client.js +0 -150
- package/node_modules/@commandgarden/daemon/dist/cdp-client.js.map +0 -1
|
@@ -1,94 +1,94 @@
|
|
|
1
|
-
// Runs in Saba Cloud page context via js_evaluate step.
|
|
2
|
-
// Fetches pending mandatory learning items from Saba Cloud (v64+) using two
|
|
3
|
-
// confirmed API endpoints:
|
|
4
|
-
//
|
|
5
|
-
// 1. GET /Saba/api/ui/torque/uicontext/currentuser
|
|
6
|
-
// → userInfo.userId (e.g. "emplo000000001653633")
|
|
7
|
-
//
|
|
8
|
-
// 2. GET /Saba/api/common/todocontroller/detail/{userId}?context=learning&expand=savetabpref
|
|
9
|
-
// → searchResults[1] (Saba list format: ["list", [...items]])
|
|
10
|
-
|
|
11
|
-
const ORIGIN = 'https://daimler.sabacloud.com';
|
|
12
|
-
|
|
13
|
-
// ── Wait for valid Saba session (SSO / redirects, up to 60s) ─────────────
|
|
14
|
-
|
|
15
|
-
const __deadline = Date.now() + 60000;
|
|
16
|
-
while (Date.now() < __deadline) {
|
|
17
|
-
if (location.hostname === 'daimler.sabacloud.com' && document.readyState === 'complete') break;
|
|
18
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
19
|
-
}
|
|
20
|
-
if (location.hostname !== 'daimler.sabacloud.com') {
|
|
21
|
-
throw new Error('AUTH_REQUIRED: Not signed in to Saba — log in at daimler.sabacloud.com and retry');
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// ── Step 1: Resolve current user ID ──────────────────────────────────────
|
|
25
|
-
|
|
26
|
-
const meResp = await fetch(`${ORIGIN}/Saba/api/ui/torque/uicontext/currentuser`, {
|
|
27
|
-
headers: { Accept: 'application/json' },
|
|
28
|
-
credentials: 'include',
|
|
29
|
-
});
|
|
30
|
-
if (meResp.status === 401 || meResp.status === 403) {
|
|
31
|
-
throw new Error('AUTH_REQUIRED: Saba session expired — log in and retry');
|
|
32
|
-
}
|
|
33
|
-
if (!meResp.ok) {
|
|
34
|
-
throw new Error(`currentuser API returned HTTP ${meResp.status}`);
|
|
35
|
-
}
|
|
36
|
-
const meData = await meResp.json();
|
|
37
|
-
const userId = meData?.userInfo?.userId;
|
|
38
|
-
if (!userId) {
|
|
39
|
-
throw new Error('Could not read userInfo.userId from currentuser response');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// ── Step 2: Fetch pending learning to-do items ────────────────────────────
|
|
43
|
-
|
|
44
|
-
const todoResp = await fetch(
|
|
45
|
-
`${ORIGIN}/Saba/api/common/todocontroller/detail/${encodeURIComponent(userId)}?context=learning&expand=savetabpref`,
|
|
46
|
-
{ headers: { Accept: 'application/json' }, credentials: 'include' }
|
|
47
|
-
);
|
|
48
|
-
if (!todoResp.ok) {
|
|
49
|
-
throw new Error(`todocontroller API returned HTTP ${todoResp.status} for userId=${userId}`);
|
|
50
|
-
}
|
|
51
|
-
const todoData = await todoResp.json();
|
|
52
|
-
|
|
53
|
-
// searchResults is serialised as ["list", [item, item, ...]]
|
|
54
|
-
const rawList = todoData?.searchResults;
|
|
55
|
-
const items = Array.isArray(rawList)
|
|
56
|
-
? (rawList[0] === 'list' ? rawList[1] : rawList)
|
|
57
|
-
: [];
|
|
58
|
-
|
|
59
|
-
if (!Array.isArray(items)) {
|
|
60
|
-
throw new Error('Unexpected todocontroller response shape — searchResults is not a list');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// ── Step 3: Process and return rows ──────────────────────────────────────
|
|
64
|
-
|
|
65
|
-
const DONE_STATUSES = new Set([
|
|
66
|
-
'completed', 'passed', 'withdrawn', 'cancelled', 'waived', 'successful',
|
|
67
|
-
]);
|
|
68
|
-
|
|
69
|
-
const today = new Date();
|
|
70
|
-
today.setHours(0, 0, 0, 0);
|
|
71
|
-
|
|
72
|
-
return items
|
|
73
|
-
.filter(item => {
|
|
74
|
-
if (!item.mandatory) return false;
|
|
75
|
-
const status = (item.itemDisplayStatus || '').toLowerCase();
|
|
76
|
-
return !DONE_STATUSES.has(status);
|
|
77
|
-
})
|
|
78
|
-
.map(item => {
|
|
79
|
-
const dueDateMs = item.dueDate?.date ?? null;
|
|
80
|
-
const dueDateObj = dueDateMs ? new Date(dueDateMs) : null;
|
|
81
|
-
const daysUntilDue = item.dueDateTs?.localDaysDifference
|
|
82
|
-
?? (dueDateObj !== null
|
|
83
|
-
? Math.ceil((dueDateObj.getTime() - today.getTime()) / 86400000)
|
|
84
|
-
: null);
|
|
85
|
-
return {
|
|
86
|
-
title: item.itemName || '(unknown)',
|
|
87
|
-
type: item.itemType || 'Training',
|
|
88
|
-
status: item.itemDisplayStatus || 'Pending',
|
|
89
|
-
dueDate: dueDateObj ? dueDateObj.toISOString().slice(0, 10) : '',
|
|
90
|
-
daysUntilDue: daysUntilDue ?? 9999,
|
|
91
|
-
isOverdue: item.overdue ?? (daysUntilDue !== null && daysUntilDue < 0),
|
|
92
|
-
};
|
|
93
|
-
})
|
|
94
|
-
.sort((a, b) => a.daysUntilDue - b.daysUntilDue);
|
|
1
|
+
// Runs in Saba Cloud page context via js_evaluate step.
|
|
2
|
+
// Fetches pending mandatory learning items from Saba Cloud (v64+) using two
|
|
3
|
+
// confirmed API endpoints:
|
|
4
|
+
//
|
|
5
|
+
// 1. GET /Saba/api/ui/torque/uicontext/currentuser
|
|
6
|
+
// → userInfo.userId (e.g. "emplo000000001653633")
|
|
7
|
+
//
|
|
8
|
+
// 2. GET /Saba/api/common/todocontroller/detail/{userId}?context=learning&expand=savetabpref
|
|
9
|
+
// → searchResults[1] (Saba list format: ["list", [...items]])
|
|
10
|
+
|
|
11
|
+
const ORIGIN = 'https://daimler.sabacloud.com';
|
|
12
|
+
|
|
13
|
+
// ── Wait for valid Saba session (SSO / redirects, up to 60s) ─────────────
|
|
14
|
+
|
|
15
|
+
const __deadline = Date.now() + 60000;
|
|
16
|
+
while (Date.now() < __deadline) {
|
|
17
|
+
if (location.hostname === 'daimler.sabacloud.com' && document.readyState === 'complete') break;
|
|
18
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
19
|
+
}
|
|
20
|
+
if (location.hostname !== 'daimler.sabacloud.com') {
|
|
21
|
+
throw new Error('AUTH_REQUIRED: Not signed in to Saba — log in at daimler.sabacloud.com and retry');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ── Step 1: Resolve current user ID ──────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
const meResp = await fetch(`${ORIGIN}/Saba/api/ui/torque/uicontext/currentuser`, {
|
|
27
|
+
headers: { Accept: 'application/json' },
|
|
28
|
+
credentials: 'include',
|
|
29
|
+
});
|
|
30
|
+
if (meResp.status === 401 || meResp.status === 403) {
|
|
31
|
+
throw new Error('AUTH_REQUIRED: Saba session expired — log in and retry');
|
|
32
|
+
}
|
|
33
|
+
if (!meResp.ok) {
|
|
34
|
+
throw new Error(`currentuser API returned HTTP ${meResp.status}`);
|
|
35
|
+
}
|
|
36
|
+
const meData = await meResp.json();
|
|
37
|
+
const userId = meData?.userInfo?.userId;
|
|
38
|
+
if (!userId) {
|
|
39
|
+
throw new Error('Could not read userInfo.userId from currentuser response');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ── Step 2: Fetch pending learning to-do items ────────────────────────────
|
|
43
|
+
|
|
44
|
+
const todoResp = await fetch(
|
|
45
|
+
`${ORIGIN}/Saba/api/common/todocontroller/detail/${encodeURIComponent(userId)}?context=learning&expand=savetabpref`,
|
|
46
|
+
{ headers: { Accept: 'application/json' }, credentials: 'include' }
|
|
47
|
+
);
|
|
48
|
+
if (!todoResp.ok) {
|
|
49
|
+
throw new Error(`todocontroller API returned HTTP ${todoResp.status} for userId=${userId}`);
|
|
50
|
+
}
|
|
51
|
+
const todoData = await todoResp.json();
|
|
52
|
+
|
|
53
|
+
// searchResults is serialised as ["list", [item, item, ...]]
|
|
54
|
+
const rawList = todoData?.searchResults;
|
|
55
|
+
const items = Array.isArray(rawList)
|
|
56
|
+
? (rawList[0] === 'list' ? rawList[1] : rawList)
|
|
57
|
+
: [];
|
|
58
|
+
|
|
59
|
+
if (!Array.isArray(items)) {
|
|
60
|
+
throw new Error('Unexpected todocontroller response shape — searchResults is not a list');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ── Step 3: Process and return rows ──────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
const DONE_STATUSES = new Set([
|
|
66
|
+
'completed', 'passed', 'withdrawn', 'cancelled', 'waived', 'successful',
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
const today = new Date();
|
|
70
|
+
today.setHours(0, 0, 0, 0);
|
|
71
|
+
|
|
72
|
+
return items
|
|
73
|
+
.filter(item => {
|
|
74
|
+
if (!item.mandatory) return false;
|
|
75
|
+
const status = (item.itemDisplayStatus || '').toLowerCase();
|
|
76
|
+
return !DONE_STATUSES.has(status);
|
|
77
|
+
})
|
|
78
|
+
.map(item => {
|
|
79
|
+
const dueDateMs = item.dueDate?.date ?? null;
|
|
80
|
+
const dueDateObj = dueDateMs ? new Date(dueDateMs) : null;
|
|
81
|
+
const daysUntilDue = item.dueDateTs?.localDaysDifference
|
|
82
|
+
?? (dueDateObj !== null
|
|
83
|
+
? Math.ceil((dueDateObj.getTime() - today.getTime()) / 86400000)
|
|
84
|
+
: null);
|
|
85
|
+
return {
|
|
86
|
+
title: item.itemName || '(unknown)',
|
|
87
|
+
type: item.itemType || 'Training',
|
|
88
|
+
status: item.itemDisplayStatus || 'Pending',
|
|
89
|
+
dueDate: dueDateObj ? dueDateObj.toISOString().slice(0, 10) : '',
|
|
90
|
+
daysUntilDue: daysUntilDue ?? 9999,
|
|
91
|
+
isOverdue: item.overdue ?? (daysUntilDue !== null && daysUntilDue < 0),
|
|
92
|
+
};
|
|
93
|
+
})
|
|
94
|
+
.sort((a, b) => a.daysUntilDue - b.daysUntilDue);
|
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
site: saba
|
|
2
|
-
name: pending-training
|
|
3
|
-
version: "1.0"
|
|
4
|
-
description: "Pending mandatory learning plan items from Saba Cloud (daimler.sabacloud.com)"
|
|
5
|
-
access: read
|
|
6
|
-
|
|
7
|
-
domains:
|
|
8
|
-
- "daimler.sabacloud.com"
|
|
9
|
-
capabilities:
|
|
10
|
-
- navigate
|
|
11
|
-
- js_evaluate
|
|
12
|
-
|
|
13
|
-
columns:
|
|
14
|
-
- name: title
|
|
15
|
-
type: string
|
|
16
|
-
- name: type
|
|
17
|
-
type: string
|
|
18
|
-
- name: status
|
|
19
|
-
type: string
|
|
20
|
-
- name: dueDate
|
|
21
|
-
type: string
|
|
22
|
-
- name: daysUntilDue
|
|
23
|
-
type: number
|
|
24
|
-
- name: isOverdue
|
|
25
|
-
type: boolean
|
|
26
|
-
|
|
27
|
-
pipeline:
|
|
28
|
-
- step: navigate
|
|
29
|
-
url: "https://daimler.sabacloud.com/"
|
|
30
|
-
- step: wait
|
|
31
|
-
selector: "body"
|
|
32
|
-
timeout: 60000
|
|
33
|
-
- step: js_evaluate
|
|
34
|
-
file: saba-pending-training.eval.js
|
|
1
|
+
site: saba
|
|
2
|
+
name: pending-training
|
|
3
|
+
version: "1.0"
|
|
4
|
+
description: "Pending mandatory learning plan items from Saba Cloud (daimler.sabacloud.com)"
|
|
5
|
+
access: read
|
|
6
|
+
|
|
7
|
+
domains:
|
|
8
|
+
- "daimler.sabacloud.com"
|
|
9
|
+
capabilities:
|
|
10
|
+
- navigate
|
|
11
|
+
- js_evaluate
|
|
12
|
+
|
|
13
|
+
columns:
|
|
14
|
+
- name: title
|
|
15
|
+
type: string
|
|
16
|
+
- name: type
|
|
17
|
+
type: string
|
|
18
|
+
- name: status
|
|
19
|
+
type: string
|
|
20
|
+
- name: dueDate
|
|
21
|
+
type: string
|
|
22
|
+
- name: daysUntilDue
|
|
23
|
+
type: number
|
|
24
|
+
- name: isOverdue
|
|
25
|
+
type: boolean
|
|
26
|
+
|
|
27
|
+
pipeline:
|
|
28
|
+
- step: navigate
|
|
29
|
+
url: "https://daimler.sabacloud.com/"
|
|
30
|
+
- step: wait
|
|
31
|
+
selector: "body"
|
|
32
|
+
timeout: 60000
|
|
33
|
+
- step: js_evaluate
|
|
34
|
+
file: saba-pending-training.eval.js
|
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
site: socket
|
|
2
|
-
name: security-news
|
|
3
|
-
version: "1.0"
|
|
4
|
-
description: "Latest security news from Socket.dev blog"
|
|
5
|
-
access: read
|
|
6
|
-
|
|
7
|
-
domains:
|
|
8
|
-
- "socket.dev"
|
|
9
|
-
capabilities:
|
|
10
|
-
- navigate
|
|
11
|
-
- cookie_read
|
|
12
|
-
|
|
13
|
-
args:
|
|
14
|
-
- name: tag
|
|
15
|
-
type: string
|
|
16
|
-
required: false
|
|
17
|
-
default: ""
|
|
18
|
-
help: "Filter by tag (e.g. 'Research', 'Security News')"
|
|
19
|
-
|
|
20
|
-
columns:
|
|
21
|
-
- name: title
|
|
22
|
-
type: string
|
|
23
|
-
- name: summary
|
|
24
|
-
type: string
|
|
25
|
-
- name: url
|
|
26
|
-
type: string
|
|
27
|
-
- name: date
|
|
28
|
-
type: string
|
|
29
|
-
- name: author
|
|
30
|
-
type: string
|
|
31
|
-
- name: tags
|
|
32
|
-
type: string
|
|
33
|
-
|
|
34
|
-
pipeline:
|
|
35
|
-
- step: navigate
|
|
36
|
-
url: "https://socket.dev/"
|
|
37
|
-
- step: wait
|
|
38
|
-
selector: "body"
|
|
39
|
-
timeout: 30000
|
|
40
|
-
- step: fetch
|
|
41
|
-
url: "https://socket.dev/api/blog/feed.json"
|
|
42
|
-
method: "GET"
|
|
43
|
-
headers:
|
|
44
|
-
Accept: "application/json"
|
|
45
|
-
as: "feed"
|
|
46
|
-
- step: transform
|
|
47
|
-
type: json_unwrap
|
|
48
|
-
input: feed
|
|
49
|
-
as: _data
|
|
50
|
-
options:
|
|
51
|
-
path: "items"
|
|
52
|
-
- step: map
|
|
53
|
-
fields:
|
|
54
|
-
title: "${{ row.title }}"
|
|
55
|
-
summary: "${{ row.summary }}"
|
|
56
|
-
url: "${{ row.url }}"
|
|
57
|
-
date: "${{ row.date_published }}"
|
|
58
|
-
author: "${{ row.author.name }}"
|
|
59
|
-
tags: "${{ row.tags }}"
|
|
60
|
-
- step: filter # empty tag = show all (contains '' is always true)
|
|
61
|
-
field: tags
|
|
62
|
-
operator: contains
|
|
63
|
-
value: "${{ args.tag | default('') }}"
|
|
1
|
+
site: socket
|
|
2
|
+
name: security-news
|
|
3
|
+
version: "1.0"
|
|
4
|
+
description: "Latest security news from Socket.dev blog"
|
|
5
|
+
access: read
|
|
6
|
+
|
|
7
|
+
domains:
|
|
8
|
+
- "socket.dev"
|
|
9
|
+
capabilities:
|
|
10
|
+
- navigate
|
|
11
|
+
- cookie_read
|
|
12
|
+
|
|
13
|
+
args:
|
|
14
|
+
- name: tag
|
|
15
|
+
type: string
|
|
16
|
+
required: false
|
|
17
|
+
default: ""
|
|
18
|
+
help: "Filter by tag (e.g. 'Research', 'Security News')"
|
|
19
|
+
|
|
20
|
+
columns:
|
|
21
|
+
- name: title
|
|
22
|
+
type: string
|
|
23
|
+
- name: summary
|
|
24
|
+
type: string
|
|
25
|
+
- name: url
|
|
26
|
+
type: string
|
|
27
|
+
- name: date
|
|
28
|
+
type: string
|
|
29
|
+
- name: author
|
|
30
|
+
type: string
|
|
31
|
+
- name: tags
|
|
32
|
+
type: string
|
|
33
|
+
|
|
34
|
+
pipeline:
|
|
35
|
+
- step: navigate
|
|
36
|
+
url: "https://socket.dev/"
|
|
37
|
+
- step: wait
|
|
38
|
+
selector: "body"
|
|
39
|
+
timeout: 30000
|
|
40
|
+
- step: fetch
|
|
41
|
+
url: "https://socket.dev/api/blog/feed.json"
|
|
42
|
+
method: "GET"
|
|
43
|
+
headers:
|
|
44
|
+
Accept: "application/json"
|
|
45
|
+
as: "feed"
|
|
46
|
+
- step: transform
|
|
47
|
+
type: json_unwrap
|
|
48
|
+
input: feed
|
|
49
|
+
as: _data
|
|
50
|
+
options:
|
|
51
|
+
path: "items"
|
|
52
|
+
- step: map
|
|
53
|
+
fields:
|
|
54
|
+
title: "${{ row.title }}"
|
|
55
|
+
summary: "${{ row.summary }}"
|
|
56
|
+
url: "${{ row.url }}"
|
|
57
|
+
date: "${{ row.date_published }}"
|
|
58
|
+
author: "${{ row.author.name }}"
|
|
59
|
+
tags: "${{ row.tags }}"
|
|
60
|
+
- step: filter # empty tag = show all (contains '' is always true)
|
|
61
|
+
field: tags
|
|
62
|
+
operator: contains
|
|
63
|
+
value: "${{ args.tag | default('') }}"
|