@leemour/max-cli 0.6.0 → 0.8.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.
Files changed (252) hide show
  1. package/README.md +201 -74
  2. package/dist/cache/index.d.ts +1 -0
  3. package/dist/cache/index.d.ts.map +1 -1
  4. package/dist/cache/index.js +4 -4
  5. package/dist/cache/index.js.map +1 -1
  6. package/dist/cache/schema.d.ts +13 -8
  7. package/dist/cache/schema.d.ts.map +1 -1
  8. package/dist/cache/schema.js +84 -23
  9. package/dist/cache/schema.js.map +1 -1
  10. package/dist/cache/store.d.ts +15 -0
  11. package/dist/cache/store.d.ts.map +1 -1
  12. package/dist/cache/store.js +32 -3
  13. package/dist/cache/store.js.map +1 -1
  14. package/dist/client.d.ts +205 -9
  15. package/dist/client.d.ts.map +1 -1
  16. package/dist/client.js +866 -31
  17. package/dist/client.js.map +1 -1
  18. package/dist/commands/account.d.ts.map +1 -1
  19. package/dist/commands/account.js +71 -3
  20. package/dist/commands/account.js.map +1 -1
  21. package/dist/commands/chats.d.ts.map +1 -1
  22. package/dist/commands/chats.js +167 -0
  23. package/dist/commands/chats.js.map +1 -1
  24. package/dist/commands/complete.d.ts +10 -0
  25. package/dist/commands/complete.d.ts.map +1 -0
  26. package/dist/commands/complete.js +89 -0
  27. package/dist/commands/complete.js.map +1 -0
  28. package/dist/commands/config.d.ts.map +1 -1
  29. package/dist/commands/config.js +10 -4
  30. package/dist/commands/config.js.map +1 -1
  31. package/dist/commands/contacts.d.ts +3 -0
  32. package/dist/commands/contacts.d.ts.map +1 -1
  33. package/dist/commands/contacts.js +85 -0
  34. package/dist/commands/contacts.js.map +1 -1
  35. package/dist/commands/context.d.ts +22 -3
  36. package/dist/commands/context.d.ts.map +1 -1
  37. package/dist/commands/context.js +26 -7
  38. package/dist/commands/context.js.map +1 -1
  39. package/dist/commands/export.d.ts +3 -0
  40. package/dist/commands/export.d.ts.map +1 -0
  41. package/dist/commands/export.js +83 -0
  42. package/dist/commands/export.js.map +1 -0
  43. package/dist/commands/folders.d.ts +4 -0
  44. package/dist/commands/folders.d.ts.map +1 -0
  45. package/dist/commands/folders.js +76 -0
  46. package/dist/commands/folders.js.map +1 -0
  47. package/dist/commands/inbox.js +2 -2
  48. package/dist/commands/inbox.js.map +1 -1
  49. package/dist/commands/mcp.d.ts +3 -0
  50. package/dist/commands/mcp.d.ts.map +1 -0
  51. package/dist/commands/mcp.js +24 -0
  52. package/dist/commands/mcp.js.map +1 -0
  53. package/dist/commands/messages.d.ts.map +1 -1
  54. package/dist/commands/messages.js +210 -5
  55. package/dist/commands/messages.js.map +1 -1
  56. package/dist/commands/models.d.ts +7 -0
  57. package/dist/commands/models.d.ts.map +1 -0
  58. package/dist/commands/models.js +47 -0
  59. package/dist/commands/models.js.map +1 -0
  60. package/dist/commands/reactions.d.ts.map +1 -1
  61. package/dist/commands/reactions.js +17 -0
  62. package/dist/commands/reactions.js.map +1 -1
  63. package/dist/commands/serve.d.ts +10 -0
  64. package/dist/commands/serve.d.ts.map +1 -0
  65. package/dist/commands/serve.js +98 -0
  66. package/dist/commands/serve.js.map +1 -0
  67. package/dist/commands/session.d.ts.map +1 -1
  68. package/dist/commands/session.js +16 -3
  69. package/dist/commands/session.js.map +1 -1
  70. package/dist/commands/update.d.ts +7 -0
  71. package/dist/commands/update.d.ts.map +1 -0
  72. package/dist/commands/update.js +53 -0
  73. package/dist/commands/update.js.map +1 -0
  74. package/dist/commands/watch.d.ts +9 -0
  75. package/dist/commands/watch.d.ts.map +1 -0
  76. package/dist/commands/watch.js +64 -0
  77. package/dist/commands/watch.js.map +1 -0
  78. package/dist/config.d.ts +32 -1
  79. package/dist/config.d.ts.map +1 -1
  80. package/dist/config.js +82 -5
  81. package/dist/config.js.map +1 -1
  82. package/dist/domain/map.d.ts +8 -1
  83. package/dist/domain/map.d.ts.map +1 -1
  84. package/dist/domain/map.js +47 -1
  85. package/dist/domain/map.js.map +1 -1
  86. package/dist/domain/models.d.ts +61 -0
  87. package/dist/domain/models.d.ts.map +1 -1
  88. package/dist/domain/models.js.map +1 -1
  89. package/dist/download.d.ts +2 -0
  90. package/dist/download.d.ts.map +1 -1
  91. package/dist/download.js +11 -0
  92. package/dist/download.js.map +1 -1
  93. package/dist/export.d.ts +26 -0
  94. package/dist/export.d.ts.map +1 -0
  95. package/dist/export.js +75 -0
  96. package/dist/export.js.map +1 -0
  97. package/dist/generated/client.generated.d.ts +28 -0
  98. package/dist/generated/client.generated.d.ts.map +1 -1
  99. package/dist/generated/client.generated.js +28 -0
  100. package/dist/generated/client.generated.js.map +1 -1
  101. package/dist/generated/opcodes.generated.d.ts +23 -13
  102. package/dist/generated/opcodes.generated.d.ts.map +1 -1
  103. package/dist/generated/opcodes.generated.js +23 -13
  104. package/dist/generated/opcodes.generated.js.map +1 -1
  105. package/dist/generated/operations.generated.d.ts +180 -6
  106. package/dist/generated/operations.generated.d.ts.map +1 -1
  107. package/dist/generated/operations.generated.js +29 -4
  108. package/dist/generated/operations.generated.js.map +1 -1
  109. package/dist/mcp/confirm.d.ts +31 -0
  110. package/dist/mcp/confirm.d.ts.map +1 -0
  111. package/dist/mcp/confirm.js +62 -0
  112. package/dist/mcp/confirm.js.map +1 -0
  113. package/dist/mcp/instructions.d.ts +14 -0
  114. package/dist/mcp/instructions.d.ts.map +1 -0
  115. package/dist/mcp/instructions.js +39 -0
  116. package/dist/mcp/instructions.js.map +1 -0
  117. package/dist/mcp/server.d.ts +25 -0
  118. package/dist/mcp/server.d.ts.map +1 -0
  119. package/dist/mcp/server.js +57 -0
  120. package/dist/mcp/server.js.map +1 -0
  121. package/dist/mcp/session.d.ts +28 -0
  122. package/dist/mcp/session.d.ts.map +1 -0
  123. package/dist/mcp/session.js +91 -0
  124. package/dist/mcp/session.js.map +1 -0
  125. package/dist/mcp/tools.d.ts +15 -0
  126. package/dist/mcp/tools.d.ts.map +1 -0
  127. package/dist/mcp/tools.js +351 -0
  128. package/dist/mcp/tools.js.map +1 -0
  129. package/dist/program.d.ts.map +1 -1
  130. package/dist/program.js +23 -2
  131. package/dist/program.js.map +1 -1
  132. package/dist/protocol/connection.d.ts +10 -8
  133. package/dist/protocol/connection.d.ts.map +1 -1
  134. package/dist/protocol/connection.js +64 -7
  135. package/dist/protocol/connection.js.map +1 -1
  136. package/dist/protocol/frame.d.ts +2 -1
  137. package/dist/protocol/frame.d.ts.map +1 -1
  138. package/dist/protocol/frame.js.map +1 -1
  139. package/dist/rendering/messages.d.ts.map +1 -1
  140. package/dist/rendering/messages.js +17 -9
  141. package/dist/rendering/messages.js.map +1 -1
  142. package/dist/sends/guard.d.ts +8 -3
  143. package/dist/sends/guard.d.ts.map +1 -1
  144. package/dist/sends/guard.js +40 -15
  145. package/dist/sends/guard.js.map +1 -1
  146. package/dist/sends/journal.d.ts +20 -2
  147. package/dist/sends/journal.d.ts.map +1 -1
  148. package/dist/sends/journal.js.map +1 -1
  149. package/dist/sends/permissions.d.ts +11 -0
  150. package/dist/sends/permissions.d.ts.map +1 -0
  151. package/dist/sends/permissions.js +47 -0
  152. package/dist/sends/permissions.js.map +1 -0
  153. package/dist/server/lines.d.ts +9 -0
  154. package/dist/server/lines.d.ts.map +1 -0
  155. package/dist/server/lines.js +25 -0
  156. package/dist/server/lines.js.map +1 -0
  157. package/dist/server/server-connection.d.ts +45 -0
  158. package/dist/server/server-connection.d.ts.map +1 -0
  159. package/dist/server/server-connection.js +201 -0
  160. package/dist/server/server-connection.js.map +1 -0
  161. package/dist/server/server.d.ts +69 -0
  162. package/dist/server/server.d.ts.map +1 -0
  163. package/dist/server/server.js +360 -0
  164. package/dist/server/server.js.map +1 -0
  165. package/dist/server/start.d.ts +28 -0
  166. package/dist/server/start.d.ts.map +1 -0
  167. package/dist/server/start.js +80 -0
  168. package/dist/server/start.js.map +1 -0
  169. package/dist/server/subscribe.d.ts +9 -0
  170. package/dist/server/subscribe.d.ts.map +1 -0
  171. package/dist/server/subscribe.js +26 -0
  172. package/dist/server/subscribe.js.map +1 -0
  173. package/dist/session/adopt.d.ts.map +1 -1
  174. package/dist/session/adopt.js +4 -0
  175. package/dist/session/adopt.js.map +1 -1
  176. package/dist/session/browser.d.ts +1 -1
  177. package/dist/session/browser.d.ts.map +1 -1
  178. package/dist/session/browser.js +86 -59
  179. package/dist/session/browser.js.map +1 -1
  180. package/dist/session/handshake.d.ts +1 -1
  181. package/dist/session/store.d.ts +2 -0
  182. package/dist/session/store.d.ts.map +1 -1
  183. package/dist/session/store.js +4 -0
  184. package/dist/session/store.js.map +1 -1
  185. package/dist/spec/define.d.ts.map +1 -1
  186. package/dist/spec/define.js +14 -1
  187. package/dist/spec/define.js.map +1 -1
  188. package/dist/spec/index.d.ts.map +1 -1
  189. package/dist/spec/index.js +30 -7
  190. package/dist/spec/index.js.map +1 -1
  191. package/dist/spec/operations/account.d.ts +20 -0
  192. package/dist/spec/operations/account.d.ts.map +1 -0
  193. package/dist/spec/operations/account.js +62 -0
  194. package/dist/spec/operations/account.js.map +1 -0
  195. package/dist/spec/operations/chats.d.ts +66 -2
  196. package/dist/spec/operations/chats.d.ts.map +1 -1
  197. package/dist/spec/operations/chats.js +153 -5
  198. package/dist/spec/operations/chats.js.map +1 -1
  199. package/dist/spec/operations/contacts.d.ts +19 -1
  200. package/dist/spec/operations/contacts.d.ts.map +1 -1
  201. package/dist/spec/operations/contacts.js +49 -6
  202. package/dist/spec/operations/contacts.js.map +1 -1
  203. package/dist/spec/operations/folders.d.ts +24 -0
  204. package/dist/spec/operations/folders.d.ts.map +1 -0
  205. package/dist/spec/operations/folders.js +64 -0
  206. package/dist/spec/operations/folders.js.map +1 -0
  207. package/dist/spec/operations/messages.d.ts +56 -6
  208. package/dist/spec/operations/messages.d.ts.map +1 -1
  209. package/dist/spec/operations/messages.js +104 -21
  210. package/dist/spec/operations/messages.js.map +1 -1
  211. package/dist/spec/operations/session.d.ts +7 -0
  212. package/dist/spec/operations/session.d.ts.map +1 -1
  213. package/dist/spec/operations/session.js +17 -0
  214. package/dist/spec/operations/session.js.map +1 -1
  215. package/dist/spec/operations/uploads.d.ts +18 -0
  216. package/dist/spec/operations/uploads.d.ts.map +1 -0
  217. package/dist/spec/operations/uploads.js +35 -0
  218. package/dist/spec/operations/uploads.js.map +1 -0
  219. package/dist/testing/mock-max.d.ts +12 -2
  220. package/dist/testing/mock-max.d.ts.map +1 -1
  221. package/dist/testing/mock-max.js +15 -2
  222. package/dist/testing/mock-max.js.map +1 -1
  223. package/dist/testing/sandbox.js +2 -0
  224. package/dist/testing/sandbox.js.map +1 -1
  225. package/dist/transcribe/index.d.ts +34 -0
  226. package/dist/transcribe/index.d.ts.map +1 -0
  227. package/dist/transcribe/index.js +43 -0
  228. package/dist/transcribe/index.js.map +1 -0
  229. package/dist/transcribe/install.d.ts +18 -0
  230. package/dist/transcribe/install.d.ts.map +1 -0
  231. package/dist/transcribe/install.js +67 -0
  232. package/dist/transcribe/install.js.map +1 -0
  233. package/dist/transcribe/models.d.ts +32 -0
  234. package/dist/transcribe/models.d.ts.map +1 -0
  235. package/dist/transcribe/models.js +124 -0
  236. package/dist/transcribe/models.js.map +1 -0
  237. package/dist/transcribe/speech.d.ts +25 -0
  238. package/dist/transcribe/speech.d.ts.map +1 -0
  239. package/dist/transcribe/speech.js +105 -0
  240. package/dist/transcribe/speech.js.map +1 -0
  241. package/dist/update.d.ts +26 -0
  242. package/dist/update.d.ts.map +1 -0
  243. package/dist/update.js +42 -0
  244. package/dist/update.js.map +1 -0
  245. package/dist/upload.d.ts +10 -0
  246. package/dist/upload.d.ts.map +1 -0
  247. package/dist/upload.js +61 -0
  248. package/dist/upload.js.map +1 -0
  249. package/dist/version.d.ts +1 -1
  250. package/dist/version.js +1 -1
  251. package/package.json +17 -3
  252. package/skills/max-cli/SKILL.md +28 -3
