@bountyagents/bountyagents-task 2026.3.100 → 2026.3.102

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/src/publisher.ts DELETED
@@ -1,322 +0,0 @@
1
- import { Type } from "@sinclair/typebox";
2
- import { createPublicClient, createWalletClient, http, parseUnits } from "viem";
3
- import { privateKeyToAccount } from "viem/accounts";
4
- import { bscTestnet } from "viem/chains";
5
- import { taskDepositKey } from "./escrow.js";
6
- import { json, getPrivateKey } from "./helper.js";
7
- import { BountyAgentsPublisherPlugin } from "./index.js";
8
- import { PrivateKeySigner } from "./signers.js";
9
-
10
- const SERVICE_URL = "http://localhost:3000";
11
- const CONTRACT_ADDRESS = "0x55D45aFA265d0381C8A81328FfeA408D2Dd45F40";
12
- const TEST_TOKEN_ADDRESS = "0x56DA32693A4e6dDd0eDC932b295cb00372f37f8b";
13
-
14
- const AGENT_ESCROW_ABI = [
15
- {
16
- inputs: [
17
- { name: "key", type: "bytes32" },
18
- { name: "token", type: "address" },
19
- { name: "amount", type: "uint256" },
20
- ],
21
- name: "deposit",
22
- outputs: [],
23
- stateMutability: "nonpayable",
24
- type: "function",
25
- },
26
- {
27
- inputs: [{ name: "key", type: "bytes32" }],
28
- name: "getDeposit",
29
- outputs: [
30
- {
31
- components: [
32
- { name: "owner", type: "address" },
33
- { name: "token", type: "address" },
34
- { name: "amountLocked", type: "uint256" },
35
- { name: "released", type: "bool" },
36
- ],
37
- name: "",
38
- type: "tuple",
39
- },
40
- ],
41
- stateMutability: "view",
42
- type: "function",
43
- },
44
- ] as const;
45
-
46
- const ERC20_ABI = [
47
- {
48
- inputs: [
49
- { name: "spender", type: "address" },
50
- { name: "amount", type: "uint256" },
51
- ],
52
- name: "approve",
53
- outputs: [{ name: "", type: "bool" }],
54
- stateMutability: "nonpayable",
55
- type: "function",
56
- },
57
- {
58
- inputs: [],
59
- name: "decimals",
60
- outputs: [{ name: "", type: "uint8" }],
61
- stateMutability: "view",
62
- type: "function",
63
- },
64
- ] as const;
65
-
66
- export async function createBountyTask(params: {
67
- title: string;
68
- content: string;
69
- }) {
70
- const signer = new PrivateKeySigner(getPrivateKey());
71
- const plugin = new BountyAgentsPublisherPlugin(signer, {
72
- serviceUrl: SERVICE_URL,
73
- contractAddress: CONTRACT_ADDRESS,
74
- });
75
-
76
- const task = (await plugin.executeTool("bountyagents.publisher.task.create", {
77
- id: crypto.randomUUID(),
78
- title: params.title,
79
- content: params.content,
80
- })) as any;
81
- return task;
82
- }
83
-
84
- export async function fundBountyTask(params: {
85
- taskId: string;
86
- token: string;
87
- }) {
88
- const signer = new PrivateKeySigner(getPrivateKey());
89
- const plugin = new BountyAgentsPublisherPlugin(signer, {
90
- serviceUrl: SERVICE_URL,
91
- contractAddress: CONTRACT_ADDRESS,
92
- });
93
-
94
- const task = (await plugin.executeTool("bountyagents.publisher.task.fund", {
95
- taskId: params.taskId,
96
- token: params.token,
97
- })) as any;
98
- return task;
99
- }
100
-
101
- export async function depositToken(params: {
102
- taskId: string;
103
- amount?: string;
104
- }) {
105
- const account = privateKeyToAccount(getPrivateKey());
106
- const chain = bscTestnet;
107
- const transport = http();
108
-
109
- const walletClient = createWalletClient({
110
- account,
111
- chain,
112
- transport,
113
- });
114
-
115
- const publicClient = createPublicClient({
116
- chain,
117
- transport,
118
- });
119
-
120
- // Fetch decimals from the token contract
121
- console.log(`Fetching decimals for token ${TEST_TOKEN_ADDRESS}...`);
122
- const decimals = await publicClient.readContract({
123
- address: TEST_TOKEN_ADDRESS as `0x${string}`,
124
- abi: ERC20_ABI,
125
- functionName: "decimals",
126
- });
127
- console.log(`Token decimals: ${decimals}`);
128
-
129
- const amount = parseUnits(params.amount || "100", decimals);
130
-
131
- const depositKey = taskDepositKey(params.taskId);
132
- console.log(`Deposit key: ${depositKey}`);
133
-
134
- // 1. Approve
135
- console.log(`Approving ${params.amount || "100"} tokens...`);
136
- const approveHash = await walletClient.writeContract({
137
- address: TEST_TOKEN_ADDRESS as `0x${string}`,
138
- abi: ERC20_ABI,
139
- functionName: "approve",
140
- args: [CONTRACT_ADDRESS as `0x${string}`, amount],
141
- });
142
- console.log(`Approve transaction sent: ${approveHash}`);
143
- console.log(`Waiting for confirmation...`);
144
- await publicClient.waitForTransactionReceipt({ hash: approveHash });
145
- console.log(`Approve confirmed!`);
146
-
147
- // 2. Deposit
148
- console.log(`Depositing tokens into AgentEscrow...`);
149
- const depositHash = await walletClient.writeContract({
150
- address: CONTRACT_ADDRESS as `0x${string}`,
151
- abi: AGENT_ESCROW_ABI,
152
- functionName: "deposit",
153
- args: [depositKey, TEST_TOKEN_ADDRESS as `0x${string}`, amount],
154
- });
155
-
156
- await publicClient.waitForTransactionReceipt({ hash: depositHash });
157
- console.log(`Deposit confirmed!`);
158
-
159
- const depositInfo = await publicClient.readContract({
160
- address: CONTRACT_ADDRESS as `0x${string}`,
161
- abi: AGENT_ESCROW_ABI,
162
- functionName: "getDeposit",
163
- args: [depositKey],
164
- });
165
-
166
- return {
167
- message: `Successfully deposited ${params.amount || "100"} test tokens`,
168
- taskId: params.taskId,
169
- approveTransactionHash: approveHash,
170
- depositTransactionHash: depositHash,
171
- rawAmount: amount.toString(),
172
- rawLockedAmount: depositInfo.amountLocked.toString(),
173
- };
174
- }
175
-
176
- export async function decideOnResponse(params: {
177
- responseId: string;
178
- workerAddress: string;
179
- price: string;
180
- status: "approved" | "rejected";
181
- }) {
182
- const signer = new PrivateKeySigner(getPrivateKey());
183
- const plugin = new BountyAgentsPublisherPlugin(signer, {
184
- serviceUrl: SERVICE_URL,
185
- contractAddress: CONTRACT_ADDRESS,
186
- });
187
-
188
- const response = (await plugin.executeTool(
189
- "bountyagents.publisher.task.decision",
190
- params
191
- )) as any;
192
- return response;
193
- }
194
-
195
- export async function openUpclawDashboard() {
196
- const signer = new PrivateKeySigner(getPrivateKey());
197
- const message = `login-${Date.now()}`;
198
- const signature = await signer.signMessage(message);
199
-
200
- const response = await fetch(`${SERVICE_URL}/request-token`, {
201
- method: "POST",
202
- headers: {
203
- "Content-Type": "application/json",
204
- },
205
- body: JSON.stringify({
206
- address: signer.address,
207
- message,
208
- signature,
209
- }),
210
- });
211
-
212
- if (!response.ok) {
213
- const errorText = await response.text();
214
- throw new Error(`Failed to request token: ${errorText}`);
215
- }
216
-
217
- const { token } = await response.json() as { token: string };
218
- return { url: `http://localhost:6006/?token=${token}` };
219
- }
220
-
221
- export function registerPublisherTools(api: any) {
222
- api.registerTool({
223
- name: "create_bounty_task",
224
- description:
225
- "Create a draft bounty task for an agent based on user request, tell user to give title and content of the task, and return the created task id to user if successful",
226
- parameters: Type.Object({
227
- title: Type.String(),
228
- content: Type.String(),
229
- }),
230
- async execute(_id: string, params: any) {
231
- try {
232
- const result = await createBountyTask(params);
233
- return json(result);
234
- } catch (error: any) {
235
- return json({
236
- error: error instanceof Error ? error.message : String(error),
237
- });
238
- }
239
- },
240
- });
241
-
242
- api.registerTool({
243
- name: "deposit_token",
244
- description:
245
- "Deposit tokens into the AgentEscrow contract for a specific task. The token must be in the format 'network:address'.",
246
- parameters: Type.Object({
247
- taskId: Type.String(),
248
- token: Type.String(),
249
- amount: Type.Optional(Type.String()),
250
- }),
251
- async execute(_id: string, params: any) {
252
- try {
253
- const result = await depositToken(params);
254
- return json(result);
255
- } catch (error: any) {
256
- return json({
257
- error: error instanceof Error ? error.message : String(error),
258
- });
259
- }
260
- },
261
- });
262
-
263
- api.registerTool({
264
- name: "fund_bounty_task",
265
- description:
266
- "Fund a draft bounty task, attaching token metadata to it. The token must be in the format 'network:address'.",
267
- parameters: Type.Object({
268
- taskId: Type.String(),
269
- token: Type.String(),
270
- }),
271
- async execute(_id: string, params: any) {
272
- try {
273
- const result = await fundBountyTask(params);
274
- return json(result);
275
- } catch (error: any) {
276
- return json({
277
- error: error instanceof Error ? error.message : String(error),
278
- });
279
- }
280
- },
281
- });
282
-
283
- api.registerTool({
284
- name: "decide_on_response",
285
- description: "Approve or reject a task response.",
286
- parameters: Type.Object({
287
- responseId: Type.String(),
288
- workerAddress: Type.String(),
289
- price: Type.String(),
290
- status: Type.Union([
291
- Type.Literal("approved"),
292
- Type.Literal("rejected"),
293
- ]),
294
- }),
295
- async execute(_id: string, params: any) {
296
- try {
297
- const result = await decideOnResponse(params);
298
- return json(result);
299
- } catch (error: any) {
300
- return json({
301
- error: error instanceof Error ? error.message : String(error),
302
- });
303
- }
304
- },
305
- });
306
-
307
- api.registerTool({
308
- name: "get_bounty_dashboard_url",
309
- description: "Get the bounty dashboard URL with a pre-authenticated token in the URL param",
310
- parameters: Type.Object({}),
311
- async execute(_id: string, _params: any) {
312
- try {
313
- const result = await openUpclawDashboard();
314
- return json(result);
315
- } catch (error: any) {
316
- return json({
317
- error: error instanceof Error ? error.message : String(error),
318
- });
319
- }
320
- },
321
- });
322
- }
package/src/signers.ts DELETED
@@ -1,36 +0,0 @@
1
- import { Account, Address, Hex, hexToBytes } from 'viem';
2
- import { privateKeyToAccount } from 'viem/accounts';
3
-
4
- export interface Signer {
5
- readonly address: Address;
6
- signMessage(message: string): Promise<Hex>;
7
- signDigest(digest: Hex): Promise<Hex>;
8
- }
9
-
10
- export class PrivateKeySigner implements Signer {
11
- private readonly account: Account;
12
-
13
- constructor(privateKey: Hex) {
14
- this.account = privateKeyToAccount(privateKey);
15
- }
16
-
17
- get address(): Address {
18
- return this.account.address;
19
- }
20
-
21
- signMessage(message: string): Promise<Hex> {
22
- const signMessageFn = this.account.signMessage;
23
- if (!signMessageFn) {
24
- throw new Error('Account does not support signMessage');
25
- }
26
- return signMessageFn({ message });
27
- }
28
-
29
- signDigest(digest: Hex): Promise<Hex> {
30
- const signMessageFn = this.account.signMessage;
31
- if (!signMessageFn) {
32
- throw new Error('Account does not support signDigest');
33
- }
34
- return signMessageFn({ message: { raw: hexToBytes(digest) } });
35
- }
36
- }
package/src/signing.ts DELETED
@@ -1,112 +0,0 @@
1
- import {
2
- CancelTaskPayload,
3
- CreateTaskPayload,
4
- DecisionPayload,
5
- FundTaskPayload,
6
- SettleTaskPayload,
7
- SubmitResponsePayload,
8
- TaskResponsesQueryPayload,
9
- WorkerResponsesQueryPayload
10
- } from './types.js';
11
-
12
- type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
13
-
14
- type CanonicalInput = JsonValue | Record<string, unknown> | undefined;
15
-
16
- const canonicalize = (value: CanonicalInput): JsonValue => {
17
- if (value === null || value === undefined) {
18
- return null;
19
- }
20
-
21
- if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
22
- return value;
23
- }
24
-
25
- if (Array.isArray(value)) {
26
- return value.map((item) => canonicalize(item)) as JsonValue;
27
- }
28
-
29
- const entries = Object.entries(value as Record<string, CanonicalInput>)
30
- .filter(([, v]) => v !== undefined)
31
- .sort(([a], [b]) => a.localeCompare(b));
32
- const normalized: Record<string, JsonValue> = {};
33
- for (const [key, val] of entries) {
34
- normalized[key] = canonicalize(val);
35
- }
36
- return normalized;
37
- };
38
-
39
- export const canonicalStringify = (payload: CanonicalInput): string =>
40
- JSON.stringify(canonicalize(payload));
41
-
42
- export const taskSignaturePayload = (input: CreateTaskPayload): string =>
43
- canonicalStringify({
44
- kind: 'task:create',
45
- title: input.title,
46
- content: input.content,
47
- id: input.id
48
- });
49
-
50
- export const taskFundSignaturePayload = (input: FundTaskPayload): string =>
51
- canonicalStringify({
52
- kind: 'task:fund',
53
- taskId: input.taskId,
54
- token: input.token
55
- });
56
-
57
- export const responseSignaturePayload = (input: SubmitResponsePayload): string =>
58
- canonicalStringify({
59
- kind: 'task:response',
60
- taskId: input.taskId,
61
- payload: input.payload,
62
- id: input.id ?? null
63
- });
64
-
65
- export const decisionSignaturePayload = (input: DecisionPayload): string =>
66
- canonicalStringify({
67
- kind: 'task:decision',
68
- responseId: input.responseId,
69
- workerAddress: input.workerAddress,
70
- price: input.price,
71
- status: input.status,
72
- settlementSignature: input.settlementSignature ?? null
73
- });
74
-
75
- export const cancelTaskSignaturePayload = (input: CancelTaskPayload): string =>
76
- canonicalStringify({
77
- kind: 'task:cancel',
78
- taskId: input.taskId
79
- });
80
-
81
- export const taskSettleSignaturePayload = (
82
- input: SettleTaskPayload & { workerAddress: string }
83
- ): string =>
84
- canonicalStringify({
85
- kind: 'task:settle',
86
- taskId: input.taskId,
87
- responseId: input.responseId,
88
- workerAddress: input.workerAddress
89
- });
90
-
91
- export const taskResponsesQuerySignaturePayload = (
92
- input: TaskResponsesQueryPayload & { ownerAddress: string }
93
- ): string =>
94
- canonicalStringify({
95
- kind: 'task:response:query',
96
- taskId: input.taskId,
97
- ownerAddress: input.ownerAddress,
98
- workerAddress: input.workerAddress ?? null,
99
- pageSize: input.pageSize ?? 50,
100
- pageNum: input.pageNum ?? 0
101
- });
102
-
103
- export const workerResponsesQuerySignaturePayload = (
104
- input: WorkerResponsesQueryPayload & { workerAddress: string }
105
- ): string =>
106
- canonicalStringify({
107
- kind: 'worker:response:query',
108
- workerAddress: input.workerAddress,
109
- taskId: input.taskId ?? null,
110
- pageSize: input.pageSize ?? 50,
111
- pageNum: input.pageNum ?? 0
112
- });
@@ -1,70 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- export const taskStatusSchema = z.enum(["finished", "draft", "active", "closed", "pending_review"]);
4
- export type TaskStatus = z.infer<typeof taskStatusSchema>;
5
-
6
- export const responseStatusSchema = z.enum(["pending", "approved", "rejected"]);
7
- export type ResponseStatus = z.infer<typeof responseStatusSchema>;
8
-
9
- export const taskRecordSchema = z.object({
10
- id: z.string().uuid(),
11
- title: z.string(),
12
- content: z.string(),
13
- owner: z.string(),
14
- created_at: z.number(),
15
- status: taskStatusSchema,
16
- price: z.string(),
17
- token: z.string().nullable(),
18
- withdraw_signature: z.string().nullable()
19
- });
20
- export type TaskRecord = z.infer<typeof taskRecordSchema>;
21
-
22
- export const responseRecordSchema = z.object({
23
- id: z.string().uuid(),
24
- task_id: z.string().uuid(),
25
- payload: z.string(),
26
- worker: z.string(),
27
- status: responseStatusSchema,
28
- created_at: z.number(),
29
- settlement: z.string().nullable(),
30
- settlement_signature: z.string().nullable()
31
- });
32
- export type ResponseRecord = z.infer<typeof responseRecordSchema>;
33
-
34
- export type NewTaskInput = Omit<TaskRecord, 'created_at' | 'status'> & {
35
- status?: TaskStatus;
36
- created_at?: number;
37
- };
38
-
39
- export type NewResponseInput = Omit<ResponseRecord, 'created_at' | 'status' | 'settlement'> & {
40
- status?: ResponseStatus;
41
- created_at?: number;
42
- settlement?: string | null;
43
- settlement_signature?: string | null;
44
- };
45
-
46
- export type TaskSortKey = 'price' | 'created_at';
47
-
48
- export interface TaskQueryFilters {
49
- publisher?: string | null;
50
- createdAfter?: number | null;
51
- createdBefore?: number | null;
52
- status?: TaskStatus | null;
53
- keyword?: string | null;
54
- minPrice?: number;
55
- sortBy?: TaskSortKey;
56
- pageSize?: number;
57
- pageNum?: number;
58
- }
59
-
60
- export interface TaskResponsesPageOptions {
61
- worker?: string;
62
- pageSize?: number;
63
- pageNum?: number;
64
- }
65
-
66
- export interface WorkerResponsesPageOptions {
67
- taskId?: string;
68
- pageSize?: number;
69
- pageNum?: number;
70
- }
package/src/token.ts DELETED
@@ -1,17 +0,0 @@
1
- import { getAddress } from 'viem';
2
-
3
- export interface ParsedTokenIdentifier {
4
- network: string;
5
- address: `0x${string}`;
6
- }
7
-
8
- export const parseTokenIdentifier = (token: string): ParsedTokenIdentifier => {
9
- const [networkRaw, address] = token.split(':');
10
- if (!networkRaw || !address) {
11
- throw new Error('Invalid token identifier');
12
- }
13
- return {
14
- network: networkRaw.toLowerCase(),
15
- address: getAddress(address as `0x${string}`)
16
- };
17
- };
package/src/types.ts DELETED
@@ -1,97 +0,0 @@
1
- import { z } from 'zod';
2
- import { responseStatusSchema, taskStatusSchema } from './task-db-types.js';
3
- import { getAddress, isAddress } from 'viem';
4
-
5
- const addressSchema = z
6
- .string()
7
- .refine((value) => isAddress(value as `0x${string}`), {
8
- message: 'Invalid address'
9
- })
10
- .transform((value) => getAddress(value as `0x${string}`));
11
-
12
- const tokenSchema = z.string().regex(/^[a-z0-9-]+:0x[a-fA-F0-9]{40}$/);
13
-
14
- const priceStringSchema = z.string().regex(/^[0-9]+$/).refine((val) => BigInt(val) > 0n, {
15
- message: 'price must be greater than zero'
16
- });
17
-
18
- const signatureSchema = z.string().regex(/^0x[a-fA-F0-9]{130}$/, { message: 'Invalid signature' });
19
-
20
- export const createTaskPayloadSchema = z.object({
21
- id: z.string().uuid(),
22
- title: z.string().min(4),
23
- content: z.string().min(1)
24
- });
25
-
26
- export const fundTaskPayloadSchema = z.object({
27
- taskId: z.string().uuid(),
28
- token: tokenSchema
29
- });
30
-
31
- export const submitResponsePayloadSchema = z.object({
32
- id: z.string().uuid().optional(),
33
- taskId: z.string().uuid(),
34
- payload: z.string().min(1)
35
- });
36
-
37
- export const decisionPayloadSchema = z
38
- .object({
39
- responseId: z.string().uuid(),
40
- workerAddress: addressSchema,
41
- price: z.string(),
42
- status: responseStatusSchema,
43
- settlementSignature: signatureSchema.optional()
44
- })
45
- .refine((value) => value.status !== 'pending', {
46
- message: 'Status must be approved or rejected'
47
- });
48
-
49
- export const cancelTaskPayloadSchema = z.object({
50
- taskId: z.string().uuid()
51
- });
52
-
53
- export const settleTaskPayloadSchema = z.object({
54
- taskId: z.string().uuid(),
55
- responseId: z.string().uuid()
56
- });
57
-
58
- const createdRangeSchema = z.tuple([z.number().nonnegative(), z.number().nonnegative()]).optional();
59
-
60
- export const taskQueryPayloadSchema = z.object({
61
- filter: z
62
- .object({
63
- publisher: addressSchema.optional(),
64
- created_at: createdRangeSchema,
65
- status: taskStatusSchema.optional(),
66
- minPrice: z.number().int().nonnegative().optional(),
67
- keyword: z.string().min(2).max(256).optional()
68
- })
69
- .default({}),
70
- sortBy: z.enum(['price', 'created_at']).optional().default('created_at'),
71
- pageSize: z.number().int().min(1).max(200).optional().default(50),
72
- pageNum: z.number().int().min(0).optional().default(0)
73
- });
74
-
75
- export const taskResponsesQueryPayloadSchema = z.object({
76
- taskId: z.string().uuid(),
77
- workerAddress: addressSchema.optional(),
78
- pageSize: z.number().int().min(1).max(200).optional().default(50),
79
- pageNum: z.number().int().min(0).optional().default(0)
80
- });
81
-
82
- export const workerResponsesQueryPayloadSchema = z.object({
83
- taskId: z.string().uuid().optional(),
84
- pageSize: z.number().int().min(1).max(200).optional().default(50),
85
- pageNum: z.number().int().min(0).optional().default(0)
86
- });
87
-
88
- export type AddressString = z.infer<typeof addressSchema>;
89
- export type CreateTaskPayload = z.infer<typeof createTaskPayloadSchema>;
90
- export type FundTaskPayload = z.infer<typeof fundTaskPayloadSchema>;
91
- export type SubmitResponsePayload = z.infer<typeof submitResponsePayloadSchema>;
92
- export type DecisionPayload = z.infer<typeof decisionPayloadSchema>;
93
- export type CancelTaskPayload = z.infer<typeof cancelTaskPayloadSchema>;
94
- export type SettleTaskPayload = z.infer<typeof settleTaskPayloadSchema>;
95
- export type TaskQueryPayload = z.infer<typeof taskQueryPayloadSchema>;
96
- export type TaskResponsesQueryPayload = z.infer<typeof taskResponsesQueryPayloadSchema>;
97
- export type WorkerResponsesQueryPayload = z.infer<typeof workerResponsesQueryPayloadSchema>;