Skip to content

Commit 413f1e0

Browse files
authored
Merge pull request #966 from mnfst/fix/version-indicator
fix: show plugin version in local mode, hide in cloud mode
2 parents 020dc06 + d04f4c7 commit 413f1e0

File tree

6 files changed

+37
-37
lines changed

6 files changed

+37
-37
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"manifest": patch
3+
---
4+
5+
Fix version indicator: show plugin version in local mode, hide in cloud mode, fix upgrade command

packages/backend/src/health/health.controller.spec.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
jest.mock('fs', () => ({
2-
readFileSync: jest.fn().mockReturnValue(JSON.stringify({ version: '1.2.3' })),
3-
}));
4-
51
import { HealthController } from './health.controller';
62
import { VersionCheckService } from './version-check.service';
73

84
function createMockVersionCheck(overrides: Partial<VersionCheckService> = {}): VersionCheckService {
95
return {
106
onModuleInit: jest.fn(),
11-
getCurrentVersion: jest.fn().mockReturnValue('1.2.3'),
7+
getCurrentVersion: jest.fn().mockReturnValue('5.20.0'),
128
getUpdateInfo: jest.fn().mockReturnValue({}),
139
isNewer: jest.fn().mockReturnValue(false),
1410
fetchLatestVersion: jest.fn().mockResolvedValue(null),
@@ -21,6 +17,7 @@ describe('HealthController', () => {
2117
let mockVersionCheck: VersionCheckService;
2218

2319
beforeEach(() => {
20+
delete process.env['MANIFEST_MODE'];
2421
mockVersionCheck = createMockVersionCheck();
2522
controller = new HealthController(mockVersionCheck);
2623
});
@@ -30,9 +27,16 @@ describe('HealthController', () => {
3027
expect(result.status).toBe('healthy');
3128
});
3229

33-
it('returns version from package.json', () => {
30+
it('returns plugin version in local mode', () => {
31+
process.env['MANIFEST_MODE'] = 'local';
32+
const result = controller.getHealth();
33+
expect(result.version).toBe('5.20.0');
34+
});
35+
36+
it('omits version in cloud mode', () => {
37+
delete process.env['MANIFEST_MODE'];
3438
const result = controller.getHealth();
35-
expect(result.version).toBe('1.2.3');
39+
expect(result).not.toHaveProperty('version');
3640
});
3741

3842
it('returns uptime in seconds', () => {
@@ -48,11 +52,9 @@ describe('HealthController', () => {
4852
});
4953

5054
it('returns local mode when MANIFEST_MODE=local', () => {
51-
const orig = process.env['MANIFEST_MODE'];
5255
process.env['MANIFEST_MODE'] = 'local';
5356
const result = controller.getHealth();
5457
expect(result.mode).toBe('local');
55-
process.env['MANIFEST_MODE'] = orig;
5658
});
5759

5860
it('includes update info when available', () => {

packages/backend/src/health/health.controller.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import { Controller, Get } from '@nestjs/common';
22
import { Public } from '../common/decorators/public.decorator';
3-
import { readFileSync } from 'fs';
4-
import { join } from 'path';
53
import { VersionCheckService } from './version-check.service';
64

7-
const pkg = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf-8')) as { version: string };
8-
95
@Controller('api/v1')
106
export class HealthController {
117
private readonly startTime = Date.now();
@@ -15,12 +11,13 @@ export class HealthController {
1511
@Public()
1612
@Get('health')
1713
getHealth() {
14+
const isLocal = process.env['MANIFEST_MODE'] === 'local';
1815
const optOut = process.env['MANIFEST_TELEMETRY_OPTOUT'];
1916
return {
2017
status: 'healthy',
2118
uptime_seconds: Math.floor((Date.now() - this.startTime) / 1000),
22-
version: pkg.version,
23-
mode: process.env['MANIFEST_MODE'] === 'local' ? 'local' : 'cloud',
19+
...(isLocal ? { version: this.versionCheck.getCurrentVersion() } : {}),
20+
mode: isLocal ? 'local' : 'cloud',
2421
telemetryOptOut: optOut === '1' || optOut === 'true',
2522
...this.versionCheck.getUpdateInfo(),
2623
};

packages/backend/test/health.e2e-spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe('GET /api/v1/health', () => {
2020

2121
expect(res.body).toHaveProperty('status', 'healthy');
2222
expect(res.body).toHaveProperty('uptime_seconds');
23-
expect(res.body).toHaveProperty('version', '0.1.0');
23+
expect(res.body).toHaveProperty('version', '0.0.0');
2424
expect(typeof res.body.uptime_seconds).toBe('number');
2525
});
2626
});

packages/frontend/src/components/VersionIndicator.tsx

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,48 @@
1-
import { createSignal, Show } from "solid-js";
2-
import { updateInfo } from "../services/local-mode.js";
1+
import { createSignal, Show } from 'solid-js';
2+
import { updateInfo } from '../services/local-mode.js';
33

4-
const UPGRADE_COMMAND = "openclaw plugins upgrade manifest";
4+
const UPGRADE_COMMAND = 'openclaw plugins update';
55

66
const VersionIndicator = () => {
77
const info = () => updateInfo();
88
const hasUpdate = () => info()?.updateAvailable === true;
99
const [copied, setCopied] = createSignal(false);
1010

1111
function copyCommand() {
12-
navigator.clipboard.writeText(UPGRADE_COMMAND).then(() => {
13-
setCopied(true);
14-
setTimeout(() => setCopied(false), 2000);
15-
}).catch(() => {});
12+
navigator.clipboard
13+
.writeText(UPGRADE_COMMAND)
14+
.then(() => {
15+
setCopied(true);
16+
setTimeout(() => setCopied(false), 2000);
17+
})
18+
.catch(() => {});
1619
}
1720

1821
return (
1922
<Show when={info()}>
2023
<div
2124
class="version-indicator"
22-
classList={{ "version-indicator--update": hasUpdate() }}
25+
classList={{ 'version-indicator--update': hasUpdate() }}
2326
aria-label={
24-
hasUpdate()
25-
? `Update available: v${info()!.latestVersion}`
26-
: `Version ${info()!.version}`
27+
hasUpdate() ? `Update available: v${info()!.latestVersion}` : `Version ${info()!.version}`
2728
}
2829
>
29-
<Show
30-
when={hasUpdate()}
31-
fallback={<span>v{info()!.version}</span>}
32-
>
30+
<Show when={hasUpdate()} fallback={<span>v{info()!.version}</span>}>
3331
<div class="version-indicator__tooltip-wrap">
3432
<span>New version available</span>
3533
<div class="version-indicator__bubble" role="tooltip">
3634
<span class="version-indicator__range">
3735
v{info()!.version} &rarr; v{info()!.latestVersion}
3836
</span>
3937
<div class="version-indicator__command-row">
40-
<code class="version-indicator__command">
41-
{UPGRADE_COMMAND}
42-
</code>
38+
<code class="version-indicator__command">{UPGRADE_COMMAND}</code>
4339
<button
4440
class="version-indicator__copy-btn"
4541
onClick={copyCommand}
46-
title={copied() ? "Copied!" : "Copy command"}
42+
title={copied() ? 'Copied!' : 'Copy command'}
4743
aria-label="Copy upgrade command"
4844
>
49-
{copied() ? "\u2713" : "\u2398"}
45+
{copied() ? '\u2713' : '\u2398'}
5046
</button>
5147
</div>
5248
</div>

packages/frontend/tests/components/VersionIndicator.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ describe("VersionIndicator", () => {
7272
const { container } = render(() => <VersionIndicator />);
7373
const cmd = container.querySelector(".version-indicator__command");
7474
expect(cmd).not.toBeNull();
75-
expect(cmd!.textContent).toContain("openclaw plugins upgrade manifest");
75+
expect(cmd!.textContent).toContain("openclaw plugins update");
7676
});
7777

7878
it("renders copy button in tooltip", () => {

0 commit comments

Comments
 (0)