@leemour/max-cli 0.10.0 → 0.11.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 +24 -3
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +10 -4
- package/dist/cache/index.js.map +1 -1
- package/dist/client.d.ts +35 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +178 -44
- package/dist/client.js.map +1 -1
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +6 -2
- package/dist/commands/backup.js.map +1 -1
- package/dist/commands/cache.d.ts.map +1 -1
- package/dist/commands/cache.js +6 -5
- package/dist/commands/cache.js.map +1 -1
- package/dist/commands/chats.js +2 -2
- package/dist/commands/chats.js.map +1 -1
- package/dist/commands/complete.d.ts.map +1 -1
- package/dist/commands/complete.js +28 -22
- package/dist/commands/complete.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +5 -1
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/context.d.ts +4 -0
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +6 -14
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/doctor.js +51 -39
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/mcp.js +3 -3
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/messages.d.ts.map +1 -1
- package/dist/commands/messages.js +6 -4
- package/dist/commands/messages.js.map +1 -1
- package/dist/commands/models.d.ts.map +1 -1
- package/dist/commands/models.js +29 -23
- package/dist/commands/models.js.map +1 -1
- package/dist/commands/recipients.d.ts.map +1 -1
- package/dist/commands/recipients.js +25 -17
- package/dist/commands/recipients.js.map +1 -1
- package/dist/commands/sends.d.ts.map +1 -1
- package/dist/commands/sends.js +8 -6
- package/dist/commands/sends.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +43 -35
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +6 -10
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/watch.d.ts +17 -0
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +73 -38
- package/dist/commands/watch.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +18 -2
- package/dist/config.js.map +1 -1
- package/dist/diagnose.d.ts +4 -3
- package/dist/diagnose.d.ts.map +1 -1
- package/dist/diagnose.js +4 -4
- package/dist/diagnose.js.map +1 -1
- package/dist/domain/models.d.ts +20 -0
- package/dist/domain/models.d.ts.map +1 -1
- package/dist/domain/models.js.map +1 -1
- package/dist/download.d.ts +22 -3
- package/dist/download.d.ts.map +1 -1
- package/dist/download.js +133 -27
- package/dist/download.js.map +1 -1
- package/dist/export.d.ts +3 -0
- package/dist/export.d.ts.map +1 -1
- package/dist/export.js +13 -9
- package/dist/export.js.map +1 -1
- package/dist/generated/client.generated.d.ts +9 -0
- package/dist/generated/client.generated.d.ts.map +1 -1
- package/dist/generated/client.generated.js +9 -0
- package/dist/generated/client.generated.js.map +1 -1
- package/dist/generated/opcodes.generated.d.ts +3 -0
- package/dist/generated/opcodes.generated.d.ts.map +1 -1
- package/dist/generated/opcodes.generated.js +3 -0
- package/dist/generated/opcodes.generated.js.map +1 -1
- package/dist/generated/operations.generated.d.ts +20 -0
- package/dist/generated/operations.generated.d.ts.map +1 -1
- package/dist/generated/operations.generated.js +6 -0
- package/dist/generated/operations.generated.js.map +1 -1
- package/dist/mcp/confirm.d.ts +17 -9
- package/dist/mcp/confirm.d.ts.map +1 -1
- package/dist/mcp/confirm.js +53 -29
- package/dist/mcp/confirm.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +9 -4
- package/dist/mcp/tools.js.map +1 -1
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +24 -2
- package/dist/output.js.map +1 -1
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +51 -5
- package/dist/program.js.map +1 -1
- package/dist/protocol/connection.d.ts +2 -0
- package/dist/protocol/connection.d.ts.map +1 -1
- package/dist/protocol/connection.js +27 -5
- package/dist/protocol/connection.js.map +1 -1
- package/dist/protocol/frame.d.ts +2 -0
- package/dist/protocol/frame.d.ts.map +1 -1
- package/dist/protocol/frame.js +3 -1
- package/dist/protocol/frame.js.map +1 -1
- package/dist/protocol/lz4.js +8 -5
- package/dist/protocol/lz4.js.map +1 -1
- package/dist/rendering/messages.js +9 -10
- package/dist/rendering/messages.js.map +1 -1
- package/dist/report.d.ts +10 -6
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +45 -11
- package/dist/report.js.map +1 -1
- package/dist/resolve.d.ts +4 -3
- package/dist/resolve.d.ts.map +1 -1
- package/dist/resolve.js +9 -10
- package/dist/resolve.js.map +1 -1
- package/dist/runs/recording.d.ts +1 -0
- package/dist/runs/recording.d.ts.map +1 -1
- package/dist/runs/recording.js +13 -0
- package/dist/runs/recording.js.map +1 -1
- package/dist/sends/guard.d.ts +35 -4
- package/dist/sends/guard.d.ts.map +1 -1
- package/dist/sends/guard.js +128 -28
- package/dist/sends/guard.js.map +1 -1
- package/dist/sends/journal.d.ts +17 -3
- package/dist/sends/journal.d.ts.map +1 -1
- package/dist/sends/journal.js +62 -2
- package/dist/sends/journal.js.map +1 -1
- package/dist/sends/recipients.d.ts +3 -1
- package/dist/sends/recipients.d.ts.map +1 -1
- package/dist/sends/recipients.js +7 -2
- package/dist/sends/recipients.js.map +1 -1
- package/dist/server/lines.d.ts +8 -2
- package/dist/server/lines.d.ts.map +1 -1
- package/dist/server/lines.js +13 -2
- package/dist/server/lines.js.map +1 -1
- package/dist/server/server-connection.d.ts +9 -6
- package/dist/server/server-connection.d.ts.map +1 -1
- package/dist/server/server-connection.js +16 -7
- package/dist/server/server-connection.js.map +1 -1
- package/dist/server/server.d.ts +11 -4
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +116 -14
- package/dist/server/server.js.map +1 -1
- package/dist/server/start.d.ts +2 -0
- package/dist/server/start.d.ts.map +1 -1
- package/dist/server/start.js +6 -1
- package/dist/server/start.js.map +1 -1
- package/dist/session/handshake.d.ts +12 -1
- package/dist/session/handshake.d.ts.map +1 -1
- package/dist/session/handshake.js +4 -2
- package/dist/session/handshake.js.map +1 -1
- package/dist/session/prompt.d.ts.map +1 -1
- package/dist/session/prompt.js +7 -1
- package/dist/session/prompt.js.map +1 -1
- package/dist/session/store.d.ts +3 -1
- package/dist/session/store.d.ts.map +1 -1
- package/dist/session/store.js +5 -1
- package/dist/session/store.js.map +1 -1
- package/dist/spec/define.d.ts +12 -0
- package/dist/spec/define.d.ts.map +1 -1
- package/dist/spec/define.js.map +1 -1
- package/dist/spec/guards.d.ts +16 -0
- package/dist/spec/guards.d.ts.map +1 -0
- package/dist/spec/guards.js +30 -0
- package/dist/spec/guards.js.map +1 -0
- package/dist/spec/index.d.ts.map +1 -1
- package/dist/spec/index.js +6 -0
- package/dist/spec/index.js.map +1 -1
- package/dist/spec/operations/account.d.ts.map +1 -1
- package/dist/spec/operations/account.js +3 -0
- package/dist/spec/operations/account.js.map +1 -1
- package/dist/spec/operations/assets.d.ts +10 -0
- package/dist/spec/operations/assets.d.ts.map +1 -0
- package/dist/spec/operations/assets.js +20 -0
- package/dist/spec/operations/assets.js.map +1 -0
- package/dist/spec/operations/attachments.d.ts.map +1 -1
- package/dist/spec/operations/attachments.js +2 -0
- package/dist/spec/operations/attachments.js.map +1 -1
- package/dist/spec/operations/banners.d.ts +7 -0
- package/dist/spec/operations/banners.d.ts.map +1 -0
- package/dist/spec/operations/banners.js +16 -0
- package/dist/spec/operations/banners.js.map +1 -0
- package/dist/spec/operations/calls.d.ts +8 -0
- package/dist/spec/operations/calls.d.ts.map +1 -0
- package/dist/spec/operations/calls.js +18 -0
- package/dist/spec/operations/calls.js.map +1 -0
- package/dist/spec/operations/chats.d.ts.map +1 -1
- package/dist/spec/operations/chats.js +46 -0
- package/dist/spec/operations/chats.js.map +1 -1
- package/dist/spec/operations/contacts.d.ts.map +1 -1
- package/dist/spec/operations/contacts.js +8 -0
- package/dist/spec/operations/contacts.js.map +1 -1
- package/dist/spec/operations/folders.d.ts.map +1 -1
- package/dist/spec/operations/folders.js +5 -0
- package/dist/spec/operations/folders.js.map +1 -1
- package/dist/spec/operations/login.d.ts.map +1 -1
- package/dist/spec/operations/login.js +6 -0
- package/dist/spec/operations/login.js.map +1 -1
- package/dist/spec/operations/messages.d.ts.map +1 -1
- package/dist/spec/operations/messages.js +50 -0
- package/dist/spec/operations/messages.js.map +1 -1
- package/dist/spec/operations/session.d.ts +5 -1
- package/dist/spec/operations/session.d.ts.map +1 -1
- package/dist/spec/operations/session.js +9 -1
- package/dist/spec/operations/session.js.map +1 -1
- package/dist/spec/operations/uploads.d.ts.map +1 -1
- package/dist/spec/operations/uploads.js +2 -0
- package/dist/spec/operations/uploads.js.map +1 -1
- package/dist/transcribe/index.d.ts.map +1 -1
- package/dist/transcribe/index.js +4 -1
- package/dist/transcribe/index.js.map +1 -1
- package/dist/transcribe/install.d.ts +3 -1
- package/dist/transcribe/install.d.ts.map +1 -1
- package/dist/transcribe/install.js +24 -1
- package/dist/transcribe/install.js.map +1 -1
- package/dist/upload.d.ts +3 -1
- package/dist/upload.d.ts.map +1 -1
- package/dist/upload.js +34 -4
- package/dist/upload.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +11 -11
- package/skills/max-cli/SKILL.md +6 -2
- package/dist/testing/mock-max.d.ts +0 -49
- package/dist/testing/mock-max.d.ts.map +0 -1
- package/dist/testing/mock-max.js +0 -80
- package/dist/testing/mock-max.js.map +0 -1
- package/dist/testing/sandbox.d.ts +0 -2
- package/dist/testing/sandbox.d.ts.map +0 -1
- package/dist/testing/sandbox.js +0 -36
- package/dist/testing/sandbox.js.map +0 -1
- package/dist/testing/unscripted.d.ts +0 -2
- package/dist/testing/unscripted.d.ts.map +0 -1
- package/dist/testing/unscripted.js +0 -18
- package/dist/testing/unscripted.js.map +0 -1
package/dist/sends/guard.js
CHANGED
|
@@ -1,21 +1,38 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { CliError } from "@leemour/cli-core";
|
|
3
|
+
import { SendJournal, sendsPathFor, } from "./journal.js";
|
|
2
4
|
import { permissionFor } from "./permissions.js";
|
|
5
|
+
import { RecipientList, recipientsPathFor } from "./recipients.js";
|
|
3
6
|
const HOUR_MS = 60 * 60 * 1000;
|
|
4
7
|
/**
|
|
5
|
-
* What puts
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
8
|
+
* What puts something in front of somebody (`NEED-168`, widened by `NEED-282`). A message, a
|
|
9
|
+
* forward, an edit, a pin that notifies, a new group and people added to one all do. A reaction,
|
|
10
|
+
* a quiet pin or a change of title wakes nobody. A deletion wakes nobody either, but many at once
|
|
11
|
+
* is what a ban for automation looks like (`MAX-47`).
|
|
9
12
|
*/
|
|
10
|
-
const countsTowardLimit = ({ kind = "message", action }) => kind === "message" ||
|
|
11
|
-
|
|
13
|
+
const countsTowardLimit = ({ kind = "message", action, notify }) => kind === "message" ||
|
|
14
|
+
kind === "forward" ||
|
|
15
|
+
kind === "edit" ||
|
|
16
|
+
kind === "delete" ||
|
|
17
|
+
(kind === "pin" && notify === true) ||
|
|
18
|
+
action === "create" ||
|
|
19
|
+
action === "members.add";
|
|
20
|
+
/** Each deleted message, and each person added to a group, counts as one. */
|
|
21
|
+
const weightOf = ({ kind, action, count, people }) => kind === "delete"
|
|
22
|
+
? (count ?? 1)
|
|
23
|
+
: action === "create" || action === "members.add"
|
|
24
|
+
? Math.max(1, people ?? count ?? 1)
|
|
25
|
+
: 1;
|
|
26
|
+
/** A scheduled message counts in the hour MAX sends it. */
|
|
27
|
+
const timeOf = (entry) => Date.parse(entry.scheduledFor ?? entry.at);
|
|
12
28
|
/**
|
|
13
29
|
* These stop a model that was talked into sending by a message it read. They do **not** stop an
|
|
14
30
|
* agent that edits the configuration itself — that needs a boundary outside this process
|
|
15
31
|
* (`docs/security.md`).
|
|
16
32
|
*/
|
|
17
|
-
export const sendGuard = ({ profile, readOnly, readOnlyFrom, allow, allowFrom = "default", sendsPerHour, journal, recipients, warn, now = () => new Date(), }) =>
|
|
18
|
-
|
|
33
|
+
export const sendGuard = ({ profile, readOnly, readOnlyFrom, allow, allowFrom = "default", sendsPerHour, journal, recipients, warn, now = () => new Date(), }) => {
|
|
34
|
+
let reservation;
|
|
35
|
+
const permitted = ({ chatId, kind = "message", action, personIds }) => {
|
|
19
36
|
if (readOnly) {
|
|
20
37
|
throw new CliError("permission_error", `profile ${profile} is read-only (readOnly, from the ${readOnlyFrom}) — it cannot send, react, change chats or change the account`);
|
|
21
38
|
}
|
|
@@ -30,19 +47,37 @@ export const sendGuard = ({ profile, readOnly, readOnlyFrom, allow, allowFrom =
|
|
|
30
47
|
throw new CliError("confirmation_required", `chat ${chatId} is not on the recipient list of profile ${profile} — ` +
|
|
31
48
|
`the owner adds it with \`max ${profile} recipients add ${chatId}\``, { chatId });
|
|
32
49
|
}
|
|
33
|
-
|
|
34
|
-
|
|
50
|
+
// Adding somebody to a group writes to them as surely as a message does.
|
|
51
|
+
const strangers = allowed ? (personIds ?? []).filter((id) => !allowed.some((chat) => chat.partnerId === id)) : [];
|
|
52
|
+
if (strangers.length > 0) {
|
|
53
|
+
throw new CliError("confirmation_required", `${strangers.join(", ")} ${strangers.length === 1 ? "is" : "are"} not on the recipient list of profile ${profile} — ` +
|
|
54
|
+
`the owner adds the one-to-one chat with each person (\`max ${profile} recipients add <chat>\`); ` +
|
|
55
|
+
"a chat added to the list earlier is added again to be recognised", { personIds: strangers });
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const withinLimit = (request, entries) => {
|
|
59
|
+
const { chatId, cid } = request;
|
|
60
|
+
const asked = { ...request, people: request.personIds?.length ?? request.count };
|
|
61
|
+
const count = weightOf(asked);
|
|
35
62
|
if (count > sendsPerHour) {
|
|
36
63
|
throw new CliError("rate_limited", `${count} at once is more than the hourly limit of profile ${profile} (sendsPerHour ${sendsPerHour}) — ` +
|
|
37
64
|
"do fewer at a time");
|
|
38
65
|
}
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
.entries()
|
|
66
|
+
const at = request.scheduledFor ? Date.parse(request.scheduledFor) : now().getTime();
|
|
67
|
+
const counted = entries
|
|
42
68
|
.filter(countsTowardLimit)
|
|
43
|
-
.filter((entry) => entry.outcome === "sent" || entry.outcome === "outcome_unknown")
|
|
44
|
-
.
|
|
45
|
-
|
|
69
|
+
.filter((entry) => entry.outcome === "sent" || entry.outcome === "outcome_unknown" || entry.outcome === "reserved")
|
|
70
|
+
.filter((entry) => Math.abs(timeOf(entry) - at) < HOUR_MS);
|
|
71
|
+
// A retry of a send whose outcome was unknown repeats its `cid` in the same chat, and MAX
|
|
72
|
+
// delivers one message for both. Only that: a `cid` that was sent, or another chat, is a new send.
|
|
73
|
+
const unsure = (entry) => entry.outcome === "outcome_unknown";
|
|
74
|
+
const same = (a, b) => a.cid !== undefined && a.cid === b.cid && a.chatId === b.chatId;
|
|
75
|
+
const last = [...counted].reverse().find((entry) => same(entry, { chatId, cid }));
|
|
76
|
+
if (cid !== undefined && last && unsure(last))
|
|
77
|
+
return false;
|
|
78
|
+
const recent = counted
|
|
79
|
+
.filter((entry, index) => !(unsure(entry) && counted.slice(index + 1).some((later) => same(later, entry))))
|
|
80
|
+
.map((entry) => ({ time: timeOf(entry), weight: weightOf(entry) }))
|
|
46
81
|
.sort((a, b) => a.time - b.time);
|
|
47
82
|
const used = recent.reduce((sum, { weight }) => sum + weight, 0);
|
|
48
83
|
if (used + count > sendsPerHour) {
|
|
@@ -55,19 +90,84 @@ export const sendGuard = ({ profile, readOnly, readOnlyFrom, allow, allowFrom =
|
|
|
55
90
|
});
|
|
56
91
|
const nextMs = (opening?.time ?? 0) + HOUR_MS;
|
|
57
92
|
const next = new Date(nextMs).toISOString();
|
|
58
|
-
throw new CliError("rate_limited", `profile ${profile} has sent ${used} messages in the
|
|
93
|
+
throw new CliError("rate_limited", `profile ${profile} has sent ${used} messages in the hour around ${new Date(at).toISOString()}, and its limit is ` +
|
|
59
94
|
`${sendsPerHour} (sendsPerHour) — the next send is possible at ${next}`, { retryAfterMs: nextMs - now().getTime(), retryAt: next });
|
|
60
95
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
96
|
+
return true;
|
|
97
|
+
};
|
|
98
|
+
return {
|
|
99
|
+
check: (request, { reserve = true } = {}) => {
|
|
100
|
+
permitted(request);
|
|
101
|
+
const asked = { ...request, people: request.personIds?.length ?? request.count };
|
|
102
|
+
if (!countsTowardLimit(asked))
|
|
103
|
+
return;
|
|
104
|
+
if (!reserve) {
|
|
105
|
+
withinLimit(request, journal.entries());
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
journal.locked(() => {
|
|
109
|
+
if (!withinLimit(request, journal.entries()))
|
|
110
|
+
return;
|
|
111
|
+
const id = randomUUID();
|
|
112
|
+
const { chatId, kind, action, cid, scheduledFor, notify } = request;
|
|
113
|
+
journal.append({
|
|
114
|
+
at: now().toISOString(),
|
|
115
|
+
profile,
|
|
116
|
+
chatId,
|
|
117
|
+
outcome: "reserved",
|
|
118
|
+
reservation: id,
|
|
119
|
+
...(kind ? { kind } : {}),
|
|
120
|
+
...(action ? { action } : {}),
|
|
121
|
+
...(asked.people === undefined ? {} : kind === "delete" ? { count: asked.people } : { people: asked.people }),
|
|
122
|
+
...(cid === undefined ? {} : { cid }),
|
|
123
|
+
...(scheduledFor ? { scheduledFor } : {}),
|
|
124
|
+
...(notify === undefined ? {} : { notify }),
|
|
125
|
+
});
|
|
126
|
+
reservation = id;
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
// After a send, a failure to write here must not become the command's answer: the message is
|
|
130
|
+
// already with a person, and an error would invite the caller to send it again.
|
|
131
|
+
record: (entry) => {
|
|
132
|
+
const { personIds: _, ...kept } = entry;
|
|
133
|
+
const settles = entry.outcome === "refused" ? undefined : reservation;
|
|
134
|
+
reservation = undefined;
|
|
135
|
+
try {
|
|
136
|
+
journal.append({ at: now().toISOString(), profile, ...kept, ...(settles ? { reservation: settles } : {}) });
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
warn(`this send is not in the send journal: ${error instanceof Error ? error.message : String(error)}`);
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* The guard a profile's configuration asks for — the command's, and `max serve`'s for every write
|
|
146
|
+
* it forwards. Built per request in the server, so `config set readOnly true` needs no restart.
|
|
147
|
+
*/
|
|
148
|
+
export const guardFor = (settings, warn) => sendGuard({
|
|
149
|
+
profile: settings.profile,
|
|
150
|
+
readOnly: settings.readOnly,
|
|
151
|
+
readOnlyFrom: settings.sources.readOnly,
|
|
152
|
+
...(settings.allow ? { allow: settings.allow, allowFrom: settings.sources.allow } : {}),
|
|
153
|
+
sendsPerHour: settings.sendsPerHour,
|
|
154
|
+
journal: new SendJournal(sendsPathFor(settings.profile)),
|
|
155
|
+
recipients: new RecipientList(recipientsPathFor(settings.profile)),
|
|
156
|
+
warn,
|
|
72
157
|
});
|
|
158
|
+
/**
|
|
159
|
+
* Over `max serve`, the server journals what it forwards, with the outcome it saw; the command
|
|
160
|
+
* writes only the refusals of its own check, which never reached the server.
|
|
161
|
+
*/
|
|
162
|
+
export const sharedJournal = (guard, wire) => wire
|
|
163
|
+
? {
|
|
164
|
+
// The check comes before the connection, when `journals` cannot tell yet; the server
|
|
165
|
+
// reserves under its own lock, and a command that falls back to its own socket goes unreserved.
|
|
166
|
+
check: (request) => guard.check(request, { reserve: false }),
|
|
167
|
+
record: (entry) => {
|
|
168
|
+
if (entry.outcome === "refused" || !wire.journals)
|
|
169
|
+
guard.record(entry);
|
|
170
|
+
},
|
|
171
|
+
}
|
|
172
|
+
: guard;
|
|
73
173
|
//# sourceMappingURL=guard.js.map
|
package/dist/sends/guard.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../../src/sends/guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAG5C,OAAO,EAAmB,aAAa,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../../src/sends/guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAG5C,OAAO,EAIL,WAAW,EAEX,YAAY,GACb,MAAM,cAAc,CAAA;AACrB,OAAO,EAAmB,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACjE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAElE,MAAM,OAAO,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AA2C9B;;;;;GAKG;AACH,MAAM,iBAAiB,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,EAAW,EAAE,EAAE,CAC1E,IAAI,KAAK,SAAS;IAClB,IAAI,KAAK,SAAS;IAClB,IAAI,KAAK,MAAM;IACf,IAAI,KAAK,QAAQ;IACjB,CAAC,IAAI,KAAK,KAAK,IAAI,MAAM,KAAK,IAAI,CAAC;IACnC,MAAM,KAAK,QAAQ;IACnB,MAAM,KAAK,aAAa,CAAA;AAE1B,6EAA6E;AAC7E,MAAM,QAAQ,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAW,EAAU,EAAE,CACpE,IAAI,KAAK,QAAQ;IACf,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IACd,CAAC,CAAC,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,aAAa;QAC/C,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC,CAAA;AAET,2DAA2D;AAC3D,MAAM,MAAM,GAAG,CAAC,KAA6C,EAAU,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,EAAE,CAAC,CAAA;AAEpH;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,EACxB,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,KAAK,EACL,SAAS,GAAG,SAAS,EACrB,YAAY,EACZ,OAAO,EACP,UAAU,EACV,IAAI,EACJ,GAAG,GAAG,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,GACL,EAAa,EAAE;IAChC,IAAI,WAA+B,CAAA;IAEnC,MAAM,SAAS,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,GAAG,SAAS,EAAE,MAAM,EAAE,SAAS,EAAgB,EAAE,EAAE;QAClF,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,IAAI,QAAQ,CAChB,kBAAkB,EAClB,WAAW,OAAO,qCAAqC,YAAY,+DAA+D,CACnI,CAAA;QACH,CAAC;QAED,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QAC9C,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YACzC,MAAM,OAAO,GACX,SAAS,KAAK,iBAAiB,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAC,CAAC,OAAO,OAAO,mBAAmB,CAAA;YACzG,MAAM,IAAI,QAAQ,CAChB,kBAAkB,EAClB,WAAW,OAAO,mBAAmB,UAAU,YAAY,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS,eAAe,SAAS,KAAK;gBACnH,gBAAgB,OAAO,IAAI,CAAC,GAAG,KAAK,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAC/D,EAAE,UAAU,EAAE,CACf,CAAA;QACH,CAAC;QAED,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,CAAA;QACjC,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC;YAC9E,MAAM,IAAI,QAAQ,CAChB,uBAAuB,EACvB,QAAQ,MAAM,4CAA4C,OAAO,KAAK;gBACpE,gCAAgC,OAAO,mBAAmB,MAAM,IAAI,EACtE,EAAE,MAAM,EAAE,CACX,CAAA;QACH,CAAC;QACD,yEAAyE;QACzE,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QACjH,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,QAAQ,CAChB,uBAAuB,EACvB,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,yCAAyC,OAAO,KAAK;gBACnH,8DAA8D,OAAO,6BAA6B;gBAClG,kEAAkE,EACpE,EAAE,SAAS,EAAE,SAAS,EAAE,CACzB,CAAA;QACH,CAAC;IACH,CAAC,CAAA;IAED,MAAM,WAAW,GAAG,CAAC,OAAqB,EAAE,OAAoB,EAAE,EAAE;QAClE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAA;QAC/B,MAAM,KAAK,GAAG,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,SAAS,EAAE,MAAM,IAAI,OAAO,CAAC,KAAK,EAAE,CAAA;QAChF,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;QAC7B,IAAI,KAAK,GAAG,YAAY,EAAE,CAAC;YACzB,MAAM,IAAI,QAAQ,CAChB,cAAc,EACd,GAAG,KAAK,qDAAqD,OAAO,kBAAkB,YAAY,MAAM;gBACtG,oBAAoB,CACvB,CAAA;QACH,CAAC;QAED,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAA;QACpF,MAAM,OAAO,GAAG,OAAO;aACpB,MAAM,CAAC,iBAAiB,CAAC;aACzB,MAAM,CACL,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,MAAM,IAAI,KAAK,CAAC,OAAO,KAAK,iBAAiB,IAAI,KAAK,CAAC,OAAO,KAAK,UAAU,CAC3G;aACA,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAA;QAC5D,0FAA0F;QAC1F,mGAAmG;QACnG,MAAM,MAAM,GAAG,CAAC,KAAgB,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,iBAAiB,CAAA;QACxE,MAAM,IAAI,GAAG,CAAC,CAAY,EAAE,CAAsC,EAAE,EAAE,CACpE,CAAC,CAAC,GAAG,KAAK,SAAS,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,CAAA;QACjE,MAAM,IAAI,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAA;QACjF,IAAI,GAAG,KAAK,SAAS,IAAI,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAA;QAC3D,MAAM,MAAM,GAAG,OAAO;aACnB,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;aAC1G,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;aAClE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAA;QAClC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,EAAE,CAAC,CAAC,CAAA;QAChE,IAAI,IAAI,GAAG,KAAK,GAAG,YAAY,EAAE,CAAC;YAChC,wFAAwF;YACxF,sDAAsD;YACtD,IAAI,KAAK,GAAG,CAAC,CAAA;YACb,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE;gBACzC,KAAK,IAAI,MAAM,CAAA;gBACf,OAAO,IAAI,GAAG,KAAK,GAAG,KAAK,IAAI,YAAY,CAAA;YAC7C,CAAC,CAAC,CAAA;YACF,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,CAAC,GAAG,OAAO,CAAA;YAC7C,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAA;YAC3C,MAAM,IAAI,QAAQ,CAChB,cAAc,EACd,WAAW,OAAO,aAAa,IAAI,gCAAgC,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,qBAAqB;gBAChH,GAAG,YAAY,kDAAkD,IAAI,EAAE,EACzE,EAAE,YAAY,EAAE,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAC1D,CAAA;QACH,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC,CAAA;IAED,OAAO;QACL,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,OAAO,GAAG,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE;YAC1C,SAAS,CAAC,OAAO,CAAC,CAAA;YAClB,MAAM,KAAK,GAAG,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,SAAS,EAAE,MAAM,IAAI,OAAO,CAAC,KAAK,EAAE,CAAA;YAChF,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC;gBAAE,OAAM;YACrC,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;gBACvC,OAAM;YACR,CAAC;YACD,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE;gBAClB,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;oBAAE,OAAM;gBACpD,MAAM,EAAE,GAAG,UAAU,EAAE,CAAA;gBACvB,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAAA;gBACnE,OAAO,CAAC,MAAM,CAAC;oBACb,EAAE,EAAE,GAAG,EAAE,CAAC,WAAW,EAAE;oBACvB,OAAO;oBACP,MAAM;oBACN,OAAO,EAAE,UAAU;oBACnB,WAAW,EAAE,EAAE;oBACf,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACzB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC7B,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;oBAC7G,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;oBACrC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACzC,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;iBAC5C,CAAC,CAAA;gBACF,WAAW,GAAG,EAAE,CAAA;YAClB,CAAC,CAAC,CAAA;QACJ,CAAC;QAED,6FAA6F;QAC7F,gFAAgF;QAChF,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAChB,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,KAA4C,CAAA;YAC9E,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAA;YACrE,WAAW,GAAG,SAAS,CAAA;YACvB,IAAI,CAAC;gBACH,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;YAC7G,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,yCAAyC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YACzG,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,QAAkB,EAAE,IAA+B,EAAa,EAAE,CACzF,SAAS,CAAC;IACR,OAAO,EAAE,QAAQ,CAAC,OAAO;IACzB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;IAC3B,YAAY,EAAE,QAAQ,CAAC,OAAO,CAAC,QAAQ;IACvC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACvF,YAAY,EAAE,QAAQ,CAAC,YAAY;IACnC,OAAO,EAAE,IAAI,WAAW,CAAC,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACxD,UAAU,EAAE,IAAI,aAAa,CAAC,iBAAiB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAClE,IAAI;CACL,CAAC,CAAA;AAEJ;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAgB,EAAE,IAAgD,EAAa,EAAE,CAC7G,IAAI;IACF,CAAC,CAAC;QACE,qFAAqF;QACrF,gGAAgG;QAChG,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC5D,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAChB,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,QAAQ;gBAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACxE,CAAC;KACF;IACH,CAAC,CAAC,KAAK,CAAA"}
|
package/dist/sends/journal.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Id } from "../domain/models.js";
|
|
2
|
-
|
|
2
|
+
/** `reserved` holds a place under the hourly limit while the write is on its way; its outcome follows. */
|
|
3
|
+
export type SendOutcome = "sent" | "outcome_unknown" | "refused" | "failed" | "reserved";
|
|
3
4
|
/** Absent in the journal means a message: that is every line written before reactions were guarded. */
|
|
4
5
|
export type SendKind = "message" | "reaction" | "edit" | "forward" | "pin" | "read" | "delete" | "chat" | "account";
|
|
5
6
|
/** What a `chat` entry did. Never a title, a description or a link — only which action. */
|
|
@@ -28,8 +29,12 @@ export interface SendEntry {
|
|
|
28
29
|
kind: "photo" | "file";
|
|
29
30
|
bytes: number;
|
|
30
31
|
}[];
|
|
31
|
-
/** When MAX will send it;
|
|
32
|
+
/** When MAX will send it; it counts toward the limit of that hour, not of the hour it was queued. */
|
|
32
33
|
scheduledFor?: string;
|
|
34
|
+
/** Whether a pin told the members. */
|
|
35
|
+
notify?: boolean;
|
|
36
|
+
/** Pairs an outcome with the `reserved` line it settles. */
|
|
37
|
+
reservation?: string;
|
|
33
38
|
errorCode?: string;
|
|
34
39
|
}
|
|
35
40
|
/**
|
|
@@ -38,10 +43,19 @@ export interface SendEntry {
|
|
|
38
43
|
*/
|
|
39
44
|
export declare const sendsPathFor: (profile: string, env?: NodeJS.ProcessEnv) => string;
|
|
40
45
|
export declare class SendJournal {
|
|
46
|
+
#private;
|
|
41
47
|
readonly path: string;
|
|
42
48
|
constructor(path: string);
|
|
49
|
+
/**
|
|
50
|
+
* Runs `body` with the journal to itself, across processes: two sends at the limit must not both
|
|
51
|
+
* read "one left". A lock file opened with `wx`, because `flock` is not there on Windows.
|
|
52
|
+
*/
|
|
53
|
+
locked<T>(body: () => T): T;
|
|
43
54
|
append(entry: SendEntry): void;
|
|
44
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Oldest first, each reservation folded into its outcome; one with no outcome yet is in flight,
|
|
57
|
+
* or its process died. A line that does not parse is skipped: a torn last write must not block sending.
|
|
58
|
+
*/
|
|
45
59
|
entries(): SendEntry[];
|
|
46
60
|
}
|
|
47
61
|
//# sourceMappingURL=journal.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../../src/sends/journal.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAA;AAE7C,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,iBAAiB,GAAG,SAAS,GAAG,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../../src/sends/journal.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAA;AAE7C,0GAA0G;AAC1G,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,iBAAiB,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,CAAA;AAExF,uGAAuG;AACvG,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAA;AAEnH,2FAA2F;AAC3F,MAAM,MAAM,UAAU,GAClB,QAAQ,GACR,MAAM,GACN,OAAO,GACP,aAAa,GACb,gBAAgB,GAChB,YAAY,GACZ,eAAe,GACf,QAAQ,GACR,UAAU,GACV,iBAAiB,GACjB,kBAAkB,GAClB,YAAY,CAAA;AAEhB,oGAAoG;AACpG,MAAM,MAAM,aAAa,GACrB,aAAa,GACb,gBAAgB,GAChB,gBAAgB,GAChB,SAAS,GACT,eAAe,GACf,eAAe,GACf,eAAe,GACf,cAAc,CAAA;AAElB,iEAAiE;AACjE,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,oHAAoH;IACpH,MAAM,EAAE,EAAE,GAAG,IAAI,CAAA;IACjB,OAAO,EAAE,WAAW,CAAA;IACpB,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,MAAM,CAAC,EAAE,UAAU,GAAG,aAAa,CAAA;IACnC,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,EAAE,CAAA;IACd,0FAA0F;IAC1F,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,+DAA+D;IAC/D,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACzD,qGAAqG;IACrG,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,sCAAsC;IACtC,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,YAAa,MAAM,QAAO,MAAM,CAAC,UAAU,KAAiB,MACc,CAAA;AAOnG,qBAAa,WAAW;;IACV,QAAQ,CAAC,IAAI,EAAE,MAAM;IAAjC,YAAqB,IAAI,EAAE,MAAM,EAAI;IAErC;;;OAGG;IACH,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CA4B1B;IAED,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAG7B;IAED;;;OAGG;IACH,OAAO,IAAI,SAAS,EAAE,CAMrB;CAoBF"}
|
package/dist/sends/journal.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { appendFileSync, mkdirSync, readFileSync } from "node:fs";
|
|
1
|
+
import { appendFileSync, closeSync, mkdirSync, openSync, readFileSync, statSync, unlinkSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { resolvePaths } from "@leemour/cli-core";
|
|
4
4
|
/**
|
|
@@ -6,17 +6,69 @@ import { resolvePaths } from "@leemour/cli-core";
|
|
|
6
6
|
* `max cache clear` cannot erase what was sent in the owner's name (the reason of `NEED-51`).
|
|
7
7
|
*/
|
|
8
8
|
export const sendsPathFor = (profile, env = process.env) => join(resolvePaths({ appName: "max-cli", prefix: "MAX", env }).state, "sends", `${profile}.jsonl`);
|
|
9
|
+
const LOCK_WAIT_MS = 5_000;
|
|
10
|
+
/** Longer than any check and append takes; a lock this old was left by a process that died holding it. */
|
|
11
|
+
const LOCK_STALE_MS = 30_000;
|
|
12
|
+
const pause = new Int32Array(new SharedArrayBuffer(4));
|
|
9
13
|
export class SendJournal {
|
|
10
14
|
path;
|
|
11
15
|
constructor(path) {
|
|
12
16
|
this.path = path;
|
|
13
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Runs `body` with the journal to itself, across processes: two sends at the limit must not both
|
|
20
|
+
* read "one left". A lock file opened with `wx`, because `flock` is not there on Windows.
|
|
21
|
+
*/
|
|
22
|
+
locked(body) {
|
|
23
|
+
mkdirSync(dirname(this.path), { recursive: true, mode: 0o700 });
|
|
24
|
+
const lock = `${this.path}.lock`;
|
|
25
|
+
const deadline = Date.now() + LOCK_WAIT_MS;
|
|
26
|
+
for (;;) {
|
|
27
|
+
try {
|
|
28
|
+
closeSync(openSync(lock, "wx", 0o600));
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (error.code !== "EEXIST")
|
|
33
|
+
throw error;
|
|
34
|
+
if (Date.now() - lockTime(lock) > LOCK_STALE_MS) {
|
|
35
|
+
try {
|
|
36
|
+
unlinkSync(lock);
|
|
37
|
+
}
|
|
38
|
+
catch { }
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (Date.now() > deadline)
|
|
42
|
+
throw new Error(`the send journal stayed locked for ${LOCK_WAIT_MS / 1000}s (${lock})`);
|
|
43
|
+
Atomics.wait(pause, 0, 0, 20);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
return body();
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
try {
|
|
51
|
+
unlinkSync(lock);
|
|
52
|
+
}
|
|
53
|
+
catch { }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
14
56
|
append(entry) {
|
|
15
57
|
mkdirSync(dirname(this.path), { recursive: true, mode: 0o700 });
|
|
16
58
|
appendFileSync(this.path, `${JSON.stringify(entry)}\n`, { mode: 0o600 });
|
|
17
59
|
}
|
|
18
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Oldest first, each reservation folded into its outcome; one with no outcome yet is in flight,
|
|
62
|
+
* or its process died. A line that does not parse is skipped: a torn last write must not block sending.
|
|
63
|
+
*/
|
|
19
64
|
entries() {
|
|
65
|
+
const lines = this.#lines();
|
|
66
|
+
const settled = new Set(lines.filter((entry) => entry.outcome !== "reserved").map((entry) => entry.reservation));
|
|
67
|
+
return lines
|
|
68
|
+
.filter((entry) => entry.outcome !== "reserved" || !settled.has(entry.reservation))
|
|
69
|
+
.map(({ reservation, ...entry }) => (entry.outcome === "reserved" ? { ...entry, reservation } : entry));
|
|
70
|
+
}
|
|
71
|
+
#lines() {
|
|
20
72
|
let text;
|
|
21
73
|
try {
|
|
22
74
|
text = readFileSync(this.path, "utf8");
|
|
@@ -37,4 +89,12 @@ export class SendJournal {
|
|
|
37
89
|
});
|
|
38
90
|
}
|
|
39
91
|
}
|
|
92
|
+
const lockTime = (lock) => {
|
|
93
|
+
try {
|
|
94
|
+
return statSync(lock).mtimeMs;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return Date.now();
|
|
98
|
+
}
|
|
99
|
+
};
|
|
40
100
|
//# sourceMappingURL=journal.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"journal.js","sourceRoot":"","sources":["../../src/sends/journal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"journal.js","sourceRoot":"","sources":["../../src/sends/journal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAC5G,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AA+DhD;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAe,EAAE,GAAG,GAAsB,OAAO,CAAC,GAAG,EAAU,EAAE,CAC5F,IAAI,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,QAAQ,CAAC,CAAA;AAEnG,MAAM,YAAY,GAAG,KAAK,CAAA;AAC1B,0GAA0G;AAC1G,MAAM,aAAa,GAAG,MAAM,CAAA;AAC5B,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,IAAI,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAA;AAEtD,MAAM,OAAO,WAAW;IACD,IAAI;IAAzB,YAAqB,IAAY;oBAAZ,IAAI;IAAW,CAAC;IAErC;;;OAGG;IACH,MAAM,CAAI,IAAa;QACrB,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QAC/D,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,IAAI,OAAO,CAAA;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,CAAA;QAC1C,SAAS,CAAC;YACR,IAAI,CAAC;gBACH,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAA;gBACtC,MAAK;YACP,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;oBAAE,MAAM,KAAK,CAAA;gBACnE,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,aAAa,EAAE,CAAC;oBAChD,IAAI,CAAC;wBACH,UAAU,CAAC,IAAI,CAAC,CAAA;oBAClB,CAAC;oBAAC,MAAM,CAAC,CAAA,CAAC;oBACV,SAAQ;gBACV,CAAC;gBACD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ;oBACvB,MAAM,IAAI,KAAK,CAAC,sCAAsC,YAAY,GAAG,IAAI,MAAM,IAAI,GAAG,CAAC,CAAA;gBACzF,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;YAC/B,CAAC;QACH,CAAC;QACD,IAAI,CAAC;YACH,OAAO,IAAI,EAAE,CAAA;QACf,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC;gBACH,UAAU,CAAC,IAAI,CAAC,CAAA;YAClB,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QACZ,CAAC;IACH,CAAC;IAED,MAAM,CAAC,KAAgB;QACrB,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QAC/D,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IAC1E,CAAC;IAED;;;OAGG;IACH,OAAO;QACL,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,CAAA;QAC3B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAA;QAChH,OAAO,KAAK;aACT,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,UAAU,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;aAClF,GAAG,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAA;IAC3G,CAAC;IAED,MAAM;QACJ,IAAI,IAAY,CAAA;QAChB,IAAI,CAAC;YACH,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAA;QACX,CAAC;QACD,OAAO,IAAI;aACR,KAAK,CAAC,IAAI,CAAC;aACX,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;aACpC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;YAChB,IAAI,CAAC;gBACH,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAc,CAAC,CAAA;YACxC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,EAAE,CAAA;YACX,CAAC;QACH,CAAC,CAAC,CAAA;IACN,CAAC;CACF;AAED,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAU,EAAE;IACxC,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,CAAA;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,GAAG,EAAE,CAAA;IACnB,CAAC;AACH,CAAC,CAAA"}
|
|
@@ -2,6 +2,8 @@ import type { Id } from "../domain/models.js";
|
|
|
2
2
|
export interface Recipient {
|
|
3
3
|
id: Id;
|
|
4
4
|
title: string | null;
|
|
5
|
+
/** The other person, for a one-to-one chat: who may be put in a group when the list is on. */
|
|
6
|
+
partnerId?: Id;
|
|
5
7
|
addedAt: string;
|
|
6
8
|
}
|
|
7
9
|
/**
|
|
@@ -19,7 +21,7 @@ export declare class RecipientList {
|
|
|
19
21
|
constructor(path: string);
|
|
20
22
|
/** `undefined` when the list is off. A file that cannot be read refuses rather than opening up. */
|
|
21
23
|
read(): Recipient[] | undefined;
|
|
22
|
-
/** Whether it was new. */
|
|
24
|
+
/** Whether it was new. Adding one again fills in a partner an older entry was stored without. */
|
|
23
25
|
add(recipient: Recipient): boolean;
|
|
24
26
|
/** The id or the title as it was stored; `undefined` when neither matched. */
|
|
25
27
|
remove(reference: string): Recipient | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recipients.d.ts","sourceRoot":"","sources":["../../src/sends/recipients.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAA;AAE7C,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,EAAE,CAAA;IACN,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,YAAa,MAAM,QAAO,MAAM,CAAC,UAAU,KAAiB,MACsB,CAAA;AAEhH;;;GAGG;AACH,qBAAa,aAAa;;IACZ,QAAQ,CAAC,IAAI,EAAE,MAAM;IAAjC,YAAqB,IAAI,EAAE,MAAM,EAAI;IAErC,mGAAmG;IACnG,IAAI,IAAI,SAAS,EAAE,GAAG,SAAS,CAa9B;IAED,
|
|
1
|
+
{"version":3,"file":"recipients.d.ts","sourceRoot":"","sources":["../../src/sends/recipients.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAA;AAE7C,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,EAAE,CAAA;IACN,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,8FAA8F;IAC9F,SAAS,CAAC,EAAE,EAAE,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,YAAa,MAAM,QAAO,MAAM,CAAC,UAAU,KAAiB,MACsB,CAAA;AAEhH;;;GAGG;AACH,qBAAa,aAAa;;IACZ,QAAQ,CAAC,IAAI,EAAE,MAAM;IAAjC,YAAqB,IAAI,EAAE,MAAM,EAAI;IAErC,mGAAmG;IACnG,IAAI,IAAI,SAAS,EAAE,GAAG,SAAS,CAa9B;IAED,iGAAiG;IACjG,GAAG,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAWjC;IAED,8EAA8E;IAC9E,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAQ/C;IAED,4CAA4C;IAC5C,GAAG,IAAI,OAAO,CAIb;CAKF"}
|
package/dist/sends/recipients.js
CHANGED
|
@@ -30,11 +30,16 @@ export class RecipientList {
|
|
|
30
30
|
"fix the file or turn the list off with `max recipients off`");
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
/** Whether it was new. */
|
|
33
|
+
/** Whether it was new. Adding one again fills in a partner an older entry was stored without. */
|
|
34
34
|
add(recipient) {
|
|
35
35
|
const chats = this.read() ?? [];
|
|
36
|
-
|
|
36
|
+
const known = chats.find((chat) => chat.id === recipient.id);
|
|
37
|
+
if (known) {
|
|
38
|
+
if (recipient.partnerId && !known.partnerId) {
|
|
39
|
+
this.#write(chats.map((chat) => (chat === known ? { ...known, partnerId: recipient.partnerId } : chat)));
|
|
40
|
+
}
|
|
37
41
|
return false;
|
|
42
|
+
}
|
|
38
43
|
this.#write([...chats, recipient]);
|
|
39
44
|
return true;
|
|
40
45
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recipients.js","sourceRoot":"","sources":["../../src/sends/recipients.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"recipients.js","sourceRoot":"","sources":["../../src/sends/recipients.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAWzE;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,OAAe,EAAE,GAAG,GAAsB,OAAO,CAAC,GAAG,EAAU,EAAE,CACjG,IAAI,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,OAAO,kBAAkB,CAAC,CAAA;AAEhH;;;GAGG;AACH,MAAM,OAAO,aAAa;IACH,IAAI;IAAzB,YAAqB,IAAY;oBAAZ,IAAI;IAAW,CAAC;IAErC,mGAAmG;IACnG,IAAI;QACF,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,SAAS,CAAA;QAC5C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAwB,CAAA;YACjF,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAA;YACrE,OAAO,MAAM,CAAC,KAAoB,CAAA;QACpC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,QAAQ,CAChB,qBAAqB,EACrB,sBAAsB,IAAI,CAAC,IAAI,oBAAoB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM;gBAC7G,6DAA6D,CAChE,CAAA;QACH,CAAC;IACH,CAAC;IAED,iGAAiG;IACjG,GAAG,CAAC,SAAoB;QACtB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAA;QAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,CAAC,CAAA;QAC5D,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,SAAS,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;gBAC5C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YAC1G,CAAC;YACD,OAAO,KAAK,CAAA;QACd,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,EAAE,SAAS,CAAC,CAAC,CAAA;QAClC,OAAO,IAAI,CAAA;IACb,CAAC;IAED,8EAA8E;IAC9E,MAAM,CAAC,SAAiB;QACtB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAA;QAC/B,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,CAAA;QAC/B,MAAM,IAAI,GACR,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC;YACxC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,iBAAiB,EAAE,KAAK,MAAM,CAAC,iBAAiB,EAAE,CAAC,CAAA;QACtF,IAAI,IAAI;YAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAA;QAC5D,OAAO,IAAI,CAAA;IACb,CAAC;IAED,4CAA4C;IAC5C,GAAG;QACD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAA;QACxC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACjB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,MAAM,CAAC,KAAkB;QACvB,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC5E,CAAC;CACF"}
|
package/dist/server/lines.d.ts
CHANGED
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export declare const toLine: (value: unknown) => string;
|
|
10
10
|
export declare const fromLine: (line: string) => Record<string, unknown>;
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Calls `onLine` for each complete line `data` finishes, keeping the rest for next time. With
|
|
13
|
+
* `maxLength`, a line that grows past it calls `onTooLong` instead and nothing more is read.
|
|
14
|
+
*/
|
|
15
|
+
export declare const lineReader: (onLine: (line: string) => void, { maxLength, onTooLong }?: {
|
|
16
|
+
maxLength?: number;
|
|
17
|
+
onTooLong?: () => void;
|
|
18
|
+
}) => (data: Buffer | string) => void;
|
|
13
19
|
//# sourceMappingURL=lines.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lines.d.ts","sourceRoot":"","sources":["../../src/server/lines.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,UAAW,OAAO,KAAG,MACiE,CAAA;AAEzG,eAAO,MAAM,QAAQ,SAAU,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAG/B,CAAA;AAU/B
|
|
1
|
+
{"version":3,"file":"lines.d.ts","sourceRoot":"","sources":["../../src/server/lines.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,UAAW,OAAO,KAAG,MACiE,CAAA;AAEzG,eAAO,MAAM,QAAQ,SAAU,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAG/B,CAAA;AAU/B;;;GAGG;AACH,eAAO,MAAM,UAAU,WACb,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,6BACkC;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,YAIhG,MAAM,GAAG,MAAM,SAgB9B,CAAA"}
|
package/dist/server/lines.js
CHANGED
|
@@ -16,10 +16,16 @@ const isTagged = (item) => typeof item === "object" &&
|
|
|
16
16
|
item !== null &&
|
|
17
17
|
Object.keys(item).length === 1 &&
|
|
18
18
|
typeof item[BIGINT] === "string";
|
|
19
|
-
/**
|
|
20
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Calls `onLine` for each complete line `data` finishes, keeping the rest for next time. With
|
|
21
|
+
* `maxLength`, a line that grows past it calls `onTooLong` instead and nothing more is read.
|
|
22
|
+
*/
|
|
23
|
+
export const lineReader = (onLine, { maxLength = Number.POSITIVE_INFINITY, onTooLong = () => { } } = {}) => {
|
|
21
24
|
let buffered = "";
|
|
25
|
+
let stopped = false;
|
|
22
26
|
return (data) => {
|
|
27
|
+
if (stopped)
|
|
28
|
+
return;
|
|
23
29
|
buffered += String(data);
|
|
24
30
|
let end = buffered.indexOf("\n");
|
|
25
31
|
while (end >= 0) {
|
|
@@ -29,6 +35,11 @@ export const lineReader = (onLine) => {
|
|
|
29
35
|
onLine(line);
|
|
30
36
|
end = buffered.indexOf("\n");
|
|
31
37
|
}
|
|
38
|
+
if (buffered.length > maxLength) {
|
|
39
|
+
stopped = true;
|
|
40
|
+
buffered = "";
|
|
41
|
+
onTooLong();
|
|
42
|
+
}
|
|
32
43
|
};
|
|
33
44
|
};
|
|
34
45
|
//# sourceMappingURL=lines.js.map
|
package/dist/server/lines.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lines.js","sourceRoot":"","sources":["../../src/server/lines.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAEzE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,KAAc,EAAU,EAAE,CAC/C,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAA;AAEzG,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAA2B,EAAE,CAChE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE;IACvE,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;CAC9F,CAA4B,CAAA;AAE/B,MAAM,MAAM,GAAG,SAAS,CAAA;AAExB,MAAM,QAAQ,GAAG,CAAC,IAAa,EAAgC,EAAE,CAC/D,OAAO,IAAI,KAAK,QAAQ;IACxB,IAAI,KAAK,IAAI;IACb,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;IAC9B,OAAQ,IAAgC,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAA;AAE/D
|
|
1
|
+
{"version":3,"file":"lines.js","sourceRoot":"","sources":["../../src/server/lines.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAEzE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,KAAc,EAAU,EAAE,CAC/C,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAA;AAEzG,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAA2B,EAAE,CAChE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE;IACvE,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;CAC9F,CAA4B,CAAA;AAE/B,MAAM,MAAM,GAAG,SAAS,CAAA;AAExB,MAAM,QAAQ,GAAG,CAAC,IAAa,EAAgC,EAAE,CAC/D,OAAO,IAAI,KAAK,QAAQ;IACxB,IAAI,KAAK,IAAI;IACb,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;IAC9B,OAAQ,IAAgC,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAA;AAE/D;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,MAA8B,EAC9B,EAAE,SAAS,GAAG,MAAM,CAAC,iBAAiB,EAAE,SAAS,GAAG,GAAG,EAAE,GAAE,CAAC,EAAE,GAAmD,EAAE,EACnH,EAAE;IACF,IAAI,QAAQ,GAAG,EAAE,CAAA;IACjB,IAAI,OAAO,GAAG,KAAK,CAAA;IACnB,OAAO,CAAC,IAAqB,EAAE,EAAE;QAC/B,IAAI,OAAO;YAAE,OAAM;QACnB,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,CAAA;QACxB,IAAI,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QAChC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC;YAChB,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;YACnC,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAA;YAClC,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;gBAAE,MAAM,CAAC,IAAI,CAAC,CAAA;YACpC,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QAC9B,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;YAChC,OAAO,GAAG,IAAI,CAAA;YACd,QAAQ,GAAG,EAAE,CAAA;YACb,SAAS,EAAE,CAAA;QACb,CAAC;IACH,CAAC,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Connection, type Wire, type WireEvent } from "../protocol/connection.js";
|
|
2
2
|
import { type Payload } from "../protocol/frame.js";
|
|
3
3
|
import type { SessionStore } from "../session/store.js";
|
|
4
|
+
import type { Operation } from "../spec/define.js";
|
|
4
5
|
/**
|
|
5
6
|
* **A command's connection: `max serve`'s, shared** (`MAX-35`, `NEED-229`).
|
|
6
7
|
*
|
|
7
8
|
* One connection to MAX per profile, whoever asks — CLI commands, `max mcp`, `max watch`. INIT
|
|
8
9
|
* and LOGIN are answered by the server's own login; **every other request goes over its
|
|
9
|
-
* connection, sends included**. The send guards
|
|
10
|
-
* before
|
|
10
|
+
* connection, sends included**. The server runs the send guards on every write and journals it;
|
|
11
|
+
* the command's client checks too, so a refusal comes before an upload, and journals only that.
|
|
11
12
|
*
|
|
12
13
|
* No server answering: `ensure` starts one and waits for it. A connection of the command's own
|
|
13
14
|
* is opened only when there is no `ensure` (the owner said `serve: false`) and no server, or for
|
|
@@ -24,15 +25,17 @@ export declare class ServerConnection implements Wire {
|
|
|
24
25
|
ensure?: () => Promise<boolean>;
|
|
25
26
|
});
|
|
26
27
|
open(): Promise<void>;
|
|
28
|
+
/** Whether the server is the one answering — and so the one writing the send journal. */
|
|
29
|
+
get journals(): boolean;
|
|
27
30
|
invoke(opcode: number, payload?: Payload, watch?: (event: WireEvent) => void): Promise<Payload>;
|
|
28
31
|
close(): Promise<void>;
|
|
29
32
|
}
|
|
30
33
|
/**
|
|
31
|
-
*
|
|
32
|
-
* QR or SMS is `max session start` on a connection of
|
|
33
|
-
*
|
|
34
|
+
* The operation the server passes on for this opcode, or `undefined` for one it does not: logging
|
|
35
|
+
* in is the server's own business, a login by QR or SMS is `max session start` on a connection of
|
|
36
|
+
* its own, and a reserved number is never sent at all.
|
|
34
37
|
*/
|
|
35
|
-
export declare const
|
|
38
|
+
export declare const forwardedOperation: (opcode: number) => Operation | undefined;
|
|
36
39
|
/**
|
|
37
40
|
* Asks a running server to stop. `"refused"` is a server started by hand, which only Ctrl-C
|
|
38
41
|
* stops; `"none"` is nobody listening.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-connection.d.ts","sourceRoot":"","sources":["../../src/server/server-connection.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAiB,KAAK,IAAI,EAAE,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAChG,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"server-connection.d.ts","sourceRoot":"","sources":["../../src/server/server-connection.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAiB,KAAK,IAAI,EAAE,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAChG,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AASlD;;;;;;;;;;;GAWG;AACH,qBAAa,gBAAiB,YAAW,IAAI;;IAc3C,YAAY,EACV,IAAI,EACJ,KAAK,EACL,SAAkB,EAClB,MAAM,EACN,MAAM,GACP,EAAE;QACD,IAAI,EAAE,MAAM,CAAA;QACZ,KAAK,EAAE,YAAY,CAAA;QACnB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,MAAM,CAAC,EAAE,MAAM,UAAU,CAAA;QACzB,yFAAyF;QACzF,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;KAChC,EAMA;IAEK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAG;IAE9B,yFAAyF;IACzF,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAEK,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,OAAY,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAqBxG;IA6BK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAS3B;CAsEF;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,WAAY,MAAM,KAAG,SAAS,GAAG,SAI7D,CAAA;AAEH;;;GAGG;AACH,eAAO,MAAM,UAAU,SAAU,MAAM;;MAA2B,OAAO,CAAC,SAAS,GAAG,SAAS,GAAG,MAAM,CAyBpG,CAAA;AAEJ,mFAAmF;AACnF,eAAO,MAAM,YAAY,SAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAenF,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { connect } from "node:net";
|
|
2
|
-
import { CliError } from "@leemour/cli-core";
|
|
2
|
+
import { CliError, errorCodes } from "@leemour/cli-core";
|
|
3
3
|
import { Opcode } from "../generated/opcodes.generated.js";
|
|
4
4
|
import { OPERATIONS } from "../generated/operations.generated.js";
|
|
5
5
|
import { Connection, ProtocolError } from "../protocol/connection.js";
|
|
@@ -10,8 +10,8 @@ import { fromLine, lineReader, toLine } from "./lines.js";
|
|
|
10
10
|
*
|
|
11
11
|
* One connection to MAX per profile, whoever asks — CLI commands, `max mcp`, `max watch`. INIT
|
|
12
12
|
* and LOGIN are answered by the server's own login; **every other request goes over its
|
|
13
|
-
* connection, sends included**. The send guards
|
|
14
|
-
* before
|
|
13
|
+
* connection, sends included**. The server runs the send guards on every write and journals it;
|
|
14
|
+
* the command's client checks too, so a refusal comes before an upload, and journals only that.
|
|
15
15
|
*
|
|
16
16
|
* No server answering: `ensure` starts one and waits for it. A connection of the command's own
|
|
17
17
|
* is opened only when there is no `ensure` (the owner said `serve: false`) and no server, or for
|
|
@@ -38,6 +38,10 @@ export class ServerConnection {
|
|
|
38
38
|
this.#direct = direct ?? (() => new Connection({ timeoutMs }));
|
|
39
39
|
}
|
|
40
40
|
async open() { }
|
|
41
|
+
/** Whether the server is the one answering — and so the one writing the send journal. */
|
|
42
|
+
get journals() {
|
|
43
|
+
return this.#login !== undefined && this.#own === undefined;
|
|
44
|
+
}
|
|
41
45
|
async invoke(opcode, payload = {}, watch) {
|
|
42
46
|
if (this.#own)
|
|
43
47
|
return this.#own.invoke(opcode, payload, watch);
|
|
@@ -116,6 +120,11 @@ export class ServerConnection {
|
|
|
116
120
|
socket.write(toLine({ id, ...request }));
|
|
117
121
|
});
|
|
118
122
|
const error = answer.error;
|
|
123
|
+
// The server's send guard: the same refusal, and so the same exit code, as the command's own.
|
|
124
|
+
if (error?.guard === true) {
|
|
125
|
+
const code = errorCodes.find((known) => known === error.code) ?? "permission_error";
|
|
126
|
+
throw new CliError(code, error.message ?? "the server's send guard refused it", error.details);
|
|
127
|
+
}
|
|
119
128
|
if (error?.code === "refused") {
|
|
120
129
|
throw new ProtocolError(error.message ?? "MAX refused it", Number(request.opcode), error.payload ?? null);
|
|
121
130
|
}
|
|
@@ -154,11 +163,11 @@ export class ServerConnection {
|
|
|
154
163
|
}
|
|
155
164
|
}
|
|
156
165
|
/**
|
|
157
|
-
*
|
|
158
|
-
* QR or SMS is `max session start` on a connection of
|
|
159
|
-
*
|
|
166
|
+
* The operation the server passes on for this opcode, or `undefined` for one it does not: logging
|
|
167
|
+
* in is the server's own business, a login by QR or SMS is `max session start` on a connection of
|
|
168
|
+
* its own, and a reserved number is never sent at all.
|
|
160
169
|
*/
|
|
161
|
-
export const
|
|
170
|
+
export const forwardedOperation = (opcode) => Object.values(OPERATIONS).find((operation) => operation.opcode === opcode && !operation.name.startsWith("session.") && !operation.name.startsWith("login."));
|
|
162
171
|
/**
|
|
163
172
|
* Asks a running server to stop. `"refused"` is a server started by hand, which only Ctrl-C
|
|
164
173
|
* stops; `"none"` is nobody listening.
|