@ziggs-ai/api-client 0.1.3 → 0.1.4
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 +13 -7
- package/dist/ConnectionManager.d.ts +45 -0
- package/dist/ConnectionManager.js +118 -0
- package/dist/http/AgentSearchClient.d.ts +36 -0
- package/dist/http/AgentSearchClient.js +72 -0
- package/dist/http/AgreementClient.d.ts +153 -0
- package/dist/http/AgreementClient.js +457 -0
- package/dist/http/ArtifactsClient.d.ts +48 -0
- package/dist/http/ArtifactsClient.js +90 -0
- package/dist/http/ChatClient.d.ts +14 -0
- package/dist/http/ChatClient.js +69 -0
- package/dist/http/MarketplaceClient.d.ts +19 -0
- package/dist/http/MarketplaceClient.js +72 -0
- package/dist/http/MessagesClient.d.ts +22 -0
- package/dist/http/MessagesClient.js +41 -0
- package/dist/http/ScopeClient.d.ts +33 -0
- package/dist/http/ScopeClient.js +39 -0
- package/dist/http/TaskClient.d.ts +75 -0
- package/dist/http/TaskClient.js +343 -0
- package/dist/http/TelemetryClient.d.ts +11 -0
- package/dist/http/TelemetryClient.js +53 -0
- package/dist/http/index.d.ts +12 -0
- package/dist/http/index.js +9 -0
- package/dist/index.d.ts +9 -0
- package/{src → dist}/index.js +2 -12
- package/dist/shared/runtimeLog.d.ts +14 -0
- package/dist/shared/runtimeLog.js +64 -0
- package/dist/types.d.ts +120 -0
- package/dist/types.js +50 -0
- package/dist/utils/urlUtils.d.ts +2 -0
- package/dist/utils/urlUtils.js +8 -0
- package/dist/websocket/ControlSocket.d.ts +13 -0
- package/dist/websocket/ControlSocket.js +36 -0
- package/dist/websocket/WebSocketClient.d.ts +71 -0
- package/dist/websocket/WebSocketClient.js +217 -0
- package/dist/websocket/index.js +1 -0
- package/package.json +13 -6
- package/src/ConnectionManager.js +0 -179
- package/src/http/AgentSearchClient.js +0 -113
- package/src/http/ContextReader.js +0 -99
- package/src/http/ContextWriter.js +0 -98
- package/src/http/TaskClient.js +0 -612
- package/src/http/TelemetryClient.js +0 -43
- package/src/http/index.js +0 -6
- package/src/types.js +0 -28
- package/src/utils/urlUtils.js +0 -17
- package/src/websocket/ControlSocket.js +0 -55
- package/src/websocket/WebSocketClient.js +0 -318
- /package/{src/websocket/index.js → dist/websocket/index.d.ts} +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import { type Creds, type Agreement } from '../types.js';
|
|
3
|
+
export interface PublishOfferPayload {
|
|
4
|
+
description: string;
|
|
5
|
+
price?: number;
|
|
6
|
+
lifecycle?: string;
|
|
7
|
+
expiresAt?: string;
|
|
8
|
+
maxExecutions?: number;
|
|
9
|
+
/** `hire` = claimer becomes the provider's principal on claim. Defaults to `service` server-side. */
|
|
10
|
+
engagementKind?: 'hire' | 'service';
|
|
11
|
+
metadata?: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
export declare function publishOffer(payload: PublishOfferPayload, creds: Creds): Promise<Agreement>;
|
|
14
|
+
export interface PullOffersOptions {
|
|
15
|
+
limit?: number;
|
|
16
|
+
since?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function pullOffers(options: PullOffersOptions | undefined, creds: Creds): Promise<Agreement[]>;
|
|
19
|
+
export declare function claimOffer(agreementId: string, creds: Creds): Promise<Agreement>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
|
+
import { ApiError } from '../types.js';
|
|
4
|
+
function getMarketplaceBaseUrl() { return `${getBackendUrl()}/marketplace`; }
|
|
5
|
+
function buildHeaders(creds) {
|
|
6
|
+
return {
|
|
7
|
+
'content-type': 'application/json',
|
|
8
|
+
Authorization: `Bearer ${creds.operatorKey}`,
|
|
9
|
+
'X-Agent-Id': creds.agentId,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function assertCreds(creds, op) {
|
|
13
|
+
if (!creds?.operatorKey)
|
|
14
|
+
throw new Error(`operatorKey is required for ${op}`);
|
|
15
|
+
if (!creds?.agentId)
|
|
16
|
+
throw new Error(`agentId is required for ${op}`);
|
|
17
|
+
}
|
|
18
|
+
function throwApiError(response, body, defaultMsg) {
|
|
19
|
+
let msg = defaultMsg;
|
|
20
|
+
try {
|
|
21
|
+
const d = JSON.parse(body);
|
|
22
|
+
msg = d['details'] || d['error'] || d['message'] || defaultMsg;
|
|
23
|
+
}
|
|
24
|
+
catch { /**/ }
|
|
25
|
+
throw new ApiError(msg, response.status);
|
|
26
|
+
}
|
|
27
|
+
export async function publishOffer(payload, creds) {
|
|
28
|
+
assertCreds(creds, 'marketplace offer publish');
|
|
29
|
+
const res = await fetch(`${getMarketplaceBaseUrl()}/offers/publish`, {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
headers: buildHeaders(creds),
|
|
32
|
+
body: JSON.stringify(payload || {}),
|
|
33
|
+
});
|
|
34
|
+
if (!res.ok) {
|
|
35
|
+
const body = await res.text().catch(() => '');
|
|
36
|
+
throwApiError(res, body, `Marketplace offer publish failed: ${res.status}`);
|
|
37
|
+
}
|
|
38
|
+
const data = await res.json().catch(() => null);
|
|
39
|
+
return (data?.['offer'] ?? data);
|
|
40
|
+
}
|
|
41
|
+
export async function pullOffers(options, creds) {
|
|
42
|
+
assertCreds(creds, 'marketplace offers pull');
|
|
43
|
+
const res = await fetch(`${getMarketplaceBaseUrl()}/offers/pull`, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: buildHeaders(creds),
|
|
46
|
+
body: JSON.stringify(options || {}),
|
|
47
|
+
});
|
|
48
|
+
if (!res.ok) {
|
|
49
|
+
const body = await res.text().catch(() => '');
|
|
50
|
+
throwApiError(res, body, `Marketplace offers pull failed: ${res.status}`);
|
|
51
|
+
}
|
|
52
|
+
const data = await res.json().catch(() => null);
|
|
53
|
+
return data?.['offers'] ?? [];
|
|
54
|
+
}
|
|
55
|
+
export async function claimOffer(agreementId, creds) {
|
|
56
|
+
if (!agreementId)
|
|
57
|
+
throw new Error('agreementId is required');
|
|
58
|
+
assertCreds(creds, 'marketplace offer claim');
|
|
59
|
+
const res = await fetch(`${getMarketplaceBaseUrl()}/offers/claim`, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: buildHeaders(creds),
|
|
62
|
+
body: JSON.stringify({ agreementId }),
|
|
63
|
+
});
|
|
64
|
+
if (!res.ok) {
|
|
65
|
+
const body = await res.text().catch(() => '');
|
|
66
|
+
throwApiError(res, body, `Marketplace offer claim failed: ${res.status}`);
|
|
67
|
+
}
|
|
68
|
+
const data = await res.json().catch(() => null);
|
|
69
|
+
if (!data?.['ok'])
|
|
70
|
+
throw new Error(data?.['error'] || 'Claim failed');
|
|
71
|
+
return data['offer'];
|
|
72
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
export interface ListMessagesOptions {
|
|
3
|
+
/** ISO timestamp; only entries with `timestamp > after` are returned. */
|
|
4
|
+
after?: string;
|
|
5
|
+
/** Page size, default 100, hard cap 1000. */
|
|
6
|
+
limit?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface ListMessagesResult {
|
|
9
|
+
messages: unknown[];
|
|
10
|
+
latestSequence: string | null;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Read-side client for `GET /messages?chatId=&after=&limit=`. Replaces the
|
|
14
|
+
* `history[]` slice of the old context blob.
|
|
15
|
+
*/
|
|
16
|
+
export declare class MessagesClient {
|
|
17
|
+
private readonly operatorKey;
|
|
18
|
+
private readonly agentId;
|
|
19
|
+
constructor(operatorKey: string, agentId: string);
|
|
20
|
+
list(chatId: string, opts?: ListMessagesOptions): Promise<ListMessagesResult>;
|
|
21
|
+
private _headers;
|
|
22
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
|
+
/**
|
|
4
|
+
* Read-side client for `GET /messages?chatId=&after=&limit=`. Replaces the
|
|
5
|
+
* `history[]` slice of the old context blob.
|
|
6
|
+
*/
|
|
7
|
+
export class MessagesClient {
|
|
8
|
+
operatorKey;
|
|
9
|
+
agentId;
|
|
10
|
+
constructor(operatorKey, agentId) {
|
|
11
|
+
if (!operatorKey)
|
|
12
|
+
throw new Error('MessagesClient: operatorKey is required');
|
|
13
|
+
if (!agentId)
|
|
14
|
+
throw new Error('MessagesClient: agentId is required (operator-token impersonation)');
|
|
15
|
+
this.operatorKey = operatorKey;
|
|
16
|
+
this.agentId = agentId;
|
|
17
|
+
}
|
|
18
|
+
async list(chatId, opts = {}) {
|
|
19
|
+
if (!chatId)
|
|
20
|
+
return { messages: [], latestSequence: null };
|
|
21
|
+
const url = new URL(`${getBackendUrl()}/messages`);
|
|
22
|
+
url.searchParams.set('chatId', chatId);
|
|
23
|
+
if (opts.after)
|
|
24
|
+
url.searchParams.set('after', opts.after);
|
|
25
|
+
if (opts.limit != null)
|
|
26
|
+
url.searchParams.set('limit', String(opts.limit));
|
|
27
|
+
const res = await fetch(url.toString(), { headers: this._headers() });
|
|
28
|
+
if (!res.ok) {
|
|
29
|
+
const body = await res.text().catch(() => '');
|
|
30
|
+
throw new Error(`MessagesClient.list ${res.status} ${res.statusText} ${body.slice(0, 200)}`);
|
|
31
|
+
}
|
|
32
|
+
return (await res.json());
|
|
33
|
+
}
|
|
34
|
+
_headers() {
|
|
35
|
+
return {
|
|
36
|
+
'content-type': 'application/json',
|
|
37
|
+
Authorization: `Bearer ${this.operatorKey}`,
|
|
38
|
+
'X-Agent-Id': this.agentId,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
export type ScopeKind = 'chat' | 'agreement' | 'task' | 'counterparty';
|
|
3
|
+
export interface PartyRef {
|
|
4
|
+
id: string;
|
|
5
|
+
role: 'payer' | 'provider' | 'creator' | 'proposedTo' | 'providerPrincipal';
|
|
6
|
+
}
|
|
7
|
+
export interface ScopeResult {
|
|
8
|
+
via: {
|
|
9
|
+
kind: ScopeKind;
|
|
10
|
+
id: string;
|
|
11
|
+
};
|
|
12
|
+
agent: string;
|
|
13
|
+
accessible: {
|
|
14
|
+
chats: string[];
|
|
15
|
+
agreements: string[];
|
|
16
|
+
tasks: string[];
|
|
17
|
+
counterparties: PartyRef[];
|
|
18
|
+
};
|
|
19
|
+
permissions: string[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Client for `GET /scope?via=<kind>:<id>`. Returns the access graph reachable
|
|
23
|
+
* from an entry point for the impersonated agent. Used by `runTurn` to
|
|
24
|
+
* bootstrap "what does my current session-key resolve to" before fanning out
|
|
25
|
+
* to primitive reads.
|
|
26
|
+
*/
|
|
27
|
+
export declare class ScopeClient {
|
|
28
|
+
private readonly operatorKey;
|
|
29
|
+
private readonly agentId;
|
|
30
|
+
constructor(operatorKey: string, agentId: string);
|
|
31
|
+
get(kind: ScopeKind, id: string): Promise<ScopeResult>;
|
|
32
|
+
private _headers;
|
|
33
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
|
+
/**
|
|
4
|
+
* Client for `GET /scope?via=<kind>:<id>`. Returns the access graph reachable
|
|
5
|
+
* from an entry point for the impersonated agent. Used by `runTurn` to
|
|
6
|
+
* bootstrap "what does my current session-key resolve to" before fanning out
|
|
7
|
+
* to primitive reads.
|
|
8
|
+
*/
|
|
9
|
+
export class ScopeClient {
|
|
10
|
+
operatorKey;
|
|
11
|
+
agentId;
|
|
12
|
+
constructor(operatorKey, agentId) {
|
|
13
|
+
if (!operatorKey)
|
|
14
|
+
throw new Error('ScopeClient: operatorKey is required');
|
|
15
|
+
if (!agentId)
|
|
16
|
+
throw new Error('ScopeClient: agentId is required (operator-token impersonation)');
|
|
17
|
+
this.operatorKey = operatorKey;
|
|
18
|
+
this.agentId = agentId;
|
|
19
|
+
}
|
|
20
|
+
async get(kind, id) {
|
|
21
|
+
if (!id)
|
|
22
|
+
throw new Error('ScopeClient.get: id is required');
|
|
23
|
+
const url = new URL(`${getBackendUrl()}/scope`);
|
|
24
|
+
url.searchParams.set('via', `${kind}:${id}`);
|
|
25
|
+
const res = await fetch(url.toString(), { headers: this._headers() });
|
|
26
|
+
if (!res.ok) {
|
|
27
|
+
const body = await res.text().catch(() => '');
|
|
28
|
+
throw new Error(`ScopeClient.get ${res.status} ${res.statusText} ${body.slice(0, 200)}`);
|
|
29
|
+
}
|
|
30
|
+
return (await res.json());
|
|
31
|
+
}
|
|
32
|
+
_headers() {
|
|
33
|
+
return {
|
|
34
|
+
'content-type': 'application/json',
|
|
35
|
+
Authorization: `Bearer ${this.operatorKey}`,
|
|
36
|
+
'X-Agent-Id': this.agentId,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import { type Creds, type Task, type TaskState, type Agreement } from '../types.js';
|
|
3
|
+
export interface CreateTaskData {
|
|
4
|
+
description: string;
|
|
5
|
+
agreementId: string;
|
|
6
|
+
parentTaskId?: string;
|
|
7
|
+
plan?: unknown;
|
|
8
|
+
idempotencyKey?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function createTask(taskData: CreateTaskData, creds: Creds): Promise<Task>;
|
|
11
|
+
export declare function getTask(taskId: string, creds: Creds): Promise<Task>;
|
|
12
|
+
export interface UpdateTaskStateData {
|
|
13
|
+
result?: unknown;
|
|
14
|
+
errorMessage?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function updateTaskState(taskId: string, state: TaskState, data: UpdateTaskStateData | undefined, creds: Creds): Promise<Task>;
|
|
17
|
+
export declare function getActiveTasksForAgent(agentId: string, creds: Creds): Promise<Task[]>;
|
|
18
|
+
export declare function getActiveTasksForChat(chatId: string, creds: Creds): Promise<Task[]>;
|
|
19
|
+
export declare function cancelTask(taskId: string, creds: Creds): Promise<Task>;
|
|
20
|
+
export declare function getSubtasks(parentTaskId: string, creds: Creds): Promise<Task[]>;
|
|
21
|
+
export interface PlanReplaceStep {
|
|
22
|
+
stepId: string;
|
|
23
|
+
description: string;
|
|
24
|
+
order: number;
|
|
25
|
+
}
|
|
26
|
+
export declare function replaceTaskPlan(taskId: string, steps: PlanReplaceStep[], creds: Creds): Promise<Task>;
|
|
27
|
+
export interface PublishToLedgerPayload {
|
|
28
|
+
description: string;
|
|
29
|
+
chatId?: string;
|
|
30
|
+
payerId?: string;
|
|
31
|
+
parentTaskId?: string;
|
|
32
|
+
price?: number;
|
|
33
|
+
lifecycle?: string;
|
|
34
|
+
expiresAt?: string;
|
|
35
|
+
maxExecutions?: number;
|
|
36
|
+
agreementDescription?: string;
|
|
37
|
+
}
|
|
38
|
+
export declare function publishToLedger(payload: PublishToLedgerPayload, creds: Creds): Promise<Agreement>;
|
|
39
|
+
export interface PullFromLedgerOptions {
|
|
40
|
+
limit?: number;
|
|
41
|
+
since?: string;
|
|
42
|
+
}
|
|
43
|
+
export declare function pullFromLedger(options: PullFromLedgerOptions | undefined, creds: Creds): Promise<Agreement[]>;
|
|
44
|
+
export declare function claimLedgerTask(agreementId: string, creds: Creds, options?: {
|
|
45
|
+
idempotencyKey?: string;
|
|
46
|
+
}): Promise<Agreement>;
|
|
47
|
+
export declare function reportTask(taskId: string, message: string | undefined, creds: Creds): Promise<Task>;
|
|
48
|
+
export declare function setTaskSatisfaction(taskId: string, satisfaction: 'positive' | 'negative', creds: Creds): Promise<Task>;
|
|
49
|
+
export interface CountTasksFilters {
|
|
50
|
+
chatId?: string;
|
|
51
|
+
state?: string;
|
|
52
|
+
userId?: string;
|
|
53
|
+
agentId?: string;
|
|
54
|
+
}
|
|
55
|
+
export declare function countTasks(filters: CountTasksFilters | undefined, creds: Creds): Promise<number>;
|
|
56
|
+
export declare class TaskClient {
|
|
57
|
+
private creds;
|
|
58
|
+
constructor(operatorKey: string, agentId: string);
|
|
59
|
+
createTask(data: CreateTaskData): Promise<Task>;
|
|
60
|
+
getTask(taskId: string): Promise<Task>;
|
|
61
|
+
updateTaskState(taskId: string, state: TaskState, data?: UpdateTaskStateData): Promise<Task>;
|
|
62
|
+
getActiveTasksForAgent(agentId: string): Promise<Task[]>;
|
|
63
|
+
getActiveTasksForChat(chatId: string): Promise<Task[]>;
|
|
64
|
+
cancelTask(taskId: string): Promise<Task>;
|
|
65
|
+
getSubtasks(parentTaskId: string): Promise<Task[]>;
|
|
66
|
+
replaceTaskPlan(taskId: string, steps: PlanReplaceStep[]): Promise<Task>;
|
|
67
|
+
publishToLedger(payload: PublishToLedgerPayload): Promise<Agreement>;
|
|
68
|
+
pullFromLedger(options?: PullFromLedgerOptions): Promise<Agreement[]>;
|
|
69
|
+
claimLedgerTask(taskId: string, options?: {
|
|
70
|
+
idempotencyKey?: string;
|
|
71
|
+
}): Promise<Agreement>;
|
|
72
|
+
reportTask(taskId: string, message?: string): Promise<Task>;
|
|
73
|
+
setTaskSatisfaction(taskId: string, satisfaction: 'positive' | 'negative'): Promise<Task>;
|
|
74
|
+
countTasks(filters?: CountTasksFilters): Promise<number>;
|
|
75
|
+
}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
|
+
import { ApiError } from '../types.js';
|
|
4
|
+
function getTaskBaseUrl() { return `${getBackendUrl()}/tasks`; }
|
|
5
|
+
function getMarketplaceBaseUrl() { return `${getBackendUrl()}/marketplace`; }
|
|
6
|
+
function buildHeaders(creds) {
|
|
7
|
+
return {
|
|
8
|
+
'content-type': 'application/json',
|
|
9
|
+
Authorization: `Bearer ${creds.operatorKey}`,
|
|
10
|
+
'X-Agent-Id': creds.agentId,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function assertCreds(creds, op) {
|
|
14
|
+
if (!creds?.operatorKey)
|
|
15
|
+
throw new Error(`operatorKey is required for ${op}`);
|
|
16
|
+
if (!creds?.agentId)
|
|
17
|
+
throw new Error(`agentId is required for ${op}`);
|
|
18
|
+
}
|
|
19
|
+
function parseErrorMessage(responseBody, defaultMessage) {
|
|
20
|
+
if (!responseBody)
|
|
21
|
+
return defaultMessage;
|
|
22
|
+
try {
|
|
23
|
+
const d = JSON.parse(responseBody);
|
|
24
|
+
return d['details'] || d['error'] || d['message'] || defaultMessage;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return responseBody || defaultMessage;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function throwApiError(response, responseBody, defaultMessage) {
|
|
31
|
+
throw new ApiError(parseErrorMessage(responseBody, defaultMessage), response.status, responseBody);
|
|
32
|
+
}
|
|
33
|
+
function extractTask(data) {
|
|
34
|
+
if (!data || typeof data !== 'object')
|
|
35
|
+
return null;
|
|
36
|
+
const d = data;
|
|
37
|
+
if (d['task'] && typeof d['task'] === 'object' && d['task']['taskId']) {
|
|
38
|
+
return d['task'];
|
|
39
|
+
}
|
|
40
|
+
if (d['taskId'])
|
|
41
|
+
return d;
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
export async function createTask(taskData, creds) {
|
|
45
|
+
if (!taskData)
|
|
46
|
+
throw new Error('Task data is required for task creation');
|
|
47
|
+
assertCreds(creds, 'task creation');
|
|
48
|
+
const { idempotencyKey, ...bodyData } = taskData;
|
|
49
|
+
const headers = buildHeaders(creds);
|
|
50
|
+
if (idempotencyKey)
|
|
51
|
+
headers['Idempotency-Key'] = idempotencyKey;
|
|
52
|
+
const res = await fetch(getTaskBaseUrl(), {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers,
|
|
55
|
+
body: JSON.stringify(bodyData),
|
|
56
|
+
});
|
|
57
|
+
if (!res.ok) {
|
|
58
|
+
const body = await res.text().catch(() => '');
|
|
59
|
+
throwApiError(res, body, `Task creation failed: ${res.status} ${res.statusText}`);
|
|
60
|
+
}
|
|
61
|
+
const data = await res.json().catch(() => null);
|
|
62
|
+
const task = extractTask(data);
|
|
63
|
+
if (!task)
|
|
64
|
+
throw new Error('Invalid response: task data not found');
|
|
65
|
+
return task;
|
|
66
|
+
}
|
|
67
|
+
export async function getTask(taskId, creds) {
|
|
68
|
+
if (!taskId)
|
|
69
|
+
throw new Error('getTask: taskId is required');
|
|
70
|
+
assertCreds(creds, 'task retrieval');
|
|
71
|
+
const res = await fetch(`${getTaskBaseUrl()}/${taskId}`, {
|
|
72
|
+
method: 'GET',
|
|
73
|
+
headers: buildHeaders(creds),
|
|
74
|
+
});
|
|
75
|
+
if (!res.ok) {
|
|
76
|
+
const body = await res.text().catch(() => '');
|
|
77
|
+
throwApiError(res, body, `Task get failed: ${res.status} ${res.statusText}`);
|
|
78
|
+
}
|
|
79
|
+
const data = await res.json().catch(() => null);
|
|
80
|
+
const task = extractTask(data);
|
|
81
|
+
if (!task)
|
|
82
|
+
throw new Error('Invalid response: task data not found');
|
|
83
|
+
return task;
|
|
84
|
+
}
|
|
85
|
+
export async function updateTaskState(taskId, state, data = {}, creds) {
|
|
86
|
+
if (!taskId)
|
|
87
|
+
throw new Error('updateTaskState: taskId is required');
|
|
88
|
+
if (!state)
|
|
89
|
+
throw new Error('updateTaskState: state is required');
|
|
90
|
+
assertCreds(creds, 'task state update');
|
|
91
|
+
const res = await fetch(`${getTaskBaseUrl()}/${taskId}/state`, {
|
|
92
|
+
method: 'PATCH',
|
|
93
|
+
headers: buildHeaders(creds),
|
|
94
|
+
body: JSON.stringify({ state, ...data }),
|
|
95
|
+
});
|
|
96
|
+
if (!res.ok) {
|
|
97
|
+
const body = await res.text().catch(() => '');
|
|
98
|
+
throwApiError(res, body, `Task state update failed: ${res.status} ${res.statusText}`);
|
|
99
|
+
}
|
|
100
|
+
const result = await res.json().catch(() => null);
|
|
101
|
+
const task = extractTask(result);
|
|
102
|
+
if (!task)
|
|
103
|
+
throw new Error('Invalid response: task data not found');
|
|
104
|
+
return task;
|
|
105
|
+
}
|
|
106
|
+
export async function getActiveTasksForAgent(agentId, creds) {
|
|
107
|
+
if (!agentId)
|
|
108
|
+
throw new Error('getActiveTasksForAgent: agentId is required');
|
|
109
|
+
assertCreds(creds, 'getting active tasks for agent');
|
|
110
|
+
const url = new URL(`${getBackendUrl()}/agents/${encodeURIComponent(agentId)}/tasks`);
|
|
111
|
+
url.searchParams.set('state', 'active');
|
|
112
|
+
const res = await fetch(url.toString(), {
|
|
113
|
+
method: 'GET',
|
|
114
|
+
headers: buildHeaders(creds),
|
|
115
|
+
});
|
|
116
|
+
if (!res.ok) {
|
|
117
|
+
const body = await res.text().catch(() => '');
|
|
118
|
+
throwApiError(res, body, `Get active tasks for agent failed: ${res.status} ${res.statusText}`);
|
|
119
|
+
}
|
|
120
|
+
const data = await res.json().catch(() => null);
|
|
121
|
+
return Array.isArray(data?.['tasks']) ? data['tasks'] : [];
|
|
122
|
+
}
|
|
123
|
+
// Backend deliberately has no `/chats/:id/tasks` route — it's composable
|
|
124
|
+
// from links → per-agreement tasks. We do the composition here so callers
|
|
125
|
+
// don't have to.
|
|
126
|
+
export async function getActiveTasksForChat(chatId, creds) {
|
|
127
|
+
if (!chatId)
|
|
128
|
+
throw new Error('getActiveTasksForChat: chatId is required');
|
|
129
|
+
assertCreds(creds, 'getting active tasks for chat');
|
|
130
|
+
const linksRes = await fetch(`${getBackendUrl()}/agreements/by-chat/${encodeURIComponent(chatId)}`, { method: 'GET', headers: buildHeaders(creds) });
|
|
131
|
+
if (!linksRes.ok) {
|
|
132
|
+
const body = await linksRes.text().catch(() => '');
|
|
133
|
+
throwApiError(linksRes, body, `Get agreements for chat failed: ${linksRes.status} ${linksRes.statusText}`);
|
|
134
|
+
}
|
|
135
|
+
const linksData = await linksRes.json().catch(() => null);
|
|
136
|
+
const rawLinks = linksData?.['links'] ?? linksData?.['agreements'];
|
|
137
|
+
const agreements = Array.isArray(rawLinks) ? rawLinks : [];
|
|
138
|
+
const agreementIds = Array.from(new Set(agreements.map((a) => a['agreementId']).filter(Boolean)));
|
|
139
|
+
if (agreementIds.length === 0)
|
|
140
|
+
return [];
|
|
141
|
+
const lists = await Promise.all(agreementIds.map(async (aid) => {
|
|
142
|
+
const url = new URL(`${getBackendUrl()}/agreements/${encodeURIComponent(aid)}/tasks`);
|
|
143
|
+
url.searchParams.set('state', 'active');
|
|
144
|
+
const res = await fetch(url.toString(), { method: 'GET', headers: buildHeaders(creds) });
|
|
145
|
+
if (!res.ok) {
|
|
146
|
+
const body = await res.text().catch(() => '');
|
|
147
|
+
throwApiError(res, body, `Get tasks for agreement ${aid} failed: ${res.status}`);
|
|
148
|
+
}
|
|
149
|
+
const data = await res.json().catch(() => null);
|
|
150
|
+
return Array.isArray(data?.['tasks']) ? data['tasks'] : [];
|
|
151
|
+
}));
|
|
152
|
+
return lists.flat();
|
|
153
|
+
}
|
|
154
|
+
export async function cancelTask(taskId, creds) {
|
|
155
|
+
if (!taskId)
|
|
156
|
+
throw new Error('cancelTask: taskId is required');
|
|
157
|
+
assertCreds(creds, 'task cancellation');
|
|
158
|
+
const res = await fetch(`${getTaskBaseUrl()}/${taskId}/cancel`, {
|
|
159
|
+
method: 'PATCH',
|
|
160
|
+
headers: buildHeaders(creds),
|
|
161
|
+
});
|
|
162
|
+
if (!res.ok) {
|
|
163
|
+
const body = await res.text().catch(() => '');
|
|
164
|
+
throwApiError(res, body, `Task cancel failed: ${res.status} ${res.statusText}`);
|
|
165
|
+
}
|
|
166
|
+
const result = await res.json().catch(() => null);
|
|
167
|
+
const task = extractTask(result);
|
|
168
|
+
if (!task)
|
|
169
|
+
throw new Error('Invalid response: task data not found');
|
|
170
|
+
return task;
|
|
171
|
+
}
|
|
172
|
+
export async function getSubtasks(parentTaskId, creds) {
|
|
173
|
+
if (!parentTaskId)
|
|
174
|
+
throw new Error('getSubtasks: parentTaskId is required');
|
|
175
|
+
assertCreds(creds, 'getting subtasks');
|
|
176
|
+
const url = new URL(`${getTaskBaseUrl()}/${encodeURIComponent(parentTaskId)}/subtasks`);
|
|
177
|
+
const res = await fetch(url.toString(), {
|
|
178
|
+
method: 'GET',
|
|
179
|
+
headers: buildHeaders(creds),
|
|
180
|
+
});
|
|
181
|
+
if (!res.ok) {
|
|
182
|
+
const body = await res.text().catch(() => '');
|
|
183
|
+
throwApiError(res, body, `Get subtasks failed: ${res.status} ${res.statusText}`);
|
|
184
|
+
}
|
|
185
|
+
const data = await res.json().catch(() => null);
|
|
186
|
+
return Array.isArray(data?.['tasks']) ? data['tasks'] : [];
|
|
187
|
+
}
|
|
188
|
+
export async function replaceTaskPlan(taskId, steps, creds) {
|
|
189
|
+
if (!taskId)
|
|
190
|
+
throw new Error('replaceTaskPlan: taskId is required');
|
|
191
|
+
if (!steps || !Array.isArray(steps))
|
|
192
|
+
throw new Error('replaceTaskPlan: steps must be an array');
|
|
193
|
+
assertCreds(creds, 'plan replace');
|
|
194
|
+
const res = await fetch(`${getTaskBaseUrl()}/${taskId}/plan`, {
|
|
195
|
+
method: 'PATCH',
|
|
196
|
+
headers: buildHeaders(creds),
|
|
197
|
+
body: JSON.stringify({ steps }),
|
|
198
|
+
});
|
|
199
|
+
if (!res.ok) {
|
|
200
|
+
const responseBody = await res.text().catch(() => '');
|
|
201
|
+
throwApiError(res, responseBody, `Plan replace failed: ${res.status} ${res.statusText}`);
|
|
202
|
+
}
|
|
203
|
+
const data = await res.json().catch(() => null);
|
|
204
|
+
const task = extractTask(data);
|
|
205
|
+
if (!task)
|
|
206
|
+
throw new Error('Invalid response: task data not found');
|
|
207
|
+
return task;
|
|
208
|
+
}
|
|
209
|
+
export async function publishToLedger(payload, creds) {
|
|
210
|
+
assertCreds(creds, 'quest publish');
|
|
211
|
+
const res = await fetch(`${getMarketplaceBaseUrl()}/quests/publish`, {
|
|
212
|
+
method: 'POST',
|
|
213
|
+
headers: buildHeaders(creds),
|
|
214
|
+
body: JSON.stringify(payload || {}),
|
|
215
|
+
});
|
|
216
|
+
if (!res.ok) {
|
|
217
|
+
const body = await res.text().catch(() => '');
|
|
218
|
+
throwApiError(res, body, `Quest publish failed: ${res.status}`);
|
|
219
|
+
}
|
|
220
|
+
const data = await res.json().catch(() => null);
|
|
221
|
+
if (!data?.['agreement'])
|
|
222
|
+
throw new Error('Quest publish returned no agreement');
|
|
223
|
+
return data['agreement'];
|
|
224
|
+
}
|
|
225
|
+
export async function pullFromLedger(options, creds) {
|
|
226
|
+
assertCreds(creds, 'quest pull');
|
|
227
|
+
const res = await fetch(`${getMarketplaceBaseUrl()}/quests/pull`, {
|
|
228
|
+
method: 'POST',
|
|
229
|
+
headers: buildHeaders(creds),
|
|
230
|
+
body: JSON.stringify(options || {}),
|
|
231
|
+
});
|
|
232
|
+
if (!res.ok) {
|
|
233
|
+
const body = await res.text().catch(() => '');
|
|
234
|
+
throwApiError(res, body, `Quest pull failed: ${res.status}`);
|
|
235
|
+
}
|
|
236
|
+
const data = await res.json().catch(() => null);
|
|
237
|
+
return data?.['agreements'] ?? [];
|
|
238
|
+
}
|
|
239
|
+
export async function claimLedgerTask(agreementId, creds, options) {
|
|
240
|
+
if (!agreementId)
|
|
241
|
+
throw new Error('agreementId is required');
|
|
242
|
+
assertCreds(creds, 'quest claim');
|
|
243
|
+
const headers = buildHeaders(creds);
|
|
244
|
+
if (options?.idempotencyKey)
|
|
245
|
+
headers['Idempotency-Key'] = options.idempotencyKey;
|
|
246
|
+
const res = await fetch(`${getMarketplaceBaseUrl()}/quests/claim`, {
|
|
247
|
+
method: 'POST',
|
|
248
|
+
headers,
|
|
249
|
+
body: JSON.stringify({ agreementId }),
|
|
250
|
+
});
|
|
251
|
+
if (!res.ok) {
|
|
252
|
+
const body = await res.text().catch(() => '');
|
|
253
|
+
throwApiError(res, body, `Quest claim failed: ${res.status}`);
|
|
254
|
+
}
|
|
255
|
+
const data = await res.json().catch(() => null);
|
|
256
|
+
if (!data?.['ok'])
|
|
257
|
+
throw new Error(data?.['error'] || 'Claim failed');
|
|
258
|
+
return data['agreement'];
|
|
259
|
+
}
|
|
260
|
+
// ---------------------------------------------------------------------------
|
|
261
|
+
// Reporting & satisfaction
|
|
262
|
+
// ---------------------------------------------------------------------------
|
|
263
|
+
export async function reportTask(taskId, message = '', creds) {
|
|
264
|
+
if (!taskId)
|
|
265
|
+
throw new Error('reportTask: taskId is required');
|
|
266
|
+
assertCreds(creds, 'task report');
|
|
267
|
+
const res = await fetch(`${getTaskBaseUrl()}/${taskId}/report`, {
|
|
268
|
+
method: 'POST',
|
|
269
|
+
headers: buildHeaders(creds),
|
|
270
|
+
body: JSON.stringify({ message }),
|
|
271
|
+
});
|
|
272
|
+
if (!res.ok) {
|
|
273
|
+
const body = await res.text().catch(() => '');
|
|
274
|
+
throwApiError(res, body, `Task report failed: ${res.status} ${res.statusText}`);
|
|
275
|
+
}
|
|
276
|
+
const data = await res.json().catch(() => null);
|
|
277
|
+
const task = extractTask(data);
|
|
278
|
+
if (!task)
|
|
279
|
+
throw new Error('Invalid response: task data not found');
|
|
280
|
+
return task;
|
|
281
|
+
}
|
|
282
|
+
export async function setTaskSatisfaction(taskId, satisfaction, creds) {
|
|
283
|
+
if (!taskId)
|
|
284
|
+
throw new Error('setTaskSatisfaction: taskId is required');
|
|
285
|
+
if (!satisfaction)
|
|
286
|
+
throw new Error('setTaskSatisfaction: satisfaction is required');
|
|
287
|
+
assertCreds(creds, 'task satisfaction');
|
|
288
|
+
const res = await fetch(`${getTaskBaseUrl()}/${taskId}/satisfaction`, {
|
|
289
|
+
method: 'PATCH',
|
|
290
|
+
headers: buildHeaders(creds),
|
|
291
|
+
body: JSON.stringify({ satisfaction }),
|
|
292
|
+
});
|
|
293
|
+
if (!res.ok) {
|
|
294
|
+
const body = await res.text().catch(() => '');
|
|
295
|
+
throwApiError(res, body, `Task satisfaction failed: ${res.status} ${res.statusText}`);
|
|
296
|
+
}
|
|
297
|
+
const data = await res.json().catch(() => null);
|
|
298
|
+
const task = extractTask(data);
|
|
299
|
+
if (!task)
|
|
300
|
+
throw new Error('Invalid response: task data not found');
|
|
301
|
+
return task;
|
|
302
|
+
}
|
|
303
|
+
export async function countTasks(filters = {}, creds) {
|
|
304
|
+
assertCreds(creds, 'count tasks');
|
|
305
|
+
const res = await fetch(`${getTaskBaseUrl()}/countTasks`, {
|
|
306
|
+
method: 'POST',
|
|
307
|
+
headers: buildHeaders(creds),
|
|
308
|
+
body: JSON.stringify(filters),
|
|
309
|
+
});
|
|
310
|
+
if (!res.ok) {
|
|
311
|
+
const body = await res.text().catch(() => '');
|
|
312
|
+
throwApiError(res, body, `Count tasks failed: ${res.status} ${res.statusText}`);
|
|
313
|
+
}
|
|
314
|
+
const data = await res.json().catch(() => null);
|
|
315
|
+
return typeof data?.['count'] === 'number' ? data['count'] : 0;
|
|
316
|
+
}
|
|
317
|
+
// ---------------------------------------------------------------------------
|
|
318
|
+
// TaskClient class — bind creds once
|
|
319
|
+
// ---------------------------------------------------------------------------
|
|
320
|
+
export class TaskClient {
|
|
321
|
+
creds;
|
|
322
|
+
constructor(operatorKey, agentId) {
|
|
323
|
+
if (!operatorKey)
|
|
324
|
+
throw new Error('TaskClient: operatorKey is required');
|
|
325
|
+
if (!agentId)
|
|
326
|
+
throw new Error('TaskClient: agentId is required');
|
|
327
|
+
this.creds = { operatorKey, agentId };
|
|
328
|
+
}
|
|
329
|
+
createTask(data) { return createTask(data, this.creds); }
|
|
330
|
+
getTask(taskId) { return getTask(taskId, this.creds); }
|
|
331
|
+
updateTaskState(taskId, state, data) { return updateTaskState(taskId, state, data ?? {}, this.creds); }
|
|
332
|
+
getActiveTasksForAgent(agentId) { return getActiveTasksForAgent(agentId, this.creds); }
|
|
333
|
+
getActiveTasksForChat(chatId) { return getActiveTasksForChat(chatId, this.creds); }
|
|
334
|
+
cancelTask(taskId) { return cancelTask(taskId, this.creds); }
|
|
335
|
+
getSubtasks(parentTaskId) { return getSubtasks(parentTaskId, this.creds); }
|
|
336
|
+
replaceTaskPlan(taskId, steps) { return replaceTaskPlan(taskId, steps, this.creds); }
|
|
337
|
+
publishToLedger(payload) { return publishToLedger(payload, this.creds); }
|
|
338
|
+
pullFromLedger(options) { return pullFromLedger(options, this.creds); }
|
|
339
|
+
claimLedgerTask(taskId, options) { return claimLedgerTask(taskId, this.creds, options); }
|
|
340
|
+
reportTask(taskId, message) { return reportTask(taskId, message, this.creds); }
|
|
341
|
+
setTaskSatisfaction(taskId, satisfaction) { return setTaskSatisfaction(taskId, satisfaction, this.creds); }
|
|
342
|
+
countTasks(filters) { return countTasks(filters, this.creds); }
|
|
343
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
export declare class TelemetryClient {
|
|
3
|
+
private readonly operatorKey;
|
|
4
|
+
private readonly agentId;
|
|
5
|
+
private _queue;
|
|
6
|
+
private _flushing;
|
|
7
|
+
constructor(operatorKey: string, agentId: string);
|
|
8
|
+
send(payload: unknown): Promise<void>;
|
|
9
|
+
private _flush;
|
|
10
|
+
private _post;
|
|
11
|
+
}
|