@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 +2 -0
- package/dist/asks-mcp.mjs +151 -53
- package/package.json +1 -1
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
259468
|
-
const pinned = pinOf(agent,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
259503
|
-
const pinned = pinOf(agent,
|
|
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)
|
|
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)
|
|
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,
|
|
259526
|
-
const pin =
|
|
259527
|
-
if (pin && pin.agent === agent && pin.principal === principal2 && !pin.signed)
|
|
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(
|
|
259530
|
-
const pin =
|
|
259531
|
-
|
|
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,
|
|
259608
|
-
const pin =
|
|
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 (`
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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,
|
|
296116
|
-
const preview = `${face(total, coinSymbol)} to open "${name8}". ${
|
|
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:
|
|
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
|
+
"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": {
|