oceum 0.1.1 → 0.2.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 +82 -1
- package/index.d.ts +49 -0
- package/index.js +175 -52
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# oceum
|
|
2
2
|
|
|
3
|
-
Official SDK for [Oceum](https://oceum.ai) — the
|
|
3
|
+
Official SDK for [Oceum](https://oceum.ai) — the command layer for AI agents. Monitor, log, track costs, and orchestrate your agents from one dashboard.
|
|
4
4
|
|
|
5
5
|
Zero dependencies. Works with Node.js 18+, Deno, and Bun.
|
|
6
6
|
|
|
@@ -30,11 +30,56 @@ const result = await client.wrap('Process leads', async () => {
|
|
|
30
30
|
return leads.length;
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
+
// Report LLM usage for cost tracking
|
|
34
|
+
await client.reportUsage({
|
|
35
|
+
model: 'claude-sonnet',
|
|
36
|
+
tokensInput: 1200,
|
|
37
|
+
tokensOutput: 450,
|
|
38
|
+
});
|
|
39
|
+
|
|
33
40
|
// Clean up
|
|
34
41
|
client.stopHeartbeat();
|
|
35
42
|
await client.setStatus('idle');
|
|
36
43
|
```
|
|
37
44
|
|
|
45
|
+
## No SDK? No Problem
|
|
46
|
+
|
|
47
|
+
Oceum is just HTTP. Any language, any framework — POST JSON to the webhook endpoint:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Heartbeat
|
|
51
|
+
curl -X POST https://oceum.ai/api/webhook \
|
|
52
|
+
-H "Authorization: Bearer $OCEUM_API_KEY" \
|
|
53
|
+
-H "Content-Type: application/json" \
|
|
54
|
+
-d '{"event":"heartbeat","agentId":"agt_xxx"}'
|
|
55
|
+
|
|
56
|
+
# Report a completed task
|
|
57
|
+
curl -X POST https://oceum.ai/api/webhook \
|
|
58
|
+
-H "Authorization: Bearer $OCEUM_API_KEY" \
|
|
59
|
+
-H "Content-Type: application/json" \
|
|
60
|
+
-d '{"event":"task_complete","agentId":"agt_xxx","data":{"taskName":"sync-orders"}}'
|
|
61
|
+
|
|
62
|
+
# Report LLM usage
|
|
63
|
+
curl -X POST https://oceum.ai/api/webhook \
|
|
64
|
+
-H "Authorization: Bearer $OCEUM_API_KEY" \
|
|
65
|
+
-H "Content-Type: application/json" \
|
|
66
|
+
-d '{"event":"usage","agentId":"agt_xxx","data":{"model":"gpt-4o","tokensInput":500,"tokensOutput":200}}'
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Python:**
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
import requests
|
|
73
|
+
|
|
74
|
+
API_KEY = "oc_xxx"
|
|
75
|
+
AGENT_ID = "agt_xxx"
|
|
76
|
+
|
|
77
|
+
requests.post("https://oceum.ai/api/webhook",
|
|
78
|
+
headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
|
|
79
|
+
json={"event": "heartbeat", "agentId": AGENT_ID}
|
|
80
|
+
)
|
|
81
|
+
```
|
|
82
|
+
|
|
38
83
|
## API
|
|
39
84
|
|
|
40
85
|
| Method | Description |
|
|
@@ -48,6 +93,34 @@ await client.setStatus('idle');
|
|
|
48
93
|
| `startHeartbeat(ms?)` | Auto-heartbeat every N ms (default: 60s) |
|
|
49
94
|
| `stopHeartbeat()` | Stop auto-heartbeat |
|
|
50
95
|
| `wrap(name, fn, meta?)` | Auto start/complete/error around async fn |
|
|
96
|
+
| `reportUsage(opts?)` | Report LLM token usage for cost tracking |
|
|
97
|
+
| `memory(content, opts?)` | Write shared memory visible to peer agents |
|
|
98
|
+
| `readMemory(opts?)` | Read shared memory entries |
|
|
99
|
+
| `vaultStore(data, opts?)` | Store sensitive data, returns vault token |
|
|
100
|
+
| `vaultRetrieve(token)` | Retrieve data using vault token |
|
|
101
|
+
| `vaultProxy(token, req)` | Zero-knowledge API call with vault credential |
|
|
102
|
+
| `vaultRevoke(token)` | Permanently revoke a vault token |
|
|
103
|
+
| `vaultList(opts?)` | List vault tokens (metadata only) |
|
|
104
|
+
|
|
105
|
+
## Cost Tracking
|
|
106
|
+
|
|
107
|
+
Report LLM token usage per call. Oceum calculates cost using configurable model pricing and enforces budget caps automatically.
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
// After an LLM call
|
|
111
|
+
await client.reportUsage({
|
|
112
|
+
model: 'claude-haiku',
|
|
113
|
+
tokensInput: 800,
|
|
114
|
+
tokensOutput: 200,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Or include usage in task_complete meta for automatic tracking
|
|
118
|
+
await client.taskComplete('generate-report', {
|
|
119
|
+
usage: { model: 'gpt-4o', tokensInput: 2000, tokensOutput: 1500 }
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Budget caps, alert thresholds, and per-model pricing are configured in the Oceum dashboard under Settings > Cost Controls.
|
|
51
124
|
|
|
52
125
|
## Error Handling
|
|
53
126
|
|
|
@@ -64,6 +137,14 @@ try {
|
|
|
64
137
|
}
|
|
65
138
|
```
|
|
66
139
|
|
|
140
|
+
## Webhook Verification
|
|
141
|
+
|
|
142
|
+
Verify incoming webhook signatures with HMAC-SHA256:
|
|
143
|
+
|
|
144
|
+
```javascript
|
|
145
|
+
const valid = Oceum.verifyWebhookSignature(rawBody, signature, secret);
|
|
146
|
+
```
|
|
147
|
+
|
|
67
148
|
## Docs
|
|
68
149
|
|
|
69
150
|
Full documentation: [oceum.ai/docs](https://oceum.ai/docs.html)
|
package/index.d.ts
CHANGED
|
@@ -70,6 +70,9 @@ export class Oceum {
|
|
|
70
70
|
/** Wrap an async function with automatic task_start, task_complete, and error reporting. */
|
|
71
71
|
wrap<T>(taskName: string, fn: () => T | Promise<T>, meta?: Record<string, unknown>): Promise<T>;
|
|
72
72
|
|
|
73
|
+
/** Report LLM token usage for cost tracking and budget enforcement. */
|
|
74
|
+
reportUsage(options?: UsageReportOptions): Promise<UsageReportResponse>;
|
|
75
|
+
|
|
73
76
|
/** Write a shared memory entry visible to peer agents. */
|
|
74
77
|
memory(content: string, options?: MemoryWriteOptions): Promise<WebhookResponse>;
|
|
75
78
|
|
|
@@ -82,11 +85,23 @@ export class Oceum {
|
|
|
82
85
|
/** Retrieve sensitive data from the Vault using a vault token. */
|
|
83
86
|
vaultRetrieve(token: string): Promise<VaultRetrieveResponse>;
|
|
84
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Zero-Knowledge Vault Proxy — make an API call with a vault-stored credential
|
|
90
|
+
* without ever seeing the raw secret.
|
|
91
|
+
*/
|
|
92
|
+
vaultProxy(token: string, request: VaultProxyRequest): Promise<VaultProxyResponse>;
|
|
93
|
+
|
|
85
94
|
/** Revoke a vault token permanently. */
|
|
86
95
|
vaultRevoke(token: string): Promise<{ ok: boolean; token: string; revoked: boolean }>;
|
|
87
96
|
|
|
88
97
|
/** List vault tokens (metadata only). */
|
|
89
98
|
vaultList(options?: VaultListOptions): Promise<VaultListResponse>;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Verify a webhook signature using HMAC-SHA256 with timing-safe comparison.
|
|
102
|
+
* @returns true if the signature is valid
|
|
103
|
+
*/
|
|
104
|
+
static verifyWebhookSignature(payload: string, signature: string, secret: string): boolean;
|
|
90
105
|
}
|
|
91
106
|
|
|
92
107
|
export interface MemoryWriteOptions {
|
|
@@ -130,6 +145,22 @@ export interface VaultStoreOptions {
|
|
|
130
145
|
allowedAgents?: string;
|
|
131
146
|
ttl?: '1h' | '6h' | '24h' | '7d' | '30d' | 'permanent';
|
|
132
147
|
label?: string;
|
|
148
|
+
targetDomains?: string[];
|
|
149
|
+
injectionTemplate?: string;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface VaultProxyRequest {
|
|
153
|
+
method: string;
|
|
154
|
+
url: string;
|
|
155
|
+
headers?: Record<string, string>;
|
|
156
|
+
body?: unknown;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface VaultProxyResponse {
|
|
160
|
+
ok: boolean;
|
|
161
|
+
status: number;
|
|
162
|
+
headers: Record<string, string>;
|
|
163
|
+
data: unknown;
|
|
133
164
|
}
|
|
134
165
|
|
|
135
166
|
export interface VaultStoreResponse {
|
|
@@ -171,3 +202,21 @@ export interface VaultListResponse {
|
|
|
171
202
|
entries: VaultEntry[];
|
|
172
203
|
count: number;
|
|
173
204
|
}
|
|
205
|
+
|
|
206
|
+
export interface UsageReportOptions {
|
|
207
|
+
/** Model name (e.g. 'claude-haiku', 'gpt-4o') */
|
|
208
|
+
model?: string;
|
|
209
|
+
/** Input/prompt tokens */
|
|
210
|
+
tokensInput?: number;
|
|
211
|
+
/** Output/completion tokens */
|
|
212
|
+
tokensOutput?: number;
|
|
213
|
+
/** Additional metadata */
|
|
214
|
+
meta?: Record<string, unknown>;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface UsageReportResponse {
|
|
218
|
+
ok: boolean;
|
|
219
|
+
event: string;
|
|
220
|
+
agentId: string;
|
|
221
|
+
costUsd: number;
|
|
222
|
+
}
|
package/index.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
|
|
5
|
+
const MAX_RETRIES = 3;
|
|
6
|
+
const BACKOFF_BASE_MS = 1000;
|
|
7
|
+
|
|
3
8
|
class OceumError extends Error {
|
|
4
9
|
constructor(message, statusCode, body) {
|
|
5
10
|
super(message);
|
|
@@ -31,33 +36,146 @@ class Oceum {
|
|
|
31
36
|
this.#heartbeatTimer = null;
|
|
32
37
|
}
|
|
33
38
|
|
|
39
|
+
// ── Helpers ─────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
#sleep(ms) {
|
|
42
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
43
|
+
}
|
|
44
|
+
|
|
34
45
|
// ── Core transport ─────────────────────────────────
|
|
35
46
|
|
|
36
47
|
async #send(event, data = {}) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
let lastError;
|
|
49
|
+
|
|
50
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
51
|
+
try {
|
|
52
|
+
const res = await fetch(`${this.#baseUrl}/api/webhook`, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: {
|
|
55
|
+
'Content-Type': 'application/json',
|
|
56
|
+
'Authorization': `Bearer ${this.#apiKey}`,
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify({
|
|
59
|
+
event,
|
|
60
|
+
agentId: this.#agentId,
|
|
61
|
+
data,
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// 429 — respect Retry-After header
|
|
66
|
+
if (res.status === 429 && attempt < MAX_RETRIES) {
|
|
67
|
+
const retryAfter = parseInt(res.headers.get('Retry-After'), 10);
|
|
68
|
+
const waitMs = (retryAfter > 0 ? retryAfter : 1) * 1000;
|
|
69
|
+
await this.#sleep(waitMs);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 5xx — exponential backoff
|
|
74
|
+
if (res.status >= 500 && attempt < MAX_RETRIES) {
|
|
75
|
+
await this.#sleep(BACKOFF_BASE_MS * Math.pow(2, attempt));
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const body = await res.json().catch(() => ({}));
|
|
80
|
+
|
|
81
|
+
if (!res.ok) {
|
|
82
|
+
throw new OceumError(
|
|
83
|
+
body.error || `HTTP ${res.status}`,
|
|
84
|
+
res.status,
|
|
85
|
+
body
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return body;
|
|
90
|
+
} catch (err) {
|
|
91
|
+
lastError = err;
|
|
92
|
+
// If it's already an OceumError (non-retryable status), rethrow immediately
|
|
93
|
+
if (err instanceof OceumError) throw err;
|
|
94
|
+
// Network error — retry with backoff
|
|
95
|
+
if (attempt < MAX_RETRIES) {
|
|
96
|
+
await this.#sleep(BACKOFF_BASE_MS * Math.pow(2, attempt));
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
49
101
|
|
|
50
|
-
|
|
102
|
+
throw lastError instanceof OceumError
|
|
103
|
+
? lastError
|
|
104
|
+
: new OceumError(lastError?.message || 'Request failed after retries', 0, {});
|
|
105
|
+
}
|
|
51
106
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
107
|
+
async #get(url) {
|
|
108
|
+
let lastError;
|
|
109
|
+
|
|
110
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
111
|
+
try {
|
|
112
|
+
const res = await fetch(url, {
|
|
113
|
+
headers: { 'Authorization': `Bearer ${this.#apiKey}` },
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// 429 — respect Retry-After header
|
|
117
|
+
if (res.status === 429 && attempt < MAX_RETRIES) {
|
|
118
|
+
const retryAfter = parseInt(res.headers.get('Retry-After'), 10);
|
|
119
|
+
const waitMs = (retryAfter > 0 ? retryAfter : 1) * 1000;
|
|
120
|
+
await this.#sleep(waitMs);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 5xx — exponential backoff
|
|
125
|
+
if (res.status >= 500 && attempt < MAX_RETRIES) {
|
|
126
|
+
await this.#sleep(BACKOFF_BASE_MS * Math.pow(2, attempt));
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const body = await res.json().catch(() => ({}));
|
|
131
|
+
|
|
132
|
+
if (!res.ok) {
|
|
133
|
+
throw new OceumError(
|
|
134
|
+
body.error || `HTTP ${res.status}`,
|
|
135
|
+
res.status,
|
|
136
|
+
body
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return body;
|
|
141
|
+
} catch (err) {
|
|
142
|
+
lastError = err;
|
|
143
|
+
if (err instanceof OceumError) throw err;
|
|
144
|
+
if (attempt < MAX_RETRIES) {
|
|
145
|
+
await this.#sleep(BACKOFF_BASE_MS * Math.pow(2, attempt));
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
58
149
|
}
|
|
59
150
|
|
|
60
|
-
|
|
151
|
+
throw lastError instanceof OceumError
|
|
152
|
+
? lastError
|
|
153
|
+
: new OceumError(lastError?.message || 'Request failed after retries', 0, {});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ── Webhook signature verification ──────────────────
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Verify a webhook signature using HMAC-SHA256 with timing-safe comparison.
|
|
160
|
+
* @param {string} payload - Raw request body string
|
|
161
|
+
* @param {string} signature - Signature from the X-Oceum-Signature header
|
|
162
|
+
* @param {string} secret - Your webhook signing secret
|
|
163
|
+
* @returns {boolean}
|
|
164
|
+
*/
|
|
165
|
+
static verifyWebhookSignature(payload, signature, secret) {
|
|
166
|
+
const expected = crypto
|
|
167
|
+
.createHmac('sha256', secret)
|
|
168
|
+
.update(payload)
|
|
169
|
+
.digest('hex');
|
|
170
|
+
try {
|
|
171
|
+
return crypto.timingSafeEqual(
|
|
172
|
+
Buffer.from(expected, 'utf8'),
|
|
173
|
+
Buffer.from(signature, 'utf8')
|
|
174
|
+
);
|
|
175
|
+
} catch {
|
|
176
|
+
// Lengths differ — not equal
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
61
179
|
}
|
|
62
180
|
|
|
63
181
|
// ── Events ─────────────────────────────────────────
|
|
@@ -187,6 +305,20 @@ class Oceum {
|
|
|
187
305
|
}
|
|
188
306
|
}
|
|
189
307
|
|
|
308
|
+
// ── Usage / Cost Tracking ──────────────────────────────
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Report LLM token usage for cost tracking and budget enforcement.
|
|
312
|
+
* @param {Object} options
|
|
313
|
+
* @param {string} [options.model] - Model name (e.g. 'claude-haiku', 'gpt-4o')
|
|
314
|
+
* @param {number} [options.tokensInput=0] - Input/prompt tokens
|
|
315
|
+
* @param {number} [options.tokensOutput=0] - Output/completion tokens
|
|
316
|
+
* @param {Object} [options.meta] - Additional metadata
|
|
317
|
+
*/
|
|
318
|
+
async reportUsage({ model, tokensInput = 0, tokensOutput = 0, meta } = {}) {
|
|
319
|
+
return this.#send('usage', { model, tokensInput, tokensOutput, meta });
|
|
320
|
+
}
|
|
321
|
+
|
|
190
322
|
// ── Shared Memory ────────────────────────────────────
|
|
191
323
|
|
|
192
324
|
/**
|
|
@@ -221,21 +353,7 @@ class Oceum {
|
|
|
221
353
|
if (tag) params.set('tag', tag);
|
|
222
354
|
params.set('limit', String(limit));
|
|
223
355
|
|
|
224
|
-
|
|
225
|
-
headers: { 'Authorization': `Bearer ${this.#apiKey}` },
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
const body = await res.json().catch(() => ({}));
|
|
229
|
-
|
|
230
|
-
if (!res.ok) {
|
|
231
|
-
throw new OceumError(
|
|
232
|
-
body.error || `HTTP ${res.status}`,
|
|
233
|
-
res.status,
|
|
234
|
-
body
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
return body;
|
|
356
|
+
return this.#get(`${this.#baseUrl}/api/memory?${params}`);
|
|
239
357
|
}
|
|
240
358
|
|
|
241
359
|
// ── Vault (Secure Data) ──────────────────────────
|
|
@@ -250,9 +368,11 @@ class Oceum {
|
|
|
250
368
|
* @param {string} [options.allowedAgents] - Comma-separated agent IDs (for specified_agents)
|
|
251
369
|
* @param {'1h'|'6h'|'24h'|'7d'|'30d'|'permanent'} [options.ttl='30d']
|
|
252
370
|
* @param {string} [options.label] - Human-readable label
|
|
371
|
+
* @param {string} [options.targetDomains] - Comma-separated domain allowlist for vault proxy (e.g. 'api.stripe.com,api.openai.com')
|
|
372
|
+
* @param {string} [options.injectionTemplate] - How to inject the secret into proxied requests (default: 'header:Authorization:Bearer {{secret}}')
|
|
253
373
|
*/
|
|
254
|
-
async vaultStore(data, { category = 'custom', accessPolicy = 'source_agent', allowedAgents, ttl = '30d', label } = {}) {
|
|
255
|
-
return this.#send('vault_store', { data, category, accessPolicy, allowedAgents, ttl, label });
|
|
374
|
+
async vaultStore(data, { category = 'custom', accessPolicy = 'source_agent', allowedAgents, ttl = '30d', label, targetDomains, injectionTemplate } = {}) {
|
|
375
|
+
return this.#send('vault_store', { data, category, accessPolicy, allowedAgents, ttl, label, targetDomains, injectionTemplate });
|
|
256
376
|
}
|
|
257
377
|
|
|
258
378
|
/**
|
|
@@ -263,6 +383,23 @@ class Oceum {
|
|
|
263
383
|
return this.#send('vault_retrieve', { token });
|
|
264
384
|
}
|
|
265
385
|
|
|
386
|
+
/**
|
|
387
|
+
* Zero-Knowledge Vault Proxy — make an API call with a vault-stored credential
|
|
388
|
+
* without ever seeing the raw secret. The platform decrypts and injects the
|
|
389
|
+
* credential into the outgoing request, then returns only the API response.
|
|
390
|
+
*
|
|
391
|
+
* @param {string} token - Vault token (vtk_xxx) containing the credential
|
|
392
|
+
* @param {Object} request - The outgoing HTTP request specification
|
|
393
|
+
* @param {string} request.method - HTTP method (GET, POST, PUT, DELETE, PATCH)
|
|
394
|
+
* @param {string} request.url - Target API URL (must match token's target_domains allowlist)
|
|
395
|
+
* @param {Object} [request.headers] - Additional headers (credential injected separately via template)
|
|
396
|
+
* @param {*} [request.body] - Request body (object for JSON, string for raw)
|
|
397
|
+
* @returns {{ ok: boolean, status: number, headers: Object, data: * }}
|
|
398
|
+
*/
|
|
399
|
+
async vaultProxy(token, { method, url, headers, body } = {}) {
|
|
400
|
+
return this.#send('vault_proxy', { token, method, url, headers, body });
|
|
401
|
+
}
|
|
402
|
+
|
|
266
403
|
/**
|
|
267
404
|
* Revoke a vault token, permanently preventing future retrievals.
|
|
268
405
|
* @param {string} token - Vault token (vtk_xxx)
|
|
@@ -303,21 +440,7 @@ class Oceum {
|
|
|
303
440
|
params.set('status', status);
|
|
304
441
|
params.set('limit', String(limit));
|
|
305
442
|
|
|
306
|
-
|
|
307
|
-
headers: { 'Authorization': `Bearer ${this.#apiKey}` },
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
const body = await res.json().catch(() => ({}));
|
|
311
|
-
|
|
312
|
-
if (!res.ok) {
|
|
313
|
-
throw new OceumError(
|
|
314
|
-
body.error || `HTTP ${res.status}`,
|
|
315
|
-
res.status,
|
|
316
|
-
body
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
return body;
|
|
443
|
+
return this.#get(`${this.#baseUrl}/api/vault?${params}`);
|
|
321
444
|
}
|
|
322
445
|
|
|
323
446
|
// ── Getters ────────────────────────────────────────
|
package/package.json
CHANGED