thinknagent 0.1.31 → 0.1.33
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/lib/metrics.js +5 -14
- package/lib/shell.js +82 -13
- package/package.json +1 -1
package/lib/metrics.js
CHANGED
|
@@ -236,22 +236,13 @@ class MetricsPoller {
|
|
|
236
236
|
// pehle alert engine aur history ko raw metrics pass karo — 24/7 Edge evaluation
|
|
237
237
|
this.onMetricsEmit?.(payload);
|
|
238
238
|
|
|
239
|
-
// MITM / Zero-Knowledge Defense: Encrypt metrics before sending over socket
|
|
240
239
|
const store = require('./store');
|
|
241
|
-
const
|
|
242
|
-
const roomId = this.conn?.roomId || store.get('roomId');
|
|
240
|
+
const agentId = this.conn?.agentId || store.get('agentId');
|
|
243
241
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
ts: payload.ts,
|
|
249
|
-
encrypted,
|
|
250
|
-
e2ee: true
|
|
251
|
-
});
|
|
252
|
-
} else {
|
|
253
|
-
this.conn.emit('agent:metrics', payload);
|
|
254
|
-
}
|
|
242
|
+
this.conn.emit('agent:metrics', {
|
|
243
|
+
agentId,
|
|
244
|
+
...payload
|
|
245
|
+
});
|
|
255
246
|
|
|
256
247
|
} catch (err) {
|
|
257
248
|
console.error('[metrics] Poll error:', err.message);
|
package/lib/shell.js
CHANGED
|
@@ -1,12 +1,34 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
let pty;
|
|
3
|
+
let pty = null;
|
|
4
4
|
try {
|
|
5
5
|
pty = require('node-pty');
|
|
6
6
|
} catch {
|
|
7
7
|
pty = null;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
let detectedPtyLauncher = null;
|
|
11
|
+
function getPtyLauncher() {
|
|
12
|
+
if (detectedPtyLauncher) return detectedPtyLauncher;
|
|
13
|
+
if (pty) {
|
|
14
|
+
detectedPtyLauncher = 'node-pty';
|
|
15
|
+
return 'node-pty';
|
|
16
|
+
}
|
|
17
|
+
const { execSync } = require('child_process');
|
|
18
|
+
try {
|
|
19
|
+
execSync('python3 -c "import pty"', { stdio: 'ignore' });
|
|
20
|
+
detectedPtyLauncher = 'python3';
|
|
21
|
+
return 'python3';
|
|
22
|
+
} catch {}
|
|
23
|
+
try {
|
|
24
|
+
execSync('python -c "import pty"', { stdio: 'ignore' });
|
|
25
|
+
detectedPtyLauncher = 'python';
|
|
26
|
+
return 'python';
|
|
27
|
+
} catch {}
|
|
28
|
+
detectedPtyLauncher = 'spawn';
|
|
29
|
+
return 'spawn';
|
|
30
|
+
}
|
|
31
|
+
|
|
10
32
|
// explicit allowlist — agent process ke secrets PTY mein nahi jayenge
|
|
11
33
|
const PTY_ENV_ALLOWLIST = new Set([
|
|
12
34
|
'PATH', 'HOME', 'SHELL', 'TERM', 'LANG', 'LC_ALL', 'LC_CTYPE',
|
|
@@ -61,13 +83,19 @@ class ShellBridge {
|
|
|
61
83
|
this._sessions.delete(id);
|
|
62
84
|
}
|
|
63
85
|
|
|
64
|
-
|
|
65
|
-
|
|
86
|
+
const launcher = getPtyLauncher();
|
|
87
|
+
const shellPath = process.env.SHELL || '/bin/bash';
|
|
88
|
+
const homeDir = process.env.HOME || '/';
|
|
89
|
+
const safeEnv = buildSafeEnv();
|
|
90
|
+
|
|
91
|
+
if (launcher === 'node-pty' && pty) {
|
|
92
|
+
// Tier 1: node-pty native module
|
|
93
|
+
const proc = pty.spawn(shellPath, [], {
|
|
66
94
|
name: 'xterm-256color',
|
|
67
95
|
cols,
|
|
68
96
|
rows,
|
|
69
|
-
cwd:
|
|
70
|
-
env:
|
|
97
|
+
cwd: homeDir,
|
|
98
|
+
env: safeEnv,
|
|
71
99
|
});
|
|
72
100
|
|
|
73
101
|
proc.onData(data => s.emit('shell:data', { sessionId, data }));
|
|
@@ -79,18 +107,57 @@ class ShellBridge {
|
|
|
79
107
|
|
|
80
108
|
this._sessions.set(sessionId, proc);
|
|
81
109
|
s.emit('shell:opened', { sessionId });
|
|
82
|
-
console.log(`[shell] PTY session ${sessionId} opened (${cols}x${rows})`);
|
|
110
|
+
console.log(`[shell] PTY session ${sessionId} opened via node-pty (${cols}x${rows})`);
|
|
111
|
+
} else if (launcher === 'python3' || launcher === 'python') {
|
|
112
|
+
// Tier 2: Native OS pseudo-terminal via standard library pty.spawn
|
|
113
|
+
// Eliminates 'Inappropriate ioctl for device' & 'no job control' completely
|
|
114
|
+
const { spawn } = require('child_process');
|
|
115
|
+
const pyCode = 'import pty, os; os.environ["TERM"]="xterm-256color"; pty.spawn([os.environ.get("SHELL", "/bin/bash")])';
|
|
116
|
+
const proc = spawn(launcher, ['-c', pyCode], {
|
|
117
|
+
cwd: homeDir,
|
|
118
|
+
env: safeEnv,
|
|
119
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
proc.stdout.on('data', data => s.emit('shell:data', { sessionId, data: data.toString() }));
|
|
123
|
+
proc.stderr.on('data', data => {
|
|
124
|
+
const clean = data.toString()
|
|
125
|
+
.replace(/^bash: cannot set terminal process group.*?\n/gm, '')
|
|
126
|
+
.replace(/^bash: no job control in this shell.*?\n/gm, '');
|
|
127
|
+
if (clean) s.emit('shell:data', { sessionId, data: clean });
|
|
128
|
+
});
|
|
129
|
+
proc.on('exit', exitCode => {
|
|
130
|
+
s.emit('shell:exit', { sessionId, exitCode: exitCode || 0 });
|
|
131
|
+
this._sessions.delete(sessionId);
|
|
132
|
+
console.log(`[shell] Python PTY session ${sessionId} exited (code ${exitCode})`);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
this._sessions.set(sessionId, {
|
|
136
|
+
write: data => proc.stdin.write(data),
|
|
137
|
+
resize: () => {},
|
|
138
|
+
kill: () => {
|
|
139
|
+
try { proc.kill('SIGTERM'); } catch {}
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
s.emit('shell:opened', { sessionId });
|
|
143
|
+
console.log(`[shell] Genuine OS PTY session ${sessionId} opened via ${launcher}`);
|
|
83
144
|
} else {
|
|
84
|
-
//
|
|
145
|
+
// Tier 3: Pure ChildProcess fallback with suppressed ioctl noise
|
|
85
146
|
const { spawn } = require('child_process');
|
|
86
|
-
const proc = spawn(
|
|
87
|
-
cwd:
|
|
88
|
-
env:
|
|
147
|
+
const proc = spawn(shellPath, ['--login'], {
|
|
148
|
+
cwd: homeDir,
|
|
149
|
+
env: safeEnv,
|
|
89
150
|
stdio: ['pipe', 'pipe', 'pipe']
|
|
90
151
|
});
|
|
91
152
|
|
|
92
153
|
proc.stdout.on('data', data => s.emit('shell:data', { sessionId, data: data.toString() }));
|
|
93
|
-
proc.stderr.on('data', data =>
|
|
154
|
+
proc.stderr.on('data', data => {
|
|
155
|
+
// Filter out terminal ioctl startup noise on non-tty pipes
|
|
156
|
+
const clean = data.toString()
|
|
157
|
+
.replace(/^bash: cannot set terminal process group.*?\n/gm, '')
|
|
158
|
+
.replace(/^bash: no job control in this shell.*?\n/gm, '');
|
|
159
|
+
if (clean) s.emit('shell:data', { sessionId, data: clean });
|
|
160
|
+
});
|
|
94
161
|
proc.on('exit', exitCode => {
|
|
95
162
|
s.emit('shell:exit', { sessionId, exitCode: exitCode || 0 });
|
|
96
163
|
this._sessions.delete(sessionId);
|
|
@@ -99,10 +166,12 @@ class ShellBridge {
|
|
|
99
166
|
this._sessions.set(sessionId, {
|
|
100
167
|
write: data => proc.stdin.write(data),
|
|
101
168
|
resize: () => {},
|
|
102
|
-
kill: () =>
|
|
169
|
+
kill: () => {
|
|
170
|
+
try { proc.kill('SIGTERM'); } catch {}
|
|
171
|
+
}
|
|
103
172
|
});
|
|
104
173
|
s.emit('shell:opened', { sessionId });
|
|
105
|
-
console.log(`[shell]
|
|
174
|
+
console.log(`[shell] Fallback login shell session ${sessionId} opened`);
|
|
106
175
|
}
|
|
107
176
|
});
|
|
108
177
|
|
package/package.json
CHANGED