@leemour/max-cli 0.1.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 (183) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +176 -0
  3. package/dist/bin/max.d.ts +3 -0
  4. package/dist/bin/max.d.ts.map +1 -0
  5. package/dist/bin/max.js +14 -0
  6. package/dist/bin/max.js.map +1 -0
  7. package/dist/cache/driver.d.ts +38 -0
  8. package/dist/cache/driver.d.ts.map +1 -0
  9. package/dist/cache/driver.js +26 -0
  10. package/dist/cache/driver.js.map +1 -0
  11. package/dist/cache/drivers/bun-sqlite.d.ts +8 -0
  12. package/dist/cache/drivers/bun-sqlite.d.ts.map +1 -0
  13. package/dist/cache/drivers/bun-sqlite.js +23 -0
  14. package/dist/cache/drivers/bun-sqlite.js.map +1 -0
  15. package/dist/cache/drivers/node-sqlite.d.ts +8 -0
  16. package/dist/cache/drivers/node-sqlite.d.ts.map +1 -0
  17. package/dist/cache/drivers/node-sqlite.js +22 -0
  18. package/dist/cache/drivers/node-sqlite.js.map +1 -0
  19. package/dist/cache/index.d.ts +22 -0
  20. package/dist/cache/index.d.ts.map +1 -0
  21. package/dist/cache/index.js +33 -0
  22. package/dist/cache/index.js.map +1 -0
  23. package/dist/cache/open.d.ts +14 -0
  24. package/dist/cache/open.d.ts.map +1 -0
  25. package/dist/cache/open.js +26 -0
  26. package/dist/cache/open.js.map +1 -0
  27. package/dist/cache/schema.d.ts +14 -0
  28. package/dist/cache/schema.d.ts.map +1 -0
  29. package/dist/cache/schema.js +95 -0
  30. package/dist/cache/schema.js.map +1 -0
  31. package/dist/cache/store.d.ts +32 -0
  32. package/dist/cache/store.d.ts.map +1 -0
  33. package/dist/cache/store.js +153 -0
  34. package/dist/cache/store.js.map +1 -0
  35. package/dist/client.d.ts +103 -0
  36. package/dist/client.d.ts.map +1 -0
  37. package/dist/client.js +383 -0
  38. package/dist/client.js.map +1 -0
  39. package/dist/commands/account.d.ts +3 -0
  40. package/dist/commands/account.d.ts.map +1 -0
  41. package/dist/commands/account.js +24 -0
  42. package/dist/commands/account.js.map +1 -0
  43. package/dist/commands/cache.d.ts +10 -0
  44. package/dist/commands/cache.d.ts.map +1 -0
  45. package/dist/commands/cache.js +36 -0
  46. package/dist/commands/cache.js.map +1 -0
  47. package/dist/commands/chats.d.ts +3 -0
  48. package/dist/commands/chats.d.ts.map +1 -0
  49. package/dist/commands/chats.js +28 -0
  50. package/dist/commands/chats.js.map +1 -0
  51. package/dist/commands/contacts.d.ts +10 -0
  52. package/dist/commands/contacts.d.ts.map +1 -0
  53. package/dist/commands/contacts.js +33 -0
  54. package/dist/commands/contacts.js.map +1 -0
  55. package/dist/commands/context.d.ts +34 -0
  56. package/dist/commands/context.d.ts.map +1 -0
  57. package/dist/commands/context.js +40 -0
  58. package/dist/commands/context.js.map +1 -0
  59. package/dist/commands/messages.d.ts +3 -0
  60. package/dist/commands/messages.d.ts.map +1 -0
  61. package/dist/commands/messages.js +62 -0
  62. package/dist/commands/messages.js.map +1 -0
  63. package/dist/commands/runs.d.ts +9 -0
  64. package/dist/commands/runs.d.ts.map +1 -0
  65. package/dist/commands/runs.js +68 -0
  66. package/dist/commands/runs.js.map +1 -0
  67. package/dist/commands/session.d.ts +3 -0
  68. package/dist/commands/session.d.ts.map +1 -0
  69. package/dist/commands/session.js +74 -0
  70. package/dist/commands/session.js.map +1 -0
  71. package/dist/config.d.ts +53 -0
  72. package/dist/config.d.ts.map +1 -0
  73. package/dist/config.js +83 -0
  74. package/dist/config.js.map +1 -0
  75. package/dist/domain/map.d.ts +22 -0
  76. package/dist/domain/map.d.ts.map +1 -0
  77. package/dist/domain/map.js +107 -0
  78. package/dist/domain/map.js.map +1 -0
  79. package/dist/domain/models.d.ts +57 -0
  80. package/dist/domain/models.d.ts.map +1 -0
  81. package/dist/domain/models.js +7 -0
  82. package/dist/domain/models.js.map +1 -0
  83. package/dist/generated/client.generated.d.ts +30 -0
  84. package/dist/generated/client.generated.d.ts.map +1 -0
  85. package/dist/generated/client.generated.js +26 -0
  86. package/dist/generated/client.generated.js.map +1 -0
  87. package/dist/generated/opcodes.generated.d.ts +44 -0
  88. package/dist/generated/opcodes.generated.d.ts.map +1 -0
  89. package/dist/generated/opcodes.generated.js +44 -0
  90. package/dist/generated/opcodes.generated.js.map +1 -0
  91. package/dist/generated/operations.generated.d.ts +76 -0
  92. package/dist/generated/operations.generated.d.ts.map +1 -0
  93. package/dist/generated/operations.generated.js +15 -0
  94. package/dist/generated/operations.generated.js.map +1 -0
  95. package/dist/output.d.ts +22 -0
  96. package/dist/output.d.ts.map +1 -0
  97. package/dist/output.js +34 -0
  98. package/dist/output.js.map +1 -0
  99. package/dist/profile.d.ts +33 -0
  100. package/dist/profile.d.ts.map +1 -0
  101. package/dist/profile.js +60 -0
  102. package/dist/profile.js.map +1 -0
  103. package/dist/program.d.ts +29 -0
  104. package/dist/program.d.ts.map +1 -0
  105. package/dist/program.js +130 -0
  106. package/dist/program.js.map +1 -0
  107. package/dist/protocol/connection.d.ts +60 -0
  108. package/dist/protocol/connection.d.ts.map +1 -0
  109. package/dist/protocol/connection.js +142 -0
  110. package/dist/protocol/connection.js.map +1 -0
  111. package/dist/protocol/frame.d.ts +44 -0
  112. package/dist/protocol/frame.d.ts.map +1 -0
  113. package/dist/protocol/frame.js +68 -0
  114. package/dist/protocol/frame.js.map +1 -0
  115. package/dist/runs/events.d.ts +92 -0
  116. package/dist/runs/events.d.ts.map +1 -0
  117. package/dist/runs/events.js +85 -0
  118. package/dist/runs/events.js.map +1 -0
  119. package/dist/runs/recording.d.ts +35 -0
  120. package/dist/runs/recording.d.ts.map +1 -0
  121. package/dist/runs/recording.js +53 -0
  122. package/dist/runs/recording.js.map +1 -0
  123. package/dist/runs/run.d.ts +81 -0
  124. package/dist/runs/run.d.ts.map +1 -0
  125. package/dist/runs/run.js +175 -0
  126. package/dist/runs/run.js.map +1 -0
  127. package/dist/session/handshake.d.ts +26 -0
  128. package/dist/session/handshake.d.ts.map +1 -0
  129. package/dist/session/handshake.js +32 -0
  130. package/dist/session/handshake.js.map +1 -0
  131. package/dist/session/prompt.d.ts +18 -0
  132. package/dist/session/prompt.d.ts.map +1 -0
  133. package/dist/session/prompt.js +41 -0
  134. package/dist/session/prompt.js.map +1 -0
  135. package/dist/session/store.d.ts +46 -0
  136. package/dist/session/store.d.ts.map +1 -0
  137. package/dist/session/store.js +80 -0
  138. package/dist/session/store.js.map +1 -0
  139. package/dist/spec/check.d.ts +16 -0
  140. package/dist/spec/check.d.ts.map +1 -0
  141. package/dist/spec/check.js +44 -0
  142. package/dist/spec/check.js.map +1 -0
  143. package/dist/spec/define.d.ts +95 -0
  144. package/dist/spec/define.d.ts.map +1 -0
  145. package/dist/spec/define.js +34 -0
  146. package/dist/spec/define.js.map +1 -0
  147. package/dist/spec/identity.d.ts +23 -0
  148. package/dist/spec/identity.d.ts.map +1 -0
  149. package/dist/spec/identity.js +23 -0
  150. package/dist/spec/identity.js.map +1 -0
  151. package/dist/spec/index.d.ts +15 -0
  152. package/dist/spec/index.d.ts.map +1 -0
  153. package/dist/spec/index.js +30 -0
  154. package/dist/spec/index.js.map +1 -0
  155. package/dist/spec/operations/chats.d.ts +25 -0
  156. package/dist/spec/operations/chats.d.ts.map +1 -0
  157. package/dist/spec/operations/chats.js +49 -0
  158. package/dist/spec/operations/chats.js.map +1 -0
  159. package/dist/spec/operations/contacts.d.ts +9 -0
  160. package/dist/spec/operations/contacts.d.ts.map +1 -0
  161. package/dist/spec/operations/contacts.js +39 -0
  162. package/dist/spec/operations/contacts.js.map +1 -0
  163. package/dist/spec/operations/messages.d.ts +19 -0
  164. package/dist/spec/operations/messages.d.ts.map +1 -0
  165. package/dist/spec/operations/messages.js +37 -0
  166. package/dist/spec/operations/messages.js.map +1 -0
  167. package/dist/spec/operations/session.d.ts +69 -0
  168. package/dist/spec/operations/session.d.ts.map +1 -0
  169. package/dist/spec/operations/session.js +106 -0
  170. package/dist/spec/operations/session.js.map +1 -0
  171. package/dist/spec/scalars.d.ts +16 -0
  172. package/dist/spec/scalars.d.ts.map +1 -0
  173. package/dist/spec/scalars.js +28 -0
  174. package/dist/spec/scalars.js.map +1 -0
  175. package/dist/testing/mock-max.d.ts +29 -0
  176. package/dist/testing/mock-max.d.ts.map +1 -0
  177. package/dist/testing/mock-max.js +46 -0
  178. package/dist/testing/mock-max.js.map +1 -0
  179. package/dist/version.d.ts +3 -0
  180. package/dist/version.d.ts.map +1 -0
  181. package/dist/version.js +3 -0
  182. package/dist/version.js.map +1 -0
  183. package/package.json +58 -0
