tether-name 1.0.5 → 2.0.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 +24 -27
- package/dist/index.d.mts +10 -10
- package/dist/index.d.ts +10 -10
- package/dist/index.js +13 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +13 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -16,8 +16,8 @@ Requires Node.js 20+ (uses native `fetch` and `crypto` modules).
|
|
|
16
16
|
import { TetherClient } from 'tether-name';
|
|
17
17
|
|
|
18
18
|
const client = new TetherClient({
|
|
19
|
-
|
|
20
|
-
privateKeyPath: '/path/to/your/private-key.
|
|
19
|
+
agentId: 'your-agent-id',
|
|
20
|
+
privateKeyPath: '/path/to/your/private-key.pem'
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
// One-call verification
|
|
@@ -35,7 +35,7 @@ For more control over the verification process:
|
|
|
35
35
|
import { TetherClient } from 'tether-name';
|
|
36
36
|
|
|
37
37
|
const client = new TetherClient({
|
|
38
|
-
|
|
38
|
+
agentId: 'your-agent-id',
|
|
39
39
|
privateKeyPem: `-----BEGIN RSA PRIVATE KEY-----
|
|
40
40
|
MIIEpAIBAAKCAQEA...
|
|
41
41
|
-----END RSA PRIVATE KEY-----`
|
|
@@ -71,8 +71,8 @@ interface TetherClientConfig {
|
|
|
71
71
|
// API key (for agent management — no private key needed)
|
|
72
72
|
apiKey?: string; // Or use TETHER_API_KEY env var
|
|
73
73
|
|
|
74
|
-
//
|
|
75
|
-
|
|
74
|
+
// Agent ID (required for verify/sign, optional with apiKey)
|
|
75
|
+
agentId?: string; // Or use TETHER_AGENT_ID env var
|
|
76
76
|
|
|
77
77
|
// Private key (required for verify/sign, choose one)
|
|
78
78
|
privateKeyPath?: string; // Path to DER or PEM file
|
|
@@ -95,46 +95,46 @@ const client = new TetherClient({
|
|
|
95
95
|
const agent = await client.createAgent('my-bot');
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
-
**API key +
|
|
98
|
+
**API key + agent + private key** — full access (management and verification):
|
|
99
99
|
|
|
100
100
|
```typescript
|
|
101
101
|
const client = new TetherClient({
|
|
102
102
|
apiKey: 'sk-tether-name-...',
|
|
103
|
-
|
|
104
|
-
privateKeyPath: '/path/to/key.
|
|
103
|
+
agentId: 'your-agent-id',
|
|
104
|
+
privateKeyPath: '/path/to/key.pem'
|
|
105
105
|
});
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
**
|
|
108
|
+
**Agent + private key only** — verification without agent management (original behavior):
|
|
109
109
|
|
|
110
110
|
```typescript
|
|
111
111
|
const client = new TetherClient({
|
|
112
|
-
|
|
113
|
-
privateKeyPath: '/path/to/key.
|
|
112
|
+
agentId: 'your-agent-id',
|
|
113
|
+
privateKeyPath: '/path/to/key.pem'
|
|
114
114
|
});
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
### Key Format Support
|
|
118
118
|
|
|
119
|
-
The SDK supports both
|
|
119
|
+
The SDK supports both PEM and DER private key formats:
|
|
120
120
|
|
|
121
121
|
```typescript
|
|
122
122
|
// From file path (auto-detects format)
|
|
123
123
|
const client1 = new TetherClient({
|
|
124
|
-
|
|
125
|
-
privateKeyPath: '/path/to/key.
|
|
124
|
+
agentId: 'your-id',
|
|
125
|
+
privateKeyPath: '/path/to/key.pem' // or .der
|
|
126
126
|
});
|
|
127
127
|
|
|
128
128
|
// From PEM string
|
|
129
129
|
const client2 = new TetherClient({
|
|
130
|
-
|
|
130
|
+
agentId: 'your-id',
|
|
131
131
|
privateKeyPem: '-----BEGIN RSA PRIVATE KEY-----\n...'
|
|
132
132
|
});
|
|
133
133
|
|
|
134
134
|
// From DER buffer
|
|
135
135
|
const derBuffer = fs.readFileSync('/path/to/key.der');
|
|
136
136
|
const client3 = new TetherClient({
|
|
137
|
-
|
|
137
|
+
agentId: 'your-id',
|
|
138
138
|
privateKeyBuffer: derBuffer
|
|
139
139
|
});
|
|
140
140
|
```
|
|
@@ -150,7 +150,7 @@ const client = new TetherClient({ apiKey: 'sk-tether-name-...' });
|
|
|
150
150
|
const agent = await client.createAgent('my-bot', 'Does helpful things');
|
|
151
151
|
console.log(agent.id); // "abc123"
|
|
152
152
|
console.log(agent.agentName); // "my-bot"
|
|
153
|
-
console.log(agent.registrationToken); // Use to register
|
|
153
|
+
console.log(agent.registrationToken); // Use to register the agent
|
|
154
154
|
|
|
155
155
|
// List all agents
|
|
156
156
|
const agents = await client.listAgents();
|
|
@@ -161,12 +161,12 @@ await client.deleteAgent(agent.id);
|
|
|
161
161
|
|
|
162
162
|
## Environment Variables
|
|
163
163
|
|
|
164
|
-
Set these environment variables to avoid hardcoding
|
|
164
|
+
Set these environment variables to avoid hardcoding secrets:
|
|
165
165
|
|
|
166
166
|
```bash
|
|
167
167
|
export TETHER_API_KEY="sk-tether-name-..." # API key for agent management
|
|
168
|
-
export
|
|
169
|
-
export TETHER_PRIVATE_KEY_PATH="/path/to/your/private-key.
|
|
168
|
+
export TETHER_AGENT_ID="your-agent-id"
|
|
169
|
+
export TETHER_PRIVATE_KEY_PATH="/path/to/your/private-key.pem"
|
|
170
170
|
```
|
|
171
171
|
|
|
172
172
|
Then initialize without parameters:
|
|
@@ -248,18 +248,15 @@ interface Agent {
|
|
|
248
248
|
- `TetherVerificationError` - Verification failed
|
|
249
249
|
- `TetherAPIError` - API request failed
|
|
250
250
|
|
|
251
|
-
## Getting Your
|
|
251
|
+
## Getting Your Agent Identity
|
|
252
252
|
|
|
253
253
|
1. Visit [tether.name](https://tether.name)
|
|
254
|
-
2. Register your agent and get
|
|
254
|
+
2. Register your agent and get an agent ID
|
|
255
255
|
3. Generate an RSA-2048 private key:
|
|
256
256
|
|
|
257
257
|
```bash
|
|
258
|
-
# Generate private key
|
|
259
|
-
openssl
|
|
260
|
-
|
|
261
|
-
# Convert to DER format (optional)
|
|
262
|
-
openssl rsa -in private-key.pem -outform DER -out private-key.der
|
|
258
|
+
# Generate private key (PEM format)
|
|
259
|
+
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private-key.pem
|
|
263
260
|
```
|
|
264
261
|
|
|
265
262
|
## Requirements
|
package/dist/index.d.mts
CHANGED
|
@@ -4,15 +4,15 @@ import { KeyObject } from 'crypto';
|
|
|
4
4
|
* Configuration options for TetherClient
|
|
5
5
|
*/
|
|
6
6
|
interface TetherClientConfig {
|
|
7
|
-
/** The
|
|
8
|
-
|
|
7
|
+
/** The agent ID */
|
|
8
|
+
agentId?: string;
|
|
9
9
|
/** Path to the private key file (DER or PEM format) */
|
|
10
10
|
privateKeyPath?: string;
|
|
11
11
|
/** Private key as a string (PEM format) */
|
|
12
12
|
privateKeyPem?: string;
|
|
13
13
|
/** Private key as a Buffer (DER format) */
|
|
14
14
|
privateKeyBuffer?: Buffer;
|
|
15
|
-
/** API key for management operations (alternative to
|
|
15
|
+
/** API key for management operations (alternative to agent auth) */
|
|
16
16
|
apiKey?: string;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
@@ -27,7 +27,7 @@ interface ChallengeResponse {
|
|
|
27
27
|
interface VerificationRequest {
|
|
28
28
|
challenge: string;
|
|
29
29
|
proof: string;
|
|
30
|
-
|
|
30
|
+
agentId: string;
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
33
|
* Response from the challenge verification endpoint
|
|
@@ -76,9 +76,9 @@ interface Agent {
|
|
|
76
76
|
lastVerifiedAt?: number;
|
|
77
77
|
}
|
|
78
78
|
/**
|
|
79
|
-
* Response from the issue
|
|
79
|
+
* Response from the issue agent endpoint
|
|
80
80
|
*/
|
|
81
|
-
interface
|
|
81
|
+
interface IssueAgentResponse {
|
|
82
82
|
id: string;
|
|
83
83
|
agentName: string;
|
|
84
84
|
description: string;
|
|
@@ -90,7 +90,7 @@ interface IssueCredentialResponse {
|
|
|
90
90
|
* TetherClient - Official SDK for tether.name agent identity verification
|
|
91
91
|
*/
|
|
92
92
|
declare class TetherClient {
|
|
93
|
-
private readonly
|
|
93
|
+
private readonly agentId;
|
|
94
94
|
private readonly privateKey;
|
|
95
95
|
private readonly baseUrl;
|
|
96
96
|
private readonly apiKey?;
|
|
@@ -108,9 +108,9 @@ declare class TetherClient {
|
|
|
108
108
|
*/
|
|
109
109
|
private _requireApiKey;
|
|
110
110
|
/**
|
|
111
|
-
* Ensures
|
|
111
|
+
* Ensures an agent ID is available, throwing if not
|
|
112
112
|
*/
|
|
113
|
-
private
|
|
113
|
+
private _requireAgentId;
|
|
114
114
|
/**
|
|
115
115
|
* Request a challenge from the Tether API
|
|
116
116
|
*/
|
|
@@ -181,4 +181,4 @@ declare function signChallenge(privateKey: KeyObject, challenge: string): string
|
|
|
181
181
|
*/
|
|
182
182
|
declare function detectKeyFormat(keyPath: string): KeyFormat;
|
|
183
183
|
|
|
184
|
-
export { type Agent, type ChallengeResponse, type
|
|
184
|
+
export { type Agent, type ChallengeResponse, type IssueAgentResponse, type KeyFormat, TetherAPIError, TetherClient, type TetherClientConfig, TetherError, TetherVerificationError, type VerificationRequest, type VerificationResponse, type VerificationResult, detectKeyFormat, loadPrivateKey, signChallenge };
|
package/dist/index.d.ts
CHANGED
|
@@ -4,15 +4,15 @@ import { KeyObject } from 'crypto';
|
|
|
4
4
|
* Configuration options for TetherClient
|
|
5
5
|
*/
|
|
6
6
|
interface TetherClientConfig {
|
|
7
|
-
/** The
|
|
8
|
-
|
|
7
|
+
/** The agent ID */
|
|
8
|
+
agentId?: string;
|
|
9
9
|
/** Path to the private key file (DER or PEM format) */
|
|
10
10
|
privateKeyPath?: string;
|
|
11
11
|
/** Private key as a string (PEM format) */
|
|
12
12
|
privateKeyPem?: string;
|
|
13
13
|
/** Private key as a Buffer (DER format) */
|
|
14
14
|
privateKeyBuffer?: Buffer;
|
|
15
|
-
/** API key for management operations (alternative to
|
|
15
|
+
/** API key for management operations (alternative to agent auth) */
|
|
16
16
|
apiKey?: string;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
@@ -27,7 +27,7 @@ interface ChallengeResponse {
|
|
|
27
27
|
interface VerificationRequest {
|
|
28
28
|
challenge: string;
|
|
29
29
|
proof: string;
|
|
30
|
-
|
|
30
|
+
agentId: string;
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
33
|
* Response from the challenge verification endpoint
|
|
@@ -76,9 +76,9 @@ interface Agent {
|
|
|
76
76
|
lastVerifiedAt?: number;
|
|
77
77
|
}
|
|
78
78
|
/**
|
|
79
|
-
* Response from the issue
|
|
79
|
+
* Response from the issue agent endpoint
|
|
80
80
|
*/
|
|
81
|
-
interface
|
|
81
|
+
interface IssueAgentResponse {
|
|
82
82
|
id: string;
|
|
83
83
|
agentName: string;
|
|
84
84
|
description: string;
|
|
@@ -90,7 +90,7 @@ interface IssueCredentialResponse {
|
|
|
90
90
|
* TetherClient - Official SDK for tether.name agent identity verification
|
|
91
91
|
*/
|
|
92
92
|
declare class TetherClient {
|
|
93
|
-
private readonly
|
|
93
|
+
private readonly agentId;
|
|
94
94
|
private readonly privateKey;
|
|
95
95
|
private readonly baseUrl;
|
|
96
96
|
private readonly apiKey?;
|
|
@@ -108,9 +108,9 @@ declare class TetherClient {
|
|
|
108
108
|
*/
|
|
109
109
|
private _requireApiKey;
|
|
110
110
|
/**
|
|
111
|
-
* Ensures
|
|
111
|
+
* Ensures an agent ID is available, throwing if not
|
|
112
112
|
*/
|
|
113
|
-
private
|
|
113
|
+
private _requireAgentId;
|
|
114
114
|
/**
|
|
115
115
|
* Request a challenge from the Tether API
|
|
116
116
|
*/
|
|
@@ -181,4 +181,4 @@ declare function signChallenge(privateKey: KeyObject, challenge: string): string
|
|
|
181
181
|
*/
|
|
182
182
|
declare function detectKeyFormat(keyPath: string): KeyFormat;
|
|
183
183
|
|
|
184
|
-
export { type Agent, type ChallengeResponse, type
|
|
184
|
+
export { type Agent, type ChallengeResponse, type IssueAgentResponse, type KeyFormat, TetherAPIError, TetherClient, type TetherClientConfig, TetherError, TetherVerificationError, type VerificationRequest, type VerificationResponse, type VerificationResult, detectKeyFormat, loadPrivateKey, signChallenge };
|
package/dist/index.js
CHANGED
|
@@ -133,14 +133,14 @@ function detectKeyFormat(keyPath) {
|
|
|
133
133
|
|
|
134
134
|
// src/client.ts
|
|
135
135
|
var TetherClient = class {
|
|
136
|
-
|
|
136
|
+
agentId;
|
|
137
137
|
privateKey;
|
|
138
138
|
baseUrl;
|
|
139
139
|
apiKey;
|
|
140
140
|
constructor(config) {
|
|
141
141
|
this.baseUrl = "https://api.tether.name";
|
|
142
142
|
this.apiKey = config.apiKey || process.env.TETHER_API_KEY;
|
|
143
|
-
this.
|
|
143
|
+
this.agentId = config.agentId || process.env.TETHER_AGENT_ID || "";
|
|
144
144
|
const keyPath = config.privateKeyPath || process.env.TETHER_PRIVATE_KEY_PATH;
|
|
145
145
|
const hasKeyMaterial = keyPath || config.privateKeyPem || config.privateKeyBuffer;
|
|
146
146
|
if (hasKeyMaterial) {
|
|
@@ -154,7 +154,7 @@ var TetherClient = class {
|
|
|
154
154
|
}
|
|
155
155
|
if (!this.apiKey && !this.privateKey) {
|
|
156
156
|
}
|
|
157
|
-
if (!this.apiKey && !this.
|
|
157
|
+
if (!this.apiKey && !this.agentId) {
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
/**
|
|
@@ -188,15 +188,15 @@ var TetherClient = class {
|
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
/**
|
|
191
|
-
* Ensures
|
|
191
|
+
* Ensures an agent ID is available, throwing if not
|
|
192
192
|
*/
|
|
193
|
-
|
|
194
|
-
if (!this.
|
|
193
|
+
_requireAgentId() {
|
|
194
|
+
if (!this.agentId) {
|
|
195
195
|
throw new TetherError(
|
|
196
|
-
"
|
|
196
|
+
"Agent ID is required for this operation. Provide it in config or set TETHER_AGENT_ID environment variable."
|
|
197
197
|
);
|
|
198
198
|
}
|
|
199
|
-
return this.
|
|
199
|
+
return this.agentId;
|
|
200
200
|
}
|
|
201
201
|
/**
|
|
202
202
|
* Request a challenge from the Tether API
|
|
@@ -245,12 +245,12 @@ var TetherClient = class {
|
|
|
245
245
|
* Submit proof for a challenge
|
|
246
246
|
*/
|
|
247
247
|
async submitProof(challenge, proof) {
|
|
248
|
-
const
|
|
248
|
+
const agentId = this._requireAgentId();
|
|
249
249
|
try {
|
|
250
250
|
const payload = {
|
|
251
251
|
challenge,
|
|
252
252
|
proof,
|
|
253
|
-
|
|
253
|
+
agentId
|
|
254
254
|
};
|
|
255
255
|
const response = await fetch(`${this.baseUrl}/challenge/verify`, {
|
|
256
256
|
method: "POST",
|
|
@@ -320,7 +320,7 @@ var TetherClient = class {
|
|
|
320
320
|
async createAgent(agentName, description = "") {
|
|
321
321
|
this._requireApiKey();
|
|
322
322
|
try {
|
|
323
|
-
const response = await fetch(`${this.baseUrl}/
|
|
323
|
+
const response = await fetch(`${this.baseUrl}/agents/issue`, {
|
|
324
324
|
method: "POST",
|
|
325
325
|
headers: {
|
|
326
326
|
"Content-Type": "application/json",
|
|
@@ -356,7 +356,7 @@ var TetherClient = class {
|
|
|
356
356
|
async listAgents() {
|
|
357
357
|
this._requireApiKey();
|
|
358
358
|
try {
|
|
359
|
-
const response = await fetch(`${this.baseUrl}/
|
|
359
|
+
const response = await fetch(`${this.baseUrl}/agents`, {
|
|
360
360
|
method: "GET",
|
|
361
361
|
headers: {
|
|
362
362
|
...this._authHeaders()
|
|
@@ -390,7 +390,7 @@ var TetherClient = class {
|
|
|
390
390
|
async deleteAgent(agentId) {
|
|
391
391
|
this._requireApiKey();
|
|
392
392
|
try {
|
|
393
|
-
const response = await fetch(`${this.baseUrl}/
|
|
393
|
+
const response = await fetch(`${this.baseUrl}/agents/${agentId}`, {
|
|
394
394
|
method: "DELETE",
|
|
395
395
|
headers: {
|
|
396
396
|
...this._authHeaders()
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/crypto.ts","../src/client.ts"],"sourcesContent":["/**\n * Tether Name SDK - Official Node.js library for tether.name agent identity verification\n * \n * @example\n * ```typescript\n * import { TetherClient } from 'tether-name';\n * \n * const client = new TetherClient({\n * credentialId: 'your-credential-id',\n * privateKeyPath: '/path/to/key.der'\n * });\n * \n * const result = await client.verify();\n * console.log(result.verified, result.agentName);\n * ```\n */\n\n// Main exports\nexport { TetherClient } from './client.js';\n\n// Types\nexport type {\n TetherClientConfig,\n ChallengeResponse,\n Agent,\n IssueCredentialResponse,\n VerificationRequest,\n VerificationResponse,\n VerificationResult,\n KeyFormat\n} from './types.js';\n\n// Errors\nexport {\n TetherError,\n TetherAPIError,\n TetherVerificationError\n} from './errors.js';\n\n// Crypto utilities (for advanced use cases)\nexport {\n loadPrivateKey,\n signChallenge,\n detectKeyFormat\n} from './crypto.js';","/**\n * Base error class for all Tether-related errors\n */\nexport class TetherError extends Error {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = 'TetherError';\n \n // Maintain proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, TetherError);\n }\n }\n}\n\n/**\n * Error thrown when verification fails\n */\nexport class TetherVerificationError extends TetherError {\n constructor(message: string, cause?: Error) {\n super(message, cause);\n this.name = 'TetherVerificationError';\n }\n}\n\n/**\n * Error thrown when API requests fail\n */\nexport class TetherAPIError extends TetherError {\n constructor(\n message: string,\n public readonly status?: number,\n public readonly response?: string,\n cause?: Error\n ) {\n super(message, cause);\n this.name = 'TetherAPIError';\n }\n}","import { createSign, createPrivateKey, KeyObject } from 'crypto';\nimport { readFileSync } from 'fs';\nimport { TetherError } from './errors.js';\nimport type { KeyFormat } from './types.js';\n\n/**\n * Imports a DER-encoded private key, trying PKCS#8 first then PKCS#1.\n */\nfunction importDerKey(derData: Buffer): KeyObject {\n try {\n return createPrivateKey({ key: derData, format: 'der', type: 'pkcs8' });\n } catch {\n // PKCS#8 failed — try PKCS#1\n }\n try {\n return createPrivateKey({ key: derData, format: 'der', type: 'pkcs1' });\n } catch {\n // PKCS#1 also failed\n }\n throw new TetherError(\n 'Failed to load DER private key: data is not valid PKCS#8 or PKCS#1. ' +\n 'Ensure the file is an RSA private key in DER format.'\n );\n}\n\n/**\n * Loads a private key from various sources\n */\nexport function loadPrivateKey(options: {\n keyPath?: string;\n keyPem?: string;\n keyBuffer?: Buffer;\n}): KeyObject {\n const { keyPath, keyPem, keyBuffer } = options;\n\n try {\n if (keyPem) {\n // PEM string provided directly\n return createPrivateKey(keyPem);\n }\n\n if (keyBuffer) {\n // DER buffer provided directly\n return importDerKey(keyBuffer);\n }\n\n if (keyPath) {\n // Read from file - detect format by extension or content\n const keyData = readFileSync(keyPath);\n\n // Try to detect format\n if (keyPath.endsWith('.pem') || keyData.toString().includes('-----BEGIN')) {\n // PEM format\n return createPrivateKey(keyData);\n } else {\n // Assume DER format\n return importDerKey(keyData);\n }\n }\n\n throw new TetherError('No private key provided');\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherError(\n `Failed to load private key: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n}\n\n/**\n * Signs a challenge string using RSA-SHA256\n * Returns URL-safe base64 encoded signature (no padding)\n */\nexport function signChallenge(privateKey: KeyObject, challenge: string): string {\n try {\n const sign = createSign('SHA256');\n sign.update(challenge);\n sign.end();\n \n const signature = sign.sign(privateKey);\n \n // Convert to URL-safe base64 without padding\n return signature\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '');\n } catch (error) {\n throw new TetherError(\n `Failed to sign challenge: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n}\n\n/**\n * Utility function to detect key format from file extension or content\n */\nexport function detectKeyFormat(keyPath: string): KeyFormat {\n if (keyPath.endsWith('.pem')) {\n return 'pem';\n }\n if (keyPath.endsWith('.der')) {\n return 'der';\n }\n \n // Try to read a small portion to detect format\n try {\n const keyData = readFileSync(keyPath, { encoding: 'utf8', flag: 'r' });\n if (keyData.includes('-----BEGIN')) {\n return 'pem';\n }\n } catch {\n // If we can't read as text, it's probably DER\n }\n \n return 'der';\n}","import { KeyObject } from 'crypto';\nimport { TetherError, TetherAPIError, TetherVerificationError } from './errors.js';\nimport { loadPrivateKey, signChallenge } from './crypto.js';\nimport type {\n TetherClientConfig,\n ChallengeResponse,\n Agent,\n IssueCredentialResponse,\n VerificationRequest,\n VerificationResponse,\n VerificationResult\n} from './types.js';\n\n/**\n * TetherClient - Official SDK for tether.name agent identity verification\n */\nexport class TetherClient {\n private readonly credentialId: string;\n private readonly privateKey: KeyObject | null;\n private readonly baseUrl: string;\n private readonly apiKey?: string;\n\n constructor(config: TetherClientConfig) {\n // Set base URL\n this.baseUrl = 'https://api.tether.name';\n\n // Get API key from config or environment\n this.apiKey = config.apiKey || process.env.TETHER_API_KEY;\n\n // Get credential ID from config or environment\n this.credentialId = config.credentialId || process.env.TETHER_CREDENTIAL_ID || '';\n\n // Load private key if key material is provided\n const keyPath = config.privateKeyPath || process.env.TETHER_PRIVATE_KEY_PATH;\n const hasKeyMaterial = keyPath || config.privateKeyPem || config.privateKeyBuffer;\n\n if (hasKeyMaterial) {\n this.privateKey = loadPrivateKey({\n keyPath,\n keyPem: config.privateKeyPem,\n keyBuffer: config.privateKeyBuffer\n });\n } else {\n this.privateKey = null;\n }\n\n // If no API key and no private key, credential ID and key are still needed for verify/sign\n // but we defer the error to when those methods are called\n if (!this.apiKey && !this.privateKey) {\n // Allow construction — errors thrown at method call time\n }\n\n if (!this.apiKey && !this.credentialId) {\n // Allow construction — errors thrown at method call time\n }\n }\n\n /**\n * Returns authorization headers when an API key is configured\n */\n private _authHeaders(): Record<string, string> {\n if (this.apiKey) {\n return { 'Authorization': `Bearer ${this.apiKey}` };\n }\n return {};\n }\n\n /**\n * Ensures a private key is available, throwing if not\n */\n private _requirePrivateKey(): KeyObject {\n if (!this.privateKey) {\n throw new TetherError(\n 'Private key is required for this operation. Provide privateKeyPath, privateKeyPem, or privateKeyBuffer in config, or set TETHER_PRIVATE_KEY_PATH environment variable.'\n );\n }\n return this.privateKey;\n }\n\n /**\n * Ensures an API key is available, throwing if not\n */\n private _requireApiKey(): void {\n if (!this.apiKey) {\n throw new TetherError(\n 'API key is required for agent management operations. Provide apiKey in config or set TETHER_API_KEY environment variable.'\n );\n }\n }\n\n /**\n * Ensures a credential ID is available, throwing if not\n */\n private _requireCredentialId(): string {\n if (!this.credentialId) {\n throw new TetherError(\n 'Credential ID is required for this operation. Provide it in config or set TETHER_CREDENTIAL_ID environment variable.'\n );\n }\n return this.credentialId;\n }\n\n /**\n * Request a challenge from the Tether API\n */\n async requestChallenge(): Promise<string> {\n try {\n const response = await fetch(`${this.baseUrl}/challenge`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n }\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `Challenge request failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n const data = await response.json() as ChallengeResponse;\n\n if (!data.code) {\n throw new TetherAPIError('Invalid challenge response: missing code');\n }\n\n return data.code;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to request challenge: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Sign a challenge string\n */\n sign(challenge: string): string {\n const key = this._requirePrivateKey();\n return signChallenge(key, challenge);\n }\n\n /**\n * Submit proof for a challenge\n */\n async submitProof(challenge: string, proof: string): Promise<VerificationResult> {\n const credentialId = this._requireCredentialId();\n\n try {\n const payload: VerificationRequest = {\n challenge,\n proof,\n credentialId\n };\n\n const response = await fetch(`${this.baseUrl}/challenge/verify`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(payload)\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `Verification failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n const data = await response.json() as VerificationResponse;\n\n // Convert API response to our result format\n const registeredSince = typeof data.registeredSince === 'number'\n ? new Date(data.registeredSince).toISOString()\n : data.registeredSince;\n\n return {\n verified: data.valid,\n agentName: data.agentName,\n verifyUrl: data.verifyUrl,\n email: data.email,\n registeredSince,\n error: data.error,\n challenge\n };\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to submit proof: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Perform complete verification in one call\n */\n async verify(): Promise<VerificationResult> {\n try {\n const challenge = await this.requestChallenge();\n const proof = this.sign(challenge);\n const result = await this.submitProof(challenge, proof);\n\n if (!result.verified) {\n throw new TetherVerificationError(\n result.error || 'Verification failed for unknown reason'\n );\n }\n\n return result;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherVerificationError(\n `Verification failed: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Create a new agent\n */\n async createAgent(agentName: string, description: string = ''): Promise<Agent> {\n this._requireApiKey();\n try {\n const response = await fetch(`${this.baseUrl}/credentials/issue`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...this._authHeaders()\n },\n body: JSON.stringify({ agentName, description })\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `Create agent failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n const data = await response.json() as IssueCredentialResponse;\n return data;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to create agent: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * List all agents\n */\n async listAgents(): Promise<Agent[]> {\n this._requireApiKey();\n try {\n const response = await fetch(`${this.baseUrl}/credentials`, {\n method: 'GET',\n headers: {\n ...this._authHeaders()\n }\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `List agents failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n const data = await response.json() as Agent[];\n return data;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to list agents: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Delete an agent by ID\n */\n async deleteAgent(agentId: string): Promise<boolean> {\n this._requireApiKey();\n try {\n const response = await fetch(`${this.baseUrl}/credentials/${agentId}`, {\n method: 'DELETE',\n headers: {\n ...this._authHeaders()\n }\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `Delete agent failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n return true;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to delete agent: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n}"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/crypto.ts","../src/client.ts"],"sourcesContent":["/**\n * Tether Name SDK - Official Node.js library for tether.name agent identity verification\n * \n * @example\n * ```typescript\n * import { TetherClient } from 'tether-name';\n * \n * const client = new TetherClient({\n * agentId: 'your-agent-id',\n * privateKeyPath: '/path/to/key.pem'\n * });\n * \n * const result = await client.verify();\n * console.log(result.verified, result.agentName);\n * ```\n */\n\n// Main exports\nexport { TetherClient } from './client.js';\n\n// Types\nexport type {\n TetherClientConfig,\n ChallengeResponse,\n Agent,\n IssueAgentResponse,\n VerificationRequest,\n VerificationResponse,\n VerificationResult,\n KeyFormat\n} from './types.js';\n\n// Errors\nexport {\n TetherError,\n TetherAPIError,\n TetherVerificationError\n} from './errors.js';\n\n// Crypto utilities (for advanced use cases)\nexport {\n loadPrivateKey,\n signChallenge,\n detectKeyFormat\n} from './crypto.js';","/**\n * Base error class for all Tether-related errors\n */\nexport class TetherError extends Error {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = 'TetherError';\n \n // Maintain proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, TetherError);\n }\n }\n}\n\n/**\n * Error thrown when verification fails\n */\nexport class TetherVerificationError extends TetherError {\n constructor(message: string, cause?: Error) {\n super(message, cause);\n this.name = 'TetherVerificationError';\n }\n}\n\n/**\n * Error thrown when API requests fail\n */\nexport class TetherAPIError extends TetherError {\n constructor(\n message: string,\n public readonly status?: number,\n public readonly response?: string,\n cause?: Error\n ) {\n super(message, cause);\n this.name = 'TetherAPIError';\n }\n}","import { createSign, createPrivateKey, KeyObject } from 'crypto';\nimport { readFileSync } from 'fs';\nimport { TetherError } from './errors.js';\nimport type { KeyFormat } from './types.js';\n\n/**\n * Imports a DER-encoded private key, trying PKCS#8 first then PKCS#1.\n */\nfunction importDerKey(derData: Buffer): KeyObject {\n try {\n return createPrivateKey({ key: derData, format: 'der', type: 'pkcs8' });\n } catch {\n // PKCS#8 failed — try PKCS#1\n }\n try {\n return createPrivateKey({ key: derData, format: 'der', type: 'pkcs1' });\n } catch {\n // PKCS#1 also failed\n }\n throw new TetherError(\n 'Failed to load DER private key: data is not valid PKCS#8 or PKCS#1. ' +\n 'Ensure the file is an RSA private key in DER format.'\n );\n}\n\n/**\n * Loads a private key from various sources\n */\nexport function loadPrivateKey(options: {\n keyPath?: string;\n keyPem?: string;\n keyBuffer?: Buffer;\n}): KeyObject {\n const { keyPath, keyPem, keyBuffer } = options;\n\n try {\n if (keyPem) {\n // PEM string provided directly\n return createPrivateKey(keyPem);\n }\n\n if (keyBuffer) {\n // DER buffer provided directly\n return importDerKey(keyBuffer);\n }\n\n if (keyPath) {\n // Read from file - detect format by extension or content\n const keyData = readFileSync(keyPath);\n\n // Try to detect format\n if (keyPath.endsWith('.pem') || keyData.toString().includes('-----BEGIN')) {\n // PEM format\n return createPrivateKey(keyData);\n } else {\n // Assume DER format\n return importDerKey(keyData);\n }\n }\n\n throw new TetherError('No private key provided');\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherError(\n `Failed to load private key: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n}\n\n/**\n * Signs a challenge string using RSA-SHA256\n * Returns URL-safe base64 encoded signature (no padding)\n */\nexport function signChallenge(privateKey: KeyObject, challenge: string): string {\n try {\n const sign = createSign('SHA256');\n sign.update(challenge);\n sign.end();\n \n const signature = sign.sign(privateKey);\n \n // Convert to URL-safe base64 without padding\n return signature\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '');\n } catch (error) {\n throw new TetherError(\n `Failed to sign challenge: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n}\n\n/**\n * Utility function to detect key format from file extension or content\n */\nexport function detectKeyFormat(keyPath: string): KeyFormat {\n if (keyPath.endsWith('.pem')) {\n return 'pem';\n }\n if (keyPath.endsWith('.der')) {\n return 'der';\n }\n \n // Try to read a small portion to detect format\n try {\n const keyData = readFileSync(keyPath, { encoding: 'utf8', flag: 'r' });\n if (keyData.includes('-----BEGIN')) {\n return 'pem';\n }\n } catch {\n // If we can't read as text, it's probably DER\n }\n \n return 'der';\n}","import { KeyObject } from 'crypto';\nimport { TetherError, TetherAPIError, TetherVerificationError } from './errors.js';\nimport { loadPrivateKey, signChallenge } from './crypto.js';\nimport type {\n TetherClientConfig,\n ChallengeResponse,\n Agent,\n IssueAgentResponse,\n VerificationRequest,\n VerificationResponse,\n VerificationResult\n} from './types.js';\n\n/**\n * TetherClient - Official SDK for tether.name agent identity verification\n */\nexport class TetherClient {\n private readonly agentId: string;\n private readonly privateKey: KeyObject | null;\n private readonly baseUrl: string;\n private readonly apiKey?: string;\n\n constructor(config: TetherClientConfig) {\n // Set base URL\n this.baseUrl = 'https://api.tether.name';\n\n // Get API key from config or environment\n this.apiKey = config.apiKey || process.env.TETHER_API_KEY;\n\n // Get agent ID from config or environment\n this.agentId = config.agentId || process.env.TETHER_AGENT_ID || '';\n\n // Load private key if key material is provided\n const keyPath = config.privateKeyPath || process.env.TETHER_PRIVATE_KEY_PATH;\n const hasKeyMaterial = keyPath || config.privateKeyPem || config.privateKeyBuffer;\n\n if (hasKeyMaterial) {\n this.privateKey = loadPrivateKey({\n keyPath,\n keyPem: config.privateKeyPem,\n keyBuffer: config.privateKeyBuffer\n });\n } else {\n this.privateKey = null;\n }\n\n // If no API key and no private key, agent ID and key are still needed for verify/sign\n // but we defer the error to when those methods are called\n if (!this.apiKey && !this.privateKey) {\n // Allow construction — errors thrown at method call time\n }\n\n if (!this.apiKey && !this.agentId) {\n // Allow construction — errors thrown at method call time\n }\n }\n\n /**\n * Returns authorization headers when an API key is configured\n */\n private _authHeaders(): Record<string, string> {\n if (this.apiKey) {\n return { 'Authorization': `Bearer ${this.apiKey}` };\n }\n return {};\n }\n\n /**\n * Ensures a private key is available, throwing if not\n */\n private _requirePrivateKey(): KeyObject {\n if (!this.privateKey) {\n throw new TetherError(\n 'Private key is required for this operation. Provide privateKeyPath, privateKeyPem, or privateKeyBuffer in config, or set TETHER_PRIVATE_KEY_PATH environment variable.'\n );\n }\n return this.privateKey;\n }\n\n /**\n * Ensures an API key is available, throwing if not\n */\n private _requireApiKey(): void {\n if (!this.apiKey) {\n throw new TetherError(\n 'API key is required for agent management operations. Provide apiKey in config or set TETHER_API_KEY environment variable.'\n );\n }\n }\n\n /**\n * Ensures an agent ID is available, throwing if not\n */\n private _requireAgentId(): string {\n if (!this.agentId) {\n throw new TetherError(\n 'Agent ID is required for this operation. Provide it in config or set TETHER_AGENT_ID environment variable.'\n );\n }\n return this.agentId;\n }\n\n /**\n * Request a challenge from the Tether API\n */\n async requestChallenge(): Promise<string> {\n try {\n const response = await fetch(`${this.baseUrl}/challenge`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n }\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `Challenge request failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n const data = await response.json() as ChallengeResponse;\n\n if (!data.code) {\n throw new TetherAPIError('Invalid challenge response: missing code');\n }\n\n return data.code;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to request challenge: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Sign a challenge string\n */\n sign(challenge: string): string {\n const key = this._requirePrivateKey();\n return signChallenge(key, challenge);\n }\n\n /**\n * Submit proof for a challenge\n */\n async submitProof(challenge: string, proof: string): Promise<VerificationResult> {\n const agentId = this._requireAgentId();\n\n try {\n const payload: VerificationRequest = {\n challenge,\n proof,\n agentId\n };\n\n const response = await fetch(`${this.baseUrl}/challenge/verify`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(payload)\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `Verification failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n const data = await response.json() as VerificationResponse;\n\n // Convert API response to our result format\n const registeredSince = typeof data.registeredSince === 'number'\n ? new Date(data.registeredSince).toISOString()\n : data.registeredSince;\n\n return {\n verified: data.valid,\n agentName: data.agentName,\n verifyUrl: data.verifyUrl,\n email: data.email,\n registeredSince,\n error: data.error,\n challenge\n };\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to submit proof: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Perform complete verification in one call\n */\n async verify(): Promise<VerificationResult> {\n try {\n const challenge = await this.requestChallenge();\n const proof = this.sign(challenge);\n const result = await this.submitProof(challenge, proof);\n\n if (!result.verified) {\n throw new TetherVerificationError(\n result.error || 'Verification failed for unknown reason'\n );\n }\n\n return result;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherVerificationError(\n `Verification failed: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Create a new agent\n */\n async createAgent(agentName: string, description: string = ''): Promise<Agent> {\n this._requireApiKey();\n try {\n const response = await fetch(`${this.baseUrl}/agents/issue`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...this._authHeaders()\n },\n body: JSON.stringify({ agentName, description })\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `Create agent failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n const data = await response.json() as IssueAgentResponse;\n return data;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to create agent: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * List all agents\n */\n async listAgents(): Promise<Agent[]> {\n this._requireApiKey();\n try {\n const response = await fetch(`${this.baseUrl}/agents`, {\n method: 'GET',\n headers: {\n ...this._authHeaders()\n }\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `List agents failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n const data = await response.json() as Agent[];\n return data;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to list agents: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Delete an agent by ID\n */\n async deleteAgent(agentId: string): Promise<boolean> {\n this._requireApiKey();\n try {\n const response = await fetch(`${this.baseUrl}/agents/${agentId}`, {\n method: 'DELETE',\n headers: {\n ...this._authHeaders()\n }\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `Delete agent failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n return true;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to delete agent: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -101,14 +101,14 @@ function detectKeyFormat(keyPath) {
|
|
|
101
101
|
|
|
102
102
|
// src/client.ts
|
|
103
103
|
var TetherClient = class {
|
|
104
|
-
|
|
104
|
+
agentId;
|
|
105
105
|
privateKey;
|
|
106
106
|
baseUrl;
|
|
107
107
|
apiKey;
|
|
108
108
|
constructor(config) {
|
|
109
109
|
this.baseUrl = "https://api.tether.name";
|
|
110
110
|
this.apiKey = config.apiKey || process.env.TETHER_API_KEY;
|
|
111
|
-
this.
|
|
111
|
+
this.agentId = config.agentId || process.env.TETHER_AGENT_ID || "";
|
|
112
112
|
const keyPath = config.privateKeyPath || process.env.TETHER_PRIVATE_KEY_PATH;
|
|
113
113
|
const hasKeyMaterial = keyPath || config.privateKeyPem || config.privateKeyBuffer;
|
|
114
114
|
if (hasKeyMaterial) {
|
|
@@ -122,7 +122,7 @@ var TetherClient = class {
|
|
|
122
122
|
}
|
|
123
123
|
if (!this.apiKey && !this.privateKey) {
|
|
124
124
|
}
|
|
125
|
-
if (!this.apiKey && !this.
|
|
125
|
+
if (!this.apiKey && !this.agentId) {
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
/**
|
|
@@ -156,15 +156,15 @@ var TetherClient = class {
|
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
/**
|
|
159
|
-
* Ensures
|
|
159
|
+
* Ensures an agent ID is available, throwing if not
|
|
160
160
|
*/
|
|
161
|
-
|
|
162
|
-
if (!this.
|
|
161
|
+
_requireAgentId() {
|
|
162
|
+
if (!this.agentId) {
|
|
163
163
|
throw new TetherError(
|
|
164
|
-
"
|
|
164
|
+
"Agent ID is required for this operation. Provide it in config or set TETHER_AGENT_ID environment variable."
|
|
165
165
|
);
|
|
166
166
|
}
|
|
167
|
-
return this.
|
|
167
|
+
return this.agentId;
|
|
168
168
|
}
|
|
169
169
|
/**
|
|
170
170
|
* Request a challenge from the Tether API
|
|
@@ -213,12 +213,12 @@ var TetherClient = class {
|
|
|
213
213
|
* Submit proof for a challenge
|
|
214
214
|
*/
|
|
215
215
|
async submitProof(challenge, proof) {
|
|
216
|
-
const
|
|
216
|
+
const agentId = this._requireAgentId();
|
|
217
217
|
try {
|
|
218
218
|
const payload = {
|
|
219
219
|
challenge,
|
|
220
220
|
proof,
|
|
221
|
-
|
|
221
|
+
agentId
|
|
222
222
|
};
|
|
223
223
|
const response = await fetch(`${this.baseUrl}/challenge/verify`, {
|
|
224
224
|
method: "POST",
|
|
@@ -288,7 +288,7 @@ var TetherClient = class {
|
|
|
288
288
|
async createAgent(agentName, description = "") {
|
|
289
289
|
this._requireApiKey();
|
|
290
290
|
try {
|
|
291
|
-
const response = await fetch(`${this.baseUrl}/
|
|
291
|
+
const response = await fetch(`${this.baseUrl}/agents/issue`, {
|
|
292
292
|
method: "POST",
|
|
293
293
|
headers: {
|
|
294
294
|
"Content-Type": "application/json",
|
|
@@ -324,7 +324,7 @@ var TetherClient = class {
|
|
|
324
324
|
async listAgents() {
|
|
325
325
|
this._requireApiKey();
|
|
326
326
|
try {
|
|
327
|
-
const response = await fetch(`${this.baseUrl}/
|
|
327
|
+
const response = await fetch(`${this.baseUrl}/agents`, {
|
|
328
328
|
method: "GET",
|
|
329
329
|
headers: {
|
|
330
330
|
...this._authHeaders()
|
|
@@ -358,7 +358,7 @@ var TetherClient = class {
|
|
|
358
358
|
async deleteAgent(agentId) {
|
|
359
359
|
this._requireApiKey();
|
|
360
360
|
try {
|
|
361
|
-
const response = await fetch(`${this.baseUrl}/
|
|
361
|
+
const response = await fetch(`${this.baseUrl}/agents/${agentId}`, {
|
|
362
362
|
method: "DELETE",
|
|
363
363
|
headers: {
|
|
364
364
|
...this._authHeaders()
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/crypto.ts","../src/client.ts"],"sourcesContent":["/**\n * Base error class for all Tether-related errors\n */\nexport class TetherError extends Error {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = 'TetherError';\n \n // Maintain proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, TetherError);\n }\n }\n}\n\n/**\n * Error thrown when verification fails\n */\nexport class TetherVerificationError extends TetherError {\n constructor(message: string, cause?: Error) {\n super(message, cause);\n this.name = 'TetherVerificationError';\n }\n}\n\n/**\n * Error thrown when API requests fail\n */\nexport class TetherAPIError extends TetherError {\n constructor(\n message: string,\n public readonly status?: number,\n public readonly response?: string,\n cause?: Error\n ) {\n super(message, cause);\n this.name = 'TetherAPIError';\n }\n}","import { createSign, createPrivateKey, KeyObject } from 'crypto';\nimport { readFileSync } from 'fs';\nimport { TetherError } from './errors.js';\nimport type { KeyFormat } from './types.js';\n\n/**\n * Imports a DER-encoded private key, trying PKCS#8 first then PKCS#1.\n */\nfunction importDerKey(derData: Buffer): KeyObject {\n try {\n return createPrivateKey({ key: derData, format: 'der', type: 'pkcs8' });\n } catch {\n // PKCS#8 failed — try PKCS#1\n }\n try {\n return createPrivateKey({ key: derData, format: 'der', type: 'pkcs1' });\n } catch {\n // PKCS#1 also failed\n }\n throw new TetherError(\n 'Failed to load DER private key: data is not valid PKCS#8 or PKCS#1. ' +\n 'Ensure the file is an RSA private key in DER format.'\n );\n}\n\n/**\n * Loads a private key from various sources\n */\nexport function loadPrivateKey(options: {\n keyPath?: string;\n keyPem?: string;\n keyBuffer?: Buffer;\n}): KeyObject {\n const { keyPath, keyPem, keyBuffer } = options;\n\n try {\n if (keyPem) {\n // PEM string provided directly\n return createPrivateKey(keyPem);\n }\n\n if (keyBuffer) {\n // DER buffer provided directly\n return importDerKey(keyBuffer);\n }\n\n if (keyPath) {\n // Read from file - detect format by extension or content\n const keyData = readFileSync(keyPath);\n\n // Try to detect format\n if (keyPath.endsWith('.pem') || keyData.toString().includes('-----BEGIN')) {\n // PEM format\n return createPrivateKey(keyData);\n } else {\n // Assume DER format\n return importDerKey(keyData);\n }\n }\n\n throw new TetherError('No private key provided');\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherError(\n `Failed to load private key: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n}\n\n/**\n * Signs a challenge string using RSA-SHA256\n * Returns URL-safe base64 encoded signature (no padding)\n */\nexport function signChallenge(privateKey: KeyObject, challenge: string): string {\n try {\n const sign = createSign('SHA256');\n sign.update(challenge);\n sign.end();\n \n const signature = sign.sign(privateKey);\n \n // Convert to URL-safe base64 without padding\n return signature\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '');\n } catch (error) {\n throw new TetherError(\n `Failed to sign challenge: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n}\n\n/**\n * Utility function to detect key format from file extension or content\n */\nexport function detectKeyFormat(keyPath: string): KeyFormat {\n if (keyPath.endsWith('.pem')) {\n return 'pem';\n }\n if (keyPath.endsWith('.der')) {\n return 'der';\n }\n \n // Try to read a small portion to detect format\n try {\n const keyData = readFileSync(keyPath, { encoding: 'utf8', flag: 'r' });\n if (keyData.includes('-----BEGIN')) {\n return 'pem';\n }\n } catch {\n // If we can't read as text, it's probably DER\n }\n \n return 'der';\n}","import { KeyObject } from 'crypto';\nimport { TetherError, TetherAPIError, TetherVerificationError } from './errors.js';\nimport { loadPrivateKey, signChallenge } from './crypto.js';\nimport type {\n TetherClientConfig,\n ChallengeResponse,\n Agent,\n IssueCredentialResponse,\n VerificationRequest,\n VerificationResponse,\n VerificationResult\n} from './types.js';\n\n/**\n * TetherClient - Official SDK for tether.name agent identity verification\n */\nexport class TetherClient {\n private readonly credentialId: string;\n private readonly privateKey: KeyObject | null;\n private readonly baseUrl: string;\n private readonly apiKey?: string;\n\n constructor(config: TetherClientConfig) {\n // Set base URL\n this.baseUrl = 'https://api.tether.name';\n\n // Get API key from config or environment\n this.apiKey = config.apiKey || process.env.TETHER_API_KEY;\n\n // Get credential ID from config or environment\n this.credentialId = config.credentialId || process.env.TETHER_CREDENTIAL_ID || '';\n\n // Load private key if key material is provided\n const keyPath = config.privateKeyPath || process.env.TETHER_PRIVATE_KEY_PATH;\n const hasKeyMaterial = keyPath || config.privateKeyPem || config.privateKeyBuffer;\n\n if (hasKeyMaterial) {\n this.privateKey = loadPrivateKey({\n keyPath,\n keyPem: config.privateKeyPem,\n keyBuffer: config.privateKeyBuffer\n });\n } else {\n this.privateKey = null;\n }\n\n // If no API key and no private key, credential ID and key are still needed for verify/sign\n // but we defer the error to when those methods are called\n if (!this.apiKey && !this.privateKey) {\n // Allow construction — errors thrown at method call time\n }\n\n if (!this.apiKey && !this.credentialId) {\n // Allow construction — errors thrown at method call time\n }\n }\n\n /**\n * Returns authorization headers when an API key is configured\n */\n private _authHeaders(): Record<string, string> {\n if (this.apiKey) {\n return { 'Authorization': `Bearer ${this.apiKey}` };\n }\n return {};\n }\n\n /**\n * Ensures a private key is available, throwing if not\n */\n private _requirePrivateKey(): KeyObject {\n if (!this.privateKey) {\n throw new TetherError(\n 'Private key is required for this operation. Provide privateKeyPath, privateKeyPem, or privateKeyBuffer in config, or set TETHER_PRIVATE_KEY_PATH environment variable.'\n );\n }\n return this.privateKey;\n }\n\n /**\n * Ensures an API key is available, throwing if not\n */\n private _requireApiKey(): void {\n if (!this.apiKey) {\n throw new TetherError(\n 'API key is required for agent management operations. Provide apiKey in config or set TETHER_API_KEY environment variable.'\n );\n }\n }\n\n /**\n * Ensures a credential ID is available, throwing if not\n */\n private _requireCredentialId(): string {\n if (!this.credentialId) {\n throw new TetherError(\n 'Credential ID is required for this operation. Provide it in config or set TETHER_CREDENTIAL_ID environment variable.'\n );\n }\n return this.credentialId;\n }\n\n /**\n * Request a challenge from the Tether API\n */\n async requestChallenge(): Promise<string> {\n try {\n const response = await fetch(`${this.baseUrl}/challenge`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n }\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `Challenge request failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n const data = await response.json() as ChallengeResponse;\n\n if (!data.code) {\n throw new TetherAPIError('Invalid challenge response: missing code');\n }\n\n return data.code;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to request challenge: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Sign a challenge string\n */\n sign(challenge: string): string {\n const key = this._requirePrivateKey();\n return signChallenge(key, challenge);\n }\n\n /**\n * Submit proof for a challenge\n */\n async submitProof(challenge: string, proof: string): Promise<VerificationResult> {\n const credentialId = this._requireCredentialId();\n\n try {\n const payload: VerificationRequest = {\n challenge,\n proof,\n credentialId\n };\n\n const response = await fetch(`${this.baseUrl}/challenge/verify`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(payload)\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `Verification failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n const data = await response.json() as VerificationResponse;\n\n // Convert API response to our result format\n const registeredSince = typeof data.registeredSince === 'number'\n ? new Date(data.registeredSince).toISOString()\n : data.registeredSince;\n\n return {\n verified: data.valid,\n agentName: data.agentName,\n verifyUrl: data.verifyUrl,\n email: data.email,\n registeredSince,\n error: data.error,\n challenge\n };\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to submit proof: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Perform complete verification in one call\n */\n async verify(): Promise<VerificationResult> {\n try {\n const challenge = await this.requestChallenge();\n const proof = this.sign(challenge);\n const result = await this.submitProof(challenge, proof);\n\n if (!result.verified) {\n throw new TetherVerificationError(\n result.error || 'Verification failed for unknown reason'\n );\n }\n\n return result;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherVerificationError(\n `Verification failed: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Create a new agent\n */\n async createAgent(agentName: string, description: string = ''): Promise<Agent> {\n this._requireApiKey();\n try {\n const response = await fetch(`${this.baseUrl}/credentials/issue`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...this._authHeaders()\n },\n body: JSON.stringify({ agentName, description })\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `Create agent failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n const data = await response.json() as IssueCredentialResponse;\n return data;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to create agent: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * List all agents\n */\n async listAgents(): Promise<Agent[]> {\n this._requireApiKey();\n try {\n const response = await fetch(`${this.baseUrl}/credentials`, {\n method: 'GET',\n headers: {\n ...this._authHeaders()\n }\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `List agents failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n const data = await response.json() as Agent[];\n return data;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to list agents: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Delete an agent by ID\n */\n async deleteAgent(agentId: string): Promise<boolean> {\n this._requireApiKey();\n try {\n const response = await fetch(`${this.baseUrl}/credentials/${agentId}`, {\n method: 'DELETE',\n headers: {\n ...this._authHeaders()\n }\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `Delete agent failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n return true;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to delete agent: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n}"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/crypto.ts","../src/client.ts"],"sourcesContent":["/**\n * Base error class for all Tether-related errors\n */\nexport class TetherError extends Error {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = 'TetherError';\n \n // Maintain proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, TetherError);\n }\n }\n}\n\n/**\n * Error thrown when verification fails\n */\nexport class TetherVerificationError extends TetherError {\n constructor(message: string, cause?: Error) {\n super(message, cause);\n this.name = 'TetherVerificationError';\n }\n}\n\n/**\n * Error thrown when API requests fail\n */\nexport class TetherAPIError extends TetherError {\n constructor(\n message: string,\n public readonly status?: number,\n public readonly response?: string,\n cause?: Error\n ) {\n super(message, cause);\n this.name = 'TetherAPIError';\n }\n}","import { createSign, createPrivateKey, KeyObject } from 'crypto';\nimport { readFileSync } from 'fs';\nimport { TetherError } from './errors.js';\nimport type { KeyFormat } from './types.js';\n\n/**\n * Imports a DER-encoded private key, trying PKCS#8 first then PKCS#1.\n */\nfunction importDerKey(derData: Buffer): KeyObject {\n try {\n return createPrivateKey({ key: derData, format: 'der', type: 'pkcs8' });\n } catch {\n // PKCS#8 failed — try PKCS#1\n }\n try {\n return createPrivateKey({ key: derData, format: 'der', type: 'pkcs1' });\n } catch {\n // PKCS#1 also failed\n }\n throw new TetherError(\n 'Failed to load DER private key: data is not valid PKCS#8 or PKCS#1. ' +\n 'Ensure the file is an RSA private key in DER format.'\n );\n}\n\n/**\n * Loads a private key from various sources\n */\nexport function loadPrivateKey(options: {\n keyPath?: string;\n keyPem?: string;\n keyBuffer?: Buffer;\n}): KeyObject {\n const { keyPath, keyPem, keyBuffer } = options;\n\n try {\n if (keyPem) {\n // PEM string provided directly\n return createPrivateKey(keyPem);\n }\n\n if (keyBuffer) {\n // DER buffer provided directly\n return importDerKey(keyBuffer);\n }\n\n if (keyPath) {\n // Read from file - detect format by extension or content\n const keyData = readFileSync(keyPath);\n\n // Try to detect format\n if (keyPath.endsWith('.pem') || keyData.toString().includes('-----BEGIN')) {\n // PEM format\n return createPrivateKey(keyData);\n } else {\n // Assume DER format\n return importDerKey(keyData);\n }\n }\n\n throw new TetherError('No private key provided');\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherError(\n `Failed to load private key: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n}\n\n/**\n * Signs a challenge string using RSA-SHA256\n * Returns URL-safe base64 encoded signature (no padding)\n */\nexport function signChallenge(privateKey: KeyObject, challenge: string): string {\n try {\n const sign = createSign('SHA256');\n sign.update(challenge);\n sign.end();\n \n const signature = sign.sign(privateKey);\n \n // Convert to URL-safe base64 without padding\n return signature\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '');\n } catch (error) {\n throw new TetherError(\n `Failed to sign challenge: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n}\n\n/**\n * Utility function to detect key format from file extension or content\n */\nexport function detectKeyFormat(keyPath: string): KeyFormat {\n if (keyPath.endsWith('.pem')) {\n return 'pem';\n }\n if (keyPath.endsWith('.der')) {\n return 'der';\n }\n \n // Try to read a small portion to detect format\n try {\n const keyData = readFileSync(keyPath, { encoding: 'utf8', flag: 'r' });\n if (keyData.includes('-----BEGIN')) {\n return 'pem';\n }\n } catch {\n // If we can't read as text, it's probably DER\n }\n \n return 'der';\n}","import { KeyObject } from 'crypto';\nimport { TetherError, TetherAPIError, TetherVerificationError } from './errors.js';\nimport { loadPrivateKey, signChallenge } from './crypto.js';\nimport type {\n TetherClientConfig,\n ChallengeResponse,\n Agent,\n IssueAgentResponse,\n VerificationRequest,\n VerificationResponse,\n VerificationResult\n} from './types.js';\n\n/**\n * TetherClient - Official SDK for tether.name agent identity verification\n */\nexport class TetherClient {\n private readonly agentId: string;\n private readonly privateKey: KeyObject | null;\n private readonly baseUrl: string;\n private readonly apiKey?: string;\n\n constructor(config: TetherClientConfig) {\n // Set base URL\n this.baseUrl = 'https://api.tether.name';\n\n // Get API key from config or environment\n this.apiKey = config.apiKey || process.env.TETHER_API_KEY;\n\n // Get agent ID from config or environment\n this.agentId = config.agentId || process.env.TETHER_AGENT_ID || '';\n\n // Load private key if key material is provided\n const keyPath = config.privateKeyPath || process.env.TETHER_PRIVATE_KEY_PATH;\n const hasKeyMaterial = keyPath || config.privateKeyPem || config.privateKeyBuffer;\n\n if (hasKeyMaterial) {\n this.privateKey = loadPrivateKey({\n keyPath,\n keyPem: config.privateKeyPem,\n keyBuffer: config.privateKeyBuffer\n });\n } else {\n this.privateKey = null;\n }\n\n // If no API key and no private key, agent ID and key are still needed for verify/sign\n // but we defer the error to when those methods are called\n if (!this.apiKey && !this.privateKey) {\n // Allow construction — errors thrown at method call time\n }\n\n if (!this.apiKey && !this.agentId) {\n // Allow construction — errors thrown at method call time\n }\n }\n\n /**\n * Returns authorization headers when an API key is configured\n */\n private _authHeaders(): Record<string, string> {\n if (this.apiKey) {\n return { 'Authorization': `Bearer ${this.apiKey}` };\n }\n return {};\n }\n\n /**\n * Ensures a private key is available, throwing if not\n */\n private _requirePrivateKey(): KeyObject {\n if (!this.privateKey) {\n throw new TetherError(\n 'Private key is required for this operation. Provide privateKeyPath, privateKeyPem, or privateKeyBuffer in config, or set TETHER_PRIVATE_KEY_PATH environment variable.'\n );\n }\n return this.privateKey;\n }\n\n /**\n * Ensures an API key is available, throwing if not\n */\n private _requireApiKey(): void {\n if (!this.apiKey) {\n throw new TetherError(\n 'API key is required for agent management operations. Provide apiKey in config or set TETHER_API_KEY environment variable.'\n );\n }\n }\n\n /**\n * Ensures an agent ID is available, throwing if not\n */\n private _requireAgentId(): string {\n if (!this.agentId) {\n throw new TetherError(\n 'Agent ID is required for this operation. Provide it in config or set TETHER_AGENT_ID environment variable.'\n );\n }\n return this.agentId;\n }\n\n /**\n * Request a challenge from the Tether API\n */\n async requestChallenge(): Promise<string> {\n try {\n const response = await fetch(`${this.baseUrl}/challenge`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n }\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `Challenge request failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n const data = await response.json() as ChallengeResponse;\n\n if (!data.code) {\n throw new TetherAPIError('Invalid challenge response: missing code');\n }\n\n return data.code;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to request challenge: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Sign a challenge string\n */\n sign(challenge: string): string {\n const key = this._requirePrivateKey();\n return signChallenge(key, challenge);\n }\n\n /**\n * Submit proof for a challenge\n */\n async submitProof(challenge: string, proof: string): Promise<VerificationResult> {\n const agentId = this._requireAgentId();\n\n try {\n const payload: VerificationRequest = {\n challenge,\n proof,\n agentId\n };\n\n const response = await fetch(`${this.baseUrl}/challenge/verify`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(payload)\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `Verification failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n const data = await response.json() as VerificationResponse;\n\n // Convert API response to our result format\n const registeredSince = typeof data.registeredSince === 'number'\n ? new Date(data.registeredSince).toISOString()\n : data.registeredSince;\n\n return {\n verified: data.valid,\n agentName: data.agentName,\n verifyUrl: data.verifyUrl,\n email: data.email,\n registeredSince,\n error: data.error,\n challenge\n };\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to submit proof: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Perform complete verification in one call\n */\n async verify(): Promise<VerificationResult> {\n try {\n const challenge = await this.requestChallenge();\n const proof = this.sign(challenge);\n const result = await this.submitProof(challenge, proof);\n\n if (!result.verified) {\n throw new TetherVerificationError(\n result.error || 'Verification failed for unknown reason'\n );\n }\n\n return result;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherVerificationError(\n `Verification failed: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Create a new agent\n */\n async createAgent(agentName: string, description: string = ''): Promise<Agent> {\n this._requireApiKey();\n try {\n const response = await fetch(`${this.baseUrl}/agents/issue`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...this._authHeaders()\n },\n body: JSON.stringify({ agentName, description })\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `Create agent failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n const data = await response.json() as IssueAgentResponse;\n return data;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to create agent: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * List all agents\n */\n async listAgents(): Promise<Agent[]> {\n this._requireApiKey();\n try {\n const response = await fetch(`${this.baseUrl}/agents`, {\n method: 'GET',\n headers: {\n ...this._authHeaders()\n }\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `List agents failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n const data = await response.json() as Agent[];\n return data;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to list agents: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Delete an agent by ID\n */\n async deleteAgent(agentId: string): Promise<boolean> {\n this._requireApiKey();\n try {\n const response = await fetch(`${this.baseUrl}/agents/${agentId}`, {\n method: 'DELETE',\n headers: {\n ...this._authHeaders()\n }\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new TetherAPIError(\n `Delete agent failed: ${response.status} ${response.statusText}`,\n response.status,\n errorText\n );\n }\n\n return true;\n } catch (error) {\n if (error instanceof TetherError) {\n throw error;\n }\n throw new TetherAPIError(\n `Failed to delete agent: ${error instanceof Error ? error.message : String(error)}`,\n undefined,\n undefined,\n error instanceof Error ? error : undefined\n );\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tether-name",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Official Node.js SDK for tether.name - AI agent identity verification",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"homepage": "https://tether.name",
|
|
35
35
|
"repository": {
|
|
36
36
|
"type": "git",
|
|
37
|
-
"url": "https://github.com/tether-name/tether-name-node.git"
|
|
37
|
+
"url": "git+https://github.com/tether-name/tether-name-node.git"
|
|
38
38
|
},
|
|
39
39
|
"bugs": {
|
|
40
40
|
"url": "https://github.com/tether-name/tether-name-node/issues"
|