@voidwire/argus-send 2.0.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.
- package/LICENSE +21 -0
- package/README.md +174 -0
- package/cli.ts +288 -0
- package/index.ts +187 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Rudy Ruiz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# argus-send
|
|
2
|
+
|
|
3
|
+
Send events to Argus observability platform from the command line.
|
|
4
|
+
|
|
5
|
+
## Philosophy
|
|
6
|
+
|
|
7
|
+
**Synchronous delivery** - Blocks until Argus confirms event captured, ensuring durability.
|
|
8
|
+
|
|
9
|
+
**Config-aware** - Automatically reads API key from `~/.config/argus/config.toml`, no manual config needed.
|
|
10
|
+
|
|
11
|
+
**Pipes JSON** - Accepts JSON data from other tools (gitignore-check, language-detect) via stdin.
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Tool event with agent observability fields
|
|
17
|
+
argus-send --source momentum --type tool \
|
|
18
|
+
--hook PreToolUse \
|
|
19
|
+
--session-id "f10e9765-1999-456f-81c3-eb4c531ecee2" \
|
|
20
|
+
--tool-name Bash \
|
|
21
|
+
--tool-use-id "toolu_01ABC" \
|
|
22
|
+
--message "Bash: git status"
|
|
23
|
+
|
|
24
|
+
# Session event
|
|
25
|
+
argus-send --source momentum --type session \
|
|
26
|
+
--hook SessionStart \
|
|
27
|
+
--session-id "f10e9765-1999-456f-81c3-eb4c531ecee2" \
|
|
28
|
+
--message "Session started: argus (active)"
|
|
29
|
+
|
|
30
|
+
# Pipe from another tool
|
|
31
|
+
gitignore-check . | argus-send --source llcli-tools --type tool --stdin
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
cd packages/argus-send
|
|
38
|
+
bun link
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Now `argus-send` is available globally.
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
argus-send --source <name> --type <event-type> [options]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Required Arguments
|
|
50
|
+
|
|
51
|
+
- `--source <name>` - Source name (e.g., "llcli-tools", "momentum")
|
|
52
|
+
- `--type <event-type>` - Event type: `tool`, `session`, `agent`, `response`, `prompt`
|
|
53
|
+
|
|
54
|
+
### Optional Arguments
|
|
55
|
+
|
|
56
|
+
- `--message <text>` - Human-readable message
|
|
57
|
+
- `--hook <hook>` - Hook name: `PreToolUse`, `PostToolUse`, `Stop`, `SessionStart`, `SessionEnd`, `SubagentStart`, `SubagentStop`, `UserPromptSubmit`
|
|
58
|
+
- `--session-id <id>` - Claude Code session identifier
|
|
59
|
+
- `--tool-name <name>` - Tool name (Bash, Read, Edit, Task, etc.)
|
|
60
|
+
- `--tool-use-id <id>` - Correlates PreToolUse/PostToolUse pairs
|
|
61
|
+
- `--status <status>` - Event outcome: `success`, `failure`, `pending`
|
|
62
|
+
- `--data <json>` - JSON data string
|
|
63
|
+
- `--stdin` - Read data object from stdin (JSON)
|
|
64
|
+
- `--host <url>` - Argus host (default: `http://127.0.0.1:8765`)
|
|
65
|
+
- `--api-key <key>` - Override API key from config
|
|
66
|
+
- `-h, --help` - Show help
|
|
67
|
+
|
|
68
|
+
### Environment Variables
|
|
69
|
+
|
|
70
|
+
- `ARGUS_API_KEY` - Override config file API key
|
|
71
|
+
- `ARGUS_HOST` - Override default host
|
|
72
|
+
|
|
73
|
+
## Output Format
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"captured": true,
|
|
78
|
+
"event_id": 123
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Configuration
|
|
83
|
+
|
|
84
|
+
argus-send reads the API key from `~/.config/argus/config.toml` automatically:
|
|
85
|
+
|
|
86
|
+
```toml
|
|
87
|
+
[server]
|
|
88
|
+
api_keys = ["your-api-key-here"]
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
No additional config needed. If the file doesn't exist, install Argus:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
cd ~/development/projects/argus
|
|
95
|
+
uv run argus config init
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Integration with Other Tools
|
|
99
|
+
|
|
100
|
+
### gitignore-check
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# Send compliance check results to Argus
|
|
104
|
+
gitignore-check . | argus-send --source llcli-tools --type gitignore-check --stdin
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### language-detect
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Track language detection events
|
|
111
|
+
language-detect . | argus-send --source llcli-tools --type language-detect --stdin
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Momentum Hooks
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// In momentum pre-tool-use hook
|
|
118
|
+
const result = Bun.spawnSync([
|
|
119
|
+
"argus-send",
|
|
120
|
+
"--source", "momentum",
|
|
121
|
+
"--type", "tool",
|
|
122
|
+
"--hook", "PreToolUse",
|
|
123
|
+
"--session-id", data.session_id,
|
|
124
|
+
"--tool-name", data.tool_name,
|
|
125
|
+
"--tool-use-id", data.tool_use_id,
|
|
126
|
+
"--message", `${data.tool_name}: ${summary}`
|
|
127
|
+
]);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Architecture
|
|
131
|
+
|
|
132
|
+
- **Zero dependencies** - Uses Bun's native `fetch`
|
|
133
|
+
- **TOML parsing** - Regex-based extraction (no TOML library needed)
|
|
134
|
+
- **Synchronous POST** - Blocks until Argus stores event
|
|
135
|
+
- **Stdin support** - Reads piped JSON for composability
|
|
136
|
+
|
|
137
|
+
## Why Synchronous?
|
|
138
|
+
|
|
139
|
+
Argus uses synchronous POST to guarantee event delivery before continuing. This prevents event loss on crashes and ensures observability gaps are filled.
|
|
140
|
+
|
|
141
|
+
The tool blocks until Argus returns `{"status": "captured", "event_id": 123}`, confirming the event is durably stored in SQLite with WAL mode.
|
|
142
|
+
|
|
143
|
+
## Error Handling
|
|
144
|
+
|
|
145
|
+
**Exit codes:**
|
|
146
|
+
- `0` - Event captured successfully
|
|
147
|
+
- `1` - Argus server error (connection failed, rejected event)
|
|
148
|
+
- `2` - Client error (missing args, invalid JSON, config not found)
|
|
149
|
+
|
|
150
|
+
**Common errors:**
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
# API key not found
|
|
154
|
+
❌ API key not found in config
|
|
155
|
+
# Solution: Run `uv run argus config init` in argus project
|
|
156
|
+
|
|
157
|
+
# Connection refused
|
|
158
|
+
❌ Failed: Connection failed: error sending request
|
|
159
|
+
# Solution: Start Argus server with `uv run argus serve`
|
|
160
|
+
|
|
161
|
+
# Invalid JSON
|
|
162
|
+
❌ Invalid JSON in --data
|
|
163
|
+
# Solution: Check JSON syntax, ensure proper quotes
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Examples
|
|
167
|
+
|
|
168
|
+
See [QUICKSTART.md](QUICKSTART.md) for comprehensive examples.
|
|
169
|
+
|
|
170
|
+
## Related
|
|
171
|
+
|
|
172
|
+
- [Argus](https://github.com/yourusername/argus) - Observability platform this tool sends to
|
|
173
|
+
- [gitignore-check](../gitignore-check/) - Compliance checker that can pipe to argus-send
|
|
174
|
+
- [language-detect](../language-detect/) - Language detector that can pipe to argus-send
|
package/cli.ts
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* argus-send CLI
|
|
4
|
+
*
|
|
5
|
+
* Philosophy:
|
|
6
|
+
* - Synchronous delivery - Blocks until Argus confirms capture
|
|
7
|
+
* - Config-aware - Reads API key from ~/.config/argus/config.toml
|
|
8
|
+
* - Composable - Accepts data via --data flag or stdin pipe
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* argus-send --source <name> --type <event-type> [options]
|
|
12
|
+
*
|
|
13
|
+
* Exit codes:
|
|
14
|
+
* 0 - Event captured successfully
|
|
15
|
+
* 1 - Argus server error (connection failed, rejected event)
|
|
16
|
+
* 2 - Client error (missing args, invalid data, config not found)
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
sendEvent,
|
|
21
|
+
loadConfig,
|
|
22
|
+
type ArgusEvent,
|
|
23
|
+
type ArgusHook,
|
|
24
|
+
type ArgusStatus,
|
|
25
|
+
} from "./index";
|
|
26
|
+
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// Stdin Reading
|
|
29
|
+
// ============================================================================
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Read JSON data from stdin
|
|
33
|
+
*/
|
|
34
|
+
async function readStdin(): Promise<unknown | null> {
|
|
35
|
+
const chunks: Buffer[] = [];
|
|
36
|
+
|
|
37
|
+
for await (const chunk of Bun.stdin.stream()) {
|
|
38
|
+
chunks.push(Buffer.from(chunk));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (chunks.length === 0) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const input = Buffer.concat(chunks).toString("utf-8").trim();
|
|
46
|
+
if (!input) return null;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
return JSON.parse(input);
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// CLI
|
|
57
|
+
// ============================================================================
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Print usage and exit
|
|
61
|
+
*/
|
|
62
|
+
function printUsage(): void {
|
|
63
|
+
console.error(`
|
|
64
|
+
argus-send - Send events to Argus observability platform
|
|
65
|
+
|
|
66
|
+
Philosophy:
|
|
67
|
+
Synchronous delivery ensures event captured before continuing.
|
|
68
|
+
Config-aware tool reads API key from Argus config automatically.
|
|
69
|
+
Composable with other llcli tools via JSON piping.
|
|
70
|
+
|
|
71
|
+
Usage: argus-send --source <name> --type <event-type> [options]
|
|
72
|
+
|
|
73
|
+
Required:
|
|
74
|
+
--source <name> Source name (e.g., "llcli-tools", "momentum")
|
|
75
|
+
--type <event-type> Event type: tool, session, agent, response, prompt
|
|
76
|
+
|
|
77
|
+
Optional:
|
|
78
|
+
--message <text> Human-readable message
|
|
79
|
+
--hook <hook> Hook name: PreToolUse, PostToolUse, Stop, SessionStart,
|
|
80
|
+
SessionEnd, SubagentStart, SubagentStop, UserPromptSubmit
|
|
81
|
+
--session-id <id> Claude Code session identifier
|
|
82
|
+
--tool-name <name> Tool name (Bash, Read, Edit, Task, etc.)
|
|
83
|
+
--tool-use-id <id> Correlates PreToolUse/PostToolUse pairs
|
|
84
|
+
--status <status> Event outcome: success, failure, pending
|
|
85
|
+
--data <json> JSON data string
|
|
86
|
+
--stdin Read data from stdin (JSON)
|
|
87
|
+
--host <url> Argus host (default: http://127.0.0.1:8765)
|
|
88
|
+
--api-key <key> API key (default: from ~/.config/argus/config.toml)
|
|
89
|
+
-h, --help Show this help
|
|
90
|
+
|
|
91
|
+
Environment:
|
|
92
|
+
ARGUS_API_KEY Override config file API key
|
|
93
|
+
ARGUS_HOST Override default host
|
|
94
|
+
|
|
95
|
+
Examples:
|
|
96
|
+
# Tool event
|
|
97
|
+
argus-send --source momentum --type tool \\
|
|
98
|
+
--hook PreToolUse \\
|
|
99
|
+
--session-id "f10e9765-1999-456f-81c3-eb4c531ecee2" \\
|
|
100
|
+
--tool-name Bash \\
|
|
101
|
+
--tool-use-id "toolu_01ABC" \\
|
|
102
|
+
--message "Bash: git status"
|
|
103
|
+
|
|
104
|
+
# Session event
|
|
105
|
+
argus-send --source momentum --type session \\
|
|
106
|
+
--hook SessionStart \\
|
|
107
|
+
--session-id "f10e9765-1999-456f-81c3-eb4c531ecee2" \\
|
|
108
|
+
--message "Session started: argus (active)"
|
|
109
|
+
|
|
110
|
+
# Pipe data from another tool
|
|
111
|
+
gitignore-check . | argus-send --source llcli-tools --type tool --stdin
|
|
112
|
+
`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
interface ParsedArgs {
|
|
116
|
+
source: string;
|
|
117
|
+
eventType: string;
|
|
118
|
+
message?: string;
|
|
119
|
+
hook?: ArgusHook;
|
|
120
|
+
sessionId?: string;
|
|
121
|
+
toolName?: string;
|
|
122
|
+
toolUseId?: string;
|
|
123
|
+
status?: ArgusStatus;
|
|
124
|
+
dataStr?: string;
|
|
125
|
+
useStdin: boolean;
|
|
126
|
+
host: string;
|
|
127
|
+
apiKey: string | null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Parse command-line arguments
|
|
132
|
+
*/
|
|
133
|
+
function parseArgs(
|
|
134
|
+
argv: string[],
|
|
135
|
+
config: ReturnType<typeof loadConfig>,
|
|
136
|
+
): ParsedArgs | null {
|
|
137
|
+
const args = argv.slice(2);
|
|
138
|
+
|
|
139
|
+
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Check for deprecated --level flag
|
|
144
|
+
if (args.includes("--level")) {
|
|
145
|
+
console.error(
|
|
146
|
+
"❌ --level flag is deprecated and rejected by Argus API. Remove it from your command.",
|
|
147
|
+
);
|
|
148
|
+
process.exit(2);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
let source: string | null = null;
|
|
152
|
+
let eventType: string | null = null;
|
|
153
|
+
let message: string | undefined;
|
|
154
|
+
let hook: ArgusHook | undefined;
|
|
155
|
+
let sessionId: string | undefined;
|
|
156
|
+
let toolName: string | undefined;
|
|
157
|
+
let toolUseId: string | undefined;
|
|
158
|
+
let status: ArgusStatus | undefined;
|
|
159
|
+
let dataStr: string | undefined;
|
|
160
|
+
let useStdin = false;
|
|
161
|
+
let host = process.env.ARGUS_HOST || config.host;
|
|
162
|
+
let apiKey: string | null = process.env.ARGUS_API_KEY || config.apiKey;
|
|
163
|
+
|
|
164
|
+
for (let i = 0; i < args.length; i++) {
|
|
165
|
+
const arg = args[i];
|
|
166
|
+
|
|
167
|
+
if (arg === "--source" && i + 1 < args.length) {
|
|
168
|
+
source = args[++i];
|
|
169
|
+
} else if (arg === "--type" && i + 1 < args.length) {
|
|
170
|
+
eventType = args[++i];
|
|
171
|
+
} else if (arg === "--message" && i + 1 < args.length) {
|
|
172
|
+
message = args[++i];
|
|
173
|
+
} else if (arg === "--hook" && i + 1 < args.length) {
|
|
174
|
+
hook = args[++i] as ArgusHook;
|
|
175
|
+
} else if (arg === "--session-id" && i + 1 < args.length) {
|
|
176
|
+
sessionId = args[++i];
|
|
177
|
+
} else if (arg === "--tool-name" && i + 1 < args.length) {
|
|
178
|
+
toolName = args[++i];
|
|
179
|
+
} else if (arg === "--tool-use-id" && i + 1 < args.length) {
|
|
180
|
+
toolUseId = args[++i];
|
|
181
|
+
} else if (arg === "--status" && i + 1 < args.length) {
|
|
182
|
+
status = args[++i] as ArgusStatus;
|
|
183
|
+
} else if (arg === "--data" && i + 1 < args.length) {
|
|
184
|
+
dataStr = args[++i];
|
|
185
|
+
} else if (arg === "--stdin") {
|
|
186
|
+
useStdin = true;
|
|
187
|
+
} else if (arg === "--host" && i + 1 < args.length) {
|
|
188
|
+
host = args[++i];
|
|
189
|
+
} else if (arg === "--api-key" && i + 1 < args.length) {
|
|
190
|
+
apiKey = args[++i];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!source || !eventType) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
source,
|
|
200
|
+
eventType,
|
|
201
|
+
message,
|
|
202
|
+
hook,
|
|
203
|
+
sessionId,
|
|
204
|
+
toolName,
|
|
205
|
+
toolUseId,
|
|
206
|
+
status,
|
|
207
|
+
dataStr,
|
|
208
|
+
useStdin,
|
|
209
|
+
host,
|
|
210
|
+
apiKey,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Main entry point
|
|
216
|
+
*/
|
|
217
|
+
async function main(): Promise<void> {
|
|
218
|
+
const config = loadConfig();
|
|
219
|
+
const parsed = parseArgs(process.argv, config);
|
|
220
|
+
|
|
221
|
+
if (!parsed) {
|
|
222
|
+
printUsage();
|
|
223
|
+
process.exit(parsed === null && process.argv.length > 2 ? 2 : 0);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Validate API key
|
|
227
|
+
if (!parsed.apiKey) {
|
|
228
|
+
console.log(
|
|
229
|
+
JSON.stringify({
|
|
230
|
+
captured: false,
|
|
231
|
+
error: "API key not found (check ~/.config/argus/config.toml)",
|
|
232
|
+
}),
|
|
233
|
+
);
|
|
234
|
+
console.error("❌ API key not found in config");
|
|
235
|
+
process.exit(2);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Build event
|
|
239
|
+
const event: ArgusEvent = {
|
|
240
|
+
source: parsed.source,
|
|
241
|
+
event_type: parsed.eventType,
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
if (parsed.message) event.message = parsed.message;
|
|
245
|
+
if (parsed.hook) event.hook = parsed.hook;
|
|
246
|
+
if (parsed.sessionId) event.session_id = parsed.sessionId;
|
|
247
|
+
if (parsed.toolName) event.tool_name = parsed.toolName;
|
|
248
|
+
if (parsed.toolUseId) event.tool_use_id = parsed.toolUseId;
|
|
249
|
+
if (parsed.status) event.status = parsed.status;
|
|
250
|
+
|
|
251
|
+
// Parse data from --data flag or stdin
|
|
252
|
+
if (parsed.useStdin) {
|
|
253
|
+
const stdinData = await readStdin();
|
|
254
|
+
if (stdinData) {
|
|
255
|
+
event.data = stdinData;
|
|
256
|
+
}
|
|
257
|
+
} else if (parsed.dataStr) {
|
|
258
|
+
try {
|
|
259
|
+
event.data = JSON.parse(parsed.dataStr);
|
|
260
|
+
} catch {
|
|
261
|
+
console.log(
|
|
262
|
+
JSON.stringify({
|
|
263
|
+
captured: false,
|
|
264
|
+
error: "Invalid JSON in --data argument",
|
|
265
|
+
}),
|
|
266
|
+
);
|
|
267
|
+
console.error("❌ Invalid JSON in --data");
|
|
268
|
+
process.exit(2);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Send event
|
|
273
|
+
const result = await sendEvent(event, parsed.apiKey, parsed.host);
|
|
274
|
+
|
|
275
|
+
// Output JSON (stdout)
|
|
276
|
+
console.log(JSON.stringify(result));
|
|
277
|
+
|
|
278
|
+
// Diagnostic to stderr
|
|
279
|
+
if (result.captured) {
|
|
280
|
+
console.error(`✅ Event captured (ID: ${result.event_id})`);
|
|
281
|
+
process.exit(0);
|
|
282
|
+
} else {
|
|
283
|
+
console.error(`❌ Failed: ${result.error}`);
|
|
284
|
+
process.exit(1);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
main();
|
package/index.ts
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* argus-send - Library exports
|
|
3
|
+
*
|
|
4
|
+
* Send events to Argus observability platform.
|
|
5
|
+
* Pure functions, no process.exit, no stderr output.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { sendEvent, loadConfig } from "argus-send";
|
|
9
|
+
* const config = loadConfig();
|
|
10
|
+
* const result = await sendEvent(event, config.apiKey!, config.host);
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { readFileSync, existsSync } from "fs";
|
|
14
|
+
import { join } from "path";
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Types
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
export type ArgusEventType =
|
|
21
|
+
| "tool"
|
|
22
|
+
| "session"
|
|
23
|
+
| "agent"
|
|
24
|
+
| "response"
|
|
25
|
+
| "prompt";
|
|
26
|
+
|
|
27
|
+
export type ArgusHook =
|
|
28
|
+
| "PreToolUse"
|
|
29
|
+
| "PostToolUse"
|
|
30
|
+
| "Stop"
|
|
31
|
+
| "SessionStart"
|
|
32
|
+
| "SessionEnd"
|
|
33
|
+
| "SubagentStart"
|
|
34
|
+
| "SubagentStop"
|
|
35
|
+
| "UserPromptSubmit";
|
|
36
|
+
|
|
37
|
+
export type ArgusStatus = "success" | "failure" | "pending";
|
|
38
|
+
|
|
39
|
+
export interface ArgusEvent {
|
|
40
|
+
source: string;
|
|
41
|
+
event_type: string;
|
|
42
|
+
timestamp?: string;
|
|
43
|
+
message?: string;
|
|
44
|
+
data?: unknown;
|
|
45
|
+
// Agent observability fields (send at top level, not in data)
|
|
46
|
+
session_id?: string;
|
|
47
|
+
hook?: ArgusHook;
|
|
48
|
+
tool_name?: string;
|
|
49
|
+
tool_use_id?: string;
|
|
50
|
+
status?: ArgusStatus;
|
|
51
|
+
agent_id?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface SendResult {
|
|
55
|
+
captured: boolean;
|
|
56
|
+
event_id?: number;
|
|
57
|
+
error?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface ArgusConfig {
|
|
61
|
+
host: string;
|
|
62
|
+
apiKey: string | null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ============================================================================
|
|
66
|
+
// Config Loading
|
|
67
|
+
// ============================================================================
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Load Argus configuration from config file
|
|
71
|
+
* Config: ~/.config/argus/config.toml
|
|
72
|
+
*
|
|
73
|
+
* @returns ArgusConfig with host URL and API key
|
|
74
|
+
*/
|
|
75
|
+
export function loadConfig(): ArgusConfig {
|
|
76
|
+
const configPath = join(process.env.HOME!, ".config", "argus", "config.toml");
|
|
77
|
+
const defaultHost = "http://127.0.0.1:8765";
|
|
78
|
+
|
|
79
|
+
if (!existsSync(configPath)) {
|
|
80
|
+
return { host: defaultHost, apiKey: null };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const content = readFileSync(configPath, "utf-8");
|
|
85
|
+
|
|
86
|
+
// Parse server.host (default: 127.0.0.1)
|
|
87
|
+
const hostMatch = content.match(/^\s*host\s*=\s*"([^"]+)"/m);
|
|
88
|
+
const host = hostMatch ? hostMatch[1] : "127.0.0.1";
|
|
89
|
+
|
|
90
|
+
// Parse server.port (default: 8765)
|
|
91
|
+
const portMatch = content.match(/^\s*port\s*=\s*(\d+)/m);
|
|
92
|
+
const port = portMatch ? portMatch[1] : "8765";
|
|
93
|
+
|
|
94
|
+
// Parse api_keys array, extract first key
|
|
95
|
+
const keysMatch = content.match(/api_keys\s*=\s*\[([^\]]+)\]/);
|
|
96
|
+
let apiKey: string | null = null;
|
|
97
|
+
if (keysMatch) {
|
|
98
|
+
const firstKey = keysMatch[1].match(/"([^"]+)"/);
|
|
99
|
+
apiKey = firstKey ? firstKey[1] : null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
host: `http://${host}:${port}`,
|
|
104
|
+
apiKey,
|
|
105
|
+
};
|
|
106
|
+
} catch {
|
|
107
|
+
return { host: defaultHost, apiKey: null };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ============================================================================
|
|
112
|
+
// Main API
|
|
113
|
+
// ============================================================================
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Send event to Argus
|
|
117
|
+
*
|
|
118
|
+
* @param event - The event to send
|
|
119
|
+
* @param apiKey - Argus API key
|
|
120
|
+
* @param host - Argus host URL (default: http://127.0.0.1:8765)
|
|
121
|
+
* @returns SendResult with captured status and event_id or error
|
|
122
|
+
*/
|
|
123
|
+
export async function sendEvent(
|
|
124
|
+
event: ArgusEvent,
|
|
125
|
+
apiKey: string,
|
|
126
|
+
host: string = "http://127.0.0.1:8765",
|
|
127
|
+
): Promise<SendResult> {
|
|
128
|
+
try {
|
|
129
|
+
// Add timestamp if missing
|
|
130
|
+
const eventWithTimestamp = {
|
|
131
|
+
...event,
|
|
132
|
+
timestamp: event.timestamp || new Date().toISOString(),
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const response = await fetch(`${host}/events`, {
|
|
136
|
+
method: "POST",
|
|
137
|
+
headers: {
|
|
138
|
+
"X-API-Key": apiKey,
|
|
139
|
+
"Content-Type": "application/json",
|
|
140
|
+
},
|
|
141
|
+
body: JSON.stringify(eventWithTimestamp),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (!response.ok) {
|
|
145
|
+
const text = await response.text();
|
|
146
|
+
return {
|
|
147
|
+
captured: false,
|
|
148
|
+
error: `HTTP ${response.status}: ${text}`,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const result = await response.json();
|
|
153
|
+
return {
|
|
154
|
+
captured: true,
|
|
155
|
+
event_id: result.event_id,
|
|
156
|
+
};
|
|
157
|
+
} catch (error) {
|
|
158
|
+
return {
|
|
159
|
+
captured: false,
|
|
160
|
+
error: `Connection failed: ${String(error)}`,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Send event using config from loadConfig()
|
|
167
|
+
* Convenience function that handles config loading
|
|
168
|
+
*
|
|
169
|
+
* @param event - The event to send
|
|
170
|
+
* @returns SendResult with captured status and event_id or error
|
|
171
|
+
*/
|
|
172
|
+
export async function send(event: ArgusEvent): Promise<SendResult> {
|
|
173
|
+
const config = loadConfig();
|
|
174
|
+
|
|
175
|
+
// Check environment overrides
|
|
176
|
+
const apiKey = process.env.ARGUS_API_KEY || config.apiKey;
|
|
177
|
+
const host = process.env.ARGUS_HOST || config.host;
|
|
178
|
+
|
|
179
|
+
if (!apiKey) {
|
|
180
|
+
return {
|
|
181
|
+
captured: false,
|
|
182
|
+
error: "API key not found (check ~/.config/argus/config.toml)",
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return sendEvent(event, apiKey, host);
|
|
187
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@voidwire/argus-send",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Send events to Argus observability platform",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.ts",
|
|
9
|
+
"./cli": "./cli.ts"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"argus-send": "./cli.ts"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"index.ts",
|
|
16
|
+
"cli.ts",
|
|
17
|
+
"lib/**/*.ts",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"dev": "bun run cli.ts"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"argus",
|
|
26
|
+
"observability",
|
|
27
|
+
"events",
|
|
28
|
+
"llcli"
|
|
29
|
+
],
|
|
30
|
+
"author": "nickpending <nickpending@users.noreply.github.com>",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/nickpending/llmcli-tools.git",
|
|
35
|
+
"directory": "packages/argus-send"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/nickpending/llmcli-tools/tree/main/packages/argus-send#readme",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/nickpending/llmcli-tools/issues"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"bun": ">=1.0.0"
|
|
43
|
+
}
|
|
44
|
+
}
|