@iola_adm/iola-cli 0.2.25 → 0.2.27
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/package.json +1 -1
- package/src/cli.js +23 -3
- package/test/smoke-test.js +2 -0
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -4038,7 +4038,7 @@ async function yandexMailSend(args = {}) {
|
|
|
4038
4038
|
await smtpCommand(session, `MAIL FROM:<${email}>`);
|
|
4039
4039
|
for (const recipient of to) await smtpCommand(session, `RCPT TO:<${recipient}>`);
|
|
4040
4040
|
await smtpCommand(session, "DATA", { expect: /^354/u });
|
|
4041
|
-
await smtpCommand(session, `${buildMimeMessage({ from: email, to, subject, text })}\r\n.`);
|
|
4041
|
+
await smtpCommand(session, `${dotStuffSmtpData(buildMimeMessage({ from: email, to, subject, text }))}\r\n.`);
|
|
4042
4042
|
await smtpCommand(session, "QUIT").catch(() => {});
|
|
4043
4043
|
return { from: email, to, subject, status: "sent" };
|
|
4044
4044
|
} finally {
|
|
@@ -4052,18 +4052,28 @@ function buildXoauth2(email, token) {
|
|
|
4052
4052
|
|
|
4053
4053
|
function buildMimeMessage({ from, to, subject, text }) {
|
|
4054
4054
|
const encodedSubject = Buffer.from(subject, "utf8").toString("base64");
|
|
4055
|
+
const messageIdDomain = String(from || "").split("@")[1] || "localhost";
|
|
4056
|
+
const messageId = `${randomUUID()}@${messageIdDomain}`;
|
|
4055
4057
|
return [
|
|
4056
4058
|
`From: ${from}`,
|
|
4057
4059
|
`To: ${to.join(", ")}`,
|
|
4060
|
+
`Reply-To: ${from}`,
|
|
4058
4061
|
`Subject: =?UTF-8?B?${encodedSubject}?=`,
|
|
4062
|
+
`Date: ${new Date().toUTCString()}`,
|
|
4063
|
+
`Message-ID: <${messageId}>`,
|
|
4059
4064
|
"MIME-Version: 1.0",
|
|
4060
4065
|
"Content-Type: text/plain; charset=utf-8",
|
|
4061
4066
|
"Content-Transfer-Encoding: base64",
|
|
4067
|
+
"X-Mailer: IOLA CLI",
|
|
4062
4068
|
"",
|
|
4063
4069
|
Buffer.from(text.replace(/\r?\n/g, "\r\n"), "utf8").toString("base64").replace(/.{1,76}/g, "$&\r\n").trim(),
|
|
4064
4070
|
].join("\r\n");
|
|
4065
4071
|
}
|
|
4066
4072
|
|
|
4073
|
+
function dotStuffSmtpData(message) {
|
|
4074
|
+
return String(message || "").replace(/\r?\n/g, "\r\n").replace(/(^|\r\n)\./g, "$1..");
|
|
4075
|
+
}
|
|
4076
|
+
|
|
4067
4077
|
function imapConnect() {
|
|
4068
4078
|
return tlsLineSession("imap.yandex.ru", 993, "* OK");
|
|
4069
4079
|
}
|
|
@@ -9578,7 +9588,7 @@ async function buildYandexDirectAnswer(question, history = []) {
|
|
|
9578
9588
|
const normalized = String(question || "").toLocaleLowerCase("ru-RU");
|
|
9579
9589
|
const previousAssistantText = [...(history || [])].reverse().find((item) => item.role === "assistant")?.content || "";
|
|
9580
9590
|
const mailContext = /Яндекс Почта|Письмо #|\bUID\b|#\d{3,}/iu.test(previousAssistantText);
|
|
9581
|
-
if (
|
|
9591
|
+
if (!isYandexServiceQuestion(normalized) && !(/^\s*\d{3,}\s*$/u.test(question) && mailContext)) return "";
|
|
9582
9592
|
try {
|
|
9583
9593
|
if (/^\s*\d{3,}\s*$/u.test(question) && mailContext) {
|
|
9584
9594
|
const uid = extractYandexMailUid(question);
|
|
@@ -9587,7 +9597,7 @@ async function buildYandexDirectAnswer(question, history = []) {
|
|
|
9587
9597
|
return formatYandexMailRead(row);
|
|
9588
9598
|
}
|
|
9589
9599
|
|
|
9590
|
-
if (
|
|
9600
|
+
if (isYandexIdentityQuestion(normalized)) {
|
|
9591
9601
|
const profile = await getYandexIdentityProfile();
|
|
9592
9602
|
return [
|
|
9593
9603
|
"Подключен Yandex ID:",
|
|
@@ -9648,6 +9658,16 @@ async function buildYandexDirectAnswer(question, history = []) {
|
|
|
9648
9658
|
return "";
|
|
9649
9659
|
}
|
|
9650
9660
|
|
|
9661
|
+
function isYandexServiceQuestion(normalized) {
|
|
9662
|
+
return /(яндекс|яндес|язндекс|язндекс|яндкс|yandex|почт|письм|календар|контакт|телемост)/iu.test(String(normalized || ""));
|
|
9663
|
+
}
|
|
9664
|
+
|
|
9665
|
+
function isYandexIdentityQuestion(normalized) {
|
|
9666
|
+
const text = String(normalized || "");
|
|
9667
|
+
return /(аккаунт|профил|логин|кто подключен|какой.*подключен|email|e-mail)/iu.test(text)
|
|
9668
|
+
&& /(яндекс|яндес|язндекс|язндекс|яндкс|yandex)/iu.test(text);
|
|
9669
|
+
}
|
|
9670
|
+
|
|
9651
9671
|
function cleanupYandexQuery(question) {
|
|
9652
9672
|
const stop = /^(?:в|на|у|из|для|по|яндекс|yandex|найди|поиск|покажи|посмотри|проверь|почт\p{L}*|письм\p{L}*|календар\p{L}*|контакт\p{L}*)$/iu;
|
|
9653
9673
|
return String(question || "")
|
package/test/smoke-test.js
CHANGED
|
@@ -76,6 +76,8 @@ assertIncludes(cliSource, "partial (", "Yandex connector status should report pa
|
|
|
76
76
|
assertIncludes(cliSource, "hasYandexOAuthAppToken", "Yandex setup should detect tokens per OAuth app");
|
|
77
77
|
assertIncludes(cliSource, "isYandexConnectorFullyConnected", "Yandex master status should require all OAuth app tokens");
|
|
78
78
|
assertIncludes(cliSource, "--app", "Yandex token command should persist tokens by OAuth app group");
|
|
79
|
+
assertIncludes(cliSource, "isYandexIdentityQuestion", "Yandex ID questions should be handled directly");
|
|
80
|
+
assertIncludes(cliSource, "язндекс", "Yandex direct router should tolerate common typos");
|
|
79
81
|
assertNotIncludes(cliSource, "Сервисы через запятую [identity,disk]", "Yandex setup should not ask for services during connector setup");
|
|
80
82
|
if (!packageJson.files.includes("docs/assets/iola-oauth-icon.png")) {
|
|
81
83
|
throw new Error("package files should include the Yandex OAuth icon");
|