@agentsoc/beacon 0.0.1 → 0.0.2

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 CHANGED
@@ -12,50 +12,36 @@ The Syslog Beacon CLI allows you to configure, install, and run a lightweight lo
12
12
 
13
13
  ## Installation
14
14
 
15
- ### 🚀 Quick Install (One-Line Commands)
15
+ Installers are **hosted on the marketing site** (not duplicated in this repo):
16
16
 
17
- #### macOS / Linux
17
+ | Method | URL / command |
18
+ |--------|----------------|
19
+ | **macOS / Linux (curl)** | `curl -fsSL https://agentsoc.com/connectors/beacon.sh \| bash` |
20
+ | **Windows PowerShell** | `irm https://agentsoc.com/connectors/beacon-install.ps1 \| iex` |
21
+ | **Windows (batch)** | Download and run [beacon-install.bat](https://agentsoc.com/connectors/beacon-install.bat) |
22
+ | **npm** | `npm install -g @agentsoc/beacon` |
18
23
 
19
- ```bash
20
- curl -fsSL https://raw.githubusercontent.com/yourusername/platform.agentsoc.com/main/apps/connectors/syslog-beacon-cli/install.sh | bash
21
- ```
22
-
23
- #### Windows PowerShell
24
-
25
- ```powershell
26
- irm https://raw.githubusercontent.com/yourusername/platform.agentsoc.com/main/apps/connectors/syslog-beacon-cli/install.ps1 | iex
27
- ```
28
-
29
- #### Standard npm
30
-
31
- Published as [`@agentsoc/beacon`](https://www.npmjs.com/package/@agentsoc/beacon) (npm requires a lowercase scope; same AgentSOC package).
32
-
33
- ```bash
34
- npm install -g @agentsoc/beacon
35
- ```
24
+ Published package: [`@agentsoc/beacon`](https://www.npmjs.com/package/@agentsoc/beacon) (npm scope is lowercase `agentsoc`).
36
25
 
37
26
  The `syslog-beacon` command is also installed as an alias for the same CLI.
38
27
 
39
- ### Option 2: Run from source with Bun
28
+ ### Run from source (this monorepo)
40
29
 
41
30
  If you have the repository cloned and [Bun](https://bun.sh/) installed:
42
31
 
43
32
  ```bash
44
33
  cd apps/connectors/syslog-beacon-cli
45
34
  bun install
46
- bun run src/cli.ts config --url <url> --key <key>
47
- bun run src/cli.ts run
35
+ bun run build:simple
36
+ npm install -g .
48
37
  ```
49
38
 
50
- ### Installation Scripts
51
-
52
- Automated installation scripts are provided for easy setup:
39
+ Then use `beacon` / `syslog-beacon` like a normal global install:
53
40
 
54
- - **`install.sh`** - macOS/Linux installer
55
- - **`install.ps1`** - Windows PowerShell installer
56
- - **`install.bat`** - Windows batch installer
57
-
58
- These scripts automatically check for Node.js/npm, install the package, and verify the installation.
41
+ ```bash
42
+ beacon config --url <url> --key <key>
43
+ beacon run
44
+ ```
59
45
 
60
46
  ## Usage
61
47
 
@@ -115,34 +101,22 @@ beacon install
115
101
 
116
102
  - Start the daemon using npm script: `npm start` or `bun run start`
117
103
  - Install service using npm script: `npm run install-service` or `bun run install-service`
118
- - Build the distributable: `npm run build:simple` (compiles TypeScript to dist/)
119
-
120
- ## Building & Distribution
104
+ - Build the distributable: `npm run build:simple` (compiles TypeScript to `dist/`)
121
105
 
122
- ### Build the Package
106
+ ## Building & distribution
123
107
 
124
- To build the TypeScript files into the `dist/` folder:
108
+ ### Build the package
125
109
 
126
110
  ```bash
127
111
  npm run build:simple
128
112
  ```
129
113
 
130
- ### Share Installation Scripts
114
+ ### Publish to npm
131
115
 
132
- Users can install the package using one of the provided installation scripts:
133
-
134
- - Copy the `install.sh` script to your repository (macOS/Linux users)
135
- - Copy the `install.ps1` script to your repository (Windows PowerShell users)
136
- - Copy the `install.bat` script to your repository (Windows batch users)
137
-
138
- Users can then run:
116
+ From this directory, after bumping `version` in `package.json`:
139
117
 
140
118
  ```bash
141
- curl -fsSL https://raw.githubusercontent.com/yourusername/platform.agentsoc.com/main/apps/connectors/syslog-beacon-cli/install.sh | bash
119
+ npm publish --access public
142
120
  ```
143
121
 
144
- Or use npm:
145
-
146
- ```bash
147
- npm install -g @agentsoc/beacon
148
- ```
122
+ Public install entrypoints live under **`https://agentsoc.com/connectors/`** (`beacon.sh`, `beacon-install.ps1`, `beacon-install.bat`).
package/dist/cli.js CHANGED
@@ -3,6 +3,8 @@ import { Command } from "commander";
3
3
  import { loadConfig, saveConfig } from "./config.js";
4
4
  import { runBeacon } from "./run-beacon.js";
5
5
  import { installService } from "./service-install.js";
6
+ import { probeServiceStatus } from "./service-status.js";
7
+ import { getStatsFilePath, readStats } from "./stats.js";
6
8
  const program = new Command();
7
9
  program
8
10
  .name("beacon")
@@ -43,6 +45,39 @@ program
43
45
  process.exit(1);
44
46
  }
45
47
  });
48
+ program
49
+ .command("status")
50
+ .description("Show service state and forwarding statistics")
51
+ .action(async () => {
52
+ const svc = probeServiceStatus();
53
+ const stats = await readStats();
54
+ const statsPath = getStatsFilePath();
55
+ console.log("\n[beacon] Status\n");
56
+ console.log(` Service (${svc.manager}): ${svc.installed ? svc.stateLabel : "not installed"}`);
57
+ if (svc.active === true)
58
+ console.log(" Running: yes");
59
+ else if (svc.active === false)
60
+ console.log(" Running: no");
61
+ else
62
+ console.log(" Running: n/a");
63
+ if (svc.detail)
64
+ console.log(` Note: ${svc.detail}`);
65
+ console.log("\n Forwarding (from this machine's beacon stats file):");
66
+ console.log(` Stats file: ${statsPath}`);
67
+ console.log(` Log entries forwarded (successful): ${stats.logsForwarded}`);
68
+ console.log(` Successful batches: ${stats.batchesSucceeded}`);
69
+ console.log(` Failed batches: ${stats.batchesFailed}`);
70
+ if (stats.updatedAt && stats.updatedAt !== new Date(0).toISOString()) {
71
+ console.log(` Last stats update: ${stats.updatedAt}`);
72
+ }
73
+ else {
74
+ console.log(" Last stats update: (never - no successful forwards yet)");
75
+ }
76
+ if (stats.lastError) {
77
+ console.log(` Last error: ${stats.lastError}`);
78
+ }
79
+ console.log("\n If the service runs as another user (e.g. root), stats may be under that user's config directory.\n");
80
+ });
46
81
  program
47
82
  .command("run")
48
83
  .description("Run the forwarder daemon (foreground)")
package/dist/forwarder.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { recordFailedBatch, recordSuccessfulBatch } from "./stats.js";
1
2
  function parseSyslogPri(line) {
2
3
  // Default mapping if line does not start with <PRI>
3
4
  let severity = 5; // notice (maps to 'low' severity in our logic)
@@ -114,14 +115,18 @@ export class Forwarder {
114
115
  });
115
116
  if (!res.ok) {
116
117
  const text = await res.text();
118
+ const msg = `HTTP ${res.status} ${text}`;
117
119
  console.error(`[syslog-beacon] Failed to push logs: HTTP ${res.status} ${text}`);
120
+ await recordFailedBatch(msg);
118
121
  }
119
122
  else {
120
123
  console.log(`[syslog-beacon] Successfully pushed ${payload.length} logs`);
124
+ await recordSuccessfulBatch(payload.length);
121
125
  }
122
126
  }
