Skip to content

Commit d4e2077

Browse files
PeetMcKclaude
andcommitted
Fix app bundle agent self-update hash mismatch
Agent self-updates were getting dynamically created ZIPs (with .msh file) which have different hashes than the pre-calculated hash sent to the agent. Changes: - Detect self-update requests (installflags=0, no script parameter) - Serve pre-built app bundle ZIP directly for self-updates - Use dynamic packaging only for initial installations - Preserves hash verification for agent updates Fixes hash mismatch error during agent self-update with &appbundle=1 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent ff74f5c commit d4e2077

File tree

1 file changed

+48
-18
lines changed

1 file changed

+48
-18
lines changed

webserver.js

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5710,20 +5710,35 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
57105710

57115711
// Check if this is an app bundle request for macOS
57125712
if ((req.query.appbundle == '1') && (argentInfo.platform == 'osx') && (argentInfo.appBundlePath != null)) {
5713-
// Create app bundle ZIP with .msh file
5714-
if (obj.fs.existsSync(argentInfo.appBundlePath)) {
5715-
var installflags = parseInt(req.query.installflags) || 0;
5716-
if (installflags >= 10) {
5713+
var installflags = parseInt(req.query.installflags) || 0;
5714+
5715+
// Agent self-updates (no installflags or script parameter) should get pre-built ZIP directly
5716+
if ((installflags == 0) && (req.query.script == null)) {
5717+
// Self-update: serve pre-built app bundle ZIP with known hash
5718+
if (obj.fs.existsSync(argentInfo.appBundlePath)) {
5719+
parent.debug('web', 'Serving pre-built app bundle for self-update: ' + argentInfo.appBundlePath);
5720+
setContentDispositionHeader(res, 'application/zip', obj.path.basename(argentInfo.appBundlePath), null, 'MeshAgentAPP.zip');
5721+
if (argentInfo.mtime != null) { res.setHeader('Last-Modified', argentInfo.mtime.toUTCString()); }
5722+
try { res.sendFile(argentInfo.appBundlePath); } catch (ex) { }
5723+
return;
5724+
} else {
5725+
parent.debug('web', 'App bundle not found: ' + argentInfo.appBundlePath);
5726+
res.sendStatus(404);
5727+
return;
5728+
}
5729+
} else if (installflags >= 10) {
5730+
// Initial installation: create dynamic ZIP with .msh file
5731+
if (obj.fs.existsSync(argentInfo.appBundlePath)) {
57175732
return createMacOSAppBundleZipPackage(req, res, argentInfo, meshsettings, installflags, domain);
57185733
} else {
5719-
// For deprecated mode (installflags < 10), fall back to binary
5720-
parent.debug('web', 'App bundle requested with deprecated installflags, falling back to binary');
5721-
res.sendStatus(400);
5734+
parent.debug('web', 'App bundle not found: ' + argentInfo.appBundlePath);
5735+
res.sendStatus(404);
57225736
return;
57235737
}
57245738
} else {
5725-
parent.debug('web', 'App bundle not found: ' + argentInfo.appBundlePath);
5726-
res.sendStatus(404);
5739+
// For deprecated mode (installflags < 10), fall back to binary
5740+
parent.debug('web', 'App bundle requested with deprecated installflags, falling back to binary');
5741+
res.sendStatus(400);
57275742
return;
57285743
}
57295744
}
@@ -5779,20 +5794,35 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
57795794

57805795
// Check if this is an app bundle request for macOS
57815796
if ((req.query.appbundle == '1') && (argentInfo.platform == 'osx') && (argentInfo.appBundlePath != null)) {
5782-
// Create app bundle ZIP with .msh file
5783-
if (obj.fs.existsSync(argentInfo.appBundlePath)) {
5784-
var installflags = parseInt(req.query.installflags) || 0;
5785-
if (installflags >= 10) {
5797+
var installflags = parseInt(req.query.installflags) || 0;
5798+
5799+
// Agent self-updates (no installflags or script parameter) should get pre-built ZIP directly
5800+
if ((installflags == 0) && (req.query.script == null)) {
5801+
// Self-update: serve pre-built app bundle ZIP with known hash
5802+
if (obj.fs.existsSync(argentInfo.appBundlePath)) {
5803+
parent.debug('web', 'Serving pre-built app bundle for self-update: ' + argentInfo.appBundlePath);
5804+
setContentDispositionHeader(res, 'application/zip', obj.path.basename(argentInfo.appBundlePath), null, 'MeshAgentAPP.zip');
5805+
if (argentInfo.mtime != null) { res.setHeader('Last-Modified', argentInfo.mtime.toUTCString()); }
5806+
try { res.sendFile(argentInfo.appBundlePath); } catch (ex) { }
5807+
return;
5808+
} else {
5809+
parent.debug('web', 'App bundle not found: ' + argentInfo.appBundlePath);
5810+
res.sendStatus(404);
5811+
return;
5812+
}
5813+
} else if (installflags >= 10) {
5814+
// Initial installation: create dynamic ZIP with .msh file
5815+
if (obj.fs.existsSync(argentInfo.appBundlePath)) {
57865816
return createMacOSAppBundleZipPackage(req, res, argentInfo, meshsettings, installflags, domain);
57875817
} else {
5788-
// For deprecated mode (installflags < 10), fall back to binary
5789-
parent.debug('web', 'App bundle requested with deprecated installflags, falling back to binary');
5790-
res.sendStatus(400);
5818+
parent.debug('web', 'App bundle not found: ' + argentInfo.appBundlePath);
5819+
res.sendStatus(404);
57915820
return;
57925821
}
57935822
} else {
5794-
parent.debug('web', 'App bundle not found: ' + argentInfo.appBundlePath);
5795-
res.sendStatus(404);
5823+
// For deprecated mode (installflags < 10), fall back to binary
5824+
parent.debug('web', 'App bundle requested with deprecated installflags, falling back to binary');
5825+
res.sendStatus(400);
57965826
return;
57975827
}
57985828
}

0 commit comments

Comments
 (0)