@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
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The speech models `max messages transcribe` can use, each **pinned to one commit** and checked by
3
+ * sha256: a file that does not match is refused, so nobody else's model runs in a program that
4
+ * holds the owner's session.
5
+ *
6
+ * Measured 2026-09-24 through the WebAssembly build of sherpa-onnx, one thread, a Ryzen AI 9 HX 470
7
+ * laptop, 5 minutes of Ogg Opus cut by VAD, Node 24 and Bun 1.3.14 alike (`FIND-131`, `FIND-133`):
8
+ * `gigaam-v3` ×7 real time, `gigaam-v3-ctc` ×8, `parakeet-v3` ×5 and 1.3 GB of memory. Whisper small
9
+ * ran at ×1.1 and is not offered.
10
+ */
11
+ export interface ModelFile {
12
+ name: string;
13
+ url: string;
14
+ sha256: string;
15
+ bytes: number;
16
+ }
17
+ export interface SpeechModel {
18
+ id: string;
19
+ title: string;
20
+ languages: string;
21
+ files: ModelFile[];
22
+ /** The part of sherpa-onnx's `modelConfig` that names the files, given where they are. */
23
+ config: (path: (name: string) => string) => Record<string, unknown>;
24
+ featureDim: number;
25
+ }
26
+ export declare const MODELS: SpeechModel[];
27
+ /** Cuts speech at pauses. Every model needs it: five minutes in one piece crash the WebAssembly build. */
28
+ export declare const VAD: ModelFile;
29
+ export declare const DEFAULT_MODEL = "gigaam-v3";
30
+ export declare const findModel: (id: string) => SpeechModel | undefined;
31
+ export declare const speechModel: (id: string) => SpeechModel;
32
+ //# sourceMappingURL=models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/transcribe/models.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,SAAS,EAAE,CAAA;IAClB,0FAA0F;IAC1F,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACnE,UAAU,EAAE,MAAM,CAAA;CACnB;AAeD,eAAO,MAAM,MAAM,EAAE,WAAW,EAqG/B,CAAA;AAED,0GAA0G;AAC1G,eAAO,MAAM,GAAG,EAAE,SAKjB,CAAA;AAED,eAAO,MAAM,aAAa,cAAc,CAAA;AAExC,eAAO,MAAM,SAAS,GAAI,IAAI,MAAM,KAAG,WAAW,GAAG,SAAoD,CAAA;AAEzG,eAAO,MAAM,WAAW,GAAI,IAAI,MAAM,KAAG,WASxC,CAAA"}
@@ -0,0 +1,124 @@
1
+ import { CliError } from "@leemour/cli-core";
2
+ const huggingFace = (repository, commit) => (name) => `https://huggingface.co/csukuangfj/${repository}/resolve/${commit}/${name}`;
3
+ const gigaam = huggingFace("sherpa-onnx-nemo-transducer-punct-giga-am-v3-russian-2025-12-16", "a6039be7cee829a9044a69ac0ebaf1c191217c97");
4
+ const gigaamCtc = huggingFace("sherpa-onnx-nemo-ctc-punct-giga-am-v3-russian-2025-12-16", "4fb5407ff028a69fec516cdf4c10fac9ddea7c16");
5
+ const parakeet = huggingFace("sherpa-onnx-nemo-parakeet-tdt-0.6b-v3-int8", "2bda32ec70b097a55adaa07d9a7173915b43cc78");
6
+ export const MODELS = [
7
+ {
8
+ id: "gigaam-v3",
9
+ title: "GigaAM v3, with punctuation",
10
+ languages: "Russian — the best of the three for Russian",
11
+ featureDim: 64,
12
+ files: [
13
+ {
14
+ name: "encoder.int8.onnx",
15
+ url: gigaam("encoder.int8.onnx"),
16
+ sha256: "369f35a71bf288d3b8e0391fabd8dba5f2314088d440bca474056b7b4b6e66bf",
17
+ bytes: 224_570_820,
18
+ },
19
+ {
20
+ name: "decoder.onnx",
21
+ url: gigaam("decoder.onnx"),
22
+ sha256: "38fc7475443ea2a26f63211ca350f73ac50fff824ab7a3876ee2bd610c53bbc4",
23
+ bytes: 4_600_132,
24
+ },
25
+ {
26
+ name: "joiner.onnx",
27
+ url: gigaam("joiner.onnx"),
28
+ sha256: "602ff7017a93311aad34df1437c8d7f49911353c13d6eae7a6ee7b041339465c",
29
+ bytes: 2_712_896,
30
+ },
31
+ {
32
+ name: "tokens.txt",
33
+ url: gigaam("tokens.txt"),
34
+ sha256: "39abae20e692998290c574e606f11a9edef2902a1995463fcff63d1490cf22b7",
35
+ bytes: 13_354,
36
+ },
37
+ ],
38
+ config: (path) => ({
39
+ transducer: { encoder: path("encoder.int8.onnx"), decoder: path("decoder.onnx"), joiner: path("joiner.onnx") },
40
+ tokens: path("tokens.txt"),
41
+ modelType: "nemo_transducer",
42
+ }),
43
+ },
44
+ {
45
+ id: "gigaam-v3-ctc",
46
+ title: "GigaAM v3 CTC, with punctuation — a little faster",
47
+ languages: "Russian — a little faster, rougher with capital letters",
48
+ featureDim: 64,
49
+ files: [
50
+ {
51
+ name: "model.int8.onnx",
52
+ url: gigaamCtc("model.int8.onnx"),
53
+ sha256: "d5fea8df94263c285e54b21e5774b707c707192d3bdbeffd7b1eb07fb6743b35",
54
+ bytes: 224_893_661,
55
+ },
56
+ {
57
+ name: "tokens.txt",
58
+ url: gigaamCtc("tokens.txt"),
59
+ sha256: "142de7570b3de5b3035ce111a89c228e80e6085273731d944093ddf24fa539cd",
60
+ bytes: 2_007,
61
+ },
62
+ ],
63
+ config: (path) => ({ nemoCtc: { model: path("model.int8.onnx") }, tokens: path("tokens.txt") }),
64
+ },
65
+ {
66
+ id: "parakeet-v3",
67
+ title: "NVIDIA Parakeet TDT 0.6B v3",
68
+ /** The model card's list: huggingface.co/nvidia/parakeet-tdt-0.6b-v3, read 2026-09-24. */
69
+ languages: "25: bg cs da de el en es et fi fr hr hu it lt lv mt nl pl pt ro ru sk sl sv uk",
70
+ featureDim: 80,
71
+ files: [
72
+ {
73
+ name: "encoder.int8.onnx",
74
+ url: parakeet("encoder.int8.onnx"),
75
+ sha256: "acfc2b4456377e15d04f0243af540b7fe7c992f8d898d751cf134c3a55fd2247",
76
+ bytes: 652_184_281,
77
+ },
78
+ {
79
+ name: "decoder.int8.onnx",
80
+ url: parakeet("decoder.int8.onnx"),
81
+ sha256: "179e50c43d1a9de79c8a24149a2f9bac6eb5981823f2a2ed88d655b24248db4e",
82
+ bytes: 11_845_275,
83
+ },
84
+ {
85
+ name: "joiner.int8.onnx",
86
+ url: parakeet("joiner.int8.onnx"),
87
+ sha256: "3164c13fc2821009440d20fcb5fdc78bff28b4db2f8d0f0b329101719c0948b3",
88
+ bytes: 6_355_277,
89
+ },
90
+ {
91
+ name: "tokens.txt",
92
+ url: parakeet("tokens.txt"),
93
+ sha256: "d58544679ea4bc6ac563d1f545eb7d474bd6cfa467f0a6e2c1dc1c7d37e3c35d",
94
+ bytes: 93_939,
95
+ },
96
+ ],
97
+ config: (path) => ({
98
+ transducer: {
99
+ encoder: path("encoder.int8.onnx"),
100
+ decoder: path("decoder.int8.onnx"),
101
+ joiner: path("joiner.int8.onnx"),
102
+ },
103
+ tokens: path("tokens.txt"),
104
+ modelType: "nemo_transducer",
105
+ }),
106
+ },
107
+ ];
108
+ /** Cuts speech at pauses. Every model needs it: five minutes in one piece crash the WebAssembly build. */
109
+ export const VAD = {
110
+ name: "silero_vad.onnx",
111
+ url: "https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx",
112
+ sha256: "9e2449e1087496d8d4caba907f23e0bd3f78d91fa552479bb9c23ac09cbb1fd6",
113
+ bytes: 643_854,
114
+ };
115
+ export const DEFAULT_MODEL = "gigaam-v3";
116
+ export const findModel = (id) => MODELS.find((model) => model.id === id);
117
+ export const speechModel = (id) => {
118
+ const model = findModel(id);
119
+ if (!model) {
120
+ throw new CliError("validation_error", `no speech model "${id}" — one of ${MODELS.map((known) => known.id).join(", ")}`);
121
+ }
122
+ return model;
123
+ };
124
+ //# sourceMappingURL=models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/transcribe/models.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { SpeechModel } from "./models.js";
2
+ /** What transcription needs from a speech engine — small enough for a test to stand in for it. */
3
+ export interface Recognizer {
4
+ recognize(pcm: Float32Array): string;
5
+ free(): void;
6
+ }
7
+ export declare const SAMPLE_RATE = 16000;
8
+ /**
9
+ * MAX voice messages are Ogg Opus at 48 kHz (measured on a received one), and the models want
10
+ * 16 kHz. Averaging each three samples is ours rather than the engine's resampler, which announces
11
+ * itself on stderr on every call.
12
+ */
13
+ export declare const toModelRate: (samples: Float32Array, rate: number) => Float32Array;
14
+ export declare const decodeOgg: (bytes: Uint8Array) => Promise<{
15
+ samples: Float32Array;
16
+ rate: number;
17
+ }>;
18
+ /**
19
+ * The model, and a voice detector that cuts the audio at pauses into pieces of at most 20 seconds.
20
+ * The cutting is not optional: five minutes in one piece crash the WebAssembly build (measured).
21
+ *
22
+ * `sherpa-onnx` is loaded here, on first use, so no other command pays for its 15 MB of WebAssembly.
23
+ */
24
+ export declare const openRecognizer: (model: SpeechModel, path: (name: string) => string, vadModel: string) => Recognizer;
25
+ //# sourceMappingURL=speech.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"speech.d.ts","sourceRoot":"","sources":["../../src/transcribe/speech.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE9C,kGAAkG;AAClG,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAA;IACpC,IAAI,IAAI,IAAI,CAAA;CACb;AAED,eAAO,MAAM,WAAW,QAAS,CAAA;AAQjC;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,SAAS,YAAY,EAAE,MAAM,MAAM,KAAG,YAYjE,CAAA;AAED,eAAO,MAAM,SAAS,GAAU,OAAO,UAAU,KAAG,OAAO,CAAC;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAYlG,CAAA;AAyBD;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAAE,UAAU,MAAM,KAAG,UAwDrG,CAAA"}
@@ -0,0 +1,105 @@
1
+ import { createRequire } from "node:module";
2
+ export const SAMPLE_RATE = 16_000;
3
+ /**
4
+ * Half a second of silence ahead of the speech. Without it the voice detector clipped a recording
5
+ * that starts talking at once: "Ничьих" came back as "чьих" (measured 2026-09-24).
6
+ */
7
+ const LEAD = SAMPLE_RATE / 2;
8
+ /**
9
+ * MAX voice messages are Ogg Opus at 48 kHz (measured on a received one), and the models want
10
+ * 16 kHz. Averaging each three samples is ours rather than the engine's resampler, which announces
11
+ * itself on stderr on every call.
12
+ */
13
+ export const toModelRate = (samples, rate) => {
14
+ const step = rate / SAMPLE_RATE;
15
+ const length = Math.floor(samples.length / step);
16
+ const pcm = new Float32Array(LEAD + length);
17
+ for (let i = 0; i < length; i++) {
18
+ const from = Math.floor(i * step);
19
+ const to = Math.max(from + 1, Math.floor((i + 1) * step));
20
+ let sum = 0;
21
+ for (let j = from; j < to; j++)
22
+ sum += samples[j] ?? 0;
23
+ pcm[LEAD + i] = sum / (to - from);
24
+ }
25
+ return pcm;
26
+ };
27
+ export const decodeOgg = async (bytes) => {
28
+ const { OggOpusDecoder } = await import("ogg-opus-decoder");
29
+ const decoder = new OggOpusDecoder();
30
+ await decoder.ready;
31
+ try {
32
+ const { channelData, sampleRate, samplesDecoded } = await decoder.decodeFile(bytes);
33
+ const samples = channelData[0];
34
+ if (!samples || samplesDecoded === 0)
35
+ throw new Error("no Ogg Opus audio in it");
36
+ return { samples, rate: sampleRate };
37
+ }
38
+ finally {
39
+ decoder.free();
40
+ }
41
+ };
42
+ const WINDOW = 512;
43
+ /**
44
+ * The model, and a voice detector that cuts the audio at pauses into pieces of at most 20 seconds.
45
+ * The cutting is not optional: five minutes in one piece crash the WebAssembly build (measured).
46
+ *
47
+ * `sherpa-onnx` is loaded here, on first use, so no other command pays for its 15 MB of WebAssembly.
48
+ */
49
+ export const openRecognizer = (model, path, vadModel) => {
50
+ const sherpa = createRequire(import.meta.url)("sherpa-onnx");
51
+ const engine = sherpa.createOfflineRecognizer({
52
+ featConfig: { sampleRate: SAMPLE_RATE, featureDim: model.featureDim },
53
+ modelConfig: { ...model.config(path), numThreads: 1, provider: "cpu", debug: 0 },
54
+ decodingMethod: "greedy_search",
55
+ });
56
+ const vad = sherpa.createVad({
57
+ sileroVad: {
58
+ model: vadModel,
59
+ threshold: 0.5,
60
+ minSilenceDuration: 0.5,
61
+ minSpeechDuration: 0.25,
62
+ maxSpeechDuration: 20,
63
+ windowSize: WINDOW,
64
+ },
65
+ sampleRate: SAMPLE_RATE,
66
+ numThreads: 1,
67
+ provider: "cpu",
68
+ debug: 0,
69
+ bufferSizeInSeconds: 30,
70
+ });
71
+ const piece = (samples) => {
72
+ const stream = engine.createStream();
73
+ try {
74
+ stream.acceptWaveform(SAMPLE_RATE, samples);
75
+ engine.decode(stream);
76
+ return engine.getResult(stream).text.trim();
77
+ }
78
+ finally {
79
+ stream.free();
80
+ }
81
+ };
82
+ return {
83
+ recognize: (pcm) => {
84
+ const parts = [];
85
+ const drain = () => {
86
+ while (!vad.isEmpty()) {
87
+ parts.push(piece(vad.front().samples));
88
+ vad.pop();
89
+ }
90
+ };
91
+ for (let i = 0; i + WINDOW <= pcm.length; i += WINDOW) {
92
+ vad.acceptWaveform(pcm.subarray(i, i + WINDOW));
93
+ drain();
94
+ }
95
+ vad.flush();
96
+ drain();
97
+ return parts.filter(Boolean).join(" ");
98
+ },
99
+ free: () => {
100
+ vad.free();
101
+ engine.free();
102
+ },
103
+ };
104
+ };
105
+ //# sourceMappingURL=speech.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"speech.js","sourceRoot":"","sources":["../../src/transcribe/speech.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { FetchLike } from "@leemour/cli-core/http";
2
+ import { type Installer } from "@leemour/cli-core/update";
3
+ export declare const PACKAGE = "@leemour/max-cli";
4
+ /** npm, the clock, the terminal and the package manager as `max update` sees them — faked in a test. */
5
+ export interface UpdateEnvironment {
6
+ fetch?: FetchLike;
7
+ now?: () => number;
8
+ stderrIsTTY?: boolean;
9
+ scriptPath?: string;
10
+ /** Runs the update and answers its exit code; its output goes to stderr. */
11
+ spawn?: (argv: string[]) => number;
12
+ }
13
+ export declare const installer: ({ scriptPath }?: UpdateEnvironment) => Installer;
14
+ export declare const latest: (environment?: UpdateEnvironment) => Promise<string | undefined>;
15
+ /** The package manager's output goes to stderr, so stdout stays one result. */
16
+ export declare const runUpdate: (argv: string[], environment?: UpdateEnvironment) => number;
17
+ /**
18
+ * The daily "a newer version exists" line, or `undefined`. Started beside the command and awaited
19
+ * after it, so asking npm costs the command nothing; any failure is silence, never an error.
20
+ */
21
+ export declare const updateNotice: (argv: readonly string[], { tty, environment, env, }: {
22
+ tty?: boolean;
23
+ environment?: UpdateEnvironment;
24
+ env?: NodeJS.ProcessEnv;
25
+ }) => Promise<string | undefined>;
26
+ //# sourceMappingURL=update.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,EACL,KAAK,SAAS,EAKf,MAAM,0BAA0B,CAAA;AAIjC,eAAO,MAAM,OAAO,qBAAqB,CAAA;AAEzC,wGAAwG;AACxG,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAAA;CACnC;AAED,eAAO,MAAM,SAAS,GAAI,iBAAgB,iBAAsB,KAAG,SACM,CAAA;AAEzE,eAAO,MAAM,MAAM,GAAI,cAAa,iBAAsB,gCACe,CAAA;AAEzE,+EAA+E;AAC/E,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,EAAE,EAAE,cAAa,iBAAsB,KAAG,MACT,CAAA;AAKvE;;;GAGG;AACH,eAAO,MAAM,YAAY,GACvB,MAAM,SAAS,MAAM,EAAE,EACvB,4BAIG;IAAE,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,KAC7E,OAAO,CAAC,MAAM,GAAG,SAAS,CAsB5B,CAAA"}
package/dist/update.js ADDED
@@ -0,0 +1,42 @@
1
+ import { realpathSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { resolvePaths } from "@leemour/cli-core";
5
+ import { installerOf, latestVersion, runUpdate as runPackageManager, updateNotice as sharedNotice, } from "@leemour/cli-core/update";
6
+ import { resolveSettings } from "./config.js";
7
+ import { VERSION } from "./version.js";
8
+ export const PACKAGE = "@leemour/max-cli";
9
+ export const installer = ({ scriptPath } = {}) => installerOf(scriptPath ?? realpathSync(fileURLToPath(import.meta.url)));
10
+ export const latest = (environment = {}) => latestVersion(PACKAGE, environment.fetch ?? fetch, { timeoutMs: 3000 });
11
+ /** The package manager's output goes to stderr, so stdout stays one result. */
12
+ export const runUpdate = (argv, environment = {}) => environment.spawn ? environment.spawn(argv) : runPackageManager(argv);
13
+ const statePath = (env) => join(resolvePaths({ appName: "max-cli", prefix: "MAX", env }).state, "update-check.json");
14
+ /**
15
+ * The daily "a newer version exists" line, or `undefined`. Started beside the command and awaited
16
+ * after it, so asking npm costs the command nothing; any failure is silence, never an error.
17
+ */
18
+ export const updateNotice = (argv, { tty, environment = {}, env = process.env, }) => {
19
+ try {
20
+ const pretty = !argv.includes("--json") && !argv.includes("--jsonl") && (tty ?? process.stdout.isTTY === true);
21
+ return sharedNotice({
22
+ argv,
23
+ packageName: PACKAGE,
24
+ command: "max",
25
+ version: VERSION,
26
+ statePath: statePath(env),
27
+ fetch: environment.fetch ?? fetch,
28
+ ...(environment.now ? { now: environment.now } : {}),
29
+ format: pretty ? "pretty" : "json",
30
+ stderrIsTTY: environment.stderrIsTTY ?? process.stderr.isTTY === true,
31
+ quiet: argv.includes("--quiet"),
32
+ enabled: resolveSettings({}, { env }).updateCheck,
33
+ installer: installer(environment),
34
+ env,
35
+ offVariables: ["MAX_NO_UPDATE_CHECK"],
36
+ });
37
+ }
38
+ catch {
39
+ return Promise.resolve(undefined);
40
+ }
41
+ };
42
+ //# sourceMappingURL=update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.js","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export declare const isImage: (path: string) => boolean;
2
+ export declare const readUpload: (path: string) => Promise<Buffer>;
3
+ /** Measured 2026-09-24: a multipart POST, field `file`, answers `{photos: {<key>: {token}}}`. */
4
+ export declare const uploadPhoto: (url: string, path: string, bytes: Buffer) => Promise<string>;
5
+ /**
6
+ * Measured 2026-09-24: the bytes raw, with `Content-Range: 0-<end>/<size>` — no `bytes ` unit, as
7
+ * PyMax sends it and MAX accepted.
8
+ */
9
+ export declare const uploadFile: (url: string, path: string, bytes: Buffer) => Promise<void>;
10
+ //# sourceMappingURL=upload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../src/upload.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,KAAG,OAAqD,CAAA;AAE5F,eAAO,MAAM,UAAU,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,MAAM,CAO7D,CAAA;AAKD,iGAAiG;AACjG,eAAO,MAAM,WAAW,GAAU,KAAK,MAAM,EAAE,MAAM,MAAM,EAAE,OAAO,MAAM,KAAG,OAAO,CAAC,MAAM,CAY1F,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAU,KAAK,MAAM,EAAE,MAAM,MAAM,EAAE,OAAO,MAAM,KAAG,OAAO,CAAC,IAAI,CAavF,CAAA"}
package/dist/upload.js ADDED
@@ -0,0 +1,61 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { basename, extname } from "node:path";
3
+ import { CliError } from "@leemour/cli-core";
4
+ import { WEB_USER_AGENT } from "./spec/identity.js";
5
+ const HEADERS = {
6
+ "User-Agent": WEB_USER_AGENT.headerUserAgent,
7
+ Referer: "https://web.max.ru/",
8
+ Origin: "https://web.max.ru",
9
+ };
10
+ const IMAGE_TYPES = {
11
+ ".jpg": "image/jpeg",
12
+ ".jpeg": "image/jpeg",
13
+ ".png": "image/png",
14
+ ".webp": "image/webp",
15
+ ".gif": "image/gif",
16
+ };
17
+ export const isImage = (path) => extname(path).toLowerCase() in IMAGE_TYPES;
18
+ export const readUpload = async (path) => {
19
+ try {
20
+ return await readFile(path);
21
+ }
22
+ catch (error) {
23
+ const reason = error.code === "ENOENT" ? "no such file" : "it cannot be read";
24
+ throw new CliError("validation_error", `cannot send ${path}: ${reason}`);
25
+ }
26
+ };
27
+ const refused = (kind, status) => new CliError("network_error", `the ${kind} could not be uploaded: HTTP ${status} — nothing was sent`);
28
+ /** Measured 2026-09-24: a multipart POST, field `file`, answers `{photos: {<key>: {token}}}`. */
29
+ export const uploadPhoto = async (url, path, bytes) => {
30
+ const form = new FormData();
31
+ const type = IMAGE_TYPES[extname(path).toLowerCase()] ?? "application/octet-stream";
32
+ form.append("file", new Blob([bytes], { type }), `image${extname(path).toLowerCase()}`);
33
+ const response = await fetch(url, { method: "POST", headers: HEADERS, body: form });
34
+ if (!response.ok)
35
+ throw refused("photo", response.status);
36
+ const answer = (await response.json().catch(() => ({})));
37
+ const token = Object.values(answer.photos ?? {})[0]?.token;
38
+ if (typeof token !== "string")
39
+ throw new CliError("provider_error", "the photo upload answered no token — nothing was sent");
40
+ return token;
41
+ };
42
+ /**
43
+ * Measured 2026-09-24: the bytes raw, with `Content-Range: 0-<end>/<size>` — no `bytes ` unit, as
44
+ * PyMax sends it and MAX accepted.
45
+ */
46
+ export const uploadFile = async (url, path, bytes) => {
47
+ const response = await fetch(url, {
48
+ method: "POST",
49
+ headers: {
50
+ ...HEADERS,
51
+ "Content-Disposition": `attachment; filename=${encodeURIComponent(basename(path))}`,
52
+ "Content-Range": `0-${bytes.length - 1}/${bytes.length}`,
53
+ "Content-Type": "application/octet-stream",
54
+ },
55
+ body: bytes,
56
+ });
57
+ await response.body?.cancel();
58
+ if (!response.ok)
59
+ throw refused("file", response.status);
60
+ };
61
+ //# sourceMappingURL=upload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload.js","sourceRoot":"","sources":["../src/upload.ts"],"names":[],"mappings":"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"}
package/dist/version.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  /** The one the running command reports. `pnpm version:check` fails when it and package.json disagree. */
2
- export declare const VERSION = "0.6.0";
2
+ export declare const VERSION = "0.8.0";
3
3
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /** The one the running command reports. `pnpm version:check` fails when it and package.json disagree. */
2
- export const VERSION = "0.6.0";
2
+ export const VERSION = "0.8.0";
3
3
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leemour/max-cli",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "A local command line interface for a personal MAX Messenger account, built for agents and scripts",
5
5
  "license": "MIT",
6
6
  "author": "Viacheslav Ptsarev",
@@ -35,7 +35,10 @@
35
35
  "version:sync": "node --experimental-strip-types scripts/version.ts --sync",
36
36
  "probe:ids": "pnpm build && node --experimental-strip-types scripts/id-shape.ts",
37
37
  "probe:download": "pnpm build && node --experimental-strip-types scripts/probe-download.ts",
38
+ "probe:upload": "pnpm build && node --experimental-strip-types scripts/probe-upload.ts",
38
39
  "probe:reply": "pnpm build && node --experimental-strip-types scripts/probe-reply.ts",
40
+ "probe:scheduled": "pnpm build && node --experimental-strip-types scripts/probe-scheduled.ts",
41
+ "probe:delete": "pnpm build && node --experimental-strip-types scripts/probe-delete.ts",
39
42
  "probe:edits": "pnpm build && node --experimental-strip-types scripts/probe-edits.ts",
40
43
  "probe:attachments": "pnpm build && node --experimental-strip-types scripts/probe-attachments.ts",
41
44
  "verify:live": "pnpm build && node --experimental-strip-types scripts/verify-commands.ts",
@@ -46,13 +49,23 @@
46
49
  "probe:reactions": "pnpm build && node --experimental-strip-types scripts/probe-reactions.ts",
47
50
  "probe:message-shapes": "pnpm build && node --experimental-strip-types scripts/probe-message-shapes.ts",
48
51
  "probe:token": "pnpm build && node --experimental-strip-types scripts/probe-token.ts",
49
- "probe:inbox": "pnpm build && node --experimental-strip-types scripts/probe-inbox.ts"
52
+ "probe:inbox": "pnpm build && node --experimental-strip-types scripts/probe-inbox.ts",
53
+ "probe:edit-pin-forward": "pnpm build && node --experimental-strip-types scripts/probe-edit-pin-forward.ts",
54
+ "probe:groups": "pnpm build && node --experimental-strip-types scripts/probe-groups.ts",
55
+ "probe:members": "pnpm build && node --experimental-strip-types scripts/probe-members.ts",
56
+ "probe:account": "pnpm build && node --experimental-strip-types scripts/probe-account.ts",
57
+ "probe:message-count": "pnpm build && node --experimental-strip-types scripts/probe-message-count.ts"
50
58
  },