@@ -1,36 +1,99 @@
1
1
  import * as v from "valibot";
2
- import { defineOperation, reserveOpcode } from "../define.js";
2
+ import { defineOperation } from "../define.js";
3
3
  import { id } from "../scalars.js";
4
4
  export const messagesSend = defineOperation({
5
5
  name: "messages.send",
6
6
  constant: "MSG_SEND",
7
7
  opcode: 64,
8
8
  auth: true,
9
+ request: v.union([
10
+ v.strictObject({
11
+ chatId: id(),
12
+ message: v.strictObject({
13
+ /** Left out on a forward, as the web client does: the forwarded message is the content. */
14
+ text: v.optional(v.string()),
15
+ /**
16
+ * The client id MAX deduplicates by — measured, and it is what makes one retry safe. It must
17
+ * never be regenerated on a retry: a fresh one means a second message in somebody's chat.
18
+ */
19
+ cid: v.pipe(v.number(), v.integer()),
20
+ /** Markup in UTF-16 positions. STRONG, EMPHASIZED, STRIKETHROUGH, MONOSPACED read back from MAX 2026-09-24. */
21
+ elements: v.optional(v.array(v.strictObject({ type: v.string(), from: v.number(), length: v.number() }))),
22
+ attaches: v.array(v.unknown()),
23
+ /** Read back as `{type, chatId, message}` — the quoted message whole (measured 2026-09-23). */
24
+ link: v.optional(v.variant("type", [
25
+ v.strictObject({ type: v.literal("REPLY"), messageId: id() }),
26
+ /** `chatId` is the chat the message is forwarded **from** (web.max.ru, PyMax). */
27
+ v.strictObject({ type: v.literal("FORWARD"), messageId: id(), chatId: id() }),
28
+ ])),
29
+ /**
30
+ * When MAX itself sends it, in epoch milliseconds — the message waits in the chat's DELAYED
31
+ * history until then. The answer adds `notifySender` and `notifyOpponents`, which we leave
32
+ * to MAX as the web client does (measured 2026-09-24, `FIND-139`).
33
+ */
34
+ delayedAttributes: v.optional(v.strictObject({ timeToFire: v.pipe(v.number(), v.integer()) })),
35
+ }),
36
+ notify: v.boolean(),
37
+ }),
38
+ /**
39
+ * Creating a group is a message too: a CONTROL attachment with no chat to send it to. Measured
40
+ * 2026-09-24 with nobody invited (`pnpm probe:groups`); the answer adds `chat` and `chatId`.
41
+ */
42
+ v.strictObject({
43
+ message: v.strictObject({
44
+ cid: v.pipe(v.number(), v.integer()),
45
+ attaches: v.tuple([
46
+ v.strictObject({
47
+ _type: v.literal("CONTROL"),
48
+ event: v.literal("new"),
49
+ chatType: v.literal("CHAT"),
50
+ title: v.pipe(v.string(), v.minLength(1)),
51
+ userIds: v.array(id()),
52
+ }),
53
+ ]),
54
+ }),
55
+ notify: v.boolean(),
56
+ }),
57
+ ]),
58
+ response: v.looseObject({ message: v.optional(v.looseObject({})), chat: v.optional(v.looseObject({})) }),
59
+ provenance: {
60
+ confidence: "measured",
61
+ sources: [
62
+ "measured against MAX 2026-09-19, including deduplication by `cid` across two connections",
63
+ "`link` and `elements` measured 2026-09-23 in Saved messages (`pnpm probe:reply`); shapes from tsmax and PyMax",
64
+ "the FORWARD link: web.max.ru `_app/immutable/chunks/5oCuRT0F.js` (2026-09-24), PyMax `api/messages/payloads.py:56-73`",
65
+ "a forward with no `text` and no `elements` measured 2026-09-24 in Saved messages (`pnpm probe:edit-pin-forward`)",
66
+ "group creation measured 2026-09-24 (`pnpm probe:groups`); shape from PyMax create_group",
67
+ "`delayedAttributes` from web.max.ru (2026-09-24, `FIND-78`), measured 2026-09-24 in Saved messages (`pnpm probe:scheduled`)",
68
+ ],
69
+ notes: "How long MAX remembers a `cid` is still unmeasured (`PROTO-2`); the two probes were seconds apart. " +
70
+ "The forward was measured from chat 0 to chat 0, so that `link.chatId` is the source rests on the two clients.",
71
+ },
72
+ });
73
+ export const messagesEdit = defineOperation({
74
+ name: "messages.edit",
75
+ constant: "MSG_EDIT",
76
+ opcode: 67,
77
+ auth: true,
9
78
  request: v.strictObject({
10
79
  chatId: id(),
11
- message: v.strictObject({
12
- text: v.string(),
13
- /**
14
- * The client id MAX deduplicates by — measured, and it is what makes one retry safe. It must
15
- * never be regenerated on a retry: a fresh one means a second message in somebody's chat.
16
- */
17
- cid: v.pipe(v.number(), v.integer()),
18
- /** Markup in UTF-16 positions. STRONG, EMPHASIZED, STRIKETHROUGH, MONOSPACED read back from MAX 2026-09-24. */
19
- elements: v.array(v.strictObject({ type: v.string(), from: v.number(), length: v.number() })),
20
- attaches: v.array(v.unknown()),
21
- /** Read back as `{type, chatId, message}` — the quoted message whole (measured 2026-09-23). */
22
- link: v.optional(v.strictObject({ type: v.literal("REPLY"), messageId: id() })),
23
- }),
24
- notify: v.boolean(),
80
+ messageId: id(),
81
+ text: v.string(),
82
+ elements: v.array(v.strictObject({ type: v.string(), from: v.number(), length: v.number() })),
83
+ /** `attachments`, not `attaches` as in MSG_SEND — every source spells them differently. */
84
+ attachments: v.array(v.unknown()),
25
85
  }),
26
86
  response: v.looseObject({ message: v.optional(v.looseObject({})) }),
27
87
  provenance: {
28
88
  confidence: "measured",
29
89
  sources: [
30
- "measured against MAX 2026-09-19, including deduplication by `cid` across two connections",
31
- "`link` and `elements` measured 2026-09-23 in Saved messages (`pnpm probe:reply`); shapes from tsmax and PyMax",
90
+ "measured against MAX 2026-09-24 in Saved messages (`pnpm probe:edit-pin-forward`)",
91
+ "web.max.ru `_app/immutable/chunks/5oCuRT0F.js` (2026-09-24)",
92
+ "PyMax `api/messages/payloads.py:21-28` (53103f0)",
32
93
  ],
33
- notes: "How long MAX remembers a `cid` is still unmeasured (`PROTO-2`); the two probes were seconds apart.",
94
+ notes: 'Answers `{message}` with `status: "EDITED"`. `attachments: []` removes a photo; the ones history gives, sent back ' +
95
+ "as they are, keep it. LOGIN's `config.server.edit-timeout` was 604800 s. The web client edits only your own " +
96
+ "messages, and never a forward.",
34
97
  },
35
98
  });
