@songsid/agend 2.1.4-beta.8 → 2.1.4

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.
Files changed (173) hide show
  1. package/README.md +1 -1
  2. package/README.zh-TW.md +1 -1
  3. package/dist/agent-cli-instructions.md +2 -2
  4. package/dist/agent-endpoint.js +1 -1
  5. package/dist/agent-endpoint.js.map +1 -1
  6. package/dist/backend/antigravity.d.ts +12 -8
  7. package/dist/backend/antigravity.js +189 -42
  8. package/dist/backend/antigravity.js.map +1 -1
  9. package/dist/backend/claude-code.d.ts +87 -23
  10. package/dist/backend/claude-code.js +361 -46
  11. package/dist/backend/claude-code.js.map +1 -1
  12. package/dist/backend/codex.d.ts +18 -0
  13. package/dist/backend/codex.js +139 -5
  14. package/dist/backend/codex.js.map +1 -1
  15. package/dist/backend/gemini-cli.js +2 -2
  16. package/dist/backend/gemini-cli.js.map +1 -1
  17. package/dist/backend/grok.js +2 -2
  18. package/dist/backend/grok.js.map +1 -1
  19. package/dist/backend/kiro.d.ts +52 -2
  20. package/dist/backend/kiro.js +267 -12
  21. package/dist/backend/kiro.js.map +1 -1
  22. package/dist/backend/opencode.js +17 -4
  23. package/dist/backend/opencode.js.map +1 -1
  24. package/dist/backend/types.d.ts +122 -3
  25. package/dist/backend/types.js +5 -1
  26. package/dist/backend/types.js.map +1 -1
  27. package/dist/backend-outage.d.ts +61 -0
  28. package/dist/backend-outage.js +71 -0
  29. package/dist/backend-outage.js.map +1 -0
  30. package/dist/channel/adapters/discord.d.ts +44 -2
  31. package/dist/channel/adapters/discord.js +529 -89
  32. package/dist/channel/adapters/discord.js.map +1 -1
  33. package/dist/channel/adapters/telegram.d.ts +30 -0
  34. package/dist/channel/adapters/telegram.js +221 -19
  35. package/dist/channel/adapters/telegram.js.map +1 -1
  36. package/dist/channel/agy-mcp-launcher.d.ts +2 -0
  37. package/dist/channel/agy-mcp-launcher.js +28 -0
  38. package/dist/channel/agy-mcp-launcher.js.map +1 -0
  39. package/dist/channel/ipc-bridge.d.ts +2 -1
  40. package/dist/channel/ipc-bridge.js +33 -4
  41. package/dist/channel/ipc-bridge.js.map +1 -1
  42. package/dist/channel/markdown-chunk.d.ts +42 -0
  43. package/dist/channel/markdown-chunk.js +205 -0
  44. package/dist/channel/markdown-chunk.js.map +1 -0
  45. package/dist/channel/mcp-server.js +34 -5
  46. package/dist/channel/mcp-server.js.map +1 -1
  47. package/dist/channel/mcp-tools.js +5 -3
  48. package/dist/channel/mcp-tools.js.map +1 -1
  49. package/dist/channel/message-queue.js +64 -8
  50. package/dist/channel/message-queue.js.map +1 -1
  51. package/dist/channel/types.d.ts +31 -1
  52. package/dist/classic-channel-manager.d.ts +65 -0
  53. package/dist/classic-channel-manager.js +172 -3
  54. package/dist/classic-channel-manager.js.map +1 -1
  55. package/dist/cli.js +124 -83
  56. package/dist/cli.js.map +1 -1
  57. package/dist/config-validator.js +16 -0
  58. package/dist/config-validator.js.map +1 -1
  59. package/dist/config.d.ts +4 -0
  60. package/dist/config.js +12 -1
  61. package/dist/config.js.map +1 -1
  62. package/dist/cross-instance-envelope.d.ts +6 -0
  63. package/dist/cross-instance-envelope.js +30 -0
  64. package/dist/cross-instance-envelope.js.map +1 -0
  65. package/dist/daemon.d.ts +538 -11
  66. package/dist/daemon.js +2272 -288
  67. package/dist/daemon.js.map +1 -1
  68. package/dist/doctor.d.ts +41 -0
  69. package/dist/doctor.js +267 -0
  70. package/dist/doctor.js.map +1 -0
  71. package/dist/fleet-context.d.ts +63 -0
  72. package/dist/fleet-context.js.map +1 -1
  73. package/dist/fleet-manager.d.ts +424 -11
  74. package/dist/fleet-manager.js +2753 -444
  75. package/dist/fleet-manager.js.map +1 -1
  76. package/dist/fleet-yaml-slim.d.ts +10 -0
  77. package/dist/fleet-yaml-slim.js +59 -0
  78. package/dist/fleet-yaml-slim.js.map +1 -0
  79. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +1 -0
  80. package/dist/general-knowledge/skills/model-discovery/SKILL.md +63 -16
  81. package/dist/general-knowledge/skills/session-management/SKILL.md +171 -8
  82. package/dist/instance-lifecycle.d.ts +114 -1
  83. package/dist/instance-lifecycle.js +404 -14
  84. package/dist/instance-lifecycle.js.map +1 -1
  85. package/dist/instructions.js +42 -4
  86. package/dist/instructions.js.map +1 -1
  87. package/dist/locale.js +1024 -310
  88. package/dist/locale.js.map +1 -1
  89. package/dist/login-flows.d.ts +91 -0
  90. package/dist/login-flows.js +170 -0
  91. package/dist/login-flows.js.map +1 -0
  92. package/dist/login-manager.d.ts +63 -0
  93. package/dist/login-manager.js +134 -0
  94. package/dist/login-manager.js.map +1 -0
  95. package/dist/network-family.d.ts +18 -0
  96. package/dist/network-family.js +20 -0
  97. package/dist/network-family.js.map +1 -0
  98. package/dist/outbound-handlers.d.ts +16 -1
  99. package/dist/outbound-handlers.js +174 -40
  100. package/dist/outbound-handlers.js.map +1 -1
  101. package/dist/outbound-schemas.d.ts +10 -3
  102. package/dist/outbound-schemas.js +10 -0
  103. package/dist/outbound-schemas.js.map +1 -1
  104. package/dist/pane-input-residue.d.ts +52 -0
  105. package/dist/pane-input-residue.js +107 -0
  106. package/dist/pane-input-residue.js.map +1 -0
  107. package/dist/process-memory.d.ts +6 -0
  108. package/dist/process-memory.js +45 -0
  109. package/dist/process-memory.js.map +1 -1
  110. package/dist/reply-dedup.d.ts +7 -8
  111. package/dist/reply-dedup.js +0 -0
  112. package/dist/reply-dedup.js.map +1 -1
  113. package/dist/restart-progress.d.ts +2 -0
  114. package/dist/restart-progress.js +3 -0
  115. package/dist/restart-progress.js.map +1 -1
  116. package/dist/scheduler/db.d.ts +12 -0
  117. package/dist/scheduler/db.js +59 -0
  118. package/dist/scheduler/db.js.map +1 -1
  119. package/dist/service-installer.d.ts +11 -0
  120. package/dist/service-installer.js +84 -18
  121. package/dist/service-installer.js.map +1 -1
  122. package/dist/settings-api.js +1 -1
  123. package/dist/settings-api.js.map +1 -1
  124. package/dist/setup-wizard.js +2 -2
  125. package/dist/setup-wizard.js.map +1 -1
  126. package/dist/spawn-gate.d.ts +30 -0
  127. package/dist/spawn-gate.js +79 -0
  128. package/dist/spawn-gate.js.map +1 -0
  129. package/dist/steer-capability.d.ts +1 -0
  130. package/dist/steer-capability.js +6 -0
  131. package/dist/steer-capability.js.map +1 -0
  132. package/dist/storm-window.d.ts +83 -0
  133. package/dist/storm-window.js +251 -0
  134. package/dist/storm-window.js.map +1 -0
  135. package/dist/tips.d.ts +44 -0
  136. package/dist/tips.js +355 -0
  137. package/dist/tips.js.map +1 -0
  138. package/dist/tmux-manager.d.ts +16 -0
  139. package/dist/tmux-manager.js +110 -25
  140. package/dist/tmux-manager.js.map +1 -1
  141. package/dist/tool-progress.d.ts +40 -0
  142. package/dist/tool-progress.js +289 -0
  143. package/dist/tool-progress.js.map +1 -0
  144. package/dist/topic-commands.d.ts +45 -6
  145. package/dist/topic-commands.js +394 -66
  146. package/dist/topic-commands.js.map +1 -1
  147. package/dist/transcript-monitor.d.ts +15 -2
  148. package/dist/transcript-monitor.js +63 -17
  149. package/dist/transcript-monitor.js.map +1 -1
  150. package/dist/transcript-sources.d.ts +131 -0
  151. package/dist/transcript-sources.js +580 -0
  152. package/dist/transcript-sources.js.map +1 -0
  153. package/dist/types.d.ts +11 -0
  154. package/dist/ui/dashboard.html +55 -32
  155. package/dist/ui/settings.html +200 -60
  156. package/dist/ui/view.html +147 -30
  157. package/dist/usage/format-rich.d.ts +1 -1
  158. package/dist/usage/format-rich.js +28 -24
  159. package/dist/usage/format-rich.js.map +1 -1
  160. package/dist/usage/i18n-keys.d.ts +7 -0
  161. package/dist/usage/i18n-keys.js +34 -0
  162. package/dist/usage/i18n-keys.js.map +1 -0
  163. package/dist/usage/i18n.d.ts +5 -0
  164. package/dist/usage/i18n.js +27 -0
  165. package/dist/usage/i18n.js.map +1 -0
  166. package/dist/usage/providers.d.ts +21 -0
  167. package/dist/usage/providers.js +153 -75
  168. package/dist/usage/providers.js.map +1 -1
  169. package/dist/usage/usage-api.d.ts +11 -3
  170. package/dist/usage/usage-api.js +61 -24
  171. package/dist/usage/usage-api.js.map +1 -1
  172. package/dist/workflow-templates/default.md +2 -1
  173. package/package.json +2 -2
