@ixo/asks-mcp 1.6.0-dev.3 → 1.6.0-dev.5

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/README.md CHANGED
@@ -87,6 +87,8 @@ naming what is missing; nothing is posted and nothing is spent. `my_status` and
87
87
 
88
88
  On first run the server makes a key in `$OPENHANDS_HOME/<network>/key.json` (by default `~/.openhands/testnet/key.json`), readable only by you. The server refuses to use it if anyone else can read the file, anyone else can write to its directory, or either is a symlink. The mnemonic never leaves the server: no tool returns it and nothing logs it. The spend ledger (`spend.json`), the account it acts for (`principal.json`) and open-call drafts (`calls/`) sit beside it.
89
89
 
90
+ On Windows the server verifies the actual ACL using Windows PowerShell, because Node's POSIX mode bits cannot express Windows permissions. The current Windows user must have Full control; only that user, SYSTEM and the built-in Administrators group may have access. Inherited private permissions are accepted. The server does not rewrite Windows ACLs, and refuses to read a key when it cannot verify them. For a refusal, check the directory and file under **Properties > Security**, or choose an `OPENHANDS_HOME` that already has private permissions. No permission-check bypass is available.
91
+
90
92
  Spending tools preview first and need the exact total confirmed; daily caps are counted on disk, so restarting the client does not reset them. Earnings go only to `OPENHANDS_HOME_ADDRESS`.
91
93
 
92
94
  ## The hosted server instead
package/dist/asks-mcp.mjs CHANGED
@@ -7409,6 +7409,16 @@ var init_given = __esm({
7409
7409
  }
7410
7410
  });
7411
7411
 
7412
+ // ../../packages/openhands-sdk/src/chain/rewardDenom.ts
7413
+ var USDC_MAINNET, rewardDenomFor;
7414
+ var init_rewardDenom = __esm({
7415
+ "../../packages/openhands-sdk/src/chain/rewardDenom.ts"() {
7416
+ "use strict";
7417
+ USDC_MAINNET = "ibc/77C12480BD8C29BF0ACEC2143736C15BAF8CD232CA27BAE09336677B828955E6";
7418
+ rewardDenomFor = (network2, configured) => network2 === "mainnet" && configured?.trim() !== "upay" ? USDC_MAINNET : "upay";
7419
+ }
7420
+ });
7421
+
7412
7422
  // ../../packages/openhands-sdk/src/chain/money.ts
7413
7423
  var money_exports = {};
