@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,40 @@
1
+ import { MaxClient } from "../client.js";
2
+ import { resolveSettings } from "../config.js";
3
+ import { resolveOutput } from "../output.js";
4
+ import { recorded } from "../runs/recording.js";
5
+ import { SessionStore } from "../session/store.js";
6
+ /**
7
+ * Everything a command needs, resolved once — and **the only place a `MaxClient` is built.**
8
+ *
9
+ * One construction site is what keeps the protocol note quiet under `--quiet`: a command building
10
+ * its own client gets the default `warn`, which writes straight to stderr past the renderer
11
+ * (`BUG-7`). With eight commands, "remember to pass `warn`" is a rule that gets broken once and
12
+ * then nobody can see it.
13
+ *
14
+ * It is also where `--record` stops being a flag and becomes a setting: `recorded` takes what
15
+ * `resolveSettings` decided — flag, then environment, then the configuration file — rather than
16
+ * what `commander` happened to parse.
17
+ *
18
+ * The cache is not opened here on purpose — only the reading commands want one, and opening it
19
+ * would create a database file for `session end`, which will never read it.
20
+ */
21
+ export const forCommand = (flags) => {
22
+ const settings = resolveSettings(flags);
23
+ const { renderer, format } = resolveOutput(settings);
24
+ const store = new SessionStore({ profile: settings.profile });
25
+ return {
26
+ settings,
27
+ renderer,
28
+ format,
29
+ store,
30
+ createClient: (extra = {}) => new MaxClient({ store, timeoutMs: settings.timeoutMs, warn: renderer.note, ...extra }),
31
+ run: (command, body) => recorded({
32
+ command,
33
+ profile: settings.profile,
34
+ options: { record: settings.record, verbose: settings.verbose },
35
+ format,
36
+ keepDays: settings.keepRunsForDays,
37
+ }, body),
38
+ };
39
+ };
40
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAyB,MAAM,cAAc,CAAA;AAC/D,OAAO,EAAoB,eAAe,EAAiB,MAAM,cAAc,CAAA;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAgBlD;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAkB,EAAkB,EAAE;IAC/D,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,CAAC,CAAA;IACvC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAA;IACpD,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;IAE7D,OAAO;QACL,QAAQ;QACR,QAAQ;QACR,MAAM;QACN,KAAK;QACL,YAAY,EAAE,CAAC,KAAK,GAAG,EAAE,EAAE,EAAE,CAC3B,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC;QACxF,GAAG,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CACrB,QAAQ,CACN;YACE,OAAO;YACP,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,OAAO,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE;YAC/D,MAAM;YACN,QAAQ,EAAE,QAAQ,CAAC,eAAe;SACnC,EACD,IAAI,CACL;KACJ,CAAA;AACH,CAAC,CAAA"}
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare const messagesCommand: () => Command;
3
+ //# sourceMappingURL=messages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/commands/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAInC,eAAO,MAAM,eAAe,QAAO,OAgElC,CAAA"}
@@ -0,0 +1,62 @@
1
+ import { Command } from "commander";
2
+ import { openProfileCache } from "../cache/index.js";
3
+ import { forCommand } from "./context.js";
4
+ export const messagesCommand = () => {
5
+ const command = new Command("messages").description("read and send messages in a chat");
6
+ /**
7
+ * Observational, and deliberately so: this reads `CHAT_HISTORY` and never sends `CHAT_MARK`, so
8
+ * looking at a conversation does not tell anyone you read it (REQUIREMENTS §19).
9
+ */
10
+ command
11
+ .command("list")
12
+ .argument("<chat>", "chat id, or part of a chat name")
13
+ .description("recent messages in a chat, oldest first")
14
+ .option("--limit <n>", "how many to read", (value) => Number.parseInt(value, 10))
15
+ .action(async function (chat) {
16
+ const { renderer, settings, createClient, run } = forCommand(this.optsWithGlobals());
17
+ const cache = await openProfileCache(settings.profile, { onProblem: (message) => renderer.note(message) });
18
+ await run("messages list", async (events) => {
19
+ const client = createClient({ events, ...(cache ? { cache } : {}) });
20
+ try {
21
+ const chatId = await client.chats.resolve(chat);
22
+ renderer.result(await client.messages.list(chatId, settings.limit));
23
+ }
24
+ finally {
25
+ await client.close();
26
+ cache?.close();
27
+ }
28
+ });
29
+ });
30
+ /**
31
+ * One message, one command, no retry.
32
+ *
33
+ * There is no `--dry-run` here: the target and the text are both in the line the person typed, so
34
+ * a preview would restate the command back at them (REQUIREMENTS §21). What this command does owe
35
+ * the caller is honesty about an outcome it does not know — see `MaxClient.messages.send`.
36
+ */
37
+ command
38
+ .command("send")
39
+ .argument("<chat>", "chat id, or part of a chat name")
40
+ .argument("<text>", "what to say")
41
+ .description("send one text message")
42
+ .option("--cid <n>", "reuse a client id from an earlier ambiguous send; MAX collapses the duplicate", (value) => Number.parseInt(value, 10))
43
+ .action(async function (chat, text) {
44
+ const options = this.optsWithGlobals();
45
+ const { renderer, settings, createClient, run } = forCommand(options);
46
+ const cache = await openProfileCache(settings.profile, { onProblem: (message) => renderer.note(message) });
47
+ await run("messages send", async (events) => {
48
+ const client = createClient({ events, ...(cache ? { cache } : {}) });
49
+ try {
50
+ const chatId = await client.chats.resolve(chat);
51
+ const sent = await client.messages.send(chatId, text, options.cid === undefined ? {} : { cid: options.cid });
52
+ renderer.result(sent);
53
+ }
54
+ finally {
55
+ await client.close();
56
+ cache?.close();
57
+ }
58
+ });
59
+ });
60
+ return command;
61
+ };
62
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/commands/messages.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { Command } from "commander";
2
+ /**
3
+ * What this tool did, when it was asked to keep the record.
4
+ *
5
+ * Reading the log is not itself an action worth recording, so **nothing here starts a run** — a
6
+ * `max runs list` that created a run directory while listing them would be its own joke.
7
+ */
8
+ export declare const runsCommand: () => Command;
9
+ //# sourceMappingURL=runs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runs.d.ts","sourceRoot":"","sources":["../../src/commands/runs.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAInC;;;;;GAKG;AACH,eAAO,MAAM,WAAW,QAAO,OAgD9B,CAAA"}
@@ -0,0 +1,68 @@
1
+ import { CliError } from "@leemour/cli-core";
2
+ import { Command } from "commander";
3
+ import { resolveOutput } from "../output.js";
4
+ import { findRun, listRuns, readEvents, runsDirFor } from "../runs/run.js";
5
+ /**
6
+ * What this tool did, when it was asked to keep the record.
7
+ *
8
+ * Reading the log is not itself an action worth recording, so **nothing here starts a run** — a
9
+ * `max runs list` that created a run directory while listing them would be its own joke.
10
+ */
11
+ export const runsCommand = () => {
12
+ const command = new Command("runs").description("recorded runs — what this tool did, and when");
13
+ command
14
+ .command("list")
15
+ .description("recorded runs, newest first")
16
+ .option("--limit <n>", "how many to show", (value) => Number.parseInt(value, 10), 20)
17
+ .action(function () {
18
+ const options = this.optsWithGlobals();
19
+ const { renderer } = resolveOutput(options);
20
+ const runs = listRuns(runsDirFor());
21
+ renderer.result(runs.slice(0, options.limit));
22
+ // An empty list reads as broken otherwise, and the reason is that nothing asked for a
23
+ // record — recording is off unless it is asked for (`NEED-52`). The explanation is a
24
+ // diagnostic, so stdout still carries one JSON value and nothing else.
25
+ if (runs.length === 0)
26
+ renderer.note("nothing recorded — a run is kept only with `--record`");
27
+ });
28
+ command
29
+ .command("show")
30
+ .argument("<run-id>", "an id from `max runs list`")
31
+ .description("one run: what it was, and one line per request")
32
+ .action(function (id) {
33
+ const options = this.optsWithGlobals();
34
+ const { renderer } = resolveOutput(options);
35
+ const found = runOrRefuse(id);
36
+ // Both, because neither answers the question on its own: the metadata says how it ended and
37
+ // the events say where it went.
38
+ renderer.result({ ...found.metadata, directory: found.dir, events: readEvents(found.dir).map(readable) });
39
+ });
40
+ command
41
+ .command("path")
42
+ .argument("<run-id>", "an id from `max runs list`")
43
+ .description("the directory holding one run")
44
+ .action(function (id) {
45
+ const options = this.optsWithGlobals();
46
+ const { renderer } = resolveOutput(options);
47
+ // One JSON value on stdout here too, with no exception for convenience (`NEED-88`): a script
48
+ // reads `--json | jq -r .path`, and a terminal prints the bare path as a labelled line.
49
+ renderer.result({ path: runOrRefuse(id).dir });
50
+ });
51
+ return command;
52
+ };
53
+ /**
54
+ * Drops what Pino puts on every line: the level, and the run id, command and profile that are the
55
+ * same for the whole run and are printed above it anyway.
56
+ *
57
+ * Without this the table is five constant columns wide before it reaches the opcode, and the
58
+ * durations and sizes — the reason anyone opened it — fall off the right of an 80-column terminal.
59
+ * `max runs path <id>` still gives the file, envelope and all.
60
+ */
61
+ const readable = ({ level, runId, command, profile, ...event }) => event;
62
+ const runOrRefuse = (id) => {
63
+ const found = findRun(runsDirFor(), id);
64
+ if (!found)
65
+ throw new CliError("not_found", `no recorded run "${id}" — \`max runs list\` shows what there is`);
66
+ return found;
67
+ };
68
+ //# sourceMappingURL=runs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runs.js","sourceRoot":"","sources":["../../src/commands/runs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare const sessionCommand: () => Command;
3
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/commands/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKnC,eAAO,MAAM,cAAc,QAAO,OA4EjC,CAAA"}
@@ -0,0 +1,74 @@
1
+ import { Command } from "commander";
2
+ import { commandWords, refuseCommandName, rootOf } from "../profile.js";
3
+ import { readSecret } from "../session/prompt.js";
4
+ import { forCommand } from "./context.js";
5
+ export const sessionCommand = () => {
6
+ const command = new Command("session").description("the stored MAX session for this profile");
7
+ /**
8
+ * For now, importing a token obtained elsewhere — from `web.max.ru`, or from another client.
9
+ *
10
+ * This is not a stopgap: it is the escape hatch that keeps every other command usable when our
11
+ * own login flow meets a captcha or an unusual second factor, and it is how the slice is tested
12
+ * without an SMS per run. The interactive phone-and-code flow lands beside it, not instead of it.
13
+ *
14
+ * **The token is asked for, not passed.** A token in argv is read by `ps` and kept by shell
15
+ * history, and a file leaves a copy nobody deletes, so with `MAX_TOKEN` unset it is read from the
16
+ * terminal without being echoed — or from a pipe, for `pass show max | max session start`. From
17
+ * there it goes to the OS keyring.
18
+ */
19
+ command
20
+ .command("start")
21
+ .description("store a MAX session for this profile")
22
+ .action(async function () {
23
+ const { renderer, settings, store, createClient, run } = forCommand(this.optsWithGlobals());
24
+ // Creation is the only moment the collision can still be explained; after this the name is
25
+ // written down and `max <name>` would silently be a command instead.
26
+ refuseCommandName(settings.profile, commandWords(rootOf(this)));
27
+ const token = process.env.MAX_TOKEN?.trim() || (await readSecret("MAX token: "));
28
+ // Before the run directory: nothing was attempted, so there is nothing to record.
29
+ if (!token) {
30
+ renderer.failure("no token given");
31
+ process.exitCode = 2;
32
+ return;
33
+ }
34
+ store.writeToken(token);
35
+ await run("session start", async (events) => {
36
+ const client = createClient({ events });
37
+ try {
38
+ await client.connect();
39
+ const profile = client.account.me();
40
+ renderer.result({ profile, stored: true });
41
+ renderer.success(`logged in as ${profile.name ?? profile.id}`);
42
+ }
43
+ finally {
44
+ await client.close();
45
+ }
46
+ });
47
+ });
48
+ /**
49
+ * Local only, for now: it forgets the token and the device identity on this machine.
50
+ *
51
+ * It does **not** send LOGOUT (opcode 20), which would end the session on MAX's side too — and
52
+ * with it the browser tab the token came from. That distinction is in the output rather than
53
+ * glossed over: a forgotten token that is still live elsewhere is a different thing from a
54
+ * revoked one.
55
+ */
56
+ command
57
+ .command("end")
58
+ .description("forget the stored session for this profile")
59
+ .action(async function () {
60
+ const { renderer, store, run } = forCommand(this.optsWithGlobals());
61
+ // It contacts nobody, so the run holds no events — but forgetting a session is an action, and
62
+ // "when did this profile stop working" is a question the record is kept to answer.
63
+ await run("session end", async () => {
64
+ const had = store.forget();
65
+ renderer.result({ profile: store.profile, forgotten: had, revokedOnServer: false });
66
+ if (had)
67
+ renderer.success(`forgot the session for "${store.profile}" on this machine`);
68
+ else
69
+ renderer.note(`there was no session for "${store.profile}"`);
70
+ });
71
+ });
72
+ return command;
73
+ };
74
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/commands/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import * as v from "valibot";
2
+ export declare const configSchema: v.StrictObjectSchema<{
3
+ readonly defaultProfile: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
4
+ readonly profiles: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.StrictObjectSchema<{
5
+ readonly limit: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>]>, undefined>;
6
+ readonly timeoutMs: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>]>, undefined>;
7
+ readonly color: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
8
+ /** The run log reads these two; nothing records anything until it exists. */
9
+ readonly record: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
10
+ readonly keepRunsForDays: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>]>, undefined>;
11
+ }, undefined>, undefined>, {}>;
12
+ }, undefined>;
13
+ export type Config = v.InferOutput<typeof configSchema>;
14
+ /** Whatever the command line carried. Everything is optional: absent means "not given here". */
15
+ export interface GlobalFlags {
16
+ profile?: string;
17
+ json?: boolean;
18
+ quiet?: boolean;
19
+ verbose?: boolean;
20
+ limit?: number;
21
+ record?: boolean;
22
+ }
23
+ export interface Settings {
24
+ profile: string;
25
+ json: boolean;
26
+ quiet: boolean;
27
+ verbose: boolean;
28
+ /** Unset means "decide from the terminal", which is `resolveOutput`'s job, not this one's. */
29
+ color: boolean | undefined;
30
+ limit: number;
31
+ /** Unset means the transport's own default; the number lives in `protocol/connection.ts`. */
32
+ timeoutMs: number | undefined;
33
+ record: boolean;
34
+ keepRunsForDays: number;
35
+ /** Named in errors and in `max --help`, so a person can find the file that decided this. */
36
+ configPath: string;
37
+ }
38
+ export interface ResolveOptions {
39
+ env?: NodeJS.ProcessEnv;
40
+ /** Tests pass a temporary directory; nothing else should need this. */
41
+ configDir?: string;
42
+ }
43
+ /**
44
+ * **Flag, then environment, then the configuration file, then the built-in default.** One place,
45
+ * so no command can decide the order differently from another.
46
+ *
47
+ * Two things already jump this queue and are documented rather than re-litigated here:
48
+ * `MAX_TOKEN` outranks the keyring (`cli-core`'s `Credentials.read`), and `MAX_CONFIG_DIR`,
49
+ * `MAX_STATE_DIR` or `MAX_CACHE_DIR` move the whole installation — including which keyring entry
50
+ * a profile means (`ARCHITECTURE.md` §13).
51
+ */
52
+ export declare const resolveSettings: (flags?: GlobalFlags, { env, configDir }?: ResolveOptions) => Settings;
53
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AA6B5B,eAAO,MAAM,YAAY;;;;;;QALvB,6EAA6E;;;;aAQ7E,CAAA;AAEF,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,YAAY,CAAC,CAAA;AAEvD,gGAAgG;AAChG,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,8FAA8F;IAC9F,KAAK,EAAE,OAAO,GAAG,SAAS,CAAA;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,6FAA6F;IAC7F,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,MAAM,EAAE,OAAO,CAAA;IACf,eAAe,EAAE,MAAM,CAAA;IACvB,4FAA4F;IAC5F,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,GAAI,QAAO,WAAgB,EAAE,qBAAkC,cAAmB,aA4B7G,CAAA"}
package/dist/config.js ADDED
@@ -0,0 +1,83 @@
1
+ import { CliError, configFilePath, loadConfigFile, resolvePaths } from "@leemour/cli-core";
2
+ import * as v from "valibot";
3
+ import { DEFAULT_PROFILE, usableProfileName } from "./profile.js";
4
+ const APP = "max-cli";
5
+ const DEFAULT_LIMIT = 20;
6
+ const DEFAULT_KEEP_RUNS_FOR_DAYS = 30;
7
+ const count = v.pipe(v.number(), v.integer(), v.minValue(1));
8
+ /**
9
+ * ⚠ `strictObject`, not `object`: an unknown key is an error here.
10
+ *
11
+ * `object()` drops what it does not recognise — measured with valibot 1.5, `{ limitt: 5 }` parses
12
+ * to `{}` and reports nothing — so a misspelled setting would run with the default and never say
13
+ * why. That is the failure this schema exists to prevent, and it is the **opposite** of the rule
14
+ * for MAX's own answers, which keep unknown fields on purpose (`NEED-35`).
15
+ *
16
+ * **No field here can hold a secret.** No token, no phone number, no chat id: a schema with
17
+ * nowhere to put one is stronger than a rule saying do not put one there.
18
+ */
19
+ const profileSettings = v.strictObject({
20
+ limit: v.optional(count),
21
+ timeoutMs: v.optional(count),
22
+ color: v.optional(v.boolean()),
23
+ /** The run log reads these two; nothing records anything until it exists. */
24
+ record: v.optional(v.boolean()),
25
+ keepRunsForDays: v.optional(count),
26
+ });
27
+ export const configSchema = v.strictObject({
28
+ defaultProfile: v.optional(v.string()),
29
+ profiles: v.optional(v.record(v.string(), profileSettings), {}),
30
+ });
31
+ /**
32
+ * **Flag, then environment, then the configuration file, then the built-in default.** One place,
33
+ * so no command can decide the order differently from another.
34
+ *
35
+ * Two things already jump this queue and are documented rather than re-litigated here:
36
+ * `MAX_TOKEN` outranks the keyring (`cli-core`'s `Credentials.read`), and `MAX_CONFIG_DIR`,
37
+ * `MAX_STATE_DIR` or `MAX_CACHE_DIR` move the whole installation — including which keyring entry
38
+ * a profile means (`ARCHITECTURE.md` §13).
39
+ */
40
+ export const resolveSettings = (flags = {}, { env = process.env, configDir } = {}) => {
41
+ const paths = resolvePaths({ appName: APP, prefix: "MAX", env });
42
+ const configPath = configFilePath(configDir ?? paths.config);
43
+ const config = readConfig(configPath);
44
+ const profile = usableProfileName(flags.profile ?? given(env.MAX_PROFILE) ?? config.defaultProfile ?? DEFAULT_PROFILE);
45
+ const configured = config.profiles[profile] ?? {};
46
+ const settings = {
47
+ profile,
48
+ json: flags.json === true,
49
+ quiet: flags.quiet === true,
50
+ verbose: flags.verbose === true,
51
+ color: configured.color,
52
+ limit: flags.limit ?? configured.limit ?? DEFAULT_LIMIT,
53
+ timeoutMs: configured.timeoutMs,
54
+ record: flags.record ?? configured.record ?? false,
55
+ keepRunsForDays: configured.keepRunsForDays ?? DEFAULT_KEEP_RUNS_FOR_DAYS,
56
+ configPath,
57
+ };
58
+ // The file was checked by the schema; a flag was not, and `--limit abc` is `NaN` by the time it
59
+ // gets here, which slices an array to nothing without complaining.
60
+ if (!Number.isInteger(settings.limit) || settings.limit < 1) {
61
+ throw new CliError("validation_error", `--limit takes a whole number from 1 upwards, not ${flags.limit}`);
62
+ }
63
+ return settings;
64
+ };
65
+ /**
66
+ * A missing file is not an error — it is a program nobody has configured. A malformed one is, in
67
+ * every output mode: an agent reading JSON deserves the same refusal a person gets, and silently
68
+ * falling back to the defaults is what makes a typo cost an afternoon.
69
+ */
70
+ const readConfig = (path) => {
71
+ try {
72
+ return loadConfigFile(path, configSchema, () => ({ profiles: {} }));
73
+ }
74
+ catch (error) {
75
+ throw new CliError("configuration_error", error instanceof Error ? error.message : String(error));
76
+ }
77
+ };
78
+ /** An environment variable set to the empty string is not a value; it is the shell being unset. */
79
+ const given = (value) => {
80
+ const trimmed = value?.trim();
81
+ return trimmed === undefined || trimmed === "" ? undefined : trimmed;
82
+ };
83
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { type Payload } from "../protocol/frame.js";
2
+ import type { Chat, Contact, Id, Message, Profile } from "./models.js";
3
+ /**
4
+ * Wire shapes into our own types.
5
+ *
6
+ * **Tolerant when reading** (REQUIREMENTS §29): an unknown field is ignored, a missing one becomes
7
+ * `null`, and nothing here throws. This is an unofficial protocol — a response that gained a field
8
+ * must not break a command, and a response that lost one must say so rather than invent a value.
9
+ */
10
+ export interface NameLookup {
11
+ /** Display names by user id, built from the `contacts` the login response carries. */
12
+ names?: Map<Id, string>;
13
+ /** Whose account this is, so a message can say whether it is ours. */
14
+ viewerId?: Id;
15
+ }
16
+ export declare const toChat: (raw: Payload) => Chat;
17
+ export declare const toMessage: (raw: Payload, chatId: Id, lookup?: NameLookup) => Message;
18
+ export declare const toContact: (raw: Payload) => Contact;
19
+ export declare const toProfile: (raw: Payload) => Profile;
20
+ /** The names a login response carries, so a message can name its sender without another request. */
21
+ export declare const namesFrom: (contacts: unknown) => Map<Id, string>;
22
+ //# sourceMappingURL=map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../../src/domain/map.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,KAAK,EAAc,IAAI,EAAY,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAE5F;;;;;;GAMG;AAEH,MAAM,WAAW,UAAU;IACzB,sFAAsF;IACtF,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;IACvB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,EAAE,CAAA;CACd;AAED,eAAO,MAAM,MAAM,GAAI,KAAK,OAAO,KAAG,IAOpC,CAAA;AAEF,eAAO,MAAM,SAAS,GAAI,KAAK,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAQ,UAAe,KAAG,OAc7E,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,KAAK,OAAO,KAAG,OAKvC,CAAA;AAEF,eAAO,MAAM,SAAS,GAAI,KAAK,OAAO,KAAG,OAQxC,CAAA;AAED,oGAAoG;AACpG,eAAO,MAAM,SAAS,GAAI,UAAU,OAAO,KAAG,GAAG,CAAC,EAAE,EAAE,MAAM,CAY3D,CAAA"}
@@ -0,0 +1,107 @@
1
+ import { asId } from "../protocol/frame.js";
2
+ export const toChat = (raw) => ({
3
+ id: asId(raw.id) ?? "",
4
+ title: text(raw.title),
5
+ kind: chatKind(raw.type),
6
+ unreadCount: count(raw.newMessages),
7
+ lastMessageAt: timestamp(raw.lastEventTime ?? asRecord(raw.lastMessage)?.time),
8
+ participantsCount: count(raw.participantsCount),
9
+ });
10
+ export const toMessage = (raw, chatId, lookup = {}) => {
11
+ const senderId = asId(raw.sender);
12
+ return {
13
+ id: asId(raw.id) ?? "",
14
+ chatId,
15
+ senderId: senderId ?? null,
16
+ senderName: senderId ? (lookup.names?.get(senderId) ?? null) : null,
17
+ timestamp: timestamp(raw.time) ?? new Date(0).toISOString(),
18
+ editedAt: timestamp(raw.updateTime),
19
+ text: text(raw.text) ?? "",
20
+ outgoing: lookup.viewerId === undefined || senderId === undefined ? null : senderId === lookup.viewerId,
21
+ attachments: attachments(raw.attaches),
22
+ };
23
+ };
24
+ export const toContact = (raw) => ({
25
+ id: asId(raw.id) ?? "",
26
+ name: displayName(raw.names) ?? text(raw.name),
27
+ username: text(raw.link),
28
+ description: text(raw.description),
29
+ });
30
+ export const toProfile = (raw) => {
31
+ const contact = asRecord(raw.contact) ?? raw;
32
+ return {
33
+ id: asId(contact.id) ?? "",
34
+ name: displayName(contact.names) ?? text(contact.name),
35
+ phone: phone(contact.phones) ?? text(contact.phone),
36
+ };
37
+ };
38
+ /** The names a login response carries, so a message can name its sender without another request. */
39
+ export const namesFrom = (contacts) => {
40
+ const names = new Map();
41
+ if (!Array.isArray(contacts))
42
+ return names;
43
+ for (const entry of contacts) {
44
+ const contact = asRecord(entry);
45
+ if (!contact)
46
+ continue;
47
+ const id = asId(contact.id);
48
+ const name = displayName(contact.names) ?? text(contact.name);
49
+ if (id && name)
50
+ names.set(id, name);
51
+ }
52
+ return names;
53
+ };
54
+ const asRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
55
+ const text = (value) => (typeof value === "string" && value !== "" ? value : null);
56
+ const count = (value) => {
57
+ const asNumber = typeof value === "bigint" ? Number(value) : value;
58
+ return typeof asNumber === "number" && Number.isFinite(asNumber) ? asNumber : null;
59
+ };
60
+ /** MAX sends Unix milliseconds; output is ISO 8601, which sorts, parses and needs no timezone guess. */
61
+ const timestamp = (value) => {
62
+ const ms = count(value);
63
+ if (ms === null || ms <= 0)
64
+ return null;
65
+ const date = new Date(ms);
66
+ return Number.isNaN(date.getTime()) ? null : date.toISOString();
67
+ };
68
+ const chatKind = (value) => {
69
+ switch (typeof value === "string" ? value.toUpperCase() : "") {
70
+ case "DIALOG":
71
+ return "dialog";
72
+ case "CHAT":
73
+ return "group";
74
+ case "CHANNEL":
75
+ return "channel";
76
+ default:
77
+ return "unknown";
78
+ }
79
+ };
80
+ /** `names` is a list of `{name, type}`; the full name wins, and any name beats none. */
81
+ const displayName = (value) => {
82
+ if (!Array.isArray(value))
83
+ return null;
84
+ const entries = value.map(asRecord).filter((entry) => entry !== undefined);
85
+ const full = entries.find((entry) => entry.type === "FULL_NAME");
86
+ return text(full?.name) ?? text(entries[0]?.name);
87
+ };
88
+ const phone = (value) => {
89
+ if (!Array.isArray(value))
90
+ return null;
91
+ const first = asRecord(value[0]);
92
+ const number = first?.number;
93
+ return text(number) ?? (typeof number === "number" || typeof number === "bigint" ? `+${number}` : null);
94
+ };
95
+ /**
96
+ * Attachment **metadata**, never its content. A message that carries a photo says so; downloading
97
+ * it is a different command that does not exist yet (§11 of the brief).
98
+ */
99
+ const attachments = (value) => {
100
+ if (!Array.isArray(value))
101
+ return [];
102
+ return value
103
+ .map(asRecord)
104
+ .filter((entry) => entry !== undefined)
105
+ .map((entry) => ({ kind: typeof entry._type === "string" ? entry._type.toLowerCase() : "unknown" }));
106
+ };
107
+ //# sourceMappingURL=map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map.js","sourceRoot":"","sources":["../../src/domain/map.ts"],"names":[],"mappings":"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"}