@lucid-agents/taskmarket 1.7.3 → 1.8.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/index.js +1037 -445
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ var __export = (target, all) => {
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
// src/index.ts
|
|
9
|
-
import { Command as
|
|
9
|
+
import { Command as Command67 } from "commander";
|
|
10
10
|
import { createRequire } from "module";
|
|
11
11
|
|
|
12
12
|
// src/commands/init.ts
|
|
@@ -4271,7 +4271,10 @@ var TaskCreateSchema = external_exports.object({
|
|
|
4271
4271
|
taskVisibility: TaskVisibility.optional().default("public"),
|
|
4272
4272
|
submissionVisibility: SubmissionVisibility.optional().default("public"),
|
|
4273
4273
|
stakeRequired: external_exports.boolean().optional().default(false),
|
|
4274
|
-
|
|
4274
|
+
// Integral: this is client-supplied and lands in a `uint16` on chain, where a fraction is
|
|
4275
|
+
// not representable at all. Without `.int()` a 12.5 was accepted here and silently became
|
|
4276
|
+
// something else by the time it reached the contract (ADR-0069).
|
|
4277
|
+
stakeBps: external_exports.number().int().min(0).max(1e4).optional().default(0),
|
|
4275
4278
|
pitchDeadline: external_exports.number().positive().optional(),
|
|
4276
4279
|
bidDeadline: external_exports.number().positive().optional(),
|
|
4277
4280
|
maxPrice: PositiveUsdcBaseUnitsSchema.optional(),
|
|
@@ -4283,7 +4286,8 @@ var TaskCreateSchema = external_exports.object({
|
|
|
4283
4286
|
hookContract: external_exports.string().optional(),
|
|
4284
4287
|
hookData: external_exports.string().regex(/^0x(?:[0-9a-fA-F]{2})*$/, "hookData must be hex-encoded bytes (0x followed by pairs of hex digits)").optional(),
|
|
4285
4288
|
evaluator: external_exports.string().optional(),
|
|
4286
|
-
|
|
4289
|
+
// Integral for the same reason as `stakeBps` above: client-supplied, `uint16` on chain.
|
|
4290
|
+
evaluatorFeeBps: external_exports.number().int().min(0).max(1e4).optional(),
|
|
4287
4291
|
evaluationWindowHours: external_exports.number().positive().optional(),
|
|
4288
4292
|
appealWindowHours: external_exports.number().positive().optional(),
|
|
4289
4293
|
disputeResolver: external_exports.string().optional(),
|
|
@@ -4399,7 +4403,9 @@ var TaskResponseSchema = external_exports.object({
|
|
|
4399
4403
|
// never the hash itself, and never returned for a non-private task.
|
|
4400
4404
|
hasAccessPassword: external_exports.boolean().optional(),
|
|
4401
4405
|
stakeRequired: external_exports.boolean(),
|
|
4402
|
-
|
|
4406
|
+
// Same shape the request side already enforces (ADR-0069): a `uint16` fraction of the escrow,
|
|
4407
|
+
// so the response cannot describe a stake the contract could not have held.
|
|
4408
|
+
stakeBps: external_exports.number().int().min(0).max(1e4),
|
|
4403
4409
|
pitchDeadline: external_exports.string().nullable(),
|
|
4404
4410
|
bidDeadline: external_exports.string().nullable(),
|
|
4405
4411
|
maxPrice: external_exports.string().nullable(),
|
|
@@ -4407,7 +4413,10 @@ var TaskResponseSchema = external_exports.object({
|
|
|
4407
4413
|
metricTarget: external_exports.string().nullable(),
|
|
4408
4414
|
claimedBy: external_exports.string().nullable(),
|
|
4409
4415
|
claimedAt: external_exports.string().nullable(),
|
|
4410
|
-
|
|
4416
|
+
// Required, never optional: this is the multiplier every net-reward figure a client shows is
|
|
4417
|
+
// derived from, and the column behind it has been `NOT NULL` since the first migration. A
|
|
4418
|
+
// reader that had to supply its own value on absence would be guessing at money.
|
|
4419
|
+
platformFeeBps: external_exports.number().int().min(0).max(1e4),
|
|
4411
4420
|
submissionCount: external_exports.number().optional().default(0),
|
|
4412
4421
|
awardCount: external_exports.number().int().nonnegative().optional(),
|
|
4413
4422
|
// Read-time projection of the rank-1 task_awards row -- not a separately
|
|
@@ -4433,7 +4442,9 @@ var TaskResponseSchema = external_exports.object({
|
|
|
4433
4442
|
hookContract: external_exports.string().nullable().optional(),
|
|
4434
4443
|
evaluator: external_exports.string().nullable().optional(),
|
|
4435
4444
|
evaluatorStake: external_exports.string().nullable().optional(),
|
|
4436
|
-
|
|
4445
|
+
// Genuinely absent when no evaluator is configured -- unlike `platformFeeBps`, null here means
|
|
4446
|
+
// "no evaluator fee", which is the same thing as zero, so a reader coalescing to 0 is right.
|
|
4447
|
+
evaluatorFeeBps: external_exports.number().int().min(0).max(1e4).nullable().optional(),
|
|
4437
4448
|
evaluationWindow: external_exports.number().nullable().optional(),
|
|
4438
4449
|
appealWindow: external_exports.number().nullable().optional(),
|
|
4439
4450
|
disputeResolver: external_exports.string().nullable().optional(),
|
|
@@ -4492,7 +4503,11 @@ var TaskDetailResponseSchema = TaskResponseSchema.extend({
|
|
|
4492
4503
|
pendingActions: PendingActionSchema.array(),
|
|
4493
4504
|
awards: TaskAwardSchema.array().optional(),
|
|
4494
4505
|
dreamsPerUsdc: external_exports.string().optional(),
|
|
4495
|
-
|
|
4506
|
+
// Optional as a set, not individually: the router emits `dreamsPerUsdc`, `bonusBps` and every
|
|
4507
|
+
// `estimated*` field together or not at all, depending on whether the task carries the DREAMS
|
|
4508
|
+
// hook. No client multiplies this by a split of its own -- the worker/requester estimates are
|
|
4509
|
+
// computed server-side -- so the absent case needs no fallback multiplier anywhere.
|
|
4510
|
+
bonusBps: external_exports.number().int().min(0).max(1e4).optional(),
|
|
4496
4511
|
estimatedUsdBonusValue: external_exports.string().optional(),
|
|
4497
4512
|
estimatedWorkerUsdBonusValue: external_exports.string().optional(),
|
|
4498
4513
|
estimatedRequesterUsdBonusValue: external_exports.string().optional(),
|
|
@@ -4506,6 +4521,16 @@ var RejectSubmissionInputSchema = external_exports.object({
|
|
|
4506
4521
|
taskId: external_exports.string(),
|
|
4507
4522
|
worker: external_exports.string()
|
|
4508
4523
|
});
|
|
4524
|
+
var AssignEvaluatorInputSchema = external_exports.object({
|
|
4525
|
+
taskId: external_exports.string(),
|
|
4526
|
+
evaluator: external_exports.string(),
|
|
4527
|
+
// Integral: same field, same `uint16`, and the two request-side schemas must not disagree
|
|
4528
|
+
// about what a caller may send.
|
|
4529
|
+
evaluatorFeeBps: external_exports.number().int().min(0).max(1e4).optional(),
|
|
4530
|
+
evaluationWindowHours: external_exports.number().positive().optional(),
|
|
4531
|
+
appealWindowHours: external_exports.number().positive().optional(),
|
|
4532
|
+
disputeResolver: external_exports.string().optional()
|
|
4533
|
+
});
|
|
4509
4534
|
var RefundExpiredInputSchema = external_exports.object({
|
|
4510
4535
|
taskId: external_exports.string()
|
|
4511
4536
|
});
|
|
@@ -4714,7 +4739,47 @@ var FeedbackSchema = external_exports.object({
|
|
|
4714
4739
|
var HealthResponseSchema = external_exports.object({
|
|
4715
4740
|
status: external_exports.literal("ok"),
|
|
4716
4741
|
timestamp: external_exports.string(),
|
|
4717
|
-
environment: external_exports.string()
|
|
4742
|
+
environment: external_exports.string(),
|
|
4743
|
+
/**
|
|
4744
|
+
* The submission limits this process is actually enforcing, after any environment
|
|
4745
|
+
* override. Reported because a limit a caller cannot observe is a limit a test can only
|
|
4746
|
+
* guess at: smoke-rate-limit.ts asserted against its own copy of the ceiling and had no
|
|
4747
|
+
* way to notice the backend was started with a different one, so it failed with a
|
|
4748
|
+
* confusing boundary error instead of saying the two disagreed.
|
|
4749
|
+
*/
|
|
4750
|
+
limits: external_exports.object({
|
|
4751
|
+
freeSubmissionAllowance: external_exports.number().int().positive(),
|
|
4752
|
+
hardSubmissionCeiling: external_exports.number().int().positive()
|
|
4753
|
+
}),
|
|
4754
|
+
/**
|
|
4755
|
+
* Relayed-intent facts a caller can poll for, in the same spirit as `limits`: a queryable
|
|
4756
|
+
* number, not an alert (ADR-0053).
|
|
4757
|
+
*
|
|
4758
|
+
* Optional because health is what tells you the service is up. If no count is available the
|
|
4759
|
+
* object is omitted rather than reported as a sentinel -- absence under an optional field is
|
|
4760
|
+
* unambiguous, whereas a `-1` or an `"unknown"` string is a value every consumer has to learn
|
|
4761
|
+
* not to plot, and it is also exactly how this endpoint already reads to a caller running
|
|
4762
|
+
* against a build from before the field existed.
|
|
4763
|
+
*
|
|
4764
|
+
* Absent means "no answer here", and it covers a process whose sweep has not run yet as well
|
|
4765
|
+
* as one that does not sweep at all. It never means zero.
|
|
4766
|
+
*/
|
|
4767
|
+
intents: external_exports.object({
|
|
4768
|
+
/**
|
|
4769
|
+
* When the count below was computed, not when it was served.
|
|
4770
|
+
*
|
|
4771
|
+
* These numbers come from a periodic sweep rather than from the request, so the response
|
|
4772
|
+
* is a cache and says so. Without this a consumer cannot tell a live zero from a zero
|
|
4773
|
+
* left behind by a worker that stopped running -- and the second is the more urgent of
|
|
4774
|
+
* the two, because a sweep that is not running is a queue nothing is draining.
|
|
4775
|
+
*/
|
|
4776
|
+
measuredAt: external_exports.string(),
|
|
4777
|
+
/**
|
|
4778
|
+
* Intents that are neither completed nor failed and have not been touched for well past
|
|
4779
|
+
* normal settlement. A count only -- the endpoint is public.
|
|
4780
|
+
*/
|
|
4781
|
+
staleNonTerminal: external_exports.number().int().nonnegative()
|
|
4782
|
+
}).optional()
|
|
4718
4783
|
});
|
|
4719
4784
|
|
|
4720
4785
|
// ../../packages/shared/dist/schemas/pitch.schemas.js
|
|
@@ -4873,8 +4938,12 @@ var WithdrawDreamsOutputSchema = external_exports.object({
|
|
|
4873
4938
|
});
|
|
4874
4939
|
var ExchangeRateOutputSchema = external_exports.object({
|
|
4875
4940
|
dreamsPerUsdc: external_exports.string(),
|
|
4876
|
-
|
|
4877
|
-
|
|
4941
|
+
// Both are read straight off `TaskTokenRewardHook`, which rejects either above 10000
|
|
4942
|
+
// (`InvalidBps`), and both are required here rather than optional: a client that had to
|
|
4943
|
+
// default the split would default it to the whole reward, and one that had to default the
|
|
4944
|
+
// bonus would report no bonus at all. Neither is a fact this endpoint may leave to the reader.
|
|
4945
|
+
workerSplitBps: external_exports.number().int().min(0).max(1e4),
|
|
4946
|
+
bonusBps: external_exports.number().int().min(0).max(1e4)
|
|
4878
4947
|
});
|
|
4879
4948
|
|
|
4880
4949
|
// ../../packages/shared/dist/schemas/xmtp.schemas.js
|
|
@@ -5204,6 +5273,225 @@ var AllowedViewerResponseSchema = external_exports.object({
|
|
|
5204
5273
|
});
|
|
5205
5274
|
var ListAllowedViewersResponseSchema = external_exports.array(AllowedViewerResponseSchema);
|
|
5206
5275
|
|
|
5276
|
+
// ../../packages/shared/dist/schemas/api-error.schemas.js
|
|
5277
|
+
var API_ERROR_REASONS = [
|
|
5278
|
+
/**
|
|
5279
|
+
* No terminal outcome has been established. The write is owned by the settlement path and
|
|
5280
|
+
* may still succeed. **This is not a failure and not a success**, and it makes no claim about
|
|
5281
|
+
* whether the payment moved (ADR-0049 point 3). Never resubmit; poll `intents.get`.
|
|
5282
|
+
*/
|
|
5283
|
+
"intent_in_flight",
|
|
5284
|
+
/**
|
|
5285
|
+
* A write carrying this idempotency key already exists, and this request neither charged the
|
|
5286
|
+
* caller nor submitted anything a second time. Idempotency working, reported as a conflict --
|
|
5287
|
+
* see ADR-0058 for why this is not a 200 carrying the original result.
|
|
5288
|
+
*/
|
|
5289
|
+
"idempotency_key_reused",
|
|
5290
|
+
/**
|
|
5291
|
+
* The request arrived with no `X-Taskmarket-Idempotency-Key` header (the value of
|
|
5292
|
+
* `IDEMPOTENCY_KEY_HEADER`), or one that is not a UUID. Rejected before the 402 challenge,
|
|
5293
|
+
* so nothing was charged (ADR-0052).
|
|
5294
|
+
*/
|
|
5295
|
+
"idempotency_key_required",
|
|
5296
|
+
/**
|
|
5297
|
+
* The key is already bound to a *different* operation, or to a different caller's write. Not
|
|
5298
|
+
* the benign repeat: nothing here can be waited on, and the caller must generate a fresh key.
|
|
5299
|
+
* Distinguished from `idempotency_key_reused` because the correct client response is opposite
|
|
5300
|
+
* -- poll there, resubmit under a new key here.
|
|
5301
|
+
*/
|
|
5302
|
+
"idempotency_key_conflict",
|
|
5303
|
+
/**
|
|
5304
|
+
* The key names the *same* operation by the *same* caller, but with different arguments. One
|
|
5305
|
+
* level finer than `idempotency_key_conflict`, and a distinct reason because the caller's
|
|
5306
|
+
* mistake is distinct: they reused a key while changing what they were asking for, so the
|
|
5307
|
+
* write they just described never happened and the one the key already names is not it
|
|
5308
|
+
* (ADR-0061). Re-send the original arguments to retry the original write, or generate a fresh
|
|
5309
|
+
* key for the new one.
|
|
5310
|
+
*/
|
|
5311
|
+
"idempotency_key_payload_mismatch",
|
|
5312
|
+
/**
|
|
5313
|
+
* The settled payment behind this request has already funded another intent. A retry that
|
|
5314
|
+
* failed to reuse its key, or a replayed payment. The money is spent and this write did not
|
|
5315
|
+
* happen; the caller reads the intent that did consume it from `intents.get`.
|
|
5316
|
+
*/
|
|
5317
|
+
"payment_already_spent",
|
|
5318
|
+
/**
|
|
5319
|
+
* The idempotency precondition could not be read, so the request was refused rather than
|
|
5320
|
+
* risking a double charge on an unverified assumption. Safe to retry with the same key.
|
|
5321
|
+
*/
|
|
5322
|
+
"idempotency_check_unavailable",
|
|
5323
|
+
/**
|
|
5324
|
+
* The chain call is confirmed and the work happened, but recording it did not finish. There
|
|
5325
|
+
* is nothing to refund and nothing to resubmit; the intent is retried automatically.
|
|
5326
|
+
*/
|
|
5327
|
+
"intent_completion_deferred",
|
|
5328
|
+
/**
|
|
5329
|
+
* No intent answers to that id or key *for this caller*. Deliberately one reason for both "no
|
|
5330
|
+
* such intent" and "not yours": telling them apart would make this surface an oracle for which
|
|
5331
|
+
* intent ids and idempotency keys exist (ADR-0049 point 6).
|
|
5332
|
+
*/
|
|
5333
|
+
"intent_not_found",
|
|
5334
|
+
/** The x402 exchange did not produce a settled payment. Nothing was charged. */
|
|
5335
|
+
"payment_rejected",
|
|
5336
|
+
/** A pre-settlement check on the request's own inputs or on task state rejected it. */
|
|
5337
|
+
"payment_preflight_rejected",
|
|
5338
|
+
/**
|
|
5339
|
+
* The settled payment came from an address other than the one the request is authenticated as
|
|
5340
|
+
* acting for. The write was refused; the payment had already settled when the mismatch was
|
|
5341
|
+
* seen, so the fee is not returned by this path.
|
|
5342
|
+
*/
|
|
5343
|
+
"payment_payer_mismatch",
|
|
5344
|
+
/**
|
|
5345
|
+
* The error carries no more specific classification. Present so that every error has a
|
|
5346
|
+
* reason field and a client's switch is total; not a licence to leave one here.
|
|
5347
|
+
*/
|
|
5348
|
+
"unclassified"
|
|
5349
|
+
];
|
|
5350
|
+
var ApiErrorReasonSchema = external_exports.enum(API_ERROR_REASONS);
|
|
5351
|
+
var ApiErrorIntentStatusSchema = external_exports.enum([
|
|
5352
|
+
"reserved",
|
|
5353
|
+
"recorded",
|
|
5354
|
+
"broadcast",
|
|
5355
|
+
"completed",
|
|
5356
|
+
"failed"
|
|
5357
|
+
]);
|
|
5358
|
+
var REASONS_WITHOUT_REQUIRED_INTENT_STATUS = API_ERROR_REASONS.filter((reason) => reason !== "idempotency_key_reused");
|
|
5359
|
+
var ENVELOPE_FIELDS = {
|
|
5360
|
+
/** The durable handle to the write, where one was recorded. Key on this, not on `txHash`. */
|
|
5361
|
+
intentId: external_exports.string().optional(),
|
|
5362
|
+
/** The operation the intent stands for, e.g. `tasks.create`. */
|
|
5363
|
+
operation: external_exports.string().optional(),
|
|
5364
|
+
/** The caller's own key for the operation, echoed so a dropped response is still traceable. */
|
|
5365
|
+
idempotencyKey: external_exports.string().optional(),
|
|
5366
|
+
/**
|
|
5367
|
+
* Known once something has been broadcast, and explicitly not the handle: the reconciler may
|
|
5368
|
+
* land a replacement at the same nonce, which is a different hash for the same intent.
|
|
5369
|
+
*/
|
|
5370
|
+
txHash: external_exports.string().optional()
|
|
5371
|
+
};
|
|
5372
|
+
var ApiErrorEnvelopeSchema = external_exports.discriminatedUnion("reason", [
|
|
5373
|
+
external_exports.object({
|
|
5374
|
+
reason: external_exports.literal("idempotency_key_reused"),
|
|
5375
|
+
/** Required here: the reason means an intent under this key exists, so it has a status. */
|
|
5376
|
+
intentStatus: ApiErrorIntentStatusSchema,
|
|
5377
|
+
...ENVELOPE_FIELDS
|
|
5378
|
+
}),
|
|
5379
|
+
external_exports.object({
|
|
5380
|
+
reason: external_exports.enum(REASONS_WITHOUT_REQUIRED_INTENT_STATUS),
|
|
5381
|
+
/** The intent's status at the moment this answer was produced, where there is an intent. */
|
|
5382
|
+
intentStatus: ApiErrorIntentStatusSchema.optional(),
|
|
5383
|
+
...ENVELOPE_FIELDS
|
|
5384
|
+
})
|
|
5385
|
+
]);
|
|
5386
|
+
var IN_FLIGHT_REASONS = /* @__PURE__ */ new Set([
|
|
5387
|
+
"intent_in_flight",
|
|
5388
|
+
"idempotency_key_reused",
|
|
5389
|
+
// Not "may still succeed" but "already succeeded, still being written down". Both are
|
|
5390
|
+
// answered the same way -- poll, never resubmit -- and reporting this one as settled told a
|
|
5391
|
+
// script following the documented rule that re-running it was an ordinary retry. It is not:
|
|
5392
|
+
// the chain call is confirmed, so a re-run is a second paid action for work already done.
|
|
5393
|
+
"intent_completion_deferred"
|
|
5394
|
+
]);
|
|
5395
|
+
function isInFlightApiError(envelope) {
|
|
5396
|
+
if (!envelope || !IN_FLIGHT_REASONS.has(envelope.reason))
|
|
5397
|
+
return false;
|
|
5398
|
+
if (envelope.reason !== "idempotency_key_reused")
|
|
5399
|
+
return true;
|
|
5400
|
+
return envelope.intentStatus !== "completed" && envelope.intentStatus !== "failed";
|
|
5401
|
+
}
|
|
5402
|
+
function apiErrorEnvelopeOf(value) {
|
|
5403
|
+
if (!value || typeof value !== "object")
|
|
5404
|
+
return null;
|
|
5405
|
+
const at = (source, key) => source && typeof source === "object" ? source[key] : void 0;
|
|
5406
|
+
const candidate = at(value, "taskmarket") ?? at(at(value, "data"), "taskmarket") ?? at(at(at(value, "error"), "data"), "taskmarket") ?? value;
|
|
5407
|
+
const parsed = ApiErrorEnvelopeSchema.safeParse(candidate);
|
|
5408
|
+
return parsed.success ? parsed.data : null;
|
|
5409
|
+
}
|
|
5410
|
+
|
|
5411
|
+
// ../../packages/shared/dist/schemas/intent.schemas.js
|
|
5412
|
+
var IntentStatusInputSchema = external_exports.object({
|
|
5413
|
+
intentId: external_exports.string().min(1).optional(),
|
|
5414
|
+
idempotencyKey: external_exports.string().min(1).optional()
|
|
5415
|
+
}).refine((value) => Boolean(value.intentId) !== Boolean(value.idempotencyKey), {
|
|
5416
|
+
message: "Provide exactly one of intentId or idempotencyKey"
|
|
5417
|
+
});
|
|
5418
|
+
var IntentRefundStateSchema = external_exports.object({
|
|
5419
|
+
status: external_exports.enum(["pending", "refunding", "refunded", "failed"]),
|
|
5420
|
+
txHash: external_exports.string().nullable()
|
|
5421
|
+
});
|
|
5422
|
+
var IntentStatusResponseSchema = external_exports.object({
|
|
5423
|
+
intentId: external_exports.string(),
|
|
5424
|
+
idempotencyKey: external_exports.string(),
|
|
5425
|
+
/** The operation kind, e.g. `tasks.create`. */
|
|
5426
|
+
operation: external_exports.string(),
|
|
5427
|
+
/**
|
|
5428
|
+
* `reserved` is the pre-payment state (ADR-0067): the key is claimed and the caller has been
|
|
5429
|
+
* challenged, but no payment has landed and nothing has been sent to the chain. It is
|
|
5430
|
+
* non-terminal, it is never broadcastable, and it expires if it is never filled.
|
|
5431
|
+
*/
|
|
5432
|
+
status: external_exports.enum(["reserved", "recorded", "broadcast", "completed", "failed"]),
|
|
5433
|
+
/**
|
|
5434
|
+
* Known once something has been broadcast, and explicitly not the handle: the reconciler
|
|
5435
|
+
* may land a replacement at the same nonce, which is a different hash for the same intent.
|
|
5436
|
+
*/
|
|
5437
|
+
txHash: external_exports.string().nullable(),
|
|
5438
|
+
/** Why it failed, where it did. Null on every non-terminal status. */
|
|
5439
|
+
terminalReason: external_exports.string().nullable(),
|
|
5440
|
+
/**
|
|
5441
|
+
* The task a completed `tasks.create` produced.
|
|
5442
|
+
*
|
|
5443
|
+
* This is where a caller whose creation went in flight learns the id. The id does not
|
|
5444
|
+
* exist until the transaction runs -- the contract derives it from a requester nonce it
|
|
5445
|
+
* increments as it goes -- so an in-flight creation has no id to report, and reporting one
|
|
5446
|
+
* would be a guess of the kind that made concurrent creates overwrite each other. Null
|
|
5447
|
+
* until the intent completes, and null for every other operation.
|
|
5448
|
+
*/
|
|
5449
|
+
taskId: external_exports.string().nullable(),
|
|
5450
|
+
/**
|
|
5451
|
+
* Only present where the intent carried a payment and that payment has been written off.
|
|
5452
|
+
* Null means no refund has been decided -- which is not the same as "no refund is coming"
|
|
5453
|
+
* while the intent is still in flight.
|
|
5454
|
+
*/
|
|
5455
|
+
refund: IntentRefundStateSchema.nullable()
|
|
5456
|
+
}).superRefine((value, ctx) => {
|
|
5457
|
+
if (value.status === "reserved") {
|
|
5458
|
+
for (const field of ["txHash", "terminalReason", "taskId"]) {
|
|
5459
|
+
if (value[field] !== null) {
|
|
5460
|
+
ctx.addIssue({
|
|
5461
|
+
code: external_exports.ZodIssueCode.custom,
|
|
5462
|
+
path: [field],
|
|
5463
|
+
message: `A reserved intent has not been sent to the chain, so ${field} must be null`
|
|
5464
|
+
});
|
|
5465
|
+
}
|
|
5466
|
+
}
|
|
5467
|
+
return;
|
|
5468
|
+
}
|
|
5469
|
+
if (value.terminalReason !== null && value.status !== "failed") {
|
|
5470
|
+
ctx.addIssue({
|
|
5471
|
+
code: external_exports.ZodIssueCode.custom,
|
|
5472
|
+
path: ["terminalReason"],
|
|
5473
|
+
message: "Only a failed intent has a terminal reason"
|
|
5474
|
+
});
|
|
5475
|
+
}
|
|
5476
|
+
if (value.taskId === null)
|
|
5477
|
+
return;
|
|
5478
|
+
if (value.status !== "completed") {
|
|
5479
|
+
ctx.addIssue({
|
|
5480
|
+
code: external_exports.ZodIssueCode.custom,
|
|
5481
|
+
path: ["taskId"],
|
|
5482
|
+
message: "taskId is only known once the intent has completed"
|
|
5483
|
+
});
|
|
5484
|
+
return;
|
|
5485
|
+
}
|
|
5486
|
+
if (value.operation !== "tasks.create") {
|
|
5487
|
+
ctx.addIssue({
|
|
5488
|
+
code: external_exports.ZodIssueCode.custom,
|
|
5489
|
+
path: ["taskId"],
|
|
5490
|
+
message: "Only a tasks.create intent produces a taskId"
|
|
5491
|
+
});
|
|
5492
|
+
}
|
|
5493
|
+
});
|
|
5494
|
+
|
|
5207
5495
|
// ../../packages/shared/dist/platform.constants.js
|
|
5208
5496
|
var USDC_DECIMALS = 6;
|
|
5209
5497
|
|
|
@@ -5508,6 +5796,7 @@ function buildReadAuthMessage(address) {
|
|
|
5508
5796
|
var READ_AUTH_ADDRESS_HEADER = "X-Taskmarket-Caller-Address";
|
|
5509
5797
|
var READ_AUTH_SIGNATURE_HEADER = "X-Taskmarket-Caller-Signature";
|
|
5510
5798
|
var TASK_ACCESS_GRANT_HEADER = "X-Taskmarket-Task-Access-Grant";
|
|
5799
|
+
var IDEMPOTENCY_KEY_HEADER = "X-Taskmarket-Idempotency-Key";
|
|
5511
5800
|
function buildSubmitMessage(taskId, contentBindings) {
|
|
5512
5801
|
return contentBindings ? `taskmarket:submit:${taskId}:${contentBindings.join(",")}` : `taskmarket:submit:${taskId}`;
|
|
5513
5802
|
}
|
|
@@ -6035,17 +6324,103 @@ async function keystoreExists(keystorePath) {
|
|
|
6035
6324
|
}
|
|
6036
6325
|
}
|
|
6037
6326
|
|
|
6327
|
+
// src/lib/idempotency.ts
|
|
6328
|
+
import { randomUUID } from "crypto";
|
|
6329
|
+
var pendingEnvKey = process.env.TASKMARKET_IDEMPOTENCY_KEY?.trim() || void 0;
|
|
6330
|
+
var keyByError = /* @__PURE__ */ new WeakMap();
|
|
6331
|
+
function rememberIdempotencyKeyFor(error, key) {
|
|
6332
|
+
if (error === null || typeof error !== "object" && typeof error !== "function") return;
|
|
6333
|
+
if (keyByError.has(error)) return;
|
|
6334
|
+
keyByError.set(error, key);
|
|
6335
|
+
}
|
|
6336
|
+
function idempotencyKeyForError(error) {
|
|
6337
|
+
if (error === null || typeof error !== "object" && typeof error !== "function")
|
|
6338
|
+
return void 0;
|
|
6339
|
+
return keyByError.get(error);
|
|
6340
|
+
}
|
|
6341
|
+
function newIdempotencyKey() {
|
|
6342
|
+
return randomUUID();
|
|
6343
|
+
}
|
|
6344
|
+
function resolveIdempotencyKey(explicit) {
|
|
6345
|
+
if (explicit !== void 0) {
|
|
6346
|
+
return explicit;
|
|
6347
|
+
}
|
|
6348
|
+
if (pendingEnvKey !== void 0) {
|
|
6349
|
+
const fromEnv = pendingEnvKey;
|
|
6350
|
+
pendingEnvKey = void 0;
|
|
6351
|
+
return fromEnv;
|
|
6352
|
+
}
|
|
6353
|
+
return newIdempotencyKey();
|
|
6354
|
+
}
|
|
6355
|
+
async function withIdempotentWrite(key, fn) {
|
|
6356
|
+
try {
|
|
6357
|
+
return await fn();
|
|
6358
|
+
} catch (error) {
|
|
6359
|
+
rememberIdempotencyKeyFor(error, key);
|
|
6360
|
+
throw error;
|
|
6361
|
+
}
|
|
6362
|
+
}
|
|
6363
|
+
function idempotencyHeaders(key) {
|
|
6364
|
+
return { [IDEMPOTENCY_KEY_HEADER]: key };
|
|
6365
|
+
}
|
|
6366
|
+
|
|
6038
6367
|
// src/lib/api.ts
|
|
6039
6368
|
var API_URL = process.env.TASKMARKET_API_URL ?? "https://api.taskmarket.dev";
|
|
6040
6369
|
var API_ORIGIN = new URL(API_URL).origin;
|
|
6041
6370
|
var ApiError = class extends Error {
|
|
6042
6371
|
status;
|
|
6043
|
-
|
|
6372
|
+
/**
|
|
6373
|
+
* The idempotency key the failed write was sent under, when the request was a write. This is
|
|
6374
|
+
* the only identifier the caller can still hold after a failure -- the intent id is minted by
|
|
6375
|
+
* the backend and reaches the caller only in the response the failure destroyed -- so it
|
|
6376
|
+
* travels on the error rather than being recoverable from the response. See
|
|
6377
|
+
* apps/cli/src/index.ts and lib/output.ts, which surface it on the CLI's JSON envelope.
|
|
6378
|
+
*/
|
|
6379
|
+
idempotencyKey;
|
|
6380
|
+
/**
|
|
6381
|
+
* The backend's machine-readable classification of the failure (ADR-0058).
|
|
6382
|
+
*
|
|
6383
|
+
* Before it existed the CLI rendered every failure identically, which is why
|
|
6384
|
+
* `docs/CLI_GUIDE.md` had to tell script authors never to auto-retry a paid command: there was
|
|
6385
|
+
* no way to tell a write that is still landing from one that was definitively rejected, and
|
|
6386
|
+
* retrying the first is a second payment. `reason` is that way.
|
|
6387
|
+
*
|
|
6388
|
+
* Absent when the backend answered with no envelope -- an older deployment, or a failure that
|
|
6389
|
+
* never reached the API at all -- in which case a caller must fall back to the old rule and
|
|
6390
|
+
* treat the outcome as unknown rather than as safe to retry.
|
|
6391
|
+
*/
|
|
6392
|
+
envelope;
|
|
6393
|
+
constructor(status, message, idempotencyKey, envelope) {
|
|
6044
6394
|
super(message);
|
|
6045
6395
|
this.name = "ApiError";
|
|
6046
6396
|
this.status = status;
|
|
6397
|
+
this.idempotencyKey = idempotencyKey;
|
|
6398
|
+
this.envelope = envelope;
|
|
6047
6399
|
}
|
|
6048
6400
|
};
|
|
6401
|
+
async function readFailureBody(res) {
|
|
6402
|
+
const text = await res.text().catch(() => "");
|
|
6403
|
+
try {
|
|
6404
|
+
return { body: JSON.parse(text), text };
|
|
6405
|
+
} catch {
|
|
6406
|
+
return { body: void 0, text };
|
|
6407
|
+
}
|
|
6408
|
+
}
|
|
6409
|
+
function failureMessage(prefix, status, read) {
|
|
6410
|
+
return `${prefix} (${status}): ${read.body !== void 0 ? JSON.stringify(read.body) : read.text}`;
|
|
6411
|
+
}
|
|
6412
|
+
function withErrorContext(error, context) {
|
|
6413
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6414
|
+
const wrapped = error instanceof ApiError ? new ApiError(error.status, `${context}: ${message}`, error.idempotencyKey, error.envelope) : new Error(`${context}: ${message}`);
|
|
6415
|
+
const key = idempotencyKeyForError(error);
|
|
6416
|
+
if (key !== void 0) {
|
|
6417
|
+
rememberIdempotencyKeyFor(wrapped, key);
|
|
6418
|
+
}
|
|
6419
|
+
return wrapped;
|
|
6420
|
+
}
|
|
6421
|
+
function isPendingApiError(error) {
|
|
6422
|
+
return error instanceof ApiError && isInFlightApiError(error.envelope);
|
|
6423
|
+
}
|
|
6049
6424
|
function legalReceiptHeadersForKeystore(keystore, path4, method) {
|
|
6050
6425
|
const canSend = keystore.legalAcceptanceApiOrigin === API_ORIGIN && (method === "POST" || path4 === "/api/legal/status");
|
|
6051
6426
|
return canSend ? buildLegalReceiptHeaders(keystore.legalAcceptanceReceipt) : {};
|
|
@@ -6069,61 +6444,100 @@ async function apiGet(path4, options) {
|
|
|
6069
6444
|
...options?.headers ?? {}
|
|
6070
6445
|
}
|
|
6071
6446
|
});
|
|
6072
|
-
const body = await res.json();
|
|
6073
6447
|
if (!res.ok) {
|
|
6074
|
-
|
|
6448
|
+
const read = await readFailureBody(res);
|
|
6449
|
+
throw new ApiError(
|
|
6450
|
+
res.status,
|
|
6451
|
+
failureMessage(`GET ${path4} failed`, res.status, read),
|
|
6452
|
+
void 0,
|
|
6453
|
+
apiErrorEnvelopeOf(read.body) ?? void 0
|
|
6454
|
+
);
|
|
6075
6455
|
}
|
|
6076
|
-
return
|
|
6456
|
+
return res.json();
|
|
6077
6457
|
}
|
|
6078
6458
|
async function apiPost(path4, body, options) {
|
|
6079
6459
|
const legalHeaders = await legalReceiptHeaders(path4, "POST");
|
|
6080
|
-
const
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6460
|
+
const idempotencyKey = resolveIdempotencyKey(options?.idempotencyKey);
|
|
6461
|
+
return withIdempotentWrite(idempotencyKey, async () => {
|
|
6462
|
+
const res = await fetch(`${API_URL}${path4}`, {
|
|
6463
|
+
method: "POST",
|
|
6464
|
+
redirect: "error",
|
|
6465
|
+
headers: {
|
|
6466
|
+
"Content-Type": "application/json",
|
|
6467
|
+
...legalHeaders,
|
|
6468
|
+
...idempotencyHeaders(idempotencyKey),
|
|
6469
|
+
...options?.headers ?? {}
|
|
6470
|
+
},
|
|
6471
|
+
body: JSON.stringify(body)
|
|
6472
|
+
});
|
|
6473
|
+
if (!res.ok) {
|
|
6474
|
+
const read = await readFailureBody(res);
|
|
6475
|
+
throw new ApiError(
|
|
6476
|
+
res.status,
|
|
6477
|
+
failureMessage(`POST ${path4} failed`, res.status, read),
|
|
6478
|
+
idempotencyKey,
|
|
6479
|
+
apiErrorEnvelopeOf(read.body) ?? void 0
|
|
6480
|
+
);
|
|
6481
|
+
}
|
|
6482
|
+
return { data: await res.json(), idempotencyKey };
|
|
6089
6483
|
});
|
|
6090
|
-
const result = await res.json();
|
|
6091
|
-
if (!res.ok) {
|
|
6092
|
-
throw new ApiError(
|
|
6093
|
-
res.status,
|
|
6094
|
-
`POST ${path4} failed (${res.status}): ${JSON.stringify(result)}`
|
|
6095
|
-
);
|
|
6096
|
-
}
|
|
6097
|
-
return result;
|
|
6098
6484
|
}
|
|
6099
6485
|
async function apiDelete(path4, options) {
|
|
6100
6486
|
const legalHeaders = await legalReceiptHeaders(path4, "POST");
|
|
6101
|
-
const
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6487
|
+
const idempotencyKey = resolveIdempotencyKey(options?.idempotencyKey);
|
|
6488
|
+
return withIdempotentWrite(idempotencyKey, async () => {
|
|
6489
|
+
const res = await fetch(`${API_URL}${path4}`, {
|
|
6490
|
+
method: "DELETE",
|
|
6491
|
+
redirect: "error",
|
|
6492
|
+
headers: {
|
|
6493
|
+
"Content-Type": "application/json",
|
|
6494
|
+
...legalHeaders,
|
|
6495
|
+
...idempotencyHeaders(idempotencyKey),
|
|
6496
|
+
...options?.headers ?? {}
|
|
6497
|
+
}
|
|
6498
|
+
});
|
|
6499
|
+
if (!res.ok) {
|
|
6500
|
+
const read = await readFailureBody(res);
|
|
6501
|
+
throw new ApiError(
|
|
6502
|
+
res.status,
|
|
6503
|
+
failureMessage(`DELETE ${path4} failed`, res.status, read),
|
|
6504
|
+
idempotencyKey,
|
|
6505
|
+
apiErrorEnvelopeOf(read.body) ?? void 0
|
|
6506
|
+
);
|
|
6108
6507
|
}
|
|
6508
|
+
return { data: await res.json(), idempotencyKey };
|
|
6109
6509
|
});
|
|
6110
|
-
const result = await res.json();
|
|
6111
|
-
if (!res.ok) {
|
|
6112
|
-
throw new ApiError(
|
|
6113
|
-
res.status,
|
|
6114
|
-
`DELETE ${path4} failed (${res.status}): ${JSON.stringify(result)}`
|
|
6115
|
-
);
|
|
6116
|
-
}
|
|
6117
|
-
return result;
|
|
6118
6510
|
}
|
|
6119
6511
|
|
|
6120
6512
|
// src/lib/output.ts
|
|
6121
|
-
function printResult(data) {
|
|
6122
|
-
|
|
6513
|
+
function printResult(data, options) {
|
|
6514
|
+
const idempotencyKey = options?.idempotencyKey;
|
|
6515
|
+
console.log(
|
|
6516
|
+
JSON.stringify({ ok: true, data, ...idempotencyKey !== void 0 ? { idempotencyKey } : {} })
|
|
6517
|
+
);
|
|
6123
6518
|
}
|
|
6124
|
-
function
|
|
6519
|
+
function renderFailure(error, options) {
|
|
6520
|
+
const message = error instanceof Error ? error.message : options?.fallback ?? String(error);
|
|
6521
|
+
const apiError = error instanceof ApiError ? error : void 0;
|
|
6522
|
+
const idempotencyKey = options?.idempotencyKey ?? apiError?.idempotencyKey ?? idempotencyKeyForError(error);
|
|
6523
|
+
const envelope = apiError?.envelope;
|
|
6524
|
+
process.stderr.write(
|
|
6525
|
+
JSON.stringify({
|
|
6526
|
+
ok: false,
|
|
6527
|
+
error: message,
|
|
6528
|
+
...apiError !== void 0 ? { status: apiError.status } : {},
|
|
6529
|
+
...idempotencyKey !== void 0 ? { idempotencyKey } : {},
|
|
6530
|
+
...envelope !== void 0 ? { ...envelope, pending: isInFlightApiError(envelope) } : {},
|
|
6531
|
+
...options?.details ?? {}
|
|
6532
|
+
}) + "\n"
|
|
6533
|
+
);
|
|
6534
|
+
process.exitCode = 1;
|
|
6535
|
+
}
|
|
6536
|
+
function printError(message, options) {
|
|
6537
|
+
return renderFailure(new Error(message), options);
|
|
6538
|
+
}
|
|
6539
|
+
function printWarning(message) {
|
|
6125
6540
|
process.stderr.write(JSON.stringify({ ok: false, error: message }) + "\n");
|
|
6126
|
-
process.exit(1);
|
|
6127
6541
|
}
|
|
6128
6542
|
|
|
6129
6543
|
// src/lib/agent.ts
|
|
@@ -6372,21 +6786,21 @@ async function acceptLegalBundle(options) {
|
|
|
6372
6786
|
}
|
|
6373
6787
|
await confirmAcceptance(bundle, options.assumeYes);
|
|
6374
6788
|
const challenge = LegalChallengeResponseSchema.parse(
|
|
6375
|
-
await apiPost("/api/legal/challenge", {
|
|
6789
|
+
(await apiPost("/api/legal/challenge", {
|
|
6376
6790
|
walletAddress: options.walletAddress
|
|
6377
|
-
})
|
|
6791
|
+
})).data
|
|
6378
6792
|
);
|
|
6379
6793
|
validateLegalChallenge(bundle, challenge, options.walletAddress);
|
|
6380
6794
|
const signature = await options.signMessage(challenge.message);
|
|
6381
6795
|
const result = LegalAcceptanceResponseSchema.parse(
|
|
6382
|
-
await apiPost("/api/legal/accept/wallet", {
|
|
6796
|
+
(await apiPost("/api/legal/accept/wallet", {
|
|
6383
6797
|
...affirmations,
|
|
6384
6798
|
bundleDigest: bundle.bundleDigest,
|
|
6385
6799
|
bundleVersion: bundle.version,
|
|
6386
6800
|
nonce: challenge.nonce,
|
|
6387
6801
|
signature,
|
|
6388
6802
|
walletAddress: options.walletAddress
|
|
6389
|
-
})
|
|
6803
|
+
})).data
|
|
6390
6804
|
);
|
|
6391
6805
|
if (result.bundleDigest !== bundle.bundleDigest || result.bundleVersion !== bundle.version) {
|
|
6392
6806
|
throw new Error("The server returned a receipt for a different legal bundle.");
|
|
@@ -6458,7 +6872,7 @@ function sanitizeEmailUsername(agentId, suffix) {
|
|
|
6458
6872
|
}
|
|
6459
6873
|
async function tryRegisterEmail(deviceId, apiToken, username, explicit) {
|
|
6460
6874
|
try {
|
|
6461
|
-
const reg = await apiPost("/api/emails/register", {
|
|
6875
|
+
const { data: reg } = await apiPost("/api/emails/register", {
|
|
6462
6876
|
deviceId,
|
|
6463
6877
|
apiToken,
|
|
6464
6878
|
username
|
|
@@ -6467,11 +6881,8 @@ async function tryRegisterEmail(deviceId, apiToken, username, explicit) {
|
|
|
6467
6881
|
} catch (err) {
|
|
6468
6882
|
if (explicit) {
|
|
6469
6883
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
ok: false,
|
|
6473
|
-
error: `Email registration failed: ${msg}. Run: taskmarket email register --username ${username}`
|
|
6474
|
-
}) + "\n"
|
|
6884
|
+
printWarning(
|
|
6885
|
+
`Email registration failed: ${msg}. Run: taskmarket email register --username ${username}`
|
|
6475
6886
|
);
|
|
6476
6887
|
}
|
|
6477
6888
|
return null;
|
|
@@ -6490,6 +6901,7 @@ var initCommand = new Command2("init").description("Create and register a new ag
|
|
|
6490
6901
|
);
|
|
6491
6902
|
if (!check.available) {
|
|
6492
6903
|
printError(`Email username "${opts.email}" is not available.`);
|
|
6904
|
+
return;
|
|
6493
6905
|
}
|
|
6494
6906
|
}
|
|
6495
6907
|
if (await keystoreExists()) {
|
|
@@ -6606,68 +7018,80 @@ var addressCommand = new Command3("address").description("Print the wallet addre
|
|
|
6606
7018
|
import { Command as Command4 } from "commander";
|
|
6607
7019
|
|
|
6608
7020
|
// src/lib/x402.ts
|
|
6609
|
-
async function x402Post(path4, body) {
|
|
7021
|
+
async function x402Post(path4, body, options) {
|
|
6610
7022
|
const url = `${API_URL}${path4}`;
|
|
6611
7023
|
const keystore = await loadKeystore();
|
|
6612
7024
|
const legalHeaders = legalReceiptHeadersForKeystore(keystore, path4, "POST");
|
|
6613
|
-
const
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
}
|
|
6654
|
-
|
|
7025
|
+
const idempotencyKey = resolveIdempotencyKey(options?.idempotencyKey);
|
|
7026
|
+
const idempotency = idempotencyHeaders(idempotencyKey);
|
|
7027
|
+
return withIdempotentWrite(idempotencyKey, async () => {
|
|
7028
|
+
const r1 = await fetch(url, {
|
|
7029
|
+
method: "POST",
|
|
7030
|
+
redirect: "error",
|
|
7031
|
+
headers: { "Content-Type": "application/json", ...legalHeaders, ...idempotency },
|
|
7032
|
+
body: JSON.stringify(body)
|
|
7033
|
+
});
|
|
7034
|
+
if (r1.status !== 402) {
|
|
7035
|
+
if (r1.ok) {
|
|
7036
|
+
return { data: await r1.json(), idempotencyKey };
|
|
7037
|
+
}
|
|
7038
|
+
const read = await readFailureBody(r1);
|
|
7039
|
+
throw new ApiError(
|
|
7040
|
+
r1.status,
|
|
7041
|
+
`POST ${path4} failed (${r1.status}): ${read.text}`,
|
|
7042
|
+
idempotencyKey,
|
|
7043
|
+
apiErrorEnvelopeOf(read.body) ?? void 0
|
|
7044
|
+
);
|
|
7045
|
+
}
|
|
7046
|
+
const requirements = await r1.json();
|
|
7047
|
+
if (!requirements.accepts || requirements.accepts.length === 0) {
|
|
7048
|
+
throw new Error("No payment methods accepted by server");
|
|
7049
|
+
}
|
|
7050
|
+
const accept = requirements.accepts[0];
|
|
7051
|
+
const { authorization, signature } = await createTransferAuthorization(accept, keystore);
|
|
7052
|
+
const paymentPayload = {
|
|
7053
|
+
x402Version: 2,
|
|
7054
|
+
resource: requirements.resource,
|
|
7055
|
+
accepted: {
|
|
7056
|
+
scheme: accept.scheme,
|
|
7057
|
+
network: accept.network,
|
|
7058
|
+
amount: accept.amount,
|
|
7059
|
+
asset: accept.asset,
|
|
7060
|
+
payTo: accept.payTo,
|
|
7061
|
+
maxTimeoutSeconds: accept.maxTimeoutSeconds,
|
|
7062
|
+
extra: accept.extra
|
|
7063
|
+
},
|
|
7064
|
+
payload: { authorization, signature }
|
|
7065
|
+
};
|
|
7066
|
+
const r2 = await fetch(url, {
|
|
7067
|
+
method: "POST",
|
|
7068
|
+
redirect: "error",
|
|
7069
|
+
headers: {
|
|
7070
|
+
"Content-Type": "application/json",
|
|
7071
|
+
...legalHeaders,
|
|
7072
|
+
...idempotency,
|
|
7073
|
+
"PAYMENT-SIGNATURE": Buffer.from(JSON.stringify(paymentPayload)).toString("base64")
|
|
7074
|
+
},
|
|
7075
|
+
body: JSON.stringify(body)
|
|
7076
|
+
});
|
|
7077
|
+
if (!r2.ok) {
|
|
7078
|
+
const read = await readFailureBody(r2);
|
|
7079
|
+
throw new ApiError(
|
|
7080
|
+
r2.status,
|
|
7081
|
+
failureMessage(`POST ${path4} failed after payment`, r2.status, read),
|
|
7082
|
+
idempotencyKey,
|
|
7083
|
+
apiErrorEnvelopeOf(read.body) ?? void 0
|
|
7084
|
+
);
|
|
7085
|
+
}
|
|
7086
|
+
return { data: await r2.json(), idempotencyKey };
|
|
6655
7087
|
});
|
|
6656
|
-
const result = await r2.json();
|
|
6657
|
-
if (!r2.ok) {
|
|
6658
|
-
throw new ApiError(
|
|
6659
|
-
r2.status,
|
|
6660
|
-
`POST ${path4} failed after payment (${r2.status}): ${JSON.stringify(result)}`
|
|
6661
|
-
);
|
|
6662
|
-
}
|
|
6663
|
-
return result;
|
|
6664
7088
|
}
|
|
6665
7089
|
|
|
6666
7090
|
// src/commands/identity.ts
|
|
6667
7091
|
var identityCommand = new Command4("identity").description("Manage agent identity");
|
|
6668
7092
|
identityCommand.command("register").description("Register ERC-8004 agent identity (costs 0.001 USDC)").action(async () => {
|
|
6669
|
-
const result = await x402Post("/api/identity/register", {});
|
|
6670
|
-
printResult({ agentId: result.agentId });
|
|
7093
|
+
const { data: result, idempotencyKey } = await x402Post("/api/identity/register", {});
|
|
7094
|
+
printResult({ agentId: result.agentId }, { idempotencyKey });
|
|
6671
7095
|
});
|
|
6672
7096
|
identityCommand.command("status").description("Check identity registration status").action(async () => {
|
|
6673
7097
|
const keystore = await loadKeystore();
|
|
@@ -6723,7 +7147,7 @@ var statsCommand = new Command5("stats").description("View agent statistics").op
|
|
|
6723
7147
|
});
|
|
6724
7148
|
|
|
6725
7149
|
// src/commands/task/index.ts
|
|
6726
|
-
import { Command as
|
|
7150
|
+
import { Command as Command41 } from "commander";
|
|
6727
7151
|
|
|
6728
7152
|
// src/commands/task/create.ts
|
|
6729
7153
|
import { Command as Command6 } from "commander";
|
|
@@ -6759,9 +7183,8 @@ var createCmd = new Command6("create").description("Create a new task (costs rew
|
|
|
6759
7183
|
try {
|
|
6760
7184
|
rewardBaseUnits = usdcToBaseUnits(opts.reward);
|
|
6761
7185
|
} catch (err) {
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
);
|
|
7186
|
+
renderFailure(withErrorContext(err, "Invalid --reward"));
|
|
7187
|
+
return;
|
|
6765
7188
|
}
|
|
6766
7189
|
const duration = Number(opts.duration);
|
|
6767
7190
|
if (!Number.isFinite(duration) || duration <= 0) {
|
|
@@ -6821,9 +7244,8 @@ var createCmd = new Command6("create").description("Create a new task (costs rew
|
|
|
6821
7244
|
try {
|
|
6822
7245
|
maxPriceBaseUnits = usdcToBaseUnits(opts.maxPrice);
|
|
6823
7246
|
} catch (err) {
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
);
|
|
7247
|
+
renderFailure(withErrorContext(err, "Invalid --max-price"));
|
|
7248
|
+
return;
|
|
6827
7249
|
}
|
|
6828
7250
|
if (maxPriceBaseUnits !== rewardBaseUnits) {
|
|
6829
7251
|
return void printError(
|
|
@@ -6865,18 +7287,16 @@ var createCmd = new Command6("create").description("Create a new task (costs rew
|
|
|
6865
7287
|
try {
|
|
6866
7288
|
body.auctionStartPrice = usdcToBaseUnits(opts.auctionStartPrice, { allowZero: true });
|
|
6867
7289
|
} catch (err) {
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
);
|
|
7290
|
+
renderFailure(withErrorContext(err, "Invalid --auction-start-price"));
|
|
7291
|
+
return;
|
|
6871
7292
|
}
|
|
6872
7293
|
}
|
|
6873
7294
|
if (opts.auctionFloorPrice) {
|
|
6874
7295
|
try {
|
|
6875
7296
|
body.auctionFloorPrice = usdcToBaseUnits(opts.auctionFloorPrice, { allowZero: true });
|
|
6876
7297
|
} catch (err) {
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
);
|
|
7298
|
+
renderFailure(withErrorContext(err, "Invalid --auction-floor-price"));
|
|
7299
|
+
return;
|
|
6880
7300
|
}
|
|
6881
7301
|
}
|
|
6882
7302
|
for (const [flag, value] of [
|
|
@@ -6924,8 +7344,11 @@ var createCmd = new Command6("create").description("Create a new task (costs rew
|
|
|
6924
7344
|
if (opts.appealWindow) body.appealWindowHours = parseFloat(opts.appealWindow);
|
|
6925
7345
|
if (opts.disputeResolver) body.disputeResolver = opts.disputeResolver;
|
|
6926
7346
|
}
|
|
6927
|
-
const result = await x402Post(
|
|
6928
|
-
|
|
7347
|
+
const { data: result, idempotencyKey } = await x402Post(
|
|
7348
|
+
"/api/tasks",
|
|
7349
|
+
body
|
|
7350
|
+
);
|
|
7351
|
+
printResult({ taskId: result.taskId }, { idempotencyKey });
|
|
6929
7352
|
}
|
|
6930
7353
|
);
|
|
6931
7354
|
|
|
@@ -7036,6 +7459,7 @@ var getCmd = new Command8("get").description("Get task details").argument("<task
|
|
|
7036
7459
|
printError(
|
|
7037
7460
|
`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>)`
|
|
7038
7461
|
);
|
|
7462
|
+
return;
|
|
7039
7463
|
}
|
|
7040
7464
|
printResult(task);
|
|
7041
7465
|
});
|
|
@@ -7136,6 +7560,7 @@ var submitCmd = new Command9("submit").description("Submit work for a task").arg
|
|
|
7136
7560
|
try {
|
|
7137
7561
|
if (!opts.file || opts.file.length === 0) {
|
|
7138
7562
|
printError("--file is required");
|
|
7563
|
+
return;
|
|
7139
7564
|
}
|
|
7140
7565
|
const keystore = await loadKeystore();
|
|
7141
7566
|
const signature = await signMessage(buildSubmitMessage(taskId), keystore);
|
|
@@ -7144,9 +7569,11 @@ var submitCmd = new Command9("submit").description("Submit work for a task").arg
|
|
|
7144
7569
|
const mimeType = mimeTypeForPath(filePath);
|
|
7145
7570
|
const data = await fsPromises.readFile(filePath);
|
|
7146
7571
|
if (data.length === 0) {
|
|
7147
|
-
|
|
7572
|
+
throw new Error(`File is empty: ${basename(filePath)}`);
|
|
7148
7573
|
}
|
|
7149
|
-
const {
|
|
7574
|
+
const {
|
|
7575
|
+
data: { uploadUrl, artifactKey }
|
|
7576
|
+
} = await apiPost(
|
|
7150
7577
|
`/api/tasks/${taskId}/submissions/request-upload-url`,
|
|
7151
7578
|
{
|
|
7152
7579
|
taskId,
|
|
@@ -7181,13 +7608,16 @@ var submitCmd = new Command9("submit").description("Submit work for a task").arg
|
|
|
7181
7608
|
),
|
|
7182
7609
|
keystore
|
|
7183
7610
|
);
|
|
7184
|
-
const result = await x402Post(
|
|
7185
|
-
taskId
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7611
|
+
const { data: result, idempotencyKey } = await x402Post(
|
|
7612
|
+
`/api/tasks/${taskId}/submissions/from-keys`,
|
|
7613
|
+
{
|
|
7614
|
+
taskId,
|
|
7615
|
+
workerAddress: keystore.walletAddress,
|
|
7616
|
+
artifacts: artifactInputs,
|
|
7617
|
+
signature: submitSignature
|
|
7618
|
+
}
|
|
7619
|
+
);
|
|
7620
|
+
printResult({ submissionId: result.submissionId }, { idempotencyKey });
|
|
7191
7621
|
} finally {
|
|
7192
7622
|
submitCmd.setOptionValue("file", []);
|
|
7193
7623
|
}
|
|
@@ -7199,7 +7629,6 @@ var acceptCmd = new Command10("accept").description("Accept a submission (costs
|
|
|
7199
7629
|
const task = await apiGet(`/api/tasks/${taskId}`);
|
|
7200
7630
|
if (!task) {
|
|
7201
7631
|
printError("Task not found");
|
|
7202
|
-
process.exit(1);
|
|
7203
7632
|
return;
|
|
7204
7633
|
}
|
|
7205
7634
|
const canAccept = task.pendingActions?.some(
|
|
@@ -7215,14 +7644,13 @@ var acceptCmd = new Command10("accept").description("Accept a submission (costs
|
|
|
7215
7644
|
printError(
|
|
7216
7645
|
hasRefund ? "Task has expired with no submissions. 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."
|
|
7217
7646
|
);
|
|
7218
|
-
process.exit(1);
|
|
7219
7647
|
return;
|
|
7220
7648
|
}
|
|
7221
|
-
await x402Post(`/api/tasks/${taskId}/accept`, {
|
|
7649
|
+
const { idempotencyKey } = await x402Post(`/api/tasks/${taskId}/accept`, {
|
|
7222
7650
|
taskId,
|
|
7223
7651
|
worker: opts.worker
|
|
7224
7652
|
});
|
|
7225
|
-
printResult({ accepted: true });
|
|
7653
|
+
printResult({ accepted: true }, { idempotencyKey });
|
|
7226
7654
|
});
|
|
7227
7655
|
|
|
7228
7656
|
// src/commands/task/accept-submissions.ts
|
|
@@ -7257,13 +7685,16 @@ var acceptSubmissionsCmd = new Command11("accept-submissions").description(
|
|
|
7257
7685
|
if (sum !== 1e4) {
|
|
7258
7686
|
throw new Error(`Winner shares must sum to 10000 basis points (got ${sum})`);
|
|
7259
7687
|
}
|
|
7260
|
-
const result = await x402Post(
|
|
7261
|
-
taskId
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7688
|
+
const { data: result, idempotencyKey } = await x402Post(
|
|
7689
|
+
`/api/tasks/${taskId}/accept-submissions`,
|
|
7690
|
+
{
|
|
7691
|
+
taskId,
|
|
7692
|
+
winners
|
|
7693
|
+
}
|
|
7694
|
+
);
|
|
7695
|
+
printResult({ accepted: result.success, winners: winners.length }, { idempotencyKey });
|
|
7265
7696
|
} catch (err) {
|
|
7266
|
-
|
|
7697
|
+
renderFailure(err);
|
|
7267
7698
|
}
|
|
7268
7699
|
});
|
|
7269
7700
|
|
|
@@ -7273,14 +7704,15 @@ var rateCmd = new Command12("rate").description("Rate a worker (costs 0.001 USDC
|
|
|
7273
7704
|
const rating = parseInt(opts.rating, 10);
|
|
7274
7705
|
if (rating < 0 || rating > 100) {
|
|
7275
7706
|
printError("Rating must be between 0 and 100");
|
|
7707
|
+
return;
|
|
7276
7708
|
}
|
|
7277
|
-
const result = await x402Post(`/api/tasks/${taskId}/rate`, {
|
|
7709
|
+
const { data: result, idempotencyKey } = await x402Post(`/api/tasks/${taskId}/rate`, {
|
|
7278
7710
|
taskId,
|
|
7279
7711
|
worker: opts.worker,
|
|
7280
7712
|
rating,
|
|
7281
7713
|
...opts.feedback ? { feedbackText: opts.feedback } : {}
|
|
7282
7714
|
});
|
|
7283
|
-
printResult({ feedbackId: result.feedbackId });
|
|
7715
|
+
printResult({ feedbackId: result.feedbackId }, { idempotencyKey });
|
|
7284
7716
|
});
|
|
7285
7717
|
|
|
7286
7718
|
// src/commands/task/claim.ts
|
|
@@ -7289,11 +7721,14 @@ var claimCmd = new Command13("claim").description("Claim a task as a worker").ar
|
|
|
7289
7721
|
const keystore = await loadKeystore();
|
|
7290
7722
|
const message = buildClaimMessage(taskId);
|
|
7291
7723
|
const signature = await signMessage(message, keystore);
|
|
7292
|
-
const result = await apiPost(
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
7724
|
+
const { data: result, idempotencyKey } = await apiPost(
|
|
7725
|
+
`/api/tasks/${taskId}/claim`,
|
|
7726
|
+
{
|
|
7727
|
+
workerAddress: keystore.walletAddress,
|
|
7728
|
+
signature
|
|
7729
|
+
}
|
|
7730
|
+
);
|
|
7731
|
+
printResult({ claimId: result.claimId }, { idempotencyKey });
|
|
7297
7732
|
});
|
|
7298
7733
|
|
|
7299
7734
|
// src/commands/task/pitch.ts
|
|
@@ -7301,14 +7736,17 @@ import { Command as Command14 } from "commander";
|
|
|
7301
7736
|
var pitchCmd = new Command14("pitch").description("Submit a pitch for a task (costs 0.001 USDC, anchors hash on-chain)").argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption("--text <text>", "Pitch text").option("--duration <hours>", "Estimated duration in hours").action(async (taskId, opts) => {
|
|
7302
7737
|
const keystore = await loadKeystore();
|
|
7303
7738
|
const signature = await signMessage(`taskmarket:pitch:${taskId}`, keystore);
|
|
7304
|
-
const result = await x402Post(
|
|
7305
|
-
taskId
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7739
|
+
const { data: result, idempotencyKey } = await x402Post(
|
|
7740
|
+
`/api/tasks/${taskId}/pitches`,
|
|
7741
|
+
{
|
|
7742
|
+
taskId,
|
|
7743
|
+
workerAddress: keystore.walletAddress,
|
|
7744
|
+
pitchText: opts.text,
|
|
7745
|
+
...opts.duration ? { estimatedDuration: parseInt(opts.duration, 10) } : {},
|
|
7746
|
+
signature
|
|
7747
|
+
}
|
|
7748
|
+
);
|
|
7749
|
+
printResult({ pitchId: result.pitchId }, { idempotencyKey });
|
|
7312
7750
|
});
|
|
7313
7751
|
|
|
7314
7752
|
// src/commands/task/pitches.ts
|
|
@@ -7319,7 +7757,7 @@ var pitchesCmd = new Command15("pitches").description("List pitches for a task")
|
|
|
7319
7757
|
const headers = { ...auth?.headers ?? {}, ...await taskAccessGrantHeaders(taskId) };
|
|
7320
7758
|
printResult(await apiGet(`/api/tasks/${taskId}/pitches`, { headers }));
|
|
7321
7759
|
} catch (err) {
|
|
7322
|
-
|
|
7760
|
+
renderFailure(err);
|
|
7323
7761
|
}
|
|
7324
7762
|
});
|
|
7325
7763
|
|
|
@@ -7329,13 +7767,13 @@ var selectWorkerCmd = new Command16("select-worker").description("Select a worke
|
|
|
7329
7767
|
const keystore = await loadKeystore();
|
|
7330
7768
|
const message = buildSelectWorkerMessage(taskId, opts.pitch, opts.worker);
|
|
7331
7769
|
const signature = await signMessage(message, keystore);
|
|
7332
|
-
await apiPost(`/api/tasks/${taskId}/pitches/select`, {
|
|
7770
|
+
const { idempotencyKey } = await apiPost(`/api/tasks/${taskId}/pitches/select`, {
|
|
7333
7771
|
taskId,
|
|
7334
7772
|
pitchId: opts.pitch,
|
|
7335
7773
|
workerAddress: opts.worker,
|
|
7336
7774
|
signature
|
|
7337
7775
|
});
|
|
7338
|
-
printResult({ selected: true });
|
|
7776
|
+
printResult({ selected: true }, { idempotencyKey });
|
|
7339
7777
|
});
|
|
7340
7778
|
|
|
7341
7779
|
// src/commands/task/proof.ts
|
|
@@ -7343,7 +7781,7 @@ import { Command as Command17 } from "commander";
|
|
|
7343
7781
|
var proofCmd = new Command17("proof").description("Submit a benchmark proof (costs 0.001 USDC, 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) => {
|
|
7344
7782
|
const keystore = await loadKeystore();
|
|
7345
7783
|
const signature = await signMessage(`taskmarket:proof:${taskId}`, keystore);
|
|
7346
|
-
const result = await x402Post(`/api/tasks/${taskId}/proofs`, {
|
|
7784
|
+
const { data: result, idempotencyKey } = await x402Post(`/api/tasks/${taskId}/proofs`, {
|
|
7347
7785
|
taskId,
|
|
7348
7786
|
workerAddress: keystore.walletAddress,
|
|
7349
7787
|
proofData: opts.data,
|
|
@@ -7351,7 +7789,7 @@ var proofCmd = new Command17("proof").description("Submit a benchmark proof (cos
|
|
|
7351
7789
|
...opts.metric ? { metricValue: opts.metric } : {},
|
|
7352
7790
|
signature
|
|
7353
7791
|
});
|
|
7354
|
-
printResult({ proofId: result.proofId, submissionId: result.submissionId });
|
|
7792
|
+
printResult({ proofId: result.proofId, submissionId: result.submissionId }, { idempotencyKey });
|
|
7355
7793
|
});
|
|
7356
7794
|
|
|
7357
7795
|
// src/commands/task/proofs.ts
|
|
@@ -7362,7 +7800,7 @@ var proofsCmd = new Command18("proofs").description("List proofs for a benchmark
|
|
|
7362
7800
|
const headers = { ...auth?.headers ?? {}, ...await taskAccessGrantHeaders(taskId) };
|
|
7363
7801
|
printResult(await apiGet(`/api/tasks/${taskId}/proofs`, { headers }));
|
|
7364
7802
|
} catch (err) {
|
|
7365
|
-
|
|
7803
|
+
renderFailure(err);
|
|
7366
7804
|
}
|
|
7367
7805
|
});
|
|
7368
7806
|
|
|
@@ -7373,15 +7811,17 @@ var bidCmd = new Command19("bid").description("Submit a bid on an auction task")
|
|
|
7373
7811
|
try {
|
|
7374
7812
|
priceBaseUnits = usdcToBaseUnits(opts.price);
|
|
7375
7813
|
} catch (err) {
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
);
|
|
7814
|
+
renderFailure(withErrorContext(err, "Invalid --price"));
|
|
7815
|
+
return;
|
|
7379
7816
|
}
|
|
7380
|
-
const result = await x402Post(
|
|
7381
|
-
taskId
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7817
|
+
const { data: result, idempotencyKey } = await x402Post(
|
|
7818
|
+
`/api/tasks/${taskId}/bids`,
|
|
7819
|
+
{
|
|
7820
|
+
taskId,
|
|
7821
|
+
price: priceBaseUnits
|
|
7822
|
+
}
|
|
7823
|
+
);
|
|
7824
|
+
printResult({ bidId: result.bidId }, { idempotencyKey });
|
|
7385
7825
|
});
|
|
7386
7826
|
|
|
7387
7827
|
// src/commands/task/auction-accept.ts
|
|
@@ -7395,17 +7835,19 @@ var auctionAcceptCmd = new Command20("auction-accept").description("Accept curre
|
|
|
7395
7835
|
try {
|
|
7396
7836
|
body.minPrice = usdcToBaseUnits(opts.minPrice, { allowZero: true });
|
|
7397
7837
|
} catch (error) {
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
);
|
|
7838
|
+
renderFailure(withErrorContext(error, "Invalid --min-price"));
|
|
7839
|
+
return;
|
|
7401
7840
|
}
|
|
7402
7841
|
}
|
|
7403
|
-
const result = await x402Post(`/api/tasks/${taskId}/bids/accept`, body);
|
|
7404
|
-
printResult(
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7842
|
+
const { data: result, idempotencyKey } = await x402Post(`/api/tasks/${taskId}/bids/accept`, body);
|
|
7843
|
+
printResult(
|
|
7844
|
+
{
|
|
7845
|
+
acceptedPrice: result.acceptedPrice,
|
|
7846
|
+
acceptedPriceUsdc: formatUsdcBaseUnits(result.acceptedPrice),
|
|
7847
|
+
workerAddress: result.workerAddress
|
|
7848
|
+
},
|
|
7849
|
+
{ idempotencyKey }
|
|
7850
|
+
);
|
|
7409
7851
|
});
|
|
7410
7852
|
|
|
7411
7853
|
// src/commands/task/submissions.ts
|
|
@@ -7416,8 +7858,8 @@ var submissionsCmd = new Command21("submissions").description("List submissions
|
|
|
7416
7858
|
try {
|
|
7417
7859
|
subs = await apiGet(`/api/tasks/${taskId}/submissions`, { headers: auth?.headers ?? {} });
|
|
7418
7860
|
} catch (err) {
|
|
7419
|
-
|
|
7420
|
-
|
|
7861
|
+
renderFailure(err, { fallback: "Failed to fetch submissions." });
|
|
7862
|
+
return;
|
|
7421
7863
|
}
|
|
7422
7864
|
printResult(subs);
|
|
7423
7865
|
});
|
|
@@ -7426,15 +7868,17 @@ var submissionsCmd = new Command21("submissions").description("List submissions
|
|
|
7426
7868
|
import { Command as Command22 } from "commander";
|
|
7427
7869
|
var selectWinnerCmd = new Command22("select-winner").description("Permissionlessly finalize the lowest bidder after the auction deadline").argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
7428
7870
|
let result;
|
|
7871
|
+
let idempotencyKey;
|
|
7429
7872
|
try {
|
|
7430
|
-
result = await apiPost(
|
|
7431
|
-
taskId
|
|
7432
|
-
|
|
7873
|
+
({ data: result, idempotencyKey } = await apiPost(
|
|
7874
|
+
`/api/tasks/${taskId}/bids/select-winner`,
|
|
7875
|
+
{ taskId }
|
|
7876
|
+
));
|
|
7433
7877
|
} catch (err) {
|
|
7434
|
-
|
|
7435
|
-
|
|
7878
|
+
renderFailure(err, { fallback: "Failed to select winner." });
|
|
7879
|
+
return;
|
|
7436
7880
|
}
|
|
7437
|
-
printResult(result);
|
|
7881
|
+
printResult(result, { idempotencyKey });
|
|
7438
7882
|
});
|
|
7439
7883
|
|
|
7440
7884
|
// src/commands/task/download.ts
|
|
@@ -7457,18 +7901,19 @@ var downloadCmd = new Command23("download").description("Download a submission f
|
|
|
7457
7901
|
if (opts.artifact) {
|
|
7458
7902
|
body.artifactId = opts.artifact;
|
|
7459
7903
|
}
|
|
7460
|
-
const result = await apiPost(
|
|
7904
|
+
const { data: result } = await apiPost(
|
|
7461
7905
|
`/api/tasks/${taskId}/submissions/${opts.submission}/preview`,
|
|
7462
7906
|
body
|
|
7463
7907
|
);
|
|
7464
7908
|
presignedUrl = result.presignedUrl;
|
|
7465
7909
|
} catch (err) {
|
|
7466
|
-
|
|
7467
|
-
|
|
7910
|
+
renderFailure(err, { fallback: "Failed to get download URL." });
|
|
7911
|
+
return;
|
|
7468
7912
|
}
|
|
7469
7913
|
const res = await fetch(presignedUrl);
|
|
7470
7914
|
if (!res.ok) {
|
|
7471
7915
|
printError(`Failed to download file: ${res.status}`);
|
|
7916
|
+
return;
|
|
7472
7917
|
}
|
|
7473
7918
|
const content = Buffer.from(await res.arrayBuffer());
|
|
7474
7919
|
if (opts.output) {
|
|
@@ -7482,6 +7927,7 @@ var downloadCmd = new Command23("download").description("Download a submission f
|
|
|
7482
7927
|
printError(
|
|
7483
7928
|
"Downloaded content is not valid UTF-8 text and cannot be safely printed to the terminal. Use --output <path> to save it to a file instead."
|
|
7484
7929
|
);
|
|
7930
|
+
return;
|
|
7485
7931
|
}
|
|
7486
7932
|
process.stdout.write(sanitizeForTerminal(decoded));
|
|
7487
7933
|
}
|
|
@@ -7491,8 +7937,11 @@ var downloadCmd = new Command23("download").description("Download a submission f
|
|
|
7491
7937
|
// src/commands/task/cancel.ts
|
|
7492
7938
|
import { Command as Command24 } from "commander";
|
|
7493
7939
|
var cancelCmd = new Command24("cancel").description("Cancel an open task and refund the escrowed reward (costs 0.001 USDC)").argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
7494
|
-
const result = await x402Post(
|
|
7495
|
-
|
|
7940
|
+
const { data: result, idempotencyKey } = await x402Post(
|
|
7941
|
+
`/api/tasks/${taskId}/cancel`,
|
|
7942
|
+
{ taskId }
|
|
7943
|
+
);
|
|
7944
|
+
printResult(result, { idempotencyKey });
|
|
7496
7945
|
});
|
|
7497
7946
|
|
|
7498
7947
|
// src/commands/task/refund-expired.ts
|
|
@@ -7501,11 +7950,13 @@ var refundExpiredCmd = new Command25("refund-expired").description(
|
|
|
7501
7950
|
"Refund an expired task with no submissions back to the requester (costs 0.001 USDC)"
|
|
7502
7951
|
).argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
7503
7952
|
try {
|
|
7504
|
-
const result = await x402Post(
|
|
7505
|
-
|
|
7953
|
+
const { data: result, idempotencyKey } = await x402Post(
|
|
7954
|
+
`/api/tasks/${taskId}/refund-expired`,
|
|
7955
|
+
{ taskId }
|
|
7956
|
+
);
|
|
7957
|
+
printResult(result, { idempotencyKey });
|
|
7506
7958
|
} catch (err) {
|
|
7507
|
-
|
|
7508
|
-
process.exit(1);
|
|
7959
|
+
renderFailure(err);
|
|
7509
7960
|
}
|
|
7510
7961
|
});
|
|
7511
7962
|
|
|
@@ -7515,14 +7966,16 @@ var rejectSubmissionCmd = new Command26("reject-submission").description(
|
|
|
7515
7966
|
"Reject a spam or low-quality submission (costs 0.001 USDC relay fee). Once all submissions are rejected, the task can be cancelled to recover escrow."
|
|
7516
7967
|
).argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption("--worker <address>", "Worker address whose submission to reject").action(async (taskId, opts) => {
|
|
7517
7968
|
try {
|
|
7518
|
-
const result = await x402Post(
|
|
7519
|
-
taskId
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7969
|
+
const { data: result, idempotencyKey } = await x402Post(
|
|
7970
|
+
`/api/tasks/${taskId}/reject-submission`,
|
|
7971
|
+
{
|
|
7972
|
+
taskId,
|
|
7973
|
+
worker: opts.worker
|
|
7974
|
+
}
|
|
7975
|
+
);
|
|
7976
|
+
printResult(result, { idempotencyKey });
|
|
7523
7977
|
} catch (err) {
|
|
7524
|
-
|
|
7525
|
-
process.exit(1);
|
|
7978
|
+
renderFailure(err);
|
|
7526
7979
|
}
|
|
7527
7980
|
});
|
|
7528
7981
|
|
|
@@ -7544,7 +7997,8 @@ var rejectAllSubmissionsCmd = new Command27("reject-all-submissions").descriptio
|
|
|
7544
7997
|
try {
|
|
7545
7998
|
submissions = await apiGet(`/api/tasks/${taskId}/submissions`);
|
|
7546
7999
|
} catch (err) {
|
|
7547
|
-
|
|
8000
|
+
renderFailure(err);
|
|
8001
|
+
return;
|
|
7548
8002
|
}
|
|
7549
8003
|
const workers = activeSubmissionWorkers(submissions);
|
|
7550
8004
|
if (workers.length === 0) {
|
|
@@ -7554,20 +8008,40 @@ var rejectAllSubmissionsCmd = new Command27("reject-all-submissions").descriptio
|
|
|
7554
8008
|
const results = [];
|
|
7555
8009
|
for (const worker of workers) {
|
|
7556
8010
|
try {
|
|
7557
|
-
const res = await x402Post(
|
|
7558
|
-
taskId
|
|
7559
|
-
worker
|
|
7560
|
-
|
|
7561
|
-
results.push({ worker, txHash: res.txHash });
|
|
8011
|
+
const { data: res, idempotencyKey } = await x402Post(
|
|
8012
|
+
`/api/tasks/${taskId}/reject-submission`,
|
|
8013
|
+
{ taskId, worker }
|
|
8014
|
+
);
|
|
8015
|
+
results.push({ worker, txHash: res.txHash, idempotencyKey });
|
|
7562
8016
|
} catch (err) {
|
|
7563
|
-
results.push({
|
|
8017
|
+
results.push({
|
|
8018
|
+
worker,
|
|
8019
|
+
error: err instanceof Error ? err.message : String(err),
|
|
8020
|
+
idempotencyKey: idempotencyKeyForError(err),
|
|
8021
|
+
cause: err
|
|
8022
|
+
});
|
|
7564
8023
|
}
|
|
7565
8024
|
}
|
|
7566
|
-
const failed = results.filter((r) => r.error);
|
|
8025
|
+
const failed = results.filter((r) => r.error !== void 0);
|
|
7567
8026
|
if (failed.length > 0) {
|
|
7568
|
-
|
|
7569
|
-
|
|
8027
|
+
const representative = failed.find((r) => isPendingApiError(r.cause)) ?? failed[0];
|
|
8028
|
+
renderFailure(
|
|
8029
|
+
withErrorContext(
|
|
8030
|
+
representative.cause,
|
|
8031
|
+
`${failed.length} of ${results.length} rejection(s) failed`
|
|
8032
|
+
),
|
|
8033
|
+
{
|
|
8034
|
+
details: {
|
|
8035
|
+
results: results.map(({ worker, txHash, idempotencyKey, error }) => ({
|
|
8036
|
+
worker,
|
|
8037
|
+
txHash,
|
|
8038
|
+
idempotencyKey,
|
|
8039
|
+
error
|
|
8040
|
+
}))
|
|
8041
|
+
}
|
|
8042
|
+
}
|
|
7570
8043
|
);
|
|
8044
|
+
return;
|
|
7571
8045
|
}
|
|
7572
8046
|
if (!opts.cancel) {
|
|
7573
8047
|
printResult({ rejected: results.length, results });
|
|
@@ -7575,11 +8049,15 @@ var rejectAllSubmissionsCmd = new Command27("reject-all-submissions").descriptio
|
|
|
7575
8049
|
}
|
|
7576
8050
|
let cancelRes;
|
|
7577
8051
|
try {
|
|
7578
|
-
cancelRes = await x402Post(
|
|
8052
|
+
({ data: cancelRes } = await x402Post(
|
|
8053
|
+
`/api/tasks/${taskId}/cancel`,
|
|
8054
|
+
{ taskId }
|
|
8055
|
+
));
|
|
7579
8056
|
} catch (err) {
|
|
7580
|
-
|
|
7581
|
-
`All ${results.length} submission(s) rejected but cancel failed
|
|
8057
|
+
renderFailure(
|
|
8058
|
+
withErrorContext(err, `All ${results.length} submission(s) rejected but cancel failed`)
|
|
7582
8059
|
);
|
|
8060
|
+
return;
|
|
7583
8061
|
}
|
|
7584
8062
|
printResult({ rejected: results.length, results, cancelTxHash: cancelRes.txHash });
|
|
7585
8063
|
});
|
|
@@ -7599,8 +8077,7 @@ var mySubmissionsCmd = new Command28("my-submissions").description("List all sub
|
|
|
7599
8077
|
);
|
|
7600
8078
|
printResult(result);
|
|
7601
8079
|
} catch (err) {
|
|
7602
|
-
|
|
7603
|
-
process.exit(1);
|
|
8080
|
+
renderFailure(err);
|
|
7604
8081
|
}
|
|
7605
8082
|
});
|
|
7606
8083
|
|
|
@@ -7613,9 +8090,8 @@ var updateCmd = new Command29("update").description("Update an open task (costs
|
|
|
7613
8090
|
try {
|
|
7614
8091
|
body.reward = usdcToBaseUnits(opts.reward);
|
|
7615
8092
|
} catch (err) {
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
);
|
|
8093
|
+
renderFailure(withErrorContext(err, "Invalid --reward"));
|
|
8094
|
+
return;
|
|
7619
8095
|
}
|
|
7620
8096
|
}
|
|
7621
8097
|
if (opts.extendExpiry !== void 0) {
|
|
@@ -7624,10 +8100,12 @@ var updateCmd = new Command29("update").description("Update an open task (costs
|
|
|
7624
8100
|
printError(
|
|
7625
8101
|
`--extend-expiry must be a positive integer (seconds), got: ${opts.extendExpiry}`
|
|
7626
8102
|
);
|
|
8103
|
+
return;
|
|
7627
8104
|
}
|
|
7628
8105
|
const task = await apiGet(`/api/tasks/${taskId}`);
|
|
7629
8106
|
if (!task) {
|
|
7630
8107
|
printError(`Task not found: ${taskId}`);
|
|
8108
|
+
return;
|
|
7631
8109
|
}
|
|
7632
8110
|
const currentExpiry = Math.floor(new Date(task.expiryTime).getTime() / 1e3);
|
|
7633
8111
|
const newExpiry = currentExpiry + delta;
|
|
@@ -7637,6 +8115,7 @@ var updateCmd = new Command29("update").description("Update an open task (costs
|
|
|
7637
8115
|
printError(
|
|
7638
8116
|
`--extend-expiry would set expiry in the past. Task expired ${expiredAgo}s ago; pass at least ${expiredAgo + 1} seconds to extend beyond now.`
|
|
7639
8117
|
);
|
|
8118
|
+
return;
|
|
7640
8119
|
}
|
|
7641
8120
|
body.expiryTime = newExpiry;
|
|
7642
8121
|
}
|
|
@@ -7644,6 +8123,7 @@ var updateCmd = new Command29("update").description("Update an open task (costs
|
|
|
7644
8123
|
const ts = Math.floor(new Date(opts.bidDeadline).getTime() / 1e3);
|
|
7645
8124
|
if (ts <= Math.floor(Date.now() / 1e3)) {
|
|
7646
8125
|
printError("--bid-deadline must be in the future");
|
|
8126
|
+
return;
|
|
7647
8127
|
} else {
|
|
7648
8128
|
body.bidDeadline = ts;
|
|
7649
8129
|
}
|
|
@@ -7652,6 +8132,7 @@ var updateCmd = new Command29("update").description("Update an open task (costs
|
|
|
7652
8132
|
const ts = Math.floor(new Date(opts.pitchDeadline).getTime() / 1e3);
|
|
7653
8133
|
if (ts <= Math.floor(Date.now() / 1e3)) {
|
|
7654
8134
|
printError("--pitch-deadline must be in the future");
|
|
8135
|
+
return;
|
|
7655
8136
|
} else {
|
|
7656
8137
|
body.pitchDeadline = ts;
|
|
7657
8138
|
}
|
|
@@ -7660,18 +8141,16 @@ var updateCmd = new Command29("update").description("Update an open task (costs
|
|
|
7660
8141
|
try {
|
|
7661
8142
|
body.auctionFloorPrice = usdcToBaseUnits(opts.auctionFloorPrice, { allowZero: true });
|
|
7662
8143
|
} catch (err) {
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
);
|
|
8144
|
+
renderFailure(withErrorContext(err, "Invalid --auction-floor-price"));
|
|
8145
|
+
return;
|
|
7666
8146
|
}
|
|
7667
8147
|
}
|
|
7668
8148
|
if (opts.auctionStartPrice !== void 0) {
|
|
7669
8149
|
try {
|
|
7670
8150
|
body.auctionStartPrice = usdcToBaseUnits(opts.auctionStartPrice, { allowZero: true });
|
|
7671
8151
|
} catch (err) {
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
);
|
|
8152
|
+
renderFailure(withErrorContext(err, "Invalid --auction-start-price"));
|
|
8153
|
+
return;
|
|
7675
8154
|
}
|
|
7676
8155
|
}
|
|
7677
8156
|
if (opts.description !== void 0) {
|
|
@@ -7683,8 +8162,11 @@ var updateCmd = new Command29("update").description("Update an open task (costs
|
|
|
7683
8162
|
if (opts.metricDescription !== void 0) {
|
|
7684
8163
|
body.metricDescription = opts.metricDescription;
|
|
7685
8164
|
}
|
|
7686
|
-
const result = await x402Post(
|
|
7687
|
-
|
|
8165
|
+
const { data: result, idempotencyKey } = await x402Post(
|
|
8166
|
+
`/api/tasks/${taskId}/update`,
|
|
8167
|
+
body
|
|
8168
|
+
);
|
|
8169
|
+
printResult(result, { idempotencyKey });
|
|
7688
8170
|
}
|
|
7689
8171
|
);
|
|
7690
8172
|
|
|
@@ -7695,20 +8177,77 @@ var forfeitCmd = new Command30("forfeit").description("Reclaim a claim-mode task
|
|
|
7695
8177
|
const keystore = await loadKeystore();
|
|
7696
8178
|
const message = buildForfeitMessage(taskId);
|
|
7697
8179
|
const signature = await signMessage(message, keystore);
|
|
7698
|
-
const result = await apiPost(
|
|
7699
|
-
taskId
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
|
|
8180
|
+
const { data: result, idempotencyKey } = await apiPost(
|
|
8181
|
+
`/api/tasks/${taskId}/forfeit`,
|
|
8182
|
+
{
|
|
8183
|
+
taskId,
|
|
8184
|
+
requesterAddress: keystore.walletAddress,
|
|
8185
|
+
signature
|
|
8186
|
+
}
|
|
8187
|
+
);
|
|
8188
|
+
printResult(result, { idempotencyKey });
|
|
7704
8189
|
} catch (err) {
|
|
7705
|
-
|
|
8190
|
+
renderFailure(err);
|
|
7706
8191
|
}
|
|
7707
8192
|
});
|
|
7708
8193
|
|
|
7709
|
-
// src/commands/task/
|
|
8194
|
+
// src/commands/task/assign-evaluator.ts
|
|
7710
8195
|
import { Command as Command31 } from "commander";
|
|
7711
|
-
var
|
|
8196
|
+
var ADDRESS_PATTERN = /^0x[0-9a-fA-F]{40}$/;
|
|
8197
|
+
var assignEvaluatorCmd = new Command31("assign-evaluator").description(
|
|
8198
|
+
"Assign an evaluator to an open, unclaimed task you requested (costs 0.001 USDC). Only possible before a worker claims the task."
|
|
8199
|
+
).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").action(
|
|
8200
|
+
async (taskId, opts) => {
|
|
8201
|
+
if (!ADDRESS_PATTERN.test(opts.evaluator)) {
|
|
8202
|
+
return void printError(`--evaluator must be a wallet address, got: ${opts.evaluator}`);
|
|
8203
|
+
}
|
|
8204
|
+
if (opts.disputeResolver !== void 0 && !ADDRESS_PATTERN.test(opts.disputeResolver)) {
|
|
8205
|
+
return void printError(
|
|
8206
|
+
`--dispute-resolver must be a wallet address, got: ${opts.disputeResolver}`
|
|
8207
|
+
);
|
|
8208
|
+
}
|
|
8209
|
+
const body = { taskId, evaluator: opts.evaluator };
|
|
8210
|
+
if (opts.evaluatorFeeBps !== void 0) {
|
|
8211
|
+
const bps = Number(opts.evaluatorFeeBps);
|
|
8212
|
+
if (!Number.isInteger(bps) || bps < 0 || bps > 1e4) {
|
|
8213
|
+
return void printError(
|
|
8214
|
+
`--evaluator-fee-bps must be an integer between 0 and 10000, got: ${opts.evaluatorFeeBps}`
|
|
8215
|
+
);
|
|
8216
|
+
}
|
|
8217
|
+
body.evaluatorFeeBps = bps;
|
|
8218
|
+
}
|
|
8219
|
+
if (opts.evaluationWindow !== void 0) {
|
|
8220
|
+
const hours = Number(opts.evaluationWindow);
|
|
8221
|
+
if (!Number.isFinite(hours) || hours <= 0) {
|
|
8222
|
+
return void printError(
|
|
8223
|
+
`--evaluation-window must be a positive number of hours, got: ${opts.evaluationWindow}`
|
|
8224
|
+
);
|
|
8225
|
+
}
|
|
8226
|
+
body.evaluationWindowHours = hours;
|
|
8227
|
+
}
|
|
8228
|
+
if (opts.appealWindow !== void 0) {
|
|
8229
|
+
const hours = Number(opts.appealWindow);
|
|
8230
|
+
if (!Number.isFinite(hours) || hours <= 0) {
|
|
8231
|
+
return void printError(
|
|
8232
|
+
`--appeal-window must be a positive number of hours, got: ${opts.appealWindow}`
|
|
8233
|
+
);
|
|
8234
|
+
}
|
|
8235
|
+
body.appealWindowHours = hours;
|
|
8236
|
+
}
|
|
8237
|
+
if (opts.disputeResolver !== void 0) {
|
|
8238
|
+
body.disputeResolver = opts.disputeResolver;
|
|
8239
|
+
}
|
|
8240
|
+
const { data: result, idempotencyKey } = await x402Post(
|
|
8241
|
+
`/api/tasks/${taskId}/evaluator`,
|
|
8242
|
+
body
|
|
8243
|
+
);
|
|
8244
|
+
printResult(result, { idempotencyKey });
|
|
8245
|
+
}
|
|
8246
|
+
);
|
|
8247
|
+
|
|
8248
|
+
// src/commands/task/evaluate.ts
|
|
8249
|
+
import { Command as Command32 } from "commander";
|
|
8250
|
+
var evaluateCmd = new Command32("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(
|
|
7712
8251
|
"--award <worker:amount:rank>",
|
|
7713
8252
|
"Award entry (repeatable): worker:amountUSDC:rank",
|
|
7714
8253
|
collect,
|
|
@@ -7737,9 +8276,8 @@ var evaluateCmd = new Command31("evaluate").description("Submit an evaluation ve
|
|
|
7737
8276
|
try {
|
|
7738
8277
|
microUnits = usdcToBaseUnits(amountUSDC);
|
|
7739
8278
|
} catch (err) {
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
);
|
|
8279
|
+
renderFailure(withErrorContext(err, `Invalid amount in '${entry}'`));
|
|
8280
|
+
return;
|
|
7743
8281
|
}
|
|
7744
8282
|
const parsedRank = Number(rankStr);
|
|
7745
8283
|
if (!Number.isInteger(parsedRank) || parsedRank < 1) {
|
|
@@ -7762,8 +8300,11 @@ var evaluateCmd = new Command31("evaluate").description("Submit an evaluation ve
|
|
|
7762
8300
|
}
|
|
7763
8301
|
body.evidenceHash = opts.evidenceHash;
|
|
7764
8302
|
}
|
|
7765
|
-
const result = await x402Post(
|
|
7766
|
-
|
|
8303
|
+
const { data: result, idempotencyKey } = await x402Post(
|
|
8304
|
+
`/api/tasks/${taskId}/evaluate`,
|
|
8305
|
+
body
|
|
8306
|
+
);
|
|
8307
|
+
printResult({ txHash: result.txHash }, { idempotencyKey });
|
|
7767
8308
|
}
|
|
7768
8309
|
);
|
|
7769
8310
|
function collect(val, prev) {
|
|
@@ -7771,28 +8312,28 @@ function collect(val, prev) {
|
|
|
7771
8312
|
}
|
|
7772
8313
|
|
|
7773
8314
|
// src/commands/task/appeal.ts
|
|
7774
|
-
import { Command as
|
|
7775
|
-
var appealCmd = new
|
|
7776
|
-
const result = await x402Post(`/api/tasks/${taskId}/appeal`, { taskId });
|
|
7777
|
-
printResult({ txHash: result.txHash });
|
|
8315
|
+
import { Command as Command33 } from "commander";
|
|
8316
|
+
var appealCmd = new Command33("appeal").description("Appeal an evaluator verdict while in Appealing state").argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
8317
|
+
const { data: result, idempotencyKey } = await x402Post(`/api/tasks/${taskId}/appeal`, { taskId });
|
|
8318
|
+
printResult({ txHash: result.txHash }, { idempotencyKey });
|
|
7778
8319
|
});
|
|
7779
8320
|
|
|
7780
8321
|
// src/commands/task/evaluator-timeout.ts
|
|
7781
|
-
import { Command as
|
|
7782
|
-
var evaluatorTimeoutCmd = new
|
|
7783
|
-
const result = await x402Post(`/api/tasks/${taskId}/evaluator-timeout`, { taskId });
|
|
7784
|
-
printResult({ txHash: result.txHash });
|
|
8322
|
+
import { Command as Command34 } from "commander";
|
|
8323
|
+
var evaluatorTimeoutCmd = new Command34("evaluator-timeout").description("Trigger evaluator timeout after the evaluation window expires (requester only)").argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
8324
|
+
const { data: result, idempotencyKey } = await x402Post(`/api/tasks/${taskId}/evaluator-timeout`, { taskId });
|
|
8325
|
+
printResult({ txHash: result.txHash }, { idempotencyKey });
|
|
7785
8326
|
});
|
|
7786
8327
|
|
|
7787
8328
|
// src/commands/task/finalize-verdict.ts
|
|
7788
|
-
import { Command as
|
|
7789
|
-
var finalizeVerdictCmd = new
|
|
7790
|
-
const result = await apiPost(`/api/tasks/${taskId}/finalize-verdict`, { taskId });
|
|
7791
|
-
printResult({ txHash: result.txHash });
|
|
8329
|
+
import { Command as Command35 } from "commander";
|
|
8330
|
+
var finalizeVerdictCmd = new Command35("finalize-verdict").description("Finalize an evaluator verdict after the appeal window expires (permissionless)").argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
8331
|
+
const { data: result, idempotencyKey } = await apiPost(`/api/tasks/${taskId}/finalize-verdict`, { taskId });
|
|
8332
|
+
printResult({ txHash: result.txHash }, { idempotencyKey });
|
|
7792
8333
|
});
|
|
7793
8334
|
|
|
7794
8335
|
// src/commands/task/resolve-dispute.ts
|
|
7795
|
-
import { Command as
|
|
8336
|
+
import { Command as Command36 } from "commander";
|
|
7796
8337
|
function parseAward(spec) {
|
|
7797
8338
|
const parts = spec.split(":");
|
|
7798
8339
|
if (parts.length !== 3) {
|
|
@@ -7813,7 +8354,7 @@ function parseAward(spec) {
|
|
|
7813
8354
|
rank
|
|
7814
8355
|
};
|
|
7815
8356
|
}
|
|
7816
|
-
var resolveDisputeCmd = new
|
|
8357
|
+
var resolveDisputeCmd = new Command36("resolve-dispute").description(
|
|
7817
8358
|
"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"
|
|
7818
8359
|
).argument("<taskId>", "Task ID (0x-prefixed hex)").requiredOption("--verdict <type>", "Verdict type: approve or partial").requiredOption(
|
|
7819
8360
|
"--award <spec...>",
|
|
@@ -7827,76 +8368,80 @@ var resolveDisputeCmd = new Command35("resolve-dispute").description(
|
|
|
7827
8368
|
try {
|
|
7828
8369
|
awards = opts.award.map(parseAward);
|
|
7829
8370
|
} catch (err) {
|
|
7830
|
-
|
|
8371
|
+
renderFailure(err);
|
|
7831
8372
|
return;
|
|
7832
8373
|
}
|
|
7833
|
-
const result = await x402Post(
|
|
7834
|
-
taskId
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
8374
|
+
const { data: result, idempotencyKey } = await x402Post(
|
|
8375
|
+
`/api/tasks/${taskId}/resolve-dispute`,
|
|
8376
|
+
{
|
|
8377
|
+
taskId,
|
|
8378
|
+
verdict: opts.verdict,
|
|
8379
|
+
awards
|
|
8380
|
+
}
|
|
8381
|
+
);
|
|
8382
|
+
printResult({ txHash: result.txHash }, { idempotencyKey });
|
|
7839
8383
|
});
|
|
7840
8384
|
|
|
7841
8385
|
// src/commands/task/unlock.ts
|
|
7842
|
-
import { Command as
|
|
7843
|
-
var unlockCmd = new
|
|
8386
|
+
import { Command as Command37 } from "commander";
|
|
8387
|
+
var unlockCmd = new Command37("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) => {
|
|
7844
8388
|
let result;
|
|
8389
|
+
let idempotencyKey;
|
|
7845
8390
|
try {
|
|
7846
|
-
result = await apiPost(
|
|
7847
|
-
taskId
|
|
7848
|
-
password: opts.password
|
|
7849
|
-
|
|
8391
|
+
({ data: result, idempotencyKey } = await apiPost(
|
|
8392
|
+
`/api/tasks/${taskId}/private-access/verify`,
|
|
8393
|
+
{ taskId, password: opts.password }
|
|
8394
|
+
));
|
|
7850
8395
|
} catch (err) {
|
|
7851
|
-
|
|
8396
|
+
renderFailure(err, { fallback: "Failed to unlock task." });
|
|
7852
8397
|
return;
|
|
7853
8398
|
}
|
|
7854
8399
|
await saveTaskAccessGrant(taskId, result.grant, result.expiresAt);
|
|
7855
|
-
printResult({ taskId, expiresAt: result.expiresAt });
|
|
8400
|
+
printResult({ taskId, expiresAt: result.expiresAt }, { idempotencyKey });
|
|
7856
8401
|
});
|
|
7857
8402
|
|
|
7858
8403
|
// src/commands/task/invite.ts
|
|
7859
|
-
import { Command as
|
|
7860
|
-
var inviteCmd = new
|
|
8404
|
+
import { Command as Command38 } from "commander";
|
|
8405
|
+
var inviteCmd = new Command38("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) => {
|
|
7861
8406
|
const auth = await signReadAuth();
|
|
7862
8407
|
if (!auth || Object.keys(auth.headers).length === 0) {
|
|
7863
8408
|
printError("No keystore found. Run `taskmarket init` first.");
|
|
7864
8409
|
return;
|
|
7865
8410
|
}
|
|
7866
8411
|
try {
|
|
7867
|
-
const result = await apiPost(
|
|
8412
|
+
const { data: result, idempotencyKey } = await apiPost(
|
|
7868
8413
|
`/api/tasks/${taskId}/private-access/viewers`,
|
|
7869
8414
|
{ taskId, viewerAddress: address },
|
|
7870
8415
|
{ headers: auth.headers }
|
|
7871
8416
|
);
|
|
7872
|
-
printResult(result);
|
|
8417
|
+
printResult(result, { idempotencyKey });
|
|
7873
8418
|
} catch (err) {
|
|
7874
|
-
|
|
8419
|
+
renderFailure(err, { fallback: "Failed to invite viewer." });
|
|
7875
8420
|
}
|
|
7876
8421
|
});
|
|
7877
8422
|
|
|
7878
8423
|
// src/commands/task/uninvite.ts
|
|
7879
|
-
import { Command as
|
|
7880
|
-
var uninviteCmd = new
|
|
8424
|
+
import { Command as Command39 } from "commander";
|
|
8425
|
+
var uninviteCmd = new Command39("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) => {
|
|
7881
8426
|
const auth = await signReadAuth();
|
|
7882
8427
|
if (!auth || Object.keys(auth.headers).length === 0) {
|
|
7883
8428
|
printError("No keystore found. Run `taskmarket init` first.");
|
|
7884
8429
|
return;
|
|
7885
8430
|
}
|
|
7886
8431
|
try {
|
|
7887
|
-
const result = await apiDelete(
|
|
8432
|
+
const { data: result, idempotencyKey } = await apiDelete(
|
|
7888
8433
|
`/api/tasks/${taskId}/private-access/viewers/${encodeURIComponent(address)}`,
|
|
7889
8434
|
{ headers: auth.headers }
|
|
7890
8435
|
);
|
|
7891
|
-
printResult(result);
|
|
8436
|
+
printResult(result, { idempotencyKey });
|
|
7892
8437
|
} catch (err) {
|
|
7893
|
-
|
|
8438
|
+
renderFailure(err, { fallback: "Failed to remove viewer." });
|
|
7894
8439
|
}
|
|
7895
8440
|
});
|
|
7896
8441
|
|
|
7897
8442
|
// src/commands/task/viewers.ts
|
|
7898
|
-
import { Command as
|
|
7899
|
-
var viewersCmd = new
|
|
8443
|
+
import { Command as Command40 } from "commander";
|
|
8444
|
+
var viewersCmd = new Command40("viewers").description("List a private task's wallet allowlist (requester only)").argument("<taskId>", "Task ID (0x-prefixed hex)").action(async (taskId) => {
|
|
7900
8445
|
const auth = await signReadAuth();
|
|
7901
8446
|
if (!auth || Object.keys(auth.headers).length === 0) {
|
|
7902
8447
|
printError("No keystore found. Run `taskmarket init` first.");
|
|
@@ -7908,12 +8453,12 @@ var viewersCmd = new Command39("viewers").description("List a private task's wal
|
|
|
7908
8453
|
});
|
|
7909
8454
|
printResult(result);
|
|
7910
8455
|
} catch (err) {
|
|
7911
|
-
|
|
8456
|
+
renderFailure(err, { fallback: "Failed to list viewers." });
|
|
7912
8457
|
}
|
|
7913
8458
|
});
|
|
7914
8459
|
|
|
7915
8460
|
// src/commands/task/index.ts
|
|
7916
|
-
var taskCommand = new
|
|
8461
|
+
var taskCommand = new Command41("task").description("Manage tasks");
|
|
7917
8462
|
taskCommand.addCommand(createCmd);
|
|
7918
8463
|
taskCommand.addCommand(listCmd);
|
|
7919
8464
|
taskCommand.addCommand(getCmd);
|
|
@@ -7939,6 +8484,7 @@ taskCommand.addCommand(rejectAllSubmissionsCmd);
|
|
|
7939
8484
|
taskCommand.addCommand(mySubmissionsCmd);
|
|
7940
8485
|
taskCommand.addCommand(updateCmd);
|
|
7941
8486
|
taskCommand.addCommand(forfeitCmd);
|
|
8487
|
+
taskCommand.addCommand(assignEvaluatorCmd);
|
|
7942
8488
|
taskCommand.addCommand(evaluateCmd);
|
|
7943
8489
|
taskCommand.addCommand(appealCmd);
|
|
7944
8490
|
taskCommand.addCommand(evaluatorTimeoutCmd);
|
|
@@ -7950,8 +8496,8 @@ taskCommand.addCommand(uninviteCmd);
|
|
|
7950
8496
|
taskCommand.addCommand(viewersCmd);
|
|
7951
8497
|
|
|
7952
8498
|
// src/commands/agents.ts
|
|
7953
|
-
import { Command as
|
|
7954
|
-
var agentsCommand = new
|
|
8499
|
+
import { Command as Command42 } from "commander";
|
|
8500
|
+
var agentsCommand = new Command42("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) => {
|
|
7955
8501
|
const params = new URLSearchParams();
|
|
7956
8502
|
if (opts.sort) params.set("sort", opts.sort);
|
|
7957
8503
|
if (opts.skill) params.set("skill", opts.skill);
|
|
@@ -7962,11 +8508,12 @@ var agentsCommand = new Command41("agents").description("Browse the agent direct
|
|
|
7962
8508
|
});
|
|
7963
8509
|
|
|
7964
8510
|
// src/commands/inbox.ts
|
|
7965
|
-
import { Command as
|
|
7966
|
-
var inboxCommand = new
|
|
8511
|
+
import { Command as Command43 } from "commander";
|
|
8512
|
+
var inboxCommand = new Command43("inbox").description("Show tasks you created and tasks you are working on").action(async () => {
|
|
7967
8513
|
const auth = await signReadAuth();
|
|
7968
8514
|
if (!auth) {
|
|
7969
8515
|
printError("No keystore found. Run `taskmarket init` first.");
|
|
8516
|
+
return;
|
|
7970
8517
|
}
|
|
7971
8518
|
const address = auth.walletAddress;
|
|
7972
8519
|
const hasReadAuth = Object.keys(auth.headers).length > 0;
|
|
@@ -7983,22 +8530,22 @@ var inboxCommand = new Command42("inbox").description("Show tasks you created an
|
|
|
7983
8530
|
});
|
|
7984
8531
|
|
|
7985
8532
|
// src/commands/deposit.ts
|
|
7986
|
-
import { Command as
|
|
7987
|
-
var depositCommand = new
|
|
8533
|
+
import { Command as Command44 } from "commander";
|
|
8534
|
+
var depositCommand = new Command44("deposit").description("Show wallet address and network info for funding").action(async () => {
|
|
7988
8535
|
let address;
|
|
7989
8536
|
try {
|
|
7990
8537
|
address = await getWalletAddress();
|
|
7991
8538
|
} catch (err) {
|
|
7992
|
-
|
|
7993
|
-
|
|
8539
|
+
renderFailure(err, { fallback: "No keystore found. Run `taskmarket init` first." });
|
|
8540
|
+
return;
|
|
7994
8541
|
}
|
|
7995
8542
|
let networkInfo;
|
|
7996
8543
|
try {
|
|
7997
8544
|
const response = await apiGet("/trpc/network.info");
|
|
7998
8545
|
networkInfo = response.result.data;
|
|
7999
8546
|
} catch (err) {
|
|
8000
|
-
|
|
8001
|
-
|
|
8547
|
+
renderFailure(err, { fallback: "Failed to fetch network info from backend." });
|
|
8548
|
+
return;
|
|
8002
8549
|
}
|
|
8003
8550
|
const data = {
|
|
8004
8551
|
address,
|
|
@@ -8011,11 +8558,11 @@ var depositCommand = new Command43("deposit").description("Show wallet address a
|
|
|
8011
8558
|
});
|
|
8012
8559
|
|
|
8013
8560
|
// src/commands/wallet/index.ts
|
|
8014
|
-
import { Command as
|
|
8561
|
+
import { Command as Command50 } from "commander";
|
|
8015
8562
|
|
|
8016
8563
|
// src/commands/wallet/balance.ts
|
|
8017
|
-
import { Command as
|
|
8018
|
-
var walletBalanceCommand = new
|
|
8564
|
+
import { Command as Command45 } from "commander";
|
|
8565
|
+
var walletBalanceCommand = new Command45("balance").description("Show USDC balance").option("--address <addr>", "Address to check (defaults to own wallet)").action(async (opts) => {
|
|
8019
8566
|
let address = opts.address;
|
|
8020
8567
|
if (!address) {
|
|
8021
8568
|
const keystore = await loadKeystore();
|
|
@@ -8026,7 +8573,7 @@ var walletBalanceCommand = new Command44("balance").description("Show USDC balan
|
|
|
8026
8573
|
});
|
|
8027
8574
|
|
|
8028
8575
|
// src/commands/wallet/import.ts
|
|
8029
|
-
import { Command as
|
|
8576
|
+
import { Command as Command46 } from "commander";
|
|
8030
8577
|
import { createInterface as createInterface2 } from "readline";
|
|
8031
8578
|
import { privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
|
|
8032
8579
|
function normalizePrivateKey(raw) {
|
|
@@ -8061,7 +8608,7 @@ function promptHiddenInput(prompt) {
|
|
|
8061
8608
|
});
|
|
8062
8609
|
});
|
|
8063
8610
|
}
|
|
8064
|
-
var walletImportCommand = new
|
|
8611
|
+
var walletImportCommand = new Command46("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(
|
|
8065
8612
|
"--yes",
|
|
8066
8613
|
"Confirm legal acceptance non-interactively after reviewing every current policy"
|
|
8067
8614
|
).action(async (opts) => {
|
|
@@ -8140,69 +8687,77 @@ var walletImportCommand = new Command45("import").description("Import an existin
|
|
|
8140
8687
|
});
|
|
8141
8688
|
|
|
8142
8689
|
// src/commands/wallet/set-withdrawal-address.ts
|
|
8143
|
-
import { Command as
|
|
8690
|
+
import { Command as Command47 } from "commander";
|
|
8144
8691
|
function isValidAddress(addr) {
|
|
8145
8692
|
return /^0x[a-fA-F0-9]{40}$/.test(addr);
|
|
8146
8693
|
}
|
|
8147
|
-
var setWithdrawalAddressCommand = new
|
|
8694
|
+
var setWithdrawalAddressCommand = new Command47("set-withdrawal-address").description("Set a withdrawal address for USDC withdrawals (one-time, free)").argument("<address>", "Ethereum address to receive withdrawals").action(async (address) => {
|
|
8148
8695
|
if (!isValidAddress(address)) {
|
|
8149
8696
|
printError("Invalid Ethereum address: must be 0x followed by 40 hex characters");
|
|
8697
|
+
return;
|
|
8150
8698
|
}
|
|
8151
8699
|
let keystore;
|
|
8152
8700
|
try {
|
|
8153
8701
|
keystore = await loadKeystore();
|
|
8154
8702
|
} catch {
|
|
8155
8703
|
printError("No keystore found. Run `taskmarket init` first.");
|
|
8704
|
+
return;
|
|
8156
8705
|
}
|
|
8157
8706
|
const walletAddress = keystore.walletAddress;
|
|
8158
8707
|
const message = buildSetWithdrawalAddressMessage(address);
|
|
8159
8708
|
const signature = await signMessage(message, keystore);
|
|
8160
|
-
const result = await apiPost(
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
|
|
8709
|
+
const { data: result, idempotencyKey } = await apiPost(
|
|
8710
|
+
"/api/wallet/set-withdrawal-address",
|
|
8711
|
+
{
|
|
8712
|
+
walletAddress,
|
|
8713
|
+
withdrawalAddress: address,
|
|
8714
|
+
signature
|
|
8715
|
+
}
|
|
8716
|
+
);
|
|
8717
|
+
printResult({ withdrawalAddress: result.withdrawalAddress }, { idempotencyKey });
|
|
8166
8718
|
});
|
|
8167
8719
|
|
|
8168
8720
|
// src/commands/wallet/publish-key.ts
|
|
8169
|
-
import { Command as
|
|
8170
|
-
var publishKeyCommand = new
|
|
8721
|
+
import { Command as Command48 } from "commander";
|
|
8722
|
+
var publishKeyCommand = new Command48("publish-key").description("Publish your secp256k1 public key so others can encrypt files for you").action(async () => {
|
|
8171
8723
|
let keystore;
|
|
8172
8724
|
try {
|
|
8173
8725
|
keystore = await loadKeystore();
|
|
8174
8726
|
} catch {
|
|
8175
8727
|
printError("No keystore found. Run `taskmarket init` first.");
|
|
8728
|
+
return;
|
|
8176
8729
|
}
|
|
8177
8730
|
const dek = await fetchDeviceKey(keystore.deviceId, keystore.apiToken);
|
|
8178
8731
|
const privateKey = decryptPrivateKey(dek, keystore.encryptedKey);
|
|
8179
8732
|
const publicKey = deriveCompressedPublicKey(privateKey);
|
|
8180
|
-
const result = await apiPost("/trpc/agents.setPublicKey", {
|
|
8733
|
+
const { data: result, idempotencyKey } = await apiPost("/trpc/agents.setPublicKey", {
|
|
8181
8734
|
deviceId: keystore.deviceId,
|
|
8182
8735
|
apiToken: keystore.apiToken,
|
|
8183
8736
|
publicKey
|
|
8184
8737
|
});
|
|
8185
|
-
printResult({ publicKey: result.result.data.publicKey });
|
|
8738
|
+
printResult({ publicKey: result.result.data.publicKey }, { idempotencyKey });
|
|
8186
8739
|
});
|
|
8187
8740
|
|
|
8188
8741
|
// src/commands/wallet/withdraw-dreams.ts
|
|
8189
|
-
import { Command as
|
|
8742
|
+
import { Command as Command49 } from "commander";
|
|
8190
8743
|
import { toHex as toHex2 } from "viem";
|
|
8191
8744
|
var AUTHORIZATION_VALIDITY_SECONDS = 300;
|
|
8192
8745
|
function isValidAddress2(addr) {
|
|
8193
8746
|
return /^0x[a-fA-F0-9]{40}$/.test(addr);
|
|
8194
8747
|
}
|
|
8195
|
-
var withdrawDreamsCommand = new
|
|
8748
|
+
var withdrawDreamsCommand = new Command49("withdraw-dreams").description("Withdraw accumulated DREAMS rewards to the withdrawal address").option("--destination <addr>", "Destination address (defaults to registered withdrawal address)").action(async (opts) => {
|
|
8196
8749
|
let keystore;
|
|
8197
8750
|
try {
|
|
8198
8751
|
keystore = await loadKeystore();
|
|
8199
8752
|
} catch {
|
|
8200
8753
|
printError("No keystore found. Run `taskmarket init` first.");
|
|
8754
|
+
return;
|
|
8201
8755
|
}
|
|
8202
8756
|
const workerAddress = keystore.walletAddress;
|
|
8203
8757
|
let destination = opts.destination;
|
|
8204
8758
|
if (destination !== void 0 && !isValidAddress2(destination)) {
|
|
8205
8759
|
printError("Invalid Ethereum address: must be 0x followed by 40 hex characters");
|
|
8760
|
+
return;
|
|
8206
8761
|
}
|
|
8207
8762
|
if (!destination) {
|
|
8208
8763
|
const addressResponse = await apiGet(
|
|
@@ -8212,6 +8767,7 @@ var withdrawDreamsCommand = new Command48("withdraw-dreams").description("Withdr
|
|
|
8212
8767
|
printError(
|
|
8213
8768
|
"No withdrawal address set. Run: taskmarket wallet set-withdrawal-address <address>"
|
|
8214
8769
|
);
|
|
8770
|
+
return;
|
|
8215
8771
|
}
|
|
8216
8772
|
destination = addressResponse.withdrawalAddress;
|
|
8217
8773
|
}
|
|
@@ -8219,25 +8775,31 @@ var withdrawDreamsCommand = new Command48("withdraw-dreams").description("Withdr
|
|
|
8219
8775
|
const validBefore = String(Math.floor(Date.now() / 1e3) + AUTHORIZATION_VALIDITY_SECONDS);
|
|
8220
8776
|
const message = buildWithdrawDreamsMessage(destination, nonce, validBefore);
|
|
8221
8777
|
const signature = await signMessage(message, keystore);
|
|
8222
|
-
const result = await apiPost(
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8230
|
-
|
|
8231
|
-
|
|
8232
|
-
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8236
|
-
|
|
8778
|
+
const { data: result, idempotencyKey } = await apiPost(
|
|
8779
|
+
"/api/wallet/withdraw-dreams",
|
|
8780
|
+
{
|
|
8781
|
+
workerAddress,
|
|
8782
|
+
destination,
|
|
8783
|
+
nonce,
|
|
8784
|
+
validBefore,
|
|
8785
|
+
signature
|
|
8786
|
+
}
|
|
8787
|
+
);
|
|
8788
|
+
printResult(
|
|
8789
|
+
{
|
|
8790
|
+
txHash: result.txHash,
|
|
8791
|
+
destination: result.destination,
|
|
8792
|
+
claimedBaseUnits: result.claimedBaseUnits,
|
|
8793
|
+
claimedDreams: formatDreams(result.claimedBaseUnits),
|
|
8794
|
+
dreamsPerUsdc: result.dreamsPerUsdc,
|
|
8795
|
+
usdEquivalent: result.usdEquivalent
|
|
8796
|
+
},
|
|
8797
|
+
{ idempotencyKey }
|
|
8798
|
+
);
|
|
8237
8799
|
});
|
|
8238
8800
|
|
|
8239
8801
|
// src/commands/wallet/index.ts
|
|
8240
|
-
var walletCommand = new
|
|
8802
|
+
var walletCommand = new Command50("wallet").description("Wallet management commands");
|
|
8241
8803
|
walletCommand.addCommand(walletBalanceCommand);
|
|
8242
8804
|
walletCommand.addCommand(walletImportCommand);
|
|
8243
8805
|
walletCommand.addCommand(setWithdrawalAddressCommand);
|
|
@@ -8245,7 +8807,7 @@ walletCommand.addCommand(publishKeyCommand);
|
|
|
8245
8807
|
walletCommand.addCommand(withdrawDreamsCommand);
|
|
8246
8808
|
|
|
8247
8809
|
// src/commands/withdraw.ts
|
|
8248
|
-
import { Command as
|
|
8810
|
+
import { Command as Command51 } from "commander";
|
|
8249
8811
|
import { toHex as toHex3 } from "viem";
|
|
8250
8812
|
var USDC_TYPES = {
|
|
8251
8813
|
TransferWithAuthorization: [
|
|
@@ -8257,10 +8819,11 @@ var USDC_TYPES = {
|
|
|
8257
8819
|
{ name: "nonce", type: "bytes32" }
|
|
8258
8820
|
]
|
|
8259
8821
|
};
|
|
8260
|
-
var withdrawCommand = new
|
|
8822
|
+
var withdrawCommand = new Command51("withdraw").description("Withdraw USDC to the registered withdrawal address").argument("<amount>", "Amount in USDC (e.g. 5 for 5 USDC)").action(async (amount) => {
|
|
8261
8823
|
const parsed = parseFloat(amount);
|
|
8262
8824
|
if (isNaN(parsed) || parsed <= 0) {
|
|
8263
8825
|
printError("Invalid amount: must be a positive number (e.g. 5 for 5 USDC)");
|
|
8826
|
+
return;
|
|
8264
8827
|
}
|
|
8265
8828
|
const amountBaseUnits = String(Math.round(parsed * 1e6));
|
|
8266
8829
|
let keystore;
|
|
@@ -8268,6 +8831,7 @@ var withdrawCommand = new Command50("withdraw").description("Withdraw USDC to th
|
|
|
8268
8831
|
keystore = await loadKeystore();
|
|
8269
8832
|
} catch {
|
|
8270
8833
|
printError("No keystore found. Run `taskmarket init` first.");
|
|
8834
|
+
return;
|
|
8271
8835
|
}
|
|
8272
8836
|
const from = keystore.walletAddress;
|
|
8273
8837
|
const addressResponse = await apiGet(
|
|
@@ -8277,6 +8841,7 @@ var withdrawCommand = new Command50("withdraw").description("Withdraw USDC to th
|
|
|
8277
8841
|
printError(
|
|
8278
8842
|
"No withdrawal address set. Run: taskmarket wallet set-withdrawal-address <address>"
|
|
8279
8843
|
);
|
|
8844
|
+
return;
|
|
8280
8845
|
}
|
|
8281
8846
|
const { withdrawalAddress, usdcDomain } = addressResponse;
|
|
8282
8847
|
const now = Math.floor(Date.now() / 1e3);
|
|
@@ -8297,30 +8862,38 @@ var withdrawCommand = new Command50("withdraw").description("Withdraw USDC to th
|
|
|
8297
8862
|
},
|
|
8298
8863
|
keystore
|
|
8299
8864
|
);
|
|
8300
|
-
const result = await apiPost(
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8865
|
+
const { data: result, idempotencyKey } = await apiPost(
|
|
8866
|
+
"/api/wallet/withdraw",
|
|
8867
|
+
{
|
|
8868
|
+
from,
|
|
8869
|
+
amountBaseUnits,
|
|
8870
|
+
authorization,
|
|
8871
|
+
signature
|
|
8872
|
+
}
|
|
8873
|
+
);
|
|
8874
|
+
printResult(
|
|
8875
|
+
{ txHash: result.txHash, amountBaseUnits: result.amountBaseUnits, to: result.to },
|
|
8876
|
+
{ idempotencyKey }
|
|
8877
|
+
);
|
|
8307
8878
|
});
|
|
8308
8879
|
|
|
8309
8880
|
// src/commands/encrypt.ts
|
|
8310
|
-
import { Command as
|
|
8881
|
+
import { Command as Command52 } from "commander";
|
|
8311
8882
|
import { promises as fs3 } from "fs";
|
|
8312
|
-
var encryptCommand = new
|
|
8883
|
+
var encryptCommand = new Command52("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) => {
|
|
8313
8884
|
let plaintext;
|
|
8314
8885
|
try {
|
|
8315
8886
|
plaintext = await fs3.readFile(file);
|
|
8316
8887
|
} catch {
|
|
8317
8888
|
printError(`Cannot read file: ${file}`);
|
|
8889
|
+
return;
|
|
8318
8890
|
}
|
|
8319
8891
|
let keystore;
|
|
8320
8892
|
try {
|
|
8321
8893
|
keystore = await loadKeystore();
|
|
8322
8894
|
} catch {
|
|
8323
8895
|
printError("No keystore found. Run `taskmarket init` first.");
|
|
8896
|
+
return;
|
|
8324
8897
|
}
|
|
8325
8898
|
let recipientPubKey;
|
|
8326
8899
|
let recipientAddress;
|
|
@@ -8333,12 +8906,9 @@ var encryptCommand = new Command51("encrypt").description("Encrypt a file with E
|
|
|
8333
8906
|
);
|
|
8334
8907
|
} catch (err) {
|
|
8335
8908
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
);
|
|
8340
|
-
}
|
|
8341
|
-
printError(`Failed to fetch recipient public key: ${msg}`);
|
|
8909
|
+
const context = msg.includes("publish their public key") || msg.includes("NOT_FOUND") ? "Recipient has not published their public key. Ask them to run: taskmarket wallet publish-key" : "Failed to fetch recipient public key";
|
|
8910
|
+
renderFailure(withErrorContext(err, context));
|
|
8911
|
+
return;
|
|
8342
8912
|
}
|
|
8343
8913
|
recipientPubKey = result.result.data.publicKey;
|
|
8344
8914
|
} else {
|
|
@@ -8358,20 +8928,22 @@ var encryptCommand = new Command51("encrypt").description("Encrypt a file with E
|
|
|
8358
8928
|
});
|
|
8359
8929
|
|
|
8360
8930
|
// src/commands/decrypt.ts
|
|
8361
|
-
import { Command as
|
|
8931
|
+
import { Command as Command53 } from "commander";
|
|
8362
8932
|
import { promises as fs4 } from "fs";
|
|
8363
|
-
var decryptCommand = new
|
|
8933
|
+
var decryptCommand = new Command53("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) => {
|
|
8364
8934
|
let ciphertext;
|
|
8365
8935
|
try {
|
|
8366
8936
|
ciphertext = await fs4.readFile(file);
|
|
8367
8937
|
} catch {
|
|
8368
8938
|
printError(`Cannot read file: ${file}`);
|
|
8939
|
+
return;
|
|
8369
8940
|
}
|
|
8370
8941
|
let keystore;
|
|
8371
8942
|
try {
|
|
8372
8943
|
keystore = await loadKeystore();
|
|
8373
8944
|
} catch {
|
|
8374
8945
|
printError("No keystore found. Run `taskmarket init` first.");
|
|
8946
|
+
return;
|
|
8375
8947
|
}
|
|
8376
8948
|
const dek = await fetchDeviceKey(keystore.deviceId, keystore.apiToken);
|
|
8377
8949
|
const privateKey = decryptPrivateKey(dek, keystore.encryptedKey);
|
|
@@ -8379,8 +8951,8 @@ var decryptCommand = new Command52("decrypt").description("Decrypt a file using
|
|
|
8379
8951
|
try {
|
|
8380
8952
|
plaintext = decryptWithPrivateKey(ciphertext, privateKey);
|
|
8381
8953
|
} catch (err) {
|
|
8382
|
-
|
|
8383
|
-
|
|
8954
|
+
renderFailure(err);
|
|
8955
|
+
return;
|
|
8384
8956
|
}
|
|
8385
8957
|
let outputPath;
|
|
8386
8958
|
if (opts.output) {
|
|
@@ -8398,21 +8970,21 @@ var decryptCommand = new Command52("decrypt").description("Decrypt a file using
|
|
|
8398
8970
|
});
|
|
8399
8971
|
|
|
8400
8972
|
// src/commands/xmtp.ts
|
|
8401
|
-
import { Command as
|
|
8973
|
+
import { Command as Command54 } from "commander";
|
|
8402
8974
|
|
|
8403
8975
|
// src/lib/xmtp-client.ts
|
|
8404
8976
|
import os3 from "os";
|
|
8405
8977
|
import path3 from "path";
|
|
8406
|
-
import { randomUUID as
|
|
8978
|
+
import { randomUUID as randomUUID3, hkdfSync as hkdfSync2 } from "crypto";
|
|
8407
8979
|
import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
|
|
8408
8980
|
|
|
8409
8981
|
// src/lib/xmtp-envelope.ts
|
|
8410
|
-
import { randomUUID } from "crypto";
|
|
8982
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
8411
8983
|
function buildEnvelope(input) {
|
|
8412
8984
|
return AgentMessageEnvelopeSchema.parse({
|
|
8413
8985
|
schemaVersion: 1,
|
|
8414
8986
|
type: input.type,
|
|
8415
|
-
requestId: input.requestId ??
|
|
8987
|
+
requestId: input.requestId ?? randomUUID2(),
|
|
8416
8988
|
replyToRequestId: input.replyToRequestId,
|
|
8417
8989
|
senderInboxId: input.senderInboxId,
|
|
8418
8990
|
senderAddress: input.senderAddress,
|
|
@@ -8565,7 +9137,7 @@ function defaultDbPath(walletAddress) {
|
|
|
8565
9137
|
}
|
|
8566
9138
|
async function createDevClient(input) {
|
|
8567
9139
|
const inboxId = input.existingInboxId ?? `dev-inbox-${input.walletAddress.slice(2, 10).toLowerCase()}`;
|
|
8568
|
-
const installationId = input.existingInstallationId ??
|
|
9140
|
+
const installationId = input.existingInstallationId ?? randomUUID3();
|
|
8569
9141
|
const dbPath = input.existingDbPath ?? defaultDbPath(input.walletAddress);
|
|
8570
9142
|
return {
|
|
8571
9143
|
inboxId,
|
|
@@ -8994,7 +9566,7 @@ async function createProductionClient(input) {
|
|
|
8994
9566
|
"non-retryable"
|
|
8995
9567
|
);
|
|
8996
9568
|
}
|
|
8997
|
-
const installationId = extractString(client, ["installationId", "installationID"]) ?? extractString(asObject(client)?.installation, ["id", "installationId"]) ?? input.existingInstallationId ??
|
|
9569
|
+
const installationId = extractString(client, ["installationId", "installationID"]) ?? extractString(asObject(client)?.installation, ["id", "installationId"]) ?? input.existingInstallationId ?? randomUUID3();
|
|
8998
9570
|
return {
|
|
8999
9571
|
inboxId,
|
|
9000
9572
|
installationId,
|
|
@@ -9162,7 +9734,7 @@ function getDefaultQueryTimeoutMs() {
|
|
|
9162
9734
|
}
|
|
9163
9735
|
return Math.floor(parsed);
|
|
9164
9736
|
}
|
|
9165
|
-
var xmtpCommand = new
|
|
9737
|
+
var xmtpCommand = new Command54("xmtp").description("Manage XMTP messaging for this agent");
|
|
9166
9738
|
xmtpCommand.command("init").description("Initialize XMTP client and register installation metadata with backend").action(async () => {
|
|
9167
9739
|
const keystore = await loadKeystore();
|
|
9168
9740
|
const client = await createXmtpClient({
|
|
@@ -9172,7 +9744,7 @@ xmtpCommand.command("init").description("Initialize XMTP client and register ins
|
|
|
9172
9744
|
existingDbPath: keystore.xmtpDbPath,
|
|
9173
9745
|
keystore
|
|
9174
9746
|
});
|
|
9175
|
-
const status = await apiPost("/api/xmtp/bootstrap", {
|
|
9747
|
+
const { data: status, idempotencyKey } = await apiPost("/api/xmtp/bootstrap", {
|
|
9176
9748
|
deviceId: keystore.deviceId,
|
|
9177
9749
|
apiToken: keystore.apiToken,
|
|
9178
9750
|
inboxId: client.inboxId,
|
|
@@ -9186,7 +9758,7 @@ xmtpCommand.command("init").description("Initialize XMTP client and register ins
|
|
|
9186
9758
|
xmtpInstallationId: status.installationId,
|
|
9187
9759
|
xmtpDbPath: client.dbPath
|
|
9188
9760
|
});
|
|
9189
|
-
printResult({ policyMode: status.policyMode });
|
|
9761
|
+
printResult({ policyMode: status.policyMode }, { idempotencyKey });
|
|
9190
9762
|
});
|
|
9191
9763
|
xmtpCommand.command("status").description("Check XMTP status and active installation state").action(async () => {
|
|
9192
9764
|
const keystore = await loadKeystore();
|
|
@@ -9299,12 +9871,15 @@ xmtpCommand.command("heartbeat").description("Send a one-shot heartbeat to keep
|
|
|
9299
9871
|
if (!keystore.xmtpInstallationId) {
|
|
9300
9872
|
throw new Error("XMTP not initialized. Run `taskmarket xmtp init` first.");
|
|
9301
9873
|
}
|
|
9302
|
-
const result = await apiPost(
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9874
|
+
const { data: result, idempotencyKey } = await apiPost(
|
|
9875
|
+
"/api/xmtp/heartbeat",
|
|
9876
|
+
{
|
|
9877
|
+
deviceId: keystore.deviceId,
|
|
9878
|
+
apiToken: keystore.apiToken,
|
|
9879
|
+
installationId: keystore.xmtpInstallationId
|
|
9880
|
+
}
|
|
9881
|
+
);
|
|
9882
|
+
printResult(result, { idempotencyKey });
|
|
9308
9883
|
});
|
|
9309
9884
|
var peersCommand = xmtpCommand.command("peers").description("Manage taskmarket peer messaging policies");
|
|
9310
9885
|
peersCommand.command("list").description("List per-peer messaging policies stored on the backend").action(async () => {
|
|
@@ -9322,14 +9897,17 @@ peersCommand.command("set").description("Set messaging policy for a specific pee
|
|
|
9322
9897
|
}
|
|
9323
9898
|
const keystore = await loadKeystore();
|
|
9324
9899
|
const peerInboxId = await resolveInboxId(options.to);
|
|
9325
|
-
const result = await apiPost(
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
|
|
9900
|
+
const { data: result, idempotencyKey } = await apiPost(
|
|
9901
|
+
"/api/xmtp/peers",
|
|
9902
|
+
{
|
|
9903
|
+
deviceId: keystore.deviceId,
|
|
9904
|
+
apiToken: keystore.apiToken,
|
|
9905
|
+
peerInboxId,
|
|
9906
|
+
policy: options.policy,
|
|
9907
|
+
reason: options.reason
|
|
9908
|
+
}
|
|
9909
|
+
);
|
|
9910
|
+
printResult(result, { idempotencyKey });
|
|
9333
9911
|
});
|
|
9334
9912
|
var allowlistCommand = xmtpCommand.command("allowlist").description("Manage XMTP SDK consent allowlist (protocol-level)");
|
|
9335
9913
|
allowlistCommand.command("add").description("Allow messages from a peer inbox (XMTP SDK consent)").requiredOption("--to <agentIdOrAddrOrInboxId>", "Target agent ID, wallet address, or inboxId").action(async (options) => {
|
|
@@ -9382,15 +9960,18 @@ allowlistCommand.command("check").description("Check the XMTP SDK consent state
|
|
|
9382
9960
|
});
|
|
9383
9961
|
xmtpCommand.command("purge").description("Revoke stale XMTP installations that have missed heartbeats").action(async () => {
|
|
9384
9962
|
const keystore = await loadKeystore();
|
|
9385
|
-
const result = await apiPost(
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
|
|
9963
|
+
const { data: result, idempotencyKey } = await apiPost(
|
|
9964
|
+
"/api/xmtp/purge",
|
|
9965
|
+
{
|
|
9966
|
+
deviceId: keystore.deviceId,
|
|
9967
|
+
apiToken: keystore.apiToken
|
|
9968
|
+
}
|
|
9969
|
+
);
|
|
9970
|
+
printResult(result, { idempotencyKey });
|
|
9390
9971
|
});
|
|
9391
9972
|
|
|
9392
9973
|
// src/commands/daemon.ts
|
|
9393
|
-
import { Command as
|
|
9974
|
+
import { Command as Command55 } from "commander";
|
|
9394
9975
|
function diffTaskStatuses(prev, next) {
|
|
9395
9976
|
const changes = [];
|
|
9396
9977
|
for (const [taskId, newStatus] of next) {
|
|
@@ -9418,7 +9999,7 @@ function sleepOrAbort(ms, signal) {
|
|
|
9418
9999
|
});
|
|
9419
10000
|
}
|
|
9420
10001
|
var MAX_SEEN_TASK_IDS = 2e3;
|
|
9421
|
-
var daemonCommand = new
|
|
10002
|
+
var daemonCommand = new Command55("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(
|
|
9422
10003
|
"--auction-poll-interval <ms>",
|
|
9423
10004
|
"Poll interval for clock-based auction tasks (dutch/reverse_dutch) in milliseconds",
|
|
9424
10005
|
"15000"
|
|
@@ -9502,12 +10083,15 @@ var daemonCommand = new Command54("daemon").description("Long-running agent daem
|
|
|
9502
10083
|
await sleepOrAbort(heartbeatIntervalMs, abortController.signal);
|
|
9503
10084
|
if (stopped) break;
|
|
9504
10085
|
try {
|
|
9505
|
-
await apiPost("/trpc/xmtp.heartbeat", {
|
|
10086
|
+
const { idempotencyKey } = await apiPost("/trpc/xmtp.heartbeat", {
|
|
9506
10087
|
deviceId: keystore.deviceId,
|
|
9507
10088
|
apiToken: keystore.apiToken,
|
|
9508
10089
|
installationId: client.installationId
|
|
9509
10090
|
});
|
|
9510
|
-
printResult(
|
|
10091
|
+
printResult(
|
|
10092
|
+
{ event: "xmtp.heartbeat", installationId: client.installationId },
|
|
10093
|
+
{ idempotencyKey }
|
|
10094
|
+
);
|
|
9511
10095
|
} catch (err) {
|
|
9512
10096
|
process.stderr.write(
|
|
9513
10097
|
`Heartbeat failed: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -9739,29 +10323,33 @@ var daemonCommand = new Command54("daemon").description("Long-running agent daem
|
|
|
9739
10323
|
);
|
|
9740
10324
|
|
|
9741
10325
|
// src/commands/email/index.ts
|
|
9742
|
-
import { Command as
|
|
10326
|
+
import { Command as Command64 } from "commander";
|
|
9743
10327
|
|
|
9744
10328
|
// src/commands/email/register.ts
|
|
9745
|
-
import { Command as
|
|
9746
|
-
var registerCommand = new
|
|
10329
|
+
import { Command as Command56 } from "commander";
|
|
10330
|
+
var registerCommand = new Command56("register").description("Register an email address for this agent").requiredOption("--username <name>", "Desired username (e.g. myagent)").action(async (opts) => {
|
|
9747
10331
|
const check = await apiGet(
|
|
9748
10332
|
`/api/emails/check-username?username=${encodeURIComponent(opts.username)}`
|
|
9749
10333
|
);
|
|
9750
10334
|
if (!check.available) {
|
|
9751
10335
|
printError(`Username "${opts.username}" is not available.`);
|
|
10336
|
+
return;
|
|
9752
10337
|
}
|
|
9753
10338
|
const keystore = await loadKeystore();
|
|
9754
|
-
const result = await apiPost(
|
|
9755
|
-
|
|
9756
|
-
|
|
9757
|
-
|
|
9758
|
-
|
|
9759
|
-
|
|
10339
|
+
const { data: result, idempotencyKey } = await apiPost(
|
|
10340
|
+
"/api/emails/register",
|
|
10341
|
+
{
|
|
10342
|
+
deviceId: keystore.deviceId,
|
|
10343
|
+
apiToken: keystore.apiToken,
|
|
10344
|
+
username: opts.username
|
|
10345
|
+
}
|
|
10346
|
+
);
|
|
10347
|
+
printResult({ emailAddress: result.emailAddress }, { idempotencyKey });
|
|
9760
10348
|
});
|
|
9761
10349
|
|
|
9762
10350
|
// src/commands/email/inbox.ts
|
|
9763
|
-
import { Command as
|
|
9764
|
-
var inboxCommand2 = new
|
|
10351
|
+
import { Command as Command57 } from "commander";
|
|
10352
|
+
var inboxCommand2 = new Command57("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) => {
|
|
9765
10353
|
const keystore = await loadKeystore();
|
|
9766
10354
|
const params = new URLSearchParams({
|
|
9767
10355
|
deviceId: keystore.deviceId,
|
|
@@ -9776,8 +10364,8 @@ var inboxCommand2 = new Command56("inbox").description("List emails in your inbo
|
|
|
9776
10364
|
});
|
|
9777
10365
|
|
|
9778
10366
|
// src/commands/email/read.ts
|
|
9779
|
-
import { Command as
|
|
9780
|
-
var readCommand = new
|
|
10367
|
+
import { Command as Command58 } from "commander";
|
|
10368
|
+
var readCommand = new Command58("read").description("Read an email by ID (marks it as read)").argument("<id>", "Email ID").action(async (id) => {
|
|
9781
10369
|
const keystore = await loadKeystore();
|
|
9782
10370
|
const params = new URLSearchParams({
|
|
9783
10371
|
deviceId: keystore.deviceId,
|
|
@@ -9789,22 +10377,22 @@ var readCommand = new Command57("read").description("Read an email by ID (marks
|
|
|
9789
10377
|
});
|
|
9790
10378
|
|
|
9791
10379
|
// src/commands/email/send.ts
|
|
9792
|
-
import { Command as
|
|
9793
|
-
var sendCommand = new
|
|
10380
|
+
import { Command as Command59 } from "commander";
|
|
10381
|
+
var sendCommand = new Command59("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) => {
|
|
9794
10382
|
const keystore = await loadKeystore();
|
|
9795
|
-
const result = await apiPost("/api/emails/send", {
|
|
10383
|
+
const { data: result, idempotencyKey } = await apiPost("/api/emails/send", {
|
|
9796
10384
|
deviceId: keystore.deviceId,
|
|
9797
10385
|
apiToken: keystore.apiToken,
|
|
9798
10386
|
to: opts.to,
|
|
9799
10387
|
subject: opts.subject,
|
|
9800
10388
|
bodyText: opts.body
|
|
9801
10389
|
});
|
|
9802
|
-
printResult({ sent: result.sent });
|
|
10390
|
+
printResult({ sent: result.sent }, { idempotencyKey });
|
|
9803
10391
|
});
|
|
9804
10392
|
|
|
9805
10393
|
// src/commands/email/reply.ts
|
|
9806
|
-
import { Command as
|
|
9807
|
-
var replyCommand = new
|
|
10394
|
+
import { Command as Command60 } from "commander";
|
|
10395
|
+
var replyCommand = new Command60("reply").description("Reply to an email").argument("<id>", "Email ID to reply to").requiredOption("--body <text>", "Reply body text").action(async (id, opts) => {
|
|
9808
10396
|
const keystore = await loadKeystore();
|
|
9809
10397
|
const params = new URLSearchParams({
|
|
9810
10398
|
deviceId: keystore.deviceId,
|
|
@@ -9815,54 +10403,60 @@ var replyCommand = new Command59("reply").description("Reply to an email").argum
|
|
|
9815
10403
|
const to = original.fromAddress;
|
|
9816
10404
|
const rawSubject = original.subject ?? "";
|
|
9817
10405
|
const subject = rawSubject.startsWith("Re: ") ? rawSubject : `Re: ${rawSubject}`;
|
|
9818
|
-
const result = await apiPost("/api/emails/send", {
|
|
10406
|
+
const { data: result, idempotencyKey } = await apiPost("/api/emails/send", {
|
|
9819
10407
|
deviceId: keystore.deviceId,
|
|
9820
10408
|
apiToken: keystore.apiToken,
|
|
9821
10409
|
to,
|
|
9822
10410
|
subject,
|
|
9823
10411
|
bodyText: opts.body
|
|
9824
10412
|
});
|
|
9825
|
-
printResult({ sent: result.sent });
|
|
10413
|
+
printResult({ sent: result.sent }, { idempotencyKey });
|
|
9826
10414
|
});
|
|
9827
10415
|
|
|
9828
10416
|
// src/commands/email/delete.ts
|
|
9829
|
-
import { Command as
|
|
9830
|
-
var deleteCommand = new
|
|
10417
|
+
import { Command as Command61 } from "commander";
|
|
10418
|
+
var deleteCommand = new Command61("delete").description("Delete an email by ID").argument("<id>", "Email ID").action(async (id) => {
|
|
9831
10419
|
const keystore = await loadKeystore();
|
|
9832
|
-
const result = await apiPost(
|
|
9833
|
-
|
|
9834
|
-
|
|
9835
|
-
|
|
9836
|
-
|
|
9837
|
-
|
|
10420
|
+
const { data: result, idempotencyKey } = await apiPost(
|
|
10421
|
+
"/api/emails/delete",
|
|
10422
|
+
{
|
|
10423
|
+
deviceId: keystore.deviceId,
|
|
10424
|
+
apiToken: keystore.apiToken,
|
|
10425
|
+
id
|
|
10426
|
+
}
|
|
10427
|
+
);
|
|
10428
|
+
printResult({ deleted: result.deleted }, { idempotencyKey });
|
|
9838
10429
|
});
|
|
9839
10430
|
|
|
9840
10431
|
// src/commands/email/address.ts
|
|
9841
|
-
import { Command as
|
|
9842
|
-
var addressCommand2 = new
|
|
10432
|
+
import { Command as Command62 } from "commander";
|
|
10433
|
+
var addressCommand2 = new Command62("address").description("Show your registered email address").action(async () => {
|
|
9843
10434
|
const keystore = await loadKeystore();
|
|
9844
10435
|
const result = await apiGet(`/api/agents/stats?address=${keystore.walletAddress}`);
|
|
9845
10436
|
printResult({ emailAddress: result.emailAddress ?? null });
|
|
9846
10437
|
});
|
|
9847
10438
|
|
|
9848
10439
|
// src/commands/email/mark-read.ts
|
|
9849
|
-
import { Command as
|
|
10440
|
+
import { Command as Command63 } from "commander";
|
|
9850
10441
|
async function setReadStatus(id, read) {
|
|
9851
10442
|
const keystore = await loadKeystore();
|
|
9852
|
-
const result = await apiPost(
|
|
9853
|
-
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
|
|
9857
|
-
|
|
9858
|
-
|
|
10443
|
+
const { data: result, idempotencyKey } = await apiPost(
|
|
10444
|
+
"/api/emails/mark-read",
|
|
10445
|
+
{
|
|
10446
|
+
deviceId: keystore.deviceId,
|
|
10447
|
+
apiToken: keystore.apiToken,
|
|
10448
|
+
id,
|
|
10449
|
+
read
|
|
10450
|
+
}
|
|
10451
|
+
);
|
|
10452
|
+
printResult({ id: result.id, isRead: result.isRead }, { idempotencyKey });
|
|
9859
10453
|
}
|
|
9860
|
-
var markReadCommand = new
|
|
10454
|
+
var markReadCommand = new Command63("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) => {
|
|
9861
10455
|
await setReadStatus(id, !opts.unread);
|
|
9862
10456
|
});
|
|
9863
10457
|
|
|
9864
10458
|
// src/commands/email/index.ts
|
|
9865
|
-
var emailCommand = new
|
|
10459
|
+
var emailCommand = new Command64("email").description("Email management commands");
|
|
9866
10460
|
emailCommand.addCommand(registerCommand);
|
|
9867
10461
|
emailCommand.addCommand(inboxCommand2);
|
|
9868
10462
|
emailCommand.addCommand(readCommand);
|
|
@@ -9873,32 +10467,30 @@ emailCommand.addCommand(addressCommand2);
|
|
|
9873
10467
|
emailCommand.addCommand(markReadCommand);
|
|
9874
10468
|
|
|
9875
10469
|
// src/commands/requester/index.ts
|
|
9876
|
-
import { Command as
|
|
10470
|
+
import { Command as Command66 } from "commander";
|
|
9877
10471
|
|
|
9878
10472
|
// src/commands/requester/stats.ts
|
|
9879
|
-
import { Command as
|
|
9880
|
-
var requesterStatsCmd = new
|
|
10473
|
+
import { Command as Command65 } from "commander";
|
|
10474
|
+
var requesterStatsCmd = new Command65("stats").description("Get requester reputation stats for an address").argument("<address>", "Wallet address of the requester").action(async (address) => {
|
|
9881
10475
|
if (!/^0x[0-9a-fA-F]{40}$/.test(address)) {
|
|
9882
10476
|
printError("Address must be a 0x-prefixed 20-byte hex string");
|
|
9883
|
-
process.exit(1);
|
|
9884
10477
|
return;
|
|
9885
10478
|
}
|
|
9886
10479
|
const data = await apiGet(`/api/requester/${address}/stats`);
|
|
9887
10480
|
if (!data) {
|
|
9888
10481
|
printError("No stats found for address");
|
|
9889
|
-
process.exit(1);
|
|
9890
10482
|
return;
|
|
9891
10483
|
}
|
|
9892
10484
|
printResult(data);
|
|
9893
10485
|
});
|
|
9894
10486
|
|
|
9895
10487
|
// src/commands/requester/index.ts
|
|
9896
|
-
var requesterCmd = new
|
|
10488
|
+
var requesterCmd = new Command66("requester").description("Requester reputation commands").addCommand(requesterStatsCmd);
|
|
9897
10489
|
|
|
9898
10490
|
// src/index.ts
|
|
9899
10491
|
var require2 = createRequire(import.meta.url);
|
|
9900
10492
|
var { version } = require2("../package.json");
|
|
9901
|
-
var program = new
|
|
10493
|
+
var program = new Command67();
|
|
9902
10494
|
program.name("taskmarket").description("Taskmarket CLI for AI agents").version(version);
|
|
9903
10495
|
program.addCommand(initCommand);
|
|
9904
10496
|
program.addCommand(walletCommand);
|
|
@@ -9917,10 +10509,10 @@ program.addCommand(daemonCommand);
|
|
|
9917
10509
|
program.addCommand(emailCommand);
|
|
9918
10510
|
program.addCommand(requesterCmd);
|
|
9919
10511
|
program.addCommand(legalCommand);
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
});
|
|
10512
|
+
void (async () => {
|
|
10513
|
+
try {
|
|
10514
|
+
await program.parseAsync(process.argv);
|
|
10515
|
+
} catch (err) {
|
|
10516
|
+
renderFailure(err);
|
|
10517
|
+
}
|
|
10518
|
+
})();
|