@leemour/max-cli 0.5.0 → 0.7.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 (261) hide show
  1. package/README.md +22 -5
  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/store.d.ts.map +1 -1
  7. package/dist/cache/store.js +1 -0
  8. package/dist/cache/store.js.map +1 -1
  9. package/dist/client.d.ts +230 -7
  10. package/dist/client.d.ts.map +1 -1
  11. package/dist/client.js +959 -78
  12. package/dist/client.js.map +1 -1
  13. package/dist/commands/account.d.ts.map +1 -1
  14. package/dist/commands/account.js +71 -3
  15. package/dist/commands/account.js.map +1 -1
  16. package/dist/commands/chats.d.ts.map +1 -1
  17. package/dist/commands/chats.js +159 -0
  18. package/dist/commands/chats.js.map +1 -1
  19. package/dist/commands/commands.d.ts +8 -0
  20. package/dist/commands/commands.d.ts.map +1 -0
  21. package/dist/commands/commands.js +36 -0
  22. package/dist/commands/commands.js.map +1 -0
  23. package/dist/commands/complete.d.ts +10 -0
  24. package/dist/commands/complete.d.ts.map +1 -0
  25. package/dist/commands/complete.js +89 -0
  26. package/dist/commands/complete.js.map +1 -0
  27. package/dist/commands/config.d.ts.map +1 -1
  28. package/dist/commands/config.js +6 -2
  29. package/dist/commands/config.js.map +1 -1
  30. package/dist/commands/contacts.d.ts +3 -0
  31. package/dist/commands/contacts.d.ts.map +1 -1
  32. package/dist/commands/contacts.js +85 -0
  33. package/dist/commands/contacts.js.map +1 -1
  34. package/dist/commands/context.d.ts +26 -1
  35. package/dist/commands/context.d.ts.map +1 -1
  36. package/dist/commands/context.js +47 -6
  37. package/dist/commands/context.js.map +1 -1
  38. package/dist/commands/folders.d.ts +4 -0
  39. package/dist/commands/folders.d.ts.map +1 -0
  40. package/dist/commands/folders.js +76 -0
  41. package/dist/commands/folders.js.map +1 -0
  42. package/dist/commands/inbox.d.ts +14 -0
  43. package/dist/commands/inbox.d.ts.map +1 -0
  44. package/dist/commands/inbox.js +90 -0
  45. package/dist/commands/inbox.js.map +1 -0
  46. package/dist/commands/mcp.d.ts +3 -0
  47. package/dist/commands/mcp.d.ts.map +1 -0
  48. package/dist/commands/mcp.js +17 -0
  49. package/dist/commands/mcp.js.map +1 -0
  50. package/dist/commands/messages.d.ts.map +1 -1
  51. package/dist/commands/messages.js +90 -4
  52. package/dist/commands/messages.js.map +1 -1
  53. package/dist/commands/reactions.d.ts +3 -0
  54. package/dist/commands/reactions.d.ts.map +1 -0
  55. package/dist/commands/reactions.js +43 -0
  56. package/dist/commands/reactions.js.map +1 -0
  57. package/dist/commands/recipients.d.ts +9 -0
  58. package/dist/commands/recipients.d.ts.map +1 -0
  59. package/dist/commands/recipients.js +68 -0
  60. package/dist/commands/recipients.js.map +1 -0
  61. package/dist/commands/sends.d.ts +4 -0
  62. package/dist/commands/sends.d.ts.map +1 -0
  63. package/dist/commands/sends.js +21 -0
  64. package/dist/commands/sends.js.map +1 -0
  65. package/dist/commands/serve.d.ts +10 -0
  66. package/dist/commands/serve.d.ts.map +1 -0
  67. package/dist/commands/serve.js +50 -0
  68. package/dist/commands/serve.js.map +1 -0
  69. package/dist/commands/session.d.ts.map +1 -1
  70. package/dist/commands/session.js +84 -16
  71. package/dist/commands/session.js.map +1 -1
  72. package/dist/commands/skill.d.ts.map +1 -1
  73. package/dist/commands/skill.js +2 -1
  74. package/dist/commands/skill.js.map +1 -1
  75. package/dist/commands/update.d.ts +7 -0
  76. package/dist/commands/update.d.ts.map +1 -0
  77. package/dist/commands/update.js +53 -0
  78. package/dist/commands/update.js.map +1 -0
  79. package/dist/commands/watch.d.ts +9 -0
  80. package/dist/commands/watch.d.ts.map +1 -0
  81. package/dist/commands/watch.js +68 -0
  82. package/dist/commands/watch.js.map +1 -0
  83. package/dist/config.d.ts +24 -1
  84. package/dist/config.d.ts.map +1 -1
  85. package/dist/config.js +43 -4
  86. package/dist/config.js.map +1 -1
  87. package/dist/domain/map.d.ts +10 -1
  88. package/dist/domain/map.d.ts.map +1 -1
  89. package/dist/domain/map.js +51 -1
  90. package/dist/domain/map.js.map +1 -1
  91. package/dist/domain/models.d.ts +80 -0
  92. package/dist/domain/models.d.ts.map +1 -1
  93. package/dist/domain/models.js.map +1 -1
  94. package/dist/generated/client.generated.d.ts +36 -0
  95. package/dist/generated/client.generated.d.ts.map +1 -1
  96. package/dist/generated/client.generated.js +36 -0
  97. package/dist/generated/client.generated.js.map +1 -1
  98. package/dist/generated/opcodes.generated.d.ts +37 -5
  99. package/dist/generated/opcodes.generated.d.ts.map +1 -1
  100. package/dist/generated/opcodes.generated.js +37 -5
  101. package/dist/generated/opcodes.generated.js.map +1 -1
  102. package/dist/generated/operations.generated.d.ts +245 -3
  103. package/dist/generated/operations.generated.d.ts.map +1 -1
  104. package/dist/generated/operations.generated.js +36 -4
  105. package/dist/generated/operations.generated.js.map +1 -1
  106. package/dist/markdown.d.ts +18 -0
  107. package/dist/markdown.d.ts.map +1 -0
  108. package/dist/markdown.js +52 -0
  109. package/dist/markdown.js.map +1 -0
  110. package/dist/mcp/confirm.d.ts +24 -0
  111. package/dist/mcp/confirm.d.ts.map +1 -0
  112. package/dist/mcp/confirm.js +53 -0
  113. package/dist/mcp/confirm.js.map +1 -0
  114. package/dist/mcp/instructions.d.ts +10 -0
  115. package/dist/mcp/instructions.d.ts.map +1 -0
  116. package/dist/mcp/instructions.js +25 -0
  117. package/dist/mcp/instructions.js.map +1 -0
  118. package/dist/mcp/server.d.ts +23 -0
  119. package/dist/mcp/server.d.ts.map +1 -0
  120. package/dist/mcp/server.js +38 -0
  121. package/dist/mcp/server.js.map +1 -0
  122. package/dist/mcp/session.d.ts +28 -0
  123. package/dist/mcp/session.d.ts.map +1 -0
  124. package/dist/mcp/session.js +91 -0
  125. package/dist/mcp/session.js.map +1 -0
  126. package/dist/mcp/tools.d.ts +8 -0
  127. package/dist/mcp/tools.d.ts.map +1 -0
  128. package/dist/mcp/tools.js +274 -0
  129. package/dist/mcp/tools.js.map +1 -0
  130. package/dist/program.d.ts.map +1 -1
  131. package/dist/program.js +27 -0
  132. package/dist/program.js.map +1 -1
  133. package/dist/protocol/connection.d.ts +10 -8
  134. package/dist/protocol/connection.d.ts.map +1 -1
  135. package/dist/protocol/connection.js +64 -7
  136. package/dist/protocol/connection.js.map +1 -1
  137. package/dist/protocol/frame.d.ts +2 -1
  138. package/dist/protocol/frame.d.ts.map +1 -1
  139. package/dist/protocol/frame.js.map +1 -1
  140. package/dist/rendering/messages.d.ts.map +1 -1
  141. package/dist/rendering/messages.js +21 -7
  142. package/dist/rendering/messages.js.map +1 -1
  143. package/dist/resolve.d.ts +15 -0
  144. package/dist/resolve.d.ts.map +1 -0
  145. package/dist/resolve.js +48 -0
  146. package/dist/resolve.js.map +1 -0
  147. package/dist/sends/guard.d.ts +27 -0
  148. package/dist/sends/guard.d.ts.map +1 -0
  149. package/dist/sends/guard.js +52 -0
  150. package/dist/sends/guard.js.map +1 -0
  151. package/dist/sends/journal.d.ts +42 -0
  152. package/dist/sends/journal.d.ts.map +1 -0
  153. package/dist/sends/journal.js +40 -0
  154. package/dist/sends/journal.js.map +1 -0
  155. package/dist/sends/recipients.d.ts +29 -0
  156. package/dist/sends/recipients.d.ts.map +1 -0
  157. package/dist/sends/recipients.js +62 -0
  158. package/dist/sends/recipients.js.map +1 -0
  159. package/dist/server/lines.d.ts +9 -0
  160. package/dist/server/lines.d.ts.map +1 -0
  161. package/dist/server/lines.js +25 -0
  162. package/dist/server/lines.js.map +1 -0
  163. package/dist/server/server-connection.d.ts +35 -0
  164. package/dist/server/server-connection.d.ts.map +1 -0
  165. package/dist/server/server-connection.js +195 -0
  166. package/dist/server/server-connection.js.map +1 -0
  167. package/dist/server/server.d.ts +62 -0
  168. package/dist/server/server.d.ts.map +1 -0
  169. package/dist/server/server.js +297 -0
  170. package/dist/server/server.js.map +1 -0
  171. package/dist/server/start.d.ts +12 -0
  172. package/dist/server/start.d.ts.map +1 -0
  173. package/dist/server/start.js +41 -0
  174. package/dist/server/start.js.map +1 -0
  175. package/dist/server/subscribe.d.ts +9 -0
  176. package/dist/server/subscribe.d.ts.map +1 -0
  177. package/dist/server/subscribe.js +26 -0
  178. package/dist/server/subscribe.js.map +1 -0
  179. package/dist/session/adopt.d.ts.map +1 -1
  180. package/dist/session/adopt.js +7 -6
  181. package/dist/session/adopt.js.map +1 -1
  182. package/dist/session/browser.d.ts +55 -0
  183. package/dist/session/browser.d.ts.map +1 -0
  184. package/dist/session/browser.js +319 -0
  185. package/dist/session/browser.js.map +1 -0
  186. package/dist/session/handshake.d.ts +2 -0
  187. package/dist/session/handshake.d.ts.map +1 -1
  188. package/dist/session/handshake.js +3 -1
  189. package/dist/session/handshake.js.map +1 -1
  190. package/dist/session/login.d.ts +20 -0
  191. package/dist/session/login.d.ts.map +1 -0
  192. package/dist/session/login.js +66 -0
  193. package/dist/session/login.js.map +1 -0
  194. package/dist/session/prompt.d.ts +3 -1
  195. package/dist/session/prompt.d.ts.map +1 -1
  196. package/dist/session/prompt.js +2 -2
  197. package/dist/session/prompt.js.map +1 -1
  198. package/dist/session/qr-terminal.d.ts +9 -0
  199. package/dist/session/qr-terminal.d.ts.map +1 -0
  200. package/dist/session/qr-terminal.js +34 -0
  201. package/dist/session/qr-terminal.js.map +1 -0
  202. package/dist/session/store.d.ts +4 -0
  203. package/dist/session/store.d.ts.map +1 -1
  204. package/dist/session/store.js +6 -0
  205. package/dist/session/store.js.map +1 -1
  206. package/dist/spec/define.d.ts.map +1 -1
  207. package/dist/spec/define.js +14 -1
  208. package/dist/spec/define.js.map +1 -1
  209. package/dist/spec/index.d.ts.map +1 -1
  210. package/dist/spec/index.js +38 -5
  211. package/dist/spec/index.js.map +1 -1
  212. package/dist/spec/operations/account.d.ts +20 -0
  213. package/dist/spec/operations/account.d.ts.map +1 -0
  214. package/dist/spec/operations/account.js +62 -0
  215. package/dist/spec/operations/account.js.map +1 -0
  216. package/dist/spec/operations/chats.d.ts +54 -0
  217. package/dist/spec/operations/chats.d.ts.map +1 -1
  218. package/dist/spec/operations/chats.js +121 -0
  219. package/dist/spec/operations/chats.js.map +1 -1
  220. package/dist/spec/operations/contacts.d.ts +19 -1
  221. package/dist/spec/operations/contacts.d.ts.map +1 -1
  222. package/dist/spec/operations/contacts.js +49 -6
  223. package/dist/spec/operations/contacts.js.map +1 -1
  224. package/dist/spec/operations/folders.d.ts +24 -0
  225. package/dist/spec/operations/folders.d.ts.map +1 -0
  226. package/dist/spec/operations/folders.js +64 -0
  227. package/dist/spec/operations/folders.js.map +1 -0
  228. package/dist/spec/operations/login.d.ts +64 -0
  229. package/dist/spec/operations/login.d.ts.map +1 -0
  230. package/dist/spec/operations/login.js +99 -0
  231. package/dist/spec/operations/login.js.map +1 -0
  232. package/dist/spec/operations/messages.d.ts +67 -3
  233. package/dist/spec/operations/messages.d.ts.map +1 -1
  234. package/dist/spec/operations/messages.js +126 -13
  235. package/dist/spec/operations/messages.js.map +1 -1
  236. package/dist/spec/operations/session.d.ts +7 -0
  237. package/dist/spec/operations/session.d.ts.map +1 -1
  238. package/dist/spec/operations/session.js +17 -0
  239. package/dist/spec/operations/session.js.map +1 -1
  240. package/dist/spec/operations/uploads.d.ts +18 -0
  241. package/dist/spec/operations/uploads.d.ts.map +1 -0
  242. package/dist/spec/operations/uploads.js +35 -0
  243. package/dist/spec/operations/uploads.js.map +1 -0
  244. package/dist/testing/mock-max.d.ts +15 -4
  245. package/dist/testing/mock-max.d.ts.map +1 -1
  246. package/dist/testing/mock-max.js +16 -3
  247. package/dist/testing/mock-max.js.map +1 -1
  248. package/dist/testing/sandbox.js +3 -0
  249. package/dist/testing/sandbox.js.map +1 -1
  250. package/dist/update.d.ts +36 -0
  251. package/dist/update.d.ts.map +1 -0
  252. package/dist/update.js +70 -0
  253. package/dist/update.js.map +1 -0
  254. package/dist/upload.d.ts +10 -0
  255. package/dist/upload.d.ts.map +1 -0
  256. package/dist/upload.js +61 -0
  257. package/dist/upload.js.map +1 -0
  258. package/dist/version.d.ts +1 -1
  259. package/dist/version.js +1 -1
  260. package/package.json +16 -3
  261. package/skills/max-cli/SKILL.md +23 -3
