omnimancer-cli 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.
- omnimancer/__init__.py +42 -0
- omnimancer/__main__.py +9 -0
- omnimancer/cli/__init__.py +8 -0
- omnimancer/cli/agent_manager.py +826 -0
- omnimancer/cli/agent_wizard.py +944 -0
- omnimancer/cli/approval_formatter.py +734 -0
- omnimancer/cli/approval_integration.py +734 -0
- omnimancer/cli/approval_prompt.py +1286 -0
- omnimancer/cli/batch_approval_display.py +617 -0
- omnimancer/cli/batch_approval_filters.py +729 -0
- omnimancer/cli/batch_operation_monitor.py +775 -0
- omnimancer/cli/commands/performance_cmd.py +624 -0
- omnimancer/cli/commands.py +757 -0
- omnimancer/cli/completion_handler.py +281 -0
- omnimancer/cli/config_setup_wizard.py +783 -0
- omnimancer/cli/dynamic_commands.py +371 -0
- omnimancer/cli/handlers/__init__.py +18 -0
- omnimancer/cli/handlers/agent_handler.py +865 -0
- omnimancer/cli/handlers/agent_persona_handler.py +1048 -0
- omnimancer/cli/handlers/custom_agent_handler.py +352 -0
- omnimancer/cli/handlers/permissions_handler.py +1609 -0
- omnimancer/cli/interface.py +6695 -0
- omnimancer/cli/performance_dashboard.py +612 -0
- omnimancer/core/__init__.py +34 -0
- omnimancer/core/agent/__init__.py +256 -0
- omnimancer/core/agent/agent_config.py +715 -0
- omnimancer/core/agent/agent_switcher.py +668 -0
- omnimancer/core/agent/approval_context.py +460 -0
- omnimancer/core/agent/approval_dialog.py +902 -0
- omnimancer/core/agent/approval_interface.py +628 -0
- omnimancer/core/agent/approval_manager.py +760 -0
- omnimancer/core/agent/cli_integration.py +470 -0
- omnimancer/core/agent/config.py +845 -0
- omnimancer/core/agent/custom_agent_integration.py +654 -0
- omnimancer/core/agent/diff_renderer.py +706 -0
- omnimancer/core/agent/file_content_display.py +503 -0
- omnimancer/core/agent/file_modification_workflow.py +658 -0
- omnimancer/core/agent/file_system_manager.py +1790 -0
- omnimancer/core/agent/input_handler.py +614 -0
- omnimancer/core/agent/metrics_collector.py +771 -0
- omnimancer/core/agent/optimization_engine.py +729 -0
- omnimancer/core/agent/performance_monitor.py +717 -0
- omnimancer/core/agent/persona.py +870 -0
- omnimancer/core/agent/persona_validator.py +672 -0
- omnimancer/core/agent/program_executor.py +748 -0
- omnimancer/core/agent/proposed_changes_integration.py +663 -0
- omnimancer/core/agent/read_before_write_errors.py +492 -0
- omnimancer/core/agent/read_before_write_ui.py +647 -0
- omnimancer/core/agent/rich_renderer.py +763 -0
- omnimancer/core/agent/status_config.py +238 -0
- omnimancer/core/agent/status_core.py +446 -0
- omnimancer/core/agent/status_integrated_engine.py +540 -0
- omnimancer/core/agent/status_manager.py +878 -0
- omnimancer/core/agent/status_ui_components.py +535 -0
- omnimancer/core/agent/template_applicator.py +548 -0
- omnimancer/core/agent/terminal_status_listener.py +394 -0
- omnimancer/core/agent/token_tracker.py +679 -0
- omnimancer/core/agent/types.py +60 -0
- omnimancer/core/agent/web_client.py +745 -0
- omnimancer/core/agent/workflow_orchestrator.py +826 -0
- omnimancer/core/agent_engine.py +1625 -0
- omnimancer/core/agent_mode_manager.py +706 -0
- omnimancer/core/agent_progress_ui.py +545 -0
- omnimancer/core/chat_manager.py +141 -0
- omnimancer/core/config_manager.py +2199 -0
- omnimancer/core/config_migration.py +772 -0
- omnimancer/core/config_provider.py +1974 -0
- omnimancer/core/config_security_validator.py +393 -0
- omnimancer/core/config_validator.py +768 -0
- omnimancer/core/conversation_manager.py +352 -0
- omnimancer/core/engine.py +787 -0
- omnimancer/core/enhanced_input.py +523 -0
- omnimancer/core/env_loader.py +83 -0
- omnimancer/core/fallback_manager.py +568 -0
- omnimancer/core/health_monitor.py +462 -0
- omnimancer/core/history_manager.py +462 -0
- omnimancer/core/mcp_integration_layer.py +878 -0
- omnimancer/core/models.py +1889 -0
- omnimancer/core/provider_capabilities.py +403 -0
- omnimancer/core/provider_initializer.py +450 -0
- omnimancer/core/provider_registry.py +432 -0
- omnimancer/core/security/__init__.py +16 -0
- omnimancer/core/security/approval_workflow.py +406 -0
- omnimancer/core/security/audit_logger.py +511 -0
- omnimancer/core/security/permission_controller.py +511 -0
- omnimancer/core/security/sandbox_manager.py +379 -0
- omnimancer/core/security/security_manager.py +530 -0
- omnimancer/core/setup_wizard.py +345 -0
- omnimancer/core/setup_wizard_core.py +449 -0
- omnimancer/core/setup_wizard_guide_generator.py +376 -0
- omnimancer/core/setup_wizard_provider_setup.py +454 -0
- omnimancer/core/setup_wizard_ui.py +317 -0
- omnimancer/core/setup_wizard_validation.py +371 -0
- omnimancer/core/signal_handler.py +174 -0
- omnimancer/mcp/__init__.py +11 -0
- omnimancer/mcp/client.py +725 -0
- omnimancer/mcp/manager.py +649 -0
- omnimancer/providers/__init__.py +24 -0
- omnimancer/providers/azure.py +608 -0
- omnimancer/providers/base.py +262 -0
- omnimancer/providers/bedrock.py +711 -0
- omnimancer/providers/claude.py +426 -0
- omnimancer/providers/claude_code.py +406 -0
- omnimancer/providers/cohere.py +412 -0
- omnimancer/providers/factory.py +988 -0
- omnimancer/providers/gemini.py +708 -0
- omnimancer/providers/mistral.py +554 -0
- omnimancer/providers/ollama.py +510 -0
- omnimancer/providers/openai.py +362 -0
- omnimancer/providers/openrouter.py +709 -0
- omnimancer/providers/perplexity.py +476 -0
- omnimancer/providers/vertex.py +617 -0
- omnimancer/providers/xai.py +484 -0
- omnimancer/ui/__init__.py +6 -0
- omnimancer/ui/cancellation_handler.py +235 -0
- omnimancer/ui/progress_indicator.py +318 -0
- omnimancer/utils/__init__.py +22 -0
- omnimancer/utils/error_handler.py +589 -0
- omnimancer/utils/errors.py +667 -0
- omnimancer/utils/retry.py +238 -0
- omnimancer_cli-0.1.0.dist-info/METADATA +413 -0
- omnimancer_cli-0.1.0.dist-info/RECORD +126 -0
- omnimancer_cli-0.1.0.dist-info/WHEEL +5 -0
- omnimancer_cli-0.1.0.dist-info/entry_points.txt +4 -0
- omnimancer_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
- omnimancer_cli-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Approval Context and Data Structures for Omnimancer Agent Operations.
|
|
3
|
+
|
|
4
|
+
This module defines the data structures and context objects used
|
|
5
|
+
throughout the approval workflow system.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any, Dict, List, Optional, Union
|
|
13
|
+
|
|
14
|
+
from .rich_renderer import RiskLevel
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class OperationStatus(Enum):
|
|
18
|
+
"""Status of an operation."""
|
|
19
|
+
|
|
20
|
+
PENDING = "pending"
|
|
21
|
+
APPROVED = "approved"
|
|
22
|
+
DENIED = "denied"
|
|
23
|
+
EXECUTING = "executing"
|
|
24
|
+
COMPLETED = "completed"
|
|
25
|
+
FAILED = "failed"
|
|
26
|
+
CANCELLED = "cancelled"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class FileOperationType(Enum):
|
|
30
|
+
"""Types of file operations."""
|
|
31
|
+
|
|
32
|
+
READ = "read"
|
|
33
|
+
WRITE = "write"
|
|
34
|
+
CREATE = "create"
|
|
35
|
+
DELETE = "delete"
|
|
36
|
+
MOVE = "move"
|
|
37
|
+
COPY = "copy"
|
|
38
|
+
MODIFY = "modify"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class FileChange:
|
|
43
|
+
"""Represents a file change in an operation."""
|
|
44
|
+
|
|
45
|
+
path: Path
|
|
46
|
+
operation: FileOperationType
|
|
47
|
+
content_preview: Optional[str] = None
|
|
48
|
+
size_bytes: Optional[int] = None
|
|
49
|
+
permissions: Optional[str] = None
|
|
50
|
+
backup_path: Optional[Path] = None
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def relative_path(self) -> str:
|
|
54
|
+
"""Get relative path as string."""
|
|
55
|
+
return str(self.path)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class SecurityFlags:
|
|
60
|
+
"""Security-related flags for operations."""
|
|
61
|
+
|
|
62
|
+
requires_sudo: bool = False
|
|
63
|
+
modifies_system_files: bool = False
|
|
64
|
+
accesses_network: bool = False
|
|
65
|
+
executes_code: bool = False
|
|
66
|
+
modifies_permissions: bool = False
|
|
67
|
+
creates_processes: bool = False
|
|
68
|
+
accesses_sensitive_data: bool = False
|
|
69
|
+
|
|
70
|
+
def get_risk_factors(self) -> List[str]:
|
|
71
|
+
"""Get list of security risk factors."""
|
|
72
|
+
factors = []
|
|
73
|
+
|
|
74
|
+
if self.requires_sudo:
|
|
75
|
+
factors.append("Requires elevated privileges")
|
|
76
|
+
if self.modifies_system_files:
|
|
77
|
+
factors.append("Modifies system files")
|
|
78
|
+
if self.accesses_network:
|
|
79
|
+
factors.append("Makes network requests")
|
|
80
|
+
if self.executes_code:
|
|
81
|
+
factors.append("Executes external code")
|
|
82
|
+
if self.modifies_permissions:
|
|
83
|
+
factors.append("Changes file permissions")
|
|
84
|
+
if self.creates_processes:
|
|
85
|
+
factors.append("Creates new processes")
|
|
86
|
+
if self.accesses_sensitive_data:
|
|
87
|
+
factors.append("Accesses sensitive data")
|
|
88
|
+
|
|
89
|
+
return factors
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass
|
|
93
|
+
class OperationDetails:
|
|
94
|
+
"""Detailed information about an operation requiring approval."""
|
|
95
|
+
|
|
96
|
+
# Core operation information
|
|
97
|
+
operation_type: str
|
|
98
|
+
target: Optional[str] = None
|
|
99
|
+
description: Optional[str] = None
|
|
100
|
+
|
|
101
|
+
# Risk assessment
|
|
102
|
+
risk_level: Union[RiskLevel, int, str] = RiskLevel.MEDIUM
|
|
103
|
+
risk_factors: List[str] = field(default_factory=list)
|
|
104
|
+
security_flags: SecurityFlags = field(default_factory=SecurityFlags)
|
|
105
|
+
|
|
106
|
+
# Command/execution details
|
|
107
|
+
command: Optional[str] = None
|
|
108
|
+
arguments: List[str] = field(default_factory=list)
|
|
109
|
+
working_directory: Optional[str] = None
|
|
110
|
+
environment_vars: Dict[str, str] = field(default_factory=dict)
|
|
111
|
+
|
|
112
|
+
# File operations
|
|
113
|
+
files_affected: List[Path] = field(default_factory=list)
|
|
114
|
+
file_changes: List[FileChange] = field(default_factory=list)
|
|
115
|
+
|
|
116
|
+
# Timing and resources
|
|
117
|
+
estimated_time: Optional[float] = None
|
|
118
|
+
max_memory_mb: Optional[int] = None
|
|
119
|
+
max_disk_mb: Optional[int] = None
|
|
120
|
+
|
|
121
|
+
# Operation metadata
|
|
122
|
+
operation_id: Optional[str] = None
|
|
123
|
+
parent_operation_id: Optional[str] = None
|
|
124
|
+
batch_id: Optional[str] = None
|
|
125
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
126
|
+
|
|
127
|
+
def __post_init__(self):
|
|
128
|
+
"""Post-initialization processing."""
|
|
129
|
+
# Auto-populate risk factors from security flags
|
|
130
|
+
if not self.risk_factors and hasattr(self.security_flags, "get_risk_factors"):
|
|
131
|
+
self.risk_factors = self.security_flags.get_risk_factors()
|
|
132
|
+
|
|
133
|
+
# Ensure paths are Path objects
|
|
134
|
+
self.files_affected = [
|
|
135
|
+
Path(f) if not isinstance(f, Path) else f for f in self.files_affected
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
def add_file_change(
|
|
139
|
+
self,
|
|
140
|
+
path: Union[str, Path],
|
|
141
|
+
operation: FileOperationType,
|
|
142
|
+
content_preview: Optional[str] = None,
|
|
143
|
+
) -> None:
|
|
144
|
+
"""Add a file change to this operation."""
|
|
145
|
+
file_change = FileChange(
|
|
146
|
+
path=Path(path) if not isinstance(path, Path) else path,
|
|
147
|
+
operation=operation,
|
|
148
|
+
content_preview=content_preview,
|
|
149
|
+
)
|
|
150
|
+
self.file_changes.append(file_change)
|
|
151
|
+
|
|
152
|
+
# Also add to files_affected if not already present
|
|
153
|
+
if file_change.path not in self.files_affected:
|
|
154
|
+
self.files_affected.append(file_change.path)
|
|
155
|
+
|
|
156
|
+
def get_total_files_affected(self) -> int:
|
|
157
|
+
"""Get total number of files affected."""
|
|
158
|
+
return len(set(self.files_affected + [fc.path for fc in self.file_changes]))
|
|
159
|
+
|
|
160
|
+
def get_operation_summary(self) -> str:
|
|
161
|
+
"""Get a brief summary of the operation."""
|
|
162
|
+
if self.description:
|
|
163
|
+
return self.description
|
|
164
|
+
|
|
165
|
+
summary_parts = [self.operation_type]
|
|
166
|
+
|
|
167
|
+
if self.target:
|
|
168
|
+
summary_parts.append(f"on {self.target}")
|
|
169
|
+
elif self.files_affected:
|
|
170
|
+
if len(self.files_affected) == 1:
|
|
171
|
+
summary_parts.append(f"on {self.files_affected[0]}")
|
|
172
|
+
else:
|
|
173
|
+
summary_parts.append(f"on {len(self.files_affected)} files")
|
|
174
|
+
|
|
175
|
+
return " ".join(summary_parts)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@dataclass
|
|
179
|
+
class ApprovalDecision:
|
|
180
|
+
"""Represents a user's approval decision."""
|
|
181
|
+
|
|
182
|
+
approved: bool
|
|
183
|
+
reason: Optional[str] = None
|
|
184
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
185
|
+
user_id: Optional[str] = None
|
|
186
|
+
session_id: Optional[str] = None
|
|
187
|
+
additional_data: Dict[str, Any] = field(default_factory=dict)
|
|
188
|
+
|
|
189
|
+
# Decision metadata
|
|
190
|
+
response_time_seconds: Optional[float] = None
|
|
191
|
+
approval_method: Optional[str] = None # "manual", "auto", "cached", etc.
|
|
192
|
+
confidence_level: Optional[float] = None # 0.0 - 1.0
|
|
193
|
+
|
|
194
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
195
|
+
"""Convert to dictionary for serialization."""
|
|
196
|
+
return {
|
|
197
|
+
"approved": self.approved,
|
|
198
|
+
"reason": self.reason,
|
|
199
|
+
"timestamp": self.timestamp.isoformat(),
|
|
200
|
+
"user_id": self.user_id,
|
|
201
|
+
"session_id": self.session_id,
|
|
202
|
+
"additional_data": self.additional_data,
|
|
203
|
+
"response_time_seconds": self.response_time_seconds,
|
|
204
|
+
"approval_method": self.approval_method,
|
|
205
|
+
"confidence_level": self.confidence_level,
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
@classmethod
|
|
209
|
+
def from_dict(cls, data: Dict[str, Any]) -> "ApprovalDecision":
|
|
210
|
+
"""Create from dictionary."""
|
|
211
|
+
timestamp = (
|
|
212
|
+
datetime.fromisoformat(data["timestamp"])
|
|
213
|
+
if "timestamp" in data
|
|
214
|
+
else datetime.now()
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
return cls(
|
|
218
|
+
approved=data["approved"],
|
|
219
|
+
reason=data.get("reason"),
|
|
220
|
+
timestamp=timestamp,
|
|
221
|
+
user_id=data.get("user_id"),
|
|
222
|
+
session_id=data.get("session_id"),
|
|
223
|
+
additional_data=data.get("additional_data", {}),
|
|
224
|
+
response_time_seconds=data.get("response_time_seconds"),
|
|
225
|
+
approval_method=data.get("approval_method"),
|
|
226
|
+
confidence_level=data.get("confidence_level"),
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@dataclass
|
|
231
|
+
class ApprovalContext:
|
|
232
|
+
"""Complete context for an approval request."""
|
|
233
|
+
|
|
234
|
+
# Core identification
|
|
235
|
+
context_id: str = field(
|
|
236
|
+
default_factory=lambda: f"ctx_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
|
237
|
+
)
|
|
238
|
+
agent_name: str = "Omnimancer Agent"
|
|
239
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
240
|
+
|
|
241
|
+
# Operation details
|
|
242
|
+
operation_details: OperationDetails = field(
|
|
243
|
+
default_factory=lambda: OperationDetails(operation_type="unknown")
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# Content for display
|
|
247
|
+
diff_content: Optional[str] = None
|
|
248
|
+
log_content: Optional[str] = None
|
|
249
|
+
preview_content: Optional[str] = None
|
|
250
|
+
|
|
251
|
+
# Context and history
|
|
252
|
+
previous_operations: List[OperationDetails] = field(default_factory=list)
|
|
253
|
+
related_context: Dict[str, Any] = field(default_factory=dict)
|
|
254
|
+
|
|
255
|
+
# Session information
|
|
256
|
+
session_id: Optional[str] = None
|
|
257
|
+
user_id: Optional[str] = None
|
|
258
|
+
conversation_history: List[str] = field(default_factory=list)
|
|
259
|
+
|
|
260
|
+
# Approval workflow state
|
|
261
|
+
status: OperationStatus = OperationStatus.PENDING
|
|
262
|
+
decision: Optional[ApprovalDecision] = None
|
|
263
|
+
auto_approval_eligible: bool = False
|
|
264
|
+
approval_timeout_seconds: Optional[int] = None
|
|
265
|
+
|
|
266
|
+
# Display customization
|
|
267
|
+
display_options: Dict[str, Any] = field(default_factory=dict)
|
|
268
|
+
|
|
269
|
+
def add_conversation_entry(self, message: str) -> None:
|
|
270
|
+
"""Add an entry to the conversation history."""
|
|
271
|
+
timestamp = datetime.now().strftime("%H:%M:%S")
|
|
272
|
+
self.conversation_history.append(f"[{timestamp}] {message}")
|
|
273
|
+
|
|
274
|
+
def add_related_context(self, key: str, value: Any) -> None:
|
|
275
|
+
"""Add related context information."""
|
|
276
|
+
self.related_context[key] = value
|
|
277
|
+
|
|
278
|
+
def set_decision(self, decision: ApprovalDecision) -> None:
|
|
279
|
+
"""Set the approval decision and update status."""
|
|
280
|
+
self.decision = decision
|
|
281
|
+
self.status = (
|
|
282
|
+
OperationStatus.APPROVED if decision.approved else OperationStatus.DENIED
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
def get_context_summary(self) -> Dict[str, Any]:
|
|
286
|
+
"""Get a summary of the context for logging."""
|
|
287
|
+
return {
|
|
288
|
+
"context_id": self.context_id,
|
|
289
|
+
"agent_name": self.agent_name,
|
|
290
|
+
"operation_type": self.operation_details.operation_type,
|
|
291
|
+
"risk_level": str(self.operation_details.risk_level),
|
|
292
|
+
"files_affected": len(self.operation_details.files_affected),
|
|
293
|
+
"status": self.status.value,
|
|
294
|
+
"approved": self.decision.approved if self.decision else None,
|
|
295
|
+
"timestamp": self.timestamp.isoformat(),
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
def is_completed(self) -> bool:
|
|
299
|
+
"""Check if the approval process is completed."""
|
|
300
|
+
return self.status in [
|
|
301
|
+
OperationStatus.APPROVED,
|
|
302
|
+
OperationStatus.DENIED,
|
|
303
|
+
OperationStatus.CANCELLED,
|
|
304
|
+
]
|
|
305
|
+
|
|
306
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
307
|
+
"""Convert to dictionary for serialization."""
|
|
308
|
+
return {
|
|
309
|
+
"context_id": self.context_id,
|
|
310
|
+
"agent_name": self.agent_name,
|
|
311
|
+
"timestamp": self.timestamp.isoformat(),
|
|
312
|
+
"operation_details": {
|
|
313
|
+
"operation_type": self.operation_details.operation_type,
|
|
314
|
+
"target": self.operation_details.target,
|
|
315
|
+
"description": self.operation_details.description,
|
|
316
|
+
"risk_level": str(self.operation_details.risk_level),
|
|
317
|
+
"risk_factors": self.operation_details.risk_factors,
|
|
318
|
+
"command": self.operation_details.command,
|
|
319
|
+
"arguments": self.operation_details.arguments,
|
|
320
|
+
"files_affected": [
|
|
321
|
+
str(f) for f in self.operation_details.files_affected
|
|
322
|
+
],
|
|
323
|
+
"metadata": self.operation_details.metadata,
|
|
324
|
+
},
|
|
325
|
+
"diff_content": self.diff_content,
|
|
326
|
+
"status": self.status.value,
|
|
327
|
+
"decision": self.decision.to_dict() if self.decision else None,
|
|
328
|
+
"session_id": self.session_id,
|
|
329
|
+
"user_id": self.user_id,
|
|
330
|
+
"conversation_history": self.conversation_history,
|
|
331
|
+
"related_context": self.related_context,
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
# Utility functions for creating common contexts
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def create_file_operation_context(
|
|
339
|
+
operation_type: str,
|
|
340
|
+
file_path: Union[str, Path],
|
|
341
|
+
content: Optional[str] = None,
|
|
342
|
+
agent_name: str = "Omnimancer Agent",
|
|
343
|
+
) -> ApprovalContext:
|
|
344
|
+
"""Create approval context for file operations."""
|
|
345
|
+
file_path = Path(file_path) if not isinstance(file_path, Path) else file_path
|
|
346
|
+
|
|
347
|
+
# Determine file operation type
|
|
348
|
+
file_op_type = FileOperationType.MODIFY
|
|
349
|
+
if "create" in operation_type.lower():
|
|
350
|
+
file_op_type = FileOperationType.CREATE
|
|
351
|
+
elif "delete" in operation_type.lower():
|
|
352
|
+
file_op_type = FileOperationType.DELETE
|
|
353
|
+
elif "read" in operation_type.lower():
|
|
354
|
+
file_op_type = FileOperationType.READ
|
|
355
|
+
elif "write" in operation_type.lower():
|
|
356
|
+
file_op_type = FileOperationType.WRITE
|
|
357
|
+
|
|
358
|
+
# Create operation details
|
|
359
|
+
operation_details = OperationDetails(
|
|
360
|
+
operation_type=operation_type,
|
|
361
|
+
target=str(file_path),
|
|
362
|
+
description=f"{operation_type} on {file_path.name}",
|
|
363
|
+
files_affected=[file_path],
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
# Add file change
|
|
367
|
+
operation_details.add_file_change(
|
|
368
|
+
file_path,
|
|
369
|
+
file_op_type,
|
|
370
|
+
content[:200] + "..." if content and len(content) > 200 else content,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
# Create context
|
|
374
|
+
context = ApprovalContext(
|
|
375
|
+
agent_name=agent_name,
|
|
376
|
+
operation_details=operation_details,
|
|
377
|
+
preview_content=content,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
return context
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def create_command_execution_context(
|
|
384
|
+
command: str,
|
|
385
|
+
arguments: Optional[List[str]] = None,
|
|
386
|
+
working_directory: Optional[str] = None,
|
|
387
|
+
agent_name: str = "Omnimancer Agent",
|
|
388
|
+
) -> ApprovalContext:
|
|
389
|
+
"""Create approval context for command execution."""
|
|
390
|
+
arguments = arguments or []
|
|
391
|
+
|
|
392
|
+
# Assess security flags
|
|
393
|
+
security_flags = SecurityFlags()
|
|
394
|
+
|
|
395
|
+
# Basic security assessment based on command
|
|
396
|
+
if command in ["sudo", "su", "doas"]:
|
|
397
|
+
security_flags.requires_sudo = True
|
|
398
|
+
|
|
399
|
+
if any(
|
|
400
|
+
sys_path in " ".join([command] + arguments)
|
|
401
|
+
for sys_path in ["/etc", "/usr/bin", "/sys"]
|
|
402
|
+
):
|
|
403
|
+
security_flags.modifies_system_files = True
|
|
404
|
+
|
|
405
|
+
if any(net_cmd in command for net_cmd in ["curl", "wget", "nc", "ssh"]):
|
|
406
|
+
security_flags.accesses_network = True
|
|
407
|
+
|
|
408
|
+
if any(
|
|
409
|
+
exec_cmd in command for exec_cmd in ["eval", "exec", "python", "bash", "sh"]
|
|
410
|
+
):
|
|
411
|
+
security_flags.executes_code = True
|
|
412
|
+
|
|
413
|
+
# Create operation details
|
|
414
|
+
operation_details = OperationDetails(
|
|
415
|
+
operation_type="command_execute",
|
|
416
|
+
target=command,
|
|
417
|
+
description=f"Execute: {command} {' '.join(arguments)}",
|
|
418
|
+
command=command,
|
|
419
|
+
arguments=arguments,
|
|
420
|
+
working_directory=working_directory,
|
|
421
|
+
security_flags=security_flags,
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
# Create context
|
|
425
|
+
context = ApprovalContext(
|
|
426
|
+
agent_name=agent_name, operation_details=operation_details
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
return context
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def create_web_request_context(
|
|
433
|
+
url: str,
|
|
434
|
+
method: str = "GET",
|
|
435
|
+
headers: Optional[Dict[str, str]] = None,
|
|
436
|
+
agent_name: str = "Omnimancer Agent",
|
|
437
|
+
) -> ApprovalContext:
|
|
438
|
+
"""Create approval context for web requests."""
|
|
439
|
+
# Create security flags
|
|
440
|
+
security_flags = SecurityFlags(accesses_network=True)
|
|
441
|
+
|
|
442
|
+
# Additional assessment based on URL
|
|
443
|
+
if any(sensitive in url.lower() for sensitive in ["admin", "api", "auth", "login"]):
|
|
444
|
+
security_flags.accesses_sensitive_data = True
|
|
445
|
+
|
|
446
|
+
# Create operation details
|
|
447
|
+
operation_details = OperationDetails(
|
|
448
|
+
operation_type="web_request",
|
|
449
|
+
target=url,
|
|
450
|
+
description=f"{method} request to {url}",
|
|
451
|
+
security_flags=security_flags,
|
|
452
|
+
metadata={"method": method, "headers": headers or {}, "url": url},
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
# Create context
|
|
456
|
+
context = ApprovalContext(
|
|
457
|
+
agent_name=agent_name, operation_details=operation_details
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
return context
|