@kivel.io/sdk 0.6.1 → 0.9.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/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @kivel.io/sdk
2
2
 
3
+ ## 0.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - fde4ed4: Show clear messages and structured details for plan allowances and request rate limits.
8
+ - fde4ed4: Type the usage response, including workspace allowance counters and reset times when the server provides them.
9
+
10
+ ### Patch Changes
11
+
12
+ - fde4ed4: Read the machine code from the new `code` field while retaining support for older servers. The HTTP `error` field remains a readable message. Exhausted hosted tool, email, and resource allowances return 402. Request rate limits return 429. Timed limits include `Retry-After`, which the SDK and CLI expose to callers.
13
+
14
+ ## 0.8.0
15
+
16
+ ### Minor Changes
17
+
18
+ - 7290344: Add test wallets, x402 paywalls and mock checkouts with approval-aware payments, ledgers and test outcome controls.
19
+
20
+ ## 0.7.0
21
+
22
+ ### Minor Changes
23
+
24
+ - 7cbd3fc: Add workspace credential storage, password generation and approved reveals.
25
+
3
26
  ## 0.6.1
4
27
 
5
28
  ### Patch Changes
package/README.md CHANGED
@@ -44,7 +44,7 @@ write a local credential file. Do not log responses that contain keys or tokens.
44
44
 
