skilllite 0.1.1__py3-none-any.whl → 0.1.2__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.
- skilllite/core/__init__.py +2 -0
- skilllite/core/adapters/__init__.py +74 -0
- skilllite/core/adapters/langchain.py +362 -0
- skilllite/core/adapters/llamaindex.py +264 -0
- skilllite/core/handler.py +179 -4
- skilllite/core/loops.py +175 -13
- skilllite/core/manager.py +82 -15
- skilllite/core/metadata.py +14 -7
- skilllite/core/security.py +420 -0
- skilllite/mcp/server.py +266 -58
- skilllite/quick.py +14 -4
- skilllite/sandbox/context.py +155 -0
- skilllite/sandbox/execution_service.py +254 -0
- skilllite/sandbox/skillbox/executor.py +124 -19
- skilllite/sandbox/unified_executor.py +359 -0
- {skilllite-0.1.1.dist-info → skilllite-0.1.2.dist-info}/METADATA +98 -1
- {skilllite-0.1.1.dist-info → skilllite-0.1.2.dist-info}/RECORD +21 -14
- {skilllite-0.1.1.dist-info → skilllite-0.1.2.dist-info}/WHEEL +0 -0
- {skilllite-0.1.1.dist-info → skilllite-0.1.2.dist-info}/entry_points.txt +0 -0
- {skilllite-0.1.1.dist-info → skilllite-0.1.2.dist-info}/licenses/LICENSE +0 -0
- {skilllite-0.1.1.dist-info → skilllite-0.1.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unified Executor - Single source of truth for all skill execution.
|
|
3
|
+
|
|
4
|
+
This module provides the UnifiedExecutor class which handles all execution logic.
|
|
5
|
+
It uses ExecutionContext to get configuration at runtime, ensuring that any
|
|
6
|
+
changes to environment variables or context overrides are immediately reflected.
|
|
7
|
+
|
|
8
|
+
Key Design Principles:
|
|
9
|
+
1. Never use instance variables for configuration
|
|
10
|
+
2. Always read from ExecutionContext at execution time
|
|
11
|
+
3. Single command building logic
|
|
12
|
+
4. Single subprocess execution logic
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import os
|
|
17
|
+
import subprocess
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Any, Dict, List, Optional
|
|
20
|
+
|
|
21
|
+
from .base import ExecutionResult
|
|
22
|
+
from .context import ExecutionContext
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class UnifiedExecutor:
|
|
26
|
+
"""
|
|
27
|
+
Unified executor - all skill execution goes through this class.
|
|
28
|
+
|
|
29
|
+
This class is stateless regarding configuration. All configuration
|
|
30
|
+
comes from ExecutionContext passed to each method.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self):
|
|
34
|
+
"""Initialize the executor."""
|
|
35
|
+
from .skillbox import find_binary
|
|
36
|
+
self._binary_path = find_binary()
|
|
37
|
+
if not self._binary_path:
|
|
38
|
+
raise RuntimeError("skillbox binary not found")
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def binary_path(self) -> str:
|
|
42
|
+
"""Path to the skillbox binary."""
|
|
43
|
+
return self._binary_path
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def is_available(self) -> bool:
|
|
47
|
+
"""Check if skillbox is available."""
|
|
48
|
+
return self._binary_path is not None and os.path.exists(self._binary_path)
|
|
49
|
+
|
|
50
|
+
def execute(
|
|
51
|
+
self,
|
|
52
|
+
context: ExecutionContext,
|
|
53
|
+
skill_dir: Path,
|
|
54
|
+
input_data: Dict[str, Any],
|
|
55
|
+
entry_point: Optional[str] = None,
|
|
56
|
+
args: Optional[List[str]] = None,
|
|
57
|
+
) -> ExecutionResult:
|
|
58
|
+
"""
|
|
59
|
+
Execute a skill with the given context.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
context: Execution context with all configuration
|
|
63
|
+
skill_dir: Path to the skill directory
|
|
64
|
+
input_data: Input data for the skill
|
|
65
|
+
entry_point: Optional specific script to execute
|
|
66
|
+
args: Optional command line arguments
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
ExecutionResult with output or error
|
|
70
|
+
"""
|
|
71
|
+
if entry_point:
|
|
72
|
+
return self.exec_script(
|
|
73
|
+
context=context,
|
|
74
|
+
skill_dir=skill_dir,
|
|
75
|
+
script_path=entry_point,
|
|
76
|
+
input_data=input_data,
|
|
77
|
+
args=args,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Build command for skillbox run
|
|
81
|
+
cmd = self._build_run_command(context, skill_dir, input_data)
|
|
82
|
+
return self._run_subprocess(cmd, context, skill_dir)
|
|
83
|
+
|
|
84
|
+
def exec_script(
|
|
85
|
+
self,
|
|
86
|
+
context: ExecutionContext,
|
|
87
|
+
skill_dir: Path,
|
|
88
|
+
script_path: str,
|
|
89
|
+
input_data: Dict[str, Any],
|
|
90
|
+
args: Optional[List[str]] = None,
|
|
91
|
+
) -> ExecutionResult:
|
|
92
|
+
"""
|
|
93
|
+
Execute a specific script directly.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
context: Execution context with all configuration
|
|
97
|
+
skill_dir: Path to the skill directory
|
|
98
|
+
script_path: Relative path to the script
|
|
99
|
+
input_data: Input data for the script
|
|
100
|
+
args: Optional command line arguments
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
ExecutionResult with output or error
|
|
104
|
+
"""
|
|
105
|
+
# Convert JSON input to CLI args if no explicit args provided
|
|
106
|
+
if args is None and input_data:
|
|
107
|
+
args = self._convert_json_to_cli_args(input_data)
|
|
108
|
+
|
|
109
|
+
# For Level 1/2 Python scripts, use direct execution for better performance
|
|
110
|
+
if script_path.endswith('.py') and context.sandbox_level != "3":
|
|
111
|
+
return self._exec_python_direct(context, skill_dir, script_path, args)
|
|
112
|
+
|
|
113
|
+
# Build command for skillbox exec
|
|
114
|
+
cmd = self._build_exec_command(context, skill_dir, script_path, input_data, args)
|
|
115
|
+
return self._run_subprocess(cmd, context, skill_dir)
|
|
116
|
+
|
|
117
|
+
def _build_run_command(
|
|
118
|
+
self,
|
|
119
|
+
context: ExecutionContext,
|
|
120
|
+
skill_dir: Path,
|
|
121
|
+
input_data: Dict[str, Any],
|
|
122
|
+
) -> List[str]:
|
|
123
|
+
"""Build command for skillbox run."""
|
|
124
|
+
# Convert to absolute path to avoid path issues
|
|
125
|
+
abs_skill_dir = Path(skill_dir).resolve()
|
|
126
|
+
cmd = [
|
|
127
|
+
self._binary_path,
|
|
128
|
+
"run",
|
|
129
|
+
str(abs_skill_dir),
|
|
130
|
+
json.dumps(input_data),
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
# Add sandbox level from context (NOT from instance variable)
|
|
134
|
+
cmd.extend(["--sandbox-level", context.sandbox_level])
|
|
135
|
+
|
|
136
|
+
if context.allow_network:
|
|
137
|
+
cmd.append("--allow-network")
|
|
138
|
+
|
|
139
|
+
cmd.extend(["--timeout", str(context.timeout)])
|
|
140
|
+
cmd.extend(["--max-memory", str(context.max_memory_mb)])
|
|
141
|
+
|
|
142
|
+
return cmd
|
|
143
|
+
|
|
144
|
+
def _build_exec_command(
|
|
145
|
+
self,
|
|
146
|
+
context: ExecutionContext,
|
|
147
|
+
skill_dir: Path,
|
|
148
|
+
script_path: str,
|
|
149
|
+
input_data: Dict[str, Any],
|
|
150
|
+
args: Optional[List[str]] = None,
|
|
151
|
+
) -> List[str]:
|
|
152
|
+
"""Build command for skillbox exec."""
|
|
153
|
+
# Convert to absolute path to avoid path issues
|
|
154
|
+
abs_skill_dir = Path(skill_dir).resolve()
|
|
155
|
+
cmd = [
|
|
156
|
+
self._binary_path,
|
|
157
|
+
"exec",
|
|
158
|
+
str(abs_skill_dir),
|
|
159
|
+
script_path,
|
|
160
|
+
json.dumps(input_data),
|
|
161
|
+
]
|
|
162
|
+
|
|
163
|
+
if args:
|
|
164
|
+
args_str = " ".join(args) if isinstance(args, list) else args
|
|
165
|
+
cmd.extend(["--args", args_str])
|
|
166
|
+
|
|
167
|
+
# Add sandbox level from context (NOT from instance variable)
|
|
168
|
+
cmd.extend(["--sandbox-level", context.sandbox_level])
|
|
169
|
+
|
|
170
|
+
if context.allow_network:
|
|
171
|
+
cmd.append("--allow-network")
|
|
172
|
+
|
|
173
|
+
cmd.extend(["--timeout", str(context.timeout)])
|
|
174
|
+
cmd.extend(["--max-memory", str(context.max_memory_mb)])
|
|
175
|
+
|
|
176
|
+
return cmd
|
|
177
|
+
|
|
178
|
+
def _run_subprocess(
|
|
179
|
+
self,
|
|
180
|
+
cmd: List[str],
|
|
181
|
+
context: ExecutionContext,
|
|
182
|
+
skill_dir: Path,
|
|
183
|
+
) -> ExecutionResult:
|
|
184
|
+
"""Run subprocess with the given command."""
|
|
185
|
+
env = self._build_env(context, skill_dir)
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
if context.sandbox_level == "3" and not context.confirmed:
|
|
189
|
+
# Level 3 without confirmation: allow stderr for prompts
|
|
190
|
+
result = subprocess.run(
|
|
191
|
+
cmd,
|
|
192
|
+
stdin=None,
|
|
193
|
+
stdout=subprocess.PIPE,
|
|
194
|
+
stderr=None,
|
|
195
|
+
text=True,
|
|
196
|
+
timeout=context.timeout,
|
|
197
|
+
env=env,
|
|
198
|
+
)
|
|
199
|
+
return self._parse_output(result.stdout, "", result.returncode)
|
|
200
|
+
else:
|
|
201
|
+
# Level 1/2 or confirmed: capture all output
|
|
202
|
+
result = subprocess.run(
|
|
203
|
+
cmd,
|
|
204
|
+
capture_output=True,
|
|
205
|
+
text=True,
|
|
206
|
+
timeout=context.timeout,
|
|
207
|
+
env=env,
|
|
208
|
+
)
|
|
209
|
+
return self._parse_output(result.stdout, result.stderr, result.returncode)
|
|
210
|
+
|
|
211
|
+
except subprocess.TimeoutExpired:
|
|
212
|
+
return ExecutionResult(
|
|
213
|
+
success=False,
|
|
214
|
+
error=f"Execution timed out after {context.timeout} seconds",
|
|
215
|
+
exit_code=-1,
|
|
216
|
+
)
|
|
217
|
+
except FileNotFoundError:
|
|
218
|
+
return ExecutionResult(
|
|
219
|
+
success=False,
|
|
220
|
+
error=f"skillbox binary not found at: {self._binary_path}",
|
|
221
|
+
exit_code=-1,
|
|
222
|
+
)
|
|
223
|
+
except Exception as e:
|
|
224
|
+
return ExecutionResult(
|
|
225
|
+
success=False,
|
|
226
|
+
error=f"Execution failed: {str(e)}",
|
|
227
|
+
exit_code=-1,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
def _build_env(
|
|
231
|
+
self,
|
|
232
|
+
context: ExecutionContext,
|
|
233
|
+
skill_dir: Path,
|
|
234
|
+
) -> Dict[str, str]:
|
|
235
|
+
"""Build environment variables for subprocess."""
|
|
236
|
+
env = os.environ.copy()
|
|
237
|
+
|
|
238
|
+
# Set sandbox level in environment (for consistency)
|
|
239
|
+
env["SKILLBOX_SANDBOX_LEVEL"] = context.sandbox_level
|
|
240
|
+
env["SKILLBOX_AUTO_APPROVE"] = "1" if context.auto_approve or context.confirmed else "1"
|
|
241
|
+
|
|
242
|
+
# Set skill-specific environment
|
|
243
|
+
env["SKILL_DIR"] = str(skill_dir)
|
|
244
|
+
env["SKILLBOX_TIMEOUT_SECS"] = str(context.timeout)
|
|
245
|
+
env["SKILLBOX_MAX_MEMORY_MB"] = str(context.max_memory_mb)
|
|
246
|
+
|
|
247
|
+
return env
|
|
248
|
+
|
|
249
|
+
def _exec_python_direct(
|
|
250
|
+
self,
|
|
251
|
+
context: ExecutionContext,
|
|
252
|
+
skill_dir: Path,
|
|
253
|
+
script_path: str,
|
|
254
|
+
args: Optional[List[str]] = None,
|
|
255
|
+
) -> ExecutionResult:
|
|
256
|
+
"""Execute Python script directly (for Level 1/2)."""
|
|
257
|
+
import sys
|
|
258
|
+
|
|
259
|
+
# Convert to absolute path to avoid path duplication issues
|
|
260
|
+
abs_skill_dir = Path(skill_dir).resolve()
|
|
261
|
+
full_script_path = abs_skill_dir / script_path
|
|
262
|
+
|
|
263
|
+
if not full_script_path.exists():
|
|
264
|
+
return ExecutionResult(
|
|
265
|
+
success=False,
|
|
266
|
+
error=f"Script not found: {full_script_path}",
|
|
267
|
+
exit_code=-1,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
cmd = [sys.executable, str(full_script_path)]
|
|
271
|
+
if args:
|
|
272
|
+
cmd.extend(args)
|
|
273
|
+
|
|
274
|
+
env = self._build_env(context, abs_skill_dir)
|
|
275
|
+
env["PYTHONPATH"] = str(abs_skill_dir)
|
|
276
|
+
|
|
277
|
+
try:
|
|
278
|
+
# Don't set cwd to skill_dir - let scripts run from project root
|
|
279
|
+
# Scripts can use SKILL_DIR env var to find their own location
|
|
280
|
+
# This allows scripts like skill-creator to work with relative paths
|
|
281
|
+
# that are relative to the project root, not the skill directory
|
|
282
|
+
result = subprocess.run(
|
|
283
|
+
cmd,
|
|
284
|
+
capture_output=True,
|
|
285
|
+
text=True,
|
|
286
|
+
timeout=context.timeout,
|
|
287
|
+
env=env,
|
|
288
|
+
)
|
|
289
|
+
return self._parse_output(result.stdout, result.stderr, result.returncode)
|
|
290
|
+
except subprocess.TimeoutExpired:
|
|
291
|
+
return ExecutionResult(
|
|
292
|
+
success=False,
|
|
293
|
+
error=f"Execution timed out after {context.timeout} seconds",
|
|
294
|
+
exit_code=-1,
|
|
295
|
+
)
|
|
296
|
+
except Exception as e:
|
|
297
|
+
return ExecutionResult(
|
|
298
|
+
success=False,
|
|
299
|
+
error=f"Execution failed: {str(e)}",
|
|
300
|
+
exit_code=-1,
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
def _convert_json_to_cli_args(self, input_data: Dict[str, Any]) -> List[str]:
|
|
304
|
+
"""Convert JSON input to CLI arguments.
|
|
305
|
+
|
|
306
|
+
Delegates to the shared utility function that properly handles
|
|
307
|
+
positional arguments like 'skill_name'.
|
|
308
|
+
"""
|
|
309
|
+
from .utils import convert_json_to_cli_args
|
|
310
|
+
return convert_json_to_cli_args(input_data)
|
|
311
|
+
|
|
312
|
+
def _parse_output(
|
|
313
|
+
self,
|
|
314
|
+
stdout: str,
|
|
315
|
+
stderr: str,
|
|
316
|
+
returncode: int,
|
|
317
|
+
) -> ExecutionResult:
|
|
318
|
+
"""Parse subprocess output into ExecutionResult."""
|
|
319
|
+
combined = stdout + stderr
|
|
320
|
+
|
|
321
|
+
# Try to parse JSON output
|
|
322
|
+
try:
|
|
323
|
+
# Look for JSON in output
|
|
324
|
+
for line in combined.split('\n'):
|
|
325
|
+
line = line.strip()
|
|
326
|
+
if line.startswith('{') and line.endswith('}'):
|
|
327
|
+
data = json.loads(line)
|
|
328
|
+
if isinstance(data, dict):
|
|
329
|
+
return ExecutionResult(
|
|
330
|
+
success=returncode == 0,
|
|
331
|
+
output=data,
|
|
332
|
+
exit_code=returncode,
|
|
333
|
+
stdout=stdout,
|
|
334
|
+
stderr=stderr,
|
|
335
|
+
)
|
|
336
|
+
except json.JSONDecodeError:
|
|
337
|
+
pass
|
|
338
|
+
|
|
339
|
+
# Return as plain text
|
|
340
|
+
if returncode == 0:
|
|
341
|
+
return ExecutionResult(
|
|
342
|
+
success=True,
|
|
343
|
+
output={"result": stdout.strip()} if stdout.strip() else None,
|
|
344
|
+
exit_code=returncode,
|
|
345
|
+
stdout=stdout,
|
|
346
|
+
stderr=stderr,
|
|
347
|
+
)
|
|
348
|
+
else:
|
|
349
|
+
error_msg = stderr.strip() if stderr.strip() else stdout.strip()
|
|
350
|
+
return ExecutionResult(
|
|
351
|
+
success=False,
|
|
352
|
+
error=f"Skill execution failed with exit code {returncode}: {error_msg}",
|
|
353
|
+
exit_code=returncode,
|
|
354
|
+
stdout=stdout,
|
|
355
|
+
stderr=stderr,
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
__all__ = ["UnifiedExecutor"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: skilllite
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: A lightweight Skills execution engine with LLM integration for LLM agents
|
|
5
5
|
Author-email: SkillLite Team <skilllite@example.com>
|
|
6
6
|
License: MIT
|
|
@@ -32,10 +32,16 @@ Provides-Extra: anthropic
|
|
|
32
32
|
Requires-Dist: anthropic>=0.18.0; extra == "anthropic"
|
|
33
33
|
Provides-Extra: mcp
|
|
34
34
|
Requires-Dist: mcp>=1.0.0; extra == "mcp"
|
|
35
|
+
Provides-Extra: langchain
|
|
36
|
+
Requires-Dist: langchain-core>=0.1.0; extra == "langchain"
|
|
37
|
+
Provides-Extra: llamaindex
|
|
38
|
+
Requires-Dist: llama-index-core>=0.10.0; extra == "llamaindex"
|
|
35
39
|
Provides-Extra: all
|
|
36
40
|
Requires-Dist: openai>=1.0.0; extra == "all"
|
|
37
41
|
Requires-Dist: anthropic>=0.18.0; extra == "all"
|
|
38
42
|
Requires-Dist: mcp>=1.0.0; extra == "all"
|
|
43
|
+
Requires-Dist: langchain-core>=0.1.0; extra == "all"
|
|
44
|
+
Requires-Dist: llama-index-core>=0.10.0; extra == "all"
|
|
39
45
|
Provides-Extra: dev
|
|
40
46
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
41
47
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
@@ -288,6 +294,97 @@ Enum for LLM provider formats:
|
|
|
288
294
|
- `ToolFormat.CLAUDE`
|
|
289
295
|
- `ToolFormat.OPENAI`
|
|
290
296
|
|
|
297
|
+
## Framework Adapters
|
|
298
|
+
|
|
299
|
+
SkillLite provides adapters for popular AI frameworks with security confirmation support.
|
|
300
|
+
|
|
301
|
+
### LangChain Integration
|
|
302
|
+
|
|
303
|
+
For LangChain/LangGraph integration, we recommend using the dedicated **[langchain-skilllite](https://pypi.org/project/langchain-skilllite/)** package:
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
pip install langchain-skilllite
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
from langchain_skilllite import SkillLiteToolkit
|
|
311
|
+
from langchain_openai import ChatOpenAI
|
|
312
|
+
from langgraph.prebuilt import create_react_agent
|
|
313
|
+
|
|
314
|
+
# Load all skills from a directory as LangChain tools
|
|
315
|
+
tools = SkillLiteToolkit.from_directory("./skills")
|
|
316
|
+
|
|
317
|
+
# Create a LangGraph agent
|
|
318
|
+
agent = create_react_agent(ChatOpenAI(model="gpt-4"), tools)
|
|
319
|
+
|
|
320
|
+
# Run the agent
|
|
321
|
+
result = agent.invoke({
|
|
322
|
+
"messages": [("user", "Convert 'hello world' to uppercase")]
|
|
323
|
+
})
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
With security confirmation (sandbox_level=3):
|
|
327
|
+
|
|
328
|
+
```python
|
|
329
|
+
def confirm_execution(report: str, scan_id: str) -> bool:
|
|
330
|
+
print(report)
|
|
331
|
+
return input("Continue? [y/N]: ").lower() == 'y'
|
|
332
|
+
|
|
333
|
+
tools = SkillLiteToolkit.from_directory(
|
|
334
|
+
"./skills",
|
|
335
|
+
sandbox_level=3, # 1=no sandbox, 2=sandbox only, 3=sandbox+scan
|
|
336
|
+
confirmation_callback=confirm_execution
|
|
337
|
+
)
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
For more details, see the [langchain-skilllite documentation](../langchain-skilllite/README.md).
|
|
341
|
+
|
|
342
|
+
**Alternative**: You can also use the built-in adapter:
|
|
343
|
+
|
|
344
|
+
```python
|
|
345
|
+
from skilllite import SkillManager
|
|
346
|
+
from skilllite.core.adapters.langchain import SkillLiteToolkit
|
|
347
|
+
|
|
348
|
+
manager = SkillManager(skills_dir="./skills")
|
|
349
|
+
tools = SkillLiteToolkit.from_manager(manager).get_tools()
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### LlamaIndex Integration
|
|
353
|
+
|
|
354
|
+
```python
|
|
355
|
+
from skilllite import SkillManager
|
|
356
|
+
from skilllite.core.adapters.llamaindex import SkillLiteToolSpec
|
|
357
|
+
|
|
358
|
+
manager = SkillManager(skills_dir="./skills")
|
|
359
|
+
|
|
360
|
+
# Basic usage
|
|
361
|
+
tool_spec = SkillLiteToolSpec.from_manager(manager)
|
|
362
|
+
tools = tool_spec.to_tool_list()
|
|
363
|
+
|
|
364
|
+
# With security confirmation
|
|
365
|
+
def confirm(report: str, scan_id: str) -> bool:
|
|
366
|
+
print(report)
|
|
367
|
+
return input("Continue? [y/N]: ").lower() == 'y'
|
|
368
|
+
|
|
369
|
+
tool_spec = SkillLiteToolSpec.from_manager(
|
|
370
|
+
manager,
|
|
371
|
+
sandbox_level=3,
|
|
372
|
+
confirmation_callback=confirm
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
# Use with LlamaIndex agent
|
|
376
|
+
from llama_index.core.agent import ReActAgent
|
|
377
|
+
agent = ReActAgent.from_tools(tools, llm=llm)
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Security Levels
|
|
381
|
+
|
|
382
|
+
| Level | Description |
|
|
383
|
+
|-------|-------------|
|
|
384
|
+
| 1 | No sandbox - direct execution |
|
|
385
|
+
| 2 | Sandbox isolation only |
|
|
386
|
+
| 3 | Sandbox + static security scan (requires confirmation for high-severity issues) |
|
|
387
|
+
|
|
291
388
|
## OpenCode Integration
|
|
292
389
|
|
|
293
390
|
SkillLite can be integrated with [OpenCode](https://github.com/opencode-ai/opencode) as an MCP (Model Context Protocol) server, providing secure sandbox execution capabilities.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
skilllite/__init__.py,sha256=D_6otb8PNLUqpxlsp_7WtLJlX7JDziOxGBc8iQhER9Q,3984
|
|
2
2
|
skilllite/analyzer.py,sha256=YG-ZJ6bO59pkP8LGECjcyCDPpH6wUYOm5QGx6et-ItA,14504
|
|
3
3
|
skilllite/builtin_tools.py,sha256=MGIlMlYTfj590PCY4ngbQteDIryfH7sDoKQyrDTH6L8,8067
|
|
4
|
-
skilllite/quick.py,sha256=
|
|
4
|
+
skilllite/quick.py,sha256=_NWMBMywn0EK0dNeicbz37-M3UfnC7di3wjdBB0rPA4,16768
|
|
5
5
|
skilllite/validation.py,sha256=K3Hn6VKT7aybdKX8GXSRaxLbKmyVAnMYmP9cqh-D4s4,4308
|
|
6
6
|
skilllite/cli/__init__.py,sha256=PATRR5ZeAoB9H4gzVgw_5VAiP1GybZOuiAa1rOKFdW0,611
|
|
7
7
|
skilllite/cli/__main__.py,sha256=69kpU78u45_64qSgv1v6x3m3xxaWjHwb-QdjbpxXZCo,152
|
|
@@ -10,29 +10,36 @@ skilllite/cli/main.py,sha256=cyLb6l7dDuvmu7ZCJhUsU9MeTzQo0qKLrnC75EKf5Sw,3983
|
|
|
10
10
|
skilllite/cli/mcp.py,sha256=Kp-Z6aRZ7NB3T-y-b56jx6AnRyPOkfzfFQmBxYAB23Q,767
|
|
11
11
|
skilllite/cli/integrations/__init__.py,sha256=-NWqUaDEPCRpykfDOvl2zfpUfLIH3UxG1qUbmTvb92o,120
|
|
12
12
|
skilllite/cli/integrations/opencode.py,sha256=wT7rCqOkmX9GdhI994-0HhUtZEJ3PELPOC9PRscQDls,10521
|
|
13
|
-
skilllite/core/__init__.py,sha256=
|
|
13
|
+
skilllite/core/__init__.py,sha256=N6ZEqHBnmnhtZksqVDayCZ0oPEM_rACRsGhzNm191tU,2089
|
|
14
14
|
skilllite/core/executor.py,sha256=eNPcVKxKcLUjHJ7K2hstKtxobZiDIvPjk2IhbZv3nxo,6517
|
|
15
|
-
skilllite/core/handler.py,sha256=
|
|
16
|
-
skilllite/core/loops.py,sha256=
|
|
17
|
-
skilllite/core/manager.py,sha256=
|
|
18
|
-
skilllite/core/metadata.py,sha256=
|
|
15
|
+
skilllite/core/handler.py,sha256=wVucut7ZfDxgCa3b6A99hxq-6OycRRQaMfGyBuxNMWk,16623
|
|
16
|
+
skilllite/core/loops.py,sha256=MVPMUOYgJT6VEvtAavB1PR-6kBh5kE7BlFE4ndBRgzE,39195
|
|
17
|
+
skilllite/core/manager.py,sha256=t87Qyql2YZQrUTiGAbI3q-8LF5Hiuy81HUvKCzwWtyQ,22451
|
|
18
|
+
skilllite/core/metadata.py,sha256=N-EOtPIRVM8sCAvvd75ogpqE3w2qIN-WKQ562mKHtPI,11607
|
|
19
19
|
skilllite/core/prompt_builder.py,sha256=rsubLzien9lKjOqM6iTn5nourPnz1-QQGzljiO0Z_3I,12844
|
|
20
20
|
skilllite/core/registry.py,sha256=AeljsV1uuYEJFGjpSwYxU6JGJ6EUuW8uNO3KirzHkuQ,6622
|
|
21
|
+
skilllite/core/security.py,sha256=Z91v4lltHDTJsk2tM1Ezu2TwjmZiWUqKlFKDABZEv2k,14185
|
|
21
22
|
skilllite/core/skill_info.py,sha256=4_jsArmn1e7jEUXDEkmDAzjSI_903sdpfNLHOp1t1-M,6079
|
|
22
23
|
skilllite/core/tool_builder.py,sha256=k28ze50h7AjBiEfgKR6Y4DUFIGZJ1sCYbHEk_VkYl7w,12866
|
|
23
24
|
skilllite/core/tools.py,sha256=3RiAnZ_7S7iE6ZHXTPNKmAYQFY8SBUEzcQJZA0tZmX8,8168
|
|
25
|
+
skilllite/core/adapters/__init__.py,sha256=cvPkwBxRCyoNi4GWkAYwsoNuJqR-BdziLm83EEkBhio,2698
|
|
26
|
+
skilllite/core/adapters/langchain.py,sha256=osacceLNv3mo5x8Oe9obzefGpH1hhxhbXqMavSKAo5Y,13169
|
|
27
|
+
skilllite/core/adapters/llamaindex.py,sha256=El-oQxzniMv9rPVdH6udmzrzz_hE50rd1goVWxzib68,9037
|
|
24
28
|
skilllite/mcp/__init__.py,sha256=xrpoWy9zs8IJkW6fkkR4XjQrITo5PWvFW9NkmK8E2Fg,1028
|
|
25
|
-
skilllite/mcp/server.py,sha256=
|
|
29
|
+
skilllite/mcp/server.py,sha256=VsdR36dF4YR3l5IUEsE7NfXd_zppy5J6egRVs_PhfSM,47934
|
|
26
30
|
skilllite/sandbox/__init__.py,sha256=6LPisVXB94SJ-C34nBAxd05NGEZR4PPxz2xaDpX4qEk,918
|
|
27
31
|
skilllite/sandbox/base.py,sha256=-Ul2QufoQ3myJNzBOEdv30A2HVSR9Oe4zEqpe9oJGqY,2628
|
|
28
32
|
skilllite/sandbox/config.py,sha256=eA3ceou6jF4S5kptv6cmIGKynQTaGDx3_vRrkJGQaAE,8207
|
|
33
|
+
skilllite/sandbox/context.py,sha256=3C8Z3RiXJCcFI-Sx4nT9Dkf0d38BPzRTCsAbj-NODWc,5982
|
|
34
|
+
skilllite/sandbox/execution_service.py,sha256=vQERxKzRk4rc50qmuPQfR9OY62bybm3KH_Q-kuzCl8s,8937
|
|
35
|
+
skilllite/sandbox/unified_executor.py,sha256=4gSwTd1-EwLWSZ1dyM3eXauhyTd32vCkS6sVr6m3qYM,12229
|
|
29
36
|
skilllite/sandbox/utils.py,sha256=o5AZVkhqIcZE9veUy7FTfN7uMHVWJJLYXs7gYIXsptg,2572
|
|
30
37
|
skilllite/sandbox/skillbox/__init__.py,sha256=SFnNlLnnAc_NEEkqOkDbxYNfXROL7n1S09K_HFI_BOE,866
|
|
31
38
|
skilllite/sandbox/skillbox/binary.py,sha256=TNKQamWucMw-J6L5BjAasNpYqhBcBpHwQPMqjUOkgp4,11838
|
|
32
|
-
skilllite/sandbox/skillbox/executor.py,sha256=
|
|
33
|
-
skilllite-0.1.
|
|
34
|
-
skilllite-0.1.
|
|
35
|
-
skilllite-0.1.
|
|
36
|
-
skilllite-0.1.
|
|
37
|
-
skilllite-0.1.
|
|
38
|
-
skilllite-0.1.
|
|
39
|
+
skilllite/sandbox/skillbox/executor.py,sha256=xobVd5JmzJnwouVOO3arvN6IuC5VH28SXPqKHCXalHM,27175
|
|
40
|
+
skilllite-0.1.2.dist-info/licenses/LICENSE,sha256=ESBZ3GI5LkrbA2s5jZUQDqdcQ8oRlmk04aIYh34cOsw,1071
|
|
41
|
+
skilllite-0.1.2.dist-info/METADATA,sha256=A56Fqpe_U_BY5vls_YUSxnvqvYb4lunbyQRPaB1Gguc,12706
|
|
42
|
+
skilllite-0.1.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
43
|
+
skilllite-0.1.2.dist-info/entry_points.txt,sha256=H-meSKN3XzctxMvY6fa-YjAWyzs9t_0IjujOfumn4Wk,84
|
|
44
|
+
skilllite-0.1.2.dist-info/top_level.txt,sha256=aqo-9FEJuBbFT7WGE3aycTj27OxyQPCN6TuZtOcClAI,10
|
|
45
|
+
skilllite-0.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|