kingkont 0.7.40 → 0.7.42

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/bin/kingkont.js CHANGED
@@ -106,7 +106,12 @@ if (cmd && NODE_COMMANDS[cmd]) {
106
106
  process.env.PORT = String(port);
107
107
  const targetPath = args[1] && !args[1].startsWith('--') ? path.resolve(args[1]) : null;
108
108
  const { start } = require(path.join(root, 'server.js'));
109
- start(port).then(actualPort => {
109
+ // Live-чтение settings.json на каждый запрос — так же как в Electron-режиме
110
+ // (main.js). Без этого use*-флаги и chatium.token не доходят до server.js,
111
+ // и провайдеры (chatium / openrouter / kie / elevenlabs) висят выключенными,
112
+ // даже если юзер залогинен через UI приложения.
113
+ const startOpts = { getSettings: () => settingsLib.loadSettings() };
114
+ start(port, startOpts).then(actualPort => {
110
115
  const url = `http://localhost:${actualPort}/`;
111
116
  console.log(`\n▶ KingKont готов: ${url}`);
112
117
  if (targetPath) console.log(` подразумеваемый проект: ${targetPath}`);
package/main.js CHANGED
@@ -154,19 +154,25 @@ ipcMain.handle('claudeMd:template', () => {
154
154
  catch { return ''; }
155
155
  });
156
156
 
157
- // Установить kingkont skill в ~/.claude/skills/kingkont/SKILL.md.
157
+ // Установить kingkont skill в ~/.claude/skills/kingkont/SKILL.md
158
+ // (на Windows: %USERPROFILE%\.claude\skills\kingkont\SKILL.md).
158
159
  // То же самое что `kingkont install-skill` в CLI — для юзеров которые
159
160
  // никогда не запускали bin/kingkont напрямую (только Electron-приложение).
161
+ //
162
+ // Read+write, не copyFileSync — source может лежать внутри app.asar в
163
+ // bundled-сборке (.dmg/.exe), там copy с asar→disk периодически глючит.
160
164
  ipcMain.handle('claudeMd:installSkill', () => {
161
165
  try {
162
166
  const src = path.join(__dirname, 'skill', 'SKILL.md');
163
- if (!fs.existsSync(src)) {
164
- return { ok: false, error: 'Шаблон skill/SKILL.md не найден в пакете' };
167
+ let buf;
168
+ try { buf = fs.readFileSync(src); }
169
+ catch (e) {
170
+ return { ok: false, error: 'Шаблон skill/SKILL.md не найден в пакете: ' + e.message };
165
171
  }
166
172
  const dest = path.join(os.homedir(), '.claude', 'skills', 'kingkont');
167
173
  fs.mkdirSync(dest, { recursive: true });
168
174
  const destFile = path.join(dest, 'SKILL.md');
169
- fs.copyFileSync(src, destFile);
175
+ fs.writeFileSync(destFile, buf);
170
176
  return { ok: true, path: destFile };
171
177
  } catch (e) {
172
178
  return { ok: false, error: e?.message || String(e) };
@@ -758,19 +764,41 @@ app.whenReady().then(async () => {
758
764
 
759
765
  // Фиксированный порт нужен потому что Chromium хранит IndexedDB и FSAH-handle
760
766
  // per-origin (http://localhost:PORT). Со случайным портом каждый запуск
761
- // получал бы новую пустую IDB. Если порт занят fallback на случайный, но
762
- // тогда recents-handle потеряется (это меньшая беда, чем падение запуска).
763
- // server.js на каждый запрос читает live-настройки через getSettings — это
764
- // позволяет переключать use*-флаги через UI без перезапуска. settings.json
765
- // на диске единственный источник правды; readSettings() делает свежий read.
767
+ // получал бы новую пустую IDB и юзер должен заново указывать папки в recents.
768
+ //
769
+ // RETRY на 17893: при relaunch'е старый instance отпускает singleton-lock
770
+ // быстрее, чем macOS освобождает TCP-сокет (TIME_WAIT). Без retry сразу
771
+ // падаем на random и теряем handle. 8×500мс = 4 сек обычно хватает.
772
+ // server.js на каждый запрос читает live-настройки через getSettings —
773
+ // settings.json на диске единственный источник правды.
766
774
  const startOpts = { getSettings: () => readSettings() };
767
- try {
768
- port = await start(17893, startOpts);
769
- } catch {
770
- console.warn('port 17893 busy, falling back to random');
775
+ const PRIMARY_PORT = 17893;
776
+ const RETRIES = 8;
777
+ let lastErr;
778
+ for (let i = 0; i < RETRIES; i++) {
779
+ try { port = await start(PRIMARY_PORT, startOpts); break; }
780
+ catch (e) {
781
+ lastErr = e;
782
+ if (e.code !== 'EADDRINUSE') break; // не race — нет смысла ретраить
783
+ console.warn(`port ${PRIMARY_PORT} busy (attempt ${i + 1}/${RETRIES}), waiting…`);
784
+ await new Promise(r => setTimeout(r, 500));
785
+ }
786
+ }
787
+ if (!port) {
788
+ // Сначала попробуем фиксированные fallback'и (17894/17895/...) —
789
+ // это сохранит хотя бы часть recents (origin будет стабильным от
790
+ // запуска к запуску при условии что 17893 хронически занят).
791
+ for (const p of [17894, 17895, 17896, 17897, 17898, 17899]) {
792
+ try { port = await start(p, startOpts); console.warn(`fell back to port ${p}`); break; }
793
+ catch { /* try next */ }
794
+ }
795
+ }
796
+ if (!port) {
797
+ // Последний резерв — случайный порт (потеряем handle).
798
+ console.warn('all stable ports busy, falling back to random (recents-handle will be lost)');
771
799
  try { port = await start(0, startOpts); }
772
800
  catch (e) {
773
- console.error('Server failed to start:', e);
801
+ console.error('Server failed to start:', e || lastErr);
774
802
  app.quit();
775
803
  return;
776
804
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kingkont",
3
- "version": "0.7.40",
3
+ "version": "0.7.42",
4
4
  "description": "KingKont \u00b7 Chatium \u2014 \u043d\u043e\u0434-\u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0441\u0446\u0435\u043d \u0441 AI-\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0435\u0439 (\u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438/\u0432\u0438\u0434\u0435\u043e/\u0433\u043e\u043b\u043e\u0441/SFX/\u043c\u0443\u0437\u044b\u043a\u0430/\u0442\u0435\u043a\u0441\u0442)",
5
5
  "main": "main.js",
6
6
  "bin": {
@@ -63,6 +63,7 @@
63
63
  "assets/**/*",
64
64
  "lib/**/*",
65
65
  "renderer/**/*",
66
+ "skill/**/*",
66
67
  "package.json",
67
68
  "updates.html"
68
69
  ],
package/settings.html CHANGED
@@ -175,7 +175,9 @@
175
175
  Скилл <code>kingkont</code> для Claude Code
176
176
  </div>
177
177
  <div class="hint" style="margin-top:0; margin-bottom:12px;">
178
- Положит <code>~/.claude/skills/kingkont/SKILL.md</code> Claude
178
+ Положит <code>SKILL.md</code> в папку настроек Claude Code в твоём
179
+ домашнем каталоге (macOS/Linux: <code>~/.claude/skills/kingkont/</code>;
180
+ Windows: <code>%USERPROFILE%\.claude\skills\kingkont\</code>). Claude
179
181
  сможет управлять проектами KingKont (создавать ноды, запускать
180
182
  генерации) без открытия приложения. После установки нужен
181
183
  <code>/reload</code> в Claude Code.
@@ -184,6 +186,7 @@
184
186
  <button id="installSkill" class="secondary">Установить skill для Claude</button>
185
187
  <span class="saved" id="skillSaved" style="margin-left:auto;">Установлено ✓</span>
186
188
  </div>
189
+ <div class="hint" id="skillPath" style="display:none; margin-top:8px; word-break:break-all; font-family:ui-monospace,monospace; color:var(--ok);"></div>
187
190
  <div class="error" id="skillError" style="display:none;"></div>
188
191
  </div>
189
192
 
@@ -328,12 +331,15 @@ function escapeHtml(s) {
328
331
  }
329
332
 
330
333
  // Кнопка «Установить skill для Claude» — копирует skill/SKILL.md в
331
- // ~/.claude/skills/kingkont/ через main-process IPC.
334
+ // ~/.claude/skills/kingkont/ (или %USERPROFILE%\.claude\skills\kingkont\
335
+ // на Windows) через main-process IPC. После успеха показывает реальный путь.
332
336
  $('installSkill').addEventListener('click', async () => {
333
337
  const btn = $('installSkill');
334
338
  const ok = $('skillSaved');
335
339
  const err = $('skillError');
340
+ const pathEl = $('skillPath');
336
341
  err.style.display = 'none';
342
+ pathEl.style.display = 'none';
337
343
  ok.classList.remove('show');
338
344
  btn.disabled = true;
339
345
  btn.textContent = 'Устанавливаю…';
@@ -343,6 +349,10 @@ $('installSkill').addEventListener('click', async () => {
343
349
  btn.textContent = 'Переустановить skill для Claude';
344
350
  ok.classList.add('show');
345
351
  setTimeout(() => ok.classList.remove('show'), 2500);
352
+ if (res.path) {
353
+ pathEl.style.display = '';
354
+ pathEl.textContent = '✓ ' + res.path;
355
+ }
346
356
  } else {
347
357
  throw new Error(res?.error || 'Не удалось установить');
348
358
  }