@leemour/max-cli 0.7.0 → 0.9.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 +198 -79
- package/dist/cache/schema.d.ts +13 -8
- package/dist/cache/schema.d.ts.map +1 -1
- package/dist/cache/schema.js +84 -23
- package/dist/cache/schema.js.map +1 -1
- package/dist/cache/store.d.ts +15 -0
- package/dist/cache/store.d.ts.map +1 -1
- package/dist/cache/store.js +32 -3
- package/dist/cache/store.js.map +1 -1
- package/dist/client.d.ts +28 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +112 -13
- package/dist/client.js.map +1 -1
- package/dist/commands/chats.d.ts.map +1 -1
- package/dist/commands/chats.js +8 -0
- package/dist/commands/chats.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +6 -2
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/context.d.ts +14 -2
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +18 -19
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/export.d.ts +3 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +83 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +9 -2
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/messages.d.ts.map +1 -1
- package/dist/commands/messages.js +126 -3
- package/dist/commands/messages.js.map +1 -1
- package/dist/commands/models.d.ts +7 -0
- package/dist/commands/models.d.ts.map +1 -0
- package/dist/commands/models.js +47 -0
- package/dist/commands/models.js.map +1 -0
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +50 -2
- package/dist/commands/serve.js.map +1 -1
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +13 -3
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +12 -16
- package/dist/commands/watch.js.map +1 -1
- package/dist/config.d.ts +18 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +58 -4
- package/dist/config.js.map +1 -1
- package/dist/domain/map.d.ts.map +1 -1
- package/dist/domain/map.js +5 -0
- package/dist/domain/map.js.map +1 -1
- package/dist/domain/models.d.ts +14 -0
- package/dist/domain/models.d.ts.map +1 -1
- package/dist/domain/models.js.map +1 -1
- package/dist/download.d.ts +2 -0
- package/dist/download.d.ts.map +1 -1
- package/dist/download.js +11 -0
- package/dist/download.js.map +1 -1
- package/dist/export.d.ts +26 -0
- package/dist/export.d.ts.map +1 -0
- package/dist/export.js +75 -0
- package/dist/export.js.map +1 -0
- package/dist/generated/client.generated.d.ts +2 -0
- package/dist/generated/client.generated.d.ts.map +1 -1
- package/dist/generated/client.generated.js +2 -0
- package/dist/generated/client.generated.js.map +1 -1
- package/dist/generated/opcodes.generated.d.ts +0 -9
- package/dist/generated/opcodes.generated.d.ts.map +1 -1
- package/dist/generated/opcodes.generated.js +0 -9
- package/dist/generated/opcodes.generated.js.map +1 -1
- package/dist/generated/operations.generated.d.ts +24 -6
- package/dist/generated/operations.generated.d.ts.map +1 -1
- package/dist/generated/operations.generated.js +4 -2
- package/dist/generated/operations.generated.js.map +1 -1
- package/dist/mcp/confirm.d.ts +7 -0
- package/dist/mcp/confirm.d.ts.map +1 -1
- package/dist/mcp/confirm.js +16 -7
- package/dist/mcp/confirm.js.map +1 -1
- package/dist/mcp/instructions.d.ts +5 -1
- package/dist/mcp/instructions.d.ts.map +1 -1
- package/dist/mcp/instructions.js +16 -2
- package/dist/mcp/instructions.js.map +1 -1
- package/dist/mcp/server.d.ts +3 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +22 -3
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/session.d.ts +1 -1
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +1 -1
- package/dist/mcp/session.js.map +1 -1
- package/dist/mcp/tools.d.ts +8 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +87 -10
- package/dist/mcp/tools.js.map +1 -1
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +6 -2
- package/dist/program.js.map +1 -1
- package/dist/protocol/connection.d.ts +2 -1
- package/dist/protocol/connection.d.ts.map +1 -1
- package/dist/protocol/connection.js +45 -14
- package/dist/protocol/connection.js.map +1 -1
- package/dist/protocol/frame.d.ts +22 -14
- package/dist/protocol/frame.d.ts.map +1 -1
- package/dist/protocol/frame.js +116 -37
- package/dist/protocol/frame.js.map +1 -1
- package/dist/protocol/lz4.d.ts +15 -0
- package/dist/protocol/lz4.d.ts.map +1 -0
- package/dist/protocol/lz4.js +154 -0
- package/dist/protocol/lz4.js.map +1 -0
- package/dist/sends/guard.d.ts +7 -3
- package/dist/sends/guard.d.ts.map +1 -1
- package/dist/sends/guard.js +32 -11
- package/dist/sends/guard.js.map +1 -1
- package/dist/sends/journal.d.ts +6 -1
- package/dist/sends/journal.d.ts.map +1 -1
- package/dist/sends/journal.js.map +1 -1
- package/dist/sends/permissions.d.ts +11 -0
- package/dist/sends/permissions.d.ts.map +1 -0
- package/dist/sends/permissions.js +47 -0
- package/dist/sends/permissions.js.map +1 -0
- package/dist/server/lines.d.ts +4 -0
- package/dist/server/lines.d.ts.map +1 -1
- package/dist/server/lines.js +11 -2
- package/dist/server/lines.js.map +1 -1
- package/dist/server/server-connection.d.ts +25 -15
- package/dist/server/server-connection.d.ts.map +1 -1
- package/dist/server/server-connection.js +69 -63
- package/dist/server/server-connection.js.map +1 -1
- package/dist/server/server.d.ts +7 -0
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +84 -19
- package/dist/server/server.js.map +1 -1
- package/dist/server/start.d.ts +23 -1
- package/dist/server/start.d.ts.map +1 -1
- package/dist/server/start.js +61 -7
- package/dist/server/start.js.map +1 -1
- package/dist/session/adopt.d.ts.map +1 -1
- package/dist/session/adopt.js +4 -0
- package/dist/session/adopt.js.map +1 -1
- package/dist/session/handshake.d.ts +5 -3
- package/dist/session/handshake.d.ts.map +1 -1
- package/dist/session/handshake.js +3 -3
- package/dist/session/handshake.js.map +1 -1
- package/dist/spec/identity.d.ts +5 -4
- package/dist/spec/identity.d.ts.map +1 -1
- package/dist/spec/identity.js +5 -4
- package/dist/spec/identity.js.map +1 -1
- package/dist/spec/index.js +4 -4
- package/dist/spec/index.js.map +1 -1
- package/dist/spec/operations/chats.d.ts +16 -6
- package/dist/spec/operations/chats.d.ts.map +1 -1
- package/dist/spec/operations/chats.js +38 -9
- package/dist/spec/operations/chats.js.map +1 -1
- package/dist/spec/operations/messages.d.ts +13 -1
- package/dist/spec/operations/messages.d.ts.map +1 -1
- package/dist/spec/operations/messages.js +18 -4
- package/dist/spec/operations/messages.js.map +1 -1
- package/dist/spec/operations/session.d.ts +5 -4
- package/dist/spec/operations/session.d.ts.map +1 -1
- package/dist/spec/operations/session.js +7 -6
- package/dist/spec/operations/session.js.map +1 -1
- package/dist/spec/scalars.js +2 -2
- package/dist/testing/mock-max.d.ts +5 -1
- package/dist/testing/mock-max.d.ts.map +1 -1
- package/dist/testing/mock-max.js +6 -3
- package/dist/testing/mock-max.js.map +1 -1
- package/dist/transcribe/index.d.ts +34 -0
- package/dist/transcribe/index.d.ts.map +1 -0
- package/dist/transcribe/index.js +43 -0
- package/dist/transcribe/index.js.map +1 -0
- package/dist/transcribe/install.d.ts +18 -0
- package/dist/transcribe/install.d.ts.map +1 -0
- package/dist/transcribe/install.js +67 -0
- package/dist/transcribe/install.js.map +1 -0
- package/dist/transcribe/models.d.ts +32 -0
- package/dist/transcribe/models.d.ts.map +1 -0
- package/dist/transcribe/models.js +124 -0
- package/dist/transcribe/models.js.map +1 -0
- package/dist/transcribe/speech.d.ts +25 -0
- package/dist/transcribe/speech.d.ts.map +1 -0
- package/dist/transcribe/speech.js +105 -0
- package/dist/transcribe/speech.js.map +1 -0
- package/dist/update.d.ts +1 -11
- package/dist/update.d.ts.map +1 -1
- package/dist/update.js +14 -42
- package/dist/update.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +9 -3
- package/skills/max-cli/SKILL.md +19 -3
package/dist/protocol/frame.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The binary protocol web.max.ru speaks, captured from a live socket on 2026-09-25
|
|
3
|
+
* (`src/testing/fixtures/web-capture-2026-09-25.json`). JSON on protocol 11 was what we used to
|
|
4
|
+
* send, and nothing the web client sends looks like it.
|
|
5
|
+
*/
|
|
6
|
+
export declare const PROTOCOL_VERSION = 10;
|
|
3
7
|
export declare const Command: {
|
|
4
8
|
readonly REQUEST: 0;
|
|
5
9
|
readonly RESPONSE: 1;
|
|
@@ -11,29 +15,33 @@ export type Payload = Record<string, unknown>;
|
|
|
11
15
|
export interface OutboundFrame {
|
|
12
16
|
seq: number;
|
|
13
17
|
opcode: number;
|
|
14
|
-
/** Absent
|
|
18
|
+
/** Absent, or empty, goes out as a zero-length body — as the web client's ping answer does. */
|
|
15
19
|
payload?: Payload;
|
|
16
20
|
cmd?: Command;
|
|
17
21
|
}
|
|
18
22
|
export interface InboundFrame {
|
|
19
23
|
ver: number;
|
|
20
24
|
cmd: number;
|
|
21
|
-
seq: number
|
|
25
|
+
seq: number;
|
|
22
26
|
opcode: number;
|
|
23
27
|
payload: Payload | null;
|
|
24
28
|
}
|
|
25
|
-
|
|
29
|
+
/** `ver` `cmd` `seq:u16` `opcode:u16` `flags:u8` `length:u24`, big-endian. */
|
|
30
|
+
export declare const HEADER_BYTES = 10;
|
|
31
|
+
export declare const SEQ_MODULO = 65536;
|
|
32
|
+
export declare const encodeFrame: (frame: OutboundFrame) => Uint8Array;
|
|
33
|
+
/** The header alone, so a body that cannot be read still says which request it answered. */
|
|
34
|
+
export declare const decodeHeader: (bytes: Uint8Array) => Omit<InboundFrame, "payload"> & {
|
|
35
|
+
flags: number;
|
|
36
|
+
length: number;
|
|
37
|
+
};
|
|
26
38
|
/**
|
|
27
|
-
* **
|
|
28
|
-
*
|
|
29
|
-
* `
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* ⚠ Measured 2026-09-19: **chat ids on the owner's account reach 14 digits, not 19**, and contact
|
|
33
|
-
* ids reach 9 — all comfortably inside a number. The hazard is real and it is the message ids that
|
|
34
|
-
* carry it; a 19-digit chat id is an illustration, not something seen.
|
|
39
|
+
* **Integers come back as `number` unless a number would lose digits.** MessagePack hands every
|
|
40
|
+
* 64-bit integer over as a `bigint`, and MAX sends times as 64-bit — a `bigint` timestamp breaks
|
|
41
|
+
* every `Date` and every subtraction above this layer. Message ids are 18 digits, past
|
|
42
|
+
* `Number.MAX_SAFE_INTEGER`, and stay `bigint`; every id leaves the domain mapper as a string.
|
|
35
43
|
*/
|
|
36
|
-
export declare const decodeFrame: (
|
|
44
|
+
export declare const decodeFrame: (bytes: Uint8Array) => InboundFrame;
|
|
37
45
|
export declare class FrameError extends Error {
|
|
38
46
|
constructor(what: string);
|
|
39
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../../src/protocol/frame.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../../src/protocol/frame.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAElC,eAAO,MAAM,OAAO;;;;;CAKV,CAAA;AACV,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,OAAO,OAAO,CAAC,CAAA;AAE5D,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAE7C,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,+FAA+F;IAC/F,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,GAAG,CAAC,EAAE,OAAO,CAAA;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;CACxB;AAED,8EAA8E;AAC9E,eAAO,MAAM,YAAY,KAAK,CAAA;AAS9B,eAAO,MAAM,UAAU,QAAW,CAAA;AAKlC,eAAO,MAAM,WAAW,GAAI,OAAO,aAAa,KAAG,UAwBlD,CAAA;AAED,4FAA4F;AAC5F,eAAO,MAAM,YAAY,GAAI,OAAO,UAAU,KAAG,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAgB/G,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,UAAU,KAAG,YAW/C,CAAA;AAED,qBAAa,UAAW,SAAQ,KAAK;gBACvB,IAAI,EAAE,MAAM;CAIzB;AAED;;;GAGG;AACH,eAAO,MAAM,IAAI,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,SAK9C,CAAA"}
|
package/dist/protocol/frame.js
CHANGED
|
@@ -1,42 +1,89 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { decode, ExtData, ExtensionCodec, encode } from "@msgpack/msgpack";
|
|
2
|
+
import { compressBlock, decompressBlock } from "./lz4.js";
|
|
3
|
+
/**
|
|
4
|
+
* The binary protocol web.max.ru speaks, captured from a live socket on 2026-09-25
|
|
5
|
+
* (`src/testing/fixtures/web-capture-2026-09-25.json`). JSON on protocol 11 was what we used to
|
|
6
|
+
* send, and nothing the web client sends looks like it.
|
|
7
|
+
*/
|
|
8
|
+
export const PROTOCOL_VERSION = 10;
|
|
4
9
|
export const Command = {
|
|
5
10
|
REQUEST: 0,
|
|
6
11
|
RESPONSE: 1,
|
|
7
12
|
EVENT: 2,
|
|
8
13
|
ERROR: 3,
|
|
9
14
|
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
/** `ver` `cmd` `seq:u16` `opcode:u16` `flags:u8` `length:u24`, big-endian. */
|
|
16
|
+
export const HEADER_BYTES = 10;
|
|
17
|
+
/** The web client sends 31 bytes plain and 37 compressed; where between them it switches was not seen. */
|
|
18
|
+
const COMPRESS_FROM = 32;
|
|
19
|
+
const ZSTD = 0xff;
|
|
20
|
+
const MAX_LZ4_FLAGS = 0x7f;
|
|
21
|
+
/** MAX wraps a number it means as a 64-bit id or time in extension type 1; so does the web client. */
|
|
22
|
+
const WRAPPED = 1;
|
|
23
|
+
export const SEQ_MODULO = 0x1_0000;
|
|
24
|
+
const wrapped = new ExtensionCodec();
|
|
25
|
+
wrapped.register({ type: WRAPPED, encode: () => null, decode: (data) => decodeBody(data) });
|
|
26
|
+
export const encodeFrame = (frame) => {
|
|
27
|
+
const raw = frame.payload && Object.keys(frame.payload).length > 0 ? encodeBody(frame.payload) : new Uint8Array(0);
|
|
28
|
+
let body = raw;
|
|
29
|
+
let flags = 0;
|
|
30
|
+
if (raw.length >= COMPRESS_FROM) {
|
|
31
|
+
const compressed = compressBlock(raw);
|
|
32
|
+
const factor = Math.ceil(raw.length / compressed.length);
|
|
33
|
+
if (factor <= MAX_LZ4_FLAGS) {
|
|
34
|
+
// Kept even when it came out larger: the web client does the same (47 bytes sent as 49).
|
|
35
|
+
body = compressed;
|
|
36
|
+
flags = factor;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const bytes = new Uint8Array(HEADER_BYTES + body.length);
|
|
40
|
+
const view = new DataView(bytes.buffer);
|
|
41
|
+
view.setUint8(0, PROTOCOL_VERSION);
|
|
42
|
+
view.setUint8(1, frame.cmd ?? Command.REQUEST);
|
|
43
|
+
view.setUint16(2, frame.seq % SEQ_MODULO);
|
|
44
|
+
view.setUint16(4, frame.opcode);
|
|
45
|
+
view.setUint32(6, ((flags << 24) | body.length) >>> 0);
|
|
46
|
+
bytes.set(body, HEADER_BYTES);
|
|
47
|
+
return bytes;
|
|
48
|
+
};
|
|
49
|
+
/** The header alone, so a body that cannot be read still says which request it answered. */
|
|
50
|
+
export const decodeHeader = (bytes) => {
|
|
51
|
+
if (bytes.length < HEADER_BYTES)
|
|
52
|
+
throw new FrameError(`a frame of ${bytes.length} bytes, shorter than its header`);
|
|
53
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
54
|
+
const packed = view.getUint32(6);
|
|
55
|
+
const header = {
|
|
56
|
+
ver: view.getUint8(0),
|
|
57
|
+
cmd: view.getUint8(1),
|
|
58
|
+
seq: view.getUint16(2),
|
|
59
|
+
opcode: view.getUint16(4),
|
|
60
|
+
flags: packed >>> 24,
|
|
61
|
+
length: packed & 0xff_ffff,
|
|
62
|
+
};
|
|
63
|
+
if (HEADER_BYTES + header.length > bytes.length) {
|
|
64
|
+
throw new FrameError(`a frame that claims ${header.length} bytes and carries ${bytes.length - HEADER_BYTES}`);
|
|
65
|
+
}
|
|
66
|
+
return header;
|
|
67
|
+
};
|
|
17
68
|
/**
|
|
18
|
-
* **
|
|
19
|
-
*
|
|
20
|
-
* `
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* ⚠ Measured 2026-09-19: **chat ids on the owner's account reach 14 digits, not 19**, and contact
|
|
24
|
-
* ids reach 9 — all comfortably inside a number. The hazard is real and it is the message ids that
|
|
25
|
-
* carry it; a 19-digit chat id is an illustration, not something seen.
|
|
69
|
+
* **Integers come back as `number` unless a number would lose digits.** MessagePack hands every
|
|
70
|
+
* 64-bit integer over as a `bigint`, and MAX sends times as 64-bit — a `bigint` timestamp breaks
|
|
71
|
+
* every `Date` and every subtraction above this layer. Message ids are 18 digits, past
|
|
72
|
+
* `Number.MAX_SAFE_INTEGER`, and stay `bigint`; every id leaves the domain mapper as a string.
|
|
26
73
|
*/
|
|
27
|
-
export const decodeFrame = (
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
};
|
|
74
|
+
export const decodeFrame = (bytes) => {
|
|
75
|
+
const { flags, length, ...header } = decodeHeader(bytes);
|
|
76
|
+
let body = bytes.subarray(HEADER_BYTES, HEADER_BYTES + length);
|
|
77
|
+
if (flags === ZSTD)
|
|
78
|
+
throw new FrameError("a zstd-compressed frame, which the web client was never seen receiving");
|
|
79
|
+
if (flags > MAX_LZ4_FLAGS)
|
|
80
|
+
throw new FrameError(`a frame with compression flags ${flags}`);
|
|
81
|
+
if (flags > 0)
|
|
82
|
+
body = decompressBlock(body, flags * length);
|
|
83
|
+
if (body.length === 0)
|
|
84
|
+
return { ...header, payload: null };
|
|
85
|
+
const value = decodeBody(body);
|
|
86
|
+
return { ...header, payload: isRecord(value) ? value : null };
|
|
40
87
|
};
|
|
41
88
|
export class FrameError extends Error {
|
|
42
89
|
constructor(what) {
|
|
@@ -57,12 +104,44 @@ export const asId = (value) => {
|
|
|
57
104
|
return value;
|
|
58
105
|
return undefined;
|
|
59
106
|
};
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
107
|
+
/** A `bigint` is an id we built from a string (`toWireId`), and goes out wrapped the way the web client wraps ids. */
|
|
108
|
+
const encodeBody = (payload) => encode(wrapBigints(payload), { extensionCodec: wrapped, useBigInt64: true, ignoreUndefined: true });
|
|
109
|
+
const decodeBody = (bytes) => narrow(decode(bytes, { extensionCodec: wrapped, useBigInt64: true, mapKeyConverter: asKey }));
|
|
110
|
+
/**
|
|
111
|
+
* ⚠ With `useBigInt64` the library writes every `number` past 32 bits as a **float64**, and MAX
|
|
112
|
+
* refuses a login whose time arrives as one (`proto.payload`, measured 2026-09-25). A whole number
|
|
113
|
+
* that wide goes out as a bare 64-bit integer instead, as the web client sends `from` and
|
|
114
|
+
* `contactsSync`; only a `bigint` — an id — is wrapped.
|
|
115
|
+
*/
|
|
116
|
+
const wrapBigints = (value) => {
|
|
117
|
+
if (typeof value === "bigint")
|
|
118
|
+
return new ExtData(WRAPPED, encode(value, { useBigInt64: true }));
|
|
119
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && (value > 0xffff_ffff || value < -0x8000_0000))
|
|
120
|
+
return BigInt(value);
|
|
121
|
+
if (Array.isArray(value))
|
|
122
|
+
return value.map(wrapBigints);
|
|
123
|
+
if (isRecord(value))
|
|
124
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, wrapBigints(item)]));
|
|
125
|
+
return value;
|
|
126
|
+
};
|
|
127
|
+
const narrow = (value) => {
|
|
128
|
+
if (typeof value === "bigint")
|
|
129
|
+
return Number.MIN_SAFE_INTEGER <= value && value <= Number.MAX_SAFE_INTEGER ? Number(value) : value;
|
|
130
|
+
if (Array.isArray(value))
|
|
131
|
+
return value.map(narrow);
|
|
132
|
+
if (isRecord(value)) {
|
|
133
|
+
for (const key of Object.keys(value))
|
|
134
|
+
value[key] = narrow(value[key]);
|
|
65
135
|
}
|
|
66
|
-
return
|
|
136
|
+
return value;
|
|
137
|
+
};
|
|
138
|
+
/** Map keys are chat and message ids as often as names; ours are always strings. */
|
|
139
|
+
const asKey = (key) => {
|
|
140
|
+
if (typeof key === "string")
|
|
141
|
+
return key;
|
|
142
|
+
if (typeof key === "number" || typeof key === "bigint")
|
|
143
|
+
return String(key);
|
|
144
|
+
throw new FrameError(`a map key of type ${typeof key}`);
|
|
67
145
|
};
|
|
146
|
+
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Uint8Array);
|
|
68
147
|
//# sourceMappingURL=frame.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frame.js","sourceRoot":"","sources":["../../src/protocol/frame.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"frame.js","sourceRoot":"","sources":["../../src/protocol/frame.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LZ4 *block* format — no frame header, no checksum — which is what MAX puts in a frame body.
|
|
3
|
+
*
|
|
4
|
+
* Hand-written because both directions are small and a native binding is what breaks under Bun
|
|
5
|
+
* (REQUIREMENTS §36). The decompressor mirrors the one that read all 73 frames of the 2026-09-25
|
|
6
|
+
* capture; the compressor keeps the format's end-of-block rules, which a lenient decoder such as
|
|
7
|
+
* ours would not notice being broken but MAX's might.
|
|
8
|
+
*/
|
|
9
|
+
export declare class Lz4Error extends Error {
|
|
10
|
+
constructor(what: string);
|
|
11
|
+
}
|
|
12
|
+
export declare const compressBlock: (src: Uint8Array) => Uint8Array;
|
|
13
|
+
/** `maxOutput` bounds a hostile or corrupt block; the frame header gives the caller one. */
|
|
14
|
+
export declare const decompressBlock: (src: Uint8Array, maxOutput: number) => Uint8Array;
|
|
15
|
+
//# sourceMappingURL=lz4.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lz4.d.ts","sourceRoot":"","sources":["../../src/protocol/lz4.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAUH,qBAAa,QAAS,SAAQ,KAAK;gBACrB,IAAI,EAAE,MAAM;CAIzB;AAED,eAAO,MAAM,aAAa,GAAI,KAAK,UAAU,KAAG,UA4B/C,CAAA;AAED,4FAA4F;AAC5F,eAAO,MAAM,eAAe,GAAI,KAAK,UAAU,EAAE,WAAW,MAAM,KAAG,UA6BpE,CAAA"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LZ4 *block* format — no frame header, no checksum — which is what MAX puts in a frame body.
|
|
3
|
+
*
|
|
4
|
+
* Hand-written because both directions are small and a native binding is what breaks under Bun
|
|
5
|
+
* (REQUIREMENTS §36). The decompressor mirrors the one that read all 73 frames of the 2026-09-25
|
|
6
|
+
* capture; the compressor keeps the format's end-of-block rules, which a lenient decoder such as
|
|
7
|
+
* ours would not notice being broken but MAX's might.
|
|
8
|
+
*/
|
|
9
|
+
const MIN_MATCH = 4;
|
|
10
|
+
/** The last five bytes of a block are always literals. */
|
|
11
|
+
const LAST_LITERALS = 5;
|
|
12
|
+
/** No match may start within the last twelve bytes. */
|
|
13
|
+
const MF_LIMIT = 12;
|
|
14
|
+
const MAX_OFFSET = 0xffff;
|
|
15
|
+
const HASH_BITS = 12;
|
|
16
|
+
export class Lz4Error extends Error {
|
|
17
|
+
constructor(what) {
|
|
18
|
+
super(`MAX sent an LZ4 block that ${what}`);
|
|
19
|
+
this.name = "Lz4Error";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export const compressBlock = (src) => {
|
|
23
|
+
const out = new Growable(src.length + (src.length >> 8) + 16);
|
|
24
|
+
const table = new Int32Array(1 << HASH_BITS).fill(-1);
|
|
25
|
+
const matchLimit = src.length - LAST_LITERALS;
|
|
26
|
+
let anchor = 0;
|
|
27
|
+
let i = 0;
|
|
28
|
+
while (i < src.length - MF_LIMIT) {
|
|
29
|
+
const sequence = read32(src, i);
|
|
30
|
+
const slot = Math.imul(sequence, 2654435761) >>> (32 - HASH_BITS);
|
|
31
|
+
const candidate = table[slot];
|
|
32
|
+
table[slot] = i;
|
|
33
|
+
if (candidate < 0 || i - candidate > MAX_OFFSET || read32(src, candidate) !== sequence) {
|
|
34
|
+
i += 1;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
let length = MIN_MATCH;
|
|
38
|
+
while (i + length < matchLimit && src[i + length] === src[candidate + length])
|
|
39
|
+
length += 1;
|
|
40
|
+
writeSequence(out, src.subarray(anchor, i), i - candidate, length);
|
|
41
|
+
i += length;
|
|
42
|
+
anchor = i;
|
|
43
|
+
}
|
|
44
|
+
writeSequence(out, src.subarray(anchor), 0, 0);
|
|
45
|
+
return out.bytes();
|
|
46
|
+
};
|
|
47
|
+
/** `maxOutput` bounds a hostile or corrupt block; the frame header gives the caller one. */
|
|
48
|
+
export const decompressBlock = (src, maxOutput) => {
|
|
49
|
+
const out = new Growable(Math.min(maxOutput, src.length * 4));
|
|
50
|
+
let p = 0;
|
|
51
|
+
while (p < src.length) {
|
|
52
|
+
const token = src[p++];
|
|
53
|
+
let literals = token >> 4;
|
|
54
|
+
if (literals === 15)
|
|
55
|
+
literals += readLength(src, () => p++);
|
|
56
|
+
if (p + literals > src.length)
|
|
57
|
+
throw new Lz4Error("runs past its own end");
|
|
58
|
+
out.append(src.subarray(p, p + literals));
|
|
59
|
+
p += literals;
|
|
60
|
+
if (out.length > maxOutput)
|
|
61
|
+
throw new Lz4Error("is larger than its frame allows");
|
|
62
|
+
if (p >= src.length)
|
|
63
|
+
break;
|
|
64
|
+
if (p + 2 > src.length)
|
|
65
|
+
throw new Lz4Error("ends in the middle of an offset");
|
|
66
|
+
const offset = src[p] | (src[p + 1] << 8);
|
|
67
|
+
p += 2;
|
|
68
|
+
if (offset === 0 || offset > out.length)
|
|
69
|
+
throw new Lz4Error("points before its own start");
|
|
70
|
+
let length = token & 15;
|
|
71
|
+
if (length === 15)
|
|
72
|
+
length += readLength(src, () => p++);
|
|
73
|
+
length += MIN_MATCH;
|
|
74
|
+
if (out.length + length > maxOutput)
|
|
75
|
+
throw new Lz4Error("is larger than its frame allows");
|
|
76
|
+
out.copyBack(offset, length);
|
|
77
|
+
}
|
|
78
|
+
return out.bytes();
|
|
79
|
+
};
|
|
80
|
+
const read32 = (bytes, at) => (bytes[at] |
|
|
81
|
+
(bytes[at + 1] << 8) |
|
|
82
|
+
(bytes[at + 2] << 16) |
|
|
83
|
+
(bytes[at + 3] << 24)) >>>
|
|
84
|
+
0;
|
|
85
|
+
const readLength = (src, next) => {
|
|
86
|
+
let total = 0;
|
|
87
|
+
let byte;
|
|
88
|
+
do {
|
|
89
|
+
const at = next();
|
|
90
|
+
if (at >= src.length)
|
|
91
|
+
throw new Lz4Error("ends in the middle of a length");
|
|
92
|
+
byte = src[at];
|
|
93
|
+
total += byte;
|
|
94
|
+
} while (byte === 255);
|
|
95
|
+
return total;
|
|
96
|
+
};
|
|
97
|
+
const writeLength = (out, value) => {
|
|
98
|
+
let rest = value;
|
|
99
|
+
while (rest >= 255) {
|
|
100
|
+
out.push(255);
|
|
101
|
+
rest -= 255;
|
|
102
|
+
}
|
|
103
|
+
out.push(rest);
|
|
104
|
+
};
|
|
105
|
+
/** A match length of zero means the final, literals-only sequence. */
|
|
106
|
+
const writeSequence = (out, literals, offset, matchLength) => {
|
|
107
|
+
const literalNibble = Math.min(literals.length, 15);
|
|
108
|
+
const matchNibble = matchLength === 0 ? 0 : Math.min(matchLength - MIN_MATCH, 15);
|
|
109
|
+
out.push((literalNibble << 4) | matchNibble);
|
|
110
|
+
if (literals.length >= 15)
|
|
111
|
+
writeLength(out, literals.length - 15);
|
|
112
|
+
out.append(literals);
|
|
113
|
+
if (matchLength === 0)
|
|
114
|
+
return;
|
|
115
|
+
out.push(offset & 0xff);
|
|
116
|
+
out.push(offset >> 8);
|
|
117
|
+
if (matchLength - MIN_MATCH >= 15)
|
|
118
|
+
writeLength(out, matchLength - MIN_MATCH - 15);
|
|
119
|
+
};
|
|
120
|
+
class Growable {
|
|
121
|
+
#buffer;
|
|
122
|
+
length = 0;
|
|
123
|
+
constructor(capacity) {
|
|
124
|
+
this.#buffer = new Uint8Array(Math.max(capacity, 16));
|
|
125
|
+
}
|
|
126
|
+
push(byte) {
|
|
127
|
+
this.#reserve(1);
|
|
128
|
+
this.#buffer[this.length++] = byte;
|
|
129
|
+
}
|
|
130
|
+
append(bytes) {
|
|
131
|
+
this.#reserve(bytes.length);
|
|
132
|
+
this.#buffer.set(bytes, this.length);
|
|
133
|
+
this.length += bytes.length;
|
|
134
|
+
}
|
|
135
|
+
/** Byte by byte on purpose: a match may overlap the bytes it is still producing. */
|
|
136
|
+
copyBack(offset, length) {
|
|
137
|
+
this.#reserve(length);
|
|
138
|
+
const from = this.length - offset;
|
|
139
|
+
for (let k = 0; k < length; k++)
|
|
140
|
+
this.#buffer[this.length + k] = this.#buffer[from + k];
|
|
141
|
+
this.length += length;
|
|
142
|
+
}
|
|
143
|
+
bytes() {
|
|
144
|
+
return this.#buffer.slice(0, this.length);
|
|
145
|
+
}
|
|
146
|
+
#reserve(extra) {
|
|
147
|
+
if (this.length + extra <= this.#buffer.length)
|
|
148
|
+
return;
|
|
149
|
+
const next = new Uint8Array(Math.max(this.#buffer.length * 2, this.length + extra));
|
|
150
|
+
next.set(this.#buffer.subarray(0, this.length));
|
|
151
|
+
this.#buffer = next;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=lz4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lz4.js","sourceRoot":"","sources":["../../src/protocol/lz4.ts"],"names":[],"mappings":"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"}
|
package/dist/sends/guard.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { Id } from "../domain/models.js";
|
|
2
|
-
import type { ChatAction, SendEntry, SendJournal, SendKind } from "./journal.js";
|
|
2
|
+
import type { AccountAction, ChatAction, SendEntry, SendJournal, SendKind } from "./journal.js";
|
|
3
|
+
import { type Permission } from "./permissions.js";
|
|
3
4
|
import type { RecipientList } from "./recipients.js";
|
|
4
5
|
/** What `MaxClient.messages.send` asks before it sends, and tells after — on every outcome. */
|
|
5
6
|
export interface SendGuard {
|
|
6
7
|
/** `null` for a chat that does not exist yet — joining or creating one — or for the account itself: no list can name either. */
|
|
7
|
-
check(chatId: Id | null, kind?: SendKind, action?: ChatAction): void;
|
|
8
|
+
check(chatId: Id | null, kind?: SendKind, action?: ChatAction | AccountAction, count?: number): void;
|
|
8
9
|
record(entry: Omit<SendEntry, "at" | "profile">): void;
|
|
9
10
|
}
|
|
10
11
|
export interface SendGuardOptions {
|
|
@@ -12,6 +13,9 @@ export interface SendGuardOptions {
|
|
|
12
13
|
readOnly: boolean;
|
|
13
14
|
/** Named in the refusal, so the owner can find what decided it. */
|
|
14
15
|
readOnlyFrom: string;
|
|
16
|
+
/** `undefined` allows every action; a list only those (`CLI-37`). */
|
|
17
|
+
allow?: readonly Permission[];
|
|
18
|
+
allowFrom?: string;
|
|
15
19
|
sendsPerHour: number;
|
|
16
20
|
journal: SendJournal;
|
|
17
21
|
recipients: RecipientList;
|
|
@@ -23,5 +27,5 @@ export interface SendGuardOptions {
|
|
|
23
27
|
* agent that edits the configuration itself — that needs a boundary outside this process
|
|
24
28
|
* (`docs/security.md`).
|
|
25
29
|
*/
|
|
26
|
-
export declare const sendGuard: ({ profile, readOnly, readOnlyFrom, sendsPerHour, journal, recipients, warn, now, }: SendGuardOptions) => SendGuard;
|
|
30
|
+
export declare const sendGuard: ({ profile, readOnly, readOnlyFrom, allow, allowFrom, sendsPerHour, journal, recipients, warn, now, }: SendGuardOptions) => SendGuard;
|
|
27
31
|
//# sourceMappingURL=guard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/sends/guard.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAA;AAC7C,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/sends/guard.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAA;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC/F,OAAO,EAAE,KAAK,UAAU,EAAiB,MAAM,kBAAkB,CAAA;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAIpD,+FAA+F;AAC/F,MAAM,WAAW,SAAS;IACxB,gIAAgI;IAChI,KAAK,CAAC,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,aAAa,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpG,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,GAAG,SAAS,CAAC,GAAG,IAAI,CAAA;CACvD;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,OAAO,CAAA;IACjB,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAA;IACpB,qEAAqE;IACrE,KAAK,CAAC,EAAE,SAAS,UAAU,EAAE,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,WAAW,CAAA;IACpB,UAAU,EAAE,aAAa,CAAA;IACzB,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CACjB;AAaD;;;;GAIG;AACH,eAAO,MAAM,SAAS,GAAI,sGAWvB,gBAAgB,KAAG,SA8EpB,CAAA"}
|
package/dist/sends/guard.js
CHANGED
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
import { CliError } from "@leemour/cli-core";
|
|
2
|
+
import { permissionFor } from "./permissions.js";
|
|
2
3
|
const HOUR_MS = 60 * 60 * 1000;
|
|
3
4
|
/**
|
|
4
5
|
* What puts a new message in somebody's chat (`NEED-168`). A reaction, an edit, a quiet pin or a
|
|
5
|
-
* change to a chat wakes nobody up; creating a group does — the people in it are told.
|
|
6
|
+
* change to a chat wakes nobody up; creating a group does — the people in it are told. A deletion
|
|
7
|
+
* wakes nobody either, but many at once is what a ban for automation looks like, so each deleted
|
|
8
|
+
* message counts as one (`MAX-47`).
|
|
6
9
|
*/
|
|
7
|
-
const countsTowardLimit = ({ kind = "message", action }) => kind === "message" || kind === "forward" || action === "create";
|
|
10
|
+
const countsTowardLimit = ({ kind = "message", action }) => kind === "message" || kind === "forward" || kind === "delete" || action === "create";
|
|
11
|
+
const weightOf = (entry) => (entry.kind === "delete" ? (entry.count ?? 1) : 1);
|
|
8
12
|
/**
|
|
9
13
|
* These stop a model that was talked into sending by a message it read. They do **not** stop an
|
|
10
14
|
* agent that edits the configuration itself — that needs a boundary outside this process
|
|
11
15
|
* (`docs/security.md`).
|
|
12
16
|
*/
|
|
13
|
-
export const sendGuard = ({ profile, readOnly, readOnlyFrom, sendsPerHour, journal, recipients, warn, now = () => new Date(), }) => ({
|
|
14
|
-
check: (chatId, kind = "message", action) => {
|
|
17
|
+
export const sendGuard = ({ profile, readOnly, readOnlyFrom, allow, allowFrom = "default", sendsPerHour, journal, recipients, warn, now = () => new Date(), }) => ({
|
|
18
|
+
check: (chatId, kind = "message", action, count = 1) => {
|
|
15
19
|
if (readOnly) {
|
|
16
20
|
throw new CliError("permission_error", `profile ${profile} is read-only (readOnly, from the ${readOnlyFrom}) — it cannot send, react, change chats or change the account`);
|
|
17
21
|
}
|
|
22
|
+
const permission = permissionFor(kind, action);
|
|
23
|
+
if (allow && !allow.includes(permission)) {
|
|
24
|
+
const command = allowFrom === "config defaults" ? "max config set --defaults allow" : `max ${profile} config set allow`;
|
|
25
|
+
throw new CliError("permission_error", `profile ${profile} does not allow ${permission} (allow: ${allow.join(", ") || "nothing"} — from the ${allowFrom}); ` +
|
|
26
|
+
`to allow it: ${command} ${[...allow, permission].join(",")}`, { permission });
|
|
27
|
+
}
|
|
18
28
|
const allowed = recipients.read();
|
|
19
29
|
if (chatId !== null && allowed && !allowed.some((chat) => chat.id === chatId)) {
|
|
20
30
|
throw new CliError("confirmation_required", `chat ${chatId} is not on the recipient list of profile ${profile} — ` +
|
|
@@ -22,19 +32,30 @@ export const sendGuard = ({ profile, readOnly, readOnlyFrom, sendsPerHour, journ
|
|
|
22
32
|
}
|
|
23
33
|
if (!countsTowardLimit({ kind, action }))
|
|
24
34
|
return;
|
|
35
|
+
if (count > sendsPerHour) {
|
|
36
|
+
throw new CliError("rate_limited", `${count} at once is more than the hourly limit of profile ${profile} (sendsPerHour ${sendsPerHour}) — ` +
|
|
37
|
+
"do fewer at a time");
|
|
38
|
+
}
|
|
25
39
|
const since = now().getTime() - HOUR_MS;
|
|
26
40
|
const recent = journal
|
|
27
41
|
.entries()
|
|
28
42
|
.filter(countsTowardLimit)
|
|
29
43
|
.filter((entry) => entry.outcome === "sent" || entry.outcome === "outcome_unknown")
|
|
30
|
-
.map((entry) => Date.parse(entry.at))
|
|
31
|
-
.filter((time) => time > since)
|
|
32
|
-
.sort((a, b) => a - b);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
.map((entry) => ({ time: Date.parse(entry.at), weight: weightOf(entry) }))
|
|
45
|
+
.filter(({ time }) => time > since)
|
|
46
|
+
.sort((a, b) => a.time - b.time);
|
|
47
|
+
const used = recent.reduce((sum, { weight }) => sum + weight, 0);
|
|
48
|
+
if (used + count > sendsPerHour) {
|
|
49
|
+
// The limit may have been lowered below what the last hour already holds: it opens when
|
|
50
|
+
// enough of it has aged out, not when the oldest has.
|
|
51
|
+
let freed = 0;
|
|
52
|
+
const opening = recent.find(({ weight }) => {
|
|
53
|
+
freed += weight;
|
|
54
|
+
return used - freed + count <= sendsPerHour;
|
|
55
|
+
});
|
|
56
|
+
const nextMs = (opening?.time ?? 0) + HOUR_MS;
|
|
36
57
|
const next = new Date(nextMs).toISOString();
|
|
37
|
-
throw new CliError("rate_limited", `profile ${profile} has sent ${
|
|
58
|
+
throw new CliError("rate_limited", `profile ${profile} has sent ${used} messages in the last hour, and its limit is ` +
|
|
38
59
|
`${sendsPerHour} (sendsPerHour) — the next send is possible at ${next}`, { retryAfterMs: nextMs - now().getTime(), retryAt: next });
|
|
39
60
|
}
|
|
40
61
|
},
|
package/dist/sends/guard.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../../src/sends/guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../../src/sends/guard.ts"],"names":[],"mappings":"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"}
|
package/dist/sends/journal.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Id } from "../domain/models.js";
|
|
2
2
|
export type SendOutcome = "sent" | "outcome_unknown" | "refused" | "failed";
|
|
3
3
|
/** Absent in the journal means a message: that is every line written before reactions were guarded. */
|
|
4
|
-
export type SendKind = "message" | "reaction" | "edit" | "forward" | "pin" | "chat" | "account";
|
|
4
|
+
export type SendKind = "message" | "reaction" | "edit" | "forward" | "pin" | "read" | "delete" | "chat" | "account";
|
|
5
5
|
/** What a `chat` entry did. Never a title, a description or a link — only which action. */
|
|
6
6
|
export type ChatAction = "create" | "join" | "leave" | "members.add" | "members.remove" | "admins.add" | "admins.remove" | "update" | "settings" | "requests.accept" | "requests.decline" | "link.reset";
|
|
7
7
|
/** What an `account` entry changed. Never the value it changed it to — no name, number or title. */
|
|
@@ -18,6 +18,9 @@ export interface SendEntry {
|
|
|
18
18
|
/** How many people a `chat` entry added or removed. */
|
|
19
19
|
people?: number;
|
|
20
20
|
messageId?: Id;
|
|
21
|
+
/** How many messages a `delete` entry named — each one counts toward the hourly limit. */
|
|
22
|
+
count?: number;
|
|
23
|
+
forEveryone?: boolean;
|
|
21
24
|
cid?: number;
|
|
22
25
|
length?: number;
|
|
23
26
|
/** What was attached, by kind and size — never a file name. */
|
|
@@ -25,6 +28,8 @@ export interface SendEntry {
|
|
|
25
28
|
kind: "photo" | "file";
|
|
26
29
|
bytes: number;
|
|
27
30
|
}[];
|
|
31
|
+
/** When MAX will send it; the entry is written when it was queued, and the guard counted it then. */
|
|
32
|
+
scheduledFor?: string;
|
|
28
33
|
errorCode?: string;
|
|
29
34
|
}
|
|
30
35
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../../src/sends/journal.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAA;AAE7C,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,iBAAiB,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE3E,uGAAuG;AACvG,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../../src/sends/journal.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAA;AAE7C,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,iBAAiB,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE3E,uGAAuG;AACvG,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAA;AAEnH,2FAA2F;AAC3F,MAAM,MAAM,UAAU,GAClB,QAAQ,GACR,MAAM,GACN,OAAO,GACP,aAAa,GACb,gBAAgB,GAChB,YAAY,GACZ,eAAe,GACf,QAAQ,GACR,UAAU,GACV,iBAAiB,GACjB,kBAAkB,GAClB,YAAY,CAAA;AAEhB,oGAAoG;AACpG,MAAM,MAAM,aAAa,GACrB,aAAa,GACb,gBAAgB,GAChB,gBAAgB,GAChB,SAAS,GACT,eAAe,GACf,eAAe,GACf,eAAe,GACf,cAAc,CAAA;AAElB,iEAAiE;AACjE,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,oHAAoH;IACpH,MAAM,EAAE,EAAE,GAAG,IAAI,CAAA;IACjB,OAAO,EAAE,WAAW,CAAA;IACpB,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,MAAM,CAAC,EAAE,UAAU,GAAG,aAAa,CAAA;IACnC,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,EAAE,CAAA;IACd,0FAA0F;IAC1F,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,+DAA+D;IAC/D,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACzD,qGAAqG;IACrG,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,SAAS,MAAM,EAAE,MAAK,MAAM,CAAC,UAAwB,KAAG,MACc,CAAA;AAEnG,qBAAa,WAAW;IACV,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;IAEjC,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAK9B,qGAAqG;IACrG,OAAO,IAAI,SAAS,EAAE;CAkBvB"}
|