#+PROPERTY: header-args:lisp :tangle (expand-file-name "org-skill-cli-gateway.lisp" (concat (or (getenv "INSTALL_DIR") ".") "/skills")) :PROPERTIES: :ID: cli-gateway-skill :CREATED: [2026-04-13 Mon 17:00] :END: #+TITLE: SKILL: CLI Gateway (Universal Literate Note) #+STARTUP: content #+FILETAGS: :gateway:cli:io:autonomy: * 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 #+begin_src lisp (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) #+end_src