36
99
  export const messagesReactions = defineOperation({
@@ -71,11 +134,31 @@ export const messagesReact = defineOperation({
71
134
  notes: "Answers `{reactionInfo: {counters: [{count, reaction}], yourReaction, totalCount}}`. Allowed by the owner (`NEED-141`).",
72
135
  },
73
136
  });
74
- export const messageDelete = reserveOpcode({
137
+ export const messagesUnreact = defineOperation({
138
+ name: "messages.unreact",
139
+ constant: "MSG_CANCEL_REACTION",
140
+ opcode: 179,
141
+ auth: true,
142
+ request: v.strictObject({ chatId: id(), messageId: id() }),
143
+ response: v.looseObject({ reactionInfo: v.optional(v.looseObject({})) }),
144
+ provenance: {
145
+ confidence: "measured",
146
+ sources: ["measured against MAX 2026-09-24 in Saved messages", "tsmax removeReaction", "PyMax remove_reaction"],
147
+ notes: "Answers the reactions left, `{reactionInfo: {}}` when none. A second call is answered the same.",
148
+ },
149
+ });
150
+ export const messagesDelete = defineOperation({
75
151
  name: "messages.delete",
76
152
  constant: "MSG_DELETE",
77
153
  opcode: 66,
78
- reason: "Deliberately never called. Giving this tool the ability to destroy somebody's messages, in order to tidy up after a test, is a poor trade (`NEED-32`).",
79
- provenance: { confidence: "observed", sources: ["tsmax"] },
154
+ auth: true,
155
+ request: v.strictObject({ chatId: id(), messageIds: v.array(id()), forMe: v.boolean() }),
156
+ response: v.looseObject({}),
157
+ provenance: {
158
+ confidence: "observed",
159
+ sources: ["PyMax 2.4.1 `delete_message`", "tsmax"],
160
+ notes: "`forMe: true` removes the messages for this account only, `false` for everyone in the chat. Sent only by " +
161
+ "`max messages delete`, on the owner's word each time (`NEED-32` corrected, `MAX-47`). Not yet measured.",
162
+ },
80
163
  });
81
164
  //# sourceMappingURL=messages.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../../src/spec/operations/messages.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../../src/spec/operations/messages.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,11 @@
1
1
  import * as v from "valibot";
2
+ /**
3
+ * **The keep-alive, which only `max serve` sends** — a one-shot command is gone long before it
4
+ * would matter. `interactive` is whether the tab is visible; ours is never, as LOGIN's is.
5
+ */
6
+ export declare const sessionPing: import("../define.js").Operation<v.StrictObjectSchema<{
7
+ readonly interactive: v.BooleanSchema<undefined>;
8
+ }, undefined>, v.LooseObjectSchema<{}, undefined>>;
2
9
  export declare const sessionInit: import("../define.js").Operation<v.StrictObjectSchema<{
3
10
  readonly userAgent: v.StrictObjectSchema<{
4
11
  readonly deviceType: v.LiteralSchema<"WEB", undefined>;
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/spec/operations/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAsB5B,eAAO,MAAM,WAAW;;;;;;;;;;;;;;kDAYtB,CAAA;AAEF,eAAO,MAAM,YAAY;;IAOrB;;;;OAIG;;;IAKH;;;;;;;;;;;OAWG;;;;;;;;;IAUH;;;;;;;;;;;;;;OAcG;;IAEH;;;;;;;;;;;;;;OAcG;;;;;;;cAkBL,CAAA;AAEF,eAAO,MAAM,MAAM,oCAOjB,CAAA"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/spec/operations/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAsB5B;;;GAGG;AACH,eAAO,MAAM,WAAW;;kDAYtB,CAAA;AAEF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;kDAYtB,CAAA;AAEF,eAAO,MAAM,YAAY;;IAOrB;;;;OAIG;;;IAKH;;;;;;;;;;;OAWG;;;;;;;;;IAUH;;;;;;;;;;;;;;OAcG;;IAEH;;;;;;;;;;;;;;OAcG;;;;;;;cAkBL,CAAA;AAEF,eAAO,MAAM,MAAM,oCAOjB,CAAA"}
@@ -18,6 +18,23 @@ const WebUserAgent = v.strictObject({
18
18
  screen: v.string(),
19
19
  timezone: v.string(),
20
20
  });
21
+ /**
22
+ * **The keep-alive, which only `max serve` sends** — a one-shot command is gone long before it
23
+ * would matter. `interactive` is whether the tab is visible; ours is never, as LOGIN's is.
24
+ */
25
+ export const sessionPing = defineOperation({
26
+ name: "session.ping",
27
+ constant: "PING",
28
+ opcode: 1,
29
+ auth: true,
30
+ request: v.strictObject({ interactive: v.boolean() }),
31
+ response: v.looseObject({}),
32
+ provenance: {
33
+ confidence: "confirmed",
34
+ sources: ["web.max.ru bundle read 2026-09-24: `cmd(1, {interactive})` every 30 s", "PyMax Opcode.PING = 1"],
35
+ notes: "MAX pings too; `Connection` answers those itself when it is live.",
36
+ },
37
+ });
21
38
  export const sessionInit = defineOperation({
22
39
  name: "session.init",
23
40
  constant: "SESSION_INIT",
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/spec/operations/session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/spec/operations/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAE7D;;;;;GAKG;AACH,MAAM,YAAY,GAAG,CAAC,CAAC,YAAY,CAAC;IAClC,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;IAC5B,cAAc,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;IACpC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE;IAC3B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;CACrB,CAAC,CAAA;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,eAAe,CAAC;IACzC,IAAI,EAAE,cAAc;IACpB,QAAQ,EAAE,MAAM;IAChB,MAAM,EAAE,CAAC;IACT,IAAI,EAAE,IAAI;IACV,OAAO,EAAE,CAAC,CAAC,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;IACrD,QAAQ,EAAE,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC;IAC3B,UAAU,EAAE;QACV,UAAU,EAAE,WAAW;QACvB,OAAO,EAAE,CAAC,uEAAuE,EAAE,uBAAuB,CAAC;QAC3G,KAAK,EAAE,mEAAmE;KAC3E;CACF,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,eAAe,CAAC;IACzC,IAAI,EAAE,cAAc;IACpB,QAAQ,EAAE,cAAc;IACxB,MAAM,EAAE,CAAC;IACT,IAAI,EAAE,KAAK;IACX,OAAO,EAAE,CAAC,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IAC1E,QAAQ,EAAE,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC;IAC3B,UAAU,EAAE;QACV,UAAU,EAAE,UAAU;QACtB,OAAO,EAAE,CAAC,iCAAiC,EAAE,+BAA+B,EAAE,sBAAsB,CAAC;QACrG,KAAK,EAAE,2DAA2D;KACnE;CACF,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC;IAC1C,IAAI,EAAE,eAAe;IACrB,QAAQ,EAAE,OAAO;IACjB,MAAM,EAAE,EAAE;IACV,IAAI,EAAE,KAAK;IACX,OAAO,EAAE,CAAC,CAAC,YAAY,CAAC;QACtB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;QACjB;;;;WAIG;QACH,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;QAC7B,2FAA2F;QAC3F,gFAAgF;QAChF,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC3E;;;;;;;;;;;WAWG;QACH,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;QACrB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;QACxB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;QACxB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;KACvB,CAAC;IACF,QAAQ,EAAE,CAAC,CAAC,WAAW,CAAC;QACtB,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QACtC,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7C,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;QAChD;;;;;;;;;;;;;;WAcG;QACH,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QACjC;;;;;;;;;;;;;;WAcG;QACH,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAC7B,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QACjC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAC5B,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QACnC,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAC/B,gBAAgB,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;KAC1C,CAAC;IACF,UAAU,EAAE;QACV,UAAU,EAAE,UAAU;QACtB,OAAO,EAAE;YACP,iCAAiC;YACjC,2DAA2D;YAC3D,0FAA0F;SAC3F;QACD,KAAK,EACH,uIAAuI;KAC1I;CACF,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,MAAM,GAAG,aAAa,CAAC;IAClC,IAAI,EAAE,gBAAgB;IACtB,QAAQ,EAAE,QAAQ;IAClB,MAAM,EAAE,EAAE;IACV,MAAM,EACJ,sMAAsM;IACxM,UAAU,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,+BAA+B,CAAC,EAAE;CACnF,CAAC,CAAA"}
@@ -0,0 +1,18 @@
1
+ import * as v from "valibot";
2
+ export declare const uploadsPhoto: import("../define.js").Operation<v.StrictObjectSchema<{
3
+ readonly count: v.LiteralSchema<1, undefined>;
4
+ readonly type: v.LiteralSchema<0, undefined>;
5
+ readonly uploaderType: v.LiteralSchema<0, undefined>;
6
+ readonly profile: v.LiteralSchema<false, undefined>;
7
+ }, undefined>, v.LooseObjectSchema<{
8
+ readonly url: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
9
+ }, undefined>>;
10
+ export declare const uploadsFile: import("../define.js").Operation<v.StrictObjectSchema<{
11
+ readonly count: v.LiteralSchema<1, undefined>;
12
+ readonly type: v.LiteralSchema<0, undefined>;
13
+ readonly uploaderType: v.LiteralSchema<0, undefined>;
14
+ readonly profile: v.LiteralSchema<false, undefined>;
15
+ }, undefined>, v.LooseObjectSchema<{
16
+ readonly info: v.OptionalSchema<v.ArraySchema<v.LooseObjectSchema<{}, undefined>, undefined>, undefined>;
17
+ }, undefined>>;
18
+ //# sourceMappingURL=uploads.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploads.d.ts","sourceRoot":"","sources":["../../../src/spec/operations/uploads.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAU5B,eAAO,MAAM,YAAY;;;;;;;cAavB,CAAA;AAEF,eAAO,MAAM,WAAW;;;;;;;cAatB,CAAA"}
@@ -0,0 +1,35 @@
1
+ import * as v from "valibot";
2
+ import { defineOperation } from "../define.js";
3
+ const uploadRequest = v.strictObject({
4
+ count: v.literal(1),
5
+ type: v.literal(0),
6
+ uploaderType: v.literal(0),
7
+ profile: v.literal(false),
8
+ });
9
+ export const uploadsPhoto = defineOperation({
10
+ name: "uploads.photo",
11
+ constant: "PHOTO_UPLOAD",
12
+ opcode: 80,
13
+ auth: true,
14
+ request: uploadRequest,
15
+ response: v.looseObject({ url: v.optional(v.string()) }),
16
+ provenance: {
17
+ confidence: "measured",
18
+ sources: ["measured against MAX 2026-09-24 in Saved messages (`pnpm probe:upload`)", "PyMax upload_photo"],
19
+ notes: 'Answers `{url}`. A multipart POST of the image, field `file`, answers `{photos: {<key>: {token}}}`; the message attaches `{_type: "PHOTO", photoToken}`.',
20
+ },
21
+ });
22
+ export const uploadsFile = defineOperation({
23
+ name: "uploads.file",
24
+ constant: "FILE_UPLOAD",
25
+ opcode: 87,
26
+ auth: true,
27
+ request: uploadRequest,
28
+ response: v.looseObject({ info: v.optional(v.array(v.looseObject({}))) }),
29
+ provenance: {
30
+ confidence: "measured",
31
+ sources: ["measured against MAX 2026-09-24 in Saved messages (`pnpm probe:upload`)", "PyMax upload_file"],
32
+ notes: 'Answers `{info: [{url, fileId, token}]}`. The bytes are POSTed raw with `Content-Range: 0-<end>/<size>`; MAX then pushes 136 `{fileId}`, and a send before that is refused `attachment.not.ready`. The message attaches `{_type: "FILE", fileId}`.',
33
+ },
34
+ });
35
+ //# sourceMappingURL=uploads.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploads.js","sourceRoot":"","sources":["../../../src/spec/operations/uploads.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAE9C,MAAM,aAAa,GAAG,CAAC,CAAC,YAAY,CAAC;IACnC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACnB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAClB,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1B,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;CAC1B,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC;IAC1C,IAAI,EAAE,eAAe;IACrB,QAAQ,EAAE,cAAc;IACxB,MAAM,EAAE,EAAE;IACV,IAAI,EAAE,IAAI;IACV,OAAO,EAAE,aAAa;IACtB,QAAQ,EAAE,CAAC,CAAC,WAAW,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;IACxD,UAAU,EAAE;QACV,UAAU,EAAE,UAAU;QACtB,OAAO,EAAE,CAAC,yEAAyE,EAAE,oBAAoB,CAAC;QAC1G,KAAK,EACH,0JAA0J;KAC7J;CACF,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,eAAe,CAAC;IACzC,IAAI,EAAE,cAAc;IACpB,QAAQ,EAAE,aAAa;IACvB,MAAM,EAAE,EAAE;IACV,IAAI,EAAE,IAAI;IACV,OAAO,EAAE,aAAa;IACtB,QAAQ,EAAE,CAAC,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACzE,UAAU,EAAE;QACV,UAAU,EAAE,UAAU;QACtB,OAAO,EAAE,CAAC,yEAAyE,EAAE,mBAAmB,CAAC;QACzG,KAAK,EACH,oPAAoP;KACvP;CACF,CAAC,CAAA"}
@@ -8,8 +8,8 @@ export interface MockMaxOptions {
8
8
  * one answer can differ by chat.
9
9
  */
10
10
  answers: Record<number, Payload | ((request: Payload) => Payload | undefined)>;
11
- /** Opcodes to refuse, as MAX does: `cmd=3` with an `error` in the payload. */
12
- refuse?: Record<number, string>;
11
+ /** Opcodes to refuse, as MAX does: `cmd=3` with an `error` in the payload. A function may answer `undefined` to let one through. */
12
+ refuse?: Record<number, string | (() => string | undefined)>;
13
13
  }
14
14
  export interface MockMax {
15
15
  createSocket: () => WebSocket;
@@ -20,7 +20,17 @@ export interface MockMax {
20
20
  }[];
21
21
  /** Calls with no scripted answer. Any left at the end of a test fail it (`unscripted.ts`). */
22
22
  unexpected: number[];
23
+ /** What the client answered to MAX's own frames — a ping, a new message. */
24
+ answered: {
25
+ opcode: number;
26
+ seq: number | null;
27
+ payload: Payload | null;
28
+ }[];
23
29
  closed: boolean;
30
+ /** MAX sends a frame of its own, as it does when a message arrives. */
31
+ push: (opcode: number, payload: Payload, seq: number) => void;
32
+ /** MAX drops the socket from its side. */
33
+ drop: () => void;
24
34
  }
25
35
  /** Every mock built since the last call, and forgets them — so each test sees only its own. */
26
36
  export declare const takeBuiltMocks: () => MockMax[];
@@ -1 +1 @@
1
- {"version":3,"file":"mock-max.d.ts","sourceRoot":"","sources":["../../src/testing/mock-max.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAA;AACnC,OAAO,EAAqC,KAAK,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAEtF,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,GAAG,SAAS,CAAC,CAAC,CAAA;IAC9E,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAChC;AAED,MAAM,WAAW,OAAO;IACtB,YAAY,EAAE,MAAM,SAAS,CAAA;IAC7B,sFAAsF;IACtF,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;IAC5C,8FAA8F;IAC9F,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,MAAM,EAAE,OAAO,CAAA;CAChB;AAID,+FAA+F;AAC/F,eAAO,MAAM,cAAc,QAAO,OAAO,EAAqB,CAAA;AAE9D;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,GAAI,qBAA0B,cAAc,KAAG,OA0DlE,CAAA"}
1
+ {"version":3,"file":"mock-max.d.ts","sourceRoot":"","sources":["../../src/testing/mock-max.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAA;AACnC,OAAO,EAAqC,KAAK,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAEtF,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,GAAG,SAAS,CAAC,CAAC,CAAA;IAC9E,oIAAoI;IACpI,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAA;CAC7D;AAED,MAAM,WAAW,OAAO;IACtB,YAAY,EAAE,MAAM,SAAS,CAAA;IAC7B,sFAAsF;IACtF,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;IAC5C,8FAA8F;IAC9F,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,4EAA4E;IAC5E,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;KAAE,EAAE,CAAA;IAC3E,MAAM,EAAE,OAAO,CAAA;IACf,uEAAuE;IACvE,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7D,0CAA0C;IAC1C,IAAI,EAAE,MAAM,IAAI,CAAA;CACjB;AAID,+FAA+F;AAC/F,eAAO,MAAM,cAAc,QAAO,OAAO,EAAqB,CAAA;AAE9D;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,GAAI,qBAA0B,cAAc,KAAG,OAuElE,CAAA"}
@@ -17,7 +17,15 @@ export const mockMax = ({ answers, refuse = {} }) => {
17
17
  queueMicrotask(() => socket.emit("open"));
18
18
  return socket;
19
19
  };
20
- const state = { createSocket, sent: [], unexpected: [], closed: false };
20
+ const state = {
21
+ createSocket,
22
+ sent: [],
23
+ unexpected: [],
24
+ answered: [],
25
+ closed: false,
26
+ push: (opcode, payload, seq) => socket.answer({ seq, opcode, payload, cmd: Command.REQUEST }),
27
+ drop: () => queueMicrotask(() => socket.emit("close")),
28
+ };
21
29
  built.push(state);
22
30
  const socket = new (class extends EventEmitter {
23
31
  readyState = 1;
@@ -26,8 +34,13 @@ export const mockMax = ({ answers, refuse = {} }) => {
26
34
  CLOSING = 2;
27
35
  send(raw) {
28
36
  const frame = decodeFrame(raw);
37
+ if (frame.cmd === Command.RESPONSE) {
38
+ state.answered.push({ opcode: frame.opcode, seq: frame.seq, payload: frame.payload });
39
+ return;
40
+ }
29
41
  state.sent.push({ opcode: frame.opcode, payload: frame.payload ?? {} });
30
- const refusal = refuse[frame.opcode];
42
+ const rule = refuse[frame.opcode];
43
+ const refusal = typeof rule === "function" ? rule() : rule;
31
44
  if (refusal !== undefined) {
32
45
  this.answer({ seq: frame.seq ?? 0, opcode: frame.opcode, payload: { error: refusal }, cmd: Command.ERROR });
33
46
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"mock-max.js","sourceRoot":"","sources":["../../src/testing/mock-max.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mock-max.js","sourceRoot":"","sources":["../../src/testing/mock-max.ts"],"names":[],"mappings":"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"}
@@ -30,5 +30,7 @@ process.env.TMPDIR = sandbox;
30
30
  // Read before the keyring, so a token exported in the shell would log the suite in to the real account.
31
31
  delete process.env.MAX_TOKEN;
32
32
  process.env.MAX_TEST_SANDBOX = "1";
33
+ // A terminal running the suite must not make it ask npm; the tests that want the check pass their own env.
34
+ process.env.MAX_NO_UPDATE_CHECK = "1";
33
35
  afterAll(() => rmSync(sandbox, { recursive: true, force: true }));
34
36
  //# sourceMappingURL=sandbox.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/testing/sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAEjC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,WAAW,CAAC,CAAC,CAAA;AAExD,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;AACpD,OAAO,CAAC,GAAG,CAAC,aAAa,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AAClD,OAAO,CAAC,GAAG,CAAC,aAAa,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AAClD,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,OAAO,CAAA;AAC5B,wGAAwG;AACxG,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,CAAA;AAC5B,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,GAAG,CAAA;AAElC,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/testing/sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAEjC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,WAAW,CAAC,CAAC,CAAA;AAExD,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;AACpD,OAAO,CAAC,GAAG,CAAC,aAAa,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AAClD,OAAO,CAAC,GAAG,CAAC,aAAa,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AAClD,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,OAAO,CAAA;AAC5B,wGAAwG;AACxG,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,CAAA;AAC5B,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,GAAG,CAAA;AAClC,2GAA2G;AAC3G,OAAO,CAAC,GAAG,CAAC,mBAAmB,GAAG,GAAG,CAAA;AAErC,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA"}
@@ -0,0 +1,34 @@
1
+ import { CliError } from "@leemour/cli-core";
2
+ import type { CacheStore } from "../cache/index.js";
3
+ import type { MaxClient } from "../client.js";
4
+ import type { AttachmentLink, Id } from "../domain/models.js";
5
+ import { type SpeechModel } from "./models.js";
6
+ import { type Recognizer } from "./speech.js";
7
+ export interface Transcript {
8
+ chatId: Id;
9
+ messageId: Id;
10
+ text: string;
11
+ model: string;
12
+ /** Length of the recording; `null` when the text came from the cache. */
13
+ seconds: number | null;
14
+ cached: boolean;
15
+ }
16
+ export interface TranscribeOptions {
17
+ model: SpeechModel;
18
+ directory: string;
19
+ cache?: CacheStore | undefined;
20
+ /** Called once the audio is in hand, so the connection closes before the long part begins. */
21
+ release?: () => Promise<void>;
22
+ fetchAudio?: (link: AttachmentLink) => Promise<Uint8Array>;
23
+ open?: (model: SpeechModel, directory: string) => Recognizer;
24
+ }
25
+ export declare const notDownloaded: (model: SpeechModel) => CliError;
26
+ /**
27
+ * One voice message to text, on this machine. A text already heard by the same model comes from
28
+ * the cache, with no connection and no model loaded.
29
+ *
30
+ * ⚠ **The model is never downloaded from here** (`NEED-231`): an agent calling this through MCP must
31
+ * not start a 230–670 MB download. The refusal names the command that does it.
32
+ */
33
+ export declare const transcribe: (client: MaxClient, chatId: Id, messageId: Id, { model, directory, cache, release, fetchAudio, open }: TranscribeOptions) => Promise<Transcript>;
34
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transcribe/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAA;AAG7D,OAAO,EAAE,KAAK,WAAW,EAAO,MAAM,aAAa,CAAA;AACnD,OAAO,EAA6B,KAAK,UAAU,EAAe,MAAM,aAAa,CAAA;AAErF,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,EAAE,CAAA;IACV,SAAS,EAAE,EAAE,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,WAAW,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAA;IAC9B,8FAA8F;IAC9F,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;IAC1D,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,KAAK,UAAU,CAAA;CAC7D;AAED,eAAO,MAAM,aAAa,GAAI,OAAO,WAAW,KAAG,QAKhD,CAAA;AAKH;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GACrB,QAAQ,SAAS,EACjB,QAAQ,EAAE,EACV,WAAW,EAAE,EACb,wDAAqF,iBAAiB,KACrG,OAAO,CAAC,UAAU,CA0BpB,CAAA"}
@@ -0,0 +1,43 @@
1
+ import { CliError } from "@leemour/cli-core";
2
+ import { fetchBytes } from "../download.js";
3
+ import { installedBytes, isInstalled, megabytes, modelPath, vadPath } from "./install.js";
4
+ import { VAD } from "./models.js";
5
+ import { decodeOgg, openRecognizer, toModelRate } from "./speech.js";
6
+ export const notDownloaded = (model) => new CliError("not_found", `the speech model ${model.id} is not downloaded — \`max models audio download ${model.id}\` fetches it ` +
7
+ `(${megabytes(installedBytes(model) + VAD.bytes)}, once)`);
8
+ const openInstalled = (model, directory) => openRecognizer(model, modelPath(directory, model), vadPath(directory));
9
+ /**
10
+ * One voice message to text, on this machine. A text already heard by the same model comes from
11
+ * the cache, with no connection and no model loaded.
12
+ *
13
+ * ⚠ **The model is never downloaded from here** (`NEED-231`): an agent calling this through MCP must
14
+ * not start a 230–670 MB download. The refusal names the command that does it.
15
+ */
16
+ export const transcribe = async (client, chatId, messageId, { model, directory, cache, release, fetchAudio = fetchBytes, open = openInstalled }) => {
17
+ const kept = cache?.messages.transcript(chatId, messageId);
18
+ if (kept && kept.model === model.id) {
19
+ return { chatId, messageId, text: kept.text, model: model.id, seconds: null, cached: true };
20
+ }
21
+ if (!isInstalled(model, directory))
22
+ throw notDownloaded(model);
23
+ const { links } = await client.messages.links(chatId, messageId);
24
+ const voice = links.find((link) => link.kind === "audio");
25
+ if (!voice)
26
+ throw new CliError("validation_error", `message ${messageId} has no voice recording to transcribe`);
27
+ const bytes = await fetchAudio(voice);
28
+ await release?.();
29
+ const { samples, rate } = await decodeOgg(bytes).catch((error) => {
30
+ throw new CliError("invalid_response", `the recording could not be read as Ogg Opus: ${error.message}`);
31
+ });
32
+ const pcm = toModelRate(samples, rate);
33
+ const recognizer = open(model, directory);
34
+ try {
35
+ const text = recognizer.recognize(pcm);
36
+ cache?.messages.keepTranscript(chatId, messageId, text, model.id);
37
+ return { chatId, messageId, text, model: model.id, seconds: Math.round(samples.length / rate), cached: false };
38
+ }
39
+ finally {
40
+ recognizer.free();
41
+ }
42
+ };
43
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transcribe/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { type SpeechModel } from "./models.js";
2
+ /** Beside the profile caches, so `max cache clear` — which empties a database — leaves them alone. */
3
+ export declare const modelsDirectory: (env?: NodeJS.ProcessEnv) => string;
4
+ export declare const modelPath: (directory: string, model: SpeechModel) => (name: string) => string;
5
+ export declare const vadPath: (directory: string) => string;
6
+ /**
7
+ * Size, not hash: hashing 650 MB on every call would cost more than the transcription. The hash is
8
+ * checked once, as the file arrives, and a file only reaches its name after passing it.
9
+ */
10
+ export declare const isInstalled: (model: SpeechModel, directory: string) => boolean;
11
+ export declare const installedBytes: (model: SpeechModel) => number;
12
+ export type Fetch = (url: string) => Promise<Response>;
13
+ export declare const install: (model: SpeechModel, directory: string, { fetch: get, progress }?: {
14
+ fetch?: Fetch;
15
+ progress?: (line: string) => void;
16
+ }) => Promise<void>;
17
+ export declare const megabytes: (bytes: number) => string;
18
+ //# sourceMappingURL=install.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/transcribe/install.ts"],"names":[],"mappings":"AAOA,OAAO,EAAkB,KAAK,WAAW,EAAO,MAAM,aAAa,CAAA;AAEnE,sGAAsG;AACtG,eAAO,MAAM,eAAe,GAAI,MAAK,MAAM,CAAC,UAAwB,KAAG,MACkB,CAAA;AAEzF,eAAO,MAAM,SAAS,GAAI,WAAW,MAAM,EAAE,OAAO,WAAW,MAAM,MAAM,MAAM,WAAoC,CAAA;AAErH,eAAO,MAAM,OAAO,GAAI,WAAW,MAAM,KAAG,MAAmC,CAAA;AAO/E;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,WAAW,EAAE,WAAW,MAAM,KAAG,OACW,CAAA;AAU/E,eAAO,MAAM,cAAc,GAAI,OAAO,WAAW,KAAG,MAAgE,CAAA;AAEpH,MAAM,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAEtD,eAAO,MAAM,OAAO,GAClB,OAAO,WAAW,EAClB,WAAW,MAAM,EACjB,2BAAkC;IAAE,KAAK,CAAC,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAO,KAC1F,OAAO,CAAC,IAAI,CAOd,CAAA;AAqCD,eAAO,MAAM,SAAS,GAAI,OAAO,MAAM,KAAG,MACgC,CAAA"}
@@ -0,0 +1,67 @@
1
+ import { createHash } from "node:crypto";
2
+ import { createWriteStream, mkdirSync, statSync } from "node:fs";
3
+ import { rename, rm } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ import { Readable, Transform } from "node:stream";
6
+ import { pipeline } from "node:stream/promises";
7
+ import { CliError, resolvePaths } from "@leemour/cli-core";
8
+ import { VAD } from "./models.js";
9
+ /** Beside the profile caches, so `max cache clear` — which empties a database — leaves them alone. */
10
+ export const modelsDirectory = (env = process.env) => join(resolvePaths({ appName: "max-cli", prefix: "MAX", env }).cache, "models", "audio");
11
+ export const modelPath = (directory, model) => (name) => join(directory, model.id, name);
12
+ export const vadPath = (directory) => join(directory, VAD.name);
13
+ const placed = (model, directory) => [
14
+ ...model.files.map((file) => [file, modelPath(directory, model)(file.name)]),
15
+ [VAD, vadPath(directory)],
16
+ ];
17
+ /**
18
+ * Size, not hash: hashing 650 MB on every call would cost more than the transcription. The hash is
19
+ * checked once, as the file arrives, and a file only reaches its name after passing it.
20
+ */
21
+ export const isInstalled = (model, directory) => placed(model, directory).every(([file, path]) => sizeOf(path) === file.bytes);
22
+ const sizeOf = (path) => {
23
+ try {
24
+ return statSync(path).size;
25
+ }
26
+ catch {
27
+ return undefined;
28
+ }
29
+ };
30
+ export const installedBytes = (model) => model.files.reduce((sum, file) => sum + file.bytes, 0);
31
+ export const install = async (model, directory, { fetch: get = fetch, progress } = {}) => {
32
+ mkdirSync(join(directory, model.id), { recursive: true, mode: 0o700 });
33
+ for (const [file, path] of placed(model, directory)) {
34
+ if (sizeOf(path) === file.bytes)
35
+ continue;
36
+ progress?.(`downloading ${file.name} (${megabytes(file.bytes)})`);
37
+ await fetchVerified(file, path, get);
38
+ }
39
+ };
40
+ const fetchVerified = async (file, path, get) => {
41
+ const response = await get(file.url);
42
+ if (!response.ok || !response.body) {
43
+ await response.body?.cancel();
44
+ throw new CliError("network_error", `${file.name} could not be downloaded: HTTP ${response.status}`);
45
+ }
46
+ const partial = `${path}.${process.pid}.part`;
47
+ const hash = createHash("sha256");
48
+ const hashing = new Transform({
49
+ transform(chunk, _encoding, done) {
50
+ hash.update(chunk);
51
+ done(null, chunk);
52
+ },
53
+ });
54
+ try {
55
+ await pipeline(Readable.fromWeb(response.body), hashing, createWriteStream(partial, { mode: 0o600 }));
56
+ const actual = hash.digest("hex");
57
+ if (actual !== file.sha256) {
58
+ throw new CliError("invalid_response", `${file.name} is not the file this version of max expects (sha256 ${actual}) — nothing was installed`);
59
+ }
60
+ await rename(partial, path);
61
+ }
62
+ finally {
63
+ await rm(partial, { force: true });
64
+ }
65
+ };
66
+ export const megabytes = (bytes) => bytes < 1_000_000 ? "under 1 MB" : `${Math.round(bytes / 1_000_000)} MB`;
67
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/transcribe/install.ts"],"names":[],"mappings":"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"}