@flame0510/project-aether 1.1.9
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 +150 -0
- package/agent-templates/README.md +37 -0
- package/agent-templates/argus/AGENTS.md +52 -0
- package/agent-templates/argus/IDENTITY.md +7 -0
- package/agent-templates/argus/MEMORY.md +9 -0
- package/agent-templates/argus/SOUL.md +24 -0
- package/agent-templates/argus/TOOLS.md +8 -0
- package/agent-templates/atlas/AGENTS.md +47 -0
- package/agent-templates/atlas/HEARTBEAT.md +10 -0
- package/agent-templates/atlas/IDENTITY.md +6 -0
- package/agent-templates/atlas/MEMORY.md +9 -0
- package/agent-templates/atlas/SOUL.md +21 -0
- package/agent-templates/atlas/TOOLS.md +4 -0
- package/agent-templates/base-image/.trigger +0 -0
- package/agent-templates/base-image/Dockerfile +90 -0
- package/agent-templates/base-image/entrypoint.sh +37 -0
- package/agent-templates/custom/AGENTS.md +29 -0
- package/agent-templates/custom/BOOTSTRAP.md +56 -0
- package/agent-templates/custom/MEMORY.md +8 -0
- package/agent-templates/prometheus/AGENTS.md +37 -0
- package/agent-templates/prometheus/IDENTITY.md +6 -0
- package/agent-templates/prometheus/MEMORY.md +9 -0
- package/agent-templates/prometheus/SOUL.md +21 -0
- package/agent-templates/prometheus/TOOLS.md +4 -0
- package/app/agents/ChannelManager.tsx +465 -0
- package/app/agents/ImageDownloadBanner.tsx +159 -0
- package/app/agents/PageClient.tsx +1285 -0
- package/app/agents/create/PageClient.tsx +437 -0
- package/app/agents/create/page.tsx +126 -0
- package/app/agents/loading.tsx +14 -0
- package/app/agents/page.tsx +5 -0
- package/app/api/agents/[id]/backup/route.ts +169 -0
- package/app/api/agents/[id]/channels/pairing/route.ts +69 -0
- package/app/api/agents/[id]/channels/route.ts +14 -0
- package/app/api/agents/[id]/channels/telegram/route.ts +50 -0
- package/app/api/agents/[id]/lifecycle/route.ts +94 -0
- package/app/api/agents/[id]/recreate/route.ts +199 -0
- package/app/api/agents/[id]/restart/route.ts +33 -0
- package/app/api/agents/[id]/restore/route.ts +81 -0
- package/app/api/agents/[id]/route.ts +280 -0
- package/app/api/agents/channels-summary/route.ts +39 -0
- package/app/api/agents/config-json/route.ts +58 -0
- package/app/api/agents/create/route.ts +488 -0
- package/app/api/agents/download-image/route.ts +32 -0
- package/app/api/agents/image-status/route.ts +126 -0
- package/app/api/agents/route.ts +213 -0
- package/app/api/agents/token/route.ts +94 -0
- package/app/api/agents-active/route.ts +190 -0
- package/app/api/agents-config/route.ts +629 -0
- package/app/api/alerts/smoke/route.ts +26 -0
- package/app/api/aliases/add/route.ts +28 -0
- package/app/api/aliases/remove/route.ts +27 -0
- package/app/api/assistant/route.ts +204 -0
- package/app/api/auth/check/route.ts +38 -0
- package/app/api/auth/login/route.ts +85 -0
- package/app/api/auth/logout/route.ts +7 -0
- package/app/api/auth/status/route.ts +33 -0
- package/app/api/config/env/route.ts +132 -0
- package/app/api/config/restart/route.ts +26 -0
- package/app/api/containers/route.ts +75 -0
- package/app/api/cost-override/route.ts +46 -0
- package/app/api/costs/route.ts +84 -0
- package/app/api/credentials/[id]/reveal/route.ts +21 -0
- package/app/api/credentials/[id]/route.ts +52 -0
- package/app/api/credentials/[id]/sync/route.ts +58 -0
- package/app/api/credentials/detect/route.ts +246 -0
- package/app/api/credentials/route.ts +33 -0
- package/app/api/crons/[id]/route.ts +69 -0
- package/app/api/crons/route.ts +12 -0
- package/app/api/crons/runs/route.ts +31 -0
- package/app/api/debug/route.ts +11 -0
- package/app/api/envcheck/route.ts +18 -0
- package/app/api/events/route.ts +24 -0
- package/app/api/gateway/agent/route.ts +154 -0
- package/app/api/gateway/provider/balance/route.ts +38 -0
- package/app/api/gateway/provider/keys.ts +38 -0
- package/app/api/gateway/provider/oauth/route.ts +304 -0
- package/app/api/gateway/provider/route.ts +209 -0
- package/app/api/gateway/route.ts +223 -0
- package/app/api/gateway/sync.ts +189 -0
- package/app/api/lineage/route.ts +54 -0
- package/app/api/memory-context/route.ts +15 -0
- package/app/api/metrics/route.ts +35 -0
- package/app/api/models/route.ts +77 -0
- package/app/api/plugins/route.js +37 -0
- package/app/api/provider/upstream.ts +142 -0
- package/app/api/provider/v1/chat/completions/route.ts +146 -0
- package/app/api/provider/v1/models/route.ts +167 -0
- package/app/api/provider-usage/route.ts +629 -0
- package/app/api/session/route.ts +39 -0
- package/app/api/sessions/[id]/route.ts +38 -0
- package/app/api/sessions/route.ts +23 -0
- package/app/api/setup/agent-image/route.ts +102 -0
- package/app/api/setup/password/route.ts +111 -0
- package/app/api/setup/restart/route.ts +76 -0
- package/app/api/skills/delete/route.js +105 -0
- package/app/api/skills/promote/route.js +129 -0
- package/app/api/skills/route.js +203 -0
- package/app/api/skills/save/route.js +142 -0
- package/app/api/stats/route.ts +34 -0
- package/app/api/stats-since/route.ts +20 -0
- package/app/api/stream/route.ts +70 -0
- package/app/api/system-health/route.ts +233 -0
- package/app/api/tool-calls/route.ts +24 -0
- package/app/api/tools-config/route.ts +163 -0
- package/app/api/update-check/route.ts +127 -0
- package/app/api/version/route.ts +25 -0
- package/app/api/wizard/complete/route.ts +32 -0
- package/app/api/wizard/reset/route.ts +28 -0
- package/app/api/wizard/status/route.ts +28 -0
- package/app/api/workspace/route.ts +392 -0
- package/app/api/workspace/stream/route.ts +136 -0
- package/app/components/AppShell.tsx +33 -0
- package/app/components/AuthGuard.tsx +91 -0
- package/app/components/CostBreakdown.tsx +37 -0
- package/app/components/DashboardHeader.tsx +73 -0
- package/app/components/DashboardLayout.tsx +76 -0
- package/app/components/DashboardToolbar.tsx +61 -0
- package/app/components/FloatingActions.tsx +33 -0
- package/app/components/Icons.tsx +78 -0
- package/app/components/LineageGraphPage.tsx +142 -0
- package/app/components/LiveFeed.tsx +48 -0
- package/app/components/MobileBottomNav.tsx +84 -0
- package/app/components/ModelPickerModal.tsx +94 -0
- package/app/components/PasswordInput.tsx +86 -0
- package/app/components/PulseChat.tsx +362 -0
- package/app/components/Rev4aLoader.tsx +48 -0
- package/app/components/SessionDrawer.tsx +263 -0
- package/app/components/SessionTopology.tsx +289 -0
- package/app/components/Sidebar.tsx +294 -0
- package/app/components/Skeleton.tsx +133 -0
- package/app/components/SystemCockpit.tsx +169 -0
- package/app/components/VersionBanner.tsx +105 -0
- package/app/components/WizardButton.tsx +30 -0
- package/app/components/ui/Badge.tsx +30 -0
- package/app/components/ui/Button.tsx +59 -0
- package/app/components/ui/ConfirmModal.tsx +46 -0
- package/app/components/ui/FilterBar.tsx +33 -0
- package/app/components/ui/Input.tsx +25 -0
- package/app/components/ui/ItemList.tsx +83 -0
- package/app/components/ui/LoadingSpinner.tsx +30 -0
- package/app/components/ui/Metric.tsx +22 -0
- package/app/components/ui/Modal.tsx +42 -0
- package/app/components/ui/Page.tsx +18 -0
- package/app/components/ui/Pill.tsx +6 -0
- package/app/components/ui/PropertyList.tsx +24 -0
- package/app/components/ui/Select.tsx +26 -0
- package/app/components/ui/StatusCard.tsx +32 -0
- package/app/components/ui/Surface.tsx +14 -0
- package/app/components/ui/Tabs.tsx +55 -0
- package/app/components/ui/TemplateOption.tsx +59 -0
- package/app/components/ui/Toast.tsx +44 -0
- package/app/components/ui/index.ts +19 -0
- package/app/components/ui/tokens.ts +18 -0
- package/app/config/PageClient.tsx +293 -0
- package/app/config/loading.tsx +10 -0
- package/app/config/page.tsx +7 -0
- package/app/containers/ContainersClient.tsx +129 -0
- package/app/containers/loading.tsx +21 -0
- package/app/containers/page.tsx +5 -0
- package/app/containers/terminal/[id]/TerminalClient.tsx +313 -0
- package/app/containers/terminal/[id]/page.tsx +6 -0
- package/app/credentials/PageClient.tsx +598 -0
- package/app/credentials/loading.tsx +10 -0
- package/app/credentials/page.tsx +7 -0
- package/app/crons/PageClient.tsx +538 -0
- package/app/crons/loading.tsx +10 -0
- package/app/crons/page.tsx +5 -0
- package/app/design-system.ts +57 -0
- package/app/gateway/PageClient.tsx +1048 -0
- package/app/gateway/loading.tsx +24 -0
- package/app/gateway/page.tsx +5 -0
- package/app/globals.css +1474 -0
- package/app/layout.tsx +45 -0
- package/app/lib/models-context.tsx +41 -0
- package/app/lineage/loading.tsx +10 -0
- package/app/lineage/page.tsx +13 -0
- package/app/loading.tsx +16 -0
- package/app/login/page.tsx +160 -0
- package/app/memory/MemoryContextPageClient.tsx +196 -0
- package/app/memory/loading.tsx +10 -0
- package/app/memory/page.tsx +8 -0
- package/app/page.tsx +31 -0
- package/app/plugins/PageClient.tsx +296 -0
- package/app/plugins/loading.tsx +10 -0
- package/app/plugins/page.tsx +5 -0
- package/app/setup/PageClient.tsx +227 -0
- package/app/setup/loading.tsx +21 -0
- package/app/setup/page.tsx +7 -0
- package/app/skills/PageClient.tsx +499 -0
- package/app/skills/loading.tsx +10 -0
- package/app/skills/page.tsx +5 -0
- package/app/tools/ToolsPageClient.tsx +308 -0
- package/app/tools/loading.tsx +10 -0
- package/app/tools/page.tsx +62 -0
- package/app/tools/tools-catalog.ts +141 -0
- package/app/wizard/PageClient.tsx +485 -0
- package/app/wizard/icons.tsx +89 -0
- package/app/wizard/loading.tsx +24 -0
- package/app/wizard/page.tsx +7 -0
- package/app/wizard/storage.ts +33 -0
- package/app/wizard/useWizard.ts +101 -0
- package/app/workspace/WorkspaceClient.tsx +633 -0
- package/app/workspace/loading.tsx +36 -0
- package/app/workspace/page.tsx +8 -0
- package/bin/docker-entrypoint.sh +21 -0
- package/bin/postinstall.js +20 -0
- package/bin/rev4a.js +696 -0
- package/daemon.js +783 -0
- package/docs/ARCHITECTURE.md +557 -0
- package/docs/CONTAINER-TERMINAL.md +280 -0
- package/docs/DESIGN-SYSTEM.md +99 -0
- package/docs/FRONTEND-ARCHITECTURE.md +119 -0
- package/docs/REV4A.md +504 -0
- package/docs/dev/API-REFERENCE.md +1835 -0
- package/docs/dev/DATABASE.md +224 -0
- package/docs/dev/GATEWAY.md +300 -0
- package/docs/dev/PROVIDERS.md +229 -0
- package/docs/dev/SESSION-MAINTENANCE-PLAN.md +607 -0
- package/docs/dev/WORKSPACE.md +199 -0
- package/docs/rag/DATA-FRESHNESS.md +109 -0
- package/docs/rag/GLOSSARY.md +125 -0
- package/docs/rag/REV4A-OVERVIEW.md +135 -0
- package/docs/rag/WHAT-I-CAN-ANSWER.md +124 -0
- package/lib/agent-setup.ts +130 -0
- package/lib/alerts.ts +115 -0
- package/lib/apiFetch.ts +7 -0
- package/lib/auth.ts +28 -0
- package/lib/billing.ts +100 -0
- package/lib/buildAgentImage.ts +346 -0
- package/lib/channelManager.ts +386 -0
- package/lib/container.ts +60 -0
- package/lib/credentials/db.ts +86 -0
- package/lib/credentials/delivery.ts +448 -0
- package/lib/credentials/detect.ts +149 -0
- package/lib/credentials/hash.ts +9 -0
- package/lib/credentials/providers.ts +110 -0
- package/lib/credentials/vault.ts +195 -0
- package/lib/db-bootstrap.d.ts +3 -0
- package/lib/db-bootstrap.mjs +144 -0
- package/lib/db.ts +25 -0
- package/lib/docker-utils.ts +60 -0
- package/lib/hooks/useDashboard.ts +264 -0
- package/lib/hooks/useRev4aTimezone.ts +28 -0
- package/lib/hooks/useScreenshot.ts +30 -0
- package/lib/memory-context.ts +385 -0
- package/lib/model-catalogue.ts +131 -0
- package/lib/model-pricing.ts +63 -0
- package/lib/openclaw-cron.ts +350 -0
- package/lib/patterns/ApiAdapter.ts +73 -0
- package/lib/patterns/ApiClient.ts +65 -0
- package/lib/patterns/EventBus.ts +113 -0
- package/lib/patterns/FilterStrategy.ts +79 -0
- package/lib/patterns/SessionFactory.ts +117 -0
- package/lib/patterns/sessionPresentation.ts +117 -0
- package/lib/provider-balance.ts +163 -0
- package/lib/requireAuth.tsx +38 -0
- package/lib/rev4a-auth.d.ts +3 -0
- package/lib/rev4a-auth.js +36 -0
- package/lib/rev4a-paths.ts +145 -0
- package/lib/timezone.ts +46 -0
- package/lib/types/index.ts +96 -0
- package/lib/utils/format.ts +82 -0
- package/lineage.js +56 -0
- package/model-pricing.json +482 -0
- package/models.config.json +844 -0
- package/next-env.d.ts +6 -0
- package/next.config.mjs +7 -0
- package/package.json +82 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon-64.png +0 -0
- package/public/favicon.svg +58 -0
- package/public/icon-192-maskable.png +0 -0
- package/public/icon-192.png +0 -0
- package/public/icon-512-maskable.png +0 -0
- package/public/icon-512.png +0 -0
- package/public/manifest.json +38 -0
- package/public/rev4a-logo.png +0 -0
- package/public/rev4a-logo.svg +58 -0
- package/rev4a-rules/AGENTS.md +37 -0
- package/scripts/add-auth.cjs +185 -0
- package/scripts/backup.sh +66 -0
- package/scripts/check-providers.mjs +143 -0
- package/scripts/docker/traefik/docker-compose.yml +32 -0
- package/scripts/docker/traefik/dynamic/.gitkeep +18 -0
- package/scripts/docker/traefik/traefik.yml +37 -0
- package/scripts/remove-auth-profile.js +56 -0
- package/scripts/restore.sh +82 -0
- package/terminal-ws-server.js +139 -0
- package/tsconfig.json +42 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { chromium } from '@playwright/test';
|
|
2
|
+
import http from 'http';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Playwright check: providers page, Atlas agent, DeepSeek key visibility.
|
|
6
|
+
* Uses existing session cookie from login.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const BASE = 'http://127.0.0.1:3740';
|
|
10
|
+
const PASSWORD = process.env.REV4A_PASSWORD || '';
|
|
11
|
+
|
|
12
|
+
function postLogin() {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const data = JSON.stringify({ password: PASSWORD });
|
|
15
|
+
const req = http.request(`${BASE}/api/auth/login`, {
|
|
16
|
+
method: 'POST',
|
|
17
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) },
|
|
18
|
+
}, (res) => {
|
|
19
|
+
let body = '';
|
|
20
|
+
res.on('data', (c) => body += c);
|
|
21
|
+
res.on('end', () => {
|
|
22
|
+
const cookies = (res.headers['set-cookie'] || []).join('; ');
|
|
23
|
+
const token = cookies.match(/rev4a_token=([^;]+)/)?.[1];
|
|
24
|
+
resolve(token ? { token, cookie: cookies } : null);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
req.on('error', reject);
|
|
28
|
+
req.write(data);
|
|
29
|
+
req.end();
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function getAgentProviders(cookie) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
http.get(`${BASE}/api/agent-providers`, { headers: { Cookie: cookie } }, (res) => {
|
|
36
|
+
let body = '';
|
|
37
|
+
res.on('data', (c) => body += c);
|
|
38
|
+
res.on('end', () => {
|
|
39
|
+
try { resolve(JSON.parse(body)); } catch { resolve(null); }
|
|
40
|
+
});
|
|
41
|
+
}).on('error', reject);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
(async () => {
|
|
46
|
+
console.log('=== Provider check script ===\n');
|
|
47
|
+
|
|
48
|
+
// 1. Login and get session cookie
|
|
49
|
+
const login = await postLogin();
|
|
50
|
+
if (!login) {
|
|
51
|
+
console.log('FAIL: could not login');
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
console.log('OK: logged in');
|
|
55
|
+
|
|
56
|
+
// 2. Check API agent-providers returns Atlas with deepseek
|
|
57
|
+
const agents = await getAgentProviders(login.cookie);
|
|
58
|
+
if (!agents) {
|
|
59
|
+
console.log('FAIL: /api/agent-providers returned null');
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
console.log(`Found ${agents.length} agent(s):`);
|
|
63
|
+
let atlasDeepseekCount = 0;
|
|
64
|
+
for (const a of agents) {
|
|
65
|
+
const providerNames = (a.providers || []).map(p => p.provider).join(', ');
|
|
66
|
+
console.log(` ${a.agentId}: ${providerNames || '(none)'}`);
|
|
67
|
+
if (a.agentId === 'openclaw-atlas') {
|
|
68
|
+
for (const p of (a.providers || [])) {
|
|
69
|
+
if (p.provider === 'deepseek') atlasDeepseekCount++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (atlasDeepseekCount === 0) {
|
|
75
|
+
console.log('\nWARN: deepseek NOT found in Atlas via API. UI will show it inactive.\n');
|
|
76
|
+
} else {
|
|
77
|
+
console.log(`\nOK: deepseek found in Atlas (${atlasDeepseekCount} entry)\n`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 3. Playwright: headless browser visit providers page with cookie
|
|
81
|
+
const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] });
|
|
82
|
+
const context = await browser.newContext();
|
|
83
|
+
// Set the rev4a_token cookie manually for the domain
|
|
84
|
+
await context.addCookies([{
|
|
85
|
+
name: 'rev4a_token',
|
|
86
|
+
value: login.token,
|
|
87
|
+
domain: '127.0.0.1',
|
|
88
|
+
path: '/',
|
|
89
|
+
httpOnly: true,
|
|
90
|
+
sameSite: 'Lax',
|
|
91
|
+
}]);
|
|
92
|
+
const page = await context.newPage();
|
|
93
|
+
|
|
94
|
+
console.log('Navigating to providers page...');
|
|
95
|
+
await page.goto(`${BASE}/providers`, { waitUntil: 'networkidle' });
|
|
96
|
+
// Wait a moment for agent loading
|
|
97
|
+
await page.waitForTimeout(2000);
|
|
98
|
+
|
|
99
|
+
// 4. Check page has agent selector
|
|
100
|
+
const selectors = await page.locator('select').count();
|
|
101
|
+
console.log(`Select dropdowns found: ${selectors}`);
|
|
102
|
+
|
|
103
|
+
// Try to select Atlas from dropdown
|
|
104
|
+
const selects = page.locator('select');
|
|
105
|
+
const selectCount = await selects.count();
|
|
106
|
+
if (selectCount > 0) {
|
|
107
|
+
// First select is usually the agent selector
|
|
108
|
+
const firstSelect = selects.first();
|
|
109
|
+
const options = await firstSelect.locator('option').allTextContents();
|
|
110
|
+
console.log(`Agent options: ${options.join(', ')}`);
|
|
111
|
+
|
|
112
|
+
if (options.some(o => o.includes('atlas'))) {
|
|
113
|
+
console.log('OK: Atlas option found in selector');
|
|
114
|
+
await firstSelect.selectOption(options.find(o => o.includes('atlas')) || options[1]);
|
|
115
|
+
await page.waitForTimeout(2000);
|
|
116
|
+
|
|
117
|
+
// Check for deepseek in visible text
|
|
118
|
+
const bodyText = await page.locator('body').innerText();
|
|
119
|
+
const hasDeepseek = bodyText.toLowerCase().includes('deepseek');
|
|
120
|
+
console.log(`Deepseek visible on page after selecting Atlas: ${hasDeepseek ? 'YES' : 'NO'}`);
|
|
121
|
+
|
|
122
|
+
// Look for active indicators
|
|
123
|
+
const activeElements = page.locator('text=ACTIVE');
|
|
124
|
+
const activeCount = await activeElements.count();
|
|
125
|
+
console.log(`"ACTIVE" text occurrences: ${activeCount}`);
|
|
126
|
+
} else {
|
|
127
|
+
console.log('WARN: Atlas option NOT found in agent selector');
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
console.log('WARN: no selector found on page');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 5. Screenshot for visual inspection
|
|
134
|
+
await page.screenshot({ path: '/tmp/providers-atlas-check.png', fullPage: false });
|
|
135
|
+
console.log('Screenshot saved to /tmp/providers-atlas-check.png');
|
|
136
|
+
|
|
137
|
+
await browser.close();
|
|
138
|
+
console.log('\n=== Check complete ===');
|
|
139
|
+
|
|
140
|
+
// Exit with status: 0 if deepseek visible, 1 if not
|
|
141
|
+
const finalBody = await page.locator('body').innerText().catch(() => '');
|
|
142
|
+
process.exit(finalBody.toLowerCase().includes('deepseek') && atlasDeepseekCount > 0 ? 0 : 1);
|
|
143
|
+
})();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Rev4a Traefik — dedicated reverse proxy
|
|
2
|
+
#
|
|
3
|
+
# This Traefik instance is isolated from any other Traefik running on the host.
|
|
4
|
+
# It uses its own Docker network (rev4a-traefik) and config directory.
|
|
5
|
+
#
|
|
6
|
+
# Ports:
|
|
7
|
+
# 80 → HTTP (entrypoint: web)
|
|
8
|
+
# 443 → HTTPS (entrypoint: websecure)
|
|
9
|
+
# 8080 → Dashboard (internal only, not exposed)
|
|
10
|
+
|
|
11
|
+
services:
|
|
12
|
+
traefik:
|
|
13
|
+
image: traefik:latest
|
|
14
|
+
container_name: traefik-'rev4a'
|
|
15
|
+
restart: unless-stopped
|
|
16
|
+
ports:
|
|
17
|
+
- "80:80"
|
|
18
|
+
- "443:443"
|
|
19
|
+
networks:
|
|
20
|
+
- rev4a-traefik
|
|
21
|
+
volumes:
|
|
22
|
+
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
23
|
+
- ./traefik.yml:/etc/traefik/traefik.yml:ro
|
|
24
|
+
- ./dynamic:/etc/traefik/dynamic:ro
|
|
25
|
+
- ./certs:/etc/traefik/certs
|
|
26
|
+
labels:
|
|
27
|
+
- "traefik.enable=false"
|
|
28
|
+
|
|
29
|
+
networks:
|
|
30
|
+
rev4a-traefik:
|
|
31
|
+
name: rev4a-traefik
|
|
32
|
+
external: true # created by setup.sh before this Compose stack starts
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Add dynamic Traefik routes here.
|
|
2
|
+
# Files in this directory are watched and applied automatically.
|
|
3
|
+
#
|
|
4
|
+
# Example: route rev4a.yourdomain.com to the Rev4a dashboard:
|
|
5
|
+
#
|
|
6
|
+
# http:
|
|
7
|
+
# routers:
|
|
8
|
+
# rev4a-dashboard:
|
|
9
|
+
# entryPoints: [websecure]
|
|
10
|
+
# rule: "Host(`rev4a.yourdomain.com`)"
|
|
11
|
+
# service: rev4a
|
|
12
|
+
# tls:
|
|
13
|
+
# certResolver: letsencrypt
|
|
14
|
+
# services:
|
|
15
|
+
# rev4a:
|
|
16
|
+
# loadBalancer:
|
|
17
|
+
# servers:
|
|
18
|
+
# - url: "http://127.0.0.1:3740"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Traefik static configuration
|
|
2
|
+
# See: https://doc.traefik.io/traefik/reference/static-configuration/file/
|
|
3
|
+
|
|
4
|
+
global:
|
|
5
|
+
checkNewVersion: false
|
|
6
|
+
sendAnonymousUsage: false
|
|
7
|
+
|
|
8
|
+
log:
|
|
9
|
+
level: WARN
|
|
10
|
+
|
|
11
|
+
api:
|
|
12
|
+
dashboard: true
|
|
13
|
+
insecure: false
|
|
14
|
+
|
|
15
|
+
entryPoints:
|
|
16
|
+
web:
|
|
17
|
+
address: ":80"
|
|
18
|
+
websecure:
|
|
19
|
+
address: ":443"
|
|
20
|
+
|
|
21
|
+
providers:
|
|
22
|
+
docker:
|
|
23
|
+
endpoint: "unix:///var/run/docker.sock"
|
|
24
|
+
exposedByDefault: false
|
|
25
|
+
network: rev4a-traefik
|
|
26
|
+
|
|
27
|
+
file:
|
|
28
|
+
directory: /etc/traefik/dynamic
|
|
29
|
+
watch: true
|
|
30
|
+
|
|
31
|
+
certificatesResolvers:
|
|
32
|
+
letsencrypt:
|
|
33
|
+
acme:
|
|
34
|
+
email: "admin@example.com" # CHANGE THIS to your real email before enabling HTTPS
|
|
35
|
+
storage: /etc/traefik/certs/acme.json
|
|
36
|
+
httpChallenge:
|
|
37
|
+
entryPoint: web
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove an auth profile from the OpenClaw SQLite database.
|
|
3
|
+
* Usage: node remove-auth-profile.js <db-path> <provider-name>
|
|
4
|
+
*
|
|
5
|
+
* Reads the store_json from auth_profile_store, removes any profiles
|
|
6
|
+
* matching the provider, and writes back.
|
|
7
|
+
*/
|
|
8
|
+
const { execSync: run } = require('child_process');
|
|
9
|
+
|
|
10
|
+
const db = process.argv[2];
|
|
11
|
+
const provider = process.argv[3];
|
|
12
|
+
|
|
13
|
+
if (!db || !provider) {
|
|
14
|
+
process.stderr.write('Usage: node remove-auth-profile.js <db-path> <provider>\n');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const row = run(
|
|
20
|
+
'sqlite3 ' + JSON.stringify(db) + ' "SELECT store_json FROM auth_profile_store WHERE store_key=\'primary\'"',
|
|
21
|
+
{ encoding: 'utf8', timeout: 5000 }
|
|
22
|
+
).trim();
|
|
23
|
+
|
|
24
|
+
if (!row) process.exit(0);
|
|
25
|
+
|
|
26
|
+
const data = JSON.parse(row);
|
|
27
|
+
if (!data.profiles) process.exit(0);
|
|
28
|
+
|
|
29
|
+
let changed = false;
|
|
30
|
+
for (const pid of Object.keys(data.profiles)) {
|
|
31
|
+
const p = data.profiles[pid];
|
|
32
|
+
if (p.provider === provider || pid.startsWith(provider + ':')) {
|
|
33
|
+
delete data.profiles[pid];
|
|
34
|
+
changed = true;
|
|
35
|
+
process.stderr.write('Removed profile: ' + pid + '\n');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!changed) process.exit(0);
|
|
40
|
+
|
|
41
|
+
const newJson = JSON.stringify(data);
|
|
42
|
+
// Escape single quotes for SQLite shell
|
|
43
|
+
const escaped = newJson.replace(/'/g, "''");
|
|
44
|
+
const updateCmd =
|
|
45
|
+
'sqlite3 ' + JSON.stringify(db) +
|
|
46
|
+
' "UPDATE auth_profile_store SET store_json=\'' + escaped +
|
|
47
|
+
'\', updated_at=' + Date.now() +
|
|
48
|
+
' WHERE store_key=\'primary\'"';
|
|
49
|
+
|
|
50
|
+
run(updateCmd, { timeout: 5000 });
|
|
51
|
+
process.stderr.write('Updated auth_profile_store\n');
|
|
52
|
+
console.log('ok');
|
|
53
|
+
} catch (e) {
|
|
54
|
+
process.stderr.write('Error: ' + e.message + '\n');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# restore.sh — Restore Rev4a data from a backup archive
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# sudo bash scripts/restore.sh backups/rev4a-backup-20260724-120000.tar.gz
|
|
6
|
+
#
|
|
7
|
+
# The script restores:
|
|
8
|
+
# - data/*.db
|
|
9
|
+
# - data/provider-keys.json
|
|
10
|
+
# - data/agents-token.json
|
|
11
|
+
# - models.config.json
|
|
12
|
+
# - .env
|
|
13
|
+
#
|
|
14
|
+
# Existing files are backed up to a .pre-restore directory before overwriting.
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
18
|
+
REV4A_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
19
|
+
|
|
20
|
+
ARCHIVE="${1:-}"
|
|
21
|
+
|
|
22
|
+
if [[ -z "$ARCHIVE" ]]; then
|
|
23
|
+
echo "Usage: sudo bash scripts/restore.sh <backup-archive.tar.gz>"
|
|
24
|
+
echo ""
|
|
25
|
+
echo "Available backups:"
|
|
26
|
+
if [[ -d "${REV4A_ROOT}/backups" ]]; then
|
|
27
|
+
ls -lh "${REV4A_ROOT}/backups/"*.tar.gz 2>/dev/null || echo " (none)"
|
|
28
|
+
else
|
|
29
|
+
echo " (no backups directory)"
|
|
30
|
+
fi
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
if [[ ! -f "$ARCHIVE" ]]; then
|
|
35
|
+
echo "Error: Archive not found: $ARCHIVE"
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
echo "Restoring Rev4a from: $ARCHIVE"
|
|
40
|
+
echo ""
|
|
41
|
+
|
|
42
|
+
# Create pre-restore backup of current state
|
|
43
|
+
PRE_DIR="${REV4A_ROOT}/backups/.pre-restore-$(date -u +%Y%m%d-%H%M%S)"
|
|
44
|
+
mkdir -p "$PRE_DIR"
|
|
45
|
+
|
|
46
|
+
echo "Saving current state to: $PRE_DIR"
|
|
47
|
+
cd "$REV4A_ROOT"
|
|
48
|
+
for f in data/*.db data/*.db-shm data/*.db-wal data/provider-keys.json data/agents-token.json models.config.json .env; do
|
|
49
|
+
if [[ -f "$f" ]]; then
|
|
50
|
+
cp --parents "$f" "$PRE_DIR/" 2>/dev/null || true
|
|
51
|
+
fi
|
|
52
|
+
done
|
|
53
|
+
|
|
54
|
+
# Stop Rev4a services before restore
|
|
55
|
+
if systemctl is-active --quiet rev4a.service 2>/dev/null; then
|
|
56
|
+
echo "Stopping Rev4a services..."
|
|
57
|
+
sudo systemctl stop rev4a.service
|
|
58
|
+
echo "Services stopped."
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Extract the archive
|
|
62
|
+
echo "Extracting backup..."
|
|
63
|
+
tar -xzf "$ARCHIVE" -C "$REV4A_ROOT"
|
|
64
|
+
|
|
65
|
+
# Fix permissions
|
|
66
|
+
chmod 600 "${REV4A_ROOT}/.env" 2>/dev/null || true
|
|
67
|
+
chmod 600 "${REV4A_ROOT}/data/provider-keys.json" 2>/dev/null || true
|
|
68
|
+
chmod 600 "${REV4A_ROOT}/data/agents-token.json" 2>/dev/null || true
|
|
69
|
+
|
|
70
|
+
echo ""
|
|
71
|
+
echo "=============================================="
|
|
72
|
+
echo " Restore complete!"
|
|
73
|
+
echo "=============================================="
|
|
74
|
+
echo ""
|
|
75
|
+
echo " Previous state saved to: ${PRE_DIR}"
|
|
76
|
+
echo ""
|
|
77
|
+
echo " To restart Rev4a:"
|
|
78
|
+
echo " sudo systemctl start rev4a.service"
|
|
79
|
+
echo ""
|
|
80
|
+
echo " If something went wrong, restore previous state from:"
|
|
81
|
+
echo " ${PRE_DIR}"
|
|
82
|
+
echo "=============================================="
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// Terminal WebSocket Server — standalone
|
|
2
|
+
// Uses node-pty for real PTY with docker exec - fully interactive
|
|
3
|
+
const { WebSocketServer } = require('ws');
|
|
4
|
+
const http = require('http');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const pty = require(path.resolve(__dirname, 'node_modules/node-pty'));
|
|
8
|
+
|
|
9
|
+
const PORT = parseInt(process.env.TERMINAL_WS_PORT || '3741', 10);
|
|
10
|
+
const SESSION_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
|
+
const IDLE_RESET_TIMEOUT_MS = 60 * 1000;
|
|
12
|
+
|
|
13
|
+
const server = http.createServer((_req, res) => {
|
|
14
|
+
res.writeHead(426, { 'Content-Type': 'text/plain' });
|
|
15
|
+
res.end('Use WebSocket');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const wss = new WebSocketServer({ server });
|
|
19
|
+
|
|
20
|
+
wss.on('connection', (ws, req) => {
|
|
21
|
+
const url = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
|
|
22
|
+
const containerId = url.searchParams.get('id');
|
|
23
|
+
|
|
24
|
+
if (!containerId) {
|
|
25
|
+
ws.send(`\r\n[Error: Missing container id]\r\n`);
|
|
26
|
+
ws.close();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let term = null;
|
|
31
|
+
let idleTimer = setTimeout(() => {
|
|
32
|
+
ws.send('\r\n[Session timeout: 30 minutes]\r\n');
|
|
33
|
+
try { ws.close(); } catch {}
|
|
34
|
+
}, SESSION_TIMEOUT_MS);
|
|
35
|
+
|
|
36
|
+
function resetIdleTimer() {
|
|
37
|
+
clearTimeout(idleTimer);
|
|
38
|
+
idleTimer = setTimeout(() => {
|
|
39
|
+
ws.send('\r\n[Session timeout: 30 minutes]\r\n');
|
|
40
|
+
try { ws.close(); } catch {}
|
|
41
|
+
}, SESSION_TIMEOUT_MS);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
term = pty.spawn('docker', ['exec', '-it', containerId, 'env', 'TERM=xterm-256color', 'bash'], {
|
|
46
|
+
name: 'xterm-256color',
|
|
47
|
+
cols: 80,
|
|
48
|
+
rows: 30,
|
|
49
|
+
});
|
|
50
|
+
} catch (err) {
|
|
51
|
+
ws.send(`\r\n[Error: ${err.message}]\r\n`);
|
|
52
|
+
ws.close();
|
|
53
|
+
clearTimeout(idleTimer);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// PTY output → WebSocket.
|
|
58
|
+
// Use a queue + micro-delay to avoid flooding xterm.js with one giant chunk.
|
|
59
|
+
let outputBuf = '';
|
|
60
|
+
let outputTimer = null;
|
|
61
|
+
|
|
62
|
+
function flushOutput() {
|
|
63
|
+
outputTimer = null;
|
|
64
|
+
if (!outputBuf) return;
|
|
65
|
+
if (ws.readyState !== ws.OPEN) { outputBuf = ''; return; }
|
|
66
|
+
ws.send(outputBuf);
|
|
67
|
+
outputBuf = '';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
term.onData((data) => {
|
|
71
|
+
if (!data) return;
|
|
72
|
+
outputBuf += data;
|
|
73
|
+
// Flush every 10ms max (roughly 30-40 chunks/sec — safe for DOM renderer)
|
|
74
|
+
if (!outputTimer) {
|
|
75
|
+
outputTimer = setTimeout(flushOutput, 10);
|
|
76
|
+
}
|
|
77
|
+
// If buffer grows huge, flush immediately in chunks
|
|
78
|
+
if (outputBuf.length > 4000) {
|
|
79
|
+
// Split into 2k chunks
|
|
80
|
+
while (outputBuf.length > 2000) {
|
|
81
|
+
const chunk = outputBuf.slice(0, 2000);
|
|
82
|
+
outputBuf = outputBuf.slice(2000);
|
|
83
|
+
if (ws.readyState === ws.OPEN) ws.send(chunk);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// WebSocket input → PTY
|
|
89
|
+
ws.on('message', (data) => {
|
|
90
|
+
resetIdleTimer();
|
|
91
|
+
if (!term) return;
|
|
92
|
+
|
|
93
|
+
const msg = data.toString();
|
|
94
|
+
|
|
95
|
+
// Handle resize message from client
|
|
96
|
+
if (msg.startsWith('{"type":"resize"}') || msg.startsWith('{"type":"resize"') && msg.includes('"cols"')) {
|
|
97
|
+
try {
|
|
98
|
+
const parsed = JSON.parse(msg);
|
|
99
|
+
if (parsed.type === 'resize' && parsed.cols && parsed.rows) {
|
|
100
|
+
term.resize(parsed.cols, parsed.rows);
|
|
101
|
+
}
|
|
102
|
+
} catch {}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
term.write(msg);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
term.onExit(({ exitCode, signal }) => {
|
|
110
|
+
clearTimeout(idleTimer);
|
|
111
|
+
if (ws.readyState === ws.OPEN) {
|
|
112
|
+
ws.send(`\r\n[Process exited with code ${exitCode}]\r\n`);
|
|
113
|
+
ws.close();
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
ws.on('close', () => {
|
|
118
|
+
clearTimeout(idleTimer);
|
|
119
|
+
if (term) term.kill('SIGTERM');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
ws.on('error', () => {
|
|
123
|
+
clearTimeout(idleTimer);
|
|
124
|
+
if (term) term.kill('SIGTERM');
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
term.write('\n');
|
|
128
|
+
ws.send(`\r\n[Connected to container: ${containerId}]\r\n`);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
server.listen(PORT, '127.0.0.1', () => {
|
|
132
|
+
console.log(`[TerminalWS] Server listening on ws://127.0.0.1:${PORT}`);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
process.on('SIGTERM', () => {
|
|
136
|
+
console.log('[TerminalWS] Shutting down...');
|
|
137
|
+
wss.close();
|
|
138
|
+
server.close(() => process.exit(0));
|
|
139
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"lib": [
|
|
5
|
+
"dom",
|
|
6
|
+
"dom.iterable",
|
|
7
|
+
"esnext"
|
|
8
|
+
],
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"strict": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"esModuleInterop": true,
|
|
14
|
+
"module": "esnext",
|
|
15
|
+
"moduleResolution": "bundler",
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"isolatedModules": true,
|
|
18
|
+
"jsx": "react-jsx",
|
|
19
|
+
"incremental": true,
|
|
20
|
+
"plugins": [
|
|
21
|
+
{
|
|
22
|
+
"name": "next"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"paths": {
|
|
26
|
+
"@/*": [
|
|
27
|
+
"./*"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"include": [
|
|
32
|
+
"next-env.d.ts",
|
|
33
|
+
"**/*.ts",
|
|
34
|
+
"**/*.tsx",
|
|
35
|
+
".next/types/**/*.ts",
|
|
36
|
+
".next/dev/types/**/*.ts",
|
|
37
|
+
".next/dev/dev/types/**/*.ts"
|
|
38
|
+
],
|
|
39
|
+
"exclude": [
|
|
40
|
+
"node_modules"
|
|
41
|
+
]
|
|
42
|
+
}
|