@ztimson/momentum 1.5.6 → 1.5.8

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/dist/index.mjs CHANGED
@@ -1133,34 +1133,88 @@ var Table = class {
1133
1133
  };
1134
1134
  var PromiseProgress = class PromiseProgress extends Promise {
1135
1135
  listeners = [];
1136
- _progress = 0;
1136
+ _meta = {
1137
+ start: Date.now(),
1138
+ end: null,
1139
+ elapsed: 0,
1140
+ speed: 0,
1141
+ progress: 0,
1142
+ size: 0,
1143
+ total: 0,
1144
+ eta: null
1145
+ };
1146
+ get meta() {
1147
+ return this._meta;
1148
+ }
1149
+ get start() {
1150
+ return this._meta.start;
1151
+ }
1152
+ get end() {
1153
+ return this._meta.end;
1154
+ }
1155
+ get elapsed() {
1156
+ return this._meta.elapsed;
1157
+ }
1158
+ get speed() {
1159
+ return this._meta.speed;
1160
+ }
1137
1161
  get progress() {
1138
- return this._progress;
1162
+ return this._meta.progress;
1139
1163
  }
1140
- set progress(p) {
1141
- if (p == this._progress) return;
1142
- this._progress = p;
1143
- this.listeners.forEach((l) => l(p));
1164
+ get size() {
1165
+ return this._meta.size;
1166
+ }
1167
+ get total() {
1168
+ return this._meta.total;
1169
+ }
1170
+ get eta() {
1171
+ return this._meta.eta;
1172
+ }
1173
+ update(size, total) {
1174
+ const elapsed = (Date.now() - this._meta.start) / 1e3;
1175
+ const speed = elapsed ? size / elapsed : 0;
1176
+ const progress = total ? size / total : this._meta.progress;
1177
+ this._meta = {
1178
+ ...this._meta,
1179
+ size,
1180
+ total,
1181
+ progress,
1182
+ elapsed,
1183
+ speed,
1184
+ eta: speed && total ? (total - size) / speed : null
1185
+ };
1186
+ (this.listeners ||= []).forEach((l) => l(this._meta));
1144
1187
  }
1145
1188
  constructor(executor) {
1146
- super((resolve, reject) => executor((value) => resolve(value), (reason) => reject(reason), (progress) => this.progress = progress));
1189
+ super((resolve, reject) => executor((value) => {
1190
+ this._meta = {
1191
+ ...this._meta,
1192
+ end: Date.now()
1193
+ };
1194
+ resolve(value);
1195
+ }, (reason) => {
1196
+ this._meta = {
1197
+ ...this._meta,
1198
+ end: Date.now()
1199
+ };
1200
+ reject(reason);
1201
+ }, (size, total) => this.update(size, total ?? this._meta.total)));
1147
1202
  }
1148
1203
  static from(promise) {
1149
1204
  if (promise instanceof PromiseProgress) return promise;
1150
- return new PromiseProgress((res, rej) => promise.then((...args) => res(...args)).catch((...args) => rej(...args)));
1205
+ return new PromiseProgress((res, rej) => promise.then(res).catch(rej));
1151
1206
  }
1152
1207
  from(promise) {
1153
1208
  const newPromise = PromiseProgress.from(promise);
1154
- this.onProgress((p) => newPromise.progress = p);
1209
+ this.onProgress((m) => newPromise.update(m.size, m.total));
1155
1210
  return newPromise;
1156
1211
  }
1157
1212
  onProgress(callback) {
1158
- this.listeners.push(callback);
1213
+ (this.listeners ||= []).push(callback);
1159
1214
  return this;
1160
1215
  }
1161
1216
  then(res, rej) {
1162
- const resp = super.then(res, rej);
1163
- return this.from(resp);
1217
+ return this.from(super.then(res, rej));
1164
1218
  }
1165
1219
  catch(rej) {
1166
1220
  return this.from(super.catch(rej));
@@ -1209,7 +1263,7 @@ function uploadWithProgress(options) {
1209
1263
  const formData2 = new FormData();
1210
1264
  options.files.forEach((f) => formData2.append("files", f));
1211
1265
  xhr.withCredentials = !!options.withCredentials;
1212
- xhr.upload.addEventListener("progress", (event) => event.lengthComputable ? prog(event.loaded / event.total) : null);
1266
+ xhr.upload.addEventListener("progress", (event) => event.lengthComputable ? prog(event.loaded, event.total) : null);
1213
1267
  xhr.addEventListener("loadend", () => res(JSONAttemptParse(xhr.responseText)));
1214
1268
  xhr.addEventListener("error", () => rej(JSONAttemptParse(xhr.responseText)));
1215
1269
  xhr.addEventListener("timeout", () => rej({ error: "Request timed out" }));
@@ -2046,6 +2100,24 @@ var Actions = class extends AssetController {
2046
2100
  };
2047
2101
  //#endregion
2048
2102
  //#region src/ai.ts
2103
+ var TEXT_FILE = /^text\/|\.(txt|md|csv|json|xml|ya?ml|html?|js|ts|py|log)$/i;
2104
+ async function toFileUpload(f) {
2105
+ if (TEXT_FILE.test(f.type) || TEXT_FILE.test(f.name)) return {
2106
+ name: f.name,
2107
+ mime: f.type,
2108
+ content: await f.text()
2109
+ };
2110
+ const dataUrl = await new Promise((res) => {
2111
+ const reader = new FileReader();
2112
+ reader.onload = () => res(reader.result);
2113
+ reader.readAsDataURL(f);
2114
+ });
2115
+ return {
2116
+ name: f.name,
2117
+ mime: f.type,
2118
+ content: dataUrl.split(",")[1]
2119
+ };
2120
+ }
2049
2121
  /** Manage and render templates */
2050
2122
  var Agents = class extends AssetController {
2051
2123
  momentum;
@@ -2059,6 +2131,103 @@ var Agents = class extends AssetController {
2059
2131
  }
2060
2132
  };
2061
2133
  /** Manage and render templates */
2134
+ var Conversations = class extends AssetController {
2135
+ momentum;
2136
+ constructor(momentum) {
2137
+ super(momentum, {
2138
+ module: "ai",
2139
+ path: "ai/conversations",
2140
+ key: "_id"
2141
+ });
2142
+ this.momentum = momentum;
2143
+ }
2144
+ /** List all conversations; omit owner to list the current session's own */
2145
+ list(owner, reload) {
2146
+ if (!owner) return super.all(reload);
2147
+ return this.momentum.api.request({ url: `api/${this.opts.path}/${owner}` }).then((resp) => {
2148
+ this.emit(PES`${this.opts.module}/${owner}:r`, resp);
2149
+ return resp;
2150
+ });
2151
+ }
2152
+ /** Fetch a single conversation, including full history */
2153
+ read(id, reload) {
2154
+ const cached = !reload ? this.cache.find({ _id: id }) : null;
2155
+ if (cached) return Promise.resolve(cached);
2156
+ return this.momentum.api.request({ url: `api/${this.opts.path}/${id}/history` }).then((resp) => {
2157
+ this.cache.add(resp);
2158
+ this.emit(PES`${this.opts.module}/${id}:r`, resp);
2159
+ return resp;
2160
+ });
2161
+ }
2162
+ /** Create a new conversation; pass owner to create on behalf of another user */
2163
+ create(value = {}, owner) {
2164
+ const url = owner ? `api/${this.opts.path}/${owner}` : `api/${this.opts.path}`;
2165
+ return this.momentum.api.request({
2166
+ url,
2167
+ method: "POST",
2168
+ body: { title: value.title }
2169
+ }).then((resp) => {
2170
+ this.cache.add(resp);
2171
+ this.emit(PES`${this.opts.module}/${resp._id}:c`, resp);
2172
+ return resp;
2173
+ });
2174
+ }
2175
+ /** Delete a conversation */
2176
+ async delete(id) {
2177
+ await this.momentum.api.request({
2178
+ url: `api/${this.opts.path}/${id}`,
2179
+ method: "DELETE"
2180
+ });
2181
+ this.cache.delete(id);
2182
+ this.emit(PES`${this.opts.module}/${id}:d`, id);
2183
+ return 1;
2184
+ }
2185
+ /** Rename and/or pin a conversation */
2186
+ rename(id, title) {
2187
+ return this.update({
2188
+ _id: id,
2189
+ title
2190
+ });
2191
+ }
2192
+ /** Pin/unpin a conversation */
2193
+ pin(id, pinned = true) {
2194
+ return this.update({
2195
+ _id: id,
2196
+ pinned
2197
+ });
2198
+ }
2199
+ /** Cancel any in-flight request for this conversation */
2200
+ abort(id) {
2201
+ return this.momentum.api.request({
2202
+ url: `api/${this.opts.path}/${id}/abort`,
2203
+ method: "POST"
2204
+ });
2205
+ }
2206
+ /** Delete a message and everything that follows it from a conversation's history (no LLM call) */
2207
+ deleteMessage(id, index) {
2208
+ return this.momentum.api.request({
2209
+ url: `api/${this.opts.path}/${id}/${index}`,
2210
+ method: "DELETE"
2211
+ }).then((resp) => {
2212
+ this.cache.add(resp);
2213
+ this.emit(PES`${this.opts.module}/${id}:u`, resp);
2214
+ return resp;
2215
+ });
2216
+ }
2217
+ /** Fork a conversation's history up to `index` into a new conversation titled `Forked: <title>` */
2218
+ fork(id, index) {
2219
+ return this.momentum.api.request({
2220
+ url: `api/${this.opts.path}/${id}/fork`,
2221
+ method: "POST",
2222
+ body: { index }
2223
+ }).then((resp) => {
2224
+ this.cache.add(resp);
2225
+ this.emit(PES`${this.opts.module}/${resp._id}:c`, resp);
2226
+ return resp;
2227
+ });
2228
+ }
2229
+ };
2230
+ /** Manage and render templates */
2062
2231
  var Skills = class extends AssetController {
2063
2232
  momentum;
2064
2233
  constructor(momentum) {
@@ -2074,49 +2243,41 @@ var Skills = class extends AssetController {
2074
2243
  var Ai = class extends PathEventEmitter {
2075
2244
  momentum;
2076
2245
  agents;
2246
+ conversations;
2077
2247
  skills;
2078
2248
  constructor(momentum) {
2079
2249
  super("ai");
2080
2250
  this.momentum = momentum;
2081
2251
  this.agents = new Agents(momentum);
2252
+ this.conversations = new Conversations(momentum);
2082
2253
  this.skills = new Skills(momentum);
2083
2254
  this.relayEvents(this.agents);
2255
+ this.relayEvents(this.conversations);
2084
2256
  this.relayEvents(this.skills);
2085
2257
  }
2086
- /** Cancel current AI requests */
2087
- abort() {
2088
- return this.momentum.api.request({
2089
- url: "/api/ai/abort",
2090
- method: "POST"
2091
- });
2092
- }
2093
2258
  /**
2094
2259
  * Ask the AI assistant a question
2095
2260
  * @param {string} question Users question
2096
- * @param {AiOptions & {stream: Function}} options context - Any hidden context information. stream - Receive response in chunks
2261
+ * @param {AiOptions & {stream: Function}} options Pass a conversationId to persist/continue a thread; omit for a one-off.
2262
+ * Pass messageIndex to edit (new text) or retry (same text) a prior exchange - history is truncated to that point first.
2097
2263
  * @return {Promise<string>} AI's response
2098
2264
  */
2099
2265
  async ask(question, options = {}) {
2100
2266
  if (!question) throw new Error("Cannot ask AI, missing question");
2101
- const files = (await Promise.all((options?.files || []).map((f) => new Promise((res) => {
2102
- const reader = new FileReader();
2103
- reader.onload = () => res([f.name, reader.result]);
2104
- reader.readAsDataURL(f);
2105
- })))).reduce((acc, f) => ({
2106
- ...acc,
2107
- [f[0]]: f[1]
2108
- }), {});
2267
+ const { stream, files: rawFiles, ...rest } = options;
2268
+ const files = await Promise.all((rawFiles || []).map(toFileUpload));
2109
2269
  const q = {
2110
- ...options,
2270
+ ...rest,
2111
2271
  question: question.trim(),
2112
- files: Object.keys(files).length ? files : void 0
2272
+ files: files.length ? files : void 0
2113
2273
  };
2114
- if (options.stream && this.momentum.socket?.connected) {
2274
+ if (!q.conversationId) delete q.conversationId;
2275
+ if (stream && this.momentum.socket?.connected) {
2115
2276
  let unsub = () => {};
2116
2277
  return new Promise((res) => {
2117
2278
  let buffer = "";
2118
2279
  unsub = this.momentum.socket?.on("llm", (event, chunk) => {
2119
- options.stream?.(chunk);
2280
+ stream(chunk);
2120
2281
  if (chunk.text) buffer += chunk.text;
2121
2282
  if (chunk.done) res(buffer);
2122
2283
  });
@@ -2132,34 +2293,190 @@ var Ai = class extends PathEventEmitter {
2132
2293
  });
2133
2294
  }
2134
2295
  /**
2135
- * Clear AI assistant memory & context
2136
- * @return {Promise<void>} Resolves once complete
2137
- */
2138
- clear() {
2139
- return this.momentum.api.request({
2140
- url: "api/ai",
2141
- method: "DELETE"
2142
- }).then(() => this.emit(PES`:d`, this.momentum.api.token));
2143
- }
2144
- /**
2145
- * Current chat history
2146
- * @return {Promise<AiMessage[]>}
2147
- */
2148
- history() {
2149
- return this.momentum.api.request({ url: "api/ai" }).then((resp) => {
2150
- this.emit(PES`:r`, resp);
2151
- return resp;
2152
- });
2153
- }
2154
- /**
2155
2296
  * Get model info
2156
- * @return {AiInfo>} Model Info
2297
+ * @return {Promise<{[name: string]: string}>} Model Info
2157
2298
  */
2158
2299
  tools() {
2159
2300
  return this.momentum.api.request({ url: "api/ai/tools" });
2160
2301
  }
2161
2302
  };
2162
2303
  //#endregion
2304
+ //#region src/captcha.ts
2305
+ var Captcha = class {
2306
+ start = Date.now();
2307
+ events = {
2308
+ mouse: [],
2309
+ keys: [],
2310
+ clicks: [],
2311
+ scroll: [],
2312
+ focus: []
2313
+ };
2314
+ lastMouse = null;
2315
+ velocities = [];
2316
+ _score = .5;
2317
+ onScoreChange;
2318
+ set score(v) {
2319
+ const next = +Math.max(0, Math.min(1, Number.isFinite(v) ? v : .5)).toFixed(2);
2320
+ if (next == this._score) return;
2321
+ this._score = next;
2322
+ this.onScoreChange?.(next);
2323
+ }
2324
+ get score() {
2325
+ return this._score;
2326
+ }
2327
+ get verdict() {
2328
+ if (this.score >= .45 && this.score <= .55) return "unknown";
2329
+ if (this.score > .5) return "human";
2330
+ return "bot";
2331
+ }
2332
+ constructor(onScoreChange) {
2333
+ this.onScoreChange = onScoreChange;
2334
+ if (typeof window === "undefined" || !document) {
2335
+ this._score = 0;
2336
+ return;
2337
+ }
2338
+ document.addEventListener("pointermove", (e) => {
2339
+ const now = Date.now();
2340
+ const curr = {
2341
+ x: e.clientX,
2342
+ y: e.clientY,
2343
+ t: now
2344
+ };
2345
+ this.events.mouse.push(curr);
2346
+ if (this.lastMouse) {
2347
+ const dt = now - this.lastMouse.t;
2348
+ const dx = curr.x - this.lastMouse.x;
2349
+ const dy = curr.y - this.lastMouse.y;
2350
+ const dist = Math.sqrt(dx * dx + dy * dy);
2351
+ if (dt > 0 && dt < 1e3) this.velocities.push(dist / dt);
2352
+ }
2353
+ this.lastMouse = curr;
2354
+ if (this.events.mouse.length > 80) this.events.mouse.shift();
2355
+ if (this.velocities.length > 40) this.velocities.shift();
2356
+ }, { passive: true });
2357
+ document.addEventListener("keydown", () => {
2358
+ this.events.keys.push(Date.now());
2359
+ if (this.events.keys.length > 30) this.events.keys.shift();
2360
+ this.update();
2361
+ });
2362
+ document.addEventListener("pointerdown", (e) => {
2363
+ this.events.clicks.push({
2364
+ x: e.clientX,
2365
+ y: e.clientY,
2366
+ t: Date.now()
2367
+ });
2368
+ if (this.events.clicks.length > 20) this.events.clicks.shift();
2369
+ this.update();
2370
+ }, { passive: true });
2371
+ window.addEventListener("scroll", () => {
2372
+ this.events.scroll.push({
2373
+ x: window.scrollX,
2374
+ y: window.scrollY,
2375
+ t: Date.now()
2376
+ });
2377
+ if (this.events.scroll.length > 20) this.events.scroll.shift();
2378
+ }, { passive: true });
2379
+ document.addEventListener("visibilitychange", () => {
2380
+ this.events.focus.push({
2381
+ visible: document.visibilityState == "visible",
2382
+ t: Date.now()
2383
+ });
2384
+ if (this.events.focus.length > 20) this.events.focus.shift();
2385
+ this.update();
2386
+ });
2387
+ window.addEventListener("focus", () => {
2388
+ this.events.focus.push({
2389
+ visible: true,
2390
+ t: Date.now()
2391
+ });
2392
+ this.update();
2393
+ });
2394
+ window.addEventListener("blur", () => {
2395
+ this.events.focus.push({
2396
+ visible: false,
2397
+ t: Date.now()
2398
+ });
2399
+ this.update();
2400
+ });
2401
+ setInterval(() => this.update(), 2e3);
2402
+ }
2403
+ update() {
2404
+ if (navigator?.webdriver) {
2405
+ this._score = 0;
2406
+ this.onScoreChange?.(0);
2407
+ return;
2408
+ }
2409
+ let delta = 0;
2410
+ const elapsed = (Date.now() - this.start) / 1e3;
2411
+ delta += elapsed > 30 ? .08 : elapsed > 10 ? .04 : elapsed > 3 ? .01 : -.05;
2412
+ if (this.events.mouse.length >= 6) {
2413
+ const recent = this.events.mouse.slice(-30);
2414
+ const buckets = new Array(8).fill(0);
2415
+ for (let i = 1; i < recent.length; i++) {
2416
+ const dx = recent[i].x - recent[i - 1].x;
2417
+ const dy = recent[i].y - recent[i - 1].y;
2418
+ if (dx == 0 && dy == 0) continue;
2419
+ let angle = Math.atan2(dy, dx);
2420
+ if (angle < 0) angle += Math.PI * 2;
2421
+ buckets[Math.min(7, Math.floor(angle / (Math.PI * 2) * 8))]++;
2422
+ }
2423
+ const total = buckets.reduce((a, b) => a + b, 0);
2424
+ if (total >= 4) {
2425
+ let entropy = 0;
2426
+ for (const count of buckets) {
2427
+ if (!count) continue;
2428
+ const p = count / total;
2429
+ entropy -= p * Math.log2(p);
2430
+ }
2431
+ entropy /= Math.log2(8);
2432
+ if (entropy > .65) delta += .12;
2433
+ else if (entropy > .4) delta += .05;
2434
+ else delta -= .07;
2435
+ }
2436
+ } else if (this.events.mouse.length == 0) delta -= .01;
2437
+ if (this.velocities.length >= 5) {
2438
+ const avg = this.velocities.reduce((a, b) => a + b, 0) / this.velocities.length;
2439
+ const variance = this.velocities.reduce((a, b) => a + Math.pow(b - avg, 2), 0) / this.velocities.length;
2440
+ const cv = avg > 0 ? Math.sqrt(variance) / avg : 0;
2441
+ if (cv > .45) delta += .1;
2442
+ else if (cv > .18) delta += .04;
2443
+ else if (avg > 0) delta -= .08;
2444
+ }
2445
+ if (this.events.clicks.length > 0) {
2446
+ const lastClick = this.events.clicks.at(-1);
2447
+ const mouseBeforeClick = this.events.mouse.some((m) => m.t < lastClick.t && lastClick.t - m.t < 2e3);
2448
+ delta += mouseBeforeClick ? .06 : -.08;
2449
+ const suspiciousClicks = this.events.clicks.filter((c) => Number.isInteger(c.x) && Number.isInteger(c.y) && c.x % 10 == 0 && c.y % 10 == 0).length;
2450
+ if (this.events.clicks.length >= 4 && suspiciousClicks / this.events.clicks.length > .5) delta -= .06;
2451
+ }
2452
+ if (this.events.keys.length >= 4) {
2453
+ const intervals = [];
2454
+ for (let i = 1; i < this.events.keys.length; i++) intervals.push(this.events.keys[i] - this.events.keys[i - 1]);
2455
+ const avg = intervals.reduce((a, b) => a + b, 0) / intervals.length;
2456
+ const stdDev = Math.sqrt(intervals.reduce((a, b) => a + Math.pow(b - avg, 2), 0) / intervals.length);
2457
+ if (avg < 20) delta -= .15;
2458
+ else if (stdDev < 5) delta -= .1;
2459
+ else if (avg < 50) delta -= .04;
2460
+ else if (avg < 1e3 && stdDev > 20) delta += .08;
2461
+ }
2462
+ if (this.events.scroll.length >= 4) {
2463
+ let directionChanges = 0;
2464
+ let previousDirection = 0;
2465
+ for (let i = 1; i < this.events.scroll.length; i++) {
2466
+ const direction = Math.sign(this.events.scroll[i].y - this.events.scroll[i - 1].y);
2467
+ if (direction && previousDirection && direction != previousDirection) directionChanges++;
2468
+ if (direction) previousDirection = direction;
2469
+ }
2470
+ if (directionChanges >= 2) delta += .04;
2471
+ }
2472
+ if (this.events.focus.length >= 2) {
2473
+ const visible = this.events.focus.filter((x) => x.visible).length;
2474
+ delta += visible / this.events.focus.length > .4 ? .02 : -.02;
2475
+ }
2476
+ this.score = this.score + delta * .25;
2477
+ }
2478
+ };
2479
+ //#endregion
2163
2480
  //#region src/dialog.ts
2164
2481
  var dialogCount = 0;
2165
2482
  var closed = 0;
@@ -2372,6 +2689,16 @@ function createDialog(html, opts = {}) {
2372
2689
  var Analytics = class extends AssetController {
2373
2690
  momentum;
2374
2691
  lastPage;
2692
+ captcha = new Captcha((score) => this.updateCaptcha(score));
2693
+ captchaSecret;
2694
+ captchaSequence = 0;
2695
+ captchaUpdate;
2696
+ get captchaScore() {
2697
+ return this.captcha.score;
2698
+ }
2699
+ get actorType() {
2700
+ return this.captcha.verdict;
2701
+ }
2375
2702
  _ready;
2376
2703
  ready = new Promise((res) => this._ready = res);
2377
2704
  _performance = {
@@ -2450,6 +2777,9 @@ var Analytics = class extends AssetController {
2450
2777
  this._id = "";
2451
2778
  this._consent = void 0;
2452
2779
  this._new = true;
2780
+ this.captchaSecret = void 0;
2781
+ this.captchaSequence = 0;
2782
+ delete this.momentum.api.headers["X-Captcha"];
2453
2783
  });
2454
2784
  this.momentum.auth.on("login:c", async () => {
2455
2785
  if (this.consent) await this.init();
@@ -2526,7 +2856,7 @@ var Analytics = class extends AssetController {
2526
2856
  async init() {
2527
2857
  if (!this.momentum.opts.analytics) return;
2528
2858
  const performance = await this.performance;
2529
- this.id = await this.momentum.api.request({
2859
+ const response = await this.momentum.api.request({
2530
2860
  url: `api/analytics`,
2531
2861
  body: [
2532
2862
  navigator?.language,
@@ -2544,6 +2874,10 @@ var Analytics = class extends AssetController {
2544
2874
  performance.total
2545
2875
  ]
2546
2876
  });
2877
+ this.id = response.id;
2878
+ this.captchaSecret = response.captchaSecret;
2879
+ this.captchaSequence = response.captchaSequence;
2880
+ await this.updateCaptcha(this.captcha.score, true);
2547
2881
  if (this._ready) {
2548
2882
  this._ready();
2549
2883
  this._ready = null;
@@ -2553,6 +2887,25 @@ var Analytics = class extends AssetController {
2553
2887
  window.addEventListener("beforeunload", () => this.page("CLOSED"));
2554
2888
  return this.id;
2555
2889
  }
2890
+ updateCaptcha(score, initial = false) {
2891
+ if (!this.captchaSecret || !this.id || !globalThis.crypto?.subtle) return Promise.resolve();
2892
+ const update = this.captchaUpdate?.catch(() => {}) || Promise.resolve();
2893
+ this.captchaUpdate = update.then(() => this.attestCaptcha(score, initial)).catch(() => {});
2894
+ return this.captchaUpdate;
2895
+ }
2896
+ async attestCaptcha(score, initial) {
2897
+ if (!this.captchaSecret || !this.id) return;
2898
+ if (!initial) this.captchaSequence++;
2899
+ const canonical = Math.max(0, Math.min(1, Number.isFinite(score) ? score : .5)).toFixed(2);
2900
+ const payload = `${canonical}:${this.captchaSequence}`;
2901
+ const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(this.captchaSecret), {
2902
+ name: "HMAC",
2903
+ hash: "SHA-256"
2904
+ }, false, ["sign"]);
2905
+ const signature = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(payload));
2906
+ const proof = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
2907
+ this.momentum.api.headers["X-Captcha"] = `${canonical},${this.captchaSequence},${proof}`;
2908
+ }
2556
2909
  async consentPrompt(content) {
2557
2910
  const settings = await this.momentum.settings.map();
2558
2911
  const { close, dialog, result } = createDialog(`
@@ -2926,19 +3279,29 @@ var Auth = class extends PathEventEmitter {
2926
3279
  loginRedirect(host = location.origin) {
2927
3280
  return new Promise((res, rej) => {
2928
3281
  let listener, win;
3282
+ const loginOrigin = new URL(this.momentum.opts.loginUrl, location.href).origin;
3283
+ const hostOrigin = new URL(host, location.href).origin;
2929
3284
  window.addEventListener("message", listener = async (event) => {
3285
+ if (event.origin !== loginOrigin || event?.data?.sender !== loginOrigin) return;
2930
3286
  const data = event?.data || {};
2931
- if (event.origin != origin || data.sender != origin) return;
2932
- if (!data.token) return rej("Unknown response from login");
3287
+ if (!data.token) {
3288
+ window.removeEventListener("message", listener);
3289
+ return rej(/* @__PURE__ */ new Error("Unknown response from login"));
3290
+ }
2933
3291
  window.removeEventListener("message", listener);
2934
- await this.readSession(data.token, true);
2935
- win.close();
2936
- res(data.token);
3292
+ try {
3293
+ await this.readSession(data.token, true);
3294
+ win?.close();
3295
+ res(data.token);
3296
+ } catch (err) {
3297
+ win?.close();
3298
+ rej(err);
3299
+ }
2937
3300
  });
2938
- win = window.open(`${this.momentum.opts.loginUrl}?redirect=postmessage&host=${encodeURI(host)}`, "_blank");
3301
+ win = window.open(`${this.momentum.opts.loginUrl}?redirect=postmessage&host=${encodeURIComponent(hostOrigin)}`, "_blank");
2939
3302
  if (!win) {
2940
3303
  window.removeEventListener("message", listener);
2941
- return rej("Unable to open login");
3304
+ return rej(/* @__PURE__ */ new Error("Unable to open login"));
2942
3305
  }
2943
3306
  });
2944
3307
  }
@@ -3064,174 +3427,20 @@ var Call = class extends PathEventEmitter {
3064
3427
  }
3065
3428
  };
3066
3429
  //#endregion
3067
- //#region src/captcha.ts
3068
- var Captcha = class {
3069
- start = Date.now();
3070
- events = {
3071
- mouse: [],
3072
- keys: [],
3073
- clicks: []
3074
- };
3075
- lastMouse = null;
3076
- velocities = [];
3077
- _score = .5;
3078
- set score(v) {
3079
- this._score = v;
3080
- }
3081
- get score() {
3082
- return this._score;
3083
- }
3084
- get verdict() {
3085
- if (this.score == .5) return "unknown";
3086
- if (this.score > .5) return "human";
3087
- return "bot";
3088
- }
3089
- constructor() {
3090
- if (typeof window === "undefined" || !window.innerWidth || !this.validUserAgent()) {
3091
- this.score = 0;
3092
- return;
3093
- }
3094
- document.addEventListener("mousemove", (e) => {
3095
- const now = Date.now();
3096
- const curr = {
3097
- x: e.clientX,
3098
- y: e.clientY,
3099
- t: now
3100
- };
3101
- this.events.mouse.push(curr);
3102
- if (this.lastMouse) {
3103
- const dt = now - this.lastMouse.t;
3104
- const dx = curr.x - this.lastMouse.x;
3105
- const dy = curr.y - this.lastMouse.y;
3106
- const dist = Math.sqrt(dx * dx + dy * dy);
3107
- if (dt > 0) this.velocities.push(dist / dt);
3108
- }
3109
- this.lastMouse = curr;
3110
- if (this.events.mouse.length > 50) this.events.mouse.shift();
3111
- if (this.velocities.length > 30) this.velocities.shift();
3112
- });
3113
- document.addEventListener("keydown", (e) => {
3114
- this.events.keys.push(Date.now());
3115
- if (this.events.keys.length > 20) this.events.keys.shift();
3116
- });
3117
- document.addEventListener("click", (e) => {
3118
- this.events.clicks.push({
3119
- x: e.clientX,
3120
- y: e.clientY,
3121
- t: Date.now()
3122
- });
3123
- if (this.events.clicks.length > 15) this.events.clicks.shift();
3124
- });
3125
- setInterval(() => this.update(), 1e3);
3126
- }
3127
- validUserAgent() {
3128
- if (!navigator?.userAgent) return false;
3129
- const ua = navigator.userAgent.toLowerCase();
3130
- if ([
3131
- "bot",
3132
- "crawler",
3133
- "spider",
3134
- "scraper",
3135
- "headless",
3136
- "phantom",
3137
- "selenium",
3138
- "webdriver",
3139
- "puppeteer",
3140
- "playwright",
3141
- "cypress",
3142
- "automation"
3143
- ].some((pattern) => ua.includes(pattern))) return false;
3144
- if (![
3145
- "chrome",
3146
- "firefox",
3147
- "safari",
3148
- "edge",
3149
- "opera"
3150
- ].some((browser) => ua.includes(browser))) return false;
3151
- if (![
3152
- "windows",
3153
- "mac",
3154
- "linux",
3155
- "android",
3156
- "ios"
3157
- ].some((os) => ua.includes(os))) return false;
3158
- return ua.length > 20 && ua.length < 1e3;
3159
- }
3160
- update() {
3161
- if (navigator.webdriver) {
3162
- this.score = 0;
3163
- return;
3164
- }
3165
- let delta = 0;
3166
- const elapsed = (Date.now() - this.start) / 1e3;
3167
- delta += elapsed > 30 ? .08 : elapsed > 10 ? .04 : elapsed > 3 ? .01 : -.05;
3168
- if (this.events.mouse.length >= 5) {
3169
- const recent = this.events.mouse.slice(-20);
3170
- let changes = 0;
3171
- let maxStraightRun = 0;
3172
- let currentRun = 0;
3173
- for (let i = 2; i < recent.length; i++) {
3174
- const a1 = Math.atan2(recent[i - 1].y - recent[i - 2].y, recent[i - 1].x - recent[i - 2].x);
3175
- const a2 = Math.atan2(recent[i].y - recent[i - 1].y, recent[i].x - recent[i - 1].x);
3176
- if (Math.abs(a1 - a2) > .3) {
3177
- changes++;
3178
- currentRun = 0;
3179
- } else {
3180
- currentRun++;
3181
- maxStraightRun = Math.max(maxStraightRun, currentRun);
3182
- }
3183
- }
3184
- const entropy = changes / (recent.length - 2);
3185
- if (entropy > .4) delta += .12;
3186
- else if (entropy > .2) delta += .05;
3187
- else delta -= .08;
3188
- if (maxStraightRun > 8) delta -= .1;
3189
- } else if (this.events.mouse.length === 0) delta -= .03;
3190
- if (this.velocities.length >= 5) {
3191
- const avg = this.velocities.reduce((a, b) => a + b, 0) / this.velocities.length;
3192
- const variance = this.velocities.reduce((a, b) => a + Math.pow(b - avg, 2), 0) / this.velocities.length;
3193
- const cv = avg > 0 ? Math.sqrt(variance) / avg : 0;
3194
- if (cv > .4) delta += .1;
3195
- else if (cv > .15) delta += .04;
3196
- else if (avg > 0) delta -= .1;
3197
- }
3198
- if (this.events.clicks.length > 0) {
3199
- const lastClick = this.events.clicks[this.events.clicks.length - 1];
3200
- const mouseBeforeClick = this.events.mouse.some((m) => m.t < lastClick.t && lastClick.t - m.t < 2e3);
3201
- delta += mouseBeforeClick ? .08 : -.12;
3202
- if (this.events.clicks.filter((c) => Number.isInteger(c.x) && Number.isInteger(c.y) && c.x % 10 === 0 && c.y % 10 === 0).length / this.events.clicks.length > .5) delta -= .08;
3203
- }
3204
- if (this.events.keys.length >= 3) {
3205
- const intervals = [];
3206
- for (let i = 1; i < this.events.keys.length; i++) intervals.push(this.events.keys[i] - this.events.keys[i - 1]);
3207
- const avg = intervals.reduce((a, b) => a + b, 0) / intervals.length;
3208
- const stdDev = Math.sqrt(intervals.reduce((a, b) => a + Math.pow(b - avg, 2), 0) / intervals.length);
3209
- if (avg < 20) delta -= .15;
3210
- else if (stdDev < 5) delta -= .12;
3211
- else if (avg < 50) delta -= .05;
3212
- else if (avg < 1e3 && stdDev > 20) delta += .1;
3213
- }
3214
- const raw = this.score + delta;
3215
- this.score = this.score * .75 + raw * .25;
3216
- this.score = Math.max(.05, Math.min(.95, this.score));
3217
- }
3218
- };
3219
- //#endregion
3220
3430
  //#region src/client.ts
3221
3431
  /** Handle device settings */
3222
3432
  var Client = class Client extends PathEventEmitter {
3223
3433
  momentum;
3224
- captcha = new Captcha();
3225
3434
  installTimeout;
3226
3435
  nativePwaPrompt;
3227
3436
  pendingInstall = null;
3228
3437
  speed;
3229
3438
  updating = false;
3230
3439
  get actorType() {
3231
- return this.captcha.verdict;
3440
+ return this.momentum.analytics.actorType;
3232
3441
  }
3233
3442
  get captchaScore() {
3234
- return this.captcha.score;
3443
+ return this.momentum.analytics.captchaScore;
3235
3444
  }
3236
3445
  /** Check for updates */
3237
3446
  updateReady = new Promise((res) => {
@@ -3377,36 +3586,16 @@ var Client = class Client extends PathEventEmitter {
3377
3586
  const t = {
3378
3587
  background: setting?.background,
3379
3588
  backgroundContrast: contrast(setting?.background),
3380
- surface: setting?.darkMode ? "#2e2e2e" : "#ffffff",
3381
- border: setting?.darkMode ? "#374151" : "#E5E7EB",
3382
- text: setting?.darkMode ? "#ffffff" : "#000000",
3383
- muted: setting?.darkMode ? "#cccccc" : "#6c757d",
3384
3589
  primary: setting?.primary,
3385
3590
  primaryContrast: contrast(setting?.primary),
3386
3591
  accent: setting?.accent,
3387
3592
  accentContrast: contrast(setting?.accent),
3388
- info: "#6157cc",
3389
- success: "#65c147",
3390
- warn: "#d88750",
3391
- danger: "#bf4f4f",
3392
- black: "black",
3393
- white: "white",
3394
3593
  font: setting?.font,
3395
- lighter: setting?.darkMode ? "rgba(255, 255, 255, 0.1)" : "rgba(255, 255, 255, 0.05)",
3396
- darker: setting?.darkMode ? "rgba(0, 0, 0, 0.1)" : "rgba(0, 0, 0, 0.05)"
3594
+ radius: "0.5rem"
3397
3595
  };
3398
3596
  if (!css) return t;
3399
- return `:root, :root .theme-${setting?.darkMode ? "dark" : "light"} {
3597
+ return `:root {
3400
3598
  ${Object.entries(t).map(([name, value]) => `--theme-${kebabCase(name)}: ${value} !important;`).join("\n")}
3401
- }
3402
-
3403
- :root .theme-${!setting?.darkMode ? "dark" : "light"} {
3404
- --theme-surface: ${!setting?.darkMode ? "#2e2e2e" : "#ffffff"} !important;
3405
- --theme-border: ${!setting?.darkMode ? "#374151" : "#E5E7EB"} !important;
3406
- --theme-text: ${!setting?.darkMode ? "#ffffff" : "#000000"} !important;
3407
- --theme-muted: ${!setting?.darkMode ? "#cccccc" : "#6c757d"} !important;
3408
- --theme-lighter: ${!setting?.darkMode ? "rgba(255, 255, 255, 0.1)" : "rgba(255, 255, 255, 0.05)"};
3409
- --theme-darker: ${!setting?.darkMode ? "rgba(0, 0, 0, 0.1)" : "rgba(0, 0, 0, 0.05)"};
3410
3599
  }`;
3411
3600
  }
3412
3601
  /**
@@ -4770,7 +4959,7 @@ var Storage = class extends PathEventEmitter {
4770
4959
  headers: this.momentum.api.headers,
4771
4960
  files: f
4772
4961
  }).onProgress((p) => {
4773
- prog(p);
4962
+ prog(p.progress);
4774
4963
  }).then((resp) => {
4775
4964
  this.emit(PES`${this.module}/${path}:c`, resp);
4776
4965
  res(resp);
@@ -4948,7 +5137,7 @@ var Static = class extends Storage {
4948
5137
  };
4949
5138
  //#endregion
4950
5139
  //#region package.json
4951
- var version = "1.5.6";
5140
+ var version = "1.5.8";
4952
5141
  //#endregion
4953
5142
  //#region src/webRtc.ts
4954
5143
  var WebRtc = class extends PathEventEmitter {
@@ -5271,4 +5460,4 @@ var Momentum = class Momentum extends PathEventEmitter {
5271
5460
  }
5272
5461
  };
5273
5462
  //#endregion
5274
- export { ActionType, Actions, Agents, Ai, Analytics, Api, Audit, Auth, Blacklist, Call, Captcha, Client, Data, Discounts, Email, Forms, Groups, Logger, Momentum, Notifications, PE, PES, PathEvent, PathEventEmitter, Pdf, Permissions, Products, RateLimiter, Routes, Schemas, Settings, Skills, Sms, Socket, Static, Storage, Templates, Tokens, Totp, Transactions, Users, WebRtc, treeBuilder, validEvent };
5463
+ export { ActionType, Actions, Agents, Ai, Analytics, Api, Audit, Auth, Blacklist, Call, Captcha, Client, Conversations, Data, Discounts, Email, Forms, Groups, Logger, Momentum, Notifications, PE, PES, PathEvent, PathEventEmitter, Pdf, Permissions, Products, RateLimiter, Routes, Schemas, Settings, Skills, Sms, Socket, Static, Storage, Templates, Tokens, Totp, Transactions, Users, WebRtc, treeBuilder, validEvent };