@ixo/asks-mcp 1.6.0-dev.6 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/asks-mcp.mjs +278 -158
  2. package/package.json +1 -1
package/dist/asks-mcp.mjs CHANGED
@@ -8468,24 +8468,24 @@ function blockOf(raw) {
8468
8468
  return null;
8469
8469
  }
8470
8470
  function callOf(raw) {
8471
- const call = raw;
8472
- if (!call || typeof call !== "object") return null;
8471
+ const call2 = raw;
8472
+ if (!call2 || typeof call2 !== "object") return null;
8473
8473
  const L4 = CALL_LIMITS;
8474
- if (typeof call.slug !== "string" || !CALL_SLUG.test(call.slug)) return null;
8475
- if (!CALL_LEGS.includes(call.leg)) return null;
8476
- if (!line(call.title, L4.title) || !line(call.lede, L4.lede) || !line(call.poster, L4.poster)) return null;
8477
- if (!Array.isArray(call.points) || call.points.length === 0 || call.points.length > L4.lines || !call.points.every((one) => finite(one) && one >= 0 && one <= 100)) return null;
8478
- const money2 = call.money;
8474
+ if (typeof call2.slug !== "string" || !CALL_SLUG.test(call2.slug)) return null;
8475
+ if (!CALL_LEGS.includes(call2.leg)) return null;
8476
+ if (!line(call2.title, L4.title) || !line(call2.lede, L4.lede) || !line(call2.poster, L4.poster)) return null;
8477
+ if (!Array.isArray(call2.points) || call2.points.length === 0 || call2.points.length > L4.lines || !call2.points.every((one) => finite(one) && one >= 0 && one <= 100)) return null;
8478
+ const money2 = call2.money;
8479
8479
  if (!money2 || typeof money2 !== "object") return null;
8480
8480
  if (!amount(money2.prize) || !whole(money2.runnersN, 0, 5) || !amount(money2.runnersEach) || !amount(money2.entryEach) || !whole(money2.entryUpTo, 1, 1e4)) return null;
8481
- const share = call.share;
8481
+ const share = call2.share;
8482
8482
  if (share !== void 0 && (!share || typeof share !== "object" || !nonNegative(share.network) || !nonNegative(share.node) || share.network + share.node >= 100)) return null;
8483
- const sponsor = call.sponsor;
8483
+ const sponsor = call2.sponsor;
8484
8484
  if (sponsor !== void 0 && (!sponsor || typeof sponsor !== "object" || typeof sponsor.slug !== "string" || !CALL_SLUG.test(sponsor.slug) || !line(sponsor.name, L4.sponsorName) || typeof sponsor.node !== "string" || !ENTITY_DID.test(sponsor.node))) return null;
8485
- if (!date5(call.closes) || !date5(call.drop) || !date5(call.pick)) return null;
8485
+ if (!date5(call2.closes) || !date5(call2.drop) || !date5(call2.pick)) return null;
8486
8486
  let legs;
8487
- if (call.legs !== void 0) {
8488
- const raw2 = call.legs;
8487
+ if (call2.legs !== void 0) {
8488
+ const raw2 = call2.legs;
8489
8489
  if (!raw2 || typeof raw2 !== "object") return null;
8490
8490
  legs = {};
8491
8491
  for (const name8 of CALL_LEGS) {
@@ -8495,42 +8495,42 @@ function callOf(raw) {
8495
8495
  legs[name8] = id;
8496
8496
  }
8497
8497
  }
8498
- if (!Array.isArray(call.blocks) || call.blocks.length > L4.blocks) return null;
8499
- const blocks = call.blocks.map(blockOf);
8498
+ if (!Array.isArray(call2.blocks) || call2.blocks.length > L4.blocks) return null;
8499
+ const blocks = call2.blocks.map(blockOf);
8500
8500
  if (blocks.some((block2) => block2 === null)) return null;
8501
8501
  let review = null;
8502
- if (call.kind !== void 0) {
8503
- if (call.kind !== "review" || call.mode !== "hire") return null;
8504
- const subject = call.subject;
8502
+ if (call2.kind !== void 0) {
8503
+ if (call2.kind !== "review" || call2.mode !== "hire") return null;
8504
+ const subject = call2.subject;
8505
8505
  if (subject !== void 0) {
8506
8506
  if (!subject || typeof subject !== "object" || !line(subject.name, L4.subjectName)) return null;
8507
8507
  if (subject.version !== void 0 && !line(subject.version, L4.version)) return null;
8508
8508
  if (subject.where !== void 0 && !line(subject.where, L4.where)) return null;
8509
8509
  }
8510
- if (call.scenarios !== void 0 && (!Array.isArray(call.scenarios) || call.scenarios.length === 0 || call.scenarios.length > L4.scenarios || !call.scenarios.every((one) => line(one, L4.scenario)))) return null;
8510
+ if (call2.scenarios !== void 0 && (!Array.isArray(call2.scenarios) || call2.scenarios.length === 0 || call2.scenarios.length > L4.scenarios || !call2.scenarios.every((one) => line(one, L4.scenario)))) return null;
8511
8511
  review = {
8512
8512
  kind: "review",
8513
8513
  ...subject ? { subject: { name: subject.name, ...subject.version !== void 0 ? { version: subject.version } : {}, ...subject.where !== void 0 ? { where: subject.where } : {} } } : {},
8514
- ...call.scenarios !== void 0 ? { scenarios: [...call.scenarios] } : {}
8514
+ ...call2.scenarios !== void 0 ? { scenarios: [...call2.scenarios] } : {}
8515
8515
  };
8516
8516
  }
8517
8517
  return {
8518
8518
  call: {
8519
- slug: call.slug,
8520
- leg: call.leg,
8519
+ slug: call2.slug,
8520
+ leg: call2.leg,
8521
8521
  /* The one literal: anything else is a competition, as every call before it was. */
8522
- ...call.mode === "hire" ? { mode: "hire" } : {},
8522
+ ...call2.mode === "hire" ? { mode: "hire" } : {},
8523
8523
  ...review ?? {},
8524
- title: call.title,
8525
- lede: call.lede,
8526
- poster: call.poster,
8527
- points: [...call.points],
8524
+ title: call2.title,
8525
+ lede: call2.lede,
8526
+ poster: call2.poster,
8527
+ points: [...call2.points],
8528
8528
  money: { prize: money2.prize, runnersN: money2.runnersN, runnersEach: money2.runnersEach, entryEach: money2.entryEach, entryUpTo: money2.entryUpTo },
8529
8529
  ...share ? { share: { network: share.network, node: share.node } } : {},
8530
8530
  ...sponsor ? { sponsor: { slug: sponsor.slug, name: sponsor.name, node: sponsor.node } } : {},
8531
- closes: call.closes,
8532
- drop: call.drop,
8533
- pick: call.pick,
8531
+ closes: call2.closes,
8532
+ drop: call2.drop,
8533
+ pick: call2.pick,
8534
8534
  ...legs ? { legs } : {},
8535
8535
  blocks
8536
8536
  }
@@ -9470,7 +9470,7 @@ var init_docPath = __esm({
9470
9470
  });
9471
9471
 
9472
9472
  // ../../packages/openhands-sdk/src/data/docUrls.ts
9473
- var VFS_BY_NETWORK, vfsUrlFor, DEFAULT_IPFS_GATEWAY_URL, vfsUrl, laneVfsUrl, ipfsGatewayUrl, vfsDocUrl, ipfsDocUrl, docUrlBeside, docUrls, docHosts;
9473
+ var VFS_BY_NETWORK, vfsUrlFor, SUBSCRIPTIONS_BY_NETWORK, subscriptionsUrlFor, DEFAULT_IPFS_GATEWAY_URL, vfsUrl, laneVfsUrl, ipfsGatewayUrl, vfsDocUrl, ipfsDocUrl, docUrlBeside, docUrls, docHosts;
9474
9474
  var init_docUrls = __esm({
9475
9475
  "../../packages/openhands-sdk/src/data/docUrls.ts"() {
9476
9476
  "use strict";
@@ -9484,6 +9484,12 @@ var init_docUrls = __esm({
9484
9484
  mainnet: "https://vfs.ixo.earth"
9485
9485
  };
9486
9486
  vfsUrlFor = (net) => VFS_BY_NETWORK[net];
9487
+ SUBSCRIPTIONS_BY_NETWORK = {
9488
+ devnet: "https://subscriptions-api.ixo-api.workers.dev",
9489
+ testnet: "https://subscriptions-api-testnet.ixo-api.workers.dev",
9490
+ mainnet: "https://subscriptions-api-mainnet.ixo-api.workers.dev"
9491
+ };
9492
+ subscriptionsUrlFor = (net) => SUBSCRIPTIONS_BY_NETWORK[net];
9487
9493
  DEFAULT_IPFS_GATEWAY_URL = "https://ipfs.gateway.ixo.world";
9488
9494
  vfsUrl = () => (givenConfig().vfsUrl ?? (hasEnv() ? firstSet(process.env.NEXT_PUBLIC_VFS_URL) : void 0) ?? VFS_BY_NETWORK[network()]).replace(/\/+$/, "");
9489
9495
  laneVfsUrl = () => ((hasEnv() ? firstSet(process.env.NEXT_PUBLIC_LANE_VFS_URL) : void 0) ?? vfsUrl()).replace(/\/+$/, "");
@@ -28378,18 +28384,18 @@ function waysIn(org, oracle, sourceId) {
28378
28384
  `In Claude Code: eval-oracle api review-billing enable --source ${sourceId ?? "<source id>"} --kit ${oracle.url.replace(/\/$/, "")} --cap <dollars> (it previews first; add --yes to sign).`
28379
28385
  ];
28380
28386
  }
28381
- async function sourceCall(oracleUrl2, audience, mint, call, options = {}) {
28387
+ async function sourceCall(oracleUrl2, audience, mint, call2, options = {}) {
28382
28388
  try {
28383
- const response = await (options.fetch ?? fetch)(`${base3(oracleUrl2)}${call.path}`, {
28384
- method: call.method ?? "GET",
28389
+ const response = await (options.fetch ?? fetch)(`${base3(oracleUrl2)}${call2.path}`, {
28390
+ method: call2.method ?? "GET",
28385
28391
  headers: {
28386
28392
  accept: "application/json",
28387
- authorization: `Bearer ${await mint(audience, call.can, call.sourceId)}`,
28393
+ authorization: `Bearer ${await mint(audience, call2.can, call2.sourceId)}`,
28388
28394
  "x-auth-type": "ucan",
28389
- ...call.sourceId ? { "x-source-id": call.sourceId } : {},
28390
- ...call.body !== void 0 ? { "content-type": "application/json" } : {}
28395
+ ...call2.sourceId ? { "x-source-id": call2.sourceId } : {},
28396
+ ...call2.body !== void 0 ? { "content-type": "application/json" } : {}
28391
28397
  },
28392
- ...call.body !== void 0 ? { body: JSON.stringify(call.body) } : {},
28398
+ ...call2.body !== void 0 ? { body: JSON.stringify(call2.body) } : {},
28393
28399
  signal: AbortSignal.timeout(TIMEOUT2)
28394
28400
  });
28395
28401
  const body = await response.json().catch(() => null);
@@ -28901,6 +28907,7 @@ __export(docs_exports, {
28901
28907
  readDocJson: () => readDocJson,
28902
28908
  rememberDocStore: () => rememberDocStore,
28903
28909
  resolveDocUrl: () => resolveDocUrl,
28910
+ subscriptionsUrlFor: () => subscriptionsUrlFor,
28904
28911
  vfsDocUrl: () => vfsDocUrl,
28905
28912
  vfsUrl: () => vfsUrl,
28906
28913
  vfsUrlFor: () => vfsUrlFor
@@ -250689,10 +250696,10 @@ var require_get_proto = __commonJS({
250689
250696
  var require_hasown = __commonJS({
250690
250697
  "../../node_modules/.pnpm/hasown@2.0.4/node_modules/hasown/index.js"(exports, module) {
250691
250698
  "use strict";
250692
- var call = Function.prototype.call;
250699
+ var call2 = Function.prototype.call;
250693
250700
  var $hasOwn = Object.prototype.hasOwnProperty;
250694
250701
  var bind2 = require_function_bind();
250695
- module.exports = bind2.call(call, $hasOwn);
250702
+ module.exports = bind2.call(call2, $hasOwn);
250696
250703
  }
250697
250704
  });
250698
250705
 
@@ -260281,7 +260288,7 @@ var init_privateLane = __esm({
260281
260288
  "use strict";
260282
260289
  init_cid4();
260283
260290
  init_lane();
260284
- workerUrl = (network2, env = process.env) => (env.OPENHANDS_WORKER_URL?.trim() || (network2 === "mainnet" ? "https://api.openhands.network" : "https://test.api.openhands.network")).replace(/\/+$/, "");
260291
+ workerUrl = (network2, env = process.env) => (env.OPENHANDS_WORKER_URL?.trim() || (network2 === "mainnet" ? "https://api.asks.world" : "https://test.api.asks.world")).replace(/\/+$/, "");
260285
260292
  passLink = (site, id, agentDid, collectionId, can, exp) => `${site.replace(/\/+$/, "")}/pass?${new URLSearchParams({ id, agent: agentDid, ask: collectionId, can: can === "fs/write" ? "write" : "read", exp: String(Math.floor(exp)) })}`;
260286
260293
  shareLink = (site, collectionId, approver, exp = Math.floor((Date.now() + BOX_TTL_MS) / 1e3)) => `${site.replace(/\/+$/, "")}/pass?${new URLSearchParams({ ask: collectionId, can: "share", to: approver, exp: String(Math.floor(exp)) })}`;
260287
260294
  shareLine = (site, collectionId, isPrivate, approver, poster) => isPrivate && approver && poster && approver !== poster ? `The answers are private. So the person giving final approval can read them, open this link and tap Allow:
@@ -261302,13 +261309,16 @@ function prepareAsk(poster, entityDid, adminAccount, terms, now3 = Date.now(), s
261302
261309
  if (shape?.org && !isEntityDid(shape.org)) throw new Error(`the org must be an entity DID, got ${JSON.stringify(shape.org)}`);
261303
261310
  const offChain = Boolean(shape?.offChain);
261304
261311
  if (offChain && !shape?.org) throw new Error("an ask paid off chain is posted for an organisation: pass org");
261312
+ const card = shape?.fundedBy === "card";
261313
+ if (card && (shape?.org || offChain)) throw new Error("a card pays a person's ask on chain: not an organisation's, not off chain");
261314
+ if (card && (terms.denom ?? askDenom()) !== "upay") throw new Error("a card-funded reward is PAY: IXO's card service sends PAY");
261305
261315
  const checkReward = terms.checker && terms.checkRewardUixo ? BigInt(terms.checkRewardUixo) : 0n;
261306
261316
  const spots = BigInt(Math.max(1, terms.quota));
261307
261317
  const denom = terms.denom ?? askDenom();
261308
- if (denom === "upay" && !offChain && network() === "mainnet" && askDenom() !== "upay") throw new Error("PAY is not a reward coin on mainnet: post the reward in USDC");
261318
+ if (denom === "upay" && !offChain && !card && network() === "mainnet" && askDenom() !== "upay") throw new Error("PAY is not a reward coin on mainnet: post the reward in USDC");
261309
261319
  const feeDenom = terms.feeDenom ?? checkerFeeDenom(network());
261310
- const rewardEscrow = offChain ? 0n : BigInt(terms.amountUixo) * spots;
261311
- const feeEscrow = checkReward * spots;
261320
+ const rewardEscrow = offChain || card ? 0n : BigInt(terms.amountUixo) * spots;
261321
+ const feeEscrow = card && feeDenom === "upay" ? 0n : checkReward * spots;
261312
261322
  const coins = coinsOf([[denom, rewardEscrow], [feeDenom, feeEscrow]]);
261313
261323
  const orgFunds = Boolean(shape?.org) && coins.length > 0;
261314
261324
  const orgAdmin = shape?.orgAdmin ?? null;
@@ -261368,7 +261378,7 @@ function prepareAsk(poster, entityDid, adminAccount, terms, now3 = Date.now(), s
261368
261378
  poster,
261369
261379
  escrow: { denom: recorded, amount: coins.find((coin) => coin.denom === recorded)?.amount ?? "0" },
261370
261380
  ...feeEscrow > 0n && feeDenom !== recorded ? { fee: { denom: feeDenom, amount: String(feeEscrow), from: orgFunds ? "org" : "poster" } } : {},
261371
- ...orgFunds ? { fundedBy: "org" } : {},
261381
+ ...orgFunds ? { fundedBy: "org" } : card ? { fundedBy: "card" } : {},
261372
261382
  preparedAt: new Date(now3).toISOString(),
261373
261383
  txHash: null,
261374
261384
  ...shape ? { claimProtocolDid: shape.claimProtocolDid } : {}
@@ -263381,8 +263391,8 @@ function lineSentence(line2) {
263381
263391
  if (sentence4.length > 300) throw new Error(`a judged line is ${sentence4.length} characters with its failure; the jury reads 300 at most`);
263382
263392
  return sentence4;
263383
263393
  }
263384
- function legCall(call, leg) {
263385
- const read9 = callOf({ ...call, leg });
263394
+ function legCall(call2, leg) {
263395
+ const read9 = callOf({ ...call2, leg });
263386
263396
  if (!read9) throw new Error("the call does not read back: a field is missing, too long, or not an https address");
263387
263397
  return read9.call;
263388
263398
  }
@@ -263394,10 +263404,10 @@ function taskFor(title, leg, kind) {
263394
263404
  function callEntriesTerms(input) {
263395
263405
  if (input.lines.length !== CALL_LIMITS.lines) throw new Error(`a call is judged on exactly ${CALL_LIMITS.lines} lines`);
263396
263406
  if (input.judge.kind !== "oracle" || !input.judge.address) throw new Error("the entries are judged by the oracle, and it has to be named");
263397
- const call = legCall(input.call, "entries");
263398
- if (call.points.length !== CALL_LIMITS.lines) throw new Error("one score per judged line");
263399
- if (call.kind === "review" && !input.report) throw new Error("a review call's entries are reports: say report (the extra sections, or none)");
263400
- if (call.kind !== "review" && input.report) throw new Error("only a review call is answered as a report");
263407
+ const call2 = legCall(input.call, "entries");
263408
+ if (call2.points.length !== CALL_LIMITS.lines) throw new Error("one score per judged line");
263409
+ if (call2.kind === "review" && !input.report) throw new Error("a review call's entries are reports: say report (the extra sections, or none)");
263410
+ if (call2.kind !== "review" && input.report) throw new Error("only a review call is answered as a report");
263401
263411
  if (input.approver !== void 0 && !/^ixo1[a-z0-9]{38,64}$/.test(input.approver)) throw new Error("approver is an ixo1 address");
263402
263412
  const criteria = [
263403
263413
  { id: "present", kind: "measure", gate: { id: "non-empty" } },
@@ -263406,11 +263416,11 @@ function callEntriesTerms(input) {
263406
263416
  ];
263407
263417
  const terms = {
263408
263418
  v: 2,
263409
- task: taskFor(call.title, "entries", call.kind),
263419
+ task: taskFor(call2.title, "entries", call2.kind),
263410
263420
  deliverable: { type: "text", prompt: input.report ? REVIEW_DELIVERABLE_PROMPT : "One page, in your own words, covering all five requirements" },
263411
- campaign: callTag(call.slug),
263421
+ campaign: callTag(call2.slug),
263412
263422
  ...input.access ? { access: input.access } : {},
263413
- call,
263423
+ call: call2,
263414
263424
  criteria,
263415
263425
  judge: input.judge,
263416
263426
  ...input.approver ? { approver: input.approver } : {}
@@ -263418,17 +263428,17 @@ function callEntriesTerms(input) {
263418
263428
  return input.report ? withReport(terms, input.report, input.approver ? { v: 2 } : void 0) : terms;
263419
263429
  }
263420
263430
  function callPickTerms(input) {
263421
- const call = legCall(input.call, input.leg);
263422
- if (call.mode === "hire") throw new Error("a hiring call has no pick: the entries are the whole call");
263431
+ const call2 = legCall(input.call, input.leg);
263432
+ if (call2.mode === "hire") throw new Error("a hiring call has no pick: the entries are the whole call");
263423
263433
  const which = input.leg === "prize" ? "the pick" : "a runner-up";
263424
263434
  return {
263425
263435
  v: 2,
263426
- task: taskFor(call.title, input.leg),
263436
+ task: taskFor(call2.title, input.leg),
263427
263437
  deliverable: { type: "text", prompt: "Quote the number of your entry, nothing else" },
263428
- campaign: callTag(call.slug),
263438
+ campaign: callTag(call2.slug),
263429
263439
  ...input.access ? { access: input.access } : {},
263430
- call,
263431
- criteria: [{ id: "picked", kind: "attest", text: `The poster chose this entry as ${which} of call ${call.slug}` }],
263440
+ call: call2,
263441
+ criteria: [{ id: "picked", kind: "attest", text: `The poster chose this entry as ${which} of call ${call2.slug}` }],
263432
263442
  judge: { kind: "poster" }
263433
263443
  };
263434
263444
  }
@@ -263440,8 +263450,8 @@ function callDates(openDays, now3 = Date.now(), mode) {
263440
263450
  }
263441
263451
  return { closes: new Date(closes).toISOString(), drop: new Date(closes + DAY_MS2).toISOString(), pick: new Date(closes + 4 * DAY_MS2).toISOString() };
263442
263452
  }
263443
- function legDays(call, leg, now3 = Date.now()) {
263444
- const until = leg === "entries" ? Date.parse(call.closes) : Date.parse(call.pick) + 7 * DAY_MS2;
263453
+ function legDays(call2, leg, now3 = Date.now()) {
263454
+ const until = leg === "entries" ? Date.parse(call2.closes) : Date.parse(call2.pick) + 7 * DAY_MS2;
263445
263455
  return Math.max(1, Math.ceil((until - now3) / DAY_MS2));
263446
263456
  }
263447
263457
  var ENTRY_MIN_CHARS, REVIEW_LINES, REVIEW_DELIVERABLE_PROMPT, DAY_MS2, legsOf, pickQuota;
@@ -263461,7 +263471,7 @@ var init_call = __esm({
263461
263471
  ];
263462
263472
  REVIEW_DELIVERABLE_PROMPT = "A review report: what you tested, your steps per scenario, each finding with its evidence, how to reproduce it and its severity, what to change, and the limitations";
263463
263473
  DAY_MS2 = 24 * 3600 * 1e3;
263464
- legsOf = (call) => call.mode === "hire" ? ["entries"] : ["prize", ...call.money.runnersN > 0 ? ["runnersUp"] : [], "entries"];
263474
+ legsOf = (call2) => call2.mode === "hire" ? ["entries"] : ["prize", ...call2.money.runnersN > 0 ? ["runnersUp"] : [], "entries"];
263465
263475
  pickQuota = (winners) => winners + 2;
263466
263476
  }
263467
263477
  });
@@ -263534,12 +263544,12 @@ __export(callRead_exports, {
263534
263544
  sharedPay: () => sharedPay,
263535
263545
  stageWords: () => stageWords
263536
263546
  });
263537
- function stageWords(call, now3 = Date.now()) {
263538
- if (now3 < Date.parse(call.closes)) return `open until ${day(call.closes)}`;
263539
- if (call.mode === "hire") return `closed ${day(call.closes)}; every entry that passed is paid as it is judged`;
263540
- if (now3 < Date.parse(call.drop)) return `closed ${day(call.closes)}; the shortlist shows ${day(call.drop)}`;
263541
- if (now3 < Date.parse(call.pick)) return `closed; the shortlist is out; the pick by ${day(call.pick)}`;
263542
- return `closed; the pick was due ${day(call.pick)}`;
263547
+ function stageWords(call2, now3 = Date.now()) {
263548
+ if (now3 < Date.parse(call2.closes)) return `open until ${day(call2.closes)}`;
263549
+ if (call2.mode === "hire") return `closed ${day(call2.closes)}; every entry that passed is paid as it is judged`;
263550
+ if (now3 < Date.parse(call2.drop)) return `closed ${day(call2.closes)}; the shortlist shows ${day(call2.drop)}`;
263551
+ if (now3 < Date.parse(call2.pick)) return `closed; the shortlist is out; the pick by ${day(call2.pick)}`;
263552
+ return `closed; the pick was due ${day(call2.pick)}`;
263543
263553
  }
263544
263554
  function sharedPay(quote, share) {
263545
263555
  const leg = grossFor(Math.round(quote * 1e6), share);
@@ -263572,36 +263582,36 @@ ${block2.prompt}`;
263572
263582
  }
263573
263583
  function callReadout(terms, ctx) {
263574
263584
  const amount3 = (value, symbol2) => dollarsOfValue(value, symbol2, ctx.network ?? "mainnet");
263575
- const call = terms.call;
263576
- const m = call.money;
263577
- const hire = call.mode === "hire";
263585
+ const call2 = terms.call;
263586
+ const m = call2.money;
263587
+ const hire = call2.mode === "hire";
263578
263588
  const lines2 = terms.criteria.filter((one) => one.kind === "judge").map((one) => one.text);
263579
263589
  const n = countWords(lines2.length || 5);
263580
- const entriesId = call.leg === "entries" ? ctx.id : call.legs?.entries;
263590
+ const entriesId = call2.leg === "entries" ? ctx.id : call2.legs?.entries;
263581
263591
  const money2 = hire ? [`every entry that passes all ${n} lines is paid ${amount3(m.entryEach, ctx.symbol)}, up to ${m.entryUpTo} ${m.entryUpTo === 1 ? "entry" : "entries"}; there is no pick`] : [
263582
263592
  `the pick gets ${amount3(m.prize, ctx.symbol)}`,
263583
263593
  m.runnersN > 0 ? `${m.runnersN} runner${m.runnersN === 1 ? "-up gets" : "s-up get"} ${amount3(m.runnersEach, ctx.symbol)} each` : "",
263584
263594
  `every entry that passes all ${n} lines is paid ${amount3(m.entryEach, ctx.symbol)}, up to ${m.entryUpTo}`
263585
263595
  ].filter(Boolean);
263586
263596
  const who = terms.access === "invite" ? "By invitation: only the people the poster put on the list can enter. If you were sent a link, pass it as submit_work's `invite`; if your address or page was named, nothing more is needed." : terms.access === "apply" ? "By application: apply_to_call first, with a short note; when the poster says yes, submit_work is admitted." : "Open to anyone.";
263587
- const review = call.kind === "review";
263588
- const shared = call.share ? sharedPay(m.entryEach, call.share) : null;
263589
- const shareLine2 = shared && call.share ? `each ${review ? "pro" : hire ? "hired person" : "entry paid"} receives ${amount3(shared.receives, ctx.symbol)} (the leg is ${amount3(shared.leg, ctx.symbol)}; the network keeps ${amount3(shared.keeps, ctx.symbol)}: ${shareWords(call.share, "as posted")})${hire ? "" : "; the pick's and the runners-up's legs carry the same share on top of theirs"}` : "";
263590
- const proMoney = call.sponsor && call.share && hire ? `Money: ${feeLine({ split: call.share, sponsorName: call.sponsor.name, net: m.entryEach, symbol: ctx.symbol, network: ctx.network ?? "mainnet" })} Up to ${m.entryUpTo} ${m.entryUpTo === 1 ? "report is" : "reports are"} paid, each one that passes all ${n} lines; there is no pick. Held on the ask's own account before anyone enters.` : null;
263597
+ const review = call2.kind === "review";
263598
+ const shared = call2.share ? sharedPay(m.entryEach, call2.share) : null;
263599
+ const shareLine2 = shared && call2.share ? `each ${review ? "pro" : hire ? "hired person" : "entry paid"} receives ${amount3(shared.receives, ctx.symbol)} (the leg is ${amount3(shared.leg, ctx.symbol)}; the network keeps ${amount3(shared.keeps, ctx.symbol)}: ${shareWords(call2.share, "as posted")})${hire ? "" : "; the pick's and the runners-up's legs carry the same share on top of theirs"}` : "";
263600
+ const proMoney = call2.sponsor && call2.share && hire ? `Money: ${feeLine({ split: call2.share, sponsorName: call2.sponsor.name, net: m.entryEach, symbol: ctx.symbol, network: ctx.network ?? "mainnet" })} Up to ${m.entryUpTo} ${m.entryUpTo === 1 ? "report is" : "reports are"} paid, each one that passes all ${n} lines; there is no pick. Held on the ask's own account before anyone enters.` : null;
263591
263601
  const out = [
263592
- `${review ? "Pro ask" : hire ? "Hiring call" : "Open call"} "${call.slug}" \xB7 posted by ${call.poster} \xB7 ${stageWords(call, ctx.now)}`,
263602
+ `${review ? "Pro ask" : hire ? "Hiring call" : "Open call"} "${call2.slug}" \xB7 posted by ${call2.poster} \xB7 ${stageWords(call2, ctx.now)}`,
263593
263603
  ...review && ctx.site ? [`Its page: ${proOrigin(ctx.site)}/pro/reviews/${ctx.id}`] : [],
263594
- ...call.sponsor ? [`Marketplace: ${call.sponsor.name} (sponsor "${call.sponsor.slug}"), which verifies the pros on this pro ask and stands behind them.`] : [],
263595
- call.title,
263596
- call.lede,
263597
- ...review && call.subject ? [`Reviewing: ${call.subject.name}${call.subject.version ? ` ${call.subject.version}` : ""}${call.subject.where ? ` \xB7 ${call.subject.where}` : ""}. Access details, if any, are in your invitation.`] : [],
263598
- ...review && call.scenarios?.length ? ["Scenarios, each one a step in your report:", ...call.scenarios.map((one, index) => ` S${index + 1}. ${one}`)] : [],
263604
+ ...call2.sponsor ? [`Marketplace: ${call2.sponsor.name} (sponsor "${call2.sponsor.slug}"), which verifies the pros on this pro ask and stands behind them.`] : [],
263605
+ call2.title,
263606
+ call2.lede,
263607
+ ...review && call2.subject ? [`Reviewing: ${call2.subject.name}${call2.subject.version ? ` ${call2.subject.version}` : ""}${call2.subject.where ? ` \xB7 ${call2.subject.where}` : ""}. Access details, if any, are in your invitation.`] : [],
263608
+ ...review && call2.scenarios?.length ? ["Scenarios, each one a step in your report:", ...call2.scenarios.map((one, index) => ` S${index + 1}. ${one}`)] : [],
263599
263609
  proMoney ?? `Money: ${[...money2, shareLine2].filter(Boolean).join("; ")}. Locked in ${hire ? "the ask's own account" : "each leg's own account"} before anyone enters.`,
263600
263610
  who,
263601
- call.leg === "entries" ? review ? `Enter: a review report with screenshots, as submit_work's report and files, collectionId "${ctx.id}"${terms.approver ? `; the AI checks it against the requirements, then ${terms.approver} gives final approval to release payment` : "; the AI checks it against the requirements and a passing report is paid"}. One report per account.` : `Enter: one page of text, in your own words: submit_work collectionId "${ctx.id}". One entry per account.` : `The pick's leg: only the entry the poster picked may claim here, with one line naming it. ${entriesId ? `The entries are ask ${entriesId}.` : "The entries are the call's own ask."}`,
263611
+ call2.leg === "entries" ? review ? `Enter: a review report with screenshots, as submit_work's report and files, collectionId "${ctx.id}"${terms.approver ? `; the AI checks it against the requirements, then ${terms.approver} gives final approval to release payment` : "; the AI checks it against the requirements and a passing report is paid"}. One report per account.` : `Enter: one page of text, in your own words: submit_work collectionId "${ctx.id}". One entry per account.` : `The pick's leg: only the entry the poster picked may claim here, with one line naming it. ${entriesId ? `The entries are ask ${entriesId}.` : "The entries are the call's own ask."}`,
263602
263612
  hire ? `Judged: ${n} requirements that every entry must meet. Pass all ${n} and the entry is paid on approval; nobody is picked.` : `Judged: ${n} requirements that every entry must meet. Pass all ${n} and the entry is paid; how clearly it meets each requirement ranks the entries for the pick.`,
263603
263613
  "",
263604
- ...call.blocks.map(blockText).flatMap((text4) => [text4, ""]),
263614
+ ...call2.blocks.map(blockText).flatMap((text4) => [text4, ""]),
263605
263615
  `The ${n} requirements:`,
263606
263616
  ...lines2.length ? lines2.map((text4, index) => `${index + 1}. ${text4}`) : ["(on the entries ask)"]
263607
263617
  ];
@@ -264483,6 +264493,74 @@ var init_call2 = __esm({
264483
264493
  }
264484
264494
  });
264485
264495
 
264496
+ // src/payDraft.ts
264497
+ var payDraft_exports = {};
264498
+ __export(payDraft_exports, {
264499
+ DRAFT_ID: () => DRAFT_ID,
264500
+ cardPreview: () => cardPreview,
264501
+ confirmToken: () => confirmToken,
264502
+ draftStatus: () => draftStatus,
264503
+ quoteOf: () => quoteOf,
264504
+ saveDraft: () => saveDraft
264505
+ });
264506
+ function quoteOf(rewardBase, feeBase) {
264507
+ const proReceivesCents = Math.round(rewardBase / 1e4);
264508
+ const feeCents = Math.round(feeBase / 1e4);
264509
+ return { proReceivesCents, feeCents, youPayCents: proReceivesCents + feeCents };
264510
+ }
264511
+ function cardPreview(quote) {
264512
+ if (quote.youPayCents < MIN_CENTS || quote.youPayCents > MAX_CENTS) return { refused: `a card payment is ${dollars3(MIN_CENTS)} to ${dollars3(MAX_CENTS)}, and this is ${dollars3(quote.youPayCents)}` };
264513
+ return [
264514
+ `Pro receives ${dollars3(quote.proReceivesCents)} \xB7 Fee ${dollars3(quote.feeCents)} \xB7 You pay ${dollars3(quote.youPayCents)}`,
264515
+ "Paid by card: I'll give you a link where the person pays and posts it from their own account. Nothing is signed or spent here.",
264516
+ "",
264517
+ `To go ahead, call again with confirm: "${confirmToken(quote)}".`
264518
+ ].join("\n");
264519
+ }
264520
+ async function call(site, path, body, fetchImpl) {
264521
+ const response = await fetchImpl(`${site.replace(/\/+$/, "")}${path}`, {
264522
+ method: body === null ? "GET" : "POST",
264523
+ headers: { accept: "application/json", ...body === null ? {} : { "content-type": "application/json" } },
264524
+ ...body === null ? {} : { body: JSON.stringify(body) },
264525
+ signal: AbortSignal.timeout(2e4)
264526
+ });
264527
+ const parsed = await response.json().catch(() => ({}));
264528
+ return { status: response.status, body: parsed };
264529
+ }
264530
+ async function saveDraft(site, draft, fetchImpl = fetch) {
264531
+ const saved = await call(site, "/api/pay-drafts", draft, fetchImpl).catch((error54) => ({ status: 0, body: { error: error54 instanceof Error ? error54.message : String(error54) } }));
264532
+ const id = saved.body.id;
264533
+ if (saved.status !== 201 || typeof id !== "string" || !DRAFT_ID.test(id)) return `The pay link could not be made (${String(saved.body.error ?? saved.status)}). Nothing was posted and nothing was spent. Try again shortly.`;
264534
+ return [
264535
+ `Saved. The person pays ${confirmToken(draft.quote)} by card and posts it here, within 24 hours:`,
264536
+ `${site.replace(/\/+$/, "")}/pay/${id}`,
264537
+ `Nothing was posted or spent yet. read_ask "${id}" says whether it is paid and live.`
264538
+ ].join("\n");
264539
+ }
264540
+ async function draftStatus(site, id, fetchImpl = fetch) {
264541
+ const read9 = await call(site, `/api/pay-drafts/${id}`, null, fetchImpl).catch(() => ({ status: 0, body: {} }));
264542
+ const link5 = `${site.replace(/\/+$/, "")}/pay/${id}`;
264543
+ if (read9.status === 404) return `No pay link "${id}" on this network.`;
264544
+ if (read9.status === 410) return "That pay link expired unpaid (links last 24 hours). Nothing was paid or posted: post it again for a new link.";
264545
+ if (read9.status !== 200) return "The pay link could not be read just now. Ask me again in a minute.";
264546
+ const { state, collection, quote } = read9.body;
264547
+ if (state === "posted" && collection) return `Live as ask #${collection}: ${site.replace(/\/+$/, "")}/t/${collection}`;
264548
+ const pays = quote ? ` (${confirmToken(quote)})` : "";
264549
+ if (state === "paying") return `Paying${pays}: the card payment has started at ${link5}. It goes live once the money reaches the ask.`;
264550
+ return `Waiting for payment${pays}: nobody has paid at ${link5} yet.`;
264551
+ }
264552
+ var DRAFT_ID, MIN_CENTS, MAX_CENTS, dollars3, confirmToken;
264553
+ var init_payDraft = __esm({
264554
+ "src/payDraft.ts"() {
264555
+ "use strict";
264556
+ DRAFT_ID = /^[A-Za-z0-9_-]{22}$/;
264557
+ MIN_CENTS = 500;
264558
+ MAX_CENTS = 5e4;
264559
+ dollars3 = (cents2) => `$${(cents2 / 100).toFixed(2)}`;
264560
+ confirmToken = (quote) => dollars3(quote.youPayCents);
264561
+ }
264562
+ });
264563
+
264486
264564
  // ../../packages/openhands-sdk/src/chain/inviteMessages.ts
264487
264565
  async function inviteOpWords(op, collectionId, issuer, body, now3 = Date.now()) {
264488
264566
  return inviteOpMessage(op, collectionId, issuer, now3, INVITE_WRITES.includes(op) ? await digestOf(inviteOpBody(op, body)) : void 0);
@@ -266264,10 +266342,10 @@ function clean5(raw) {
266264
266342
  return text4.length <= MAX_LENGTH ? text4 : `${text4.slice(0, MAX_LENGTH - 1).trimEnd()}\u2026`;
266265
266343
  }
266266
266344
  async function placeName(point, zoom, reach = {}) {
266267
- const call = reach.fetch ?? globalThis.fetch;
266345
+ const call2 = reach.fetch ?? globalThis.fetch;
266268
266346
  const url4 = `${ENDPOINT}?lat=${encodeURIComponent(point.lat)}&lon=${encodeURIComponent(point.lon)}&format=jsonv2&zoom=${encodeURIComponent(zoom)}`;
266269
266347
  try {
266270
- const response = await call(url4, {
266348
+ const response = await call2(url4, {
266271
266349
  headers: { "user-agent": USER_AGENT, accept: "application/json" },
266272
266350
  signal: AbortSignal.timeout(TIMEOUT_MS6)
266273
266351
  });
@@ -267521,8 +267599,8 @@ function readKalshiEvent(body, url4) {
267521
267599
  const markets = event.markets ?? body.markets ?? [];
267522
267600
  const first = markets[0];
267523
267601
  const priceOf2 = (m) => {
267524
- const dollars3 = Number(m.last_price_dollars);
267525
- if (Number.isFinite(dollars3) && m.last_price_dollars !== void 0) return dollars3;
267602
+ const dollars4 = Number(m.last_price_dollars);
267603
+ if (Number.isFinite(dollars4) && m.last_price_dollars !== void 0) return dollars4;
267526
267604
  return typeof m.last_price === "number" ? m.last_price / 100 : null;
267527
267605
  };
267528
267606
  const question = event.sub_title ? `${event.title} (${event.sub_title})` : event.title;
@@ -267554,7 +267632,7 @@ var HEADERS, rulesOf2, jsonList;
267554
267632
  var init_market = __esm({
267555
267633
  "src/market.ts"() {
267556
267634
  "use strict";
267557
- HEADERS = { "user-agent": "Mozilla/5.0 (compatible; asks-mcp; +https://openhands.network)", accept: "application/json" };
267635
+ HEADERS = { "user-agent": "Mozilla/5.0 (compatible; asks-mcp; +https://asks.world)", accept: "application/json" };
267558
267636
  rulesOf2 = (text4) => {
267559
267637
  if (typeof text4 !== "string" || !text4.trim()) return void 0;
267560
267638
  const clean6 = text4.replace(/\s+/g, " ").trim();
@@ -292028,7 +292106,7 @@ import { randomBytes as randomBytes7 } from "node:crypto";
292028
292106
  // package.json
292029
292107
  var package_default = {
292030
292108
  name: "@ixo/asks-mcp",
292031
- version: "1.6.0-dev.6",
292109
+ version: "1.6.0",
292032
292110
  description: "The Openhands Network MCP server: read the board, answer asks, post work and get paid on the ixo chain, with a key of its own on your machine.",
292033
292111
  license: "Apache-2.0",
292034
292112
  repository: {
@@ -292187,7 +292265,7 @@ var TOOLS = {
292187
292265
  },
292188
292266
  read_ask: {
292189
292267
  title: "Read one ask",
292190
- description: "The full terms of one ask. What would count as done, what it pays, who posted it.",
292268
+ description: `The full terms of one ask. What would count as done, what it pays, who posted it. Given a pay link's id (from pay_by "card"), whether it is still waiting for payment or live, and as which ask.`,
292191
292269
  spends: false,
292192
292270
  hosted: true,
292193
292271
  annotations: reads
@@ -292208,9 +292286,9 @@ var TOOLS = {
292208
292286
  },
292209
292287
  post_ask: {
292210
292288
  title: "Post an ask (spends money)",
292211
- description: `Escrow money against a task for somebody else to do. Money moves. Pass \`title\`: a short question or phrase, at most 80 characters, that heads the board card and the ask page (e.g. "Is Red Harbor Ketchup on the shelf at Pick n Pay?"); \`task\` is everything whoever answers must do. Requires \`confirm\` to restate the exact total you were shown. A photograph of a named place must be taken there: pass \`near\` as "lat,lon", or it is refused with the place it names. Private asks: pass \`access\` "invite" (only people you invite can answer; not on the public board) or "apply" (people apply, you say yes), with \`invite\` (addresses, @handles, or "link" per single-use link). Default: anyone can answer. The board marks it as an agent's ask. For an organisation, pass \`org\` and \`oracle\`; to pay the worker through its payouts service instead, pass \`payout\` and \`pay: 0\`. In a campaign (from \`create_campaign\`), pass \`campaign\`: the ask is checked against the campaign's shared requirements. \`image\` (png, jpg or webp, at most 5 MB) is the ask's picture. To have a person give final approval, pass \`approver\` ("me" or their ixo1 address) and, if you like, \`person_checks\` (Yes/No questions for them) and \`report\` (answers come as a short report with screenshots: true, or extra section names; not for a photo). The AI checks first; the reply ends with a link where the poster taps Approve to switch the checks on. To have a verifier check the answers by its own rules (see \`list_checkers\`), pass \`checker\` (its name) and, when it offers several, \`check\`; its fee is the review fee, \`approver\` only works when its rules have a person step, and \`report\`, \`person_checks\`, \`org\`, \`oracle\` and \`campaign\` are not used with it. ${ASK_RULES}`,
292289
+ description: `Escrow money against a task for somebody else to do. Money moves. Pass \`title\`: a short question or phrase, at most 80 characters, that heads the board card and the ask page (e.g. "Is Red Harbor Ketchup on the shelf at Pick n Pay?"); \`task\` is everything whoever answers must do. Requires \`confirm\` to restate the exact total you were shown. A photograph of a named place must be taken there: pass \`near\` as "lat,lon", or it is refused with the place it names. Private asks: pass \`access\` "invite" (only people you invite can answer; not on the public board) or "apply" (people apply, you say yes), with \`invite\` (addresses, @handles, or "link" per single-use link). Default: anyone can answer. The board marks it as an agent's ask. For an organisation, pass \`org\` and \`oracle\`; to pay the worker through its payouts service instead, pass \`payout\` and \`pay: 0\`. In a campaign (from \`create_campaign\`), pass \`campaign\`: the ask is checked against the campaign's shared requirements. \`image\` (png, jpg or webp, at most 5 MB) is the ask's picture. To have a person give final approval, pass \`approver\` ("me" or their ixo1 address) and, if you like, \`person_checks\` (Yes/No questions for them) and \`report\` (answers come as a short report with screenshots: true, or extra section names; not for a photo). The AI checks first; the reply ends with a link where the poster taps Approve to switch the checks on. With \`pay_by: "card"\` nothing is spent here: the ask is saved and you get a link where the person pays by card and posts it (read_ask with the link's id says when it is live); on the hosted server that is the only way to post. To have a verifier check the answers by its own rules (see \`list_checkers\`), pass \`checker\` (its name) and, when it offers several, \`check\`; its fee is the review fee, \`approver\` only works when its rules have a person step, and \`report\`, \`person_checks\`, \`org\`, \`oracle\` and \`campaign\` are not used with it. ${ASK_RULES}`,
292212
292290
  spends: true,
292213
- hosted: false,
292291
+ hosted: true,
292214
292292
  annotations: acts
292215
292293
  },
292216
292294
  ask_the_world: {
@@ -292312,7 +292390,7 @@ var TOOLS = {
292312
292390
  },
292313
292391
  post_call: {
292314
292392
  title: "Post an open call or a pro ask (spends money)",
292315
- description: `Post a drafted open call or pro ask as the account you act for: a competition is three asks under one tag (the prize, the runners-up, the entries), a hire (every pro ask is one) is the entries alone; each holds its money until approval. One leg per call; call again until it says everything is posted. A pro ask confirms its marketplace (\`sponsor\`) with the registry first: the marketplace is written into its terms and earns its share of each approved report; a pro ask that names none, or one whose marketplace is paused or not set up here, is refused before anything moves. A call by invitation writes its list the moment the entries post, and prints the invite links. Pays in this network's own coin, said in dollars. Requires \`confirm\` to restate the exact total from the preview. ${ASK_RULES}`,
292393
+ description: `Post a drafted open call or pro ask as the account you act for: a competition is three asks under one tag (the prize, the runners-up, the entries), a hire (every pro ask is one) is the entries alone; each holds its money until approval. One leg per call; call again until it says everything is posted. A pro ask confirms its marketplace (\`sponsor\`) with the registry first: the marketplace is written into its terms and earns its share of each approved report; a pro ask that names none, or one whose marketplace is paused or not set up here, is refused before anything moves. A call by invitation writes its list the moment the entries post, and prints the invite links. Pays in this network's own coin, said in dollars. With \`pay_by: "card"\` nothing is spent here: the whole call is saved and you get a link where the person pays by card and posts it. Requires \`confirm\` to restate the exact total from the preview. ${ASK_RULES}`,
292316
292394
  spends: true,
292317
292395
  hosted: false,
292318
292396
  annotations: acts
@@ -292496,7 +292574,7 @@ var PLAN_TOOLS = ["scope_market", "draft_plan", "fund_plan", "close_plan", "plan
292496
292574
  var WHERE_TO_ACT_NAME = "where_to_act";
292497
292575
  var WHERE_TO_ACT = {
292498
292576
  title: "Where to post, submit or connect from here",
292499
- description: "This connector can only read. Anything that needs signing (posting asks, campaigns, payout settings, Pay for reviews, submitting work, getting paid, company onboarding and deploys) is done in Claude Code with the asks.world plugin (`/plugin install asks@ixo-plugins`), which runs on the user's computer so keys and data stay local. Call this and say so plainly to the user. Posting an ask, submitting work, connecting a person's account, sending earnings home and checking one key's standing need the local server, which holds its own key on the user's machine. This answers with the site's page for the action and the install lines for the local plugin and package.",
292577
+ description: "This connector holds no key: it reads, and posts an ask only as a pay link (post_ask with pay_by \"card\"). Anything else that needs signing (posting asks, campaigns, payout settings, Pay for reviews, submitting work, getting paid, company onboarding and deploys) is done in Claude Code with the asks.world plugin (`/plugin install asks@ixo-plugins`), which runs on the user's computer so keys and data stay local. Call this and say so plainly to the user. Posting an ask, submitting work, connecting a person's account, sending earnings home and checking one key's standing need the local server, which holds its own key on the user's machine. This answers with the site's page for the action and the install lines for the local plugin and package.",
292500
292578
  spends: false,
292501
292579
  hosted: true,
292502
292580
  /* Pure: it renders links and install lines and reaches nothing. */
@@ -292764,7 +292842,7 @@ var RESOURCE_PACKAGES = [
292764
292842
  { name: "@ixo/decision-oracle", range: "0.2", what: `company setup: the organisation, its verifier and Pay for reviews (used by ${PLUGIN_NAME})` },
292765
292843
  { name: "@ixo/payouts-facilitator", range: "0.1", what: `a company's own payouts service, for paying workers in USD Coin on Base (used by ${PLUGIN_NAME})` }
292766
292844
  ];
292767
- var READ_ONLY_LINE = "This connector can only read. To post asks, set up payouts, pay for reviews or get paid, use Claude Code with an asks.world plugin (asks): it runs on your computer, so your keys and data stay with you.";
292845
+ var READ_ONLY_LINE = 'This connector holds no key: it reads, and posts an ask only as a pay link (post_ask with pay_by "card"), where the person pays by card and posts it on asks.world. To post from your own money, set up payouts, pay for reviews or get paid, use Claude Code with an asks.world plugin (asks): it runs on your computer, so your keys and data stay with you.';
292768
292846
  var CLAUDE_CODE_ONE_STEP = "2.1.275";
292769
292847
  var PLUGIN_INSTALLS = ["claude-code", "claude-code-older", "codex"];
292770
292848
  function pluginLines(install, name8 = PLUGIN_NAME) {
@@ -294024,7 +294102,7 @@ async function createServer(options) {
294024
294102
  const server2 = new McpServer(
294025
294103
  { name: MCP_SERVER, version: VERSION6 },
294026
294104
  local ? void 0 : {
294027
- instructions: `asks.world (${NETWORK2}), hosted and read-only. List and read asks, collect a verified answer, check how an answer was judged. ${READ_ONLY_LINE} Install inside Claude Code: ${pluginLines("claude-code-older", plugin).replace("\n", " then ")} (plugin ${plugin}). Company onboarding and deploys run in the same plugin. Whenever a user asks for anything that needs signing, say this plainly, then call where_to_act for the website page they can use for a one-off action.`
294105
+ instructions: `asks.world (${NETWORK2}), hosted, holding no key. List and read asks, collect a verified answer, check how an answer was judged, and save an ask as a pay link the person pays by card (post_ask with pay_by "card"; read_ask with the link's id says when it is live). ${READ_ONLY_LINE} Install inside Claude Code: ${pluginLines("claude-code-older", plugin).replace("\n", " then ")} (plugin ${plugin}). Company onboarding and deploys run in the same plugin. Whenever a user asks for anything that needs signing, say this plainly, then call where_to_act for the website page they can use for a one-off action.`
294028
294106
  }
294029
294107
  );
294030
294108
  const parts = local ? await localParts() : null;
@@ -294114,7 +294192,7 @@ async function build2(options, server2, parts) {
294114
294192
  throw new BootError(`refusing to start. ${error54 instanceof Error ? error54.message : String(error54)}`);
294115
294193
  }
294116
294194
  })() : (
294117
- /* No tool that spends is registered in hosted mode, so nothing reads these. */
294195
+ /* No tool spends in hosted mode (post_ask there only saves a card's pay draft), so nothing reads these. */
294118
294196
  { perAsk: 0, perDay: 0, total: 0, perPlan: 0 }
294119
294197
  );
294120
294198
  let DEPLOYMENT = options.deployment ?? await deployment().catch((error54) => {
@@ -294425,18 +294503,18 @@ Each is its own ask and pays on its own. Read one with read_ask before deciding.
294425
294503
  calls.set(tag, { slug, entries: entries2, shared });
294426
294504
  })
294427
294505
  );
294428
- const callLine = (row, call) => {
294506
+ const callLine = (row, call2) => {
294429
294507
  const summary = row.campaignSummary;
294430
- const review = isReviewTitle(call.entries?.title);
294431
- const title = call.entries?.title.replace(/^(Open call|Expert review): /, "") ?? null;
294508
+ const review = isReviewTitle(call2.entries?.title);
294509
+ const title = call2.entries?.title.replace(/^(Open call|Expert review): /, "") ?? null;
294432
294510
  if (title) taint2(title);
294433
294511
  const facts = row.callSummary;
294434
- const shares = call.shared;
294435
- const entryPays = shares && facts?.entryEach !== void 0 && call.entries?.pays ? `${faceOf2({ value: facts.entryEach, symbol: call.entries.pays.symbol }, SANDBOX)?.text} (the network's share is on top of it)` : call.entries?.pays ? `${faceOf2(call.entries.pays, SANDBOX)?.text}${shares ? ", less the network's share" : ""}` : void 0;
294512
+ const shares = call2.shared;
294513
+ const entryPays = shares && facts?.entryEach !== void 0 && call2.entries?.pays ? `${faceOf2({ value: facts.entryEach, symbol: call2.entries.pays.symbol }, SANDBOX)?.text} (the network's share is on top of it)` : call2.entries?.pays ? `${faceOf2(call2.entries.pays, SANDBOX)?.text}${shares ? ", less the network's share" : ""}` : void 0;
294436
294514
  return [
294437
- `${review ? "Expert review" : "Open call"} "${call.slug}" \xB7 ${summary.open} of ${summary.places} legs open${entryPays ? ` \xB7 every ${review ? "report" : "entry"} that meets the bar is paid ${entryPays}` : ""} \xB7 needs-a-human`,
294515
+ `${review ? "Expert review" : "Open call"} "${call2.slug}" \xB7 ${summary.open} of ${summary.places} legs open${entryPays ? ` \xB7 every ${review ? "report" : "entry"} that meets the bar is paid ${entryPays}` : ""} \xB7 needs-a-human`,
294438
294516
  title ? asData("ask-title", title) : "",
294439
- `${review ? "A specialist's report on the review's scenarios, assessed against its requirements. A report that meets them qualifies for payment, after the builder's final approval when they give one." : "A written entry assessed against five requirements. Entries that meet all five qualify for payment. For competitions, the poster also chooses a prize winner."} ${call.entries ? `Read it with read_ask "${call.entries.id}"; the page is` : "Read its legs on the page:"} ${review ? `${EXPERT_SITE}/pro/reviews/${call.entries?.id ?? call.slug}` : `${SITE}/opencall/${call.slug}`}`
294517
+ `${review ? "A specialist's report on the review's scenarios, assessed against its requirements. A report that meets them qualifies for payment, after the builder's final approval when they give one." : "A written entry assessed against five requirements. Entries that meet all five qualify for payment. For competitions, the poster also chooses a prize winner."} ${call2.entries ? `Read it with read_ask "${call2.entries.id}"; the page is` : "Read its legs on the page:"} ${review ? `${EXPERT_SITE}/pro/reviews/${call2.entries?.id ?? call2.slug}` : `${SITE}/opencall/${call2.slug}`}`
294440
294518
  ].filter(Boolean).join("\n");
294441
294519
  };
294442
294520
  const lines2 = board.rows.filter(within2).map((row) => {
@@ -294454,8 +294532,8 @@ Each is its own ask and pays on its own. Read one with read_ask before deciding.
294454
294532
  }
294455
294533
  if ((row.card === "campaign" || row.card === "call") && row.campaignSummary) {
294456
294534
  const summary = row.campaignSummary;
294457
- const call = calls.get(summary.tag);
294458
- if (call) return callLine(row, call);
294535
+ const call2 = calls.get(summary.tag);
294536
+ if (call2) return callLine(row, call2);
294459
294537
  const low = summary.low ? faceOf2(summary.low, SANDBOX)?.text : void 0;
294460
294538
  const high = summary.high ? faceOf2(summary.high, SANDBOX)?.text : void 0;
294461
294539
  const range = summary.pool || !low ? "paid from pools" : high && high !== low ? `${low} to ${high} a place` : `${low} a place`;
@@ -294569,9 +294647,10 @@ ${reviewReportTemplate({ fields: true })}`] : []
294569
294647
  "read_ask",
294570
294648
  {
294571
294649
  ...meta3("read_ask"),
294572
- inputSchema: { collectionId: external_exports.string().regex(/^\d+$/) }
294650
+ inputSchema: { collectionId: external_exports.string().regex(/^(\d+|[A-Za-z0-9_-]{22})$/).describe(`The ask's number, or a pay link's id from post_ask/post_call with pay_by "card".`) }
294573
294651
  },
294574
294652
  async ({ collectionId }) => {
294653
+ if (!/^\d+$/.test(collectionId)) return text4(await (await Promise.resolve().then(() => (init_payDraft(), payDraft_exports))).draftStatus(SITE, collectionId, fetch2));
294575
294654
  const mine = await address2();
294576
294655
  const acting = (await actingOrWhy2(mine)).acting ?? null;
294577
294656
  const ours = [mine, ...[acting?.principal].filter((one) => Boolean(one))];
@@ -294591,11 +294670,11 @@ ${reviewReportTemplate({ fields: true })}`] : []
294591
294670
  {
294592
294671
  ...meta3("read_ask"),
294593
294672
  inputSchema: {
294594
- collectionId: external_exports.string().regex(/^\d+$/),
294673
+ collectionId: external_exports.string().regex(/^(\d+|[A-Za-z0-9_-]{22})$/).describe(`The ask's number, or a pay link's id from post_ask with pay_by "card".`),
294595
294674
  address: external_exports.string().regex(ADDRESS3).optional().describe("Your own ixo address, if you have one: the answer then says whether that address has already answered. This hosted server holds no key, so it cannot know otherwise.")
294596
294675
  }
294597
294676
  },
294598
- async ({ collectionId, address: given2 }) => readAsk(collectionId, given2 ? [given2] : [])
294677
+ async ({ collectionId, address: given2 }) => /^\d+$/.test(collectionId) ? readAsk(collectionId, given2 ? [given2] : []) : text4(await (await Promise.resolve().then(() => (init_payDraft(), payDraft_exports))).draftStatus(SITE, collectionId, fetch2))
294599
294678
  );
294600
294679
  }
294601
294680
  if (offered("submit_work")) server2.registerTool(
@@ -294863,18 +294942,22 @@ Never open it yourself.`
294863
294942
  invite: external_exports.array(external_exports.string().max(64)).max(50).optional().describe('With access "invite" (or "apply", to let some in at once): ixo1 addresses, @handles of people with a page, and "link" once per single-use link you want (each admits one new person).'),
294864
294943
  ...approverInput,
294865
294944
  ...checkerInput,
294945
+ pay_by: external_exports.enum(["card"]).optional().describe('"card": spend nothing from this key. The ask is saved and you get a link where the person pays by card and posts it from their own account. Not with org, oracle, payout, campaign or image. The only way to post from the hosted server.'),
294866
294946
  confirm: external_exports.string().optional().describe("The exact total from the preview, e.g. '$1.50'. Omit to preview.")
294867
294947
  }
294868
294948
  },
294869
- async ({ task, title, pay, quota, days, near, within_m, place, series, org, oracle, payout, campaign, image, access: access2, invite, approver, person_checks, report, checker, check: check2, confirm }) => {
294949
+ async ({ task, title, pay, quota, days, near, within_m, place, series, org, oracle, payout, campaign, image, access: access2, invite, approver, person_checks, report, checker, check: check2, pay_by, confirm }) => {
294950
+ const card = pay_by === "card";
294951
+ if (!local && !card) return text4(`This hosted server holds no key, so it posts only by card: pass pay_by: "card" for a link where the person pays and posts it. Nothing was posted.`);
294952
+ if (card && (org || oracle || payout || campaign || image)) return text4("Refused: an ask paid by card is the person's own: leave `org`, `oracle`, `payout`, `campaign` and `image` out. Nothing was posted and nothing was spent.");
294870
294953
  if (isTainted(task) || title && isTainted(title)) return text4(TAINT_REFUSAL);
294871
294954
  const door2 = (await Promise.resolve().then(() => (init_call2(), call_exports2))).doorOf(access2, invite);
294872
294955
  if ("refused" in door2) return text4(`Refused: ${door2.refused}. Nothing was posted and nothing was spent.`);
294873
294956
  const cannot = await unjudged();
294874
294957
  if (cannot) return text4(cannot);
294875
- const acting = await principal2();
294958
+ const acting = card ? null : await principal2();
294876
294959
  const { hqPlan: hqPlan2, evaluationPrice: evaluationPrice2, oracleSigner: oracleSigner2, orgDefaultOracle: orgDefaultOracle2, orgFundsRefusal: orgFundsRefusal2, rewardCoin: rewardCoin2 } = await Promise.resolve().then(() => (init_hq(), hq_exports));
294877
- const coin = rewardCoin2(void 0, NETWORK2);
294960
+ const coin = card ? FEE_DENOM : rewardCoin2(void 0, NETWORK2);
294878
294961
  if (typeof coin !== "string") return text4(`Refused: ${coin.refused}. Nothing was posted and nothing was spent.`);
294879
294962
  let expert = null;
294880
294963
  if (checker) {
@@ -295030,10 +295113,32 @@ Never open it yourself.`
295030
295113
  const { total, what: preview, details: costDetails } = orgAsk ? { total: 0, what: `nothing from this key (${orgName} pays)`, details: `What this costs ${orgName}:
295031
295114
  ${orgCosts}` } : { total: onChain.total, what: perCheck ? `${onChain.what}. Review fee: ${billing.creditsCopy(null, feeWords(perCheck.price))}` : onChain.what, details: "" };
295032
295115
  const orgToken = orgAsk ? (await Promise.resolve().then(() => (init_hq(), hq_exports))).orgConfirmToken({ offChainTotal: offChain ? `${(Number(offChain.amount) * spots).toFixed(2)} ${offChain.currency}` : null, rewards: rewardsFace, feesSetAside: !perCheck && legBase > 0 ? feeWords(legBase * spots) : null }) : void 0;
295033
- const guard = spendGuard({ bucket: "post", task, total, ...confirm ? { confirm } : {}, what: preview, details: costDetails, symbol: symbol2, ...orgToken ? { token: orgToken } : {} });
295116
+ const payDraft = card ? await Promise.resolve().then(() => (init_payDraft(), payDraft_exports)) : null;
295117
+ const quote = payDraft?.quoteOf(Number(baseFromPay2(pay)) * spots, legBase * spots);
295118
+ const cardWords = payDraft && quote ? payDraft.cardPreview(quote) : null;
295119
+ if (cardWords && typeof cardWords !== "string") return text4(`Refused: ${cardWords.refused}. Nothing was posted and nothing was spent.`);
295120
+ if (payDraft && quote && confirm) {
295121
+ if (confirm.trim() !== payDraft.confirmToken(quote)) return text4(`Refused: confirm said "${confirm}" but the person pays ${payDraft.confirmToken(quote)}. Preview it again.`);
295122
+ const draftTerms = {
295123
+ title: name8,
295124
+ terms,
295125
+ amountUixo: String(baseFromPay2(pay)),
295126
+ quota: spots,
295127
+ days: days ?? 7,
295128
+ denom: coin,
295129
+ ...reviewer ? { checker: reviewer, checkRewardUixo: legBase } : {},
295130
+ ...expert ? { protocol: expert.check.did, feeDenom: expert.feeDenom } : {},
295131
+ ...perCheck ? { perCheck } : {},
295132
+ ...series ? { series } : {},
295133
+ ...door2.access ? { access: door2.access, invite: door2.invite } : {}
295134
+ };
295135
+ return text4(await payDraft.saveDraft(SITE, { kind: "ask", terms: draftTerms, quote, network: NETWORK2 }, fetch2));
295136
+ }
295137
+ const guard = cardWords ?? spendGuard({ bucket: "post", task, total, ...confirm ? { confirm } : {}, what: preview, details: costDetails, symbol: symbol2, ...orgToken ? { token: orgToken } : {} });
295034
295138
  if (guard) {
295035
295139
  const notes = confirm ? [] : [
295036
- actingNote(acting),
295140
+ /* By card the person posts as themselves at the link; and a hosted server has no connection to name. */
295141
+ card ? null : actingNote(acting),
295037
295142
  expert ? expertNote(expert) : checkingNote(terms, acting?.principal ?? null),
295038
295143
  expert ? null : orgAsk ? `Reviewed by ${await oracleWordsFor({ did: orgAsk.oracle.did, address: orgAsk.oracle.address })}.` : reviewer && reviewer !== CHECKER ? `Reviewed by ${await oracleWordsFor({ address: reviewer })}.` : null,
295039
295144
  doorWords(door2.access, door2.invite.length),
@@ -296269,16 +296374,16 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
296269
296374
  }
296270
296375
  },
296271
296376
  async ({ slug: named, clear, ...input }) => {
296272
- const call = await Promise.resolve().then(() => (init_call2(), call_exports2));
296377
+ const call2 = await Promise.resolve().then(() => (init_call2(), call_exports2));
296273
296378
  const { CALL_SLUG: CALL_SLUG2 } = await Promise.resolve().then(() => (init_terms(), terms_exports));
296274
- if (call.typedText(input).some((one) => isTainted(one))) return text4(TAINT_REFUSAL);
296275
- const slug = named?.trim() || call.nextSlug();
296379
+ if (call2.typedText(input).some((one) => isTainted(one))) return text4(TAINT_REFUSAL);
296380
+ const slug = named?.trim() || call2.nextSlug();
296276
296381
  if (!CALL_SLUG2.test(slug)) return text4(`"${slug}" cannot name a call: lowercase letters, digits and dashes, starting with a letter, 3 to 40 long. Nothing was saved.`);
296277
- const existing = call.readDraft(slug);
296382
+ const existing = call2.readDraft(slug);
296278
296383
  const posted = existing ? Object.values(existing.posted).some(Boolean) || Boolean(existing.inFlight) : false;
296279
296384
  const changing = clear || Object.values(input).some((value) => value !== void 0);
296280
296385
  if (posted && changing) return text4(`Call "${slug}" is already posted: its terms were frozen with the money. Start a new call with another slug.`);
296281
- const start = clear || !existing ? call.freshDraft(slug) : existing;
296386
+ const start = clear || !existing ? call2.freshDraft(slug) : existing;
296282
296387
  const marketplaces = await Promise.resolve().then(() => (init_marketplaces(), marketplaces_exports));
296283
296388
  let given2 = input;
296284
296389
  const notTaken = [];
@@ -296290,18 +296395,18 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
296290
296395
  given2 = { ...input, sponsor: void 0 };
296291
296396
  }
296292
296397
  }
296293
- const merged = call.mergeDraft(start, given2);
296294
- const coin = call.callCoin(merged.draft, NETWORK2, ASK_DENOM);
296398
+ const merged = call2.mergeDraft(start, given2);
296399
+ const coin = call2.callCoin(merged.draft, NETWORK2, ASK_DENOM);
296295
296400
  if (typeof coin !== "string") return text4(`Refused: ${coin.refused}. Nothing was saved.`);
296296
296401
  const refused2 = [...notTaken, ...merged.refused];
296297
296402
  const draft = coin !== ASK_DENOM ? { ...merged.draft, coin } : merged.draft;
296298
- call.writeDraft(draft);
296403
+ call2.writeDraft(draft);
296299
296404
  const coinSymbol = symbolOf(coin);
296300
296405
  const acting = await principal2();
296301
296406
  const held = await heldBy(acting, coin);
296302
296407
  const holds = held === null ? null : pays(held.reward, coinSymbol);
296303
296408
  const choices = draft.fields.kind === "review" && !draft.fields.sponsor ? await marketplaces.readMarketplaces(SITE, fetch2) : null;
296304
- const missing2 = call.missingOf(draft.fields, { symbol: coinSymbol, holds, marketplaces: choices && "sponsors" in choices ? marketplaces.marketplaceChoices(choices.sponsors) : null });
296409
+ const missing2 = call2.missingOf(draft.fields, { symbol: coinSymbol, holds, marketplaces: choices && "sponsors" in choices ? marketplaces.marketplaceChoices(choices.sponsors) : null });
296305
296410
  const out = [`Draft "${slug}" saved${posted ? " (posted)" : ""}.`];
296306
296411
  if (refused2.length) out.push("", "Not taken:", ...refused2.map((one) => ` - ${one}`));
296307
296412
  if (missing2.length) {
@@ -296310,7 +296415,7 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
296310
296415
  "Still needed, in this order. Ask the person, a group at a time, in their words; never write the brief for them:",
296311
296416
  ...missing2.flatMap((one, index) => [` ${index + 1}. ${one.field}: ${one.question}`, ` e.g. ${one.example}`])
296312
296417
  );
296313
- const optional5 = call.optionalOf(draft.fields, originOf(NETWORK2));
296418
+ const optional5 = call2.optionalOf(draft.fields, originOf(NETWORK2));
296314
296419
  if (optional5.length) out.push("", "Optional, not given yet:", ...optional5.map((one) => ` - ${one}`));
296315
296420
  return text4(out.join("\n"));
296316
296421
  }
@@ -296320,7 +296425,7 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
296320
296425
  const checker = await callChecker2(draft.fields.kind === "review");
296321
296426
  if (checker.fee && checker.fee.denom !== coin) return text4(`Refused: ${checker.name} is paid in another coin than this call pays in, and a review pays in its verifier's coin. Start a new review.`);
296322
296427
  const perCheck = checker.fee ? false : await callPerCheck(checker.address);
296323
- const priced = call.callShare(draft.fields, call.callShares(draft.fields) ? await callSplit() : null);
296428
+ const priced = call2.callShare(draft.fields, call2.callShares(draft.fields) ? await callSplit() : null);
296324
296429
  if ("unread" in priced) {
296325
296430
  out.push("", "Not priced yet: this call is a hire by invitation or application, which carries the network's share, and the fee split could not be read just now. The draft is saved; call draft_call again in a minute.");
296326
296431
  return text4(out.join("\n"));
@@ -296329,14 +296434,14 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
296329
296434
  const resolved = proAsk && draft.fields.sponsor ? await marketplaces.resolveMarketplace(SITE, draft.fields.sponsor, NETWORK2, fetch2) : null;
296330
296435
  const sponsor = resolved && "sponsor" in resolved ? resolved.sponsor : null;
296331
296436
  const sponsorProblem = resolved && "refused" in resolved ? resolved : null;
296332
- const plan = call.callPlan(draft, { poster: shortPoster, checkerLegBase: (terms) => checker.fee ? checker.fee.base : perCheck ? 0 : checkerLegBase2(FEE_DENOM, terms, CHECK_REWARD), oracle: checker.address || "the verifier", share: priced.share, feeInReward: Boolean(checker.fee) || coin === FEE_DENOM, sponsor });
296437
+ const plan = call2.callPlan(draft, { poster: shortPoster, checkerLegBase: (terms) => checker.fee ? checker.fee.base : perCheck ? 0 : checkerLegBase2(FEE_DENOM, terms, CHECK_REWARD), oracle: checker.address || "the verifier", share: priced.share, feeInReward: Boolean(checker.fee) || coin === FEE_DENOM, sponsor });
296333
296438
  const feeTotal = plan.legs.reduce((sum, one) => sum + one.feeBase, 0);
296334
296439
  const finalOk = draft.fields.kind === "review" ? acting?.principal ?? null : null;
296335
- const preview = call.legTerms(draft, "entries", { poster: shortPoster, dates: plan.dates, oracle: { kind: "oracle", address: checker.address || "the verifier" }, approver: finalOk, share: plan.share, sponsor });
296336
- const twoCoins = acting && feeTotal > 0 ? `Can't post as ${acting.principal}: ${call.callTwoCoins}.` : null;
296440
+ const preview = call2.legTerms(draft, "entries", { poster: shortPoster, dates: plan.dates, oracle: { kind: "oracle", address: checker.address || "the verifier" }, approver: finalOk, share: plan.share, sponsor });
296441
+ const twoCoins = acting && feeTotal > 0 ? `Can't post as ${acting.principal}: ${call2.callTwoCoins}.` : null;
296337
296442
  const { capCoinRefusal: capCoinRefusal2, capIn: capIn2 } = await Promise.resolve().then(() => (init_hq(), hq_exports));
296338
296443
  const otherCoin = capCoinRefusal2(acting, coin, checker.fee ? `${SITE}/connect` : void 0);
296339
- const capped3 = call.capWords(plan.legs, CAP_BASE, coinSymbol, NETWORK2);
296444
+ const capped3 = call2.capWords(plan.legs, CAP_BASE, coinSymbol, NETWORK2);
296340
296445
  const keyOnly = !acting && feeTotal > 0;
296341
296446
  const blocked = capped3.length > 0 || Boolean(otherCoin) || Boolean(twoCoins) && draft.fields.kind !== "review" || Boolean(sponsorProblem && !sponsorProblem.unavailable);
296342
296447
  const m = draft.fields.money;
@@ -296357,15 +296462,15 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
296357
296462
  sponsorProblem ? sponsorProblem.unavailable ? `${sponsorProblem.refused} post_call confirms the marketplace again before anything moves.` : `Can't post as it stands: ${sponsorProblem.refused}` : "",
296358
296463
  draft.fields.access === "invite" && !draft.fields.invite?.length ? proAsk ? `Nobody invited yet: find_pro${draft.fields.sponsor ? ` with sponsor: "${draft.fields.sponsor}"` : ""} suggests pros from ${sponsor ? `${sponsor.name}'s` : "the marketplace's"} roster; add them with invite now, or invite_to_call after it posts.` : 'By invitation, and nobody invited yet: add invite (addresses, @handles, "link") now, or invite_to_call after it posts.' : "",
296359
296464
  draft.fields.access === "apply" ? "By application: people apply on the call's page or with apply_to_call; you say yes with decide_application before they can enter." : "",
296360
- ...call.balanceWarnings(held, { totalBase: plan.totalBase, feeTotal, symbol: coinSymbol, network: NETWORK2, feeWords }),
296465
+ ...call2.balanceWarnings(held, { totalBase: plan.totalBase, feeTotal, symbol: coinSymbol, network: NETWORK2, feeWords }),
296361
296466
  acting?.cap && !otherCoin && Number(capIn2(acting, coin)?.remainingBase ?? 0) < plan.totalBase ? `Their cap has ${pays(Number(capIn2(acting, coin)?.remainingBase ?? 0), coinSymbol)} left; the call locks ${pays(plan.totalBase, coinSymbol)}. They raise it at ${SITE}/connect.` : "",
296362
- keyOnly ? `Not connected, and a connection would not carry this call: ${call.callTwoCoins}. With plain_key: true it posts as this key itself, ${await address2()}, from its own money.` : !acting ? `Not connected: post_call refuses until the person connects this key (connect_account gives them the link). For a rehearsal, plain_key: true posts as this key itself, ${await address2()}, from its own money.` : ""
296467
+ keyOnly ? `Not connected, and a connection would not carry this call: ${call2.callTwoCoins}. With plain_key: true it posts as this key itself, ${await address2()}, from its own money.` : !acting ? `Not connected: post_call refuses until the person connects this key (connect_account gives them the link). For a rehearsal, plain_key: true posts as this key itself, ${await address2()}, from its own money.` : ""
296363
296468
  ].filter(Boolean);
296364
296469
  out.push(
296365
296470
  "",
296366
296471
  blocked ? "Drafted, but post_call would refuse it as it stands (the warnings say why). This is the call as an entrant's AI will read it:" : "Ready. This is the call as an entrant's AI will read it:",
296367
296472
  "",
296368
- call.callReadout(preview, { id: "(the entries ask, once posted)", symbol: coinSymbol, network: NETWORK2 }),
296473
+ call2.callReadout(preview, { id: "(the entries ask, once posted)", symbol: coinSymbol, network: NETWORK2 }),
296369
296474
  "",
296370
296475
  "What each leg locks:",
296371
296476
  ...legRows,
@@ -296396,21 +296501,23 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
296396
296501
  inputSchema: {
296397
296502
  slug: external_exports.string().max(40).describe("The call's name, as draft_call gave it."),
296398
296503
  confirm: external_exports.string().optional().describe("The exact total from the preview, e.g. '$4.70'. Omit to preview."),
296504
+ pay_by: external_exports.enum(["card"]).optional().describe('"card": spend nothing from this key. The whole call is saved and you get a link where the person pays by card and posts it from their own account.'),
296399
296505
  plain_key: external_exports.boolean().optional().describe("Post as this key itself, from its own money. Without a connection: a rehearsal. With one, only a review honours it, and only after the person said yes to this: the review posts from your own account (this key), which they fund, not from their account, and the AI decides each report alone, with no final OK from them. Any other call posts as the person who connected the key, and without either it is refused.")
296400
296506
  }
296401
296507
  },
296402
- async ({ slug, confirm, plain_key }) => {
296403
- const call = await Promise.resolve().then(() => (init_call2(), call_exports2));
296404
- const found = call.readDraft(slug);
296508
+ async ({ slug, confirm, plain_key, pay_by }) => {
296509
+ const card = pay_by === "card";
296510
+ const call2 = await Promise.resolve().then(() => (init_call2(), call_exports2));
296511
+ const found = call2.readDraft(slug);
296405
296512
  if (!found) return text4(`No draft called "${slug}". Start one with draft_call.`);
296406
296513
  const review = found.fields.kind === "review";
296407
296514
  const checker = await callChecker2(review);
296408
296515
  const linkOf = (posted2) => review ? `${EXPERT_SITE}/pro/reviews/${posted2.entries?.id ?? slug}` : `${SITE}/opencall/${slug}`;
296409
- const settled = found.inFlight ? await call.settleInFlight(slug, call.indexLookup) : null;
296516
+ const settled = found.inFlight ? await call2.settleInFlight(slug, call2.indexLookup) : null;
296410
296517
  if (settled?.state === "pending") return text4(settled.words);
296411
296518
  if (settled?.state === "landed") {
296412
296519
  nudge("hire", settled.id, checker.at);
296413
- const after2 = call.nextLeg(settled.draft);
296520
+ const after2 = call2.nextLeg(settled.draft);
296414
296521
  return text4(
296415
296522
  [
296416
296523
  `The earlier post landed: ${LEG_WORDS[settled.leg]} is #${settled.id}. It is recorded, and nothing was posted twice.`,
@@ -296419,23 +296526,23 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
296419
296526
  );
296420
296527
  }
296421
296528
  const draft = settled?.draft ?? found;
296422
- const coin = call.callCoin(draft, NETWORK2, ASK_DENOM);
296529
+ const coin = call2.callCoin(draft, NETWORK2, ASK_DENOM);
296423
296530
  if (typeof coin !== "string") return text4(`Refused: ${coin.refused}. Nothing was posted.`);
296424
296531
  const coinSymbol = symbolOf(coin);
296425
- const next = call.nextLeg(draft);
296532
+ const next = call2.nextLeg(draft);
296426
296533
  const ids = ["prize", "runnersUp", "entries"].flatMap((leg2) => draft.posted[leg2] ? [`${LEG_WORDS[leg2]} #${draft.posted[leg2].id}`] : []);
296427
296534
  if (!next) return text4(`Everything is posted: ${ids.join(", ")}.
296428
296535
  The call: ${linkOf(draft.posted)}`);
296429
- if (review && !draft.fields.sponsor) return text4(`${call.PRO_ASK_NEEDS_MARKETPLACE} Nothing was posted.`);
296430
- if (!call.isReady(draft.fields)) return text4(`"${slug}" is not ready: ${call.missingOf(draft.fields, { symbol: coinSymbol, holds: null }).map((one) => one.field).join(", ")} still missing. Finish it with draft_call.`);
296536
+ if (review && !draft.fields.sponsor) return text4(`${call2.PRO_ASK_NEEDS_MARKETPLACE} Nothing was posted.`);
296537
+ if (!call2.isReady(draft.fields)) return text4(`"${slug}" is not ready: ${call2.missingOf(draft.fields, { symbol: coinSymbol, holds: null }).map((one) => one.field).join(", ")} still missing. Finish it with draft_call.`);
296431
296538
  const cannot = await unjudged();
296432
296539
  if (cannot) return text4(cannot);
296433
296540
  if (!checker.address) return text4("No verifier is configured on this deployment, so nothing could judge the entries. Refusing rather than posting a call nobody can settle.");
296434
296541
  const marketplace = review ? await (await Promise.resolve().then(() => (init_marketplaces(), marketplaces_exports))).resolveMarketplace(SITE, draft.fields.sponsor, NETWORK2, fetch2) : null;
296435
296542
  if (marketplace && "refused" in marketplace) return text4(`Refused: ${marketplace.refused} Nothing was posted.`);
296436
296543
  const sponsor = marketplace?.sponsor ?? null;
296437
- const acting = plain_key && review ? null : await principal2();
296438
- if (!acting && !plain_key) {
296544
+ const acting = card || plain_key && review ? null : await principal2();
296545
+ if (!acting && !plain_key && !card) {
296439
296546
  return text4(`Not connected. A call uses the connected account as its poster and funding source. Ask them to connect this key first:
296440
296547
  ${connectLink2(review ? `${EXPERT_SITE}/pro` : SITE, await address2())}
296441
296548
  Then call post_call again. Nothing was posted.`);
@@ -296444,15 +296551,15 @@ Then call post_call again. Nothing was posted.`);
296444
296551
  const poster = acting?.principal ?? await address2();
296445
296552
  if (checker.fee && checker.fee.denom !== coin) return text4(`Refused: ${checker.name} is paid in another coin than this call pays in, and a review pays in its verifier's coin. Start a new review. Nothing was posted.`);
296446
296553
  const perCheck = checker.fee ? false : await callPerCheck(checker.address);
296447
- const split3 = call.callShares(draft.fields) ? await callSplit() : null;
296448
- const priced = call.callShare(draft.fields, split3);
296449
- if ("unread" in priced) return text4(call.SHARE_UNREAD);
296450
- const plan = call.callPlan(draft, { poster: `${poster.slice(0, 10)}\u2026${poster.slice(-4)}`, checkerLegBase: (terms) => checker.fee ? checker.fee.base : perCheck ? 0 : checkerLegBase2(FEE_DENOM, terms, CHECK_REWARD), oracle: checker.address, share: priced.share, feeInReward: Boolean(checker.fee) || coin === FEE_DENOM, sponsor });
296554
+ const split3 = call2.callShares(draft.fields) ? await callSplit() : null;
296555
+ const priced = call2.callShare(draft.fields, split3);
296556
+ if ("unread" in priced) return text4(call2.SHARE_UNREAD);
296557
+ const plan = call2.callPlan(draft, { poster: `${poster.slice(0, 10)}\u2026${poster.slice(-4)}`, checkerLegBase: (terms) => checker.fee ? checker.fee.base : perCheck ? 0 : checkerLegBase2(FEE_DENOM, terms, CHECK_REWARD), oracle: checker.address, share: priced.share, feeInReward: Boolean(checker.fee) || coin === FEE_DENOM, sponsor });
296451
296558
  const feeTotal = plan.legs.reduce((sum, one) => sum + one.feeBase, 0);
296452
296559
  const total = pays(plan.totalBase, coinSymbol);
296453
296560
  const { feeLine: feeLine2 } = await Promise.resolve().then(() => (init_feeLine(), feeLine_exports));
296454
296561
  const moneySentence = sponsor && plan.share ? feeLine2({ split: plan.share, sponsorName: sponsor.name, net: draft.fields.money.entryEach, symbol: coinSymbol, network: NETWORK2 }) : null;
296455
- if (acting && feeTotal > 0) return text4(`Refused: ${call.callTwoCoins}.${review ? ` ${PLAIN_REVIEW}` : ""} Nothing was posted.`);
296562
+ if (acting && feeTotal > 0) return text4(`Refused: ${call2.callTwoCoins}.${review ? ` ${PLAIN_REVIEW}` : ""} Nothing was posted.`);
296456
296563
  const approver = review && acting ? acting.principal : null;
296457
296564
  if (approver) {
296458
296565
  const notReady = await approverRefusal(approver, async (who) => (await Promise.resolve().then(() => (init_personReview(), personReview_exports))).approverReady(who, { self: true }));
@@ -296460,6 +296567,19 @@ Then call post_call again. Nothing was posted.`);
296460
296567
  }
296461
296568
  const leg = plan.legs.find((one) => one.leg === next);
296462
296569
  const order2 = plan.legs.map((one) => one.leg);
296570
+ if (card) {
296571
+ if (coin !== FEE_DENOM) return text4(`Refused: a card pays in ${FEE_SYMBOL}, and this call pays in ${coinSymbol}. Nothing was posted.`);
296572
+ if (ids.length) return text4(`Refused: part of "${slug}" is posted already (${ids.join(", ")}); finish it with post_call. Nothing was posted.`);
296573
+ const payDraft = await Promise.resolve().then(() => (init_payDraft(), payDraft_exports));
296574
+ const rewardBase = plan.legs.reduce((sum, one) => sum + one.payBase * one.escrowSpots, 0);
296575
+ const quote = payDraft.quoteOf(rewardBase, plan.totalBase + feeTotal - rewardBase);
296576
+ const words5 = payDraft.cardPreview(quote);
296577
+ if (typeof words5 !== "string") return text4(`Refused: ${words5.refused}. Nothing was posted.`);
296578
+ if (!confirm) return text4([`Call "${slug}" is ${order2.length} asks: ${order2.map((one) => LEG_WORDS[one]).join(", then ")}.`, ...moneySentence ? [moneySentence] : [], words5].join("\n"));
296579
+ if (confirm.trim() !== payDraft.confirmToken(quote)) return text4(`Refused: confirm said "${confirm}" but the person pays ${payDraft.confirmToken(quote)}. Preview it again.`);
296580
+ const terms = { slug, fields: draft.fields, dates: plan.dates, legs: plan.legs, checker: checker.address, perCheck, ...sponsor ? { sponsor } : {} };
296581
+ return text4(await payDraft.saveDraft(SITE, { kind: "call", terms, quote, network: NETWORK2 }, fetch2));
296582
+ }
296463
296583
  if (!confirm) {
296464
296584
  const { shareWords: shareWords2 } = await Promise.resolve().then(() => (init_callRead(), callRead_exports));
296465
296585
  const shareTotal = plan.legs.reduce((sum, one) => sum + one.shareBase * one.escrowSpots, 0);
@@ -296472,21 +296592,21 @@ Then call post_call again. Nothing was posted.`);
296472
296592
  `Next: ${LEG_WORDS[next]}, locking ${callLocks(leg.escrowBase, leg.feeBase, coinSymbol)}, plus a small network fee.`,
296473
296593
  actingNote(acting) ?? (review ? "Posting from your own account (this key), not the person's account; the AI decides each report alone, with no final OK from the person." : `Posting as this key, ${poster}.`),
296474
296594
  `Spent today (call): ${spentWords("call", coinSymbol)}. Every tool together: ${spentAllWords()}.`,
296475
- ...call.capWords(plan.legs.filter((one) => !draft.posted[one.leg]), CAP_BASE, coinSymbol, NETWORK2),
296476
- ...call.balanceWarnings(held2, { totalBase: plan.totalBase, feeTotal, symbol: coinSymbol, network: NETWORK2, feeWords }),
296595
+ ...call2.capWords(plan.legs.filter((one) => !draft.posted[one.leg]), CAP_BASE, coinSymbol, NETWORK2),
296596
+ ...call2.balanceWarnings(held2, { totalBase: plan.totalBase, feeTotal, symbol: coinSymbol, network: NETWORK2, feeWords }),
296477
296597
  "",
296478
296598
  `To go ahead, call again with confirm: "${total}".`
296479
296599
  ].join("\n")
296480
296600
  );
296481
296601
  }
296482
- if (!call.confirms(confirm, plan.totalBase, coinSymbol)) return text4(`Refused: confirm said "${confirm}" but the call locks ${total}. Preview it again.`);
296483
- if (call.overCap(leg, CAP_BASE)) return text4(`Refused: this leg locks ${pays(leg.escrowBase, coinSymbol)}, over this server's ${face(MAX_PER_ASK, coinSymbol)} per-ask cap (OPENHANDS_MAX_PER_ASK_USD).`);
296602
+ if (!call2.confirms(confirm, plan.totalBase, coinSymbol)) return text4(`Refused: confirm said "${confirm}" but the call locks ${total}. Preview it again.`);
296603
+ if (call2.overCap(leg, CAP_BASE)) return text4(`Refused: this leg locks ${pays(leg.escrowBase, coinSymbol)}, over this server's ${face(MAX_PER_ASK, coinSymbol)} per-ask cap (OPENHANDS_MAX_PER_ASK_USD).`);
296484
296604
  if (ids.length === 0) {
296485
- const taken = await fetch2(`${SITE}/api/board?campaign=${encodeURIComponent(call.tagOf(draft))}&size=5`, { signal: AbortSignal.timeout(15e3) }).then((response) => response.ok ? response.json() : null).catch(() => null);
296605
+ const taken = await fetch2(`${SITE}/api/board?campaign=${encodeURIComponent(call2.tagOf(draft))}&size=5`, { signal: AbortSignal.timeout(15e3) }).then((response) => response.ok ? response.json() : null).catch(() => null);
296486
296606
  if (taken?.rows?.length) return text4(`Refused: "${slug}" already names a call with open asks on this board. Draft it under another slug with draft_call. Nothing was posted.`);
296487
296607
  }
296488
296608
  const { balancesOfStrict: balancesOfStrict2 } = await Promise.resolve().then(() => (init_balance(), balance_exports));
296489
- const refusedBefore = await call.preCheck(leg, {
296609
+ const refusedBefore = await call2.preCheck(leg, {
296490
296610
  acting,
296491
296611
  poster,
296492
296612
  askDenom: coin,
@@ -296503,7 +296623,7 @@ Then call post_call again. Nothing was posted.`);
296503
296623
  const key = await loadKey2();
296504
296624
  let posted;
296505
296625
  try {
296506
- posted = await call.postNextLeg(slug, {
296626
+ posted = await call2.postNextLeg(slug, {
296507
296627
  site: SITE,
296508
296628
  askDenom: coin,
296509
296629
  checker: checker.address,
@@ -296520,7 +296640,7 @@ Then call post_call again. Nothing was posted.`);
296520
296640
  sponsor
296521
296641
  });
296522
296642
  } catch (error54) {
296523
- if (error54 instanceof call.InFlightError || error54 instanceof call.PostRefused) return text4(error54.message);
296643
+ if (error54 instanceof call2.InFlightError || error54 instanceof call2.PostRefused) return text4(error54.message);
296524
296644
  if (isUnanchored(error54)) return text4(unanchoredWords(error54));
296525
296645
  if (error54 instanceof Error && error54.name === "TxFailed") {
296526
296646
  return text4(`The network refused ${LEG_WORDS[next]}: ${error54.message}. Nothing moved. Fix that (often the account's balance or cap) and call post_call again.`);
@@ -296532,7 +296652,7 @@ Then call post_call again. Nothing was posted.`);
296532
296652
  if ("done" in posted) return text4(`Everything is posted.
296533
296653
  The call: ${linkOf(posted.draft.posted)}`);
296534
296654
  const done = order2.filter((one) => posted.draft.posted[one]).length;
296535
- const after = call.nextLeg(posted.draft);
296655
+ const after = call2.nextLeg(posted.draft);
296536
296656
  const hire = posted.draft.fields.mode === "hire";
296537
296657
  const held = posted.screen?.verdict === "hold" ? heldWords(posted.screen.reason) : null;
296538
296658
  const invited = !after && posted.draft.fields.access && posted.draft.fields.access !== "public" ? await writeDraftInvites(posted.draft, posted.id, acting) : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ixo/asks-mcp",
3
- "version": "1.6.0-dev.6",
3
+ "version": "1.6.0",
4
4
  "description": "The Openhands Network MCP server: read the board, answer asks, post work and get paid on the ixo chain, with a key of its own on your machine.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {