@iola_adm/iola-cli 0.2.8 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -179,6 +179,7 @@ iola geo services "Йошкар-Ола, улица Петрова, 15"
179
179
  iola cloud setup yandex-disk
180
180
  iola cloud setup mailru-cloud
181
181
  iola cloud status
182
+ iola cloud mkdir /IOLA/Фото
182
183
  iola cloud find "справка" --path /IOLA
183
184
  iola cloud upload report.md /IOLA/reports/report.md
184
185
  iola cloud share /IOLA/reports/report.md
@@ -19,7 +19,7 @@ const steps = [
19
19
  },
20
20
  {
21
21
  title: "Проверка локальной модели IOLA",
22
- args: [cliPath, "ai", "setup", "iola", "--yes", "--quiet", "--optional"],
22
+ args: [cliPath, "ai", "setup", "iola", "--yes", "--quiet", "--optional", "--preserve-active"],
23
23
  },
24
24
  ];
25
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iola_adm/iola-cli",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "description": "CLI и AI-агент городского округа Йошкар-Ола.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/adm-iola/iola-cli#readme",
@@ -12,6 +12,7 @@ description: Личные документы пользователя в обл
12
12
  Основные сценарии:
13
13
 
14
14
  - `cloud-find-document` - найти документ в подключенном облаке.
15
+ - `cloud-create-folder` - создать папку на подключенном облачном диске.
15
16
  - `cloud-save-result` - сохранить ответ, отчет, карточку или список в облако.
16
17
  - `cloud-share-link` - создать публичную ссылку на файл в Яндекс Диске.
17
18
  - `cloud-document-pack` - собрать папку с материалами для обращения.
package/src/cli.js CHANGED
@@ -1961,9 +1961,10 @@ async function doctor(args = []) {
1961
1961
  printKeyValue(report.ai);
1962
1962
  console.log("");
1963
1963
  console.log("Skills/Toolsets/Daemon");
1964
- printKeyValue({ ...report.skills, toolsets: report.toolsets.enabled, daemon: report.daemon.status });
1964
+ printKeyValueFull({ ...report.skills, toolsets: report.toolsets.enabled, daemon: report.daemon.status });
1965
1965
  console.log("");
1966
- printDiagnostics(diagnostics, recommendOllamaModel(diagnostics));
1966
+ const isLocalAi = ["iola", "ollama"].includes(activeAiProfile.provider);
1967
+ printDiagnostics(diagnostics, isLocalAi ? recommendOllamaModel(diagnostics) : null);
1967
1968
  if (options.all) {
1968
1969
  console.log("");
1969
1970
  console.log("Фичи");
@@ -2948,6 +2949,14 @@ async function handleCloud(args) {
2948
2949
  return;
2949
2950
  }
2950
2951
 
2952
+ if (action === "mkdir" || action === "create-folder") {
2953
+ const provider = await getCloudProvider(options.provider);
2954
+ const remotePath = target || `${cloudRootForProvider(provider)}/Новая папка`;
2955
+ const result = await cloudCreateFolder(provider, remotePath);
2956
+ printKeyValue(result);
2957
+ return;
2958
+ }
2959
+
2951
2960
  if (action === "upload") {
2952
2961
  if (!target) throw new Error('Пример: iola cloud upload report.md /IOLA/reports/report.md');
2953
2962
  const provider = await getCloudProvider(options.provider);
@@ -3004,6 +3013,7 @@ async function handleCloud(args) {
3004
3013
  iola cloud status|doctor
3005
3014
  iola cloud use yandex-disk
3006
3015
  iola cloud ls /IOLA
3016
+ iola cloud mkdir /IOLA/Фото
3007
3017
  iola cloud find "справка" --path /IOLA
3008
3018
  iola cloud upload local.txt /IOLA/local.txt
3009
3019
  iola cloud download /IOLA/local.txt ./local.txt
@@ -3116,6 +3126,18 @@ async function cloudFind(provider, query, options = {}) {
3116
3126
  throw new Error(`Провайдер не поддерживается: ${provider}`);
3117
3127
  }
3118
3128
 
3129
+ async function cloudCreateFolder(provider, remotePath) {
3130
+ if (provider === "yandex-disk") {
3131
+ await ensureYandexDiskDir(remotePath, { allowExisting: true });
3132
+ return { provider, path: normalizeYandexDiskPath(remotePath), status: "created-or-exists" };
3133
+ }
3134
+ if (provider === "mailru-cloud") {
3135
+ await ensureMailruCloudDir(remotePath);
3136
+ return { provider, path: remotePath, status: "created-or-exists" };
3137
+ }
3138
+ throw new Error(`Провайдер не поддерживается: ${provider}`);
3139
+ }
3140
+
3119
3141
  async function cloudShare(provider, remotePath) {
3120
3142
  if (provider === "yandex-disk") return yandexDiskShare(remotePath);
3121
3143
  throw new Error("Публичные ссылки через CLI сейчас поддерживаются только для Яндекс Диска.");
@@ -7817,25 +7839,32 @@ async function setupIolaLocal(args) {
7817
7839
  }
7818
7840
 
7819
7841
  const config = await loadConfig();
7842
+ const localProfile = {
7843
+ provider: "iola",
7844
+ model,
7845
+ runtime,
7846
+ baseUrl: "http://127.0.0.1:11434",
7847
+ repo,
7848
+ ggufRepo,
7849
+ ggufFile,
7850
+ modelDir,
7851
+ };
7852
+ const shouldActivate = !options["preserve-active"];
7853
+ const previousActiveProfile = getActiveProfileName(config);
7854
+ const nextActiveProfile = shouldActivate ? profileName : previousActiveProfile;
7855
+ const nextActiveConfig = nextActiveProfile === profileName
7856
+ ? localProfile
7857
+ : (config.ai.profiles?.[nextActiveProfile] || config.ai.profiles?.[previousActiveProfile] || localProfile);
7820
7858
  await saveConfig({
7821
7859
  ai: {
7822
7860
  ...config.ai,
7823
- activeProfile: profileName,
7824
- provider: "iola",
7825
- model,
7826
- baseUrl: "http://127.0.0.1:11434",
7861
+ activeProfile: nextActiveProfile,
7862
+ provider: nextActiveConfig.provider,
7863
+ model: nextActiveConfig.model,
7864
+ baseUrl: nextActiveConfig.baseUrl || config.ai.baseUrl,
7827
7865
  profiles: {
7828
7866
  ...(config.ai.profiles || {}),
7829
- [profileName]: {
7830
- provider: "iola",
7831
- model,
7832
- runtime,
7833
- baseUrl: "http://127.0.0.1:11434",
7834
- repo,
7835
- ggufRepo,
7836
- ggufFile,
7837
- modelDir,
7838
- },
7867
+ [profileName]: localProfile,
7839
7868
  },
7840
7869
  },
7841
7870
  });
@@ -7873,6 +7902,20 @@ async function aiAsk(args, context = {}) {
7873
7902
  const historyEnabled = !options.bare && !options["no-history"] && isFeatureEnabled("sqlite-history");
7874
7903
  const sessionId = historyEnabled && isFeatureEnabled("sessions") ? ensureSessionForAsk(options, providerConfig, question) : null;
7875
7904
  const history = context.history || (sessionId ? getSessionAiHistory(sessionId) : []);
7905
+ const cloudAnswer = await buildCloudDirectAnswer(question);
7906
+ if (cloudAnswer) {
7907
+ if (historyEnabled) {
7908
+ recordAskHistory({ question, answer: cloudAnswer, providerConfig, dataContext, error: "", sessionId });
7909
+ appendSessionExchange(sessionId, question, cloudAnswer, dataContext, "");
7910
+ }
7911
+ emitEvent(options, "answer", { length: cloudAnswer.length, sessionId, direct: true, cloud: true });
7912
+ if (options.output) {
7913
+ await assertPermission("writeFiles");
7914
+ await writeFile(options.output, cloudAnswer, "utf8");
7915
+ }
7916
+ if (!options.quiet) console.log(cloudAnswer);
7917
+ return cloudAnswer;
7918
+ }
7876
7919
  const geoAnswer = await buildGeoDirectAnswer(question);
7877
7920
  if (geoAnswer) {
7878
7921
  if (historyEnabled) {
@@ -7973,6 +8016,119 @@ async function buildDirectDataAnswer(question, dataContext) {
7973
8016
  ].join("\n");
7974
8017
  }
7975
8018
 
8019
+ async function buildCloudDirectAnswer(question) {
8020
+ if (!isCloudQuestion(question)) return "";
8021
+ const normalized = String(question || "").toLocaleLowerCase("ru-RU");
8022
+ try {
8023
+ const provider = await getCloudProvider();
8024
+ if (/(созда|сдела|добав).{0,30}(папк|директор)/iu.test(normalized) || /(папк|директор).{0,30}(созда|сдела|добав)/iu.test(normalized)) {
8025
+ const folderName = extractCloudFolderName(question) || "Новая папка";
8026
+ const remotePath = normalizeCloudUserPath(folderName, provider);
8027
+ const result = await cloudCreateFolder(provider, remotePath);
8028
+ return `Папка создана или уже была на облачном диске: ${result.path}`;
8029
+ }
8030
+
8031
+ if (/(покажи|список|что.+лежит|файлы|папки)/iu.test(normalized)) {
8032
+ const remotePath = extractCloudPath(question) || cloudRootForProvider(provider);
8033
+ const rows = await cloudList(provider, normalizeCloudUserPath(remotePath, provider));
8034
+ if (rows.length === 0) return `В папке ${normalizeCloudUserPath(remotePath, provider)} нет данных.`;
8035
+ return [
8036
+ `Облачный диск ${provider}, папка ${normalizeCloudUserPath(remotePath, provider)}:`,
8037
+ ...rows.slice(0, 20).map((row, index) => `${index + 1}. ${row.type === "dir" ? "папка" : "файл"} ${row.name} — ${row.path}`),
8038
+ ].join("\n");
8039
+ }
8040
+
8041
+ if (/(найди|поиск|где лежит)/iu.test(normalized)) {
8042
+ const query = cleanupCloudQuery(question);
8043
+ const rows = await cloudFind(provider, query, { path: cloudRootForProvider(provider), limit: 10 });
8044
+ if (rows.length === 0) return `На облачном диске не нашел: ${query}`;
8045
+ return [
8046
+ `Нашел на облачном диске ${provider}:`,
8047
+ ...rows.map((row, index) => `${index + 1}. ${row.name} — ${row.path}`),
8048
+ ].join("\n");
8049
+ }
8050
+
8051
+ if (/(ссылк|поделись|опубликуй)/iu.test(normalized)) {
8052
+ const remotePath = extractCloudPath(question);
8053
+ if (!remotePath) return "Укажите путь к файлу на облачном диске, например: /IOLA/reports/report.md";
8054
+ const result = await cloudShare(provider, normalizeCloudUserPath(remotePath, provider));
8055
+ return `Публичная ссылка: ${result.publicUrl}`;
8056
+ }
8057
+
8058
+ if (/(сохрани|запиши).{0,40}(на яндекс диске|в облак|на диск)/iu.test(normalized)) {
8059
+ const text = cleanupCloudSaveText(question);
8060
+ if (!text) return "Что сохранить на облачный диск?";
8061
+ const remotePath = `${cloudRootForProvider(provider)}/notes/iola-${timestampForFile()}.txt`;
8062
+ const tempPath = path.join(CONFIG_DIR, `cloud-save-${Date.now()}.txt`);
8063
+ await mkdir(CONFIG_DIR, { recursive: true });
8064
+ await writeFile(tempPath, text, "utf8");
8065
+ try {
8066
+ await cloudUpload(provider, tempPath, remotePath, { overwrite: true });
8067
+ } finally {
8068
+ await rm(tempPath, { force: true }).catch(() => {});
8069
+ }
8070
+ return `Сохранил текст на облачный диск: ${remotePath}`;
8071
+ }
8072
+ } catch (error) {
8073
+ return `Не смог выполнить облачный запрос: ${error instanceof Error ? error.message : String(error)}`;
8074
+ }
8075
+ return "";
8076
+ }
8077
+
8078
+ function isCloudQuestion(question) {
8079
+ return /(яндекс.?диск|yandex.?disk|облак|облачн|на диск|с диска|в диск|cloud|mail\.?ru|публичн.*ссылк|поделиться.*файл)/iu.test(String(question || ""));
8080
+ }
8081
+
8082
+ function normalizeCloudUserPath(value, provider = "yandex-disk") {
8083
+ const root = cloudRootForProvider(provider);
8084
+ let text = String(value || "").trim().replace(/\\/g, "/");
8085
+ text = text.replace(/^["'«»]+|["'«»]+$/gu, "").trim();
8086
+ if (!text) return root;
8087
+ if (text.startsWith("/")) return text;
8088
+ if (normalizeGeoText(text).startsWith(normalizeGeoText(root).replace(/^\//u, ""))) return `/${text.replace(/^\/+/u, "")}`;
8089
+ return `${root.replace(/\/+$/u, "")}/${text.replace(/^\/+/u, "")}`;
8090
+ }
8091
+
8092
+ function extractCloudFolderName(question) {
8093
+ const text = String(question || "").trim();
8094
+ const match = text.match(/(?:папк[ауи]?|директор(?:ию|ия|ии)?)\s+["'«]?([^"'».,!?]+)["'»]?/iu)
8095
+ || text.match(/(?:названи(?:ем|е)|имя)\s+["'«]?([^"'».,!?]+)["'»]?/iu);
8096
+ if (!match?.[1]) return "";
8097
+ return cleanupCloudObjectName(match[1]);
8098
+ }
8099
+
8100
+ function extractCloudPath(question) {
8101
+ const text = String(question || "").trim();
8102
+ const pathMatch = text.match(/(?:^|\s)(\/IOLA\/[^\s]+|\/[^\s]+)/iu);
8103
+ if (pathMatch?.[1]) return pathMatch[1];
8104
+ const quoted = text.match(/["«]([^"»]+)["»]/u);
8105
+ if (quoted?.[1]) return quoted[1];
8106
+ const afterFolder = text.match(/(?:папк[аеуы]?|файл[ае]?)\s+([^,.!?]+)/iu);
8107
+ return afterFolder?.[1] ? cleanupCloudObjectName(afterFolder[1]) : "";
8108
+ }
8109
+
8110
+ function cleanupCloudObjectName(value) {
8111
+ return String(value || "")
8112
+ .replace(/\b(?:на|в|у меня|яндекс.?диск(?:е)?|диск(?:е)?|облак(?:е|о)?|создай|сделай|добавь|покажи)\b/giu, " ")
8113
+ .replace(/\s+/g, " ")
8114
+ .trim();
8115
+ }
8116
+
8117
+ function cleanupCloudQuery(question) {
8118
+ return String(question || "")
8119
+ .replace(/\b(?:найди|поиск|где лежит|на|в|яндекс.?диск(?:е)?|облак(?:е|о)?|диск(?:е)?|файл|документ)\b/giu, " ")
8120
+ .replace(/[?.!]+$/u, "")
8121
+ .replace(/\s+/g, " ")
8122
+ .trim();
8123
+ }
8124
+
8125
+ function cleanupCloudSaveText(question) {
8126
+ return String(question || "")
8127
+ .replace(/^.*?(?:сохрани|запиши)\s+/iu, "")
8128
+ .replace(/\s+(?:на яндекс диске|в облак[ео]|на диск).*$/iu, "")
8129
+ .trim();
8130
+ }
8131
+
7976
8132
  function detectDirectDataFields(normalizedQuestion) {
7977
8133
  const fields = [];
7978
8134
  if (/(директ|руководител|заведующ|кто возглавляет)/iu.test(normalizedQuestion)) fields.push("head");
@@ -8256,6 +8412,11 @@ async function localToolAsk(question, providerConfig, options) {
8256
8412
  if (!options.quiet) console.log(casualAnswer);
8257
8413
  return casualAnswer;
8258
8414
  }
8415
+ const cloudAnswer = await buildCloudDirectAnswer(question);
8416
+ if (cloudAnswer) {
8417
+ if (!options.quiet) console.log(cloudAnswer);
8418
+ return cloudAnswer;
8419
+ }
8259
8420
  const geoAnswer = await buildGeoDirectAnswer(question);
8260
8421
  if (geoAnswer) {
8261
8422
  if (!options.quiet) console.log(geoAnswer);
@@ -10554,7 +10715,7 @@ function parseOptions(args) {
10554
10715
 
10555
10716
  for (let index = 0; index < args.length; index += 1) {
10556
10717
  const arg = args[index];
10557
- if (arg === "--json" || arg === "--yes" || arg === "--silent" || arg === "--events" || arg === "--stream-json" || arg === "--stdio" || arg === "--system" || arg === "--headed" || arg === "--headless" || arg === "--no-history" || arg === "--summary" || arg === "--all" || arg === "--full" || arg === "--unread" || arg === "--once" || arg === "--local" || arg === "--cache" || arg === "--tools" || arg === "--files" || arg === "--plan" || arg === "--trace" || arg === "--diff" || arg === "--stage" || arg === "--fts" || arg === "--bare" || arg === "--quiet" || arg === "--optional" || arg === "--project" || arg === "--dry-run" || arg === "--no-color" || arg === "--fail-on-empty" || arg === "--debug" || arg === "--fix" || arg === "--append") {
10718
+ if (arg === "--json" || arg === "--yes" || arg === "--silent" || arg === "--events" || arg === "--stream-json" || arg === "--stdio" || arg === "--system" || arg === "--headed" || arg === "--headless" || arg === "--no-history" || arg === "--summary" || arg === "--all" || arg === "--full" || arg === "--unread" || arg === "--once" || arg === "--local" || arg === "--cache" || arg === "--tools" || arg === "--files" || arg === "--plan" || arg === "--trace" || arg === "--diff" || arg === "--stage" || arg === "--fts" || arg === "--bare" || arg === "--quiet" || arg === "--optional" || arg === "--project" || arg === "--dry-run" || arg === "--no-color" || arg === "--fail-on-empty" || arg === "--debug" || arg === "--fix" || arg === "--append" || arg === "--preserve-active") {
10558
10719
  result[arg.slice(2)] = true;
10559
10720
  } else if (arg === "--check" || arg === "--upgrade-node") {
10560
10721
  result.check = true;
@@ -10809,8 +10970,10 @@ function selectSkillsForPrompt(config, question = "", options = {}) {
10809
10970
  if (enabled.has("local-model")) selected.add("local-model");
10810
10971
  if (enabled.has("open-data") && shouldUseDataContext(question, options)) selected.add("open-data");
10811
10972
  if (enabled.has("geo") && isGeoQuestion(normalized)) selected.add("geo");
10973
+ const cloudQuestion = isCloudQuestion(normalized);
10974
+ if (enabled.has("personal-docs") && cloudQuestion) selected.add("personal-docs");
10812
10975
  if (enabled.has("reports") && /(отчет|отчёт|выгруз|csv|xlsx|качество|провер)/iu.test(normalized)) selected.add("reports");
10813
- if (enabled.has("local-files") && (options.files || /(файл|папк|readme|документ|архив)/iu.test(normalized))) selected.add("local-files");
10976
+ if (enabled.has("local-files") && !cloudQuestion && (options.files || /(файл|папк|readme|документ|архив)/iu.test(normalized))) selected.add("local-files");
10814
10977
  if (enabled.has("browser-agent") && /(браузер|сайт|страниц|url|https?:\/\/)/iu.test(normalized)) selected.add("browser-agent");
10815
10978
  return selected;
10816
10979
  }
@@ -12249,8 +12412,8 @@ async function executeRpc(method, options = {}) {
12249
12412
 
12250
12413
  async function getLatestNpmVersion(packageName) {
12251
12414
  try {
12252
- const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`, {
12253
- headers: { accept: "application/json" },
12415
+ const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest?t=${Date.now()}`, {
12416
+ headers: { accept: "application/json", "cache-control": "no-cache" },
12254
12417
  });
12255
12418
 
12256
12419
  if (!response.ok) {
@@ -12325,8 +12488,11 @@ function printDiagnostics(diagnostics, recommendation) {
12325
12488
  vram: diagnostics.gpu.vramGb ? `${diagnostics.gpu.vramGb} GB` : "-",
12326
12489
  ollama: diagnostics.ollama.installed ? diagnostics.ollama.version : "не установлен",
12327
12490
  });
12491
+ if (!recommendation) {
12492
+ return;
12493
+ }
12328
12494
  console.log("");
12329
- console.log("Рекомендация");
12495
+ console.log("Рекомендация локальной модели");
12330
12496
  printKeyValue({
12331
12497
  profile: recommendation.profile,
12332
12498
  model: recommendation.model,
@@ -12511,6 +12677,9 @@ function sanitizeConfig(config) {
12511
12677
  if (Array.isArray(next.skills?.enabled) && next.skills.enabled.includes("open-data") && !next.skills.enabled.includes("education")) {
12512
12678
  next.skills.enabled = ["education", ...next.skills.enabled];
12513
12679
  }
12680
+ if (Array.isArray(next.skills?.enabled) && next.skills.enabled.includes("open-data") && !next.skills.enabled.includes("geo")) {
12681
+ next.skills.enabled = [...next.skills.enabled, "geo"];
12682
+ }
12514
12683
  if (Array.isArray(next.skills?.enabled) && next.skills.enabled.includes("local-files") && !next.skills.enabled.includes("personal-docs")) {
12515
12684
  next.skills.enabled = [...next.skills.enabled, "personal-docs"];
12516
12685
  }
@@ -12897,6 +13066,19 @@ function printKeyValue(value) {
12897
13066
  ]);
12898
13067
  }
12899
13068
 
13069
+ function printKeyValueFull(value) {
13070
+ const rows = Object.entries(value).map(([key, raw]) => ({
13071
+ key,
13072
+ value: raw == null || raw === "" ? "-" : String(raw),
13073
+ }));
13074
+ const keyWidth = Math.max(4, ...rows.map((row) => visibleLength(row.key)));
13075
+ console.log(`${padCell("Поле", keyWidth)} Значение`);
13076
+ console.log(`${"-".repeat(keyWidth)} ${"-".repeat(8)}`);
13077
+ for (const row of rows) {
13078
+ console.log(`${padCell(row.key, keyWidth)} ${row.value}`);
13079
+ }
13080
+ }
13081
+
12900
13082
  function printTable(rows, columns) {
12901
13083
  if (rows.length === 0) {
12902
13084
  console.log("Нет данных.");
@@ -50,6 +50,7 @@ iola cloud status
50
50
  iola cloud doctor
51
51
  iola cloud use yandex-disk
52
52
  iola cloud ls /IOLA
53
+ iola cloud mkdir /IOLA/Фото
53
54
  iola cloud find "справка" --path /IOLA
54
55
  iola cloud upload report.md /IOLA/reports/report.md
55
56
  iola cloud download /IOLA/reports/report.md ./report.md
@@ -18,6 +18,7 @@ iola cloud status
18
18
  iola cloud doctor
19
19
  iola cloud use yandex-disk
20
20
  iola cloud ls /IOLA
21
+ iola cloud mkdir /IOLA/Фото
21
22
  iola cloud find "справка" --path /IOLA
22
23
  iola cloud upload report.md /IOLA/reports/report.md
23
24
  iola cloud download /IOLA/reports/report.md ./report.md
@@ -45,7 +46,7 @@ iola cloud backup
45
46
  5. Нажмите `Перейти к созданию`.
46
47
  6. На шаге `Создание приложения` заполните:
47
48
  - `Название вашего сервиса`: например `iola-cli`;
48
- - `Иконка сервиса`: загрузите любую простую PNG-иконку до 1 МБ;
49
+ - `Иконка сервиса`: загрузите PNG-иконку до 1 МБ. Готовая иконка iola-cli доступна по ссылке: `https://raw.githubusercontent.com/adm-iola/iola-cli/main/docs/assets/iola-oauth-icon.png`;
49
50
  - `Почта для связи`: оставьте свою почту или укажите актуальную.
50
51
  7. Нажмите `Продолжить`.
51
52
  8. На шаге `Платформы приложений` выберите `Веб-сервисы`.
@@ -101,6 +102,16 @@ iola cloud doctor
101
102
 
102
103
  Обычно такой токен выдается на длительный срок. Если доступ перестал работать, получите новый токен через ту же ссылку авторизации и повторите `iola cloud setup yandex-disk`.
103
104
 
105
+ После подключения можно работать с облаком обычными фразами в CLI:
106
+
107
+ ```text
108
+ создай у меня на яндекс диске папку фото
109
+ покажи что лежит на яндекс диске
110
+ найди на яндекс диске справку
111
+ ```
112
+
113
+ Такие запросы обрабатывает skill `personal-docs`. Они не требуют включать локальный режим `iola files mode`, потому что это не работа с файлами на ПК.
114
+
104
115
  ## Облако Mail.ru
105
116
 
106
117
  Облако Mail.ru подключается через WebDAV.
@@ -114,6 +114,15 @@ iola geo services "Йошкар-Ола, улица Петрова, 15"
114
114
 
115
115
  Сохранить ответ CLI, отчет, карточку учреждения или список ближайших объектов в `/IOLA`.
116
116
 
117
+ ### cloud-create-folder
118
+
119
+ Создать папку на подключенном облачном диске.
120
+
121
+ Пример:
122
+
123
+ - `создай у меня на яндекс диске папку фото`;
124
+ - `создай папку документы в облаке`.
125
+
117
126
  ### cloud-share-link
118
127
 
119
128
  Создать публичную ссылку на файл. В первом контуре поддерживается для Яндекс Диска.