chainlesschain 0.45.1 → 0.45.3
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/package.json +1 -1
- package/src/commands/update.js +31 -0
- package/src/lib/version-checker.js +55 -27
- package/src/lib/web-ui-server.js +26 -17
package/package.json
CHANGED
package/src/commands/update.js
CHANGED
|
@@ -94,6 +94,37 @@ export function registerUpdateCommand(program) {
|
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
// When update source is npm-only (GitHub Release CI still building),
|
|
98
|
+
// skip desktop app download and only update the CLI package
|
|
99
|
+
if (result.source === "npm") {
|
|
100
|
+
logger.info(
|
|
101
|
+
`GitHub Release for v${result.latestVersion} is not yet available (CI may still be building).`,
|
|
102
|
+
);
|
|
103
|
+
logger.info(`Updating CLI package from npm...`);
|
|
104
|
+
|
|
105
|
+
const doUpdate = await askConfirm(
|
|
106
|
+
`Update CLI to v${result.latestVersion} via npm?`,
|
|
107
|
+
true,
|
|
108
|
+
);
|
|
109
|
+
if (!doUpdate) {
|
|
110
|
+
logger.info("Update cancelled");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const cliUpdated = await selfUpdateCli(result.latestVersion);
|
|
115
|
+
if (cliUpdated) {
|
|
116
|
+
logger.success(`CLI updated to v${result.latestVersion}`);
|
|
117
|
+
logger.info(
|
|
118
|
+
`Desktop app update will be available once GitHub Release CI completes.`,
|
|
119
|
+
);
|
|
120
|
+
} else {
|
|
121
|
+
logger.warn(
|
|
122
|
+
`CLI self-update failed. Please run manually:\n npm install -g chainlesschain@${result.latestVersion}`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
97
128
|
const doUpdate = await askConfirm(
|
|
98
129
|
`Download v${result.latestVersion}?`,
|
|
99
130
|
true,
|
|
@@ -2,48 +2,76 @@ import semver from "semver";
|
|
|
2
2
|
import { GITHUB_RELEASES_URL, VERSION } from "../constants.js";
|
|
3
3
|
import logger from "./logger.js";
|
|
4
4
|
|
|
5
|
+
const NPM_REGISTRY_URL = "https://registry.npmjs.org/chainlesschain/latest";
|
|
6
|
+
|
|
5
7
|
export async function checkForUpdates(options = {}) {
|
|
6
8
|
const channel = options.channel || "stable";
|
|
7
9
|
const currentVersion = options.currentVersion || VERSION;
|
|
8
10
|
|
|
11
|
+
// Try GitHub releases first (has full release notes and assets)
|
|
9
12
|
try {
|
|
10
13
|
const releases = await fetchReleases();
|
|
11
14
|
const filtered = filterByChannel(releases, channel);
|
|
12
15
|
|
|
13
|
-
if (filtered.length
|
|
16
|
+
if (filtered.length > 0) {
|
|
17
|
+
const latest = filtered[0];
|
|
18
|
+
const latestVersion = latest.tag_name.replace(/^v/, "");
|
|
19
|
+
const updateAvailable = semver.gt(latestVersion, currentVersion);
|
|
20
|
+
|
|
14
21
|
return {
|
|
15
|
-
updateAvailable
|
|
22
|
+
updateAvailable,
|
|
16
23
|
currentVersion,
|
|
17
|
-
latestVersion
|
|
24
|
+
latestVersion,
|
|
25
|
+
releaseUrl: latest.html_url,
|
|
26
|
+
publishedAt: latest.published_at,
|
|
27
|
+
releaseNotes: latest.body,
|
|
28
|
+
assets: latest.assets.map((a) => ({
|
|
29
|
+
name: a.name,
|
|
30
|
+
size: a.size,
|
|
31
|
+
downloadUrl: a.browser_download_url,
|
|
32
|
+
})),
|
|
18
33
|
};
|
|
19
34
|
}
|
|
35
|
+
} catch (err) {
|
|
36
|
+
logger.verbose(`GitHub release check failed: ${err.message}`);
|
|
37
|
+
}
|
|
20
38
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
updateAvailable,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
downloadUrl: a.browser_download_url,
|
|
36
|
-
})),
|
|
37
|
-
};
|
|
39
|
+
// Fallback: check npm registry for CLI package version
|
|
40
|
+
// This catches cases where the GitHub Release CI is still building but npm is already published
|
|
41
|
+
try {
|
|
42
|
+
const npmVersion = await fetchNpmVersion();
|
|
43
|
+
if (npmVersion) {
|
|
44
|
+
const updateAvailable = semver.gt(npmVersion, currentVersion);
|
|
45
|
+
return {
|
|
46
|
+
updateAvailable,
|
|
47
|
+
currentVersion,
|
|
48
|
+
latestVersion: npmVersion,
|
|
49
|
+
releaseUrl: `https://www.npmjs.com/package/chainlesschain/v/${npmVersion}`,
|
|
50
|
+
source: "npm",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
38
53
|
} catch (err) {
|
|
39
|
-
logger.verbose(`
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
logger.verbose(`npm registry check failed: ${err.message}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
updateAvailable: false,
|
|
59
|
+
currentVersion,
|
|
60
|
+
latestVersion: currentVersion,
|
|
61
|
+
error:
|
|
62
|
+
"Unable to check for updates (GitHub releases and npm registry both unavailable)",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function fetchNpmVersion() {
|
|
67
|
+
const response = await fetch(NPM_REGISTRY_URL, {
|
|
68
|
+
headers: { Accept: "application/json" },
|
|
69
|
+
});
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
throw new Error(`npm registry error: HTTP ${response.status}`);
|
|
46
72
|
}
|
|
73
|
+
const data = await response.json();
|
|
74
|
+
return data.version || null;
|
|
47
75
|
}
|
|
48
76
|
|
|
49
77
|
async function fetchReleases() {
|
package/src/lib/web-ui-server.js
CHANGED
|
@@ -55,8 +55,8 @@ function buildHtml({
|
|
|
55
55
|
<title>${escapeHtml(title)}</title>
|
|
56
56
|
<script>window.__CC_CONFIG__ = ${cfg};</script>
|
|
57
57
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
|
|
58
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/
|
|
59
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
|
58
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js" defer></script>
|
|
59
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js" defer></script>
|
|
60
60
|
<style>
|
|
61
61
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
62
62
|
|
|
@@ -460,7 +460,7 @@ function buildHtml({
|
|
|
460
460
|
<div id="conn-dot"></div>
|
|
461
461
|
<span id="conn-label">未连接</span>
|
|
462
462
|
</div>
|
|
463
|
-
<span id="version-label">v5.0.2.
|
|
463
|
+
<span id="version-label">v5.0.2.4</span>
|
|
464
464
|
</div>
|
|
465
465
|
</nav>
|
|
466
466
|
|
|
@@ -560,18 +560,24 @@ function buildHtml({
|
|
|
560
560
|
emptyDesc.textContent = '全局模式:未绑定项目,可直接对话或管理项目。';
|
|
561
561
|
}
|
|
562
562
|
|
|
563
|
-
// ── marked.js config
|
|
564
|
-
|
|
565
|
-
marked
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
563
|
+
// ── marked.js config (deferred — called after defer scripts load) ────────
|
|
564
|
+
function initMarked() {
|
|
565
|
+
if (window.marked) {
|
|
566
|
+
try {
|
|
567
|
+
marked.setOptions({
|
|
568
|
+
highlight: (code, lang) => {
|
|
569
|
+
if (window.hljs && lang && hljs.getLanguage(lang)) {
|
|
570
|
+
return hljs.highlight(code, { language: lang }).value;
|
|
571
|
+
}
|
|
572
|
+
return window.hljs ? hljs.highlightAuto(code).value : code;
|
|
573
|
+
},
|
|
574
|
+
breaks: true,
|
|
575
|
+
gfm: true,
|
|
576
|
+
});
|
|
577
|
+
} catch (_) {
|
|
578
|
+
// marked v10+ removed highlight option — ignore, fallback renderer handles it
|
|
579
|
+
}
|
|
580
|
+
}
|
|
575
581
|
}
|
|
576
582
|
|
|
577
583
|
// ── WebSocket ────────────────────────────────────────────────────────────
|
|
@@ -888,7 +894,7 @@ function buildHtml({
|
|
|
888
894
|
el.className = 'message user';
|
|
889
895
|
el.innerHTML =
|
|
890
896
|
'<div class="msg-avatar">U</div>' +
|
|
891
|
-
'<div class="msg-bubble">' + esc(text).
|
|
897
|
+
'<div class="msg-bubble">' + esc(text).split('\\n').join('<br>') + '</div>';
|
|
892
898
|
messages.appendChild(el);
|
|
893
899
|
scrollToBottom();
|
|
894
900
|
}
|
|
@@ -1077,11 +1083,14 @@ function buildHtml({
|
|
|
1077
1083
|
if (window.marked) {
|
|
1078
1084
|
try { return marked.parse(text); } catch (_) { /* fall through */ }
|
|
1079
1085
|
}
|
|
1080
|
-
return esc(text).
|
|
1086
|
+
return esc(text).split('\\n').join('<br>');
|
|
1081
1087
|
}
|
|
1082
1088
|
|
|
1083
1089
|
// ── Start ────────────────────────────────────────────────────────────────
|
|
1090
|
+
// Connect immediately — do NOT wait for defer scripts (marked/hljs)
|
|
1084
1091
|
connect();
|
|
1092
|
+
// Init marked after defer scripts finish loading
|
|
1093
|
+
window.addEventListener('load', initMarked);
|
|
1085
1094
|
})();
|
|
1086
1095
|
</script>
|
|
1087
1096
|
</body>
|