ovrule-lab 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,7 +8,7 @@ TypeScript client for Ovrule case-file classification, guardrails, and receipt v
8
8
  npm install ovrule-lab
9
9
  ```
10
10
 
11
- On the first successful `classify()` or `guard()` call in a process, the SDK prints a one-line ready message plus a permalink to the created case. Later successful audits print only the case permalink. Set `OVRULE_QUIET=1` to suppress those logs.
11
+ On the first successful `classify()` or `guard()` call in a process, the SDK prints the full Ovrule ASCII banner plus a permalink to the created case. Later successful audits print only the case permalink. Set `OVRULE_QUIET=1` to suppress all SDK logs. You can also run `npx ovrule-lab` at any time to print the banner manually.
12
12
 
13
13
  ## 1. Simple classify
14
14
 
package/bin/ovrule-lab ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ import "./welcome.js";
package/bin/welcome.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { logBanner } from "../dist/banner.js";
4
+
5
+ logBanner();
@@ -0,0 +1,5 @@
1
+ export declare const SDK_VERSION = "0.2.2";
2
+ export declare function isQuietModeEnabled(): boolean;
3
+ export declare function renderBanner(): string;
4
+ export declare function logBanner(): void;
5
+ export declare function getDefaultBaseUrl(): string;
package/dist/banner.js ADDED
@@ -0,0 +1,47 @@
1
+ const DEFAULT_OVRULE_BASE_URL = "https://decision-receipt-lab.vercel.app";
2
+ export const SDK_VERSION = "0.2.2";
3
+ export function isQuietModeEnabled() {
4
+ return (typeof process !== "undefined" &&
5
+ typeof process.env === "object" &&
6
+ typeof process.env.OVRULE_QUIET !== "undefined" &&
7
+ process.env.OVRULE_QUIET !== "");
8
+ }
9
+ export function renderBanner() {
10
+ const reset = "\x1b[0m";
11
+ const dim = "\x1b[2m";
12
+ const bold = "\x1b[1m";
13
+ const cyan = "\x1b[36m";
14
+ const magenta = "\x1b[35m";
15
+ const yellow = "\x1b[33m";
16
+ return [
17
+ "",
18
+ `${bold}${cyan} ╔═══════════════════════════════════════════════╗${reset}`,
19
+ `${bold}${cyan} ║${reset} ${bold}O${reset}${magenta}━━${reset}${bold}V${reset} ${bold}Ovrule${reset} · SDK v${SDK_VERSION} ${bold}${cyan}║${reset}`,
20
+ `${bold}${cyan} ║${reset} ${dim}Auditable receipts for AI agents${reset} ${bold}${cyan}║${reset}`,
21
+ `${bold}${cyan} ╚═══════════════════════════════════════════════╝${reset}`,
22
+ "",
23
+ ` ${bold}Quickstart${reset}`,
24
+ ` ${dim}import { classify } from "ovrule-lab";${reset}`,
25
+ ` ${dim}const receipt = await classify("your agent action", {${reset}`,
26
+ ` ${dim} baseUrl: "${DEFAULT_OVRULE_BASE_URL}"${reset}`,
27
+ ` ${dim}});${reset}`,
28
+ "",
29
+ ` ${bold}Methods${reset}`,
30
+ ` ${dim}classify(action, opts)${reset} — full case file audit`,
31
+ ` ${dim}guard(action, opts)${reset} — returns { allowed, decision, ... }`,
32
+ ` ${dim}verify(receipt, sig, opts)${reset} — verify a signed receipt`,
33
+ "",
34
+ ` ${bold}Docs${reset} ${yellow}${DEFAULT_OVRULE_BASE_URL}/docs${reset}`,
35
+ ` ${bold}GitHub${reset} ${yellow}https://github.com/elakumuk/decision-receipt-lab${reset}`,
36
+ "",
37
+ ].join("\n");
38
+ }
39
+ export function logBanner() {
40
+ if (isQuietModeEnabled()) {
41
+ return;
42
+ }
43
+ console.info(renderBanner());
44
+ }
45
+ export function getDefaultBaseUrl() {
46
+ return DEFAULT_OVRULE_BASE_URL;
47
+ }
package/dist/index.js CHANGED
@@ -1,11 +1,5 @@
1
- const DEFAULT_OVRULE_BASE_URL = "https://decision-receipt-lab.vercel.app";
1
+ import { getDefaultBaseUrl, isQuietModeEnabled, logBanner } from "./banner.js";
2
2
  let hasShownReadyMessage = false;
