thinknagent 0.1.25 → 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.
- package/bin/thinknagent.js +25 -16
- package/lib/history.js +17 -9
- package/lib/metrics.js +11 -0
- package/package.json +1 -1
package/bin/thinknagent.js
CHANGED
|
@@ -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:
|
|
57
|
-
interval:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
80
|
-
console.log(` Name
|
|
81
|
-
console.log(` Room ID
|
|
82
|
-
console.log(`
|
|
83
|
-
console.log(`
|
|
84
|
-
console.log(`
|
|
85
|
-
console.log(`
|
|
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',
|
|
104
|
-
.option('--interval <ms>',
|
|
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
|
|
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)) {
|
|
@@ -73,11 +80,12 @@ class HistoryManager {
|
|
|
73
80
|
}
|
|
74
81
|
}
|
|
75
82
|
|
|
76
|
-
// Deletes any history files older than
|
|
83
|
+
// Deletes any history files older than retentionDays (default 15 days or custom)
|
|
77
84
|
_pruneOldFiles() {
|
|
78
85
|
try {
|
|
86
|
+
const retentionDays = this.getRetentionDays();
|
|
79
87
|
const files = fs.readdirSync(HISTORY_DIR);
|
|
80
|
-
const cutoffTime = Date.now() - (
|
|
88
|
+
const cutoffTime = Date.now() - (retentionDays * 24 * 60 * 60 * 1000);
|
|
81
89
|
const cutoffDateKey = this._getDateKey(cutoffTime);
|
|
82
90
|
|
|
83
91
|
for (const file of files) {
|
|
@@ -86,7 +94,7 @@ class HistoryManager {
|
|
|
86
94
|
if (fileDateKey < cutoffDateKey) {
|
|
87
95
|
try {
|
|
88
96
|
fs.unlinkSync(path.join(HISTORY_DIR, file));
|
|
89
|
-
console.log(`[history] Pruned old history file: ${file}`);
|
|
97
|
+
console.log(`[history] Pruned old history file older than ${retentionDays} days: ${file}`);
|
|
90
98
|
} catch (e) {}
|
|
91
99
|
}
|
|
92
100
|
}
|
|
@@ -95,8 +103,8 @@ class HistoryManager {
|
|
|
95
103
|
}
|
|
96
104
|
}
|
|
97
105
|
|
|
98
|
-
// Retrieve historical data across the last N hours (
|
|
99
|
-
getHistory(hours =
|
|
106
|
+
// Retrieve historical data across the last N hours (e.g. 1h, 24h, 72h, 360h / 15 days, or custom)
|
|
107
|
+
getHistory(hours = 360) {
|
|
100
108
|
const minTimestamp = Date.now() - (hours * 60 * 60 * 1000);
|
|
101
109
|
const records = [];
|
|
102
110
|
|
|
@@ -123,9 +131,9 @@ class HistoryManager {
|
|
|
123
131
|
}
|
|
124
132
|
}
|
|
125
133
|
|
|
126
|
-
// If more than
|
|
127
|
-
if (records.length >
|
|
128
|
-
const step = Math.ceil(records.length /
|
|
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);
|
|
129
137
|
const sampled = [];
|
|
130
138
|
for (let i = 0; i < records.length; i += step) {
|
|
131
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