@analytix402/openclaw-skill 0.1.1 → 0.1.4

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/SKILL.md CHANGED
@@ -16,16 +16,19 @@ Analytix402 gives your OpenClaw agent financial visibility and guardrails. Track
16
16
 
17
17
  ## Configuration
18
18
 
19
+ Add these to your OpenClaw agent's `.env` file:
20
+
19
21
  ```yaml
20
22
  # Required
21
23
  ANALYTIX402_API_KEY: ax_live_your_key_here
22
24
 
23
25
  # Optional
24
- ANALYTIX402_AGENT_ID: my-openclaw-agent
25
- ANALYTIX402_BASE_URL: https://analytix402.com
26
- ANALYTIX402_DAILY_BUDGET: 50.00
27
- ANALYTIX402_PER_CALL_LIMIT: 5.00
28
- ANALYTIX402_TRACK_LLM: true
26
+ ANALYTIX402_AGENT_ID: my-openclaw-agent # Unique ID shown in dashboard
27
+ ANALYTIX402_AGENT_NAME: My OpenClaw Agent # Display name (defaults to agent ID)
28
+ ANALYTIX402_BASE_URL: https://analytix402.com # Custom endpoint
29
+ ANALYTIX402_DAILY_BUDGET: 50.00 # Daily spend limit in USD (0 = unlimited)
30
+ ANALYTIX402_PER_CALL_LIMIT: 5.00 # Per-call cap in USD (0 = unlimited)
31
+ ANALYTIX402_TRACK_LLM: true # LLM tracking (enabled by default)
29
32
  ```
30
33
 
31
34
  ## Tools
@@ -42,6 +45,14 @@ Check remaining budget before making an expensive API call.
42
45
  ### analytix402_flag_purchase
43
46
  Flag a potential duplicate or unnecessary purchase for review.
44
47
 
48
+ ## Verify it works
49
+
50
+ 1. Start your OpenClaw agent — the skill connects automatically on load
51
+ 2. Open the [Agents dashboard](https://analytix402.com/agents.html) — your agent should appear within seconds
52
+ 3. The agent stays visible with a heartbeat every 60 seconds
53
+
54
+ No need to call any tools first. The skill registers itself immediately on startup.
55
+
45
56
  ## Tags
46
57
 
47
58
  monitoring, analytics, budget, x402, payments, observability, cost-tracking
package/dist/index.js CHANGED
@@ -54,11 +54,20 @@ function getClient() {
54
54
  apiKey: API_KEY,
55
55
  baseUrl: BASE_URL,
56
56
  agentId: AGENT_ID,
57
- agentName: AGENT_NAME
57
+ agentName: AGENT_NAME,
58
+ autoConnect: true,
59
+ heartbeatIntervalMs: 6e4
58
60
  });
59
61
  }
60
62
  return client;
61
63
  }
64
+ if (API_KEY) {
65
+ try {
66
+ getClient();
67
+ } catch (err) {
68
+ console.warn("[Analytix402] Failed to connect on startup:", err instanceof Error ? err.message : err);
69
+ }
70
+ }
62
71
  async function apiRequest(method, path, body) {
63
72
  const url = `${BASE_URL}/api${path}`;
64
73
  const headers = {
package/dist/index.mjs CHANGED
@@ -23,11 +23,20 @@ function getClient() {
23
23
  apiKey: API_KEY,
24
24
  baseUrl: BASE_URL,
25
25
  agentId: AGENT_ID,
26
- agentName: AGENT_NAME
26
+ agentName: AGENT_NAME,
27
+ autoConnect: true,
28
+ heartbeatIntervalMs: 6e4
27
29
  });
28
30
  }
29
31
  return client;
30
32
  }