3
- function isQuietModeEnabled() {
4
- return (typeof process !== "undefined" &&
5
- typeof process.env === "object" &&
6
- typeof process.env.OVRULE_QUIET !== "undefined" &&
7
- process.env.OVRULE_QUIET !== "");
8
- }
9
3
  function getConsoleBaseUrl(baseUrl) {
10
4
  if (baseUrl) {
11
5
  return baseUrl;
@@ -13,7 +7,7 @@ function getConsoleBaseUrl(baseUrl) {
13
7
  if (typeof window !== "undefined" && window.location?.origin) {
14
8
  return window.location.origin;
15
9
  }
16
- return DEFAULT_OVRULE_BASE_URL;
10
+ return getDefaultBaseUrl();
17
11
  }
18
12
  function logSuccessfulAudit(receipt, baseUrl) {
19
13
  if (isQuietModeEnabled()) {
@@ -21,7 +15,7 @@ function logSuccessfulAudit(receipt, baseUrl) {
21
15
  }
22
16
  const caseUrl = `${getConsoleBaseUrl(baseUrl)}/case/${receipt.receiptId}`;
23
17
  if (!hasShownReadyMessage) {
24
- console.info(`[ovrule-lab v0.2.0] ready · docs: ${DEFAULT_OVRULE_BASE_URL}/docs`);
18
+ logBanner();
25
19
  hasShownReadyMessage = true;
26
20
  }
27
21
  console.info(`[ovrule-lab] ✓ audited via ${caseUrl}`);
@@ -45,11 +39,38 @@ async function readJson(response) {
45
39
  }
46
40
  return data;
47
41
  }
42
+ function parseSseBlock(block) {
43
+ const lines = block.split("\n");
44
+ const eventName = lines.find((line) => line.startsWith("event:"))?.slice(6).trim();
45
+ const data = lines
46
+ .filter((line) => line.startsWith("data:"))
47
+ .map((line) => line.slice(5).trim())
48
+ .join("\n");
49
+ if (!eventName || !data) {
50
+ return null;
51
+ }
52
+ return {
53
+ eventName,
54
+ data: JSON.parse(data),
55
+ };
56
+ }
57
+ function applySseEvent(parsed, currentReceipt) {
58
+ if (!parsed) {
59
+ return currentReceipt;
60
+ }
61
+ if (parsed.data.type === "session.error") {
62
+ throw new Error(parsed.data.message);
63
+ }
64
+ if (parsed.data.type === "analysis.completed") {
65
+ return parsed.data.receipt;
66
+ }
67
+ return currentReceipt;
68
+ }
48
69
  export class OvruleClient {
49
70
  baseUrl;
50
71
  fetchImpl;
51
72
  constructor(options = {}) {
52
- this.baseUrl = options.baseUrl?.replace(/\/$/, "") ?? DEFAULT_OVRULE_BASE_URL;
73
+ this.baseUrl = options.baseUrl?.replace(/\/$/, "") ?? getDefaultBaseUrl();
53
74
  this.fetchImpl = options.fetch ?? globalThis.fetch;
54
75
  if (!this.fetchImpl) {
55
76
  throw new Error("No fetch implementation available. Pass one in OvruleClient options.");
@@ -85,30 +106,21 @@ export class OvruleClient {
85
106
  let finalReceipt = null;
86
107
  while (true) {
87
108
  const { done, value } = await reader.read();
88
- if (done) {
89
- break;
109
+ if (value) {
110
+ buffer += decoder.decode(value, { stream: !done });
90
111
  }
91
- buffer += decoder.decode(value, { stream: true });
92
112
  const blocks = buffer.split("\n\n");
93
113
  buffer = blocks.pop() ?? "";
94
114
  for (const block of blocks) {
95
- const lines = block.split("\n");
96
- const eventName = lines.find((line) => line.startsWith("event:"))?.slice(6).trim();
97
- const data = lines
98
- .filter((line) => line.startsWith("data:"))
99
- .map((line) => line.slice(5).trim())
100
- .join("\n");
101
- if (!eventName || !data) {
102
- continue;
103
- }
104
- const parsed = JSON.parse(data);
105
- if (parsed.type === "analysis.completed") {
106
- finalReceipt = parsed.receipt;
107
- }
108
- if (parsed.type === "session.error") {
109
- throw new Error(parsed.message);
110
- }
115
+ finalReceipt = applySseEvent(parseSseBlock(block), finalReceipt);
111
116
  }
117
+ if (done) {
118
+ break;
119
+ }
120
+ }
121
+ buffer += decoder.decode();
122
+ if (buffer.trim()) {
123
+ finalReceipt = applySseEvent(parseSseBlock(buffer.trim()), finalReceipt);
112
124
  }
113
125
  if (!finalReceipt) {
114
126
  throw new Error("Ovrule classify stream finished without a final receipt.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ovrule-lab",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "TypeScript SDK for Ovrule case-file classification, guardrails, and receipt verification.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -14,8 +14,13 @@
14
14
  },
15
15
  "files": [
16
16
  "dist",
17
- "README.md"
17
+ "README.md",
18
+ "bin/ovrule-lab",
19
+ "bin/welcome.js"
18
20
  ],
21
+ "bin": {
22
+ "ovrule-lab": "bin/ovrule-lab"
23
+ },
19
24
  "scripts": {
20
25
  "build": "tsc -p tsconfig.json",
21
26
  "clean": "rm -rf dist",
@@ -33,5 +38,6 @@
33
38
  "sideEffects": false,
34
39
  "devDependencies": {
35
40
  "typescript": "^5.8.3"
36
- }
41
+ },
42
+ "author": ""
37
43
  }