@songsid/agend 2.1.4-beta.3 → 2.1.4-beta.31
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 +1 -1
- package/README.zh-TW.md +1 -1
- package/dist/agent-endpoint.js +1 -1
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/backend/antigravity.d.ts +9 -5
- package/dist/backend/antigravity.js +155 -30
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-code.d.ts +33 -21
- package/dist/backend/claude-code.js +103 -27
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex.d.ts +17 -0
- package/dist/backend/codex.js +123 -8
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/gemini-cli.js +2 -2
- package/dist/backend/gemini-cli.js.map +1 -1
- package/dist/backend/grok.js +2 -2
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro.d.ts +1 -0
- package/dist/backend/kiro.js +17 -3
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/opencode.d.ts +27 -11
- package/dist/backend/opencode.js +50 -39
- package/dist/backend/opencode.js.map +1 -1
- package/dist/backend/types.d.ts +20 -0
- package/dist/backend/types.js +12 -1
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/adapters/discord.js +106 -19
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.js +5 -3
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/agy-mcp-launcher.d.ts +2 -0
- package/dist/channel/agy-mcp-launcher.js +28 -0
- package/dist/channel/agy-mcp-launcher.js.map +1 -0
- package/dist/channel/mcp-server.js +6 -20
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.js +4 -2
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/types.d.ts +1 -1
- package/dist/classic-channel-manager.d.ts +40 -3
- package/dist/classic-channel-manager.js +218 -31
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +148 -33
- package/dist/cli.js.map +1 -1
- package/dist/completion-install.d.ts +70 -0
- package/dist/completion-install.js +152 -0
- package/dist/completion-install.js.map +1 -0
- package/dist/completion.d.ts +7 -1
- package/dist/completion.js +14 -4
- package/dist/completion.js.map +1 -1
- package/dist/config-validator.js +38 -7
- package/dist/config-validator.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.js +10 -1
- package/dist/config.js.map +1 -1
- package/dist/daemon.d.ts +177 -5
- package/dist/daemon.js +771 -99
- package/dist/daemon.js.map +1 -1
- package/dist/doctor.d.ts +26 -0
- package/dist/doctor.js +216 -0
- package/dist/doctor.js.map +1 -0
- package/dist/fleet-context.d.ts +47 -0
- package/dist/fleet-manager.d.ts +251 -10
- package/dist/fleet-manager.js +2059 -387
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-yaml-slim.d.ts +10 -0
- package/dist/fleet-yaml-slim.js +59 -0
- package/dist/fleet-yaml-slim.js.map +1 -0
- package/dist/general-knowledge/skills/backend-providers/SKILL.md +114 -0
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +3 -1
- package/dist/general-knowledge/skills/delegation-playbook/SKILL.md +52 -0
- package/dist/general-knowledge/skills/development-workflow/SKILL.md +28 -0
- package/dist/general-knowledge/skills/fleet-config/SKILL.md +1 -0
- package/dist/general-knowledge/skills/fleet-health/SKILL.md +1 -0
- package/dist/general-knowledge/skills/fleet-restart/SKILL.md +1 -0
- package/dist/general-knowledge/skills/instance-lifecycle/SKILL.md +1 -0
- package/dist/general-knowledge/skills/model-discovery/SKILL.md +64 -16
- package/dist/general-knowledge/skills/multi-channel/SKILL.md +1 -0
- package/dist/general-knowledge/skills/scheduling/SKILL.md +1 -0
- package/dist/general-knowledge/skills/session-management/SKILL.md +1 -0
- package/dist/general-knowledge/skills/tui-effort/SKILL.md +1 -0
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +30 -0
- package/dist/instance-lifecycle.d.ts +45 -0
- package/dist/instance-lifecycle.js +150 -5
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.d.ts +12 -0
- package/dist/instructions.js +35 -1
- package/dist/instructions.js.map +1 -1
- package/dist/locale.js +750 -288
- package/dist/locale.js.map +1 -1
- package/dist/login-flows.d.ts +85 -0
- package/dist/login-flows.js +159 -0
- package/dist/login-flows.js.map +1 -0
- package/dist/login-manager.d.ts +63 -0
- package/dist/login-manager.js +134 -0
- package/dist/login-manager.js.map +1 -0
- package/dist/outbound-handlers.d.ts +8 -0
- package/dist/outbound-handlers.js +44 -5
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +5 -0
- package/dist/outbound-schemas.js +7 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/quickstart.js +25 -0
- package/dist/quickstart.js.map +1 -1
- package/dist/restart-progress.d.ts +2 -0
- package/dist/restart-progress.js +3 -0
- package/dist/restart-progress.js.map +1 -1
- package/dist/scheduler/db.d.ts +12 -0
- package/dist/scheduler/db.js +59 -0
- package/dist/scheduler/db.js.map +1 -1
- package/dist/service-installer.d.ts +11 -0
- package/dist/service-installer.js +84 -18
- package/dist/service-installer.js.map +1 -1
- package/dist/settings-api.js +1 -1
- package/dist/settings-api.js.map +1 -1
- package/dist/setup-wizard.js +2 -2
- package/dist/setup-wizard.js.map +1 -1
- package/dist/tips.d.ts +44 -0
- package/dist/tips.js +355 -0
- package/dist/tips.js.map +1 -0
- package/dist/tmux-manager.d.ts +6 -0
- package/dist/tmux-manager.js +12 -0
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tool-progress.d.ts +40 -0
- package/dist/tool-progress.js +289 -0
- package/dist/tool-progress.js.map +1 -0
- package/dist/topic-commands.d.ts +36 -4
- package/dist/topic-commands.js +322 -64
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-monitor.d.ts +15 -2
- package/dist/transcript-monitor.js +63 -17
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +131 -0
- package/dist/transcript-sources.js +580 -0
- package/dist/transcript-sources.js.map +1 -0
- package/dist/types.d.ts +11 -0
- package/dist/ui/dashboard.html +55 -32
- package/dist/ui/settings.html +120 -51
- package/dist/ui/view.html +23 -15
- package/dist/usage/providers.d.ts +7 -0
- package/dist/usage/providers.js +18 -1
- package/dist/usage/providers.js.map +1 -1
- package/dist/usage/usage-api.d.ts +8 -2
- package/dist/usage/usage-api.js +24 -3
- package/dist/usage/usage-api.js.map +1 -1
- package/dist/workflow-templates/default.md +2 -1
- package/package.json +1 -1
package/dist/ui/settings.html
CHANGED
|
@@ -127,15 +127,15 @@
|
|
|
127
127
|
<body>
|
|
128
128
|
<header>
|
|
129
129
|
<h1 data-i18n="title">⚙️ AgEnD Settings</h1>
|
|
130
|
-
<span id="verBadge" title="What's New" style="font-size:12px;color:var(--muted);cursor:pointer;padding:2px 8px;border:1px solid var(--border);border-radius:12px">v…</span>
|
|
130
|
+
<span id="verBadge" data-i18n-title="whatsNew" title="What's New" style="font-size:12px;color:var(--muted);cursor:pointer;padding:2px 8px;border:1px solid var(--border);border-radius:12px">v…</span>
|
|
131
131
|
<div class="grow"></div>
|
|
132
|
-
<button class="help-btn" id="langBtn" title="Language">🌐</button>
|
|
133
|
-
<button class="help-btn" id="helpBtn" title="Help">ℹ️</button>
|
|
134
|
-
<button id="advancedToggle" title="Show additional controls">Show advanced</button>
|
|
132
|
+
<button class="help-btn" id="langBtn" data-i18n-title="languageButton" title="Language">🌐</button>
|
|
133
|
+
<button class="help-btn" id="helpBtn" data-i18n-title="helpButton" title="Help">ℹ️</button>
|
|
134
|
+
<button id="advancedToggle" data-i18n-title="showAdditional" title="Show additional controls">Show advanced</button>
|
|
135
135
|
</header>
|
|
136
136
|
<div class="help-backdrop" id="helpBackdrop"></div>
|
|
137
137
|
<aside class="help-panel" id="helpPanel">
|
|
138
|
-
<button class="close" id="helpClose" title="Close">×</button>
|
|
138
|
+
<button class="close" id="helpClose" data-i18n-title="close" title="Close">×</button>
|
|
139
139
|
<div id="helpContent"></div>
|
|
140
140
|
</aside>
|
|
141
141
|
<div class="banner" id="banner"></div>
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
backend: "Backend", model: "Model", workingDir: "Working directory", channelBinding: "Channel binding",
|
|
195
195
|
description: "Description", systemPrompt: "System prompt", tags: "Tags", generalTopicField: "General topic (fleet dispatcher)",
|
|
196
196
|
runtimeResources: "Runtime & Resources", autoPause: "Auto-pause after", minutes: "minutes", inheritDefault: "Inherit from defaults",
|
|
197
|
-
detection: "Detection", startup: "Startup", backendSection: "Backend", advancedSection: "Advanced & Access", hangEnabled: "Hang detector enabled", hangTimeout: "Stuck timeout", agentMode: "Agent mode", toolSet: "Tool set", logLevel: "Log level", concurrency: "Startup concurrency", staggerDelay: "Stagger delay", lightweight: "Lightweight mode", modelFailover: "Fallback model", displayName: "Display name", pause: "Pause", wake: "Wake", accessMode: "Access mode", affected: "Affected instances",
|
|
197
|
+
detection: "Detection", startup: "Startup", backendSection: "Backend", advancedSection: "Advanced & Access", hangEnabled: "Hang detector enabled", hangTimeout: "Stuck timeout", agentMode: "Agent mode", toolSet: "Tool set", toolProgress: "Tool progress", logLevel: "Log level", concurrency: "Startup concurrency", staggerDelay: "Stagger delay", lightweight: "Lightweight mode", modelFailover: "Fallback model", displayName: "Display name", pause: "Pause", wake: "Wake", accessMode: "Access mode", affected: "Affected instances",
|
|
198
198
|
language: "Language (bot replies)", defaultBackend: "Default backend", defaultModel: "Default model", allowedUsers: "Allowed users (primary bot)",
|
|
199
199
|
classicAdmins: "ClassicBot admins", allowedGuilds: "Allowed guilds", type: "Type", adapterId: "Adapter id",
|
|
200
200
|
tokenEnv: "Bot token env var", groupId: "Group / guild id", nameField: "Name",
|
|
@@ -202,7 +202,10 @@
|
|
|
202
202
|
noAgents: "No agents yet — click + New.", noBots: "No bots configured.", online: "✅ Online",
|
|
203
203
|
currentVersion: "Current version",
|
|
204
204
|
helpTitle: "📖 Help", tipReload: "💡 Click [↻ Reload Fleet] after changes to apply them.",
|
|
205
|
-
wnIntro: "Recent highlights:", releaseNotes: "📄 View full Release Notes →",
|
|
205
|
+
wnIntro: "Recent highlights:", releaseNotes: "📄 View full Release Notes →", languageButton: "Language", helpButton: "Help", showAdditional: "Show additional controls", hideAdvanced: "Hide advanced", showAdvanced: "Show advanced",
|
|
206
|
+
saved: "Saved", failed: "Failed", actionFailed: "{0} failed", noAgentMatch: "No agents match \"{0}\".", deleteAgent: "Delete agent \"{0}\"? This will be applied only after you confirm the pending changes.", nameRequired: "name required", tokenEnvRequired: "token env required", removeBot: "Remove bot \"{0}\"? (the token in .env is left untouched)",
|
|
207
|
+
changesPending: "{0} change(s) not applied", impactNow: "⚡ Immediately", impactAgent: "🔄 Restart this Agent", impactFleet: "🔄🔄 Restart AgEnD", queued: "Queued: {0}", applyFailed: "Apply failed", changesApplied: "✓ Changes applied. AgEnD is reconciling affected Agents.", reviewChanges: "Review changes", readyApply: "Ready to apply", copied: "Copied", expectedMapping: "expected a mapping", configLoadFailed: "Failed to load config — check the token in the URL.",
|
|
208
|
+
mustNonNegative: "Must be 0 or greater", mustPositive: "Must be greater than 0", nonnegativeInteger: "Must be a non-negative integer", failoverEmpty: "Empty list disables failover", concurrencyRange: "Must be an integer from 1 to 20", concurrencyWarning: "May exceed CPU capacity on smaller hosts", staggerRange: "Must be between 0 and 30000", staggerWarning: "Very low values can cause startup spikes",
|
|
206
209
|
},
|
|
207
210
|
"zh-TW": {
|
|
208
211
|
title: "⚙️ AgEnD 設定", reloadFleet: "↻ 重新載入", myAgents: "🤖 我的 Agent", bots: "🔌 機器人", classicChannels: "🎮 ClassicBot 頻道", noClassic: "沒有 ClassicBot 頻道 — 在聊天中用 /start 建立。",
|
|
@@ -212,7 +215,7 @@
|
|
|
212
215
|
backend: "後端", model: "模型", workingDir: "工作目錄", channelBinding: "頻道綁定",
|
|
213
216
|
description: "描述", systemPrompt: "System prompt", tags: "標籤", generalTopicField: "General topic(fleet 分派器)",
|
|
214
217
|
runtimeResources: "執行環境與資源", autoPause: "閒置後自動暫停", minutes: "分鐘", inheritDefault: "沿用全域預設值",
|
|
215
|
-
detection: "偵測", startup: "啟動", backendSection: "後端", advancedSection: "進階與存取", hangEnabled: "啟用卡住偵測", hangTimeout: "卡住逾時", agentMode: "Agent 模式", toolSet: "工具集", logLevel: "Log 等級", concurrency: "啟動併發數", staggerDelay: "錯開延遲", lightweight: "輕量模式", modelFailover: "備援模型", displayName: "顯示名稱", pause: "暫停", wake: "喚醒", accessMode: "存取模式", affected: "受影響 instances",
|
|
218
|
+
detection: "偵測", startup: "啟動", backendSection: "後端", advancedSection: "進階與存取", hangEnabled: "啟用卡住偵測", hangTimeout: "卡住逾時", agentMode: "Agent 模式", toolSet: "工具集", toolProgress: "工具進度", logLevel: "Log 等級", concurrency: "啟動併發數", staggerDelay: "錯開延遲", lightweight: "輕量模式", modelFailover: "備援模型", displayName: "顯示名稱", pause: "暫停", wake: "喚醒", accessMode: "存取模式", affected: "受影響 instances",
|
|
216
219
|
language: "語言(bot 回覆)", defaultBackend: "預設後端", defaultModel: "預設模型", allowedUsers: "允許使用者(主 bot)",
|
|
217
220
|
classicAdmins: "ClassicBot 管理員", allowedGuilds: "允許的伺服器", type: "類型", adapterId: "Adapter id",
|
|
218
221
|
tokenEnv: "Bot token 環境變數", groupId: "群組 / guild id", nameField: "名稱",
|
|
@@ -220,7 +223,10 @@
|
|
|
220
223
|
noAgents: "還沒有 agent — 點 + 新增。", noBots: "尚未設定機器人。", online: "✅ 上線",
|
|
221
224
|
currentVersion: "目前版本",
|
|
222
225
|
helpTitle: "📖 使用說明", tipReload: "💡 修改後點 [↻ 重新載入] 讓設定生效。",
|
|
223
|
-
wnIntro: "近期更新重點:", releaseNotes: "📄 查看完整 Release Notes →",
|
|
226
|
+
wnIntro: "近期更新重點:", releaseNotes: "📄 查看完整 Release Notes →", languageButton: "語言", helpButton: "使用說明", showAdditional: "顯示更多控制項", hideAdvanced: "隱藏進階設定", showAdvanced: "顯示進階設定",
|
|
227
|
+
saved: "已儲存", failed: "失敗", actionFailed: "{0}失敗", noAgentMatch: "沒有符合「{0}」的 Agent。", deleteAgent: "確定刪除 Agent「{0}」嗎?只有在確認套用待處理變更後才會生效。", nameRequired: "必須填寫名稱", tokenEnvRequired: "必須填寫 token 環境變數", removeBot: "確定移除機器人「{0}」嗎?(.env 中的 token 會保留)",
|
|
228
|
+
changesPending: "有 {0} 項變更尚未套用", impactNow: "⚡ 立即生效", impactAgent: "🔄 重新啟動此 Agent", impactFleet: "🔄🔄 重新啟動 AgEnD", queued: "已加入待套用:{0}", applyFailed: "套用失敗", changesApplied: "✓ 變更已套用,AgEnD 正在協調受影響的 Agent。", reviewChanges: "檢查變更", readyApply: "可套用變更", copied: "已複製", expectedMapping: "預期為 mapping", configLoadFailed: "載入設定失敗 — 請檢查網址中的 token。",
|
|
229
|
+
mustNonNegative: "必須為 0 或更大的數值", mustPositive: "必須大於 0", nonnegativeInteger: "必須是非負整數", failoverEmpty: "空白清單會停用模型備援", concurrencyRange: "必須是 1 到 20 的整數", concurrencyWarning: "在較小的主機上可能超出 CPU 負荷", staggerRange: "必須介於 0 到 30000", staggerWarning: "數值太低可能造成啟動尖峰",
|
|
224
230
|
},
|
|
225
231
|
};
|
|
226
232
|
const HELP = {
|
|
@@ -242,9 +248,11 @@
|
|
|
242
248
|
let lang = localStorage.getItem("agend_lang") || (navigator.language && navigator.language.toLowerCase().startsWith("zh") ? "zh-TW" : "en");
|
|
243
249
|
if (!I18N[lang]) lang = "en";
|
|
244
250
|
const t = (k) => (I18N[lang] && I18N[lang][k]) || I18N.en[k] || k;
|
|
251
|
+
const tf = (k, ...values) => values.reduce((text, value, index) => text.replaceAll(`{${index}}`, String(value)), t(k));
|
|
245
252
|
function applyStaticI18n() {
|
|
246
253
|
document.querySelectorAll("[data-i18n]").forEach(e => { e.textContent = t(e.getAttribute("data-i18n")); });
|
|
247
254
|
document.querySelectorAll("[data-i18n-ph]").forEach(e => { e.setAttribute("placeholder", t(e.getAttribute("data-i18n-ph"))); });
|
|
255
|
+
document.querySelectorAll("[data-i18n-title]").forEach(e => { e.setAttribute("title", t(e.getAttribute("data-i18n-title"))); });
|
|
248
256
|
// toggle labels that carry expand/collapse state
|
|
249
257
|
for (const [id, body] of [["advToggle", "advBody"]]) {
|
|
250
258
|
const b = $(body); if (b) $(id).textContent = b.classList.contains("open") ? t("collapse") : t("expand");
|
|
@@ -292,7 +300,7 @@
|
|
|
292
300
|
const b = res.body || {}; let html = "";
|
|
293
301
|
if (b.errors && b.errors.length) html += b.errors.map(e => `<span class="e">✗ ${esc(e.path ? e.path + ": " : "")}${esc(e.message)}</span>`).join(" ");
|
|
294
302
|
if (b.warnings && b.warnings.length) html += " " + b.warnings.map(w => `<span class="w">⚠ ${esc(w.message)}</span>`).join(" ");
|
|
295
|
-
if (res.ok) html = `<span class="s">✓
|
|
303
|
+
if (res.ok) html = `<span class="s">✓ ${esc(t("saved"))}</span>` + (html ? " " + html : "");
|
|
296
304
|
else if (!html) html = `<span class="e">✗ ${esc((b && b.error) || ("HTTP " + res.status))}</span>`;
|
|
297
305
|
elm.innerHTML = html;
|
|
298
306
|
}
|
|
@@ -322,18 +330,19 @@
|
|
|
322
330
|
redraw(); return box;
|
|
323
331
|
}
|
|
324
332
|
function select(value, options) { const s = el("select"); for (const o of options) { const val = typeof o === "string" ? o : o.value; const label = typeof o === "string" ? o : o.label; const opt = el("option", {}, label); opt.value = val; if (val === value) opt.selected = true; s.append(opt); } return s; }
|
|
325
|
-
const
|
|
333
|
+
const impactText = (kind) => t(kind === "now" ? "impactNow" : kind === "fleet" ? "impactFleet" : "impactAgent");
|
|
334
|
+
const impact = (kind) => el("span", { class: "impact" }, impactText(kind));
|
|
326
335
|
function updatePendingBar() {
|
|
327
336
|
const n = state.pending.size;
|
|
328
337
|
$("pendingBar").classList.toggle("open", n > 0);
|
|
329
|
-
$("pendingCount").textContent =
|
|
338
|
+
$("pendingCount").textContent = tf("changesPending", n);
|
|
330
339
|
const kinds = new Set([...state.pending.values()].map(change => change.impact));
|
|
331
|
-
$("pendingImpact").textContent = [...kinds].map(
|
|
340
|
+
$("pendingImpact").textContent = [...kinds].map(impactText).join(" · ");
|
|
332
341
|
}
|
|
333
342
|
function stageChange(key, change) {
|
|
334
343
|
state.pending.set(key, change);
|
|
335
344
|
updatePendingBar();
|
|
336
|
-
showBanner(
|
|
345
|
+
showBanner(tf("queued", change.label));
|
|
337
346
|
}
|
|
338
347
|
async function applyPendingChanges() {
|
|
339
348
|
const button = $("applyChanges"); button.disabled = true;
|
|
@@ -342,17 +351,22 @@
|
|
|
342
351
|
try { result = await change.apply(); }
|
|
343
352
|
catch (error) { result = { ok: false, body: { error: error.message } }; }
|
|
344
353
|
if (!result?.ok) {
|
|
345
|
-
showBanner(`${change.label}: ${result?.body?.error || "
|
|
354
|
+
showBanner(`${change.label}: ${result?.body?.error || t("applyFailed")}`, true);
|
|
346
355
|
button.disabled = false; return;
|
|
347
356
|
}
|
|
348
357
|
state.pending.delete(key); updatePendingBar();
|
|
349
358
|
}
|
|
350
359
|
await api("/api/settings/reload", { method: "POST" });
|
|
351
360
|
await reload();
|
|
352
|
-
showBanner("
|
|
361
|
+
showBanner(t("changesApplied"));
|
|
353
362
|
button.disabled = false;
|
|
354
363
|
}
|
|
355
364
|
const hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj || {}, key);
|
|
365
|
+
const HOT_FIELDS = new Set(["tool_progress", "mcp_proxy_reply", "auto_pause_after", "warm_cap", "display_name", "description", "tags", "log_level"]);
|
|
366
|
+
const sameValue = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
367
|
+
function changedFields(next, baseline) {
|
|
368
|
+
return Object.fromEntries(Object.entries(next).filter(([key, value]) => !sameValue(value, baseline[key])));
|
|
369
|
+
}
|
|
356
370
|
function overrideControl(inst, key, input, inheritedValue, impactKind = "instance") {
|
|
357
371
|
const inherited = !hasOwn(inst, key);
|
|
358
372
|
const toggle = el("input", { type: "checkbox" }); toggle.checked = inherited;
|
|
@@ -419,7 +433,7 @@
|
|
|
419
433
|
const filter = ($("agentSearch")?.value || "").trim().toLowerCase();
|
|
420
434
|
let names = Object.keys(insts);
|
|
421
435
|
if (filter) names = names.filter(n => n.toLowerCase().includes(filter) || shortName(n, insts[n]).toLowerCase().includes(filter));
|
|
422
|
-
if (!names.length) { host.className = "card empty"; host.textContent = "
|
|
436
|
+
if (!names.length) { host.className = "card empty"; host.textContent = tf("noAgentMatch", filter); return; }
|
|
423
437
|
host.className = "card";
|
|
424
438
|
// Group by first tag; "Other" sorts last. Within a group: Running → Crashed
|
|
425
439
|
// → Stopped, then name.
|
|
@@ -455,6 +469,7 @@
|
|
|
455
469
|
const hang = overrideControl(hangHolder, "value", el("input", { type: "number", min: "0.1", step: "0.5", value: hangRaw.timeout_minutes ?? defaults.hang_detector?.timeout_minutes ?? 15 }), defaults.hang_detector?.timeout_minutes ?? 15);
|
|
456
470
|
const agentMode = overrideControl(inst, "agent_mode", select(inst.agent_mode || defaults.agent_mode || "mcp", ["mcp", "cli"]), defaults.agent_mode || "mcp");
|
|
457
471
|
const toolSet = overrideControl(inst, "tool_set", select(inst.tool_set || defaults.tool_set || "full", ["full", "standard", "minimal"]), defaults.tool_set || "full");
|
|
472
|
+
const toolProgress = overrideControl(inst, "tool_progress", select(inst.tool_progress || defaults.tool_progress || "off", ["off", "standard", "verbose"]), defaults.tool_progress || "off", "now");
|
|
458
473
|
const logLevel = overrideControl(inst, "log_level", select(inst.log_level || defaults.log_level || "info", ["trace", "debug", "info", "warn", "error"]), defaults.log_level || "info");
|
|
459
474
|
const fLightweight = el("input", { type: "checkbox" }); fLightweight.checked = inst.lightweight ?? defaults.lightweight ?? false;
|
|
460
475
|
const lightweight = overrideControl(inst, "lightweight", fLightweight, defaults.lightweight ?? false);
|
|
@@ -468,18 +483,18 @@
|
|
|
468
483
|
const saveBtn = el("button", { class: "primary sm" }, t("save"));
|
|
469
484
|
const validate = () => {
|
|
470
485
|
const autoValue = Number(auto.input.value), hangValue = Number(hang.input.value);
|
|
471
|
-
const validAuto = setValidation(auto.input, autoFeedback, !auto.toggle.checked && (!Number.isFinite(autoValue) || autoValue < 0) ? "
|
|
472
|
-
const validHang = setValidation(hang.input, hangFeedback, !hang.toggle.checked && (!Number.isFinite(hangValue) || hangValue <= 0) ? "
|
|
473
|
-
setValidation(failover.input, failoverFeedback, "", !failover.toggle.checked && !failover.input.value.trim() ? "
|
|
486
|
+
const validAuto = setValidation(auto.input, autoFeedback, !auto.toggle.checked && (!Number.isFinite(autoValue) || autoValue < 0) ? t("mustNonNegative") : "");
|
|
487
|
+
const validHang = setValidation(hang.input, hangFeedback, !hang.toggle.checked && (!Number.isFinite(hangValue) || hangValue <= 0) ? t("mustPositive") : "");
|
|
488
|
+
setValidation(failover.input, failoverFeedback, "", !failover.toggle.checked && !failover.input.value.trim() ? t("failoverEmpty") : "");
|
|
474
489
|
saveBtn.disabled = !(validAuto && validHang);
|
|
475
490
|
return !saveBtn.disabled;
|
|
476
491
|
};
|
|
477
|
-
for (const ctl of [auto, hang, agentMode, toolSet, logLevel, lightweight, failover, displayName]) {
|
|
492
|
+
for (const ctl of [auto, hang, agentMode, toolSet, toolProgress, logLevel, lightweight, failover, displayName]) {
|
|
478
493
|
ctl.input.addEventListener("input", validate); ctl.input.addEventListener("change", validate); ctl.toggle.addEventListener("change", validate);
|
|
479
494
|
}
|
|
480
495
|
saveBtn.onclick = async () => {
|
|
481
496
|
if (!validate()) return;
|
|
482
|
-
const
|
|
497
|
+
const nextPatch = {
|
|
483
498
|
backend: fBackend.value, model: fModel.value.trim() || null,
|
|
484
499
|
working_directory: fWd.value, channel_id: fChan.value || undefined,
|
|
485
500
|
description: fDesc.value || undefined, systemPrompt: fSys.value || undefined,
|
|
@@ -488,11 +503,31 @@
|
|
|
488
503
|
hang_detector: { timeout_minutes: hang.toggle.checked ? null : Number(hang.input.value) },
|
|
489
504
|
agent_mode: agentMode.toggle.checked ? null : agentMode.input.value,
|
|
490
505
|
tool_set: toolSet.toggle.checked ? null : toolSet.input.value,
|
|
506
|
+
tool_progress: toolProgress.toggle.checked ? null : toolProgress.input.value,
|
|
491
507
|
log_level: logLevel.toggle.checked ? null : logLevel.input.value,
|
|
492
508
|
lightweight: lightweight.toggle.checked ? null : lightweight.input.checked,
|
|
493
509
|
model_failover: failover.toggle.checked ? null : (failover.input.value.trim() ? [failover.input.value.trim()] : []),
|
|
494
510
|
display_name: displayName.toggle.checked ? null : displayName.input.value.trim(),
|
|
495
511
|
};
|
|
512
|
+
const patch = changedFields(nextPatch, {
|
|
513
|
+
backend: inst.backend || defaults.backend || "claude-code",
|
|
514
|
+
model: inst.model?.trim() || null,
|
|
515
|
+
working_directory: inst.working_directory || "",
|
|
516
|
+
channel_id: inst.channel_id || undefined,
|
|
517
|
+
description: inst.description || undefined,
|
|
518
|
+
systemPrompt: inst.systemPrompt || undefined,
|
|
519
|
+
tags: inst.tags?.length ? inst.tags : undefined,
|
|
520
|
+
general_topic: !!inst.general_topic,
|
|
521
|
+
auto_pause_after: hasOwn(inst, "auto_pause_after") ? inst.auto_pause_after : null,
|
|
522
|
+
hang_detector: { timeout_minutes: hasOwn(hangRaw, "timeout_minutes") ? hangRaw.timeout_minutes : null },
|
|
523
|
+
agent_mode: hasOwn(inst, "agent_mode") ? inst.agent_mode : null,
|
|
524
|
+
tool_set: hasOwn(inst, "tool_set") ? inst.tool_set : null,
|
|
525
|
+
tool_progress: hasOwn(inst, "tool_progress") ? inst.tool_progress : null,
|
|
526
|
+
log_level: hasOwn(inst, "log_level") ? inst.log_level : null,
|
|
527
|
+
lightweight: hasOwn(inst, "lightweight") ? inst.lightweight : null,
|
|
528
|
+
model_failover: hasOwn(inst, "model_failover") ? inst.model_failover : null,
|
|
529
|
+
display_name: hasOwn(inst, "display_name") ? inst.display_name : null,
|
|
530
|
+
});
|
|
496
531
|
state.fleet.instances[name] = { ...inst, ...patch };
|
|
497
532
|
for (const [key, value] of Object.entries(patch)) if (value === null) delete state.fleet.instances[name][key];
|
|
498
533
|
if (patch.hang_detector?.timeout_minutes === null) {
|
|
@@ -501,7 +536,7 @@
|
|
|
501
536
|
}
|
|
502
537
|
stageChange(`agent:${name}`, {
|
|
503
538
|
label: `Update ${shortName(name, state.fleet.instances[name])}`,
|
|
504
|
-
impact: "instance",
|
|
539
|
+
impact: Object.keys(patch).every(key => HOT_FIELDS.has(key)) ? "now" : "instance",
|
|
505
540
|
apply: () => api(`/api/settings/fleet/instances/${encodeURIComponent(name)}`, { method: "PATCH", body: JSON.stringify(patch) }),
|
|
506
541
|
});
|
|
507
542
|
editing = null; renderAgents();
|
|
@@ -515,7 +550,7 @@
|
|
|
515
550
|
el("div", {}, el("label", {}, t("model"), impact("instance")), fModel)),
|
|
516
551
|
el("label", {}, t("workingDir"), impact("instance")), fWd,
|
|
517
552
|
el("div", { class: "grid2" },
|
|
518
|
-
el("div", {}, el("label", {}, `${t("autoPause")} (${t("minutes")})`, impact("
|
|
553
|
+
el("div", {}, el("label", {}, `${t("autoPause")} (${t("minutes")})`, impact("now")), auto.row, autoFeedback)),
|
|
519
554
|
el("div", { class: "advanced-only" },
|
|
520
555
|
el("div", { class: "sub", style: "margin:14px 0 6px;font-weight:600" }, "Advanced Agent settings"),
|
|
521
556
|
el("div", { class: "grid2" },
|
|
@@ -523,7 +558,8 @@
|
|
|
523
558
|
el("div", {}, el("label", {}, `${t("hangTimeout")} (${t("minutes")})`), hang.row, hangFeedback),
|
|
524
559
|
el("div", {}, el("label", {}, t("agentMode")), agentMode.row),
|
|
525
560
|
el("div", {}, el("label", {}, t("toolSet")), toolSet.row),
|
|
526
|
-
el("div", {}, el("label", {}, t("
|
|
561
|
+
el("div", {}, el("label", {}, t("toolProgress"), impact("now")), toolProgress.row),
|
|
562
|
+
el("div", {}, el("label", {}, t("logLevel"), impact("now")), logLevel.row),
|
|
527
563
|
el("div", {}, el("label", {}, t("lightweight")), lightweight.row),
|
|
528
564
|
el("div", {}, el("label", {}, t("modelFailover")), failover.row, failoverFeedback)),
|
|
529
565
|
el("label", {}, t("systemPrompt"), impact("instance")), fSys,
|
|
@@ -544,11 +580,11 @@
|
|
|
544
580
|
}
|
|
545
581
|
async function pauseWakeAgent(name, action) {
|
|
546
582
|
const res = await api(`/api/settings/instances/${encodeURIComponent(name)}/${action}`, { method: "POST" });
|
|
547
|
-
if (!res.ok) alert(res.body?.error ||
|
|
583
|
+
if (!res.ok) alert(res.body?.error || tf("actionFailed", action));
|
|
548
584
|
await reloadLive();
|
|
549
585
|
}
|
|
550
586
|
async function delAgent(name) {
|
|
551
|
-
if (!confirm(
|
|
587
|
+
if (!confirm(tf("deleteAgent", name))) return;
|
|
552
588
|
delete state.fleet.instances[name];
|
|
553
589
|
stageChange(`agent:${name}`, {
|
|
554
590
|
label: `Delete ${name}`, impact: "fleet",
|
|
@@ -569,7 +605,7 @@
|
|
|
569
605
|
el("div", { class: "grid2" }, el("div", {}, el("label", {}, t("nameField")), fName), el("div", {}, el("label", {}, t("backend")), fBackend)),
|
|
570
606
|
el("div", { class: "grid2" }, el("div", {}, el("label", {}, t("workingDir")), fWd), el("div", {}, el("label", {}, t("channelBinding")), fChan)),
|
|
571
607
|
el("div", { class: "actions" }, el("button", { class: "primary sm", onclick: async () => {
|
|
572
|
-
const name = fName.value.trim(); if (!name) { msg.innerHTML = `<span class="e">✗
|
|
608
|
+
const name = fName.value.trim(); if (!name) { msg.innerHTML = `<span class="e">✗ ${esc(t("nameRequired"))}</span>`; return; }
|
|
573
609
|
const body = { working_directory: fWd.value, backend: fBackend.value, channel_id: fChan.value || undefined };
|
|
574
610
|
const res = await api(`/api/settings/fleet/instances/${encodeURIComponent(name)}`, { method: "POST", body: JSON.stringify(body) });
|
|
575
611
|
resultMsg(msg, res); if (res.ok) { state.newAgentOpen = false; $("newAgentBtn").textContent = t("new"); renderNewAgent(false); await reload(); }
|
|
@@ -655,8 +691,8 @@
|
|
|
655
691
|
const validate = () => {
|
|
656
692
|
const autoValue = Number(auto.input.value);
|
|
657
693
|
const contextValue = Number(fContext.value);
|
|
658
|
-
const validAuto = setValidation(auto.input, autoFeedback, !auto.toggle.checked && (!Number.isFinite(autoValue) || autoValue < 0) ? "
|
|
659
|
-
const validContext = setValidation(fContext, contextFeedback, !Number.isInteger(contextValue) || contextValue < 0 ? "
|
|
694
|
+
const validAuto = setValidation(auto.input, autoFeedback, !auto.toggle.checked && (!Number.isFinite(autoValue) || autoValue < 0) ? t("mustNonNegative") : "");
|
|
695
|
+
const validContext = setValidation(fContext, contextFeedback, !Number.isInteger(contextValue) || contextValue < 0 ? t("nonnegativeInteger") : "");
|
|
660
696
|
saveBtn.disabled = !(validAuto && validContext);
|
|
661
697
|
return !saveBtn.disabled;
|
|
662
698
|
};
|
|
@@ -744,10 +780,10 @@
|
|
|
744
780
|
}
|
|
745
781
|
async function delBot(i) {
|
|
746
782
|
const chs = channels(); const ch = chs[i];
|
|
747
|
-
if (!confirm(
|
|
783
|
+
if (!confirm(tf("removeBot", `${ch.type} (${chLabel(i)})`))) return;
|
|
748
784
|
chs.splice(i, 1);
|
|
749
785
|
state.fleet.channels = chs; delete state.fleet.channel;
|
|
750
|
-
const res = await saveChannels(); if (res.ok) await reload(); else alert((res.body && res.body.error) || "
|
|
786
|
+
const res = await saveChannels(); if (res.ok) await reload(); else alert((res.body && res.body.error) || t("failed"));
|
|
751
787
|
}
|
|
752
788
|
function renderNewBot(open) {
|
|
753
789
|
const host = $("newBot"); host.innerHTML = ""; if (!open) return;
|
|
@@ -762,7 +798,7 @@
|
|
|
762
798
|
el("div", { class: "grid2" }, el("div", {}, el("label", {}, t("tokenEnv")), fEnv), el("div", {}, el("label", {}, t("groupId")), fGroup)),
|
|
763
799
|
el("div", { class: "hint" }, t("envHint")),
|
|
764
800
|
el("div", { class: "actions" }, el("button", { class: "primary sm", onclick: async () => {
|
|
765
|
-
if (!fEnv.value.trim()) { msg.innerHTML = `<span class="e">✗
|
|
801
|
+
if (!fEnv.value.trim()) { msg.innerHTML = `<span class="e">✗ ${esc(t("tokenEnvRequired"))}</span>`; return; }
|
|
766
802
|
const chs = channels();
|
|
767
803
|
chs.push({ id: fId.value.trim() || undefined, type: fType.value, bot_token_env: fEnv.value.trim(), group_id: fGroup.value.trim() || undefined, access: { mode: "locked", allowed_users: [] } });
|
|
768
804
|
state.fleet.channels = chs; delete state.fleet.channel;
|
|
@@ -778,6 +814,11 @@
|
|
|
778
814
|
state.fleet.defaults = state.fleet.defaults || {};
|
|
779
815
|
state.classic.defaults = state.classic.defaults || {};
|
|
780
816
|
const d = state.fleet.defaults, c = state.classic.defaults;
|
|
817
|
+
// The controls below mutate their arrays in place. Keep raw snapshots so
|
|
818
|
+
// changing one hot default does not also stage untouched access settings as
|
|
819
|
+
// a fleet restart.
|
|
820
|
+
const originalChannels = structuredClone(channels());
|
|
821
|
+
const originalClassicDefaults = structuredClone(c);
|
|
781
822
|
const hang = d.hang_detector || {};
|
|
782
823
|
const startup = d.startup || {};
|
|
783
824
|
const primary = channels()[0];
|
|
@@ -790,6 +831,7 @@
|
|
|
790
831
|
const fModel = el("input", { type: "text", value: d.model || "", placeholder: "e.g. claude-opus-4-8" });
|
|
791
832
|
const fAgentMode = select(d.agent_mode || "mcp", ["mcp", "cli"]);
|
|
792
833
|
const fToolSet = select(d.tool_set || "full", ["full", "standard", "minimal"]);
|
|
834
|
+
const fToolProgress = select(d.tool_progress || "off", ["off", "standard", "verbose"]);
|
|
793
835
|
const fLogLevel = select(d.log_level || "info", ["trace", "debug", "info", "warn", "error"]);
|
|
794
836
|
const fLocale = el("select");
|
|
795
837
|
const fAutoPause = el("input", { type: "number", min: "0", step: "1", value: d.auto_pause_after ?? 0, placeholder: "0 = disabled" });
|
|
@@ -807,37 +849,63 @@
|
|
|
807
849
|
const validate = () => {
|
|
808
850
|
const auto = Number(fAutoPause.value), timeout = Number(fHangTimeout.value);
|
|
809
851
|
const concurrency = Number(fConcurrency.value), stagger = Number(fStagger.value);
|
|
810
|
-
const validAuto = setValidation(fAutoPause, autoFeedback, !Number.isFinite(auto) || auto < 0 ? "
|
|
811
|
-
const validHang = setValidation(fHangTimeout, hangFeedback, !Number.isFinite(timeout) || timeout <= 0 ? "
|
|
812
|
-
const validConcurrency = setValidation(fConcurrency, concurrencyFeedback, !Number.isInteger(concurrency) || concurrency < 1 || concurrency > 20 ? "
|
|
813
|
-
const validStagger = setValidation(fStagger, staggerFeedback, !Number.isFinite(stagger) || stagger < 0 || stagger > 30000 ? "
|
|
852
|
+
const validAuto = setValidation(fAutoPause, autoFeedback, !Number.isFinite(auto) || auto < 0 ? t("mustNonNegative") : "");
|
|
853
|
+
const validHang = setValidation(fHangTimeout, hangFeedback, !Number.isFinite(timeout) || timeout <= 0 ? t("mustPositive") : "");
|
|
854
|
+
const validConcurrency = setValidation(fConcurrency, concurrencyFeedback, !Number.isInteger(concurrency) || concurrency < 1 || concurrency > 20 ? t("concurrencyRange") : "", concurrency > 4 ? t("concurrencyWarning") : "");
|
|
855
|
+
const validStagger = setValidation(fStagger, staggerFeedback, !Number.isFinite(stagger) || stagger < 0 || stagger > 30000 ? t("staggerRange") : "", stagger < 100 ? t("staggerWarning") : "");
|
|
814
856
|
saveBtn.disabled = !(validAuto && validHang && validConcurrency && validStagger);
|
|
815
857
|
return !saveBtn.disabled;
|
|
816
858
|
};
|
|
817
859
|
for (const input of [fAutoPause, fHangTimeout, fConcurrency, fStagger]) {
|
|
818
860
|
input.addEventListener("input", validate); input.addEventListener("change", validate);
|
|
819
861
|
}
|
|
820
|
-
saveBtn.textContent = "
|
|
862
|
+
saveBtn.textContent = t("reviewChanges");
|
|
821
863
|
saveBtn.onclick = () => {
|
|
822
864
|
if (!validate()) return;
|
|
823
|
-
const
|
|
865
|
+
const nextDefaults = {
|
|
824
866
|
backend: fBackend.value,
|
|
825
867
|
model: fModel.value || undefined,
|
|
826
868
|
locale: fLocale.value,
|
|
827
869
|
auto_pause_after: Number(fAutoPause.value),
|
|
828
870
|
agent_mode: fAgentMode.value,
|
|
829
871
|
tool_set: fToolSet.value,
|
|
872
|
+
tool_progress: fToolProgress.value,
|
|
830
873
|
log_level: fLogLevel.value,
|
|
831
874
|
hang_detector: { enabled: fHangEnabled.checked, timeout_minutes: Number(fHangTimeout.value) },
|
|
832
875
|
startup: { concurrency: Number(fConcurrency.value), stagger_delay_ms: Number(fStagger.value) },
|
|
833
876
|
};
|
|
834
|
-
const
|
|
877
|
+
const defaultsPatch = changedFields(nextDefaults, {
|
|
878
|
+
backend: d.backend || "claude-code",
|
|
879
|
+
model: d.model || undefined,
|
|
880
|
+
locale: d.locale || "",
|
|
881
|
+
auto_pause_after: d.auto_pause_after ?? 0,
|
|
882
|
+
agent_mode: d.agent_mode || "mcp",
|
|
883
|
+
tool_set: d.tool_set || "full",
|
|
884
|
+
tool_progress: d.tool_progress || "off",
|
|
885
|
+
log_level: d.log_level || "info",
|
|
886
|
+
hang_detector: { enabled: hang.enabled !== false, timeout_minutes: hang.timeout_minutes ?? 15 },
|
|
887
|
+
startup: { concurrency: startup.concurrency ?? 10, stagger_delay_ms: startup.stagger_delay_ms ?? 500 },
|
|
888
|
+
});
|
|
889
|
+
const classicPatch = changedFields(
|
|
890
|
+
{ backend: fClassicBackend.value, admin_users: c.admin_users, allowed_guilds: c.allowed_guilds },
|
|
891
|
+
{
|
|
892
|
+
backend: originalClassicDefaults.backend || d.backend || "claude-code",
|
|
893
|
+
admin_users: originalClassicDefaults.admin_users || [],
|
|
894
|
+
allowed_guilds: originalClassicDefaults.allowed_guilds || [],
|
|
895
|
+
},
|
|
896
|
+
);
|
|
835
897
|
state.fleet.defaults = { ...d, ...defaultsPatch };
|
|
836
898
|
state.classic.defaults = { ...c, ...classicPatch };
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
899
|
+
if (Object.keys(defaultsPatch).length > 0) {
|
|
900
|
+
stageChange("defaults:fleet", { label: "Update Agent defaults", impact: Object.keys(defaultsPatch).every(key => HOT_FIELDS.has(key)) ? "now" : "instance", apply: () => api("/api/settings/fleet/defaults", { method: "PUT", body: JSON.stringify(defaultsPatch) }) });
|
|
901
|
+
}
|
|
902
|
+
if (primary && !sameValue(channels(), originalChannels)) {
|
|
903
|
+
stageChange("connections:primary", { label: "Update primary connection access", impact: "fleet", apply: () => api("/api/settings/fleet/channels", { method: "PUT", body: JSON.stringify(channels()) }) });
|
|
904
|
+
}
|
|
905
|
+
if (Object.keys(classicPatch).length > 0) {
|
|
906
|
+
stageChange("defaults:classic", { label: "Update ClassicBot defaults and access", impact: "fleet", apply: () => api("/api/settings/classic/defaults", { method: "PUT", body: JSON.stringify(classicPatch) }) });
|
|
907
|
+
}
|
|
908
|
+
msg.innerHTML = `<span class="s">${esc(t("readyApply"))}</span>`;
|
|
841
909
|
};
|
|
842
910
|
|
|
843
911
|
const section = (title, open, ...children) => {
|
|
@@ -851,7 +919,7 @@
|
|
|
851
919
|
el("div", { class: "field" }, el("label", {}, t("language"), impact("now")), fLocale),
|
|
852
920
|
el("div", { class: "field" }, el("label", {}, t("defaultBackend"), impact("instance")), fBackend),
|
|
853
921
|
el("div", { class: "field" }, el("label", {}, t("defaultModel"), impact("instance")), fModel),
|
|
854
|
-
el("div", { class: "field" }, el("label", {}, `${t("autoPause")} (${t("minutes")})`, impact("
|
|
922
|
+
el("div", { class: "field" }, el("label", {}, `${t("autoPause")} (${t("minutes")})`, impact("now")), fAutoPause, autoFeedback))),
|
|
855
923
|
section("ClassicBot", true,
|
|
856
924
|
el("div", { class: "grid2" },
|
|
857
925
|
el("div", { class: "field" }, el("label", {}, "Classic default backend", impact("instance")), fClassicBackend)),
|
|
@@ -863,7 +931,8 @@
|
|
|
863
931
|
section(t("runtimeResources"), false,
|
|
864
932
|
el("div", { class: "grid2" },
|
|
865
933
|
el("div", { class: "field" }, el("label", {}, t("toolSet"), impact("instance")), fToolSet),
|
|
866
|
-
el("div", { class: "field" }, el("label", {}, t("
|
|
934
|
+
el("div", { class: "field" }, el("label", {}, t("toolProgress"), impact("now")), fToolProgress),
|
|
935
|
+
el("div", { class: "field" }, el("label", {}, t("logLevel"), impact("now")), fLogLevel),
|
|
867
936
|
el("div", { class: "field" }, el("label", {}, t("agentMode"), impact("instance")), fAgentMode))),
|
|
868
937
|
section(t("detection"), false,
|
|
869
938
|
el("div", { class: "grid2" },
|
|
@@ -906,7 +975,7 @@
|
|
|
906
975
|
el("button", { class: "sm" + (fmt === "yaml" ? " on" : ""), onclick: () => { state.advFmt = "yaml"; renderAdvanced(); } }, "YAML"),
|
|
907
976
|
el("button", { class: "sm" + (fmt === "json" ? " on" : ""), onclick: () => { state.advFmt = "json"; renderAdvanced(); } }, "JSON")),
|
|
908
977
|
el("button", { class: "sm", onclick: () => { state.advEdit = !state.advEdit; renderAdvanced(); } }, state.advEdit ? "View" : "Edit"),
|
|
909
|
-
el("button", { class: "sm", onclick: () => navigator.clipboard.writeText(ser).then(() => { msg.innerHTML = `<span class="s"
|
|
978
|
+
el("button", { class: "sm", onclick: () => navigator.clipboard.writeText(ser).then(() => { msg.innerHTML = `<span class="s">${esc(t("copied"))}</span>`; }) }, "Copy"),
|
|
910
979
|
el("button", { class: "sm", onclick: () => { const blob = new Blob([toYaml(fleetModel())], { type: "text/plain" }); const a = el("a", { href: URL.createObjectURL(blob), download: "fleet.yaml" }); a.click(); URL.revokeObjectURL(a.href); } }, "⬇ fleet.yaml"),
|
|
911
980
|
);
|
|
912
981
|
host.append(toolbar);
|
|
@@ -922,7 +991,7 @@
|
|
|
922
991
|
}
|
|
923
992
|
// Apply the edited full model: PUT defaults + channels, and diff instances.
|
|
924
993
|
async function applyFullModel(m, msg) {
|
|
925
|
-
if (!m || typeof m !== "object") { msg.innerHTML = `<span class="e">✗
|
|
994
|
+
if (!m || typeof m !== "object") { msg.innerHTML = `<span class="e">✗ ${esc(t("expectedMapping"))}</span>`; return; }
|
|
926
995
|
const results = [];
|
|
927
996
|
if (m.defaults) results.push(await api("/api/settings/fleet/defaults", { method: "PUT", body: JSON.stringify(m.defaults) }));
|
|
928
997
|
const chs = m.channels || (m.channel ? [m.channel] : null);
|
|
@@ -960,7 +1029,7 @@
|
|
|
960
1029
|
async function reload() {
|
|
961
1030
|
// Forms edit the user-authored config, not defaults-expanded runtime values.
|
|
962
1031
|
const f = await api("/api/settings/fleet/raw");
|
|
963
|
-
if (!f.ok) { $("banner").style.display = "block"; $("banner").style.background = "#fef2f2"; $("banner").style.color = "var(--red)"; $("banner").textContent = "
|
|
1032
|
+
if (!f.ok) { $("banner").style.display = "block"; $("banner").style.background = "#fef2f2"; $("banner").style.color = "var(--red)"; $("banner").textContent = t("configLoadFailed"); return; }
|
|
964
1033
|
state.fleet = f.body || {};
|
|
965
1034
|
const c = await api("/api/settings/classic"); state.classic = c.body || {};
|
|
966
1035
|
await reloadLive();
|
|
@@ -979,12 +1048,12 @@
|
|
|
979
1048
|
$("advToggle").onclick = () => { const b = $("advBody"); const open = !b.classList.contains("open"); b.classList.toggle("open", open); $("advToggle").textContent = open ? t("collapse") : t("expand"); };
|
|
980
1049
|
const advancedEnabled = localStorage.getItem("agend_settings_advanced") === "true";
|
|
981
1050
|
document.body.classList.toggle("show-advanced", advancedEnabled);
|
|
982
|
-
$("advancedToggle").textContent = advancedEnabled ? "
|
|
1051
|
+
$("advancedToggle").textContent = advancedEnabled ? t("hideAdvanced") : t("showAdvanced");
|
|
983
1052
|
$("advancedToggle").onclick = () => {
|
|
984
1053
|
const enabled = !document.body.classList.contains("show-advanced");
|
|
985
1054
|
document.body.classList.toggle("show-advanced", enabled);
|
|
986
1055
|
localStorage.setItem("agend_settings_advanced", String(enabled));
|
|
987
|
-
$("advancedToggle").textContent = enabled ? "
|
|
1056
|
+
$("advancedToggle").textContent = enabled ? t("hideAdvanced") : t("showAdvanced");
|
|
988
1057
|
};
|
|
989
1058
|
$("applyChanges").onclick = applyPendingChanges;
|
|
990
1059
|
$("discardChanges").onclick = async () => { state.pending.clear(); updatePendingBar(); await reload(); };
|
package/dist/ui/view.html
CHANGED
|
@@ -129,17 +129,17 @@
|
|
|
129
129
|
</style>
|
|
130
130
|
</head>
|
|
131
131
|
<body>
|
|
132
|
-
<button class="help-btn" id="usageBtn" title="AI usage" style="right:140px; display:none">📊</button>
|
|
133
|
-
<button class="help-btn" id="densityBtn" title="Font density" style="right:98px">⤢</button>
|
|
134
|
-
<button class="help-btn" id="langBtn" title="Language" style="right:56px">🌐</button>
|
|
135
|
-
<button class="help-btn" id="helpBtn" title="Help">ℹ️</button>
|
|
132
|
+
<button class="help-btn" id="usageBtn" data-i18n-title="usageButton" title="AI usage" style="right:140px; display:none">📊</button>
|
|
133
|
+
<button class="help-btn" id="densityBtn" data-i18n-title="densityLabel" title="Font density" style="right:98px">⤢</button>
|
|
134
|
+
<button class="help-btn" id="langBtn" data-i18n-title="languageButton" title="Language" style="right:56px">🌐</button>
|
|
135
|
+
<button class="help-btn" id="helpBtn" data-i18n-title="helpButton" title="Help">ℹ️</button>
|
|
136
136
|
<div class="help-backdrop" id="helpBackdrop"></div>
|
|
137
137
|
<aside class="help-panel" id="helpPanel">
|
|
138
|
-
<button class="close" id="helpClose" title="Close">×</button>
|
|
138
|
+
<button class="close" id="helpClose" data-i18n-title="close" title="Close">×</button>
|
|
139
139
|
<div id="helpContent"></div>
|
|
140
140
|
</aside>
|
|
141
141
|
<aside class="help-panel" id="usagePanel">
|
|
142
|
-
<button class="close" id="usageClose" title="Close">×</button>
|
|
142
|
+
<button class="close" id="usageClose" data-i18n-title="close" title="Close">×</button>
|
|
143
143
|
<div id="usageContent"></div>
|
|
144
144
|
</aside>
|
|
145
145
|
<div id="sidebar"><h1 data-i18n="viewTitle">AgEnD · View</h1><div id="list"></div></div>
|
|
@@ -152,7 +152,7 @@
|
|
|
152
152
|
<div class="meta" id="meta"></div>
|
|
153
153
|
<div class="desc" id="desc"></div>
|
|
154
154
|
</div>
|
|
155
|
-
<button id="cardToggle" title="Expand profile" aria-expanded="false">▲</button>
|
|
155
|
+
<button id="cardToggle" data-i18n-title="expandProfile" title="Expand profile" aria-expanded="false">▲</button>
|
|
156
156
|
<button id="editBtn" data-i18n="editProfile">✏️ Edit</button>
|
|
157
157
|
<div id="edit">
|
|
158
158
|
<input id="fDisplay" data-i18n-ph="displayName" placeholder="Display name">
|
|
@@ -193,7 +193,10 @@
|
|
|
193
193
|
tooltipBackend: "Backend: {value}", tooltipStatus: "Status: {value}", tooltipContext: "Context: {value}",
|
|
194
194
|
tooltipUnavailable: "Unavailable", statusRunning: "Running", statusPaused: "Paused",
|
|
195
195
|
statusStopped: "Stopped", statusCrashed: "Crashed", statusUnknown: "Unknown",
|
|
196
|
-
usageResets: "resets in {t}", usageSoon: "resets soon", usageFail: "Could not load usage data."
|
|
196
|
+
usageResets: "resets in {t}", usageSoon: "resets soon", usageFail: "Could not load usage data.",
|
|
197
|
+
usageButton: "AI usage", languageButton: "Language", helpButton: "Help", close: "Close", expandProfile: "Expand profile",
|
|
198
|
+
rosterFailed: "Failed to load roster: {error}\n(Is the fleet running?)", paneFailed: "⚠ Could not load pane for {name} (HTTP {status}).",
|
|
199
|
+
tokenRequired: "web.token required", avatarFailed: "Avatar upload failed: {error}", saved: "Saved", saveFailed: "Save failed: {error}" },
|
|
197
200
|
"zh-TW": { viewTitle: "AgEnD · 檢視", editProfile: "✏️ 編輯", save: "儲存", cancel: "取消",
|
|
198
201
|
displayName: "顯示名稱", role: "角色", description: "描述", tokenPh: "web.token(儲存時需要)",
|
|
199
202
|
saveHint: "檢視為公開(唯讀)。儲存需要 web.token(~/.agend/web.token)。", helpTitle: "📖 使用說明",
|
|
@@ -208,14 +211,19 @@
|
|
|
208
211
|
tooltipBackend: "後端:{value}", tooltipStatus: "狀態:{value}", tooltipContext: "上下文:{value}",
|
|
209
212
|
tooltipUnavailable: "無資料", statusRunning: "運行中", statusPaused: "已暫停",
|
|
210
213
|
statusStopped: "已停止", statusCrashed: "已崩潰", statusUnknown: "未知",
|
|
211
|
-
usageResets: "{t} 後重置", usageSoon: "即將重置", usageFail: "無法載入用量資料。"
|
|
214
|
+
usageResets: "{t} 後重置", usageSoon: "即將重置", usageFail: "無法載入用量資料。",
|
|
215
|
+
usageButton: "AI 訂閱用量", languageButton: "語言", helpButton: "使用說明", close: "關閉", expandProfile: "展開個人資料",
|
|
216
|
+
rosterFailed: "載入 instance 清單失敗:{error}\n(Fleet 是否正在執行?)", paneFailed: "⚠ 無法載入 {name} 的 pane(HTTP {status})。",
|
|
217
|
+
tokenRequired: "需要 web.token", avatarFailed: "上傳頭像失敗:{error}", saved: "已儲存", saveFailed: "儲存失敗:{error}" },
|
|
212
218
|
};
|
|
213
219
|
let lang = localStorage.getItem("agend_lang") || (navigator.language && navigator.language.toLowerCase().startsWith("zh") ? "zh-TW" : "en");
|
|
214
220
|
if (!I18N[lang]) lang = "en";
|
|
215
221
|
const T = (k) => (I18N[lang] && I18N[lang][k]) || I18N.en[k] || k;
|
|
222
|
+
const TF = (k, values = {}) => Object.entries(values).reduce((s, [name, value]) => s.replaceAll(`{${name}}`, String(value)), T(k));
|
|
216
223
|
function applyI18n() {
|
|
217
224
|
document.querySelectorAll("[data-i18n]").forEach(e => { e.textContent = T(e.getAttribute("data-i18n")); });
|
|
218
225
|
document.querySelectorAll("[data-i18n-ph]").forEach(e => { e.setAttribute("placeholder", T(e.getAttribute("data-i18n-ph"))); });
|
|
226
|
+
document.querySelectorAll("[data-i18n-title]").forEach(e => { e.setAttribute("title", T(e.getAttribute("data-i18n-title"))); });
|
|
219
227
|
const hc = q("helpContent");
|
|
220
228
|
if (hc) { hc.innerHTML = ""; const h3 = document.createElement("h3"); h3.textContent = T("helpTitle"); hc.append(h3);
|
|
221
229
|
const ul = document.createElement("ul"); for (const k of ["help1","help2","help3","help4","help5"]) { const li = document.createElement("li"); li.textContent = T(k); ul.append(li); } hc.append(ul); }
|
|
@@ -557,7 +565,7 @@
|
|
|
557
565
|
if (!current && roster.length) current = roster[0].instance_name;
|
|
558
566
|
computeOrder();
|
|
559
567
|
renderList(); renderCard();
|
|
560
|
-
} catch (e) { q("pre").textContent = "
|
|
568
|
+
} catch (e) { q("pre").textContent = TF("rosterFailed", { error: e.message }); }
|
|
561
569
|
}
|
|
562
570
|
|
|
563
571
|
// ── Terminal ──────────────────────────────────────────
|
|
@@ -575,7 +583,7 @@
|
|
|
575
583
|
if (!r.ok) {
|
|
576
584
|
// Surface the failure instead of leaving the previous instance's output
|
|
577
585
|
// on screen (which reads as "switching did nothing").
|
|
578
|
-
q("pre").textContent =
|
|
586
|
+
q("pre").textContent = TF("paneFailed", { name: target, status: r.status });
|
|
579
587
|
return;
|
|
580
588
|
}
|
|
581
589
|
const text = await r.text();
|
|
@@ -714,7 +722,7 @@
|
|
|
714
722
|
const it = roster.find(r => r.instance_name === current);
|
|
715
723
|
if (!it) return;
|
|
716
724
|
const wt = q("fToken").value.trim();
|
|
717
|
-
if (!wt) { q("msg").className = "err"; q("msg").textContent = "
|
|
725
|
+
if (!wt) { q("msg").className = "err"; q("msg").textContent = T("tokenRequired"); return; }
|
|
718
726
|
localStorage.setItem("agend_web_token", wt);
|
|
719
727
|
const name = encodeURIComponent(current);
|
|
720
728
|
try {
|
|
@@ -727,12 +735,12 @@
|
|
|
727
735
|
const file = q("fAvatar").files[0];
|
|
728
736
|
if (file) {
|
|
729
737
|
const ar = await fetch(`/api/avatar/${name}`, { method: "POST", headers: { "Content-Type": file.type, "X-Agend-Token": wt }, body: file });
|
|
730
|
-
if (!ar.ok) { const e = await ar.json().catch(() => ({})); throw new Error("
|
|
738
|
+
if (!ar.ok) { const e = await ar.json().catch(() => ({})); throw new Error(TF("avatarFailed", { error: e.error || ar.status })); }
|
|
731
739
|
}
|
|
732
|
-
q("msg").className = "ok"; q("msg").textContent = "
|
|
740
|
+
q("msg").className = "ok"; q("msg").textContent = T("saved");
|
|
733
741
|
await loadRoster();
|
|
734
742
|
setTimeout(exitEdit, 500);
|
|
735
|
-
} catch (e) { q("msg").className = "err"; q("msg").textContent = e.message; }
|
|
743
|
+
} catch (e) { q("msg").className = "err"; q("msg").textContent = e.message.startsWith(T("avatarFailed").split("{")[0]) ? e.message : TF("saveFailed", { error: e.message }); }
|
|
736
744
|
}
|
|
737
745
|
|
|
738
746
|
q("editBtn").onclick = enterEdit;
|
|
@@ -62,6 +62,12 @@ export declare function resolveClaudeAuth(file: {
|
|
|
62
62
|
* even live?".
|
|
63
63
|
*/
|
|
64
64
|
export declare function statuslineUsage(reading: StatuslineRateLimits, plan: string | null, now?: number): Omit<ProviderUsage, "id" | "name">;
|
|
65
|
+
/**
|
|
66
|
+
* Best-effort OAuth access token for direct Anthropic API calls (the /model
|
|
67
|
+
* catalog probe). Same resolution order as the usage panel: credentials file →
|
|
68
|
+
* CLAUDE_CODE_OAUTH_TOKEN → shell rc. Null when nothing usable exists.
|
|
69
|
+
*/
|
|
70
|
+
export declare function getClaudeOAuthToken(): Promise<string | null>;
|
|
65
71
|
/** Exported for tests — the statusline/API preference order is the subject. */
|
|
66
72
|
export declare function fetchClaudeUsage(): Promise<Omit<ProviderUsage, "id" | "name">>;
|
|
67
73
|
interface AgyBucket {
|
|
@@ -98,6 +104,7 @@ interface AgyStoredToken {
|
|
|
98
104
|
* uses) is read alongside `expiry` (RFC3339) for the same reason.
|
|
99
105
|
*/
|
|
100
106
|
export declare function readAgyToken(file: unknown): AgyStoredToken;
|
|
107
|
+
export declare function fetchCodexUsage(): Promise<Omit<ProviderUsage, "id" | "name">>;
|
|
101
108
|
interface KiroToken {
|
|
102
109
|
access_token?: string;
|
|
103
110
|
expires_at?: string;
|