@@ -0,0 +1,319 @@
1
+ import { spawn } from "node:child_process";
2
+ import { randomBytes } from "node:crypto";
3
+ import { existsSync, mkdtempSync, realpathSync, rmSync } from "node:fs";
4
+ import { createServer } from "node:http";
5
+ import { tmpdir } from "node:os";
6
+ import { delimiter, join } from "node:path";
7
+ import { setTimeout as delay } from "node:timers/promises";
8
+ import { CliError } from "@leemour/cli-core";
9
+ import qrcode from "qrcode-generator";
10
+ export const realBrowser = {
11
+ open: (url) => openUrl(url),
12
+ chromiumToken: (options) => tokenFromChromium(options),
13
+ };
14
+ const WEB_MAX = "https://web.max.ru/";
15
+ const AUTH_KEY = "__oneme_auth";
16
+ /**
17
+ * The QR page, served from `127.0.0.1` rather than written to a file: a browser installed as a
18
+ * snap cannot read our temporary directory, and a file would leave the link on disk. The path is
19
+ * random, so another local user guessing the port finds nothing.
20
+ */
21
+ export const serveQrPage = async (link) => {
22
+ const path = `/${randomBytes(16).toString("hex")}`;
23
+ const code = qrcode(0, "M");
24
+ code.addData(link);
25
+ code.make();
26
+ const page = qrHtml(code.createSvgTag({ cellSize: 8, margin: 4, scalable: true }));
27
+ const server = createServer((request, response) => {
28
+ if (request.url !== path) {
29
+ response.writeHead(404).end();
30
+ return;
31
+ }
32
+ response
33
+ .writeHead(200, {
34
+ "content-type": "text/html; charset=utf-8",
35
+ "cache-control": "no-store",
36
+ "referrer-policy": "no-referrer",
37
+ })
38
+ .end(page);
39
+ });
40
+ await new Promise((resolve, reject) => {
41
+ server.once("error", reject);
42
+ server.listen(0, "127.0.0.1", resolve);
43
+ });
44
+ const { port } = server.address();
45
+ return {
46
+ url: `http://127.0.0.1:${port}${path}`,
47
+ close: () => new Promise((resolve) => {
48
+ server.closeAllConnections();
49
+ server.close(() => resolve());
50
+ }),
51
+ };
52
+ };
53
+ const qrHtml = (svg) => `<!doctype html>
54
+ <html lang="ru"><head><meta charset="utf-8"><title>MAX — вход</title>
55
+ <style>body{font-family:system-ui,sans-serif;display:grid;place-items:center;min-height:100vh;margin:0;background:#fff;color:#111}
56
+ main{text-align:center;max-width:22rem}svg{width:18rem;height:18rem}</style></head>
57
+ <body><main>${svg}<p>Отсканируйте код приложением MAX на телефоне. Код действует около двух минут.</p>
58
+ <p>После входа эту вкладку можно закрыть.</p></main></body></html>`;
59
+ export const openUrl = async (url) => {
60
+ refuseInTests();
61
+ const [command, args] = process.platform === "darwin"
62
+ ? ["open", [url]]
63
+ : process.platform === "win32"
64
+ ? ["cmd", ["/c", "start", "", url]]
65
+ : ["xdg-open", [url]];
66
+ await new Promise((resolve, reject) => {
67
+ const child = spawn(command, args, { stdio: "ignore", detached: true, windowsHide: true });
68
+ child.once("error", () => reject(new CliError("configuration_error", "no browser could be opened here — use `session start token`")));
69
+ child.once("spawn", () => {
70
+ child.unref();
71
+ resolve();
72
+ });
73
+ });
74
+ };
75
+ /**
76
+ * Chrome, Chromium, Edge or Brave — any browser that speaks the DevTools protocol. `MAX_BROWSER`
77
+ * names another. A snap build is skipped: it keeps its own `/tmp`, so the profile we hand it would
78
+ * land where we cannot delete it.
79
+ */
80
+ export const findChromium = () => {
81
+ const named = process.env.MAX_BROWSER;
82
+ if (named)
83
+ return named;
84
+ if (process.platform === "darwin") {
85
+ return [
86
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
87
+ "/Applications/Chromium.app/Contents/MacOS/Chromium",
88
+ "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
89
+ "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
90
+ ].find((path) => existsSync(path));
91
+ }
92
+ if (process.platform === "win32") {
93
+ const roots = [process.env.PROGRAMFILES, process.env["PROGRAMFILES(X86)"], process.env.LOCALAPPDATA];
94
+ const apps = [
95
+ "Google\\Chrome\\Application\\chrome.exe",
96
+ "Microsoft\\Edge\\Application\\msedge.exe",
97
+ "BraveSoftware\\Brave-Browser\\Application\\brave.exe",
98
+ ];
99
+ return apps
100
+ .flatMap((app) => roots.filter((root) => root !== undefined).map((root) => join(root, app)))
101
+ .find(existsSync);
102
+ }
103
+ const names = [
104
+ "google-chrome",
105
+ "google-chrome-stable",
106
+ "chromium",
107
+ "chromium-browser",
108
+ "microsoft-edge",
109
+ "microsoft-edge-stable",
110
+ "brave-browser",
111
+ ];
112
+ const dirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
113
+ for (const name of names) {
114
+ for (const dir of dirs) {
115
+ const path = join(dir, name);
116
+ if (existsSync(path) && !realpathSync(path).startsWith("/snap/"))
117
+ return path;
118
+ }
119
+ }
120
+ return undefined;
121
+ };
122
+ /**
123
+ * web.max.ru logs itself in — it asks MAX for the QR code, or sends the SMS — in a real browser with
124
+ * a profile of its own, and we read the token it stores once it has one. MAX sees its own web
125
+ * client do the login, not our socket.
126
+ *
127
+ * ⚠ The profile holds a live session, so it is deleted on every way out: success, a closed window,
128
+ * `--timeout`, Ctrl-C.
129
+ */
130
+ export const tokenFromChromium = async ({ track, waitMs, }) => {
131
+ refuseInTests();
132
+ const binary = findChromium();
133
+ if (!binary) {
134
+ throw new CliError("configuration_error", "no Chrome, Chromium, Edge or Brave found — set MAX_BROWSER to one, or use `session start qr`");
135
+ }
136
+ const profile = mkdtempSync(join(tmpdir(), "max-login-"));
137
+ // A pipe, not `--remote-debugging-port`: a port on 127.0.0.1 lets any local user read the token
138
+ // while the window is open. Chrome reads commands on fd 3 and answers on fd 4.
139
+ const child = spawn(binary, [`--user-data-dir=${profile}`, "--remote-debugging-pipe", "--no-first-run", "--no-default-browser-check", WEB_MAX], { stdio: ["ignore", "ignore", "ignore", "pipe", "pipe"] });
140
+ const browser = new ChromiumSession(child, profile);
141
+ track(browser);
142
+ const stopSignals = closeOnSignal(browser);
143
+ try {
144
+ return await browser.waitForToken(waitMs);
145
+ }
146
+ finally {
147
+ stopSignals();
148
+ await browser.close();
149
+ }
150
+ };
151
+ export class ChromiumSession {
152
+ #child;
153
+ #profile;
154
+ #devtools;
155
+ #exited;
156
+ #graceMs;
157
+ #page;
158
+ #closing;
159
+ constructor(child, profile, graceMs = 5_000) {
160
+ this.#child = child;
161
+ this.#profile = profile;
162
+ this.#graceMs = graceMs;
163
+ this.#devtools = new DevToolsPipe(child);
164
+ this.#exited = new Promise((resolve) => {
165
+ child.once("exit", () => resolve());
166
+ child.once("error", () => resolve());
167
+ });
168
+ }
169
+ async waitForToken(waitMs) {
170
+ const deadline = Date.now() + waitMs;
171
+ let running = true;
172
+ void this.#exited.then(() => {
173
+ running = false;
174
+ });
175
+ while (Date.now() < deadline) {
176
+ if (!running)
177
+ throw new CliError("cancelled", "the browser was closed before the login finished");
178
+ const token = await this.#storedToken().catch(() => undefined);
179
+ if (token)
180
+ return token;
181
+ await delay(1_000);
182
+ }
183
+ throw new CliError("timeout", `nobody logged in within ${Math.round(waitMs / 60_000)} minutes`);
184
+ }
185
+ close() {
186
+ this.#closing ??= this.#shutdown();
187
+ return this.#closing;
188
+ }
189
+ async #shutdown() {
190
+ // Chrome writes to the profile while it shuts down; removing it first leaves half of it behind.
191
+ await this.#devtools.send("Browser.close").catch(() => undefined);
192
+ const exited = await Promise.race([this.#exited.then(() => true), delay(this.#graceMs).then(() => false)]);
193
+ if (!exited && this.#child.pid !== undefined) {
194
+ this.#child.kill();
195
+ await Promise.race([this.#exited, delay(2_000)]);
196
+ }
197
+ rmSync(this.#profile, { recursive: true, force: true, maxRetries: 3 });
198
+ }
199
+ async #storedToken() {
200
+ const { targetInfos } = await this.#devtools.send("Target.getTargets");
201
+ const page = (Array.isArray(targetInfos) ? targetInfos : []).find((target) => target?.type === "page" && typeof target.url === "string" && target.url.startsWith(WEB_MAX));
202
+ if (typeof page?.targetId !== "string")
203
+ return undefined;
204
+ if (this.#page?.targetId !== page.targetId) {
205
+ const { sessionId } = await this.#devtools.send("Target.attachToTarget", {
206
+ targetId: page.targetId,
207
+ flatten: true,
208
+ });
209
+ this.#page = { targetId: page.targetId, sessionId };
210
+ }
211
+ const sessionId = this.#page?.sessionId;
212
+ if (typeof sessionId !== "string")
213
+ return undefined;
214
+ const result = await this.#devtools.send("Runtime.evaluate", { expression: `localStorage.getItem(${JSON.stringify(AUTH_KEY)})`, returnByValue: true }, sessionId);
215
+ const stored = result?.result?.value;
216
+ return typeof stored === "string" ? tokenIn(stored) : undefined;
217
+ }
218
+ }
219
+ /** The DevTools protocol over Chrome's pipe: JSON messages, each ended by a NUL byte. */
220
+ class DevToolsPipe {
221
+ #input;
222
+ #waiting = new Map();
223
+ #next = 0;
224
+ #buffer = "";
225
+ constructor(child) {
226
+ this.#input = child.stdio[3];
227
+ const output = child.stdio[4];
228
+ // A pipe that breaks with no listener kills the process (HANDOFF bite 16).
229
+ this.#input?.on("error", () => { });
230
+ output?.on("error", () => { });
231
+ output?.setEncoding("utf8");
232
+ output?.on("data", (chunk) => {
233
+ this.#buffer += chunk;
234
+ for (let end = this.#buffer.indexOf("\0"); end >= 0; end = this.#buffer.indexOf("\0")) {
235
+ this.#receive(this.#buffer.slice(0, end));
236
+ this.#buffer = this.#buffer.slice(end + 1);
237
+ }
238
+ });
239
+ child.once("exit", () => this.#failAll(new Error("the browser exited")));
240
+ }
241
+ // biome-ignore lint/suspicious/noExplicitAny: see above.
242
+ send(method, params = {}, sessionId) {
243
+ const input = this.#input;
244
+ if (!input || input.destroyed)
245
+ return Promise.reject(new Error("the browser has no DevTools pipe"));
246
+ const id = ++this.#next;
247
+ return new Promise((resolve, reject) => {
248
+ const timer = setTimeout(() => {
249
+ this.#waiting.delete(id);
250
+ reject(new Error("the browser did not answer"));
251
+ }, 5_000);
252
+ this.#waiting.set(id, { resolve, reject, timer });
253
+ input.write(`${JSON.stringify({ id, method, params, ...(sessionId ? { sessionId } : {}) })}\0`);
254
+ });
255
+ }
256
+ #receive(raw) {
257
+ let message;
258
+ try {
259
+ message = JSON.parse(raw);
260
+ }
261
+ catch {
262
+ return;
263
+ }
264
+ const waiting = typeof message.id === "number" ? this.#waiting.get(message.id) : undefined;
265
+ if (!waiting)
266
+ return;
267
+ clearTimeout(waiting.timer);
268
+ this.#waiting.delete(message.id);
269
+ if (message.error)
270
+ waiting.reject(new Error(String(message.error.message ?? "DevTools refused")));
271
+ else
272
+ waiting.resolve(message.result ?? {});
273
+ }
274
+ #failAll(error) {
275
+ for (const { reject, timer } of this.#waiting.values()) {
276
+ clearTimeout(timer);
277
+ reject(error);
278
+ }
279
+ this.#waiting.clear();
280
+ }
281
+ }
282
+ /**
283
+ * The stored value is JSON holding the token, or the token itself — which one is not measured yet.
284
+ * Either way nothing here prints it.
285
+ */
286
+ export const tokenIn = (stored) => {
287
+ try {
288
+ const parsed = JSON.parse(stored);
289
+ if (typeof parsed === "object" && parsed !== null && "token" in parsed) {
290
+ const token = parsed.token;
291
+ return typeof token === "string" && token !== "" ? token : undefined;
292
+ }
293
+ return typeof parsed === "string" && parsed !== "" ? parsed : undefined;
294
+ }
295
+ catch {
296
+ return stored === "" ? undefined : stored;
297
+ }
298
+ };
299
+ /**
300
+ * Node's default Ctrl-C exits without running a `finally`, which would leave the browser open and
301
+ * its logged-in profile on disk. Returns the function that removes the handlers again.
302
+ */
303
+ const closeOnSignal = (closeable) => {
304
+ const onSignal = () => {
305
+ void closeable.close().finally(() => process.exit(130));
306
+ };
307
+ // `on`, not `once`: a second Ctrl-C during the shutdown would otherwise exit before the profile goes.
308
+ process.on("SIGINT", onSignal);
309
+ process.on("SIGTERM", onSignal);
310
+ return () => {
311
+ process.off("SIGINT", onSignal);
312
+ process.off("SIGTERM", onSignal);
313
+ };
314
+ };
315
+ const refuseInTests = () => {
316
+ if (process.env.MAX_TEST_SANDBOX)
317
+ throw new Error("a test reached the real browser — inject BrowserDoors");
318
+ };
319
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/session/browser.ts"],"names":[],"mappings":"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"}
@@ -35,5 +35,7 @@ export interface SessionOptions {
35
35
  * diagnostic hook in `MaxClient` saw nothing of the two requests every single invocation makes,
36
36
  * and `max chats list` answers from the LOGIN response without sending anything else.
37
37
  */
38
+ /** How many chats LOGIN is asked for. MAX answers the newest that many, newest first (`FIND-80`). */
39
+ export declare const LOGIN_CHATS = 40;
38
40
  export declare const startSession: (invoke: Invoke, { token, deviceId, chatsCount, sync }: SessionOptions) => Promise<Payload>;
39
41
  //# sourceMappingURL=handshake.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handshake.d.ts","sourceRoot":"","sources":["../../src/session/handshake.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAA;AAC9D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAInD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,6FAA6F;IAC7F,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,YAAY,GACvB,QAAQ,MAAM,EACd,uCAAgD,cAAc,KAC7D,OAAO,CAAC,OAAO,CAajB,CAAA"}
1
+ {"version":3,"file":"handshake.d.ts","sourceRoot":"","sources":["../../src/session/handshake.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAA;AAC9D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAInD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,6FAA6F;IAC7F,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;;;;;;;;GAeG;AACH,qGAAqG;AACrG,eAAO,MAAM,WAAW,KAAK,CAAA;AAE7B,eAAO,MAAM,YAAY,GACvB,QAAQ,MAAM,EACd,uCAAyD,cAAc,KACtE,OAAO,CAAC,OAAO,CAajB,CAAA"}
@@ -16,7 +16,9 @@ import { sessionInit, sessionLogin } from "../spec/operations/session.js";
16
16
  * diagnostic hook in `MaxClient` saw nothing of the two requests every single invocation makes,
17
17
  * and `max chats list` answers from the LOGIN response without sending anything else.
18
18
  */
19
- export const startSession = async (invoke, { token, deviceId, chatsCount = 40, sync = 0 }) => {
19
+ /** How many chats LOGIN is asked for. MAX answers the newest that many, newest first (`FIND-80`). */
20
+ export const LOGIN_CHATS = 40;
21
+ export const startSession = async (invoke, { token, deviceId, chatsCount = LOGIN_CHATS, sync = 0 }) => {
20
22
  await invoke(sessionInit, { userAgent: WEB_USER_AGENT, deviceId });
21
23
  return await invoke(sessionLogin, {
22
24
  token,
@@ -1 +1 @@
1
- {"version":3,"file":"handshake.js","sourceRoot":"","sources":["../../src/session/handshake.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAA;AAsBzE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAC/B,MAAc,EACd,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,GAAG,EAAE,EAAE,IAAI,GAAG,CAAC,EAAkB,EAC5C,EAAE;IACpB,MAAM,MAAM,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,CAAA;IAElE,OAAO,MAAM,MAAM,CAAC,YAAY,EAAE;QAChC,KAAK;QACL,8FAA8F;QAC9F,WAAW,EAAE,KAAK;QAClB,UAAU;QACV,SAAS,EAAE,IAAI;QACf,YAAY,EAAE,IAAI;QAClB,YAAY,EAAE,IAAI;QAClB,UAAU,EAAE,IAAI;KACjB,CAAC,CAAA;AACJ,CAAC,CAAA"}
1
+ {"version":3,"file":"handshake.js","sourceRoot":"","sources":["../../src/session/handshake.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAA;AAsBzE;;;;;;;;;;;;;;;GAeG;AACH,qGAAqG;AACrG,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAA;AAE7B,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAC/B,MAAc,EACd,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,GAAG,WAAW,EAAE,IAAI,GAAG,CAAC,EAAkB,EACrD,EAAE;IACpB,MAAM,MAAM,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,CAAA;IAElE,OAAO,MAAM,MAAM,CAAC,YAAY,EAAE;QAChC,KAAK;QACL,8FAA8F;QAC9F,WAAW,EAAE,KAAK;QAClB,UAAU;QACV,SAAS,EAAE,IAAI;QACf,YAAY,EAAE,IAAI;QAClB,YAAY,EAAE,IAAI;QAClB,UAAU,EAAE,IAAI;KACjB,CAAC,CAAA;AACJ,CAAC,CAAA"}
@@ -0,0 +1,20 @@
1
+ import type { WireClient } from "../generated/client.generated.js";
2
+ export interface SecondFactor {
3
+ /** Asked only when the account has a cloud password; `again` after a wrong one. */
4
+ askPassword: (hint: string | undefined, again: boolean) => Promise<string>;
5
+ }
6
+ export interface QrLogin extends SecondFactor {
7
+ /** Put the code where a phone can scan it. Resolves once it is shown, not once it is scanned. */
8
+ show: (link: string) => Promise<void>;
9
+ now?: () => number;
10
+ sleep?: (ms: number) => Promise<void>;
11
+ }
12
+ /**
13
+ * The login MAX's web client does itself when it shows a QR code: ask for a code, wait until a phone
14
+ * approves it, then exchange it for a token. Shapes from PyMax 2.4.1 (`auth/qr.py`), not measured.
15
+ *
16
+ * One code per run: an expired one is an error rather than a fresh code, so a person who walked
17
+ * away does not come back to a loop that has been asking MAX for codes all along.
18
+ */
19
+ export declare const tokenByQr: (wire: WireClient, { show, askPassword, now, sleep }: QrLogin) => Promise<string>;
20
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/session/login.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAA;AAGlE,MAAM,WAAW,YAAY;IAC3B,mFAAmF;IACnF,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CAC3E;AAED,MAAM,WAAW,OAAQ,SAAQ,YAAY;IAC3C,iGAAiG;IACjG,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACtC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,GACpB,MAAM,UAAU,EAChB,mCAAsD,OAAO,KAC5D,OAAO,CAAC,MAAM,CAiBhB,CAAA"}
@@ -0,0 +1,66 @@
1
+ import { setTimeout as delay } from "node:timers/promises";
2
+ import { CliError } from "@leemour/cli-core";
3
+ /**
4
+ * The login MAX's web client does itself when it shows a QR code: ask for a code, wait until a phone
5
+ * approves it, then exchange it for a token. Shapes from PyMax 2.4.1 (`auth/qr.py`), not measured.
6
+ *
7
+ * One code per run: an expired one is an error rather than a fresh code, so a person who walked
8
+ * away does not come back to a loop that has been asking MAX for codes all along.
9
+ */
10
+ export const tokenByQr = async (wire, { show, askPassword, now = Date.now, sleep = delay }) => {
11
+ const code = await wire.login.qrRequest({});
12
+ const trackId = text(code.trackId, "trackId");
13
+ const expiresAt = typeof code.expiresAt === "number" ? code.expiresAt : now() + 120_000;
14
+ const interval = typeof code.pollingInterval === "number" ? code.pollingInterval : 5_000;
15
+ await show(text(code.qrLink, "qrLink"));
16
+ while (now() < expiresAt) {
17
+ const answer = await wire.login.qrStatus({ trackId });
18
+ if (record(answer.status)?.loginAvailable === true) {
19
+ return await tokenFrom(wire, await wire.login.byQr({ trackId }), askPassword);
20
+ }
21
+ await sleep(interval);
22
+ }
23
+ throw new CliError("timeout", "the QR code expired before it was scanned — run the command again");
24
+ };
25
+ /**
26
+ * A few attempts, not PyMax's endless loop, and not one: a mistyped password would otherwise cost a
27
+ * fresh QR code. Only a person at a terminal gets here, so asking again cannot hang a script.
28
+ */
29
+ const tokenFrom = async (wire, answer, askPassword) => {
30
+ const issued = loginToken(answer);
31
+ if (issued)
32
+ return issued;
33
+ const challenge = record(answer.passwordChallenge);
34
+ if (!challenge)
35
+ throw new CliError("authentication_error", "MAX confirmed the login but sent no token");
36
+ const trackId = text(challenge.trackId, "trackId");
37
+ const hint = typeof challenge.hint === "string" && challenge.hint !== "" ? challenge.hint : undefined;
38
+ for (let attempt = 1; attempt <= PASSWORD_ATTEMPTS; attempt++) {
39
+ const password = await askPassword(hint, attempt > 1);
40
+ if (!password)
41
+ throw new CliError("validation_error", "no password given");
42
+ const checked = await wire.login.password({ trackId, password }).catch((error) => {
43
+ if (wrongPassword(error))
44
+ return undefined;
45
+ throw error;
46
+ });
47
+ const token = checked && loginToken(checked);
48
+ if (token)
49
+ return token;
50
+ }
51
+ throw new CliError("authentication_error", `the password was wrong ${PASSWORD_ATTEMPTS} times — run the command again`);
52
+ };
53
+ const PASSWORD_ATTEMPTS = 3;
54
+ // Measured 2026-09-24: a wrong cloud password is refused as `password2fa.wrong`.
55
+ const wrongPassword = (error) => error instanceof CliError && /password2fa\.wrong/.test(error.message);
56
+ const loginToken = (answer) => {
57
+ const token = record(record(answer.tokenAttrs)?.LOGIN)?.token;
58
+ return typeof token === "string" && token !== "" ? token : undefined;
59
+ };
60
+ const text = (value, field) => {
61
+ if (typeof value === "string" && value !== "")
62
+ return value;
63
+ throw new CliError("provider_error", `MAX's answer had no ${field}`);
64
+ };
65
+ const record = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
66
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/session/login.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,8 @@ export interface SecretInput {
3
3
  isTTY?: boolean;
4
4
  };
5
5
  output?: NodeJS.WritableStream;
6
+ /** For a phone number or an SMS code, which a person needs to see while typing. */
7
+ echo?: boolean;
6
8
  }
7
9
  /**
8
10
  * One line of secret, taken without a file and without argv.
@@ -14,5 +16,5 @@ export interface SecretInput {
14
16
  * The prompt goes to **stderr**, not stdout, so that `max session start --json` still writes one JSON
15
17
  * value and nothing else.
16
18
  */
17
- export declare const readSecret: (prompt: string, { input, output }?: SecretInput) => Promise<string>;
19
+ export declare const readSecret: (prompt: string, { input, output, echo }?: SecretInput) => Promise<string>;
18
20
  //# sourceMappingURL=prompt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/session/prompt.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,GAAG;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAA;CAC/B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,GACrB,QAAQ,MAAM,EACd,oBAAoD,WAAgB,KACnE,OAAO,CAAC,MAAM,CA0BhB,CAAA"}
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/session/prompt.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,GAAG;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAA;IAC9B,mFAAmF;IACnF,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,GACrB,QAAQ,MAAM,EACd,0BAAkE,WAAgB,KACjF,OAAO,CAAC,MAAM,CA0BhB,CAAA"}
@@ -10,7 +10,7 @@ import { Writable } from "node:stream";
10
10
  * The prompt goes to **stderr**, not stdout, so that `max session start --json` still writes one JSON
11
11
  * value and nothing else.
12
12
  */
13
- export const readSecret = async (prompt, { input = process.stdin, output = process.stderr } = {}) => {
13
+ export const readSecret = async (prompt, { input = process.stdin, output = process.stderr, echo = false } = {}) => {
14
14
  if (!input.isTTY) {
15
15
  const chunks = [];
16
16
  for await (const chunk of input)
@@ -29,7 +29,7 @@ export const readSecret = async (prompt, { input = process.stdin, output = proce
29
29
  try {
30
30
  const answer = await new Promise((resolve) => {
31
31
  reader.question(prompt, resolve);
32
- muted = true;
32
+ muted = !echo;
33
33
  });
34
34
  return answer.trim();
35
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/session/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAOtC;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAC7B,MAAc,EACd,EAAE,KAAK,GAAG,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,KAAkB,EAAE,EACnD,EAAE;IACnB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,MAAM,GAAa,EAAE,CAAA;QAC3B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,KAAK;YAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;QAChE,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;IACtD,CAAC;IAED,IAAI,KAAK,GAAG,KAAK,CAAA;IACjB,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC;QACxB,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI;YAC1B,IAAI,CAAC,KAAK;gBAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;YAC/B,IAAI,EAAE,CAAA;QACR,CAAC;KACF,CAAC,CAAA;IAEF,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IACvE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;YACnD,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;YAChC,KAAK,GAAG,IAAI,CAAA;QACd,CAAC,CAAC,CAAA;QACF,OAAO,MAAM,CAAC,IAAI,EAAE,CAAA;IACtB,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,KAAK,EAAE,CAAA;QACd,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACpB,CAAC;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/session/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAStC;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAC7B,MAAc,EACd,EAAE,KAAK,GAAG,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,IAAI,GAAG,KAAK,KAAkB,EAAE,EACjE,EAAE;IACnB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,MAAM,GAAa,EAAE,CAAA;QAC3B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,KAAK;YAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;QAChE,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;IACtD,CAAC;IAED,IAAI,KAAK,GAAG,KAAK,CAAA;IACjB,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC;QACxB,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI;YAC1B,IAAI,CAAC,KAAK;gBAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;YAC/B,IAAI,EAAE,CAAA;QACR,CAAC;KACF,CAAC,CAAA;IAEF,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IACvE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;YACnD,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;YAChC,KAAK,GAAG,CAAC,IAAI,CAAA;QACf,CAAC,CAAC,CAAA;QACF,OAAO,MAAM,CAAC,IAAI,EAAE,CAAA;IACtB,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,KAAK,EAAE,CAAA;QACd,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACpB,CAAC;AACH,CAAC,CAAA"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A QR code as text, two modules per character cell with half blocks, so it fits a terminal
3
+ * without being twice as tall as it is wide. `width` is in columns, for deciding whether it fits.
4
+ */
5
+ export declare const terminalQr: (link: string) => {
6
+ text: string;
7
+ width: number;
8
+ };
9
+ //# sourceMappingURL=qr-terminal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr-terminal.d.ts","sourceRoot":"","sources":["../../src/session/qr-terminal.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAwBtE,CAAA"}
@@ -0,0 +1,34 @@
1
+ import qrcode from "qrcode-generator";
2
+ const QUIET_ZONE = 4;
3
+ // Dark modules on a light ground whatever the terminal's theme: a phone camera reads an inverted
4
+ // code unreliably, so the colours are forced even under NO_COLOR.
5
+ const BLACK_ON_WHITE = "\x1b[30;107m";
6
+ const RESET = "\x1b[0m";
7
+ /**
8
+ * A QR code as text, two modules per character cell with half blocks, so it fits a terminal
9
+ * without being twice as tall as it is wide. `width` is in columns, for deciding whether it fits.
10
+ */
11
+ export const terminalQr = (link) => {
12
+ const code = qrcode(0, "M");
13
+ code.addData(link);
14
+ code.make();
15
+ const count = code.getModuleCount();
16
+ const size = count + QUIET_ZONE * 2;
17
+ const dark = (row, col) => {
18
+ const r = row - QUIET_ZONE;
19
+ const c = col - QUIET_ZONE;
20
+ return r >= 0 && c >= 0 && r < count && c < count && code.isDark(r, c);
21
+ };
22
+ const lines = [];
23
+ for (let row = 0; row < size; row += 2) {
24
+ let line = "";
25
+ for (let col = 0; col < size; col++) {
26
+ const top = dark(row, col);
27
+ const bottom = row + 1 < size && dark(row + 1, col);
28
+ line += top && bottom ? "█" : top ? "▀" : bottom ? "▄" : " ";
29
+ }
30
+ lines.push(`${BLACK_ON_WHITE}${line}${RESET}`);
31
+ }
32
+ return { text: lines.join("\n"), width: size };
33
+ };
34
+ //# sourceMappingURL=qr-terminal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr-terminal.js","sourceRoot":"","sources":["../../src/session/qr-terminal.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,kBAAkB,CAAA;AAErC,MAAM,UAAU,GAAG,CAAC,CAAA;AACpB,iGAAiG;AACjG,kEAAkE;AAClE,MAAM,cAAc,GAAG,cAAc,CAAA;AACrC,MAAM,KAAK,GAAG,SAAS,CAAA;AAEvB;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAY,EAAmC,EAAE;IAC1E,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IAC3B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAClB,IAAI,CAAC,IAAI,EAAE,CAAA;IAEX,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,EAAE,CAAA;IACnC,MAAM,IAAI,GAAG,KAAK,GAAG,UAAU,GAAG,CAAC,CAAA;IACnC,MAAM,IAAI,GAAG,CAAC,GAAW,EAAE,GAAW,EAAE,EAAE;QACxC,MAAM,CAAC,GAAG,GAAG,GAAG,UAAU,CAAA;QAC1B,MAAM,CAAC,GAAG,GAAG,GAAG,UAAU,CAAA;QAC1B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACxE,CAAC,CAAA;IAED,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QACvC,IAAI,IAAI,GAAG,EAAE,CAAA;QACb,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;YACpC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;YAC1B,MAAM,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,GAAG,CAAC,CAAA;YACnD,IAAI,IAAI,GAAG,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;QAC9D,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,GAAG,cAAc,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC,CAAA;IAChD,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;AAChD,CAAC,CAAA"}
@@ -14,6 +14,8 @@ export interface SessionState {
14
14
  /** Counts logins so a token that stops working can be explained rather than guessed at (NEED-8). */
15
15
  logins: number;
16
16
  lastLoginAt?: string;
17
+ /** Where `max inbox` starts next time: the newest message it has read (`NEED-162`). */
18
+ lastCheckAt?: string;
17
19
  }
18
20
  export interface SessionStoreOptions {
19
21
  profile?: string;
@@ -42,5 +44,7 @@ export declare class SessionStore {
42
44
  writeState(state: SessionState): void;
43
45
  /** Forgets everything about this profile. Returns whether there was a token to forget. */
44
46
  forget(): boolean;
47
+ /** Where `max serve` listens for this profile. Beside the state file, so a sandbox moves both. */
48
+ socketPath(): string;
45
49
  }
46
50
  //# sourceMappingURL=store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/session/store.ts"],"names":[],"mappings":"AAGA,OAAO,EAAe,KAAK,YAAY,EAAmD,MAAM,mBAAmB,CAAA;AAEnH;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oGAAoG;IACpG,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAID,qBAAa,YAAY;;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;gBAIZ,EAAE,OAAmB,EAAE,GAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAE,mBAAwB;IAe9G,8DAA8D;IAC9D,SAAS,IAAI,MAAM,GAAG,SAAS;IAI/B,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI/B;;;;;;;OAOG;IACH,SAAS,IAAI,YAAY;IAezB,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAIrC,0FAA0F;IAC1F,MAAM,IAAI,OAAO;CAclB"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/session/store.ts"],"names":[],"mappings":"AAGA,OAAO,EAAe,KAAK,YAAY,EAAmD,MAAM,mBAAmB,CAAA;AAEnH;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oGAAoG;IACpG,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uFAAuF;IACvF,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAID,qBAAa,YAAY;;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;gBAIZ,EAAE,OAAmB,EAAE,GAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAE,mBAAwB;IAe9G,8DAA8D;IAC9D,SAAS,IAAI,MAAM,GAAG,SAAS;IAI/B,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI/B;;;;;;;OAOG;IACH,SAAS,IAAI,YAAY;IAezB,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAIrC,0FAA0F;IAC1F,MAAM,IAAI,OAAO;IAWjB,kGAAkG;IAClG,UAAU,IAAI,MAAM;CAOrB"}
@@ -65,6 +65,10 @@ export class SessionStore {
65
65
  }
66
66
  return had;
67
67
  }
68
+ /** Where `max serve` listens for this profile. Beside the state file, so a sandbox moves both. */
69
+ socketPath() {
70
+ return join(this.#stateDir, "profiles", `${this.profile}.sock`);
71
+ }
68
72
  #statePath() {
69
73
  return join(this.#stateDir, "profiles", `${this.profile}.json`);
70
74
  }
@@ -75,6 +79,8 @@ const pick = (state) => {
75
79
  extra.viewerId = state.viewerId;
76
80
  if (typeof state.lastLoginAt === "string")
77
81
  extra.lastLoginAt = state.lastLoginAt;
82
+ if (typeof state.lastCheckAt === "string")
83
+ extra.lastCheckAt = state.lastCheckAt;
78
84
  return extra;
79
85
  };
80
86
  //# sourceMappingURL=store.js.map