@swarm-tips/client 0.1.1 → 0.2.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.
- package/README.md +5 -0
- package/SBOM.spdx.json +5 -5
- package/dist/coordination-game/api.d.ts +276 -0
- package/dist/coordination-game/api.js +146 -0
- package/dist/coordination-game/evm/abi.d.ts +409 -0
- package/dist/coordination-game/evm/abi.js +371 -0
- package/dist/coordination-game/evm/cross-chain.d.ts +33 -0
- package/dist/coordination-game/evm/cross-chain.js +76 -0
- package/dist/coordination-game/evm/index.d.ts +6 -0
- package/dist/coordination-game/evm/index.js +6 -0
- package/dist/coordination-game/evm/rules.d.ts +112 -0
- package/dist/coordination-game/evm/rules.js +128 -0
- package/dist/coordination-game/evm/session-funding.d.ts +111 -0
- package/dist/coordination-game/evm/session-funding.js +407 -0
- package/dist/coordination-game/evm/session.d.ts +58 -0
- package/dist/coordination-game/evm/session.js +165 -0
- package/dist/coordination-game/evm/transactions.d.ts +137 -0
- package/dist/coordination-game/evm/transactions.js +211 -0
- package/dist/coordination-game/index.d.ts +1 -0
- package/dist/coordination-game/index.js +1 -0
- package/dist/coordination-game/protocol.d.ts +58 -0
- package/dist/coordination-game/protocol.js +120 -0
- package/dist/coordination-game/solana.d.ts +100 -0
- package/dist/coordination-game/solana.js +309 -0
- package/dist/errors.d.ts +19 -0
- package/dist/errors.js +20 -0
- package/dist/http.d.ts +21 -0
- package/dist/http.js +101 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/shillbot/api.d.ts +76 -0
- package/dist/shillbot/api.js +114 -0
- package/dist/swarm/index.d.ts +84 -0
- package/dist/swarm/index.js +71 -0
- package/package.json +26 -1
package/dist/http.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { SwarmClientError, errorMessage } from "./errors.js";
|
|
2
|
+
export class HttpClient {
|
|
3
|
+
baseUrl;
|
|
4
|
+
network;
|
|
5
|
+
fetchImpl;
|
|
6
|
+
getToken;
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
9
|
+
this.network = options.network;
|
|
10
|
+
const candidate = options.fetch ?? globalThis.fetch;
|
|
11
|
+
if (!candidate)
|
|
12
|
+
throw new SwarmClientError({
|
|
13
|
+
code: "INVALID_ARGUMENT",
|
|
14
|
+
operation: "http.constructor",
|
|
15
|
+
message: "A fetch implementation is required",
|
|
16
|
+
});
|
|
17
|
+
this.fetchImpl = candidate.bind(globalThis);
|
|
18
|
+
this.getToken = options.getToken;
|
|
19
|
+
}
|
|
20
|
+
withNetwork(path) {
|
|
21
|
+
if (!this.network || this.network === "mainnet")
|
|
22
|
+
return path;
|
|
23
|
+
const separator = path.includes("?") ? "&" : "?";
|
|
24
|
+
return `${path}${separator}network=${encodeURIComponent(this.network)}`;
|
|
25
|
+
}
|
|
26
|
+
async request(path, init = {}) {
|
|
27
|
+
const operation = `${init.method ?? "GET"} ${path}`;
|
|
28
|
+
const token = init.token ?? this.getToken?.();
|
|
29
|
+
const headers = {
|
|
30
|
+
"Content-Type": "application/json",
|
|
31
|
+
...init.headers,
|
|
32
|
+
};
|
|
33
|
+
if (token)
|
|
34
|
+
headers.Authorization = `Bearer ${token}`;
|
|
35
|
+
let response;
|
|
36
|
+
try {
|
|
37
|
+
response = await this.fetchImpl(`${this.baseUrl}${this.withNetwork(path)}`, {
|
|
38
|
+
...init,
|
|
39
|
+
headers,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (cause) {
|
|
43
|
+
throw new SwarmClientError({
|
|
44
|
+
code: "NETWORK_ERROR",
|
|
45
|
+
operation,
|
|
46
|
+
message: errorMessage(cause, "Network request failed"),
|
|
47
|
+
retryable: true,
|
|
48
|
+
cause,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
const raw = await response.text().catch(() => "");
|
|
53
|
+
let message = `HTTP ${response.status}`;
|
|
54
|
+
try {
|
|
55
|
+
const body = JSON.parse(raw);
|
|
56
|
+
message = body.message ?? body.error ?? message;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
if (raw.trim())
|
|
60
|
+
message = raw.slice(0, 500);
|
|
61
|
+
}
|
|
62
|
+
throw new SwarmClientError({
|
|
63
|
+
code: "HTTP_ERROR",
|
|
64
|
+
operation,
|
|
65
|
+
message,
|
|
66
|
+
retryable: response.status === 408 || response.status === 429 || response.status >= 500,
|
|
67
|
+
status: response.status,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (response.status === 204)
|
|
71
|
+
return undefined;
|
|
72
|
+
try {
|
|
73
|
+
return (await response.json());
|
|
74
|
+
}
|
|
75
|
+
catch (cause) {
|
|
76
|
+
throw new SwarmClientError({
|
|
77
|
+
code: "INVALID_RESPONSE",
|
|
78
|
+
operation,
|
|
79
|
+
message: "Response was not valid JSON",
|
|
80
|
+
cause,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
get(path, token) {
|
|
85
|
+
return this.request(path, { method: "GET", token });
|
|
86
|
+
}
|
|
87
|
+
post(path, body, token) {
|
|
88
|
+
return this.request(path, {
|
|
89
|
+
method: "POST",
|
|
90
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
91
|
+
token,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
patch(path, body, token) {
|
|
95
|
+
return this.request(path, {
|
|
96
|
+
method: "PATCH",
|
|
97
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
98
|
+
token,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
/** The unified client package version. Functional APIs live in typed subpaths. */
|
|
2
|
-
export declare const CLIENT_VERSION: "0.1
|
|
2
|
+
export declare const CLIENT_VERSION: "0.2.1";
|
|
3
3
|
export declare const CLIENT_MANIFEST_SCHEMA: "swarm.client-manifest/v1";
|
|
4
|
+
export { SwarmClientError } from "./errors.js";
|
|
5
|
+
export type { SwarmClientErrorCode, SwarmClientErrorOptions } from "./errors.js";
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
/** The unified client package version. Functional APIs live in typed subpaths. */
|
|
2
|
-
export const CLIENT_VERSION = "0.1
|
|
2
|
+
export const CLIENT_VERSION = "0.2.1";
|
|
3
3
|
export const CLIENT_MANIFEST_SCHEMA = "swarm.client-manifest/v1";
|
|
4
|
+
export { SwarmClientError } from "./errors.js";
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Connection, type BlockhashWithExpiryBlockHeight } from "@solana/web3.js";
|
|
2
|
+
import { HttpClient, type HttpClientOptions } from "../http.js";
|
|
3
|
+
import { type Action, type TransactionInspection, type WalletSignCallback } from "./index.js";
|
|
4
|
+
export interface PreparedTransactionResponse {
|
|
5
|
+
message?: string;
|
|
6
|
+
task_id?: string;
|
|
7
|
+
transaction: string;
|
|
8
|
+
task_pda?: string;
|
|
9
|
+
evm_call?: unknown;
|
|
10
|
+
}
|
|
11
|
+
export interface ConfirmTransactionRequest {
|
|
12
|
+
tx_signature: string;
|
|
13
|
+
action: Action;
|
|
14
|
+
task_pda?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface TaskListResponse<TTask = unknown> {
|
|
17
|
+
tasks: TTask[];
|
|
18
|
+
next_cursor?: string | null;
|
|
19
|
+
}
|
|
20
|
+
export interface CampaignListResponse<TCampaign = unknown> {
|
|
21
|
+
campaigns: TCampaign[];
|
|
22
|
+
next_cursor?: string | null;
|
|
23
|
+
}
|
|
24
|
+
export declare class ShillbotApiClient {
|
|
25
|
+
readonly http: HttpClient;
|
|
26
|
+
constructor(options: HttpClientOptions);
|
|
27
|
+
get<T>(path: string): Promise<T>;
|
|
28
|
+
post<T>(path: string, body?: unknown): Promise<T>;
|
|
29
|
+
patch<T>(path: string, body?: unknown): Promise<T>;
|
|
30
|
+
listTasks<TTask = unknown>(limit?: number): Promise<TaskListResponse<TTask>>;
|
|
31
|
+
listAgentTasks<TTask = unknown>(): Promise<TaskListResponse<TTask> | TTask[]>;
|
|
32
|
+
listPendingApproval<TTask = unknown>(): Promise<TaskListResponse<TTask> | TTask[]>;
|
|
33
|
+
listCampaigns<TCampaign = unknown>(): Promise<CampaignListResponse<TCampaign> | TCampaign[]>;
|
|
34
|
+
campaign<TCampaign = unknown>(id: string): Promise<TCampaign>;
|
|
35
|
+
campaignTasks<TTask = unknown>(id: string): Promise<TaskListResponse<TTask>>;
|
|
36
|
+
campaignMetrics<TMetrics = unknown>(id: string): Promise<TMetrics>;
|
|
37
|
+
createCampaign<TResponse = {
|
|
38
|
+
campaign_id: string;
|
|
39
|
+
}>(body: unknown): Promise<TResponse>;
|
|
40
|
+
updateCampaign<TResponse = unknown>(id: string, action: string): Promise<TResponse>;
|
|
41
|
+
fundCampaignTask(id: string, body: unknown): Promise<PreparedTransactionResponse>;
|
|
42
|
+
onboard<TResponse = unknown>(): Promise<TResponse>;
|
|
43
|
+
earnings<TResponse = unknown>(): Promise<TResponse>;
|
|
44
|
+
claimTask(id: string, sponsor?: string): Promise<PreparedTransactionResponse>;
|
|
45
|
+
submitTask(id: string, body: unknown): Promise<PreparedTransactionResponse>;
|
|
46
|
+
approveTask(id: string): Promise<PreparedTransactionResponse>;
|
|
47
|
+
buildVerify(id: string): Promise<PreparedTransactionResponse>;
|
|
48
|
+
buildFinalize(id: string): Promise<PreparedTransactionResponse>;
|
|
49
|
+
confirmTask<TResponse = unknown>(id: string, body: ConfirmTransactionRequest): Promise<TResponse>;
|
|
50
|
+
}
|
|
51
|
+
export interface PreparedTransactionExpectation {
|
|
52
|
+
action: Action;
|
|
53
|
+
network: "mainnet" | "devnet";
|
|
54
|
+
wallet: string;
|
|
55
|
+
taskPda?: string;
|
|
56
|
+
feePayer?: string;
|
|
57
|
+
escrowLamports?: bigint;
|
|
58
|
+
allowedProgramIds?: string[];
|
|
59
|
+
}
|
|
60
|
+
export declare function permittedPreparedProgramIds(action: Action, network: "mainnet" | "devnet"): string[];
|
|
61
|
+
/**
|
|
62
|
+
* Fail-closed local validation for a server-prepared Shillbot transaction.
|
|
63
|
+
* The network is an explicit part of the expectation; callers must construct
|
|
64
|
+
* their Connection for that same network before signing.
|
|
65
|
+
*/
|
|
66
|
+
export declare function validatePreparedTransaction(encoded: string, expected: PreparedTransactionExpectation): TransactionInspection;
|
|
67
|
+
export declare function signBroadcastPreparedTransaction(input: {
|
|
68
|
+
connection: Connection;
|
|
69
|
+
transaction: string;
|
|
70
|
+
expected: PreparedTransactionExpectation;
|
|
71
|
+
sign: WalletSignCallback;
|
|
72
|
+
confirmation?: BlockhashWithExpiryBlockHeight;
|
|
73
|
+
timeoutMs?: number;
|
|
74
|
+
setTimeout?: typeof globalThis.setTimeout;
|
|
75
|
+
clearTimeout?: typeof globalThis.clearTimeout;
|
|
76
|
+
}): Promise<string>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { Buffer } from "buffer";
|
|
2
|
+
import { ComputeBudgetProgram, Secp256k1Program, SystemProgram } from "@solana/web3.js";
|
|
3
|
+
import { HttpClient } from "../http.js";
|
|
4
|
+
import { SwarmClientError } from "../errors.js";
|
|
5
|
+
import { SHILLBOT_PROGRAM_ID, inspectTransaction, shillbotInstructionDiscriminator, signAndBroadcast, } from "./index.js";
|
|
6
|
+
export class ShillbotApiClient {
|
|
7
|
+
http;
|
|
8
|
+
constructor(options) { this.http = new HttpClient(options); }
|
|
9
|
+
get(path) { return this.http.get(path); }
|
|
10
|
+
post(path, body) { return this.http.post(path, body); }
|
|
11
|
+
patch(path, body) { return this.http.patch(path, body); }
|
|
12
|
+
listTasks(limit = 50) { return this.get(`/tasks?limit=${encodeURIComponent(String(limit))}`); }
|
|
13
|
+
listAgentTasks() { return this.get("/agent/tasks"); }
|
|
14
|
+
listPendingApproval() { return this.get("/tasks?status=pending_approval"); }
|
|
15
|
+
listCampaigns() { return this.get("/campaigns"); }
|
|
16
|
+
campaign(id) { return this.get(`/campaigns/${encodeURIComponent(id)}`); }
|
|
17
|
+
campaignTasks(id) { return this.get(`/campaigns/${encodeURIComponent(id)}/tasks`); }
|
|
18
|
+
campaignMetrics(id) { return this.get(`/campaigns/${encodeURIComponent(id)}/metrics`); }
|
|
19
|
+
createCampaign(body) { return this.post("/campaigns", body); }
|
|
20
|
+
updateCampaign(id, action) { return this.patch(`/campaigns/${encodeURIComponent(id)}`, { action }); }
|
|
21
|
+
fundCampaignTask(id, body) { return this.post(`/campaigns/${encodeURIComponent(id)}/fund`, body); }
|
|
22
|
+
onboard() { return this.post("/agent/onboard"); }
|
|
23
|
+
earnings() { return this.get("/agent/earnings"); }
|
|
24
|
+
claimTask(id, sponsor = "auto") { return this.post(`/tasks/${encodeURIComponent(id)}/claim?sponsor=${encodeURIComponent(sponsor)}`); }
|
|
25
|
+
submitTask(id, body) { return this.post(`/tasks/${encodeURIComponent(id)}/submit`, body); }
|
|
26
|
+
approveTask(id) { return this.post(`/tasks/${encodeURIComponent(id)}/approve`); }
|
|
27
|
+
buildVerify(id) { return this.post(`/tasks/${encodeURIComponent(id)}/build-verify`); }
|
|
28
|
+
buildFinalize(id) { return this.post(`/tasks/${encodeURIComponent(id)}/build-finalize`); }
|
|
29
|
+
confirmTask(id, body) { return this.post(`/tasks/${encodeURIComponent(id)}/confirm`, body); }
|
|
30
|
+
}
|
|
31
|
+
const SWITCHBOARD_PROGRAM_IDS = {
|
|
32
|
+
mainnet: "SBondMDrcV3K4kxZR1HNVT7osZxAHVHgYXL5Ze1oMUv",
|
|
33
|
+
devnet: "Aio4gaXjXzJNVLtzwtNVmSqGKpANtXhybbkhtAC94ji2",
|
|
34
|
+
};
|
|
35
|
+
export function permittedPreparedProgramIds(action, network) {
|
|
36
|
+
return action === "verify"
|
|
37
|
+
? [SWITCHBOARD_PROGRAM_IDS[network], Secp256k1Program.programId.toBase58()]
|
|
38
|
+
: [];
|
|
39
|
+
}
|
|
40
|
+
function lifecycleName(action) {
|
|
41
|
+
if (action === "create")
|
|
42
|
+
return "create_task";
|
|
43
|
+
if (action === "submit")
|
|
44
|
+
return "submit_work";
|
|
45
|
+
return `${action}_task`;
|
|
46
|
+
}
|
|
47
|
+
function matchesDiscriminator(dataBase64, action) {
|
|
48
|
+
const data = Buffer.from(dataBase64, "base64");
|
|
49
|
+
return data.subarray(0, 8).equals(shillbotInstructionDiscriminator(lifecycleName(action)));
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Fail-closed local validation for a server-prepared Shillbot transaction.
|
|
53
|
+
* The network is an explicit part of the expectation; callers must construct
|
|
54
|
+
* their Connection for that same network before signing.
|
|
55
|
+
*/
|
|
56
|
+
export function validatePreparedTransaction(encoded, expected) {
|
|
57
|
+
if (expected.network !== "mainnet" && expected.network !== "devnet") {
|
|
58
|
+
throw new SwarmClientError({ code: "INVALID_ARGUMENT", operation: "shillbot.validatePreparedTransaction", message: "Unsupported network" });
|
|
59
|
+
}
|
|
60
|
+
const inspected = inspectTransaction(encoded);
|
|
61
|
+
if (!inspected.signers.includes(expected.wallet)) {
|
|
62
|
+
throw new SwarmClientError({ code: "TRANSACTION_MISMATCH", operation: "shillbot.validatePreparedTransaction", message: "Wallet is not a required signer" });
|
|
63
|
+
}
|
|
64
|
+
if (expected.feePayer && inspected.fee_payer !== expected.feePayer) {
|
|
65
|
+
throw new SwarmClientError({ code: "TRANSACTION_MISMATCH", operation: "shillbot.validatePreparedTransaction", message: "Fee payer differs from expectation" });
|
|
66
|
+
}
|
|
67
|
+
const shillbotId = SHILLBOT_PROGRAM_ID.toBase58();
|
|
68
|
+
const lifecycle = inspected.instructions.filter((instruction) => instruction.program_id === shillbotId && matchesDiscriminator(instruction.data_base64, expected.action));
|
|
69
|
+
if (lifecycle.length !== 1) {
|
|
70
|
+
throw new SwarmClientError({ code: "TRANSACTION_MISMATCH", operation: "shillbot.validatePreparedTransaction", message: "Expected exactly one matching Shillbot lifecycle instruction" });
|
|
71
|
+
}
|
|
72
|
+
if (expected.taskPda && !lifecycle[0].accounts.includes(expected.taskPda)) {
|
|
73
|
+
throw new SwarmClientError({ code: "TRANSACTION_MISMATCH", operation: "shillbot.validatePreparedTransaction", message: "Task PDA differs from expectation" });
|
|
74
|
+
}
|
|
75
|
+
const alwaysAllowed = new Set([
|
|
76
|
+
shillbotId,
|
|
77
|
+
ComputeBudgetProgram.programId.toBase58(),
|
|
78
|
+
SystemProgram.programId.toBase58(),
|
|
79
|
+
...permittedPreparedProgramIds(expected.action, expected.network),
|
|
80
|
+
...(expected.allowedProgramIds ?? []),
|
|
81
|
+
]);
|
|
82
|
+
const unrelated = inspected.instructions.find((instruction) => !alwaysAllowed.has(instruction.program_id));
|
|
83
|
+
if (unrelated) {
|
|
84
|
+
throw new SwarmClientError({ code: "TRANSACTION_MISMATCH", operation: "shillbot.validatePreparedTransaction", message: `Unexpected program ${unrelated.program_id}` });
|
|
85
|
+
}
|
|
86
|
+
if (expected.action === "create" && expected.escrowLamports !== undefined) {
|
|
87
|
+
const data = Buffer.from(lifecycle[0].data_base64, "base64");
|
|
88
|
+
if (data.length < 24)
|
|
89
|
+
throw new SwarmClientError({ code: "TRANSACTION_MISMATCH", operation: "shillbot.validatePreparedTransaction", message: "Create instruction is truncated" });
|
|
90
|
+
const view = new DataView(data.buffer, data.byteOffset + 16, 8);
|
|
91
|
+
const amount = view.getBigUint64(0, true);
|
|
92
|
+
if (amount !== expected.escrowLamports) {
|
|
93
|
+
throw new SwarmClientError({ code: "TRANSACTION_MISMATCH", operation: "shillbot.validatePreparedTransaction", message: `Escrow amount ${amount} differs from expected ${expected.escrowLamports}` });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return inspected;
|
|
97
|
+
}
|
|
98
|
+
export async function signBroadcastPreparedTransaction(input) {
|
|
99
|
+
validatePreparedTransaction(input.transaction, input.expected);
|
|
100
|
+
const timeoutMs = input.timeoutMs ?? 60_000;
|
|
101
|
+
let timeout;
|
|
102
|
+
try {
|
|
103
|
+
return await Promise.race([
|
|
104
|
+
signAndBroadcast(input.connection, input.transaction, input.sign, input.confirmation),
|
|
105
|
+
new Promise((_, reject) => {
|
|
106
|
+
timeout = (input.setTimeout ?? globalThis.setTimeout)(() => reject(new SwarmClientError({ code: "TIMEOUT", operation: "shillbot.signBroadcastPreparedTransaction", message: `Transaction did not confirm within ${timeoutMs}ms`, retryable: true })), timeoutMs);
|
|
107
|
+
}),
|
|
108
|
+
]);
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
if (timeout !== undefined)
|
|
112
|
+
(input.clearTimeout ?? globalThis.clearTimeout)(timeout);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { HttpClient, type FetchLike } from "../http.js";
|
|
2
|
+
export interface AgentJob {
|
|
3
|
+
source: string;
|
|
4
|
+
source_id: string;
|
|
5
|
+
source_url: string;
|
|
6
|
+
title: string;
|
|
7
|
+
description: string;
|
|
8
|
+
category: string;
|
|
9
|
+
vertical?: string;
|
|
10
|
+
tags: string[];
|
|
11
|
+
reward_amount: string;
|
|
12
|
+
reward_token: string;
|
|
13
|
+
reward_chain: string;
|
|
14
|
+
reward_usd_estimate?: number;
|
|
15
|
+
payment_model: string;
|
|
16
|
+
escrow: boolean;
|
|
17
|
+
posted_at: string;
|
|
18
|
+
deadline?: string | null;
|
|
19
|
+
status: string;
|
|
20
|
+
claims_remaining?: number | null;
|
|
21
|
+
required_capabilities?: string[];
|
|
22
|
+
client_address?: string;
|
|
23
|
+
how_to_start?: string;
|
|
24
|
+
indexed_at: string;
|
|
25
|
+
slots_open?: number;
|
|
26
|
+
}
|
|
27
|
+
export interface RankingSignals {
|
|
28
|
+
bm25_score: number;
|
|
29
|
+
final_score: number;
|
|
30
|
+
quality_prior: number;
|
|
31
|
+
source_count: number;
|
|
32
|
+
github_stars: number | null;
|
|
33
|
+
npm_weekly_downloads: number | null;
|
|
34
|
+
upstream_quality_score: number | null;
|
|
35
|
+
llm_confidence: number | null;
|
|
36
|
+
}
|
|
37
|
+
export interface SearchHit {
|
|
38
|
+
name: string;
|
|
39
|
+
title: string | null;
|
|
40
|
+
description: string | null;
|
|
41
|
+
endpoint: string | null;
|
|
42
|
+
transport: string | null;
|
|
43
|
+
github_repo: string | null;
|
|
44
|
+
npm_package: string | null;
|
|
45
|
+
category: string | null;
|
|
46
|
+
currencies: string[];
|
|
47
|
+
provenance: string;
|
|
48
|
+
ranking_signals: RankingSignals;
|
|
49
|
+
}
|
|
50
|
+
export interface EigenTrustRecord {
|
|
51
|
+
wallet: string;
|
|
52
|
+
eigentrust_score: number;
|
|
53
|
+
rank: number;
|
|
54
|
+
rank_normalized: number;
|
|
55
|
+
settlements_received: number;
|
|
56
|
+
settlements_paid: number;
|
|
57
|
+
counterparty_count: number;
|
|
58
|
+
computed_at: string;
|
|
59
|
+
}
|
|
60
|
+
export interface AgentReputation {
|
|
61
|
+
wallet: string;
|
|
62
|
+
web_position: number | null;
|
|
63
|
+
extensions_received: number;
|
|
64
|
+
has_standing: boolean;
|
|
65
|
+
eigentrust?: EigenTrustRecord | null;
|
|
66
|
+
}
|
|
67
|
+
export interface SwarmClientOptions {
|
|
68
|
+
baseUrl: string;
|
|
69
|
+
fetch?: FetchLike;
|
|
70
|
+
}
|
|
71
|
+
export declare class SwarmClient {
|
|
72
|
+
readonly http: HttpClient;
|
|
73
|
+
private readonly fetchImpl;
|
|
74
|
+
readonly baseUrl: string;
|
|
75
|
+
constructor(options: SwarmClientOptions);
|
|
76
|
+
listings(): Promise<AgentJob[]>;
|
|
77
|
+
search(query: string, limit?: number): Promise<{
|
|
78
|
+
results: SearchHit[];
|
|
79
|
+
corpus_size: number;
|
|
80
|
+
}>;
|
|
81
|
+
reputation(wallet: string, network?: string): Promise<AgentReputation>;
|
|
82
|
+
leaderboard(limit?: number): Promise<EigenTrustRecord[]>;
|
|
83
|
+
toolCount(): Promise<number | null>;
|
|
84
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { HttpClient } from "../http.js";
|
|
2
|
+
function parseRpc(body) {
|
|
3
|
+
const trimmed = body.trimStart();
|
|
4
|
+
if (trimmed.startsWith("{")) {
|
|
5
|
+
try {
|
|
6
|
+
return JSON.parse(trimmed);
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
for (const line of body.split("\n")) {
|
|
13
|
+
const value = line.trim();
|
|
14
|
+
if (!value.startsWith("data:"))
|
|
15
|
+
continue;
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(value.slice(5).trim());
|
|
18
|
+
}
|
|
19
|
+
catch { /* continue */ }
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
export class SwarmClient {
|
|
24
|
+
http;
|
|
25
|
+
fetchImpl;
|
|
26
|
+
baseUrl;
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
29
|
+
this.fetchImpl = (options.fetch ?? globalThis.fetch).bind(globalThis);
|
|
30
|
+
this.http = new HttpClient(options);
|
|
31
|
+
}
|
|
32
|
+
async listings() { return this.http.get("/internal/listings"); }
|
|
33
|
+
async search(query, limit = 30) {
|
|
34
|
+
const params = new URLSearchParams();
|
|
35
|
+
if (query)
|
|
36
|
+
params.set("query", query);
|
|
37
|
+
params.set("limit", String(limit));
|
|
38
|
+
return this.http.get(`/internal/mcp/search?${params.toString()}`);
|
|
39
|
+
}
|
|
40
|
+
reputation(wallet, network = "mainnet") {
|
|
41
|
+
if (!wallet)
|
|
42
|
+
return Promise.reject(new TypeError("wallet is required"));
|
|
43
|
+
return this.http.get(`/internal/agent-reputation?wallet=${encodeURIComponent(wallet)}&network=${encodeURIComponent(network)}`);
|
|
44
|
+
}
|
|
45
|
+
async leaderboard(limit = 25) {
|
|
46
|
+
const body = await this.http.get(`/internal/reputation/leaderboard?limit=${encodeURIComponent(String(limit))}`);
|
|
47
|
+
return body.agents ?? [];
|
|
48
|
+
}
|
|
49
|
+
async toolCount() {
|
|
50
|
+
const headers = { "Content-Type": "application/json", Accept: "application/json, text/event-stream" };
|
|
51
|
+
try {
|
|
52
|
+
const init = await this.fetchImpl(`${this.baseUrl}/mcp`, { method: "POST", headers, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2025-03-26", capabilities: {}, clientInfo: { name: "swarm-client", version: "0.2.1" } } }) });
|
|
53
|
+
if (!init.ok)
|
|
54
|
+
return null;
|
|
55
|
+
const session = init.headers.get("mcp-session-id");
|
|
56
|
+
await init.text();
|
|
57
|
+
if (!session)
|
|
58
|
+
return null;
|
|
59
|
+
const sessionHeaders = { ...headers, "mcp-session-id": session };
|
|
60
|
+
await this.fetchImpl(`${this.baseUrl}/mcp`, { method: "POST", headers: sessionHeaders, body: JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" }) });
|
|
61
|
+
const listed = await this.fetchImpl(`${this.baseUrl}/mcp`, { method: "POST", headers: sessionHeaders, body: JSON.stringify({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} }) });
|
|
62
|
+
if (!listed.ok)
|
|
63
|
+
return null;
|
|
64
|
+
const tools = parseRpc(await listed.text())?.result?.tools;
|
|
65
|
+
return Array.isArray(tools) && tools.length ? tools.length : null;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarm-tips/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Typed browser and Node.js clients for Swarm Tips, Shillbot, and Coordination Game",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,11 +17,36 @@
|
|
|
17
17
|
"import": "./dist/shillbot/index.js",
|
|
18
18
|
"require": "./dist/shillbot/index.js"
|
|
19
19
|
},
|
|
20
|
+
"./shillbot/api": {
|
|
21
|
+
"types": "./dist/shillbot/api.d.ts",
|
|
22
|
+
"import": "./dist/shillbot/api.js",
|
|
23
|
+
"require": "./dist/shillbot/api.js"
|
|
24
|
+
},
|
|
20
25
|
"./coordination-game": {
|
|
21
26
|
"types": "./dist/coordination-game/index.d.ts",
|
|
22
27
|
"import": "./dist/coordination-game/index.js",
|
|
23
28
|
"require": "./dist/coordination-game/index.js"
|
|
24
29
|
},
|
|
30
|
+
"./coordination-game/solana": {
|
|
31
|
+
"types": "./dist/coordination-game/solana.d.ts",
|
|
32
|
+
"import": "./dist/coordination-game/solana.js",
|
|
33
|
+
"require": "./dist/coordination-game/solana.js"
|
|
34
|
+
},
|
|
35
|
+
"./coordination-game/evm": {
|
|
36
|
+
"types": "./dist/coordination-game/evm/index.d.ts",
|
|
37
|
+
"import": "./dist/coordination-game/evm/index.js",
|
|
38
|
+
"require": "./dist/coordination-game/evm/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./coordination-game/api": {
|
|
41
|
+
"types": "./dist/coordination-game/api.d.ts",
|
|
42
|
+
"import": "./dist/coordination-game/api.js",
|
|
43
|
+
"require": "./dist/coordination-game/api.js"
|
|
44
|
+
},
|
|
45
|
+
"./swarm": {
|
|
46
|
+
"types": "./dist/swarm/index.d.ts",
|
|
47
|
+
"import": "./dist/swarm/index.js",
|
|
48
|
+
"require": "./dist/swarm/index.js"
|
|
49
|
+
},
|
|
25
50
|
"./evm": {
|
|
26
51
|
"types": "./dist/evm/index.d.ts",
|
|
27
52
|
"import": "./dist/evm/index.js",
|