@lucid-agents/taskmarket 1.17.1 → 1.18.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.
- package/dist/THIRD_PARTY_NOTICES.md +54 -27
- package/dist/agent-compatibility.json +1 -1
- package/dist/{budget-view-XKZC3GY5.js → budget-view-JPZ7HWR2.js} +1 -1
- package/dist/{channels-Q7XMSUSO.js → channels-544VRSKF.js} +4 -3
- package/dist/chunk-2QJMUHDT.js +351 -0
- package/dist/{launch-DE65XO5C.js → chunk-64HQ7ZF2.js} +4977 -2385
- package/dist/chunk-75LS6XS6.js +10302 -0
- package/dist/{chunk-73RXOYME.js → chunk-EB6OJBF5.js} +5 -0
- package/dist/chunk-ESPAXW2P.js +831 -0
- package/dist/chunk-JJUF3KZY.js +447 -0
- package/dist/chunk-L6ZVRENW.js +868 -0
- package/dist/{chunk-F5SS4NUF.js → chunk-MO5KMUTL.js} +106 -29
- package/dist/chunk-S4A6V3J3.js +7518 -0
- package/dist/{chunk-EIHZYRIU.js → chunk-W7CXEYST.js} +23 -23
- package/dist/chunk-ZIC2YCLV.js +65 -0
- package/dist/{doctor-BY2PBHS7.js → doctor-5FUGI42V.js} +13 -10
- package/dist/index.js +2110 -739
- package/dist/launch-C5HUFUU2.js +6364 -0
- package/dist/pi-extensions/remote-tools.cjs +174 -0
- package/dist/pi-extensions/xgate.cjs +1039 -25
- package/dist/{status-3Q55PBFC.js → prompter-LSICEYSQ.js} +3 -12
- package/dist/reconcile-Q7XFHDQQ.js +194 -0
- package/dist/status-SDLSTJIA.js +28 -0
- package/dist/sync-pull-TKM4EDGH.js +409 -0
- package/package.json +1 -1
- package/dist/chunk-3S63AB3C.js +0 -16068
- package/dist/chunk-JAZ5BWPB.js +0 -181
- package/dist/chunk-MI2GPMCZ.js +0 -154
- package/dist/reconcile-VEMPNIAO.js +0 -83
package/dist/index.js
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire as __tmCreateRequire } from 'node:module'; import { fileURLToPath as __tmFileURLToPath } from 'node:url'; import { dirname as __tmDirname } from 'node:path'; const require = __tmCreateRequire(import.meta.url); const __tmFilename = __tmFileURLToPath(import.meta.url); const __tmDirnameValue = __tmDirname(__tmFilename);
|
|
3
|
+
import {
|
|
4
|
+
TaskOperationError,
|
|
5
|
+
claimTask,
|
|
6
|
+
cliTaskReadContext,
|
|
7
|
+
cliTaskWriteContext,
|
|
8
|
+
getTask,
|
|
9
|
+
getTaskTerms,
|
|
10
|
+
listTaskSubmissions,
|
|
11
|
+
listTasks,
|
|
12
|
+
listWorkerSubmissions,
|
|
13
|
+
saveTaskAccessGrant,
|
|
14
|
+
signReadAuth,
|
|
15
|
+
submitTaskFiles,
|
|
16
|
+
taskAccessGrantHeaders
|
|
17
|
+
} from "./chunk-JJUF3KZY.js";
|
|
3
18
|
import {
|
|
4
19
|
ComputeCreditError,
|
|
5
20
|
applyComputeCreditSetup,
|
|
@@ -20,99 +35,199 @@ import {
|
|
|
20
35
|
reconcileX402PaymentById,
|
|
21
36
|
refusedPaidWrite,
|
|
22
37
|
releasePendingComputePurchase,
|
|
23
|
-
resolvePendingComputePurchases
|
|
24
|
-
|
|
25
|
-
|
|
38
|
+
resolvePendingComputePurchases
|
|
39
|
+
} from "./chunk-W7CXEYST.js";
|
|
40
|
+
import {
|
|
41
|
+
MPP_TEMPO_DEFAULT_VALIDITY_SECONDS,
|
|
42
|
+
X402PayError,
|
|
43
|
+
X402_POLICY_JSON_SCHEMA,
|
|
44
|
+
addX402PolicyRule,
|
|
45
|
+
createWalletAccountFromKeystore,
|
|
46
|
+
fetchDeviceKey,
|
|
47
|
+
getWalletAddress,
|
|
48
|
+
getX402JournalPath,
|
|
49
|
+
getX402Payment,
|
|
50
|
+
getX402PolicyPath,
|
|
51
|
+
initializeX402Policy,
|
|
52
|
+
listX402Payments,
|
|
53
|
+
loadX402Policy,
|
|
54
|
+
removeX402PolicyRule,
|
|
55
|
+
resolveX402PaymentManually,
|
|
56
|
+
setX402PolicyRuleEnabled,
|
|
57
|
+
signMessage,
|
|
58
|
+
signSponsoredTempoTransfer,
|
|
59
|
+
signTaskmarketFeePayment,
|
|
60
|
+
signTypedData
|
|
61
|
+
} from "./chunk-S4A6V3J3.js";
|
|
26
62
|
import {
|
|
27
63
|
AgentMessageEnvelopeSchema,
|
|
28
64
|
ApiError,
|
|
65
|
+
BUG_REPORT_SEVERITIES,
|
|
66
|
+
BUG_REPORT_STATUSES,
|
|
67
|
+
DEFAULT_TERMS_BOUNDS,
|
|
68
|
+
IntentStatusResponseSchema,
|
|
29
69
|
LegalAcceptanceResponseSchema,
|
|
30
70
|
LegalBundleSchema,
|
|
31
71
|
LegalChallengeResponseSchema,
|
|
32
72
|
LegalStatusResponseSchema,
|
|
33
|
-
MPP_TEMPO_DEFAULT_VALIDITY_SECONDS,
|
|
34
73
|
NETWORK_NAMES,
|
|
74
|
+
PenaltyTo,
|
|
35
75
|
READ_AUTH_ADDRESS_HEADER,
|
|
36
76
|
READ_AUTH_SIGNATURE_HEADER,
|
|
77
|
+
REDACTED,
|
|
78
|
+
RejectOutcome,
|
|
37
79
|
SponsoredTaskRewardStateSchema,
|
|
38
80
|
SponsoredTaskWorkerRewardClaimStatusSchema,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
81
|
+
SubmissionReviewAssessmentInputSchema,
|
|
82
|
+
SubmissionReviewAssessmentOutputSchema,
|
|
83
|
+
SubmissionReviewCorrectionSchema,
|
|
84
|
+
SubmissionReviewHistoryInputSchema,
|
|
85
|
+
SubmissionReviewHistoryOutputSchema,
|
|
86
|
+
SubmissionReviewJobStatusSchema,
|
|
87
|
+
SubmissionReviewLookupInputSchema,
|
|
88
|
+
SubmissionReviewPreviewInputSchema,
|
|
89
|
+
SubmissionReviewPreviewOutputSchema,
|
|
90
|
+
SubmissionReviewProfileOutputSchema,
|
|
91
|
+
SubmissionReviewResultStateSchema,
|
|
92
|
+
SubmissionReviewResultsInputSchema,
|
|
93
|
+
SubmissionReviewResultsOutputSchema,
|
|
94
|
+
SubmissionReviewRunInputSchema,
|
|
95
|
+
SubmissionReviewRunSummarySchema,
|
|
96
|
+
SubmissionReviewStartPayloadSchema,
|
|
97
|
+
SubmissionReviewTaskInputSchema,
|
|
98
|
+
SubmitBugReportInputSchema,
|
|
99
|
+
TaskTermsError,
|
|
100
|
+
TermsPresetName,
|
|
101
|
+
WALLET_BRIDGE_ORIGIN_CHAIN_ID,
|
|
102
|
+
WALLET_BRIDGE_TEMPO_CHAIN_ID,
|
|
43
103
|
apiDelete,
|
|
104
|
+
apiErrorEnvelopeOf,
|
|
44
105
|
apiGet,
|
|
45
106
|
apiOrigin,
|
|
46
107
|
apiPost,
|
|
108
|
+
apiReadPost,
|
|
47
109
|
apiUrl,
|
|
48
|
-
buildClaimMessage,
|
|
49
110
|
buildDeviceRegisterMessage,
|
|
50
111
|
buildForfeitMessage,
|
|
51
112
|
buildLegalReceiptHeaders,
|
|
113
|
+
buildPitchMessage,
|
|
114
|
+
buildProofMessage,
|
|
52
115
|
buildReadAuthMessage,
|
|
53
116
|
buildRevealAccountRecoveryCodeMessage,
|
|
54
117
|
buildSelectWorkerMessage,
|
|
55
118
|
buildSetWithdrawalAddressMessage,
|
|
119
|
+
buildSettlementActionMessage,
|
|
56
120
|
buildSponsoredTaskVaultReviewRequestMessage,
|
|
57
|
-
|
|
121
|
+
buildSubmissionReviewActionMessage,
|
|
58
122
|
buildWalletLegalAcceptanceMessage,
|
|
59
123
|
buildWithdrawDreamsMessage,
|
|
60
124
|
chainInfoFor,
|
|
61
|
-
|
|
125
|
+
claimStakeToPost,
|
|
126
|
+
clientValidityWindowSeconds,
|
|
62
127
|
decryptPrivateKey,
|
|
63
128
|
dreamsToUsd,
|
|
64
129
|
encryptPrivateKey,
|
|
65
|
-
fetchDeviceKey,
|
|
66
130
|
formatDreams,
|
|
131
|
+
formatPaymentAmount,
|
|
67
132
|
formatUsdcBaseUnits,
|
|
68
133
|
generateKeypair,
|
|
69
134
|
getTaskmarketHome,
|
|
70
|
-
getWalletAddress,
|
|
71
|
-
getX402JournalPath,
|
|
72
|
-
getX402Payment,
|
|
73
|
-
getX402PolicyPath,
|
|
74
135
|
idempotencyKeyForError,
|
|
75
|
-
initializeX402Policy,
|
|
76
136
|
isInFlightApiError,
|
|
77
137
|
isPendingApiError,
|
|
138
|
+
isRecordingPendingResponse,
|
|
139
|
+
isTerminalWalletBridgeStatus,
|
|
78
140
|
keystoreExists,
|
|
79
|
-
listX402Payments,
|
|
80
141
|
loadKeystore,
|
|
81
|
-
loadX402Policy,
|
|
82
142
|
networkToStamp,
|
|
83
143
|
paymentProtocolOf,
|
|
144
|
+
paymentTokenSymbol,
|
|
84
145
|
readFailureBody,
|
|
85
146
|
readKeystoreNetwork,
|
|
86
|
-
|
|
147
|
+
redactSecrets,
|
|
87
148
|
resolveIdempotencyKey,
|
|
88
149
|
resolveNetwork,
|
|
89
|
-
|
|
150
|
+
resolveTaskTerms,
|
|
90
151
|
saveKeystore,
|
|
91
152
|
selectedNetwork,
|
|
92
153
|
setSelectedNetwork,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
signSponsoredTempoTransfer,
|
|
96
|
-
signTaskmarketFeePayment,
|
|
97
|
-
signTypedData,
|
|
154
|
+
settlementActionExpiresAt,
|
|
155
|
+
settlementActionRequiresExpiry,
|
|
98
156
|
taskmarketChain,
|
|
99
157
|
usdcToBaseUnits,
|
|
100
158
|
withErrorContext,
|
|
101
159
|
withIdempotentWrite
|
|
102
|
-
} from "./chunk-
|
|
160
|
+
} from "./chunk-75LS6XS6.js";
|
|
103
161
|
import {
|
|
104
162
|
channelLines
|
|
105
|
-
} from "./chunk-
|
|
163
|
+
} from "./chunk-EB6OJBF5.js";
|
|
106
164
|
import "./chunk-S7SJMJ2I.js";
|
|
107
165
|
|
|
108
166
|
// src/index.ts
|
|
109
|
-
import { Command as
|
|
167
|
+
import { Command as Command103 } from "commander";
|
|
110
168
|
import { createRequire } from "module";
|
|
111
169
|
|
|
112
170
|
// src/commands/init.ts
|
|
113
171
|
import { Command as Command2 } from "commander";
|
|
114
172
|
import { privateKeyToAccount } from "viem/accounts";
|
|
115
173
|
|
|
174
|
+
// src/lib/last-error.ts
|
|
175
|
+
import fs from "fs";
|
|
176
|
+
import path from "path";
|
|
177
|
+
var REPORT_BUG_HINT = "taskmarket bug submit --include-last-error";
|
|
178
|
+
var SECRET_FLAG = /^--?(?:[\w-]*(?:key|secret|token|password|passphrase|mnemonic|seed)[\w-]*)$/i;
|
|
179
|
+
var cliVersion = "unknown";
|
|
180
|
+
function setLastErrorCliVersion(version2) {
|
|
181
|
+
cliVersion = version2;
|
|
182
|
+
}
|
|
183
|
+
function lastErrorPath() {
|
|
184
|
+
return path.join(getTaskmarketHome(), "last-error.json");
|
|
185
|
+
}
|
|
186
|
+
function redactArgv(argv) {
|
|
187
|
+
const out = [];
|
|
188
|
+
for (let i = 0; i < argv.length; i++) {
|
|
189
|
+
const arg = argv[i];
|
|
190
|
+
const eq = arg.indexOf("=");
|
|
191
|
+
if (arg.startsWith("-") && eq > 0 && SECRET_FLAG.test(arg.slice(0, eq))) {
|
|
192
|
+
out.push(`${arg.slice(0, eq)}=${REDACTED}`);
|
|
193
|
+
} else if (SECRET_FLAG.test(arg) && i + 1 < argv.length) {
|
|
194
|
+
out.push(arg, REDACTED);
|
|
195
|
+
i++;
|
|
196
|
+
} else {
|
|
197
|
+
out.push(redactSecrets(arg));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return out;
|
|
201
|
+
}
|
|
202
|
+
function currentArgs() {
|
|
203
|
+
return process.argv.slice(2);
|
|
204
|
+
}
|
|
205
|
+
function recordLastError(error, argv = currentArgs()) {
|
|
206
|
+
if (argv[0] === "bug") return false;
|
|
207
|
+
const record = {
|
|
208
|
+
command: ["taskmarket", ...redactArgv(argv)].join(" "),
|
|
209
|
+
message: redactSecrets(error.message),
|
|
210
|
+
...typeof error.code === "string" ? { code: error.code } : {},
|
|
211
|
+
...error.status !== void 0 ? { status: error.status } : {},
|
|
212
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
213
|
+
cliVersion
|
|
214
|
+
};
|
|
215
|
+
try {
|
|
216
|
+
fs.mkdirSync(getTaskmarketHome(), { recursive: true, mode: 448 });
|
|
217
|
+
fs.writeFileSync(lastErrorPath(), JSON.stringify(record, null, 2) + "\n", { mode: 384 });
|
|
218
|
+
return true;
|
|
219
|
+
} catch {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function readLastError() {
|
|
224
|
+
try {
|
|
225
|
+
return JSON.parse(fs.readFileSync(lastErrorPath(), "utf8"));
|
|
226
|
+
} catch {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
116
231
|
// src/lib/output.ts
|
|
117
232
|
function printResult(data, options) {
|
|
118
233
|
const idempotencyKey = options?.idempotencyKey;
|
|
@@ -140,6 +255,13 @@ function renderFailure(error, options) {
|
|
|
140
255
|
const x402PayError = error instanceof X402PayError ? error : void 0;
|
|
141
256
|
const idempotencyKey = options?.idempotencyKey ?? apiError?.idempotencyKey ?? idempotencyKeyForError(error);
|
|
142
257
|
const envelope = apiError?.envelope;
|
|
258
|
+
const status = apiError?.status ?? x402PayError?.status;
|
|
259
|
+
const errorCode = error?.code;
|
|
260
|
+
const recorded = recordLastError({
|
|
261
|
+
message,
|
|
262
|
+
code: envelope?.reason ?? errorCode,
|
|
263
|
+
status
|
|
264
|
+
});
|
|
143
265
|
process.stderr.write(
|
|
144
266
|
JSON.stringify({
|
|
145
267
|
ok: false,
|
|
@@ -150,7 +272,8 @@ function renderFailure(error, options) {
|
|
|
150
272
|
...x402PayError?.pending !== void 0 ? { pending: x402PayError.pending } : {},
|
|
151
273
|
...x402PayError?.payment !== void 0 ? { payment: x402PayError.payment } : {},
|
|
152
274
|
...x402PayError !== void 0 ? x402FailureFields(x402PayError) : {},
|
|
153
|
-
...options?.details ?? {}
|
|
275
|
+
...options?.details ?? {},
|
|
276
|
+
...recorded ? { reportBug: REPORT_BUG_HINT } : {}
|
|
154
277
|
}) + "\n"
|
|
155
278
|
);
|
|
156
279
|
process.exitCode = 1;
|
|
@@ -169,7 +292,9 @@ async function pollAgentId(address, maxWaitMs = 6e4) {
|
|
|
169
292
|
while (Date.now() < deadline) {
|
|
170
293
|
await new Promise((r) => setTimeout(r, interval));
|
|
171
294
|
try {
|
|
172
|
-
const res = await fetch(
|
|
295
|
+
const res = await fetch(
|
|
296
|
+
`${apiUrl()}/api/identity/status?address=${address}&chainId=${selectedNetwork().chainId}`
|
|
297
|
+
);
|
|
173
298
|
if (res.ok) {
|
|
174
299
|
const data = await res.json();
|
|
175
300
|
if (data.agentId) return data.agentId;
|
|
@@ -184,6 +309,8 @@ async function pollAgentId(address, maxWaitMs = 6e4) {
|
|
|
184
309
|
async function registerDevice(input) {
|
|
185
310
|
const response = await fetch(`${apiUrl()}/api/devices`, {
|
|
186
311
|
body: JSON.stringify({
|
|
312
|
+
// The identity the platform mints for a new device lands on this chain.
|
|
313
|
+
chainId: selectedNetwork().chainId,
|
|
187
314
|
publicKey: input.publicKey,
|
|
188
315
|
signature: input.signature,
|
|
189
316
|
walletAddress: input.walletAddress
|
|
@@ -587,7 +714,17 @@ var addressCommand = new Command3("address").description("Print the wallet addre
|
|
|
587
714
|
import { Command as Command4 } from "commander";
|
|
588
715
|
|
|
589
716
|
// src/lib/x402.ts
|
|
717
|
+
var FACILITATOR_RETRIES = 2;
|
|
718
|
+
var MAX_RETRY_AFTER_SECONDS = 60;
|
|
719
|
+
var DEFAULT_RETRY_AFTER_SECONDS = 30;
|
|
720
|
+
function retryAfterMs(res) {
|
|
721
|
+
const header = res.headers?.get?.("retry-after");
|
|
722
|
+
const seconds = header ? Number(header) : NaN;
|
|
723
|
+
const wait = Number.isFinite(seconds) && seconds >= 0 ? seconds : DEFAULT_RETRY_AFTER_SECONDS;
|
|
724
|
+
return Math.min(wait, MAX_RETRY_AFTER_SECONDS) * 1e3;
|
|
725
|
+
}
|
|
590
726
|
async function x402Post(path3, body, options) {
|
|
727
|
+
const sleep4 = options?.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
591
728
|
if (paymentProtocolOf(selectedNetwork().chainId) === "mpp")
|
|
592
729
|
return mppPost(path3, body, options);
|
|
593
730
|
const keystore = await loadKeystore();
|
|
@@ -618,28 +755,35 @@ async function x402Post(path3, body, options) {
|
|
|
618
755
|
from: keystore.walletAddress,
|
|
619
756
|
signTypedData: (typedData) => signTypedData(typedData, keystore)
|
|
620
757
|
});
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
758
|
+
for (let attempt = 0; ; attempt++) {
|
|
759
|
+
const r2 = await fetch(request.url, {
|
|
760
|
+
method: "POST",
|
|
761
|
+
redirect: "error",
|
|
762
|
+
headers: { ...request.headers, "PAYMENT-SIGNATURE": header },
|
|
763
|
+
body: request.body
|
|
764
|
+
});
|
|
765
|
+
if (r2.ok) return { data: await r2.json(), idempotencyKey };
|
|
766
|
+
const read = await readFailureBody(r2);
|
|
767
|
+
if (r2.status === 503 && apiErrorEnvelopeOf(read.body)?.reason === "facilitator_unavailable" && attempt < FACILITATOR_RETRIES) {
|
|
768
|
+
await sleep4(retryAfterMs(r2));
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
throw failedAfterPayment(path3, r2.status, read, idempotencyKey);
|
|
629
772
|
}
|
|
630
|
-
return { data: await r2.json(), idempotencyKey };
|
|
631
773
|
});
|
|
632
774
|
}
|
|
633
775
|
|
|
634
776
|
// src/commands/identity.ts
|
|
635
777
|
var identityCommand = new Command4("identity").description("Manage agent identity");
|
|
636
|
-
identityCommand.command("register").description("Register ERC-8004 agent identity (costs 0.001
|
|
778
|
+
identityCommand.command("register").description("Register ERC-8004 agent identity (costs 0.001 in the payment token)").action(async () => {
|
|
637
779
|
const { data: result, idempotencyKey } = await x402Post("/api/identity/register", {});
|
|
638
780
|
printResult({ agentId: result.agentId }, { idempotencyKey });
|
|
639
781
|
});
|
|
640
782
|
identityCommand.command("status").description("Check identity registration status").action(async () => {
|
|
641
783
|
const keystore = await loadKeystore();
|
|
642
|
-
const result = await apiGet(
|
|
784
|
+
const result = await apiGet(
|
|
785
|
+
`/api/identity/status?address=${keystore.walletAddress}&chainId=${selectedNetwork().chainId}`
|
|
786
|
+
);
|
|
643
787
|
printResult({ registered: result.registered, agentId: result.agentId });
|
|
644
788
|
});
|
|
645
789
|
|
|
@@ -654,7 +798,9 @@ var statsCommand = new Command5("stats").description("View agent statistics").op
|
|
|
654
798
|
const statsQuery = opts.agent ? `/api/agents/stats?agentId=${opts.agent}` : `/api/agents/stats?address=${address}`;
|
|
655
799
|
const [result, balanceResult, dreamsResult, exchangeRateResult] = await Promise.all([
|
|
656
800
|
apiGet(statsQuery),
|
|
657
|
-
apiGet(
|
|
801
|
+
apiGet(
|
|
802
|
+
`/api/wallet/balance?address=${address}&chainId=${selectedNetwork().chainId}`
|
|
803
|
+
),
|
|
658
804
|
apiGet(`/api/wallet/dreams-balance?address=${address}`).then((r) => r).catch(() => null),
|
|
659
805
|
apiGet("/api/wallet/exchange-rate").then((r) => r).catch(() => null)
|
|
660
806
|
]);
|
|
@@ -675,6 +821,7 @@ var statsCommand = new Command5("stats").description("View agent statistics").op
|
|
|
675
821
|
agentId: result.agentId,
|
|
676
822
|
address: result.address,
|
|
677
823
|
balanceUsdc: balanceResult.balanceUsdc,
|
|
824
|
+
balanceToken: balanceResult.tokenSymbol ?? paymentTokenSymbol(selectedNetwork().chainId),
|
|
678
825
|
balanceBaseUnits: balanceResult.balanceBaseUnits,
|
|
679
826
|
pendingDreamsRewards,
|
|
680
827
|
pendingDreamsUsd,
|
|
@@ -691,15 +838,550 @@ var statsCommand = new Command5("stats").description("View agent statistics").op
|
|
|
691
838
|
});
|
|
692
839
|
|
|
693
840
|
// src/commands/task/index.ts
|
|
694
|
-
import { Command as
|
|
841
|
+
import { Command as Command45 } from "commander";
|
|
842
|
+
|
|
843
|
+
// src/commands/task/review.ts
|
|
844
|
+
import { readFile, stat } from "fs/promises";
|
|
845
|
+
import { Command as Command6, Option } from "commander";
|
|
846
|
+
import { z as z2 } from "zod";
|
|
847
|
+
|
|
848
|
+
// src/lib/submission-reviews.ts
|
|
849
|
+
import { z } from "zod";
|
|
850
|
+
var controlOutput = z.object({
|
|
851
|
+
runId: z.string().uuid(),
|
|
852
|
+
status: z.union([SubmissionReviewJobStatusSchema, z.literal("deleted")])
|
|
853
|
+
});
|
|
854
|
+
var correctionOutput = z.object({
|
|
855
|
+
correctionId: z.string().uuid(),
|
|
856
|
+
createdAt: z.string().datetime()
|
|
857
|
+
});
|
|
858
|
+
var query = (values) => {
|
|
859
|
+
const params = new URLSearchParams();
|
|
860
|
+
for (const [key, value] of Object.entries(values))
|
|
861
|
+
if (value !== void 0) params.set(key, String(value));
|
|
862
|
+
return params.size ? `?${params}` : "";
|
|
863
|
+
};
|
|
864
|
+
var taskPath = (taskId) => `/api/tasks/${encodeURIComponent(taskId)}`;
|
|
865
|
+
var runPath = (runId) => `/api/submission-reviews/${encodeURIComponent(runId)}`;
|
|
866
|
+
function createSubmissionReviewClient(options) {
|
|
867
|
+
const now = options.now ?? Date.now;
|
|
868
|
+
const sleep4 = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
869
|
+
let headersPromise;
|
|
870
|
+
const headers = () => headersPromise ??= (async () => {
|
|
871
|
+
const signer = await options.signer();
|
|
872
|
+
return {
|
|
873
|
+
[READ_AUTH_ADDRESS_HEADER]: signer.address,
|
|
874
|
+
[READ_AUTH_SIGNATURE_HEADER]: await signer.signMessage(
|
|
875
|
+
buildReadAuthMessage(signer.address)
|
|
876
|
+
)
|
|
877
|
+
};
|
|
878
|
+
})();
|
|
879
|
+
async function get(path3, schema) {
|
|
880
|
+
return schema.parse(await options.transport.get(path3, await headers()));
|
|
881
|
+
}
|
|
882
|
+
async function write(action, payload, chainId, path3, schema, key) {
|
|
883
|
+
const idempotencyKey = z.string().uuid().parse(resolveIdempotencyKey(key)).toLowerCase();
|
|
884
|
+
return withIdempotentWrite(idempotencyKey, async () => {
|
|
885
|
+
const signer = await options.signer();
|
|
886
|
+
const expiresAt = Math.floor(now() / 1e3) + 240;
|
|
887
|
+
const signature = await signer.signMessage(
|
|
888
|
+
buildSubmissionReviewActionMessage({
|
|
889
|
+
action,
|
|
890
|
+
payload,
|
|
891
|
+
chainId,
|
|
892
|
+
requester: signer.address,
|
|
893
|
+
idempotencyKey,
|
|
894
|
+
expiresAt
|
|
895
|
+
})
|
|
896
|
+
);
|
|
897
|
+
const body = {
|
|
898
|
+
...payload,
|
|
899
|
+
chainId,
|
|
900
|
+
requesterAddress: signer.address,
|
|
901
|
+
idempotencyKey,
|
|
902
|
+
expiresAt,
|
|
903
|
+
signature,
|
|
904
|
+
...["pause", "cancel", "delete"].includes(action) ? { action } : {}
|
|
905
|
+
};
|
|
906
|
+
const result = await options.transport.post(path3, body, idempotencyKey);
|
|
907
|
+
if (result.idempotencyKey !== idempotencyKey)
|
|
908
|
+
throw new Error(
|
|
909
|
+
"Review response has a different request key; recover the original request before retrying."
|
|
910
|
+
);
|
|
911
|
+
return { data: schema.parse(result.data), idempotencyKey };
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
const client = {
|
|
915
|
+
async profile(input) {
|
|
916
|
+
const { taskId, ...params } = SubmissionReviewTaskInputSchema.parse(input);
|
|
917
|
+
return get(
|
|
918
|
+
`${taskPath(taskId)}/submission-review-profile${query(params)}`,
|
|
919
|
+
SubmissionReviewProfileOutputSchema
|
|
920
|
+
);
|
|
921
|
+
},
|
|
922
|
+
async preview(input) {
|
|
923
|
+
const parsed = SubmissionReviewPreviewInputSchema.parse(input);
|
|
924
|
+
if (parsed.scope !== "selected" && parsed.submissionIds)
|
|
925
|
+
throw new Error("Submission IDs require selected scope.");
|
|
926
|
+
if (parsed.scope === "selected" && !parsed.submissionIds?.length)
|
|
927
|
+
throw new Error("Selected scope requires submission IDs.");
|
|
928
|
+
const { taskId, ...body } = parsed;
|
|
929
|
+
return SubmissionReviewPreviewOutputSchema.parse(
|
|
930
|
+
await options.transport.preview(
|
|
931
|
+
`${taskPath(taskId)}/submission-reviews/preview`,
|
|
932
|
+
body,
|
|
933
|
+
await headers()
|
|
934
|
+
)
|
|
935
|
+
);
|
|
936
|
+
},
|
|
937
|
+
async start(input, expectedTaskId, key) {
|
|
938
|
+
const preview = SubmissionReviewPreviewOutputSchema.parse(input);
|
|
939
|
+
SubmissionReviewRunInputSchema.shape.chainId.parse(preview.chainId);
|
|
940
|
+
if (preview.taskId !== expectedTaskId)
|
|
941
|
+
throw new Error("Preview belongs to a different task.");
|
|
942
|
+
if (!preview.canStart || !preview.profile.profileFingerprint)
|
|
943
|
+
throw new Error("This preview cannot start a review. Obtain a fresh eligible preview.");
|
|
944
|
+
const payload = SubmissionReviewStartPayloadSchema.parse({
|
|
945
|
+
taskId: preview.taskId,
|
|
946
|
+
sourceFingerprint: preview.sourceFingerprint,
|
|
947
|
+
profileFingerprint: preview.profile.profileFingerprint,
|
|
948
|
+
submissionIds: preview.submissionIds
|
|
949
|
+
});
|
|
950
|
+
return write(
|
|
951
|
+
"start",
|
|
952
|
+
payload,
|
|
953
|
+
preview.chainId,
|
|
954
|
+
`${taskPath(preview.taskId)}/submission-reviews`,
|
|
955
|
+
SubmissionReviewRunSummarySchema,
|
|
956
|
+
key
|
|
957
|
+
);
|
|
958
|
+
},
|
|
959
|
+
async status(input) {
|
|
960
|
+
const { runId, ...params } = SubmissionReviewRunInputSchema.parse(input);
|
|
961
|
+
return get(`${runPath(runId)}${query(params)}`, SubmissionReviewRunSummarySchema);
|
|
962
|
+
},
|
|
963
|
+
async lookup(input) {
|
|
964
|
+
const { idempotencyKey, ...params } = SubmissionReviewLookupInputSchema.parse(input);
|
|
965
|
+
return get(
|
|
966
|
+
`/api/submission-review-requests/${idempotencyKey}${query(params)}`,
|
|
967
|
+
SubmissionReviewRunSummarySchema
|
|
968
|
+
);
|
|
969
|
+
},
|
|
970
|
+
async control(action, input, key) {
|
|
971
|
+
z.enum(["pause", "resume", "cancel", "delete"]).parse(action);
|
|
972
|
+
const { runId, chainId } = SubmissionReviewRunInputSchema.parse(input);
|
|
973
|
+
return write(
|
|
974
|
+
action,
|
|
975
|
+
{ runId },
|
|
976
|
+
chainId,
|
|
977
|
+
`${runPath(runId)}/${action === "resume" ? "resume" : "control"}`,
|
|
978
|
+
controlOutput,
|
|
979
|
+
key
|
|
980
|
+
);
|
|
981
|
+
},
|
|
982
|
+
async results(input) {
|
|
983
|
+
const { taskId, ...params } = SubmissionReviewResultsInputSchema.parse(input);
|
|
984
|
+
return get(
|
|
985
|
+
`${taskPath(taskId)}/submission-review-results${query(params)}`,
|
|
986
|
+
SubmissionReviewResultsOutputSchema
|
|
987
|
+
);
|
|
988
|
+
},
|
|
989
|
+
async allResults(input) {
|
|
990
|
+
if (input.cursor) throw new Error("Do not combine --all and --cursor.");
|
|
991
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
992
|
+
try {
|
|
993
|
+
const first = await client.results({ ...input, limit: 100 });
|
|
994
|
+
const rows = [...first.rows];
|
|
995
|
+
let cursor = first.nextCursor;
|
|
996
|
+
const seen = /* @__PURE__ */ new Set();
|
|
997
|
+
while (cursor) {
|
|
998
|
+
if (seen.has(cursor)) throw new Error("Review pagination did not advance.");
|
|
999
|
+
seen.add(cursor);
|
|
1000
|
+
const page = await client.results({ ...input, limit: 100, cursor });
|
|
1001
|
+
if (page.viewFingerprint !== first.viewFingerprint)
|
|
1002
|
+
throw new ApiError(409, "Review changed during pagination.");
|
|
1003
|
+
rows.push(...page.rows);
|
|
1004
|
+
cursor = page.nextCursor;
|
|
1005
|
+
}
|
|
1006
|
+
return { ...first, rows, nextCursor: null };
|
|
1007
|
+
} catch (error) {
|
|
1008
|
+
if (attempt === 0 && error instanceof ApiError && error.status === 409) continue;
|
|
1009
|
+
throw error;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
throw new Error("Review changed. Read the results again.");
|
|
1013
|
+
},
|
|
1014
|
+
async history(input) {
|
|
1015
|
+
const { taskId, ...params } = SubmissionReviewHistoryInputSchema.parse(input);
|
|
1016
|
+
return get(
|
|
1017
|
+
`${taskPath(taskId)}/submission-reviews${query(params)}`,
|
|
1018
|
+
SubmissionReviewHistoryOutputSchema
|
|
1019
|
+
);
|
|
1020
|
+
},
|
|
1021
|
+
async assessment(input) {
|
|
1022
|
+
const { runId, submissionId, ...params } = SubmissionReviewAssessmentInputSchema.parse(input);
|
|
1023
|
+
return get(
|
|
1024
|
+
`${runPath(runId)}/assessments/${encodeURIComponent(submissionId)}${query(params)}`,
|
|
1025
|
+
SubmissionReviewAssessmentOutputSchema
|
|
1026
|
+
);
|
|
1027
|
+
},
|
|
1028
|
+
async correct(input, key) {
|
|
1029
|
+
const { runId, chainId } = SubmissionReviewRunInputSchema.parse({
|
|
1030
|
+
runId: input.runId,
|
|
1031
|
+
chainId: input.chainId
|
|
1032
|
+
});
|
|
1033
|
+
const payload = {
|
|
1034
|
+
runId,
|
|
1035
|
+
assessmentId: z.string().uuid().parse(input.assessmentId),
|
|
1036
|
+
correction: SubmissionReviewCorrectionSchema.parse(input.correction)
|
|
1037
|
+
};
|
|
1038
|
+
return write(
|
|
1039
|
+
"correct",
|
|
1040
|
+
payload,
|
|
1041
|
+
chainId,
|
|
1042
|
+
`${runPath(runId)}/corrections`,
|
|
1043
|
+
correctionOutput,
|
|
1044
|
+
key
|
|
1045
|
+
);
|
|
1046
|
+
},
|
|
1047
|
+
async *watch(input, settings = {}) {
|
|
1048
|
+
const { intervalSeconds, timeoutSeconds } = z.object({
|
|
1049
|
+
intervalSeconds: z.number().int().min(1).max(60).default(5),
|
|
1050
|
+
timeoutSeconds: z.number().int().min(1).max(86400).default(300)
|
|
1051
|
+
}).parse(settings);
|
|
1052
|
+
const deadline = now() + timeoutSeconds * 1e3;
|
|
1053
|
+
while (true) {
|
|
1054
|
+
const status = await client.status(input);
|
|
1055
|
+
yield status;
|
|
1056
|
+
if (!["queued", "running"].includes(status.status) || now() >= deadline) return;
|
|
1057
|
+
await sleep4(Math.min(intervalSeconds * 1e3, Math.max(0, deadline - now())));
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
};
|
|
1061
|
+
return client;
|
|
1062
|
+
}
|
|
1063
|
+
function cliSubmissionReviewClient() {
|
|
1064
|
+
return createSubmissionReviewClient({
|
|
1065
|
+
signer: async () => {
|
|
1066
|
+
const keystore = await loadKeystore();
|
|
1067
|
+
return {
|
|
1068
|
+
address: keystore.walletAddress,
|
|
1069
|
+
signMessage: (message) => signMessage(message, keystore)
|
|
1070
|
+
};
|
|
1071
|
+
},
|
|
1072
|
+
transport: {
|
|
1073
|
+
get: (path3, headers) => apiGet(path3, { headers }),
|
|
1074
|
+
preview: (path3, body, headers) => apiReadPost(path3, body, { headers }),
|
|
1075
|
+
post: (path3, body, idempotencyKey) => apiPost(path3, body, { idempotencyKey })
|
|
1076
|
+
}
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
// src/commands/task/review.ts
|
|
1081
|
+
var integer = (value) => z2.coerce.number().int().positive().safe().parse(value);
|
|
1082
|
+
var collect = (value, values) => [...values, value];
|
|
1083
|
+
var taskCommand = (name, description) => new Command6(name).description(description).argument("<taskId>", "Task ID").option("--chain-id <id>", "Require this task chain", integer);
|
|
1084
|
+
var runCommand = (name, description) => new Command6(name).description(description).argument("<runId>", "Private review run ID").requiredOption("--chain-id <id>", "Chain recorded on the review run", integer);
|
|
1085
|
+
function createReviewCommand() {
|
|
1086
|
+
const group = new Command6("review").description(
|
|
1087
|
+
"Requester-private advisory brief review and experimental taste scoring; never awards or rejects work"
|
|
1088
|
+
);
|
|
1089
|
+
group.addCommand(
|
|
1090
|
+
taskCommand(
|
|
1091
|
+
"profile",
|
|
1092
|
+
"Show review availability, supported media and evaluator version"
|
|
1093
|
+
).action(async (taskId, options) => {
|
|
1094
|
+
printResult(await cliSubmissionReviewClient().profile({ taskId, chainId: options.chainId }));
|
|
1095
|
+
})
|
|
1096
|
+
);
|
|
1097
|
+
group.addCommand(
|
|
1098
|
+
taskCommand("preview", "Preview the exact brief and submission set without running inference").addOption(
|
|
1099
|
+
new Option("--scope <scope>", "latest versions, remaining versions, or selected IDs").choices(["latest", "remaining", "selected"]).default("latest")
|
|
1100
|
+
).option(
|
|
1101
|
+
"--submission-id <id>",
|
|
1102
|
+
"Submission ID for selected scope; repeat for multiple submissions",
|
|
1103
|
+
collect,
|
|
1104
|
+
[]
|
|
1105
|
+
).action(
|
|
1106
|
+
async (taskId, options) => {
|
|
1107
|
+
printResult(
|
|
1108
|
+
await cliSubmissionReviewClient().preview({
|
|
1109
|
+
taskId,
|
|
1110
|
+
chainId: options.chainId,
|
|
1111
|
+
scope: options.scope,
|
|
1112
|
+
...options.submissionId.length ? { submissionIds: options.submissionId } : {}
|
|
1113
|
+
})
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
1116
|
+
)
|
|
1117
|
+
);
|
|
1118
|
+
group.addCommand(
|
|
1119
|
+
new Command6("start").description(
|
|
1120
|
+
"Sign and start the exact saved preview; obtain a fresh preview if sources changed"
|
|
1121
|
+
).argument("<taskId>", "Task ID").requiredOption("--preview-file <path>", "JSON output from task review preview").action(async (taskId, options) => {
|
|
1122
|
+
const info = await stat(options.previewFile);
|
|
1123
|
+
if (!info.isFile() || info.size > 1048576)
|
|
1124
|
+
throw new Error("Review preview must be a JSON file of at most 1 MiB.");
|
|
1125
|
+
const input = JSON.parse(await readFile(options.previewFile, "utf8"));
|
|
1126
|
+
const preview = input && typeof input === "object" && "ok" in input && input.ok === true && "data" in input ? input.data : input;
|
|
1127
|
+
const result = await cliSubmissionReviewClient().start(preview, taskId);
|
|
1128
|
+
printResult(result.data, { idempotencyKey: result.idempotencyKey });
|
|
1129
|
+
})
|
|
1130
|
+
);
|
|
1131
|
+
group.addCommand(
|
|
1132
|
+
runCommand("status", "Read saved progress; watching never starts or resumes inference").option("--watch", "Emit one JSON result per poll until stopped, completed, or timeout").option("--interval <seconds>", "Seconds between status reads (1\u201360)", integer, 5).option(
|
|
1133
|
+
"--timeout <seconds>",
|
|
1134
|
+
"Maximum polling duration (1\u201386400); the run keeps its current state",
|
|
1135
|
+
integer,
|
|
1136
|
+
300
|
|
1137
|
+
).action(
|
|
1138
|
+
async (runId, options) => {
|
|
1139
|
+
const client = cliSubmissionReviewClient();
|
|
1140
|
+
const input = { runId, chainId: options.chainId };
|
|
1141
|
+
if (!options.watch) {
|
|
1142
|
+
printResult(await client.status(input));
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
for await (const status of client.watch(input, {
|
|
1146
|
+
intervalSeconds: options.interval,
|
|
1147
|
+
timeoutSeconds: options.timeout
|
|
1148
|
+
}))
|
|
1149
|
+
printResult(status);
|
|
1150
|
+
}
|
|
1151
|
+
)
|
|
1152
|
+
);
|
|
1153
|
+
group.addCommand(
|
|
1154
|
+
new Command6("lookup").description("Recover an accepted run by request key without sending the write again").argument("<requestKey>", "Idempotency key from the start result or error").requiredOption("--chain-id <id>", "Chain from the saved preview", integer).action(async (idempotencyKey, options) => {
|
|
1155
|
+
printResult(
|
|
1156
|
+
await cliSubmissionReviewClient().lookup({ idempotencyKey, chainId: options.chainId })
|
|
1157
|
+
);
|
|
1158
|
+
})
|
|
1159
|
+
);
|
|
1160
|
+
for (const [action, description] of [
|
|
1161
|
+
["pause", "Pause before the next provider call; already-dispatched calls may finish"],
|
|
1162
|
+
["resume", "Explicitly resume an eligible paused run"],
|
|
1163
|
+
["cancel", "Cancel remaining work and retain completed assessments"],
|
|
1164
|
+
["delete", "Delete this private report, evidence and corrections; billing records remain"]
|
|
1165
|
+
]) {
|
|
1166
|
+
const command = runCommand(action, description);
|
|
1167
|
+
if (action === "delete")
|
|
1168
|
+
command.requiredOption("--confirm", "Confirm irreversible report deletion");
|
|
1169
|
+
command.action(async (runId, options) => {
|
|
1170
|
+
const result = await cliSubmissionReviewClient().control(action, {
|
|
1171
|
+
runId,
|
|
1172
|
+
chainId: options.chainId
|
|
1173
|
+
});
|
|
1174
|
+
printResult(result.data, { idempotencyKey: result.idempotencyKey });
|
|
1175
|
+
});
|
|
1176
|
+
group.addCommand(command);
|
|
1177
|
+
}
|
|
1178
|
+
group.addCommand(
|
|
1179
|
+
taskCommand(
|
|
1180
|
+
"results",
|
|
1181
|
+
"Filter and rank saved results before pagination; new versions never inherit older scores"
|
|
1182
|
+
).option("--run-id <id>", "Inspect the exact frozen cohort of a historical run").addOption(
|
|
1183
|
+
new Option("--compliance <state>", "Brief compliance").choices([
|
|
1184
|
+
"supported",
|
|
1185
|
+
"pending",
|
|
1186
|
+
"unmet"
|
|
1187
|
+
])
|
|
1188
|
+
).addOption(
|
|
1189
|
+
new Option("--quality-band <band>", "Experimental taste band").choices([
|
|
1190
|
+
"strong",
|
|
1191
|
+
"mixed",
|
|
1192
|
+
"weak"
|
|
1193
|
+
])
|
|
1194
|
+
).addOption(
|
|
1195
|
+
new Option("--state <state>", "Processing or freshness state").choices(
|
|
1196
|
+
SubmissionReviewResultStateSchema.options
|
|
1197
|
+
)
|
|
1198
|
+
).option(
|
|
1199
|
+
"--shortlist",
|
|
1200
|
+
"Only shortlist suggestions; pending verification can still be present"
|
|
1201
|
+
).addOption(
|
|
1202
|
+
new Option("--sort <sort>", "Saved ranking or submission time").choices(["rank", "newest"]).default("rank")
|
|
1203
|
+
).option("--limit <count>", "Rows per page (1\u2013100)", integer, 24).option("--cursor <cursor>", "Cursor returned by the same result view").option("--all", "Read all pages as one consistent result; no inference").action(
|
|
1204
|
+
async (taskId, options) => {
|
|
1205
|
+
const { all, ...filters } = options;
|
|
1206
|
+
const client = cliSubmissionReviewClient();
|
|
1207
|
+
const input = { taskId, ...filters };
|
|
1208
|
+
printResult(all ? await client.allResults(input) : await client.results(input));
|
|
1209
|
+
}
|
|
1210
|
+
)
|
|
1211
|
+
);
|
|
1212
|
+
group.addCommand(
|
|
1213
|
+
taskCommand("history", "List saved review runs for this task").option("--limit <count>", "Runs per page (1\u2013100)", integer, 20).option("--cursor <runId>", "Cursor returned by the preceding history page").action(async (taskId, options) => {
|
|
1214
|
+
printResult(await cliSubmissionReviewClient().history({ taskId, ...options }));
|
|
1215
|
+
})
|
|
1216
|
+
);
|
|
1217
|
+
group.addCommand(
|
|
1218
|
+
runCommand(
|
|
1219
|
+
"assessment",
|
|
1220
|
+
"Read the frozen brief, evidence, original assessment and requester corrections"
|
|
1221
|
+
).argument("<submissionId>", "Exact submission version ID").action(async (runId, submissionId, options) => {
|
|
1222
|
+
printResult(
|
|
1223
|
+
await cliSubmissionReviewClient().assessment({
|
|
1224
|
+
runId,
|
|
1225
|
+
submissionId,
|
|
1226
|
+
chainId: options.chainId
|
|
1227
|
+
})
|
|
1228
|
+
);
|
|
1229
|
+
})
|
|
1230
|
+
);
|
|
1231
|
+
group.addCommand(
|
|
1232
|
+
runCommand(
|
|
1233
|
+
"correct",
|
|
1234
|
+
"Append a signed requester correction; preserve the original model finding"
|
|
1235
|
+
).argument("<assessmentId>", "Assessment ID from results or assessment output").requiredOption("--requirement-id <id>", "Checklist requirement to correct").addOption(
|
|
1236
|
+
new Option("--status <status>", "Corrected requirement status").choices(["supported", "unmet", "pending", "not_applicable"]).makeOptionMandatory()
|
|
1237
|
+
).requiredOption("--reason <text>", "Reason for the correction").option("--evidence-id <id>", "Existing evidence ID; repeat as needed", collect, []).action(
|
|
1238
|
+
async (runId, assessmentId, options) => {
|
|
1239
|
+
const result = await cliSubmissionReviewClient().correct({
|
|
1240
|
+
runId,
|
|
1241
|
+
assessmentId,
|
|
1242
|
+
chainId: options.chainId,
|
|
1243
|
+
correction: {
|
|
1244
|
+
requirementId: options.requirementId,
|
|
1245
|
+
status: options.status,
|
|
1246
|
+
reason: options.reason.trim(),
|
|
1247
|
+
evidenceIds: options.evidenceId
|
|
1248
|
+
}
|
|
1249
|
+
});
|
|
1250
|
+
printResult(result.data, { idempotencyKey: result.idempotencyKey });
|
|
1251
|
+
}
|
|
1252
|
+
)
|
|
1253
|
+
);
|
|
1254
|
+
return group;
|
|
1255
|
+
}
|
|
1256
|
+
var reviewCmd = createReviewCommand();
|
|
695
1257
|
|
|
696
1258
|
// src/commands/task/create.ts
|
|
697
|
-
import { Command as
|
|
1259
|
+
import { Command as Command7 } from "commander";
|
|
698
1260
|
import { parseUnits } from "viem";
|
|
1261
|
+
|
|
1262
|
+
// src/lib/settlement-terms.ts
|
|
1263
|
+
var TERM_FLAGS = {
|
|
1264
|
+
stakeRequired: "--stake-required",
|
|
1265
|
+
stakeBps: "--stake-bps",
|
|
1266
|
+
penaltyTo: "--penalty-to",
|
|
1267
|
+
reviewWindowSecs: "--review-window",
|
|
1268
|
+
decisionWindowSecs: "--decision-window",
|
|
1269
|
+
reopenExtensionSecs: "--reopen-extension",
|
|
1270
|
+
releaseAllowed: "--release",
|
|
1271
|
+
releaseGraceBps: "--release-grace-bps",
|
|
1272
|
+
rejectOutcome: "--reject-outcome",
|
|
1273
|
+
rejectFeeBps: "--reject-fee-bps",
|
|
1274
|
+
maxRevisions: "--max-revisions",
|
|
1275
|
+
resolutionWindowSecs: "--resolution-window"
|
|
1276
|
+
};
|
|
1277
|
+
function hoursToSecs(flag, value) {
|
|
1278
|
+
const hours = Number(value);
|
|
1279
|
+
if (value.trim() === "" || !Number.isFinite(hours) || hours <= 0) {
|
|
1280
|
+
return { error: `${flag} must be a positive number of hours` };
|
|
1281
|
+
}
|
|
1282
|
+
return Math.round(hours * 3600);
|
|
1283
|
+
}
|
|
1284
|
+
function integer2(flag, value, max) {
|
|
1285
|
+
if (!/^\d+$/.test(value.trim()) || Number(value) > max) {
|
|
1286
|
+
return { error: `${flag} must be an integer between 0 and ${max}` };
|
|
1287
|
+
}
|
|
1288
|
+
return Number(value);
|
|
1289
|
+
}
|
|
1290
|
+
function parseTermsFlags(opts) {
|
|
1291
|
+
const terms = {};
|
|
1292
|
+
let termsPreset;
|
|
1293
|
+
if (opts.termsPreset !== void 0) {
|
|
1294
|
+
const parsed = TermsPresetName.safeParse(opts.termsPreset);
|
|
1295
|
+
if (!parsed.success) {
|
|
1296
|
+
return { error: `--terms-preset must be one of: ${TermsPresetName.options.join(", ")}` };
|
|
1297
|
+
}
|
|
1298
|
+
termsPreset = parsed.data;
|
|
1299
|
+
}
|
|
1300
|
+
if (opts.stakeRequired !== void 0) terms.stakeRequired = opts.stakeRequired;
|
|
1301
|
+
if (opts.stakeBps !== void 0) {
|
|
1302
|
+
const bps = integer2("--stake-bps", opts.stakeBps, 1e4);
|
|
1303
|
+
if (typeof bps !== "number") return bps;
|
|
1304
|
+
terms.stakeBps = bps;
|
|
1305
|
+
if (opts.stakeRequired === void 0) terms.stakeRequired = bps > 0;
|
|
1306
|
+
} else if (opts.stakeRequired === false) {
|
|
1307
|
+
terms.stakeBps = 0;
|
|
1308
|
+
}
|
|
1309
|
+
if (opts.penaltyTo !== void 0) {
|
|
1310
|
+
const parsed = PenaltyTo.safeParse(opts.penaltyTo);
|
|
1311
|
+
if (!parsed.success) return { error: "--penalty-to must be one of: requester, protocol" };
|
|
1312
|
+
terms.penaltyTo = parsed.data;
|
|
1313
|
+
}
|
|
1314
|
+
for (const [flag, value, key] of [
|
|
1315
|
+
["--review-window", opts.reviewWindow, "reviewWindowSecs"],
|
|
1316
|
+
["--decision-window", opts.decisionWindow, "decisionWindowSecs"],
|
|
1317
|
+
["--reopen-extension", opts.reopenExtension, "reopenExtensionSecs"],
|
|
1318
|
+
["--resolution-window", opts.resolutionWindow, "resolutionWindowSecs"]
|
|
1319
|
+
]) {
|
|
1320
|
+
if (value === void 0) continue;
|
|
1321
|
+
const secs = hoursToSecs(flag, value);
|
|
1322
|
+
if (typeof secs !== "number") return secs;
|
|
1323
|
+
terms[key] = secs;
|
|
1324
|
+
}
|
|
1325
|
+
if (opts.release !== void 0) {
|
|
1326
|
+
if (opts.release !== "allowed" && opts.release !== "disallowed") {
|
|
1327
|
+
return { error: "--release must be one of: allowed, disallowed" };
|
|
1328
|
+
}
|
|
1329
|
+
terms.releaseAllowed = opts.release === "allowed";
|
|
1330
|
+
}
|
|
1331
|
+
for (const [flag, value, key, max] of [
|
|
1332
|
+
["--release-grace-bps", opts.releaseGraceBps, "releaseGraceBps", 1e4],
|
|
1333
|
+
["--reject-fee-bps", opts.rejectFeeBps, "rejectFeeBps", 1e4],
|
|
1334
|
+
["--max-revisions", opts.maxRevisions, "maxRevisions", 255]
|
|
1335
|
+
]) {
|
|
1336
|
+
if (value === void 0) continue;
|
|
1337
|
+
const n = integer2(flag, value, max);
|
|
1338
|
+
if (typeof n !== "number") return n;
|
|
1339
|
+
terms[key] = n;
|
|
1340
|
+
}
|
|
1341
|
+
if (opts.rejectOutcome !== void 0) {
|
|
1342
|
+
const parsed = RejectOutcome.safeParse(opts.rejectOutcome);
|
|
1343
|
+
if (!parsed.success) return { error: "--reject-outcome must be one of: reopen, refund" };
|
|
1344
|
+
terms.rejectOutcome = parsed.data;
|
|
1345
|
+
}
|
|
1346
|
+
return { ...termsPreset ? { termsPreset } : {}, terms };
|
|
1347
|
+
}
|
|
1348
|
+
async function fetchTermsBounds() {
|
|
1349
|
+
try {
|
|
1350
|
+
const response = await apiGet("/api/tasks/terms-bounds");
|
|
1351
|
+
return response?.bounds ?? DEFAULT_TERMS_BOUNDS;
|
|
1352
|
+
} catch {
|
|
1353
|
+
return DEFAULT_TERMS_BOUNDS;
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
function resolveTermsForCreate(args) {
|
|
1357
|
+
try {
|
|
1358
|
+
return resolveTaskTerms({
|
|
1359
|
+
mode: args.mode,
|
|
1360
|
+
hasEvaluator: args.hasEvaluator,
|
|
1361
|
+
preset: args.parsed.termsPreset,
|
|
1362
|
+
overrides: args.parsed.terms,
|
|
1363
|
+
bounds: args.bounds
|
|
1364
|
+
});
|
|
1365
|
+
} catch (error) {
|
|
1366
|
+
if (error instanceof TaskTermsError) {
|
|
1367
|
+
const flag = error.term ? TERM_FLAGS[error.term] : void 0;
|
|
1368
|
+
return { error: flag ? `${flag}: ${error.message}` : error.message };
|
|
1369
|
+
}
|
|
1370
|
+
throw error;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
function termsPresetLabel(terms) {
|
|
1374
|
+
return terms.custom ? "custom" : terms.preset;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// src/commands/task/create.ts
|
|
699
1378
|
function parseStakeOptions(opts) {
|
|
700
1379
|
if (!opts.stakeRequired && opts.stakeBps === void 0) {
|
|
701
1380
|
return { stakeRequired: false, stakeBps: 0 };
|
|
702
1381
|
}
|
|
1382
|
+
if (opts.stakeRequired === false) {
|
|
1383
|
+
return { error: "--stake-bps cannot be combined with --no-stake-required" };
|
|
1384
|
+
}
|
|
703
1385
|
if (opts.mode !== "claim") {
|
|
704
1386
|
return { error: "--stake-required and --stake-bps are only valid with --mode claim" };
|
|
705
1387
|
}
|
|
@@ -715,7 +1397,7 @@ function parseStakeOptions(opts) {
|
|
|
715
1397
|
}
|
|
716
1398
|
return { stakeRequired: true, stakeBps };
|
|
717
1399
|
}
|
|
718
|
-
var createCmd = new
|
|
1400
|
+
var createCmd = new Command7("create").description("Create a new task (costs the reward, in the payment token)").requiredOption("--description <text>", "Task description").requiredOption("--reward <amount>", "Reward in the payment token (e.g. 5)").requiredOption("--duration <hours>", "Task duration in hours").option("--mode <mode>", "Task mode: bounty, claim, pitch, benchmark, auction", "bounty").option(
|
|
719
1401
|
"--task-visibility <mode>",
|
|
720
1402
|
"Task visibility: public (default), unlisted, or private. Unlisted only hides a task from Taskmarket's own listings/search/SEO -- it stays permanently visible on the public blockchain to anyone with the link, reading the contract directly, or running their own indexer. private is genuinely access-controlled: only the requester and invited/assigned workers can view it via listings, get, or the gated reads -- see --allowed-viewers and --access-password. Even private, the on-chain existence, reward, and participation stay publicly observable regardless.",
|
|
721
1403
|
"public"
|
|
@@ -729,15 +1411,18 @@ var createCmd = new Command6("create").description("Create a new task (costs rew
|
|
|
729
1411
|
"--submission-visibility <mode>",
|
|
730
1412
|
"Submission visibility: public (default), reveal_all, winner_only, or never. Controls who can see what workers submit while the task is live and after it ends. public matches today's behavior exactly. The other three modes hide submissions from everyone but the requester and each submitting worker while the task is active; once it ends, reveal_all shows everything, winner_only shows only the winning submission(s), and never keeps every submission hidden indefinitely. Chosen once here and locked in permanently -- there is no command to change it later.",
|
|
731
1413
|
"public"
|
|
732
|
-
).option("--tags <tags>", "Comma-separated tags").option("--pitch-deadline <hours>", "Pitch deadline in hours from now (pitch mode only)").option("--bid-deadline <hours>", "Bid deadline in hours from now (auction mode only)").option(
|
|
1414
|
+
).option("--tags <tags>", "Comma-separated tags").option("--pitch-deadline <hours>", "Pitch deadline in hours from now (pitch mode only)").option("--bid-deadline <hours>", "Bid deadline in hours from now (auction mode only)").option(
|
|
1415
|
+
"--max-price <amount>",
|
|
1416
|
+
"Maximum bid price in the payment token (required for auction mode)"
|
|
1417
|
+
).option(
|
|
733
1418
|
"--auction-type <type>",
|
|
734
1419
|
"Auction subtype: dutch, english, reverse_dutch, reverse_english (required for auction mode)"
|
|
735
1420
|
).option(
|
|
736
|
-
"--auction-start-price <
|
|
737
|
-
"Starting clock price in
|
|
1421
|
+
"--auction-start-price <amount>",
|
|
1422
|
+
"Starting clock price in the payment token for reverse_dutch (required for reverse_dutch)"
|
|
738
1423
|
).option(
|
|
739
|
-
"--auction-floor-price <
|
|
740
|
-
"Floor price in
|
|
1424
|
+
"--auction-floor-price <amount>",
|
|
1425
|
+
"Floor price in the payment token for dutch clock (required for dutch)"
|
|
741
1426
|
).option(
|
|
742
1427
|
"--hook <address>",
|
|
743
1428
|
"Custom ITMPHook contract address (repeat in execution order; protocol defaults run first)",
|
|
@@ -752,13 +1437,46 @@ var createCmd = new Command6("create").description("Create a new task (costs rew
|
|
|
752
1437
|
).option(
|
|
753
1438
|
"--sponsored-task-tokens <amount>",
|
|
754
1439
|
"Whole-token amount to reserve from the --sponsored-task vault (e.g. 100 for 100 tokens). Required with --sponsored-task."
|
|
1440
|
+
).option(
|
|
1441
|
+
"--terms-preset <preset>",
|
|
1442
|
+
"Settlement terms preset: standard (default), fast or flexible. Term flags below override it."
|
|
755
1443
|
).option(
|
|
756
1444
|
"--stake-required",
|
|
757
1445
|
"Require the worker who claims the task to stake a share of the reward first (claim mode only)"
|
|
758
|
-
).option(
|
|
1446
|
+
).option("--no-stake-required", "Require no stake, overriding the preset (claim mode only)").option(
|
|
759
1447
|
"--stake-bps <bps>",
|
|
760
|
-
"Stake as basis points of the reward
|
|
761
|
-
).option("--
|
|
1448
|
+
"Stake as basis points of the reward (e.g. 1000 = 10%, at most 5000). Requires --stake-required."
|
|
1449
|
+
).option("--penalty-to <recipient>", "Where a forfeited stake goes: requester or protocol").option(
|
|
1450
|
+
"--review-window <hours>",
|
|
1451
|
+
"Hours the requester has to accept, reject or ask for a revision after delivery; the worker is paid if it lapses (claim, pitch, auction without an evaluator)"
|
|
1452
|
+
).option(
|
|
1453
|
+
"--decision-window <hours>",
|
|
1454
|
+
"Hours after expiry to pick winners; after it the requester can only accept (bounty, benchmark)"
|
|
1455
|
+
).option(
|
|
1456
|
+
"--reopen-extension <hours>",
|
|
1457
|
+
"When the task reopens, its deadline moves out at least this many hours (claim, pitch)"
|
|
1458
|
+
).option(
|
|
1459
|
+
"--release <allowed|disallowed>",
|
|
1460
|
+
"Whether a worker may hand a claim back before delivery (claim mode only)"
|
|
1461
|
+
).option(
|
|
1462
|
+
"--release-grace-bps <bps>",
|
|
1463
|
+
"Share of the claim time, in bps, during which handing back is free; after it the cost rises to the full stake at expiry"
|
|
1464
|
+
).option(
|
|
1465
|
+
"--reject-outcome <outcome>",
|
|
1466
|
+
"What happens after a rejected delivery: reopen or refund (claim, pitch; auction is refund only)"
|
|
1467
|
+
).option(
|
|
1468
|
+
"--reject-fee-bps <bps>",
|
|
1469
|
+
"Share of the reward, in bps, the worker still receives on a rejected delivery"
|
|
1470
|
+
).option(
|
|
1471
|
+
"--max-revisions <n>",
|
|
1472
|
+
"How many times the requester may send work back for fixes before accepting or rejecting"
|
|
1473
|
+
).option("--evaluator <address>", "Evaluator address (optional)").option("--evaluator-fee-bps <bps>", "Evaluator fee in basis points (optional)").option(
|
|
1474
|
+
"--evaluator-stake <amount>",
|
|
1475
|
+
"Stake behind the evaluator in the payment token (e.g. 1), paid with the reward. Requires --evaluator and --yes."
|
|
1476
|
+
).option("--yes", "Confirm paying the evaluator stake on top of the reward", false).option("--evaluation-window <hours>", "Evaluation window in hours (default: 24)").option("--appeal-window <hours>", "Appeal window in hours (default: 24)").option("--dispute-resolver <address>", "Dispute resolver address (optional)").option(
|
|
1477
|
+
"--resolution-window <hours>",
|
|
1478
|
+
"When the worker appeals the evaluator's verdict, the deadline moves out at least this many hours so the dispute resolver can rule (tasks with an evaluator; default 72)"
|
|
1479
|
+
).action(
|
|
762
1480
|
async (opts) => {
|
|
763
1481
|
let rewardBaseUnits;
|
|
764
1482
|
try {
|
|
@@ -767,6 +1485,29 @@ var createCmd = new Command6("create").description("Create a new task (costs rew
|
|
|
767
1485
|
renderFailure(withErrorContext(err, "Invalid --reward"));
|
|
768
1486
|
return;
|
|
769
1487
|
}
|
|
1488
|
+
let evaluatorStakeBaseUnits = "0";
|
|
1489
|
+
if (opts.evaluatorStake !== void 0) {
|
|
1490
|
+
try {
|
|
1491
|
+
evaluatorStakeBaseUnits = usdcToBaseUnits(opts.evaluatorStake, { allowZero: true });
|
|
1492
|
+
} catch (err) {
|
|
1493
|
+
renderFailure(withErrorContext(err, "Invalid --evaluator-stake"));
|
|
1494
|
+
return;
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
const evaluatorStaked = BigInt(evaluatorStakeBaseUnits) > 0n;
|
|
1498
|
+
if (evaluatorStaked) {
|
|
1499
|
+
if (!opts.evaluator) {
|
|
1500
|
+
return void printError("--evaluator-stake requires --evaluator");
|
|
1501
|
+
}
|
|
1502
|
+
if (opts.sponsoredTask) {
|
|
1503
|
+
return void printError("--evaluator-stake cannot be combined with --sponsored-task");
|
|
1504
|
+
}
|
|
1505
|
+
if (!opts.yes) {
|
|
1506
|
+
return void printError(
|
|
1507
|
+
`This task stakes ${opts.evaluatorStake} (${evaluatorStakeBaseUnits} base units) behind the evaluator, paid now on top of the ${opts.reward} reward (${BigInt(rewardBaseUnits) + BigInt(evaluatorStakeBaseUnits)} base units in total) and forfeitable under the task's terms. Re-run with --yes to confirm.`
|
|
1508
|
+
);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
770
1511
|
const duration = Number(opts.duration);
|
|
771
1512
|
if (!Number.isFinite(duration) || duration <= 0) {
|
|
772
1513
|
return void printError("--duration must be a positive number of hours");
|
|
@@ -834,9 +1575,9 @@ var createCmd = new Command6("create").description("Create a new task (costs rew
|
|
|
834
1575
|
);
|
|
835
1576
|
}
|
|
836
1577
|
}
|
|
837
|
-
const
|
|
838
|
-
if ("error" in
|
|
839
|
-
return void printError(
|
|
1578
|
+
const stakeFlags = parseStakeOptions(opts);
|
|
1579
|
+
if ("error" in stakeFlags) {
|
|
1580
|
+
return void printError(stakeFlags.error);
|
|
840
1581
|
}
|
|
841
1582
|
const tags = opts.tags ? opts.tags.split(",").map((t) => t.trim()) : [];
|
|
842
1583
|
const body = {
|
|
@@ -846,9 +1587,7 @@ var createCmd = new Command6("create").description("Create a new task (costs rew
|
|
|
846
1587
|
mode: opts.mode,
|
|
847
1588
|
taskVisibility: opts.taskVisibility,
|
|
848
1589
|
submissionVisibility: opts.submissionVisibility,
|
|
849
|
-
tags
|
|
850
|
-
stakeRequired: stake.stakeRequired,
|
|
851
|
-
stakeBps: stake.stakeBps
|
|
1590
|
+
tags
|
|
852
1591
|
};
|
|
853
1592
|
if (allowedViewers && allowedViewers.length > 0) {
|
|
854
1593
|
body.allowedViewers = allowedViewers;
|
|
@@ -905,6 +1644,27 @@ var createCmd = new Command6("create").description("Create a new task (costs rew
|
|
|
905
1644
|
"--hook-data cannot be combined with --sponsored-task -- the sponsored task funding request is the hookData"
|
|
906
1645
|
);
|
|
907
1646
|
}
|
|
1647
|
+
const termsFlags = parseTermsFlags(opts);
|
|
1648
|
+
if ("error" in termsFlags) {
|
|
1649
|
+
return void printError(termsFlags.error);
|
|
1650
|
+
}
|
|
1651
|
+
const resolvedTerms = resolveTermsForCreate({
|
|
1652
|
+
mode: opts.mode,
|
|
1653
|
+
hasEvaluator: Boolean(opts.evaluator),
|
|
1654
|
+
parsed: termsFlags,
|
|
1655
|
+
bounds: await fetchTermsBounds()
|
|
1656
|
+
});
|
|
1657
|
+
if ("error" in resolvedTerms) {
|
|
1658
|
+
return void printError(resolvedTerms.error);
|
|
1659
|
+
}
|
|
1660
|
+
const stake = {
|
|
1661
|
+
stakeRequired: resolvedTerms.stakeRequired,
|
|
1662
|
+
stakeBps: resolvedTerms.stakeBps
|
|
1663
|
+
};
|
|
1664
|
+
body.stakeRequired = stake.stakeRequired;
|
|
1665
|
+
body.stakeBps = stake.stakeBps;
|
|
1666
|
+
if (termsFlags.termsPreset) body.termsPreset = termsFlags.termsPreset;
|
|
1667
|
+
if (Object.keys(termsFlags.terms).length > 0) body.terms = termsFlags.terms;
|
|
908
1668
|
const ethAddrRe = /^0x[0-9a-fA-F]{40}$/;
|
|
909
1669
|
const defaultHookAddressRe = /^0x[0-9a-f]{40}$/i;
|
|
910
1670
|
const hooks = opts.hook ?? [];
|
|
@@ -1040,377 +1800,34 @@ var createCmd = new Command6("create").description("Create a new task (costs rew
|
|
|
1040
1800
|
if (opts.evaluator) {
|
|
1041
1801
|
body.evaluator = opts.evaluator;
|
|
1042
1802
|
if (feeBps !== void 0) body.evaluatorFeeBps = feeBps;
|
|
1803
|
+
if (evaluatorStaked) body.evaluatorStake = evaluatorStakeBaseUnits;
|
|
1043
1804
|
if (opts.evaluationWindow) body.evaluationWindowHours = parseFloat(opts.evaluationWindow);
|
|
1044
1805
|
if (opts.appealWindow) body.appealWindowHours = parseFloat(opts.appealWindow);
|
|
1045
1806
|
if (opts.disputeResolver) body.disputeResolver = opts.disputeResolver;
|
|
1046
1807
|
}
|
|
1047
|
-
const { data: result, idempotencyKey } = await x402Post(
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
);
|
|
1051
|
-
printResult({ taskId: result.taskId }, { idempotencyKey });
|
|
1052
|
-
}
|
|
1053
|
-
);
|
|
1054
|
-
|
|
1055
|
-
// src/commands/task/search.ts
|
|
1056
|
-
import { Command as Command7 } from "commander";
|
|
1057
|
-
|
|
1058
|
-
// src/lib/read-auth.ts
|
|
1059
|
-
async function signReadAuth() {
|
|
1060
|
-
let keystore;
|
|
1061
|
-
try {
|
|
1062
|
-
keystore = await loadKeystore();
|
|
1063
|
-
} catch {
|
|
1064
|
-
return null;
|
|
1065
|
-
}
|
|
1066
|
-
try {
|
|
1067
|
-
const account = await createWalletAccountFromKeystore(keystore);
|
|
1068
|
-
const signature = await account.signMessage({
|
|
1069
|
-
message: buildReadAuthMessage(keystore.walletAddress)
|
|
1070
|
-
});
|
|
1071
|
-
return {
|
|
1072
|
-
walletAddress: keystore.walletAddress,
|
|
1073
|
-
headers: {
|
|
1074
|
-
[READ_AUTH_ADDRESS_HEADER]: keystore.walletAddress,
|
|
1075
|
-
[READ_AUTH_SIGNATURE_HEADER]: signature
|
|
1076
|
-
}
|
|
1077
|
-
};
|
|
1078
|
-
} catch {
|
|
1079
|
-
return { walletAddress: keystore.walletAddress, headers: {} };
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
// src/lib/task-access-grants.ts
|
|
1084
|
-
import { promises as fs } from "fs";
|
|
1085
|
-
import path from "path";
|
|
1086
|
-
function getGrantsPath() {
|
|
1087
|
-
return path.join(getTaskmarketHome(), "task-access-grants.json");
|
|
1088
|
-
}
|
|
1089
|
-
async function readGrantsFile() {
|
|
1090
|
-
try {
|
|
1091
|
-
const raw = await fs.readFile(getGrantsPath(), "utf-8");
|
|
1092
|
-
return JSON.parse(raw);
|
|
1093
|
-
} catch {
|
|
1094
|
-
return {};
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
async function loadTaskAccessGrant(taskId) {
|
|
1098
|
-
const grants = await readGrantsFile();
|
|
1099
|
-
const record = grants[taskId];
|
|
1100
|
-
if (!record) return null;
|
|
1101
|
-
if (new Date(record.expiresAt).getTime() <= Date.now()) return null;
|
|
1102
|
-
return record.grant;
|
|
1103
|
-
}
|
|
1104
|
-
async function taskAccessGrantHeaders(taskId) {
|
|
1105
|
-
const grant = await loadTaskAccessGrant(taskId);
|
|
1106
|
-
return grant ? { [TASK_ACCESS_GRANT_HEADER]: grant } : {};
|
|
1107
|
-
}
|
|
1108
|
-
async function saveTaskAccessGrant(taskId, grant, expiresAt) {
|
|
1109
|
-
const grants = await readGrantsFile();
|
|
1110
|
-
grants[taskId] = { grant, expiresAt };
|
|
1111
|
-
const dir = path.dirname(getGrantsPath());
|
|
1112
|
-
await fs.mkdir(dir, { recursive: true });
|
|
1113
|
-
await fs.writeFile(getGrantsPath(), JSON.stringify(grants, null, 2), {
|
|
1114
|
-
mode: 384
|
|
1115
|
-
});
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
// src/sdk/artifact-upload.ts
|
|
1119
|
-
import { extname } from "path";
|
|
1120
|
-
import { Readable } from "stream";
|
|
1121
|
-
import https from "https";
|
|
1122
|
-
import http from "http";
|
|
1123
|
-
function mimeTypeForPath(filePath) {
|
|
1124
|
-
switch (extname(filePath).toLowerCase()) {
|
|
1125
|
-
case ".png":
|
|
1126
|
-
return "image/png";
|
|
1127
|
-
case ".jpg":
|
|
1128
|
-
case ".jpeg":
|
|
1129
|
-
return "image/jpeg";
|
|
1130
|
-
case ".webp":
|
|
1131
|
-
return "image/webp";
|
|
1132
|
-
case ".gif":
|
|
1133
|
-
return "image/gif";
|
|
1134
|
-
case ".svg":
|
|
1135
|
-
return "image/svg+xml";
|
|
1136
|
-
case ".pdf":
|
|
1137
|
-
return "application/pdf";
|
|
1138
|
-
case ".mp4":
|
|
1139
|
-
return "video/mp4";
|
|
1140
|
-
case ".webm":
|
|
1141
|
-
return "video/webm";
|
|
1142
|
-
case ".mp3":
|
|
1143
|
-
return "audio/mpeg";
|
|
1144
|
-
case ".wav":
|
|
1145
|
-
return "audio/wav";
|
|
1146
|
-
case ".md":
|
|
1147
|
-
return "text/markdown";
|
|
1148
|
-
case ".txt":
|
|
1149
|
-
return "text/plain";
|
|
1150
|
-
case ".json":
|
|
1151
|
-
return "application/json";
|
|
1152
|
-
case ".zip":
|
|
1153
|
-
return "application/zip";
|
|
1154
|
-
default:
|
|
1155
|
-
return "application/octet-stream";
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
function putArtifactBytes(uploadUrl, data, mimeType, onProgress) {
|
|
1159
|
-
return new Promise((resolve, reject) => {
|
|
1160
|
-
const url = new URL(uploadUrl);
|
|
1161
|
-
const transport = url.protocol === "https:" ? https : http;
|
|
1162
|
-
const req = transport.request(
|
|
1163
|
-
url,
|
|
1808
|
+
const { data: result, idempotencyKey } = await x402Post("/api/tasks", body);
|
|
1809
|
+
const terms = result.terms ?? resolvedTerms;
|
|
1810
|
+
printResult(
|
|
1164
1811
|
{
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
}
|
|
1812
|
+
taskId: result.taskId,
|
|
1813
|
+
terms,
|
|
1814
|
+
termsPreset: termsPresetLabel(terms),
|
|
1815
|
+
...evaluatorStaked ? { evaluatorStake: evaluatorStakeBaseUnits } : {}
|
|
1170
1816
|
},
|
|
1171
|
-
|
|
1172
|
-
const chunks = [];
|
|
1173
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
1174
|
-
res.on("end", () => {
|
|
1175
|
-
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
1176
|
-
resolve();
|
|
1177
|
-
} else {
|
|
1178
|
-
const body = Buffer.concat(chunks).toString("utf8").slice(0, 500);
|
|
1179
|
-
reject(
|
|
1180
|
-
new Error(
|
|
1181
|
-
`Upload failed (${res.statusCode ?? "unknown"}): ${body || "no response body"}`
|
|
1182
|
-
)
|
|
1183
|
-
);
|
|
1184
|
-
}
|
|
1185
|
-
});
|
|
1186
|
-
}
|
|
1817
|
+
{ idempotencyKey }
|
|
1187
1818
|
);
|
|
1188
|
-
req.on("error", reject);
|
|
1189
|
-
let uploaded = 0;
|
|
1190
|
-
const stream = Readable.from([data]);
|
|
1191
|
-
stream.on("data", (chunk) => {
|
|
1192
|
-
uploaded += chunk.length;
|
|
1193
|
-
onProgress?.(Math.round(uploaded / data.length * 100));
|
|
1194
|
-
});
|
|
1195
|
-
stream.on("error", reject);
|
|
1196
|
-
stream.pipe(req);
|
|
1197
|
-
});
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
// src/lib/task-sdk.ts
|
|
1201
|
-
async function putWithProgress(upload) {
|
|
1202
|
-
process.stderr.write(` Uploading ${upload.fileName}...
|
|
1203
|
-
`);
|
|
1204
|
-
await putArtifactBytes(upload.uploadUrl, upload.data, upload.mimeType, (percent) => {
|
|
1205
|
-
process.stderr.write(`\r ${upload.fileName}: ${percent}%`);
|
|
1206
|
-
});
|
|
1207
|
-
process.stderr.write("\n");
|
|
1208
|
-
}
|
|
1209
|
-
var cliTaskTransport = {
|
|
1210
|
-
get: (path3, headers) => apiGet(path3, { headers }),
|
|
1211
|
-
// A write the caller gave no key is sent exactly as before the SDK existed, with no options,
|
|
1212
|
-
// so `apiPost` resolves its key itself (explicit, then TASKMARKET_IDEMPOTENCY_KEY, then fresh).
|
|
1213
|
-
post: (path3, body, options) => options === void 0 ? apiPost(path3, body) : apiPost(path3, body, options),
|
|
1214
|
-
feePost: (path3, body, options) => x402FeePost(path3, body, options),
|
|
1215
|
-
put: putWithProgress
|
|
1216
|
-
};
|
|
1217
|
-
function keystoreTaskSigner(keystore) {
|
|
1218
|
-
return {
|
|
1219
|
-
address: keystore.walletAddress,
|
|
1220
|
-
signMessage: (message) => signMessage(message, keystore)
|
|
1221
|
-
};
|
|
1222
|
-
}
|
|
1223
|
-
function cliTaskReadContext() {
|
|
1224
|
-
return {
|
|
1225
|
-
transport: cliTaskTransport,
|
|
1226
|
-
readAuth: signReadAuth,
|
|
1227
|
-
accessGrantHeaders: taskAccessGrantHeaders
|
|
1228
|
-
};
|
|
1229
|
-
}
|
|
1230
|
-
function cliTaskWriteContext(keystore) {
|
|
1231
|
-
return { ...cliTaskReadContext(), signer: keystoreTaskSigner(keystore) };
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
// src/sdk/tasks.ts
|
|
1235
|
-
import { promises as fsPromises } from "fs";
|
|
1236
|
-
import { basename } from "path";
|
|
1237
|
-
import { createHash } from "crypto";
|
|
1238
|
-
import { keccak256 } from "viem";
|
|
1239
|
-
var TaskOperationError = class extends Error {
|
|
1240
|
-
code;
|
|
1241
|
-
constructor(code, message) {
|
|
1242
|
-
super(message);
|
|
1243
|
-
this.name = "TaskOperationError";
|
|
1244
|
-
this.code = code;
|
|
1245
|
-
}
|
|
1246
|
-
};
|
|
1247
|
-
function withChainQuery(path3, chainId) {
|
|
1248
|
-
if (chainId === void 0) return path3;
|
|
1249
|
-
return `${path3}${path3.includes("?") ? "&" : "?"}chainId=${chainId}`;
|
|
1250
|
-
}
|
|
1251
|
-
function withChainBody(body, chainId) {
|
|
1252
|
-
return chainId === void 0 ? body : { ...body, chainId };
|
|
1253
|
-
}
|
|
1254
|
-
function writeOptions(idempotencyKey) {
|
|
1255
|
-
return idempotencyKey === void 0 ? void 0 : { idempotencyKey };
|
|
1256
|
-
}
|
|
1257
|
-
async function readAuthHeaders(ctx) {
|
|
1258
|
-
return (await ctx.readAuth?.())?.headers ?? {};
|
|
1259
|
-
}
|
|
1260
|
-
async function getTask(ctx, ref) {
|
|
1261
|
-
const headers = {
|
|
1262
|
-
...await readAuthHeaders(ctx),
|
|
1263
|
-
...await ctx.accessGrantHeaders?.(ref.taskId) ?? {}
|
|
1264
|
-
};
|
|
1265
|
-
const task = await ctx.transport.get(
|
|
1266
|
-
withChainQuery(`/api/tasks/${ref.taskId}`, ref.chainId),
|
|
1267
|
-
headers
|
|
1268
|
-
);
|
|
1269
|
-
return task ?? null;
|
|
1270
|
-
}
|
|
1271
|
-
async function listTasks(ctx, filter = {}) {
|
|
1272
|
-
const params = new URLSearchParams();
|
|
1273
|
-
if (filter.status) params.set("status", filter.status);
|
|
1274
|
-
if (filter.phase) params.set("phase", filter.phase);
|
|
1275
|
-
if (filter.mode) params.set("mode", filter.mode);
|
|
1276
|
-
if (filter.auctionType) params.set("auctionType", filter.auctionType);
|
|
1277
|
-
if (filter.tags) params.set("tags", filter.tags);
|
|
1278
|
-
if (filter.minReward) params.set("minReward", filter.minReward);
|
|
1279
|
-
if (filter.maxReward) params.set("maxReward", filter.maxReward);
|
|
1280
|
-
if (filter.deadlineHours) params.set("deadlineHours", filter.deadlineHours);
|
|
1281
|
-
if (filter.limit) params.set("limit", filter.limit);
|
|
1282
|
-
if (filter.cursor) params.set("cursor", filter.cursor);
|
|
1283
|
-
const result = await ctx.transport.get(
|
|
1284
|
-
`/api/tasks?${params.toString()}`,
|
|
1285
|
-
await readAuthHeaders(ctx)
|
|
1286
|
-
);
|
|
1287
|
-
return { tasks: result.tasks, hasMore: result.hasMore, nextCursor: result.nextCursor };
|
|
1288
|
-
}
|
|
1289
|
-
async function listTaskSubmissions(ctx, ref) {
|
|
1290
|
-
const headers = await readAuthHeaders(ctx);
|
|
1291
|
-
return await ctx.transport.get(
|
|
1292
|
-
withChainQuery(`/api/tasks/${ref.taskId}/submissions`, ref.chainId),
|
|
1293
|
-
headers
|
|
1294
|
-
);
|
|
1295
|
-
}
|
|
1296
|
-
async function listWorkerSubmissions(ctx, options = {}) {
|
|
1297
|
-
const auth = await ctx.readAuth?.();
|
|
1298
|
-
const address = options.address ?? auth?.walletAddress;
|
|
1299
|
-
if (!address) {
|
|
1300
|
-
throw new TaskOperationError(
|
|
1301
|
-
"no_address",
|
|
1302
|
-
"No worker address was given and there is no reading wallet to default to"
|
|
1303
|
-
);
|
|
1304
|
-
}
|
|
1305
|
-
return ctx.transport.get(`/api/submissions/mine?workerAddress=${encodeURIComponent(address)}`, {
|
|
1306
|
-
...auth?.headers ?? {}
|
|
1307
|
-
});
|
|
1308
|
-
}
|
|
1309
|
-
async function claimTask(ctx, ref, options = {}) {
|
|
1310
|
-
const signature = await ctx.signer.signMessage(buildClaimMessage(ref.taskId));
|
|
1311
|
-
const { data, idempotencyKey } = await ctx.transport.post(
|
|
1312
|
-
`/api/tasks/${ref.taskId}/claim`,
|
|
1313
|
-
withChainBody({ workerAddress: ctx.signer.address, signature }, ref.chainId),
|
|
1314
|
-
writeOptions(options.idempotencyKey)
|
|
1315
|
-
);
|
|
1316
|
-
return { claimId: data.claimId, idempotencyKey };
|
|
1317
|
-
}
|
|
1318
|
-
async function authorizeUploads(ctx, ref) {
|
|
1319
|
-
return { signature: await ctx.signer.signMessage(buildSubmitMessage(ref.taskId)) };
|
|
1320
|
-
}
|
|
1321
|
-
async function readArtifactFile(file) {
|
|
1322
|
-
const fileName = file.fileName ?? basename(file.path);
|
|
1323
|
-
const data = await fsPromises.readFile(file.path);
|
|
1324
|
-
if (data.length === 0) {
|
|
1325
|
-
throw new TaskOperationError("empty_artifact", `File is empty: ${fileName}`);
|
|
1326
|
-
}
|
|
1327
|
-
return {
|
|
1328
|
-
data,
|
|
1329
|
-
artifact: {
|
|
1330
|
-
fileName,
|
|
1331
|
-
mimeType: file.mimeType ?? mimeTypeForPath(file.path),
|
|
1332
|
-
role: file.role,
|
|
1333
|
-
sizeBytes: data.length,
|
|
1334
|
-
sha256Hash: createHash("sha256").update(data).digest("hex"),
|
|
1335
|
-
keccak256Hash: keccak256(new Uint8Array(data))
|
|
1336
|
-
}
|
|
1337
|
-
};
|
|
1338
|
-
}
|
|
1339
|
-
async function requestArtifactUpload(ctx, ref, artifact, authorization) {
|
|
1340
|
-
const {
|
|
1341
|
-
data: { uploadUrl, artifactKey }
|
|
1342
|
-
} = await ctx.transport.post(
|
|
1343
|
-
`/api/tasks/${ref.taskId}/submissions/request-upload-url`,
|
|
1344
|
-
withChainBody(
|
|
1345
|
-
{
|
|
1346
|
-
taskId: ref.taskId,
|
|
1347
|
-
workerAddress: ctx.signer.address,
|
|
1348
|
-
signature: authorization.signature,
|
|
1349
|
-
fileName: artifact.fileName,
|
|
1350
|
-
mimeType: artifact.mimeType,
|
|
1351
|
-
role: artifact.role,
|
|
1352
|
-
sizeBytes: artifact.sizeBytes
|
|
1353
|
-
},
|
|
1354
|
-
ref.chainId
|
|
1355
|
-
)
|
|
1356
|
-
);
|
|
1357
|
-
return { uploadUrl, artifactKey };
|
|
1358
|
-
}
|
|
1359
|
-
async function uploadArtifact(ctx, ref, file, authorization) {
|
|
1360
|
-
const { data, artifact } = await readArtifactFile(file);
|
|
1361
|
-
const { uploadUrl, artifactKey } = await requestArtifactUpload(ctx, ref, artifact, authorization);
|
|
1362
|
-
await ctx.transport.put({
|
|
1363
|
-
uploadUrl,
|
|
1364
|
-
data,
|
|
1365
|
-
mimeType: artifact.mimeType,
|
|
1366
|
-
fileName: artifact.fileName
|
|
1367
|
-
});
|
|
1368
|
-
return { artifactKey, ...artifact };
|
|
1369
|
-
}
|
|
1370
|
-
async function submitUploadedArtifacts(ctx, ref, artifacts, options = {}) {
|
|
1371
|
-
if (artifacts.length === 0) {
|
|
1372
|
-
throw new TaskOperationError("no_artifacts", "At least one artifact is required");
|
|
1373
1819
|
}
|
|
1374
|
-
|
|
1375
|
-
buildSubmitMessage(
|
|
1376
|
-
ref.taskId,
|
|
1377
|
-
artifacts.map((artifact) => artifact.artifactKey)
|
|
1378
|
-
)
|
|
1379
|
-
);
|
|
1380
|
-
const { data, idempotencyKey } = await ctx.transport.feePost(
|
|
1381
|
-
`/api/tasks/${ref.taskId}/submissions/from-keys`,
|
|
1382
|
-
withChainBody(
|
|
1383
|
-
{
|
|
1384
|
-
taskId: ref.taskId,
|
|
1385
|
-
workerAddress: ctx.signer.address,
|
|
1386
|
-
artifacts,
|
|
1387
|
-
signature
|
|
1388
|
-
},
|
|
1389
|
-
ref.chainId
|
|
1390
|
-
),
|
|
1391
|
-
options
|
|
1392
|
-
);
|
|
1393
|
-
return { submissionId: data.submissionId, idempotencyKey };
|
|
1394
|
-
}
|
|
1395
|
-
async function submitTaskFiles(ctx, ref, files, options = {}) {
|
|
1396
|
-
if (files.length === 0) {
|
|
1397
|
-
throw new TaskOperationError("no_artifacts", "At least one file is required");
|
|
1398
|
-
}
|
|
1399
|
-
const authorization = await authorizeUploads(ctx, ref);
|
|
1400
|
-
const artifacts = await Promise.all(
|
|
1401
|
-
files.map((file) => uploadArtifact(ctx, ref, file, authorization))
|
|
1402
|
-
);
|
|
1403
|
-
return submitUploadedArtifacts(ctx, ref, artifacts, options);
|
|
1404
|
-
}
|
|
1820
|
+
);
|
|
1405
1821
|
|
|
1406
1822
|
// src/commands/task/search.ts
|
|
1407
|
-
|
|
1823
|
+
import { Command as Command8 } from "commander";
|
|
1824
|
+
var listCmd = new Command8("list").alias("search").description("List available tasks").option("--status <status>", "Filter by status (e.g. open)", "open").option(
|
|
1408
1825
|
"--phase <phase>",
|
|
1409
1826
|
"Filter by derived lifecycle phase (active, in_review, awaiting_settlement, resolved) -- independent of --status, e.g. --phase awaiting_settlement finds tasks whose deadline has passed but are still open/claimed/worker_selected"
|
|
1410
1827
|
).option("--mode <mode>", "Filter by mode (bounty, claim, pitch, benchmark, auction)").option(
|
|
1411
1828
|
"--auction-type <type>",
|
|
1412
1829
|
"Filter auction tasks by subtype (dutch, english, reverse_dutch, reverse_english)"
|
|
1413
|
-
).option("--tags <tags>", "Comma-separated tags to filter by").option("--skill <skill>", "Filter by skill tag (comma-separated, alias for --tags)").option("--reward-min <n>", "Minimum reward in
|
|
1830
|
+
).option("--tags <tags>", "Comma-separated tags to filter by").option("--skill <skill>", "Filter by skill tag (comma-separated, alias for --tags)").option("--reward-min <n>", "Minimum reward in the payment token").option("--reward-max <n>", "Maximum reward in the payment token").option("--deadline-hours <n>", "Only tasks expiring within this many hours").option("--limit <n>", "Maximum results to return", "20").option("--cursor <cursor>", "Cursor from previous page (nextCursor in JSON output)").action(
|
|
1414
1831
|
async (opts) => {
|
|
1415
1832
|
const tagsValue = opts.tags ?? opts.skill;
|
|
1416
1833
|
const result = await listTasks(cliTaskReadContext(), {
|
|
@@ -1430,8 +1847,8 @@ var listCmd = new Command7("list").alias("search").description("List available t
|
|
|
1430
1847
|
);
|
|
1431
1848
|
|
|
1432
1849
|
// src/commands/task/get.ts
|
|
1433
|
-
import { Command as
|
|
1434
|
-
var getCmd = new
|
|
1850
|
+
import { Command as Command9 } from "commander";
|
|
1851
|
+
var getCmd = new Command9("get").description("Get task details").argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
1435
1852
|
const task = await getTask(cliTaskReadContext(), { taskId });
|
|
1436
1853
|
if (!task) {
|
|
1437
1854
|
printError(
|
|
@@ -1439,16 +1856,24 @@ var getCmd = new Command8("get").description("Get task details").argument("<task
|
|
|
1439
1856
|
);
|
|
1440
1857
|
return;
|
|
1441
1858
|
}
|
|
1442
|
-
printResult(task);
|
|
1859
|
+
printResult({ ...task, ...await termsAmountsAndDeadlines(taskId) });
|
|
1443
1860
|
});
|
|
1861
|
+
async function termsAmountsAndDeadlines(taskId) {
|
|
1862
|
+
try {
|
|
1863
|
+
const detail = await getTaskTerms(cliTaskReadContext(), { taskId });
|
|
1864
|
+
return detail ? { termsAmounts: detail.amounts, termsDeadlines: detail.deadlines } : {};
|
|
1865
|
+
} catch {
|
|
1866
|
+
return {};
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1444
1869
|
|
|
1445
1870
|
// src/commands/task/submit.ts
|
|
1446
|
-
import { Command as
|
|
1871
|
+
import { Command as Command10, Option as Option2 } from "commander";
|
|
1447
1872
|
function collectFile(value, previous) {
|
|
1448
1873
|
return [...previous, value];
|
|
1449
1874
|
}
|
|
1450
|
-
var submitCmd = new
|
|
1451
|
-
new
|
|
1875
|
+
var submitCmd = new Command10("submit").description("Submit work for a task").argument("<taskId>", "Task ID (0x-prefixed hex)").option("--file <path>", "Path to a submission file (repeatable)", collectFile, []).addOption(
|
|
1876
|
+
new Option2("--role <role>", "Artifact role applied to all files").choices(["preview", "source", "final", "attachment"]).default("attachment")
|
|
1452
1877
|
).action(async (taskId, opts) => {
|
|
1453
1878
|
try {
|
|
1454
1879
|
if (!opts.file || opts.file.length === 0) {
|
|
@@ -1470,8 +1895,53 @@ var submitCmd = new Command9("submit").description("Submit work for a task").arg
|
|
|
1470
1895
|
});
|
|
1471
1896
|
|
|
1472
1897
|
// src/commands/task/accept.ts
|
|
1473
|
-
import { Command as
|
|
1474
|
-
|
|
1898
|
+
import { Command as Command11 } from "commander";
|
|
1899
|
+
|
|
1900
|
+
// src/lib/recording.ts
|
|
1901
|
+
var RECORDING_POLL_INTERVAL_MS = 3e3;
|
|
1902
|
+
var RECORDING_POLL_TIMEOUT_MS = 75e3;
|
|
1903
|
+
var sleepFor = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1904
|
+
async function settleRecording(data, idempotencyKey, options = {}) {
|
|
1905
|
+
if (!isRecordingPendingResponse(data)) return {};
|
|
1906
|
+
const { txHash, intentId } = data;
|
|
1907
|
+
const intervalMs = options.intervalMs ?? RECORDING_POLL_INTERVAL_MS;
|
|
1908
|
+
const timeoutMs = options.timeoutMs ?? RECORDING_POLL_TIMEOUT_MS;
|
|
1909
|
+
const sleep4 = options.sleep ?? sleepFor;
|
|
1910
|
+
const now = options.now ?? Date.now;
|
|
1911
|
+
const log = options.log ?? ((line) => process.stderr.write(`${line}
|
|
1912
|
+
`));
|
|
1913
|
+
log(
|
|
1914
|
+
`Transaction ${txHash ?? "(unknown)"} is confirmed on chain; recording it is pending. Waiting up to ${Math.round(timeoutMs / 1e3)}s for the backend to record it...`
|
|
1915
|
+
);
|
|
1916
|
+
const headers = (await signReadAuth())?.headers ?? {};
|
|
1917
|
+
const path3 = `/api/intents?idempotencyKey=${encodeURIComponent(idempotencyKey)}`;
|
|
1918
|
+
const deadline = now() + timeoutMs;
|
|
1919
|
+
while (now() < deadline) {
|
|
1920
|
+
await sleep4(intervalMs);
|
|
1921
|
+
let intent;
|
|
1922
|
+
try {
|
|
1923
|
+
intent = IntentStatusResponseSchema.safeParse(await apiGet(path3, { headers }));
|
|
1924
|
+
} catch {
|
|
1925
|
+
continue;
|
|
1926
|
+
}
|
|
1927
|
+
if (!intent.success) continue;
|
|
1928
|
+
if (intent.data.status === "completed") return { status: "recorded" };
|
|
1929
|
+
if (intent.data.status === "failed") {
|
|
1930
|
+
throw new Error(
|
|
1931
|
+
`Transaction ${txHash ?? "(unknown)"} confirmed, but the backend marked the write failed: ${intent.data.terminalReason ?? "no reason recorded"}`
|
|
1932
|
+
);
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
return {
|
|
1936
|
+
status: "recording_pending",
|
|
1937
|
+
...txHash !== void 0 ? { txHash } : {},
|
|
1938
|
+
...intentId !== void 0 ? { intentId } : {},
|
|
1939
|
+
nextStep: `The transaction is confirmed on chain and the backend retries the recording automatically. Do not re-run this command; check later with GET /api/intents?idempotencyKey=${idempotencyKey}.`
|
|
1940
|
+
};
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
// src/commands/task/accept.ts
|
|
1944
|
+
var acceptCmd = new Command11("accept").description("Accept a submission (costs 0.001 in the payment token)").argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption("--worker <addr>", "Worker wallet address").action(async (taskId, opts) => {
|
|
1475
1945
|
const task = await getTask(cliTaskReadContext(), { taskId });
|
|
1476
1946
|
if (!task) {
|
|
1477
1947
|
printError(
|
|
@@ -1490,19 +1960,22 @@ var acceptCmd = new Command10("accept").description("Accept a submission (costs
|
|
|
1490
1960
|
(a) => a.action === "select_worker" && a.role === "requester"
|
|
1491
1961
|
);
|
|
1492
1962
|
printError(
|
|
1493
|
-
hasRefund ? "Task has expired
|
|
1963
|
+
hasRefund ? "Task has expired and can no longer be accepted. Use taskmarket task refund-expired to recover escrow." : hasSelectWorker ? "Pitch window has closed. Use taskmarket task select-worker to pick from received pitches, or cancel the task." : "Accept is not available for this task in its current state."
|
|
1494
1964
|
);
|
|
1495
1965
|
return;
|
|
1496
1966
|
}
|
|
1497
|
-
const { idempotencyKey } = await x402Post(`/api/tasks/${taskId}/accept`, {
|
|
1967
|
+
const { data: result, idempotencyKey } = await x402Post(`/api/tasks/${taskId}/accept`, {
|
|
1498
1968
|
taskId,
|
|
1499
1969
|
worker: opts.worker
|
|
1500
1970
|
});
|
|
1501
|
-
printResult(
|
|
1971
|
+
printResult(
|
|
1972
|
+
{ accepted: true, ...await settleRecording(result, idempotencyKey) },
|
|
1973
|
+
{ idempotencyKey }
|
|
1974
|
+
);
|
|
1502
1975
|
});
|
|
1503
1976
|
|
|
1504
1977
|
// src/commands/task/accept-submissions.ts
|
|
1505
|
-
import { Command as
|
|
1978
|
+
import { Command as Command12 } from "commander";
|
|
1506
1979
|
function parseWinner(spec) {
|
|
1507
1980
|
const parts = spec.split(":");
|
|
1508
1981
|
if (parts.length < 2 || parts.length > 3) {
|
|
@@ -1521,8 +1994,8 @@ function parseWinner(spec) {
|
|
|
1521
1994
|
const submissionId = submissionIdStr ? submissionIdStr : void 0;
|
|
1522
1995
|
return { worker, share, ...submissionId !== void 0 && { submissionId } };
|
|
1523
1996
|
}
|
|
1524
|
-
var acceptSubmissionsCmd = new
|
|
1525
|
-
"Accept N submissions for a Bounty/Benchmark task with explicit share basis points (costs 0.001
|
|
1997
|
+
var acceptSubmissionsCmd = new Command12("accept-submissions").description(
|
|
1998
|
+
"Accept N submissions for a Bounty/Benchmark task with explicit share basis points (costs 0.001 in the payment token).\nShares must sum to 10000. The contract resolves each deliverable from on-chain submission history.\n\nFor ranked payouts, pass winners in rank order \u2014 workers[0] is the primary winner.\n\nTo pin a specific submission version, append the submission ID as a third field.\n\nExamples:\n task accept-submissions 0x\u2026 --winner 0xAlice:5000 --winner 0xBob:3000 --winner 0xCarol:2000\n task accept-submissions 0x\u2026 --winner 0xAlice:5000:sub_abc123 --winner 0xBob:5000:sub_def456"
|
|
1526
1999
|
).argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption(
|
|
1527
2000
|
"--winner <spec...>",
|
|
1528
2001
|
"Winner spec <addr>:<share> or <addr>:<share>:<submissionId>. Pass multiple times."
|
|
@@ -1540,15 +2013,22 @@ var acceptSubmissionsCmd = new Command11("accept-submissions").description(
|
|
|
1540
2013
|
winners
|
|
1541
2014
|
}
|
|
1542
2015
|
);
|
|
1543
|
-
printResult(
|
|
2016
|
+
printResult(
|
|
2017
|
+
{
|
|
2018
|
+
accepted: result.success,
|
|
2019
|
+
winners: winners.length,
|
|
2020
|
+
...await settleRecording(result, idempotencyKey)
|
|
2021
|
+
},
|
|
2022
|
+
{ idempotencyKey }
|
|
2023
|
+
);
|
|
1544
2024
|
} catch (err) {
|
|
1545
2025
|
renderFailure(err);
|
|
1546
2026
|
}
|
|
1547
2027
|
});
|
|
1548
2028
|
|
|
1549
2029
|
// src/commands/task/rate.ts
|
|
1550
|
-
import { Command as
|
|
1551
|
-
var rateCmd = new
|
|
2030
|
+
import { Command as Command13 } from "commander";
|
|
2031
|
+
var rateCmd = new Command13("rate").description("Rate a worker (costs 0.001 in the payment token)").argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption("--worker <addr>", "Worker wallet address").requiredOption("--rating <n>", "Rating 0-100").option("--feedback <text>", "Optional feedback text").action(async (taskId, opts) => {
|
|
1552
2032
|
const rating = parseInt(opts.rating, 10);
|
|
1553
2033
|
if (rating < 0 || rating > 100) {
|
|
1554
2034
|
printError("Rating must be between 0 and 100");
|
|
@@ -1560,22 +2040,92 @@ var rateCmd = new Command12("rate").description("Rate a worker (costs 0.001 USDC
|
|
|
1560
2040
|
rating,
|
|
1561
2041
|
...opts.feedback ? { feedbackText: opts.feedback } : {}
|
|
1562
2042
|
});
|
|
1563
|
-
printResult(
|
|
2043
|
+
printResult(
|
|
2044
|
+
{ feedbackId: result.feedbackId, ...await settleRecording(result, idempotencyKey) },
|
|
2045
|
+
{ idempotencyKey }
|
|
2046
|
+
);
|
|
1564
2047
|
});
|
|
1565
2048
|
|
|
1566
2049
|
// src/commands/task/claim.ts
|
|
1567
|
-
import { Command as
|
|
1568
|
-
|
|
2050
|
+
import { Command as Command14 } from "commander";
|
|
2051
|
+
|
|
2052
|
+
// src/lib/stake-confirmation.ts
|
|
2053
|
+
async function confirmStakeTerms(action, taskId, yes) {
|
|
2054
|
+
if (yes) return true;
|
|
2055
|
+
let detail;
|
|
2056
|
+
try {
|
|
2057
|
+
detail = await getTaskTerms(cliTaskReadContext(), { taskId });
|
|
2058
|
+
} catch {
|
|
2059
|
+
return true;
|
|
2060
|
+
}
|
|
2061
|
+
if (!detail?.terms.stakeRequired) return true;
|
|
2062
|
+
if (detail.amounts.enforcedStakeAmount === "0") return true;
|
|
2063
|
+
const stakeAmount = detail.amounts.enforcedStakeAmount ?? detail.amounts.stakeAmount;
|
|
2064
|
+
renderFailure(
|
|
2065
|
+
new Error(
|
|
2066
|
+
`This task requires a stake of ${stakeAmount} base units (${detail.terms.stakeBps} bps of the reward) to ${action}. Review the terms, then re-run with --yes to accept them.`
|
|
2067
|
+
),
|
|
2068
|
+
{
|
|
2069
|
+
details: {
|
|
2070
|
+
code: "stake_confirmation_required",
|
|
2071
|
+
terms: detail.terms,
|
|
2072
|
+
amounts: detail.amounts,
|
|
2073
|
+
deadlines: detail.deadlines
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
);
|
|
2077
|
+
return false;
|
|
2078
|
+
}
|
|
2079
|
+
async function confirmStakeAffordable(address, stake) {
|
|
2080
|
+
if (stake <= 0n) return true;
|
|
2081
|
+
let balance;
|
|
2082
|
+
try {
|
|
2083
|
+
const result = await apiGet(
|
|
2084
|
+
`/api/wallet/balance?address=${address}&chainId=${selectedNetwork().chainId}`
|
|
2085
|
+
);
|
|
2086
|
+
if (typeof result?.balanceBaseUnits !== "string") return true;
|
|
2087
|
+
balance = BigInt(result.balanceBaseUnits);
|
|
2088
|
+
} catch {
|
|
2089
|
+
return true;
|
|
2090
|
+
}
|
|
2091
|
+
if (balance >= stake) return true;
|
|
2092
|
+
renderFailure(
|
|
2093
|
+
new Error(
|
|
2094
|
+
`This claim needs a stake of ${stake} base units, and the wallet holds ${balance}. Fund the wallet (taskmarket deposit), then claim again.`
|
|
2095
|
+
),
|
|
2096
|
+
{
|
|
2097
|
+
details: {
|
|
2098
|
+
code: "stake_balance_short",
|
|
2099
|
+
stakeAmount: stake.toString(),
|
|
2100
|
+
balance: balance.toString()
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
);
|
|
2104
|
+
return false;
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
// src/commands/task/claim.ts
|
|
2108
|
+
var claimCmd = new Command14("claim").description("Claim a task as a worker").argument("<taskId>", "Task ID (0x-prefixed hex)").option("--yes", "Accept the task's settlement terms, including paying any required stake").action(async (taskId, opts) => {
|
|
2109
|
+
if (!await confirmStakeTerms("claim", taskId, opts.yes)) return;
|
|
2110
|
+
const task = await getTask(cliTaskReadContext(), { taskId });
|
|
2111
|
+
const stake = task ? claimStakeToPost(task) : 0n;
|
|
1569
2112
|
const keystore = await loadKeystore();
|
|
2113
|
+
if (!await confirmStakeAffordable(keystore.walletAddress, stake)) return;
|
|
1570
2114
|
const { claimId, idempotencyKey } = await claimTask(cliTaskWriteContext(keystore), { taskId });
|
|
1571
|
-
printResult(
|
|
2115
|
+
printResult(
|
|
2116
|
+
{ claimId, ...stake > 0n ? { stake: stake.toString() } : {} },
|
|
2117
|
+
{ idempotencyKey }
|
|
2118
|
+
);
|
|
1572
2119
|
});
|
|
1573
2120
|
|
|
1574
2121
|
// src/commands/task/pitch.ts
|
|
1575
|
-
import { Command as
|
|
1576
|
-
var pitchCmd = new
|
|
2122
|
+
import { Command as Command15 } from "commander";
|
|
2123
|
+
var pitchCmd = new Command15("pitch").description(
|
|
2124
|
+
"Submit a pitch for a task (costs 0.001 in the payment token, anchors hash on-chain)"
|
|
2125
|
+
).argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption("--text <text>", "Pitch text").option("--duration <hours>", "Estimated duration in hours").option("--yes", "Accept the task's settlement terms, including any required stake").action(async (taskId, opts) => {
|
|
2126
|
+
if (!await confirmStakeTerms("pitch", taskId, opts.yes)) return;
|
|
1577
2127
|
const keystore = await loadKeystore();
|
|
1578
|
-
const signature = await signMessage(
|
|
2128
|
+
const signature = await signMessage(buildPitchMessage(taskId), keystore);
|
|
1579
2129
|
const { data: result, idempotencyKey } = await x402Post(
|
|
1580
2130
|
`/api/tasks/${taskId}/pitches`,
|
|
1581
2131
|
{
|
|
@@ -1590,8 +2140,8 @@ var pitchCmd = new Command14("pitch").description("Submit a pitch for a task (co
|
|
|
1590
2140
|
});
|
|
1591
2141
|
|
|
1592
2142
|
// src/commands/task/pitches.ts
|
|
1593
|
-
import { Command as
|
|
1594
|
-
var pitchesCmd = new
|
|
2143
|
+
import { Command as Command16 } from "commander";
|
|
2144
|
+
var pitchesCmd = new Command16("pitches").description("List pitches for a task").argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
1595
2145
|
try {
|
|
1596
2146
|
const auth = await signReadAuth();
|
|
1597
2147
|
const headers = { ...auth?.headers ?? {}, ...await taskAccessGrantHeaders(taskId) };
|
|
@@ -1602,25 +2152,28 @@ var pitchesCmd = new Command15("pitches").description("List pitches for a task")
|
|
|
1602
2152
|
});
|
|
1603
2153
|
|
|
1604
2154
|
// src/commands/task/select-worker.ts
|
|
1605
|
-
import { Command as
|
|
1606
|
-
var selectWorkerCmd = new
|
|
2155
|
+
import { Command as Command17 } from "commander";
|
|
2156
|
+
var selectWorkerCmd = new Command17("select-worker").description("Select a worker from pitches (requester only)").argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption("--pitch <pitchId>", "Pitch ID to select").requiredOption("--worker <address>", "Worker wallet address to assign").action(async (taskId, opts) => {
|
|
1607
2157
|
const keystore = await loadKeystore();
|
|
1608
2158
|
const message = buildSelectWorkerMessage(taskId, opts.pitch, opts.worker);
|
|
1609
2159
|
const signature = await signMessage(message, keystore);
|
|
1610
|
-
const { idempotencyKey } = await x402Post(`/api/tasks/${taskId}/pitches/select`, {
|
|
2160
|
+
const { data: result, idempotencyKey } = await x402Post(`/api/tasks/${taskId}/pitches/select`, {
|
|
1611
2161
|
taskId,
|
|
1612
2162
|
pitchId: opts.pitch,
|
|
1613
2163
|
workerAddress: opts.worker,
|
|
1614
2164
|
signature
|
|
1615
2165
|
});
|
|
1616
|
-
printResult(
|
|
2166
|
+
printResult(
|
|
2167
|
+
{ selected: true, ...await settleRecording(result, idempotencyKey) },
|
|
2168
|
+
{ idempotencyKey }
|
|
2169
|
+
);
|
|
1617
2170
|
});
|
|
1618
2171
|
|
|
1619
2172
|
// src/commands/task/proof.ts
|
|
1620
|
-
import { Command as
|
|
1621
|
-
var proofCmd = new
|
|
2173
|
+
import { Command as Command18 } from "commander";
|
|
2174
|
+
var proofCmd = new Command18("proof").description("Submit a benchmark proof (costs 0.001 in the payment token, anchors hash on-chain)").argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption("--data <data>", "Proof data").requiredOption("--type <type>", "Proof type").option("--metric <val>", "Metric value").action(async (taskId, opts) => {
|
|
1622
2175
|
const keystore = await loadKeystore();
|
|
1623
|
-
const signature = await signMessage(
|
|
2176
|
+
const signature = await signMessage(buildProofMessage(taskId), keystore);
|
|
1624
2177
|
const { data: result, idempotencyKey } = await x402Post(`/api/tasks/${taskId}/proofs`, {
|
|
1625
2178
|
taskId,
|
|
1626
2179
|
workerAddress: keystore.walletAddress,
|
|
@@ -1633,8 +2186,8 @@ var proofCmd = new Command17("proof").description("Submit a benchmark proof (cos
|
|
|
1633
2186
|
});
|
|
1634
2187
|
|
|
1635
2188
|
// src/commands/task/proofs.ts
|
|
1636
|
-
import { Command as
|
|
1637
|
-
var proofsCmd = new
|
|
2189
|
+
import { Command as Command19 } from "commander";
|
|
2190
|
+
var proofsCmd = new Command19("proofs").description("List proofs for a benchmark task").argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
1638
2191
|
try {
|
|
1639
2192
|
const auth = await signReadAuth();
|
|
1640
2193
|
const headers = { ...auth?.headers ?? {}, ...await taskAccessGrantHeaders(taskId) };
|
|
@@ -1645,8 +2198,8 @@ var proofsCmd = new Command18("proofs").description("List proofs for a benchmark
|
|
|
1645
2198
|
});
|
|
1646
2199
|
|
|
1647
2200
|
// src/commands/task/bid.ts
|
|
1648
|
-
import { Command as
|
|
1649
|
-
var bidCmd = new
|
|
2201
|
+
import { Command as Command20 } from "commander";
|
|
2202
|
+
var bidCmd = new Command20("bid").description("Submit a bid on an auction task").argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption("--price <amount>", "Bid price in the payment token (e.g. 3 or 1.5)").option("--yes", "Accept the task's settlement terms, including any required stake").action(async (taskId, opts) => {
|
|
1650
2203
|
let priceBaseUnits;
|
|
1651
2204
|
try {
|
|
1652
2205
|
priceBaseUnits = usdcToBaseUnits(opts.price);
|
|
@@ -1654,6 +2207,7 @@ var bidCmd = new Command19("bid").description("Submit a bid on an auction task")
|
|
|
1654
2207
|
renderFailure(withErrorContext(err, "Invalid --price"));
|
|
1655
2208
|
return;
|
|
1656
2209
|
}
|
|
2210
|
+
if (!await confirmStakeTerms("bid", taskId, opts.yes)) return;
|
|
1657
2211
|
const { data: result, idempotencyKey } = await x402Post(
|
|
1658
2212
|
`/api/tasks/${taskId}/bids`,
|
|
1659
2213
|
{
|
|
@@ -1665,9 +2219,9 @@ var bidCmd = new Command19("bid").description("Submit a bid on an auction task")
|
|
|
1665
2219
|
});
|
|
1666
2220
|
|
|
1667
2221
|
// src/commands/task/auction-accept.ts
|
|
1668
|
-
import { Command as
|
|
1669
|
-
var auctionAcceptCmd = new
|
|
1670
|
-
"--min-price <
|
|
2222
|
+
import { Command as Command21 } from "commander";
|
|
2223
|
+
var auctionAcceptCmd = new Command21("auction-accept").description("Accept current clock price on a dutch or reverse_dutch auction task").argument("<taskId>", "Task ID (0x-prefixed hex)").option(
|
|
2224
|
+
"--min-price <amount>",
|
|
1671
2225
|
"Reject if current clock price is below this value (price floor guard)"
|
|
1672
2226
|
).action(async (taskId, opts) => {
|
|
1673
2227
|
const body = { taskId };
|
|
@@ -1680,19 +2234,24 @@ var auctionAcceptCmd = new Command20("auction-accept").description("Accept curre
|
|
|
1680
2234
|
}
|
|
1681
2235
|
}
|
|
1682
2236
|
const { data: result, idempotencyKey } = await x402Post(`/api/tasks/${taskId}/bids/accept`, body);
|
|
2237
|
+
const { chainId } = selectedNetwork();
|
|
1683
2238
|
printResult(
|
|
1684
2239
|
{
|
|
1685
2240
|
acceptedPrice: result.acceptedPrice,
|
|
2241
|
+
// Field name kept for scripts; the number is in the selected network's payment token.
|
|
1686
2242
|
acceptedPriceUsdc: formatUsdcBaseUnits(result.acceptedPrice),
|
|
1687
|
-
|
|
2243
|
+
acceptedPriceFormatted: formatPaymentAmount(result.acceptedPrice, chainId),
|
|
2244
|
+
tokenSymbol: paymentTokenSymbol(chainId),
|
|
2245
|
+
workerAddress: result.workerAddress,
|
|
2246
|
+
...await settleRecording(result, idempotencyKey)
|
|
1688
2247
|
},
|
|
1689
2248
|
{ idempotencyKey }
|
|
1690
2249
|
);
|
|
1691
2250
|
});
|
|
1692
2251
|
|
|
1693
2252
|
// src/commands/task/submissions.ts
|
|
1694
|
-
import { Command as
|
|
1695
|
-
var submissionsCmd = new
|
|
2253
|
+
import { Command as Command22 } from "commander";
|
|
2254
|
+
var submissionsCmd = new Command22("submissions").description("List submissions for a task").argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
1696
2255
|
let subs;
|
|
1697
2256
|
try {
|
|
1698
2257
|
subs = await listTaskSubmissions(cliTaskReadContext(), { taskId });
|
|
@@ -1704,8 +2263,8 @@ var submissionsCmd = new Command21("submissions").description("List submissions
|
|
|
1704
2263
|
});
|
|
1705
2264
|
|
|
1706
2265
|
// src/commands/task/select-winner.ts
|
|
1707
|
-
import { Command as
|
|
1708
|
-
var selectWinnerCmd = new
|
|
2266
|
+
import { Command as Command23 } from "commander";
|
|
2267
|
+
var selectWinnerCmd = new Command23("select-winner").description("Permissionlessly finalize the lowest bidder after the auction deadline").argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
1709
2268
|
let result;
|
|
1710
2269
|
let idempotencyKey;
|
|
1711
2270
|
try {
|
|
@@ -1714,19 +2273,38 @@ var selectWinnerCmd = new Command22("select-winner").description("Permissionless
|
|
|
1714
2273
|
{ taskId }
|
|
1715
2274
|
));
|
|
1716
2275
|
} catch (err) {
|
|
1717
|
-
renderFailure(err, { fallback: "Failed to select winner." });
|
|
1718
|
-
return;
|
|
2276
|
+
renderFailure(err, { fallback: "Failed to select winner." });
|
|
2277
|
+
return;
|
|
2278
|
+
}
|
|
2279
|
+
printResult(result, { idempotencyKey });
|
|
2280
|
+
});
|
|
2281
|
+
|
|
2282
|
+
// src/commands/task/start-review.ts
|
|
2283
|
+
import { Command as Command24 } from "commander";
|
|
2284
|
+
var startReviewCmd = new Command24("start-review").description(
|
|
2285
|
+
"Start the review window on a delivery made before the settlement-terms upgrade that has none yet (anyone may call). Once the window lapses undecided, `task settle` pays the worker."
|
|
2286
|
+
).argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
2287
|
+
try {
|
|
2288
|
+
const { data: result, idempotencyKey } = await apiPost(
|
|
2289
|
+
`/api/tasks/${taskId}/start-review`,
|
|
2290
|
+
{ taskId }
|
|
2291
|
+
);
|
|
2292
|
+
printResult(
|
|
2293
|
+
{ ...result, ...await settleRecording(result, idempotencyKey) },
|
|
2294
|
+
{ idempotencyKey }
|
|
2295
|
+
);
|
|
2296
|
+
} catch (err) {
|
|
2297
|
+
renderFailure(err);
|
|
1719
2298
|
}
|
|
1720
|
-
printResult(result, { idempotencyKey });
|
|
1721
2299
|
});
|
|
1722
2300
|
|
|
1723
2301
|
// src/commands/task/download.ts
|
|
1724
|
-
import { Command as
|
|
2302
|
+
import { Command as Command25 } from "commander";
|
|
1725
2303
|
import { writeFileSync } from "fs";
|
|
1726
2304
|
function sanitizeForTerminal(text) {
|
|
1727
2305
|
return text.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
|
|
1728
2306
|
}
|
|
1729
|
-
var downloadCmd = new
|
|
2307
|
+
var downloadCmd = new Command25("download").description("Download a submission file (requester or worker)").argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption("--submission <id>", "Submission ID (from `task submissions`)").option("--artifact <id>", "Artifact ID for multi-artifact submissions").option("--output <path>", "Save to file instead of printing to stdout").action(
|
|
1730
2308
|
async (taskId, opts) => {
|
|
1731
2309
|
const keystore = await loadKeystore();
|
|
1732
2310
|
let presignedUrl;
|
|
@@ -1774,36 +2352,90 @@ var downloadCmd = new Command23("download").description("Download a submission f
|
|
|
1774
2352
|
);
|
|
1775
2353
|
|
|
1776
2354
|
// src/commands/task/cancel.ts
|
|
1777
|
-
import { Command as
|
|
1778
|
-
|
|
2355
|
+
import { Command as Command26 } from "commander";
|
|
2356
|
+
|
|
2357
|
+
// src/lib/decision-window.ts
|
|
2358
|
+
var DECISION_WINDOW_LAPSED_CODE = "decision_window_lapsed";
|
|
2359
|
+
async function decisionWindowLapse(taskId, options) {
|
|
2360
|
+
try {
|
|
2361
|
+
const detail = await getTaskTerms(cliTaskReadContext(), { taskId });
|
|
2362
|
+
if (!detail || detail.chainSupportsTerms !== true) return null;
|
|
2363
|
+
const deadline = detail.deadlines?.decisionDeadline;
|
|
2364
|
+
if (!deadline) return null;
|
|
2365
|
+
if (Date.parse(deadline) > (options.now ?? Date.now())) return null;
|
|
2366
|
+
if (options.requireLiveSubmissions) {
|
|
2367
|
+
const submissions = await apiGet(`/api/tasks/${taskId}/submissions`);
|
|
2368
|
+
if (!Array.isArray(submissions) || !submissions.some((s) => s.rejectedAt == null)) {
|
|
2369
|
+
return null;
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
return { decisionDeadline: deadline };
|
|
2373
|
+
} catch {
|
|
2374
|
+
return null;
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
function renderDecisionWindowLapsed(action, lapse) {
|
|
2378
|
+
renderFailure(
|
|
2379
|
+
new Error(
|
|
2380
|
+
`The decision window closed at ${lapse.decisionDeadline} with submissions still live: ${action} would revert on chain (DecisionWindowLapsed). Accept a winner to settle the task.`
|
|
2381
|
+
),
|
|
2382
|
+
{ details: { code: DECISION_WINDOW_LAPSED_CODE, decisionDeadline: lapse.decisionDeadline } }
|
|
2383
|
+
);
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
// src/commands/task/cancel.ts
|
|
2387
|
+
var cancelCmd = new Command26("cancel").description(
|
|
2388
|
+
"Cancel an open task and refund the escrowed reward (costs 0.001 in the payment token)"
|
|
2389
|
+
).argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
2390
|
+
const lapse = await decisionWindowLapse(taskId, { requireLiveSubmissions: true });
|
|
2391
|
+
if (lapse) {
|
|
2392
|
+
renderDecisionWindowLapsed("cancel", lapse);
|
|
2393
|
+
return;
|
|
2394
|
+
}
|
|
1779
2395
|
const { data: result, idempotencyKey } = await x402Post(
|
|
1780
2396
|
`/api/tasks/${taskId}/cancel`,
|
|
1781
2397
|
{ taskId }
|
|
1782
2398
|
);
|
|
1783
|
-
printResult(
|
|
2399
|
+
printResult(
|
|
2400
|
+
{ ...result, ...await settleRecording(result, idempotencyKey) },
|
|
2401
|
+
{ idempotencyKey }
|
|
2402
|
+
);
|
|
1784
2403
|
});
|
|
1785
2404
|
|
|
1786
2405
|
// src/commands/task/refund-expired.ts
|
|
1787
|
-
import { Command as
|
|
1788
|
-
var refundExpiredCmd = new
|
|
1789
|
-
"Refund an expired task
|
|
2406
|
+
import { Command as Command27 } from "commander";
|
|
2407
|
+
var refundExpiredCmd = new Command27("refund-expired").description(
|
|
2408
|
+
"Refund an expired task back to the requester; bounty and benchmark tasks need every submission rejected first (costs 0.001 in the payment token)"
|
|
1790
2409
|
).argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
2410
|
+
const lapse = await decisionWindowLapse(taskId, { requireLiveSubmissions: true });
|
|
2411
|
+
if (lapse) {
|
|
2412
|
+
renderDecisionWindowLapsed("refund-expired", lapse);
|
|
2413
|
+
return;
|
|
2414
|
+
}
|
|
1791
2415
|
try {
|
|
1792
2416
|
const { data: result, idempotencyKey } = await x402Post(
|
|
1793
2417
|
`/api/tasks/${taskId}/refund-expired`,
|
|
1794
2418
|
{ taskId }
|
|
1795
2419
|
);
|
|
1796
|
-
printResult(
|
|
2420
|
+
printResult(
|
|
2421
|
+
{ ...result, ...await settleRecording(result, idempotencyKey) },
|
|
2422
|
+
{ idempotencyKey }
|
|
2423
|
+
);
|
|
1797
2424
|
} catch (err) {
|
|
1798
2425
|
renderFailure(err);
|
|
1799
2426
|
}
|
|
1800
2427
|
});
|
|
1801
2428
|
|
|
1802
2429
|
// src/commands/task/reject-submission.ts
|
|
1803
|
-
import { Command as
|
|
1804
|
-
var rejectSubmissionCmd = new
|
|
1805
|
-
"Reject a spam or low-quality submission (costs 0.001
|
|
2430
|
+
import { Command as Command28 } from "commander";
|
|
2431
|
+
var rejectSubmissionCmd = new Command28("reject-submission").description(
|
|
2432
|
+
"Reject a spam or low-quality submission (costs a 0.001 relay fee in the payment token). Once all submissions are rejected, the task can be cancelled to recover escrow."
|
|
1806
2433
|
).argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption("--worker <address>", "Worker address whose submission to reject").action(async (taskId, opts) => {
|
|
2434
|
+
const lapse = await decisionWindowLapse(taskId, { requireLiveSubmissions: false });
|
|
2435
|
+
if (lapse) {
|
|
2436
|
+
renderDecisionWindowLapsed("reject-submission", lapse);
|
|
2437
|
+
return;
|
|
2438
|
+
}
|
|
1807
2439
|
try {
|
|
1808
2440
|
const { data: result, idempotencyKey } = await x402Post(
|
|
1809
2441
|
`/api/tasks/${taskId}/reject-submission`,
|
|
@@ -1812,14 +2444,17 @@ var rejectSubmissionCmd = new Command26("reject-submission").description(
|
|
|
1812
2444
|
worker: opts.worker
|
|
1813
2445
|
}
|
|
1814
2446
|
);
|
|
1815
|
-
printResult(
|
|
2447
|
+
printResult(
|
|
2448
|
+
{ ...result, ...await settleRecording(result, idempotencyKey) },
|
|
2449
|
+
{ idempotencyKey }
|
|
2450
|
+
);
|
|
1816
2451
|
} catch (err) {
|
|
1817
2452
|
renderFailure(err);
|
|
1818
2453
|
}
|
|
1819
2454
|
});
|
|
1820
2455
|
|
|
1821
2456
|
// src/commands/task/reject-all-submissions.ts
|
|
1822
|
-
import { Command as
|
|
2457
|
+
import { Command as Command29 } from "commander";
|
|
1823
2458
|
function activeSubmissionWorkers(submissions) {
|
|
1824
2459
|
const workers = /* @__PURE__ */ new Map();
|
|
1825
2460
|
for (const submission of submissions) {
|
|
@@ -1829,7 +2464,7 @@ function activeSubmissionWorkers(submissions) {
|
|
|
1829
2464
|
}
|
|
1830
2465
|
return [...workers.values()];
|
|
1831
2466
|
}
|
|
1832
|
-
var rejectAllSubmissionsCmd = new
|
|
2467
|
+
var rejectAllSubmissionsCmd = new Command29("reject-all-submissions").description(
|
|
1833
2468
|
"Reject every active submission on a bounty or benchmark task in one go. Lists active submissions, rejects each unique worker in sequence, then cancels the task. Use this to recover escrow when all submitted work is unsuitable."
|
|
1834
2469
|
).argument("<taskId>", "Task ID (0x-prefixed hex)").option("--no-cancel", "Reject all submissions but do not cancel the task afterward").action(async (taskId, opts) => {
|
|
1835
2470
|
let submissions;
|
|
@@ -1844,6 +2479,11 @@ var rejectAllSubmissionsCmd = new Command27("reject-all-submissions").descriptio
|
|
|
1844
2479
|
printResult({ rejected: 0, message: "no active submissions to reject" });
|
|
1845
2480
|
return;
|
|
1846
2481
|
}
|
|
2482
|
+
const lapse = await decisionWindowLapse(taskId, { requireLiveSubmissions: false });
|
|
2483
|
+
if (lapse) {
|
|
2484
|
+
renderDecisionWindowLapsed("reject-all-submissions", lapse);
|
|
2485
|
+
return;
|
|
2486
|
+
}
|
|
1847
2487
|
const results = [];
|
|
1848
2488
|
for (const worker of workers) {
|
|
1849
2489
|
try {
|
|
@@ -1902,8 +2542,8 @@ var rejectAllSubmissionsCmd = new Command27("reject-all-submissions").descriptio
|
|
|
1902
2542
|
});
|
|
1903
2543
|
|
|
1904
2544
|
// src/commands/task/my-submissions.ts
|
|
1905
|
-
import { Command as
|
|
1906
|
-
var mySubmissionsCmd = new
|
|
2545
|
+
import { Command as Command30 } from "commander";
|
|
2546
|
+
var mySubmissionsCmd = new Command30("my-submissions").description("List all submissions made by your wallet across all tasks.").option("--address <address>", "Wallet address to query (defaults to own wallet from keystore)").action(async (opts) => {
|
|
1907
2547
|
try {
|
|
1908
2548
|
const result = await listWorkerSubmissions(cliTaskReadContext(), { address: opts.address });
|
|
1909
2549
|
printResult(result);
|
|
@@ -1915,8 +2555,8 @@ var mySubmissionsCmd = new Command28("my-submissions").description("List all sub
|
|
|
1915
2555
|
});
|
|
1916
2556
|
|
|
1917
2557
|
// src/commands/task/update.ts
|
|
1918
|
-
import { Command as
|
|
1919
|
-
var updateCmd = new
|
|
2558
|
+
import { Command as Command31 } from "commander";
|
|
2559
|
+
var updateCmd = new Command31("update").description("Update an open task (costs 0.001 plus any reward increase, in the payment token)").argument("<taskId>", "Task ID (0x-prefixed hex)").option("--reward <amount>", 'New reward in the payment token (human units, e.g. "50")').option("--extend-expiry <seconds>", "Extend expiry by this many seconds").option("--bid-deadline <iso>", "New bid deadline (ISO 8601)").option("--pitch-deadline <iso>", "New pitch deadline (ISO 8601)").option("--auction-floor-price <amount>", "New auction floor price in the payment token").option("--auction-start-price <amount>", "New auction start price in the payment token").option("--description <text>", "New description").option("--tags <csv>", "New tags (comma-separated)").option("--metric-description <text>", "New metric description").action(
|
|
1920
2560
|
async (taskId, opts) => {
|
|
1921
2561
|
const body = { taskId };
|
|
1922
2562
|
if (opts.reward !== void 0) {
|
|
@@ -2004,8 +2644,8 @@ var updateCmd = new Command29("update").description("Update an open task (costs
|
|
|
2004
2644
|
);
|
|
2005
2645
|
|
|
2006
2646
|
// src/commands/task/forfeit.ts
|
|
2007
|
-
import { Command as
|
|
2008
|
-
var forfeitCmd = new
|
|
2647
|
+
import { Command as Command32 } from "commander";
|
|
2648
|
+
var forfeitCmd = new Command32("forfeit").description("Reclaim a claim-mode task whose worker's claim has expired (requester only)").argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
2009
2649
|
try {
|
|
2010
2650
|
const keystore = await loadKeystore();
|
|
2011
2651
|
const message = buildForfeitMessage(taskId);
|
|
@@ -2018,18 +2658,24 @@ var forfeitCmd = new Command30("forfeit").description("Reclaim a claim-mode task
|
|
|
2018
2658
|
signature
|
|
2019
2659
|
}
|
|
2020
2660
|
);
|
|
2021
|
-
printResult(
|
|
2661
|
+
printResult(
|
|
2662
|
+
{ ...result, ...await settleRecording(result, idempotencyKey) },
|
|
2663
|
+
{ idempotencyKey }
|
|
2664
|
+
);
|
|
2022
2665
|
} catch (err) {
|
|
2023
2666
|
renderFailure(err);
|
|
2024
2667
|
}
|
|
2025
2668
|
});
|
|
2026
2669
|
|
|
2027
2670
|
// src/commands/task/assign-evaluator.ts
|
|
2028
|
-
import { Command as
|
|
2671
|
+
import { Command as Command33 } from "commander";
|
|
2029
2672
|
var ADDRESS_PATTERN = /^0x[0-9a-fA-F]{40}$/;
|
|
2030
|
-
var assignEvaluatorCmd = new
|
|
2031
|
-
"Assign an evaluator to an open, unclaimed task you requested (costs 0.001
|
|
2032
|
-
).argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption("--evaluator <address>", "Evaluator wallet address").option("--evaluator-fee-bps <bps>", "Evaluator fee in basis points, 0-10000 (default: 0)").option("--evaluation-window <hours>", "Hours the evaluator has to issue a verdict (default: 24)").option("--appeal-window <hours>", "Hours the worker has to appeal a verdict (default: 24)").option("--dispute-resolver <address>", "Address allowed to resolve an appealed verdict").
|
|
2673
|
+
var assignEvaluatorCmd = new Command33("assign-evaluator").description(
|
|
2674
|
+
"Assign an evaluator to an open, unclaimed task you requested (costs 0.001 in the payment token). Only possible before a worker claims the task."
|
|
2675
|
+
).argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption("--evaluator <address>", "Evaluator wallet address").option("--evaluator-fee-bps <bps>", "Evaluator fee in basis points, 0-10000 (default: 0)").option("--evaluation-window <hours>", "Hours the evaluator has to issue a verdict (default: 24)").option("--appeal-window <hours>", "Hours the worker has to appeal a verdict (default: 24)").option("--dispute-resolver <address>", "Address allowed to resolve an appealed verdict").option(
|
|
2676
|
+
"--evaluator-stake <amount>",
|
|
2677
|
+
"Stake behind the evaluator in the payment token (e.g. 1), paid with the action fee. Requires --yes."
|
|
2678
|
+
).option("--yes", "Confirm paying the evaluator stake", false).action(
|
|
2033
2679
|
async (taskId, opts) => {
|
|
2034
2680
|
if (!ADDRESS_PATTERN.test(opts.evaluator)) {
|
|
2035
2681
|
return void printError(`--evaluator must be a wallet address, got: ${opts.evaluator}`);
|
|
@@ -2039,7 +2685,23 @@ var assignEvaluatorCmd = new Command31("assign-evaluator").description(
|
|
|
2039
2685
|
`--dispute-resolver must be a wallet address, got: ${opts.disputeResolver}`
|
|
2040
2686
|
);
|
|
2041
2687
|
}
|
|
2688
|
+
let evaluatorStake = "0";
|
|
2689
|
+
if (opts.evaluatorStake !== void 0) {
|
|
2690
|
+
try {
|
|
2691
|
+
evaluatorStake = usdcToBaseUnits(opts.evaluatorStake, { allowZero: true });
|
|
2692
|
+
} catch (err) {
|
|
2693
|
+
renderFailure(withErrorContext(err, "Invalid --evaluator-stake"));
|
|
2694
|
+
return;
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
const staked = BigInt(evaluatorStake) > 0n;
|
|
2698
|
+
if (staked && !opts.yes) {
|
|
2699
|
+
return void printError(
|
|
2700
|
+
`This assignment stakes ${opts.evaluatorStake} (${evaluatorStake} base units) behind the evaluator, paid now with the action fee and forfeitable under the task's terms. Re-run with --yes to confirm.`
|
|
2701
|
+
);
|
|
2702
|
+
}
|
|
2042
2703
|
const body = { taskId, evaluator: opts.evaluator };
|
|
2704
|
+
if (staked) body.evaluatorStake = evaluatorStake;
|
|
2043
2705
|
if (opts.evaluatorFeeBps !== void 0) {
|
|
2044
2706
|
const bps = Number(opts.evaluatorFeeBps);
|
|
2045
2707
|
if (!Number.isInteger(bps) || bps < 0 || bps > 1e4) {
|
|
@@ -2074,16 +2736,19 @@ var assignEvaluatorCmd = new Command31("assign-evaluator").description(
|
|
|
2074
2736
|
`/api/tasks/${taskId}/evaluator`,
|
|
2075
2737
|
body
|
|
2076
2738
|
);
|
|
2077
|
-
printResult(
|
|
2739
|
+
printResult(
|
|
2740
|
+
{ ...result, ...await settleRecording(result, idempotencyKey) },
|
|
2741
|
+
{ idempotencyKey }
|
|
2742
|
+
);
|
|
2078
2743
|
}
|
|
2079
2744
|
);
|
|
2080
2745
|
|
|
2081
2746
|
// src/commands/task/evaluate.ts
|
|
2082
|
-
import { Command as
|
|
2083
|
-
var evaluateCmd = new
|
|
2747
|
+
import { Command as Command34 } from "commander";
|
|
2748
|
+
var evaluateCmd = new Command34("evaluate").description("Submit an evaluation verdict for a task in Review state").argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption("--verdict <type>", "Verdict: approve, reject, or partial").option("--score <n>", "Score 0-1000 (default: 1000)", "1000").option("--confidence <n>", "Confidence 0-1000 (default: 1000)", "1000").option("--evidence-hash <hash>", "Evidence hash (32-byte hex)").option(
|
|
2084
2749
|
"--award <worker:amount:rank>",
|
|
2085
|
-
"Award entry (repeatable): worker:
|
|
2086
|
-
|
|
2750
|
+
"Award entry (repeatable): worker:amount:rank",
|
|
2751
|
+
collect2,
|
|
2087
2752
|
[]
|
|
2088
2753
|
).action(
|
|
2089
2754
|
async (taskId, opts) => {
|
|
@@ -2095,11 +2760,9 @@ var evaluateCmd = new Command32("evaluate").description("Submit an evaluation ve
|
|
|
2095
2760
|
for (const entry of opts.award) {
|
|
2096
2761
|
const parts = entry.split(":");
|
|
2097
2762
|
if (parts.length < 3 || !parts[0] || !parts[1] || !parts[2]) {
|
|
2098
|
-
return void printError(
|
|
2099
|
-
`Invalid award format '${entry}'. Expected worker:amountUSDC:rank`
|
|
2100
|
-
);
|
|
2763
|
+
return void printError(`Invalid award format '${entry}'. Expected worker:amount:rank`);
|
|
2101
2764
|
}
|
|
2102
|
-
const [worker,
|
|
2765
|
+
const [worker, amount, rankStr] = parts;
|
|
2103
2766
|
if (!/^0x[0-9a-fA-F]{40}$/.test(worker)) {
|
|
2104
2767
|
return void printError(
|
|
2105
2768
|
`Invalid worker address in '${entry}': must be 0x-prefixed 40-char hex`
|
|
@@ -2107,7 +2770,7 @@ var evaluateCmd = new Command32("evaluate").description("Submit an evaluation ve
|
|
|
2107
2770
|
}
|
|
2108
2771
|
let microUnits;
|
|
2109
2772
|
try {
|
|
2110
|
-
microUnits = usdcToBaseUnits(
|
|
2773
|
+
microUnits = usdcToBaseUnits(amount);
|
|
2111
2774
|
} catch (err) {
|
|
2112
2775
|
renderFailure(withErrorContext(err, `Invalid amount in '${entry}'`));
|
|
2113
2776
|
return;
|
|
@@ -2137,40 +2800,52 @@ var evaluateCmd = new Command32("evaluate").description("Submit an evaluation ve
|
|
|
2137
2800
|
`/api/tasks/${taskId}/evaluate`,
|
|
2138
2801
|
body
|
|
2139
2802
|
);
|
|
2140
|
-
printResult(
|
|
2803
|
+
printResult(
|
|
2804
|
+
{ txHash: result.txHash, ...await settleRecording(result, idempotencyKey) },
|
|
2805
|
+
{ idempotencyKey }
|
|
2806
|
+
);
|
|
2141
2807
|
}
|
|
2142
2808
|
);
|
|
2143
|
-
function
|
|
2809
|
+
function collect2(val, prev) {
|
|
2144
2810
|
return prev.concat([val]);
|
|
2145
2811
|
}
|
|
2146
2812
|
|
|
2147
2813
|
// src/commands/task/appeal.ts
|
|
2148
|
-
import { Command as
|
|
2149
|
-
var appealCmd = new
|
|
2814
|
+
import { Command as Command35 } from "commander";
|
|
2815
|
+
var appealCmd = new Command35("appeal").description("Appeal an evaluator verdict while in Appealing state").argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
2150
2816
|
const { data: result, idempotencyKey } = await x402Post(`/api/tasks/${taskId}/appeal`, { taskId });
|
|
2151
|
-
printResult(
|
|
2817
|
+
printResult(
|
|
2818
|
+
{ txHash: result.txHash, ...await settleRecording(result, idempotencyKey) },
|
|
2819
|
+
{ idempotencyKey }
|
|
2820
|
+
);
|
|
2152
2821
|
});
|
|
2153
2822
|
|
|
2154
2823
|
// src/commands/task/evaluator-timeout.ts
|
|
2155
|
-
import { Command as
|
|
2156
|
-
var evaluatorTimeoutCmd = new
|
|
2824
|
+
import { Command as Command36 } from "commander";
|
|
2825
|
+
var evaluatorTimeoutCmd = new Command36("evaluator-timeout").description("Trigger evaluator timeout after the evaluation window expires (anyone)").argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
2157
2826
|
const { data: result, idempotencyKey } = await x402Post(`/api/tasks/${taskId}/evaluator-timeout`, { taskId });
|
|
2158
|
-
printResult(
|
|
2827
|
+
printResult(
|
|
2828
|
+
{ txHash: result.txHash, ...await settleRecording(result, idempotencyKey) },
|
|
2829
|
+
{ idempotencyKey }
|
|
2830
|
+
);
|
|
2159
2831
|
});
|
|
2160
2832
|
|
|
2161
2833
|
// src/commands/task/finalize-verdict.ts
|
|
2162
|
-
import { Command as
|
|
2163
|
-
var finalizeVerdictCmd = new
|
|
2834
|
+
import { Command as Command37 } from "commander";
|
|
2835
|
+
var finalizeVerdictCmd = new Command37("finalize-verdict").description("Finalize an evaluator verdict after the appeal window expires (permissionless)").argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
2164
2836
|
const { data: result, idempotencyKey } = await apiPost(`/api/tasks/${taskId}/finalize-verdict`, { taskId });
|
|
2165
|
-
printResult(
|
|
2837
|
+
printResult(
|
|
2838
|
+
{ txHash: result.txHash, ...await settleRecording(result, idempotencyKey) },
|
|
2839
|
+
{ idempotencyKey }
|
|
2840
|
+
);
|
|
2166
2841
|
});
|
|
2167
2842
|
|
|
2168
2843
|
// src/commands/task/resolve-dispute.ts
|
|
2169
|
-
import { Command as
|
|
2844
|
+
import { Command as Command38 } from "commander";
|
|
2170
2845
|
function parseAward(spec) {
|
|
2171
2846
|
const parts = spec.split(":");
|
|
2172
2847
|
if (parts.length !== 3) {
|
|
2173
|
-
throw new Error(`Invalid --award value "${spec}". Expected <addr>:<
|
|
2848
|
+
throw new Error(`Invalid --award value "${spec}". Expected <addr>:<amount>:<rank>`);
|
|
2174
2849
|
}
|
|
2175
2850
|
const [worker, amountStr, rankStr] = parts;
|
|
2176
2851
|
if (!/^0x[0-9a-fA-F]{40}$/.test(worker)) {
|
|
@@ -2187,11 +2862,11 @@ function parseAward(spec) {
|
|
|
2187
2862
|
rank
|
|
2188
2863
|
};
|
|
2189
2864
|
}
|
|
2190
|
-
var resolveDisputeCmd = new
|
|
2865
|
+
var resolveDisputeCmd = new Command38("resolve-dispute").description(
|
|
2191
2866
|
"Resolve a disputed task as the designated dispute resolver (X402 required).\n\nExamples:\n task resolve-dispute 0x\u2026 --verdict approve --award 0xAlice:5:1\n task resolve-dispute 0x\u2026 --verdict partial --award 0xAlice:3:1 --award 0xBob:2:2"
|
|
2192
2867
|
).argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption("--verdict <type>", "Verdict type: approve or partial").requiredOption(
|
|
2193
2868
|
"--award <spec...>",
|
|
2194
|
-
"Award spec <addr>:<
|
|
2869
|
+
"Award spec <addr>:<amount>:<rank>, amount in whole payment tokens (USDC on Base, OUSD on Tempo). Pass multiple times."
|
|
2195
2870
|
).action(async (taskId, opts) => {
|
|
2196
2871
|
if (opts.verdict !== "approve" && opts.verdict !== "partial") {
|
|
2197
2872
|
printError('--verdict must be "approve" or "partial"');
|
|
@@ -2212,12 +2887,15 @@ var resolveDisputeCmd = new Command36("resolve-dispute").description(
|
|
|
2212
2887
|
awards
|
|
2213
2888
|
}
|
|
2214
2889
|
);
|
|
2215
|
-
printResult(
|
|
2890
|
+
printResult(
|
|
2891
|
+
{ txHash: result.txHash, ...await settleRecording(result, idempotencyKey) },
|
|
2892
|
+
{ idempotencyKey }
|
|
2893
|
+
);
|
|
2216
2894
|
});
|
|
2217
2895
|
|
|
2218
2896
|
// src/commands/task/unlock.ts
|
|
2219
|
-
import { Command as
|
|
2220
|
-
var unlockCmd = new
|
|
2897
|
+
import { Command as Command39 } from "commander";
|
|
2898
|
+
var unlockCmd = new Command39("unlock").description("Unlock a private task with its password, caching a task-scoped access grant").argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption("--password <password>", "The private task's password").action(async (taskId, opts) => {
|
|
2221
2899
|
let result;
|
|
2222
2900
|
let idempotencyKey;
|
|
2223
2901
|
try {
|
|
@@ -2234,8 +2912,8 @@ var unlockCmd = new Command37("unlock").description("Unlock a private task with
|
|
|
2234
2912
|
});
|
|
2235
2913
|
|
|
2236
2914
|
// src/commands/task/invite.ts
|
|
2237
|
-
import { Command as
|
|
2238
|
-
var inviteCmd = new
|
|
2915
|
+
import { Command as Command40 } from "commander";
|
|
2916
|
+
var inviteCmd = new Command40("invite").description("Invite a wallet to view a private task (requester only)").argument("<taskId>", "Task ID (0x-prefixed hex)").argument("<address>", "Wallet address to invite").action(async (taskId, address) => {
|
|
2239
2917
|
const auth = await signReadAuth();
|
|
2240
2918
|
if (!auth || Object.keys(auth.headers).length === 0) {
|
|
2241
2919
|
printError("No keystore found. Run `taskmarket init` first.");
|
|
@@ -2254,8 +2932,8 @@ var inviteCmd = new Command38("invite").description("Invite a wallet to view a p
|
|
|
2254
2932
|
});
|
|
2255
2933
|
|
|
2256
2934
|
// src/commands/task/uninvite.ts
|
|
2257
|
-
import { Command as
|
|
2258
|
-
var uninviteCmd = new
|
|
2935
|
+
import { Command as Command41 } from "commander";
|
|
2936
|
+
var uninviteCmd = new Command41("uninvite").description("Remove a wallet from a private task's allowlist (requester only)").argument("<taskId>", "Task ID (0x-prefixed hex)").argument("<address>", "Wallet address to remove").action(async (taskId, address) => {
|
|
2259
2937
|
const auth = await signReadAuth();
|
|
2260
2938
|
if (!auth || Object.keys(auth.headers).length === 0) {
|
|
2261
2939
|
printError("No keystore found. Run `taskmarket init` first.");
|
|
@@ -2273,8 +2951,8 @@ var uninviteCmd = new Command39("uninvite").description("Remove a wallet from a
|
|
|
2273
2951
|
});
|
|
2274
2952
|
|
|
2275
2953
|
// src/commands/task/viewers.ts
|
|
2276
|
-
import { Command as
|
|
2277
|
-
var viewersCmd = new
|
|
2954
|
+
import { Command as Command42 } from "commander";
|
|
2955
|
+
var viewersCmd = new Command42("viewers").description("List a private task's wallet allowlist (requester only)").argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
2278
2956
|
const auth = await signReadAuth();
|
|
2279
2957
|
if (!auth || Object.keys(auth.headers).length === 0) {
|
|
2280
2958
|
printError("No keystore found. Run `taskmarket init` first.");
|
|
@@ -2290,49 +2968,173 @@ var viewersCmd = new Command40("viewers").description("List a private task's wal
|
|
|
2290
2968
|
}
|
|
2291
2969
|
});
|
|
2292
2970
|
|
|
2971
|
+
// src/commands/task/terms.ts
|
|
2972
|
+
import { Command as Command43 } from "commander";
|
|
2973
|
+
var termsCmd = new Command43("terms").description(
|
|
2974
|
+
"Show a task's settlement terms: stake, review window, reject fee and outcome, revisions, release cost now and deadlines"
|
|
2975
|
+
).argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
2976
|
+
const detail = await getTaskTerms(cliTaskReadContext(), { taskId });
|
|
2977
|
+
if (!detail) {
|
|
2978
|
+
printError(
|
|
2979
|
+
`Task not found: ${taskId} (or this is a private task you don't have access to -- if you have a password for it, run: taskmarket task unlock ${taskId} --password <password>)`
|
|
2980
|
+
);
|
|
2981
|
+
return;
|
|
2982
|
+
}
|
|
2983
|
+
printResult(detail);
|
|
2984
|
+
});
|
|
2985
|
+
|
|
2986
|
+
// src/lib/settlement-action.ts
|
|
2987
|
+
import { Command as Command44 } from "commander";
|
|
2988
|
+
async function readTerms(taskId) {
|
|
2989
|
+
try {
|
|
2990
|
+
return await getTaskTerms(cliTaskReadContext(), { taskId });
|
|
2991
|
+
} catch {
|
|
2992
|
+
return null;
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
async function confirmSettlementAction(name, confirm2, taskId, yes) {
|
|
2996
|
+
if (!confirm2 || yes) return true;
|
|
2997
|
+
const detail = await readTerms(taskId);
|
|
2998
|
+
const termsDetails = detail ? { terms: detail.terms, amounts: detail.amounts, deadlines: detail.deadlines } : {};
|
|
2999
|
+
if (confirm2 === "release-cost") {
|
|
3000
|
+
if (!detail) {
|
|
3001
|
+
renderFailure(
|
|
3002
|
+
new Error(
|
|
3003
|
+
"Could not read the task terms, so the cost of releasing now is unknown. Retry, or re-run with --yes to release whatever it costs."
|
|
3004
|
+
),
|
|
3005
|
+
{ details: { code: "release_terms_unavailable" } }
|
|
3006
|
+
);
|
|
3007
|
+
return false;
|
|
3008
|
+
}
|
|
3009
|
+
const cost = detail.amounts.releaseCostNow;
|
|
3010
|
+
if (!cost || BigInt(cost) <= 0n) return true;
|
|
3011
|
+
renderFailure(
|
|
3012
|
+
new Error(
|
|
3013
|
+
`Releasing this claim now forfeits ${cost} base units of the stake. Review the terms, then re-run with --yes to accept the cost.`
|
|
3014
|
+
),
|
|
3015
|
+
{
|
|
3016
|
+
details: { code: "release_confirmation_required", releaseCostNow: cost, ...termsDetails }
|
|
3017
|
+
}
|
|
3018
|
+
);
|
|
3019
|
+
return false;
|
|
3020
|
+
}
|
|
3021
|
+
renderFailure(
|
|
3022
|
+
new Error(
|
|
3023
|
+
`task ${name} cannot be undone. Review the terms, then re-run with --yes to confirm.`
|
|
3024
|
+
),
|
|
3025
|
+
{ details: { code: `${name}_confirmation_required`, ...termsDetails } }
|
|
3026
|
+
);
|
|
3027
|
+
return false;
|
|
3028
|
+
}
|
|
3029
|
+
function settlementActionCommand(spec) {
|
|
3030
|
+
const command = new Command44(spec.name).description(spec.description).argument("<taskId>", "Task ID (0x-prefixed hex)");
|
|
3031
|
+
if (spec.confirm === "release-cost") {
|
|
3032
|
+
command.option("--yes", "Accept the stake forfeited by releasing now (releaseCostNow)");
|
|
3033
|
+
} else if (spec.confirm === "always") {
|
|
3034
|
+
command.option("--yes", "Confirm the action; it cannot be undone");
|
|
3035
|
+
}
|
|
3036
|
+
return command.action(async (taskId, opts) => {
|
|
3037
|
+
try {
|
|
3038
|
+
if (!await confirmSettlementAction(spec.name, spec.confirm, taskId, opts.yes)) return;
|
|
3039
|
+
const keystore = await loadKeystore();
|
|
3040
|
+
const expiresAt = settlementActionRequiresExpiry(spec.kind) ? settlementActionExpiresAt() : void 0;
|
|
3041
|
+
const signature = await signMessage(
|
|
3042
|
+
buildSettlementActionMessage(spec.kind, taskId, expiresAt),
|
|
3043
|
+
keystore
|
|
3044
|
+
);
|
|
3045
|
+
const { data: result, idempotencyKey } = await apiPost(
|
|
3046
|
+
`/api/tasks/${taskId}/${spec.kind}`,
|
|
3047
|
+
{
|
|
3048
|
+
taskId,
|
|
3049
|
+
callerAddress: keystore.walletAddress,
|
|
3050
|
+
signature,
|
|
3051
|
+
...expiresAt === void 0 ? {} : { expiresAt }
|
|
3052
|
+
}
|
|
3053
|
+
);
|
|
3054
|
+
printResult(
|
|
3055
|
+
{ ...result, ...await settleRecording(result, idempotencyKey) },
|
|
3056
|
+
{ idempotencyKey }
|
|
3057
|
+
);
|
|
3058
|
+
} catch (err) {
|
|
3059
|
+
renderFailure(err);
|
|
3060
|
+
}
|
|
3061
|
+
});
|
|
3062
|
+
}
|
|
3063
|
+
|
|
3064
|
+
// src/commands/task/settlement-actions.ts
|
|
3065
|
+
var releaseCmd = settlementActionCommand({
|
|
3066
|
+
name: "release",
|
|
3067
|
+
kind: "release",
|
|
3068
|
+
confirm: "release-cost",
|
|
3069
|
+
description: "Hand a claim back before delivering (claim worker only). Free during the grace share of the claim time, then up to the full stake at expiry. Refuses without --yes when releaseCostNow is above zero."
|
|
3070
|
+
});
|
|
3071
|
+
var requestRevisionCmd = settlementActionCommand({
|
|
3072
|
+
name: "request-revision",
|
|
3073
|
+
kind: "request-revision",
|
|
3074
|
+
description: "Send a delivery under review back to the worker for fixes (requester only, while revisions remain)"
|
|
3075
|
+
});
|
|
3076
|
+
var rejectCmd = settlementActionCommand({
|
|
3077
|
+
name: "reject",
|
|
3078
|
+
kind: "reject-delivery",
|
|
3079
|
+
confirm: "always",
|
|
3080
|
+
description: "Reject a delivery under review (requester only). The worker still receives the task's reject fee, and the task reopens or refunds per its reject outcome. This cannot be undone; requires --yes."
|
|
3081
|
+
});
|
|
3082
|
+
var settleCmd = settlementActionCommand({
|
|
3083
|
+
name: "settle",
|
|
3084
|
+
kind: "settle",
|
|
3085
|
+
description: "Pay the worker for a delivery whose review window lapsed with no response (anyone may call)"
|
|
3086
|
+
});
|
|
3087
|
+
|
|
2293
3088
|
// src/commands/task/index.ts
|
|
2294
|
-
var
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
3089
|
+
var taskCommand2 = new Command45("task").description("Manage tasks");
|
|
3090
|
+
taskCommand2.addCommand(createCmd);
|
|
3091
|
+
taskCommand2.addCommand(listCmd);
|
|
3092
|
+
taskCommand2.addCommand(getCmd);
|
|
3093
|
+
taskCommand2.addCommand(submitCmd);
|
|
3094
|
+
taskCommand2.addCommand(acceptCmd);
|
|
3095
|
+
taskCommand2.addCommand(acceptSubmissionsCmd);
|
|
3096
|
+
taskCommand2.addCommand(rateCmd);
|
|
3097
|
+
taskCommand2.addCommand(claimCmd);
|
|
3098
|
+
taskCommand2.addCommand(pitchCmd);
|
|
3099
|
+
taskCommand2.addCommand(pitchesCmd);
|
|
3100
|
+
taskCommand2.addCommand(selectWorkerCmd);
|
|
3101
|
+
taskCommand2.addCommand(proofCmd);
|
|
3102
|
+
taskCommand2.addCommand(proofsCmd);
|
|
3103
|
+
taskCommand2.addCommand(bidCmd);
|
|
3104
|
+
taskCommand2.addCommand(auctionAcceptCmd);
|
|
3105
|
+
taskCommand2.addCommand(submissionsCmd);
|
|
3106
|
+
taskCommand2.addCommand(selectWinnerCmd);
|
|
3107
|
+
taskCommand2.addCommand(downloadCmd);
|
|
3108
|
+
taskCommand2.addCommand(cancelCmd);
|
|
3109
|
+
taskCommand2.addCommand(refundExpiredCmd);
|
|
3110
|
+
taskCommand2.addCommand(rejectSubmissionCmd);
|
|
3111
|
+
taskCommand2.addCommand(rejectAllSubmissionsCmd);
|
|
3112
|
+
taskCommand2.addCommand(mySubmissionsCmd);
|
|
3113
|
+
taskCommand2.addCommand(updateCmd);
|
|
3114
|
+
taskCommand2.addCommand(forfeitCmd);
|
|
3115
|
+
taskCommand2.addCommand(assignEvaluatorCmd);
|
|
3116
|
+
taskCommand2.addCommand(evaluateCmd);
|
|
3117
|
+
taskCommand2.addCommand(appealCmd);
|
|
3118
|
+
taskCommand2.addCommand(evaluatorTimeoutCmd);
|
|
3119
|
+
taskCommand2.addCommand(finalizeVerdictCmd);
|
|
3120
|
+
taskCommand2.addCommand(resolveDisputeCmd);
|
|
3121
|
+
taskCommand2.addCommand(unlockCmd);
|
|
3122
|
+
taskCommand2.addCommand(inviteCmd);
|
|
3123
|
+
taskCommand2.addCommand(uninviteCmd);
|
|
3124
|
+
taskCommand2.addCommand(viewersCmd);
|
|
3125
|
+
taskCommand2.addCommand(termsCmd);
|
|
3126
|
+
taskCommand2.addCommand(releaseCmd);
|
|
3127
|
+
taskCommand2.addCommand(requestRevisionCmd);
|
|
3128
|
+
taskCommand2.addCommand(rejectCmd);
|
|
3129
|
+
taskCommand2.addCommand(settleCmd);
|
|
3130
|
+
taskCommand2.addCommand(reviewCmd);
|
|
3131
|
+
taskCommand2.addCommand(startReviewCmd);
|
|
2330
3132
|
|
|
2331
3133
|
// src/commands/agent/index.ts
|
|
2332
|
-
import { Command as
|
|
3134
|
+
import { Command as Command48, Option as Option3 } from "commander";
|
|
2333
3135
|
|
|
2334
3136
|
// src/commands/agent/credits.ts
|
|
2335
|
-
import { Command as
|
|
3137
|
+
import { Command as Command46 } from "commander";
|
|
2336
3138
|
|
|
2337
3139
|
// src/lib/tty-confirm.ts
|
|
2338
3140
|
import { createInterface as createInterface2 } from "readline";
|
|
@@ -2385,7 +3187,7 @@ function setupLines(plan, pricing) {
|
|
|
2385
3187
|
...rateLines(pricing)
|
|
2386
3188
|
];
|
|
2387
3189
|
}
|
|
2388
|
-
var setupCommand = new
|
|
3190
|
+
var setupCommand = new Command46("setup").description(
|
|
2389
3191
|
"Approve the compute allocation that pays for sandboxes: daily and monthly caps, and optional auto-top-up"
|
|
2390
3192
|
).requiredOption("--per-day <usd>", "Most compute credit bought in one UTC day, in USD").requiredOption("--per-month <usd>", "Most compute credit bought in one UTC month, in USD").option("--total <usd>", "Most this allocation ever buys, in USD (default: 12 months)").option(
|
|
2391
3193
|
"--auto-top-up-below <credits>",
|
|
@@ -2422,7 +3224,7 @@ var setupCommand = new Command42("setup").description(
|
|
|
2422
3224
|
printResult({ config, caps: plan.caps, autoTopUp: plan.autoTopUp ?? null });
|
|
2423
3225
|
}
|
|
2424
3226
|
);
|
|
2425
|
-
var buyCommand = new
|
|
3227
|
+
var buyCommand = new Command46("buy").description("Buy compute credit over x402 from the compute allocation, after showing the rate").option("--amount <usd>", "Purchase size in whole USD (see `credits status` for the sizes)").option("--yes", "Skip the confirmation prompt (the allocation caps still apply)").action(async (options) => {
|
|
2426
3228
|
const config = await loadComputeCreditConfig();
|
|
2427
3229
|
if (!config) {
|
|
2428
3230
|
throw new ComputeCreditError(
|
|
@@ -2452,17 +3254,17 @@ var buyCommand = new Command42("buy").description("Buy compute credit over x402
|
|
|
2452
3254
|
rate: { rateCardVersion: pricing.rateCardVersion, usdPerCredit: pricing.usdPerCredit }
|
|
2453
3255
|
});
|
|
2454
3256
|
});
|
|
2455
|
-
var statusCommand2 = new
|
|
3257
|
+
var statusCommand2 = new Command46("status").description(
|
|
2456
3258
|
"Show compute credit balance, rate, recent debits and purchases, and the caps; an approved auto-top-up runs when the balance is below its threshold"
|
|
2457
3259
|
).action(async () => {
|
|
2458
3260
|
printResult(await computeCreditStatus());
|
|
2459
3261
|
});
|
|
2460
|
-
var checkCommand = new
|
|
3262
|
+
var checkCommand = new Command46("check").description(
|
|
2461
3263
|
"Check the compute credit balance and, when auto-top-up was approved and the balance is low, buy one chunk"
|
|
2462
3264
|
).action(async () => {
|
|
2463
3265
|
printResult(await checkComputeBalance());
|
|
2464
3266
|
});
|
|
2465
|
-
var reconcileCommand = new
|
|
3267
|
+
var reconcileCommand = new Command46("reconcile").description(
|
|
2466
3268
|
"Settle purchases whose outcome was lost, by their idempotency key; never pays again"
|
|
2467
3269
|
).option(
|
|
2468
3270
|
"--release <key>",
|
|
@@ -2509,10 +3311,10 @@ var reconcileCommand = new Command42("reconcile").description(
|
|
|
2509
3311
|
...recoveryError === void 0 ? {} : { recoveryError }
|
|
2510
3312
|
});
|
|
2511
3313
|
});
|
|
2512
|
-
var disableAutoTopUpCommand = new
|
|
3314
|
+
var disableAutoTopUpCommand = new Command46("disable-auto-top-up").description("Turn off auto-top-up and close its allocation").action(async () => {
|
|
2513
3315
|
printResult({ config: await disableComputeAutoTopUp() });
|
|
2514
3316
|
});
|
|
2515
|
-
var agentCreditsCommand = new
|
|
3317
|
+
var agentCreditsCommand = new Command46("credits").description("Prepaid compute credit for agent sandboxes, bought over x402").addCommand(setupCommand).addCommand(buyCommand).addCommand(statusCommand2).addCommand(checkCommand).addCommand(reconcileCommand).addCommand(disableAutoTopUpCommand);
|
|
2516
3318
|
|
|
2517
3319
|
// src/commands/agent/launch.ts
|
|
2518
3320
|
var EXPERIMENTAL_AGENT_ENV = "TASKMARKET_EXPERIMENTAL_AGENT";
|
|
@@ -2534,8 +3336,13 @@ async function runAgentCommand(options) {
|
|
|
2534
3336
|
try {
|
|
2535
3337
|
const { assertAgentCompatibility } = await import("./compatibility-KUYGZSUC.js");
|
|
2536
3338
|
const pi = assertAgentCompatibility();
|
|
2537
|
-
const { runAgent } = await import("./launch-
|
|
2538
|
-
result = await runAgent({
|
|
3339
|
+
const { runAgent } = await import("./launch-C5HUFUU2.js");
|
|
3340
|
+
result = await runAgent({
|
|
3341
|
+
model: options.model,
|
|
3342
|
+
pi,
|
|
3343
|
+
sync: options.sync,
|
|
3344
|
+
includeSecret: options.includeSecret
|
|
3345
|
+
});
|
|
2539
3346
|
} catch (error) {
|
|
2540
3347
|
renderFailure(error, {
|
|
2541
3348
|
details: error instanceof AgentLaunchError ? { ...error.details, code: error.code } : void 0
|
|
@@ -2560,7 +3367,7 @@ async function runAgentCommand(options) {
|
|
|
2560
3367
|
}
|
|
2561
3368
|
|
|
2562
3369
|
// src/commands/agent/manage.ts
|
|
2563
|
-
import { Command as
|
|
3370
|
+
import { Command as Command47 } from "commander";
|
|
2564
3371
|
async function run(options, work, lines) {
|
|
2565
3372
|
try {
|
|
2566
3373
|
const value = await work();
|
|
@@ -2585,37 +3392,76 @@ async function requireExperimental() {
|
|
|
2585
3392
|
);
|
|
2586
3393
|
}
|
|
2587
3394
|
function agentManagementCommands() {
|
|
2588
|
-
const status = new
|
|
3395
|
+
const status = new Command47("status").description(
|
|
2589
3396
|
"Show the agent channels (locked, charged, refundable), purchases with an unknown outcome, the sandbox and compute credit. Reads only."
|
|
2590
3397
|
).option("--json", "Print the versioned JSON envelope").action(async (options) => {
|
|
2591
|
-
const { collectAgentStatus, statusLines } = await import("./status-
|
|
3398
|
+
const { collectAgentStatus, statusLines } = await import("./status-SDLSTJIA.js");
|
|
2592
3399
|
await run(options, () => collectAgentStatus(), statusLines);
|
|
2593
3400
|
});
|
|
2594
|
-
const doctor = new
|
|
3401
|
+
const doctor = new Command47("doctor").description(
|
|
2595
3402
|
"Check that the agent can start: Node and the bundled runtime, wallet and policy, channels, API, sandbox broker, host keys, xgate and RPC. Reads only."
|
|
2596
3403
|
).option("--json", "Print the versioned JSON envelope").action(async (options) => {
|
|
2597
|
-
const { runDoctor, doctorLines } = await import("./doctor-
|
|
3404
|
+
const { runDoctor, doctorLines } = await import("./doctor-5FUGI42V.js");
|
|
2598
3405
|
const report = await run(options, () => runDoctor(), doctorLines);
|
|
2599
3406
|
if (report && !report.ok) process.exitCode = 1;
|
|
2600
3407
|
});
|
|
2601
|
-
const reconcile = new
|
|
2602
|
-
"Decide an agent purchase whose outcome is unknown, by its invocation ID. Never sends
|
|
2603
|
-
).argument("
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
3408
|
+
const reconcile = new Command47("reconcile").description(
|
|
3409
|
+
"Decide an agent purchase whose outcome is unknown, by its invocation ID; or, with --channel or --rail, everything one channel still has open. Never sends anything again."
|
|
3410
|
+
).argument("[invocation-id]", "The purchase, as status or the agent named it").option("--rail <id>", "Reconcile the open purchases of this rail's channel").option(
|
|
3411
|
+
"--channel <channel-id>",
|
|
3412
|
+
"Reconcile the open purchases of this channel, as status lists it; also reaches retired channels"
|
|
3413
|
+
).option("--json", "Print the versioned JSON envelope").action(
|
|
3414
|
+
async (invocationId, options) => {
|
|
3415
|
+
const { reconcileChannelPurchases, reconcileInvocation } = await import("./reconcile-Q7XFHDQQ.js");
|
|
3416
|
+
const choice = { rail: options.rail, channel: options.channel };
|
|
3417
|
+
if (invocationId !== void 0) {
|
|
3418
|
+
await run(
|
|
3419
|
+
options,
|
|
3420
|
+
() => reconcileInvocation(invocationId, { choice }),
|
|
3421
|
+
(view) => [
|
|
3422
|
+
`Purchase ${view.invocationId} on ${view.rail}: ${view.outcome}`,
|
|
3423
|
+
view.message,
|
|
3424
|
+
...view.releasedFrom.map(
|
|
3425
|
+
(rail) => `Released the lease it had left on the ${rail} channel.`
|
|
3426
|
+
)
|
|
3427
|
+
]
|
|
3428
|
+
);
|
|
3429
|
+
return;
|
|
3430
|
+
}
|
|
3431
|
+
await run(
|
|
3432
|
+
options,
|
|
3433
|
+
async () => {
|
|
3434
|
+
if (options.rail === void 0 && options.channel === void 0) {
|
|
3435
|
+
const { AgentLaunchError } = await import("./errors-2L357HGA.js");
|
|
3436
|
+
throw new AgentLaunchError(
|
|
3437
|
+
"invalid_invocation_id",
|
|
3438
|
+
"Name a purchase (taskmarket agent reconcile <invocation-id>) or a channel (--channel <id> or --rail <id>)"
|
|
3439
|
+
);
|
|
3440
|
+
}
|
|
3441
|
+
return reconcileChannelPurchases(choice);
|
|
3442
|
+
},
|
|
3443
|
+
(view) => [
|
|
3444
|
+
`Channel ${view.channelId} (${view.rail}${view.retired ? ", retired" : ""})`,
|
|
3445
|
+
...view.purchases.map(
|
|
3446
|
+
(p) => p.outcome === "failed" ? ` ${p.invocationId}: not reconciled (${p.code}): ${p.message}` : ` ${p.invocationId}: ${p.outcome}. ${p.message}`
|
|
3447
|
+
),
|
|
3448
|
+
...view.releasedLease ? [
|
|
3449
|
+
`Released the lease another channel's purchase ${view.releasedLease} had left on it.`
|
|
3450
|
+
] : [],
|
|
3451
|
+
view.message,
|
|
3452
|
+
...channelLines(view.channel)
|
|
3453
|
+
]
|
|
3454
|
+
);
|
|
3455
|
+
}
|
|
3456
|
+
);
|
|
3457
|
+
const topup = new Command47("topup").description(
|
|
2612
3458
|
"Check a channel top-up against the approved budget: within it, the channel deposits by itself with the next turn that needs funds; over it, a new approval is needed"
|
|
2613
3459
|
).requiredOption("--amount <usdc>", "Top-up in USDC, e.g. 5 or 2.50").option("--rail <id>", "The rail to top up (required when the policy has more than one)").option("--json", "Print the versioned JSON envelope").action(async (options) => {
|
|
2614
3460
|
await run(
|
|
2615
3461
|
options,
|
|
2616
3462
|
async () => {
|
|
2617
3463
|
await requireExperimental();
|
|
2618
|
-
const { usdcToAtomic, AGENT_VIEW_VERSION } = await import("./budget-view-
|
|
3464
|
+
const { usdcToAtomic, AGENT_VIEW_VERSION } = await import("./budget-view-JPZ7HWR2.js");
|
|
2619
3465
|
const { AgentLaunchError } = await import("./errors-2L357HGA.js");
|
|
2620
3466
|
const amount = usdcToAtomic(options.amount);
|
|
2621
3467
|
if (amount === void 0 || amount === "0") {
|
|
@@ -2624,7 +3470,7 @@ function agentManagementCommands() {
|
|
|
2624
3470
|
`Not a USDC amount: ${options.amount} (digits, at most 6 decimals)`
|
|
2625
3471
|
);
|
|
2626
3472
|
}
|
|
2627
|
-
const { openAgentChannels, requestTopUp, selectRail, withdrawalHatch } = await import("./channels-
|
|
3473
|
+
const { openAgentChannels, requestTopUp, selectRail, withdrawalHatch } = await import("./channels-544VRSKF.js");
|
|
2628
3474
|
const { rails } = await openAgentChannels();
|
|
2629
3475
|
const result = await requestTopUp(selectRail(rails, options.rail), amount);
|
|
2630
3476
|
return {
|
|
@@ -2637,7 +3483,7 @@ function agentManagementCommands() {
|
|
|
2637
3483
|
(view) => [view.note, ...channelLines(view.channel)]
|
|
2638
3484
|
);
|
|
2639
3485
|
});
|
|
2640
|
-
const close = new
|
|
3486
|
+
const close = new Command47("close").description(
|
|
2641
3487
|
"Close the agent's channel and refund its unused balance cooperatively; checks an earlier close instead of sending it again"
|
|
2642
3488
|
).option("--rail <id>", "The rail to close (required when the policy has more than one)").option(
|
|
2643
3489
|
"--channel <channel-id>",
|
|
@@ -2647,9 +3493,11 @@ function agentManagementCommands() {
|
|
|
2647
3493
|
options,
|
|
2648
3494
|
async () => {
|
|
2649
3495
|
await requireExperimental();
|
|
2650
|
-
const { AGENT_VIEW_VERSION } = await import("./budget-view-
|
|
2651
|
-
const { closeChannel, openAgentChannels, selectChannel } = await import("./channels-
|
|
2652
|
-
const {
|
|
3496
|
+
const { AGENT_VIEW_VERSION } = await import("./budget-view-JPZ7HWR2.js");
|
|
3497
|
+
const { closeChannel, openAgentChannels, selectChannel } = await import("./channels-544VRSKF.js");
|
|
3498
|
+
const { reconcileLeasedPurchase } = await import("./reconcile-Q7XFHDQQ.js");
|
|
3499
|
+
const opened = await openAgentChannels();
|
|
3500
|
+
const { rail, retired } = selectChannel(opened, options);
|
|
2653
3501
|
const result = await closeChannel(
|
|
2654
3502
|
rail,
|
|
2655
3503
|
async (lines) => {
|
|
@@ -2660,7 +3508,16 @@ function agentManagementCommands() {
|
|
|
2660
3508
|
}
|
|
2661
3509
|
return confirmAtTerminal(lines, "pass --yes to close without a prompt");
|
|
2662
3510
|
},
|
|
2663
|
-
{
|
|
3511
|
+
{
|
|
3512
|
+
retired,
|
|
3513
|
+
reconcilePurchase: (id) => reconcileLeasedPurchase(opened, id),
|
|
3514
|
+
wait: {
|
|
3515
|
+
onWait: (progress2) => process.stderr.write(
|
|
3516
|
+
`Refund ${progress2.transaction} sent; waiting for it to confirm on chain (${Math.round(progress2.waitedMs / 1e3)} s of up to ${Math.round(progress2.timeoutMs / 1e3)} s)
|
|
3517
|
+
`
|
|
3518
|
+
)
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
2664
3521
|
);
|
|
2665
3522
|
return {
|
|
2666
3523
|
version: AGENT_VIEW_VERSION,
|
|
@@ -2671,7 +3528,7 @@ function agentManagementCommands() {
|
|
|
2671
3528
|
(view) => [
|
|
2672
3529
|
{
|
|
2673
3530
|
refunded: `Closed: the unused balance of ${view.rail} was refunded to the wallet.`,
|
|
2674
|
-
pending: `The refund of ${view.rail}
|
|
3531
|
+
pending: view.operation?.transaction ? `The refund of ${view.rail} was sent (transaction ${view.operation.transaction}) and is still confirming on chain. Run taskmarket agent close again in a minute to record it; it is never sent twice.` : `The refund of ${view.rail} may have been sent, and its outcome is not known yet. Run taskmarket agent close again later to check it; it is never sent twice.`,
|
|
2675
3532
|
failed: `The router refused the refund of ${view.rail}; nothing was returned.`,
|
|
2676
3533
|
closed: `The ${view.rail} channel is already closed.`
|
|
2677
3534
|
}[view.outcome],
|
|
@@ -2684,22 +3541,85 @@ function agentManagementCommands() {
|
|
|
2684
3541
|
]
|
|
2685
3542
|
);
|
|
2686
3543
|
});
|
|
2687
|
-
|
|
3544
|
+
const pull = new Command47("pull").description(
|
|
3545
|
+
"After a session: review the changes the agent left in its sandbox (saved when the session ended; the sandbox is not resumed) and apply them to this project on approval. Conflicts with your local edits and host configuration are listed, never applied. Inside a session use /pull."
|
|
3546
|
+
).option("--diff", "Print the full diff").option("--apply", "Apply the clean changes without asking").option(
|
|
3547
|
+
"--overwrite <path>",
|
|
3548
|
+
"Overwrite this conflicting file with the sandbox version (repeatable)",
|
|
3549
|
+
(value, previous = []) => [...previous, value]
|
|
3550
|
+
).option(
|
|
3551
|
+
"--keep-local <path>",
|
|
3552
|
+
"Resolve this saved change by keeping the file as it is in this project (repeatable)",
|
|
3553
|
+
(value, previous = []) => [...previous, value]
|
|
3554
|
+
).option(
|
|
3555
|
+
"--recover",
|
|
3556
|
+
"After a failed or interrupted end-of-session save: reconnect to the sandbox the session left running, save its changes here, then stop it"
|
|
3557
|
+
).option(
|
|
3558
|
+
"--discard",
|
|
3559
|
+
"Drop the saved changes (or the record of a failed save) instead of pulling them"
|
|
3560
|
+
).option(
|
|
3561
|
+
"--force-unclaim",
|
|
3562
|
+
"Release the project from a session or pull that is not running but still holds it (refused while it is)"
|
|
3563
|
+
).option("--yes", "With --discard or --force-unclaim: skip the confirmation prompt").option("--json", "Print the versioned JSON envelope").action(
|
|
3564
|
+
async (options) => {
|
|
3565
|
+
await run(
|
|
3566
|
+
options,
|
|
3567
|
+
async () => {
|
|
3568
|
+
await requireExperimental();
|
|
3569
|
+
const { runPull } = await import("./sync-pull-TKM4EDGH.js");
|
|
3570
|
+
const { terminalPrompter } = await import("./prompter-LSICEYSQ.js");
|
|
3571
|
+
return runPull(options, {
|
|
3572
|
+
interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY) && !options.json,
|
|
3573
|
+
prompter: () => terminalPrompter(),
|
|
3574
|
+
say: (lines) => {
|
|
3575
|
+
if (!options.json && lines.length > 0)
|
|
3576
|
+
process.stdout.write(`${lines.join("\n")}
|
|
3577
|
+
`);
|
|
3578
|
+
}
|
|
3579
|
+
});
|
|
3580
|
+
},
|
|
3581
|
+
(view) => {
|
|
3582
|
+
const lines = [];
|
|
3583
|
+
for (const skip of view.skipped) {
|
|
3584
|
+
lines.push(` not applied: ${skip.path} (${skip.reason.replace(/_/g, " ")})`);
|
|
3585
|
+
}
|
|
3586
|
+
lines.push(view.note);
|
|
3587
|
+
return lines;
|
|
3588
|
+
}
|
|
3589
|
+
);
|
|
3590
|
+
}
|
|
3591
|
+
);
|
|
3592
|
+
return [status, doctor, reconcile, topup, close, pull];
|
|
2688
3593
|
}
|
|
2689
3594
|
|
|
2690
3595
|
// src/commands/agent/index.ts
|
|
2691
3596
|
var experimental = agentCommandEnabled();
|
|
2692
|
-
var agentCommand = new
|
|
3597
|
+
var agentCommand = new Command48("agent").description(
|
|
2693
3598
|
experimental ? "The Taskmarket agent and the compute it runs on. Without a subcommand (experimental), open Pi's interactive coding agent in this project, with paid xgate inference from your wallet and tools in your cloud sandbox" : "The Taskmarket agent and the compute it runs on"
|
|
2694
3599
|
).addOption(
|
|
2695
|
-
new
|
|
3600
|
+
new Option3("--model <id>", "Start on one model from your approved spending policy").hideHelp(
|
|
2696
3601
|
!experimental
|
|
2697
3602
|
)
|
|
3603
|
+
).addOption(
|
|
3604
|
+
new Option3(
|
|
3605
|
+
"--sync",
|
|
3606
|
+
"Upload this project to the sandbox for this session without asking (what goes up is still shown); needed without a terminal until you approve the project once"
|
|
3607
|
+
).hideHelp(!experimental)
|
|
3608
|
+
).addOption(
|
|
3609
|
+
new Option3(
|
|
3610
|
+
"--no-sync",
|
|
3611
|
+
"Start with an empty sandbox: upload nothing from this project"
|
|
3612
|
+
).hideHelp(!experimental)
|
|
3613
|
+
).addOption(
|
|
3614
|
+
new Option3(
|
|
3615
|
+
"--include-secret <path>",
|
|
3616
|
+
"Upload this secret-like file too (a path in the project, not a pattern; repeatable)"
|
|
3617
|
+
).argParser((value, previous = []) => [...previous, value]).hideHelp(!experimental)
|
|
2698
3618
|
).allowExcessArguments(false).action(runAgentCommand);
|
|
2699
3619
|
if (experimental) {
|
|
2700
3620
|
agentCommand.addHelpText(
|
|
2701
3621
|
"after",
|
|
2702
|
-
"\nThe session offers only models your spending policy approves and xgate qualifies. Pi runs\non this machine with its configuration under TASKMARKET_HOME; every tool runs in a sandbox."
|
|
3622
|
+
"\nThe session offers only models your spending policy approves and xgate qualifies. Pi runs\non this machine with its configuration under TASKMARKET_HOME; every tool runs in a sandbox.\nThe project is uploaded with your approval (asked once per project); secret-like files stay\nhere unless you name them. /pull in the session, or taskmarket agent pull after it, shows\nwhat the agent changed and applies it only when you approve; /sync sends your local edits."
|
|
2703
3623
|
);
|
|
2704
3624
|
}
|
|
2705
3625
|
agentCommand.addCommand(agentCreditsCommand);
|
|
@@ -2708,8 +3628,8 @@ for (const command of agentManagementCommands()) {
|
|
|
2708
3628
|
}
|
|
2709
3629
|
|
|
2710
3630
|
// src/commands/agents.ts
|
|
2711
|
-
import { Command as
|
|
2712
|
-
var agentsCommand = new
|
|
3631
|
+
import { Command as Command49 } from "commander";
|
|
3632
|
+
var agentsCommand = new Command49("agents").description("Browse the agent directory").option("--sort <order>", "Sort order: reputation or tasks", "reputation").option("--skill <tag>", "Filter by skill tag").option("--search <query>", "Search by agentId or wallet address").option("--limit <n>", "Maximum results to return", "20").action(async (opts) => {
|
|
2713
3633
|
const params = new URLSearchParams();
|
|
2714
3634
|
if (opts.sort) params.set("sort", opts.sort);
|
|
2715
3635
|
if (opts.skill) params.set("skill", opts.skill);
|
|
@@ -2720,8 +3640,8 @@ var agentsCommand = new Command45("agents").description("Browse the agent direct
|
|
|
2720
3640
|
});
|
|
2721
3641
|
|
|
2722
3642
|
// src/commands/inbox.ts
|
|
2723
|
-
import { Command as
|
|
2724
|
-
var inboxCommand = new
|
|
3643
|
+
import { Command as Command50 } from "commander";
|
|
3644
|
+
var inboxCommand = new Command50("inbox").description("Show tasks you created and tasks you are working on").action(async () => {
|
|
2725
3645
|
const auth = await signReadAuth();
|
|
2726
3646
|
if (!auth) {
|
|
2727
3647
|
printError("No keystore found. Run `taskmarket init` first.");
|
|
@@ -2742,8 +3662,8 @@ var inboxCommand = new Command46("inbox").description("Show tasks you created an
|
|
|
2742
3662
|
});
|
|
2743
3663
|
|
|
2744
3664
|
// src/commands/actions.ts
|
|
2745
|
-
import { Command as
|
|
2746
|
-
var actionsCommand = new
|
|
3665
|
+
import { Command as Command51 } from "commander";
|
|
3666
|
+
var actionsCommand = new Command51("actions").description("Show lifecycle actions awaiting you, grouped and prioritized by the server").action(async () => {
|
|
2747
3667
|
const auth = await signReadAuth();
|
|
2748
3668
|
if (!auth) {
|
|
2749
3669
|
printError("No keystore found. Run `taskmarket init` first.");
|
|
@@ -2757,8 +3677,8 @@ var actionsCommand = new Command47("actions").description("Show lifecycle action
|
|
|
2757
3677
|
});
|
|
2758
3678
|
|
|
2759
3679
|
// src/commands/deposit.ts
|
|
2760
|
-
import { Command as
|
|
2761
|
-
var depositCommand = new
|
|
3680
|
+
import { Command as Command52 } from "commander";
|
|
3681
|
+
var depositCommand = new Command52("deposit").description("Show wallet address and network info for funding").action(async () => {
|
|
2762
3682
|
let address;
|
|
2763
3683
|
try {
|
|
2764
3684
|
address = await getWalletAddress();
|
|
@@ -2774,33 +3694,40 @@ var depositCommand = new Command48("deposit").description("Show wallet address a
|
|
|
2774
3694
|
renderFailure(err, { fallback: "Failed to fetch network info from backend." });
|
|
2775
3695
|
return;
|
|
2776
3696
|
}
|
|
3697
|
+
const chain = chainInfoFor(networkInfo, selectedNetwork().chainId);
|
|
3698
|
+
const chainId = chain?.chainId ?? networkInfo.chainId;
|
|
3699
|
+
const tokenAddress = chain?.tokenAddress ?? networkInfo.usdcAddress;
|
|
2777
3700
|
const data = {
|
|
2778
3701
|
address,
|
|
2779
|
-
network: networkInfo.networkName,
|
|
2780
|
-
chainId
|
|
2781
|
-
currency:
|
|
2782
|
-
|
|
3702
|
+
network: chain?.networkName ?? networkInfo.networkName,
|
|
3703
|
+
chainId,
|
|
3704
|
+
currency: paymentTokenSymbol(chainId),
|
|
3705
|
+
tokenAddress,
|
|
3706
|
+
/** Kept for scripts that read it; the same address as `tokenAddress`. */
|
|
3707
|
+
usdcContract: tokenAddress
|
|
2783
3708
|
};
|
|
2784
3709
|
printResult(data);
|
|
2785
3710
|
});
|
|
2786
3711
|
|
|
2787
3712
|
// src/commands/wallet/index.ts
|
|
2788
|
-
import { Command as
|
|
3713
|
+
import { Command as Command63 } from "commander";
|
|
2789
3714
|
|
|
2790
3715
|
// src/commands/wallet/balance.ts
|
|
2791
|
-
import { Command as
|
|
2792
|
-
var walletBalanceCommand = new
|
|
3716
|
+
import { Command as Command53 } from "commander";
|
|
3717
|
+
var walletBalanceCommand = new Command53("balance").description("Show the wallet's payment-token balance on the selected network").option("--address <addr>", "Address to check (defaults to own wallet)").action(async (opts) => {
|
|
2793
3718
|
let address = opts.address;
|
|
2794
3719
|
if (!address) {
|
|
2795
3720
|
const keystore = await loadKeystore();
|
|
2796
3721
|
address = keystore.walletAddress;
|
|
2797
3722
|
}
|
|
2798
|
-
const result = await apiGet(
|
|
3723
|
+
const result = await apiGet(
|
|
3724
|
+
`/api/wallet/balance?address=${address}&chainId=${selectedNetwork().chainId}`
|
|
3725
|
+
);
|
|
2799
3726
|
printResult(result);
|
|
2800
3727
|
});
|
|
2801
3728
|
|
|
2802
3729
|
// src/commands/wallet/import.ts
|
|
2803
|
-
import { Command as
|
|
3730
|
+
import { Command as Command54 } from "commander";
|
|
2804
3731
|
import { createInterface as createInterface3 } from "readline";
|
|
2805
3732
|
import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
|
|
2806
3733
|
function normalizePrivateKey(raw) {
|
|
@@ -2835,7 +3762,7 @@ function promptHiddenInput(prompt) {
|
|
|
2835
3762
|
});
|
|
2836
3763
|
});
|
|
2837
3764
|
}
|
|
2838
|
-
var walletImportCommand = new
|
|
3765
|
+
var walletImportCommand = new Command54("import").description("Import an existing private key as the agent wallet").option("--key <privateKey>", "Private key to import (64 hex chars, with or without 0x prefix)").option(
|
|
2839
3766
|
"--yes",
|
|
2840
3767
|
"Confirm legal acceptance non-interactively after reviewing every current policy"
|
|
2841
3768
|
).action(async (opts) => {
|
|
@@ -2915,11 +3842,57 @@ var walletImportCommand = new Command50("import").description("Import an existin
|
|
|
2915
3842
|
});
|
|
2916
3843
|
|
|
2917
3844
|
// src/commands/wallet/set-withdrawal-address.ts
|
|
2918
|
-
import {
|
|
3845
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
3846
|
+
import { Command as Command55 } from "commander";
|
|
3847
|
+
|
|
3848
|
+
// src/lib/withdrawal-pin.ts
|
|
3849
|
+
var WithdrawalAddressMismatchError = class extends Error {
|
|
3850
|
+
constructor(pinned, server) {
|
|
3851
|
+
super(
|
|
3852
|
+
`Refusing to sign: the server reports withdrawal address ${server}, but this CLI has ${pinned} pinned locally. Nothing was signed or sent. If you changed the withdrawal address yourself (for example on the web with your account recovery code), accept the new address with: taskmarket wallet sync-withdrawal-address`
|
|
3853
|
+
);
|
|
3854
|
+
this.pinned = pinned;
|
|
3855
|
+
this.server = server;
|
|
3856
|
+
this.name = "WithdrawalAddressMismatchError";
|
|
3857
|
+
}
|
|
3858
|
+
};
|
|
3859
|
+
async function pinWithdrawalAddress(keystore, address) {
|
|
3860
|
+
await saveKeystore({ ...keystore, withdrawalAddress: address });
|
|
3861
|
+
}
|
|
3862
|
+
async function verifyWithdrawalAddress(keystore, serverAddress, log = (line) => process.stderr.write(line + "\n")) {
|
|
3863
|
+
const pinned = keystore.withdrawalAddress;
|
|
3864
|
+
if (!pinned) {
|
|
3865
|
+
await pinWithdrawalAddress(keystore, serverAddress);
|
|
3866
|
+
log(`Note: pinned withdrawal address ${serverAddress} in the local keystore.`);
|
|
3867
|
+
return serverAddress;
|
|
3868
|
+
}
|
|
3869
|
+
if (pinned.toLowerCase() !== serverAddress.toLowerCase()) {
|
|
3870
|
+
throw new WithdrawalAddressMismatchError(pinned, serverAddress);
|
|
3871
|
+
}
|
|
3872
|
+
return pinned;
|
|
3873
|
+
}
|
|
3874
|
+
function paymentTokenDomain(chainId) {
|
|
3875
|
+
const { payment } = taskmarketChain(chainId);
|
|
3876
|
+
const verifyingContract = payment.token ?? (chainId === 31337 ? process.env.USDC_TOKEN_ADDRESS : void 0);
|
|
3877
|
+
if (!verifyingContract) {
|
|
3878
|
+
throw new Error(
|
|
3879
|
+
`Chain ${chainId} has no known payment token address` + (chainId === 31337 ? "; set USDC_TOKEN_ADDRESS to the local mock token" : "")
|
|
3880
|
+
);
|
|
3881
|
+
}
|
|
3882
|
+
return {
|
|
3883
|
+
name: payment.domain.name,
|
|
3884
|
+
version: payment.domain.version,
|
|
3885
|
+
chainId,
|
|
3886
|
+
verifyingContract
|
|
3887
|
+
};
|
|
3888
|
+
}
|
|
3889
|
+
|
|
3890
|
+
// src/commands/wallet/set-withdrawal-address.ts
|
|
3891
|
+
var SIGNATURE_TTL_SECONDS = 5 * 60;
|
|
2919
3892
|
function isValidAddress(addr) {
|
|
2920
3893
|
return /^0x[a-fA-F0-9]{40}$/.test(addr);
|
|
2921
3894
|
}
|
|
2922
|
-
var setWithdrawalAddressCommand = new
|
|
3895
|
+
var setWithdrawalAddressCommand = new Command55("set-withdrawal-address").description("Set a withdrawal address for payment-token withdrawals (one-time, free)").argument("<address>", "Ethereum address to receive withdrawals").action(async (address) => {
|
|
2923
3896
|
if (!isValidAddress(address)) {
|
|
2924
3897
|
printError("Invalid Ethereum address: must be 0x followed by 40 hex characters");
|
|
2925
3898
|
return;
|
|
@@ -2932,13 +3905,26 @@ var setWithdrawalAddressCommand = new Command51("set-withdrawal-address").descri
|
|
|
2932
3905
|
return;
|
|
2933
3906
|
}
|
|
2934
3907
|
const walletAddress = keystore.walletAddress;
|
|
2935
|
-
const
|
|
3908
|
+
const chainId = selectedNetwork().chainId;
|
|
3909
|
+
const nonce = `0x${randomBytes2(32).toString("hex")}`;
|
|
3910
|
+
const expiresAt = Math.floor(Date.now() / 1e3) + SIGNATURE_TTL_SECONDS;
|
|
3911
|
+
const message = buildSetWithdrawalAddressMessage({
|
|
3912
|
+
walletAddress,
|
|
3913
|
+
withdrawalAddress: address,
|
|
3914
|
+
chainId,
|
|
3915
|
+
nonce,
|
|
3916
|
+
expiresAt
|
|
3917
|
+
});
|
|
2936
3918
|
const signature = await signMessage(message, keystore);
|
|
2937
3919
|
const { data: result, idempotencyKey } = await apiPost("/api/wallet/set-withdrawal-address", {
|
|
2938
3920
|
walletAddress,
|
|
2939
3921
|
withdrawalAddress: address,
|
|
2940
|
-
signature
|
|
3922
|
+
signature,
|
|
3923
|
+
nonce,
|
|
3924
|
+
expiresAt,
|
|
3925
|
+
chainId
|
|
2941
3926
|
});
|
|
3927
|
+
await pinWithdrawalAddress(keystore, result.withdrawalAddress);
|
|
2942
3928
|
printResult(
|
|
2943
3929
|
{
|
|
2944
3930
|
withdrawalAddress: result.withdrawalAddress,
|
|
@@ -2950,8 +3936,8 @@ var setWithdrawalAddressCommand = new Command51("set-withdrawal-address").descri
|
|
|
2950
3936
|
});
|
|
2951
3937
|
|
|
2952
3938
|
// src/commands/wallet/get-withdrawal-address.ts
|
|
2953
|
-
import { Command as
|
|
2954
|
-
var getWithdrawalAddressCommand = new
|
|
3939
|
+
import { Command as Command56 } from "commander";
|
|
3940
|
+
var getWithdrawalAddressCommand = new Command56("get-withdrawal-address").description("Show the currently registered withdrawal address, if any").option("--address <addr>", "Wallet address to check (defaults to own wallet)").action(async (opts) => {
|
|
2955
3941
|
let address = opts.address;
|
|
2956
3942
|
if (!address) {
|
|
2957
3943
|
const keystore = await loadKeystore();
|
|
@@ -2962,14 +3948,14 @@ var getWithdrawalAddressCommand = new Command52("get-withdrawal-address").descri
|
|
|
2962
3948
|
});
|
|
2963
3949
|
|
|
2964
3950
|
// src/commands/wallet/change-withdrawal-address.ts
|
|
2965
|
-
import { Command as
|
|
3951
|
+
import { Command as Command57 } from "commander";
|
|
2966
3952
|
function isValidAddress2(addr) {
|
|
2967
3953
|
return /^0x[a-fA-F0-9]{40}$/.test(addr);
|
|
2968
3954
|
}
|
|
2969
3955
|
function isValidRecoveryCode(code) {
|
|
2970
3956
|
return /^\d{8}$/.test(code);
|
|
2971
3957
|
}
|
|
2972
|
-
var changeWithdrawalAddressCommand = new
|
|
3958
|
+
var changeWithdrawalAddressCommand = new Command57("change-withdrawal-address").description("Change the withdrawal address using the account recovery code (RFC-0012)").argument("<recovery-code>", "The 8-digit account recovery code from set-withdrawal-address").argument("<new-address>", "New Ethereum address to receive withdrawals").action(async (recoveryCode, newAddress) => {
|
|
2973
3959
|
if (!isValidRecoveryCode(recoveryCode)) {
|
|
2974
3960
|
printError("Invalid account recovery code: must be 8 digits");
|
|
2975
3961
|
return;
|
|
@@ -2990,6 +3976,7 @@ var changeWithdrawalAddressCommand = new Command53("change-withdrawal-address").
|
|
|
2990
3976
|
accountRecoveryCode: recoveryCode,
|
|
2991
3977
|
newWithdrawalAddress: newAddress
|
|
2992
3978
|
});
|
|
3979
|
+
await pinWithdrawalAddress(keystore, result.withdrawalAddress);
|
|
2993
3980
|
printResult(
|
|
2994
3981
|
{
|
|
2995
3982
|
withdrawalAddress: result.withdrawalAddress,
|
|
@@ -3000,9 +3987,39 @@ var changeWithdrawalAddressCommand = new Command53("change-withdrawal-address").
|
|
|
3000
3987
|
);
|
|
3001
3988
|
});
|
|
3002
3989
|
|
|
3990
|
+
// src/commands/wallet/sync-withdrawal-address.ts
|
|
3991
|
+
import { Command as Command58 } from "commander";
|
|
3992
|
+
var syncWithdrawalAddressCommand = new Command58("sync-withdrawal-address").description(
|
|
3993
|
+
"Accept the withdrawal address registered on the server as the locally pinned one (after a change made elsewhere, e.g. on the web)"
|
|
3994
|
+
).action(async () => {
|
|
3995
|
+
let keystore;
|
|
3996
|
+
try {
|
|
3997
|
+
keystore = await loadKeystore();
|
|
3998
|
+
} catch {
|
|
3999
|
+
printError("No keystore found. Run `taskmarket init` first.");
|
|
4000
|
+
return;
|
|
4001
|
+
}
|
|
4002
|
+
const { withdrawalAddress } = await apiGet(
|
|
4003
|
+
`/api/wallet/withdrawal-address?address=${keystore.walletAddress}`
|
|
4004
|
+
);
|
|
4005
|
+
if (!withdrawalAddress) {
|
|
4006
|
+
printError(
|
|
4007
|
+
"No withdrawal address set. Run: taskmarket wallet set-withdrawal-address <address>"
|
|
4008
|
+
);
|
|
4009
|
+
return;
|
|
4010
|
+
}
|
|
4011
|
+
const previous = keystore.withdrawalAddress ?? null;
|
|
4012
|
+
await pinWithdrawalAddress(keystore, withdrawalAddress);
|
|
4013
|
+
printResult({
|
|
4014
|
+
previousWithdrawalAddress: previous,
|
|
4015
|
+
withdrawalAddress,
|
|
4016
|
+
changed: previous === null || previous.toLowerCase() !== withdrawalAddress.toLowerCase()
|
|
4017
|
+
});
|
|
4018
|
+
});
|
|
4019
|
+
|
|
3003
4020
|
// src/commands/wallet/reveal-recovery-code.ts
|
|
3004
|
-
import { Command as
|
|
3005
|
-
var revealRecoveryCodeCommand = new
|
|
4021
|
+
import { Command as Command59 } from "commander";
|
|
4022
|
+
var revealRecoveryCodeCommand = new Command59("reveal-recovery-code").description("One-time reveal of your account recovery code, if one has not been generated yet").action(async () => {
|
|
3006
4023
|
let keystore;
|
|
3007
4024
|
try {
|
|
3008
4025
|
keystore = await loadKeystore();
|
|
@@ -3027,8 +4044,8 @@ var revealRecoveryCodeCommand = new Command54("reveal-recovery-code").descriptio
|
|
|
3027
4044
|
});
|
|
3028
4045
|
|
|
3029
4046
|
// src/commands/wallet/publish-key.ts
|
|
3030
|
-
import { Command as
|
|
3031
|
-
var publishKeyCommand = new
|
|
4047
|
+
import { Command as Command60 } from "commander";
|
|
4048
|
+
var publishKeyCommand = new Command60("publish-key").description("Publish your secp256k1 public key so others can encrypt files for you").action(async () => {
|
|
3032
4049
|
let keystore;
|
|
3033
4050
|
try {
|
|
3034
4051
|
keystore = await loadKeystore();
|
|
@@ -3048,13 +4065,13 @@ var publishKeyCommand = new Command55("publish-key").description("Publish your s
|
|
|
3048
4065
|
});
|
|
3049
4066
|
|
|
3050
4067
|
// src/commands/wallet/withdraw-dreams.ts
|
|
3051
|
-
import { Command as
|
|
4068
|
+
import { Command as Command61 } from "commander";
|
|
3052
4069
|
import { toHex } from "viem";
|
|
3053
4070
|
var AUTHORIZATION_VALIDITY_SECONDS = 300;
|
|
3054
4071
|
function isValidAddress3(addr) {
|
|
3055
4072
|
return /^0x[a-fA-F0-9]{40}$/.test(addr);
|
|
3056
4073
|
}
|
|
3057
|
-
var withdrawDreamsCommand = new
|
|
4074
|
+
var withdrawDreamsCommand = new Command61("withdraw-dreams").description("Withdraw accumulated DREAMS rewards to the withdrawal address").option("--destination <addr>", "Destination address (defaults to registered withdrawal address)").action(async (opts) => {
|
|
3058
4075
|
let keystore;
|
|
3059
4076
|
try {
|
|
3060
4077
|
keystore = await loadKeystore();
|
|
@@ -3078,7 +4095,12 @@ var withdrawDreamsCommand = new Command56("withdraw-dreams").description("Withdr
|
|
|
3078
4095
|
);
|
|
3079
4096
|
return;
|
|
3080
4097
|
}
|
|
3081
|
-
|
|
4098
|
+
try {
|
|
4099
|
+
destination = await verifyWithdrawalAddress(keystore, addressResponse.withdrawalAddress);
|
|
4100
|
+
} catch (err) {
|
|
4101
|
+
renderFailure(err);
|
|
4102
|
+
return;
|
|
4103
|
+
}
|
|
3082
4104
|
}
|
|
3083
4105
|
const nonce = toHex(crypto.getRandomValues(new Uint8Array(32)));
|
|
3084
4106
|
const validBefore = String(Math.floor(Date.now() / 1e3) + AUTHORIZATION_VALIDITY_SECONDS);
|
|
@@ -3107,20 +4129,196 @@ var withdrawDreamsCommand = new Command56("withdraw-dreams").description("Withdr
|
|
|
3107
4129
|
);
|
|
3108
4130
|
});
|
|
3109
4131
|
|
|
4132
|
+
// src/commands/wallet/bridge.ts
|
|
4133
|
+
import { Command as Command62 } from "commander";
|
|
4134
|
+
import { toHex as toHex2 } from "viem";
|
|
4135
|
+
var USDC_TYPES = {
|
|
4136
|
+
TransferWithAuthorization: [
|
|
4137
|
+
{ name: "from", type: "address" },
|
|
4138
|
+
{ name: "to", type: "address" },
|
|
4139
|
+
{ name: "value", type: "uint256" },
|
|
4140
|
+
{ name: "validAfter", type: "uint256" },
|
|
4141
|
+
{ name: "validBefore", type: "uint256" },
|
|
4142
|
+
{ name: "nonce", type: "bytes32" }
|
|
4143
|
+
]
|
|
4144
|
+
};
|
|
4145
|
+
var bridgeTiming = { intervalMs: 3e3, timeoutMs: 10 * 6e4 };
|
|
4146
|
+
function progress(message) {
|
|
4147
|
+
process.stderr.write(`${message}
|
|
4148
|
+
`);
|
|
4149
|
+
}
|
|
4150
|
+
function sleep(ms) {
|
|
4151
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4152
|
+
}
|
|
4153
|
+
function sameAddress(a, b) {
|
|
4154
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
4155
|
+
}
|
|
4156
|
+
async function pollBridgeStatus(requestId) {
|
|
4157
|
+
const deadline = Date.now() + bridgeTiming.timeoutMs;
|
|
4158
|
+
let last;
|
|
4159
|
+
for (; ; ) {
|
|
4160
|
+
let status = null;
|
|
4161
|
+
try {
|
|
4162
|
+
status = await apiGet(
|
|
4163
|
+
`/api/wallet/bridge-status?requestId=${encodeURIComponent(requestId)}`
|
|
4164
|
+
);
|
|
4165
|
+
} catch (err) {
|
|
4166
|
+
progress(`Could not read bridge status: ${err instanceof Error ? err.message : String(err)}`);
|
|
4167
|
+
}
|
|
4168
|
+
if (status) {
|
|
4169
|
+
if (status.status !== last) progress(`Bridge status: ${status.status}`);
|
|
4170
|
+
last = status.status;
|
|
4171
|
+
if (isTerminalWalletBridgeStatus(status.status)) return status;
|
|
4172
|
+
}
|
|
4173
|
+
if (Date.now() >= deadline) return status;
|
|
4174
|
+
await sleep(bridgeTiming.intervalMs);
|
|
4175
|
+
}
|
|
4176
|
+
}
|
|
4177
|
+
var walletBridgeCommand = new Command62("bridge").description(
|
|
4178
|
+
"Move free USDC on Base to OUSD at the same address on Tempo (gasless, through Relay)"
|
|
4179
|
+
).requiredOption("--to <chain>", 'Destination chain (only "tempo")').argument("<amount>", "Amount of USDC in whole tokens (minimum 1, e.g. 25)").action(async (amount, opts) => {
|
|
4180
|
+
if (opts.to !== "tempo") {
|
|
4181
|
+
printError('Invalid --to: the only bridge destination is "tempo"');
|
|
4182
|
+
return;
|
|
4183
|
+
}
|
|
4184
|
+
if (!/^\d+(\.\d{1,6})?$/.test(amount) || Number(amount) <= 0) {
|
|
4185
|
+
printError("Invalid amount: must be a positive number with at most 6 decimals (e.g. 25)");
|
|
4186
|
+
return;
|
|
4187
|
+
}
|
|
4188
|
+
const [whole, fraction = ""] = amount.split(".");
|
|
4189
|
+
const amountBaseUnits = (BigInt(whole) * 1000000n + BigInt(fraction.padEnd(6, "0"))).toString();
|
|
4190
|
+
let keystore;
|
|
4191
|
+
try {
|
|
4192
|
+
keystore = await loadKeystore();
|
|
4193
|
+
} catch {
|
|
4194
|
+
printError("No keystore found. Run `taskmarket init` first.");
|
|
4195
|
+
return;
|
|
4196
|
+
}
|
|
4197
|
+
const from = keystore.walletAddress;
|
|
4198
|
+
let quote;
|
|
4199
|
+
try {
|
|
4200
|
+
({ data: quote } = await apiPost("/api/wallet/bridge-quote", {
|
|
4201
|
+
chainId: WALLET_BRIDGE_ORIGIN_CHAIN_ID,
|
|
4202
|
+
from,
|
|
4203
|
+
to: "tempo",
|
|
4204
|
+
amountBaseUnits
|
|
4205
|
+
}));
|
|
4206
|
+
} catch (err) {
|
|
4207
|
+
renderFailure(err);
|
|
4208
|
+
return;
|
|
4209
|
+
}
|
|
4210
|
+
if (!sameAddress(quote.recipient, from) || !sameAddress(quote.refundTo, from) || !sameAddress(quote.authorization.message.to, quote.depositAddress) || quote.amountBaseUnits !== amountBaseUnits || quote.authorization.message.value !== amountBaseUnits || quote.destinationChainId !== WALLET_BRIDGE_TEMPO_CHAIN_ID) {
|
|
4211
|
+
printError("The bridge quote does not match this request; nothing was signed");
|
|
4212
|
+
return;
|
|
4213
|
+
}
|
|
4214
|
+
let domain;
|
|
4215
|
+
try {
|
|
4216
|
+
domain = paymentTokenDomain(WALLET_BRIDGE_ORIGIN_CHAIN_ID);
|
|
4217
|
+
} catch (err) {
|
|
4218
|
+
renderFailure(err);
|
|
4219
|
+
return;
|
|
4220
|
+
}
|
|
4221
|
+
progress(
|
|
4222
|
+
`Bridging ${formatPaymentAmount(amountBaseUnits, WALLET_BRIDGE_ORIGIN_CHAIN_ID)}: expect ${formatPaymentAmount(quote.expectedOutBaseUnits, WALLET_BRIDGE_TEMPO_CHAIN_ID)}, at least ${formatPaymentAmount(quote.minimumOutBaseUnits, WALLET_BRIDGE_TEMPO_CHAIN_ID)} (Relay fee ${quote.fees.relayerUsd} USD)`
|
|
4223
|
+
);
|
|
4224
|
+
const authorization = {
|
|
4225
|
+
from,
|
|
4226
|
+
to: quote.depositAddress,
|
|
4227
|
+
value: amountBaseUnits,
|
|
4228
|
+
validAfter: quote.authorization.message.validAfter,
|
|
4229
|
+
validBefore: quote.authorization.message.validBefore,
|
|
4230
|
+
nonce: toHex2(crypto.getRandomValues(new Uint8Array(32)))
|
|
4231
|
+
};
|
|
4232
|
+
const signature = await signTypedData(
|
|
4233
|
+
{
|
|
4234
|
+
domain,
|
|
4235
|
+
types: USDC_TYPES,
|
|
4236
|
+
primaryType: "TransferWithAuthorization",
|
|
4237
|
+
message: authorization
|
|
4238
|
+
},
|
|
4239
|
+
keystore
|
|
4240
|
+
);
|
|
4241
|
+
let bridged;
|
|
4242
|
+
let idempotencyKey;
|
|
4243
|
+
try {
|
|
4244
|
+
({ data: bridged, idempotencyKey } = await apiPost("/api/wallet/bridge", {
|
|
4245
|
+
chainId: WALLET_BRIDGE_ORIGIN_CHAIN_ID,
|
|
4246
|
+
from,
|
|
4247
|
+
amountBaseUnits,
|
|
4248
|
+
authorization,
|
|
4249
|
+
signature
|
|
4250
|
+
}));
|
|
4251
|
+
} catch (err) {
|
|
4252
|
+
renderFailure(err);
|
|
4253
|
+
return;
|
|
4254
|
+
}
|
|
4255
|
+
progress(`Deposited on Base: ${bridged.txHash}. Waiting for Relay to fill on Tempo...`);
|
|
4256
|
+
const status = await pollBridgeStatus(bridged.requestId);
|
|
4257
|
+
const tempoExplorer = taskmarketChain(WALLET_BRIDGE_TEMPO_CHAIN_ID).explorerUrl;
|
|
4258
|
+
const baseExplorer = taskmarketChain(WALLET_BRIDGE_ORIGIN_CHAIN_ID).explorerUrl;
|
|
4259
|
+
const output = {
|
|
4260
|
+
status: status?.status ?? "unknown",
|
|
4261
|
+
requestId: bridged.requestId,
|
|
4262
|
+
from,
|
|
4263
|
+
recipient: bridged.recipient,
|
|
4264
|
+
amountBaseUnits,
|
|
4265
|
+
amount: formatPaymentAmount(amountBaseUnits, WALLET_BRIDGE_ORIGIN_CHAIN_ID),
|
|
4266
|
+
expectedOut: formatPaymentAmount(quote.expectedOutBaseUnits, WALLET_BRIDGE_TEMPO_CHAIN_ID),
|
|
4267
|
+
minimumOut: formatPaymentAmount(quote.minimumOutBaseUnits, WALLET_BRIDGE_TEMPO_CHAIN_ID),
|
|
4268
|
+
tokenSymbol: "OUSD",
|
|
4269
|
+
depositTxHash: bridged.txHash,
|
|
4270
|
+
depositExplorerUrl: `${baseExplorer}/tx/${bridged.txHash}`
|
|
4271
|
+
};
|
|
4272
|
+
if (status?.status === "success") {
|
|
4273
|
+
const fillTxHash = status.txHashes[0];
|
|
4274
|
+
printResult(
|
|
4275
|
+
{
|
|
4276
|
+
...output,
|
|
4277
|
+
fillTxHash: fillTxHash ?? null,
|
|
4278
|
+
explorerUrl: fillTxHash ? `${tempoExplorer}/tx/${fillTxHash}` : `${tempoExplorer}/address/${bridged.recipient}`
|
|
4279
|
+
},
|
|
4280
|
+
{ idempotencyKey }
|
|
4281
|
+
);
|
|
4282
|
+
return;
|
|
4283
|
+
}
|
|
4284
|
+
if (status?.status === "refund") {
|
|
4285
|
+
printError(
|
|
4286
|
+
`Relay refunded the bridge to ${from} on Base (minus gas)` + (status.failReason ? `: ${status.failReason}` : "") + `. Request ${bridged.requestId}.`,
|
|
4287
|
+
{ idempotencyKey }
|
|
4288
|
+
);
|
|
4289
|
+
return;
|
|
4290
|
+
}
|
|
4291
|
+
if (status?.status === "failure") {
|
|
4292
|
+
printError(
|
|
4293
|
+
`The bridge failed` + (status.failReason ? `: ${status.failReason}` : "") + `. Request ${bridged.requestId}; check taskmarket wallet balance on Base and Tempo.`,
|
|
4294
|
+
{ idempotencyKey }
|
|
4295
|
+
);
|
|
4296
|
+
return;
|
|
4297
|
+
}
|
|
4298
|
+
printResult(
|
|
4299
|
+
{ ...output, explorerUrl: `${tempoExplorer}/address/${bridged.recipient}` },
|
|
4300
|
+
{
|
|
4301
|
+
idempotencyKey
|
|
4302
|
+
}
|
|
4303
|
+
);
|
|
4304
|
+
});
|
|
4305
|
+
|
|
3110
4306
|
// src/commands/wallet/index.ts
|
|
3111
|
-
var walletCommand = new
|
|
4307
|
+
var walletCommand = new Command63("wallet").description("Wallet management commands");
|
|
3112
4308
|
walletCommand.addCommand(walletBalanceCommand);
|
|
3113
4309
|
walletCommand.addCommand(walletImportCommand);
|
|
3114
4310
|
walletCommand.addCommand(setWithdrawalAddressCommand);
|
|
3115
4311
|
walletCommand.addCommand(getWithdrawalAddressCommand);
|
|
3116
4312
|
walletCommand.addCommand(changeWithdrawalAddressCommand);
|
|
4313
|
+
walletCommand.addCommand(syncWithdrawalAddressCommand);
|
|
3117
4314
|
walletCommand.addCommand(revealRecoveryCodeCommand);
|
|
3118
4315
|
walletCommand.addCommand(publishKeyCommand);
|
|
3119
4316
|
walletCommand.addCommand(withdrawDreamsCommand);
|
|
4317
|
+
walletCommand.addCommand(walletBridgeCommand);
|
|
3120
4318
|
|
|
3121
4319
|
// src/commands/withdraw.ts
|
|
3122
|
-
import { Command as
|
|
3123
|
-
import { toHex as
|
|
4320
|
+
import { Command as Command64 } from "commander";
|
|
4321
|
+
import { toHex as toHex3 } from "viem";
|
|
3124
4322
|
var RECEIVE_POLICY_BLOCKED = "recipient_receive_policy_blocked";
|
|
3125
4323
|
async function withdrawOnTempo(input) {
|
|
3126
4324
|
const { keystore, chainId, withdrawalAddress, amountBaseUnits } = input;
|
|
@@ -3133,7 +4331,7 @@ async function withdrawOnTempo(input) {
|
|
|
3133
4331
|
token,
|
|
3134
4332
|
to: withdrawalAddress,
|
|
3135
4333
|
amount: BigInt(amountBaseUnits),
|
|
3136
|
-
validBefore: Math.floor(Date.now() / 1e3) + MPP_TEMPO_DEFAULT_VALIDITY_SECONDS,
|
|
4334
|
+
validBefore: Math.floor(Date.now() / 1e3) + clientValidityWindowSeconds(chainId, MPP_TEMPO_DEFAULT_VALIDITY_SECONDS),
|
|
3137
4335
|
format: "sender"
|
|
3138
4336
|
});
|
|
3139
4337
|
try {
|
|
@@ -3156,7 +4354,16 @@ async function withdrawOnTempo(input) {
|
|
|
3156
4354
|
throw error;
|
|
3157
4355
|
}
|
|
3158
4356
|
}
|
|
3159
|
-
|
|
4357
|
+
function withdrawOutput(result, chainId) {
|
|
4358
|
+
return {
|
|
4359
|
+
txHash: result.txHash,
|
|
4360
|
+
amountBaseUnits: result.amountBaseUnits,
|
|
4361
|
+
amount: formatPaymentAmount(result.amountBaseUnits, chainId),
|
|
4362
|
+
tokenSymbol: paymentTokenSymbol(chainId),
|
|
4363
|
+
to: result.to
|
|
4364
|
+
};
|
|
4365
|
+
}
|
|
4366
|
+
var USDC_TYPES2 = {
|
|
3160
4367
|
TransferWithAuthorization: [
|
|
3161
4368
|
{ name: "from", type: "address" },
|
|
3162
4369
|
{ name: "to", type: "address" },
|
|
@@ -3166,12 +4373,12 @@ var USDC_TYPES = {
|
|
|
3166
4373
|
{ name: "nonce", type: "bytes32" }
|
|
3167
4374
|
]
|
|
3168
4375
|
};
|
|
3169
|
-
var withdrawCommand = new
|
|
4376
|
+
var withdrawCommand = new Command64("withdraw").description(
|
|
3170
4377
|
"Withdraw the payment token (USDC on Base, OUSD on Tempo) to the registered withdrawal address"
|
|
3171
4378
|
).argument("<amount>", "Amount in whole tokens (e.g. 5 for 5 USDC or 5 OUSD)").action(async (amount) => {
|
|
3172
4379
|
const parsed = parseFloat(amount);
|
|
3173
4380
|
if (isNaN(parsed) || parsed <= 0) {
|
|
3174
|
-
printError("Invalid amount: must be a positive number (e.g. 5
|
|
4381
|
+
printError("Invalid amount: must be a positive number (e.g. 5)");
|
|
3175
4382
|
return;
|
|
3176
4383
|
}
|
|
3177
4384
|
const amountBaseUnits = String(Math.round(parsed * 1e6));
|
|
@@ -3192,7 +4399,16 @@ var withdrawCommand = new Command58("withdraw").description(
|
|
|
3192
4399
|
);
|
|
3193
4400
|
return;
|
|
3194
4401
|
}
|
|
3195
|
-
|
|
4402
|
+
let withdrawalAddress;
|
|
4403
|
+
try {
|
|
4404
|
+
withdrawalAddress = await verifyWithdrawalAddress(
|
|
4405
|
+
keystore,
|
|
4406
|
+
addressResponse.withdrawalAddress
|
|
4407
|
+
);
|
|
4408
|
+
} catch (err) {
|
|
4409
|
+
renderFailure(err);
|
|
4410
|
+
return;
|
|
4411
|
+
}
|
|
3196
4412
|
const { chainId } = selectedNetwork();
|
|
3197
4413
|
if (paymentProtocolOf(chainId) === "mpp") {
|
|
3198
4414
|
const { data: result2, idempotencyKey: idempotencyKey2 } = await withdrawOnTempo({
|
|
@@ -3201,10 +4417,14 @@ var withdrawCommand = new Command58("withdraw").description(
|
|
|
3201
4417
|
withdrawalAddress,
|
|
3202
4418
|
amountBaseUnits
|
|
3203
4419
|
});
|
|
3204
|
-
printResult(
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
4420
|
+
printResult(withdrawOutput(result2, chainId), { idempotencyKey: idempotencyKey2 });
|
|
4421
|
+
return;
|
|
4422
|
+
}
|
|
4423
|
+
let domain;
|
|
4424
|
+
try {
|
|
4425
|
+
domain = paymentTokenDomain(chainId);
|
|
4426
|
+
} catch (err) {
|
|
4427
|
+
renderFailure(err);
|
|
3208
4428
|
return;
|
|
3209
4429
|
}
|
|
3210
4430
|
const now = Math.floor(Date.now() / 1e3);
|
|
@@ -3214,12 +4434,12 @@ var withdrawCommand = new Command58("withdraw").description(
|
|
|
3214
4434
|
value: amountBaseUnits,
|
|
3215
4435
|
validAfter: String(now - 60),
|
|
3216
4436
|
validBefore: String(now + 300),
|
|
3217
|
-
nonce:
|
|
4437
|
+
nonce: toHex3(crypto.getRandomValues(new Uint8Array(32)))
|
|
3218
4438
|
};
|
|
3219
4439
|
const signature = await signTypedData(
|
|
3220
4440
|
{
|
|
3221
|
-
domain
|
|
3222
|
-
types:
|
|
4441
|
+
domain,
|
|
4442
|
+
types: USDC_TYPES2,
|
|
3223
4443
|
primaryType: "TransferWithAuthorization",
|
|
3224
4444
|
message: authorization
|
|
3225
4445
|
},
|
|
@@ -3234,16 +4454,13 @@ var withdrawCommand = new Command58("withdraw").description(
|
|
|
3234
4454
|
signature
|
|
3235
4455
|
}
|
|
3236
4456
|
);
|
|
3237
|
-
printResult(
|
|
3238
|
-
{ txHash: result.txHash, amountBaseUnits: result.amountBaseUnits, to: result.to },
|
|
3239
|
-
{ idempotencyKey }
|
|
3240
|
-
);
|
|
4457
|
+
printResult(withdrawOutput(result, chainId), { idempotencyKey });
|
|
3241
4458
|
});
|
|
3242
4459
|
|
|
3243
4460
|
// src/commands/encrypt.ts
|
|
3244
|
-
import { Command as
|
|
4461
|
+
import { Command as Command65 } from "commander";
|
|
3245
4462
|
import { promises as fs2 } from "fs";
|
|
3246
|
-
var encryptCommand = new
|
|
4463
|
+
var encryptCommand = new Command65("encrypt").description("Encrypt a file with ECIES using wallet keys").argument("<file>", "Path to the file to encrypt").option("--recipient <address>", "Recipient wallet address (default: self)").option("--output <path>", "Output path (default: <file>.enc)").action(async (file, opts) => {
|
|
3247
4464
|
let plaintext;
|
|
3248
4465
|
try {
|
|
3249
4466
|
plaintext = await fs2.readFile(file);
|
|
@@ -3291,9 +4508,9 @@ var encryptCommand = new Command59("encrypt").description("Encrypt a file with E
|
|
|
3291
4508
|
});
|
|
3292
4509
|
|
|
3293
4510
|
// src/commands/decrypt.ts
|
|
3294
|
-
import { Command as
|
|
4511
|
+
import { Command as Command66 } from "commander";
|
|
3295
4512
|
import { promises as fs3 } from "fs";
|
|
3296
|
-
var decryptCommand = new
|
|
4513
|
+
var decryptCommand = new Command66("decrypt").description("Decrypt a file using your wallet key").argument("<file>", "Path to the encrypted file").option("--output <path>", "Output path (default: strips .enc, otherwise appends .dec)").action(async (file, opts) => {
|
|
3297
4514
|
let ciphertext;
|
|
3298
4515
|
try {
|
|
3299
4516
|
ciphertext = await fs3.readFile(file);
|
|
@@ -3333,7 +4550,7 @@ var decryptCommand = new Command60("decrypt").description("Decrypt a file using
|
|
|
3333
4550
|
});
|
|
3334
4551
|
|
|
3335
4552
|
// src/commands/xmtp.ts
|
|
3336
|
-
import { Command as
|
|
4553
|
+
import { Command as Command67 } from "commander";
|
|
3337
4554
|
|
|
3338
4555
|
// src/lib/xmtp-client.ts
|
|
3339
4556
|
import path2 from "path";
|
|
@@ -3438,7 +4655,7 @@ function shouldHandleEnvelope(envelope, options) {
|
|
|
3438
4655
|
options.cache.add(key);
|
|
3439
4656
|
return true;
|
|
3440
4657
|
}
|
|
3441
|
-
function
|
|
4658
|
+
function sleep2(ms) {
|
|
3442
4659
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3443
4660
|
}
|
|
3444
4661
|
async function runStreamWithReconnect(options) {
|
|
@@ -3468,7 +4685,7 @@ async function runStreamWithReconnect(options) {
|
|
|
3468
4685
|
throw error;
|
|
3469
4686
|
}
|
|
3470
4687
|
options.onReconnect?.(reconnectAttempt, error);
|
|
3471
|
-
await
|
|
4688
|
+
await sleep2(backoffMs(reconnectAttempt));
|
|
3472
4689
|
}
|
|
3473
4690
|
}
|
|
3474
4691
|
}
|
|
@@ -3614,7 +4831,7 @@ function toTransportError(error, fallbackMessage) {
|
|
|
3614
4831
|
const message = error instanceof Error && error.message.length > 0 ? error.message : `${fallbackMessage} (${String(error)})`;
|
|
3615
4832
|
return new XmtpTransportError(message, category, error);
|
|
3616
4833
|
}
|
|
3617
|
-
function
|
|
4834
|
+
function sleep3(ms) {
|
|
3618
4835
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3619
4836
|
}
|
|
3620
4837
|
function parseRetryAttempts() {
|
|
@@ -3642,7 +4859,7 @@ async function withRetry(operation, maxAttempts) {
|
|
|
3642
4859
|
if (!mapped.retryable || attempt >= maxAttempts) {
|
|
3643
4860
|
throw mapped;
|
|
3644
4861
|
}
|
|
3645
|
-
await
|
|
4862
|
+
await sleep3(sendBackoffMs(attempt));
|
|
3646
4863
|
}
|
|
3647
4864
|
}
|
|
3648
4865
|
throw new XmtpTransportError("XMTP retry attempts exhausted", "non-retryable");
|
|
@@ -4096,7 +5313,7 @@ function getDefaultQueryTimeoutMs() {
|
|
|
4096
5313
|
}
|
|
4097
5314
|
return Math.floor(parsed);
|
|
4098
5315
|
}
|
|
4099
|
-
var xmtpCommand = new
|
|
5316
|
+
var xmtpCommand = new Command67("xmtp").description("Manage XMTP messaging for this agent");
|
|
4100
5317
|
xmtpCommand.command("init").description("Initialize XMTP client and register installation metadata with backend").action(async () => {
|
|
4101
5318
|
const keystore = await loadKeystore();
|
|
4102
5319
|
const client = await createXmtpClient({
|
|
@@ -4333,7 +5550,7 @@ xmtpCommand.command("purge").description("Revoke stale XMTP installations that h
|
|
|
4333
5550
|
});
|
|
4334
5551
|
|
|
4335
5552
|
// src/commands/daemon.ts
|
|
4336
|
-
import { Command as
|
|
5553
|
+
import { Command as Command68 } from "commander";
|
|
4337
5554
|
function diffTaskStatuses(prev, next) {
|
|
4338
5555
|
const changes = [];
|
|
4339
5556
|
for (const [taskId, newStatus] of next) {
|
|
@@ -4361,7 +5578,7 @@ function sleepOrAbort(ms, signal) {
|
|
|
4361
5578
|
});
|
|
4362
5579
|
}
|
|
4363
5580
|
var MAX_SEEN_TASK_IDS = 2e3;
|
|
4364
|
-
var daemonCommand = new
|
|
5581
|
+
var daemonCommand = new Command68("daemon").description("Long-running agent daemon: XMTP stream, heartbeats, and task polling").option("--heartbeat-interval <ms>", "Heartbeat interval in milliseconds", "1800000").option("--inbox-interval <ms>", "Inbox poll interval in milliseconds", "15000").option("--task-interval <ms>", "New task poll interval in milliseconds", "15000").option(
|
|
4365
5582
|
"--auction-poll-interval <ms>",
|
|
4366
5583
|
"Poll interval for clock-based auction tasks (dutch/reverse_dutch) in milliseconds",
|
|
4367
5584
|
"15000"
|
|
@@ -4694,11 +5911,11 @@ var daemonCommand = new Command62("daemon").description("Long-running agent daem
|
|
|
4694
5911
|
);
|
|
4695
5912
|
|
|
4696
5913
|
// src/commands/email/index.ts
|
|
4697
|
-
import { Command as
|
|
5914
|
+
import { Command as Command77 } from "commander";
|
|
4698
5915
|
|
|
4699
5916
|
// src/commands/email/register.ts
|
|
4700
|
-
import { Command as
|
|
4701
|
-
var registerCommand = new
|
|
5917
|
+
import { Command as Command69 } from "commander";
|
|
5918
|
+
var registerCommand = new Command69("register").description("Register an email address for this agent").requiredOption("--username <name>", "Desired username (e.g. myagent)").action(async (opts) => {
|
|
4702
5919
|
const check = await apiGet(
|
|
4703
5920
|
`/api/emails/check-username?username=${encodeURIComponent(opts.username)}`
|
|
4704
5921
|
);
|
|
@@ -4719,8 +5936,8 @@ var registerCommand = new Command63("register").description("Register an email a
|
|
|
4719
5936
|
});
|
|
4720
5937
|
|
|
4721
5938
|
// src/commands/email/inbox.ts
|
|
4722
|
-
import { Command as
|
|
4723
|
-
var inboxCommand2 = new
|
|
5939
|
+
import { Command as Command70 } from "commander";
|
|
5940
|
+
var inboxCommand2 = new Command70("inbox").description("List emails in your inbox").option("--limit <n>", "Number of emails to return", "20").option("--unread", "Show only unread emails").action(async (opts) => {
|
|
4724
5941
|
const keystore = await loadKeystore();
|
|
4725
5942
|
const params = new URLSearchParams({
|
|
4726
5943
|
deviceId: keystore.deviceId,
|
|
@@ -4735,8 +5952,8 @@ var inboxCommand2 = new Command64("inbox").description("List emails in your inbo
|
|
|
4735
5952
|
});
|
|
4736
5953
|
|
|
4737
5954
|
// src/commands/email/read.ts
|
|
4738
|
-
import { Command as
|
|
4739
|
-
var readCommand = new
|
|
5955
|
+
import { Command as Command71 } from "commander";
|
|
5956
|
+
var readCommand = new Command71("read").description("Read an email by ID (marks it as read)").argument("<id>", "Email ID").action(async (id) => {
|
|
4740
5957
|
const keystore = await loadKeystore();
|
|
4741
5958
|
const params = new URLSearchParams({
|
|
4742
5959
|
deviceId: keystore.deviceId,
|
|
@@ -4748,8 +5965,8 @@ var readCommand = new Command65("read").description("Read an email by ID (marks
|
|
|
4748
5965
|
});
|
|
4749
5966
|
|
|
4750
5967
|
// src/commands/email/send.ts
|
|
4751
|
-
import { Command as
|
|
4752
|
-
var sendCommand = new
|
|
5968
|
+
import { Command as Command72 } from "commander";
|
|
5969
|
+
var sendCommand = new Command72("send").description("Send an email").requiredOption("--to <address>", "Recipient email address").requiredOption("--subject <subject>", "Email subject").requiredOption("--body <text>", "Email body text").action(async (opts) => {
|
|
4753
5970
|
const keystore = await loadKeystore();
|
|
4754
5971
|
const { data: result, idempotencyKey } = await apiPost("/api/emails/send", {
|
|
4755
5972
|
deviceId: keystore.deviceId,
|
|
@@ -4762,8 +5979,8 @@ var sendCommand = new Command66("send").description("Send an email").requiredOpt
|
|
|
4762
5979
|
});
|
|
4763
5980
|
|
|
4764
5981
|
// src/commands/email/reply.ts
|
|
4765
|
-
import { Command as
|
|
4766
|
-
var replyCommand = new
|
|
5982
|
+
import { Command as Command73 } from "commander";
|
|
5983
|
+
var replyCommand = new Command73("reply").description("Reply to an email").argument("<id>", "Email ID to reply to").requiredOption("--body <text>", "Reply body text").action(async (id, opts) => {
|
|
4767
5984
|
const keystore = await loadKeystore();
|
|
4768
5985
|
const params = new URLSearchParams({
|
|
4769
5986
|
deviceId: keystore.deviceId,
|
|
@@ -4785,8 +6002,8 @@ var replyCommand = new Command67("reply").description("Reply to an email").argum
|
|
|
4785
6002
|
});
|
|
4786
6003
|
|
|
4787
6004
|
// src/commands/email/delete.ts
|
|
4788
|
-
import { Command as
|
|
4789
|
-
var deleteCommand = new
|
|
6005
|
+
import { Command as Command74 } from "commander";
|
|
6006
|
+
var deleteCommand = new Command74("delete").description("Delete an email by ID").argument("<id>", "Email ID").action(async (id) => {
|
|
4790
6007
|
const keystore = await loadKeystore();
|
|
4791
6008
|
const { data: result, idempotencyKey } = await apiPost(
|
|
4792
6009
|
"/api/emails/delete",
|
|
@@ -4800,15 +6017,15 @@ var deleteCommand = new Command68("delete").description("Delete an email by ID")
|
|
|
4800
6017
|
});
|
|
4801
6018
|
|
|
4802
6019
|
// src/commands/email/address.ts
|
|
4803
|
-
import { Command as
|
|
4804
|
-
var addressCommand2 = new
|
|
6020
|
+
import { Command as Command75 } from "commander";
|
|
6021
|
+
var addressCommand2 = new Command75("address").description("Show your registered email address").action(async () => {
|
|
4805
6022
|
const keystore = await loadKeystore();
|
|
4806
6023
|
const result = await apiGet(`/api/agents/stats?address=${keystore.walletAddress}`);
|
|
4807
6024
|
printResult({ emailAddress: result.emailAddress ?? null });
|
|
4808
6025
|
});
|
|
4809
6026
|
|
|
4810
6027
|
// src/commands/email/mark-read.ts
|
|
4811
|
-
import { Command as
|
|
6028
|
+
import { Command as Command76 } from "commander";
|
|
4812
6029
|
async function setReadStatus(id, read) {
|
|
4813
6030
|
const keystore = await loadKeystore();
|
|
4814
6031
|
const { data: result, idempotencyKey } = await apiPost(
|
|
@@ -4822,12 +6039,12 @@ async function setReadStatus(id, read) {
|
|
|
4822
6039
|
);
|
|
4823
6040
|
printResult({ id: result.id, isRead: result.isRead }, { idempotencyKey });
|
|
4824
6041
|
}
|
|
4825
|
-
var markReadCommand = new
|
|
6042
|
+
var markReadCommand = new Command76("mark-read").description("Mark an email as read or unread").argument("<id>", "Email ID").option("--unread", "Mark as unread instead of read").action(async (id, opts) => {
|
|
4826
6043
|
await setReadStatus(id, !opts.unread);
|
|
4827
6044
|
});
|
|
4828
6045
|
|
|
4829
6046
|
// src/commands/email/index.ts
|
|
4830
|
-
var emailCommand = new
|
|
6047
|
+
var emailCommand = new Command77("email").description("Email management commands");
|
|
4831
6048
|
emailCommand.addCommand(registerCommand);
|
|
4832
6049
|
emailCommand.addCommand(inboxCommand2);
|
|
4833
6050
|
emailCommand.addCommand(readCommand);
|
|
@@ -4837,12 +6054,158 @@ emailCommand.addCommand(deleteCommand);
|
|
|
4837
6054
|
emailCommand.addCommand(addressCommand2);
|
|
4838
6055
|
emailCommand.addCommand(markReadCommand);
|
|
4839
6056
|
|
|
6057
|
+
// src/commands/bug/index.ts
|
|
6058
|
+
import { Command as Command81 } from "commander";
|
|
6059
|
+
|
|
6060
|
+
// src/commands/bug/list.ts
|
|
6061
|
+
import { Command as Command78 } from "commander";
|
|
6062
|
+
var listCommand = new Command78("list").description("List the bug reports you submitted and their status").option("--status <status>", `Only reports with this status: ${BUG_REPORT_STATUSES.join(", ")}`).option("--limit <n>", "Maximum reports to return (1-100)", "50").action(async (opts) => {
|
|
6063
|
+
if (opts.status && !BUG_REPORT_STATUSES.includes(opts.status)) {
|
|
6064
|
+
printError(`--status must be one of ${BUG_REPORT_STATUSES.join(", ")}`);
|
|
6065
|
+
return;
|
|
6066
|
+
}
|
|
6067
|
+
const limit = Number(opts.limit);
|
|
6068
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
6069
|
+
printError("--limit must be an integer from 1 to 100");
|
|
6070
|
+
return;
|
|
6071
|
+
}
|
|
6072
|
+
const auth = await signReadAuth();
|
|
6073
|
+
if (!auth || Object.keys(auth.headers).length === 0) {
|
|
6074
|
+
printError("Listing bug reports needs a wallet. Run `taskmarket init` first.");
|
|
6075
|
+
return;
|
|
6076
|
+
}
|
|
6077
|
+
const params = new URLSearchParams({ limit: String(limit) });
|
|
6078
|
+
if (opts.status) params.set("status", opts.status);
|
|
6079
|
+
try {
|
|
6080
|
+
printResult(
|
|
6081
|
+
await apiGet(`/api/bug-reports/mine?${params.toString()}`, { headers: auth.headers })
|
|
6082
|
+
);
|
|
6083
|
+
} catch (error) {
|
|
6084
|
+
return renderFailure(error);
|
|
6085
|
+
}
|
|
6086
|
+
});
|
|
6087
|
+
|
|
6088
|
+
// src/commands/bug/show.ts
|
|
6089
|
+
import { Command as Command79 } from "commander";
|
|
6090
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6091
|
+
var showCommand = new Command79("show").description("Show one of your bug reports, its status and any linked GitHub issue").argument("<id>", "Bug report id").action(async (id) => {
|
|
6092
|
+
if (!UUID.test(id)) {
|
|
6093
|
+
printError("Bug report id must be a UUID");
|
|
6094
|
+
return;
|
|
6095
|
+
}
|
|
6096
|
+
const auth = await signReadAuth();
|
|
6097
|
+
if (!auth || Object.keys(auth.headers).length === 0) {
|
|
6098
|
+
printError("Reading a bug report needs a wallet. Run `taskmarket init` first.");
|
|
6099
|
+
return;
|
|
6100
|
+
}
|
|
6101
|
+
try {
|
|
6102
|
+
printResult(await apiGet(`/api/bug-reports/${id}`, { headers: auth.headers }));
|
|
6103
|
+
} catch (error) {
|
|
6104
|
+
return renderFailure(error);
|
|
6105
|
+
}
|
|
6106
|
+
});
|
|
6107
|
+
|
|
6108
|
+
// src/commands/bug/submit.ts
|
|
6109
|
+
import { Command as Command80 } from "commander";
|
|
6110
|
+
|
|
6111
|
+
// src/commands/bug/context.ts
|
|
6112
|
+
import os from "os";
|
|
6113
|
+
function collectBugContext(cliVersion2, extra) {
|
|
6114
|
+
return {
|
|
6115
|
+
cliVersion: cliVersion2,
|
|
6116
|
+
os: `${os.platform()} ${os.release()} ${os.arch()}`,
|
|
6117
|
+
nodeVersion: process.version,
|
|
6118
|
+
network: selectedNetwork().name,
|
|
6119
|
+
...extra.taskId ? { taskId: extra.taskId } : {},
|
|
6120
|
+
...extra.txHash ? { txHash: extra.txHash } : {}
|
|
6121
|
+
};
|
|
6122
|
+
}
|
|
6123
|
+
function cliVersionOf(command) {
|
|
6124
|
+
let root = command;
|
|
6125
|
+
while (root.parent) root = root.parent;
|
|
6126
|
+
return root.version() ?? "unknown";
|
|
6127
|
+
}
|
|
6128
|
+
|
|
6129
|
+
// src/commands/bug/submit.ts
|
|
6130
|
+
async function readStdin() {
|
|
6131
|
+
const chunks = [];
|
|
6132
|
+
for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
6133
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
6134
|
+
}
|
|
6135
|
+
var submitCommand = new Command80("submit").description("Report a Taskmarket bug; CLI version, OS, Node version and network are attached").requiredOption("--title <text>", "Short summary (at most 200 characters)").option(
|
|
6136
|
+
"--description <text>",
|
|
6137
|
+
'What happened and how to reproduce it; read from stdin when omitted or "-"'
|
|
6138
|
+
).option("--severity <level>", `How bad it is: ${BUG_REPORT_SEVERITIES.join(", ")}`, "medium").option("--task <id>", "Task the bug happened on").option("--tx <hash>", "Transaction hash involved").option(
|
|
6139
|
+
"--include-last-error",
|
|
6140
|
+
"Attach the last failed taskmarket command and its error (secrets already removed)"
|
|
6141
|
+
).action(async function(opts) {
|
|
6142
|
+
let description = opts.description;
|
|
6143
|
+
if (description === void 0 || description === "-") {
|
|
6144
|
+
if (process.stdin.isTTY) {
|
|
6145
|
+
printError("Pass --description or pipe the description on stdin");
|
|
6146
|
+
return;
|
|
6147
|
+
}
|
|
6148
|
+
description = await readStdin();
|
|
6149
|
+
}
|
|
6150
|
+
const context = collectBugContext(cliVersionOf(this), { taskId: opts.task, txHash: opts.tx });
|
|
6151
|
+
if (opts.includeLastError) {
|
|
6152
|
+
const lastError = readLastError();
|
|
6153
|
+
if (!lastError) {
|
|
6154
|
+
printError("No failed command is recorded yet; run without --include-last-error");
|
|
6155
|
+
return;
|
|
6156
|
+
}
|
|
6157
|
+
context.command = lastError.command;
|
|
6158
|
+
context.error = JSON.stringify({
|
|
6159
|
+
message: lastError.message,
|
|
6160
|
+
code: lastError.code,
|
|
6161
|
+
status: lastError.status,
|
|
6162
|
+
at: lastError.at,
|
|
6163
|
+
cliVersion: lastError.cliVersion
|
|
6164
|
+
});
|
|
6165
|
+
}
|
|
6166
|
+
const parsed = SubmitBugReportInputSchema.safeParse({
|
|
6167
|
+
title: opts.title,
|
|
6168
|
+
description,
|
|
6169
|
+
severity: opts.severity,
|
|
6170
|
+
source: "cli",
|
|
6171
|
+
context
|
|
6172
|
+
});
|
|
6173
|
+
if (!parsed.success) {
|
|
6174
|
+
const issue = parsed.error.issues[0];
|
|
6175
|
+
printError(`Invalid bug report: ${issue.path.join(".") || "input"}: ${issue.message}`);
|
|
6176
|
+
return;
|
|
6177
|
+
}
|
|
6178
|
+
const auth = await signReadAuth();
|
|
6179
|
+
if (!auth || Object.keys(auth.headers).length === 0) {
|
|
6180
|
+
printError("Reporting a bug needs a wallet. Run `taskmarket init` first.");
|
|
6181
|
+
return;
|
|
6182
|
+
}
|
|
6183
|
+
try {
|
|
6184
|
+
const { data, idempotencyKey } = await apiPost(
|
|
6185
|
+
"/api/bug-reports",
|
|
6186
|
+
parsed.data,
|
|
6187
|
+
{ headers: auth.headers }
|
|
6188
|
+
);
|
|
6189
|
+
printResult(data, { idempotencyKey });
|
|
6190
|
+
} catch (error) {
|
|
6191
|
+
return renderFailure(error);
|
|
6192
|
+
}
|
|
6193
|
+
});
|
|
6194
|
+
|
|
6195
|
+
// src/commands/bug/index.ts
|
|
6196
|
+
var bugCommand = new Command81("bug").description(
|
|
6197
|
+
"Report Taskmarket bugs and follow their status"
|
|
6198
|
+
);
|
|
6199
|
+
bugCommand.addCommand(submitCommand);
|
|
6200
|
+
bugCommand.addCommand(listCommand);
|
|
6201
|
+
bugCommand.addCommand(showCommand);
|
|
6202
|
+
|
|
4840
6203
|
// src/commands/requester/index.ts
|
|
4841
|
-
import { Command as
|
|
6204
|
+
import { Command as Command83 } from "commander";
|
|
4842
6205
|
|
|
4843
6206
|
// src/commands/requester/stats.ts
|
|
4844
|
-
import { Command as
|
|
4845
|
-
var requesterStatsCmd = new
|
|
6207
|
+
import { Command as Command82 } from "commander";
|
|
6208
|
+
var requesterStatsCmd = new Command82("stats").description("Get requester reputation stats for an address").argument("<address>", "Wallet address of the requester").action(async (address) => {
|
|
4846
6209
|
if (!/^0x[0-9a-fA-F]{40}$/.test(address)) {
|
|
4847
6210
|
printError("Address must be a 0x-prefixed 20-byte hex string");
|
|
4848
6211
|
return;
|
|
@@ -4856,13 +6219,13 @@ var requesterStatsCmd = new Command72("stats").description("Get requester reputa
|
|
|
4856
6219
|
});
|
|
4857
6220
|
|
|
4858
6221
|
// src/commands/requester/index.ts
|
|
4859
|
-
var requesterCmd = new
|
|
6222
|
+
var requesterCmd = new Command83("requester").description("Requester reputation commands").addCommand(requesterStatsCmd);
|
|
4860
6223
|
|
|
4861
6224
|
// src/commands/x402/index.ts
|
|
4862
|
-
import { Command as
|
|
6225
|
+
import { Command as Command87 } from "commander";
|
|
4863
6226
|
|
|
4864
6227
|
// src/commands/x402/payments.ts
|
|
4865
|
-
import { Command as
|
|
6228
|
+
import { Command as Command84, Option as Option4 } from "commander";
|
|
4866
6229
|
import { createInterface as createInterface4 } from "readline";
|
|
4867
6230
|
var PAYMENT_STATES = [
|
|
4868
6231
|
"reserved",
|
|
@@ -4888,10 +6251,10 @@ async function confirmManualResolution() {
|
|
|
4888
6251
|
readline.close();
|
|
4889
6252
|
return answer.trim().toLowerCase() === "resolve";
|
|
4890
6253
|
}
|
|
4891
|
-
var x402PaymentsCommand = new
|
|
6254
|
+
var x402PaymentsCommand = new Command84("payments").description(
|
|
4892
6255
|
"Inspect and reconcile X402 Pay payment records"
|
|
4893
6256
|
);
|
|
4894
|
-
x402PaymentsCommand.command("list").description("List current payment records").addOption(new
|
|
6257
|
+
x402PaymentsCommand.command("list").description("List current payment records").addOption(new Option4("--state <state>", "Filter by state").choices(PAYMENT_STATES)).action(async (options) => {
|
|
4895
6258
|
const payments = await listX402Payments({ state: options.state }, getX402JournalPath());
|
|
4896
6259
|
printResult({ payments });
|
|
4897
6260
|
});
|
|
@@ -4917,7 +6280,7 @@ x402PaymentsCommand.command("resolve").description("Manually record an externall
|
|
|
4917
6280
|
|
|
4918
6281
|
// src/commands/x402/policy.ts
|
|
4919
6282
|
import { promises as fs4 } from "fs";
|
|
4920
|
-
import { Command as
|
|
6283
|
+
import { Command as Command85 } from "commander";
|
|
4921
6284
|
import { createInterface as createInterface5 } from "readline";
|
|
4922
6285
|
async function confirmUnattendedRule(rule) {
|
|
4923
6286
|
if (!process.stdin.isTTY) {
|
|
@@ -4943,7 +6306,7 @@ async function confirmUnattendedRule(rule) {
|
|
|
4943
6306
|
readline.close();
|
|
4944
6307
|
return answer.trim().toLowerCase() === "authorize";
|
|
4945
6308
|
}
|
|
4946
|
-
var x402PolicyCommand = new
|
|
6309
|
+
var x402PolicyCommand = new Command85("policy").description(
|
|
4947
6310
|
"Manage local X402 Pay spending policy"
|
|
4948
6311
|
);
|
|
4949
6312
|
x402PolicyCommand.command("init").description("Create a deny-all policy file").action(async () => {
|
|
@@ -4979,17 +6342,17 @@ for (const enabled of [true, false]) {
|
|
|
4979
6342
|
}
|
|
4980
6343
|
|
|
4981
6344
|
// src/commands/x402/request.ts
|
|
4982
|
-
import { Command as
|
|
4983
|
-
function
|
|
6345
|
+
import { Command as Command86, Option as Option5 } from "commander";
|
|
6346
|
+
function collect3(value, previous) {
|
|
4984
6347
|
return [...previous, value];
|
|
4985
6348
|
}
|
|
4986
6349
|
function confirm(lines) {
|
|
4987
6350
|
return confirmAtTerminal(lines, "use --non-interactive with policy");
|
|
4988
6351
|
}
|
|
4989
|
-
var x402RequestCommand = new
|
|
6352
|
+
var x402RequestCommand = new Command86("request").description("Request and pay for a direct x402 HTTP resource (X402 Pay)").argument("<url>", "HTTPS resource URL").addOption(new Option5("--method <method>", "HTTP method").choices(["GET", "POST"]).default("GET")).option("--json <json>", "Inline JSON POST body").option("--body-file <path>", "Read JSON POST body from a file").option("--headers-file <path>", "Read request headers from an owner-only JSON file").option(
|
|
4990
6353
|
"--header-env <header=ENV_VAR>",
|
|
4991
6354
|
"Read one request header from an environment variable (repeatable)",
|
|
4992
|
-
|
|
6355
|
+
collect3,
|
|
4993
6356
|
[]
|
|
4994
6357
|
).option("--output <path>", "Write response bytes to a new file").option("--non-interactive", "Authorize only through a persistent unattended policy rule").option("--policy <ruleId>", "Restrict authorization to one policy rule").option(
|
|
4995
6358
|
"--invocation-id <id>",
|
|
@@ -5026,7 +6389,7 @@ var x402RequestCommand = new Command76("request").description("Request and pay f
|
|
|
5026
6389
|
`Asset: ${details.asset}`,
|
|
5027
6390
|
`Spender: ${details.spender}`,
|
|
5028
6391
|
`Bounded allowance: ${details.allowance} atomic units`,
|
|
5029
|
-
`Estimated maximum gas: ${details.estimatedGasWei} wei`
|
|
6392
|
+
details.estimatedGasUsd ? `Estimated maximum gas: ${details.estimatedGasUsd} (paid in the fee token)` : `Estimated maximum gas: ${details.estimatedGasWei} wei`
|
|
5030
6393
|
])
|
|
5031
6394
|
});
|
|
5032
6395
|
printResult(result);
|
|
@@ -5034,7 +6397,7 @@ var x402RequestCommand = new Command76("request").description("Request and pay f
|
|
|
5034
6397
|
);
|
|
5035
6398
|
|
|
5036
6399
|
// src/commands/x402/index.ts
|
|
5037
|
-
var x402Command = new
|
|
6400
|
+
var x402Command = new Command87("x402").description(
|
|
5038
6401
|
"Pay x402 services with local spending policies (X402 Pay)"
|
|
5039
6402
|
);
|
|
5040
6403
|
x402Command.addCommand(x402RequestCommand);
|
|
@@ -5042,11 +6405,11 @@ x402Command.addCommand(x402PolicyCommand);
|
|
|
5042
6405
|
x402Command.addCommand(x402PaymentsCommand);
|
|
5043
6406
|
|
|
5044
6407
|
// src/commands/sponsored-task/index.ts
|
|
5045
|
-
import { Command as
|
|
6408
|
+
import { Command as Command102 } from "commander";
|
|
5046
6409
|
|
|
5047
6410
|
// src/commands/sponsored-task/create.ts
|
|
5048
|
-
import { Command as
|
|
5049
|
-
import { toHex as
|
|
6411
|
+
import { Command as Command88 } from "commander";
|
|
6412
|
+
import { toHex as toHex4 } from "viem";
|
|
5050
6413
|
|
|
5051
6414
|
// ../../packages/contracts/abi/generated/SponsoredTasksVault.ts
|
|
5052
6415
|
var SponsoredTasksVaultABI = [
|
|
@@ -8167,7 +9530,7 @@ import {
|
|
|
8167
9530
|
createPublicClient,
|
|
8168
9531
|
createWalletClient,
|
|
8169
9532
|
defineChain,
|
|
8170
|
-
http
|
|
9533
|
+
http
|
|
8171
9534
|
} from "viem";
|
|
8172
9535
|
function resolveRpcUrl() {
|
|
8173
9536
|
const value = process.env["TASKMARKET_RPC_URL"]?.trim();
|
|
@@ -8186,13 +9549,13 @@ async function createSponsoredTaskVaultChainClients(keystore, chainId) {
|
|
|
8186
9549
|
nativeCurrency: { name: "Native gas token", symbol: "GAS", decimals: 18 },
|
|
8187
9550
|
rpcUrls: { default: { http: [rpcUrl] } }
|
|
8188
9551
|
});
|
|
8189
|
-
const publicClient = createPublicClient({ chain, transport:
|
|
9552
|
+
const publicClient = createPublicClient({ chain, transport: http(rpcUrl) });
|
|
8190
9553
|
const rpcChainId = await publicClient.getChainId();
|
|
8191
9554
|
if (rpcChainId !== chainId) {
|
|
8192
9555
|
throw new Error(`TASKMARKET_RPC_URL points at chain ${rpcChainId}, vault needs ${chainId}`);
|
|
8193
9556
|
}
|
|
8194
9557
|
const account = await createWalletAccountFromKeystore(keystore);
|
|
8195
|
-
const walletClient = createWalletClient({ account, chain, transport:
|
|
9558
|
+
const walletClient = createWalletClient({ account, chain, transport: http(rpcUrl) });
|
|
8196
9559
|
return { account, chain, publicClient, walletClient };
|
|
8197
9560
|
}
|
|
8198
9561
|
async function writeSponsoredTaskVaultContract(input) {
|
|
@@ -8267,7 +9630,7 @@ async function approveSponsoredTaskSpend(input) {
|
|
|
8267
9630
|
function isValidAddress4(addr) {
|
|
8268
9631
|
return /^0x[a-fA-F0-9]{40}$/.test(addr);
|
|
8269
9632
|
}
|
|
8270
|
-
var sponsoredTaskCreateCommand = new
|
|
9633
|
+
var sponsoredTaskCreateCommand = new Command88("create").description("Create a new Sponsored Task vault for a project token").requiredOption("--factory <address>", "SponsoredTaskFactory contract address").requiredOption("--chain-id <id>", "Chain id the factory is deployed on").requiredOption("--token <address>", "Project ERC-20 token address to fund the vault with").option("--owner <address>", "Vault owner (defaults to your own wallet)").action(async (opts) => {
|
|
8271
9634
|
if (!isValidAddress4(opts.factory) || !isValidAddress4(opts.token)) {
|
|
8272
9635
|
printError("Invalid Ethereum address for --factory or --token");
|
|
8273
9636
|
return;
|
|
@@ -8289,7 +9652,7 @@ var sponsoredTaskCreateCommand = new Command78("create").description("Create a n
|
|
|
8289
9652
|
chainId
|
|
8290
9653
|
);
|
|
8291
9654
|
const vaultOwner = opts.owner ?? account.address;
|
|
8292
|
-
const userSalt =
|
|
9655
|
+
const userSalt = toHex4(crypto.getRandomValues(new Uint8Array(32)));
|
|
8293
9656
|
const { txHash, result } = await writeSponsoredTaskVaultContract({
|
|
8294
9657
|
account,
|
|
8295
9658
|
publicClient,
|
|
@@ -8304,11 +9667,11 @@ var sponsoredTaskCreateCommand = new Command78("create").description("Create a n
|
|
|
8304
9667
|
});
|
|
8305
9668
|
|
|
8306
9669
|
// src/commands/sponsored-task/fund.ts
|
|
8307
|
-
import { Command as
|
|
9670
|
+
import { Command as Command89 } from "commander";
|
|
8308
9671
|
function isValidAddress5(addr) {
|
|
8309
9672
|
return /^0x[a-fA-F0-9]{40}$/.test(addr);
|
|
8310
9673
|
}
|
|
8311
|
-
var sponsoredTaskFundCommand = new
|
|
9674
|
+
var sponsoredTaskFundCommand = new Command89("fund").description("Fund a Sponsored Task vault with its project token (base units)").requiredOption("--vault <address>", "Vault address").requiredOption("--token <address>", "Project ERC-20 token address").requiredOption("--chain-id <id>", "Chain id the vault is deployed on").requiredOption("--amount <baseUnits>", "Amount to fund, in the token base units").action(async (opts) => {
|
|
8312
9675
|
if (!isValidAddress5(opts.vault) || !isValidAddress5(opts.token)) {
|
|
8313
9676
|
printError("Invalid Ethereum address for --vault or --token");
|
|
8314
9677
|
return;
|
|
@@ -8347,7 +9710,7 @@ var sponsoredTaskFundCommand = new Command79("fund").description("Fund a Sponsor
|
|
|
8347
9710
|
});
|
|
8348
9711
|
|
|
8349
9712
|
// src/commands/sponsored-task/managers.ts
|
|
8350
|
-
import { Command as
|
|
9713
|
+
import { Command as Command90 } from "commander";
|
|
8351
9714
|
function isValidAddress6(addr) {
|
|
8352
9715
|
return /^0x[a-fA-F0-9]{40}$/.test(addr);
|
|
8353
9716
|
}
|
|
@@ -8393,7 +9756,7 @@ async function configure(opts) {
|
|
|
8393
9756
|
});
|
|
8394
9757
|
printResult({ txHash, manager: opts.manager, spendCap: cap.toString(), active: opts.active });
|
|
8395
9758
|
}
|
|
8396
|
-
var sponsoredTaskManagersCommand = new
|
|
9759
|
+
var sponsoredTaskManagersCommand = new Command90("managers").description(
|
|
8397
9760
|
"Manage authorized managers for a Sponsored Task vault"
|
|
8398
9761
|
);
|
|
8399
9762
|
sponsoredTaskManagersCommand.command("add").description("Authorize a manager with a token-denominated spend cap").requiredOption("--vault <address>", "Vault address").requiredOption("--chain-id <id>", "Chain id the vault is deployed on").requiredOption("--manager <address>", "Manager address to authorize").requiredOption("--cap <baseUnits>", "Spend cap, in the project token base units").action((opts) => configure({ ...opts, active: true }));
|
|
@@ -8404,8 +9767,8 @@ sponsoredTaskManagersCommand.command("list").description("List a vault authorize
|
|
|
8404
9767
|
});
|
|
8405
9768
|
|
|
8406
9769
|
// src/commands/sponsored-task/managed.ts
|
|
8407
|
-
import { Command as
|
|
8408
|
-
var sponsoredTaskManagedCommand = new
|
|
9770
|
+
import { Command as Command91 } from "commander";
|
|
9771
|
+
var sponsoredTaskManagedCommand = new Command91("managed").description(
|
|
8409
9772
|
"List the active Sponsored Task vaults an address can fund tasks from as an active manager (defaults to your wallet)"
|
|
8410
9773
|
).option("--address <address>", "Manager address (defaults to your wallet)").option("--limit <n>", "Max vaults to return (1-100)", "50").option("--cursor <vaultId>", "Pagination cursor from a previous nextCursor").action(async (opts) => {
|
|
8411
9774
|
let address = opts.address;
|
|
@@ -8435,7 +9798,7 @@ var sponsoredTaskManagedCommand = new Command81("managed").description(
|
|
|
8435
9798
|
});
|
|
8436
9799
|
|
|
8437
9800
|
// src/commands/sponsored-task/fee-bank.ts
|
|
8438
|
-
import { Command as
|
|
9801
|
+
import { Command as Command92 } from "commander";
|
|
8439
9802
|
function isValidAddress7(addr) {
|
|
8440
9803
|
return /^0x[a-fA-F0-9]{40}$/.test(addr);
|
|
8441
9804
|
}
|
|
@@ -8447,7 +9810,7 @@ async function requireKeystore2() {
|
|
|
8447
9810
|
return null;
|
|
8448
9811
|
}
|
|
8449
9812
|
}
|
|
8450
|
-
var sponsoredTaskFeeBankCommand = new
|
|
9813
|
+
var sponsoredTaskFeeBankCommand = new Command92("fee-bank").description(
|
|
8451
9814
|
"Manage a manager USDC credit balance, charged by the Sponsored Task hook at task creation"
|
|
8452
9815
|
);
|
|
8453
9816
|
sponsoredTaskFeeBankCommand.command("deposit").description("Deposit USDC credit for creating sponsored tasks").requiredOption("--fee-bank <address>", "SponsoredTaskFeeBank contract address").requiredOption("--usdc <address>", "USDC token address").requiredOption("--chain-id <id>", "Chain id the fee bank is deployed on").requiredOption("--amount <baseUnits>", "Amount to deposit, in USDC base units (6 decimals)").action(async (opts) => {
|
|
@@ -8531,8 +9894,8 @@ sponsoredTaskFeeBankCommand.command("withdraw").description("Withdraw unused USD
|
|
|
8531
9894
|
);
|
|
8532
9895
|
|
|
8533
9896
|
// src/commands/sponsored-task/list.ts
|
|
8534
|
-
import { Command as
|
|
8535
|
-
var sponsoredTaskListCommand = new
|
|
9897
|
+
import { Command as Command93 } from "commander";
|
|
9898
|
+
var sponsoredTaskListCommand = new Command93("list").description("List Sponsored Task vaults").option("--limit <n>", "Max vaults to return", "50").option("--cursor <vaultId>", "Pagination cursor").option("--include-unverified", "Include vaults not yet curator-approved", false).action(async (opts) => {
|
|
8536
9899
|
const params = new URLSearchParams({ limit: opts.limit });
|
|
8537
9900
|
if (opts.cursor) params.set("cursor", opts.cursor);
|
|
8538
9901
|
if (opts.includeUnverified) params.set("includeUnverified", "true");
|
|
@@ -8541,8 +9904,8 @@ var sponsoredTaskListCommand = new Command83("list").description("List Sponsored
|
|
|
8541
9904
|
});
|
|
8542
9905
|
|
|
8543
9906
|
// src/commands/sponsored-task/settlements.ts
|
|
8544
|
-
import { Command as
|
|
8545
|
-
var sponsoredTaskSettlementsCommand = new
|
|
9907
|
+
import { Command as Command94 } from "commander";
|
|
9908
|
+
var sponsoredTaskSettlementsCommand = new Command94("settlements").description("List the settlements a Sponsored Task vault has installed, newest first").argument("<vaultId>", "Vault id").option("--limit <n>", "Max settlements to return (1-100)", "25").option("--cursor <cursor>", "Pagination cursor from a previous nextCursor").action(async (vaultId, opts) => {
|
|
8546
9909
|
const limit = Number(opts.limit);
|
|
8547
9910
|
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
8548
9911
|
printError("--limit must be an integer from 1 to 100");
|
|
@@ -8557,17 +9920,17 @@ var sponsoredTaskSettlementsCommand = new Command84("settlements").description("
|
|
|
8557
9920
|
});
|
|
8558
9921
|
|
|
8559
9922
|
// src/commands/sponsored-task/show.ts
|
|
8560
|
-
import { Command as
|
|
8561
|
-
var sponsoredTaskShowCommand = new
|
|
9923
|
+
import { Command as Command95 } from "commander";
|
|
9924
|
+
var sponsoredTaskShowCommand = new Command95("show").description("Show a Sponsored Task vault by id").argument("<vaultId>", "Vault id").action(async (vaultId) => {
|
|
8562
9925
|
const result = await apiGet(`/api/sponsored-task-vaults/${vaultId}`);
|
|
8563
9926
|
printResult(result);
|
|
8564
9927
|
});
|
|
8565
9928
|
|
|
8566
9929
|
// src/commands/sponsored-task/request-review.ts
|
|
8567
|
-
import { randomBytes as
|
|
8568
|
-
import { Command as
|
|
9930
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
9931
|
+
import { Command as Command96 } from "commander";
|
|
8569
9932
|
var REQUEST_TTL_SECONDS = 5 * 60;
|
|
8570
|
-
var sponsoredTaskRequestReviewCommand = new
|
|
9933
|
+
var sponsoredTaskRequestReviewCommand = new Command96("request-review").description("Ask Taskmarket curators to review a Sponsored Task vault (vault owner only)").argument("<vaultId>", "Vault id").action(async (vaultId) => {
|
|
8571
9934
|
let keystore;
|
|
8572
9935
|
try {
|
|
8573
9936
|
keystore = await loadKeystore();
|
|
@@ -8587,7 +9950,7 @@ var sponsoredTaskRequestReviewCommand = new Command86("request-review").descript
|
|
|
8587
9950
|
);
|
|
8588
9951
|
return;
|
|
8589
9952
|
}
|
|
8590
|
-
const nonce = `0x${
|
|
9953
|
+
const nonce = `0x${randomBytes3(32).toString("hex")}`;
|
|
8591
9954
|
const expiresAt = Math.floor(Date.now() / 1e3) + REQUEST_TTL_SECONDS;
|
|
8592
9955
|
const signature = await signMessage(
|
|
8593
9956
|
buildSponsoredTaskVaultReviewRequestMessage({
|
|
@@ -8624,18 +9987,18 @@ var sponsoredTaskRequestReviewCommand = new Command86("request-review").descript
|
|
|
8624
9987
|
});
|
|
8625
9988
|
|
|
8626
9989
|
// src/commands/sponsored-task/balance.ts
|
|
8627
|
-
import { Command as
|
|
8628
|
-
var sponsoredTaskBalanceCommand = new
|
|
9990
|
+
import { Command as Command97 } from "commander";
|
|
9991
|
+
var sponsoredTaskBalanceCommand = new Command97("balance").description("Show a Sponsored Task vault funded/available/reserved balances").argument("<vaultId>", "Vault id").action(async (vaultId) => {
|
|
8629
9992
|
const result = await apiGet(`/api/sponsored-task-vaults/${vaultId}/balance`);
|
|
8630
9993
|
printResult(result);
|
|
8631
9994
|
});
|
|
8632
9995
|
|
|
8633
9996
|
// src/commands/sponsored-task/withdraw.ts
|
|
8634
|
-
import { Command as
|
|
9997
|
+
import { Command as Command98 } from "commander";
|
|
8635
9998
|
function isValidAddress8(addr) {
|
|
8636
9999
|
return /^0x[a-fA-F0-9]{40}$/.test(addr);
|
|
8637
10000
|
}
|
|
8638
|
-
var sponsoredTaskWithdrawCommand = new
|
|
10001
|
+
var sponsoredTaskWithdrawCommand = new Command98("withdraw").description("Withdraw unreserved project tokens from a vault (owner only)").requiredOption("--vault <address>", "Vault address").requiredOption("--chain-id <id>", "Chain id the vault is deployed on").requiredOption("--amount <baseUnits>", "Amount to withdraw, in the project token base units").option("--recipient <address>", "Recipient (defaults to your own wallet)").action(async (opts) => {
|
|
8639
10002
|
if (!isValidAddress8(opts.vault) || opts.recipient && !isValidAddress8(opts.recipient)) {
|
|
8640
10003
|
printError("Invalid Ethereum address for --vault or --recipient");
|
|
8641
10004
|
return;
|
|
@@ -8665,11 +10028,11 @@ var sponsoredTaskWithdrawCommand = new Command88("withdraw").description("Withdr
|
|
|
8665
10028
|
});
|
|
8666
10029
|
|
|
8667
10030
|
// src/commands/sponsored-task/exit.ts
|
|
8668
|
-
import { Command as
|
|
10031
|
+
import { Command as Command99 } from "commander";
|
|
8669
10032
|
function isValidAddress9(addr) {
|
|
8670
10033
|
return /^0x[a-fA-F0-9]{40}$/.test(addr);
|
|
8671
10034
|
}
|
|
8672
|
-
var sponsoredTaskExitCommand = new
|
|
10035
|
+
var sponsoredTaskExitCommand = new Command99("exit").description(
|
|
8673
10036
|
"Terminally exit a vault: sweeps every reserved and unclaimed token back to you, invalidating every outstanding reservation, claim signature, and Merkle proof at once. There is no undo and no per-allocation revoke -- a worker with a pending but unclaimed entitlement gets nothing once this runs."
|
|
8674
10037
|
).requiredOption("--vault <address>", "Vault address").requiredOption("--chain-id <id>", "Chain id the vault is deployed on").option("--recipient <address>", "Recipient (defaults to your own wallet)").option("--yes", "Skip the confirmation prompt", false).action(async (opts) => {
|
|
8675
10038
|
if (!isValidAddress9(opts.vault) || opts.recipient && !isValidAddress9(opts.recipient)) {
|
|
@@ -8705,7 +10068,7 @@ var sponsoredTaskExitCommand = new Command89("exit").description(
|
|
|
8705
10068
|
});
|
|
8706
10069
|
|
|
8707
10070
|
// src/commands/sponsored-task/claim.ts
|
|
8708
|
-
import { Command as
|
|
10071
|
+
import { Command as Command100 } from "commander";
|
|
8709
10072
|
var CLAIM_TO_TYPES = {
|
|
8710
10073
|
ClaimTo: [
|
|
8711
10074
|
{ name: "vaultId", type: "bytes32" },
|
|
@@ -8726,7 +10089,7 @@ function isRetryableClaimConflict(err) {
|
|
|
8726
10089
|
return err instanceof ApiError && (err.status === 409 || err.status >= 500) && /retry/i.test(err.message);
|
|
8727
10090
|
}
|
|
8728
10091
|
async function postClaimWithRetry(path3, body, options = {}) {
|
|
8729
|
-
const
|
|
10092
|
+
const sleep4 = options.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
8730
10093
|
const deadline = Date.now() + (options.budgetMs ?? CLAIM_RETRY_BUDGET_MS);
|
|
8731
10094
|
const idempotencyKey = resolveIdempotencyKey();
|
|
8732
10095
|
let delayMs = 500;
|
|
@@ -8735,12 +10098,12 @@ async function postClaimWithRetry(path3, body, options = {}) {
|
|
|
8735
10098
|
return await apiPost(path3, body, { idempotencyKey });
|
|
8736
10099
|
} catch (err) {
|
|
8737
10100
|
if (!isRetryableClaimConflict(err) || Date.now() + delayMs > deadline) throw err;
|
|
8738
|
-
await
|
|
10101
|
+
await sleep4(delayMs);
|
|
8739
10102
|
delayMs = Math.min(delayMs * 2, 4e3);
|
|
8740
10103
|
}
|
|
8741
10104
|
}
|
|
8742
10105
|
}
|
|
8743
|
-
var sponsoredTaskClaimCommand = new
|
|
10106
|
+
var sponsoredTaskClaimCommand = new Command100("claim").description("Claim your unclaimed Sponsored Task reward for a vault task").argument("<vaultId>", "Vault id").option(
|
|
8744
10107
|
"--task-id <id>",
|
|
8745
10108
|
"Claim the entitlement for a specific task (defaults to the first unclaimed one)"
|
|
8746
10109
|
).action(async (vaultId, opts) => {
|
|
@@ -8761,7 +10124,13 @@ var sponsoredTaskClaimCommand = new Command90("claim").description("Claim your u
|
|
|
8761
10124
|
);
|
|
8762
10125
|
return;
|
|
8763
10126
|
}
|
|
8764
|
-
|
|
10127
|
+
let destination;
|
|
10128
|
+
try {
|
|
10129
|
+
destination = await verifyWithdrawalAddress(keystore, addressResponse.withdrawalAddress);
|
|
10130
|
+
} catch (err) {
|
|
10131
|
+
renderFailure(err);
|
|
10132
|
+
return;
|
|
10133
|
+
}
|
|
8765
10134
|
const entitlements = await apiGet(
|
|
8766
10135
|
`/api/sponsored-task-vaults/${vaultId}/entitlements?workerAddress=${workerAddress}`
|
|
8767
10136
|
);
|
|
@@ -8820,10 +10189,10 @@ var sponsoredTaskClaimCommand = new Command90("claim").description("Claim your u
|
|
|
8820
10189
|
});
|
|
8821
10190
|
|
|
8822
10191
|
// src/commands/sponsored-task/rewards.ts
|
|
8823
|
-
import { Command as
|
|
10192
|
+
import { Command as Command101 } from "commander";
|
|
8824
10193
|
var REWARD_STATES = SponsoredTaskRewardStateSchema.options;
|
|
8825
10194
|
var CLAIM_STATUSES = SponsoredTaskWorkerRewardClaimStatusSchema.options;
|
|
8826
|
-
var sponsoredTaskRewardsCommand = new
|
|
10195
|
+
var sponsoredTaskRewardsCommand = new Command101("rewards").description("List Sponsored Task rewards across every vault (defaults to your wallet)").option("--address <address>", "Worker address (defaults to your wallet)").option("--state <state>", `Reward state: ${REWARD_STATES.join(", ")}`).option("--claim-status <status>", `Claim status: ${CLAIM_STATUSES.join(", ")}`).option("--limit <n>", "Max rewards to return (1-100)", "50").option("--cursor <cursor>", "Pagination cursor from a previous nextCursor").action(
|
|
8827
10196
|
async (opts) => {
|
|
8828
10197
|
let address = opts.address;
|
|
8829
10198
|
if (address === void 0) {
|
|
@@ -8863,7 +10232,7 @@ var sponsoredTaskRewardsCommand = new Command91("rewards").description("List Spo
|
|
|
8863
10232
|
);
|
|
8864
10233
|
|
|
8865
10234
|
// src/commands/sponsored-task/index.ts
|
|
8866
|
-
var sponsoredTaskCommand = new
|
|
10235
|
+
var sponsoredTaskCommand = new Command102("sponsored-task").description(
|
|
8867
10236
|
"Sponsored Task vault administration and reward claims"
|
|
8868
10237
|
);
|
|
8869
10238
|
sponsoredTaskCommand.addCommand(sponsoredTaskCreateCommand);
|
|
@@ -8884,7 +10253,8 @@ sponsoredTaskCommand.addCommand(sponsoredTaskRewardsCommand);
|
|
|
8884
10253
|
// src/index.ts
|
|
8885
10254
|
var require2 = createRequire(import.meta.url);
|
|
8886
10255
|
var { version } = require2("../package.json");
|
|
8887
|
-
var program = new
|
|
10256
|
+
var program = new Command103();
|
|
10257
|
+
setLastErrorCliVersion(version);
|
|
8888
10258
|
program.name("taskmarket").description("Taskmarket CLI for AI agents").version(version);
|
|
8889
10259
|
program.option(
|
|
8890
10260
|
"--network <name>",
|
|
@@ -8904,7 +10274,7 @@ program.addCommand(walletCommand);
|
|
|
8904
10274
|
program.addCommand(addressCommand);
|
|
8905
10275
|
program.addCommand(identityCommand);
|
|
8906
10276
|
program.addCommand(statsCommand);
|
|
8907
|
-
program.addCommand(
|
|
10277
|
+
program.addCommand(taskCommand2);
|
|
8908
10278
|
program.addCommand(agentCommand);
|
|
8909
10279
|
program.addCommand(agentsCommand);
|
|
8910
10280
|
program.addCommand(inboxCommand);
|
|
@@ -8920,6 +10290,7 @@ program.addCommand(requesterCmd);
|
|
|
8920
10290
|
program.addCommand(legalCommand);
|
|
8921
10291
|
program.addCommand(x402Command);
|
|
8922
10292
|
program.addCommand(sponsoredTaskCommand);
|
|
10293
|
+
program.addCommand(bugCommand);
|
|
8923
10294
|
void (async () => {
|
|
8924
10295
|
try {
|
|
8925
10296
|
await program.parseAsync(process.argv);
|