123
127
  catch (err) {
124
128
  console.error(`[syslog-beacon] Ingest error:`, err);
129
+ await recordFailedBatch(err instanceof Error ? err.message : String(err));
125
130
  }
126
131
  }
127
132
  async stop() {
@@ -0,0 +1,95 @@
1
+ import fs from "node:fs";
2
+ import { execFileSync } from "node:child_process";
3
+ function trimDetail(s, max = 200) {
4
+ const one = s.replace(/\s+/g, " ").trim();
5
+ return one.length <= max ? one : `${one.slice(0, max)}…`;
6
+ }
7
+ export function probeServiceStatus() {
8
+ if (process.platform === "linux") {
9
+ const unitPath = "/etc/systemd/system/syslog-beacon.service";
10
+ const installed = fs.existsSync(unitPath);
11
+ if (!installed) {
12
+ return {
13
+ manager: "systemd",
14
+ installed: false,
15
+ active: null,
16
+ stateLabel: "not installed",
17
+ detail: "No unit at /etc/systemd/system/syslog-beacon.service",
18
+ };
19
+ }
20
+ try {
21
+ const out = execFileSync("systemctl", ["is-active", "syslog-beacon"], {
22
+ encoding: "utf8",
23
+ stdio: ["ignore", "pipe", "pipe"],
24
+ }).trim();
25
+ const active = out === "active";
26
+ return {
27
+ manager: "systemd",
28
+ installed: true,
29
+ active,
30
+ stateLabel: out || "unknown",
31
+ };
32
+ }
33
+ catch (err) {
34
+ const status = err.status;
35
+ const stderr = err.stderr?.toString?.() ?? "";
36
+ if (status === 3) {
37
+ return {
38
+ manager: "systemd",
39
+ installed: true,
40
+ active: false,
41
+ stateLabel: "inactive",
42
+ };
43
+ }
44
+ return {
45
+ manager: "systemd",
46
+ installed: true,
47
+ active: false,
48
+ stateLabel: "unknown",
49
+ detail: trimDetail(stderr || String(err)),
50
+ };
51
+ }
52
+ }
53
+ if (process.platform === "darwin") {
54
+ const plistPath = "/Library/LaunchDaemons/com.agentsoc.syslog-beacon.plist";
55
+ const installed = fs.existsSync(plistPath);
56
+ if (!installed) {
57
+ return {
58
+ manager: "launchd",
59
+ installed: false,
60
+ active: null,
61
+ stateLabel: "not installed",
62
+ detail: `No plist at ${plistPath}`,
63
+ };
64
+ }
65
+ try {
66
+ const out = execFileSync("launchctl", ["print", "system/com.agentsoc.syslog-beacon"], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
67
+ const m = out.match(/^\s*state\s*=\s*(\S+)/m);
68
+ const state = m?.[1] ?? "unknown";
69
+ const active = state === "running";
70
+ return {
71
+ manager: "launchd",
72
+ installed: true,
73
+ active,
74
+ stateLabel: state,
75
+ };
76
+ }
77
+ catch (err) {
78
+ const stderr = err.stderr?.toString?.() ?? "";
79
+ return {
80
+ manager: "launchd",
81
+ installed: true,
82
+ active: null,
83
+ stateLabel: "unknown",
84
+ detail: trimDetail(stderr || String(err)),
85
+ };
86
+ }
87
+ }
88
+ return {
89
+ manager: "none",
90
+ installed: false,
91
+ active: null,
92
+ stateLabel: "unsupported",
93
+ detail: "Automatic service detection is only available on Linux (systemd) and macOS (launchd).",
94
+ };
95
+ }
package/dist/stats.js ADDED
@@ -0,0 +1,71 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { getConfigDir } from "./config.js";
4
+ const STATS_VERSION = 1;
5
+ const emptyStats = () => ({
6
+ version: STATS_VERSION,
7
+ updatedAt: new Date(0).toISOString(),
8
+ logsForwarded: 0,
9
+ batchesSucceeded: 0,
10
+ batchesFailed: 0,
11
+ });
12
+ export function getStatsFilePath() {
13
+ return path.join(getConfigDir(), "stats.json");
14
+ }
15
+ export async function readStats() {
16
+ try {
17
+ const raw = await fs.readFile(getStatsFilePath(), "utf8");
18
+ const parsed = JSON.parse(raw);
19
+ if (parsed.version !== STATS_VERSION)
20
+ return emptyStats();
21
+ return {
22
+ ...emptyStats(),
23
+ ...parsed,
24
+ version: STATS_VERSION,
25
+ };
26
+ }
27
+ catch {
28
+ return emptyStats();
29
+ }
30
+ }
31
+ async function writeStatsAtomic(stats) {
32
+ const file = getStatsFilePath();
33
+ const dir = path.dirname(file);
34
+ await fs.mkdir(dir, { recursive: true });
35
+ const tmp = `${file}.${process.pid}.tmp`;
36
+ await fs.writeFile(tmp, JSON.stringify(stats, null, 2), "utf8");
37
+ await fs.rename(tmp, file);
38
+ }
39
+ export async function recordSuccessfulBatch(logCount) {
40
+ try {
41
+ const cur = await readStats();
42
+ await writeStatsAtomic({
43
+ version: STATS_VERSION,
44
+ updatedAt: new Date().toISOString(),
45
+ logsForwarded: cur.logsForwarded + logCount,
46
+ batchesSucceeded: cur.batchesSucceeded + 1,
47
+ batchesFailed: cur.batchesFailed,
48
+ lastError: undefined,
49
+ });
50
+ }
51
+ catch {
52
+ // stats are best-effort; never break forwarding
53
+ }
54
+ }
55
+ export async function recordFailedBatch(message) {
56
+ try {
57
+ const cur = await readStats();
58
+ const trimmed = message.replace(/\s+/g, " ").slice(0, 500);
59
+ await writeStatsAtomic({
60
+ version: STATS_VERSION,
61
+ updatedAt: new Date().toISOString(),
62
+ logsForwarded: cur.logsForwarded,
63
+ batchesSucceeded: cur.batchesSucceeded,
64
+ batchesFailed: cur.batchesFailed + 1,
65
+ lastError: trimmed || "Unknown error",
66
+ });
67
+ }
68
+ catch {
69
+ // best-effort
70
+ }
71
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentsoc/beacon",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Lightweight, background-running log forwarder (beacon) for AgentSOC",
5
5
  "type": "module",
6
6
  "bin": {