@wolpertingerlabs/drawlatch 1.0.0-alpha.2 → 1.0.0-alpha.4
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/README.md +5 -3
- package/bin/drawlatch.js +43 -2
- package/dist/connections/anthropic.json +17 -1
- package/dist/connections/bluesky.json +39 -0
- package/dist/connections/devin.json +5 -1
- package/dist/connections/discord-bot.json +88 -0
- package/dist/connections/discord-oauth.json +5 -1
- package/dist/connections/github.json +57 -0
- package/dist/connections/google-ai.json +5 -1
- package/dist/connections/google.json +5 -1
- package/dist/connections/hex.json +5 -1
- package/dist/connections/lichess.json +5 -1
- package/dist/connections/linear.json +44 -0
- package/dist/connections/mastodon.json +38 -0
- package/dist/connections/notion.json +40 -0
- package/dist/connections/openai.json +5 -1
- package/dist/connections/openrouter.json +5 -1
- package/dist/connections/reddit.json +50 -0
- package/dist/connections/slack.json +50 -0
- package/dist/connections/stripe.json +47 -0
- package/dist/connections/telegram.json +38 -0
- package/dist/connections/trello.json +87 -1
- package/dist/connections/twitch.json +38 -0
- package/dist/connections/x.json +38 -0
- package/dist/mcp/server.js +279 -1
- package/dist/remote/ingestors/base-ingestor.d.ts +14 -3
- package/dist/remote/ingestors/base-ingestor.js +10 -2
- package/dist/remote/ingestors/discord/discord-gateway.d.ts +2 -2
- package/dist/remote/ingestors/discord/discord-gateway.js +5 -5
- package/dist/remote/ingestors/manager.d.ts +75 -9
- package/dist/remote/ingestors/manager.js +309 -40
- package/dist/remote/ingestors/poll/poll-ingestor.d.ts +2 -2
- package/dist/remote/ingestors/poll/poll-ingestor.js +5 -5
- package/dist/remote/ingestors/registry.d.ts +2 -2
- package/dist/remote/ingestors/registry.js +2 -2
- package/dist/remote/ingestors/slack/socket-mode.d.ts +2 -2
- package/dist/remote/ingestors/slack/socket-mode.js +5 -5
- package/dist/remote/ingestors/types.d.ts +17 -0
- package/dist/remote/ingestors/webhook/base-webhook-ingestor.d.ts +46 -7
- package/dist/remote/ingestors/webhook/base-webhook-ingestor.js +115 -10
- package/dist/remote/ingestors/webhook/github-webhook-ingestor.d.ts +14 -0
- package/dist/remote/ingestors/webhook/github-webhook-ingestor.js +27 -2
- package/dist/remote/ingestors/webhook/lifecycle-types.d.ts +63 -0
- package/dist/remote/ingestors/webhook/lifecycle-types.js +12 -0
- package/dist/remote/ingestors/webhook/stripe-webhook-ingestor.js +2 -2
- package/dist/remote/ingestors/webhook/trello-webhook-ingestor.d.ts +17 -5
- package/dist/remote/ingestors/webhook/trello-webhook-ingestor.js +32 -26
- package/dist/remote/ingestors/webhook/webhook-lifecycle-manager.d.ts +46 -0
- package/dist/remote/ingestors/webhook/webhook-lifecycle-manager.js +261 -0
- package/dist/remote/server.js +629 -16
- package/dist/remote/tunnel.d.ts +40 -0
- package/dist/remote/tunnel.js +116 -0
- package/dist/shared/config.d.ts +45 -0
- package/dist/shared/config.js +8 -3
- package/dist/shared/connections.d.ts +9 -0
- package/dist/shared/connections.js +4 -0
- package/dist/shared/listener-config.d.ts +157 -0
- package/dist/shared/listener-config.js +10 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Drawlatch
|
|
2
|
+
|
|
3
|
+
> **Alpha Software:** This project is in alpha. Expect breaking changes between updates.
|
|
2
4
|
|
|
3
5
|
A config-driven MCP (Model Context Protocol) proxy that lets Claude Code make authenticated HTTP requests to external APIs. Supports 22 pre-built API connections with endpoint allowlisting, per-caller access control, and real-time event ingestion — all configured through a single JSON file.
|
|
4
6
|
|
|
@@ -29,7 +31,7 @@ The crypto layer uses **Ed25519** signatures for authentication and **X25519 ECD
|
|
|
29
31
|
|
|
30
32
|
### Local Mode (In-Process Library)
|
|
31
33
|
|
|
32
|
-
In local mode, there is no separate server, no network port, and no encryption. Your application imports
|
|
34
|
+
In local mode, there is no separate server, no network port, and no encryption. Your application imports Drawlatch's core functions directly and calls them in-process:
|
|
33
35
|
|
|
34
36
|
```
|
|
35
37
|
┌──────────────────────────────────────────┐ Authenticated ┌──────────────┐
|
|
@@ -678,7 +680,7 @@ These additional protections apply when running the two-component remote archite
|
|
|
678
680
|
|
|
679
681
|
### Local Mode Caveat
|
|
680
682
|
|
|
681
|
-
When using
|
|
683
|
+
When using Drawlatch as an in-process library (local mode), secrets are resolved from `process.env` on the same machine as the agent. The encryption and mutual authentication layers are not used. The security value in local mode comes from **structured access control** (endpoint allowlisting, per-caller route isolation) rather than cryptographic secret isolation.
|
|
682
684
|
|
|
683
685
|
## License
|
|
684
686
|
|
package/bin/drawlatch.js
CHANGED
|
@@ -57,10 +57,11 @@ try {
|
|
|
57
57
|
help: { type: "boolean", short: "h", default: false },
|
|
58
58
|
version: { type: "boolean", short: "v", default: false },
|
|
59
59
|
foreground: { type: "boolean", short: "f", default: false },
|
|
60
|
+
tunnel: { type: "boolean", short: "t", default: false },
|
|
60
61
|
port: { type: "string" },
|
|
61
62
|
host: { type: "string" },
|
|
62
63
|
lines: { type: "string", short: "n", default: "50" },
|
|
63
|
-
follow: { type: "boolean", default:
|
|
64
|
+
follow: { type: "boolean", default: false },
|
|
64
65
|
path: { type: "boolean", default: false },
|
|
65
66
|
},
|
|
66
67
|
strict: false,
|
|
@@ -182,6 +183,7 @@ async function cmdStart() {
|
|
|
182
183
|
NODE_ENV: "production",
|
|
183
184
|
...(values.port ? { DRAWLATCH_PORT: String(port) } : {}),
|
|
184
185
|
...(values.host ? { DRAWLATCH_HOST: host } : {}),
|
|
186
|
+
...(values.tunnel ? { DRAWLATCH_TUNNEL: "1" } : {}),
|
|
185
187
|
},
|
|
186
188
|
cwd: PKG_ROOT,
|
|
187
189
|
});
|
|
@@ -195,6 +197,18 @@ async function cmdStart() {
|
|
|
195
197
|
if (healthy) {
|
|
196
198
|
console.log(`\nRemote server is running (PID ${child.pid}).`);
|
|
197
199
|
console.log(` Listening: ${host}:${port}`);
|
|
200
|
+
if (values.tunnel) {
|
|
201
|
+
// The tunnel starts asynchronously after the server is healthy —
|
|
202
|
+
// poll the health endpoint until the tunnel URL appears (up to 20s).
|
|
203
|
+
console.log(` Tunnel: waiting for cloudflared...`);
|
|
204
|
+
const tunnelUrl = await waitForTunnelUrl(host, port, 20000);
|
|
205
|
+
if (tunnelUrl) {
|
|
206
|
+
console.log(` Tunnel: ${tunnelUrl}`);
|
|
207
|
+
console.log(` Webhooks: ${tunnelUrl}/webhooks/<path>`);
|
|
208
|
+
} else {
|
|
209
|
+
console.log(` Tunnel: not available (check logs: drawlatch logs)`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
198
212
|
console.log(` Logs: drawlatch logs`);
|
|
199
213
|
} else {
|
|
200
214
|
console.log(
|
|
@@ -209,6 +223,7 @@ async function cmdStartForeground() {
|
|
|
209
223
|
process.env.NODE_ENV = process.env.NODE_ENV || "production";
|
|
210
224
|
if (values.port) process.env.DRAWLATCH_PORT = values.port;
|
|
211
225
|
if (values.host) process.env.DRAWLATCH_HOST = values.host;
|
|
226
|
+
if (values.tunnel) process.env.DRAWLATCH_TUNNEL = "1";
|
|
212
227
|
|
|
213
228
|
ensureConfigDir();
|
|
214
229
|
|
|
@@ -250,6 +265,17 @@ async function cmdStop() {
|
|
|
250
265
|
async function cmdRestart() {
|
|
251
266
|
const pid = readPid();
|
|
252
267
|
if (pid) {
|
|
268
|
+
// If the previous server had an active tunnel, carry the flag forward
|
|
269
|
+
// so the restarted server also starts a tunnel (unless --tunnel is
|
|
270
|
+
// already set or the user explicitly omitted it).
|
|
271
|
+
if (!values.tunnel) {
|
|
272
|
+
const config = loadRemoteConfig();
|
|
273
|
+
const prevHealth = await healthCheckFull(config.host, config.port);
|
|
274
|
+
if (prevHealth?.tunnelUrl) {
|
|
275
|
+
console.log("Previous server had an active tunnel — re-enabling --tunnel.");
|
|
276
|
+
values.tunnel = true;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
253
279
|
await cmdStop();
|
|
254
280
|
}
|
|
255
281
|
await cmdStart();
|
|
@@ -285,6 +311,9 @@ async function cmdStatus() {
|
|
|
285
311
|
);
|
|
286
312
|
if (healthData) {
|
|
287
313
|
console.log(` Active sessions: ${healthData.activeSessions}`);
|
|
314
|
+
if (healthData.tunnelUrl) {
|
|
315
|
+
console.log(` Tunnel: ${healthData.tunnelUrl}`);
|
|
316
|
+
}
|
|
288
317
|
}
|
|
289
318
|
}
|
|
290
319
|
|
|
@@ -449,6 +478,16 @@ async function healthCheckFull(host, port) {
|
|
|
449
478
|
}
|
|
450
479
|
}
|
|
451
480
|
|
|
481
|
+
async function waitForTunnelUrl(host, port, timeoutMs) {
|
|
482
|
+
const start = Date.now();
|
|
483
|
+
while (Date.now() - start < timeoutMs) {
|
|
484
|
+
const data = await healthCheckFull(host, port);
|
|
485
|
+
if (data?.tunnelUrl) return data.tunnelUrl;
|
|
486
|
+
await sleep(500);
|
|
487
|
+
}
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
|
|
452
491
|
async function waitForHealth(host, port, timeoutMs) {
|
|
453
492
|
const start = Date.now();
|
|
454
493
|
while (Date.now() - start < timeoutMs) {
|
|
@@ -537,6 +576,7 @@ Examples:
|
|
|
537
576
|
drawlatch Show status or help
|
|
538
577
|
drawlatch start Start remote server in background
|
|
539
578
|
drawlatch start -f Start remote server in foreground
|
|
579
|
+
drawlatch start -f --tunnel Start with a public tunnel for webhooks
|
|
540
580
|
drawlatch start --port 8080 Start on a custom port
|
|
541
581
|
drawlatch status Check if server is running
|
|
542
582
|
drawlatch logs -n 100 View last 100 log lines
|
|
@@ -555,6 +595,7 @@ Usage: drawlatch start [options]
|
|
|
555
595
|
|
|
556
596
|
Options:
|
|
557
597
|
-f, --foreground Run in foreground (default when no command given)
|
|
598
|
+
-t, --tunnel Start a Cloudflare tunnel for webhook ingestion (requires cloudflared)
|
|
558
599
|
--port <number> Override the configured port
|
|
559
600
|
--host <address> Override the configured host
|
|
560
601
|
-h, --help Show this help message
|
|
@@ -623,7 +664,7 @@ Usage: drawlatch logs [options]
|
|
|
623
664
|
|
|
624
665
|
Options:
|
|
625
666
|
-n, --lines <number> Number of lines to show (default: 50)
|
|
626
|
-
--
|
|
667
|
+
--follow Follow/tail the log output (default: print and exit)
|
|
627
668
|
-h, --help Show this help message
|
|
628
669
|
|
|
629
670
|
Log file: ~/.drawlatch/logs/drawlatch.log
|
|
@@ -12,5 +12,21 @@
|
|
|
12
12
|
},
|
|
13
13
|
"allowedEndpoints": [
|
|
14
14
|
"https://api.anthropic.com/**"
|
|
15
|
-
]
|
|
15
|
+
],
|
|
16
|
+
"testConnection": {
|
|
17
|
+
"method": "POST",
|
|
18
|
+
"url": "https://api.anthropic.com/v1/messages",
|
|
19
|
+
"body": {
|
|
20
|
+
"model": "claude-3-haiku-20240307",
|
|
21
|
+
"max_tokens": 1,
|
|
22
|
+
"messages": [
|
|
23
|
+
{
|
|
24
|
+
"role": "user",
|
|
25
|
+
"content": "hi"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"description": "Sends a minimal message to verify API key",
|
|
30
|
+
"expectedStatus": [200]
|
|
31
|
+
}
|
|
16
32
|
}
|
|
@@ -22,5 +22,44 @@
|
|
|
22
22
|
"deduplicateBy": "uri",
|
|
23
23
|
"eventType": "notification"
|
|
24
24
|
}
|
|
25
|
+
},
|
|
26
|
+
"testIngestor": {
|
|
27
|
+
"description": "Fetches notifications to verify Bluesky access token",
|
|
28
|
+
"strategy": "poll_once",
|
|
29
|
+
"request": {
|
|
30
|
+
"url": "https://bsky.social/xrpc/app.bsky.notification.listNotifications?limit=1",
|
|
31
|
+
"expectedStatus": [200]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"listenerConfig": {
|
|
35
|
+
"name": "Bluesky Poll Listener",
|
|
36
|
+
"description": "Polls Bluesky notifications at a configurable interval.",
|
|
37
|
+
"fields": [
|
|
38
|
+
{
|
|
39
|
+
"key": "intervalMs",
|
|
40
|
+
"label": "Poll Interval (ms)",
|
|
41
|
+
"description": "How often to check for new notifications, in milliseconds.",
|
|
42
|
+
"type": "number",
|
|
43
|
+
"default": 60000,
|
|
44
|
+
"min": 10000,
|
|
45
|
+
"max": 3600000,
|
|
46
|
+
"group": "Connection"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"key": "bufferSize",
|
|
50
|
+
"label": "Buffer Size",
|
|
51
|
+
"description": "Maximum number of events to keep in memory.",
|
|
52
|
+
"type": "number",
|
|
53
|
+
"default": 200,
|
|
54
|
+
"min": 10,
|
|
55
|
+
"max": 1000,
|
|
56
|
+
"group": "Advanced"
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
"testConnection": {
|
|
61
|
+
"url": "https://bsky.social/xrpc/app.bsky.actor.getProfile?actor=did:self",
|
|
62
|
+
"description": "Fetches the authenticated user's profile",
|
|
63
|
+
"expectedStatus": [200, 400]
|
|
25
64
|
}
|
|
26
65
|
}
|
|
@@ -20,5 +20,93 @@
|
|
|
20
20
|
"protocol": "discord",
|
|
21
21
|
"intents": 3276799
|
|
22
22
|
}
|
|
23
|
+
},
|
|
24
|
+
"testIngestor": {
|
|
25
|
+
"description": "Verifies the bot token has Gateway access",
|
|
26
|
+
"strategy": "websocket_auth",
|
|
27
|
+
"request": {
|
|
28
|
+
"url": "https://discord.com/api/v10/gateway/bot",
|
|
29
|
+
"description": "Checks bot token has gateway access"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"listenerConfig": {
|
|
33
|
+
"name": "Discord Gateway Listener",
|
|
34
|
+
"description": "Real-time events from Discord via the Gateway WebSocket.",
|
|
35
|
+
"fields": [
|
|
36
|
+
{
|
|
37
|
+
"key": "eventFilter",
|
|
38
|
+
"label": "Event Types",
|
|
39
|
+
"description": "Which Gateway event types to capture. Leave empty for all.",
|
|
40
|
+
"type": "multiselect",
|
|
41
|
+
"default": [],
|
|
42
|
+
"options": [
|
|
43
|
+
{"value": "MESSAGE_CREATE", "label": "Message Create"},
|
|
44
|
+
{"value": "MESSAGE_UPDATE", "label": "Message Update"},
|
|
45
|
+
{"value": "MESSAGE_DELETE", "label": "Message Delete"},
|
|
46
|
+
{"value": "MESSAGE_REACTION_ADD", "label": "Reaction Add"},
|
|
47
|
+
{"value": "MESSAGE_REACTION_REMOVE", "label": "Reaction Remove"},
|
|
48
|
+
{"value": "GUILD_MEMBER_ADD", "label": "Member Join"},
|
|
49
|
+
{"value": "GUILD_MEMBER_REMOVE", "label": "Member Leave"},
|
|
50
|
+
{"value": "CHANNEL_CREATE", "label": "Channel Create"},
|
|
51
|
+
{"value": "CHANNEL_UPDATE", "label": "Channel Update"},
|
|
52
|
+
{"value": "CHANNEL_DELETE", "label": "Channel Delete"},
|
|
53
|
+
{"value": "PRESENCE_UPDATE", "label": "Presence Update"},
|
|
54
|
+
{"value": "TYPING_START", "label": "Typing Start"},
|
|
55
|
+
{"value": "VOICE_STATE_UPDATE", "label": "Voice State Update"}
|
|
56
|
+
],
|
|
57
|
+
"group": "Filtering"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"key": "guildIds",
|
|
61
|
+
"label": "Server (Guild) IDs",
|
|
62
|
+
"description": "Only receive events from these Discord servers. Leave empty for all.",
|
|
63
|
+
"type": "text[]",
|
|
64
|
+
"placeholder": "e.g., 123456789012345678",
|
|
65
|
+
"dynamicOptions": {
|
|
66
|
+
"url": "https://discord.com/api/v10/users/@me/guilds",
|
|
67
|
+
"labelField": "name",
|
|
68
|
+
"valueField": "id"
|
|
69
|
+
},
|
|
70
|
+
"group": "Filtering"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"key": "channelIds",
|
|
74
|
+
"label": "Channel IDs",
|
|
75
|
+
"description": "Only receive events from these channels. Leave empty for all.",
|
|
76
|
+
"type": "text[]",
|
|
77
|
+
"placeholder": "e.g., 123456789012345678",
|
|
78
|
+
"group": "Filtering"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"key": "userIds",
|
|
82
|
+
"label": "User IDs",
|
|
83
|
+
"description": "Only receive events from these users. Leave empty for all.",
|
|
84
|
+
"type": "text[]",
|
|
85
|
+
"placeholder": "e.g., 123456789012345678",
|
|
86
|
+
"group": "Filtering"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"key": "intents",
|
|
90
|
+
"label": "Gateway Intents",
|
|
91
|
+
"description": "Bitmask controlling which events the bot receives. See Discord docs for values.",
|
|
92
|
+
"type": "number",
|
|
93
|
+
"default": 3276799,
|
|
94
|
+
"group": "Advanced"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"key": "bufferSize",
|
|
98
|
+
"label": "Buffer Size",
|
|
99
|
+
"description": "Maximum number of events to keep in memory.",
|
|
100
|
+
"type": "number",
|
|
101
|
+
"default": 200,
|
|
102
|
+
"min": 10,
|
|
103
|
+
"max": 1000,
|
|
104
|
+
"group": "Advanced"
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
"testConnection": {
|
|
109
|
+
"url": "https://discord.com/api/v10/users/@me",
|
|
110
|
+
"description": "Fetches the authenticated bot user"
|
|
23
111
|
}
|
|
24
112
|
}
|
|
@@ -21,5 +21,62 @@
|
|
|
21
21
|
"signatureHeader": "X-Hub-Signature-256",
|
|
22
22
|
"signatureSecret": "GITHUB_WEBHOOK_SECRET"
|
|
23
23
|
}
|
|
24
|
+
},
|
|
25
|
+
"testIngestor": {
|
|
26
|
+
"description": "Verifies that the GitHub webhook secret is configured",
|
|
27
|
+
"strategy": "webhook_verify",
|
|
28
|
+
"requireSecrets": ["GITHUB_WEBHOOK_SECRET"]
|
|
29
|
+
},
|
|
30
|
+
"listenerConfig": {
|
|
31
|
+
"name": "GitHub Webhook Listener",
|
|
32
|
+
"description": "Receives real-time events from GitHub via webhooks. Supports multiple concurrent instances to filter events from different repositories.",
|
|
33
|
+
"supportsMultiInstance": true,
|
|
34
|
+
"fields": [
|
|
35
|
+
{
|
|
36
|
+
"key": "repoFilter",
|
|
37
|
+
"label": "Repository Filter",
|
|
38
|
+
"description": "Only capture webhook events from these repositories (owner/repo format). Leave empty for all.",
|
|
39
|
+
"type": "text[]",
|
|
40
|
+
"instanceKey": true,
|
|
41
|
+
"placeholder": "e.g., octocat/Hello-World",
|
|
42
|
+
"group": "Filtering"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"key": "eventFilter",
|
|
46
|
+
"label": "Event Types",
|
|
47
|
+
"description": "Which GitHub event types to capture. Leave empty for all.",
|
|
48
|
+
"type": "multiselect",
|
|
49
|
+
"default": [],
|
|
50
|
+
"options": [
|
|
51
|
+
{"value": "push", "label": "Push"},
|
|
52
|
+
{"value": "pull_request", "label": "Pull Request"},
|
|
53
|
+
{"value": "issues", "label": "Issues"},
|
|
54
|
+
{"value": "issue_comment", "label": "Issue Comment"},
|
|
55
|
+
{"value": "create", "label": "Branch/Tag Created"},
|
|
56
|
+
{"value": "delete", "label": "Branch/Tag Deleted"},
|
|
57
|
+
{"value": "release", "label": "Release"},
|
|
58
|
+
{"value": "workflow_run", "label": "Workflow Run"},
|
|
59
|
+
{"value": "check_run", "label": "Check Run"},
|
|
60
|
+
{"value": "star", "label": "Star"},
|
|
61
|
+
{"value": "fork", "label": "Fork"},
|
|
62
|
+
{"value": "deployment", "label": "Deployment"}
|
|
63
|
+
],
|
|
64
|
+
"group": "Filtering"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"key": "bufferSize",
|
|
68
|
+
"label": "Buffer Size",
|
|
69
|
+
"description": "Maximum number of events to keep in memory.",
|
|
70
|
+
"type": "number",
|
|
71
|
+
"default": 200,
|
|
72
|
+
"min": 10,
|
|
73
|
+
"max": 1000,
|
|
74
|
+
"group": "Advanced"
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
"testConnection": {
|
|
79
|
+
"url": "https://api.github.com/user",
|
|
80
|
+
"description": "Fetches the authenticated user profile"
|
|
24
81
|
}
|
|
25
82
|
}
|
|
@@ -24,5 +24,9 @@
|
|
|
24
24
|
"https://youtube.googleapis.com/**",
|
|
25
25
|
"https://youtubeanalytics.googleapis.com/**",
|
|
26
26
|
"https://cloudresourcemanager.googleapis.com/**"
|
|
27
|
-
]
|
|
27
|
+
],
|
|
28
|
+
"testConnection": {
|
|
29
|
+
"url": "https://www.googleapis.com/oauth2/v1/userinfo",
|
|
30
|
+
"description": "Fetches the authenticated user info"
|
|
31
|
+
}
|
|
28
32
|
}
|
|
@@ -25,5 +25,49 @@
|
|
|
25
25
|
"deduplicateBy": "id",
|
|
26
26
|
"eventType": "issue_updated"
|
|
27
27
|
}
|
|
28
|
+
},
|
|
29
|
+
"testIngestor": {
|
|
30
|
+
"description": "Executes a minimal GraphQL query to verify Linear API access",
|
|
31
|
+
"strategy": "poll_once",
|
|
32
|
+
"request": {
|
|
33
|
+
"method": "POST",
|
|
34
|
+
"url": "https://api.linear.app/graphql",
|
|
35
|
+
"body": {"query": "{ viewer { id } }"},
|
|
36
|
+
"expectedStatus": [200]
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"listenerConfig": {
|
|
40
|
+
"name": "Linear Poll Listener",
|
|
41
|
+
"description": "Polls Linear for recently updated issues at a configurable interval.",
|
|
42
|
+
"fields": [
|
|
43
|
+
{
|
|
44
|
+
"key": "intervalMs",
|
|
45
|
+
"label": "Poll Interval (ms)",
|
|
46
|
+
"description": "How often to check for new events, in milliseconds.",
|
|
47
|
+
"type": "number",
|
|
48
|
+
"default": 60000,
|
|
49
|
+
"min": 10000,
|
|
50
|
+
"max": 3600000,
|
|
51
|
+
"group": "Connection"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"key": "bufferSize",
|
|
55
|
+
"label": "Buffer Size",
|
|
56
|
+
"description": "Maximum number of events to keep in memory.",
|
|
57
|
+
"type": "number",
|
|
58
|
+
"default": 200,
|
|
59
|
+
"min": 10,
|
|
60
|
+
"max": 1000,
|
|
61
|
+
"group": "Advanced"
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
"testConnection": {
|
|
66
|
+
"method": "POST",
|
|
67
|
+
"url": "https://api.linear.app/graphql",
|
|
68
|
+
"body": {
|
|
69
|
+
"query": "{ viewer { id name } }"
|
|
70
|
+
},
|
|
71
|
+
"description": "Fetches the authenticated user via GraphQL"
|
|
28
72
|
}
|
|
29
73
|
}
|
|
@@ -21,5 +21,43 @@
|
|
|
21
21
|
"deduplicateBy": "id",
|
|
22
22
|
"eventType": "status"
|
|
23
23
|
}
|
|
24
|
+
},
|
|
25
|
+
"testIngestor": {
|
|
26
|
+
"description": "Verifies Mastodon credentials by checking account",
|
|
27
|
+
"strategy": "poll_once",
|
|
28
|
+
"request": {
|
|
29
|
+
"url": "https://mastodon.social/api/v1/accounts/verify_credentials",
|
|
30
|
+
"expectedStatus": [200]
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"listenerConfig": {
|
|
34
|
+
"name": "Mastodon Poll Listener",
|
|
35
|
+
"description": "Polls the Mastodon home timeline at a configurable interval.",
|
|
36
|
+
"fields": [
|
|
37
|
+
{
|
|
38
|
+
"key": "intervalMs",
|
|
39
|
+
"label": "Poll Interval (ms)",
|
|
40
|
+
"description": "How often to check for new statuses, in milliseconds.",
|
|
41
|
+
"type": "number",
|
|
42
|
+
"default": 60000,
|
|
43
|
+
"min": 10000,
|
|
44
|
+
"max": 3600000,
|
|
45
|
+
"group": "Connection"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"key": "bufferSize",
|
|
49
|
+
"label": "Buffer Size",
|
|
50
|
+
"description": "Maximum number of events to keep in memory.",
|
|
51
|
+
"type": "number",
|
|
52
|
+
"default": 200,
|
|
53
|
+
"min": 10,
|
|
54
|
+
"max": 1000,
|
|
55
|
+
"group": "Advanced"
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
"testConnection": {
|
|
60
|
+
"url": "https://mastodon.social/api/v1/accounts/verify_credentials",
|
|
61
|
+
"description": "Verifies Mastodon account credentials"
|
|
24
62
|
}
|
|
25
63
|
}
|
|
@@ -29,5 +29,45 @@
|
|
|
29
29
|
"deduplicateBy": "id",
|
|
30
30
|
"eventType": "page_updated"
|
|
31
31
|
}
|
|
32
|
+
},
|
|
33
|
+
"testIngestor": {
|
|
34
|
+
"description": "Executes a single search poll to verify Notion API access",
|
|
35
|
+
"strategy": "poll_once",
|
|
36
|
+
"request": {
|
|
37
|
+
"method": "POST",
|
|
38
|
+
"url": "https://api.notion.com/v1/search",
|
|
39
|
+
"body": {"page_size": 1},
|
|
40
|
+
"expectedStatus": [200]
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"listenerConfig": {
|
|
44
|
+
"name": "Notion Poll Listener",
|
|
45
|
+
"description": "Polls Notion for recently edited pages at a configurable interval.",
|
|
46
|
+
"fields": [
|
|
47
|
+
{
|
|
48
|
+
"key": "intervalMs",
|
|
49
|
+
"label": "Poll Interval (ms)",
|
|
50
|
+
"description": "How often to check for new events, in milliseconds.",
|
|
51
|
+
"type": "number",
|
|
52
|
+
"default": 60000,
|
|
53
|
+
"min": 10000,
|
|
54
|
+
"max": 3600000,
|
|
55
|
+
"group": "Connection"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"key": "bufferSize",
|
|
59
|
+
"label": "Buffer Size",
|
|
60
|
+
"description": "Maximum number of events to keep in memory.",
|
|
61
|
+
"type": "number",
|
|
62
|
+
"default": 200,
|
|
63
|
+
"min": 10,
|
|
64
|
+
"max": 1000,
|
|
65
|
+
"group": "Advanced"
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
"testConnection": {
|
|
70
|
+
"url": "https://api.notion.com/v1/users/me",
|
|
71
|
+
"description": "Fetches the authenticated bot user"
|
|
32
72
|
}
|
|
33
73
|
}
|
|
@@ -24,5 +24,55 @@
|
|
|
24
24
|
"deduplicateBy": "data.name",
|
|
25
25
|
"eventType": "new_post"
|
|
26
26
|
}
|
|
27
|
+
},
|
|
28
|
+
"testIngestor": {
|
|
29
|
+
"description": "Fetches the authenticated user to verify Reddit API access",
|
|
30
|
+
"strategy": "poll_once",
|
|
31
|
+
"request": {
|
|
32
|
+
"url": "https://oauth.reddit.com/api/v1/me",
|
|
33
|
+
"expectedStatus": [200]
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"listenerConfig": {
|
|
37
|
+
"name": "Reddit Poll Listener",
|
|
38
|
+
"description": "Polls a subreddit for new posts at a configurable interval. Supports multiple concurrent instances to watch different subreddits simultaneously.",
|
|
39
|
+
"supportsMultiInstance": true,
|
|
40
|
+
"fields": [
|
|
41
|
+
{
|
|
42
|
+
"key": "subreddit",
|
|
43
|
+
"label": "Subreddit",
|
|
44
|
+
"description": "The subreddit to watch for new posts (without the r/ prefix).",
|
|
45
|
+
"type": "text",
|
|
46
|
+
"required": true,
|
|
47
|
+
"instanceKey": true,
|
|
48
|
+
"overrideKey": "REDDIT_SUBREDDIT",
|
|
49
|
+
"placeholder": "e.g., rust",
|
|
50
|
+
"group": "Connection"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"key": "intervalMs",
|
|
54
|
+
"label": "Poll Interval (ms)",
|
|
55
|
+
"description": "How often to check for new posts, in milliseconds.",
|
|
56
|
+
"type": "number",
|
|
57
|
+
"default": 60000,
|
|
58
|
+
"min": 10000,
|
|
59
|
+
"max": 3600000,
|
|
60
|
+
"group": "Connection"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"key": "bufferSize",
|
|
64
|
+
"label": "Buffer Size",
|
|
65
|
+
"description": "Maximum number of events to keep in memory.",
|
|
66
|
+
"type": "number",
|
|
67
|
+
"default": 200,
|
|
68
|
+
"min": 10,
|
|
69
|
+
"max": 1000,
|
|
70
|
+
"group": "Advanced"
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
"testConnection": {
|
|
75
|
+
"url": "https://oauth.reddit.com/api/v1/me",
|
|
76
|
+
"description": "Fetches the authenticated Reddit user"
|
|
27
77
|
}
|
|
28
78
|
}
|