bosun 0.36.0 → 0.36.2
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/.env.example +98 -16
- package/README.md +27 -0
- package/agent-event-bus.mjs +5 -5
- package/agent-pool.mjs +129 -12
- package/agent-prompts.mjs +7 -1
- package/agent-sdk.mjs +13 -2
- package/agent-supervisor.mjs +2 -2
- package/agent-work-report.mjs +1 -1
- package/anomaly-detector.mjs +6 -6
- package/autofix.mjs +15 -15
- package/bosun-skills.mjs +4 -4
- package/bosun.schema.json +160 -4
- package/claude-shell.mjs +11 -11
- package/cli.mjs +21 -21
- package/codex-config.mjs +19 -19
- package/codex-shell.mjs +180 -29
- package/config-doctor.mjs +27 -2
- package/config.mjs +60 -7
- package/copilot-shell.mjs +4 -4
- package/error-detector.mjs +1 -1
- package/fleet-coordinator.mjs +2 -2
- package/gemini-shell.mjs +692 -0
- package/github-oauth-portal.mjs +1 -1
- package/github-reconciler.mjs +2 -2
- package/kanban-adapter.mjs +741 -168
- package/merge-strategy.mjs +25 -25
- package/monitor.mjs +123 -105
- package/opencode-shell.mjs +22 -22
- package/package.json +7 -1
- package/postinstall.mjs +22 -22
- package/pr-cleanup-daemon.mjs +6 -6
- package/prepublish-check.mjs +4 -4
- package/presence.mjs +2 -2
- package/primary-agent.mjs +85 -7
- package/publish.mjs +1 -1
- package/review-agent.mjs +1 -1
- package/session-tracker.mjs +11 -0
- package/setup-web-server.mjs +429 -21
- package/setup.mjs +367 -12
- package/shared-knowledge.mjs +1 -1
- package/startup-service.mjs +9 -9
- package/stream-resilience.mjs +58 -4
- package/sync-engine.mjs +2 -2
- package/task-assessment.mjs +9 -9
- package/task-cli.mjs +1 -1
- package/task-complexity.mjs +71 -2
- package/task-context.mjs +1 -2
- package/task-executor.mjs +104 -41
- package/telegram-bot.mjs +825 -494
- package/telegram-sentinel.mjs +28 -28
- package/ui/app.js +256 -23
- package/ui/app.monolith.js +1 -1
- package/ui/components/agent-selector.js +4 -3
- package/ui/components/chat-view.js +101 -28
- package/ui/components/diff-viewer.js +3 -3
- package/ui/components/kanban-board.js +3 -3
- package/ui/components/session-list.js +255 -35
- package/ui/components/workspace-switcher.js +3 -3
- package/ui/demo.html +209 -194
- package/ui/index.html +3 -3
- package/ui/modules/icon-utils.js +206 -142
- package/ui/modules/icons.js +2 -27
- package/ui/modules/settings-schema.js +29 -5
- package/ui/modules/streaming.js +30 -2
- package/ui/modules/vision-stream.js +275 -0
- package/ui/modules/voice-client.js +102 -9
- package/ui/modules/voice-fallback.js +62 -6
- package/ui/modules/voice-overlay.js +594 -59
- package/ui/modules/voice.js +31 -38
- package/ui/setup.html +284 -34
- package/ui/styles/components.css +47 -0
- package/ui/styles/sessions.css +75 -0
- package/ui/tabs/agents.js +73 -43
- package/ui/tabs/chat.js +37 -40
- package/ui/tabs/control.js +2 -2
- package/ui/tabs/dashboard.js +1 -1
- package/ui/tabs/infra.js +10 -10
- package/ui/tabs/library.js +8 -8
- package/ui/tabs/logs.js +10 -10
- package/ui/tabs/settings.js +20 -20
- package/ui/tabs/tasks.js +76 -47
- package/ui-server.mjs +1761 -124
- package/update-check.mjs +13 -13
- package/ve-kanban.mjs +1 -1
- package/whatsapp-channel.mjs +5 -5
- package/workflow-engine.mjs +20 -1
- package/workflow-nodes.mjs +904 -4
- package/workflow-templates/agents.mjs +321 -7
- package/workflow-templates/ci-cd.mjs +6 -6
- package/workflow-templates/github.mjs +156 -84
- package/workflow-templates/planning.mjs +8 -8
- package/workflow-templates/reliability.mjs +8 -8
- package/workflow-templates/security.mjs +3 -3
- package/workflow-templates.mjs +15 -9
- package/workspace-manager.mjs +85 -1
- package/workspace-monitor.mjs +2 -2
- package/workspace-registry.mjs +2 -2
- package/worktree-manager.mjs +1 -1
package/ui/index.html
CHANGED
|
@@ -148,7 +148,7 @@
|
|
|
148
148
|
await loadApp(false);
|
|
149
149
|
} catch (err) {
|
|
150
150
|
if (!shimReady || !window.importShim) {
|
|
151
|
-
bootErr('
|
|
151
|
+
bootErr(':alert: Failed to load app modules.<br><small style="color:#888">Try refreshing. If this persists, run <code>npm install</code> in the bosun directory.</small>');
|
|
152
152
|
return;
|
|
153
153
|
}
|
|
154
154
|
console.warn("[ui] Primary CDN failed, switching to fallback.", err);
|
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
await loadApp(true);
|
|
158
158
|
} catch (fallbackErr) {
|
|
159
159
|
console.error("[ui] Fallback CDN failed.", fallbackErr);
|
|
160
|
-
bootErr('
|
|
160
|
+
bootErr(':alert: Failed to load app modules.<br><small style="color:#888">Local vendor and CDN fallback both failed. Run <code>npm install</code> in the bosun directory or check your connection.</small>');
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
})();
|
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
var el = document.getElementById('boot-loader');
|
|
171
171
|
if (el) {
|
|
172
172
|
var s = document.getElementById('boot-status');
|
|
173
|
-
if (s) s.innerHTML = '
|
|
173
|
+
if (s) s.innerHTML = ':alert: Failed to load app modules.<br><small style="color:#888">Try refreshing. If this persists, run <code>npm install</code> in the bosun directory.</small>';
|
|
174
174
|
}
|
|
175
175
|
}, 12000);
|
|
176
176
|
</script>
|
package/ui/modules/icon-utils.js
CHANGED
|
@@ -9,157 +9,201 @@ import htm from "htm";
|
|
|
9
9
|
import { ICONS } from "./icons.js";
|
|
10
10
|
|
|
11
11
|
const html = htm.bind(h);
|
|
12
|
+
const TOKEN_ICON_REGEX = /^:([a-zA-Z][a-zA-Z0-9_-]*):$/;
|
|
13
|
+
const INLINE_TOKEN_REGEX = /^:([a-zA-Z][a-zA-Z0-9_-]*):/;
|
|
12
14
|
|
|
13
15
|
export const EMOJI_ICON_MAP = {
|
|
14
|
-
"
|
|
16
|
+
"\u{2705}": "check",
|
|
15
17
|
"✓": "check",
|
|
16
18
|
"✕": "close",
|
|
17
|
-
"
|
|
19
|
+
"\u{2716}": "close",
|
|
18
20
|
"✗": "close",
|
|
19
21
|
"✘": "close",
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
22
|
+
"\u{274c}": "close",
|
|
23
|
+
"\u{2753}": "help",
|
|
24
|
+
"\u{2795}": "plus",
|
|
23
25
|
"➤": "arrowRight",
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
"
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
"
|
|
122
|
-
"
|
|
123
|
-
"
|
|
124
|
-
"
|
|
125
|
-
"
|
|
126
|
-
"
|
|
127
|
-
"
|
|
128
|
-
"
|
|
129
|
-
"
|
|
130
|
-
"
|
|
131
|
-
"
|
|
132
|
-
"
|
|
133
|
-
"
|
|
134
|
-
"
|
|
135
|
-
"
|
|
136
|
-
"
|
|
137
|
-
"
|
|
138
|
-
"
|
|
139
|
-
"
|
|
140
|
-
"
|
|
141
|
-
"
|
|
142
|
-
"
|
|
26
|
+
"\u{1f30d}": "globe",
|
|
27
|
+
"\u{1f310}": "globe",
|
|
28
|
+
"\u{1f333}": "git",
|
|
29
|
+
"\u{1f33f}": "git",
|
|
30
|
+
"\u{1f389}": "star",
|
|
31
|
+
"\u{1f39b}": "sliders",
|
|
32
|
+
"\u{1f3a4}": "mic",
|
|
33
|
+
"\u{1f3a8}": "palette",
|
|
34
|
+
"\u{1f3af}": "target",
|
|
35
|
+
"\u{1f3c1}": "flag",
|
|
36
|
+
"\u{1f3e0}": "home",
|
|
37
|
+
"\u{1f3e5}": "heart",
|
|
38
|
+
"\u{1f3f7}": "tag",
|
|
39
|
+
"\u{1f40d}": "file",
|
|
40
|
+
"\u{1f419}": "git",
|
|
41
|
+
"\u{1f41a}": "terminal",
|
|
42
|
+
"\u{1f41b}": "bug",
|
|
43
|
+
"\u{1f440}": "eye",
|
|
44
|
+
"\u{1f441}": "eye",
|
|
45
|
+
"\u{1f464}": "user",
|
|
46
|
+
"\u{1f465}": "users",
|
|
47
|
+
"\u{1f48e}": "diamond",
|
|
48
|
+
"\u{1f493}": "heart",
|
|
49
|
+
"\u{1f49a}": "heart",
|
|
50
|
+
"\u{1f4a1}": "lightbulb",
|
|
51
|
+
"\u{1f4a5}": "zap",
|
|
52
|
+
"\u{1f4ac}": "chat",
|
|
53
|
+
"\u{1f4bb}": "monitor",
|
|
54
|
+
"\u{1f4be}": "save",
|
|
55
|
+
"\u{1f4c1}": "folder",
|
|
56
|
+
"\u{1f4c2}": "folder",
|
|
57
|
+
"\u{1f4c4}": "file",
|
|
58
|
+
"\u{1f4c8}": "chart",
|
|
59
|
+
"\u{1f4ca}": "chart",
|
|
60
|
+
"\u{1f4cb}": "clipboard",
|
|
61
|
+
"\u{1f4cc}": "pin",
|
|
62
|
+
"\u{1f4cf}": "ruler",
|
|
63
|
+
"\u{1f4d0}": "ruler",
|
|
64
|
+
"\u{1f4d6}": "file",
|
|
65
|
+
"\u{1f4dc}": "file",
|
|
66
|
+
"\u{1f4dd}": "edit",
|
|
67
|
+
"\u{1f4e1}": "server",
|
|
68
|
+
"\u{1f4e4}": "upload",
|
|
69
|
+
"\u{1f4e5}": "download",
|
|
70
|
+
"\u{1f4e6}": "box",
|
|
71
|
+
"\u{1f4e8}": "mail",
|
|
72
|
+
"\u{1f4ec}": "mail",
|
|
73
|
+
"\u{1f4f1}": "phone",
|
|
74
|
+
"\u{1f4f8}": "camera",
|
|
75
|
+
"\u{1f500}": "git",
|
|
76
|
+
"\u{1f501}": "repeat",
|
|
77
|
+
"\u{1f504}": "refresh",
|
|
78
|
+
"\u{1f50c}": "plug",
|
|
79
|
+
"\u{1f50d}": "search",
|
|
80
|
+
"\u{1f50e}": "search",
|
|
81
|
+
"\u{1f510}": "lock",
|
|
82
|
+
"\u{1f512}": "lock",
|
|
83
|
+
"\u{1f513}": "unlock",
|
|
84
|
+
"\u{1f514}": "bell",
|
|
85
|
+
"\u{1f517}": "link",
|
|
86
|
+
"\u{1f522}": "hash",
|
|
87
|
+
"\u{1f523}": "terminal",
|
|
88
|
+
"\u{1f525}": "zap",
|
|
89
|
+
"\u{1f527}": "settings",
|
|
90
|
+
"\u{1f528}": "hammer",
|
|
91
|
+
"\u{1f534}": "dot",
|
|
92
|
+
"\u{1f535}": "dot",
|
|
93
|
+
"\u{26aa}": "dot",
|
|
94
|
+
"\u{1f537}": "diamond",
|
|
95
|
+
"\u{1f5a5}": "monitor",
|
|
96
|
+
"\u{1f5c2}": "folder",
|
|
97
|
+
"\u{1f5c3}": "archive",
|
|
98
|
+
"\u{1f5d1}": "trash",
|
|
99
|
+
"\u{1f5dc}": "filter",
|
|
100
|
+
"\u{1f5fa}": "grid",
|
|
101
|
+
"\u{1f648}": "eyeOff",
|
|
102
|
+
"\u{1f680}": "rocket",
|
|
103
|
+
"\u{1f6a6}": "alert",
|
|
104
|
+
"\u{1f6a7}": "alert",
|
|
105
|
+
"\u{1f6a8}": "alert",
|
|
106
|
+
"\u{1f6ab}": "ban",
|
|
107
|
+
"\u{1f6d1}": "close",
|
|
108
|
+
"\u{1f6e1}": "shield",
|
|
109
|
+
"\u{1f6f0}": "server",
|
|
110
|
+
"\u{1f7e1}": "dot",
|
|
111
|
+
"\u{1f7e2}": "dot",
|
|
112
|
+
"\u{1f916}": "bot",
|
|
113
|
+
"\u{1f980}": "file",
|
|
114
|
+
"\u{1f9e0}": "cpu",
|
|
115
|
+
"\u{1f9ea}": "beaker",
|
|
116
|
+
"\u{1f9ed}": "compass",
|
|
117
|
+
"\u{1f9f0}": "settings",
|
|
118
|
+
"\u{1f9f5}": "link",
|
|
119
|
+
"\u{1f9f9}": "trash",
|
|
120
|
+
"\u{1fa9d}": "link",
|
|
121
|
+
"\u{267b}": "repeat",
|
|
122
|
+
"\u{267b}️": "repeat",
|
|
123
|
+
"\u{2b07}": "download",
|
|
124
|
+
"\u{2b07}️": "download",
|
|
125
|
+
"\u{2b06}": "upload",
|
|
126
|
+
"\u{2b06}️": "upload",
|
|
127
|
+
"\u{2728}": "star",
|
|
128
|
+
"\u{2b50}": "star",
|
|
129
|
+
"\u{2699}": "settings",
|
|
130
|
+
"\u{2699}️": "settings",
|
|
131
|
+
"\u{26a0}": "alert",
|
|
132
|
+
"\u{26a0}️": "alert",
|
|
133
|
+
"\u{26a1}": "zap",
|
|
134
|
+
"\u{23f1}": "clock",
|
|
135
|
+
"\u{23f1}️": "clock",
|
|
136
|
+
"\u{23f8}": "pause",
|
|
137
|
+
"\u{23f8}️": "pause",
|
|
138
|
+
"\u{23f9}": "stop",
|
|
139
|
+
"\u{23f9}️": "stop",
|
|
140
|
+
"\u{25b6}": "play",
|
|
141
|
+
"\u{25b6}️": "play",
|
|
142
|
+
"\u{23f3}": "clock",
|
|
143
|
+
"\u{26d4}": "ban",
|
|
144
|
+
"\u{2630}": "menu",
|
|
143
145
|
"#️⃣": "hash",
|
|
144
|
-
"
|
|
145
|
-
"
|
|
146
|
-
"
|
|
147
|
-
"
|
|
148
|
-
"
|
|
149
|
-
"
|
|
150
|
-
"
|
|
146
|
+
"\u{1f39b}️": "sliders",
|
|
147
|
+
"\u{1f5fa}️": "grid",
|
|
148
|
+
"\u{1f5a5}️": "monitor",
|
|
149
|
+
"\u{1f3f7}️": "tag",
|
|
150
|
+
"\u{1f6f0}️": "server",
|
|
151
|
+
"\u{1f6e1}️": "shield",
|
|
152
|
+
"\u{1f441}️": "eye",
|
|
151
153
|
};
|
|
152
154
|
|
|
153
|
-
|
|
155
|
+
const ICON_ALIAS_MAP = Object.freeze({
|
|
156
|
+
ok: "check",
|
|
157
|
+
success: "check",
|
|
158
|
+
fail: "close",
|
|
159
|
+
error: "close",
|
|
160
|
+
warning: "alert",
|
|
161
|
+
warn: "alert",
|
|
162
|
+
info: "help",
|
|
163
|
+
question: "help",
|
|
164
|
+
sparkles: "star",
|
|
165
|
+
brain: "cpu",
|
|
166
|
+
tasks: "clipboard",
|
|
167
|
+
log: "file",
|
|
168
|
+
logs: "file",
|
|
169
|
+
stopSign: "stop",
|
|
170
|
+
playCircle: "play",
|
|
171
|
+
pauseCircle: "pause",
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
function normalizeIconInput(icon) {
|
|
175
|
+
return String(icon || "").replace(/[\uFE0E\uFE0F]/g, "");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function resolveIconName(icon) {
|
|
154
179
|
if (!icon) return null;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
180
|
+
const normalizedRaw = normalizeIconInput(icon);
|
|
181
|
+
if (!normalizedRaw) return null;
|
|
182
|
+
|
|
183
|
+
const tokenMatch = normalizedRaw.match(TOKEN_ICON_REGEX);
|
|
184
|
+
const tokenName = tokenMatch ? tokenMatch[1] : normalizedRaw;
|
|
185
|
+
const normalized = normalizeIconInput(tokenName).trim();
|
|
186
|
+
if (!normalized) return null;
|
|
187
|
+
|
|
188
|
+
if (ICONS[normalized]) return normalized;
|
|
189
|
+
const lowered = normalized.toLowerCase();
|
|
190
|
+
if (ICONS[lowered]) return lowered;
|
|
191
|
+
const aliased = ICON_ALIAS_MAP[lowered];
|
|
192
|
+
if (aliased && ICONS[aliased]) return aliased;
|
|
193
|
+
const mapped = EMOJI_ICON_MAP[icon] || EMOJI_ICON_MAP[normalizedRaw] || EMOJI_ICON_MAP[normalized];
|
|
194
|
+
if (mapped && ICONS[mapped]) return mapped;
|
|
160
195
|
return null;
|
|
161
196
|
}
|
|
162
197
|
|
|
198
|
+
function appendIconPart(parts, iconName) {
|
|
199
|
+
parts.push(html`<span class="icon-inline" aria-hidden="true">${ICONS[iconName]}</span>`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function resolveIcon(icon) {
|
|
203
|
+
const iconName = resolveIconName(icon);
|
|
204
|
+
return iconName ? ICONS[iconName] : null;
|
|
205
|
+
}
|
|
206
|
+
|
|
163
207
|
export function iconText(text, { className = "" } = {}) {
|
|
164
208
|
if (text == null) return text;
|
|
165
209
|
const str = String(text);
|
|
@@ -167,16 +211,36 @@ export function iconText(text, { className = "" } = {}) {
|
|
|
167
211
|
const parts = [];
|
|
168
212
|
let buffer = "";
|
|
169
213
|
|
|
170
|
-
for (
|
|
214
|
+
for (let i = 0; i < str.length; ) {
|
|
215
|
+
const tokenMatch = str.slice(i).match(INLINE_TOKEN_REGEX);
|
|
216
|
+
if (tokenMatch) {
|
|
217
|
+
const fullMatch = tokenMatch[0];
|
|
218
|
+
const iconName = resolveIconName(tokenMatch[1]);
|
|
219
|
+
if (iconName) {
|
|
220
|
+
if (buffer) {
|
|
221
|
+
parts.push(buffer);
|
|
222
|
+
buffer = "";
|
|
223
|
+
}
|
|
224
|
+
appendIconPart(parts, iconName);
|
|
225
|
+
hasIcon = true;
|
|
226
|
+
i += fullMatch.length;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const codePoint = str.codePointAt(i);
|
|
232
|
+
if (typeof codePoint !== "number") break;
|
|
233
|
+
const ch = String.fromCodePoint(codePoint);
|
|
234
|
+
i += ch.length;
|
|
235
|
+
|
|
171
236
|
if (ch === "\uFE0E" || ch === "\uFE0F") continue;
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
if (icon) {
|
|
237
|
+
const iconName = resolveIconName(ch);
|
|
238
|
+
if (iconName) {
|
|
175
239
|
if (buffer) {
|
|
176
240
|
parts.push(buffer);
|
|
177
241
|
buffer = "";
|
|
178
242
|
}
|
|
179
|
-
parts
|
|
243
|
+
appendIconPart(parts, iconName);
|
|
180
244
|
hasIcon = true;
|
|
181
245
|
} else {
|
|
182
246
|
buffer += ch;
|
package/ui/modules/icons.js
CHANGED
|
@@ -13,7 +13,7 @@ const html = htm.bind(h);
|
|
|
13
13
|
* viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
14
14
|
* stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
15
15
|
*
|
|
16
|
-
*
|
|
16
|
+
* Icon size is controlled exclusively by CSS (.icon-inline svg sets width/height).
|
|
17
17
|
*/
|
|
18
18
|
export const ICONS = {
|
|
19
19
|
/* ── Brand ── */
|
|
@@ -356,8 +356,6 @@ export const ICONS = {
|
|
|
356
356
|
stroke-width="2"
|
|
357
357
|
stroke-linecap="round"
|
|
358
358
|
stroke-linejoin="round"
|
|
359
|
-
width="16"
|
|
360
|
-
height="16"
|
|
361
359
|
>
|
|
362
360
|
<polyline points="6 9 12 15 18 9" />
|
|
363
361
|
</svg>`,
|
|
@@ -369,8 +367,6 @@ export const ICONS = {
|
|
|
369
367
|
stroke-width="2"
|
|
370
368
|
stroke-linecap="round"
|
|
371
369
|
stroke-linejoin="round"
|
|
372
|
-
width="16"
|
|
373
|
-
height="16"
|
|
374
370
|
>
|
|
375
371
|
<line x1="22" y1="2" x2="11" y2="13" />
|
|
376
372
|
<polygon points="22 2 15 22 11 13 2 9 22 2" />
|
|
@@ -383,8 +379,6 @@ export const ICONS = {
|
|
|
383
379
|
stroke-width="2"
|
|
384
380
|
stroke-linecap="round"
|
|
385
381
|
stroke-linejoin="round"
|
|
386
|
-
width="16"
|
|
387
|
-
height="16"
|
|
388
382
|
>
|
|
389
383
|
<polyline points="23 4 23 10 17 10" />
|
|
390
384
|
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10" />
|
|
@@ -397,8 +391,6 @@ export const ICONS = {
|
|
|
397
391
|
stroke-width="2"
|
|
398
392
|
stroke-linecap="round"
|
|
399
393
|
stroke-linejoin="round"
|
|
400
|
-
width="16"
|
|
401
|
-
height="16"
|
|
402
394
|
>
|
|
403
395
|
<circle cx="11" cy="11" r="8" />
|
|
404
396
|
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
@@ -411,8 +403,6 @@ export const ICONS = {
|
|
|
411
403
|
stroke-width="2"
|
|
412
404
|
stroke-linecap="round"
|
|
413
405
|
stroke-linejoin="round"
|
|
414
|
-
width="16"
|
|
415
|
-
height="16"
|
|
416
406
|
>
|
|
417
407
|
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
|
|
418
408
|
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
@@ -425,8 +415,6 @@ export const ICONS = {
|
|
|
425
415
|
stroke-width="2"
|
|
426
416
|
stroke-linecap="round"
|
|
427
417
|
stroke-linejoin="round"
|
|
428
|
-
width="16"
|
|
429
|
-
height="16"
|
|
430
418
|
>
|
|
431
419
|
<polyline points="3 6 5 6 21 6" />
|
|
432
420
|
<path
|
|
@@ -443,8 +431,6 @@ export const ICONS = {
|
|
|
443
431
|
stroke-width="2"
|
|
444
432
|
stroke-linecap="round"
|
|
445
433
|
stroke-linejoin="round"
|
|
446
|
-
width="16"
|
|
447
|
-
height="16"
|
|
448
434
|
>
|
|
449
435
|
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
|
|
450
436
|
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
|
|
@@ -457,8 +443,6 @@ export const ICONS = {
|
|
|
457
443
|
stroke-width="2"
|
|
458
444
|
stroke-linecap="round"
|
|
459
445
|
stroke-linejoin="round"
|
|
460
|
-
width="16"
|
|
461
|
-
height="16"
|
|
462
446
|
>
|
|
463
447
|
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
|
|
464
448
|
<circle cx="12" cy="12" r="3" />
|
|
@@ -471,8 +455,6 @@ export const ICONS = {
|
|
|
471
455
|
stroke-width="2"
|
|
472
456
|
stroke-linecap="round"
|
|
473
457
|
stroke-linejoin="round"
|
|
474
|
-
width="16"
|
|
475
|
-
height="16"
|
|
476
458
|
>
|
|
477
459
|
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
|
|
478
460
|
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
|
|
@@ -485,8 +467,6 @@ export const ICONS = {
|
|
|
485
467
|
stroke-width="2"
|
|
486
468
|
stroke-linecap="round"
|
|
487
469
|
stroke-linejoin="round"
|
|
488
|
-
width="16"
|
|
489
|
-
height="16"
|
|
490
470
|
>
|
|
491
471
|
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
|
492
472
|
<polyline points="7 10 12 15 17 10" />
|
|
@@ -500,8 +480,6 @@ export const ICONS = {
|
|
|
500
480
|
stroke-width="2"
|
|
501
481
|
stroke-linecap="round"
|
|
502
482
|
stroke-linejoin="round"
|
|
503
|
-
width="16"
|
|
504
|
-
height="16"
|
|
505
483
|
>
|
|
506
484
|
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
|
507
485
|
<polyline points="17 8 12 3 7 8" />
|
|
@@ -515,8 +493,6 @@ export const ICONS = {
|
|
|
515
493
|
stroke-width="2"
|
|
516
494
|
stroke-linecap="round"
|
|
517
495
|
stroke-linejoin="round"
|
|
518
|
-
width="16"
|
|
519
|
-
height="16"
|
|
520
496
|
>
|
|
521
497
|
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
|
|
522
498
|
<circle cx="9" cy="7" r="4" />
|
|
@@ -531,8 +507,6 @@ export const ICONS = {
|
|
|
531
507
|
stroke-width="2"
|
|
532
508
|
stroke-linecap="round"
|
|
533
509
|
stroke-linejoin="round"
|
|
534
|
-
width="16"
|
|
535
|
-
height="16"
|
|
536
510
|
>
|
|
537
511
|
<path
|
|
538
512
|
d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"
|
|
@@ -1074,3 +1048,4 @@ export const ICONS = {
|
|
|
1074
1048
|
<polygon points="16 8 14 14 8 16 10 10 16 8" />
|
|
1075
1049
|
</svg>`,
|
|
1076
1050
|
};
|
|
1051
|
+
|
|
@@ -66,21 +66,31 @@ export const SETTINGS_SCHEMA = [
|
|
|
66
66
|
{ key: "TELEGRAM_UI_ALLOW_UNSAFE", label: "Allow Unsafe (No Auth)", category: "miniapp", type: "boolean", defaultVal: false, description: "DANGER: Disables ALL authentication. Anyone with your URL can control agents, read secrets, and execute code on your machine. Combined with a tunnel, this exposes your machine to the ENTIRE INTERNET. Only enable for localhost-only debugging with tunnel DISABLED.", restart: true, danger: true },
|
|
67
67
|
{ key: "_UNSAFE_TUNNEL_WARNING", label: "", category: "miniapp", type: "info", description: "WARNING: If 'Allow Unsafe' is ON and 'Tunnel Mode' is not disabled, your UI is publicly accessible with ZERO authentication. This is a critical security risk." },
|
|
68
68
|
{ key: "TELEGRAM_UI_AUTH_MAX_AGE_SEC", label: "Auth Token Max Age", category: "miniapp", type: "number", defaultVal: 86400, min: 300, max: 604800, unit: "sec", description: "Maximum age for Telegram initData tokens before they expire." },
|
|
69
|
-
{ key: "TELEGRAM_UI_TUNNEL", label: "Tunnel Mode", category: "miniapp", type: "select", defaultVal: "
|
|
69
|
+
{ key: "TELEGRAM_UI_TUNNEL", label: "Tunnel Mode", category: "miniapp", type: "select", defaultVal: "named", options: ["named", "quick", "auto", "cloudflared", "disabled"], description: "Cloudflare tunnel mode. 'named' is default and uses persistent hostname + DNS sync. 'quick' is explicit fallback.", restart: true },
|
|
70
|
+
{ key: "TELEGRAM_UI_ALLOW_QUICK_TUNNEL_FALLBACK", label: "Allow Quick Fallback", category: "miniapp", type: "boolean", defaultVal: false, description: "When named tunnel startup fails, allow fallback to random trycloudflare hostname.", restart: true, advanced: true },
|
|
71
|
+
{ key: "TELEGRAM_UI_FALLBACK_AUTH_ENABLED", label: "Enable Fallback Auth", category: "miniapp", type: "boolean", defaultVal: true, description: "Enable admin fallback PIN/password auth for recovering access when Telegram/session token auth is unavailable.", restart: false },
|
|
72
|
+
{ key: "TELEGRAM_UI_FALLBACK_AUTH_RATE_LIMIT_IP_PER_MIN", label: "Fallback Per-IP Limit", category: "miniapp", type: "number", defaultVal: 10, min: 1, max: 200, unit: "req/min", description: "Per-IP rate limit for fallback auth attempts.", advanced: true },
|
|
73
|
+
{ key: "TELEGRAM_UI_FALLBACK_AUTH_RATE_LIMIT_GLOBAL_PER_MIN", label: "Fallback Global Limit", category: "miniapp", type: "number", defaultVal: 60, min: 1, max: 500, unit: "req/min", description: "Global rate limit for fallback auth attempts.", advanced: true },
|
|
74
|
+
{ key: "TELEGRAM_UI_FALLBACK_AUTH_MAX_FAILURES", label: "Fallback Max Failures", category: "miniapp", type: "number", defaultVal: 5, min: 1, max: 50, description: "Failed fallback auth attempts before temporary lockout.", advanced: true },
|
|
75
|
+
{ key: "TELEGRAM_UI_FALLBACK_AUTH_LOCKOUT_MS", label: "Fallback Lockout", category: "miniapp", type: "number", defaultVal: 600000, min: 10000, max: 86400000, unit: "ms", description: "Temporary lockout duration after too many failed fallback auth attempts.", advanced: true },
|
|
76
|
+
{ key: "TELEGRAM_UI_FALLBACK_AUTH_ROTATE_DAYS", label: "Fallback Rotate Days", category: "miniapp", type: "number", defaultVal: 30, min: 1, max: 365, unit: "days", description: "Rotation target for fallback auth credential.", advanced: true },
|
|
77
|
+
{ key: "TELEGRAM_UI_FALLBACK_AUTH_TRANSIENT_COOLDOWN_MS", label: "Fallback Cooldown", category: "miniapp", type: "number", defaultVal: 5000, min: 1000, max: 60000, unit: "ms", description: "Cooldown when fallback auth backend returns transient errors.", advanced: true },
|
|
70
78
|
|
|
71
79
|
// ── Executor / AI ──────────────────────────────────────────
|
|
72
80
|
{ key: "EXECUTOR_MODE", label: "Executor Mode", category: "executor", type: "select", defaultVal: "internal", options: ["vk", "internal", "hybrid"], description: "Task execution mode. 'internal' uses built-in agent pool, 'vk' delegates to Vibe-Kanban, 'hybrid' uses both.", restart: true },
|
|
73
81
|
{ key: "INTERNAL_EXECUTOR_PARALLEL", label: "Max Parallel Agents", category: "executor", type: "number", defaultVal: 3, min: 1, max: 20, description: "Maximum number of concurrent agent execution slots." },
|
|
74
|
-
{ key: "INTERNAL_EXECUTOR_SDK", label: "Default SDK", category: "executor", type: "select", defaultVal: "auto", options: ["auto", "codex", "copilot", "claude"], description: "Default AI SDK for task execution. 'auto' selects based on availability and task complexity." },
|
|
82
|
+
{ key: "INTERNAL_EXECUTOR_SDK", label: "Default SDK", category: "executor", type: "select", defaultVal: "auto", options: ["auto", "codex", "copilot", "claude", "gemini", "opencode"], description: "Default AI SDK for task execution. 'auto' selects based on availability and task complexity." },
|
|
75
83
|
{ key: "INTERNAL_EXECUTOR_TIMEOUT_MS", label: "Task Timeout", category: "executor", type: "number", defaultVal: 5400000, min: 60000, max: 14400000, unit: "ms", description: "Maximum time a single task execution can run (default: 90 min)." },
|
|
76
84
|
{ key: "INTERNAL_EXECUTOR_MAX_RETRIES", label: "Max Retries", category: "executor", type: "number", defaultVal: 2, min: 0, max: 10, description: "Number of automatic retries per task before marking as failed." },
|
|
77
85
|
{ key: "INTERNAL_EXECUTOR_POLL_MS", label: "Poll Interval", category: "executor", type: "number", defaultVal: 30000, min: 5000, max: 300000, unit: "ms", description: "How often the executor polls the kanban board for new tasks.", advanced: true },
|
|
78
86
|
{ key: "INTERNAL_EXECUTOR_REVIEW_AGENT_ENABLED", label: "PR Review Agent", category: "executor", type: "boolean", defaultVal: true, description: "Enable automatic PR review handoff after task completion." },
|
|
79
87
|
{ key: "INTERNAL_EXECUTOR_REPLENISH_ENABLED", label: "Auto Replenish Backlog", category: "executor", type: "boolean", defaultVal: false, description: "Automatically generate new tasks when backlog is low." },
|
|
80
|
-
{ key: "PRIMARY_AGENT", label: "Primary Agent SDK", category: "executor", type: "select", defaultVal: "codex-sdk", options: ["codex-sdk", "copilot-sdk", "claude-sdk"], description: "Which AI SDK handles primary agent sessions and chat commands." },
|
|
88
|
+
{ key: "PRIMARY_AGENT", label: "Primary Agent SDK", category: "executor", type: "select", defaultVal: "codex-sdk", options: ["codex-sdk", "copilot-sdk", "claude-sdk", "gemini-sdk", "opencode-sdk"], description: "Which AI SDK handles primary agent sessions and chat commands." },
|
|
81
89
|
{ key: "CODEX_SDK_DISABLED", label: "Disable Codex SDK", category: "executor", type: "boolean", defaultVal: false, description: "When true, Codex SDK is unavailable for chat and will not appear in the executor picker.", restart: true },
|
|
82
90
|
{ key: "COPILOT_SDK_DISABLED", label: "Disable Copilot SDK", category: "executor", type: "boolean", defaultVal: false, description: "When true, Copilot SDK is unavailable for chat and will not appear in the executor picker.", restart: true },
|
|
83
91
|
{ key: "CLAUDE_SDK_DISABLED", label: "Disable Claude SDK", category: "executor", type: "boolean", defaultVal: false, description: "When true, Claude SDK is unavailable for chat and will not appear in the executor picker.", restart: true },
|
|
92
|
+
{ key: "GEMINI_SDK_DISABLED", label: "Disable Gemini SDK", category: "executor", type: "boolean", defaultVal: false, description: "When true, Gemini SDK is unavailable for chat and will not appear in the executor picker.", restart: true },
|
|
93
|
+
{ key: "OPENCODE_SDK_DISABLED", label: "Disable OpenCode SDK", category: "executor", type: "boolean", defaultVal: false, description: "When true, OpenCode SDK is unavailable for chat and will not appear in the executor picker.", restart: true },
|
|
84
94
|
{ key: "EXECUTORS", label: "Executor Distribution", category: "executor", type: "string", defaultVal: "CODEX:DEFAULT:100", description: "Weighted executor configuration. Format: TYPE:VARIANT:WEIGHT[:MODEL|MODEL],... (e.g., CODEX:DEFAULT:70:gpt-5.2-codex|gpt-5.1-codex-mini,COPILOT:CLAUDE_OPUS_4_6:30:claude-opus-4.6)", validate: "^[A-Z_]+:[A-Z_]+:\\d+" },
|
|
85
95
|
{ key: "EXECUTOR_DISTRIBUTION", label: "Distribution Strategy", category: "executor", type: "select", defaultVal: "weighted", options: ["weighted", "round-robin", "primary-only"], description: "How tasks are distributed across configured executors.", advanced: true },
|
|
86
96
|
{ key: "FAILOVER_STRATEGY", label: "Failover Strategy", category: "executor", type: "select", defaultVal: "next-in-line", options: ["next-in-line", "weighted-random", "round-robin"], description: "Strategy for selecting next executor when the primary fails.", advanced: true },
|
|
@@ -104,20 +114,26 @@ export const SETTINGS_SCHEMA = [
|
|
|
104
114
|
{ key: "CODEX_SUBAGENT_MODEL", label: "Subagent Model", category: "executor", type: "string", defaultVal: "gpt-5.1-codex-mini", description: "Preferred lightweight model for delegated/subagent work." },
|
|
105
115
|
{ key: "ANTHROPIC_API_KEY", label: "Anthropic API Key", category: "executor", type: "secret", sensitive: true, description: "Anthropic API key for Claude SDK. Required if using Claude executor." },
|
|
106
116
|
{ key: "CLAUDE_MODEL", label: "Claude Model", category: "executor", type: "string", defaultVal: "claude-opus-4-6", description: "Model for Claude SDK. E.g., claude-opus-4-6, claude-sonnet-4-5." },
|
|
117
|
+
{ key: "GEMINI_API_KEY", label: "Gemini API Key", category: "executor", type: "secret", sensitive: true, description: "Google AI Studio key for Gemini SDK (alternative: GOOGLE_API_KEY)." },
|
|
118
|
+
{ key: "GOOGLE_API_KEY", label: "Google API Key", category: "executor", type: "secret", sensitive: true, description: "Alternative key env used by Gemini SDK." },
|
|
119
|
+
{ key: "GEMINI_MODEL", label: "Gemini Model", category: "executor", type: "string", defaultVal: "gemini-2.5-pro", description: "Model for Gemini SDK sessions (for example gemini-2.5-pro)." },
|
|
120
|
+
{ key: "GEMINI_TRANSPORT", label: "Gemini Transport", category: "executor", type: "select", defaultVal: "auto", options: ["auto", "sdk", "cli"], description: "Gemini adapter transport. 'auto' uses SDK then falls back to CLI." },
|
|
121
|
+
{ key: "OPENCODE_MODEL", label: "OpenCode Model", category: "executor", type: "string", defaultVal: "gpt-5.2-codex", description: "Model override passed to OpenCode sessions." },
|
|
107
122
|
{ key: "COPILOT_MODEL", label: "Copilot Model", category: "executor", type: "string", defaultVal: "gpt-5", description: "Model for Copilot SDK sessions." },
|
|
108
123
|
{ key: "COPILOT_CLI_TOKEN", label: "Copilot CLI Token", category: "executor", type: "secret", sensitive: true, description: "Auth token for Copilot CLI remote mode." },
|
|
109
124
|
|
|
110
125
|
// ── Voice Assistant ──────────────────────────────────────────
|
|
111
126
|
{ key: "VOICE_ENABLED", label: "Enable Voice Mode", category: "voice", type: "boolean", defaultVal: true, description: "Enable the real-time voice assistant in the chat UI." },
|
|
112
|
-
{ key: "VOICE_PROVIDER", label: "Voice Provider", category: "voice", type: "select", defaultVal: "auto", options: ["auto", "openai", "azure", "fallback"], description: "Voice API provider. 'auto' selects based on available keys. 'fallback' uses browser speech APIs." },
|
|
127
|
+
{ key: "VOICE_PROVIDER", label: "Voice Provider", category: "voice", type: "select", defaultVal: "auto", options: ["auto", "openai", "azure", "claude", "gemini", "fallback"], description: "Voice API provider. 'auto' selects based on available keys. 'fallback' uses browser speech APIs." },
|
|
113
128
|
{ key: "VOICE_MODEL", label: "Voice Model", category: "voice", type: "string", defaultVal: "gpt-4o-realtime-preview-2024-12-17", description: "OpenAI Realtime model to use for voice sessions." },
|
|
129
|
+
{ key: "VOICE_VISION_MODEL", label: "Vision Model", category: "voice", type: "string", defaultVal: "gpt-4.1-mini", description: "Vision model used for live screen/camera understanding in voice mode." },
|
|
114
130
|
{ key: "OPENAI_REALTIME_API_KEY", label: "OpenAI Realtime Key", category: "voice", type: "secret", sensitive: true, description: "Dedicated API key for voice. Falls back to OPENAI_API_KEY if not set." },
|
|
115
131
|
{ key: "AZURE_OPENAI_REALTIME_ENDPOINT", label: "Azure Realtime Endpoint", category: "voice", type: "string", description: "Azure OpenAI endpoint for Realtime API (e.g., https://myresource.openai.azure.com).", validate: "^$|^https?://" },
|
|
116
132
|
{ key: "AZURE_OPENAI_REALTIME_API_KEY", label: "Azure Realtime Key", category: "voice", type: "secret", sensitive: true, description: "Azure OpenAI API key for Realtime API. Falls back to AZURE_OPENAI_API_KEY if not set." },
|
|
117
133
|
{ key: "AZURE_OPENAI_REALTIME_DEPLOYMENT", label: "Azure Deployment", category: "voice", type: "string", defaultVal: "gpt-4o-realtime-preview", description: "Azure deployment name for the Realtime model." },
|
|
118
134
|
{ key: "VOICE_ID", label: "Voice", category: "voice", type: "select", defaultVal: "alloy", options: ["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"], description: "Voice personality for text-to-speech output." },
|
|
119
135
|
{ key: "VOICE_TURN_DETECTION", label: "Turn Detection", category: "voice", type: "select", defaultVal: "server_vad", options: ["server_vad", "semantic_vad", "none"], description: "How the model detects when you stop speaking. 'semantic_vad' is more intelligent but higher latency." },
|
|
120
|
-
{ key: "VOICE_DELEGATE_EXECUTOR", label: "Delegate Executor", category: "voice", type: "select", defaultVal: "codex-sdk", options: ["codex-sdk", "copilot-sdk", "claude-sdk", "opencode-sdk"], description: "Which agent executor voice tool calls delegate to for complex tasks." },
|
|
136
|
+
{ key: "VOICE_DELEGATE_EXECUTOR", label: "Delegate Executor", category: "voice", type: "select", defaultVal: "codex-sdk", options: ["codex-sdk", "copilot-sdk", "claude-sdk", "gemini-sdk", "opencode-sdk"], description: "Which agent executor voice tool calls delegate to for complex tasks." },
|
|
121
137
|
{ key: "VOICE_FALLBACK_MODE", label: "Fallback Mode", category: "voice", type: "select", defaultVal: "browser", options: ["browser", "disabled"], description: "When Tier 1 (Realtime API) is unavailable, use browser speech APIs as fallback." },
|
|
122
138
|
|
|
123
139
|
// ── Kanban / Tasks ─────────────────────────────────────────
|
|
@@ -172,6 +188,14 @@ export const SETTINGS_SCHEMA = [
|
|
|
172
188
|
// ── Network / Tunnel ──────────────────────────────────────
|
|
173
189
|
{ key: "CLOUDFLARE_TUNNEL_NAME", label: "Tunnel Name", category: "network", type: "string", description: "Named Cloudflare tunnel for persistent URL. Leave empty for random quick tunnel." },
|
|
174
190
|
{ key: "CLOUDFLARE_TUNNEL_CREDENTIALS", label: "Tunnel Credentials", category: "network", type: "secret", sensitive: true, description: "Path to Cloudflare tunnel credentials JSON file." },
|
|
191
|
+
{ key: "CLOUDFLARE_BASE_DOMAIN", label: "Base Domain", category: "network", type: "string", description: "Base domain for deterministic per-user hostnames (for example bosun.det.io)." },
|
|
192
|
+
{ key: "CLOUDFLARE_TUNNEL_HOSTNAME", label: "Explicit Hostname", category: "network", type: "string", description: "Optional explicit hostname override (for example jon.bosun.det.io)." },
|
|
193
|
+
{ key: "CLOUDFLARE_USERNAME_HOSTNAME_POLICY", label: "Hostname Policy", category: "network", type: "select", defaultVal: "per-user-fixed", options: ["per-user-fixed", "fixed"], description: "per-user-fixed creates stable hostname per workstation user. fixed uses shared CLOUDFLARE_FIXED_HOST_LABEL.", advanced: true },
|
|
194
|
+
{ key: "CLOUDFLARE_ZONE_ID", label: "Zone ID", category: "network", type: "string", description: "Cloudflare zone ID used by DNS orchestration API client.", advanced: true },
|
|
195
|
+
{ key: "CLOUDFLARE_API_TOKEN", label: "API Token", category: "network", type: "secret", sensitive: true, description: "Cloudflare API token scoped to Zone DNS edit for the configured zone.", advanced: true },
|
|
196
|
+
{ key: "CLOUDFLARE_DNS_SYNC_ENABLED", label: "DNS Sync Enabled", category: "network", type: "boolean", defaultVal: true, description: "Enable idempotent Cloudflare DNS create/verify/update for the named tunnel hostname.", advanced: true },
|
|
197
|
+
{ key: "CLOUDFLARE_DNS_MAX_RETRIES", label: "DNS Retry Count", category: "network", type: "number", defaultVal: 3, min: 1, max: 8, description: "Retry attempts for Cloudflare DNS API operations.", advanced: true },
|
|
198
|
+
{ key: "CLOUDFLARE_DNS_RETRY_BASE_MS", label: "DNS Retry Base", category: "network", type: "number", defaultVal: 750, min: 100, max: 5000, unit: "ms", description: "Base delay for Cloudflare DNS API retry backoff.", advanced: true },
|
|
175
199
|
{ key: "TELEGRAM_PRESENCE_INTERVAL_SEC", label: "Presence Interval", category: "network", type: "number", defaultVal: 60, min: 10, max: 600, unit: "sec", description: "How often this instance announces its presence to the coordinator." },
|
|
176
200
|
{ key: "TELEGRAM_PRESENCE_DISABLED", label: "Disable Presence", category: "network", type: "boolean", defaultVal: false, description: "Disable multi-instance presence entirely." },
|
|
177
201
|
{ key: "VE_INSTANCE_LABEL", label: "Instance Label", category: "network", type: "string", description: "Human-friendly name for this instance in multi-instance setups." },
|