@grindfree/guard 0.0.0-stage → 0.1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 GrindFree / Rob
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 CHANGED
@@ -1,3 +1,125 @@
1
- # Temporary Holding Version
1
+ # @grindfree/guard
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Drop-in guards that screen **[GrindFree](https://grindfree.net)** x402 agent-security APIs **before** an agent acts, then enforce `allow | review | deny`. Payments use the **caller’s** Base USDC wallet via the official [`@x402/fetch`](https://www.npmjs.com/package/@x402/fetch) / Python `x402` clients. This repo never ships or accepts GrindFree keys.
4
+
5
+ | Service | Endpoint | Price | What it checks |
6
+ |---|---|---|---|
7
+ | **PromptShield** | `POST https://promptshield.grindfree.net/v1/screen` · `/v1/clean` | $0.002 | Prompt-injection screen + HTML→LLM clean text |
8
+ | **CrawlSafe** | `POST https://crawlsafe.grindfree.net/v1/check` | $0.002 | robots.txt / X-Robots-Tag crawl gate (not malware scanning) |
9
+ | **ToolGuard** | `POST https://toolguard.grindfree.net/v1/evaluate` | $0.002 | Pre-tool-call `allow \| review \| deny` vs owner policy |
10
+ | **CommandGuard** | `POST https://commandguard.grindfree.net/v1/command` · `/v1/sql` | $0.001 | Shell + SQL check: `allow \| warn \| block` |
11
+ | **MemoryGuard** | `POST https://memoryguard.grindfree.net/v1/screen` | $0.002 | Content before it is written to agent memory |
12
+ | **WireShield** | `POST https://wireshield.grindfree.net/v1/scan` | $0.002 | Outbound leak check on agent output |
13
+ | **DocShield** | `POST https://docshield.grindfree.net/v1/scan` | $0.004 | Hidden instructions in PDF / Office / HTML / Markdown |
14
+
15
+ Network: Base (`eip155:8453`). The remote API is advisory — **your adapter must enforce the decision**. This package does that: `deny` / `block` / high risk throws `GuardDenied` and does not run the action; `review` / `warn` / medium risk throws `GuardReviewRequired` unless you set `onReview: "allow"`. Transport failures **fail closed** (`onError: "deny"` by default).
16
+
17
+ Also available: the MCP server [`@grindfree/agent-security-mcp`](https://github.com/ronin93/grindfree-agent-security) (`npx -y @grindfree/agent-security-mcp`).
18
+
19
+ Contact: [rob@grindfree.net](mailto:rob@grindfree.net) · [@grindfreelabs](https://x.com/grindfreelabs)
20
+
21
+ ---
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ npm i @grindfree/guard
27
+ # until the npm publish is approved, install from git:
28
+ # npm i github:ronin93/grindfree-guard
29
+ ```
30
+
31
+ ```bash
32
+ pip install 'grindfree-guard[x402]'
33
+ # until PyPI is published: pip install -e 'python/[x402]'
34
+ ```
35
+
36
+ Set the **caller’s** funded Base USDC key (never commit it):
37
+
38
+ ```bash
39
+ export X402_PRIVATE_KEY=0xYOUR_BASE_USDC_PRIVATE_KEY
40
+ # PRIVATE_KEY and EVM_PRIVATE_KEY are also accepted
41
+ ```
42
+
43
+ ## Quickstart (TypeScript)
44
+
45
+ ```ts
46
+ import { createGuard, GuardDenied, GuardReviewRequired } from "@grindfree/guard";
47
+
48
+ const guard = createGuard({
49
+ policy: { allow_tools: ["http.fetch"], allow_hosts: ["api.example.com"], require_https: true },
50
+ });
51
+
52
+ const { value } = await guard.guardToolCall(
53
+ { tool: "http.fetch", destination: { host: "api.example.com", scheme: "https", port: 443 }, arg_classes: ["url"] },
54
+ () => fetch("https://api.example.com/items").then((r) => r.json()),
55
+ );
56
+
57
+ // Shell: CommandGuard runs first. A wipe like `rm -rf $HOME` throws GuardDenied and is not executed.
58
+ await guard.guardCommand({ command: "ls -la" }, () => runShell("ls -la"));
59
+ ```
60
+
61
+ `createGuard({ fetch })` (JS) or `create_guard(post=...)` (Python) injects a mock for tests — unit tests in this repo never make a funded call.
62
+
63
+ ### Verdicts
64
+
65
+ | Service result | Normalized `verdict` | Default handling |
66
+ |---|---|---|
67
+ | ToolGuard `allow`; CommandGuard `allow`; PromptShield `low`; CrawlSafe `crawl_allowed: true` | `allow` | run the action |
68
+ | ToolGuard `review`; CommandGuard `warn`; PromptShield `medium`; Memory/Wire/Doc `review` | `review` | throw `GuardReviewRequired` (or set `onReview: "allow"` / `"deny"` / a callback) |
69
+ | ToolGuard `deny`; CommandGuard `block`; PromptShield `high`; CrawlSafe not allowed; Memory/Wire/Doc `block` | `deny` | throw `GuardDenied`, do not run the action |
70
+ | Timeout / HTTP error | (fail closed) | throw `GuardDenied` unless `onError: "throw"` or `"allow"` |
71
+
72
+ ## Framework examples
73
+
74
+ Copy-paste starters (wallet still comes from `X402_PRIVATE_KEY`):
75
+
76
+ - [`examples/basic.ts`](examples/basic.ts) — `guardToolCall` + `guardCommand`
77
+ - [`examples/langchain-js/guard-tools.ts`](examples/langchain-js/guard-tools.ts) — wrap LangChain JS tools
78
+ - [`examples/langchain-py/guard_tools.py`](examples/langchain-py/guard_tools.py) — LangChain Python wrapper + callback
79
+ - [`examples/openai-agents/guardrails.ts`](examples/openai-agents/guardrails.ts) — OpenAI Agents SDK input/output guardrails
80
+ - [`examples/mcp-client/wrap-mcp.ts`](examples/mcp-client/wrap-mcp.ts) — wrap `client.callTool` (CommandGuard for shell/SQL tools, ToolGuard otherwise)
81
+
82
+ ```ts
83
+ import { wrapMcpClient } from "@grindfree/guard";
84
+ const client = wrapMcpClient(mcpClient, { policy: { allow_tools: ["mcp.search"] } });
85
+ // Shell/SQL-named tools go through CommandGuard; everything else through ToolGuard.
86
+ ```
87
+
88
+ Python twin (`guard_tool_call`, `guard_command`, `require_safe_prompt`, …) lives in [`python/`](python/).
89
+
90
+ ## API (JS)
91
+
92
+ ```ts
93
+ const guard = createGuard({
94
+ privateKey, // optional; otherwise env
95
+ fetch, // optional mock / pre-wrapped paid fetch
96
+ policy, // default owner policy
97
+ onReview: "throw", // or "allow" | "deny" | async (d) => "allow"
98
+ onError: "deny", // or "throw" | "allow"
99
+ timeoutMs: 15_000,
100
+ });
101
+
102
+ await guard.guardToolCall(call, fn);
103
+ guard.wrapTool(name, fn, classify?);
104
+ await guard.guardCommand({ command }, fn);
105
+ await guard.guardSql({ sql }, fn);
106
+ await guard.requireSafePrompt({ content, format });
107
+ await guard.requireCrawlAllowed({ url });
108
+ await guard.requireSafeMemory({ content, source });
109
+ await guard.requireSafeOutput({ content, format });
110
+ await guard.requireSafeDocument({ content, format });
111
+ // plus evaluateTool / checkCommand / screenPrompt / … for inspect-only
112
+ ```
113
+
114
+ ## Honest limits
115
+
116
+ - Heuristic / policy layers, not a full security program.
117
+ - CrawlSafe is not malware, phishing, or legal advice.
118
+ - Deterministic rule packs can miss novel phrasing. Decisions are advisory at the API; **this library enforces them in-process** and cannot stop a caller who bypasses it.
119
+ - DocShield parses structure and text; it does not OCR images. Legacy `.doc` / `.xls` / `.ppt` return 415 and are not charged.
120
+ - Missing ToolGuard policy → `review`.
121
+ - Unit tests mock payment. Do not point tests at the live paid endpoints without a separate spend approval.
122
+
123
+ ## License
124
+
125
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,18 @@
1
+ export type PaidFetch = typeof fetch;
2
+ export type ClientOptions = {
3
+ /** Caller wallet. Falls back to X402_PRIVATE_KEY, PRIVATE_KEY, or EVM_PRIVATE_KEY. */
4
+ privateKey?: string;
5
+ /**
6
+ * Injected fetch. Use this in tests (mocked, never pays) or to supply a
7
+ * pre-wrapped x402 fetch. When set, no private key is read or required.
8
+ */
9
+ fetch?: PaidFetch;
10
+ timeoutMs?: number;
11
+ };
12
+ export declare function readPrivateKey(explicit?: string): string | undefined;
13
+ export declare function createPaidFetch(opts?: ClientOptions): PaidFetch;
14
+ export declare function postJson(paidFetch: PaidFetch, url: string, body: unknown, opts?: {
15
+ timeoutMs?: number;
16
+ service: string;
17
+ }): Promise<Record<string, unknown>>;
18
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,SAAS,GAAG,OAAO,KAAK,CAAC;AAErC,MAAM,MAAM,aAAa,GAAG;IAC1B,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEpE;AAED,wBAAgB,eAAe,CAAC,IAAI,GAAE,aAAkB,GAAG,SAAS,CAenE;AAED,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,SAAS,EACpB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,OAAO,EACb,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAA2B,GACrE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAoClC"}
package/dist/client.js ADDED
@@ -0,0 +1,65 @@
1
+ import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
2
+ import { ExactEvmScheme } from "@x402/evm";
3
+ import { privateKeyToAccount } from "viem/accounts";
4
+ import { GuardConfigError, GuardError } from "./errors.js";
5
+ import { NETWORK } from "./services.js";
6
+ const KEY_RE = /^0x[0-9a-fA-F]{64}$/;
7
+ const DEFAULT_TIMEOUT_MS = 15_000;
8
+ export function readPrivateKey(explicit) {
9
+ return explicit || process.env.X402_PRIVATE_KEY || process.env.PRIVATE_KEY || process.env.EVM_PRIVATE_KEY;
10
+ }
11
+ export function createPaidFetch(opts = {}) {
12
+ if (opts.fetch)
13
+ return opts.fetch;
14
+ const pk = readPrivateKey(opts.privateKey);
15
+ if (!pk) {
16
+ throw new GuardConfigError("Missing X402_PRIVATE_KEY (or PRIVATE_KEY / EVM_PRIVATE_KEY). Set the caller's Base USDC wallet key. GrindFree never supplies keys. For tests, pass { fetch } with a mock.");
17
+ }
18
+ if (!KEY_RE.test(pk)) {
19
+ throw new GuardConfigError("X402_PRIVATE_KEY / PRIVATE_KEY / EVM_PRIVATE_KEY must be 0x + 64 hex characters (caller wallet only).");
20
+ }
21
+ const account = privateKeyToAccount(pk);
22
+ return wrapFetchWithPaymentFromConfig(fetch, {
23
+ schemes: [{ network: NETWORK, client: new ExactEvmScheme(account) }],
24
+ });
25
+ }
26
+ export async function postJson(paidFetch, url, body, opts = { service: "unknown" }) {
27
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
28
+ const controller = new AbortController();
29
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
30
+ let res;
31
+ try {
32
+ res = await paidFetch(url, {
33
+ method: "POST",
34
+ headers: { "content-type": "application/json", accept: "application/json" },
35
+ body: JSON.stringify(body ?? {}),
36
+ signal: controller.signal,
37
+ });
38
+ }
39
+ catch (err) {
40
+ const aborted = err instanceof Error && err.name === "AbortError";
41
+ throw new GuardError(aborted ? `Request timed out after ${timeoutMs}ms` : err instanceof Error ? err.message : String(err), {
42
+ service: opts.service,
43
+ });
44
+ }
45
+ finally {
46
+ clearTimeout(timer);
47
+ }
48
+ const text = await res.text();
49
+ let json;
50
+ try {
51
+ json = text ? JSON.parse(text) : {};
52
+ }
53
+ catch {
54
+ json = { raw: text.slice(0, 2000) };
55
+ }
56
+ if (!res.ok) {
57
+ throw new GuardError(`${opts.service} HTTP ${res.status}`, {
58
+ service: opts.service,
59
+ status: res.status,
60
+ body: json,
61
+ });
62
+ }
63
+ return json;
64
+ }
65
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { ServiceName } from "./services.js";
2
+ /** Unified verdict used by every GrindFree check. */
3
+ export type Verdict = "allow" | "review" | "deny";
4
+ export type ReviewMode = "throw" | "allow" | "deny";
5
+ export type ErrorMode = "deny" | "throw" | "allow";
6
+ export interface NormalizedDecision {
7
+ /** allow = proceed; review = pause for a human (CommandGuard "warn", medium risk); deny = do not proceed (block / high risk). */
8
+ verdict: Verdict;
9
+ service: ServiceName;
10
+ endpoint: string;
11
+ /** Raw service decision when present (allow/review/deny/warn/block). */
12
+ rawDecision?: string;
13
+ /** PromptShield risk when present. */
14
+ risk?: "low" | "medium" | "high";
15
+ reasons: string[];
16
+ findings: unknown[];
17
+ advice?: string;
18
+ sanitized?: string;
19
+ safeText?: string;
20
+ crawlAllowed?: boolean;
21
+ raw: Record<string, unknown>;
22
+ }
23
+ export type EnforceOptions = {
24
+ onReview?: ReviewMode | ((d: NormalizedDecision) => ReviewMode | Promise<ReviewMode>);
25
+ onError?: ErrorMode;
26
+ };
27
+ export declare function normalizeDecision(service: ServiceName, endpoint: string, raw: Record<string, unknown>): NormalizedDecision;
28
+ /**
29
+ * Apply allow/review/deny policy. Deny and (by default) review throw.
30
+ * Timeouts and transport errors should be turned into GuardError first,
31
+ * then passed through enforceError.
32
+ */
33
+ export declare function enforce(decision: NormalizedDecision, opts?: EnforceOptions): Promise<NormalizedDecision>;
34
+ export declare function enforceError(err: unknown, service: string, opts?: EnforceOptions): never;
35
+ //# sourceMappingURL=decisions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decisions.d.ts","sourceRoot":"","sources":["../src/decisions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGjD,qDAAqD;AACrD,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAElD,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;AACpD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAEnD,MAAM,WAAW,kBAAkB;IACjC,iIAAiI;IACjI,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,IAAI,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACjC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC9B;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC,EAAE,kBAAkB,KAAK,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IACtF,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB,CAAC;AAEF,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,kBAAkB,CAoCpB;AA4CD;;;;GAIG;AACH,wBAAsB,OAAO,CAC3B,QAAQ,EAAE,kBAAkB,EAC5B,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,kBAAkB,CAAC,CAQ7B;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,KAAK,CAyB5F"}
@@ -0,0 +1,131 @@
1
+ import { GuardDenied, GuardError, GuardReviewRequired } from "./errors.js";
2
+ export function normalizeDecision(service, endpoint, raw) {
3
+ const findings = Array.isArray(raw.findings) ? raw.findings : [];
4
+ const reasons = collectReasons(raw, findings);
5
+ const sanitized = asString(raw.sanitized);
6
+ const safeText = asString(raw.safe_text) ?? asString(raw.safeText);
7
+ const advice = asString(raw.advice);
8
+ const rawDecision = typeof raw.decision === "string" ? raw.decision.toLowerCase() : undefined;
9
+ const risk = asRisk(raw.risk);
10
+ let verdict;
11
+ if (service === "promptshield" && endpoint === "screen") {
12
+ verdict = risk === "high" ? "deny" : risk === "medium" ? "review" : "allow";
13
+ }
14
+ else if (service === "crawlsafe") {
15
+ const allowed = raw.crawl_allowed === true || raw.crawlAllowed === true;
16
+ verdict = allowed ? "allow" : "deny";
17
+ }
18
+ else {
19
+ verdict = mapRawDecision(rawDecision);
20
+ }
21
+ const crawlAllowed = service === "crawlsafe" ? raw.crawl_allowed === true || raw.crawlAllowed === true : undefined;
22
+ return {
23
+ verdict,
24
+ service,
25
+ endpoint,
26
+ rawDecision,
27
+ risk,
28
+ reasons,
29
+ findings,
30
+ advice,
31
+ sanitized,
32
+ safeText,
33
+ crawlAllowed,
34
+ raw,
35
+ };
36
+ }
37
+ function mapRawDecision(raw) {
38
+ if (!raw)
39
+ return "review";
40
+ if (raw === "allow")
41
+ return "allow";
42
+ if (raw === "review" || raw === "warn")
43
+ return "review";
44
+ if (raw === "deny" || raw === "block")
45
+ return "deny";
46
+ return "review";
47
+ }
48
+ function collectReasons(raw, findings) {
49
+ const out = [];
50
+ if (Array.isArray(raw.reasons)) {
51
+ for (const r of raw.reasons) {
52
+ if (typeof r === "string")
53
+ out.push(r);
54
+ else if (r && typeof r === "object" && "reason" in r && typeof r.reason === "string") {
55
+ out.push(r.reason);
56
+ }
57
+ }
58
+ }
59
+ for (const f of findings) {
60
+ if (f && typeof f === "object") {
61
+ const rec = f;
62
+ const rule = typeof rec.rule === "string" ? rec.rule : undefined;
63
+ const reason = typeof rec.reason === "string" ? rec.reason : undefined;
64
+ if (rule && reason)
65
+ out.push(`${rule}: ${reason}`);
66
+ else if (rule)
67
+ out.push(rule);
68
+ else if (reason)
69
+ out.push(reason);
70
+ }
71
+ else if (typeof f === "string") {
72
+ out.push(f);
73
+ }
74
+ }
75
+ return out;
76
+ }
77
+ function asString(v) {
78
+ return typeof v === "string" ? v : undefined;
79
+ }
80
+ function asRisk(v) {
81
+ if (v === "low" || v === "medium" || v === "high")
82
+ return v;
83
+ return undefined;
84
+ }
85
+ /**
86
+ * Apply allow/review/deny policy. Deny and (by default) review throw.
87
+ * Timeouts and transport errors should be turned into GuardError first,
88
+ * then passed through enforceError.
89
+ */
90
+ export async function enforce(decision, opts = {}) {
91
+ if (decision.verdict === "allow")
92
+ return decision;
93
+ if (decision.verdict === "deny")
94
+ throw new GuardDenied(decision);
95
+ const mode = await resolveReview(opts.onReview ?? "throw", decision);
96
+ if (mode === "allow")
97
+ return decision;
98
+ if (mode === "deny")
99
+ throw new GuardDenied(decision, `GrindFree ${decision.service} review treated as deny`);
100
+ throw new GuardReviewRequired(decision);
101
+ }
102
+ export function enforceError(err, service, opts = {}) {
103
+ const mode = opts.onError ?? "deny";
104
+ if (err instanceof GuardDenied || err instanceof GuardReviewRequired)
105
+ throw err;
106
+ const wrapped = err instanceof GuardError
107
+ ? err
108
+ : new GuardError(err instanceof Error ? err.message : String(err), { service });
109
+ if (mode === "throw")
110
+ throw wrapped;
111
+ if (mode === "allow") {
112
+ // Caller asked to proceed on transport failure. Still throw so they can
113
+ // distinguish this path if they catch GuardError; use mode "allow" only
114
+ // via tryGuard helpers that swallow this.
115
+ throw wrapped;
116
+ }
117
+ throw new GuardDenied({
118
+ verdict: "deny",
119
+ service: service,
120
+ endpoint: "error",
121
+ reasons: [wrapped.message],
122
+ findings: [],
123
+ raw: { error: wrapped.message, status: wrapped.status },
124
+ }, `GrindFree ${service} failed closed: ${wrapped.message}`);
125
+ }
126
+ async function resolveReview(onReview, decision) {
127
+ if (typeof onReview === "function")
128
+ return onReview(decision);
129
+ return onReview;
130
+ }
131
+ //# sourceMappingURL=decisions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decisions.js","sourceRoot":"","sources":["../src/decisions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { NormalizedDecision } from "./decisions.js";
2
+ export declare class GuardConfigError extends Error {
3
+ readonly code = "GUARD_CONFIG";
4
+ constructor(message: string);
5
+ }
6
+ export declare class GuardError extends Error {
7
+ readonly code = "GUARD_ERROR";
8
+ readonly status?: number;
9
+ readonly service: string;
10
+ readonly body?: unknown;
11
+ constructor(message: string, opts: {
12
+ service: string;
13
+ status?: number;
14
+ body?: unknown;
15
+ });
16
+ }
17
+ export declare class GuardDenied extends Error {
18
+ readonly code = "GUARD_DENIED";
19
+ readonly verdict: "deny";
20
+ readonly service: string;
21
+ readonly decision: NormalizedDecision;
22
+ constructor(decision: NormalizedDecision, message?: string);
23
+ }
24
+ export declare class GuardReviewRequired extends Error {
25
+ readonly code = "GUARD_REVIEW";
26
+ readonly verdict: "review";
27
+ readonly service: string;
28
+ readonly decision: NormalizedDecision;
29
+ constructor(decision: NormalizedDecision, message?: string);
30
+ }
31
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,kBAAkB;gBACnB,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,iBAAiB;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;gBACZ,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE;CAOxF;AAED,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,OAAO,EAAG,MAAM,CAAU;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;gBAC1B,QAAQ,EAAE,kBAAkB,EAAE,OAAO,CAAC,EAAE,MAAM;CAM3D;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,OAAO,EAAG,QAAQ,CAAU;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;gBAC1B,QAAQ,EAAE,kBAAkB,EAAE,OAAO,CAAC,EAAE,MAAM;CAM3D"}
package/dist/errors.js ADDED
@@ -0,0 +1,49 @@
1
+ export class GuardConfigError extends Error {
2
+ code = "GUARD_CONFIG";
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = "GuardConfigError";
6
+ }
7
+ }
8
+ export class GuardError extends Error {
9
+ code = "GUARD_ERROR";
10
+ status;
11
+ service;
12
+ body;
13
+ constructor(message, opts) {
14
+ super(message);
15
+ this.name = "GuardError";
16
+ this.service = opts.service;
17
+ this.status = opts.status;
18
+ this.body = opts.body;
19
+ }
20
+ }
21
+ export class GuardDenied extends Error {
22
+ code = "GUARD_DENIED";
23
+ verdict = "deny";
24
+ service;
25
+ decision;
26
+ constructor(decision, message) {
27
+ super(message ?? defaultMessage(decision, "denied"));
28
+ this.name = "GuardDenied";
29
+ this.service = decision.service;
30
+ this.decision = decision;
31
+ }
32
+ }
33
+ export class GuardReviewRequired extends Error {
34
+ code = "GUARD_REVIEW";
35
+ verdict = "review";
36
+ service;
37
+ decision;
38
+ constructor(decision, message) {
39
+ super(message ?? defaultMessage(decision, "needs review"));
40
+ this.name = "GuardReviewRequired";
41
+ this.service = decision.service;
42
+ this.decision = decision;
43
+ }
44
+ }
45
+ function defaultMessage(d, kind) {
46
+ const reason = d.reasons[0] ?? d.advice ?? d.rawDecision ?? d.risk ?? kind;
47
+ return `GrindFree ${d.service} ${kind}: ${reason}`;
48
+ }
49
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAChC,IAAI,GAAG,cAAc,CAAC;IAC/B,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED,MAAM,OAAO,UAAW,SAAQ,KAAK;IAC1B,IAAI,GAAG,aAAa,CAAC;IACrB,MAAM,CAAU;IAChB,OAAO,CAAS;IAChB,IAAI,CAAW;IACxB,YAAY,OAAe,EAAE,IAA0D;QACrF,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACxB,CAAC;CACF;AAED,MAAM,OAAO,WAAY,SAAQ,KAAK;IAC3B,IAAI,GAAG,cAAc,CAAC;IACtB,OAAO,GAAG,MAAe,CAAC;IAC1B,OAAO,CAAS;IAChB,QAAQ,CAAqB;IACtC,YAAY,QAA4B,EAAE,OAAgB;QACxD,KAAK,CAAC,OAAO,IAAI,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;QAChC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF;AAED,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IACnC,IAAI,GAAG,cAAc,CAAC;IACtB,OAAO,GAAG,QAAiB,CAAC;IAC5B,OAAO,CAAS;IAChB,QAAQ,CAAqB;IACtC,YAAY,QAA4B,EAAE,OAAgB;QACxD,KAAK,CAAC,OAAO,IAAI,cAAc,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;QAChC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF;AAED,SAAS,cAAc,CAAC,CAAqB,EAAE,IAAY;IACzD,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC;IAC3E,OAAO,aAAa,CAAC,CAAC,OAAO,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;AACrD,CAAC"}
@@ -0,0 +1,134 @@
1
+ import { type ClientOptions } from "./client.js";
2
+ import { type EnforceOptions, type NormalizedDecision } from "./decisions.js";
3
+ export type ToolCallInput = {
4
+ tool: string;
5
+ destination?: {
6
+ host?: string;
7
+ scheme?: string;
8
+ port?: number;
9
+ path_class?: string;
10
+ };
11
+ arg_classes?: string[];
12
+ taint?: string[];
13
+ secret_labels?: string[];
14
+ policy?: Record<string, unknown>;
15
+ policy_hash?: string;
16
+ rule_pack?: string;
17
+ };
18
+ export type CommandInput = {
19
+ command: string;
20
+ shell?: "bash" | "sh" | "zsh" | "fish" | "powershell" | "cmd";
21
+ policy?: Record<string, unknown>;
22
+ };
23
+ export type SqlInput = {
24
+ sql: string;
25
+ dialect?: "postgres" | "mysql" | "mariadb" | "sqlite" | "mssql" | "snowflake" | "bigquery" | "other";
26
+ policy?: Record<string, unknown>;
27
+ };
28
+ export type PromptInput = {
29
+ content: string;
30
+ format?: "text" | "html";
31
+ };
32
+ export type CrawlInput = {
33
+ url: string;
34
+ user_agent?: string;
35
+ check_page?: boolean;
36
+ };
37
+ export type MemoryInput = {
38
+ content?: string;
39
+ source?: "user" | "assistant" | "tool" | "web" | "document" | "email" | "retrieved" | "mcp" | "unknown";
40
+ memory_type?: "fact" | "preference" | "instruction" | "episodic" | "summary";
41
+ entries?: Array<{
42
+ key?: string;
43
+ content: string;
44
+ source?: string;
45
+ }>;
46
+ policy?: Record<string, unknown>;
47
+ };
48
+ export type OutputInput = {
49
+ content: string;
50
+ format?: "markdown" | "text" | "html" | "json";
51
+ allowed_domains?: string[];
52
+ canaries?: string[];
53
+ system_prompt?: string;
54
+ policy?: Record<string, unknown>;
55
+ };
56
+ export type DocumentInput = {
57
+ content?: string;
58
+ content_base64?: string;
59
+ format?: "html" | "markdown" | "text";
60
+ filename?: string;
61
+ include_text?: boolean;
62
+ policy?: Record<string, unknown>;
63
+ };
64
+ export type GuardOptions = ClientOptions & EnforceOptions & {
65
+ /** Default ToolGuard / CommandGuard policy sent when a call omits one. */
66
+ policy?: Record<string, unknown>;
67
+ };
68
+ export type GuardedResult<T> = {
69
+ value: T;
70
+ decision: NormalizedDecision;
71
+ };
72
+ export declare class GrindFreeGuard {
73
+ readonly options: GuardOptions;
74
+ private readonly paidFetch;
75
+ constructor(options?: GuardOptions);
76
+ private enforceOpts;
77
+ private call;
78
+ private callAndEnforce;
79
+ /** ToolGuard evaluate only (no enforcement). */
80
+ evaluateTool(input: ToolCallInput): Promise<NormalizedDecision>;
81
+ /**
82
+ * Screen a tool call with ToolGuard, enforce allow/review/deny, then run `fn`.
83
+ * On deny (or review with default onReview=throw) `fn` is not called.
84
+ */
85
+ guardToolCall<T>(input: ToolCallInput, fn: () => Promise<T> | T): Promise<GuardedResult<T>>;
86
+ /**
87
+ * Wrap any tool function so ToolGuard runs first.
88
+ * `classify` maps arguments to a ToolGuard body; defaults to `{ tool: name }`.
89
+ */
90
+ wrapTool<Args extends unknown[], R>(name: string, fn: (...args: Args) => Promise<R> | R, classify?: (...args: Args) => ToolCallInput | Promise<ToolCallInput>): (...args: Args) => Promise<R>;
91
+ checkCommand(input: CommandInput): Promise<NormalizedDecision>;
92
+ guardCommand<T>(input: CommandInput, fn: () => Promise<T> | T): Promise<GuardedResult<T>>;
93
+ checkSql(input: SqlInput): Promise<NormalizedDecision>;
94
+ guardSql<T>(input: SqlInput, fn: () => Promise<T> | T): Promise<GuardedResult<T>>;
95
+ screenPrompt(input: PromptInput): Promise<NormalizedDecision>;
96
+ /**
97
+ * Screen untrusted text/HTML. Throws on high risk (deny). Returns sanitized
98
+ * text (or the original when the service did not supply one).
99
+ */
100
+ requireSafePrompt(input: PromptInput): Promise<{
101
+ text: string;
102
+ decision: NormalizedDecision;
103
+ }>;
104
+ cleanHtml(content: string): Promise<{
105
+ text: string;
106
+ raw: Record<string, unknown>;
107
+ }>;
108
+ checkCrawl(input: CrawlInput): Promise<NormalizedDecision>;
109
+ requireCrawlAllowed(input: CrawlInput): Promise<NormalizedDecision>;
110
+ screenMemory(input: MemoryInput): Promise<NormalizedDecision>;
111
+ requireSafeMemory(input: MemoryInput): Promise<{
112
+ text?: string;
113
+ decision: NormalizedDecision;
114
+ }>;
115
+ scanOutput(input: OutputInput): Promise<NormalizedDecision>;
116
+ requireSafeOutput(input: OutputInput): Promise<{
117
+ text: string;
118
+ decision: NormalizedDecision;
119
+ }>;
120
+ scanDocument(input: DocumentInput): Promise<NormalizedDecision>;
121
+ requireSafeDocument(input: DocumentInput): Promise<{
122
+ text?: string;
123
+ decision: NormalizedDecision;
124
+ }>;
125
+ }
126
+ export declare function createGuard(options?: GuardOptions): GrindFreeGuard;
127
+ /** Convenience wrapper around `createGuard(...).guardToolCall`. */
128
+ export declare function guardToolCall<T>(input: ToolCallInput, fn: () => Promise<T> | T, options?: GuardOptions): Promise<GuardedResult<T>>;
129
+ /**
130
+ * Express/fetch-style middleware: evaluate an outbound tool described by
131
+ * `classify(request)` and only call `next` when the verdict allows it.
132
+ */
133
+ export declare function createGuardMiddleware(options?: GuardOptions): <Req, Res>(classify: (req: Req) => ToolCallInput | Promise<ToolCallInput>, next: (req: Req) => Promise<Res> | Res, req: Req) => Promise<GuardedResult<Res>>;
134
+ //# sourceMappingURL=guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"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"}
package/dist/guard.js ADDED
@@ -0,0 +1,159 @@
1
+ import { createPaidFetch, postJson } from "./client.js";
2
+ import { enforce, enforceError, normalizeDecision } from "./decisions.js";
3
+ import { GuardDenied } from "./errors.js";
4
+ import { serviceUrl } from "./services.js";
5
+ function clean(obj) {
6
+ return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined));
7
+ }
8
+ export class GrindFreeGuard {
9
+ options;
10
+ paidFetch;
11
+ constructor(options = {}) {
12
+ this.options = options;
13
+ this.paidFetch = createPaidFetch(options);
14
+ }
15
+ enforceOpts() {
16
+ return { onReview: this.options.onReview, onError: this.options.onError };
17
+ }
18
+ async call(service, endpoint, body) {
19
+ const url = serviceUrl(service, endpoint);
20
+ try {
21
+ const raw = await postJson(this.paidFetch, url, body, {
22
+ timeoutMs: this.options.timeoutMs,
23
+ service,
24
+ });
25
+ return normalizeDecision(service, endpoint, raw);
26
+ }
27
+ catch (err) {
28
+ if (this.options.onError === "allow") {
29
+ if (err instanceof GuardDenied)
30
+ throw err;
31
+ const message = err instanceof Error ? err.message : String(err);
32
+ return {
33
+ verdict: "allow",
34
+ service,
35
+ endpoint,
36
+ reasons: [`transport failure allowed by onError=allow: ${message}`],
37
+ findings: [],
38
+ raw: { error: message },
39
+ };
40
+ }
41
+ enforceError(err, service, this.enforceOpts());
42
+ }
43
+ }
44
+ async callAndEnforce(service, endpoint, body) {
45
+ const decision = await this.call(service, endpoint, body);
46
+ return enforce(decision, this.enforceOpts());
47
+ }
48
+ /** ToolGuard evaluate only (no enforcement). */
49
+ evaluateTool(input) {
50
+ const body = clean({ ...input, policy: input.policy ?? this.options.policy });
51
+ return this.call("toolguard", "evaluate", body);
52
+ }
53
+ /**
54
+ * Screen a tool call with ToolGuard, enforce allow/review/deny, then run `fn`.
55
+ * On deny (or review with default onReview=throw) `fn` is not called.
56
+ */
57
+ async guardToolCall(input, fn) {
58
+ const decision = await this.callAndEnforce("toolguard", "evaluate", clean({ ...input, policy: input.policy ?? this.options.policy }));
59
+ const value = await fn();
60
+ return { value, decision };
61
+ }
62
+ /**
63
+ * Wrap any tool function so ToolGuard runs first.
64
+ * `classify` maps arguments to a ToolGuard body; defaults to `{ tool: name }`.
65
+ */
66
+ wrapTool(name, fn, classify) {
67
+ return async (...args) => {
68
+ const input = classify ? await classify(...args) : { tool: name };
69
+ const { value } = await this.guardToolCall({ ...input, tool: input.tool ?? name }, () => fn(...args));
70
+ return value;
71
+ };
72
+ }
73
+ checkCommand(input) {
74
+ return this.call("commandguard", "command", clean({ ...input, policy: input.policy ?? this.options.policy }));
75
+ }
76
+ async guardCommand(input, fn) {
77
+ const decision = await this.callAndEnforce("commandguard", "command", clean({ ...input, policy: input.policy ?? this.options.policy }));
78
+ return { value: await fn(), decision };
79
+ }
80
+ checkSql(input) {
81
+ return this.call("commandguard", "sql", clean({ ...input, policy: input.policy ?? this.options.policy }));
82
+ }
83
+ async guardSql(input, fn) {
84
+ const decision = await this.callAndEnforce("commandguard", "sql", clean({ ...input, policy: input.policy ?? this.options.policy }));
85
+ return { value: await fn(), decision };
86
+ }
87
+ screenPrompt(input) {
88
+ return this.call("promptshield", "screen", input);
89
+ }
90
+ /**
91
+ * Screen untrusted text/HTML. Throws on high risk (deny). Returns sanitized
92
+ * text (or the original when the service did not supply one).
93
+ */
94
+ async requireSafePrompt(input) {
95
+ const decision = await this.callAndEnforce("promptshield", "screen", input);
96
+ return { text: decision.sanitized ?? input.content, decision };
97
+ }
98
+ async cleanHtml(content) {
99
+ try {
100
+ const raw = await postJson(this.paidFetch, serviceUrl("promptshield", "clean"), { content }, {
101
+ timeoutMs: this.options.timeoutMs,
102
+ service: "promptshield",
103
+ });
104
+ return { text: typeof raw.text === "string" ? raw.text : "", raw };
105
+ }
106
+ catch (err) {
107
+ if (this.options.onError === "allow") {
108
+ return { text: content, raw: { error: err instanceof Error ? err.message : String(err) } };
109
+ }
110
+ enforceError(err, "promptshield", this.enforceOpts());
111
+ }
112
+ }
113
+ checkCrawl(input) {
114
+ return this.call("crawlsafe", "check", input);
115
+ }
116
+ async requireCrawlAllowed(input) {
117
+ return this.callAndEnforce("crawlsafe", "check", input);
118
+ }
119
+ screenMemory(input) {
120
+ return this.call("memoryguard", "screen", clean({ ...input, policy: input.policy ?? this.options.policy }));
121
+ }
122
+ async requireSafeMemory(input) {
123
+ const decision = await this.callAndEnforce("memoryguard", "screen", clean({ ...input, policy: input.policy ?? this.options.policy }));
124
+ return { text: decision.sanitized ?? input.content, decision };
125
+ }
126
+ scanOutput(input) {
127
+ return this.call("wireshield", "scan", clean({ ...input, policy: input.policy ?? this.options.policy }));
128
+ }
129
+ async requireSafeOutput(input) {
130
+ const decision = await this.callAndEnforce("wireshield", "scan", clean({ ...input, policy: input.policy ?? this.options.policy }));
131
+ return { text: decision.sanitized ?? input.content, decision };
132
+ }
133
+ scanDocument(input) {
134
+ return this.call("docshield", "scan", clean({ ...input, policy: input.policy ?? this.options.policy }));
135
+ }
136
+ async requireSafeDocument(input) {
137
+ const decision = await this.callAndEnforce("docshield", "scan", clean({ ...input, policy: input.policy ?? this.options.policy }));
138
+ return { text: decision.safeText ?? decision.sanitized ?? input.content, decision };
139
+ }
140
+ }
141
+ export function createGuard(options = {}) {
142
+ return new GrindFreeGuard(options);
143
+ }
144
+ /** Convenience wrapper around `createGuard(...).guardToolCall`. */
145
+ export function guardToolCall(input, fn, options = {}) {
146
+ return createGuard(options).guardToolCall(input, fn);
147
+ }
148
+ /**
149
+ * Express/fetch-style middleware: evaluate an outbound tool described by
150
+ * `classify(request)` and only call `next` when the verdict allows it.
151
+ */
152
+ export function createGuardMiddleware(options = {}) {
153
+ const guard = createGuard(options);
154
+ return async function grindfreeMiddleware(classify, next, req) {
155
+ const input = await classify(req);
156
+ return guard.guardToolCall(input, () => next(req));
157
+ };
158
+ }
159
+ //# sourceMappingURL=guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.js","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export { createGuard, guardToolCall, createGuardMiddleware, GrindFreeGuard } from "./guard.js";
2
+ export type { GuardOptions, GuardedResult, ToolCallInput, CommandInput, SqlInput, PromptInput, CrawlInput, MemoryInput, OutputInput, DocumentInput, } from "./guard.js";
3
+ export { createPaidFetch, postJson, readPrivateKey } from "./client.js";
4
+ export type { ClientOptions, PaidFetch } from "./client.js";
5
+ export { normalizeDecision, enforce, enforceError } from "./decisions.js";
6
+ export type { Verdict, ReviewMode, ErrorMode, NormalizedDecision, EnforceOptions } from "./decisions.js";
7
+ export { GuardConfigError, GuardError, GuardDenied, GuardReviewRequired } from "./errors.js";
8
+ export { SERVICES, NETWORK, PAY_TO, serviceUrl } from "./services.js";
9
+ export type { ServiceName, ServiceSpec, EndpointSpec } from "./services.js";
10
+ export { wrapMcpClient } from "./wrap-mcp.js";
11
+ export type { WrapMcpOptions } from "./wrap-mcp.js";
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC/F,YAAY,EACV,YAAY,EACZ,aAAa,EACb,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,UAAU,EACV,WAAW,EACX,WAAW,EACX,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACxE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC1E,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACzG,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC7F,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACtE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export { createGuard, guardToolCall, createGuardMiddleware, GrindFreeGuard } from "./guard.js";
2
+ export { createPaidFetch, postJson, readPrivateKey } from "./client.js";
3
+ export { normalizeDecision, enforce, enforceError } from "./decisions.js";
4
+ export { GuardConfigError, GuardError, GuardDenied, GuardReviewRequired } from "./errors.js";
5
+ export { SERVICES, NETWORK, PAY_TO, serviceUrl } from "./services.js";
6
+ export { wrapMcpClient } from "./wrap-mcp.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAa/F,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAExE,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE1E,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC7F,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEtE,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,87 @@
1
+ /** Live GrindFree x402 origins, prices, and paths. USDC on Base. */
2
+ export declare const NETWORK: "eip155:8453";
3
+ export declare const PAY_TO: "0xe4b5827994B7B3Cd38F9830d9ECF58b51B77721b";
4
+ export type ServiceName = "promptshield" | "crawlsafe" | "toolguard" | "commandguard" | "memoryguard" | "wireshield" | "docshield";
5
+ export interface EndpointSpec {
6
+ path: string;
7
+ priceUsdc: string;
8
+ }
9
+ export interface ServiceSpec {
10
+ origin: string;
11
+ endpoints: Record<string, EndpointSpec>;
12
+ }
13
+ export declare const SERVICES: {
14
+ readonly promptshield: {
15
+ readonly origin: "https://promptshield.grindfree.net";
16
+ readonly endpoints: {
17
+ readonly screen: {
18
+ readonly path: "/v1/screen";
19
+ readonly priceUsdc: "0.002";
20
+ };
21
+ readonly clean: {
22
+ readonly path: "/v1/clean";
23
+ readonly priceUsdc: "0.002";
24
+ };
25
+ };
26
+ };
27
+ readonly crawlsafe: {
28
+ readonly origin: "https://crawlsafe.grindfree.net";
29
+ readonly endpoints: {
30
+ readonly check: {
31
+ readonly path: "/v1/check";
32
+ readonly priceUsdc: "0.002";
33
+ };
34
+ };
35
+ };
36
+ readonly toolguard: {
37
+ readonly origin: "https://toolguard.grindfree.net";
38
+ readonly endpoints: {
39
+ readonly evaluate: {
40
+ readonly path: "/v1/evaluate";
41
+ readonly priceUsdc: "0.002";
42
+ };
43
+ };
44
+ };
45
+ readonly commandguard: {
46
+ readonly origin: "https://commandguard.grindfree.net";
47
+ readonly endpoints: {
48
+ readonly command: {
49
+ readonly path: "/v1/command";
50
+ readonly priceUsdc: "0.001";
51
+ };
52
+ readonly sql: {
53
+ readonly path: "/v1/sql";
54
+ readonly priceUsdc: "0.001";
55
+ };
56
+ };
57
+ };
58
+ readonly memoryguard: {
59
+ readonly origin: "https://memoryguard.grindfree.net";
60
+ readonly endpoints: {
61
+ readonly screen: {
62
+ readonly path: "/v1/screen";
63
+ readonly priceUsdc: "0.002";
64
+ };
65
+ };
66
+ };
67
+ readonly wireshield: {
68
+ readonly origin: "https://wireshield.grindfree.net";
69
+ readonly endpoints: {
70
+ readonly scan: {
71
+ readonly path: "/v1/scan";
72
+ readonly priceUsdc: "0.002";
73
+ };
74
+ };
75
+ };
76
+ readonly docshield: {
77
+ readonly origin: "https://docshield.grindfree.net";
78
+ readonly endpoints: {
79
+ readonly scan: {
80
+ readonly path: "/v1/scan";
81
+ readonly priceUsdc: "0.004";
82
+ };
83
+ };
84
+ };
85
+ };
86
+ export declare function serviceUrl(service: ServiceName, endpoint: string): string;
87
+ //# sourceMappingURL=services.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../src/services.ts"],"names":[],"mappings":"AAAA,oEAAoE;AAEpE,eAAO,MAAM,OAAO,EAAG,aAAsB,CAAC;AAC9C,eAAO,MAAM,MAAM,EAAG,4CAAqD,CAAC;AAE5E,MAAM,MAAM,WAAW,GACnB,cAAc,GACd,WAAW,GACX,WAAW,GACX,cAAc,GACd,aAAa,GACb,YAAY,GACZ,WAAW,CAAC;AAEhB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACzC;AAED,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6CgC,CAAC;AAEtD,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKzE"}
@@ -0,0 +1,57 @@
1
+ /** Live GrindFree x402 origins, prices, and paths. USDC on Base. */
2
+ export const NETWORK = "eip155:8453";
3
+ export const PAY_TO = "0xe4b5827994B7B3Cd38F9830d9ECF58b51B77721b";
4
+ export const SERVICES = {
5
+ promptshield: {
6
+ origin: "https://promptshield.grindfree.net",
7
+ endpoints: {
8
+ screen: { path: "/v1/screen", priceUsdc: "0.002" },
9
+ clean: { path: "/v1/clean", priceUsdc: "0.002" },
10
+ },
11
+ },
12
+ crawlsafe: {
13
+ origin: "https://crawlsafe.grindfree.net",
14
+ endpoints: {
15
+ check: { path: "/v1/check", priceUsdc: "0.002" },
16
+ },
17
+ },
18
+ toolguard: {
19
+ origin: "https://toolguard.grindfree.net",
20
+ endpoints: {
21
+ evaluate: { path: "/v1/evaluate", priceUsdc: "0.002" },
22
+ },
23
+ },
24
+ commandguard: {
25
+ origin: "https://commandguard.grindfree.net",
26
+ endpoints: {
27
+ command: { path: "/v1/command", priceUsdc: "0.001" },
28
+ sql: { path: "/v1/sql", priceUsdc: "0.001" },
29
+ },
30
+ },
31
+ memoryguard: {
32
+ origin: "https://memoryguard.grindfree.net",
33
+ endpoints: {
34
+ screen: { path: "/v1/screen", priceUsdc: "0.002" },
35
+ },
36
+ },
37
+ wireshield: {
38
+ origin: "https://wireshield.grindfree.net",
39
+ endpoints: {
40
+ scan: { path: "/v1/scan", priceUsdc: "0.002" },
41
+ },
42
+ },
43
+ docshield: {
44
+ origin: "https://docshield.grindfree.net",
45
+ endpoints: {
46
+ scan: { path: "/v1/scan", priceUsdc: "0.004" },
47
+ },
48
+ },
49
+ };
50
+ export function serviceUrl(service, endpoint) {
51
+ const spec = SERVICES[service];
52
+ const ep = spec.endpoints[endpoint];
53
+ if (!ep)
54
+ throw new Error(`Unknown endpoint ${service} ${endpoint}`);
55
+ return `${spec.origin}${ep.path}`;
56
+ }
57
+ //# sourceMappingURL=services.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"services.js","sourceRoot":"","sources":["../src/services.ts"],"names":[],"mappings":"AAAA,oEAAoE;AAEpE,MAAM,CAAC,MAAM,OAAO,GAAG,aAAsB,CAAC;AAC9C,MAAM,CAAC,MAAM,MAAM,GAAG,4CAAqD,CAAC;AAqB5E,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,YAAY,EAAE;QACZ,MAAM,EAAE,oCAAoC;QAC5C,SAAS,EAAE;YACT,MAAM,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE;YAClD,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE;SACjD;KACF;IACD,SAAS,EAAE;QACT,MAAM,EAAE,iCAAiC;QACzC,SAAS,EAAE;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE;SACjD;KACF;IACD,SAAS,EAAE;QACT,MAAM,EAAE,iCAAiC;QACzC,SAAS,EAAE;YACT,QAAQ,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,SAAS,EAAE,OAAO,EAAE;SACvD;KACF;IACD,YAAY,EAAE;QACZ,MAAM,EAAE,oCAAoC;QAC5C,SAAS,EAAE;YACT,OAAO,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE;YACpD,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE;SAC7C;KACF;IACD,WAAW,EAAE;QACX,MAAM,EAAE,mCAAmC;QAC3C,SAAS,EAAE;YACT,MAAM,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE;SACnD;KACF;IACD,UAAU,EAAE;QACV,MAAM,EAAE,kCAAkC;QAC1C,SAAS,EAAE;YACT,IAAI,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE;SAC/C;KACF;IACD,SAAS,EAAE;QACT,MAAM,EAAE,iCAAiC;QACzC,SAAS,EAAE;YACT,IAAI,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE;SAC/C;KACF;CACkD,CAAC;AAEtD,MAAM,UAAU,UAAU,CAAC,OAAoB,EAAE,QAAgB;IAC/D,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAgB,CAAC;IAC9C,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACpC,IAAI,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,OAAO,IAAI,QAAQ,EAAE,CAAC,CAAC;IACpE,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC;AACpC,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * MCP client wrapper: evaluate ToolGuard before client.callTool, and
3
+ * CommandGuard when the tool looks like a shell/SQL execution.
4
+ *
5
+ * No MCP SDK dependency. Pass any client with callTool().
6
+ * export X402_PRIVATE_KEY=0xYOUR_BASE_USDC_PRIVATE_KEY
7
+ */
8
+ import { type GrindFreeGuard } from "./guard.js";
9
+ export type WrapMcpOptions = {
10
+ guard?: GrindFreeGuard;
11
+ /** Tool names treated as shell execution (CommandGuard). */
12
+ commandTools?: string[];
13
+ /** Tool names treated as SQL execution (CommandGuard /v1/sql). */
14
+ sqlTools?: string[];
15
+ policy?: Record<string, unknown>;
16
+ };
17
+ /**
18
+ * Wrap an MCP client so every callTool is screened first. The original
19
+ * client is returned for chaining; callTool is replaced in place.
20
+ */
21
+ export declare function wrapMcpClient<T extends {
22
+ callTool: (...args: any[]) => any;
23
+ }>(client: T, opts?: WrapMcpOptions): T;
24
+ //# sourceMappingURL=wrap-mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrap-mcp.d.ts","sourceRoot":"","sources":["../src/wrap-mcp.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAe,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAG9D,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,CAAC;AAcF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS;IAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAA;CAAE,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,GAAE,cAAmB,GAAG,CAAC,CA8BtH"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * MCP client wrapper: evaluate ToolGuard before client.callTool, and
3
+ * CommandGuard when the tool looks like a shell/SQL execution.
4
+ *
5
+ * No MCP SDK dependency. Pass any client with callTool().
6
+ * export X402_PRIVATE_KEY=0xYOUR_BASE_USDC_PRIVATE_KEY
7
+ */
8
+ import { createGuard } from "./guard.js";
9
+ import { GuardDenied, GuardReviewRequired } from "./errors.js";
10
+ const DEFAULT_COMMAND_TOOLS = ["shell", "bash", "sh", "terminal", "run_command", "execute_command", "command"];
11
+ const DEFAULT_SQL_TOOLS = ["sql", "query", "execute_sql", "run_sql"];
12
+ function nameOf(tool) {
13
+ return typeof tool === "string" ? tool : tool.name ?? "tool";
14
+ }
15
+ function looksLike(name, list) {
16
+ const n = name.toLowerCase();
17
+ return list.some((t) => n === t || n.endsWith(`.${t}`) || n.endsWith(`_${t}`) || n.includes(t));
18
+ }
19
+ /**
20
+ * Wrap an MCP client so every callTool is screened first. The original
21
+ * client is returned for chaining; callTool is replaced in place.
22
+ */
23
+ export function wrapMcpClient(client, opts = {}) {
24
+ const guard = opts.guard ?? createGuard({ policy: opts.policy });
25
+ const commandTools = opts.commandTools ?? DEFAULT_COMMAND_TOOLS;
26
+ const sqlTools = opts.sqlTools ?? DEFAULT_SQL_TOOLS;
27
+ const original = client.callTool.bind(client);
28
+ client.callTool = async (params, ...rest) => {
29
+ const toolName = nameOf(params ?? {});
30
+ const args = (params?.arguments ?? {});
31
+ try {
32
+ if (looksLike(toolName, commandTools) && typeof (args.command ?? args.cmd ?? args.input) === "string") {
33
+ const command = String(args.command ?? args.cmd ?? args.input);
34
+ await guard.guardCommand({ command }, async () => undefined);
35
+ }
36
+ else if (looksLike(toolName, sqlTools) && typeof (args.sql ?? args.query ?? args.statement) === "string") {
37
+ const sql = String(args.sql ?? args.query ?? args.statement);
38
+ await guard.guardSql({ sql }, async () => undefined);
39
+ }
40
+ else {
41
+ await guard.guardToolCall({ tool: `mcp.${toolName}`, arg_classes: Object.keys(args) }, async () => undefined);
42
+ }
43
+ }
44
+ catch (err) {
45
+ if (err instanceof GuardDenied || err instanceof GuardReviewRequired)
46
+ throw err;
47
+ throw err;
48
+ }
49
+ return original(params, ...rest);
50
+ };
51
+ return client;
52
+ }
53
+ //# sourceMappingURL=wrap-mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrap-mcp.js","sourceRoot":"","sources":["../src/wrap-mcp.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,60 @@
1
1
  {
2
2
  "name": "@grindfree/guard",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Drop-in x402 guards that screen PromptShield, CrawlSafe, ToolGuard, CommandGuard, MemoryGuard, WireShield and DocShield before an agent acts. Caller wallet only; never embeds keys.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "engines": {
20
+ "node": ">=20"
21
+ },
22
+ "scripts": {
23
+ "build": "tsc",
24
+ "test": "tsx --test test/*.test.ts",
25
+ "prepublishOnly": "npm test && npm run build",
26
+ "prepare": "npm run build"
27
+ },
28
+ "keywords": [
29
+ "x402",
30
+ "agents",
31
+ "security",
32
+ "guardrail",
33
+ "toolguard",
34
+ "commandguard",
35
+ "promptshield",
36
+ "mcp",
37
+ "langchain",
38
+ "openai-agents"
39
+ ],
40
+ "license": "MIT",
41
+ "author": "GrindFree",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "git+https://github.com/ronin93/grindfree-guard.git"
45
+ },
46
+ "homepage": "https://github.com/ronin93/grindfree-guard#readme",
47
+ "bugs": {
48
+ "url": "https://github.com/ronin93/grindfree-guard/issues"
49
+ },
50
+ "dependencies": {
51
+ "@x402/evm": "^2.29.0",
52
+ "@x402/fetch": "^2.29.0",
53
+ "viem": "^2.57.16"
54
+ },
55
+ "devDependencies": {
56
+ "@types/node": "^22.10.0",
57
+ "tsx": "^4.19.2",
58
+ "typescript": "^5.7.2"
59
+ }
60
+ }