prometheus

by openclaw

Query Prometheus monitoring data to check server metrics, resource usage, and system health. Use when the user asks about server status, disk space, CPU/memory usage, network stats, or any metrics collected by Prometheus. Supports HTTP Basic Auth via environment variables.

Scanned
Risk
Low Risk
Status
warning
Findings
1
Last Scanned
2/12/2026

Discussion

Sign in to join the discussion.

No comments yet. Be the first to share your thoughts.

Scan Report

Duration
207.9s
Rules checked
147
Scanned at
2/12/2026, 11:41:10 PM

Scanners5/5 ran

clawguard-rules
0 findings1ms
No findings — all checks passed.
View logs
clawguard-rules1ms
1[2026-02-12T23:37:42.567Z] Running @yourclaw/clawguard-rules pattern matcher
2Scanning: /tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus/SKILL.md
3Content length: 2790 chars
4Patterns matched: 0
5✓ Completed in 1ms
gitleaks
0 findings151605ms
No findings — all checks passed.
View logs
gitleaks151605ms
1[2026-02-12T23:40:14.172Z] $ gitleaks detect --source /tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus --report-format json --report-path /dev/stdout --no-git
2
3⚠ stderr output:
4
5 │╲
6 │ ○
7 ○ ░
8 ░ gitleaks
9
1011:40PM FTL Report path is not writable: /dev/stdout error="open /dev/stdout: no such device or address"
11
12Process exited with code 1
13✓ Completed in 151605ms
semgrep
0 findings207873ms
No findings — all checks passed.
View logs
semgrep207873ms
1[2026-02-12T23:41:10.443Z] $ semgrep scan --json --quiet --config auto /tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus
2{"version":"1.151.0","results":[],"errors":[],"paths":{"scanned":["/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus/SKILL.md","/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus/_meta.json","/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus/package.json","/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus/scripts/cli.js","/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus/scripts/common.js","/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus/scripts/query.js"]},"time":{"rules":[],"rules_parse_time":14.347040891647339,"profiling_times":{"config_time":17.170315742492676,"core_time":20.18515658378601,"ignores_time":0.017472267150878906,"total_time":37.43305683135986},"parsing_time":{"total_time":0.0,"per_file_time":{"mean":0.0,"std_dev":0.0},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_files":[]},"scanning_time":{"total_time":2.748607873916626,"per_file_time":{"mean":0.1616828161127427,"std_dev":0.10788524688738858},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_files":[]},"matching_time":{"total_time":0.0,"per_file_and_rule_time":{"mean":0.0,"std_dev":0.0},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_rules_on_files":[]},"tainting_time":{"total_time":0.0,"per_def_and_rule_time":{"mean":0.0,"std_dev":0.0},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_rules_on_defs":[]},"fixpoint_timeouts":[],"prefiltering":{"project_level_time":0.0,"file_level_time":0.0,"rules_with_project_prefilters_ratio":0.0,"rules_with_file_prefilters_ratio":0.9842312746386334,"rules_selected_ratio":0.04336399474375821,"rules_matched_ratio":0.04336399474375821},"targets":[],"total_bytes":0,"max_memory_bytes":1196547712},"engine_requested":"OSS","skipped_rules":[],"profiling_results":[]}
3
4Process exited with code 0
5✓ Completed in 207873ms
mcp-scan
1 finding162667ms
MCP-W004The MCP server is not in our registry.
View logs
mcp-scan162667ms
1[2026-02-12T23:40:25.241Z] $ mcp-scan --skills /tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus --json
2{
3 "/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom": {
4 "client": "not-available",
5 "path": "/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom",
6 "servers": [
7 {
8 "name": "prometheus",
9 "server": {
10 "path": "/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus",
11 "type": "skill"
12 },
13 "signature": {
14 "metadata": {
15 "meta": null,
16 "protocolVersion": "built-in",
17 "capabilities": {
18 "experimental": null,
19 "logging": null,
20 "prompts": null,
21 "resources": null,
22 "tools": {
23 "listChanged": false
24 },
25 "completions": null,
26 "tasks": null
27 },
28 "serverInfo": {
29 "name": "prometheus",
30 "title": null,
31 "version": "skills",
32 "websiteUrl": null,
33 "icons": null
34 },
35 "instructions": "Query Prometheus monitoring data to check server metrics, resource usage, and system health. Use when the user asks about server status, disk space, CPU/memory usage, network stats, or any metrics collected by Prometheus. Supports HTTP Basic Auth via environment variables.",
36 "prompts": {
37 "listChanged": false
38 },
39 "resources": {
40 "subscribe": null,
41 "listChanged": false
42 }
43 },
44 "prompts": [
45 {
46 "name": "SKILL.md",
47 "title": null,
48 "description": "\n\n# Prometheus Skill\n\nQuery Prometheus monitoring data to get insights about your infrastructure.\n\n## Environment Variables\n\nSet in `.env` file:\n- `PROMETHEUS_URL` - Prometheus server URL (e.g., `http://localhost:9090`)\n- `PROMETHEUS_USER` - HTTP Basic Auth username (optional)\n- `PROMETHEUS_PASSWORD` - HTTP Basic Auth password (optional)\n\n## Usage\n\n### Query Metrics\n\nUse the CLI to run PromQL queries:\n\n```bash\nsource .env && node scripts/cli.js query '<promql_query>'\n```\n\n### Common Examples\n\n**Disk space usage:**\n```bash\nnode scripts/cli.js query '100 - (node_filesystem_avail_bytes / node_filesystem_size_bytes * 100)'\n```\n\n**CPU usage:**\n```bash\nnode scripts/cli.js query '100 - (avg by (instance) (irate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)'\n```\n\n**Memory usage:**\n```bash\nnode scripts/cli.js query '(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100'\n```\n\n**Load average:**\n```bash\nnode scripts/cli.js query 'node_load1'\n```\n\n### List Metrics\n\nFind available metrics matching a pattern:\n\n```bash\nnode scripts/cli.js metrics 'node_memory_*'\n```\n\n### Series Discovery\n\nFind time series by label selectors:\n\n```bash\nnode scripts/cli.js series '{__name__=~\"node_cpu_.*\", instance=~\".*:9100\"}'\n```\n\n### Get Labels\n\nList label names:\n\n```bash\nnode scripts/cli.js labels\n```\n\nList values for a specific label:\n\n```bash\nnode scripts/cli.js label-values instance\n```\n\n## Output Format\n\nAll commands output JSON for easy parsing. Use `jq` for pretty printing:\n\n```bash\nnode scripts/cli.js query 'up' | jq .\n```\n\n## Common Queries Reference\n\n| Metric | PromQL Query |\n|--------|--------------|\n| Disk free % | `node_filesystem_avail_bytes / node_filesystem_size_bytes * 100` |\n| Disk used % | `100 - (node_filesystem_avail_bytes / node_filesystem_size_bytes * 100)` |\n| CPU idle % | `avg by (instance) (irate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100` |\n| Memory used % | `(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100` |\n| Network RX | `rate(node_network_receive_bytes_total[5m])` |\n| Network TX | `rate(node_network_transmit_bytes_total[5m])` |\n| Uptime | `node_time_seconds - node_boot_time_seconds` |\n| Service up | `up` |\n\n## Notes\n\n- Time range defaults to last 1 hour for instant queries\n- Use range queries `[5m]` for rate calculations\n- All queries return JSON with `data.result` containing the results\n- Instance labels typically show `host:port` format\n",
49 "arguments": [],
50 "icons": null,
51 "meta": null
52 }
53 ],
54 "resources": [
55 {
56 "name": "package.json",
57 "title": null,
58 "uri": "skill://package.json",
59 "description": "{\n \"name\": \"prometheus-cli\",\n \"version\": \"1.0.0\",\n \"description\": \"CLI for querying Prometheus monitoring data\",\n \"type\": \"module\",\n \"engines\": {\n \"node\": \">=18.0.0\"\n },\n \"scripts\": {\n \"query\": \"node scripts/cli.js query\",\n \"labels\": \"node scripts/cli.js labels\",\n \"alerts\": \"node scripts/cli.js alerts\",\n \"targets\": \"node scripts/cli.js targets\"\n }\n}\n",
60 "mimeType": null,
61 "size": null,
62 "icons": null,
63 "annotations": null,
64 "meta": null
65 },
66 {
67 "name": "_meta.json",
68 "title": null,
69 "uri": "skill://_meta.json",
70 "description": "{\n \"owner\": \"akellacom\",\n \"slug\": \"prometheus\",\n \"displayName\": \"Prometheus\",\n \"latest\": {\n \"version\": \"1.0.0\",\n \"publishedAt\": 1770680251127,\n \"commit\": \"https://github.com/openclaw/skills/commit/6567ada814180129bf375a7c6847e593e6089101\"\n },\n \"history\": []\n}\n",
71 "mimeType": null,
72 "size": null,
73 "icons": null,
74 "annotations": null,
75 "meta": null
76 }
77 ],
78 "resource_templates": [],
79 "tools": [
80 {
81 "name": "cli.js",
82 "title": null,
83 "description": "Script: cli.js. Code:\n#!/usr/bin/env node\n\n/**\n * Prometheus CLI - Query Prometheus monitoring data\n * \n * Usage:\n * node cli.js query '<promql>' - Run instant query\n * node cli.js range '<promql>' <start> <end> - Run range query\n * node cli.js labels - List label names\n * node cli.js label-values <label> - Get values for label\n * node cli.js series '<selector>' - Find time series\n * node cli.js metrics [pattern] - Get metrics metadata\n * node cli.js alerts - Get active alerts\n * node cli.js targets - Get scrape targets\n * \n * Environment variables:\n * PROMETHEUS_URL - Prometheus server URL (required)\n * PROMETHEUS_USER - HTTP Basic Auth username (optional)\n * PROMETHEUS_PASSWORD - HTTP Basic Auth password (optional)\n */\n\nimport { \n instantQuery, \n rangeQuery, \n getLabels, \n getLabelValues, \n getSeries, \n getMetadata,\n getAlerts,\n getTargets\n} from './query.js';\n\nconst command = process.argv[2];\n\nfunction output(data) {\n console.log(JSON.stringify(data, null, 2));\n}\n\nfunction errorExit(message) {\n console.error(`Error: ${message}`);\n process.exit(1);\n}\n\nasync function main() {\n try {\n switch (command) {\n case 'query':\n case 'q': {\n const queryStr = process.argv[3];\n if (!queryStr) errorExit('Query string required');\n const result = await instantQuery(queryStr);\n output(result);\n break;\n }\n\n case 'range':\n case 'r': {\n const queryStr = process.argv[3];\n const start = process.argv[4];\n const end = process.argv[5];\n const step = parseInt(process.argv[6]) || 60;\n if (!queryStr || !start || !end) {\n errorExit('Usage: range <query> <start> <end> [step]');\n }\n const result = await rangeQuery(queryStr, start, end, step);\n output(result);\n break;\n }\n\n case 'labels':\n case 'l': {\n const result = await getLabels();\n output(result);\n break;\n }\n\n case 'label-values':\n case 'lv': {\n const label = process.argv[3];\n if (!label) errorExit('Label name required');\n const result = await getLabelValues(label);\n output(result);\n break;\n }\n\n case 'series':\n case 's': {\n const match = process.argv[3];\n if (!match) errorExit('Series selector required (e.g., \\'{__name__=\\\"up\\\"}\\')');\n const result = await getSeries(match);\n output(result);\n break;\n }\n\n case 'metrics':\n case 'm': {\n const pattern = process.argv[3] || '';\n const result = await getMetadata(pattern);\n output(result);\n break;\n }\n\n case 'alerts':\n case 'a': {\n const result = await getAlerts();\n output(result);\n break;\n }\n\n case 'targets':\n case 't': {\n const result = await getTargets();\n output(result);\n break;\n }\n\n case 'help':\n case 'h':\n case '--help':\n case '-h':\n default: {\n console.log(`\nPrometheus CLI - Query Prometheus monitoring data\n\nCommands:\n query <promql> Run instant query\n range <q> <start> <end> Run range query (timestamps in RFC3339 or Unix)\n labels List all label names\n label-values <label> Get values for a specific label\n series <selector> Find time series by label matchers\n metrics [pattern] Get metrics metadata\n alerts Get active alerts\n targets Get scrape targets\n\nEnvironment variables:\n PROMETHEUS_URL Prometheus server URL (required)\n PROMETHEUS_USER HTTP Basic Auth username (optional)\n PROMETHEUS_PASSWORD HTTP Basic Auth password (optional)\n\nExamples:\n node cli.js query 'up'\n node cli.js query '100 - (node_filesystem_avail_bytes / node_filesystem_size_bytes * 100)'\n node cli.js labels\n node cli.js label-values instance\n node cli.js series '{__name__=~\"node_cpu_.*\"}'\n`);\n break;\n }\n }\n } catch (err) {\n errorExit(err.message);\n }\n}\n\nmain();\n",
84 "inputSchema": {},
85 "outputSchema": null,
86 "icons": null,
87 "annotations": null,
88 "meta": null,
89 "execution": null
90 },
91 {
92 "name": "common.js",
93 "title": null,
94 "description": "Script: common.js. Code:\n/**\n * Common utilities for Prometheus API client\n */\n\n/**\n * Get environment variable with optional default\n * @param {string} name - Environment variable name\n * @param {string} defaultValue - Default value if not set\n * @returns {string}\n */\nexport function getEnv(name, defaultValue = '') {\n return process.env[name] || defaultValue;\n}\n\n/**\n * Get Prometheus base URL from environment\n * @returns {string}\n */\nexport function getPrometheusUrl() {\n const url = getEnv('PROMETHEUS_URL');\n if (!url) {\n throw new Error('PROMETHEUS_URL environment variable is required');\n }\n return url.replace(/\\/$/, ''); // Remove trailing slash\n}\n\n/**\n * Create authentication headers if credentials are provided\n * @returns {Object} Headers object\n */\nexport function createAuthHeader() {\n const headers = {\n 'Accept': 'application/json'\n };\n \n const user = getEnv('PROMETHEUS_USER');\n const password = getEnv('PROMETHEUS_PASSWORD');\n \n if (user && password) {\n const auth = Buffer.from(`${user}:${password}`).toString('base64');\n headers['Authorization'] = `Basic ${auth}`;\n }\n \n return headers;\n}\n\n/**\n * Build full URL for API endpoint\n * @param {string} path - API path\n * @returns {string} Full URL\n */\nexport function buildUrl(path) {\n return `${getPrometheusUrl()}${path}`;\n}\n\n/**\n * Handle API response and parse JSON\n * @param {Response} response - Fetch response\n * @returns {Promise<any>} Parsed response data\n */\nexport async function handleResponse(response) {\n if (!response.ok) {\n const text = await response.text().catch(() => 'Unknown error');\n throw new Error(`HTTP ${response.status}: ${text}`);\n }\n \n const data = await response.json();\n \n if (data.status !== 'success') {\n throw new Error(`Prometheus error: ${data.error || 'Unknown error'}`);\n }\n \n return data.data;\n}\n\n/**\n * Format bytes to human readable string\n * @param {number} bytes - Bytes to format\n * @returns {string} Formatted string\n */\nexport function formatBytes(bytes) {\n if (bytes === 0) return '0 B';\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];\n}\n\n/**\n * Format seconds to human readable duration\n * @param {number} seconds - Seconds to format\n * @returns {string} Formatted string\n */\nexport function formatDuration(seconds) {\n const days = Math.floor(seconds / 86400);\n const hours = Math.floor((seconds % 86400) / 3600);\n const mins = Math.floor((seconds % 3600) / 60);\n \n if (days > 0) return `${days}d ${hours}h`;\n if (hours > 0) return `${hours}h ${mins}m`;\n return `${mins}m`;\n}\n",
95 "inputSchema": {},
96 "outputSchema": null,
97 "icons": null,
98 "annotations": null,
99 "meta": null,
100 "execution": null
101 },
102 {
103 "name": "query.js",
104 "title": null,
105 "description": "Script: query.js. Code:\nimport { getEnv, createAuthHeader, handleResponse, buildUrl } from './common.js';\n\n/**\n * Execute instant query against Prometheus\n * @param {string} query - PromQL query string\n * @param {string} time - Optional Unix timestamp\n * @returns {Promise<Object>} Query result\n */\nexport async function instantQuery(query, time = null) {\n const url = buildUrl('/api/v1/query');\n const params = new URLSearchParams({ query });\n if (time) params.append('time', time);\n \n const response = await fetch(`${url}?${params}`, {\n headers: createAuthHeader()\n });\n \n return handleResponse(response);\n}\n\n/**\n * Execute range query against Prometheus\n * @param {string} query - PromQL query string\n * @param {string} start - Start time (RFC3339 or Unix timestamp)\n * @param {string} end - End time (RFC3339 or Unix timestamp)\n * @param {number} step - Query resolution step width in seconds\n * @returns {Promise<Object>} Query result\n */\nexport async function rangeQuery(query, start, end, step = 60) {\n const url = buildUrl('/api/v1/query_range');\n const params = new URLSearchParams({ query, start, end, step: String(step) });\n \n const response = await fetch(`${url}?${params}`, {\n headers: createAuthHeader()\n });\n \n return handleResponse(response);\n}\n\n/**\n * Get label names\n * @returns {Promise<string[]>} Array of label names\n */\nexport async function getLabels() {\n const url = buildUrl('/api/v1/labels');\n \n const response = await fetch(url, {\n headers: createAuthHeader()\n });\n \n return handleResponse(response);\n}\n\n/**\n * Get values for a specific label\n * @param {string} label - Label name\n * @returns {Promise<string[]>} Array of label values\n */\nexport async function getLabelValues(label) {\n const url = buildUrl(`/api/v1/label/${encodeURIComponent(label)}/values`);\n \n const response = await fetch(url, {\n headers: createAuthHeader()\n });\n \n return handleResponse(response);\n}\n\n/**\n * Find time series by label matchers\n * @param {string} match - Series selector (e.g., '{__name__=\"up\"}')\n * @param {string} start - Optional start time\n * @param {string} end - Optional end time\n * @returns {Promise<Object[]>} Array of series\n */\nexport async function getSeries(match, start = null, end = null) {\n const url = buildUrl('/api/v1/series');\n const params = new URLSearchParams();\n params.append('match[]', match);\n if (start) params.append('start', start);\n if (end) params.append('end', end);\n \n const response = await fetch(`${url}?${params}`, {\n headers: createAuthHeader()\n });\n \n return handleResponse(response);\n}\n\n/**\n * Get metadata about metrics\n * @param {string} metric - Optional metric name to filter\n * @returns {Promise<Object>} Metrics metadata\n */\nexport async function getMetadata(metric = '') {\n const url = buildUrl('/api/v1/metadata');\n const params = metric ? `?metric=${encodeURIComponent(metric)}` : '';\n \n const response = await fetch(`${url}${params}`, {\n headers: createAuthHeader()\n });\n \n return handleResponse(response);\n}\n\n/**\n * Get current alerts\n * @returns {Promise<Object>} Active alerts\n */\nexport async function getAlerts() {\n const url = buildUrl('/api/v1/alerts');\n \n const response = await fetch(url, {\n headers: createAuthHeader()\n });\n \n return handleResponse(response);\n}\n\n/**\n * Get target discovery status\n * @returns {Promise<Object>} Scrape targets\n */\nexport async function getTargets() {\n const url = buildUrl('/api/v1/targets');\n \n const response = await fetch(url, {\n headers: createAuthHeader()\n });\n \n return handleResponse(response);\n}\n",
106 "inputSchema": {},
107 "outputSchema": null,
108 "icons": null,
109 "annotations": null,
110 "meta": null,
111 "execution": null
112 }
113 ]
114 },
115 "error": null
116 }
117 ],
118 "issues": [
119 {
120 "code": "W004",
121 "message": "The MCP server is not in our registry.",
122 "reference": [
123 0,
124 null
125 ],
126 "extra_data": null
127 }
128 ],
129 "labels": [
130 [
131 {
132 "is_public_sink": 0,
133 "destructive": 0,
134 "untrusted_content": 0,
135 "private_data": 0
136 },
137 {
138 "is_public_sink": 0,
139 "destructive": 0,
140 "untrusted_content": 0,
141 "private_data": 0
142 },
143 {
144 "is_public_sink": 0,
145 "destructive": 0,
146 "untrusted_content": 0,
147 "private_data": 0
148 },
149 {
150 "is_public_sink": 0,
151 "destructive": 0,
152 "untrusted_content": 0,
153 "private_data": 0
154 },
155 {
156 "is_public_sink": 0,
157 "destructive": 0,
158 "untrusted_content": 0,
159 "private_data": 0
160 },
161 {
162 "is_public_sink": 0,
163 "destructive": 0,
164 "untrusted_content": 0,
165 "private_data": 0
166 }
167 ]
168 ],
169 "error": null
170 }
171}
172
173Process exited with code 0
174✓ Completed in 162667ms
npm-audit
0 findings145376ms
No findings — all checks passed.
View logs
npm-audit145376ms
1[2026-02-12T23:40:07.953Z] $ npm audit --json --prefix /tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus
2{
3 "error": {
4 "code": "ENOLOCK",
5 "summary": "This command requires an existing lockfile.",
6 "detail": "Try creating one first with: npm i --package-lock-only\nOriginal error: loadVirtual requires existing shrinkwrap file"
7 }
8}
9
10⚠ stderr output:
11npm error code ENOLOCK
12npm error audit This command requires an existing lockfile.
13npm error audit Try creating one first with: npm i --package-lock-only
14npm error audit Original error: loadVirtual requires existing shrinkwrap file
15npm error A complete log of this run can be found in: /home/runner/.npm/_logs/2026-02-12T23_40_01_136Z-debug-0.log
16
17Process exited with code 1
18✓ Completed in 145376ms

