ec-web 0.0.21 → 0.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +4 -2
- package/dist/ipc-client.js +12 -1
- package/dist/paths.js +18 -1
- package/dist/server.js +111 -154
- package/dist/sources/baseagent-detector.js +27 -102
- package/dist/sources/basic-settings.js +21 -4
- package/dist/sources/models.js +13 -6
- package/dist/sources/monitor.js +22 -5
- package/dist/sources/relation-roles.js +67 -17
- package/dist/sources/role-definitions.js +64 -15
- package/dist/sources/session-codex.js +18 -6
- package/dist/sources/triggers.js +58 -0
- package/dist/static/app.js +173 -39
- package/dist/static/basic-settings.js +81 -14
- package/dist/static/index.html +1 -1
- package/package.json +1 -1
package/dist/static/app.js
CHANGED
|
@@ -1976,6 +1976,58 @@ async function tryLocalAutoPair() {
|
|
|
1976
1976
|
return false;
|
|
1977
1977
|
}
|
|
1978
1978
|
|
|
1979
|
+
let authRecoveryPromise = null;
|
|
1980
|
+
|
|
1981
|
+
function authenticatedRequestUrl(pathOrUrl) {
|
|
1982
|
+
const raw = String(pathOrUrl || '');
|
|
1983
|
+
const target = raw.startsWith(BASE) ? raw : apiUrl(raw);
|
|
1984
|
+
const parsed = new URL(target, location.origin);
|
|
1985
|
+
const token = localStorage.getItem(TOKEN_KEY);
|
|
1986
|
+
if (token) parsed.searchParams.set('token', token);
|
|
1987
|
+
else parsed.searchParams.delete('token');
|
|
1988
|
+
return parsed.pathname + parsed.search + parsed.hash;
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
function authenticatedRequestInit(init = {}) {
|
|
1992
|
+
const headers = new Headers(init.headers || {});
|
|
1993
|
+
const token = localStorage.getItem(TOKEN_KEY);
|
|
1994
|
+
if (token) headers.set('Authorization', `Bearer ${token}`);
|
|
1995
|
+
else headers.delete('Authorization');
|
|
1996
|
+
return { ...init, headers };
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
async function recoverHttpAuthentication() {
|
|
2000
|
+
if (authRecoveryPromise) return authRecoveryPromise;
|
|
2001
|
+
localStorage.removeItem(TOKEN_KEY);
|
|
2002
|
+
authRecoveryPromise = (async () => {
|
|
2003
|
+
if (await tryLocalAutoPair()) return true;
|
|
2004
|
+
showPairPage(t('pair.error.tokenInvalid'));
|
|
2005
|
+
return false;
|
|
2006
|
+
})().finally(() => {
|
|
2007
|
+
authRecoveryPromise = null;
|
|
2008
|
+
});
|
|
2009
|
+
return authRecoveryPromise;
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
async function authFetch(pathOrUrl, init = {}) {
|
|
2013
|
+
const send = async () => {
|
|
2014
|
+
const token = localStorage.getItem(TOKEN_KEY);
|
|
2015
|
+
const response = await fetch(authenticatedRequestUrl(pathOrUrl), authenticatedRequestInit(init));
|
|
2016
|
+
return { response, token };
|
|
2017
|
+
};
|
|
2018
|
+
|
|
2019
|
+
let attempt = await send();
|
|
2020
|
+
if (attempt.response.status !== 401) return attempt.response;
|
|
2021
|
+
|
|
2022
|
+
const currentToken = localStorage.getItem(TOKEN_KEY);
|
|
2023
|
+
if (currentToken && currentToken !== attempt.token) {
|
|
2024
|
+
return (await send()).response;
|
|
2025
|
+
}
|
|
2026
|
+
if (!(await recoverHttpAuthentication())) return attempt.response;
|
|
2027
|
+
attempt = await send();
|
|
2028
|
+
return attempt.response;
|
|
2029
|
+
}
|
|
2030
|
+
|
|
1979
2031
|
function showPairPage(hint) {
|
|
1980
2032
|
if (ws) { try { ws.close(); } catch {} ws = null; }
|
|
1981
2033
|
$('#pair-page').style.display = 'flex';
|
|
@@ -2256,6 +2308,12 @@ function subscribeRoleManagementSubview() {
|
|
|
2256
2308
|
function setRoleManagementSubview(subview, options = {}) {
|
|
2257
2309
|
roleManagementSubview = subview === 'assignments' ? 'assignments' : 'definitions';
|
|
2258
2310
|
localStorage.setItem(ROLE_SUBVIEW_KEY, roleManagementSubview);
|
|
2311
|
+
const newRoleButton = $('#btn-new-role');
|
|
2312
|
+
if (newRoleButton) {
|
|
2313
|
+
const visible = roleManagementSubview === 'definitions';
|
|
2314
|
+
newRoleButton.hidden = !visible;
|
|
2315
|
+
newRoleButton.setAttribute('aria-hidden', String(!visible));
|
|
2316
|
+
}
|
|
2259
2317
|
document.querySelectorAll('[data-role-subview]').forEach(tab => {
|
|
2260
2318
|
const active = tab.dataset.roleSubview === roleManagementSubview;
|
|
2261
2319
|
tab.classList.toggle('active', active);
|
|
@@ -3815,7 +3873,7 @@ function renderBlocks(blocks, options = {}) {
|
|
|
3815
3873
|
: '';
|
|
3816
3874
|
out += `<details class="blk blk-tool" open><summary class="tool-head">${icon} <span class="tool-name">${esc(tool)}</span></summary>${toolParams}</details>`;
|
|
3817
3875
|
} else if (b.kind === 'tool_result') {
|
|
3818
|
-
const rejection = b.rejection
|
|
3876
|
+
const rejection = b.rejection && (b.rejection.kind === 'approval_rejected' || b.rejection.kind === 'policy_blocked') ? b.rejection : undefined;
|
|
3819
3877
|
const cls = b.isError ? `blk-result err${rejection ? ' rejection' : ''}` : 'blk-result';
|
|
3820
3878
|
const loadFull = b.truncated && b.resultKey
|
|
3821
3879
|
? `<button class="ctrl-btn result-load-full" type="button" data-result-key="${esc(b.resultKey)}">${t('sessions.loadFullResult') || '加载完整结果'}</button>`
|
|
@@ -3839,7 +3897,7 @@ function renderBlocks(blocks, options = {}) {
|
|
|
3839
3897
|
: '';
|
|
3840
3898
|
return '<div class="result-rejection-meta">' +
|
|
3841
3899
|
`<span class="result-rejection-badge">⚠ ${t('sessions.block.rejection.title')}</span>` +
|
|
3842
|
-
`<span>${rejection.source === 'evolcore-policy' ? t('sessions.block.rejection.source.policy') : t('sessions.block.rejection.source.approval')}</span>` +
|
|
3900
|
+
`<span>${rejection.kind === 'policy_blocked' || rejection.source === 'evolcore-policy' ? t('sessions.block.rejection.source.policy') : t('sessions.block.rejection.source.approval')}</span>` +
|
|
3843
3901
|
`<span>${t('sessions.block.rejection.notExecuted')}</span>` +
|
|
3844
3902
|
`<span>${t(`sessions.block.rejection.evidence.${evidenceKey}`)}</span>` +
|
|
3845
3903
|
backendReason + auditText +
|
|
@@ -4132,12 +4190,7 @@ function agentOpNew() {
|
|
|
4132
4190
|
openAgentCreateDrawer();
|
|
4133
4191
|
}
|
|
4134
4192
|
|
|
4135
|
-
const AGENT_BASEAGENT_TYPES = ['claude', 'codex', 'gemini'];
|
|
4136
|
-
const AGENT_EFFORT_FALLBACK = {
|
|
4137
|
-
claude: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
4138
|
-
codex: ['low', 'medium', 'high'],
|
|
4139
|
-
gemini: [],
|
|
4140
|
-
};
|
|
4193
|
+
const AGENT_BASEAGENT_TYPES = ['claude', 'codex', 'gemini', 'ecagent'];
|
|
4141
4194
|
const AGENT_AID_RE = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?){2,}$/;
|
|
4142
4195
|
let _agentEdit = null;
|
|
4143
4196
|
let _agentCreate = null;
|
|
@@ -4588,7 +4641,7 @@ async function agentFetchCreateModelOptions(baseagent) {
|
|
|
4588
4641
|
const catalog = await fetchModelCatalogData(baseagent);
|
|
4589
4642
|
return (catalog?.models || []).map(model => ({
|
|
4590
4643
|
value: model.id,
|
|
4591
|
-
label: model.id
|
|
4644
|
+
label: `${model.id}${model.verification === 'unverified' ? ' [unverified]' : ''}`,
|
|
4592
4645
|
}));
|
|
4593
4646
|
}
|
|
4594
4647
|
|
|
@@ -4678,7 +4731,8 @@ async function syncAgentCreateOptionFields(ctx, values) {
|
|
|
4678
4731
|
async function syncAgentCreateEffortOptions(ctx, baseagent, model, currentEffort, token) {
|
|
4679
4732
|
const effortSelect = ctx.backdrop.querySelector('#ag-create-effort');
|
|
4680
4733
|
if (!effortSelect) return;
|
|
4681
|
-
const
|
|
4734
|
+
const catalog = await fetchModelCatalogData(baseagent);
|
|
4735
|
+
const efforts = catalog.models.find(entry => entry.id === model)?.reasoningLevels || [];
|
|
4682
4736
|
if (_agentCreate !== ctx || ctx.optionLoadToken !== token) return;
|
|
4683
4737
|
effortSelect.innerHTML = agentSelectOptions(efforts, currentEffort, t('common.default'));
|
|
4684
4738
|
effortSelect.disabled = Boolean(ctx.saving);
|
|
@@ -5079,12 +5133,11 @@ async function syncAgentEffortOptions(ctx, baseagent, model, currentEffort, toke
|
|
|
5079
5133
|
setAgentSelectLoading(effortSelect, currentEffort, currentEffort ? `${currentEffort} (${t('common.loadingSuffix')})` : t('common.loading'));
|
|
5080
5134
|
try {
|
|
5081
5135
|
let efforts = await agentFetchOptions('effort', ctx.aid, { baseagent, model, effort: currentEffort || 'auto' });
|
|
5082
|
-
if (!efforts.length && AGENT_EFFORT_FALLBACK[baseagent]) efforts = AGENT_EFFORT_FALLBACK[baseagent];
|
|
5083
5136
|
if (_agentEdit !== ctx || ctx.optionLoadToken !== token) return;
|
|
5084
5137
|
effortSelect.innerHTML = agentSelectOptions(efforts, currentEffort, t('common.default'));
|
|
5085
5138
|
} catch (e) {
|
|
5086
5139
|
if (_agentEdit === ctx && ctx.optionLoadToken === token) {
|
|
5087
|
-
const fallback =
|
|
5140
|
+
const fallback = [];
|
|
5088
5141
|
effortSelect.innerHTML = agentSelectOptions(fallback, currentEffort, t('common.default'));
|
|
5089
5142
|
toast(e.message || String(e), true);
|
|
5090
5143
|
}
|
|
@@ -6877,12 +6930,11 @@ async function syncTriggerEffortOptions(ctx, baseagent, model, currentEffort, to
|
|
|
6877
6930
|
setAgentSelectLoading(effortSelect, currentEffort, currentEffort ? `${currentEffort} (${t('common.loadingSuffix')})` : t('common.loading'));
|
|
6878
6931
|
try {
|
|
6879
6932
|
let efforts = await agentFetchOptions('effort', ctx.original.agentAid, { baseagent, model, effort: currentEffort || 'auto' });
|
|
6880
|
-
if (!efforts.length && AGENT_EFFORT_FALLBACK[baseagent]) efforts = AGENT_EFFORT_FALLBACK[baseagent];
|
|
6881
6933
|
if (_triggerEdit !== ctx || ctx.optionLoadToken !== token) return;
|
|
6882
6934
|
effortSelect.innerHTML = agentSelectOptions(efforts, currentEffort, t('common.inherited'));
|
|
6883
6935
|
} catch (e) {
|
|
6884
6936
|
if (_triggerEdit === ctx && ctx.optionLoadToken === token) {
|
|
6885
|
-
const fallback =
|
|
6937
|
+
const fallback = [];
|
|
6886
6938
|
effortSelect.innerHTML = agentSelectOptions(fallback, currentEffort, t('common.inherited'));
|
|
6887
6939
|
toast(e.message || String(e), true);
|
|
6888
6940
|
}
|
|
@@ -7805,7 +7857,7 @@ async function loadUsageOverview(rangeType, customFrom, customTo) {
|
|
|
7805
7857
|
if (fromTs) params.set('from', String(fromTs));
|
|
7806
7858
|
if (toTs) params.set('to', String(toTs));
|
|
7807
7859
|
|
|
7808
|
-
const resp = await
|
|
7860
|
+
const resp = await authFetch('api/stats/overview?' + params.toString(), {
|
|
7809
7861
|
headers: { Authorization: 'Bearer ' + localStorage.getItem(TOKEN_KEY) }
|
|
7810
7862
|
});
|
|
7811
7863
|
data = resp.ok ? await resp.json() : null;
|
|
@@ -8010,7 +8062,7 @@ function initDetailQuery() {
|
|
|
8010
8062
|
|
|
8011
8063
|
async function loadDetailAgentList() {
|
|
8012
8064
|
try {
|
|
8013
|
-
const resp = await
|
|
8065
|
+
const resp = await authFetch('api/stats/agents', {
|
|
8014
8066
|
headers: { Authorization: 'Bearer ' + localStorage.getItem(TOKEN_KEY) }
|
|
8015
8067
|
});
|
|
8016
8068
|
if (!resp.ok) return;
|
|
@@ -8050,7 +8102,7 @@ async function loadDetailModelList() {
|
|
|
8050
8102
|
if (timeRange.fromTs) params.set('from', String(timeRange.fromTs));
|
|
8051
8103
|
if (timeRange.toTs) params.set('to', String(timeRange.toTs));
|
|
8052
8104
|
|
|
8053
|
-
const resp = await
|
|
8105
|
+
const resp = await authFetch('api/stats/models?' + params.toString(), {
|
|
8054
8106
|
headers: { Authorization: 'Bearer ' + localStorage.getItem(TOKEN_KEY) }
|
|
8055
8107
|
});
|
|
8056
8108
|
if (!resp.ok) return;
|
|
@@ -8103,7 +8155,7 @@ async function queryDetailUsage() {
|
|
|
8103
8155
|
params.set('offset', String(offset));
|
|
8104
8156
|
|
|
8105
8157
|
try {
|
|
8106
|
-
const resp = await
|
|
8158
|
+
const resp = await authFetch('api/stats/detail?' + params.toString(), {
|
|
8107
8159
|
headers: { Authorization: 'Bearer ' + localStorage.getItem(TOKEN_KEY) }
|
|
8108
8160
|
});
|
|
8109
8161
|
if (seq !== _detailQuerySeq) return; // 已有更新的查询发出,丢弃本次结果
|
|
@@ -8424,7 +8476,7 @@ async function loadExplorerModels() {
|
|
|
8424
8476
|
if (_expTimeRange.toTs) params.set('to', String(_expTimeRange.toTs));
|
|
8425
8477
|
|
|
8426
8478
|
try {
|
|
8427
|
-
const resp = await
|
|
8479
|
+
const resp = await authFetch('api/stats/models?' + params.toString(), {
|
|
8428
8480
|
headers: { Authorization: 'Bearer ' + localStorage.getItem(TOKEN_KEY) }
|
|
8429
8481
|
});
|
|
8430
8482
|
if (!resp.ok) return;
|
|
@@ -8461,7 +8513,7 @@ async function fetchExplorerOverviewData(filterParams) {
|
|
|
8461
8513
|
if (filterParams.peer) params.set('peer', filterParams.peer);
|
|
8462
8514
|
}
|
|
8463
8515
|
|
|
8464
|
-
const resp = await
|
|
8516
|
+
const resp = await authFetch('api/stats/overview?' + params.toString(), {
|
|
8465
8517
|
headers: { Authorization: 'Bearer ' + localStorage.getItem(TOKEN_KEY) }
|
|
8466
8518
|
});
|
|
8467
8519
|
const data = resp.ok ? await resp.json() : null;
|
|
@@ -8493,7 +8545,7 @@ async function loadExplorerSidebar() {
|
|
|
8493
8545
|
var token = localStorage.getItem(TOKEN_KEY);
|
|
8494
8546
|
var headers = { Authorization: 'Bearer ' + token };
|
|
8495
8547
|
try {
|
|
8496
|
-
var agentsResp = await
|
|
8548
|
+
var agentsResp = await authFetch('api/stats/agents', { headers });
|
|
8497
8549
|
var agents = agentsResp.ok ? await agentsResp.json() : [];
|
|
8498
8550
|
renderExplorerAgentList(agents);
|
|
8499
8551
|
|
|
@@ -8554,7 +8606,7 @@ async function loadPeersForAgent(agentAid) {
|
|
|
8554
8606
|
params.set('agent', agentAid);
|
|
8555
8607
|
// 不传递时间范围,获取该 agent 的所有 peers
|
|
8556
8608
|
|
|
8557
|
-
var resp = await
|
|
8609
|
+
var resp = await authFetch('api/stats/peers?' + params.toString(), { headers });
|
|
8558
8610
|
var peers = resp.ok ? await resp.json() : [];
|
|
8559
8611
|
renderExplorerPeerList(peers);
|
|
8560
8612
|
} catch {
|
|
@@ -8636,7 +8688,7 @@ async function runExplorerQuery() {
|
|
|
8636
8688
|
|
|
8637
8689
|
var data;
|
|
8638
8690
|
try {
|
|
8639
|
-
var resp = await
|
|
8691
|
+
var resp = await authFetch('api/stats/explorer?' + params.toString(), {
|
|
8640
8692
|
headers: { Authorization: 'Bearer ' + localStorage.getItem(TOKEN_KEY) }
|
|
8641
8693
|
});
|
|
8642
8694
|
if (!resp.ok) return;
|
|
@@ -9092,11 +9144,39 @@ function monDualLine(elId, varKey, times, isDark, title, series, fmtY, yRange) {
|
|
|
9092
9144
|
}
|
|
9093
9145
|
|
|
9094
9146
|
function renderRoles(data) {
|
|
9095
|
-
if (!data) return;
|
|
9096
|
-
|
|
9097
9147
|
const agentSelect = $('#roles-agent-select');
|
|
9148
|
+
const tbody = document.querySelector('#relations-table tbody');
|
|
9149
|
+
if (!data) {
|
|
9150
|
+
if (tbody) {
|
|
9151
|
+
tbody.innerHTML = '';
|
|
9152
|
+
const row = tbody.insertRow();
|
|
9153
|
+
const cell = row.insertCell();
|
|
9154
|
+
cell.colSpan = 7;
|
|
9155
|
+
cell.textContent = t('common.loading');
|
|
9156
|
+
cell.style.textAlign = 'center';
|
|
9157
|
+
cell.style.color = 'var(--dim)';
|
|
9158
|
+
cell.style.padding = '40px';
|
|
9159
|
+
}
|
|
9160
|
+
return;
|
|
9161
|
+
}
|
|
9098
9162
|
if (!agentSelect) return;
|
|
9099
9163
|
|
|
9164
|
+
if (data.error) {
|
|
9165
|
+
agentSelect.innerHTML = `<option value="">${esc(t('roles.selectAgentPlaceholder'))}</option>`;
|
|
9166
|
+
rolesCurrentAgent = null;
|
|
9167
|
+
if (tbody) {
|
|
9168
|
+
tbody.innerHTML = '';
|
|
9169
|
+
const row = tbody.insertRow();
|
|
9170
|
+
const cell = row.insertCell();
|
|
9171
|
+
cell.colSpan = 7;
|
|
9172
|
+
cell.textContent = `${t('common.error')}: ${String(data.error)}`;
|
|
9173
|
+
cell.style.textAlign = 'center';
|
|
9174
|
+
cell.style.color = 'var(--red)';
|
|
9175
|
+
cell.style.padding = '40px';
|
|
9176
|
+
}
|
|
9177
|
+
return;
|
|
9178
|
+
}
|
|
9179
|
+
|
|
9100
9180
|
agentSelect.innerHTML = '';
|
|
9101
9181
|
const placeholderOption = document.createElement('option');
|
|
9102
9182
|
placeholderOption.value = '';
|
|
@@ -9152,6 +9232,34 @@ function peerTypeText(peerType) {
|
|
|
9152
9232
|
return '-';
|
|
9153
9233
|
}
|
|
9154
9234
|
|
|
9235
|
+
function roleVerificationStatus(item) {
|
|
9236
|
+
const status = String(item?.verifyStatus || 'unknown').toLowerCase();
|
|
9237
|
+
if (status === 'agentmd' || status === 'verified') return 'verified';
|
|
9238
|
+
if (status === 'agentmd-unverified' || status === 'unverified') return 'agentmd-unverified';
|
|
9239
|
+
if (status === 'invalid') return 'invalid';
|
|
9240
|
+
return 'unknown';
|
|
9241
|
+
}
|
|
9242
|
+
|
|
9243
|
+
function roleVerificationMatches(item, filter) {
|
|
9244
|
+
if (!filter) return true;
|
|
9245
|
+
const status = roleVerificationStatus(item);
|
|
9246
|
+
return filter === 'verified' ? status === 'verified' : status === filter;
|
|
9247
|
+
}
|
|
9248
|
+
|
|
9249
|
+
function roleVerificationBadge(item) {
|
|
9250
|
+
const status = roleVerificationStatus(item);
|
|
9251
|
+
if (status === 'invalid') {
|
|
9252
|
+
return ` <span class="verify-badge verify-invalid" title="${esc(t('roles.verify.invalid'))}">⚠️ ${esc(t('roles.verify.invalidShort'))}</span>`;
|
|
9253
|
+
}
|
|
9254
|
+
if (status === 'agentmd-unverified') {
|
|
9255
|
+
return ` <span class="verify-badge verify-unverified" title="${esc(t('roles.verify.unverified'))}">⚠️</span>`;
|
|
9256
|
+
}
|
|
9257
|
+
if (status === 'unknown') {
|
|
9258
|
+
return ` <span class="verify-badge verify-unknown" title="${esc(t('roles.verify.unknown'))}">❓</span>`;
|
|
9259
|
+
}
|
|
9260
|
+
return '';
|
|
9261
|
+
}
|
|
9262
|
+
|
|
9155
9263
|
function roleClass(role) {
|
|
9156
9264
|
return String(role || 'none').replace(/[^a-z0-9_-]/gi, '-');
|
|
9157
9265
|
}
|
|
@@ -9188,6 +9296,16 @@ function renderRelationsTable(data, filterAid = null) {
|
|
|
9188
9296
|
if (!tbody) return;
|
|
9189
9297
|
|
|
9190
9298
|
tbody.innerHTML = '';
|
|
9299
|
+
if (data?.error) {
|
|
9300
|
+
const row = tbody.insertRow();
|
|
9301
|
+
const cell = row.insertCell();
|
|
9302
|
+
cell.colSpan = 7;
|
|
9303
|
+
cell.textContent = `${t('common.error')}: ${String(data.error)}`;
|
|
9304
|
+
cell.style.textAlign = 'center';
|
|
9305
|
+
cell.style.color = 'var(--red)';
|
|
9306
|
+
cell.style.padding = '40px';
|
|
9307
|
+
return;
|
|
9308
|
+
}
|
|
9191
9309
|
let conversations = data.conversations || [];
|
|
9192
9310
|
|
|
9193
9311
|
if (filterAid) {
|
|
@@ -9215,6 +9333,16 @@ function renderRelationsTable(data, filterAid = null) {
|
|
|
9215
9333
|
conversations = conversations.filter(conv => conv.chatType === rolesChatTypeFilter);
|
|
9216
9334
|
}
|
|
9217
9335
|
|
|
9336
|
+
if (rolesVerifyFilter) {
|
|
9337
|
+
conversations = conversations
|
|
9338
|
+
.map(conv => conv.chatType === 'group'
|
|
9339
|
+
? { ...conv, members: (conv.members || []).filter(member => roleVerificationMatches(member, rolesVerifyFilter)) }
|
|
9340
|
+
: conv)
|
|
9341
|
+
.filter(conv => conv.chatType === 'group'
|
|
9342
|
+
? (conv.members || []).length > 0
|
|
9343
|
+
: roleVerificationMatches(conv, rolesVerifyFilter));
|
|
9344
|
+
}
|
|
9345
|
+
|
|
9218
9346
|
conversations.sort((a, b) => {
|
|
9219
9347
|
const lastDiff = (b.lastAt || 0) - (a.lastAt || 0);
|
|
9220
9348
|
if (lastDiff !== 0) return lastDiff;
|
|
@@ -9244,10 +9372,12 @@ function renderPrivateConversationRow(tbody, conv) {
|
|
|
9244
9372
|
const peerId = conv.peerId || conv.peerAid || conv.peerKey || conv.conversationId;
|
|
9245
9373
|
const name = conv.peerName || conv.name || shortAid(peerId);
|
|
9246
9374
|
const assignedRole = assignedRoleFor('private', conv);
|
|
9375
|
+
const verifyBadge = roleVerificationBadge(conv);
|
|
9376
|
+
const verifyClass = roleVerificationStatus(conv) === 'verified' ? '' : ` peer-${roleVerificationStatus(conv) === 'unknown' ? 'unknown' : 'unverified'}`;
|
|
9247
9377
|
const row = tbody.insertRow();
|
|
9248
9378
|
row.innerHTML = `
|
|
9249
9379
|
<td><span class="chat-tag chat-tag-private">${esc(t('roles.chatType.private') || 'Private')}</span></td>
|
|
9250
|
-
<td><strong>${esc(name)}</strong
|
|
9380
|
+
<td class="${verifyClass.trim()}"><strong>${esc(name)}</strong>${verifyBadge}</td>
|
|
9251
9381
|
<td><code>${esc(peerId)}</code></td>
|
|
9252
9382
|
<td>${esc(peerTypeText(conv.peerType))}</td>
|
|
9253
9383
|
<td><span class="role-badge role-${esc(roleClass(conv.role))}">${esc(displayRole(conv.role))}</span></td>
|
|
@@ -9312,11 +9442,13 @@ function renderGroupMemberRow(tbody, conv, member) {
|
|
|
9312
9442
|
const peerId = member.peerId || member.peerAid || member.peerKey;
|
|
9313
9443
|
const name = member.peerName || shortAid(peerId);
|
|
9314
9444
|
const assignedRole = assignedRoleFor('group-member', member);
|
|
9445
|
+
const verifyBadge = roleVerificationBadge(member);
|
|
9446
|
+
const verifyClass = roleVerificationStatus(member) === 'verified' ? '' : ` peer-${roleVerificationStatus(member) === 'unknown' ? 'unknown' : 'unverified'}`;
|
|
9315
9447
|
const row = tbody.insertRow();
|
|
9316
9448
|
row.className = 'group-member-row';
|
|
9317
9449
|
row.innerHTML = `
|
|
9318
9450
|
<td></td>
|
|
9319
|
-
<td><span class="group-member-indent">${esc(name)}</span
|
|
9451
|
+
<td class="${verifyClass.trim()}"><span class="group-member-indent">${esc(name)}</span>${verifyBadge}</td>
|
|
9320
9452
|
<td><code>${esc(peerId)}</code></td>
|
|
9321
9453
|
<td>${esc(peerTypeText(member.peerType))}</td>
|
|
9322
9454
|
<td><span class="role-badge role-${esc(roleClass(member.role))}">${esc(displayRole(member.role))}</span></td>
|
|
@@ -9344,7 +9476,7 @@ async function updatePeerRoleOverride(agentAid, scope, targetId, role, groupId,
|
|
|
9344
9476
|
if (peerId) body.peerId = peerId;
|
|
9345
9477
|
if (role) body.role = role;
|
|
9346
9478
|
|
|
9347
|
-
const res = await
|
|
9479
|
+
const res = await authFetch(`api/assignments/peer/${encodeURIComponent(agentAid)}/${encodeURIComponent(targetId)}`, {
|
|
9348
9480
|
method: role ? 'PUT' : 'DELETE',
|
|
9349
9481
|
headers: {
|
|
9350
9482
|
'Content-Type': 'application/json',
|
|
@@ -9639,7 +9771,7 @@ function renderDefaultRoleSelector(data) {
|
|
|
9639
9771
|
const next = { private: privateSelect.value || null, group: groupSelect.value || null };
|
|
9640
9772
|
try {
|
|
9641
9773
|
const token = localStorage.getItem(TOKEN_KEY);
|
|
9642
|
-
const writeRes = await
|
|
9774
|
+
const writeRes = await authFetch(roleDefinitionsApi('api/role-definitions'), {
|
|
9643
9775
|
method: 'PUT',
|
|
9644
9776
|
headers: {
|
|
9645
9777
|
'Content-Type': 'application/json',
|
|
@@ -9707,7 +9839,7 @@ function formatCommandPermissions(commandPermissions) {
|
|
|
9707
9839
|
async function fetchRoleOperationsCatalog() {
|
|
9708
9840
|
if (roleOperationsCache) return roleOperationsCache;
|
|
9709
9841
|
const token = localStorage.getItem(TOKEN_KEY);
|
|
9710
|
-
const res = await
|
|
9842
|
+
const res = await authFetch('api/role-definitions/operations', {
|
|
9711
9843
|
headers: { 'Authorization': `Bearer ${token}` }
|
|
9712
9844
|
});
|
|
9713
9845
|
if (!res.ok) throw new Error('Failed to load CLI operation catalog');
|
|
@@ -10229,8 +10361,9 @@ function createRoleCard(roleName, roleDef) {
|
|
|
10229
10361
|
const model = roleDef.permissions?.['baseagents.claude.model']?.default || '—';
|
|
10230
10362
|
const commandStats = getCommandPermissionStats(roleDef.commandPermissions);
|
|
10231
10363
|
|
|
10232
|
-
//
|
|
10233
|
-
|
|
10364
|
+
// Management identities and built-in user roles are both protected from
|
|
10365
|
+
// deletion; their local policy overrides can be reset instead.
|
|
10366
|
+
const builtinRoles = ['owner', 'admin', 'member', 'visitor'];
|
|
10234
10367
|
const isBuiltin = builtinRoles.includes(roleName);
|
|
10235
10368
|
|
|
10236
10369
|
card.innerHTML = `
|
|
@@ -10316,7 +10449,7 @@ function initRoleDefinitionsTab() {
|
|
|
10316
10449
|
|
|
10317
10450
|
try {
|
|
10318
10451
|
const token = localStorage.getItem(TOKEN_KEY);
|
|
10319
|
-
const res = await
|
|
10452
|
+
const res = await authFetch(roleDefinitionsApi(`api/role-definitions/${roleName}/reset`), {
|
|
10320
10453
|
method: 'POST',
|
|
10321
10454
|
headers: { 'Authorization': `Bearer ${token}` }
|
|
10322
10455
|
});
|
|
@@ -10361,7 +10494,7 @@ function initRoleDefinitionsTab() {
|
|
|
10361
10494
|
: replacement
|
|
10362
10495
|
? `?replacementRoleId=${encodeURIComponent(replacement)}`
|
|
10363
10496
|
: '';
|
|
10364
|
-
const res = await
|
|
10497
|
+
const res = await authFetch(roleDefinitionsApi(`api/role-definitions/${roleName}${replacementQuery}`), {
|
|
10365
10498
|
method: 'DELETE',
|
|
10366
10499
|
headers: { 'Authorization': `Bearer ${token}` }
|
|
10367
10500
|
});
|
|
@@ -10514,7 +10647,7 @@ function renderRoleModelPermissionSection(roleDef) {
|
|
|
10514
10647
|
|
|
10515
10648
|
async function fetchRoleModelPermissionData(roleName) {
|
|
10516
10649
|
const token = localStorage.getItem(TOKEN_KEY);
|
|
10517
|
-
const res = await
|
|
10650
|
+
const res = await authFetch(roleDefinitionsApi(`api/role-definitions/${encodeURIComponent(roleName)}/configurable-models`), {
|
|
10518
10651
|
headers: { 'Authorization': `Bearer ${token}` }
|
|
10519
10652
|
});
|
|
10520
10653
|
if (!res.ok) throw new Error('Failed to load role model permissions');
|
|
@@ -10522,13 +10655,14 @@ async function fetchRoleModelPermissionData(roleName) {
|
|
|
10522
10655
|
return json.data;
|
|
10523
10656
|
}
|
|
10524
10657
|
|
|
10525
|
-
async function fetchModelCatalogData(baseagent = 'claude') {
|
|
10658
|
+
async function fetchModelCatalogData(baseagent = 'claude', aid = '') {
|
|
10526
10659
|
const token = localStorage.getItem(TOKEN_KEY);
|
|
10527
|
-
const res = await
|
|
10660
|
+
const res = await authFetch(`api/models/catalog?baseagent=${encodeURIComponent(baseagent)}${aid ? `&aid=${encodeURIComponent(aid)}` : ''}`, {
|
|
10528
10661
|
headers: { 'Authorization': `Bearer ${token}` }
|
|
10529
10662
|
});
|
|
10530
10663
|
if (!res.ok) throw new Error('Failed to load model catalog');
|
|
10531
10664
|
const json = await res.json();
|
|
10665
|
+
if (json.data?.diagnostics?.some(d => d.code === 'AUTH_FAILED')) throw new Error('Model gateway authentication failed');
|
|
10532
10666
|
return json.data;
|
|
10533
10667
|
}
|
|
10534
10668
|
|
|
@@ -10691,7 +10825,7 @@ async function initRoleModelPermissionEditor(container, roleName, roleDef) {
|
|
|
10691
10825
|
try {
|
|
10692
10826
|
const data = roleName && roleName !== '__new__'
|
|
10693
10827
|
? await fetchRoleModelPermissionData(roleName)
|
|
10694
|
-
: await fetchModelCatalogData();
|
|
10828
|
+
: await fetchModelCatalogData('claude', roleDefinitionsCurrentAgent || state.roleDefinitions?.aid || '');
|
|
10695
10829
|
catalogModels = data?.catalog?.models || data?.models || [];
|
|
10696
10830
|
} catch (err) {
|
|
10697
10831
|
console.warn('[roleDefinitions] Failed to load model catalog:', err);
|
|
@@ -11488,7 +11622,7 @@ async function saveRoleDefinition() {
|
|
|
11488
11622
|
const method = isNew ? 'POST' : 'PUT';
|
|
11489
11623
|
const url = isNew ? roleDefinitionsApi('api/role-definitions') : roleDefinitionsApi(`api/role-definitions/${encodeURIComponent(actualRoleName)}`);
|
|
11490
11624
|
|
|
11491
|
-
const res = await
|
|
11625
|
+
const res = await authFetch(url, {
|
|
11492
11626
|
method,
|
|
11493
11627
|
headers: {
|
|
11494
11628
|
'Content-Type': 'application/json',
|