@astrasyncai/verification-gateway 2.3.10 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,368 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/registration/index.ts
31
+ var registration_exports = {};
32
+ __export(registration_exports, {
33
+ AstraSync: () => AstraSync,
34
+ AstraSyncError: () => AstraSyncError,
35
+ AuthenticationError: () => AuthenticationError,
36
+ KYDRequiredError: () => KYDRequiredError,
37
+ RegistrationDeniedError: () => RegistrationDeniedError,
38
+ RegistrationExpiredError: () => RegistrationExpiredError,
39
+ RegistrationTimeoutError: () => RegistrationTimeoutError
40
+ });
41
+ module.exports = __toCommonJS(registration_exports);
42
+
43
+ // src/registration/errors.ts
44
+ var AstraSyncError = class extends Error {
45
+ constructor(message, statusCode, code) {
46
+ super(message);
47
+ this.name = "AstraSyncError";
48
+ this.statusCode = statusCode;
49
+ this.code = code;
50
+ }
51
+ };
52
+ var KYDRequiredError = class extends AstraSyncError {
53
+ constructor(response) {
54
+ const kydUrl = response.kydUrl || "https://astrasync.ai/developer-profile";
55
+ super(
56
+ `KYD verification required before registering agents.
57
+ Complete your KYD profile at: ${kydUrl}`,
58
+ 403,
59
+ "KYD_REQUIRED"
60
+ );
61
+ this.name = "KYDRequiredError";
62
+ this.kydUrl = kydUrl;
63
+ this.ownerNotified = response.ownerNotified || false;
64
+ }
65
+ };
66
+ var AuthenticationError = class extends AstraSyncError {
67
+ constructor(message) {
68
+ super(message, 401, "AUTH_FAILED");
69
+ this.name = "AuthenticationError";
70
+ }
71
+ };
72
+ var RegistrationDeniedError = class extends AstraSyncError {
73
+ constructor(requestId, reason) {
74
+ super(
75
+ `Registration request ${requestId} was denied by the account owner.${reason ? ` Reason: ${reason}` : ""}`,
76
+ 403,
77
+ "REGISTRATION_DENIED"
78
+ );
79
+ this.name = "RegistrationDeniedError";
80
+ this.requestId = requestId;
81
+ this.reason = reason;
82
+ }
83
+ };
84
+ var RegistrationExpiredError = class extends AstraSyncError {
85
+ constructor(requestId) {
86
+ super(
87
+ `Registration request ${requestId} expired before the owner approved it. Submit a new registration request.`,
88
+ 410,
89
+ "REGISTRATION_EXPIRED"
90
+ );
91
+ this.name = "RegistrationExpiredError";
92
+ this.requestId = requestId;
93
+ }
94
+ };
95
+ var RegistrationTimeoutError = class extends AstraSyncError {
96
+ constructor(requestId) {
97
+ super(
98
+ `Timed out waiting for owner approval of registration request ${requestId}. The request is still active server-side; poll the request to resume waiting.`,
99
+ 408,
100
+ "REGISTRATION_TIMEOUT"
101
+ );
102
+ this.name = "RegistrationTimeoutError";
103
+ this.requestId = requestId;
104
+ }
105
+ };
106
+
107
+ // src/registration/api.ts
108
+ var DEFAULT_BASE_URL = "https://astrasync.ai";
109
+ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
110
+ var AstraSync = class {
111
+ constructor(config = {}) {
112
+ this.baseUrl = (config.baseUrl || process.env.ASTRASYNC_API_URL || DEFAULT_BASE_URL).replace(
113
+ /\/+$/,
114
+ ""
115
+ );
116
+ this.apiKey = config.apiKey || process.env.ASTRASYNC_API_KEY;
117
+ this.email = config.email;
118
+ this.password = config.password;
119
+ this.privateKey = config.privateKey;
120
+ if (!this.apiKey && !this.email) {
121
+ throw new AuthenticationError(
122
+ "Authentication required. Provide apiKey, or email+password. Set ASTRASYNC_API_KEY env var or pass config to constructor."
123
+ );
124
+ }
125
+ if (this.email && !this.password) {
126
+ throw new AuthenticationError("Password is required when using email authentication.");
127
+ }
128
+ }
129
+ /**
130
+ * Register a new AI agent on the AstraSync KYA Platform.
131
+ *
132
+ * The backend response depends on auth context:
133
+ * - **Crypto-keypair signed** (`privateKey` configured): synchronous 201,
134
+ * returns `{ status: 'active', agent }`.
135
+ * - **API-key only** (no signature): 202 pending, returns
136
+ * `{ status: 'pending_approval', requestId, pollUrl, expiresAt }`. The
137
+ * owner is notified by email and a dashboard alert is emitted; the agent
138
+ * becomes active only after the owner approves.
139
+ *
140
+ * Blocking mode: pass `{ waitForApproval: true }` to have the SDK poll the
141
+ * request until it resolves, then return the live agent record. The promise
142
+ * rejects with `RegistrationDeniedError`, `RegistrationExpiredError`, or
143
+ * `RegistrationTimeoutError` on the corresponding terminal states.
144
+ *
145
+ * @example Non-blocking (default — best for serverless / scheduled agents):
146
+ * ```typescript
147
+ * const result = await sdk.register({ name, pdlss });
148
+ * if (result.status === 'pending_approval') {
149
+ * storeRequestId(result.requestId);
150
+ * return; // function exits; resume later via pollRegistration()
151
+ * }
152
+ * ```
153
+ *
154
+ * @example Blocking (best for long-running services + CLI):
155
+ * ```typescript
156
+ * const agent = await sdk.register({
157
+ * name, pdlss, waitForApproval: true, timeoutMs: 600_000,
158
+ * onPending: ({ ageMs }) => console.log(`waiting ${ageMs}ms`),
159
+ * });
160
+ * ```
161
+ */
162
+ async register(options) {
163
+ const body = {
164
+ name: options.name,
165
+ ...options.description && { description: options.description },
166
+ ...options.agentType && { agentType: options.agentType },
167
+ ...options.apiEndpoint && { apiEndpoint: options.apiEndpoint },
168
+ ...options.model && { model: options.model },
169
+ ...options.framework && { framework: options.framework },
170
+ ...options.protocols && { protocols: options.protocols },
171
+ ...options.metadata && { metadata: options.metadata },
172
+ ...options.pdlss && { pdlss: options.pdlss }
173
+ };
174
+ const { status, body: raw } = await this.requestWithStatus("POST", "/api/agents/register", body);
175
+ if (status === 201) {
176
+ const active = {
177
+ status: "active",
178
+ agent: raw.data.agent
179
+ };
180
+ return active;
181
+ }
182
+ const pendingBody = raw;
183
+ const pending = {
184
+ status: "pending_approval",
185
+ requestId: pendingBody.requestId,
186
+ expiresAt: pendingBody.expiresAt,
187
+ pollUrl: pendingBody.pollUrl,
188
+ message: pendingBody.message
189
+ };
190
+ if (!options.waitForApproval) return pending;
191
+ return this.waitForApproval(pendingBody.requestId, options);
192
+ }
193
+ /**
194
+ * Poll the current state of a pending-approval registration request.
195
+ *
196
+ * Useful for caller-driven polling when `waitForApproval: false` (the
197
+ * default). The endpoint is unauthenticated — pass the `requestId` that
198
+ * was returned from the 202 response.
199
+ *
200
+ * @returns `state: 'pending'` while awaiting; `'approved'` carries the
201
+ * minted agent in `agent`; `'denied'` may carry the owner's
202
+ * `reason`; `'expired'` is terminal after 14 days.
203
+ */
204
+ async pollRegistration(requestId) {
205
+ const url = `${this.baseUrl}/api/agents/request-registration/${requestId}`;
206
+ const res = await fetch(url, { headers: { Accept: "application/json" } });
207
+ if (!res.ok) {
208
+ const errBody = await res.json().catch(() => ({}));
209
+ throw new AstraSyncError(
210
+ errBody.error || `pollRegistration failed: ${res.status}`,
211
+ res.status,
212
+ errBody.code
213
+ );
214
+ }
215
+ return await res.json();
216
+ }
217
+ /**
218
+ * Block until a pending registration request resolves to a terminal state.
219
+ * Resolves to the live `AgentRecord` on approval; rejects with the matching
220
+ * Registration*Error on deny/expire/timeout. Usually called via
221
+ * `register({ waitForApproval: true })`, but exposed for callers that want
222
+ * to fire-and-forget the initial register call and resume waiting later
223
+ * (e.g. after restoring a stored `requestId` on cold start).
224
+ */
225
+ async waitForApproval(requestId, options = {}) {
226
+ const timeoutMs = options.timeoutMs ?? 10 * 60 * 1e3;
227
+ const pollIntervalMs = options.pollIntervalMs ?? 5e3;
228
+ const start = Date.now();
229
+ const deadline = start + timeoutMs;
230
+ while (Date.now() < deadline) {
231
+ const result = await this.pollRegistration(requestId);
232
+ const ageMs = Date.now() - start;
233
+ options.onPending?.({ requestId, ageMs });
234
+ if (result.state === "approved") {
235
+ if (!result.agent) {
236
+ throw new AstraSyncError(
237
+ `Registration ${requestId} reported approved but no agent payload returned.`,
238
+ 500
239
+ );
240
+ }
241
+ return result.agent;
242
+ }
243
+ if (result.state === "denied") {
244
+ throw new RegistrationDeniedError(requestId, result.reason);
245
+ }
246
+ if (result.state === "expired") {
247
+ throw new RegistrationExpiredError(requestId);
248
+ }
249
+ await sleep(pollIntervalMs);
250
+ }
251
+ throw new RegistrationTimeoutError(requestId);
252
+ }
253
+ /**
254
+ * Look up an agent's public profile by ASTRA ID or UUID.
255
+ */
256
+ async verify(agentId) {
257
+ return this.request("GET", `/api/agents/verify/${agentId}`);
258
+ }
259
+ /**
260
+ * Check API health.
261
+ */
262
+ async health() {
263
+ const res = await fetch(`${this.baseUrl}/api/health/`);
264
+ if (!res.ok) {
265
+ throw new AstraSyncError(`Health check failed: ${res.status}`, res.status);
266
+ }
267
+ return res.json();
268
+ }
269
+ // ── Private helpers ──────────────────────────────────────────────
270
+ async request(method, endpoint, body) {
271
+ const { body: parsed } = await this.requestWithStatus(method, endpoint, body);
272
+ return parsed;
273
+ }
274
+ /**
275
+ * Variant of {@link request} that also returns the HTTP status code, so
276
+ * callers can branch on 201 vs 202 (or other success codes) without losing
277
+ * type information about the response body.
278
+ */
279
+ async requestWithStatus(method, endpoint, body) {
280
+ const url = `${this.baseUrl}${endpoint}`;
281
+ const headers = {
282
+ "Content-Type": "application/json"
283
+ };
284
+ const token = await this.getAuthToken();
285
+ headers["Authorization"] = `Bearer ${token}`;
286
+ if (this.privateKey) {
287
+ const signature = await this.signRequest(method, endpoint, body || {});
288
+ headers["X-AstraSync-Signature"] = signature;
289
+ }
290
+ const res = await fetch(url, {
291
+ method,
292
+ headers,
293
+ ...body ? { body: JSON.stringify(body) } : {}
294
+ });
295
+ if (!res.ok) {
296
+ const errorBody = await res.json().catch(() => ({ error: res.statusText }));
297
+ if (res.status === 403 && errorBody.code === "KYD_REQUIRED") {
298
+ throw new KYDRequiredError(errorBody);
299
+ }
300
+ throw new AstraSyncError(
301
+ errorBody.error || `Request failed: ${res.status}`,
302
+ res.status,
303
+ errorBody.code
304
+ );
305
+ }
306
+ return { status: res.status, body: await res.json() };
307
+ }
308
+ async getAuthToken() {
309
+ if (this.apiKey) {
310
+ return this.apiKey;
311
+ }
312
+ if (this.cachedJwt && this.jwtExpiresAt && Date.now() < this.jwtExpiresAt) {
313
+ return this.cachedJwt;
314
+ }
315
+ const res = await fetch(`${this.baseUrl}/api/auth/login`, {
316
+ method: "POST",
317
+ headers: { "Content-Type": "application/json" },
318
+ body: JSON.stringify({ email: this.email, password: this.password })
319
+ });
320
+ if (!res.ok) {
321
+ const errorBody = await res.json().catch(() => ({}));
322
+ throw new AuthenticationError(
323
+ errorBody.message || errorBody.error || "Login failed"
324
+ );
325
+ }
326
+ const data = await res.json();
327
+ this.cachedJwt = data.data.token;
328
+ this.jwtExpiresAt = Date.now() + 6 * 24 * 60 * 60 * 1e3;
329
+ return this.cachedJwt;
330
+ }
331
+ /**
332
+ * Sign a request using secp256k1 (ethers.js).
333
+ * Canonical message format: METHOD:ENDPOINT:SORTED_JSON_BODY
334
+ * Must match apps/backend/src/services/signature-verify.service.ts exactly.
335
+ */
336
+ async signRequest(method, endpoint, body) {
337
+ const { Wallet } = await import("ethers");
338
+ const sorted = this.sortObjectKeys(body);
339
+ const canonical = `${method}:${endpoint}:${JSON.stringify(sorted)}`;
340
+ const wallet = new Wallet(this.privateKey);
341
+ return wallet.signMessage(canonical);
342
+ }
343
+ /** Recursively sort object keys for canonical JSON representation. */
344
+ sortObjectKeys(obj) {
345
+ if (obj === null || typeof obj !== "object") {
346
+ return obj;
347
+ }
348
+ if (Array.isArray(obj)) {
349
+ return obj.map((item) => this.sortObjectKeys(item));
350
+ }
351
+ const sorted = {};
352
+ for (const key of Object.keys(obj).sort()) {
353
+ sorted[key] = this.sortObjectKeys(obj[key]);
354
+ }
355
+ return sorted;
356
+ }
357
+ };
358
+ // Annotate the CommonJS export names for ESM import in node:
359
+ 0 && (module.exports = {
360
+ AstraSync,
361
+ AstraSyncError,
362
+ AuthenticationError,
363
+ KYDRequiredError,
364
+ RegistrationDeniedError,
365
+ RegistrationExpiredError,
366
+ RegistrationTimeoutError
367
+ });
368
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/registration/index.ts","../../src/registration/errors.ts","../../src/registration/api.ts"],"sourcesContent":["export { AstraSync } from './api';\nexport {\n AstraSyncError,\n KYDRequiredError,\n AuthenticationError,\n RegistrationDeniedError,\n RegistrationExpiredError,\n RegistrationTimeoutError,\n} from './errors';\nexport type {\n AstraSyncConfig,\n RegisterOptions,\n RegisterResult,\n WaitForApprovalOptions,\n PendingRegistrationResponse,\n PollRegistrationResult,\n RegistrationResponse,\n AgentRecord,\n VerifyResponse,\n HealthResponse,\n PDLSSConfig,\n PDLSSPurpose,\n PDLSSDuration,\n PDLSSLimits,\n PDLSSScope,\n PDLSSSelfInstantiation,\n ModelConfig,\n FrameworkConfig,\n AgentProtocol,\n} from './types';\n","import type { ApiErrorResponse } from './types';\n\n/** Base error class for AstraSync SDK errors. */\nexport class AstraSyncError extends Error {\n public readonly code?: string;\n public readonly statusCode: number;\n\n constructor(message: string, statusCode: number, code?: string) {\n super(message);\n this.name = 'AstraSyncError';\n this.statusCode = statusCode;\n this.code = code;\n }\n}\n\n/** Thrown when KYD verification is required before agent registration. */\nexport class KYDRequiredError extends AstraSyncError {\n public readonly kydUrl: string;\n public readonly ownerNotified: boolean;\n\n constructor(response: ApiErrorResponse) {\n const kydUrl = response.kydUrl || 'https://astrasync.ai/developer-profile';\n super(\n `KYD verification required before registering agents.\\nComplete your KYD profile at: ${kydUrl}`,\n 403,\n 'KYD_REQUIRED'\n );\n this.name = 'KYDRequiredError';\n this.kydUrl = kydUrl;\n this.ownerNotified = response.ownerNotified || false;\n }\n}\n\n/** Thrown when authentication fails. */\nexport class AuthenticationError extends AstraSyncError {\n constructor(message: string) {\n super(message, 401, 'AUTH_FAILED');\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Thrown by `register({ waitForApproval: true })` when the owner denies the\n * pending registration request. The `reason` field, when present, mirrors the\n * deny note the owner left in the dashboard.\n */\nexport class RegistrationDeniedError extends AstraSyncError {\n public readonly requestId: string;\n public readonly reason?: string;\n\n constructor(requestId: string, reason?: string) {\n super(\n `Registration request ${requestId} was denied by the account owner.${reason ? ` Reason: ${reason}` : ''}`,\n 403,\n 'REGISTRATION_DENIED'\n );\n this.name = 'RegistrationDeniedError';\n this.requestId = requestId;\n this.reason = reason;\n }\n}\n\n/**\n * Thrown by `register({ waitForApproval: true })` when the pending request\n * passes its 14-day TTL with no owner decision. The agent must re-submit.\n */\nexport class RegistrationExpiredError extends AstraSyncError {\n public readonly requestId: string;\n\n constructor(requestId: string) {\n super(\n `Registration request ${requestId} expired before the owner approved it. Submit a new registration request.`,\n 410,\n 'REGISTRATION_EXPIRED'\n );\n this.name = 'RegistrationExpiredError';\n this.requestId = requestId;\n }\n}\n\n/**\n * Thrown by `register({ waitForApproval: true })` when the caller's local\n * `timeoutMs` elapses before the owner makes a decision. The request is still\n * live server-side — poll `pollRegistration(requestId)` to resume waiting, or\n * call `waitForApproval` again with a longer timeout.\n */\nexport class RegistrationTimeoutError extends AstraSyncError {\n public readonly requestId: string;\n\n constructor(requestId: string) {\n super(\n `Timed out waiting for owner approval of registration request ${requestId}. The request is still active server-side; poll the request to resume waiting.`,\n 408,\n 'REGISTRATION_TIMEOUT'\n );\n this.name = 'RegistrationTimeoutError';\n this.requestId = requestId;\n }\n}\n","import type {\n AstraSyncConfig,\n RegisterOptions,\n RegisterResult,\n WaitForApprovalOptions,\n PendingRegistrationResponse,\n PollRegistrationResult,\n RegistrationResponse,\n VerifyResponse,\n HealthResponse,\n ApiErrorResponse,\n AgentRecord,\n} from './types';\nimport {\n AstraSyncError,\n KYDRequiredError,\n AuthenticationError,\n RegistrationDeniedError,\n RegistrationExpiredError,\n RegistrationTimeoutError,\n} from './errors';\n\nconst DEFAULT_BASE_URL = 'https://astrasync.ai';\n\nconst sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));\n\n/**\n * AstraSync SDK client for registering and managing AI agents.\n *\n * @example\n * ```typescript\n * const client = new AstraSync({ apiKey: 'kya_your_api_key' });\n * const result = await client.register({\n * name: 'My Agent',\n * model: { modelName: 'gpt-4o', modelProvider: 'openai', modelType: 'llm' },\n * });\n * ```\n *\n * For staging, pass `baseUrl: 'https://staging.astrasync.ai'`.\n */\nexport class AstraSync {\n private readonly baseUrl: string;\n private readonly apiKey?: string;\n private readonly email?: string;\n private readonly password?: string;\n private readonly privateKey?: string;\n private cachedJwt?: string;\n private jwtExpiresAt?: number;\n\n constructor(config: AstraSyncConfig = {}) {\n this.baseUrl = (config.baseUrl || process.env.ASTRASYNC_API_URL || DEFAULT_BASE_URL).replace(\n /\\/+$/,\n ''\n );\n\n this.apiKey = config.apiKey || process.env.ASTRASYNC_API_KEY;\n this.email = config.email;\n this.password = config.password;\n this.privateKey = config.privateKey;\n\n if (!this.apiKey && !this.email) {\n throw new AuthenticationError(\n 'Authentication required. Provide apiKey, or email+password. ' +\n 'Set ASTRASYNC_API_KEY env var or pass config to constructor.'\n );\n }\n\n if (this.email && !this.password) {\n throw new AuthenticationError('Password is required when using email authentication.');\n }\n }\n\n /**\n * Register a new AI agent on the AstraSync KYA Platform.\n *\n * The backend response depends on auth context:\n * - **Crypto-keypair signed** (`privateKey` configured): synchronous 201,\n * returns `{ status: 'active', agent }`.\n * - **API-key only** (no signature): 202 pending, returns\n * `{ status: 'pending_approval', requestId, pollUrl, expiresAt }`. The\n * owner is notified by email and a dashboard alert is emitted; the agent\n * becomes active only after the owner approves.\n *\n * Blocking mode: pass `{ waitForApproval: true }` to have the SDK poll the\n * request until it resolves, then return the live agent record. The promise\n * rejects with `RegistrationDeniedError`, `RegistrationExpiredError`, or\n * `RegistrationTimeoutError` on the corresponding terminal states.\n *\n * @example Non-blocking (default — best for serverless / scheduled agents):\n * ```typescript\n * const result = await sdk.register({ name, pdlss });\n * if (result.status === 'pending_approval') {\n * storeRequestId(result.requestId);\n * return; // function exits; resume later via pollRegistration()\n * }\n * ```\n *\n * @example Blocking (best for long-running services + CLI):\n * ```typescript\n * const agent = await sdk.register({\n * name, pdlss, waitForApproval: true, timeoutMs: 600_000,\n * onPending: ({ ageMs }) => console.log(`waiting ${ageMs}ms`),\n * });\n * ```\n */\n async register(\n options: RegisterOptions & WaitForApprovalOptions\n ): Promise<RegisterResult | AgentRecord> {\n const body: Record<string, unknown> = {\n name: options.name,\n ...(options.description && { description: options.description }),\n ...(options.agentType && { agentType: options.agentType }),\n ...(options.apiEndpoint && { apiEndpoint: options.apiEndpoint }),\n ...(options.model && { model: options.model }),\n ...(options.framework && { framework: options.framework }),\n ...(options.protocols && { protocols: options.protocols }),\n ...(options.metadata && { metadata: options.metadata }),\n ...(options.pdlss && { pdlss: options.pdlss }),\n };\n\n const { status, body: raw } = await this.requestWithStatus<\n RegistrationResponse | PendingRegistrationResponse\n >('POST', '/api/agents/register', body);\n\n if (status === 201) {\n const active: RegisterResult = {\n status: 'active',\n agent: (raw as RegistrationResponse).data.agent,\n };\n return active;\n }\n\n // 202 Accepted — owner approval required.\n const pendingBody = raw as PendingRegistrationResponse;\n const pending: RegisterResult = {\n status: 'pending_approval',\n requestId: pendingBody.requestId,\n expiresAt: pendingBody.expiresAt,\n pollUrl: pendingBody.pollUrl,\n message: pendingBody.message,\n };\n\n if (!options.waitForApproval) return pending;\n\n return this.waitForApproval(pendingBody.requestId, options);\n }\n\n /**\n * Poll the current state of a pending-approval registration request.\n *\n * Useful for caller-driven polling when `waitForApproval: false` (the\n * default). The endpoint is unauthenticated — pass the `requestId` that\n * was returned from the 202 response.\n *\n * @returns `state: 'pending'` while awaiting; `'approved'` carries the\n * minted agent in `agent`; `'denied'` may carry the owner's\n * `reason`; `'expired'` is terminal after 14 days.\n */\n async pollRegistration(requestId: string): Promise<PollRegistrationResult> {\n const url = `${this.baseUrl}/api/agents/request-registration/${requestId}`;\n const res = await fetch(url, { headers: { Accept: 'application/json' } });\n if (!res.ok) {\n const errBody = (await res.json().catch(() => ({}))) as ApiErrorResponse;\n throw new AstraSyncError(\n errBody.error || `pollRegistration failed: ${res.status}`,\n res.status,\n errBody.code\n );\n }\n return (await res.json()) as PollRegistrationResult;\n }\n\n /**\n * Block until a pending registration request resolves to a terminal state.\n * Resolves to the live `AgentRecord` on approval; rejects with the matching\n * Registration*Error on deny/expire/timeout. Usually called via\n * `register({ waitForApproval: true })`, but exposed for callers that want\n * to fire-and-forget the initial register call and resume waiting later\n * (e.g. after restoring a stored `requestId` on cold start).\n */\n async waitForApproval(\n requestId: string,\n options: WaitForApprovalOptions = {}\n ): Promise<AgentRecord> {\n const timeoutMs = options.timeoutMs ?? 10 * 60 * 1000;\n const pollIntervalMs = options.pollIntervalMs ?? 5_000;\n const start = Date.now();\n const deadline = start + timeoutMs;\n\n while (Date.now() < deadline) {\n const result = await this.pollRegistration(requestId);\n const ageMs = Date.now() - start;\n options.onPending?.({ requestId, ageMs });\n\n if (result.state === 'approved') {\n if (!result.agent) {\n throw new AstraSyncError(\n `Registration ${requestId} reported approved but no agent payload returned.`,\n 500\n );\n }\n return result.agent;\n }\n if (result.state === 'denied') {\n throw new RegistrationDeniedError(requestId, result.reason);\n }\n if (result.state === 'expired') {\n throw new RegistrationExpiredError(requestId);\n }\n await sleep(pollIntervalMs);\n }\n throw new RegistrationTimeoutError(requestId);\n }\n\n /**\n * Look up an agent's public profile by ASTRA ID or UUID.\n */\n async verify(agentId: string): Promise<VerifyResponse> {\n return this.request<VerifyResponse>('GET', `/api/agents/verify/${agentId}`);\n }\n\n /**\n * Check API health.\n */\n async health(): Promise<HealthResponse> {\n const res = await fetch(`${this.baseUrl}/api/health/`);\n if (!res.ok) {\n throw new AstraSyncError(`Health check failed: ${res.status}`, res.status);\n }\n return res.json() as Promise<HealthResponse>;\n }\n\n // ── Private helpers ──────────────────────────────────────────────\n\n private async request<T>(method: string, endpoint: string, body?: unknown): Promise<T> {\n const { body: parsed } = await this.requestWithStatus<T>(method, endpoint, body);\n return parsed;\n }\n\n /**\n * Variant of {@link request} that also returns the HTTP status code, so\n * callers can branch on 201 vs 202 (or other success codes) without losing\n * type information about the response body.\n */\n private async requestWithStatus<T>(\n method: string,\n endpoint: string,\n body?: unknown\n ): Promise<{ status: number; body: T }> {\n const url = `${this.baseUrl}${endpoint}`;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n // Set auth header\n const token = await this.getAuthToken();\n headers['Authorization'] = `Bearer ${token}`;\n\n // Sign request if private key is configured\n if (this.privateKey) {\n const signature = await this.signRequest(method, endpoint, body || {});\n headers['X-AstraSync-Signature'] = signature;\n }\n\n const res = await fetch(url, {\n method,\n headers,\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n\n if (!res.ok) {\n const errorBody = (await res\n .json()\n .catch(() => ({ error: res.statusText }))) as ApiErrorResponse;\n\n // Handle KYD_REQUIRED specifically\n if (res.status === 403 && errorBody.code === 'KYD_REQUIRED') {\n throw new KYDRequiredError(errorBody);\n }\n\n throw new AstraSyncError(\n errorBody.error || `Request failed: ${res.status}`,\n res.status,\n errorBody.code\n );\n }\n\n return { status: res.status, body: (await res.json()) as T };\n }\n\n private async getAuthToken(): Promise<string> {\n // API key auth — use directly\n if (this.apiKey) {\n return this.apiKey;\n }\n\n // Email+password — login and cache JWT\n if (this.cachedJwt && this.jwtExpiresAt && Date.now() < this.jwtExpiresAt) {\n return this.cachedJwt;\n }\n\n const res = await fetch(`${this.baseUrl}/api/auth/login`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email: this.email, password: this.password }),\n });\n\n if (!res.ok) {\n const errorBody = (await res.json().catch(() => ({}))) as Record<string, unknown>;\n throw new AuthenticationError(\n (errorBody.message as string) || (errorBody.error as string) || 'Login failed'\n );\n }\n\n const data = (await res.json()) as { data: { token: string } };\n this.cachedJwt = data.data.token;\n // Cache for 6 days (tokens expire in 7)\n this.jwtExpiresAt = Date.now() + 6 * 24 * 60 * 60 * 1000;\n\n return this.cachedJwt;\n }\n\n /**\n * Sign a request using secp256k1 (ethers.js).\n * Canonical message format: METHOD:ENDPOINT:SORTED_JSON_BODY\n * Must match apps/backend/src/services/signature-verify.service.ts exactly.\n */\n private async signRequest(method: string, endpoint: string, body: unknown): Promise<string> {\n const { Wallet } = await import('ethers');\n const sorted = this.sortObjectKeys(body);\n const canonical = `${method}:${endpoint}:${JSON.stringify(sorted)}`;\n const wallet = new Wallet(this.privateKey!);\n return wallet.signMessage(canonical);\n }\n\n /** Recursively sort object keys for canonical JSON representation. */\n private sortObjectKeys(obj: unknown): unknown {\n if (obj === null || typeof obj !== 'object') {\n return obj;\n }\n if (Array.isArray(obj)) {\n return obj.map((item) => this.sortObjectKeys(item));\n }\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(obj).sort()) {\n sorted[key] = this.sortObjectKeys((obj as Record<string, unknown>)[key]);\n }\n return sorted;\n }\n}\n"],"mappings":"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","names":[]}