aipou-mcp-server 0.5.0 → 0.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.
- package/README.md +7 -0
- package/dist/bounded-input.d.ts +8 -0
- package/dist/bounded-input.js +21 -0
- package/dist/bounded-input.test.d.ts +1 -0
- package/dist/bounded-input.test.js +24 -0
- package/dist/claims.js +119 -31
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +1 -1
- package/dist/doctor.test.js +2 -0
- package/dist/evidence.d.ts +21 -0
- package/dist/evidence.js +14 -0
- package/dist/feedback.d.ts +17 -0
- package/dist/feedback.js +24 -0
- package/dist/identity.d.ts +3 -0
- package/dist/identity.js +25 -1
- package/dist/index.js +1842 -523
- package/dist/init.d.ts +6 -2
- package/dist/init.js +16 -3
- package/dist/init.test.js +18 -4
- package/dist/lock.js +47 -9
- package/dist/lock.test.d.ts +1 -0
- package/dist/lock.test.js +26 -0
- package/dist/observed-outcome-pilot.d.ts +16 -0
- package/dist/observed-outcome-pilot.js +59 -0
- package/dist/observed-outcome-pilot.test.d.ts +1 -0
- package/dist/observed-outcome-pilot.test.js +25 -0
- package/dist/paybox-link.d.ts +34 -0
- package/dist/paybox-link.js +52 -0
- package/dist/paybox-link.test.d.ts +1 -0
- package/dist/paybox-link.test.js +36 -0
- package/dist/private-json.d.ts +2 -0
- package/dist/private-json.js +39 -0
- package/dist/protocol.test.js +23 -0
- package/dist/receipt-metadata.d.ts +8 -0
- package/dist/receipt-metadata.js +12 -0
- package/dist/receipt-metadata.test.js +18 -1
- package/dist/receipt-verifier.d.ts +13 -0
- package/dist/receipt-verifier.js +125 -0
- package/dist/receipt-verifier.test.d.ts +1 -0
- package/dist/receipt-verifier.test.js +54 -0
- package/dist/receipts.js +11 -2
- package/dist/settlement-driver.d.ts +3 -0
- package/dist/settlement-driver.js +120 -0
- package/dist/settlement-driver.test.d.ts +1 -0
- package/dist/settlement-driver.test.js +78 -0
- package/dist/settlement-runner.d.ts +34 -0
- package/dist/settlement-runner.js +40 -0
- package/dist/settlement-runner.test.d.ts +1 -0
- package/dist/settlement-runner.test.js +128 -0
- package/dist/status.js +2 -0
- package/dist/store.d.ts +2 -0
- package/dist/store.js +33 -9
- package/dist/technocore-link.d.ts +34 -0
- package/dist/technocore-link.js +49 -0
- package/dist/technocore-link.test.d.ts +1 -0
- package/dist/technocore-link.test.js +36 -0
- package/dist/tool-metadata.test.d.ts +1 -0
- package/dist/tool-metadata.test.js +86 -0
- package/dist/types.d.ts +6 -0
- package/dist/usage-recovery.test.d.ts +1 -0
- package/dist/usage-recovery.test.js +121 -0
- package/package.json +4 -4
- package/server.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3105,9 +3105,28 @@ var require_utils = __commonJS({
|
|
|
3105
3105
|
"use strict";
|
|
3106
3106
|
var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
|
|
3107
3107
|
var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
|
|
3108
|
+
var isPort = RegExp.prototype.test.bind(/^\d*$/u);
|
|
3108
3109
|
var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
|
|
3109
3110
|
var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
|
|
3110
|
-
var isPathCharacter = RegExp.prototype.test.bind(/^[
|
|
3111
|
+
var isPathCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);
|
|
3112
|
+
var isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/?]$/u);
|
|
3113
|
+
var isUserinfoCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:]$/u);
|
|
3114
|
+
var BYTE_HEX = new Array(256);
|
|
3115
|
+
{
|
|
3116
|
+
const HEX_DIGITS = "0123456789ABCDEF";
|
|
3117
|
+
for (let i = 0; i < 256; i++) {
|
|
3118
|
+
BYTE_HEX[i] = "%" + HEX_DIGITS[i >> 4] + HEX_DIGITS[i & 15];
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
3121
|
+
function percentEncodeNonAscii(cp) {
|
|
3122
|
+
if (cp < 2048) {
|
|
3123
|
+
return BYTE_HEX[192 | cp >> 6] + BYTE_HEX[128 | cp & 63];
|
|
3124
|
+
}
|
|
3125
|
+
if (cp < 65536) {
|
|
3126
|
+
return BYTE_HEX[224 | cp >> 12] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
|
|
3127
|
+
}
|
|
3128
|
+
return BYTE_HEX[240 | cp >> 18] + BYTE_HEX[128 | cp >> 12 & 63] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
|
|
3129
|
+
}
|
|
3111
3130
|
function stringArrayToHexStripped(input) {
|
|
3112
3131
|
let acc = "";
|
|
3113
3132
|
let code = 0;
|
|
@@ -3132,91 +3151,105 @@ var require_utils = __commonJS({
|
|
|
3132
3151
|
}
|
|
3133
3152
|
return acc;
|
|
3134
3153
|
}
|
|
3154
|
+
var isHextet = RegExp.prototype.test.bind(/^[\dA-Fa-f]{1,4}$/);
|
|
3155
|
+
var isIPvFuture = RegExp.prototype.test.bind(/^[vV][\dA-Fa-f]+\.[A-Za-z\d\-._~!$&'()*+,;=:]+$/);
|
|
3156
|
+
var isZoneCharacter = RegExp.prototype.test.bind(/^[A-Za-z\d\-._~]$/);
|
|
3135
3157
|
var nonSimpleDomain = RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);
|
|
3136
|
-
function
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
if (hex !== "") {
|
|
3144
|
-
address.push(hex);
|
|
3145
|
-
} else {
|
|
3146
|
-
output.error = true;
|
|
3147
|
-
return false;
|
|
3158
|
+
function isZoneIdentifier(zone) {
|
|
3159
|
+
if (zone.length === 0) return false;
|
|
3160
|
+
for (let i = 0; i < zone.length; i++) {
|
|
3161
|
+
if (isZoneCharacter(zone[i])) continue;
|
|
3162
|
+
if (zone[i] === "%" && i + 2 < zone.length && isHexPair(zone.slice(i + 1, i + 3))) {
|
|
3163
|
+
i += 2;
|
|
3164
|
+
continue;
|
|
3148
3165
|
}
|
|
3149
|
-
|
|
3166
|
+
return false;
|
|
3150
3167
|
}
|
|
3151
3168
|
return true;
|
|
3152
3169
|
}
|
|
3153
|
-
function
|
|
3154
|
-
let
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
let
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
}
|
|
3166
|
-
if (cursor === ":") {
|
|
3167
|
-
if (endipv6Encountered === true) {
|
|
3168
|
-
endIpv6 = true;
|
|
3169
|
-
}
|
|
3170
|
-
if (!consume(buffer, address, output)) {
|
|
3171
|
-
break;
|
|
3172
|
-
}
|
|
3173
|
-
if (++tokenCount > 7) {
|
|
3174
|
-
output.error = true;
|
|
3175
|
-
break;
|
|
3176
|
-
}
|
|
3177
|
-
if (i > 0 && input[i - 1] === ":") {
|
|
3178
|
-
endipv6Encountered = true;
|
|
3170
|
+
function compressIPv6ZeroRun(hextets) {
|
|
3171
|
+
let bestStart = -1;
|
|
3172
|
+
let bestLength = 0;
|
|
3173
|
+
let runStart = -1;
|
|
3174
|
+
let runLength = 0;
|
|
3175
|
+
for (let i = 0; i < hextets.length; i++) {
|
|
3176
|
+
if (hextets[i] === "0") {
|
|
3177
|
+
if (runStart === -1) runStart = i;
|
|
3178
|
+
runLength++;
|
|
3179
|
+
if (runLength > bestLength) {
|
|
3180
|
+
bestLength = runLength;
|
|
3181
|
+
bestStart = runStart;
|
|
3179
3182
|
}
|
|
3180
|
-
address.push(":");
|
|
3181
|
-
continue;
|
|
3182
|
-
} else if (cursor === "%") {
|
|
3183
|
-
if (!consume(buffer, address, output)) {
|
|
3184
|
-
break;
|
|
3185
|
-
}
|
|
3186
|
-
consume = consumeIsZone;
|
|
3187
3183
|
} else {
|
|
3188
|
-
|
|
3184
|
+
runStart = -1;
|
|
3185
|
+
runLength = 0;
|
|
3186
|
+
}
|
|
3187
|
+
}
|
|
3188
|
+
if (bestLength < 2) return hextets.join(":");
|
|
3189
|
+
const head = hextets.slice(0, bestStart).join(":");
|
|
3190
|
+
const tail = hextets.slice(bestStart + bestLength).join(":");
|
|
3191
|
+
return head + "::" + tail;
|
|
3192
|
+
}
|
|
3193
|
+
function normalizeIPv6Address(input) {
|
|
3194
|
+
const compression = input.indexOf("::");
|
|
3195
|
+
if (compression !== -1 && input.indexOf("::", compression + 1) !== -1) return void 0;
|
|
3196
|
+
const left = compression === -1 ? input.split(":") : input.slice(0, compression).split(":");
|
|
3197
|
+
const right = compression === -1 ? [] : input.slice(compression + 2).split(":");
|
|
3198
|
+
if (compression !== -1) {
|
|
3199
|
+
if (left.length === 1 && left[0] === "") left.length = 0;
|
|
3200
|
+
if (right.length === 1 && right[0] === "") right.length = 0;
|
|
3201
|
+
}
|
|
3202
|
+
const parts = left.concat(right);
|
|
3203
|
+
let hextetCount = 0;
|
|
3204
|
+
for (let i = 0; i < parts.length; i++) {
|
|
3205
|
+
const part = parts[i];
|
|
3206
|
+
if (part === "") return void 0;
|
|
3207
|
+
if (part.indexOf(".") !== -1) {
|
|
3208
|
+
if (i !== parts.length - 1 || compression !== -1 && right.length === 0 || !isIPv4(part)) return void 0;
|
|
3209
|
+
hextetCount += 2;
|
|
3189
3210
|
continue;
|
|
3190
3211
|
}
|
|
3212
|
+
if (!isHextet(part)) return void 0;
|
|
3213
|
+
parts[i] = parseInt(part, 16).toString(16);
|
|
3214
|
+
hextetCount++;
|
|
3191
3215
|
}
|
|
3192
|
-
if (
|
|
3193
|
-
if (
|
|
3194
|
-
|
|
3195
|
-
} else if (endIpv6) {
|
|
3196
|
-
address.push(buffer.join(""));
|
|
3197
|
-
} else {
|
|
3198
|
-
address.push(stringArrayToHexStripped(buffer));
|
|
3199
|
-
}
|
|
3216
|
+
if (compression === -1) {
|
|
3217
|
+
if (hextetCount !== 8) return void 0;
|
|
3218
|
+
return compressIPv6ZeroRun(parts);
|
|
3200
3219
|
}
|
|
3201
|
-
|
|
3202
|
-
|
|
3220
|
+
if (hextetCount >= 8) return void 0;
|
|
3221
|
+
const expanded = parts.slice(0, left.length);
|
|
3222
|
+
for (let i = hextetCount; i < 8; i++) expanded.push("0");
|
|
3223
|
+
for (let i = left.length; i < parts.length; i++) expanded.push(parts[i]);
|
|
3224
|
+
return compressIPv6ZeroRun(expanded);
|
|
3203
3225
|
}
|
|
3204
3226
|
function normalizeIPv6(host) {
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
}
|
|
3208
|
-
|
|
3209
|
-
if (
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3227
|
+
const bracketed = host[0] === "[" && host[host.length - 1] === "]";
|
|
3228
|
+
const hasBracket = host[0] === "[" || host[host.length - 1] === "]";
|
|
3229
|
+
if (hasBracket && !bracketed) return { host, isIPV6: false, error: true };
|
|
3230
|
+
let input = bracketed ? host.slice(1, -1) : host;
|
|
3231
|
+
if (bracketed && isIPvFuture(input)) {
|
|
3232
|
+
input = input.toLowerCase();
|
|
3233
|
+
return { host: `[${input}]`, escapedHost: input, isIPV6: false, isIPVFuture: true };
|
|
3234
|
+
}
|
|
3235
|
+
if (findToken(input, ":") < 2) {
|
|
3236
|
+
return { host, isIPV6: false, error: bracketed };
|
|
3237
|
+
}
|
|
3238
|
+
let zoneIdentifier = "";
|
|
3239
|
+
const zoneSeparator = input.indexOf("%");
|
|
3240
|
+
if (zoneSeparator !== -1) {
|
|
3241
|
+
const separatorLength = input.slice(zoneSeparator, zoneSeparator + 3).toLowerCase() === "%25" ? 3 : 1;
|
|
3242
|
+
zoneIdentifier = input.slice(zoneSeparator + separatorLength);
|
|
3243
|
+
if (!isZoneIdentifier(zoneIdentifier)) return { host, isIPV6: false, error: true };
|
|
3244
|
+
input = input.slice(0, zoneSeparator);
|
|
3245
|
+
}
|
|
3246
|
+
const address = normalizeIPv6Address(input);
|
|
3247
|
+
if (address === void 0) return { host, isIPV6: false, error: true };
|
|
3248
|
+
return {
|
|
3249
|
+
host: address + (zoneIdentifier ? "%" + zoneIdentifier : ""),
|
|
3250
|
+
escapedHost: address + (zoneIdentifier ? "%25" + zoneIdentifier : ""),
|
|
3251
|
+
isIPV6: true
|
|
3252
|
+
};
|
|
3220
3253
|
}
|
|
3221
3254
|
function findToken(str, token) {
|
|
3222
3255
|
let ind = 0;
|
|
@@ -3225,8 +3258,8 @@ var require_utils = __commonJS({
|
|
|
3225
3258
|
}
|
|
3226
3259
|
return ind;
|
|
3227
3260
|
}
|
|
3228
|
-
function removeDotSegments(
|
|
3229
|
-
let input =
|
|
3261
|
+
function removeDotSegments(path12) {
|
|
3262
|
+
let input = path12;
|
|
3230
3263
|
const output = [];
|
|
3231
3264
|
let nextSlash = -1;
|
|
3232
3265
|
let len = 0;
|
|
@@ -3300,6 +3333,37 @@ var require_utils = __commonJS({
|
|
|
3300
3333
|
}
|
|
3301
3334
|
return output.join("");
|
|
3302
3335
|
}
|
|
3336
|
+
function removeDotSegmentsRelative(path12) {
|
|
3337
|
+
if (path12 === "" || path12[0] === "/") {
|
|
3338
|
+
return removeDotSegments(path12);
|
|
3339
|
+
}
|
|
3340
|
+
const segments = path12.split("/");
|
|
3341
|
+
const last = segments.length - 1;
|
|
3342
|
+
const output = [];
|
|
3343
|
+
let trailingDirectory = false;
|
|
3344
|
+
for (let i = 0; i <= last; i++) {
|
|
3345
|
+
const segment = segments[i];
|
|
3346
|
+
if (segment === "." || segment === "..") {
|
|
3347
|
+
if (segment === "..") {
|
|
3348
|
+
if (output.length !== 0 && output[output.length - 1] !== "..") {
|
|
3349
|
+
output.pop();
|
|
3350
|
+
} else {
|
|
3351
|
+
output.push("..");
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
trailingDirectory = i === last;
|
|
3355
|
+
} else {
|
|
3356
|
+
output.push(segment);
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
if (trailingDirectory && output[output.length - 1] !== "..") {
|
|
3360
|
+
output.push("");
|
|
3361
|
+
}
|
|
3362
|
+
if (output.length === 0 || output[0] === "" || output[0].includes(":")) {
|
|
3363
|
+
output.unshift(".");
|
|
3364
|
+
}
|
|
3365
|
+
return output.join("/");
|
|
3366
|
+
}
|
|
3303
3367
|
var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
|
|
3304
3368
|
var HOST_DELIM_RE = /[@/?#:]/g;
|
|
3305
3369
|
var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
|
|
@@ -3335,7 +3399,8 @@ var require_utils = __commonJS({
|
|
|
3335
3399
|
function normalizePathEncoding(input) {
|
|
3336
3400
|
let output = "";
|
|
3337
3401
|
for (let i = 0; i < input.length; i++) {
|
|
3338
|
-
|
|
3402
|
+
const ch = input[i];
|
|
3403
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
3339
3404
|
const hex = input.slice(i + 1, i + 3);
|
|
3340
3405
|
if (isHexPair(hex)) {
|
|
3341
3406
|
const normalizedHex = hex.toUpperCase();
|
|
@@ -3349,10 +3414,152 @@ var require_utils = __commonJS({
|
|
|
3349
3414
|
continue;
|
|
3350
3415
|
}
|
|
3351
3416
|
}
|
|
3352
|
-
if (isPathCharacter(
|
|
3353
|
-
output +=
|
|
3417
|
+
if (isPathCharacter(ch)) {
|
|
3418
|
+
output += ch;
|
|
3419
|
+
} else {
|
|
3420
|
+
const code = input.charCodeAt(i);
|
|
3421
|
+
if (code < 128) {
|
|
3422
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
|
|
3423
|
+
} else if (code < 55296 || code > 57343) {
|
|
3424
|
+
output += percentEncodeNonAscii(code);
|
|
3425
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
3426
|
+
const low = input.charCodeAt(i + 1);
|
|
3427
|
+
if (low >= 56320 && low <= 57343) {
|
|
3428
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3429
|
+
i++;
|
|
3430
|
+
} else {
|
|
3431
|
+
output += percentEncodeNonAscii(65533);
|
|
3432
|
+
}
|
|
3433
|
+
} else {
|
|
3434
|
+
output += percentEncodeNonAscii(65533);
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
return output;
|
|
3439
|
+
}
|
|
3440
|
+
function serializePathEncoding(input, pathNoScheme = false) {
|
|
3441
|
+
let output = "";
|
|
3442
|
+
let firstSegment = pathNoScheme && input[0] !== "/";
|
|
3443
|
+
for (let i = 0; i < input.length; i++) {
|
|
3444
|
+
const ch = input[i];
|
|
3445
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
3446
|
+
const hex = input.slice(i + 1, i + 3);
|
|
3447
|
+
if (isHexPair(hex)) {
|
|
3448
|
+
output += "%" + hex.toUpperCase();
|
|
3449
|
+
i += 2;
|
|
3450
|
+
continue;
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
if (ch === "/") {
|
|
3454
|
+
firstSegment = false;
|
|
3455
|
+
}
|
|
3456
|
+
if (isPathCharacter(ch) && (ch !== ":" || !firstSegment)) {
|
|
3457
|
+
output += ch;
|
|
3458
|
+
} else {
|
|
3459
|
+
const code = input.charCodeAt(i);
|
|
3460
|
+
if (code < 128) {
|
|
3461
|
+
output += BYTE_HEX[code];
|
|
3462
|
+
} else if (code < 55296 || code > 57343) {
|
|
3463
|
+
output += percentEncodeNonAscii(code);
|
|
3464
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
3465
|
+
const low = input.charCodeAt(i + 1);
|
|
3466
|
+
if (low >= 56320 && low <= 57343) {
|
|
3467
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3468
|
+
i++;
|
|
3469
|
+
} else {
|
|
3470
|
+
output += percentEncodeNonAscii(65533);
|
|
3471
|
+
}
|
|
3472
|
+
} else {
|
|
3473
|
+
output += percentEncodeNonAscii(65533);
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
return output;
|
|
3478
|
+
}
|
|
3479
|
+
function encodeComponent(input, isAllowed) {
|
|
3480
|
+
let output = "";
|
|
3481
|
+
for (let i = 0; i < input.length; i++) {
|
|
3482
|
+
const ch = input[i];
|
|
3483
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
3484
|
+
const hex = input.slice(i + 1, i + 3);
|
|
3485
|
+
if (isHexPair(hex)) {
|
|
3486
|
+
output += "%" + hex.toUpperCase();
|
|
3487
|
+
i += 2;
|
|
3488
|
+
continue;
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
if (isAllowed(ch)) {
|
|
3492
|
+
output += ch;
|
|
3493
|
+
} else {
|
|
3494
|
+
const code = input.charCodeAt(i);
|
|
3495
|
+
if (code < 128) {
|
|
3496
|
+
output += BYTE_HEX[code];
|
|
3497
|
+
} else if (code < 55296 || code > 57343) {
|
|
3498
|
+
output += percentEncodeNonAscii(code);
|
|
3499
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
3500
|
+
const low = input.charCodeAt(i + 1);
|
|
3501
|
+
if (low >= 56320 && low <= 57343) {
|
|
3502
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3503
|
+
i++;
|
|
3504
|
+
} else {
|
|
3505
|
+
output += percentEncodeNonAscii(65533);
|
|
3506
|
+
}
|
|
3507
|
+
} else {
|
|
3508
|
+
output += percentEncodeNonAscii(65533);
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
}
|
|
3512
|
+
return output;
|
|
3513
|
+
}
|
|
3514
|
+
function encodeUserinfo(input) {
|
|
3515
|
+
return encodeComponent(input, isUserinfoCharacter);
|
|
3516
|
+
}
|
|
3517
|
+
function encodeQuery(input) {
|
|
3518
|
+
return encodeComponent(input, isQueryFragmentCharacter);
|
|
3519
|
+
}
|
|
3520
|
+
function encodeFragment(input) {
|
|
3521
|
+
return encodeComponent(input, isQueryFragmentCharacter);
|
|
3522
|
+
}
|
|
3523
|
+
function isEscapeSafe(cp) {
|
|
3524
|
+
return cp >= 48 && cp <= 57 || cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp === 42 || cp === 43 || cp === 45 || cp === 46 || cp === 47 || cp === 64 || cp === 95;
|
|
3525
|
+
}
|
|
3526
|
+
function normalizeQueryFragmentEncoding(input) {
|
|
3527
|
+
let output = "";
|
|
3528
|
+
for (let i = 0; i < input.length; i++) {
|
|
3529
|
+
const ch = input[i];
|
|
3530
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
3531
|
+
const hex = input.slice(i + 1, i + 3);
|
|
3532
|
+
if (isHexPair(hex)) {
|
|
3533
|
+
const normalizedHex = hex.toUpperCase();
|
|
3534
|
+
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
3535
|
+
if (isUnreserved(decoded)) {
|
|
3536
|
+
output += decoded;
|
|
3537
|
+
} else {
|
|
3538
|
+
output += "%" + normalizedHex;
|
|
3539
|
+
}
|
|
3540
|
+
i += 2;
|
|
3541
|
+
continue;
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
if (isQueryFragmentCharacter(ch)) {
|
|
3545
|
+
output += ch;
|
|
3354
3546
|
} else {
|
|
3355
|
-
|
|
3547
|
+
const code = input.charCodeAt(i);
|
|
3548
|
+
if (code < 128) {
|
|
3549
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
|
|
3550
|
+
} else if (code < 55296 || code > 57343) {
|
|
3551
|
+
output += percentEncodeNonAscii(code);
|
|
3552
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
3553
|
+
const low = input.charCodeAt(i + 1);
|
|
3554
|
+
if (low >= 56320 && low <= 57343) {
|
|
3555
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3556
|
+
i++;
|
|
3557
|
+
} else {
|
|
3558
|
+
output += percentEncodeNonAscii(65533);
|
|
3559
|
+
}
|
|
3560
|
+
} else {
|
|
3561
|
+
output += percentEncodeNonAscii(65533);
|
|
3562
|
+
}
|
|
3356
3563
|
}
|
|
3357
3564
|
}
|
|
3358
3565
|
return output;
|
|
@@ -3375,14 +3582,18 @@ var require_utils = __commonJS({
|
|
|
3375
3582
|
function recomposeAuthority(component) {
|
|
3376
3583
|
const uriTokens = [];
|
|
3377
3584
|
if (component.userinfo !== void 0) {
|
|
3378
|
-
uriTokens.push(component.userinfo);
|
|
3585
|
+
uriTokens.push(encodeUserinfo(component.userinfo));
|
|
3379
3586
|
uriTokens.push("@");
|
|
3380
3587
|
}
|
|
3381
3588
|
if (component.host !== void 0) {
|
|
3382
|
-
let host =
|
|
3589
|
+
let host = component.host;
|
|
3383
3590
|
if (!isIPv4(host)) {
|
|
3384
|
-
|
|
3385
|
-
if (ipV6res.isIPV6
|
|
3591
|
+
let ipV6res = normalizeIPv6(host);
|
|
3592
|
+
if (ipV6res.isIPV6 !== true && ipV6res.isIPVFuture !== true) {
|
|
3593
|
+
host = normalizePercentEncoding(host, true);
|
|
3594
|
+
ipV6res = normalizeIPv6(host);
|
|
3595
|
+
}
|
|
3596
|
+
if (ipV6res.isIPV6 === true || ipV6res.isIPVFuture === true) {
|
|
3386
3597
|
host = `[${ipV6res.escapedHost}]`;
|
|
3387
3598
|
} else {
|
|
3388
3599
|
host = reescapeHostDelimiters(host, false);
|
|
@@ -3391,8 +3602,12 @@ var require_utils = __commonJS({
|
|
|
3391
3602
|
uriTokens.push(host);
|
|
3392
3603
|
}
|
|
3393
3604
|
if (typeof component.port === "number" || typeof component.port === "string") {
|
|
3605
|
+
const port = String(component.port);
|
|
3606
|
+
if (!isPort(port)) {
|
|
3607
|
+
throw new TypeError("URI port is malformed.");
|
|
3608
|
+
}
|
|
3394
3609
|
uriTokens.push(":");
|
|
3395
|
-
uriTokens.push(
|
|
3610
|
+
uriTokens.push(port);
|
|
3396
3611
|
}
|
|
3397
3612
|
return uriTokens.length ? uriTokens.join("") : void 0;
|
|
3398
3613
|
}
|
|
@@ -3402,8 +3617,14 @@ var require_utils = __commonJS({
|
|
|
3402
3617
|
reescapeHostDelimiters,
|
|
3403
3618
|
normalizePercentEncoding,
|
|
3404
3619
|
normalizePathEncoding,
|
|
3620
|
+
serializePathEncoding,
|
|
3621
|
+
normalizeQueryFragmentEncoding,
|
|
3622
|
+
encodeUserinfo,
|
|
3623
|
+
encodeQuery,
|
|
3624
|
+
encodeFragment,
|
|
3405
3625
|
escapePreservingEscapes,
|
|
3406
3626
|
removeDotSegments,
|
|
3627
|
+
removeDotSegmentsRelative,
|
|
3407
3628
|
isIPv4,
|
|
3408
3629
|
isUUID,
|
|
3409
3630
|
normalizeIPv6,
|
|
@@ -3417,7 +3638,7 @@ var require_schemes = __commonJS({
|
|
|
3417
3638
|
"../node_modules/fast-uri/lib/schemes.js"(exports, module) {
|
|
3418
3639
|
"use strict";
|
|
3419
3640
|
var { isUUID } = require_utils();
|
|
3420
|
-
var URN_REG =
|
|
3641
|
+
var URN_REG = /^([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-./:;=@]|%[\da-f]{2})+)$/iu;
|
|
3421
3642
|
var supportedSchemeNames = (
|
|
3422
3643
|
/** @type {const} */
|
|
3423
3644
|
[
|
|
@@ -3478,9 +3699,10 @@ var require_schemes = __commonJS({
|
|
|
3478
3699
|
wsComponent.secure = void 0;
|
|
3479
3700
|
}
|
|
3480
3701
|
if (wsComponent.resourceName) {
|
|
3481
|
-
const
|
|
3482
|
-
|
|
3483
|
-
wsComponent.
|
|
3702
|
+
const queryIndex = wsComponent.resourceName.indexOf("?");
|
|
3703
|
+
const path12 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
|
|
3704
|
+
wsComponent.path = path12 && path12 !== "/" ? path12 : void 0;
|
|
3705
|
+
wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
|
|
3484
3706
|
wsComponent.resourceName = void 0;
|
|
3485
3707
|
}
|
|
3486
3708
|
wsComponent.fragment = void 0;
|
|
@@ -3492,7 +3714,7 @@ var require_schemes = __commonJS({
|
|
|
3492
3714
|
return urnComponent;
|
|
3493
3715
|
}
|
|
3494
3716
|
const matches = urnComponent.path.match(URN_REG);
|
|
3495
|
-
if (matches) {
|
|
3717
|
+
if (matches && matches[0] === urnComponent.path) {
|
|
3496
3718
|
const scheme = options.scheme || urnComponent.scheme || "urn";
|
|
3497
3719
|
urnComponent.nid = matches[1].toLowerCase();
|
|
3498
3720
|
urnComponent.nss = matches[2];
|
|
@@ -3626,8 +3848,19 @@ var require_schemes = __commonJS({
|
|
|
3626
3848
|
var require_fast_uri = __commonJS({
|
|
3627
3849
|
"../node_modules/fast-uri/index.js"(exports, module) {
|
|
3628
3850
|
"use strict";
|
|
3629
|
-
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding,
|
|
3851
|
+
var { normalizeIPv6, removeDotSegments, removeDotSegmentsRelative, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
|
|
3630
3852
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
3853
|
+
var VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
|
|
3854
|
+
var MALFORMED_SCHEME_ERROR = "URI scheme is malformed.";
|
|
3855
|
+
var MALFORMED_HOST_ERROR = "URI host is malformed.";
|
|
3856
|
+
var FORBIDDEN_DOMAIN_HOST_CHARACTER = /[\0-\x20"#/:<>?@[\\\]^`{|}\x7F]/u;
|
|
3857
|
+
function validateScheme(scheme) {
|
|
3858
|
+
const stringScheme = String(scheme);
|
|
3859
|
+
if (!VALID_SCHEME.test(stringScheme)) {
|
|
3860
|
+
throw new TypeError(MALFORMED_SCHEME_ERROR);
|
|
3861
|
+
}
|
|
3862
|
+
return stringScheme;
|
|
3863
|
+
}
|
|
3631
3864
|
function normalize(uri, options) {
|
|
3632
3865
|
if (typeof uri === "string") {
|
|
3633
3866
|
uri = /** @type {T} */
|
|
@@ -3640,7 +3873,37 @@ var require_fast_uri = __commonJS({
|
|
|
3640
3873
|
}
|
|
3641
3874
|
function resolve(baseURI, relativeURI, options) {
|
|
3642
3875
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3643
|
-
const
|
|
3876
|
+
const {
|
|
3877
|
+
parsed: baseParsed,
|
|
3878
|
+
malformedAuthorityOrPort: baseMalformed,
|
|
3879
|
+
malformedPercentEncoding: baseMalformedPercentEncoding,
|
|
3880
|
+
malformedSchemeSpecific: baseMalformedSchemeSpecific,
|
|
3881
|
+
malformedHost: baseMalformedHost,
|
|
3882
|
+
malformedScheme: baseMalformedScheme
|
|
3883
|
+
} = parseWithStatus(baseURI, schemelessOptions);
|
|
3884
|
+
const {
|
|
3885
|
+
parsed: relativeParsed,
|
|
3886
|
+
malformedAuthorityOrPort: relativeMalformed,
|
|
3887
|
+
malformedPercentEncoding: relativeMalformedPercentEncoding,
|
|
3888
|
+
malformedSchemeSpecific: relativeMalformedSchemeSpecific,
|
|
3889
|
+
malformedHost: relativeMalformedHost,
|
|
3890
|
+
malformedScheme: relativeMalformedScheme
|
|
3891
|
+
} = parseWithStatus(relativeURI, schemelessOptions);
|
|
3892
|
+
if (baseMalformed || relativeMalformed || baseMalformedPercentEncoding || relativeMalformedPercentEncoding || baseMalformedSchemeSpecific || relativeMalformedSchemeSpecific || baseMalformedHost || relativeMalformedHost || baseMalformedScheme || relativeMalformedScheme) {
|
|
3893
|
+
throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
|
|
3894
|
+
}
|
|
3895
|
+
const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
|
|
3896
|
+
const resolvedSchemeHandler = getSchemeHandler(options && options.scheme || resolved.scheme);
|
|
3897
|
+
const resolvedHost = resolved.host;
|
|
3898
|
+
const resolvedHostIsIP = resolvedHost !== void 0 && resolvedHost !== "" && (isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
|
|
3899
|
+
if (hasForbiddenDomainHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP)) {
|
|
3900
|
+
throw new Error(MALFORMED_HOST_ERROR);
|
|
3901
|
+
}
|
|
3902
|
+
canonicalizeHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
|
|
3903
|
+
const encodedASCIIHost = resolvedHost && resolvedHost.indexOf("%") !== -1 && !new RegExp("\\P{ASCII}", "u").test(resolvedHost);
|
|
3904
|
+
if (resolved.error && !encodedASCIIHost) {
|
|
3905
|
+
throw new Error(resolved.error);
|
|
3906
|
+
}
|
|
3644
3907
|
schemelessOptions.skipEscape = true;
|
|
3645
3908
|
return serialize(resolved, schemelessOptions);
|
|
3646
3909
|
}
|
|
@@ -3677,14 +3940,15 @@ var require_fast_uri = __commonJS({
|
|
|
3677
3940
|
if (relative.path[0] === "/") {
|
|
3678
3941
|
target.path = removeDotSegments(relative.path);
|
|
3679
3942
|
} else {
|
|
3680
|
-
|
|
3943
|
+
const baseHasAuthority = base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0;
|
|
3944
|
+
if (baseHasAuthority && !base.path) {
|
|
3681
3945
|
target.path = "/" + relative.path;
|
|
3682
3946
|
} else if (!base.path) {
|
|
3683
3947
|
target.path = relative.path;
|
|
3684
3948
|
} else {
|
|
3685
3949
|
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative.path;
|
|
3686
3950
|
}
|
|
3687
|
-
target.path = removeDotSegments(target.path);
|
|
3951
|
+
target.path = base.scheme === void 0 && !baseHasAuthority && target.path[0] !== "/" ? removeDotSegmentsRelative(target.path) : removeDotSegments(target.path);
|
|
3688
3952
|
}
|
|
3689
3953
|
target.query = relative.query;
|
|
3690
3954
|
}
|
|
@@ -3700,7 +3964,7 @@ var require_fast_uri = __commonJS({
|
|
|
3700
3964
|
function equal(uriA, uriB, options) {
|
|
3701
3965
|
const normalizedA = normalizeComparableURI(uriA, options);
|
|
3702
3966
|
const normalizedB = normalizeComparableURI(uriB, options);
|
|
3703
|
-
return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA
|
|
3967
|
+
return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA === normalizedB;
|
|
3704
3968
|
}
|
|
3705
3969
|
function serialize(cmpts, opts) {
|
|
3706
3970
|
const component = {
|
|
@@ -3721,19 +3985,23 @@ var require_fast_uri = __commonJS({
|
|
|
3721
3985
|
};
|
|
3722
3986
|
const options = Object.assign({}, opts);
|
|
3723
3987
|
const uriTokens = [];
|
|
3988
|
+
if (component.scheme) {
|
|
3989
|
+
component.scheme = validateScheme(component.scheme);
|
|
3990
|
+
}
|
|
3724
3991
|
const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
|
|
3725
3992
|
if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
|
|
3993
|
+
const hasAuthority = component.userinfo !== void 0 || component.host !== void 0 || component.port !== void 0;
|
|
3994
|
+
const schemelessReference = (component.scheme === void 0 || options.reference === "suffix") && !hasAuthority;
|
|
3995
|
+
const pathNoScheme = !options.skipEscape && schemelessReference;
|
|
3726
3996
|
if (component.path !== void 0) {
|
|
3727
3997
|
if (!options.skipEscape) {
|
|
3728
|
-
component.path =
|
|
3729
|
-
if (component.scheme !== void 0) {
|
|
3730
|
-
component.path = component.path.split("%3A").join(":");
|
|
3731
|
-
}
|
|
3998
|
+
component.path = serializePathEncoding(component.path, pathNoScheme);
|
|
3732
3999
|
} else {
|
|
3733
4000
|
component.path = normalizePercentEncoding(component.path);
|
|
3734
4001
|
}
|
|
3735
4002
|
}
|
|
3736
4003
|
if (options.reference !== "suffix" && component.scheme) {
|
|
4004
|
+
component.scheme = validateScheme(component.scheme);
|
|
3737
4005
|
uriTokens.push(component.scheme, ":");
|
|
3738
4006
|
}
|
|
3739
4007
|
const authority = recomposeAuthority(component);
|
|
@@ -3749,7 +4017,10 @@ var require_fast_uri = __commonJS({
|
|
|
3749
4017
|
if (component.path !== void 0) {
|
|
3750
4018
|
let s = component.path;
|
|
3751
4019
|
if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) {
|
|
3752
|
-
s = removeDotSegments(s);
|
|
4020
|
+
s = schemelessReference && s[0] !== "/" ? removeDotSegmentsRelative(s) : removeDotSegments(s);
|
|
4021
|
+
}
|
|
4022
|
+
if (schemelessReference) {
|
|
4023
|
+
s = serializePathEncoding(s, true);
|
|
3753
4024
|
}
|
|
3754
4025
|
if (authority === void 0 && s[0] === "/" && s[1] === "/") {
|
|
3755
4026
|
s = "/%2F" + s.slice(2);
|
|
@@ -3757,15 +4028,16 @@ var require_fast_uri = __commonJS({
|
|
|
3757
4028
|
uriTokens.push(s);
|
|
3758
4029
|
}
|
|
3759
4030
|
if (component.query !== void 0) {
|
|
3760
|
-
uriTokens.push("?", component.query);
|
|
4031
|
+
uriTokens.push("?", encodeQuery(component.query));
|
|
3761
4032
|
}
|
|
3762
4033
|
if (component.fragment !== void 0) {
|
|
3763
|
-
uriTokens.push("#", component.fragment);
|
|
4034
|
+
uriTokens.push("#", encodeFragment(component.fragment));
|
|
3764
4035
|
}
|
|
3765
4036
|
return uriTokens.join("");
|
|
3766
4037
|
}
|
|
3767
4038
|
var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
|
|
3768
4039
|
var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
|
|
4040
|
+
var AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/;
|
|
3769
4041
|
function getParseError(parsed, matches) {
|
|
3770
4042
|
if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
|
|
3771
4043
|
return 'URI path must start with "/" when authority is present.';
|
|
@@ -3775,6 +4047,53 @@ var require_fast_uri = __commonJS({
|
|
|
3775
4047
|
}
|
|
3776
4048
|
return void 0;
|
|
3777
4049
|
}
|
|
4050
|
+
function hasMalformedPercentEncoding(component) {
|
|
4051
|
+
if (component === void 0) return false;
|
|
4052
|
+
let percent = component.indexOf("%");
|
|
4053
|
+
while (percent !== -1) {
|
|
4054
|
+
if (percent + 2 >= component.length || !/^[\da-f]{2}$/iu.test(component.slice(percent + 1, percent + 3))) {
|
|
4055
|
+
return true;
|
|
4056
|
+
}
|
|
4057
|
+
percent = component.indexOf("%", percent + 3);
|
|
4058
|
+
}
|
|
4059
|
+
return false;
|
|
4060
|
+
}
|
|
4061
|
+
function isIPLiteral(host) {
|
|
4062
|
+
return host[0] === "[" && host[host.length - 1] === "]";
|
|
4063
|
+
}
|
|
4064
|
+
function hasMalformedComponentPercentEncoding(matches) {
|
|
4065
|
+
const host = matches[4];
|
|
4066
|
+
return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !isIPLiteral(host) && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
|
|
4067
|
+
}
|
|
4068
|
+
function canonicalizeHost(parsed, options, schemeHandler, isIP) {
|
|
4069
|
+
if (hasForbiddenDomainHost(parsed, options, schemeHandler, isIP)) {
|
|
4070
|
+
parsed.error = parsed.error || MALFORMED_HOST_ERROR;
|
|
4071
|
+
return true;
|
|
4072
|
+
}
|
|
4073
|
+
if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
|
|
4074
|
+
let url;
|
|
4075
|
+
try {
|
|
4076
|
+
url = new URL("http://" + parsed.host);
|
|
4077
|
+
} catch (e) {
|
|
4078
|
+
parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
|
|
4079
|
+
return true;
|
|
4080
|
+
}
|
|
4081
|
+
if (!isHostOnlyURL(url)) {
|
|
4082
|
+
parsed.error = parsed.error || MALFORMED_HOST_ERROR;
|
|
4083
|
+
return true;
|
|
4084
|
+
}
|
|
4085
|
+
parsed.host = url.hostname;
|
|
4086
|
+
}
|
|
4087
|
+
return false;
|
|
4088
|
+
}
|
|
4089
|
+
function hasForbiddenDomainHost(parsed, options, schemeHandler, isIP) {
|
|
4090
|
+
return Boolean(
|
|
4091
|
+
parsed.host && isIP === false && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && FORBIDDEN_DOMAIN_HOST_CHARACTER.test(parsed.host)
|
|
4092
|
+
);
|
|
4093
|
+
}
|
|
4094
|
+
function isHostOnlyURL(url) {
|
|
4095
|
+
return url.username === "" && url.password === "" && url.port === "" && url.pathname === "/" && url.search === "" && url.hash === "" && url.hostname !== "";
|
|
4096
|
+
}
|
|
3778
4097
|
function parseWithStatus(uri, opts) {
|
|
3779
4098
|
const options = Object.assign({}, opts);
|
|
3780
4099
|
const parsed = {
|
|
@@ -3787,6 +4106,11 @@ var require_fast_uri = __commonJS({
|
|
|
3787
4106
|
fragment: void 0
|
|
3788
4107
|
};
|
|
3789
4108
|
let malformedAuthorityOrPort = false;
|
|
4109
|
+
let malformedPercentEncoding = false;
|
|
4110
|
+
let malformedSchemeSpecific = false;
|
|
4111
|
+
let malformedHost = false;
|
|
4112
|
+
let malformedIPLiteral = false;
|
|
4113
|
+
let malformedScheme = false;
|
|
3790
4114
|
let isIP = false;
|
|
3791
4115
|
if (options.reference === "suffix") {
|
|
3792
4116
|
if (options.scheme) {
|
|
@@ -3800,6 +4124,20 @@ var require_fast_uri = __commonJS({
|
|
|
3800
4124
|
parsed.error = "URI authority must not contain a literal backslash.";
|
|
3801
4125
|
malformedAuthorityOrPort = true;
|
|
3802
4126
|
}
|
|
4127
|
+
const introducerMatch = uri.match(AUTHORITY_INTRODUCER_REGION);
|
|
4128
|
+
if (introducerMatch !== null) {
|
|
4129
|
+
const region = introducerMatch[1];
|
|
4130
|
+
const normalizedRegion = region.replace(/[\t\n\r]/g, "");
|
|
4131
|
+
if (normalizedRegion.length >= 2) {
|
|
4132
|
+
if (normalizedRegion.slice(0, 2) !== "//") {
|
|
4133
|
+
parsed.error = parsed.error || "URI authority must not contain a literal backslash.";
|
|
4134
|
+
malformedAuthorityOrPort = true;
|
|
4135
|
+
} else if (region.length !== normalizedRegion.length) {
|
|
4136
|
+
parsed.error = parsed.error || "URI authority introducer must not contain whitespace.";
|
|
4137
|
+
malformedAuthorityOrPort = true;
|
|
4138
|
+
}
|
|
4139
|
+
}
|
|
4140
|
+
}
|
|
3803
4141
|
const matches = uri.match(URI_PARSE);
|
|
3804
4142
|
if (matches) {
|
|
3805
4143
|
parsed.scheme = matches[1];
|
|
@@ -3809,6 +4147,18 @@ var require_fast_uri = __commonJS({
|
|
|
3809
4147
|
parsed.path = matches[6] || "";
|
|
3810
4148
|
parsed.query = matches[7];
|
|
3811
4149
|
parsed.fragment = matches[8];
|
|
4150
|
+
if (parsed.scheme !== void 0) {
|
|
4151
|
+
if (VALID_SCHEME.test(parsed.scheme)) {
|
|
4152
|
+
parsed.scheme = parsed.scheme.toLowerCase();
|
|
4153
|
+
} else {
|
|
4154
|
+
parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
|
|
4155
|
+
malformedScheme = true;
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
malformedPercentEncoding = hasMalformedComponentPercentEncoding(matches);
|
|
4159
|
+
if (malformedPercentEncoding) {
|
|
4160
|
+
parsed.error = parsed.error || "URI contains malformed percent-encoding.";
|
|
4161
|
+
}
|
|
3812
4162
|
if (isNaN(parsed.port)) {
|
|
3813
4163
|
parsed.port = matches[5];
|
|
3814
4164
|
}
|
|
@@ -3820,9 +4170,16 @@ var require_fast_uri = __commonJS({
|
|
|
3820
4170
|
if (parsed.host) {
|
|
3821
4171
|
const ipv4result = isIPv4(parsed.host);
|
|
3822
4172
|
if (ipv4result === false) {
|
|
4173
|
+
const bracketedIPLiteral = isIPLiteral(parsed.host);
|
|
4174
|
+
const hasIPLiteralBracket = parsed.host.indexOf("[") !== -1 || parsed.host.indexOf("]") !== -1;
|
|
3823
4175
|
const ipv6result = normalizeIPv6(parsed.host);
|
|
3824
|
-
|
|
3825
|
-
|
|
4176
|
+
isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
|
|
4177
|
+
malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
|
|
4178
|
+
parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
|
|
4179
|
+
if (malformedIPLiteral) {
|
|
4180
|
+
parsed.error = parsed.error || MALFORMED_HOST_ERROR;
|
|
4181
|
+
malformedAuthorityOrPort = true;
|
|
4182
|
+
}
|
|
3826
4183
|
} else {
|
|
3827
4184
|
isIP = true;
|
|
3828
4185
|
}
|
|
@@ -3840,42 +4197,37 @@ var require_fast_uri = __commonJS({
|
|
|
3840
4197
|
parsed.error = parsed.error || "URI is not a " + options.reference + " reference.";
|
|
3841
4198
|
}
|
|
3842
4199
|
const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
|
|
3843
|
-
if (!
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
4200
|
+
if (!malformedIPLiteral) {
|
|
4201
|
+
malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
|
|
4202
|
+
}
|
|
4203
|
+
if (uri.indexOf("%") !== -1 && parsed.host !== void 0 && !malformedIPLiteral) {
|
|
4204
|
+
let host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
|
|
4205
|
+
if (!isIP) {
|
|
4206
|
+
host = normalizePercentEncoding(host.toLowerCase());
|
|
3850
4207
|
}
|
|
4208
|
+
parsed.host = isIP || options.scheme !== "null" ? reescapeHostDelimiters(host, isIP) : host;
|
|
3851
4209
|
}
|
|
3852
4210
|
if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
|
|
3853
|
-
if (uri.indexOf("%") !== -1) {
|
|
3854
|
-
if (parsed.scheme !== void 0) {
|
|
3855
|
-
parsed.scheme = unescape(parsed.scheme);
|
|
3856
|
-
}
|
|
3857
|
-
if (parsed.host !== void 0) {
|
|
3858
|
-
parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
|
|
3859
|
-
}
|
|
3860
|
-
}
|
|
3861
4211
|
if (parsed.path) {
|
|
3862
4212
|
parsed.path = normalizePathEncoding(parsed.path);
|
|
3863
4213
|
}
|
|
4214
|
+
if (parsed.query) {
|
|
4215
|
+
parsed.query = normalizeQueryFragmentEncoding(parsed.query);
|
|
4216
|
+
}
|
|
3864
4217
|
if (parsed.fragment) {
|
|
3865
|
-
|
|
3866
|
-
parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
|
|
3867
|
-
} catch {
|
|
3868
|
-
parsed.error = parsed.error || "URI malformed";
|
|
3869
|
-
}
|
|
4218
|
+
parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
|
|
3870
4219
|
}
|
|
3871
4220
|
}
|
|
3872
4221
|
if (schemeHandler && schemeHandler.parse) {
|
|
3873
4222
|
schemeHandler.parse(parsed, options);
|
|
4223
|
+
if (schemeHandler === SCHEMES.urn && parsed.nid === void 0) {
|
|
4224
|
+
malformedSchemeSpecific = true;
|
|
4225
|
+
}
|
|
3874
4226
|
}
|
|
3875
4227
|
} else {
|
|
3876
4228
|
parsed.error = parsed.error || "URI can not be parsed.";
|
|
3877
4229
|
}
|
|
3878
|
-
return { parsed, malformedAuthorityOrPort };
|
|
4230
|
+
return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
|
|
3879
4231
|
}
|
|
3880
4232
|
function parse(uri, opts) {
|
|
3881
4233
|
return parseWithStatus(uri, opts).parsed;
|
|
@@ -3884,20 +4236,28 @@ var require_fast_uri = __commonJS({
|
|
|
3884
4236
|
return normalizeStringWithStatus(uri, opts).normalized;
|
|
3885
4237
|
}
|
|
3886
4238
|
function normalizeStringWithStatus(uri, opts) {
|
|
3887
|
-
const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
|
|
4239
|
+
const { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = parseWithStatus(uri, opts);
|
|
3888
4240
|
return {
|
|
3889
|
-
normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
|
|
3890
|
-
malformedAuthorityOrPort
|
|
4241
|
+
normalized: malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? uri : serialize(parsed, opts),
|
|
4242
|
+
malformedAuthorityOrPort,
|
|
4243
|
+
malformedPercentEncoding,
|
|
4244
|
+
malformedSchemeSpecific,
|
|
4245
|
+
malformedHost,
|
|
4246
|
+
malformedScheme
|
|
3891
4247
|
};
|
|
3892
4248
|
}
|
|
3893
4249
|
function normalizeComparableURI(uri, opts) {
|
|
3894
|
-
if (typeof uri
|
|
3895
|
-
|
|
3896
|
-
return malformedAuthorityOrPort ? void 0 : normalized;
|
|
4250
|
+
if (typeof uri !== "string" && typeof uri !== "object") {
|
|
4251
|
+
return void 0;
|
|
3897
4252
|
}
|
|
3898
|
-
|
|
3899
|
-
|
|
4253
|
+
let value;
|
|
4254
|
+
try {
|
|
4255
|
+
value = typeof uri === "string" ? uri : serialize(uri, opts);
|
|
4256
|
+
} catch {
|
|
4257
|
+
return void 0;
|
|
3900
4258
|
}
|
|
4259
|
+
const { normalized, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = normalizeStringWithStatus(value, opts);
|
|
4260
|
+
return malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? void 0 : normalized;
|
|
3901
4261
|
}
|
|
3902
4262
|
var fastUri = {
|
|
3903
4263
|
SCHEMES,
|
|
@@ -6980,17 +7340,33 @@ function normalizeObjectSchema(schema) {
|
|
|
6980
7340
|
}
|
|
6981
7341
|
return void 0;
|
|
6982
7342
|
}
|
|
7343
|
+
function getDotPath(path12) {
|
|
7344
|
+
if (path12.length === 0) {
|
|
7345
|
+
return "object root";
|
|
7346
|
+
}
|
|
7347
|
+
return path12.reduce((acc, seg, index) => {
|
|
7348
|
+
if (index === 0) {
|
|
7349
|
+
return String(seg);
|
|
7350
|
+
}
|
|
7351
|
+
if (typeof seg === "number") {
|
|
7352
|
+
return `${acc}[${seg}]`;
|
|
7353
|
+
}
|
|
7354
|
+
return `${acc}.${seg}`;
|
|
7355
|
+
}, "");
|
|
7356
|
+
}
|
|
6983
7357
|
function getParseErrorMessage(error) {
|
|
6984
7358
|
if (error && typeof error === "object") {
|
|
7359
|
+
if ("issues" in error && Array.isArray(error.issues) && error.issues.length > 0) {
|
|
7360
|
+
return error.issues.map((i) => {
|
|
7361
|
+
if (!i.path?.length) {
|
|
7362
|
+
return i.message;
|
|
7363
|
+
}
|
|
7364
|
+
return `${i.message} at ${getDotPath(i.path)}`;
|
|
7365
|
+
}).join("\n");
|
|
7366
|
+
}
|
|
6985
7367
|
if ("message" in error && typeof error.message === "string") {
|
|
6986
7368
|
return error.message;
|
|
6987
7369
|
}
|
|
6988
|
-
if ("issues" in error && Array.isArray(error.issues) && error.issues.length > 0) {
|
|
6989
|
-
const firstIssue = error.issues[0];
|
|
6990
|
-
if (firstIssue && typeof firstIssue === "object" && "message" in firstIssue) {
|
|
6991
|
-
return String(firstIssue.message);
|
|
6992
|
-
}
|
|
6993
|
-
}
|
|
6994
7370
|
try {
|
|
6995
7371
|
return JSON.stringify(error);
|
|
6996
7372
|
} catch {
|
|
@@ -9954,6 +10330,9 @@ var Protocol = class {
|
|
|
9954
10330
|
this.setRequestHandler(GetTaskPayloadRequestSchema, async (request, extra) => {
|
|
9955
10331
|
const handleTaskResult = async () => {
|
|
9956
10332
|
const taskId = request.params.taskId;
|
|
10333
|
+
if (!await this._taskStore.getTask(taskId, extra.sessionId)) {
|
|
10334
|
+
throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
|
|
10335
|
+
}
|
|
9957
10336
|
if (this._taskMessageQueue) {
|
|
9958
10337
|
let queuedMessage;
|
|
9959
10338
|
while (queuedMessage = await this._taskMessageQueue.dequeue(taskId, extra.sessionId)) {
|
|
@@ -9984,12 +10363,12 @@ var Protocol = class {
|
|
|
9984
10363
|
throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
|
|
9985
10364
|
}
|
|
9986
10365
|
if (!isTerminal(task.status)) {
|
|
9987
|
-
await this._waitForTaskUpdate(taskId, extra.signal);
|
|
10366
|
+
await this._waitForTaskUpdate(taskId, extra.signal, extra.sessionId);
|
|
9988
10367
|
return await handleTaskResult();
|
|
9989
10368
|
}
|
|
9990
10369
|
if (isTerminal(task.status)) {
|
|
9991
10370
|
const result = await this._taskStore.getTaskResult(taskId, extra.sessionId);
|
|
9992
|
-
this._clearTaskQueue(taskId);
|
|
10371
|
+
this._clearTaskQueue(taskId, extra.sessionId);
|
|
9993
10372
|
return {
|
|
9994
10373
|
...result,
|
|
9995
10374
|
_meta: {
|
|
@@ -10026,7 +10405,7 @@ var Protocol = class {
|
|
|
10026
10405
|
throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task in terminal status: ${task.status}`);
|
|
10027
10406
|
}
|
|
10028
10407
|
await this._taskStore.updateTaskStatus(request.params.taskId, "cancelled", "Client cancelled task execution.", extra.sessionId);
|
|
10029
|
-
this._clearTaskQueue(request.params.taskId);
|
|
10408
|
+
this._clearTaskQueue(request.params.taskId, extra.sessionId);
|
|
10030
10409
|
const cancelledTask = await this._taskStore.getTask(request.params.taskId, extra.sessionId);
|
|
10031
10410
|
if (!cancelledTask) {
|
|
10032
10411
|
throw new McpError(ErrorCode.InvalidParams, `Task not found after cancellation: ${request.params.taskId}`);
|
|
@@ -10154,6 +10533,19 @@ var Protocol = class {
|
|
|
10154
10533
|
const handler = this._requestHandlers.get(request.method) ?? this.fallbackRequestHandler;
|
|
10155
10534
|
const capturedTransport = this._transport;
|
|
10156
10535
|
const relatedTaskId = request.params?._meta?.[RELATED_TASK_META_KEY]?.taskId;
|
|
10536
|
+
const sessionId = capturedTransport?.sessionId;
|
|
10537
|
+
const store = this._taskStore;
|
|
10538
|
+
let relatedTaskFound = true;
|
|
10539
|
+
let relatedTaskLookup;
|
|
10540
|
+
if (relatedTaskId && store && this._taskMessageQueue && sessionId !== void 0) {
|
|
10541
|
+
relatedTaskFound = false;
|
|
10542
|
+
relatedTaskLookup = (async () => {
|
|
10543
|
+
if (!await store.getTask(relatedTaskId, sessionId)) {
|
|
10544
|
+
throw new McpError(ErrorCode.InvalidParams, `Task not found: ${relatedTaskId}`);
|
|
10545
|
+
}
|
|
10546
|
+
relatedTaskFound = true;
|
|
10547
|
+
})();
|
|
10548
|
+
}
|
|
10157
10549
|
if (handler === void 0) {
|
|
10158
10550
|
const errorResponse = {
|
|
10159
10551
|
jsonrpc: "2.0",
|
|
@@ -10163,7 +10555,10 @@ var Protocol = class {
|
|
|
10163
10555
|
message: "Method not found"
|
|
10164
10556
|
}
|
|
10165
10557
|
};
|
|
10166
|
-
if (relatedTaskId &&
|
|
10558
|
+
if (relatedTaskId && relatedTaskLookup) {
|
|
10559
|
+
const queuedError = { type: "error", message: errorResponse, timestamp: Date.now() };
|
|
10560
|
+
relatedTaskLookup.then(() => this._enqueueTaskMessage(relatedTaskId, queuedError, sessionId), () => capturedTransport?.send(errorResponse)).catch((error) => this._onerror(new Error(`Failed to send an error response: ${error}`)));
|
|
10561
|
+
} else if (relatedTaskId && this._taskMessageQueue) {
|
|
10167
10562
|
this._enqueueTaskMessage(relatedTaskId, {
|
|
10168
10563
|
type: "error",
|
|
10169
10564
|
message: errorResponse,
|
|
@@ -10214,7 +10609,10 @@ var Protocol = class {
|
|
|
10214
10609
|
closeSSEStream: extra?.closeSSEStream,
|
|
10215
10610
|
closeStandaloneSSEStream: extra?.closeStandaloneSSEStream
|
|
10216
10611
|
};
|
|
10217
|
-
Promise.resolve().then(() => {
|
|
10612
|
+
(relatedTaskLookup ?? Promise.resolve()).then(() => {
|
|
10613
|
+
if (relatedTaskLookup && abortController.signal.aborted) {
|
|
10614
|
+
throw new McpError(ErrorCode.ConnectionClosed, "Request was cancelled");
|
|
10615
|
+
}
|
|
10218
10616
|
if (taskCreationParams) {
|
|
10219
10617
|
this.assertTaskHandlerCapability(request.method);
|
|
10220
10618
|
}
|
|
@@ -10249,7 +10647,7 @@ var Protocol = class {
|
|
|
10249
10647
|
...error["data"] !== void 0 && { data: error["data"] }
|
|
10250
10648
|
}
|
|
10251
10649
|
};
|
|
10252
|
-
if (relatedTaskId && this._taskMessageQueue) {
|
|
10650
|
+
if (relatedTaskId && this._taskMessageQueue && relatedTaskFound) {
|
|
10253
10651
|
await this._enqueueTaskMessage(relatedTaskId, {
|
|
10254
10652
|
type: "error",
|
|
10255
10653
|
message: errorResponse,
|
|
@@ -10729,7 +11127,7 @@ var Protocol = class {
|
|
|
10729
11127
|
throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");
|
|
10730
11128
|
}
|
|
10731
11129
|
const maxQueueSize = this._options?.maxTaskQueueSize;
|
|
10732
|
-
await this._taskMessageQueue.enqueue(taskId, message, sessionId, maxQueueSize);
|
|
11130
|
+
await this._taskMessageQueue.enqueue(taskId, message, sessionId ?? this._transport?.sessionId, maxQueueSize);
|
|
10733
11131
|
}
|
|
10734
11132
|
/**
|
|
10735
11133
|
* Clears the message queue for a task and rejects any pending request resolvers.
|
|
@@ -10758,12 +11156,13 @@ var Protocol = class {
|
|
|
10758
11156
|
* Uses polling to check for updates at the task's configured poll interval.
|
|
10759
11157
|
* @param taskId The task ID to wait for
|
|
10760
11158
|
* @param signal Abort signal to cancel the wait
|
|
11159
|
+
* @param sessionId Session of the request that waits, passed to the task store
|
|
10761
11160
|
* @returns Promise that resolves when an update occurs or rejects if aborted
|
|
10762
11161
|
*/
|
|
10763
|
-
async _waitForTaskUpdate(taskId, signal) {
|
|
11162
|
+
async _waitForTaskUpdate(taskId, signal, sessionId) {
|
|
10764
11163
|
let interval = this._options?.defaultTaskPollInterval ?? 1e3;
|
|
10765
11164
|
try {
|
|
10766
|
-
const task = await this._taskStore?.getTask(taskId);
|
|
11165
|
+
const task = await this._taskStore?.getTask(taskId, sessionId);
|
|
10767
11166
|
if (task?.pollInterval) {
|
|
10768
11167
|
interval = task.pollInterval;
|
|
10769
11168
|
}
|
|
@@ -11249,16 +11648,7 @@ var Server = class extends Protocol {
|
|
|
11249
11648
|
if (!methodSchema) {
|
|
11250
11649
|
throw new Error("Schema is missing a method literal");
|
|
11251
11650
|
}
|
|
11252
|
-
|
|
11253
|
-
if (isZ4Schema(methodSchema)) {
|
|
11254
|
-
const v4Schema = methodSchema;
|
|
11255
|
-
const v4Def = v4Schema._zod?.def;
|
|
11256
|
-
methodValue = v4Def?.value ?? v4Schema.value;
|
|
11257
|
-
} else {
|
|
11258
|
-
const v3Schema = methodSchema;
|
|
11259
|
-
const legacyDef = v3Schema._def;
|
|
11260
|
-
methodValue = legacyDef?.value ?? v3Schema.value;
|
|
11261
|
-
}
|
|
11651
|
+
const methodValue = getLiteralValue(methodSchema);
|
|
11262
11652
|
if (typeof methodValue !== "string") {
|
|
11263
11653
|
throw new Error("Schema method literal must be a string");
|
|
11264
11654
|
}
|
|
@@ -11652,6 +12042,42 @@ var ExperimentalMcpServerTasks = class {
|
|
|
11652
12042
|
|
|
11653
12043
|
// ../node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
|
|
11654
12044
|
import { ZodOptional } from "zod";
|
|
12045
|
+
function toolInputElementCount(value, max) {
|
|
12046
|
+
let count = 0;
|
|
12047
|
+
const stack = [value];
|
|
12048
|
+
while (stack.length > 0) {
|
|
12049
|
+
const node = stack.pop();
|
|
12050
|
+
if (node === null || typeof node !== "object")
|
|
12051
|
+
continue;
|
|
12052
|
+
if (Array.isArray(node)) {
|
|
12053
|
+
for (const child of node) {
|
|
12054
|
+
if (++count > max)
|
|
12055
|
+
return count;
|
|
12056
|
+
if (child !== null && typeof child === "object")
|
|
12057
|
+
stack.push(child);
|
|
12058
|
+
}
|
|
12059
|
+
} else {
|
|
12060
|
+
for (const key in node) {
|
|
12061
|
+
if (!Object.prototype.hasOwnProperty.call(node, key))
|
|
12062
|
+
continue;
|
|
12063
|
+
if (++count > max)
|
|
12064
|
+
return count;
|
|
12065
|
+
const child = node[key];
|
|
12066
|
+
if (child !== null && typeof child === "object")
|
|
12067
|
+
stack.push(child);
|
|
12068
|
+
}
|
|
12069
|
+
}
|
|
12070
|
+
}
|
|
12071
|
+
return count;
|
|
12072
|
+
}
|
|
12073
|
+
function resolveMaxToolInputElements(value) {
|
|
12074
|
+
if (value === void 0 || value === Infinity)
|
|
12075
|
+
return void 0;
|
|
12076
|
+
if (typeof value !== "number" || Number.isNaN(value) || value < 1) {
|
|
12077
|
+
throw new RangeError(`maxToolInputElements must be a number of at least 1, or Infinity, got ${String(value)}`);
|
|
12078
|
+
}
|
|
12079
|
+
return value;
|
|
12080
|
+
}
|
|
11655
12081
|
var McpServer = class {
|
|
11656
12082
|
constructor(serverInfo, options) {
|
|
11657
12083
|
this._registeredResources = {};
|
|
@@ -11663,6 +12089,16 @@ var McpServer = class {
|
|
|
11663
12089
|
this._resourceHandlersInitialized = false;
|
|
11664
12090
|
this._promptHandlersInitialized = false;
|
|
11665
12091
|
this.server = new Server(serverInfo, options);
|
|
12092
|
+
this._maxToolInputElements = resolveMaxToolInputElements(options?.maxToolInputElements);
|
|
12093
|
+
if (options?.capabilities?.tools) {
|
|
12094
|
+
this.setToolRequestHandlers();
|
|
12095
|
+
}
|
|
12096
|
+
if (options?.capabilities?.resources) {
|
|
12097
|
+
this.setResourceRequestHandlers();
|
|
12098
|
+
}
|
|
12099
|
+
if (options?.capabilities?.prompts) {
|
|
12100
|
+
this.setPromptRequestHandlers();
|
|
12101
|
+
}
|
|
11666
12102
|
}
|
|
11667
12103
|
/**
|
|
11668
12104
|
* Access experimental features.
|
|
@@ -11793,12 +12229,15 @@ var McpServer = class {
|
|
|
11793
12229
|
* Validates tool input arguments against the tool's input schema.
|
|
11794
12230
|
*/
|
|
11795
12231
|
async validateToolInput(tool, args, toolName) {
|
|
12232
|
+
if (this._maxToolInputElements !== void 0 && toolInputElementCount(args, this._maxToolInputElements) > this._maxToolInputElements) {
|
|
12233
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for tool ${toolName}: arguments contain more than the maximum of ${this._maxToolInputElements} elements`);
|
|
12234
|
+
}
|
|
11796
12235
|
if (!tool.inputSchema) {
|
|
11797
12236
|
return void 0;
|
|
11798
12237
|
}
|
|
11799
12238
|
const inputObj = normalizeObjectSchema(tool.inputSchema);
|
|
11800
12239
|
const schemaToParse = inputObj ?? tool.inputSchema;
|
|
11801
|
-
const parseResult = await safeParseAsync2(schemaToParse, args);
|
|
12240
|
+
const parseResult = await safeParseAsync2(schemaToParse, args ?? {});
|
|
11802
12241
|
if (!parseResult.success) {
|
|
11803
12242
|
const error = "error" in parseResult ? parseResult.error : "Unknown error";
|
|
11804
12243
|
const errorMessage = getParseErrorMessage(error);
|
|
@@ -12036,7 +12475,7 @@ var McpServer = class {
|
|
|
12036
12475
|
}
|
|
12037
12476
|
if (prompt.argsSchema) {
|
|
12038
12477
|
const argsObj = normalizeObjectSchema(prompt.argsSchema);
|
|
12039
|
-
const parseResult = await safeParseAsync2(argsObj, request.params.arguments);
|
|
12478
|
+
const parseResult = await safeParseAsync2(argsObj, request.params.arguments ?? {});
|
|
12040
12479
|
if (!parseResult.success) {
|
|
12041
12480
|
const error = "error" in parseResult ? parseResult.error : "Unknown error";
|
|
12042
12481
|
const errorMessage = getParseErrorMessage(error);
|
|
@@ -12447,8 +12886,17 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
12447
12886
|
import process2 from "node:process";
|
|
12448
12887
|
|
|
12449
12888
|
// ../node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
|
|
12889
|
+
var STDIO_DEFAULT_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
|
|
12450
12890
|
var ReadBuffer = class {
|
|
12891
|
+
constructor(options) {
|
|
12892
|
+
this._maxBufferSize = options?.maxBufferSize ?? STDIO_DEFAULT_MAX_BUFFER_SIZE;
|
|
12893
|
+
}
|
|
12451
12894
|
append(chunk) {
|
|
12895
|
+
const newSize = (this._buffer?.length ?? 0) + chunk.length;
|
|
12896
|
+
if (newSize > this._maxBufferSize) {
|
|
12897
|
+
this.clear();
|
|
12898
|
+
throw new Error(`ReadBuffer exceeded maximum size of ${this._maxBufferSize} bytes`);
|
|
12899
|
+
}
|
|
12452
12900
|
this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
|
|
12453
12901
|
}
|
|
12454
12902
|
readMessage() {
|
|
@@ -12476,18 +12924,24 @@ function serializeMessage(message) {
|
|
|
12476
12924
|
|
|
12477
12925
|
// ../node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
12478
12926
|
var StdioServerTransport = class {
|
|
12479
|
-
constructor(_stdin = process2.stdin, _stdout = process2.stdout) {
|
|
12927
|
+
constructor(_stdin = process2.stdin, _stdout = process2.stdout, options) {
|
|
12480
12928
|
this._stdin = _stdin;
|
|
12481
12929
|
this._stdout = _stdout;
|
|
12482
|
-
this._readBuffer = new ReadBuffer();
|
|
12483
12930
|
this._started = false;
|
|
12484
12931
|
this._ondata = (chunk) => {
|
|
12485
|
-
|
|
12486
|
-
|
|
12932
|
+
try {
|
|
12933
|
+
this._readBuffer.append(chunk);
|
|
12934
|
+
this.processReadBuffer();
|
|
12935
|
+
} catch (error) {
|
|
12936
|
+
this.onerror?.(error);
|
|
12937
|
+
this.close().catch(() => {
|
|
12938
|
+
});
|
|
12939
|
+
}
|
|
12487
12940
|
};
|
|
12488
12941
|
this._onerror = (error) => {
|
|
12489
12942
|
this.onerror?.(error);
|
|
12490
12943
|
};
|
|
12944
|
+
this._readBuffer = new ReadBuffer({ maxBufferSize: options?.maxBufferSize });
|
|
12491
12945
|
}
|
|
12492
12946
|
/**
|
|
12493
12947
|
* Starts listening for messages on stdin.
|
|
@@ -12538,98 +12992,119 @@ var StdioServerTransport = class {
|
|
|
12538
12992
|
// src/index.ts
|
|
12539
12993
|
import { z as z2 } from "zod";
|
|
12540
12994
|
|
|
12541
|
-
// src/
|
|
12542
|
-
import {
|
|
12543
|
-
|
|
12544
|
-
|
|
12545
|
-
|
|
12546
|
-
|
|
12547
|
-
|
|
12548
|
-
|
|
12549
|
-
|
|
12550
|
-
|
|
12551
|
-
|
|
12552
|
-
|
|
12553
|
-
|
|
12554
|
-
|
|
12555
|
-
|
|
12556
|
-
|
|
12557
|
-
|
|
12558
|
-
|
|
12559
|
-
|
|
12995
|
+
// src/bounded-input.ts
|
|
12996
|
+
import { Transform } from "node:stream";
|
|
12997
|
+
var BoundedStdioInput = class extends Transform {
|
|
12998
|
+
constructor(maxLineBytes = 1024 * 1024) {
|
|
12999
|
+
super();
|
|
13000
|
+
this.maxLineBytes = maxLineBytes;
|
|
13001
|
+
}
|
|
13002
|
+
maxLineBytes;
|
|
13003
|
+
bytes = 0;
|
|
13004
|
+
_transform(chunk, _encoding, callback) {
|
|
13005
|
+
for (const byte of chunk) {
|
|
13006
|
+
if (byte === 10) this.bytes = 0;
|
|
13007
|
+
else if (++this.bytes > this.maxLineBytes) {
|
|
13008
|
+
callback(
|
|
13009
|
+
new Error("AIPOU_INPUT_TOO_LARGE: JSON-RPC line exceeds 1 MiB")
|
|
13010
|
+
);
|
|
13011
|
+
return;
|
|
13012
|
+
}
|
|
13013
|
+
}
|
|
13014
|
+
callback(null, chunk);
|
|
12560
13015
|
}
|
|
12561
|
-
|
|
12562
|
-
return `{${Object.keys(record2).filter((key) => record2[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record2[key])}`).join(",")}}`;
|
|
12563
|
-
}
|
|
12564
|
-
function sha256Hex(value) {
|
|
12565
|
-
return `0x${createHash("sha256").update(value).digest("hex")}`;
|
|
12566
|
-
}
|
|
13016
|
+
};
|
|
12567
13017
|
|
|
12568
|
-
// src/
|
|
12569
|
-
import {
|
|
12570
|
-
|
|
12571
|
-
|
|
12572
|
-
|
|
12573
|
-
|
|
12574
|
-
|
|
12575
|
-
|
|
12576
|
-
const principal = process.env.USERDOMAIN ? `${process.env.USERDOMAIN}\\${user}` : user;
|
|
12577
|
-
try {
|
|
12578
|
-
await execFileAsync("icacls", [filePath, "/inheritance:r", "/grant:r", `${principal}:F`]);
|
|
12579
|
-
return true;
|
|
12580
|
-
} catch {
|
|
12581
|
-
return false;
|
|
13018
|
+
// src/version.ts
|
|
13019
|
+
import { readFile } from "node:fs/promises";
|
|
13020
|
+
var SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
13021
|
+
async function getPackageVersion() {
|
|
13022
|
+
const packageUrl = new URL("../package.json", import.meta.url);
|
|
13023
|
+
const metadata = JSON.parse(await readFile(packageUrl, "utf8"));
|
|
13024
|
+
if (typeof metadata.version !== "string" || !SEMVER.test(metadata.version)) {
|
|
13025
|
+
throw new Error("mcp-server/package.json must contain a valid semantic version");
|
|
12582
13026
|
}
|
|
13027
|
+
return metadata.version;
|
|
12583
13028
|
}
|
|
12584
13029
|
|
|
12585
|
-
// src/
|
|
12586
|
-
|
|
12587
|
-
|
|
12588
|
-
|
|
12589
|
-
|
|
12590
|
-
|
|
12591
|
-
|
|
12592
|
-
|
|
12593
|
-
|
|
12594
|
-
}
|
|
12595
|
-
async function ensureCollectorKeys() {
|
|
12596
|
-
await mkdir(dataDir(), { recursive: true });
|
|
13030
|
+
// src/store.ts
|
|
13031
|
+
import { mkdir as mkdir2, readFile as readFile4, readdir } from "node:fs/promises";
|
|
13032
|
+
import path3 from "node:path";
|
|
13033
|
+
|
|
13034
|
+
// src/lock.ts
|
|
13035
|
+
import { readFile as readFile2, rm, stat, writeFile } from "node:fs/promises";
|
|
13036
|
+
var STALE_LOCK_MS = 15e3;
|
|
13037
|
+
var ACQUIRE_TIMEOUT_MS = 1e4;
|
|
13038
|
+
async function reclaimDeadLock(lockPath2) {
|
|
13039
|
+
const gate = `${lockPath2}.reclaim`;
|
|
12597
13040
|
try {
|
|
12598
|
-
|
|
12599
|
-
|
|
12600
|
-
|
|
12601
|
-
|
|
12602
|
-
|
|
13041
|
+
await writeFile(gate, `${process.pid}
|
|
13042
|
+
`, { flag: "wx", mode: 384 });
|
|
13043
|
+
} catch (error) {
|
|
13044
|
+
if (error.code === "EEXIST") return;
|
|
13045
|
+
throw error;
|
|
13046
|
+
}
|
|
13047
|
+
try {
|
|
13048
|
+
const info = await stat(lockPath2);
|
|
13049
|
+
if (Date.now() - info.mtimeMs <= STALE_LOCK_MS) return;
|
|
13050
|
+
const pid = Number((await readFile2(lockPath2, "utf8")).trim());
|
|
13051
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) return;
|
|
13052
|
+
try {
|
|
13053
|
+
process.kill(pid, 0);
|
|
13054
|
+
} catch (error) {
|
|
13055
|
+
if (error.code === "ESRCH") await rm(lockPath2);
|
|
13056
|
+
}
|
|
12603
13057
|
} catch (error) {
|
|
12604
13058
|
if (error.code !== "ENOENT") throw error;
|
|
13059
|
+
} finally {
|
|
13060
|
+
await rm(gate, { force: true });
|
|
12605
13061
|
}
|
|
12606
|
-
const pair = generateKeyPairSync("ed25519");
|
|
12607
|
-
const privateKey = pair.privateKey.export({ format: "pem", type: "pkcs8" }).toString();
|
|
12608
|
-
const publicKey = pair.publicKey.export({ format: "pem", type: "spki" }).toString();
|
|
12609
|
-
await writeFile(privateKeyPath(), privateKey, { encoding: "utf8", mode: 384, flag: "wx" });
|
|
12610
|
-
await restrictToCurrentUser(privateKeyPath());
|
|
12611
|
-
await writeFile(publicKeyPath(), publicKey, { encoding: "utf8", flag: "wx" });
|
|
12612
|
-
return { privateKey, publicKey };
|
|
12613
13062
|
}
|
|
12614
|
-
async function
|
|
12615
|
-
|
|
12616
|
-
|
|
12617
|
-
|
|
12618
|
-
|
|
12619
|
-
|
|
12620
|
-
|
|
12621
|
-
|
|
12622
|
-
|
|
12623
|
-
|
|
13063
|
+
async function acquire(lockPath2) {
|
|
13064
|
+
const deadline = Date.now() + ACQUIRE_TIMEOUT_MS;
|
|
13065
|
+
for (; ; ) {
|
|
13066
|
+
try {
|
|
13067
|
+
await writeFile(lockPath2, `${process.pid}
|
|
13068
|
+
`, {
|
|
13069
|
+
encoding: "utf8",
|
|
13070
|
+
flag: "wx"
|
|
13071
|
+
});
|
|
13072
|
+
return;
|
|
13073
|
+
} catch (error) {
|
|
13074
|
+
if (error.code !== "EEXIST") throw error;
|
|
13075
|
+
}
|
|
13076
|
+
try {
|
|
13077
|
+
const info = await stat(lockPath2);
|
|
13078
|
+
if (Date.now() - info.mtimeMs > STALE_LOCK_MS) {
|
|
13079
|
+
await reclaimDeadLock(lockPath2);
|
|
13080
|
+
}
|
|
13081
|
+
} catch (error) {
|
|
13082
|
+
if (error.code === "ENOENT") continue;
|
|
13083
|
+
throw error;
|
|
13084
|
+
}
|
|
13085
|
+
if (Date.now() > deadline) {
|
|
13086
|
+
throw new Error(
|
|
13087
|
+
"AIPOU operation is busy; retry when the other process finishes. Never remove an active lock."
|
|
13088
|
+
);
|
|
13089
|
+
}
|
|
13090
|
+
await new Promise(
|
|
13091
|
+
(resolve) => setTimeout(resolve, 25 + Math.floor(Math.random() * 75))
|
|
13092
|
+
);
|
|
13093
|
+
}
|
|
12624
13094
|
}
|
|
12625
|
-
function
|
|
12626
|
-
|
|
13095
|
+
async function withFileLock(lockPath2, operation) {
|
|
13096
|
+
await acquire(lockPath2);
|
|
13097
|
+
try {
|
|
13098
|
+
return await operation();
|
|
13099
|
+
} finally {
|
|
13100
|
+
await rm(lockPath2, { force: true });
|
|
13101
|
+
}
|
|
12627
13102
|
}
|
|
12628
13103
|
|
|
12629
13104
|
// src/identity.ts
|
|
12630
13105
|
import { randomBytes } from "node:crypto";
|
|
12631
13106
|
import { readFileSync } from "node:fs";
|
|
12632
|
-
import
|
|
13107
|
+
import path from "node:path";
|
|
12633
13108
|
import { Wallet, getAddress, hexlify, isAddress, verifyTypedData } from "ethers";
|
|
12634
13109
|
var authorizationTypes = {
|
|
12635
13110
|
TaskAuthorization: [
|
|
@@ -12641,9 +13116,25 @@ var authorizationTypes = {
|
|
|
12641
13116
|
{ name: "issuedAt", type: "uint256" }
|
|
12642
13117
|
]
|
|
12643
13118
|
};
|
|
13119
|
+
var DEFAULT_TASK_SESSION_TTL_SECONDS = 24 * 60 * 60;
|
|
13120
|
+
function taskSessionTtlSeconds() {
|
|
13121
|
+
const configured = process.env.AIPOU_TASK_SESSION_TTL_SECONDS;
|
|
13122
|
+
if (!configured) return DEFAULT_TASK_SESSION_TTL_SECONDS;
|
|
13123
|
+
const value = Number.parseInt(configured, 10);
|
|
13124
|
+
if (!Number.isInteger(value) || value < 60 || value > 7 * DEFAULT_TASK_SESSION_TTL_SECONDS) {
|
|
13125
|
+
throw new Error("AIPOU_TASK_SESSION_TTL_SECONDS must be an integer from 60 through 604800");
|
|
13126
|
+
}
|
|
13127
|
+
return value;
|
|
13128
|
+
}
|
|
13129
|
+
function taskSessionExpiry(session) {
|
|
13130
|
+
return session.expiresAt ?? session.issuedAt + taskSessionTtlSeconds();
|
|
13131
|
+
}
|
|
13132
|
+
function isTaskSessionExpired(session, nowSeconds = Math.floor(Date.now() / 1e3)) {
|
|
13133
|
+
return nowSeconds >= taskSessionExpiry(session);
|
|
13134
|
+
}
|
|
12644
13135
|
function readKeyFile(keyFile) {
|
|
12645
13136
|
try {
|
|
12646
|
-
return readFileSync(
|
|
13137
|
+
return readFileSync(path.resolve(keyFile), "utf8").trim();
|
|
12647
13138
|
} catch {
|
|
12648
13139
|
throw new Error(
|
|
12649
13140
|
`AIPOU_AGENT_KEY_FILE could not be read (${keyFile}); run aipou-mcp --init to create a dedicated wallet key`
|
|
@@ -12681,7 +13172,14 @@ async function createTaskSession(input) {
|
|
|
12681
13172
|
issuedAt
|
|
12682
13173
|
};
|
|
12683
13174
|
const walletAuthorization = await wallet.signTypedData(domain, authorizationTypes, value);
|
|
12684
|
-
return {
|
|
13175
|
+
return {
|
|
13176
|
+
...input,
|
|
13177
|
+
wallet: wallet.address,
|
|
13178
|
+
nonce,
|
|
13179
|
+
issuedAt,
|
|
13180
|
+
expiresAt: issuedAt + taskSessionTtlSeconds(),
|
|
13181
|
+
walletAuthorization
|
|
13182
|
+
};
|
|
12685
13183
|
}
|
|
12686
13184
|
function verifyTaskAuthorization(session) {
|
|
12687
13185
|
const domain = {
|
|
@@ -12701,6 +13199,315 @@ function verifyTaskAuthorization(session) {
|
|
|
12701
13199
|
return verifyTypedData(domain, authorizationTypes, value, session.walletAuthorization) === session.wallet;
|
|
12702
13200
|
}
|
|
12703
13201
|
|
|
13202
|
+
// src/canonical.ts
|
|
13203
|
+
import { createHash } from "node:crypto";
|
|
13204
|
+
function canonicalJson(value) {
|
|
13205
|
+
if (value === null || typeof value !== "object") {
|
|
13206
|
+
return JSON.stringify(value);
|
|
13207
|
+
}
|
|
13208
|
+
if (Array.isArray(value)) {
|
|
13209
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
13210
|
+
}
|
|
13211
|
+
const record2 = value;
|
|
13212
|
+
return `{${Object.keys(record2).filter((key) => record2[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record2[key])}`).join(",")}}`;
|
|
13213
|
+
}
|
|
13214
|
+
function sha256Hex(value) {
|
|
13215
|
+
return `0x${createHash("sha256").update(value).digest("hex")}`;
|
|
13216
|
+
}
|
|
13217
|
+
|
|
13218
|
+
// src/private-json.ts
|
|
13219
|
+
import { mkdir, open, rename, readFile as readFile3 } from "node:fs/promises";
|
|
13220
|
+
import path2 from "node:path";
|
|
13221
|
+
|
|
13222
|
+
// src/secure-file.ts
|
|
13223
|
+
import { execFile } from "node:child_process";
|
|
13224
|
+
import { promisify } from "node:util";
|
|
13225
|
+
var execFileAsync = promisify(execFile);
|
|
13226
|
+
async function restrictToCurrentUser(filePath) {
|
|
13227
|
+
if (process.platform !== "win32") return true;
|
|
13228
|
+
const user = process.env.USERNAME;
|
|
13229
|
+
if (!user) return false;
|
|
13230
|
+
const principal = process.env.USERDOMAIN ? `${process.env.USERDOMAIN}\\${user}` : user;
|
|
13231
|
+
try {
|
|
13232
|
+
await execFileAsync("icacls", [filePath, "/inheritance:r", "/grant:r", `${principal}:F`]);
|
|
13233
|
+
return true;
|
|
13234
|
+
} catch {
|
|
13235
|
+
return false;
|
|
13236
|
+
}
|
|
13237
|
+
}
|
|
13238
|
+
|
|
13239
|
+
// src/private-json.ts
|
|
13240
|
+
async function writePrivateJson(file, value) {
|
|
13241
|
+
await mkdir(path2.dirname(file), { recursive: true, mode: 448 });
|
|
13242
|
+
const temp = `${file}.${process.pid}.tmp`;
|
|
13243
|
+
const handle = await open(temp, "w", 384);
|
|
13244
|
+
try {
|
|
13245
|
+
if (process.platform !== "win32") await handle.chmod(384);
|
|
13246
|
+
await handle.writeFile(JSON.stringify(value, null, 2) + "\n");
|
|
13247
|
+
await handle.sync();
|
|
13248
|
+
} finally {
|
|
13249
|
+
await handle.close();
|
|
13250
|
+
}
|
|
13251
|
+
if (!await restrictToCurrentUser(temp))
|
|
13252
|
+
throw new Error(
|
|
13253
|
+
"Could not restrict private state file access; update was not committed"
|
|
13254
|
+
);
|
|
13255
|
+
await rename(temp, file);
|
|
13256
|
+
if (process.platform !== "win32") {
|
|
13257
|
+
const directory = await open(path2.dirname(file), "r");
|
|
13258
|
+
try {
|
|
13259
|
+
await directory.sync();
|
|
13260
|
+
} finally {
|
|
13261
|
+
await directory.close();
|
|
13262
|
+
}
|
|
13263
|
+
}
|
|
13264
|
+
}
|
|
13265
|
+
async function readOptionalJson(file) {
|
|
13266
|
+
try {
|
|
13267
|
+
return JSON.parse(await readFile3(file, "utf8"));
|
|
13268
|
+
} catch (error) {
|
|
13269
|
+
if (error.code === "ENOENT") return null;
|
|
13270
|
+
throw error;
|
|
13271
|
+
}
|
|
13272
|
+
}
|
|
13273
|
+
|
|
13274
|
+
// src/store.ts
|
|
13275
|
+
var emptyState = () => ({ sessions: {}, receipts: [], completedEvidence: {}, batches: [] });
|
|
13276
|
+
var mutationQueue = Promise.resolve();
|
|
13277
|
+
function dataDir() {
|
|
13278
|
+
return path3.resolve(process.env.AIPOU_DATA_DIR || ".aipou");
|
|
13279
|
+
}
|
|
13280
|
+
function statePath() {
|
|
13281
|
+
return path3.join(dataDir(), "state.json");
|
|
13282
|
+
}
|
|
13283
|
+
function lockPath() {
|
|
13284
|
+
return `${statePath()}.lock`;
|
|
13285
|
+
}
|
|
13286
|
+
function archiveDir() {
|
|
13287
|
+
return path3.join(dataDir(), "settled");
|
|
13288
|
+
}
|
|
13289
|
+
async function readState() {
|
|
13290
|
+
try {
|
|
13291
|
+
return JSON.parse(await readFile4(statePath(), "utf8"));
|
|
13292
|
+
} catch (error) {
|
|
13293
|
+
if (error.code === "ENOENT") return emptyState();
|
|
13294
|
+
throw error;
|
|
13295
|
+
}
|
|
13296
|
+
}
|
|
13297
|
+
async function writeState(state) {
|
|
13298
|
+
await writePrivateJson(statePath(), state);
|
|
13299
|
+
}
|
|
13300
|
+
async function mutate(operation) {
|
|
13301
|
+
const next = mutationQueue.then(async () => {
|
|
13302
|
+
await mkdir2(dataDir(), { recursive: true });
|
|
13303
|
+
return withFileLock(lockPath(), async () => {
|
|
13304
|
+
const state = await readState();
|
|
13305
|
+
const result = await operation(state);
|
|
13306
|
+
await writeState(state);
|
|
13307
|
+
return result;
|
|
13308
|
+
});
|
|
13309
|
+
});
|
|
13310
|
+
mutationQueue = next.catch(() => void 0);
|
|
13311
|
+
return next;
|
|
13312
|
+
}
|
|
13313
|
+
async function saveSession(session) {
|
|
13314
|
+
await mutate((state) => {
|
|
13315
|
+
if (state.sessions[session.nonce]) throw new Error("Task nonce already exists");
|
|
13316
|
+
state.sessions[session.nonce] = session;
|
|
13317
|
+
});
|
|
13318
|
+
}
|
|
13319
|
+
async function getSession(nonce) {
|
|
13320
|
+
return (await readState()).sessions[nonce] || null;
|
|
13321
|
+
}
|
|
13322
|
+
async function saveCompletedReceipt(evidenceKey, receipt) {
|
|
13323
|
+
await mutate((state) => {
|
|
13324
|
+
const session = state.sessions[receipt.nonce];
|
|
13325
|
+
if (!session) throw new Error("Unknown task nonce");
|
|
13326
|
+
if (session.completedAt) throw new Error("Task nonce has already been completed; recover its stored receipt with export_ai_receipts and match the nonce.");
|
|
13327
|
+
if (state.completedEvidence[evidenceKey]) {
|
|
13328
|
+
throw new Error(`Duplicate task/output evidence already recorded as ${state.completedEvidence[evidenceKey]}`);
|
|
13329
|
+
}
|
|
13330
|
+
if (state.receipts.some((item) => item.receiptId === receipt.receiptId)) {
|
|
13331
|
+
throw new Error("Receipt id has already been recorded");
|
|
13332
|
+
}
|
|
13333
|
+
if ((state.settledReceiptIds ?? []).includes(receipt.receiptId)) {
|
|
13334
|
+
throw new Error("Receipt id has already been settled and archived");
|
|
13335
|
+
}
|
|
13336
|
+
session.completedAt = receipt.recordedAt;
|
|
13337
|
+
state.completedEvidence[evidenceKey] = receipt.receiptId;
|
|
13338
|
+
state.receipts.push(receipt);
|
|
13339
|
+
});
|
|
13340
|
+
}
|
|
13341
|
+
async function readArchivedReceipts() {
|
|
13342
|
+
let files;
|
|
13343
|
+
try {
|
|
13344
|
+
files = await readdir(archiveDir());
|
|
13345
|
+
} catch (error) {
|
|
13346
|
+
if (error.code === "ENOENT") return [];
|
|
13347
|
+
throw error;
|
|
13348
|
+
}
|
|
13349
|
+
const receipts = [];
|
|
13350
|
+
for (const file of files.filter((name) => name.endsWith(".json")).sort()) {
|
|
13351
|
+
receipts.push(...JSON.parse(await readFile4(path3.join(archiveDir(), file), "utf8")));
|
|
13352
|
+
}
|
|
13353
|
+
return receipts;
|
|
13354
|
+
}
|
|
13355
|
+
async function exportStoredReceipts(wallet) {
|
|
13356
|
+
const unique = /* @__PURE__ */ new Map();
|
|
13357
|
+
for (const receipt of [...(await readState()).receipts, ...await readArchivedReceipts()]) {
|
|
13358
|
+
const prior = unique.get(receipt.receiptId);
|
|
13359
|
+
if (!prior?.claimTransaction) unique.set(receipt.receiptId, receipt);
|
|
13360
|
+
}
|
|
13361
|
+
const receipts = [...unique.values()];
|
|
13362
|
+
if (!wallet) return receipts;
|
|
13363
|
+
return receipts.filter((receipt) => receipt.wallet.toLowerCase() === wallet.toLowerCase());
|
|
13364
|
+
}
|
|
13365
|
+
async function unsettledReceipts(limit) {
|
|
13366
|
+
return (await readState()).receipts.filter((receipt) => !receipt.claimTransaction).slice(0, limit);
|
|
13367
|
+
}
|
|
13368
|
+
async function protocolStateCounts() {
|
|
13369
|
+
const state = await readState();
|
|
13370
|
+
const sessions = Object.values(state.sessions);
|
|
13371
|
+
return {
|
|
13372
|
+
sessions: sessions.length,
|
|
13373
|
+
completedSessions: sessions.filter((session) => session.completedAt).length,
|
|
13374
|
+
incompleteSessions: sessions.filter((session) => !session.completedAt).length,
|
|
13375
|
+
activeSessions: sessions.filter((session) => !session.completedAt && !isTaskSessionExpired(session)).length,
|
|
13376
|
+
expiredSessions: sessions.filter((session) => !session.completedAt && isTaskSessionExpired(session)).length,
|
|
13377
|
+
batches: state.batches.length
|
|
13378
|
+
};
|
|
13379
|
+
}
|
|
13380
|
+
async function settledReceiptsSince(sinceMs) {
|
|
13381
|
+
const receipts = await exportStoredReceipts();
|
|
13382
|
+
return receipts.filter(
|
|
13383
|
+
(receipt) => receipt.claimTransaction && Date.parse(receipt.recordedAt) >= sinceMs
|
|
13384
|
+
);
|
|
13385
|
+
}
|
|
13386
|
+
async function markBatchSettled(batch) {
|
|
13387
|
+
await mutate(async (state) => {
|
|
13388
|
+
const existing = state.batches.find((item) => item.root === batch.root);
|
|
13389
|
+
if (existing) {
|
|
13390
|
+
if (existing.claimTransaction !== batch.claimTransaction) throw new Error("Settlement archive transaction mismatch");
|
|
13391
|
+
return;
|
|
13392
|
+
}
|
|
13393
|
+
const included = new Set(batch.receiptIds);
|
|
13394
|
+
const settled = [];
|
|
13395
|
+
for (const receipt of state.receipts) {
|
|
13396
|
+
if (included.has(receipt.receiptId)) {
|
|
13397
|
+
if (receipt.claimTransaction) throw new Error(`Receipt ${receipt.receiptId} is already settled`);
|
|
13398
|
+
receipt.batchRoot = batch.root;
|
|
13399
|
+
receipt.claimTransaction = batch.claimTransaction;
|
|
13400
|
+
settled.push(receipt);
|
|
13401
|
+
}
|
|
13402
|
+
}
|
|
13403
|
+
await mkdir2(archiveDir(), { recursive: true });
|
|
13404
|
+
const archivePath = path3.join(archiveDir(), `batch-${batch.root.slice(2)}.json`);
|
|
13405
|
+
try {
|
|
13406
|
+
const prior = JSON.parse(await readFile4(archivePath, "utf8"));
|
|
13407
|
+
if (canonicalJson(prior) !== canonicalJson(settled)) throw new Error("Settlement archive does not match saved batch");
|
|
13408
|
+
} catch (error) {
|
|
13409
|
+
if (error.code !== "ENOENT") throw error;
|
|
13410
|
+
await writePrivateJson(archivePath, settled);
|
|
13411
|
+
}
|
|
13412
|
+
state.receipts = state.receipts.filter((receipt) => !included.has(receipt.receiptId));
|
|
13413
|
+
state.settledReceiptIds = [...state.settledReceiptIds ?? [], ...batch.receiptIds];
|
|
13414
|
+
state.batches.push(batch);
|
|
13415
|
+
});
|
|
13416
|
+
}
|
|
13417
|
+
|
|
13418
|
+
// src/feedback.ts
|
|
13419
|
+
async function localFeedbackSummary() {
|
|
13420
|
+
const receipts = await exportStoredReceipts();
|
|
13421
|
+
const counts = await protocolStateCounts();
|
|
13422
|
+
const activeDays = new Set(receipts.map((r) => r.recordedAt.slice(0, 10)));
|
|
13423
|
+
return {
|
|
13424
|
+
schema: "aipou-local-feedback-v1",
|
|
13425
|
+
version: await getPackageVersion(),
|
|
13426
|
+
platform: process.platform,
|
|
13427
|
+
node: process.versions.node,
|
|
13428
|
+
localReceiptCount: receipts.length,
|
|
13429
|
+
localActiveDays: activeDays.size,
|
|
13430
|
+
completedSessions: counts.completedSessions,
|
|
13431
|
+
activeSessions: counts.activeSessions,
|
|
13432
|
+
expiredSessions: counts.expiredSessions,
|
|
13433
|
+
measuredUsageReceipts: receipts.filter(
|
|
13434
|
+
(r) => r.tokenUsageMeasurement === "measured"
|
|
13435
|
+
).length,
|
|
13436
|
+
unmeasuredUsageReceipts: receipts.filter(
|
|
13437
|
+
(r) => r.tokenUsageMeasurement === "unmeasured"
|
|
13438
|
+
).length,
|
|
13439
|
+
unspecifiedUsageReceipts: receipts.filter(
|
|
13440
|
+
(r) => !r.tokenUsageMeasurement || r.tokenUsageMeasurement === "unspecified"
|
|
13441
|
+
).length,
|
|
13442
|
+
transmission: "none; review before sharing voluntarily",
|
|
13443
|
+
interpretation: "one local installation; not unique-user, organic-adoption or revenue metrics"
|
|
13444
|
+
};
|
|
13445
|
+
}
|
|
13446
|
+
|
|
13447
|
+
// src/claims.ts
|
|
13448
|
+
import { MerkleTree } from "merkletreejs";
|
|
13449
|
+
import {
|
|
13450
|
+
AbiCoder,
|
|
13451
|
+
Contract as Contract2,
|
|
13452
|
+
JsonRpcProvider as JsonRpcProvider2,
|
|
13453
|
+
Transaction as Transaction2,
|
|
13454
|
+
Wallet as Wallet3,
|
|
13455
|
+
concat,
|
|
13456
|
+
getAddress as getAddress2,
|
|
13457
|
+
getBytes,
|
|
13458
|
+
keccak256,
|
|
13459
|
+
parseUnits
|
|
13460
|
+
} from "ethers";
|
|
13461
|
+
import { mkdir as mkdir4, readFile as readFile7, rm as rm2 } from "node:fs/promises";
|
|
13462
|
+
import path6 from "node:path";
|
|
13463
|
+
|
|
13464
|
+
// src/collector.ts
|
|
13465
|
+
import { createHash as createHash2, createPublicKey, generateKeyPairSync, sign, verify } from "node:crypto";
|
|
13466
|
+
import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile2 } from "node:fs/promises";
|
|
13467
|
+
import path4 from "node:path";
|
|
13468
|
+
function dataDir2() {
|
|
13469
|
+
return path4.resolve(process.env.AIPOU_DATA_DIR || ".aipou");
|
|
13470
|
+
}
|
|
13471
|
+
function privateKeyPath() {
|
|
13472
|
+
return path4.join(dataDir2(), "collector-ed25519-private.pem");
|
|
13473
|
+
}
|
|
13474
|
+
function publicKeyPath() {
|
|
13475
|
+
return path4.join(dataDir2(), "collector-ed25519-public.pem");
|
|
13476
|
+
}
|
|
13477
|
+
async function ensureCollectorKeys() {
|
|
13478
|
+
await mkdir3(dataDir2(), { recursive: true });
|
|
13479
|
+
try {
|
|
13480
|
+
const [privateKey2, publicKey2] = await Promise.all([
|
|
13481
|
+
readFile5(privateKeyPath(), "utf8"),
|
|
13482
|
+
readFile5(publicKeyPath(), "utf8")
|
|
13483
|
+
]);
|
|
13484
|
+
return { privateKey: privateKey2, publicKey: publicKey2 };
|
|
13485
|
+
} catch (error) {
|
|
13486
|
+
if (error.code !== "ENOENT") throw error;
|
|
13487
|
+
}
|
|
13488
|
+
const pair = generateKeyPairSync("ed25519");
|
|
13489
|
+
const privateKey = pair.privateKey.export({ format: "pem", type: "pkcs8" }).toString();
|
|
13490
|
+
const publicKey = pair.publicKey.export({ format: "pem", type: "spki" }).toString();
|
|
13491
|
+
await writeFile2(privateKeyPath(), privateKey, { encoding: "utf8", mode: 384, flag: "wx" });
|
|
13492
|
+
await restrictToCurrentUser(privateKeyPath());
|
|
13493
|
+
await writeFile2(publicKeyPath(), publicKey, { encoding: "utf8", flag: "wx" });
|
|
13494
|
+
return { privateKey, publicKey };
|
|
13495
|
+
}
|
|
13496
|
+
async function getCollectorPublicKey() {
|
|
13497
|
+
return (await ensureCollectorKeys()).publicKey;
|
|
13498
|
+
}
|
|
13499
|
+
function collectorFingerprint(publicKey) {
|
|
13500
|
+
const der = createPublicKey(publicKey).export({ format: "der", type: "spki" });
|
|
13501
|
+
return `sha256:${createHash2("sha256").update(der).digest("hex")}`;
|
|
13502
|
+
}
|
|
13503
|
+
async function signCollectorPayload(payload) {
|
|
13504
|
+
const { privateKey } = await ensureCollectorKeys();
|
|
13505
|
+
return sign(null, Buffer.from(canonicalJson(payload)), privateKey).toString("base64");
|
|
13506
|
+
}
|
|
13507
|
+
function verifyCollectorPayload(payload, signature, publicKey) {
|
|
13508
|
+
return verify(null, Buffer.from(canonicalJson(payload)), publicKey, Buffer.from(signature, "base64"));
|
|
13509
|
+
}
|
|
13510
|
+
|
|
12704
13511
|
// src/policy.ts
|
|
12705
13512
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
12706
13513
|
function positiveIntFromEnv(name, fallback) {
|
|
@@ -12755,12 +13562,12 @@ function filterEligibleReceipts(candidates, recentlySettled, policy, now = Date.
|
|
|
12755
13562
|
|
|
12756
13563
|
// src/providers.ts
|
|
12757
13564
|
import { verify as verify2 } from "node:crypto";
|
|
12758
|
-
import { readFile as
|
|
12759
|
-
import
|
|
13565
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
13566
|
+
import path5 from "node:path";
|
|
12760
13567
|
async function configuredKeys() {
|
|
12761
13568
|
const file = process.env.AIPOU_PROVIDER_KEYS_FILE;
|
|
12762
13569
|
if (!file) return {};
|
|
12763
|
-
return JSON.parse(await
|
|
13570
|
+
return JSON.parse(await readFile6(path5.resolve(file), "utf8"));
|
|
12764
13571
|
}
|
|
12765
13572
|
function providerAssertion(session, input) {
|
|
12766
13573
|
return {
|
|
@@ -12787,196 +13594,303 @@ async function deriveTrustTier(session, input, evidence) {
|
|
|
12787
13594
|
return "provider_signed";
|
|
12788
13595
|
}
|
|
12789
13596
|
|
|
12790
|
-
// src/
|
|
12791
|
-
|
|
12792
|
-
|
|
12793
|
-
|
|
12794
|
-
|
|
12795
|
-
|
|
12796
|
-
|
|
12797
|
-
|
|
12798
|
-
|
|
12799
|
-
|
|
12800
|
-
|
|
12801
|
-
|
|
12802
|
-
await
|
|
12803
|
-
|
|
12804
|
-
|
|
12805
|
-
|
|
12806
|
-
|
|
12807
|
-
|
|
12808
|
-
|
|
12809
|
-
|
|
12810
|
-
|
|
12811
|
-
|
|
12812
|
-
|
|
12813
|
-
|
|
12814
|
-
|
|
12815
|
-
|
|
12816
|
-
|
|
12817
|
-
|
|
12818
|
-
throw new Error(`Timed out waiting for lock ${lockPath2}; remove it manually if no other AIPOU process is running`);
|
|
12819
|
-
}
|
|
12820
|
-
await new Promise((resolve) => setTimeout(resolve, 25 + Math.floor(Math.random() * 75)));
|
|
12821
|
-
}
|
|
12822
|
-
}
|
|
12823
|
-
async function withFileLock(lockPath2, operation) {
|
|
12824
|
-
await acquire(lockPath2);
|
|
12825
|
-
try {
|
|
12826
|
-
return await operation();
|
|
12827
|
-
} finally {
|
|
12828
|
-
await rm(lockPath2, { force: true });
|
|
12829
|
-
}
|
|
12830
|
-
}
|
|
12831
|
-
|
|
12832
|
-
// src/store.ts
|
|
12833
|
-
var emptyState = () => ({ sessions: {}, receipts: [], completedEvidence: {}, batches: [] });
|
|
12834
|
-
var mutationQueue = Promise.resolve();
|
|
12835
|
-
function dataDir2() {
|
|
12836
|
-
return path4.resolve(process.env.AIPOU_DATA_DIR || ".aipou");
|
|
12837
|
-
}
|
|
12838
|
-
function statePath() {
|
|
12839
|
-
return path4.join(dataDir2(), "state.json");
|
|
12840
|
-
}
|
|
12841
|
-
function lockPath() {
|
|
12842
|
-
return `${statePath()}.lock`;
|
|
12843
|
-
}
|
|
12844
|
-
function archiveDir() {
|
|
12845
|
-
return path4.join(dataDir2(), "settled");
|
|
12846
|
-
}
|
|
12847
|
-
async function readState() {
|
|
12848
|
-
try {
|
|
12849
|
-
return JSON.parse(await readFile3(statePath(), "utf8"));
|
|
12850
|
-
} catch (error) {
|
|
12851
|
-
if (error.code === "ENOENT") return emptyState();
|
|
12852
|
-
throw error;
|
|
13597
|
+
// src/settlement-runner.ts
|
|
13598
|
+
async function executeSettlementPlan(plan, driver, persist) {
|
|
13599
|
+
const execute = async (kind, indices2) => {
|
|
13600
|
+
let tx = plan[kind];
|
|
13601
|
+
if (!tx) {
|
|
13602
|
+
tx = await driver.prepare(kind, plan, indices2);
|
|
13603
|
+
plan[kind] = tx;
|
|
13604
|
+
await persist(plan);
|
|
13605
|
+
}
|
|
13606
|
+
let result = await driver.receipt(tx.hash);
|
|
13607
|
+
if (!result) {
|
|
13608
|
+
await driver.broadcast(tx);
|
|
13609
|
+
result = await driver.wait(tx.hash);
|
|
13610
|
+
}
|
|
13611
|
+
if (!result)
|
|
13612
|
+
throw new Error(
|
|
13613
|
+
"SETTLEMENT_UNCONFIRMED: rerun to reconcile the saved transaction; do not replace it."
|
|
13614
|
+
);
|
|
13615
|
+
if (result.hash.toLowerCase() !== tx.hash.toLowerCase() || result.status !== 1) {
|
|
13616
|
+
throw new Error(
|
|
13617
|
+
"SETTLEMENT_REVERTED: inspect the saved hash before an operator resets the journal."
|
|
13618
|
+
);
|
|
13619
|
+
}
|
|
13620
|
+
return result.hash;
|
|
13621
|
+
};
|
|
13622
|
+
if (plan.claim) {
|
|
13623
|
+
const claimTransaction2 = await execute("claim", []);
|
|
13624
|
+
return { publishTransaction: plan.publish?.hash ?? "", claimTransaction: claimTransaction2 };
|
|
12853
13625
|
}
|
|
12854
|
-
|
|
12855
|
-
|
|
12856
|
-
|
|
12857
|
-
|
|
12858
|
-
|
|
12859
|
-
|
|
12860
|
-
|
|
12861
|
-
|
|
12862
|
-
const
|
|
12863
|
-
|
|
12864
|
-
|
|
12865
|
-
|
|
12866
|
-
|
|
12867
|
-
|
|
12868
|
-
|
|
12869
|
-
|
|
12870
|
-
|
|
12871
|
-
|
|
12872
|
-
|
|
12873
|
-
|
|
12874
|
-
|
|
12875
|
-
|
|
12876
|
-
|
|
12877
|
-
|
|
12878
|
-
|
|
12879
|
-
|
|
12880
|
-
|
|
12881
|
-
|
|
12882
|
-
|
|
12883
|
-
|
|
12884
|
-
|
|
12885
|
-
|
|
12886
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
12889
|
-
|
|
12890
|
-
|
|
12891
|
-
|
|
12892
|
-
|
|
13626
|
+
const claimed = await Promise.all(
|
|
13627
|
+
plan.receiptIds.map((id) => driver.claimed(id))
|
|
13628
|
+
);
|
|
13629
|
+
if (claimed.some(Boolean))
|
|
13630
|
+
throw new Error(
|
|
13631
|
+
"SETTLEMENT_RECONCILIATION_REQUIRED: onchain claims exist without a saved transaction; inspect events before changing local state."
|
|
13632
|
+
);
|
|
13633
|
+
const indices = plan.receiptIds.map((_, i) => i);
|
|
13634
|
+
const rootApproved = await driver.approved(plan.root);
|
|
13635
|
+
await driver.preflight(plan, !rootApproved, indices);
|
|
13636
|
+
const publishTransaction = rootApproved ? plan.publish?.hash ?? "" : await execute("publish", []);
|
|
13637
|
+
if (!await driver.approved(plan.root))
|
|
13638
|
+
throw new Error("SETTLEMENT_ROOT_UNCONFIRMED: rerun to reconcile.");
|
|
13639
|
+
const claimTransaction = await execute("claim", indices);
|
|
13640
|
+
return { publishTransaction, claimTransaction };
|
|
13641
|
+
}
|
|
13642
|
+
|
|
13643
|
+
// src/settlement-driver.ts
|
|
13644
|
+
import { Contract, Transaction } from "ethers";
|
|
13645
|
+
var abi = [
|
|
13646
|
+
"function approvedRoots(bytes32) view returns(bool)",
|
|
13647
|
+
"function claimedReceipts(bytes32) view returns(bool)",
|
|
13648
|
+
"function publishRoot(bytes32)",
|
|
13649
|
+
"function claimBatch(bytes32,address[],uint256[],bytes32[],bytes32[][])"
|
|
13650
|
+
];
|
|
13651
|
+
function createSettlementDriver(signer, contract) {
|
|
13652
|
+
const provider = signer.provider;
|
|
13653
|
+
const claims = new Contract(contract, abi, signer);
|
|
13654
|
+
const data = (kind, plan) => kind === "publish" ? claims.interface.encodeFunctionData("publishRoot", [plan.root]) : claims.interface.encodeFunctionData("claimBatch", [
|
|
13655
|
+
plan.root,
|
|
13656
|
+
plan.accounts,
|
|
13657
|
+
plan.amounts,
|
|
13658
|
+
plan.receiptIds,
|
|
13659
|
+
plan.proofs
|
|
13660
|
+
]);
|
|
13661
|
+
const fees = async () => {
|
|
13662
|
+
const fee = await provider.getFeeData();
|
|
13663
|
+
const gasPrice = fee.maxFeePerGas ?? fee.gasPrice;
|
|
13664
|
+
if (gasPrice === null || gasPrice <= 0n)
|
|
13665
|
+
throw new Error(
|
|
13666
|
+
"SETTLEMENT_FEE_UNAVAILABLE: RPC did not supply a gas price."
|
|
13667
|
+
);
|
|
13668
|
+
return fee.maxFeePerGas !== null && fee.maxPriorityFeePerGas !== null ? {
|
|
13669
|
+
maxFeePerGas: fee.maxFeePerGas,
|
|
13670
|
+
maxPriorityFeePerGas: fee.maxPriorityFeePerGas
|
|
13671
|
+
} : { gasPrice };
|
|
13672
|
+
};
|
|
13673
|
+
const budget = async (cost) => {
|
|
13674
|
+
const cap = process.env.AIPOU_MAX_SETTLEMENT_FEE_WEI;
|
|
13675
|
+
if (cap && (!/^\d+$/.test(cap) || cost > BigInt(cap)))
|
|
13676
|
+
throw new Error(
|
|
13677
|
+
"SETTLEMENT_FEE_CAP: required fee exceeds AIPOU_MAX_SETTLEMENT_FEE_WEI."
|
|
13678
|
+
);
|
|
13679
|
+
if (await provider.getBalance(signer.address) < cost) {
|
|
13680
|
+
throw new Error(
|
|
13681
|
+
`SETTLEMENT_INSUFFICIENT_GAS: validator needs up to ${cost} wei for this batch including a conservative Base data-fee allowance. Fund the validator on Base and retry; no automatic transfer is performed.`
|
|
13682
|
+
);
|
|
12893
13683
|
}
|
|
12894
|
-
|
|
12895
|
-
|
|
13684
|
+
};
|
|
13685
|
+
return {
|
|
13686
|
+
approved: (root) => claims.approvedRoots(root),
|
|
13687
|
+
claimed: (id) => claims.claimedReceipts(id),
|
|
13688
|
+
preflight: async (plan, publishNeeded) => {
|
|
13689
|
+
const fee = await fees();
|
|
13690
|
+
const price = "gasPrice" in fee ? fee.gasPrice : fee.maxFeePerGas;
|
|
13691
|
+
const claimGas = publishNeeded ? 120000n + BigInt(plan.receiptIds.length) * 100000n : await provider.estimateGas({
|
|
13692
|
+
from: signer.address,
|
|
13693
|
+
to: contract,
|
|
13694
|
+
data: data("claim", plan)
|
|
13695
|
+
});
|
|
13696
|
+
const pubGas = publishNeeded ? await provider.estimateGas({
|
|
13697
|
+
from: signer.address,
|
|
13698
|
+
to: contract,
|
|
13699
|
+
data: data("publish", plan)
|
|
13700
|
+
}) : 0n;
|
|
13701
|
+
await budget(
|
|
13702
|
+
(claimGas + pubGas) * price * 120n / 100n + 20000000000000n
|
|
13703
|
+
);
|
|
13704
|
+
},
|
|
13705
|
+
prepare: async (kind, plan) => {
|
|
13706
|
+
const request = {
|
|
13707
|
+
to: contract,
|
|
13708
|
+
data: data(kind, plan),
|
|
13709
|
+
value: 0n,
|
|
13710
|
+
...await fees()
|
|
13711
|
+
};
|
|
13712
|
+
const gasLimit = await provider.estimateGas({ ...request, from: signer.address }) * 120n / 100n;
|
|
13713
|
+
const price = "gasPrice" in request ? request.gasPrice : request.maxFeePerGas;
|
|
13714
|
+
await budget(gasLimit * price + 20000000000000n);
|
|
13715
|
+
const populated = await signer.populateTransaction({
|
|
13716
|
+
...request,
|
|
13717
|
+
gasLimit
|
|
13718
|
+
});
|
|
13719
|
+
const raw = await signer.signTransaction(populated);
|
|
13720
|
+
const hash = Transaction.from(raw).hash;
|
|
13721
|
+
return { hash, raw };
|
|
13722
|
+
},
|
|
13723
|
+
receipt: async (hash) => {
|
|
13724
|
+
const result = await provider.getTransactionReceipt(hash);
|
|
13725
|
+
return result ? { hash: result.hash, status: result.status } : null;
|
|
13726
|
+
},
|
|
13727
|
+
broadcast: async (tx) => {
|
|
13728
|
+
const decoded = Transaction.from(tx.raw);
|
|
13729
|
+
if (decoded.hash !== tx.hash || decoded.from !== signer.address || decoded.to?.toLowerCase() !== contract.toLowerCase() || decoded.chainId !== 8453n || decoded.value !== 0n) {
|
|
13730
|
+
throw new Error(
|
|
13731
|
+
"SETTLEMENT_JOURNAL_INVALID: saved transaction identity does not match this validator and contract."
|
|
13732
|
+
);
|
|
13733
|
+
}
|
|
13734
|
+
if (await provider.getTransaction(tx.hash)) return;
|
|
13735
|
+
try {
|
|
13736
|
+
await provider.broadcastTransaction(tx.raw);
|
|
13737
|
+
} catch {
|
|
13738
|
+
if (await provider.getTransaction(tx.hash)) return;
|
|
13739
|
+
throw new Error(
|
|
13740
|
+
"SETTLEMENT_BROADCAST_UNCONFIRMED: saved transaction retained; retry to reconcile. No replacement transaction was signed."
|
|
13741
|
+
);
|
|
13742
|
+
}
|
|
13743
|
+
},
|
|
13744
|
+
wait: async (hash) => {
|
|
13745
|
+
try {
|
|
13746
|
+
const result = await provider.waitForTransaction(hash, 1, 9e4);
|
|
13747
|
+
return result ? { hash: result.hash, status: result.status } : null;
|
|
13748
|
+
} catch {
|
|
13749
|
+
throw new Error(
|
|
13750
|
+
"SETTLEMENT_CONFIRMATION_UNAVAILABLE: saved transaction retained; retry to reconcile."
|
|
13751
|
+
);
|
|
13752
|
+
}
|
|
12896
13753
|
}
|
|
12897
|
-
|
|
12898
|
-
state.completedEvidence[evidenceKey] = receipt.receiptId;
|
|
12899
|
-
state.receipts.push(receipt);
|
|
12900
|
-
});
|
|
13754
|
+
};
|
|
12901
13755
|
}
|
|
12902
|
-
|
|
12903
|
-
|
|
12904
|
-
|
|
12905
|
-
|
|
12906
|
-
|
|
12907
|
-
|
|
12908
|
-
|
|
12909
|
-
|
|
12910
|
-
|
|
12911
|
-
|
|
12912
|
-
|
|
12913
|
-
|
|
12914
|
-
|
|
13756
|
+
|
|
13757
|
+
// src/evidence.ts
|
|
13758
|
+
function receiptEvidenceReport(input) {
|
|
13759
|
+
return {
|
|
13760
|
+
receiptIntegrity: input.receiptIntegrity ? "VERIFIED" : "UNKNOWN",
|
|
13761
|
+
walletAuthorization: input.walletAuthorization ? "VERIFIED" : "UNKNOWN",
|
|
13762
|
+
collectorSignature: input.collectorSignature ? "VERIFIED" : "UNKNOWN",
|
|
13763
|
+
hostExecution: input.hostExecution ?? "UNKNOWN",
|
|
13764
|
+
// These require their own artifacts and verifiers. AIPOU receipts do not
|
|
13765
|
+
// infer them from a completed local task.
|
|
13766
|
+
delivery: "UNKNOWN",
|
|
13767
|
+
businessAcceptance: "ABSENT",
|
|
13768
|
+
settlement: "ABSENT",
|
|
13769
|
+
externalEffect: "UNKNOWN"
|
|
13770
|
+
};
|
|
12915
13771
|
}
|
|
12916
|
-
|
|
12917
|
-
|
|
12918
|
-
|
|
12919
|
-
return
|
|
13772
|
+
|
|
13773
|
+
// src/receipt-verifier.ts
|
|
13774
|
+
function isBytes32(value) {
|
|
13775
|
+
return typeof value === "string" && /^0x[0-9a-f]{64}$/i.test(value);
|
|
12920
13776
|
}
|
|
12921
|
-
|
|
12922
|
-
|
|
13777
|
+
function assertReceiptShape(receipt) {
|
|
13778
|
+
const errors = [];
|
|
13779
|
+
const fields = /* @__PURE__ */ new Set(["receiptId", "nonce", "wallet", "provider", "model", "inputTokens", "outputTokens", "durationSeconds", "taskHash", "outputHash", "client", "trustTier", "walletAuthorization", "authorizationIssuedAt", "authorizationChainId", "authorizationContract", "providerEvidence", "collectorPublicKey", "collectorSignature", "recordedAt", "estimatedReward", "batchRoot", "claimTransaction", "tokenUsageMeasurement"]);
|
|
13780
|
+
if (Object.keys(receipt).some((key) => !fields.has(key))) return ["receipt contains unsupported fields"];
|
|
13781
|
+
for (const [key, value] of Object.entries(receipt)) {
|
|
13782
|
+
if (key === "providerEvidence") {
|
|
13783
|
+
if (value !== void 0 && (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((k) => !["keyId", "signature"].includes(k)) || typeof value.keyId !== "string" || typeof value.signature !== "string" || value.keyId.length > 128 || value.signature.length > 2048)) return ["providerEvidence must contain bounded keyId and signature strings"];
|
|
13784
|
+
} else if (value !== void 0 && typeof value !== "string" && typeof value !== "number") return ["receipt fields must be scalar values"];
|
|
13785
|
+
if (typeof value === "string" && value.length > 8192) return ["receipt field exceeds length limit"];
|
|
13786
|
+
}
|
|
13787
|
+
for (const field of ["receiptId", "nonce", "taskHash", "outputHash"]) {
|
|
13788
|
+
if (!isBytes32(receipt[field])) errors.push(`${field} must be a bytes32 hex value`);
|
|
13789
|
+
}
|
|
13790
|
+
if (typeof receipt.walletAuthorization !== "string" || !/^0x[0-9a-f]+$/i.test(receipt.walletAuthorization)) {
|
|
13791
|
+
errors.push("walletAuthorization must be a hex EIP-712 signature");
|
|
13792
|
+
}
|
|
13793
|
+
if (typeof receipt.wallet !== "string" || !/^0x[0-9a-f]{40}$/i.test(receipt.wallet)) errors.push("wallet must be an address");
|
|
13794
|
+
if (typeof receipt.authorizationContract !== "string" || !/^0x[0-9a-f]{40}$/i.test(receipt.authorizationContract)) {
|
|
13795
|
+
errors.push("authorizationContract must be an address");
|
|
13796
|
+
}
|
|
13797
|
+
for (const field of ["provider", "model", "client", "collectorPublicKey", "collectorSignature", "recordedAt", "estimatedReward"]) {
|
|
13798
|
+
if (typeof receipt[field] !== "string" || receipt[field].length === 0) errors.push(`${field} must be a non-empty string`);
|
|
13799
|
+
}
|
|
13800
|
+
for (const field of ["inputTokens", "outputTokens", "durationSeconds", "authorizationIssuedAt", "authorizationChainId"]) {
|
|
13801
|
+
if (!Number.isSafeInteger(receipt[field]) || Number(receipt[field]) < 0) errors.push(`${field} must be a non-negative safe integer`);
|
|
13802
|
+
}
|
|
13803
|
+
if (receipt.trustTier !== "client_signed" && receipt.trustTier !== "provider_signed") errors.push("trustTier is unsupported");
|
|
13804
|
+
if (receipt.tokenUsageMeasurement !== void 0 && !["measured", "unmeasured", "unspecified"].includes(receipt.tokenUsageMeasurement)) errors.push("tokenUsageMeasurement is unsupported");
|
|
13805
|
+
if (receipt.tokenUsageMeasurement === "unmeasured" && (receipt.inputTokens !== 0 || receipt.outputTokens !== 0)) errors.push("unmeasured token usage must have zero counts");
|
|
13806
|
+
if (Number.isNaN(Date.parse(String(receipt.recordedAt)))) errors.push("recordedAt must be an ISO date");
|
|
13807
|
+
return errors;
|
|
12923
13808
|
}
|
|
12924
|
-
|
|
12925
|
-
|
|
12926
|
-
|
|
13809
|
+
function verifyAipouReceipt(candidate) {
|
|
13810
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) {
|
|
13811
|
+
return {
|
|
13812
|
+
valid: false,
|
|
13813
|
+
errors: ["receipt must be an object"],
|
|
13814
|
+
workReceiptId: null,
|
|
13815
|
+
claimedWorkReceiptId: null,
|
|
13816
|
+
evidence: receiptEvidenceReport({ receiptIntegrity: false, walletAuthorization: false, collectorSignature: false })
|
|
13817
|
+
};
|
|
13818
|
+
}
|
|
13819
|
+
const receipt = candidate;
|
|
13820
|
+
const errors = assertReceiptShape(receipt);
|
|
13821
|
+
if (errors.length > 0) {
|
|
13822
|
+
return {
|
|
13823
|
+
valid: false,
|
|
13824
|
+
errors,
|
|
13825
|
+
workReceiptId: null,
|
|
13826
|
+
claimedWorkReceiptId: isBytes32(receipt.receiptId) ? receipt.receiptId.toLowerCase() : null,
|
|
13827
|
+
evidence: receiptEvidenceReport({ receiptIntegrity: false, walletAuthorization: false, collectorSignature: false })
|
|
13828
|
+
};
|
|
13829
|
+
}
|
|
13830
|
+
const { receiptId, collectorSignature: signature, batchRoot: _batchRoot, claimTransaction: _claimTransaction, ...unsigned } = receipt;
|
|
13831
|
+
const receiptIntegrity = sha256Hex(canonicalJson(unsigned)).toLowerCase() === receiptId.toLowerCase();
|
|
13832
|
+
if (!receiptIntegrity) errors.push("receiptId does not match canonical unsigned receipt payload");
|
|
13833
|
+
let collectorSignatureValid = false;
|
|
13834
|
+
try {
|
|
13835
|
+
collectorSignatureValid = verifyCollectorPayload({ receiptId, ...unsigned }, signature, receipt.collectorPublicKey);
|
|
13836
|
+
} catch {
|
|
13837
|
+
collectorSignatureValid = false;
|
|
13838
|
+
}
|
|
13839
|
+
if (!collectorSignatureValid) errors.push("collector signature is invalid");
|
|
13840
|
+
const session = {
|
|
13841
|
+
nonce: receipt.nonce,
|
|
13842
|
+
wallet: receipt.wallet,
|
|
13843
|
+
provider: receipt.provider,
|
|
13844
|
+
model: receipt.model,
|
|
13845
|
+
taskHash: receipt.taskHash,
|
|
13846
|
+
client: receipt.client,
|
|
13847
|
+
issuedAt: receipt.authorizationIssuedAt,
|
|
13848
|
+
chainId: receipt.authorizationChainId,
|
|
13849
|
+
verifyingContract: receipt.authorizationContract,
|
|
13850
|
+
walletAuthorization: receipt.walletAuthorization,
|
|
13851
|
+
completedAt: receipt.recordedAt
|
|
13852
|
+
};
|
|
13853
|
+
let walletAuthorization = false;
|
|
13854
|
+
try {
|
|
13855
|
+
walletAuthorization = verifyTaskAuthorization(session);
|
|
13856
|
+
} catch {
|
|
13857
|
+
walletAuthorization = false;
|
|
13858
|
+
}
|
|
13859
|
+
if (!walletAuthorization) errors.push("wallet authorization is invalid");
|
|
12927
13860
|
return {
|
|
12928
|
-
|
|
12929
|
-
|
|
12930
|
-
|
|
12931
|
-
|
|
13861
|
+
valid: errors.length === 0,
|
|
13862
|
+
errors,
|
|
13863
|
+
workReceiptId: errors.length === 0 ? receiptId.toLowerCase() : null,
|
|
13864
|
+
claimedWorkReceiptId: errors.length === 0 ? null : receiptId.toLowerCase(),
|
|
13865
|
+
evidence: receiptEvidenceReport({
|
|
13866
|
+
receiptIntegrity,
|
|
13867
|
+
walletAuthorization,
|
|
13868
|
+
collectorSignature: collectorSignatureValid,
|
|
13869
|
+
hostExecution: receiptIntegrity && collectorSignatureValid ? "OBSERVED" : "UNKNOWN"
|
|
13870
|
+
})
|
|
12932
13871
|
};
|
|
12933
13872
|
}
|
|
12934
|
-
async function settledReceiptsSince(sinceMs) {
|
|
12935
|
-
const receipts = [...await readArchivedReceipts(), ...(await readState()).receipts];
|
|
12936
|
-
return receipts.filter(
|
|
12937
|
-
(receipt) => receipt.claimTransaction && Date.parse(receipt.recordedAt) >= sinceMs
|
|
12938
|
-
);
|
|
12939
|
-
}
|
|
12940
|
-
async function markBatchSettled(batch) {
|
|
12941
|
-
await mutate(async (state) => {
|
|
12942
|
-
const included = new Set(batch.receiptIds);
|
|
12943
|
-
const settled = [];
|
|
12944
|
-
for (const receipt of state.receipts) {
|
|
12945
|
-
if (included.has(receipt.receiptId)) {
|
|
12946
|
-
if (receipt.claimTransaction) throw new Error(`Receipt ${receipt.receiptId} is already settled`);
|
|
12947
|
-
receipt.batchRoot = batch.root;
|
|
12948
|
-
receipt.claimTransaction = batch.claimTransaction;
|
|
12949
|
-
settled.push(receipt);
|
|
12950
|
-
}
|
|
12951
|
-
}
|
|
12952
|
-
await mkdir2(archiveDir(), { recursive: true });
|
|
12953
|
-
const archivePath = path4.join(archiveDir(), `batch-${batch.root.slice(2, 18)}.json`);
|
|
12954
|
-
await writeFile3(archivePath, `${JSON.stringify(settled, null, 2)}
|
|
12955
|
-
`, { encoding: "utf8", flag: "wx" });
|
|
12956
|
-
state.receipts = state.receipts.filter((receipt) => !included.has(receipt.receiptId));
|
|
12957
|
-
state.settledReceiptIds = [...state.settledReceiptIds ?? [], ...batch.receiptIds];
|
|
12958
|
-
state.batches.push(batch);
|
|
12959
|
-
});
|
|
12960
|
-
}
|
|
12961
13873
|
|
|
12962
13874
|
// src/claims.ts
|
|
12963
13875
|
var claimsAbi = [
|
|
12964
13876
|
"function token() view returns (address)",
|
|
12965
13877
|
"function validator() view returns (address)",
|
|
12966
13878
|
"function approvedRoots(bytes32 root) view returns (bool)",
|
|
13879
|
+
"function claimedReceipts(bytes32 receiptId) view returns (bool)",
|
|
12967
13880
|
"function publishRoot(bytes32 root)",
|
|
12968
13881
|
"function claimBatch(bytes32 root, address[] accounts, uint256[] amounts, bytes32[] receiptIds, bytes32[][] proofs)"
|
|
12969
13882
|
];
|
|
12970
13883
|
var tokenAbi = ["function emissionController() view returns (address)"];
|
|
12971
13884
|
function claimsAddress() {
|
|
12972
13885
|
const value = process.env.AIPOU_CLAIMS_ADDRESS;
|
|
12973
|
-
if (!value)
|
|
13886
|
+
if (!value)
|
|
13887
|
+
throw new Error("AIPOU_CLAIMS_ADDRESS is required to settle rewards");
|
|
12974
13888
|
return getAddress2(value);
|
|
12975
13889
|
}
|
|
12976
13890
|
async function validatorPrivateKey() {
|
|
12977
13891
|
const keyFile = process.env.AIPOU_VALIDATOR_KEY_FILE;
|
|
12978
13892
|
if (keyFile) {
|
|
12979
|
-
const key = (await
|
|
13893
|
+
const key = (await readFile7(path6.resolve(keyFile), "utf8")).trim();
|
|
12980
13894
|
if (!key) throw new Error(`AIPOU_VALIDATOR_KEY_FILE ${keyFile} is empty`);
|
|
12981
13895
|
return key;
|
|
12982
13896
|
}
|
|
@@ -12987,26 +13901,46 @@ async function validatorPrivateKey() {
|
|
|
12987
13901
|
);
|
|
12988
13902
|
return envKey;
|
|
12989
13903
|
}
|
|
12990
|
-
throw new Error(
|
|
13904
|
+
throw new Error(
|
|
13905
|
+
"AIPOU_VALIDATOR_KEY_FILE (or legacy AIPOU_VALIDATOR_PRIVATE_KEY) is required only on the protocol validator server"
|
|
13906
|
+
);
|
|
12991
13907
|
}
|
|
12992
13908
|
async function validatorWallet() {
|
|
12993
|
-
const provider = new
|
|
12994
|
-
|
|
13909
|
+
const provider = new JsonRpcProvider2(
|
|
13910
|
+
process.env.AIPOU_RPC_URL || "https://mainnet.base.org"
|
|
13911
|
+
);
|
|
13912
|
+
return new Wallet3(await validatorPrivateKey(), provider);
|
|
12995
13913
|
}
|
|
12996
13914
|
async function trustedCollectorFingerprints() {
|
|
12997
13915
|
const file = process.env.AIPOU_TRUSTED_COLLECTORS_FILE;
|
|
12998
|
-
if (!file)
|
|
12999
|
-
|
|
13000
|
-
|
|
13916
|
+
if (!file)
|
|
13917
|
+
throw new Error(
|
|
13918
|
+
"AIPOU_TRUSTED_COLLECTORS_FILE is required on the protocol validator server"
|
|
13919
|
+
);
|
|
13920
|
+
const content = JSON.parse(await readFile7(path6.resolve(file), "utf8"));
|
|
13921
|
+
if (!Array.isArray(content.fingerprints))
|
|
13922
|
+
throw new Error(
|
|
13923
|
+
"Trusted collectors file must contain a fingerprints array"
|
|
13924
|
+
);
|
|
13001
13925
|
return new Set(content.fingerprints);
|
|
13002
13926
|
}
|
|
13003
13927
|
async function verifyReceipt(receipt, trustedCollectors) {
|
|
13928
|
+
if (!verifyAipouReceipt(receipt).valid)
|
|
13929
|
+
throw new Error(
|
|
13930
|
+
"Invalid receipt integrity or signature; settlement stopped before submission"
|
|
13931
|
+
);
|
|
13004
13932
|
const fingerprint = collectorFingerprint(receipt.collectorPublicKey);
|
|
13005
13933
|
if (!trustedCollectors.has(fingerprint)) {
|
|
13006
|
-
throw new Error(
|
|
13934
|
+
throw new Error(
|
|
13935
|
+
`Untrusted collector ${fingerprint} for ${receipt.receiptId}`
|
|
13936
|
+
);
|
|
13007
13937
|
}
|
|
13008
13938
|
const { collectorSignature, batchRoot, claimTransaction, ...signedPayload } = receipt;
|
|
13009
|
-
if (!verifyCollectorPayload(
|
|
13939
|
+
if (!verifyCollectorPayload(
|
|
13940
|
+
signedPayload,
|
|
13941
|
+
collectorSignature,
|
|
13942
|
+
receipt.collectorPublicKey
|
|
13943
|
+
)) {
|
|
13010
13944
|
throw new Error(`Invalid collector signature for ${receipt.receiptId}`);
|
|
13011
13945
|
}
|
|
13012
13946
|
const session = {
|
|
@@ -13022,7 +13956,8 @@ async function verifyReceipt(receipt, trustedCollectors) {
|
|
|
13022
13956
|
walletAuthorization: receipt.walletAuthorization,
|
|
13023
13957
|
completedAt: receipt.recordedAt
|
|
13024
13958
|
};
|
|
13025
|
-
if (!verifyTaskAuthorization(session))
|
|
13959
|
+
if (!verifyTaskAuthorization(session))
|
|
13960
|
+
throw new Error(`Invalid wallet authorization for ${receipt.receiptId}`);
|
|
13026
13961
|
const derivedTier = await deriveTrustTier(
|
|
13027
13962
|
session,
|
|
13028
13963
|
{
|
|
@@ -13031,31 +13966,81 @@ async function verifyReceipt(receipt, trustedCollectors) {
|
|
|
13031
13966
|
outputTokens: receipt.outputTokens,
|
|
13032
13967
|
durationSeconds: receipt.durationSeconds,
|
|
13033
13968
|
outputHash: receipt.outputHash,
|
|
13969
|
+
tokenUsageMeasurement: receipt.tokenUsageMeasurement,
|
|
13034
13970
|
providerEvidence: receipt.providerEvidence
|
|
13035
13971
|
},
|
|
13036
13972
|
receipt.providerEvidence
|
|
13037
13973
|
);
|
|
13038
|
-
if (derivedTier !== receipt.trustTier)
|
|
13974
|
+
if (derivedTier !== receipt.trustTier)
|
|
13975
|
+
throw new Error(`Invalid trust tier for ${receipt.receiptId}`);
|
|
13039
13976
|
}
|
|
13040
13977
|
async function settleRewards(maxReceipts = 25) {
|
|
13978
|
+
if (!Number.isInteger(maxReceipts) || maxReceipts < 1 || maxReceipts > 100)
|
|
13979
|
+
throw new Error("Batch size must be 1 through 100");
|
|
13980
|
+
const directory = path6.resolve(process.env.AIPOU_DATA_DIR || ".aipou");
|
|
13981
|
+
await mkdir4(directory, { recursive: true, mode: 448 });
|
|
13982
|
+
try {
|
|
13983
|
+
return await withFileLock(
|
|
13984
|
+
path6.join(directory, "settlement.lock"),
|
|
13985
|
+
() => settleRewardsLocked(maxReceipts, directory)
|
|
13986
|
+
);
|
|
13987
|
+
} catch (error) {
|
|
13988
|
+
const message = error instanceof Error ? error.message : "";
|
|
13989
|
+
if (message.startsWith("SETTLEMENT_") || message === "There are no unsettled receipts" || message.startsWith("No receipts pass the settlement policy"))
|
|
13990
|
+
throw error;
|
|
13991
|
+
throw new Error(
|
|
13992
|
+
"SETTLEMENT_FAILED: inspect local configuration and saved settlement state before retrying; private RPC and transaction details are withheld."
|
|
13993
|
+
);
|
|
13994
|
+
}
|
|
13995
|
+
}
|
|
13996
|
+
async function settleRewardsLocked(maxReceipts, directory) {
|
|
13997
|
+
const journalPath = path6.join(directory, "settlement-pending.json");
|
|
13998
|
+
const pending = await readOptionalJson(journalPath);
|
|
13041
13999
|
const candidates = await unsettledReceipts(Number.MAX_SAFE_INTEGER);
|
|
13042
|
-
if (candidates.length === 0
|
|
14000
|
+
if (candidates.length === 0 && !pending)
|
|
14001
|
+
throw new Error("There are no unsettled receipts");
|
|
13043
14002
|
const policy = settlementPolicyFromEnv();
|
|
13044
14003
|
const recentlySettled = await settledReceiptsSince(Date.now() - DAY_MS);
|
|
13045
|
-
const { eligible, skipped } = filterEligibleReceipts(
|
|
13046
|
-
|
|
14004
|
+
const { eligible, skipped } = filterEligibleReceipts(
|
|
14005
|
+
candidates,
|
|
14006
|
+
recentlySettled,
|
|
14007
|
+
policy
|
|
14008
|
+
);
|
|
14009
|
+
if (eligible.length === 0 && !pending) {
|
|
13047
14010
|
const reasons = skipped.map((item) => `${item.receiptId.slice(0, 10)}\u2026: ${item.reason}`).join("; ");
|
|
13048
|
-
throw new Error(
|
|
14011
|
+
throw new Error(
|
|
14012
|
+
`No receipts pass the settlement policy. Skipped: ${reasons}`
|
|
14013
|
+
);
|
|
14014
|
+
}
|
|
14015
|
+
let receipts = eligible.slice(0, maxReceipts);
|
|
14016
|
+
if (pending) {
|
|
14017
|
+
if (pending.version !== 1 || pending.chainId !== 8453 || !Array.isArray(pending.receiptIds) || pending.receiptIds.length < 1 || pending.receiptIds.length > 100) {
|
|
14018
|
+
throw new Error("SETTLEMENT_JOURNAL_INVALID: invalid saved batch");
|
|
14019
|
+
}
|
|
14020
|
+
const stored = await exportStoredReceipts();
|
|
14021
|
+
receipts = pending.receiptIds.map((id) => {
|
|
14022
|
+
const receipt = stored.find((item) => item.receiptId === id);
|
|
14023
|
+
if (!receipt)
|
|
14024
|
+
throw new Error("SETTLEMENT_JOURNAL_INVALID: saved receipt is missing");
|
|
14025
|
+
return receipt;
|
|
14026
|
+
});
|
|
13049
14027
|
}
|
|
13050
|
-
const receipts = eligible.slice(0, maxReceipts);
|
|
13051
14028
|
const trustedCollectors = await trustedCollectorFingerprints();
|
|
13052
|
-
for (const receipt of receipts)
|
|
14029
|
+
for (const receipt of receipts)
|
|
14030
|
+
await verifyReceipt(receipt, trustedCollectors);
|
|
13053
14031
|
const accounts = receipts.map((receipt) => receipt.wallet);
|
|
13054
|
-
const amounts = receipts.map(
|
|
14032
|
+
const amounts = receipts.map(
|
|
14033
|
+
(receipt) => parseUnits(receipt.estimatedReward, 18)
|
|
14034
|
+
);
|
|
13055
14035
|
const receiptIds = receipts.map((receipt) => receipt.receiptId);
|
|
13056
14036
|
const coder = AbiCoder.defaultAbiCoder();
|
|
13057
14037
|
const leaves = receipts.map((receipt, index) => {
|
|
13058
|
-
const inner = keccak256(
|
|
14038
|
+
const inner = keccak256(
|
|
14039
|
+
coder.encode(
|
|
14040
|
+
["address", "uint256", "bytes32"],
|
|
14041
|
+
[receipt.wallet, amounts[index], receipt.receiptId]
|
|
14042
|
+
)
|
|
14043
|
+
);
|
|
13059
14044
|
return Buffer.from(getBytes(keccak256(concat([inner]))));
|
|
13060
14045
|
});
|
|
13061
14046
|
const tree = new MerkleTree(
|
|
@@ -13067,35 +14052,77 @@ async function settleRewards(maxReceipts = 25) {
|
|
|
13067
14052
|
const proofs = leaves.map((leaf) => tree.getHexProof(leaf));
|
|
13068
14053
|
const signer = await validatorWallet();
|
|
13069
14054
|
const address = claimsAddress();
|
|
13070
|
-
|
|
14055
|
+
if ((await signer.provider.getNetwork()).chainId !== 8453n)
|
|
14056
|
+
throw new Error("Settlement requires Base mainnet (8453)");
|
|
14057
|
+
const claims = new Contract2(address, claimsAbi, signer);
|
|
13071
14058
|
const configuredValidator = getAddress2(await claims.validator());
|
|
13072
14059
|
if (configuredValidator !== signer.address) {
|
|
13073
|
-
throw new Error(
|
|
14060
|
+
throw new Error(
|
|
14061
|
+
`Validator mismatch: contract expects ${configuredValidator}, server uses ${signer.address}`
|
|
14062
|
+
);
|
|
13074
14063
|
}
|
|
13075
14064
|
const tokenAddress = await claims.token();
|
|
13076
|
-
const token = new
|
|
14065
|
+
const token = new Contract2(tokenAddress, tokenAbi, signer.provider);
|
|
13077
14066
|
if (getAddress2(await token.emissionController()) !== address) {
|
|
13078
14067
|
throw new Error("AIPOUClaims is not the token emission controller");
|
|
13079
14068
|
}
|
|
13080
|
-
const
|
|
13081
|
-
|
|
13082
|
-
|
|
13083
|
-
|
|
13084
|
-
|
|
13085
|
-
|
|
13086
|
-
|
|
13087
|
-
|
|
13088
|
-
|
|
13089
|
-
|
|
13090
|
-
if (
|
|
14069
|
+
const expected = {
|
|
14070
|
+
version: 1,
|
|
14071
|
+
chainId: 8453,
|
|
14072
|
+
contract: address,
|
|
14073
|
+
root,
|
|
14074
|
+
accounts,
|
|
14075
|
+
amounts: amounts.map(String),
|
|
14076
|
+
receiptIds,
|
|
14077
|
+
proofs
|
|
14078
|
+
};
|
|
14079
|
+
if (pending) {
|
|
14080
|
+
const { publish: _publish, claim: _claim, ...saved } = pending;
|
|
14081
|
+
if (canonicalJson(saved) !== canonicalJson(expected))
|
|
14082
|
+
throw new Error(
|
|
14083
|
+
"SETTLEMENT_JOURNAL_INVALID: batch does not match verified receipts and configured contract"
|
|
14084
|
+
);
|
|
14085
|
+
}
|
|
14086
|
+
const plan = pending ?? expected;
|
|
14087
|
+
for (const kind of ["publish", "claim"]) {
|
|
14088
|
+
const saved = plan[kind];
|
|
14089
|
+
if (!saved) continue;
|
|
14090
|
+
const transaction = Transaction2.from(saved.raw);
|
|
14091
|
+
const expectedData = kind === "publish" ? claims.interface.encodeFunctionData("publishRoot", [root]) : claims.interface.encodeFunctionData("claimBatch", [
|
|
14092
|
+
root,
|
|
14093
|
+
accounts,
|
|
14094
|
+
amounts,
|
|
14095
|
+
receiptIds,
|
|
14096
|
+
proofs
|
|
14097
|
+
]);
|
|
14098
|
+
if (transaction.hash !== saved.hash || transaction.from !== signer.address || transaction.chainId !== 8453n || transaction.to?.toLowerCase() !== address.toLowerCase() || transaction.value !== 0n || transaction.data !== expectedData) {
|
|
14099
|
+
throw new Error(
|
|
14100
|
+
"SETTLEMENT_JOURNAL_INVALID: saved transaction does not match the verified batch"
|
|
14101
|
+
);
|
|
14102
|
+
}
|
|
14103
|
+
}
|
|
14104
|
+
const persist = (value) => writePrivateJson(journalPath, value);
|
|
14105
|
+
await persist(plan);
|
|
14106
|
+
const transactions = await executeSettlementPlan(
|
|
14107
|
+
plan,
|
|
14108
|
+
createSettlementDriver(signer, address),
|
|
14109
|
+
persist
|
|
14110
|
+
);
|
|
14111
|
+
const confirmations = await Promise.all(
|
|
14112
|
+
receiptIds.map((id) => claims.claimedReceipts(id))
|
|
14113
|
+
);
|
|
14114
|
+
if (!confirmations.every(Boolean))
|
|
14115
|
+
throw new Error(
|
|
14116
|
+
"SETTLEMENT_CLAIMS_UNCONFIRMED: receipt flags are not visible; journal retained for reconciliation"
|
|
14117
|
+
);
|
|
13091
14118
|
const batch = {
|
|
13092
14119
|
root,
|
|
13093
14120
|
receiptIds,
|
|
13094
|
-
|
|
13095
|
-
claimTransaction: claimReceipt.hash,
|
|
14121
|
+
...transactions,
|
|
13096
14122
|
settledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13097
14123
|
};
|
|
13098
14124
|
await markBatchSettled(batch);
|
|
14125
|
+
await rm2(journalPath, { force: true });
|
|
13099
14126
|
return { ...batch, skippedReceipts: skipped };
|
|
13100
14127
|
}
|
|
13101
14128
|
async function settleAllRewards(batchSize = 100, maxBatches = 20) {
|
|
@@ -13110,7 +14137,8 @@ async function settleAllRewards(batchSize = 100, maxBatches = 20) {
|
|
|
13110
14137
|
skippedByReceiptId.set(skipped.receiptId, skipped);
|
|
13111
14138
|
}
|
|
13112
14139
|
} catch (error) {
|
|
13113
|
-
const
|
|
14140
|
+
const raw = error instanceof Error ? error.message : "Settlement failed";
|
|
14141
|
+
const message = raw.startsWith("SETTLEMENT_") || raw === "There are no unsettled receipts" ? raw.slice(0, 400) : "SETTLEMENT_FAILED: inspect local diagnostics and saved settlement state before retrying; no private transaction payload is returned.";
|
|
13114
14142
|
if (message === "There are no unsettled receipts") break;
|
|
13115
14143
|
stoppedReason = message;
|
|
13116
14144
|
break;
|
|
@@ -13124,10 +14152,13 @@ async function settleAllRewards(batchSize = 100, maxBatches = 20) {
|
|
|
13124
14152
|
return {
|
|
13125
14153
|
batches,
|
|
13126
14154
|
batchCount: batches.length,
|
|
13127
|
-
settledReceiptCount: batches.reduce(
|
|
14155
|
+
settledReceiptCount: batches.reduce(
|
|
14156
|
+
(total, batch) => total + batch.receiptIds.length,
|
|
14157
|
+
0
|
|
14158
|
+
),
|
|
13128
14159
|
skippedReceipts,
|
|
13129
14160
|
pendingReceiptCount,
|
|
13130
|
-
publishTransactions: batches.map((batch) => batch.publishTransaction),
|
|
14161
|
+
publishTransactions: batches.map((batch) => batch.publishTransaction).filter(Boolean),
|
|
13131
14162
|
claimTransactions: batches.map((batch) => batch.claimTransaction),
|
|
13132
14163
|
stoppedReason,
|
|
13133
14164
|
settledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -13135,8 +14166,8 @@ async function settleAllRewards(batchSize = 100, maxBatches = 20) {
|
|
|
13135
14166
|
}
|
|
13136
14167
|
|
|
13137
14168
|
// src/contract.ts
|
|
13138
|
-
import { readFile as
|
|
13139
|
-
import
|
|
14169
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
14170
|
+
import path7 from "node:path";
|
|
13140
14171
|
var DEFAULT_LOGO_URI = "https://raw.githubusercontent.com/0xddneto/AI-Proof-of-Us/main/assets/token/aipou.png";
|
|
13141
14172
|
var aipouTokenAbi = [
|
|
13142
14173
|
"function name() view returns (string)",
|
|
@@ -13155,9 +14186,9 @@ var aipouClaimsAbi = [
|
|
|
13155
14186
|
];
|
|
13156
14187
|
async function configuredClaimsAddress() {
|
|
13157
14188
|
if (process.env.AIPOU_CLAIMS_ADDRESS) return process.env.AIPOU_CLAIMS_ADDRESS;
|
|
13158
|
-
const claimsPath =
|
|
14189
|
+
const claimsPath = path7.resolve(process.env.AIPOU_CLAIMS_DEPLOYMENT_FILE || "deployments/base-claims.json");
|
|
13159
14190
|
try {
|
|
13160
|
-
const deployment = JSON.parse(await
|
|
14191
|
+
const deployment = JSON.parse(await readFile8(claimsPath, "utf8"));
|
|
13161
14192
|
return deployment.address || null;
|
|
13162
14193
|
} catch (error) {
|
|
13163
14194
|
if (error.code === "ENOENT") return null;
|
|
@@ -13185,9 +14216,9 @@ function envConfig() {
|
|
|
13185
14216
|
};
|
|
13186
14217
|
}
|
|
13187
14218
|
async function deploymentFileConfig() {
|
|
13188
|
-
const deploymentPath =
|
|
14219
|
+
const deploymentPath = path7.resolve(process.env.AIPOU_DEPLOYMENT_FILE || "deployments/base.json");
|
|
13189
14220
|
try {
|
|
13190
|
-
const deployment = JSON.parse(await
|
|
14221
|
+
const deployment = JSON.parse(await readFile8(deploymentPath, "utf8"));
|
|
13191
14222
|
const address = deployment.token?.address;
|
|
13192
14223
|
if (!address) {
|
|
13193
14224
|
return null;
|
|
@@ -13244,10 +14275,10 @@ async function getTokenContractConfig() {
|
|
|
13244
14275
|
|
|
13245
14276
|
// src/demo.ts
|
|
13246
14277
|
import { createHash as createHash3 } from "node:crypto";
|
|
13247
|
-
import { mkdtemp, rm as
|
|
14278
|
+
import { mkdtemp, rm as rm3 } from "node:fs/promises";
|
|
13248
14279
|
import os from "node:os";
|
|
13249
|
-
import
|
|
13250
|
-
import { Wallet as
|
|
14280
|
+
import path8 from "node:path";
|
|
14281
|
+
import { Wallet as Wallet4 } from "ethers";
|
|
13251
14282
|
|
|
13252
14283
|
// src/rewards.ts
|
|
13253
14284
|
var tierMultiplier = {
|
|
@@ -13272,12 +14303,20 @@ async function beginTask(input) {
|
|
|
13272
14303
|
async function completeTask(input) {
|
|
13273
14304
|
const session = await getSession(input.nonce);
|
|
13274
14305
|
if (!session) throw new Error("Unknown task nonce");
|
|
13275
|
-
if (session.completedAt) throw new Error("Task nonce has already been completed");
|
|
14306
|
+
if (session.completedAt) throw new Error("Task nonce has already been completed; use export_ai_receipts and find this nonce to recover its stored receipt. Do not rerun the work or begin a new task to recover a lost response.");
|
|
14307
|
+
if (isTaskSessionExpired(session)) throw new Error("Task session has expired; begin a new task instead of completing stale authority");
|
|
13276
14308
|
if (!verifyTaskAuthorization(session)) throw new Error("Invalid EIP-712 wallet authorization");
|
|
14309
|
+
if (input.tokenUsageMeasurement !== void 0 && !["measured", "unmeasured", "unspecified"].includes(input.tokenUsageMeasurement)) {
|
|
14310
|
+
throw new Error("Unsupported tokenUsageMeasurement");
|
|
14311
|
+
}
|
|
14312
|
+
if (input.tokenUsageMeasurement === "unmeasured" && (input.inputTokens !== 0 || input.outputTokens !== 0)) {
|
|
14313
|
+
throw new Error("Unmeasured token usage must use zero counts");
|
|
14314
|
+
}
|
|
13277
14315
|
const trustTier = await deriveTrustTier(session, input, input.providerEvidence);
|
|
13278
14316
|
const recordedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13279
14317
|
const collectorPublicKey = await getCollectorPublicKey();
|
|
13280
14318
|
const unsigned = {
|
|
14319
|
+
tokenUsageMeasurement: input.tokenUsageMeasurement ?? "unspecified",
|
|
13281
14320
|
nonce: session.nonce,
|
|
13282
14321
|
wallet: session.wallet,
|
|
13283
14322
|
provider: session.provider,
|
|
@@ -13330,8 +14369,8 @@ async function runLocalReceiptDemo() {
|
|
|
13330
14369
|
const previousEnvironment = new Map(
|
|
13331
14370
|
DEMO_ENVIRONMENT_KEYS.map((key) => [key, process.env[key]])
|
|
13332
14371
|
);
|
|
13333
|
-
const dataDir3 = await mkdtemp(
|
|
13334
|
-
const wallet =
|
|
14372
|
+
const dataDir3 = await mkdtemp(path8.join(os.tmpdir(), "aipou-one-command-demo-"));
|
|
14373
|
+
const wallet = Wallet4.createRandom();
|
|
13335
14374
|
process.env.AIPOU_AGENT_PRIVATE_KEY = wallet.privateKey;
|
|
13336
14375
|
process.env.AIPOU_CONTRACT_ADDRESS = TOKEN_ADDRESS;
|
|
13337
14376
|
process.env.AIPOU_CLAIMS_ADDRESS = CLAIMS_ADDRESS;
|
|
@@ -13395,7 +14434,7 @@ async function runLocalReceiptDemo() {
|
|
|
13395
14434
|
}
|
|
13396
14435
|
};
|
|
13397
14436
|
} finally {
|
|
13398
|
-
await
|
|
14437
|
+
await rm3(dataDir3, { recursive: true, force: true });
|
|
13399
14438
|
for (const key of DEMO_ENVIRONMENT_KEYS) {
|
|
13400
14439
|
const previous = previousEnvironment.get(key);
|
|
13401
14440
|
if (previous === void 0) delete process.env[key];
|
|
@@ -13404,25 +14443,92 @@ async function runLocalReceiptDemo() {
|
|
|
13404
14443
|
}
|
|
13405
14444
|
}
|
|
13406
14445
|
|
|
14446
|
+
// src/observed-outcome-pilot.ts
|
|
14447
|
+
import { mkdtemp as mkdtemp2, rm as rm4 } from "node:fs/promises";
|
|
14448
|
+
import os2 from "node:os";
|
|
14449
|
+
import path9 from "node:path";
|
|
14450
|
+
import { Wallet as Wallet5 } from "ethers";
|
|
14451
|
+
var CLAIMS_ADDRESS2 = "0x4ca4C98fB784D20EdC8E2A7F531dAab4c6e53058";
|
|
14452
|
+
var ENVIRONMENT_KEYS = ["AIPOU_AGENT_PRIVATE_KEY", "AIPOU_DATA_DIR"];
|
|
14453
|
+
async function runObservedOutcomePilot() {
|
|
14454
|
+
const previous = new Map(ENVIRONMENT_KEYS.map((key) => [key, process.env[key]]));
|
|
14455
|
+
const dataDir3 = await mkdtemp2(path9.join(os2.tmpdir(), "aipou-observed-outcome-pilot-"));
|
|
14456
|
+
process.env.AIPOU_DATA_DIR = dataDir3;
|
|
14457
|
+
process.env.AIPOU_AGENT_PRIVATE_KEY = Wallet5.createRandom().privateKey;
|
|
14458
|
+
try {
|
|
14459
|
+
const session = await beginTask({
|
|
14460
|
+
provider: "local-pilot",
|
|
14461
|
+
model: "observed-outcome-pilot",
|
|
14462
|
+
client: "aipou-cli",
|
|
14463
|
+
taskHash: sha256Hex(canonicalJson({ kind: "pilot", action: "bounded_local_execution" })),
|
|
14464
|
+
chainId: 8453,
|
|
14465
|
+
verifyingContract: CLAIMS_ADDRESS2
|
|
14466
|
+
});
|
|
14467
|
+
const receipt = await completeTask({
|
|
14468
|
+
nonce: session.nonce,
|
|
14469
|
+
inputTokens: 24,
|
|
14470
|
+
outputTokens: 16,
|
|
14471
|
+
durationSeconds: 1,
|
|
14472
|
+
outputHash: sha256Hex(canonicalJson({ artifact: "pilot-output-reference", status: "host-observed" }))
|
|
14473
|
+
});
|
|
14474
|
+
const verification = verifyAipouReceipt(receipt);
|
|
14475
|
+
if (!verification.valid) throw new Error(`Pilot receipt verification failed: ${verification.errors.join("; ")}`);
|
|
14476
|
+
return {
|
|
14477
|
+
mode: "observed_outcome_pilot",
|
|
14478
|
+
storage: "removed_after_pilot",
|
|
14479
|
+
workReceiptId: receipt.receiptId,
|
|
14480
|
+
evidence: verification.evidence,
|
|
14481
|
+
safety: {
|
|
14482
|
+
networkRequired: false,
|
|
14483
|
+
fundsMoved: false,
|
|
14484
|
+
claimSubmitted: false,
|
|
14485
|
+
deliveryClaimed: false,
|
|
14486
|
+
acceptanceClaimed: false
|
|
14487
|
+
}
|
|
14488
|
+
};
|
|
14489
|
+
} finally {
|
|
14490
|
+
await rm4(dataDir3, { recursive: true, force: true });
|
|
14491
|
+
for (const key of ENVIRONMENT_KEYS) {
|
|
14492
|
+
const value = previous.get(key);
|
|
14493
|
+
if (value === void 0) delete process.env[key];
|
|
14494
|
+
else process.env[key] = value;
|
|
14495
|
+
}
|
|
14496
|
+
}
|
|
14497
|
+
}
|
|
14498
|
+
|
|
13407
14499
|
// src/doctor.ts
|
|
13408
14500
|
import { constants } from "node:fs";
|
|
13409
|
-
import { access, readFile as
|
|
13410
|
-
import
|
|
13411
|
-
import
|
|
13412
|
-
import { Wallet as
|
|
14501
|
+
import { access, readFile as readFile10, stat as stat2 } from "node:fs/promises";
|
|
14502
|
+
import os4 from "node:os";
|
|
14503
|
+
import path11 from "node:path";
|
|
14504
|
+
import { Wallet as Wallet7, isAddress as isAddress2 } from "ethers";
|
|
13413
14505
|
|
|
13414
14506
|
// src/init.ts
|
|
13415
|
-
import { mkdir as
|
|
13416
|
-
import
|
|
13417
|
-
import
|
|
13418
|
-
import {
|
|
14507
|
+
import { mkdir as mkdir5, readFile as readFile9, rm as rm5, writeFile as writeFile3 } from "node:fs/promises";
|
|
14508
|
+
import os3 from "node:os";
|
|
14509
|
+
import path10 from "node:path";
|
|
14510
|
+
import { fileURLToPath } from "node:url";
|
|
14511
|
+
import { Wallet as Wallet6 } from "ethers";
|
|
13419
14512
|
var AIPOU_TOKEN_ADDRESS = "0x55f0Cc5e51A1284D20337d6cbb18938C8A1ABCbB";
|
|
13420
14513
|
var AIPOU_CLAIMS_ADDRESS = "0x4ca4C98fB784D20EdC8E2A7F531dAab4c6e53058";
|
|
13421
14514
|
function identityPaths(dataDirInput) {
|
|
13422
|
-
const dataDir3 =
|
|
13423
|
-
return { dataDir: dataDir3, keyFile:
|
|
14515
|
+
const dataDir3 = path10.resolve(dataDirInput || path10.join(os3.homedir(), ".aipou"));
|
|
14516
|
+
return { dataDir: dataDir3, keyFile: path10.join(dataDir3, "agent-wallet.key") };
|
|
14517
|
+
}
|
|
14518
|
+
async function installationCommand(moduleUrl = import.meta.url) {
|
|
14519
|
+
const directory = path10.dirname(fileURLToPath(moduleUrl));
|
|
14520
|
+
try {
|
|
14521
|
+
const workspace = JSON.parse(await readFile9(path10.resolve(directory, "../../package.json"), "utf8"));
|
|
14522
|
+
if (workspace.name === "ai-proof-of-us" && workspace.workspaces?.includes("mcp-server")) {
|
|
14523
|
+
return { command: process.execPath, args: [path10.join(directory, "index.js")] };
|
|
14524
|
+
}
|
|
14525
|
+
} catch {
|
|
14526
|
+
}
|
|
14527
|
+
const pkg = JSON.parse(await readFile9(path10.resolve(directory, "../package.json"), "utf8"));
|
|
14528
|
+
return { command: "npx", args: ["-y", `aipou-mcp-server@${pkg.version}`] };
|
|
13424
14529
|
}
|
|
13425
|
-
function buildResult(mode, walletAddress, dataDir3, keyFile) {
|
|
14530
|
+
async function buildResult(mode, walletAddress, dataDir3, keyFile) {
|
|
14531
|
+
const launch = await installationCommand();
|
|
13426
14532
|
return {
|
|
13427
14533
|
mode,
|
|
13428
14534
|
wallet: walletAddress,
|
|
@@ -13432,8 +14538,7 @@ function buildResult(mode, walletAddress, dataDir3, keyFile) {
|
|
|
13432
14538
|
mcpConfig: {
|
|
13433
14539
|
mcpServers: {
|
|
13434
14540
|
aipou: {
|
|
13435
|
-
|
|
13436
|
-
args: ["-y", "aipou-mcp-server"],
|
|
14541
|
+
...launch,
|
|
13437
14542
|
env: {
|
|
13438
14543
|
AIPOU_AGENT_KEY_FILE: keyFile,
|
|
13439
14544
|
AIPOU_DATA_DIR: dataDir3,
|
|
@@ -13454,9 +14559,9 @@ function buildResult(mode, walletAddress, dataDir3, keyFile) {
|
|
|
13454
14559
|
}
|
|
13455
14560
|
async function initializePersistentIdentity(dataDirInput) {
|
|
13456
14561
|
const { dataDir: dataDir3, keyFile } = identityPaths(dataDirInput);
|
|
13457
|
-
const wallet =
|
|
13458
|
-
await
|
|
13459
|
-
await
|
|
14562
|
+
const wallet = Wallet6.createRandom();
|
|
14563
|
+
await mkdir5(dataDir3, { recursive: true, mode: 448 });
|
|
14564
|
+
await writeFile3(keyFile, `${wallet.privateKey}
|
|
13460
14565
|
`, {
|
|
13461
14566
|
encoding: "utf8",
|
|
13462
14567
|
mode: 384,
|
|
@@ -13464,15 +14569,15 @@ async function initializePersistentIdentity(dataDirInput) {
|
|
|
13464
14569
|
});
|
|
13465
14570
|
const platformAccessRestricted = await restrictToCurrentUser(keyFile);
|
|
13466
14571
|
if (!platformAccessRestricted) {
|
|
13467
|
-
await
|
|
14572
|
+
await rm5(keyFile, { force: true });
|
|
13468
14573
|
throw new Error("Could not restrict dedicated-wallet key access to the current user");
|
|
13469
14574
|
}
|
|
13470
14575
|
return buildResult("persistent_identity_initialized", wallet.address, dataDir3, keyFile);
|
|
13471
14576
|
}
|
|
13472
14577
|
async function describeExistingIdentity(dataDirInput) {
|
|
13473
14578
|
const { dataDir: dataDir3, keyFile } = identityPaths(dataDirInput);
|
|
13474
|
-
const privateKey = (await
|
|
13475
|
-
const wallet = new
|
|
14579
|
+
const privateKey = (await readFile9(keyFile, "utf8")).trim();
|
|
14580
|
+
const wallet = new Wallet6(privateKey);
|
|
13476
14581
|
const platformAccessRestricted = await restrictToCurrentUser(keyFile);
|
|
13477
14582
|
if (!platformAccessRestricted) {
|
|
13478
14583
|
throw new Error("Existing dedicated-wallet key found, but its access could not be restricted to the current user");
|
|
@@ -13480,18 +14585,6 @@ async function describeExistingIdentity(dataDirInput) {
|
|
|
13480
14585
|
return buildResult("persistent_identity_already_initialized", wallet.address, dataDir3, keyFile);
|
|
13481
14586
|
}
|
|
13482
14587
|
|
|
13483
|
-
// src/version.ts
|
|
13484
|
-
import { readFile as readFile7 } from "node:fs/promises";
|
|
13485
|
-
var SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
13486
|
-
async function getPackageVersion() {
|
|
13487
|
-
const packageUrl = new URL("../package.json", import.meta.url);
|
|
13488
|
-
const metadata = JSON.parse(await readFile7(packageUrl, "utf8"));
|
|
13489
|
-
if (typeof metadata.version !== "string" || !SEMVER.test(metadata.version)) {
|
|
13490
|
-
throw new Error("mcp-server/package.json must contain a valid semantic version");
|
|
13491
|
-
}
|
|
13492
|
-
return metadata.version;
|
|
13493
|
-
}
|
|
13494
|
-
|
|
13495
14588
|
// src/doctor.ts
|
|
13496
14589
|
function pass(detail) {
|
|
13497
14590
|
return { status: "pass", detail };
|
|
@@ -13503,10 +14596,10 @@ function warning(detail) {
|
|
|
13503
14596
|
return { status: "warning", detail };
|
|
13504
14597
|
}
|
|
13505
14598
|
async function runInstallationDoctor(dataDirInput) {
|
|
13506
|
-
const dataDir3 =
|
|
14599
|
+
const dataDir3 = path11.resolve(dataDirInput || process.env.AIPOU_DATA_DIR || path11.join(os4.homedir(), ".aipou"));
|
|
13507
14600
|
const configuredKeyFile = dataDirInput ? void 0 : process.env.AIPOU_AGENT_KEY_FILE?.trim();
|
|
13508
14601
|
const inlinePrivateKey = dataDirInput ? void 0 : process.env.AIPOU_AGENT_PRIVATE_KEY?.trim();
|
|
13509
|
-
const keyFile = inlinePrivateKey ? null :
|
|
14602
|
+
const keyFile = inlinePrivateKey ? null : path11.resolve(configuredKeyFile || path11.join(dataDir3, "agent-wallet.key"));
|
|
13510
14603
|
const nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
13511
14604
|
const tokenAddress = process.env.AIPOU_CONTRACT_ADDRESS?.trim() || AIPOU_TOKEN_ADDRESS;
|
|
13512
14605
|
const claimsAddress2 = process.env.AIPOU_CLAIMS_ADDRESS?.trim() || AIPOU_CLAIMS_ADDRESS;
|
|
@@ -13521,9 +14614,9 @@ async function runInstallationDoctor(dataDirInput) {
|
|
|
13521
14614
|
let identity;
|
|
13522
14615
|
let keySource;
|
|
13523
14616
|
try {
|
|
13524
|
-
const privateKey = inlinePrivateKey || (keyFile ? (await
|
|
14617
|
+
const privateKey = inlinePrivateKey || (keyFile ? (await readFile10(keyFile, "utf8")).trim() : "");
|
|
13525
14618
|
if (!privateKey) throw new Error("missing");
|
|
13526
|
-
wallet = new
|
|
14619
|
+
wallet = new Wallet7(privateKey).address;
|
|
13527
14620
|
keySource = inlinePrivateKey ? "inline_private_key" : "key_file";
|
|
13528
14621
|
identity = inlinePrivateKey ? warning("Dedicated wallet key is valid, but a protected key file is safer than an inline environment variable.") : pass("Dedicated-wallet key file is readable and valid; its secret was not returned.");
|
|
13529
14622
|
} catch {
|
|
@@ -13539,7 +14632,7 @@ async function runInstallationDoctor(dataDirInput) {
|
|
|
13539
14632
|
try {
|
|
13540
14633
|
const keyStat = await stat2(keyFile);
|
|
13541
14634
|
if (process.platform === "win32") {
|
|
13542
|
-
keyPermissions =
|
|
14635
|
+
keyPermissions = { status: "not_checked", detail: "Windows ACLs are not inspected by this read-only doctor. This is not a detected permission failure; init reports whether it successfully applied access restrictions." };
|
|
13543
14636
|
} else if ((keyStat.mode & 63) === 0) {
|
|
13544
14637
|
keyPermissions = pass("Key file has no group or other permission bits.");
|
|
13545
14638
|
} else {
|
|
@@ -13594,8 +14687,103 @@ function buildReceiptResultMeta(receipt) {
|
|
|
13594
14687
|
};
|
|
13595
14688
|
}
|
|
13596
14689
|
|
|
14690
|
+
// src/paybox-link.ts
|
|
14691
|
+
var PAYBOX_OPERATION_SCHEME = "paybox-operation-v1";
|
|
14692
|
+
var EXTERNAL_EVIDENCE_LINK_SCHEME = "external-evidence-link-v1";
|
|
14693
|
+
var receiptIdPattern = /^0x[0-9a-f]{64}$/;
|
|
14694
|
+
var sha256Pattern = /^sha256:[0-9a-f]{64}$/;
|
|
14695
|
+
function assertValidInput(input) {
|
|
14696
|
+
if (!receiptIdPattern.test(input.workReceiptId)) {
|
|
14697
|
+
throw new Error("workReceiptId must be a lowercase 0x-prefixed SHA-256 identifier");
|
|
14698
|
+
}
|
|
14699
|
+
if (!sha256Pattern.test(input.payboxOperationDigest)) {
|
|
14700
|
+
throw new Error("payboxOperationDigest must be a lowercase sha256 digest");
|
|
14701
|
+
}
|
|
14702
|
+
if (input.payboxOperationId.length < 1 || input.payboxOperationId.length > 256) {
|
|
14703
|
+
throw new Error("payboxOperationId must be an opaque reference between 1 and 256 characters");
|
|
14704
|
+
}
|
|
14705
|
+
if (!Number.isFinite(Date.parse(input.issuedAt))) {
|
|
14706
|
+
throw new Error("issuedAt must be an ISO-8601 timestamp");
|
|
14707
|
+
}
|
|
14708
|
+
}
|
|
14709
|
+
function createPayboxWorkLink(input) {
|
|
14710
|
+
assertValidInput(input);
|
|
14711
|
+
const identity = {
|
|
14712
|
+
scheme: EXTERNAL_EVIDENCE_LINK_SCHEME,
|
|
14713
|
+
relation: "supports",
|
|
14714
|
+
source: {
|
|
14715
|
+
kind: "receipt",
|
|
14716
|
+
scheme: "aipou-receipt-v1",
|
|
14717
|
+
id: input.workReceiptId,
|
|
14718
|
+
digest: `sha256:${input.workReceiptId.slice(2)}`
|
|
14719
|
+
},
|
|
14720
|
+
target: {
|
|
14721
|
+
kind: "payment-operation",
|
|
14722
|
+
scheme: PAYBOX_OPERATION_SCHEME,
|
|
14723
|
+
id: input.payboxOperationId,
|
|
14724
|
+
digest: input.payboxOperationDigest
|
|
14725
|
+
},
|
|
14726
|
+
privacy: "digest_only"
|
|
14727
|
+
};
|
|
14728
|
+
const payload = {
|
|
14729
|
+
...identity,
|
|
14730
|
+
issuedAt: input.issuedAt
|
|
14731
|
+
};
|
|
14732
|
+
return {
|
|
14733
|
+
...payload,
|
|
14734
|
+
linkId: `sha256:${sha256Hex(canonicalJson(identity)).slice(2)}`,
|
|
14735
|
+
linkDigest: `sha256:${sha256Hex(canonicalJson(payload)).slice(2)}`
|
|
14736
|
+
};
|
|
14737
|
+
}
|
|
14738
|
+
|
|
14739
|
+
// src/technocore-link.ts
|
|
14740
|
+
var TECHNOCORE_TRANSPORT_SCHEME = "technocore-room-receipt-v1";
|
|
14741
|
+
var EXTERNAL_EVIDENCE_LINK_SCHEME2 = "external-evidence-link-v1";
|
|
14742
|
+
var receiptIdPattern2 = /^0x[0-9a-f]{64}$/;
|
|
14743
|
+
var sha256Pattern2 = /^sha256:[0-9a-f]{64}$/;
|
|
14744
|
+
function assertValidInput2(input) {
|
|
14745
|
+
if (!receiptIdPattern2.test(input.workReceiptId)) {
|
|
14746
|
+
throw new Error("workReceiptId must be a lowercase 0x-prefixed SHA-256 identifier");
|
|
14747
|
+
}
|
|
14748
|
+
if (!sha256Pattern2.test(input.transportArtifactDigest)) {
|
|
14749
|
+
throw new Error("transportArtifactDigest must be a lowercase sha256 digest");
|
|
14750
|
+
}
|
|
14751
|
+
if (input.transportArtifactId.length < 1 || input.transportArtifactId.length > 256) {
|
|
14752
|
+
throw new Error("transportArtifactId must be an opaque reference between 1 and 256 characters");
|
|
14753
|
+
}
|
|
14754
|
+
if (!Number.isFinite(Date.parse(input.issuedAt))) {
|
|
14755
|
+
throw new Error("issuedAt must be an ISO-8601 timestamp");
|
|
14756
|
+
}
|
|
14757
|
+
}
|
|
14758
|
+
function createTechnocoreWorkLink(input) {
|
|
14759
|
+
assertValidInput2(input);
|
|
14760
|
+
const identity = {
|
|
14761
|
+
scheme: EXTERNAL_EVIDENCE_LINK_SCHEME2,
|
|
14762
|
+
relation: "supports",
|
|
14763
|
+
source: {
|
|
14764
|
+
kind: "receipt",
|
|
14765
|
+
scheme: "aipou-receipt-v1",
|
|
14766
|
+
id: input.workReceiptId,
|
|
14767
|
+
digest: `sha256:${input.workReceiptId.slice(2)}`
|
|
14768
|
+
},
|
|
14769
|
+
target: {
|
|
14770
|
+
kind: "transport-record",
|
|
14771
|
+
scheme: TECHNOCORE_TRANSPORT_SCHEME,
|
|
14772
|
+
id: input.transportArtifactId,
|
|
14773
|
+
digest: input.transportArtifactDigest
|
|
14774
|
+
},
|
|
14775
|
+
privacy: "digest_only"
|
|
14776
|
+
};
|
|
14777
|
+
const payload = { ...identity, issuedAt: input.issuedAt };
|
|
14778
|
+
return {
|
|
14779
|
+
...payload,
|
|
14780
|
+
linkId: `sha256:${sha256Hex(canonicalJson(identity)).slice(2)}`,
|
|
14781
|
+
linkDigest: `sha256:${sha256Hex(canonicalJson(payload)).slice(2)}`
|
|
14782
|
+
};
|
|
14783
|
+
}
|
|
14784
|
+
|
|
13597
14785
|
// src/status.ts
|
|
13598
|
-
import { Contract as
|
|
14786
|
+
import { Contract as Contract3, JsonRpcProvider as JsonRpcProvider3, formatUnits } from "ethers";
|
|
13599
14787
|
function sumRewards(receipts) {
|
|
13600
14788
|
const total = receipts.reduce((sum, receipt) => sum + Number(receipt.estimatedReward), 0);
|
|
13601
14789
|
return Number.isInteger(total) ? total.toFixed(0) : total.toFixed(6).replace(/0+$/, "").replace(/\.$/, "");
|
|
@@ -13611,8 +14799,8 @@ async function getAipouStatus() {
|
|
|
13611
14799
|
let onchain = { available: false };
|
|
13612
14800
|
if (contract.address) {
|
|
13613
14801
|
try {
|
|
13614
|
-
const provider = new
|
|
13615
|
-
const token = new
|
|
14802
|
+
const provider = new JsonRpcProvider3(contract.rpcUrl);
|
|
14803
|
+
const token = new Contract3(contract.address, aipouTokenAbi, provider);
|
|
13616
14804
|
const [decimals, balance, totalSupply] = await Promise.all([
|
|
13617
14805
|
token.decimals(),
|
|
13618
14806
|
token.balanceOf(wallet),
|
|
@@ -13643,6 +14831,8 @@ async function getAipouStatus() {
|
|
|
13643
14831
|
claimed: claimedReceipts.length,
|
|
13644
14832
|
pending: walletPendingReceipts.length,
|
|
13645
14833
|
incompleteSessions: counts.incompleteSessions,
|
|
14834
|
+
activeSessions: counts.activeSessions,
|
|
14835
|
+
expiredSessions: counts.expiredSessions,
|
|
13646
14836
|
batches: counts.batches
|
|
13647
14837
|
},
|
|
13648
14838
|
rewards: {
|
|
@@ -13664,9 +14854,9 @@ async function getAipouStatus() {
|
|
|
13664
14854
|
// src/index.ts
|
|
13665
14855
|
var bytes32 = z2.string().regex(/^0x[a-fA-F0-9]{64}$/);
|
|
13666
14856
|
var usageCounts = {
|
|
13667
|
-
inputTokens: z2.number().int().min(0).max(1e7),
|
|
13668
|
-
outputTokens: z2.number().int().min(0).max(1e7),
|
|
13669
|
-
durationSeconds: z2.number().int().min(0).max(86400)
|
|
14857
|
+
inputTokens: z2.number().int().min(0).max(1e7).describe("Non-negative input-token count for this task, capped at 10,000,000."),
|
|
14858
|
+
outputTokens: z2.number().int().min(0).max(1e7).describe("Non-negative output-token count for this task, capped at 10,000,000."),
|
|
14859
|
+
durationSeconds: z2.number().int().min(0).max(86400).describe("Task duration in whole seconds from 0 through 86,400.")
|
|
13670
14860
|
};
|
|
13671
14861
|
var server = new McpServer(
|
|
13672
14862
|
{ name: "aipou-mcp", version: await getPackageVersion() },
|
|
@@ -13676,8 +14866,14 @@ var server = new McpServer(
|
|
|
13676
14866
|
);
|
|
13677
14867
|
server.tool(
|
|
13678
14868
|
"get_aipou_contract",
|
|
13679
|
-
"
|
|
14869
|
+
"Read the configured AIPOU token and claims contract details for display or client setup. Returns Base chain metadata, explorer URLs, and minimal ABIs; it does not contact a wallet, submit a transaction, or change local state.",
|
|
13680
14870
|
{},
|
|
14871
|
+
{
|
|
14872
|
+
readOnlyHint: true,
|
|
14873
|
+
destructiveHint: false,
|
|
14874
|
+
idempotentHint: true,
|
|
14875
|
+
openWorldHint: false
|
|
14876
|
+
},
|
|
13681
14877
|
async () => {
|
|
13682
14878
|
const contract = await getTokenContractConfig();
|
|
13683
14879
|
return {
|
|
@@ -13687,8 +14883,14 @@ server.tool(
|
|
|
13687
14883
|
);
|
|
13688
14884
|
server.tool(
|
|
13689
14885
|
"get_aipou_identity",
|
|
13690
|
-
"
|
|
14886
|
+
"Read the public identity used by this MCP installation when a client needs to display or verify its farming wallet and collector. Returns the wallet address, Ed25519 public key, and fingerprint; it never returns private keys, submits transactions, or changes state.",
|
|
13691
14887
|
{},
|
|
14888
|
+
{
|
|
14889
|
+
readOnlyHint: true,
|
|
14890
|
+
destructiveHint: false,
|
|
14891
|
+
idempotentHint: true,
|
|
14892
|
+
openWorldHint: false
|
|
14893
|
+
},
|
|
13692
14894
|
async () => {
|
|
13693
14895
|
const wallet = agentWallet();
|
|
13694
14896
|
const collectorPublicKey = await getCollectorPublicKey();
|
|
@@ -13702,8 +14904,14 @@ server.tool(
|
|
|
13702
14904
|
);
|
|
13703
14905
|
server.tool(
|
|
13704
14906
|
"get_aipou_status",
|
|
13705
|
-
"
|
|
14907
|
+
"Use this first when the user asks for pending, claimed, or wallet-balance information; use a settlement tool only after an explicit claim request. Returns a read-only JSON summary of receipt counts, estimated pending AIPOU, prior settlement state, and the farming wallet's on-chain AIPOU balance. It may read Base through the configured RPC, but it never signs or submits a transaction, changes receipts, reveals private keys, or returns full receipt payloads.",
|
|
13706
14908
|
{},
|
|
14909
|
+
{
|
|
14910
|
+
readOnlyHint: true,
|
|
14911
|
+
destructiveHint: false,
|
|
14912
|
+
idempotentHint: true,
|
|
14913
|
+
openWorldHint: true
|
|
14914
|
+
},
|
|
13707
14915
|
async () => {
|
|
13708
14916
|
const status = await getAipouStatus();
|
|
13709
14917
|
return { content: [{ type: "text", text: JSON.stringify(status, null, 2) }] };
|
|
@@ -13711,8 +14919,14 @@ server.tool(
|
|
|
13711
14919
|
);
|
|
13712
14920
|
server.tool(
|
|
13713
14921
|
"estimate_ai_reward",
|
|
13714
|
-
"
|
|
14922
|
+
"Use before complete_ai_task only when a preview is useful. Provide non-negative inputTokens, outputTokens, and durationSeconds for one task; it returns JSON with estimatedReward and unit for a client-signed estimate. It creates no task or receipt, changes no state, and submits no transaction. The result is informational: complete_ai_task derives the receipt evidence, and the validator determines final eligibility and trust tier.",
|
|
13715
14923
|
usageCounts,
|
|
14924
|
+
{
|
|
14925
|
+
readOnlyHint: true,
|
|
14926
|
+
destructiveHint: false,
|
|
14927
|
+
idempotentHint: true,
|
|
14928
|
+
openWorldHint: false
|
|
14929
|
+
},
|
|
13716
14930
|
async (input) => ({
|
|
13717
14931
|
content: [{
|
|
13718
14932
|
type: "text",
|
|
@@ -13722,12 +14936,18 @@ server.tool(
|
|
|
13722
14936
|
);
|
|
13723
14937
|
server.tool(
|
|
13724
14938
|
"begin_ai_task",
|
|
13725
|
-
"
|
|
14939
|
+
"Use once before meaningful AI work to create a task session. Provide the provider, model, client, and a 32-byte hash of the task description; returns a unique nonce and EIP-712 authorization signed by the dedicated farming wallet. This writes a pending local task session but does not publish, mint, or submit an on-chain transaction. Pass the returned nonce to complete_ai_task; do not reuse it for another task.",
|
|
14940
|
+
{
|
|
14941
|
+
provider: z2.string().min(1).max(64).describe("AI provider name for this task session, from 1 through 64 characters."),
|
|
14942
|
+
model: z2.string().min(1).max(128).describe("Provider model identifier used for this task, from 1 through 128 characters."),
|
|
14943
|
+
taskHash: bytes32.describe("32-byte hash of the task description; never send the raw prompt or task text."),
|
|
14944
|
+
client: z2.string().min(1).max(64).describe("Host client or agent identifier that starts this task session, from 1 through 64 characters.")
|
|
14945
|
+
},
|
|
13726
14946
|
{
|
|
13727
|
-
|
|
13728
|
-
|
|
13729
|
-
|
|
13730
|
-
|
|
14947
|
+
readOnlyHint: false,
|
|
14948
|
+
destructiveHint: false,
|
|
14949
|
+
idempotentHint: false,
|
|
14950
|
+
openWorldHint: false
|
|
13731
14951
|
},
|
|
13732
14952
|
async (input) => {
|
|
13733
14953
|
const contract = await getTokenContractConfig();
|
|
@@ -13743,15 +14963,22 @@ server.tool(
|
|
|
13743
14963
|
);
|
|
13744
14964
|
server.tool(
|
|
13745
14965
|
"complete_ai_task",
|
|
13746
|
-
"
|
|
14966
|
+
"Use exactly once after begin_ai_task finishes meaningful work. Supply that session's nonce, non-negative inputTokens and outputTokens, whole-second durationSeconds, and a 32-byte outputHash; providerEvidence is optional and only for a configured provider key. It validates the nonce and bounded usage, derives the evidence tier, rejects replay, then writes the local receipt store and returns an Ed25519-signed receipt plus compact workReceiptId metadata. It does not publish a Merkle root, mint tokens, or submit a blockchain transaction. Use get_aipou_status to inspect recorded work; repeating a nonce or evidence fails closed instead of creating another receipt.",
|
|
13747
14967
|
{
|
|
13748
|
-
nonce: bytes32,
|
|
14968
|
+
nonce: bytes32.describe("Unique 32-byte nonce returned by begin_ai_task for this exact task."),
|
|
13749
14969
|
...usageCounts,
|
|
13750
|
-
|
|
14970
|
+
tokenUsageMeasurement: z2.enum(["measured", "unmeasured", "unspecified"]).describe("Whether token counts were measured. Use unmeasured with zero counts when usage is unavailable; omitted means unspecified, including legacy clients.").optional(),
|
|
14971
|
+
outputHash: bytes32.describe("SHA-256-style 32-byte hash of the task output; never send the raw output."),
|
|
13751
14972
|
providerEvidence: z2.object({
|
|
13752
|
-
keyId: z2.string().min(1).max(128),
|
|
13753
|
-
signature: z2.string().min(32)
|
|
13754
|
-
}).optional()
|
|
14973
|
+
keyId: z2.string().min(1).max(128).describe("Configured provider-verification key identifier, between 1 and 128 characters."),
|
|
14974
|
+
signature: z2.string().min(32).describe("Provider signature over the canonical evidence payload; invalid evidence fails closed.")
|
|
14975
|
+
}).describe("Optional provider-signed evidence. Omit it for a client-signed receipt.").optional()
|
|
14976
|
+
},
|
|
14977
|
+
{
|
|
14978
|
+
readOnlyHint: false,
|
|
14979
|
+
destructiveHint: false,
|
|
14980
|
+
idempotentHint: false,
|
|
14981
|
+
openWorldHint: false
|
|
13755
14982
|
},
|
|
13756
14983
|
async (input) => {
|
|
13757
14984
|
const receipt = await completeTask(input);
|
|
@@ -13763,17 +14990,87 @@ server.tool(
|
|
|
13763
14990
|
);
|
|
13764
14991
|
server.tool(
|
|
13765
14992
|
"export_ai_receipts",
|
|
13766
|
-
"Export stored
|
|
13767
|
-
{
|
|
14993
|
+
"Export signed receipts already stored in this MCP installation, optionally limited to one farming wallet address. Returns JSON with count and receipt payloads, so use it only where local receipt data is appropriate to expose. It does not create a receipt, validate a claim, contact Base, or submit a transaction; use get_aipou_status instead for a compact private summary.",
|
|
14994
|
+
{
|
|
14995
|
+
wallet: z2.string().regex(/^0x[a-fA-F0-9]{40}$/).describe("Optional 20-byte farming wallet address filter. Omit it to export every locally stored receipt.").optional()
|
|
14996
|
+
},
|
|
14997
|
+
{
|
|
14998
|
+
readOnlyHint: true,
|
|
14999
|
+
destructiveHint: false,
|
|
15000
|
+
idempotentHint: true,
|
|
15001
|
+
openWorldHint: false
|
|
15002
|
+
},
|
|
13768
15003
|
async ({ wallet }) => {
|
|
13769
15004
|
const receipts = await exportReceipts(wallet);
|
|
13770
15005
|
return { content: [{ type: "text", text: JSON.stringify({ count: receipts.length, receipts }, null, 2) }] };
|
|
13771
15006
|
}
|
|
13772
15007
|
);
|
|
15008
|
+
server.tool(
|
|
15009
|
+
"verify_aipou_receipt",
|
|
15010
|
+
"Verify the self-contained integrity, wallet authorization, and collector signature of one supplied AIPOU receipt. Returns separately typed evidence states; it does not trust a collector, query a registry, submit a claim, prove delivery, business acceptance, settlement, or external effect.",
|
|
15011
|
+
{
|
|
15012
|
+
receipt: z2.record(z2.string(), z2.unknown()).describe("A receipt object containing hashes and signatures only. Do not include raw prompts, outputs, private keys, or customer data.")
|
|
15013
|
+
},
|
|
15014
|
+
{
|
|
15015
|
+
readOnlyHint: true,
|
|
15016
|
+
destructiveHint: false,
|
|
15017
|
+
idempotentHint: true,
|
|
15018
|
+
openWorldHint: false
|
|
15019
|
+
},
|
|
15020
|
+
async ({ receipt }) => ({
|
|
15021
|
+
content: [{ type: "text", text: JSON.stringify(verifyAipouReceipt(receipt), null, 2) }]
|
|
15022
|
+
})
|
|
15023
|
+
);
|
|
15024
|
+
server.tool(
|
|
15025
|
+
"create_paybox_work_link",
|
|
15026
|
+
"Derive and return a digest-only external evidence link between an existing AIPOU workReceiptId and an opaque Paybox operation artifact. It is a pure local calculation: it does not persist the link, call Paybox, unlock a wallet, create a payment, sign a transaction, or change AIPOU claim eligibility. Use it for portable correlation only, and verify both artifacts with their native systems before relying on the returned link.",
|
|
15027
|
+
{
|
|
15028
|
+
workReceiptId: z2.string().regex(/^0x[a-f0-9]{64}$/).describe("Existing lowercase AIPOU workReceiptId."),
|
|
15029
|
+
payboxOperationId: z2.string().min(1).max(256).describe("Opaque Paybox operation reference. Never send a credential, wallet address, payment token, or secret."),
|
|
15030
|
+
payboxOperationDigest: z2.string().regex(/^sha256:[a-f0-9]{64}$/).describe("Lowercase SHA-256 digest of the Paybox operation artifact bytes."),
|
|
15031
|
+
issuedAt: z2.string().datetime().describe("ISO-8601 time at which this correlation artifact is issued.")
|
|
15032
|
+
},
|
|
15033
|
+
{
|
|
15034
|
+
readOnlyHint: true,
|
|
15035
|
+
destructiveHint: false,
|
|
15036
|
+
idempotentHint: true,
|
|
15037
|
+
openWorldHint: false
|
|
15038
|
+
},
|
|
15039
|
+
async (input) => ({
|
|
15040
|
+
content: [{ type: "text", text: JSON.stringify(createPayboxWorkLink(input), null, 2) }]
|
|
15041
|
+
})
|
|
15042
|
+
);
|
|
15043
|
+
server.tool(
|
|
15044
|
+
"create_technocore_work_link",
|
|
15045
|
+
"Derive and return a digest-only external evidence link between an existing AIPOU workReceiptId and a Technocore transport artifact. It is a pure local calculation: it does not persist the link, call Technocore, sign a message, read a key, verify a transport signature, change AIPOU claim eligibility, or establish delivery. Use it for portable correlation only, and verify the Technocore artifact with its native verifier before relying on the returned link.",
|
|
15046
|
+
{
|
|
15047
|
+
workReceiptId: z2.string().regex(/^0x[a-f0-9]{64}$/).describe("Existing lowercase AIPOU workReceiptId."),
|
|
15048
|
+
transportArtifactId: z2.string().min(1).max(256).describe("Opaque Technocore transport artifact reference. Never send a DID private key, seed, passphrase, wallet, or secret."),
|
|
15049
|
+
transportArtifactDigest: z2.string().regex(/^sha256:[a-f0-9]{64}$/).describe("Lowercase SHA-256 digest of the canonical Technocore transport artifact bytes."),
|
|
15050
|
+
issuedAt: z2.string().datetime().describe("ISO-8601 time at which this correlation artifact is issued.")
|
|
15051
|
+
},
|
|
15052
|
+
{
|
|
15053
|
+
readOnlyHint: true,
|
|
15054
|
+
destructiveHint: false,
|
|
15055
|
+
idempotentHint: true,
|
|
15056
|
+
openWorldHint: false
|
|
15057
|
+
},
|
|
15058
|
+
async (input) => ({
|
|
15059
|
+
content: [{ type: "text", text: JSON.stringify(createTechnocoreWorkLink(input), null, 2) }]
|
|
15060
|
+
})
|
|
15061
|
+
);
|
|
13773
15062
|
server.tool(
|
|
13774
15063
|
"settle_ai_rewards",
|
|
13775
|
-
"
|
|
13776
|
-
{
|
|
15064
|
+
"Use only after an explicit user request to claim one limited batch, not for status or reward previews. maxReceipts defaults to 25 and is capped at 100; the tool validates eligible local receipts against settlement policy, publishes one Merkle root, and mints the included rewards. It submits two Base transactions, so the host client and user must apply their own confirmation policy. Use settle_all_ai_rewards instead for an explicit request to claim all pending eligible receipts.",
|
|
15065
|
+
{
|
|
15066
|
+
maxReceipts: z2.number().int().min(1).max(100).default(25).describe("Maximum eligible receipts to include in this one settlement batch, from 1 through 100; defaults to 25.")
|
|
15067
|
+
},
|
|
15068
|
+
{
|
|
15069
|
+
readOnlyHint: false,
|
|
15070
|
+
destructiveHint: false,
|
|
15071
|
+
idempotentHint: false,
|
|
15072
|
+
openWorldHint: true
|
|
15073
|
+
},
|
|
13777
15074
|
async ({ maxReceipts }) => {
|
|
13778
15075
|
const batch = await settleRewards(maxReceipts);
|
|
13779
15076
|
return { content: [{ type: "text", text: JSON.stringify(batch, null, 2) }] };
|
|
@@ -13781,10 +15078,16 @@ server.tool(
|
|
|
13781
15078
|
);
|
|
13782
15079
|
server.tool(
|
|
13783
15080
|
"settle_all_ai_rewards",
|
|
13784
|
-
"
|
|
15081
|
+
"Use only after an explicit broad request such as 'claim my AIPOU' or 'settle all pending AIPOU'; do not use it for a status check. batchSize defaults to 100 and maxBatches to 20, with bounds of 1-100 and 1-50, so large queues remain bounded. It validates all currently eligible local receipts and submits Base transactions for each batch: one Merkle-root publication and one claim transaction per batch. The host client and user must apply their own confirmation policy; use settle_ai_rewards when the user asks for one limited batch.",
|
|
15082
|
+
{
|
|
15083
|
+
batchSize: z2.number().int().min(1).max(100).default(100).describe("Eligible receipts per settlement batch, from 1 through 100; defaults to 100."),
|
|
15084
|
+
maxBatches: z2.number().int().min(1).max(50).default(20).describe("Maximum settlement batches to submit in this call, from 1 through 50; defaults to 20.")
|
|
15085
|
+
},
|
|
13785
15086
|
{
|
|
13786
|
-
|
|
13787
|
-
|
|
15087
|
+
readOnlyHint: false,
|
|
15088
|
+
destructiveHint: false,
|
|
15089
|
+
idempotentHint: false,
|
|
15090
|
+
openWorldHint: true
|
|
13788
15091
|
},
|
|
13789
15092
|
async ({ batchSize, maxBatches }) => {
|
|
13790
15093
|
const result = await settleAllRewards(batchSize, maxBatches);
|
|
@@ -13803,6 +15106,10 @@ function cliValue(name) {
|
|
|
13803
15106
|
}
|
|
13804
15107
|
if (cliArguments.has("--demo")) {
|
|
13805
15108
|
console.log(JSON.stringify(await runLocalReceiptDemo(), null, 2));
|
|
15109
|
+
} else if (cliArguments.has("--pilot")) {
|
|
15110
|
+
console.log(JSON.stringify(await runObservedOutcomePilot(), null, 2));
|
|
15111
|
+
} else if (cliArguments.has("--feedback")) {
|
|
15112
|
+
console.log(JSON.stringify(await localFeedbackSummary(), null, 2));
|
|
13806
15113
|
} else if (cliArguments.has("--doctor")) {
|
|
13807
15114
|
const result = await runInstallationDoctor(cliValue("--data-dir"));
|
|
13808
15115
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -13825,10 +15132,12 @@ if (cliArguments.has("--demo")) {
|
|
|
13825
15132
|
Usage:
|
|
13826
15133
|
aipou-mcp Start the MCP stdio server
|
|
13827
15134
|
aipou-mcp --demo Create and verify one disposable local receipt
|
|
15135
|
+
aipou-mcp --pilot Rehearse a bounded lifecycle and report typed evidence states
|
|
13828
15136
|
aipou-mcp --init Create a protected persistent farming identity
|
|
13829
15137
|
aipou-mcp --init --data-dir <path>
|
|
13830
15138
|
Initialize in a specific directory
|
|
13831
15139
|
aipou-mcp --doctor Validate a persistent installation without changes
|
|
15140
|
+
aipou-mcp --feedback Print local usage counts without identifiers or uploads
|
|
13832
15141
|
aipou-mcp --doctor --data-dir <path>
|
|
13833
15142
|
Diagnose a specific identity directory
|
|
13834
15143
|
aipou-mcp --help Show this help
|
|
@@ -13838,8 +15147,18 @@ wallet, collector key, and receipt state are removed before the command exits.
|
|
|
13838
15147
|
Init writes a new dedicated-wallet key with restricted access and never prints
|
|
13839
15148
|
the secret. Re-running init reports the existing identity instead of
|
|
13840
15149
|
overwriting it. Doctor reads configuration without writing files, making
|
|
13841
|
-
network requests, moving funds, or submitting claims
|
|
15150
|
+
network requests, moving funds, or submitting claims. The pilot is also
|
|
15151
|
+
offline and disposable: it does not claim delivery, acceptance, settlement,
|
|
15152
|
+
or external effect.`);
|
|
13842
15153
|
} else {
|
|
13843
|
-
const
|
|
15154
|
+
const input = new BoundedStdioInput();
|
|
15155
|
+
input.on("error", () => {
|
|
15156
|
+
console.error("AIPOU_INPUT_TOO_LARGE: input connection closed; limit each JSON-RPC message to 1 MiB.");
|
|
15157
|
+
process.exitCode = 1;
|
|
15158
|
+
void server.close();
|
|
15159
|
+
process.stdin.destroy();
|
|
15160
|
+
});
|
|
15161
|
+
process.stdin.pipe(input);
|
|
15162
|
+
const transport = new StdioServerTransport(input, process.stdout);
|
|
13844
15163
|
await server.connect(transport);
|
|
13845
15164
|
}
|