@riv-io/sdk 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/README.md +122 -0
- package/dist/index.cjs +142 -0
- package/dist/index.d.cts +73 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +112 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# @riv-io/sdk
|
|
2
|
+
|
|
3
|
+
SDK TypeScript do **Riv** — governança financeira para agentes de IA. Embrulha a
|
|
4
|
+
API pública do Riv (`POST /api/v1/authorize` e `GET /api/v1/activities`) num
|
|
5
|
+
client tipado, autenticado pela chave `riv_` do agente.
|
|
6
|
+
|
|
7
|
+
É uma **casca fina**: autenticação, motor de decisão e ledger vivem no servidor
|
|
8
|
+
do Riv. O SDK só dá tipos, ergonomia e erros bem definidos.
|
|
9
|
+
|
|
10
|
+
## Quickstart
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @riv-io/sdk
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { Riv } from "@riv-io/sdk";
|
|
18
|
+
|
|
19
|
+
const riv = new Riv({ apiKey: process.env.RIV_API_KEY! });
|
|
20
|
+
const r = await riv.authorize({ amount: 49.9, currency: "BRL", description: "API OpenAI" });
|
|
21
|
+
if (r.decision === "allow") {
|
|
22
|
+
// execute o gasto
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Cinco linhas até a primeira autorização. A `RIV_API_KEY` é a credencial `riv_`
|
|
27
|
+
emitida ao conectar o agente no painel do Riv.
|
|
28
|
+
|
|
29
|
+
## API
|
|
30
|
+
|
|
31
|
+
### `new Riv(options)`
|
|
32
|
+
|
|
33
|
+
- `apiKey` (obrigatória) — credencial `riv_` do agente.
|
|
34
|
+
- `baseUrl` (opcional) — base da API do Riv. Default: `http://localhost:3000`.
|
|
35
|
+
- `timeoutMs` (opcional) — timeout por chamada. Sem default (sem timeout).
|
|
36
|
+
|
|
37
|
+
### `riv.authorize({ amount, currency, description? })`
|
|
38
|
+
|
|
39
|
+
Pergunta ao Riv se uma transação é permitida **antes** de executá-la. Retorna:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
{ decision: "allow" | "block" | "require_approval", reason: string, activityId: string }
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`block` e `require_approval` são **decisões válidas, não exceções** — o agente
|
|
46
|
+
decide o que fazer (parar, ou aguardar aprovação humana). O SDK **nunca faz
|
|
47
|
+
retry automático** de `authorize`: cada chamada grava uma entrada no ledger.
|
|
48
|
+
|
|
49
|
+
### `riv.activity(activityId)`
|
|
50
|
+
|
|
51
|
+
Consulta pontual: o registro de uma autorização no ledger (use o `activityId`
|
|
52
|
+
retornado por `authorize`). Lança `RivNotFoundError` se não existir ou
|
|
53
|
+
pertencer a outro agente.
|
|
54
|
+
|
|
55
|
+
### `riv.activities({ limit? })`
|
|
56
|
+
|
|
57
|
+
Extrato: as atividades mais recentes do agente (padrão 10, máx. 50).
|
|
58
|
+
|
|
59
|
+
## Erros
|
|
60
|
+
|
|
61
|
+
Falhas reais da chamada lançam subclasses de `RivError` (com `.code` e
|
|
62
|
+
`.status`); decisões de governança nunca lançam.
|
|
63
|
+
|
|
64
|
+
| Erro | Quando |
|
|
65
|
+
|---|---|
|
|
66
|
+
| `RivAuthError` | credencial inválida (401) |
|
|
67
|
+
| `RivInputError` | input rejeitado pelo servidor (400) |
|
|
68
|
+
| `RivNotFoundError` | atividade inexistente / de outro agente (404) |
|
|
69
|
+
| `RivNetworkError` | falha de rede ou timeout |
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { RivAuthError } from "@riv-io/sdk";
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
await riv.authorize({ amount: 10, currency: "BRL" });
|
|
76
|
+
} catch (e) {
|
|
77
|
+
if (e instanceof RivAuthError) {
|
|
78
|
+
// rotacione/configure a RIV_API_KEY
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Frameworks
|
|
84
|
+
|
|
85
|
+
- **LangChain.js** — use o [`@riv-io/langchain`](../adapters/langchain/README.md).
|
|
86
|
+
Caminho recomendado: `withRivGuard`, que embrulha sua tool de pagamento e
|
|
87
|
+
consulta o Riv antes de cada execução (determinístico, fail-closed). As tools
|
|
88
|
+
`riv_authorize`/`riv_get_activity` são o complemento para consulta ao ledger
|
|
89
|
+
e fluxos sem tool de pagamento.
|
|
90
|
+
- **CrewAI (Python)** — use o **MCP server do Riv** (`mcp/` neste repo): o
|
|
91
|
+
CrewAI suporta MCP nativamente, então o agente ganha as tools `authorize` e
|
|
92
|
+
`get_activity` sem SDK Python. Configure o server stdio com a `RIV_API_KEY`
|
|
93
|
+
do agente — instruções em [`mcp/README.md`](../mcp/README.md). Exemplo com
|
|
94
|
+
`crewai-tools`:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from crewai_tools import MCPServerAdapter
|
|
98
|
+
from mcp import StdioServerParameters
|
|
99
|
+
|
|
100
|
+
riv_mcp = StdioServerParameters(
|
|
101
|
+
command="node",
|
|
102
|
+
args=["/caminho/para/riv/mcp/dist/index.js"],
|
|
103
|
+
env={"RIV_API_KEY": "riv_...", "RIV_API_URL": "https://sua-instancia-riv"},
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
with MCPServerAdapter(riv_mcp) as tools:
|
|
107
|
+
# tools contém authorize e get_activity, prontas para os agents da crew
|
|
108
|
+
...
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Desenvolvimento
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
cd sdk
|
|
115
|
+
npm install
|
|
116
|
+
npm run build # tsup → dist/ (ESM + CJS + d.ts)
|
|
117
|
+
npm run typecheck
|
|
118
|
+
npm run test # unitários (fetch mockado)
|
|
119
|
+
|
|
120
|
+
# E2E (a partir da raiz do repo; sobe a app em porta efêmera):
|
|
121
|
+
# node --env-file=.env scripts/verify-sdk.mjs
|
|
122
|
+
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
Riv: () => Riv,
|
|
24
|
+
RivAuthError: () => RivAuthError,
|
|
25
|
+
RivError: () => RivError,
|
|
26
|
+
RivInputError: () => RivInputError,
|
|
27
|
+
RivNetworkError: () => RivNetworkError,
|
|
28
|
+
RivNotFoundError: () => RivNotFoundError
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(index_exports);
|
|
31
|
+
var RivError = class extends Error {
|
|
32
|
+
code;
|
|
33
|
+
status;
|
|
34
|
+
constructor(code, message, status) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = new.target.name;
|
|
37
|
+
this.code = code;
|
|
38
|
+
this.status = status;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var RivAuthError = class extends RivError {
|
|
42
|
+
constructor(message = "Credencial inv\xE1lida.") {
|
|
43
|
+
super("unauthorized", message, 401);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
var RivInputError = class extends RivError {
|
|
47
|
+
constructor(message) {
|
|
48
|
+
super("invalid_input", message, 400);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
var RivNotFoundError = class extends RivError {
|
|
52
|
+
constructor(message = "Atividade n\xE3o encontrada.") {
|
|
53
|
+
super("not_found", message, 404);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var RivNetworkError = class extends RivError {
|
|
57
|
+
constructor(message) {
|
|
58
|
+
super("network_error", message);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var Riv = class {
|
|
62
|
+
#apiKey;
|
|
63
|
+
#baseUrl;
|
|
64
|
+
#timeoutMs;
|
|
65
|
+
constructor(options) {
|
|
66
|
+
if (!options?.apiKey) {
|
|
67
|
+
throw new RivAuthError("apiKey \xE9 obrigat\xF3ria (credencial riv_ do agente).");
|
|
68
|
+
}
|
|
69
|
+
this.#apiKey = options.apiKey;
|
|
70
|
+
this.#baseUrl = (options.baseUrl ?? "http://localhost:3000").replace(/\/+$/, "");
|
|
71
|
+
this.#timeoutMs = options.timeoutMs;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Pergunta ao Riv se uma transação é permitida ANTES de executá-la.
|
|
75
|
+
* Sem retry automático: cada chamada grava uma entrada no ledger.
|
|
76
|
+
*/
|
|
77
|
+
async authorize(input) {
|
|
78
|
+
const data = await this.#request("POST", "/api/v1/authorize", input);
|
|
79
|
+
return data;
|
|
80
|
+
}
|
|
81
|
+
/** Consulta pontual: o registro de uma autorização pelo activityId. */
|
|
82
|
+
async activity(activityId) {
|
|
83
|
+
const qs = new URLSearchParams({ id: activityId });
|
|
84
|
+
const data = await this.#request("GET", `/api/v1/activities?${qs}`);
|
|
85
|
+
return data.activities[0];
|
|
86
|
+
}
|
|
87
|
+
/** Extrato: as atividades mais recentes do agente (padrão 10, máx. 50). */
|
|
88
|
+
async activities(options) {
|
|
89
|
+
const qs = new URLSearchParams();
|
|
90
|
+
if (options?.limit !== void 0) qs.set("limit", String(options.limit));
|
|
91
|
+
const suffix = qs.size > 0 ? `?${qs}` : "";
|
|
92
|
+
const data = await this.#request("GET", `/api/v1/activities${suffix}`);
|
|
93
|
+
return data.activities;
|
|
94
|
+
}
|
|
95
|
+
async #request(method, path, body) {
|
|
96
|
+
const controller = this.#timeoutMs !== void 0 ? new AbortController() : null;
|
|
97
|
+
const timer = controller && setTimeout(() => controller.abort(), this.#timeoutMs);
|
|
98
|
+
let res;
|
|
99
|
+
try {
|
|
100
|
+
res = await fetch(`${this.#baseUrl}${path}`, {
|
|
101
|
+
method,
|
|
102
|
+
headers: {
|
|
103
|
+
authorization: `Bearer ${this.#apiKey}`,
|
|
104
|
+
...body !== void 0 ? { "content-type": "application/json" } : {}
|
|
105
|
+
},
|
|
106
|
+
...body !== void 0 ? { body: JSON.stringify(body) } : {},
|
|
107
|
+
...controller ? { signal: controller.signal } : {}
|
|
108
|
+
});
|
|
109
|
+
} catch (err) {
|
|
110
|
+
const aborted = err instanceof Error && err.name === "AbortError";
|
|
111
|
+
throw new RivNetworkError(
|
|
112
|
+
aborted ? `Timeout de ${this.#timeoutMs}ms ao chamar o Riv em ${this.#baseUrl}.` : `Erro de rede ao chamar o Riv em ${this.#baseUrl}: ${err instanceof Error ? err.message : String(err)}`
|
|
113
|
+
);
|
|
114
|
+
} finally {
|
|
115
|
+
if (timer) clearTimeout(timer);
|
|
116
|
+
}
|
|
117
|
+
const data = await res.json().catch(() => null);
|
|
118
|
+
if (!res.ok) {
|
|
119
|
+
const message = data?.message ?? `HTTP ${res.status}`;
|
|
120
|
+
switch (res.status) {
|
|
121
|
+
case 401:
|
|
122
|
+
throw new RivAuthError(message);
|
|
123
|
+
case 400:
|
|
124
|
+
throw new RivInputError(message);
|
|
125
|
+
case 404:
|
|
126
|
+
throw new RivNotFoundError(message);
|
|
127
|
+
default:
|
|
128
|
+
throw new RivError(data?.error ?? "api_error", message, res.status);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return data;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
135
|
+
0 && (module.exports = {
|
|
136
|
+
Riv,
|
|
137
|
+
RivAuthError,
|
|
138
|
+
RivError,
|
|
139
|
+
RivInputError,
|
|
140
|
+
RivNetworkError,
|
|
141
|
+
RivNotFoundError
|
|
142
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
type Decision = "allow" | "block" | "require_approval";
|
|
2
|
+
type AuthorizeInput = {
|
|
3
|
+
/** Valor da transação (> 0, até 2 casas decimais — validado no servidor). */
|
|
4
|
+
amount: number;
|
|
5
|
+
/** Moeda da transação, ex.: "BRL", "USD". */
|
|
6
|
+
currency: string;
|
|
7
|
+
/** Descrição opcional, registrada no ledger para auditoria. */
|
|
8
|
+
description?: string;
|
|
9
|
+
};
|
|
10
|
+
type AuthorizeResult = {
|
|
11
|
+
decision: Decision;
|
|
12
|
+
reason: string;
|
|
13
|
+
/** Id do registro no ledger — use riv.activity(activityId) para consultar. */
|
|
14
|
+
activityId: string;
|
|
15
|
+
};
|
|
16
|
+
type Activity = {
|
|
17
|
+
id: string;
|
|
18
|
+
type: string;
|
|
19
|
+
amount: number;
|
|
20
|
+
currency: string;
|
|
21
|
+
rail: string | null;
|
|
22
|
+
status: string;
|
|
23
|
+
description: string | null;
|
|
24
|
+
/** ISO 8601. */
|
|
25
|
+
createdAt: string;
|
|
26
|
+
};
|
|
27
|
+
type RivOptions = {
|
|
28
|
+
/** Credencial riv_ do agente, emitida ao conectá-lo no Riv. */
|
|
29
|
+
apiKey: string;
|
|
30
|
+
/** Base da API do Riv. Default: http://localhost:3000. */
|
|
31
|
+
baseUrl?: string;
|
|
32
|
+
/** Timeout por chamada em ms. Sem default: sem timeout. */
|
|
33
|
+
timeoutMs?: number;
|
|
34
|
+
};
|
|
35
|
+
/** Base de todos os erros do SDK. Decisões (block/require_approval) NÃO lançam. */
|
|
36
|
+
declare class RivError extends Error {
|
|
37
|
+
readonly code: string;
|
|
38
|
+
readonly status?: number;
|
|
39
|
+
constructor(code: string, message: string, status?: number);
|
|
40
|
+
}
|
|
41
|
+
/** Credencial inválida (401). */
|
|
42
|
+
declare class RivAuthError extends RivError {
|
|
43
|
+
constructor(message?: string);
|
|
44
|
+
}
|
|
45
|
+
/** Input rejeitado pelo servidor (400). */
|
|
46
|
+
declare class RivInputError extends RivError {
|
|
47
|
+
constructor(message: string);
|
|
48
|
+
}
|
|
49
|
+
/** Atividade não encontrada — ou pertence a outro agente (404). */
|
|
50
|
+
declare class RivNotFoundError extends RivError {
|
|
51
|
+
constructor(message?: string);
|
|
52
|
+
}
|
|
53
|
+
/** Falha de rede ou timeout — a chamada pode nem ter chegado ao Riv. */
|
|
54
|
+
declare class RivNetworkError extends RivError {
|
|
55
|
+
constructor(message: string);
|
|
56
|
+
}
|
|
57
|
+
declare class Riv {
|
|
58
|
+
#private;
|
|
59
|
+
constructor(options: RivOptions);
|
|
60
|
+
/**
|
|
61
|
+
* Pergunta ao Riv se uma transação é permitida ANTES de executá-la.
|
|
62
|
+
* Sem retry automático: cada chamada grava uma entrada no ledger.
|
|
63
|
+
*/
|
|
64
|
+
authorize(input: AuthorizeInput): Promise<AuthorizeResult>;
|
|
65
|
+
/** Consulta pontual: o registro de uma autorização pelo activityId. */
|
|
66
|
+
activity(activityId: string): Promise<Activity>;
|
|
67
|
+
/** Extrato: as atividades mais recentes do agente (padrão 10, máx. 50). */
|
|
68
|
+
activities(options?: {
|
|
69
|
+
limit?: number;
|
|
70
|
+
}): Promise<Activity[]>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export { type Activity, type AuthorizeInput, type AuthorizeResult, type Decision, Riv, RivAuthError, RivError, RivInputError, RivNetworkError, RivNotFoundError, type RivOptions };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
type Decision = "allow" | "block" | "require_approval";
|
|
2
|
+
type AuthorizeInput = {
|
|
3
|
+
/** Valor da transação (> 0, até 2 casas decimais — validado no servidor). */
|
|
4
|
+
amount: number;
|
|
5
|
+
/** Moeda da transação, ex.: "BRL", "USD". */
|
|
6
|
+
currency: string;
|
|
7
|
+
/** Descrição opcional, registrada no ledger para auditoria. */
|
|
8
|
+
description?: string;
|
|
9
|
+
};
|
|
10
|
+
type AuthorizeResult = {
|
|
11
|
+
decision: Decision;
|
|
12
|
+
reason: string;
|
|
13
|
+
/** Id do registro no ledger — use riv.activity(activityId) para consultar. */
|
|
14
|
+
activityId: string;
|
|
15
|
+
};
|
|
16
|
+
type Activity = {
|
|
17
|
+
id: string;
|
|
18
|
+
type: string;
|
|
19
|
+
amount: number;
|
|
20
|
+
currency: string;
|
|
21
|
+
rail: string | null;
|
|
22
|
+
status: string;
|
|
23
|
+
description: string | null;
|
|
24
|
+
/** ISO 8601. */
|
|
25
|
+
createdAt: string;
|
|
26
|
+
};
|
|
27
|
+
type RivOptions = {
|
|
28
|
+
/** Credencial riv_ do agente, emitida ao conectá-lo no Riv. */
|
|
29
|
+
apiKey: string;
|
|
30
|
+
/** Base da API do Riv. Default: http://localhost:3000. */
|
|
31
|
+
baseUrl?: string;
|
|
32
|
+
/** Timeout por chamada em ms. Sem default: sem timeout. */
|
|
33
|
+
timeoutMs?: number;
|
|
34
|
+
};
|
|
35
|
+
/** Base de todos os erros do SDK. Decisões (block/require_approval) NÃO lançam. */
|
|
36
|
+
declare class RivError extends Error {
|
|
37
|
+
readonly code: string;
|
|
38
|
+
readonly status?: number;
|
|
39
|
+
constructor(code: string, message: string, status?: number);
|
|
40
|
+
}
|
|
41
|
+
/** Credencial inválida (401). */
|
|
42
|
+
declare class RivAuthError extends RivError {
|
|
43
|
+
constructor(message?: string);
|
|
44
|
+
}
|
|
45
|
+
/** Input rejeitado pelo servidor (400). */
|
|
46
|
+
declare class RivInputError extends RivError {
|
|
47
|
+
constructor(message: string);
|
|
48
|
+
}
|
|
49
|
+
/** Atividade não encontrada — ou pertence a outro agente (404). */
|
|
50
|
+
declare class RivNotFoundError extends RivError {
|
|
51
|
+
constructor(message?: string);
|
|
52
|
+
}
|
|
53
|
+
/** Falha de rede ou timeout — a chamada pode nem ter chegado ao Riv. */
|
|
54
|
+
declare class RivNetworkError extends RivError {
|
|
55
|
+
constructor(message: string);
|
|
56
|
+
}
|
|
57
|
+
declare class Riv {
|
|
58
|
+
#private;
|
|
59
|
+
constructor(options: RivOptions);
|
|
60
|
+
/**
|
|
61
|
+
* Pergunta ao Riv se uma transação é permitida ANTES de executá-la.
|
|
62
|
+
* Sem retry automático: cada chamada grava uma entrada no ledger.
|
|
63
|
+
*/
|
|
64
|
+
authorize(input: AuthorizeInput): Promise<AuthorizeResult>;
|
|
65
|
+
/** Consulta pontual: o registro de uma autorização pelo activityId. */
|
|
66
|
+
activity(activityId: string): Promise<Activity>;
|
|
67
|
+
/** Extrato: as atividades mais recentes do agente (padrão 10, máx. 50). */
|
|
68
|
+
activities(options?: {
|
|
69
|
+
limit?: number;
|
|
70
|
+
}): Promise<Activity[]>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export { type Activity, type AuthorizeInput, type AuthorizeResult, type Decision, Riv, RivAuthError, RivError, RivInputError, RivNetworkError, RivNotFoundError, type RivOptions };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var RivError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
status;
|
|
5
|
+
constructor(code, message, status) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = new.target.name;
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.status = status;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var RivAuthError = class extends RivError {
|
|
13
|
+
constructor(message = "Credencial inv\xE1lida.") {
|
|
14
|
+
super("unauthorized", message, 401);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var RivInputError = class extends RivError {
|
|
18
|
+
constructor(message) {
|
|
19
|
+
super("invalid_input", message, 400);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var RivNotFoundError = class extends RivError {
|
|
23
|
+
constructor(message = "Atividade n\xE3o encontrada.") {
|
|
24
|
+
super("not_found", message, 404);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var RivNetworkError = class extends RivError {
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super("network_error", message);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var Riv = class {
|
|
33
|
+
#apiKey;
|
|
34
|
+
#baseUrl;
|
|
35
|
+
#timeoutMs;
|
|
36
|
+
constructor(options) {
|
|
37
|
+
if (!options?.apiKey) {
|
|
38
|
+
throw new RivAuthError("apiKey \xE9 obrigat\xF3ria (credencial riv_ do agente).");
|
|
39
|
+
}
|
|
40
|
+
this.#apiKey = options.apiKey;
|
|
41
|
+
this.#baseUrl = (options.baseUrl ?? "http://localhost:3000").replace(/\/+$/, "");
|
|
42
|
+
this.#timeoutMs = options.timeoutMs;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Pergunta ao Riv se uma transação é permitida ANTES de executá-la.
|
|
46
|
+
* Sem retry automático: cada chamada grava uma entrada no ledger.
|
|
47
|
+
*/
|
|
48
|
+
async authorize(input) {
|
|
49
|
+
const data = await this.#request("POST", "/api/v1/authorize", input);
|
|
50
|
+
return data;
|
|
51
|
+
}
|
|
52
|
+
/** Consulta pontual: o registro de uma autorização pelo activityId. */
|
|
53
|
+
async activity(activityId) {
|
|
54
|
+
const qs = new URLSearchParams({ id: activityId });
|
|
55
|
+
const data = await this.#request("GET", `/api/v1/activities?${qs}`);
|
|
56
|
+
return data.activities[0];
|
|
57
|
+
}
|
|
58
|
+
/** Extrato: as atividades mais recentes do agente (padrão 10, máx. 50). */
|
|
59
|
+
async activities(options) {
|
|
60
|
+
const qs = new URLSearchParams();
|
|
61
|
+
if (options?.limit !== void 0) qs.set("limit", String(options.limit));
|
|
62
|
+
const suffix = qs.size > 0 ? `?${qs}` : "";
|
|
63
|
+
const data = await this.#request("GET", `/api/v1/activities${suffix}`);
|
|
64
|
+
return data.activities;
|
|
65
|
+
}
|
|
66
|
+
async #request(method, path, body) {
|
|
67
|
+
const controller = this.#timeoutMs !== void 0 ? new AbortController() : null;
|
|
68
|
+
const timer = controller && setTimeout(() => controller.abort(), this.#timeoutMs);
|
|
69
|
+
let res;
|
|
70
|
+
try {
|
|
71
|
+
res = await fetch(`${this.#baseUrl}${path}`, {
|
|
72
|
+
method,
|
|
73
|
+
headers: {
|
|
74
|
+
authorization: `Bearer ${this.#apiKey}`,
|
|
75
|
+
...body !== void 0 ? { "content-type": "application/json" } : {}
|
|
76
|
+
},
|
|
77
|
+
...body !== void 0 ? { body: JSON.stringify(body) } : {},
|
|
78
|
+
...controller ? { signal: controller.signal } : {}
|
|
79
|
+
});
|
|
80
|
+
} catch (err) {
|
|
81
|
+
const aborted = err instanceof Error && err.name === "AbortError";
|
|
82
|
+
throw new RivNetworkError(
|
|
83
|
+
aborted ? `Timeout de ${this.#timeoutMs}ms ao chamar o Riv em ${this.#baseUrl}.` : `Erro de rede ao chamar o Riv em ${this.#baseUrl}: ${err instanceof Error ? err.message : String(err)}`
|
|
84
|
+
);
|
|
85
|
+
} finally {
|
|
86
|
+
if (timer) clearTimeout(timer);
|
|
87
|
+
}
|
|
88
|
+
const data = await res.json().catch(() => null);
|
|
89
|
+
if (!res.ok) {
|
|
90
|
+
const message = data?.message ?? `HTTP ${res.status}`;
|
|
91
|
+
switch (res.status) {
|
|
92
|
+
case 401:
|
|
93
|
+
throw new RivAuthError(message);
|
|
94
|
+
case 400:
|
|
95
|
+
throw new RivInputError(message);
|
|
96
|
+
case 404:
|
|
97
|
+
throw new RivNotFoundError(message);
|
|
98
|
+
default:
|
|
99
|
+
throw new RivError(data?.error ?? "api_error", message, res.status);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return data;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
export {
|
|
106
|
+
Riv,
|
|
107
|
+
RivAuthError,
|
|
108
|
+
RivError,
|
|
109
|
+
RivInputError,
|
|
110
|
+
RivNetworkError,
|
|
111
|
+
RivNotFoundError
|
|
112
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@riv-io/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "SDK TypeScript do Riv — autorização de gastos e consulta do ledger para agentes de IA.",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"prepublishOnly": "npm run typecheck && npm run test && npm run build",
|
|
21
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
22
|
+
"typecheck": "tsc --noEmit",
|
|
23
|
+
"lint": "tsc --noEmit",
|
|
24
|
+
"test": "vitest run"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^20",
|
|
28
|
+
"tsup": "^8",
|
|
29
|
+
"typescript": "^5",
|
|
30
|
+
"vitest": "^4.1.8"
|
|
31
|
+
}
|
|
32
|
+
}
|