Files analyzed

SKILL.mdcli.jscommon.jsquery.js

Rules coverage147 patterns

58
prompt injection
15
secrets
53
malware
21
permissions

Security Findings

LowMCP-W004mcp-scanmcp

The MCP server is not in our registry.

Scan History2 scans

Warning
1 finding
0
critical
0
high
0
medium
1
low
0
info

Scanners5/5 ran

clawguard-rules
0 findings1ms
No findings — all checks passed.
View logs
clawguard-rules1ms
1[2026-02-12T23:37:42.567Z] Running @yourclaw/clawguard-rules pattern matcher
2Scanning: /tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus/SKILL.md
3Content length: 2790 chars
4Patterns matched: 0
5✓ Completed in 1ms
gitleaks
0 findings151605ms
No findings — all checks passed.
View logs
gitleaks151605ms
1[2026-02-12T23:40:14.172Z] $ gitleaks detect --source /tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus --report-format json --report-path /dev/stdout --no-git
2
3⚠ stderr output:
4
5 │╲
6 │ ○
7 ○ ░
8 ░ gitleaks
9
1011:40PM FTL Report path is not writable: /dev/stdout error="open /dev/stdout: no such device or address"
11
12Process exited with code 1
13✓ Completed in 151605ms
semgrep
0 findings207873ms
No findings — all checks passed.
View logs
semgrep207873ms
1[2026-02-12T23:41:10.443Z] $ semgrep scan --json --quiet --config auto /tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus
2{"version":"1.151.0","results":[],"errors":[],"paths":{"scanned":["/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus/SKILL.md","/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus/_meta.json","/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus/package.json","/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus/scripts/cli.js","/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus/scripts/common.js","/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus/scripts/query.js"]},"time":{"rules":[],"rules_parse_time":14.347040891647339,"profiling_times":{"config_time":17.170315742492676,"core_time":20.18515658378601,"ignores_time":0.017472267150878906,"total_time":37.43305683135986},"parsing_time":{"total_time":0.0,"per_file_time":{"mean":0.0,"std_dev":0.0},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_files":[]},"scanning_time":{"total_time":2.748607873916626,"per_file_time":{"mean":0.1616828161127427,"std_dev":0.10788524688738858},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_files":[]},"matching_time":{"total_time":0.0,"per_file_and_rule_time":{"mean":0.0,"std_dev":0.0},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_rules_on_files":[]},"tainting_time":{"total_time":0.0,"per_def_and_rule_time":{"mean":0.0,"std_dev":0.0},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_rules_on_defs":[]},"fixpoint_timeouts":[],"prefiltering":{"project_level_time":0.0,"file_level_time":0.0,"rules_with_project_prefilters_ratio":0.0,"rules_with_file_prefilters_ratio":0.9842312746386334,"rules_selected_ratio":0.04336399474375821,"rules_matched_ratio":0.04336399474375821},"targets":[],"total_bytes":0,"max_memory_bytes":1196547712},"engine_requested":"OSS","skipped_rules":[],"profiling_results":[]}
3
4Process exited with code 0
5✓ Completed in 207873ms
mcp-scan
1 finding162667ms
MCP-W004The MCP server is not in our registry.
View logs
mcp-scan162667ms
1[2026-02-12T23:40:25.241Z] $ mcp-scan --skills /tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus --json
2{
3 "/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom": {
4 "client": "not-available",
5 "path": "/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom",
6 "servers": [
7 {
8 "name": "prometheus",
9 "server": {
10 "path": "/tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus",
11 "type": "skill"
12 },
13 "signature": {
14 "metadata": {
15 "meta": null,
16 "protocolVersion": "built-in",
17 "capabilities": {
18 "experimental": null,
19 "logging": null,
20 "prompts": null,
21 "resources": null,
22 "tools": {
23 "listChanged": false
24 },
25 "completions": null,
26 "tasks": null
27 },
28 "serverInfo": {
29 "name": "prometheus",
30 "title": null,
31 "version": "skills",
32 "websiteUrl": null,
33 "icons": null
34 },
35 "instructions": "Query Prometheus monitoring data to check server metrics, resource usage, and system health. Use when the user asks about server status, disk space, CPU/memory usage, network stats, or any metrics collected by Prometheus. Supports HTTP Basic Auth via environment variables.",
36 "prompts": {
37 "listChanged": false
38 },
39 "resources": {
40 "subscribe": null,
41 "listChanged": false
42 }
43 },
44 "prompts": [
45 {
46 "name": "SKILL.md",
47 "title": null,
48 "description": "\n\n# Prometheus Skill\n\nQuery Prometheus monitoring data to get insights about your infrastructure.\n\n## Environment Variables\n\nSet in `.env` file:\n- `PROMETHEUS_URL` - Prometheus server URL (e.g., `http://localhost:9090`)\n- `PROMETHEUS_USER` - HTTP Basic Auth username (optional)\n- `PROMETHEUS_PASSWORD` - HTTP Basic Auth password (optional)\n\n## Usage\n\n### Query Metrics\n\nUse the CLI to run PromQL queries:\n\n```bash\nsource .env && node scripts/cli.js query '<promql_query>'\n```\n\n### Common Examples\n\n**Disk space usage:**\n```bash\nnode scripts/cli.js query '100 - (node_filesystem_avail_bytes / node_filesystem_size_bytes * 100)'\n```\n\n**CPU usage:**\n```bash\nnode scripts/cli.js query '100 - (avg by (instance) (irate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)'\n```\n\n**Memory usage:**\n```bash\nnode scripts/cli.js query '(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100'\n```\n\n**Load average:**\n```bash\nnode scripts/cli.js query 'node_load1'\n```\n\n### List Metrics\n\nFind available metrics matching a pattern:\n\n```bash\nnode scripts/cli.js metrics 'node_memory_*'\n```\n\n### Series Discovery\n\nFind time series by label selectors:\n\n```bash\nnode scripts/cli.js series '{__name__=~\"node_cpu_.*\", instance=~\".*:9100\"}'\n```\n\n### Get Labels\n\nList label names:\n\n```bash\nnode scripts/cli.js labels\n```\n\nList values for a specific label:\n\n```bash\nnode scripts/cli.js label-values instance\n```\n\n## Output Format\n\nAll commands output JSON for easy parsing. Use `jq` for pretty printing:\n\n```bash\nnode scripts/cli.js query 'up' | jq .\n```\n\n## Common Queries Reference\n\n| Metric | PromQL Query |\n|--------|--------------|\n| Disk free % | `node_filesystem_avail_bytes / node_filesystem_size_bytes * 100` |\n| Disk used % | `100 - (node_filesystem_avail_bytes / node_filesystem_size_bytes * 100)` |\n| CPU idle % | `avg by (instance) (irate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100` |\n| Memory used % | `(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100` |\n| Network RX | `rate(node_network_receive_bytes_total[5m])` |\n| Network TX | `rate(node_network_transmit_bytes_total[5m])` |\n| Uptime | `node_time_seconds - node_boot_time_seconds` |\n| Service up | `up` |\n\n## Notes\n\n- Time range defaults to last 1 hour for instant queries\n- Use range queries `[5m]` for rate calculations\n- All queries return JSON with `data.result` containing the results\n- Instance labels typically show `host:port` format\n",
49 "arguments": [],
50 "icons": null,
51 "meta": null
52 }
53 ],
54 "resources": [
55 {
56 "name": "package.json",
57 "title": null,
58 "uri": "skill://package.json",
59 "description": "{\n \"name\": \"prometheus-cli\",\n \"version\": \"1.0.0\",\n \"description\": \"CLI for querying Prometheus monitoring data\",\n \"type\": \"module\",\n \"engines\": {\n \"node\": \">=18.0.0\"\n },\n \"scripts\": {\n \"query\": \"node scripts/cli.js query\",\n \"labels\": \"node scripts/cli.js labels\",\n \"alerts\": \"node scripts/cli.js alerts\",\n \"targets\": \"node scripts/cli.js targets\"\n }\n}\n",
60 "mimeType": null,
61 "size": null,
62 "icons": null,
63 "annotations": null,
64 "meta": null
65 },
66 {
67 "name": "_meta.json",
68 "title": null,
69 "uri": "skill://_meta.json",
70 "description": "{\n \"owner\": \"akellacom\",\n \"slug\": \"prometheus\",\n \"displayName\": \"Prometheus\",\n \"latest\": {\n \"version\": \"1.0.0\",\n \"publishedAt\": 1770680251127,\n \"commit\": \"https://github.com/openclaw/skills/commit/6567ada814180129bf375a7c6847e593e6089101\"\n },\n \"history\": []\n}\n",
71 "mimeType": null,
72 "size": null,
73 "icons": null,
74 "annotations": null,
75 "meta": null
76 }
77 ],
78 "resource_templates": [],
79 "tools": [
80 {
81 "name": "cli.js",
82 "title": null,
83 "description": "Script: cli.js. Code:\n#!/usr/bin/env node\n\n/**\n * Prometheus CLI - Query Prometheus monitoring data\n * \n * Usage:\n * node cli.js query '<promql>' - Run instant query\n * node cli.js range '<promql>' <start> <end> - Run range query\n * node cli.js labels - List label names\n * node cli.js label-values <label> - Get values for label\n * node cli.js series '<selector>' - Find time series\n * node cli.js metrics [pattern] - Get metrics metadata\n * node cli.js alerts - Get active alerts\n * node cli.js targets - Get scrape targets\n * \n * Environment variables:\n * PROMETHEUS_URL - Prometheus server URL (required)\n * PROMETHEUS_USER - HTTP Basic Auth username (optional)\n * PROMETHEUS_PASSWORD - HTTP Basic Auth password (optional)\n */\n\nimport { \n instantQuery, \n rangeQuery, \n getLabels, \n getLabelValues, \n getSeries, \n getMetadata,\n getAlerts,\n getTargets\n} from './query.js';\n\nconst command = process.argv[2];\n\nfunction output(data) {\n console.log(JSON.stringify(data, null, 2));\n}\n\nfunction errorExit(message) {\n console.error(`Error: ${message}`);\n process.exit(1);\n}\n\nasync function main() {\n try {\n switch (command) {\n case 'query':\n case 'q': {\n const queryStr = process.argv[3];\n if (!queryStr) errorExit('Query string required');\n const result = await instantQuery(queryStr);\n output(result);\n break;\n }\n\n case 'range':\n case 'r': {\n const queryStr = process.argv[3];\n const start = process.argv[4];\n const end = process.argv[5];\n const step = parseInt(process.argv[6]) || 60;\n if (!queryStr || !start || !end) {\n errorExit('Usage: range <query> <start> <end> [step]');\n }\n const result = await rangeQuery(queryStr, start, end, step);\n output(result);\n break;\n }\n\n case 'labels':\n case 'l': {\n const result = await getLabels();\n output(result);\n break;\n }\n\n case 'label-values':\n case 'lv': {\n const label = process.argv[3];\n if (!label) errorExit('Label name required');\n const result = await getLabelValues(label);\n output(result);\n break;\n }\n\n case 'series':\n case 's': {\n const match = process.argv[3];\n if (!match) errorExit('Series selector required (e.g., \\'{__name__=\\\"up\\\"}\\')');\n const result = await getSeries(match);\n output(result);\n break;\n }\n\n case 'metrics':\n case 'm': {\n const pattern = process.argv[3] || '';\n const result = await getMetadata(pattern);\n output(result);\n break;\n }\n\n case 'alerts':\n case 'a': {\n const result = await getAlerts();\n output(result);\n break;\n }\n\n case 'targets':\n case 't': {\n const result = await getTargets();\n output(result);\n break;\n }\n\n case 'help':\n case 'h':\n case '--help':\n case '-h':\n default: {\n console.log(`\nPrometheus CLI - Query Prometheus monitoring data\n\nCommands:\n query <promql> Run instant query\n range <q> <start> <end> Run range query (timestamps in RFC3339 or Unix)\n labels List all label names\n label-values <label> Get values for a specific label\n series <selector> Find time series by label matchers\n metrics [pattern] Get metrics metadata\n alerts Get active alerts\n targets Get scrape targets\n\nEnvironment variables:\n PROMETHEUS_URL Prometheus server URL (required)\n PROMETHEUS_USER HTTP Basic Auth username (optional)\n PROMETHEUS_PASSWORD HTTP Basic Auth password (optional)\n\nExamples:\n node cli.js query 'up'\n node cli.js query '100 - (node_filesystem_avail_bytes / node_filesystem_size_bytes * 100)'\n node cli.js labels\n node cli.js label-values instance\n node cli.js series '{__name__=~\"node_cpu_.*\"}'\n`);\n break;\n }\n }\n } catch (err) {\n errorExit(err.message);\n }\n}\n\nmain();\n",
84 "inputSchema": {},
85 "outputSchema": null,
86 "icons": null,
87 "annotations": null,
88 "meta": null,
89 "execution": null
90 },
91 {
92 "name": "common.js",
93 "title": null,
94 "description": "Script: common.js. Code:\n/**\n * Common utilities for Prometheus API client\n */\n\n/**\n * Get environment variable with optional default\n * @param {string} name - Environment variable name\n * @param {string} defaultValue - Default value if not set\n * @returns {string}\n */\nexport function getEnv(name, defaultValue = '') {\n return process.env[name] || defaultValue;\n}\n\n/**\n * Get Prometheus base URL from environment\n * @returns {string}\n */\nexport function getPrometheusUrl() {\n const url = getEnv('PROMETHEUS_URL');\n if (!url) {\n throw new Error('PROMETHEUS_URL environment variable is required');\n }\n return url.replace(/\\/$/, ''); // Remove trailing slash\n}\n\n/**\n * Create authentication headers if credentials are provided\n * @returns {Object} Headers object\n */\nexport function createAuthHeader() {\n const headers = {\n 'Accept': 'application/json'\n };\n \n const user = getEnv('PROMETHEUS_USER');\n const password = getEnv('PROMETHEUS_PASSWORD');\n \n if (user && password) {\n const auth = Buffer.from(`${user}:${password}`).toString('base64');\n headers['Authorization'] = `Basic ${auth}`;\n }\n \n return headers;\n}\n\n/**\n * Build full URL for API endpoint\n * @param {string} path - API path\n * @returns {string} Full URL\n */\nexport function buildUrl(path) {\n return `${getPrometheusUrl()}${path}`;\n}\n\n/**\n * Handle API response and parse JSON\n * @param {Response} response - Fetch response\n * @returns {Promise<any>} Parsed response data\n */\nexport async function handleResponse(response) {\n if (!response.ok) {\n const text = await response.text().catch(() => 'Unknown error');\n throw new Error(`HTTP ${response.status}: ${text}`);\n }\n \n const data = await response.json();\n \n if (data.status !== 'success') {\n throw new Error(`Prometheus error: ${data.error || 'Unknown error'}`);\n }\n \n return data.data;\n}\n\n/**\n * Format bytes to human readable string\n * @param {number} bytes - Bytes to format\n * @returns {string} Formatted string\n */\nexport function formatBytes(bytes) {\n if (bytes === 0) return '0 B';\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];\n}\n\n/**\n * Format seconds to human readable duration\n * @param {number} seconds - Seconds to format\n * @returns {string} Formatted string\n */\nexport function formatDuration(seconds) {\n const days = Math.floor(seconds / 86400);\n const hours = Math.floor((seconds % 86400) / 3600);\n const mins = Math.floor((seconds % 3600) / 60);\n \n if (days > 0) return `${days}d ${hours}h`;\n if (hours > 0) return `${hours}h ${mins}m`;\n return `${mins}m`;\n}\n",
95 "inputSchema": {},
96 "outputSchema": null,
97 "icons": null,
98 "annotations": null,
99 "meta": null,
100 "execution": null
101 },
102 {
103 "name": "query.js",
104 "title": null,
105 "description": "Script: query.js. Code:\nimport { getEnv, createAuthHeader, handleResponse, buildUrl } from './common.js';\n\n/**\n * Execute instant query against Prometheus\n * @param {string} query - PromQL query string\n * @param {string} time - Optional Unix timestamp\n * @returns {Promise<Object>} Query result\n */\nexport async function instantQuery(query, time = null) {\n const url = buildUrl('/api/v1/query');\n const params = new URLSearchParams({ query });\n if (time) params.append('time', time);\n \n const response = await fetch(`${url}?${params}`, {\n headers: createAuthHeader()\n });\n \n return handleResponse(response);\n}\n\n/**\n * Execute range query against Prometheus\n * @param {string} query - PromQL query string\n * @param {string} start - Start time (RFC3339 or Unix timestamp)\n * @param {string} end - End time (RFC3339 or Unix timestamp)\n * @param {number} step - Query resolution step width in seconds\n * @returns {Promise<Object>} Query result\n */\nexport async function rangeQuery(query, start, end, step = 60) {\n const url = buildUrl('/api/v1/query_range');\n const params = new URLSearchParams({ query, start, end, step: String(step) });\n \n const response = await fetch(`${url}?${params}`, {\n headers: createAuthHeader()\n });\n \n return handleResponse(response);\n}\n\n/**\n * Get label names\n * @returns {Promise<string[]>} Array of label names\n */\nexport async function getLabels() {\n const url = buildUrl('/api/v1/labels');\n \n const response = await fetch(url, {\n headers: createAuthHeader()\n });\n \n return handleResponse(response);\n}\n\n/**\n * Get values for a specific label\n * @param {string} label - Label name\n * @returns {Promise<string[]>} Array of label values\n */\nexport async function getLabelValues(label) {\n const url = buildUrl(`/api/v1/label/${encodeURIComponent(label)}/values`);\n \n const response = await fetch(url, {\n headers: createAuthHeader()\n });\n \n return handleResponse(response);\n}\n\n/**\n * Find time series by label matchers\n * @param {string} match - Series selector (e.g., '{__name__=\"up\"}')\n * @param {string} start - Optional start time\n * @param {string} end - Optional end time\n * @returns {Promise<Object[]>} Array of series\n */\nexport async function getSeries(match, start = null, end = null) {\n const url = buildUrl('/api/v1/series');\n const params = new URLSearchParams();\n params.append('match[]', match);\n if (start) params.append('start', start);\n if (end) params.append('end', end);\n \n const response = await fetch(`${url}?${params}`, {\n headers: createAuthHeader()\n });\n \n return handleResponse(response);\n}\n\n/**\n * Get metadata about metrics\n * @param {string} metric - Optional metric name to filter\n * @returns {Promise<Object>} Metrics metadata\n */\nexport async function getMetadata(metric = '') {\n const url = buildUrl('/api/v1/metadata');\n const params = metric ? `?metric=${encodeURIComponent(metric)}` : '';\n \n const response = await fetch(`${url}${params}`, {\n headers: createAuthHeader()\n });\n \n return handleResponse(response);\n}\n\n/**\n * Get current alerts\n * @returns {Promise<Object>} Active alerts\n */\nexport async function getAlerts() {\n const url = buildUrl('/api/v1/alerts');\n \n const response = await fetch(url, {\n headers: createAuthHeader()\n });\n \n return handleResponse(response);\n}\n\n/**\n * Get target discovery status\n * @returns {Promise<Object>} Scrape targets\n */\nexport async function getTargets() {\n const url = buildUrl('/api/v1/targets');\n \n const response = await fetch(url, {\n headers: createAuthHeader()\n });\n \n return handleResponse(response);\n}\n",
106 "inputSchema": {},
107 "outputSchema": null,
108 "icons": null,
109 "annotations": null,
110 "meta": null,
111 "execution": null
112 }
113 ]
114 },
115 "error": null
116 }
117 ],
118 "issues": [
119 {
120 "code": "W004",
121 "message": "The MCP server is not in our registry.",
122 "reference": [
123 0,
124 null
125 ],
126 "extra_data": null
127 }
128 ],
129 "labels": [
130 [
131 {
132 "is_public_sink": 0,
133 "destructive": 0,
134 "untrusted_content": 0,
135 "private_data": 0
136 },
137 {
138 "is_public_sink": 0,
139 "destructive": 0,
140 "untrusted_content": 0,
141 "private_data": 0
142 },
143 {
144 "is_public_sink": 0,
145 "destructive": 0,
146 "untrusted_content": 0,
147 "private_data": 0
148 },
149 {
150 "is_public_sink": 0,
151 "destructive": 0,
152 "untrusted_content": 0,
153 "private_data": 0
154 },
155 {
156 "is_public_sink": 0,
157 "destructive": 0,
158 "untrusted_content": 0,
159 "private_data": 0
160 },
161 {
162 "is_public_sink": 0,
163 "destructive": 0,
164 "untrusted_content": 0,
165 "private_data": 0
166 }
167 ]
168 ],
169 "error": null
170 }
171}
172
173Process exited with code 0
174✓ Completed in 162667ms
npm-audit
0 findings145376ms
No findings — all checks passed.
View logs
npm-audit145376ms
1[2026-02-12T23:40:07.953Z] $ npm audit --json --prefix /tmp/clawguard-scan-DeYfvE/repo/skills/akellacom/prometheus
2{
3 "error": {
4 "code": "ENOLOCK",
5 "summary": "This command requires an existing lockfile.",
6 "detail": "Try creating one first with: npm i --package-lock-only\nOriginal error: loadVirtual requires existing shrinkwrap file"
7 }
8}
9
10⚠ stderr output:
11npm error code ENOLOCK
12npm error audit This command requires an existing lockfile.
13npm error audit Try creating one first with: npm i --package-lock-only
14npm error audit Original error: loadVirtual requires existing shrinkwrap file
15npm error A complete log of this run can be found in: /home/runner/.npm/_logs/2026-02-12T23_40_01_136Z-debug-0.log
16
17Process exited with code 1
18✓ Completed in 145376ms

