tether-name 1.0.0 → 1.0.2

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 CHANGED
@@ -16,7 +16,7 @@ Requires Node.js 18+ (uses native `fetch` and `crypto` modules).
16
16
  import { TetherClient } from 'tether-name';
17
17
 
18
18
  const client = new TetherClient({
19
- credentialId: 'rgUOzbqar8z0Ag9RZH5I',
19
+ credentialId: 'your-credential-id',
20
20
  privateKeyPath: '/path/to/your/private-key.der'
21
21
  });
22
22
 
@@ -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
- credentialId: 'rgUOzbqar8z0Ag9RZH5I',
38
+ credentialId: 'your-credential-id',
39
39
  privateKeyPem: `-----BEGIN RSA PRIVATE KEY-----
40
40
  MIIEpAIBAAKCAQEA...
41
41
  -----END RSA PRIVATE KEY-----`
@@ -68,19 +68,53 @@ try {
68
68
 
69
69
  ```typescript
70
70
  interface TetherClientConfig {
71
- // Credential ID (required)
71
+ // API key (for agent management — no private key needed)
72
+ apiKey?: string; // Or use TETHER_API_KEY env var
73
+
74
+ // Credential ID (required for verify/sign, optional with apiKey)
72
75
  credentialId?: string; // Or use TETHER_CREDENTIAL_ID env var
73
-
74
- // Private key (choose one)
76
+
77
+ // Private key (required for verify/sign, choose one)
75
78
  privateKeyPath?: string; // Path to DER or PEM file
76
79
  privateKeyPem?: string; // PEM string directly
77
80
  privateKeyBuffer?: Buffer; // DER buffer directly
78
-
81
+
79
82
  // Optional
80
83
  baseUrl?: string; // API base URL (defaults to https://api.tether.name)
81
84
  }
82
85
  ```
83
86
 
87
+ ### Authentication Modes
88
+
89
+ **API key only** — manage agents without a private key:
90
+
91
+ ```typescript
92
+ const client = new TetherClient({
93
+ apiKey: 'sk-tether-name-...'
94
+ });
95
+
96
+ const agent = await client.createAgent('my-bot');
97
+ ```
98
+
99
+ **API key + credential + private key** — full access (management and verification):
100
+
101
+ ```typescript
102
+ const client = new TetherClient({
103
+ apiKey: 'sk-tether-name-...',
104
+ credentialId: 'your-credential-id',
105
+ privateKeyPath: '/path/to/key.der'
106
+ });
107
+ ```
108
+
109
+ **Credential + private key only** — verification without agent management (original behavior):
110
+
111
+ ```typescript
112
+ const client = new TetherClient({
113
+ credentialId: 'your-credential-id',
114
+ privateKeyPath: '/path/to/key.der'
115
+ });
116
+ ```
117
+
84
118
  ### Key Format Support
85
119
 
86
120
  The SDK supports both DER and PEM private key formats:
@@ -106,11 +140,32 @@ const client3 = new TetherClient({
106
140
  });
107
141
  ```
108
142
 
143
+ ## Agent Management
144
+
145
+ Create and manage agents programmatically with an API key:
146
+
147
+ ```typescript
148
+ const client = new TetherClient({ apiKey: 'sk-tether-name-...' });
149
+
150
+ // Create an agent
151
+ const agent = await client.createAgent('my-bot', 'Does helpful things');
152
+ console.log(agent.id); // "abc123"
153
+ console.log(agent.agentName); // "my-bot"
154
+ console.log(agent.registrationToken); // Use to register credentials
155
+
156
+ // List all agents
157
+ const agents = await client.listAgents();
158
+
159
+ // Delete an agent
160
+ await client.deleteAgent(agent.id);
161
+ ```
162
+
109
163
  ## Environment Variables
110
164
 
111
165
  Set these environment variables to avoid hardcoding credentials:
112
166
 
113
167
  ```bash
168
+ export TETHER_API_KEY="sk-tether-name-..." # API key for agent management
114
169
  export TETHER_CREDENTIAL_ID="your-credential-id"
115
170
  export TETHER_PRIVATE_KEY_PATH="/path/to/your/private-key.der"
116
171
  ```
@@ -151,6 +206,18 @@ Signs a challenge using the configured private key.
151
206
 
152
207
  Submits signed proof to verify the challenge.
153
208
 
209
+ #### `async createAgent(agentName: string, description?: string): Promise<Agent>`
210
+
211
+ Creates a new agent. Requires API key authentication.
212
+
213
+ #### `async listAgents(): Promise<Agent[]>`
214
+
215
+ Lists all agents for the authenticated account. Requires API key authentication.
216
+
217
+ #### `async deleteAgent(agentId: string): Promise<boolean>`
218
+
219
+ Deletes an agent by ID. Requires API key authentication. Returns `true` on success.
220
+
154
221
  ### Types
155
222
 
156
223
  ```typescript
@@ -165,6 +232,17 @@ interface VerificationResult {
165
232
  }
166
233
  ```
167
234
 
235
+ ```typescript
236
+ interface Agent {
237
+ id: string;
238
+ agentName: string;
239
+ description: string;
240
+ createdAt: number;
241
+ registrationToken?: string;
242
+ lastVerifiedAt?: number;
243
+ }
244
+ ```
245
+
168
246
  ### Errors
169
247
 
170
248
  - `TetherError` - Base error class
@@ -198,6 +276,30 @@ openssl rsa -in private-key.pem -outform DER -out private-key.der
198
276
  - The SDK uses SHA256withRSA signatures with URL-safe base64 encoding
199
277
  - All verification happens server-side at tether.name
200
278
 
279
+ ## Publishing
280
+
281
+ Published to npm automatically via GitHub Actions when a release is created.
282
+
283
+ ### Version checklist
284
+
285
+ Update the version in:
286
+
287
+ 1. `package.json` → `"version"`
288
+
289
+ ### Steps
290
+
291
+ 1. Update version numbers above
292
+ 2. Commit and push to `main`
293
+ 3. Create a GitHub release with a matching tag (e.g. `v1.0.0`)
294
+ 4. CI builds and publishes to npm automatically
295
+
296
+ ### Manual publish (if needed)
297
+
298
+ ```bash
299
+ npm run build
300
+ npm publish --access public
301
+ ```
302
+
201
303
  ## License
202
304
 
203
305
  MIT License - see [LICENSE](LICENSE) file for details.
@@ -205,6 +307,6 @@ MIT License - see [LICENSE](LICENSE) file for details.
205
307
  ## Links
206
308
 
207
309
  - 🌐 [Tether Website](https://tether.name)
208
- - 📘 [Documentation](https://tether.name/docs)
209
- - 🐛 [Issues](https://github.com/Commit451/tether-name-node/issues)
310
+ - 📘 [Documentation](https://docs.tether.name)
311
+ - 🐛 [Issues](https://github.com/tether-name/tether-name-node/issues)
210
312
  - 📦 [npm Package](https://www.npmjs.com/package/tether-name)
package/dist/index.d.mts CHANGED
@@ -14,6 +14,8 @@ interface TetherClientConfig {
14
14
  privateKeyBuffer?: Buffer;
15
15
  /** Base URL for the Tether API (defaults to https://api.tether.name) */
16
16
  baseUrl?: string;
17
+ /** API key for management operations (alternative to credential auth) */
18
+ apiKey?: string;
17
19
  }
18
20
  /**
19
21
  * Response from the challenge request endpoint
@@ -63,6 +65,27 @@ interface VerificationResult {
63
65
  * Supported private key formats
64
66
  */
65
67
  type KeyFormat = 'pem' | 'der';
68
+ /**
69
+ * An agent with its associated metadata
70
+ */
71
+ interface Agent {
72
+ id: string;
73
+ agentName: string;
74
+ description: string;
75
+ createdAt: number;
76
+ registrationToken?: string;
77
+ lastVerifiedAt?: number;
78
+ }
79
+ /**
80
+ * Response from the issue credential endpoint
81
+ */
82
+ interface IssueCredentialResponse {
83
+ id: string;
84
+ agentName: string;
85
+ description: string;
86
+ createdAt: number;
87
+ registrationToken: string;
88
+ }
66
89
 
67
90
  /**
68
91
  * TetherClient - Official SDK for tether.name agent identity verification
@@ -71,7 +94,20 @@ declare class TetherClient {
71
94
  private readonly credentialId;
72
95
  private readonly privateKey;
73
96
  private readonly baseUrl;
97
+ private readonly apiKey?;
74
98
  constructor(config: TetherClientConfig);
99
+ /**
100
+ * Returns authorization headers when an API key is configured
101
+ */
102
+ private _authHeaders;
103
+ /**
104
+ * Ensures a private key is available, throwing if not
105
+ */
106
+ private _requirePrivateKey;
107
+ /**
108
+ * Ensures a credential ID is available, throwing if not
109
+ */
110
+ private _requireCredentialId;
75
111
  /**
76
112
  * Request a challenge from the Tether API
77
113
  */
@@ -88,6 +124,18 @@ declare class TetherClient {
88
124
  * Perform complete verification in one call
89
125
  */
90
126
  verify(): Promise<VerificationResult>;
127
+ /**
128
+ * Create a new agent
129
+ */
130
+ createAgent(agentName: string, description?: string): Promise<Agent>;
131
+ /**
132
+ * List all agents
133
+ */
134
+ listAgents(): Promise<Agent[]>;
135
+ /**
136
+ * Delete an agent by ID
137
+ */
138
+ deleteAgent(agentId: string): Promise<boolean>;
91
139
  }
92
140
 
93
141
  /**
@@ -130,4 +178,4 @@ declare function signChallenge(privateKey: KeyObject, challenge: string): string
130
178
  */
131
179
  declare function detectKeyFormat(keyPath: string): KeyFormat;
132
180
 
133
- export { type ChallengeResponse, type KeyFormat, TetherAPIError, TetherClient, type TetherClientConfig, TetherError, TetherVerificationError, type VerificationRequest, type VerificationResponse, type VerificationResult, detectKeyFormat, loadPrivateKey, signChallenge };
181
+ export { type Agent, type ChallengeResponse, type IssueCredentialResponse, type KeyFormat, TetherAPIError, TetherClient, type TetherClientConfig, TetherError, TetherVerificationError, type VerificationRequest, type VerificationResponse, type VerificationResult, detectKeyFormat, loadPrivateKey, signChallenge };
package/dist/index.d.ts CHANGED
@@ -14,6 +14,8 @@ interface TetherClientConfig {
14
14
  privateKeyBuffer?: Buffer;
15
15
  /** Base URL for the Tether API (defaults to https://api.tether.name) */
16
16
  baseUrl?: string;
17
+ /** API key for management operations (alternative to credential auth) */
18
+ apiKey?: string;
17
19
  }
18
20
  /**
19
21
  * Response from the challenge request endpoint
@@ -63,6 +65,27 @@ interface VerificationResult {
63
65
  * Supported private key formats
64
66
  */
65
67
  type KeyFormat = 'pem' | 'der';
68
+ /**
69
+ * An agent with its associated metadata
70
+ */
71
+ interface Agent {
72
+ id: string;
73
+ agentName: string;
74
+ description: string;
75
+ createdAt: number;
76
+ registrationToken?: string;
77
+ lastVerifiedAt?: number;
78
+ }
79
+ /**
80
+ * Response from the issue credential endpoint
81
+ */
82
+ interface IssueCredentialResponse {
83
+ id: string;
84
+ agentName: string;
85
+ description: string;
86
+ createdAt: number;
87
+ registrationToken: string;
88
+ }
66
89
 
67
90
  /**
68
91
  * TetherClient - Official SDK for tether.name agent identity verification
@@ -71,7 +94,20 @@ declare class TetherClient {
71
94
  private readonly credentialId;
72
95
  private readonly privateKey;
73
96
  private readonly baseUrl;
97
+ private readonly apiKey?;
74
98
  constructor(config: TetherClientConfig);
99
+ /**
100
+ * Returns authorization headers when an API key is configured
101
+ */
102
+ private _authHeaders;
103
+ /**
104
+ * Ensures a private key is available, throwing if not
105
+ */
106
+ private _requirePrivateKey;
107
+ /**
108
+ * Ensures a credential ID is available, throwing if not
109
+ */
110
+ private _requireCredentialId;
75
111
  /**
76
112
  * Request a challenge from the Tether API
77
113
  */
@@ -88,6 +124,18 @@ declare class TetherClient {
88
124
  * Perform complete verification in one call
89
125
  */
90
126
  verify(): Promise<VerificationResult>;
127
+ /**
128
+ * Create a new agent
129
+ */
130
+ createAgent(agentName: string, description?: string): Promise<Agent>;
131
+ /**
132
+ * List all agents
133
+ */
134
+ listAgents(): Promise<Agent[]>;
135
+ /**
136
+ * Delete an agent by ID
137
+ */
138
+ deleteAgent(agentId: string): Promise<boolean>;
91
139
  }
92
140
 
93
141
  /**
@@ -130,4 +178,4 @@ declare function signChallenge(privateKey: KeyObject, challenge: string): string
130
178
  */
131
179
  declare function detectKeyFormat(keyPath: string): KeyFormat;
132
180
 
133
- export { type ChallengeResponse, type KeyFormat, TetherAPIError, TetherClient, type TetherClientConfig, TetherError, TetherVerificationError, type VerificationRequest, type VerificationResponse, type VerificationResult, detectKeyFormat, loadPrivateKey, signChallenge };
181
+ export { type Agent, type ChallengeResponse, type IssueCredentialResponse, type KeyFormat, TetherAPIError, TetherClient, type TetherClientConfig, TetherError, TetherVerificationError, type VerificationRequest, type VerificationResponse, type VerificationResult, detectKeyFormat, loadPrivateKey, signChallenge };
package/dist/index.js CHANGED
@@ -131,18 +131,57 @@ var TetherClient = class {
131
131
  credentialId;
132
132
  privateKey;
133
133
  baseUrl;
134
+ apiKey;
134
135
  constructor(config) {
136
+ this.baseUrl = config.baseUrl || "https://api.tether.name";
137
+ this.apiKey = config.apiKey || process.env.TETHER_API_KEY;
135
138
  this.credentialId = config.credentialId || process.env.TETHER_CREDENTIAL_ID || "";
139
+ const keyPath = config.privateKeyPath || process.env.TETHER_PRIVATE_KEY_PATH;
140
+ const hasKeyMaterial = keyPath || config.privateKeyPem || config.privateKeyBuffer;
141
+ if (hasKeyMaterial) {
142
+ this.privateKey = loadPrivateKey({
143
+ keyPath,
144
+ keyPem: config.privateKeyPem,
145
+ keyBuffer: config.privateKeyBuffer
146
+ });
147
+ } else {
148
+ this.privateKey = null;
149
+ }
150
+ if (!this.apiKey && !this.privateKey) {
151
+ }
152
+ if (!this.apiKey && !this.credentialId) {
153
+ }
154
+ }
155
+ /**
156
+ * Returns authorization headers when an API key is configured
157
+ */
158
+ _authHeaders() {
159
+ if (this.apiKey) {
160
+ return { "Authorization": `Bearer ${this.apiKey}` };
161
+ }
162
+ return {};
163
+ }
164
+ /**
165
+ * Ensures a private key is available, throwing if not
166
+ */
167
+ _requirePrivateKey() {
168
+ if (!this.privateKey) {
169
+ throw new TetherError(
170
+ "Private key is required for this operation. Provide privateKeyPath, privateKeyPem, or privateKeyBuffer in config, or set TETHER_PRIVATE_KEY_PATH environment variable."
171
+ );
172
+ }
173
+ return this.privateKey;
174
+ }
175
+ /**
176
+ * Ensures a credential ID is available, throwing if not
177
+ */
178
+ _requireCredentialId() {
136
179
  if (!this.credentialId) {
137
- throw new TetherError("Credential ID is required. Provide it in config or set TETHER_CREDENTIAL_ID environment variable.");
180
+ throw new TetherError(
181
+ "Credential ID is required for this operation. Provide it in config or set TETHER_CREDENTIAL_ID environment variable."
182
+ );
138
183
  }
139
- const keyPath = config.privateKeyPath || process.env.TETHER_PRIVATE_KEY_PATH;
140
- this.privateKey = loadPrivateKey({
141
- keyPath,
142
- keyPem: config.privateKeyPem,
143
- keyBuffer: config.privateKeyBuffer
144
- });
145
- this.baseUrl = config.baseUrl || "https://api.tether.name";
184
+ return this.credentialId;
146
185
  }
147
186
  /**
148
187
  * Request a challenge from the Tether API
@@ -184,17 +223,19 @@ var TetherClient = class {
184
223
  * Sign a challenge string
185
224
  */
186
225
  sign(challenge) {
187
- return signChallenge(this.privateKey, challenge);
226
+ const key = this._requirePrivateKey();
227
+ return signChallenge(key, challenge);
188
228
  }
189
229
  /**
190
230
  * Submit proof for a challenge
191
231
  */
192
232
  async submitProof(challenge, proof) {
233
+ const credentialId = this._requireCredentialId();
193
234
  try {
194
235
  const payload = {
195
236
  challenge,
196
237
  proof,
197
- credentialId: this.credentialId
238
+ credentialId
198
239
  };
199
240
  const response = await fetch(`${this.baseUrl}/challenge/verify`, {
200
241
  method: "POST",
@@ -257,6 +298,106 @@ var TetherClient = class {
257
298
  );
258
299
  }
259
300
  }
301
+ /**
302
+ * Create a new agent
303
+ */
304
+ async createAgent(agentName, description = "") {
305
+ try {
306
+ const response = await fetch(`${this.baseUrl}/credentials/issue`, {
307
+ method: "POST",
308
+ headers: {
309
+ "Content-Type": "application/json",
310
+ ...this._authHeaders()
311
+ },
312
+ body: JSON.stringify({ agentName, description })
313
+ });
314
+ if (!response.ok) {
315
+ const errorText = await response.text().catch(() => "Unknown error");
316
+ throw new TetherAPIError(
317
+ `Create agent failed: ${response.status} ${response.statusText}`,
318
+ response.status,
319
+ errorText
320
+ );
321
+ }
322
+ const data = await response.json();
323
+ return data;
324
+ } catch (error) {
325
+ if (error instanceof TetherError) {
326
+ throw error;
327
+ }
328
+ throw new TetherAPIError(
329
+ `Failed to create agent: ${error instanceof Error ? error.message : String(error)}`,
330
+ void 0,
331
+ void 0,
332
+ error instanceof Error ? error : void 0
333
+ );
334
+ }
335
+ }
336
+ /**
337
+ * List all agents
338
+ */
339
+ async listAgents() {
340
+ try {
341
+ const response = await fetch(`${this.baseUrl}/credentials`, {
342
+ method: "GET",
343
+ headers: {
344
+ ...this._authHeaders()
345
+ }
346
+ });
347
+ if (!response.ok) {
348
+ const errorText = await response.text().catch(() => "Unknown error");
349
+ throw new TetherAPIError(
350
+ `List agents failed: ${response.status} ${response.statusText}`,
351
+ response.status,
352
+ errorText
353
+ );
354
+ }
355
+ const data = await response.json();
356
+ return data;
357
+ } catch (error) {
358
+ if (error instanceof TetherError) {
359
+ throw error;
360
+ }
361
+ throw new TetherAPIError(
362
+ `Failed to list agents: ${error instanceof Error ? error.message : String(error)}`,
363
+ void 0,
364
+ void 0,
365
+ error instanceof Error ? error : void 0
366
+ );
367
+ }
368
+ }
369
+ /**
370
+ * Delete an agent by ID
371
+ */
372
+ async deleteAgent(agentId) {
373
+ try {
374
+ const response = await fetch(`${this.baseUrl}/credentials/${agentId}`, {
375
+ method: "DELETE",
376
+ headers: {
377
+ ...this._authHeaders()
378
+ }
379
+ });
380
+ if (!response.ok) {
381
+ const errorText = await response.text().catch(() => "Unknown error");
382
+ throw new TetherAPIError(
383
+ `Delete agent failed: ${response.status} ${response.statusText}`,
384
+ response.status,
385
+ errorText
386
+ );
387
+ }
388
+ return true;
389
+ } catch (error) {
390
+ if (error instanceof TetherError) {
391
+ throw error;
392
+ }
393
+ throw new TetherAPIError(
394
+ `Failed to delete agent: ${error instanceof Error ? error.message : String(error)}`,
395
+ void 0,
396
+ void 0,
397
+ error instanceof Error ? error : void 0
398
+ );
399
+ }
400
+ }
260
401
  };
261
402
  // Annotate the CommonJS export names for ESM import in node:
262
403
  0 && (module.exports = {
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 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 * 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 createPrivateKey({\n key: keyBuffer,\n format: 'der',\n type: 'pkcs1'\n });\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 createPrivateKey({\n key: keyData,\n format: 'der',\n type: 'pkcs1'\n });\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 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;\n private readonly baseUrl: string;\n\n constructor(config: TetherClientConfig) {\n // Get credential ID from config or environment\n this.credentialId = config.credentialId || process.env.TETHER_CREDENTIAL_ID || '';\n if (!this.credentialId) {\n throw new TetherError('Credential ID is required. Provide it in config or set TETHER_CREDENTIAL_ID environment variable.');\n }\n\n // Load private key\n const keyPath = config.privateKeyPath || process.env.TETHER_PRIVATE_KEY_PATH;\n this.privateKey = loadPrivateKey({\n keyPath,\n keyPem: config.privateKeyPem,\n keyBuffer: config.privateKeyBuffer\n });\n\n // Set base URL\n this.baseUrl = config.baseUrl || 'https://api.tether.name';\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 return signChallenge(this.privateKey, challenge);\n }\n\n /**\n * Submit proof for a challenge\n */\n async submitProof(challenge: string, proof: string): Promise<VerificationResult> {\n try {\n const payload: VerificationRequest = {\n challenge,\n proof,\n credentialId: this.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 return {\n verified: data.valid,\n agentName: data.agentName,\n verifyUrl: data.verifyUrl,\n email: data.email,\n registeredSince: data.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}"],"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 * 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 * 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 createPrivateKey({\n key: keyBuffer,\n format: 'der',\n type: 'pkcs1'\n });\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 createPrivateKey({\n key: keyData,\n format: 'der',\n type: 'pkcs1'\n });\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 = config.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 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 return {\n verified: data.valid,\n agentName: data.agentName,\n verifyUrl: data.verifyUrl,\n email: data.email,\n registeredSince: data.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 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 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 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":[]}
package/dist/index.mjs CHANGED
@@ -99,18 +99,57 @@ var TetherClient = class {
99
99
  credentialId;
100
100
  privateKey;
101
101
  baseUrl;
102
+ apiKey;
102
103
  constructor(config) {
104
+ this.baseUrl = config.baseUrl || "https://api.tether.name";
105
+ this.apiKey = config.apiKey || process.env.TETHER_API_KEY;
103
106
  this.credentialId = config.credentialId || process.env.TETHER_CREDENTIAL_ID || "";
107
+ const keyPath = config.privateKeyPath || process.env.TETHER_PRIVATE_KEY_PATH;
108
+ const hasKeyMaterial = keyPath || config.privateKeyPem || config.privateKeyBuffer;
109
+ if (hasKeyMaterial) {
110
+ this.privateKey = loadPrivateKey({
111
+ keyPath,
112
+ keyPem: config.privateKeyPem,
113
+ keyBuffer: config.privateKeyBuffer
114
+ });
115
+ } else {
116
+ this.privateKey = null;
117
+ }
118
+ if (!this.apiKey && !this.privateKey) {
119
+ }
120
+ if (!this.apiKey && !this.credentialId) {
121
+ }
122
+ }
123
+ /**
124
+ * Returns authorization headers when an API key is configured
125
+ */
126
+ _authHeaders() {
127
+ if (this.apiKey) {
128
+ return { "Authorization": `Bearer ${this.apiKey}` };
129
+ }
130
+ return {};
131
+ }
132
+ /**
133
+ * Ensures a private key is available, throwing if not
134
+ */
135
+ _requirePrivateKey() {
136
+ if (!this.privateKey) {
137
+ throw new TetherError(
138
+ "Private key is required for this operation. Provide privateKeyPath, privateKeyPem, or privateKeyBuffer in config, or set TETHER_PRIVATE_KEY_PATH environment variable."
139
+ );
140
+ }
141
+ return this.privateKey;
142
+ }
143
+ /**
144
+ * Ensures a credential ID is available, throwing if not
145
+ */
146
+ _requireCredentialId() {
104
147
  if (!this.credentialId) {
105
- throw new TetherError("Credential ID is required. Provide it in config or set TETHER_CREDENTIAL_ID environment variable.");
148
+ throw new TetherError(
149
+ "Credential ID is required for this operation. Provide it in config or set TETHER_CREDENTIAL_ID environment variable."
150
+ );
106
151
  }
107
- const keyPath = config.privateKeyPath || process.env.TETHER_PRIVATE_KEY_PATH;
108
- this.privateKey = loadPrivateKey({
109
- keyPath,
110
- keyPem: config.privateKeyPem,
111
- keyBuffer: config.privateKeyBuffer
112
- });
113
- this.baseUrl = config.baseUrl || "https://api.tether.name";
152
+ return this.credentialId;
114
153
  }
115
154
  /**
116
155
  * Request a challenge from the Tether API
@@ -152,17 +191,19 @@ var TetherClient = class {
152
191
  * Sign a challenge string
153
192
  */
154
193
  sign(challenge) {
155
- return signChallenge(this.privateKey, challenge);
194
+ const key = this._requirePrivateKey();
195
+ return signChallenge(key, challenge);
156
196
  }
157
197
  /**
158
198
  * Submit proof for a challenge
159
199
  */
160
200
  async submitProof(challenge, proof) {
201
+ const credentialId = this._requireCredentialId();
161
202
  try {
162
203
  const payload = {
163
204
  challenge,
164
205
  proof,
165
- credentialId: this.credentialId
206
+ credentialId
166
207
  };
167
208
  const response = await fetch(`${this.baseUrl}/challenge/verify`, {
168
209
  method: "POST",
@@ -225,6 +266,106 @@ var TetherClient = class {
225
266
  );
226
267
  }
227
268
  }
269
+ /**
270
+ * Create a new agent
271
+ */
272
+ async createAgent(agentName, description = "") {
273
+ try {
274
+ const response = await fetch(`${this.baseUrl}/credentials/issue`, {
275
+ method: "POST",
276
+ headers: {
277
+ "Content-Type": "application/json",
278
+ ...this._authHeaders()
279
+ },
280
+ body: JSON.stringify({ agentName, description })
281
+ });
282
+ if (!response.ok) {
283
+ const errorText = await response.text().catch(() => "Unknown error");
284
+ throw new TetherAPIError(
285
+ `Create agent failed: ${response.status} ${response.statusText}`,
286
+ response.status,
287
+ errorText
288
+ );
289
+ }
290
+ const data = await response.json();
291
+ return data;
292
+ } catch (error) {
293
+ if (error instanceof TetherError) {
294
+ throw error;
295
+ }
296
+ throw new TetherAPIError(
297
+ `Failed to create agent: ${error instanceof Error ? error.message : String(error)}`,
298
+ void 0,
299
+ void 0,
300
+ error instanceof Error ? error : void 0
301
+ );
302
+ }
303
+ }
304
+ /**
305
+ * List all agents
306
+ */
307
+ async listAgents() {
308
+ try {
309
+ const response = await fetch(`${this.baseUrl}/credentials`, {
310
+ method: "GET",
311
+ headers: {
312
+ ...this._authHeaders()
313
+ }
314
+ });
315
+ if (!response.ok) {
316
+ const errorText = await response.text().catch(() => "Unknown error");
317
+ throw new TetherAPIError(
318
+ `List agents failed: ${response.status} ${response.statusText}`,
319
+ response.status,
320
+ errorText
321
+ );
322
+ }
323
+ const data = await response.json();
324
+ return data;
325
+ } catch (error) {
326
+ if (error instanceof TetherError) {
327
+ throw error;
328
+ }
329
+ throw new TetherAPIError(
330
+ `Failed to list agents: ${error instanceof Error ? error.message : String(error)}`,
331
+ void 0,
332
+ void 0,
333
+ error instanceof Error ? error : void 0
334
+ );
335
+ }
336
+ }
337
+ /**
338
+ * Delete an agent by ID
339
+ */
340
+ async deleteAgent(agentId) {
341
+ try {
342
+ const response = await fetch(`${this.baseUrl}/credentials/${agentId}`, {
343
+ method: "DELETE",
344
+ headers: {
345
+ ...this._authHeaders()
346
+ }
347
+ });
348
+ if (!response.ok) {
349
+ const errorText = await response.text().catch(() => "Unknown error");
350
+ throw new TetherAPIError(
351
+ `Delete agent failed: ${response.status} ${response.statusText}`,
352
+ response.status,
353
+ errorText
354
+ );
355
+ }
356
+ return true;
357
+ } catch (error) {
358
+ if (error instanceof TetherError) {
359
+ throw error;
360
+ }
361
+ throw new TetherAPIError(
362
+ `Failed to delete agent: ${error instanceof Error ? error.message : String(error)}`,
363
+ void 0,
364
+ void 0,
365
+ error instanceof Error ? error : void 0
366
+ );
367
+ }
368
+ }
228
369
  };
229
370
  export {
230
371
  TetherAPIError,
@@ -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 * 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 createPrivateKey({\n key: keyBuffer,\n format: 'der',\n type: 'pkcs1'\n });\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 createPrivateKey({\n key: keyData,\n format: 'der',\n type: 'pkcs1'\n });\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 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;\n private readonly baseUrl: string;\n\n constructor(config: TetherClientConfig) {\n // Get credential ID from config or environment\n this.credentialId = config.credentialId || process.env.TETHER_CREDENTIAL_ID || '';\n if (!this.credentialId) {\n throw new TetherError('Credential ID is required. Provide it in config or set TETHER_CREDENTIAL_ID environment variable.');\n }\n\n // Load private key\n const keyPath = config.privateKeyPath || process.env.TETHER_PRIVATE_KEY_PATH;\n this.privateKey = loadPrivateKey({\n keyPath,\n keyPem: config.privateKeyPem,\n keyBuffer: config.privateKeyBuffer\n });\n\n // Set base URL\n this.baseUrl = config.baseUrl || 'https://api.tether.name';\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 return signChallenge(this.privateKey, challenge);\n }\n\n /**\n * Submit proof for a challenge\n */\n async submitProof(challenge: string, proof: string): Promise<VerificationResult> {\n try {\n const payload: VerificationRequest = {\n challenge,\n proof,\n credentialId: this.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 return {\n verified: data.valid,\n agentName: data.agentName,\n verifyUrl: data.verifyUrl,\n email: data.email,\n registeredSince: data.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}"],"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 * 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 createPrivateKey({\n key: keyBuffer,\n format: 'der',\n type: 'pkcs1'\n });\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 createPrivateKey({\n key: keyData,\n format: 'der',\n type: 'pkcs1'\n });\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 = config.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 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 return {\n verified: data.valid,\n agentName: data.agentName,\n verifyUrl: data.verifyUrl,\n email: data.email,\n registeredSince: data.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 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 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 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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tether-name",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
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",
@@ -40,12 +40,12 @@
40
40
  "url": "https://github.com/tether-name/tether-name-node/issues"
41
41
  },
42
42
  "engines": {
43
- "node": ">=18.0.0"
43
+ "node": ">=20.0.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/node": "^20.0.0",
47
47
  "tsup": "^8.0.0",
48
48
  "typescript": "^5.0.0",
49
- "vitest": "^1.0.0"
49
+ "vitest": "^4.0.18"
50
50
  }
51
- }
51
+ }