@symerian/symi 3.4.30 → 3.4.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/dist/{agent-O3PihJOY.js → agent-ZFfYdexc.js} +10 -10
- package/dist/{agents-C2MpIr16.js → agents-C8naPegc.js} +5 -5
- package/dist/{audit-yf3H4Y1h.js → audit-DOfexJi4.js} +8 -8
- package/dist/{auth-choice-CyBcsQ9T.js → auth-choice-DxH-c3vK.js} +4 -4
- package/dist/{banner-D7__HlM-.js → banner-D7zYLyA8.js} +1 -1
- package/dist/{browser-cli-B_6WWHm_.js → browser-cli-MYIql1Ic.js} +3 -3
- package/dist/build-info.json +1 -1
- package/dist/bundled/boot-md/handler.js +4 -4
- package/dist/bundled/session-memory/handler.js +4 -4
- package/dist/{call-IfleM0ap.js → call-D9U2Sfhp.js} +1 -1
- package/dist/{channel-options-B5974x-7.js → channel-options-9PzSNQLG.js} +1 -1
- package/dist/{channels-cli-C6Nv9mzy.js → channels-cli-C3XnNkE5.js} +30 -30
- package/dist/{chrome-DT1fIVG1.js → chrome-B4P7ycw5.js} +7 -7
- package/dist/{chrome-DJCkCRLf.js → chrome-ClVIwINy.js} +1 -1
- package/dist/{cli-DZoms4qg.js → cli-DhTdzhjk.js} +26 -26
- package/dist/{command-registry--D98SJ6k.js → command-registry-BOwku-e8.js} +11 -11
- package/dist/{completion-cli-CHYvpIPw.js → completion-cli-KZYbGY4H.js} +2 -2
- package/dist/{config-BTSBEAnk.js → config-Byd2Y9rr.js} +5 -5
- package/dist/{config-cli-DstpcB33.js → config-cli-CJ1CRfG8.js} +5 -5
- package/dist/{config-guard-CCJrDmON.js → config-guard-zt0zhFHl.js} +2 -2
- package/dist/{config-validation-YCrMlM9Z.js → config-validation-CRazk6Kd.js} +1 -1
- package/dist/{configure-B-UvSZZ7.js → configure-CFY_XEps.js} +10 -10
- package/dist/{control-service-CwRG4M_O.js → control-service-DN6-IdFk.js} +4 -4
- package/dist/{cron-cli-DUETe3Of.js → cron-cli-fmiz43Pz.js} +3 -3
- package/dist/{daemon-cli-Dy5eY4M7.js → daemon-cli-BilAL40g.js} +6 -6
- package/dist/{daemon-runtime-ChztAKDA.js → daemon-runtime-D4KHzQq2.js} +1 -1
- package/dist/{deliver-59sRVaYQ.js → deliver-BkCYBlzi.js} +4 -4
- package/dist/{deliver-BHmK4isn.js → deliver-CnsfN7km.js} +1 -1
- package/dist/{devices-cli-BCZ2HHjT.js → devices-cli-D7HdOwRI.js} +2 -2
- package/dist/{directory-cli-BVGd0l0T.js → directory-cli-ed6QacBC.js} +1 -1
- package/dist/{dns-cli-DH-ksmiI.js → dns-cli-RPa1dGTQ.js} +1 -1
- package/dist/{docs-cli-BuX8FDdh.js → docs-cli-ZTdS_Gak.js} +2 -2
- package/dist/{doctor-completion-C4Sf8CFV.js → doctor-completion-DpGX8dau.js} +1 -1
- package/dist/{doctor-config-flow-BhmF6EAq.js → doctor-config-flow-CIrG_yW_.js} +4 -4
- package/dist/entry.js +3 -3
- package/dist/{exec-approvals-cli-CyXTXgft.js → exec-approvals-cli-65pcsa1y.js} +6 -6
- package/dist/{frontmatter-BH3ExkUY.js → frontmatter-B4levtVg.js} +2 -2
- package/dist/{gateway-cli-fT43bqlv.js → gateway-cli-CTi7Wl2k.js} +50 -50
- package/dist/{gateway-rpc-BEqCo2JK.js → gateway-rpc-2VPLhMhx.js} +1 -1
- package/dist/{glass-ui-ws-BfMKuf9v.js → glass-ui-ws-Bah_A-jn.js} +38 -38
- package/dist/{gmail-setup-utils-Cz9Tb30q.js → gmail-setup-utils-C9WBC6Cd.js} +1 -1
- package/dist/{health-DDxrkioj.js → health-B5rRzhyV.js} +3 -3
- package/dist/{hooks-cli-Btgk__9z.js → hooks-cli-CSDCGO-W.js} +29 -29
- package/dist/{hooks-status-BaV4_YaJ.js → hooks-status-OorbLcc2.js} +2 -2
- package/dist/index.js +0 -0
- package/dist/{lifecycle-core-B0tG8ERp.js → lifecycle-core-BRc-XqTk.js} +2 -2
- package/dist/llm-slug-generator.js +4 -4
- package/dist/{logs-cli-CUhZrDw7.js → logs-cli-D5phVUOT.js} +3 -3
- package/dist/{manager-BeNnzSJv.js → manager-BfhHvsI9.js} +1 -1
- package/dist/{manager-Di9qtuZF.js → manager-DzBH9uQG.js} +1 -1
- package/dist/{memory-BDeydRz3.js → memory-BCj0cj5v.js} +3 -3
- package/dist/{memory-cli-D4hZiGr5.js → memory-cli-9WUMBHol.js} +3 -3
- package/dist/{model-catalog-DPnCNs5N.js → model-catalog-CD2kTz4V.js} +2 -2
- package/dist/{model-picker-DuPommMc.js → model-picker-CVd5UuXV.js} +2 -2
- package/dist/{models-cli-CL5K06Wu.js → models-cli-rd4eX_6P.js} +30 -30
- package/dist/{models-config-CPlvO1b0.js → models-config-oxaGiRUT.js} +1 -1
- package/dist/{models-B0wNKA18.js → models-sbKIrJBG.js} +9 -9
- package/dist/{node-cli-CWWMajKF.js → node-cli-ClLXSdFL.js} +9 -9
- package/dist/{nodes-cli-YmE7S-JB.js → nodes-cli-DY38p5In.js} +3 -3
- package/dist/{onboard-HqUZVKyD.js → onboard-BAyp_UFm.js} +7 -7
- package/dist/{onboard-channels-BbF5-N3g.js → onboard-channels-CUpkV_IJ.js} +1 -1
- package/dist/{onboard-custom-63SI3jrk.js → onboard-custom-BMgLK-vo.js} +2 -2
- package/dist/{onboard-helpers-Cb3WFLl9.js → onboard-helpers-DIQjpQ-s.js} +2 -2
- package/dist/{onboard-hooks-DXXg3YH9.js → onboard-hooks-KZKmj5Dk.js} +3 -3
- package/dist/{onboard-remote-CqtCrqtm.js → onboard-remote-C-_NUgBl.js} +1 -1
- package/dist/{onboard-skills-CS3l5fhH.js → onboard-skills-CwvicFTi.js} +3 -3
- package/dist/{onboarding-Be_HJIFA.js → onboarding-BxCNNo75.js} +11 -11
- package/dist/{onboarding.finalize-dsCUTKCQ.js → onboarding.finalize-DgMPXTtO.js} +18 -18
- package/dist/{onboarding.gateway-config-Ctd1_QW4.js → onboarding.gateway-config-BAaWvPSX.js} +4 -4
- package/dist/{outbound-send-deps-DPsU_Xdy.js → outbound-send-deps-qhFaNYAY.js} +1 -1
- package/dist/{pairing-cli-PtlNv7Gr.js → pairing-cli-CFkiSx8t.js} +1 -1
- package/dist/{pi-embedded-helpers-CVKIHnFv.js → pi-embedded-helpers-CRG5LMS7.js} +1 -1
- package/dist/{pi-tools.policy-8_wTu7v2.js → pi-tools.policy-De6xHjVL.js} +2 -2
- package/dist/{plugin-registry-BG51uZOA.js → plugin-registry-BNe5jtPK.js} +2 -2
- package/dist/plugin-sdk/index.js +6 -6
- package/dist/{plugins-cli-B-zVOiQN.js → plugins-cli-CWj33_Hj.js} +27 -27
- package/dist/{program-BOh0XGsA.js → program-Z2OLlhsB.js} +33 -33
- package/dist/{prompt-select-styled-Bj-hrPCx.js → prompt-select-styled-BNwbBBcH.js} +16 -16
- package/dist/{provider-auth-helpers-BK6NFk2Q.js → provider-auth-helpers-5qb15Hje.js} +2 -2
- package/dist/{push-apns-CMxk3Hxf.js → push-apns-D6qV5P4h.js} +1 -1
- package/dist/{pw-ai-C5MJKzUM.js → pw-ai-BsEf8C15.js} +1 -1
- package/dist/{pw-ai-CE2TBGif.js → pw-ai-C7mvVllB.js} +2 -2
- package/dist/{qr-cli-BeHlC33g.js → qr-cli-oENcXq8d.js} +1 -1
- package/dist/{redact-identifier-B1VHIbnd.js → redact-identifier-ZE8OIUof.js} +1 -1
- package/dist/{register.agent-COv5OPLQ.js → register.agent-DpiHPMca.js} +36 -36
- package/dist/{register.configure-RhKVlfWH.js → register.configure-DRFaroYF.js} +40 -40
- package/dist/{register.maintenance-BAK-DiQY.js → register.maintenance-BGNNTt_1.js} +38 -38
- package/dist/{register.message-CCBmn5ja.js → register.message-R4AIHhlU.js} +28 -28
- package/dist/{register.onboard-B3jO_Goy.js → register.onboard-Cl-_Suyv.js} +13 -13
- package/dist/{register.setup-CYSISTDO.js → register.setup-Dt98v2V3.js} +15 -15
- package/dist/{register.status-health-sessions-CGbg9k6d.js → register.status-health-sessions-DiX5lVyr.js} +23 -23
- package/dist/{register.subclis-loifbhVx.js → register.subclis-DVObuR_P.js} +28 -28
- package/dist/{replies-Dj640LxQ.js → replies-VTJu1jUK.js} +1 -1
- package/dist/{routes-B5y-oAKp.js → routes-DOFg6X5M.js} +3 -3
- package/dist/{rpc-qWm1bRo5.js → rpc-BIHKXdlN.js} +1 -1
- package/dist/{run-main-B7Ria3zD.js → run-main-NAgYKzSu.js} +44 -44
- package/dist/{sandbox-BJgHc4Vu.js → sandbox-EB7cFSML.js} +6 -6
- package/dist/{sandbox-cli-CSudrHYL.js → sandbox-cli-D4dy6sUK.js} +8 -8
- package/dist/{security-cli-B2lCR2xX.js → security-cli-DwCKOrFm.js} +11 -11
- package/dist/{send-CY9na-Jw.js → send-CkRtuQbr.js} +1 -1
- package/dist/{server-context-DdWWaDf6.js → server-context-ww1jytet.js} +5 -5
- package/dist/{server-methods-Dk_EnINI.js → server-methods-BDiZyPvS.js} +23 -23
- package/dist/{server-node-events-BHegSgG9.js → server-node-events-9cY40B4m.js} +29 -29
- package/dist/{session-utils-CAVPV2Hh.js → session-utils-CStjqGE6.js} +3 -3
- package/dist/{sessions-BDM9kNY0.js → sessions-BSRHJH8T.js} +3 -3
- package/dist/{sessions-CqLQkkWg.js → sessions-CInbrqsF.js} +1 -1
- package/dist/{shared-CpwtQRJB.js → shared-DEAo94kI.js} +1 -1
- package/dist/{skill-commands-CRQniaHy.js → skill-commands-BkKfrbHt.js} +2 -2
- package/dist/{skills-CTcjFd_F.js → skills-CFLijAZ-.js} +1 -1
- package/dist/{skills-cli-9Mlb7VRg.js → skills-cli-BbagnO6p.js} +5 -5
- package/dist/{skills-install-D5ikt72d.js → skills-install-dLRO1KGp.js} +2 -2
- package/dist/{skills-remote-BASG1Doo.js → skills-remote-DQVB-3Pj.js} +1 -1
- package/dist/{skills-status-1iVfuVIG.js → skills-status-HWybfj0K.js} +2 -2
- package/dist/{status-Bci6O3aA.js → status-BSrVEUoJ.js} +2 -2
- package/dist/{status-D5RhVO0A.js → status-FIp7UKru.js} +12 -12
- package/dist/{status.update-D5CachQ_.js → status.update-Cm8Fm8QD.js} +1 -1
- package/dist/{subagent-registry-CsXaK_sy.js → subagent-registry-DqryocLT.js} +31 -31
- package/dist/{synthesis-dplLmFIP.js → synthesis-CS-lrEGy.js} +4 -4
- package/dist/{synthesis-BoZf8BeO.js → synthesis-DKhH0YwU.js} +26 -26
- package/dist/{system-cli-D5FffFX5.js → system-cli-DYSI6cIB.js} +3 -3
- package/dist/{systemd-hints-CufcSfMk.js → systemd-hints-EIfW5y8K.js} +1 -1
- package/dist/{tui-DZ8gksSZ.js → tui-C20rHOeB.js} +4 -4
- package/dist/{tui-cli-CyBJYh9u.js → tui-cli-JwlzHCza.js} +11 -11
- package/dist/{unified-runner-BFLLzEHE.js → unified-runner-AM9tFC5q.js} +10 -10
- package/dist/{update-cli-yW6Xl6fs.js → update-cli-BYfA3RPc.js} +41 -41
- package/dist/{update-runner-CIgLduhH.js → update-runner-CBG8p9iG.js} +1 -1
- package/dist/{webhooks-cli-4bmyKzwb.js → webhooks-cli-MuG_QGLp.js} +4 -4
- package/dist/{with-timeout-bWr6yBeX.js → with-timeout-BvxaeAo6.js} +1 -1
- package/dist/{workspace-DxscDsm6.js → workspace-D0d7Gi4-.js} +1 -1
- package/extensions/memory-core/node_modules/.bin/symi +0 -0
- package/extensions/msteams/node_modules/.bin/symi +0 -0
- package/extensions/outlook/node_modules/.bin/symi +0 -0
- package/extensions/slack/node_modules/.bin/symi +0 -0
- package/package.json +108 -79
- package/dist/control-ui/css/revert-red-theme.md +0 -141
- package/dist/control-ui/css/style.css +0 -5843
- package/dist/control-ui/css/style.css.backup-2026-03-03-162525 +0 -3546
- package/dist/control-ui/css/style.css.backup-before-red-2026-03-03-162525 +0 -3546
- package/dist/control-ui/css/style.css.backup-before-red-theme-2026-03-03-162530 +0 -3546
- package/dist/control-ui/css/style.css.pre-2row +0 -2165
- package/dist/control-ui/css/style.css.pre-brand +0 -1776
- package/dist/control-ui/css/style.css.pre-history +0 -1974
- package/dist/control-ui/css/style.css.pre-nav +0 -2264
- package/dist/control-ui/css/style.css.pre-newsession +0 -1898
- package/dist/control-ui/css/style.css.pre-queue +0 -2195
- package/dist/control-ui/css/style.css.pre-red-prompt +0 -2524
- package/dist/control-ui/css/style.css.pre-stop +0 -2239
- package/dist/control-ui/css/style.css.pre-textarea +0 -2184
- package/dist/control-ui/css/style.css.pre-watchdog +0 -1848
- package/dist/control-ui/css/style.css.red-theme +0 -2999
- package/dist/control-ui/index.html +0 -1042
- package/dist/control-ui/js/app.js +0 -1304
- package/dist/control-ui/js/app.js.pre-2row +0 -463
- package/dist/control-ui/js/app.js.pre-heartbeat-filter +0 -595
- package/dist/control-ui/js/app.js.pre-newsession +0 -408
- package/dist/control-ui/js/app.js.pre-queue +0 -476
- package/dist/control-ui/js/app.js.pre-stop +0 -564
- package/dist/control-ui/js/app.js.pre-textarea +0 -467
- package/dist/control-ui/js/app.js.pre-watchdog +0 -293
- package/dist/control-ui/js/connections.js +0 -438
- package/dist/control-ui/js/gateway.js +0 -233
- package/dist/control-ui/js/gateway.js.pre-stop +0 -110
- package/dist/control-ui/js/history.js +0 -732
- package/dist/control-ui/js/logs.js +0 -238
- package/dist/control-ui/js/menu.js +0 -230
- package/dist/control-ui/js/menu.js.pre-nav +0 -66
- package/dist/control-ui/js/metrics.js +0 -53
- package/dist/control-ui/js/models.js +0 -138
- package/dist/control-ui/js/render.js +0 -882
- package/dist/control-ui/js/render.test.js +0 -112
- package/dist/control-ui/js/scheduling.js +0 -461
- package/dist/control-ui/js/settings.js +0 -909
- package/dist/control-ui/js/slash-autocomplete.js +0 -168
- package/dist/control-ui/js/subagents.js +0 -560
- package/dist/control-ui/js/utils.js +0 -29
- package/dist/control-ui/vendor/highlight.min.js +0 -2518
- package/dist/control-ui/vendor/marked.min.js +0 -69
|
@@ -1,909 +0,0 @@
|
|
|
1
|
-
// ── Native Settings Panels ────────────────────────────────────────────
|
|
2
|
-
// Renders Config, Debug, and Logs natively inside the page overlay.
|
|
3
|
-
// No iframe, no SPA proxy — uses gateway RPC over WebSocket.
|
|
4
|
-
|
|
5
|
-
// ── Shared helpers ────────────────────────────────────────────────────
|
|
6
|
-
|
|
7
|
-
function escapeSettingsHtml(s) {
|
|
8
|
-
if (!s) {
|
|
9
|
-
return "";
|
|
10
|
-
}
|
|
11
|
-
return String(s).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/** Create or get the native content container inside the page overlay. */
|
|
15
|
-
function getSettingsContainer() {
|
|
16
|
-
let el = document.getElementById("native-settings-container");
|
|
17
|
-
if (!el) {
|
|
18
|
-
const overlay = document.querySelector("body > .page-overlay");
|
|
19
|
-
if (!overlay) {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
el = document.createElement("div");
|
|
23
|
-
el.id = "native-settings-container";
|
|
24
|
-
overlay.appendChild(el);
|
|
25
|
-
}
|
|
26
|
-
return el;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Show native container, hide iframe. */
|
|
30
|
-
function showSettingsContainer() {
|
|
31
|
-
const container = getSettingsContainer();
|
|
32
|
-
const frame = document.getElementById("page-overlay-frame");
|
|
33
|
-
if (frame) {
|
|
34
|
-
frame.style.display = "none";
|
|
35
|
-
}
|
|
36
|
-
if (container) {
|
|
37
|
-
container.style.display = "flex";
|
|
38
|
-
}
|
|
39
|
-
return container;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** Hide native container, restore iframe. */
|
|
43
|
-
function hideSettingsContainer() {
|
|
44
|
-
const container = document.getElementById("native-settings-container");
|
|
45
|
-
if (container) {
|
|
46
|
-
container.style.display = "none";
|
|
47
|
-
container.innerHTML = "";
|
|
48
|
-
}
|
|
49
|
-
const frame = document.getElementById("page-overlay-frame");
|
|
50
|
-
if (frame) {
|
|
51
|
-
frame.style.display = "";
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// ══════════════════════════════════════════════════════════════════════
|
|
56
|
-
// CONFIG PANEL
|
|
57
|
-
// ══════════════════════════════════════════════════════════════════════
|
|
58
|
-
|
|
59
|
-
let configData = null;
|
|
60
|
-
let configBaseHash = null;
|
|
61
|
-
let configEditMode = false;
|
|
62
|
-
|
|
63
|
-
async function fetchConfig() {
|
|
64
|
-
if (!window.gateway?.connected) {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
try {
|
|
68
|
-
const result = await window.gateway.rpc("config.get", {});
|
|
69
|
-
return result;
|
|
70
|
-
} catch (err) {
|
|
71
|
-
console.error("[settings:config] fetch error:", err);
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function renderConfigValue(key, value, depth) {
|
|
77
|
-
const indent = depth * 1;
|
|
78
|
-
if (value === null || value === undefined) {
|
|
79
|
-
return `<span class="cfg-null">null</span>`;
|
|
80
|
-
}
|
|
81
|
-
if (typeof value === "boolean") {
|
|
82
|
-
return `<span class="cfg-bool">${value}</span>`;
|
|
83
|
-
}
|
|
84
|
-
if (typeof value === "number") {
|
|
85
|
-
return `<span class="cfg-num">${value}</span>`;
|
|
86
|
-
}
|
|
87
|
-
if (typeof value === "string") {
|
|
88
|
-
if (value.length > 120) {
|
|
89
|
-
return `<span class="cfg-str">"${escapeSettingsHtml(value.slice(0, 120))}..."</span>`;
|
|
90
|
-
}
|
|
91
|
-
return `<span class="cfg-str">"${escapeSettingsHtml(value)}"</span>`;
|
|
92
|
-
}
|
|
93
|
-
if (Array.isArray(value)) {
|
|
94
|
-
if (value.length === 0) {
|
|
95
|
-
return `<span class="cfg-bracket">[]</span>`;
|
|
96
|
-
}
|
|
97
|
-
const items = value
|
|
98
|
-
.map(
|
|
99
|
-
(v, i) =>
|
|
100
|
-
`<div class="cfg-row" style="padding-left:${indent + 1}em">${renderConfigValue(i, v, depth + 1)}</div>`,
|
|
101
|
-
)
|
|
102
|
-
.join("");
|
|
103
|
-
return `<span class="cfg-bracket">[</span>${items}<div style="padding-left:${indent}em"><span class="cfg-bracket">]</span></div>`;
|
|
104
|
-
}
|
|
105
|
-
if (typeof value === "object") {
|
|
106
|
-
const entries = Object.entries(value);
|
|
107
|
-
if (entries.length === 0) {
|
|
108
|
-
return `<span class="cfg-bracket">{}</span>`;
|
|
109
|
-
}
|
|
110
|
-
const rows = entries
|
|
111
|
-
.map(
|
|
112
|
-
([k, v]) =>
|
|
113
|
-
`<div class="cfg-row" style="padding-left:${indent + 1}em"><span class="cfg-key">${escapeSettingsHtml(k)}</span><span class="cfg-colon">: </span>${renderConfigValue(k, v, depth + 1)}</div>`,
|
|
114
|
-
)
|
|
115
|
-
.join("");
|
|
116
|
-
return `<span class="cfg-bracket">{</span>${rows}<div style="padding-left:${indent}em"><span class="cfg-bracket">}</span></div>`;
|
|
117
|
-
}
|
|
118
|
-
return escapeSettingsHtml(String(value));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function renderConfigSection(key, value, isRedacted) {
|
|
122
|
-
const isExpanded = typeof value === "object" && value !== null;
|
|
123
|
-
const icon = isExpanded ? "▾" : "▸";
|
|
124
|
-
const redactedBadge = isRedacted ? '<span class="cfg-redacted">REDACTED</span>' : "";
|
|
125
|
-
|
|
126
|
-
return `
|
|
127
|
-
<div class="settings-section" data-key="${escapeSettingsHtml(key)}">
|
|
128
|
-
<div class="settings-section-header" onclick="this.parentElement.classList.toggle('collapsed')">
|
|
129
|
-
<span class="settings-section-arrow">${icon}</span>
|
|
130
|
-
<span class="settings-section-key">${escapeSettingsHtml(key)}</span>
|
|
131
|
-
${redactedBadge}
|
|
132
|
-
</div>
|
|
133
|
-
<div class="settings-section-body">
|
|
134
|
-
${renderConfigValue(key, value, 0)}
|
|
135
|
-
</div>
|
|
136
|
-
</div>
|
|
137
|
-
`;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
async function openConfigPanel() {
|
|
141
|
-
const container = showSettingsContainer();
|
|
142
|
-
if (!container) {
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
container.innerHTML = `
|
|
147
|
-
<div class="settings-panel">
|
|
148
|
-
<div class="settings-toolbar">
|
|
149
|
-
<div class="settings-toolbar-left">
|
|
150
|
-
<span class="settings-toolbar-label">SYSTEM CONFIGURATION</span>
|
|
151
|
-
</div>
|
|
152
|
-
<div class="settings-toolbar-right">
|
|
153
|
-
<button class="settings-btn" id="config-refresh-btn" title="Refresh">Refresh</button>
|
|
154
|
-
<button class="settings-btn settings-btn--primary" id="config-edit-btn" title="Edit raw JSON">Edit</button>
|
|
155
|
-
</div>
|
|
156
|
-
</div>
|
|
157
|
-
<div class="settings-content" id="config-content">
|
|
158
|
-
<div class="settings-loading">Loading configuration...</div>
|
|
159
|
-
</div>
|
|
160
|
-
</div>
|
|
161
|
-
`;
|
|
162
|
-
|
|
163
|
-
// Wire refresh
|
|
164
|
-
document
|
|
165
|
-
.getElementById("config-refresh-btn")
|
|
166
|
-
.addEventListener("click", () => void loadConfigView());
|
|
167
|
-
document.getElementById("config-edit-btn").addEventListener("click", () => toggleConfigEditor());
|
|
168
|
-
|
|
169
|
-
await loadConfigView();
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
async function loadConfigView() {
|
|
173
|
-
const content = document.getElementById("config-content");
|
|
174
|
-
if (!content) {
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
content.innerHTML = '<div class="settings-loading">Loading...</div>';
|
|
179
|
-
const result = await fetchConfig();
|
|
180
|
-
|
|
181
|
-
if (!result || !result.config) {
|
|
182
|
-
content.innerHTML =
|
|
183
|
-
'<div class="settings-empty">Unable to load configuration. Gateway may not be connected.</div>';
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
configData = result.config;
|
|
188
|
-
configBaseHash = result.hash;
|
|
189
|
-
configEditMode = false;
|
|
190
|
-
|
|
191
|
-
// Render tree view
|
|
192
|
-
const redacted = new Set(result.redactedPaths || []);
|
|
193
|
-
const sections = Object.entries(configData);
|
|
194
|
-
|
|
195
|
-
if (sections.length === 0) {
|
|
196
|
-
content.innerHTML = '<div class="settings-empty">Configuration is empty.</div>';
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
content.innerHTML = sections
|
|
201
|
-
.map(([key, value]) => {
|
|
202
|
-
const isRedacted = redacted.has(key);
|
|
203
|
-
return renderConfigSection(key, value, isRedacted);
|
|
204
|
-
})
|
|
205
|
-
.join("");
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function toggleConfigEditor() {
|
|
209
|
-
const content = document.getElementById("config-content");
|
|
210
|
-
const editBtn = document.getElementById("config-edit-btn");
|
|
211
|
-
if (!content) {
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (configEditMode) {
|
|
216
|
-
// Switch back to tree view
|
|
217
|
-
configEditMode = false;
|
|
218
|
-
editBtn.textContent = "Edit";
|
|
219
|
-
editBtn.classList.remove("settings-btn--active");
|
|
220
|
-
void loadConfigView();
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Switch to editor
|
|
225
|
-
configEditMode = true;
|
|
226
|
-
editBtn.textContent = "View";
|
|
227
|
-
editBtn.classList.add("settings-btn--active");
|
|
228
|
-
|
|
229
|
-
const json = JSON.stringify(configData, null, 2);
|
|
230
|
-
content.innerHTML = `
|
|
231
|
-
<div class="config-editor-wrap">
|
|
232
|
-
<textarea class="config-editor" id="config-editor-textarea" spellcheck="false">${escapeSettingsHtml(json)}</textarea>
|
|
233
|
-
<div class="config-editor-actions">
|
|
234
|
-
<span class="config-editor-hint">Edit the JSON above, then save to apply changes.</span>
|
|
235
|
-
<button class="settings-btn settings-btn--danger" id="config-save-btn">Save & Apply</button>
|
|
236
|
-
</div>
|
|
237
|
-
</div>
|
|
238
|
-
`;
|
|
239
|
-
|
|
240
|
-
document.getElementById("config-save-btn").addEventListener("click", saveConfig);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
async function saveConfig() {
|
|
244
|
-
const textarea = document.getElementById("config-editor-textarea");
|
|
245
|
-
const saveBtn = document.getElementById("config-save-btn");
|
|
246
|
-
if (!textarea || !saveBtn) {
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
let parsed;
|
|
251
|
-
try {
|
|
252
|
-
parsed = JSON.parse(textarea.value);
|
|
253
|
-
} catch (err) {
|
|
254
|
-
alert("Invalid JSON: " + err.message);
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
saveBtn.textContent = "Saving...";
|
|
259
|
-
saveBtn.disabled = true;
|
|
260
|
-
|
|
261
|
-
try {
|
|
262
|
-
await window.gateway.rpc("config.apply", {
|
|
263
|
-
raw: parsed,
|
|
264
|
-
baseHash: configBaseHash,
|
|
265
|
-
});
|
|
266
|
-
saveBtn.textContent = "Saved!";
|
|
267
|
-
setTimeout(() => {
|
|
268
|
-
configEditMode = false;
|
|
269
|
-
const editBtn = document.getElementById("config-edit-btn");
|
|
270
|
-
if (editBtn) {
|
|
271
|
-
editBtn.textContent = "Edit";
|
|
272
|
-
editBtn.classList.remove("settings-btn--active");
|
|
273
|
-
}
|
|
274
|
-
void loadConfigView();
|
|
275
|
-
}, 800);
|
|
276
|
-
} catch (err) {
|
|
277
|
-
saveBtn.textContent = "Save & Apply";
|
|
278
|
-
saveBtn.disabled = false;
|
|
279
|
-
alert("Save failed: " + err.message);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// ══════════════════════════════════════════════════════════════════════
|
|
284
|
-
// DEBUG PANEL
|
|
285
|
-
// ══════════════════════════════════════════════════════════════════════
|
|
286
|
-
|
|
287
|
-
let debugEntries = [];
|
|
288
|
-
let debugPaused = false;
|
|
289
|
-
const DEBUG_MAX = 500;
|
|
290
|
-
|
|
291
|
-
function openDebugPanel() {
|
|
292
|
-
const container = showSettingsContainer();
|
|
293
|
-
if (!container) {
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
container.innerHTML = `
|
|
298
|
-
<div class="settings-panel">
|
|
299
|
-
<div class="settings-toolbar">
|
|
300
|
-
<div class="settings-toolbar-left">
|
|
301
|
-
<span class="settings-toolbar-label">DEBUG CONSOLE</span>
|
|
302
|
-
<span class="settings-toolbar-count" id="debug-count">0 events</span>
|
|
303
|
-
</div>
|
|
304
|
-
<div class="settings-toolbar-right">
|
|
305
|
-
<button class="settings-btn" id="debug-pause-btn">Pause</button>
|
|
306
|
-
<button class="settings-btn" id="debug-clear-btn">Clear</button>
|
|
307
|
-
<button class="settings-btn" id="debug-export-btn">Export</button>
|
|
308
|
-
</div>
|
|
309
|
-
</div>
|
|
310
|
-
<div class="settings-content debug-scroll" id="debug-content">
|
|
311
|
-
<div class="settings-empty">Listening for events...</div>
|
|
312
|
-
</div>
|
|
313
|
-
</div>
|
|
314
|
-
`;
|
|
315
|
-
|
|
316
|
-
debugEntries = [];
|
|
317
|
-
debugPaused = false;
|
|
318
|
-
|
|
319
|
-
document.getElementById("debug-pause-btn").addEventListener("click", () => {
|
|
320
|
-
debugPaused = !debugPaused;
|
|
321
|
-
document.getElementById("debug-pause-btn").textContent = debugPaused ? "Resume" : "Pause";
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
document.getElementById("debug-clear-btn").addEventListener("click", () => {
|
|
325
|
-
debugEntries = [];
|
|
326
|
-
renderDebugEntries();
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
document.getElementById("debug-export-btn").addEventListener("click", () => {
|
|
330
|
-
const text = debugEntries.map((e) => `${e.time} [${e.type}] ${e.summary}`).join("\n");
|
|
331
|
-
const blob = new Blob([text], { type: "text/plain" });
|
|
332
|
-
const a = document.createElement("a");
|
|
333
|
-
a.href = URL.createObjectURL(blob);
|
|
334
|
-
a.download = `symi-debug-${new Date().toISOString().slice(0, 19)}.txt`;
|
|
335
|
-
a.click();
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
// Start capturing events. If a previous capture closure is still attached
|
|
339
|
-
// (rapid re-open race), mark it cancelled so its body no-ops before we
|
|
340
|
-
// swap in the new one. Without this, the previous closure keeps reading
|
|
341
|
-
// the old panel's DOM even after it's gone and throws silently.
|
|
342
|
-
if (window.__debugPanelCapture && typeof window.__debugPanelCapture === "function") {
|
|
343
|
-
try {
|
|
344
|
-
window.__debugPanelCapture.__cancelled = true;
|
|
345
|
-
} catch {
|
|
346
|
-
/* non-extensible function — ignore */
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
const capture = function (type, payload) {
|
|
350
|
-
if (capture.__cancelled) {
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
if (debugPaused) {
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
const time = new Date().toLocaleTimeString("en-US", { hour12: false });
|
|
357
|
-
let summary = "";
|
|
358
|
-
try {
|
|
359
|
-
summary =
|
|
360
|
-
typeof payload === "object"
|
|
361
|
-
? JSON.stringify(payload).slice(0, 200)
|
|
362
|
-
: String(payload).slice(0, 200);
|
|
363
|
-
} catch {
|
|
364
|
-
summary = "[unserializable]";
|
|
365
|
-
}
|
|
366
|
-
debugEntries.unshift({ time, type, summary, payload });
|
|
367
|
-
if (debugEntries.length > DEBUG_MAX) {
|
|
368
|
-
debugEntries.length = DEBUG_MAX;
|
|
369
|
-
}
|
|
370
|
-
renderDebugEntries();
|
|
371
|
-
};
|
|
372
|
-
capture.__cancelled = false;
|
|
373
|
-
window.__debugPanelCapture = capture;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
function renderDebugEntries() {
|
|
377
|
-
const content = document.getElementById("debug-content");
|
|
378
|
-
const count = document.getElementById("debug-count");
|
|
379
|
-
if (!content) {
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
if (count) {
|
|
383
|
-
count.textContent = `${debugEntries.length} events`;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
if (debugEntries.length === 0) {
|
|
387
|
-
content.innerHTML = '<div class="settings-empty">Listening for events...</div>';
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
const html = debugEntries
|
|
392
|
-
.map((e) => {
|
|
393
|
-
const typeClass =
|
|
394
|
-
e.type === "error"
|
|
395
|
-
? "debug-type--error"
|
|
396
|
-
: e.type === "chat"
|
|
397
|
-
? "debug-type--chat"
|
|
398
|
-
: e.type === "agent"
|
|
399
|
-
? "debug-type--agent"
|
|
400
|
-
: "debug-type--other";
|
|
401
|
-
return `<div class="debug-entry-row">
|
|
402
|
-
<span class="debug-time">${e.time}</span>
|
|
403
|
-
<span class="debug-type ${typeClass}">${escapeSettingsHtml(e.type)}</span>
|
|
404
|
-
<span class="debug-summary">${escapeSettingsHtml(e.summary)}</span>
|
|
405
|
-
</div>`;
|
|
406
|
-
})
|
|
407
|
-
.join("");
|
|
408
|
-
|
|
409
|
-
content.innerHTML = html;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
function closeDebugPanel() {
|
|
413
|
-
// Cancel any pending capture closure before dropping the reference so
|
|
414
|
-
// in-flight capture() calls short-circuit if they were already scheduled.
|
|
415
|
-
const existing = window.__debugPanelCapture;
|
|
416
|
-
if (existing && typeof existing === "function") {
|
|
417
|
-
try {
|
|
418
|
-
existing.__cancelled = true;
|
|
419
|
-
} catch {
|
|
420
|
-
/* non-extensible — ignore */
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
window.__debugPanelCapture = null;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// ══════════════════════════════════════════════════════════════════════
|
|
427
|
-
// LOGS PANEL (replaces the dead-code logs.js)
|
|
428
|
-
// ══════════════════════════════════════════════════════════════════════
|
|
429
|
-
|
|
430
|
-
const LOGS_POLL_MS = 3000;
|
|
431
|
-
const LOGS_LIMIT = 500;
|
|
432
|
-
const LOGS_MAX_BYTES = 250000;
|
|
433
|
-
|
|
434
|
-
const LEVEL_COLORS = {
|
|
435
|
-
TRACE: "#6b7280",
|
|
436
|
-
DEBUG: "#8b5cf6",
|
|
437
|
-
INFO: "#60a5fa",
|
|
438
|
-
WARN: "#f59e0b",
|
|
439
|
-
ERROR: "#ef4444",
|
|
440
|
-
FATAL: "#dc2626",
|
|
441
|
-
};
|
|
442
|
-
|
|
443
|
-
const LEVEL_ORDER = ["TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"];
|
|
444
|
-
|
|
445
|
-
let logsEntries = [];
|
|
446
|
-
let logsCursor = null;
|
|
447
|
-
let logsTimer = null;
|
|
448
|
-
let logsAutoFollow = true;
|
|
449
|
-
let logsLevelFilters = new Set(LEVEL_ORDER);
|
|
450
|
-
let logsFilterText = "";
|
|
451
|
-
|
|
452
|
-
function parseLogLine(line) {
|
|
453
|
-
try {
|
|
454
|
-
const obj = JSON.parse(line);
|
|
455
|
-
const level = obj._meta?.logLevelName || "INFO";
|
|
456
|
-
const time = obj.time || obj._meta?.date || "";
|
|
457
|
-
const msg = obj["1"] || obj.msg || obj.message || "";
|
|
458
|
-
const sub = obj["0"] || "";
|
|
459
|
-
return { level, time, msg, sub, raw: line, parsed: obj };
|
|
460
|
-
} catch {
|
|
461
|
-
return { level: "INFO", time: "", msg: line, sub: "", raw: line, parsed: null };
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
function getVisibleLogEntries() {
|
|
466
|
-
return logsEntries.filter((e) => {
|
|
467
|
-
if (!logsLevelFilters.has(e.level)) {
|
|
468
|
-
return false;
|
|
469
|
-
}
|
|
470
|
-
if (logsFilterText && !e.raw.toLowerCase().includes(logsFilterText)) {
|
|
471
|
-
return false;
|
|
472
|
-
}
|
|
473
|
-
return true;
|
|
474
|
-
});
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
function renderLogEntries() {
|
|
478
|
-
const el = document.getElementById("logs-entries");
|
|
479
|
-
if (!el) {
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
const visible = getVisibleLogEntries();
|
|
484
|
-
const html = visible
|
|
485
|
-
.map((e) => {
|
|
486
|
-
const color = LEVEL_COLORS[e.level] || "#999";
|
|
487
|
-
const timeStr = e.time ? new Date(e.time).toLocaleTimeString("en-US", { hour12: false }) : "";
|
|
488
|
-
const escapedMsg = escapeSettingsHtml(e.msg).substring(0, 500);
|
|
489
|
-
const escapedSub = escapeSettingsHtml(e.sub);
|
|
490
|
-
return `<div class="log-row">
|
|
491
|
-
<span class="log-time">${timeStr}</span>
|
|
492
|
-
<span class="log-level" style="color:${color}">${e.level.padEnd(5)}</span>
|
|
493
|
-
${escapedSub ? `<span class="log-sub">${escapedSub}</span>` : ""}
|
|
494
|
-
<span class="log-msg">${escapedMsg}</span>
|
|
495
|
-
</div>`;
|
|
496
|
-
})
|
|
497
|
-
.join("");
|
|
498
|
-
|
|
499
|
-
el.innerHTML = html || '<div class="settings-empty">No log entries.</div>';
|
|
500
|
-
|
|
501
|
-
if (logsAutoFollow) {
|
|
502
|
-
const scroll = document.getElementById("logs-scroll-area");
|
|
503
|
-
if (scroll) {
|
|
504
|
-
scroll.scrollTop = scroll.scrollHeight;
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
async function fetchLogs() {
|
|
510
|
-
if (!window.gateway?.connected) {
|
|
511
|
-
return;
|
|
512
|
-
}
|
|
513
|
-
try {
|
|
514
|
-
const params = { limit: LOGS_LIMIT, maxBytes: LOGS_MAX_BYTES };
|
|
515
|
-
if (logsCursor != null) {
|
|
516
|
-
params.cursor = logsCursor;
|
|
517
|
-
}
|
|
518
|
-
const result = await window.gateway.rpc("logs.tail", params);
|
|
519
|
-
if (!result) {
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
if (result.reset) {
|
|
524
|
-
logsEntries = [];
|
|
525
|
-
logsCursor = null;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (result.lines && result.lines.length > 0) {
|
|
529
|
-
const newEntries = result.lines.map(parseLogLine);
|
|
530
|
-
logsEntries.push(...newEntries);
|
|
531
|
-
if (logsEntries.length > 2000) {
|
|
532
|
-
logsEntries = logsEntries.slice(logsEntries.length - 2000);
|
|
533
|
-
}
|
|
534
|
-
renderLogEntries();
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
if (typeof result.cursor === "number") {
|
|
538
|
-
logsCursor = result.cursor;
|
|
539
|
-
}
|
|
540
|
-
} catch (err) {
|
|
541
|
-
console.error("[settings:logs] fetch error:", err);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
function startLogsPolling() {
|
|
546
|
-
logsCursor = null;
|
|
547
|
-
logsEntries = [];
|
|
548
|
-
|
|
549
|
-
if (!window.gateway?.connected) {
|
|
550
|
-
const waitIv = setInterval(() => {
|
|
551
|
-
if (window.gateway?.connected) {
|
|
552
|
-
clearInterval(waitIv);
|
|
553
|
-
void fetchLogs();
|
|
554
|
-
if (logsTimer) {
|
|
555
|
-
clearInterval(logsTimer);
|
|
556
|
-
}
|
|
557
|
-
logsTimer = setInterval(fetchLogs, LOGS_POLL_MS);
|
|
558
|
-
}
|
|
559
|
-
}, 500);
|
|
560
|
-
setTimeout(() => clearInterval(waitIv), 30000);
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
void fetchLogs();
|
|
565
|
-
if (logsTimer) {
|
|
566
|
-
clearInterval(logsTimer);
|
|
567
|
-
}
|
|
568
|
-
logsTimer = setInterval(fetchLogs, LOGS_POLL_MS);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
function stopLogsPolling() {
|
|
572
|
-
if (logsTimer) {
|
|
573
|
-
clearInterval(logsTimer);
|
|
574
|
-
logsTimer = null;
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
function openLogsPanel() {
|
|
579
|
-
const container = showSettingsContainer();
|
|
580
|
-
if (!container) {
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
container.innerHTML = `
|
|
585
|
-
<div class="settings-panel">
|
|
586
|
-
<div class="settings-toolbar">
|
|
587
|
-
<div class="settings-toolbar-left">
|
|
588
|
-
<span class="settings-toolbar-label">SYSTEM LOGS</span>
|
|
589
|
-
<span class="settings-toolbar-count" id="logs-count">0 entries</span>
|
|
590
|
-
</div>
|
|
591
|
-
<div class="settings-toolbar-right">
|
|
592
|
-
<div class="logs-level-filters">
|
|
593
|
-
${LEVEL_ORDER.map(
|
|
594
|
-
(l) => `
|
|
595
|
-
<label class="logs-level-chip" data-level="${l}">
|
|
596
|
-
<input type="checkbox" checked data-level="${l}">
|
|
597
|
-
<span style="color:${LEVEL_COLORS[l]}">${l}</span>
|
|
598
|
-
</label>
|
|
599
|
-
`,
|
|
600
|
-
).join("")}
|
|
601
|
-
</div>
|
|
602
|
-
<input type="text" class="settings-search" id="logs-filter-input" placeholder="Filter..." />
|
|
603
|
-
<label class="settings-checkbox-label">
|
|
604
|
-
<input type="checkbox" id="logs-follow-cb" checked> Follow
|
|
605
|
-
</label>
|
|
606
|
-
<button class="settings-btn" id="logs-export-btn">Export</button>
|
|
607
|
-
</div>
|
|
608
|
-
</div>
|
|
609
|
-
<div class="settings-content logs-scroll" id="logs-scroll-area">
|
|
610
|
-
<div id="logs-entries">
|
|
611
|
-
<div class="settings-loading">Connecting to log stream...</div>
|
|
612
|
-
</div>
|
|
613
|
-
</div>
|
|
614
|
-
</div>
|
|
615
|
-
`;
|
|
616
|
-
|
|
617
|
-
// Wire level filters
|
|
618
|
-
container.querySelectorAll(".logs-level-chip input").forEach((cb) => {
|
|
619
|
-
cb.addEventListener("change", () => {
|
|
620
|
-
const level = cb.dataset.level;
|
|
621
|
-
if (cb.checked) {
|
|
622
|
-
logsLevelFilters.add(level);
|
|
623
|
-
} else {
|
|
624
|
-
logsLevelFilters.delete(level);
|
|
625
|
-
}
|
|
626
|
-
renderLogEntries();
|
|
627
|
-
});
|
|
628
|
-
});
|
|
629
|
-
|
|
630
|
-
// Wire filter text
|
|
631
|
-
document.getElementById("logs-filter-input").addEventListener("input", (e) => {
|
|
632
|
-
logsFilterText = e.target.value.toLowerCase();
|
|
633
|
-
renderLogEntries();
|
|
634
|
-
});
|
|
635
|
-
|
|
636
|
-
// Wire auto-follow
|
|
637
|
-
document.getElementById("logs-follow-cb").addEventListener("change", (e) => {
|
|
638
|
-
logsAutoFollow = e.target.checked;
|
|
639
|
-
if (logsAutoFollow) {
|
|
640
|
-
const scroll = document.getElementById("logs-scroll-area");
|
|
641
|
-
if (scroll) {
|
|
642
|
-
scroll.scrollTop = scroll.scrollHeight;
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
});
|
|
646
|
-
|
|
647
|
-
// Wire export
|
|
648
|
-
document.getElementById("logs-export-btn").addEventListener("click", () => {
|
|
649
|
-
const visible = getVisibleLogEntries();
|
|
650
|
-
const text = visible.map((e) => e.raw).join("\n");
|
|
651
|
-
const blob = new Blob([text], { type: "text/plain" });
|
|
652
|
-
const a = document.createElement("a");
|
|
653
|
-
a.href = URL.createObjectURL(blob);
|
|
654
|
-
a.download = `symi-logs-${new Date().toISOString().slice(0, 19)}.txt`;
|
|
655
|
-
a.click();
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
startLogsPolling();
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
function closeLogsPanel() {
|
|
662
|
-
stopLogsPolling();
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
// ══════════════════════════════════════════════════════════════════════
|
|
666
|
-
// GENERIC RPC LIST PANEL — used by Overview, Channels, Sessions, etc.
|
|
667
|
-
// ══════════════════════════════════════════════════════════════════════
|
|
668
|
-
|
|
669
|
-
const PAGE_RPC = {
|
|
670
|
-
overview: { method: "system.status", label: "SYSTEM OVERVIEW", type: "object" },
|
|
671
|
-
channels: {
|
|
672
|
-
method: "channels.status",
|
|
673
|
-
label: "MESSAGING CHANNELS",
|
|
674
|
-
type: "list",
|
|
675
|
-
key: "channels",
|
|
676
|
-
},
|
|
677
|
-
instances: { method: "system.status", label: "AI INSTANCES", type: "object" },
|
|
678
|
-
sessions: { method: "sessions.list", label: "ACTIVE SESSIONS", type: "list", key: "sessions" },
|
|
679
|
-
usage: { method: "usage.status", label: "USAGE & TOKENS", type: "object" },
|
|
680
|
-
cron: { method: "cron.list", label: "SCHEDULED TASKS", type: "list", key: "jobs" },
|
|
681
|
-
tasks: { method: "tasks.list", label: "BACKGROUND TASKS", type: "list", key: "tasks" },
|
|
682
|
-
agents: { method: "agents.list", label: "AGENT MANAGEMENT", type: "list", key: "agents" },
|
|
683
|
-
skills: { method: "skills.status", label: "AGENT SKILLS", type: "object" },
|
|
684
|
-
nodes: { method: "node.list", label: "CONNECTED DEVICES", type: "list", key: "nodes" },
|
|
685
|
-
};
|
|
686
|
-
|
|
687
|
-
function renderDataCard(key, value) {
|
|
688
|
-
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
689
|
-
const rows = Object.entries(value)
|
|
690
|
-
.map(([k, v]) => {
|
|
691
|
-
const display = v == null ? "—" : JSON.stringify(v);
|
|
692
|
-
return `<div class="data-card-row">
|
|
693
|
-
<span class="data-card-label">${escapeSettingsHtml(k)}</span>
|
|
694
|
-
<span class="data-card-value">${escapeSettingsHtml(display.slice(0, 200))}</span>
|
|
695
|
-
</div>`;
|
|
696
|
-
})
|
|
697
|
-
.join("");
|
|
698
|
-
return `<div class="data-card">
|
|
699
|
-
<div class="data-card-header">${escapeSettingsHtml(String(key))}</div>
|
|
700
|
-
${rows}
|
|
701
|
-
</div>`;
|
|
702
|
-
}
|
|
703
|
-
return `<div class="data-card">
|
|
704
|
-
<div class="data-card-header">${escapeSettingsHtml(String(key))}</div>
|
|
705
|
-
<div class="data-card-row"><span class="data-card-value">${escapeSettingsHtml(value != null ? JSON.stringify(value) : "—")}</span></div>
|
|
706
|
-
</div>`;
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
// Pagination state per generic panel — keyed by the page identifier.
|
|
710
|
-
// Resets when the panel is opened; survives across refresh within one session.
|
|
711
|
-
const GENERIC_PAGE_SIZE = 50;
|
|
712
|
-
const genericShownCount = new Map();
|
|
713
|
-
|
|
714
|
-
function resetGenericPagination(page) {
|
|
715
|
-
genericShownCount.set(page, GENERIC_PAGE_SIZE);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
function bumpGenericPagination(page) {
|
|
719
|
-
const cur = genericShownCount.get(page) ?? GENERIC_PAGE_SIZE;
|
|
720
|
-
genericShownCount.set(page, cur + GENERIC_PAGE_SIZE);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
function renderListData(items, page) {
|
|
724
|
-
if (!items || items.length === 0) {
|
|
725
|
-
return '<div class="settings-empty">No items found.</div>';
|
|
726
|
-
}
|
|
727
|
-
const shown = page ? (genericShownCount.get(page) ?? GENERIC_PAGE_SIZE) : items.length;
|
|
728
|
-
const slice = items.slice(0, shown);
|
|
729
|
-
const html = slice
|
|
730
|
-
.map((item, i) => {
|
|
731
|
-
const title = item.name || item.id || item.sessionKey || item.label || `Item ${i + 1}`;
|
|
732
|
-
return renderDataCard(title, item);
|
|
733
|
-
})
|
|
734
|
-
.join("");
|
|
735
|
-
if (!page || items.length <= shown) {
|
|
736
|
-
return html;
|
|
737
|
-
}
|
|
738
|
-
const remaining = items.length - shown;
|
|
739
|
-
const nextBatch = Math.min(GENERIC_PAGE_SIZE, remaining);
|
|
740
|
-
return `${html}
|
|
741
|
-
<div class="settings-pagination">
|
|
742
|
-
<span class="settings-pagination-info">Showing ${shown} of ${items.length}</span>
|
|
743
|
-
<button class="settings-btn" id="generic-show-more-btn" data-page="${escapeSettingsHtml(page)}">Show ${nextBatch} more</button>
|
|
744
|
-
</div>`;
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
function renderObjectData(data) {
|
|
748
|
-
if (!data || Object.keys(data).length === 0) {
|
|
749
|
-
return '<div class="settings-empty">No data available.</div>';
|
|
750
|
-
}
|
|
751
|
-
return Object.entries(data)
|
|
752
|
-
.map(([key, value]) => {
|
|
753
|
-
if (Array.isArray(value)) {
|
|
754
|
-
return `<div class="data-card">
|
|
755
|
-
<div class="data-card-header">${escapeSettingsHtml(key)} <span class="data-card-badge">${value.length}</span></div>
|
|
756
|
-
${
|
|
757
|
-
value.length === 0
|
|
758
|
-
? '<div class="data-card-row"><span class="data-card-value">Empty</span></div>'
|
|
759
|
-
: value
|
|
760
|
-
.slice(0, 20)
|
|
761
|
-
.map((item, i) => {
|
|
762
|
-
const label =
|
|
763
|
-
typeof item === "object" ? item.name || item.id || `#${i + 1}` : String(item);
|
|
764
|
-
const detail = typeof item === "object" ? JSON.stringify(item).slice(0, 150) : "";
|
|
765
|
-
return `<div class="data-card-row">
|
|
766
|
-
<span class="data-card-label">${escapeSettingsHtml(String(label))}</span>
|
|
767
|
-
${detail ? `<span class="data-card-value">${escapeSettingsHtml(detail)}</span>` : ""}
|
|
768
|
-
</div>`;
|
|
769
|
-
})
|
|
770
|
-
.join("") +
|
|
771
|
-
(value.length > 20
|
|
772
|
-
? `<div class="data-card-row"><span class="data-card-value">... and ${value.length - 20} more</span></div>`
|
|
773
|
-
: "")
|
|
774
|
-
}
|
|
775
|
-
</div>`;
|
|
776
|
-
}
|
|
777
|
-
if (typeof value === "object" && value !== null) {
|
|
778
|
-
return renderDataCard(key, value);
|
|
779
|
-
}
|
|
780
|
-
return `<div class="data-card-inline">
|
|
781
|
-
<span class="data-card-label">${escapeSettingsHtml(key)}</span>
|
|
782
|
-
<span class="data-card-value">${escapeSettingsHtml(value != null ? JSON.stringify(value) : "—")}</span>
|
|
783
|
-
</div>`;
|
|
784
|
-
})
|
|
785
|
-
.join("");
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
async function openGenericPanel(page) {
|
|
789
|
-
const spec = PAGE_RPC[page];
|
|
790
|
-
if (!spec) {
|
|
791
|
-
return;
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
const container = showSettingsContainer();
|
|
795
|
-
if (!container) {
|
|
796
|
-
return;
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
container.innerHTML = `
|
|
800
|
-
<div class="settings-panel">
|
|
801
|
-
<div class="settings-toolbar">
|
|
802
|
-
<div class="settings-toolbar-left">
|
|
803
|
-
<span class="settings-toolbar-label">${spec.label}</span>
|
|
804
|
-
</div>
|
|
805
|
-
<div class="settings-toolbar-right">
|
|
806
|
-
<button class="settings-btn" id="generic-refresh-btn">Refresh</button>
|
|
807
|
-
</div>
|
|
808
|
-
</div>
|
|
809
|
-
<div class="settings-content" id="generic-content">
|
|
810
|
-
<div class="settings-loading">Loading...</div>
|
|
811
|
-
</div>
|
|
812
|
-
</div>
|
|
813
|
-
`;
|
|
814
|
-
|
|
815
|
-
document.getElementById("generic-refresh-btn").addEventListener("click", () => {
|
|
816
|
-
resetGenericPagination(page);
|
|
817
|
-
void loadGenericData(page);
|
|
818
|
-
});
|
|
819
|
-
// Delegated click handler for the "Show more" button rendered inside
|
|
820
|
-
// generic-content. We attach on the container so it survives innerHTML
|
|
821
|
-
// re-renders driven by loadGenericData.
|
|
822
|
-
container.addEventListener("click", (e) => {
|
|
823
|
-
const target = e.target;
|
|
824
|
-
if (target && target.id === "generic-show-more-btn") {
|
|
825
|
-
bumpGenericPagination(page);
|
|
826
|
-
void loadGenericData(page);
|
|
827
|
-
}
|
|
828
|
-
});
|
|
829
|
-
resetGenericPagination(page);
|
|
830
|
-
await loadGenericData(page);
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
async function loadGenericData(page) {
|
|
834
|
-
const spec = PAGE_RPC[page];
|
|
835
|
-
const content = document.getElementById("generic-content");
|
|
836
|
-
if (!spec || !content) {
|
|
837
|
-
return;
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
content.innerHTML = '<div class="settings-loading">Loading...</div>';
|
|
841
|
-
|
|
842
|
-
if (!window.gateway?.connected) {
|
|
843
|
-
content.innerHTML = '<div class="settings-empty">Gateway not connected.</div>';
|
|
844
|
-
return;
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
try {
|
|
848
|
-
const result = await window.gateway.rpc(spec.method, {});
|
|
849
|
-
if (!result) {
|
|
850
|
-
content.innerHTML = '<div class="settings-empty">No data returned.</div>';
|
|
851
|
-
return;
|
|
852
|
-
}
|
|
853
|
-
if (spec.type === "list") {
|
|
854
|
-
const items = spec.key ? result[spec.key] : Array.isArray(result) ? result : [];
|
|
855
|
-
content.innerHTML = renderListData(items, page);
|
|
856
|
-
} else {
|
|
857
|
-
content.innerHTML = renderObjectData(result);
|
|
858
|
-
}
|
|
859
|
-
} catch (err) {
|
|
860
|
-
content.innerHTML = `<div class="settings-empty">Error: ${escapeSettingsHtml(err.message)}</div>`;
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
// ══════════════════════════════════════════════════════════════════════
|
|
865
|
-
// PUBLIC API — called by menu.js
|
|
866
|
-
// ══════════════════════════════════════════════════════════════════════
|
|
867
|
-
|
|
868
|
-
let activeSettingsPanel = null;
|
|
869
|
-
|
|
870
|
-
window.openNativeSettings = function (page) {
|
|
871
|
-
// Close previous panel cleanup
|
|
872
|
-
closeActiveSettingsPanel();
|
|
873
|
-
|
|
874
|
-
activeSettingsPanel = page;
|
|
875
|
-
|
|
876
|
-
switch (page) {
|
|
877
|
-
case "config":
|
|
878
|
-
void openConfigPanel();
|
|
879
|
-
break;
|
|
880
|
-
case "debug":
|
|
881
|
-
openDebugPanel();
|
|
882
|
-
break;
|
|
883
|
-
case "logs":
|
|
884
|
-
openLogsPanel();
|
|
885
|
-
break;
|
|
886
|
-
default:
|
|
887
|
-
if (PAGE_RPC[page]) {
|
|
888
|
-
void openGenericPanel(page);
|
|
889
|
-
} else {
|
|
890
|
-
console.warn("[settings] unknown page:", page);
|
|
891
|
-
}
|
|
892
|
-
break;
|
|
893
|
-
}
|
|
894
|
-
};
|
|
895
|
-
|
|
896
|
-
window.closeNativeSettings = function () {
|
|
897
|
-
closeActiveSettingsPanel();
|
|
898
|
-
hideSettingsContainer();
|
|
899
|
-
activeSettingsPanel = null;
|
|
900
|
-
};
|
|
901
|
-
|
|
902
|
-
function closeActiveSettingsPanel() {
|
|
903
|
-
if (activeSettingsPanel === "debug") {
|
|
904
|
-
closeDebugPanel();
|
|
905
|
-
}
|
|
906
|
-
if (activeSettingsPanel === "logs") {
|
|
907
|
-
closeLogsPanel();
|
|
908
|
-
}
|
|
909
|
-
}
|