@@ -276,6 +276,29 @@
276
276
 
277
277
  <script>
278
278
  const T = new URLSearchParams(location.search).get("token") || "";
279
+ const UI_LANG = localStorage.getItem("agend_lang") || (navigator.language?.toLowerCase().startsWith("zh") ? "zh-TW" : "en");
280
+ const UI_TEXT = {
281
+ en: {
282
+ disconnected: "disconnected", selectTarget: "Select Fleet or an instance to begin", deleteConfirm: 'Type "delete {0}" to confirm:',
283
+ instanceDeleted: "Instance deleted", noMessages: "No messages yet", sendFirst: "Send a message to start the conversation",
284
+ loadFailed: "Failed to load", configLoadFailed: "Failed to load config", topicRequired: "Topic Name is required when Directory is empty",
285
+ titleRequired: "Title required", scheduleFieldsRequired: "Cron, message, and target required", teamFieldsRequired: "Name and at least one member required",
286
+ deleteSchedule: "Delete this schedule?", deleteTeam: 'Delete team "{0}"?', creating: "Creating...", create: "Create",
287
+ instanceCreated: "Instance created", taskCreated: "Task created", scheduleCreated: "Schedule created", scheduleDeleted: "Schedule deleted",
288
+ teamCreated: "Team created", teamDeleted: "Team deleted", configSaved: "Config saved", configRestart: " (restart Fleet to apply channel changes)",
289
+ },
290
+ "zh-TW": {
291
+ disconnected: "已斷線", selectTarget: "請選擇 Fleet 或 instance", deleteConfirm: '請輸入「delete {0}」以確認:',
292
+ instanceDeleted: "Instance 已刪除", noMessages: "尚無訊息", sendFirst: "傳送訊息以開始對話",
293
+ loadFailed: "載入失敗", configLoadFailed: "載入設定失敗", topicRequired: "Directory 留空時必須填寫 Topic Name",
294
+ titleRequired: "必須填寫標題", scheduleFieldsRequired: "必須填寫 Cron、訊息與目標 instance", teamFieldsRequired: "必須填寫名稱並選擇至少一名成員",
295
+ deleteSchedule: "確定刪除此排程嗎?", deleteTeam: '確定刪除 team「{0}」嗎?', creating: "建立中…", create: "建立",
296
+ instanceCreated: "Instance 已建立", taskCreated: "Task 已建立", scheduleCreated: "排程已建立", scheduleDeleted: "排程已刪除",
297
+ teamCreated: "Team 已建立", teamDeleted: "Team 已刪除", configSaved: "設定已儲存", configRestart: "(請重新啟動 Fleet 以套用頻道變更)",
298
+ },
299
+ };
300
+ const tr = key => UI_TEXT[UI_LANG]?.[key] || UI_TEXT.en[key] || key;
301
+ const trf = (key, ...values) => values.reduce((text, value, index) => text.replaceAll(`{${index}}`, String(value)), tr(key));
279
302
  let cur = null, curTab = "chat", mode = "none", instances = [];
