@imadtg/tgsm 0.0.6 → 0.0.7
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/dist/index.js +240 -58
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
5
|
+
import { once } from "events";
|
|
5
6
|
import process2 from "process";
|
|
6
7
|
import { Command } from "commander";
|
|
7
8
|
|
|
@@ -532,8 +533,15 @@ function previewText(text, limit = 80) {
|
|
|
532
533
|
import path3 from "path";
|
|
533
534
|
import { createInterface } from "readline/promises";
|
|
534
535
|
import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
535
|
-
import { TelegramClient, getMarkedPeerId } from "@mtcute/node";
|
|
536
|
+
import { TelegramClient, getMarkedPeerId, networkMiddlewares } from "@mtcute/node";
|
|
537
|
+
var TELEGRAM_FLOOD_WAIT_MAX_MS = 3e4;
|
|
538
|
+
var TELEGRAM_CONNECT_TIMEOUT_MS = 3e4;
|
|
539
|
+
var TELEGRAM_RPC_TIMEOUT_MS = 45e3;
|
|
540
|
+
var TELEGRAM_DESTROY_TIMEOUT_MS = 5e3;
|
|
536
541
|
var TelegramSource = class {
|
|
542
|
+
constructor(options = {}) {
|
|
543
|
+
this.options = options;
|
|
544
|
+
}
|
|
537
545
|
backend = "telegram";
|
|
538
546
|
async authLogin(accountDir, input) {
|
|
539
547
|
const config = {
|
|
@@ -542,12 +550,14 @@ var TelegramSource = class {
|
|
|
542
550
|
phone: input.phone
|
|
543
551
|
};
|
|
544
552
|
await saveTelegramConfig(accountDir, config);
|
|
545
|
-
|
|
553
|
+
this.debug("auth.config_saved", { account_dir: accountDir, phone: input.phone });
|
|
554
|
+
const client = createTelegramClient(accountDir, config, this.options);
|
|
546
555
|
const rl = createInterface({
|
|
547
556
|
input: process.stdin,
|
|
548
557
|
output: process.stderr
|
|
549
558
|
});
|
|
550
559
|
try {
|
|
560
|
+
this.debug("auth.start.begin", { account_dir: accountDir });
|
|
551
561
|
const user = await client.start({
|
|
552
562
|
phone: input.phone,
|
|
553
563
|
code: async () => {
|
|
@@ -567,6 +577,7 @@ var TelegramSource = class {
|
|
|
567
577
|
`);
|
|
568
578
|
}
|
|
569
579
|
});
|
|
580
|
+
this.debug("auth.start.done", { user_id: String(user.id), display_name: user.displayName });
|
|
570
581
|
return {
|
|
571
582
|
authenticated: true,
|
|
572
583
|
user: {
|
|
@@ -581,8 +592,10 @@ var TelegramSource = class {
|
|
|
581
592
|
retryable: false
|
|
582
593
|
});
|
|
583
594
|
} finally {
|
|
595
|
+
this.debug("auth.cleanup.begin");
|
|
584
596
|
rl.close();
|
|
585
|
-
await destroyClientQuietly(client);
|
|
597
|
+
const cleanup = await destroyClientQuietly(client);
|
|
598
|
+
this.debug("auth.cleanup.done", { status: cleanup });
|
|
586
599
|
}
|
|
587
600
|
}
|
|
588
601
|
async authStatus(accountDir) {
|
|
@@ -593,10 +606,16 @@ var TelegramSource = class {
|
|
|
593
606
|
user: null
|
|
594
607
|
};
|
|
595
608
|
}
|
|
596
|
-
const client = createTelegramClient(accountDir, config);
|
|
609
|
+
const client = createTelegramClient(accountDir, config, this.options);
|
|
597
610
|
try {
|
|
598
|
-
|
|
611
|
+
this.debug("auth_status.start.begin", { account_dir: accountDir });
|
|
612
|
+
await withTimeout(
|
|
613
|
+
client.start({}),
|
|
614
|
+
TELEGRAM_CONNECT_TIMEOUT_MS,
|
|
615
|
+
"Timed out while connecting to Telegram."
|
|
616
|
+
);
|
|
599
617
|
const me = await client.getMe();
|
|
618
|
+
this.debug("auth_status.start.done", { user_id: String(me.id), display_name: me.displayName });
|
|
600
619
|
return {
|
|
601
620
|
authenticated: true,
|
|
602
621
|
user: {
|
|
@@ -605,12 +624,15 @@ var TelegramSource = class {
|
|
|
605
624
|
}
|
|
606
625
|
};
|
|
607
626
|
} catch {
|
|
627
|
+
this.debug("auth_status.start.failed");
|
|
608
628
|
return {
|
|
609
629
|
authenticated: false,
|
|
610
630
|
user: null
|
|
611
631
|
};
|
|
612
632
|
} finally {
|
|
613
|
-
|
|
633
|
+
this.debug("auth_status.cleanup.begin");
|
|
634
|
+
const cleanup = await destroyClientQuietly(client);
|
|
635
|
+
this.debug("auth_status.cleanup.done", { status: cleanup });
|
|
614
636
|
}
|
|
615
637
|
}
|
|
616
638
|
async sync(accountDir) {
|
|
@@ -623,18 +645,36 @@ var TelegramSource = class {
|
|
|
623
645
|
suggestion: "Run `tgsm auth login` first."
|
|
624
646
|
});
|
|
625
647
|
}
|
|
626
|
-
const client = createTelegramClient(accountDir, config);
|
|
648
|
+
const client = createTelegramClient(accountDir, config, this.options);
|
|
627
649
|
try {
|
|
628
|
-
|
|
650
|
+
this.debug("sync.start.begin", { account_dir: accountDir });
|
|
651
|
+
const me = await withTimeout(
|
|
652
|
+
client.start({}),
|
|
653
|
+
TELEGRAM_CONNECT_TIMEOUT_MS,
|
|
654
|
+
"Timed out while connecting to Telegram."
|
|
655
|
+
);
|
|
656
|
+
this.debug("sync.start.done", { user_id: String(me.id), display_name: me.displayName });
|
|
629
657
|
const syncedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
658
|
+
this.debug("sync.dialogs.begin");
|
|
659
|
+
const dialogsResponse = await withTimeout(
|
|
660
|
+
client.call({
|
|
661
|
+
_: "messages.getSavedDialogs",
|
|
662
|
+
excludePinned: false,
|
|
663
|
+
offsetDate: 0,
|
|
664
|
+
offsetId: 0,
|
|
665
|
+
offsetPeer: { _: "inputPeerEmpty" },
|
|
666
|
+
limit: 1e3,
|
|
667
|
+
hash: 0
|
|
668
|
+
}, {
|
|
669
|
+
maxRetryCount: 5,
|
|
670
|
+
floodSleepThreshold: TELEGRAM_FLOOD_WAIT_MAX_MS
|
|
671
|
+
}),
|
|
672
|
+
TELEGRAM_RPC_TIMEOUT_MS,
|
|
673
|
+
"Timed out while fetching saved dialogs from Telegram."
|
|
674
|
+
);
|
|
675
|
+
this.debug("sync.dialogs.done", {
|
|
676
|
+
result_type: dialogsResponse._,
|
|
677
|
+
dialog_count: "dialogs" in dialogsResponse && Array.isArray(dialogsResponse.dialogs) ? dialogsResponse.dialogs.length : 0
|
|
638
678
|
});
|
|
639
679
|
if (dialogsResponse._ === "messages.savedDialogsNotModified") {
|
|
640
680
|
return {
|
|
@@ -661,7 +701,17 @@ var TelegramSource = class {
|
|
|
661
701
|
const savedPeerId = savedPeerIdFromPeer(dialog.peer, lookup.selfUserId);
|
|
662
702
|
const peerInput = await client.resolvePeer(getMarkedPeerId(dialog.peer));
|
|
663
703
|
const title = peerTitle(dialog.peer, lookup);
|
|
664
|
-
|
|
704
|
+
this.debug("sync.dialog_history.begin", {
|
|
705
|
+
saved_peer_id: savedPeerId,
|
|
706
|
+
title,
|
|
707
|
+
top_message_id: dialog.topMessage ?? null
|
|
708
|
+
});
|
|
709
|
+
const messages2 = await fetchSavedHistory(client, peerInput, lookup, this.options);
|
|
710
|
+
this.debug("sync.dialog_history.done", {
|
|
711
|
+
saved_peer_id: savedPeerId,
|
|
712
|
+
title,
|
|
713
|
+
message_count: messages2.length
|
|
714
|
+
});
|
|
665
715
|
const topMessage = messages2.find((message) => message.message_id === dialog.topMessage) ?? messages2[0] ?? null;
|
|
666
716
|
dialogs.push({
|
|
667
717
|
saved_peer_id: savedPeerId,
|
|
@@ -690,46 +740,113 @@ var TelegramSource = class {
|
|
|
690
740
|
synced_at: syncedAt
|
|
691
741
|
};
|
|
692
742
|
} catch (error) {
|
|
743
|
+
this.debug("sync.failed", {
|
|
744
|
+
message: error instanceof Error ? error.message : "Unknown sync error"
|
|
745
|
+
});
|
|
693
746
|
throw new TgsmError({
|
|
694
747
|
code: "TELEGRAM_SYNC_FAILED",
|
|
695
748
|
message: error instanceof Error ? error.message : "Telegram sync failed.",
|
|
696
749
|
retryable: true
|
|
697
750
|
});
|
|
698
751
|
} finally {
|
|
699
|
-
|
|
752
|
+
this.debug("sync.cleanup.begin");
|
|
753
|
+
const cleanup = await destroyClientQuietly(client);
|
|
754
|
+
this.debug("sync.cleanup.done", { status: cleanup });
|
|
700
755
|
}
|
|
701
756
|
}
|
|
757
|
+
debug(event, fields = {}) {
|
|
758
|
+
if (!this.options.debug) return;
|
|
759
|
+
this.options.logger?.(event, fields);
|
|
760
|
+
}
|
|
702
761
|
};
|
|
703
|
-
function createTelegramClient(accountDir, config) {
|
|
704
|
-
|
|
762
|
+
function createTelegramClient(accountDir, config, options) {
|
|
763
|
+
const client = new TelegramClient({
|
|
705
764
|
apiId: config.apiId,
|
|
706
765
|
apiHash: config.apiHash,
|
|
707
766
|
storage: path3.join(accountDir, "mtcute-session"),
|
|
708
|
-
logLevel: 0
|
|
767
|
+
logLevel: 0,
|
|
768
|
+
network: {
|
|
769
|
+
middlewares: networkMiddlewares.basic({
|
|
770
|
+
floodWaiter: {
|
|
771
|
+
maxWait: TELEGRAM_FLOOD_WAIT_MAX_MS,
|
|
772
|
+
maxRetries: 5,
|
|
773
|
+
onBeforeWait: (ctx, seconds) => {
|
|
774
|
+
options.logger?.("telegram.flood_wait", {
|
|
775
|
+
seconds,
|
|
776
|
+
method: ctx.request._
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
})
|
|
781
|
+
}
|
|
709
782
|
});
|
|
783
|
+
if (options.debug) {
|
|
784
|
+
client.onConnectionState.add((state) => {
|
|
785
|
+
options.logger?.("telegram.connection_state", { state });
|
|
786
|
+
});
|
|
787
|
+
client.onError.add((error) => {
|
|
788
|
+
options.logger?.("telegram.client_error", {
|
|
789
|
+
message: error.message,
|
|
790
|
+
name: error.name
|
|
791
|
+
});
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
return client;
|
|
710
795
|
}
|
|
711
796
|
async function destroyClientQuietly(client) {
|
|
712
797
|
try {
|
|
713
|
-
await
|
|
714
|
-
|
|
798
|
+
await withTimeout(
|
|
799
|
+
client.destroy(),
|
|
800
|
+
TELEGRAM_DESTROY_TIMEOUT_MS,
|
|
801
|
+
"Timed out while closing Telegram client."
|
|
802
|
+
);
|
|
803
|
+
return "destroyed";
|
|
804
|
+
} catch (error) {
|
|
805
|
+
if (error instanceof Error && error.message === "Timed out while closing Telegram client.") {
|
|
806
|
+
return "timed_out";
|
|
807
|
+
}
|
|
808
|
+
return "failed";
|
|
715
809
|
}
|
|
716
810
|
}
|
|
717
|
-
async function fetchSavedHistory(client, peer, lookup) {
|
|
811
|
+
async function fetchSavedHistory(client, peer, lookup, options) {
|
|
718
812
|
let offsetId = 0;
|
|
719
813
|
let offsetDate = 0;
|
|
720
814
|
const records = /* @__PURE__ */ new Map();
|
|
815
|
+
let pages = 0;
|
|
721
816
|
while (true) {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
817
|
+
pages += 1;
|
|
818
|
+
if (options.debug) {
|
|
819
|
+
options.logger?.("sync.dialog_history.page.begin", {
|
|
820
|
+
page: pages,
|
|
821
|
+
offset_id: offsetId,
|
|
822
|
+
offset_date: offsetDate
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
const history = await withTimeout(
|
|
826
|
+
client.call({
|
|
827
|
+
_: "messages.getSavedHistory",
|
|
828
|
+
peer,
|
|
829
|
+
offsetId,
|
|
830
|
+
offsetDate,
|
|
831
|
+
addOffset: 0,
|
|
832
|
+
limit: 100,
|
|
833
|
+
maxId: 0,
|
|
834
|
+
minId: 0,
|
|
835
|
+
hash: 0
|
|
836
|
+
}, {
|
|
837
|
+
maxRetryCount: 5,
|
|
838
|
+
floodSleepThreshold: TELEGRAM_FLOOD_WAIT_MAX_MS
|
|
839
|
+
}),
|
|
840
|
+
TELEGRAM_RPC_TIMEOUT_MS,
|
|
841
|
+
"Timed out while fetching saved history from Telegram."
|
|
842
|
+
);
|
|
843
|
+
if (options.debug) {
|
|
844
|
+
options.logger?.("sync.dialog_history.page.done", {
|
|
845
|
+
page: pages,
|
|
846
|
+
result_type: history._,
|
|
847
|
+
message_count: "messages" in history ? history.messages.length : 0
|
|
848
|
+
});
|
|
849
|
+
}
|
|
733
850
|
if (!("messages" in history)) {
|
|
734
851
|
break;
|
|
735
852
|
}
|
|
@@ -884,6 +1001,23 @@ async function saveTelegramConfig(accountDir, config) {
|
|
|
884
1001
|
"utf8"
|
|
885
1002
|
);
|
|
886
1003
|
}
|
|
1004
|
+
async function withTimeout(promise, timeoutMs, message) {
|
|
1005
|
+
let timer;
|
|
1006
|
+
try {
|
|
1007
|
+
return await Promise.race([
|
|
1008
|
+
promise,
|
|
1009
|
+
new Promise((_, reject) => {
|
|
1010
|
+
timer = setTimeout(() => {
|
|
1011
|
+
reject(new Error(message));
|
|
1012
|
+
}, timeoutMs);
|
|
1013
|
+
})
|
|
1014
|
+
]);
|
|
1015
|
+
} finally {
|
|
1016
|
+
if (timer) {
|
|
1017
|
+
clearTimeout(timer);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
887
1021
|
|
|
888
1022
|
// src/format.ts
|
|
889
1023
|
function formatSyncResult(result) {
|
|
@@ -981,7 +1115,7 @@ function renderNode(node, prefix, isLast, lines) {
|
|
|
981
1115
|
// package.json
|
|
982
1116
|
var package_default = {
|
|
983
1117
|
name: "@imadtg/tgsm",
|
|
984
|
-
version: "0.0.
|
|
1118
|
+
version: "0.0.7",
|
|
985
1119
|
type: "module",
|
|
986
1120
|
description: "A retrieval-first CLI for navigating Telegram Saved Messages as structured, agent-readable context.",
|
|
987
1121
|
license: "MIT",
|
|
@@ -1030,7 +1164,7 @@ var package_default = {
|
|
|
1030
1164
|
|
|
1031
1165
|
// src/index.ts
|
|
1032
1166
|
var program = new Command();
|
|
1033
|
-
program.name("tgsm").description("Retrieval-first Telegram Saved Messages CLI").version(package_default.version, "-V, --version", "Display the tgsm version").option("--json", "Emit JSON instead of default text output").option("--backend <backend>", "telegram or fixture", "telegram").option("--fixture <path>", "Fixture path for the fixture backend").option("--home <path>", "Override TGSM home directory").option("--account <name>", "Account namespace", "default");
|
|
1167
|
+
program.name("tgsm").description("Retrieval-first Telegram Saved Messages CLI").version(package_default.version, "-V, --version", "Display the tgsm version").option("--json", "Emit JSON instead of default text output").option("--debug", "Emit debug telemetry to stderr").option("--backend <backend>", "telegram or fixture", "telegram").option("--fixture <path>", "Fixture path for the fixture backend").option("--home <path>", "Override TGSM home directory").option("--account <name>", "Account namespace", "default");
|
|
1034
1168
|
program.command("auth").description("Telegram auth commands").addCommand(
|
|
1035
1169
|
new Command("login").action(async (_, command) => {
|
|
1036
1170
|
await withService(command.optsWithGlobals(), async (service, options) => {
|
|
@@ -1117,29 +1251,11 @@ program.command("threads").description("Thread commands").addCommand(
|
|
|
1117
1251
|
});
|
|
1118
1252
|
})
|
|
1119
1253
|
);
|
|
1120
|
-
|
|
1121
|
-
const tgsmError = error instanceof TgsmError ? error : new TgsmError({
|
|
1122
|
-
code: "UNEXPECTED_ERROR",
|
|
1123
|
-
message: error instanceof Error ? error.message : "Unexpected error",
|
|
1124
|
-
retryable: false
|
|
1125
|
-
});
|
|
1126
|
-
const options = program.opts();
|
|
1127
|
-
if (options.json) {
|
|
1128
|
-
process2.stdout.write(`${JSON.stringify(tgsmError.toJSON(), null, 2)}
|
|
1129
|
-
`);
|
|
1130
|
-
} else {
|
|
1131
|
-
process2.stderr.write(`error: ${tgsmError.message}
|
|
1132
|
-
`);
|
|
1133
|
-
if (tgsmError.suggestion) {
|
|
1134
|
-
process2.stderr.write(`suggestion: ${tgsmError.suggestion}
|
|
1135
|
-
`);
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
process2.exitCode = errorCode(tgsmError.code);
|
|
1139
|
-
});
|
|
1254
|
+
void main();
|
|
1140
1255
|
async function withService(options, fn) {
|
|
1141
1256
|
const normalized = {
|
|
1142
1257
|
json: Boolean(options.json),
|
|
1258
|
+
debug: Boolean(options.debug),
|
|
1143
1259
|
backend: options.backend ?? "telegram",
|
|
1144
1260
|
fixture: options.fixture ?? "",
|
|
1145
1261
|
home: options.home ?? "",
|
|
@@ -1153,6 +1269,13 @@ async function withService(options, fn) {
|
|
|
1153
1269
|
homeDir: normalized.home || void 0,
|
|
1154
1270
|
account: normalized.account
|
|
1155
1271
|
});
|
|
1272
|
+
debugLog(normalized, "cli.service.setup", {
|
|
1273
|
+
backend: normalized.backend,
|
|
1274
|
+
account: normalized.account,
|
|
1275
|
+
cache_path: cachePath,
|
|
1276
|
+
home: normalized.home || void 0,
|
|
1277
|
+
fixture: normalized.fixture || void 0
|
|
1278
|
+
});
|
|
1156
1279
|
const source = resolveSource(normalized);
|
|
1157
1280
|
const service = new TgsmService({
|
|
1158
1281
|
cachePath,
|
|
@@ -1171,7 +1294,12 @@ function resolveSource(options) {
|
|
|
1171
1294
|
}
|
|
1172
1295
|
return new FixtureSource(options.fixture);
|
|
1173
1296
|
}
|
|
1174
|
-
return new TelegramSource(
|
|
1297
|
+
return new TelegramSource({
|
|
1298
|
+
debug: options.debug,
|
|
1299
|
+
logger: (event, fields = {}) => {
|
|
1300
|
+
debugLog(options, event, fields);
|
|
1301
|
+
}
|
|
1302
|
+
});
|
|
1175
1303
|
}
|
|
1176
1304
|
function emit(value, options, format) {
|
|
1177
1305
|
if (options.json) {
|
|
@@ -1187,4 +1315,58 @@ function errorCode(code) {
|
|
|
1187
1315
|
if (code.includes("SYNC") || code.includes("TELEGRAM")) return 2;
|
|
1188
1316
|
return 1;
|
|
1189
1317
|
}
|
|
1318
|
+
function debugLog(options, event, fields = {}) {
|
|
1319
|
+
if (!options.debug) return;
|
|
1320
|
+
const detail = Object.entries(fields).filter(([, value]) => value !== void 0).map(([key, value]) => `${key}=${formatDebugValue(value)}`).join(" ");
|
|
1321
|
+
process2.stderr.write(
|
|
1322
|
+
`[tgsm debug ${(/* @__PURE__ */ new Date()).toISOString()}] ${event}${detail ? ` ${detail}` : ""}
|
|
1323
|
+
`
|
|
1324
|
+
);
|
|
1325
|
+
}
|
|
1326
|
+
function formatDebugValue(value) {
|
|
1327
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
1328
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
1329
|
+
if (value === null) return "null";
|
|
1330
|
+
try {
|
|
1331
|
+
return JSON.stringify(value);
|
|
1332
|
+
} catch {
|
|
1333
|
+
return String(value);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
async function main() {
|
|
1337
|
+
let exitCode = 0;
|
|
1338
|
+
try {
|
|
1339
|
+
await program.parseAsync(process2.argv);
|
|
1340
|
+
} catch (error) {
|
|
1341
|
+
const tgsmError = error instanceof TgsmError ? error : new TgsmError({
|
|
1342
|
+
code: "UNEXPECTED_ERROR",
|
|
1343
|
+
message: error instanceof Error ? error.message : "Unexpected error",
|
|
1344
|
+
retryable: false
|
|
1345
|
+
});
|
|
1346
|
+
const options = program.opts();
|
|
1347
|
+
if (options.json) {
|
|
1348
|
+
process2.stdout.write(`${JSON.stringify(tgsmError.toJSON(), null, 2)}
|
|
1349
|
+
`);
|
|
1350
|
+
} else {
|
|
1351
|
+
process2.stderr.write(`error: ${tgsmError.message}
|
|
1352
|
+
`);
|
|
1353
|
+
if (tgsmError.suggestion) {
|
|
1354
|
+
process2.stderr.write(`suggestion: ${tgsmError.suggestion}
|
|
1355
|
+
`);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
exitCode = errorCode(tgsmError.code);
|
|
1359
|
+
}
|
|
1360
|
+
await flushStreams();
|
|
1361
|
+
process2.exit(exitCode);
|
|
1362
|
+
}
|
|
1363
|
+
async function flushStreams() {
|
|
1364
|
+
await Promise.all([flushStream(process2.stdout), flushStream(process2.stderr)]);
|
|
1365
|
+
}
|
|
1366
|
+
async function flushStream(stream) {
|
|
1367
|
+
if (!stream.writableNeedDrain) {
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
await once(stream, "drain");
|
|
1371
|
+
}
|
|
1190
1372
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../../core/src/errors.ts","../../core/src/paths.ts","../../core/src/cache.ts","../../core/src/fixture.ts","../../core/src/service.ts","../../core/src/telegram.ts","../src/format.ts","../package.json"],"sourcesContent":["import { createInterface } from 'node:readline/promises'\nimport process from 'node:process'\nimport { Command } from 'commander'\nimport {\n FixtureSource,\n TgsmError,\n TgsmService,\n TelegramSource,\n ensureAccountDir,\n getCachePath,\n type GetMessageOptions,\n type ListMessagesOptions,\n type TgsmSourceAdapter,\n} from '@tgsm/core'\nimport {\n formatAuthStatus,\n formatContextBundle,\n formatMessagesPage,\n formatSavedDialogs,\n formatSyncResult,\n formatThread,\n} from './format'\nimport pkg from '../package.json'\n\ninterface GlobalOptions {\n json?: boolean\n backend?: 'telegram' | 'fixture'\n fixture?: string\n home?: string\n account?: string\n}\n\nconst program = new Command()\n\nprogram\n .name('tgsm')\n .description('Retrieval-first Telegram Saved Messages CLI')\n .version(pkg.version, '-V, --version', 'Display the tgsm version')\n .option('--json', 'Emit JSON instead of default text output')\n .option('--backend <backend>', 'telegram or fixture', 'telegram')\n .option('--fixture <path>', 'Fixture path for the fixture backend')\n .option('--home <path>', 'Override TGSM home directory')\n .option('--account <name>', 'Account namespace', 'default')\n\nprogram\n .command('auth')\n .description('Telegram auth commands')\n .addCommand(\n new Command('login').action(async (_, command) => {\n await withService(command.optsWithGlobals(), async (service, options) => {\n if (options.backend !== 'telegram') {\n throw new TgsmError({\n code: 'AUTH_UNSUPPORTED',\n message: 'Auth login is only supported with the telegram backend.',\n retryable: false,\n })\n }\n\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n })\n\n try {\n const apiId = Number(await rl.question('API ID: '))\n const apiHash = await rl.question('API Hash: ')\n const phone = await rl.question('Phone: ')\n\n const result = await service.authLogin({\n apiId,\n apiHash,\n phone,\n })\n\n emit(result, options, formatAuthStatus)\n } finally {\n rl.close()\n }\n })\n }),\n )\n .addCommand(\n new Command('status').action(async (_, command) => {\n await withService(command.optsWithGlobals(), async (service, options) => {\n emit(await service.authStatus(), options, formatAuthStatus)\n })\n }),\n )\n\nprogram.command('sync').action(async (_, command) => {\n await withService(command.optsWithGlobals(), async (service, options) => {\n emit(await service.sync(), options, formatSyncResult)\n })\n})\n\nprogram\n .command('saved-dialogs')\n .description('Saved dialog commands')\n .addCommand(\n new Command('list').action(async (_, command) => {\n await withService(command.optsWithGlobals(), async (service, options) => {\n emit(await service.listSavedDialogs(), options, formatSavedDialogs)\n })\n }),\n )\n\nconst messages = program.command('messages').description('Message commands')\n\nmessages\n .command('list')\n .option('--dialog <savedPeerId>')\n .option('--search <query>')\n .option('--limit <number>', 'Page size', '20')\n .option('--cursor <cursor>')\n .action(async (commandOptions, command) => {\n await withService(command.optsWithGlobals(), async (service, options) => {\n const result = await service.listMessages({\n dialog: commandOptions.dialog,\n search: commandOptions.search,\n limit: Number(commandOptions.limit),\n cursor: commandOptions.cursor,\n } satisfies ListMessagesOptions)\n emit(result, options, formatMessagesPage)\n })\n })\n\nmessages\n .command('get')\n .argument('<id>')\n .option('--dialog <savedPeerId>')\n .action(async (id, commandOptions, command) => {\n await withService(command.optsWithGlobals(), async (service, options) => {\n const result = await service.getMessage(Number(id), {\n dialog: commandOptions.dialog,\n } satisfies GetMessageOptions)\n emit(result, options, formatContextBundle)\n })\n })\n\nmessages\n .command('context')\n .argument('<id>')\n .option('--dialog <savedPeerId>')\n .action(async (id, commandOptions, command) => {\n await withService(command.optsWithGlobals(), async (service, options) => {\n const result = await service.getContext(Number(id), {\n dialog: commandOptions.dialog,\n } satisfies GetMessageOptions)\n emit(result, options, formatContextBundle)\n })\n })\n\nprogram\n .command('threads')\n .description('Thread commands')\n .addCommand(\n new Command('inspect')\n .argument('<id>')\n .option('--dialog <savedPeerId>')\n .action(async (id, commandOptions, command) => {\n await withService(command.optsWithGlobals(), async (service, options) => {\n const result = await service.inspectThread(Number(id), {\n dialog: commandOptions.dialog,\n } satisfies GetMessageOptions)\n emit(result, options, formatThread)\n })\n }),\n )\n\nprogram.parseAsync(process.argv).catch((error: unknown) => {\n const tgsmError =\n error instanceof TgsmError\n ? error\n : new TgsmError({\n code: 'UNEXPECTED_ERROR',\n message: error instanceof Error ? error.message : 'Unexpected error',\n retryable: false,\n })\n\n const options = program.opts<GlobalOptions>()\n if (options.json) {\n process.stdout.write(`${JSON.stringify(tgsmError.toJSON(), null, 2)}\\n`)\n } else {\n process.stderr.write(`error: ${tgsmError.message}\\n`)\n if (tgsmError.suggestion) {\n process.stderr.write(`suggestion: ${tgsmError.suggestion}\\n`)\n }\n }\n\n process.exitCode = errorCode(tgsmError.code)\n})\n\nasync function withService(\n options: GlobalOptions,\n fn: (service: TgsmService, options: Required<GlobalOptions>) => Promise<void>,\n): Promise<void> {\n const normalized: Required<GlobalOptions> = {\n json: Boolean(options.json),\n backend: (options.backend ?? 'telegram') as 'telegram' | 'fixture',\n fixture: options.fixture ?? '',\n home: options.home ?? '',\n account: options.account ?? 'default',\n }\n\n await ensureAccountDir({\n homeDir: normalized.home || undefined,\n account: normalized.account,\n })\n\n const cachePath = getCachePath({\n homeDir: normalized.home || undefined,\n account: normalized.account,\n })\n\n const source = resolveSource(normalized)\n const service = new TgsmService({\n cachePath,\n source,\n })\n\n await fn(service, normalized)\n}\n\nfunction resolveSource(options: Required<GlobalOptions>): TgsmSourceAdapter {\n if (options.backend === 'fixture') {\n if (!options.fixture) {\n throw new TgsmError({\n code: 'FIXTURE_REQUIRED',\n message: 'The fixture backend requires --fixture <path>.',\n retryable: false,\n })\n }\n return new FixtureSource(options.fixture)\n }\n\n return new TelegramSource()\n}\n\nfunction emit<T>(\n value: T,\n options: Required<GlobalOptions>,\n format?: (value: T) => string,\n): void {\n if (options.json) {\n process.stdout.write(`${JSON.stringify(value, null, 2)}\\n`)\n return\n }\n\n process.stdout.write(`${format ? format(value) : String(value)}\\n`)\n}\n\nfunction errorCode(code: string): number {\n if (code.startsWith('AUTH')) return 3\n if (code.includes('SYNC') || code.includes('TELEGRAM')) return 2\n return 1\n}\n","import type { OperationErrorShape } from './types'\n\nexport class TgsmError extends Error {\n readonly code: string\n readonly retryable: boolean\n readonly suggestion?: string\n\n constructor(shape: OperationErrorShape) {\n super(shape.message)\n this.name = 'TgsmError'\n this.code = shape.code\n this.retryable = shape.retryable\n this.suggestion = shape.suggestion\n }\n\n toJSON(): OperationErrorShape {\n return {\n code: this.code,\n message: this.message,\n retryable: this.retryable,\n suggestion: this.suggestion,\n }\n }\n}\n\nexport function asTgsmError(error: unknown): TgsmError {\n if (error instanceof TgsmError) {\n return error\n }\n\n if (error instanceof Error) {\n return new TgsmError({\n code: 'UNEXPECTED_ERROR',\n message: error.message,\n retryable: false,\n })\n }\n\n return new TgsmError({\n code: 'UNEXPECTED_ERROR',\n message: 'Unexpected error',\n retryable: false,\n })\n}\n","import os from 'node:os'\nimport path from 'node:path'\nimport { mkdir } from 'node:fs/promises'\n\nexport interface PathOptions {\n homeDir?: string\n account?: string\n}\n\nexport function getHomeDir(homeDir?: string): string {\n return homeDir ?? process.env.TGSM_HOME ?? path.join(os.homedir(), '.tgsm')\n}\n\nexport function getAccountName(account?: string): string {\n return account ?? 'default'\n}\n\nexport function getAccountDir(options: PathOptions = {}): string {\n return path.join(getHomeDir(options.homeDir), getAccountName(options.account))\n}\n\nexport function getCachePath(options: PathOptions = {}): string {\n return path.join(getAccountDir(options), 'cache.json')\n}\n\nexport function getTelegramConfigPath(options: PathOptions = {}): string {\n return path.join(getAccountDir(options), 'telegram.json')\n}\n\nexport function getTelegramSessionPath(options: PathOptions = {}): string {\n return path.join(getAccountDir(options), 'mtcute-session')\n}\n\nexport async function ensureAccountDir(options: PathOptions = {}): Promise<string> {\n const accountDir = getAccountDir(options)\n await mkdir(accountDir, { recursive: true })\n return accountDir\n}\n","import { readFile, writeFile } from 'node:fs/promises'\nimport type { CacheState, SourceSnapshot } from './types'\n\nexport const EMPTY_CACHE: CacheState = {\n version: 1,\n backend: 'fixture',\n account: null,\n synced_at: null,\n dialogs: [],\n messages: [],\n}\n\nexport async function readCache(cachePath: string): Promise<CacheState> {\n try {\n const raw = await readFile(cachePath, 'utf8')\n return JSON.parse(raw) as CacheState\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return EMPTY_CACHE\n }\n\n throw error\n }\n}\n\nexport async function writeCache(cachePath: string, snapshot: SourceSnapshot): Promise<void> {\n const state: CacheState = {\n version: 1,\n backend: snapshot.backend,\n account: snapshot.account,\n synced_at: snapshot.synced_at,\n dialogs: snapshot.dialogs,\n messages: snapshot.messages,\n }\n\n await writeFile(cachePath, `${JSON.stringify(state, null, 2)}\\n`, 'utf8')\n}\n","import { readFile } from 'node:fs/promises'\nimport { TgsmError } from './errors'\nimport type { AuthStatus, SourceSnapshot, TgsmSourceAdapter } from './types'\n\ninterface FixtureFile {\n account?: SourceSnapshot['account']\n dialogs: SourceSnapshot['dialogs']\n messages: SourceSnapshot['messages']\n}\n\nexport class FixtureSource implements TgsmSourceAdapter {\n readonly backend = 'fixture' as const\n\n constructor(private readonly fixturePath: string) {}\n\n async sync(): Promise<SourceSnapshot> {\n const raw = await readFile(this.fixturePath, 'utf8')\n const parsed = JSON.parse(raw) as FixtureFile\n\n if (!Array.isArray(parsed.dialogs) || !Array.isArray(parsed.messages)) {\n throw new TgsmError({\n code: 'INVALID_FIXTURE',\n message: `Fixture at ${this.fixturePath} is missing dialogs/messages arrays`,\n retryable: false,\n })\n }\n\n const syncedAt = new Date().toISOString()\n\n return {\n backend: this.backend,\n account:\n parsed.account ??\n ({\n id: 'fixture',\n display_name: 'Fixture Account',\n } as const),\n dialogs: parsed.dialogs.map((dialog) => ({\n ...dialog,\n last_synced_at: syncedAt,\n })),\n messages: parsed.messages,\n synced_at: syncedAt,\n }\n }\n\n async authStatus(): Promise<AuthStatus> {\n return {\n authenticated: true,\n user: {\n id: 'fixture',\n display_name: 'Fixture Account',\n },\n }\n }\n}\n","import path from 'node:path'\nimport { readCache, writeCache } from './cache'\nimport { TgsmError } from './errors'\nimport type {\n BackreplyEdgeSummary,\n CacheMessageRecord,\n CacheState,\n ContextMessage,\n GetMessageOptions,\n ListMessagesOptions,\n MessageContextBundle,\n MessageEnvelope,\n MessageListItem,\n MessageRef,\n SavedDialogSummary,\n SearchResultPage,\n SyncResult,\n ThreadInspectNode,\n ThreadInspectResult,\n TgsmSourceAdapter,\n} from './types'\n\nexport interface TgsmServiceOptions {\n cachePath: string\n source?: TgsmSourceAdapter\n}\n\ninterface Indexes {\n dialogsById: Map<string, SavedDialogSummary>\n messagesByKey: Map<string, CacheMessageRecord>\n messagesByGlobalId: Map<number, CacheMessageRecord[]>\n messagesByDialog: Map<string, CacheMessageRecord[]>\n backreplyIndex: Map<string, CacheMessageRecord[]>\n}\n\nconst DEFAULT_CHRONOLOGY_LIMIT = 20\n\nexport class TgsmService {\n constructor(private readonly options: TgsmServiceOptions) {}\n\n async authLogin(input: Parameters<NonNullable<TgsmSourceAdapter['authLogin']>>[1]) {\n if (!this.options.source?.authLogin) {\n throw new TgsmError({\n code: 'AUTH_UNSUPPORTED',\n message: 'This backend does not support auth login.',\n retryable: false,\n })\n }\n\n return this.options.source.authLogin(this.accountDir(), input)\n }\n\n async authStatus() {\n if (!this.options.source?.authStatus) {\n return {\n authenticated: false,\n user: null,\n }\n }\n\n return this.options.source.authStatus(this.accountDir())\n }\n\n async sync(): Promise<SyncResult> {\n if (!this.options.source) {\n throw new TgsmError({\n code: 'SYNC_UNAVAILABLE',\n message: 'No source backend configured for sync.',\n retryable: false,\n })\n }\n\n const snapshot = await this.options.source.sync(this.accountDir())\n await writeCache(this.options.cachePath, snapshot)\n\n return {\n backend: snapshot.backend,\n synced_at: snapshot.synced_at,\n synced_dialogs: snapshot.dialogs.length,\n synced_messages: snapshot.messages.length,\n }\n }\n\n async listSavedDialogs(): Promise<SavedDialogSummary[]> {\n const cache = await this.loadCache()\n return [...cache.dialogs].sort((a, b) => {\n const left = a.last_synced_at ?? ''\n const right = b.last_synced_at ?? ''\n return right.localeCompare(left) || a.saved_peer_id.localeCompare(b.saved_peer_id)\n })\n }\n\n async listMessages(options: ListMessagesOptions = {}): Promise<SearchResultPage<MessageListItem>> {\n const cache = await this.loadCache()\n const indexes = this.buildIndexes(cache)\n const dialogId = options.dialog ?? null\n\n if (dialogId && !indexes.dialogsById.has(dialogId)) {\n throw new TgsmError({\n code: 'DIALOG_NOT_FOUND',\n message: `Saved dialog ${dialogId} was not found.`,\n retryable: false,\n suggestion: 'Run `tgsm saved-dialogs list` to inspect available dialogs.',\n })\n }\n\n const scoped = dialogId\n ? indexes.messagesByDialog.get(dialogId) ?? []\n : cache.messages\n\n const filtered = options.search\n ? scoped.filter((message: CacheMessageRecord) => matchesSearch(message, options.search!))\n : scoped\n\n const sorted = [...filtered].sort(compareByDateDesc)\n const limit = Math.max(1, options.limit ?? 20)\n const offset = decodeCursor(options.cursor)\n const slice = sorted.slice(offset, offset + limit)\n\n return {\n items: slice.map((message) => this.toMessageListItem(message, indexes)),\n scope: dialogId ? 'saved_dialog' : 'all_saved_dialogs',\n saved_peer_id: dialogId,\n next_cursor: offset + limit < sorted.length ? encodeCursor(offset + limit) : null,\n result_count: filtered.length,\n }\n }\n\n async getMessage(messageId: number, options: GetMessageOptions = {}): Promise<MessageContextBundle> {\n const cache = await this.loadCache()\n const indexes = this.buildIndexes(cache)\n const target = this.findMessage(indexes, messageId, options.dialog)\n return this.buildContextBundle(target, indexes)\n }\n\n async getContext(messageId: number, options: GetMessageOptions = {}): Promise<MessageContextBundle> {\n return this.getMessage(messageId, options)\n }\n\n async inspectThread(messageId: number, options: GetMessageOptions = {}): Promise<ThreadInspectResult> {\n const cache = await this.loadCache()\n const indexes = this.buildIndexes(cache)\n const target = this.findMessage(indexes, messageId, options.dialog)\n const root = this.findThreadRoot(target, indexes)\n const dialog = indexes.dialogsById.get(root.saved_peer_id)\n\n if (!dialog) {\n throw new TgsmError({\n code: 'DIALOG_NOT_FOUND',\n message: `Saved dialog ${root.saved_peer_id} was not found.`,\n retryable: false,\n })\n }\n\n return {\n dialog,\n root: this.toEnvelope(root, indexes),\n nodes: [this.buildThreadNode(root, indexes, 0)],\n }\n }\n\n private accountDir(): string {\n return path.dirname(this.options.cachePath)\n }\n\n private async loadCache(): Promise<CacheState> {\n return readCache(this.options.cachePath)\n }\n\n private buildIndexes(cache: CacheState): Indexes {\n const dialogsById = new Map<string, SavedDialogSummary>(\n cache.dialogs.map((dialog: SavedDialogSummary) => [dialog.saved_peer_id, dialog]),\n )\n const messagesByKey = new Map<string, CacheMessageRecord>()\n const messagesByGlobalId = new Map<number, CacheMessageRecord[]>()\n const messagesByDialog = new Map<string, CacheMessageRecord[]>()\n const backreplyIndex = new Map<string, CacheMessageRecord[]>()\n\n for (const message of cache.messages) {\n messagesByKey.set(makeMessageKey(message.saved_peer_id, message.message_id), message)\n\n const existingGlobal = messagesByGlobalId.get(message.message_id) ?? []\n existingGlobal.push(message)\n messagesByGlobalId.set(message.message_id, existingGlobal)\n\n const dialogMessages = messagesByDialog.get(message.saved_peer_id) ?? []\n dialogMessages.push(message)\n messagesByDialog.set(message.saved_peer_id, dialogMessages)\n\n if (message.reply_to_message_id !== null) {\n const replyDialog = message.reply_to_saved_peer_id ?? message.saved_peer_id\n const key = makeMessageKey(replyDialog, message.reply_to_message_id)\n const children = backreplyIndex.get(key) ?? []\n children.push(message)\n backreplyIndex.set(key, children)\n }\n }\n\n for (const records of messagesByDialog.values()) {\n records.sort(compareByDateAsc)\n }\n\n for (const records of backreplyIndex.values()) {\n records.sort(compareByDateAsc)\n }\n\n return {\n dialogsById,\n messagesByKey,\n messagesByGlobalId,\n messagesByDialog,\n backreplyIndex,\n }\n }\n\n private findMessage(indexes: Indexes, messageId: number, dialog?: string): CacheMessageRecord {\n if (dialog) {\n const scoped = indexes.messagesByKey.get(makeMessageKey(dialog, messageId))\n if (!scoped) {\n throw new TgsmError({\n code: 'MESSAGE_NOT_FOUND',\n message: `Message ${messageId} was not found in dialog ${dialog}.`,\n retryable: false,\n suggestion: 'Run `tgsm messages list --dialog <saved_peer_id>` to inspect the dialog.',\n })\n }\n return scoped\n }\n\n const candidates = indexes.messagesByGlobalId.get(messageId) ?? []\n\n if (candidates.length === 0) {\n throw new TgsmError({\n code: 'MESSAGE_NOT_FOUND',\n message: `Message ${messageId} was not found in the selected scope.`,\n retryable: false,\n suggestion: 'Run `tgsm messages list` or narrow the dialog scope.',\n })\n }\n\n if (candidates.length > 1) {\n throw new TgsmError({\n code: 'AMBIGUOUS_MESSAGE_ID',\n message: `Message ID ${messageId} exists in multiple saved dialogs.`,\n retryable: false,\n suggestion: 'Pass `--dialog <saved_peer_id>` to disambiguate.',\n })\n }\n\n return candidates[0]!\n }\n\n private buildContextBundle(target: CacheMessageRecord, indexes: Indexes): MessageContextBundle {\n const dialog = indexes.dialogsById.get(target.saved_peer_id)\n\n if (!dialog) {\n throw new TgsmError({\n code: 'DIALOG_NOT_FOUND',\n message: `Saved dialog ${target.saved_peer_id} was not found.`,\n retryable: false,\n })\n }\n\n const chronological = indexes.messagesByDialog.get(target.saved_peer_id) ?? []\n const targetIndex = chronological.findIndex(\n (message) => message.message_id === target.message_id && message.saved_peer_id === target.saved_peer_id,\n )\n\n const maxEachSide = Math.floor(DEFAULT_CHRONOLOGY_LIMIT / 2)\n const before = chronological.slice(Math.max(0, targetIndex - maxEachSide), targetIndex)\n const after = chronological.slice(targetIndex + 1, targetIndex + 1 + maxEachSide)\n\n const replyParent = target.reply_to_message_id\n ? indexes.messagesByKey.get(\n makeMessageKey(target.reply_to_saved_peer_id ?? target.saved_peer_id, target.reply_to_message_id),\n ) ?? null\n : null\n\n const directBackreplies =\n indexes.backreplyIndex.get(makeMessageKey(target.saved_peer_id, target.message_id)) ?? []\n\n const notes: string[] = []\n if (target.reply_to_message_id !== null && !replyParent) {\n notes.push(`Reply target #${target.reply_to_message_id} could not be resolved in cache.`)\n }\n\n const contexts = new Map<string, ContextMessage>()\n const pushContext = (\n message: CacheMessageRecord,\n role: ContextMessage['context_roles'][number],\n ): void => {\n const key = makeMessageKey(message.saved_peer_id, message.message_id)\n const existing = contexts.get(key)\n if (existing) {\n if (!existing.context_roles.includes(role)) {\n existing.context_roles.push(role)\n }\n return\n }\n\n contexts.set(key, {\n message: this.toEnvelope(message, indexes),\n context_roles: [role],\n })\n }\n\n for (const message of before) pushContext(message, 'chronology_before')\n if (replyParent) pushContext(replyParent, 'reply_parent')\n pushContext(target, 'target')\n for (const message of directBackreplies) pushContext(message, 'backreply_child')\n for (const message of after) pushContext(message, 'chronology_after')\n\n const contextMessages = [...contexts.values()].sort((left, right) => {\n const rank = (roles: ContextMessage['context_roles']): number => {\n if (roles.includes('chronology_before')) return 1\n if (roles.includes('reply_parent')) return 2\n if (roles.includes('target')) return 3\n if (roles.includes('backreply_child')) return 4\n return 5\n }\n\n return (\n rank(left.context_roles) - rank(right.context_roles) ||\n left.message.date.localeCompare(right.message.date) ||\n left.message.message_id - right.message.message_id\n )\n })\n\n return {\n target: this.toEnvelope(target, indexes),\n dialog,\n context_messages: contextMessages,\n window: {\n chronology_total_limit: DEFAULT_CHRONOLOGY_LIMIT,\n chronology_before_count: before.length,\n chronology_after_count: after.length,\n direct_reply_ancestor_included: Boolean(replyParent),\n direct_backreply_count_included: directBackreplies.length,\n },\n notes,\n }\n }\n\n private toMessageListItem(message: CacheMessageRecord, indexes: Indexes): MessageListItem {\n const dialog = indexes.dialogsById.get(message.saved_peer_id)\n const directBackreplyCount =\n indexes.backreplyIndex.get(makeMessageKey(message.saved_peer_id, message.message_id))?.length ?? 0\n\n return {\n message_id: message.message_id,\n saved_peer_id: message.saved_peer_id,\n dialog_title: dialog?.title ?? message.saved_peer_id,\n date: message.date,\n text_preview: previewText(message.text),\n from_self: message.from_self,\n forwarded: message.forwarded,\n reply_to_message_id: message.reply_to_message_id,\n direct_backreply_count: directBackreplyCount,\n queued_for_delete: message.queued_for_delete,\n }\n }\n\n private toEnvelope(message: CacheMessageRecord, indexes: Indexes): MessageEnvelope {\n const replyTarget =\n message.reply_to_message_id !== null\n ? indexes.messagesByKey.get(\n makeMessageKey(message.reply_to_saved_peer_id ?? message.saved_peer_id, message.reply_to_message_id),\n ) ?? null\n : null\n\n const directBackreplies =\n indexes.backreplyIndex.get(makeMessageKey(message.saved_peer_id, message.message_id)) ?? []\n\n const reply: MessageEnvelope['reply'] =\n message.reply_to_message_id === null\n ? {\n exists: false,\n target: null,\n status: 'resolved',\n }\n : {\n exists: true,\n target: replyTarget\n ? this.toMessageRef(replyTarget, 'reply_to')\n : {\n message_id: message.reply_to_message_id,\n saved_peer_id: message.reply_to_saved_peer_id ?? message.saved_peer_id,\n text_preview: '(missing from cache)',\n date: '',\n relationship: 'reply_to',\n },\n status: replyTarget ? 'resolved' : 'missing',\n }\n\n const backreplies: BackreplyEdgeSummary[] = directBackreplies.map((child) => ({\n message: this.toMessageRef(child, 'backreply'),\n thread_depth_from_target: 1,\n subtree_size_hint: this.countDescendants(child, indexes),\n }))\n\n return {\n message_id: message.message_id,\n saved_peer_id: message.saved_peer_id,\n date: message.date,\n edit_date: message.edit_date,\n text: message.text,\n text_preview: previewText(message.text),\n from_self: message.from_self,\n forwarded: message.forwarded,\n forward_origin: message.forward_origin,\n reply,\n backreplies,\n thread: {\n ancestors_known: this.countAncestors(message, indexes),\n direct_backreply_count: backreplies.length,\n descendant_count_hint: this.countDescendants(message, indexes),\n max_known_depth: this.maxDepth(message, indexes),\n },\n links: message.links,\n media_summary: message.media_summary,\n queued_for_delete: message.queued_for_delete,\n }\n }\n\n private toMessageRef(\n message: CacheMessageRecord,\n relationship: MessageRef['relationship'],\n ): MessageRef {\n return {\n message_id: message.message_id,\n saved_peer_id: message.saved_peer_id,\n text_preview: previewText(message.text),\n date: message.date,\n relationship,\n }\n }\n\n private findThreadRoot(message: CacheMessageRecord, indexes: Indexes): CacheMessageRecord {\n let current = message\n\n while (current.reply_to_message_id !== null) {\n const parent = indexes.messagesByKey.get(\n makeMessageKey(current.reply_to_saved_peer_id ?? current.saved_peer_id, current.reply_to_message_id),\n )\n if (!parent || parent.saved_peer_id !== current.saved_peer_id) {\n break\n }\n current = parent\n }\n\n return current\n }\n\n private buildThreadNode(\n message: CacheMessageRecord,\n indexes: Indexes,\n depth: number,\n ): ThreadInspectNode {\n const children =\n indexes.backreplyIndex.get(makeMessageKey(message.saved_peer_id, message.message_id)) ?? []\n\n return {\n message: this.toEnvelope(message, indexes),\n depth,\n children: children.map((child) => this.buildThreadNode(child, indexes, depth + 1)),\n }\n }\n\n private countAncestors(message: CacheMessageRecord, indexes: Indexes): number {\n let count = 0\n let current = message\n\n while (current.reply_to_message_id !== null) {\n const parent = indexes.messagesByKey.get(\n makeMessageKey(current.reply_to_saved_peer_id ?? current.saved_peer_id, current.reply_to_message_id),\n )\n if (!parent) break\n count += 1\n current = parent\n }\n\n return count\n }\n\n private countDescendants(message: CacheMessageRecord, indexes: Indexes): number {\n const children =\n indexes.backreplyIndex.get(makeMessageKey(message.saved_peer_id, message.message_id)) ?? []\n\n return children.reduce((count, child) => count + 1 + this.countDescendants(child, indexes), 0)\n }\n\n private maxDepth(message: CacheMessageRecord, indexes: Indexes): number {\n const children =\n indexes.backreplyIndex.get(makeMessageKey(message.saved_peer_id, message.message_id)) ?? []\n if (children.length === 0) return 0\n return 1 + Math.max(...children.map((child) => this.maxDepth(child, indexes)))\n }\n}\n\nfunction makeMessageKey(savedPeerId: string, messageId: number): string {\n return `${savedPeerId}:${messageId}`\n}\n\nfunction encodeCursor(offset: number): string {\n return Buffer.from(String(offset), 'utf8').toString('base64url')\n}\n\nfunction decodeCursor(cursor?: string | null): number {\n if (!cursor) return 0\n\n try {\n const value = Number(Buffer.from(cursor, 'base64url').toString('utf8'))\n return Number.isFinite(value) && value >= 0 ? value : 0\n } catch {\n return 0\n }\n}\n\nfunction compareByDateAsc(left: CacheMessageRecord, right: CacheMessageRecord): number {\n return left.date.localeCompare(right.date) || left.message_id - right.message_id\n}\n\nfunction compareByDateDesc(left: CacheMessageRecord, right: CacheMessageRecord): number {\n return right.date.localeCompare(left.date) || right.message_id - left.message_id\n}\n\nfunction matchesSearch(message: CacheMessageRecord, query: string): boolean {\n const normalized = query.trim().toLowerCase()\n if (!normalized) return true\n\n return [\n message.text,\n message.forward_origin?.title ?? '',\n message.saved_peer_id,\n ]\n .join('\\n')\n .toLowerCase()\n .includes(normalized)\n}\n\nfunction previewText(text: string, limit = 80): string {\n const normalized = text.replace(/\\s+/g, ' ').trim()\n if (!normalized) return '(no text)'\n return normalized.length <= limit ? normalized : `${normalized.slice(0, limit - 1)}…`\n}\n","import path from 'node:path'\nimport { createInterface } from 'node:readline/promises'\nimport { readFile, writeFile } from 'node:fs/promises'\nimport { TelegramClient, getMarkedPeerId, type tl } from '@mtcute/node'\nimport { TgsmError } from './errors'\nimport type {\n AuthStatus,\n CacheMessageRecord,\n ForwardOriginSummary,\n LinkSummary,\n SavedDialogSummary,\n SourceSnapshot,\n TelegramLoginInput,\n TgsmSourceAdapter,\n} from './types'\n\ninterface TelegramConfig {\n apiId: number\n apiHash: string\n phone?: string\n}\n\ninterface EntityLookup {\n users: Map<number, tl.TypeUser>\n chats: Map<number, tl.TypeChat>\n selfUserId: number\n}\n\nexport class TelegramSource implements TgsmSourceAdapter {\n readonly backend = 'telegram' as const\n\n async authLogin(accountDir: string, input: TelegramLoginInput): Promise<AuthStatus> {\n const config: TelegramConfig = {\n apiId: input.apiId,\n apiHash: input.apiHash,\n phone: input.phone,\n }\n\n await saveTelegramConfig(accountDir, config)\n const client = createTelegramClient(accountDir, config)\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n })\n\n try {\n const user = await client.start({\n phone: input.phone,\n code: async () => {\n if (input.code && input.code.trim().length > 0) {\n return input.code\n }\n\n return rl.question('Code: ')\n },\n password: async () =>\n input.password ??\n (await rl.question('2FA password (leave empty if not enabled): ')),\n codeSentCallback: async (sentCode) => {\n const type =\n typeof sentCode.type === 'string'\n ? sentCode.type\n : 'unknown'\n process.stderr.write(`Login code requested via ${type}.\\n`)\n },\n invalidCodeCallback: async (type) => {\n process.stderr.write(`Invalid ${type}, please try again.\\n`)\n },\n })\n\n return {\n authenticated: true,\n user: {\n id: String(user.id),\n display_name: user.displayName,\n },\n }\n } catch (error) {\n throw new TgsmError({\n code: 'AUTH_FAILED',\n message: error instanceof Error ? error.message : 'Telegram auth failed.',\n retryable: false,\n })\n } finally {\n rl.close()\n await destroyClientQuietly(client)\n }\n }\n\n async authStatus(accountDir: string): Promise<AuthStatus> {\n const config = await loadTelegramConfig(accountDir)\n if (!config) {\n return {\n authenticated: false,\n user: null,\n }\n }\n\n const client = createTelegramClient(accountDir, config)\n\n try {\n await client.start({})\n const me = await client.getMe()\n return {\n authenticated: true,\n user: {\n id: String(me.id),\n display_name: me.displayName,\n },\n }\n } catch {\n return {\n authenticated: false,\n user: null,\n }\n } finally {\n await destroyClientQuietly(client)\n }\n }\n\n async sync(accountDir: string): Promise<SourceSnapshot> {\n const config = await loadTelegramConfig(accountDir)\n if (!config) {\n throw new TgsmError({\n code: 'AUTH_REQUIRED',\n message: 'Telegram credentials are not configured.',\n retryable: false,\n suggestion: 'Run `tgsm auth login` first.',\n })\n }\n\n const client = createTelegramClient(accountDir, config)\n\n try {\n const me = await client.start({})\n const syncedAt = new Date().toISOString()\n const dialogsResponse = await client.call({\n _: 'messages.getSavedDialogs',\n excludePinned: false,\n offsetDate: 0,\n offsetId: 0,\n offsetPeer: { _: 'inputPeerEmpty' },\n limit: 1000,\n hash: 0 as never,\n })\n\n if (dialogsResponse._ === 'messages.savedDialogsNotModified') {\n return {\n backend: this.backend,\n account: {\n id: String(me.id),\n display_name: me.displayName,\n },\n dialogs: [],\n messages: [],\n synced_at: syncedAt,\n }\n }\n\n const lookup: EntityLookup = {\n users: new Map(),\n chats: new Map(),\n selfUserId: Number(me.id),\n }\n\n ingestEntities(lookup, dialogsResponse.users, dialogsResponse.chats)\n\n const dialogs: SavedDialogSummary[] = []\n const messagesByKey = new Map<string, CacheMessageRecord>()\n\n for (const dialog of dialogsResponse.dialogs) {\n if (dialog._ !== 'savedDialog') continue\n\n const savedPeerId = savedPeerIdFromPeer(dialog.peer, lookup.selfUserId)\n const peerInput = await client.resolvePeer(getMarkedPeerId(dialog.peer))\n const title = peerTitle(dialog.peer, lookup)\n\n const messages = await fetchSavedHistory(client, peerInput, lookup)\n const topMessage = messages.find((message) => message.message_id === dialog.topMessage) ?? messages[0] ?? null\n\n dialogs.push({\n saved_peer_id: savedPeerId,\n kind: peerKindFromPeer(dialog.peer, lookup.selfUserId),\n title,\n top_message_id: dialog.topMessage ?? topMessage?.message_id ?? null,\n top_text_preview: topMessage ? previewText(topMessage.text) : null,\n message_count: messages.length,\n pinned: dialog.pinned ?? null,\n last_synced_at: syncedAt,\n })\n\n for (const message of messages) {\n messagesByKey.set(`${message.saved_peer_id}:${message.message_id}`, message)\n }\n }\n\n return {\n backend: this.backend,\n account: {\n id: String(me.id),\n display_name: me.displayName,\n },\n dialogs,\n messages: [...messagesByKey.values()].sort((a, b) =>\n a.saved_peer_id.localeCompare(b.saved_peer_id) || a.date.localeCompare(b.date) || a.message_id - b.message_id,\n ),\n synced_at: syncedAt,\n }\n } catch (error) {\n throw new TgsmError({\n code: 'TELEGRAM_SYNC_FAILED',\n message: error instanceof Error ? error.message : 'Telegram sync failed.',\n retryable: true,\n })\n } finally {\n await destroyClientQuietly(client)\n }\n }\n}\n\nfunction createTelegramClient(accountDir: string, config: TelegramConfig): TelegramClient {\n return new TelegramClient({\n apiId: config.apiId,\n apiHash: config.apiHash,\n storage: path.join(accountDir, 'mtcute-session'),\n logLevel: 0,\n })\n}\n\nasync function destroyClientQuietly(client: TelegramClient): Promise<void> {\n try {\n await client.destroy()\n } catch {\n // Best-effort cleanup only. The session is already persisted on disk.\n }\n}\n\nasync function fetchSavedHistory(\n client: TelegramClient,\n peer: tl.TypeInputPeer,\n lookup: EntityLookup,\n): Promise<CacheMessageRecord[]> {\n let offsetId = 0\n let offsetDate = 0\n const records = new Map<string, CacheMessageRecord>()\n\n while (true) {\n const history = await client.call({\n _: 'messages.getSavedHistory',\n peer,\n offsetId,\n offsetDate,\n addOffset: 0,\n limit: 100,\n maxId: 0,\n minId: 0,\n hash: 0 as never,\n })\n\n if (!('messages' in history)) {\n break\n }\n\n ingestEntities(lookup, history.users ?? [], history.chats ?? [])\n\n const rawMessages = history.messages.filter(\n (message: tl.TypeMessage): message is tl.RawMessage | tl.RawMessageService =>\n message._ === 'message' || message._ === 'messageService',\n )\n\n if (rawMessages.length === 0) {\n break\n }\n\n for (const raw of rawMessages) {\n const record = normalizeRawMessage(raw, lookup)\n records.set(`${record.saved_peer_id}:${record.message_id}`, record)\n }\n\n const oldest = rawMessages[rawMessages.length - 1]!\n offsetId = oldest.id\n offsetDate = oldest.date\n\n if (rawMessages.length < 100) {\n break\n }\n }\n\n return [...records.values()].sort((a, b) => a.date.localeCompare(b.date) || a.message_id - b.message_id)\n}\n\nexport function normalizeRawMessage(raw: tl.RawMessage | tl.RawMessageService, lookup: EntityLookup): CacheMessageRecord {\n const savedPeerId = savedPeerIdFromPeer(raw.peerId, lookup.selfUserId)\n const replyHeader =\n 'replyTo' in raw && raw.replyTo && raw.replyTo._ === 'messageReplyHeader' ? raw.replyTo : null\n const replyPeer = replyHeader?.replyToPeerId ?? raw.peerId\n const fwdFrom = 'fwdFrom' in raw ? raw.fwdFrom : undefined\n const editDate = 'editDate' in raw ? raw.editDate : undefined\n const media = 'media' in raw ? raw.media : undefined\n const text = 'message' in raw ? raw.message : ''\n\n return {\n message_id: raw.id,\n saved_peer_id: savedPeerId,\n date: new Date(raw.date * 1000).toISOString(),\n edit_date: editDate ? new Date(editDate * 1000).toISOString() : null,\n text,\n from_self:\n ('out' in raw ? Boolean(raw.out) : false) ||\n peerIsSelf('fromId' in raw ? raw.fromId : null, lookup.selfUserId) ||\n (savedPeerId === 'self' && !fwdFrom),\n forwarded: Boolean(fwdFrom),\n forward_origin: fwdFrom ? forwardOriginFromHeader(fwdFrom, lookup) : null,\n reply_to_message_id: replyHeader?.replyToMsgId ?? null,\n reply_to_saved_peer_id: replyHeader?.replyToMsgId\n ? savedPeerIdFromPeer(replyPeer, lookup.selfUserId)\n : null,\n links: extractLinks(text),\n media_summary: mediaSummary(media),\n queued_for_delete: false,\n }\n}\n\nfunction forwardOriginFromHeader(\n header: tl.RawMessageFwdHeader,\n lookup: EntityLookup,\n): ForwardOriginSummary {\n if (header.savedFromPeer) {\n return {\n saved_peer_id: savedPeerIdFromPeer(header.savedFromPeer, lookup.selfUserId),\n title: peerTitle(header.savedFromPeer, lookup),\n message_id: header.savedFromMsgId ?? null,\n }\n }\n\n if (header.fromId) {\n return {\n saved_peer_id: savedPeerIdFromPeer(header.fromId, lookup.selfUserId),\n title: header.fromName ?? peerTitle(header.fromId, lookup),\n message_id: header.savedFromMsgId ?? null,\n }\n }\n\n return {\n saved_peer_id: null,\n title: header.fromName ?? header.savedFromName ?? null,\n message_id: header.savedFromMsgId ?? null,\n }\n}\n\nfunction peerIsSelf(peer: tl.TypePeer | undefined | null, selfUserId: number): boolean {\n return Boolean(peer && peer._ === 'peerUser' && peer.userId === selfUserId)\n}\n\nfunction peerKindFromPeer(peer: tl.TypePeer, selfUserId: number): SavedDialogSummary['kind'] {\n if (peer._ === 'peerUser' && peer.userId === selfUserId) return 'self'\n if (peer._ === 'peerUser' || peer._ === 'peerChat') return 'peer'\n if (peer._ === 'peerChannel') return 'channel'\n return 'unknown'\n}\n\nexport function savedPeerIdFromPeer(peer: tl.TypePeer, selfUserId: number): string {\n if (peer._ === 'peerUser' && peer.userId === selfUserId) {\n return 'self'\n }\n\n if (peer._ === 'peerUser') return `user:${peer.userId}`\n if (peer._ === 'peerChat') return `chat:${peer.chatId}`\n if (peer._ === 'peerChannel') return `channel:${peer.channelId}`\n return `unknown:${getMarkedPeerId(peer)}`\n}\n\nfunction peerTitle(peer: tl.TypePeer, lookup: EntityLookup): string {\n if (peer._ === 'peerUser') {\n if (peer.userId === lookup.selfUserId) return 'Self'\n const user = lookup.users.get(peer.userId)\n const firstName = user && user._ === 'user' ? user.firstName ?? '' : ''\n const lastName = user && user._ === 'user' ? user.lastName ?? '' : ''\n const username = user && user._ === 'user' ? user.username ?? '' : ''\n const parts = [firstName, lastName].filter(Boolean)\n return parts.join(' ').trim() || username || `User ${peer.userId}`\n }\n\n const entity = lookup.chats.get(peer._ === 'peerChat' ? peer.chatId : peer.channelId)\n if (entity && 'title' in entity) {\n return entity.title\n }\n\n if (peer._ === 'peerChat') return `Chat ${peer.chatId}`\n if (peer._ === 'peerChannel') return `Channel ${peer.channelId}`\n return 'Unknown'\n}\n\nfunction ingestEntities(lookup: EntityLookup, users: tl.TypeUser[], chats: tl.TypeChat[]): void {\n for (const user of users) {\n if ('id' in user) {\n lookup.users.set(Number(user.id), user)\n }\n }\n\n for (const chat of chats) {\n if ('id' in chat) {\n lookup.chats.set(Number(chat.id), chat)\n }\n }\n}\n\nfunction extractLinks(text: string): LinkSummary[] {\n const matches = text.matchAll(/https?:\\/\\/[^\\s)]+/g)\n const seen = new Set<string>()\n const links: LinkSummary[] = []\n\n for (const match of matches) {\n const url = match[0]!\n if (seen.has(url)) continue\n seen.add(url)\n links.push({ url })\n }\n\n return links\n}\n\nfunction mediaSummary(media: tl.TypeMessageMedia | undefined): string | null {\n if (!media) return null\n return media._\n}\n\nfunction previewText(text: string, limit = 80): string {\n const normalized = text.replace(/\\s+/g, ' ').trim()\n if (!normalized) return '(no text)'\n return normalized.length <= limit ? normalized : `${normalized.slice(0, limit - 1)}…`\n}\n\nasync function loadTelegramConfig(accountDir: string): Promise<TelegramConfig | null> {\n try {\n const raw = await readFile(path.join(accountDir, 'telegram.json'), 'utf8')\n return JSON.parse(raw) as TelegramConfig\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null\n throw error\n }\n}\n\nasync function saveTelegramConfig(accountDir: string, config: TelegramConfig): Promise<void> {\n await writeFile(\n path.join(accountDir, 'telegram.json'),\n `${JSON.stringify(config, null, 2)}\\n`,\n 'utf8',\n )\n}\n","import type {\n AuthStatus,\n MessageContextBundle,\n MessageListItem,\n SavedDialogSummary,\n SearchResultPage,\n SyncResult,\n ThreadInspectNode,\n ThreadInspectResult,\n} from '@tgsm/core'\n\nexport function formatSyncResult(result: SyncResult): string {\n return [\n `sync backend=${result.backend}`,\n `synced_at=${result.synced_at}`,\n `dialogs=${result.synced_dialogs}`,\n `messages=${result.synced_messages}`,\n ].join('\\n')\n}\n\nexport function formatAuthStatus(status: AuthStatus): string {\n if (!status.authenticated || !status.user) {\n return 'authenticated: false'\n }\n\n return [`authenticated: true`, `user: ${status.user.display_name}`, `id: ${status.user.id}`].join('\\n')\n}\n\nexport function formatSavedDialogs(dialogs: SavedDialogSummary[]): string {\n if (dialogs.length === 0) return 'No saved dialogs found.'\n\n return dialogs\n .map((dialog) =>\n [\n `${dialog.saved_peer_id} (${dialog.kind})`,\n `title: ${dialog.title}`,\n `messages: ${dialog.message_count}`,\n `top_message_id: ${dialog.top_message_id ?? 'n/a'}`,\n `top_text: ${dialog.top_text_preview ?? '(none)'}`,\n ].join('\\n'),\n )\n .join('\\n\\n')\n}\n\nexport function formatMessagesPage(page: SearchResultPage<MessageListItem>): string {\n if (page.items.length === 0) return 'No messages found.'\n\n const header =\n page.scope === 'saved_dialog'\n ? `messages scope=${page.saved_peer_id} total=${page.result_count}`\n : `messages scope=all_saved_dialogs total=${page.result_count}`\n\n const blocks = page.items.map((item) =>\n [\n `#${item.message_id} ${item.date}`,\n `dialog: ${item.saved_peer_id} (${item.dialog_title})`,\n `from_self: ${item.from_self} forwarded: ${item.forwarded}`,\n `reply_to: ${item.reply_to_message_id ?? 'none'} backreplies: ${item.direct_backreply_count}`,\n `text: ${item.text_preview}`,\n ].join('\\n'),\n )\n\n return [header, ...blocks, page.next_cursor ? `next_cursor: ${page.next_cursor}` : ''].filter(Boolean).join('\\n\\n')\n}\n\nexport function formatContextBundle(bundle: MessageContextBundle): string {\n const lines: string[] = [\n `MESSAGE #${bundle.target.message_id}`,\n `dialog: ${bundle.dialog.saved_peer_id} (${bundle.dialog.title})`,\n `date: ${bundle.target.date}`,\n `from_self: ${bundle.target.from_self}`,\n `thread: direct_backreplies=${bundle.target.thread.direct_backreply_count} descendant_hint=${bundle.target.thread.descendant_count_hint ?? 0}`,\n '',\n 'text:',\n bundle.target.text || '(no text)',\n ]\n\n const replySection = bundle.target.reply.exists\n ? bundle.target.reply.target\n ? [``, 'reply_to:', `- #${bundle.target.reply.target.message_id} ${bundle.target.reply.target.text_preview}`]\n : [``, 'reply_to:', '- (missing from cache)']\n : []\n\n const backreplySection =\n bundle.target.backreplies.length > 0\n ? [\n '',\n 'backreplies:',\n ...bundle.target.backreplies.map(\n (item) => `- #${item.message.message_id} ${item.message.text_preview}`,\n ),\n ]\n : []\n\n const before = bundle.context_messages.filter((item) => item.context_roles.includes('chronology_before'))\n const after = bundle.context_messages.filter((item) => item.context_roles.includes('chronology_after'))\n\n if (before.length > 0) {\n lines.push('', 'chronology_before:')\n for (const item of before) lines.push(`- #${item.message.message_id} ${item.message.text_preview}`)\n }\n\n if (after.length > 0) {\n lines.push('', 'chronology_after:')\n for (const item of after) lines.push(`- #${item.message.message_id} ${item.message.text_preview}`)\n }\n\n lines.push(...replySection, ...backreplySection)\n\n if (bundle.notes.length > 0) {\n lines.push('', 'notes:')\n for (const note of bundle.notes) lines.push(`- ${note}`)\n }\n\n return lines.join('\\n')\n}\n\nexport function formatThread(result: ThreadInspectResult): string {\n const lines = [`THREAD #${result.root.message_id}`, `dialog: ${result.dialog.saved_peer_id} (${result.dialog.title})`, '']\n\n const root = result.nodes[0]\n if (!root) return lines.join('\\n')\n\n renderNode(root, '', true, lines)\n return lines.join('\\n')\n}\n\nfunction renderNode(node: ThreadInspectNode, prefix: string, isLast: boolean, lines: string[]): void {\n const branch = prefix ? `${prefix}${isLast ? '└── ' : '├── '}` : ''\n lines.push(`${branch}#${node.message.message_id} ${node.message.text_preview}`)\n\n const nextPrefix = prefix ? `${prefix}${isLast ? ' ' : '│ '}` : ''\n node.children.forEach((child, index) => {\n renderNode(child, nextPrefix, index === node.children.length - 1, lines)\n })\n}\n","{\n \"name\": \"@imadtg/tgsm\",\n \"version\": \"0.0.6\",\n \"type\": \"module\",\n \"description\": \"A retrieval-first CLI for navigating Telegram Saved Messages as structured, agent-readable context.\",\n \"license\": \"MIT\",\n \"homepage\": \"https://github.com/imadtg/tgsm\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/imadtg/tgsm.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/imadtg/tgsm/issues\"\n },\n \"keywords\": [\n \"telegram\",\n \"saved-messages\",\n \"cli\",\n \"agents\",\n \"mtproto\"\n ],\n \"engines\": {\n \"node\": \">=20\"\n },\n \"bin\": {\n \"tgsm\": \"dist/index.js\"\n },\n \"main\": \"./dist/index.js\",\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"test\": \"bun test\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"dependencies\": {\n \"@mtcute/node\": \"^0.28.2\",\n \"commander\": \"^14.0.1\"\n },\n \"devDependencies\": {\n \"@tgsm/core\": \"workspace:*\"\n }\n}\n"],"mappings":"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","names":["createInterface","process","readFile","readFile","path","path","path","readFile","writeFile","messages","previewText","path","readFile","writeFile","createInterface","process"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../../core/src/errors.ts","../../core/src/paths.ts","../../core/src/cache.ts","../../core/src/fixture.ts","../../core/src/service.ts","../../core/src/telegram.ts","../src/format.ts","../package.json"],"sourcesContent":["import { createInterface } from 'node:readline/promises'\nimport { once } from 'node:events'\nimport process from 'node:process'\nimport { Command } from 'commander'\nimport {\n FixtureSource,\n TgsmError,\n TgsmService,\n TelegramSource,\n ensureAccountDir,\n getCachePath,\n type GetMessageOptions,\n type ListMessagesOptions,\n type TgsmSourceAdapter,\n} from '@tgsm/core'\nimport {\n formatAuthStatus,\n formatContextBundle,\n formatMessagesPage,\n formatSavedDialogs,\n formatSyncResult,\n formatThread,\n} from './format'\nimport pkg from '../package.json'\n\ninterface GlobalOptions {\n json?: boolean\n debug?: boolean\n backend?: 'telegram' | 'fixture'\n fixture?: string\n home?: string\n account?: string\n}\n\nconst program = new Command()\n\nprogram\n .name('tgsm')\n .description('Retrieval-first Telegram Saved Messages CLI')\n .version(pkg.version, '-V, --version', 'Display the tgsm version')\n .option('--json', 'Emit JSON instead of default text output')\n .option('--debug', 'Emit debug telemetry to stderr')\n .option('--backend <backend>', 'telegram or fixture', 'telegram')\n .option('--fixture <path>', 'Fixture path for the fixture backend')\n .option('--home <path>', 'Override TGSM home directory')\n .option('--account <name>', 'Account namespace', 'default')\n\nprogram\n .command('auth')\n .description('Telegram auth commands')\n .addCommand(\n new Command('login').action(async (_, command) => {\n await withService(command.optsWithGlobals(), async (service, options) => {\n if (options.backend !== 'telegram') {\n throw new TgsmError({\n code: 'AUTH_UNSUPPORTED',\n message: 'Auth login is only supported with the telegram backend.',\n retryable: false,\n })\n }\n\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n })\n\n try {\n const apiId = Number(await rl.question('API ID: '))\n const apiHash = await rl.question('API Hash: ')\n const phone = await rl.question('Phone: ')\n\n const result = await service.authLogin({\n apiId,\n apiHash,\n phone,\n })\n\n emit(result, options, formatAuthStatus)\n } finally {\n rl.close()\n }\n })\n }),\n )\n .addCommand(\n new Command('status').action(async (_, command) => {\n await withService(command.optsWithGlobals(), async (service, options) => {\n emit(await service.authStatus(), options, formatAuthStatus)\n })\n }),\n )\n\nprogram.command('sync').action(async (_, command) => {\n await withService(command.optsWithGlobals(), async (service, options) => {\n emit(await service.sync(), options, formatSyncResult)\n })\n})\n\nprogram\n .command('saved-dialogs')\n .description('Saved dialog commands')\n .addCommand(\n new Command('list').action(async (_, command) => {\n await withService(command.optsWithGlobals(), async (service, options) => {\n emit(await service.listSavedDialogs(), options, formatSavedDialogs)\n })\n }),\n )\n\nconst messages = program.command('messages').description('Message commands')\n\nmessages\n .command('list')\n .option('--dialog <savedPeerId>')\n .option('--search <query>')\n .option('--limit <number>', 'Page size', '20')\n .option('--cursor <cursor>')\n .action(async (commandOptions, command) => {\n await withService(command.optsWithGlobals(), async (service, options) => {\n const result = await service.listMessages({\n dialog: commandOptions.dialog,\n search: commandOptions.search,\n limit: Number(commandOptions.limit),\n cursor: commandOptions.cursor,\n } satisfies ListMessagesOptions)\n emit(result, options, formatMessagesPage)\n })\n })\n\nmessages\n .command('get')\n .argument('<id>')\n .option('--dialog <savedPeerId>')\n .action(async (id, commandOptions, command) => {\n await withService(command.optsWithGlobals(), async (service, options) => {\n const result = await service.getMessage(Number(id), {\n dialog: commandOptions.dialog,\n } satisfies GetMessageOptions)\n emit(result, options, formatContextBundle)\n })\n })\n\nmessages\n .command('context')\n .argument('<id>')\n .option('--dialog <savedPeerId>')\n .action(async (id, commandOptions, command) => {\n await withService(command.optsWithGlobals(), async (service, options) => {\n const result = await service.getContext(Number(id), {\n dialog: commandOptions.dialog,\n } satisfies GetMessageOptions)\n emit(result, options, formatContextBundle)\n })\n })\n\nprogram\n .command('threads')\n .description('Thread commands')\n .addCommand(\n new Command('inspect')\n .argument('<id>')\n .option('--dialog <savedPeerId>')\n .action(async (id, commandOptions, command) => {\n await withService(command.optsWithGlobals(), async (service, options) => {\n const result = await service.inspectThread(Number(id), {\n dialog: commandOptions.dialog,\n } satisfies GetMessageOptions)\n emit(result, options, formatThread)\n })\n }),\n )\n\nvoid main()\n\nasync function withService(\n options: GlobalOptions,\n fn: (service: TgsmService, options: Required<GlobalOptions>) => Promise<void>,\n): Promise<void> {\n const normalized: Required<GlobalOptions> = {\n json: Boolean(options.json),\n debug: Boolean(options.debug),\n backend: (options.backend ?? 'telegram') as 'telegram' | 'fixture',\n fixture: options.fixture ?? '',\n home: options.home ?? '',\n account: options.account ?? 'default',\n }\n\n await ensureAccountDir({\n homeDir: normalized.home || undefined,\n account: normalized.account,\n })\n\n const cachePath = getCachePath({\n homeDir: normalized.home || undefined,\n account: normalized.account,\n })\n\n debugLog(normalized, 'cli.service.setup', {\n backend: normalized.backend,\n account: normalized.account,\n cache_path: cachePath,\n home: normalized.home || undefined,\n fixture: normalized.fixture || undefined,\n })\n\n const source = resolveSource(normalized)\n const service = new TgsmService({\n cachePath,\n source,\n })\n\n await fn(service, normalized)\n}\n\nfunction resolveSource(options: Required<GlobalOptions>): TgsmSourceAdapter {\n if (options.backend === 'fixture') {\n if (!options.fixture) {\n throw new TgsmError({\n code: 'FIXTURE_REQUIRED',\n message: 'The fixture backend requires --fixture <path>.',\n retryable: false,\n })\n }\n return new FixtureSource(options.fixture)\n }\n\n return new TelegramSource({\n debug: options.debug,\n logger: (event, fields = {}) => {\n debugLog(options, event, fields)\n },\n })\n}\n\nfunction emit<T>(\n value: T,\n options: Required<GlobalOptions>,\n format?: (value: T) => string,\n): void {\n if (options.json) {\n process.stdout.write(`${JSON.stringify(value, null, 2)}\\n`)\n return\n }\n\n process.stdout.write(`${format ? format(value) : String(value)}\\n`)\n}\n\nfunction errorCode(code: string): number {\n if (code.startsWith('AUTH')) return 3\n if (code.includes('SYNC') || code.includes('TELEGRAM')) return 2\n return 1\n}\n\nfunction debugLog(\n options: Pick<Required<GlobalOptions>, 'debug'>,\n event: string,\n fields: Record<string, unknown> = {},\n): void {\n if (!options.debug) return\n\n const detail = Object.entries(fields)\n .filter(([, value]) => value !== undefined)\n .map(([key, value]) => `${key}=${formatDebugValue(value)}`)\n .join(' ')\n\n process.stderr.write(\n `[tgsm debug ${new Date().toISOString()}] ${event}${detail ? ` ${detail}` : ''}\\n`,\n )\n}\n\nfunction formatDebugValue(value: unknown): string {\n if (typeof value === 'string') return JSON.stringify(value)\n if (typeof value === 'number' || typeof value === 'boolean') return String(value)\n if (value === null) return 'null'\n\n try {\n return JSON.stringify(value)\n } catch {\n return String(value)\n }\n}\n\nasync function main(): Promise<void> {\n let exitCode = 0\n\n try {\n await program.parseAsync(process.argv)\n } catch (error: unknown) {\n const tgsmError =\n error instanceof TgsmError\n ? error\n : new TgsmError({\n code: 'UNEXPECTED_ERROR',\n message: error instanceof Error ? error.message : 'Unexpected error',\n retryable: false,\n })\n\n const options = program.opts<GlobalOptions>()\n if (options.json) {\n process.stdout.write(`${JSON.stringify(tgsmError.toJSON(), null, 2)}\\n`)\n } else {\n process.stderr.write(`error: ${tgsmError.message}\\n`)\n if (tgsmError.suggestion) {\n process.stderr.write(`suggestion: ${tgsmError.suggestion}\\n`)\n }\n }\n\n exitCode = errorCode(tgsmError.code)\n }\n\n await flushStreams()\n process.exit(exitCode)\n}\n\nasync function flushStreams(): Promise<void> {\n await Promise.all([flushStream(process.stdout), flushStream(process.stderr)])\n}\n\nasync function flushStream(stream: NodeJS.WriteStream): Promise<void> {\n if (!stream.writableNeedDrain) {\n return\n }\n\n await once(stream, 'drain')\n}\n","import type { OperationErrorShape } from './types'\n\nexport class TgsmError extends Error {\n readonly code: string\n readonly retryable: boolean\n readonly suggestion?: string\n\n constructor(shape: OperationErrorShape) {\n super(shape.message)\n this.name = 'TgsmError'\n this.code = shape.code\n this.retryable = shape.retryable\n this.suggestion = shape.suggestion\n }\n\n toJSON(): OperationErrorShape {\n return {\n code: this.code,\n message: this.message,\n retryable: this.retryable,\n suggestion: this.suggestion,\n }\n }\n}\n\nexport function asTgsmError(error: unknown): TgsmError {\n if (error instanceof TgsmError) {\n return error\n }\n\n if (error instanceof Error) {\n return new TgsmError({\n code: 'UNEXPECTED_ERROR',\n message: error.message,\n retryable: false,\n })\n }\n\n return new TgsmError({\n code: 'UNEXPECTED_ERROR',\n message: 'Unexpected error',\n retryable: false,\n })\n}\n","import os from 'node:os'\nimport path from 'node:path'\nimport { mkdir } from 'node:fs/promises'\n\nexport interface PathOptions {\n homeDir?: string\n account?: string\n}\n\nexport function getHomeDir(homeDir?: string): string {\n return homeDir ?? process.env.TGSM_HOME ?? path.join(os.homedir(), '.tgsm')\n}\n\nexport function getAccountName(account?: string): string {\n return account ?? 'default'\n}\n\nexport function getAccountDir(options: PathOptions = {}): string {\n return path.join(getHomeDir(options.homeDir), getAccountName(options.account))\n}\n\nexport function getCachePath(options: PathOptions = {}): string {\n return path.join(getAccountDir(options), 'cache.json')\n}\n\nexport function getTelegramConfigPath(options: PathOptions = {}): string {\n return path.join(getAccountDir(options), 'telegram.json')\n}\n\nexport function getTelegramSessionPath(options: PathOptions = {}): string {\n return path.join(getAccountDir(options), 'mtcute-session')\n}\n\nexport async function ensureAccountDir(options: PathOptions = {}): Promise<string> {\n const accountDir = getAccountDir(options)\n await mkdir(accountDir, { recursive: true })\n return accountDir\n}\n","import { readFile, writeFile } from 'node:fs/promises'\nimport type { CacheState, SourceSnapshot } from './types'\n\nexport const EMPTY_CACHE: CacheState = {\n version: 1,\n backend: 'fixture',\n account: null,\n synced_at: null,\n dialogs: [],\n messages: [],\n}\n\nexport async function readCache(cachePath: string): Promise<CacheState> {\n try {\n const raw = await readFile(cachePath, 'utf8')\n return JSON.parse(raw) as CacheState\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return EMPTY_CACHE\n }\n\n throw error\n }\n}\n\nexport async function writeCache(cachePath: string, snapshot: SourceSnapshot): Promise<void> {\n const state: CacheState = {\n version: 1,\n backend: snapshot.backend,\n account: snapshot.account,\n synced_at: snapshot.synced_at,\n dialogs: snapshot.dialogs,\n messages: snapshot.messages,\n }\n\n await writeFile(cachePath, `${JSON.stringify(state, null, 2)}\\n`, 'utf8')\n}\n","import { readFile } from 'node:fs/promises'\nimport { TgsmError } from './errors'\nimport type { AuthStatus, SourceSnapshot, TgsmSourceAdapter } from './types'\n\ninterface FixtureFile {\n account?: SourceSnapshot['account']\n dialogs: SourceSnapshot['dialogs']\n messages: SourceSnapshot['messages']\n}\n\nexport class FixtureSource implements TgsmSourceAdapter {\n readonly backend = 'fixture' as const\n\n constructor(private readonly fixturePath: string) {}\n\n async sync(): Promise<SourceSnapshot> {\n const raw = await readFile(this.fixturePath, 'utf8')\n const parsed = JSON.parse(raw) as FixtureFile\n\n if (!Array.isArray(parsed.dialogs) || !Array.isArray(parsed.messages)) {\n throw new TgsmError({\n code: 'INVALID_FIXTURE',\n message: `Fixture at ${this.fixturePath} is missing dialogs/messages arrays`,\n retryable: false,\n })\n }\n\n const syncedAt = new Date().toISOString()\n\n return {\n backend: this.backend,\n account:\n parsed.account ??\n ({\n id: 'fixture',\n display_name: 'Fixture Account',\n } as const),\n dialogs: parsed.dialogs.map((dialog) => ({\n ...dialog,\n last_synced_at: syncedAt,\n })),\n messages: parsed.messages,\n synced_at: syncedAt,\n }\n }\n\n async authStatus(): Promise<AuthStatus> {\n return {\n authenticated: true,\n user: {\n id: 'fixture',\n display_name: 'Fixture Account',\n },\n }\n }\n}\n","import path from 'node:path'\nimport { readCache, writeCache } from './cache'\nimport { TgsmError } from './errors'\nimport type {\n BackreplyEdgeSummary,\n CacheMessageRecord,\n CacheState,\n ContextMessage,\n GetMessageOptions,\n ListMessagesOptions,\n MessageContextBundle,\n MessageEnvelope,\n MessageListItem,\n MessageRef,\n SavedDialogSummary,\n SearchResultPage,\n SyncResult,\n ThreadInspectNode,\n ThreadInspectResult,\n TgsmSourceAdapter,\n} from './types'\n\nexport interface TgsmServiceOptions {\n cachePath: string\n source?: TgsmSourceAdapter\n}\n\ninterface Indexes {\n dialogsById: Map<string, SavedDialogSummary>\n messagesByKey: Map<string, CacheMessageRecord>\n messagesByGlobalId: Map<number, CacheMessageRecord[]>\n messagesByDialog: Map<string, CacheMessageRecord[]>\n backreplyIndex: Map<string, CacheMessageRecord[]>\n}\n\nconst DEFAULT_CHRONOLOGY_LIMIT = 20\n\nexport class TgsmService {\n constructor(private readonly options: TgsmServiceOptions) {}\n\n async authLogin(input: Parameters<NonNullable<TgsmSourceAdapter['authLogin']>>[1]) {\n if (!this.options.source?.authLogin) {\n throw new TgsmError({\n code: 'AUTH_UNSUPPORTED',\n message: 'This backend does not support auth login.',\n retryable: false,\n })\n }\n\n return this.options.source.authLogin(this.accountDir(), input)\n }\n\n async authStatus() {\n if (!this.options.source?.authStatus) {\n return {\n authenticated: false,\n user: null,\n }\n }\n\n return this.options.source.authStatus(this.accountDir())\n }\n\n async sync(): Promise<SyncResult> {\n if (!this.options.source) {\n throw new TgsmError({\n code: 'SYNC_UNAVAILABLE',\n message: 'No source backend configured for sync.',\n retryable: false,\n })\n }\n\n const snapshot = await this.options.source.sync(this.accountDir())\n await writeCache(this.options.cachePath, snapshot)\n\n return {\n backend: snapshot.backend,\n synced_at: snapshot.synced_at,\n synced_dialogs: snapshot.dialogs.length,\n synced_messages: snapshot.messages.length,\n }\n }\n\n async listSavedDialogs(): Promise<SavedDialogSummary[]> {\n const cache = await this.loadCache()\n return [...cache.dialogs].sort((a, b) => {\n const left = a.last_synced_at ?? ''\n const right = b.last_synced_at ?? ''\n return right.localeCompare(left) || a.saved_peer_id.localeCompare(b.saved_peer_id)\n })\n }\n\n async listMessages(options: ListMessagesOptions = {}): Promise<SearchResultPage<MessageListItem>> {\n const cache = await this.loadCache()\n const indexes = this.buildIndexes(cache)\n const dialogId = options.dialog ?? null\n\n if (dialogId && !indexes.dialogsById.has(dialogId)) {\n throw new TgsmError({\n code: 'DIALOG_NOT_FOUND',\n message: `Saved dialog ${dialogId} was not found.`,\n retryable: false,\n suggestion: 'Run `tgsm saved-dialogs list` to inspect available dialogs.',\n })\n }\n\n const scoped = dialogId\n ? indexes.messagesByDialog.get(dialogId) ?? []\n : cache.messages\n\n const filtered = options.search\n ? scoped.filter((message: CacheMessageRecord) => matchesSearch(message, options.search!))\n : scoped\n\n const sorted = [...filtered].sort(compareByDateDesc)\n const limit = Math.max(1, options.limit ?? 20)\n const offset = decodeCursor(options.cursor)\n const slice = sorted.slice(offset, offset + limit)\n\n return {\n items: slice.map((message) => this.toMessageListItem(message, indexes)),\n scope: dialogId ? 'saved_dialog' : 'all_saved_dialogs',\n saved_peer_id: dialogId,\n next_cursor: offset + limit < sorted.length ? encodeCursor(offset + limit) : null,\n result_count: filtered.length,\n }\n }\n\n async getMessage(messageId: number, options: GetMessageOptions = {}): Promise<MessageContextBundle> {\n const cache = await this.loadCache()\n const indexes = this.buildIndexes(cache)\n const target = this.findMessage(indexes, messageId, options.dialog)\n return this.buildContextBundle(target, indexes)\n }\n\n async getContext(messageId: number, options: GetMessageOptions = {}): Promise<MessageContextBundle> {\n return this.getMessage(messageId, options)\n }\n\n async inspectThread(messageId: number, options: GetMessageOptions = {}): Promise<ThreadInspectResult> {\n const cache = await this.loadCache()\n const indexes = this.buildIndexes(cache)\n const target = this.findMessage(indexes, messageId, options.dialog)\n const root = this.findThreadRoot(target, indexes)\n const dialog = indexes.dialogsById.get(root.saved_peer_id)\n\n if (!dialog) {\n throw new TgsmError({\n code: 'DIALOG_NOT_FOUND',\n message: `Saved dialog ${root.saved_peer_id} was not found.`,\n retryable: false,\n })\n }\n\n return {\n dialog,\n root: this.toEnvelope(root, indexes),\n nodes: [this.buildThreadNode(root, indexes, 0)],\n }\n }\n\n private accountDir(): string {\n return path.dirname(this.options.cachePath)\n }\n\n private async loadCache(): Promise<CacheState> {\n return readCache(this.options.cachePath)\n }\n\n private buildIndexes(cache: CacheState): Indexes {\n const dialogsById = new Map<string, SavedDialogSummary>(\n cache.dialogs.map((dialog: SavedDialogSummary) => [dialog.saved_peer_id, dialog]),\n )\n const messagesByKey = new Map<string, CacheMessageRecord>()\n const messagesByGlobalId = new Map<number, CacheMessageRecord[]>()\n const messagesByDialog = new Map<string, CacheMessageRecord[]>()\n const backreplyIndex = new Map<string, CacheMessageRecord[]>()\n\n for (const message of cache.messages) {\n messagesByKey.set(makeMessageKey(message.saved_peer_id, message.message_id), message)\n\n const existingGlobal = messagesByGlobalId.get(message.message_id) ?? []\n existingGlobal.push(message)\n messagesByGlobalId.set(message.message_id, existingGlobal)\n\n const dialogMessages = messagesByDialog.get(message.saved_peer_id) ?? []\n dialogMessages.push(message)\n messagesByDialog.set(message.saved_peer_id, dialogMessages)\n\n if (message.reply_to_message_id !== null) {\n const replyDialog = message.reply_to_saved_peer_id ?? message.saved_peer_id\n const key = makeMessageKey(replyDialog, message.reply_to_message_id)\n const children = backreplyIndex.get(key) ?? []\n children.push(message)\n backreplyIndex.set(key, children)\n }\n }\n\n for (const records of messagesByDialog.values()) {\n records.sort(compareByDateAsc)\n }\n\n for (const records of backreplyIndex.values()) {\n records.sort(compareByDateAsc)\n }\n\n return {\n dialogsById,\n messagesByKey,\n messagesByGlobalId,\n messagesByDialog,\n backreplyIndex,\n }\n }\n\n private findMessage(indexes: Indexes, messageId: number, dialog?: string): CacheMessageRecord {\n if (dialog) {\n const scoped = indexes.messagesByKey.get(makeMessageKey(dialog, messageId))\n if (!scoped) {\n throw new TgsmError({\n code: 'MESSAGE_NOT_FOUND',\n message: `Message ${messageId} was not found in dialog ${dialog}.`,\n retryable: false,\n suggestion: 'Run `tgsm messages list --dialog <saved_peer_id>` to inspect the dialog.',\n })\n }\n return scoped\n }\n\n const candidates = indexes.messagesByGlobalId.get(messageId) ?? []\n\n if (candidates.length === 0) {\n throw new TgsmError({\n code: 'MESSAGE_NOT_FOUND',\n message: `Message ${messageId} was not found in the selected scope.`,\n retryable: false,\n suggestion: 'Run `tgsm messages list` or narrow the dialog scope.',\n })\n }\n\n if (candidates.length > 1) {\n throw new TgsmError({\n code: 'AMBIGUOUS_MESSAGE_ID',\n message: `Message ID ${messageId} exists in multiple saved dialogs.`,\n retryable: false,\n suggestion: 'Pass `--dialog <saved_peer_id>` to disambiguate.',\n })\n }\n\n return candidates[0]!\n }\n\n private buildContextBundle(target: CacheMessageRecord, indexes: Indexes): MessageContextBundle {\n const dialog = indexes.dialogsById.get(target.saved_peer_id)\n\n if (!dialog) {\n throw new TgsmError({\n code: 'DIALOG_NOT_FOUND',\n message: `Saved dialog ${target.saved_peer_id} was not found.`,\n retryable: false,\n })\n }\n\n const chronological = indexes.messagesByDialog.get(target.saved_peer_id) ?? []\n const targetIndex = chronological.findIndex(\n (message) => message.message_id === target.message_id && message.saved_peer_id === target.saved_peer_id,\n )\n\n const maxEachSide = Math.floor(DEFAULT_CHRONOLOGY_LIMIT / 2)\n const before = chronological.slice(Math.max(0, targetIndex - maxEachSide), targetIndex)\n const after = chronological.slice(targetIndex + 1, targetIndex + 1 + maxEachSide)\n\n const replyParent = target.reply_to_message_id\n ? indexes.messagesByKey.get(\n makeMessageKey(target.reply_to_saved_peer_id ?? target.saved_peer_id, target.reply_to_message_id),\n ) ?? null\n : null\n\n const directBackreplies =\n indexes.backreplyIndex.get(makeMessageKey(target.saved_peer_id, target.message_id)) ?? []\n\n const notes: string[] = []\n if (target.reply_to_message_id !== null && !replyParent) {\n notes.push(`Reply target #${target.reply_to_message_id} could not be resolved in cache.`)\n }\n\n const contexts = new Map<string, ContextMessage>()\n const pushContext = (\n message: CacheMessageRecord,\n role: ContextMessage['context_roles'][number],\n ): void => {\n const key = makeMessageKey(message.saved_peer_id, message.message_id)\n const existing = contexts.get(key)\n if (existing) {\n if (!existing.context_roles.includes(role)) {\n existing.context_roles.push(role)\n }\n return\n }\n\n contexts.set(key, {\n message: this.toEnvelope(message, indexes),\n context_roles: [role],\n })\n }\n\n for (const message of before) pushContext(message, 'chronology_before')\n if (replyParent) pushContext(replyParent, 'reply_parent')\n pushContext(target, 'target')\n for (const message of directBackreplies) pushContext(message, 'backreply_child')\n for (const message of after) pushContext(message, 'chronology_after')\n\n const contextMessages = [...contexts.values()].sort((left, right) => {\n const rank = (roles: ContextMessage['context_roles']): number => {\n if (roles.includes('chronology_before')) return 1\n if (roles.includes('reply_parent')) return 2\n if (roles.includes('target')) return 3\n if (roles.includes('backreply_child')) return 4\n return 5\n }\n\n return (\n rank(left.context_roles) - rank(right.context_roles) ||\n left.message.date.localeCompare(right.message.date) ||\n left.message.message_id - right.message.message_id\n )\n })\n\n return {\n target: this.toEnvelope(target, indexes),\n dialog,\n context_messages: contextMessages,\n window: {\n chronology_total_limit: DEFAULT_CHRONOLOGY_LIMIT,\n chronology_before_count: before.length,\n chronology_after_count: after.length,\n direct_reply_ancestor_included: Boolean(replyParent),\n direct_backreply_count_included: directBackreplies.length,\n },\n notes,\n }\n }\n\n private toMessageListItem(message: CacheMessageRecord, indexes: Indexes): MessageListItem {\n const dialog = indexes.dialogsById.get(message.saved_peer_id)\n const directBackreplyCount =\n indexes.backreplyIndex.get(makeMessageKey(message.saved_peer_id, message.message_id))?.length ?? 0\n\n return {\n message_id: message.message_id,\n saved_peer_id: message.saved_peer_id,\n dialog_title: dialog?.title ?? message.saved_peer_id,\n date: message.date,\n text_preview: previewText(message.text),\n from_self: message.from_self,\n forwarded: message.forwarded,\n reply_to_message_id: message.reply_to_message_id,\n direct_backreply_count: directBackreplyCount,\n queued_for_delete: message.queued_for_delete,\n }\n }\n\n private toEnvelope(message: CacheMessageRecord, indexes: Indexes): MessageEnvelope {\n const replyTarget =\n message.reply_to_message_id !== null\n ? indexes.messagesByKey.get(\n makeMessageKey(message.reply_to_saved_peer_id ?? message.saved_peer_id, message.reply_to_message_id),\n ) ?? null\n : null\n\n const directBackreplies =\n indexes.backreplyIndex.get(makeMessageKey(message.saved_peer_id, message.message_id)) ?? []\n\n const reply: MessageEnvelope['reply'] =\n message.reply_to_message_id === null\n ? {\n exists: false,\n target: null,\n status: 'resolved',\n }\n : {\n exists: true,\n target: replyTarget\n ? this.toMessageRef(replyTarget, 'reply_to')\n : {\n message_id: message.reply_to_message_id,\n saved_peer_id: message.reply_to_saved_peer_id ?? message.saved_peer_id,\n text_preview: '(missing from cache)',\n date: '',\n relationship: 'reply_to',\n },\n status: replyTarget ? 'resolved' : 'missing',\n }\n\n const backreplies: BackreplyEdgeSummary[] = directBackreplies.map((child) => ({\n message: this.toMessageRef(child, 'backreply'),\n thread_depth_from_target: 1,\n subtree_size_hint: this.countDescendants(child, indexes),\n }))\n\n return {\n message_id: message.message_id,\n saved_peer_id: message.saved_peer_id,\n date: message.date,\n edit_date: message.edit_date,\n text: message.text,\n text_preview: previewText(message.text),\n from_self: message.from_self,\n forwarded: message.forwarded,\n forward_origin: message.forward_origin,\n reply,\n backreplies,\n thread: {\n ancestors_known: this.countAncestors(message, indexes),\n direct_backreply_count: backreplies.length,\n descendant_count_hint: this.countDescendants(message, indexes),\n max_known_depth: this.maxDepth(message, indexes),\n },\n links: message.links,\n media_summary: message.media_summary,\n queued_for_delete: message.queued_for_delete,\n }\n }\n\n private toMessageRef(\n message: CacheMessageRecord,\n relationship: MessageRef['relationship'],\n ): MessageRef {\n return {\n message_id: message.message_id,\n saved_peer_id: message.saved_peer_id,\n text_preview: previewText(message.text),\n date: message.date,\n relationship,\n }\n }\n\n private findThreadRoot(message: CacheMessageRecord, indexes: Indexes): CacheMessageRecord {\n let current = message\n\n while (current.reply_to_message_id !== null) {\n const parent = indexes.messagesByKey.get(\n makeMessageKey(current.reply_to_saved_peer_id ?? current.saved_peer_id, current.reply_to_message_id),\n )\n if (!parent || parent.saved_peer_id !== current.saved_peer_id) {\n break\n }\n current = parent\n }\n\n return current\n }\n\n private buildThreadNode(\n message: CacheMessageRecord,\n indexes: Indexes,\n depth: number,\n ): ThreadInspectNode {\n const children =\n indexes.backreplyIndex.get(makeMessageKey(message.saved_peer_id, message.message_id)) ?? []\n\n return {\n message: this.toEnvelope(message, indexes),\n depth,\n children: children.map((child) => this.buildThreadNode(child, indexes, depth + 1)),\n }\n }\n\n private countAncestors(message: CacheMessageRecord, indexes: Indexes): number {\n let count = 0\n let current = message\n\n while (current.reply_to_message_id !== null) {\n const parent = indexes.messagesByKey.get(\n makeMessageKey(current.reply_to_saved_peer_id ?? current.saved_peer_id, current.reply_to_message_id),\n )\n if (!parent) break\n count += 1\n current = parent\n }\n\n return count\n }\n\n private countDescendants(message: CacheMessageRecord, indexes: Indexes): number {\n const children =\n indexes.backreplyIndex.get(makeMessageKey(message.saved_peer_id, message.message_id)) ?? []\n\n return children.reduce((count, child) => count + 1 + this.countDescendants(child, indexes), 0)\n }\n\n private maxDepth(message: CacheMessageRecord, indexes: Indexes): number {\n const children =\n indexes.backreplyIndex.get(makeMessageKey(message.saved_peer_id, message.message_id)) ?? []\n if (children.length === 0) return 0\n return 1 + Math.max(...children.map((child) => this.maxDepth(child, indexes)))\n }\n}\n\nfunction makeMessageKey(savedPeerId: string, messageId: number): string {\n return `${savedPeerId}:${messageId}`\n}\n\nfunction encodeCursor(offset: number): string {\n return Buffer.from(String(offset), 'utf8').toString('base64url')\n}\n\nfunction decodeCursor(cursor?: string | null): number {\n if (!cursor) return 0\n\n try {\n const value = Number(Buffer.from(cursor, 'base64url').toString('utf8'))\n return Number.isFinite(value) && value >= 0 ? value : 0\n } catch {\n return 0\n }\n}\n\nfunction compareByDateAsc(left: CacheMessageRecord, right: CacheMessageRecord): number {\n return left.date.localeCompare(right.date) || left.message_id - right.message_id\n}\n\nfunction compareByDateDesc(left: CacheMessageRecord, right: CacheMessageRecord): number {\n return right.date.localeCompare(left.date) || right.message_id - left.message_id\n}\n\nfunction matchesSearch(message: CacheMessageRecord, query: string): boolean {\n const normalized = query.trim().toLowerCase()\n if (!normalized) return true\n\n return [\n message.text,\n message.forward_origin?.title ?? '',\n message.saved_peer_id,\n ]\n .join('\\n')\n .toLowerCase()\n .includes(normalized)\n}\n\nfunction previewText(text: string, limit = 80): string {\n const normalized = text.replace(/\\s+/g, ' ').trim()\n if (!normalized) return '(no text)'\n return normalized.length <= limit ? normalized : `${normalized.slice(0, limit - 1)}…`\n}\n","import path from 'node:path'\nimport { createInterface } from 'node:readline/promises'\nimport { readFile, writeFile } from 'node:fs/promises'\nimport { TelegramClient, getMarkedPeerId, networkMiddlewares, type tl } from '@mtcute/node'\nimport { TgsmError } from './errors'\nimport type {\n AuthStatus,\n CacheMessageRecord,\n ForwardOriginSummary,\n LinkSummary,\n SavedDialogSummary,\n SourceSnapshot,\n TelegramLoginInput,\n TgsmSourceAdapter,\n} from './types'\n\ninterface TelegramConfig {\n apiId: number\n apiHash: string\n phone?: string\n}\n\ninterface EntityLookup {\n users: Map<number, tl.TypeUser>\n chats: Map<number, tl.TypeChat>\n selfUserId: number\n}\n\ninterface TelegramSourceOptions {\n debug?: boolean\n logger?: (event: string, fields?: Record<string, unknown>) => void\n}\n\nconst TELEGRAM_FLOOD_WAIT_MAX_MS = 30_000\nconst TELEGRAM_CONNECT_TIMEOUT_MS = 30_000\nconst TELEGRAM_RPC_TIMEOUT_MS = 45_000\nconst TELEGRAM_DESTROY_TIMEOUT_MS = 5_000\n\nexport class TelegramSource implements TgsmSourceAdapter {\n readonly backend = 'telegram' as const\n\n constructor(private readonly options: TelegramSourceOptions = {}) {}\n\n async authLogin(accountDir: string, input: TelegramLoginInput): Promise<AuthStatus> {\n const config: TelegramConfig = {\n apiId: input.apiId,\n apiHash: input.apiHash,\n phone: input.phone,\n }\n\n await saveTelegramConfig(accountDir, config)\n this.debug('auth.config_saved', { account_dir: accountDir, phone: input.phone })\n const client = createTelegramClient(accountDir, config, this.options)\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n })\n\n try {\n this.debug('auth.start.begin', { account_dir: accountDir })\n const user = await client.start({\n phone: input.phone,\n code: async () => {\n if (input.code && input.code.trim().length > 0) {\n return input.code\n }\n\n return rl.question('Code: ')\n },\n password: async () =>\n input.password ??\n (await rl.question('2FA password (leave empty if not enabled): ')),\n codeSentCallback: async (sentCode) => {\n const type =\n typeof sentCode.type === 'string'\n ? sentCode.type\n : 'unknown'\n process.stderr.write(`Login code requested via ${type}.\\n`)\n },\n invalidCodeCallback: async (type) => {\n process.stderr.write(`Invalid ${type}, please try again.\\n`)\n },\n })\n\n this.debug('auth.start.done', { user_id: String(user.id), display_name: user.displayName })\n return {\n authenticated: true,\n user: {\n id: String(user.id),\n display_name: user.displayName,\n },\n }\n } catch (error) {\n throw new TgsmError({\n code: 'AUTH_FAILED',\n message: error instanceof Error ? error.message : 'Telegram auth failed.',\n retryable: false,\n })\n } finally {\n this.debug('auth.cleanup.begin')\n rl.close()\n const cleanup = await destroyClientQuietly(client)\n this.debug('auth.cleanup.done', { status: cleanup })\n }\n }\n\n async authStatus(accountDir: string): Promise<AuthStatus> {\n const config = await loadTelegramConfig(accountDir)\n if (!config) {\n return {\n authenticated: false,\n user: null,\n }\n }\n\n const client = createTelegramClient(accountDir, config, this.options)\n\n try {\n this.debug('auth_status.start.begin', { account_dir: accountDir })\n await withTimeout(\n client.start({}),\n TELEGRAM_CONNECT_TIMEOUT_MS,\n 'Timed out while connecting to Telegram.',\n )\n const me = await client.getMe()\n this.debug('auth_status.start.done', { user_id: String(me.id), display_name: me.displayName })\n return {\n authenticated: true,\n user: {\n id: String(me.id),\n display_name: me.displayName,\n },\n }\n } catch {\n this.debug('auth_status.start.failed')\n return {\n authenticated: false,\n user: null,\n }\n } finally {\n this.debug('auth_status.cleanup.begin')\n const cleanup = await destroyClientQuietly(client)\n this.debug('auth_status.cleanup.done', { status: cleanup })\n }\n }\n\n async sync(accountDir: string): Promise<SourceSnapshot> {\n const config = await loadTelegramConfig(accountDir)\n if (!config) {\n throw new TgsmError({\n code: 'AUTH_REQUIRED',\n message: 'Telegram credentials are not configured.',\n retryable: false,\n suggestion: 'Run `tgsm auth login` first.',\n })\n }\n\n const client = createTelegramClient(accountDir, config, this.options)\n\n try {\n this.debug('sync.start.begin', { account_dir: accountDir })\n const me = await withTimeout(\n client.start({}),\n TELEGRAM_CONNECT_TIMEOUT_MS,\n 'Timed out while connecting to Telegram.',\n )\n this.debug('sync.start.done', { user_id: String(me.id), display_name: me.displayName })\n const syncedAt = new Date().toISOString()\n this.debug('sync.dialogs.begin')\n const dialogsResponse = await withTimeout(\n client.call({\n _: 'messages.getSavedDialogs',\n excludePinned: false,\n offsetDate: 0,\n offsetId: 0,\n offsetPeer: { _: 'inputPeerEmpty' },\n limit: 1000,\n hash: 0 as never,\n }, {\n maxRetryCount: 5,\n floodSleepThreshold: TELEGRAM_FLOOD_WAIT_MAX_MS,\n }),\n TELEGRAM_RPC_TIMEOUT_MS,\n 'Timed out while fetching saved dialogs from Telegram.',\n )\n this.debug('sync.dialogs.done', {\n result_type: dialogsResponse._,\n dialog_count:\n 'dialogs' in dialogsResponse && Array.isArray(dialogsResponse.dialogs)\n ? dialogsResponse.dialogs.length\n : 0,\n })\n\n if (dialogsResponse._ === 'messages.savedDialogsNotModified') {\n return {\n backend: this.backend,\n account: {\n id: String(me.id),\n display_name: me.displayName,\n },\n dialogs: [],\n messages: [],\n synced_at: syncedAt,\n }\n }\n\n const lookup: EntityLookup = {\n users: new Map(),\n chats: new Map(),\n selfUserId: Number(me.id),\n }\n\n ingestEntities(lookup, dialogsResponse.users, dialogsResponse.chats)\n\n const dialogs: SavedDialogSummary[] = []\n const messagesByKey = new Map<string, CacheMessageRecord>()\n\n for (const dialog of dialogsResponse.dialogs) {\n if (dialog._ !== 'savedDialog') continue\n\n const savedPeerId = savedPeerIdFromPeer(dialog.peer, lookup.selfUserId)\n const peerInput = await client.resolvePeer(getMarkedPeerId(dialog.peer))\n const title = peerTitle(dialog.peer, lookup)\n\n this.debug('sync.dialog_history.begin', {\n saved_peer_id: savedPeerId,\n title,\n top_message_id: dialog.topMessage ?? null,\n })\n const messages = await fetchSavedHistory(client, peerInput, lookup, this.options)\n this.debug('sync.dialog_history.done', {\n saved_peer_id: savedPeerId,\n title,\n message_count: messages.length,\n })\n const topMessage = messages.find((message) => message.message_id === dialog.topMessage) ?? messages[0] ?? null\n\n dialogs.push({\n saved_peer_id: savedPeerId,\n kind: peerKindFromPeer(dialog.peer, lookup.selfUserId),\n title,\n top_message_id: dialog.topMessage ?? topMessage?.message_id ?? null,\n top_text_preview: topMessage ? previewText(topMessage.text) : null,\n message_count: messages.length,\n pinned: dialog.pinned ?? null,\n last_synced_at: syncedAt,\n })\n\n for (const message of messages) {\n messagesByKey.set(`${message.saved_peer_id}:${message.message_id}`, message)\n }\n }\n\n return {\n backend: this.backend,\n account: {\n id: String(me.id),\n display_name: me.displayName,\n },\n dialogs,\n messages: [...messagesByKey.values()].sort((a, b) =>\n a.saved_peer_id.localeCompare(b.saved_peer_id) || a.date.localeCompare(b.date) || a.message_id - b.message_id,\n ),\n synced_at: syncedAt,\n }\n } catch (error) {\n this.debug('sync.failed', {\n message: error instanceof Error ? error.message : 'Unknown sync error',\n })\n throw new TgsmError({\n code: 'TELEGRAM_SYNC_FAILED',\n message: error instanceof Error ? error.message : 'Telegram sync failed.',\n retryable: true,\n })\n } finally {\n this.debug('sync.cleanup.begin')\n const cleanup = await destroyClientQuietly(client)\n this.debug('sync.cleanup.done', { status: cleanup })\n }\n }\n\n private debug(event: string, fields: Record<string, unknown> = {}): void {\n if (!this.options.debug) return\n this.options.logger?.(event, fields)\n }\n}\n\nfunction createTelegramClient(\n accountDir: string,\n config: TelegramConfig,\n options: TelegramSourceOptions,\n): TelegramClient {\n const client = new TelegramClient({\n apiId: config.apiId,\n apiHash: config.apiHash,\n storage: path.join(accountDir, 'mtcute-session'),\n logLevel: 0,\n network: {\n middlewares: networkMiddlewares.basic({\n floodWaiter: {\n maxWait: TELEGRAM_FLOOD_WAIT_MAX_MS,\n maxRetries: 5,\n onBeforeWait: (ctx, seconds) => {\n options.logger?.('telegram.flood_wait', {\n seconds,\n method: ctx.request._,\n })\n },\n },\n }),\n },\n })\n\n if (options.debug) {\n client.onConnectionState.add((state) => {\n options.logger?.('telegram.connection_state', { state })\n })\n client.onError.add((error) => {\n options.logger?.('telegram.client_error', {\n message: error.message,\n name: error.name,\n })\n })\n }\n\n return client\n}\n\nasync function destroyClientQuietly(client: TelegramClient): Promise<'destroyed' | 'timed_out' | 'failed'> {\n try {\n await withTimeout(\n client.destroy(),\n TELEGRAM_DESTROY_TIMEOUT_MS,\n 'Timed out while closing Telegram client.',\n )\n return 'destroyed'\n } catch (error) {\n if (error instanceof Error && error.message === 'Timed out while closing Telegram client.') {\n return 'timed_out'\n }\n return 'failed'\n }\n}\n\nasync function fetchSavedHistory(\n client: TelegramClient,\n peer: tl.TypeInputPeer,\n lookup: EntityLookup,\n options: TelegramSourceOptions,\n): Promise<CacheMessageRecord[]> {\n let offsetId = 0\n let offsetDate = 0\n const records = new Map<string, CacheMessageRecord>()\n let pages = 0\n\n while (true) {\n pages += 1\n if (options.debug) {\n options.logger?.('sync.dialog_history.page.begin', {\n page: pages,\n offset_id: offsetId,\n offset_date: offsetDate,\n })\n }\n const history = await withTimeout(\n client.call({\n _: 'messages.getSavedHistory',\n peer,\n offsetId,\n offsetDate,\n addOffset: 0,\n limit: 100,\n maxId: 0,\n minId: 0,\n hash: 0 as never,\n }, {\n maxRetryCount: 5,\n floodSleepThreshold: TELEGRAM_FLOOD_WAIT_MAX_MS,\n }),\n TELEGRAM_RPC_TIMEOUT_MS,\n 'Timed out while fetching saved history from Telegram.',\n )\n if (options.debug) {\n options.logger?.('sync.dialog_history.page.done', {\n page: pages,\n result_type: history._,\n message_count: 'messages' in history ? history.messages.length : 0,\n })\n }\n\n if (!('messages' in history)) {\n break\n }\n\n ingestEntities(lookup, history.users ?? [], history.chats ?? [])\n\n const rawMessages = history.messages.filter(\n (message: tl.TypeMessage): message is tl.RawMessage | tl.RawMessageService =>\n message._ === 'message' || message._ === 'messageService',\n )\n\n if (rawMessages.length === 0) {\n break\n }\n\n for (const raw of rawMessages) {\n const record = normalizeRawMessage(raw, lookup)\n records.set(`${record.saved_peer_id}:${record.message_id}`, record)\n }\n\n const oldest = rawMessages[rawMessages.length - 1]!\n offsetId = oldest.id\n offsetDate = oldest.date\n\n if (rawMessages.length < 100) {\n break\n }\n }\n\n return [...records.values()].sort((a, b) => a.date.localeCompare(b.date) || a.message_id - b.message_id)\n}\n\nexport function normalizeRawMessage(raw: tl.RawMessage | tl.RawMessageService, lookup: EntityLookup): CacheMessageRecord {\n const savedPeerId = savedPeerIdFromPeer(raw.peerId, lookup.selfUserId)\n const replyHeader =\n 'replyTo' in raw && raw.replyTo && raw.replyTo._ === 'messageReplyHeader' ? raw.replyTo : null\n const replyPeer = replyHeader?.replyToPeerId ?? raw.peerId\n const fwdFrom = 'fwdFrom' in raw ? raw.fwdFrom : undefined\n const editDate = 'editDate' in raw ? raw.editDate : undefined\n const media = 'media' in raw ? raw.media : undefined\n const text = 'message' in raw ? raw.message : ''\n\n return {\n message_id: raw.id,\n saved_peer_id: savedPeerId,\n date: new Date(raw.date * 1000).toISOString(),\n edit_date: editDate ? new Date(editDate * 1000).toISOString() : null,\n text,\n from_self:\n ('out' in raw ? Boolean(raw.out) : false) ||\n peerIsSelf('fromId' in raw ? raw.fromId : null, lookup.selfUserId) ||\n (savedPeerId === 'self' && !fwdFrom),\n forwarded: Boolean(fwdFrom),\n forward_origin: fwdFrom ? forwardOriginFromHeader(fwdFrom, lookup) : null,\n reply_to_message_id: replyHeader?.replyToMsgId ?? null,\n reply_to_saved_peer_id: replyHeader?.replyToMsgId\n ? savedPeerIdFromPeer(replyPeer, lookup.selfUserId)\n : null,\n links: extractLinks(text),\n media_summary: mediaSummary(media),\n queued_for_delete: false,\n }\n}\n\nfunction forwardOriginFromHeader(\n header: tl.RawMessageFwdHeader,\n lookup: EntityLookup,\n): ForwardOriginSummary {\n if (header.savedFromPeer) {\n return {\n saved_peer_id: savedPeerIdFromPeer(header.savedFromPeer, lookup.selfUserId),\n title: peerTitle(header.savedFromPeer, lookup),\n message_id: header.savedFromMsgId ?? null,\n }\n }\n\n if (header.fromId) {\n return {\n saved_peer_id: savedPeerIdFromPeer(header.fromId, lookup.selfUserId),\n title: header.fromName ?? peerTitle(header.fromId, lookup),\n message_id: header.savedFromMsgId ?? null,\n }\n }\n\n return {\n saved_peer_id: null,\n title: header.fromName ?? header.savedFromName ?? null,\n message_id: header.savedFromMsgId ?? null,\n }\n}\n\nfunction peerIsSelf(peer: tl.TypePeer | undefined | null, selfUserId: number): boolean {\n return Boolean(peer && peer._ === 'peerUser' && peer.userId === selfUserId)\n}\n\nfunction peerKindFromPeer(peer: tl.TypePeer, selfUserId: number): SavedDialogSummary['kind'] {\n if (peer._ === 'peerUser' && peer.userId === selfUserId) return 'self'\n if (peer._ === 'peerUser' || peer._ === 'peerChat') return 'peer'\n if (peer._ === 'peerChannel') return 'channel'\n return 'unknown'\n}\n\nexport function savedPeerIdFromPeer(peer: tl.TypePeer, selfUserId: number): string {\n if (peer._ === 'peerUser' && peer.userId === selfUserId) {\n return 'self'\n }\n\n if (peer._ === 'peerUser') return `user:${peer.userId}`\n if (peer._ === 'peerChat') return `chat:${peer.chatId}`\n if (peer._ === 'peerChannel') return `channel:${peer.channelId}`\n return `unknown:${getMarkedPeerId(peer)}`\n}\n\nfunction peerTitle(peer: tl.TypePeer, lookup: EntityLookup): string {\n if (peer._ === 'peerUser') {\n if (peer.userId === lookup.selfUserId) return 'Self'\n const user = lookup.users.get(peer.userId)\n const firstName = user && user._ === 'user' ? user.firstName ?? '' : ''\n const lastName = user && user._ === 'user' ? user.lastName ?? '' : ''\n const username = user && user._ === 'user' ? user.username ?? '' : ''\n const parts = [firstName, lastName].filter(Boolean)\n return parts.join(' ').trim() || username || `User ${peer.userId}`\n }\n\n const entity = lookup.chats.get(peer._ === 'peerChat' ? peer.chatId : peer.channelId)\n if (entity && 'title' in entity) {\n return entity.title\n }\n\n if (peer._ === 'peerChat') return `Chat ${peer.chatId}`\n if (peer._ === 'peerChannel') return `Channel ${peer.channelId}`\n return 'Unknown'\n}\n\nfunction ingestEntities(lookup: EntityLookup, users: tl.TypeUser[], chats: tl.TypeChat[]): void {\n for (const user of users) {\n if ('id' in user) {\n lookup.users.set(Number(user.id), user)\n }\n }\n\n for (const chat of chats) {\n if ('id' in chat) {\n lookup.chats.set(Number(chat.id), chat)\n }\n }\n}\n\nfunction extractLinks(text: string): LinkSummary[] {\n const matches = text.matchAll(/https?:\\/\\/[^\\s)]+/g)\n const seen = new Set<string>()\n const links: LinkSummary[] = []\n\n for (const match of matches) {\n const url = match[0]!\n if (seen.has(url)) continue\n seen.add(url)\n links.push({ url })\n }\n\n return links\n}\n\nfunction mediaSummary(media: tl.TypeMessageMedia | undefined): string | null {\n if (!media) return null\n return media._\n}\n\nfunction previewText(text: string, limit = 80): string {\n const normalized = text.replace(/\\s+/g, ' ').trim()\n if (!normalized) return '(no text)'\n return normalized.length <= limit ? normalized : `${normalized.slice(0, limit - 1)}…`\n}\n\nasync function loadTelegramConfig(accountDir: string): Promise<TelegramConfig | null> {\n try {\n const raw = await readFile(path.join(accountDir, 'telegram.json'), 'utf8')\n return JSON.parse(raw) as TelegramConfig\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null\n throw error\n }\n}\n\nasync function saveTelegramConfig(accountDir: string, config: TelegramConfig): Promise<void> {\n await writeFile(\n path.join(accountDir, 'telegram.json'),\n `${JSON.stringify(config, null, 2)}\\n`,\n 'utf8',\n )\n}\n\nasync function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n message: string,\n): Promise<T> {\n let timer: NodeJS.Timeout | undefined\n\n try {\n return await Promise.race([\n promise,\n new Promise<T>((_, reject) => {\n timer = setTimeout(() => {\n reject(new Error(message))\n }, timeoutMs)\n }),\n ])\n } finally {\n if (timer) {\n clearTimeout(timer)\n }\n }\n}\n","import type {\n AuthStatus,\n MessageContextBundle,\n MessageListItem,\n SavedDialogSummary,\n SearchResultPage,\n SyncResult,\n ThreadInspectNode,\n ThreadInspectResult,\n} from '@tgsm/core'\n\nexport function formatSyncResult(result: SyncResult): string {\n return [\n `sync backend=${result.backend}`,\n `synced_at=${result.synced_at}`,\n `dialogs=${result.synced_dialogs}`,\n `messages=${result.synced_messages}`,\n ].join('\\n')\n}\n\nexport function formatAuthStatus(status: AuthStatus): string {\n if (!status.authenticated || !status.user) {\n return 'authenticated: false'\n }\n\n return [`authenticated: true`, `user: ${status.user.display_name}`, `id: ${status.user.id}`].join('\\n')\n}\n\nexport function formatSavedDialogs(dialogs: SavedDialogSummary[]): string {\n if (dialogs.length === 0) return 'No saved dialogs found.'\n\n return dialogs\n .map((dialog) =>\n [\n `${dialog.saved_peer_id} (${dialog.kind})`,\n `title: ${dialog.title}`,\n `messages: ${dialog.message_count}`,\n `top_message_id: ${dialog.top_message_id ?? 'n/a'}`,\n `top_text: ${dialog.top_text_preview ?? '(none)'}`,\n ].join('\\n'),\n )\n .join('\\n\\n')\n}\n\nexport function formatMessagesPage(page: SearchResultPage<MessageListItem>): string {\n if (page.items.length === 0) return 'No messages found.'\n\n const header =\n page.scope === 'saved_dialog'\n ? `messages scope=${page.saved_peer_id} total=${page.result_count}`\n : `messages scope=all_saved_dialogs total=${page.result_count}`\n\n const blocks = page.items.map((item) =>\n [\n `#${item.message_id} ${item.date}`,\n `dialog: ${item.saved_peer_id} (${item.dialog_title})`,\n `from_self: ${item.from_self} forwarded: ${item.forwarded}`,\n `reply_to: ${item.reply_to_message_id ?? 'none'} backreplies: ${item.direct_backreply_count}`,\n `text: ${item.text_preview}`,\n ].join('\\n'),\n )\n\n return [header, ...blocks, page.next_cursor ? `next_cursor: ${page.next_cursor}` : ''].filter(Boolean).join('\\n\\n')\n}\n\nexport function formatContextBundle(bundle: MessageContextBundle): string {\n const lines: string[] = [\n `MESSAGE #${bundle.target.message_id}`,\n `dialog: ${bundle.dialog.saved_peer_id} (${bundle.dialog.title})`,\n `date: ${bundle.target.date}`,\n `from_self: ${bundle.target.from_self}`,\n `thread: direct_backreplies=${bundle.target.thread.direct_backreply_count} descendant_hint=${bundle.target.thread.descendant_count_hint ?? 0}`,\n '',\n 'text:',\n bundle.target.text || '(no text)',\n ]\n\n const replySection = bundle.target.reply.exists\n ? bundle.target.reply.target\n ? [``, 'reply_to:', `- #${bundle.target.reply.target.message_id} ${bundle.target.reply.target.text_preview}`]\n : [``, 'reply_to:', '- (missing from cache)']\n : []\n\n const backreplySection =\n bundle.target.backreplies.length > 0\n ? [\n '',\n 'backreplies:',\n ...bundle.target.backreplies.map(\n (item) => `- #${item.message.message_id} ${item.message.text_preview}`,\n ),\n ]\n : []\n\n const before = bundle.context_messages.filter((item) => item.context_roles.includes('chronology_before'))\n const after = bundle.context_messages.filter((item) => item.context_roles.includes('chronology_after'))\n\n if (before.length > 0) {\n lines.push('', 'chronology_before:')\n for (const item of before) lines.push(`- #${item.message.message_id} ${item.message.text_preview}`)\n }\n\n if (after.length > 0) {\n lines.push('', 'chronology_after:')\n for (const item of after) lines.push(`- #${item.message.message_id} ${item.message.text_preview}`)\n }\n\n lines.push(...replySection, ...backreplySection)\n\n if (bundle.notes.length > 0) {\n lines.push('', 'notes:')\n for (const note of bundle.notes) lines.push(`- ${note}`)\n }\n\n return lines.join('\\n')\n}\n\nexport function formatThread(result: ThreadInspectResult): string {\n const lines = [`THREAD #${result.root.message_id}`, `dialog: ${result.dialog.saved_peer_id} (${result.dialog.title})`, '']\n\n const root = result.nodes[0]\n if (!root) return lines.join('\\n')\n\n renderNode(root, '', true, lines)\n return lines.join('\\n')\n}\n\nfunction renderNode(node: ThreadInspectNode, prefix: string, isLast: boolean, lines: string[]): void {\n const branch = prefix ? `${prefix}${isLast ? '└── ' : '├── '}` : ''\n lines.push(`${branch}#${node.message.message_id} ${node.message.text_preview}`)\n\n const nextPrefix = prefix ? `${prefix}${isLast ? ' ' : '│ '}` : ''\n node.children.forEach((child, index) => {\n renderNode(child, nextPrefix, index === node.children.length - 1, lines)\n })\n}\n","{\n \"name\": \"@imadtg/tgsm\",\n \"version\": \"0.0.7\",\n \"type\": \"module\",\n \"description\": \"A retrieval-first CLI for navigating Telegram Saved Messages as structured, agent-readable context.\",\n \"license\": \"MIT\",\n \"homepage\": \"https://github.com/imadtg/tgsm\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/imadtg/tgsm.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/imadtg/tgsm/issues\"\n },\n \"keywords\": [\n \"telegram\",\n \"saved-messages\",\n \"cli\",\n \"agents\",\n \"mtproto\"\n ],\n \"engines\": {\n \"node\": \">=20\"\n },\n \"bin\": {\n \"tgsm\": \"dist/index.js\"\n },\n \"main\": \"./dist/index.js\",\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"test\": \"bun test\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"dependencies\": {\n \"@mtcute/node\": \"^0.28.2\",\n \"commander\": \"^14.0.1\"\n },\n \"devDependencies\": {\n \"@tgsm/core\": \"workspace:*\"\n }\n}\n"],"mappings":"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","names":["createInterface","process","readFile","readFile","path","path","path","readFile","writeFile","messages","previewText","path","readFile","writeFile","createInterface","process"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imadtg/tgsm",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A retrieval-first CLI for navigating Telegram Saved Messages as structured, agent-readable context.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,6 +43,6 @@
|
|
|
43
43
|
"commander": "^14.0.1"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@tgsm/core": "
|
|
46
|
+
"@tgsm/core": "0.0.1"
|
|
47
47
|
}
|
|
48
48
|
}
|