@varun-ai07/covenant-mcp 1.3.2 → 2.0.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.
Files changed (214) hide show
  1. package/dist/abis/GrantProgram.json +778 -0
  2. package/dist/abis/RevisionManager.json +544 -0
  3. package/dist/abis/TrainingMarketplace.json +787 -0
  4. package/dist/cli.js +0 -0
  5. package/dist/config.d.ts +15 -1
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js +23 -3
  8. package/dist/config.js.map +1 -1
  9. package/dist/server.d.ts +3 -2
  10. package/dist/server.d.ts.map +1 -1
  11. package/dist/server.js +63 -63
  12. package/dist/server.js.map +1 -1
  13. package/dist/shared-types.js +8 -8
  14. package/dist/tools/{registry.d.ts → corven-agent.d.ts} +1 -1
  15. package/dist/tools/corven-agent.d.ts.map +1 -0
  16. package/dist/tools/corven-agent.js +90 -0
  17. package/dist/tools/corven-agent.js.map +1 -0
  18. package/dist/tools/corven-attest.d.ts +3 -0
  19. package/dist/tools/corven-attest.d.ts.map +1 -0
  20. package/dist/tools/corven-attest.js +87 -0
  21. package/dist/tools/corven-attest.js.map +1 -0
  22. package/dist/tools/{batches.d.ts → corven-batch.d.ts} +1 -1
  23. package/dist/tools/corven-batch.d.ts.map +1 -0
  24. package/dist/tools/corven-batch.js +116 -0
  25. package/dist/tools/corven-batch.js.map +1 -0
  26. package/dist/tools/{bounties.d.ts → corven-bounty.d.ts} +1 -1
  27. package/dist/tools/corven-bounty.d.ts.map +1 -0
  28. package/dist/tools/corven-bounty.js +72 -0
  29. package/dist/tools/corven-bounty.js.map +1 -0
  30. package/dist/tools/{collectives.d.ts → corven-collective.d.ts} +1 -1
  31. package/dist/tools/corven-collective.d.ts.map +1 -0
  32. package/dist/tools/corven-collective.js +105 -0
  33. package/dist/tools/corven-collective.js.map +1 -0
  34. package/dist/tools/corven-fiat.d.ts +3 -0
  35. package/dist/tools/corven-fiat.d.ts.map +1 -0
  36. package/dist/tools/corven-fiat.js +42 -0
  37. package/dist/tools/corven-fiat.js.map +1 -0
  38. package/dist/tools/corven-govern.d.ts +3 -0
  39. package/dist/tools/corven-govern.d.ts.map +1 -0
  40. package/dist/tools/corven-govern.js +65 -0
  41. package/dist/tools/corven-govern.js.map +1 -0
  42. package/dist/tools/{grants.d.ts → corven-grants.d.ts} +1 -1
  43. package/dist/tools/corven-grants.d.ts.map +1 -0
  44. package/dist/tools/corven-grants.js +67 -0
  45. package/dist/tools/corven-grants.js.map +1 -0
  46. package/dist/tools/{insurance.d.ts → corven-insurance.d.ts} +1 -1
  47. package/dist/tools/corven-insurance.d.ts.map +1 -0
  48. package/dist/tools/corven-insurance.js +103 -0
  49. package/dist/tools/corven-insurance.js.map +1 -0
  50. package/dist/tools/{market.d.ts → corven-market.d.ts} +1 -1
  51. package/dist/tools/corven-market.d.ts.map +1 -0
  52. package/dist/tools/corven-market.js +113 -0
  53. package/dist/tools/corven-market.js.map +1 -0
  54. package/dist/tools/corven-match.d.ts +3 -0
  55. package/dist/tools/corven-match.d.ts.map +1 -0
  56. package/dist/tools/corven-match.js +235 -0
  57. package/dist/tools/corven-match.js.map +1 -0
  58. package/dist/tools/corven-message.d.ts +3 -0
  59. package/dist/tools/corven-message.d.ts.map +1 -0
  60. package/dist/tools/corven-message.js +82 -0
  61. package/dist/tools/corven-message.js.map +1 -0
  62. package/dist/tools/corven-multi.d.ts +3 -0
  63. package/dist/tools/corven-multi.d.ts.map +1 -0
  64. package/dist/tools/corven-multi.js +197 -0
  65. package/dist/tools/corven-multi.js.map +1 -0
  66. package/dist/tools/corven-reputation.d.ts +3 -0
  67. package/dist/tools/corven-reputation.d.ts.map +1 -0
  68. package/dist/tools/corven-reputation.js +126 -0
  69. package/dist/tools/corven-reputation.js.map +1 -0
  70. package/dist/tools/{revisions.d.ts → corven-revision.d.ts} +1 -1
  71. package/dist/tools/corven-revision.d.ts.map +1 -0
  72. package/dist/tools/corven-revision.js +73 -0
  73. package/dist/tools/corven-revision.js.map +1 -0
  74. package/dist/tools/{router.d.ts → corven-router.d.ts} +1 -1
  75. package/dist/tools/corven-router.d.ts.map +1 -0
  76. package/dist/tools/corven-router.js +116 -0
  77. package/dist/tools/corven-router.js.map +1 -0
  78. package/dist/tools/corven-stats.d.ts +3 -0
  79. package/dist/tools/corven-stats.d.ts.map +1 -0
  80. package/dist/tools/corven-stats.js +97 -0
  81. package/dist/tools/corven-stats.js.map +1 -0
  82. package/dist/tools/corven-stream.d.ts +3 -0
  83. package/dist/tools/corven-stream.d.ts.map +1 -0
  84. package/dist/tools/corven-stream.js +197 -0
  85. package/dist/tools/corven-stream.js.map +1 -0
  86. package/dist/tools/corven-task.d.ts +3 -0
  87. package/dist/tools/corven-task.d.ts.map +1 -0
  88. package/dist/tools/corven-task.js +105 -0
  89. package/dist/tools/corven-task.js.map +1 -0
  90. package/dist/tools/{training.d.ts → corven-training.d.ts} +1 -1
  91. package/dist/tools/corven-training.d.ts.map +1 -0
  92. package/dist/tools/corven-training.js +73 -0
  93. package/dist/tools/corven-training.js.map +1 -0
  94. package/dist/tools/corven-verify.d.ts +3 -0
  95. package/dist/tools/corven-verify.d.ts.map +1 -0
  96. package/dist/tools/corven-verify.js +149 -0
  97. package/dist/tools/corven-verify.js.map +1 -0
  98. package/dist/tools/corven-wallet.d.ts +3 -0
  99. package/dist/tools/corven-wallet.d.ts.map +1 -0
  100. package/dist/tools/corven-wallet.js +235 -0
  101. package/dist/tools/corven-wallet.js.map +1 -0
  102. package/dist/tools/covenant-help.d.ts.map +1 -1
  103. package/dist/tools/covenant-help.js +88 -312
  104. package/dist/tools/covenant-help.js.map +1 -1
  105. package/dist/tools/disputes.d.ts.map +1 -1
  106. package/dist/tools/disputes.js +26 -0
  107. package/dist/tools/disputes.js.map +1 -1
  108. package/dist/types.d.ts +2 -0
  109. package/dist/types.d.ts.map +1 -1
  110. package/package.json +1 -1
  111. package/dist/abis/v2/DisputeResolution.json +0 -493
  112. package/dist/abis/v2/InsurancePool.json +0 -645
  113. package/dist/tools/account-abstraction.d.ts +0 -3
  114. package/dist/tools/account-abstraction.d.ts.map +0 -1
  115. package/dist/tools/account-abstraction.js +0 -364
  116. package/dist/tools/account-abstraction.js.map +0 -1
  117. package/dist/tools/batches.d.ts.map +0 -1
  118. package/dist/tools/batches.js +0 -231
  119. package/dist/tools/batches.js.map +0 -1
  120. package/dist/tools/bounties.d.ts.map +0 -1
  121. package/dist/tools/bounties.js +0 -304
  122. package/dist/tools/bounties.js.map +0 -1
  123. package/dist/tools/bridge.d.ts +0 -3
  124. package/dist/tools/bridge.d.ts.map +0 -1
  125. package/dist/tools/bridge.js +0 -190
  126. package/dist/tools/bridge.js.map +0 -1
  127. package/dist/tools/collectives.d.ts.map +0 -1
  128. package/dist/tools/collectives.js +0 -249
  129. package/dist/tools/collectives.js.map +0 -1
  130. package/dist/tools/cross-chain.d.ts +0 -3
  131. package/dist/tools/cross-chain.d.ts.map +0 -1
  132. package/dist/tools/cross-chain.js +0 -77
  133. package/dist/tools/cross-chain.js.map +0 -1
  134. package/dist/tools/escrow.d.ts +0 -3
  135. package/dist/tools/escrow.d.ts.map +0 -1
  136. package/dist/tools/escrow.js +0 -606
  137. package/dist/tools/escrow.js.map +0 -1
  138. package/dist/tools/fiat-onramp.d.ts +0 -3
  139. package/dist/tools/fiat-onramp.d.ts.map +0 -1
  140. package/dist/tools/fiat-onramp.js +0 -108
  141. package/dist/tools/fiat-onramp.js.map +0 -1
  142. package/dist/tools/governance.d.ts +0 -3
  143. package/dist/tools/governance.d.ts.map +0 -1
  144. package/dist/tools/governance.js +0 -271
  145. package/dist/tools/governance.js.map +0 -1
  146. package/dist/tools/grants.d.ts.map +0 -1
  147. package/dist/tools/grants.js +0 -269
  148. package/dist/tools/grants.js.map +0 -1
  149. package/dist/tools/insurance.d.ts.map +0 -1
  150. package/dist/tools/insurance.js +0 -271
  151. package/dist/tools/insurance.js.map +0 -1
  152. package/dist/tools/market.d.ts.map +0 -1
  153. package/dist/tools/market.js +0 -393
  154. package/dist/tools/market.js.map +0 -1
  155. package/dist/tools/matching.d.ts +0 -3
  156. package/dist/tools/matching.d.ts.map +0 -1
  157. package/dist/tools/matching.js +0 -233
  158. package/dist/tools/matching.js.map +0 -1
  159. package/dist/tools/messaging.d.ts +0 -3
  160. package/dist/tools/messaging.d.ts.map +0 -1
  161. package/dist/tools/messaging.js +0 -159
  162. package/dist/tools/messaging.js.map +0 -1
  163. package/dist/tools/multi-token.d.ts +0 -3
  164. package/dist/tools/multi-token.d.ts.map +0 -1
  165. package/dist/tools/multi-token.js +0 -274
  166. package/dist/tools/multi-token.js.map +0 -1
  167. package/dist/tools/offchain-coordinator.d.ts +0 -3
  168. package/dist/tools/offchain-coordinator.d.ts.map +0 -1
  169. package/dist/tools/offchain-coordinator.js +0 -436
  170. package/dist/tools/offchain-coordinator.js.map +0 -1
  171. package/dist/tools/protocol.d.ts +0 -3
  172. package/dist/tools/protocol.d.ts.map +0 -1
  173. package/dist/tools/protocol.js +0 -127
  174. package/dist/tools/protocol.js.map +0 -1
  175. package/dist/tools/receipts.d.ts +0 -3
  176. package/dist/tools/receipts.d.ts.map +0 -1
  177. package/dist/tools/receipts.js +0 -147
  178. package/dist/tools/receipts.js.map +0 -1
  179. package/dist/tools/registry.d.ts.map +0 -1
  180. package/dist/tools/registry.js +0 -245
  181. package/dist/tools/registry.js.map +0 -1
  182. package/dist/tools/reputation-vc.d.ts +0 -3
  183. package/dist/tools/reputation-vc.d.ts.map +0 -1
  184. package/dist/tools/reputation-vc.js +0 -438
  185. package/dist/tools/reputation-vc.js.map +0 -1
  186. package/dist/tools/revisions.d.ts.map +0 -1
  187. package/dist/tools/revisions.js +0 -108
  188. package/dist/tools/revisions.js.map +0 -1
  189. package/dist/tools/router.d.ts.map +0 -1
  190. package/dist/tools/router.js +0 -104
  191. package/dist/tools/router.js.map +0 -1
  192. package/dist/tools/streaming.d.ts +0 -3
  193. package/dist/tools/streaming.d.ts.map +0 -1
  194. package/dist/tools/streaming.js +0 -350
  195. package/dist/tools/streaming.js.map +0 -1
  196. package/dist/tools/templates.d.ts +0 -3
  197. package/dist/tools/templates.d.ts.map +0 -1
  198. package/dist/tools/templates.js +0 -392
  199. package/dist/tools/templates.js.map +0 -1
  200. package/dist/tools/training.d.ts.map +0 -1
  201. package/dist/tools/training.js +0 -304
  202. package/dist/tools/training.js.map +0 -1
  203. package/dist/tools/v2-settlement.d.ts +0 -3
  204. package/dist/tools/v2-settlement.d.ts.map +0 -1
  205. package/dist/tools/v2-settlement.js +0 -226
  206. package/dist/tools/v2-settlement.js.map +0 -1
  207. package/dist/tools/verification.d.ts +0 -3
  208. package/dist/tools/verification.d.ts.map +0 -1
  209. package/dist/tools/verification.js +0 -215
  210. package/dist/tools/verification.js.map +0 -1
  211. package/dist/tools/verify-deep.d.ts +0 -3
  212. package/dist/tools/verify-deep.d.ts.map +0 -1
  213. package/dist/tools/verify-deep.js +0 -125
  214. package/dist/tools/verify-deep.js.map +0 -1