280
303
  // mode: "none" | "fleet" | "instance"
281
304
  const msgs = {};
@@ -307,7 +330,7 @@ sse.addEventListener("message", e => {
307
330
  if (msgs[m.instance].length > 500) msgs[m.instance].shift();
308
331
  if (m.instance === cur && curTab === "chat") renderMsgs();
309
332
  });
310
- sse.onerror = () => { document.getElementById("uptime").textContent = "disconnected"; };
333
+ sse.onerror = () => { document.getElementById("uptime").textContent = tr("disconnected"); };
311
334
 
312
335
  function renderList() {
313
336
  document.getElementById("fleetEntry").className = `fleet-entry${mode==="fleet"?" active":""}`;
@@ -327,7 +350,7 @@ function renderMain() {
327
350
  const main = document.getElementById("mainArea");
328
351
  if (mode === "fleet") return renderFleetMain(main);
329
352
  if (mode === "instance" && cur) return renderInstanceMain(main);
330
- main.innerHTML = '<div class="no-sel"><svg width="56" height="56" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg><span>Select Fleet or an instance to begin</span></div>';
353
+ main.innerHTML = `<div class="no-sel"><svg width="56" height="56" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg><span>${tr("selectTarget")}</span></div>`;
331
354
  }
332
355
 
333
356
  function renderFleetMain(main) {
@@ -374,12 +397,12 @@ function renderActions(i) {
374
397
  async function doAction(a) {
375
398
  if (!cur) return;
376
399
  if (a === "delete") {
377
- const c = prompt(`Type "delete ${cur}" to confirm:`);
400
+ const c = prompt(trf("deleteConfirm", cur));
378
401
  if (c !== `delete ${cur}`) return;
379
402
  const r = await api("POST", `/ui/instances/${encodeURIComponent(cur)}/delete`, { confirm: c });
380
403
  if (r.error) { toast(r.error, false); return; }
381
- toast(`${cur} deleted`); cur = null;
382
- document.getElementById("mainArea").innerHTML = '<div class="no-sel"><span>Instance deleted</span></div>';
404
+ toast(`${cur}: ${tr("instanceDeleted")}`); cur = null;
405
+ document.getElementById("mainArea").innerHTML = `<div class="no-sel"><span>${tr("instanceDeleted")}</span></div>`;
383
406
  return;
384
407
  }
385
408
  const ep = `/ui/${a}/${encodeURIComponent(cur)}`;
@@ -391,7 +414,7 @@ async function doAction(a) {
391
414
  function renderMsgs() {
392
415
  const el = document.getElementById("messages"); if (!el) return;
393
416
  const m = msgs[cur] || [];
394
- if (!m.length) { el.innerHTML = '<div class="msg-empty"><svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>No messages yet<br><span style="font-size:12px;color:var(--text-muted)">Send a message to start the conversation</span></div>'; return; }
417
+ if (!m.length) { el.innerHTML = `<div class="msg-empty"><svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>${tr("noMessages")}<br><span style="font-size:12px;color:var(--text-muted)">${tr("sendFirst")}</span></div>`; return; }
395
418
  el.innerHTML = m.map(x => {
396
419
  const u = x.sender==="web-user"||(!x.sender.startsWith("agend")&&x.sender!==cur&&x.sender!=="general");
397
420
  const t = x.ts ? new Date(x.ts).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}) : "";
@@ -429,7 +452,7 @@ async function loadDetail() {
429
452
  h += `</div>`;
430
453
  }
431
454
  el.innerHTML = h;
432
- } catch { el.innerHTML = `<div style="color:var(--error);padding:24px">Failed to load</div>`; }
455
+ } catch { el.innerHTML = `<div style="color:var(--error);padding:24px">${tr("loadFailed")}</div>`; }
433
456
  }
434
457
 
435
458
  async function loadTasks() {
@@ -451,7 +474,7 @@ async function loadTasks() {
451
474
  </div></div>`;
452
475
  }
453
476
  el.innerHTML = h;
454
- } catch { el.innerHTML = `<div style="color:var(--error);padding:24px">Failed to load</div>`; }
477
+ } catch { el.innerHTML = `<div style="color:var(--error);padding:24px">${tr("loadFailed")}</div>`; }
455
478
  }
456
479
  async function taskAct(id, action) {
457
480
  const r = await api("POST", `/ui/tasks/${id}`, { action });
@@ -490,8 +513,8 @@ async function showCreateInstance() {
490
513
  ov.querySelector("#ci-go").addEventListener("click", async () => {
491
514
  const dir = document.getElementById("ci-dir").value.trim();
492
515
  const topic = document.getElementById("ci-topic").value.trim();
493
- if (!dir && !topic) { toast("Topic Name is required when Directory is empty", false); return; }
494
- const btn = ov.querySelector("#ci-go"); btn.disabled = true; btn.textContent = "Creating...";
516
+ if (!dir && !topic) { toast(tr("topicRequired"), false); return; }
517
+ const btn = ov.querySelector("#ci-go"); btn.disabled = true; btn.textContent = tr("creating");
495
518
  const body = {};
496
519
  if (dir) body.directory = dir;
497
520
  const v = id => document.getElementById(id).value.trim();
@@ -503,8 +526,8 @@ async function showCreateInstance() {
503
526
  const tags = v("ci-tags"); if (tags) body.tags = tags.split(",").map(t => t.trim()).filter(Boolean);
504
527
  if (v("ci-prompt")) body.systemPrompt = v("ci-prompt");
505
528
  const r = await api("POST", "/ui/instances", body);
506
- if (r.error) { toast(r.error, false); btn.disabled = false; btn.textContent = "Create"; }
507
- else { toast("Instance created"); ov.remove(); }
529
+ if (r.error) { toast(r.error, false); btn.disabled = false; btn.textContent = tr("create"); }
530
+ else { toast(tr("instanceCreated")); ov.remove(); }
508
531
  });
509
532
  }
510
533
 
@@ -522,14 +545,14 @@ function showCreateTask() {
522
545
  ov.addEventListener("click", e => { if (e.target === ov) ov.remove(); });
523
546
  ov.querySelector("#ct-go").addEventListener("click", async () => {
524
547
  const title = document.getElementById("ct-title").value.trim();
525
- if (!title) { toast("Title required", false); return; }
526
- const btn = ov.querySelector("#ct-go"); btn.disabled = true; btn.textContent = "Creating...";
548
+ if (!title) { toast(tr("titleRequired"), false); return; }
549
+ const btn = ov.querySelector("#ct-go"); btn.disabled = true; btn.textContent = tr("creating");
527
550
  const body = { title, priority: document.getElementById("ct-pri").value };
528
551
  const desc = document.getElementById("ct-desc").value.trim(); if (desc) body.description = desc;
529
552
  const asgn = document.getElementById("ct-assign").value; if (asgn) body.assignee = asgn;
530
553
  const r = await api("POST", "/ui/tasks", body);
531
- if (r.error) { toast(r.error, false); btn.disabled = false; btn.textContent = "Create"; }
532
- else { toast("Task created"); ov.remove(); loadTasks(); }
554
+ if (r.error) { toast(r.error, false); btn.disabled = false; btn.textContent = tr("create"); }
555
+ else { toast(tr("taskCreated")); ov.remove(); loadTasks(); }
533
556
  });
534
557
  }
535
558
 
@@ -553,12 +576,12 @@ async function loadSchedules() {
553
576
  </div></div>`;
554
577
  }
555
578
  el.innerHTML = h;
556
- } catch { el.innerHTML = `<div style="color:var(--error);padding:24px">Failed to load</div>`; }
579
+ } catch { el.innerHTML = `<div style="color:var(--error);padding:24px">${tr("loadFailed")}</div>`; }
557
580
  }
558
581
  async function delSchedule(id) {
559
- if (!confirm("Delete this schedule?")) return;
582
+ if (!confirm(tr("deleteSchedule"))) return;
560
583
  const r = await api("DELETE", `/ui/schedules/${id}`);
561
- if (r.error) toast(r.error, false); else { toast("Schedule deleted"); loadSchedules(); }
584
+ if (r.error) toast(r.error, false); else { toast(tr("scheduleDeleted")); loadSchedules(); }
562
585
  }
563
586
  function showCreateSchedule() {
564
587
  const ov = document.createElement("div"); ov.className = "form-overlay";
@@ -576,13 +599,13 @@ function showCreateSchedule() {
576
599
  const cron = document.getElementById("cs-cron").value.trim();
577
600
  const msg = document.getElementById("cs-msg").value.trim();
578
601
  const target = document.getElementById("cs-target").value;
579
- if (!cron || !msg || !target) { toast("Cron, message, and target required", false); return; }
580
- const btn = ov.querySelector("#cs-go"); btn.disabled = true; btn.textContent = "Creating...";
602
+ if (!cron || !msg || !target) { toast(tr("scheduleFieldsRequired"), false); return; }
603
+ const btn = ov.querySelector("#cs-go"); btn.disabled = true; btn.textContent = tr("creating");
581
604
  const body = { cron, message: msg, target, source: "web-user", reply_chat_id: "", reply_thread_id: "" };
582
605
  const label = document.getElementById("cs-label").value.trim(); if (label) body.label = label;
583
606
  const r = await api("POST", "/ui/schedules", body);
584
- if (r.error) { toast(r.error, false); btn.disabled = false; btn.textContent = "Create"; }
585
- else { toast("Schedule created"); ov.remove(); loadSchedules(); }
607
+ if (r.error) { toast(r.error, false); btn.disabled = false; btn.textContent = tr("create"); }
608
+ else { toast(tr("scheduleCreated")); ov.remove(); loadSchedules(); }
586
609
  });
587
610
  }
