thinknagent 0.1.19 → 0.1.22
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 +87 -279
- package/bin/thinknagent.js +26 -18
- package/bin/thinkncollab-mcp.js +387 -0
- package/lib/agent.js +24 -6
- package/lib/daemon.js +5 -4
- package/lib/e2ee.js +45 -0
- package/lib/logwatcher.js +46 -29
- package/lib/metrics.js +15 -4
- package/lib/shell.js +14 -0
- package/package.json +4 -3
- package/install/setup.sh +0 -90
- package/lib/app.js +0 -327
- package/thinknagent.sh +0 -560
package/thinknagent.sh
DELETED
|
@@ -1,560 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# ==============================================================================
|
|
3
|
-
# ThinkNCollab Standalone Server Agent (thinknagent.sh)
|
|
4
|
-
# Zero-dependency universal bash agent for Linux and macOS.
|
|
5
|
-
# ==============================================================================
|
|
6
|
-
|
|
7
|
-
set -eo pipefail
|
|
8
|
-
|
|
9
|
-
if [[ -n "$SUDO_USER" ]]; then
|
|
10
|
-
USER_HOME=$(getent passwd "$SUDO_USER" 2>/dev/null | cut -d: -f6 || eval echo "~$SUDO_USER")
|
|
11
|
-
if [[ -f "${USER_HOME}/.thinknagent/config.json" ]]; then
|
|
12
|
-
CONFIG_DIR="${USER_HOME}/.thinknagent"
|
|
13
|
-
else
|
|
14
|
-
CONFIG_DIR="${HOME}/.thinknagent"
|
|
15
|
-
fi
|
|
16
|
-
else
|
|
17
|
-
CONFIG_DIR="${HOME}/.thinknagent"
|
|
18
|
-
fi
|
|
19
|
-
|
|
20
|
-
CONFIG_FILE="${CONFIG_DIR}/config.json"
|
|
21
|
-
PID_FILE="${CONFIG_DIR}/agent.pid"
|
|
22
|
-
|
|
23
|
-
mkdir -p "$CONFIG_DIR" 2>/dev/null || true
|
|
24
|
-
|
|
25
|
-
# ── Colors & Helpers ──────────────────────────────────────────────────────────
|
|
26
|
-
RED='\033[0;31m'
|
|
27
|
-
GREEN='\033[0;32m'
|
|
28
|
-
YELLOW='\033[1;33m'
|
|
29
|
-
BLUE='\033[0;34m'
|
|
30
|
-
NC='\033[0m'
|
|
31
|
-
|
|
32
|
-
log_info() { echo -e "${BLUE}[thinknagent]${NC} $1"; }
|
|
33
|
-
log_ok() { echo -e "${GREEN}[thinknagent]${NC} $1"; }
|
|
34
|
-
log_warn() { echo -e "${YELLOW}[thinknagent]${NC} $1"; }
|
|
35
|
-
log_err() { echo -e "${RED}[thinknagent] ERROR:${NC} $1"; }
|
|
36
|
-
|
|
37
|
-
get_json_val() {
|
|
38
|
-
local key="$1"
|
|
39
|
-
if [[ -f "$CONFIG_FILE" ]]; then
|
|
40
|
-
grep -o "\"$key\": *\"[^\"]*\"" "$CONFIG_FILE" | head -n1 | cut -d'"' -f4 || echo ""
|
|
41
|
-
else
|
|
42
|
-
echo ""
|
|
43
|
-
fi
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
save_config() {
|
|
47
|
-
local server="$1"
|
|
48
|
-
local name="$2"
|
|
49
|
-
local room="$3"
|
|
50
|
-
local agentId="$4"
|
|
51
|
-
local token="$5"
|
|
52
|
-
local status="$6"
|
|
53
|
-
|
|
54
|
-
cat > "$CONFIG_FILE" << EOF
|
|
55
|
-
{
|
|
56
|
-
"serverUrl": "$server",
|
|
57
|
-
"name": "$name",
|
|
58
|
-
"roomId": "$room",
|
|
59
|
-
"agentId": "$agentId",
|
|
60
|
-
"agentToken": "$token",
|
|
61
|
-
"status": "$status",
|
|
62
|
-
"updatedAt": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
63
|
-
}
|
|
64
|
-
EOF
|
|
65
|
-
chmod 600 "$CONFIG_FILE"
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
# ── Metric Collectors (Native Linux / macOS) ──────────────────────────────────
|
|
69
|
-
get_cpu_usage() {
|
|
70
|
-
if [[ -f /proc/stat ]]; then
|
|
71
|
-
read -r cpu user nice system idle iowait irq softirq steal guest guest_nice < /proc/stat
|
|
72
|
-
local prev_idle=$idle
|
|
73
|
-
local prev_total=$((user + nice + system + idle + iowait + irq + softirq + steal))
|
|
74
|
-
sleep 0.5
|
|
75
|
-
read -r cpu user nice system idle iowait irq softirq steal guest guest_nice < /proc/stat
|
|
76
|
-
local idle_diff=$((idle - prev_idle))
|
|
77
|
-
local total_diff=$(((user + nice + system + idle + iowait + irq + softirq + steal) - prev_total))
|
|
78
|
-
if [[ $total_diff -gt 0 ]]; then
|
|
79
|
-
awk "BEGIN {printf \"%.1f\", (1 - ($idle_diff / $total_diff)) * 100}"
|
|
80
|
-
else
|
|
81
|
-
echo "0.0"
|
|
82
|
-
fi
|
|
83
|
-
else
|
|
84
|
-
ps -A -o %cpu | awk '{s+=$1} END {printf "%.1f", s}'
|
|
85
|
-
fi
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
get_mem_info() {
|
|
89
|
-
if [[ -f /proc/meminfo ]]; then
|
|
90
|
-
local total=$(grep MemTotal /proc/meminfo | awk '{print $2}')
|
|
91
|
-
local free=$(grep MemAvailable /proc/meminfo | awk '{print $2}')
|
|
92
|
-
local used=$((total - free))
|
|
93
|
-
local pct=$(awk "BEGIN {printf \"%.1f\", ($used / $total) * 100}")
|
|
94
|
-
local total_bytes=$((total * 1024))
|
|
95
|
-
local used_bytes=$((used * 1024))
|
|
96
|
-
local free_bytes=$((free * 1024))
|
|
97
|
-
echo "{\"total\": $total_bytes, \"used\": $used_bytes, \"free\": $free_bytes, \"usedPct\": $pct}"
|
|
98
|
-
else
|
|
99
|
-
echo "{\"total\": 17179869184, \"used\": 8589934592, \"free\": 8589934592, \"usedPct\": 50.0}"
|
|
100
|
-
fi
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
get_cores() {
|
|
104
|
-
if [[ -f /proc/cpuinfo ]]; then
|
|
105
|
-
grep -c ^processor /proc/cpuinfo
|
|
106
|
-
else
|
|
107
|
-
sysctl -n hw.ncpu 2>/dev/null || echo "2"
|
|
108
|
-
fi
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
get_load_avg() {
|
|
112
|
-
if [[ -f /proc/loadavg ]]; then
|
|
113
|
-
awk '{print $1}' /proc/loadavg
|
|
114
|
-
else
|
|
115
|
-
uptime | awk -F'load averages?: ' '{print $2}' | awk '{print $1}' | tr -d ',' || echo "0.0"
|
|
116
|
-
fi
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
get_top_procs() {
|
|
120
|
-
local procs_json="[]"
|
|
121
|
-
if command -v ps >/dev/null 2>&1; then
|
|
122
|
-
local list=$(ps -eo pid,pcpu,pmem,comm --sort=-pcpu 2>/dev/null | head -n 6 | tail -n 5 || ps -eo pid,%cpu,%mem,comm | head -n 6 | tail -n 5)
|
|
123
|
-
local items=()
|
|
124
|
-
while read -r pid cpu mem comm; do
|
|
125
|
-
if [[ -n "$pid" && "$pid" != "PID" ]]; then
|
|
126
|
-
items+=("{\"pid\": $pid, \"name\": \"$comm\", \"cpu\": $cpu, \"mem\": $mem}")
|
|
127
|
-
fi
|
|
128
|
-
done <<< "$list"
|
|
129
|
-
local IFS=","
|
|
130
|
-
procs_json="[${items[*]}]"
|
|
131
|
-
fi
|
|
132
|
-
echo "$procs_json"
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
get_disks() {
|
|
136
|
-
local disk_items=()
|
|
137
|
-
while read -r fs size used avail pct mount; do
|
|
138
|
-
if [[ "$fs" != "Filesystem" && -n "$size" && "$size" =~ ^[0-9]+$ ]]; then
|
|
139
|
-
local size_b=$((size * 1024))
|
|
140
|
-
local used_b=$((used * 1024))
|
|
141
|
-
local pct_num=$(echo "$pct" | tr -d '%')
|
|
142
|
-
disk_items+=("{\"fs\": \"$fs\", \"mount\": \"$mount\", \"size\": $size_b, \"used\": $used_b, \"usedPct\": $pct_num}")
|
|
143
|
-
fi
|
|
144
|
-
done < <(df -k -P 2>/dev/null | tail -n +2)
|
|
145
|
-
local IFS=","
|
|
146
|
-
echo "[${disk_items[*]}]"
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
# ── Commands ──────────────────────────────────────────────────────────────────
|
|
150
|
-
|
|
151
|
-
cmd_init() {
|
|
152
|
-
local server=""
|
|
153
|
-
local name="$(hostname)"
|
|
154
|
-
local room=""
|
|
155
|
-
|
|
156
|
-
while [[ $# -gt 0 ]]; do
|
|
157
|
-
case $1 in
|
|
158
|
-
--server) server="$2"; shift 2 ;;
|
|
159
|
-
--name) name="$2"; shift 2 ;;
|
|
160
|
-
--room) room="$2"; shift 2 ;;
|
|
161
|
-
*) shift ;;
|
|
162
|
-
esac
|
|
163
|
-
done
|
|
164
|
-
|
|
165
|
-
if [[ -z "$server" || -z "$room" ]]; then
|
|
166
|
-
log_err "Missing arguments. Usage: thinknagent.sh init --server <url> --name <name> --room <roomId>"
|
|
167
|
-
exit 1
|
|
168
|
-
fi
|
|
169
|
-
|
|
170
|
-
local existing_id=$(get_json_val "agentId")
|
|
171
|
-
local agentId="${existing_id:-$(uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid 2>/dev/null || echo "agent-$RANDOM-$RANDOM")}"
|
|
172
|
-
|
|
173
|
-
log_info "Registering server with ThinkNCollab..."
|
|
174
|
-
log_info "Server : $server"
|
|
175
|
-
log_info "Node Name: $name"
|
|
176
|
-
log_info "Room ID : $room"
|
|
177
|
-
log_info "Agent ID : $agentId"
|
|
178
|
-
|
|
179
|
-
local payload="{\"agentId\":\"$agentId\",\"name\":\"$name\",\"hostname\":\"$(hostname)\",\"version\":\"1.0.0-bash\",\"roomId\":\"$room\"}"
|
|
180
|
-
|
|
181
|
-
local res=$(curl -s -X POST "${server}/devops/api/agent/register" \
|
|
182
|
-
-H "Content-Type: application/json" \
|
|
183
|
-
-d "$payload" || echo '{"error":"Connection failed"}')
|
|
184
|
-
|
|
185
|
-
local status=$(echo "$res" | grep -o '"status":"[^"]*"' | cut -d'"' -f4 || echo "pending")
|
|
186
|
-
save_config "$server" "$name" "$room" "$agentId" "" "$status"
|
|
187
|
-
|
|
188
|
-
echo ""
|
|
189
|
-
log_ok "Registration submitted! Status: $(echo "$status" | tr '[:lower:]' '[:upper:]')"
|
|
190
|
-
log_info "Approve this server in your Room DevOps Wall: ${server}/devops/${room}"
|
|
191
|
-
echo ""
|
|
192
|
-
echo "Next step: Run './thinknagent.sh start' or install systemd service with './thinknagent.sh service'"
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
encrypt_e2ee() {
|
|
196
|
-
local plaintext="$1"
|
|
197
|
-
local pass="$2"
|
|
198
|
-
if command -v openssl >/dev/null 2>&1; then
|
|
199
|
-
local enc=$(echo -n "$plaintext" | openssl enc -aes-256-cbc -pbkdf2 -iter 10000 -k "$pass" -base64 -A 2>/dev/null)
|
|
200
|
-
if [[ -n "$enc" ]]; then
|
|
201
|
-
echo "e2ee:v1:${enc}"
|
|
202
|
-
return
|
|
203
|
-
fi
|
|
204
|
-
fi
|
|
205
|
-
echo "$plaintext"
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
get_logs_json() {
|
|
209
|
-
local raw_logs=$(get_json_val "logs")
|
|
210
|
-
local room_key=$(get_json_val "roomId")
|
|
211
|
-
if [[ -z "$raw_logs" ]]; then
|
|
212
|
-
echo "[]"
|
|
213
|
-
return
|
|
214
|
-
fi
|
|
215
|
-
local paths=()
|
|
216
|
-
for item in ${raw_logs//,/ }; do
|
|
217
|
-
paths+=("$item")
|
|
218
|
-
done
|
|
219
|
-
local log_items=()
|
|
220
|
-
for p in "${paths[@]}"; do
|
|
221
|
-
p="${p/#\~/$HOME}"
|
|
222
|
-
if [[ -f "$p" ]]; then
|
|
223
|
-
local lines_arr=()
|
|
224
|
-
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
225
|
-
local enc_line=$(encrypt_e2ee "$line" "$room_key")
|
|
226
|
-
local clean_line=$(echo "$enc_line" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/ /g' | tr -d '\r')
|
|
227
|
-
lines_arr+=("{\"ts\": $(date +%s%3N 2>/dev/null || echo "1724000000000"), \"text\": \"$clean_line\"}")
|
|
228
|
-
done < <(tail -n 25 "$p" 2>/dev/null)
|
|
229
|
-
local lines_str=$(IFS=,; echo "${lines_arr[*]}")
|
|
230
|
-
log_items+=("{\"file\": \"$p\", \"lines\": [$lines_str]}")
|
|
231
|
-
fi
|
|
232
|
-
done
|
|
233
|
-
local IFS=","
|
|
234
|
-
echo "[${log_items[*]}]"
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
cmd_start() {
|
|
238
|
-
local server=$(get_json_val "serverUrl")
|
|
239
|
-
local agentId=$(get_json_val "agentId")
|
|
240
|
-
local room=$(get_json_val "roomId")
|
|
241
|
-
local token=$(get_json_val "agentToken")
|
|
242
|
-
local name=$(get_json_val "name")
|
|
243
|
-
|
|
244
|
-
if [[ -z "$server" || -z "$agentId" ]]; then
|
|
245
|
-
log_err "Agent not initialized. Run './thinknagent.sh init --server <url> --name <name> --room <roomId>' first."
|
|
246
|
-
exit 1
|
|
247
|
-
fi
|
|
248
|
-
|
|
249
|
-
log_info "Starting thinknagent telemetry poller (PID: $$)..."
|
|
250
|
-
echo "$$" > "$PID_FILE"
|
|
251
|
-
|
|
252
|
-
trap 'rm -f "$PID_FILE"; log_info "Agent stopped."; exit 0' SIGINT SIGTERM
|
|
253
|
-
|
|
254
|
-
local cores=$(get_cores)
|
|
255
|
-
|
|
256
|
-
while true; do
|
|
257
|
-
if [[ -z "$token" ]]; then
|
|
258
|
-
local status_res=$(curl -s "${server}/devops/api/agent/status/${agentId}" 2>/dev/null || echo '{}')
|
|
259
|
-
local status=$(echo "$status_res" | grep -o '"status":"[^"]*"' | cut -d'"' -f4 || echo "")
|
|
260
|
-
local new_token=$(echo "$status_res" | grep -o '"token":"[^"]*"' | cut -d'"' -f4 || echo "")
|
|
261
|
-
|
|
262
|
-
if [[ "$status" == "approved" && -n "$new_token" ]]; then
|
|
263
|
-
token="$new_token"
|
|
264
|
-
save_config "$server" "$name" "$room" "$agentId" "$token" "approved"
|
|
265
|
-
log_ok "Agent approved by room owner! Token acquired."
|
|
266
|
-
else
|
|
267
|
-
log_warn "Agent is pending approval in room. Waiting..."
|
|
268
|
-
sleep 5
|
|
269
|
-
continue
|
|
270
|
-
fi
|
|
271
|
-
fi
|
|
272
|
-
|
|
273
|
-
local cpu=$(get_cpu_usage)
|
|
274
|
-
local load=$(get_load_avg)
|
|
275
|
-
local mem_json=$(get_mem_info)
|
|
276
|
-
local disk_json=$(get_disks)
|
|
277
|
-
local proc_json=$(get_top_procs)
|
|
278
|
-
local logs_json=$(get_logs_json)
|
|
279
|
-
|
|
280
|
-
local payload=$(cat << EOF
|
|
281
|
-
{
|
|
282
|
-
"agentId": "$agentId",
|
|
283
|
-
"token": "$token",
|
|
284
|
-
"cpu": { "usage": $cpu, "cores": $cores, "loadAvg": $load },
|
|
285
|
-
"memory": $mem_json,
|
|
286
|
-
"disk": $disk_json,
|
|
287
|
-
"processes": { "total": $(ps -e | wc -l 2>/dev/null || echo "100"), "running": 1, "top": $proc_json },
|
|
288
|
-
"logs": $logs_json,
|
|
289
|
-
"apm": {
|
|
290
|
-
"nodeName": "$(hostname)",
|
|
291
|
-
"p50": 1.8,
|
|
292
|
-
"p90": 4.5,
|
|
293
|
-
"p95": 8.2,
|
|
294
|
-
"p99": 14.0,
|
|
295
|
-
"errorRate": "0.0%",
|
|
296
|
-
"totalTraced": 8
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
EOF
|
|
300
|
-
)
|
|
301
|
-
|
|
302
|
-
curl -s -X POST "${server}/devops/api/agent/metrics" \
|
|
303
|
-
-H "Content-Type: application/json" \
|
|
304
|
-
-d "$payload" > /dev/null 2>&1 || true
|
|
305
|
-
|
|
306
|
-
# Sub-second fast poller for near-instant interactive terminal response
|
|
307
|
-
for _i in {1..6}; do
|
|
308
|
-
local cmd_res=$(curl -s "${server}/devops/api/agent/command/poll?agentId=${agentId}&token=${token}" 2>/dev/null || echo '{}')
|
|
309
|
-
local cmd=$(echo "$cmd_res" | grep -o '"cmd":"[^"]*"' | head -n1 | cut -d'"' -f4 || echo "")
|
|
310
|
-
local sid=$(echo "$cmd_res" | grep -o '"sessionId":"[^"]*"' | head -n1 | cut -d'"' -f4 || echo "")
|
|
311
|
-
if [[ -n "$cmd" ]]; then
|
|
312
|
-
# Strip any ANSI escape sequences from command
|
|
313
|
-
cmd=$(echo "$cmd" | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' | sed 's/u001b\[[0-9;]*[a-zA-Z]//g')
|
|
314
|
-
|
|
315
|
-
# Auto-adjust interactive blocking commands to crisp one-shot outputs
|
|
316
|
-
if [[ "$cmd" == "pm2 logs"* || "$cmd" == "pm2 log"* ]]; then
|
|
317
|
-
cmd="pm2 logs --lines 40 --nostream 2>&1 || tail -n 40 ~/.pm2/logs/*.log 2>&1"
|
|
318
|
-
elif [[ "$cmd" == "top" ]]; then
|
|
319
|
-
cmd="top -b -n 1 | head -n 35"
|
|
320
|
-
elif [[ "$cmd" == "htop" ]]; then
|
|
321
|
-
cmd="top -b -n 1 | head -n 35"
|
|
322
|
-
elif [[ "$cmd" == "ping "* && "$cmd" != *"-c "* ]]; then
|
|
323
|
-
cmd="$cmd -c 3"
|
|
324
|
-
fi
|
|
325
|
-
|
|
326
|
-
local cur_dir=$(cat "$CONFIG_DIR/cwd.state" 2>/dev/null || echo "$HOME")
|
|
327
|
-
[[ -d "$cur_dir" ]] || cur_dir="$HOME"
|
|
328
|
-
|
|
329
|
-
local temp_out=$(mktemp 2>/dev/null || echo "/tmp/tnc_cmd_out_$$")
|
|
330
|
-
local temp_pwd=$(mktemp 2>/dev/null || echo "/tmp/tnc_cmd_pwd_$$")
|
|
331
|
-
|
|
332
|
-
(
|
|
333
|
-
# Load comprehensive environment and standard user paths
|
|
334
|
-
export HOME="${HOME:-/home/ubuntu}"
|
|
335
|
-
export USER="${USER:-ubuntu}"
|
|
336
|
-
export PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:$HOME/.npm-global/bin:$HOME/.local/bin:$PATH"
|
|
337
|
-
if [[ -d "$HOME/.nvm" ]]; then
|
|
338
|
-
export NVM_DIR="$HOME/.nvm"
|
|
339
|
-
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" 2>/dev/null || true
|
|
340
|
-
fi
|
|
341
|
-
export GIT_TERMINAL_PROMPT=0
|
|
342
|
-
export CI=true
|
|
343
|
-
export TERM=xterm-256color
|
|
344
|
-
|
|
345
|
-
cd "$cur_dir" 2>/dev/null || cd "$HOME"
|
|
346
|
-
# Run command with 15s timeout to prevent zombie locks
|
|
347
|
-
if command -v timeout >/dev/null 2>&1; then
|
|
348
|
-
timeout 15s bash -c "$cmd" > "$temp_out" 2>&1 || true
|
|
349
|
-
else
|
|
350
|
-
eval "$cmd" > "$temp_out" 2>&1 || true
|
|
351
|
-
fi
|
|
352
|
-
pwd > "$temp_pwd"
|
|
353
|
-
)
|
|
354
|
-
|
|
355
|
-
local new_dir=$(cat "$temp_pwd" 2>/dev/null || echo "$cur_dir")
|
|
356
|
-
[[ -d "$new_dir" ]] && echo "$new_dir" > "$CONFIG_DIR/cwd.state"
|
|
357
|
-
local output=$(cat "$temp_out" 2>/dev/null || echo "")
|
|
358
|
-
rm -f "$temp_out" "$temp_pwd" 2>/dev/null || true
|
|
359
|
-
|
|
360
|
-
# Robust JSON serialization of terminal output
|
|
361
|
-
local clean_out
|
|
362
|
-
if command -v python3 >/dev/null 2>&1; then
|
|
363
|
-
clean_out=$(python3 -c "import sys, json; print(json.dumps(sys.stdin.read()))" <<< "$output" 2>/dev/null || echo '""')
|
|
364
|
-
else
|
|
365
|
-
local escaped=$(echo -n "$output" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/ /g' | awk '{printf "%s\\n", $0}' | sed 's/\\n$//')
|
|
366
|
-
clean_out="\"$escaped\""
|
|
367
|
-
fi
|
|
368
|
-
|
|
369
|
-
local result_payload="{\"agentId\":\"$agentId\",\"token\":\"$token\",\"sessionId\":\"$sid\",\"output\":$clean_out,\"cwd\":\"$new_dir\",\"exitCode\":0}"
|
|
370
|
-
curl -s -X POST "${server}/devops/api/agent/command/result" \
|
|
371
|
-
-H "Content-Type: application/json" \
|
|
372
|
-
-d "$result_payload" > /dev/null 2>&1 || true
|
|
373
|
-
fi
|
|
374
|
-
sleep 0.5
|
|
375
|
-
done
|
|
376
|
-
done
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
cmd_stop() {
|
|
380
|
-
if [[ -f "$PID_FILE" ]]; then
|
|
381
|
-
local pid=$(cat "$PID_FILE" 2>/dev/null)
|
|
382
|
-
if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
|
|
383
|
-
kill "$pid" 2>/dev/null || true
|
|
384
|
-
rm -f "$PID_FILE"
|
|
385
|
-
log_ok "thinknagent stopped (PID $pid)."
|
|
386
|
-
return
|
|
387
|
-
fi
|
|
388
|
-
fi
|
|
389
|
-
# Fallback to kill by process name
|
|
390
|
-
pkill -f "thinknagent" 2>/dev/null || true
|
|
391
|
-
rm -f "$PID_FILE"
|
|
392
|
-
log_ok "thinknagent stopped."
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
cmd_restart() {
|
|
396
|
-
log_info "Restarting thinknagent..."
|
|
397
|
-
cmd_stop
|
|
398
|
-
sleep 1
|
|
399
|
-
cmd_daemon
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
cmd_daemon() {
|
|
403
|
-
local server=$(get_json_val "serverUrl")
|
|
404
|
-
local agentId=$(get_json_val "agentId")
|
|
405
|
-
if [[ -z "$server" || -z "$agentId" ]]; then
|
|
406
|
-
log_err "Agent not initialized. Run './thinknagent.sh init --room <roomId>' first."
|
|
407
|
-
exit 1
|
|
408
|
-
fi
|
|
409
|
-
|
|
410
|
-
local script_path="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
|
|
411
|
-
local log_file="${CONFIG_DIR}/daemon.log"
|
|
412
|
-
|
|
413
|
-
if [[ -f "$PID_FILE" ]] && kill -0 "$(cat "$PID_FILE" 2>/dev/null)" 2>/dev/null; then
|
|
414
|
-
log_warn "thinknagent is already running (PID $(cat "$PID_FILE"))."
|
|
415
|
-
return
|
|
416
|
-
fi
|
|
417
|
-
|
|
418
|
-
nohup bash -c "
|
|
419
|
-
while true; do
|
|
420
|
-
bash \"$script_path\" start >> \"$log_file\" 2>&1
|
|
421
|
-
sleep 3
|
|
422
|
-
done
|
|
423
|
-
" > /dev/null 2>&1 &
|
|
424
|
-
|
|
425
|
-
local daemon_pid=$!
|
|
426
|
-
echo "$daemon_pid" > "$PID_FILE"
|
|
427
|
-
|
|
428
|
-
log_ok "thinknagent daemon started with auto-restart! (PID $daemon_pid)"
|
|
429
|
-
log_info "Logs: $log_file"
|
|
430
|
-
log_info "Check status: thinknagent status"
|
|
431
|
-
log_info "Stop daemon : thinknagent stop"
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
cmd_logs() {
|
|
435
|
-
local log_file="${CONFIG_DIR}/daemon.log"
|
|
436
|
-
if [[ -f "$log_file" ]]; then
|
|
437
|
-
echo ""
|
|
438
|
-
echo " --- thinknagent logs (last 50 lines) ---"
|
|
439
|
-
tail -n 50 "$log_file"
|
|
440
|
-
echo " --- end of logs ---"
|
|
441
|
-
echo ""
|
|
442
|
-
else
|
|
443
|
-
log_info "No logs found yet at $log_file"
|
|
444
|
-
fi
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
cmd_revoke() {
|
|
448
|
-
cmd_stop
|
|
449
|
-
rm -f "$CONFIG_FILE" "$PID_FILE"
|
|
450
|
-
log_ok "All credentials cleared from ${CONFIG_DIR}."
|
|
451
|
-
log_info "To re-register: thinknagent init --room <roomId>"
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
cmd_app() {
|
|
455
|
-
local port="${1:-4455}"
|
|
456
|
-
if command -v thinknagent >/dev/null 2>&1; then
|
|
457
|
-
thinknagent app -p "$port"
|
|
458
|
-
elif command -v npx >/dev/null 2>&1; then
|
|
459
|
-
npx thinknagent app -p "$port"
|
|
460
|
-
elif [[ -f "${HOME}/.thinknagent/node_modules/.bin/thinknagent" ]]; then
|
|
461
|
-
"${HOME}/.thinknagent/node_modules/.bin/thinknagent" app -p "$port"
|
|
462
|
-
elif [[ -f "$(dirname "${BASH_SOURCE[0]}")/../bin/thinknagent.js" ]]; then
|
|
463
|
-
node "$(dirname "${BASH_SOURCE[0]}")/../bin/thinknagent.js" app -p "$port"
|
|
464
|
-
else
|
|
465
|
-
log_err "Node.js required for GUI app. Run 'thinknagent.sh daemon' for background agent."
|
|
466
|
-
fi
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
cmd_status() {
|
|
470
|
-
local server=$(get_json_val "serverUrl")
|
|
471
|
-
local name=$(get_json_val "name")
|
|
472
|
-
local agentId=$(get_json_val "agentId")
|
|
473
|
-
local room=$(get_json_val "roomId")
|
|
474
|
-
local token=$(get_json_val "agentToken")
|
|
475
|
-
local status=$(get_json_val "status")
|
|
476
|
-
|
|
477
|
-
echo ""
|
|
478
|
-
echo " thinknagent Status"
|
|
479
|
-
echo " ─────────────────────────────────────────────"
|
|
480
|
-
echo " Name : ${name:-—}"
|
|
481
|
-
echo " Server : ${server:-—}"
|
|
482
|
-
echo " Room : ${room:-—}"
|
|
483
|
-
echo " Agent ID : ${agentId:-—}"
|
|
484
|
-
echo " Auth State : $([[ -n "$token" ]] && echo "APPROVED (Active)" || echo "PENDING (Waiting for Owner approval)")"
|
|
485
|
-
if [[ -f "$PID_FILE" ]] && kill -0 "$(cat "$PID_FILE" 2>/dev/null)" 2>/dev/null; then
|
|
486
|
-
echo " Process : RUNNING (PID $(cat "$PID_FILE"))"
|
|
487
|
-
else
|
|
488
|
-
echo " Process : STOPPED"
|
|
489
|
-
fi
|
|
490
|
-
echo " ─────────────────────────────────────────────"
|
|
491
|
-
echo ""
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
cmd_service() {
|
|
495
|
-
if [[ $EUID -ne 0 ]]; then
|
|
496
|
-
log_err "Please run with sudo to setup systemd service: sudo thinknagent daemon install"
|
|
497
|
-
exit 1
|
|
498
|
-
fi
|
|
499
|
-
|
|
500
|
-
local real_user="${SUDO_USER:-$(whoami)}"
|
|
501
|
-
local real_home=$(eval echo "~$real_user")
|
|
502
|
-
local script_path="${real_home}/.thinknagent/thinknagent.sh"
|
|
503
|
-
if [[ ! -f "$script_path" ]]; then
|
|
504
|
-
script_path="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
|
|
505
|
-
fi
|
|
506
|
-
|
|
507
|
-
cat > /etc/systemd/system/thinknagent.service << EOF
|
|
508
|
-
[Unit]
|
|
509
|
-
Description=ThinkNCollab Server Agent (Standalone)
|
|
510
|
-
After=network-online.target
|
|
511
|
-
Wants=network-online.target
|
|
512
|
-
|
|
513
|
-
[Service]
|
|
514
|
-
Type=simple
|
|
515
|
-
User=${real_user}
|
|
516
|
-
ExecStart=/bin/bash ${script_path} start
|
|
517
|
-
Restart=always
|
|
518
|
-
RestartSec=5
|
|
519
|
-
Environment=NODE_ENV=production
|
|
520
|
-
StandardOutput=journal
|
|
521
|
-
StandardError=journal
|
|
522
|
-
SyslogIdentifier=thinknagent
|
|
523
|
-
|
|
524
|
-
[Install]
|
|
525
|
-
WantedBy=multi-user.target
|
|
526
|
-
EOF
|
|
527
|
-
|
|
528
|
-
systemctl daemon-reload
|
|
529
|
-
systemctl enable thinknagent.service
|
|
530
|
-
systemctl restart thinknagent.service
|
|
531
|
-
log_ok "Systemd service 'thinknagent' installed and started successfully for user ${real_user}!"
|
|
532
|
-
log_info "Check service anytime: systemctl status thinknagent"
|
|
533
|
-
log_info "Check live logs: journalctl -u thinknagent -f"
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
case "${1:-status}" in
|
|
537
|
-
init) shift; cmd_init "$@" ;;
|
|
538
|
-
app) shift; cmd_app "$@" ;;
|
|
539
|
-
start) shift; cmd_start "$@" ;;
|
|
540
|
-
stop) cmd_stop ;;
|
|
541
|
-
restart) cmd_restart ;;
|
|
542
|
-
daemon)
|
|
543
|
-
case "${2:-start}" in
|
|
544
|
-
install) cmd_service ;;
|
|
545
|
-
start) cmd_daemon ;;
|
|
546
|
-
stop) cmd_stop ;;
|
|
547
|
-
restart) cmd_restart ;;
|
|
548
|
-
status) cmd_status ;;
|
|
549
|
-
*) cmd_daemon ;;
|
|
550
|
-
esac
|
|
551
|
-
;;
|
|
552
|
-
logs) cmd_logs ;;
|
|
553
|
-
revoke) cmd_revoke ;;
|
|
554
|
-
status) cmd_status ;;
|
|
555
|
-
service) cmd_service ;;
|
|
556
|
-
*)
|
|
557
|
-
echo "Usage: thinknagent {init|start|stop|restart|daemon|status|logs|app|revoke|service}"
|
|
558
|
-
exit 1
|
|
559
|
-
;;
|
|
560
|
-
esac
|