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.
@@ -0,0 +1,3 @@
1
+ include views/*.tpl
2
+ recursive-include views *.tpl
3
+
@@ -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,2 @@
1
+ [console_scripts]
2
+ sailor-remote-agent = app:main
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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
+