@commandgarden/cli 1.4.2 → 2.1.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 +67 -8
- package/node_modules/@commandgarden/app/dist/client/assets/index-GpK1-Tyq.js +255 -0
- package/node_modules/@commandgarden/app/dist/client/assets/index-JB_Cdl_F.css +1 -0
- package/node_modules/@commandgarden/app/dist/client/index.html +2 -2
- package/node_modules/@commandgarden/app/dist/server/journal/journal.config.d.ts +25 -0
- package/node_modules/@commandgarden/app/dist/server/journal/journal.config.js +31 -0
- package/node_modules/@commandgarden/app/dist/server/journal/journal.service.d.ts +37 -0
- package/node_modules/@commandgarden/app/dist/server/journal/journal.service.js +142 -0
- package/node_modules/@commandgarden/app/dist/server/journal/journal.types.d.ts +91 -0
- package/node_modules/@commandgarden/app/dist/server/journal/journal.types.js +4 -0
- package/node_modules/@commandgarden/app/dist/server/journal/sources/git.source.d.ts +21 -0
- package/node_modules/@commandgarden/app/dist/server/journal/sources/git.source.js +72 -0
- package/node_modules/@commandgarden/app/dist/server/journal/sources/jira.source.d.ts +15 -0
- package/node_modules/@commandgarden/app/dist/server/journal/sources/jira.source.js +42 -0
- package/node_modules/@commandgarden/app/dist/server/journal/sources/meetings.source.d.ts +21 -0
- package/node_modules/@commandgarden/app/dist/server/journal/sources/meetings.source.js +25 -0
- package/node_modules/@commandgarden/app/dist/server/journal/sources/timetracking.source.d.ts +17 -0
- package/node_modules/@commandgarden/app/dist/server/journal/sources/timetracking.source.js +83 -0
- package/node_modules/@commandgarden/app/dist/server/routes/connectors.js +2 -0
- package/node_modules/@commandgarden/app/dist/server/routes/index.js +6 -0
- package/node_modules/@commandgarden/app/dist/server/routes/journal.d.ts +9 -0
- package/node_modules/@commandgarden/app/dist/server/routes/journal.js +56 -0
- package/node_modules/@commandgarden/app/dist/server/routes/security-news-cache.d.ts +3 -0
- package/node_modules/@commandgarden/app/dist/server/routes/security-news-cache.js +17 -0
- package/node_modules/@commandgarden/app/dist/server/routes/skills.d.ts +2 -0
- package/node_modules/@commandgarden/app/dist/server/routes/skills.js +65 -0
- package/node_modules/@commandgarden/app/dist/server/store.d.ts +5 -0
- package/node_modules/@commandgarden/app/dist/server/store.js +19 -0
- package/node_modules/@commandgarden/chrome/dist/content-script.js +84 -2
- package/node_modules/@commandgarden/chrome/dist/content-script.js.map +2 -2
- package/node_modules/@commandgarden/chrome/dist/popup.html +74 -53
- package/node_modules/@commandgarden/chrome/dist/service-worker.js +73 -8
- package/node_modules/@commandgarden/chrome/dist/service-worker.js.map +3 -3
- package/node_modules/@commandgarden/daemon/connectors/ado-git-commits.eval.js +143 -0
- package/node_modules/@commandgarden/daemon/connectors/ado-git-commits.yaml +70 -0
- package/node_modules/@commandgarden/daemon/connectors/gcs-kb-content.yaml +58 -0
- package/node_modules/@commandgarden/daemon/connectors/gcs-kb-pages.yaml +47 -0
- package/node_modules/@commandgarden/daemon/connectors/jira-my-tickets.eval.js +115 -0
- package/node_modules/@commandgarden/daemon/connectors/jira-my-tickets.yaml +54 -0
- package/node_modules/@commandgarden/daemon/connectors/outlook-my-meetings.eval.js +186 -0
- package/node_modules/@commandgarden/daemon/connectors/outlook-my-meetings.yaml +41 -0
- package/node_modules/@commandgarden/daemon/connectors/socket-security-news.yaml +63 -0
- package/node_modules/@commandgarden/daemon/connectors/tldrsec-newsletter.eval.js +27 -0
- package/node_modules/@commandgarden/daemon/connectors/tldrsec-newsletter.yaml +38 -0
- package/node_modules/@commandgarden/daemon/connectors/tokenmaster-client-trustedby.yaml +62 -0
- package/node_modules/@commandgarden/daemon/connectors/tokenmaster-clients-list.yaml +55 -0
- package/node_modules/@commandgarden/daemon/connectors/wiz-blog-security.eval.js +29 -0
- package/node_modules/@commandgarden/daemon/connectors/wiz-blog-security.yaml +44 -0
- package/node_modules/@commandgarden/daemon/dist/daemon-runner.d.ts +12 -0
- package/node_modules/@commandgarden/daemon/dist/daemon-runner.d.ts.map +1 -0
- package/node_modules/@commandgarden/daemon/dist/daemon-runner.js +65 -0
- package/node_modules/@commandgarden/daemon/dist/daemon-runner.js.map +1 -0
- package/node_modules/@commandgarden/daemon/dist/server.d.ts.map +1 -1
- package/node_modules/@commandgarden/daemon/dist/server.js +87 -13
- package/node_modules/@commandgarden/daemon/dist/server.js.map +1 -1
- package/node_modules/@commandgarden/daemon/dist/transforms.d.ts +3 -0
- package/node_modules/@commandgarden/daemon/dist/transforms.d.ts.map +1 -0
- package/node_modules/@commandgarden/daemon/dist/transforms.js +36 -0
- package/node_modules/@commandgarden/daemon/dist/transforms.js.map +1 -0
- package/node_modules/@commandgarden/daemon/package.json +4 -2
- package/node_modules/@commandgarden/shared/dist/connector.d.ts +175 -0
- package/node_modules/@commandgarden/shared/dist/connector.d.ts.map +1 -1
- package/node_modules/@commandgarden/shared/dist/connector.js +2 -0
- package/node_modules/@commandgarden/shared/dist/connector.js.map +1 -1
- package/node_modules/@commandgarden/shared/dist/expression/evaluator.d.ts +2 -0
- package/node_modules/@commandgarden/shared/dist/expression/evaluator.d.ts.map +1 -1
- package/node_modules/@commandgarden/shared/dist/expression/evaluator.js.map +1 -1
- package/node_modules/@commandgarden/shared/dist/expression/filters.d.ts.map +1 -1
- package/node_modules/@commandgarden/shared/dist/expression/filters.js +5 -0
- package/node_modules/@commandgarden/shared/dist/expression/filters.js.map +1 -1
- package/node_modules/@commandgarden/shared/dist/fan-out.d.ts +28 -0
- package/node_modules/@commandgarden/shared/dist/fan-out.d.ts.map +1 -0
- package/node_modules/@commandgarden/shared/dist/fan-out.js +55 -0
- package/node_modules/@commandgarden/shared/dist/fan-out.js.map +1 -0
- package/node_modules/@commandgarden/shared/dist/index.d.ts +2 -1
- package/node_modules/@commandgarden/shared/dist/index.d.ts.map +1 -1
- package/node_modules/@commandgarden/shared/dist/index.js +3 -1
- package/node_modules/@commandgarden/shared/dist/index.js.map +1 -1
- package/node_modules/@commandgarden/shared/dist/loader.d.ts.map +1 -1
- package/node_modules/@commandgarden/shared/dist/loader.js +17 -0
- package/node_modules/@commandgarden/shared/dist/loader.js.map +1 -1
- package/node_modules/@commandgarden/shared/dist/pipeline.d.ts +223 -1
- package/node_modules/@commandgarden/shared/dist/pipeline.d.ts.map +1 -1
- package/node_modules/@commandgarden/shared/dist/pipeline.js +58 -2
- package/node_modules/@commandgarden/shared/dist/pipeline.js.map +1 -1
- package/node_modules/@commandgarden/shared/dist/protocol.d.ts +1 -0
- package/node_modules/@commandgarden/shared/dist/protocol.d.ts.map +1 -1
- package/node_modules/@commandgarden/shared/dist/protocol.js.map +1 -1
- package/package.json +1 -1
- package/node_modules/@commandgarden/app/dist/client/assets/index-B-_w54e9.css +0 -1
- package/node_modules/@commandgarden/app/dist/client/assets/index-Bb1BOK1E.js +0 -252
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
site: outlook
|
|
2
|
+
name: my-meetings
|
|
3
|
+
version: "1.0"
|
|
4
|
+
description: "User's own meeting schedule from Outlook Calendar for a given day"
|
|
5
|
+
access: read
|
|
6
|
+
|
|
7
|
+
domains:
|
|
8
|
+
- "outlook.cloud.microsoft.mcas.ms"
|
|
9
|
+
capabilities:
|
|
10
|
+
- navigate
|
|
11
|
+
- js_evaluate
|
|
12
|
+
|
|
13
|
+
args:
|
|
14
|
+
- name: date
|
|
15
|
+
type: string
|
|
16
|
+
required: true
|
|
17
|
+
help: "Day to fetch in YYYY-MM-DD format"
|
|
18
|
+
pattern: "^\\d{4}-\\d{2}-\\d{2}$"
|
|
19
|
+
|
|
20
|
+
columns:
|
|
21
|
+
- name: date
|
|
22
|
+
type: string
|
|
23
|
+
- name: subject
|
|
24
|
+
type: string
|
|
25
|
+
- name: start
|
|
26
|
+
type: string
|
|
27
|
+
- name: end
|
|
28
|
+
type: string
|
|
29
|
+
- name: durationMin
|
|
30
|
+
type: number
|
|
31
|
+
- name: state
|
|
32
|
+
type: string
|
|
33
|
+
|
|
34
|
+
pipeline:
|
|
35
|
+
- step: navigate
|
|
36
|
+
url: "https://outlook.cloud.microsoft.mcas.ms/calendar/deeplink/compose"
|
|
37
|
+
- step: wait
|
|
38
|
+
selector: "body"
|
|
39
|
+
timeout: 30000
|
|
40
|
+
- step: js_evaluate
|
|
41
|
+
file: outlook-my-meetings.eval.js
|
|
@@ -0,0 +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('') }}"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// tldrsec-newsletter.eval.js
|
|
2
|
+
// Extracts newsletter posts from Remix's __remixContext embedded JSON.
|
|
3
|
+
// Returns an array of flattened post objects for the pipeline.
|
|
4
|
+
// NOTE: No IIFE — evaluateInPage wraps this in AsyncFunction already.
|
|
5
|
+
|
|
6
|
+
const ctx = window.__remixContext;
|
|
7
|
+
if (!ctx) throw new Error('__remixContext not found');
|
|
8
|
+
|
|
9
|
+
const loaderData = ctx.state?.loaderData;
|
|
10
|
+
if (!loaderData) throw new Error('loaderData not found in __remixContext');
|
|
11
|
+
|
|
12
|
+
// The route key for the tag/category page
|
|
13
|
+
const categoryData = loaderData['routes/t/$category'];
|
|
14
|
+
if (!categoryData) throw new Error('routes/t/$category loader data not found');
|
|
15
|
+
|
|
16
|
+
const posts = categoryData.paginatedPosts?.posts;
|
|
17
|
+
if (!Array.isArray(posts)) throw new Error('paginatedPosts.posts is not an array');
|
|
18
|
+
|
|
19
|
+
return posts.map((p) => ({
|
|
20
|
+
title: p.web_title || p.meta_default_title || '',
|
|
21
|
+
slug: p.slug || '',
|
|
22
|
+
url: 'https://tldrsec.com/p/' + (p.slug || ''),
|
|
23
|
+
publishedAt: p.override_scheduled_at || p.created_at || '',
|
|
24
|
+
excerpt: p.web_subtitle || p.meta_default_description || '',
|
|
25
|
+
authors: (p.authors || []).map((a) => a.name).join(', '),
|
|
26
|
+
tags: (p.content_tags || []).map((t) => t.display).join(', '),
|
|
27
|
+
}));
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
site: tldrsec
|
|
2
|
+
name: newsletter
|
|
3
|
+
version: "1.0"
|
|
4
|
+
description: "Latest newsletter issues from tl;dr sec — titles, summaries, authors, tags, and publish dates"
|
|
5
|
+
access: read
|
|
6
|
+
|
|
7
|
+
domains:
|
|
8
|
+
- "tldrsec.com"
|
|
9
|
+
capabilities:
|
|
10
|
+
- navigate
|
|
11
|
+
- js_evaluate
|
|
12
|
+
|
|
13
|
+
args: []
|
|
14
|
+
|
|
15
|
+
columns:
|
|
16
|
+
- name: title
|
|
17
|
+
type: string
|
|
18
|
+
- name: slug
|
|
19
|
+
type: string
|
|
20
|
+
- name: url
|
|
21
|
+
type: string
|
|
22
|
+
- name: publishedAt
|
|
23
|
+
type: string
|
|
24
|
+
- name: excerpt
|
|
25
|
+
type: string
|
|
26
|
+
- name: authors
|
|
27
|
+
type: string
|
|
28
|
+
- name: tags
|
|
29
|
+
type: string
|
|
30
|
+
|
|
31
|
+
pipeline:
|
|
32
|
+
- step: navigate
|
|
33
|
+
url: "https://tldrsec.com/t/Newsletter"
|
|
34
|
+
- step: wait
|
|
35
|
+
selector: "body"
|
|
36
|
+
timeout: 15000
|
|
37
|
+
- step: js_evaluate
|
|
38
|
+
file: tldrsec-newsletter.eval.js
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
site: tokenmaster
|
|
2
|
+
name: client-trustedby
|
|
3
|
+
version: "2.0"
|
|
4
|
+
description: "List the clients that trust a given client in TokenMaster"
|
|
5
|
+
access: read
|
|
6
|
+
|
|
7
|
+
vars:
|
|
8
|
+
domains:
|
|
9
|
+
emea: tma.query.api.dvb.corpinter.net
|
|
10
|
+
amap: tma.query.api.am.dvb.corpinter.net
|
|
11
|
+
cn: tma.query.api.dvb.corpinter.net.cn
|
|
12
|
+
|
|
13
|
+
args:
|
|
14
|
+
- name: clientid
|
|
15
|
+
type: string
|
|
16
|
+
required: true
|
|
17
|
+
help: "The client ID to look up trustedby relationships for"
|
|
18
|
+
- name: region
|
|
19
|
+
type: string
|
|
20
|
+
required: false
|
|
21
|
+
default: "emea"
|
|
22
|
+
enum: [emea, amap, cn]
|
|
23
|
+
help: "Region to query (emea, amap, cn, or 'all' for fan-out). Default: emea"
|
|
24
|
+
|
|
25
|
+
domains:
|
|
26
|
+
- "tma.query.api.dvb.corpinter.net"
|
|
27
|
+
- "tma.query.api.am.dvb.corpinter.net"
|
|
28
|
+
- "tma.query.api.dvb.corpinter.net.cn"
|
|
29
|
+
capabilities:
|
|
30
|
+
- navigate
|
|
31
|
+
- cookie_read
|
|
32
|
+
|
|
33
|
+
columns:
|
|
34
|
+
- name: id
|
|
35
|
+
type: string
|
|
36
|
+
- name: name
|
|
37
|
+
type: string
|
|
38
|
+
- name: is_onboard_client
|
|
39
|
+
type: boolean
|
|
40
|
+
- name: idDisplay
|
|
41
|
+
type: string
|
|
42
|
+
- name: expiry_date
|
|
43
|
+
type: string
|
|
44
|
+
|
|
45
|
+
pipeline:
|
|
46
|
+
- step: navigate
|
|
47
|
+
url: "https://${{ args.region | lookup(vars.domains) }}/v2/clients/${{ args.clientid }}/trustedby"
|
|
48
|
+
- step: wait
|
|
49
|
+
selector: "body"
|
|
50
|
+
timeout: 30000
|
|
51
|
+
- step: fetch
|
|
52
|
+
url: "https://${{ args.region | lookup(vars.domains) }}/v2/clients/${{ args.clientid }}/trustedby"
|
|
53
|
+
method: "GET"
|
|
54
|
+
headers:
|
|
55
|
+
Accept: "application/json"
|
|
56
|
+
- step: map
|
|
57
|
+
fields:
|
|
58
|
+
id: "${{ row.id }}"
|
|
59
|
+
name: "${{ row.name }}"
|
|
60
|
+
is_onboard_client: "${{ row.is_onboard_client }}"
|
|
61
|
+
idDisplay: "${{ row.idDisplay }}"
|
|
62
|
+
expiry_date: "${{ row.expiry_date }}"
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
site: tokenmaster
|
|
2
|
+
name: clients-list
|
|
3
|
+
version: "2.0"
|
|
4
|
+
description: "List the clients managed by the current user in TokenMaster"
|
|
5
|
+
access: read
|
|
6
|
+
|
|
7
|
+
vars:
|
|
8
|
+
domains:
|
|
9
|
+
emea: tma.query.api.dvb.corpinter.net
|
|
10
|
+
amap: tma.query.api.am.dvb.corpinter.net
|
|
11
|
+
cn: tma.query.api.dvb.corpinter.net.cn
|
|
12
|
+
|
|
13
|
+
args:
|
|
14
|
+
- name: region
|
|
15
|
+
type: string
|
|
16
|
+
required: false
|
|
17
|
+
default: "all"
|
|
18
|
+
enum: [emea, amap, cn]
|
|
19
|
+
help: "Region to query (emea, amap, cn, or 'all' for fan-out). Default: all"
|
|
20
|
+
|
|
21
|
+
domains:
|
|
22
|
+
- "tma.query.api.dvb.corpinter.net"
|
|
23
|
+
- "tma.query.api.am.dvb.corpinter.net"
|
|
24
|
+
- "tma.query.api.dvb.corpinter.net.cn"
|
|
25
|
+
capabilities:
|
|
26
|
+
- navigate
|
|
27
|
+
- cookie_read
|
|
28
|
+
|
|
29
|
+
columns:
|
|
30
|
+
- name: id
|
|
31
|
+
type: string
|
|
32
|
+
- name: name
|
|
33
|
+
type: string
|
|
34
|
+
- name: status
|
|
35
|
+
type: string
|
|
36
|
+
- name: admins
|
|
37
|
+
type: string
|
|
38
|
+
|
|
39
|
+
pipeline:
|
|
40
|
+
- step: navigate
|
|
41
|
+
url: "https://${{ args.region | lookup(vars.domains) }}/v1/clients"
|
|
42
|
+
- step: wait
|
|
43
|
+
selector: "body"
|
|
44
|
+
timeout: 30000
|
|
45
|
+
- step: fetch
|
|
46
|
+
url: "https://${{ args.region | lookup(vars.domains) }}/v1/clients"
|
|
47
|
+
method: "GET"
|
|
48
|
+
headers:
|
|
49
|
+
Accept: "application/json"
|
|
50
|
+
- step: map
|
|
51
|
+
fields:
|
|
52
|
+
id: "${{ row.id }}"
|
|
53
|
+
name: "${{ row.name }}"
|
|
54
|
+
status: "${{ row.status }}"
|
|
55
|
+
admins: "${{ row.admins }}"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// wiz-blog-security.eval.js
|
|
2
|
+
// Extracts blog posts from Next.js __NEXT_DATA__ embedded JSON.
|
|
3
|
+
// Returns an array of flattened post objects for the pipeline.
|
|
4
|
+
// NOTE: No IIFE — evaluateInPage wraps this in AsyncFunction already.
|
|
5
|
+
|
|
6
|
+
const script = document.getElementById('__NEXT_DATA__');
|
|
7
|
+
if (!script) throw new Error('__NEXT_DATA__ script tag not found');
|
|
8
|
+
|
|
9
|
+
const raw = script.textContent;
|
|
10
|
+
if (!raw) throw new Error('__NEXT_DATA__ script tag is empty');
|
|
11
|
+
|
|
12
|
+
const nextData = JSON.parse(raw);
|
|
13
|
+
const pageProps = nextData?.props?.pageProps;
|
|
14
|
+
if (!pageProps) throw new Error('pageProps not found in __NEXT_DATA__');
|
|
15
|
+
|
|
16
|
+
const posts = pageProps.initialPosts;
|
|
17
|
+
if (!Array.isArray(posts)) throw new Error('initialPosts is not an array');
|
|
18
|
+
|
|
19
|
+
return posts
|
|
20
|
+
.filter((p) => !p.hiddenFromArchive)
|
|
21
|
+
.map((p) => ({
|
|
22
|
+
title: p.title || '',
|
|
23
|
+
slug: p.slug || '',
|
|
24
|
+
url: 'https://www.wiz.io/blog/' + (p.slug || ''),
|
|
25
|
+
publishedAt: p._firstPublishedAt || '',
|
|
26
|
+
excerpt: p.excerpt || '',
|
|
27
|
+
authors: (p.authors || []).map((a) => a.fullName).join(', '),
|
|
28
|
+
tags: (p.tags || []).map((t) => t.name).join(', '),
|
|
29
|
+
}));
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
site: wiz
|
|
2
|
+
name: blog-security
|
|
3
|
+
version: "1.0"
|
|
4
|
+
description: "Latest security blog posts from Wiz — titles, excerpts, authors, tags, and publish dates"
|
|
5
|
+
access: read
|
|
6
|
+
|
|
7
|
+
domains:
|
|
8
|
+
- "www.wiz.io"
|
|
9
|
+
capabilities:
|
|
10
|
+
- navigate
|
|
11
|
+
- js_evaluate
|
|
12
|
+
|
|
13
|
+
args:
|
|
14
|
+
- name: tag
|
|
15
|
+
type: string
|
|
16
|
+
required: false
|
|
17
|
+
default: "security"
|
|
18
|
+
pattern: "^[a-z0-9-]+$"
|
|
19
|
+
help: "Blog tag slug to filter by (e.g. 'security', 'research', 'ai'). Default: security"
|
|
20
|
+
|
|
21
|
+
columns:
|
|
22
|
+
- name: title
|
|
23
|
+
type: string
|
|
24
|
+
- name: slug
|
|
25
|
+
type: string
|
|
26
|
+
- name: url
|
|
27
|
+
type: string
|
|
28
|
+
- name: publishedAt
|
|
29
|
+
type: string
|
|
30
|
+
- name: excerpt
|
|
31
|
+
type: string
|
|
32
|
+
- name: authors
|
|
33
|
+
type: string
|
|
34
|
+
- name: tags
|
|
35
|
+
type: string
|
|
36
|
+
|
|
37
|
+
pipeline:
|
|
38
|
+
- step: navigate
|
|
39
|
+
url: "https://www.wiz.io/blog/tag/${{ args.tag }}"
|
|
40
|
+
- step: wait
|
|
41
|
+
selector: "#__NEXT_DATA__"
|
|
42
|
+
timeout: 15000
|
|
43
|
+
- step: js_evaluate
|
|
44
|
+
file: wiz-blog-security.eval.js
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PipelineStep } from '@commandgarden/shared';
|
|
2
|
+
export interface DaemonRunnerInput {
|
|
3
|
+
steps: PipelineStep[];
|
|
4
|
+
data: Record<string, unknown>[];
|
|
5
|
+
vars: Record<string, unknown>;
|
|
6
|
+
args?: Record<string, string | number | boolean>;
|
|
7
|
+
}
|
|
8
|
+
export declare function runDaemonSteps(input: DaemonRunnerInput): {
|
|
9
|
+
data: Record<string, unknown>[];
|
|
10
|
+
vars: Record<string, unknown>;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=daemon-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon-runner.d.ts","sourceRoot":"","sources":["../src/daemon-runner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAsC,MAAM,uBAAuB,CAAC;AAI9F,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CAClD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,iBAAiB,GAAG;IACxD,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B,CAwDA"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { interpolate } from '@commandgarden/shared';
|
|
2
|
+
import { getTransform } from './transforms.js';
|
|
3
|
+
export function runDaemonSteps(input) {
|
|
4
|
+
const vars = { ...input.vars };
|
|
5
|
+
const args = input.args ?? {};
|
|
6
|
+
let data = input.data;
|
|
7
|
+
for (const step of input.steps) {
|
|
8
|
+
switch (step.step) {
|
|
9
|
+
case 'transform': {
|
|
10
|
+
const ts = step;
|
|
11
|
+
const inputVal = vars[ts.input] ?? '';
|
|
12
|
+
const fn = getTransform(ts.type);
|
|
13
|
+
const result = fn(inputVal, ts.options);
|
|
14
|
+
if (Array.isArray(result)) {
|
|
15
|
+
data = result;
|
|
16
|
+
}
|
|
17
|
+
else if (typeof result === 'object') {
|
|
18
|
+
Object.assign(vars, result);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
vars[ts.as] = result;
|
|
22
|
+
}
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
// map and filter are dual-mode — they run here when they appear after a transform
|
|
26
|
+
case 'map': {
|
|
27
|
+
const ms = step;
|
|
28
|
+
data = data.map(row => {
|
|
29
|
+
const mapped = {};
|
|
30
|
+
const ctx = { args, vars: { ...vars, row }, cookies: {}, row };
|
|
31
|
+
for (const [key, expr] of Object.entries(ms.fields)) {
|
|
32
|
+
mapped[key] = interpolate(expr, ctx);
|
|
33
|
+
}
|
|
34
|
+
return mapped;
|
|
35
|
+
});
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
case 'filter': {
|
|
39
|
+
const fs = step;
|
|
40
|
+
const filterCtx = { args, vars, cookies: {} };
|
|
41
|
+
const filterValue = interpolate(fs.value, filterCtx);
|
|
42
|
+
data = data.filter(row => {
|
|
43
|
+
const actual = row[fs.field];
|
|
44
|
+
switch (fs.operator) {
|
|
45
|
+
case 'eq': return actual === filterValue;
|
|
46
|
+
case 'ne': return actual !== filterValue;
|
|
47
|
+
case 'contains': return String(actual).includes(String(filterValue));
|
|
48
|
+
case 'matches': try {
|
|
49
|
+
return new RegExp(String(filterValue)).test(String(actual));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
default: return true;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
default:
|
|
60
|
+
throw new Error(`DaemonRunner does not support step type: ${step.step}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { data, vars };
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=daemon-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon-runner.js","sourceRoot":"","sources":["../src/daemon-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,SAAS,CAAC;AAM9B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,SAAS,CAAC;AAM9B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAKxC,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,UAAU,sSA6YlD"}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
import Fastify from 'fastify';
|
|
3
3
|
import websocket from '@fastify/websocket';
|
|
4
4
|
import { randomUUID, createHash } from 'node:crypto';
|
|
5
|
-
import { isRunCommandRequest, createAuditEvent, STEP_CAPABILITY_MAP } from '@commandgarden/shared';
|
|
5
|
+
import { isRunCommandRequest, createAuditEvent, STEP_CAPABILITY_MAP, expandFanOut, validateEnumArgs, splitPipeline } from '@commandgarden/shared';
|
|
6
6
|
import { userInfo } from 'node:os';
|
|
7
|
+
import { runDaemonSteps } from './daemon-runner.js';
|
|
7
8
|
import { validateCommand } from './validator.js';
|
|
8
9
|
import { validateToken } from './auth.js';
|
|
9
10
|
import { SseManager } from './sse-manager.js';
|
|
@@ -182,11 +183,16 @@ export async function createServer(deps) {
|
|
|
182
183
|
reply.code(404).send({ ok: false, error: validation.denialReason });
|
|
183
184
|
return;
|
|
184
185
|
}
|
|
186
|
+
const connector = validation.connector;
|
|
187
|
+
const enumError = validateEnumArgs(body.args, connector.args ?? []);
|
|
188
|
+
if (enumError) {
|
|
189
|
+
reply.code(400).send({ ok: false, error: enumError });
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
185
192
|
if (!deps.wsRelay.connected) {
|
|
186
193
|
reply.code(503).send({ ok: false, error: 'Extension not connected' });
|
|
187
194
|
return;
|
|
188
195
|
}
|
|
189
|
-
const connector = validation.connector;
|
|
190
196
|
const requiresApproval = connectorNeedsApproval(body.connector, connector);
|
|
191
197
|
const approvalConfig = requiresApproval ? buildApprovalConfig() : undefined;
|
|
192
198
|
const correlationId = randomUUID();
|
|
@@ -195,10 +201,20 @@ export async function createServer(deps) {
|
|
|
195
201
|
? createHash('sha256').update(meta.yamlContent).digest('hex').slice(0, 16)
|
|
196
202
|
: undefined;
|
|
197
203
|
const startTime = Date.now();
|
|
204
|
+
// Fan-out: expand "all" on enum args into sequential runs
|
|
205
|
+
const fanOut = expandFanOut(body.args, connector.args ?? []);
|
|
206
|
+
if (fanOut.isFanOut && requiresApproval) {
|
|
207
|
+
reply.code(400).send({
|
|
208
|
+
ok: false,
|
|
209
|
+
error: 'Fan-out (--region all) is not supported with approval-required connectors. Specify a single region.',
|
|
210
|
+
});
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const resolvedArgs = fanOut.argSets[0];
|
|
198
214
|
try {
|
|
199
215
|
deps.auditStore.insert(createAuditEvent({
|
|
200
216
|
type: 'command.start', connector: body.connector, user,
|
|
201
|
-
args: body.args,
|
|
217
|
+
args: fanOut.isFanOut ? body.args : resolvedArgs,
|
|
202
218
|
domains: connector.domains, capabilities: [...connector.capabilities],
|
|
203
219
|
correlationId, connectorHash,
|
|
204
220
|
}));
|
|
@@ -207,24 +223,48 @@ export async function createServer(deps) {
|
|
|
207
223
|
reply.code(500).send({ ok: false, error: 'Audit system unavailable \u2014 command blocked' });
|
|
208
224
|
return;
|
|
209
225
|
}
|
|
210
|
-
const runPipeline = async (requestId) => {
|
|
226
|
+
const runPipeline = async (requestId, runArgs) => {
|
|
211
227
|
try {
|
|
212
|
-
|
|
228
|
+
// Split pipeline: extension steps run in browser, daemon steps run here
|
|
229
|
+
const { extensionSteps, daemonSteps } = splitPipeline(connector.pipeline);
|
|
230
|
+
const extConnector = { ...connector, pipeline: extensionSteps };
|
|
231
|
+
const resp = await deps.wsRelay.send(extConnector, runArgs, approvalConfig, undefined, requestId || undefined);
|
|
232
|
+
// Run daemon-side steps (e.g. transform) if extension succeeded
|
|
233
|
+
let finalData = resp.data;
|
|
234
|
+
let finalVars = resp.vars ?? {};
|
|
235
|
+
if (resp.ok && daemonSteps.length > 0) {
|
|
236
|
+
// Also include data row fields as vars for daemon transforms
|
|
237
|
+
const dataVars = finalData.length > 0 ? finalData[0] : {};
|
|
238
|
+
const mergedVars = { ...dataVars, ...finalVars };
|
|
239
|
+
const daemonResult = runDaemonSteps({ steps: daemonSteps, data: finalData, vars: mergedVars, args: runArgs });
|
|
240
|
+
finalData = daemonResult.data;
|
|
241
|
+
finalVars = daemonResult.vars;
|
|
242
|
+
// Merge daemon vars back into data rows
|
|
243
|
+
if (finalData.length > 0) {
|
|
244
|
+
for (const row of finalData) {
|
|
245
|
+
for (const [k, v] of Object.entries(finalVars)) {
|
|
246
|
+
if (connector.columns?.some(c => c.name === k) && !(k in row)) {
|
|
247
|
+
row[k] = v;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
213
253
|
const durationMs = Date.now() - startTime;
|
|
214
254
|
deps.auditStore.insert(createAuditEvent({
|
|
215
255
|
type: resp.ok ? 'command.success' : 'command.error',
|
|
216
256
|
connector: body.connector, user,
|
|
217
|
-
args:
|
|
257
|
+
args: runArgs,
|
|
218
258
|
domains: connector.domains, capabilities: [...connector.capabilities],
|
|
219
|
-
rowCount:
|
|
259
|
+
rowCount: finalData.length,
|
|
220
260
|
columns: connector.columns?.map(c => c.name),
|
|
221
261
|
durationMs, error: resp.error,
|
|
222
262
|
correlationId, connectorHash, steps: resp.steps,
|
|
223
263
|
}));
|
|
224
264
|
return {
|
|
225
|
-
ok: resp.ok, connector: body.connector, rowCount:
|
|
265
|
+
ok: resp.ok, connector: body.connector, rowCount: finalData.length,
|
|
226
266
|
columns: connector.columns?.map(c => c.name) ?? [],
|
|
227
|
-
data:
|
|
267
|
+
data: finalData, error: resp.error, durationMs,
|
|
228
268
|
requestId, requiresApproval,
|
|
229
269
|
};
|
|
230
270
|
}
|
|
@@ -233,16 +273,50 @@ export async function createServer(deps) {
|
|
|
233
273
|
const error = err instanceof Error ? err.message : 'Unknown error';
|
|
234
274
|
deps.auditStore.insert(createAuditEvent({
|
|
235
275
|
type: 'command.error', connector: body.connector, user,
|
|
236
|
-
args:
|
|
276
|
+
args: runArgs, durationMs, error,
|
|
237
277
|
correlationId, connectorHash,
|
|
238
278
|
}));
|
|
239
279
|
return { ok: false, data: [], error, durationMs, requestId, requiresApproval };
|
|
240
280
|
}
|
|
241
281
|
};
|
|
282
|
+
if (fanOut.isFanOut) {
|
|
283
|
+
const { fanOutArgName } = fanOut;
|
|
284
|
+
const allData = [];
|
|
285
|
+
const errors = [];
|
|
286
|
+
for (const argSet of fanOut.argSets) {
|
|
287
|
+
const result = await runPipeline('', argSet);
|
|
288
|
+
if (result.ok) {
|
|
289
|
+
for (const row of result.data) {
|
|
290
|
+
row[fanOutArgName] = argSet[fanOutArgName];
|
|
291
|
+
allData.push(row);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
else if (result.error) {
|
|
295
|
+
errors.push(`${argSet[fanOutArgName]}: ${result.error}`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const columns = connector.columns?.map(c => c.name) ?? [];
|
|
299
|
+
if (!columns.includes(fanOutArgName)) {
|
|
300
|
+
columns.unshift(fanOutArgName);
|
|
301
|
+
}
|
|
302
|
+
const durationMs = Date.now() - startTime;
|
|
303
|
+
const hasErrors = errors.length > 0;
|
|
304
|
+
return {
|
|
305
|
+
ok: !hasErrors || allData.length > 0,
|
|
306
|
+
connector: body.connector,
|
|
307
|
+
rowCount: allData.length,
|
|
308
|
+
columns,
|
|
309
|
+
data: allData,
|
|
310
|
+
error: hasErrors ? errors.join('; ') : undefined,
|
|
311
|
+
durationMs,
|
|
312
|
+
requestId: '',
|
|
313
|
+
requiresApproval: false,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
242
316
|
if (requiresApproval) {
|
|
243
317
|
const requestId = randomUUID();
|
|
244
318
|
sse.waitForConnection(requestId, SSE_CONNECT_TIMEOUT_MS)
|
|
245
|
-
.then(() => runPipeline(requestId))
|
|
319
|
+
.then(() => runPipeline(requestId, resolvedArgs))
|
|
246
320
|
.then(result => {
|
|
247
321
|
sse.send(requestId, 'result', result);
|
|
248
322
|
sse.remove(requestId);
|
|
@@ -251,7 +325,7 @@ export async function createServer(deps) {
|
|
|
251
325
|
const durationMs = Date.now() - startTime;
|
|
252
326
|
deps.auditStore.insert(createAuditEvent({
|
|
253
327
|
type: 'command.error', connector: body.connector, user,
|
|
254
|
-
args:
|
|
328
|
+
args: resolvedArgs, durationMs,
|
|
255
329
|
error: err instanceof Error ? err.message : 'SSE timeout',
|
|
256
330
|
}));
|
|
257
331
|
});
|
|
@@ -261,7 +335,7 @@ export async function createServer(deps) {
|
|
|
261
335
|
});
|
|
262
336
|
}
|
|
263
337
|
else {
|
|
264
|
-
const result = await runPipeline('');
|
|
338
|
+
const result = await runPipeline('', resolvedArgs);
|
|
265
339
|
if (!result.ok) {
|
|
266
340
|
reply.code(500).send(result);
|
|
267
341
|
return;
|