agi.sysinfo.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package agi
  2. import (
  3. "encoding/json"
  4. "log"
  5. "sync"
  6. "time"
  7. "github.com/robertkrimen/otto"
  8. "imuslab.com/arozos/mod/agi/static"
  9. "imuslab.com/arozos/mod/disk/diskspace"
  10. usageinfo "imuslab.com/arozos/mod/info/usageinfo"
  11. "imuslab.com/arozos/mod/network/netstat"
  12. )
  13. /*
  14. AGI System Info Library
  15. Author: tobychui
  16. Exposes CPU, RAM and network usage to AGI scripts via the "sysinfo" library.
  17. Usage in AGI:
  18. requirelib("sysinfo");
  19. var cpu = sysinfo.getCPUUsage(); // float percentage 0-100
  20. var ram = sysinfo.getRAMUsage(); // {used, total, percent}
  21. var net = sysinfo.getNetworkUsage(); // {rxRate, txRate, rxTotal, txTotal}
  22. */
  23. // networkSample holds a single point-in-time reading of cumulative network bytes.
  24. type networkSample struct {
  25. rxBytes int64
  26. txBytes int64
  27. timestamp time.Time
  28. }
  29. var (
  30. netMu sync.Mutex
  31. prevNetSample *networkSample
  32. )
  33. func (g *Gateway) SysinfoLibRegister() {
  34. err := g.RegisterLib("sysinfo", g.injectSysinfoLibFunctions)
  35. if err != nil {
  36. log.Fatal(err)
  37. }
  38. }
  39. func (g *Gateway) injectSysinfoLibFunctions(payload *static.AgiLibInjectionPayload) {
  40. vm := payload.VM
  41. // CPU Usage – returns a float64 percentage (0–100)
  42. vm.Set("_sysinfo_getcpu", func(call otto.FunctionCall) otto.Value {
  43. usage := usageinfo.GetCPUUsage()
  44. result, _ := vm.ToValue(usage)
  45. return result
  46. })
  47. // RAM Usage – returns JSON {used, total, percent}
  48. vm.Set("_sysinfo_getram", func(call otto.FunctionCall) otto.Value {
  49. used, total := usageinfo.GetNumericRAMUsage()
  50. percent := float64(0)
  51. if total > 0 {
  52. percent = float64(used) / float64(total) * 100.0
  53. }
  54. resp := map[string]interface{}{
  55. "used": used,
  56. "total": total,
  57. "percent": percent,
  58. }
  59. js, _ := json.Marshal(resp)
  60. result, _ := vm.ToValue(string(js))
  61. return result
  62. })
  63. // Network Usage – returns JSON {rxRate, txRate, rxTotal, txTotal} (bytes and bytes/sec)
  64. vm.Set("_sysinfo_getnet", func(call otto.FunctionCall) otto.Value {
  65. rxRate, txRate, rxTotal, txTotal := getNetworkUsage()
  66. resp := map[string]interface{}{
  67. "rxRate": rxRate,
  68. "txRate": txRate,
  69. "rxTotal": rxTotal,
  70. "txTotal": txTotal,
  71. }
  72. js, _ := json.Marshal(resp)
  73. result, _ := vm.ToValue(string(js))
  74. return result
  75. })
  76. // Disk Info – returns JSON array of logical disk volumes
  77. vm.Set("_sysinfo_getdisk", func(call otto.FunctionCall) otto.Value {
  78. disks := diskspace.GetAllLogicDiskInfo()
  79. js, _ := json.Marshal(disks)
  80. result, _ := vm.ToValue(string(js))
  81. return result
  82. })
  83. //nolint:errcheck
  84. vm.Run(`
  85. var sysinfo = {};
  86. sysinfo.getCPUUsage = function() {
  87. return _sysinfo_getcpu();
  88. };
  89. sysinfo.getRAMUsage = function() {
  90. var raw = _sysinfo_getram();
  91. try { return JSON.parse(raw); } catch(e) { return {used: -1, total: -1, percent: 0}; }
  92. };
  93. sysinfo.getNetworkUsage = function() {
  94. var raw = _sysinfo_getnet();
  95. try { return JSON.parse(raw); } catch(e) { return {rxRate: 0, txRate: 0, rxTotal: 0, txTotal: 0}; }
  96. };
  97. sysinfo.getDiskInfo = function() {
  98. var raw = _sysinfo_getdisk();
  99. try { return JSON.parse(raw); } catch(e) { return []; }
  100. };
  101. `)
  102. }
  103. // getNetworkUsage returns byte rates and totals by delegating to the shared
  104. // netstat.GetNetworkInterfaceStats helper (supports Linux, Darwin, Windows).
  105. // GetNetworkInterfaceStats returns accumulated bits, so we convert to bytes before
  106. // computing the per-second rate against the previous sample.
  107. func getNetworkUsage() (rxRate float64, txRate float64, rxTotal int64, txTotal int64) {
  108. rxBits, txBits, err := netstat.GetNetworkInterfaceStats()
  109. if err != nil {
  110. return 0, 0, 0, 0
  111. }
  112. // Convert accumulated bits → bytes
  113. rx := rxBits / 8
  114. tx := txBits / 8
  115. now := time.Now()
  116. netMu.Lock()
  117. defer netMu.Unlock()
  118. if prevNetSample != nil {
  119. elapsed := now.Sub(prevNetSample.timestamp).Seconds()
  120. if elapsed > 0 {
  121. rxRate = float64(rx-prevNetSample.rxBytes) / elapsed
  122. txRate = float64(tx-prevNetSample.txBytes) / elapsed
  123. if rxRate < 0 {
  124. rxRate = 0
  125. }
  126. if txRate < 0 {
  127. txRate = 0
  128. }
  129. }
  130. }
  131. prevNetSample = &networkSample{
  132. rxBytes: rx,
  133. txBytes: tx,
  134. timestamp: now,
  135. }
  136. return rxRate, txRate, rx, tx
  137. }