7414
7424
  __export(money_exports, {
@@ -7451,12 +7461,12 @@ function orgShortfall(org, reward, fee, held) {
7451
7461
  const holds = [...need.keys()].map((denom) => `${face(Number(held[denom] ?? 0))} ${symbolOf(denom)}`).join(" and ");
7452
7462
  return `${org} needs ${[rewards, reviews].filter(Boolean).join(" and ")}; it holds ${holds}`;
7453
7463
  }
7454
- var USDC_MAINNET, USDC_NOBLE_LEGACY, asRewardDenom, usdcDenom, envDenom, currentNetwork, askDenom, heldDenom, rewardDenom, checkerFeeDenom, symbolOf, MICRO, toBase, isPegged;
7464
+ var USDC_NOBLE_LEGACY, asRewardDenom, usdcDenom, envDenom, currentNetwork, askDenom, heldDenom, rewardDenom, checkerFeeDenom, symbolOf, MICRO, toBase, isPegged;
7455
7465
  var init_money = __esm({
7456
7466
  "../../packages/openhands-sdk/src/chain/money.ts"() {
7457
7467
  "use strict";
7458
7468
  init_given();
7459
- USDC_MAINNET = "ibc/77C12480BD8C29BF0ACEC2143736C15BAF8CD232CA27BAE09336677B828955E6";
7469
+ init_rewardDenom();
7460
7470
  USDC_NOBLE_LEGACY = "ibc/6BBE9BD4246F8E04948D5A4EEE7164B2630263B9EBB5E7DC5F0A46C62A2FF97B";
7461
7471
  asRewardDenom = (_value, network2) => rewardDenom(network2);
7462
7472
  usdcDenom = (network2) => network2 === "mainnet" ? USDC_MAINNET : null;
@@ -7467,7 +7477,7 @@ var init_money = __esm({
7467
7477
  const env = envDenom();
7468
7478
  return env === "upay" || env === USDC_MAINNET ? env : "uixo";
7469
7479
  };
7470
- rewardDenom = (network2) => network2 === "mainnet" ? USDC_MAINNET : "upay";
7480
+ rewardDenom = (network2) => rewardDenomFor(network2, envDenom());
7471
7481
  checkerFeeDenom = (_network) => "upay";
7472
7482
  symbolOf = (denom) => denom === "upay" ? "PAY" : denom === USDC_MAINNET ? "USDC" : denom === USDC_NOBLE_LEGACY ? "USDC (old)" : "IXO";
7473
7483
  MICRO = 1e6;
@@ -7864,9 +7874,10 @@ function money(base6, denom, viewer) {
7864
7874
  return { face, local: localOf(exact, viewer), approx: false, title: `${face} in US dollars` };
7865
7875
  }
7866
7876
  if (denom === "uixo" && viewer.sandbox) return { face: dollarsOfBase(exact), local: null, approx: true, title: "sandbox amount" };
7877
+ if (denom === "uixo") return { face: ixoWords(Number(exact) / 1e6), local: null, approx: false, title: IXO_TITLE };
7867
7878
  return null;
7868
7879
  }
7869
- var USDC_MAINNET2, USDC_NOBLE_LEGACY2, DOLLAR_DENOMS, MICRO2, placesFor;
7880
+ var USDC_MAINNET2, USDC_NOBLE_LEGACY2, DOLLAR_DENOMS, MICRO2, placesFor, ixoWords, IXO_TITLE;
7870
7881
  var init_money2 = __esm({
7871
7882
  "../../packages/openhands-sdk/src/data/money.ts"() {
7872
7883
  "use strict";
@@ -7875,6 +7886,8 @@ var init_money2 = __esm({
7875
7886
  DOLLAR_DENOMS = /* @__PURE__ */ new Set(["upay", USDC_MAINNET2, USDC_NOBLE_LEGACY2]);
7876
7887
  MICRO2 = 1000000n;
7877
7888
  placesFor = (value) => value === 0 ? 2 : Math.abs(value) < 1e-4 ? 6 : Math.abs(value) < 1 ? 4 : 2;
7889
+ ixoWords = (value) => `${value.toLocaleString("en-US", { maximumFractionDigits: placesFor(value) })} IXO`;
7890
+ IXO_TITLE = "IXO, the network's own coin, not a dollar coin";
7878
7891
  }
7879
7892
  });
7880
7893
 
@@ -8058,7 +8071,14 @@ function reportProblems(raw, requirement, fileIds = []) {
8058
8071
  }
8059
8072
  function readReport(envelope) {
8060
8073
  const doc = obj(envelope);
8061
- const report = obj(doc?.report) ?? (doc?.kind === REPORT_KIND ? doc : null);
8074
+ let report = obj(doc?.report) ?? (doc?.kind === REPORT_KIND ? doc : null);
8075
+ if (!report && typeof doc?.deliverable === "string" && doc.deliverable.length <= REPORT_LIMITS.bytes) {
8076
+ try {
8077
+ const pasted = obj(JSON.parse(doc.deliverable));
8078
+ if (pasted?.kind === REPORT_KIND && obj(pasted.summary)) report = pasted;
8079
+ } catch {
8080
+ }
8081
+ }
8062
8082
  if (!report) return null;
8063
8083
  const summary = obj(report.summary) ?? {};
8064
8084
  const read9 = {
@@ -8090,6 +8110,16 @@ function readReport(envelope) {
8090
8110
  sections: Object.entries(obj(report.sections) ?? {}).map(([title, value]) => ({ title, text: String(value) }))
8091
8111
  };
8092
8112
  const files = (Array.isArray(doc?.files) ? doc.files : []).map(obj).filter((one) => one !== null && typeof one.id === "string" && typeof one.url === "string" && STORED.test(one.url)).slice(0, 24).map((one) => ({ id: String(one.id), url: String(one.url), sha256: String(one.sha256 ?? ""), mediaType: String(one.mediaType ?? "application/octet-stream"), name: String(one.name ?? one.id) }));
8113
+ for (const field2 of Object.values(doc ?? {})) {
8114
+ if (!Array.isArray(field2)) continue;
8115
+ for (const value of field2) {
8116
+ const file3 = obj(value);
8117
+ if (!file3 || !isStoredFile(file3.content) || files.some((one) => one.url === file3.content) || files.length >= 24) continue;
8118
+ let id = `attachment-${files.length + 1}`;
8119
+ while (files.some((one) => one.id === id)) id += "-file";
8120
+ files.push({ id, url: file3.content, sha256: "", mediaType: str(file3.type) ?? "application/octet-stream", name: str(file3.name) ?? "Attachment" });
8121
+ }
8122
+ }
8093
8123
  return { doc: read9, files };
8094
8124
  }
8095
8125
  function formAnswersOf(envelope) {
@@ -30166,6 +30196,7 @@ function faceOf(amount3, sandbox = envSandbox()) {
30166
30196
  const { value, symbol: symbol2 } = amount3;
30167
30197
  if (PEGGED.has(symbol2)) return { value, prefix: "$", title: `${dollars2(value)} in US dollars`, text: dollars2(value) };
30168
30198
  if (symbol2 === "IXO" && sandbox) return { value, prefix: "$", title: "sandbox amount", text: dollars2(value), approx: true };
30199
+ if (symbol2 === "IXO") return { value, title: "IXO, the network's own coin, not a dollar coin", text: `${value.toLocaleString("en-US", { maximumFractionDigits: fractionFor(value) })} IXO` };
30169
30200
  return { value, title: "Paid in the network's own coin: the public record has the amount", text: UNFACED2, unfaced: true };
30170
30201
  }
30171
30202
  function approvalAmount(payments) {
@@ -30395,6 +30426,73 @@ var init_site = __esm({
30395
30426
  }
30396
30427
  });
30397
30428
 
30429
+ // src/keyPermissions.ts
30430
+ import { spawnSync } from "node:child_process";
30431
+ import { lstatSync } from "node:fs";
30432
+ import { win32 } from "node:path";
30433
+ function assertWindowsPrivatePath(path, run2 = spawnSync) {
30434
+ const root = process.env.SystemRoot;
30435
+ if (!root || !win32.isAbsolute(root)) throw new Error(`Cannot verify Windows permissions for ${path}: SystemRoot is unavailable. No key was read.`);
30436
+ const powershellHome = win32.join(root, "System32", "WindowsPowerShell", "v1.0");
30437
+ const env = { ...process.env };
30438
+ for (const name8 of Object.keys(env)) if (/^(psmodulepath|openhands_acl_path)$/i.test(name8)) delete env[name8];
30439
+ const result = run2(win32.join(powershellHome, "powershell.exe"), [
30440
+ "-NoLogo",
30441
+ "-NoProfile",
30442
+ "-NonInteractive",
30443
+ "-EncodedCommand",
30444
+ Buffer.from(CHECK_ACL, "utf16le").toString("base64")
30445
+ ], {
30446
+ env: { ...env, PSModulePath: win32.join(powershellHome, "Modules"), OPENHANDS_ACL_PATH: path },
30447
+ timeout: 3e4,
30448
+ windowsHide: true,
30449
+ stdio: "ignore"
30450
+ });
30451
+ if (result.error || result.signal || result.status !== 0) {
30452
+ const why = result.error?.code === "ETIMEDOUT" ? "inspection timed out" : `inspection exit ${result.status ?? "unavailable"}`;
30453
+ throw new Error(`Cannot verify private Windows permissions for ${path} (${why}). Only your Windows account, SYSTEM and Administrators may have access, and your account must have Full control. Check Properties > Security, or choose a private OPENHANDS_HOME. No key was read.`);
30454
+ }
30455
+ }
30456
+ function assertSafe(path, what, platform = process.platform, run2) {
30457
+ const stat = lstatSync(path);
30458
+ if (stat.isSymbolicLink()) throw new Error(`${path} is a symlink. A key that lives where another program decides is not a key we will use.`);
30459
+ if (what === "file" ? !stat.isFile() : !stat.isDirectory()) throw new Error(`${path} is not a ${what}.`);
30460
+ if (platform === "win32") {
30461
+ assertWindowsPrivatePath(path, run2);
30462
+ return;
30463
+ }
30464
+ const mode = stat.mode & 511;
30465
+ const tooOpen = what === "file" ? mode & 63 : mode & 18;
30466
+ if (tooOpen) throw new Error(`${path} is readable or writable by others (mode ${mode.toString(8)}). Run: chmod ${what === "file" ? "600" : "700"} ${path}`);
30467
+ }
30468
+ var CHECK_ACL;
30469
+ var init_keyPermissions = __esm({
30470
+ "src/keyPermissions.ts"() {
30471
+ "use strict";
30472
+ CHECK_ACL = String.raw`
30473
+ $ErrorActionPreference = 'Stop'
30474
+ try {
30475
+ $item = Get-Item -LiteralPath $env:OPENHANDS_ACL_PATH -Force
30476
+ if (($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) { exit 2 }
30477
+ $acl = Get-Acl -LiteralPath $env:OPENHANDS_ACL_PATH
30478
+ $me = [Security.Principal.WindowsIdentity]::GetCurrent().User.Value
30479
+ $trusted = @($me, 'S-1-5-18', 'S-1-5-32-544')
30480
+ if ($trusted -notcontains $acl.GetOwner([Security.Principal.SecurityIdentifier]).Value) { exit 3 }
30481
+ $mine = $false
30482
+ foreach ($rule in $acl.GetAccessRules($true, $true, [Security.Principal.SecurityIdentifier])) {
30483
+ if (($rule.PropagationFlags -band [Security.AccessControl.PropagationFlags]::InheritOnly) -ne 0) { continue }
30484
+ if ($rule.AccessControlType -ne [Security.AccessControl.AccessControlType]::Allow) { continue }
30485
+ if ($trusted -notcontains $rule.IdentityReference.Value) { exit 3 }
30486
+ if ($rule.IdentityReference.Value -eq $me -and
30487
+ ($rule.FileSystemRights -band [Security.AccessControl.FileSystemRights]::FullControl) -eq [Security.AccessControl.FileSystemRights]::FullControl) { $mine = $true }
30488
+ }
30489
+ if (-not $mine) { exit 3 }
30490
+ exit 0
30491
+ } catch { exit 4 }
30492
+ `;
30493
+ }
30494
+ });
30495
+
30398
30496
  // ../../node_modules/.pnpm/cosmjs-types@0.11.0/node_modules/cosmjs-types/utf8.js
30399
30497
  var require_utf82 = __commonJS({
30400
30498
  "../../node_modules/.pnpm/cosmjs-types@0.11.0/node_modules/cosmjs-types/utf8.js"(exports) {
@@ -49621,26 +49719,14 @@ __export(keys_exports, {
49621
49719
  signer: () => signer,
49622
49720
  writePin: () => writePin
49623
49721
  });
49624
- import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
49722
+ import { chmodSync, existsSync, lstatSync as lstatSync2, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
49625
49723
  import { homedir } from "node:os";
49626
49724
  import { join as join3 } from "node:path";
49627
- function assertSafe(path, what) {
49628
- const stat = lstatSync(path);
49629
- if (stat.isSymbolicLink()) {
49630
- throw new Error(`${path} is a symlink. A key that lives where another program decides is not a key we will use.`);
49631
- }
49632
- const mode = stat.mode & 511;
49633
- const tooOpen = what === "file" ? mode & 63 : mode & 18;
49634
- if (tooOpen) {
49635
- throw new Error(
49636
- `${path} is readable or writable by others (mode ${mode.toString(8)}). Run: chmod ${what === "file" ? "600" : "700"} ${path}`
49637
- );
49638
- }
49639
- }
49640
49725
  function ensureDir() {
49641
49726
  const dir = DIR();
49642
49727
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 448 });
49643
- chmodSync(dir, 448);
49728
+ if (lstatSync2(dir).isSymbolicLink()) throw new Error(`${dir} is a symlink.`);
49729
+ if (process.platform !== "win32") chmodSync(dir, 448);
49644
49730
  assertSafe(dir, "dir");
49645
49731
  }
49646
49732
  async function loadKey() {
@@ -49667,7 +49753,7 @@ async function loadKey() {
49667
49753
  const [account] = await wallet.getAccounts();
49668
49754
  const made = { mnemonic: wallet.mnemonic, address: account.address };
49669
49755
  writeFileSync(KEY_FILE2, JSON.stringify(made, null, 2), { mode: 384 });
49670
- chmodSync(KEY_FILE2, 384);
49756
+ if (process.platform !== "win32") chmodSync(KEY_FILE2, 384);
49671
49757
  assertSafe(KEY_FILE2, "file");
49672
49758
  cached2.set(KEY_FILE2, made);
49673
49759
  return made;
@@ -49691,6 +49777,10 @@ async function signer() {
49691
49777
  function readPin() {
49692
49778
  const PIN_FILE = pinFile();
49693
49779
  if (!existsSync(PIN_FILE)) return null;
49780
+ const { ino, mtimeMs, ctimeMs } = lstatSync2(PIN_FILE);
49781
+ const stamp = `${ino}:${mtimeMs}:${ctimeMs}`;
49782
+ const seen2 = pins.get(PIN_FILE);
49783
+ if (seen2?.stamp === stamp) return seen2.pin;
49694
49784
  assertSafe(PIN_FILE, "file");
49695
49785
  let parsed;
49696
49786
  try {
@@ -49701,7 +49791,9 @@ function readPin() {
49701
49791
  if (typeof parsed.principal !== "string" || !PRINCIPAL.test(parsed.principal) || typeof parsed.agent !== "string") {
49702
49792
  throw new Error(`${PIN_FILE} does not name an account. Remove it and call connect_account again.`);
49703
49793
  }
49704
- return { principal: parsed.principal, agent: parsed.agent, pinnedAt: typeof parsed.pinnedAt === "string" ? parsed.pinnedAt : "", ...parsed.signed === true ? { signed: true } : {} };
49794
+ const pin = { principal: parsed.principal, agent: parsed.agent, pinnedAt: typeof parsed.pinnedAt === "string" ? parsed.pinnedAt : "", ...parsed.signed === true ? { signed: true } : {} };
49795
+ pins.set(PIN_FILE, { stamp, pin });
49796
+ return pin;
49705
49797
  }
49706
49798
  function writePin(pin) {
49707
49799
  if (!PRINCIPAL.test(pin.principal)) throw new Error("that is not an ixo account address");
@@ -49709,17 +49801,18 @@ function writePin(pin) {
49709
49801
  const PIN_FILE = pinFile();
49710
49802
  const temporary = `${PIN_FILE}.tmp`;
49711
49803
  writeFileSync(temporary, JSON.stringify(pin, null, 2), { mode: 384 });
49712
- chmodSync(temporary, 384);
49804
+ if (process.platform !== "win32") chmodSync(temporary, 384);
49713
49805
  renameSync(temporary, PIN_FILE);
49714
49806
  assertSafe(PIN_FILE, "file");
49715
49807
  }
49716
49808
  function clearPin() {
49717
49809
  rmSync(pinFile(), { force: true });
49718
49810
  }
49719
- var DIR, KEY_FILE, cached2, keyLocation, pinFile, PRINCIPAL, pinLocation;
49811
+ var DIR, KEY_FILE, cached2, keyLocation, pinFile, PRINCIPAL, pins, pinLocation;
49720
49812
  var init_keys = __esm({
49721
49813
  "src/keys.ts"() {
49722
49814
  "use strict";
49815
+ init_keyPermissions();
49723
49816
  init_net();
49724
49817
  DIR = () => join3(process.env.OPENHANDS_HOME || join3(homedir(), ".openhands"), currentNetwork2());
49725
49818
  KEY_FILE = () => join3(DIR(), "key.json");
@@ -49727,6 +49820,7 @@ var init_keys = __esm({
49727
49820
  keyLocation = () => KEY_FILE();
49728
49821
  pinFile = () => join3(DIR(), "principal.json");
49729
49822
  PRINCIPAL = /^(ixo1[a-z0-9]{38,58}|self)$/;
49823
+ pins = /* @__PURE__ */ new Map();
49730
49824
  pinLocation = () => pinFile();
49731
49825
  }
49732
49826
  });
@@ -259464,8 +259558,8 @@ function noteIgnored(agent, pinned, others, log) {
259464
259558
  for (const one of fresh2) told.add(`${agent}:${pinned}:${one}`);
259465
259559
  log(`asks-mcp: ignoring ${fresh2.length === 1 ? "a connection" : "connections"} to this key from ${fresh2.join(", ")}: it is pinned to ${pinned === SELF ? "itself" : pinned}, and only connect_account changes that.`);
259466
259560
  }
259467
- async function actingFor(agent, reads2 = chainReads, pins = keyPins) {
259468
- const pinned = pinOf(agent, pins);
259561
+ async function actingFor(agent, reads2 = chainReads, pins2 = keyPins) {
259562
+ const pinned = pinOf(agent, pins2);
259469
259563
  if (!pinned || pinned === SELF) return null;
259470
259564
  let found;
259471
259565
  try {
@@ -259475,14 +259569,14 @@ async function actingFor(agent, reads2 = chainReads, pins = keyPins) {
259475
259569
  }
259476
259570
  return found ? actingOf(found) : null;
259477
259571
  }
259478
- async function actingOrWhy(agent, reads2 = chainReads, pins = keyPins, log = stderr) {
259572
+ async function actingOrWhy(agent, reads2 = chainReads, pins2 = keyPins, log = stderr) {
259479
259573
  let acting = null;
259480
259574
  try {
259481
- acting = await actingFor(agent, reads2, pins);
259575
+ acting = await actingFor(agent, reads2, pins2);
259482
259576
  } catch (error54) {
259483
259577
  return { acting: null, problem: error54 instanceof Error ? error54.message : String(error54), candidates: [] };
259484
259578
  }
259485
- const pinned = pinOf(agent, pins);
259579
+ const pinned = pinOf(agent, pins2);
259486
259580
  let standing;
259487
259581
  try {
259488
259582
  standing = (await reads2.connectionsTo(agent)).map((one) => one.principal);
@@ -259499,16 +259593,16 @@ function candidateWords(candidates) {
259499
259593
  if (!candidates.length) return null;
259500
259594
  return `Not acting for anyone: ${candidates.length === 1 ? "an account has" : `${candidates.length} accounts have`} connected this key (${list5(candidates)}), and none has been chosen. Anybody can grant to a public address, so ask the person which is theirs and call connect_account with principal set to it (or principal: "self" to stay as this key and stop being asked).`;
259501
259595
  }
259502
- async function connectAs(agent, choice, reads2 = chainReads, pins = keyPins, now3 = /* @__PURE__ */ new Date(), log = stderr) {
259503
- const pinned = pinOf(agent, pins);
259596
+ async function connectAs(agent, choice, reads2 = chainReads, pins2 = keyPins, now3 = /* @__PURE__ */ new Date(), log = stderr) {
259597
+ const pinned = pinOf(agent, pins2);
259504
259598
  if (choice === SELF) {
259505
- if (pinned !== SELF) pins.write({ principal: SELF, agent, pinnedAt: now3.toISOString() });
259599
+ if (pinned !== SELF) pins2.write({ principal: SELF, agent, pinnedAt: now3.toISOString() });
259506
259600
  return { kind: "self", pinnedNow: pinned !== SELF };
259507
259601
  }
259508
259602
  if (choice) {
259509
259603
  const found = await reads2.connectionFor(agent, choice);
259510
259604
  if (!found) return { kind: "not-granted", principal: choice };
259511
- if (pinned !== choice) pins.write({ principal: choice, agent, pinnedAt: now3.toISOString() });
259605
+ if (pinned !== choice) pins2.write({ principal: choice, agent, pinnedAt: now3.toISOString() });
259512
259606
  const others = (await reads2.connectionsTo(agent)).map((one) => one.principal).filter((one) => one !== choice);
259513
259607
  return { kind: "connected", acting: actingOf(found), pinnedNow: pinned !== choice, others };
259514
259608
  }
@@ -259522,13 +259616,13 @@ async function connectAs(agent, choice, reads2 = chainReads, pins = keyPins, now
259522
259616
  const standing = (await reads2.connectionsTo(agent)).map((one) => one.principal);
259523
259617
  return standing.length ? { kind: "choose", principals: standing } : { kind: "none" };
259524
259618
  }
259525
- function markSigned(agent, principal2, pins = keyPins) {
259526
- const pin = pins.read();
259527
- if (pin && pin.agent === agent && pin.principal === principal2 && !pin.signed) pins.write({ ...pin, signed: true });
259619
+ function markSigned(agent, principal2, pins2 = keyPins) {
259620
+ const pin = pins2.read();
259621
+ if (pin && pin.agent === agent && pin.principal === principal2 && !pin.signed) pins2.write({ ...pin, signed: true });
259528
259622
  }
259529
- function disconnect(pins = keyPins) {
259530
- const pin = pins.read();
259531
- pins.clear();
259623
+ function disconnect(pins2 = keyPins) {
259624
+ const pin = pins2.read();
259625
+ pins2.clear();
259532
259626
  return pin;
259533
259627
  }
259534
259628
  function actingWords(acting, face) {
@@ -259604,8 +259698,8 @@ var init_connect = __esm({
259604
259698
  SELF = "self";
259605
259699
  told = /* @__PURE__ */ new Set();
259606
259700
  stderr = (line2) => console.error(line2);
259607
- pinOf = (agent, pins) => {
259608
- const pin = pins.read();
259701
+ pinOf = (agent, pins2) => {
259702
+ const pin = pins2.read();
259609
259703
  return pin && pin.agent === agent ? pin.principal : null;
259610
259704
  };
259611
259705
  feeAllowanceSpent = (error54) => /codespace:?\s*feegrant|fee limit exceeded|fee allowance/i.test(error54 instanceof Error ? error54.message : String(error54));
@@ -261212,7 +261306,7 @@ function prepareAsk(poster, entityDid, adminAccount, terms, now3 = Date.now(), s
261212
261306
  const checkReward = terms.checker && terms.checkRewardUixo ? BigInt(terms.checkRewardUixo) : 0n;
261213
261307
  const spots = BigInt(Math.max(1, terms.quota));
261214
261308
  const denom = terms.denom ?? askDenom();
261215
- if (denom === "upay" && !offChain && network() === "mainnet") throw new Error("PAY is not a reward coin on mainnet: post the reward in USDC");
261309
+ if (denom === "upay" && !offChain && network() === "mainnet" && askDenom() !== "upay") throw new Error("PAY is not a reward coin on mainnet: post the reward in USDC");
261216
261310
  const feeDenom = terms.feeDenom ?? checkerFeeDenom(network());
261217
261311
  const rewardEscrow = offChain ? 0n : BigInt(terms.amountUixo) * spots;
261218
261312
  const feeEscrow = checkReward * spots;
@@ -261965,7 +262059,7 @@ function assertAskShape(ask) {
261965
262059
  const campaignAsk = typeof ask.terms?.campaign === "string";
261966
262060
  const asky = askyChecker(network());
261967
262061
  const askyReview = ask.terms?.call?.kind === "review" && asky !== null && asky.fee.denom === "upay" && ask.checker === asky.address;
261968
- if (network() === "mainnet" && !ask.payoutTerms && !campaignAsk && !askyReview && (ask.denom ?? askDenom()) === "upay") throw new Error(PAY_NOT_A_REWARD);
262062
+ if (network() === "mainnet" && !ask.payoutTerms && !campaignAsk && !askyReview && askDenom() !== "upay" && (ask.denom ?? askDenom()) === "upay") throw new Error(PAY_NOT_A_REWARD);
261969
262063
  if (ask.payoutTerms) {
261970
262064
  if (!ask.org) throw new Error("payout terms need the organisation (org) whose facilitator pays them");
261971
262065
  if (ask.amountUixo !== 0) throw new Error("an ask paid off chain carries nothing on chain: amountUixo must be 0, or the worker is paid twice");
@@ -264382,7 +264476,7 @@ var init_call2 = __esm({
264382
264476
  isTxFailed = (error54) => error54 instanceof Error && error54.name === "TxFailed";
264383
264477
  tagOf = (draft) => callTag(draft.slug);
264384
264478
  TESTNET_CALL_DEFAULTS = {
264385
- /* The sandbox's reward coin is test PAY (build sheet P5); read for one release, the network decides (`readDenom`). */
264479
+ /* The sandbox's reward coin is test PAY (build sheet P5); read for one release, the network decides (`rewardDenomFor`). */
264386
264480
  NEXT_PUBLIC_ASK_DENOM: "upay",
264387
264481
  NEXT_PUBLIC_CHECKER_ADDRESS: "ixo175rlze3q6gap5ddxzc6m92u2ahw7a9h8n4uksq",
264388
264482
  NEXT_PUBLIC_GRANTER_ADDRESS: "ixo1rknlkm29zs2cd8m8y2c6ahmh2jald8swy9g7rn",
@@ -291941,7 +292035,7 @@ import { randomBytes as randomBytes7 } from "node:crypto";
291941
292035
  // package.json
291942
292036
  var package_default = {
291943
292037
  name: "@ixo/asks-mcp",
291944
- version: "1.6.0-dev.3",
292038
+ version: "1.6.0-dev.5",
291945
292039
  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.",
291946
292040
  license: "Apache-2.0",
291947
292041
  repository: {
@@ -292450,7 +292544,7 @@ init_networks();
292450
292544
  init_env();
292451
292545
  init_net();
292452
292546
  init_given();
292453
- init_money();
292547
+ init_rewardDenom();
292454
292548
 
292455
292549
  // ../../packages/openhands-sdk/src/data/plans.ts
292456
292550
  var plansEnabled = () => typeof process !== "undefined" && process.env.NEXT_PUBLIC_PLANS_ENABLED === "1";
@@ -292458,7 +292552,9 @@ var plansEnabled = () => typeof process !== "undefined" && process.env.NEXT_PUBL
292458
292552
  // src/config.ts
292459
292553
  var siteOf = () => (ownEnv().OPENHANDS_SITE || originOf(currentNetwork2())).replace(/\/$/, "");
292460
292554
  var expertSiteOf = () => proOrigin(siteOf());
292461
- var readDenom = (_value, network2) => rewardDenom(network2);
292555
+ function adoptAskDenom(facts) {
292556
+ adopt({ askDenom: facts.askDenom });
292557
+ }
292462
292558
  var CLAIM_FORM_ENV = ["NEXT_PUBLIC_VCT_ENTITY_DID", "NEXT_PUBLIC_VCT_PROOF", "NEXT_PUBLIC_VCT_URL"];
292463
292559
  function claimFormIn(env) {
292464
292560
  const [entity, proof, url4] = CLAIM_FORM_ENV.map((name8) => env[name8]?.trim() || "");
@@ -292482,7 +292578,7 @@ function fromEnv() {
292482
292578
  const env = ownEnv();
292483
292579
  return {
292484
292580
  network: currentNetwork2(),
292485
- askDenom: readDenom(env.NEXT_PUBLIC_ASK_DENOM, currentNetwork2()),
292581
+ askDenom: rewardDenomFor(currentNetwork2(), env.NEXT_PUBLIC_ASK_DENOM),
292486
292582
  checker: env.NEXT_PUBLIC_CHECKER_ADDRESS?.trim() || "",
292487
292583
  gate: env.NEXT_PUBLIC_GRANTER_ADDRESS?.trim() || "",
292488
292584
  checkRewardBase: Number(env.QI_CHECK_REWARD_BASE || env.QI_CHECK_REWARD_UIXO || 1e5),
@@ -292530,7 +292626,7 @@ async function deployment() {
292530
292626
  const engineSet = own2.ENGINE_ORACLE_ADDRESS !== void 0 || own2.QI_ENGINE_LIVE !== void 0;
292531
292627
  return {
292532
292628
  network: NETWORK2,
292533
- askDenom: readDenom(own2.NEXT_PUBLIC_ASK_DENOM || answer.askDenom, NETWORK2),
292629
+ askDenom: rewardDenomFor(NETWORK2, own2.NEXT_PUBLIC_ASK_DENOM || answer.askDenom),
292534
292630
  checker: env.checker || answer.checker?.trim() || "",
292535
292631
  gate: env.gate || answer.gate?.trim() || "",
292536
292632
  checkRewardBase: own2.QI_CHECK_REWARD_BASE || own2.QI_CHECK_REWARD_UIXO ? env.checkRewardBase : Number(answer.checkRewardBase || 1e5),
@@ -294059,6 +294155,7 @@ async function build2(options, server2, parts) {
294059
294155
  adoptReviewBilling(DEPLOYMENT);
294060
294156
  adoptOracleUrl(DEPLOYMENT);
294061
294157
  if (local) adoptFeeSplit(DEPLOYMENT);
294158
+ if (local) adoptAskDenom(DEPLOYMENT);
294062
294159
  const OWN_FORM_ENV = Object.fromEntries(CLAIM_FORM_ENV.map((name8) => [name8, ownEnv()[name8]]));
294063
294160
  if (local) adoptClaimForm(DEPLOYMENT);
294064
294161
  let CLAIM_FORM = DEPLOYMENT.claimForm;
@@ -294077,6 +294174,7 @@ async function build2(options, server2, parts) {
294077
294174
  adoptReviewBilling(fresh2);
294078
294175
  adoptOracleUrl(fresh2);
294079
294176
  adoptFeeSplit(fresh2);
294177
+ adoptAskDenom(fresh2);
294080
294178
  adoptClaimForm(fresh2);
294081
294179
  CLAIM_FORM = fresh2.claimForm;
294082
294180
  applyDeployment(fresh2);
@@ -296091,7 +296189,7 @@ ${settings}`
296091
296189
  confirm: external_exports.string().optional().describe("The exact total from the preview. Omit to preview.")
296092
296190
  }
296093
296191
  },
296094
- async ({ name: name8, pins, pin_pay, currency, days, metres, confirm }) => {
296192
+ async ({ name: name8, pins: pins2, pin_pay, currency, days, metres, confirm }) => {
296095
296193
  const acting = await principal2();
296096
296194
  const coin = await coinFor(currency, acting);
296097
296195
  if (typeof coin !== "string") return text4(`Refused: ${coin.refused}. Nothing was opened and nothing was spent.`);
@@ -296112,8 +296210,8 @@ ${settings}`
296112
296210
  terms.via = "mcp";
296113
296211
  const unread = await unreadable(terms);
296114
296212
  if (unread) return text4(unread);
296115
- const { total } = escrowTotal(pin_pay, pins, legBase, coinSymbol, coin === FEE_DENOM);
296116
- const preview = `${face(total, coinSymbol)} to open "${name8}". ${pins} ${pins === 1 ? "report" : "reports"} at ${face(pin_pay, coinSymbol)} each, plus ${feeWords(legBase)} to whoever checks each one. Mapping stays open ${open} days, and two pins closer than ${apart} m are the same mess`;
296213
+ const { total } = escrowTotal(pin_pay, pins2, legBase, coinSymbol, coin === FEE_DENOM);
296214
+ const preview = `${face(total, coinSymbol)} to open "${name8}". ${pins2} ${pins2 === 1 ? "report" : "reports"} at ${face(pin_pay, coinSymbol)} each, plus ${feeWords(legBase)} to whoever checks each one. Mapping stays open ${open} days, and two pins closer than ${apart} m are the same mess`;
296117
296215
  const guard = spendGuard({ bucket: "organise", task: `campaign ${tag}`, total, ...confirm ? { confirm } : {}, what: preview, symbol: coinSymbol });
296118
296216
  if (guard) {
296119
296217
  return text4(
@@ -296138,7 +296236,7 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
296138
296236
  ...await postedAs(acting),
296139
296237
  title: terms.task,
296140
296238
  amountUixo: baseFromPay2(pin_pay),
296141
- quota: pins,
296239
+ quota: pins2,
296142
296240
  days: open,
296143
296241
  denom: coin,
296144
296242
  terms,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ixo/asks-mcp",
3
- "version": "1.6.0-dev.3",
3
+ "version": "1.6.0-dev.5",
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": {