@leemour/max-cli 0.6.0 → 0.8.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 +201 -74
- package/dist/cache/index.d.ts +1 -0
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +4 -4
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/schema.d.ts +13 -8
- package/dist/cache/schema.d.ts.map +1 -1
- package/dist/cache/schema.js +84 -23
- package/dist/cache/schema.js.map +1 -1
- package/dist/cache/store.d.ts +15 -0
- package/dist/cache/store.d.ts.map +1 -1
- package/dist/cache/store.js +32 -3
- package/dist/cache/store.js.map +1 -1
- package/dist/client.d.ts +205 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +866 -31
- package/dist/client.js.map +1 -1
- package/dist/commands/account.d.ts.map +1 -1
- package/dist/commands/account.js +71 -3
- package/dist/commands/account.js.map +1 -1
- package/dist/commands/chats.d.ts.map +1 -1
- package/dist/commands/chats.js +167 -0
- package/dist/commands/chats.js.map +1 -1
- package/dist/commands/complete.d.ts +10 -0
- package/dist/commands/complete.d.ts.map +1 -0
- package/dist/commands/complete.js +89 -0
- package/dist/commands/complete.js.map +1 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +10 -4
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/contacts.d.ts +3 -0
- package/dist/commands/contacts.d.ts.map +1 -1
- package/dist/commands/contacts.js +85 -0
- package/dist/commands/contacts.js.map +1 -1
- package/dist/commands/context.d.ts +22 -3
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +26 -7
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/export.d.ts +3 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +83 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/folders.d.ts +4 -0
- package/dist/commands/folders.d.ts.map +1 -0
- package/dist/commands/folders.js +76 -0
- package/dist/commands/folders.js.map +1 -0
- package/dist/commands/inbox.js +2 -2
- package/dist/commands/inbox.js.map +1 -1
- package/dist/commands/mcp.d.ts +3 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +24 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/messages.d.ts.map +1 -1
- package/dist/commands/messages.js +210 -5
- package/dist/commands/messages.js.map +1 -1
- package/dist/commands/models.d.ts +7 -0
- package/dist/commands/models.d.ts.map +1 -0
- package/dist/commands/models.js +47 -0
- package/dist/commands/models.js.map +1 -0
- package/dist/commands/reactions.d.ts.map +1 -1
- package/dist/commands/reactions.js +17 -0
- package/dist/commands/reactions.js.map +1 -1
- package/dist/commands/serve.d.ts +10 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/commands/serve.js +98 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +16 -3
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +53 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/watch.d.ts +9 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +64 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/config.d.ts +32 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +82 -5
- package/dist/config.js.map +1 -1
- package/dist/domain/map.d.ts +8 -1
- package/dist/domain/map.d.ts.map +1 -1
- package/dist/domain/map.js +47 -1
- package/dist/domain/map.js.map +1 -1
- package/dist/domain/models.d.ts +61 -0
- package/dist/domain/models.d.ts.map +1 -1
- package/dist/domain/models.js.map +1 -1
- package/dist/download.d.ts +2 -0
- package/dist/download.d.ts.map +1 -1
- package/dist/download.js +11 -0
- package/dist/download.js.map +1 -1
- package/dist/export.d.ts +26 -0
- package/dist/export.d.ts.map +1 -0
- package/dist/export.js +75 -0
- package/dist/export.js.map +1 -0
- package/dist/generated/client.generated.d.ts +28 -0
- package/dist/generated/client.generated.d.ts.map +1 -1
- package/dist/generated/client.generated.js +28 -0
- package/dist/generated/client.generated.js.map +1 -1
- package/dist/generated/opcodes.generated.d.ts +23 -13
- package/dist/generated/opcodes.generated.d.ts.map +1 -1
- package/dist/generated/opcodes.generated.js +23 -13
- package/dist/generated/opcodes.generated.js.map +1 -1
- package/dist/generated/operations.generated.d.ts +180 -6
- package/dist/generated/operations.generated.d.ts.map +1 -1
- package/dist/generated/operations.generated.js +29 -4
- package/dist/generated/operations.generated.js.map +1 -1
- package/dist/mcp/confirm.d.ts +31 -0
- package/dist/mcp/confirm.d.ts.map +1 -0
- package/dist/mcp/confirm.js +62 -0
- package/dist/mcp/confirm.js.map +1 -0
- package/dist/mcp/instructions.d.ts +14 -0
- package/dist/mcp/instructions.d.ts.map +1 -0
- package/dist/mcp/instructions.js +39 -0
- package/dist/mcp/instructions.js.map +1 -0
- package/dist/mcp/server.d.ts +25 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +57 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/session.d.ts +28 -0
- package/dist/mcp/session.d.ts.map +1 -0
- package/dist/mcp/session.js +91 -0
- package/dist/mcp/session.js.map +1 -0
- package/dist/mcp/tools.d.ts +15 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +351 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +23 -2
- package/dist/program.js.map +1 -1
- package/dist/protocol/connection.d.ts +10 -8
- package/dist/protocol/connection.d.ts.map +1 -1
- package/dist/protocol/connection.js +64 -7
- package/dist/protocol/connection.js.map +1 -1
- package/dist/protocol/frame.d.ts +2 -1
- package/dist/protocol/frame.d.ts.map +1 -1
- package/dist/protocol/frame.js.map +1 -1
- package/dist/rendering/messages.d.ts.map +1 -1
- package/dist/rendering/messages.js +17 -9
- package/dist/rendering/messages.js.map +1 -1
- package/dist/sends/guard.d.ts +8 -3
- package/dist/sends/guard.d.ts.map +1 -1
- package/dist/sends/guard.js +40 -15
- package/dist/sends/guard.js.map +1 -1
- package/dist/sends/journal.d.ts +20 -2
- package/dist/sends/journal.d.ts.map +1 -1
- package/dist/sends/journal.js.map +1 -1
- package/dist/sends/permissions.d.ts +11 -0
- package/dist/sends/permissions.d.ts.map +1 -0
- package/dist/sends/permissions.js +47 -0
- package/dist/sends/permissions.js.map +1 -0
- package/dist/server/lines.d.ts +9 -0
- package/dist/server/lines.d.ts.map +1 -0
- package/dist/server/lines.js +25 -0
- package/dist/server/lines.js.map +1 -0
- package/dist/server/server-connection.d.ts +45 -0
- package/dist/server/server-connection.d.ts.map +1 -0
- package/dist/server/server-connection.js +201 -0
- package/dist/server/server-connection.js.map +1 -0
- package/dist/server/server.d.ts +69 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +360 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/start.d.ts +28 -0
- package/dist/server/start.d.ts.map +1 -0
- package/dist/server/start.js +80 -0
- package/dist/server/start.js.map +1 -0
- package/dist/server/subscribe.d.ts +9 -0
- package/dist/server/subscribe.d.ts.map +1 -0
- package/dist/server/subscribe.js +26 -0
- package/dist/server/subscribe.js.map +1 -0
- package/dist/session/adopt.d.ts.map +1 -1
- package/dist/session/adopt.js +4 -0
- package/dist/session/adopt.js.map +1 -1
- package/dist/session/browser.d.ts +1 -1
- package/dist/session/browser.d.ts.map +1 -1
- package/dist/session/browser.js +86 -59
- package/dist/session/browser.js.map +1 -1
- package/dist/session/handshake.d.ts +1 -1
- package/dist/session/store.d.ts +2 -0
- package/dist/session/store.d.ts.map +1 -1
- package/dist/session/store.js +4 -0
- package/dist/session/store.js.map +1 -1
- package/dist/spec/define.d.ts.map +1 -1
- package/dist/spec/define.js +14 -1
- package/dist/spec/define.js.map +1 -1
- package/dist/spec/index.d.ts.map +1 -1
- package/dist/spec/index.js +30 -7
- package/dist/spec/index.js.map +1 -1
- package/dist/spec/operations/account.d.ts +20 -0
- package/dist/spec/operations/account.d.ts.map +1 -0
- package/dist/spec/operations/account.js +62 -0
- package/dist/spec/operations/account.js.map +1 -0
- package/dist/spec/operations/chats.d.ts +66 -2
- package/dist/spec/operations/chats.d.ts.map +1 -1
- package/dist/spec/operations/chats.js +153 -5
- package/dist/spec/operations/chats.js.map +1 -1
- package/dist/spec/operations/contacts.d.ts +19 -1
- package/dist/spec/operations/contacts.d.ts.map +1 -1
- package/dist/spec/operations/contacts.js +49 -6
- package/dist/spec/operations/contacts.js.map +1 -1
- package/dist/spec/operations/folders.d.ts +24 -0
- package/dist/spec/operations/folders.d.ts.map +1 -0
- package/dist/spec/operations/folders.js +64 -0
- package/dist/spec/operations/folders.js.map +1 -0
- package/dist/spec/operations/messages.d.ts +56 -6
- package/dist/spec/operations/messages.d.ts.map +1 -1
- package/dist/spec/operations/messages.js +104 -21
- package/dist/spec/operations/messages.js.map +1 -1
- package/dist/spec/operations/session.d.ts +7 -0
- package/dist/spec/operations/session.d.ts.map +1 -1
- package/dist/spec/operations/session.js +17 -0
- package/dist/spec/operations/session.js.map +1 -1
- package/dist/spec/operations/uploads.d.ts +18 -0
- package/dist/spec/operations/uploads.d.ts.map +1 -0
- package/dist/spec/operations/uploads.js +35 -0
- package/dist/spec/operations/uploads.js.map +1 -0
- package/dist/testing/mock-max.d.ts +12 -2
- package/dist/testing/mock-max.d.ts.map +1 -1
- package/dist/testing/mock-max.js +15 -2
- package/dist/testing/mock-max.js.map +1 -1
- package/dist/testing/sandbox.js +2 -0
- package/dist/testing/sandbox.js.map +1 -1
- package/dist/transcribe/index.d.ts +34 -0
- package/dist/transcribe/index.d.ts.map +1 -0
- package/dist/transcribe/index.js +43 -0
- package/dist/transcribe/index.js.map +1 -0
- package/dist/transcribe/install.d.ts +18 -0
- package/dist/transcribe/install.d.ts.map +1 -0
- package/dist/transcribe/install.js +67 -0
- package/dist/transcribe/install.js.map +1 -0
- package/dist/transcribe/models.d.ts +32 -0
- package/dist/transcribe/models.d.ts.map +1 -0
- package/dist/transcribe/models.js +124 -0
- package/dist/transcribe/models.js.map +1 -0
- package/dist/transcribe/speech.d.ts +25 -0
- package/dist/transcribe/speech.d.ts.map +1 -0
- package/dist/transcribe/speech.js +105 -0
- package/dist/transcribe/speech.js.map +1 -0
- package/dist/update.d.ts +26 -0
- package/dist/update.d.ts.map +1 -0
- package/dist/update.js +42 -0
- package/dist/update.js.map +1 -0
- package/dist/upload.d.ts +10 -0
- package/dist/upload.d.ts.map +1 -0
- package/dist/upload.js +61 -0
- package/dist/upload.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +17 -3
- package/skills/max-cli/SKILL.md +28 -3
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { chmodSync, mkdirSync, rmSync } from "node:fs";
|
|
2
|
+
import { connect, createServer } from "node:net";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { CliError } from "@leemour/cli-core";
|
|
5
|
+
import { MaxClient } from "../client.js";
|
|
6
|
+
import { Opcode } from "../generated/opcodes.generated.js";
|
|
7
|
+
import { Connection, ProtocolError } from "../protocol/connection.js";
|
|
8
|
+
import { fromLine, lineReader, toLine } from "./lines.js";
|
|
9
|
+
import { forwarded, stopServer } from "./server-connection.js";
|
|
10
|
+
/** The web client's keep-alive interval (web.max.ru bundle, 2026-09-24). A different one is a fingerprint. */
|
|
11
|
+
const PING_EVERY_MS = 30_000;
|
|
12
|
+
/** A message arrived — what MAX pushes, and what a send through this server is handed on as. */
|
|
13
|
+
const NEW_MESSAGE = 128;
|
|
14
|
+
/** A login MAX counts; after one, the next background login waits at least this long. */
|
|
15
|
+
const REFRESH_EVERY_MS = 60_000;
|
|
16
|
+
/**
|
|
17
|
+
* **One logged-in connection per profile, kept open** (`MAX-16`), and a Unix socket beside the
|
|
18
|
+
* profile's state for local readers.
|
|
19
|
+
*
|
|
20
|
+
* It reopens REQUIREMENTS §3 and §18 on purpose and only here: every other command stays one-shot.
|
|
21
|
+
* What it does on the wire copies web.max.ru — a ping every 30 s, MAX's pings answered, each new
|
|
22
|
+
* message acknowledged (`Connection` with `live`). It never marks anything read and never sends.
|
|
23
|
+
*
|
|
24
|
+
* The socket answers `{"subscribe": true}` with a stream of `ServerEvent` lines and
|
|
25
|
+
* `{"status": true}` with one. For a command reusing the connection (`ServerConnection`) it answers
|
|
26
|
+
* `{"id", "login": true}` with the login it holds, and `{"id", "opcode", "payload"}` by passing the
|
|
27
|
+
* request to MAX — **reads only** (`NEED-185`). Anything that writes goes on the command's own
|
|
28
|
+
* connection, through the send guards, which live in the client and nowhere else.
|
|
29
|
+
*/
|
|
30
|
+
export class MaxServer {
|
|
31
|
+
#options;
|
|
32
|
+
#subscribers = new Set();
|
|
33
|
+
#listener;
|
|
34
|
+
#client;
|
|
35
|
+
#ping;
|
|
36
|
+
#retry;
|
|
37
|
+
#attempt = 0;
|
|
38
|
+
#stopped = false;
|
|
39
|
+
/** Every client on the socket — a command, `max mcp`, `max watch`. Idle means none at all. */
|
|
40
|
+
#open = new Set();
|
|
41
|
+
/** Message ids already handed to watchers, newest last. */
|
|
42
|
+
#seen = new Set();
|
|
43
|
+
/** Settles when a connection is up; replaced while the server reconnects. */
|
|
44
|
+
#up;
|
|
45
|
+
#markUp = () => { };
|
|
46
|
+
#refresh;
|
|
47
|
+
#lastRefresh = 0;
|
|
48
|
+
#lastUse = Date.now();
|
|
49
|
+
#idle;
|
|
50
|
+
#finish;
|
|
51
|
+
/** Settles when the server stops — cleanly, or with the error that stopped it. */
|
|
52
|
+
done;
|
|
53
|
+
constructor(options) {
|
|
54
|
+
this.#options = options;
|
|
55
|
+
this.#up = new Promise((resolve) => {
|
|
56
|
+
this.#markUp = resolve;
|
|
57
|
+
});
|
|
58
|
+
this.done = new Promise((resolve, reject) => {
|
|
59
|
+
this.#finish = (error) => (error ? reject(error) : resolve());
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
get connected() {
|
|
63
|
+
return this.#client !== undefined;
|
|
64
|
+
}
|
|
65
|
+
/** Logs in first: a token that does not work should fail here, before anything listens. */
|
|
66
|
+
async start() {
|
|
67
|
+
// The socket first: whoever binds it is the profile's one server, and a second one learns it
|
|
68
|
+
// before it has logged in — not after, with a login MAX has already counted.
|
|
69
|
+
try {
|
|
70
|
+
await this.#listen();
|
|
71
|
+
await this.#connect();
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
await this.stop();
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
rmSync(startingPath(this.#options.store), { force: true });
|
|
79
|
+
}
|
|
80
|
+
const { idleMs } = this.#options;
|
|
81
|
+
if (idleMs !== undefined) {
|
|
82
|
+
this.#idle = setInterval(() => {
|
|
83
|
+
if (this.#open.size === 0 && Date.now() - this.#lastUse >= idleMs) {
|
|
84
|
+
this.#options.note(`nobody has used it for ${Math.round(idleMs / 60_000)} min — stopping`);
|
|
85
|
+
void this.stop();
|
|
86
|
+
}
|
|
87
|
+
}, Math.min(idleMs, 30_000));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async stop(error) {
|
|
91
|
+
if (this.#stopped)
|
|
92
|
+
return;
|
|
93
|
+
this.#stopped = true;
|
|
94
|
+
// Anyone still waiting for a connection gets "not connected" rather than waiting forever.
|
|
95
|
+
this.#markUp();
|
|
96
|
+
clearInterval(this.#ping);
|
|
97
|
+
clearTimeout(this.#retry);
|
|
98
|
+
clearTimeout(this.#refresh);
|
|
99
|
+
clearInterval(this.#idle);
|
|
100
|
+
for (const socket of this.#open)
|
|
101
|
+
socket.destroy();
|
|
102
|
+
this.#subscribers.clear();
|
|
103
|
+
// Only the server that bound the socket removes it: one refused as "already running" would
|
|
104
|
+
// otherwise delete the running server's socket and let a third start beside it.
|
|
105
|
+
if (this.#listener) {
|
|
106
|
+
await new Promise((resolve) => this.#listener?.close(() => resolve()));
|
|
107
|
+
rmSync(this.#options.store.socketPath(), { force: true });
|
|
108
|
+
}
|
|
109
|
+
await this.#client?.close();
|
|
110
|
+
this.#client = undefined;
|
|
111
|
+
this.#finish?.(error);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Logs in on a new connection and, once that worked, makes it the server's — closing the one it
|
|
115
|
+
* replaces, if any. Pushes that arrive before the login finishes are held and replayed, not
|
|
116
|
+
* dropped: `Connection` has already acknowledged them, so MAX will not send them again.
|
|
117
|
+
*/
|
|
118
|
+
async #connect() {
|
|
119
|
+
const { store, cache, timeoutMs, events } = this.#options;
|
|
120
|
+
let mine;
|
|
121
|
+
const early = [];
|
|
122
|
+
const connection = (this.#options.connection ??
|
|
123
|
+
((hooks) => new Connection({ ...hooks, live: true, ...(timeoutMs ? { timeoutMs } : {}) })))({
|
|
124
|
+
onEvent: (frame) => {
|
|
125
|
+
if (mine)
|
|
126
|
+
this.#pushed(mine, frame.opcode, frame.payload ?? {});
|
|
127
|
+
else
|
|
128
|
+
early.push([frame.opcode, frame.payload ?? {}]);
|
|
129
|
+
},
|
|
130
|
+
onClose: (error) => {
|
|
131
|
+
if (mine && mine === this.#client)
|
|
132
|
+
this.#lost(error);
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
const client = new MaxClient({
|
|
136
|
+
store,
|
|
137
|
+
connection,
|
|
138
|
+
fullLogin: true,
|
|
139
|
+
warn: this.#options.note,
|
|
140
|
+
...(cache ? { cache } : {}),
|
|
141
|
+
...(events ? { events } : {}),
|
|
142
|
+
});
|
|
143
|
+
try {
|
|
144
|
+
await client.connect();
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
// A socket left open after a refused login keeps the process alive after it said it failed.
|
|
148
|
+
await client.close();
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
const replaced = this.#client;
|
|
152
|
+
mine = client;
|
|
153
|
+
this.#client = client;
|
|
154
|
+
this.#markUp();
|
|
155
|
+
this.#attempt = 0;
|
|
156
|
+
this.#lastRefresh = Date.now();
|
|
157
|
+
clearInterval(this.#ping);
|
|
158
|
+
this.#ping = setInterval(() => {
|
|
159
|
+
client.live.ping().catch(() => { });
|
|
160
|
+
}, this.#options.pingEveryMs ?? PING_EVERY_MS);
|
|
161
|
+
if (replaced)
|
|
162
|
+
await replaced.close().catch(() => { });
|
|
163
|
+
else
|
|
164
|
+
this.#broadcast({ event: "status", connected: true, at: new Date().toISOString() });
|
|
165
|
+
for (const [opcode, payload] of early)
|
|
166
|
+
this.#pushed(client, opcode, payload);
|
|
167
|
+
}
|
|
168
|
+
#pushed(client, opcode, payload) {
|
|
169
|
+
if (!client.live.patch(opcode, payload))
|
|
170
|
+
this.#goneStale();
|
|
171
|
+
client.live
|
|
172
|
+
.message(opcode, payload)
|
|
173
|
+
.then((message) => message && this.#broadcast({ event: "message", message }))
|
|
174
|
+
.catch((error) => this.#options.note(`a pushed message could not be read: ${error.message}`));
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* The login can no longer be handed out. Rather than wait for a drop that may be hours away, log
|
|
178
|
+
* in again in the background — at most once a minute, since each is a login MAX counts.
|
|
179
|
+
*/
|
|
180
|
+
#goneStale() {
|
|
181
|
+
if (this.#refresh || this.#stopped)
|
|
182
|
+
return;
|
|
183
|
+
const every = this.#options.refreshEveryMs ?? REFRESH_EVERY_MS;
|
|
184
|
+
const wait = Math.max(0, this.#lastRefresh + every - Date.now());
|
|
185
|
+
this.#refresh = setTimeout(() => {
|
|
186
|
+
this.#connect()
|
|
187
|
+
.catch((error) => this.#options.note(`could not log in again: ${error.message}`))
|
|
188
|
+
.finally(() => {
|
|
189
|
+
this.#refresh = undefined;
|
|
190
|
+
});
|
|
191
|
+
}, wait);
|
|
192
|
+
}
|
|
193
|
+
#lost(error) {
|
|
194
|
+
clearInterval(this.#ping);
|
|
195
|
+
const client = this.#client;
|
|
196
|
+
this.#client = undefined;
|
|
197
|
+
this.#up = new Promise((resolve) => {
|
|
198
|
+
this.#markUp = resolve;
|
|
199
|
+
});
|
|
200
|
+
client?.close().catch(() => { });
|
|
201
|
+
if (this.#stopped)
|
|
202
|
+
return;
|
|
203
|
+
this.#broadcast({ event: "status", connected: false, at: new Date().toISOString() });
|
|
204
|
+
this.#reconnectLater(error);
|
|
205
|
+
}
|
|
206
|
+
#reconnectLater(error) {
|
|
207
|
+
const wait = (this.#options.retryAfterMs ?? backoff)(this.#attempt);
|
|
208
|
+
this.#attempt += 1;
|
|
209
|
+
this.#options.note(`${error.message} — connecting again in ${Math.round(wait / 1000)}s`);
|
|
210
|
+
this.#retry = setTimeout(() => {
|
|
211
|
+
this.#connect().catch((failure) => {
|
|
212
|
+
// A token MAX no longer takes will not start working on the next attempt.
|
|
213
|
+
if (failure instanceof CliError && failure.code === "authentication_error")
|
|
214
|
+
this.stop(failure);
|
|
215
|
+
else
|
|
216
|
+
this.#reconnectLater(failure);
|
|
217
|
+
});
|
|
218
|
+
}, wait);
|
|
219
|
+
}
|
|
220
|
+
#broadcast(event) {
|
|
221
|
+
if (event.event === "message") {
|
|
222
|
+
// A retried send answers with the same message, and MAX repeats pushes after a hiccup.
|
|
223
|
+
if (this.#seen.has(event.message.id))
|
|
224
|
+
return;
|
|
225
|
+
this.#seen.add(event.message.id);
|
|
226
|
+
if (this.#seen.size > 500)
|
|
227
|
+
this.#seen.delete(this.#seen.values().next().value);
|
|
228
|
+
}
|
|
229
|
+
const line = toLine(event);
|
|
230
|
+
for (const socket of this.#subscribers)
|
|
231
|
+
socket.write(line);
|
|
232
|
+
}
|
|
233
|
+
async #listen() {
|
|
234
|
+
const path = this.#options.store.socketPath();
|
|
235
|
+
// A server a command started gives way to one started by hand; one started by hand does not.
|
|
236
|
+
if ((await answers(path)) && !(!this.#options.startedByCommand && (await stopServer(path)) === "stopped")) {
|
|
237
|
+
throw new CliError("validation_error", `a server is already running for profile "${this.#options.store.profile}"`);
|
|
238
|
+
}
|
|
239
|
+
// Nobody answers on it, so it is what a crashed server left behind.
|
|
240
|
+
rmSync(path, { force: true });
|
|
241
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
242
|
+
const listener = createServer((socket) => this.#serve(socket));
|
|
243
|
+
await new Promise((resolve, reject) => {
|
|
244
|
+
listener.once("error", reject);
|
|
245
|
+
listener.listen(path, () => resolve());
|
|
246
|
+
});
|
|
247
|
+
chmodSync(path, 0o600);
|
|
248
|
+
this.#listener = listener;
|
|
249
|
+
}
|
|
250
|
+
#serve(socket) {
|
|
251
|
+
this.#open.add(socket);
|
|
252
|
+
socket.on("error", () => this.#subscribers.delete(socket));
|
|
253
|
+
socket.on("close", () => {
|
|
254
|
+
this.#subscribers.delete(socket);
|
|
255
|
+
this.#open.delete(socket);
|
|
256
|
+
this.#lastUse = Date.now();
|
|
257
|
+
});
|
|
258
|
+
socket.on("data", lineReader((line) => {
|
|
259
|
+
this.#request(socket, line).catch(() => socket.destroy());
|
|
260
|
+
}));
|
|
261
|
+
}
|
|
262
|
+
async #request(socket, line) {
|
|
263
|
+
let request;
|
|
264
|
+
try {
|
|
265
|
+
request = fromLine(line);
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
socket.write(toLine({ error: { code: "bad_request", message: "one JSON object per line" } }));
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const status = {
|
|
272
|
+
event: "status",
|
|
273
|
+
connected: this.connected,
|
|
274
|
+
at: new Date().toISOString(),
|
|
275
|
+
byHand: !this.#options.startedByCommand,
|
|
276
|
+
pid: process.pid,
|
|
277
|
+
};
|
|
278
|
+
const { id } = request;
|
|
279
|
+
this.#lastUse = Date.now();
|
|
280
|
+
if (request.subscribe === true) {
|
|
281
|
+
this.#subscribers.add(socket);
|
|
282
|
+
socket.write(toLine(status));
|
|
283
|
+
}
|
|
284
|
+
else if (request.status === true) {
|
|
285
|
+
socket.write(toLine(status));
|
|
286
|
+
}
|
|
287
|
+
else if (request.stop === true) {
|
|
288
|
+
// Only the owner can reach this socket (mode 600). `max session end` asks, so a forgotten
|
|
289
|
+
// session is not kept alive by a server a command started — one started by hand stays.
|
|
290
|
+
if (!this.#options.startedByCommand && request.force !== true) {
|
|
291
|
+
socket.end(toLine({ id, stopped: false, reason: "started by hand; Ctrl-C, or `max serve --stop`" }));
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
socket.end(toLine({ id, stopped: true }));
|
|
295
|
+
await this.stop();
|
|
296
|
+
}
|
|
297
|
+
else if (request.login === true) {
|
|
298
|
+
// A request that arrives while the server logs in, or logs in again, waits for it: the
|
|
299
|
+
// command's own timeout is what gives up, not this. A login a deletion made stale is still
|
|
300
|
+
// handed out — only a deleted last message can be wrong in it — while a fresh one is fetched.
|
|
301
|
+
await this.#up;
|
|
302
|
+
const client = this.#client;
|
|
303
|
+
socket.write(toLine(client
|
|
304
|
+
? { id, payload: client.live.snapshot() }
|
|
305
|
+
: { id, error: { code: "unavailable", message: "not connected" } }));
|
|
306
|
+
}
|
|
307
|
+
else if (typeof request.opcode === "number") {
|
|
308
|
+
socket.write(toLine({ id, ...(await this.#forward(request.opcode, request.payload)) }));
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
socket.write(toLine({ id, error: { code: "bad_request", message: "subscribe, status, login or an opcode" } }));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
async #forward(opcode, payload) {
|
|
315
|
+
await this.#up;
|
|
316
|
+
const client = this.#client;
|
|
317
|
+
if (!forwarded(opcode)) {
|
|
318
|
+
return {
|
|
319
|
+
error: { code: "not_allowed", message: `opcode ${opcode} is a login; the server does its own` },
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
if (!client)
|
|
323
|
+
return { error: { code: "unavailable", message: "not connected" } };
|
|
324
|
+
try {
|
|
325
|
+
const request = (payload ?? {});
|
|
326
|
+
const answer = await client.live.forward(opcode, request);
|
|
327
|
+
// MAX does not push a message back to the connection that sent it, and every send now comes
|
|
328
|
+
// through this one — so `max watch` would never see what `max` itself sent. The answer
|
|
329
|
+
// carries the message; hand it on as if it had been pushed. A scheduled one is not sent yet.
|
|
330
|
+
const scheduled = request.message?.delayedAttributes;
|
|
331
|
+
if (opcode === Opcode.MSG_SEND && answer.message && !scheduled) {
|
|
332
|
+
this.#pushed(client, NEW_MESSAGE, { chatId: request.chatId, message: answer.message });
|
|
333
|
+
}
|
|
334
|
+
// Nor does it push our own deletion back; the chat's last message may be the one deleted.
|
|
335
|
+
if (opcode === Opcode.MSG_DELETE)
|
|
336
|
+
this.#goneStale();
|
|
337
|
+
return { payload: answer };
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
if (error instanceof ProtocolError) {
|
|
341
|
+
return { error: { code: "refused", message: error.message, payload: error.payload } };
|
|
342
|
+
}
|
|
343
|
+
return { error: { code: "unavailable", message: error instanceof Error ? error.message : String(error) } };
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/** 1 s, 2 s, 4 s … a minute at most — a server that hammers MAX after a drop looks like nothing MAX knows. */
|
|
348
|
+
const backoff = (attempt) => Math.min(60_000, 1000 * 2 ** attempt);
|
|
349
|
+
/** Present while a server is being started in the background, so two commands do not start two. */
|
|
350
|
+
export const startingPath = (store) => `${store.socketPath()}.starting`;
|
|
351
|
+
/** Whether something is listening on the socket — a live server, not a leftover file. */
|
|
352
|
+
export const answers = (path) => new Promise((resolve) => {
|
|
353
|
+
const probe = connect(path);
|
|
354
|
+
probe.once("connect", () => {
|
|
355
|
+
probe.destroy();
|
|
356
|
+
resolve(true);
|
|
357
|
+
});
|
|
358
|
+
probe.once("error", () => resolve(false));
|
|
359
|
+
});
|
|
360
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { SessionStore } from "../session/store.js";
|
|
2
|
+
/** A server a command started stops after this long unused. */
|
|
3
|
+
export declare const IDLE_MS: number;
|
|
4
|
+
/**
|
|
5
|
+
* Written by a background server whose login MAX refused. Without it, every command after a
|
|
6
|
+
* session expires would start a server that fails the same way — one more refused login each.
|
|
7
|
+
* `max session start` removes it.
|
|
8
|
+
*/
|
|
9
|
+
export declare const refusedPath: (store: SessionStore) => string;
|
|
10
|
+
/**
|
|
11
|
+
* Starts `max serve` for this profile in the background, detached, and returns at once — the
|
|
12
|
+
* command that asked goes on with its own connection and the next one finds the server.
|
|
13
|
+
*
|
|
14
|
+
* Its stderr goes to `<state>/profiles/<profile>.serve.log`, mode 600: nobody is watching a
|
|
15
|
+
* terminal for it, and that file is where a login that failed says so.
|
|
16
|
+
*/
|
|
17
|
+
/** Where a background server writes what it would have said on a terminal. */
|
|
18
|
+
export declare const logPath: (store: SessionStore) => string;
|
|
19
|
+
export declare const startInBackground: (store: SessionStore, { serveArgs, entry }?: {
|
|
20
|
+
serveArgs?: string[] | undefined;
|
|
21
|
+
entry?: string | undefined;
|
|
22
|
+
}) => number | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* A server for this profile, answering — started if none is, waited for if another command is
|
|
25
|
+
* already starting one. `false`: none came up (no session, or MAX refused its login lately).
|
|
26
|
+
*/
|
|
27
|
+
export declare const ensureServer: (store: SessionStore) => Promise<boolean>;
|
|
28
|
+
//# sourceMappingURL=start.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/server/start.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAGvD,+DAA+D;AAC/D,eAAO,MAAM,OAAO,QAAc,CAAA;AAKlC;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,YAAY,KAAG,MAAyC,CAAA;AAK3F;;;;;;GAMG;AACH,8EAA8E;AAC9E,eAAO,MAAM,OAAO,GAAI,OAAO,YAAY,KAAG,MAAkE,CAAA;AAEhH,eAAO,MAAM,iBAAiB,GAC5B,OAAO,YAAY,EACnB;;;CAAwG,KACvG,MAAM,GAAG,SAyBX,CAAA;AAKD;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAU,OAAO,YAAY,KAAG,OAAO,CAAC,OAAO,CAevE,CAAA"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { closeSync, mkdirSync, openSync, rmSync, statSync } from "node:fs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { answers, startingPath } from "./server.js";
|
|
5
|
+
/** A server a command started stops after this long unused. */
|
|
6
|
+
export const IDLE_MS = 15 * 60_000;
|
|
7
|
+
/** After a background login MAX refused, commands leave the server alone this long. */
|
|
8
|
+
const REFUSED_PAUSE_MS = 10 * 60_000;
|
|
9
|
+
/**
|
|
10
|
+
* Written by a background server whose login MAX refused. Without it, every command after a
|
|
11
|
+
* session expires would start a server that fails the same way — one more refused login each.
|
|
12
|
+
* `max session start` removes it.
|
|
13
|
+
*/
|
|
14
|
+
export const refusedPath = (store) => `${store.socketPath()}.refused`;
|
|
15
|
+
/** A start that has not listened by now crashed; the next command may try again. */
|
|
16
|
+
const START_GRACE_MS = 30_000;
|
|
17
|
+
/**
|
|
18
|
+
* Starts `max serve` for this profile in the background, detached, and returns at once — the
|
|
19
|
+
* command that asked goes on with its own connection and the next one finds the server.
|
|
20
|
+
*
|
|
21
|
+
* Its stderr goes to `<state>/profiles/<profile>.serve.log`, mode 600: nobody is watching a
|
|
22
|
+
* terminal for it, and that file is where a login that failed says so.
|
|
23
|
+
*/
|
|
24
|
+
/** Where a background server writes what it would have said on a terminal. */
|
|
25
|
+
export const logPath = (store) => `${store.socketPath().replace(/\.sock$/, "")}.serve.log`;
|
|
26
|
+
export const startInBackground = (store, { serveArgs = ["--idle", `${IDLE_MS / 60_000}m`, "--started-by-command"], entry = process.argv[1] } = {}) => {
|
|
27
|
+
if (!entry || store.readToken() === undefined)
|
|
28
|
+
return undefined;
|
|
29
|
+
const refusedAt = statSync(refusedPath(store), { throwIfNoEntry: false })?.mtimeMs;
|
|
30
|
+
if (refusedAt !== undefined && Date.now() - refusedAt < REFUSED_PAUSE_MS)
|
|
31
|
+
return undefined;
|
|
32
|
+
const lock = startingPath(store);
|
|
33
|
+
mkdirSync(dirname(lock), { recursive: true, mode: 0o700 });
|
|
34
|
+
try {
|
|
35
|
+
closeSync(openSync(lock, "wx", 0o600));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
const since = statSync(lock, { throwIfNoEntry: false })?.mtimeMs ?? 0;
|
|
39
|
+
if (Date.now() - since <= START_GRACE_MS)
|
|
40
|
+
return undefined;
|
|
41
|
+
rmSync(lock, { force: true });
|
|
42
|
+
closeSync(openSync(lock, "wx", 0o600));
|
|
43
|
+
}
|
|
44
|
+
const log = openSync(logPath(store), "a", 0o600);
|
|
45
|
+
const child = spawn(process.execPath, [entry, "--no-record", "serve", ...serveArgs], {
|
|
46
|
+
detached: true,
|
|
47
|
+
stdio: ["ignore", "ignore", log],
|
|
48
|
+
env: { ...process.env, MAX_PROFILE: store.profile },
|
|
49
|
+
});
|
|
50
|
+
child.unref();
|
|
51
|
+
closeSync(log);
|
|
52
|
+
return child.pid;
|
|
53
|
+
};
|
|
54
|
+
/** Logging in takes a second or two; a server that has not answered by now is not coming. */
|
|
55
|
+
const START_WAIT_MS = 15_000;
|
|
56
|
+
/**
|
|
57
|
+
* A server for this profile, answering — started if none is, waited for if another command is
|
|
58
|
+
* already starting one. `false`: none came up (no session, or MAX refused its login lately).
|
|
59
|
+
*/
|
|
60
|
+
export const ensureServer = async (store) => {
|
|
61
|
+
if (await answers(store.socketPath()))
|
|
62
|
+
return true;
|
|
63
|
+
if (store.readToken() === undefined)
|
|
64
|
+
return false;
|
|
65
|
+
const refusedAt = statSync(refusedPath(store), { throwIfNoEntry: false })?.mtimeMs;
|
|
66
|
+
if (refusedAt !== undefined && Date.now() - refusedAt < REFUSED_PAUSE_MS)
|
|
67
|
+
return false;
|
|
68
|
+
const asked = Date.now();
|
|
69
|
+
startInBackground(store);
|
|
70
|
+
const until = asked + START_WAIT_MS;
|
|
71
|
+
while (Date.now() < until) {
|
|
72
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
73
|
+
if (await answers(store.socketPath()))
|
|
74
|
+
return true;
|
|
75
|
+
if ((statSync(refusedPath(store), { throwIfNoEntry: false })?.mtimeMs ?? 0) >= asked)
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=start.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/server/start.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ServerEvent } from "./server.js";
|
|
2
|
+
/**
|
|
3
|
+
* Listens to a running `max serve` until it goes away or `signal` fires.
|
|
4
|
+
*
|
|
5
|
+
* No server is an error that names the command to start one — starting it silently would leave a
|
|
6
|
+
* long-lived connection nobody asked for.
|
|
7
|
+
*/
|
|
8
|
+
export declare const subscribe: (path: string, profile: string, onEvent: (event: ServerEvent) => void, signal?: AbortSignal) => Promise<void>;
|
|
9
|
+
//# sourceMappingURL=subscribe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscribe.d.ts","sourceRoot":"","sources":["../../src/server/subscribe.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE9C;;;;;GAKG;AACH,eAAO,MAAM,SAAS,GACpB,MAAM,MAAM,EACZ,SAAS,MAAM,EACf,SAAS,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,EACrC,SAAS,WAAW,KACnB,OAAO,CAAC,IAAI,CAmBX,CAAA"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { connect } from "node:net";
|
|
2
|
+
import { CliError } from "@leemour/cli-core";
|
|
3
|
+
import { asFirstWord } from "../profile.js";
|
|
4
|
+
import { fromLine, lineReader } from "./lines.js";
|
|
5
|
+
/**
|
|
6
|
+
* Listens to a running `max serve` until it goes away or `signal` fires.
|
|
7
|
+
*
|
|
8
|
+
* No server is an error that names the command to start one — starting it silently would leave a
|
|
9
|
+
* long-lived connection nobody asked for.
|
|
10
|
+
*/
|
|
11
|
+
export const subscribe = (path, profile, onEvent, signal) => new Promise((resolve, reject) => {
|
|
12
|
+
const socket = connect(path);
|
|
13
|
+
signal?.addEventListener("abort", () => socket.end(), { once: true });
|
|
14
|
+
socket.once("connect", () => socket.write(`${JSON.stringify({ subscribe: true })}\n`));
|
|
15
|
+
socket.on("data", lineReader((line) => onEvent(fromLine(line))));
|
|
16
|
+
socket.once("error", (error) => {
|
|
17
|
+
if (error.code === "ENOENT" || error.code === "ECONNREFUSED") {
|
|
18
|
+
const command = `max ${asFirstWord(profile)}serve`;
|
|
19
|
+
reject(new CliError("not_found", `no server is running for profile "${profile}" — start one with \`${command}\``));
|
|
20
|
+
}
|
|
21
|
+
else
|
|
22
|
+
reject(error);
|
|
23
|
+
});
|
|
24
|
+
socket.once("close", () => resolve());
|
|
25
|
+
});
|
|
26
|
+
//# sourceMappingURL=subscribe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscribe.js","sourceRoot":"","sources":["../../src/server/subscribe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAGjD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,IAAY,EACZ,OAAe,EACf,OAAqC,EACrC,MAAoB,EACL,EAAE,CACjB,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IAC9B,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE5B,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IACrE,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAA;IACtF,MAAM,CAAC,EAAE,CACP,MAAM,EACN,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA2B,CAAC,CAAC,CACxE,CAAA;IACD,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAA4B,EAAE,EAAE;QACpD,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YAC7D,MAAM,OAAO,GAAG,OAAO,WAAW,CAAC,OAAO,CAAC,OAAO,CAAA;YAClD,MAAM,CACJ,IAAI,QAAQ,CAAC,WAAW,EAAE,qCAAqC,OAAO,wBAAwB,OAAO,IAAI,CAAC,CAC3G,CAAA;QACH,CAAC;;YAAM,MAAM,CAAC,KAAK,CAAC,CAAA;IACtB,CAAC,CAAC,CAAA;IACF,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAA;AACvC,CAAC,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adopt.d.ts","sourceRoot":"","sources":["../../src/session/adopt.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"adopt.d.ts","sourceRoot":"","sources":["../../src/session/adopt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,UAAU,GAAU,QAAQ,SAAS,EAAE,OAAO,YAAY,EAAE,OAAO,MAAM,KAAG,OAAO,CAAC,IAAI,CAYpG,CAAA"}
|
package/dist/session/adopt.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { rmSync } from "node:fs";
|
|
2
|
+
import { refusedPath } from "../server/start.js";
|
|
1
3
|
/**
|
|
2
4
|
* Takes a token into a profile — **logging in first, and writing to the keyring only after.**
|
|
3
5
|
*
|
|
@@ -26,5 +28,7 @@ export const adoptToken = async (client, store, token) => {
|
|
|
26
28
|
// 2026-09-24 a profile that already held a token kept it, and the command still said "stored".
|
|
27
29
|
if (store.readToken() === before)
|
|
28
30
|
store.writeToken(token);
|
|
31
|
+
// A new session: background servers may try again at once.
|
|
32
|
+
rmSync(refusedPath(store), { force: true });
|
|
29
33
|
};
|
|
30
34
|
//# sourceMappingURL=adopt.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adopt.js","sourceRoot":"","sources":["../../src/session/adopt.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"adopt.js","sourceRoot":"","sources":["../../src/session/adopt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAEhC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAGhD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAAE,MAAiB,EAAE,KAAmB,EAAE,KAAa,EAAiB,EAAE;IACvG,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,EAAE,CAAA;IAChC,MAAM,MAAM,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;IAE/B,+FAA+F;IAC/F,kGAAkG;IAClG,iGAAiG;IACjG,6FAA6F;IAC7F,+FAA+F;IAC/F,IAAI,KAAK,CAAC,SAAS,EAAE,KAAK,MAAM;QAAE,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;IACzD,2DAA2D;IAC3D,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;AAC7C,CAAC,CAAA"}
|
|
@@ -43,7 +43,7 @@ export declare const tokenFromChromium: ({ track, waitMs, }: {
|
|
|
43
43
|
}) => Promise<string>;
|
|
44
44
|
export declare class ChromiumSession implements Closeable {
|
|
45
45
|
#private;
|
|
46
|
-
constructor(child: ChildProcess, profile: string,
|
|
46
|
+
constructor(child: ChildProcess, profile: string, graceMs?: number);
|
|
47
47
|
waitForToken(waitMs: number): Promise<string>;
|
|
48
48
|
close(): Promise<void>;
|
|
49
49
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/session/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,oBAAoB,CAAA;AAW7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE/C;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,oEAAoE;IACpE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpC,4FAA4F;IAC5F,aAAa,EAAE,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CACvG;AAED,eAAO,MAAM,WAAW,EAAE,YAGzB,CAAA;AAKD;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAkCnF,CAAA;AASD,eAAO,MAAM,OAAO,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,IAAI,CAmBvD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,QAAO,MAAM,GAAG,SA0CxC,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAAU,oBAGrC;IACD,KAAK,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAA;IACrC,MAAM,EAAE,MAAM,CAAA;CACf,KAAG,OAAO,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/session/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,oBAAoB,CAAA;AAW7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE/C;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,oEAAoE;IACpE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpC,4FAA4F;IAC5F,aAAa,EAAE,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CACvG;AAED,eAAO,MAAM,WAAW,EAAE,YAGzB,CAAA;AAKD;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAkCnF,CAAA;AASD,eAAO,MAAM,OAAO,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,IAAI,CAmBvD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,QAAO,MAAM,GAAG,SA0CxC,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAAU,oBAGrC;IACD,KAAK,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAA;IACrC,MAAM,EAAE,MAAM,CAAA;CACf,KAAG,OAAO,CAAC,MAAM,CA4BjB,CAAA;AAED,qBAAa,eAAgB,YAAW,SAAS;;gBASnC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,SAAQ;IAW3D,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBnD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CA0CvB;AAuED;;;GAGG;AACH,eAAO,MAAM,OAAO,GAAI,QAAQ,MAAM,KAAG,MAAM,GAAG,SAWjD,CAAA"}
|