Merge krystie-improvements into main

Resolves 24 conflicts between Hermes (DC-008/009/010 + response-helper
envelope standardization) and Krystie (DC-005 src/ refactor path fixes,
DC-006 TOTP integration, DC-007 new test suites, cloud backup
destinations).

Conflict resolutions:
- src/utils/logging.js:    took ours (consumers depend on logError/
                            safeErrorMessage/createLogger exports)
- src/config/site.js:      merged (her factored validateAndLogConfig +
                            applyConfigFields helpers)
- src/context/dns.js:      took hers (admin/readonly role iteration for
                            write operations)
- src/utilities/backup-
  manager.js:              took hers (Dropbox/WebDAV/SFTP cloud feature)
- status/dist/*, status/
  sw.js:                   took hers (minified bundles + newer SW cache)

Additional fix (post-merge regression):
- src/monitoring/health-checker.js: fixed DC-005 path miss —
  'require(./platform-paths)' → 'require(../../platform-paths)'

Test status: 921/922 passing. One known failure in logging.test.js
(async file-handle timing) tracked as follow-up.
This commit is contained in:
Hermes
2026-06-25 16:43:10 -07:00
171 changed files with 11759 additions and 1006 deletions
+12 -11
View File
@@ -1,5 +1,6 @@
const express = require('express');
const http = require('http');
const { ok, errorResponse, notFound, conflict } = require('../src/utils/responses');
/**
* OpenClaw management routes
@@ -94,8 +95,8 @@ module.exports = function openClawRoutes(ctx) {
proxyRes.on('data', function(d) { res.write(d); });
proxyRes.on('end', function() { res.end(); });
});
proxyReq.on('error', function(e) { res.status(502).json({ success: false, error: e.message }); });
proxyReq.setTimeout(15000, function() { proxyReq.destroy(); res.status(504).json({ success: false, error: 'gateway timeout' }); });
proxyReq.on('error', function(e) { errorResponse(res, 502, e.message); });
proxyReq.setTimeout(15000, function() { proxyReq.destroy(); errorResponse(res, 504, 'gateway timeout'); });
proxyReq.write(body);
proxyReq.end();
} else {
@@ -105,8 +106,8 @@ module.exports = function openClawRoutes(ctx) {
proxyRes.on('data', function(d) { res.write(d); });
proxyRes.on('end', function() { res.end(); });
});
proxyReq.on('error', function(e) { res.status(502).json({ success: false, error: e.message }); });
proxyReq.setTimeout(15000, function() { proxyReq.destroy(); res.status(504).json({ success: false, error: 'gateway timeout' }); });
proxyReq.on('error', function(e) { errorResponse(res, 502, e.message); });
proxyReq.setTimeout(15000, function() { proxyReq.destroy(); errorResponse(res, 504, 'gateway timeout'); });
}
}
@@ -149,7 +150,7 @@ module.exports = function openClawRoutes(ctx) {
router.post('/deploy', asyncHandler(async function(req, res) {
const existing = await findOpenClawContainer();
if (existing) {
return res.status(409).json({ success: false, error: 'OpenClaw is already deployed' });
return conflict(res, 'OpenClaw is already deployed');
}
const image = 'ghcr.io/nousresearch/openclaw:latest';
@@ -170,7 +171,7 @@ module.exports = function openClawRoutes(ctx) {
});
} catch(e) {
log.error('OpenClaw pull failed: ' + e.message);
return res.status(500).json({ success: false, error: 'Failed to pull image: ' + e.message });
return errorResponse(res, 500, 'Failed to pull image: ' + e.message);
}
// Create + start container
@@ -206,7 +207,7 @@ module.exports = function openClawRoutes(ctx) {
});
} catch(e) {
log.error('OpenClaw deploy failed: ' + e.message);
res.status(500).json({ success: false, error: 'Deploy failed: ' + e.message });
errorResponse(res, 500, 'Deploy failed: ' + e.message);
}
}));
@@ -214,7 +215,7 @@ module.exports = function openClawRoutes(ctx) {
router.get('/proxy/*', asyncHandler(async function(req, res) {
const container = await findOpenClawContainer();
if (!container) return res.status(404).json({ success: false, error: 'OpenClaw not deployed' });
if (!container) return notFound(res, 'OpenClaw not deployed');
const token = await getGatewayToken(container.Id);
const port = await getContainerPort(container.Id);
@@ -228,7 +229,7 @@ module.exports = function openClawRoutes(ctx) {
router.post('/proxy/*', asyncHandler(async function(req, res) {
const container = await findOpenClawContainer();
if (!container) return res.status(404).json({ success: false, error: 'OpenClaw not deployed' });
if (!container) return notFound(res, 'OpenClaw not deployed');
const token = await getGatewayToken(container.Id);
const port = await getContainerPort(container.Id);
@@ -242,7 +243,7 @@ module.exports = function openClawRoutes(ctx) {
router.delete('/', asyncHandler(async function(req, res) {
const container = await findOpenClawContainer();
if (!container) return res.status(404).json({ success: false, error: 'OpenClaw not deployed' });
if (!container) return notFound(res, 'OpenClaw not deployed');
try {
const c = docker.client.container(container.Id);
@@ -252,7 +253,7 @@ module.exports = function openClawRoutes(ctx) {
ok(res, { message: 'OpenClaw removed' });
} catch(e) {
log.error('Failed to remove OpenClaw: ' + e.message);
res.status(500).json({ success: false, error: e.message });
errorResponse(res, 500, e.message);
}
}));