crewswarm 0.9.5 → 1.0.0
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 +56 -7
- package/apps/dashboard/dist/assets/{index-D-sRshvg.css → index-C5-vlIwl.css} +1 -1
- package/apps/dashboard/dist/assets/index-CSooN9fi.js +2 -0
- package/apps/dashboard/dist/assets/index-CSooN9fi.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-spending-tab-DcXD5TQY.js +1 -0
- package/apps/dashboard/dist/assets/tab-spending-tab-DcXD5TQY.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-testing-tab-Ea5K-rsb.js +1 -0
- package/apps/dashboard/dist/index.html +83 -7
- package/apps/dashboard/dist/index.html.br +0 -0
- package/contrib/openclaw-plugin/index.ts +20 -11
- package/lib/autoharness/index.mjs +151 -1
- package/lib/chat/history.mjs +1 -1
- package/lib/contacts/identity-linker.mjs +24 -3
- package/lib/contacts/index.mjs +2 -1
- package/lib/crew-lead/chat-handler.mjs +56 -33
- package/lib/crew-lead/llm-caller.mjs +71 -14
- package/lib/crew-lead/prompts.mjs +4 -2
- package/lib/engines/rt-envelope.mjs +4 -1
- package/package.json +5 -3
- package/scripts/dashboard.mjs +216 -25
- package/scripts/health-check.mjs +70 -28
- package/scripts/restart-all-from-repo.sh +25 -21
- package/scripts/start.mjs +35 -15
- package/apps/dashboard/dist/assets/chat-core-uXb_C0GM.js.br +0 -0
- package/apps/dashboard/dist/assets/cli-process-CNZ_UBCt.js.br +0 -0
- package/apps/dashboard/dist/assets/components-BS9fQjE_.js.br +0 -0
- package/apps/dashboard/dist/assets/core-utils-CmOkXgzi.js.br +0 -0
- package/apps/dashboard/dist/assets/index-BeVllEj_.js +0 -2
- package/apps/dashboard/dist/assets/index-BeVllEj_.js.br +0 -0
- package/apps/dashboard/dist/assets/index-D-sRshvg.css.br +0 -0
- package/apps/dashboard/dist/assets/orchestration-Ca2DLWN-.js.br +0 -0
- package/apps/dashboard/dist/assets/setup-wizard-CA0Or47w.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-agents-tab-BgpIsjkw.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-benchmarks-tab-BHjKCPm3.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-comms-tab-kguqTIzD.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-contacts-tab-DiOyMYth.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-engines-tab-BsdZVvU0.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-memory-tab-Cu6u13EQ.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-models-tab-dNRgsTOO.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-pm-loop-tab-DiAPTJXu.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-projects-tab-SFH4E--a.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-prompts-tab-DVkUNaJd.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-services-tab-DU_LH3uG.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-settings-tab-CuvH_Fj_.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-skills-tab-DR7PJ7NB.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-spending-tab-DEccQHnt.js +0 -1
- package/apps/dashboard/dist/assets/tab-spending-tab-DEccQHnt.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-swarm-chat-tab-BNrd88-r.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-swarm-tab-B1AcjL1W.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js +0 -1
- package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-usage-tab-BIOOnB-Y.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-waves-tab-SaJDkb4x.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-workflows-tab-B-soSy1k.js.br +0 -0
- package/apps/dashboard/dist/index.html.gz +0 -0
package/scripts/health-check.mjs
CHANGED
|
@@ -16,6 +16,8 @@ import fs from "node:fs";
|
|
|
16
16
|
import path from "node:path";
|
|
17
17
|
import os from "node:os";
|
|
18
18
|
import { execSync } from "node:child_process";
|
|
19
|
+
import http from "node:http";
|
|
20
|
+
import https from "node:https";
|
|
19
21
|
|
|
20
22
|
const JSON_MODE = process.argv.includes("--json");
|
|
21
23
|
const QUIET_MODE = process.argv.includes("--quiet");
|
|
@@ -51,9 +53,31 @@ function authHeaders() {
|
|
|
51
53
|
return { "content-type": "application/json", ...(t ? { authorization: `Bearer ${t}` } : {}) };
|
|
52
54
|
}
|
|
53
55
|
|
|
56
|
+
function request(url, opts = {}) {
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const parsed = new URL(url);
|
|
59
|
+
const transport = parsed.protocol === "https:" ? https : http;
|
|
60
|
+
const req = transport.request(parsed, {
|
|
61
|
+
method: opts.method || "GET",
|
|
62
|
+
headers: opts.headers || {},
|
|
63
|
+
}, (res) => {
|
|
64
|
+
let body = "";
|
|
65
|
+
res.setEncoding("utf8");
|
|
66
|
+
res.on("data", chunk => { body += chunk; });
|
|
67
|
+
res.on("end", () => resolve({ status: res.statusCode || 0, body, headers: res.headers }));
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
req.on("error", reject);
|
|
71
|
+
req.setTimeout(opts.timeout || 10000, () => req.destroy(new Error("timeout")));
|
|
72
|
+
|
|
73
|
+
if (opts.body) req.write(opts.body);
|
|
74
|
+
req.end();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
54
78
|
async function ping(url, label, opts = {}) {
|
|
55
79
|
try {
|
|
56
|
-
const res = await
|
|
80
|
+
const res = await request(url, { timeout: opts.timeout || 10000, headers: authHeaders() });
|
|
57
81
|
return { ok: res.ok || res.status < 500, status: res.status };
|
|
58
82
|
} catch (e) {
|
|
59
83
|
return { ok: false, error: e.message };
|
|
@@ -84,7 +108,7 @@ async function run() {
|
|
|
84
108
|
check("~/.crewswarm/crewswarm.json", hasSwarm ? "pass" : "fail", hasSwarm ? "" : "run: bash install.sh");
|
|
85
109
|
|
|
86
110
|
const token = getToken();
|
|
87
|
-
check("Auth token", token ? "pass" : "
|
|
111
|
+
check("Auth token", token ? "pass" : "warn", token ? `${token.slice(0,8)}…` : "no rt.authToken (optional for local dev)");
|
|
88
112
|
|
|
89
113
|
// ── 2. API keys ──────────────────────────────────────────────────────────────
|
|
90
114
|
section("API Keys");
|
|
@@ -127,19 +151,32 @@ async function run() {
|
|
|
127
151
|
if (!NO_SERVICES) {
|
|
128
152
|
section("Agents");
|
|
129
153
|
try {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
154
|
+
// Try with auth first, fall back to no-auth for local dev
|
|
155
|
+
let res = await request(`${CREW_LEAD}/api/agents`, { headers: authHeaders(), timeout: 5000 });
|
|
156
|
+
if (res.status === 401) {
|
|
157
|
+
// No token or wrong token — try the dashboard proxy which may not require auth
|
|
158
|
+
try {
|
|
159
|
+
res = await request(`${DASHBOARD}/api/agents`, { timeout: 5000 });
|
|
160
|
+
} catch { /* dashboard proxy also failed, use original 401 response */ }
|
|
161
|
+
}
|
|
162
|
+
const d = JSON.parse(res.body || "{}");
|
|
163
|
+
// Dashboard returns array directly, crew-lead returns { agents: [...] }
|
|
164
|
+
const agents = Array.isArray(d) ? d : (d.agents || []);
|
|
165
|
+
if (agents.length === 0 && d.error) {
|
|
166
|
+
// Auth required but no valid token — report agent count from bridge process list
|
|
167
|
+
const bridgeCount = (() => { try { return execSync("pgrep -f 'gateway-bridge.mjs' | wc -l", { encoding: "utf8", timeout: 2000 }).trim(); } catch { return "0"; } })();
|
|
168
|
+
check(`Agents`, bridgeCount > 0 ? "pass" : "warn", `${bridgeCount} bridge processes running (auth required for detailed status)`);
|
|
169
|
+
} else {
|
|
170
|
+
const online = agents.filter(a => a.online || a.alive || a.liveness === "online" || a.liveness === "alive");
|
|
171
|
+
const coreAgents = ["crew-coder","crew-qa","crew-pm","crew-main","crew-fixer"];
|
|
172
|
+
check(`Agents online (${online.length}/${agents.length})`,
|
|
173
|
+
online.length > 0 ? "pass" : "warn",
|
|
174
|
+
online.length === 0 ? "bridges not started — run: npm run start-crew" : online.map(a => a.id?.replace("crew-","")).join(", ").slice(0,80));
|
|
175
|
+
for (const core of coreAgents) {
|
|
176
|
+
const a = agents.find(x => x.id === core);
|
|
177
|
+
const isOnline = a?.online || a?.alive || a?.liveness === "online" || a?.liveness === "alive";
|
|
178
|
+
check(` ${core}`, isOnline ? "pass" : "warn", isOnline ? "" : "bridge not running");
|
|
179
|
+
}
|
|
143
180
|
}
|
|
144
181
|
} catch (e) {
|
|
145
182
|
check("Agents", "fail", `could not reach crew-lead: ${e.message}`);
|
|
@@ -150,7 +187,7 @@ async function run() {
|
|
|
150
187
|
section("CLI Tools");
|
|
151
188
|
const [cursorCli, claudeCli, opencodeCli, nodeCli] = await Promise.all([
|
|
152
189
|
Promise.resolve(cliCheck("cursor --version 2>/dev/null || cursor-cli --version 2>/dev/null", "cursor")),
|
|
153
|
-
Promise.resolve(cliCheck("
|
|
190
|
+
Promise.resolve(cliCheck("which claude 2>/dev/null && claude --version 2>/dev/null || echo 'not found'", "claude")),
|
|
154
191
|
Promise.resolve(cliCheck("opencode --version 2>/dev/null || echo 'not found'", "opencode")),
|
|
155
192
|
Promise.resolve(cliCheck("node --version", "node")),
|
|
156
193
|
]);
|
|
@@ -168,23 +205,23 @@ async function run() {
|
|
|
168
205
|
if (!NO_SERVICES && mcpServer.ok) {
|
|
169
206
|
section("MCP Protocol");
|
|
170
207
|
try {
|
|
171
|
-
const initRes = await
|
|
208
|
+
const initRes = await request(`${MCP_URL}/mcp`, {
|
|
172
209
|
method: "POST",
|
|
173
210
|
headers: { "content-type": "application/json" },
|
|
174
211
|
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2024-11-05", clientInfo: { name: "health-check", version: "1.0" } } }),
|
|
175
|
-
|
|
212
|
+
timeout: 4000,
|
|
176
213
|
});
|
|
177
|
-
const initData =
|
|
214
|
+
const initData = JSON.parse(initRes.body || "{}");
|
|
178
215
|
check("MCP initialize", initData?.result?.serverInfo ? "pass" : "fail",
|
|
179
216
|
initData?.result?.serverInfo?.name || JSON.stringify(initData).slice(0,60));
|
|
180
217
|
|
|
181
|
-
const toolsRes = await
|
|
218
|
+
const toolsRes = await request(`${MCP_URL}/mcp`, {
|
|
182
219
|
method: "POST",
|
|
183
220
|
headers: { "content-type": "application/json" },
|
|
184
221
|
body: JSON.stringify({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} }),
|
|
185
|
-
|
|
222
|
+
timeout: 4000,
|
|
186
223
|
});
|
|
187
|
-
const toolsData =
|
|
224
|
+
const toolsData = JSON.parse(toolsRes.body || "{}");
|
|
188
225
|
const toolCount = toolsData?.result?.tools?.length || 0;
|
|
189
226
|
check(`MCP tools/list (${toolCount} tools)`, toolCount >= 5 ? "pass" : "warn",
|
|
190
227
|
toolsData?.result?.tools?.map(t => t.name).join(", ").slice(0, 80));
|
|
@@ -200,17 +237,22 @@ async function run() {
|
|
|
200
237
|
} else {
|
|
201
238
|
try {
|
|
202
239
|
const start = Date.now();
|
|
203
|
-
const res = await
|
|
240
|
+
const res = await request(`${CREW_LEAD}/chat`, {
|
|
204
241
|
method: "POST",
|
|
205
242
|
headers: authHeaders(),
|
|
206
243
|
body: JSON.stringify({ message: "say: HEALTH_OK", sessionId: "health-check" }),
|
|
207
|
-
|
|
244
|
+
timeout: 15000,
|
|
208
245
|
});
|
|
209
|
-
const d =
|
|
246
|
+
const d = JSON.parse(res.body || "{}");
|
|
210
247
|
const elapsed = Date.now() - start;
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
248
|
+
if (res.status === 401) {
|
|
249
|
+
// Auth required — crew-lead is up but we can't chat without token
|
|
250
|
+
check("crew-lead responds", "pass", `up (auth required for chat test)`);
|
|
251
|
+
} else {
|
|
252
|
+
const reply = d.reply || d.message || "";
|
|
253
|
+
check("crew-lead responds", reply.length > 0 ? "pass" : "warn",
|
|
254
|
+
`${Math.round(elapsed/100)/10}s — "${reply.slice(0,60) || "(empty reply)"}"`);
|
|
255
|
+
}
|
|
214
256
|
} catch (e) {
|
|
215
257
|
check("crew-lead chat", "fail", e.message);
|
|
216
258
|
}
|
|
@@ -21,8 +21,9 @@ YELLOW='\033[0;33m'
|
|
|
21
21
|
BOLD='\033[1m'
|
|
22
22
|
RESET='\033[0m'
|
|
23
23
|
|
|
24
|
-
# ── Service status tracking
|
|
25
|
-
|
|
24
|
+
# ── Service status tracking (bash 3.2 compatible — no associative arrays) ────
|
|
25
|
+
svc_set() { eval "SVC_$(echo "$1" | tr '-' '_')=$2"; }
|
|
26
|
+
svc_get() { eval "echo \${SVC_$(echo "$1" | tr '-' '_'):-unknown}"; }
|
|
26
27
|
|
|
27
28
|
# ── Graceful kill: SIGTERM first, SIGKILL after 5s ────────────────────────────
|
|
28
29
|
graceful_kill_pattern() {
|
|
@@ -71,14 +72,14 @@ wait_for_health() {
|
|
|
71
72
|
while [ $attempt -lt $max_attempts ]; do
|
|
72
73
|
if curl -s --max-time 2 "$url" > /dev/null 2>&1; then
|
|
73
74
|
echo -e " ${GREEN}✓${RESET} $name is up"
|
|
74
|
-
|
|
75
|
+
svc_set "$name" "up"
|
|
75
76
|
return 0
|
|
76
77
|
fi
|
|
77
78
|
attempt=$((attempt + 1))
|
|
78
79
|
sleep 1
|
|
79
80
|
done
|
|
80
81
|
echo -e " ${RED}✗${RESET} $name failed to start (tried ${max_attempts}s)"
|
|
81
|
-
|
|
82
|
+
svc_set "$name" "down"
|
|
82
83
|
return 1
|
|
83
84
|
}
|
|
84
85
|
|
|
@@ -112,6 +113,8 @@ graceful_kill_pattern "telegram-bridge.mjs"
|
|
|
112
113
|
graceful_kill_pattern "whatsapp-bridge.mjs"
|
|
113
114
|
graceful_kill_pattern "opencode serve"
|
|
114
115
|
graceful_kill_pattern "pm-loop.mjs"
|
|
116
|
+
graceful_kill_pattern "node --test" # orphaned test runners from dashboard
|
|
117
|
+
graceful_kill_pattern "npx playwright" # orphaned playwright runners
|
|
115
118
|
graceful_kill_pattern "apps/vibe/server.mjs"
|
|
116
119
|
graceful_kill_pattern "watch-server.mjs"
|
|
117
120
|
graceful_kill_pattern "vite.*vibe"
|
|
@@ -151,11 +154,11 @@ OPENCODE_BIN="$(command -v opencode 2>/dev/null)" || OPENCODE_BIN="/usr/local/bi
|
|
|
151
154
|
if [[ -x "$OPENCODE_BIN" ]]; then
|
|
152
155
|
nohup "$OPENCODE_BIN" serve --port 4096 --hostname 127.0.0.1 >> /tmp/opencode.log 2>&1 &
|
|
153
156
|
# Non-critical — don't block on health
|
|
154
|
-
|
|
157
|
+
svc_set "opencode" "up"
|
|
155
158
|
echo " Started (PID $!)"
|
|
156
159
|
else
|
|
157
160
|
echo " (opencode not found; skip)"
|
|
158
|
-
|
|
161
|
+
svc_set "opencode" "skip"
|
|
159
162
|
fi
|
|
160
163
|
|
|
161
164
|
# ── 2. RT daemon (port 18889) ────────────────────────────────────────────────
|
|
@@ -181,7 +184,7 @@ fi
|
|
|
181
184
|
echo ""
|
|
182
185
|
echo "Starting gateway bridges (crew-main, crew-pm, crew-coder, etc.)..."
|
|
183
186
|
"$NODE" scripts/start-crew.mjs --force
|
|
184
|
-
|
|
187
|
+
svc_set "agents" "up"
|
|
185
188
|
sleep 1
|
|
186
189
|
|
|
187
190
|
# ── 4. crew-lead (port 5010) — CRITICAL ─────────────────────────────────────
|
|
@@ -206,27 +209,27 @@ if [[ "$START_DASH" -eq 1 ]]; then
|
|
|
206
209
|
fi
|
|
207
210
|
wait_for_health "http://127.0.0.1:4319/" "dashboard" 30 || true
|
|
208
211
|
else
|
|
209
|
-
|
|
212
|
+
svc_set "dashboard" "skip"
|
|
210
213
|
fi
|
|
211
214
|
|
|
212
215
|
# ── 6. Messaging bridges (Telegram + WhatsApp) ──────────────────────────────
|
|
213
216
|
if [[ "$START_BRIDGES" -eq 1 ]]; then
|
|
214
217
|
echo ""
|
|
215
218
|
# Verify RT bus is still up before starting bridges
|
|
216
|
-
if [[ "$
|
|
219
|
+
if [[ "$(svc_get rt-bus)" == "up" ]]; then
|
|
217
220
|
echo "Starting Telegram bridge..."
|
|
218
221
|
nohup "$NODE" "$REPO_DIR/telegram-bridge.mjs" >> /tmp/telegram-bridge.log 2>&1 &
|
|
219
|
-
|
|
222
|
+
svc_set "telegram" "up"
|
|
220
223
|
echo " PID: $!"
|
|
221
224
|
|
|
222
225
|
echo "Starting WhatsApp bridge..."
|
|
223
226
|
nohup "$NODE" "$REPO_DIR/whatsapp-bridge.mjs" >> /tmp/whatsapp-bridge.log 2>&1 &
|
|
224
|
-
|
|
227
|
+
svc_set "whatsapp" "up"
|
|
225
228
|
echo " PID: $!"
|
|
226
229
|
else
|
|
227
230
|
echo -e " ${YELLOW}Skipping messaging bridges — RT bus is not up${RESET}"
|
|
228
|
-
|
|
229
|
-
|
|
231
|
+
svc_set "telegram" "skip"
|
|
232
|
+
svc_set "whatsapp" "skip"
|
|
230
233
|
fi
|
|
231
234
|
fi
|
|
232
235
|
|
|
@@ -238,7 +241,7 @@ if ! lsof -ti :5020 >/dev/null 2>&1; then
|
|
|
238
241
|
wait_for_health "http://127.0.0.1:5020/health" "mcp-server" 15 || true
|
|
239
242
|
else
|
|
240
243
|
echo " (already running on :5020)"
|
|
241
|
-
|
|
244
|
+
svc_set "mcp-server" "up"
|
|
242
245
|
fi
|
|
243
246
|
|
|
244
247
|
# ── 8. Vibe + file watcher (ports 3333, 3334) ───────────────────────────────
|
|
@@ -257,18 +260,18 @@ if [[ "$START_STUDIO" -eq 1 ]]; then
|
|
|
257
260
|
for i in $(seq 1 10); do
|
|
258
261
|
if lsof -ti :3334 >/dev/null 2>&1; then
|
|
259
262
|
echo -e " ${GREEN}✓${RESET} file watcher is up"
|
|
260
|
-
|
|
263
|
+
svc_set "file-watcher" "up"
|
|
261
264
|
break
|
|
262
265
|
fi
|
|
263
266
|
if [[ "$i" -eq 10 ]]; then
|
|
264
267
|
echo -e " ${YELLOW}!${RESET} file watcher not detected on :3334"
|
|
265
|
-
|
|
268
|
+
svc_set "file-watcher" "down"
|
|
266
269
|
fi
|
|
267
270
|
sleep 1
|
|
268
271
|
done
|
|
269
272
|
else
|
|
270
|
-
|
|
271
|
-
|
|
273
|
+
svc_set "vibe-studio" "skip"
|
|
274
|
+
svc_set "file-watcher" "skip"
|
|
272
275
|
fi
|
|
273
276
|
|
|
274
277
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
@@ -280,7 +283,7 @@ echo -e "${BOLD}━━━ Service Summary ━━━${RESET}"
|
|
|
280
283
|
print_status() {
|
|
281
284
|
local name="$1"
|
|
282
285
|
local port="$2"
|
|
283
|
-
local status="$
|
|
286
|
+
local status="$(svc_get "$name")"
|
|
284
287
|
case "$status" in
|
|
285
288
|
up) echo -e " ${GREEN}✓${RESET} $name :$port" ;;
|
|
286
289
|
down) echo -e " ${RED}✗${RESET} $name :$port" ;;
|
|
@@ -309,12 +312,13 @@ echo "Logs: /tmp/opencode.log /tmp/opencrew-rt-daemon.log /tmp/crew-lead.log /tm
|
|
|
309
312
|
if [[ -f "$REPO_DIR/scripts/health-check.mjs" ]]; then
|
|
310
313
|
echo ""
|
|
311
314
|
echo -e "${BOLD}Running health check...${RESET}"
|
|
315
|
+
sleep 2 # let services fully warm up
|
|
312
316
|
"$NODE" "$REPO_DIR/scripts/health-check.mjs" --quiet 2>/dev/null || true
|
|
313
317
|
fi
|
|
314
318
|
|
|
315
319
|
# ── Exit code: 0 if critical services are up, 1 otherwise ────────────────────
|
|
316
|
-
CREW_LEAD_OK="$
|
|
317
|
-
DASHBOARD_OK="$
|
|
320
|
+
CREW_LEAD_OK="$(svc_get crew-lead)"
|
|
321
|
+
DASHBOARD_OK="$(svc_get dashboard)"
|
|
318
322
|
|
|
319
323
|
if [[ "$CREW_LEAD_OK" == "up" ]] && { [[ "$DASHBOARD_OK" == "up" ]] || [[ "$START_DASH" -eq 0 ]]; }; then
|
|
320
324
|
echo ""
|
package/scripts/start.mjs
CHANGED
|
@@ -75,36 +75,46 @@ function tryReadJSON(p) {
|
|
|
75
75
|
try { return JSON.parse(fs.readFileSync(p, "utf8")); } catch { return null; }
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
// Bootstrap config directory + minimal config if missing — the dashboard's
|
|
79
|
+
// setup wizard will guide the user through API key entry on first visit.
|
|
80
|
+
let firstRun = false;
|
|
78
81
|
if (!fs.existsSync(CREWSWARM_DIR)) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
`Or, for a one-liner from the web:\n\n` +
|
|
83
|
-
` bash <(curl -fsSL https://raw.githubusercontent.com/crewswarm/crewswarm/main/install.sh)`
|
|
84
|
-
);
|
|
82
|
+
fs.mkdirSync(CREWSWARM_DIR, { recursive: true });
|
|
83
|
+
info(`Created config directory: ${CREWSWARM_DIR}`);
|
|
84
|
+
firstRun = true;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
// ── 3. crewswarm.json ─────────────────────────────────────────────────────────
|
|
88
88
|
if (!fs.existsSync(SWARM_CFG)) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
const defaultConfig = {
|
|
90
|
+
agents: [
|
|
91
|
+
{ id: "crew-lead", model: "groq/llama-3.3-70b-versatile" },
|
|
92
|
+
{ id: "crew-main", model: "groq/llama-3.3-70b-versatile" },
|
|
93
|
+
{ id: "crew-coder", model: "groq/llama-3.3-70b-versatile" },
|
|
94
|
+
{ id: "crew-qa", model: "groq/llama-3.3-70b-versatile" },
|
|
95
|
+
{ id: "crew-fixer", model: "groq/llama-3.3-70b-versatile" },
|
|
96
|
+
{ id: "crew-pm", model: "groq/llama-3.3-70b-versatile" }
|
|
97
|
+
],
|
|
98
|
+
providers: {}
|
|
99
|
+
};
|
|
100
|
+
fs.writeFileSync(SWARM_CFG, JSON.stringify(defaultConfig, null, 2));
|
|
101
|
+
info(`Created default ${SWARM_CFG} — add API keys in the dashboard setup wizard`);
|
|
102
|
+
firstRun = true;
|
|
93
103
|
}
|
|
94
|
-
|
|
104
|
+
let swarm = tryReadJSON(SWARM_CFG);
|
|
95
105
|
if (!swarm) {
|
|
96
106
|
fatal(
|
|
97
107
|
`Cannot parse ${SWARM_CFG} — file may be corrupt.`,
|
|
98
108
|
`Check it is valid JSON, or re-run:\n\n bash ${INSTALL_SH}`
|
|
99
109
|
);
|
|
100
110
|
}
|
|
101
|
-
success(`crewswarm.json found`);
|
|
111
|
+
success(`crewswarm.json found${firstRun ? " (first run — setup wizard will launch)" : ""}`);
|
|
102
112
|
|
|
103
113
|
// ── 4. config.json ───────────────────────────────────────────────────────────
|
|
104
114
|
if (!fs.existsSync(SYS_CFG)) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
115
|
+
const defaultSysCfg = { rt: { authToken: `local-${Date.now().toString(36)}` } };
|
|
116
|
+
fs.writeFileSync(SYS_CFG, JSON.stringify(defaultSysCfg, null, 2));
|
|
117
|
+
info(`Created default ${SYS_CFG}`);
|
|
108
118
|
} else {
|
|
109
119
|
const sys = tryReadJSON(SYS_CFG);
|
|
110
120
|
if (!sys) {
|
|
@@ -114,6 +124,16 @@ if (!fs.existsSync(SYS_CFG)) {
|
|
|
114
124
|
}
|
|
115
125
|
}
|
|
116
126
|
|
|
127
|
+
// ── Bootstrap supporting files if missing ────────────────────────────────────
|
|
128
|
+
const CMD_ALLOWLIST = path.join(CREWSWARM_DIR, "cmd-allowlist.json");
|
|
129
|
+
const AGENT_PROMPTS = path.join(CREWSWARM_DIR, "agent-prompts.json");
|
|
130
|
+
if (!fs.existsSync(CMD_ALLOWLIST)) {
|
|
131
|
+
fs.writeFileSync(CMD_ALLOWLIST, '["npm *","node *","npx *","git *"]');
|
|
132
|
+
}
|
|
133
|
+
if (!fs.existsSync(AGENT_PROMPTS)) {
|
|
134
|
+
fs.writeFileSync(AGENT_PROMPTS, "{}");
|
|
135
|
+
}
|
|
136
|
+
|
|
117
137
|
// ── 5. Provider check ────────────────────────────────────────────────────────
|
|
118
138
|
const providers = swarm.providers || {};
|
|
119
139
|
const configured = Object.entries(providers).filter(([, v]) => v?.apiKey && String(v.apiKey).trim().length > 0 && !String(v.apiKey).startsWith("your-"));
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/tab-benchmarks-tab-BHjKCPm3.js","assets/core-utils-CmOkXgzi.js"])))=>i.map(i=>d[i]);
|
|
2
|
-
var e,t,n;import{a as o,g as a,b as s,s as i,p as r,d as l,c,k as d,r as p,h as m,l as u,m as g,e as h}from"./core-utils-CmOkXgzi.js";import{c as v}from"./setup-wizard-CA0Or47w.js";import{i as f}from"./components-BS9fQjE_.js";import{s as y}from"./orchestration-Ca2DLWN-.js";import"./cli-process-CNZ_UBCt.js";import{i as w}from"./chat-core-uXb_C0GM.js";import{i as b,s as x,h as k}from"./tab-swarm-chat-tab-BNrd88-r.js";import{i as C}from"./tab-waves-tab-SaJDkb4x.js";import{i as E,s as I}from"./tab-workflows-tab-B-soSy1k.js";import{c as T,m as B,s as S,l as j,a as A}from"./tab-memory-tab-Cu6u13EQ.js";import{i as P,s as L,l as M,a as _,r as N}from"./tab-services-tab-DU_LH3uG.js";import{i as O,s as R,l as F,a as H,t as D,b as z,c as G,d as $,e as V,f as q,g as K,h as U,j as W,k as J,m as Q,n as Y,o as X,p as Z,r as ee,q as te,u as ne,v as oe,w as ae,x as se,y as ie,z as re,A as le}from"./tab-agents-tab-BgpIsjkw.js";import{i as ce,a as de}from"./tab-prompts-tab-DVkUNaJd.js";import{i as pe,s as me,l as ue,r as ge}from"./tab-testing-tab-CezZOZcJ.js";import{s as he,a as ve,l as fe,c as ye,b as we,i as be,t as xe,d as ke,f as Ce,r as Ee,e as Ie,g as Te,u as Be}from"./tab-skills-tab-DR7PJ7NB.js";import{s as Se,i as je,a as Ae,l as Pe}from"./tab-contacts-tab-DiOyMYth.js";import{i as Le,t as Me,l as _e,d as Ne}from"./tab-engines-tab-BsdZVvU0.js";import{s as Oe,a as Re,b as Fe,c as He,t as De,d as ze,r as Ge,i as $e}from"./tab-swarm-tab-B1AcjL1W.js";import{i as Ve,s as qe,t as Ke,a as Ue,b as We,c as Je,d as Qe,e as Ye,f as Xe,g as Ze,h as et,j as tt,k as nt,l as ot,m as at}from"./tab-models-tab-dNRgsTOO.js";import{s as st,a as it,b as rt,t as lt,c as ct,d as dt,e as pt,f as mt,g as ut,h as gt,i as ht,j as vt,k as ft,l as yt,m as wt,u as bt,n as xt,o as kt,p as Ct,q as Et,r as It,v as Tt,w as Bt,x as St,y as jt,z as At,A as Pt,B as Lt,C as Mt,D as _t,E as Nt,F as Ot,G as Rt,H as Ft,I as Ht,J as Dt}from"./tab-settings-tab-CuvH_Fj_.js";import{i as zt,s as Gt,l as $t,a as Vt,b as qt,c as Kt,d as Ut,e as Wt,f as Jt,g as Qt,h as Yt,r as Xt,j as Zt}from"./tab-comms-tab-kguqTIzD.js";import{i as en,p as tn,r as nn,c as on,a as an,b as sn,e as rn,d as ln,l as cn,o as dn,f as pn,h as mn,j as un,k as gn,m as hn,n as vn,q as fn,t as yn,v as wn,w as bn}from"./tab-projects-tab-SFH4E--a.js";import{a as xn,r as kn,c as Cn,b as En,l as In,d as Tn}from"./tab-usage-tab-BIOOnB-Y.js";import{s as Bn,r as Sn,l as jn,a as An,b as Pn}from"./tab-spending-tab-DEccQHnt.js";import{i as Ln}from"./tab-pm-loop-tab-DiAPTJXu.js";!function(){const e=document.createElement("link").relList;if(!(e&&e.supports&&e.supports("modulepreload"))){for(const e of document.querySelectorAll('link[rel="modulepreload"]'))t(e);new MutationObserver(e=>{for(const n of e)if("childList"===n.type)for(const e of n.addedNodes)"LINK"===e.tagName&&"modulepreload"===e.rel&&t(e)}).observe(document,{childList:!0,subtree:!0})}function t(e){if(e.ep)return;e.ep=!0;const t=function(e){const t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),"use-credentials"===e.crossOrigin?t.credentials="include":"anonymous"===e.crossOrigin?t.credentials="omit":t.credentials="same-origin",t}(e);fetch(e.href,t)}}();const Mn={};let _n=null;async function Nn(){return _n||(_n=await function(e,t){let n=Promise.resolve();if(t&&t.length>0){let e=function(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:"fulfilled",value:e}),e=>({status:"rejected",reason:e}))))};document.getElementsByTagName("link");const o=document.querySelector("meta[property=csp-nonce]"),a=(null==o?void 0:o.nonce)||(null==o?void 0:o.getAttribute("nonce"));n=e(t.map(e=>{if((e=function(e){return"/"+e}(e))in Mn)return;Mn[e]=!0;const t=e.endsWith(".css"),n=t?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${e}"]${n}`))return;const o=document.createElement("link");return o.rel=t?"stylesheet":"modulepreload",t||(o.as="script"),o.crossOrigin="",o.href=e,a&&o.setAttribute("nonce",a),document.head.appendChild(o),t?new Promise((t,n)=>{o.addEventListener("load",t),o.addEventListener("error",()=>n(new Error(`Unable to preload CSS for ${e}`)))}):void 0}))}function o(e){const t=new Event("vite:preloadError",{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return n.then(t=>{for(const e of t||[])"rejected"===e.status&&o(e.reason);return e().catch(o)})}(()=>import("./tab-benchmarks-tab-BHjKCPm3.js"),__vite__mapDeps([0,1]))),_n}async function On(){try{const e=document.getElementById("statusDot");document.getElementById("status").textContent="online",e.className="status-dot online",await Cn();const t=await a("/api/dlq"),n=document.getElementById("dlqBadge");t.length?(n.textContent=t.length,n.classList.remove("hidden")):n.classList.add("hidden")}catch(e){document.getElementById("status").textContent="error",document.getElementById("statusDot").className="status-dot error"}}async function Rn(){await On()}function Fn(e){document.querySelectorAll(".nav-item").forEach(e=>e.classList.remove("active"));const t=document.getElementById(e);t&&t.classList.add("active")}function Hn(){u(o.activeTab),document.querySelectorAll(".view, .view-sessions").forEach(e=>{e.classList.remove("active"),e.style.display&&(e.style.display="")});const e=document.querySelector(".msg-bar");e&&(e.style.display="")}async function Dn(e){const t=document.getElementById(e),n=encodeURIComponent((null==t?void 0:t.value)||window._crewHome||""),o=await a("/api/pick-folder?default="+n).catch(()=>null);(null==o?void 0:o.path)&&t&&(t.value=o.path)}function zn(){const e=(location.hash||"#chat").slice(1),t=(e.split("?")[0]||"chat").split("/");return{view:t[0]||"chat",subtab:t[1],raw:e}}function Gn(){return zn().view}function $n(e){const t=e&&String(e).trim()&&"undefined"!==e?e:"general",n=`#chat?project=${encodeURIComponent(t)}`;location.hash!==n&&history.replaceState(null,"",n)}async function Vn(){try{const e=await a("/api/ui/active-project");return String((null==e?void 0:e.projectId)||"").trim()||"general"}catch{return"general"}}async function qn(e){const t=e&&String(e).trim()&&"undefined"!==e?String(e).trim():"general";try{await r("/api/ui/active-project",{projectId:t})}catch{}}async function Kn(){Hn(),document.getElementById("chatView").classList.add("active"),Fn("navChat"),o.activeTab="chat",s();const e=document.querySelector(".msg-bar");e&&(e.style.display="none"),y();const t=document.getElementById("chatMessages");"true"===(null==t?void 0:t.dataset.historyLoading)&&await io();const n=t&&"true"===t.dataset.historyLoaded&&t.children.length>0;try{const e=(await a("/api/projects")).projects||[];o.projectsData={},e.forEach(e=>{o.projectsData[e.id]=e}),s(),tn(e)}catch(c){console.warn("Failed to refresh projects dropdown:",c)}const i=new URLSearchParams(window.location.hash.replace(/^#chat\?/,"")).get("project");if(i)o.chatActiveProjectId=i;else{const e=await Vn();try{o.chatActiveProjectId=e||localStorage.getItem("crewswarm_chat_active_project_id")||"general"}catch{o.chatActiveProjectId=e||"general"}}window.location.hash.includes("?project=")||$n(o.chatActiveProjectId),console.log("🔵 [INIT] Active project from URL:",o.chatActiveProjectId);const r=document.getElementById("chatProjectTabs");r&&Array.from(r.children).forEach(e=>{e.dataset.projectId===o.chatActiveProjectId?e.classList.add("active"):e.classList.remove("active")});const l=document.getElementById("chatProjectSelect");l&&o.chatActiveProjectId&&l.querySelector('option[value="'+o.chatActiveProjectId+'"]')&&(l.value=o.chatActiveProjectId),qn(o.chatActiveProjectId),Cn(),Qn(),async function(){try{const e=((await a("/api/agents-config")).agents||[]).find(e=>"crew-lead"===e.id);if(!e)return;window._crewLeadInfo={emoji:e.emoji||"🧠",name:e.name||"crew-lead",theme:e.theme||""};const t=document.getElementById("chatAgentTitle"),n=document.getElementById("chatAgentSub");t&&(t.textContent=(e.emoji||"🧠")+" "+(e.name||"Crew Lead")),n&&e.theme&&(n.textContent=e.theme+" — chat naturally, dispatch tasks to the crew")}catch(c){}}(),window.loadChatAgentSelector&&window.loadChatAgentSelector(),n?m("chat"):await so()}function Un(){Hn(),document.getElementById("filesView").classList.add("active"),Fn("navFiles"),o.activeTab="files",s(),bo()}function Wn(){return"owner"}P({hideAllViews:Hn,setNavActive:Fn}),O({hideAllViews:Hn,setNavActive:Fn,refreshAgents:F}),ce({hideAllViews:Hn,setNavActive:Fn}),$e({hideAllViews:Hn,setNavActive:Fn}),C(),E({hideAllViews:Hn,setNavActive:Fn}),pe({hideAllViews:Hn,setNavActive:Fn});let Jn=null;function Qn(){if(Jn)return;const e=`http://${window.location.hostname||"127.0.0.1"}:5010/events`;console.log("[crewswarm] Starting EventSource listener for",e),Jn=new EventSource(e);const t="undefined"!=typeof localStorage&&"1"===localStorage.getItem("crewswarm_debug_sse");Jn.onmessage=e=>{if(e.data)try{const a=JSON.parse(e.data),s=e=>e&&"general"!==e?e:"general",r="owner";t&&console.log("[crewswarm] SSE:",a.type,e.data.slice(0,120));const l=document.getElementById("chatMessages");if(k(a))return;if("chat_stream"===a.type&&a.sessionId===r){const e=s(a.projectId);if(s(o.chatActiveProjectId)!==e)return;let t=document.getElementById("streaming-bubble");if(!t){const e=document.createElement("div");e.id="streaming-wrapper",e.style.cssText="display:flex;flex-direction:column;align-items:flex-start;gap:4px;";const n=document.createElement("div");n.style.cssText="font-size:11px;color:var(--text-3);padding:0 6px;";const o=window._crewLeadInfo||{emoji:"🧠",name:"crew-lead"};n.textContent=o.emoji+" "+o.name+" (streaming...)",t=document.createElement("div"),t.id="streaming-bubble",t.className="chat-bubble assistant",t.style.cssText="max-width:80%;padding:10px 14px;border-radius:14px 14px 14px 4px;background:var(--surface-2);color:var(--text-2);font-size:14px;line-height:1.5;white-space:pre-wrap;word-break:break-word;border:1px solid var(--border);",t._textNode=document.createTextNode(""),t.appendChild(t._textNode),e.appendChild(n),e.appendChild(t),l&&l.appendChild(e)}const n=(t.dataset.streamChunk||"")+a.token;return t.dataset.streamChunk=n,void(t._rafId||(t._rafId=requestAnimationFrame(()=>{const e=t.dataset.streamChunk||"";e&&(t._textNode||(t._textNode=document.createTextNode(""),t.appendChild(t._textNode)),t._textNode.textContent+=e,t.dataset.streamChunk=""),l&&(l.scrollTop=l.scrollHeight),t._rafId=null})))}if("draft_discarded"===a.type&&a.draftId){const e=document.querySelector('[data-draft-id="'+a.draftId+'"]');return void(e&&e.remove())}if("context_warning"===a.type&&"owner"===a.sessionId){const e=document.getElementById("contextWarningBanner");e&&e.remove();const t=document.createElement("div");t.id="contextWarningBanner";const n="critical"===a.level;t.style.cssText=`display:flex;align-items:center;gap:10px;padding:8px 14px;border-radius:8px;margin:6px 0;font-size:12px;background:${n?"rgba(239,68,68,0.1)":"rgba(245,158,11,0.1)"};border:1px solid ${n?"rgba(239,68,68,0.3)":"rgba(245,158,11,0.3)"};color:${n?"#f87171":"#f59e0b"};`,t.innerHTML=`<span style="flex:1;">${a.message}</span><button onclick="clearChatHistory()" style="padding:2px 8px;font-size:11px;border-radius:4px;border:1px solid currentColor;background:transparent;color:inherit;cursor:pointer;">Clear now</button><button onclick="this.parentElement.remove()" style="background:none;border:none;cursor:pointer;color:inherit;font-size:14px;padding:0 2px;">✕</button>`;const o=document.getElementById("chatMessages");return void(o&&(o.appendChild(t),o.scrollTop=o.scrollHeight))}if("chat_message"===a.type&&"owner"===a.sessionId){const e=s(o.chatActiveProjectId),t=s(a.projectId);if(e!==t)return void console.log("[crewswarm] ❌ SKIP - projectId mismatch:",{current:e||"(General)",message:t||"(General)"});if(console.log("[crewswarm] ✅ Displaying message for current session"),"user"===a.role){if(a.content===ao)return console.log("[crewswarm] Skipping SSE echo of locally-sent message"),void(ao=null);a.content!==oo?(console.log("[crewswarm] Appending user bubble:",a.content.slice(0,50)),g("user",a.content),oo=a.content):console.log("[crewswarm] Skipping duplicate user message")}else if("assistant"===a.role){document.querySelectorAll('[id^="typing-"]').forEach(e=>e.remove());const e=String(a.content||"").trim();if(e&&function(e){if(!e)return"";for(let t=e.children.length-1;t>=0;t--){const n=e.children[t];if("streaming-wrapper"!==n.id&&!(n.children.length<2)&&String(n.style.alignItems||"").includes("flex-start"))return(n.children[1].textContent||"").trim()}return""}(l)===e)return no=a.content,void(l&&(l.scrollTop=l.scrollHeight));const t=document.getElementById("streaming-wrapper"),n=document.getElementById("streaming-bubble");if(n){n._rafId&&cancelAnimationFrame(n._rafId),n._rafId=null;const e=n.dataset.streamChunk||"";e&&(n._textNode||(n._textNode=document.createTextNode(""),n.appendChild(n._textNode)),n._textNode.textContent+=e,n.dataset.streamChunk="")}if(t&&n){const e=window._crewLeadInfo||{emoji:"🧠",name:"crew-lead"},o=t.firstElementChild;o&&o!==n&&(o.textContent=e.emoji+" "+e.name);const s=a.content??"";n._textNode?n._textNode.textContent=s:n.textContent=s,t.removeAttribute("id"),n.removeAttribute("id"),delete n.dataset.streamChunk,no=a.content}else{t&&t.remove();const e=a.content===no&&function(e,t){if(!e||null==t)return!1;const n=String(t).trim();if(!n)return!1;for(let o=e.children.length-1;o>=0;o--){const t=e.children[o];if("streaming-wrapper"!==t.id&&(!(t.children.length<2)&&String(t.style.alignItems||"").includes("flex-start")&&(t.children[1].textContent||"").trim()===n))return!0}return!1}(l,a.content);e?console.log("[crewswarm] Skipping duplicate assistant message"):(console.log("[crewswarm] Appending assistant bubble (final)"),g("assistant",a.content,a.fallbackModel,a.fallbackReason,a.model,a.engineUsed),no=a.content)}}return void(l&&(l.scrollTop=l.scrollHeight))}if("pending_project"===a.type&&"owner"===a.sessionId&&a.pendingProject&&l)return to(l,a.pendingProject),void(l.scrollTop=l.scrollHeight);if("agent_working"===a.type&&a.agent){const e=document.getElementById("coding-dot-"+a.agent);e&&(e.style.display="inline-flex")}if("agent_idle"===a.type&&a.agent){const e=document.getElementById("coding-dot-"+a.agent);e&&(e.style.display="none")}if("opencode_event"===a.type){const e=document.getElementById("ocFeed"),t=document.getElementById("ocFeedDot");if(!e)return;t&&(t.style.display="inline-block");const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:8px;padding:5px 10px;border-radius:8px;background:var(--bg-2);font-size:12px;font-family:var(--font-mono,monospace);animation:fadeIn .25s ease;";const s=new Date(a.ts||Date.now()).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"});let i="⚙️",r="";if("session_start"===a.kind){i="▶",o.style.borderLeft="3px solid var(--green-hi)";var n=a.dir||"";r="session started"+(n?" — "+n.split("/").pop():"")}else if("session_end"===a.kind)i="■",o.style.borderLeft="3px solid var(--text-3)",r="session ended",t&&(t.style.display="none");else if("file_edit"===a.kind)i="✏️",o.style.borderLeft="3px solid var(--amber)",r=(a.file||a.path||"")+(a.extra?' <span style="opacity:.5;">'+a.extra+"</span>":"");else if("error"===a.kind)i="✗",o.style.borderLeft="3px solid var(--red-hi)",o.style.color="var(--red-hi)",r=a.message||"error";else if("tool"===a.kind){const e={read_file:"var(--accent)",write_file:"var(--amber)",bash:"var(--purple)",list_directory:"var(--green)",grep:"var(--green)"}[a.tool]||"var(--text-2)";i="done"===a.phase?"✓":"→",o.style.borderLeft="3px solid "+e,o.style.color="done"===a.phase?"var(--text-2)":"var(--text-1)",r='<span style="color:'+e+';font-weight:600;">'+(a.tool||"")+"</span>"+(a.label?' <span style="opacity:.6;">'+a.label+"</span>":"")}for(o.innerHTML='<span style="opacity:.4;flex-shrink:0;">'+s+'</span><span style="flex-shrink:0;">'+i+'</span><span style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">'+r+"</span>",e.appendChild(o);e.children.length>80;)e.removeChild(e.firstChild);return void(e.scrollTop=e.scrollHeight)}if("agent_working"===a.type&&a.agent){const e="agent-spinner-"+(a.taskId||a.agent);if(l&&!document.getElementById(e)){const t=document.createElement("div");t.id=e,t.className="msg a",t.style.cssText="opacity:.7; font-style:italic;",t.innerHTML='<div class="meta"><strong>'+a.agent+'</strong> · working…</div><div class="t" style="display:flex;align-items:center;gap:8px;"><span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--accent);animation:pulse 1s ease-in-out infinite;"></span>Processing task…</div>',l.appendChild(t),l.scrollTop=l.scrollHeight}return}if("agent_reply"===a.type||a.from&&a.content){if(!a.from||!a.content)return;if(a._passthroughSummary)return;const e="agent-spinner-"+(a.taskId||a.from),t=document.getElementById(e);t&&t.remove();const n=document.getElementById("agent-spinner-"+a.from);return n&&n.remove(),g("🤖 "+a.from,a.content,!1,null,null,a.engineUsed),l&&(l.scrollTop=l.scrollHeight),void i(a.from+" finished a task")}if("task.timeout"===a.type&&a.agent){const e="agent-spinner-"+(a.taskId||a.agent),t=document.getElementById(e);t&&t.remove();const n=document.getElementById("agent-spinner-"+a.agent);n&&n.remove();const o="[crew-lead] Task to "+a.agent+" timed out (no reply in 90s). Consider @@SERVICE restart "+a.agent+" or re-dispatch to another agent.";if(l){const e=document.createElement("div");e.className="msg a",e.style.cssText="opacity:.85; font-style:italic; color:var(--text-3);",e.innerHTML='<div class="meta"><strong>'+a.agent+'</strong> · no reply</div><div class="t">'+h(o)+"</div>",l.appendChild(e),l.scrollTop=l.scrollHeight}return void i("Task to "+a.agent+" timed out")}if("pipeline_progress"===a.type){let e;e=a.agents?"Wave "+(a.waveIndex+1)+"/"+a.totalWaves+" → "+a.agents.join(" + "):"Step "+(a.stepIndex+1)+"/"+a.total+" → "+a.agent;const t=document.createElement("div");return t.style.cssText="font-size:11px;color:var(--text-3);padding:2px 8px;margin:2px 0;",t.textContent="↳ "+e,void(l&&(l.appendChild(t),l.scrollTop=l.scrollHeight))}if("pipeline_quality_gate"===a.type){const e=document.createElement("div"),t=a.willRetry?" — retrying wave":" — advancing anyway";return e.style.cssText="font-size:11px;color:var(--warning, #e8a030);padding:2px 8px;margin:2px 0;",e.textContent="⚠️ Wave "+(a.waveIndex+1)+" quality gate: "+(a.issues||[]).join("; ")+t,void(l&&(l.appendChild(e),l.scrollTop=l.scrollHeight))}if("project_launched"===a.type&&a.project){const e=a.project.projectId||a.project.id;return void setTimeout(async()=>{await ln(),e&&bn(e);const t=document.getElementById("chatMessages");if(t){const n=document.createElement("div");n.style.cssText="font-size:11px;color:var(--green);padding:2px 8px;margin:2px 0;",n.textContent='📁 Project "'+(a.project.name||e)+'" registered — selected in chat',t.appendChild(n),t.scrollTop=t.scrollHeight}},800)}if("pipeline_done"===a.type){const e=document.createElement("div");return e.style.cssText="font-size:11px;color:var(--green);padding:2px 8px;margin:2px 0;",e.textContent="✅ Pipeline complete",void(l&&(l.appendChild(e),l.scrollTop=l.scrollHeight))}if("confirm_run_cmd"===a.type&&a.approvalId)return void function(e,t,n){if(document.getElementById("cmd-approval-"+e))return;const o=document.createElement("div");o.id="cmd-approval-"+e,o.style.cssText=["position:fixed;bottom:80px;right:24px;z-index:9999;","background:var(--bg-card);border:1px solid var(--border);border-radius:12px;","padding:16px 20px;max-width:440px;box-shadow:0 8px 32px rgba(0,0,0,.4);","display:flex;flex-direction:column;gap:10px;"].join("");const a=document.createElement("div");a.style.cssText="font-size:13px;font-weight:600;color:var(--text-1);",a.textContent="🔐 "+t+" wants to run a command";const s=document.createElement("code");s.style.cssText="display:block;font-size:12px;color:var(--accent);background:var(--bg-1);padding:6px 10px;border-radius:6px;word-break:break-all;",s.textContent=n;const r=document.createElement("label");r.style.cssText="display:flex;align-items:center;gap:8px;font-size:12px;color:var(--text-2);cursor:pointer;";const l=document.createElement("input");l.type="checkbox",l.style.cssText="width:14px;height:14px;cursor:pointer;accent-color:var(--green);";const c=n.trim().split(/\s+/)[0]+" *";r.appendChild(l),r.appendChild(document.createTextNode("Always allow "));const d=document.createElement("code");d.style.cssText="font-size:11px;background:var(--bg-1);padding:2px 6px;border-radius:4px;color:var(--accent);",d.textContent=c,r.appendChild(d);const p=document.createElement("div");p.style.cssText="font-size:11px;color:var(--text-3);";let m=60;p.textContent="Auto-reject in "+m+"s";const u=setInterval(()=>{m--,p.textContent="Auto-reject in "+m+"s",m<=0&&(clearInterval(u),o.remove())},1e3),g=document.createElement("div");g.style.cssText="display:flex;gap:8px;";const h=document.createElement("button");h.textContent="✅ Allow",h.style.cssText="flex:1;padding:8px;border-radius:8px;border:none;background:var(--green);color:#fff;cursor:pointer;font-weight:600;font-size:13px;",h.onclick=async()=>{clearInterval(u),o.remove(),l.checked&&(await fetch("/api/cmd-allowlist",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({pattern:c})}),i("Allowlisted: "+c)),await fetch("/api/cmd-approve",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({approvalId:e})}).catch(e=>i("Approve failed: "+e.message,!0)),l.checked||i(t+": command approved")};const v=document.createElement("button");v.textContent="⛔ Deny",v.style.cssText="flex:1;padding:8px;border-radius:8px;border:none;background:var(--red-hi);color:#fff;cursor:pointer;font-weight:600;font-size:13px;",v.onclick=async()=>{clearInterval(u),o.remove(),await fetch("/api/cmd-reject",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({approvalId:e})}).catch(e=>i("Reject failed: "+e.message,!0)),i(t+": command denied")},g.appendChild(h),g.appendChild(v),o.appendChild(a),o.appendChild(s),o.appendChild(r),o.appendChild(p),o.appendChild(g),document.body.appendChild(o)}(a.approvalId,a.agent,a.cmd);if("telemetry"===a.type&&a.payload){window._telemetryEvents=window._telemetryEvents||[],window._telemetryEvents.push(a.payload),window._telemetryEvents.length>100&&window._telemetryEvents.shift();const e=document.getElementById("toolMatrixView");e&&e.classList.contains("active")&&Tn(window._telemetryEvents)}}catch{}else console.warn("[crewswarm] SSE message with null/empty data")},Jn.onopen=()=>{console.log("[crewswarm] SSE connection opened"),window._sseReconnectDelay=2e3},Jn.onerror=e=>{console.error("[crewswarm] SSE error:",e),Jn.close(),Jn=null,window._sseReconnectTimer&&clearTimeout(window._sseReconnectTimer),window._sseReconnectTimer=setTimeout(()=>{window._sseReconnectTimer=null,window._sseReconnectDelay=Math.min(2*(window._sseReconnectDelay||2e3),3e4),Qn()},window._sseReconnectDelay||2e3)}}const Yn=[{label:"npm",pattern:"npm *",desc:"install, run, build, test…"},{label:"node",pattern:"node *",desc:"run any node script"},{label:"python",pattern:"python *",desc:"python / python3 scripts"},{label:"pip",pattern:"pip *",desc:"pip install packages"},{label:"git",pattern:"git *",desc:"all git operations"},{label:"cursor",pattern:"cursor *",desc:"open files in Cursor"},{label:"make",pattern:"make *",desc:"Makefile targets"},{label:"yarn",pattern:"yarn *",desc:"yarn install / build / run"},{label:"pnpm",pattern:"pnpm *",desc:"pnpm package manager"},{label:"ls / cat / echo",pattern:"ls *",desc:"read-only shell utilities"}];async function Xn(){const e=document.getElementById("cmdAllowlistItems"),t=document.getElementById("cmdPresets");if(!e)return;const n=(await a("/api/cmd-allowlist").catch(()=>({list:[]}))).list||[];t&&(t.innerHTML="",Yn.forEach(function(e){const o=n.includes(e.pattern),a=document.createElement("label");a.style.cssText="display:flex;align-items:center;gap:8px;cursor:pointer;padding:4px 6px;border-radius:6px;transition:background 0.1s;",a.onmouseover=function(){a.style.background="var(--bg-hover)"},a.onmouseout=function(){a.style.background=""};const s=document.createElement("input");s.type="checkbox",s.checked=o,s.style.cssText="width:14px;height:14px;cursor:pointer;accent-color:var(--green);flex-shrink:0;",s.onchange=async function(){s.checked?await fetch("/api/cmd-allowlist",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({pattern:e.pattern})}).catch(e=>i("Failed to add pattern: "+e.message,!0)):await fetch("/api/cmd-allowlist",{method:"DELETE",headers:{"content-type":"application/json"},body:JSON.stringify({pattern:e.pattern})}).catch(e=>i("Failed to remove pattern: "+e.message,!0)),Xn()};const r=document.createElement("code");r.style.cssText="font-size:12px;color:var(--accent);min-width:90px;",r.textContent=e.pattern;const l=document.createElement("span");l.style.cssText="font-size:11px;color:var(--text-3);",l.textContent=e.desc,a.appendChild(s),a.appendChild(r),a.appendChild(l),t.appendChild(a)}));const o=new Set(Yn.map(function(e){return e.pattern})),s=t?n.filter(function(e){return!o.has(e)}):n;if(e.innerHTML="",s.length)for(const a of s){const t=document.createElement("div");t.style.cssText="display:flex;align-items:center;gap:8px;padding:5px 0;border-bottom:1px solid var(--border);";const n=document.createElement("code");n.style.cssText="flex:1;font-size:12px;color:var(--accent);",n.textContent=a;const o=document.createElement("button");o.textContent="✕",o.style.cssText="border:none;background:transparent;color:var(--text-3);cursor:pointer;font-size:14px;padding:0 4px;",o.title="Remove",o.onclick=async function(){await fetch("/api/cmd-allowlist",{method:"DELETE",headers:{"content-type":"application/json"},body:JSON.stringify({pattern:a})}).catch(e=>i("Failed to delete pattern: "+e.message,!0)),Xn()},t.appendChild(n),t.appendChild(o),e.appendChild(t)}else e.innerHTML='<div style="color:var(--text-3);font-size:12px;padding:4px 0;">'+(t?"No custom patterns yet.":"No patterns yet.")+"</div>"}async function Zn(){const e=document.getElementById("cmdAllowlistInput"),t=e?e.value.trim():"";t&&(await fetch("/api/cmd-allowlist",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({pattern:t})}).catch(e=>i("Failed to add pattern: "+e.message,!0)),e.value="",Xn())}window._telemetryEvents=window._telemetryEvents||[];const eo=()=>In(Pn);function to(e,{draftId:t,name:n,outputDir:o,roadmapMd:a}){function s(e){return(e.match(/^- \[ \]/gm)||[]).length}const i=document.createElement("div");i.setAttribute("data-draft-id",t),i.style.cssText="width:100%;display:flex;flex-direction:column;gap:4px;";const l=document.createElement("div");l.style.cssText="font-size:11px;color:var(--text-3);padding:0 6px;",l.textContent="🗺️ Roadmap draft — review before building";const c=document.createElement("div");c.style.cssText="width:100%;border:1px solid var(--border);border-radius:12px;overflow:hidden;background:var(--bg-card);";const d=document.createElement("div");d.style.cssText="background:var(--bg-card2);padding:10px 14px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--border);",d.innerHTML='<div><div style="font-size:13px;font-weight:600;color:var(--accent);">🚀 '+n+'</div><div style="font-size:11px;color:var(--blue);margin-top:2px;">'+o+'</div></div><span style="font-size:10px;color:var(--text-3);padding:2px 7px;background:var(--bg-card2);border-radius:10px;" class="task-count">'+s(a)+" tasks</span>";const p=document.createElement("textarea");p.value=a,p.spellcheck=!1,p.style.cssText="width:100%;background:var(--bg-card);border:none;outline:none;color:var(--text-1);font-size:11.5px;font-family:SF Mono,Monaco,Menlo,monospace;line-height:1.6;padding:12px 14px;resize:none;min-height:160px;max-height:320px;display:block;",setTimeout(()=>{p.style.height="",p.style.height=Math.min(p.scrollHeight,320)+"px"},50),p.addEventListener("input",()=>{p.style.height="",p.style.height=Math.min(p.scrollHeight,320)+"px",d.querySelector(".task-count").textContent=s(p.value)+" tasks"});const m=document.createElement("div");m.style.cssText="display:flex;gap:8px;align-items:center;padding:10px 14px 12px;border-top:1px solid var(--border);background:var(--bg-card2);";const u=document.createElement("button");u.textContent="▶ Start Building",u.style.cssText="background:var(--green-hi);color:#000;border:none;border-radius:8px;padding:8px 16px;font-size:12px;font-weight:700;cursor:pointer;",u.onclick=async()=>{u.disabled=!0,u.textContent="⏳ Launching…";try{const e=await r("/api/crew-lead/confirm-project",{draftId:t,roadmapMd:p.value});e.ok?(c.innerHTML='<div style="padding:14px;color:var(--green-hi);font-size:13px;font-weight:600;">✅ '+n+' — project created, PM loop running!<br><span style="color:var(--blue);font-size:11px;font-weight:400">'+(e.outputDir||o)+"</span></div>",g("assistant","🚀 "+n+" is building. Check the Projects tab to watch progress.")):(u.disabled=!1,u.textContent="▶ Start Building",v.textContent="⚠️ "+(e.error||"Launch failed"))}catch(e){u.disabled=!1,u.textContent="▶ Start Building",v.textContent="⚠️ "+e.message}};const h=document.createElement("button");h.textContent="Discard",h.style.cssText="background:none;border:1px solid var(--border);color:var(--text-3);border-radius:8px;padding:8px 14px;font-size:12px;cursor:pointer;",h.onclick=async()=>{await r("/api/crew-lead/discard-project",{draftId:t}).catch(()=>{}),i.remove()};const v=document.createElement("span");v.style.cssText="font-size:11px;color:var(--blue);margin-left:auto;",v.textContent="Edit above, then confirm",m.appendChild(u),m.appendChild(h),m.appendChild(v),c.appendChild(d),c.appendChild(p),c.appendChild(m),i.appendChild(l),i.appendChild(c),e.appendChild(i),e.scrollTop=e.scrollHeight}let no="",oo="",ao=null;const{loadChatHistory:so,waitForChatHistoryIdle:io,chatAtAtInput:ro,chatKeydown:lo,sendChat:co,clearChatHistory:po,stopAll:mo,killAll:uo,killPassthrough:go,refreshSessionIndicator:ho,clearPassthroughSession:vo,resetSendButton:fo,handleImageUpload:yo,toggleVoiceRecording:wo}=w({postJSON:r,getJSON:a,appendChatBubble:g,showNotification:i,state:o,getChatSessionId:()=>"owner",getChatActiveProjectId:()=>o.chatActiveProjectId,getCrewLeadInfo:()=>window._crewLeadInfo,appendRoadmapCard:to,getLastAppendedAssistantContent:()=>no,setLastAppendedAssistantContent:e=>{no=e},setLastAppendedUserContent:e=>{oo=e},setLastSentContent:e=>{ao=e}});async function bo(e){const t=document.getElementById("filesContent"),n=document.getElementById("filesDir").value.trim()||window._crewCwd||(window._crewHome?window._crewHome+"/CrewSwarm":"");d(t,"Scanning "+n+"...");try{const e=await a("/api/files?dir="+encodeURIComponent(n));if(!e.files||!e.files.length)return void c(t,"No files found in "+n);const o={};e.files.forEach(e=>{const t=e.path.split(".").pop().toLowerCase()||"other";o[t]||(o[t]=[]),o[t].push(e)});const s=["html","css","js","mjs","ts","json","md","sh","txt","other"],i={html:"🌐",css:"🎨",js:"⚡",mjs:"⚡",ts:"🔷",json:"📋",md:"📝",sh:"🖥️",txt:"📄",other:"📁"};let r='<div style="display:grid;gap:1rem;padding:4px 0;">';for(const t of s)o[t]&&(r+="<div>",r+='<div style="font-size:11px;font-weight:600;color:var(--text-2);text-transform:uppercase;letter-spacing:0.08em;margin-bottom:8px;padding-left:2px;">'+(i[t]||"📁")+" ."+t+" — "+o[t].length+" file"+(o[t].length>1?"s":"")+"</div>",r+='<div style="display:grid;gap:6px;">',o[t].sort((e,t)=>t.mtime-e.mtime).forEach(e=>{const t=e.path.replace(n+"/",""),o=Co(e.mtime),a=Eo(e.size);r+='<div class="file-row">',r+='<div class="file-info"><span class="file-name">'+t+'</span><span class="file-meta">'+a+" · "+o+"</span></div>",r+='<div class="file-actions">',r+='<a href="cursor://file/'+e.path+'" class="file-btn file-btn-cursor" title="Open in Cursor">Cursor</a>',r+='<a href="opencode://open?path='+encodeURIComponent(e.path)+'" class="file-btn file-btn-opencode" title="Open in OpenCode">OpenCode</a>',r+='<button data-action="previewFile" data-arg=\''+e.path.replace(/'/g,"'")+'\' data-self="1" class="file-btn" title="Preview">👁</button>',r+="</div></div>"}),r+="</div></div>");r+="</div>",r+='<div id="file-preview-pane" style="display:none;margin-top:1rem;background:#0d1117;border:1px solid var(--border);border-radius:8px;overflow:hidden;"><div id="file-preview-bar" style="display:flex;align-items:center;gap:8px;padding:8px 12px;background:#0d1420;border-bottom:1px solid var(--border);font-size:12px;color:var(--text-2);"><span id="file-preview-name"></span><button data-action="closePreviewPane" style="margin-left:auto;background:none;border:none;color:var(--text-2);cursor:pointer;">✕</button></div><pre id="file-preview-content" style="margin:0;padding:1rem;font-size:0.75rem;overflow:auto;max-height:400px;"></pre></div>',t.innerHTML=r}catch(o){l(t,"Error: "+o.message)}}async function xo(e,t){const n=document.getElementById("file-preview-pane"),o=document.getElementById("file-preview-content"),s=document.getElementById("file-preview-name");if(n){s.textContent=e.split("/").pop(),o.textContent="Loading...",n.style.display="block",n.scrollIntoView({behavior:"smooth",block:"nearest"});try{const t=await a("/api/file-content?path="+encodeURIComponent(e));o.textContent=t.content||"(empty)"}catch(i){o.textContent="Error: "+i.message}}}function ko(){const e=document.getElementById("file-preview-pane");e&&(e.style.display="none")}function Co(e){const t=Date.now()-e,n=Math.floor(t/6e4);if(n<1)return"just now";if(n<60)return n+"m ago";const o=Math.floor(n/60);return o<24?o+"h ago":Math.floor(o/24)+"d ago"}function Eo(e){return e<1024?e+"B":e<1048576?(e/1024).toFixed(1)+"KB":(e/1024/1024).toFixed(1)+"MB"}function Io(){Hn(),document.getElementById("settingsView").classList.add("active"),Fn("navSettings"),o.activeTab="settings",s();const e=(location.hash||"").replace("#settings/",""),t={system:"engines",telegram:"comms",whatsapp:"comms"}[e]||e;To(["usage","engines","comms","security","webhooks"].includes(t)?t:"usage")}function To(e){var t;["usage","engines","comms","security","webhooks"].forEach(t=>{const n=document.getElementById("stab-panel-"+t),o=document.getElementById("stab-"+t);n&&o&&(n.style.display=t===e?"usage"===t?"grid":"block":"none",o.classList.toggle("active",t===e))}),"usage"===e&&(En(),An()),"engines"===e&&(Et(),It(),Tt(),Bt(),St(),jt(),At(),Pt(),Lt(),Mt(),_t(),Nt(),Ot(),Rt(),Ft()),"comms"===e&&Zt(),"security"===e&&(Xn(),Ht()),(null==(t=document.getElementById("settingsView"))?void 0:t.classList.contains("active"))&&history.replaceState(null,"","#settings/"+e)}function Bo(){Hn(),document.getElementById("enginesView").classList.add("active"),Fn("navEngines"),_e()}null==(e=document.getElementById("attachImageBtn"))||e.addEventListener("click",()=>{document.getElementById("imageUpload").click()}),null==(t=document.getElementById("imageUpload"))||t.addEventListener("change",yo),null==(n=document.getElementById("recordVoiceBtn"))||n.addEventListener("click",wo),window.loadChatHistory=so,window.getChatSessionId=Wn,window.selectProjectTab=e=>{const t=e&&String(e).trim()&&"undefined"!==e?e:"general",n=o.chatActiveProjectId;console.log("🔵 [TAB CLICK] START",t,"- from:",n);const a=document.getElementById("chatProjectTabs");if(!a)return void console.error("🔵 [TAB CLICK] ERROR: chatProjectTabs container not found!");if(n===t)return void console.log("🔵 [TAB CLICK] Already on this tab, skipping reload");$n(t),Array.from(a.children).forEach(e=>{e.classList.remove("active")});const s=Array.from(a.children).find(e=>e.dataset.projectId===t);s&&s.classList.add("active"),o.chatActiveProjectId=t;try{localStorage.setItem("crewswarm_chat_active_project_id",t)}catch{}qn(t),console.log("🔵 [TAB CLICK] Updated state:",{projectId:o.chatActiveProjectId,sessionId:"owner",url:window.location.hash}),console.log("🔵 [TAB CLICK] Calling loadChatHistory()..."),so().then(()=>{console.log("🔵 [TAB CLICK] loadChatHistory() completed");const e=document.getElementById("chatMessages");console.log("🔵 [TAB CLICK] Messages in DOM:",(null==e?void 0:e.children.length)||0)}).catch(e=>{console.error("🔵 [TAB CLICK] loadChatHistory() ERROR:",e)})},window.addEventListener("focus",()=>{(async function(){if("chat"!==zn().view)return;const e=await Vn(),t=e&&"undefined"!==e?e:"general";t!==(o.chatActiveProjectId&&"undefined"!==o.chatActiveProjectId?o.chatActiveProjectId:"general")&&window.selectProjectTab&&window.selectProjectTab(t)})().catch(()=>{})}),zt({showSettings:Io,showSettingsTab:To}),Dt({getModels:F,populateModelDropdown:le}),at({hideAllViews:Hn,setNavActive:Fn,loadAgents:F}),Ve(),b({hideAllViews:Hn,setNavActive:Fn});const So=async()=>{Hn(),Fn("navBenchmarks");const e=document.getElementById("benchmarksView");e&&e.classList.add("active");const{showBenchmarks:t}=await Nn();t({hideAllViews:Hn,setNavActive:Fn})};function jo(){Hn(),document.getElementById("memoryView").classList.add("active"),Fn("navMemory"),A()}function Ao(){Hn(),document.getElementById("cliProcessView").classList.add("active"),Fn("navCLI"),window.initCLIProcess&&window.initCLIProcess()}function Po(){Hn(),document.getElementById("toolMatrixView").classList.add("active"),Fn("navToolMatrix"),xn()}async function Lo(){const e=document.getElementById("webhookChannel").value.trim()||"test";let t={};try{const e=document.getElementById("webhookPayload").value.trim();e&&(t=JSON.parse(e))}catch{t={raw:document.getElementById("webhookPayload").value}}const n=document.getElementById("webhookTestResult");try{const o=await fetch("/proxy-webhook/"+e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),a=await o.json();n.textContent=a.ok?"✅ Sent to RT bus":"❌ "+(a.error||"failed"),n.style.color=a.ok?"var(--green)":"var(--red)"}catch(o){n.textContent="❌ "+o.message,n.style.color="var(--red)"}}async function Mo(){document.getElementById("pendingApprovals").innerHTML='<div style="color:var(--text-3);font-size:12px;">Pending skill approvals appear here when an agent triggers a skill marked requiresApproval. You will also receive a Telegram notification with inline Approve/Reject buttons if Telegram is configured.</div>'}function _o(){un({hideAllViews:Hn,setNavActive:Fn})}function No(){mn({hideAllViews:Hn,setNavActive:Fn})}en({showChat:Kn,showBuild:_o}),On(),setInterval(On,3e4),(async()=>{try{const e=(await a("/api/projects")).projects||[];o.projectsData={},e.forEach(e=>{o.projectsData[e.id]=e}),tn(e),s(),"#projects"===location.hash&&No()}catch{}})(),document.getElementById("refreshBtn").onclick=Rn,document.getElementById("runBuildBtn").onclick=nn,document.getElementById("continuousBuildBtn").onclick=on,document.getElementById("stopBuildBtn").onclick=an,document.getElementById("stopContinuousBtn").onclick=sn,document.getElementById("enhancePromptBtn").onclick=rn,Ln(),document.getElementById("newProjectBtn").onclick=()=>{const e=document.getElementById("newProjectForm");e.style.display="none"===e.style.display?"block":"none"},document.getElementById("npCancelBtn").onclick=()=>{document.getElementById("newProjectForm").style.display="none"},document.getElementById("npCreateBtn").onclick=async()=>{const e=document.getElementById("npName").value.trim(),t=document.getElementById("npDesc").value.trim(),n=document.getElementById("npOutputDir").value.trim(),o=document.getElementById("npFeaturesDoc").value.trim();if(e&&n)try{const a=await r("/api/projects",{name:e,description:t,outputDir:n,featuresDoc:o});i(`Project "${a.project.name}" created!`),document.getElementById("newProjectForm").style.display="none",document.getElementById("npName").value="",document.getElementById("npDesc").value="",document.getElementById("npOutputDir").value="",document.getElementById("npFeaturesDoc").value="",ln()}catch(a){i("Failed: "+a.message,!0)}else i("Name and output directory required",!0)};const Oo={chat:Kn,"swarm-chat":x,swarm:Fe,rt:Re,dlq:Oe,files:Un,services:L,agents:R,models:qe,settings:Io,engines:Bo,skills:ve,"run-skills":he,benchmarks:So,"tool-matrix":Po,build:_o,messaging:Gt,projects:No,contacts:Se,memory:jo,workflows:I,"cli-process":Ao,prompts:de,testing:me};for(const[zo,Go]of Object.entries(Oo)){const e=Go,t=function(...t){const n=location.hash||"";return"chat"===zo?n.startsWith("#chat")||history.replaceState(null,"","#chat"):history.replaceState(null,"","#"+zo),e(...t)};Oo[zo]=t,window[e.name]=t}function Ro(e){const t=String(e||"chat").split("?")[0].split("/")[0];(Oo[t]||Oo.chat)()}(async()=>{if(await v())return;const{view:e,subtab:t}=zn();"1"===new URLSearchParams(window.location.search).get("focus")?setTimeout(()=>{const e=document.getElementById("chatInput");e&&(Ro("chat"),e.focus())},500):(Ro(e||"chat"),"settings"===e&&t&&To(t))})(),window.addEventListener("hashchange",()=>{const{view:e,subtab:t}=zn(),n=Do[e];n?(n(),"settings"===e&&t&&To(t)):Kn()}),fetch("/api/env").then(e=>e.json()).then(e=>{window._crewHome=e.HOME||"",window._crewCwd=e.cwd||"";const t=document.getElementById("filesDir");t&&!t.value&&(t.value=e.cwd||"")}).catch(()=>{}),F().catch(e=>console.error("Initial agents-config load failed:",e)),Rn(),function(){function e(e){if(e.closest("form"))return;const t=document.createElement("form");t.autocomplete="off",t.onsubmit=()=>!1,t.style.cssText="margin:0;padding:0;display:contents;";const n=document.createElement("input");n.type="text",n.autocomplete="username",n.setAttribute("aria-hidden","true"),n.style.cssText="display:none;position:absolute;width:0;height:0;opacity:0;",t.appendChild(n),e.parentNode.insertBefore(t,e),t.appendChild(e)}function t(t){(t||document).querySelectorAll('input[type="password"]').forEach(e)}t();new MutationObserver(n=>{for(const o of n)for(const n of o.addedNodes)1===n.nodeType&&(n.matches&&n.matches('input[type="password"]')?e(n):t(n))}).observe(document.body,{childList:!0,subtree:!0})}();const Fo={showChat:Kn,showSwarm:Fe,showRT:Re,showBuild:_o,showFiles:Un,showDLQ:Oe,showProjects:No,showAgents:R,showModels:qe,showEngines:Bo,showSkills:ve,showRunSkills:he,showBenchmarks:So,showToolMatrix:Po,showServices:L,showTesting:me,refreshTesting:()=>me(),runTests:e=>ge(e),loadRunDetail:e=>ue(e),showSettings:Io,pickFolder:e=>Dn(e),loadFiles:e=>bo(),clearChatHistory:po,clearAgentChat:()=>{const e=document.getElementById("agentChatSelector"),t=document.getElementById("agentChatMessages"),n=document.getElementById("agentChatInput");t&&(t.innerHTML='<div class="empty-state">No messages yet. Start chatting!</div>'),n&&(n.value=""),(null==e?void 0:e.value)&&i("Chat history cleared","success")},sendChat:co,stopAll:mo,killAll:uo,stopPassthrough:go,clearPassthroughSession:vo,loadServices:M,saveRTToken:kt,lockConfig:xt,unlockConfig:bt,startCrew:H,toggleEmojiPicker:e=>oe(e),bulkSetRoute:(e,t)=>ae(e,t),loadSpending:jn,resetSpending:Sn,saveGlobalCaps:Bn,loadOcStats:eo,addAllowlistPattern:Zn,sendTestWebhook:Lo,startTgBridge:Yt,stopTgBridge:Qt,saveTgConfig:Jt,loadTelegramSessions:Wt,loadTgMessages:Ut,startWaBridge:Kt,stopWaBridge:qt,saveWaConfig:Vt,loadWaMessages:$t,saveOpencodeSettings:wt,saveOpencodeModel:yt,saveGlobalFallback:ft,toggleBgConsciousness:vt,toggleCursorWaves:ht,toggleTmuxBridge:gt,toggleAutonomousMentions:ut,toggleClaudeCode:mt,toggleCodexExecutor:pt,toggleGeminiCliExecutor:dt,toggleCrewCliExecutor:ct,toggleOpencodeExecutor:lt,saveGlobalOcLoop:rt,saveGlobalOcLoopRounds:it,savePassthroughNotify:st,toggleAddSkill:ke,toggleImportSkill:xe,importSkillFromUrl:be,showSkills:ve,saveSkill:we,cancelSkillForm:ye,loadRunSkills:fe,loadBenchmarks:async()=>(await Nn()).loadBenchmarks(),loadBenchmarkLeaderboard:async()=>(await Nn()).loadBenchmarkLeaderboard(),loadBenchmarkTasks:async()=>(await Nn()).loadBenchmarkTasks(),onBenchmarkTaskSelect:async e=>(await Nn()).onBenchmarkTaskSelect(e),runBenchmarkTask:async()=>(await Nn()).runBenchmarkTask(),stopBenchmarkRun:async()=>(await Nn()).stopBenchmarkRun(),loadMemoryStats:j,searchMemory:S,migrateMemory:B,compactMemory:T,loadEngines:_e,toggleImportEngine:Me,importEngineFromUrl:Le,deleteEngine:e=>Ne(e),loadToolMatrix:xn,loadBuildProjectPicker:cn,scrollRTToBottom:()=>{const e=document.getElementById("rtView");e&&(e.scrollTop=e.scrollHeight)},toggleRTPause:De,clearRTMessages:He,togglePmAdvanced:()=>{const e=document.getElementById("pmAdvanced");e&&(e.style.display="none"===e.style.display?"block":"none")},toggleRTTokenVis:()=>{const e=document.getElementById("rtTokenInput");e&&(e.type="password"===e.type?"text":"password")},restartService:e=>N(e),stopService:e=>_(e),closePreviewPane:ko,previewFile:(e,t)=>xo(e),replayDLQ:e=>Ge(e),deleteDLQ:e=>ze(e),runSkillFromUI:e=>Ee(e),editSkill:e=>Ie(e),deleteSkill:e=>Te(e),restartAgentFromUI:e=>kn(e),saveSearchTool:e=>Qe(e),testSearchTool:e=>Ue(e),saveBuiltinKey:e=>Xe(e),testBuiltinProvider:e=>Je(e),fetchBuiltinModels:(e,t)=>et(e,t),saveOauthModel:e=>ot(e),testOauthProvider:e=>nt(e),showOauthModels:e=>tt(e),saveKey:e=>Ye(e),testKey:e=>We(e),fetchModels:(e,t)=>Ze(e,t),toggleKeyVis:(e,t)=>Ke(e,t),toggleAgentBody:e=>D(e),deleteAgent:e=>te(e),saveAgentModel:e=>Q(e),saveAgentFallback:e=>Z(e),saveAgentVoice:e=>X(e),toggleEmojiPicker:e=>oe(e),saveAgentIdentity:e=>Y(e),saveAgentPrompt:e=>J(e),resetAgentSession:e=>ee(e),saveAgentTools:e=>W(e),applyToolPreset:e=>ne(e),setRoute:(e,t)=>z(e,t),saveOpenCodeConfig:e=>V(e),saveOpenCodeFallback:e=>$(e),saveCursorCliConfig:e=>U(e),saveClaudeCodeConfig:e=>K(e),saveCodexConfig:e=>G(e),saveGeminiCliConfig:e=>q(e),saveCrewCLIConfig:e=>re(e),"pm-toggle":e=>{var t;const n=null==(t=o.projects)?void 0:t.find(t=>t.id===e);n&&n.running?yn(e):wn(e)},"edit-roadmap":e=>{var t;const n=null==(t=o.projects)?void 0:t.find(t=>t.id===e);n&&fn(e,n.roadmapFile)},"retry-failed":e=>{var t;const n=null==(t=o.projects)?void 0:t.find(t=>t.id===e);n&&vn(n.roadmapFile)},"save-roadmap":e=>hn(e),"reset-failed":e=>gn(e),showSettingsTab:e=>To(e)};let Ho=!1;document.addEventListener("touchstart",e=>{if(!(e.target instanceof Element))return;e.target.closest("[data-action]")&&(Ho=!0,setTimeout(()=>{Ho=!1},500))},{passive:!0}),document.addEventListener("click",e=>{if(!(e.target instanceof Element))return;const t=e.target.closest("[data-action]");if(!t)return;if(Ho)return void e.preventDefault();e.stopPropagation();const n=t.dataset.action,o=Fo[n];if(!o)return void console.warn("[crewswarm] unknown data-action:",n);const a=t.dataset.arg??null,s=t.dataset.arg2??null,i="1"===t.dataset.self;null!==a&&null!==s?o(a,s):null!==a&&i?o(a,t):null!==a?o(a):i?o(t):o()}),document.addEventListener("change",e=>{const t=e.target.closest("[data-onchange]");if(!t)return;const n=Fo[t.dataset.onchange];if(!n)return;const o="this.value"===t.dataset.onchangeArg?t.value:null;null!==o?n(o):n()}),document.addEventListener("DOMContentLoaded",()=>{f("activeTasksPanel"),je();const e=document.getElementById("dashSelfLink");e&&(e.href=window.location.origin,e.textContent=window.location.host),document.querySelectorAll(".nav-item").forEach(e=>{e.addEventListener("click",t=>{t.preventDefault(),t.stopPropagation();const n=e.dataset.view;if(!n)return;if(Gn()!==n)return void(window.location.hash=n);const o=Do[n];o&&o()})});const t=document.getElementById("chatInput");t&&!t.dataset.boundChatComposer&&(t.dataset.boundChatComposer="1",t.addEventListener("keydown",lo),t.addEventListener("input",ro));const n=document.getElementById("chatSendBtn")||document.querySelector('[data-action="sendChat"]');n&&!n.dataset.boundChatComposer&&(n.dataset.boundChatComposer="1",n.addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),co()}));const o=document.getElementById("cmdAllowlistInput");o&&o.addEventListener("keydown",e=>{"Enter"===e.key&&Zn()});const a=document.getElementById("waAllowedNumbers");a&&a.addEventListener("input",Xt);const s=document.getElementById("skillSearch");s&&s.addEventListener("input",e=>Ce(e.target.value));const i=document.getElementById("passthroughEngine");i&&i.addEventListener("change",()=>{ho(),function(){const e=document.getElementById("passthroughEngine"),t=document.getElementById("passthroughModel");if(!e||!t)return;const n=e.value,o={cursor:[{value:"",label:"— default (composer-2-fast) —"},{optgroup:"Cursor Defaults"},{value:"composer-2-fast",label:"🟢 Composer 2 Fast (default)"},{value:"composer-2-thinking",label:"Composer 2 Thinking"},{optgroup:"Recommended (No Rate Limits)"},{value:"gpt-5.4",label:"🟢 GPT-5.4"},{value:"gemini-3-flash",label:"🟢 Gemini 3 Flash (fastest)"},{value:"gemini-3-pro",label:"🟢 Gemini 3 Pro"},{value:"gemini-3.1-pro",label:"🟢 Gemini 3.1 Pro"},{value:"gpt-5.2-codex",label:"🟢 GPT-5.2 Codex"},{value:"gpt-5.3-codex",label:"GPT-5.3 Codex"},{optgroup:"Claude Models (May Hit Rate Limits)"},{value:"sonnet-4.6",label:"🟡 Claude 4.6 Sonnet (current)"},{value:"opus-4.6",label:"🟡 Claude 4.6 Opus"},{optgroup:"Thinking Models (Slower)"},{value:"sonnet-4.6-thinking",label:"Claude 4.6 Sonnet Thinking"},{value:"opus-4.6-thinking",label:"Claude 4.6 Opus Thinking"},{optgroup:"Compatible Claude Models"},{value:"sonnet-4.5",label:"Claude 4.5 Sonnet"},{value:"opus-4.5",label:"Claude 4.5 Opus"},{value:"sonnet-4.5-thinking",label:"Claude 4.5 Sonnet Thinking"},{optgroup:"Other"},{value:"grok",label:"xAI Grok"},{value:"kimi-k2.5",label:"Moonshot Kimi K2.5"}],claude:[{value:"",label:"— default (Sonnet 4.6) —"},{optgroup:"Recommended"},{value:"sonnet",label:"🟢 Sonnet (alias for latest)"},{value:"Default",label:"🟢 Default (Sonnet 4.6)"},{optgroup:"Specific Versions"},{value:"claude-sonnet-4-6",label:"Sonnet 4.6 · Best for everyday tasks"},{value:"Opus",label:"Opus (Opus 4.6) · Most capable for complex work"},{value:"claude-opus-4-6",label:"Opus 4.6 · Most capable"},{value:"Haiku",label:"Haiku (Haiku 4.5) · Fastest for quick answers"},{value:"claude-haiku-4-5",label:"Haiku 4.5 · Fastest"},{optgroup:"Legacy"},{value:"claude-sonnet-4-5",label:"Sonnet 4.5 (legacy)"}],codex:[{value:"",label:"— default (gpt-5.4) —"},{optgroup:"Recommended"},{value:"gpt-5.4",label:"🟢 GPT-5.4 (current)"},{value:"gpt-5.3-codex",label:"GPT-5.3 Codex"},{value:"gpt-5.2-codex",label:"🟢 GPT-5.2 Codex"},{optgroup:"Specialized"},{value:"gpt-5.1-codex-max",label:"GPT-5.1 Codex Max (deep reasoning)"},{value:"gpt-5.2",label:"GPT-5.2 (general purpose)"},{value:"gpt-5.1-codex-mini",label:"GPT-5.1 Codex Mini (fast & cheap)"}],opencode:[{value:"",label:"— default —"},{optgroup:"Free Models 🎁"},{value:"opencode/big-pickle",label:"🆓 Big Pickle (Free)"},{value:"opencode/minimax-m2.5-free",label:"🆓 MiniMax M2.5 Free"},{value:"openai/gpt-5-nano",label:"🆓 GPT 5 Nano (Free)"},{optgroup:"Budget Models 💰"},{value:"openai/gpt-5.1-codex-mini",label:"💰 GPT 5.1 Codex Mini ($0.25/$2)"},{value:"google/gemini-3-flash",label:"💰 Gemini 3 Flash ($0.50/$3)"},{value:"anthropic/claude-haiku-4-5",label:"💰 Claude Haiku 4.5 ($1/$5)"},{optgroup:"Interesting Models 🎯"},{value:"moonshot/kimi-k2.5",label:"Kimi K2.5 ($0.60/$3)"},{value:"moonshot/kimi-k2-thinking",label:"Kimi K2 Thinking ($0.40/$2.50)"},{value:"alibaba/qwen3-coder-480b",label:"Qwen3 Coder 480B ($0.45/$1.50)"},{value:"zhipu/glm-5",label:"GLM 5 ($1/$3.20)"},{optgroup:"Premium Claude"},{value:"anthropic/claude-sonnet-4-6",label:"Claude Sonnet 4.6 ($3/$15)"},{value:"anthropic/claude-opus-4-6",label:"Claude Opus 4.6 ($5/$25)"},{optgroup:"Premium OpenAI"},{value:"openai/gpt-5.4",label:"GPT 5.4 ($3/$15)"},{value:"openai/gpt-5.3-codex",label:"GPT 5.3 Codex ($1.75/$14)"},{value:"openai/gpt-5.2-codex",label:"GPT 5.2 Codex ($1.75/$14)"},{value:"openai/gpt-5.1-codex-max",label:"GPT 5.1 Codex Max ($1.25/$10)"},{optgroup:"Premium Google"},{value:"google/gemini-3.1-pro",label:"Gemini 3.1 Pro ($2/$12)"},{value:"google/gemini-3-pro",label:"Gemini 3 Pro ($2/$12)"}],gemini:[{value:"",label:"— default (gemini-3-flash-preview) —"},{optgroup:"Recommended (Latest)"},{value:"gemini-3-flash-preview",label:"🟢 Gemini 3 Flash Preview (current)"},{value:"gemini-3.1-pro-preview",label:"🟢 Gemini 3.1 Pro Preview"},{optgroup:"Gemini 2.5 Series"},{value:"gemini-2.5-pro",label:"Gemini 2.5 Pro"},{value:"gemini-2.5-flash",label:"Gemini 2.5 Flash"},{value:"gemini-2.5-flash-lite",label:"Gemini 2.5 Flash Lite (fastest)"}]};if(!n||!o[n])return void(t.style.display="none");t.style.display="inline-block",t.innerHTML="";let a=null;for(const s of o[n])if(s.optgroup)a=document.createElement("optgroup"),a.label=s.optgroup,t.appendChild(a);else{const e=document.createElement("option");e.value=s.value,e.textContent=s.label,a?a.appendChild(e):t.appendChild(e)}}(),fo()});const r=document.getElementById("chatProjectSelect");r&&r.addEventListener("change",ho);const l=document.getElementById("passthroughModel");l&&l.addEventListener("change",()=>{fo()})},{once:!0});const Do={chat:Kn,"swarm-chat":x,swarm:Fe,rt:Re,build:_o,files:Un,dlq:Oe,projects:No,contacts:Se,agents:R,models:qe,engines:Bo,skills:ve,"run-skills":he,waves:()=>{Hn(),document.getElementById("wavesView").style.display="block",Fn("navWaves")},workflows:I,benchmarks:So,"tool-matrix":Po,memory:jo,"cli-process":Ao,services:L,prompts:de,testing:me,settings:Io};document.addEventListener("click",e=>{const t=e.target.closest("[data-view]");if(t){const e=t.dataset.view,n=Do[e];return void(n&&(Gn()!==e?window.location.hash=e:n()))}const n=e.target.closest("[data-stab]");if(n){const e=n.dataset.stab;window.location.hash=`settings/${e}`,To(e)}const o=e.target.closest("[data-toggle-child]");if(o){const e=o.dataset.toggleChild,t=o.parentElement&&o.parentElement.querySelector(e);t&&(t.style.display="none"===t.style.display?"block":"none")}const a=e.target.closest("[data-toggle-sibling]");a&&a.nextElementSibling&&a.nextElementSibling.classList.toggle(a.dataset.toggleSibling)}),Object.assign(window,{addAllowlistPattern:Zn,applyNewAgentToolPreset:ie,applyPromptPreset:se,bulkSetRoute:ae,cancelSkillForm:ye,chatAtAtInput:ro,chatKeydown:lo,clearChatHistory:po,filterSkills:Ce,loadAllUsage:An,loadBenchmarkLeaderboard:async()=>(await Nn()).loadBenchmarkLeaderboard(),loadBenchmarks:async()=>(await Nn()).loadBenchmarks(),loadBenchmarkTasks:async()=>(await Nn()).loadBenchmarkTasks(),onBenchmarkTaskSelect:async e=>(await Nn()).onBenchmarkTaskSelect(e),runBenchmarkTask:async()=>(await Nn()).runBenchmarkTask(),stopBenchmarkRun:async()=>(await Nn()).stopBenchmarkRun(),loadMemoryStats:j,searchMemory:S,migrateMemory:B,compactMemory:T,loadBuildProjectPicker:cn,loadFiles:bo,loadOcStats:eo,loadRunSkills:fe,loadServices:M,loadSpending:jn,loadTelegramSessions:Wt,loadTgMessages:Ut,loadToolMatrix:xn,loadWaMessages:$t,onBuildProjectChange:pn,onChatProjectChange:dn,pickFolder:Dn,renderWaContactRows:Xt,resetSpending:Sn,approveSkill:async function(e){try{await fetch("/api/skills/approve",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({approvalId:e})}),i("Approved"),Mo()}catch(t){i("Failed: "+t.message,"error")}},loadPendingApprovals:Mo,rejectSkill:async function(e){try{await fetch("/api/skills/reject",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({approvalId:e})}),i("Rejected"),Mo()}catch(t){i("Failed: "+t.message,"error")}},saveGlobalCaps:Bn,saveGlobalFallback:ft,saveBgConsciousnessModel:Ct,saveOpencodeSettings:wt,saveRTToken:kt,saveSkill:we,saveTgConfig:Jt,saveWaConfig:Vt,sendChat:co,sendTestWebhook:Lo,showAgents:R,showBenchmarks:So,showBuild:_o,showChat:Kn,showContacts:Se,showDLQ:Oe,showFiles:Un,showModels:qe,showProjects:No,showRT:Re,showRunSkills:he,showServices:L,showSettings:Io,showSettingsTab:To,showSkills:ve,showSwarm:Fe,showToolMatrix:Po,showMemoryView:jo,startCrew:H,startTgBridge:Yt,startWaBridge:Kt,stopTgBridge:Qt,stopWaBridge:qt,toggleAddSkill:ke,toggleBgConsciousness:vt,toggleCursorWaves:ht,toggleTmuxBridge:gt,toggleClaudeCode:mt,toggleEmojiPicker:oe,updateSkillAuthFields:Be,navigateTo:Ro,renderStatusBadge:p,showLoading:d,showEmpty:c,showError:l,loadContacts:Pe,applyContactFilters:Ae,applyToolPreset:ne,closePreviewPane:ko,deleteAgent:te,deleteSkill:Te,editSkill:Ie,fetchBuiltinModels:et,fetchModels:Ze,previewFile:xo,resetAgentSession:ee,restartAgentFromUI:kn,restartService:N,runSkillFromUI:Ee,saveAgentFallback:Z,saveAgentVoice:X,saveAgentIdentity:Y,saveAgentModel:Q,saveAgentPrompt:J,saveAgentTools:W,saveBuiltinKey:Xe,saveCursorCliConfig:U,saveClaudeCodeConfig:K,saveGeminiCliConfig:q,saveKey:Ye,saveOpenCodeConfig:V,saveOpenCodeFallback:$,saveSearchTool:Qe,saveCodexConfig:G,setRoute:z,stopService:_,testBuiltinProvider:Je,testKey:We,testSearchTool:Ue,toggleAgentBody:D,toggleKeyVis:Ke});
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|