@leemour/max-cli 0.1.0 → 0.3.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.
- package/README.md +28 -12
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +3 -0
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/schema.d.ts +13 -1
- package/dist/cache/schema.d.ts.map +1 -1
- package/dist/cache/schema.js +159 -8
- package/dist/cache/schema.js.map +1 -1
- package/dist/cache/store.d.ts +115 -4
- package/dist/cache/store.d.ts.map +1 -1
- package/dist/cache/store.js +248 -27
- package/dist/cache/store.js.map +1 -1
- package/dist/client.d.ts +91 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +371 -33
- package/dist/client.js.map +1 -1
- package/dist/commands/chats.d.ts.map +1 -1
- package/dist/commands/chats.js +25 -8
- package/dist/commands/chats.js.map +1 -1
- package/dist/commands/contacts.d.ts.map +1 -1
- package/dist/commands/contacts.js +51 -5
- package/dist/commands/contacts.js.map +1 -1
- package/dist/commands/context.d.ts +4 -1
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +4 -2
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/login.d.ts +15 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +43 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +10 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +22 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/me.d.ts +3 -0
- package/dist/commands/me.d.ts.map +1 -0
- package/dist/commands/me.js +18 -0
- package/dist/commands/me.js.map +1 -0
- package/dist/commands/messages.d.ts.map +1 -1
- package/dist/commands/messages.js +82 -3
- package/dist/commands/messages.js.map +1 -1
- package/dist/commands/paging.d.ts +41 -0
- package/dist/commands/paging.d.ts.map +1 -0
- package/dist/commands/paging.js +54 -0
- package/dist/commands/paging.js.map +1 -0
- package/dist/commands/send.d.ts +10 -0
- package/dist/commands/send.d.ts.map +1 -0
- package/dist/commands/send.js +30 -0
- package/dist/commands/send.js.map +1 -0
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +3 -3
- package/dist/commands/session.js.map +1 -1
- package/dist/config.d.ts +16 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +15 -1
- package/dist/config.js.map +1 -1
- package/dist/domain/map.d.ts +1 -0
- package/dist/domain/map.d.ts.map +1 -1
- package/dist/domain/map.js +44 -3
- package/dist/domain/map.js.map +1 -1
- package/dist/domain/models.d.ts +54 -0
- package/dist/domain/models.d.ts.map +1 -1
- package/dist/generated/operations.generated.d.ts +1 -0
- package/dist/generated/operations.generated.d.ts.map +1 -1
- package/dist/output.d.ts +7 -4
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +8 -11
- package/dist/output.js.map +1 -1
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +4 -2
- package/dist/program.js.map +1 -1
- package/dist/protocol/connection.d.ts +10 -0
- package/dist/protocol/connection.d.ts.map +1 -1
- package/dist/protocol/connection.js +11 -3
- package/dist/protocol/connection.js.map +1 -1
- package/dist/protocol/session.d.ts +54 -0
- package/dist/protocol/session.d.ts.map +1 -0
- package/dist/protocol/session.js +57 -0
- package/dist/protocol/session.js.map +1 -0
- package/dist/rendering/messages.d.ts +19 -0
- package/dist/rendering/messages.d.ts.map +1 -0
- package/dist/rendering/messages.js +137 -0
- package/dist/rendering/messages.js.map +1 -0
- package/dist/runs/events.d.ts +1 -1
- package/dist/runs/recording.d.ts +2 -2
- package/dist/runs/recording.d.ts.map +1 -1
- package/dist/runs/recording.js +3 -3
- package/dist/runs/recording.js.map +1 -1
- package/dist/session/adopt.d.ts +22 -0
- package/dist/session/adopt.d.ts.map +1 -0
- package/dist/session/adopt.js +29 -0
- package/dist/session/adopt.js.map +1 -0
- package/dist/session/handshake.d.ts +14 -1
- package/dist/session/handshake.d.ts.map +1 -1
- package/dist/session/handshake.js +5 -5
- package/dist/session/handshake.js.map +1 -1
- package/dist/spec/operations/auth.d.ts +42 -0
- package/dist/spec/operations/auth.d.ts.map +1 -0
- package/dist/spec/operations/session.d.ts +16 -0
- package/dist/spec/operations/session.d.ts.map +1 -1
- package/dist/spec/operations/session.js +21 -1
- package/dist/spec/operations/session.js.map +1 -1
- package/dist/testing/sandbox.d.ts +2 -0
- package/dist/testing/sandbox.d.ts.map +1 -0
- package/dist/testing/sandbox.js +24 -0
- package/dist/testing/sandbox.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +9 -5
package/dist/commands/chats.js
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
|
+
import { CliError } from "@leemour/cli-core";
|
|
1
2
|
import { Command } from "commander";
|
|
2
3
|
import { openProfileCache } from "../cache/index.js";
|
|
3
4
|
import { forCommand } from "./context.js";
|
|
5
|
+
import { renderPage, window, withPaging } from "./paging.js";
|
|
4
6
|
export const chatsCommand = () => {
|
|
5
7
|
const command = new Command("chats").description("the chats this account is in");
|
|
6
|
-
command
|
|
7
|
-
.
|
|
8
|
-
.
|
|
9
|
-
// No default on the option: with one, the flag is always set and a configured `limit` could
|
|
10
|
-
// never win it. The number lives once, in `resolveSettings`.
|
|
11
|
-
.option("--limit <n>", "how many to show", (value) => Number.parseInt(value, 10))
|
|
8
|
+
withPaging(command.command("list").description("the chats this account is in"))
|
|
9
|
+
.option("--search <text>", "only chats whose name contains this; at least 3 characters")
|
|
10
|
+
.option("--kind <dialog|group|channel>", "only chats of this kind")
|
|
12
11
|
.action(async function () {
|
|
13
|
-
const
|
|
12
|
+
const options = this.optsWithGlobals();
|
|
13
|
+
const context = forCommand(options);
|
|
14
|
+
const { renderer, settings, createClient, run } = context;
|
|
14
15
|
const cache = await openProfileCache(settings.profile, { onProblem: (message) => renderer.note(message) });
|
|
15
16
|
await run("chats list", async (events) => {
|
|
16
17
|
const client = createClient({ events, ...(cache ? { cache } : {}) });
|
|
17
18
|
try {
|
|
18
|
-
|
|
19
|
+
renderPage(context, await client.chats.list({
|
|
20
|
+
...window(settings),
|
|
21
|
+
...(options.search === undefined ? {} : { query: String(options.search) }),
|
|
22
|
+
...(options.kind === undefined ? {} : { kind: chatKind(options.kind) }),
|
|
23
|
+
}));
|
|
19
24
|
}
|
|
20
25
|
finally {
|
|
21
26
|
await client.close();
|
|
@@ -25,4 +30,16 @@ export const chatsCommand = () => {
|
|
|
25
30
|
});
|
|
26
31
|
return command;
|
|
27
32
|
};
|
|
33
|
+
/**
|
|
34
|
+
* `--kind` is checked here and refused by name, rather than passed through to return nothing.
|
|
35
|
+
*
|
|
36
|
+
* A value the tool does not know is a typo, and an empty list is the one answer a typo must never
|
|
37
|
+
* produce — it is indistinguishable from "you are in no group chats".
|
|
38
|
+
*/
|
|
39
|
+
const chatKind = (value) => {
|
|
40
|
+
const wanted = String(value).toLowerCase();
|
|
41
|
+
if (wanted === "dialog" || wanted === "group" || wanted === "channel")
|
|
42
|
+
return wanted;
|
|
43
|
+
throw new CliError("validation_error", `--kind takes dialog, group or channel, not "${String(value)}"`);
|
|
44
|
+
};
|
|
28
45
|
//# sourceMappingURL=chats.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chats.js","sourceRoot":"","sources":["../../src/commands/chats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"chats.js","sourceRoot":"","sources":["../../src/commands/chats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAEpD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE5D,MAAM,CAAC,MAAM,YAAY,GAAG,GAAY,EAAE;IACxC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,8BAA8B,CAAC,CAAA;IAEhF,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,8BAA8B,CAAC,CAAC;SAC5E,MAAM,CAAC,iBAAiB,EAAE,4DAA4D,CAAC;SACvF,MAAM,CAAC,+BAA+B,EAAE,yBAAyB,CAAC;SAClE,MAAM,CAAC,KAAK;QACX,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,EAAE,CAAA;QACtC,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAA;QACnC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,OAAO,CAAA;QACzD,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAE1G,MAAM,GAAG,CAAC,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YACvC,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;YAEpE,IAAI,CAAC;gBACH,UAAU,CACR,OAAO,EACP,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;oBACtB,GAAG,MAAM,CAAC,QAAQ,CAAC;oBACnB,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC1E,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;iBACxE,CAAC,CACH,CAAA;YACH,CAAC;oBAAS,CAAC;gBACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;gBACpB,KAAK,EAAE,KAAK,EAAE,CAAA;YAChB,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEJ,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAY,EAAE;IAC5C,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAA;IAC1C,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAA;IACpF,MAAM,IAAI,QAAQ,CAAC,kBAAkB,EAAE,+CAA+C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;AACzG,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contacts.d.ts","sourceRoot":"","sources":["../../src/commands/contacts.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"contacts.d.ts","sourceRoot":"","sources":["../../src/commands/contacts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAMnC;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,QAAO,OA+DlC,CAAA"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { CliError } from "@leemour/cli-core";
|
|
1
2
|
import { Command } from "commander";
|
|
2
3
|
import { openProfileCache } from "../cache/index.js";
|
|
3
4
|
import { forCommand } from "./context.js";
|
|
5
|
+
import { renderPage, window, withPaging } from "./paging.js";
|
|
4
6
|
/**
|
|
5
7
|
* The people this account has a one-to-one chat with.
|
|
6
8
|
*
|
|
@@ -10,17 +12,50 @@ import { forCommand } from "./context.js";
|
|
|
10
12
|
*/
|
|
11
13
|
export const contactsCommand = () => {
|
|
12
14
|
const command = new Command("contacts").description("people you have a one-to-one chat with");
|
|
15
|
+
withPaging(command.command("list").description("people you have a one-to-one chat with"))
|
|
16
|
+
.option("--order <recent|name>", "newest conversation first, or alphabetical")
|
|
17
|
+
.option("--search <text>", "only people whose name or @username contains this; at least 3 characters")
|
|
18
|
+
.action(async function () {
|
|
19
|
+
const options = this.optsWithGlobals();
|
|
20
|
+
const context = forCommand(options);
|
|
21
|
+
const { renderer, settings, createClient, run } = context;
|
|
22
|
+
const cache = await openProfileCache(settings.profile, { onProblem: (message) => renderer.note(message) });
|
|
23
|
+
const order = orderFrom(options.order);
|
|
24
|
+
await run("contacts list", async (events) => {
|
|
25
|
+
const client = createClient({ events, ...(cache ? { cache } : {}) });
|
|
26
|
+
try {
|
|
27
|
+
renderPage(context, await client.contacts.list({
|
|
28
|
+
order,
|
|
29
|
+
...window(settings),
|
|
30
|
+
...(options.search === undefined ? {} : { query: String(options.search) }),
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
await client.close();
|
|
35
|
+
cache?.close();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* The repair tool of the contact store, and **not how contacts normally arrive**: every command
|
|
41
|
+
* logs in, and every login carries the delta, so the store is already current. This is for a
|
|
42
|
+
* store that has drifted, or one a schema rebuild emptied.
|
|
43
|
+
*
|
|
44
|
+
* ⚠ **The summary is counts.** No name, no username, no description, no phone number — a person
|
|
45
|
+
* in a diagnostic is the one leak this project's sixth constraint is about.
|
|
46
|
+
*/
|
|
13
47
|
command
|
|
14
|
-
.command("
|
|
15
|
-
.description("
|
|
16
|
-
.option("--limit <n>", "how many to show", (value) => Number.parseInt(value, 10))
|
|
48
|
+
.command("sync")
|
|
49
|
+
.description("forget where the last sync left off and take the whole list again")
|
|
17
50
|
.action(async function () {
|
|
18
51
|
const { renderer, settings, createClient, run } = forCommand(this.optsWithGlobals());
|
|
19
52
|
const cache = await openProfileCache(settings.profile, { onProblem: (message) => renderer.note(message) });
|
|
20
|
-
await run("contacts
|
|
53
|
+
await run("contacts sync", async (events) => {
|
|
21
54
|
const client = createClient({ events, ...(cache ? { cache } : {}) });
|
|
22
55
|
try {
|
|
23
|
-
|
|
56
|
+
const summary = await client.contacts.sync();
|
|
57
|
+
renderer.result(summary);
|
|
58
|
+
renderer.success(`${summary.added} new, ${summary.changed} changed, ${summary.known} people known`);
|
|
24
59
|
}
|
|
25
60
|
finally {
|
|
26
61
|
await client.close();
|
|
@@ -30,4 +65,15 @@ export const contactsCommand = () => {
|
|
|
30
65
|
});
|
|
31
66
|
return command;
|
|
32
67
|
};
|
|
68
|
+
/**
|
|
69
|
+
* ⚠ **A flag and no configuration field**, deliberately. `--order` and a `contactOrder` setting
|
|
70
|
+
* would be two spellings of one thing, which is what `--profile` was deleted for.
|
|
71
|
+
*/
|
|
72
|
+
const orderFrom = (value) => {
|
|
73
|
+
if (value === undefined)
|
|
74
|
+
return "recent";
|
|
75
|
+
if (value === "recent" || value === "name")
|
|
76
|
+
return value;
|
|
77
|
+
throw new CliError("validation_error", `--order takes "recent" or "name", not "${String(value)}"`);
|
|
78
|
+
};
|
|
33
79
|
//# sourceMappingURL=contacts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contacts.js","sourceRoot":"","sources":["../../src/commands/contacts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"contacts.js","sourceRoot":"","sources":["../../src/commands/contacts.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Renderer, RenderFormat } from "@leemour/cli-core";
|
|
1
|
+
import type { Renderer, RenderFormat, Streams } from "@leemour/cli-core";
|
|
2
2
|
import { MaxClient, type MaxClientOptions } from "../client.js";
|
|
3
3
|
import { type GlobalFlags, type Settings } from "../config.js";
|
|
4
4
|
import { SessionStore } from "../session/store.js";
|
|
@@ -6,6 +6,9 @@ export interface CommandContext {
|
|
|
6
6
|
settings: Settings;
|
|
7
7
|
renderer: Renderer;
|
|
8
8
|
format: RenderFormat;
|
|
9
|
+
/** Whether the human view may use colour — the renderer's decision, handed to views that print their own text. */
|
|
10
|
+
color: boolean;
|
|
11
|
+
streams: Streams;
|
|
9
12
|
store: SessionStore;
|
|
10
13
|
/** Not connected yet: the action owns the `finally` that closes it — and the cache, if it opened one. */
|
|
11
14
|
createClient: (extra?: Partial<Omit<MaxClientOptions, "store">>) => MaxClient;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AACxE,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/D,OAAO,EAAE,KAAK,WAAW,EAAmB,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAA;AAG/E,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAElD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,QAAQ,CAAA;IAClB,QAAQ,EAAE,QAAQ,CAAA;IAClB,MAAM,EAAE,YAAY,CAAA;IACpB,kHAAkH;IAClH,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,YAAY,CAAA;IACnB,yGAAyG;IACzG,YAAY,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC,KAAK,SAAS,CAAA;IAC7E;;;OAGG;IACH,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;CAClG;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,WAAW,KAAG,cA0B/C,CAAA"}
|
package/dist/commands/context.js
CHANGED
|
@@ -20,18 +20,20 @@ import { SessionStore } from "../session/store.js";
|
|
|
20
20
|
*/
|
|
21
21
|
export const forCommand = (flags) => {
|
|
22
22
|
const settings = resolveSettings(flags);
|
|
23
|
-
const { renderer, format } = resolveOutput(settings);
|
|
23
|
+
const { renderer, format, color, streams } = resolveOutput(settings);
|
|
24
24
|
const store = new SessionStore({ profile: settings.profile });
|
|
25
25
|
return {
|
|
26
26
|
settings,
|
|
27
27
|
renderer,
|
|
28
28
|
format,
|
|
29
|
+
color,
|
|
30
|
+
streams,
|
|
29
31
|
store,
|
|
30
32
|
createClient: (extra = {}) => new MaxClient({ store, timeoutMs: settings.timeoutMs, warn: renderer.note, ...extra }),
|
|
31
33
|
run: (command, body) => recorded({
|
|
32
34
|
command,
|
|
33
35
|
profile: settings.profile,
|
|
34
|
-
options: { record: settings.record,
|
|
36
|
+
options: { record: settings.record, trace: settings.trace },
|
|
35
37
|
format,
|
|
36
38
|
keepDays: settings.keepRunsForDays,
|
|
37
39
|
}, body),
|
|
@@ -1 +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;
|
|
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;AAmBlD;;;;;;;;;;;;;;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,KAAK,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAA;IACpE,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,OAAO;QACP,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,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE;YAC3D,MAAM;YACN,QAAQ,EAAE,QAAQ,CAAC,eAAe;SACnC,EACD,IAAI,CACL;KACJ,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
/**
|
|
3
|
+
* For now, importing a token obtained elsewhere — from `web.max.ru`, or from another client.
|
|
4
|
+
*
|
|
5
|
+
* This is not a stopgap: it is the escape hatch that keeps every other command usable when our own
|
|
6
|
+
* login flow meets a captcha or an unusual second factor, and it is how the slice is tested
|
|
7
|
+
* without an SMS per run. The interactive phone-and-code flow lands beside it, not instead of it.
|
|
8
|
+
*
|
|
9
|
+
* **The token is asked for, not passed.** `--token` puts it in `ps` output and shell history, and a
|
|
10
|
+
* file leaves a copy nobody deletes, so with neither given it is read from the terminal without
|
|
11
|
+
* being echoed — or from a pipe, for `pass show max | max login`. From there it goes to the OS
|
|
12
|
+
* keyring and nowhere else.
|
|
13
|
+
*/
|
|
14
|
+
export declare const loginCommand: () => Command;
|
|
15
|
+
//# sourceMappingURL=login.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAMnC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY,QAAO,OA2B1B,CAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { MaxClient } from "../client.js";
|
|
3
|
+
import { resolveOutput } from "../output.js";
|
|
4
|
+
import { readSecret } from "../session/prompt.js";
|
|
5
|
+
import { SessionStore } from "../session/store.js";
|
|
6
|
+
/**
|
|
7
|
+
* For now, importing a token obtained elsewhere — from `web.max.ru`, or from another client.
|
|
8
|
+
*
|
|
9
|
+
* This is not a stopgap: it is the escape hatch that keeps every other command usable when our own
|
|
10
|
+
* login flow meets a captcha or an unusual second factor, and it is how the slice is tested
|
|
11
|
+
* without an SMS per run. The interactive phone-and-code flow lands beside it, not instead of it.
|
|
12
|
+
*
|
|
13
|
+
* **The token is asked for, not passed.** `--token` puts it in `ps` output and shell history, and a
|
|
14
|
+
* file leaves a copy nobody deletes, so with neither given it is read from the terminal without
|
|
15
|
+
* being echoed — or from a pipe, for `pass show max | max login`. From there it goes to the OS
|
|
16
|
+
* keyring and nowhere else.
|
|
17
|
+
*/
|
|
18
|
+
export const loginCommand = () => new Command("login")
|
|
19
|
+
.description("store a MAX session for this profile")
|
|
20
|
+
.option("--token <token>", "a session token; prompted for, or read from a pipe, when omitted")
|
|
21
|
+
.action(async function () {
|
|
22
|
+
const options = this.optsWithGlobals();
|
|
23
|
+
const { renderer } = resolveOutput(options);
|
|
24
|
+
const token = options.token ?? process.env.MAX_TOKEN?.trim() ?? (await readSecret("MAX token: "));
|
|
25
|
+
if (!token) {
|
|
26
|
+
renderer.failure("no token given");
|
|
27
|
+
process.exitCode = 2;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const store = new SessionStore({ profile: options.profile });
|
|
31
|
+
store.writeToken(token);
|
|
32
|
+
const client = new MaxClient({ store });
|
|
33
|
+
try {
|
|
34
|
+
await client.connect();
|
|
35
|
+
const profile = client.account.me();
|
|
36
|
+
renderer.result({ profile, stored: true });
|
|
37
|
+
renderer.success(`logged in as ${profile.name ?? profile.id}`);
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
await client.close();
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAElD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,GAAY,EAAE,CACxC,IAAI,OAAO,CAAC,OAAO,CAAC;KACjB,WAAW,CAAC,sCAAsC,CAAC;KACnD,MAAM,CAAC,iBAAiB,EAAE,kEAAkE,CAAC;KAC7F,MAAM,CAAC,KAAK;IACX,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,EAAE,CAAA;IACtC,MAAM,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,CAAA;IAC3C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,UAAU,CAAC,aAAa,CAAC,CAAC,CAAA;IAEjG,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,QAAQ,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAA;QAClC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;QACpB,OAAM;IACR,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;IAC5D,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;IAEvB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;IACvC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,EAAE,CAAA;QACtB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,CAAA;QACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;QAC1C,QAAQ,CAAC,OAAO,CAAC,gBAAgB,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,EAAE,EAAE,CAAC,CAAA;IAChE,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;IACtB,CAAC;AACH,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
/**
|
|
3
|
+
* Local only, for now: it forgets the token and the device identity on this machine.
|
|
4
|
+
*
|
|
5
|
+
* It does **not** yet send LOGOUT (opcode 20), which would end the session on MAX's side too. That
|
|
6
|
+
* distinction is in the output rather than glossed over: a forgotten token that is still live
|
|
7
|
+
* elsewhere is a different thing from a revoked one.
|
|
8
|
+
*/
|
|
9
|
+
export declare const logoutCommand: () => Command;
|
|
10
|
+
//# sourceMappingURL=logout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAInC;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,QAAO,OAU7B,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { resolveOutput } from "../output.js";
|
|
3
|
+
import { SessionStore } from "../session/store.js";
|
|
4
|
+
/**
|
|
5
|
+
* Local only, for now: it forgets the token and the device identity on this machine.
|
|
6
|
+
*
|
|
7
|
+
* It does **not** yet send LOGOUT (opcode 20), which would end the session on MAX's side too. That
|
|
8
|
+
* distinction is in the output rather than glossed over: a forgotten token that is still live
|
|
9
|
+
* elsewhere is a different thing from a revoked one.
|
|
10
|
+
*/
|
|
11
|
+
export const logoutCommand = () => new Command("logout").description("forget the stored session for this profile").action(function () {
|
|
12
|
+
const options = this.optsWithGlobals();
|
|
13
|
+
const { renderer } = resolveOutput(options);
|
|
14
|
+
const store = new SessionStore({ profile: options.profile });
|
|
15
|
+
const had = store.forget();
|
|
16
|
+
renderer.result({ profile: store.profile, forgotten: had, revokedOnServer: false });
|
|
17
|
+
if (had)
|
|
18
|
+
renderer.success(`forgot the session for "${store.profile}" on this machine`);
|
|
19
|
+
else
|
|
20
|
+
renderer.note(`there was no session for "${store.profile}"`);
|
|
21
|
+
});
|
|
22
|
+
//# sourceMappingURL=logout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAElD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,GAAY,EAAE,CACzC,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,4CAA4C,CAAC,CAAC,MAAM,CAAC;IACrF,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,EAAE,CAAA;IACtC,MAAM,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,CAAA;IAC3C,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;IAC5D,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,EAAE,CAAA;IAE1B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAA;IACnF,IAAI,GAAG;QAAE,QAAQ,CAAC,OAAO,CAAC,2BAA2B,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;;QACjF,QAAQ,CAAC,IAAI,CAAC,6BAA6B,KAAK,CAAC,OAAO,GAAG,CAAC,CAAA;AACnE,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"me.d.ts","sourceRoot":"","sources":["../../src/commands/me.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKnC,eAAO,MAAM,SAAS,QAAO,OAazB,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { MaxClient } from "../client.js";
|
|
3
|
+
import { resolveOutput } from "../output.js";
|
|
4
|
+
import { SessionStore } from "../session/store.js";
|
|
5
|
+
export const meCommand = () => new Command("me").description("who this profile is logged in as").action(async function () {
|
|
6
|
+
const options = this.optsWithGlobals();
|
|
7
|
+
const { renderer } = resolveOutput(options);
|
|
8
|
+
const client = new MaxClient({ store: new SessionStore({ profile: options.profile }) });
|
|
9
|
+
try {
|
|
10
|
+
await client.connect();
|
|
11
|
+
renderer.result(client.account.me());
|
|
12
|
+
}
|
|
13
|
+
finally {
|
|
14
|
+
// Nothing below this line: an open socket keeps the process alive after the answer printed.
|
|
15
|
+
await client.close();
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
//# sourceMappingURL=me.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"me.js","sourceRoot":"","sources":["../../src/commands/me.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAElD,MAAM,CAAC,MAAM,SAAS,GAAG,GAAY,EAAE,CACrC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,kCAAkC,CAAC,CAAC,MAAM,CAAC,KAAK;IAC5E,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,EAAE,CAAA;IACtC,MAAM,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,CAAA;IAC3C,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAA;IAEvF,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,EAAE,CAAA;QACtB,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAA;IACtC,CAAC;YAAS,CAAC;QACT,4FAA4F;QAC5F,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;IACtB,CAAC;AACH,CAAC,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/commands/messages.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/commands/messages.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAOnC,eAAO,MAAM,eAAe,QAAO,OAoIlC,CAAA"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { CliError } from "@leemour/cli-core";
|
|
1
2
|
import { Command } from "commander";
|
|
2
3
|
import { openProfileCache } from "../cache/index.js";
|
|
4
|
+
import { renderMessages } from "../rendering/messages.js";
|
|
3
5
|
import { forCommand } from "./context.js";
|
|
6
|
+
import { renderPage } from "./paging.js";
|
|
4
7
|
export const messagesCommand = () => {
|
|
5
8
|
const command = new Command("messages").description("read and send messages in a chat");
|
|
6
9
|
/**
|
|
@@ -12,14 +15,64 @@ export const messagesCommand = () => {
|
|
|
12
15
|
.argument("<chat>", "chat id, or part of a chat name")
|
|
13
16
|
.description("recent messages in a chat, oldest first")
|
|
14
17
|
.option("--limit <n>", "how many to read", (value) => Number.parseInt(value, 10))
|
|
18
|
+
// Not `--page`: this history is anchored in time, so paging backwards through it is exact
|
|
19
|
+
// rather than approximate. A message id is what the reader has in front of them, having just
|
|
20
|
+
// read the output; an ISO 8601 time is what still works once that message is gone.
|
|
21
|
+
.option("--before <id-or-time>", "read what came before this message id, or this ISO 8601 time")
|
|
15
22
|
.action(async function (chat) {
|
|
16
|
-
const
|
|
23
|
+
const options = this.optsWithGlobals();
|
|
24
|
+
const context = forCommand(options);
|
|
25
|
+
const { renderer, settings, createClient, run } = context;
|
|
17
26
|
const cache = await openProfileCache(settings.profile, { onProblem: (message) => renderer.note(message) });
|
|
18
27
|
await run("messages list", async (events) => {
|
|
19
28
|
const client = createClient({ events, ...(cache ? { cache } : {}) });
|
|
20
29
|
try {
|
|
21
30
|
const chatId = await client.chats.resolve(chat);
|
|
22
|
-
|
|
31
|
+
const before = options.before === undefined ? {} : { before: client.messages.before(String(options.before)) };
|
|
32
|
+
renderPage(context, await client.messages.list(chatId, { limit: settings.limit, ...before }), feed(context), ([oldest]) => `older ones: \`--before ${oldest?.id}\``);
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
await client.close();
|
|
36
|
+
cache?.close();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* **Search reads this machine's copy and never asks MAX.**
|
|
42
|
+
*
|
|
43
|
+
* There is no search operation in MAX's protocol as we know it, so there is nothing to send.
|
|
44
|
+
* That makes this command the opposite of every other read here — it opens no socket, spends no
|
|
45
|
+
* login, and can only find what `max messages list` has already brought down.
|
|
46
|
+
*
|
|
47
|
+
* ⚠ **A short result is therefore ambiguous**, and the note on stderr is what resolves it: not
|
|
48
|
+
* "there are no such messages", but "there are none in what has been read". Saying so costs one
|
|
49
|
+
* line and is the difference between an answer and a misleading one.
|
|
50
|
+
*/
|
|
51
|
+
command
|
|
52
|
+
.command("search")
|
|
53
|
+
.argument("<text>", "what to look for; at least 3 characters")
|
|
54
|
+
.description("find messages in what this machine has already read")
|
|
55
|
+
.option("--chat <id>", "only this chat; an id, because searching never connects to resolve a name")
|
|
56
|
+
.option("--limit <n>", "how many to show", (value) => Number.parseInt(value, 10))
|
|
57
|
+
.action(async function (text) {
|
|
58
|
+
const options = this.optsWithGlobals();
|
|
59
|
+
const context = forCommand(options);
|
|
60
|
+
const { renderer, settings, createClient, run } = context;
|
|
61
|
+
const cache = await openProfileCache(settings.profile, { onProblem: (message) => renderer.note(message) });
|
|
62
|
+
await run("messages search", async (events) => {
|
|
63
|
+
const client = createClient({ events, ...(cache ? { cache } : {}), offline: true });
|
|
64
|
+
try {
|
|
65
|
+
// Resolving a chat by name would need the chat list, which needs a login — and this
|
|
66
|
+
// command promises not to. An id works offline; a name is refused with the reason.
|
|
67
|
+
const chatId = options.chat === undefined ? undefined : offlineChat(String(options.chat));
|
|
68
|
+
const found = await client.messages.search(text, {
|
|
69
|
+
...(chatId === undefined ? {} : { chatId }),
|
|
70
|
+
limit: settings.limit,
|
|
71
|
+
});
|
|
72
|
+
renderer.note(found.items.length === 0
|
|
73
|
+
? "nothing matched what this machine has read — `max messages list <chat>` reads more"
|
|
74
|
+
: "searched the local copy only; a chat nobody has opened is not in it");
|
|
75
|
+
renderPage(context, found, feed(context), () => "more matched — raise `--limit`");
|
|
23
76
|
}
|
|
24
77
|
finally {
|
|
25
78
|
await client.close();
|
|
@@ -40,6 +93,10 @@ export const messagesCommand = () => {
|
|
|
40
93
|
.argument("<text>", "what to say")
|
|
41
94
|
.description("send one text message")
|
|
42
95
|
.option("--cid <n>", "reuse a client id from an earlier ambiguous send; MAX collapses the duplicate", (value) => Number.parseInt(value, 10))
|
|
96
|
+
// `notify` is part of MSG_SEND and has always been sent as `true`. It is the one send option
|
|
97
|
+
// whose absence is felt at the other end rather than here: a script posting at 3am wakes
|
|
98
|
+
// somebody up, and there was no way to say otherwise.
|
|
99
|
+
.option("--silent", "deliver without a notification")
|
|
43
100
|
.action(async function (chat, text) {
|
|
44
101
|
const options = this.optsWithGlobals();
|
|
45
102
|
const { renderer, settings, createClient, run } = forCommand(options);
|
|
@@ -48,7 +105,10 @@ export const messagesCommand = () => {
|
|
|
48
105
|
const client = createClient({ events, ...(cache ? { cache } : {}) });
|
|
49
106
|
try {
|
|
50
107
|
const chatId = await client.chats.resolve(chat);
|
|
51
|
-
const sent = await client.messages.send(chatId, text,
|
|
108
|
+
const sent = await client.messages.send(chatId, text, {
|
|
109
|
+
...(options.cid === undefined ? {} : { cid: options.cid }),
|
|
110
|
+
...(options.silent === true ? { notify: false } : {}),
|
|
111
|
+
});
|
|
52
112
|
renderer.result(sent);
|
|
53
113
|
}
|
|
54
114
|
finally {
|
|
@@ -59,4 +119,23 @@ export const messagesCommand = () => {
|
|
|
59
119
|
});
|
|
60
120
|
return command;
|
|
61
121
|
};
|
|
122
|
+
const feed = ({ color, settings }) => (messages) => renderMessages(messages, {
|
|
123
|
+
color,
|
|
124
|
+
senderColors: settings.senderColors,
|
|
125
|
+
verbosity: settings.detail,
|
|
126
|
+
width: process.stdout.columns ?? 80,
|
|
127
|
+
profile: settings.profile,
|
|
128
|
+
});
|
|
129
|
+
/**
|
|
130
|
+
* `--chat` on a search takes an id, because resolving a name needs the chat list and the chat list
|
|
131
|
+
* needs a login — which this command promises not to spend. Refused by name rather than silently
|
|
132
|
+
* searching every chat, since "I asked for one chat and got all of them" is a wrong answer.
|
|
133
|
+
*/
|
|
134
|
+
const offlineChat = (reference) => {
|
|
135
|
+
const trimmed = reference.trim();
|
|
136
|
+
if (/^-?\d+$/.test(trimmed))
|
|
137
|
+
return trimmed;
|
|
138
|
+
throw new CliError("validation_error", `\`messages search --chat\` takes a chat id, not a name — searching never connects, and a name ` +
|
|
139
|
+
`can only be resolved by asking MAX. \`max chats list --search ${trimmed}\` gives you the id.`);
|
|
140
|
+
};
|
|
62
141
|
//# sourceMappingURL=messages.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/commands/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/commands/messages.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Renderer, RenderFormat, Streams } from "@leemour/cli-core";
|
|
2
|
+
import type { Command } from "commander";
|
|
3
|
+
import type { Settings } from "../config.js";
|
|
4
|
+
import type { Page } from "../domain/models.js";
|
|
5
|
+
/**
|
|
6
|
+
* The three flags every listing shares, so no command invents its own spelling of them.
|
|
7
|
+
*
|
|
8
|
+
* ⚠ **None of them gets a `default`.** With one, commander reports the flag as always given and a
|
|
9
|
+
* configured `limit` could never win it; the number lives once, in `resolveSettings`.
|
|
10
|
+
*/
|
|
11
|
+
export declare const withPaging: (command: Command) => Command;
|
|
12
|
+
/**
|
|
13
|
+
* Which rows to ask the store for. `--all` means no limit, which the store reads as "everything".
|
|
14
|
+
*
|
|
15
|
+
* ⚠ **A page number over a live list can repeat or skip a row.** The order is most-recent-first,
|
|
16
|
+
* so a message arriving between page one and page two pushes somebody across the boundary. Every
|
|
17
|
+
* CLI that pages this way has this; it is in `--help` rather than engineered away.
|
|
18
|
+
*/
|
|
19
|
+
export declare const window: ({ limit, page, all }: Settings) => {
|
|
20
|
+
limit?: number;
|
|
21
|
+
offset: number;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* **One shape for every listing**, and the caller never has to work out which one it got.
|
|
25
|
+
*
|
|
26
|
+
* In the machine modes stdout carries the envelope. For a person it carries the table it always
|
|
27
|
+
* did, and the line about there being another page goes to **stderr** as a note — stdout is data
|
|
28
|
+
* in every mode, and a pager's hint is not data.
|
|
29
|
+
*
|
|
30
|
+
* It is a helper each command calls rather than something inside `renderer.result`, because
|
|
31
|
+
* `account show` and `session start` are not lists and keep answering a bare object.
|
|
32
|
+
*/
|
|
33
|
+
export declare const renderPage: <T>({ renderer, format, settings, streams, }: {
|
|
34
|
+
renderer: Renderer;
|
|
35
|
+
format: RenderFormat;
|
|
36
|
+
settings: Settings;
|
|
37
|
+
streams: Streams;
|
|
38
|
+
}, { items, hasMore }: Page<T>, view?: (items: T[]) => string,
|
|
39
|
+
/** What to type for the rest, when the listing is not paged by `--page`. */
|
|
40
|
+
more?: (items: T[]) => string) => void;
|
|
41
|
+
//# sourceMappingURL=paging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paging.d.ts","sourceRoot":"","sources":["../../src/commands/paging.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AACxE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,SAAS,OAAO,KAAG,OAIF,CAAA;AAE5C;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,GAAI,sBAAsB,QAAQ,KAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAC3B,CAAA;AAE7D;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,EAC1B,0CAKG;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,YAAY,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,EACrF,oBAAoB,IAAI,CAAC,CAAC,CAAC,EAC3B,OAAO,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,MAAM;AAC7B,4EAA4E;AAC5E,OAAO,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,MAAM,KAC5B,IAsBF,CAAA"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The three flags every listing shares, so no command invents its own spelling of them.
|
|
3
|
+
*
|
|
4
|
+
* ⚠ **None of them gets a `default`.** With one, commander reports the flag as always given and a
|
|
5
|
+
* configured `limit` could never win it; the number lives once, in `resolveSettings`.
|
|
6
|
+
*/
|
|
7
|
+
export const withPaging = (command) => command
|
|
8
|
+
.option("--limit <n>", "how many to show", (value) => Number.parseInt(value, 10))
|
|
9
|
+
.option("--page <n>", "which page, starting at 1", (value) => Number.parseInt(value, 10))
|
|
10
|
+
.option("--all", "every row, no paging");
|
|
11
|
+
/**
|
|
12
|
+
* Which rows to ask the store for. `--all` means no limit, which the store reads as "everything".
|
|
13
|
+
*
|
|
14
|
+
* ⚠ **A page number over a live list can repeat or skip a row.** The order is most-recent-first,
|
|
15
|
+
* so a message arriving between page one and page two pushes somebody across the boundary. Every
|
|
16
|
+
* CLI that pages this way has this; it is in `--help` rather than engineered away.
|
|
17
|
+
*/
|
|
18
|
+
export const window = ({ limit, page, all }) => all ? { offset: 0 } : { limit, offset: (page - 1) * limit };
|
|
19
|
+
/**
|
|
20
|
+
* **One shape for every listing**, and the caller never has to work out which one it got.
|
|
21
|
+
*
|
|
22
|
+
* In the machine modes stdout carries the envelope. For a person it carries the table it always
|
|
23
|
+
* did, and the line about there being another page goes to **stderr** as a note — stdout is data
|
|
24
|
+
* in every mode, and a pager's hint is not data.
|
|
25
|
+
*
|
|
26
|
+
* It is a helper each command calls rather than something inside `renderer.result`, because
|
|
27
|
+
* `account show` and `session start` are not lists and keep answering a bare object.
|
|
28
|
+
*/
|
|
29
|
+
export const renderPage = ({ renderer, format, settings, streams, }, { items, hasMore }, view,
|
|
30
|
+
/** What to type for the rest, when the listing is not paged by `--page`. */
|
|
31
|
+
more) => {
|
|
32
|
+
if (format === "jsonl") {
|
|
33
|
+
renderer.stream(items);
|
|
34
|
+
if (!settings.all && hasMore)
|
|
35
|
+
renderer.note(more ? more(items) : `more — \`--page ${settings.page + 1}\` for the next`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (format !== "pretty") {
|
|
39
|
+
renderer.result({
|
|
40
|
+
items,
|
|
41
|
+
page: settings.all ? 1 : settings.page,
|
|
42
|
+
limit: settings.all ? items.length : settings.limit,
|
|
43
|
+
hasMore: settings.all ? false : hasMore,
|
|
44
|
+
});
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (view)
|
|
48
|
+
streams.data(view(items));
|
|
49
|
+
else
|
|
50
|
+
renderer.result(items);
|
|
51
|
+
if (!settings.all && hasMore)
|
|
52
|
+
renderer.note(more ? more(items) : `page ${settings.page} of more — \`--page ${settings.page + 1}\` for the next`);
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=paging.js.map
|