45
45
  [Method reference](https://kivel.io/docs/sdk/) ·
46
46
  [Agent workflows](https://kivel.io/docs/agent-workflows/) ·
47
- [Plan limits](https://kivel.io/plans/)
47
+ [Plan limits](https://kivel.io/pricing/)
48
48
 
49
49
  Licensed under Apache-2.0. Support: support@kivel.io.
50
50
 
@@ -85,3 +85,14 @@ await kivel.deleteOAuthProvider(provider.id);
85
85
  `oauthProviders()` lists workspace providers; `updateOAuthProvider(id, config)` replaces
86
86
  the users, claims and test scenario. Endpoint use is public; management stays private.
87
87
  See the [OAuth guide](https://kivel.io/docs/mock-oauth/) for token flows and failure scenarios.
88
+
89
+ ## Workspace vault
90
+
91
+ Use `saveCredential({ name, url, username })` to generate and store a password, or
92
+ include `password` to save one you already have. The generated value is returned
93
+ once. `credentials()` and `credential(id)` return metadata.
94
+ `revealCredential(id)` requests human approval by default. Wait with
95
+ `waitForApproval(approvalId)`, then repeat the reveal with that approval ID.
96
+ Each approval permits one reveal by the same caller. Only signed-in owners and
97
+ admins can change `agentMayRead`. Use `generatePassword(length)` for a standalone
98
+ password, `updateCredential(id, changes)` to edit, and `deleteCredential(id)` to remove it.
package/dist/index.d.ts CHANGED
@@ -51,13 +51,39 @@ export interface Execution {
51
51
  resetsAt: string;
52
52
  };
53
53
  }
54
+ export interface KivelUsage {
55
+ plan: 'anonymous' | 'free' | 'pro' | 'team';
56
+ limits: Record<string, unknown>;
57
+ runsToday: number;
58
+ resetsAt: string;
59
+ period?: {
60
+ start: string;
61
+ end: string;
62
+ };
63
+ items?: {
64
+ key: string;
65
+ label: string;
66
+ unit: 'count' | 'bytes';
67
+ used: number;
68
+ max: number;
69
+ resetsAt?: string;
70
+ }[];
71
+ }
72
+ export interface KivelLimitDetails {
73
+ code: 'limit_reached' | 'rate_limited';
74
+ limit: string;
75
+ used?: number;
76
+ max?: number;
77
+ plan?: KivelUsage['plan'];
78
+ }
54
79
  export declare class KivelError extends Error {
55
80
  readonly status: number;
56
81
  readonly requestId?: string | undefined;
57
82
  readonly retryAfter?: number | undefined;
58
83
  readonly requiredPlan?: string | undefined;
59
84
  readonly upgradeUrl?: string | undefined;
60
- constructor(message: string, status: number, requestId?: string | undefined, retryAfter?: number | undefined, requiredPlan?: string | undefined, upgradeUrl?: string | undefined);
85
+ readonly limitDetails?: KivelLimitDetails | undefined;
86
+ constructor(message: string, status: number, requestId?: string | undefined, retryAfter?: number | undefined, requiredPlan?: string | undefined, upgradeUrl?: string | undefined, limitDetails?: KivelLimitDetails | undefined);
61
87
  }
62
88
  export declare class Kivel {
63
89
  private options;
@@ -123,6 +149,57 @@ export declare class Kivel {
123
149
  deleteAuthenticator(id: string): Promise<void>;
124
150
  getEmailAttachment(id: string, messageId: string, attachmentId: string): Promise<EmailAttachmentLink>;
125
151
  downloadEmailAttachment(id: string, messageId: string, attachmentId: string): Promise<Uint8Array>;
152
+ createTestWallet(input: import('./types.js').TestWalletInput): Promise<import("./types.js").TestPaymentResource>;
153
+ createTestPaywall(input: import('./types.js').TestPaywallInput): Promise<import("./types.js").TestPaymentResource>;
154
+ createTestCheckout(input: import('./types.js').TestCheckoutInput): Promise<import("./types.js").TestPaymentResource & {
155
+ webhookSecret: string;
156
+ }>;
157
+ testPayments(kind: 'wallets' | 'paywalls' | 'checkouts'): Promise<{
158
+ testMode: true;
159
+ resources: import('./types.js').TestPaymentResource[];
160
+ }>;
161
+ testPayment(kind: 'wallets' | 'paywalls' | 'checkouts', id: string): Promise<import("./types.js").TestPaymentResource>;
162
+ updateTestPayment(kind: 'wallets' | 'paywalls' | 'checkouts', id: string, input: Partial<import('./types.js').TestWalletInput | import('./types.js').TestPaywallInput | import('./types.js').TestCheckoutInput>): Promise<import("./types.js").TestPaymentResource>;
163
+ deleteTestPayment(kind: 'wallets' | 'paywalls' | 'checkouts', id: string): Promise<void>;
164
+ walletBalance(id: string): Promise<import("./types.js").TestPaymentResource>;
165
+ topUpTestWallet(id: string, amount: number): Promise<{
166
+ testMode: true;
167
+ balance: number;
168
+ transaction: import('./types.js').TestTransaction;
169
+ }>;
170
+ walletPay(id: string, input: import('./types.js').TestPaymentInput): Promise<import("./types.js").TestPaymentResult>;
171
+ listWalletTransactions(id: string, options?: {
172
+ cursor?: string;
173
+ limit?: number;
174
+ }): Promise<{
175
+ testMode: true;
176
+ transactions: import('./types.js').TestTransaction[];
177
+ nextCursor?: string;
178
+ }>;
179
+ completeTestCheckout(id: string, input: {
180
+ idempotencyKey: string;
181
+ card: {
182
+ number: string;
183
+ expiry?: string;
184
+ cvc?: string;
185
+ };
186
+ }): Promise<import("./types.js").TestCheckoutAttempt>;
187
+ confirmTestCheckout(id: string, confirmationToken: string): Promise<import("./types.js").TestCheckoutAttempt>;
188
+ saveCredential(input: import('./types.js').CredentialInput): Promise<{
189
+ credential: import('./types.js').Credential;
190
+ password?: string;
191
+ }>;
192
+ credentials(): Promise<{
193
+ credentials: import('./types.js').Credential[];
194
+ limit: number;
195
+ }>;
196
+ credential(id: string): Promise<import("./types.js").Credential>;
197
+ updateCredential(id: string, input: Partial<import('./types.js').CredentialInput>): Promise<import("./types.js").Credential>;
198
+ deleteCredential(id: string): Promise<void>;
199
+ revealCredential(id: string, approvalId?: string): Promise<import("./types.js").CredentialReveal>;
200
+ generatePassword(length?: number): Promise<{
201
+ password: string;
202
+ }>;
126
203
  createEmailInbox(settings?: import('./types.js').EmailInboxSettings): Promise<EmailInbox>;
127
204
  emailInboxes(): Promise<{
128
205
  inboxes: EmailInbox[];
@@ -304,7 +381,7 @@ export declare class Kivel {
304
381
  inboxDeliveries(id: string, token?: string): Promise<unknown>;
305
382
  private waitTimeout;
306
383
  private waitQuery;
307
- usage(): Promise<unknown>;
384
+ usage(): Promise<KivelUsage>;
308
385
  resources(): Promise<unknown>;
309
386
  /** Senders can POST to webhookUrl without credentials by default. Inspection stays private.
310
387
  * Set visibility to private to require X-Access-Token on incoming webhooks. */
package/dist/index.js CHANGED
@@ -5,13 +5,15 @@ export class KivelError extends Error {
5
5
  retryAfter;
6
6
  requiredPlan;
7
7
  upgradeUrl;
8
- constructor(message, status, requestId, retryAfter, requiredPlan, upgradeUrl) {
8
+ limitDetails;
9
+ constructor(message, status, requestId, retryAfter, requiredPlan, upgradeUrl, limitDetails) {
9
10
  super(message);
10
11
  this.status = status;
11
12
  this.requestId = requestId;
12
13
  this.retryAfter = retryAfter;
13
14
  this.requiredPlan = requiredPlan;
14
15
  this.upgradeUrl = upgradeUrl;
16
+ this.limitDetails = limitDetails;
15
17
  this.name = 'KivelError';
16
18
  }
17
19
  }
@@ -85,23 +87,25 @@ export class Kivel {
85
87
  throw new KivelError('The server returned an invalid response.', response.status, response.headers.get('x-request-id') ?? undefined);
86
88
  }
87
89
  if (!response.ok) {
88
- const message = value &&
89
- typeof value === 'object' &&
90
- 'error' in value &&
91
- typeof value.error === 'string'
92
- ? value.error
93
- : `Request failed (${response.status}).`;
94
- throw new KivelError(message, response.status, response.headers.get('x-request-id') ?? undefined, Number(response.headers.get('retry-after')) || undefined, value &&
95
- typeof value === 'object' &&
96
- 'requiredPlan' in value &&
97
- typeof value.requiredPlan === 'string'
98
- ? value.requiredPlan
99
- : undefined, value &&
100
- typeof value === 'object' &&
101
- 'upgradeUrl' in value &&
102
- typeof value.upgradeUrl === 'string'
103
- ? value.upgradeUrl
104
- : undefined);
90
+ const error = value && typeof value === 'object' ? value : {};
91
+ const code = error.code ?? error.error;
92
+ const details = (code === 'limit_reached' || code === 'rate_limited') &&
93
+ typeof error.limit === 'string'
94
+ ? {
95
+ code,
96
+ limit: error.limit,
97
+ ...(typeof error.used === 'number' ? { used: error.used } : {}),
98
+ ...(typeof error.max === 'number' ? { max: error.max } : {}),
99
+ ...(['anonymous', 'free', 'pro', 'team'].some((plan) => plan === error.plan)
100
+ ? { plan: error.plan }
101
+ : {}),
102
+ }
103
+ : undefined;
104
+ throw new KivelError(typeof error.message === 'string'
105
+ ? error.message
106
+ : typeof error.error === 'string'
107
+ ? error.error
108
+ : `Request failed (${response.status}).`, response.status, response.headers.get('x-request-id') ?? undefined, Number(response.headers.get('retry-after')) || undefined, typeof error.requiredPlan === 'string' ? error.requiredPlan : undefined, typeof error.upgradeUrl === 'string' ? error.upgradeUrl : undefined, details);
105
109
  }
106
110
  return value;
107
111
  }
@@ -170,6 +174,71 @@ export class Kivel {
170
174
  throw new KivelError('Attachment link expired or unavailable.', response.status, response.headers.get('x-request-id') ?? undefined);
171
175
  return new Uint8Array(await response.arrayBuffer());
172
176
  }
177
+ createTestWallet(input) {
178
+ return this.request('POST', '/api/test-wallets', input);
179
+ }
180
+ createTestPaywall(input) {
181
+ return this.request('POST', '/api/test-paywalls', input);
182
+ }
183
+ createTestCheckout(input) {
184
+ return this.request('POST', '/api/test-checkouts', input);
185
+ }
186
+ testPayments(kind) {
187
+ return this.request('GET', `/api/test-${kind}`);
188
+ }
189
+ testPayment(kind, id) {
190
+ return this.request('GET', `/api/test-${kind}/${encodeURIComponent(id)}`);
191
+ }
192
+ updateTestPayment(kind, id, input) {
193
+ return this.request('PATCH', `/api/test-${kind}/${encodeURIComponent(id)}`, input);
194
+ }
195
+ deleteTestPayment(kind, id) {
196
+ return this.request('DELETE', `/api/test-${kind}/${encodeURIComponent(id)}`);
197
+ }
198
+ walletBalance(id) {
199
+ return this.testPayment('wallets', id);
200
+ }
201
+ topUpTestWallet(id, amount) {
202
+ return this.request('POST', `/api/test-wallets/${encodeURIComponent(id)}/top-up`, { amount });
203
+ }
204
+ walletPay(id, input) {
205
+ return this.request('POST', `/api/test-wallets/${encodeURIComponent(id)}/pay`, input);
206
+ }
207
+ listWalletTransactions(id, options = {}) {
208
+ const query = new URLSearchParams();
209
+ if (options.cursor)
210
+ query.set('cursor', options.cursor);
211
+ if (options.limit !== undefined)
212
+ query.set('limit', String(options.limit));
213
+ return this.request('GET', `/api/test-wallets/${encodeURIComponent(id)}/transactions?${query}`);
214
+ }
215
+ completeTestCheckout(id, input) {
216
+ return this.request('POST', `/api/test-checkouts/${encodeURIComponent(id)}/complete`, input);
217
+ }
218
+ confirmTestCheckout(id, confirmationToken) {
219
+ return this.request('POST', `/api/test-checkouts/${encodeURIComponent(id)}/confirm`, { confirmationToken });
220
+ }
221
+ saveCredential(input) {
222
+ return this.request('POST', '/api/credentials', input);
223
+ }
224
+ credentials() {
225
+ return this.request('GET', '/api/credentials');
226
+ }
227
+ credential(id) {
228
+ return this.request('GET', `/api/credentials/${encodeURIComponent(id)}`);
229
+ }
230
+ updateCredential(id, input) {
231
+ return this.request('PATCH', `/api/credentials/${encodeURIComponent(id)}`, input);
232
+ }
233
+ deleteCredential(id) {
234
+ return this.request('DELETE', `/api/credentials/${encodeURIComponent(id)}`);
235
+ }
236
+ revealCredential(id, approvalId) {
237
+ return this.request('POST', `/api/credentials/${encodeURIComponent(id)}/reveal`, approvalId ? { approvalId } : {});
238
+ }
239
+ generatePassword(length = 24) {
240
+ return this.request('POST', '/api/passwords/generate', { length });
241
+ }
173
242
  createEmailInbox(settings = {}) {
174
243
  return this.request('POST', '/api/email-inboxes', settings);
175
244
  }
package/dist/types.d.ts CHANGED
@@ -274,3 +274,158 @@ export interface OAuthProvider {
274
274
  export interface CreatedOAuthProvider extends OAuthProvider {
275
275
  client_secret: string;
276
276
  }
277
+ export interface Credential {
278
+ id: string;
279
+ name: string;
280
+ url: string;
281
+ username: string;
282
+ agentMayRead: boolean;
283
+ authenticatorId?: string;
284
+ inboxId?: string;
285
+ createdAt: string;
286
+ updatedAt: string;
287
+ }
288
+ export interface CredentialInput {
289
+ name: string;
290
+ url: string;
291
+ username: string;
292
+ password?: string;
293
+ passwordLength?: number;
294
+ notes?: string;
295
+ agentMayRead?: boolean;
296
+ authenticatorId?: string | null;
297
+ inboxId?: string | null;
298
+ }
299
+ export type CredentialReveal = {
300
+ status: 'awaitingApproval';
301
+ approvalId: string;
302
+ answerUrl: string;
303
+ } | {
304
+ status: 'revealed';
305
+ id: string;
306
+ password: string;
307
+ notes: string;
308
+ };
309
+ export type TestPaymentKind = 'wallet' | 'paywall' | 'checkout';
310
+ export type CheckoutOutcome = 'succeeded' | 'declined' | 'insufficient_funds' | 'requires_verification' | 'expired';
311
+ export interface TestTransaction {
312
+ id: string;
313
+ at: string;
314
+ kind: 'payment' | 'topup';
315
+ amount: number;
316
+ payee: string;
317
+ memo: string;
318
+ status: 'awaitingApproval' | 'authorized' | 'succeeded' | 'refused' | 'expired';
319
+ reason?: string;
320
+ approvalId?: string;
321
+ events: {
322
+ at: string;
323
+ status: string;
324
+ reason?: string;
325
+ }[];
326
+ }
327
+ export interface TestPaymentResource {
328
+ id: string;
329
+ kind: TestPaymentKind;
330
+ testMode: true;
331
+ name: string;
332
+ expiresAt: string;
333
+ endpointUrl?: string;
334
+ balance?: number;
335
+ availableBalance?: number;
336
+ reservedBalance?: number;
337
+ perPaymentLimit?: number;
338
+ dailyLimit?: number;
339
+ approvalThreshold?: number;
340
+ amount?: number;
341
+ currency?: string;
342
+ item?: string;
343
+ description?: string;
344
+ modes?: ('wallet' | 'permissive')[];
345
+ response?: {
346
+ status: number;
347
+ body: unknown;
348
+ };
349
+ successUrl?: string;
350
+ cancelUrl?: string;
351
+ webhookUrl?: string;
352
+ attempts?: {
353
+ id: string;
354
+ at: string;
355
+ outcome: CheckoutOutcome;
356
+ last4?: string;
357
+ verified?: boolean;
358
+ }[];
359
+ deliveries?: {
360
+ requestId: string;
361
+ at: string;
362
+ attempts: number;
363
+ status?: number;
364
+ error?: string;
365
+ }[];
366
+ }
367
+ export interface TestWalletInput {
368
+ name: string;
369
+ balance?: number;
370
+ availableBalance?: number;
371
+ reservedBalance?: number;
372
+ ttlSeconds?: number;
373
+ persistent?: boolean;
374
+ /** Only an owner/admin browser session can set spending controls. Agent keys omit them. */
375
+ perPaymentLimit?: number;
376
+ dailyLimit?: number;
377
+ approvalThreshold?: number;
378
+ }
379
+ export interface TestPaywallInput {
380
+ name: string;
381
+ amount: number;
382
+ description?: string;
383
+ modes?: ('wallet' | 'permissive')[];
384
+ response?: {
385
+ status: number;
386
+ body: unknown;
387
+ };
388
+ ttlSeconds?: number;
389
+ persistent?: boolean;
390
+ }
391
+ export interface TestCheckoutInput {
392
+ name: string;
393
+ item: string;
394
+ description?: string;
395
+ amount: number;
396
+ currency?: 'USD' | 'EUR' | 'GBP' | 'CAD' | 'AUD';
397
+ successUrl?: string;
398
+ cancelUrl?: string;
399
+ webhookUrl?: string;
400
+ ttlSeconds?: number;
401
+ persistent?: boolean;
402
+ }
403
+ export interface TestPaymentInput {
404
+ payee: string;
405
+ amount?: number;
406
+ memo?: string;
407
+ idempotencyKey: string;
408
+ authorizeOnly?: boolean;
409
+ }
410
+ export interface TestPaymentResult {
411
+ testMode: true;
412
+ status: 'awaitingApproval' | 'authorized' | 'succeeded';
413
+ transaction?: TestTransaction;
414
+ transactionId?: string;
415
+ approvalId?: string;
416
+ answerUrl?: string;
417
+ paymentHeader?: string;
418
+ headerName?: 'X-PAYMENT';
419
+ expiresAt?: string;
420
+ content?: unknown;
421
+ checkout?: TestCheckoutAttempt;
422
+ }
423
+ export interface TestCheckoutAttempt {
424
+ testMode: true;
425
+ id: string;
426
+ at: string;
427
+ outcome: CheckoutOutcome;
428
+ last4?: string;
429
+ verified?: boolean;
430
+ confirmationToken?: string;
431
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kivel.io/sdk",
3
- "version": "0.6.1",
3
+ "version": "0.9.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {