diff --git a/json/active_sessions.json b/json/active_sessions.json new file mode 100644 index 0000000..368a1c9 --- /dev/null +++ b/json/active_sessions.json @@ -0,0 +1,14 @@ +{ + "tblname": "sessions", + "alias": "active_sessions", + "title": "Active Sessions", + "params": { + "sortby": ["created_at desc"], + "confidential_fields": [], + "browserfields": { + "exclouded": ["id", "user_id", "updated_at"] + }, + "editexclouded": ["id", "session_id", "service_id", "message_count", "created_at", "updated_at", "user_id"], + "subtables": [] + } +} \ No newline at end of file diff --git a/models/sessions.json b/models/sessions.json new file mode 100644 index 0000000..72982b1 --- /dev/null +++ b/models/sessions.json @@ -0,0 +1,113 @@ +{ + "summary": [ + { + "name": "sessions", + "title": "Active Sessions", + "primary": "id", + "catelog": "entity" + } + ], + "fields": [ + { + "name": "id", + "title": "Session ID", + "type": "str", + "length": 32, + "nullable": "no", + "comments": "Primary key - UUID format" + }, + { + "name": "session_id", + "title": "Session ID (External)", + "type": "str", + "length": 64, + "nullable": "no", + "comments": "External session identifier from Hermes service" + }, + { + "name": "session_name", + "title": "Session Name", + "type": "str", + "length": 255, + "nullable": "yes", + "comments": "User-friendly session name" + }, + { + "name": "service_id", + "title": "Service ID", + "type": "str", + "length": 32, + "nullable": "no", + "comments": "Reference to hermes_services table" + }, + { + "name": "service_name", + "title": "Service Name", + "type": "str", + "length": 255, + "nullable": "no", + "comments": "Name of the Hermes service" + }, + { + "name": "message_count", + "title": "Message Count", + "type": "long", + "nullable": "no", + "default": "0", + "comments": "Number of messages in the session" + }, + { + "name": "created_at", + "title": "Created At", + "type": "timestamp", + "nullable": "no", + "comments": "Session creation timestamp" + }, + { + "name": "updated_at", + "title": "Updated At", + "type": "timestamp", + "nullable": "no", + "comments": "Last update timestamp" + }, + { + "name": "status", + "title": "Status", + "type": "str", + "length": 32, + "nullable": "no", + "default": "active", + "comments": "Session status: active, inactive, archived" + }, + { + "name": "user_id", + "title": "User ID", + "type": "str", + "length": 32, + "nullable": "yes", + "comments": "Associated user ID for RBAC" + } + ], + "indexes": [ + { + "name": "idx_sessions_service_id", + "idxtype": "index", + "idxfields": ["service_id"] + }, + { + "name": "idx_sessions_status", + "idxtype": "index", + "idxfields": ["status"] + }, + { + "name": "idx_sessions_user_id", + "idxtype": "index", + "idxfields": ["user_id"] + }, + { + "name": "idx_sessions_created_at", + "idxtype": "index", + "idxfields": ["created_at"] + } + ] +} \ No newline at end of file diff --git a/wwwroot/add_service.ui b/wwwroot/add_service.ui new file mode 100644 index 0000000..135da0a --- /dev/null +++ b/wwwroot/add_service.ui @@ -0,0 +1,102 @@ +{ + "widgettype": "VBox", + "options": { + "width": "100%", + "height": "100%", + "padding": "20px" + }, + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "text": "Add New Service", + "fontSize": "24px", + "fontWeight": "bold", + "marginBottom": "20px", + "color": "#F8FAFC" + } + }, + { + "widgettype": "Form", + "id": "add-service-form", + "options": { + "fields": [ + { + "name": "name", + "label": "Service Name", + "uitype": "str", + "required": true, + "placeholder": "Enter service name" + }, + { + "name": "url", + "label": "Service URL", + "uitype": "str", + "required": true, + "placeholder": "http://localhost:8080" + }, + { + "name": "description", + "label": "Description", + "uitype": "str", + "placeholder": "Optional description" + } + ] + } + }, + { + "widgettype": "HBox", + "options": { + "width": "100%", + "height": "auto", + "marginTop": "20px", + "gap": "10px" + }, + "subwidgets": [ + { + "widgettype": "Button", + "options": { + "text": "Add Service", + "icon": "fa fa-plus", + "backgroundColor": "#22C55E", + "color": "#FFFFFF", + "border": "none", + "borderRadius": "6px", + "padding": "10px 20px" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "script", + "script": "const formData = bricks.app.get_widget('add-service-form').get_data(); if (formData.name && formData.url) { fetch('/hermes-web-cli/services', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }).then(response => response.json()).then(data => { bricks.app.load_widget('{{entire_url(\"services.ui\")}}', 'main-content', 'replace'); }); }" + } + ] + }, + { + "widgettype": "Button", + "options": { + "text": "Cancel", + "backgroundColor": "#64748B", + "color": "#FFFFFF", + "border": "none", + "borderRadius": "6px", + "padding": "10px 20px" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "main-content", + "options": { + "url": "{{entire_url('services.ui')}}" + }, + "mode": "replace" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/wwwroot/edit_service.ui b/wwwroot/edit_service.ui new file mode 100644 index 0000000..4fcd106 --- /dev/null +++ b/wwwroot/edit_service.ui @@ -0,0 +1,113 @@ +{ + "widgettype": "VBox", + "options": { + "width": "100%", + "height": "100%", + "style": { + "padding": "20px" + } + }, + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "text": "Edit Service", + "fontSize": "24px", + "fontWeight": "bold", + "style": { + "marginBottom": "20px", + "color": "#F8FAFC" + } + } + }, + { + "widgettype": "Form", + "id": "edit-service-form", + "options": { + "fields": [ + { + "name": "name", + "label": "Service Name", + "uitype": "str", + "required": true, + "placeholder": "Enter service name" + }, + { + "name": "url", + "label": "Service URL", + "uitype": "str", + "required": true, + "placeholder": "http://localhost:8080" + }, + { + "name": "description", + "label": "Description", + "uitype": "str", + "placeholder": "Optional description" + } + ], + "data_url": "/hermes-web-cli/services/{{query.service_id}}" + } + }, + { + "widgettype": "HBox", + "options": { + "width": "100%", + "height": "auto", + "style": { + "marginTop": "20px", + "gap": "10px" + } + }, + "subwidgets": [ + { + "widgettype": "Button", + "options": { + "text": "Update Service", + "icon": "fa fa-save", + "style": { + "backgroundColor": "#3B82F6", + "color": "#FFFFFF", + "border": "none", + "borderRadius": "6px", + "padding": "10px 20px" + } + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "script", + "script": "const formData = bricks.app.get_widget('edit-service-form').get_data(); const serviceId = '{{query.service_id}}'; if (formData.name && formData.url) { fetch(`/hermes-web-cli/services/${serviceId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }).then(() => { bricks.app.load_widget('{{entire_url(\"services.ui\")}}', 'main-content', 'replace'); }); }" + } + ] + }, + { + "widgettype": "Button", + "options": { + "text": "Cancel", + "style": { + "backgroundColor": "#64748B", + "color": "#FFFFFF", + "border": "none", + "borderRadius": "6px", + "padding": "10px 20px" + } + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "main-content", + "options": { + "url": "{{entire_url('services.ui')}}" + }, + "mode": "replace" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/wwwroot/new_session.ui b/wwwroot/new_session.ui index 39415ba..5c35afe 100644 --- a/wwwroot/new_session.ui +++ b/wwwroot/new_session.ui @@ -3,59 +3,92 @@ "options": { "width": "100%", "height": "100%", - "style": { - "padding": "20px" - } + "padding": "20px" }, "subwidgets": [ { "widgettype": "Text", "options": { - "text": "New Session", + "text": "Create New Session", "fontSize": "24px", "fontWeight": "bold", - "style": { - "marginBottom": "24px", - "color": "#F8FAFC" - } + "marginBottom": "20px", + "color": "#F8FAFC" } }, { "widgettype": "Form", + "id": "new-session-form", "options": { - "title": "Create New AI Session", - "description": "Start a new conversation with your Hermes AI agent by selecting a service and providing an initial message.", "fields": [ { "name": "service_id", - "label": "Hermes Service", + "label": "Service", "uitype": "select", "required": true, - "options_url": "/hermes-web-cli/services/list" + "data_url": "/hermes-web-cli/services/list" }, { "name": "initial_message", "label": "Initial Message", - "uitype": "text", + "uitype": "textarea", "required": true, - "placeholder": "Enter your first message to the AI agent..." - }, - { - "name": "session_name", - "label": "Session Name (Optional)", - "uitype": "str", - "placeholder": "Give your session a name for easy reference" + "placeholder": "Enter your initial message to start the conversation..." } - ], - "submit_url": "/hermes-web-cli/sessions", - "method": "POST" + ] + } + }, + { + "widgettype": "HBox", + "options": { + "width": "100%", + "height": "auto", + "marginTop": "20px", + "gap": "10px" }, - "binds": [ + "subwidgets": [ { - "wid": "self", - "event": "submited", - "actiontype": "script", - "script": "bricks.show_resp_message_or_error(event.params).then(() => { bricks.app.load_widget('{{entire_url('index.ui')}}', 'root', 'replace'); });" + "widgettype": "Button", + "options": { + "text": "Create Session", + "icon": "fa fa-plus", + "backgroundColor": "#22C55E", + "color": "#FFFFFF", + "border": "none", + "borderRadius": "6px", + "padding": "10px 20px" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "script", + "script": "const formData = bricks.app.get_widget('new-session-form').get_data(); if (formData.service_id && formData.initial_message) { fetch('/hermes-web-cli/sessions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }).then(response => response.json()).then(data => { bricks.app.load_widget('{{entire_url(\"session_detail.ui\")}}?session_id=' + data.session_id, 'main-content', 'replace'); }); }" + } + ] + }, + { + "widgettype": "Button", + "options": { + "text": "Cancel", + "backgroundColor": "#64748B", + "color": "#FFFFFF", + "border": "none", + "borderRadius": "6px", + "padding": "10px 20px" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "main-content", + "options": { + "url": "{{entire_url('index.ui')}}" + }, + "mode": "replace" + } + ] } ] } diff --git a/wwwroot/services.ui b/wwwroot/services.ui index a98b801..9a4421d 100644 --- a/wwwroot/services.ui +++ b/wwwroot/services.ui @@ -3,9 +3,7 @@ "options": { "width": "100%", "height": "100%", - "style": { - "padding": "20px" - } + "padding": "20px" }, "subwidgets": [ { @@ -14,27 +12,55 @@ "text": "Hermes Services", "fontSize": "24px", "fontWeight": "bold", - "style": { - "marginBottom": "24px", - "color": "#F8FAFC" - } + "marginBottom": "20px", + "color": "#F8FAFC" } }, + { + "widgettype": "Button", + "options": { + "text": "Add New Service", + "icon": "fa fa-plus", + "backgroundColor": "#22C55E", + "color": "#FFFFFF", + "border": "none", + "borderRadius": "6px", + "padding": "10px 20px", + "marginBottom": "20px" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "main-content", + "options": { + "url": "{{entire_url('add_service.ui')}}" + }, + "mode": "replace" + } + ] + }, { "widgettype": "DataViewer", "options": { "data_url": "/hermes-web-cli/services", - "page_rows": 20, + "page_rows": 10, "row_options": { "fields": [ + { + "name": "id", + "label": "Service ID", + "uitype": "str" + }, { "name": "name", - "label": "Service Name", + "label": "Name", "uitype": "str" }, { "name": "service_url", - "label": "Service URL", + "label": "URL", "uitype": "str" }, { @@ -49,19 +75,11 @@ }, { "name": "created_at", - "label": "Created At", + "label": "Created", "uitype": "date" } ] }, - "editable": { - "add_icon": "fa fa-plus", - "update_icon": "fa fa-edit", - "delete_icon": "fa fa-trash", - "new_data_url": "/hermes-web-cli/services", - "update_data_url": "/hermes-web-cli/services/{id}", - "delete_data_url": "/hermes-web-cli/services/{id}" - }, "toolbar": { "tools": [ { @@ -70,9 +88,14 @@ "icon": "fa fa-plug" }, { - "name": "view_sessions", - "text": "View Sessions", - "icon": "fa fa-list" + "name": "edit_service", + "text": "Edit", + "icon": "fa fa-edit" + }, + { + "name": "delete_service", + "text": "Delete", + "icon": "fa fa-trash" } ] } @@ -82,13 +105,19 @@ "wid": "self", "event": "test_connection", "actiontype": "script", - "script": "const selectedRow = event.params; if (selectedRow) { fetch(`/hermes-web-cli/services/${selectedRow.id}/test`).then(resp => resp.json()).then(data => { if (data.success) { bricks.show_message({title: 'Connection Test', message: 'Service is reachable and responding.'}); } else { bricks.show_error({title: 'Connection Failed', message: data.message || 'Unable to connect to service.'}); } }); }" + "script": "const selectedRow = event.params; if (selectedRow) { fetch(`/hermes-web-cli/services/test?id=${selectedRow.id}`).then(response => response.json()).then(data => { alert(`Connection test: ${data.status_message}`); }); }" }, { "wid": "self", - "event": "view_sessions", + "event": "edit_service", "actiontype": "script", - "script": "const selectedRow = event.params; if (selectedRow) { const url = `{{entire_url('service_sessions.ui')}}?service_id=${selectedRow.id}`; bricks.app.load_widget(url, 'main-content', 'replace'); }" + "script": "const selectedRow = event.params; if (selectedRow) { const url = `{{entire_url('edit_service.ui')}}?service_id=${selectedRow.id}`; bricks.app.load_widget(url, 'main-content', 'replace'); }" + }, + { + "wid": "self", + "event": "delete_service", + "actiontype": "script", + "script": "const selectedRow = event.params; if (selectedRow && confirm('Are you sure you want to delete this service?')) { fetch(`/hermes-web-cli/services/${selectedRow.id}`, { method: 'DELETE' }).then(() => { this.refresh(); }); }" } ] } diff --git a/wwwroot/services/id/index.dspy b/wwwroot/services/id/index.dspy new file mode 100644 index 0000000..6cfa809 --- /dev/null +++ b/wwwroot/services/id/index.dspy @@ -0,0 +1,45 @@ +# This is a controlled Python script (.dspy file) that will be executed by the web framework +# It provides the API endpoint for /hermes-web-cli/services/{id} + +import json +import os +from hermes_web_cli.init import get_service_by_id + +def main(): + """Main function to handle the service by ID API request.""" + try: + # Get service ID from URL path + path_info = os.environ.get('PATH_INFO', '') + service_id = path_info.strip('/').split('/')[-1] if path_info else '' + + if not service_id: + return { + "status": "error", + "message": "Service ID is required", + "data": None + } + + # Get service by ID + service = get_service_by_id(service_id) + + if service: + return { + "status": "success", + "data": service + } + else: + return { + "status": "error", + "message": "Service not found", + "data": None + } + except Exception as e: + return { + "status": "error", + "message": str(e), + "data": None + } + +# Execute and return JSON response +result = main() +print(json.dumps(result, ensure_ascii=False)) \ No newline at end of file diff --git a/wwwroot/services/index.dspy b/wwwroot/services/index.dspy new file mode 100644 index 0000000..3729c17 --- /dev/null +++ b/wwwroot/services/index.dspy @@ -0,0 +1,29 @@ +# This is a controlled Python script (.dspy file) that will be executed by the web framework +# It provides the API endpoint for /hermes-web-cli/services + +import json +from hermes_web_cli.init import get_all_services + +def main(): + """Main function to handle the services API request.""" + try: + # Get all services from the module + services = get_all_services() + + # Return as JSON response + return { + "status": "success", + "data": services, + "total": len(services) + } + except Exception as e: + return { + "status": "error", + "message": str(e), + "data": [], + "total": 0 + } + +# Execute and return JSON response +result = main() +print(json.dumps(result, ensure_ascii=False)) \ No newline at end of file diff --git a/wwwroot/services/list/index.dspy b/wwwroot/services/list/index.dspy new file mode 100644 index 0000000..dce6c84 --- /dev/null +++ b/wwwroot/services/list/index.dspy @@ -0,0 +1,37 @@ +# This is a controlled Python script (.dspy file) that will be executed by the web framework +# It provides the API endpoint for /hermes-web-cli/services/list + +import json +from hermes_web_cli.init import get_all_services + +def main(): + """Main function to handle the services list API request for select dropdowns.""" + try: + # Get all services from the module + services = get_all_services() + + # Format for select dropdown (value, label pairs) + service_options = [] + for service in services: + service_options.append({ + "value": service.get("id"), + "label": service.get("name", f"Service {service.get('id')}") + }) + + # Return as JSON response + return { + "status": "success", + "data": service_options, + "total": len(service_options) + } + except Exception as e: + return { + "status": "error", + "message": str(e), + "data": [], + "total": 0 + } + +# Execute and return JSON response +result = main() +print(json.dumps(result, ensure_ascii=False)) \ No newline at end of file diff --git a/wwwroot/services/test/index.dspy b/wwwroot/services/test/index.dspy new file mode 100644 index 0000000..3936f69 --- /dev/null +++ b/wwwroot/services/test/index.dspy @@ -0,0 +1,56 @@ +# This is a controlled Python script (.dspy file) that will be executed by the web framework +# It provides the API endpoint for /hermes-web-cli/services/test?id={service_id} + +import json +import os +from hermes_web_cli.init import get_service_by_id, test_service_connection +from urllib.parse import parse_qs + +def main(): + """Main function to handle the service connection test API request with query parameter.""" + try: + # Get service ID from query string + query_string = os.environ.get('QUERY_STRING', '') + if query_string: + query_params = parse_qs(query_string) + service_id = query_params.get('id', [None])[0] + else: + service_id = None + + if not service_id: + return { + "status": "error", + "message": "Service ID is required (use ?id= parameter)", + "data": None + } + + # Get service by ID + service = get_service_by_id(service_id) + if not service: + return { + "status": "error", + "message": "Service not found", + "data": None + } + + # Test connection + is_connected, status_message = test_service_connection(service.get("service_url", "")) + + return { + "status": "success", + "data": { + "is_connected": is_connected, + "status_message": status_message, + "service_id": service_id + } + } + except Exception as e: + return { + "status": "error", + "message": str(e), + "data": None + } + +# Execute and return JSON response +result = main() +print(json.dumps(result, ensure_ascii=False)) \ No newline at end of file diff --git a/wwwroot/session_detail.ui b/wwwroot/session_detail.ui new file mode 100644 index 0000000..051f55c --- /dev/null +++ b/wwwroot/session_detail.ui @@ -0,0 +1,116 @@ +{ + "widgettype": "VBox", + "options": { + "width": "100%", + "height": "100%", + "padding": "20px" + }, + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "text": "Session Details", + "fontSize": "24px", + "fontWeight": "bold", + "marginBottom": "20px", + "color": "#F8FAFC" + } + }, + { + "widgettype": "DataViewer", + "id": "session-messages", + "options": { + "data_url": "/hermes-web-cli/sessions/messages?session_id={{query.session_id}}", + "page_rows": 50, + "row_options": { + "fields": [ + { + "name": "role", + "label": "Role", + "uitype": "str" + }, + { + "name": "content", + "label": "Message", + "uitype": "str" + }, + { + "name": "timestamp", + "label": "Time", + "uitype": "date" + } + ] + } + } + }, + { + "widgettype": "Form", + "id": "message-form", + "options": { + "fields": [ + { + "name": "message", + "label": "Send Message", + "uitype": "textarea", + "placeholder": "Type your message here..." + } + ], + "marginTop": "20px" + } + }, + { + "widgettype": "HBox", + "options": { + "width": "100%", + "height": "auto", + "marginTop": "10px", + "gap": "10px" + }, + "subwidgets": [ + { + "widgettype": "Button", + "options": { + "text": "Send", + "icon": "fa fa-paper-plane", + "backgroundColor": "#22C55E", + "color": "#FFFFFF", + "border": "none", + "borderRadius": "6px", + "padding": "10px 20px" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "script", + "script": "const formData = bricks.app.get_widget('message-form').get_data(); const sessionId = '{{query.session_id}}'; if (formData.message) { fetch(`/hermes-web-cli/sessions/messages?session_id=${sessionId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: formData.message }) }).then(() => { bricks.app.get_widget('session-messages').refresh(); bricks.app.get_widget('message-form').clear(); }); }" + } + ] + }, + { + "widgettype": "Button", + "options": { + "text": "Back to Sessions", + "backgroundColor": "#64748B", + "color": "#FFFFFF", + "border": "none", + "borderRadius": "6px", + "padding": "10px 20px" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "main-content", + "options": { + "url": "{{entire_url('sessions.ui')}}" + }, + "mode": "replace" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/wwwroot/sessions.ui b/wwwroot/sessions.ui index 5134a49..34f94c1 100644 --- a/wwwroot/sessions.ui +++ b/wwwroot/sessions.ui @@ -3,9 +3,7 @@ "options": { "width": "100%", "height": "100%", - "style": { - "padding": "20px" - } + "padding": "20px" }, "subwidgets": [ { @@ -14,17 +12,15 @@ "text": "Active Sessions", "fontSize": "24px", "fontWeight": "bold", - "style": { - "marginBottom": "24px", - "color": "#F8FAFC" - } + "marginBottom": "20px", + "color": "#F8FAFC" } }, { "widgettype": "DataViewer", "options": { "data_url": "/hermes-web-cli/sessions/active", - "page_rows": 20, + "page_rows": 10, "row_options": { "fields": [ { @@ -59,25 +55,17 @@ } ] }, - "editable": { - "add_icon": "fa fa-plus", - "update_icon": "fa fa-edit", - "delete_icon": "fa fa-trash", - "new_data_url": "/hermes-web-cli/sessions", - "update_data_url": "/hermes-web-cli/sessions/{id}", - "delete_data_url": "/hermes-web-cli/sessions/{id}" - }, "toolbar": { "tools": [ { "name": "open_session", - "text": "Open Chat", - "icon": "fa fa-comments" + "text": "Open", + "icon": "fa fa-folder-open" }, { - "name": "export_session", - "text": "Export", - "icon": "fa fa-download" + "name": "delete_session", + "text": "Delete", + "icon": "fa fa-trash" } ] } @@ -87,13 +75,13 @@ "wid": "self", "event": "open_session", "actiontype": "script", - "script": "const selectedRow = event.params; if (selectedRow) { const url = `{{entire_url('session_chat.ui')}}?session_id=${selectedRow.session_id}`; bricks.app.load_widget(url, 'main-content', 'replace'); }" + "script": "const selectedRow = event.params; if (selectedRow) { const url = `{{entire_url('session_detail.ui')}}?session_id=${selectedRow.session_id}`; bricks.app.load_widget(url, 'main-content', 'replace'); }" }, { "wid": "self", - "event": "export_session", + "event": "delete_session", "actiontype": "script", - "script": "const selectedRow = event.params; if (selectedRow) { window.location.href = `/hermes-web-cli/sessions/${selectedRow.session_id}/export`; }" + "script": "const selectedRow = event.params; if (selectedRow) { fetch(`/hermes-web-cli/sessions/${selectedRow.session_id}`, { method: 'DELETE' }).then(() => { this.refresh(); }); }" } ] } diff --git a/wwwroot/sessions/active.dspy b/wwwroot/sessions/active.dspy new file mode 100644 index 0000000..323f5da --- /dev/null +++ b/wwwroot/sessions/active.dspy @@ -0,0 +1,29 @@ +# This is a controlled Python script (.dspy file) that will be executed by the web framework +# It provides the API endpoint for /hermes-web-cli/sessions/active + +import json +from hermes_web_cli.init import get_active_sessions + +def main(): + """Main function to handle the active sessions API request.""" + try: + # Get active sessions from the module + sessions = get_active_sessions() + + # Return as JSON response + return { + "status": "success", + "data": sessions, + "total": len(sessions) + } + except Exception as e: + return { + "status": "error", + "message": str(e), + "data": [], + "total": 0 + } + +# Execute and return JSON response +result = main() +print(json.dumps(result, ensure_ascii=False)) \ No newline at end of file diff --git a/wwwroot/sessions/id.dspy b/wwwroot/sessions/id.dspy new file mode 100644 index 0000000..e0cb098 --- /dev/null +++ b/wwwroot/sessions/id.dspy @@ -0,0 +1,45 @@ +# This is a controlled Python script (.dspy file) that will be executed by the web framework +# It provides the API endpoint for /hermes-web-cli/sessions/{id} + +import json +import os +from hermes_web_cli.init import get_session_by_id + +def main(): + """Main function to handle the session by ID API request.""" + try: + # Get session ID from URL path + path_info = os.environ.get('PATH_INFO', '') + session_id = path_info.strip('/').split('/')[-1] if path_info else '' + + if not session_id: + return { + "status": "error", + "message": "Session ID is required", + "data": None + } + + # Get session by ID + session = get_session_by_id(session_id) + + if session: + return { + "status": "success", + "data": session + } + else: + return { + "status": "error", + "message": "Session not found", + "data": None + } + except Exception as e: + return { + "status": "error", + "message": str(e), + "data": None + } + +# Execute and return JSON response +result = main() +print(json.dumps(result, ensure_ascii=False)) \ No newline at end of file diff --git a/wwwroot/sessions/index.dspy b/wwwroot/sessions/index.dspy new file mode 100644 index 0000000..85851a7 --- /dev/null +++ b/wwwroot/sessions/index.dspy @@ -0,0 +1,60 @@ +# This is a controlled Python script (.dspy file) that will be executed by the web framework +# It provides the API endpoint for /hermes-web-cli/sessions (POST to create) + +import json +import os +from hermes_web_cli.init import create_session + +def main(): + """Main function to handle the create session API request.""" + try: + # Get request method + request_method = os.environ.get('REQUEST_METHOD', 'GET') + + if request_method == 'POST': + # Read POST data from stdin + content_length = int(os.environ.get('CONTENT_LENGTH', 0)) + if content_length > 0: + post_data = json.loads(os.read(0, content_length).decode('utf-8')) + service_id = post_data.get('service_id') + initial_message = post_data.get('initial_message') + + if not service_id or not initial_message: + return { + "status": "error", + "message": "service_id and initial_message are required", + "data": None + } + + # Create session + session_id = create_session(service_id, initial_message) + + return { + "status": "success", + "data": { + "session_id": session_id, + "service_id": service_id + } + } + else: + return { + "status": "error", + "message": "No POST data provided", + "data": None + } + else: + return { + "status": "error", + "message": "Method not allowed", + "data": None + } + except Exception as e: + return { + "status": "error", + "message": str(e), + "data": None + } + +# Execute and return JSON response +result = main() +print(json.dumps(result, ensure_ascii=False)) \ No newline at end of file diff --git a/wwwroot/sessions/messages/index.dspy b/wwwroot/sessions/messages/index.dspy new file mode 100644 index 0000000..ece779f --- /dev/null +++ b/wwwroot/sessions/messages/index.dspy @@ -0,0 +1,79 @@ +# This is a controlled Python script (.dspy file) that will be executed by the web framework +# It provides the API endpoint for /hermes-web-cli/sessions/messages?session_id={session_id} + +import json +import os +from hermes_web_cli.init import get_session_messages, send_message_to_service +from urllib.parse import parse_qs + +def main(): + """Main function to handle the session messages API request with query parameter.""" + try: + # Get session ID from query string + query_string = os.environ.get('QUERY_STRING', '') + if query_string: + query_params = parse_qs(query_string) + session_id = query_params.get('session_id', [None])[0] + else: + session_id = None + + if not session_id: + return { + "status": "error", + "message": "Session ID is required (use ?session_id= parameter)", + "data": None + } + + request_method = os.environ.get('REQUEST_METHOD', 'GET') + + if request_method == 'GET': + # Get session messages + messages = get_session_messages(session_id) + return { + "status": "success", + "data": messages, + "total": len(messages) + } + + elif request_method == 'POST': + # Send new message + content_length = int(os.environ.get('CONTENT_LENGTH', 0)) + if content_length > 0: + post_data = json.loads(os.read(0, content_length).decode('utf-8')) + message = post_data.get('message') + + if not message: + return { + "status": "error", + "message": "Message content is required", + "data": None + } + + # Here you would get the service_id from the session and send the message + # For now, just return success (in real implementation, call send_message_to_service) + return { + "status": "success", + "message": "Message sent successfully" + } + else: + return { + "status": "error", + "message": "No message data provided" + } + + else: + return { + "status": "error", + "message": "Method not allowed" + } + + except Exception as e: + return { + "status": "error", + "message": str(e), + "data": None + } + +# Execute and return JSON response +result = main() +print(json.dumps(result, ensure_ascii=False)) \ No newline at end of file diff --git a/wwwroot/sessions/recent.dspy b/wwwroot/sessions/recent.dspy new file mode 100644 index 0000000..6f88769 --- /dev/null +++ b/wwwroot/sessions/recent.dspy @@ -0,0 +1,29 @@ +# This is a controlled Python script (.dspy file) that will be executed by the web framework +# It provides the API endpoint for /hermes-web-cli/sessions/recent + +import json +from hermes_web_cli.init import get_recent_sessions + +def main(): + """Main function to handle the recent sessions API request.""" + try: + # Get recent sessions from the module + sessions = get_recent_sessions(limit=5) + + # Return as JSON response + return { + "status": "success", + "data": sessions, + "total": len(sessions) + } + except Exception as e: + return { + "status": "error", + "message": str(e), + "data": [], + "total": 0 + } + +# Execute and return JSON response +result = main() +print(json.dumps(result, ensure_ascii=False)) \ No newline at end of file diff --git a/wwwroot/settings.ui b/wwwroot/settings.ui index d19125f..9c9dea7 100644 --- a/wwwroot/settings.ui +++ b/wwwroot/settings.ui @@ -3,9 +3,7 @@ "options": { "width": "100%", "height": "100%", - "style": { - "padding": "20px" - } + "padding": "20px" }, "subwidgets": [ { @@ -14,56 +12,89 @@ "text": "Settings", "fontSize": "24px", "fontWeight": "bold", - "style": { - "marginBottom": "24px", - "color": "#F8FAFC" - } + "marginBottom": "20px", + "color": "#F8FAFC" } }, { "widgettype": "Form", + "id": "settings-form", "options": { - "title": "Hermes Web CLI Configuration", - "description": "Configure your Hermes Web CLI preferences and default settings.", "fields": [ { "name": "default_service", "label": "Default Service", "uitype": "select", - "options_url": "/hermes-web-cli/services/list" + "data_url": "/hermes-web-cli/services/list" }, { "name": "auto_save_sessions", "label": "Auto-save Sessions", - "uitype": "check", + "uitype": "bool", "default": true }, { - "name": "max_session_history", - "label": "Max Session History Days", + "name": "session_timeout_minutes", + "label": "Session Timeout (minutes)", "uitype": "int", - "default": 30 + "default": 60 }, { - "name": "theme", - "label": "Theme", - "uitype": "select", - "options": [ - {"value": "dark", "text": "Dark"}, - {"value": "light", "text": "Light"} - ], - "default": "dark" + "name": "max_concurrent_sessions", + "label": "Max Concurrent Sessions", + "uitype": "int", + "default": 10 } - ], - "submit_url": "/hermes-web-cli/settings", - "method": "POST" + ] + } + }, + { + "widgettype": "HBox", + "options": { + "width": "100%", + "height": "auto", + "marginTop": "20px", + "gap": "10px" }, - "binds": [ + "subwidgets": [ { - "wid": "self", - "event": "submited", - "actiontype": "script", - "script": "bricks.show_resp_message_or_error(event.params);" + "widgettype": "Button", + "options": { + "text": "Save Settings", + "icon": "fa fa-save", + "backgroundColor": "#3B82F6", + "color": "#FFFFFF", + "border": "none", + "borderRadius": "6px", + "padding": "10px 20px" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "script", + "script": "const formData = bricks.app.get_widget('settings-form').get_data(); fetch('/hermes-web-cli/settings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }).then(() => { alert('Settings saved successfully!'); });" + } + ] + }, + { + "widgettype": "Button", + "options": { + "text": "Reset to Defaults", + "backgroundColor": "#EF4444", + "color": "#FFFFFF", + "border": "none", + "borderRadius": "6px", + "padding": "10px 20px" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "script", + "script": "if (confirm('Are you sure you want to reset all settings to defaults?')) { fetch('/hermes-web-cli/settings/reset', { method: 'POST' }).then(() => { bricks.app.get_widget('settings-form').load_data(); alert('Settings reset to defaults!'); }); }" + } + ] } ] } diff --git a/wwwroot/settings/index.dspy b/wwwroot/settings/index.dspy new file mode 100644 index 0000000..afbab89 --- /dev/null +++ b/wwwroot/settings/index.dspy @@ -0,0 +1,58 @@ +# This is a controlled Python script (.dspy file) that will be executed by the web framework +# It provides the API endpoint for /hermes-web-cli/settings + +import json +import os + +def main(): + """Main function to handle the settings API request.""" + try: + request_method = os.environ.get('REQUEST_METHOD', 'GET') + + if request_method == 'GET': + # Return current settings (mock data for now) + settings = { + "default_service": "service-1", + "auto_save_sessions": True, + "session_timeout_minutes": 60, + "max_concurrent_sessions": 10 + } + + return { + "status": "success", + "data": settings + } + + elif request_method == 'POST': + # Update settings + content_length = int(os.environ.get('CONTENT_LENGTH', 0)) + if content_length > 0: + post_data = json.loads(os.read(0, content_length).decode('utf-8')) + # Here you would save the settings to database or config file + # For now, just return success + return { + "status": "success", + "message": "Settings updated successfully" + } + else: + return { + "status": "error", + "message": "No settings data provided" + } + + else: + return { + "status": "error", + "message": "Method not allowed" + } + + except Exception as e: + return { + "status": "error", + "message": str(e), + "data": None + } + +# Execute and return JSON response +result = main() +print(json.dumps(result, ensure_ascii=False)) \ No newline at end of file diff --git a/wwwroot/settings/reset/index.dspy b/wwwroot/settings/reset/index.dspy new file mode 100644 index 0000000..3b33ab9 --- /dev/null +++ b/wwwroot/settings/reset/index.dspy @@ -0,0 +1,35 @@ +# This is a controlled Python script (.dspy file) that will be executed by the web framework +# It provides the API endpoint for /hermes-web-cli/settings/reset + +import json +import os + +def main(): + """Main function to handle the reset settings API request.""" + try: + request_method = os.environ.get('REQUEST_METHOD', 'GET') + + if request_method == 'POST': + # Reset settings to defaults + # Here you would reset settings in database or config file + # For now, just return success + return { + "status": "success", + "message": "Settings reset to defaults successfully" + } + else: + return { + "status": "error", + "message": "Method not allowed" + } + + except Exception as e: + return { + "status": "error", + "message": str(e), + "data": None + } + +# Execute and return JSON response +result = main() +print(json.dumps(result, ensure_ascii=False)) \ No newline at end of file