Scanned: 2/12/2026, 11:41:12 PM

Passed7d0d5f4
0 findings
0
critical
0
high
0
medium
0
low
0
info

Scanners3/5 ran

clawguard-rules
0 findings1ms
No findings — all checks passed.
View logs
clawguard-rules1ms
1[2026-02-11T18:05:07.601Z] Running @yourclaw/clawguard-rules pattern matcher
2Scanning: /tmp/clawguard-scan-dt3foR/repo/skills/akellacom/prometheus/SKILL.md
3Content length: 2790 chars
4Patterns matched: 0
5✓ Completed in 1ms
gitleaks
0 findings162681ms
No findings — all checks passed.
View logs
gitleaks162681ms
1[2026-02-11T18:07:50.282Z] $ gitleaks detect --source /tmp/clawguard-scan-dt3foR/repo/skills/akellacom/prometheus --report-format json --report-path /dev/stdout --no-git
2
3⚠ stderr output:
4
5 │╲
6 │ ○
7 ○ ░
8 ░ gitleaks
9
106:07PM FTL Report path is not writable: /dev/stdout error="open /dev/stdout: no such device or address"
11
12Process exited with code 1
13✓ Completed in 162681ms
semgrep
error
semgrep error: Command 'semgrep' failed:
View logs
semgrep192032ms
1[2026-02-11T18:08:19.635Z] $ semgrep scan --json --quiet --config auto /tmp/clawguard-scan-dt3foR/repo/skills/akellacom/prometheus
2
3✗ Command 'semgrep' failed:
4
5Process exited with code null
6✓ Completed in 192032ms
mcp-scan
error
mcp-scan error: Command 'mcp-scan' failed:
View logs
mcp-scan191861ms
1[2026-02-11T18:08:19.467Z] $ mcp-scan --skills /tmp/clawguard-scan-dt3foR/repo/skills/akellacom/prometheus --json
2
3✗ Command 'mcp-scan' failed:
4
5Process exited with code null
6✓ Completed in 191861ms
npm-audit
0 findings153461ms
No findings — all checks passed.
View logs
npm-audit153461ms
1[2026-02-11T18:07:41.069Z] $ npm audit --json --prefix /tmp/clawguard-scan-dt3foR/repo/skills/akellacom/prometheus
2{
3 "error": {
4 "code": "ENOLOCK",
5 "summary": "This command requires an existing lockfile.",
6 "detail": "Try creating one first with: npm i --package-lock-only\nOriginal error: loadVirtual requires existing shrinkwrap file"
7 }
8}
9
10⚠ stderr output:
11npm error code ENOLOCK
12npm error audit This command requires an existing lockfile.
13npm error audit Try creating one first with: npm i --package-lock-only
14npm error audit Original error: loadVirtual requires existing shrinkwrap file
15npm error A complete log of this run can be found in: /home/runner/.npm/_logs/2026-02-11T18_07_23_284Z-debug-0.log
16
17Process exited with code 1
18✓ Completed in 153461ms

Scanned: 2/11/2026, 6:08:20 PM