thinknagent 0.1.12 → 0.1.14
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/Readme.md +24 -6
- package/bin/thinknagent.js +4 -0
- package/lib/agent.js +49 -0
- package/lib/connect.js +53 -12
- package/package.json +9 -4
package/Readme.md
CHANGED
|
@@ -45,6 +45,7 @@ chmod +x (which thinknagent)
|
|
|
45
45
|
| `--room <roomId>` | ✅ | Room ID to connect to |
|
|
46
46
|
| `--gpu` | ❌ | Enable GPU metrics (requires nvidia-smi) |
|
|
47
47
|
| `--logs <paths>` | ❌ | Comma-separated log file paths to stream |
|
|
48
|
+
| `--app-path <path>` | ❌ | Path to the deployed application folder (to track and display version) |
|
|
48
49
|
|
|
49
50
|
---
|
|
50
51
|
|
|
@@ -78,14 +79,15 @@ thinknagent init \
|
|
|
78
79
|
--logs <YOUR_PM2_LOG_FILE>
|
|
79
80
|
```
|
|
80
81
|
|
|
81
|
-
**With GPU metrics:**
|
|
82
|
+
**With GPU metrics & application version tracking:**
|
|
82
83
|
```bash
|
|
83
84
|
thinknagent init \
|
|
84
85
|
--server YOUR_APP_SERVER \
|
|
85
86
|
--name my-server \
|
|
86
87
|
--room <roomId> \
|
|
87
88
|
--gpu \
|
|
88
|
-
--logs <YOUR_SYS_LOG_FILE>
|
|
89
|
+
--logs <YOUR_SYS_LOG_FILE> \
|
|
90
|
+
--app-path /var/www/my-node-app
|
|
89
91
|
```
|
|
90
92
|
|
|
91
93
|
Get your `roomId` from the room URL:
|
|
@@ -98,24 +100,40 @@ thinknagent start
|
|
|
98
100
|
```
|
|
99
101
|
|
|
100
102
|
Output:
|
|
103
|
+
```
|
|
101
104
|
Starting thinknagent — my-server
|
|
102
105
|
Status: PENDING — waiting for Owner approval
|
|
103
106
|
[agent] Connecting to https://thinkncollab.com...
|
|
104
107
|
[thinknagent] Registered as <agentId> — waiting for Owner approval...
|
|
108
|
+
```
|
|
105
109
|
|
|
106
110
|
### Step 3 — Approve in Browser
|
|
107
111
|
|
|
108
|
-
Go to your room's DevOps Wall:
|
|
109
|
-
https://thinkncollab.com/devops/<roomId>/devops
|
|
110
|
-
|
|
111
|
-
Click **Approve** on the pending agent in the sidebar.
|
|
112
|
+
1. Go to your room's DevOps Wall:
|
|
113
|
+
`https://thinkncollab.com/devops/<roomId>/devops`
|
|
114
|
+
2. Click **Approve** on the pending agent in the sidebar.
|
|
112
115
|
|
|
113
116
|
Once approved:
|
|
117
|
+
```
|
|
114
118
|
[thinknagent] Approved! Role: monitor | Room: <roomId>
|
|
115
119
|
[agent] Active. Role: monitor | Room: <roomId>
|
|
116
120
|
[metrics] Poller started
|
|
117
121
|
[logs] Watching 2 file(s)
|
|
118
122
|
[shell] Bridge ready
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Step 4 — Run with PM2 (Keep Alive)
|
|
126
|
+
|
|
127
|
+
To run the agent in the background so that all features (metrics, logs, shell) stay permanently active and accessible after you disconnect your terminal session:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# Start the agent using PM2
|
|
131
|
+
pm2 start thinknagent --name "thinknagent_logs" -- start
|
|
132
|
+
|
|
133
|
+
# Save PM2 process list and configure startup
|
|
134
|
+
pm2 save
|
|
135
|
+
pm2 startup
|
|
136
|
+
```
|
|
119
137
|
|
|
120
138
|
---
|
|
121
139
|
|
package/bin/thinknagent.js
CHANGED
|
@@ -24,6 +24,7 @@ program
|
|
|
24
24
|
.option('--room <roomId>', 'Room ID to connect to')
|
|
25
25
|
.option('--gpu', 'Enable GPU metrics (requires nvidia-smi)')
|
|
26
26
|
.option('--logs <paths>', 'Comma-separated log file paths to stream')
|
|
27
|
+
.option('--app-path <path>', 'Path to the deployed application folder (to track version)')
|
|
27
28
|
.action(async (opts) => {
|
|
28
29
|
const existing = store.get('agentToken');
|
|
29
30
|
if (existing) {
|
|
@@ -40,6 +41,7 @@ program
|
|
|
40
41
|
gpu: !!opts.gpu,
|
|
41
42
|
logs: opts.logs ? opts.logs.split(',').map(s => s.trim()) : [],
|
|
42
43
|
roomId: opts.room,
|
|
44
|
+
appPath: opts.appPath || null,
|
|
43
45
|
alerts: [
|
|
44
46
|
// sensible defaults — Owner can edit in browser
|
|
45
47
|
{ id: 'cpu-high', metric: 'cpu.usage', op: 'gt', value: 85, for: 60, severity: 'warning' },
|
|
@@ -58,6 +60,7 @@ program
|
|
|
58
60
|
console.log(` Agent ID: ${chalk.white(agentId)}`);
|
|
59
61
|
console.log(` GPU : ${cfg.gpu ? chalk.green('enabled') : chalk.gray('disabled')}`);
|
|
60
62
|
console.log(` Logs : ${cfg.logs.length ? chalk.white(cfg.logs.join(', ')) : chalk.gray('none')}`);
|
|
63
|
+
console.log(` App Path: ${cfg.appPath ? chalk.white(cfg.appPath) : chalk.gray('none')}`);
|
|
61
64
|
console.log(chalk.gray(' ─────────────────────────────────────────'));
|
|
62
65
|
console.log(chalk.yellow('\n Next step:'));
|
|
63
66
|
console.log(' 1. Run: ' + chalk.cyan('thinknagent start'));
|
|
@@ -109,6 +112,7 @@ program
|
|
|
109
112
|
console.log(` Status : ${cfg.agentToken ? chalk.green('APPROVED') : chalk.yellow('PENDING')}`);
|
|
110
113
|
console.log(` GPU : ${cfg.gpu ? chalk.green('enabled') : chalk.gray('disabled')}`);
|
|
111
114
|
console.log(` Logs : ${(cfg.logs||[]).length ? cfg.logs.join(', ') : chalk.gray('none')}`);
|
|
115
|
+
console.log(` App Path: ${cfg.appPath ? chalk.white(cfg.appPath) : chalk.gray('none')}`);
|
|
112
116
|
console.log(` Alerts : ${(cfg.alerts||[]).length} rule(s)`);
|
|
113
117
|
console.log(chalk.gray(' ────────────────────────────────\n'));
|
|
114
118
|
});
|
package/lib/agent.js
CHANGED
|
@@ -7,6 +7,8 @@ const LogWatcher = require('./logwatcher');
|
|
|
7
7
|
const AlertEngine = require('./alerts');
|
|
8
8
|
const ShellBridge = require('./shell');
|
|
9
9
|
const store = require('./store');
|
|
10
|
+
const chokidar = require('chokidar');
|
|
11
|
+
const fs = require('fs');
|
|
10
12
|
|
|
11
13
|
// allowed base dirs for log streaming
|
|
12
14
|
// room owner agent:logs_updated se bahar ke paths reject ho jayenge
|
|
@@ -121,10 +123,52 @@ class Agent {
|
|
|
121
123
|
if (this._active) this.logs.start();
|
|
122
124
|
});
|
|
123
125
|
|
|
126
|
+
// Start watching the application path for git pulls or package.json updates
|
|
127
|
+
this._startAppWatcher(cfg);
|
|
128
|
+
|
|
124
129
|
process.on('SIGTERM', () => this._shutdown('SIGTERM'));
|
|
125
130
|
process.on('SIGINT', () => this._shutdown('SIGINT'));
|
|
126
131
|
}
|
|
127
132
|
|
|
133
|
+
_startAppWatcher(cfg) {
|
|
134
|
+
if (!cfg.appPath) return;
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const resolvedPath = path.resolve(cfg.appPath);
|
|
138
|
+
const gitHeadPath = path.join(resolvedPath, '.git', 'HEAD');
|
|
139
|
+
const gitIndex = path.join(resolvedPath, '.git', 'index');
|
|
140
|
+
const pkgJsonPath = path.join(resolvedPath, 'package.json');
|
|
141
|
+
|
|
142
|
+
const filesToWatch = [];
|
|
143
|
+
if (fs.existsSync(gitHeadPath)) filesToWatch.push(gitHeadPath);
|
|
144
|
+
if (fs.existsSync(gitIndex)) filesToWatch.push(gitIndex);
|
|
145
|
+
if (fs.existsSync(pkgJsonPath)) filesToWatch.push(pkgJsonPath);
|
|
146
|
+
|
|
147
|
+
if (filesToWatch.length === 0) return;
|
|
148
|
+
|
|
149
|
+
console.log(`[agent] Watching application path for version changes: ${resolvedPath}`);
|
|
150
|
+
const watcher = chokidar.watch(filesToWatch, {
|
|
151
|
+
persistent: true,
|
|
152
|
+
ignoreInitial: true,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
let debounceTimeout = null;
|
|
156
|
+
watcher.on('change', (filePath) => {
|
|
157
|
+
console.log(`[agent] Detected app file change: ${filePath}. Recalculating version...`);
|
|
158
|
+
if (debounceTimeout) clearTimeout(debounceTimeout);
|
|
159
|
+
debounceTimeout = setTimeout(() => {
|
|
160
|
+
const newVersion = Connection.getDeployedAppVersion(cfg.appPath);
|
|
161
|
+
console.log(`[agent] Reporting updated app version: ${newVersion}`);
|
|
162
|
+
this.conn.emit('agent:meta_update', { appVersion: newVersion });
|
|
163
|
+
}, 2000);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
this._appWatcher = watcher;
|
|
167
|
+
} catch (err) {
|
|
168
|
+
console.warn(`[agent] Failed to initialize application path watcher: ${err.message}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
128
172
|
_onReady(role, roomId) {
|
|
129
173
|
if (this._active) {
|
|
130
174
|
console.log(`[agent] Reconnected. Role: ${role}`);
|
|
@@ -152,6 +196,11 @@ class Agent {
|
|
|
152
196
|
this.metrics.stop();
|
|
153
197
|
this.logs.stop();
|
|
154
198
|
this.shell.killAll();
|
|
199
|
+
if (this._appWatcher) {
|
|
200
|
+
try {
|
|
201
|
+
this._appWatcher.close();
|
|
202
|
+
} catch (e) {}
|
|
203
|
+
}
|
|
155
204
|
this.conn.socket?.disconnect();
|
|
156
205
|
process.exit(0);
|
|
157
206
|
}
|
package/lib/connect.js
CHANGED
|
@@ -2,6 +2,45 @@
|
|
|
2
2
|
|
|
3
3
|
const { io } = require('socket.io-client');
|
|
4
4
|
const store = require('./store');
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
function getDeployedAppVersion(appPath) {
|
|
10
|
+
try {
|
|
11
|
+
const resolvedPath = path.resolve(appPath || process.cwd());
|
|
12
|
+
let pkgVersion = '';
|
|
13
|
+
let gitCommit = '';
|
|
14
|
+
|
|
15
|
+
const pkgJsonPath = path.join(resolvedPath, 'package.json');
|
|
16
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
17
|
+
try {
|
|
18
|
+
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
|
|
19
|
+
pkgVersion = pkg.version || '';
|
|
20
|
+
} catch (e) {}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
gitCommit = execSync('git log -n 1 --format="%h - %s" --no-color', {
|
|
25
|
+
cwd: resolvedPath,
|
|
26
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
27
|
+
timeout: 2000,
|
|
28
|
+
encoding: 'utf8'
|
|
29
|
+
}).trim();
|
|
30
|
+
} catch (e) {}
|
|
31
|
+
|
|
32
|
+
if (pkgVersion && gitCommit) {
|
|
33
|
+
return `v${pkgVersion} (${gitCommit})`;
|
|
34
|
+
} else if (pkgVersion) {
|
|
35
|
+
return `v${pkgVersion}`;
|
|
36
|
+
} else if (gitCommit) {
|
|
37
|
+
return gitCommit;
|
|
38
|
+
}
|
|
39
|
+
return 'unknown';
|
|
40
|
+
} catch (err) {
|
|
41
|
+
return 'unknown';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
5
44
|
|
|
6
45
|
const NAMESPACE = '/devops';
|
|
7
46
|
const RECONNECT_DELAY = 5000;
|
|
@@ -34,18 +73,19 @@ class Connection {
|
|
|
34
73
|
reconnection: true,
|
|
35
74
|
reconnectionDelay: RECONNECT_DELAY,
|
|
36
75
|
reconnectionAttempts: Infinity,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
76
|
+
auth: (cb) => {
|
|
77
|
+
const freshCfg = store.read();
|
|
78
|
+
console.log('[debug] Reconnecting with token:', !!freshCfg.agentToken, 'agentId:', freshCfg.agentId);
|
|
79
|
+
cb({
|
|
80
|
+
agentId: freshCfg.agentId,
|
|
81
|
+
agentToken: freshCfg.agentToken || null,
|
|
82
|
+
name: freshCfg.name,
|
|
83
|
+
hostname: require('os').hostname(),
|
|
84
|
+
version: require('../package.json').version,
|
|
85
|
+
roomId: freshCfg.roomId || null,
|
|
86
|
+
appVersion: getDeployedAppVersion(freshCfg.appPath),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
49
89
|
});
|
|
50
90
|
|
|
51
91
|
this._bind();
|
|
@@ -133,4 +173,5 @@ s.on('agent:approved', ({ agentToken, role, roomId }) => {
|
|
|
133
173
|
}
|
|
134
174
|
}
|
|
135
175
|
|
|
176
|
+
Connection.getDeployedAppVersion = getDeployedAppVersion;
|
|
136
177
|
module.exports = Connection;
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thinknagent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "ThinkNCollab server agent — metrics, logs, alerts, shell bridge",
|
|
5
5
|
"main": "lib/agent.js",
|
|
6
|
-
"author": "ThinkNCollab Team <
|
|
6
|
+
"author": "ThinkNCollab Team <raman@thinkncollab.com>",
|
|
7
7
|
"bin": {
|
|
8
|
-
"thinknagent": "
|
|
8
|
+
"thinknagent": "bin/thinknagent.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"start": "node bin/thinknagent.js start",
|
|
@@ -25,6 +25,11 @@
|
|
|
25
25
|
"engines": {
|
|
26
26
|
"node": ">=18.0.0"
|
|
27
27
|
},
|
|
28
|
-
"keywords": [
|
|
28
|
+
"keywords": [
|
|
29
|
+
"thinkncollab",
|
|
30
|
+
"devops",
|
|
31
|
+
"monitoring",
|
|
32
|
+
"agent"
|
|
33
|
+
],
|
|
29
34
|
"license": "MIT"
|
|
30
35
|
}
|