@@ -1,606 +0,0 @@
1
- /**
2
- * TaskEscrow MCP Tools
3
- *
4
- * create_task — Create and fund a task (one-shot)
5
- * get_task — Read task details by ID
6
- * submit_work — Submit deliverable hash for a task
7
- * verify_task — Mark a submitted task as verified / completed
8
- * dispute_task — Open a dispute on a task
9
- */
10
- import { z } from "zod";
11
- import { parseEther, formatEther, isAddress } from "viem";
12
- import { loadAbi, CONTRACTS, getAccount } from "../config.js";
13
- import { executeOrPrepare, readContract } from "../handlers/wallet.js";
14
- import { formatTxResult, formatReadResult } from "../handlers/transactions.js";
15
- import { formatSuccess, formatStructuredError, parseContractError } from "../lib/formatResponse.js";
16
- import { ethAddress, ethAmount, ipfsCid, unixDeadline, taskId as taskIdSchema, priority as prioritySchema } from "../lib/schemaHelpers.js";
17
- import { TASK_STATUS } from "../types.js";
18
- const ABI = loadAbi("TaskEscrow");
19
- // Input validation schemas
20
- const createTaskSchema = z.object({
21
- worker: z.string().refine(isAddress, { message: "Invalid worker Ethereum address" }),
22
- payment: z.string().regex(/^\d+\.\d{1,18}$/, "Invalid ETH amount format")
23
- .refine(val => {
24
- const paymentAmount = parseFloat(val);
25
- return paymentAmount >= 0.001 && paymentAmount <= 1000; // Allow up to 1000 ETH for tasks
26
- }, { message: "Payment must be between 0.001 and 1000 ETH" }),
27
- deadline: z.number().int().positive()
28
- .refine(val => {
29
- const deadlineTimestamp = val * 1000; // Convert to milliseconds
30
- const now = Date.now();
31
- const oneYearFromNow = now + (365 * 24 * 60 * 60 * 1000); // 1 year max
32
- return deadlineTimestamp > now && deadlineTimestamp < oneYearFromNow;
33
- }, { message: "Deadline must be a future timestamp within 1 year" }),
34
- descriptionHash: z.string().min(1).max(100), // Basic IPFS CID validation
35
- priority: z.number().int().min(0).max(3).optional().default(1) // 0-3 for priority levels
36
- });
37
- const getTaskSchema = z.object({
38
- taskId: z.number().int().positive()
39
- });
40
- const submitWorkSchema = z.object({
41
- taskId: z.number().int().positive(),
42
- deliverableHash: z.string().min(1).max(100) // Basic IPFS CID validation
43
- });
44
- const verifyTaskSchema = z.object({
45
- taskId: z.number().int().positive(),
46
- success: z.boolean() // Whether task passed verification
47
- });
48
- const disputeTaskSchema = z.object({
49
- taskId: z.number().int().positive(),
50
- reason: z.string().max(500).optional() // Limit reason length
51
- });
52
- export function registerEscrowTools(server) {
53
- // ──────────────────────────────────────────────────────────────
54
- // create_task
55
- // ──────────────────────────────────────────────────────────────
56
- server.registerTool("corven_create_task", {
57
- title: "Create & Fund Task",
58
- description: "Creates a direct-hire task and locks payment in escrow in a single transaction.\n" +
59
- "USE WHEN: You have a specific worker address and want to hire them directly. Use corven_post_open_task if you want competitive bidding instead.\n" +
60
- "REQUIRES: Both client AND worker must be registered with corven_register_agent. Client wallet needs payment amount plus ~0.0003 ETH gas.\n" +
61
- "RETURNS: taskId (save this for all subsequent calls), escrow status, deadline, worker address, Basescan link.\n" +
62
- "COMES AFTER: corven_find_workers to get the worker address.\n" +
63
- "COMES BEFORE: Worker calls corven_submit_work. Client calls corven_verify_task.\n" +
64
- "NOTE: Payment is locked — neither party can access it until verification completes.",
65
- inputSchema: {
66
- worker: ethAddress,
67
- payment: ethAmount,
68
- deadline: unixDeadline,
69
- descriptionHash: ipfsCid,
70
- priority: prioritySchema,
71
- },
72
- }, async ({ worker, payment, deadline, descriptionHash, priority }) => {
73
- try {
74
- const validationResult = createTaskSchema.safeParse({ worker, payment, deadline, descriptionHash, priority });
75
- if (!validationResult.success) {
76
- return formatStructuredError("Invalid task parameters.", validationResult.error.issues.map((e) => e.message).join(", "), "Check: worker must be full 42-char 0x address, payment decimal ETH string (0.001-1000), deadline future Unix timestamp, descriptionHash valid IPFS CID.", true);
77
- }
78
- const account = getAccount();
79
- if (!account) {
80
- return formatStructuredError("No private key configured.", "PRIVATE_KEY environment variable is not set.", "Set PRIVATE_KEY in your .env file.", false);
81
- }
82
- const paymentWei = parseEther(payment);
83
- const priorityLevel = priority ?? 1;
84
- // Calculate total value: payment + protocol fee (1%) + priority fee
85
- const PROTOCOL_FEE_BPS = 100n; // 1%
86
- const PRIORITY_FEES = [50n, 100n, 200n, 500n]; // Low, Medium, High, Urgent
87
- const priorityFeeBps = PRIORITY_FEES[priorityLevel] ?? 100n;
88
- const totalFeeBps = PROTOCOL_FEE_BPS + priorityFeeBps;
89
- const feeAmount = paymentWei * totalFeeBps / 10000n;
90
- const totalValue = paymentWei + feeAmount;
91
- const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "createAndFundTask", [
92
- worker,
93
- paymentWei,
94
- BigInt(deadline),
95
- descriptionHash,
96
- ], totalValue);
97
- if (result.status === "success") {
98
- const deadlineDate = new Date(deadline * 1000).toUTCString();
99
- return formatSuccess(`Task created. ${payment} ETH locked in escrow for worker.`, {
100
- worker,
101
- client: account,
102
- payment: `${payment} ETH`,
103
- deadline: deadlineDate,
104
- specificationIpfs: descriptionHash,
105
- status: "Funded",
106
- priority: priorityLevel,
107
- }, result.txHash, [
108
- "Wait for worker to call corven_submit_work with your taskId.",
109
- "Then call corven_verify_task to release payment after reviewing work.",
110
- "Check status anytime with corven_get_task.",
111
- ]);
112
- }
113
- return formatTxResult(result);
114
- }
115
- catch (e) {
116
- const parsed = parseContractError(e);
117
- return formatStructuredError(parsed.error, parsed.cause, parsed.fix, parsed.retryable);
118
- }
119
- });
120
- // ──────────────────────────────────────────────────────────────
121
- // get_task
122
- // ──────────────────────────────────────────────────────────────
123
- server.registerTool("corven_get_task", {
124
- title: "Get Task Details",
125
- description: "Returns complete details for any task including current lifecycle status.\n" +
126
- "USE WHEN: Checking if a worker has submitted work. Confirming payment released. Getting deliverable IPFS hash. Checking deadline.\n" +
127
- "REQUIRES: Nothing. Free read-only call.\n" +
128
- "RETURNS: Status, client/worker addresses, payment, deadline, specification hash, deliverable hash.\n" +
129
- "STATUS MEANINGS: Funded=worker can begin. InProgress=worker acknowledged. Submitted=work ready for review. Completed=paid. Failed=rejected or expired.",
130
- inputSchema: {
131
- taskId: taskIdSchema,
132
- },
133
- }, async ({ taskId }) => {
134
- try {
135
- const validationResult = getTaskSchema.safeParse({ taskId });
136
- if (!validationResult.success) {
137
- return formatStructuredError("Invalid task ID.", `Received '${taskId}' — must be a positive integer.`, "Pass the numeric taskId returned by corven_create_task. Find your task IDs with corven_get_client_tasks or corven_get_worker_tasks.", false);
138
- }
139
- const validatedTaskId = validationResult.data.taskId;
140
- const data = await readContract(CONTRACTS.TaskEscrow, ABI, "getTask", [BigInt(validatedTaskId)]);
141
- const enriched = {
142
- ...data,
143
- statusLabel: TASK_STATUS[data.status] ?? `Unknown(${data.status})`,
144
- paymentEth: formatEther(data.payment),
145
- };
146
- return formatReadResult(enriched, `Task #${taskId}`);
147
- }
148
- catch (e) {
149
- const parsed = parseContractError(e);
150
- return formatStructuredError(parsed.error, parsed.cause, parsed.fix, parsed.retryable);
151
- }
152
- });
153
- // ──────────────────────────────────────────────────────────────
154
- // submit_work
155
- // ──────────────────────────────────────────────────────────────
156
- server.registerTool("corven_submit_work", {
157
- title: "Submit Work Deliverable",
158
- description: "Worker submits completed deliverable IPFS hash on-chain. Commits work permanently and notifies the client.\n" +
159
- "USE WHEN: You are the worker and have finished executing the task. Upload deliverable to IPFS first. Then call this with the CID.\n" +
160
- "REQUIRES: You must be the assigned worker. Task status must be Funded or InProgress. Deadline must not have passed.\n" +
161
- "RETURNS: Submission confirmation, IPFS hash recorded on-chain, next action for the client.\n" +
162
- "COMES AFTER: Worker executes task off-chain and uploads deliverable to IPFS.\n" +
163
- "COMES BEFORE: Client calls corven_verify_task.",
164
- inputSchema: {
165
- taskId: taskIdSchema,
166
- deliverableHash: ipfsCid,
167
- },
168
- }, async ({ taskId, deliverableHash }) => {
169
- try {
170
- const validationResult = submitWorkSchema.safeParse({ taskId, deliverableHash });
171
- if (!validationResult.success) {
172
- return formatStructuredError("Invalid parameters.", validationResult.error.issues.map((e) => e.message).join(", "), "taskId must be a positive integer. deliverableHash must be a valid IPFS CID (Qm... or bafy...).", true);
173
- }
174
- const account = getAccount();
175
- if (!account) {
176
- return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
177
- }
178
- const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "submitWork", [BigInt(taskId), deliverableHash]);
179
- if (result.status === "success") {
180
- return formatSuccess(`Work submitted for Task #${taskId}. Client will be notified.`, { taskId, deliverableHash, status: "Submitted" }, result.txHash, ["Wait for client to review and call corven_verify_task.", "Payment releases automatically on approval."]);
181
- }
182
- return formatTxResult(result);
183
- }
184
- catch (e) {
185
- const parsed = parseContractError(e);
186
- return formatStructuredError(parsed.error, parsed.cause, parsed.fix, parsed.retryable);
187
- }
188
- });
189
- // ──────────────────────────────────────────────────────────────
190
- // verify_task
191
- // ──────────────────────────────────────────────────────────────
192
- server.registerTool("corven_verify_task", {
193
- title: "Verify & Approve Task",
194
- description: "Client approves submitted work. Triggers automatic payment release. Worker receives ETH. Reputation updates. ERC-8004 receipt created.\n" +
195
- "USE WHEN: You are the client. Worker has submitted work (corven_get_task shows status Submitted). You have reviewed and approve.\n" +
196
- "REQUIRES: You must be the client. Task status must be Submitted. Call corven_dispute_task instead to reject.\n" +
197
- "RETURNS: Payment release confirmation, new reputation scores for both agents, receipt ID, Basescan link.\n" +
198
- "COMES AFTER: corven_submit_work by the worker.\n" +
199
- "NOTE: This is the final step. Payment releases automatically — no manual transfer needed.",
200
- inputSchema: {
201
- taskId: taskIdSchema,
202
- success: z.boolean().describe("true = approve work and release payment, false = reject work and refund client"),
203
- },
204
- }, async ({ taskId, success }) => {
205
- try {
206
- const validationResult = verifyTaskSchema.safeParse({ taskId, success });
207
- if (!validationResult.success) {
208
- return formatStructuredError("Invalid parameters.", validationResult.error.issues.map((e) => e.message).join(", "), "taskId must be positive integer. success must be boolean.", true);
209
- }
210
- const account = getAccount();
211
- if (!account) {
212
- return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
213
- }
214
- const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "verifyTask", [BigInt(taskId), success], undefined, "TaskEscrow");
215
- if (result.status === "success") {
216
- return formatSuccess(success
217
- ? `Task #${taskId} approved. Payment released to worker automatically.`
218
- : `Task #${taskId} rejected. Payment refunded to client.`, { taskId, verdict: success ? "APPROVED" : "REJECTED" }, result.txHash, success
219
- ? ["Payment has been transferred to the worker's wallet.", "Both agents' reputation scores have been updated.", "An ERC-8004 receipt has been created as permanent proof."]
220
- : ["Payment has been refunded to your wallet.", "Worker's reputation has been penalized."]);
221
- }
222
- return formatTxResult(result);
223
- }
224
- catch (e) {
225
- const parsed = parseContractError(e);
226
- return formatStructuredError(parsed.error, parsed.cause, parsed.fix, parsed.retryable);
227
- }
228
- });
229
- // ──────────────────────────────────────────────────────────────
230
- // dispute_task
231
- // ──────────────────────────────────────────────────────────────
232
- server.registerTool("corven_dispute_task", {
233
- title: "Dispute a Task",
234
- description: "Freezes a task and initiates jury-based dispute resolution. Three randomly-selected agents vote. Majority decides.\n" +
235
- "USE WHEN: You are the client and submitted work clearly fails the specification. Or you are the worker and were unfairly rejected.\n" +
236
- "REQUIRES: Task status must be Submitted. Either client or worker can call.\n" +
237
- "RETURNS: Dispute ID, jury selection confirmation, voting deadline.\n" +
238
- "NOTE: Call corven_get_task first to confirm the task is in Submitted status.",
239
- inputSchema: {
240
- taskId: taskIdSchema,
241
- reason: z.string().max(500).optional().describe("Optional reason for the dispute"),
242
- },
243
- }, async ({ taskId, reason }) => {
244
- try {
245
- const account = getAccount();
246
- if (!account) {
247
- return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
248
- }
249
- const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "disputeTask", [BigInt(taskId)], undefined, "TaskEscrow");
250
- if (result.status === "success") {
251
- return formatSuccess(`Task #${taskId} disputed. Payment frozen pending jury resolution.`, { taskId, reason: reason || "No reason provided", status: "Disputed" }, result.txHash, ["Three jurors will be randomly selected to vote.", "Resolution typically takes 24-48 hours."]);
252
- }
253
- return formatTxResult(result);
254
- }
255
- catch (e) {
256
- const parsed = parseContractError(e);
257
- return formatStructuredError(parsed.error, parsed.cause, parsed.fix, parsed.retryable);
258
- }
259
- });
260
- // ──────────────────────────────────────────────────────────────
261
- // corven_create_task_with_priority
262
- // ──────────────────────────────────────────────────────────────
263
- server.registerTool("corven_create_task_with_priority", {
264
- title: "Create Task with Priority",
265
- description: "Creates a direct-hire task with a specific priority level and locks payment in escrow.\n" +
266
- "USE WHEN: You have a specific worker and need urgent or high-priority execution with guaranteed faster attention.\n" +
267
- "REQUIRES: Both client AND worker registered. Client wallet needs payment + priority fee + ~0.0003 ETH gas.\n" +
268
- "RETURNS: taskId, escrow status, priority level, total cost breakdown, Basescan link.\n" +
269
- "COMES AFTER: corven_find_workers to get the worker address.\n" +
270
- "COMES BEFORE: Worker calls corven_submit_work. Client calls corven_verify_task.\n" +
271
- "NOTE: Priority fees — Low: 0.5%, Medium: 1%, High: 2%, Urgent: 5%. Use corven_create_task for default Medium priority.",
272
- inputSchema: {
273
- worker: ethAddress,
274
- payment: ethAmount,
275
- deadline: unixDeadline,
276
- descriptionHash: ipfsCid,
277
- priority: prioritySchema,
278
- },
279
- }, async ({ worker, payment, deadline, descriptionHash, priority }) => {
280
- try {
281
- const account = getAccount();
282
- if (!account)
283
- return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
284
- const paymentWei = parseEther(payment);
285
- // Calculate total value: payment + protocol fee (1%) + priority fee
286
- const PROTOCOL_FEE_BPS = 100n;
287
- const PRIORITY_FEES = [50n, 100n, 200n, 500n];
288
- const priorityFeeBps = PRIORITY_FEES[priority] ?? 100n;
289
- const totalFeeBps = PROTOCOL_FEE_BPS + priorityFeeBps;
290
- const feeAmount = paymentWei * totalFeeBps / 10000n;
291
- const totalValue = paymentWei + feeAmount;
292
- const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "createAndFundTaskWithPriority", [worker, paymentWei, BigInt(deadline), descriptionHash, priority], totalValue);
293
- return formatTxResult(result);
294
- }
295
- catch (e) {
296
- const p = parseContractError(e);
297
- return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
298
- }
299
- });
300
- // ──────────────────────────────────────────────────────────────
301
- // corven_create_milestone_task
302
- // ──────────────────────────────────────────────────────────────
303
- server.registerTool("corven_create_milestone_task", {
304
- title: "Create Milestone Task",
305
- description: "Creates a task with incremental milestone-based payments. Each milestone is verified and paid independently.\n" +
306
- "USE WHEN: Task has distinct phases (e.g., research, draft, final). You want partial payment tied to checkpoints.\n" +
307
- "REQUIRES: Both client AND worker registered. Client wallet needs totalPayment + ~2% fees + ~0.0003 ETH gas.\n" +
308
- "RETURNS: taskId, milestone count, individual milestone amounts, total escrowed, Basescan link.\n" +
309
- "COMES AFTER: corven_find_workers to get the worker address.\n" +
310
- "COMES BEFORE: Worker calls corven_submit_milestone per milestone. Client calls corven_verify_milestone to release each payment.\n" +
311
- "NOTE: totalPayment must equal the sum of milestonePayments. Milestones are verified in order (0, 1, 2, ...).",
312
- inputSchema: {
313
- worker: ethAddress,
314
- totalPayment: ethAmount,
315
- deadline: unixDeadline,
316
- descriptionHash: ipfsCid,
317
- milestoneDescriptions: z.array(z.string()).describe("Array of milestone descriptions"),
318
- milestonePayments: z.array(z.string()).describe("Array of milestone payments in ETH"),
319
- },
320
- }, async ({ worker, totalPayment, deadline, descriptionHash, milestoneDescriptions, milestonePayments }) => {
321
- try {
322
- const account = getAccount();
323
- if (!account)
324
- return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
325
- const paymentWei = parseEther(totalPayment);
326
- const payments = milestonePayments.map(p => parseEther(p));
327
- // Calculate total value: payment + protocol fee (1%) + priority fee (default Medium=1%)
328
- const PROTOCOL_FEE_BPS = 100n;
329
- const PRIORITY_FEE_BPS = 100n; // Default Medium priority
330
- const totalFeeBps = PROTOCOL_FEE_BPS + PRIORITY_FEE_BPS;
331
- const feeAmount = paymentWei * totalFeeBps / 10000n;
332
- const totalValue = paymentWei + feeAmount;
333
- const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "createTaskWithMilestones", [worker, paymentWei, BigInt(deadline), descriptionHash, milestoneDescriptions, payments], totalValue);
334
- return formatTxResult(result);
335
- }
336
- catch (e) {
337
- const p = parseContractError(e);
338
- return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
339
- }
340
- });
341
- // ──────────────────────────────────────────────────────────────
342
- // corven_submit_milestone
343
- // ──────────────────────────────────────────────────────────────
344
- server.registerTool("corven_submit_milestone", {
345
- title: "Submit Milestone",
346
- description: "Worker submits a deliverable for a specific milestone in a milestone-based task.\n" +
347
- "USE WHEN: You are the worker and have completed one milestone phase. Upload deliverable to IPFS first.\n" +
348
- "REQUIRES: You must be the assigned worker. Milestone must not already be submitted. Task status must be Funded or InProgress.\n" +
349
- "RETURNS: Submission confirmation, milestone index, IPFS hash recorded on-chain.\n" +
350
- "COMES AFTER: corven_create_milestone_task created the task with milestones.\n" +
351
- "COMES BEFORE: Client calls corven_verify_milestone to approve and release payment for this milestone.\n" +
352
- "NOTE: Milestones must be submitted in order. You cannot skip ahead.",
353
- inputSchema: {
354
- taskId: taskIdSchema,
355
- milestoneIndex: z.number().describe("Milestone index (0-based)"),
356
- deliverableHash: ipfsCid,
357
- },
358
- }, async ({ taskId, milestoneIndex, deliverableHash }) => {
359
- try {
360
- const account = getAccount();
361
- if (!account)
362
- return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
363
- const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "submitMilestone", [BigInt(taskId), BigInt(milestoneIndex), deliverableHash]);
364
- return formatTxResult(result);
365
- }
366
- catch (e) {
367
- const p = parseContractError(e);
368
- return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
369
- }
370
- });
371
- // ──────────────────────────────────────────────────────────────
372
- // corven_verify_milestone
373
- // ──────────────────────────────────────────────────────────────
374
- server.registerTool("corven_verify_milestone", {
375
- title: "Verify Milestone",
376
- description: "Client verifies a submitted milestone and releases its payment to the worker.\n" +
377
- "USE WHEN: You are the client. Worker has submitted a milestone (corven_get_milestone shows submitted). You have reviewed and approve.\n" +
378
- "REQUIRES: You must be the client. Milestone must have been submitted by the worker.\n" +
379
- "RETURNS: Verification result, payment released amount, milestone status update.\n" +
380
- "COMES AFTER: corven_submit_milestone by the worker.\n" +
381
- "COMES BEFORE: Next milestone submission, or task completion if this was the final milestone.\n" +
382
- "NOTE: Rejecting a milestone does not refund the entire task — only that milestone's payment is withheld.",
383
- inputSchema: {
384
- taskId: taskIdSchema,
385
- milestoneIndex: z.number().describe("Milestone index (0-based)"),
386
- success: z.boolean().describe("Whether the milestone passes verification"),
387
- },
388
- }, async ({ taskId, milestoneIndex, success }) => {
389
- try {
390
- const account = getAccount();
391
- if (!account)
392
- return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
393
- const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "verifyMilestone", [BigInt(taskId), BigInt(milestoneIndex), success], undefined, "TaskEscrow");
394
- return formatTxResult(result);
395
- }
396
- catch (e) {
397
- const p = parseContractError(e);
398
- return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
399
- }
400
- });
401
- // ──────────────────────────────────────────────────────────────
402
- // corven_get_milestone (includes count when no index provided)
403
- // ──────────────────────────────────────────────────────────────
404
- server.registerTool("corven_get_milestone", {
405
- title: "Get Milestone",
406
- description: "Reads milestone details by index, or returns the total milestone count if no index is provided.\n" +
407
- "USE WHEN: Checking which milestones have been submitted or verified. Counting milestones before submission.\n" +
408
- "REQUIRES: Nothing. Free read-only call.\n" +
409
- "RETURNS: Milestone description, payment amount, submission hash, verification status — or milestone count if index omitted.\n" +
410
- "COMES AFTER: corven_create_milestone_task created the task.\n" +
411
- "COMES BEFORE: corven_submit_milestone or corven_verify_milestone.",
412
- inputSchema: {
413
- taskId: taskIdSchema,
414
- milestoneIndex: z.number().optional().describe("Milestone index (0-based). Omit to get count."),
415
- },
416
- }, async ({ taskId, milestoneIndex }) => {
417
- try {
418
- if (milestoneIndex === undefined) {
419
- const count = await readContract(CONTRACTS.TaskEscrow, ABI, "getMilestoneCount", [BigInt(taskId)]);
420
- return formatReadResult({ taskId, milestoneCount: Number(count) }, `Milestone count for Task #${taskId}`);
421
- }
422
- const data = await readContract(CONTRACTS.TaskEscrow, ABI, "getMilestone", [BigInt(taskId), BigInt(milestoneIndex)]);
423
- return formatReadResult(data, `Milestone ${milestoneIndex} of Task #${taskId}`);
424
- }
425
- catch (e) {
426
- const p = parseContractError(e);
427
- return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
428
- }
429
- });
430
- // ──────────────────────────────────────────────────────────────
431
- // corven_create_subtask
432
- // ──────────────────────────────────────────────────────────────
433
- server.registerTool("corven_create_subtask", {
434
- title: "Create Subtask",
435
- description: "Creates a child task under a parent task with its own worker, payment, and deadline.\n" +
436
- "USE WHEN: Decomposing a large task into parallel subtasks for different specialists.\n" +
437
- "REQUIRES: Parent task must exist. Both client and subtask worker must be registered. Client wallet needs payment + ~2% fees.\n" +
438
- "RETURNS: Subtask taskId, parent reference, worker, payment, deadline.\n" +
439
- "COMES AFTER: corven_create_task created the parent task.\n" +
440
- "COMES BEFORE: Subtask worker calls corven_submit_work. Client calls corven_verify_task on the subtask.\n" +
441
- "NOTE: Subtask payment is funded independently from the parent. Use corven_get_child_tasks to list all subtasks.",
442
- inputSchema: {
443
- parentTaskId: taskIdSchema,
444
- worker: ethAddress,
445
- payment: ethAmount,
446
- deadline: unixDeadline,
447
- descriptionHash: ipfsCid,
448
- },
449
- }, async ({ parentTaskId, worker, payment, deadline, descriptionHash }) => {
450
- try {
451
- const account = getAccount();
452
- if (!account)
453
- return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
454
- const paymentWei = parseEther(payment);
455
- // Calculate total value: payment + protocol fee (1%) + priority fee (default Medium=1%)
456
- const PROTOCOL_FEE_BPS = 100n;
457
- const PRIORITY_FEE_BPS = 100n;
458
- const totalFeeBps = PROTOCOL_FEE_BPS + PRIORITY_FEE_BPS;
459
- const feeAmount = paymentWei * totalFeeBps / 10000n;
460
- const totalValue = paymentWei + feeAmount;
461
- const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "createSubtask", [BigInt(parentTaskId), worker, paymentWei, BigInt(deadline), descriptionHash], totalValue);
462
- return formatTxResult(result);
463
- }
464
- catch (e) {
465
- const p = parseContractError(e);
466
- return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
467
- }
468
- });
469
- // ──────────────────────────────────────────────────────────────
470
- // corven_get_child_tasks
471
- // ──────────────────────────────────────────────────────────────
472
- server.registerTool("corven_get_child_tasks", {
473
- title: "Get Child Tasks",
474
- description: "Returns the IDs of all child tasks (subtasks) under a parent task.\n" +
475
- "USE WHEN: Tracking progress of decomposed tasks. Checking if all subtasks are complete before parent verification.\n" +
476
- "REQUIRES: Nothing. Free read-only call.\n" +
477
- "RETURNS: Parent task ID, child count, array of child task IDs.\n" +
478
- "COMES AFTER: corven_create_subtask created child tasks.\n" +
479
- "COMES BEFORE: Use corven_get_task on each child ID to check individual status.",
480
- inputSchema: { parentTaskId: taskIdSchema },
481
- }, async ({ parentTaskId }) => {
482
- try {
483
- const childIds = await readContract(CONTRACTS.TaskEscrow, ABI, "getChildTasks", [BigInt(parentTaskId)]);
484
- return formatReadResult({ parentTaskId, childCount: childIds.length, childTaskIds: childIds }, `Child tasks of Task #${parentTaskId}`);
485
- }
486
- catch (e) {
487
- const p = parseContractError(e);
488
- return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
489
- }
490
- });
491
- // ──────────────────────────────────────────────────────────────
492
- // corven_submit_query
493
- // ──────────────────────────────────────────────────────────────
494
- server.registerTool("corven_submit_query", {
495
- title: "Submit Task Query",
496
- description: "Worker submits a clarifying question about a task during execution. The query is recorded on-chain.\n" +
497
- "USE WHEN: Task specification is ambiguous. You need additional resources. You want to confirm feasibility before proceeding.\n" +
498
- "REQUIRES: You must be the assigned worker. Task must be in Funded or InProgress status.\n" +
499
- "RETURNS: Query ID, task ID, query type, submission confirmation.\n" +
500
- "COMES AFTER: corven_create_task assigned you the task.\n" +
501
- "COMES BEFORE: Client calls corven_respond_to_query. Then you continue work.\n" +
502
- "NOTE: Query types: 0=Specification (unclear requirements), 1=Resource (need more time/data), 2=Feasibility (concern about viability).",
503
- inputSchema: {
504
- taskId: taskIdSchema,
505
- queryText: z.string().describe("The query text"),
506
- queryType: z.number().describe("Query type: 0=Specification, 1=Resource, 2=Feasibility"),
507
- },
508
- }, async ({ taskId, queryText, queryType }) => {
509
- try {
510
- const account = getAccount();
511
- if (!account)
512
- return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
513
- const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "submitQuery", [BigInt(taskId), queryText, queryType]);
514
- return formatTxResult(result);
515
- }
516
- catch (e) {
517
- const p = parseContractError(e);
518
- return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
519
- }
520
- });
521
- // ──────────────────────────────────────────────────────────────
522
- // corven_respond_to_query
523
- // ──────────────────────────────────────────────────────────────
524
- server.registerTool("corven_respond_to_query", {
525
- title: "Respond to Query",
526
- description: "Client responds to a worker's clarifying query about a task. Response is recorded on-chain.\n" +
527
- "USE WHEN: A worker has submitted a query (corven_get_query shows pending queries). You want to provide clarification.\n" +
528
- "REQUIRES: You must be the task client. A query must exist on the task.\n" +
529
- "RETURNS: Response confirmation, task ID, response text recorded.\n" +
530
- "COMES AFTER: corven_submit_query by the worker.\n" +
531
- "COMES BEFORE: Worker continues task execution with your clarification.\n" +
532
- "NOTE: Check query details first with corven_get_query to understand what the worker is asking.",
533
- inputSchema: {
534
- taskId: taskIdSchema,
535
- responseText: z.string().describe("The response text"),
536
- },
537
- }, async ({ taskId, responseText }) => {
538
- try {
539
- const account = getAccount();
540
- if (!account)
541
- return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
542
- const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "respondToQuery", [BigInt(taskId), responseText]);
543
- return formatTxResult(result);
544
- }
545
- catch (e) {
546
- const p = parseContractError(e);
547
- return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
548
- }
549
- });
550
- // ──────────────────────────────────────────────────────────────
551
- // corven_get_query (includes count when no queryId provided)
552
- // ──────────────────────────────────────────────────────────────
553
- server.registerTool("corven_get_query", {
554
- title: "Get Query",
555
- description: "Reads query details by index, or returns the total query count if no index is provided.\n" +
556
- "USE WHEN: Checking if a worker has submitted questions. Reading query text before responding. Counting outstanding queries.\n" +
557
- "REQUIRES: Nothing. Free read-only call.\n" +
558
- "RETURNS: Query text, type (Specification/Resource/Feasibility), response status — or query count if index omitted.\n" +
559
- "COMES AFTER: corven_submit_query created a query.\n" +
560
- "COMES BEFORE: corven_respond_to_query to provide an answer.",
561
- inputSchema: {
562
- taskId: taskIdSchema,
563
- queryId: z.number().optional().describe("Query index (0-based). Omit to get count."),
564
- },
565
- }, async ({ taskId, queryId }) => {
566
- try {
567
- if (queryId === undefined) {
568
- const count = await readContract(CONTRACTS.TaskEscrow, ABI, "getQueryCount", [BigInt(taskId)]);
569
- return formatReadResult({ taskId, queryCount: Number(count) }, `Query count for Task #${taskId}`);
570
- }
571
- const data = await readContract(CONTRACTS.TaskEscrow, ABI, "getQuery", [BigInt(taskId), BigInt(queryId)]);
572
- return formatReadResult(data, `Query ${queryId} on Task #${taskId}`);
573
- }
574
- catch (e) {
575
- const p = parseContractError(e);
576
- return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
577
- }
578
- });
579
- // ──────────────────────────────────────────────────────────────
580
- // corven_get_tasks (consolidated: client, worker, or child tasks)
581
- // ──────────────────────────────────────────────────────────────
582
- server.registerTool("corven_get_tasks", {
583
- title: "Get Tasks",
584
- description: "Returns all task IDs where the given address is the client or the worker.\n" +
585
- "USE WHEN: Finding your active tasks. Checking which tasks you need to work on. Building a task dashboard.\n" +
586
- "REQUIRES: Nothing. Free read-only call.\n" +
587
- "RETURNS: Address, role, task count, array of task IDs.\n" +
588
- "COMES BEFORE: Use corven_get_task on each returned ID to get full details.\n" +
589
- "NOTE: Use role='worker' to find tasks assigned to you. Use role='client' to find tasks you posted.",
590
- inputSchema: {
591
- address: ethAddress,
592
- role: z.enum(["client", "worker"]).describe("Filter: 'client' or 'worker'"),
593
- },
594
- }, async ({ address, role }) => {
595
- try {
596
- const fn = role === "client" ? "getClientTasks" : "getWorkerTasks";
597
- const taskIds = await readContract(CONTRACTS.TaskEscrow, ABI, fn, [address]);
598
- return formatReadResult({ address, role, taskCount: taskIds.length, taskIds }, `Tasks where ${address} is ${role}`);
599
- }
600
- catch (e) {
601
- const p = parseContractError(e);
602
- return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
603
- }
604
- });
605
- }
606
- //# sourceMappingURL=escrow.js.map