Page 1 of 1

OpenClaw concepts on HIX

Posted: Sun Feb 15, 2026 8:42 pm
by Antonio Linares

HIX Cron/Task Testing - Walkthrough

🎯 Objetivo

Crear y probar ejemplos completos de la nueva funcionalidad de gestión de tareas (cron) de HIX, que permite ejecutar threads de Harbour en segundo plano.

📦 Archivos Creados

He creado 6 archivos de prueba en c:\hix para demostrar diferentes capacidades de la funcionalidad de tareas:

1. [test_init.prg](file:///c:/hix/test_init.prg) ⏰

Propósito: Ejemplo básico - Reloj que muestra la hora cada segundo

URL: http://localhost/task/test_init.prg

Código:

Code: Select all | Expand

function main()
	? 'Init Thread Task: ', HIX_AddTask( 'TIME', hb_threadStart( @zTime() ) )	
retu nil 

function zTime()
	while .t. 
		_d( time() )
		hb_idleSleep(1)
	end 
retu nil

Qué hace:

  • Inicia una tarea llamada 'TIME'
  • Muestra la hora actual cada segundo
  • Perfecto para verificar que la funcionalidad básica funciona

2. [test_counter.prg](file:///c:/hix/test_counter.prg) 🔢

Propósito: Contador incremental que demuestra persistencia de variables

URL: http://localhost/task/test_counter.prg

Características:

  • Contador que incrementa cada 2 segundos
  • Muestra cómo mantener estado en un thread
  • Útil para verificar que las variables persisten

3. [test_logger.prg](file:///c:/hix/test_logger.prg) 📝

Propósito: Sistema de logging con timestamps

URL: http://localhost/task/test_logger.prg

Características:

  • Escribe logs cada 5 segundos
  • Archivo de salida: .log\task_logger.txt
  • Incluye fecha y hora en cada entrada
  • Demuestra I/O de archivos desde threads

4. [test_multiple.prg](file:///c:/hix/test_multiple.prg) 🔀

Propósito: Múltiples tareas concurrentes

URL: http://localhost/task/test_multiple.prg

Tareas que inicia:

  1. RELOJ: Hora cada segundo
  2. STATS: Estadísticas del sistema cada 10 segundos
  3. HEARTBEAT: Señal de vida cada 3 segundos

Código relevante:

Code: Select all | Expand

HIX_AddTask( 'RELOJ', hb_threadStart( @zClock() ) )
HIX_AddTask( 'STATS', hb_threadStart( @zStats() ) )
HIX_AddTask( 'HEARTBEAT', hb_threadStart( @zHeartBeat() ) )

Qué demuestra:

  • Ejecución paralela de múltiples threads
  • Diferentes intervalos de ejecución
  • Monitoreo concurrente

5. [test_status.prg](file:///c:/hix/test_status.prg) 📊

Propósito: Monitor de estado de tareas (placeholder)

URL: http://localhost/task/test_status.prg

Nota: Este archivo es un esqueleto para futuras implementaciones que requieren funciones adicionales como:

  • HIX_GetTasks() - Para listar tareas activas
  • HIX_GetTaskStatus() - Para obtener estado de una tarea
  • HIX_ListTasks() - Para enumerar todas las tareas

6. [test_scheduled.prg](file:///c:/hix/test_scheduled.prg) ⏲️

Propósito: Tareas programadas estilo cron

URL: http://localhost/task/test_scheduled.prg

Características:

  • Tarea horaria automática (cada vez que cambia la hora)
  • Tareas específicas en horarios definidos:
    • 09:00 - Inicio del día
    • 12:00 - Mediodía
    • 18:00 - Fin del día
    • 00:00 - Mantenimiento nocturno
  • Archivo de salida: .log\scheduled_tasks.txt
  • Verificación cada 30 segundos

Uso ideal:

  • Backups programados
  • Reportes periódicos
  • Limpieza automática
  • Sincronización de datos

🧪 Cómo Probar

Método 1: Navegador (Recomendado)

Simplemente navega en tu navegador a cualquiera de estas URLs mientras HIX.exe está corriendo:

  • http://localhost/task/test_init.prg
  • http://localhost/task/test_counter.prg
  • http://localhost/task/test_logger.prg
  • http://localhost/task/test_multiple.prg
  • http://localhost/task/test_status.prg
  • http://localhost/task/test_scheduled.prg

Método 2: Curl (Línea de comandos)

Code: Select all | Expand

curl http://localhost/task/test_init.prg
curl http://localhost/task/test_counter.prg
curl http://localhost/task/test_logger.prg

Método 3: PowerShell

Code: Select all | Expand

Invoke-WebRequest -Uri "http://localhost/task/test_init.prg"

🔍 Verificación

Después de ejecutar las pruebas, verifica:

  1. Salida en consola/logs de HIX:

    • Busca mensajes como [RELOJ], [STATS], [HEARTBEAT]
    • Verifica timestamps actualizándose
  2. Archivos de log creados:

    • c:\hix\.log\task_logger.txt - Del test_logger
    • c:\hix\.log\scheduled_tasks.txt - Del test_scheduled
  3. Comportamiento esperado:

    • Las tareas deben seguir ejecutándose después de la inicialización
    • Los contadores deben incrementar
    • Los logs deben actualizarse periódicamente

💡 Sugerencias de Casos de Uso Reales

Basándome en estos ejemplos, aquí hay casos de uso prácticos:

1. Backup Automático de Bases de Datos

Code: Select all | Expand

function zBackupDB()
    while .t.
        if Time() >= '02:00:00' .and. Time() < '02:05:00'
            // Ejecutar backup a las 2 AM
            BackupDatabase()
        endif
        hb_idleSleep(300) // Check cada 5 minutos
    end
retu nil

2. Monitor de Integridad de DBF

Code: Select all | Expand

function zDbIntegrityCheck()
    while .t.
        CheckAllDatabases()
        ReportIssues()
        hb_idleSleep(3600) // Cada hora
    end
retu nil

3. Limpieza de Archivos Temporales

Code: Select all | Expand

function zCleanupTemp()
    while .t.
        CleanOldFiles('.tmp', 7) // Archivos > 7 días
        hb_idleSleep(86400) // Una vez al día
    end
retu nil

4. Sincronización con API Externa

Code: Select all | Expand

function zApiSync()
    while .t.
        SyncWithExternalAPI()
        LogSyncStatus()
        hb_idleSleep(600) // Cada 10 minutos
    end
retu nil

5. Generación de Reportes Programados

Code: Select all | Expand

function zDailyReports()
    while .t.
        if Time() >= '17:00:00' .and. Time() < '17:01:00'
            GenerateDailyReport()
            EmailReport()
        endif
        hb_idleSleep(60)
    end
retu nil

🔧 Funciones Útiles de Harbour

Estas funciones son particularmente útiles en el contexto de tareas:

FunciónDescripción
hb_threadStart(@func)Inicia un thread con la función especificada
hb_idleSleep(nSec)Pausa el thread N segundos
Time()Hora actual (HH:MM:SS)
Date()Fecha actual
DToC(date)Convierte fecha a string
Memory(0)Memoria en uso (bytes)
MemoRead(file)Lee archivo de texto completo
MemoWrit(file, content)Escribe archivo de texto
hb_valToStr(val)Convierte cualquier valor a string

❓ Preguntas para Investigar

  1. Gestión de tareas:

    • ¿Cómo detener una tarea? (HIX_RemoveTask(), HIX_StopTask()?)
    • ¿Cómo listar tareas activas? (HIX_ListTasks()?)
    • ¿Cómo obtener estado de una tarea?
  2. Límites:

    • ¿Cuántas tareas concurrentes soporta HIX?
    • ¿Hay límite de memoria por tarea?
  3. Persistencia:

    • ¿Las tareas sobreviven un reinicio de HIX?
    • ¿Se pueden auto-iniciar tareas al arrancar el servidor?
  4. Comunicación:

    • ¿Pueden las tareas compartir datos entre sí?
    • ¿Hay mecanismos de sincronización disponibles?
  5. Monitoreo:

    • ¿Existe un dashboard para ver tareas activas?
    • ¿Se puede ver el output de las tareas en tiempo real?

📋 Checklist de Pruebas

  • Ejecutar test_init.prg y verificar logs
  • Ejecutar test_counter.prg y ver incremento
  • Ejecutar test_logger.prg y revisar archivo .log\task_logger.txt
  • Ejecutar test_multiple.prg y verificar 3 tareas concurrentes
  • Ejecutar test_scheduled.prg y esperar al cambio de hora
  • Verificar que las tareas siguen corriendo después de minutos
  • Intentar detener tareas (si existe la funcionalidad)
  • Intentar listar tareas activas
  • Monitorear uso de CPU/memoria con múltiples tareas
  • Probar qué pasa si se reinicia HIX.exe

🎓 Conclusión

He creado una suite completa de ejemplos que cubren:

✅ Inicialización básica de tareas
✅ Persistencia de estado con variables
✅ I/O de archivos desde threads
✅ Múltiples tareas concurrentes
✅ Tareas programadas estilo cron
✅ Casos de uso prácticos documentados

Los archivos están listos para probar. Simplemente navega a las URLs correspondientes mientras HIX.exe está corriendo en localhost.


📁 Resumen de Archivos

ArchivoLíneasPropósito
[test_init.prg](file:///c:/hix/test_init.prg)15Reloj básico
[test_counter.prg](file:///c:/hix/test_counter.prg)17Contador incremental
[test_logger.prg](file:///c:/hix/test_logger.prg)22Sistema de logging
[test_multiple.prg](file:///c:/hix/test_multiple.prg)47Tareas concurrentes
[test_status.prg](file:///c:/hix/test_status.prg)16Monitor de estado
[test_scheduled.prg](file:///c:/hix/test_scheduled.prg)68Tareas programadas
TOTAL1856 ejemplos completos

¡Listo para probar! 🚀


Re: OpenClaw concepts on HIX

Posted: Sun Feb 15, 2026 8:43 pm
by Antonio Linares

Guía de Pruebas - Funcionalidad Cron/Tasks de HIX

📋 Resumen

Esta guía documenta los ejemplos creados para probar la nueva funcionalidad de gestión de tareas (cron) en HIX. La función HIX_AddTask() permite iniciar threads de Harbour que se ejecutan en segundo plano de forma continua.

🚀 Archivos de Prueba Creados

1. test_init.prg - Reloj Básico

  • URL: http://localhost/task/test_init.prg
  • Descripción: Muestra la hora actual cada segundo
  • Uso: Ejemplo básico de inicialización de tareas
  • Código clave:

    Code: Select all | Expand

      HIX_AddTask( 'TIME', hb_threadStart( @zTime() ) )

2. test_counter.prg - Contador Incremental

  • URL: http://localhost/task/test_counter.prg
  • Descripción: Contador que incrementa cada 2 segundos
  • Uso: Demuestra variables persistentes en threads
  • Características:
    • Contador numérico incremental
    • Intervalo: 2 segundos
    • Muestra el progreso en tiempo real

3. test_logger.prg - Sistema de Logging

  • URL: http://localhost/task/test_logger.prg
  • Descripción: Escribe logs con timestamp cada 5 segundos
  • Uso: Demuestra persistencia de datos y escritura de archivos
  • Archivo de salida: .log\task_logger.txt
  • Características:
    • Registro con fecha y hora
    • Escritura en archivo de texto
    • Ideal para monitoreo de procesos largos

4. test_multiple.prg - Tareas Concurrentes

  • URL: http://localhost/task/test_multiple.prg
  • Descripción: Inicia 3 tareas simultáneas
  • Tareas:
    1. RELOJ: Muestra hora cada segundo
    2. STATS: Estadísticas del sistema cada 10 segundos
    3. HEARTBEAT: Señal de vida cada 3 segundos
  • Uso: Demuestra múltiples threads corriendo en paralelo

5. test_status.prg - Monitor de Estado

  • URL: http://localhost/task/test_status.prg
  • Descripción: Placeholder para monitoreo de tareas activas
  • Uso: Base para implementar dashboard de tareas
  • Nota: Requiere funciones adicionales como HIX_GetTasks() o HIX_ListTasks()

6. test_scheduled.prg - Tareas Programadas (Cron-like)

  • URL: http://localhost/task/test_scheduled.prg
  • Descripción: Ejecuta tareas en horarios específicos
  • Características:
    • Tarea horaria automática
    • Tareas específicas a las 09:00, 12:00, 18:00, 00:00
    • Sistema de verificación cada 30 segundos
  • Archivo de salida: .log\scheduled_tasks.txt
  • Uso: Perfecto para backups, reportes, mantenimiento

🧪 Plan de Pruebas Sugerido

Prueba 1: Inicialización Básica

  1. Asegurarse que HIX.exe está corriendo
  2. Navegar a: http://localhost/task/test_init.prg
  3. Verificar que se muestra el mensaje de inicialización
  4. Revisar logs del servidor para ver la salida del tiempo

Prueba 2: Contador Persistente

  1. Ejecutar: http://localhost/task/test_counter.prg
  2. Monitorear los logs para ver el contador incrementando
  3. Verificar que el contador persiste y no se reinicia

Prueba 3: Sistema de Logging

  1. Ejecutar: http://localhost/task/test_logger.prg
  2. Esperar al menos 30 segundos
  3. Revisar el archivo: c:\hix\.log\task_logger.txt
  4. Verificar que contiene múltiples entradas con timestamps

Prueba 4: Tareas Concurrentes

  1. Ejecutar: http://localhost/task/test_multiple.prg
  2. Monitorear los logs del servidor
  3. Verificar que se ven mensajes de las 3 tareas intercalados:
    • [RELOJ] cada 1 segundo
    • [HEARTBEAT] cada 3 segundos
    • [STATS] cada 10 segundos

Prueba 5: Tareas Programadas

  1. Ejecutar: http://localhost/task/test_scheduled.prg
  2. Esperar al cambio de hora
  3. Revisar: c:\hix\.log\scheduled_tasks.txt
  4. Verificar que se ejecutan las tareas horarias
  5. Si es posible, probar en las horas especiales (09:00, 12:00, 18:00, 00:00)

💡 Ideas Adicionales para Probar

1. Sistema de Backup Automático

Code: Select all | Expand

function zBackupTask()
    while .t.
        // Ejecutar backup cada 6 horas
        DoBackup()
        hb_idleSleep(21600) // 6 horas en segundos
    end
retu nil

2. Monitor de Base de Datos

Code: Select all | Expand

function zDbMonitor()
    while .t.
        // Verificar integridad de DBF cada 15 minutos
        CheckDatabaseIntegrity()
        hb_idleSleep(900)
    end
retu nil

3. Limpieza de Archivos Temporales

Code: Select all | Expand

function zCleanupTask()
    while .t.
        // Limpiar archivos temporales cada día
        CleanTempFiles()
        hb_idleSleep(86400) // 24 horas
    end
retu nil

4. Envío de Reportes por Email

Code: Select all | Expand

function zEmailReports()
    while .t.
        if Time() >= '17:00:00' .and. Time() < '17:01:00'
            SendDailyReport()
        endif
        hb_idleSleep(60)
    end
retu nil

5. Monitor de API Externa

Code: Select all | Expand

function zApiMonitor()
    while .t.
        // Verificar disponibilidad de API cada 5 minutos
        CheckApiStatus()
        hb_idleSleep(300)
    end
retu nil

📊 Preguntas para Investigar

  1. ¿Cómo detener una tarea?

    • ¿Existe HIX_RemoveTask('TASKNAME')?
    • ¿Hay un HIX_StopTask()?
  2. ¿Cómo listar tareas activas?

    • ¿Existe HIX_ListTasks() o HIX_GetTasks()?
    • ¿Devuelve un array con información de las tareas?
  3. ¿Límite de tareas concurrentes?

    • ¿Cuántas tareas puede manejar HIX simultáneamente?
  4. ¿Persistencia de tareas?

    • ¿Las tareas sobreviven un reinicio de HIX?
    • ¿Se pueden guardar/cargar configuraciones de tareas?
  5. ¿Comunicación entre tareas?

    • ¿Pueden las tareas compartir datos?
    • ¿Hay mecanismos de sincronización?

🔧 Funciones Útiles de Harbour para Tasks

  • hb_threadStart(@function) - Inicia un thread
  • hb_idleSleep(nSeconds) - Pausa el thread
  • _d(cMessage) - Debug output (probablemente)
  • Time() - Hora actual
  • Date() - Fecha actual
  • DToC(dDate) - Convierte fecha a string
  • Memory(0) - Memoria en uso
  • MemoRead(cFile) / MemoWrit(cFile, cContent) - I/O de archivos

📝 Notas Importantes

  • Todas las tareas corren en un loop infinito (while .t.)
  • Es importante usar hb_idleSleep() para no saturar el CPU
  • Los logs se pueden acumular, considerar rotación de logs
  • Manejar errores apropiadamente en tareas de producción
  • Las tareas son ideales para:
    • Monitoreo continuo
    • Procesamiento en background
    • Tareas programadas (cron-like)
    • Sincronización de datos
    • Limpieza automática
    • Backups programados

🎯 Próximos Pasos

  1. Probar todos los ejemplos en localhost
  2. Documentar el comportamiento observado
  3. Identificar funciones adicionales de la API de HIX_AddTask
  4. Crear ejemplos de casos de uso reales
  5. Implementar manejo de errores robusto
  6. Considerar sistema de notificaciones para tareas críticas

Re: OpenClaw concepts on HIX

Posted: Sun Feb 15, 2026 9:13 pm
by Antonio Linares

test_counter.prg

Code: Select all | Expand

// test_counter.prg
// Ejemplo de contador incremental
// Ejecutar desde: http://localhost/task/test_counter.prg

function main()
	
? 'Init Counter Task: ', HIX_AddTask( 'COUNTER', hb_threadStart( @zCounter() ) )	

retu nil 

// Función contador que incrementa cada 2 segundos
function zCounter()
    local nCount := 0

while .t. 
nCount++
_d( 'Counter: ' + str(nCount) )
hb_idleSleep(2)
end 

retu nil

test_logger.prg

Code: Select all | Expand

// test_logger.prg
// Ejemplo de logger con fecha y hora
// Ejecutar desde: http://localhost/task/test_logger.prg

function main()
	
? 'Init Logger Task: ', HIX_AddTask( 'LOGGER', hb_threadStart( @zLogger() ) )	

retu nil 

// Función logger que registra fecha/hora cada 5 segundos
function zLogger()
    local cLogFile := '.log\task_logger.txt'

while .t. 
// Escribir al log
MemoWrit( cLogFile, ;
    MemoRead(cLogFile) + ;
    DToC(Date()) + ' ' + Time() + ' - Log Entry' + Chr(13) + Chr(10) )
	
_d( 'Log written: ' + DToC(Date()) + ' ' + Time() )
hb_idleSleep(5)
end 

retu nil

test_multiple.prg

Code: Select all | Expand

// test_multiple.prg
// Ejemplo de múltiples tareas concurrentes
// Ejecutar desde: http://localhost/task/test_multiple.prg

function main()
    local cHtml := ''
	
cHtml += '<h2>Inicializando múltiples tareas...</h2>'
cHtml += '<ul>'
cHtml += '<li>Tarea RELOJ: ' + hb_valToStr( HIX_AddTask( 'RELOJ', hb_threadStart( @zClock() ) ) ) + '</li>'
cHtml += '<li>Tarea STATS: ' + hb_valToStr( HIX_AddTask( 'STATS', hb_threadStart( @zStats() ) ) ) + '</li>'
cHtml += '<li>Tarea HEARTBEAT: ' + hb_valToStr( HIX_AddTask( 'HEARTBEAT', hb_threadStart( @zHeartBeat() ) ) ) + '</li>'
cHtml += '</ul>'
cHtml += '<p>Tareas iniciadas correctamente</p>'

? cHtml

retu nil 

// Tarea 1: Reloj que muestra la hora cada segundo
function zClock()
    while .t. 
    _d( '[RELOJ] ' + Time() )
    hb_idleSleep(1)
    end 
retu nil

// Tarea 2: Estadísticas del sistema cada 10 segundos
function zStats()
    local nCount := 0
    while .t. 
    nCount++
    _d( '[STATS] Iteración #' + str(nCount) + ' - Memoria: ' + str(Memory(0)) + ' bytes' )
    hb_idleSleep(10)
    end 
retu nil

// Tarea 3: HeartBeat cada 3 segundos
function zHeartBeat()
    while .t. 
    _d( '[HEARTBEAT] ♥ Sistema activo' )
    hb_idleSleep(3)
    end 
retu nil

test_scheduled.prg

Code: Select all | Expand

// test_scheduled.prg
// Ejemplo de tareas programadas (cron-like)
// Ejecutar desde: http://localhost/task/test_scheduled.prg

function main()
	
? 'Init Scheduled Task: ', HIX_AddTask( 'SCHEDULED', hb_threadStart( @zScheduledTask() ) )	

retu nil 

// Tarea que se ejecuta en momentos específicos
function zScheduledTask()
    local cLastHour := ''
    local cCurrentHour := ''
    local cLogFile := '.log\scheduled_tasks.txt'

while .t. 
cCurrentHour := SubStr(Time(), 1, 2)  // Obtener hora actual
	
// Ejecutar tarea cada hora (cuando cambia la hora)
if cCurrentHour != cLastHour
cLastHour := cCurrentHour
		
// Tarea programada por hora
DoHourlyTask(cLogFile)
		
// Tareas específicas según la hora
do case
case cCurrentHour == '09'
_d( '[SCHEDULED] Tarea de inicio del día ejecutada' )
MemoWrit( cLogFile, MemoRead(cLogFile) + ;
    DToC(Date()) + ' 09:00 - Inicio del día' + Chr(13) + Chr(10) )
				
case cCurrentHour == '12'
_d( '[SCHEDULED] Tarea del mediodía ejecutada' )
MemoWrit( cLogFile, MemoRead(cLogFile) + ;
    DToC(Date()) + ' 12:00 - Mediodía' + Chr(13) + Chr(10) )
				
case cCurrentHour == '18'
_d( '[SCHEDULED] Tarea de fin del día ejecutada' )
MemoWrit( cLogFile, MemoRead(cLogFile) + ;
    DToC(Date()) + ' 18:00 - Fin del día' + Chr(13) + Chr(10) )
				
case cCurrentHour == '00'
_d( '[SCHEDULED] Tarea de mantenimiento nocturno ejecutada' )
MemoWrit( cLogFile, MemoRead(cLogFile) + ;
    DToC(Date()) + ' 00:00 - Mantenimiento nocturno' + Chr(13) + Chr(10) )
endcase
endif
	
// Dormir 30 segundos antes de verificar de nuevo
hb_idleSleep(30)
end 

retu nil

// Tarea que se ejecuta cada hora
function DoHourlyTask(cLogFile)
    _d( '[SCHEDULED] Tarea horaria ejecutada: ' + Time() )
    MemoWrit( cLogFile, MemoRead(cLogFile) + ;
        DToC(Date()) + ' ' + Time() + ' - Tarea horaria' + Chr(13) + Chr(10) )
retu nil

Re: OpenClaw concepts on HIX

Posted: Sun Feb 15, 2026 9:25 pm
by asesormix

Muy bueno Antonio. Gracias.
Nos abre mas la mente para ser creativos.
Cordial Saludo.


Re: OpenClaw concepts on HIX

Posted: Sun Feb 15, 2026 9:37 pm
by Antonio Linares

El concepto de "Skills" en HIX:

Image

skills_loader.prg

Code: Select all | Expand

// skill_loader.prg - Con auto-generación de skills de ejemplo
#include "hix.ch"

function main()
    local cHtml
    local aDirs, cDir, cSkillPath, oSkill
    local nTotal := 0
    
// Auto-generar skills de ejemplo si no existen AutoGenerateDemoSkills() cHtml := '<!DOCTYPE html><html><head>' cHtml += '<meta charset="UTF-8">' cHtml += '<title>HIX Skills System</title>' cHtml += '<style>' cHtml += 'body { font-family: Arial, sans-serif; max-width: 900px; margin: 20px auto; padding: 20px; }' cHtml += 'h1 { color: #333; border-bottom: 3px solid #007bff; padding-bottom: 10px; }' cHtml += '.skill-card { border: 1px solid #ddd; padding: 15px; margin: 10px 0; border-radius: 8px; background: #f9f9f9; }' cHtml += '.skill-name { font-size: 20px; font-weight: bold; color: #333; }' cHtml += '.skill-version { color: #666; font-size: 14px; }' cHtml += '.skill-desc { margin: 10px 0; color: #555; }' cHtml += '.badge { padding: 4px 10px; border-radius: 4px; font-size: 12px; margin-right: 5px; display: inline-block; }' cHtml += '.badge-danger { background: #dc3545; color: white; }' cHtml += '.badge-warning { background: #ffc107; color: black; }' cHtml += '.badge-info { background: #17a2b8; color: white; }' cHtml += '.summary { background: #e7f3ff; padding: 15px; border-radius: 8px; margin: 20px 0; }' cHtml += '</style></head><body>' aDirs := Directory('skills\*.*', 'D') cHtml += '<h1>📦 HIX Skills System</h1>' for each cDir in aDirs if cDir[5] == 'D' .and. !(cDir[1] $ '.,') .and. cDir[1] != '..' cSkillPath := 'skills\' + cDir[1] oSkill := ParseSkill(cSkillPath) if oSkill != NIL nTotal++ cHtml += '<div class="skill-card">' cHtml += '<div class="skill-name">' + oSkill['name'] + ' ' cHtml += '<span class="skill-version">v' + oSkill['version'] + '</span>' cHtml += '</div>' cHtml += '<div class="skill-desc">' + oSkill['description'] + '</div>' cHtml += '<div>' cHtml += '<span class="badge badge-info">📂 ' + hb_HGetDef(oSkill, 'category', 'general') + '</span> ' if hb_HGetDef(oSkill, 'requires_permission', .t.) cHtml += '<span class="badge badge-warning">🔐 Requiere Permiso</span> ' endif if oSkill['dangerous'] cHtml += '<span class="badge badge-danger">⚠️ PELIGROSA</span>' endif cHtml += '</div>' cHtml += '</div>' endif endif next cHtml += '<div class="summary">' cHtml += '<strong>✅ Total:</strong> ' + AllTrim(Str(nTotal)) + ' skills cargadas correctamente' cHtml += '</div>' cHtml += '</body></html>' UWrite(cHtml) retu "" //------------------------------------------------------------------------------ // Auto-genera las 3 skills de demostración si no existen //------------------------------------------------------------------------------ function AutoGenerateDemoSkills()
if !File('skills\gmail\SKILL.md') CreateGmailSkill() endif if !File('skills\whatsapp\SKILL.md') CreateWhatsAppSkill() endif if !File('skills\filesystem\SKILL.md') CreateFileSystemSkill() endif retu NIL function CreateGmailSkill() local cContent := ''
MkDir('skills') MkDir('skills\gmail') cContent += '---' + Chr(13) + Chr(10) cContent += 'name: "gmail"' + Chr(13) + Chr(10) cContent += 'version: "1.0.0"' + Chr(13) + Chr(10) cContent += 'description: "Gestiona correos electrónicos mediante Gmail API"' + Chr(13) + Chr(10) cContent += 'category: "communication"' + Chr(13) + Chr(10) cContent += 'requires_permission: true' + Chr(13) + Chr(10) cContent += 'dangerous: false' + Chr(13) + Chr(10) cContent += '---' + Chr(13) + Chr(10) cContent += Chr(13) + Chr(10) cContent += '# Gmail Skill' + Chr(13) + Chr(10) cContent += Chr(13) + Chr(10) cContent += 'Permite enviar, leer y gestionar correos electrónicos mediante Gmail API.' + Chr(13) + Chr(10) MemoWrit('skills\gmail\SKILL.md', cContent) retu NIL function CreateWhatsAppSkill() local cContent := ''
MkDir('skills') MkDir('skills\whatsapp') cContent += '---' + Chr(13) + Chr(10) cContent += 'name: "whatsapp"' + Chr(13) + Chr(10) cContent += 'version: "1.0.0"' + Chr(13) + Chr(10) cContent += 'description: "Envía y recibe mensajes de WhatsApp mediante WhatsApp Business API"' + Chr(13) + Chr(10) cContent += 'category: "communication"' + Chr(13) + Chr(10) cContent += 'requires_permission: true' + Chr(13) + Chr(10) cContent += 'dangerous: false' + Chr(13) + Chr(10) cContent += '---' + Chr(13) + Chr(10) cContent += Chr(13) + Chr(10) cContent += '# WhatsApp Skill' + Chr(13) + Chr(10) cContent += Chr(13) + Chr(10) cContent += 'Permite enviar mensajes, imágenes y archivos por WhatsApp.' + Chr(13) + Chr(10) MemoWrit('skills\whatsapp\SKILL.md', cContent) retu NIL function CreateFileSystemSkill() local cContent := ''
MkDir('skills') MkDir('skills\filesystem') cContent += '---' + Chr(13) + Chr(10) cContent += 'name: "filesystem"' + Chr(13) + Chr(10) cContent += 'version: "1.0.0"' + Chr(13) + Chr(10) cContent += 'description: "Gestiona archivos y directorios del sistema de archivos"' + Chr(13) + Chr(10) cContent += 'category: "filesystem"' + Chr(13) + Chr(10) cContent += 'requires_permission: true' + Chr(13) + Chr(10) cContent += 'dangerous: true' + Chr(13) + Chr(10) cContent += '---' + Chr(13) + Chr(10) cContent += Chr(13) + Chr(10) cContent += '# FileSystem Skill' + Chr(13) + Chr(10) cContent += Chr(13) + Chr(10) cContent += 'Permite listar, buscar, leer, escribir, mover y eliminar archivos.' + Chr(13) + Chr(10) cContent += Chr(13) + Chr(10) cContent += '⚠️ **ADVERTENCIA**: Esta skill puede modificar/eliminar archivos del sistema.' + Chr(13) + Chr(10) MemoWrit('skills\filesystem\SKILL.md', cContent) retu NIL function ParseSkill(cPath) local cFile := cPath + '\SKILL.md' local cContent, aLines, cLine local oSkill := {=>} local lInFront := .f. local aParts, cKey, cValue
if !File(cFile) retu NIL endif cContent := MemoRead(cFile) if Empty(cContent) retu NIL endif aLines := hb_ATokens(cContent, Chr(13) + Chr(10)) for each cLine in aLines cLine := AllTrim(cLine) if cLine == '---' lInFront := !lInFront loop endif if lInFront .and. ':' $ cLine aParts := hb_ATokens(cLine, ':') if Len(aParts) >= 2 cKey := AllTrim(aParts[1]) cValue := AllTrim(SubStr(cLine, At(':', cLine) + 1)) cValue := StrTran(cValue, '"', '') cValue := StrTran(cValue, "'", "") do case case Lower(cValue) == 'true' oSkill[cKey] := .t. case Lower(cValue) == 'false' oSkill[cKey] := .f. otherwise oSkill[cKey] := cValue endcase endif endif if !lInFront .and. !Empty(oSkill) exit endif next // Defaults if !hb_HHasKey(oSkill, 'version') oSkill['version'] := '1.0.0' endif if !hb_HHasKey(oSkill, 'dangerous') oSkill['dangerous'] := .f. endif if !hb_HHasKey(oSkill, 'requires_permission') oSkill['requires_permission'] := .t. endif if Empty(hb_HGetDef(oSkill, 'name', '')) retu NIL endif retu oSkill

Re: OpenClaw concepts on HIX

Posted: Mon Feb 16, 2026 1:59 pm
by Antonio Linares

HIX + Gemini (chat memory support)

Image

gemini_chat.prg

Code: Select all | Expand

function main()
    local cHtml := ""
    
cHtml += "<!DOCTYPE html>" cHtml += "<html lang='es'>" cHtml += "<head>" cHtml += " <meta charset='UTF-8'>" cHtml += " <meta name='viewport' content='width=device-width, initial-scale=1.0'>" cHtml += " <title>HIX AIOS v13 - Chat Premium</title>" cHtml += " <link href='https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600&family=JetBrains+Mono&display=swap' rel='stylesheet'>" cHtml += " <style>" cHtml += " :root {" cHtml += " --bg: #0a0b10;" cHtml += " --glass: rgba(255, 255, 255, 0.05);" cHtml += " --glass-border: rgba(255, 255, 255, 0.1);" cHtml += " --accent: linear-gradient(135deg, #6366f1, #a855f7);" cHtml += " --text: #e2e8f0;" cHtml += " --bot-bubble: rgba(30, 41, 59, 0.7);" cHtml += " --user-bubble: linear-gradient(135deg, #4f46e5, #7c3aed);" cHtml += " }" cHtml += " * { margin:0; padding:0; box-sizing: border-box; }" cHtml += " body {" cHtml += " font-family: 'Outfit', sans-serif;" cHtml += " background: var(--bg);" cHtml += " background-image: radial-gradient(circle at 20% 20%, rgba(99, 102, 241, 0.15) 0%, transparent 40%)," cHtml += " radial-gradient(circle at 80% 80%, rgba(168, 85, 247, 0.15) 0%, transparent 40%);" cHtml += " color: var(--text);" cHtml += " height: 100vh;" cHtml += " display: flex;" cHtml += " flex-direction: column;" cHtml += " overflow: hidden;" cHtml += " }" cHtml += " header {" cHtml += " padding: 1.5rem 2rem;" cHtml += " background: var(--glass);" cHtml += " backdrop-filter: blur(10px);" cHtml += " border-bottom: 1px solid var(--glass-border);" cHtml += " display: flex;" cHtml += " justify-content: space-between;" cHtml += " align-items: center;" cHtml += " z-index: 10;" cHtml += " }" cHtml += " header h1 { font-size: 1.5rem; font-weight: 600; letter-spacing: -0.5px; }" cHtml += " .version-badge { background: var(--accent); padding: 0.2rem 0.6rem; border-radius: 20px; font-size: 0.75rem; font-weight: bold; }" cHtml += " #chat-container {" cHtml += " flex: 1;" cHtml += " overflow-y: auto;" cHtml += " padding: 2rem;" cHtml += " display: flex;" cHtml += " flex-direction: column;" cHtml += " gap: 1.5rem;" cHtml += " }" cHtml += " .message {" cHtml += " max-width: 85%;" cHtml += " padding: 1rem 1.25rem;" cHtml += " border-radius: 1.2rem;" cHtml += " line-height: 1.6;" cHtml += " animation: fadeIn 0.3s ease-out;" cHtml += " }" cHtml += " @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }" cHtml += " .user { align-self: flex-end; background: var(--user-bubble); color: white; border-bottom-right-radius: 0.2rem; }" cHtml += " .bot { align-self: flex-start; background: var(--bot-bubble); backdrop-filter: blur(5px); border: 1px solid var(--glass-border); border-bottom-left-radius: 0.2rem; }" cHtml += " .error { align-self: center; background: rgba(255,0,0,0.2); color: #ff8888; border: 1px solid #ff0000; padding: 0.5rem 1rem; border-radius: 10px; }" cHtml += " pre { background: #1e1e1e; padding: 1rem; border-radius: 8px; overflow-x: auto; font-family: 'JetBrains Mono', monospace; }" cHtml += " .input-wrapper { padding: 2rem; background: linear-gradient(to top, var(--bg), transparent); }" cHtml += " .input-box {" cHtml += " max-width: 900px; margin: 0 auto; background: var(--glass); backdrop-filter: blur(20px); border: 1px solid var(--glass-border);" cHtml += " border-radius: 1.5rem; padding: 0.5rem; display: flex; align-items: center; gap: 0.5rem;" cHtml += " }" cHtml += " input { flex: 1; background: transparent; border: none; color: white; padding: 0.75rem 1rem; font-size: 1rem; outline: none; }" cHtml += " button { background: var(--accent); border: none; color: white; padding: 0.75rem 1.5rem; border-radius: 1.1rem; font-weight: 600; cursor: pointer; }" cHtml += " button:disabled { background: #444; }" cHtml += " .typing { display: flex; gap: 4px; padding: 10px; }" cHtml += " .dot { width: 8px; height: 8px; background: #6366f1; border-radius: 50%; animation: bounce 1.4s infinite ease-in-out; }" cHtml += " @keyframes bounce { 0%, 80%, 100% { transform: scale(0); } 40% { transform: scale(1); } }" cHtml += " </style>" cHtml += "</head>" cHtml += "<body>" cHtml += "<header>" cHtml += " <h1>HIX AIOS <span class='version-badge'>v13</span></h1>" cHtml += " <button onclick='clearChat()'>Limpiar</button>" cHtml += "</header>" cHtml += "<div id='chat-container'></div>" cHtml += "<div class='input-wrapper'>" cHtml += " <div class='input-box'>" cHtml += " <input type='text' id='input' placeholder='Pregunta algo...' onkeydown='if(event.keyCode===13) sendMessage()'>" cHtml += " <button id='send-btn' onclick='sendMessage()'>Enviar</button>" cHtml += " </div>" cHtml += "</div>" cHtml += "<script>" cHtml += " window.onerror = function(m, u, l) { alert('Error: ' + m + ' en ' + l); };" cHtml += " var history_data = [];" cHtml += " function addMessage(role, text) {" cHtml += " var container = document.getElementById('chat-container');" cHtml += " var div = document.createElement('div');" cHtml += " div.className = 'message ' + role;" cHtml += " div.innerHTML = text.split('\\n').join('<br>');" cHtml += " container.appendChild(div);" cHtml += " setTimeout(function() { container.scrollTop = container.scrollHeight; }, 50);" cHtml += " }" cHtml += " function addTyping() {" cHtml += " var container = document.getElementById('chat-container');" cHtml += " var div = document.createElement('div');" cHtml += " div.id = 'typing-indicator';" cHtml += " div.className = 'message bot typing';" cHtml += " div.innerHTML = 'ÔÜÖ´©Å Pensando...';" cHtml += " container.appendChild(div);" cHtml += " container.scrollTop = container.scrollHeight;" cHtml += " }" cHtml += " function removeTyping() {" cHtml += " var indicator = document.getElementById('typing-indicator');" cHtml += " if(indicator) indicator.parentNode.removeChild(indicator);" cHtml += " }" cHtml += " function sendMessage() {" cHtml += " var input = document.getElementById('input');" cHtml += " var text = input.value.trim();" cHtml += " var btn = document.getElementById('send-btn');" cHtml += " if (!text) return;" cHtml += " addMessage('user', text);" cHtml += " input.value = '';" cHtml += " addTyping();" cHtml += " btn.disabled = true;" cHtml += " var params = 'query=' + encodeURIComponent(text);" cHtml += " if (history_data.length > 0) params += '&history=' + encodeURIComponent(JSON.stringify(history_data));" cHtml += " console.log('SENDING:', params);" cHtml += " var xhr = new XMLHttpRequest();" cHtml += " xhr.open('POST', 'gemini_aios_v13.prg', true);" cHtml += " xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');" cHtml += " xhr.onreadystatechange = function() {" cHtml += " if (xhr.status !== 200 && xhr.status !== 0) { addMessage('error', 'Status: ' + xhr.status); }" cHtml += " if (xhr.readyState == 4) {" cHtml += " removeTyping(); btn.disabled = false;" cHtml += " if (xhr.status == 200) {" cHtml += " try {" cHtml += " var data = JSON.parse(xhr.responseText);" cHtml += " if (data.success) {" cHtml += " addMessage('bot', data.text);" cHtml += " history_data.push({role:'user', parts:[{text:text}]});" cHtml += " history_data.push({role:'model', parts:[{text:data.text}]});" cHtml += " } else { addMessage('error', 'Error: ' + data.error); }" cHtml += " } catch(e) { addMessage('error', 'JSON Error: ' + xhr.responseText.substring(0,100)); }" cHtml += " } else if(xhr.status !== 0) { addMessage('error', 'Server Error: ' + xhr.status); }" cHtml += " }" cHtml += " };" cHtml += " xhr.send(params);" cHtml += " }" cHtml += " function clearChat() {" cHtml += " document.getElementById('chat-container').innerHTML = '';" cHtml += " history_data = [];" cHtml += " }" cHtml += " window.onload = function() {" cHtml += " addMessage('bot', 'HIX AIOS v13 listo. Modo ultra-estable activado.');" cHtml += " };" cHtml += "</script>" cHtml += "</body></html>" UWrite(cHtml) return ""

gemini_aios_v13.prg

Code: Select all | Expand

// gemini_aios_v13.prg - Memory Fix Version
#include "hbcurl.ch"

function main()
    local cQuery, cModel, cHistory, cLogDoc := ""
    local hResult
    
cQuery := UGet("query") cModel := UGet("model") cHistory := UGet("history") if Empty(cQuery) ; cQuery := UPost("query") ; endif if Empty(cModel) ; cModel := UPost("model") ; endif if Empty(cHistory) ; cHistory := UPost("history") ; endif // Auto-decode if raw if !Empty(cHistory) .and. "%" $ hb_ValToStr(cHistory) cHistory := Hix_UrlDecode(hb_ValToStr(cHistory)) endif if !Empty(cQuery) .and. "%" $ hb_ValToStr(cQuery) cQuery := Hix_UrlDecode(hb_ValToStr(cQuery)) endif cLogDoc := hb_eol() + ">>> [" + Time() + "] NEW REQ" + hb_eol() cLogDoc += "QUERY: " + hb_ValToStr(cQuery) + hb_eol() if cQuery == "ping" UWrite('{"success":true, "text":"pong"}') return "" endif if Empty(cQuery) UWrite('{"success":false, "error":"No query provided"}') return "" endif if Empty(cModel) ; cModel := "gemini-2.0-flash" ; endif hResult := ExecuteReasoningLoop(cQuery, cModel, hb_ValToStr(cHistory), @cLogDoc) cLogDoc += "<<< [" + Time() + "] RESP SENT" + hb_eol() MemoWrit("aios_debug.log", MemoRead("aios_debug.log") + cLogDoc) UWrite(hb_jsonEncode(hResult)) return "" //------------------------------------------------------------------------------ function ExecuteReasoningLoop(cQuery, cModel, cHistory, cTrace) local aFunctions := BuildAiosFunctions() local aMessages := {}, nMaxSteps := 5, nStep := 0 local hResult := {=>}, aStepsArr := {}, hMsg, hGeminiResult, hSkillResult local nErr
if !Empty(cHistory) nErr := hb_jsonDecode(cHistory, @aMessages) // In HIX, hb_jsonDecode returns the POS where it stopped. // If it reaches the end, it's successful. if ValType(aMessages) != "A" .or. Len(aMessages) == 0 if nErr == 0 .or. nErr < Len(AllTrim(cHistory)) cTrace += "!! History Decode Failed. Pos: " + hb_ValToStr(nErr) + hb_eol() aMessages := {} endif endif endif if ValType(aMessages) != "A" ; aMessages := {} ; endif cTrace += "HIST_TURNS: " + hb_ValToStr(Len(aMessages)) + hb_eol() aAdd(aMessages, { "role" => "user", "parts" => { { "text" => cQuery } } }) do while nStep < nMaxSteps nStep++ hGeminiResult := GeminiCallFC(aMessages, aFunctions, cModel, @cTrace) if !hGeminiResult['success'] hResult['success'] := .f. ; hResult['error'] := hGeminiResult['error'] ; retu hResult endif hMsg := { "role" => "model", "parts" => hGeminiResult["raw_parts"] } aAdd(aMessages, hMsg) if hGeminiResult['type'] == 'function_call' aAdd(aStepsArr, { "type" => "call", "name" => hGeminiResult['function_name'], "args" => hGeminiResult['arguments'] }) hSkillResult := ExecuteAiosSkill(hGeminiResult['function_name'], hGeminiResult['arguments']) aAdd(aMessages, { "role" => "function", "parts" => { { "functionResponse" => { "name" => hGeminiResult['function_name'], "response" => { "content" => hb_jsonEncode(hSkillResult) } } } } }) aAdd(aStepsArr, { "type" => "result", "success" => hSkillResult['success'], "result" => hSkillResult }) else hResult['success'] := .t. ; hResult['text'] := hGeminiResult['text'] ; hResult['steps'] := aStepsArr ; hResult['model_used'] := cModel retu hResult endif enddo hResult['success'] := .f. ; hResult['error'] := "Max steps" retu hResult //------------------------------------------------------------------------------ function GeminiCallFC(aMessages, aFunctions, cModel, cTrace) local cApiKey := GetAiosApiKey(), cUrl, cJson, hCurl, nError, cResponse := "", hResult := {=>}
cUrl := "https://generativelanguage.googleapis.com/v1beta/models/" + cModel + ":generateContent?key=" + cApiKey cJson := hb_jsonEncode({ "contents" => aMessages, "tools" => { { "function_declarations" => aFunctions } } }) hCurl := curl_easy_init() if !Empty(hCurl) curl_easy_setopt(hCurl, HB_CURLOPT_POST, .T.) curl_easy_setopt(hCurl, HB_CURLOPT_URL, cUrl) curl_easy_setopt(hCurl, HB_CURLOPT_HTTPHEADER, { "Content-Type: application/json" }) curl_easy_setopt(hCurl, HB_CURLOPT_SSL_VERIFYPEER, .F.) curl_easy_setopt(hCurl, HB_CURLOPT_POSTFIELDS, cJson) curl_easy_setopt(hCurl, HB_CURLOPT_DL_BUFF_SETUP) nError := curl_easy_perform(hCurl) if nError == HB_CURLE_OK cResponse := curl_easy_dl_buff_get(hCurl) hResult := ParseAiosFCResponse(cResponse, @cTrace) else hResult['success'] := .f. ; hResult['error'] := "Curl error: " + hb_ValToStr(nError) cTrace += "CURL_ERR: " + hb_ValToStr(nError) + hb_eol() endif curl_easy_cleanup(hCurl) else ; hResult['success'] := .f. ; hResult['error'] := "Curl init failed" ; endif retu hResult //------------------------------------------------------------------------------ function ParseAiosFCResponse(cJSON, cTrace) local hResult := { "success" => .f. }, hResponse := {=>}, aParts, nErr := 0 local nStart, nEnd, cWorking
cJSON := StrTran(hb_ValToStr(cJSON), Chr(0), "") cWorking := AllTrim(cJSON) nStart := At("{", cWorking) nEnd := RAt("}", cWorking) if nStart > 0 .and. nEnd > nStart cWorking := SubStr(cWorking, nStart, nEnd - nStart + 1) endif nErr := hb_jsonDecode(cWorking, @hResponse) do while nErr > 0 .and. Len(cWorking) > 10 cWorking := Left(cWorking, Len(cWorking) - 1) if Right(cWorking, 1) $ '}"0123456789' nErr := hb_jsonDecode(cWorking, @hResponse) endif enddo if (nErr == 0 .or. nErr == Len(cWorking)) .and. hb_HHasKey(hResponse, "candidates") .and. Len(hResponse["candidates"]) > 0 aParts := hResponse["candidates"][1]["content"]["parts"] hResult['success'] := .t. ; hResult['raw_parts'] := aParts if hb_HHasKey(aParts[1], "functionCall") hResult['type'] := 'function_call' hResult['function_name'] := aParts[1]["functionCall"]["name"] hResult['arguments'] := aParts[1]["functionCall"]["args"] else hResult['type'] := 'text' hResult['text'] := hb_HGetDef(aParts[1], "text", "") endif else hResult['error'] := "API JSON malformed (" + hb_ValToStr(nErr) + ")" cTrace += "!! Decoder failed on: " + Left(cWorking, 100) + hb_eol() endif retu hResult //------------------------------------------------------------------------------ function BuildAiosFunctions() local aFuncs := {} aAdd(aFuncs, { "name" => "filesystem_search", "description" => "Search files. pattern, path.", "parameters" => { "type" => "object", "properties" => { "pattern" => { "type" => "string" }, "path" => { "type" => "string" } }, "required" => {"pattern", "path"} } }) aAdd(aFuncs, { "name" => "filesystem_read", "description" => "Read file content.", "parameters" => { "type" => "object", "properties" => { "file" => { "type" => "string" } }, "required" => {"file"} } }) aAdd(aFuncs, { "name" => "filesystem_write", "description" => "Write file content.", "parameters" => { "type" => "object", "properties" => { "file" => { "type" => "string" }, "content" => { "type" => "string" } }, "required" => {"file", "content"} } }) aAdd(aFuncs, { "name" => "shell_execute", "description" => "Execute shell command.", "parameters" => { "type" => "object", "properties" => { "command" => { "type" => "string" } }, "required" => {"command"} } }) aAdd(aFuncs, { "name" => "identity_get_context", "description" => "Returns the identity and user soul information. Use this to know who you are and who the user is.", "parameters" => { "type" => "object", "properties" => {=>}, "required" => {} } }) retu aFuncs //------------------------------------------------------------------------------ function ExecuteAiosSkill(cName, hArgs) local hRes := { "success" => .f., "error" => "" } do case case cName == 'filesystem_search' .or. cName == 'filesystem_read' .or. cName == 'filesystem_write' hRes := Aios_FileSystem(cName, hArgs) case cName == 'shell_execute' hRes := Aios_Shell(hArgs) case cName == 'identity_get_context' hRes := Aios_Identity() endcase retu hRes //------------------------------------------------------------------------------ function Aios_Identity() local hR := { "success" => .t. }, cId := "", cSoul := "" if File("persona\IDENTITY.md") ; cId := MemoRead("persona\IDENTITY.md") ; endif if File("persona\SOUL.md") ; cSoul := MemoRead("persona\SOUL.md") ; endif hR['identity'] := cId hR['soul'] := cSoul if Empty(cId) .and. Empty(cSoul) ; hR['success'] := .f. ; hR['error'] := "Identity files not found" ; endif retu hR //------------------------------------------------------------------------------ function Aios_FileSystem(cAct, hP) local hR := { "success" => .t. }, cPat, cPath, cFile, aRes := {} if cAct == 'filesystem_search' cPat := hb_HGetDef(hP, 'pattern', '*.*') ; cPath := hb_HGetDef(hP, 'path', 'c:\hix') for each cFile in Directory(cPath + '\' + cPat) if !(cFile[1] $ '.,') ; aAdd(aRes, { "name" => cFile[1], "size" => cFile[2], "date" => DToC(cFile[3]), "time" => cFile[4] }) ; endif next hR['files'] := aRes ; hR['count'] := Len(aRes) elseif cAct == 'filesystem_read' cFile := hb_HGetDef(hP, 'file', '') if File(cFile) ; hR['content'] := Left(MemoRead(cFile), 5000) ; else ; hR['success'] := .f. ; hR['error'] := "File not found" ; endif elseif cAct == 'filesystem_write' if MemoWrit(hb_HGetDef(hP, 'file', ''), hb_HGetDef(hP, 'content', '')) ; hR['msg'] := "Stored" else ; hR['success'] := .f. ; hR['error'] := "Writ error" ; endif endif retu hR //------------------------------------------------------------------------------ function Aios_Shell(hP) local hR := { "success" => .t. }, cCmd := hb_HGetDef(hP, 'command', '') if Empty(cCmd) ; hR['success'] := .f. ; return hR ; endif hb_Run(cCmd + " > shell_out.tmp 2>&1") hR['output'] := MemoRead("shell_out.tmp") ; FErase("shell_out.tmp") retu hR //------------------------------------------------------------------------------ function GetAiosApiKey() local cKey := GetEnv("GEMINI_API_KEY") if Empty(cKey) .and. File("gemini_config.json") cKey := hb_HGetDef(hb_jsonDecode(MemoRead("gemini_config.json")), "api_key", "") endif retu cKey //------------------------------------------------------------------------------ function Hix_UrlDecode( cT ) local cR := "", n := 1, cX if Empty(cT) ; return "" ; endif cT := StrTran( cT, "+", " " ) do while n <= Len( cT ) if SubStr( cT, n, 1 ) == "%" cX := SubStr( cT, n + 1, 2 ) ; cR += Chr( Hix_HexToNum( cX ) ) ; n += 3 else ; cR += SubStr( cT, n, 1 ) ; n++ ; endif enddo retu cR //------------------------------------------------------------------------------ function Hix_HexToNum( cX ) local n := 0, i, cC, nV cX := Upper( cX ) for i := 1 to Len( cX ) cC := SubStr( cX, i, 1 ) ; nV := At( cC, "0123456789ABCDEF" ) - 1 if nV >= 0 ; n := n * 16 + nV ; endif next retu n

Re: OpenClaw concepts on HIX

Posted: Mon Feb 16, 2026 2:33 pm
by Antonio Linares

Persistent support for "PERSONA" files: IDENTITY.md and SOUL.md (like OpenClaw)

Image

gemini_aios_v13.prg

Code: Select all | Expand

// gemini_aios_v13.prg - Memory Fix Version
#include "hbcurl.ch"

function main()
    local cQuery, cModel, cHistory, cLogDoc := "", hIdContext, cSystemPrompt := ""
    local hResult
    
cQuery := UGet("query") cModel := UGet("model") cHistory := UGet("history") if Empty(cQuery) ; cQuery := UPost("query") ; endif if Empty(cModel) ; cModel := UPost("model") ; endif if Empty(cHistory) ; cHistory := UPost("history") ; endif // Auto-decode if raw if !Empty(cHistory) .and. "%" $ hb_ValToStr(cHistory) cHistory := Hix_UrlDecode(hb_ValToStr(cHistory)) endif if !Empty(cQuery) .and. "%" $ hb_ValToStr(cQuery) cQuery := Hix_UrlDecode(hb_ValToStr(cQuery)) endif cLogDoc := hb_eol() + ">>> [" + Time() + "] NEW REQ" + hb_eol() cLogDoc += "QUERY: " + hb_ValToStr(cQuery) + hb_eol() if cQuery == "ping" UWrite('{"success":true, "text":"pong"}') return "" endif if Empty(cQuery) UWrite('{"success":false, "error":"No query provided"}') return "" endif if Empty(cModel) ; cModel := "gemini-2.0-flash" ; endif // Pre-load identity context as system instructions hIdContext := Aios_Identity() cSystemPrompt := "Eres HIX AIOS (Harbourix AI Operating System)." + hb_eol() if hIdContext['success'] cSystemPrompt += "Tu IDENTIDAD actual:" + hb_eol() + hIdContext['identity'] + hb_eol() cSystemPrompt += "Tu ALMA (User Data):" + hb_eol() + hIdContext['soul'] + hb_eol() cSystemPrompt += "IMPORTANTE: Puedes actualizar estos ficheros usando filesystem_write en las rutas: " + hb_eol() cSystemPrompt += "- persona\IDENTITY.md" + hb_eol() cSystemPrompt += "- persona\SOUL.md" + hb_eol() endif hResult := ExecuteReasoningLoop(cQuery, cModel, hb_ValToStr(cHistory), @cLogDoc, cSystemPrompt) cLogDoc += "<<< [" + Time() + "] RESP SENT" + hb_eol() MemoWrit("aios_debug.log", MemoRead("aios_debug.log") + cLogDoc) UWrite(hb_jsonEncode(hResult)) return "" //------------------------------------------------------------------------------ function ExecuteReasoningLoop(cQuery, cModel, cHistory, cTrace, cSystemPrompt) local aFunctions := BuildAiosFunctions() local aMessages := {}, nMaxSteps := 5, nStep := 0 local hResult := {=>}, aStepsArr := {}, hMsg, hGeminiResult, hSkillResult local nErr
if !Empty(cHistory) nErr := hb_jsonDecode(cHistory, @aMessages) // In HIX, hb_jsonDecode returns the POS where it stopped. // If it reaches the end, it's successful. if ValType(aMessages) != "A" .or. Len(aMessages) == 0 if nErr == 0 .or. nErr < Len(AllTrim(cHistory)) cTrace += "!! History Decode Failed. Pos: " + hb_ValToStr(nErr) + hb_eol() aMessages := {} endif endif endif if ValType(aMessages) != "A" ; aMessages := {} ; endif cTrace += "HIST_TURNS: " + hb_ValToStr(Len(aMessages)) + hb_eol() aAdd(aMessages, { "role" => "user", "parts" => { { "text" => cQuery } } }) do while nStep < nMaxSteps nStep++ hGeminiResult := GeminiCallFC(aMessages, aFunctions, cModel, @cTrace, cSystemPrompt) if !hGeminiResult['success'] hResult['success'] := .f. ; hResult['error'] := hGeminiResult['error'] ; retu hResult endif hMsg := { "role" => "model", "parts" => hGeminiResult["raw_parts"] } aAdd(aMessages, hMsg) if hGeminiResult['type'] == 'function_call' aAdd(aStepsArr, { "type" => "call", "name" => hGeminiResult['function_name'], "args" => hGeminiResult['arguments'] }) hSkillResult := ExecuteAiosSkill(hGeminiResult['function_name'], hGeminiResult['arguments']) aAdd(aMessages, { "role" => "function", "parts" => { { "functionResponse" => { "name" => hGeminiResult['function_name'], "response" => { "content" => hb_jsonEncode(hSkillResult) } } } } }) aAdd(aStepsArr, { "type" => "result", "success" => hSkillResult['success'], "result" => hSkillResult }) else hResult['success'] := .t. ; hResult['text'] := hGeminiResult['text'] ; hResult['steps'] := aStepsArr ; hResult['model_used'] := cModel retu hResult endif enddo hResult['success'] := .f. ; hResult['error'] := "Max steps" retu hResult //------------------------------------------------------------------------------ function GeminiCallFC(aMessages, aFunctions, cModel, cTrace, cSystemPrompt) local cApiKey := GetAiosApiKey(), cUrl, cJson, hCurl, nError, cResponse := "", hResult := {=>} local hPayload := { "contents" => aMessages, "tools" => { { "function_declarations" => aFunctions } } }
if !Empty(cSystemPrompt) hPayload["system_instruction"] := { "parts" => { { "text" => cSystemPrompt } } } endif cUrl := "https://generativelanguage.googleapis.com/v1beta/models/" + cModel + ":generateContent?key=" + cApiKey cJson := hb_jsonEncode(hPayload) hCurl := curl_easy_init() if !Empty(hCurl) curl_easy_setopt(hCurl, HB_CURLOPT_POST, .T.) curl_easy_setopt(hCurl, HB_CURLOPT_URL, cUrl) curl_easy_setopt(hCurl, HB_CURLOPT_HTTPHEADER, { "Content-Type: application/json" }) curl_easy_setopt(hCurl, HB_CURLOPT_SSL_VERIFYPEER, .F.) curl_easy_setopt(hCurl, HB_CURLOPT_POSTFIELDS, cJson) curl_easy_setopt(hCurl, HB_CURLOPT_DL_BUFF_SETUP) nError := curl_easy_perform(hCurl) if nError == HB_CURLE_OK cResponse := curl_easy_dl_buff_get(hCurl) hResult := ParseAiosFCResponse(cResponse, @cTrace) else hResult['success'] := .f. ; hResult['error'] := "Curl error: " + hb_ValToStr(nError) cTrace += "CURL_ERR: " + hb_ValToStr(nError) + hb_eol() endif curl_easy_cleanup(hCurl) else ; hResult['success'] := .f. ; hResult['error'] := "Curl init failed" ; endif retu hResult //------------------------------------------------------------------------------ function ParseAiosFCResponse(cJSON, cTrace) local hResult := { "success" => .f. }, hResponse := {=>}, aParts, nErr := 0 local nStart, nEnd, cWorking
cJSON := StrTran(hb_ValToStr(cJSON), Chr(0), "") cWorking := AllTrim(cJSON) nStart := At("{", cWorking) nEnd := RAt("}", cWorking) if nStart > 0 .and. nEnd > nStart cWorking := SubStr(cWorking, nStart, nEnd - nStart + 1) endif nErr := hb_jsonDecode(cWorking, @hResponse) do while nErr > 0 .and. Len(cWorking) > 10 cWorking := Left(cWorking, Len(cWorking) - 1) if Right(cWorking, 1) $ '}"0123456789' nErr := hb_jsonDecode(cWorking, @hResponse) endif enddo if (nErr == 0 .or. nErr == Len(cWorking)) .and. hb_HHasKey(hResponse, "candidates") .and. Len(hResponse["candidates"]) > 0 aParts := hResponse["candidates"][1]["content"]["parts"] hResult['success'] := .t. ; hResult['raw_parts'] := aParts if hb_HHasKey(aParts[1], "functionCall") hResult['type'] := 'function_call' hResult['function_name'] := aParts[1]["functionCall"]["name"] hResult['arguments'] := aParts[1]["functionCall"]["args"] else hResult['type'] := 'text' hResult['text'] := hb_HGetDef(aParts[1], "text", "") endif else hResult['error'] := "API JSON malformed (" + hb_ValToStr(nErr) + ")" cTrace += "!! Decoder failed on: " + Left(cWorking, 100) + hb_eol() endif retu hResult //------------------------------------------------------------------------------ function BuildAiosFunctions() local aFuncs := {} aAdd(aFuncs, { "name" => "filesystem_search", "description" => "Search files. pattern, path.", "parameters" => { "type" => "object", "properties" => { "pattern" => { "type" => "string" }, "path" => { "type" => "string" } }, "required" => {"pattern", "path"} } }) aAdd(aFuncs, { "name" => "filesystem_read", "description" => "Read file content.", "parameters" => { "type" => "object", "properties" => { "file" => { "type" => "string" } }, "required" => {"file"} } }) aAdd(aFuncs, { "name" => "filesystem_write", "description" => "Write file content.", "parameters" => { "type" => "object", "properties" => { "file" => { "type" => "string" }, "content" => { "type" => "string" } }, "required" => {"file", "content"} } }) aAdd(aFuncs, { "name" => "shell_execute", "description" => "Execute shell command.", "parameters" => { "type" => "object", "properties" => { "command" => { "type" => "string" } }, "required" => {"command"} } }) aAdd(aFuncs, { "name" => "identity_get_context", "description" => "Returns the identity and user soul information. Use this to know who you are and who the user is.", "parameters" => { "type" => "object", "properties" => {=>}, "required" => {} } }) retu aFuncs //------------------------------------------------------------------------------ function ExecuteAiosSkill(cName, hArgs) local hRes := { "success" => .f., "error" => "" } do case case cName == 'filesystem_search' .or. cName == 'filesystem_read' .or. cName == 'filesystem_write' hRes := Aios_FileSystem(cName, hArgs) case cName == 'shell_execute' hRes := Aios_Shell(hArgs) case cName == 'identity_get_context' hRes := Aios_Identity() endcase retu hRes //------------------------------------------------------------------------------ function Aios_Identity() local hR := { "success" => .t. }, cId := "", cSoul := "" if File("persona\IDENTITY.md") ; cId := MemoRead("persona\IDENTITY.md") ; endif if File("persona\SOUL.md") ; cSoul := MemoRead("persona\SOUL.md") ; endif hR['identity'] := cId hR['soul'] := cSoul if Empty(cId) .and. Empty(cSoul) ; hR['success'] := .f. ; hR['error'] := "Identity files not found" ; endif retu hR //------------------------------------------------------------------------------ function Aios_FileSystem(cAct, hP) local hR := { "success" => .t. }, cPat, cPath, cFile, aRes := {} if cAct == 'filesystem_search' cPat := hb_HGetDef(hP, 'pattern', '*.*') ; cPath := hb_HGetDef(hP, 'path', 'c:\hix') for each cFile in Directory(cPath + '\' + cPat) if !(cFile[1] $ '.,') ; aAdd(aRes, { "name" => cFile[1], "size" => cFile[2], "date" => DToC(cFile[3]), "time" => cFile[4] }) ; endif next hR['files'] := aRes ; hR['count'] := Len(aRes) elseif cAct == 'filesystem_read' cFile := hb_HGetDef(hP, 'file', '') if File(cFile) ; hR['content'] := Left(MemoRead(cFile), 5000) ; else ; hR['success'] := .f. ; hR['error'] := "File not found" ; endif elseif cAct == 'filesystem_write' if MemoWrit(hb_HGetDef(hP, 'file', ''), hb_HGetDef(hP, 'content', '')) ; hR['msg'] := "Stored" else ; hR['success'] := .f. ; hR['error'] := "Writ error" ; endif endif retu hR //------------------------------------------------------------------------------ function Aios_Shell(hP) local hR := { "success" => .t. }, cCmd := hb_HGetDef(hP, 'command', '') if Empty(cCmd) ; hR['success'] := .f. ; return hR ; endif hb_Run(cCmd + " > shell_out.tmp 2>&1") hR['output'] := MemoRead("shell_out.tmp") ; FErase("shell_out.tmp") retu hR //------------------------------------------------------------------------------ function GetAiosApiKey() local cKey := GetEnv("GEMINI_API_KEY") if Empty(cKey) .and. File("gemini_config.json") cKey := hb_HGetDef(hb_jsonDecode(MemoRead("gemini_config.json")), "api_key", "") endif retu cKey //------------------------------------------------------------------------------ function Hix_UrlDecode( cT ) local cR := "", n := 1, cX if Empty(cT) ; return "" ; endif cT := StrTran( cT, "+", " " ) do while n <= Len( cT ) if SubStr( cT, n, 1 ) == "%" cX := SubStr( cT, n + 1, 2 ) ; cR += Chr( Hix_HexToNum( cX ) ) ; n += 3 else ; cR += SubStr( cT, n, 1 ) ; n++ ; endif enddo retu cR //------------------------------------------------------------------------------ function Hix_HexToNum( cX ) local n := 0, i, cC, nV cX := Upper( cX ) for i := 1 to Len( cX ) cC := SubStr( cX, i, 1 ) ; nV := At( cC, "0123456789ABCDEF" ) - 1 if nV >= 0 ; n := n * 16 + nV ; endif next retu n

Re: OpenClaw concepts on HIX

Posted: Wed Feb 18, 2026 10:18 pm
by Antonio Linares

Telegram Skill for HIX:

telegram.prg

Code: Select all | Expand

// telegram.prg - Telegram Bot API Integration for HIX AIOS
#include "hbcurl.ch"

function Aios_Telegram(cAct, hP)
    local hR := { "success" => .t. }
    
do case case cAct == 'telegram_send_message' hR := Telegram_SendMessage(hb_HGetDef(hP, "chat_id", ""), hb_HGetDef(hP, "text", "")) case cAct == 'telegram_get_updates' hR := Telegram_GetUpdates() otherwise hR := { "success" => .f., "error" => "Telegram action not supported: " + cAct } endcase retu hR //------------------------------------------------------------------------------ function Telegram_SendMessage(cChatId, cText) local hR := { "success" => .f. }, cToken := GetTelegramToken() local cUrl, cPayload, hCurl, nError, cResponse := ""
if Empty(cToken) ; hR['error'] := "Telegram Token not found" ; retu hR ; endif if Empty(cChatId) ; hR['error'] := "Chat ID required" ; retu hR ; endif cUrl := "https://api.telegram.org/bot" + cToken + "/sendMessage" cPayload := hb_jsonEncode({ "chat_id" => cChatId, "text" => cText }) hCurl := curl_easy_init() if !Empty(hCurl) curl_easy_setopt(hCurl, HB_CURLOPT_POST, .T.) curl_easy_setopt(hCurl, HB_CURLOPT_URL, cUrl) curl_easy_setopt(hCurl, HB_CURLOPT_HTTPHEADER, { "Content-Type: application/json" }) curl_easy_setopt(hCurl, HB_CURLOPT_SSL_VERIFYPEER, .F.) curl_easy_setopt(hCurl, HB_CURLOPT_POSTFIELDS, cPayload) curl_easy_setopt(hCurl, HB_CURLOPT_DL_BUFF_SETUP) nError := curl_easy_perform(hCurl) if nError == HB_CURLE_OK cResponse := curl_easy_dl_buff_get(hCurl) hR['success'] := .t. hR['raw_response'] := cResponse else hR['error'] := "Curl error: " + hb_ValToStr(nError) endif curl_easy_cleanup(hCurl) else hR['error'] := "Curl init failed" endif retu hR //------------------------------------------------------------------------------ function Telegram_GetUpdates() local hR := { "success" => .f. }, cToken := GetTelegramToken() local cUrl, hCurl, nError, cResponse := ""
if Empty(cToken) ; hR['error'] := "Telegram Token not found" ; retu hR ; endif cUrl := "https://api.telegram.org/bot" + cToken + "/getUpdates" hCurl := curl_easy_init() if !Empty(hCurl) curl_easy_setopt(hCurl, HB_CURLOPT_URL, cUrl) curl_easy_setopt(hCurl, HB_CURLOPT_SSL_VERIFYPEER, .F.) curl_easy_setopt(hCurl, HB_CURLOPT_DL_BUFF_SETUP) nError := curl_easy_perform(hCurl) if nError == HB_CURLE_OK cResponse := curl_easy_dl_buff_get(hCurl) hR['success'] := .t. hR['updates'] := cResponse else hR['error'] := "Curl error: " + hb_ValToStr(nError) endif curl_easy_cleanup(hCurl) else hR['error'] := "Curl init failed" endif retu hR //------------------------------------------------------------------------------ function GetTelegramToken() local cKey := GetEnv("TELEGRAM_BOT_TOKEN") local hConfig if Empty(cKey) .and. File("telegram_config.json") hConfig := {=>} hb_jsonDecode(MemoRead("telegram_config.json"), @hConfig) cKey := hb_HGetDef(hConfig, "bot_token", "") endif retu cKey