Files
passepartout/skills/org-skill-cli-gateway.org

4.2 KiB

SKILL: CLI Gateway (Universal Literate Note)

Overview

The CLI Gateway is the primary sensory and actuating interface for human interaction. It implements a TCP-based S-expression protocol that allows multiple clients (terminal, Emacs, web) to establish secure bidirectional channels with the Brain.

Implementation

(in-package :opencortex)

(defvar *cli-port* 9105)
(defvar *cli-server-socket* nil)
(defvar *cli-server-thread* nil)

(defun execute-cli-action (action context)
  "Sends a framed message back to the connected CLI client."
  (let* ((payload (proto-get action :PAYLOAD))
         (meta (getf context :meta))
         (stream (getf meta :reply-stream)))
    (handler-case
        (if (and stream (open-stream-p stream))
            (progn
              (format stream "~a" (frame-message action))
              (finish-output stream)
              (format stream "~a" (frame-message '(:TYPE :STATUS :SCRIBE :IDLE :GARDENER :SLEEPING)))
              (finish-output stream))
            (harness-log "CLI ERROR: No active or open reply stream for signal.)
      (error (c) (harness-log "CLI ACTUATOR ERROR: ~a" c)))))

(defun handle-cli-slash-command (cmd stream)
  (cond
    ((string= cmd "/exit (return-from handle-cli-slash-command :exit))
    (t (format stream "~a" (frame-message (list :TYPE :REQUEST :PAYLOAD (list :ACTION :MESSAGE :TEXT (format nil "Unknown command: ~a" cmd))))))))

(defun handle-cli-client (stream)
  "Reads framed messages from a CLI client and injects them as stimuli."
  (harness-log "CLI: Client connected.
  (handler-case
      (progn
        ;; 1. Send Handshake
        (format stream "~a" (frame-message (make-hello-message "0.1.0))
        (finish-output stream)
        (format stream "~a" (frame-message '(:TYPE :STATUS :SCRIBE :IDLE :GARDENER :SLEEPING)))
        (finish-output stream)
        
        ;; 2. Communication Loop
        (loop
          (let ((msg (read-framed-message stream)))
            (cond ((eq msg :eof) (return))
                  ((eq msg :error) (return))
                  (t (let* ((payload (proto-get msg :payload))
                            (text (proto-get payload :text))
                            (meta (proto-get msg :meta)))
                       (if (and text (stringp text) (char= (char text 0) #\/))
                           (when (eq (handle-cli-slash-command text stream) :exit) (return))
                           (progn
                             ;; Default meta if missing
                             (unless meta
                               (setf (getf msg :meta) (list :SOURCE :CLI :SESSION-ID "default))
                             (harness-log "CLI: Received input -> ~s" msg)
                             (inject-stimulus msg :stream stream)))))))))
    (error (c) (harness-log "CLI CLIENT DISCONNECT: ~a" c)))
  (harness-log "CLI: Client disconnected.)

(defun start-cli-gateway (&optional (port *cli-port*))
  "Starts the TCP listener for local CLI clients."
  (setf *cli-server-socket* (usocket:socket-listen "0.0.0.0" port :reuse-address t))
  (setf *cli-server-thread*
        (bt:make-thread
         (lambda ()
           (unwind-protect
                (loop
                  (let* ((socket (usocket:socket-accept *cli-server-socket*))
                         (stream (usocket:socket-stream socket)))
                    (bt:make-thread (lambda () 
                                      (unwind-protect (handle-cli-client stream)
                                        (usocket:socket-close socket)))
                                    :name "opencortex-cli-client-handler))
             (usocket:socket-close *cli-server-socket*)))
         :name "opencortex-cli-gateway)
  (harness-log "CLI: Gateway listening on port ~a" port))

(register-actuator :CLI #'execute-cli-action)

(defskill :skill-gateway-cli
  :priority 200
  :trigger (lambda (ctx) (declare (ignore ctx)) nil)
  :probabilistic nil
  :deterministic (lambda (action ctx) (declare (ignore ctx)) action))

(start-cli-gateway)