@love-moon/conductor-cli 0.2.19 → 0.2.21
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/bin/conductor-channel.js +130 -0
- package/bin/conductor-config.js +1 -1
- package/bin/conductor-daemon.js +51 -0
- package/bin/conductor-diagnose.js +25 -0
- package/bin/conductor-fire.js +230 -1
- package/bin/conductor-update.js +15 -110
- package/bin/conductor.js +77 -52
- package/package.json +11 -4
- package/src/cli-update-notifier.js +241 -0
- package/src/daemon.js +1006 -32
- package/src/version-check.js +240 -0
package/bin/conductor.js
CHANGED
|
@@ -10,18 +10,16 @@
|
|
|
10
10
|
* update - Update the CLI to the latest version
|
|
11
11
|
* diagnose - Diagnose a task in production/backend
|
|
12
12
|
* send-file - Upload a local file into a task session
|
|
13
|
+
* channel - Connect user-owned chat channel providers
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
|
-
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
16
17
|
import path from "node:path";
|
|
17
|
-
import { createRequire } from "node:module";
|
|
18
18
|
import fs from "node:fs";
|
|
19
|
-
import
|
|
20
|
-
import { hideBin } from "yargs/helpers";
|
|
19
|
+
import { maybeCheckForUpdates } from "../src/cli-update-notifier.js";
|
|
21
20
|
|
|
22
21
|
const __filename = fileURLToPath(import.meta.url);
|
|
23
22
|
const __dirname = path.dirname(__filename);
|
|
24
|
-
const require = createRequire(import.meta.url);
|
|
25
23
|
const PKG_ROOT = path.join(__dirname, "..");
|
|
26
24
|
|
|
27
25
|
const pkgJson = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, "package.json"), "utf-8"));
|
|
@@ -29,57 +27,81 @@ const pkgJson = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, "package.json"),
|
|
|
29
27
|
// Parse command line arguments
|
|
30
28
|
const argv = process.argv.slice(2);
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
process.
|
|
30
|
+
export function runConductorCli(args = argv, deps = {}) {
|
|
31
|
+
const consoleImpl = deps.console || console;
|
|
32
|
+
const importModule = deps.importModule || ((subcommandPath) => import(subcommandPath));
|
|
33
|
+
const env = deps.env || process.env;
|
|
34
|
+
const processArgv = deps.processArgv || process.argv;
|
|
35
|
+
const fsExistsSync = deps.existsSync || fs.existsSync;
|
|
36
|
+
const checkForUpdates = deps.maybeCheckForUpdates || maybeCheckForUpdates;
|
|
37
|
+
const validSubcommands = ["fire", "daemon", "config", "update", "diagnose", "send-file", "channel"];
|
|
38
|
+
|
|
39
|
+
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
40
|
+
showHelp(consoleImpl);
|
|
41
|
+
return { shouldExit: true, exitCode: 0 };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (args[0] === "--version" || args[0] === "-v") {
|
|
45
|
+
const commitId = pkgJson.gitCommitId || "unknown";
|
|
46
|
+
consoleImpl.log(`conductor version ${pkgJson.version} (${commitId})`);
|
|
47
|
+
return { shouldExit: true, exitCode: 0 };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const subcommand = args[0];
|
|
51
|
+
if (!validSubcommands.includes(subcommand)) {
|
|
52
|
+
consoleImpl.error(`Error: Unknown subcommand '${subcommand}'`);
|
|
53
|
+
consoleImpl.error(`Valid subcommands: ${validSubcommands.join(", ")}`);
|
|
54
|
+
consoleImpl.error(`Run 'conductor --help' for usage information.`);
|
|
55
|
+
return { shouldExit: true, exitCode: 1 };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
void checkForUpdates({
|
|
59
|
+
currentVersion: pkgJson.version,
|
|
60
|
+
subcommand,
|
|
61
|
+
env,
|
|
62
|
+
}).catch(() => {});
|
|
63
|
+
|
|
64
|
+
const subcommandArgs = args.slice(1);
|
|
65
|
+
env.CONDUCTOR_CLI_NAME = `conductor ${subcommand}`;
|
|
66
|
+
env.CONDUCTOR_LAUNCHER_SCRIPT = processArgv[1] || "";
|
|
67
|
+
env.CONDUCTOR_SUBCOMMAND = subcommand;
|
|
68
|
+
env.CONDUCTOR_SUBCOMMAND_ARGS_JSON = JSON.stringify(subcommandArgs);
|
|
69
|
+
|
|
70
|
+
const subcommandPath = path.join(__dirname, `conductor-${subcommand}.js`);
|
|
71
|
+
if (!fsExistsSync(subcommandPath)) {
|
|
72
|
+
consoleImpl.error(`Error: Subcommand implementation not found: ${subcommandPath}`);
|
|
73
|
+
return { shouldExit: true, exitCode: 1 };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
process.argv = [processArgv[0], subcommandPath, ...subcommandArgs];
|
|
77
|
+
importModule(subcommandPath).catch((error) => {
|
|
78
|
+
consoleImpl.error(`Error loading subcommand '${subcommand}': ${error.message}`);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
});
|
|
81
|
+
return { shouldExit: false, exitCode: 0 };
|
|
36
82
|
}
|
|
37
83
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
84
|
+
const isDirectExecution = (() => {
|
|
85
|
+
const entryPath = process.argv[1];
|
|
86
|
+
if (!entryPath) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
return pathToFileURL(entryPath).href === import.meta.url;
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
})();
|
|
95
|
+
|
|
96
|
+
if (isDirectExecution) {
|
|
97
|
+
const result = runConductorCli();
|
|
98
|
+
if (result.shouldExit) {
|
|
99
|
+
process.exit(result.exitCode);
|
|
100
|
+
}
|
|
43
101
|
}
|
|
44
102
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// Valid subcommands
|
|
49
|
-
const validSubcommands = ["fire", "daemon", "config", "update", "diagnose", "send-file"];
|
|
50
|
-
|
|
51
|
-
if (!validSubcommands.includes(subcommand)) {
|
|
52
|
-
console.error(`Error: Unknown subcommand '${subcommand}'`);
|
|
53
|
-
console.error(`Valid subcommands: ${validSubcommands.join(", ")}`);
|
|
54
|
-
console.error(`Run 'conductor --help' for usage information.`);
|
|
55
|
-
process.exit(1);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Route to the appropriate subcommand
|
|
59
|
-
const subcommandArgs = argv.slice(1);
|
|
60
|
-
|
|
61
|
-
// Set environment variable to track the CLI name for logging
|
|
62
|
-
process.env.CONDUCTOR_CLI_NAME = `conductor ${subcommand}`;
|
|
63
|
-
|
|
64
|
-
// Import and execute the subcommand
|
|
65
|
-
const subcommandPath = path.join(__dirname, `conductor-${subcommand}.js`);
|
|
66
|
-
|
|
67
|
-
if (!fs.existsSync(subcommandPath)) {
|
|
68
|
-
console.error(`Error: Subcommand implementation not found: ${subcommandPath}`);
|
|
69
|
-
process.exit(1);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Replace process.argv to pass the correct arguments to the subcommand
|
|
73
|
-
process.argv = [process.argv[0], subcommandPath, ...subcommandArgs];
|
|
74
|
-
|
|
75
|
-
// Dynamically import and execute the subcommand
|
|
76
|
-
import(subcommandPath).catch((error) => {
|
|
77
|
-
console.error(`Error loading subcommand '${subcommand}': ${error.message}`);
|
|
78
|
-
process.exit(1);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
function showHelp() {
|
|
82
|
-
console.log(`conductor - Conductor CLI tool
|
|
103
|
+
function showHelp(consoleImpl = console) {
|
|
104
|
+
consoleImpl.log(`conductor - Conductor CLI tool
|
|
83
105
|
|
|
84
106
|
Usage: conductor <subcommand> [options]
|
|
85
107
|
|
|
@@ -90,6 +112,7 @@ Subcommands:
|
|
|
90
112
|
update Update the CLI to the latest version
|
|
91
113
|
diagnose Diagnose a task and print likely root cause
|
|
92
114
|
send-file Upload a local file into a task session
|
|
115
|
+
channel Connect user-owned chat channel providers
|
|
93
116
|
|
|
94
117
|
Options:
|
|
95
118
|
-h, --help Show this help message
|
|
@@ -101,6 +124,7 @@ Examples:
|
|
|
101
124
|
conductor daemon --config-file ~/.conductor/config.yaml
|
|
102
125
|
conductor diagnose <task-id>
|
|
103
126
|
conductor send-file ./screenshot.png
|
|
127
|
+
conductor channel connect feishu
|
|
104
128
|
conductor config
|
|
105
129
|
conductor update
|
|
106
130
|
|
|
@@ -111,6 +135,7 @@ For subcommand-specific help:
|
|
|
111
135
|
conductor update --help
|
|
112
136
|
conductor diagnose --help
|
|
113
137
|
conductor send-file --help
|
|
138
|
+
conductor channel --help
|
|
114
139
|
|
|
115
140
|
Version: ${pkgJson.version}
|
|
116
141
|
`);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@love-moon/conductor-cli",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"gitCommitId": "
|
|
3
|
+
"version": "0.2.21",
|
|
4
|
+
"gitCommitId": "fa11085",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"conductor": "bin/conductor.js"
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"test": "node --test test/*.test.js"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@love-moon/ai-sdk": "0.2.
|
|
21
|
-
"@love-moon/conductor-sdk": "0.2.
|
|
20
|
+
"@love-moon/ai-sdk": "0.2.21",
|
|
21
|
+
"@love-moon/conductor-sdk": "0.2.21",
|
|
22
22
|
"dotenv": "^16.4.5",
|
|
23
23
|
"enquirer": "^2.4.1",
|
|
24
24
|
"js-yaml": "^4.1.1",
|
|
@@ -28,7 +28,14 @@
|
|
|
28
28
|
"chrome-launcher": "^1.2.1",
|
|
29
29
|
"chrome-remote-interface": "^0.33.0"
|
|
30
30
|
},
|
|
31
|
+
"optionalDependencies": {
|
|
32
|
+
"@roamhq/wrtc": "^0.10.0"
|
|
33
|
+
},
|
|
31
34
|
"pnpm": {
|
|
35
|
+
"onlyBuiltDependencies": [
|
|
36
|
+
"node-pty",
|
|
37
|
+
"@roamhq/wrtc"
|
|
38
|
+
],
|
|
32
39
|
"overrides": {
|
|
33
40
|
"@love-moon/ai-sdk": "file:../modules/ai-sdk",
|
|
34
41
|
"@love-moon/conductor-sdk": "file:../modules/conductor-sdk"
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import { fetchLatestVersion, isNewerVersion } from "./version-check.js";
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_VERSION_CHECK_INTERVAL_MS = 12 * 60 * 60 * 1000;
|
|
8
|
+
export const DEFAULT_VERSION_NOTIFY_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
9
|
+
export const DEFAULT_VERSION_CHECK_TIMEOUT_MS = 800;
|
|
10
|
+
const DEFAULT_CACHE_FILE = "version-check.json";
|
|
11
|
+
|
|
12
|
+
function normalizeOptionalString(value) {
|
|
13
|
+
if (typeof value !== "string") {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const normalized = value.trim();
|
|
17
|
+
return normalized || null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function parseBooleanLike(value) {
|
|
21
|
+
if (typeof value !== "string") {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseTimestamp(value) {
|
|
28
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const parsed = Date.parse(value);
|
|
32
|
+
if (Number.isNaN(parsed)) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return new Date(parsed).toISOString();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isTimestampOlderThan(value, ageMs, nowMs) {
|
|
39
|
+
const parsed = value ? Date.parse(value) : NaN;
|
|
40
|
+
if (Number.isNaN(parsed)) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return nowMs - parsed >= ageMs;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function resolveVersionCheckCachePath(options = {}) {
|
|
47
|
+
const homeDir = options.homeDir || process.env.HOME || os.homedir() || "/tmp";
|
|
48
|
+
return path.join(homeDir, ".conductor", DEFAULT_CACHE_FILE);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function normalizeVersionCheckCache(value) {
|
|
52
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const latestVersion = normalizeOptionalString(value.latestVersion);
|
|
56
|
+
const lastNotifiedVersion = normalizeOptionalString(value.lastNotifiedVersion);
|
|
57
|
+
const normalized = {
|
|
58
|
+
lastCheckedAt: parseTimestamp(value.lastCheckedAt),
|
|
59
|
+
latestVersion,
|
|
60
|
+
latestCheckedAt: parseTimestamp(value.latestCheckedAt),
|
|
61
|
+
lastNotifiedVersion,
|
|
62
|
+
lastNotifiedAt: parseTimestamp(value.lastNotifiedAt),
|
|
63
|
+
};
|
|
64
|
+
if (
|
|
65
|
+
!normalized.lastCheckedAt &&
|
|
66
|
+
!normalized.latestVersion &&
|
|
67
|
+
!normalized.latestCheckedAt &&
|
|
68
|
+
!normalized.lastNotifiedVersion &&
|
|
69
|
+
!normalized.lastNotifiedAt
|
|
70
|
+
) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
return normalized;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function readVersionCheckCache(options = {}) {
|
|
77
|
+
const readFileFn = options.readFile || fs.readFile;
|
|
78
|
+
const cachePath = options.cachePath || resolveVersionCheckCachePath(options);
|
|
79
|
+
try {
|
|
80
|
+
const content = await readFileFn(cachePath, "utf8");
|
|
81
|
+
return normalizeVersionCheckCache(JSON.parse(content));
|
|
82
|
+
} catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function writeVersionCheckCache(cache, options = {}) {
|
|
88
|
+
const writeFileFn = options.writeFile || fs.writeFile;
|
|
89
|
+
const mkdirFn = options.mkdir || fs.mkdir;
|
|
90
|
+
const cachePath = options.cachePath || resolveVersionCheckCachePath(options);
|
|
91
|
+
await mkdirFn(path.dirname(cachePath), { recursive: true });
|
|
92
|
+
await writeFileFn(cachePath, JSON.stringify(cache, null, 2), "utf8");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function shouldSkipVersionCheck(options = {}) {
|
|
96
|
+
const env = options.env || process.env;
|
|
97
|
+
const subcommand = normalizeOptionalString(options.subcommand);
|
|
98
|
+
const stdoutIsTTY = options.stdoutIsTTY ?? Boolean(process.stdout?.isTTY);
|
|
99
|
+
|
|
100
|
+
if (parseBooleanLike(env.CONDUCTOR_SKIP_UPDATE_CHECK)) {
|
|
101
|
+
return { skip: true, reason: "disabled_by_env" };
|
|
102
|
+
}
|
|
103
|
+
if (subcommand === "update") {
|
|
104
|
+
return { skip: true, reason: "update_subcommand" };
|
|
105
|
+
}
|
|
106
|
+
if (!stdoutIsTTY) {
|
|
107
|
+
return { skip: true, reason: "non_tty" };
|
|
108
|
+
}
|
|
109
|
+
if (parseBooleanLike(env.CI)) {
|
|
110
|
+
return { skip: true, reason: "ci" };
|
|
111
|
+
}
|
|
112
|
+
if (normalizeOptionalString(env.CONDUCTOR_CLI_COMMAND)) {
|
|
113
|
+
return { skip: true, reason: "nested_cli" };
|
|
114
|
+
}
|
|
115
|
+
return { skip: false, reason: null };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function buildUpdateNotice({ currentVersion, latestVersion }) {
|
|
119
|
+
return `New conductor version available: ${currentVersion} -> ${latestVersion}. Run: conductor update`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function shouldNotifyVersion({ latestVersion, currentVersion, cache, nowMs, notifyIntervalMs }) {
|
|
123
|
+
if (!latestVersion || !isNewerVersion(latestVersion, currentVersion)) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
if (!cache?.lastNotifiedVersion || cache.lastNotifiedVersion !== latestVersion) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
return isTimestampOlderThan(cache.lastNotifiedAt, notifyIntervalMs, nowMs);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function shouldRefreshVersion({ cache, nowMs, checkIntervalMs }) {
|
|
133
|
+
if (!cache?.lastCheckedAt) {
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
return isTimestampOlderThan(cache.lastCheckedAt, checkIntervalMs, nowMs);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function createUpdatedCache(previousCache, updates = {}) {
|
|
140
|
+
return normalizeVersionCheckCache({
|
|
141
|
+
...previousCache,
|
|
142
|
+
...updates,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function maybeCheckForUpdates(options = {}) {
|
|
147
|
+
const env = options.env || process.env;
|
|
148
|
+
const currentVersion = normalizeOptionalString(options.currentVersion);
|
|
149
|
+
const subcommand = normalizeOptionalString(options.subcommand);
|
|
150
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
151
|
+
const checkIntervalMs = options.checkIntervalMs ?? DEFAULT_VERSION_CHECK_INTERVAL_MS;
|
|
152
|
+
const notifyIntervalMs = options.notifyIntervalMs ?? DEFAULT_VERSION_NOTIFY_INTERVAL_MS;
|
|
153
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_VERSION_CHECK_TIMEOUT_MS;
|
|
154
|
+
const skip = shouldSkipVersionCheck({
|
|
155
|
+
env,
|
|
156
|
+
subcommand,
|
|
157
|
+
stdoutIsTTY: options.stdoutIsTTY,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
if (skip.skip || !currentVersion) {
|
|
161
|
+
return { skipped: true, reason: skip.reason || "missing_current_version" };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const writeNotice = options.writeNotice || ((message) => process.stderr.write(`${message}\n`));
|
|
165
|
+
const fetchLatestVersionFn = options.fetchLatestVersion || fetchLatestVersion;
|
|
166
|
+
const cacheOptions = {
|
|
167
|
+
cachePath: options.cachePath,
|
|
168
|
+
homeDir: options.homeDir || env.HOME,
|
|
169
|
+
readFile: options.readFile,
|
|
170
|
+
writeFile: options.writeFile,
|
|
171
|
+
mkdir: options.mkdir,
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
let cache = await readVersionCheckCache(cacheOptions);
|
|
175
|
+
const needsRefresh = shouldRefreshVersion({ cache, nowMs, checkIntervalMs });
|
|
176
|
+
|
|
177
|
+
if (!needsRefresh) {
|
|
178
|
+
if (cache?.latestVersion && shouldNotifyVersion({
|
|
179
|
+
latestVersion: cache.latestVersion,
|
|
180
|
+
currentVersion,
|
|
181
|
+
cache,
|
|
182
|
+
nowMs,
|
|
183
|
+
notifyIntervalMs,
|
|
184
|
+
})) {
|
|
185
|
+
writeNotice(buildUpdateNotice({ currentVersion, latestVersion: cache.latestVersion }));
|
|
186
|
+
cache = createUpdatedCache(cache, {
|
|
187
|
+
lastNotifiedVersion: cache.latestVersion,
|
|
188
|
+
lastNotifiedAt: new Date(nowMs).toISOString(),
|
|
189
|
+
});
|
|
190
|
+
if (cache) {
|
|
191
|
+
await writeVersionCheckCache(cache, cacheOptions);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return { skipped: false, refreshed: false, latestVersion: cache?.latestVersion || null };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let latestVersion = null;
|
|
198
|
+
try {
|
|
199
|
+
latestVersion = await fetchLatestVersionFn(undefined, { timeoutMs });
|
|
200
|
+
} catch {
|
|
201
|
+
latestVersion = null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
cache = createUpdatedCache(cache, {
|
|
205
|
+
lastCheckedAt: new Date(nowMs).toISOString(),
|
|
206
|
+
...(latestVersion
|
|
207
|
+
? {
|
|
208
|
+
latestVersion,
|
|
209
|
+
latestCheckedAt: new Date(nowMs).toISOString(),
|
|
210
|
+
}
|
|
211
|
+
: {}),
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
if (cache) {
|
|
215
|
+
await writeVersionCheckCache(cache, cacheOptions);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const versionToNotify = latestVersion || cache?.latestVersion || null;
|
|
219
|
+
if (versionToNotify && shouldNotifyVersion({
|
|
220
|
+
latestVersion: versionToNotify,
|
|
221
|
+
currentVersion,
|
|
222
|
+
cache,
|
|
223
|
+
nowMs,
|
|
224
|
+
notifyIntervalMs,
|
|
225
|
+
})) {
|
|
226
|
+
writeNotice(buildUpdateNotice({ currentVersion, latestVersion: versionToNotify }));
|
|
227
|
+
cache = createUpdatedCache(cache, {
|
|
228
|
+
lastNotifiedVersion: versionToNotify,
|
|
229
|
+
lastNotifiedAt: new Date(nowMs).toISOString(),
|
|
230
|
+
});
|
|
231
|
+
if (cache) {
|
|
232
|
+
await writeVersionCheckCache(cache, cacheOptions);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
skipped: false,
|
|
238
|
+
refreshed: true,
|
|
239
|
+
latestVersion,
|
|
240
|
+
};
|
|
241
|
+
}
|