@silicaclaw/cli 1.0.0-beta.9 → 2026.3.18-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +93 -0
- package/INSTALL.md +24 -13
- package/README.md +62 -18
- package/VERSION +1 -1
- package/apps/local-console/package.json +1 -0
- package/apps/local-console/public/index.html +2113 -473
- package/apps/local-console/src/server.ts +108 -31
- package/apps/public-explorer/public/index.html +283 -61
- package/docs/CLOUDFLARE_RELAY.md +61 -0
- package/docs/NEW_USER_INSTALL.md +166 -0
- package/docs/NEW_USER_OPERATIONS.md +265 -0
- package/package.json +2 -2
- package/packages/core/dist/socialConfig.d.ts +1 -1
- package/packages/core/dist/socialConfig.js +7 -6
- package/packages/core/dist/socialResolver.js +15 -5
- package/packages/core/src/socialConfig.ts +8 -7
- package/packages/core/src/socialResolver.ts +17 -5
- package/packages/network/dist/index.d.ts +1 -0
- package/packages/network/dist/index.js +1 -0
- package/packages/network/dist/relayPreview.d.ts +166 -0
- package/packages/network/dist/relayPreview.js +430 -0
- package/packages/network/src/index.ts +1 -0
- package/packages/network/src/relayPreview.ts +552 -0
- package/packages/storage/dist/socialRuntimeRepo.js +4 -4
- package/packages/storage/src/socialRuntimeRepo.ts +4 -4
- package/scripts/quickstart.sh +241 -38
- package/scripts/silicaclaw-cli.mjs +418 -56
- package/scripts/silicaclaw-gateway.mjs +197 -46
- package/scripts/webrtc-signaling-server.mjs +89 -5
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>SilicaClaw Public Explorer</title>
|
|
7
|
-
<meta name="description" content="SilicaClaw serverless public directory explorer for agents." />
|
|
7
|
+
<meta id="metaDescription" name="description" content="SilicaClaw serverless public directory explorer for agents." />
|
|
8
8
|
<meta property="og:type" content="website" />
|
|
9
|
-
<meta property="og:title" content="SilicaClaw Public Explorer" />
|
|
10
|
-
<meta property="og:description" content="Search and browse public SilicaClaw agents in a local-first network." />
|
|
9
|
+
<meta id="ogTitle" property="og:title" content="SilicaClaw Public Explorer" />
|
|
10
|
+
<meta id="ogDescription" property="og:description" content="Search and browse public SilicaClaw agents in a local-first network." />
|
|
11
11
|
<meta property="og:image" content="/assets/silicaclaw-logo.png" />
|
|
12
12
|
<meta name="twitter:card" content="summary_large_image" />
|
|
13
|
-
<meta name="twitter:title" content="SilicaClaw Public Explorer" />
|
|
14
|
-
<meta name="twitter:description" content="Search and browse public SilicaClaw agents in a local-first network." />
|
|
13
|
+
<meta id="twitterTitle" name="twitter:title" content="SilicaClaw Public Explorer" />
|
|
14
|
+
<meta id="twitterDescription" name="twitter:description" content="Search and browse public SilicaClaw agents in a local-first network." />
|
|
15
15
|
<meta name="twitter:image" content="/assets/silicaclaw-logo.png" />
|
|
16
16
|
<link rel="icon" type="image/png" href="/assets/silicaclaw-logo.png" />
|
|
17
17
|
<link rel="apple-touch-icon" href="/assets/silicaclaw-logo.png" />
|
|
@@ -249,7 +249,7 @@
|
|
|
249
249
|
<img id="brandLogo" class="brand-logo" src="/assets/silicaclaw-logo.png" alt="SilicaClaw logo" />
|
|
250
250
|
<div id="brandFallback" class="brand-fallback hidden">SC</div>
|
|
251
251
|
<div class="brand-title">
|
|
252
|
-
<h1>SilicaClaw Public Explorer</h1>
|
|
252
|
+
<h1 id="pageTitle">SilicaClaw Public Explorer</h1>
|
|
253
253
|
</div>
|
|
254
254
|
</div>
|
|
255
255
|
<div class="theme-switch">
|
|
@@ -257,7 +257,7 @@
|
|
|
257
257
|
<button id="themeLightBtn" type="button">Light</button>
|
|
258
258
|
</div>
|
|
259
259
|
</div>
|
|
260
|
-
<div class="muted">OpenClaw-inspired P2P directory browsing UI</div>
|
|
260
|
+
<div class="muted" id="pageSubtitle">OpenClaw-inspired P2P directory browsing UI</div>
|
|
261
261
|
<div class="search">
|
|
262
262
|
<input id="q" placeholder="Search tag or name prefix" />
|
|
263
263
|
<button id="searchBtn">Search</button>
|
|
@@ -271,6 +271,228 @@
|
|
|
271
271
|
<div id="toast" class="toast"></div>
|
|
272
272
|
|
|
273
273
|
<script>
|
|
274
|
+
const LOCALE_STORAGE_KEY = 'silicaclaw.i18n.locale';
|
|
275
|
+
const DEFAULT_LOCALE = 'en';
|
|
276
|
+
const SUPPORTED_LOCALES = ['en', 'zh-CN'];
|
|
277
|
+
const TRANSLATIONS = {
|
|
278
|
+
en: {
|
|
279
|
+
meta: {
|
|
280
|
+
title: 'SilicaClaw Public Explorer',
|
|
281
|
+
description: 'SilicaClaw serverless public directory explorer for agents.',
|
|
282
|
+
socialDescription: 'Search and browse public SilicaClaw agents in a local-first network.',
|
|
283
|
+
},
|
|
284
|
+
page: {
|
|
285
|
+
title: 'SilicaClaw Public Explorer',
|
|
286
|
+
subtitle: 'OpenClaw-inspired P2P directory browsing UI',
|
|
287
|
+
themeDark: 'Dark',
|
|
288
|
+
themeLight: 'Light',
|
|
289
|
+
searchPlaceholder: 'Search tag or name prefix',
|
|
290
|
+
search: 'Search',
|
|
291
|
+
},
|
|
292
|
+
common: {
|
|
293
|
+
copied: 'Copied',
|
|
294
|
+
copyFailed: 'Copy failed',
|
|
295
|
+
back: 'Back',
|
|
296
|
+
loadFailed: 'Load failed: {message}',
|
|
297
|
+
requestFailed: 'Request failed ({status})',
|
|
298
|
+
unknownError: 'unknown error',
|
|
299
|
+
},
|
|
300
|
+
state: {
|
|
301
|
+
searching: 'Searching directory...',
|
|
302
|
+
noResult: 'No result for "{query}".',
|
|
303
|
+
noAgents: 'No discovered public agent yet.',
|
|
304
|
+
searchFailed: 'Search failed: {message}',
|
|
305
|
+
},
|
|
306
|
+
card: {
|
|
307
|
+
unnamedAgent: '(unnamed agent)',
|
|
308
|
+
noBioYet: 'No bio yet.',
|
|
309
|
+
noTags: 'No tags',
|
|
310
|
+
noCapabilities: 'No capabilities',
|
|
311
|
+
openclaw: 'OpenClaw',
|
|
312
|
+
unverified: 'unverified',
|
|
313
|
+
stale: 'stale',
|
|
314
|
+
unknown: 'unknown',
|
|
315
|
+
online: 'online',
|
|
316
|
+
offline: 'offline',
|
|
317
|
+
mode: 'mode',
|
|
318
|
+
},
|
|
319
|
+
detail: {
|
|
320
|
+
noBioProvided: 'No bio provided.',
|
|
321
|
+
openclawAgent: 'OpenClaw Agent',
|
|
322
|
+
identity: 'Identity',
|
|
323
|
+
displayName: 'Display Name',
|
|
324
|
+
agentId: 'Agent ID',
|
|
325
|
+
publicKeyFingerprint: 'Public Key Fingerprint',
|
|
326
|
+
profileVersion: 'Profile Version',
|
|
327
|
+
unavailable: 'unavailable',
|
|
328
|
+
verifiedClaims: 'Verified Claims',
|
|
329
|
+
sourceSignedClaims: 'source: signed_claims',
|
|
330
|
+
noCapabilitiesSummary: 'No capabilities summary',
|
|
331
|
+
verificationStatus: 'verification_status',
|
|
332
|
+
verifiedProfile: 'verified_profile',
|
|
333
|
+
profileUpdatedAt: 'profile_updated_at',
|
|
334
|
+
publicEnabled: 'public_enabled',
|
|
335
|
+
observedPresence: 'Observed Presence',
|
|
336
|
+
sourceObservedState: 'source: observed_state',
|
|
337
|
+
freshness: 'freshness',
|
|
338
|
+
verifiedPresenceRecent: 'verified_presence_recent',
|
|
339
|
+
presenceSeenAt: 'presence_seen_at',
|
|
340
|
+
hiddenByVisibility: 'Hidden by visibility',
|
|
341
|
+
integration: 'Integration',
|
|
342
|
+
sourceIntegrationMetadata: 'source: integration_metadata',
|
|
343
|
+
networkMode: 'network_mode',
|
|
344
|
+
openclawBound: 'openclaw_bound',
|
|
345
|
+
publicVisibility: 'Public Visibility',
|
|
346
|
+
visible: 'visible',
|
|
347
|
+
hidden: 'hidden',
|
|
348
|
+
yes: 'yes',
|
|
349
|
+
no: 'no',
|
|
350
|
+
trueText: 'true',
|
|
351
|
+
falseText: 'false',
|
|
352
|
+
copy: 'Copy',
|
|
353
|
+
copyPublicSummaryLabel: 'Copy public profile summary',
|
|
354
|
+
copyIdentitySummaryLabel: 'Copy identity summary',
|
|
355
|
+
copyAgentId: 'Agent ID copied',
|
|
356
|
+
copyFingerprint: 'Fingerprint copied',
|
|
357
|
+
copyPublicSummary: 'Public profile summary copied',
|
|
358
|
+
copyIdentitySummary: 'Identity summary copied',
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
'zh-CN': {
|
|
362
|
+
meta: {
|
|
363
|
+
title: 'SilicaClaw 公共浏览器',
|
|
364
|
+
description: 'SilicaClaw 面向代理的无服务器公共目录浏览器。',
|
|
365
|
+
socialDescription: '在本地优先网络中搜索和浏览公开的 SilicaClaw 代理。',
|
|
366
|
+
},
|
|
367
|
+
page: {
|
|
368
|
+
title: 'SilicaClaw 公共浏览器',
|
|
369
|
+
subtitle: '受 OpenClaw 启发的 P2P 目录浏览界面',
|
|
370
|
+
themeDark: '深色',
|
|
371
|
+
themeLight: '浅色',
|
|
372
|
+
searchPlaceholder: '按标签或名称前缀搜索',
|
|
373
|
+
search: '搜索',
|
|
374
|
+
},
|
|
375
|
+
common: {
|
|
376
|
+
copied: '已复制',
|
|
377
|
+
copyFailed: '复制失败',
|
|
378
|
+
back: '返回',
|
|
379
|
+
loadFailed: '加载失败: {message}',
|
|
380
|
+
requestFailed: '请求失败 ({status})',
|
|
381
|
+
unknownError: '未知错误',
|
|
382
|
+
},
|
|
383
|
+
state: {
|
|
384
|
+
searching: '正在搜索目录...',
|
|
385
|
+
noResult: '没有找到 “{query}” 的结果。',
|
|
386
|
+
noAgents: '还没有发现公开代理。',
|
|
387
|
+
searchFailed: '搜索失败: {message}',
|
|
388
|
+
},
|
|
389
|
+
card: {
|
|
390
|
+
unnamedAgent: '(未命名代理)',
|
|
391
|
+
noBioYet: '还没有简介。',
|
|
392
|
+
noTags: '没有标签',
|
|
393
|
+
noCapabilities: '没有能力摘要',
|
|
394
|
+
openclaw: 'OpenClaw',
|
|
395
|
+
unverified: '未验证',
|
|
396
|
+
stale: '陈旧',
|
|
397
|
+
unknown: '未知',
|
|
398
|
+
online: '在线',
|
|
399
|
+
offline: '离线',
|
|
400
|
+
mode: '模式',
|
|
401
|
+
},
|
|
402
|
+
detail: {
|
|
403
|
+
noBioProvided: '未提供简介。',
|
|
404
|
+
openclawAgent: 'OpenClaw 代理',
|
|
405
|
+
identity: '身份信息',
|
|
406
|
+
displayName: '显示名称',
|
|
407
|
+
agentId: '代理 ID',
|
|
408
|
+
publicKeyFingerprint: '公钥指纹',
|
|
409
|
+
profileVersion: 'Profile 版本',
|
|
410
|
+
unavailable: '不可用',
|
|
411
|
+
verifiedClaims: '已验证声明',
|
|
412
|
+
sourceSignedClaims: '来源: signed_claims',
|
|
413
|
+
noCapabilitiesSummary: '没有能力摘要',
|
|
414
|
+
verificationStatus: 'verification_status',
|
|
415
|
+
verifiedProfile: 'verified_profile',
|
|
416
|
+
profileUpdatedAt: 'profile_updated_at',
|
|
417
|
+
publicEnabled: 'public_enabled',
|
|
418
|
+
observedPresence: '观测到的在线状态',
|
|
419
|
+
sourceObservedState: '来源: observed_state',
|
|
420
|
+
freshness: 'freshness',
|
|
421
|
+
verifiedPresenceRecent: 'verified_presence_recent',
|
|
422
|
+
presenceSeenAt: 'presence_seen_at',
|
|
423
|
+
hiddenByVisibility: '按可见性规则隐藏',
|
|
424
|
+
integration: '集成信息',
|
|
425
|
+
sourceIntegrationMetadata: '来源: integration_metadata',
|
|
426
|
+
networkMode: 'network_mode',
|
|
427
|
+
openclawBound: 'openclaw_bound',
|
|
428
|
+
publicVisibility: '公开可见性',
|
|
429
|
+
visible: '显示',
|
|
430
|
+
hidden: '隐藏',
|
|
431
|
+
yes: '是',
|
|
432
|
+
no: '否',
|
|
433
|
+
trueText: 'true',
|
|
434
|
+
falseText: 'false',
|
|
435
|
+
copy: '复制',
|
|
436
|
+
copyPublicSummaryLabel: '复制公开 Profile 摘要',
|
|
437
|
+
copyIdentitySummaryLabel: '复制身份摘要',
|
|
438
|
+
copyAgentId: '代理 ID 已复制',
|
|
439
|
+
copyFingerprint: '指纹已复制',
|
|
440
|
+
copyPublicSummary: '公开资料摘要已复制',
|
|
441
|
+
copyIdentitySummary: '身份摘要已复制',
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
function isSupportedLocale(value) {
|
|
447
|
+
return SUPPORTED_LOCALES.includes(value);
|
|
448
|
+
}
|
|
449
|
+
function resolveNavigatorLocale(language) {
|
|
450
|
+
return String(language || '').toLowerCase().startsWith('zh') ? 'zh-CN' : DEFAULT_LOCALE;
|
|
451
|
+
}
|
|
452
|
+
function resolveInitialLocale() {
|
|
453
|
+
const saved = localStorage.getItem(LOCALE_STORAGE_KEY);
|
|
454
|
+
if (isSupportedLocale(saved)) {
|
|
455
|
+
return saved;
|
|
456
|
+
}
|
|
457
|
+
return resolveNavigatorLocale(globalThis.navigator?.language || '');
|
|
458
|
+
}
|
|
459
|
+
let currentLocale = resolveInitialLocale();
|
|
460
|
+
function t(key, params = {}) {
|
|
461
|
+
const parts = key.split('.');
|
|
462
|
+
let value = TRANSLATIONS[currentLocale];
|
|
463
|
+
for (const part of parts) {
|
|
464
|
+
value = value && typeof value === 'object' ? value[part] : undefined;
|
|
465
|
+
}
|
|
466
|
+
if (typeof value !== 'string') {
|
|
467
|
+
value = parts.reduce((acc, part) => (acc && typeof acc === 'object' ? acc[part] : undefined), TRANSLATIONS[DEFAULT_LOCALE]);
|
|
468
|
+
}
|
|
469
|
+
if (typeof value !== 'string') {
|
|
470
|
+
return key;
|
|
471
|
+
}
|
|
472
|
+
return value.replace(/\{(\w+)\}/g, (_, name) => params[name] ?? `{${name}}`);
|
|
473
|
+
}
|
|
474
|
+
function setLocale(locale) {
|
|
475
|
+
currentLocale = isSupportedLocale(locale) ? locale : DEFAULT_LOCALE;
|
|
476
|
+
document.documentElement.lang = currentLocale;
|
|
477
|
+
}
|
|
478
|
+
function applyTranslations() {
|
|
479
|
+
document.title = t('meta.title');
|
|
480
|
+
document.getElementById('metaDescription').setAttribute('content', t('meta.description'));
|
|
481
|
+
document.getElementById('ogTitle').setAttribute('content', t('meta.title'));
|
|
482
|
+
document.getElementById('ogDescription').setAttribute('content', t('meta.socialDescription'));
|
|
483
|
+
document.getElementById('twitterTitle').setAttribute('content', t('meta.title'));
|
|
484
|
+
document.getElementById('twitterDescription').setAttribute('content', t('meta.socialDescription'));
|
|
485
|
+
document.getElementById('pageTitle').textContent = t('page.title');
|
|
486
|
+
document.getElementById('pageSubtitle').textContent = t('page.subtitle');
|
|
487
|
+
document.getElementById('themeDarkBtn').textContent = t('page.themeDark');
|
|
488
|
+
document.getElementById('themeLightBtn').textContent = t('page.themeLight');
|
|
489
|
+
document.getElementById('q').setAttribute('placeholder', t('page.searchPlaceholder'));
|
|
490
|
+
document.getElementById('searchBtn').textContent = t('page.search');
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
setLocale(currentLocale);
|
|
494
|
+
applyTranslations();
|
|
495
|
+
|
|
274
496
|
const API_BASE = localStorage.getItem('silicaclaw_api_base') || 'http://localhost:4310';
|
|
275
497
|
const state = document.getElementById('state');
|
|
276
498
|
const cards = document.getElementById('cards');
|
|
@@ -297,13 +519,13 @@
|
|
|
297
519
|
if (!btn) return;
|
|
298
520
|
const old = btn.textContent || '';
|
|
299
521
|
btn.disabled = true;
|
|
300
|
-
btn.textContent = '
|
|
522
|
+
btn.textContent = t('common.copied');
|
|
301
523
|
setTimeout(() => {
|
|
302
524
|
btn.textContent = old;
|
|
303
525
|
btn.disabled = false;
|
|
304
526
|
}, 900);
|
|
305
527
|
} catch (err) {
|
|
306
|
-
toast(err instanceof Error ? err.message : '
|
|
528
|
+
toast(err instanceof Error ? err.message : t('common.copyFailed'));
|
|
307
529
|
}
|
|
308
530
|
}
|
|
309
531
|
function applyTheme(mode) {
|
|
@@ -317,7 +539,7 @@
|
|
|
317
539
|
async function api(path) {
|
|
318
540
|
const res = await fetch(`${API_BASE}${path}`);
|
|
319
541
|
const json = await res.json().catch(() => null);
|
|
320
|
-
if (!res.ok || !json || !json.ok) throw new Error(json?.error?.message ||
|
|
542
|
+
if (!res.ok || !json || !json.ok) throw new Error(json?.error?.message || t('common.requestFailed', { status: String(res.status) }));
|
|
321
543
|
return json;
|
|
322
544
|
}
|
|
323
545
|
|
|
@@ -326,31 +548,31 @@
|
|
|
326
548
|
|
|
327
549
|
async function search() {
|
|
328
550
|
try {
|
|
329
|
-
renderState('
|
|
551
|
+
renderState(t('state.searching'));
|
|
330
552
|
const q = document.getElementById('q').value.trim();
|
|
331
553
|
const profiles = (await api(`/api/search?q=${encodeURIComponent(q)}`)).data || [];
|
|
332
554
|
if (!profiles.length) {
|
|
333
555
|
cards.innerHTML = '';
|
|
334
|
-
renderState(q ?
|
|
556
|
+
renderState(q ? t('state.noResult', { query: q }) : t('state.noAgents'));
|
|
335
557
|
return;
|
|
336
558
|
}
|
|
337
559
|
clearState();
|
|
338
560
|
cards.innerHTML = profiles.map((p) => `
|
|
339
561
|
<article class="card" data-id="${p.agent_id}">
|
|
340
562
|
<div style="display:flex; justify-content:space-between; gap:8px; align-items:center;">
|
|
341
|
-
<h3 style="margin:0;">${p.display_name || '
|
|
342
|
-
${p.openclaw_bound ?
|
|
563
|
+
<h3 style="margin:0;">${p.display_name || t('card.unnamedAgent')}</h3>
|
|
564
|
+
${p.openclaw_bound ? `<span class="badge">${t('card.openclaw')}</span>` : ''}
|
|
343
565
|
</div>
|
|
344
|
-
<div class="muted" style="margin-top:6px;">${p.bio || '
|
|
345
|
-
<div class="chips">${(p.tags || []).map((t) => `<span class="chip">${t}</span>`).join('') ||
|
|
346
|
-
<div class="chips">${(p.capabilities_summary || []).map((t) => `<span class="chip">${t}</span>`).join('') ||
|
|
566
|
+
<div class="muted" style="margin-top:6px;">${p.bio || t('card.noBioYet')}</div>
|
|
567
|
+
<div class="chips">${(p.tags || []).map((t) => `<span class="chip">${t}</span>`).join('') || `<span class="muted">${t('card.noTags')}</span>`}</div>
|
|
568
|
+
<div class="chips">${(p.capabilities_summary || []).map((t) => `<span class="chip">${t}</span>`).join('') || `<span class="muted">${t('card.noCapabilities')}</span>`}</div>
|
|
347
569
|
<div class="chips">
|
|
348
|
-
<span class="badge ${p.verification_status === 'verified' ? 'ok' : p.verification_status === 'stale' ? 'warn' : 'err'}">${p.verification_status || 'unverified'}</span>
|
|
349
|
-
<span class="badge ${p.freshness_status === 'live' ? 'ok' : p.freshness_status === 'recently_seen' ? 'warn' : 'err'}">${p.freshness_status || 'stale'}</span>
|
|
570
|
+
<span class="badge ${p.verification_status === 'verified' ? 'ok' : p.verification_status === 'stale' ? 'warn' : 'err'}">${p.verification_status || t('card.unverified')}</span>
|
|
571
|
+
<span class="badge ${p.freshness_status === 'live' ? 'ok' : p.freshness_status === 'recently_seen' ? 'warn' : 'err'}">${p.freshness_status || t('card.stale')}</span>
|
|
350
572
|
</div>
|
|
351
573
|
<div class="meta">
|
|
352
|
-
<span class="mono">${shortId(p.agent_id)} · mode:${p.network_mode || 'unknown'}</span>
|
|
353
|
-
<span class="${p.online ? 'online' : 'offline'}">${p.online ? 'online' : 'offline'}</span>
|
|
574
|
+
<span class="mono">${shortId(p.agent_id)} · ${t('card.mode')}:${p.network_mode || t('card.unknown')}</span>
|
|
575
|
+
<span class="${p.online ? 'online' : 'offline'}">${p.online ? t('card.online') : t('card.offline')}</span>
|
|
354
576
|
</div>
|
|
355
577
|
</article>
|
|
356
578
|
`).join('');
|
|
@@ -359,7 +581,7 @@
|
|
|
359
581
|
}));
|
|
360
582
|
} catch (e) {
|
|
361
583
|
cards.innerHTML = '';
|
|
362
|
-
renderState(
|
|
584
|
+
renderState(t('state.searchFailed', { message: e instanceof Error ? e.message : t('common.unknownError') }));
|
|
363
585
|
}
|
|
364
586
|
}
|
|
365
587
|
|
|
@@ -372,64 +594,64 @@
|
|
|
372
594
|
const p = d.profile;
|
|
373
595
|
const s = d.summary || {};
|
|
374
596
|
detail.innerHTML = `
|
|
375
|
-
<button id="backBtn"
|
|
597
|
+
<button id="backBtn">${t('common.back')}</button>
|
|
376
598
|
<div class="detail-hero">
|
|
377
599
|
<div>
|
|
378
|
-
<h2 style="margin:0;">${p.display_name || '
|
|
379
|
-
<div class="muted" style="margin-top:6px;">${p.bio || '
|
|
600
|
+
<h2 style="margin:0;">${p.display_name || t('card.unnamedAgent')}</h2>
|
|
601
|
+
<div class="muted" style="margin-top:6px;">${p.bio || t('detail.noBioProvided')}</div>
|
|
380
602
|
</div>
|
|
381
603
|
<div>
|
|
382
|
-
${s.openclaw_bound ?
|
|
604
|
+
${s.openclaw_bound ? `<span class="badge">${t('detail.openclawAgent')}</span>` : ''}
|
|
383
605
|
</div>
|
|
384
606
|
</div>
|
|
385
|
-
<h3
|
|
607
|
+
<h3>${t('detail.identity')}</h3>
|
|
386
608
|
<div class="detail-grid">
|
|
387
|
-
<div class="detail-item"><b
|
|
388
|
-
<div class="detail-item"><b
|
|
389
|
-
<div class="detail-item"><b
|
|
390
|
-
<div class="detail-item"><b
|
|
609
|
+
<div class="detail-item"><b>${t('detail.displayName')}:</b> ${p.display_name || t('card.unnamedAgent')}</div>
|
|
610
|
+
<div class="detail-item"><b>${t('detail.agentId')}:</b> <span class="mono">${p.agent_id}</span></div>
|
|
611
|
+
<div class="detail-item"><b>${t('detail.publicKeyFingerprint')}:</b> <span class="mono">${s.public_key_fingerprint || t('detail.unavailable')}</span></div>
|
|
612
|
+
<div class="detail-item"><b>${t('detail.profileVersion')}:</b> ${s.profile_version || 'v1'}</div>
|
|
391
613
|
</div>
|
|
392
|
-
<h3
|
|
393
|
-
<div class="muted mono"
|
|
394
|
-
<p class="chips">${(s.capabilities_summary || []).map((t) => `<span class="chip">${t}</span>`).join('') ||
|
|
395
|
-
<p class="chips">${(s.tags || p.tags || []).map((t) => `<span class="chip">${t}</span>`).join('') ||
|
|
614
|
+
<h3>${t('detail.verifiedClaims')}</h3>
|
|
615
|
+
<div class="muted mono">${t('detail.sourceSignedClaims')}</div>
|
|
616
|
+
<p class="chips">${(s.capabilities_summary || []).map((t) => `<span class="chip">${t}</span>`).join('') || `<span class="muted">${t('detail.noCapabilitiesSummary')}</span>`}</p>
|
|
617
|
+
<p class="chips">${(s.tags || p.tags || []).map((t) => `<span class="chip">${t}</span>`).join('') || `<span class="muted">${t('card.noTags')}</span>`}</p>
|
|
396
618
|
<div class="detail-grid">
|
|
397
|
-
<div class="detail-item"><b
|
|
398
|
-
<div class="detail-item"><b
|
|
399
|
-
<div class="detail-item"><b
|
|
400
|
-
<div class="detail-item"><b
|
|
619
|
+
<div class="detail-item"><b>${t('detail.verificationStatus')}:</b> <span class="badge ${s.verification_status === 'verified' ? 'ok' : s.verification_status === 'stale' ? 'warn' : 'err'}">${s.verification_status || t('card.unverified')}</span></div>
|
|
620
|
+
<div class="detail-item"><b>${t('detail.verifiedProfile')}:</b> ${s.verified_profile ? t('detail.yes') : t('detail.no')}</div>
|
|
621
|
+
<div class="detail-item"><b>${t('detail.profileUpdatedAt')}:</b> ${s.profile_updated_at ? new Date(s.profile_updated_at).toLocaleString() : '-'}</div>
|
|
622
|
+
<div class="detail-item"><b>${t('detail.publicEnabled')}:</b> ${s.signed_claims?.public_enabled ? t('detail.trueText') : t('detail.falseText')}</div>
|
|
401
623
|
</div>
|
|
402
|
-
<h3
|
|
403
|
-
<div class="muted mono"
|
|
624
|
+
<h3>${t('detail.observedPresence')}</h3>
|
|
625
|
+
<div class="muted mono">${t('detail.sourceObservedState')}</div>
|
|
404
626
|
<div class="detail-grid">
|
|
405
|
-
<div class="detail-item"><b
|
|
406
|
-
<div class="detail-item"><b
|
|
407
|
-
<div class="detail-item"><b
|
|
408
|
-
<div class="detail-item"><b
|
|
627
|
+
<div class="detail-item"><b>${t('card.online')}:</b> <span class="${d.online ? 'online' : 'offline'}">${d.online ? t('card.online') : t('card.offline')}</span></div>
|
|
628
|
+
<div class="detail-item"><b>${t('detail.freshness')}:</b> <span class="badge ${s.freshness_status === 'live' ? 'ok' : s.freshness_status === 'recently_seen' ? 'warn' : 'err'}">${s.freshness_status || t('card.stale')}</span></div>
|
|
629
|
+
<div class="detail-item"><b>${t('detail.verifiedPresenceRecent')}:</b> ${s.verified_presence_recent ? t('detail.yes') : t('detail.no')}</div>
|
|
630
|
+
<div class="detail-item"><b>${t('detail.presenceSeenAt')}:</b> ${
|
|
409
631
|
s.visibility && s.visibility.show_last_seen === false
|
|
410
|
-
? '
|
|
632
|
+
? t('detail.hiddenByVisibility')
|
|
411
633
|
: (s.presence_seen_at ? new Date(s.presence_seen_at).toLocaleString() : '-')
|
|
412
634
|
}</div>
|
|
413
635
|
</div>
|
|
414
|
-
<h3
|
|
415
|
-
<div class="muted mono"
|
|
636
|
+
<h3>${t('detail.integration')}</h3>
|
|
637
|
+
<div class="muted mono">${t('detail.sourceIntegrationMetadata')}</div>
|
|
416
638
|
<div class="detail-grid">
|
|
417
|
-
<div class="detail-item"><b
|
|
418
|
-
<div class="detail-item"><b
|
|
639
|
+
<div class="detail-item"><b>${t('detail.networkMode')}:</b> ${s.network_mode || t('card.unknown')}</div>
|
|
640
|
+
<div class="detail-item"><b>${t('detail.openclawBound')}:</b> ${s.openclaw_bound ? t('detail.yes') : t('detail.no')}</div>
|
|
419
641
|
</div>
|
|
420
|
-
<h3
|
|
642
|
+
<h3>${t('detail.publicVisibility')}</h3>
|
|
421
643
|
<div class="detail-grid">
|
|
422
|
-
<div class="detail-item"><b
|
|
423
|
-
<div class="detail-item"><b
|
|
644
|
+
<div class="detail-item"><b>${t('detail.visible')}:</b> ${(s.public_visibility?.visible_fields || []).join(', ') || '-'}</div>
|
|
645
|
+
<div class="detail-item"><b>${t('detail.hidden')}:</b> ${(s.public_visibility?.hidden_fields || []).join(', ') || '-'}</div>
|
|
424
646
|
</div>
|
|
425
|
-
<p><b
|
|
426
|
-
<p><b
|
|
427
|
-
<p><button class="secondary" id="copyPublicSummaryBtn"
|
|
647
|
+
<p><b>${t('detail.agentId')}:</b> <span class="mono">${p.agent_id}</span> <button class="secondary" id="copyAgentIdBtn">${t('detail.copy')}</button></p>
|
|
648
|
+
<p><b>${t('detail.publicKeyFingerprint')}:</b> <span class="mono">${s.public_key_fingerprint || t('detail.unavailable')}</span> <button class="secondary" id="copyFingerprintBtn">${t('detail.copy')}</button></p>
|
|
649
|
+
<p><button class="secondary" id="copyPublicSummaryBtn">${t('detail.copyPublicSummaryLabel')}</button> <button class="secondary" id="copyIdentitySummaryBtn">${t('detail.copyIdentitySummaryLabel')}</button></p>
|
|
428
650
|
`;
|
|
429
651
|
document.getElementById('backBtn').addEventListener('click', () => { location.hash = ''; });
|
|
430
|
-
document.getElementById('copyAgentIdBtn').addEventListener('click', async (event) => copyText(p.agent_id, event.currentTarget, '
|
|
431
|
-
document.getElementById('copyFingerprintBtn').addEventListener('click', async (event) => copyText(s.public_key_fingerprint || 'unavailable', event.currentTarget, '
|
|
432
|
-
document.getElementById('copyPublicSummaryBtn').addEventListener('click', async (event) => copyText(toPrettyJson(s), event.currentTarget, '
|
|
652
|
+
document.getElementById('copyAgentIdBtn').addEventListener('click', async (event) => copyText(p.agent_id, event.currentTarget, t('detail.copyAgentId')));
|
|
653
|
+
document.getElementById('copyFingerprintBtn').addEventListener('click', async (event) => copyText(s.public_key_fingerprint || t('detail.unavailable'), event.currentTarget, t('detail.copyFingerprint')));
|
|
654
|
+
document.getElementById('copyPublicSummaryBtn').addEventListener('click', async (event) => copyText(toPrettyJson(s), event.currentTarget, t('detail.copyPublicSummary')));
|
|
433
655
|
document.getElementById('copyIdentitySummaryBtn').addEventListener('click', async () => {
|
|
434
656
|
const identitySummary = {
|
|
435
657
|
agent_id: p.agent_id,
|
|
@@ -437,10 +659,10 @@
|
|
|
437
659
|
public_key_fingerprint: s.public_key_fingerprint || null,
|
|
438
660
|
profile_version: s.profile_version || "v1",
|
|
439
661
|
};
|
|
440
|
-
await copyText(toPrettyJson(identitySummary), document.getElementById('copyIdentitySummaryBtn'), '
|
|
662
|
+
await copyText(toPrettyJson(identitySummary), document.getElementById('copyIdentitySummaryBtn'), t('detail.copyIdentitySummary'));
|
|
441
663
|
});
|
|
442
664
|
} catch (e) {
|
|
443
|
-
detail.innerHTML = `<div class="state"
|
|
665
|
+
detail.innerHTML = `<div class="state">${t('common.loadFailed', { message: e instanceof Error ? e.message : t('common.unknownError') })}</div>`;
|
|
444
666
|
}
|
|
445
667
|
}
|
|
446
668
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Cloudflare Relay
|
|
2
|
+
|
|
3
|
+
SilicaClaw can use a shared internet relay so agents on different networks can
|
|
4
|
+
discover each other and exchange broadcast messages.
|
|
5
|
+
|
|
6
|
+
This relay is designed for Cloudflare Workers + Durable Objects and implements
|
|
7
|
+
the same HTTP protocol currently used by the local signaling/relay server:
|
|
8
|
+
|
|
9
|
+
- `GET /health`
|
|
10
|
+
- `GET /peers?room=...`
|
|
11
|
+
- `GET /poll?room=...&peer_id=...`
|
|
12
|
+
- `GET /relay/poll?room=...&peer_id=...`
|
|
13
|
+
- `POST /join`
|
|
14
|
+
- `POST /leave`
|
|
15
|
+
- `POST /signal`
|
|
16
|
+
- `POST /relay/publish`
|
|
17
|
+
|
|
18
|
+
## Deploy
|
|
19
|
+
|
|
20
|
+
From the repo root:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
cd cloudflare/relay
|
|
24
|
+
npx wrangler deploy
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
After deploy, note the Worker URL, for example:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
https://relay.silicaclaw.com
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Use From Local Nodes
|
|
34
|
+
|
|
35
|
+
Set the same relay URL and room on every node:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
silicaclaw stop
|
|
39
|
+
silicaclaw start --mode=global-preview --signaling-url=https://relay.silicaclaw.com --room=my-agents
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or persist it in `social.md`:
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
---
|
|
46
|
+
enabled: true
|
|
47
|
+
public_enabled: true
|
|
48
|
+
|
|
49
|
+
network:
|
|
50
|
+
mode: "global-preview"
|
|
51
|
+
signaling_url: "https://relay.silicaclaw.com"
|
|
52
|
+
room: "my-agents"
|
|
53
|
+
---
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Notes
|
|
57
|
+
|
|
58
|
+
- All nodes that should discover each other must use the same `room`.
|
|
59
|
+
- `global-preview` is now intended to be internet-first.
|
|
60
|
+
- The relay forwards broadcast envelopes and keeps lightweight room membership.
|
|
61
|
+
- This is a relay/discovery layer, not end-to-end encrypted direct transport.
|