@@ -0,0 +1,32 @@
1
+ import type { Chat, Contact, Id, Message } from "../domain/models.js";
2
+ import type { CacheDatabase } from "./driver.js";
3
+ export interface CacheOptions {
4
+ database: CacheDatabase;
5
+ /** Injected so a freshness test costs nothing and does not wait. */
6
+ now?: () => number;
7
+ }
8
+ /** Nothing here ever throws on missing data: absent is a cache miss, not an error. */
9
+ export interface CacheStore {
10
+ chats: {
11
+ read(freshForMs: number): Chat[] | undefined;
12
+ write(chats: Chat[]): void;
13
+ };
14
+ contacts: {
15
+ read(freshForMs: number): Contact[] | undefined;
16
+ write(contacts: Contact[]): void;
17
+ };
18
+ messages: {
19
+ /** The newest `limit` messages, but only if the window we hold is both fresh and contiguous. */
20
+ read(chatId: Id, limit: number, freshForMs: number): Message[] | undefined;
21
+ write(chatId: Id, messages: Message[]): void;
22
+ /** After a send, what we hold for that chat is missing the message we just added. */
23
+ invalidate(chatId: Id): void;
24
+ };
25
+ /** True when this process may go to MAX for that window; false when somebody else already is. */
26
+ claim(chatId: Id, anchor: string, holder: string, forMs: number): boolean;
27
+ release(chatId: Id, anchor: string): void;
28
+ clear(): void;
29
+ close(): void;
30
+ }
31
+ export declare const openStore: ({ database, now }: CacheOptions) => CacheStore;
32
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/cache/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAGhD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,aAAa,CAAA;IACvB,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,sFAAsF;AACtF,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE;QACL,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,SAAS,CAAA;QAC5C,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAAA;KAC3B,CAAA;IACD,QAAQ,EAAE;QACR,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,EAAE,GAAG,SAAS,CAAA;QAC/C,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;KACjC,CAAA;IACD,QAAQ,EAAE;QACR,gGAAgG;QAChG,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,EAAE,GAAG,SAAS,CAAA;QAC1E,KAAK,CAAC,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;QAC5C,qFAAqF;QACrF,UAAU,CAAC,MAAM,EAAE,EAAE,GAAG,IAAI,CAAA;KAC7B,CAAA;IACD,iGAAiG;IACjG,KAAK,CAAC,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;IACzE,OAAO,CAAC,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACzC,KAAK,IAAI,IAAI,CAAA;IACb,KAAK,IAAI,IAAI,CAAA;CACd;AAED,eAAO,MAAM,SAAS,GAAI,mBAAsC,YAAY,KAAG,UA8L9E,CAAA"}
@@ -0,0 +1,153 @@
1
+ import { migrate } from "./schema.js";
2
+ export const openStore = ({ database, now = () => Date.now() }) => {
3
+ migrate(database);
4
+ const markFetched = database.prepare("INSERT INTO fetched (kind, at) VALUES (?, ?) ON CONFLICT(kind) DO UPDATE SET at = excluded.at");
5
+ const fetchedAt = database.prepare("SELECT at FROM fetched WHERE kind = ?");
6
+ const isFresh = (kind, freshForMs) => {
7
+ const at = fetchedAt.get(kind)?.at;
8
+ return at !== undefined && now() - at <= freshForMs;
9
+ };
10
+ const putChat = database.prepare(`
11
+ INSERT INTO chats (id, title, kind, unread_count, last_message_at, participants_count, fetched_at)
12
+ VALUES (?, ?, ?, ?, ?, ?, ?)
13
+ ON CONFLICT(id) DO UPDATE SET
14
+ title = excluded.title, kind = excluded.kind, unread_count = excluded.unread_count,
15
+ last_message_at = excluded.last_message_at, participants_count = excluded.participants_count,
16
+ fetched_at = excluded.fetched_at, generation = chats.generation + 1`);
17
+ const putContact = database.prepare(`
18
+ INSERT INTO contacts (id, name, username, description, fetched_at) VALUES (?, ?, ?, ?, ?)
19
+ ON CONFLICT(id) DO UPDATE SET
20
+ name = excluded.name, username = excluded.username,
21
+ description = excluded.description, fetched_at = excluded.fetched_at`);
22
+ /**
23
+ * **Compares MAX's clock, never ours.** `update_time` is what MAX sets on an edit and is `NULL`
24
+ * on a message nobody has changed (measured 2026-09-20).
25
+ *
26
+ * The comparison has to be on that value rather than on when we fetched, and the difference is
27
+ * not academic: agent A reads a chat after an edit, agent B read it before but finishes writing
28
+ * second. Ordering on fetch time puts B's pre-edit text back over A's correct one. Ordering on
29
+ * MAX's `update_time`, an edit always beats a version that never saw it, whoever writes last.
30
+ *
31
+ * `>=` rather than `>` so that two ordinary re-fetches of the same unedited message both go
32
+ * through — they carry identical text, so the write is harmless and refreshing `fetched_at` is
33
+ * the point of it.
34
+ */
35
+ const putMessage = database.prepare(`
36
+ INSERT INTO messages (chat_id, id, sender_id, sender_name, time, update_time, text, outgoing, attachments, fetched_at)
37
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
38
+ ON CONFLICT(chat_id, id) DO UPDATE SET
39
+ sender_name = excluded.sender_name, update_time = excluded.update_time, text = excluded.text,
40
+ outgoing = excluded.outgoing, attachments = excluded.attachments, fetched_at = excluded.fetched_at
41
+ WHERE coalesce(excluded.update_time, 0) >= coalesce(messages.update_time, 0)`);
42
+ const toChat = (row) => ({
43
+ id: String(row.id),
44
+ title: row.title === null ? null : String(row.title),
45
+ kind: String(row.kind),
46
+ unreadCount: row.unread_count === null ? null : Number(row.unread_count),
47
+ lastMessageAt: row.last_message_at === null ? null : new Date(Number(row.last_message_at)).toISOString(),
48
+ participantsCount: row.participants_count === null ? null : Number(row.participants_count),
49
+ });
50
+ const toMessage = (row) => ({
51
+ id: String(row.id),
52
+ chatId: String(row.chat_id),
53
+ senderId: row.sender_id === null ? null : String(row.sender_id),
54
+ senderName: row.sender_name === null ? null : String(row.sender_name),
55
+ timestamp: new Date(Number(row.time)).toISOString(),
56
+ editedAt: row.update_time === null ? null : new Date(Number(row.update_time)).toISOString(),
57
+ text: String(row.text),
58
+ outgoing: row.outgoing === null ? null : row.outgoing === 1,
59
+ attachments: JSON.parse(String(row.attachments)),
60
+ });
61
+ const epoch = (iso) => (iso === null ? null : new Date(iso).getTime());
62
+ return {
63
+ chats: {
64
+ read: (freshForMs) => {
65
+ if (!isFresh("chats", freshForMs))
66
+ return undefined;
67
+ return database.prepare("SELECT * FROM chats ORDER BY last_message_at DESC").all().map(toChat);
68
+ },
69
+ write: (chats) => {
70
+ const at = now();
71
+ for (const chat of chats) {
72
+ putChat.run(chat.id, chat.title, chat.kind, chat.unreadCount, epoch(chat.lastMessageAt), chat.participantsCount, at);
73
+ }
74
+ markFetched.run("chats", at);
75
+ },
76
+ },
77
+ contacts: {
78
+ read: (freshForMs) => {
79
+ if (!isFresh("contacts", freshForMs))
80
+ return undefined;
81
+ return database
82
+ .prepare("SELECT * FROM contacts ORDER BY name")
83
+ .all()
84
+ .map((row) => ({
85
+ id: String(row.id),
86
+ name: row.name === null ? null : String(row.name),
87
+ username: row.username === null ? null : String(row.username),
88
+ description: row.description === null ? null : String(row.description),
89
+ }));
90
+ },
91
+ write: (contacts) => {
92
+ const at = now();
93
+ for (const contact of contacts) {
94
+ putContact.run(contact.id, contact.name, contact.username, contact.description, at);
95
+ }
96
+ markFetched.run("contacts", at);
97
+ },
98
+ },
99
+ messages: {
100
+ read: (chatId, limit, freshForMs) => {
101
+ if (!isFresh(`messages:${chatId}`, freshForMs))
102
+ return undefined;
103
+ const rows = database
104
+ .prepare("SELECT * FROM messages WHERE chat_id = ? ORDER BY time DESC LIMIT ?")
105
+ .all(chatId, limit);
106
+ // A window we never claimed to hold completely cannot answer "the newest twenty": we have
107
+ // no way to know whether a twenty-first arrived between the fetch and now.
108
+ const range = database
109
+ .prepare("SELECT to_time FROM ranges WHERE chat_id = ? ORDER BY to_time DESC LIMIT 1")
110
+ .get(chatId);
111
+ if (!range)
112
+ return undefined;
113
+ return rows.map(toMessage).reverse();
114
+ },
115
+ write: (chatId, messages) => {
116
+ if (messages.length === 0)
117
+ return;
118
+ const at = now();
119
+ for (const message of messages) {
120
+ putMessage.run(chatId, message.id, message.senderId, message.senderName, epoch(message.timestamp) ?? 0, epoch(message.editedAt), message.text, message.outgoing === null ? null : Number(message.outgoing), JSON.stringify(message.attachments), at);
121
+ }
122
+ const times = messages.map((message) => epoch(message.timestamp) ?? 0);
123
+ database
124
+ .prepare(`INSERT INTO ranges (chat_id, from_time, to_time) VALUES (?, ?, ?)
125
+ ON CONFLICT(chat_id, from_time) DO UPDATE SET to_time = max(ranges.to_time, excluded.to_time)`)
126
+ .run(chatId, Math.min(...times), Math.max(...times));
127
+ markFetched.run(`messages:${chatId}`, at);
128
+ },
129
+ invalidate: (chatId) => {
130
+ database.prepare("DELETE FROM fetched WHERE kind = ?").run(`messages:${chatId}`);
131
+ },
132
+ },
133
+ claim: (chatId, anchor, holder, forMs) => {
134
+ const at = now();
135
+ return (database
136
+ .prepare(`INSERT INTO fetch_lease (chat_id, anchor, holder, expires_at) VALUES (?, ?, ?, ?)
137
+ ON CONFLICT(chat_id, anchor) DO UPDATE
138
+ SET holder = excluded.holder, expires_at = excluded.expires_at
139
+ WHERE fetch_lease.expires_at <= ?`)
140
+ .run(chatId, anchor, holder, at + forMs, at).changes === 1);
141
+ },
142
+ release: (chatId, anchor) => {
143
+ database.prepare("DELETE FROM fetch_lease WHERE chat_id = ? AND anchor = ?").run(chatId, anchor);
144
+ },
145
+ clear: () => {
146
+ for (const table of ["messages", "chats", "contacts", "ranges", "fetched", "fetch_lease"]) {
147
+ database.exec(`DELETE FROM ${table}`);
148
+ }
149
+ },
150
+ close: () => database.close(),
151
+ };
152
+ };
153
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/cache/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ import type { CacheStore } from "./cache/store.js";
2
+ import type { Chat, Contact, Id, Message, Profile } from "./domain/models.js";
3
+ import { Connection } from "./protocol/connection.js";
4
+ import { type DiagnosticEvent } from "./runs/events.js";
5
+ import type { SessionStore } from "./session/store.js";
6
+ export interface MaxClientOptions {
7
+ store: SessionStore;
8
+ timeoutMs?: number;
9
+ /** Injected by tests; defaults to a real WebSocket connection. */
10
+ connection?: Connection;
11
+ /**
12
+ * Where a protocol note goes. Never stdout: in machine mode that stream carries one JSON value
13
+ * and nothing else.
14
+ */
15
+ warn?: (message: string) => void;
16
+ /** Where reads are recorded. It is a record, not a shortcut — see `offline`. */
17
+ cache?: CacheStore;
18
+ /**
19
+ * Answer from what was recorded and **never connect**.
20
+ *
21
+ * Off by default, and deliberately: `LOGIN` already returns the chats, the contacts and recent
22
+ * messages, so once a command connects at all those are fresh for free. Serving them from disk
23
+ * instead buys only the right to skip connecting — which is the same act as deciding not to find
24
+ * out what changed. For a messenger that is backwards (§3.4z of the cache plan).
25
+ */
26
+ offline?: boolean;
27
+ /**
28
+ * One event per request, for whoever is keeping a diagnostic. Injected the way `warn` is: the
29
+ * client reports what it did and never decides where that goes. Absent means nothing is kept.
30
+ */
31
+ events?: (event: DiagnosticEvent) => void;
32
+ }
33
+ /**
34
+ * **The only thing above this line that knows MAX exists.** Commands speak the domain model; the
35
+ * opcodes and the frames stop here, so a different transport underneath changes this file and
36
+ * nothing else (REQUIREMENTS §25).
37
+ *
38
+ * The methods are grouped by subject — `client.chats.list()`, `client.messages.send()` — which is
39
+ * the shape §8 sketched. **There is only this door.** The generated per-operation wrappers sit
40
+ * underneath and stay internal: they return raw MAX payloads, without the resend rule, the name
41
+ * filling or the id conversion that make this safe to point at a real account (`NEED-34`).
42
+ *
43
+ * One instance is one connection. `open` then `close`, always in a `finally`.
44
+ */
45
+ export declare class MaxClient {
46
+ #private;
47
+ constructor({ store, timeoutMs, connection, warn, cache, offline, events }: MaxClientOptions);
48
+ readonly account: {
49
+ me: () => Profile;
50
+ };
51
+ readonly chats: {
52
+ /**
53
+ * Chats come with the login; a limit trims rather than fetching more.
54
+ *
55
+ * **A one-to-one chat has no title of its own** — its name is the other person's, which MAX
56
+ * does not put in the chat object. So the partner is looked up once, for every dialog at a
57
+ * time, and the name is filled in. Without this, `max chats` shows a column of blanks for
58
+ * exactly the chats a person recognises by name.
59
+ */
60
+ list: (limit?: number) => Promise<Chat[]>;
61
+ /**
62
+ * Turns what the person typed into a chat id.
63
+ *
64
+ * A number is taken as an id. Anything else is matched against chat titles, exactly first and
65
+ * then as a fragment — and **an ambiguous name is an error, not a guess**: sending to the wrong
66
+ * conversation is not undoable, so the caller is shown the candidates and asked to be specific.
67
+ */
68
+ resolve: (reference: string) => Promise<Id>;
69
+ };
70
+ readonly contacts: {
71
+ /** The people this account has a one-to-one chat with, named. */
72
+ list: () => Promise<Contact[]>;
73
+ };
74
+ readonly messages: {
75
+ list: (chatId: Id, limit?: number) => Promise<Message[]>;
76
+ /**
77
+ * Sends one message, retrying **only with the same `cid`**.
78
+ *
79
+ * §17 allows a send to be retried when the protocol has a verified deduplication handle. It has
80
+ * one, measured against MAX on 2026-09-19: sending the same `cid` twice returned the same
81
+ * message id and left **one** copy in the chat — and it held across two separate connections
82
+ * and logins, which is the case a retry actually faces.
83
+ *
84
+ * So one retry, same `cid`, and nothing beyond that. What is still unmeasured is how long the
85
+ * server remembers a `cid`; the two probes were seconds apart. If the retry also fails the
86
+ * answer is `outcome_unknown` — never failed, never sent — and it names the `cid`, because
87
+ * `max send --cid <n>` can then repeat the attempt without risking a second message.
88
+ */
89
+ send: (chatId: Id, text: string, options?: {
90
+ cid?: number;
91
+ notify?: boolean;
92
+ }) => Promise<Message>;
93
+ };
94
+ /**
95
+ * Opens the connection and logs in with the stored token.
96
+ *
97
+ * The login response carries the profile, the chats, the contacts and recent messages, so most
98
+ * commands need no further request — measured against MAX on 2026-09-19.
99
+ */
100
+ connect(): Promise<void>;
101
+ close(): Promise<void>;
102
+ }
103
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAG7E,OAAO,EAAE,UAAU,EAAiB,MAAM,0BAA0B,CAAA;AAEpE,OAAO,EAAY,KAAK,eAAe,EAAS,MAAM,kBAAkB,CAAA;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAGtD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,YAAY,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,kEAAkE;IAClE,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB;;;OAGG;IACH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,gFAAgF;IAChF,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;CAC1C;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,SAAS;;gBAaR,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,OAAe,EAAE,MAAM,EAAE,EAAE,gBAAgB;IASpG,QAAQ,CAAC,OAAO;kBACN,OAAO;MAChB;IAED,QAAQ,CAAC,KAAK;QACZ;;;;;;;WAOG;uBACkB,MAAM,KAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAuB7C;;;;;;WAMG;6BACwB,MAAM,KAAG,OAAO,CAAC,EAAE,CAAC;MAgBhD;IAED,QAAQ,CAAC,QAAQ;QACf,iEAAiE;oBACjD,OAAO,CAAC,OAAO,EAAE,CAAC;MASnC;IAED,QAAQ,CAAC,QAAQ;uBACM,EAAE,qBAAe,OAAO,CAAC,OAAO,EAAE,CAAC;QA0BxD;;;;;;;;;;;;WAYG;uBACkB,EAAE,QAAQ,MAAM,YAAW;YAAE,GAAG,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,OAAO,CAAA;SAAE,KAAQ,OAAO,CAAC,OAAO,CAAC;MAuC3G;IAED;;;;;OAKG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA6BxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAyJ7B"}