aiden-runtime 3.19.5 → 3.19.7
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 +1 -1
- package/dist/api/server.js +24 -25
- package/dist/core/agentLoop.js +8 -6
- package/dist/core/aidenPersonality.js +20 -3
- package/dist/core/protectedContext.js +15 -2
- package/dist/core/skillLoader.js +2 -0
- package/dist/core/skillTeacher.js +18 -5
- package/dist/core/version.js +1 -1
- package/dist-bundle/cli.js +50 -18
- package/dist-bundle/index.js +73 -35
- package/package.json +2 -1
- package/scripts/postinstall.js +70 -1
- package/workspace-templates/HEARTBEAT.md +16 -0
- package/workspace-templates/SOUL.md +267 -0
- package/workspace-templates/STANDING_ORDERS.md +21 -0
- package/workspace-templates/permissions.yaml +180 -0
- package/workspace-templates/skills/architecture-diagram/SKILL.md +126 -0
- package/workspace-templates/skills/architecture-diagram/skill.json +25 -0
- package/workspace-templates/skills/arxiv/SKILL.md +124 -0
- package/workspace-templates/skills/arxiv/skill.json +26 -0
- package/workspace-templates/skills/ascii-art/SKILL.md +142 -0
- package/workspace-templates/skills/ascii-art/skill.json +26 -0
- package/workspace-templates/skills/blogwatcher/SKILL.md +147 -0
- package/workspace-templates/skills/blogwatcher/skill.json +26 -0
- package/workspace-templates/skills/censys/SKILL.md +104 -0
- package/workspace-templates/skills/censys/index.ts +133 -0
- package/workspace-templates/skills/censys/skill.json +25 -0
- package/workspace-templates/skills/clipboard-history/SKILL.md +101 -0
- package/workspace-templates/skills/clipboard-history/skill.json +23 -0
- package/workspace-templates/skills/crt-sh/SKILL.md +102 -0
- package/workspace-templates/skills/crt-sh/index.ts +59 -0
- package/workspace-templates/skills/crt-sh/skill.json +25 -0
- package/workspace-templates/skills/cveapi/SKILL.md +114 -0
- package/workspace-templates/skills/cveapi/index.ts +249 -0
- package/workspace-templates/skills/cveapi/skill.json +25 -0
- package/workspace-templates/skills/docker-management/SKILL.md +156 -0
- package/workspace-templates/skills/docker-management/skill.json +25 -0
- package/workspace-templates/skills/excalidraw/SKILL.md +148 -0
- package/workspace-templates/skills/excalidraw/skill.json +25 -0
- package/workspace-templates/skills/explainshell/SKILL.md +93 -0
- package/workspace-templates/skills/explainshell/index.ts +132 -0
- package/workspace-templates/skills/explainshell/skill.json +25 -0
- package/workspace-templates/skills/financial_research/SKILL.md +21 -0
- package/workspace-templates/skills/financial_research/skill.json +24 -0
- package/workspace-templates/skills/gif-search/SKILL.md +122 -0
- package/workspace-templates/skills/gif-search/skill.json +25 -0
- package/workspace-templates/skills/github-auth/SKILL.md +134 -0
- package/workspace-templates/skills/github-auth/skill.json +26 -0
- package/workspace-templates/skills/github-issues/SKILL.md +130 -0
- package/workspace-templates/skills/github-issues/skill.json +25 -0
- package/workspace-templates/skills/github-pr-workflow/SKILL.md +143 -0
- package/workspace-templates/skills/github-pr-workflow/skill.json +26 -0
- package/workspace-templates/skills/github-repo-management/SKILL.md +147 -0
- package/workspace-templates/skills/github-repo-management/skill.json +26 -0
- package/workspace-templates/skills/google-workspace/SKILL.md +110 -0
- package/workspace-templates/skills/google-workspace/skill.json +26 -0
- package/workspace-templates/skills/greynoise/SKILL.md +96 -0
- package/workspace-templates/skills/greynoise/index.ts +107 -0
- package/workspace-templates/skills/greynoise/skill.json +25 -0
- package/workspace-templates/skills/haveibeenpwned/SKILL.md +100 -0
- package/workspace-templates/skills/haveibeenpwned/index.ts +72 -0
- package/workspace-templates/skills/haveibeenpwned/skill.json +24 -0
- package/workspace-templates/skills/jupyter-live-kernel/SKILL.md +116 -0
- package/workspace-templates/skills/jupyter-live-kernel/skill.json +25 -0
- package/workspace-templates/skills/linear/SKILL.md +107 -0
- package/workspace-templates/skills/linear/skill.json +25 -0
- package/workspace-templates/skills/nano-pdf/SKILL.md +113 -0
- package/workspace-templates/skills/nano-pdf/skill.json +26 -0
- package/workspace-templates/skills/notion/SKILL.md +108 -0
- package/workspace-templates/skills/notion/skill.json +24 -0
- package/workspace-templates/skills/obsidian/SKILL.md +115 -0
- package/workspace-templates/skills/obsidian/skill.json +24 -0
- package/workspace-templates/skills/ocr-and-documents/SKILL.md +125 -0
- package/workspace-templates/skills/ocr-and-documents/skill.json +26 -0
- package/workspace-templates/skills/p5js/SKILL.md +163 -0
- package/workspace-templates/skills/p5js/skill.json +24 -0
- package/workspace-templates/skills/research-paper-writing/SKILL.md +158 -0
- package/workspace-templates/skills/research-paper-writing/skill.json +26 -0
- package/workspace-templates/skills/securityheaders/SKILL.md +99 -0
- package/workspace-templates/skills/securityheaders/index.ts +213 -0
- package/workspace-templates/skills/securityheaders/skill.json +26 -0
- package/workspace-templates/skills/shodan/SKILL.md +113 -0
- package/workspace-templates/skills/shodan/index.ts +94 -0
- package/workspace-templates/skills/shodan/skill.json +26 -0
- package/workspace-templates/skills/songsee/SKILL.md +152 -0
- package/workspace-templates/skills/songsee/skill.json +25 -0
- package/workspace-templates/skills/ssllabs/SKILL.md +107 -0
- package/workspace-templates/skills/ssllabs/index.ts +208 -0
- package/workspace-templates/skills/ssllabs/skill.json +27 -0
- package/workspace-templates/skills/stable-diffusion-image-generation/SKILL.md +136 -0
- package/workspace-templates/skills/stable-diffusion-image-generation/skill.json +24 -0
- package/workspace-templates/skills/systematic-debugging/SKILL.md +131 -0
- package/workspace-templates/skills/systematic-debugging/skill.json +25 -0
- package/workspace-templates/skills/test-driven-development/SKILL.md +164 -0
- package/workspace-templates/skills/test-driven-development/skill.json +25 -0
- package/workspace-templates/skills/urlscan/SKILL.md +118 -0
- package/workspace-templates/skills/urlscan/index.ts +94 -0
- package/workspace-templates/skills/urlscan/skill.json +24 -0
- package/workspace-templates/skills/virustotal/SKILL.md +120 -0
- package/workspace-templates/skills/virustotal/index.ts +124 -0
- package/workspace-templates/skills/virustotal/skill.json +26 -0
- package/workspace-templates/skills/web_research/SKILL.md +18 -0
- package/workspace-templates/skills/web_research/skill.json +20 -0
- package/workspace-templates/skills/xitter/SKILL.md +148 -0
- package/workspace-templates/skills/xitter/skill.json +26 -0
- package/workspace-templates/skills/youtube-content/SKILL.md +121 -0
- package/workspace-templates/skills/youtube-content/skill.json +25 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: greynoise
|
|
3
|
+
description: Classify IP addresses as internet scanners (benign/malicious) or targeted attackers — filters noise from security alerts
|
|
4
|
+
category: security
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
license: Apache-2.0
|
|
7
|
+
origin: aiden
|
|
8
|
+
tags: security, greynoise, scanner, threat-intel, ip, osint, soc, incident-response, noise-filtering
|
|
9
|
+
env_required:
|
|
10
|
+
- GREYNOISE_API_KEY
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# GreyNoise — Internet Scanner Intelligence
|
|
14
|
+
|
|
15
|
+
GreyNoise collects and analyses internet-wide scan traffic. It tells you whether an IP hitting your firewall is a known mass-scanner (benign research tool or malicious crawler) versus a targeted attacker — helping SOC teams filter out internet noise from real threats.
|
|
16
|
+
|
|
17
|
+
**Community tier:** The `/v3/community/{ip}` endpoint works with limited volume even without a key. Set `GREYNOISE_API_KEY` for higher rate limits.
|
|
18
|
+
|
|
19
|
+
## When to Use
|
|
20
|
+
|
|
21
|
+
- An IP triggers a firewall rule and you want to know if it is just background internet noise
|
|
22
|
+
- SOC triage: is this alert a known scanner or a targeted attack?
|
|
23
|
+
- Filter false positives from SIEM before escalating an alert
|
|
24
|
+
- User asks "is this IP a scanner?", "is this noisy traffic?", "is IP X benign?"
|
|
25
|
+
|
|
26
|
+
## How to Use
|
|
27
|
+
|
|
28
|
+
### Check if an IP is a known internet scanner
|
|
29
|
+
|
|
30
|
+
```powershell
|
|
31
|
+
$ip = "71.6.135.131"
|
|
32
|
+
$headers = @{}
|
|
33
|
+
if ($env:GREYNOISE_API_KEY) { $headers['key'] = $env:GREYNOISE_API_KEY }
|
|
34
|
+
|
|
35
|
+
$result = Invoke-RestMethod -Uri "https://api.greynoise.io/v3/community/$ip" -Headers $headers
|
|
36
|
+
Write-Host "IP: $($result.ip)"
|
|
37
|
+
Write-Host "Noise: $($result.noise)" # true = mass-scanner
|
|
38
|
+
Write-Host "RIOT: $($result.riot)" # true = trusted benign service
|
|
39
|
+
Write-Host "Classification: $($result.classification)" # benign | malicious | unknown
|
|
40
|
+
Write-Host "Name: $($result.name)"
|
|
41
|
+
Write-Host "Last seen: $($result.last_seen)"
|
|
42
|
+
Write-Host "Link: $($result.link)"
|
|
43
|
+
Write-Host "Message: $($result.message)"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Triage a batch of IPs from a firewall log
|
|
47
|
+
|
|
48
|
+
```powershell
|
|
49
|
+
$ips = @("1.1.1.1", "8.8.8.8", "71.6.135.131", "45.83.66.42")
|
|
50
|
+
$headers = @{}
|
|
51
|
+
if ($env:GREYNOISE_API_KEY) { $headers['key'] = $env:GREYNOISE_API_KEY }
|
|
52
|
+
|
|
53
|
+
foreach ($ip in $ips) {
|
|
54
|
+
try {
|
|
55
|
+
$r = Invoke-RestMethod -Uri "https://api.greynoise.io/v3/community/$ip" -Headers $headers
|
|
56
|
+
$tag = if ($r.riot) { '[RIOT-trusted]' } elseif ($r.noise) { '[SCANNER]' } else { '[TARGETED?]' }
|
|
57
|
+
Write-Host "$ip $tag $($r.classification) $($r.name)"
|
|
58
|
+
} catch {
|
|
59
|
+
Write-Host "$ip [ERROR] $($_.Exception.Message)"
|
|
60
|
+
}
|
|
61
|
+
Start-Sleep -Milliseconds 200
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Interpret the results
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
noise = true → IP is a known mass-scanner (benign researchers, crawlers, etc.)
|
|
69
|
+
riot = true → IP belongs to a trusted service (Cloudflare, Google, AWS, etc.)
|
|
70
|
+
classification = "malicious" → Known malicious scanner — block and investigate
|
|
71
|
+
classification = "benign" → Probably safe background noise — may deprioritise
|
|
72
|
+
classification = "unknown" → No data — treat with caution
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Examples
|
|
76
|
+
|
|
77
|
+
**"Is 71.6.135.131 an attacker?"**
|
|
78
|
+
→ Returns classification: malicious, noise: true — known malicious scanner.
|
|
79
|
+
|
|
80
|
+
**"This IP keeps hitting our web server — is it just background noise?"**
|
|
81
|
+
→ `noise: true` means it is scanning the whole internet, not targeting you specifically.
|
|
82
|
+
|
|
83
|
+
**"Is 8.8.8.8 a threat?"**
|
|
84
|
+
→ `riot: true` — belongs to Google DNS, trusted RIOT (Rule It Out) list.
|
|
85
|
+
|
|
86
|
+
## Cautions
|
|
87
|
+
|
|
88
|
+
- Community endpoint returns limited data — `GREYNOISE_API_KEY` unlocks full context
|
|
89
|
+
- `noise: false` and `riot: false` does not mean the IP is malicious — GreyNoise may simply have no data for it
|
|
90
|
+
- Data reflects GreyNoise's scanner observations — gaps exist for low-volume IPs
|
|
91
|
+
- Free community endpoint: ~50 lookups/day per source IP without a key
|
|
92
|
+
|
|
93
|
+
## Requirements
|
|
94
|
+
|
|
95
|
+
- No key required for low-volume community lookups
|
|
96
|
+
- `GREYNOISE_API_KEY` — free community key at https://viz.greynoise.io/account
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// skills/greynoise/index.ts
|
|
2
|
+
// Programmatic handler — IP scanner intelligence via GreyNoise community API.
|
|
3
|
+
|
|
4
|
+
import { ApiSkill } from '../../core/apiSkillBase'
|
|
5
|
+
|
|
6
|
+
// Community endpoint works with or without a key.
|
|
7
|
+
// We create the skill instance conditionally to avoid sending an empty key header.
|
|
8
|
+
let _skill: ApiSkill | null = null
|
|
9
|
+
|
|
10
|
+
function getSkill(): ApiSkill {
|
|
11
|
+
if (_skill) return _skill
|
|
12
|
+
|
|
13
|
+
const key = process.env['GREYNOISE_API_KEY']
|
|
14
|
+
|
|
15
|
+
_skill = key
|
|
16
|
+
? new ApiSkill({
|
|
17
|
+
name: 'greynoise',
|
|
18
|
+
baseUrl: 'https://api.greynoise.io/v3',
|
|
19
|
+
authType: 'header',
|
|
20
|
+
authHeader: 'key',
|
|
21
|
+
apiKey: key,
|
|
22
|
+
rateLimit: { requests: 10, windowMs: 60_000 },
|
|
23
|
+
timeout: 15_000,
|
|
24
|
+
retries: 2,
|
|
25
|
+
})
|
|
26
|
+
: new ApiSkill({
|
|
27
|
+
name: 'greynoise',
|
|
28
|
+
baseUrl: 'https://api.greynoise.io/v3',
|
|
29
|
+
authType: 'none',
|
|
30
|
+
rateLimit: { requests: 5, windowMs: 60_000 },
|
|
31
|
+
timeout: 15_000,
|
|
32
|
+
retries: 2,
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
return _skill
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ── Output types ──────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
export type GreyNoiseClassification = 'benign' | 'malicious' | 'unknown'
|
|
41
|
+
|
|
42
|
+
export interface GreyNoiseReport {
|
|
43
|
+
ip: string
|
|
44
|
+
noise: boolean // true = known mass internet scanner
|
|
45
|
+
riot: boolean // true = trusted benign service (Google, Cloudflare, etc.)
|
|
46
|
+
classification: GreyNoiseClassification | string
|
|
47
|
+
name: string // actor/org name if known
|
|
48
|
+
link: string // GreyNoise visualiser URL
|
|
49
|
+
lastSeen: string
|
|
50
|
+
message: string
|
|
51
|
+
hasKey: boolean // whether an API key was used
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ── Public API ────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Check whether an IP address is a known internet scanner.
|
|
58
|
+
*
|
|
59
|
+
* Uses the GreyNoise Community endpoint (/v3/community/{ip}).
|
|
60
|
+
* Works without an API key at low volume (~50 lookups/day).
|
|
61
|
+
* Set GREYNOISE_API_KEY for higher limits.
|
|
62
|
+
*/
|
|
63
|
+
export async function checkIp(ip: string): Promise<GreyNoiseReport> {
|
|
64
|
+
const trimmed = ip.trim()
|
|
65
|
+
|
|
66
|
+
// Basic IPv4/IPv6 sanity check
|
|
67
|
+
if (!trimmed) throw new Error('greynoise: IP address is required')
|
|
68
|
+
|
|
69
|
+
const raw = await getSkill().get(`/community/${encodeURIComponent(trimmed)}`)
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
ip: raw.ip ?? trimmed,
|
|
73
|
+
noise: raw.noise ?? false,
|
|
74
|
+
riot: raw.riot ?? false,
|
|
75
|
+
classification: raw.classification ?? 'unknown',
|
|
76
|
+
name: raw.name ?? '',
|
|
77
|
+
link: raw.link ?? `https://viz.greynoise.io/ip/${trimmed}`,
|
|
78
|
+
lastSeen: raw.last_seen ?? '',
|
|
79
|
+
message: raw.message ?? '',
|
|
80
|
+
hasKey: !!process.env['GREYNOISE_API_KEY'],
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Format a GreyNoiseReport as a human-readable summary. */
|
|
85
|
+
export function formatReport(report: GreyNoiseReport): string {
|
|
86
|
+
const tag = report.riot
|
|
87
|
+
? '✅ RIOT (trusted service)'
|
|
88
|
+
: report.noise
|
|
89
|
+
? (report.classification === 'malicious' ? '🚨 MALICIOUS SCANNER' : '🔍 Known scanner')
|
|
90
|
+
: '⚠️ Not in GreyNoise (may be targeted)'
|
|
91
|
+
|
|
92
|
+
const lines = [
|
|
93
|
+
`IP: ${report.ip}`,
|
|
94
|
+
`Tag: ${tag}`,
|
|
95
|
+
`Classification: ${report.classification}`,
|
|
96
|
+
`Noise: ${report.noise}`,
|
|
97
|
+
`RIOT: ${report.riot}`,
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
if (report.name) lines.push(`Name: ${report.name}`)
|
|
101
|
+
if (report.lastSeen) lines.push(`Last seen: ${report.lastSeen}`)
|
|
102
|
+
if (report.message) lines.push(`Message: ${report.message}`)
|
|
103
|
+
|
|
104
|
+
lines.push(`Profile: ${report.link}`)
|
|
105
|
+
|
|
106
|
+
return lines.join('\n')
|
|
107
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "greynoise",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Classify IP addresses as internet scanners (benign/malicious) or targeted attackers — filters noise from security alerts",
|
|
5
|
+
"author": "aiden",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"tools": [],
|
|
8
|
+
"trigger_phrases": [],
|
|
9
|
+
"compatible_agents": [
|
|
10
|
+
"aiden"
|
|
11
|
+
],
|
|
12
|
+
"min_agent_version": "3.0.0",
|
|
13
|
+
"tags": [
|
|
14
|
+
"security",
|
|
15
|
+
"greynoise",
|
|
16
|
+
"scanner",
|
|
17
|
+
"threat-intel",
|
|
18
|
+
"ip",
|
|
19
|
+
"osint",
|
|
20
|
+
"soc",
|
|
21
|
+
"incident-response",
|
|
22
|
+
"noise-filtering"
|
|
23
|
+
],
|
|
24
|
+
"created": "2026-04-27T17:11:39.765Z"
|
|
25
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: haveibeenpwned
|
|
3
|
+
description: Check if an email address or username appears in known data breaches using the Have I Been Pwned v3 API
|
|
4
|
+
category: security
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
license: Apache-2.0
|
|
7
|
+
origin: aiden
|
|
8
|
+
tags: security, breach, pwned, email, password, leak, osint, hibp
|
|
9
|
+
env_required:
|
|
10
|
+
- HIBP_API_KEY
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Have I Been Pwned — Breach Checker
|
|
14
|
+
|
|
15
|
+
Look up whether an email address has appeared in any publicly known data breaches. Uses the [Have I Been Pwned](https://haveibeenpwned.com) v3 API.
|
|
16
|
+
|
|
17
|
+
**Requires:** `HIBP_API_KEY` in `.env` — $3.50/month subscription at haveibeenpwned.com/API/Key
|
|
18
|
+
|
|
19
|
+
## When to Use
|
|
20
|
+
|
|
21
|
+
- User wants to know if their email was part of a data breach
|
|
22
|
+
- User wants a list of breaches an email was found in
|
|
23
|
+
- User is auditing accounts before a security review
|
|
24
|
+
- User asks "was my email leaked" or "has X been pwned"
|
|
25
|
+
|
|
26
|
+
## How to Use
|
|
27
|
+
|
|
28
|
+
### Check a single email for breaches
|
|
29
|
+
|
|
30
|
+
```powershell
|
|
31
|
+
$email = "user@example.com"
|
|
32
|
+
$key = $env:HIBP_API_KEY
|
|
33
|
+
$url = "https://haveibeenpwned.com/api/v3/breachedaccount/$([Uri]::EscapeDataString($email))?truncateResponse=false"
|
|
34
|
+
|
|
35
|
+
$result = Invoke-RestMethod -Uri $url -Headers @{ "hibp-api-key" = $key } -ErrorAction SilentlyContinue
|
|
36
|
+
|
|
37
|
+
if ($null -eq $result) {
|
|
38
|
+
Write-Host "✅ $email was NOT found in any known data breaches."
|
|
39
|
+
} else {
|
|
40
|
+
Write-Host "⚠️ $email found in $($result.Count) breach(es):"
|
|
41
|
+
$result | ForEach-Object {
|
|
42
|
+
Write-Host " • $($_.Name) ($($_.BreachDate)) — $($_.DataClasses -join ', ')"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Check which passwords were exposed (pastes)
|
|
48
|
+
|
|
49
|
+
```powershell
|
|
50
|
+
$email = "user@example.com"
|
|
51
|
+
$key = $env:HIBP_API_KEY
|
|
52
|
+
$url = "https://haveibeenpwned.com/api/v3/pasteaccount/$([Uri]::EscapeDataString($email))"
|
|
53
|
+
|
|
54
|
+
$result = Invoke-RestMethod -Uri $url -Headers @{ "hibp-api-key" = $key } -ErrorAction SilentlyContinue
|
|
55
|
+
|
|
56
|
+
if ($null -eq $result) {
|
|
57
|
+
Write-Host "✅ No paste exposures found for $email"
|
|
58
|
+
} else {
|
|
59
|
+
Write-Host "Found in $($result.Count) paste(s):"
|
|
60
|
+
$result | Select-Object Source, Title, Date | Format-Table
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Bulk check a list of emails from a file
|
|
65
|
+
|
|
66
|
+
```powershell
|
|
67
|
+
$key = $env:HIBP_API_KEY
|
|
68
|
+
$emails = Get-Content "emails.txt"
|
|
69
|
+
|
|
70
|
+
foreach ($email in $emails) {
|
|
71
|
+
Start-Sleep -Milliseconds 1600 # respect 1 req/1.5s rate limit
|
|
72
|
+
$url = "https://haveibeenpwned.com/api/v3/breachedaccount/$([Uri]::EscapeDataString($email))"
|
|
73
|
+
$res = Invoke-RestMethod -Uri $url -Headers @{ "hibp-api-key" = $key } -ErrorAction SilentlyContinue
|
|
74
|
+
$status = if ($res) { "PWNED ($($res.Count) breaches)" } else { "Clean" }
|
|
75
|
+
Write-Host "$email — $status"
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Examples
|
|
80
|
+
|
|
81
|
+
**"Check if test@example.com has been in any breaches"**
|
|
82
|
+
→ Run the single-email check above with that address.
|
|
83
|
+
|
|
84
|
+
**"Was my email leaked in the Adobe breach?"**
|
|
85
|
+
→ Run the single check; then filter: `$result | Where-Object Name -eq 'Adobe'`
|
|
86
|
+
|
|
87
|
+
**"Check all emails in a file for breaches"**
|
|
88
|
+
→ Use the bulk check snippet — it respects the 1.5s rate limit automatically.
|
|
89
|
+
|
|
90
|
+
## Cautions
|
|
91
|
+
|
|
92
|
+
- API key is required — there is no free anonymous tier for the v3 breached-account endpoint
|
|
93
|
+
- Rate limit is 1 request per 1.5 seconds — always add `Start-Sleep -Milliseconds 1600` in loops
|
|
94
|
+
- HTTP 404 means the email was not found (not an error) — `Invoke-RestMethod -ErrorAction SilentlyContinue` handles this silently
|
|
95
|
+
- HIBP does not store plaintext passwords — it only records breach metadata and data class types
|
|
96
|
+
- For password hash checks (k-anonymity model), use the `/range/{hash5}` endpoint — no API key required
|
|
97
|
+
|
|
98
|
+
## Requirements
|
|
99
|
+
|
|
100
|
+
- `HIBP_API_KEY` — subscribe at https://haveibeenpwned.com/API/Key ($3.50/month)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// skills/haveibeenpwned/index.ts
|
|
2
|
+
// Programmatic handler — uses ApiSkill for HTTP, auth, and rate limiting.
|
|
3
|
+
|
|
4
|
+
import { ApiSkill, requireApiKey } from '../../core/apiSkillBase'
|
|
5
|
+
|
|
6
|
+
const skill = new ApiSkill({
|
|
7
|
+
name: 'haveibeenpwned',
|
|
8
|
+
baseUrl: 'https://haveibeenpwned.com/api/v3',
|
|
9
|
+
apiKeyEnv: 'HIBP_API_KEY',
|
|
10
|
+
authType: 'header',
|
|
11
|
+
authHeader: 'hibp-api-key',
|
|
12
|
+
rateLimit: { requests: 1, windowMs: 1_500 }, // 1 req / 1.5 s
|
|
13
|
+
timeout: 15_000,
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
export interface Breach {
|
|
17
|
+
Name: string
|
|
18
|
+
BreachDate: string
|
|
19
|
+
DataClasses: string[]
|
|
20
|
+
Description: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function checkEmail(email: string): Promise<string> {
|
|
24
|
+
requireApiKey('HIBP_API_KEY')
|
|
25
|
+
|
|
26
|
+
let breaches: Breach[]
|
|
27
|
+
try {
|
|
28
|
+
breaches = await skill.get(
|
|
29
|
+
`/breachedaccount/${encodeURIComponent(email)}`,
|
|
30
|
+
{ truncateResponse: false },
|
|
31
|
+
)
|
|
32
|
+
} catch (e: any) {
|
|
33
|
+
// 404 → not found (clean)
|
|
34
|
+
if (e.message.includes('HTTP 404')) {
|
|
35
|
+
return `✅ ${email} was NOT found in any known data breaches.`
|
|
36
|
+
}
|
|
37
|
+
throw e
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!Array.isArray(breaches) || breaches.length === 0) {
|
|
41
|
+
return `✅ ${email} was NOT found in any known data breaches.`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const lines = breaches.map(
|
|
45
|
+
b => ` • ${b.Name} (${b.BreachDate}) — ${b.DataClasses.join(', ')}`,
|
|
46
|
+
)
|
|
47
|
+
return [
|
|
48
|
+
`⚠️ ${email} found in ${breaches.length} breach(es):`,
|
|
49
|
+
...lines,
|
|
50
|
+
].join('\n')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function checkPastes(email: string): Promise<string> {
|
|
54
|
+
requireApiKey('HIBP_API_KEY')
|
|
55
|
+
|
|
56
|
+
let pastes: any[]
|
|
57
|
+
try {
|
|
58
|
+
pastes = await skill.get(`/pasteaccount/${encodeURIComponent(email)}`)
|
|
59
|
+
} catch (e: any) {
|
|
60
|
+
if (e.message.includes('HTTP 404')) {
|
|
61
|
+
return `✅ ${email} was NOT found in any known pastes.`
|
|
62
|
+
}
|
|
63
|
+
throw e
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!Array.isArray(pastes) || pastes.length === 0) {
|
|
67
|
+
return `✅ ${email} was NOT found in any known pastes.`
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const lines = pastes.map(p => ` • ${p.Source} — ${p.Title ?? 'untitled'} (${p.Date ?? 'unknown date'})`)
|
|
71
|
+
return [`⚠️ ${email} found in ${pastes.length} paste(s):`, ...lines].join('\n')
|
|
72
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "haveibeenpwned",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Check if an email address or username appears in known data breaches using the Have I Been Pwned v3 API",
|
|
5
|
+
"author": "aiden",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"tools": [],
|
|
8
|
+
"trigger_phrases": [],
|
|
9
|
+
"compatible_agents": [
|
|
10
|
+
"aiden"
|
|
11
|
+
],
|
|
12
|
+
"min_agent_version": "3.0.0",
|
|
13
|
+
"tags": [
|
|
14
|
+
"security",
|
|
15
|
+
"breach",
|
|
16
|
+
"pwned",
|
|
17
|
+
"email",
|
|
18
|
+
"password",
|
|
19
|
+
"leak",
|
|
20
|
+
"osint",
|
|
21
|
+
"hibp"
|
|
22
|
+
],
|
|
23
|
+
"created": "2026-04-27T17:11:39.790Z"
|
|
24
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jupyter-live-kernel
|
|
3
|
+
description: Execute code in a stateful Jupyter kernel session, maintaining variables across cells using hamelnb or jupyter CLI
|
|
4
|
+
category: developer
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
origin: aiden
|
|
7
|
+
license: Apache-2.0
|
|
8
|
+
tags: jupyter, notebook, kernel, python, data-science, ipython, stateful, cells, pandas
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Jupyter Live Kernel Execution
|
|
12
|
+
|
|
13
|
+
Run Python code in a persistent Jupyter kernel so that variables, imports, and state carry over between executions — exactly like working in a notebook, but from the CLI.
|
|
14
|
+
|
|
15
|
+
## When to Use
|
|
16
|
+
|
|
17
|
+
- User wants to run data analysis across multiple code cells with shared state
|
|
18
|
+
- User wants to explore a dataset step by step
|
|
19
|
+
- User wants to run ML training and inspect intermediate results
|
|
20
|
+
- User wants to execute a `.ipynb` notebook file from the command line
|
|
21
|
+
- User wants to maintain a REPL-like Python session with persistent variables
|
|
22
|
+
|
|
23
|
+
## How to Use
|
|
24
|
+
|
|
25
|
+
### 1. Install hamelnb (stateful kernel CLI)
|
|
26
|
+
|
|
27
|
+
```powershell
|
|
28
|
+
pip install hamelnb
|
|
29
|
+
# or use jupyter directly
|
|
30
|
+
pip install jupyter
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 2. Start a kernel and run cells (hamelnb)
|
|
34
|
+
|
|
35
|
+
```powershell
|
|
36
|
+
# Start a persistent kernel session (keeps running between calls)
|
|
37
|
+
hamelnb start --name datasession
|
|
38
|
+
|
|
39
|
+
# Execute a code snippet in the named session
|
|
40
|
+
hamelnb run datasession "import pandas as pd; df = pd.read_csv('data.csv'); print(df.shape)"
|
|
41
|
+
|
|
42
|
+
# Execute next cell — df variable is still available
|
|
43
|
+
hamelnb run datasession "print(df.describe())"
|
|
44
|
+
|
|
45
|
+
# Stop session when done
|
|
46
|
+
hamelnb stop datasession
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 3. Execute a notebook file
|
|
50
|
+
|
|
51
|
+
```powershell
|
|
52
|
+
# Run all cells in a notebook and save output
|
|
53
|
+
jupyter nbconvert --to notebook --execute analysis.ipynb --output analysis_out.ipynb
|
|
54
|
+
|
|
55
|
+
# Run and convert output to HTML for viewing
|
|
56
|
+
jupyter nbconvert --to html --execute analysis.ipynb --output report.html
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 4. Run Python code in a Jupyter kernel via Python API
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import jupyter_client, queue
|
|
63
|
+
|
|
64
|
+
km = jupyter_client.KernelManager(kernel_name="python3")
|
|
65
|
+
km.start_kernel()
|
|
66
|
+
kc = km.client()
|
|
67
|
+
kc.start_channels()
|
|
68
|
+
kc.wait_for_ready(timeout=30)
|
|
69
|
+
|
|
70
|
+
def run_cell(code):
|
|
71
|
+
kc.execute(code)
|
|
72
|
+
outputs = []
|
|
73
|
+
while True:
|
|
74
|
+
try:
|
|
75
|
+
msg = kc.get_iopub_msg(timeout=10)
|
|
76
|
+
if msg["msg_type"] == "stream":
|
|
77
|
+
outputs.append(msg["content"]["text"])
|
|
78
|
+
elif msg["msg_type"] == "execute_result":
|
|
79
|
+
outputs.append(msg["content"]["data"].get("text/plain",""))
|
|
80
|
+
elif msg["msg_type"] == "status" and msg["content"]["execution_state"] == "idle":
|
|
81
|
+
break
|
|
82
|
+
except queue.Empty:
|
|
83
|
+
break
|
|
84
|
+
return "".join(outputs)
|
|
85
|
+
|
|
86
|
+
print(run_cell("import pandas as pd; df = pd.read_csv('data.csv'); df.shape"))
|
|
87
|
+
print(run_cell("df.describe()")) # df is still in scope!
|
|
88
|
+
km.shutdown_kernel()
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 5. Inject variables into a running kernel
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
# Use run_cell from step 4 to inject values
|
|
95
|
+
run_cell("x = 42; y = [1, 2, 3]")
|
|
96
|
+
result = run_cell("print(x * 2, sum(y))")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Examples
|
|
100
|
+
|
|
101
|
+
**"Load sales.csv and show the top 10 rows, then plot revenue by month"**
|
|
102
|
+
→ Use step 4: run cell 1 to load and preview the CSV, run cell 2 to group by month and show results — `df` persists between calls.
|
|
103
|
+
|
|
104
|
+
**"Execute my analysis.ipynb notebook and give me the output"**
|
|
105
|
+
→ Use step 3 with `jupyter nbconvert --to notebook --execute`.
|
|
106
|
+
|
|
107
|
+
**"Explore the wine quality dataset — check correlations step by step"**
|
|
108
|
+
→ Use hamelnb (step 2) to build up analysis iteratively with named session.
|
|
109
|
+
|
|
110
|
+
## Cautions
|
|
111
|
+
|
|
112
|
+
- Kernel sessions consume memory for as long as they run — always `km.shutdown_kernel()` when done
|
|
113
|
+
- Long-running cells (ML training) will block until complete — set reasonable timeouts
|
|
114
|
+
- `nbconvert --execute` re-runs all cells from scratch — it does not resume a previous state
|
|
115
|
+
- hamelnb is a third-party tool — verify it is installed with `pip show hamelnb` before use
|
|
116
|
+
- Never pass user secrets as inline code strings — use environment variables or config files instead
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jupyter-live-kernel",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Execute code in a stateful Jupyter kernel session, maintaining variables across cells using hamelnb or jupyter CLI",
|
|
5
|
+
"author": "aiden",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"tools": [],
|
|
8
|
+
"trigger_phrases": [],
|
|
9
|
+
"compatible_agents": [
|
|
10
|
+
"aiden"
|
|
11
|
+
],
|
|
12
|
+
"min_agent_version": "3.0.0",
|
|
13
|
+
"tags": [
|
|
14
|
+
"jupyter",
|
|
15
|
+
"notebook",
|
|
16
|
+
"kernel",
|
|
17
|
+
"python",
|
|
18
|
+
"data-science",
|
|
19
|
+
"ipython",
|
|
20
|
+
"stateful",
|
|
21
|
+
"cells",
|
|
22
|
+
"pandas"
|
|
23
|
+
],
|
|
24
|
+
"created": "2026-04-27T17:11:39.871Z"
|
|
25
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: linear
|
|
3
|
+
description: Manage Linear issues, projects, and cycles via the Linear GraphQL API
|
|
4
|
+
category: productivity
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
origin: aiden
|
|
7
|
+
license: Apache-2.0
|
|
8
|
+
tags: linear, issues, project-management, graphql, engineering, sprint, cycle, team, tasks
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Linear Issue Tracking
|
|
12
|
+
|
|
13
|
+
Query and mutate Linear data — issues, projects, cycles, and teams — using the Linear GraphQL API. Requires `LINEAR_API_KEY` set as an environment variable.
|
|
14
|
+
|
|
15
|
+
## When to Use
|
|
16
|
+
|
|
17
|
+
- User wants to list open issues in a Linear team or project
|
|
18
|
+
- User wants to create a new Linear issue
|
|
19
|
+
- User wants to update issue status or priority
|
|
20
|
+
- User wants to see issues assigned to them
|
|
21
|
+
- User wants to look up current cycle/sprint progress
|
|
22
|
+
|
|
23
|
+
## How to Use
|
|
24
|
+
|
|
25
|
+
### 1. Set API key
|
|
26
|
+
|
|
27
|
+
Generate a Personal API key at https://linear.app/settings/api — select `read` and `write` scopes.
|
|
28
|
+
|
|
29
|
+
```powershell
|
|
30
|
+
$env:LINEAR_API_KEY = "lin_api_..."
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 2. Run a GraphQL query helper
|
|
34
|
+
|
|
35
|
+
All Linear API calls are POST to `https://api.linear.app/graphql`.
|
|
36
|
+
|
|
37
|
+
```powershell
|
|
38
|
+
function Invoke-Linear($query, $variables = @{}) {
|
|
39
|
+
$body = @{ query = $query; variables = $variables } | ConvertTo-Json -Depth 10
|
|
40
|
+
$headers = @{ "Authorization" = $env:LINEAR_API_KEY; "Content-Type" = "application/json" }
|
|
41
|
+
(Invoke-RestMethod -Uri "https://api.linear.app/graphql" -Method Post -Headers $headers -Body $body).data
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 3. List my assigned issues
|
|
46
|
+
|
|
47
|
+
```powershell
|
|
48
|
+
$q = '{ viewer { assignedIssues(first: 20, filter: { state: { type: { in: ["started","unstarted"] } } }) { nodes { identifier title priority state { name } } } } }'
|
|
49
|
+
(Invoke-Linear $q).viewer.assignedIssues.nodes | Format-Table identifier, title, priority
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 4. List issues in a team
|
|
53
|
+
|
|
54
|
+
```powershell
|
|
55
|
+
$q = 'query($key:String!) { team(key:$key) { issues(first:30, filter:{state:{type:{in:["started","unstarted"]}}}) { nodes { identifier title assignee { name } state { name } } } } }'
|
|
56
|
+
(Invoke-Linear $q @{ key = "ENG" }).team.issues.nodes | Format-Table identifier, title
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 5. Create a new issue
|
|
60
|
+
|
|
61
|
+
```powershell
|
|
62
|
+
# First get teamId
|
|
63
|
+
$teamQ = '{ teams { nodes { id key name } } }'
|
|
64
|
+
$teams = (Invoke-Linear $teamQ).teams.nodes
|
|
65
|
+
$teamId = ($teams | Where-Object key -eq "ENG").id
|
|
66
|
+
|
|
67
|
+
$mutation = 'mutation($input:IssueCreateInput!) { issueCreate(input:$input) { issue { identifier title url } } }'
|
|
68
|
+
$vars = @{ input = @{ teamId = $teamId; title = "Fix login timeout bug"; description = "Users are being logged out after 5 min of inactivity."; priority = 2 } }
|
|
69
|
+
(Invoke-Linear $mutation $vars).issueCreate.issue
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 6. Update issue state
|
|
73
|
+
|
|
74
|
+
```powershell
|
|
75
|
+
# Get state IDs for the team first
|
|
76
|
+
$stateQ = 'query($key:String!) { team(key:$key) { states { nodes { id name } } } }'
|
|
77
|
+
$states = (Invoke-Linear $stateQ @{ key = "ENG" }).team.states.nodes
|
|
78
|
+
$doneId = ($states | Where-Object name -eq "Done").id
|
|
79
|
+
|
|
80
|
+
$mutation = 'mutation($id:String! $stateId:String!) { issueUpdate(id:$id input:{stateId:$stateId}) { issue { identifier state { name } } } }'
|
|
81
|
+
(Invoke-Linear $mutation @{ id = "ENG-42"; stateId = $doneId }).issueUpdate.issue
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 7. List current cycle
|
|
85
|
+
|
|
86
|
+
```powershell
|
|
87
|
+
$q = 'query($key:String!) { team(key:$key) { activeCycle { name startsAt endsAt progress issues(first:50) { nodes { identifier title state { name } } } } } }'
|
|
88
|
+
(Invoke-Linear $q @{ key = "ENG" }).team.activeCycle | Select-Object name, progress
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Examples
|
|
92
|
+
|
|
93
|
+
**"Show me all my open Linear issues"**
|
|
94
|
+
→ Use steps 2–3 to list viewer's assigned unstarted/in-progress issues.
|
|
95
|
+
|
|
96
|
+
**"Create a Linear issue: 'Update onboarding flow' in the PRODUCT team"**
|
|
97
|
+
→ Use step 5 — fetch team ID for PRODUCT, then create with given title.
|
|
98
|
+
|
|
99
|
+
**"What's the progress on the current sprint?"**
|
|
100
|
+
→ Use step 7 — ask user for their team key, then show activeCycle progress.
|
|
101
|
+
|
|
102
|
+
## Cautions
|
|
103
|
+
|
|
104
|
+
- Linear API keys are personal — they act as the user who created them
|
|
105
|
+
- Priority values: 0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low
|
|
106
|
+
- Issue IDs look like `ENG-42` but mutations need the UUID — always fetch UUID from the issues list
|
|
107
|
+
- GraphQL depth limit is ~6 levels — avoid deeply nested queries
|