@pingagent/sdk 0.1.7 → 0.1.9
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/bin/pingagent.js +24 -3
- package/dist/chunk-3OEFISNL.js +2433 -0
- package/dist/chunk-5Z6HZWDA.js +2603 -0
- package/dist/chunk-BSDY6AKB.js +2918 -0
- package/dist/chunk-PFABO4C7.js +2961 -0
- package/dist/chunk-QK2GMSWC.js +2959 -0
- package/dist/chunk-TCYDOFRQ.js +2085 -0
- package/dist/chunk-V7HHUQT6.js +1962 -0
- package/dist/index.d.ts +403 -5
- package/dist/index.js +41 -3
- package/dist/web-server.js +1151 -16
- package/package.json +11 -3
- package/__tests__/cli.test.ts +0 -225
- package/__tests__/identity.test.ts +0 -47
- package/__tests__/store.test.ts +0 -332
- package/src/a2a-adapter.ts +0 -159
- package/src/auth.ts +0 -50
- package/src/client.ts +0 -582
- package/src/contacts.ts +0 -210
- package/src/history.ts +0 -269
- package/src/identity.ts +0 -86
- package/src/index.ts +0 -25
- package/src/paths.ts +0 -52
- package/src/store.ts +0 -62
- package/src/transport.ts +0 -141
- package/src/web-server.ts +0 -1148
- package/src/ws-subscription.ts +0 -428
- package/tsconfig.json +0 -8
package/src/a2a-adapter.ts
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A2A adapter for PingAgentClient.
|
|
3
|
-
* Allows PingAgent to call external agents that speak the A2A protocol,
|
|
4
|
-
* translating PingAgent's task model to A2A's message/send + tasks/get.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
A2AClient,
|
|
9
|
-
A2AClientError,
|
|
10
|
-
type A2AClientOptions,
|
|
11
|
-
type AgentCard,
|
|
12
|
-
type Task as A2ATask,
|
|
13
|
-
type SendMessageResult,
|
|
14
|
-
type TextPart,
|
|
15
|
-
type DataPart,
|
|
16
|
-
type Part,
|
|
17
|
-
type Message as A2AMessage,
|
|
18
|
-
} from '@pingagent/a2a';
|
|
19
|
-
|
|
20
|
-
export interface A2AAdapterOptions {
|
|
21
|
-
agentUrl: string;
|
|
22
|
-
authToken?: string;
|
|
23
|
-
timeoutMs?: number;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Wraps an A2AClient and exposes PingAgent-style operations that delegate
|
|
28
|
-
* to an external A2A agent.
|
|
29
|
-
*/
|
|
30
|
-
export class A2AAdapter {
|
|
31
|
-
private client: A2AClient;
|
|
32
|
-
private cachedCard: AgentCard | null = null;
|
|
33
|
-
|
|
34
|
-
constructor(opts: A2AAdapterOptions) {
|
|
35
|
-
this.client = new A2AClient({
|
|
36
|
-
agentUrl: opts.agentUrl,
|
|
37
|
-
authToken: opts.authToken ? `Bearer ${opts.authToken}` : undefined,
|
|
38
|
-
timeoutMs: opts.timeoutMs,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async getAgentCard(): Promise<AgentCard> {
|
|
43
|
-
if (!this.cachedCard) {
|
|
44
|
-
this.cachedCard = await this.client.fetchAgentCard();
|
|
45
|
-
}
|
|
46
|
-
return this.cachedCard;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Send a task to the external A2A agent and optionally wait for completion.
|
|
51
|
-
*/
|
|
52
|
-
async sendTask(opts: {
|
|
53
|
-
title: string;
|
|
54
|
-
description?: string;
|
|
55
|
-
input?: unknown;
|
|
56
|
-
wait?: boolean;
|
|
57
|
-
timeoutMs?: number;
|
|
58
|
-
}): Promise<A2ATaskResult> {
|
|
59
|
-
const parts: Part[] = [];
|
|
60
|
-
parts.push({ kind: 'text', text: opts.title } as TextPart);
|
|
61
|
-
if (opts.description) {
|
|
62
|
-
parts.push({ kind: 'text', text: opts.description } as TextPart);
|
|
63
|
-
}
|
|
64
|
-
if (opts.input) {
|
|
65
|
-
parts.push({
|
|
66
|
-
kind: 'data',
|
|
67
|
-
data: typeof opts.input === 'object'
|
|
68
|
-
? (opts.input as Record<string, unknown>)
|
|
69
|
-
: { value: opts.input },
|
|
70
|
-
} as DataPart);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const messageId = `msg_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
74
|
-
const message: A2AMessage = {
|
|
75
|
-
kind: 'message',
|
|
76
|
-
messageId,
|
|
77
|
-
role: 'user',
|
|
78
|
-
parts,
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
if (opts.wait) {
|
|
82
|
-
const task = await this.client.sendAndWait(
|
|
83
|
-
{ message, configuration: { blocking: true } },
|
|
84
|
-
{ maxPollMs: opts.timeoutMs ?? 120_000 },
|
|
85
|
-
);
|
|
86
|
-
return this.convertTask(task);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const result = await this.client.sendMessage({ message });
|
|
90
|
-
if (result.kind === 'task') {
|
|
91
|
-
return this.convertTask(result);
|
|
92
|
-
}
|
|
93
|
-
return {
|
|
94
|
-
taskId: result.messageId,
|
|
95
|
-
state: 'completed' as const,
|
|
96
|
-
summary: this.extractText(result.parts),
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async getTaskStatus(taskId: string): Promise<A2ATaskResult> {
|
|
101
|
-
const task = await this.client.getTask(taskId);
|
|
102
|
-
return this.convertTask(task);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async cancelTask(taskId: string): Promise<A2ATaskResult> {
|
|
106
|
-
const task = await this.client.cancelTask(taskId);
|
|
107
|
-
return this.convertTask(task);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async sendText(text: string, opts?: { blocking?: boolean }): Promise<A2ATaskResult> {
|
|
111
|
-
const result = await this.client.sendText(text, { blocking: opts?.blocking });
|
|
112
|
-
if (result.kind === 'task') {
|
|
113
|
-
return this.convertTask(result);
|
|
114
|
-
}
|
|
115
|
-
return {
|
|
116
|
-
taskId: result.messageId,
|
|
117
|
-
state: 'completed',
|
|
118
|
-
summary: this.extractText(result.parts),
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
private convertTask(task: A2ATask): A2ATaskResult {
|
|
123
|
-
const summary = task.artifacts
|
|
124
|
-
?.flatMap((a) => a.parts)
|
|
125
|
-
.filter((p): p is TextPart => p.kind === 'text')
|
|
126
|
-
.map((p) => p.text)
|
|
127
|
-
.join('\n');
|
|
128
|
-
|
|
129
|
-
const output = task.artifacts
|
|
130
|
-
?.flatMap((a) => a.parts)
|
|
131
|
-
.filter((p): p is DataPart => p.kind === 'data')
|
|
132
|
-
.map((p) => p.data);
|
|
133
|
-
|
|
134
|
-
return {
|
|
135
|
-
taskId: task.id,
|
|
136
|
-
contextId: task.contextId,
|
|
137
|
-
state: task.status.state,
|
|
138
|
-
summary: summary || undefined,
|
|
139
|
-
output: output && output.length > 0 ? (output.length === 1 ? output[0] : output) : undefined,
|
|
140
|
-
timestamp: task.status.timestamp,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
private extractText(parts: Part[]): string | undefined {
|
|
145
|
-
return parts
|
|
146
|
-
.filter((p): p is TextPart => p.kind === 'text')
|
|
147
|
-
.map((p) => p.text)
|
|
148
|
-
.join('\n') || undefined;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export interface A2ATaskResult {
|
|
153
|
-
taskId: string;
|
|
154
|
-
contextId?: string;
|
|
155
|
-
state: string;
|
|
156
|
-
summary?: string;
|
|
157
|
-
output?: unknown;
|
|
158
|
-
timestamp?: string;
|
|
159
|
-
}
|
package/src/auth.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Proactive token refresh: call before creating the client when token is near or past expiry.
|
|
3
|
-
* Server allows refresh only within a short grace period (default 5 min) after expiry.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { loadIdentity, updateStoredToken, identityExists } from './identity.js';
|
|
7
|
-
|
|
8
|
-
const REFRESH_GRACE_MS = 5 * 60 * 1000; // 5 minutes, must match server's verifyTokenWithGrace
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* If the stored token is expired or expires within the refresh grace window,
|
|
12
|
-
* call the server's /v1/auth/refresh and persist the new token.
|
|
13
|
-
* Call this after loadIdentity() and before creating PingAgentClient.
|
|
14
|
-
* @returns true if a refresh was performed and the identity file was updated, false otherwise
|
|
15
|
-
*/
|
|
16
|
-
export async function ensureTokenValid(
|
|
17
|
-
identityPath?: string,
|
|
18
|
-
serverUrl?: string,
|
|
19
|
-
): Promise<boolean> {
|
|
20
|
-
if (!identityExists(identityPath)) return false;
|
|
21
|
-
const id = loadIdentity(identityPath);
|
|
22
|
-
const token = id.accessToken;
|
|
23
|
-
const expiresAt = id.tokenExpiresAt;
|
|
24
|
-
if (!token || expiresAt == null) return false;
|
|
25
|
-
|
|
26
|
-
const now = Date.now();
|
|
27
|
-
if (expiresAt > now + REFRESH_GRACE_MS) return false; // still valid, no need to refresh
|
|
28
|
-
|
|
29
|
-
const baseUrl = (serverUrl ?? id.serverUrl ?? 'https://pingagent.chat').replace(/\/$/, '');
|
|
30
|
-
try {
|
|
31
|
-
const res = await fetch(`${baseUrl}/v1/auth/refresh`, {
|
|
32
|
-
method: 'POST',
|
|
33
|
-
headers: { 'Content-Type': 'application/json' },
|
|
34
|
-
body: JSON.stringify({ access_token: token }),
|
|
35
|
-
});
|
|
36
|
-
const text = await res.text();
|
|
37
|
-
let data: { ok?: boolean; data?: { access_token?: string; expires_ms?: number } };
|
|
38
|
-
try {
|
|
39
|
-
data = text ? JSON.parse(text) : {};
|
|
40
|
-
} catch {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
if (!res.ok || !data.ok || !data.data?.access_token || data.data.expires_ms == null) return false;
|
|
44
|
-
const newExpiresAt = now + data.data.expires_ms;
|
|
45
|
-
updateStoredToken(data.data.access_token, newExpiresAt, identityPath);
|
|
46
|
-
return true;
|
|
47
|
-
} catch {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
}
|