arch-ops-server 0.1.0__py3-none-any.whl
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.
- arch_ops_server/__init__.py +79 -0
- arch_ops_server/aur.py +1132 -0
- arch_ops_server/pacman.py +319 -0
- arch_ops_server/py.typed +0 -0
- arch_ops_server/server.py +672 -0
- arch_ops_server/utils.py +272 -0
- arch_ops_server/wiki.py +244 -0
- arch_ops_server-0.1.0.dist-info/METADATA +109 -0
- arch_ops_server-0.1.0.dist-info/RECORD +11 -0
- arch_ops_server-0.1.0.dist-info/WHEEL +4 -0
- arch_ops_server-0.1.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Server setup for Arch Linux operations.
|
|
3
|
+
|
|
4
|
+
This module contains the MCP server configuration, resources, tools, and prompts
|
|
5
|
+
for the Arch Linux MCP server.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import json
|
|
10
|
+
from typing import Any
|
|
11
|
+
from urllib.parse import urlparse
|
|
12
|
+
|
|
13
|
+
from mcp.server import Server
|
|
14
|
+
from mcp.types import (
|
|
15
|
+
Resource,
|
|
16
|
+
Tool,
|
|
17
|
+
TextContent,
|
|
18
|
+
ImageContent,
|
|
19
|
+
EmbeddedResource,
|
|
20
|
+
Prompt,
|
|
21
|
+
PromptMessage,
|
|
22
|
+
GetPromptResult,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
from . import (
|
|
26
|
+
search_wiki,
|
|
27
|
+
get_wiki_page_as_text,
|
|
28
|
+
search_aur,
|
|
29
|
+
get_aur_info,
|
|
30
|
+
get_pkgbuild,
|
|
31
|
+
analyze_pkgbuild_safety,
|
|
32
|
+
analyze_package_metadata_risk,
|
|
33
|
+
get_official_package_info,
|
|
34
|
+
check_updates_dry_run,
|
|
35
|
+
install_package_secure,
|
|
36
|
+
IS_ARCH,
|
|
37
|
+
run_command,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Configure logging
|
|
41
|
+
logger = logging.getLogger(__name__)
|
|
42
|
+
|
|
43
|
+
# Initialize MCP server
|
|
44
|
+
server = Server("arch-ops-server")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# ============================================================================
|
|
48
|
+
# RESOURCES
|
|
49
|
+
# ============================================================================
|
|
50
|
+
|
|
51
|
+
@server.list_resources()
|
|
52
|
+
async def list_resources() -> list[Resource]:
|
|
53
|
+
"""
|
|
54
|
+
List available resource URI schemes.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
List of Resource objects describing available URI schemes
|
|
58
|
+
"""
|
|
59
|
+
return [
|
|
60
|
+
Resource(
|
|
61
|
+
uri="archwiki://Installation_guide",
|
|
62
|
+
name="Arch Wiki - Installation Guide",
|
|
63
|
+
mimeType="text/markdown",
|
|
64
|
+
description="Example: Fetch Arch Wiki pages as Markdown"
|
|
65
|
+
),
|
|
66
|
+
Resource(
|
|
67
|
+
uri="aur://yay/pkgbuild",
|
|
68
|
+
name="AUR - yay PKGBUILD",
|
|
69
|
+
mimeType="text/x-script.shell",
|
|
70
|
+
description="Example: Fetch AUR package PKGBUILD files"
|
|
71
|
+
),
|
|
72
|
+
Resource(
|
|
73
|
+
uri="aur://yay/info",
|
|
74
|
+
name="AUR - yay Package Info",
|
|
75
|
+
mimeType="application/json",
|
|
76
|
+
description="Example: Fetch AUR package metadata (votes, maintainer, etc)"
|
|
77
|
+
),
|
|
78
|
+
Resource(
|
|
79
|
+
uri="archrepo://vim",
|
|
80
|
+
name="Official Repository - Package Info",
|
|
81
|
+
mimeType="application/json",
|
|
82
|
+
description="Example: Fetch official repository package details"
|
|
83
|
+
),
|
|
84
|
+
Resource(
|
|
85
|
+
uri="pacman://installed",
|
|
86
|
+
name="System - Installed Packages",
|
|
87
|
+
mimeType="application/json",
|
|
88
|
+
description="List installed packages on Arch Linux system"
|
|
89
|
+
),
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@server.read_resource()
|
|
94
|
+
async def read_resource(uri: str) -> str:
|
|
95
|
+
"""
|
|
96
|
+
Read a resource by URI.
|
|
97
|
+
|
|
98
|
+
Supported schemes:
|
|
99
|
+
- archwiki://{page_title} - Returns Wiki page as Markdown
|
|
100
|
+
- aur://{package}/pkgbuild - Returns raw PKGBUILD file
|
|
101
|
+
- aur://{package}/info - Returns AUR package metadata
|
|
102
|
+
- archrepo://{package} - Returns official repository package info
|
|
103
|
+
- pacman://installed - Returns list of installed packages (Arch only)
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
uri: Resource URI (can be string or AnyUrl object)
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Resource content as string
|
|
110
|
+
|
|
111
|
+
Raises:
|
|
112
|
+
ValueError: If URI scheme is unsupported or resource not found
|
|
113
|
+
"""
|
|
114
|
+
# Convert to string if it's a Pydantic AnyUrl object
|
|
115
|
+
uri_str = str(uri)
|
|
116
|
+
logger.info(f"Reading resource: {uri_str}")
|
|
117
|
+
|
|
118
|
+
parsed = urlparse(uri_str)
|
|
119
|
+
scheme = parsed.scheme
|
|
120
|
+
|
|
121
|
+
if scheme == "archwiki":
|
|
122
|
+
# Extract page title from path (remove leading /)
|
|
123
|
+
page_title = parsed.path.lstrip('/')
|
|
124
|
+
|
|
125
|
+
if not page_title:
|
|
126
|
+
# If only hostname provided, use it as title
|
|
127
|
+
page_title = parsed.netloc
|
|
128
|
+
|
|
129
|
+
if not page_title:
|
|
130
|
+
raise ValueError("Wiki page title required in URI (e.g., archwiki://Installation_guide)")
|
|
131
|
+
|
|
132
|
+
# Fetch Wiki page as Markdown
|
|
133
|
+
content = await get_wiki_page_as_text(page_title)
|
|
134
|
+
return content
|
|
135
|
+
|
|
136
|
+
elif scheme == "aur":
|
|
137
|
+
# Extract package name from netloc or path
|
|
138
|
+
package_name = parsed.netloc or parsed.path.lstrip('/').split('/')[0]
|
|
139
|
+
|
|
140
|
+
if not package_name:
|
|
141
|
+
raise ValueError("AUR package name required in URI (e.g., aur://yay/pkgbuild)")
|
|
142
|
+
|
|
143
|
+
# Determine what to fetch based on path
|
|
144
|
+
path_parts = parsed.path.lstrip('/').split('/')
|
|
145
|
+
|
|
146
|
+
if len(path_parts) > 1 and path_parts[1] == "pkgbuild":
|
|
147
|
+
# Fetch PKGBUILD
|
|
148
|
+
pkgbuild_content = await get_pkgbuild(package_name)
|
|
149
|
+
return pkgbuild_content
|
|
150
|
+
elif len(path_parts) > 1 and path_parts[1] == "info":
|
|
151
|
+
# Fetch package info
|
|
152
|
+
package_info = await get_aur_info(package_name)
|
|
153
|
+
return json.dumps(package_info, indent=2)
|
|
154
|
+
else:
|
|
155
|
+
# Default to package info
|
|
156
|
+
package_info = await get_aur_info(package_name)
|
|
157
|
+
return json.dumps(package_info, indent=2)
|
|
158
|
+
|
|
159
|
+
elif scheme == "archrepo":
|
|
160
|
+
# Extract package name from netloc or path
|
|
161
|
+
package_name = parsed.netloc or parsed.path.lstrip('/')
|
|
162
|
+
|
|
163
|
+
if not package_name:
|
|
164
|
+
raise ValueError("Package name required in URI (e.g., archrepo://vim)")
|
|
165
|
+
|
|
166
|
+
# Fetch official package info
|
|
167
|
+
package_info = await get_official_package_info(package_name)
|
|
168
|
+
return json.dumps(package_info, indent=2)
|
|
169
|
+
|
|
170
|
+
elif scheme == "pacman":
|
|
171
|
+
if parsed.netloc == "installed" or parsed.path == "/installed":
|
|
172
|
+
if not IS_ARCH:
|
|
173
|
+
raise ValueError("pacman://installed only available on Arch Linux systems")
|
|
174
|
+
|
|
175
|
+
# Get installed packages
|
|
176
|
+
result = run_command(["pacman", "-Q"])
|
|
177
|
+
if result.returncode != 0:
|
|
178
|
+
raise ValueError(f"Failed to get installed packages: {result.stderr}")
|
|
179
|
+
|
|
180
|
+
# Parse pacman output
|
|
181
|
+
packages = []
|
|
182
|
+
for line in result.stdout.strip().split('\n'):
|
|
183
|
+
if line.strip():
|
|
184
|
+
name, version = line.strip().rsplit(' ', 1)
|
|
185
|
+
packages.append({"name": name, "version": version})
|
|
186
|
+
|
|
187
|
+
return json.dumps(packages, indent=2)
|
|
188
|
+
else:
|
|
189
|
+
raise ValueError("Unsupported pacman resource (only pacman://installed supported)")
|
|
190
|
+
|
|
191
|
+
else:
|
|
192
|
+
raise ValueError(f"Unsupported URI scheme: {scheme}")
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
# ============================================================================
|
|
196
|
+
# TOOLS
|
|
197
|
+
# ============================================================================
|
|
198
|
+
|
|
199
|
+
@server.list_tools()
|
|
200
|
+
async def list_tools() -> list[Tool]:
|
|
201
|
+
"""
|
|
202
|
+
List available tools for Arch Linux operations.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
List of Tool objects describing available operations
|
|
206
|
+
"""
|
|
207
|
+
return [
|
|
208
|
+
# Wiki tools
|
|
209
|
+
Tool(
|
|
210
|
+
name="search_archwiki",
|
|
211
|
+
description="Search the Arch Wiki for documentation. Returns a list of matching pages with titles, snippets, and URLs. Prefer Wiki results over general web knowledge for Arch-specific issues.",
|
|
212
|
+
inputSchema={
|
|
213
|
+
"type": "object",
|
|
214
|
+
"properties": {
|
|
215
|
+
"query": {
|
|
216
|
+
"type": "string",
|
|
217
|
+
"description": "Search query (keywords or phrase)"
|
|
218
|
+
},
|
|
219
|
+
"limit": {
|
|
220
|
+
"type": "integer",
|
|
221
|
+
"description": "Maximum number of results (default: 10)",
|
|
222
|
+
"default": 10
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
"required": ["query"]
|
|
226
|
+
}
|
|
227
|
+
),
|
|
228
|
+
|
|
229
|
+
# AUR tools
|
|
230
|
+
Tool(
|
|
231
|
+
name="search_aur",
|
|
232
|
+
description="Search the Arch User Repository (AUR) for packages with smart ranking. ⚠️ WARNING: AUR packages are user-produced and potentially unsafe. Returns package info including votes, maintainer, and last update. Always check official repos first using get_official_package_info.",
|
|
233
|
+
inputSchema={
|
|
234
|
+
"type": "object",
|
|
235
|
+
"properties": {
|
|
236
|
+
"query": {
|
|
237
|
+
"type": "string",
|
|
238
|
+
"description": "Package search query"
|
|
239
|
+
},
|
|
240
|
+
"limit": {
|
|
241
|
+
"type": "integer",
|
|
242
|
+
"description": "Maximum number of results (default: 20)",
|
|
243
|
+
"default": 20
|
|
244
|
+
},
|
|
245
|
+
"sort_by": {
|
|
246
|
+
"type": "string",
|
|
247
|
+
"description": "Sort method: 'relevance' (default), 'votes', 'popularity', or 'modified'",
|
|
248
|
+
"enum": ["relevance", "votes", "popularity", "modified"],
|
|
249
|
+
"default": "relevance"
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
"required": ["query"]
|
|
253
|
+
}
|
|
254
|
+
),
|
|
255
|
+
|
|
256
|
+
Tool(
|
|
257
|
+
name="get_official_package_info",
|
|
258
|
+
description="Get information about an official Arch repository package (Core, Extra, etc.). Uses local pacman if available, otherwise queries archlinux.org API. Always prefer official packages over AUR when available.",
|
|
259
|
+
inputSchema={
|
|
260
|
+
"type": "object",
|
|
261
|
+
"properties": {
|
|
262
|
+
"package_name": {
|
|
263
|
+
"type": "string",
|
|
264
|
+
"description": "Exact package name"
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
"required": ["package_name"]
|
|
268
|
+
}
|
|
269
|
+
),
|
|
270
|
+
|
|
271
|
+
Tool(
|
|
272
|
+
name="check_updates_dry_run",
|
|
273
|
+
description="Check for available system updates without applying them. Only works on Arch Linux systems. Requires pacman-contrib package. Safe read-only operation that shows pending updates.",
|
|
274
|
+
inputSchema={
|
|
275
|
+
"type": "object",
|
|
276
|
+
"properties": {}
|
|
277
|
+
}
|
|
278
|
+
),
|
|
279
|
+
|
|
280
|
+
Tool(
|
|
281
|
+
name="install_package_secure",
|
|
282
|
+
description="Install a package with comprehensive security checks. Workflow: 1. Check official repos first (safer) 2. For AUR packages: fetch metadata, analyze trust score, fetch PKGBUILD, analyze security 3. Block installation if critical security issues found 4. Check for AUR helper (paru > yay) 5. Install with --noconfirm if all checks pass. Only works on Arch Linux. Requires sudo access and paru/yay for AUR packages.",
|
|
283
|
+
inputSchema={
|
|
284
|
+
"type": "object",
|
|
285
|
+
"properties": {
|
|
286
|
+
"package_name": {
|
|
287
|
+
"type": "string",
|
|
288
|
+
"description": "Name of package to install (checks official repos first, then AUR)"
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
"required": ["package_name"]
|
|
292
|
+
}
|
|
293
|
+
),
|
|
294
|
+
|
|
295
|
+
Tool(
|
|
296
|
+
name="analyze_pkgbuild_safety",
|
|
297
|
+
description="Analyze PKGBUILD content for security issues and dangerous patterns. Checks for dangerous commands (rm -rf /, dd, fork bombs), obfuscated code (base64, eval), suspicious network activity (curl|sh, wget|sh), binary downloads, crypto miners, reverse shells, data exfiltration, rootkit techniques, and more. Returns risk score (0-100) and detailed findings. Use this tool to manually audit AUR packages before installation.",
|
|
298
|
+
inputSchema={
|
|
299
|
+
"type": "object",
|
|
300
|
+
"properties": {
|
|
301
|
+
"pkgbuild_content": {
|
|
302
|
+
"type": "string",
|
|
303
|
+
"description": "Raw PKGBUILD content to analyze"
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
"required": ["pkgbuild_content"]
|
|
307
|
+
}
|
|
308
|
+
),
|
|
309
|
+
|
|
310
|
+
Tool(
|
|
311
|
+
name="analyze_package_metadata_risk",
|
|
312
|
+
description="Analyze AUR package metadata for trustworthiness and security indicators. Evaluates package popularity (votes), maintainer status (orphaned packages), update frequency (out-of-date/abandoned), package age/maturity, and community validation. Returns trust score (0-100) with risk factors and trust indicators. Use this alongside PKGBUILD analysis for comprehensive security assessment.",
|
|
313
|
+
inputSchema={
|
|
314
|
+
"type": "object",
|
|
315
|
+
"properties": {
|
|
316
|
+
"package_info": {
|
|
317
|
+
"type": "object",
|
|
318
|
+
"description": "Package metadata from AUR (from search_aur or get_aur_info results)"
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
"required": ["package_info"]
|
|
322
|
+
}
|
|
323
|
+
),
|
|
324
|
+
]
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
@server.call_tool()
|
|
328
|
+
async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent | ImageContent | EmbeddedResource]:
|
|
329
|
+
"""
|
|
330
|
+
Execute a tool by name with the provided arguments.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
name: Tool name
|
|
334
|
+
arguments: Tool arguments
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
List of content objects with tool results
|
|
338
|
+
|
|
339
|
+
Raises:
|
|
340
|
+
ValueError: If tool name is unknown
|
|
341
|
+
"""
|
|
342
|
+
logger.info(f"Calling tool: {name} with args: {arguments}")
|
|
343
|
+
|
|
344
|
+
if name == "search_archwiki":
|
|
345
|
+
query = arguments["query"]
|
|
346
|
+
limit = arguments.get("limit", 10)
|
|
347
|
+
results = await search_wiki(query, limit)
|
|
348
|
+
return [TextContent(type="text", text=json.dumps(results, indent=2))]
|
|
349
|
+
|
|
350
|
+
elif name == "search_aur":
|
|
351
|
+
query = arguments["query"]
|
|
352
|
+
limit = arguments.get("limit", 20)
|
|
353
|
+
sort_by = arguments.get("sort_by", "relevance")
|
|
354
|
+
results = await search_aur(query, limit, sort_by)
|
|
355
|
+
return [TextContent(type="text", text=json.dumps(results, indent=2))]
|
|
356
|
+
|
|
357
|
+
elif name == "get_official_package_info":
|
|
358
|
+
package_name = arguments["package_name"]
|
|
359
|
+
result = await get_official_package_info(package_name)
|
|
360
|
+
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
361
|
+
|
|
362
|
+
elif name == "check_updates_dry_run":
|
|
363
|
+
if not IS_ARCH:
|
|
364
|
+
return [TextContent(type="text", text="Error: check_updates_dry_run only available on Arch Linux systems")]
|
|
365
|
+
|
|
366
|
+
result = await check_updates_dry_run()
|
|
367
|
+
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
368
|
+
|
|
369
|
+
elif name == "install_package_secure":
|
|
370
|
+
if not IS_ARCH:
|
|
371
|
+
return [TextContent(type="text", text="Error: install_package_secure only available on Arch Linux systems")]
|
|
372
|
+
|
|
373
|
+
package_name = arguments["package_name"]
|
|
374
|
+
result = await install_package_secure(package_name)
|
|
375
|
+
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
376
|
+
|
|
377
|
+
elif name == "analyze_pkgbuild_safety":
|
|
378
|
+
pkgbuild_content = arguments["pkgbuild_content"]
|
|
379
|
+
result = await analyze_pkgbuild_safety(pkgbuild_content)
|
|
380
|
+
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
381
|
+
|
|
382
|
+
elif name == "analyze_package_metadata_risk":
|
|
383
|
+
package_info = arguments["package_info"]
|
|
384
|
+
result = await analyze_package_metadata_risk(package_info)
|
|
385
|
+
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
386
|
+
|
|
387
|
+
else:
|
|
388
|
+
raise ValueError(f"Unknown tool: {name}")
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
# ============================================================================
|
|
392
|
+
# PROMPTS
|
|
393
|
+
# ============================================================================
|
|
394
|
+
|
|
395
|
+
@server.list_prompts()
|
|
396
|
+
async def list_prompts() -> list[Prompt]:
|
|
397
|
+
"""
|
|
398
|
+
List available prompts for guided workflows.
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
List of Prompt objects describing available workflows
|
|
402
|
+
"""
|
|
403
|
+
return [
|
|
404
|
+
Prompt(
|
|
405
|
+
name="troubleshoot_issue",
|
|
406
|
+
description="Diagnose system errors and provide solutions using Arch Wiki knowledge",
|
|
407
|
+
arguments=[
|
|
408
|
+
{
|
|
409
|
+
"name": "error_message",
|
|
410
|
+
"description": "The error message or issue description",
|
|
411
|
+
"required": True
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
"name": "context",
|
|
415
|
+
"description": "Additional context about when/where the error occurred",
|
|
416
|
+
"required": False
|
|
417
|
+
}
|
|
418
|
+
]
|
|
419
|
+
),
|
|
420
|
+
Prompt(
|
|
421
|
+
name="audit_aur_package",
|
|
422
|
+
description="Perform comprehensive security audit of an AUR package before installation",
|
|
423
|
+
arguments=[
|
|
424
|
+
{
|
|
425
|
+
"name": "package_name",
|
|
426
|
+
"description": "Name of the AUR package to audit",
|
|
427
|
+
"required": True
|
|
428
|
+
}
|
|
429
|
+
]
|
|
430
|
+
),
|
|
431
|
+
Prompt(
|
|
432
|
+
name="analyze_dependencies",
|
|
433
|
+
description="Analyze package dependencies and suggest installation order",
|
|
434
|
+
arguments=[
|
|
435
|
+
{
|
|
436
|
+
"name": "package_name",
|
|
437
|
+
"description": "Name of the package to analyze dependencies for",
|
|
438
|
+
"required": True
|
|
439
|
+
}
|
|
440
|
+
]
|
|
441
|
+
),
|
|
442
|
+
]
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
@server.get_prompt()
|
|
446
|
+
async def get_prompt(name: str, arguments: dict[str, str]) -> GetPromptResult:
|
|
447
|
+
"""
|
|
448
|
+
Generate a prompt response for guided workflows.
|
|
449
|
+
|
|
450
|
+
Args:
|
|
451
|
+
name: Prompt name
|
|
452
|
+
arguments: Prompt arguments
|
|
453
|
+
|
|
454
|
+
Returns:
|
|
455
|
+
GetPromptResult with generated messages
|
|
456
|
+
|
|
457
|
+
Raises:
|
|
458
|
+
ValueError: If prompt name is unknown
|
|
459
|
+
"""
|
|
460
|
+
logger.info(f"Generating prompt: {name} with args: {arguments}")
|
|
461
|
+
|
|
462
|
+
if name == "troubleshoot_issue":
|
|
463
|
+
error_message = arguments["error_message"]
|
|
464
|
+
context = arguments.get("context", "")
|
|
465
|
+
|
|
466
|
+
# Extract keywords from error message for Wiki search
|
|
467
|
+
keywords = error_message.lower().split()
|
|
468
|
+
wiki_query = " ".join(keywords[:5]) # Use first 5 words as search query
|
|
469
|
+
|
|
470
|
+
# Search Wiki for relevant pages
|
|
471
|
+
try:
|
|
472
|
+
wiki_results = await search_wiki(wiki_query, limit=3)
|
|
473
|
+
except Exception as e:
|
|
474
|
+
wiki_results = []
|
|
475
|
+
|
|
476
|
+
messages = [
|
|
477
|
+
PromptMessage(
|
|
478
|
+
role="user",
|
|
479
|
+
content=PromptMessage.TextContent(
|
|
480
|
+
type="text",
|
|
481
|
+
text=f"I'm experiencing this error: {error_message}\n\nContext: {context}\n\nPlease help me troubleshoot this issue using Arch Linux knowledge."
|
|
482
|
+
)
|
|
483
|
+
)
|
|
484
|
+
]
|
|
485
|
+
|
|
486
|
+
if wiki_results:
|
|
487
|
+
wiki_content = "Here are some relevant Arch Wiki pages that might help:\n\n"
|
|
488
|
+
for result in wiki_results:
|
|
489
|
+
wiki_content += f"- **{result['title']}**: {result.get('snippet', 'No description available')}\n"
|
|
490
|
+
wiki_content += f" URL: {result['url']}\n\n"
|
|
491
|
+
|
|
492
|
+
messages.append(
|
|
493
|
+
PromptMessage(
|
|
494
|
+
role="assistant",
|
|
495
|
+
content=PromptMessage.TextContent(
|
|
496
|
+
type="text",
|
|
497
|
+
text=wiki_content
|
|
498
|
+
)
|
|
499
|
+
)
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
return GetPromptResult(
|
|
503
|
+
description=f"Troubleshooting guidance for: {error_message}",
|
|
504
|
+
messages=messages
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
elif name == "audit_aur_package":
|
|
508
|
+
package_name = arguments["package_name"]
|
|
509
|
+
|
|
510
|
+
# Get package info and PKGBUILD
|
|
511
|
+
try:
|
|
512
|
+
package_info = await get_aur_info(package_name)
|
|
513
|
+
pkgbuild_content = await get_pkgbuild(package_name)
|
|
514
|
+
|
|
515
|
+
# Analyze both metadata and PKGBUILD
|
|
516
|
+
metadata_risk = await analyze_package_metadata_risk(package_info)
|
|
517
|
+
pkgbuild_safety = await analyze_pkgbuild_safety(pkgbuild_content)
|
|
518
|
+
|
|
519
|
+
audit_summary = f"""
|
|
520
|
+
# Security Audit Report for {package_name}
|
|
521
|
+
|
|
522
|
+
## Package Metadata Analysis
|
|
523
|
+
- **Trust Score**: {metadata_risk.get('trust_score', 'N/A')}/100
|
|
524
|
+
- **Risk Factors**: {', '.join(metadata_risk.get('risk_factors', []))}
|
|
525
|
+
- **Trust Indicators**: {', '.join(metadata_risk.get('trust_indicators', []))}
|
|
526
|
+
|
|
527
|
+
## PKGBUILD Security Analysis
|
|
528
|
+
- **Risk Score**: {pkgbuild_safety.get('risk_score', 'N/A')}/100
|
|
529
|
+
- **Security Issues Found**: {len(pkgbuild_safety.get('findings', []))}
|
|
530
|
+
- **Critical Issues**: {len([f for f in pkgbuild_safety.get('findings', []) if f.get('severity') == 'critical'])}
|
|
531
|
+
|
|
532
|
+
## Recommendations
|
|
533
|
+
"""
|
|
534
|
+
|
|
535
|
+
if metadata_risk.get('trust_score', 0) < 50 or pkgbuild_safety.get('risk_score', 0) > 70:
|
|
536
|
+
audit_summary += "⚠️ **HIGH RISK** - Consider finding an alternative package or reviewing the source code manually.\n"
|
|
537
|
+
elif metadata_risk.get('trust_score', 0) < 70 or pkgbuild_safety.get('risk_score', 0) > 50:
|
|
538
|
+
audit_summary += "⚠️ **MEDIUM RISK** - Proceed with caution and review the findings below.\n"
|
|
539
|
+
else:
|
|
540
|
+
audit_summary += "✅ **LOW RISK** - Package appears safe to install.\n"
|
|
541
|
+
|
|
542
|
+
messages = [
|
|
543
|
+
PromptMessage(
|
|
544
|
+
role="user",
|
|
545
|
+
content=PromptMessage.TextContent(
|
|
546
|
+
type="text",
|
|
547
|
+
text=f"Please audit the AUR package '{package_name}' for security issues before installation."
|
|
548
|
+
)
|
|
549
|
+
),
|
|
550
|
+
PromptMessage(
|
|
551
|
+
role="assistant",
|
|
552
|
+
content=PromptMessage.TextContent(
|
|
553
|
+
type="text",
|
|
554
|
+
text=audit_summary
|
|
555
|
+
)
|
|
556
|
+
)
|
|
557
|
+
]
|
|
558
|
+
|
|
559
|
+
return GetPromptResult(
|
|
560
|
+
description=f"Security audit for AUR package: {package_name}",
|
|
561
|
+
messages=messages
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
except Exception as e:
|
|
565
|
+
return GetPromptResult(
|
|
566
|
+
description=f"Security audit for AUR package: {package_name}",
|
|
567
|
+
messages=[
|
|
568
|
+
PromptMessage(
|
|
569
|
+
role="assistant",
|
|
570
|
+
content=PromptMessage.TextContent(
|
|
571
|
+
type="text",
|
|
572
|
+
text=f"Error auditing package '{package_name}': {str(e)}"
|
|
573
|
+
)
|
|
574
|
+
)
|
|
575
|
+
]
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
elif name == "analyze_dependencies":
|
|
579
|
+
package_name = arguments["package_name"]
|
|
580
|
+
|
|
581
|
+
# Check if it's an official package first
|
|
582
|
+
try:
|
|
583
|
+
official_info = await get_official_package_info(package_name)
|
|
584
|
+
if official_info.get("found"):
|
|
585
|
+
deps = official_info.get("dependencies", [])
|
|
586
|
+
opt_deps = official_info.get("optional_dependencies", [])
|
|
587
|
+
|
|
588
|
+
analysis = f"""
|
|
589
|
+
# Dependency Analysis for {package_name} (Official Package)
|
|
590
|
+
|
|
591
|
+
## Required Dependencies
|
|
592
|
+
{chr(10).join([f"- {dep}" for dep in deps]) if deps else "None"}
|
|
593
|
+
|
|
594
|
+
## Optional Dependencies
|
|
595
|
+
{chr(10).join([f"- {dep}" for dep in opt_deps]) if opt_deps else "None"}
|
|
596
|
+
|
|
597
|
+
## Installation Order
|
|
598
|
+
1. Install required dependencies first
|
|
599
|
+
2. Install optional dependencies as needed
|
|
600
|
+
3. Install {package_name} last
|
|
601
|
+
|
|
602
|
+
## Installation Commands
|
|
603
|
+
```bash
|
|
604
|
+
# Install required dependencies
|
|
605
|
+
sudo pacman -S {' '.join(deps) if deps else '# No required dependencies'}
|
|
606
|
+
|
|
607
|
+
# Install optional dependencies (if needed)
|
|
608
|
+
sudo pacman -S {' '.join(opt_deps) if opt_deps else '# No optional dependencies'}
|
|
609
|
+
|
|
610
|
+
# Install the package
|
|
611
|
+
sudo pacman -S {package_name}
|
|
612
|
+
```
|
|
613
|
+
"""
|
|
614
|
+
else:
|
|
615
|
+
# Check AUR
|
|
616
|
+
aur_info = await get_aur_info(package_name)
|
|
617
|
+
if aur_info.get("found"):
|
|
618
|
+
analysis = f"""
|
|
619
|
+
# Dependency Analysis for {package_name} (AUR Package)
|
|
620
|
+
|
|
621
|
+
## AUR Package Information
|
|
622
|
+
- **Maintainer**: {aur_info.get('maintainer', 'Unknown')}
|
|
623
|
+
- **Last Updated**: {aur_info.get('last_modified', 'Unknown')}
|
|
624
|
+
- **Votes**: {aur_info.get('votes', 'Unknown')}
|
|
625
|
+
|
|
626
|
+
## Installation Considerations
|
|
627
|
+
1. **Security Check**: Run a security audit before installation
|
|
628
|
+
2. **Dependencies**: AUR packages may have complex dependency chains
|
|
629
|
+
3. **Build Requirements**: Check if you have all build tools installed
|
|
630
|
+
|
|
631
|
+
## Recommended Installation Process
|
|
632
|
+
```bash
|
|
633
|
+
# 1. Install build dependencies
|
|
634
|
+
sudo pacman -S base-devel git
|
|
635
|
+
|
|
636
|
+
# 2. Install AUR helper (if not already installed)
|
|
637
|
+
# Choose one: paru, yay, or manual AUR installation
|
|
638
|
+
|
|
639
|
+
# 3. Install the package
|
|
640
|
+
paru -S {package_name} # or yay -S {package_name}
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
⚠️ **Important**: Always audit AUR packages for security before installation!
|
|
644
|
+
"""
|
|
645
|
+
else:
|
|
646
|
+
analysis = f"Package '{package_name}' not found in official repositories or AUR."
|
|
647
|
+
|
|
648
|
+
except Exception as e:
|
|
649
|
+
analysis = f"Error analyzing dependencies for '{package_name}': {str(e)}"
|
|
650
|
+
|
|
651
|
+
return GetPromptResult(
|
|
652
|
+
description=f"Dependency analysis for: {package_name}",
|
|
653
|
+
messages=[
|
|
654
|
+
PromptMessage(
|
|
655
|
+
role="user",
|
|
656
|
+
content=PromptMessage.TextContent(
|
|
657
|
+
type="text",
|
|
658
|
+
text=f"Please analyze the dependencies for the package '{package_name}' and suggest the best installation approach."
|
|
659
|
+
)
|
|
660
|
+
),
|
|
661
|
+
PromptMessage(
|
|
662
|
+
role="assistant",
|
|
663
|
+
content=PromptMessage.TextContent(
|
|
664
|
+
type="text",
|
|
665
|
+
text=analysis
|
|
666
|
+
)
|
|
667
|
+
)
|
|
668
|
+
]
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
else:
|
|
672
|
+
raise ValueError(f"Unknown prompt: {name}")
|