thinknagent 0.1.24 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24,6 +24,7 @@ program
24
24
  .option('--name <name>', 'Display name for this server (default: hostname)')
25
25
  .option('--gpu', 'Enable GPU metrics (requires nvidia-smi)')
26
26
  .option('--interval <ms>', 'Metrics polling interval in ms (default: 1000)', '1000')
27
+ .option('--retention <days>', 'Disk history retention in days (default: 15)', '15')
27
28
  .option('--logs <paths>', 'Comma-separated log file paths to stream')
28
29
  .option('--app-path <path>', 'Path to the deployed application folder (to track version)')
29
30
  .option('-f, --force', 'Force overwrite existing registration')
@@ -48,18 +49,20 @@ program
48
49
  const serverUrl = (opts.server || 'https://thinkncollab.com').replace(/\/$/, '');
49
50
  const nodeName = opts.name || os.hostname();
50
51
  const intervalMs = opts.interval ? parseInt(opts.interval, 10) : (existing.interval || 1000);
52
+ const retentionDays = opts.retention ? parseInt(opts.retention, 10) : (existing.retentionDays || 15);
51
53
 
52
54
  const cfg = {
53
55
  ...existing,
54
56
  agentId,
55
57
  serverUrl,
56
- name: nodeName,
57
- interval: intervalMs,
58
- gpu: opts.gpu !== undefined ? !!opts.gpu : (existing.gpu || false),
59
- logs: opts.logs ? opts.logs.split(',').map(s => s.trim()) : (existing.logs || []),
60
- roomId: opts.room,
61
- appPath: opts.appPath || existing.appPath || null,
62
- alerts: existing.alerts || [
58
+ name: nodeName,
59
+ interval: intervalMs,
60
+ retentionDays: retentionDays,
61
+ gpu: opts.gpu !== undefined ? !!opts.gpu : (existing.gpu || false),
62
+ logs: opts.logs ? opts.logs.split(',').map(s => s.trim()) : (existing.logs || []),
63
+ roomId: opts.room,
64
+ appPath: opts.appPath || existing.appPath || null,
65
+ alerts: existing.alerts || [
63
66
  { id: 'cpu-high', metric: 'cpu.usage', op: 'gt', value: 85, for: 60, severity: 'warning' },
64
67
  { id: 'cpu-crit', metric: 'cpu.usage', op: 'gt', value: 95, for: 30, severity: 'critical' },
65
68
  { id: 'mem-high', metric: 'memory.usedPct', op: 'gt', value: 85, for: 60, severity: 'warning' },
@@ -76,13 +79,14 @@ program
76
79
 
77
80
  console.log(chalk.cyan('\n thinknagent') + chalk.gray(` v${require('../package.json').version}`));
78
81
  console.log(chalk.gray(' ─────────────────────────────────────────'));
79
- console.log(` Server : ${chalk.white(cfg.serverUrl)}`);
80
- console.log(` Name : ${chalk.white(cfg.name)}`);
81
- console.log(` Room ID : ${chalk.white(cfg.roomId)}`);
82
- console.log(` Agent ID: ${chalk.white(agentId)}`);
83
- console.log(` GPU : ${cfg.gpu ? chalk.green('enabled') : chalk.gray('disabled')}`);
84
- console.log(` Logs : ${cfg.logs.length ? chalk.white(cfg.logs.join(', ')) : chalk.gray('none')}`);
85
- console.log(` App Path: ${cfg.appPath ? chalk.white(cfg.appPath) : chalk.gray('none')}`);
82
+ console.log(` Server : ${chalk.white(cfg.serverUrl)}`);
83
+ console.log(` Name : ${chalk.white(cfg.name)}`);
84
+ console.log(` Room ID : ${chalk.white(cfg.roomId)}`);
85
+ console.log(` Retention : ${chalk.white(cfg.retentionDays + ' Days')}`);
86
+ console.log(` Agent ID : ${chalk.white(agentId)}`);
87
+ console.log(` GPU : ${cfg.gpu ? chalk.green('enabled') : chalk.gray('disabled')}`);
88
+ console.log(` Logs : ${cfg.logs.length ? chalk.white(cfg.logs.join(', ')) : chalk.gray('none')}`);
89
+ console.log(` App Path : ${cfg.appPath ? chalk.white(cfg.appPath) : chalk.gray('none')}`);
86
90
  console.log(chalk.gray(' ─────────────────────────────────────────'));
87
91
  console.log(chalk.green(' ✔ Configuration saved successfully!\n'));
88
92
 
@@ -100,8 +104,9 @@ program
100
104
  program
101
105
  .command('start')
102
106
  .description('Start the agent (connect to ThinkNCollab)')
103
- .option('--dev', 'Dev mode — verbose logging')
104
- .option('--interval <ms>', 'Metrics polling interval in ms (default: 1000)')
107
+ .option('--dev', 'Dev mode — verbose logging')
108
+ .option('--interval <ms>', 'Metrics polling interval in ms (default: 1000)')
109
+ .option('--retention <days>', 'Disk history retention in days (default: 15)')
105
110
  .action((opts) => {
106
111
  if (opts.dev) process.env.THINKNAGENT_DEV = '1';
107
112
 
@@ -115,6 +120,10 @@ program
115
120
  cfg.interval = parseInt(opts.interval, 10);
116
121
  store.write(cfg);
117
122
  }
123
+ if (opts.retention) {
124
+ cfg.retentionDays = parseInt(opts.retention, 10);
125
+ store.write(cfg);
126
+ }
118
127
 
119
128
  console.log(chalk.cyan(`\n Starting thinknagent — ${cfg.name || cfg.agentId}`));
120
129
  if (!cfg.agentToken) {
package/lib/history.js CHANGED
@@ -4,10 +4,12 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
  const os = require('os');
6
6
 
7
+ const store = require('./store');
8
+
7
9
  const HOME = os.homedir();
8
10
  const CONFIG_DIR = path.join(HOME, '.thinknagent');
9
11
  const HISTORY_DIR = path.join(CONFIG_DIR, 'history');
10
- const RETENTION_DAYS = 3;
12
+ const DEFAULT_RETENTION_DAYS = 15; // 15 Days default rolling retention
11
13
  const SNAPSHOT_INTERVAL_MS = 30000; // Snapshot to disk every 30 seconds
12
14
 
13
15
  class HistoryManager {
@@ -16,6 +18,11 @@ class HistoryManager {
16
18
  this._lastSnapshotTime = 0;
17
19
  }
18
20
 
21
+ getRetentionDays() {
22
+ const custom = store.get('retentionDays');
23
+ return (typeof custom === 'number' && custom > 0) ? custom : DEFAULT_RETENTION_DAYS;
24
+ }
25
+
19
26
  _ensureDir() {
20
27
  try {
21
28
  if (!fs.existsSync(HISTORY_DIR)) {
@@ -56,6 +63,7 @@ class HistoryManager {
56
63
  load: metrics.cpu?.loadAvg ?? 0,
57
64
  mem: metrics.memory?.usedPct ?? 0,
58
65
  memUsedMB: Math.round((metrics.memory?.used || 0) / (1024 * 1024)),
66
+ buffcache: metrics.memory?.buffcache ?? 0,
59
67
  rxSec: metrics.network?.[0]?.rxSec ?? 0,
60
68
  txSec: metrics.network?.[0]?.txSec ?? 0,
61
69
  procs: metrics.processes?.total ?? 0,
@@ -72,11 +80,12 @@ class HistoryManager {
72
80
  }
73
81
  }
74
82
 
75
- // Deletes any history files older than RETENTION_DAYS (3 days)
83
+ // Deletes any history files older than retentionDays (default 15 days or custom)
76
84
  _pruneOldFiles() {
77
85
  try {
86
+ const retentionDays = this.getRetentionDays();
78
87
  const files = fs.readdirSync(HISTORY_DIR);
79
- const cutoffTime = Date.now() - (RETENTION_DAYS * 24 * 60 * 60 * 1000);
88
+ const cutoffTime = Date.now() - (retentionDays * 24 * 60 * 60 * 1000);
80
89
  const cutoffDateKey = this._getDateKey(cutoffTime);
81
90
 
82
91
  for (const file of files) {
@@ -85,7 +94,7 @@ class HistoryManager {
85
94
  if (fileDateKey < cutoffDateKey) {
86
95
  try {
87
96
  fs.unlinkSync(path.join(HISTORY_DIR, file));
88
- console.log(`[history] Pruned old history file: ${file}`);
97
+ console.log(`[history] Pruned old history file older than ${retentionDays} days: ${file}`);
89
98
  } catch (e) {}
90
99
  }
91
100
  }
@@ -94,8 +103,8 @@ class HistoryManager {
94
103
  }
95
104
  }
96
105
 
97
- // Retrieve historical data across the last N hours (up to 72 hours / 3 days)
98
- getHistory(hours = 72) {
106
+ // Retrieve historical data across the last N hours (e.g. 1h, 24h, 72h, 360h / 15 days, or custom)
107
+ getHistory(hours = 360) {
99
108
  const minTimestamp = Date.now() - (hours * 60 * 60 * 1000);
100
109
  const records = [];
101
110
 
@@ -122,9 +131,9 @@ class HistoryManager {
122
131
  }
123
132
  }
124
133
 
125
- // If more than 300 points requested, downsample evenly to keep UI charts silky smooth
126
- if (records.length > 300) {
127
- const step = Math.ceil(records.length / 300);
134
+ // If more than 500 points requested, downsample evenly while preserving spikes
135
+ if (records.length > 500) {
136
+ const step = Math.ceil(records.length / 500);
128
137
  const sampled = [];
129
138
  for (let i = 0; i < records.length; i += step) {
130
139
  sampled.push(records[i]);
package/lib/metrics.js CHANGED
@@ -31,6 +31,17 @@ class MetricsPoller {
31
31
  console.log('[metrics] Poller stopped');
32
32
  }
33
33
 
34
+ setInterval(ms) {
35
+ const newInterval = Math.max(500, parseInt(ms, 10) || DEFAULT_POLL_INTERVAL);
36
+ if (this.interval === newInterval) return;
37
+ this.interval = newInterval;
38
+ if (this._timer) {
39
+ this.stop();
40
+ this.start();
41
+ }
42
+ console.log(`[metrics] Poller interval dynamically updated to: ${this.interval}ms`);
43
+ }
44
+
34
45
  pollNow() {
35
46
  return this._poll();
36
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinknagent",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "ThinkNCollab server agent & MCP server — metrics, logs, alerts, shell bridge, AI planning",
5
5
  "main": "lib/agent.js",
6
6
  "author": "ThinkNCollab Team <raman@thinkncollab.com>",