mortgram-hook 1.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/README.md +37 -0
- package/index.mjs +135 -0
- package/package.json +16 -0
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# mortgram-hook
|
|
2
|
+
|
|
3
|
+
**MORTGRAM Ghost Bridge** — Invisible real-time observability for your OpenClaw agents.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
openclaw hooks install mortgram-hook
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Configure
|
|
12
|
+
|
|
13
|
+
Set your MORTGRAM API key as an environment variable:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
export MORTGRAM_API_KEY=MG_LIVE_sk_your_key_here
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or add it to your OpenClaw config (`~/.openclaw/openclaw.json`).
|
|
20
|
+
|
|
21
|
+
## What it does
|
|
22
|
+
|
|
23
|
+
The Ghost Bridge hooks into your OpenClaw agent lifecycle and streams data to your MORTGRAM dashboard in real-time:
|
|
24
|
+
|
|
25
|
+
| Event | What's Tracked |
|
|
26
|
+
|-------|---------------|
|
|
27
|
+
| `agent:thought` | Latest thought, turn count |
|
|
28
|
+
| `tool:call` | Tool usage, efficiency score |
|
|
29
|
+
| `agent:error` | Error tracking |
|
|
30
|
+
| `session:usage` | Daily cost / token spend |
|
|
31
|
+
| `session:start/end` | Online/Offline status |
|
|
32
|
+
|
|
33
|
+
**Zero friction.** No wrappers, no extra processes, no code changes.
|
|
34
|
+
|
|
35
|
+
## Dashboard
|
|
36
|
+
|
|
37
|
+
View your fleet at [mortgram.com/dashboard](https://mortgram.com/dashboard)
|
package/index.mjs
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════
|
|
2
|
+
// MORTGRAM Ghost Bridge — Native OpenClaw Hook
|
|
3
|
+
// ═══════════════════════════════════════════════════════
|
|
4
|
+
// Installed via: openclaw hooks install mortgram-hook
|
|
5
|
+
// Zero-touch observability: no wrapper, no config, no friction.
|
|
6
|
+
//
|
|
7
|
+
// Environment Variables Required:
|
|
8
|
+
// MORTGRAM_API_KEY — Your MORTGRAM API key (from dashboard)
|
|
9
|
+
// MORTGRAM_API_URL — API endpoint (defaults to https://mortgram.com/api/ingest)
|
|
10
|
+
// MG_AGENT_ID — Agent identifier (defaults to "ghost-agent")
|
|
11
|
+
// ═══════════════════════════════════════════════════════
|
|
12
|
+
|
|
13
|
+
export const name = "mortgram-observer";
|
|
14
|
+
|
|
15
|
+
// Track session state for efficiency calculation
|
|
16
|
+
let totalTurns = 0;
|
|
17
|
+
let successfulTools = 0;
|
|
18
|
+
let latestThought = "Ghost Bridge Active";
|
|
19
|
+
let dailyCost = 0;
|
|
20
|
+
|
|
21
|
+
const API_KEY = process.env.MORTGRAM_API_KEY || process.env.MG_LIVE_KEY || "";
|
|
22
|
+
const API_URL = process.env.MORTGRAM_API_URL || "https://mortgram.com/api/ingest";
|
|
23
|
+
const AGENT_ID = process.env.MG_AGENT_ID || "ghost-agent";
|
|
24
|
+
|
|
25
|
+
// Silently POST to the MORTGRAM ingest endpoint
|
|
26
|
+
async function send(type, content, metadata = {}) {
|
|
27
|
+
if (!API_KEY) return;
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
await fetch(API_URL, {
|
|
31
|
+
method: "POST",
|
|
32
|
+
headers: {
|
|
33
|
+
"Content-Type": "application/json",
|
|
34
|
+
"x-mortgram-token": API_KEY
|
|
35
|
+
},
|
|
36
|
+
body: JSON.stringify({
|
|
37
|
+
agent_id: AGENT_ID,
|
|
38
|
+
type,
|
|
39
|
+
content,
|
|
40
|
+
metadata: {
|
|
41
|
+
...metadata,
|
|
42
|
+
daily_cost: dailyCost,
|
|
43
|
+
efficiency: totalTurns > 0 ? (successfulTools / totalTurns) : 0,
|
|
44
|
+
latest_thought: latestThought,
|
|
45
|
+
status: "online",
|
|
46
|
+
timestamp: new Date().toISOString()
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
});
|
|
50
|
+
} catch (_) {
|
|
51
|
+
// Silent fail — never disrupt the agent
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ─── The Main Event Handler ───
|
|
56
|
+
// OpenClaw fires events for every lifecycle step.
|
|
57
|
+
// We intercept them invisibly and stream to MORTGRAM.
|
|
58
|
+
export async function onEvent({ event, data }) {
|
|
59
|
+
switch (event) {
|
|
60
|
+
// ── Agent Thought / Reasoning ──
|
|
61
|
+
case "agent:thought":
|
|
62
|
+
case "agent:reasoning":
|
|
63
|
+
totalTurns++;
|
|
64
|
+
latestThought = data?.text || data?.content || "Thinking...";
|
|
65
|
+
await send("thought", latestThought, { event });
|
|
66
|
+
break;
|
|
67
|
+
|
|
68
|
+
// ── Tool Calls (Actions) ──
|
|
69
|
+
case "tool:call":
|
|
70
|
+
case "tool:result":
|
|
71
|
+
successfulTools++;
|
|
72
|
+
await send("action", data?.name || data?.tool || "Tool Call", {
|
|
73
|
+
event,
|
|
74
|
+
tool: data?.name,
|
|
75
|
+
args: data?.args
|
|
76
|
+
});
|
|
77
|
+
break;
|
|
78
|
+
|
|
79
|
+
// ── Errors ──
|
|
80
|
+
case "agent:error":
|
|
81
|
+
case "tool:error":
|
|
82
|
+
if (successfulTools > 0) successfulTools--;
|
|
83
|
+
await send("error", data?.message || data?.text || "Error occurred", {
|
|
84
|
+
event,
|
|
85
|
+
stack: data?.stack
|
|
86
|
+
});
|
|
87
|
+
break;
|
|
88
|
+
|
|
89
|
+
// ── Session Usage / Cost ──
|
|
90
|
+
case "session:usage":
|
|
91
|
+
case "session:cost":
|
|
92
|
+
dailyCost = data?.total_cost || data?.cost || dailyCost;
|
|
93
|
+
await send("cost_update", `Daily spend: $${dailyCost.toFixed(4)}`, {
|
|
94
|
+
event,
|
|
95
|
+
daily_cost: dailyCost,
|
|
96
|
+
tokens: data?.tokens
|
|
97
|
+
});
|
|
98
|
+
break;
|
|
99
|
+
|
|
100
|
+
// ── Session Start ──
|
|
101
|
+
case "session:start":
|
|
102
|
+
case "agent:start":
|
|
103
|
+
totalTurns = 0;
|
|
104
|
+
successfulTools = 0;
|
|
105
|
+
dailyCost = 0;
|
|
106
|
+
latestThought = "Session started";
|
|
107
|
+
await send("system", "Ghost Bridge: Session started", {
|
|
108
|
+
event,
|
|
109
|
+
status: "online"
|
|
110
|
+
});
|
|
111
|
+
break;
|
|
112
|
+
|
|
113
|
+
// ── Session End ──
|
|
114
|
+
case "session:end":
|
|
115
|
+
case "agent:end":
|
|
116
|
+
await send("system", "Ghost Bridge: Session ended", {
|
|
117
|
+
event,
|
|
118
|
+
status: "offline",
|
|
119
|
+
final_cost: dailyCost,
|
|
120
|
+
final_efficiency: totalTurns > 0 ? (successfulTools / totalTurns) : 0
|
|
121
|
+
});
|
|
122
|
+
break;
|
|
123
|
+
|
|
124
|
+
// ── Agent Response / Output ──
|
|
125
|
+
case "agent:response":
|
|
126
|
+
case "agent:message":
|
|
127
|
+
latestThought = data?.text?.slice(0, 200) || "Response generated";
|
|
128
|
+
await send("thought", latestThought, { event });
|
|
129
|
+
break;
|
|
130
|
+
|
|
131
|
+
default:
|
|
132
|
+
// Skip unrecognized events silently
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mortgram-hook",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MORTGRAM Ghost Bridge — Invisible observability for OpenClaw agents",
|
|
5
|
+
"main": "index.mjs",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"openclaw",
|
|
9
|
+
"mortgram",
|
|
10
|
+
"observability",
|
|
11
|
+
"agent",
|
|
12
|
+
"hook"
|
|
13
|
+
],
|
|
14
|
+
"author": "MORTGRAM",
|
|
15
|
+
"license": "MIT"
|
|
16
|
+
}
|