@pingagent/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,501 @@
1
+ import { ApiResponse, InboxBoxType, TaskState } from '@pingagent/schemas';
2
+ import { Identity } from '@pingagent/protocol';
3
+ import Database from 'better-sqlite3';
4
+ import { AgentCard } from '@pingagent/a2a';
5
+
6
+ declare class LocalStore {
7
+ private db;
8
+ constructor(dbPath?: string);
9
+ getDb(): Database.Database;
10
+ close(): void;
11
+ }
12
+
13
+ interface Contact {
14
+ did: string;
15
+ alias?: string;
16
+ display_name?: string;
17
+ notes?: string;
18
+ conversation_id?: string;
19
+ trusted: boolean;
20
+ added_at: number;
21
+ last_message_at?: number;
22
+ tags?: string[];
23
+ }
24
+ declare class ContactManager {
25
+ private store;
26
+ constructor(store: LocalStore);
27
+ add(contact: Omit<Contact, 'added_at'> & {
28
+ added_at?: number;
29
+ }): Contact;
30
+ remove(did: string): boolean;
31
+ get(did: string): Contact | null;
32
+ update(did: string, updates: Partial<Omit<Contact, 'did'>>): Contact | null;
33
+ list(opts?: {
34
+ tag?: string;
35
+ trusted?: boolean;
36
+ limit?: number;
37
+ offset?: number;
38
+ }): Contact[];
39
+ search(query: string): Contact[];
40
+ export(format?: 'json' | 'csv'): string;
41
+ import(data: string, format?: 'json' | 'csv'): {
42
+ imported: number;
43
+ skipped: number;
44
+ };
45
+ }
46
+
47
+ interface StoredMessage {
48
+ conversation_id: string;
49
+ message_id: string;
50
+ seq?: number;
51
+ sender_did: string;
52
+ schema: string;
53
+ payload: any;
54
+ ts_ms: number;
55
+ direction: 'sent' | 'received';
56
+ }
57
+ interface ConversationSummary {
58
+ conversation_id: string;
59
+ message_count: number;
60
+ last_message_at: number;
61
+ }
62
+ declare class HistoryManager {
63
+ private store;
64
+ constructor(store: LocalStore);
65
+ save(messages: StoredMessage[]): number;
66
+ list(conversationId: string, opts?: {
67
+ limit?: number;
68
+ beforeSeq?: number;
69
+ afterSeq?: number;
70
+ }): StoredMessage[];
71
+ /** Returns the N most recent messages (chronological order, oldest to newest of those N). */
72
+ listRecent(conversationId: string, limit: number): StoredMessage[];
73
+ search(query: string, opts?: {
74
+ conversationId?: string;
75
+ limit?: number;
76
+ }): StoredMessage[];
77
+ delete(conversationId: string): number;
78
+ listConversations(): ConversationSummary[];
79
+ getLastSyncedSeq(conversationId: string): number;
80
+ setLastSyncedSeq(conversationId: string, seq: number): void;
81
+ export(opts?: {
82
+ conversationId?: string;
83
+ format?: 'json' | 'csv';
84
+ }): string;
85
+ syncFromServer(client: PingAgentClient, conversationId: string, opts?: {
86
+ full?: boolean;
87
+ }): Promise<{
88
+ synced: number;
89
+ }>;
90
+ }
91
+
92
+ interface ClientOptions {
93
+ serverUrl: string;
94
+ identity: Identity;
95
+ accessToken: string;
96
+ mode?: 'long-running' | 'one-shot';
97
+ onTokenRefreshed?: (token: string, expiresAt: number) => void;
98
+ store?: LocalStore;
99
+ }
100
+ interface SendResponse {
101
+ message_id: string;
102
+ seq: number;
103
+ receipt: {
104
+ status: string;
105
+ reason?: string;
106
+ server_ts_ms: number;
107
+ };
108
+ }
109
+ interface FetchResponse {
110
+ messages: any[];
111
+ next_since_seq: number;
112
+ has_more: boolean;
113
+ }
114
+ interface TaskResult {
115
+ status: 'ok' | 'error';
116
+ task_id: string;
117
+ result?: {
118
+ summary?: string;
119
+ artifacts?: any[];
120
+ structured_data?: any;
121
+ };
122
+ error?: {
123
+ code: string;
124
+ message: string;
125
+ };
126
+ elapsed_ms: number;
127
+ }
128
+ interface SubscriptionUsage {
129
+ relay_today: number;
130
+ relay_limit: number;
131
+ artifact_bytes: number;
132
+ artifact_limit_bytes: number;
133
+ alias_count: number;
134
+ alias_limit: number;
135
+ }
136
+ interface SubscriptionResponse {
137
+ tier: string;
138
+ limits: {
139
+ relay_per_day: number;
140
+ max_group_size: number;
141
+ artifact_storage_mb: number;
142
+ audit_export_allowed: boolean;
143
+ alias_limit: number;
144
+ };
145
+ usage: SubscriptionUsage;
146
+ has_stripe_customer: boolean;
147
+ }
148
+ interface ConversationEntry {
149
+ conversation_id: string;
150
+ type: string;
151
+ target_did: string;
152
+ trusted: boolean;
153
+ created_at: number;
154
+ }
155
+ interface ConversationListResponse {
156
+ conversations: ConversationEntry[];
157
+ }
158
+ interface AgentProfile {
159
+ did: string;
160
+ alias?: string;
161
+ display_name?: string;
162
+ bio?: string;
163
+ capabilities?: string[];
164
+ tags?: string[];
165
+ verification_status?: string;
166
+ discoverable?: boolean;
167
+ }
168
+ interface DirectoryBrowseResponse {
169
+ agents: AgentProfile[];
170
+ total?: number;
171
+ }
172
+ interface FeedPost {
173
+ post_id: string;
174
+ text: string;
175
+ artifact_ref?: string | null;
176
+ ts_ms: number;
177
+ }
178
+ interface FeedPublicResponse {
179
+ posts: Array<FeedPost & {
180
+ did: string;
181
+ }>;
182
+ next_since?: number;
183
+ }
184
+ interface FeedByDidResponse {
185
+ did: string;
186
+ posts: FeedPost[];
187
+ }
188
+ declare class PingAgentClient {
189
+ private opts;
190
+ private transport;
191
+ private identity;
192
+ private contactManager?;
193
+ private historyManager?;
194
+ constructor(opts: ClientOptions);
195
+ getContactManager(): ContactManager | undefined;
196
+ getHistoryManager(): HistoryManager | undefined;
197
+ /** Update the in-memory access token (e.g. after proactive refresh from disk). */
198
+ setAccessToken(token: string): void;
199
+ register(developerToken?: string): Promise<ApiResponse>;
200
+ openConversation(targetDid: string): Promise<ApiResponse<{
201
+ conversation_id: string;
202
+ type: string;
203
+ trusted: boolean;
204
+ relay_ws_url: string;
205
+ }>>;
206
+ sendMessage(conversationId: string, schema: string, payload: Record<string, unknown>): Promise<ApiResponse<SendResponse>>;
207
+ sendTask(conversationId: string, task: {
208
+ task_id: string;
209
+ title: string;
210
+ description?: string;
211
+ input?: any;
212
+ timeout_ms?: number;
213
+ }): Promise<ApiResponse<SendResponse>>;
214
+ sendContactRequest(conversationId: string, message?: string): Promise<ApiResponse<SendResponse>>;
215
+ fetchInbox(conversationId: string, opts?: {
216
+ sinceSeq?: number;
217
+ limit?: number;
218
+ box?: InboxBoxType;
219
+ }): Promise<ApiResponse<FetchResponse>>;
220
+ ack(conversationId: string, forMessageId: string, status: string, opts?: {
221
+ forTaskId?: string;
222
+ reason?: string;
223
+ detail?: Record<string, unknown>;
224
+ }): Promise<ApiResponse>;
225
+ approveContact(conversationId: string): Promise<ApiResponse<{
226
+ trusted: boolean;
227
+ dm_conversation_id?: string;
228
+ }>>;
229
+ revokeConversation(conversationId: string): Promise<ApiResponse>;
230
+ cancelTask(conversationId: string, taskId: string): Promise<ApiResponse<{
231
+ task_state: TaskState;
232
+ }>>;
233
+ blockSender(conversationId: string, senderDid: string): Promise<ApiResponse>;
234
+ acquireLease(conversationId: string): Promise<ApiResponse>;
235
+ renewLease(conversationId: string): Promise<ApiResponse>;
236
+ releaseLease(conversationId: string): Promise<ApiResponse>;
237
+ uploadArtifact(content: Buffer): Promise<{
238
+ artifact_ref: string;
239
+ sha256: string;
240
+ size: number;
241
+ }>;
242
+ downloadArtifact(artifactRef: string, expectedSha256: string, expectedSize: number): Promise<Buffer>;
243
+ getSubscription(): Promise<ApiResponse<SubscriptionResponse>>;
244
+ resolveAlias(alias: string): Promise<ApiResponse<{
245
+ did: string;
246
+ alias: string;
247
+ }>>;
248
+ registerAlias(alias: string): Promise<ApiResponse>;
249
+ listConversations(opts?: {
250
+ type?: string;
251
+ }): Promise<ApiResponse<ConversationListResponse>>;
252
+ getProfile(): Promise<ApiResponse<AgentProfile>>;
253
+ updateProfile(profile: {
254
+ display_name?: string;
255
+ bio?: string;
256
+ capabilities?: string[];
257
+ tags?: string[];
258
+ discoverable?: boolean;
259
+ }): Promise<ApiResponse<AgentProfile>>;
260
+ enableDiscovery(): Promise<ApiResponse<{
261
+ discoverable: boolean;
262
+ }>>;
263
+ disableDiscovery(): Promise<ApiResponse<{
264
+ discoverable: boolean;
265
+ }>>;
266
+ browseDirectory(opts?: {
267
+ tag?: string;
268
+ query?: string;
269
+ limit?: number;
270
+ offset?: number;
271
+ sort?: 'default' | 'updated';
272
+ }): Promise<ApiResponse<DirectoryBrowseResponse>>;
273
+ publishPost(opts: {
274
+ text: string;
275
+ artifact_ref?: string;
276
+ }): Promise<ApiResponse<{
277
+ post_id: string;
278
+ ts_ms: number;
279
+ }>>;
280
+ listFeedPublic(opts?: {
281
+ limit?: number;
282
+ since?: number;
283
+ }): Promise<ApiResponse<FeedPublicResponse>>;
284
+ listFeedByDid(did: string, opts?: {
285
+ limit?: number;
286
+ }): Promise<ApiResponse<FeedByDidResponse>>;
287
+ createChannel(opts: {
288
+ name: string;
289
+ alias?: string;
290
+ description?: string;
291
+ discoverable?: boolean;
292
+ }): Promise<ApiResponse<{
293
+ conversation_id: string;
294
+ alias: string;
295
+ name: string;
296
+ discoverable: boolean;
297
+ relay_ws_url: string;
298
+ }>>;
299
+ updateChannel(opts: {
300
+ alias: string;
301
+ name?: string;
302
+ description?: string;
303
+ discoverable?: boolean;
304
+ }): Promise<ApiResponse<{
305
+ alias: string;
306
+ name: string;
307
+ description?: string | null;
308
+ discoverable: boolean;
309
+ }>>;
310
+ deleteChannel(alias: string): Promise<ApiResponse<{
311
+ deleted: boolean;
312
+ alias: string;
313
+ }>>;
314
+ discoverChannels(opts?: {
315
+ limit?: number;
316
+ query?: string;
317
+ }): Promise<ApiResponse<{
318
+ channels: Array<{
319
+ alias: string;
320
+ name: string;
321
+ description?: string | null;
322
+ owner_did: string;
323
+ }>;
324
+ }>>;
325
+ joinChannel(alias: string): Promise<ApiResponse<{
326
+ conversation_id: string;
327
+ alias: string;
328
+ owner_did: string;
329
+ relay_ws_url: string;
330
+ }>>;
331
+ getDid(): string;
332
+ createBillingLinkCode(): Promise<ApiResponse<{
333
+ code: string;
334
+ expires_in_seconds: number;
335
+ }>>;
336
+ redeemBillingLink(code: string): Promise<ApiResponse<{
337
+ primary_did: string;
338
+ }>>;
339
+ unlinkBillingDevice(did: string): Promise<ApiResponse>;
340
+ getLinkedDevices(): Promise<ApiResponse<{
341
+ primary_did: string;
342
+ linked_dids: string[];
343
+ is_primary: boolean;
344
+ }>>;
345
+ sendTaskAndWait(targetDid: string, task: {
346
+ title: string;
347
+ description?: string;
348
+ input?: any;
349
+ }, opts?: {
350
+ timeoutMs?: number;
351
+ pollIntervalMs?: number;
352
+ }): Promise<TaskResult>;
353
+ }
354
+
355
+ declare function generateIdentity(): Identity;
356
+ declare function identityExists(identityPath?: string): boolean;
357
+ declare function loadIdentity(identityPath?: string): Identity & {
358
+ serverUrl?: string;
359
+ accessToken?: string;
360
+ tokenExpiresAt?: number;
361
+ mode?: string;
362
+ };
363
+ declare function saveIdentity(identity: Identity, opts?: {
364
+ serverUrl?: string;
365
+ accessToken?: string;
366
+ tokenExpiresAt?: number;
367
+ mode?: string;
368
+ alias?: string;
369
+ }, identityPath?: string): void;
370
+ declare function updateStoredToken(accessToken: string, expiresAt: number, identityPath?: string): void;
371
+
372
+ /**
373
+ * Proactive token refresh: call before creating the client when token is near or past expiry.
374
+ * Server allows refresh only within a short grace period (default 5 min) after expiry.
375
+ */
376
+ /**
377
+ * If the stored token is expired or expires within the refresh grace window,
378
+ * call the server's /v1/auth/refresh and persist the new token.
379
+ * Call this after loadIdentity() and before creating PingAgentClient.
380
+ * @returns true if a refresh was performed and the identity file was updated, false otherwise
381
+ */
382
+ declare function ensureTokenValid(identityPath?: string, serverUrl?: string): Promise<boolean>;
383
+
384
+ declare function getRootDir(): string;
385
+ declare function getProfile(): string | undefined;
386
+ declare function getIdentityPath(explicitPath?: string): string;
387
+ declare function getStorePath(explicitPath?: string): string;
388
+
389
+ interface TransportOptions {
390
+ serverUrl: string;
391
+ accessToken: string;
392
+ maxRetries?: number;
393
+ onTokenRefreshed?: (newToken: string, expiresAt: number) => void;
394
+ }
395
+ declare class HttpTransport {
396
+ private serverUrl;
397
+ private accessToken;
398
+ private maxRetries;
399
+ private onTokenRefreshed?;
400
+ constructor(opts: TransportOptions);
401
+ setToken(token: string): void;
402
+ /** Fetch a URL with Bearer auth (e.g. artifact upload/download). */
403
+ fetchWithAuth(url: string, options?: {
404
+ method?: string;
405
+ body?: ArrayBuffer | Buffer;
406
+ contentType?: string;
407
+ }): Promise<ArrayBuffer>;
408
+ request<T = any>(method: string, path: string, body?: any, skipAuth?: boolean): Promise<ApiResponse<T>>;
409
+ private refreshToken;
410
+ }
411
+
412
+ /**
413
+ * A2A adapter for PingAgentClient.
414
+ * Allows PingAgent to call external agents that speak the A2A protocol,
415
+ * translating PingAgent's task model to A2A's message/send + tasks/get.
416
+ */
417
+
418
+ interface A2AAdapterOptions {
419
+ agentUrl: string;
420
+ authToken?: string;
421
+ timeoutMs?: number;
422
+ }
423
+ /**
424
+ * Wraps an A2AClient and exposes PingAgent-style operations that delegate
425
+ * to an external A2A agent.
426
+ */
427
+ declare class A2AAdapter {
428
+ private client;
429
+ private cachedCard;
430
+ constructor(opts: A2AAdapterOptions);
431
+ getAgentCard(): Promise<AgentCard>;
432
+ /**
433
+ * Send a task to the external A2A agent and optionally wait for completion.
434
+ */
435
+ sendTask(opts: {
436
+ title: string;
437
+ description?: string;
438
+ input?: unknown;
439
+ wait?: boolean;
440
+ timeoutMs?: number;
441
+ }): Promise<A2ATaskResult>;
442
+ getTaskStatus(taskId: string): Promise<A2ATaskResult>;
443
+ cancelTask(taskId: string): Promise<A2ATaskResult>;
444
+ sendText(text: string, opts?: {
445
+ blocking?: boolean;
446
+ }): Promise<A2ATaskResult>;
447
+ private convertTask;
448
+ private extractText;
449
+ }
450
+ interface A2ATaskResult {
451
+ taskId: string;
452
+ contextId?: string;
453
+ state: string;
454
+ summary?: string;
455
+ output?: unknown;
456
+ timestamp?: string;
457
+ }
458
+
459
+ /**
460
+ * WebSocket subscription for real-time inbox messages.
461
+ * Connects to Gateway /v1/ws per conversation and receives ws_message pushes.
462
+ */
463
+
464
+ interface WsControlPayload {
465
+ action: string;
466
+ target?: string;
467
+ }
468
+ interface WsSubscriptionOptions {
469
+ serverUrl: string;
470
+ getAccessToken: () => string | Promise<string>;
471
+ myDid: string;
472
+ listConversations: () => Promise<ConversationEntry[]>;
473
+ onMessage: (envelope: any, conversationId: string) => void;
474
+ onControl?: (control: WsControlPayload, conversationId: string) => void;
475
+ onError?: (err: Error) => void;
476
+ }
477
+ declare class WsSubscription {
478
+ private opts;
479
+ private connections;
480
+ private reconnectTimers;
481
+ private reconnectAttempts;
482
+ private listInterval;
483
+ private stopped;
484
+ /** Conversation IDs that were explicitly stopped (e.g. revoke); do not reconnect. */
485
+ private stoppedConversations;
486
+ constructor(opts: WsSubscriptionOptions);
487
+ start(): void;
488
+ stop(): void;
489
+ /**
490
+ * Stop a single conversation's WebSocket and do not reconnect.
491
+ * Used when the conversation is revoked or the client no longer wants to subscribe.
492
+ */
493
+ stopConversation(conversationId: string): void;
494
+ private wsUrl;
495
+ private connectAsync;
496
+ private scheduleReconnect;
497
+ private connectAll;
498
+ private syncConnections;
499
+ }
500
+
501
+ export { A2AAdapter, type A2AAdapterOptions, type A2ATaskResult, type AgentProfile, type ClientOptions, type Contact, ContactManager, type ConversationEntry, type ConversationListResponse, type DirectoryBrowseResponse, type FeedByDidResponse, type FeedPost, type FeedPublicResponse, type FetchResponse, HistoryManager, HttpTransport, LocalStore, PingAgentClient, type SendResponse, type StoredMessage, type SubscriptionResponse, type SubscriptionUsage, type TaskResult, type TransportOptions, WsSubscription, type WsSubscriptionOptions, ensureTokenValid, generateIdentity, getIdentityPath, getProfile, getRootDir, getStorePath, identityExists, loadIdentity, saveIdentity, updateStoredToken };
package/dist/index.js ADDED
@@ -0,0 +1,38 @@
1
+ import {
2
+ A2AAdapter,
3
+ ContactManager,
4
+ HistoryManager,
5
+ HttpTransport,
6
+ LocalStore,
7
+ PingAgentClient,
8
+ WsSubscription,
9
+ ensureTokenValid,
10
+ generateIdentity,
11
+ getIdentityPath,
12
+ getProfile,
13
+ getRootDir,
14
+ getStorePath,
15
+ identityExists,
16
+ loadIdentity,
17
+ saveIdentity,
18
+ updateStoredToken
19
+ } from "./chunk-4SRPVWK4.js";
20
+ export {
21
+ A2AAdapter,
22
+ ContactManager,
23
+ HistoryManager,
24
+ HttpTransport,
25
+ LocalStore,
26
+ PingAgentClient,
27
+ WsSubscription,
28
+ ensureTokenValid,
29
+ generateIdentity,
30
+ getIdentityPath,
31
+ getProfile,
32
+ getRootDir,
33
+ getStorePath,
34
+ identityExists,
35
+ loadIdentity,
36
+ saveIdentity,
37
+ updateStoredToken
38
+ };
@@ -0,0 +1,26 @@
1
+ import * as http from 'node:http';
2
+
3
+ /**
4
+ * PingAgent Web - Local debugging and audit UI.
5
+ * Serves a simple web app for viewing contacts, conversations, messages, and sending messages.
6
+ * Auth: CLI holds identity + token; browser only talks to localhost. No token in browser.
7
+ */
8
+
9
+ interface ProfileEntry {
10
+ id: string;
11
+ did?: string;
12
+ identityPath: string;
13
+ storePath: string;
14
+ }
15
+ interface WebServerOptions {
16
+ /** Root dir to scan for profiles (e.g. ~/.pingagent). When set, user selects profile on page. */
17
+ rootDir?: string;
18
+ /** When set, use this identity only (no profile picker). Overrides rootDir. */
19
+ fixedIdentityPath?: string;
20
+ fixedStorePath?: string;
21
+ serverUrl: string;
22
+ port?: number;
23
+ }
24
+ declare function startWebServer(opts: WebServerOptions): Promise<http.Server>;
25
+
26
+ export { type ProfileEntry, type WebServerOptions, startWebServer };