@the-seeker/server-agent 0.1.0

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.
@@ -0,0 +1,115 @@
1
+ import { createHash } from "node:crypto";
2
+ import { z } from "zod";
3
+ export const PROTOCOL_VERSION = 1;
4
+ export const MAX_HEARTBEAT_BYTES = 131072;
5
+ export const MAX_RESPONSE_BYTES = 393216;
6
+ export const LOG_LINES_OPTIONS = [100, 200, 500];
7
+ export const LOG_MAX_BYTES = 262144;
8
+ export const REQUEST_TIMEOUT_MS = 20000;
9
+ export const OFFLINE_AFTER_MS = 90000;
10
+ export const PROCESS_HISTORY_INTERVAL_MS = 60000;
11
+ export const SourceKindSchema = z.enum(["pm2_out", "pm2_err", "nginx_access", "nginx_error", "systemd_journal"]);
12
+ export const SourceSchema = z.object({
13
+ id: z.string().max(120).regex(/^[a-z0-9._:-]+$/),
14
+ kind: SourceKindSchema, label: z.string(), group: z.string(),
15
+ }).strict();
16
+ export function makeSourceId(kind, rawName) {
17
+ const slug = rawName.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").slice(0, 60);
18
+ const hash6 = createHash("sha1").update(rawName).digest("hex").slice(0, 6);
19
+ return `${kind}:${slug}-${hash6}`;
20
+ }
21
+ export const CapabilityStateSchema = z.enum(["available", "unsupported", "not_installed", "permission_denied", "error"]);
22
+ export const CapabilitiesSchema = z.object({
23
+ host: CapabilityStateSchema, pm2: CapabilityStateSchema,
24
+ systemd: CapabilityStateSchema, nginx: CapabilityStateSchema,
25
+ }).strict();
26
+ const isoTimestamp = z.iso.datetime({ offset: true });
27
+ const memory = z.object({ used: z.number(), total: z.number() }).strict();
28
+ export const HostSchema = z.object({
29
+ cpu_pct: z.number().min(0).max(100).nullable(), cpu_count: z.number(),
30
+ load: z.tuple([z.number(), z.number(), z.number()]).nullable(),
31
+ mem: memory.nullable(), swap: memory.nullable(),
32
+ disks: z.array(z.object({
33
+ mount: z.string(), fs: z.string(), used: z.number(), total: z.number(),
34
+ }).strict()).max(32),
35
+ net: z.object({ rx_bps: z.number().nullable(), tx_bps: z.number().nullable() }).strict(),
36
+ uptime_sec: z.number(),
37
+ }).strict();
38
+ export const ProcessSchema = z.object({
39
+ name: z.string(), pm_id: z.number(), status: z.string(), cpu_pct: z.number(),
40
+ memory_bytes: z.number(), restarts: z.number(), unstable_restarts: z.number(),
41
+ uptime_ms: z.number().nullable(), version: z.string().nullable(), exec_mode: z.string().nullable(),
42
+ }).strict();
43
+ export const ServiceSchema = z.object({
44
+ unit: z.string(), active_state: z.string(), sub_state: z.string(),
45
+ }).strict();
46
+ export const HelloFrameSchema = z.object({
47
+ type: z.literal("hello"), protocol_version: z.literal(PROTOCOL_VERSION),
48
+ agent_version: z.string(), hostname: z.string(),
49
+ os: z.object({ platform: z.enum(["linux", "darwin"]), release: z.string(), arch: z.string() }).strict(),
50
+ cpu_count: z.number(), boot_time: isoTimestamp, capabilities: CapabilitiesSchema,
51
+ sources: z.array(SourceSchema),
52
+ }).strict();
53
+ export const HeartbeatFrameSchema = z.object({
54
+ type: z.literal("heartbeat"), seq: z.number().int().min(1), collected_at: isoTimestamp,
55
+ host: HostSchema, processes: z.array(ProcessSchema).max(100),
56
+ services: z.array(ServiceSchema).max(50), capabilities: CapabilitiesSchema,
57
+ sources: z.array(SourceSchema).optional(),
58
+ }).strict();
59
+ const configuration = {
60
+ interval_sec: z.number(), watched_services: z.array(z.string()), nginx_enabled: z.boolean(),
61
+ };
62
+ export const WelcomeFrameSchema = z.object({
63
+ type: z.literal("welcome"), server_id: z.string(), connection_generation: z.number(), ...configuration,
64
+ }).strict();
65
+ export const ConfigFrameSchema = z.object({ type: z.literal("config"), ...configuration }).strict();
66
+ export const LogsTailParamsSchema = z.object({ source_id: z.string(), lines: z.literal(LOG_LINES_OPTIONS) }).strict();
67
+ export const ServiceStatusParamsSchema = z.object({ unit: z.string() }).strict();
68
+ export const Pm2DescribeParamsSchema = z.object({ name: z.string() }).strict();
69
+ export const NginxSourcesParamsSchema = z.object({}).strict();
70
+ const request = { type: z.literal("request"), request_id: z.uuid() };
71
+ export const RequestFrameSchema = z.discriminatedUnion("action", [
72
+ z.object({ ...request, action: z.literal("logs.tail"), params: LogsTailParamsSchema }).strict(),
73
+ z.object({ ...request, action: z.literal("service.status"), params: ServiceStatusParamsSchema }).strict(),
74
+ z.object({ ...request, action: z.literal("pm2.describe"), params: Pm2DescribeParamsSchema }).strict(),
75
+ z.object({ ...request, action: z.literal("nginx.sources"), params: NginxSourcesParamsSchema }).strict(),
76
+ ]);
77
+ export const LogsTailPayloadSchema = z.object({
78
+ lines: z.array(z.string()), truncated: z.boolean(), bytes: z.number(), source_id: z.string(),
79
+ }).strict();
80
+ export const ServiceStatusPayloadSchema = ServiceSchema;
81
+ export const Pm2DescribePayloadSchema = ProcessSchema;
82
+ export const NginxSourcesPayloadSchema = z.object({ sources: z.array(SourceSchema) }).strict();
83
+ export const ResponseErrorSchema = z.object({
84
+ code: z.enum(["not_found", "permission_denied", "unsupported", "timeout", "too_large", "internal"]),
85
+ message: z.string(),
86
+ }).strict();
87
+ export const SuccessResponseFrameSchema = z.object({
88
+ type: z.literal("response"), request_id: z.string(), ok: z.literal(true),
89
+ payload: z.union([LogsTailPayloadSchema, ServiceStatusPayloadSchema, Pm2DescribePayloadSchema, NginxSourcesPayloadSchema]),
90
+ }).strict();
91
+ export const ErrorResponseFrameSchema = z.object({
92
+ type: z.literal("response"), request_id: z.string(), ok: z.literal(false), error: ResponseErrorSchema,
93
+ }).strict();
94
+ export const ResponseFrameSchema = z.discriminatedUnion("ok", [SuccessResponseFrameSchema, ErrorResponseFrameSchema]);
95
+ export const AgentFrameSchema = z.discriminatedUnion("type", [HelloFrameSchema, HeartbeatFrameSchema, ResponseFrameSchema]);
96
+ export const GatewayFrameSchema = z.discriminatedUnion("type", [WelcomeFrameSchema, ConfigFrameSchema, RequestFrameSchema]);
97
+ function parseFrame(text, schema) {
98
+ let value;
99
+ try {
100
+ value = JSON.parse(text);
101
+ }
102
+ catch (error) {
103
+ if (error instanceof SyntaxError)
104
+ return { ok: false, error };
105
+ throw error;
106
+ }
107
+ const result = schema.safeParse(value);
108
+ return result.success ? { ok: true, frame: result.data } : { ok: false, error: result.error };
109
+ }
110
+ export function parseAgentFrame(text) {
111
+ return parseFrame(text, AgentFrameSchema);
112
+ }
113
+ export function parseGatewayFrame(text) {
114
+ return parseFrame(text, GatewayFrameSchema);
115
+ }
package/package.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@the-seeker/server-agent",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "bin": { "theseeker-agent": "dist/cli.js" },
6
+ "engines": { "node": ">=20" },
7
+ "files": ["dist", "scripts", "README.md"],
8
+ "publishConfig": { "access": "public" },
9
+ "scripts": { "build": "tsc -p tsconfig.json", "test": "vitest run" },
10
+ "dependencies": { "systeminformation": "^5", "ws": "^8", "zod": "^4.4.3" },
11
+ "devDependencies": { "@types/node": "^20", "@types/ws": "^8", "typescript": "^5.9.3", "vitest": "^4.1.7" }
12
+ }
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+ # The Seeker monitoring agent installer.
3
+ # TOKEN=sa_... [ENDPOINT=wss://ingest.theseeker.io/agent/ws] ./install.sh [--add-groups]
4
+ set -euo pipefail
5
+
6
+ TOKEN="${TOKEN:-}"
7
+ ENDPOINT="${ENDPOINT:-wss://ingest.theseeker.io/agent/ws}"
8
+
9
+ if [ -z "$TOKEN" ]; then
10
+ echo "TOKEN is required (TOKEN=sa_... ./install.sh)"
11
+ exit 1
12
+ fi
13
+
14
+ command -v node >/dev/null || { echo "Node.js 20+ required"; exit 1; }
15
+
16
+ if [ "$(uname -s)" = "Darwin" ]; then
17
+ npm i -g @the-seeker/server-agent@latest
18
+ theseeker-agent install --token "$TOKEN" --endpoint "$ENDPOINT" --user "$USER" "$@"
19
+ else
20
+ sudo npm i -g @the-seeker/server-agent@latest
21
+ sudo theseeker-agent install --token "$TOKEN" --endpoint "$ENDPOINT" --user "$USER" "$@"
22
+ fi