wendkeep 0.87.0 → 0.89.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/CHANGELOG.md +35 -0
- package/README.en.md +3 -2
- package/README.md +3 -2
- package/bin/wendkeep.mjs +1 -0
- package/docs/en/commands/ecosystem-bridges.md +172 -0
- package/docs/en/commands/observer-security.md +154 -0
- package/docs/en/commands/observer.md +30 -12
- package/docs/en/commands/verify.md +6 -0
- package/docs/pt-BR/commands/ecosystem-bridges.md +169 -0
- package/docs/pt-BR/commands/observer-security.md +154 -0
- package/docs/pt-BR/commands/observer.md +30 -12
- package/docs/pt-BR/commands/verify.md +6 -0
- package/hooks/observer-publish.mjs +3 -1
- package/package.json +2 -1
- package/packages/cli/src/index.mjs +10 -1
- package/packages/harness/src/sensors-core.mjs +49 -3
- package/packages/integrations/src/bridge-config.mjs +139 -0
- package/packages/integrations/src/bridge-contract.mjs +316 -0
- package/packages/integrations/src/bridge-diagnostics.mjs +45 -0
- package/packages/integrations/src/canonical-bridge-authority.mjs +32 -0
- package/packages/integrations/src/capabilities.mjs +34 -0
- package/packages/integrations/src/ecosystem-bridge.mjs +82 -0
- package/packages/integrations/src/index.mjs +6 -0
- package/packages/integrations/src/spec-kit-adapter.mjs +259 -0
- package/packages/integrations/src/superpowers-adapter.mjs +269 -0
- package/packages/mcp/src/executor.mjs +35 -2
- package/packages/observer/package.json +16 -0
- package/packages/observer/src/audit.mjs +1 -0
- package/packages/observer/src/authz.mjs +38 -0
- package/packages/observer/src/encryption.mjs +75 -0
- package/packages/observer/src/index.mjs +7 -0
- package/packages/observer/src/policy.mjs +305 -0
- package/packages/observer/src/purge.mjs +100 -0
- package/packages/observer/src/redaction.mjs +54 -0
- package/packages/observer/src/retention.mjs +39 -0
- package/packages/observer/src/token-registry.mjs +122 -0
- package/schema/ecosystem-bridge-artifact-manifest-v1.schema.json +30 -0
- package/schema/ecosystem-bridge-v1.schema.json +65 -0
- package/schema/observer/006-observer-security.sql +64 -0
- package/schema/observer-policy-v1.schema.json +63 -0
- package/schema/sync-event-v1.schema.json +10 -0
- package/schema/wendkeep.evidence-envelope-v2.schema.json +39 -0
- package/schema/wendkeep.sensors.schema.json +14 -0
- package/src/doctor.mjs +6 -1
- package/src/ecosystem-bridge-artifact-collector.mjs +111 -0
- package/src/ecosystem-bridge-baseline.mjs +58 -0
- package/src/ecosystem-bridge-proof.mjs +97 -0
- package/src/ecosystem-bridges.mjs +227 -0
- package/src/evidence-envelope.mjs +2 -0
- package/src/observer-auth.mjs +8 -0
- package/src/observer-privacy.mjs +7 -3
- package/src/observer-publish.mjs +31 -0
- package/src/observer-server.mjs +179 -20
- package/src/observer-sql-migrate.mjs +5 -2
- package/src/observer-sql-publish.mjs +114 -39
- package/src/observer-sql-store.mjs +299 -45
- package/src/observer-transcript-store.mjs +23 -8
- package/src/observer.mjs +145 -12
- package/src/sync-protocol.mjs +20 -0
- package/src/task-contracts.mjs +19 -0
- package/src/task.mjs +82 -0
- package/src/verify.mjs +9 -0
- package/web/observer/app.mjs +107 -31
- package/web/observer/index.html +7 -0
- package/web/observer/styles.css +5 -0
package/web/observer/app.mjs
CHANGED
|
@@ -75,9 +75,15 @@ export function classifyRefreshError(error = {}, hasModels = false) {
|
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
export function observerDashboardHeaders(token = '') {
|
|
79
|
+
return token
|
|
80
|
+
? { Accept: 'application/json', Authorization: `Bearer ${String(token)}` }
|
|
81
|
+
: { Accept: 'application/json' };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function requestJson(fetchImpl, url, token = '') {
|
|
79
85
|
const response = await fetchImpl(url, {
|
|
80
|
-
headers:
|
|
86
|
+
headers: observerDashboardHeaders(token),
|
|
81
87
|
});
|
|
82
88
|
if (!response.ok) {
|
|
83
89
|
const error = new Error(`Observer respondeu HTTP ${response.status}.`);
|
|
@@ -87,11 +93,11 @@ async function requestJson(fetchImpl, url) {
|
|
|
87
93
|
return response.json();
|
|
88
94
|
}
|
|
89
95
|
|
|
90
|
-
export async function loadProjectMemory(fetchImpl = globalThis.fetch, projectId = '') {
|
|
96
|
+
export async function loadProjectMemory(fetchImpl = globalThis.fetch, projectId = '', token = '') {
|
|
91
97
|
const id = encodeURIComponent(projectId);
|
|
92
98
|
const [tree, sync] = await Promise.all([
|
|
93
|
-
requestJson(fetchImpl, '/v1/projects/' + id + '/memory/tree'),
|
|
94
|
-
requestJson(fetchImpl, '/v1/projects/' + id + '/sync'),
|
|
99
|
+
requestJson(fetchImpl, '/v1/projects/' + id + '/memory/tree', token),
|
|
100
|
+
requestJson(fetchImpl, '/v1/projects/' + id + '/sync', token),
|
|
95
101
|
]);
|
|
96
102
|
return { tree, sync };
|
|
97
103
|
}
|
|
@@ -107,13 +113,13 @@ export function usageQuery(filters = {}) {
|
|
|
107
113
|
return query ? `?${query}` : '';
|
|
108
114
|
}
|
|
109
115
|
|
|
110
|
-
export async function loadProjectUsage(fetchImpl = globalThis.fetch, projectId = '', filters = {}) {
|
|
116
|
+
export async function loadProjectUsage(fetchImpl = globalThis.fetch, projectId = '', filters = {}, token = '') {
|
|
111
117
|
const id = encodeURIComponent(projectId);
|
|
112
118
|
const query = usageQuery(filters);
|
|
113
119
|
const [summary, breakdown, calls] = await Promise.all([
|
|
114
|
-
requestJson(fetchImpl, `/v1/projects/${id}/usage/summary${query}
|
|
115
|
-
requestJson(fetchImpl, `/v1/projects/${id}/usage/breakdown${query}
|
|
116
|
-
requestJson(fetchImpl, `/v1/projects/${id}/usage/calls${query}
|
|
120
|
+
requestJson(fetchImpl, `/v1/projects/${id}/usage/summary${query}`, token),
|
|
121
|
+
requestJson(fetchImpl, `/v1/projects/${id}/usage/breakdown${query}`, token),
|
|
122
|
+
requestJson(fetchImpl, `/v1/projects/${id}/usage/calls${query}`, token),
|
|
117
123
|
]);
|
|
118
124
|
return { summary, breakdown, calls, filters: { ...filters } };
|
|
119
125
|
}
|
|
@@ -163,29 +169,53 @@ export function buildUsageViewModel(usage = {}) {
|
|
|
163
169
|
};
|
|
164
170
|
}
|
|
165
171
|
|
|
166
|
-
export async function loadMemoryDocument(fetchImpl = globalThis.fetch, projectId = '', logicalPath = '') {
|
|
172
|
+
export async function loadMemoryDocument(fetchImpl = globalThis.fetch, projectId = '', logicalPath = '', token = '') {
|
|
167
173
|
const query = new URLSearchParams({ path: logicalPath });
|
|
168
|
-
return requestJson(fetchImpl, '/v1/projects/' + encodeURIComponent(projectId) + '/memory/document?' + query.toString());
|
|
174
|
+
return requestJson(fetchImpl, '/v1/projects/' + encodeURIComponent(projectId) + '/memory/document?' + query.toString(), token);
|
|
169
175
|
}
|
|
170
176
|
|
|
171
|
-
export async function loadProjectTranscript(fetchImpl = globalThis.fetch, projectId = '', transcriptId = '') {
|
|
172
|
-
return requestJson(fetchImpl, `/v1/projects/${encodeURIComponent(projectId)}/transcripts/${encodeURIComponent(transcriptId)}
|
|
177
|
+
export async function loadProjectTranscript(fetchImpl = globalThis.fetch, projectId = '', transcriptId = '', token = '') {
|
|
178
|
+
return requestJson(fetchImpl, `/v1/projects/${encodeURIComponent(projectId)}/transcripts/${encodeURIComponent(transcriptId)}`, token);
|
|
173
179
|
}
|
|
174
180
|
|
|
175
|
-
export async function searchProjectMemory(fetchImpl = globalThis.fetch, projectId = '', query = '') {
|
|
181
|
+
export async function searchProjectMemory(fetchImpl = globalThis.fetch, projectId = '', query = '', token = '') {
|
|
176
182
|
const params = new URLSearchParams({ q: query });
|
|
177
|
-
return requestJson(fetchImpl, '/v1/projects/' + encodeURIComponent(projectId) + '/memory/search?' + params.toString());
|
|
183
|
+
return requestJson(fetchImpl, '/v1/projects/' + encodeURIComponent(projectId) + '/memory/search?' + params.toString(), token);
|
|
178
184
|
}
|
|
179
185
|
|
|
180
|
-
export async function loadDashboardData(fetchImpl = globalThis.fetch) {
|
|
181
|
-
const index = await requestJson(fetchImpl, '/v1/projects');
|
|
186
|
+
export async function loadDashboardData(fetchImpl = globalThis.fetch, token = '') {
|
|
187
|
+
const index = await requestJson(fetchImpl, '/v1/projects', token);
|
|
182
188
|
const projects = Array.isArray(index?.projects) ? index.projects : [];
|
|
183
189
|
return Promise.all(projects.map(async (summary) => {
|
|
184
|
-
const detail = await requestJson(fetchImpl, `/v1/projects/${encodeURIComponent(summary.projectId)}
|
|
190
|
+
const detail = await requestJson(fetchImpl, `/v1/projects/${encodeURIComponent(summary.projectId)}`, token);
|
|
185
191
|
return buildProjectViewModel(summary, detail, new Date());
|
|
186
192
|
}));
|
|
187
193
|
}
|
|
188
194
|
|
|
195
|
+
export async function loadProjectSecurity(fetchImpl = globalThis.fetch, projectId = '', token = '') {
|
|
196
|
+
return requestJson(fetchImpl, `/v1/projects/${encodeURIComponent(projectId)}/security`, token);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export async function loadMemoryExport(fetchImpl = globalThis.fetch, projectId = '', token = '') {
|
|
200
|
+
return requestJson(fetchImpl, `/v1/projects/${encodeURIComponent(projectId)}/memory/export`, token);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function buildObserverSecurityViewModel(payload = {}) {
|
|
204
|
+
return {
|
|
205
|
+
tokenCount: Number(payload.tokens?.total || 0),
|
|
206
|
+
activeTokens: Number(payload.tokens?.active || 0),
|
|
207
|
+
revokedTokens: Number(payload.tokens?.revoked || 0),
|
|
208
|
+
encryptionRequired: payload.encryption?.required === true,
|
|
209
|
+
encryptionConfigured: payload.encryption?.configured === true,
|
|
210
|
+
policy: payload.policy || {},
|
|
211
|
+
recentAudit: (Array.isArray(payload.audit) ? payload.audit : []).map((row) => ({
|
|
212
|
+
capability: String(row.capability || ''),
|
|
213
|
+
outcome: String(row.outcome || ''),
|
|
214
|
+
occurredAt: String(row.occurred_at || ''),
|
|
215
|
+
})),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
189
219
|
export function buildProjectViewModel(summary = {}, detail = {}, now = new Date()) {
|
|
190
220
|
const snapshot = detail.snapshot || {};
|
|
191
221
|
const session = snapshot.session || {};
|
|
@@ -455,7 +485,7 @@ function renderChanges(container, projectId, documents) {
|
|
|
455
485
|
container.replaceChildren(heading, list);
|
|
456
486
|
}
|
|
457
487
|
|
|
458
|
-
function renderSync(container, sync, projectId = '') {
|
|
488
|
+
function renderSync(container, sync, projectId = '', onExport = null) {
|
|
459
489
|
const heading = node('div', 'workspace-section-heading');
|
|
460
490
|
heading.append(node('p', 'eyebrow', 'SYNC CONTROL'), node('h2', '', 'Sincronização'));
|
|
461
491
|
const facts = node('div', 'detail-facts');
|
|
@@ -465,11 +495,35 @@ function renderSync(container, sync, projectId = '') {
|
|
|
465
495
|
fact('Conflitos', sync?.conflict_count || 0),
|
|
466
496
|
);
|
|
467
497
|
const note = node('div', 'sync-callout', sync?.conflict_count ? 'Existem conflitos que exigem revisão.' : 'A memória local está acompanhando o container.');
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
498
|
+
const exportButton = node('button', 'workspace-open', 'Exportar cópia sanitizada →');
|
|
499
|
+
exportButton.type = 'button';
|
|
500
|
+
exportButton.addEventListener('click', async () => {
|
|
501
|
+
try {
|
|
502
|
+
const payload = await onExport?.();
|
|
503
|
+
const blob = new Blob([JSON.stringify(payload, null, 2)], { type: 'application/json' });
|
|
504
|
+
const url = URL.createObjectURL(blob);
|
|
505
|
+
const link = document.createElement('a');
|
|
506
|
+
link.href = url;
|
|
507
|
+
link.download = `wendkeep-observer-${projectId}.json`;
|
|
508
|
+
link.click();
|
|
509
|
+
URL.revokeObjectURL(url);
|
|
510
|
+
} catch { exportButton.textContent = 'Exportação não autorizada'; }
|
|
511
|
+
});
|
|
512
|
+
container.replaceChildren(heading, facts, note, exportButton);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function renderSecurity(container, payload) {
|
|
516
|
+
const view = buildObserverSecurityViewModel(payload);
|
|
517
|
+
const heading = node('div', 'workspace-section-heading');
|
|
518
|
+
heading.append(node('p', 'eyebrow', 'OBSERVER SECURITY'), node('h2', '', 'Política e acesso'));
|
|
519
|
+
const grid = node('div', 'detail-facts');
|
|
520
|
+
grid.append(
|
|
521
|
+
fact('Tokens ativos', view.activeTokens),
|
|
522
|
+
fact('Tokens revogados', view.revokedTokens),
|
|
523
|
+
fact('Criptografia', view.encryptionConfigured ? (view.encryptionRequired ? 'obrigatória' : 'configurada') : 'não configurada'),
|
|
524
|
+
);
|
|
525
|
+
const policy = node('pre', 'usage-transcript', JSON.stringify(view.policy, null, 2));
|
|
526
|
+
container.replaceChildren(heading, grid, node('h3', '', 'Política efetiva'), policy);
|
|
473
527
|
}
|
|
474
528
|
|
|
475
529
|
function formatNumber(value) {
|
|
@@ -670,6 +724,7 @@ function startDashboardV2() {
|
|
|
670
724
|
memory: new Map(),
|
|
671
725
|
usage: new Map(),
|
|
672
726
|
usageFilters: new Map(),
|
|
727
|
+
token: '',
|
|
673
728
|
route: parseObserverRoute(globalThis.location?.hash || ''),
|
|
674
729
|
};
|
|
675
730
|
const dashboardError = byId('dashboard-error');
|
|
@@ -726,12 +781,12 @@ function startDashboardV2() {
|
|
|
726
781
|
workspaceContent.replaceChildren(node('div', 'workspace-error', message || 'Não foi possível carregar a memória.'));
|
|
727
782
|
};
|
|
728
783
|
const ensureProjectMemory = async (projectId) => {
|
|
729
|
-
if (!state.memory.has(projectId)) state.memory.set(projectId, await loadProjectMemory(fetchJson, projectId));
|
|
784
|
+
if (!state.memory.has(projectId)) state.memory.set(projectId, await loadProjectMemory(fetchJson, projectId, state.token));
|
|
730
785
|
return state.memory.get(projectId);
|
|
731
786
|
};
|
|
732
787
|
const ensureProjectUsage = async (projectId) => {
|
|
733
788
|
const filters = state.usageFilters.get(projectId) || {};
|
|
734
|
-
const usage = await loadProjectUsage(fetchJson, projectId, filters);
|
|
789
|
+
const usage = await loadProjectUsage(fetchJson, projectId, filters, state.token);
|
|
735
790
|
state.usage.set(projectId, usage);
|
|
736
791
|
return usage;
|
|
737
792
|
};
|
|
@@ -752,7 +807,7 @@ function startDashboardV2() {
|
|
|
752
807
|
node('p', 'eyebrow', 'MEMORY SEARCH'),
|
|
753
808
|
node('h2', '', query ? 'Resultados para “' + query + '”' : 'Buscar na memória'),
|
|
754
809
|
);
|
|
755
|
-
const results = query ? (await searchProjectMemory(fetchJson, model.projectId, query)).results || [] : [];
|
|
810
|
+
const results = query ? (await searchProjectMemory(fetchJson, model.projectId, query, state.token)).results || [] : [];
|
|
756
811
|
const list = node('div', 'memory-document-list');
|
|
757
812
|
renderDocumentRows(list, model.projectId, results, query ? 'Nenhum documento contém esse termo.' : 'Digite um termo para pesquisar.');
|
|
758
813
|
workspaceContent?.replaceChildren(heading, list);
|
|
@@ -776,7 +831,7 @@ function startDashboardV2() {
|
|
|
776
831
|
setWorkspaceHeader(model, memory, route);
|
|
777
832
|
const documents = memory.tree?.documents || [];
|
|
778
833
|
if (route.kind === 'document') {
|
|
779
|
-
const payload = await loadMemoryDocument(fetchJson, model.projectId, route.logicalPath);
|
|
834
|
+
const payload = await loadMemoryDocument(fetchJson, model.projectId, route.logicalPath, state.token);
|
|
780
835
|
renderReader(workspaceContent, buildMemoryDocumentViewModel(payload, payload.content));
|
|
781
836
|
return;
|
|
782
837
|
}
|
|
@@ -787,12 +842,18 @@ function startDashboardV2() {
|
|
|
787
842
|
state.usageFilters.set(model.projectId, filters);
|
|
788
843
|
renderWorkspaceRoute({ ...route });
|
|
789
844
|
},
|
|
790
|
-
onTranscript: (transcriptId) => loadProjectTranscript(fetchJson, model.projectId, transcriptId),
|
|
845
|
+
onTranscript: (transcriptId) => loadProjectTranscript(fetchJson, model.projectId, transcriptId, state.token),
|
|
791
846
|
});
|
|
792
847
|
} else if (route.section === 'sessions') renderSessions(workspaceContent, model.projectId, documents);
|
|
793
848
|
else if (route.section === 'memory') renderMemory(workspaceContent, model.projectId, documents);
|
|
794
849
|
else if (route.section === 'changes') renderChanges(workspaceContent, model.projectId, documents);
|
|
795
|
-
else if (route.section === 'sync') renderSync(
|
|
850
|
+
else if (route.section === 'sync') renderSync(
|
|
851
|
+
workspaceContent,
|
|
852
|
+
memory.sync,
|
|
853
|
+
model.projectId,
|
|
854
|
+
() => loadMemoryExport(fetchJson, model.projectId, state.token),
|
|
855
|
+
);
|
|
856
|
+
else if (route.section === 'security') renderSecurity(workspaceContent, await loadProjectSecurity(fetchJson, model.projectId, state.token));
|
|
796
857
|
else renderWorkspaceOverview(workspaceContent, model, memory);
|
|
797
858
|
} catch (error) {
|
|
798
859
|
showWorkspaceError(error.message);
|
|
@@ -815,7 +876,7 @@ function startDashboardV2() {
|
|
|
815
876
|
const refresh = async () => {
|
|
816
877
|
setConnection('is-warning', 'Sincronizando');
|
|
817
878
|
try {
|
|
818
|
-
const models = await loadDashboardData(fetchJson);
|
|
879
|
+
const models = await loadDashboardData(fetchJson, state.token);
|
|
819
880
|
state.models = models;
|
|
820
881
|
if (!state.selectedId || !models.some((model) => model.projectId === state.selectedId)) state.selectedId = models[0]?.projectId || '';
|
|
821
882
|
setHidden(dashboardError, true);
|
|
@@ -831,6 +892,21 @@ function startDashboardV2() {
|
|
|
831
892
|
}
|
|
832
893
|
};
|
|
833
894
|
byId('refresh-button')?.addEventListener('click', refresh);
|
|
895
|
+
byId('observer-auth-form')?.addEventListener('submit', (event) => {
|
|
896
|
+
event.preventDefault();
|
|
897
|
+
state.token = byId('observer-token-input')?.value || '';
|
|
898
|
+
state.memory.clear();
|
|
899
|
+
state.usage.clear();
|
|
900
|
+
refresh();
|
|
901
|
+
});
|
|
902
|
+
byId('observer-token-clear')?.addEventListener('click', () => {
|
|
903
|
+
state.token = '';
|
|
904
|
+
const input = byId('observer-token-input');
|
|
905
|
+
if (input) input.value = '';
|
|
906
|
+
state.memory.clear();
|
|
907
|
+
state.usage.clear();
|
|
908
|
+
refresh();
|
|
909
|
+
});
|
|
834
910
|
byId('project-filter')?.addEventListener('input', (event) => {
|
|
835
911
|
state.filter = event.target.value;
|
|
836
912
|
renderProjectList(state.models, state.selectedId, state.filter);
|
package/web/observer/index.html
CHANGED
|
@@ -24,6 +24,12 @@
|
|
|
24
24
|
<div class="connection-cluster" aria-live="polite">
|
|
25
25
|
<span id="connection-dot" class="connection-dot is-offline" aria-hidden="true"></span>
|
|
26
26
|
<span id="connection-label">Conectando</span>
|
|
27
|
+
<form id="observer-auth-form" class="observer-auth-form" autocomplete="off">
|
|
28
|
+
<label class="sr-only" for="observer-token-input">Token do Observer</label>
|
|
29
|
+
<input id="observer-token-input" type="password" placeholder="Token" autocomplete="off" spellcheck="false">
|
|
30
|
+
<button type="submit">Conectar</button>
|
|
31
|
+
<button id="observer-token-clear" type="button" aria-label="Limpar token">×</button>
|
|
32
|
+
</form>
|
|
27
33
|
<form id="global-search-form" class="global-search">
|
|
28
34
|
<label class="sr-only" for="global-search">Buscar na memória</label>
|
|
29
35
|
<span aria-hidden="true">⌕</span>
|
|
@@ -101,6 +107,7 @@
|
|
|
101
107
|
<a data-workspace-section="memory" href="#project/project-a/memory">Memória</a>
|
|
102
108
|
<a data-workspace-section="changes" href="#project/project-a/changes">Changes</a>
|
|
103
109
|
<a data-workspace-section="sync" href="#project/project-a/sync">Sincronização</a>
|
|
110
|
+
<a data-workspace-section="security" href="#project/project-a/security">Segurança</a>
|
|
104
111
|
</nav>
|
|
105
112
|
<div id="workspace-meta" class="workspace-meta"></div>
|
|
106
113
|
</aside>
|
package/web/observer/styles.css
CHANGED
|
@@ -57,6 +57,10 @@ button:focus-visible, input:focus-visible, a:focus-visible { outline: 2px solid
|
|
|
57
57
|
.brand-name { display: block; font-family: Georgia, serif; font-size: 1.06rem; font-weight: 500; letter-spacing: -.02em; }
|
|
58
58
|
.eyebrow { margin: 0 0 7px; color: var(--faint); font-family: "Bahnschrift", sans-serif; font-size: .66rem; font-weight: 700; letter-spacing: .16em; line-height: 1; text-transform: uppercase; }
|
|
59
59
|
.connection-cluster { display: flex; align-items: center; gap: 9px; color: var(--muted); font-size: .78rem; }
|
|
60
|
+
.observer-auth-form { display: flex; align-items: center; gap: 4px; margin-left: 8px; }
|
|
61
|
+
.observer-auth-form input { width: 112px; padding: 7px 9px; border: 1px solid var(--line); border-radius: 8px; background: rgba(7, 16, 21, .65); color: var(--text); }
|
|
62
|
+
.observer-auth-form button { padding: 7px 9px; border: 1px solid var(--line); border-radius: 8px; background: transparent; color: var(--muted); cursor: pointer; }
|
|
63
|
+
.observer-auth-form button:hover { border-color: var(--mint); color: var(--mint-bright); }
|
|
60
64
|
.connection-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--faint); box-shadow: 0 0 0 4px rgba(94, 119, 116, .12); }
|
|
61
65
|
.connection-dot.is-online { background: var(--mint); box-shadow: 0 0 0 4px rgba(156, 224, 198, .1), 0 0 18px rgba(156, 224, 198, .7); }
|
|
62
66
|
.connection-dot.is-warning { background: var(--amber); box-shadow: 0 0 0 4px rgba(246, 189, 114, .1); }
|
|
@@ -232,6 +236,7 @@ input::placeholder { color: var(--faint); }
|
|
|
232
236
|
@media (max-width: 560px) {
|
|
233
237
|
.topbar { min-height: 76px; }
|
|
234
238
|
.connection-cluster > #connection-label { display: none; }
|
|
239
|
+
.observer-auth-form input { width: 80px; }
|
|
235
240
|
.global-search { margin-left: 5px; }
|
|
236
241
|
.global-search input { width: 92px; }
|
|
237
242
|
.hero-row { align-items: start; flex-direction: column; }
|