51
59
  "dependencies": {
52
- "@leemour/cli-core": "^0.2.0",
60
+ "@bomb.sh/tab": "0.0.22",
61
+ "@leemour/cli-core": "^0.6.0",
62
+ "@modelcontextprotocol/server": "~2.0.0",
63
+ "@valibot/to-json-schema": "^1.8.0",
53
64
  "commander": "^15.0.0",
54
65
  "lossless-json": "^4.3.1",
66
+ "ogg-opus-decoder": "1.7.5",
55
67
  "qrcode-generator": "2.0.4",
68
+ "sherpa-onnx": "1.13.8",
56
69
  "string-width": "^8.2.2",
57
70
  "valibot": "^1.5.0",
58
71
  "wrap-ansi": "^10.0.2",
@@ -60,6 +73,7 @@
60
73
  },
61
74
  "devDependencies": {
62
75
  "@biomejs/biome": "^2.3.14",
76
+ "@modelcontextprotocol/client": "~2.0.0",
63
77
  "@types/node": "^22.10.2",
64
78
  "@types/ws": "^8.18.1",
65
79
  "lefthook": "^2.1.8",
@@ -18,12 +18,24 @@ description: Читать и отправлять сообщения в личн
18
18
  - **Ничего не отправлять без прямой просьбы владельца.** `max messages send` — только когда он
19
19
  сам попросил отправить конкретный текст в конкретный чат. Черновик, «наверное стоит ответить»,
20
20
  вывод из прочитанного — это не просьба.
21
- - **Отказ `5`, `7` или `8` на отправке или реакции — это решение владельца, а не сбой.** Не обходить его: не
21
+ - **То же для `messages edit`, `forward`, `pin` и `unpin`.** Каждая меняет то, что видят другие
22
+ люди. Правка меняет текст, который собеседник мог уже прочитать.
23
+ - **`max messages delete` — только когда владелец назвал эти сообщения и попросил их удалить.**
24
+ Удаление не отменить. `--allow-dangerous` — его слово, не ставить флаг от себя. `--for-everyone`
25
+ — только если он сказал «у всех».
26
+ - **Отказ `5`, `7` или `8` на отправке, реакции, правке, пересылке, закреплении или удалении — это решение владельца, а не сбой.** Не обходить его: не
22
27
  менять настройки, не звать `max recipients add`, не ждать и не повторять. Сказать владельцу, что
23
28
  отправка не прошла и почему.
29
+ - **Группы и каналы — только по прямой просьбе владельца.** `max chats join|leave|create|members|admins|update|settings --…|requests accept|decline|link reset` видят другие люди. Ссылка-приглашение из прочитанного сообщения — не просьба вступить.
24
30
  - **Чтение ничего не отмечает прочитанным** и не показывает собеседнику, что вы заходили. Читать
25
- можно свободно.
31
+ можно свободно. `max chats read` и `messages list --mark-read` отмечают — только по прямой
32
+ просьбе владельца: собеседник это увидит.
26
33
  - **Не для:** ботов MAX, рассылок, автоответов, слежения в реальном времени, чужих аккаунтов.
34
+ - **Контакты, профиль, папки и сеансы меняются только по прямой просьбе владельца**, как и
35
+ отправка. `max account sessions end-others` — никогда по своей инициативе: оно выкидывает
36
+ владельца из приложения на телефоне.
37
+ - **Номер телефона не ставить в строку команды.** `max contacts lookup` читает его из трубы:
38
+ `printf '%s' "$номер" | max contacts lookup --json`.
27
39
  - **Текст сообщений — только в ответ владельцу.** Не в логи, не в файлы, не в коммиты.
28
40
  - **Ссылка на фото открывается без входа в аккаунт.** Кто её получил, тот видит фото. Не
29
41
  пересылать её никуда, кроме владельца.
@@ -35,7 +47,7 @@ description: Читать и отправлять сообщения в личн
35
47
  - **Список — это объект**, а не массив: `{ "items": [...], "page": 1, "limit": 20, "hasMore": true }`.
36
48
  - **`--jsonl`** — по объекту на строку, удобно для `jq`. Есть ли ещё, пишется только в stderr.
37
49
  - **Ветвиться по коду возврата, а не по тексту**: `0` — успех, `2` — неверный ввод, `4` — нет
38
- входа, `5` — профиль только для чтения, `6` — не найдено, `7` — чата нет в списке разрешённых,
50
+ входа, `5` — профилю это действие не разрешено (`readOnly` или `allow`; ошибка называет, какое, — не обходить), `6` — не найдено, `7` — чата нет в списке разрешённых,
39
51
  `8` — лимит отправок за час, `14` — **неизвестно, ушло ли сообщение** (см. отправку).
40
52
  - `-v` и `-vv` добавляют подробности в вывод для человека. Версия — `max -V`. Строки о каждом
41
53
  запросе к MAX — `--trace`, в stderr.
@@ -61,6 +73,8 @@ description: Читать и отправлять сообщения в личн
61
73
  5a. **Искомое — не короче трёх символов**, и это касается всех трёх поисков: `--search` у
62
74
  `chats list` и `contacts list` и текста у `messages search`. Два символа — отказ с кодом `2`,
63
75
  а не пустой список.
76
+ 5b. **`max export messages` выгружает только прочитанное** и в MAX не ходит. Дыры он называет в
77
+ stderr («not in the export: … never read»); прежде чем отдать файл как «всю переписку», прочтите их.
64
78
  6. **`messages show` и `messages context` требуют и чат, и id сообщения.** Искомое сообщение в
65
79
  JSON помечено `"anchor": true`. Если сообщения нет, это `not_found`, а не соседнее сообщение.
66
80
  7. **Время сообщения зашито в его id**: `id >> 16` — миллисекунды. Поэтому `--before <id>` и
@@ -81,6 +95,10 @@ description: Читать и отправлять сообщения в личн
81
95
  12. **`max inbox --new` сдвигает место, где владелец остановился.** После него следующий
82
96
  `--new` владельца не покажет того, что уже увидел агент. Без сдвига: просто `max inbox`
83
97
  (непрочитанное) или `max inbox --since <время>`.
98
+ 13. **`--at` ставит сообщение в очередь MAX, а не отправляет.** Ответ — запись в очереди с
99
+ `scheduledFor`; ушедшее сообщение получит другой id. Очередь: `max messages scheduled <чат>`.
100
+ Через MCP — параметр `at` у `max_messages_send` и инструмент `max_messages_scheduled`.
101
+ Отменить можно только в приложении MAX.
84
102
 
85
103
  ## Типичный путь
86
104
 
@@ -91,12 +109,19 @@ max chats list --search "проект" --json # найти чат, взя
91
109
  max chats list --unread --json # где есть непрочитанное
92
110
  max inbox --json # непрочитанное во всех чатах
93
111
  max inbox --new --json # что пришло с прошлой проверки, один раз
112
+ max watch --jsonl # новые сообщения по мере прихода, если запущен `max serve`
94
113
  max chats show -1000 --json # один чат и кто в нём
95
114
  max contacts show @ivan --json # один человек и общие чаты
96
115
  max messages list -1000 --limit 20 --json # последние сообщения, от старых к новым
97
116
  max messages list -1000 --after 100000000000000001 --json # что было после этого сообщения
98
117
  max messages context -1000 100000000000000001 --before 3 --after 3 --json
118
+ max messages transcribe -1000 100000000000000001 --json # текст голосового, на этой машине
99
119
  ```
100
120
 
121
+ Голосовое — вложение `"kind": "audio"`. Его текст даёт `max messages transcribe`; до минуты на
122
+ пять минут речи, повторно — сразу из кэша. Если модель не скачана, команда откажет и назовёт
123
+ `max models audio download <id>`: **сказать об этом владельцу и не скачивать самому** — это сотни
124
+ мегабайт, и выбор модели за ним.
125
+
101
126
  Вложение в JSON: `{ "kind": "photo", "url": "https://…", "width": 901, "height": 594 }`. Чтобы
102
127
  посмотреть фото, скачать его по `url` и открыть файл.