588
611
 
@@ -605,12 +628,12 @@ async function loadTeams() {
605
628
  </div></div>`;
606
629
  }
607
630
  el.innerHTML = h;
608
- } catch { el.innerHTML = `<div style="color:var(--error);padding:24px">Failed to load</div>`; }
631
+ } catch { el.innerHTML = `<div style="color:var(--error);padding:24px">${tr("loadFailed")}</div>`; }
609
632
  }
610
633
  async function delTeam(name) {
611
- if (!confirm(`Delete team "${name}"?`)) return;
634
+ if (!confirm(trf("deleteTeam", name))) return;
612
635
  const r = await api("DELETE", `/ui/teams/${encodeURIComponent(name)}`);
613
- if (r.error) toast(r.error, false); else { toast("Team deleted"); loadTeams(); }
636
+ if (r.error) toast(r.error, false); else { toast(tr("teamDeleted")); loadTeams(); }
614
637
  }
615
638
  function showCreateTeam() {
616
639
  const ov = document.createElement("div"); ov.className = "form-overlay";
@@ -626,13 +649,13 @@ function showCreateTeam() {
626
649
  ov.querySelector("#ct2-go").addEventListener("click", async () => {
627
650
  const name = document.getElementById("ct2-name").value.trim();
628
651
  const members = [...document.querySelectorAll("#ct2-members input:checked")].map(c => c.value);
629
- if (!name || !members.length) { toast("Name and at least one member required", false); return; }
630
- const btn = ov.querySelector("#ct2-go"); btn.disabled = true; btn.textContent = "Creating...";
652
+ if (!name || !members.length) { toast(tr("teamFieldsRequired"), false); return; }
653
+ const btn = ov.querySelector("#ct2-go"); btn.disabled = true; btn.textContent = tr("creating");
631
654
  const body = { name, members };
632
655
  const desc = document.getElementById("ct2-desc").value.trim(); if (desc) body.description = desc;
633
656
  const r = await api("POST", "/ui/teams", body);
634
- if (r.error) { toast(r.error, false); btn.disabled = false; btn.textContent = "Create"; }
635
- else { toast("Team created"); ov.remove(); loadTeams(); }
657
+ if (r.error) { toast(r.error, false); btn.disabled = false; btn.textContent = tr("create"); }
658
+ else { toast(tr("teamCreated")); ov.remove(); loadTeams(); }
636
659
  });
637
660
  }
638
661
 
@@ -701,7 +724,7 @@ async function loadConfig() {
701
724
  <button class="btn btn-accent" onclick="saveConfig()">Save Changes</button>
702
725
  <span style="font-size:11px;color:var(--text-muted)">Saving will reformat fleet.yaml (comments will be lost)</span>
703
726
  </div>`;
704
- } catch { el.innerHTML = `<div style="color:var(--error);padding:24px">Failed to load config</div>`; }
727
+ } catch { el.innerHTML = `<div style="color:var(--error);padding:24px">${tr("configLoadFailed")}</div>`; }
705
728
  }
706
729
  function togglePw(id) { const el = document.getElementById(id); el.type = el.type === "password" ? "text" : "password"; }
707
730
  function addRoot() { const el = document.getElementById("cfg-roots"); el.insertAdjacentHTML("beforeend", `<div style="display:flex;gap:4px;margin-bottom:6px"><input class="cfg-root" value="" placeholder="/path/to/projects" style="flex:1"><button class="btn btn-ghost" style="padding:4px 8px" onclick="this.parentElement.remove()">&#10005;</button></div>`); }
@@ -713,7 +736,7 @@ async function saveConfig() {
713
736
  };
714
737
  const r = await api("POST", "/ui/config", body);
715
738
  if (r.error) toast(r.error, false);
716
- else { toast("Config saved" + (r.needs_restart ? " (restart fleet to apply channel changes)" : "")); }
739
+ else { toast(tr("configSaved") + (r.needs_restart ? tr("configRestart") : "")); }
717
740
  }
718
741
  </script>
719
742
  </body>