@grantex/sdk 0.1.0 → 0.1.1

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.
Files changed (2) hide show
  1. package/README.md +604 -0
  2. package/package.json +3 -2
package/README.md ADDED
@@ -0,0 +1,604 @@
1
+ # @grantex/sdk
2
+
3
+ TypeScript SDK for the [Grantex](https://grantex.dev) delegated authorization protocol — OAuth 2.0 for AI agents.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@grantex/sdk)](https://www.npmjs.com/package/@grantex/sdk)
6
+ [![License](https://img.shields.io/npm/l/@grantex/sdk)](https://github.com/mishrasanjeev/grantex/blob/main/LICENSE)
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ npm install @grantex/sdk
12
+ ```
13
+
14
+ ## Quick Start
15
+
16
+ ```typescript
17
+ import { Grantex } from '@grantex/sdk';
18
+
19
+ const grantex = new Grantex({ apiKey: 'YOUR_API_KEY' });
20
+
21
+ // 1. Register an agent
22
+ const agent = await grantex.agents.register({
23
+ name: 'Email Assistant',
24
+ description: 'Reads and sends email on behalf of users',
25
+ scopes: ['email:read', 'email:send'],
26
+ });
27
+
28
+ // 2. Request authorization
29
+ const { consentUrl } = await grantex.authorize({
30
+ agentId: agent.id,
31
+ userId: 'usr_01J...',
32
+ scopes: ['email:read', 'email:send'],
33
+ });
34
+ // Redirect the user to consentUrl — they approve in plain language
35
+
36
+ // 3. Verify a grant token (offline, no network call)
37
+ import { verifyGrantToken } from '@grantex/sdk';
38
+
39
+ const grant = await verifyGrantToken(token, {
40
+ jwksUri: 'https://api.grantex.dev/.well-known/jwks.json',
41
+ });
42
+ console.log(grant.scopes); // ['email:read', 'email:send']
43
+ console.log(grant.principalId); // 'usr_01J...'
44
+
45
+ // 4. Revoke when done
46
+ await grantex.tokens.revoke(grant.tokenId);
47
+ ```
48
+
49
+ ## Configuration
50
+
51
+ ```typescript
52
+ const grantex = new Grantex({
53
+ apiKey: 'gx_....', // or set GRANTEX_API_KEY env var
54
+ baseUrl: 'https://api.grantex.dev', // default
55
+ timeout: 30000, // request timeout in ms (default: 30s)
56
+ });
57
+ ```
58
+
59
+ | Option | Type | Default | Description |
60
+ |--------|------|---------|-------------|
61
+ | `apiKey` | `string` | `process.env.GRANTEX_API_KEY` | API key for authentication |
62
+ | `baseUrl` | `string` | `https://api.grantex.dev` | Base URL of the Grantex API |
63
+ | `timeout` | `number` | `30000` | Request timeout in milliseconds |
64
+
65
+ ## API Reference
66
+
67
+ ### Authorization
68
+
69
+ #### `grantex.authorize(params)`
70
+
71
+ Initiate the delegated authorization flow. Returns a consent URL to redirect the user to.
72
+
73
+ ```typescript
74
+ const request = await grantex.authorize({
75
+ agentId: 'ag_01J...',
76
+ userId: 'usr_01J...',
77
+ scopes: ['files:read', 'email:send'],
78
+ expiresIn: '24h', // optional
79
+ redirectUri: 'https://...' // optional
80
+ });
81
+
82
+ console.log(request.consentUrl); // redirect user here
83
+ console.log(request.authRequestId); // track the request
84
+ console.log(request.expiresAt); // ISO 8601 timestamp
85
+ ```
86
+
87
+ **Returns**: `AuthorizationRequest`
88
+
89
+ | Field | Type | Description |
90
+ |-------|------|-------------|
91
+ | `authRequestId` | `string` | Unique ID for this authorization request |
92
+ | `consentUrl` | `string` | URL to redirect the user to for consent |
93
+ | `agentId` | `string` | The agent requesting authorization |
94
+ | `principalId` | `string` | The user being asked for consent |
95
+ | `scopes` | `string[]` | Requested scopes |
96
+ | `expiresAt` | `string` | When the request expires (ISO 8601) |
97
+ | `status` | `string` | `'pending'`, `'approved'`, `'denied'`, or `'expired'` |
98
+
99
+ ---
100
+
101
+ ### Agents
102
+
103
+ #### `grantex.agents.register(params)`
104
+
105
+ Register a new AI agent.
106
+
107
+ ```typescript
108
+ const agent = await grantex.agents.register({
109
+ name: 'Code Review Bot',
110
+ description: 'Reviews pull requests and suggests improvements',
111
+ scopes: ['repo:read', 'pr:comment'],
112
+ });
113
+ ```
114
+
115
+ #### `grantex.agents.get(agentId)`
116
+
117
+ ```typescript
118
+ const agent = await grantex.agents.get('ag_01J...');
119
+ ```
120
+
121
+ #### `grantex.agents.list()`
122
+
123
+ ```typescript
124
+ const { agents, total } = await grantex.agents.list();
125
+ ```
126
+
127
+ #### `grantex.agents.update(agentId, params)`
128
+
129
+ ```typescript
130
+ const agent = await grantex.agents.update('ag_01J...', {
131
+ name: 'Updated Name',
132
+ scopes: ['repo:read', 'pr:comment', 'pr:approve'],
133
+ });
134
+ ```
135
+
136
+ #### `grantex.agents.delete(agentId)`
137
+
138
+ ```typescript
139
+ await grantex.agents.delete('ag_01J...');
140
+ ```
141
+
142
+ ---
143
+
144
+ ### Grants
145
+
146
+ #### `grantex.grants.get(grantId)`
147
+
148
+ ```typescript
149
+ const grant = await grantex.grants.get('grnt_01J...');
150
+ ```
151
+
152
+ #### `grantex.grants.list(params?)`
153
+
154
+ ```typescript
155
+ const { grants, total } = await grantex.grants.list({
156
+ agentId: 'ag_01J...', // optional filter
157
+ principalId: 'usr_01J...', // optional filter
158
+ status: 'active', // 'active' | 'revoked' | 'expired'
159
+ page: 1,
160
+ pageSize: 20,
161
+ });
162
+ ```
163
+
164
+ #### `grantex.grants.revoke(grantId)`
165
+
166
+ ```typescript
167
+ await grantex.grants.revoke('grnt_01J...');
168
+ ```
169
+
170
+ #### `grantex.grants.delegate(params)`
171
+
172
+ Create a delegated sub-agent grant (per [SPEC Section 9](https://github.com/mishrasanjeev/grantex/blob/main/SPEC.md)).
173
+
174
+ ```typescript
175
+ const delegation = await grantex.grants.delegate({
176
+ parentGrantToken: 'eyJhbG...',
177
+ subAgentId: 'ag_02K...',
178
+ scopes: ['files:read'], // must be subset of parent scopes
179
+ expiresIn: '1h', // optional, cannot exceed parent
180
+ });
181
+
182
+ console.log(delegation.grantToken); // new JWT for the sub-agent
183
+ console.log(delegation.grantId);
184
+ ```
185
+
186
+ #### `grantex.grants.verify(token)`
187
+
188
+ Verify a grant token via the API (online verification with real-time revocation check).
189
+
190
+ ```typescript
191
+ const verified = await grantex.grants.verify('eyJhbG...');
192
+ console.log(verified.principalId);
193
+ console.log(verified.scopes);
194
+ ```
195
+
196
+ ---
197
+
198
+ ### Tokens
199
+
200
+ #### `grantex.tokens.verify(token)`
201
+
202
+ Online token verification with revocation status.
203
+
204
+ ```typescript
205
+ const result = await grantex.tokens.verify('eyJhbG...');
206
+ if (result.valid) {
207
+ console.log(result.scopes); // ['files:read']
208
+ console.log(result.principal); // 'usr_01J...'
209
+ console.log(result.agent); // 'ag_01J...'
210
+ console.log(result.grantId);
211
+ console.log(result.expiresAt);
212
+ }
213
+ ```
214
+
215
+ #### `grantex.tokens.revoke(tokenId)`
216
+
217
+ Revoke a token by its JTI. Blocklisted in Redis immediately; all sub-delegated tokens are also invalidated.
218
+
219
+ ```typescript
220
+ await grantex.tokens.revoke('tok_01J...');
221
+ ```
222
+
223
+ ---
224
+
225
+ ### Offline Token Verification
226
+
227
+ #### `verifyGrantToken(token, options)`
228
+
229
+ Verify a grant token offline using the published JWKS. No API call needed — signature is verified locally using RS256.
230
+
231
+ ```typescript
232
+ import { verifyGrantToken } from '@grantex/sdk';
233
+
234
+ const grant = await verifyGrantToken('eyJhbG...', {
235
+ jwksUri: 'https://api.grantex.dev/.well-known/jwks.json',
236
+ requiredScopes: ['files:read'], // optional — rejects if missing
237
+ audience: 'https://myapp.com', // optional — validates aud claim
238
+ });
239
+ ```
240
+
241
+ **Returns**: `VerifiedGrant`
242
+
243
+ | Field | Type | Description |
244
+ |-------|------|-------------|
245
+ | `tokenId` | `string` | Unique token ID (JWT `jti` claim) |
246
+ | `grantId` | `string` | Grant record ID |
247
+ | `principalId` | `string` | User who authorized the grant (`sub` claim) |
248
+ | `agentDid` | `string` | Agent's DID (`agt` claim) |
249
+ | `developerId` | `string` | Developer org ID (`dev` claim) |
250
+ | `scopes` | `string[]` | Granted scopes (`scp` claim) |
251
+ | `issuedAt` | `number` | Issued-at timestamp (seconds since epoch) |
252
+ | `expiresAt` | `number` | Expiry timestamp (seconds since epoch) |
253
+ | `parentAgentDid` | `string?` | Parent agent DID (delegation only) |
254
+ | `parentGrantId` | `string?` | Parent grant ID (delegation only) |
255
+ | `delegationDepth` | `number?` | Delegation depth (0 = root) |
256
+
257
+ ---
258
+
259
+ ### Audit
260
+
261
+ #### `grantex.audit.log(params)`
262
+
263
+ Log an auditable action taken by an agent.
264
+
265
+ ```typescript
266
+ const entry = await grantex.audit.log({
267
+ agentId: 'ag_01J...',
268
+ grantId: 'grnt_01J...',
269
+ action: 'email:send',
270
+ metadata: { to: 'user@example.com', subject: 'Hello' },
271
+ status: 'success', // 'success' | 'failure' | 'blocked'
272
+ });
273
+ ```
274
+
275
+ #### `grantex.audit.list(params?)`
276
+
277
+ ```typescript
278
+ const { entries, total } = await grantex.audit.list({
279
+ agentId: 'ag_01J...',
280
+ action: 'email:send',
281
+ since: '2026-01-01T00:00:00Z',
282
+ until: '2026-02-28T23:59:59Z',
283
+ page: 1,
284
+ pageSize: 50,
285
+ });
286
+ ```
287
+
288
+ #### `grantex.audit.get(entryId)`
289
+
290
+ ```typescript
291
+ const entry = await grantex.audit.get('aud_01J...');
292
+ console.log(entry.hash); // SHA-256 hash for tamper evidence
293
+ console.log(entry.prevHash); // previous entry hash (chain integrity)
294
+ ```
295
+
296
+ ---
297
+
298
+ ### Webhooks
299
+
300
+ #### `grantex.webhooks.create(params)`
301
+
302
+ ```typescript
303
+ const webhook = await grantex.webhooks.create({
304
+ url: 'https://myapp.com/webhooks/grantex',
305
+ events: ['grant.created', 'grant.revoked', 'token.issued'],
306
+ });
307
+ console.log(webhook.secret); // HMAC secret for signature verification
308
+ ```
309
+
310
+ #### `grantex.webhooks.list()`
311
+
312
+ ```typescript
313
+ const { webhooks } = await grantex.webhooks.list();
314
+ ```
315
+
316
+ #### `grantex.webhooks.delete(webhookId)`
317
+
318
+ ```typescript
319
+ await grantex.webhooks.delete('wh_01J...');
320
+ ```
321
+
322
+ #### Webhook Signature Verification
323
+
324
+ ```typescript
325
+ import { verifyWebhookSignature } from '@grantex/sdk';
326
+
327
+ // In your webhook handler
328
+ verifyWebhookSignature(requestBody, signatureHeader, webhookSecret);
329
+ ```
330
+
331
+ ---
332
+
333
+ ### Policies
334
+
335
+ Define fine-grained access control rules for agents.
336
+
337
+ #### `grantex.policies.create(params)`
338
+
339
+ ```typescript
340
+ const policy = await grantex.policies.create({
341
+ name: 'Block after hours',
342
+ effect: 'deny',
343
+ priority: 10,
344
+ scopes: ['email:send'],
345
+ timeOfDayStart: '18:00',
346
+ timeOfDayEnd: '08:00',
347
+ });
348
+ ```
349
+
350
+ #### `grantex.policies.list()`
351
+
352
+ ```typescript
353
+ const { policies, total } = await grantex.policies.list();
354
+ ```
355
+
356
+ #### `grantex.policies.get(policyId)` / `update(policyId, params)` / `delete(policyId)`
357
+
358
+ ```typescript
359
+ const policy = await grantex.policies.get('pol_01J...');
360
+
361
+ await grantex.policies.update('pol_01J...', { effect: 'allow' });
362
+
363
+ await grantex.policies.delete('pol_01J...');
364
+ ```
365
+
366
+ ---
367
+
368
+ ### Compliance
369
+
370
+ #### `grantex.compliance.getSummary(params?)`
371
+
372
+ ```typescript
373
+ const summary = await grantex.compliance.getSummary({
374
+ since: '2026-01-01T00:00:00Z',
375
+ until: '2026-02-28T23:59:59Z',
376
+ });
377
+ console.log(summary.agents); // { total, active, suspended, revoked }
378
+ console.log(summary.grants); // { total, active, revoked, expired }
379
+ console.log(summary.auditEntries); // { total, success, failure, blocked }
380
+ ```
381
+
382
+ #### `grantex.compliance.exportGrants(params?)`
383
+
384
+ ```typescript
385
+ const { grants, total } = await grantex.compliance.exportGrants({
386
+ status: 'active',
387
+ });
388
+ ```
389
+
390
+ #### `grantex.compliance.exportAudit(params?)`
391
+
392
+ ```typescript
393
+ const { entries, total } = await grantex.compliance.exportAudit({
394
+ since: '2026-01-01T00:00:00Z',
395
+ agentId: 'ag_01J...',
396
+ });
397
+ ```
398
+
399
+ #### `grantex.compliance.evidencePack(params?)`
400
+
401
+ Generate a full SOC 2 / GDPR evidence pack with audit chain integrity verification.
402
+
403
+ ```typescript
404
+ const pack = await grantex.compliance.evidencePack({
405
+ framework: 'soc2', // 'soc2' | 'gdpr' | 'all'
406
+ since: '2026-01-01T00:00:00Z',
407
+ });
408
+
409
+ console.log(pack.chainIntegrity.valid); // true
410
+ console.log(pack.chainIntegrity.checkedEntries); // 1042
411
+ console.log(pack.summary);
412
+ console.log(pack.grants);
413
+ console.log(pack.auditEntries);
414
+ console.log(pack.policies);
415
+ ```
416
+
417
+ ---
418
+
419
+ ### Anomaly Detection
420
+
421
+ #### `grantex.anomalies.detect()`
422
+
423
+ Run anomaly detection across all agents.
424
+
425
+ ```typescript
426
+ const { anomalies, total } = await grantex.anomalies.detect();
427
+ // anomaly types: 'rate_spike' | 'high_failure_rate' | 'new_principal' | 'off_hours_activity'
428
+ ```
429
+
430
+ #### `grantex.anomalies.list(params?)`
431
+
432
+ ```typescript
433
+ const { anomalies } = await grantex.anomalies.list({
434
+ unacknowledged: true, // only open anomalies
435
+ });
436
+ ```
437
+
438
+ #### `grantex.anomalies.acknowledge(anomalyId)`
439
+
440
+ ```typescript
441
+ const anomaly = await grantex.anomalies.acknowledge('anom_01J...');
442
+ ```
443
+
444
+ ---
445
+
446
+ ### Billing
447
+
448
+ #### `grantex.billing.getSubscription()`
449
+
450
+ ```typescript
451
+ const sub = await grantex.billing.getSubscription();
452
+ console.log(sub.plan); // 'free' | 'pro' | 'enterprise'
453
+ console.log(sub.status); // 'active' | 'past_due' | 'canceled'
454
+ console.log(sub.currentPeriodEnd); // ISO 8601 or null
455
+ ```
456
+
457
+ #### `grantex.billing.createCheckout(params)`
458
+
459
+ ```typescript
460
+ const { checkoutUrl } = await grantex.billing.createCheckout({
461
+ plan: 'pro',
462
+ successUrl: 'https://myapp.com/billing/success',
463
+ cancelUrl: 'https://myapp.com/billing/cancel',
464
+ });
465
+ // Redirect user to checkoutUrl
466
+ ```
467
+
468
+ #### `grantex.billing.createPortal(params)`
469
+
470
+ ```typescript
471
+ const { portalUrl } = await grantex.billing.createPortal({
472
+ returnUrl: 'https://myapp.com/settings',
473
+ });
474
+ ```
475
+
476
+ ---
477
+
478
+ ### SCIM 2.0 Provisioning
479
+
480
+ Sync users from your identity provider.
481
+
482
+ #### Token Management
483
+
484
+ ```typescript
485
+ // Create a SCIM bearer token
486
+ const { token, id, label } = await grantex.scim.createToken({
487
+ label: 'Okta SCIM integration',
488
+ });
489
+ // token is returned once — store it securely
490
+
491
+ const { tokens } = await grantex.scim.listTokens();
492
+
493
+ await grantex.scim.revokeToken('scimtok_01J...');
494
+ ```
495
+
496
+ #### User Operations
497
+
498
+ ```typescript
499
+ // List provisioned users
500
+ const { Resources, totalResults } = await grantex.scim.listUsers({
501
+ startIndex: 1,
502
+ count: 100,
503
+ });
504
+
505
+ // Create a user
506
+ const user = await grantex.scim.createUser({
507
+ userName: 'alice@example.com',
508
+ displayName: 'Alice',
509
+ emails: [{ value: 'alice@example.com', primary: true }],
510
+ });
511
+
512
+ // Get / Replace / Patch / Delete
513
+ const user = await grantex.scim.getUser('scimusr_01J...');
514
+
515
+ await grantex.scim.replaceUser('scimusr_01J...', { userName: 'alice@new.com' });
516
+
517
+ await grantex.scim.updateUser('scimusr_01J...', [
518
+ { op: 'replace', path: 'active', value: false },
519
+ ]);
520
+
521
+ await grantex.scim.deleteUser('scimusr_01J...');
522
+ ```
523
+
524
+ ---
525
+
526
+ ### SSO (OIDC)
527
+
528
+ #### `grantex.sso.createConfig(params)`
529
+
530
+ ```typescript
531
+ const config = await grantex.sso.createConfig({
532
+ issuerUrl: 'https://accounts.google.com',
533
+ clientId: 'xxx.apps.googleusercontent.com',
534
+ clientSecret: 'GOCSPX-...',
535
+ redirectUri: 'https://myapp.com/auth/callback',
536
+ });
537
+ ```
538
+
539
+ #### `grantex.sso.getConfig()` / `deleteConfig()`
540
+
541
+ ```typescript
542
+ const config = await grantex.sso.getConfig();
543
+ await grantex.sso.deleteConfig();
544
+ ```
545
+
546
+ #### `grantex.sso.getLoginUrl(org)`
547
+
548
+ ```typescript
549
+ const { authorizeUrl } = await grantex.sso.getLoginUrl('dev_01J...');
550
+ // Redirect user to authorizeUrl
551
+ ```
552
+
553
+ #### `grantex.sso.handleCallback(code, state)`
554
+
555
+ ```typescript
556
+ const { email, name, sub, developerId } = await grantex.sso.handleCallback(code, state);
557
+ ```
558
+
559
+ ---
560
+
561
+ ## Error Handling
562
+
563
+ All errors extend `GrantexError`:
564
+
565
+ ```typescript
566
+ import {
567
+ GrantexError, // base class
568
+ GrantexApiError, // API returned an error (has statusCode, body, requestId)
569
+ GrantexAuthError, // 401/403 — invalid or missing API key
570
+ GrantexTokenError, // token verification failed (invalid signature, expired, etc.)
571
+ GrantexNetworkError, // network failure (timeout, DNS, connection refused)
572
+ } from '@grantex/sdk';
573
+
574
+ try {
575
+ await grantex.agents.get('ag_invalid');
576
+ } catch (err) {
577
+ if (err instanceof GrantexAuthError) {
578
+ console.error('Auth failed:', err.statusCode); // 401 or 403
579
+ console.error('Request ID:', err.requestId);
580
+ } else if (err instanceof GrantexApiError) {
581
+ console.error('API error:', err.statusCode, err.body);
582
+ } else if (err instanceof GrantexNetworkError) {
583
+ console.error('Network error:', err.message, err.cause);
584
+ }
585
+ }
586
+ ```
587
+
588
+ ---
589
+
590
+ ## Requirements
591
+
592
+ - Node.js 18+
593
+ - ESM (`"type": "module"` in your package.json, or use dynamic `import()`)
594
+
595
+ ## Links
596
+
597
+ - [GitHub](https://github.com/mishrasanjeev/grantex)
598
+ - [Protocol Specification](https://github.com/mishrasanjeev/grantex/blob/main/SPEC.md)
599
+ - [Python SDK](https://pypi.org/project/grantex/)
600
+ - [API Reference](https://api.grantex.dev/.well-known/jwks.json)
601
+
602
+ ## License
603
+
604
+ [Apache 2.0](https://github.com/mishrasanjeev/grantex/blob/main/LICENSE)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grantex/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "TypeScript SDK for the Grantex delegated authorization protocol",
5
5
  "type": "module",
6
6
  "exports": {
@@ -12,7 +12,8 @@
12
12
  "main": "./dist/index.js",
13
13
  "types": "./dist/index.d.ts",
14
14
  "files": [
15
- "dist"
15
+ "dist",
16
+ "README.md"
16
17
  ],
17
18
  "scripts": {
18
19
  "build": "tsc -p tsconfig.build.json",