@formstr/mcp 0.1.1 → 0.3.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 +17 -5
- package/dist/index.js +2013 -611
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -766,7 +766,7 @@ var require_validation = __commonJS({
|
|
|
766
766
|
var require_receiver = __commonJS({
|
|
767
767
|
"../../node_modules/.pnpm/ws@8.21.0/node_modules/ws/lib/receiver.js"(exports2, module2) {
|
|
768
768
|
"use strict";
|
|
769
|
-
var { Writable } = require("stream");
|
|
769
|
+
var { Writable: Writable2 } = require("stream");
|
|
770
770
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
771
771
|
var {
|
|
772
772
|
BINARY_TYPES,
|
|
@@ -784,7 +784,7 @@ var require_receiver = __commonJS({
|
|
|
784
784
|
var GET_DATA = 4;
|
|
785
785
|
var INFLATING = 5;
|
|
786
786
|
var DEFER_EVENT = 6;
|
|
787
|
-
var Receiver2 = class extends
|
|
787
|
+
var Receiver2 = class extends Writable2 {
|
|
788
788
|
/**
|
|
789
789
|
* Creates a Receiver instance.
|
|
790
790
|
*
|
|
@@ -22056,243 +22056,6 @@ async function validateEvent2(event, url, method, body) {
|
|
|
22056
22056
|
return true;
|
|
22057
22057
|
}
|
|
22058
22058
|
|
|
22059
|
-
// ../../node_modules/.pnpm/@noble+hashes@2.0.1/node_modules/@noble/hashes/pbkdf2.js
|
|
22060
|
-
function pbkdf2Init(hash, _password, _salt, _opts) {
|
|
22061
|
-
ahash(hash);
|
|
22062
|
-
const opts = checkOpts({ dkLen: 32, asyncTick: 10 }, _opts);
|
|
22063
|
-
const { c, dkLen, asyncTick } = opts;
|
|
22064
|
-
anumber(c, "c");
|
|
22065
|
-
anumber(dkLen, "dkLen");
|
|
22066
|
-
anumber(asyncTick, "asyncTick");
|
|
22067
|
-
if (c < 1)
|
|
22068
|
-
throw new Error("iterations (c) must be >= 1");
|
|
22069
|
-
const password = kdfInputToBytes(_password, "password");
|
|
22070
|
-
const salt = kdfInputToBytes(_salt, "salt");
|
|
22071
|
-
const DK = new Uint8Array(dkLen);
|
|
22072
|
-
const PRF = hmac.create(hash, password);
|
|
22073
|
-
const PRFSalt = PRF._cloneInto().update(salt);
|
|
22074
|
-
return { c, dkLen, asyncTick, DK, PRF, PRFSalt };
|
|
22075
|
-
}
|
|
22076
|
-
function pbkdf2Output(PRF, PRFSalt, DK, prfW, u) {
|
|
22077
|
-
PRF.destroy();
|
|
22078
|
-
PRFSalt.destroy();
|
|
22079
|
-
if (prfW)
|
|
22080
|
-
prfW.destroy();
|
|
22081
|
-
clean(u);
|
|
22082
|
-
return DK;
|
|
22083
|
-
}
|
|
22084
|
-
function pbkdf2(hash, password, salt, opts) {
|
|
22085
|
-
const { c, dkLen, DK, PRF, PRFSalt } = pbkdf2Init(hash, password, salt, opts);
|
|
22086
|
-
let prfW;
|
|
22087
|
-
const arr = new Uint8Array(4);
|
|
22088
|
-
const view = createView(arr);
|
|
22089
|
-
const u = new Uint8Array(PRF.outputLen);
|
|
22090
|
-
for (let ti = 1, pos = 0; pos < dkLen; ti++, pos += PRF.outputLen) {
|
|
22091
|
-
const Ti = DK.subarray(pos, pos + PRF.outputLen);
|
|
22092
|
-
view.setInt32(0, ti, false);
|
|
22093
|
-
(prfW = PRFSalt._cloneInto(prfW)).update(arr).digestInto(u);
|
|
22094
|
-
Ti.set(u.subarray(0, Ti.length));
|
|
22095
|
-
for (let ui = 1; ui < c; ui++) {
|
|
22096
|
-
PRF._cloneInto(prfW).update(u).digestInto(u);
|
|
22097
|
-
for (let i4 = 0; i4 < Ti.length; i4++)
|
|
22098
|
-
Ti[i4] ^= u[i4];
|
|
22099
|
-
}
|
|
22100
|
-
}
|
|
22101
|
-
return pbkdf2Output(PRF, PRFSalt, DK, prfW, u);
|
|
22102
|
-
}
|
|
22103
|
-
|
|
22104
|
-
// ../../node_modules/.pnpm/@noble+hashes@2.0.1/node_modules/@noble/hashes/scrypt.js
|
|
22105
|
-
function XorAndSalsa(prev, pi, input, ii, out, oi) {
|
|
22106
|
-
let y00 = prev[pi++] ^ input[ii++], y01 = prev[pi++] ^ input[ii++];
|
|
22107
|
-
let y02 = prev[pi++] ^ input[ii++], y03 = prev[pi++] ^ input[ii++];
|
|
22108
|
-
let y04 = prev[pi++] ^ input[ii++], y05 = prev[pi++] ^ input[ii++];
|
|
22109
|
-
let y06 = prev[pi++] ^ input[ii++], y07 = prev[pi++] ^ input[ii++];
|
|
22110
|
-
let y08 = prev[pi++] ^ input[ii++], y09 = prev[pi++] ^ input[ii++];
|
|
22111
|
-
let y10 = prev[pi++] ^ input[ii++], y11 = prev[pi++] ^ input[ii++];
|
|
22112
|
-
let y12 = prev[pi++] ^ input[ii++], y13 = prev[pi++] ^ input[ii++];
|
|
22113
|
-
let y14 = prev[pi++] ^ input[ii++], y15 = prev[pi++] ^ input[ii++];
|
|
22114
|
-
let x00 = y00, x01 = y01, x02 = y02, x03 = y03, x04 = y04, x05 = y05, x06 = y06, x07 = y07, x08 = y08, x09 = y09, x10 = y10, x11 = y11, x12 = y12, x13 = y13, x14 = y14, x15 = y15;
|
|
22115
|
-
for (let i4 = 0; i4 < 8; i4 += 2) {
|
|
22116
|
-
x04 ^= rotl(x00 + x12 | 0, 7);
|
|
22117
|
-
x08 ^= rotl(x04 + x00 | 0, 9);
|
|
22118
|
-
x12 ^= rotl(x08 + x04 | 0, 13);
|
|
22119
|
-
x00 ^= rotl(x12 + x08 | 0, 18);
|
|
22120
|
-
x09 ^= rotl(x05 + x01 | 0, 7);
|
|
22121
|
-
x13 ^= rotl(x09 + x05 | 0, 9);
|
|
22122
|
-
x01 ^= rotl(x13 + x09 | 0, 13);
|
|
22123
|
-
x05 ^= rotl(x01 + x13 | 0, 18);
|
|
22124
|
-
x14 ^= rotl(x10 + x06 | 0, 7);
|
|
22125
|
-
x02 ^= rotl(x14 + x10 | 0, 9);
|
|
22126
|
-
x06 ^= rotl(x02 + x14 | 0, 13);
|
|
22127
|
-
x10 ^= rotl(x06 + x02 | 0, 18);
|
|
22128
|
-
x03 ^= rotl(x15 + x11 | 0, 7);
|
|
22129
|
-
x07 ^= rotl(x03 + x15 | 0, 9);
|
|
22130
|
-
x11 ^= rotl(x07 + x03 | 0, 13);
|
|
22131
|
-
x15 ^= rotl(x11 + x07 | 0, 18);
|
|
22132
|
-
x01 ^= rotl(x00 + x03 | 0, 7);
|
|
22133
|
-
x02 ^= rotl(x01 + x00 | 0, 9);
|
|
22134
|
-
x03 ^= rotl(x02 + x01 | 0, 13);
|
|
22135
|
-
x00 ^= rotl(x03 + x02 | 0, 18);
|
|
22136
|
-
x06 ^= rotl(x05 + x04 | 0, 7);
|
|
22137
|
-
x07 ^= rotl(x06 + x05 | 0, 9);
|
|
22138
|
-
x04 ^= rotl(x07 + x06 | 0, 13);
|
|
22139
|
-
x05 ^= rotl(x04 + x07 | 0, 18);
|
|
22140
|
-
x11 ^= rotl(x10 + x09 | 0, 7);
|
|
22141
|
-
x08 ^= rotl(x11 + x10 | 0, 9);
|
|
22142
|
-
x09 ^= rotl(x08 + x11 | 0, 13);
|
|
22143
|
-
x10 ^= rotl(x09 + x08 | 0, 18);
|
|
22144
|
-
x12 ^= rotl(x15 + x14 | 0, 7);
|
|
22145
|
-
x13 ^= rotl(x12 + x15 | 0, 9);
|
|
22146
|
-
x14 ^= rotl(x13 + x12 | 0, 13);
|
|
22147
|
-
x15 ^= rotl(x14 + x13 | 0, 18);
|
|
22148
|
-
}
|
|
22149
|
-
out[oi++] = y00 + x00 | 0;
|
|
22150
|
-
out[oi++] = y01 + x01 | 0;
|
|
22151
|
-
out[oi++] = y02 + x02 | 0;
|
|
22152
|
-
out[oi++] = y03 + x03 | 0;
|
|
22153
|
-
out[oi++] = y04 + x04 | 0;
|
|
22154
|
-
out[oi++] = y05 + x05 | 0;
|
|
22155
|
-
out[oi++] = y06 + x06 | 0;
|
|
22156
|
-
out[oi++] = y07 + x07 | 0;
|
|
22157
|
-
out[oi++] = y08 + x08 | 0;
|
|
22158
|
-
out[oi++] = y09 + x09 | 0;
|
|
22159
|
-
out[oi++] = y10 + x10 | 0;
|
|
22160
|
-
out[oi++] = y11 + x11 | 0;
|
|
22161
|
-
out[oi++] = y12 + x12 | 0;
|
|
22162
|
-
out[oi++] = y13 + x13 | 0;
|
|
22163
|
-
out[oi++] = y14 + x14 | 0;
|
|
22164
|
-
out[oi++] = y15 + x15 | 0;
|
|
22165
|
-
}
|
|
22166
|
-
function BlockMix(input, ii, out, oi, r) {
|
|
22167
|
-
let head = oi + 0;
|
|
22168
|
-
let tail = oi + 16 * r;
|
|
22169
|
-
for (let i4 = 0; i4 < 16; i4++)
|
|
22170
|
-
out[tail + i4] = input[ii + (2 * r - 1) * 16 + i4];
|
|
22171
|
-
for (let i4 = 0; i4 < r; i4++, head += 16, ii += 16) {
|
|
22172
|
-
XorAndSalsa(out, tail, input, ii, out, head);
|
|
22173
|
-
if (i4 > 0)
|
|
22174
|
-
tail += 16;
|
|
22175
|
-
XorAndSalsa(out, head, input, ii += 16, out, tail);
|
|
22176
|
-
}
|
|
22177
|
-
}
|
|
22178
|
-
function scryptInit(password, salt, _opts) {
|
|
22179
|
-
const opts = checkOpts({
|
|
22180
|
-
dkLen: 32,
|
|
22181
|
-
asyncTick: 10,
|
|
22182
|
-
maxmem: 1024 ** 3 + 1024
|
|
22183
|
-
}, _opts);
|
|
22184
|
-
const { N, r, p, dkLen, asyncTick, maxmem, onProgress } = opts;
|
|
22185
|
-
anumber(N, "N");
|
|
22186
|
-
anumber(r, "r");
|
|
22187
|
-
anumber(p, "p");
|
|
22188
|
-
anumber(dkLen, "dkLen");
|
|
22189
|
-
anumber(asyncTick, "asyncTick");
|
|
22190
|
-
anumber(maxmem, "maxmem");
|
|
22191
|
-
if (onProgress !== void 0 && typeof onProgress !== "function")
|
|
22192
|
-
throw new Error("progressCb must be a function");
|
|
22193
|
-
const blockSize = 128 * r;
|
|
22194
|
-
const blockSize32 = blockSize / 4;
|
|
22195
|
-
const pow32 = Math.pow(2, 32);
|
|
22196
|
-
if (N <= 1 || (N & N - 1) !== 0 || N > pow32)
|
|
22197
|
-
throw new Error('"N" expected a power of 2, and 2^1 <= N <= 2^32');
|
|
22198
|
-
if (p < 1 || p > (pow32 - 1) * 32 / blockSize)
|
|
22199
|
-
throw new Error('"p" expected integer 1..((2^32 - 1) * 32) / (128 * r)');
|
|
22200
|
-
if (dkLen < 1 || dkLen > (pow32 - 1) * 32)
|
|
22201
|
-
throw new Error('"dkLen" expected integer 1..(2^32 - 1) * 32');
|
|
22202
|
-
const memUsed = blockSize * (N + p);
|
|
22203
|
-
if (memUsed > maxmem)
|
|
22204
|
-
throw new Error('"maxmem" limit was hit, expected 128*r*(N+p) <= "maxmem"=' + maxmem);
|
|
22205
|
-
const B = pbkdf2(sha256, password, salt, { c: 1, dkLen: blockSize * p });
|
|
22206
|
-
const B32 = u32(B);
|
|
22207
|
-
const V = u32(new Uint8Array(blockSize * N));
|
|
22208
|
-
const tmp = u32(new Uint8Array(blockSize));
|
|
22209
|
-
let blockMixCb = () => {
|
|
22210
|
-
};
|
|
22211
|
-
if (onProgress) {
|
|
22212
|
-
const totalBlockMix = 2 * N * p;
|
|
22213
|
-
const callbackPer = Math.max(Math.floor(totalBlockMix / 1e4), 1);
|
|
22214
|
-
let blockMixCnt = 0;
|
|
22215
|
-
blockMixCb = () => {
|
|
22216
|
-
blockMixCnt++;
|
|
22217
|
-
if (onProgress && (!(blockMixCnt % callbackPer) || blockMixCnt === totalBlockMix))
|
|
22218
|
-
onProgress(blockMixCnt / totalBlockMix);
|
|
22219
|
-
};
|
|
22220
|
-
}
|
|
22221
|
-
return { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb, asyncTick };
|
|
22222
|
-
}
|
|
22223
|
-
function scryptOutput(password, dkLen, B, V, tmp) {
|
|
22224
|
-
const res = pbkdf2(sha256, password, B, { c: 1, dkLen });
|
|
22225
|
-
clean(B, V, tmp);
|
|
22226
|
-
return res;
|
|
22227
|
-
}
|
|
22228
|
-
function scrypt(password, salt, opts) {
|
|
22229
|
-
const { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb } = scryptInit(password, salt, opts);
|
|
22230
|
-
swap32IfBE(B32);
|
|
22231
|
-
for (let pi = 0; pi < p; pi++) {
|
|
22232
|
-
const Pi = blockSize32 * pi;
|
|
22233
|
-
for (let i4 = 0; i4 < blockSize32; i4++)
|
|
22234
|
-
V[i4] = B32[Pi + i4];
|
|
22235
|
-
for (let i4 = 0, pos = 0; i4 < N - 1; i4++) {
|
|
22236
|
-
BlockMix(V, pos, V, pos += blockSize32, r);
|
|
22237
|
-
blockMixCb();
|
|
22238
|
-
}
|
|
22239
|
-
BlockMix(V, (N - 1) * blockSize32, B32, Pi, r);
|
|
22240
|
-
blockMixCb();
|
|
22241
|
-
for (let i4 = 0; i4 < N; i4++) {
|
|
22242
|
-
const j = (B32[Pi + blockSize32 - 16] & N - 1) >>> 0;
|
|
22243
|
-
for (let k = 0; k < blockSize32; k++)
|
|
22244
|
-
tmp[k] = B32[Pi + k] ^ V[j * blockSize32 + k];
|
|
22245
|
-
BlockMix(tmp, 0, B32, Pi, r);
|
|
22246
|
-
blockMixCb();
|
|
22247
|
-
}
|
|
22248
|
-
}
|
|
22249
|
-
swap32IfBE(B32);
|
|
22250
|
-
return scryptOutput(password, dkLen, B, V, tmp);
|
|
22251
|
-
}
|
|
22252
|
-
|
|
22253
|
-
// ../../node_modules/.pnpm/nostr-tools@2.23.3_typescript@5.9.3/node_modules/nostr-tools/lib/esm/nip49.js
|
|
22254
|
-
var Bech32MaxSize2 = 5e3;
|
|
22255
|
-
function encodeBech322(prefix, data) {
|
|
22256
|
-
let words = bech32.toWords(data);
|
|
22257
|
-
return bech32.encode(prefix, words, Bech32MaxSize2);
|
|
22258
|
-
}
|
|
22259
|
-
function encodeBytes2(prefix, bytes) {
|
|
22260
|
-
return encodeBech322(prefix, bytes);
|
|
22261
|
-
}
|
|
22262
|
-
function encrypt3(sec, password, logn = 16, ksb = 2) {
|
|
22263
|
-
let salt = randomBytes(16);
|
|
22264
|
-
let n = 2 ** logn;
|
|
22265
|
-
let key = scrypt(password.normalize("NFKC"), salt, { N: n, r: 8, p: 1, dkLen: 32 });
|
|
22266
|
-
let nonce = randomBytes(24);
|
|
22267
|
-
let aad = Uint8Array.from([ksb]);
|
|
22268
|
-
let xc2p1 = xchacha20poly1305(key, nonce, aad);
|
|
22269
|
-
let ciphertext = xc2p1.encrypt(sec);
|
|
22270
|
-
let b = concatBytes(Uint8Array.from([2]), Uint8Array.from([logn]), salt, nonce, aad, ciphertext);
|
|
22271
|
-
return encodeBytes2("ncryptsec", b);
|
|
22272
|
-
}
|
|
22273
|
-
function decrypt3(ncryptsec, password) {
|
|
22274
|
-
let { prefix, words } = bech32.decode(ncryptsec, Bech32MaxSize2);
|
|
22275
|
-
if (prefix !== "ncryptsec") {
|
|
22276
|
-
throw new Error(`invalid prefix ${prefix}, expected 'ncryptsec'`);
|
|
22277
|
-
}
|
|
22278
|
-
let b = new Uint8Array(bech32.fromWords(words));
|
|
22279
|
-
let version2 = b[0];
|
|
22280
|
-
if (version2 !== 2) {
|
|
22281
|
-
throw new Error(`invalid version ${version2}, expected 0x02`);
|
|
22282
|
-
}
|
|
22283
|
-
let logn = b[1];
|
|
22284
|
-
let n = 2 ** logn;
|
|
22285
|
-
let salt = b.slice(2, 2 + 16);
|
|
22286
|
-
let nonce = b.slice(2 + 16, 2 + 16 + 24);
|
|
22287
|
-
let ksb = b[2 + 16 + 24];
|
|
22288
|
-
let aad = Uint8Array.from([ksb]);
|
|
22289
|
-
let ciphertext = b.slice(2 + 16 + 24 + 1);
|
|
22290
|
-
let key = scrypt(password.normalize("NFKC"), salt, { N: n, r: 8, p: 1, dkLen: 32 });
|
|
22291
|
-
let xc2p1 = xchacha20poly1305(key, nonce, aad);
|
|
22292
|
-
let sec = xc2p1.decrypt(ciphertext);
|
|
22293
|
-
return sec;
|
|
22294
|
-
}
|
|
22295
|
-
|
|
22296
22059
|
// ../../node_modules/.pnpm/nostr-tools@2.23.3_typescript@5.9.3/node_modules/nostr-tools/lib/esm/nip46.js
|
|
22297
22060
|
var verifiedSymbol2 = /* @__PURE__ */ Symbol("verified");
|
|
22298
22061
|
var isRecord2 = (obj) => obj instanceof Object;
|
|
@@ -22468,14 +22231,14 @@ function decodePayload2(payload) {
|
|
|
22468
22231
|
mac: data.subarray(-32)
|
|
22469
22232
|
};
|
|
22470
22233
|
}
|
|
22471
|
-
function
|
|
22234
|
+
function encrypt3(plaintext, conversationKey, nonce = randomBytes(32)) {
|
|
22472
22235
|
const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys2(conversationKey, nonce);
|
|
22473
22236
|
const padded = pad2(plaintext);
|
|
22474
22237
|
const ciphertext = chacha20(chacha_key, chacha_nonce, padded);
|
|
22475
22238
|
const mac = hmacAad2(hmac_key, ciphertext, nonce);
|
|
22476
22239
|
return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac));
|
|
22477
22240
|
}
|
|
22478
|
-
function
|
|
22241
|
+
function decrypt3(payload, conversationKey) {
|
|
22479
22242
|
const { nonce, ciphertext, mac } = decodePayload2(payload);
|
|
22480
22243
|
const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys2(conversationKey, nonce);
|
|
22481
22244
|
const calculatedMac = hmacAad2(hmac_key, ciphertext, nonce);
|
|
@@ -23426,7 +23189,7 @@ var BunkerSigner = class {
|
|
|
23426
23189
|
onevent: async (event) => {
|
|
23427
23190
|
try {
|
|
23428
23191
|
const tempConvKey = getConversationKey2(clientSecretKey, event.pubkey);
|
|
23429
|
-
const decryptedContent =
|
|
23192
|
+
const decryptedContent = decrypt3(event.content, tempConvKey);
|
|
23430
23193
|
const response = JSON.parse(decryptedContent);
|
|
23431
23194
|
if (response.result === uri.searchParams.get("secret")) {
|
|
23432
23195
|
sub.close();
|
|
@@ -23469,7 +23232,7 @@ var BunkerSigner = class {
|
|
|
23469
23232
|
},
|
|
23470
23233
|
{
|
|
23471
23234
|
onevent: async (event) => {
|
|
23472
|
-
const o = JSON.parse(
|
|
23235
|
+
const o = JSON.parse(decrypt3(event.content, convKey));
|
|
23473
23236
|
const { id, result, error: error2 } = o;
|
|
23474
23237
|
if (result === "auth_url" && waitingForAuth[id]) {
|
|
23475
23238
|
delete waitingForAuth[id];
|
|
@@ -23531,7 +23294,7 @@ var BunkerSigner = class {
|
|
|
23531
23294
|
this.setupSubscription();
|
|
23532
23295
|
this.serial++;
|
|
23533
23296
|
const id = `${this.idPrefix}-${this.serial}`;
|
|
23534
|
-
const encryptedContent =
|
|
23297
|
+
const encryptedContent = encrypt3(JSON.stringify({ id, method, params }), this.conversationKey);
|
|
23535
23298
|
const verifiedEvent = finalizeEvent2(
|
|
23536
23299
|
{
|
|
23537
23300
|
kind: NostrConnect2,
|
|
@@ -23586,7 +23349,244 @@ var BunkerSigner = class {
|
|
|
23586
23349
|
}
|
|
23587
23350
|
};
|
|
23588
23351
|
|
|
23589
|
-
// ../../node_modules/.pnpm/@
|
|
23352
|
+
// ../../node_modules/.pnpm/@noble+hashes@2.0.1/node_modules/@noble/hashes/pbkdf2.js
|
|
23353
|
+
function pbkdf2Init(hash, _password, _salt, _opts) {
|
|
23354
|
+
ahash(hash);
|
|
23355
|
+
const opts = checkOpts({ dkLen: 32, asyncTick: 10 }, _opts);
|
|
23356
|
+
const { c, dkLen, asyncTick } = opts;
|
|
23357
|
+
anumber(c, "c");
|
|
23358
|
+
anumber(dkLen, "dkLen");
|
|
23359
|
+
anumber(asyncTick, "asyncTick");
|
|
23360
|
+
if (c < 1)
|
|
23361
|
+
throw new Error("iterations (c) must be >= 1");
|
|
23362
|
+
const password = kdfInputToBytes(_password, "password");
|
|
23363
|
+
const salt = kdfInputToBytes(_salt, "salt");
|
|
23364
|
+
const DK = new Uint8Array(dkLen);
|
|
23365
|
+
const PRF = hmac.create(hash, password);
|
|
23366
|
+
const PRFSalt = PRF._cloneInto().update(salt);
|
|
23367
|
+
return { c, dkLen, asyncTick, DK, PRF, PRFSalt };
|
|
23368
|
+
}
|
|
23369
|
+
function pbkdf2Output(PRF, PRFSalt, DK, prfW, u) {
|
|
23370
|
+
PRF.destroy();
|
|
23371
|
+
PRFSalt.destroy();
|
|
23372
|
+
if (prfW)
|
|
23373
|
+
prfW.destroy();
|
|
23374
|
+
clean(u);
|
|
23375
|
+
return DK;
|
|
23376
|
+
}
|
|
23377
|
+
function pbkdf2(hash, password, salt, opts) {
|
|
23378
|
+
const { c, dkLen, DK, PRF, PRFSalt } = pbkdf2Init(hash, password, salt, opts);
|
|
23379
|
+
let prfW;
|
|
23380
|
+
const arr = new Uint8Array(4);
|
|
23381
|
+
const view = createView(arr);
|
|
23382
|
+
const u = new Uint8Array(PRF.outputLen);
|
|
23383
|
+
for (let ti = 1, pos = 0; pos < dkLen; ti++, pos += PRF.outputLen) {
|
|
23384
|
+
const Ti = DK.subarray(pos, pos + PRF.outputLen);
|
|
23385
|
+
view.setInt32(0, ti, false);
|
|
23386
|
+
(prfW = PRFSalt._cloneInto(prfW)).update(arr).digestInto(u);
|
|
23387
|
+
Ti.set(u.subarray(0, Ti.length));
|
|
23388
|
+
for (let ui = 1; ui < c; ui++) {
|
|
23389
|
+
PRF._cloneInto(prfW).update(u).digestInto(u);
|
|
23390
|
+
for (let i4 = 0; i4 < Ti.length; i4++)
|
|
23391
|
+
Ti[i4] ^= u[i4];
|
|
23392
|
+
}
|
|
23393
|
+
}
|
|
23394
|
+
return pbkdf2Output(PRF, PRFSalt, DK, prfW, u);
|
|
23395
|
+
}
|
|
23396
|
+
|
|
23397
|
+
// ../../node_modules/.pnpm/@noble+hashes@2.0.1/node_modules/@noble/hashes/scrypt.js
|
|
23398
|
+
function XorAndSalsa(prev, pi, input, ii, out, oi) {
|
|
23399
|
+
let y00 = prev[pi++] ^ input[ii++], y01 = prev[pi++] ^ input[ii++];
|
|
23400
|
+
let y02 = prev[pi++] ^ input[ii++], y03 = prev[pi++] ^ input[ii++];
|
|
23401
|
+
let y04 = prev[pi++] ^ input[ii++], y05 = prev[pi++] ^ input[ii++];
|
|
23402
|
+
let y06 = prev[pi++] ^ input[ii++], y07 = prev[pi++] ^ input[ii++];
|
|
23403
|
+
let y08 = prev[pi++] ^ input[ii++], y09 = prev[pi++] ^ input[ii++];
|
|
23404
|
+
let y10 = prev[pi++] ^ input[ii++], y11 = prev[pi++] ^ input[ii++];
|
|
23405
|
+
let y12 = prev[pi++] ^ input[ii++], y13 = prev[pi++] ^ input[ii++];
|
|
23406
|
+
let y14 = prev[pi++] ^ input[ii++], y15 = prev[pi++] ^ input[ii++];
|
|
23407
|
+
let x00 = y00, x01 = y01, x02 = y02, x03 = y03, x04 = y04, x05 = y05, x06 = y06, x07 = y07, x08 = y08, x09 = y09, x10 = y10, x11 = y11, x12 = y12, x13 = y13, x14 = y14, x15 = y15;
|
|
23408
|
+
for (let i4 = 0; i4 < 8; i4 += 2) {
|
|
23409
|
+
x04 ^= rotl(x00 + x12 | 0, 7);
|
|
23410
|
+
x08 ^= rotl(x04 + x00 | 0, 9);
|
|
23411
|
+
x12 ^= rotl(x08 + x04 | 0, 13);
|
|
23412
|
+
x00 ^= rotl(x12 + x08 | 0, 18);
|
|
23413
|
+
x09 ^= rotl(x05 + x01 | 0, 7);
|
|
23414
|
+
x13 ^= rotl(x09 + x05 | 0, 9);
|
|
23415
|
+
x01 ^= rotl(x13 + x09 | 0, 13);
|
|
23416
|
+
x05 ^= rotl(x01 + x13 | 0, 18);
|
|
23417
|
+
x14 ^= rotl(x10 + x06 | 0, 7);
|
|
23418
|
+
x02 ^= rotl(x14 + x10 | 0, 9);
|
|
23419
|
+
x06 ^= rotl(x02 + x14 | 0, 13);
|
|
23420
|
+
x10 ^= rotl(x06 + x02 | 0, 18);
|
|
23421
|
+
x03 ^= rotl(x15 + x11 | 0, 7);
|
|
23422
|
+
x07 ^= rotl(x03 + x15 | 0, 9);
|
|
23423
|
+
x11 ^= rotl(x07 + x03 | 0, 13);
|
|
23424
|
+
x15 ^= rotl(x11 + x07 | 0, 18);
|
|
23425
|
+
x01 ^= rotl(x00 + x03 | 0, 7);
|
|
23426
|
+
x02 ^= rotl(x01 + x00 | 0, 9);
|
|
23427
|
+
x03 ^= rotl(x02 + x01 | 0, 13);
|
|
23428
|
+
x00 ^= rotl(x03 + x02 | 0, 18);
|
|
23429
|
+
x06 ^= rotl(x05 + x04 | 0, 7);
|
|
23430
|
+
x07 ^= rotl(x06 + x05 | 0, 9);
|
|
23431
|
+
x04 ^= rotl(x07 + x06 | 0, 13);
|
|
23432
|
+
x05 ^= rotl(x04 + x07 | 0, 18);
|
|
23433
|
+
x11 ^= rotl(x10 + x09 | 0, 7);
|
|
23434
|
+
x08 ^= rotl(x11 + x10 | 0, 9);
|
|
23435
|
+
x09 ^= rotl(x08 + x11 | 0, 13);
|
|
23436
|
+
x10 ^= rotl(x09 + x08 | 0, 18);
|
|
23437
|
+
x12 ^= rotl(x15 + x14 | 0, 7);
|
|
23438
|
+
x13 ^= rotl(x12 + x15 | 0, 9);
|
|
23439
|
+
x14 ^= rotl(x13 + x12 | 0, 13);
|
|
23440
|
+
x15 ^= rotl(x14 + x13 | 0, 18);
|
|
23441
|
+
}
|
|
23442
|
+
out[oi++] = y00 + x00 | 0;
|
|
23443
|
+
out[oi++] = y01 + x01 | 0;
|
|
23444
|
+
out[oi++] = y02 + x02 | 0;
|
|
23445
|
+
out[oi++] = y03 + x03 | 0;
|
|
23446
|
+
out[oi++] = y04 + x04 | 0;
|
|
23447
|
+
out[oi++] = y05 + x05 | 0;
|
|
23448
|
+
out[oi++] = y06 + x06 | 0;
|
|
23449
|
+
out[oi++] = y07 + x07 | 0;
|
|
23450
|
+
out[oi++] = y08 + x08 | 0;
|
|
23451
|
+
out[oi++] = y09 + x09 | 0;
|
|
23452
|
+
out[oi++] = y10 + x10 | 0;
|
|
23453
|
+
out[oi++] = y11 + x11 | 0;
|
|
23454
|
+
out[oi++] = y12 + x12 | 0;
|
|
23455
|
+
out[oi++] = y13 + x13 | 0;
|
|
23456
|
+
out[oi++] = y14 + x14 | 0;
|
|
23457
|
+
out[oi++] = y15 + x15 | 0;
|
|
23458
|
+
}
|
|
23459
|
+
function BlockMix(input, ii, out, oi, r) {
|
|
23460
|
+
let head = oi + 0;
|
|
23461
|
+
let tail = oi + 16 * r;
|
|
23462
|
+
for (let i4 = 0; i4 < 16; i4++)
|
|
23463
|
+
out[tail + i4] = input[ii + (2 * r - 1) * 16 + i4];
|
|
23464
|
+
for (let i4 = 0; i4 < r; i4++, head += 16, ii += 16) {
|
|
23465
|
+
XorAndSalsa(out, tail, input, ii, out, head);
|
|
23466
|
+
if (i4 > 0)
|
|
23467
|
+
tail += 16;
|
|
23468
|
+
XorAndSalsa(out, head, input, ii += 16, out, tail);
|
|
23469
|
+
}
|
|
23470
|
+
}
|
|
23471
|
+
function scryptInit(password, salt, _opts) {
|
|
23472
|
+
const opts = checkOpts({
|
|
23473
|
+
dkLen: 32,
|
|
23474
|
+
asyncTick: 10,
|
|
23475
|
+
maxmem: 1024 ** 3 + 1024
|
|
23476
|
+
}, _opts);
|
|
23477
|
+
const { N, r, p, dkLen, asyncTick, maxmem, onProgress } = opts;
|
|
23478
|
+
anumber(N, "N");
|
|
23479
|
+
anumber(r, "r");
|
|
23480
|
+
anumber(p, "p");
|
|
23481
|
+
anumber(dkLen, "dkLen");
|
|
23482
|
+
anumber(asyncTick, "asyncTick");
|
|
23483
|
+
anumber(maxmem, "maxmem");
|
|
23484
|
+
if (onProgress !== void 0 && typeof onProgress !== "function")
|
|
23485
|
+
throw new Error("progressCb must be a function");
|
|
23486
|
+
const blockSize = 128 * r;
|
|
23487
|
+
const blockSize32 = blockSize / 4;
|
|
23488
|
+
const pow32 = Math.pow(2, 32);
|
|
23489
|
+
if (N <= 1 || (N & N - 1) !== 0 || N > pow32)
|
|
23490
|
+
throw new Error('"N" expected a power of 2, and 2^1 <= N <= 2^32');
|
|
23491
|
+
if (p < 1 || p > (pow32 - 1) * 32 / blockSize)
|
|
23492
|
+
throw new Error('"p" expected integer 1..((2^32 - 1) * 32) / (128 * r)');
|
|
23493
|
+
if (dkLen < 1 || dkLen > (pow32 - 1) * 32)
|
|
23494
|
+
throw new Error('"dkLen" expected integer 1..(2^32 - 1) * 32');
|
|
23495
|
+
const memUsed = blockSize * (N + p);
|
|
23496
|
+
if (memUsed > maxmem)
|
|
23497
|
+
throw new Error('"maxmem" limit was hit, expected 128*r*(N+p) <= "maxmem"=' + maxmem);
|
|
23498
|
+
const B = pbkdf2(sha256, password, salt, { c: 1, dkLen: blockSize * p });
|
|
23499
|
+
const B32 = u32(B);
|
|
23500
|
+
const V = u32(new Uint8Array(blockSize * N));
|
|
23501
|
+
const tmp = u32(new Uint8Array(blockSize));
|
|
23502
|
+
let blockMixCb = () => {
|
|
23503
|
+
};
|
|
23504
|
+
if (onProgress) {
|
|
23505
|
+
const totalBlockMix = 2 * N * p;
|
|
23506
|
+
const callbackPer = Math.max(Math.floor(totalBlockMix / 1e4), 1);
|
|
23507
|
+
let blockMixCnt = 0;
|
|
23508
|
+
blockMixCb = () => {
|
|
23509
|
+
blockMixCnt++;
|
|
23510
|
+
if (onProgress && (!(blockMixCnt % callbackPer) || blockMixCnt === totalBlockMix))
|
|
23511
|
+
onProgress(blockMixCnt / totalBlockMix);
|
|
23512
|
+
};
|
|
23513
|
+
}
|
|
23514
|
+
return { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb, asyncTick };
|
|
23515
|
+
}
|
|
23516
|
+
function scryptOutput(password, dkLen, B, V, tmp) {
|
|
23517
|
+
const res = pbkdf2(sha256, password, B, { c: 1, dkLen });
|
|
23518
|
+
clean(B, V, tmp);
|
|
23519
|
+
return res;
|
|
23520
|
+
}
|
|
23521
|
+
function scrypt(password, salt, opts) {
|
|
23522
|
+
const { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb } = scryptInit(password, salt, opts);
|
|
23523
|
+
swap32IfBE(B32);
|
|
23524
|
+
for (let pi = 0; pi < p; pi++) {
|
|
23525
|
+
const Pi = blockSize32 * pi;
|
|
23526
|
+
for (let i4 = 0; i4 < blockSize32; i4++)
|
|
23527
|
+
V[i4] = B32[Pi + i4];
|
|
23528
|
+
for (let i4 = 0, pos = 0; i4 < N - 1; i4++) {
|
|
23529
|
+
BlockMix(V, pos, V, pos += blockSize32, r);
|
|
23530
|
+
blockMixCb();
|
|
23531
|
+
}
|
|
23532
|
+
BlockMix(V, (N - 1) * blockSize32, B32, Pi, r);
|
|
23533
|
+
blockMixCb();
|
|
23534
|
+
for (let i4 = 0; i4 < N; i4++) {
|
|
23535
|
+
const j = (B32[Pi + blockSize32 - 16] & N - 1) >>> 0;
|
|
23536
|
+
for (let k = 0; k < blockSize32; k++)
|
|
23537
|
+
tmp[k] = B32[Pi + k] ^ V[j * blockSize32 + k];
|
|
23538
|
+
BlockMix(tmp, 0, B32, Pi, r);
|
|
23539
|
+
blockMixCb();
|
|
23540
|
+
}
|
|
23541
|
+
}
|
|
23542
|
+
swap32IfBE(B32);
|
|
23543
|
+
return scryptOutput(password, dkLen, B, V, tmp);
|
|
23544
|
+
}
|
|
23545
|
+
|
|
23546
|
+
// ../../node_modules/.pnpm/nostr-tools@2.23.3_typescript@5.9.3/node_modules/nostr-tools/lib/esm/nip49.js
|
|
23547
|
+
var Bech32MaxSize2 = 5e3;
|
|
23548
|
+
function encodeBech322(prefix, data) {
|
|
23549
|
+
let words = bech32.toWords(data);
|
|
23550
|
+
return bech32.encode(prefix, words, Bech32MaxSize2);
|
|
23551
|
+
}
|
|
23552
|
+
function encodeBytes2(prefix, bytes) {
|
|
23553
|
+
return encodeBech322(prefix, bytes);
|
|
23554
|
+
}
|
|
23555
|
+
function encrypt4(sec, password, logn = 16, ksb = 2) {
|
|
23556
|
+
let salt = randomBytes(16);
|
|
23557
|
+
let n = 2 ** logn;
|
|
23558
|
+
let key = scrypt(password.normalize("NFKC"), salt, { N: n, r: 8, p: 1, dkLen: 32 });
|
|
23559
|
+
let nonce = randomBytes(24);
|
|
23560
|
+
let aad = Uint8Array.from([ksb]);
|
|
23561
|
+
let xc2p1 = xchacha20poly1305(key, nonce, aad);
|
|
23562
|
+
let ciphertext = xc2p1.encrypt(sec);
|
|
23563
|
+
let b = concatBytes(Uint8Array.from([2]), Uint8Array.from([logn]), salt, nonce, aad, ciphertext);
|
|
23564
|
+
return encodeBytes2("ncryptsec", b);
|
|
23565
|
+
}
|
|
23566
|
+
function decrypt4(ncryptsec, password) {
|
|
23567
|
+
let { prefix, words } = bech32.decode(ncryptsec, Bech32MaxSize2);
|
|
23568
|
+
if (prefix !== "ncryptsec") {
|
|
23569
|
+
throw new Error(`invalid prefix ${prefix}, expected 'ncryptsec'`);
|
|
23570
|
+
}
|
|
23571
|
+
let b = new Uint8Array(bech32.fromWords(words));
|
|
23572
|
+
let version2 = b[0];
|
|
23573
|
+
if (version2 !== 2) {
|
|
23574
|
+
throw new Error(`invalid version ${version2}, expected 0x02`);
|
|
23575
|
+
}
|
|
23576
|
+
let logn = b[1];
|
|
23577
|
+
let n = 2 ** logn;
|
|
23578
|
+
let salt = b.slice(2, 2 + 16);
|
|
23579
|
+
let nonce = b.slice(2 + 16, 2 + 16 + 24);
|
|
23580
|
+
let ksb = b[2 + 16 + 24];
|
|
23581
|
+
let aad = Uint8Array.from([ksb]);
|
|
23582
|
+
let ciphertext = b.slice(2 + 16 + 24 + 1);
|
|
23583
|
+
let key = scrypt(password.normalize("NFKC"), salt, { N: n, r: 8, p: 1, dkLen: 32 });
|
|
23584
|
+
let xc2p1 = xchacha20poly1305(key, nonce, aad);
|
|
23585
|
+
let sec = xc2p1.decrypt(ciphertext);
|
|
23586
|
+
return sec;
|
|
23587
|
+
}
|
|
23588
|
+
|
|
23589
|
+
// ../../node_modules/.pnpm/@formstr+signer@0.2.2_typescript@5.9.3/node_modules/@formstr/signer/dist/index.js
|
|
23590
23590
|
var DEFAULT_PREFIX = "@formstr/signer:";
|
|
23591
23591
|
function localStorageAdapter(prefix = DEFAULT_PREFIX) {
|
|
23592
23592
|
const ls = () => {
|
|
@@ -23645,16 +23645,16 @@ var LocalSigner = class {
|
|
|
23645
23645
|
}
|
|
23646
23646
|
};
|
|
23647
23647
|
function encryptSecretKey(secretKey, passphrase) {
|
|
23648
|
-
return
|
|
23648
|
+
return encrypt4(secretKey, passphrase);
|
|
23649
23649
|
}
|
|
23650
23650
|
function decryptNcryptsec(ncryptsec, passphrase) {
|
|
23651
|
-
return
|
|
23651
|
+
return decrypt4(ncryptsec, passphrase);
|
|
23652
23652
|
}
|
|
23653
23653
|
function generateAccount(passphrase) {
|
|
23654
23654
|
const secretKey = generateSecretKey();
|
|
23655
23655
|
const pubkey = getPublicKey(secretKey);
|
|
23656
23656
|
const npub2 = nip19_exports.npubEncode(pubkey);
|
|
23657
|
-
const ncryptsec =
|
|
23657
|
+
const ncryptsec = encrypt4(secretKey, passphrase);
|
|
23658
23658
|
return { secretKey, pubkey, npub: npub2, ncryptsec };
|
|
23659
23659
|
}
|
|
23660
23660
|
function getWindowNostr() {
|
|
@@ -23696,10 +23696,15 @@ var ExtensionSigner = class {
|
|
|
23696
23696
|
};
|
|
23697
23697
|
var BunkerSigner2 = class {
|
|
23698
23698
|
#delegate;
|
|
23699
|
-
|
|
23699
|
+
#cachedUserPubkey;
|
|
23700
|
+
constructor(delegate, cachedUserPubkey) {
|
|
23700
23701
|
this.#delegate = delegate;
|
|
23702
|
+
this.#cachedUserPubkey = cachedUserPubkey ?? null;
|
|
23701
23703
|
}
|
|
23702
23704
|
getPublicKey() {
|
|
23705
|
+
if (this.#cachedUserPubkey !== null) {
|
|
23706
|
+
return Promise.resolve(this.#cachedUserPubkey);
|
|
23707
|
+
}
|
|
23703
23708
|
return this.#delegate.getPublicKey();
|
|
23704
23709
|
}
|
|
23705
23710
|
signEvent(event) {
|
|
@@ -23775,7 +23780,7 @@ async function connectWithBunkerUri(uri, options = {}) {
|
|
|
23775
23780
|
options.onRelayMismatch
|
|
23776
23781
|
);
|
|
23777
23782
|
return {
|
|
23778
|
-
signer: new BunkerSigner2(tools),
|
|
23783
|
+
signer: new BunkerSigner2(tools, pubkey),
|
|
23779
23784
|
pubkey,
|
|
23780
23785
|
pointer: { ...pointer, relays: resolvedRelays },
|
|
23781
23786
|
clientSecretKey
|
|
@@ -23811,7 +23816,7 @@ function initiateNostrConnect(options) {
|
|
|
23811
23816
|
options.onRelayMismatch
|
|
23812
23817
|
);
|
|
23813
23818
|
return {
|
|
23814
|
-
signer: new BunkerSigner2(tools),
|
|
23819
|
+
signer: new BunkerSigner2(tools, pubkey),
|
|
23815
23820
|
pubkey,
|
|
23816
23821
|
pointer: { ...tools.bp, relays: resolvedRelays },
|
|
23817
23822
|
clientSecretKey
|
|
@@ -23899,28 +23904,57 @@ var AndroidSigner = class {
|
|
|
23899
23904
|
return result;
|
|
23900
23905
|
}
|
|
23901
23906
|
};
|
|
23907
|
+
function describeIdentifier(value) {
|
|
23908
|
+
if (value === null) return "null";
|
|
23909
|
+
if (value === void 0) return "undefined";
|
|
23910
|
+
if (typeof value !== "string") {
|
|
23911
|
+
return `<${typeof value}>`;
|
|
23912
|
+
}
|
|
23913
|
+
if (value.length === 0) return "empty string";
|
|
23914
|
+
const prefix = value.slice(0, 12);
|
|
23915
|
+
const suffix = value.length > 12 ? "\u2026" : "";
|
|
23916
|
+
return `"${prefix}${suffix}" (length=${value.length})`;
|
|
23917
|
+
}
|
|
23918
|
+
var HEX_PUBKEY_RE = /^[0-9a-f]{64}$/i;
|
|
23902
23919
|
async function loginWithAndroidSigner(plugin, packageName) {
|
|
23903
23920
|
if (packageName) {
|
|
23904
23921
|
await plugin.setPackageName(packageName);
|
|
23905
23922
|
}
|
|
23906
|
-
const { npub:
|
|
23923
|
+
const { npub: rawIdentifier, package: pluginPackage } = await plugin.getPublicKey(packageName);
|
|
23907
23924
|
const resolvedPackage = pluginPackage || packageName;
|
|
23908
23925
|
if (!resolvedPackage) {
|
|
23909
23926
|
throw new Error(
|
|
23910
23927
|
"@formstr/signer: android signer did not return a package name and none was supplied"
|
|
23911
23928
|
);
|
|
23912
23929
|
}
|
|
23913
|
-
const
|
|
23914
|
-
if (decoded.type !== "npub") {
|
|
23915
|
-
throw new Error("@formstr/signer: android signer returned a non-npub identifier");
|
|
23916
|
-
}
|
|
23930
|
+
const { pubkey, npub: npub2 } = normalizeAndroidIdentifier(rawIdentifier);
|
|
23917
23931
|
return {
|
|
23918
|
-
signer: new AndroidSigner(plugin, resolvedPackage, npub2,
|
|
23919
|
-
pubkey
|
|
23932
|
+
signer: new AndroidSigner(plugin, resolvedPackage, npub2, pubkey),
|
|
23933
|
+
pubkey,
|
|
23920
23934
|
npub: npub2,
|
|
23921
23935
|
packageName: resolvedPackage
|
|
23922
23936
|
};
|
|
23923
23937
|
}
|
|
23938
|
+
function normalizeAndroidIdentifier(rawIdentifier) {
|
|
23939
|
+
if (typeof rawIdentifier === "string" && HEX_PUBKEY_RE.test(rawIdentifier)) {
|
|
23940
|
+
const pubkey = rawIdentifier.toLowerCase();
|
|
23941
|
+
return { pubkey, npub: nip19_exports.npubEncode(pubkey) };
|
|
23942
|
+
}
|
|
23943
|
+
let decoded;
|
|
23944
|
+
try {
|
|
23945
|
+
decoded = nip19_exports.decode(rawIdentifier);
|
|
23946
|
+
} catch (e) {
|
|
23947
|
+
throw new Error(
|
|
23948
|
+
`@formstr/signer: android signer returned an undecodable identifier (got ${describeIdentifier(rawIdentifier)}): ${e.message}`
|
|
23949
|
+
);
|
|
23950
|
+
}
|
|
23951
|
+
if (decoded.type !== "npub") {
|
|
23952
|
+
throw new Error(
|
|
23953
|
+
`@formstr/signer: android signer returned a non-npub identifier (type=${decoded.type}, got ${describeIdentifier(rawIdentifier)})`
|
|
23954
|
+
);
|
|
23955
|
+
}
|
|
23956
|
+
return { pubkey: decoded.data, npub: rawIdentifier };
|
|
23957
|
+
}
|
|
23924
23958
|
var ACCOUNTS_KEY = "accounts";
|
|
23925
23959
|
var ACTIVE_KEY = "active-pubkey";
|
|
23926
23960
|
var Signer = class {
|
|
@@ -24183,6 +24217,88 @@ var Signer = class {
|
|
|
24183
24217
|
getActiveSigner() {
|
|
24184
24218
|
return this.#activeSigner;
|
|
24185
24219
|
}
|
|
24220
|
+
/**
|
|
24221
|
+
* Silently unlock the active account from persisted state — no user
|
|
24222
|
+
* prompt, no fresh pairing. The package already keeps everything it
|
|
24223
|
+
* needs to reconstruct the runtime signer on disk; this method is the
|
|
24224
|
+
* way to actually use that on cold start instead of re-running each
|
|
24225
|
+
* method's first-time login flow.
|
|
24226
|
+
*
|
|
24227
|
+
* Behavior by method:
|
|
24228
|
+
*
|
|
24229
|
+
* - `extension`: constructs an {@link ExtensionSigner}, which just
|
|
24230
|
+
* proxies to `window.nostr`. No setup roundtrip — individual
|
|
24231
|
+
* operations may still prompt depending on the extension's own
|
|
24232
|
+
* permission state, but unlock itself does not.
|
|
24233
|
+
*
|
|
24234
|
+
* - `nip46`: reuses the stored `clientSecretKey` to construct a
|
|
24235
|
+
* {@link BunkerSigner} against the stored bunker pubkey + relays
|
|
24236
|
+
* via `BunkerSigner.fromBunker`. Deliberately skips the `connect`
|
|
24237
|
+
* request — the remote signer (Amber etc.) approved this client
|
|
24238
|
+
* pubkey on first pairing and re-sending `connect` is what surfaces
|
|
24239
|
+
* a fresh approval prompt every cold start. Requires `options.pool`
|
|
24240
|
+
* so the BunkerSigner has somewhere to listen for responses.
|
|
24241
|
+
* The cached user pubkey is fed into the wrapper so a follow-up
|
|
24242
|
+
* `getPublicKey()` is a memory read, not a relay request.
|
|
24243
|
+
*
|
|
24244
|
+
* - `android`: constructs an {@link AndroidSigner} directly from the
|
|
24245
|
+
* stored `androidPackageName` + `pubkey` + `npub`. Skips the
|
|
24246
|
+
* `getPublicKey` content-provider roundtrip that
|
|
24247
|
+
* {@link loginWithAndroidSigner} performs and that — on Amber —
|
|
24248
|
+
* surfaces as a permission prompt every cold start.
|
|
24249
|
+
*
|
|
24250
|
+
* - `ncryptsec`: returns `null`. There is no silent path — the user's
|
|
24251
|
+
* passphrase isn't (and shouldn't be) persisted. The caller must
|
|
24252
|
+
* drive the passphrase prompt and call {@link loginWithNcryptsec}.
|
|
24253
|
+
*
|
|
24254
|
+
* Returns `null` (without emitting any event or mutating state) when
|
|
24255
|
+
* there is no active account, when the account is missing fields
|
|
24256
|
+
* required to unlock, when `nip46` is the method but no `pool` was
|
|
24257
|
+
* supplied, or when `android` is the method but no plugin is
|
|
24258
|
+
* configured. On success emits the same `login` / `switch` event the
|
|
24259
|
+
* corresponding `loginWith*` would.
|
|
24260
|
+
*/
|
|
24261
|
+
async unlock(options = {}) {
|
|
24262
|
+
const account = this.getActiveAccount();
|
|
24263
|
+
if (!account) return null;
|
|
24264
|
+
switch (account.method) {
|
|
24265
|
+
case "extension": {
|
|
24266
|
+
const signer = new ExtensionSigner();
|
|
24267
|
+
this.#setActive(account, signer);
|
|
24268
|
+
return signer;
|
|
24269
|
+
}
|
|
24270
|
+
case "nip46": {
|
|
24271
|
+
if (!account.nip46) return null;
|
|
24272
|
+
if (!options.pool) return null;
|
|
24273
|
+
const { remoteSignerPubkey, relays, clientSecretKey } = account.nip46;
|
|
24274
|
+
if (!remoteSignerPubkey || !relays.length || !clientSecretKey) {
|
|
24275
|
+
return null;
|
|
24276
|
+
}
|
|
24277
|
+
const tools = BunkerSigner.fromBunker(
|
|
24278
|
+
hexToBytes2(clientSecretKey),
|
|
24279
|
+
{ pubkey: remoteSignerPubkey, relays, secret: null },
|
|
24280
|
+
{ pool: options.pool }
|
|
24281
|
+
);
|
|
24282
|
+
const signer = new BunkerSigner2(tools, account.pubkey);
|
|
24283
|
+
this.#setActive(account, signer);
|
|
24284
|
+
return signer;
|
|
24285
|
+
}
|
|
24286
|
+
case "android": {
|
|
24287
|
+
if (!account.androidPackageName) return null;
|
|
24288
|
+
if (!this.#defaultAndroidPlugin) return null;
|
|
24289
|
+
const signer = new AndroidSigner(
|
|
24290
|
+
this.#defaultAndroidPlugin,
|
|
24291
|
+
account.androidPackageName,
|
|
24292
|
+
account.npub,
|
|
24293
|
+
account.pubkey
|
|
24294
|
+
);
|
|
24295
|
+
this.#setActive(account, signer);
|
|
24296
|
+
return signer;
|
|
24297
|
+
}
|
|
24298
|
+
case "ncryptsec":
|
|
24299
|
+
return null;
|
|
24300
|
+
}
|
|
24301
|
+
}
|
|
24186
24302
|
/**
|
|
24187
24303
|
* Make `pubkey` the active account. Clears the in-memory signer —
|
|
24188
24304
|
* the new account starts **locked** even if it was previously
|
|
@@ -24289,7 +24405,26 @@ async function loginImport(deps) {
|
|
|
24289
24405
|
}
|
|
24290
24406
|
async function loginBunker(deps) {
|
|
24291
24407
|
const uri = (await deps.prompt("Paste the bunker:// URI: ")).trim();
|
|
24292
|
-
|
|
24408
|
+
try {
|
|
24409
|
+
await deps.signer.loginWithBunkerUri(uri, { pool: deps.pool, perms: NIP46_PERMS });
|
|
24410
|
+
} catch (err) {
|
|
24411
|
+
throw new Error(describeBunkerError(uri, err));
|
|
24412
|
+
}
|
|
24413
|
+
}
|
|
24414
|
+
function describeBunkerError(uri, err) {
|
|
24415
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
24416
|
+
const hasSecret = /[?&]secret=/.test(uri);
|
|
24417
|
+
const expected = "`bunker://<pubkey>?relay=wss://\u2026&secret=<token>`";
|
|
24418
|
+
if (/no secret/i.test(raw) || !hasSecret) {
|
|
24419
|
+
return "Could not pair with this bunker URI" + (hasSecret ? "" : " \u2014 it has no `secret=` token") + `, so the remote signer rejected the connection. A pairing URI must look like ${expected}. Copy the FULL connection URI (including \`&secret=\u2026\`) from your signer app (e.g. nsec.app \u2192 Connect app), and make sure nothing was truncated \u2014 the secret is often one-time, so generate a fresh connection if needed. Or use the QR flow: \`formstr-mcp login\` \u2192 \`q\`. (signer reported: ${raw})`;
|
|
24420
|
+
}
|
|
24421
|
+
if (/invalid bunker uri/i.test(raw)) {
|
|
24422
|
+
return `That doesn't look like a valid bunker URI. Expected ${expected}. (parser reported: ${raw})`;
|
|
24423
|
+
}
|
|
24424
|
+
if (/at least one relay/i.test(raw)) {
|
|
24425
|
+
return `This bunker URI has no relays. Expected ${expected}. (reported: ${raw})`;
|
|
24426
|
+
}
|
|
24427
|
+
return `Bunker login failed: ${raw}. Check that the URI includes a \`secret=\` token and at least one \`relay=\`, that the remote signer is online, or try the QR flow (\`formstr-mcp login\` \u2192 \`q\`).`;
|
|
24293
24428
|
}
|
|
24294
24429
|
async function loginNostrConnect(deps, log) {
|
|
24295
24430
|
const relays = deps.relays?.length ? deps.relays : DEFAULT_NOSTRCONNECT_RELAYS;
|
|
@@ -24310,6 +24445,19 @@ function whoami(signer) {
|
|
|
24310
24445
|
function listAccounts(signer) {
|
|
24311
24446
|
return signer.listAccounts();
|
|
24312
24447
|
}
|
|
24448
|
+
function findAccount(accounts, target) {
|
|
24449
|
+
return accounts.find((a) => a.npub === target || a.pubkey === target) ?? null;
|
|
24450
|
+
}
|
|
24451
|
+
async function doSwitch(signer, target) {
|
|
24452
|
+
const match = findAccount(listAccounts(signer), target);
|
|
24453
|
+
if (!match) {
|
|
24454
|
+
throw new Error(
|
|
24455
|
+
`No stored account matching "${target}". Run \`formstr-mcp accounts\` to list them.`
|
|
24456
|
+
);
|
|
24457
|
+
}
|
|
24458
|
+
await signer.switchAccount(match.pubkey);
|
|
24459
|
+
return match;
|
|
24460
|
+
}
|
|
24313
24461
|
|
|
24314
24462
|
// src/auth/kvStore.ts
|
|
24315
24463
|
var import_node_crypto = require("crypto");
|
|
@@ -25400,25 +25548,39 @@ function createPatchedPool() {
|
|
|
25400
25548
|
|
|
25401
25549
|
// src/auth/terminal.ts
|
|
25402
25550
|
var readline = __toESM(require("readline"));
|
|
25551
|
+
var import_node_stream = require("stream");
|
|
25403
25552
|
var import_qrcode = __toESM(require_lib());
|
|
25404
25553
|
function createTerminalIo() {
|
|
25405
|
-
const
|
|
25554
|
+
const stderr = process.stderr;
|
|
25555
|
+
let muted = false;
|
|
25556
|
+
const output = new import_node_stream.Writable({
|
|
25557
|
+
write(chunk, _enc, cb) {
|
|
25558
|
+
if (!muted) stderr.write(chunk);
|
|
25559
|
+
cb();
|
|
25560
|
+
}
|
|
25561
|
+
});
|
|
25562
|
+
Object.defineProperties(output, {
|
|
25563
|
+
isTTY: { get: () => stderr.isTTY },
|
|
25564
|
+
columns: { get: () => stderr.columns },
|
|
25565
|
+
rows: { get: () => stderr.rows }
|
|
25566
|
+
});
|
|
25567
|
+
const rl = readline.createInterface({
|
|
25568
|
+
input: process.stdin,
|
|
25569
|
+
output,
|
|
25570
|
+
terminal: Boolean(process.stdin.isTTY)
|
|
25571
|
+
});
|
|
25406
25572
|
return {
|
|
25407
25573
|
prompt(question) {
|
|
25408
25574
|
return new Promise((resolve) => rl.question(question, resolve));
|
|
25409
25575
|
},
|
|
25410
25576
|
promptPassphrase(question) {
|
|
25411
25577
|
return new Promise((resolve) => {
|
|
25412
|
-
|
|
25413
|
-
|
|
25414
|
-
|
|
25415
|
-
muted._writeToOutput = () => {
|
|
25416
|
-
};
|
|
25417
|
-
rl.question("", (answer) => {
|
|
25418
|
-
muted._writeToOutput = original;
|
|
25419
|
-
process.stderr.write("\n");
|
|
25578
|
+
rl.question(question, (answer) => {
|
|
25579
|
+
muted = false;
|
|
25580
|
+
stderr.write("\n");
|
|
25420
25581
|
resolve(answer);
|
|
25421
25582
|
});
|
|
25583
|
+
muted = true;
|
|
25422
25584
|
});
|
|
25423
25585
|
},
|
|
25424
25586
|
close() {
|
|
@@ -26027,15 +26189,29 @@ var EventStore = class {
|
|
|
26027
26189
|
eventsByDTag = /* @__PURE__ */ new Map();
|
|
26028
26190
|
// "kind:pubkey:dtag" → event
|
|
26029
26191
|
deletedIds = /* @__PURE__ */ new Set();
|
|
26192
|
+
// NIP-09 coordinate tombstones: "kind:pubkey:dtag" → newest deletion created_at.
|
|
26193
|
+
// An addressable event is only rejected when its created_at ≤ that time, so a
|
|
26194
|
+
// legitimate re-publish after a delete survives.
|
|
26195
|
+
deletedCoordinates = /* @__PURE__ */ new Map();
|
|
26196
|
+
// Kind-84 participant-removal tombstones.
|
|
26197
|
+
ignoredEventIds = /* @__PURE__ */ new Set();
|
|
26198
|
+
ignoredCoordinates = /* @__PURE__ */ new Set();
|
|
26030
26199
|
subscriptions = [];
|
|
26031
26200
|
/** Store event. Returns false if duplicate or older replaceable. */
|
|
26032
26201
|
store(event) {
|
|
26033
26202
|
if (this.deletedIds.has(event.id))
|
|
26034
26203
|
return false;
|
|
26204
|
+
if (this.ignoredEventIds.has(event.id))
|
|
26205
|
+
return false;
|
|
26035
26206
|
if (this.eventsById.has(event.id))
|
|
26036
26207
|
return false;
|
|
26037
26208
|
if (this.isReplaceable(event.kind) || this.isParameterizedReplaceable(event.kind)) {
|
|
26038
26209
|
const addr = this.getAddress(event);
|
|
26210
|
+
const deletedAt = this.deletedCoordinates.get(addr);
|
|
26211
|
+
if (deletedAt !== void 0 && event.created_at <= deletedAt)
|
|
26212
|
+
return false;
|
|
26213
|
+
if (this.ignoredCoordinates.has(addr))
|
|
26214
|
+
return false;
|
|
26039
26215
|
const existing = this.eventsByDTag.get(addr);
|
|
26040
26216
|
if (existing && existing.created_at >= event.created_at) {
|
|
26041
26217
|
return false;
|
|
@@ -26050,6 +26226,9 @@ var EventStore = class {
|
|
|
26050
26226
|
if (event.kind === 5) {
|
|
26051
26227
|
this.handleDeletion(event);
|
|
26052
26228
|
}
|
|
26229
|
+
if (event.kind === 84) {
|
|
26230
|
+
this.handleParticipantRemoval(event);
|
|
26231
|
+
}
|
|
26053
26232
|
this.eventsById.set(event.id, event);
|
|
26054
26233
|
if (!this.eventsByKind.has(event.kind)) {
|
|
26055
26234
|
this.eventsByKind.set(event.kind, /* @__PURE__ */ new Map());
|
|
@@ -26118,14 +26297,50 @@ var EventStore = class {
|
|
|
26118
26297
|
if (tag[0] === "e" && tag[1]) {
|
|
26119
26298
|
const targetId = tag[1];
|
|
26120
26299
|
const target = this.eventsById.get(targetId);
|
|
26121
|
-
if (target && target.pubkey
|
|
26122
|
-
|
|
26123
|
-
}
|
|
26300
|
+
if (target && target.pubkey !== deletionEvent.pubkey)
|
|
26301
|
+
continue;
|
|
26124
26302
|
this.deletedIds.add(targetId);
|
|
26303
|
+
if (target)
|
|
26304
|
+
this.remove(targetId);
|
|
26125
26305
|
}
|
|
26126
26306
|
if (tag[0] === "a" && tag[1]) {
|
|
26307
|
+
const coordAuthor = tag[1].split(":")[1];
|
|
26308
|
+
if (coordAuthor && coordAuthor !== deletionEvent.pubkey)
|
|
26309
|
+
continue;
|
|
26310
|
+
const prev = this.deletedCoordinates.get(tag[1]) ?? 0;
|
|
26311
|
+
if (deletionEvent.created_at > prev) {
|
|
26312
|
+
this.deletedCoordinates.set(tag[1], deletionEvent.created_at);
|
|
26313
|
+
}
|
|
26127
26314
|
const existing = this.eventsByDTag.get(tag[1]);
|
|
26128
|
-
if (existing && existing.
|
|
26315
|
+
if (existing && existing.created_at <= deletionEvent.created_at) {
|
|
26316
|
+
this.remove(existing.id);
|
|
26317
|
+
this.eventsByDTag.delete(tag[1]);
|
|
26318
|
+
}
|
|
26319
|
+
}
|
|
26320
|
+
}
|
|
26321
|
+
}
|
|
26322
|
+
/**
|
|
26323
|
+
* Process a kind-84 participant removal (a participant opting out of an
|
|
26324
|
+
* event). Unlike NIP-09 there is no same-author rule — any participant
|
|
26325
|
+
* (`p` tag on the target) may remove it for themselves; targets that aren't
|
|
26326
|
+
* cached yet are tombstoned unconditionally, matching the standalone
|
|
26327
|
+
* calendar's EventStore.
|
|
26328
|
+
*/
|
|
26329
|
+
handleParticipantRemoval(removalEvent) {
|
|
26330
|
+
const isParticipant = (target) => target.tags.some((t) => t[0] === "p" && t[1] === removalEvent.pubkey);
|
|
26331
|
+
for (const tag of removalEvent.tags) {
|
|
26332
|
+
if (tag[0] === "e" && tag[1]) {
|
|
26333
|
+
const existing = this.eventsById.get(tag[1]);
|
|
26334
|
+
if (existing) {
|
|
26335
|
+
if (!isParticipant(existing))
|
|
26336
|
+
continue;
|
|
26337
|
+
this.remove(tag[1]);
|
|
26338
|
+
}
|
|
26339
|
+
this.ignoredEventIds.add(tag[1]);
|
|
26340
|
+
} else if (tag[0] === "a" && tag[1]) {
|
|
26341
|
+
this.ignoredCoordinates.add(tag[1]);
|
|
26342
|
+
const existing = this.eventsByDTag.get(tag[1]);
|
|
26343
|
+
if (existing && isParticipant(existing)) {
|
|
26129
26344
|
this.remove(existing.id);
|
|
26130
26345
|
this.eventsByDTag.delete(tag[1]);
|
|
26131
26346
|
}
|
|
@@ -26149,6 +26364,9 @@ var EventStore = class {
|
|
|
26149
26364
|
this.eventsByAuthor.clear();
|
|
26150
26365
|
this.eventsByDTag.clear();
|
|
26151
26366
|
this.deletedIds.clear();
|
|
26367
|
+
this.deletedCoordinates.clear();
|
|
26368
|
+
this.ignoredEventIds.clear();
|
|
26369
|
+
this.ignoredCoordinates.clear();
|
|
26152
26370
|
this.subscriptions = [];
|
|
26153
26371
|
}
|
|
26154
26372
|
get size() {
|
|
@@ -26385,9 +26603,20 @@ var NostrRuntime = class {
|
|
|
26385
26603
|
}
|
|
26386
26604
|
});
|
|
26387
26605
|
}
|
|
26388
|
-
/**
|
|
26389
|
-
|
|
26390
|
-
|
|
26606
|
+
/**
|
|
26607
|
+
* Publish event to relays. Bounded by `timeoutMs`: a relay that never acks must
|
|
26608
|
+
* not hang the caller (e.g. a form submit) forever — best-effort fan-out.
|
|
26609
|
+
*/
|
|
26610
|
+
async publish(relays, event, timeoutMs = 1e4) {
|
|
26611
|
+
let timer;
|
|
26612
|
+
const timeout = new Promise((resolve) => {
|
|
26613
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
26614
|
+
});
|
|
26615
|
+
try {
|
|
26616
|
+
await Promise.race([Promise.allSettled(this.pool.publish(relays, event)), timeout]);
|
|
26617
|
+
} finally {
|
|
26618
|
+
clearTimeout(timer);
|
|
26619
|
+
}
|
|
26391
26620
|
this.eventStore.store(event);
|
|
26392
26621
|
}
|
|
26393
26622
|
/** Close all subscriptions + clear caches. Use in tests; rarely in production. */
|
|
@@ -26451,7 +26680,7 @@ var MODULE_DEFAULT_RELAYS = {
|
|
|
26451
26680
|
"wss://relay.primal.net",
|
|
26452
26681
|
"wss://nos.lol",
|
|
26453
26682
|
"wss://relay.nostr.wirednet.jp",
|
|
26454
|
-
"wss://
|
|
26683
|
+
"wss://nostr-01.yakihonne.com",
|
|
26455
26684
|
"wss://relay.snort.social",
|
|
26456
26685
|
"wss://relay.nostr.band",
|
|
26457
26686
|
"wss://nostr21.com"
|
|
@@ -26683,7 +26912,7 @@ var BlossomClient = class {
|
|
|
26683
26912
|
*/
|
|
26684
26913
|
async upload(data, authEvent, contentType) {
|
|
26685
26914
|
const authHeader = `Nostr ${btoa(JSON.stringify(authEvent))}`;
|
|
26686
|
-
const
|
|
26915
|
+
const sha2564 = await sha256Hex(data);
|
|
26687
26916
|
const response = await fetch(`${this.serverUrl}/upload`, {
|
|
26688
26917
|
method: "PUT",
|
|
26689
26918
|
headers: {
|
|
@@ -26691,7 +26920,7 @@ var BlossomClient = class {
|
|
|
26691
26920
|
"Content-Type": contentType ?? "application/octet-stream",
|
|
26692
26921
|
// BUD-02: lets servers verify the blob and is required by some (e.g.
|
|
26693
26922
|
// the standalone formstr-drive's default servers).
|
|
26694
|
-
"X-SHA-256":
|
|
26923
|
+
"X-SHA-256": sha2564
|
|
26695
26924
|
},
|
|
26696
26925
|
body: data
|
|
26697
26926
|
});
|
|
@@ -26701,26 +26930,26 @@ var BlossomClient = class {
|
|
|
26701
26930
|
const text = await response.text();
|
|
26702
26931
|
try {
|
|
26703
26932
|
const json = JSON.parse(text);
|
|
26704
|
-
const hash = json.sha256 ?? json.x ??
|
|
26933
|
+
const hash = json.sha256 ?? json.x ?? sha2564;
|
|
26705
26934
|
return {
|
|
26706
26935
|
sha256: hash,
|
|
26707
26936
|
url: json.url ?? `${this.serverUrl}/${hash}`,
|
|
26708
26937
|
size: json.size ?? data.byteLength
|
|
26709
26938
|
};
|
|
26710
26939
|
} catch {
|
|
26711
|
-
const hash = text.trim() ||
|
|
26940
|
+
const hash = text.trim() || sha2564;
|
|
26712
26941
|
return { sha256: hash, url: `${this.serverUrl}/${hash}`, size: data.byteLength };
|
|
26713
26942
|
}
|
|
26714
26943
|
}
|
|
26715
26944
|
/**
|
|
26716
26945
|
* BUD-03: Download file blob by SHA-256 hash.
|
|
26717
26946
|
*/
|
|
26718
|
-
async download(
|
|
26947
|
+
async download(sha2564, authEvent) {
|
|
26719
26948
|
const headers = {};
|
|
26720
26949
|
if (authEvent) {
|
|
26721
26950
|
headers["Authorization"] = `Nostr ${btoa(JSON.stringify(authEvent))}`;
|
|
26722
26951
|
}
|
|
26723
|
-
const response = await fetch(`${this.serverUrl}/${
|
|
26952
|
+
const response = await fetch(`${this.serverUrl}/${sha2564}`, { headers });
|
|
26724
26953
|
if (!response.ok) {
|
|
26725
26954
|
throw new Error(`Blossom download failed: ${response.status} ${response.statusText}`);
|
|
26726
26955
|
}
|
|
@@ -26730,9 +26959,9 @@ var BlossomClient = class {
|
|
|
26730
26959
|
/**
|
|
26731
26960
|
* BUD-04: Delete file blob by SHA-256 hash.
|
|
26732
26961
|
*/
|
|
26733
|
-
async delete(
|
|
26962
|
+
async delete(sha2564, authEvent) {
|
|
26734
26963
|
const authHeader = `Nostr ${btoa(JSON.stringify(authEvent))}`;
|
|
26735
|
-
const response = await fetch(`${this.serverUrl}/${
|
|
26964
|
+
const response = await fetch(`${this.serverUrl}/${sha2564}`, {
|
|
26736
26965
|
method: "DELETE",
|
|
26737
26966
|
headers: { Authorization: authHeader }
|
|
26738
26967
|
});
|
|
@@ -26751,18 +26980,18 @@ async function sha256Hex(data) {
|
|
|
26751
26980
|
}
|
|
26752
26981
|
|
|
26753
26982
|
// ../core/dist/blossom/auth.js
|
|
26754
|
-
async function createBlossomAuthEvent(operation,
|
|
26983
|
+
async function createBlossomAuthEvent(operation, sha2564, signer) {
|
|
26755
26984
|
const now2 = Math.floor(Date.now() / 1e3);
|
|
26756
26985
|
const event = {
|
|
26757
26986
|
kind: 24242,
|
|
26758
26987
|
created_at: now2,
|
|
26759
26988
|
tags: [
|
|
26760
26989
|
["t", operation],
|
|
26761
|
-
["x",
|
|
26990
|
+
["x", sha2564],
|
|
26762
26991
|
["expiration", String(now2 + 300)]
|
|
26763
26992
|
// 5 min validity
|
|
26764
26993
|
],
|
|
26765
|
-
content: `Authorize ${operation} for ${
|
|
26994
|
+
content: `Authorize ${operation} for ${sha2564}`
|
|
26766
26995
|
};
|
|
26767
26996
|
return signer.signEvent(event);
|
|
26768
26997
|
}
|
|
@@ -26837,17 +27066,6 @@ async function wrapEvent3(event, signer, recipientPubkey, wrapKind = 1059) {
|
|
|
26837
27066
|
const seal = await createSeal2(rumor, signer, recipientPubkey);
|
|
26838
27067
|
return createWrap2(seal, recipientPubkey, wrapKind);
|
|
26839
27068
|
}
|
|
26840
|
-
async function wrapManyEvents3(event, signer, recipientPubkeys, wrapKind = 1059) {
|
|
26841
|
-
const rumor = createRumor2(event);
|
|
26842
|
-
rumor.pubkey = await signer.getPublicKey();
|
|
26843
|
-
const wraps = [];
|
|
26844
|
-
for (const pubkey of recipientPubkeys) {
|
|
26845
|
-
const seal = await createSeal2(rumor, signer, pubkey);
|
|
26846
|
-
const wrap = await createWrap2(seal, pubkey, wrapKind);
|
|
26847
|
-
wraps.push(wrap);
|
|
26848
|
-
}
|
|
26849
|
-
return wraps;
|
|
26850
|
-
}
|
|
26851
27069
|
async function unwrapEvent3(wrappedEvent, signer) {
|
|
26852
27070
|
if (!signer.nip44Decrypt) {
|
|
26853
27071
|
throw new Error("Signer does not support NIP-44 for unwrap");
|
|
@@ -26865,21 +27083,34 @@ function randomizeTimestamp(timestamp) {
|
|
|
26865
27083
|
|
|
26866
27084
|
// ../core/dist/crypto/nkeys.js
|
|
26867
27085
|
var PREFIX = "nkeys";
|
|
27086
|
+
var BECH32_LIMIT = 2048;
|
|
26868
27087
|
function encodeNKeys(keys) {
|
|
26869
|
-
const
|
|
27088
|
+
const encoder = new TextEncoder();
|
|
27089
|
+
const names = [];
|
|
27090
|
+
const values = [];
|
|
26870
27091
|
for (const [name, value] of Object.entries(keys)) {
|
|
26871
|
-
const nameBytes =
|
|
26872
|
-
|
|
26873
|
-
|
|
26874
|
-
|
|
26875
|
-
|
|
26876
|
-
|
|
26877
|
-
|
|
26878
|
-
|
|
26879
|
-
}
|
|
26880
|
-
const
|
|
26881
|
-
const
|
|
26882
|
-
|
|
27092
|
+
const nameBytes = encoder.encode(name);
|
|
27093
|
+
const valueBytes = encoder.encode(value);
|
|
27094
|
+
if (nameBytes.length > 255)
|
|
27095
|
+
throw new Error(`nkeys: key name too long (${name.length})`);
|
|
27096
|
+
if (valueBytes.length > 255)
|
|
27097
|
+
throw new Error(`nkeys: value too long for key "${name}"`);
|
|
27098
|
+
names.push(nameBytes);
|
|
27099
|
+
values.push(valueBytes);
|
|
27100
|
+
}
|
|
27101
|
+
const tlvBytes = [];
|
|
27102
|
+
for (const nameBytes of names) {
|
|
27103
|
+
tlvBytes.push(0, nameBytes.length, ...nameBytes);
|
|
27104
|
+
}
|
|
27105
|
+
for (const valueBytes of values) {
|
|
27106
|
+
tlvBytes.push(1, valueBytes.length, ...valueBytes);
|
|
27107
|
+
}
|
|
27108
|
+
const words = bech32ToWords(new Uint8Array(tlvBytes));
|
|
27109
|
+
const encoded = bech32Encode(PREFIX, words);
|
|
27110
|
+
if (encoded.length > BECH32_LIMIT) {
|
|
27111
|
+
throw new Error(`nkeys: encoded length ${encoded.length} exceeds ${BECH32_LIMIT}`);
|
|
27112
|
+
}
|
|
27113
|
+
return encoded;
|
|
26883
27114
|
}
|
|
26884
27115
|
var CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
|
26885
27116
|
function bech32Encode(prefix, words) {
|
|
@@ -26991,11 +27222,16 @@ async function aesGcmDecrypt(ciphertext, conversationKey) {
|
|
|
26991
27222
|
}
|
|
26992
27223
|
async function encryptFileWithKey(fileBytes) {
|
|
26993
27224
|
const secretKey = generateSecretKey();
|
|
27225
|
+
const privateKeyHex = bytesToHex(secretKey);
|
|
27226
|
+
const ciphertext = await encryptFileWithExistingKey(fileBytes, privateKeyHex);
|
|
27227
|
+
return { ciphertext, privateKeyHex };
|
|
27228
|
+
}
|
|
27229
|
+
async function encryptFileWithExistingKey(fileBytes, privateKeyHex) {
|
|
27230
|
+
const secretKey = hexToBytes(privateKeyHex);
|
|
26994
27231
|
const pubkey = getPublicKey(secretKey);
|
|
26995
27232
|
const conversationKey = nip44_exports.v2.utils.getConversationKey(secretKey, pubkey);
|
|
26996
27233
|
const plaintextBase64 = uint8ArrayToBase64(fileBytes);
|
|
26997
|
-
|
|
26998
|
-
return { ciphertext, privateKeyHex: bytesToHex(secretKey) };
|
|
27234
|
+
return aesGcmEncrypt(plaintextBase64, conversationKey);
|
|
26999
27235
|
}
|
|
27000
27236
|
async function decryptFileWithKey(ciphertext, privateKeyHex) {
|
|
27001
27237
|
const secretKey = hexToBytes(privateKeyHex);
|
|
@@ -27021,23 +27257,23 @@ var KIND_MODULE_MAP = {
|
|
|
27021
27257
|
30168: "forms",
|
|
27022
27258
|
// Form template
|
|
27023
27259
|
// Calendar
|
|
27024
|
-
31922: "calendar",
|
|
27025
|
-
// Date-based calendar event
|
|
27026
27260
|
31923: "calendar",
|
|
27027
|
-
//
|
|
27028
|
-
|
|
27261
|
+
// Public time-based event (NIP-52)
|
|
27262
|
+
32678: "calendar",
|
|
27263
|
+
// Private event
|
|
27264
|
+
32679: "calendar",
|
|
27265
|
+
// Private event (legacy recurring variant, read-only)
|
|
27266
|
+
32123: "calendar",
|
|
27029
27267
|
// Calendar list
|
|
27030
27268
|
// Pages
|
|
27031
|
-
|
|
27032
|
-
//
|
|
27033
|
-
30024: "pages",
|
|
27034
|
-
// Draft article
|
|
27269
|
+
33457: "pages",
|
|
27270
|
+
// Encrypted markdown doc (nostr-docs)
|
|
27035
27271
|
// Drive
|
|
27036
|
-
|
|
27037
|
-
//
|
|
27272
|
+
34578: "drive",
|
|
27273
|
+
// File metadata (formstr-drive)
|
|
27038
27274
|
// Polls
|
|
27039
27275
|
1068: "polls"
|
|
27040
|
-
// Poll event
|
|
27276
|
+
// Poll event (NIP-88)
|
|
27041
27277
|
};
|
|
27042
27278
|
function createRef(_module, kind, pubkey, identifier, relays = []) {
|
|
27043
27279
|
return nip19_exports.naddrEncode({ kind, pubkey, identifier, relays });
|
|
@@ -27162,7 +27398,7 @@ async function bootstrap(input, deps = {}) {
|
|
|
27162
27398
|
}
|
|
27163
27399
|
async function selectAccount(signer, requested) {
|
|
27164
27400
|
if (requested) {
|
|
27165
|
-
const match = signer.listAccounts()
|
|
27401
|
+
const match = findAccount(signer.listAccounts(), requested);
|
|
27166
27402
|
if (!match) {
|
|
27167
27403
|
throw new Error(
|
|
27168
27404
|
`No stored account for --account ${requested}. Run \`formstr-mcp accounts\` to list them.`
|
|
@@ -27179,18 +27415,7 @@ async function selectAccount(signer, requested) {
|
|
|
27179
27415
|
}
|
|
27180
27416
|
async function unlock(signer, account, deps) {
|
|
27181
27417
|
if (account.method === "ncryptsec") {
|
|
27182
|
-
|
|
27183
|
-
throw new Error(
|
|
27184
|
-
"Stored ncryptsec account is missing its encrypted key. Run `formstr-mcp login`."
|
|
27185
|
-
);
|
|
27186
|
-
}
|
|
27187
|
-
const passphrase = deps.passphrase ?? process.env.FORMSTR_MCP_NCRYPTSEC_PASSPHRASE;
|
|
27188
|
-
if (!passphrase) {
|
|
27189
|
-
throw new Error(
|
|
27190
|
-
"Set FORMSTR_MCP_NCRYPTSEC_PASSPHRASE to unlock the ncryptsec account at boot."
|
|
27191
|
-
);
|
|
27192
|
-
}
|
|
27193
|
-
await signer.loginWithNcryptsec(account.ncryptsec, passphrase);
|
|
27418
|
+
await unlockNcryptsec(signer, account, deps);
|
|
27194
27419
|
return;
|
|
27195
27420
|
}
|
|
27196
27421
|
if (account.method === "nip46") {
|
|
@@ -27198,41 +27423,120 @@ async function unlock(signer, account, deps) {
|
|
|
27198
27423
|
throw new Error("Stored nip46 account is missing its session. Run `formstr-mcp login`.");
|
|
27199
27424
|
}
|
|
27200
27425
|
const pool = deps.pool ?? createPatchedPool();
|
|
27201
|
-
await signer.
|
|
27202
|
-
|
|
27203
|
-
|
|
27204
|
-
|
|
27426
|
+
const active = await signer.unlock({ pool });
|
|
27427
|
+
if (!active) {
|
|
27428
|
+
throw new Error(
|
|
27429
|
+
"Could not resume the stored nip46 session (its keys are incomplete). Run `formstr-mcp login` to re-pair."
|
|
27430
|
+
);
|
|
27431
|
+
}
|
|
27205
27432
|
return;
|
|
27206
27433
|
}
|
|
27207
27434
|
throw new Error(
|
|
27208
27435
|
`Account method "${account.method}" cannot be unlocked headlessly \u2014 use ncryptsec or nip46.`
|
|
27209
27436
|
);
|
|
27210
27437
|
}
|
|
27438
|
+
var MAX_PASSPHRASE_ATTEMPTS = 3;
|
|
27439
|
+
async function unlockNcryptsec(signer, account, deps) {
|
|
27440
|
+
if (!account.ncryptsec) {
|
|
27441
|
+
throw new Error(
|
|
27442
|
+
`Account ${account.npub} is stored as an ncryptsec but its encrypted key is missing. Re-add it with \`formstr-mcp login\`.`
|
|
27443
|
+
);
|
|
27444
|
+
}
|
|
27445
|
+
const ncryptsec = account.ncryptsec;
|
|
27446
|
+
const tryUnlock = async (passphrase) => {
|
|
27447
|
+
try {
|
|
27448
|
+
await signer.loginWithNcryptsec(ncryptsec, passphrase);
|
|
27449
|
+
return true;
|
|
27450
|
+
} catch {
|
|
27451
|
+
return false;
|
|
27452
|
+
}
|
|
27453
|
+
};
|
|
27454
|
+
const configured = deps.passphrase ?? process.env.FORMSTR_MCP_NCRYPTSEC_PASSPHRASE;
|
|
27455
|
+
if (configured) {
|
|
27456
|
+
if (await tryUnlock(configured)) return;
|
|
27457
|
+
if (!deps.promptPassphrase) {
|
|
27458
|
+
throw new Error(
|
|
27459
|
+
`The configured passphrase did not unlock account ${account.npub} \u2014 it is most likely the wrong passphrase for this account (each account has its own). Set FORMSTR_MCP_NCRYPTSEC_PASSPHRASE to the passphrase that matches this account, or switch to another with \`formstr-mcp switch <npub>\` (a nip46/bunker account needs no passphrase).`
|
|
27460
|
+
);
|
|
27461
|
+
}
|
|
27462
|
+
console.error(
|
|
27463
|
+
`formstr-mcp: the configured passphrase didn't unlock ${account.npub} \u2014 enter it manually.`
|
|
27464
|
+
);
|
|
27465
|
+
} else if (!deps.promptPassphrase) {
|
|
27466
|
+
throw new Error(
|
|
27467
|
+
`Cannot unlock account ${account.npub} (ncryptsec): no passphrase is available. FORMSTR_MCP_NCRYPTSEC_PASSPHRASE is not set and this process has no interactive terminal to prompt on \u2014 an MCP host runs the server with stdin wired to the JSON-RPC channel. Fix: add FORMSTR_MCP_NCRYPTSEC_PASSPHRASE to your MCP server config's \`env\`, or switch to a nip46/bunker account with \`formstr-mcp switch <npub>\` (resumes with no passphrase).`
|
|
27468
|
+
);
|
|
27469
|
+
}
|
|
27470
|
+
const promptPassphrase = deps.promptPassphrase;
|
|
27471
|
+
for (let attempt = 1; attempt <= MAX_PASSPHRASE_ATTEMPTS; attempt++) {
|
|
27472
|
+
const passphrase = await promptPassphrase(`Passphrase to unlock ${account.npub}: `);
|
|
27473
|
+
if (await tryUnlock(passphrase)) return;
|
|
27474
|
+
const left = MAX_PASSPHRASE_ATTEMPTS - attempt;
|
|
27475
|
+
if (left > 0) console.error(`formstr-mcp: incorrect passphrase, ${left} attempt(s) left.`);
|
|
27476
|
+
}
|
|
27477
|
+
throw new Error(
|
|
27478
|
+
`Incorrect passphrase for account ${account.npub} after ${MAX_PASSPHRASE_ATTEMPTS} attempts.`
|
|
27479
|
+
);
|
|
27480
|
+
}
|
|
27211
27481
|
|
|
27212
27482
|
// src/cli.ts
|
|
27213
|
-
var SUBCOMMANDS = /* @__PURE__ */ new Set(["login", "logout", "whoami", "accounts"]);
|
|
27483
|
+
var SUBCOMMANDS = /* @__PURE__ */ new Set(["login", "logout", "whoami", "accounts", "switch", "help"]);
|
|
27214
27484
|
function parseCli(argv) {
|
|
27215
27485
|
const rest = [...argv];
|
|
27486
|
+
if (rest.includes("-h") || rest.includes("--help")) {
|
|
27487
|
+
return { command: "help", allowWrites: false };
|
|
27488
|
+
}
|
|
27216
27489
|
let command = "run";
|
|
27217
27490
|
if (rest[0] && SUBCOMMANDS.has(rest[0])) {
|
|
27218
27491
|
command = rest.shift();
|
|
27219
27492
|
}
|
|
27220
27493
|
let relays;
|
|
27221
27494
|
let account;
|
|
27495
|
+
let target;
|
|
27222
27496
|
let allowWrites = false;
|
|
27223
27497
|
for (let i4 = 0; i4 < rest.length; i4++) {
|
|
27224
27498
|
const arg = rest[i4];
|
|
27225
27499
|
if (arg === "--relays") relays = splitRelays(rest[++i4]);
|
|
27226
27500
|
else if (arg === "--allow-writes") allowWrites = true;
|
|
27227
27501
|
else if (arg === "--account") account = rest[++i4];
|
|
27502
|
+
else if (!arg.startsWith("-") && target === void 0) target = arg;
|
|
27228
27503
|
}
|
|
27229
|
-
return { command, relays, allowWrites, account };
|
|
27504
|
+
return { command, relays, allowWrites, account, target };
|
|
27505
|
+
}
|
|
27506
|
+
function formatFatal(err, debug = false) {
|
|
27507
|
+
if (err instanceof Error) return debug && err.stack ? err.stack : err.message;
|
|
27508
|
+
return String(err);
|
|
27230
27509
|
}
|
|
27231
27510
|
function splitRelays(value) {
|
|
27232
27511
|
if (!value) return void 0;
|
|
27233
27512
|
const parts = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
27234
27513
|
return parts.length ? parts : void 0;
|
|
27235
27514
|
}
|
|
27515
|
+
function helpText() {
|
|
27516
|
+
return [
|
|
27517
|
+
"formstr-mcp \u2014 MCP server for the Formstr super-app (Nostr forms & more).",
|
|
27518
|
+
"",
|
|
27519
|
+
"Usage: formstr-mcp [command] [flags]",
|
|
27520
|
+
"",
|
|
27521
|
+
"Commands:",
|
|
27522
|
+
" run Run the stdio MCP server (default when no command is given).",
|
|
27523
|
+
" login Sign in (create / import / bunker URI / QR) and store the key.",
|
|
27524
|
+
" logout [npub] Remove a stored account (defaults to the active one).",
|
|
27525
|
+
" whoami Print the active account.",
|
|
27526
|
+
" accounts List stored accounts ('*' marks the active one).",
|
|
27527
|
+
" switch <npub> Set the active account (accepts an npub or hex pubkey).",
|
|
27528
|
+
" help Show this help (also -h, --help).",
|
|
27529
|
+
"",
|
|
27530
|
+
"Flags:",
|
|
27531
|
+
" --allow-writes Enable gated write tools (update / delete / share / submit).",
|
|
27532
|
+
" --account <npub|hex> Boot a specific account instead of the active one.",
|
|
27533
|
+
" --relays <a,b,\u2026> Override relays (comma-separated).",
|
|
27534
|
+
"",
|
|
27535
|
+
"Env:",
|
|
27536
|
+
" FORMSTR_MCP_NCRYPTSEC_PASSPHRASE Unlock the active ncryptsec account at boot.",
|
|
27537
|
+
" FORMSTR_MCP_PASSPHRASE Encrypt the keystore file (keychain-less hosts)."
|
|
27538
|
+
].join("\n");
|
|
27539
|
+
}
|
|
27236
27540
|
|
|
27237
27541
|
// src/config.ts
|
|
27238
27542
|
function resolveConfig(cli, env) {
|
|
@@ -31315,6 +31619,7 @@ __export(service_exports, {
|
|
|
31315
31619
|
createForm: () => createForm,
|
|
31316
31620
|
deleteForm: () => deleteForm,
|
|
31317
31621
|
fetchForm: () => fetchForm,
|
|
31622
|
+
fetchFormKeys: () => fetchFormKeys,
|
|
31318
31623
|
fetchFormSummaryFromRef: () => fetchFormSummaryFromRef,
|
|
31319
31624
|
fetchMyForms: () => fetchMyForms,
|
|
31320
31625
|
fetchResponses: () => fetchResponses,
|
|
@@ -31326,34 +31631,328 @@ __export(service_exports, {
|
|
|
31326
31631
|
updateForm: () => updateForm
|
|
31327
31632
|
});
|
|
31328
31633
|
|
|
31329
|
-
// ../../node_modules/.pnpm
|
|
31330
|
-
|
|
31331
|
-
|
|
31332
|
-
|
|
31333
|
-
// ../agent/src/services/forms/keys.ts
|
|
31334
|
-
function makeViewKeySigner(viewKeyHex) {
|
|
31335
|
-
return new LocalSigner2(hexToBytes3(viewKeyHex));
|
|
31634
|
+
// ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/utils.js
|
|
31635
|
+
function isBytes4(a) {
|
|
31636
|
+
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
|
|
31336
31637
|
}
|
|
31337
|
-
function
|
|
31338
|
-
|
|
31638
|
+
function abytes4(b, ...lengths) {
|
|
31639
|
+
if (!isBytes4(b))
|
|
31640
|
+
throw new Error("Uint8Array expected");
|
|
31641
|
+
if (lengths.length > 0 && !lengths.includes(b.length))
|
|
31642
|
+
throw new Error("Uint8Array expected of length " + lengths + ", got length=" + b.length);
|
|
31339
31643
|
}
|
|
31340
|
-
function
|
|
31341
|
-
|
|
31644
|
+
function aexists3(instance, checkFinished = true) {
|
|
31645
|
+
if (instance.destroyed)
|
|
31646
|
+
throw new Error("Hash instance has been destroyed");
|
|
31647
|
+
if (checkFinished && instance.finished)
|
|
31648
|
+
throw new Error("Hash#digest() has already been called");
|
|
31342
31649
|
}
|
|
31343
|
-
function
|
|
31344
|
-
|
|
31345
|
-
|
|
31346
|
-
|
|
31650
|
+
function aoutput3(out, instance) {
|
|
31651
|
+
abytes4(out);
|
|
31652
|
+
const min = instance.outputLen;
|
|
31653
|
+
if (out.length < min) {
|
|
31654
|
+
throw new Error("digestInto() expects output buffer of length at least " + min);
|
|
31655
|
+
}
|
|
31347
31656
|
}
|
|
31348
|
-
function
|
|
31349
|
-
|
|
31350
|
-
|
|
31351
|
-
const out = new Uint8Array(hex.length / 2);
|
|
31352
|
-
for (let i4 = 0; i4 < out.length; i4++) {
|
|
31353
|
-
out[i4] = parseInt(hex.slice(i4 * 2, i4 * 2 + 2), 16);
|
|
31657
|
+
function clean3(...arrays) {
|
|
31658
|
+
for (let i4 = 0; i4 < arrays.length; i4++) {
|
|
31659
|
+
arrays[i4].fill(0);
|
|
31354
31660
|
}
|
|
31355
|
-
return out;
|
|
31356
31661
|
}
|
|
31662
|
+
function createView3(arr) {
|
|
31663
|
+
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
31664
|
+
}
|
|
31665
|
+
function rotr2(word, shift) {
|
|
31666
|
+
return word << 32 - shift | word >>> shift;
|
|
31667
|
+
}
|
|
31668
|
+
function utf8ToBytes2(str) {
|
|
31669
|
+
if (typeof str !== "string")
|
|
31670
|
+
throw new Error("string expected");
|
|
31671
|
+
return new Uint8Array(new TextEncoder().encode(str));
|
|
31672
|
+
}
|
|
31673
|
+
function toBytes(data) {
|
|
31674
|
+
if (typeof data === "string")
|
|
31675
|
+
data = utf8ToBytes2(data);
|
|
31676
|
+
abytes4(data);
|
|
31677
|
+
return data;
|
|
31678
|
+
}
|
|
31679
|
+
var Hash = class {
|
|
31680
|
+
};
|
|
31681
|
+
function createHasher2(hashCons) {
|
|
31682
|
+
const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
|
|
31683
|
+
const tmp = hashCons();
|
|
31684
|
+
hashC.outputLen = tmp.outputLen;
|
|
31685
|
+
hashC.blockLen = tmp.blockLen;
|
|
31686
|
+
hashC.create = () => hashCons();
|
|
31687
|
+
return hashC;
|
|
31688
|
+
}
|
|
31689
|
+
|
|
31690
|
+
// ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/_md.js
|
|
31691
|
+
function setBigUint64(view, byteOffset, value, isLE3) {
|
|
31692
|
+
if (typeof view.setBigUint64 === "function")
|
|
31693
|
+
return view.setBigUint64(byteOffset, value, isLE3);
|
|
31694
|
+
const _32n = BigInt(32);
|
|
31695
|
+
const _u32_max = BigInt(4294967295);
|
|
31696
|
+
const wh = Number(value >> _32n & _u32_max);
|
|
31697
|
+
const wl = Number(value & _u32_max);
|
|
31698
|
+
const h = isLE3 ? 4 : 0;
|
|
31699
|
+
const l = isLE3 ? 0 : 4;
|
|
31700
|
+
view.setUint32(byteOffset + h, wh, isLE3);
|
|
31701
|
+
view.setUint32(byteOffset + l, wl, isLE3);
|
|
31702
|
+
}
|
|
31703
|
+
function Chi2(a, b, c) {
|
|
31704
|
+
return a & b ^ ~a & c;
|
|
31705
|
+
}
|
|
31706
|
+
function Maj2(a, b, c) {
|
|
31707
|
+
return a & b ^ a & c ^ b & c;
|
|
31708
|
+
}
|
|
31709
|
+
var HashMD2 = class extends Hash {
|
|
31710
|
+
constructor(blockLen, outputLen, padOffset, isLE3) {
|
|
31711
|
+
super();
|
|
31712
|
+
this.finished = false;
|
|
31713
|
+
this.length = 0;
|
|
31714
|
+
this.pos = 0;
|
|
31715
|
+
this.destroyed = false;
|
|
31716
|
+
this.blockLen = blockLen;
|
|
31717
|
+
this.outputLen = outputLen;
|
|
31718
|
+
this.padOffset = padOffset;
|
|
31719
|
+
this.isLE = isLE3;
|
|
31720
|
+
this.buffer = new Uint8Array(blockLen);
|
|
31721
|
+
this.view = createView3(this.buffer);
|
|
31722
|
+
}
|
|
31723
|
+
update(data) {
|
|
31724
|
+
aexists3(this);
|
|
31725
|
+
data = toBytes(data);
|
|
31726
|
+
abytes4(data);
|
|
31727
|
+
const { view, buffer, blockLen } = this;
|
|
31728
|
+
const len = data.length;
|
|
31729
|
+
for (let pos = 0; pos < len; ) {
|
|
31730
|
+
const take = Math.min(blockLen - this.pos, len - pos);
|
|
31731
|
+
if (take === blockLen) {
|
|
31732
|
+
const dataView = createView3(data);
|
|
31733
|
+
for (; blockLen <= len - pos; pos += blockLen)
|
|
31734
|
+
this.process(dataView, pos);
|
|
31735
|
+
continue;
|
|
31736
|
+
}
|
|
31737
|
+
buffer.set(data.subarray(pos, pos + take), this.pos);
|
|
31738
|
+
this.pos += take;
|
|
31739
|
+
pos += take;
|
|
31740
|
+
if (this.pos === blockLen) {
|
|
31741
|
+
this.process(view, 0);
|
|
31742
|
+
this.pos = 0;
|
|
31743
|
+
}
|
|
31744
|
+
}
|
|
31745
|
+
this.length += data.length;
|
|
31746
|
+
this.roundClean();
|
|
31747
|
+
return this;
|
|
31748
|
+
}
|
|
31749
|
+
digestInto(out) {
|
|
31750
|
+
aexists3(this);
|
|
31751
|
+
aoutput3(out, this);
|
|
31752
|
+
this.finished = true;
|
|
31753
|
+
const { buffer, view, blockLen, isLE: isLE3 } = this;
|
|
31754
|
+
let { pos } = this;
|
|
31755
|
+
buffer[pos++] = 128;
|
|
31756
|
+
clean3(this.buffer.subarray(pos));
|
|
31757
|
+
if (this.padOffset > blockLen - pos) {
|
|
31758
|
+
this.process(view, 0);
|
|
31759
|
+
pos = 0;
|
|
31760
|
+
}
|
|
31761
|
+
for (let i4 = pos; i4 < blockLen; i4++)
|
|
31762
|
+
buffer[i4] = 0;
|
|
31763
|
+
setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE3);
|
|
31764
|
+
this.process(view, 0);
|
|
31765
|
+
const oview = createView3(out);
|
|
31766
|
+
const len = this.outputLen;
|
|
31767
|
+
if (len % 4)
|
|
31768
|
+
throw new Error("_sha2: outputLen should be aligned to 32bit");
|
|
31769
|
+
const outLen = len / 4;
|
|
31770
|
+
const state = this.get();
|
|
31771
|
+
if (outLen > state.length)
|
|
31772
|
+
throw new Error("_sha2: outputLen bigger than state");
|
|
31773
|
+
for (let i4 = 0; i4 < outLen; i4++)
|
|
31774
|
+
oview.setUint32(4 * i4, state[i4], isLE3);
|
|
31775
|
+
}
|
|
31776
|
+
digest() {
|
|
31777
|
+
const { buffer, outputLen } = this;
|
|
31778
|
+
this.digestInto(buffer);
|
|
31779
|
+
const res = buffer.slice(0, outputLen);
|
|
31780
|
+
this.destroy();
|
|
31781
|
+
return res;
|
|
31782
|
+
}
|
|
31783
|
+
_cloneInto(to) {
|
|
31784
|
+
to || (to = new this.constructor());
|
|
31785
|
+
to.set(...this.get());
|
|
31786
|
+
const { blockLen, buffer, length, finished, destroyed, pos } = this;
|
|
31787
|
+
to.destroyed = destroyed;
|
|
31788
|
+
to.finished = finished;
|
|
31789
|
+
to.length = length;
|
|
31790
|
+
to.pos = pos;
|
|
31791
|
+
if (length % blockLen)
|
|
31792
|
+
to.buffer.set(buffer);
|
|
31793
|
+
return to;
|
|
31794
|
+
}
|
|
31795
|
+
clone() {
|
|
31796
|
+
return this._cloneInto();
|
|
31797
|
+
}
|
|
31798
|
+
};
|
|
31799
|
+
var SHA256_IV2 = /* @__PURE__ */ Uint32Array.from([
|
|
31800
|
+
1779033703,
|
|
31801
|
+
3144134277,
|
|
31802
|
+
1013904242,
|
|
31803
|
+
2773480762,
|
|
31804
|
+
1359893119,
|
|
31805
|
+
2600822924,
|
|
31806
|
+
528734635,
|
|
31807
|
+
1541459225
|
|
31808
|
+
]);
|
|
31809
|
+
|
|
31810
|
+
// ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/sha2.js
|
|
31811
|
+
var SHA256_K2 = /* @__PURE__ */ Uint32Array.from([
|
|
31812
|
+
1116352408,
|
|
31813
|
+
1899447441,
|
|
31814
|
+
3049323471,
|
|
31815
|
+
3921009573,
|
|
31816
|
+
961987163,
|
|
31817
|
+
1508970993,
|
|
31818
|
+
2453635748,
|
|
31819
|
+
2870763221,
|
|
31820
|
+
3624381080,
|
|
31821
|
+
310598401,
|
|
31822
|
+
607225278,
|
|
31823
|
+
1426881987,
|
|
31824
|
+
1925078388,
|
|
31825
|
+
2162078206,
|
|
31826
|
+
2614888103,
|
|
31827
|
+
3248222580,
|
|
31828
|
+
3835390401,
|
|
31829
|
+
4022224774,
|
|
31830
|
+
264347078,
|
|
31831
|
+
604807628,
|
|
31832
|
+
770255983,
|
|
31833
|
+
1249150122,
|
|
31834
|
+
1555081692,
|
|
31835
|
+
1996064986,
|
|
31836
|
+
2554220882,
|
|
31837
|
+
2821834349,
|
|
31838
|
+
2952996808,
|
|
31839
|
+
3210313671,
|
|
31840
|
+
3336571891,
|
|
31841
|
+
3584528711,
|
|
31842
|
+
113926993,
|
|
31843
|
+
338241895,
|
|
31844
|
+
666307205,
|
|
31845
|
+
773529912,
|
|
31846
|
+
1294757372,
|
|
31847
|
+
1396182291,
|
|
31848
|
+
1695183700,
|
|
31849
|
+
1986661051,
|
|
31850
|
+
2177026350,
|
|
31851
|
+
2456956037,
|
|
31852
|
+
2730485921,
|
|
31853
|
+
2820302411,
|
|
31854
|
+
3259730800,
|
|
31855
|
+
3345764771,
|
|
31856
|
+
3516065817,
|
|
31857
|
+
3600352804,
|
|
31858
|
+
4094571909,
|
|
31859
|
+
275423344,
|
|
31860
|
+
430227734,
|
|
31861
|
+
506948616,
|
|
31862
|
+
659060556,
|
|
31863
|
+
883997877,
|
|
31864
|
+
958139571,
|
|
31865
|
+
1322822218,
|
|
31866
|
+
1537002063,
|
|
31867
|
+
1747873779,
|
|
31868
|
+
1955562222,
|
|
31869
|
+
2024104815,
|
|
31870
|
+
2227730452,
|
|
31871
|
+
2361852424,
|
|
31872
|
+
2428436474,
|
|
31873
|
+
2756734187,
|
|
31874
|
+
3204031479,
|
|
31875
|
+
3329325298
|
|
31876
|
+
]);
|
|
31877
|
+
var SHA256_W2 = /* @__PURE__ */ new Uint32Array(64);
|
|
31878
|
+
var SHA256 = class extends HashMD2 {
|
|
31879
|
+
constructor(outputLen = 32) {
|
|
31880
|
+
super(64, outputLen, 8, false);
|
|
31881
|
+
this.A = SHA256_IV2[0] | 0;
|
|
31882
|
+
this.B = SHA256_IV2[1] | 0;
|
|
31883
|
+
this.C = SHA256_IV2[2] | 0;
|
|
31884
|
+
this.D = SHA256_IV2[3] | 0;
|
|
31885
|
+
this.E = SHA256_IV2[4] | 0;
|
|
31886
|
+
this.F = SHA256_IV2[5] | 0;
|
|
31887
|
+
this.G = SHA256_IV2[6] | 0;
|
|
31888
|
+
this.H = SHA256_IV2[7] | 0;
|
|
31889
|
+
}
|
|
31890
|
+
get() {
|
|
31891
|
+
const { A, B, C, D, E, F, G, H } = this;
|
|
31892
|
+
return [A, B, C, D, E, F, G, H];
|
|
31893
|
+
}
|
|
31894
|
+
// prettier-ignore
|
|
31895
|
+
set(A, B, C, D, E, F, G, H) {
|
|
31896
|
+
this.A = A | 0;
|
|
31897
|
+
this.B = B | 0;
|
|
31898
|
+
this.C = C | 0;
|
|
31899
|
+
this.D = D | 0;
|
|
31900
|
+
this.E = E | 0;
|
|
31901
|
+
this.F = F | 0;
|
|
31902
|
+
this.G = G | 0;
|
|
31903
|
+
this.H = H | 0;
|
|
31904
|
+
}
|
|
31905
|
+
process(view, offset) {
|
|
31906
|
+
for (let i4 = 0; i4 < 16; i4++, offset += 4)
|
|
31907
|
+
SHA256_W2[i4] = view.getUint32(offset, false);
|
|
31908
|
+
for (let i4 = 16; i4 < 64; i4++) {
|
|
31909
|
+
const W15 = SHA256_W2[i4 - 15];
|
|
31910
|
+
const W2 = SHA256_W2[i4 - 2];
|
|
31911
|
+
const s0 = rotr2(W15, 7) ^ rotr2(W15, 18) ^ W15 >>> 3;
|
|
31912
|
+
const s1 = rotr2(W2, 17) ^ rotr2(W2, 19) ^ W2 >>> 10;
|
|
31913
|
+
SHA256_W2[i4] = s1 + SHA256_W2[i4 - 7] + s0 + SHA256_W2[i4 - 16] | 0;
|
|
31914
|
+
}
|
|
31915
|
+
let { A, B, C, D, E, F, G, H } = this;
|
|
31916
|
+
for (let i4 = 0; i4 < 64; i4++) {
|
|
31917
|
+
const sigma1 = rotr2(E, 6) ^ rotr2(E, 11) ^ rotr2(E, 25);
|
|
31918
|
+
const T1 = H + sigma1 + Chi2(E, F, G) + SHA256_K2[i4] + SHA256_W2[i4] | 0;
|
|
31919
|
+
const sigma0 = rotr2(A, 2) ^ rotr2(A, 13) ^ rotr2(A, 22);
|
|
31920
|
+
const T2 = sigma0 + Maj2(A, B, C) | 0;
|
|
31921
|
+
H = G;
|
|
31922
|
+
G = F;
|
|
31923
|
+
F = E;
|
|
31924
|
+
E = D + T1 | 0;
|
|
31925
|
+
D = C;
|
|
31926
|
+
C = B;
|
|
31927
|
+
B = A;
|
|
31928
|
+
A = T1 + T2 | 0;
|
|
31929
|
+
}
|
|
31930
|
+
A = A + this.A | 0;
|
|
31931
|
+
B = B + this.B | 0;
|
|
31932
|
+
C = C + this.C | 0;
|
|
31933
|
+
D = D + this.D | 0;
|
|
31934
|
+
E = E + this.E | 0;
|
|
31935
|
+
F = F + this.F | 0;
|
|
31936
|
+
G = G + this.G | 0;
|
|
31937
|
+
H = H + this.H | 0;
|
|
31938
|
+
this.set(A, B, C, D, E, F, G, H);
|
|
31939
|
+
}
|
|
31940
|
+
roundClean() {
|
|
31941
|
+
clean3(SHA256_W2);
|
|
31942
|
+
}
|
|
31943
|
+
destroy() {
|
|
31944
|
+
this.set(0, 0, 0, 0, 0, 0, 0, 0);
|
|
31945
|
+
clean3(this.buffer);
|
|
31946
|
+
}
|
|
31947
|
+
};
|
|
31948
|
+
var sha2562 = /* @__PURE__ */ createHasher2(() => new SHA256());
|
|
31949
|
+
|
|
31950
|
+
// ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/sha256.js
|
|
31951
|
+
var sha2563 = sha2562;
|
|
31952
|
+
|
|
31953
|
+
// ../../node_modules/.pnpm/nostr-tools@2.23.3_typescript@5.9.3/node_modules/nostr-tools/lib/esm/utils.js
|
|
31954
|
+
var utf8Decoder7 = new TextDecoder("utf-8");
|
|
31955
|
+
var utf8Encoder7 = new TextEncoder();
|
|
31357
31956
|
|
|
31358
31957
|
// ../agent/src/services/forms/types.ts
|
|
31359
31958
|
var FORM_KINDS = {
|
|
@@ -31375,46 +31974,219 @@ var AnswerType = /* @__PURE__ */ ((AnswerType2) => {
|
|
|
31375
31974
|
AnswerType2["datetime"] = "datetime";
|
|
31376
31975
|
AnswerType2["fileUpload"] = "fileUpload";
|
|
31377
31976
|
AnswerType2["signature"] = "signature";
|
|
31378
|
-
AnswerType2["
|
|
31977
|
+
AnswerType2["multipleChoiceGrid"] = "multipleChoiceGrid";
|
|
31379
31978
|
AnswerType2["checkboxGrid"] = "checkboxGrid";
|
|
31979
|
+
AnswerType2["rating"] = "rating";
|
|
31380
31980
|
AnswerType2["section"] = "section";
|
|
31381
31981
|
return AnswerType2;
|
|
31382
31982
|
})(AnswerType || {});
|
|
31383
31983
|
|
|
31984
|
+
// ../agent/src/services/forms/fieldCodec.ts
|
|
31985
|
+
var PRIMITIVE_BY_TYPE = {
|
|
31986
|
+
["label" /* label */]: "label",
|
|
31987
|
+
["section" /* section */]: "section",
|
|
31988
|
+
["shortText" /* shortText */]: "text",
|
|
31989
|
+
["paragraph" /* paragraph */]: "text",
|
|
31990
|
+
["date" /* date */]: "text",
|
|
31991
|
+
["time" /* time */]: "text",
|
|
31992
|
+
["signature" /* signature */]: "text",
|
|
31993
|
+
["number" /* number */]: "number",
|
|
31994
|
+
["radioButton" /* radioButton */]: "option",
|
|
31995
|
+
["checkboxes" /* checkboxes */]: "option",
|
|
31996
|
+
["dropdown" /* dropdown */]: "option",
|
|
31997
|
+
["fileUpload" /* fileUpload */]: "file",
|
|
31998
|
+
["datetime" /* datetime */]: "datetime",
|
|
31999
|
+
["multipleChoiceGrid" /* multipleChoiceGrid */]: "grid",
|
|
32000
|
+
["checkboxGrid" /* checkboxGrid */]: "grid",
|
|
32001
|
+
["rating" /* rating */]: "rating"
|
|
32002
|
+
};
|
|
32003
|
+
var DEFAULT_TYPE_BY_PRIMITIVE = {
|
|
32004
|
+
text: "shortText" /* shortText */,
|
|
32005
|
+
number: "number" /* number */,
|
|
32006
|
+
option: "radioButton" /* radioButton */,
|
|
32007
|
+
label: "label" /* label */,
|
|
32008
|
+
section: "section" /* section */,
|
|
32009
|
+
file: "fileUpload" /* fileUpload */,
|
|
32010
|
+
datetime: "datetime" /* datetime */,
|
|
32011
|
+
grid: "multipleChoiceGrid" /* multipleChoiceGrid */,
|
|
32012
|
+
rating: "rating" /* rating */
|
|
32013
|
+
};
|
|
32014
|
+
var GRID_TYPES = /* @__PURE__ */ new Set(["multipleChoiceGrid" /* multipleChoiceGrid */, "checkboxGrid" /* checkboxGrid */]);
|
|
32015
|
+
function makeId() {
|
|
32016
|
+
return Math.random().toString(36).slice(2, 8) || "id";
|
|
32017
|
+
}
|
|
32018
|
+
function buildFieldTag(field) {
|
|
32019
|
+
let options = "[]";
|
|
32020
|
+
if (GRID_TYPES.has(field.type)) {
|
|
32021
|
+
const grid = {
|
|
32022
|
+
rows: (field.gridRows ?? []).map((label) => [makeId(), label]),
|
|
32023
|
+
columns: (field.gridCols ?? []).map((label) => [makeId(), label])
|
|
32024
|
+
};
|
|
32025
|
+
options = JSON.stringify(grid);
|
|
32026
|
+
} else if (field.options) {
|
|
32027
|
+
options = JSON.stringify(field.options.map((o) => [o.id, o.label]));
|
|
32028
|
+
}
|
|
32029
|
+
const config2 = { renderElement: field.type };
|
|
32030
|
+
if (field.required !== void 0) config2.required = field.required;
|
|
32031
|
+
if (field.placeholder !== void 0) config2.placeholder = field.placeholder;
|
|
32032
|
+
if (field.type === "rating" /* rating */ && field.maxStars !== void 0) {
|
|
32033
|
+
config2.maxStars = field.maxStars;
|
|
32034
|
+
}
|
|
32035
|
+
if (GRID_TYPES.has(field.type)) {
|
|
32036
|
+
config2.allowMultiplePerRow = field.type === "checkboxGrid" /* checkboxGrid */;
|
|
32037
|
+
}
|
|
32038
|
+
if (field.validation) {
|
|
32039
|
+
const v = field.validation;
|
|
32040
|
+
const rules = {};
|
|
32041
|
+
if (v.min !== void 0) rules.min = { min: v.min };
|
|
32042
|
+
if (v.max !== void 0) rules.max = { max: v.max };
|
|
32043
|
+
if (v.regex !== void 0) rules.regex = { pattern: v.regex, errorMessage: v.regexError ?? "" };
|
|
32044
|
+
if (Object.keys(rules).length > 0) config2.validationRules = rules;
|
|
32045
|
+
}
|
|
32046
|
+
if (field.fileConfig) {
|
|
32047
|
+
const f = field.fileConfig;
|
|
32048
|
+
if (f.blossomServer !== void 0) config2.blossomServer = f.blossomServer;
|
|
32049
|
+
if (f.maxBytes !== void 0) config2.maxFileSize = f.maxBytes / (1024 * 1024);
|
|
32050
|
+
if (f.mimeTypes !== void 0) config2.allowedTypes = f.mimeTypes;
|
|
32051
|
+
}
|
|
32052
|
+
return ["field", field.id, PRIMITIVE_BY_TYPE[field.type], field.label, options, JSON.stringify(config2)];
|
|
32053
|
+
}
|
|
32054
|
+
function isAnswerType(value) {
|
|
32055
|
+
return Object.values(AnswerType).includes(value);
|
|
32056
|
+
}
|
|
32057
|
+
function resolveType(primitive, renderElement) {
|
|
32058
|
+
if (primitive === "section") return "section" /* section */;
|
|
32059
|
+
if (renderElement) {
|
|
32060
|
+
if (renderElement === "multiChoiceGrid") return "multipleChoiceGrid" /* multipleChoiceGrid */;
|
|
32061
|
+
if (isAnswerType(renderElement)) return renderElement;
|
|
32062
|
+
}
|
|
32063
|
+
if (primitive === "multiChoiceGrid") return "multipleChoiceGrid" /* multipleChoiceGrid */;
|
|
32064
|
+
if (isAnswerType(primitive)) return primitive;
|
|
32065
|
+
return DEFAULT_TYPE_BY_PRIMITIVE[primitive] ?? "shortText" /* shortText */;
|
|
32066
|
+
}
|
|
32067
|
+
function parseFieldTag(tag) {
|
|
32068
|
+
const [, id = "", primitive = "text", label = "", optionsJson, configJson] = tag;
|
|
32069
|
+
let config2 = {};
|
|
32070
|
+
if (configJson) {
|
|
32071
|
+
try {
|
|
32072
|
+
const parsed = JSON.parse(configJson);
|
|
32073
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) config2 = parsed;
|
|
32074
|
+
} catch {
|
|
32075
|
+
}
|
|
32076
|
+
}
|
|
32077
|
+
const field = {
|
|
32078
|
+
id,
|
|
32079
|
+
type: resolveType(primitive, config2.renderElement),
|
|
32080
|
+
label
|
|
32081
|
+
};
|
|
32082
|
+
let parsedOptions;
|
|
32083
|
+
if (optionsJson) {
|
|
32084
|
+
try {
|
|
32085
|
+
parsedOptions = JSON.parse(optionsJson);
|
|
32086
|
+
} catch {
|
|
32087
|
+
}
|
|
32088
|
+
}
|
|
32089
|
+
if (Array.isArray(parsedOptions) && parsedOptions.length > 0) {
|
|
32090
|
+
field.options = parsedOptions.map(
|
|
32091
|
+
(o) => ({ id: o[0], label: o[1] })
|
|
32092
|
+
);
|
|
32093
|
+
} else if (parsedOptions && typeof parsedOptions === "object") {
|
|
32094
|
+
const grid = parsedOptions;
|
|
32095
|
+
if (Array.isArray(grid.rows)) field.gridRows = grid.rows.map((r) => r[1]);
|
|
32096
|
+
if (Array.isArray(grid.columns)) field.gridCols = grid.columns.map((c) => c[1]);
|
|
32097
|
+
}
|
|
32098
|
+
if (typeof config2.required === "boolean") field.required = config2.required;
|
|
32099
|
+
if (typeof config2.placeholder === "string") field.placeholder = config2.placeholder;
|
|
32100
|
+
if (typeof config2.maxStars === "number") field.maxStars = config2.maxStars;
|
|
32101
|
+
const rules = config2.validationRules;
|
|
32102
|
+
if (rules && typeof rules === "object") {
|
|
32103
|
+
const validation = {};
|
|
32104
|
+
if (typeof rules.range?.min === "number") validation.min = rules.range.min;
|
|
32105
|
+
if (typeof rules.range?.max === "number") validation.max = rules.range.max;
|
|
32106
|
+
if (typeof rules.min?.min === "number") validation.min = rules.min.min;
|
|
32107
|
+
if (typeof rules.max?.max === "number") validation.max = rules.max.max;
|
|
32108
|
+
if (typeof rules.regex?.pattern === "string") {
|
|
32109
|
+
validation.regex = rules.regex.pattern;
|
|
32110
|
+
if (rules.regex.errorMessage) validation.regexError = rules.regex.errorMessage;
|
|
32111
|
+
}
|
|
32112
|
+
if (Object.keys(validation).length > 0) field.validation = validation;
|
|
32113
|
+
}
|
|
32114
|
+
if (config2.blossomServer !== void 0 || config2.maxFileSize !== void 0 || config2.allowedTypes !== void 0) {
|
|
32115
|
+
field.fileConfig = {
|
|
32116
|
+
...typeof config2.blossomServer === "string" && { blossomServer: config2.blossomServer },
|
|
32117
|
+
...typeof config2.maxFileSize === "number" && {
|
|
32118
|
+
maxBytes: config2.maxFileSize * 1024 * 1024
|
|
32119
|
+
},
|
|
32120
|
+
...Array.isArray(config2.allowedTypes) && { mimeTypes: config2.allowedTypes }
|
|
32121
|
+
};
|
|
32122
|
+
}
|
|
32123
|
+
return field;
|
|
32124
|
+
}
|
|
32125
|
+
|
|
32126
|
+
// ../agent/src/services/forms/keys.ts
|
|
32127
|
+
function makeViewKeySigner(viewKeyHex) {
|
|
32128
|
+
return new LocalSigner2(hexToBytes3(viewKeyHex));
|
|
32129
|
+
}
|
|
32130
|
+
function makeSigningKeySigner(signingKeyHex) {
|
|
32131
|
+
return new LocalSigner2(hexToBytes3(signingKeyHex));
|
|
32132
|
+
}
|
|
32133
|
+
function encodeFormKeys(signingKeyHex, viewKeyHex) {
|
|
32134
|
+
return viewKeyHex ? `${signingKeyHex}:${viewKeyHex}` : signingKeyHex;
|
|
32135
|
+
}
|
|
32136
|
+
function decodeFormKeys(segment) {
|
|
32137
|
+
const idx = segment.indexOf(":");
|
|
32138
|
+
if (idx < 0) return { signingKey: segment };
|
|
32139
|
+
return { signingKey: segment.slice(0, idx), viewKey: segment.slice(idx + 1) };
|
|
32140
|
+
}
|
|
32141
|
+
function hexToBytes3(hex) {
|
|
32142
|
+
if (hex.length % 2 !== 0) throw new Error("invalid hex length");
|
|
32143
|
+
if (hex.length > 0 && !/^[0-9a-fA-F]+$/.test(hex)) throw new Error("invalid hex characters");
|
|
32144
|
+
const out = new Uint8Array(hex.length / 2);
|
|
32145
|
+
for (let i4 = 0; i4 < out.length; i4++) {
|
|
32146
|
+
out[i4] = parseInt(hex.slice(i4 * 2, i4 * 2 + 2), 16);
|
|
32147
|
+
}
|
|
32148
|
+
return out;
|
|
32149
|
+
}
|
|
32150
|
+
|
|
31384
32151
|
// ../agent/src/services/forms/service.ts
|
|
32152
|
+
function relaysForForm(formRelays) {
|
|
32153
|
+
return Array.from(/* @__PURE__ */ new Set([...relayManager.getRelaysForModule("forms"), ...formRelays ?? []]));
|
|
32154
|
+
}
|
|
32155
|
+
function buildSpecRows(formId, name, fields, settings) {
|
|
32156
|
+
const rows = [
|
|
32157
|
+
["d", formId],
|
|
32158
|
+
["name", name]
|
|
32159
|
+
];
|
|
32160
|
+
if (settings) rows.push(["settings", JSON.stringify(settings)]);
|
|
32161
|
+
for (const field of fields) rows.push(buildFieldTag(field));
|
|
32162
|
+
return rows;
|
|
32163
|
+
}
|
|
32164
|
+
function buildEncryptedOuterTags(formId, name, relays, settings) {
|
|
32165
|
+
const tags = [["d", formId], ["name", name], ...relays.map((r) => ["relay", r])];
|
|
32166
|
+
const allowed = settings?.allowedResponders ?? [];
|
|
32167
|
+
const pTags = /* @__PURE__ */ new Set([...allowed, ...settings?.collaborators ?? []]);
|
|
32168
|
+
for (const pk of allowed) tags.push(["allowed", pk]);
|
|
32169
|
+
for (const pk of pTags) tags.push(["p", pk]);
|
|
32170
|
+
return tags;
|
|
32171
|
+
}
|
|
31385
32172
|
async function createForm(params) {
|
|
31386
|
-
|
|
31387
|
-
const userPubkey = await signer.getPublicKey();
|
|
32173
|
+
await signerManager.getSigner();
|
|
31388
32174
|
const formId = crypto.randomUUID().slice(0, 8);
|
|
31389
32175
|
const relays = relayManager.getRelaysForModule("forms");
|
|
31390
|
-
const
|
|
31391
|
-
|
|
31392
|
-
|
|
31393
|
-
|
|
31394
|
-
if (params.settings) baseTags.push(["settings", JSON.stringify(params.settings)]);
|
|
31395
|
-
for (const field of params.fields) {
|
|
31396
|
-
baseTags.push(buildFieldTag(field));
|
|
31397
|
-
}
|
|
32176
|
+
const specRows = buildSpecRows(formId, params.name, params.fields, params.settings);
|
|
32177
|
+
const signingKey = generateSecretKey();
|
|
32178
|
+
const signingKeyHex = bytesToHex(signingKey);
|
|
32179
|
+
const signingPubkey = getPublicKey(signingKey);
|
|
31398
32180
|
if (params.encrypt) {
|
|
31399
|
-
const signingKey = generateSecretKey();
|
|
31400
|
-
const signingKeyHex = bytesToHex(signingKey);
|
|
31401
|
-
const signingPubkey = getPublicKey(signingKey);
|
|
31402
32181
|
const viewKey = generateSecretKey();
|
|
31403
32182
|
const viewKeyHex = bytesToHex(viewKey);
|
|
31404
32183
|
const viewPubkey = getPublicKey(viewKey);
|
|
31405
32184
|
const formSigner = new LocalSigner2(signingKey);
|
|
31406
|
-
const
|
|
31407
|
-
const content = await formSigner.nip44Encrypt(viewPubkey, JSON.stringify(fieldTags));
|
|
31408
|
-
const encTags = [
|
|
31409
|
-
["d", formId],
|
|
31410
|
-
["name", params.name],
|
|
31411
|
-
["encryption", "view-key"]
|
|
31412
|
-
];
|
|
31413
|
-
if (params.settings) encTags.push(["settings", JSON.stringify(params.settings)]);
|
|
32185
|
+
const content = await formSigner.nip44Encrypt(viewPubkey, JSON.stringify(specRows));
|
|
31414
32186
|
const event2 = {
|
|
31415
32187
|
kind: FORM_KINDS.template,
|
|
31416
32188
|
created_at: Math.floor(Date.now() / 1e3),
|
|
31417
|
-
tags:
|
|
32189
|
+
tags: buildEncryptedOuterTags(formId, params.name, relays, params.settings),
|
|
31418
32190
|
content
|
|
31419
32191
|
};
|
|
31420
32192
|
const signed2 = finalizeEvent(event2, signingKey);
|
|
@@ -31422,20 +32194,20 @@ async function createForm(params) {
|
|
|
31422
32194
|
await appendToMyFormsList(signingPubkey, formId, relays[0] ?? "", signingKeyHex, viewKeyHex);
|
|
31423
32195
|
return { formId, pubkey: signingPubkey, signingKey: signingKeyHex, viewKey: viewKeyHex };
|
|
31424
32196
|
}
|
|
31425
|
-
|
|
32197
|
+
const tags = [...specRows, ["t", "public"], ...relays.map((r) => ["relay", r])];
|
|
31426
32198
|
const event = {
|
|
31427
32199
|
kind: FORM_KINDS.template,
|
|
31428
32200
|
created_at: Math.floor(Date.now() / 1e3),
|
|
31429
|
-
tags
|
|
32201
|
+
tags,
|
|
31430
32202
|
content: ""
|
|
31431
32203
|
};
|
|
31432
|
-
const signed =
|
|
32204
|
+
const signed = finalizeEvent(event, signingKey);
|
|
31433
32205
|
await nostrRuntime.publish(relays, signed);
|
|
31434
|
-
await appendToMyFormsList(
|
|
31435
|
-
return { formId, pubkey:
|
|
32206
|
+
await appendToMyFormsList(signingPubkey, formId, relays[0] ?? "", signingKeyHex, void 0);
|
|
32207
|
+
return { formId, pubkey: signingPubkey, signingKey: signingKeyHex };
|
|
31436
32208
|
}
|
|
31437
|
-
async function fetchForm(pubkey, formId, viewKey) {
|
|
31438
|
-
const relays =
|
|
32209
|
+
async function fetchForm(pubkey, formId, viewKey, relayHints) {
|
|
32210
|
+
const relays = relaysForForm(relayHints);
|
|
31439
32211
|
const event = await nostrRuntime.fetchOne(relays, {
|
|
31440
32212
|
kinds: [FORM_KINDS.template],
|
|
31441
32213
|
authors: [pubkey],
|
|
@@ -31443,25 +32215,48 @@ async function fetchForm(pubkey, formId, viewKey) {
|
|
|
31443
32215
|
limit: 1
|
|
31444
32216
|
});
|
|
31445
32217
|
if (!event) return null;
|
|
31446
|
-
|
|
31447
|
-
if (
|
|
32218
|
+
let effectiveViewKey = viewKey;
|
|
32219
|
+
if (!effectiveViewKey && event.content && !event.tags.some((t) => t[0] === "field")) {
|
|
32220
|
+
const keys = await fetchFormKeys(pubkey, formId).catch(() => null);
|
|
32221
|
+
effectiveViewKey = keys?.viewKey;
|
|
32222
|
+
}
|
|
32223
|
+
let decryptedRows;
|
|
32224
|
+
if (event.content && effectiveViewKey) {
|
|
31448
32225
|
try {
|
|
31449
|
-
const viewSigner = makeViewKeySigner(
|
|
32226
|
+
const viewSigner = makeViewKeySigner(effectiveViewKey);
|
|
31450
32227
|
const decrypted = await viewSigner.nip44Decrypt(pubkey, event.content);
|
|
31451
|
-
const
|
|
31452
|
-
|
|
31453
|
-
|
|
31454
|
-
|
|
31455
|
-
|
|
31456
|
-
|
|
31457
|
-
|
|
31458
|
-
|
|
32228
|
+
const rows = JSON.parse(decrypted);
|
|
32229
|
+
if (Array.isArray(rows)) decryptedRows = rows;
|
|
32230
|
+
} catch {
|
|
32231
|
+
}
|
|
32232
|
+
}
|
|
32233
|
+
return parseFormEvent(event, decryptedRows);
|
|
32234
|
+
}
|
|
32235
|
+
function accessGrantAlias(formAuthor, formId, recipient) {
|
|
32236
|
+
return bytesToHex(sha2563(`${FORM_KINDS.template}:${formAuthor}:${formId}:${recipient}`));
|
|
32237
|
+
}
|
|
32238
|
+
async function fetchFormKeys(formAuthor, formId) {
|
|
32239
|
+
const signer = signerManager.getSignerIfAvailable();
|
|
32240
|
+
if (!signer?.nip44Decrypt) return null;
|
|
32241
|
+
const userPub = await signer.getPublicKey();
|
|
32242
|
+
const relays = relayManager.getRelaysForModule("forms");
|
|
32243
|
+
const wraps = await nostrRuntime.querySync(relays, {
|
|
32244
|
+
kinds: [FORM_KINDS.giftWrap],
|
|
32245
|
+
"#p": [accessGrantAlias(formAuthor, formId, userPub)]
|
|
32246
|
+
});
|
|
32247
|
+
for (const wrap of wraps) {
|
|
32248
|
+
try {
|
|
32249
|
+
const seal = JSON.parse(await signer.nip44Decrypt(wrap.pubkey, wrap.content));
|
|
32250
|
+
const rumor = JSON.parse(await signer.nip44Decrypt(seal.pubkey, seal.content));
|
|
32251
|
+
const viewKey = rumor.tags?.find((t) => t[0] === "ViewAccess")?.[1];
|
|
32252
|
+
const signingKey = rumor.tags?.find((t) => t[0] === "EditAccess")?.[1];
|
|
32253
|
+
if (viewKey || signingKey) return { viewKey, signingKey };
|
|
31459
32254
|
} catch {
|
|
31460
32255
|
}
|
|
31461
32256
|
}
|
|
31462
|
-
return
|
|
32257
|
+
return null;
|
|
31463
32258
|
}
|
|
31464
|
-
async function submitResponse(formPubkey, formId, responses, encrypt7 = false, overrideSigner) {
|
|
32259
|
+
async function submitResponse(formPubkey, formId, responses, encrypt7 = false, overrideSigner, formRelays) {
|
|
31465
32260
|
const signer = overrideSigner ?? await signerManager.getSigner();
|
|
31466
32261
|
const responseTags = responses.map((r) => ["response", r.fieldId, r.answer, r.metadata ?? ""]);
|
|
31467
32262
|
const tags = [
|
|
@@ -31480,11 +32275,10 @@ async function submitResponse(formPubkey, formId, responses, encrypt7 = false, o
|
|
|
31480
32275
|
content
|
|
31481
32276
|
};
|
|
31482
32277
|
const signed = await signer.signEvent(event);
|
|
31483
|
-
|
|
31484
|
-
await nostrRuntime.publish(relays, signed);
|
|
32278
|
+
await nostrRuntime.publish(relaysForForm(formRelays), signed);
|
|
31485
32279
|
}
|
|
31486
|
-
function subscribeToResponses(formPubkey, formId, onResponse, onEose, signingKey) {
|
|
31487
|
-
const relays =
|
|
32280
|
+
function subscribeToResponses(formPubkey, formId, onResponse, onEose, signingKey, formRelays) {
|
|
32281
|
+
const relays = relaysForForm(formRelays);
|
|
31488
32282
|
const formSigner = signingKey ? makeSigningKeySigner(signingKey) : void 0;
|
|
31489
32283
|
return nostrRuntime.subscribe(
|
|
31490
32284
|
relays,
|
|
@@ -31508,9 +32302,8 @@ function subscribeToResponses(formPubkey, formId, onResponse, onEose, signingKey
|
|
|
31508
32302
|
}
|
|
31509
32303
|
);
|
|
31510
32304
|
}
|
|
31511
|
-
async function fetchResponses(formPubkey, formId, signingKey) {
|
|
31512
|
-
const
|
|
31513
|
-
const events = await nostrRuntime.querySync(relays, {
|
|
32305
|
+
async function fetchResponses(formPubkey, formId, signingKey, formRelays) {
|
|
32306
|
+
const events = await nostrRuntime.querySync(relaysForForm(formRelays), {
|
|
31514
32307
|
kinds: [FORM_KINDS.response],
|
|
31515
32308
|
"#a": [`${FORM_KINDS.template}:${formPubkey}:${formId}`]
|
|
31516
32309
|
});
|
|
@@ -31543,6 +32336,27 @@ async function fetchLatestMyFormsEvent(relays, userPubkey) {
|
|
|
31543
32336
|
null
|
|
31544
32337
|
);
|
|
31545
32338
|
}
|
|
32339
|
+
async function decryptListEntries(signer, userPubkey, content) {
|
|
32340
|
+
let decrypted = "";
|
|
32341
|
+
if (content.includes("?iv=")) {
|
|
32342
|
+
if (!signer.decrypt) throw new Error("Signer cannot decrypt");
|
|
32343
|
+
decrypted = await signer.decrypt(userPubkey, content);
|
|
32344
|
+
} else {
|
|
32345
|
+
decrypted = await nip44SelfDecrypt(signer, content);
|
|
32346
|
+
}
|
|
32347
|
+
const parsed = JSON.parse(decrypted);
|
|
32348
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
32349
|
+
}
|
|
32350
|
+
async function publishMyFormsList(signer, relays, entries) {
|
|
32351
|
+
const encrypted = await nip44SelfEncrypt(signer, JSON.stringify(entries));
|
|
32352
|
+
const event = {
|
|
32353
|
+
kind: FORM_KINDS.myFormsList,
|
|
32354
|
+
created_at: Math.floor(Date.now() / 1e3),
|
|
32355
|
+
tags: [],
|
|
32356
|
+
content: encrypted
|
|
32357
|
+
};
|
|
32358
|
+
await nostrRuntime.publish(relays, await signer.signEvent(event));
|
|
32359
|
+
}
|
|
31546
32360
|
async function fetchMyForms() {
|
|
31547
32361
|
const signer = await signerManager.getSigner();
|
|
31548
32362
|
const userPubkey = await signer.getPublicKey();
|
|
@@ -31551,15 +32365,7 @@ async function fetchMyForms() {
|
|
|
31551
32365
|
let entries = [];
|
|
31552
32366
|
if (listEvent?.content) {
|
|
31553
32367
|
try {
|
|
31554
|
-
|
|
31555
|
-
if (listEvent.content.includes("?iv=")) {
|
|
31556
|
-
if (!signer.decrypt) throw new Error("Signer cannot decrypt");
|
|
31557
|
-
decrypted = await signer.decrypt(userPubkey, listEvent.content);
|
|
31558
|
-
} else {
|
|
31559
|
-
decrypted = await nip44SelfDecrypt(signer, listEvent.content);
|
|
31560
|
-
}
|
|
31561
|
-
const parsed = JSON.parse(decrypted);
|
|
31562
|
-
entries = Array.isArray(parsed) ? parsed : [];
|
|
32368
|
+
entries = await decryptListEntries(signer, userPubkey, listEvent.content);
|
|
31563
32369
|
} catch {
|
|
31564
32370
|
}
|
|
31565
32371
|
}
|
|
@@ -31577,7 +32383,7 @@ async function fetchMyForms() {
|
|
|
31577
32383
|
if (d) eventMap.set(`${evt.pubkey}:${d}`, evt);
|
|
31578
32384
|
}
|
|
31579
32385
|
return entries.filter((e) => e[0] === "f" && e[1]).map((entry) => {
|
|
31580
|
-
const [, coordKey, , keySegment] = entry;
|
|
32386
|
+
const [, coordKey, relayHint, keySegment] = entry;
|
|
31581
32387
|
const [formPubkey, formId] = coordKey.split(":");
|
|
31582
32388
|
if (!formPubkey || !formId) return null;
|
|
31583
32389
|
const evt = eventMap.get(coordKey);
|
|
@@ -31593,7 +32399,8 @@ async function fetchMyForms() {
|
|
|
31593
32399
|
createdAt: evt?.created_at ?? 0,
|
|
31594
32400
|
isEncrypted,
|
|
31595
32401
|
signingKey: keys?.signingKey,
|
|
31596
|
-
viewKey: keys?.viewKey
|
|
32402
|
+
viewKey: keys?.viewKey,
|
|
32403
|
+
relay: relayHint || void 0
|
|
31597
32404
|
};
|
|
31598
32405
|
return summary;
|
|
31599
32406
|
}).filter((s) => s !== null);
|
|
@@ -31606,15 +32413,7 @@ async function appendToMyFormsList(formPubkey, formId, relay, signingKeyHex, vie
|
|
|
31606
32413
|
let entries = [];
|
|
31607
32414
|
if (existing?.content) {
|
|
31608
32415
|
try {
|
|
31609
|
-
|
|
31610
|
-
if (existing.content.includes("?iv=")) {
|
|
31611
|
-
if (!signer.decrypt) throw new Error("Signer cannot decrypt");
|
|
31612
|
-
decrypted = await signer.decrypt(userPubkey, existing.content);
|
|
31613
|
-
} else {
|
|
31614
|
-
decrypted = await nip44SelfDecrypt(signer, existing.content);
|
|
31615
|
-
}
|
|
31616
|
-
const parsed = JSON.parse(decrypted);
|
|
31617
|
-
entries = Array.isArray(parsed) ? parsed : [];
|
|
32416
|
+
entries = await decryptListEntries(signer, userPubkey, existing.content);
|
|
31618
32417
|
} catch {
|
|
31619
32418
|
entries = [];
|
|
31620
32419
|
}
|
|
@@ -31627,15 +32426,7 @@ async function appendToMyFormsList(formPubkey, formId, relay, signingKeyHex, vie
|
|
|
31627
32426
|
const secrets = signingKeyHex ? encodeFormKeys(signingKeyHex, viewKeyHex) : "";
|
|
31628
32427
|
entries.push(["f", coordKey, relay, secrets]);
|
|
31629
32428
|
}
|
|
31630
|
-
|
|
31631
|
-
const listEvent = {
|
|
31632
|
-
kind: FORM_KINDS.myFormsList,
|
|
31633
|
-
created_at: Math.floor(Date.now() / 1e3),
|
|
31634
|
-
tags: [],
|
|
31635
|
-
content: encrypted
|
|
31636
|
-
};
|
|
31637
|
-
const signed = await signer.signEvent(listEvent);
|
|
31638
|
-
await nostrRuntime.publish(relays, signed);
|
|
32429
|
+
await publishMyFormsList(signer, relays, entries);
|
|
31639
32430
|
}
|
|
31640
32431
|
async function saveToMyForms(summaries) {
|
|
31641
32432
|
const signer = await signerManager.getSigner();
|
|
@@ -31643,18 +32434,10 @@ async function saveToMyForms(summaries) {
|
|
|
31643
32434
|
const entries = summaries.map((s) => [
|
|
31644
32435
|
"f",
|
|
31645
32436
|
`${s.pubkey}:${s.id}`,
|
|
31646
|
-
"",
|
|
32437
|
+
s.relay ?? "",
|
|
31647
32438
|
s.signingKey ? encodeFormKeys(s.signingKey, s.viewKey) : ""
|
|
31648
32439
|
]);
|
|
31649
|
-
|
|
31650
|
-
const event = {
|
|
31651
|
-
kind: FORM_KINDS.myFormsList,
|
|
31652
|
-
created_at: Math.floor(Date.now() / 1e3),
|
|
31653
|
-
tags: [],
|
|
31654
|
-
content: encrypted
|
|
31655
|
-
};
|
|
31656
|
-
const signed = await signer.signEvent(event);
|
|
31657
|
-
await nostrRuntime.publish(relays, signed);
|
|
32440
|
+
await publishMyFormsList(signer, relays, entries);
|
|
31658
32441
|
}
|
|
31659
32442
|
async function fetchMyFormsByAuthor(pubkey, relays) {
|
|
31660
32443
|
const filter = {
|
|
@@ -31677,6 +32460,8 @@ async function fetchMyFormsByAuthor(pubkey, relays) {
|
|
|
31677
32460
|
}
|
|
31678
32461
|
async function deleteForm(formId, formPubkey) {
|
|
31679
32462
|
const signer = await signerManager.getSigner();
|
|
32463
|
+
const userPubkey = await signer.getPublicKey();
|
|
32464
|
+
const relays = relayManager.getRelaysForModule("forms");
|
|
31680
32465
|
const coordinate = `${FORM_KINDS.template}:${formPubkey}:${formId}`;
|
|
31681
32466
|
const event = {
|
|
31682
32467
|
kind: 5,
|
|
@@ -31687,58 +32472,60 @@ async function deleteForm(formId, formPubkey) {
|
|
|
31687
32472
|
],
|
|
31688
32473
|
content: "Deleted via Formstr"
|
|
31689
32474
|
};
|
|
31690
|
-
|
|
31691
|
-
const
|
|
31692
|
-
|
|
32475
|
+
await nostrRuntime.publish(relays, await signer.signEvent(event));
|
|
32476
|
+
const listEvent = await fetchLatestMyFormsEvent(relays, userPubkey);
|
|
32477
|
+
if (!listEvent?.content) return;
|
|
32478
|
+
let entries;
|
|
32479
|
+
try {
|
|
32480
|
+
entries = await decryptListEntries(signer, userPubkey, listEvent.content);
|
|
32481
|
+
} catch {
|
|
32482
|
+
return;
|
|
32483
|
+
}
|
|
32484
|
+
const coordKey = `${formPubkey}:${formId}`;
|
|
32485
|
+
const trimmed = entries.filter((e) => e[1] !== coordKey);
|
|
32486
|
+
if (trimmed.length === entries.length) return;
|
|
32487
|
+
await publishMyFormsList(signer, relays, trimmed);
|
|
31693
32488
|
}
|
|
31694
32489
|
async function updateForm(params) {
|
|
31695
32490
|
const relays = relayManager.getRelaysForModule("forms");
|
|
31696
|
-
const
|
|
32491
|
+
const summary = (await fetchMyForms()).find(
|
|
32492
|
+
(f) => f.id === params.formId && f.pubkey === params.pubkey
|
|
32493
|
+
);
|
|
32494
|
+
const existing = await fetchForm(params.pubkey, params.formId, summary?.viewKey);
|
|
31697
32495
|
if (!existing) throw new Error(`Form not found: ${params.formId}`);
|
|
31698
32496
|
const name = params.name ?? existing.name;
|
|
31699
32497
|
const settings = { ...existing.settings, ...params.settings };
|
|
31700
32498
|
const fields = params.fields ?? existing.fields;
|
|
32499
|
+
const specRows = buildSpecRows(params.formId, name, fields, settings);
|
|
31701
32500
|
if (existing.isEncrypted) {
|
|
31702
|
-
const summary = (await fetchMyForms()).find(
|
|
31703
|
-
(f) => f.id === params.formId && f.pubkey === params.pubkey
|
|
31704
|
-
);
|
|
31705
32501
|
if (!summary?.signingKey || !summary?.viewKey) {
|
|
31706
32502
|
throw new Error("Not the form owner or signing key unavailable");
|
|
31707
32503
|
}
|
|
31708
32504
|
const signingKeyBytes = hexToBytes3(summary.signingKey);
|
|
31709
32505
|
const formSigner = makeSigningKeySigner(summary.signingKey);
|
|
31710
32506
|
const viewPubkey = getPublicKey(hexToBytes3(summary.viewKey));
|
|
31711
|
-
const
|
|
31712
|
-
const content = await formSigner.nip44Encrypt(viewPubkey, JSON.stringify(fieldTags));
|
|
31713
|
-
const tags2 = [
|
|
31714
|
-
["d", params.formId],
|
|
31715
|
-
["name", name],
|
|
31716
|
-
["encryption", "view-key"],
|
|
31717
|
-
["settings", JSON.stringify(settings)]
|
|
31718
|
-
];
|
|
32507
|
+
const content = await formSigner.nip44Encrypt(viewPubkey, JSON.stringify(specRows));
|
|
31719
32508
|
const event2 = {
|
|
31720
32509
|
kind: FORM_KINDS.template,
|
|
31721
32510
|
created_at: Math.floor(Date.now() / 1e3),
|
|
31722
|
-
tags:
|
|
32511
|
+
tags: buildEncryptedOuterTags(params.formId, name, relays, settings),
|
|
31723
32512
|
content
|
|
31724
32513
|
};
|
|
31725
32514
|
await nostrRuntime.publish(relays, finalizeEvent(event2, signingKeyBytes));
|
|
31726
32515
|
return;
|
|
31727
32516
|
}
|
|
31728
|
-
const
|
|
31729
|
-
const tags = [
|
|
31730
|
-
["d", params.formId],
|
|
31731
|
-
["name", name],
|
|
31732
|
-
["settings", JSON.stringify(settings)]
|
|
31733
|
-
];
|
|
31734
|
-
for (const field of fields) tags.push(buildFieldTag(field));
|
|
31735
|
-
if (settings.publicForm) tags.push(["t", "public"]);
|
|
32517
|
+
const tags = [...specRows, ["t", "public"], ...relays.map((r) => ["relay", r])];
|
|
31736
32518
|
const event = {
|
|
31737
32519
|
kind: FORM_KINDS.template,
|
|
31738
32520
|
created_at: Math.floor(Date.now() / 1e3),
|
|
31739
32521
|
tags,
|
|
31740
32522
|
content: ""
|
|
31741
32523
|
};
|
|
32524
|
+
if (summary?.signingKey) {
|
|
32525
|
+
await nostrRuntime.publish(relays, finalizeEvent(event, hexToBytes3(summary.signingKey)));
|
|
32526
|
+
return;
|
|
32527
|
+
}
|
|
32528
|
+
const signer = await signerManager.getSigner();
|
|
31742
32529
|
await nostrRuntime.publish(relays, await signer.signEvent(event));
|
|
31743
32530
|
}
|
|
31744
32531
|
async function shareForm(params) {
|
|
@@ -31746,27 +32533,50 @@ async function shareForm(params) {
|
|
|
31746
32533
|
const summary = (await fetchMyForms()).find(
|
|
31747
32534
|
(f) => f.id === params.formId && f.pubkey === params.formPubkey
|
|
31748
32535
|
);
|
|
31749
|
-
if (!summary?.
|
|
31750
|
-
throw new Error("Not the form owner or
|
|
31751
|
-
}
|
|
31752
|
-
const
|
|
31753
|
-
const
|
|
31754
|
-
const
|
|
31755
|
-
const
|
|
31756
|
-
|
|
31757
|
-
|
|
31758
|
-
tags: [
|
|
31759
|
-
["a", coordinate],
|
|
31760
|
-
["viewKey", summary.viewKey]
|
|
31761
|
-
],
|
|
31762
|
-
content: `Formstr form view key for ${naddr}`
|
|
31763
|
-
};
|
|
32536
|
+
if (!summary?.signingKey) {
|
|
32537
|
+
throw new Error("Not the form owner or form keys unavailable");
|
|
32538
|
+
}
|
|
32539
|
+
const signingKeyBytes = hexToBytes3(summary.signingKey);
|
|
32540
|
+
const signingPubkey = getPublicKey(signingKeyBytes);
|
|
32541
|
+
const formSigner = new LocalSigner2(signingKeyBytes);
|
|
32542
|
+
const now2 = () => Math.round(Date.now() / 1e3);
|
|
32543
|
+
const editors = new Set(params.editors ?? []);
|
|
32544
|
+
const targets = [.../* @__PURE__ */ new Set([...params.recipients, ...editors])];
|
|
31764
32545
|
const failed = [];
|
|
31765
32546
|
let published = 0;
|
|
31766
|
-
for (const recipient of
|
|
32547
|
+
for (const recipient of targets) {
|
|
31767
32548
|
try {
|
|
31768
|
-
const
|
|
31769
|
-
|
|
32549
|
+
const accessTags = [];
|
|
32550
|
+
if (editors.has(recipient)) accessTags.push(["EditAccess", summary.signingKey]);
|
|
32551
|
+
if (summary.viewKey) accessTags.push(["ViewAccess", summary.viewKey]);
|
|
32552
|
+
const rumor = {
|
|
32553
|
+
kind: 18,
|
|
32554
|
+
pubkey: signingPubkey,
|
|
32555
|
+
created_at: now2(),
|
|
32556
|
+
content: "",
|
|
32557
|
+
tags: accessTags
|
|
32558
|
+
};
|
|
32559
|
+
rumor.id = getEventHash(rumor);
|
|
32560
|
+
const seal = finalizeEvent(
|
|
32561
|
+
{
|
|
32562
|
+
kind: 13,
|
|
32563
|
+
content: await formSigner.nip44Encrypt(recipient, JSON.stringify(rumor)),
|
|
32564
|
+
created_at: now2(),
|
|
32565
|
+
tags: []
|
|
32566
|
+
},
|
|
32567
|
+
signingKeyBytes
|
|
32568
|
+
);
|
|
32569
|
+
const randomKey = generateSecretKey();
|
|
32570
|
+
const wrap = finalizeEvent(
|
|
32571
|
+
{
|
|
32572
|
+
kind: 1059,
|
|
32573
|
+
content: await new LocalSigner2(randomKey).nip44Encrypt(recipient, JSON.stringify(seal)),
|
|
32574
|
+
created_at: now2(),
|
|
32575
|
+
tags: [["p", accessGrantAlias(signingPubkey, params.formId, recipient)]]
|
|
32576
|
+
},
|
|
32577
|
+
randomKey
|
|
32578
|
+
);
|
|
32579
|
+
await nostrRuntime.publish(relays, wrap);
|
|
31770
32580
|
published++;
|
|
31771
32581
|
} catch {
|
|
31772
32582
|
failed.push(recipient);
|
|
@@ -31790,32 +32600,32 @@ async function importForm(summary) {
|
|
|
31790
32600
|
if (current.some((f) => f.id === summary.id && f.pubkey === summary.pubkey)) return;
|
|
31791
32601
|
await saveToMyForms([...current, summary]);
|
|
31792
32602
|
}
|
|
31793
|
-
function
|
|
31794
|
-
const
|
|
31795
|
-
const
|
|
31796
|
-
|
|
31797
|
-
|
|
31798
|
-
function parseFormEvent(event) {
|
|
31799
|
-
const dTag = event.tags.find((t) => t[0] === "d")?.[1] ?? "";
|
|
31800
|
-
const nameTag = event.tags.find((t) => t[0] === "name")?.[1] ?? "Untitled";
|
|
31801
|
-
const settingsTag = event.tags.find((t) => t[0] === "settings")?.[1];
|
|
32603
|
+
function parseFormEvent(event, decryptedRows) {
|
|
32604
|
+
const merged = decryptedRows ? [...event.tags, ...decryptedRows] : event.tags;
|
|
32605
|
+
const dTag = merged.find((t) => t[0] === "d")?.[1] ?? "";
|
|
32606
|
+
const nameTag = merged.find((t) => t[0] === "name")?.[1] ?? "Untitled";
|
|
32607
|
+
const settingsTag = merged.find((t) => t[0] === "settings")?.[1];
|
|
31802
32608
|
const encTag = event.tags.find((t) => t[0] === "encryption")?.[1];
|
|
31803
|
-
const
|
|
31804
|
-
|
|
31805
|
-
|
|
31806
|
-
|
|
31807
|
-
|
|
31808
|
-
|
|
31809
|
-
|
|
31810
|
-
|
|
32609
|
+
const relays = event.tags.filter((t) => t[0] === "relay" && t[1]).map((t) => t[1]);
|
|
32610
|
+
const fields = merged.filter((t) => t[0] === "field").map(parseFieldTag);
|
|
32611
|
+
const outerHasFields = event.tags.some((t) => t[0] === "field");
|
|
32612
|
+
const isEncrypted = encTag != null ? encTag === "view-key" : event.content.length > 0 && !outerHasFields;
|
|
32613
|
+
let settings = {};
|
|
32614
|
+
if (settingsTag) {
|
|
32615
|
+
try {
|
|
32616
|
+
settings = JSON.parse(settingsTag);
|
|
32617
|
+
} catch {
|
|
32618
|
+
}
|
|
32619
|
+
}
|
|
31811
32620
|
return {
|
|
31812
32621
|
id: dTag,
|
|
31813
32622
|
name: nameTag,
|
|
31814
32623
|
fields,
|
|
31815
|
-
settings
|
|
32624
|
+
settings,
|
|
31816
32625
|
pubkey: event.pubkey,
|
|
31817
32626
|
createdAt: event.created_at,
|
|
31818
32627
|
isEncrypted,
|
|
32628
|
+
...relays.length > 0 && { relays },
|
|
31819
32629
|
event
|
|
31820
32630
|
};
|
|
31821
32631
|
}
|
|
@@ -31831,15 +32641,6 @@ function parseResponseEvent(event) {
|
|
|
31831
32641
|
event
|
|
31832
32642
|
};
|
|
31833
32643
|
}
|
|
31834
|
-
function safeParseOptions(json) {
|
|
31835
|
-
try {
|
|
31836
|
-
const arr = JSON.parse(json);
|
|
31837
|
-
if (!Array.isArray(arr)) return void 0;
|
|
31838
|
-
return arr.map((o) => ({ id: o[0], label: o[1] }));
|
|
31839
|
-
} catch {
|
|
31840
|
-
return void 0;
|
|
31841
|
-
}
|
|
31842
|
-
}
|
|
31843
32644
|
|
|
31844
32645
|
// ../agent/src/services/calendar/service.ts
|
|
31845
32646
|
var service_exports2 = {};
|
|
@@ -31854,9 +32655,14 @@ __export(service_exports2, {
|
|
|
31854
32655
|
fetchCalendarLists: () => fetchCalendarLists,
|
|
31855
32656
|
fetchDeletions: () => fetchDeletions,
|
|
31856
32657
|
fetchInvitationsSync: () => fetchInvitationsSync,
|
|
32658
|
+
fetchParticipantRemovals: () => fetchParticipantRemovals,
|
|
32659
|
+
fetchRelayListsForPubkeys: () => fetchRelayListsForPubkeys,
|
|
32660
|
+
getInvitationInboxRelays: () => getInvitationInboxRelays,
|
|
31857
32661
|
isEventDeleted: () => isEventDeleted,
|
|
32662
|
+
lookupEventViewKey: () => lookupEventViewKey,
|
|
31858
32663
|
moveEventBetweenCalendarLists: () => moveEventBetweenCalendarLists,
|
|
31859
32664
|
parseCalendarEvent: () => parseCalendarEvent,
|
|
32665
|
+
publishParticipantRemovalEvent: () => publishParticipantRemovalEvent,
|
|
31860
32666
|
publishPrivateCalendarEvent: () => publishPrivateCalendarEvent,
|
|
31861
32667
|
publishPublicCalendarEvent: () => publishPublicCalendarEvent,
|
|
31862
32668
|
removeEventFromCalendarList: () => removeEventFromCalendarList,
|
|
@@ -31873,6 +32679,7 @@ function encodeCalendarList(list) {
|
|
|
31873
32679
|
["content", list.description ?? ""],
|
|
31874
32680
|
["color", list.color]
|
|
31875
32681
|
];
|
|
32682
|
+
if (list.notificationPreference === "disabled") tags.push(["notifications", "disabled"]);
|
|
31876
32683
|
for (const ref of list.eventRefs) tags.push(["a", ...ref]);
|
|
31877
32684
|
return tags;
|
|
31878
32685
|
}
|
|
@@ -31880,6 +32687,7 @@ function decodeCalendarList(tags, dTag, eventId) {
|
|
|
31880
32687
|
let title = DEFAULT_TITLE;
|
|
31881
32688
|
let description = "";
|
|
31882
32689
|
let color = DEFAULT_COLOR;
|
|
32690
|
+
let notificationPreference;
|
|
31883
32691
|
const eventRefs = [];
|
|
31884
32692
|
for (const tag of tags) {
|
|
31885
32693
|
if (!Array.isArray(tag) || tag.length === 0) continue;
|
|
@@ -31893,6 +32701,9 @@ function decodeCalendarList(tags, dTag, eventId) {
|
|
|
31893
32701
|
case "color":
|
|
31894
32702
|
color = tag[1] || DEFAULT_COLOR;
|
|
31895
32703
|
break;
|
|
32704
|
+
case "notifications":
|
|
32705
|
+
notificationPreference = tag[1] === "disabled" ? "disabled" : "enabled";
|
|
32706
|
+
break;
|
|
31896
32707
|
case "a":
|
|
31897
32708
|
if (tag[1] === "a") {
|
|
31898
32709
|
const coord = tag[2] ?? "";
|
|
@@ -31911,7 +32722,8 @@ function decodeCalendarList(tags, dTag, eventId) {
|
|
|
31911
32722
|
color,
|
|
31912
32723
|
eventRefs,
|
|
31913
32724
|
createdAt: 0,
|
|
31914
|
-
isVisible: true
|
|
32725
|
+
isVisible: true,
|
|
32726
|
+
notificationPreference
|
|
31915
32727
|
};
|
|
31916
32728
|
}
|
|
31917
32729
|
|
|
@@ -31936,6 +32748,8 @@ var CALENDAR_KINDS = {
|
|
|
31936
32748
|
rsvpGiftWrap: 1055,
|
|
31937
32749
|
rsvpRumor: 55,
|
|
31938
32750
|
participantRemoval: 84,
|
|
32751
|
+
/** Public free/busy list, one per (user, YYYY-MM month). */
|
|
32752
|
+
publicBusyList: 31926,
|
|
31939
32753
|
// Appointment scheduling (Calendly-style booking links).
|
|
31940
32754
|
schedulingPage: 31927,
|
|
31941
32755
|
schedulingPagesList: 32680,
|
|
@@ -32181,13 +32995,14 @@ async function publishPrivateCalendarEvent(draft, calendarId) {
|
|
|
32181
32995
|
const eventData = [
|
|
32182
32996
|
["title", draft.title],
|
|
32183
32997
|
["description", draft.description],
|
|
32184
|
-
["start",
|
|
32185
|
-
["end",
|
|
32998
|
+
["start", Math.floor(draft.begin.getTime() / 1e3)],
|
|
32999
|
+
["end", Math.floor(draft.end.getTime() / 1e3)],
|
|
32186
33000
|
["d", eventId]
|
|
32187
33001
|
];
|
|
32188
33002
|
if (draft.image) eventData.push(["image", draft.image]);
|
|
32189
33003
|
if (draft.location) eventData.push(["location", draft.location]);
|
|
32190
33004
|
for (const cat of draft.categories ?? []) eventData.push(["t", cat]);
|
|
33005
|
+
eventData.push(["p", pubkey]);
|
|
32191
33006
|
for (const p of draft.participants ?? []) eventData.push(["p", p]);
|
|
32192
33007
|
if (draft.startTzid) eventData.push(["start_tzid", draft.startTzid]);
|
|
32193
33008
|
if (draft.endTzid) eventData.push(["end_tzid", draft.endTzid]);
|
|
@@ -32195,7 +33010,12 @@ async function publishPrivateCalendarEvent(draft, calendarId) {
|
|
|
32195
33010
|
eventData.push(["L", "rrule"]);
|
|
32196
33011
|
eventData.push(["l", draft.rrule]);
|
|
32197
33012
|
}
|
|
32198
|
-
if (draft.
|
|
33013
|
+
if (draft.notificationPreference) eventData.push(["notification", draft.notificationPreference]);
|
|
33014
|
+
if (draft.registrationFormRef) {
|
|
33015
|
+
eventData.push(
|
|
33016
|
+
draft.registrationFormViewKey ? ["form", draft.registrationFormRef, draft.registrationFormViewKey] : ["form", draft.registrationFormRef]
|
|
33017
|
+
);
|
|
33018
|
+
}
|
|
32199
33019
|
const viewKeyNsec = draft.viewKey ?? generateViewKey().nsec;
|
|
32200
33020
|
const content = await encryptWithViewKey(viewKeyNsec, JSON.stringify(eventData));
|
|
32201
33021
|
const event = {
|
|
@@ -32210,6 +33030,7 @@ async function publishPrivateCalendarEvent(draft, calendarId) {
|
|
|
32210
33030
|
const relayHint = relays[0] ?? "";
|
|
32211
33031
|
const coordinate = `${CALENDAR_KINDS.privateEvent}:${pubkey}:${eventId}`;
|
|
32212
33032
|
if (draft.participants?.length) {
|
|
33033
|
+
const relayLists = await fetchRelayListsForPubkeys(draft.participants);
|
|
32213
33034
|
for (const participant of draft.participants) {
|
|
32214
33035
|
const wrap = await wrapEvent3(
|
|
32215
33036
|
{
|
|
@@ -32224,7 +33045,7 @@ async function publishPrivateCalendarEvent(draft, calendarId) {
|
|
|
32224
33045
|
participant,
|
|
32225
33046
|
CALENDAR_KINDS.giftWrap
|
|
32226
33047
|
);
|
|
32227
|
-
await nostrRuntime.publish(relays, wrap);
|
|
33048
|
+
await nostrRuntime.publish(relayLists.get(participant) ?? relays, wrap);
|
|
32228
33049
|
}
|
|
32229
33050
|
}
|
|
32230
33051
|
return {
|
|
@@ -32250,9 +33071,37 @@ async function publishPrivateCalendarEvent(draft, calendarId) {
|
|
|
32250
33071
|
startTzid: draft.startTzid,
|
|
32251
33072
|
endTzid: draft.endTzid,
|
|
32252
33073
|
registrationFormRef: draft.registrationFormRef,
|
|
33074
|
+
registrationFormViewKey: draft.registrationFormViewKey,
|
|
33075
|
+
notificationPreference: draft.notificationPreference,
|
|
32253
33076
|
event: signed
|
|
32254
33077
|
};
|
|
32255
33078
|
}
|
|
33079
|
+
async function fetchRelayListsForPubkeys(pubkeys) {
|
|
33080
|
+
const result = /* @__PURE__ */ new Map();
|
|
33081
|
+
if (pubkeys.length === 0) return result;
|
|
33082
|
+
const relays = relayManager.getRelaysForModule("calendar");
|
|
33083
|
+
const events = await nostrRuntime.querySync(relays, { kinds: [10002], authors: pubkeys });
|
|
33084
|
+
const newest = /* @__PURE__ */ new Map();
|
|
33085
|
+
for (const event of events) {
|
|
33086
|
+
const prev = newest.get(event.pubkey);
|
|
33087
|
+
if (!prev || event.created_at > prev.created_at) newest.set(event.pubkey, event);
|
|
33088
|
+
}
|
|
33089
|
+
for (const [pubkey, event] of newest) {
|
|
33090
|
+
const urls = event.tags.filter((t) => t[0] === "r" && t[1]).map((t) => t[1]);
|
|
33091
|
+
if (urls.length > 0) result.set(pubkey, urls);
|
|
33092
|
+
}
|
|
33093
|
+
return result;
|
|
33094
|
+
}
|
|
33095
|
+
async function getInvitationInboxRelays(pubkey) {
|
|
33096
|
+
const relays = relayManager.getRelaysForModule("calendar");
|
|
33097
|
+
try {
|
|
33098
|
+
const configs = await relayManager.fetchUserRelays(pubkey);
|
|
33099
|
+
const readRelays = configs.filter((c) => c.read).map((c) => c.url);
|
|
33100
|
+
return [.../* @__PURE__ */ new Set([...relays, ...readRelays])];
|
|
33101
|
+
} catch {
|
|
33102
|
+
return relays;
|
|
33103
|
+
}
|
|
33104
|
+
}
|
|
32256
33105
|
function subscribeToCalendarEvents(params, onEvent, onEose) {
|
|
32257
33106
|
const relays = relayManager.getRelaysForModule("calendar");
|
|
32258
33107
|
const filter = {
|
|
@@ -32539,6 +33388,42 @@ async function deleteCalendarList(coordinate) {
|
|
|
32539
33388
|
const relays = relayManager.getRelaysForModule("calendar");
|
|
32540
33389
|
await nostrRuntime.publish(relays, signed);
|
|
32541
33390
|
}
|
|
33391
|
+
async function publishParticipantRemovalEvent({
|
|
33392
|
+
kinds,
|
|
33393
|
+
eventIds = [],
|
|
33394
|
+
coordinates = [],
|
|
33395
|
+
reason = ""
|
|
33396
|
+
}) {
|
|
33397
|
+
const signer = await signerManager.getSigner();
|
|
33398
|
+
const tags = [];
|
|
33399
|
+
for (const id of eventIds) tags.push(["e", id]);
|
|
33400
|
+
for (const coord of coordinates) tags.push(["a", coord]);
|
|
33401
|
+
for (const kind of kinds) tags.push(["k", String(kind)]);
|
|
33402
|
+
const event = {
|
|
33403
|
+
kind: CALENDAR_KINDS.participantRemoval,
|
|
33404
|
+
created_at: Math.floor(Date.now() / 1e3),
|
|
33405
|
+
tags,
|
|
33406
|
+
content: reason
|
|
33407
|
+
};
|
|
33408
|
+
const signed = await signer.signEvent(event);
|
|
33409
|
+
const relays = relayManager.getRelaysForModule("calendar");
|
|
33410
|
+
await nostrRuntime.publish(relays, signed);
|
|
33411
|
+
}
|
|
33412
|
+
async function fetchParticipantRemovals(pubkey, relays = relayManager.getRelaysForModule("calendar")) {
|
|
33413
|
+
const ids = /* @__PURE__ */ new Set();
|
|
33414
|
+
const coordinates = /* @__PURE__ */ new Set();
|
|
33415
|
+
const events = await nostrRuntime.querySync(relays, {
|
|
33416
|
+
kinds: [CALENDAR_KINDS.participantRemoval],
|
|
33417
|
+
authors: [pubkey]
|
|
33418
|
+
});
|
|
33419
|
+
for (const ev of events) {
|
|
33420
|
+
for (const tag of ev.tags) {
|
|
33421
|
+
if (tag[0] === "e" && tag[1]) ids.add(tag[1]);
|
|
33422
|
+
else if (tag[0] === "a" && tag[1]) coordinates.add(tag[1]);
|
|
33423
|
+
}
|
|
33424
|
+
}
|
|
33425
|
+
return { ids, coordinates };
|
|
33426
|
+
}
|
|
32542
33427
|
async function parseCalendarEvent(event, viewKey) {
|
|
32543
33428
|
const dTag = event.tags.find((t) => t[0] === "d")?.[1] ?? "";
|
|
32544
33429
|
const isPrivate = event.kind !== CALENDAR_KINDS.publicEvent;
|
|
@@ -32566,7 +33451,10 @@ async function parseCalendarEvent(event, viewKey) {
|
|
|
32566
33451
|
const rrule = tags.find((t) => t[0] === "l" && t[2] === "rrule")?.[1] ?? (hasRruleLabel ? tags.find((t) => t[0] === "l")?.[1] : void 0) ?? tags.find((t) => t[0] === "rrule")?.[1] ?? null;
|
|
32567
33452
|
const startTzid = tags.find((t) => t[0] === "start_tzid")?.[1];
|
|
32568
33453
|
const endTzid = tags.find((t) => t[0] === "end_tzid")?.[1];
|
|
32569
|
-
const
|
|
33454
|
+
const formTag = tags.find((t) => t[0] === "form");
|
|
33455
|
+
const registrationFormRef = formTag?.[1];
|
|
33456
|
+
const registrationFormViewKey = formTag?.[2];
|
|
33457
|
+
const notificationPreference = tags.find((t) => t[0] === "notification")?.[1];
|
|
32570
33458
|
return {
|
|
32571
33459
|
id: dTag,
|
|
32572
33460
|
eventId: event.id,
|
|
@@ -32588,13 +33476,16 @@ async function parseCalendarEvent(event, viewKey) {
|
|
|
32588
33476
|
startTzid,
|
|
32589
33477
|
endTzid,
|
|
32590
33478
|
registrationFormRef,
|
|
33479
|
+
registrationFormViewKey,
|
|
33480
|
+
notificationPreference,
|
|
32591
33481
|
event
|
|
32592
33482
|
};
|
|
32593
33483
|
}
|
|
32594
33484
|
async function fetchInvitationsSync() {
|
|
32595
33485
|
const signer = await signerManager.getSigner();
|
|
32596
33486
|
const pubkey = await signer.getPublicKey();
|
|
32597
|
-
const relays =
|
|
33487
|
+
const relays = await getInvitationInboxRelays(pubkey);
|
|
33488
|
+
const removals = await fetchParticipantRemovals(pubkey, relays);
|
|
32598
33489
|
const wraps = await nostrRuntime.querySync(relays, {
|
|
32599
33490
|
kinds: [CALENDAR_KINDS.giftWrap, CALENDAR_KINDS.rsvpGiftWrap],
|
|
32600
33491
|
"#p": [pubkey]
|
|
@@ -32602,7 +33493,7 @@ async function fetchInvitationsSync() {
|
|
|
32602
33493
|
const seen = /* @__PURE__ */ new Set();
|
|
32603
33494
|
const out = [];
|
|
32604
33495
|
for (const wrap of wraps) {
|
|
32605
|
-
if (seen.has(wrap.id)) continue;
|
|
33496
|
+
if (seen.has(wrap.id) || removals.ids.has(wrap.id)) continue;
|
|
32606
33497
|
seen.add(wrap.id);
|
|
32607
33498
|
const invitation = await extractInvitationFromWrap(wrap);
|
|
32608
33499
|
if (!invitation) continue;
|
|
@@ -32614,6 +33505,15 @@ async function fetchInvitationsSync() {
|
|
|
32614
33505
|
}
|
|
32615
33506
|
return out;
|
|
32616
33507
|
}
|
|
33508
|
+
async function lookupEventViewKey(eventCoordinate) {
|
|
33509
|
+
const lists = await fetchCalendarLists();
|
|
33510
|
+
for (const list of lists) {
|
|
33511
|
+
for (const ref of list.eventRefs) {
|
|
33512
|
+
if (ref[0] === eventCoordinate && ref[2]) return ref[2];
|
|
33513
|
+
}
|
|
33514
|
+
}
|
|
33515
|
+
return void 0;
|
|
33516
|
+
}
|
|
32617
33517
|
|
|
32618
33518
|
// ../agent/src/services/calendar/booking.ts
|
|
32619
33519
|
var booking_exports = {};
|
|
@@ -32624,6 +33524,122 @@ __export(booking_exports, {
|
|
|
32624
33524
|
fetchBookingRequests: () => fetchBookingRequests,
|
|
32625
33525
|
fetchSchedulingPages: () => fetchSchedulingPages
|
|
32626
33526
|
});
|
|
33527
|
+
|
|
33528
|
+
// ../agent/src/services/calendar/busyList.ts
|
|
33529
|
+
var MONTH_KEY_RE = /^\d{4}-\d{2}$/;
|
|
33530
|
+
function busyListMonthKey(value) {
|
|
33531
|
+
const d = typeof value === "number" ? new Date(value) : value;
|
|
33532
|
+
const month = String(d.getUTCMonth() + 1).padStart(2, "0");
|
|
33533
|
+
return `${d.getUTCFullYear()}-${month}`;
|
|
33534
|
+
}
|
|
33535
|
+
function busyListMonthKeysForRange(startMs, endMs) {
|
|
33536
|
+
if (!Number.isFinite(startMs) || !Number.isFinite(endMs)) return [];
|
|
33537
|
+
const start = Math.min(startMs, endMs);
|
|
33538
|
+
const end = Math.max(startMs, endMs);
|
|
33539
|
+
const keys = [];
|
|
33540
|
+
const cursor = new Date(
|
|
33541
|
+
Date.UTC(new Date(start).getUTCFullYear(), new Date(start).getUTCMonth(), 1)
|
|
33542
|
+
);
|
|
33543
|
+
const endMonth = new Date(
|
|
33544
|
+
Date.UTC(new Date(end).getUTCFullYear(), new Date(end).getUTCMonth(), 1)
|
|
33545
|
+
);
|
|
33546
|
+
while (cursor.getTime() <= endMonth.getTime()) {
|
|
33547
|
+
keys.push(busyListMonthKey(cursor));
|
|
33548
|
+
cursor.setUTCMonth(cursor.getUTCMonth() + 1);
|
|
33549
|
+
}
|
|
33550
|
+
return keys;
|
|
33551
|
+
}
|
|
33552
|
+
function busyListToTags(list) {
|
|
33553
|
+
const tags = [
|
|
33554
|
+
["d", list.monthKey],
|
|
33555
|
+
["t", list.monthKey],
|
|
33556
|
+
["t", "busy"]
|
|
33557
|
+
];
|
|
33558
|
+
for (const r of list.ranges) {
|
|
33559
|
+
tags.push(["block", String(Math.floor(r.start / 1e3)), String(Math.floor(r.end / 1e3))]);
|
|
33560
|
+
}
|
|
33561
|
+
return tags;
|
|
33562
|
+
}
|
|
33563
|
+
function parseBusyListEvent(event) {
|
|
33564
|
+
const dTag = event.tags.find((t) => t[0] === "d")?.[1] ?? "";
|
|
33565
|
+
if (!MONTH_KEY_RE.test(dTag)) return null;
|
|
33566
|
+
const ranges = [];
|
|
33567
|
+
for (const tag of event.tags) {
|
|
33568
|
+
if (tag[0] !== "block") continue;
|
|
33569
|
+
const start = Number(tag[1]) * 1e3;
|
|
33570
|
+
const end = Number(tag[2]) * 1e3;
|
|
33571
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) continue;
|
|
33572
|
+
ranges.push({ start, end });
|
|
33573
|
+
}
|
|
33574
|
+
ranges.sort((a, b) => a.start - b.start || a.end - b.end);
|
|
33575
|
+
const deduped = [];
|
|
33576
|
+
for (const r of ranges) {
|
|
33577
|
+
const last = deduped[deduped.length - 1];
|
|
33578
|
+
if (last && last.start === r.start && last.end === r.end) continue;
|
|
33579
|
+
deduped.push(r);
|
|
33580
|
+
}
|
|
33581
|
+
return {
|
|
33582
|
+
user: event.pubkey,
|
|
33583
|
+
monthKey: dTag,
|
|
33584
|
+
ranges: deduped,
|
|
33585
|
+
eventId: event.id,
|
|
33586
|
+
createdAt: event.created_at
|
|
33587
|
+
};
|
|
33588
|
+
}
|
|
33589
|
+
async function fetchBusyListsForUser(pubkey, monthKeys) {
|
|
33590
|
+
if (monthKeys.length === 0) return [];
|
|
33591
|
+
const relays = relayManager.getRelaysForModule("calendar");
|
|
33592
|
+
const events = await nostrRuntime.querySync(relays, {
|
|
33593
|
+
kinds: [CALENDAR_KINDS.publicBusyList],
|
|
33594
|
+
authors: [pubkey],
|
|
33595
|
+
"#d": monthKeys
|
|
33596
|
+
});
|
|
33597
|
+
const newestPerMonth = /* @__PURE__ */ new Map();
|
|
33598
|
+
for (const event of events) {
|
|
33599
|
+
const list = parseBusyListEvent(event);
|
|
33600
|
+
if (!list) continue;
|
|
33601
|
+
const prev = newestPerMonth.get(list.monthKey);
|
|
33602
|
+
if (!prev || list.createdAt > prev.createdAt) newestPerMonth.set(list.monthKey, list);
|
|
33603
|
+
}
|
|
33604
|
+
return [...newestPerMonth.values()];
|
|
33605
|
+
}
|
|
33606
|
+
async function publishBusyList(list) {
|
|
33607
|
+
const signer = await signerManager.getSigner();
|
|
33608
|
+
const event = {
|
|
33609
|
+
kind: CALENDAR_KINDS.publicBusyList,
|
|
33610
|
+
created_at: Math.floor(Date.now() / 1e3),
|
|
33611
|
+
tags: busyListToTags(list),
|
|
33612
|
+
content: ""
|
|
33613
|
+
};
|
|
33614
|
+
const signed = await signer.signEvent(event);
|
|
33615
|
+
const relays = relayManager.getRelaysForModule("calendar");
|
|
33616
|
+
await nostrRuntime.publish(relays, signed);
|
|
33617
|
+
}
|
|
33618
|
+
var sameRange = (a, b) => a.start === b.start && a.end === b.end;
|
|
33619
|
+
async function addBusyRange(range) {
|
|
33620
|
+
const signer = await signerManager.getSigner();
|
|
33621
|
+
const pubkey = await signer.getPublicKey();
|
|
33622
|
+
const monthKeys = busyListMonthKeysForRange(range.start, range.end);
|
|
33623
|
+
const existing = new Map(
|
|
33624
|
+
(await fetchBusyListsForUser(pubkey, monthKeys)).map((l) => [l.monthKey, l])
|
|
33625
|
+
);
|
|
33626
|
+
for (const monthKey of monthKeys) {
|
|
33627
|
+
const list = existing.get(monthKey) ?? {
|
|
33628
|
+
user: pubkey,
|
|
33629
|
+
monthKey,
|
|
33630
|
+
ranges: [],
|
|
33631
|
+
eventId: "",
|
|
33632
|
+
createdAt: 0
|
|
33633
|
+
};
|
|
33634
|
+
if (list.ranges.some((r) => sameRange(r, range))) continue;
|
|
33635
|
+
const ranges = [...list.ranges, { start: range.start, end: range.end }].sort(
|
|
33636
|
+
(a, b) => a.start - b.start || a.end - b.end
|
|
33637
|
+
);
|
|
33638
|
+
await publishBusyList({ ...list, ranges });
|
|
33639
|
+
}
|
|
33640
|
+
}
|
|
33641
|
+
|
|
33642
|
+
// ../agent/src/services/calendar/booking.ts
|
|
32627
33643
|
var BOOKING_HOST = "https://calendar.formstr.app";
|
|
32628
33644
|
async function fetchSchedulingPages() {
|
|
32629
33645
|
const signer = await signerManager.getSigner();
|
|
@@ -32782,6 +33798,10 @@ async function approveBookingRequest(request, calendar) {
|
|
|
32782
33798
|
event.viewKey ?? request.viewKey ?? ""
|
|
32783
33799
|
];
|
|
32784
33800
|
const updatedCalendar = await addEventToCalendarList(calendar, eventRef);
|
|
33801
|
+
try {
|
|
33802
|
+
await addBusyRange({ start: request.start, end: request.end });
|
|
33803
|
+
} catch {
|
|
33804
|
+
}
|
|
32785
33805
|
await sendBookingResponse({
|
|
32786
33806
|
schedulingPageRef: request.schedulingPageRef,
|
|
32787
33807
|
bookerPubkey: request.bookerPubkey,
|
|
@@ -32809,7 +33829,9 @@ var service_exports3 = {};
|
|
|
32809
33829
|
__export(service_exports3, {
|
|
32810
33830
|
addSharedPage: () => addSharedPage,
|
|
32811
33831
|
deletePage: () => deletePage,
|
|
33832
|
+
fetchAllDocMetadata: () => fetchAllDocMetadata,
|
|
32812
33833
|
fetchDeletions: () => fetchDeletions2,
|
|
33834
|
+
fetchDocMetadata: () => fetchDocMetadata,
|
|
32813
33835
|
fetchDocTags: () => fetchDocTags,
|
|
32814
33836
|
fetchMyPages: () => fetchMyPages,
|
|
32815
33837
|
fetchPage: () => fetchPage,
|
|
@@ -32817,17 +33839,22 @@ __export(service_exports3, {
|
|
|
32817
33839
|
fetchSharedPages: () => fetchSharedPages,
|
|
32818
33840
|
generateShareLink: () => generateShareLink,
|
|
32819
33841
|
isPageDeleted: () => isPageDeleted,
|
|
33842
|
+
saveDocMetadata: () => saveDocMetadata,
|
|
32820
33843
|
savePage: () => savePage,
|
|
32821
|
-
|
|
33844
|
+
setDocSharedAs: () => setDocSharedAs,
|
|
32822
33845
|
setDocTags: () => setDocTags,
|
|
33846
|
+
setDocTitle: () => setDocTitle,
|
|
32823
33847
|
sharePage: () => sharePage
|
|
32824
33848
|
});
|
|
32825
33849
|
|
|
32826
33850
|
// ../agent/src/services/pages/types.ts
|
|
32827
33851
|
var PAGES_KINDS = {
|
|
32828
33852
|
document: 33457,
|
|
33853
|
+
/** Legacy super-app shared-with-me list — read-only migration source; upstream
|
|
33854
|
+
* (nostr-docs) has no such kind. Shares now live in kind-34579 doc metadata. */
|
|
32829
33855
|
sharedPagesList: 11234,
|
|
32830
33856
|
docMetadata: 34579,
|
|
33857
|
+
comment: 1494,
|
|
32831
33858
|
crdtOp: 22457
|
|
32832
33859
|
};
|
|
32833
33860
|
|
|
@@ -32894,6 +33921,7 @@ async function fetchMyPages(viewKeys) {
|
|
|
32894
33921
|
authors: [pubkey]
|
|
32895
33922
|
});
|
|
32896
33923
|
const deletions = await fetchDeletions2(relays, [pubkey]);
|
|
33924
|
+
const metadata = await fetchAllDocMetadata().catch(() => /* @__PURE__ */ new Map());
|
|
32897
33925
|
const newest = /* @__PURE__ */ new Map();
|
|
32898
33926
|
for (const ev of events) {
|
|
32899
33927
|
if (isPageDeleted(ev, deletions)) continue;
|
|
@@ -32904,15 +33932,18 @@ async function fetchMyPages(viewKeys) {
|
|
|
32904
33932
|
const summaries = [];
|
|
32905
33933
|
for (const [dTag, ev] of newest) {
|
|
32906
33934
|
const address = `${PAGES_KINDS.document}:${ev.pubkey}:${dTag}`;
|
|
32907
|
-
|
|
32908
|
-
|
|
32909
|
-
|
|
32910
|
-
|
|
32911
|
-
|
|
32912
|
-
|
|
32913
|
-
|
|
32914
|
-
|
|
32915
|
-
|
|
33935
|
+
const meta = metadata.get(address);
|
|
33936
|
+
const viewKey = meta?.viewKey ?? viewKeys?.get(address);
|
|
33937
|
+
let title = meta?.title || `Document ${dTag}`;
|
|
33938
|
+
if (!meta?.title) {
|
|
33939
|
+
try {
|
|
33940
|
+
title = extractTitle(await nip44SelfDecrypt(signer, ev.content));
|
|
33941
|
+
} catch {
|
|
33942
|
+
if (viewKey) {
|
|
33943
|
+
try {
|
|
33944
|
+
title = extractTitle(await decryptWithViewKey2(viewKey, ev.content));
|
|
33945
|
+
} catch {
|
|
33946
|
+
}
|
|
32916
33947
|
}
|
|
32917
33948
|
}
|
|
32918
33949
|
}
|
|
@@ -32923,7 +33954,9 @@ async function fetchMyPages(viewKeys) {
|
|
|
32923
33954
|
pubkey: ev.pubkey,
|
|
32924
33955
|
createdAt: ev.created_at,
|
|
32925
33956
|
isEncrypted: ev.content.length > 0,
|
|
32926
|
-
viewKey
|
|
33957
|
+
viewKey,
|
|
33958
|
+
...Array.isArray(meta?.tags) && meta.tags.length > 0 ? { tags: meta.tags } : {},
|
|
33959
|
+
...meta?.sharedAs ? { sharedAs: meta.sharedAs } : {}
|
|
32927
33960
|
});
|
|
32928
33961
|
}
|
|
32929
33962
|
return summaries;
|
|
@@ -32943,6 +33976,17 @@ async function fetchPage(pubkey, docId, viewKey) {
|
|
|
32943
33976
|
decrypted = true;
|
|
32944
33977
|
} catch {
|
|
32945
33978
|
}
|
|
33979
|
+
if (!decrypted && !viewKey) {
|
|
33980
|
+
try {
|
|
33981
|
+
const meta = await fetchDocMetadata(`${PAGES_KINDS.document}:${pubkey}:${docId}`);
|
|
33982
|
+
if (meta?.viewKey) {
|
|
33983
|
+
content = await decryptWithViewKey2(meta.viewKey, event.content);
|
|
33984
|
+
viewKey = meta.viewKey;
|
|
33985
|
+
decrypted = true;
|
|
33986
|
+
}
|
|
33987
|
+
} catch {
|
|
33988
|
+
}
|
|
33989
|
+
}
|
|
32946
33990
|
return {
|
|
32947
33991
|
id: docId,
|
|
32948
33992
|
address: `${PAGES_KINDS.document}:${event.pubkey}:${docId}`,
|
|
@@ -32957,12 +34001,24 @@ async function fetchPage(pubkey, docId, viewKey) {
|
|
|
32957
34001
|
}
|
|
32958
34002
|
async function deletePage(address) {
|
|
32959
34003
|
const signer = await signerManager.getSigner();
|
|
34004
|
+
const [kindStr, pubkey, dTag] = address.split(":");
|
|
34005
|
+
let versionIds = [];
|
|
34006
|
+
try {
|
|
34007
|
+
const versions = await nostrRuntime.querySync(RELAYS(), {
|
|
34008
|
+
kinds: [Number(kindStr)],
|
|
34009
|
+
authors: [pubkey],
|
|
34010
|
+
"#d": [dTag]
|
|
34011
|
+
});
|
|
34012
|
+
versionIds = versions.map((v) => v.id);
|
|
34013
|
+
} catch {
|
|
34014
|
+
}
|
|
32960
34015
|
const event = {
|
|
32961
34016
|
kind: 5,
|
|
32962
34017
|
created_at: Math.floor(Date.now() / 1e3),
|
|
32963
34018
|
tags: [
|
|
32964
34019
|
["a", address],
|
|
32965
|
-
["k", String(PAGES_KINDS.document)]
|
|
34020
|
+
["k", String(PAGES_KINDS.document)],
|
|
34021
|
+
...versionIds.map((id) => ["e", id])
|
|
32966
34022
|
],
|
|
32967
34023
|
content: "Deleted via Formstr"
|
|
32968
34024
|
};
|
|
@@ -33003,9 +34059,16 @@ function generateShareLink(address, viewKey, editKey) {
|
|
|
33003
34059
|
}
|
|
33004
34060
|
async function sharePage(params) {
|
|
33005
34061
|
const signer = await signerManager.getSigner();
|
|
33006
|
-
const [, , dTag] = params.address.split(":");
|
|
33007
|
-
const
|
|
33008
|
-
|
|
34062
|
+
const [, ownerPubkey, dTag] = params.address.split(":");
|
|
34063
|
+
const originalMeta = await fetchDocMetadata(params.address).catch(() => void 0);
|
|
34064
|
+
if (params.canEdit && originalMeta?.sharedAs) {
|
|
34065
|
+
const sharedMeta = await fetchDocMetadata(originalMeta.sharedAs).catch(() => void 0);
|
|
34066
|
+
if (sharedMeta?.viewKey && sharedMeta.editKey) {
|
|
34067
|
+
return generateShareLink(originalMeta.sharedAs, sharedMeta.viewKey, sharedMeta.editKey);
|
|
34068
|
+
}
|
|
34069
|
+
}
|
|
34070
|
+
const viewKeyHex = params.viewKey ?? originalMeta?.viewKey ?? generateViewKey2().hex;
|
|
34071
|
+
const editKeyHex = params.canEdit ? params.editKey ?? originalMeta?.editKey ?? generateViewKey2().hex : void 0;
|
|
33009
34072
|
const encrypted = await encryptWithViewKey2(viewKeyHex, params.content);
|
|
33010
34073
|
const template = {
|
|
33011
34074
|
kind: PAGES_KINDS.document,
|
|
@@ -33016,9 +34079,36 @@ async function sharePage(params) {
|
|
|
33016
34079
|
const signed = editKeyHex ? finalizeEvent(template, hexToBytes4(editKeyHex)) : await signer.signEvent(template);
|
|
33017
34080
|
await nostrRuntime.publish(RELAYS(), signed);
|
|
33018
34081
|
const newAddress = `${PAGES_KINDS.document}:${signed.pubkey}:${dTag}`;
|
|
34082
|
+
await saveDocMetadata(newAddress, {
|
|
34083
|
+
viewKey: viewKeyHex,
|
|
34084
|
+
...editKeyHex ? { editKey: editKeyHex } : {}
|
|
34085
|
+
});
|
|
34086
|
+
if (editKeyHex && ownerPubkey === await signer.getPublicKey()) {
|
|
34087
|
+
await saveDocMetadata(params.address, { sharedAs: newAddress });
|
|
34088
|
+
}
|
|
33019
34089
|
return generateShareLink(newAddress, viewKeyHex, editKeyHex);
|
|
33020
34090
|
}
|
|
33021
34091
|
async function fetchSharedList() {
|
|
34092
|
+
const metadata = await fetchAllDocMetadata();
|
|
34093
|
+
const entries = [];
|
|
34094
|
+
for (const [address, meta] of metadata) {
|
|
34095
|
+
if (typeof meta.viewKey !== "string" || !meta.viewKey) continue;
|
|
34096
|
+
entries.push(
|
|
34097
|
+
typeof meta.editKey === "string" && meta.editKey ? [address, meta.viewKey, meta.editKey] : [address, meta.viewKey]
|
|
34098
|
+
);
|
|
34099
|
+
}
|
|
34100
|
+
for (const entry of await fetchLegacySharedList()) {
|
|
34101
|
+
const [address, viewKey, editKey] = entry;
|
|
34102
|
+
if (metadata.get(address)?.viewKey) continue;
|
|
34103
|
+
entries.push(entry);
|
|
34104
|
+
try {
|
|
34105
|
+
await saveDocMetadata(address, { viewKey, ...editKey ? { editKey } : {} });
|
|
34106
|
+
} catch {
|
|
34107
|
+
}
|
|
34108
|
+
}
|
|
34109
|
+
return entries;
|
|
34110
|
+
}
|
|
34111
|
+
async function fetchLegacySharedList() {
|
|
33022
34112
|
const signer = await signerManager.getSigner();
|
|
33023
34113
|
const pubkey = await signer.getPublicKey();
|
|
33024
34114
|
const events = await nostrRuntime.querySync(RELAYS(), {
|
|
@@ -33034,30 +34124,19 @@ async function fetchSharedList() {
|
|
|
33034
34124
|
}
|
|
33035
34125
|
return [];
|
|
33036
34126
|
}
|
|
33037
|
-
async function saveSharedList(entries) {
|
|
33038
|
-
const signer = await signerManager.getSigner();
|
|
33039
|
-
const content = await nip44SelfEncrypt(signer, JSON.stringify(entries));
|
|
33040
|
-
const signed = await signer.signEvent({
|
|
33041
|
-
kind: PAGES_KINDS.sharedPagesList,
|
|
33042
|
-
created_at: Math.floor(Date.now() / 1e3),
|
|
33043
|
-
tags: [],
|
|
33044
|
-
content
|
|
33045
|
-
});
|
|
33046
|
-
await nostrRuntime.publish(RELAYS(), signed);
|
|
33047
|
-
}
|
|
33048
34127
|
async function addSharedPage(entry) {
|
|
33049
|
-
const
|
|
33050
|
-
|
|
33051
|
-
next.push(entry);
|
|
33052
|
-
await saveSharedList(next);
|
|
33053
|
-
return next;
|
|
34128
|
+
const [address, viewKey, editKey] = entry;
|
|
34129
|
+
await saveDocMetadata(address, { viewKey, ...editKey ? { editKey } : {} });
|
|
33054
34130
|
}
|
|
33055
34131
|
async function fetchSharedPages() {
|
|
34132
|
+
const signer = await signerManager.getSigner();
|
|
34133
|
+
const userPubkey = await signer.getPublicKey();
|
|
33056
34134
|
const entries = await fetchSharedList();
|
|
33057
34135
|
const out = [];
|
|
33058
34136
|
for (const [address, viewKey, editKey] of entries) {
|
|
33059
34137
|
const [, pubkey, dTag] = address.split(":");
|
|
33060
34138
|
if (!pubkey || !dTag) continue;
|
|
34139
|
+
if (pubkey === userPubkey) continue;
|
|
33061
34140
|
const doc = await fetchPage(pubkey, dTag, viewKey);
|
|
33062
34141
|
if (!doc) continue;
|
|
33063
34142
|
out.push({
|
|
@@ -33074,15 +34153,19 @@ async function fetchSharedPages() {
|
|
|
33074
34153
|
}
|
|
33075
34154
|
return out;
|
|
33076
34155
|
}
|
|
33077
|
-
|
|
33078
|
-
const
|
|
33079
|
-
if (
|
|
34156
|
+
function parseMetadataJson(json) {
|
|
34157
|
+
const parsed = JSON.parse(json);
|
|
34158
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
34159
|
+
return parsed;
|
|
34160
|
+
}
|
|
34161
|
+
return void 0;
|
|
34162
|
+
}
|
|
34163
|
+
async function fetchAllDocMetadata() {
|
|
33080
34164
|
const signer = await signerManager.getSigner();
|
|
33081
34165
|
const pubkey = await signer.getPublicKey();
|
|
33082
34166
|
const events = await nostrRuntime.querySync(RELAYS(), {
|
|
33083
34167
|
kinds: [PAGES_KINDS.docMetadata],
|
|
33084
|
-
authors: [pubkey]
|
|
33085
|
-
"#d": addresses
|
|
34168
|
+
authors: [pubkey]
|
|
33086
34169
|
});
|
|
33087
34170
|
const newest = /* @__PURE__ */ new Map();
|
|
33088
34171
|
for (const ev of events) {
|
|
@@ -33090,18 +34173,37 @@ async function fetchDocTags(addresses) {
|
|
|
33090
34173
|
const prev = newest.get(addr);
|
|
33091
34174
|
if (!prev || ev.created_at > prev.created_at) newest.set(addr, ev);
|
|
33092
34175
|
}
|
|
34176
|
+
const result = /* @__PURE__ */ new Map();
|
|
33093
34177
|
for (const [addr, ev] of newest) {
|
|
33094
34178
|
try {
|
|
33095
|
-
const
|
|
33096
|
-
if (
|
|
34179
|
+
const meta = parseMetadataJson(await nip44SelfDecrypt(signer, ev.content));
|
|
34180
|
+
if (meta) result.set(addr, meta);
|
|
33097
34181
|
} catch {
|
|
33098
34182
|
}
|
|
33099
34183
|
}
|
|
33100
34184
|
return result;
|
|
33101
34185
|
}
|
|
33102
|
-
async function
|
|
34186
|
+
async function fetchDocMetadata(address) {
|
|
34187
|
+
const signer = await signerManager.getSigner();
|
|
34188
|
+
const pubkey = await signer.getPublicKey();
|
|
34189
|
+
const events = await nostrRuntime.querySync(RELAYS(), {
|
|
34190
|
+
kinds: [PAGES_KINDS.docMetadata],
|
|
34191
|
+
authors: [pubkey],
|
|
34192
|
+
"#d": [address]
|
|
34193
|
+
});
|
|
34194
|
+
if (events.length === 0) return void 0;
|
|
34195
|
+
const newest = events.reduce((a, b) => b.created_at > a.created_at ? b : a);
|
|
34196
|
+
try {
|
|
34197
|
+
return parseMetadataJson(await nip44SelfDecrypt(signer, newest.content));
|
|
34198
|
+
} catch {
|
|
34199
|
+
return void 0;
|
|
34200
|
+
}
|
|
34201
|
+
}
|
|
34202
|
+
async function saveDocMetadata(address, patch) {
|
|
34203
|
+
const existing = await fetchDocMetadata(address) ?? {};
|
|
34204
|
+
const merged = { ...existing, ...patch };
|
|
33103
34205
|
const signer = await signerManager.getSigner();
|
|
33104
|
-
const content = await nip44SelfEncrypt(signer, JSON.stringify(
|
|
34206
|
+
const content = await nip44SelfEncrypt(signer, JSON.stringify(merged));
|
|
33105
34207
|
const signed = await signer.signEvent({
|
|
33106
34208
|
kind: PAGES_KINDS.docMetadata,
|
|
33107
34209
|
created_at: Math.floor(Date.now() / 1e3),
|
|
@@ -33109,6 +34211,41 @@ async function setDocTags(address, tags) {
|
|
|
33109
34211
|
content
|
|
33110
34212
|
});
|
|
33111
34213
|
await nostrRuntime.publish(RELAYS(), signed);
|
|
34214
|
+
return merged;
|
|
34215
|
+
}
|
|
34216
|
+
async function fetchDocTags(addresses) {
|
|
34217
|
+
const result = /* @__PURE__ */ new Map();
|
|
34218
|
+
if (addresses.length === 0) return result;
|
|
34219
|
+
const signer = await signerManager.getSigner();
|
|
34220
|
+
const pubkey = await signer.getPublicKey();
|
|
34221
|
+
const events = await nostrRuntime.querySync(RELAYS(), {
|
|
34222
|
+
kinds: [PAGES_KINDS.docMetadata],
|
|
34223
|
+
authors: [pubkey],
|
|
34224
|
+
"#d": addresses
|
|
34225
|
+
});
|
|
34226
|
+
const newest = /* @__PURE__ */ new Map();
|
|
34227
|
+
for (const ev of events) {
|
|
34228
|
+
const addr = ev.tags.find((t) => t[0] === "d")?.[1] ?? "";
|
|
34229
|
+
const prev = newest.get(addr);
|
|
34230
|
+
if (!prev || ev.created_at > prev.created_at) newest.set(addr, ev);
|
|
34231
|
+
}
|
|
34232
|
+
for (const [addr, ev] of newest) {
|
|
34233
|
+
try {
|
|
34234
|
+
const meta = parseMetadataJson(await nip44SelfDecrypt(signer, ev.content));
|
|
34235
|
+
if (meta && Array.isArray(meta.tags)) result.set(addr, meta.tags);
|
|
34236
|
+
} catch {
|
|
34237
|
+
}
|
|
34238
|
+
}
|
|
34239
|
+
return result;
|
|
34240
|
+
}
|
|
34241
|
+
async function setDocTags(address, tags) {
|
|
34242
|
+
await saveDocMetadata(address, { tags });
|
|
34243
|
+
}
|
|
34244
|
+
async function setDocTitle(address, title) {
|
|
34245
|
+
await saveDocMetadata(address, { title: title || void 0 });
|
|
34246
|
+
}
|
|
34247
|
+
async function setDocSharedAs(address, sharedAs) {
|
|
34248
|
+
await saveDocMetadata(address, { sharedAs });
|
|
33112
34249
|
}
|
|
33113
34250
|
function extractTitle(markdown) {
|
|
33114
34251
|
const firstLine = markdown.trim().split("\n")[0] ?? "";
|
|
@@ -33123,11 +34260,82 @@ function randomId(length) {
|
|
|
33123
34260
|
return result;
|
|
33124
34261
|
}
|
|
33125
34262
|
|
|
34263
|
+
// ../agent/src/services/pages/comments.ts
|
|
34264
|
+
var comments_exports = {};
|
|
34265
|
+
__export(comments_exports, {
|
|
34266
|
+
fetchPageComments: () => fetchPageComments,
|
|
34267
|
+
parsePageComment: () => parsePageComment,
|
|
34268
|
+
publishPageComment: () => publishPageComment
|
|
34269
|
+
});
|
|
34270
|
+
var RELAYS2 = () => relayManager.getRelaysForModule("pages");
|
|
34271
|
+
async function publishPageComment(draft, viewKey, docAddress, docEventId) {
|
|
34272
|
+
const signer = await signerManager.getSigner();
|
|
34273
|
+
const innerTags = [
|
|
34274
|
+
["content", draft.content],
|
|
34275
|
+
["type", draft.type]
|
|
34276
|
+
];
|
|
34277
|
+
if (draft.quote !== void 0) innerTags.push(["quote", draft.quote]);
|
|
34278
|
+
if (draft.context !== void 0) {
|
|
34279
|
+
innerTags.push(["context", draft.context.prefix, draft.context.suffix]);
|
|
34280
|
+
}
|
|
34281
|
+
const docOwnerPubkey = docAddress.split(":")[1];
|
|
34282
|
+
const event = {
|
|
34283
|
+
kind: PAGES_KINDS.comment,
|
|
34284
|
+
created_at: Math.floor(Date.now() / 1e3),
|
|
34285
|
+
content: await encryptWithViewKey2(viewKey, JSON.stringify(innerTags)),
|
|
34286
|
+
tags: [
|
|
34287
|
+
["a", docAddress],
|
|
34288
|
+
["e", docEventId],
|
|
34289
|
+
["p", docOwnerPubkey]
|
|
34290
|
+
]
|
|
34291
|
+
};
|
|
34292
|
+
const signed = await signer.signEvent(event);
|
|
34293
|
+
await nostrRuntime.publish(RELAYS2(), signed);
|
|
34294
|
+
return signed;
|
|
34295
|
+
}
|
|
34296
|
+
async function parsePageComment(event, viewKey) {
|
|
34297
|
+
try {
|
|
34298
|
+
const inner = JSON.parse(await decryptWithViewKey2(viewKey, event.content));
|
|
34299
|
+
if (!Array.isArray(inner)) return void 0;
|
|
34300
|
+
const rows = inner;
|
|
34301
|
+
const find = (name) => rows.find((r) => Array.isArray(r) && r[0] === name);
|
|
34302
|
+
const content = find("content")?.[1];
|
|
34303
|
+
if (typeof content !== "string") return void 0;
|
|
34304
|
+
const type = find("type")?.[1] === "suggestion" ? "suggestion" : "comment";
|
|
34305
|
+
const quote = find("quote")?.[1];
|
|
34306
|
+
const contextRow = find("context");
|
|
34307
|
+
return {
|
|
34308
|
+
id: event.id,
|
|
34309
|
+
author: event.pubkey,
|
|
34310
|
+
createdAt: event.created_at,
|
|
34311
|
+
content,
|
|
34312
|
+
type,
|
|
34313
|
+
...quote !== void 0 ? { quote } : {},
|
|
34314
|
+
...contextRow ? { context: { prefix: contextRow[1] ?? "", suffix: contextRow[2] ?? "" } } : {}
|
|
34315
|
+
};
|
|
34316
|
+
} catch {
|
|
34317
|
+
return void 0;
|
|
34318
|
+
}
|
|
34319
|
+
}
|
|
34320
|
+
async function fetchPageComments(docAddress, viewKey) {
|
|
34321
|
+
const events = await nostrRuntime.querySync(RELAYS2(), {
|
|
34322
|
+
kinds: [PAGES_KINDS.comment],
|
|
34323
|
+
"#a": [docAddress]
|
|
34324
|
+
});
|
|
34325
|
+
const out = [];
|
|
34326
|
+
for (const event of events) {
|
|
34327
|
+
const comment = await parsePageComment(event, viewKey);
|
|
34328
|
+
if (comment) out.push(comment);
|
|
34329
|
+
}
|
|
34330
|
+
return out.sort((a, b) => a.createdAt - b.createdAt);
|
|
34331
|
+
}
|
|
34332
|
+
|
|
33126
34333
|
// ../agent/src/services/drive/service.ts
|
|
33127
34334
|
var service_exports4 = {};
|
|
33128
34335
|
__export(service_exports4, {
|
|
33129
34336
|
deleteFile: () => deleteFile,
|
|
33130
34337
|
downloadFile: () => downloadFile,
|
|
34338
|
+
downloadPreview: () => downloadPreview,
|
|
33131
34339
|
extractFolders: () => extractFolders,
|
|
33132
34340
|
fetchBlossomServers: () => fetchBlossomServers,
|
|
33133
34341
|
fetchFileIndex: () => fetchFileIndex,
|
|
@@ -33143,8 +34351,8 @@ var DRIVE_KINDS = {
|
|
|
33143
34351
|
fileMetadata: 34578
|
|
33144
34352
|
};
|
|
33145
34353
|
var DEFAULT_BLOSSOM_SERVERS = [
|
|
33146
|
-
"https://blossom.primal.net",
|
|
33147
34354
|
"https://nostr.download",
|
|
34355
|
+
"https://blossom.primal.net",
|
|
33148
34356
|
"https://blossom.oxtr.dev"
|
|
33149
34357
|
];
|
|
33150
34358
|
|
|
@@ -33159,6 +34367,21 @@ async function uploadFile(params) {
|
|
|
33159
34367
|
const authEvent = await createBlossomAuthEvent("upload", await sha256Hex2(encryptedBytes), signer);
|
|
33160
34368
|
const blossom = new BlossomClient(server);
|
|
33161
34369
|
const result = await blossom.upload(encryptedBytes, authEvent, params.file.type);
|
|
34370
|
+
let previewHash;
|
|
34371
|
+
if (params.preview) {
|
|
34372
|
+
try {
|
|
34373
|
+
const encryptedPreview = new TextEncoder().encode(
|
|
34374
|
+
await encryptFileWithExistingKey(params.preview, privateKeyHex)
|
|
34375
|
+
);
|
|
34376
|
+
const previewAuth = await createBlossomAuthEvent(
|
|
34377
|
+
"upload",
|
|
34378
|
+
await sha256Hex2(encryptedPreview),
|
|
34379
|
+
signer
|
|
34380
|
+
);
|
|
34381
|
+
previewHash = (await blossom.upload(encryptedPreview, previewAuth, "image/webp")).sha256;
|
|
34382
|
+
} catch {
|
|
34383
|
+
}
|
|
34384
|
+
}
|
|
33162
34385
|
const metadata = {
|
|
33163
34386
|
name: params.file.name,
|
|
33164
34387
|
hash: result.sha256,
|
|
@@ -33167,11 +34390,26 @@ async function uploadFile(params) {
|
|
|
33167
34390
|
folder: params.folder ?? "/",
|
|
33168
34391
|
uploadedAt: Date.now(),
|
|
33169
34392
|
server,
|
|
33170
|
-
|
|
34393
|
+
...previewHash ? { previewHash } : {},
|
|
34394
|
+
encryptionKey: privateKeyHex,
|
|
34395
|
+
encryptionAlgorithm: "aes-gcm"
|
|
33171
34396
|
};
|
|
33172
34397
|
await saveFileMetadata(metadata);
|
|
33173
34398
|
return metadata;
|
|
33174
34399
|
}
|
|
34400
|
+
async function downloadPreview(metadata) {
|
|
34401
|
+
if (!metadata.previewHash) return null;
|
|
34402
|
+
const blossom = new BlossomClient(metadata.server);
|
|
34403
|
+
let authEvent;
|
|
34404
|
+
try {
|
|
34405
|
+
const signer = await signerManager.getSigner();
|
|
34406
|
+
authEvent = await createBlossomAuthEvent("get", metadata.previewHash, signer);
|
|
34407
|
+
} catch {
|
|
34408
|
+
}
|
|
34409
|
+
const encryptedBytes = await blossom.download(metadata.previewHash, authEvent);
|
|
34410
|
+
const ciphertext = new TextDecoder().decode(encryptedBytes);
|
|
34411
|
+
return decryptFileWithKey(ciphertext, metadata.encryptionKey);
|
|
34412
|
+
}
|
|
33175
34413
|
async function downloadFile(metadata) {
|
|
33176
34414
|
const blossom = new BlossomClient(metadata.server);
|
|
33177
34415
|
let authEvent;
|
|
@@ -33215,7 +34453,11 @@ async function fetchFileIndex() {
|
|
|
33215
34453
|
}
|
|
33216
34454
|
async function saveFileMetadata(metadata) {
|
|
33217
34455
|
const signer = await signerManager.getSigner();
|
|
33218
|
-
const
|
|
34456
|
+
const full = {
|
|
34457
|
+
...metadata,
|
|
34458
|
+
encryptionAlgorithm: metadata.encryptionAlgorithm ?? "aes-gcm"
|
|
34459
|
+
};
|
|
34460
|
+
const encrypted = await nip44SelfEncrypt(signer, JSON.stringify(full));
|
|
33219
34461
|
const event = {
|
|
33220
34462
|
kind: DRIVE_KINDS.fileMetadata,
|
|
33221
34463
|
created_at: Math.floor(Date.now() / 1e3),
|
|
@@ -33311,6 +34553,28 @@ __export(service_exports5, {
|
|
|
33311
34553
|
submitPollResponse: () => submitPollResponse
|
|
33312
34554
|
});
|
|
33313
34555
|
|
|
34556
|
+
// ../agent/src/services/polls/pow.ts
|
|
34557
|
+
function minePollEvent(unsigned, difficulty) {
|
|
34558
|
+
const event = unsigned;
|
|
34559
|
+
const nonceTag = ["nonce", "0", difficulty.toString()];
|
|
34560
|
+
event.tags.push(nonceTag);
|
|
34561
|
+
event.tags.push(["W", difficulty.toString()]);
|
|
34562
|
+
let count = 0;
|
|
34563
|
+
for (; ; ) {
|
|
34564
|
+
const now2 = Math.floor(Date.now() / 1e3);
|
|
34565
|
+
if (now2 !== event.created_at) {
|
|
34566
|
+
count = 0;
|
|
34567
|
+
event.created_at = now2;
|
|
34568
|
+
}
|
|
34569
|
+
nonceTag[1] = (++count).toString();
|
|
34570
|
+
event.id = getEventHash(event);
|
|
34571
|
+
if (nip13_exports.getPow(event.id) >= difficulty) return event;
|
|
34572
|
+
}
|
|
34573
|
+
}
|
|
34574
|
+
function hasValidPow(event, difficulty) {
|
|
34575
|
+
return nip13_exports.getPow(event.id) >= difficulty;
|
|
34576
|
+
}
|
|
34577
|
+
|
|
33314
34578
|
// ../agent/src/services/polls/types.ts
|
|
33315
34579
|
var POLLS_KINDS = {
|
|
33316
34580
|
poll: 1068,
|
|
@@ -33365,7 +34629,7 @@ async function createPoll(draft) {
|
|
|
33365
34629
|
function withModuleRelays(pollRelays) {
|
|
33366
34630
|
return Array.from(/* @__PURE__ */ new Set([...pollRelays ?? [], ...relayManager.getRelaysForModule("polls")]));
|
|
33367
34631
|
}
|
|
33368
|
-
async function submitPollResponse(pollId, pollAuthor, selectedOptionIds, pollRelays) {
|
|
34632
|
+
async function submitPollResponse(pollId, pollAuthor, selectedOptionIds, pollRelays, powDifficulty) {
|
|
33369
34633
|
const signer = await signerManager.getSigner();
|
|
33370
34634
|
const relays = withModuleRelays(pollRelays);
|
|
33371
34635
|
const tags = [
|
|
@@ -33375,12 +34639,22 @@ async function submitPollResponse(pollId, pollAuthor, selectedOptionIds, pollRel
|
|
|
33375
34639
|
for (const optionId of selectedOptionIds) {
|
|
33376
34640
|
tags.push(["response", optionId]);
|
|
33377
34641
|
}
|
|
33378
|
-
|
|
34642
|
+
let event = {
|
|
33379
34643
|
kind: POLLS_KINDS.response,
|
|
33380
34644
|
created_at: Math.floor(Date.now() / 1e3),
|
|
33381
34645
|
tags,
|
|
33382
34646
|
content: ""
|
|
33383
34647
|
};
|
|
34648
|
+
if (powDifficulty && powDifficulty > 0) {
|
|
34649
|
+
const unsigned = { ...event, pubkey: await signer.getPublicKey() };
|
|
34650
|
+
const mined = minePollEvent(unsigned, powDifficulty);
|
|
34651
|
+
event = {
|
|
34652
|
+
kind: mined.kind,
|
|
34653
|
+
created_at: mined.created_at,
|
|
34654
|
+
tags: mined.tags,
|
|
34655
|
+
content: mined.content
|
|
34656
|
+
};
|
|
34657
|
+
}
|
|
33384
34658
|
const signed = await signer.signEvent(event);
|
|
33385
34659
|
await nostrRuntime.publish(relays, signed);
|
|
33386
34660
|
}
|
|
@@ -33428,11 +34702,12 @@ async function fetchPoll(eventId) {
|
|
|
33428
34702
|
function resultRelays(poll) {
|
|
33429
34703
|
return withModuleRelays(poll.relays);
|
|
33430
34704
|
}
|
|
33431
|
-
function latestResponsesByVoter(events, deleted) {
|
|
34705
|
+
function latestResponsesByVoter(events, deleted, powDifficulty = 0) {
|
|
33432
34706
|
const latest = /* @__PURE__ */ new Map();
|
|
33433
34707
|
for (const e of events) {
|
|
33434
34708
|
if (isPollDeleted(e, deleted)) continue;
|
|
33435
34709
|
if (!e.tags.some((t) => t[0] === "response")) continue;
|
|
34710
|
+
if (powDifficulty > 0 && !hasValidPow(e, powDifficulty)) continue;
|
|
33436
34711
|
const prev = latest.get(e.pubkey);
|
|
33437
34712
|
if (!prev || e.created_at > prev.created_at) latest.set(e.pubkey, e);
|
|
33438
34713
|
}
|
|
@@ -33450,11 +34725,13 @@ async function fetchPollResults(poll) {
|
|
|
33450
34725
|
const events = await nostrRuntime.querySync(relays, {
|
|
33451
34726
|
kinds: [POLLS_KINDS.response, POLLS_KINDS.responseLegacy],
|
|
33452
34727
|
"#e": [poll.id],
|
|
33453
|
-
...poll.endsAt ? { until: poll.endsAt } : {}
|
|
34728
|
+
...poll.endsAt ? { until: poll.endsAt } : {},
|
|
34729
|
+
// PoW polls: upstream queries votes by their ["W", difficulty] tag.
|
|
34730
|
+
...poll.powDifficulty ? { "#W": [String(poll.powDifficulty)] } : {}
|
|
33454
34731
|
});
|
|
33455
34732
|
const authors = Array.from(new Set(events.map((e) => e.pubkey)));
|
|
33456
34733
|
const deleted = await fetchDeletions3(relays, authors);
|
|
33457
|
-
return computeResults(latestResponsesByVoter(events, deleted));
|
|
34734
|
+
return computeResults(latestResponsesByVoter(events, deleted, poll.powDifficulty ?? 0));
|
|
33458
34735
|
}
|
|
33459
34736
|
async function fetchRecentPolls(limit = 20) {
|
|
33460
34737
|
const relays = relayManager.getRelaysForModule("polls");
|
|
@@ -33523,7 +34800,7 @@ function computeResults(responses) {
|
|
|
33523
34800
|
for (const [pubkey, selected] of responses) {
|
|
33524
34801
|
for (const optionId of selected) {
|
|
33525
34802
|
const existing = counts.get(optionId) ?? [];
|
|
33526
|
-
existing.push(pubkey);
|
|
34803
|
+
if (!existing.includes(pubkey)) existing.push(pubkey);
|
|
33527
34804
|
counts.set(optionId, existing);
|
|
33528
34805
|
}
|
|
33529
34806
|
}
|
|
@@ -33816,11 +35093,12 @@ function buildCalendarTools() {
|
|
|
33816
35093
|
server.registerTool(
|
|
33817
35094
|
"rsvp_event",
|
|
33818
35095
|
{
|
|
33819
|
-
description: "RSVP to a calendar event on your identity. Optionally suggest a new time (suggestedStart/suggestedEnd, unix seconds) and add a note (comment). Requires confirm:true.",
|
|
35096
|
+
description: "RSVP to a calendar event on your identity. Optionally suggest a new time (suggestedStart/suggestedEnd, unix seconds) and add a note (comment). For private events pass the event's viewKey (nsec) if known \u2014 otherwise it is looked up from your calendar lists. Requires confirm:true.",
|
|
33820
35097
|
inputSchema: {
|
|
33821
35098
|
eventCoordinate: external_exports.string(),
|
|
33822
35099
|
status: external_exports.enum(["accepted", "declined", "tentative"]),
|
|
33823
35100
|
isPrivate: external_exports.boolean().optional(),
|
|
35101
|
+
viewKey: external_exports.string().optional(),
|
|
33824
35102
|
suggestedStart: external_exports.number().optional(),
|
|
33825
35103
|
suggestedEnd: external_exports.number().optional(),
|
|
33826
35104
|
comment: external_exports.string().optional(),
|
|
@@ -33831,6 +35109,7 @@ function buildCalendarTools() {
|
|
|
33831
35109
|
eventCoordinate,
|
|
33832
35110
|
status,
|
|
33833
35111
|
isPrivate,
|
|
35112
|
+
viewKey,
|
|
33834
35113
|
suggestedStart,
|
|
33835
35114
|
suggestedEnd,
|
|
33836
35115
|
comment,
|
|
@@ -33838,16 +35117,13 @@ function buildCalendarTools() {
|
|
|
33838
35117
|
}) => {
|
|
33839
35118
|
const blocked = requireConfirm("rsvp_event", { confirm }, `sends "${status}" RSVP`);
|
|
33840
35119
|
if (blocked) return blocked;
|
|
33841
|
-
|
|
33842
|
-
if (
|
|
33843
|
-
await
|
|
33844
|
-
suggestedStart,
|
|
33845
|
-
suggestedEnd,
|
|
33846
|
-
comment
|
|
33847
|
-
});
|
|
33848
|
-
} else {
|
|
33849
|
-
await rsvp_exports.rsvpToEvent(eventCoordinate, status, Boolean(isPrivate));
|
|
35120
|
+
let key = viewKey;
|
|
35121
|
+
if (isPrivate && !key) {
|
|
35122
|
+
key = await service_exports2.lookupEventViewKey(eventCoordinate);
|
|
33850
35123
|
}
|
|
35124
|
+
const hasExtra = suggestedStart !== void 0 || suggestedEnd !== void 0 || comment !== void 0;
|
|
35125
|
+
const extra = hasExtra ? { suggestedStart, suggestedEnd, comment } : void 0;
|
|
35126
|
+
await rsvp_exports.rsvpToEvent(eventCoordinate, status, Boolean(isPrivate), extra, key);
|
|
33851
35127
|
return ok(`RSVP "${status}" sent.`);
|
|
33852
35128
|
}
|
|
33853
35129
|
);
|
|
@@ -33887,6 +35163,9 @@ function buildCalendarTools() {
|
|
|
33887
35163
|
rrule: args.rrule ?? existing.repeat.rrule ?? void 0,
|
|
33888
35164
|
startTzid: args.startTzid ?? existing.startTzid,
|
|
33889
35165
|
registrationFormRef: existing.registrationFormRef,
|
|
35166
|
+
registrationFormViewKey: existing.registrationFormViewKey,
|
|
35167
|
+
notificationPreference: existing.notificationPreference,
|
|
35168
|
+
viewKey: existing.viewKey,
|
|
33890
35169
|
existingId: existing.id
|
|
33891
35170
|
};
|
|
33892
35171
|
const event = existing.isPrivate ? await service_exports2.publishPrivateCalendarEvent(draft, existing.calendarId ?? "default") : await service_exports2.publishPublicCalendarEvent(draft);
|
|
@@ -33926,6 +35205,8 @@ function buildCalendarTools() {
|
|
|
33926
35205
|
rrule: existing.repeat.rrule ?? void 0,
|
|
33927
35206
|
startTzid: existing.startTzid,
|
|
33928
35207
|
registrationFormRef: formRef,
|
|
35208
|
+
notificationPreference: existing.notificationPreference,
|
|
35209
|
+
viewKey: existing.viewKey,
|
|
33929
35210
|
existingId: existing.id
|
|
33930
35211
|
};
|
|
33931
35212
|
const event = existing.isPrivate ? await service_exports2.publishPrivateCalendarEvent(draft, existing.calendarId ?? "default") : await service_exports2.publishPublicCalendarEvent(draft);
|
|
@@ -34208,8 +35489,13 @@ function normalizePubkeyList(values) {
|
|
|
34208
35489
|
return values.map((v) => typeof v === "string" ? normalizePubkey(v) : null).filter((p) => !!p);
|
|
34209
35490
|
}
|
|
34210
35491
|
var ANSWER_TYPES = new Set(Object.values(AnswerType));
|
|
35492
|
+
var LEGACY_TYPE_ALIASES = {
|
|
35493
|
+
multiChoiceGrid: "multipleChoiceGrid" /* multipleChoiceGrid */
|
|
35494
|
+
};
|
|
34211
35495
|
function coerceType(type) {
|
|
34212
|
-
|
|
35496
|
+
if (!type) return "shortText" /* shortText */;
|
|
35497
|
+
if (LEGACY_TYPE_ALIASES[type]) return LEGACY_TYPE_ALIASES[type];
|
|
35498
|
+
return ANSWER_TYPES.has(type) ? type : "shortText" /* shortText */;
|
|
34213
35499
|
}
|
|
34214
35500
|
function normalizeOptions(options) {
|
|
34215
35501
|
if (!options) return void 0;
|
|
@@ -34229,7 +35515,8 @@ function aiFieldsToFormFields(value) {
|
|
|
34229
35515
|
validation: f.validation,
|
|
34230
35516
|
gridRows: f.gridRows,
|
|
34231
35517
|
gridCols: f.gridCols,
|
|
34232
|
-
fileConfig: f.fileConfig
|
|
35518
|
+
fileConfig: f.fileConfig,
|
|
35519
|
+
maxStars: f.maxStars
|
|
34233
35520
|
}));
|
|
34234
35521
|
}
|
|
34235
35522
|
|
|
@@ -34253,6 +35540,7 @@ var fieldShape = external_exports.object({
|
|
|
34253
35540
|
}).optional(),
|
|
34254
35541
|
gridRows: external_exports.array(external_exports.string()).optional(),
|
|
34255
35542
|
gridCols: external_exports.array(external_exports.string()).optional(),
|
|
35543
|
+
maxStars: external_exports.number().optional(),
|
|
34256
35544
|
fileConfig: external_exports.object({
|
|
34257
35545
|
blossomServer: external_exports.string().optional(),
|
|
34258
35546
|
maxBytes: external_exports.number().optional(),
|
|
@@ -34351,7 +35639,13 @@ ${form.settings.description}`);
|
|
|
34351
35639
|
const signingKey = mine.find(
|
|
34352
35640
|
(f) => f.pubkey === formAuthorPubkey && f.id === formId
|
|
34353
35641
|
)?.signingKey;
|
|
34354
|
-
const
|
|
35642
|
+
const template = await service_exports.fetchForm(formAuthorPubkey, formId);
|
|
35643
|
+
const responses = await service_exports.fetchResponses(
|
|
35644
|
+
formAuthorPubkey,
|
|
35645
|
+
formId,
|
|
35646
|
+
signingKey,
|
|
35647
|
+
template?.relays
|
|
35648
|
+
);
|
|
34355
35649
|
const blocks = responses.map((r) => {
|
|
34356
35650
|
const when = new Date(r.createdAt * 1e3).toISOString();
|
|
34357
35651
|
const answers = r.responses.length ? r.responses.map((a) => `- ${a.fieldId}: ${a.answer}`).join("\n") : r.wasEncrypted ? "_(encrypted \u2014 you are not the form owner)_" : "_(no answers)_";
|
|
@@ -34466,24 +35760,32 @@ To let collaborators decrypt it, run \`share_form\` with their npubs.` : ""),
|
|
|
34466
35760
|
},
|
|
34467
35761
|
{
|
|
34468
35762
|
name: "share_form",
|
|
34469
|
-
description: "Share an encrypted form's view key with collaborators via NIP-59 gift-wrap so they can decrypt it. Requires confirm:true.",
|
|
35763
|
+
description: "Share an encrypted form's view key with collaborators via NIP-59 gift-wrap so they can decrypt it. Pubkeys in `editors` additionally receive the signing key (edit access). Requires confirm:true.",
|
|
34470
35764
|
inputSchema: {
|
|
34471
35765
|
formId: external_exports.string(),
|
|
34472
35766
|
formPubkey: external_exports.string(),
|
|
34473
35767
|
recipients: external_exports.array(external_exports.string()),
|
|
35768
|
+
editors: external_exports.array(external_exports.string()).optional(),
|
|
34474
35769
|
confirm: external_exports.boolean().optional()
|
|
34475
35770
|
},
|
|
34476
35771
|
write: true,
|
|
34477
|
-
handler: async ({ formId, formPubkey, recipients, confirm }) => {
|
|
35772
|
+
handler: async ({ formId, formPubkey, recipients, editors, confirm }) => {
|
|
34478
35773
|
const recipientHex = normalizePubkeyList(recipients);
|
|
34479
|
-
|
|
35774
|
+
const editorHex = normalizePubkeyList(editors ?? []);
|
|
35775
|
+
if (recipientHex.length === 0 && editorHex.length === 0)
|
|
35776
|
+
return fail("No valid recipients provided.", "BAD_INPUT");
|
|
34480
35777
|
const blocked = requireConfirm(
|
|
34481
35778
|
"share_form",
|
|
34482
35779
|
{ confirm },
|
|
34483
|
-
`gift-wraps the
|
|
35780
|
+
`gift-wraps the access keys of form ${formId} to ${recipientHex.length + editorHex.length} recipient(s)`
|
|
34484
35781
|
);
|
|
34485
35782
|
if (blocked) return blocked;
|
|
34486
|
-
const result = await service_exports.shareForm({
|
|
35783
|
+
const result = await service_exports.shareForm({
|
|
35784
|
+
formId,
|
|
35785
|
+
formPubkey,
|
|
35786
|
+
recipients: recipientHex,
|
|
35787
|
+
editors: editorHex
|
|
35788
|
+
});
|
|
34487
35789
|
const failedNote = result.failed.length ? ` (${result.failed.length} failed)` : "";
|
|
34488
35790
|
return ok(`Shared view key with ${result.published} recipient(s)${failedNote}.`, {
|
|
34489
35791
|
published: result.published,
|
|
@@ -34527,6 +35829,7 @@ To let collaborators decrypt it, run \`share_form\` with their npubs.` : ""),
|
|
|
34527
35829
|
`publicly submits ${answers.length} answer(s) to form ${formId}`
|
|
34528
35830
|
);
|
|
34529
35831
|
if (blocked) return blocked;
|
|
35832
|
+
const template = await service_exports.fetchForm(formAuthorPubkey, formId);
|
|
34530
35833
|
await service_exports.submitResponse(
|
|
34531
35834
|
formAuthorPubkey,
|
|
34532
35835
|
formId,
|
|
@@ -34535,7 +35838,9 @@ To let collaborators decrypt it, run \`share_form\` with their npubs.` : ""),
|
|
|
34535
35838
|
answer: a.answer,
|
|
34536
35839
|
metadata: a.metadata
|
|
34537
35840
|
})),
|
|
34538
|
-
Boolean(encrypt7)
|
|
35841
|
+
Boolean(encrypt7),
|
|
35842
|
+
void 0,
|
|
35843
|
+
template?.relays
|
|
34539
35844
|
);
|
|
34540
35845
|
return ok(`Submitted ${answers.length} answer(s) to form ${formId}.`);
|
|
34541
35846
|
}
|
|
@@ -34590,7 +35895,7 @@ function buildPagesTools() {
|
|
|
34590
35895
|
server.registerTool(
|
|
34591
35896
|
"list_shared_pages",
|
|
34592
35897
|
{
|
|
34593
|
-
description: "List documents others have shared with you (kind-
|
|
35898
|
+
description: "List documents others have shared with you (kind-34579 doc-metadata entries carrying a viewKey).",
|
|
34594
35899
|
inputSchema: {}
|
|
34595
35900
|
},
|
|
34596
35901
|
async () => {
|
|
@@ -34615,6 +35920,25 @@ function buildPagesTools() {
|
|
|
34615
35920
|
return ok("Tags fetched.", { address, tags: map.get(address) ?? [] });
|
|
34616
35921
|
}
|
|
34617
35922
|
);
|
|
35923
|
+
server.registerTool(
|
|
35924
|
+
"list_page_comments",
|
|
35925
|
+
{
|
|
35926
|
+
description: "List the inline comments/suggestions on a shared document (kind 1494; needs the doc's viewKey).",
|
|
35927
|
+
inputSchema: { address: external_exports.string(), viewKey: external_exports.string() }
|
|
35928
|
+
},
|
|
35929
|
+
async ({ address, viewKey }) => {
|
|
35930
|
+
const comments = await comments_exports.fetchPageComments(address, viewKey);
|
|
35931
|
+
return ok(`${comments.length} comment(s).`, {
|
|
35932
|
+
comments: comments.map((c) => ({
|
|
35933
|
+
author: c.author,
|
|
35934
|
+
createdAt: c.createdAt,
|
|
35935
|
+
type: c.type,
|
|
35936
|
+
content: c.content,
|
|
35937
|
+
quote: c.quote
|
|
35938
|
+
}))
|
|
35939
|
+
});
|
|
35940
|
+
}
|
|
35941
|
+
);
|
|
34618
35942
|
server.registerTool(
|
|
34619
35943
|
"create_page",
|
|
34620
35944
|
{
|
|
@@ -34721,6 +36045,44 @@ function buildPagesTools() {
|
|
|
34721
36045
|
});
|
|
34722
36046
|
}
|
|
34723
36047
|
);
|
|
36048
|
+
server.registerTool(
|
|
36049
|
+
"add_page_comment",
|
|
36050
|
+
{
|
|
36051
|
+
description: "Add an inline comment or suggestion to a shared document (kind 1494). Requires confirm:true.",
|
|
36052
|
+
inputSchema: {
|
|
36053
|
+
address: external_exports.string(),
|
|
36054
|
+
eventId: external_exports.string(),
|
|
36055
|
+
viewKey: external_exports.string(),
|
|
36056
|
+
content: external_exports.string(),
|
|
36057
|
+
type: external_exports.enum(["comment", "suggestion"]).optional(),
|
|
36058
|
+
quote: external_exports.string().optional(),
|
|
36059
|
+
confirm: external_exports.boolean().optional()
|
|
36060
|
+
}
|
|
36061
|
+
},
|
|
36062
|
+
async ({
|
|
36063
|
+
address,
|
|
36064
|
+
eventId,
|
|
36065
|
+
viewKey,
|
|
36066
|
+
content,
|
|
36067
|
+
type,
|
|
36068
|
+
quote,
|
|
36069
|
+
confirm
|
|
36070
|
+
}) => {
|
|
36071
|
+
const blocked = requireConfirm(
|
|
36072
|
+
"add_page_comment",
|
|
36073
|
+
{ confirm },
|
|
36074
|
+
`publishes a comment on ${address}`
|
|
36075
|
+
);
|
|
36076
|
+
if (blocked) return blocked;
|
|
36077
|
+
const event = await comments_exports.publishPageComment(
|
|
36078
|
+
{ content, type: type ?? "comment", ...quote !== void 0 ? { quote } : {} },
|
|
36079
|
+
viewKey,
|
|
36080
|
+
address,
|
|
36081
|
+
eventId
|
|
36082
|
+
);
|
|
36083
|
+
return ok("Comment published.", { id: event.id });
|
|
36084
|
+
}
|
|
36085
|
+
);
|
|
34724
36086
|
return tools;
|
|
34725
36087
|
}
|
|
34726
36088
|
|
|
@@ -34857,7 +36219,13 @@ function buildPollsTools() {
|
|
|
34857
36219
|
if (blocked) return blocked;
|
|
34858
36220
|
const poll = await service_exports5.fetchPoll(pollEventId);
|
|
34859
36221
|
if (!poll) return fail("Poll not found.");
|
|
34860
|
-
await service_exports5.submitPollResponse(
|
|
36222
|
+
await service_exports5.submitPollResponse(
|
|
36223
|
+
pollEventId,
|
|
36224
|
+
poll.pubkey,
|
|
36225
|
+
optionIds,
|
|
36226
|
+
poll.relays,
|
|
36227
|
+
poll.powDifficulty
|
|
36228
|
+
);
|
|
34861
36229
|
return ok(`Voted on poll ${pollEventId}.`);
|
|
34862
36230
|
}
|
|
34863
36231
|
);
|
|
@@ -45086,7 +46454,7 @@ function adapt(r) {
|
|
|
45086
46454
|
};
|
|
45087
46455
|
}
|
|
45088
46456
|
function buildServer(ctx) {
|
|
45089
|
-
const server = new McpServer({ name: "formstr", version: "0.1.
|
|
46457
|
+
const server = new McpServer({ name: "formstr", version: "0.1.0" });
|
|
45090
46458
|
for (const t of toolRegistry) {
|
|
45091
46459
|
if (t.write && !ctx.allowWrites) continue;
|
|
45092
46460
|
server.registerTool(
|
|
@@ -45106,7 +46474,17 @@ async function startStdio(ctx) {
|
|
|
45106
46474
|
// src/index.ts
|
|
45107
46475
|
async function runServer(cli) {
|
|
45108
46476
|
const cfg = resolveConfig(cli, process.env);
|
|
45109
|
-
const
|
|
46477
|
+
const deps = process.stdin.isTTY ? {
|
|
46478
|
+
promptPassphrase: async (question) => {
|
|
46479
|
+
const io = createTerminalIo();
|
|
46480
|
+
try {
|
|
46481
|
+
return await io.promptPassphrase(question);
|
|
46482
|
+
} finally {
|
|
46483
|
+
io.close();
|
|
46484
|
+
}
|
|
46485
|
+
}
|
|
46486
|
+
} : {};
|
|
46487
|
+
const account = await bootstrap({ account: cfg.account, relays: cfg.relays }, deps);
|
|
45110
46488
|
console.error(
|
|
45111
46489
|
`formstr-mcp: signed in as ${account.npub} (${account.method}), writes ${cfg.allowWrites ? "ENABLED" : "disabled"}`
|
|
45112
46490
|
);
|
|
@@ -45115,13 +46493,14 @@ async function runServer(cli) {
|
|
|
45115
46493
|
}
|
|
45116
46494
|
async function runLogin(cli) {
|
|
45117
46495
|
const io = createTerminalIo();
|
|
46496
|
+
const pool = createPatchedPool();
|
|
45118
46497
|
try {
|
|
45119
46498
|
const account = await doLogin({
|
|
45120
46499
|
signer: await buildMcpSigner(),
|
|
45121
46500
|
prompt: io.prompt,
|
|
45122
46501
|
promptPassphrase: io.promptPassphrase,
|
|
45123
46502
|
printQr,
|
|
45124
|
-
pool
|
|
46503
|
+
pool,
|
|
45125
46504
|
relays: cli.relays
|
|
45126
46505
|
});
|
|
45127
46506
|
console.error(
|
|
@@ -45129,6 +46508,10 @@ async function runLogin(cli) {
|
|
|
45129
46508
|
);
|
|
45130
46509
|
} finally {
|
|
45131
46510
|
io.close();
|
|
46511
|
+
try {
|
|
46512
|
+
pool.destroy();
|
|
46513
|
+
} catch {
|
|
46514
|
+
}
|
|
45132
46515
|
}
|
|
45133
46516
|
}
|
|
45134
46517
|
async function main() {
|
|
@@ -45136,18 +46519,18 @@ async function main() {
|
|
|
45136
46519
|
switch (cli.command) {
|
|
45137
46520
|
case "login":
|
|
45138
46521
|
await runLogin(cli);
|
|
45139
|
-
return;
|
|
46522
|
+
return cli.command;
|
|
45140
46523
|
case "logout": {
|
|
45141
46524
|
await doLogout(await buildMcpSigner(), cli.account);
|
|
45142
46525
|
console.error("formstr-mcp: signed out.");
|
|
45143
|
-
return;
|
|
46526
|
+
return cli.command;
|
|
45144
46527
|
}
|
|
45145
46528
|
case "whoami": {
|
|
45146
46529
|
const who = whoami(await buildMcpSigner());
|
|
45147
46530
|
console.error(
|
|
45148
46531
|
who ? `formstr-mcp: ${who.npub} (${who.method})` : "formstr-mcp: not signed in."
|
|
45149
46532
|
);
|
|
45150
|
-
return;
|
|
46533
|
+
return cli.command;
|
|
45151
46534
|
}
|
|
45152
46535
|
case "accounts": {
|
|
45153
46536
|
const signer = await buildMcpSigner();
|
|
@@ -45155,26 +46538,45 @@ async function main() {
|
|
|
45155
46538
|
const accounts = listAccounts(signer);
|
|
45156
46539
|
if (accounts.length === 0) {
|
|
45157
46540
|
console.error("formstr-mcp: no accounts. Run `formstr-mcp login`.");
|
|
45158
|
-
return;
|
|
46541
|
+
return cli.command;
|
|
45159
46542
|
}
|
|
45160
46543
|
for (const a of accounts) {
|
|
45161
46544
|
const marker = active && a.pubkey === active.pubkey ? "* " : " ";
|
|
45162
46545
|
console.error(`${marker}${a.npub} (${a.method})`);
|
|
45163
46546
|
}
|
|
45164
|
-
return;
|
|
46547
|
+
return cli.command;
|
|
46548
|
+
}
|
|
46549
|
+
case "switch": {
|
|
46550
|
+
if (!cli.target) {
|
|
46551
|
+
throw new Error(
|
|
46552
|
+
"Usage: formstr-mcp switch <npub|hex>. Run `formstr-mcp accounts` to list them."
|
|
46553
|
+
);
|
|
46554
|
+
}
|
|
46555
|
+
const account = await doSwitch(await buildMcpSigner(), cli.target);
|
|
46556
|
+
console.error(`formstr-mcp: active account is now ${account.npub} (${account.method}).`);
|
|
46557
|
+
return cli.command;
|
|
45165
46558
|
}
|
|
46559
|
+
case "help":
|
|
46560
|
+
console.error(helpText());
|
|
46561
|
+
return cli.command;
|
|
45166
46562
|
case "run":
|
|
45167
46563
|
default:
|
|
45168
46564
|
await runServer(cli);
|
|
46565
|
+
return "run";
|
|
45169
46566
|
}
|
|
45170
46567
|
}
|
|
45171
|
-
main().
|
|
45172
|
-
|
|
46568
|
+
main().then((command) => {
|
|
46569
|
+
if (command !== "run") process.exit(0);
|
|
46570
|
+
}).catch((err) => {
|
|
46571
|
+
const debug = !!(process.env.FORMSTR_MCP_DEBUG || process.env.DEBUG);
|
|
46572
|
+
console.error("formstr-mcp: fatal:", formatFatal(err, debug));
|
|
46573
|
+
if (!debug) console.error("formstr-mcp: (set FORMSTR_MCP_DEBUG=1 for a full stack trace)");
|
|
45173
46574
|
process.exit(1);
|
|
45174
46575
|
});
|
|
45175
46576
|
/*! Bundled license information:
|
|
45176
46577
|
|
|
45177
46578
|
@noble/hashes/utils.js:
|
|
46579
|
+
@noble/hashes/esm/utils.js:
|
|
45178
46580
|
(*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
|
|
45179
46581
|
|
|
45180
46582
|
@noble/curves/utils.js:
|