sailor-remote-agent 0.1.0__tar.gz
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.
- sailor_remote_agent-0.1.0/MANIFEST.in +3 -0
- sailor_remote_agent-0.1.0/PKG-INFO +168 -0
- sailor_remote_agent-0.1.0/README.md +160 -0
- sailor_remote_agent-0.1.0/app.py +101 -0
- sailor_remote_agent-0.1.0/pyproject.toml +22 -0
- sailor_remote_agent-0.1.0/sailor_remote_agent.egg-info/PKG-INFO +168 -0
- sailor_remote_agent-0.1.0/sailor_remote_agent.egg-info/SOURCES.txt +12 -0
- sailor_remote_agent-0.1.0/sailor_remote_agent.egg-info/dependency_links.txt +1 -0
- sailor_remote_agent-0.1.0/sailor_remote_agent.egg-info/entry_points.txt +2 -0
- sailor_remote_agent-0.1.0/sailor_remote_agent.egg-info/requires.txt +1 -0
- sailor_remote_agent-0.1.0/sailor_remote_agent.egg-info/top_level.txt +1 -0
- sailor_remote_agent-0.1.0/setup.cfg +4 -0
- sailor_remote_agent-0.1.0/views/answer.tpl +54 -0
- sailor_remote_agent-0.1.0/views/result.tpl +60 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sailor-remote-agent
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Minimal Bottle web app for clipboard and evaluation operations
|
|
5
|
+
Requires-Python: >=3.7
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: bottle>=0.12.0
|
|
8
|
+
|
|
9
|
+
# Remote Agent
|
|
10
|
+
|
|
11
|
+
A minimal Python web app + API built with Bottle for clipboard operations and safe expression evaluation.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **GET /ping** - Health check endpoint returning `{pong: "ok"}`
|
|
16
|
+
- **POST /clipboard** - Accepts JSON with `content` field, stores in memory, and executes xclip command
|
|
17
|
+
- **POST /evaluate** - Safely evaluates Python literals using `ast.literal_eval` and stores results
|
|
18
|
+
- **GET /answer** - Renders HTML showing the latest evaluation result
|
|
19
|
+
- **GET /result** - Renders HTML showing all evaluation results
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
### Development Installation
|
|
24
|
+
|
|
25
|
+
1. Install dependencies:
|
|
26
|
+
```bash
|
|
27
|
+
pip install bottle
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
2. Run the app directly:
|
|
31
|
+
```bash
|
|
32
|
+
python app.py
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Package Installation
|
|
36
|
+
|
|
37
|
+
1. Build the package:
|
|
38
|
+
```bash
|
|
39
|
+
pip install build
|
|
40
|
+
python -m build
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This will create `dist/remote-agent-0.1.0.tar.gz` and wheel files.
|
|
44
|
+
|
|
45
|
+
2. Install locally:
|
|
46
|
+
```bash
|
|
47
|
+
pip install dist/remote-agent-0.1.0*.whl
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
3. Run using the console script:
|
|
51
|
+
```bash
|
|
52
|
+
remote-agent
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The app will start on `http://localhost:8080`
|
|
56
|
+
|
|
57
|
+
## Publishing to PyPI
|
|
58
|
+
|
|
59
|
+
### Prerequisites
|
|
60
|
+
|
|
61
|
+
1. Create accounts:
|
|
62
|
+
- PyPI account: https://pypi.org/account/register/
|
|
63
|
+
- TestPyPI account: https://test.pypi.org/account/register/
|
|
64
|
+
|
|
65
|
+
2. Install required tools:
|
|
66
|
+
```bash
|
|
67
|
+
pip install build twine
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Build and Upload Steps
|
|
71
|
+
|
|
72
|
+
1. **Update version** in `pyproject.toml` if needed:
|
|
73
|
+
```toml
|
|
74
|
+
version = "0.1.0"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
2. **Build the package**:
|
|
78
|
+
```bash
|
|
79
|
+
python -m build
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This creates:
|
|
83
|
+
- `dist/remote-agent-0.1.0.tar.gz` (source distribution)
|
|
84
|
+
- `dist/remote-agent-0.1.0-py3-none-any.whl` (wheel)
|
|
85
|
+
|
|
86
|
+
3. **Test on TestPyPI first** (recommended):
|
|
87
|
+
```bash
|
|
88
|
+
twine upload --repository testpypi dist/*
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
You'll be prompted for:
|
|
92
|
+
- Username: your TestPyPI username
|
|
93
|
+
- Password: your TestPyPI API token (create at https://test.pypi.org/manage/account/token/)
|
|
94
|
+
|
|
95
|
+
4. **Test installation from TestPyPI**:
|
|
96
|
+
```bash
|
|
97
|
+
pip install --index-url https://test.pypi.org/simple/ remote-agent
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
5. **Upload to PyPI** (production):
|
|
101
|
+
```bash
|
|
102
|
+
twine upload dist/*
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
You'll be prompted for:
|
|
106
|
+
- Username: `__token__`
|
|
107
|
+
- Password: your PyPI API token (create at https://pypi.org/manage/account/token/)
|
|
108
|
+
|
|
109
|
+
6. **Verify installation**:
|
|
110
|
+
```bash
|
|
111
|
+
pip install remote-agent
|
|
112
|
+
remote-agent
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Project Structure
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
.
|
|
119
|
+
├── app.py # Main application
|
|
120
|
+
├── __init__.py
|
|
121
|
+
├── views/
|
|
122
|
+
│ ├── answer.tpl # Template for latest result
|
|
123
|
+
│ └── result.tpl # Template for all results
|
|
124
|
+
├── pyproject.toml # Package configuration
|
|
125
|
+
└── README.md # This file
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## API Usage Examples
|
|
129
|
+
|
|
130
|
+
### Health Check
|
|
131
|
+
```bash
|
|
132
|
+
curl http://localhost:8080/ping
|
|
133
|
+
# Response: {"pong": "ok"}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Clipboard Operation
|
|
137
|
+
```bash
|
|
138
|
+
curl -X POST http://localhost:8080/clipboard \
|
|
139
|
+
-H "Content-Type: application/json" \
|
|
140
|
+
-d '{"content": "Hello World"}'
|
|
141
|
+
# Response: {"status": "ok"}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Evaluate Expression
|
|
145
|
+
```bash
|
|
146
|
+
curl -X POST http://localhost:8080/evaluate \
|
|
147
|
+
-H "Content-Type: application/json" \
|
|
148
|
+
-d '{"expr": "[1, 2, 3]"}'
|
|
149
|
+
# Response: {"status": "ok", "result": [1, 2, 3]}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### View Results
|
|
153
|
+
- Open `http://localhost:8080/answer` in browser (latest result)
|
|
154
|
+
- Open `http://localhost:8080/result` in browser (all results)
|
|
155
|
+
|
|
156
|
+
## Notes
|
|
157
|
+
|
|
158
|
+
- Storage is in-memory (data is lost on restart)
|
|
159
|
+
- Evaluation uses `ast.literal_eval` for safety (only Python literals, no code execution)
|
|
160
|
+
- Clipboard command requires `xclip` to be installed on the system
|
|
161
|
+
- The app runs on `localhost:8080` by default
|
|
162
|
+
|
|
163
|
+
## Requirements
|
|
164
|
+
|
|
165
|
+
- Python 3.7+
|
|
166
|
+
- Bottle (only dependency)
|
|
167
|
+
- xclip (for clipboard functionality on Linux)
|
|
168
|
+
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Remote Agent
|
|
2
|
+
|
|
3
|
+
A minimal Python web app + API built with Bottle for clipboard operations and safe expression evaluation.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **GET /ping** - Health check endpoint returning `{pong: "ok"}`
|
|
8
|
+
- **POST /clipboard** - Accepts JSON with `content` field, stores in memory, and executes xclip command
|
|
9
|
+
- **POST /evaluate** - Safely evaluates Python literals using `ast.literal_eval` and stores results
|
|
10
|
+
- **GET /answer** - Renders HTML showing the latest evaluation result
|
|
11
|
+
- **GET /result** - Renders HTML showing all evaluation results
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
### Development Installation
|
|
16
|
+
|
|
17
|
+
1. Install dependencies:
|
|
18
|
+
```bash
|
|
19
|
+
pip install bottle
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
2. Run the app directly:
|
|
23
|
+
```bash
|
|
24
|
+
python app.py
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Package Installation
|
|
28
|
+
|
|
29
|
+
1. Build the package:
|
|
30
|
+
```bash
|
|
31
|
+
pip install build
|
|
32
|
+
python -m build
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This will create `dist/remote-agent-0.1.0.tar.gz` and wheel files.
|
|
36
|
+
|
|
37
|
+
2. Install locally:
|
|
38
|
+
```bash
|
|
39
|
+
pip install dist/remote-agent-0.1.0*.whl
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
3. Run using the console script:
|
|
43
|
+
```bash
|
|
44
|
+
remote-agent
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The app will start on `http://localhost:8080`
|
|
48
|
+
|
|
49
|
+
## Publishing to PyPI
|
|
50
|
+
|
|
51
|
+
### Prerequisites
|
|
52
|
+
|
|
53
|
+
1. Create accounts:
|
|
54
|
+
- PyPI account: https://pypi.org/account/register/
|
|
55
|
+
- TestPyPI account: https://test.pypi.org/account/register/
|
|
56
|
+
|
|
57
|
+
2. Install required tools:
|
|
58
|
+
```bash
|
|
59
|
+
pip install build twine
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Build and Upload Steps
|
|
63
|
+
|
|
64
|
+
1. **Update version** in `pyproject.toml` if needed:
|
|
65
|
+
```toml
|
|
66
|
+
version = "0.1.0"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
2. **Build the package**:
|
|
70
|
+
```bash
|
|
71
|
+
python -m build
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This creates:
|
|
75
|
+
- `dist/remote-agent-0.1.0.tar.gz` (source distribution)
|
|
76
|
+
- `dist/remote-agent-0.1.0-py3-none-any.whl` (wheel)
|
|
77
|
+
|
|
78
|
+
3. **Test on TestPyPI first** (recommended):
|
|
79
|
+
```bash
|
|
80
|
+
twine upload --repository testpypi dist/*
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
You'll be prompted for:
|
|
84
|
+
- Username: your TestPyPI username
|
|
85
|
+
- Password: your TestPyPI API token (create at https://test.pypi.org/manage/account/token/)
|
|
86
|
+
|
|
87
|
+
4. **Test installation from TestPyPI**:
|
|
88
|
+
```bash
|
|
89
|
+
pip install --index-url https://test.pypi.org/simple/ remote-agent
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
5. **Upload to PyPI** (production):
|
|
93
|
+
```bash
|
|
94
|
+
twine upload dist/*
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
You'll be prompted for:
|
|
98
|
+
- Username: `__token__`
|
|
99
|
+
- Password: your PyPI API token (create at https://pypi.org/manage/account/token/)
|
|
100
|
+
|
|
101
|
+
6. **Verify installation**:
|
|
102
|
+
```bash
|
|
103
|
+
pip install remote-agent
|
|
104
|
+
remote-agent
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Project Structure
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
.
|
|
111
|
+
├── app.py # Main application
|
|
112
|
+
├── __init__.py
|
|
113
|
+
├── views/
|
|
114
|
+
│ ├── answer.tpl # Template for latest result
|
|
115
|
+
│ └── result.tpl # Template for all results
|
|
116
|
+
├── pyproject.toml # Package configuration
|
|
117
|
+
└── README.md # This file
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## API Usage Examples
|
|
121
|
+
|
|
122
|
+
### Health Check
|
|
123
|
+
```bash
|
|
124
|
+
curl http://localhost:8080/ping
|
|
125
|
+
# Response: {"pong": "ok"}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Clipboard Operation
|
|
129
|
+
```bash
|
|
130
|
+
curl -X POST http://localhost:8080/clipboard \
|
|
131
|
+
-H "Content-Type: application/json" \
|
|
132
|
+
-d '{"content": "Hello World"}'
|
|
133
|
+
# Response: {"status": "ok"}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Evaluate Expression
|
|
137
|
+
```bash
|
|
138
|
+
curl -X POST http://localhost:8080/evaluate \
|
|
139
|
+
-H "Content-Type: application/json" \
|
|
140
|
+
-d '{"expr": "[1, 2, 3]"}'
|
|
141
|
+
# Response: {"status": "ok", "result": [1, 2, 3]}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### View Results
|
|
145
|
+
- Open `http://localhost:8080/answer` in browser (latest result)
|
|
146
|
+
- Open `http://localhost:8080/result` in browser (all results)
|
|
147
|
+
|
|
148
|
+
## Notes
|
|
149
|
+
|
|
150
|
+
- Storage is in-memory (data is lost on restart)
|
|
151
|
+
- Evaluation uses `ast.literal_eval` for safety (only Python literals, no code execution)
|
|
152
|
+
- Clipboard command requires `xclip` to be installed on the system
|
|
153
|
+
- The app runs on `localhost:8080` by default
|
|
154
|
+
|
|
155
|
+
## Requirements
|
|
156
|
+
|
|
157
|
+
- Python 3.7+
|
|
158
|
+
- Bottle (only dependency)
|
|
159
|
+
- xclip (for clipboard functionality on Linux)
|
|
160
|
+
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Minimal Bottle web app for clipboard and evaluation operations."""
|
|
3
|
+
|
|
4
|
+
import ast
|
|
5
|
+
import subprocess
|
|
6
|
+
from bottle import Bottle, request, response, template, run
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
app = Bottle()
|
|
10
|
+
|
|
11
|
+
# In-memory storage
|
|
12
|
+
clipboard_history = []
|
|
13
|
+
evaluation_results = []
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@app.route('/ping', method='GET')
|
|
17
|
+
def ping():
|
|
18
|
+
"""Health check endpoint."""
|
|
19
|
+
response.content_type = 'application/json'
|
|
20
|
+
return {'pong': 'ok'}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@app.route('/clipboard', method='POST')
|
|
24
|
+
def clipboard():
|
|
25
|
+
"""Store clipboard content and execute xclip command."""
|
|
26
|
+
try:
|
|
27
|
+
data = request.json
|
|
28
|
+
if not data or 'content' not in data:
|
|
29
|
+
response.status = 400
|
|
30
|
+
return {'error': 'Missing content field'}
|
|
31
|
+
|
|
32
|
+
clipboard_content = data['content']
|
|
33
|
+
# Replace single quotes with escaped version
|
|
34
|
+
clipboard_content = clipboard_content.replace("'", "'\\''")
|
|
35
|
+
|
|
36
|
+
# Execute xclip command
|
|
37
|
+
command = f"echo -n '{clipboard_content}' | xclip -selection clipboard &"
|
|
38
|
+
subprocess.Popen(command, shell=True)
|
|
39
|
+
|
|
40
|
+
response.content_type = 'application/json'
|
|
41
|
+
return {'status': 'ok'}
|
|
42
|
+
except Exception as e:
|
|
43
|
+
response.status = 500
|
|
44
|
+
return {'error': str(e)}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@app.route('/evaluate', method='POST')
|
|
48
|
+
def evaluate():
|
|
49
|
+
"""Safely evaluate Python literal expression."""
|
|
50
|
+
try:
|
|
51
|
+
data = request.json
|
|
52
|
+
if not data or 'expr' not in data:
|
|
53
|
+
response.status = 400
|
|
54
|
+
return {'error': 'Missing expr field'}
|
|
55
|
+
|
|
56
|
+
expr = data['expr']
|
|
57
|
+
# Safely evaluate using ast.literal_eval
|
|
58
|
+
result = ast.literal_eval(expr)
|
|
59
|
+
|
|
60
|
+
# Store result
|
|
61
|
+
evaluation_results.append({
|
|
62
|
+
'expr': expr,
|
|
63
|
+
'result': result
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
response.content_type = 'application/json'
|
|
67
|
+
return {'status': 'ok', 'result': result}
|
|
68
|
+
except (ValueError, SyntaxError) as e:
|
|
69
|
+
response.status = 400
|
|
70
|
+
return {'error': f'Invalid expression: {str(e)}'}
|
|
71
|
+
except Exception as e:
|
|
72
|
+
response.status = 500
|
|
73
|
+
return {'error': str(e)}
|
|
74
|
+
|
|
75
|
+
@app.route('/answer', method='GET')
|
|
76
|
+
def answer():
|
|
77
|
+
"""Render HTML showing the latest evaluation result."""
|
|
78
|
+
if not evaluation_results:
|
|
79
|
+
latest = None
|
|
80
|
+
else:
|
|
81
|
+
latest = evaluation_results[-1]
|
|
82
|
+
return template('answer', latest=latest)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@app.route('/result', method='GET')
|
|
86
|
+
def result():
|
|
87
|
+
"""Render HTML showing all evaluation results."""
|
|
88
|
+
return template('result', results=evaluation_results)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def main():
|
|
92
|
+
"""Entry point for console script."""
|
|
93
|
+
# Set template lookup path to views directory
|
|
94
|
+
views_path = os.path.join(os.path.dirname(__file__), 'views')
|
|
95
|
+
app.template_lookup = [views_path]
|
|
96
|
+
# Use port 8081 if 8080 is unavailable, or allow override via environment variable
|
|
97
|
+
run(app, host='localhost', port=8080, debug=True)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
if __name__ == '__main__':
|
|
101
|
+
main()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sailor-remote-agent"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Minimal Bottle web app for clipboard and evaluation operations"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.7"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"bottle>=0.12.0",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
sailor-remote-agent = "app:main"
|
|
17
|
+
|
|
18
|
+
[tool.setuptools]
|
|
19
|
+
py-modules = ["app"]
|
|
20
|
+
|
|
21
|
+
[tool.setuptools.package-data]
|
|
22
|
+
"*" = ["views/*.tpl"]
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sailor-remote-agent
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Minimal Bottle web app for clipboard and evaluation operations
|
|
5
|
+
Requires-Python: >=3.7
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: bottle>=0.12.0
|
|
8
|
+
|
|
9
|
+
# Remote Agent
|
|
10
|
+
|
|
11
|
+
A minimal Python web app + API built with Bottle for clipboard operations and safe expression evaluation.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **GET /ping** - Health check endpoint returning `{pong: "ok"}`
|
|
16
|
+
- **POST /clipboard** - Accepts JSON with `content` field, stores in memory, and executes xclip command
|
|
17
|
+
- **POST /evaluate** - Safely evaluates Python literals using `ast.literal_eval` and stores results
|
|
18
|
+
- **GET /answer** - Renders HTML showing the latest evaluation result
|
|
19
|
+
- **GET /result** - Renders HTML showing all evaluation results
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
### Development Installation
|
|
24
|
+
|
|
25
|
+
1. Install dependencies:
|
|
26
|
+
```bash
|
|
27
|
+
pip install bottle
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
2. Run the app directly:
|
|
31
|
+
```bash
|
|
32
|
+
python app.py
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Package Installation
|
|
36
|
+
|
|
37
|
+
1. Build the package:
|
|
38
|
+
```bash
|
|
39
|
+
pip install build
|
|
40
|
+
python -m build
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This will create `dist/remote-agent-0.1.0.tar.gz` and wheel files.
|
|
44
|
+
|
|
45
|
+
2. Install locally:
|
|
46
|
+
```bash
|
|
47
|
+
pip install dist/remote-agent-0.1.0*.whl
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
3. Run using the console script:
|
|
51
|
+
```bash
|
|
52
|
+
remote-agent
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The app will start on `http://localhost:8080`
|
|
56
|
+
|
|
57
|
+
## Publishing to PyPI
|
|
58
|
+
|
|
59
|
+
### Prerequisites
|
|
60
|
+
|
|
61
|
+
1. Create accounts:
|
|
62
|
+
- PyPI account: https://pypi.org/account/register/
|
|
63
|
+
- TestPyPI account: https://test.pypi.org/account/register/
|
|
64
|
+
|
|
65
|
+
2. Install required tools:
|
|
66
|
+
```bash
|
|
67
|
+
pip install build twine
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Build and Upload Steps
|
|
71
|
+
|
|
72
|
+
1. **Update version** in `pyproject.toml` if needed:
|
|
73
|
+
```toml
|
|
74
|
+
version = "0.1.0"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
2. **Build the package**:
|
|
78
|
+
```bash
|
|
79
|
+
python -m build
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This creates:
|
|
83
|
+
- `dist/remote-agent-0.1.0.tar.gz` (source distribution)
|
|
84
|
+
- `dist/remote-agent-0.1.0-py3-none-any.whl` (wheel)
|
|
85
|
+
|
|
86
|
+
3. **Test on TestPyPI first** (recommended):
|
|
87
|
+
```bash
|
|
88
|
+
twine upload --repository testpypi dist/*
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
You'll be prompted for:
|
|
92
|
+
- Username: your TestPyPI username
|
|
93
|
+
- Password: your TestPyPI API token (create at https://test.pypi.org/manage/account/token/)
|
|
94
|
+
|
|
95
|
+
4. **Test installation from TestPyPI**:
|
|
96
|
+
```bash
|
|
97
|
+
pip install --index-url https://test.pypi.org/simple/ remote-agent
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
5. **Upload to PyPI** (production):
|
|
101
|
+
```bash
|
|
102
|
+
twine upload dist/*
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
You'll be prompted for:
|
|
106
|
+
- Username: `__token__`
|
|
107
|
+
- Password: your PyPI API token (create at https://pypi.org/manage/account/token/)
|
|
108
|
+
|
|
109
|
+
6. **Verify installation**:
|
|
110
|
+
```bash
|
|
111
|
+
pip install remote-agent
|
|
112
|
+
remote-agent
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Project Structure
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
.
|
|
119
|
+
├── app.py # Main application
|
|
120
|
+
├── __init__.py
|
|
121
|
+
├── views/
|
|
122
|
+
│ ├── answer.tpl # Template for latest result
|
|
123
|
+
│ └── result.tpl # Template for all results
|
|
124
|
+
├── pyproject.toml # Package configuration
|
|
125
|
+
└── README.md # This file
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## API Usage Examples
|
|
129
|
+
|
|
130
|
+
### Health Check
|
|
131
|
+
```bash
|
|
132
|
+
curl http://localhost:8080/ping
|
|
133
|
+
# Response: {"pong": "ok"}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Clipboard Operation
|
|
137
|
+
```bash
|
|
138
|
+
curl -X POST http://localhost:8080/clipboard \
|
|
139
|
+
-H "Content-Type: application/json" \
|
|
140
|
+
-d '{"content": "Hello World"}'
|
|
141
|
+
# Response: {"status": "ok"}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Evaluate Expression
|
|
145
|
+
```bash
|
|
146
|
+
curl -X POST http://localhost:8080/evaluate \
|
|
147
|
+
-H "Content-Type: application/json" \
|
|
148
|
+
-d '{"expr": "[1, 2, 3]"}'
|
|
149
|
+
# Response: {"status": "ok", "result": [1, 2, 3]}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### View Results
|
|
153
|
+
- Open `http://localhost:8080/answer` in browser (latest result)
|
|
154
|
+
- Open `http://localhost:8080/result` in browser (all results)
|
|
155
|
+
|
|
156
|
+
## Notes
|
|
157
|
+
|
|
158
|
+
- Storage is in-memory (data is lost on restart)
|
|
159
|
+
- Evaluation uses `ast.literal_eval` for safety (only Python literals, no code execution)
|
|
160
|
+
- Clipboard command requires `xclip` to be installed on the system
|
|
161
|
+
- The app runs on `localhost:8080` by default
|
|
162
|
+
|
|
163
|
+
## Requirements
|
|
164
|
+
|
|
165
|
+
- Python 3.7+
|
|
166
|
+
- Bottle (only dependency)
|
|
167
|
+
- xclip (for clipboard functionality on Linux)
|
|
168
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
MANIFEST.in
|
|
2
|
+
README.md
|
|
3
|
+
app.py
|
|
4
|
+
pyproject.toml
|
|
5
|
+
sailor_remote_agent.egg-info/PKG-INFO
|
|
6
|
+
sailor_remote_agent.egg-info/SOURCES.txt
|
|
7
|
+
sailor_remote_agent.egg-info/dependency_links.txt
|
|
8
|
+
sailor_remote_agent.egg-info/entry_points.txt
|
|
9
|
+
sailor_remote_agent.egg-info/requires.txt
|
|
10
|
+
sailor_remote_agent.egg-info/top_level.txt
|
|
11
|
+
views/answer.tpl
|
|
12
|
+
views/result.tpl
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
bottle>=0.12.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
app
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Latest Evaluation Result</title>
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<style>
|
|
7
|
+
body {
|
|
8
|
+
font-family: Arial, sans-serif;
|
|
9
|
+
max-width: 800px;
|
|
10
|
+
margin: 50px auto;
|
|
11
|
+
padding: 20px;
|
|
12
|
+
}
|
|
13
|
+
.result-box {
|
|
14
|
+
border: 1px solid #ddd;
|
|
15
|
+
padding: 20px;
|
|
16
|
+
margin: 20px 0;
|
|
17
|
+
background-color: #f9f9f9;
|
|
18
|
+
}
|
|
19
|
+
.expr {
|
|
20
|
+
font-weight: bold;
|
|
21
|
+
color: #333;
|
|
22
|
+
margin-bottom: 10px;
|
|
23
|
+
}
|
|
24
|
+
.result {
|
|
25
|
+
color: #0066cc;
|
|
26
|
+
font-size: 1.2em;
|
|
27
|
+
}
|
|
28
|
+
.no-result {
|
|
29
|
+
color: #999;
|
|
30
|
+
font-style: italic;
|
|
31
|
+
}
|
|
32
|
+
a {
|
|
33
|
+
color: #0066cc;
|
|
34
|
+
text-decoration: none;
|
|
35
|
+
}
|
|
36
|
+
a:hover {
|
|
37
|
+
text-decoration: underline;
|
|
38
|
+
}
|
|
39
|
+
</style>
|
|
40
|
+
</head>
|
|
41
|
+
<body>
|
|
42
|
+
<h1>Latest Evaluation Result</h1>
|
|
43
|
+
% if latest:
|
|
44
|
+
<div class="result-box">
|
|
45
|
+
<div class="expr">Expression: <code>{{latest['expr']}}</code></div>
|
|
46
|
+
<div class="result">Result: <code>{{latest['result']}}</code></div>
|
|
47
|
+
</div>
|
|
48
|
+
% else:
|
|
49
|
+
<div class="no-result">No evaluation results yet.</div>
|
|
50
|
+
% end
|
|
51
|
+
<p><a href="/result">View all results</a></p>
|
|
52
|
+
</body>
|
|
53
|
+
</html>
|
|
54
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>All Evaluation Results</title>
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<style>
|
|
7
|
+
body {
|
|
8
|
+
font-family: Arial, sans-serif;
|
|
9
|
+
max-width: 800px;
|
|
10
|
+
margin: 50px auto;
|
|
11
|
+
padding: 20px;
|
|
12
|
+
}
|
|
13
|
+
.result-box {
|
|
14
|
+
border: 1px solid #ddd;
|
|
15
|
+
padding: 15px;
|
|
16
|
+
margin: 15px 0;
|
|
17
|
+
background-color: #f9f9f9;
|
|
18
|
+
}
|
|
19
|
+
.expr {
|
|
20
|
+
font-weight: bold;
|
|
21
|
+
color: #333;
|
|
22
|
+
margin-bottom: 8px;
|
|
23
|
+
}
|
|
24
|
+
.result {
|
|
25
|
+
color: #0066cc;
|
|
26
|
+
}
|
|
27
|
+
.no-results {
|
|
28
|
+
color: #999;
|
|
29
|
+
font-style: italic;
|
|
30
|
+
}
|
|
31
|
+
a {
|
|
32
|
+
color: #0066cc;
|
|
33
|
+
text-decoration: none;
|
|
34
|
+
}
|
|
35
|
+
a:hover {
|
|
36
|
+
text-decoration: underline;
|
|
37
|
+
}
|
|
38
|
+
.count {
|
|
39
|
+
color: #666;
|
|
40
|
+
margin-bottom: 20px;
|
|
41
|
+
}
|
|
42
|
+
</style>
|
|
43
|
+
</head>
|
|
44
|
+
<body>
|
|
45
|
+
<h1>All Evaluation Results</h1>
|
|
46
|
+
% if results:
|
|
47
|
+
<div class="count">Total results: {{len(results)}}</div>
|
|
48
|
+
% for item in results:
|
|
49
|
+
<div class="result-box">
|
|
50
|
+
<div class="expr">Expression: <code>{{item['expr']}}</code></div>
|
|
51
|
+
<div class="result">Result: <code>{{item['result']}}</code></div>
|
|
52
|
+
</div>
|
|
53
|
+
% end
|
|
54
|
+
% else:
|
|
55
|
+
<div class="no-results">No evaluation results yet.</div>
|
|
56
|
+
% end
|
|
57
|
+
<p><a href="/answer">View latest result</a></p>
|
|
58
|
+
</body>
|
|
59
|
+
</html>
|
|
60
|
+
|