@agentguard-run/spend 0.1.7 → 0.1.9
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/dist/adapters/postgres-store.d.ts +47 -0
- package/dist/adapters/postgres-store.d.ts.map +1 -0
- package/dist/adapters/postgres-store.js +122 -0
- package/dist/adapters/postgres-store.js.map +1 -0
- package/dist/adapters/redis-store.d.ts +53 -0
- package/dist/adapters/redis-store.d.ts.map +1 -0
- package/dist/adapters/redis-store.js +112 -0
- package/dist/adapters/redis-store.js.map +1 -0
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +5 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/serve.d.ts +10 -0
- package/dist/cli/serve.d.ts.map +1 -0
- package/dist/cli/serve.js +334 -0
- package/dist/cli/serve.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/integrations/langchain-callback.d.ts +42 -0
- package/dist/integrations/langchain-callback.d.ts.map +1 -0
- package/dist/integrations/langchain-callback.js +118 -0
- package/dist/integrations/langchain-callback.js.map +1 -0
- package/dist/store-memory.d.ts +7 -0
- package/dist/store-memory.d.ts.map +1 -1
- package/dist/store-memory.js +4 -3
- package/dist/store-memory.js.map +1 -1
- package/package.json +11 -3
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres-backed SpendStore + DecisionLogStore.
|
|
3
|
+
*
|
|
4
|
+
* Install: `npm install @agentguard-run/spend pg`
|
|
5
|
+
*
|
|
6
|
+
* UPSERT semantics for window spend. Append-only decision log table
|
|
7
|
+
* with UNIQUE(signer_fingerprint, sequence) for crash-safe chain ordering.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* import { Pool } from 'pg';
|
|
11
|
+
* import {
|
|
12
|
+
* PostgresSpendStore, PostgresDecisionLogStore, ensureSchema,
|
|
13
|
+
* } from '@agentguard-run/spend/dist/adapters/postgres-store';
|
|
14
|
+
*
|
|
15
|
+
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
16
|
+
* await ensureSchema(pool);
|
|
17
|
+
* const config = {
|
|
18
|
+
* policy,
|
|
19
|
+
* spendStore: new PostgresSpendStore(pool),
|
|
20
|
+
* logStore: new PostgresDecisionLogStore(pool),
|
|
21
|
+
* signingKeys: { privateKey, publicKey },
|
|
22
|
+
* };
|
|
23
|
+
*/
|
|
24
|
+
import type { SignedDecisionLogEntry, SpendStore, DecisionLogStore, SpendWindow } from '../types';
|
|
25
|
+
type PoolLike = {
|
|
26
|
+
query(text: string, params?: any[]): Promise<{
|
|
27
|
+
rows: any[];
|
|
28
|
+
rowCount: number | null;
|
|
29
|
+
}>;
|
|
30
|
+
};
|
|
31
|
+
/** Create the required tables/indexes if they don't exist. Idempotent. */
|
|
32
|
+
export declare function ensureSchema(pool: PoolLike): Promise<void>;
|
|
33
|
+
export declare class PostgresSpendStore implements SpendStore {
|
|
34
|
+
private pool;
|
|
35
|
+
constructor(pool: PoolLike);
|
|
36
|
+
getWindowSpend(scopeKey: string, window: SpendWindow): Promise<number>;
|
|
37
|
+
incrementWindowSpend(scopeKey: string, window: SpendWindow, cents: number): Promise<number>;
|
|
38
|
+
}
|
|
39
|
+
export declare class PostgresDecisionLogStore implements DecisionLogStore {
|
|
40
|
+
private pool;
|
|
41
|
+
constructor(pool: PoolLike);
|
|
42
|
+
append(entry: SignedDecisionLogEntry): Promise<void>;
|
|
43
|
+
getLatest(): Promise<SignedDecisionLogEntry | null>;
|
|
44
|
+
read(fromSequence: number, limit: number): Promise<SignedDecisionLogEntry[]>;
|
|
45
|
+
}
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=postgres-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres-store.d.ts","sourceRoot":"","sources":["../../src/adapters/postgres-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAKH,OAAO,KAAK,EAAE,sBAAsB,EAAE,UAAU,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAElG,KAAK,QAAQ,GAAG;IACd,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CACxF,CAAC;AA6BF,0EAA0E;AAC1E,wBAAsB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhE;AAED,qBAAa,kBAAmB,YAAW,UAAU;IACvC,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,QAAQ;IAC5B,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAQtE,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAYlG;AAED,qBAAa,wBAAyB,YAAW,gBAAgB;IACnD,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,QAAQ;IAC5B,MAAM,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAepD,SAAS,IAAI,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC;IAQnD,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC;CAQnF"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Postgres-backed SpendStore + DecisionLogStore.
|
|
4
|
+
*
|
|
5
|
+
* Install: `npm install @agentguard-run/spend pg`
|
|
6
|
+
*
|
|
7
|
+
* UPSERT semantics for window spend. Append-only decision log table
|
|
8
|
+
* with UNIQUE(signer_fingerprint, sequence) for crash-safe chain ordering.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* import { Pool } from 'pg';
|
|
12
|
+
* import {
|
|
13
|
+
* PostgresSpendStore, PostgresDecisionLogStore, ensureSchema,
|
|
14
|
+
* } from '@agentguard-run/spend/dist/adapters/postgres-store';
|
|
15
|
+
*
|
|
16
|
+
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
17
|
+
* await ensureSchema(pool);
|
|
18
|
+
* const config = {
|
|
19
|
+
* policy,
|
|
20
|
+
* spendStore: new PostgresSpendStore(pool),
|
|
21
|
+
* logStore: new PostgresDecisionLogStore(pool),
|
|
22
|
+
* signingKeys: { privateKey, publicKey },
|
|
23
|
+
* };
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.PostgresDecisionLogStore = exports.PostgresSpendStore = void 0;
|
|
27
|
+
exports.ensureSchema = ensureSchema;
|
|
28
|
+
const store_memory_1 = require("../store-memory");
|
|
29
|
+
function _nowMs() { return Date.now(); }
|
|
30
|
+
const SPEND_SCHEMA = `
|
|
31
|
+
CREATE TABLE IF NOT EXISTS agentguard_spend (
|
|
32
|
+
scope_key TEXT NOT NULL,
|
|
33
|
+
window TEXT NOT NULL,
|
|
34
|
+
window_start_ms BIGINT NOT NULL,
|
|
35
|
+
cents BIGINT NOT NULL DEFAULT 0,
|
|
36
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
37
|
+
PRIMARY KEY (scope_key, window, window_start_ms)
|
|
38
|
+
);
|
|
39
|
+
CREATE INDEX IF NOT EXISTS agentguard_spend_updated_idx ON agentguard_spend(updated_at);
|
|
40
|
+
`;
|
|
41
|
+
const LOG_SCHEMA = `
|
|
42
|
+
CREATE TABLE IF NOT EXISTS agentguard_decision_log (
|
|
43
|
+
id BIGSERIAL PRIMARY KEY,
|
|
44
|
+
sequence BIGINT NOT NULL,
|
|
45
|
+
signer_fingerprint TEXT NOT NULL,
|
|
46
|
+
entry_hash TEXT NOT NULL,
|
|
47
|
+
previous_hash TEXT NOT NULL,
|
|
48
|
+
signature TEXT NOT NULL,
|
|
49
|
+
decision_json JSONB NOT NULL,
|
|
50
|
+
appended_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
51
|
+
UNIQUE (signer_fingerprint, sequence)
|
|
52
|
+
);
|
|
53
|
+
CREATE INDEX IF NOT EXISTS agentguard_log_signer_idx ON agentguard_decision_log(signer_fingerprint, sequence DESC);
|
|
54
|
+
`;
|
|
55
|
+
/** Create the required tables/indexes if they don't exist. Idempotent. */
|
|
56
|
+
async function ensureSchema(pool) {
|
|
57
|
+
await pool.query(SPEND_SCHEMA);
|
|
58
|
+
await pool.query(LOG_SCHEMA);
|
|
59
|
+
}
|
|
60
|
+
class PostgresSpendStore {
|
|
61
|
+
pool;
|
|
62
|
+
constructor(pool) {
|
|
63
|
+
this.pool = pool;
|
|
64
|
+
}
|
|
65
|
+
async getWindowSpend(scopeKey, window) {
|
|
66
|
+
const startMs = (0, store_memory_1.windowStart)(window, _nowMs());
|
|
67
|
+
const r = await this.pool.query('SELECT cents FROM agentguard_spend WHERE scope_key=$1 AND window=$2 AND window_start_ms=$3', [scopeKey, window, startMs]);
|
|
68
|
+
return r.rows[0] ? Number(r.rows[0].cents) : 0;
|
|
69
|
+
}
|
|
70
|
+
async incrementWindowSpend(scopeKey, window, cents) {
|
|
71
|
+
const startMs = (0, store_memory_1.windowStart)(window, _nowMs());
|
|
72
|
+
const r = await this.pool.query(`INSERT INTO agentguard_spend (scope_key, window, window_start_ms, cents)
|
|
73
|
+
VALUES ($1, $2, $3, $4)
|
|
74
|
+
ON CONFLICT (scope_key, window, window_start_ms)
|
|
75
|
+
DO UPDATE SET cents = agentguard_spend.cents + EXCLUDED.cents, updated_at = NOW()
|
|
76
|
+
RETURNING cents`, [scopeKey, window, startMs, cents]);
|
|
77
|
+
return Number(r.rows[0].cents);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
exports.PostgresSpendStore = PostgresSpendStore;
|
|
81
|
+
class PostgresDecisionLogStore {
|
|
82
|
+
pool;
|
|
83
|
+
constructor(pool) {
|
|
84
|
+
this.pool = pool;
|
|
85
|
+
}
|
|
86
|
+
async append(entry) {
|
|
87
|
+
await this.pool.query(`INSERT INTO agentguard_decision_log
|
|
88
|
+
(sequence, signer_fingerprint, entry_hash, previous_hash, signature, decision_json)
|
|
89
|
+
VALUES ($1, $2, $3, $4, $5, $6::jsonb)`, [
|
|
90
|
+
entry.sequence,
|
|
91
|
+
entry.signerFingerprint,
|
|
92
|
+
entry.entryHash,
|
|
93
|
+
entry.previousHash,
|
|
94
|
+
entry.signature,
|
|
95
|
+
JSON.stringify(entry.decision),
|
|
96
|
+
]);
|
|
97
|
+
}
|
|
98
|
+
async getLatest() {
|
|
99
|
+
const r = await this.pool.query(`SELECT sequence, signer_fingerprint, entry_hash, previous_hash, signature, decision_json
|
|
100
|
+
FROM agentguard_decision_log ORDER BY id DESC LIMIT 1`);
|
|
101
|
+
if (!r.rows[0])
|
|
102
|
+
return null;
|
|
103
|
+
return rowToEntry(r.rows[0]);
|
|
104
|
+
}
|
|
105
|
+
async read(fromSequence, limit) {
|
|
106
|
+
const r = await this.pool.query(`SELECT sequence, signer_fingerprint, entry_hash, previous_hash, signature, decision_json
|
|
107
|
+
FROM agentguard_decision_log WHERE sequence >= $1 ORDER BY sequence ASC LIMIT $2`, [fromSequence, limit]);
|
|
108
|
+
return r.rows.map(rowToEntry);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
exports.PostgresDecisionLogStore = PostgresDecisionLogStore;
|
|
112
|
+
function rowToEntry(row) {
|
|
113
|
+
return {
|
|
114
|
+
sequence: Number(row.sequence),
|
|
115
|
+
signerFingerprint: row.signer_fingerprint,
|
|
116
|
+
entryHash: row.entry_hash,
|
|
117
|
+
previousHash: row.previous_hash,
|
|
118
|
+
signature: row.signature,
|
|
119
|
+
decision: row.decision_json,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=postgres-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres-store.js","sourceRoot":"","sources":["../../src/adapters/postgres-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redis-backed SpendStore + DecisionLogStore.
|
|
3
|
+
*
|
|
4
|
+
* Install: `npm install @agentguard-run/spend ioredis`
|
|
5
|
+
*
|
|
6
|
+
* Multi-process spend tracking via INCR semantics. Decision log stored
|
|
7
|
+
* on a Redis Stream for crash-safe ordered append.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* import Redis from 'ioredis';
|
|
11
|
+
* import { RedisSpendStore, RedisDecisionLogStore } from '@agentguard-run/spend/dist/adapters/redis-store';
|
|
12
|
+
*
|
|
13
|
+
* const redis = new Redis('redis://localhost:6379');
|
|
14
|
+
* const config = {
|
|
15
|
+
* policy,
|
|
16
|
+
* spendStore: new RedisSpendStore(redis, { keyPrefix: 'agentguard' }),
|
|
17
|
+
* logStore: new RedisDecisionLogStore(redis, { streamKey: 'agentguard:log' }),
|
|
18
|
+
* signingKeys: { privateKey, publicKey },
|
|
19
|
+
* };
|
|
20
|
+
*/
|
|
21
|
+
import type { SignedDecisionLogEntry, SpendStore, DecisionLogStore, SpendWindow } from '../types';
|
|
22
|
+
type RedisLike = {
|
|
23
|
+
get(key: string): Promise<string | null>;
|
|
24
|
+
incrby(key: string, increment: number): Promise<number>;
|
|
25
|
+
expire(key: string, seconds: number): Promise<number>;
|
|
26
|
+
xadd(key: string, ...args: any[]): Promise<string | null>;
|
|
27
|
+
xrevrange(key: string, end: string, start: string, ...args: any[]): Promise<any>;
|
|
28
|
+
xrange(key: string, start: string, end: string, ...args: any[]): Promise<any>;
|
|
29
|
+
};
|
|
30
|
+
export interface RedisSpendStoreOptions {
|
|
31
|
+
keyPrefix?: string;
|
|
32
|
+
}
|
|
33
|
+
export declare class RedisSpendStore implements SpendStore {
|
|
34
|
+
private redis;
|
|
35
|
+
private prefix;
|
|
36
|
+
constructor(redis: RedisLike, opts?: RedisSpendStoreOptions);
|
|
37
|
+
private key;
|
|
38
|
+
getWindowSpend(scopeKey: string, window: SpendWindow): Promise<number>;
|
|
39
|
+
incrementWindowSpend(scopeKey: string, window: SpendWindow, cents: number): Promise<number>;
|
|
40
|
+
}
|
|
41
|
+
export interface RedisDecisionLogStoreOptions {
|
|
42
|
+
streamKey?: string;
|
|
43
|
+
}
|
|
44
|
+
export declare class RedisDecisionLogStore implements DecisionLogStore {
|
|
45
|
+
private redis;
|
|
46
|
+
private stream;
|
|
47
|
+
constructor(redis: RedisLike, opts?: RedisDecisionLogStoreOptions);
|
|
48
|
+
append(entry: SignedDecisionLogEntry): Promise<void>;
|
|
49
|
+
getLatest(): Promise<SignedDecisionLogEntry | null>;
|
|
50
|
+
read(fromSequence: number, limit: number): Promise<SignedDecisionLogEntry[]>;
|
|
51
|
+
}
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=redis-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-store.d.ts","sourceRoot":"","sources":["../../src/adapters/redis-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,OAAO,KAAK,EAAE,sBAAsB,EAAE,UAAU,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAIlG,KAAK,SAAS,GAAG;IACf,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxD,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1D,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACjF,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;CAC/E,CAAC;AAUF,MAAM,WAAW,sBAAsB;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,eAAgB,YAAW,UAAU;IAEpC,OAAO,CAAC,KAAK;IADzB,OAAO,CAAC,MAAM,CAAS;gBACH,KAAK,EAAE,SAAS,EAAE,IAAI,GAAE,sBAA2B;IAGvE,OAAO,CAAC,GAAG;IAIL,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAItE,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAMlG;AAED,MAAM,WAAW,4BAA4B;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,qBAAsB,YAAW,gBAAgB;IAEhD,OAAO,CAAC,KAAK;IADzB,OAAO,CAAC,MAAM,CAAS;gBACH,KAAK,EAAE,SAAS,EAAE,IAAI,GAAE,4BAAiC;IAGvE,MAAM,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpD,SAAS,IAAI,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC;IAcnD,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC;CAsBnF"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Redis-backed SpendStore + DecisionLogStore.
|
|
4
|
+
*
|
|
5
|
+
* Install: `npm install @agentguard-run/spend ioredis`
|
|
6
|
+
*
|
|
7
|
+
* Multi-process spend tracking via INCR semantics. Decision log stored
|
|
8
|
+
* on a Redis Stream for crash-safe ordered append.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* import Redis from 'ioredis';
|
|
12
|
+
* import { RedisSpendStore, RedisDecisionLogStore } from '@agentguard-run/spend/dist/adapters/redis-store';
|
|
13
|
+
*
|
|
14
|
+
* const redis = new Redis('redis://localhost:6379');
|
|
15
|
+
* const config = {
|
|
16
|
+
* policy,
|
|
17
|
+
* spendStore: new RedisSpendStore(redis, { keyPrefix: 'agentguard' }),
|
|
18
|
+
* logStore: new RedisDecisionLogStore(redis, { streamKey: 'agentguard:log' }),
|
|
19
|
+
* signingKeys: { privateKey, publicKey },
|
|
20
|
+
* };
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.RedisDecisionLogStore = exports.RedisSpendStore = void 0;
|
|
24
|
+
const store_memory_1 = require("../store-memory");
|
|
25
|
+
function _nowMs() { return Date.now(); }
|
|
26
|
+
const WINDOW_EXPIRY_S = {
|
|
27
|
+
per_call: 60,
|
|
28
|
+
per_minute: 120,
|
|
29
|
+
per_hour: 7200,
|
|
30
|
+
per_day: 172800,
|
|
31
|
+
per_month: 5356800,
|
|
32
|
+
};
|
|
33
|
+
class RedisSpendStore {
|
|
34
|
+
redis;
|
|
35
|
+
prefix;
|
|
36
|
+
constructor(redis, opts = {}) {
|
|
37
|
+
this.redis = redis;
|
|
38
|
+
this.prefix = opts.keyPrefix ?? 'agentguard';
|
|
39
|
+
}
|
|
40
|
+
key(scopeKey, window) {
|
|
41
|
+
const startMs = (0, store_memory_1.windowStart)(window, _nowMs());
|
|
42
|
+
return `${this.prefix}:spend:${scopeKey}:${window}:${startMs}`;
|
|
43
|
+
}
|
|
44
|
+
async getWindowSpend(scopeKey, window) {
|
|
45
|
+
const v = await this.redis.get(this.key(scopeKey, window));
|
|
46
|
+
return v ? parseInt(v, 10) : 0;
|
|
47
|
+
}
|
|
48
|
+
async incrementWindowSpend(scopeKey, window, cents) {
|
|
49
|
+
const k = this.key(scopeKey, window);
|
|
50
|
+
const newVal = await this.redis.incrby(k, cents);
|
|
51
|
+
await this.redis.expire(k, WINDOW_EXPIRY_S[window] ?? 86400);
|
|
52
|
+
return newVal;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.RedisSpendStore = RedisSpendStore;
|
|
56
|
+
class RedisDecisionLogStore {
|
|
57
|
+
redis;
|
|
58
|
+
stream;
|
|
59
|
+
constructor(redis, opts = {}) {
|
|
60
|
+
this.redis = redis;
|
|
61
|
+
this.stream = opts.streamKey ?? 'agentguard:log';
|
|
62
|
+
}
|
|
63
|
+
async append(entry) {
|
|
64
|
+
const payload = JSON.stringify(entry);
|
|
65
|
+
await this.redis.xadd(this.stream, '*', 'd', payload);
|
|
66
|
+
}
|
|
67
|
+
async getLatest() {
|
|
68
|
+
try {
|
|
69
|
+
const entries = await this.redis.xrevrange(this.stream, '+', '-', 'COUNT', 1);
|
|
70
|
+
if (!entries || entries.length === 0)
|
|
71
|
+
return null;
|
|
72
|
+
// ioredis returns [[id, [field1, val1, field2, val2, ...]], ...]
|
|
73
|
+
const [, fields] = entries[0];
|
|
74
|
+
const dIdx = fields.indexOf('d');
|
|
75
|
+
if (dIdx < 0)
|
|
76
|
+
return null;
|
|
77
|
+
const raw = fields[dIdx + 1];
|
|
78
|
+
return JSON.parse(raw);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async read(fromSequence, limit) {
|
|
85
|
+
try {
|
|
86
|
+
const entries = await this.redis.xrange(this.stream, '-', '+', 'COUNT', limit * 2);
|
|
87
|
+
const out = [];
|
|
88
|
+
for (const [, fields] of entries) {
|
|
89
|
+
const dIdx = fields.indexOf('d');
|
|
90
|
+
if (dIdx < 0)
|
|
91
|
+
continue;
|
|
92
|
+
try {
|
|
93
|
+
const parsed = JSON.parse(fields[dIdx + 1]);
|
|
94
|
+
if (parsed.sequence >= fromSequence) {
|
|
95
|
+
out.push(parsed);
|
|
96
|
+
if (out.length >= limit)
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// skip malformed
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
exports.RedisDecisionLogStore = RedisDecisionLogStore;
|
|
112
|
+
//# sourceMappingURL=redis-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-store.js","sourceRoot":"","sources":["../../src/adapters/redis-store.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA8BH,wBAAsB,IAAI,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAyClF"}
|
package/dist/cli/main.js
CHANGED
|
@@ -55,6 +55,7 @@ commands:
|
|
|
55
55
|
init Scaffold a project (policy.yaml + quickstart + .gitignore).
|
|
56
56
|
doctor Check installation health and configuration.
|
|
57
57
|
explain Decode a signed receipt with cap math + signature breakdown.
|
|
58
|
+
serve Open a local dashboard at http://localhost:8787.
|
|
58
59
|
--version Show the installed SDK version.
|
|
59
60
|
--help Show this help.
|
|
60
61
|
|
|
@@ -99,6 +100,10 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
99
100
|
const { runExplain } = await Promise.resolve().then(() => __importStar(require('./explain')));
|
|
100
101
|
return runExplain(rest);
|
|
101
102
|
}
|
|
103
|
+
if (command === 'serve') {
|
|
104
|
+
const { runServe } = await Promise.resolve().then(() => __importStar(require('./serve')));
|
|
105
|
+
return runServe(rest);
|
|
106
|
+
}
|
|
102
107
|
process.stderr.write(`agentguard: unknown command '${command}'\n\n`);
|
|
103
108
|
process.stderr.write(HELP);
|
|
104
109
|
return 2;
|
package/dist/cli/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BH,oBAyCC;AArED,oCAAoD;AAEpD,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;CAwBZ,CAAC;AAEK,KAAK,UAAU,IAAI,CAAC,OAAiB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/D,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,cAAc,gCAAwB,EAAE,CAAC,CAAC;QACtD,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACxB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAE3B,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,MAAM,EAAE,OAAO,EAAE,GAAG,wDAAa,QAAQ,GAAC,CAAC;QAC3C,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IACD,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,MAAM,EAAE,SAAS,EAAE,GAAG,wDAAa,UAAU,GAAC,CAAC;QAC/C,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IACD,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,MAAM,EAAE,OAAO,EAAE,GAAG,wDAAa,QAAQ,GAAC,CAAC;QAC3C,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IACD,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,MAAM,EAAE,SAAS,EAAE,GAAG,wDAAa,UAAU,GAAC,CAAC;QAC/C,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IACD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,EAAE,UAAU,EAAE,GAAG,wDAAa,WAAW,GAAC,CAAC;QACjD,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IACD,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QACxB,MAAM,EAAE,QAAQ,EAAE,GAAG,wDAAa,SAAS,GAAC,CAAC;QAC7C,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,OAAO,OAAO,CAAC,CAAC;IACrE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,CAAC,CAAC;AACX,CAAC;AAED,8CAA8C;AAC9C,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IAC5B,IAAI,EAAE,CAAC,IAAI,CACT,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAC5B,CAAC,GAAG,EAAE,EAAE;QACN,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agentguard serve` — local dashboard at localhost:8787 (configurable).
|
|
3
|
+
*
|
|
4
|
+
* Aggregates per-tenant decision logs under ~/.agentguard/<tenant>/decisions.ndjson
|
|
5
|
+
* + ~/.agentguard/demo/decisions.ndjson and serves a single-page dashboard.
|
|
6
|
+
*
|
|
7
|
+
* No outbound network calls. Pure Node stdlib HTTP server.
|
|
8
|
+
*/
|
|
9
|
+
export declare function runServe(argv: string[]): Promise<number>;
|
|
10
|
+
//# sourceMappingURL=serve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/cli/serve.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAiLH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA8G9D"}
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `agentguard serve` — local dashboard at localhost:8787 (configurable).
|
|
4
|
+
*
|
|
5
|
+
* Aggregates per-tenant decision logs under ~/.agentguard/<tenant>/decisions.ndjson
|
|
6
|
+
* + ~/.agentguard/demo/decisions.ndjson and serves a single-page dashboard.
|
|
7
|
+
*
|
|
8
|
+
* No outbound network calls. Pure Node stdlib HTTP server.
|
|
9
|
+
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.runServe = runServe;
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const http = __importStar(require("http"));
|
|
47
|
+
const os = __importStar(require("os"));
|
|
48
|
+
const path = __importStar(require("path"));
|
|
49
|
+
const index_1 = require("../index");
|
|
50
|
+
const decision_log_1 = require("../decision-log");
|
|
51
|
+
const colors_1 = require("./colors");
|
|
52
|
+
const AGENTGUARD_HOME = path.join(os.homedir(), '.agentguard');
|
|
53
|
+
function scanLogFiles() {
|
|
54
|
+
if (!fs.existsSync(AGENTGUARD_HOME))
|
|
55
|
+
return [];
|
|
56
|
+
const out = [];
|
|
57
|
+
for (const entry of fs.readdirSync(AGENTGUARD_HOME, { withFileTypes: true })) {
|
|
58
|
+
if (entry.isDirectory()) {
|
|
59
|
+
const candidate = path.join(AGENTGUARD_HOME, entry.name, 'decisions.ndjson');
|
|
60
|
+
if (fs.existsSync(candidate))
|
|
61
|
+
out.push(candidate);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return out.sort();
|
|
65
|
+
}
|
|
66
|
+
function loadAllDecisions() {
|
|
67
|
+
const out = [];
|
|
68
|
+
for (const logPath of scanLogFiles()) {
|
|
69
|
+
const tenant = path.basename(path.dirname(logPath));
|
|
70
|
+
try {
|
|
71
|
+
const content = fs.readFileSync(logPath, 'utf-8');
|
|
72
|
+
for (const line of content.split('\n')) {
|
|
73
|
+
if (!line.trim())
|
|
74
|
+
continue;
|
|
75
|
+
try {
|
|
76
|
+
const d = JSON.parse(line);
|
|
77
|
+
d._tenant = tenant;
|
|
78
|
+
out.push(d);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// skip malformed line
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// skip unreadable file
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
function computeStats(decisions) {
|
|
92
|
+
const totalCalls = decisions.length;
|
|
93
|
+
const blockedCount = decisions.filter((d) => d.decision?.action === 'block').length;
|
|
94
|
+
const downgradeCount = decisions.filter((d) => d.decision?.action === 'downgrade').length;
|
|
95
|
+
const allowCount = totalCalls - blockedCount - downgradeCount;
|
|
96
|
+
const spentCents = decisions
|
|
97
|
+
.filter((d) => ['allow', 'downgrade'].includes(d.decision?.action))
|
|
98
|
+
.reduce((sum, d) => sum + (d.decision.projectedCents || 0), 0);
|
|
99
|
+
const savedCents = decisions
|
|
100
|
+
.filter((d) => d.decision?.action === 'block')
|
|
101
|
+
.reduce((sum, d) => sum + (d.decision.projectedCents || 0), 0);
|
|
102
|
+
const byAgent = {};
|
|
103
|
+
const byProvider = {};
|
|
104
|
+
for (const d of decisions) {
|
|
105
|
+
if (!['allow', 'downgrade'].includes(d.decision?.action))
|
|
106
|
+
continue;
|
|
107
|
+
const scopeKey = d.decision?.triggeredScopeKey || '';
|
|
108
|
+
let agentId = 'unknown';
|
|
109
|
+
for (const part of scopeKey.split('|')) {
|
|
110
|
+
if (part.startsWith('agentId=')) {
|
|
111
|
+
agentId = part.slice(8);
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
byAgent[agentId] = (byAgent[agentId] || 0) + (d.decision.projectedCents || 0);
|
|
116
|
+
const prov = d.decision.provider || 'unknown';
|
|
117
|
+
byProvider[prov] = (byProvider[prov] || 0) + (d.decision.projectedCents || 0);
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
totals: {
|
|
121
|
+
spent_cents: spentCents,
|
|
122
|
+
saved_cents: savedCents,
|
|
123
|
+
calls: totalCalls,
|
|
124
|
+
blocked: blockedCount,
|
|
125
|
+
downgraded: downgradeCount,
|
|
126
|
+
allowed: allowCount,
|
|
127
|
+
},
|
|
128
|
+
by_agent: Object.entries(byAgent).map(([k, v]) => ({ agent: k, spent_cents: v })).sort((a, b) => b.spent_cents - a.spent_cents),
|
|
129
|
+
by_provider: Object.entries(byProvider).map(([k, v]) => ({ provider: k, spent_cents: v })).sort((a, b) => b.spent_cents - a.spent_cents),
|
|
130
|
+
recent: decisions.slice(-50).reverse(),
|
|
131
|
+
version: index_1.AGENTGUARD_SPEND_VERSION,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
// Dashboard HTML — IDENTICAL to the Python version so users see the same UI
|
|
135
|
+
// whether they ran `pip install agentguard-spend` or `npm install -g`.
|
|
136
|
+
const DASHBOARD_HTML = `<!doctype html>
|
|
137
|
+
<html lang="en"><head><meta charset="utf-8" />
|
|
138
|
+
<title>AgentGuard Spend · Local Dashboard</title>
|
|
139
|
+
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
140
|
+
<style>
|
|
141
|
+
:root { --bg:#07101E; --surface:#0C1828; --surface-2:#0F1F35; --border:rgba(42,188,180,0.18); --border-dim:rgba(255,255,255,0.08); --teal:#2abcb4; --teal-glow:rgba(42,188,180,0.15); --text:#FFF; --text-2:#DCE8F0; --muted:#8AA0B3; --green:#4ade80; --amber:#F1C40F; --red:#FF6B6B; --code:#E6F0F5; }
|
|
142
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
143
|
+
html, body { background: var(--bg); color: var(--text-2); font-family: 'DM Sans', -apple-system, system-ui, sans-serif; font-size: 15px; min-height: 100vh; -webkit-font-smoothing: antialiased; }
|
|
144
|
+
a { color: var(--teal); text-decoration: none; } a:hover { text-decoration: underline; }
|
|
145
|
+
code, .mono { font-family: 'JetBrains Mono', monospace; font-size: 13px; }
|
|
146
|
+
.topbar { display: flex; align-items: center; justify-content: space-between; padding: 16px 32px; border-bottom: 1px solid var(--border-dim); background: rgba(7,16,30,0.92); backdrop-filter: blur(10px); position: sticky; top: 0; z-index: 50; }
|
|
147
|
+
.brand { display: inline-flex; align-items: center; gap: 12px; }
|
|
148
|
+
.brand .hex { width: 22px; height: 22px; background: var(--teal); color: var(--bg); display: inline-flex; align-items: center; justify-content: center; font-weight: 800; font-size: 13px; border-radius: 4px; }
|
|
149
|
+
.brand .name { font-family: 'Cormorant Garamond', Georgia, serif; font-size: 22px; color: var(--text); }
|
|
150
|
+
.brand .name .accent { color: var(--teal); }
|
|
151
|
+
.brand .tag { margin-left: 8px; font-family: 'JetBrains Mono', monospace; font-size: 10px; font-weight: 700; letter-spacing: 0.15em; text-transform: uppercase; color: var(--teal); padding: 3px 8px; border: 1px solid var(--border); border-radius: 3px; }
|
|
152
|
+
.refresh-btn { font-family: 'JetBrains Mono', monospace; font-size: 12px; color: var(--teal); background: transparent; border: 1px solid var(--teal); border-radius: 4px; padding: 7px 14px; cursor: pointer; }
|
|
153
|
+
.refresh-btn:hover { background: var(--teal-glow); }
|
|
154
|
+
.shell { padding: 32px; max-width: 1280px; margin: 0 auto; }
|
|
155
|
+
.stats-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 28px; }
|
|
156
|
+
@media (max-width: 800px) { .stats-row { grid-template-columns: repeat(2, 1fr); } }
|
|
157
|
+
.stat-card { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 18px 20px; }
|
|
158
|
+
.stat-card .label { font-family: 'JetBrains Mono', monospace; font-size: 10px; font-weight: 700; letter-spacing: 0.15em; text-transform: uppercase; color: var(--muted); margin-bottom: 8px; }
|
|
159
|
+
.stat-card .value { font-size: 28px; font-weight: 800; color: var(--text); font-variant-numeric: tabular-nums; letter-spacing: -0.02em; }
|
|
160
|
+
.stat-card.green .value { color: var(--green); }
|
|
161
|
+
.stat-card.red .value { color: var(--red); }
|
|
162
|
+
.stat-card.amber .value { color: var(--amber); }
|
|
163
|
+
.stat-card .sub { color: var(--muted); font-size: 12px; margin-top: 4px; }
|
|
164
|
+
.section { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 24px 26px; margin-bottom: 24px; }
|
|
165
|
+
.section h2 { font-family: 'JetBrains Mono', monospace; font-size: 11px; font-weight: 700; letter-spacing: 0.15em; text-transform: uppercase; color: var(--teal); margin-bottom: 16px; }
|
|
166
|
+
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
|
|
167
|
+
@media (max-width: 800px) { .two-col { grid-template-columns: 1fr; } }
|
|
168
|
+
.bar-row { display: flex; align-items: center; gap: 12px; padding: 8px 0; border-bottom: 1px solid var(--border-dim); font-size: 13.5px; }
|
|
169
|
+
.bar-row:last-child { border-bottom: none; }
|
|
170
|
+
.bar-row .name { width: 35%; color: var(--text); }
|
|
171
|
+
.bar-row .bar { flex: 1; height: 10px; background: var(--surface-2); border-radius: 2px; overflow: hidden; }
|
|
172
|
+
.bar-row .bar-fill { height: 100%; background: var(--teal); transition: width 0.4s; }
|
|
173
|
+
.bar-row .amount { width: 80px; text-align: right; color: var(--green); font-weight: 700; font-family: 'JetBrains Mono', monospace; font-size: 13px; }
|
|
174
|
+
table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
175
|
+
th { text-align: left; padding: 10px 12px; font-family: 'JetBrains Mono', monospace; font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); border-bottom: 1px solid var(--border); }
|
|
176
|
+
td { padding: 12px; border-bottom: 1px solid var(--border-dim); vertical-align: top; }
|
|
177
|
+
td.action-block { color: var(--red); font-weight: 700; }
|
|
178
|
+
td.action-downgrade { color: var(--amber); font-weight: 700; }
|
|
179
|
+
td.action-allow { color: var(--green); }
|
|
180
|
+
td.action-shadow { color: var(--muted); }
|
|
181
|
+
td .mono { color: var(--code); }
|
|
182
|
+
.pill { display: inline-block; padding: 2px 8px; border-radius: 3px; font-family: 'JetBrains Mono', monospace; font-size: 11px; background: var(--surface-2); color: var(--text-2); border: 1px solid var(--border-dim); }
|
|
183
|
+
.empty { text-align: center; padding: 56px 20px; color: var(--muted); font-size: 14px; }
|
|
184
|
+
.empty code { display: block; margin-top: 8px; font-size: 14px; color: var(--teal); }
|
|
185
|
+
.live-indicator { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--green); margin-right: 6px; animation: pulse 2s ease-in-out infinite; }
|
|
186
|
+
@keyframes pulse { 0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(74,222,128,0.4); } 50% { opacity: 0.7; box-shadow: 0 0 0 6px rgba(74,222,128,0); } }
|
|
187
|
+
</style></head><body>
|
|
188
|
+
<div class="topbar">
|
|
189
|
+
<div class="brand"><span class="hex">⬡</span><span class="name">Agent<span class="accent">Guard</span></span><span class="tag">Spend · Local Dashboard</span></div>
|
|
190
|
+
<div><span class="mono" style="color: var(--muted); margin-right: 16px;"><span class="live-indicator"></span>v__VERSION__ · localhost:__PORT__</span><button class="refresh-btn" onclick="loadStats()">Refresh</button></div>
|
|
191
|
+
</div>
|
|
192
|
+
<div class="shell">
|
|
193
|
+
<div class="stats-row">
|
|
194
|
+
<div class="stat-card green"><div class="label">Spent (all-time)</div><div class="value" id="spent">$0.00</div><div class="sub" id="spent-sub">across <span id="call-count">0</span> calls</div></div>
|
|
195
|
+
<div class="stat-card red"><div class="label">Saved by blocks</div><div class="value" id="saved">+$0.00</div><div class="sub"><span id="blocked-count">0</span> calls blocked</div></div>
|
|
196
|
+
<div class="stat-card amber"><div class="label">Downgraded</div><div class="value" id="downgraded">0</div><div class="sub">calls routed to cheaper model</div></div>
|
|
197
|
+
<div class="stat-card"><div class="label">Allowed</div><div class="value" id="allowed">0</div><div class="sub">policy-checked & passed</div></div>
|
|
198
|
+
</div>
|
|
199
|
+
<div class="two-col">
|
|
200
|
+
<div class="section"><h2>Spend by Agent</h2><div id="by-agent"></div></div>
|
|
201
|
+
<div class="section"><h2>Spend by Provider</h2><div id="by-provider"></div></div>
|
|
202
|
+
</div>
|
|
203
|
+
<div class="section">
|
|
204
|
+
<h2>Recent Decisions</h2>
|
|
205
|
+
<table><thead><tr><th style="width:80px">Action</th><th>Agent</th><th>Provider · Model</th><th style="width:120px;text-align:right">Amount</th><th style="width:120px">Receipt</th></tr></thead><tbody id="recent-rows"></tbody></table>
|
|
206
|
+
</div>
|
|
207
|
+
<div style="text-align: center; color: var(--muted); font-size: 12px; margin-top: 32px;">Local-only. No outbound network calls. Reads from <code class="mono">~/.agentguard/*/decisions.ndjson</code>.<br/>Source: <a href="https://agentguard.run">agentguard.run</a> · <a href="https://www.npmjs.com/package/@agentguard-run/spend">npm</a> · <a href="https://pypi.org/project/agentguard-spend/">pypi</a></div>
|
|
208
|
+
</div>
|
|
209
|
+
<script>
|
|
210
|
+
function formatCents(cents) { const sign = cents < 0 ? '-' : ''; const abs = Math.abs(Math.round(cents)); const dollars = Math.floor(abs / 100); const remainder = abs % 100; return sign + '$' + dollars.toLocaleString() + '.' + String(remainder).padStart(2, '0'); }
|
|
211
|
+
function renderBars(containerId, items, key) { const el = document.getElementById(containerId); if (!items.length) { el.innerHTML = '<div style="color:var(--muted);font-size:13px;padding:8px 0">no data yet</div>'; return; } const max = Math.max(...items.map(i => i.spent_cents)) || 1; el.innerHTML = items.slice(0, 8).map(i => '<div class="bar-row"><span class="name">' + i[key] + '</span><span class="bar"><span class="bar-fill" style="width:' + (i.spent_cents/max*100).toFixed(1) + '%"></span></span><span class="amount">' + formatCents(i.spent_cents) + '</span></div>').join(''); }
|
|
212
|
+
function renderRecent(items) { const tbody = document.getElementById('recent-rows'); if (!items.length) { tbody.innerHTML = '<tr><td colspan="5"><div class="empty">no decisions yet.<br>run <code>agentguard demo</code> to see a real signed receipt here.</div></td></tr>'; return; } tbody.innerHTML = items.map(d => { const dec = d.decision; const scope = dec.triggeredScopeKey || ''; const agentMatch = scope.match(/agentId=([^|]+)/); const agent = agentMatch ? agentMatch[1] : '<span style="color:var(--muted)">—</span>'; const action = dec.action || 'allow'; return '<tr><td class="action-' + action + '">' + action.toUpperCase() + '</td><td class="mono">' + agent + '</td><td><span class="pill">' + (dec.provider || '?') + '</span> ' + (dec.modelRequested || '') + '</td><td style="text-align:right" class="mono">' + formatCents(dec.projectedCents || 0) + '</td><td class="mono"><a href="#" onclick="return verifyReceipt(\\'' + d.entryHash + '\\')">' + d.entryHash.slice(0, 12) + '...</a></td></tr>'; }).join(''); }
|
|
213
|
+
async function verifyReceipt(hash) { try { const r = await fetch('/api/verify/' + hash); const d = await r.json(); alert(d.ok ? '✓ verified: signature valid, hash matches, chain intact' : '✗ verification failed: ' + (d.reason || 'unknown')); } catch (e) { alert('verify error: ' + e); } return false; }
|
|
214
|
+
async function loadStats() { try { const r = await fetch('/api/stats'); const s = await r.json(); document.getElementById('spent').textContent = formatCents(s.totals.spent_cents); document.getElementById('saved').textContent = '+' + formatCents(s.totals.saved_cents); document.getElementById('downgraded').textContent = s.totals.downgraded; document.getElementById('allowed').textContent = s.totals.allowed; document.getElementById('call-count').textContent = s.totals.calls; document.getElementById('blocked-count').textContent = s.totals.blocked; renderBars('by-agent', s.by_agent, 'agent'); renderBars('by-provider', s.by_provider, 'provider'); renderRecent(s.recent); } catch (e) { console.error('load failed:', e); } }
|
|
215
|
+
loadStats();
|
|
216
|
+
setInterval(loadStats, 5000);
|
|
217
|
+
</script></body></html>
|
|
218
|
+
`;
|
|
219
|
+
async function runServe(argv) {
|
|
220
|
+
let port = 8787;
|
|
221
|
+
let host = '127.0.0.1';
|
|
222
|
+
let noOpen = false;
|
|
223
|
+
for (let i = 0; i < argv.length; i++) {
|
|
224
|
+
const a = argv[i];
|
|
225
|
+
if (a === '--port')
|
|
226
|
+
port = parseInt(argv[++i] ?? '8787', 10);
|
|
227
|
+
else if (a === '--host')
|
|
228
|
+
host = argv[++i] ?? '127.0.0.1';
|
|
229
|
+
else if (a === '--no-open')
|
|
230
|
+
noOpen = true;
|
|
231
|
+
else if (a === '--help' || a === '-h') {
|
|
232
|
+
console.log('agentguard serve [--port 8787] [--host 127.0.0.1] [--no-open]');
|
|
233
|
+
return 0;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
console.log('');
|
|
237
|
+
console.log(' ' + (0, colors_1.banner)(index_1.AGENTGUARD_SPEND_VERSION));
|
|
238
|
+
console.log('');
|
|
239
|
+
console.log(' ' + (0, colors_1.cyanBold)('agentguard serve') + (0, colors_1.dim)(' · local dashboard'));
|
|
240
|
+
console.log('');
|
|
241
|
+
const logFiles = scanLogFiles();
|
|
242
|
+
if (logFiles.length === 0) {
|
|
243
|
+
console.log(` ${(0, colors_1.dim)('no decision logs found yet at ~/.agentguard/*/decisions.ndjson')}`);
|
|
244
|
+
console.log(` ${(0, colors_1.dim)('run')} ${(0, colors_1.greenBold)('agentguard demo')} ${(0, colors_1.dim)('to produce one, then refresh.')}`);
|
|
245
|
+
console.log('');
|
|
246
|
+
}
|
|
247
|
+
const server = http.createServer(async (req, res) => {
|
|
248
|
+
const url = (req.url || '/').split('?')[0];
|
|
249
|
+
const sendJSON = (status, payload) => {
|
|
250
|
+
const body = JSON.stringify(payload);
|
|
251
|
+
res.writeHead(status, {
|
|
252
|
+
'Content-Type': 'application/json',
|
|
253
|
+
'Content-Length': Buffer.byteLength(body).toString(),
|
|
254
|
+
'Cache-Control': 'no-store',
|
|
255
|
+
});
|
|
256
|
+
res.end(body);
|
|
257
|
+
};
|
|
258
|
+
const sendHTML = (html) => {
|
|
259
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store' });
|
|
260
|
+
res.end(html);
|
|
261
|
+
};
|
|
262
|
+
if (url === '/' || url === '/index.html') {
|
|
263
|
+
sendHTML(DASHBOARD_HTML.replace('__VERSION__', index_1.AGENTGUARD_SPEND_VERSION).replace('__PORT__', String(port)));
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
if (url === '/api/stats') {
|
|
267
|
+
sendJSON(200, computeStats(loadAllDecisions()));
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (url.startsWith('/api/verify/')) {
|
|
271
|
+
const entryHash = url.split('/').pop();
|
|
272
|
+
const decisions = loadAllDecisions();
|
|
273
|
+
const match = decisions.find((d) => d.entryHash === entryHash);
|
|
274
|
+
if (!match) {
|
|
275
|
+
sendJSON(404, { ok: false, reason: 'receipt not found' });
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
try {
|
|
279
|
+
const publicKeyHex = match.publicKeyHex;
|
|
280
|
+
if (!publicKeyHex) {
|
|
281
|
+
sendJSON(400, { ok: false, reason: 'no public key in stored receipt' });
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
const publicKey = Uint8Array.from(Buffer.from(publicKeyHex, 'hex'));
|
|
285
|
+
const entry = {
|
|
286
|
+
sequence: match.sequence,
|
|
287
|
+
entryHash: match.entryHash,
|
|
288
|
+
previousHash: match.previousHash,
|
|
289
|
+
signature: match.signature,
|
|
290
|
+
signerFingerprint: match.signerFingerprint,
|
|
291
|
+
decision: match.decision,
|
|
292
|
+
};
|
|
293
|
+
const ok = await (0, decision_log_1.verifyEntry)(entry, publicKey);
|
|
294
|
+
sendJSON(200, { ok: Boolean(ok) });
|
|
295
|
+
}
|
|
296
|
+
catch (e) {
|
|
297
|
+
sendJSON(500, { ok: false, reason: String(e?.message || e) });
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
sendJSON(404, { error: 'not found' });
|
|
302
|
+
});
|
|
303
|
+
const url = `http://${host}:${port}`;
|
|
304
|
+
server.listen(port, host, () => {
|
|
305
|
+
console.log(` ${(0, colors_1.green)('→')} dashboard: ${(0, colors_1.greenBold)(url)}`);
|
|
306
|
+
console.log(` ${(0, colors_1.dim)(' reading: ~/.agentguard/*/decisions.ndjson')}`);
|
|
307
|
+
console.log(` ${(0, colors_1.dim)(' press Ctrl+C to stop')}`);
|
|
308
|
+
console.log('');
|
|
309
|
+
if (!noOpen) {
|
|
310
|
+
setTimeout(() => {
|
|
311
|
+
try {
|
|
312
|
+
const { exec } = require('child_process');
|
|
313
|
+
const cmd = process.platform === 'darwin'
|
|
314
|
+
? `open "${url}"`
|
|
315
|
+
: process.platform === 'win32'
|
|
316
|
+
? `start "" "${url}"`
|
|
317
|
+
: `xdg-open "${url}"`;
|
|
318
|
+
exec(cmd);
|
|
319
|
+
}
|
|
320
|
+
catch { /* best-effort */ }
|
|
321
|
+
}, 400);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
process.on('SIGINT', () => {
|
|
325
|
+
console.log('');
|
|
326
|
+
console.log(` ${(0, colors_1.green)('✓')} dashboard stopped`);
|
|
327
|
+
console.log('');
|
|
328
|
+
server.close();
|
|
329
|
+
process.exit(0);
|
|
330
|
+
});
|
|
331
|
+
// Keep alive
|
|
332
|
+
return new Promise(() => { });
|
|
333
|
+
}
|
|
334
|
+
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/cli/serve.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export { canonicalJson, sha256Hex, computeEntryHash, computeSignerFingerprint, s
|
|
|
12
12
|
export { InMemorySpendStore } from './store-memory';
|
|
13
13
|
export { SpendGuard, withSpendGuard, AgentGuardBlockedError, type SpendGuardConfig, type OpenAIBindingOptions, } from './spend-guard';
|
|
14
14
|
export { DEFAULT_LOCALE, SUPPORTED_LOCALES, TRANSLATIONS, type SupportedLocale, resolveLocale, t, formatBlockedTrace, type BlockedTraceArgs, } from './i18n';
|
|
15
|
-
export declare const AGENTGUARD_SPEND_VERSION = "0.1.
|
|
15
|
+
export declare const AGENTGUARD_SPEND_VERSION = "0.1.9";
|
|
16
16
|
/** Patent marking. 35 U.S.C. § 287 constructive notice. */
|
|
17
17
|
export declare const PATENT_NOTICE: string;
|
|
18
18
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -46,7 +46,7 @@ Object.defineProperty(exports, "TRANSLATIONS", { enumerable: true, get: function
|
|
|
46
46
|
Object.defineProperty(exports, "resolveLocale", { enumerable: true, get: function () { return i18n_1.resolveLocale; } });
|
|
47
47
|
Object.defineProperty(exports, "t", { enumerable: true, get: function () { return i18n_1.t; } });
|
|
48
48
|
Object.defineProperty(exports, "formatBlockedTrace", { enumerable: true, get: function () { return i18n_1.formatBlockedTrace; } });
|
|
49
|
-
exports.AGENTGUARD_SPEND_VERSION = '0.1.
|
|
49
|
+
exports.AGENTGUARD_SPEND_VERSION = '0.1.9';
|
|
50
50
|
/** Patent marking. 35 U.S.C. § 287 constructive notice. */
|
|
51
51
|
exports.PATENT_NOTICE = 'Protected by U.S. patent-pending technology ' +
|
|
52
52
|
'(App. Nos. 63/983,615; 63/983,621; 63/983,843; 63/984,626; ' +
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LangChain.js callback handler that fires AgentGuard spend checks before
|
|
3
|
+
* every LLM call.
|
|
4
|
+
*
|
|
5
|
+
* Install: `npm install @agentguard-run/spend @langchain/core`
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* import { ChatOpenAI } from '@langchain/openai';
|
|
9
|
+
* import { AgentGuardSpendCallbackHandler } from '@agentguard-run/spend/dist/integrations/langchain-callback';
|
|
10
|
+
*
|
|
11
|
+
* const handler = new AgentGuardSpendCallbackHandler({
|
|
12
|
+
* dailyCapDollars: 20,
|
|
13
|
+
* agentId: 'my-langchain-agent',
|
|
14
|
+
* });
|
|
15
|
+
* const llm = new ChatOpenAI({ callbacks: [handler] });
|
|
16
|
+
* await llm.invoke('hello'); // throws AgentGuardBlockedError if cap exceeded
|
|
17
|
+
*/
|
|
18
|
+
export interface AgentGuardSpendCallbackHandlerOptions {
|
|
19
|
+
dailyCapDollars: number;
|
|
20
|
+
agentId?: string;
|
|
21
|
+
tenantId?: string;
|
|
22
|
+
softCapDollars?: number;
|
|
23
|
+
downgradeTo?: string;
|
|
24
|
+
perMinuteCapDollars?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* LangChain.js BaseCallbackHandler-compatible class. We declare it without
|
|
28
|
+
* extending the real BaseCallbackHandler (which would require a peer-dep
|
|
29
|
+
* import at module load), and let LangChain duck-type it via the standard
|
|
30
|
+
* callback method names.
|
|
31
|
+
*/
|
|
32
|
+
export declare class AgentGuardSpendCallbackHandler {
|
|
33
|
+
name: string;
|
|
34
|
+
private policy;
|
|
35
|
+
private scope;
|
|
36
|
+
private spendStore;
|
|
37
|
+
constructor(opts: AgentGuardSpendCallbackHandlerOptions);
|
|
38
|
+
handleChatModelStart(llm: any, messages: any[][], _runId: string, _parentRunId: string | undefined, extraParams?: Record<string, any>): Promise<void>;
|
|
39
|
+
handleLLMStart(_llm: any, prompts: string[], _runId: string, _parentRunId: string | undefined, extraParams?: Record<string, any>): Promise<void>;
|
|
40
|
+
private preflight;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=langchain-callback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"langchain-callback.d.ts","sourceRoot":"","sources":["../../src/integrations/langchain-callback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAcH,MAAM,WAAW,qCAAqC;IACpD,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAMD;;;;;GAKG;AACH,qBAAa,8BAA8B;IACzC,IAAI,SAAsB;IAC1B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,UAAU,CAAqB;gBAE3B,IAAI,EAAE,qCAAqC;IA2CjD,oBAAoB,CACxB,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,GAAG,EAAE,EAAE,EACjB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC;IAaV,cAAc,CAClB,IAAI,EAAE,GAAG,EACT,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC;YAWF,SAAS;CAiBxB"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* LangChain.js callback handler that fires AgentGuard spend checks before
|
|
4
|
+
* every LLM call.
|
|
5
|
+
*
|
|
6
|
+
* Install: `npm install @agentguard-run/spend @langchain/core`
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import { ChatOpenAI } from '@langchain/openai';
|
|
10
|
+
* import { AgentGuardSpendCallbackHandler } from '@agentguard-run/spend/dist/integrations/langchain-callback';
|
|
11
|
+
*
|
|
12
|
+
* const handler = new AgentGuardSpendCallbackHandler({
|
|
13
|
+
* dailyCapDollars: 20,
|
|
14
|
+
* agentId: 'my-langchain-agent',
|
|
15
|
+
* });
|
|
16
|
+
* const llm = new ChatOpenAI({ callbacks: [handler] });
|
|
17
|
+
* await llm.invoke('hello'); // throws AgentGuardBlockedError if cap exceeded
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.AgentGuardSpendCallbackHandler = void 0;
|
|
21
|
+
const policy_1 = require("../policy");
|
|
22
|
+
const store_memory_1 = require("../store-memory");
|
|
23
|
+
const cost_table_1 = require("../cost-table");
|
|
24
|
+
const spend_guard_1 = require("../spend-guard");
|
|
25
|
+
function dollarsToCents(amount) {
|
|
26
|
+
return Math.round(amount * 100);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* LangChain.js BaseCallbackHandler-compatible class. We declare it without
|
|
30
|
+
* extending the real BaseCallbackHandler (which would require a peer-dep
|
|
31
|
+
* import at module load), and let LangChain duck-type it via the standard
|
|
32
|
+
* callback method names.
|
|
33
|
+
*/
|
|
34
|
+
class AgentGuardSpendCallbackHandler {
|
|
35
|
+
name = 'agentguard_spend';
|
|
36
|
+
policy;
|
|
37
|
+
scope;
|
|
38
|
+
spendStore;
|
|
39
|
+
constructor(opts) {
|
|
40
|
+
const tenant = opts.tenantId ?? 'langchain-default';
|
|
41
|
+
const agent = opts.agentId ?? 'langchain-agent';
|
|
42
|
+
const caps = [];
|
|
43
|
+
if (opts.softCapDollars !== undefined && opts.downgradeTo !== undefined) {
|
|
44
|
+
caps.push({
|
|
45
|
+
amountCents: dollarsToCents(opts.softCapDollars),
|
|
46
|
+
window: 'per_day',
|
|
47
|
+
action: 'downgrade',
|
|
48
|
+
downgradeTo: opts.downgradeTo,
|
|
49
|
+
reason: `Soft daily cap, routing to ${opts.downgradeTo}`,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
caps.push({
|
|
53
|
+
amountCents: dollarsToCents(opts.dailyCapDollars),
|
|
54
|
+
window: 'per_day',
|
|
55
|
+
action: 'block',
|
|
56
|
+
reason: `Hard daily ceiling ($${opts.dailyCapDollars.toFixed(2)}/day)`,
|
|
57
|
+
});
|
|
58
|
+
if (opts.perMinuteCapDollars !== undefined) {
|
|
59
|
+
caps.push({
|
|
60
|
+
amountCents: dollarsToCents(opts.perMinuteCapDollars),
|
|
61
|
+
window: 'per_minute',
|
|
62
|
+
action: 'block',
|
|
63
|
+
reason: `Per-minute burst guard ($${opts.perMinuteCapDollars.toFixed(2)}/min)`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
this.policy = {
|
|
67
|
+
id: `langchain-${tenant}-v1`,
|
|
68
|
+
name: `LangChain policy (${tenant})`,
|
|
69
|
+
scope: { tenantId: tenant },
|
|
70
|
+
caps,
|
|
71
|
+
mode: 'enforce',
|
|
72
|
+
version: 1,
|
|
73
|
+
effectiveFrom: new Date().toISOString(),
|
|
74
|
+
};
|
|
75
|
+
this.scope = { tenantId: tenant, agentId: agent };
|
|
76
|
+
this.spendStore = new store_memory_1.InMemorySpendStore();
|
|
77
|
+
}
|
|
78
|
+
// LangChain callback method
|
|
79
|
+
async handleChatModelStart(llm, messages, _runId, _parentRunId, extraParams) {
|
|
80
|
+
const model = (extraParams?.invocation_params?.model
|
|
81
|
+
|| extraParams?.invocation_params?.model_name
|
|
82
|
+
|| llm?.id?.[llm.id.length - 1]
|
|
83
|
+
|| 'unknown');
|
|
84
|
+
const inputChars = JSON.stringify(messages).length;
|
|
85
|
+
await this.preflight({
|
|
86
|
+
model,
|
|
87
|
+
inputTokens: Math.max(1, Math.floor(inputChars / 4)),
|
|
88
|
+
outputTokens: extraParams?.invocation_params?.max_tokens || 1024,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async handleLLMStart(_llm, prompts, _runId, _parentRunId, extraParams) {
|
|
92
|
+
const model = (extraParams?.invocation_params?.model
|
|
93
|
+
|| extraParams?.invocation_params?.model_name
|
|
94
|
+
|| 'unknown');
|
|
95
|
+
await this.preflight({
|
|
96
|
+
model,
|
|
97
|
+
inputTokens: Math.max(1, Math.floor(prompts.join('').length / 4)),
|
|
98
|
+
outputTokens: extraParams?.invocation_params?.max_tokens || 1024,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
async preflight(args) {
|
|
102
|
+
const provider = (0, cost_table_1.inferProvider)(args.model);
|
|
103
|
+
const call = {
|
|
104
|
+
provider,
|
|
105
|
+
model: args.model,
|
|
106
|
+
inputTokens: args.inputTokens,
|
|
107
|
+
outputTokens: args.outputTokens,
|
|
108
|
+
scope: this.scope,
|
|
109
|
+
};
|
|
110
|
+
const decision = await (0, policy_1.evaluatePolicy)(this.policy, call, this.spendStore);
|
|
111
|
+
if (decision.action === 'block') {
|
|
112
|
+
// Optionally sign + persist before throwing. Skipped here for latency.
|
|
113
|
+
throw new spend_guard_1.AgentGuardBlockedError(decision, this.scope);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
exports.AgentGuardSpendCallbackHandler = AgentGuardSpendCallbackHandler;
|
|
118
|
+
//# sourceMappingURL=langchain-callback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"langchain-callback.js","sourceRoot":"","sources":["../../src/integrations/langchain-callback.ts"],"names":[],"mappings":"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"}
|
package/dist/store-memory.d.ts
CHANGED
|
@@ -12,6 +12,13 @@
|
|
|
12
12
|
* and an additional application filed May 2026).
|
|
13
13
|
*/
|
|
14
14
|
import type { SpendStore, SpendWindow } from './types';
|
|
15
|
+
export declare const WINDOW_DURATION_MS: Record<SpendWindow, number>;
|
|
16
|
+
/**
|
|
17
|
+
* Returns the millisecond timestamp at which a window of the given duration
|
|
18
|
+
* containing `nowMs` started. For fixed-grid windows we align to UTC epoch
|
|
19
|
+
* (so all processes agree on window boundaries without coordination).
|
|
20
|
+
*/
|
|
21
|
+
export declare function windowStart(window: SpendWindow, nowMs: number): number;
|
|
15
22
|
/**
|
|
16
23
|
* In-memory spend store. Thread-safe in single-process Node (we rely on the
|
|
17
24
|
* event loop's atomic execution between awaits; this store does no I/O).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-memory.d.ts","sourceRoot":"","sources":["../src/store-memory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"store-memory.d.ts","sourceRoot":"","sources":["../src/store-memory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AASvD,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAM1D,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAItE;AAED;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,UAAU;IACnD,OAAO,CAAC,KAAK,CAAkC;IAE/C,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,WAAW;IAYb,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAKtE,oBAAoB,CACxB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC;IAMlB,oCAAoC;IACpC,KAAK,IAAI,IAAI;CAGd"}
|
package/dist/store-memory.js
CHANGED
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
* and an additional application filed May 2026).
|
|
14
14
|
*/
|
|
15
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.InMemorySpendStore = void 0;
|
|
17
|
-
|
|
16
|
+
exports.InMemorySpendStore = exports.WINDOW_DURATION_MS = void 0;
|
|
17
|
+
exports.windowStart = windowStart;
|
|
18
|
+
exports.WINDOW_DURATION_MS = {
|
|
18
19
|
per_call: 0, // per_call windows reset on every read
|
|
19
20
|
per_minute: 60_000,
|
|
20
21
|
per_hour: 3_600_000,
|
|
@@ -27,7 +28,7 @@ const WINDOW_DURATION_MS = {
|
|
|
27
28
|
* (so all processes agree on window boundaries without coordination).
|
|
28
29
|
*/
|
|
29
30
|
function windowStart(window, nowMs) {
|
|
30
|
-
const dur = WINDOW_DURATION_MS[window];
|
|
31
|
+
const dur = exports.WINDOW_DURATION_MS[window];
|
|
31
32
|
if (dur === 0)
|
|
32
33
|
return nowMs; // per_call is trivial
|
|
33
34
|
return Math.floor(nowMs / dur) * dur;
|
package/dist/store-memory.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-memory.js","sourceRoot":"","sources":["../src/store-memory.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;
|
|
1
|
+
{"version":3,"file":"store-memory.js","sourceRoot":"","sources":["../src/store-memory.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;AAwBH,kCAIC;AAjBY,QAAA,kBAAkB,GAAgC;IAC7D,QAAQ,EAAE,CAAC,EAAE,uCAAuC;IACpD,UAAU,EAAE,MAAM;IAClB,QAAQ,EAAE,SAAS;IACnB,OAAO,EAAE,UAAU;IACnB,SAAS,EAAE,EAAE,GAAG,UAAU,EAAE,sEAAsE;CACnG,CAAC;AAEF;;;;GAIG;AACH,SAAgB,WAAW,CAAC,MAAmB,EAAE,KAAa;IAC5D,MAAM,GAAG,GAAG,0BAAkB,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,GAAG,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,sBAAsB;IACnD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAa,kBAAkB;IACrB,KAAK,GAAG,IAAI,GAAG,EAAuB,CAAC;IAEvC,GAAG,CAAC,QAAgB,EAAE,MAAmB;QAC/C,OAAO,GAAG,QAAQ,KAAK,MAAM,EAAE,CAAC;IAClC,CAAC;IAEO,WAAW,CAAC,QAAgB,EAAE,MAAmB,EAAE,KAAa;QACtE,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACrC,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACnC,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,aAAa,KAAK,aAAa,EAAE,CAAC;YAC1D,MAAM,KAAK,GAAgB,EAAE,KAAK,EAAE,CAAC,EAAE,aAAa,EAAE,aAAa,EAAE,CAAC;YACtE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;YACzB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,QAAgB,EAAE,MAAmB;QACxD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7D,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,oBAAoB,CACxB,QAAgB,EAChB,MAAmB,EACnB,KAAa;QAEb,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7D,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC;QACrB,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IAED,oCAAoC;IACpC,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF;AAtCD,gDAsCC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentguard-run/spend",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Local-runtime spend caps and capability-gated model routing for AI agents. Prompts, API keys, and signing keys stay inside the customer runtime. Zero data plane involvement.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -72,12 +72,20 @@
|
|
|
72
72
|
"peerDependencies": {
|
|
73
73
|
"openai": ">=5.0.0",
|
|
74
74
|
"@anthropic-ai/sdk": ">=0.30.0",
|
|
75
|
-
"@aws-sdk/client-bedrock-runtime": ">=3.600.0"
|
|
75
|
+
"@aws-sdk/client-bedrock-runtime": ">=3.600.0",
|
|
76
|
+
"ioredis": ">=5.0.0",
|
|
77
|
+
"pg": ">=8.10.0",
|
|
78
|
+
"@aws-sdk/client-kms": ">=3.600.0",
|
|
79
|
+
"@langchain/core": ">=0.3.0"
|
|
76
80
|
},
|
|
77
81
|
"peerDependenciesMeta": {
|
|
78
82
|
"openai": { "optional": true },
|
|
79
83
|
"@anthropic-ai/sdk": { "optional": true },
|
|
80
|
-
"@aws-sdk/client-bedrock-runtime": { "optional": true }
|
|
84
|
+
"@aws-sdk/client-bedrock-runtime": { "optional": true },
|
|
85
|
+
"ioredis": { "optional": true },
|
|
86
|
+
"pg": { "optional": true },
|
|
87
|
+
"@aws-sdk/client-kms": { "optional": true },
|
|
88
|
+
"@langchain/core": { "optional": true }
|
|
81
89
|
},
|
|
82
90
|
"devDependencies": {
|
|
83
91
|
"@types/node": "^20.0.0",
|