33
+ if (API_KEY) {
34
+ try {
35
+ getClient();
36
+ } catch (err) {
37
+ console.warn("[Analytix402] Failed to connect on startup:", err instanceof Error ? err.message : err);
38
+ }
39
+ }
31
40
  async function apiRequest(method, path, body) {
32
41
  const url = `${BASE_URL}/api${path}`;
33
42
  const headers = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@analytix402/openclaw-skill",
3
- "version": "0.1.1",
3
+ "version": "0.1.4",
4
4
  "description": "Analytix402 skill for OpenClaw — monitor, control, and optimize your AI agent's spend",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -72,6 +72,11 @@
72
72
  "shutdown"
73
73
  ]
74
74
  },
75
+ "files": [
76
+ "dist",
77
+ "SKILL.md",
78
+ "README.md"
79
+ ],
75
80
  "keywords": [
76
81
  "openclaw",
77
82
  "openclaw-skill",
@@ -86,7 +91,7 @@
86
91
  "author": "Analytix402",
87
92
  "license": "MIT",
88
93
  "dependencies": {
89
- "@analytix402/sdk": "^0.1.1"
94
+ "@analytix402/sdk": "^0.1.2"
90
95
  },
91
96
  "devDependencies": {
92
97
  "@types/node": "^20.10.0",
package/Skill/SKILL.md DELETED
@@ -1,47 +0,0 @@
1
- # Analytix402
2
-
3
- Monitor, control, and optimize your AI agent's API spend and LLM costs in real-time.
4
-
5
- ## Description
6
-
7
- Analytix402 gives your OpenClaw agent financial visibility and guardrails. Track every API call, LLM invocation, and x402 payment your agent makes. Set budget limits, detect duplicate purchases, and get alerts before costs spiral.
8
-
9
- **What it does:**
10
- - Tracks all outbound API calls and x402 payments automatically
11
- - Monitors LLM token usage and costs across OpenAI, Anthropic, and other providers
12
- - Enforces daily budget limits and per-call spend caps
13
- - Detects duplicate API purchases to prevent waste
14
- - Sends heartbeats so you know your agent is alive and healthy
15
- - Provides a real-time dashboard at analytix402.com
16
-
17
- ## Configuration
18
-
19
- ```yaml
20
- # Required
21
- ANALYTIX402_API_KEY: ax_live_your_key_here
22
-
23
- # Optional
24
- ANALYTIX402_AGENT_ID: my-openclaw-agent
25
- ANALYTIX402_BASE_URL: https://analytix402.com
26
- ANALYTIX402_DAILY_BUDGET: 50.00
27
- ANALYTIX402_PER_CALL_LIMIT: 5.00
28
- ANALYTIX402_TRACK_LLM: true
29
- ```
30
-
31
- ## Tools
32
-
33
- ### analytix402_spend_report
34
- Get a summary of your agent's spend — total cost, breakdown by API and LLM provider, and efficiency score.
35
-
36
- ### analytix402_set_budget
37
- Set or update the daily budget limit for this agent session.
38
-
39
- ### analytix402_check_budget
40
- Check remaining budget before making an expensive API call.
41
-
42
- ### analytix402_flag_purchase
43
- Flag a potential duplicate or unnecessary purchase for review.
44
-
45
- ## Tags
46
-
47
- monitoring, analytics, budget, x402, payments, observability, cost-tracking
package/src/index.ts DELETED
@@ -1,335 +0,0 @@
1
- /**
2
- * Analytix402 OpenClaw Skill
3
- *
4
- * Provides AI agent spend tracking, budget enforcement,
5
- * and observability for OpenClaw agents.
6
- *
7
- * Integrates with the Analytix402 platform to give operators
8
- * real-time visibility into what their agents are spending.
9
- */
10
-
11
- import { createClient } from '@analytix402/sdk';
12
- import type { Analytix402Client } from '@analytix402/sdk';
13
-
14
- // ============================================================
15
- // Configuration
16
- // ============================================================
17
-
18
- const API_KEY = process.env.ANALYTIX402_API_KEY || '';
19
- const BASE_URL = (process.env.ANALYTIX402_BASE_URL || 'https://analytix402.com').replace(/\/$/, '');
20
- const AGENT_ID = process.env.ANALYTIX402_AGENT_ID || `openclaw-${Date.now()}`;
21
- const AGENT_NAME = process.env.ANALYTIX402_AGENT_NAME || 'OpenClaw Agent';
22
- const DAILY_BUDGET = parseFloat(process.env.ANALYTIX402_DAILY_BUDGET || '0') || 0;
23
- const PER_CALL_LIMIT = parseFloat(process.env.ANALYTIX402_PER_CALL_LIMIT || '0') || 0;
24
- const TRACK_LLM = process.env.ANALYTIX402_TRACK_LLM !== 'false';
25
-
26
- // ============================================================
27
- // State
28
- // ============================================================
29
-
30
- let client: Analytix402Client | null = null;
31
- let sessionSpend = 0;
32
- let sessionCalls = 0;
33
- let dailySpend = 0;
34
- const dailySpendDate = new Date().toISOString().slice(0, 10);
35
-
36
- function getClient(): Analytix402Client {
37
- if (!client) {
38
- if (!API_KEY) {
39
- throw new Error(
40
- 'ANALYTIX402_API_KEY is not set. Add it to your OpenClaw skill config.'
41
- );
42
- }
43
- client = createClient({
44
- apiKey: API_KEY,
45
- baseUrl: BASE_URL,
46
- agentId: AGENT_ID,
47
- agentName: AGENT_NAME,
48
- });
49
- }
50
- return client;
51
- }
52
-
53
- // ============================================================
54
- // API helper (for dashboard queries)
55
- // ============================================================
56
-
57
- async function apiRequest(method: string, path: string, body?: unknown): Promise<unknown> {
58
- const url = `${BASE_URL}/api${path}`;
59
- const headers: Record<string, string> = {
60
- 'Content-Type': 'application/json',
61
- 'Authorization': `Bearer ${API_KEY}`,
62
- 'X-API-Key': API_KEY,
63
- };
64
-
65
- const opts: RequestInit = { method, headers };
66
- if (body && method !== 'GET') {
67
- opts.body = JSON.stringify(body);
68
- }
69
-
70
- const res = await fetch(url, opts);
71
- const text = await res.text();
72
-
73
- let data;
74
- try {
75
- data = JSON.parse(text);
76
- } catch {
77
- data = { raw: text };
78
- }
79
-
80
- if (!res.ok) {
81
- throw new Error(`Analytix402 API ${res.status}: ${(data as Record<string, string>).error || text}`);
82
- }
83
-
84
- return data;
85
- }
86
-
87
- // ============================================================
88
- // Tool: analytix402_spend_report
89
- // ============================================================
90
-
91
- export async function analytix402_spend_report(): Promise<string> {
92
- const c = getClient();
93
-
94
- // Send a heartbeat while we're at it
95
- c.heartbeat('healthy', { sessionCalls, sessionSpend });
96
-
97
- try {
98
- const overview = await apiRequest('GET', '/agents/overview');
99
- const insights = await apiRequest('GET', '/agents/insights');
100
-
101
- return JSON.stringify({
102
- session: {
103
- totalCalls: sessionCalls,
104
- totalSpend: `$${sessionSpend.toFixed(4)}`,
105
- dailySpend: `$${dailySpend.toFixed(4)}`,
106
- dailyBudget: DAILY_BUDGET > 0 ? `$${DAILY_BUDGET.toFixed(2)}` : 'unlimited',
107
- budgetRemaining: DAILY_BUDGET > 0
108
- ? `$${(DAILY_BUDGET - dailySpend).toFixed(4)}`
109
- : 'unlimited',
110
- },
111
- platform: overview,
112
- insights,
113
- }, null, 2);
114
- } catch (error) {
115
- return JSON.stringify({
116
- session: {
117
- totalCalls: sessionCalls,
118
- totalSpend: `$${sessionSpend.toFixed(4)}`,
119
- dailySpend: `$${dailySpend.toFixed(4)}`,
120
- dailyBudget: DAILY_BUDGET > 0 ? `$${DAILY_BUDGET.toFixed(2)}` : 'unlimited',
121
- },
122
- error: error instanceof Error ? error.message : String(error),
123
- }, null, 2);
124
- }
125
- }
126
-
127
- // ============================================================
128
- // Tool: analytix402_set_budget
129
- // ============================================================
130
-
131
- export interface SetBudgetParams {
132
- daily_limit?: number;
133
- per_call_limit?: number;
134
- }
135
-
136
- export function analytix402_set_budget(params: SetBudgetParams): string {
137
- const updates: string[] = [];
138
-
139
- if (params.daily_limit !== undefined && params.daily_limit >= 0) {
140
- // Update the runtime budget (persists for this session)
141
- Object.defineProperty(globalThis, '__ax402_daily_budget', {
142
- value: params.daily_limit,
143
- writable: true,
144
- configurable: true,
145
- });
146
- updates.push(`Daily budget set to $${params.daily_limit.toFixed(2)}`);
147
- }
148
-
149
- if (params.per_call_limit !== undefined && params.per_call_limit >= 0) {
150
- Object.defineProperty(globalThis, '__ax402_per_call_limit', {
151
- value: params.per_call_limit,
152
- writable: true,
153
- configurable: true,
154
- });
155
- updates.push(`Per-call limit set to $${params.per_call_limit.toFixed(2)}`);
156
- }
157
-
158
- if (updates.length === 0) {
159
- return 'No budget parameters provided. Use daily_limit and/or per_call_limit.';
160
- }
161
-
162
- return updates.join('. ') + '.';
163
- }
164
-
165
- // ============================================================
166
- // Tool: analytix402_check_budget
167
- // ============================================================
168
-
169
- export interface CheckBudgetParams {
170
- estimated_cost?: number;
171
- }
172
-
173
- export function analytix402_check_budget(params: CheckBudgetParams): string {
174
- const budget = DAILY_BUDGET ||
175
- ((globalThis as Record<string, unknown>).__ax402_daily_budget as number) || 0;
176
- const callLimit = PER_CALL_LIMIT ||
177
- ((globalThis as Record<string, unknown>).__ax402_per_call_limit as number) || 0;
178
- const estimated = params.estimated_cost || 0;
179
-
180
- const remaining = budget > 0 ? budget - dailySpend : Infinity;
181
- const allowed = (budget === 0 || remaining >= estimated) &&
182
- (callLimit === 0 || estimated <= callLimit);
183
-
184
- return JSON.stringify({
185
- allowed,
186
- dailyBudget: budget > 0 ? `$${budget.toFixed(2)}` : 'unlimited',
187
- dailySpent: `$${dailySpend.toFixed(4)}`,
188
- remaining: budget > 0 ? `$${remaining.toFixed(4)}` : 'unlimited',
189
- estimatedCost: `$${estimated.toFixed(4)}`,
190
- perCallLimit: callLimit > 0 ? `$${callLimit.toFixed(2)}` : 'unlimited',
191
- withinPerCallLimit: callLimit === 0 || estimated <= callLimit,
192
- withinDailyBudget: budget === 0 || remaining >= estimated,
193
- }, null, 2);
194
- }
195
-
196
- // ============================================================
197
- // Tool: analytix402_flag_purchase
198
- // ============================================================
199
-
200
- export interface FlagPurchaseParams {
201
- url: string;
202
- reason?: string;
203
- estimated_cost?: number;
204
- }
205
-
206
- export function analytix402_flag_purchase(params: FlagPurchaseParams): string {
207
- const c = getClient();
208
-
209
- c.track({
210
- type: 'request',
211
- method: 'FLAG',
212
- path: params.url,
213
- endpoint: params.url,
214
- statusCode: 0,
215
- responseTimeMs: 0,
216
- timestamp: new Date().toISOString(),
217
- agentId: AGENT_ID,
218
- metadata: {
219
- flagged: true,
220
- reason: params.reason || 'Potential duplicate or unnecessary purchase',
221
- estimatedCost: params.estimated_cost,
222
- },
223
- });
224
-
225
- return `Flagged: ${params.url} — ${params.reason || 'potential duplicate purchase'}. This will appear in your Analytix402 dashboard for review.`;
226
- }
227
-
228
- // ============================================================
229
- // Hooks: auto-track LLM and API calls
230
- // ============================================================
231
-
232
- /**
233
- * Call this after any LLM invocation to track usage.
234
- * OpenClaw skills can hook into the agent lifecycle.
235
- */
236
- export function trackLLMCall(params: {
237
- model: string;
238
- provider: string;
239
- inputTokens: number;
240
- outputTokens: number;
241
- costUsd?: number;
242
- durationMs?: number;
243
- taskId?: string;
244
- }): void {
245
- if (!TRACK_LLM) return;
246
-
247
- const c = getClient();
248
- const cost = params.costUsd || 0;
249
-
250
- c.trackLLM({
251
- model: params.model,
252
- provider: params.provider,
253
- inputTokens: params.inputTokens,
254
- outputTokens: params.outputTokens,
255
- costUsd: params.costUsd,
256
- durationMs: params.durationMs,
257
- taskId: params.taskId,
258
- });
259
-
260
- sessionSpend += cost;
261
- sessionCalls += 1;
262
-
263
- // Reset daily spend if date changed
264
- const today = new Date().toISOString().slice(0, 10);
265
- if (today !== dailySpendDate) {
266
- dailySpend = 0;
267
- }
268
- dailySpend += cost;
269
- }
270
-
271
- /**
272
- * Call this after any API call to track spend.
273
- */
274
- export function trackAPICall(params: {
275
- method: string;
276
- url: string;
277
- statusCode: number;
278
- responseTimeMs: number;
279
- paymentAmount?: number;
280
- paymentCurrency?: string;
281
- txHash?: string;
282
- }): void {
283
- const c = getClient();
284
-
285
- const payment = params.paymentAmount
286
- ? {
287
- amount: String(params.paymentAmount),
288
- currency: params.paymentCurrency || 'USDC',
289
- wallet: '',
290
- status: 'success' as const,
291
- txHash: params.txHash,
292
- }
293
- : undefined;
294
-
295
- c.track({
296
- type: 'request',
297
- method: params.method,
298
- path: params.url,
299
- endpoint: params.url,
300
- statusCode: params.statusCode,
301
- responseTimeMs: params.responseTimeMs,
302
- timestamp: new Date().toISOString(),
303
- agentId: AGENT_ID,
304
- payment,
305
- });
306
-
307
- if (params.paymentAmount) {
308
- sessionSpend += params.paymentAmount;
309
- dailySpend += params.paymentAmount;
310
- }
311
- sessionCalls += 1;
312
- }
313
-
314
- /**
315
- * Send a heartbeat — call periodically to show agent is alive.
316
- */
317
- export function sendHeartbeat(status: 'healthy' | 'degraded' | 'error' = 'healthy'): void {
318
- const c = getClient();
319
- c.heartbeat(status, {
320
- sessionCalls,
321
- sessionSpend,
322
- dailySpend,
323
- uptime: process.uptime(),
324
- });
325
- }
326
-
327
- /**
328
- * Gracefully shutdown — flush all pending events.
329
- */
330
- export async function shutdown(): Promise<void> {
331
- if (client) {
332
- await client.shutdown();
333
- client = null;
334
- }
335
- }
package/tsconfig.json DELETED
@@ -1,21 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "NodeNext",
5
- "moduleResolution": "nodenext",
6
- "lib": ["ES2020", "DOM"],
7
- "declaration": true,
8
- "declarationMap": true,
9
- "sourceMap": true,
10
- "outDir": "./dist",
11
- "rootDir": "./src",
12
- "strict": true,
13
- "esModuleInterop": true,
14
- "skipLibCheck": true,
15
- "forceConsistentCasingInFileNames": true,
16
- "resolveJsonModule": true,
17
- "isolatedModules": true
18
- },
19
- "include": ["src/**/*"],
20
- "exclude": ["node_modules", "dist"]
21
- }