@love-moon/conductor-cli 0.2.37 → 0.2.38
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-fire.js +9 -0
- package/bin/conductor-update.js +13 -2
- package/package.json +5 -5
- package/src/cli-update-notifier.js +21 -5
- package/src/daemon.js +19 -2
- package/src/version-check.js +51 -1
package/bin/conductor-fire.js
CHANGED
|
@@ -53,6 +53,14 @@ export function isLaunchedByDaemon(env = process.env) {
|
|
|
53
53
|
);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
export function syncPwdEnvWithProcessCwdForDaemonLaunch(env = process.env, cwdFn = process.cwd) {
|
|
57
|
+
if (!isLaunchedByDaemon(env)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
env.PWD = cwdFn();
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
56
64
|
const ENABLE_FIRE_LOCAL_LOG = !isLaunchedByDaemon(process.env);
|
|
57
65
|
|
|
58
66
|
const pkgJson = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, "package.json"), "utf-8"));
|
|
@@ -570,6 +578,7 @@ export class FireWatchdog {
|
|
|
570
578
|
}
|
|
571
579
|
|
|
572
580
|
async function main() {
|
|
581
|
+
syncPwdEnvWithProcessCwdForDaemonLaunch();
|
|
573
582
|
const cliArgs = await parseCliArgs();
|
|
574
583
|
let runtimeProjectPath = process.cwd();
|
|
575
584
|
let backendSession = null;
|
package/bin/conductor-update.js
CHANGED
|
@@ -6,13 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import path from "node:path";
|
|
9
|
-
import { createRequire } from "node:module";
|
|
10
9
|
import fs from "node:fs";
|
|
11
10
|
import { spawn } from "node:child_process";
|
|
12
11
|
import process from "node:process";
|
|
13
12
|
import readline from "node:readline/promises";
|
|
14
13
|
import {
|
|
15
14
|
PACKAGE_NAME,
|
|
15
|
+
buildUpgradeCommand,
|
|
16
|
+
resolveInstallMethod,
|
|
16
17
|
fetchLatestVersion,
|
|
17
18
|
isNewerVersion,
|
|
18
19
|
detectPackageManager,
|
|
@@ -24,11 +25,14 @@ import {
|
|
|
24
25
|
|
|
25
26
|
const __filename = fileURLToPath(import.meta.url);
|
|
26
27
|
const __dirname = path.dirname(__filename);
|
|
27
|
-
const require = createRequire(import.meta.url);
|
|
28
28
|
const PKG_ROOT = path.join(__dirname, "..");
|
|
29
29
|
|
|
30
30
|
const pkgJson = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, "package.json"), "utf-8"));
|
|
31
31
|
const CURRENT_VERSION = pkgJson.version;
|
|
32
|
+
const INSTALL_METHOD = resolveInstallMethod({
|
|
33
|
+
env: process.env,
|
|
34
|
+
packageRoot: PKG_ROOT,
|
|
35
|
+
});
|
|
32
36
|
|
|
33
37
|
// ANSI 颜色代码
|
|
34
38
|
const COLORS = {
|
|
@@ -56,6 +60,13 @@ async function main() {
|
|
|
56
60
|
process.exit(0);
|
|
57
61
|
}
|
|
58
62
|
|
|
63
|
+
if (INSTALL_METHOD === "homebrew") {
|
|
64
|
+
console.log(colorize("🍺 Homebrew-managed install detected", "cyan"));
|
|
65
|
+
console.log("");
|
|
66
|
+
console.log(` Use ${colorize(buildUpgradeCommand({ env: process.env }), "green")} to upgrade conductor.`);
|
|
67
|
+
process.exit(0);
|
|
68
|
+
}
|
|
69
|
+
|
|
59
70
|
console.log(colorize(`📦 ${PACKAGE_NAME}`, "cyan"));
|
|
60
71
|
console.log(` Current version: ${CURRENT_VERSION}`);
|
|
61
72
|
console.log("");
|
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.38",
|
|
4
|
+
"gitCommitId": "6bf5515",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"conductor": "bin/conductor.js"
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@love-moon/ai-bridge": "0.1.4",
|
|
21
|
-
"@love-moon/ai-manager": "0.2.
|
|
22
|
-
"@love-moon/ai-sdk": "0.2.
|
|
23
|
-
"@love-moon/conductor-sdk": "0.2.
|
|
21
|
+
"@love-moon/ai-manager": "0.2.38",
|
|
22
|
+
"@love-moon/ai-sdk": "0.2.38",
|
|
23
|
+
"@love-moon/conductor-sdk": "0.2.38",
|
|
24
24
|
"chrome-launcher": "^1.2.1",
|
|
25
25
|
"chrome-remote-interface": "^0.33.0",
|
|
26
26
|
"dotenv": "^16.4.5",
|
|
@@ -2,7 +2,7 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
|
-
import { fetchLatestVersion, isNewerVersion } from "./version-check.js";
|
|
5
|
+
import { buildUpgradeCommand, fetchLatestVersion, isNewerVersion } from "./version-check.js";
|
|
6
6
|
|
|
7
7
|
export const DEFAULT_VERSION_CHECK_INTERVAL_MS = 12 * 60 * 60 * 1000;
|
|
8
8
|
export const DEFAULT_VERSION_NOTIFY_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
@@ -115,8 +115,13 @@ export function shouldSkipVersionCheck(options = {}) {
|
|
|
115
115
|
return { skip: false, reason: null };
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
export function buildUpdateNotice({ currentVersion, latestVersion }) {
|
|
119
|
-
|
|
118
|
+
export function buildUpdateNotice({ currentVersion, latestVersion, installMethod, env }) {
|
|
119
|
+
const noticeEnv =
|
|
120
|
+
installMethod && !env?.CONDUCTOR_INSTALL_METHOD
|
|
121
|
+
? { ...env, CONDUCTOR_INSTALL_METHOD: installMethod }
|
|
122
|
+
: env;
|
|
123
|
+
const upgradeCommand = buildUpgradeCommand({ env: noticeEnv });
|
|
124
|
+
return `New conductor version available: ${currentVersion} -> ${latestVersion}. Run: ${upgradeCommand}`;
|
|
120
125
|
}
|
|
121
126
|
|
|
122
127
|
function shouldNotifyVersion({ latestVersion, currentVersion, cache, nowMs, notifyIntervalMs }) {
|
|
@@ -147,6 +152,7 @@ export async function maybeCheckForUpdates(options = {}) {
|
|
|
147
152
|
const env = options.env || process.env;
|
|
148
153
|
const currentVersion = normalizeOptionalString(options.currentVersion);
|
|
149
154
|
const subcommand = normalizeOptionalString(options.subcommand);
|
|
155
|
+
const installMethod = normalizeOptionalString(env.CONDUCTOR_INSTALL_METHOD);
|
|
150
156
|
const nowMs = options.nowMs ?? Date.now();
|
|
151
157
|
const checkIntervalMs = options.checkIntervalMs ?? DEFAULT_VERSION_CHECK_INTERVAL_MS;
|
|
152
158
|
const notifyIntervalMs = options.notifyIntervalMs ?? DEFAULT_VERSION_NOTIFY_INTERVAL_MS;
|
|
@@ -182,7 +188,12 @@ export async function maybeCheckForUpdates(options = {}) {
|
|
|
182
188
|
nowMs,
|
|
183
189
|
notifyIntervalMs,
|
|
184
190
|
})) {
|
|
185
|
-
writeNotice(buildUpdateNotice({
|
|
191
|
+
writeNotice(buildUpdateNotice({
|
|
192
|
+
currentVersion,
|
|
193
|
+
latestVersion: cache.latestVersion,
|
|
194
|
+
installMethod,
|
|
195
|
+
env,
|
|
196
|
+
}));
|
|
186
197
|
cache = createUpdatedCache(cache, {
|
|
187
198
|
lastNotifiedVersion: cache.latestVersion,
|
|
188
199
|
lastNotifiedAt: new Date(nowMs).toISOString(),
|
|
@@ -223,7 +234,12 @@ export async function maybeCheckForUpdates(options = {}) {
|
|
|
223
234
|
nowMs,
|
|
224
235
|
notifyIntervalMs,
|
|
225
236
|
})) {
|
|
226
|
-
|
|
237
|
+
writeNotice(buildUpdateNotice({
|
|
238
|
+
currentVersion,
|
|
239
|
+
latestVersion: versionToNotify,
|
|
240
|
+
installMethod,
|
|
241
|
+
env,
|
|
242
|
+
}));
|
|
227
243
|
cache = createUpdatedCache(cache, {
|
|
228
244
|
lastNotifiedVersion: versionToNotify,
|
|
229
245
|
lastNotifiedAt: new Date(nowMs).toISOString(),
|
package/src/daemon.js
CHANGED
|
@@ -29,12 +29,14 @@ import {
|
|
|
29
29
|
} from "./runtime-backends.js";
|
|
30
30
|
import {
|
|
31
31
|
PACKAGE_NAME,
|
|
32
|
+
buildUpgradeCommand,
|
|
32
33
|
fetchLatestVersion,
|
|
33
34
|
isNewerVersion,
|
|
34
35
|
detectPackageManager,
|
|
35
36
|
parseUpdateWindow,
|
|
36
37
|
isInUpdateWindow,
|
|
37
38
|
isManagedInstallPath,
|
|
39
|
+
resolveInstallMethod,
|
|
38
40
|
} from "./version-check.js";
|
|
39
41
|
import {
|
|
40
42
|
ensurePnpmOnlyBuiltDependencies,
|
|
@@ -662,12 +664,22 @@ export function startDaemon(config = {}, deps = {}) {
|
|
|
662
664
|
const parseUpdateWindowFn = deps.parseUpdateWindow || parseUpdateWindow;
|
|
663
665
|
const isInUpdateWindowFn = deps.isInUpdateWindow || isInUpdateWindow;
|
|
664
666
|
const isManagedInstallPathFn = deps.isManagedInstallPath || isManagedInstallPath;
|
|
667
|
+
const resolveInstallMethodFn = deps.resolveInstallMethod || resolveInstallMethod;
|
|
665
668
|
const installedPackageRoot = deps.packageRoot || PACKAGE_ROOT;
|
|
666
669
|
const cliVersion = deps.cliVersion || CLI_VERSION;
|
|
667
670
|
const isBackgroundProcess = deps.isBackgroundProcess ?? !process.stdout.isTTY;
|
|
668
671
|
const autoUpdateForceLocal = parseBooleanEnv(process.env.CONDUCTOR_AUTO_UPDATE_FORCE_LOCAL);
|
|
672
|
+
const installMethod = resolveInstallMethodFn({
|
|
673
|
+
env: process.env,
|
|
674
|
+
packageRoot: installedPackageRoot,
|
|
675
|
+
readFileSync: deps.readFileSync || fs.readFileSync,
|
|
676
|
+
});
|
|
669
677
|
const autoUpdateSupportedInstall =
|
|
670
|
-
|
|
678
|
+
(installMethod !== "homebrew") &&
|
|
679
|
+
(autoUpdateForceLocal || isManagedInstallPathFn(installedPackageRoot, {
|
|
680
|
+
env: process.env,
|
|
681
|
+
readFileSync: deps.readFileSync || fs.readFileSync,
|
|
682
|
+
}));
|
|
671
683
|
const skipPidLockCheck = parseBooleanEnv(process.env.CONDUCTOR_TUI_DEBUG);
|
|
672
684
|
const lockHandoffToken =
|
|
673
685
|
normalizeOptionalString(config.LOCK_HANDOFF_TOKEN) ||
|
|
@@ -1570,7 +1582,11 @@ export function startDaemon(config = {}, deps = {}) {
|
|
|
1570
1582
|
});
|
|
1571
1583
|
|
|
1572
1584
|
if (!AUTO_UPDATE_ENABLED && autoUpdateSupportedInstall === false) {
|
|
1573
|
-
|
|
1585
|
+
if (installMethod === "homebrew") {
|
|
1586
|
+
log(`[auto-update] Disabled for Homebrew install; use ${buildUpgradeCommand({ env: process.env })}`);
|
|
1587
|
+
} else {
|
|
1588
|
+
log("[auto-update] Disabled for local/dev install; set CONDUCTOR_AUTO_UPDATE_FORCE_LOCAL=true to override");
|
|
1589
|
+
}
|
|
1574
1590
|
}
|
|
1575
1591
|
|
|
1576
1592
|
watchdogTimer = setInterval(() => {
|
|
@@ -4246,6 +4262,7 @@ export function startDaemon(config = {}, deps = {}) {
|
|
|
4246
4262
|
|
|
4247
4263
|
const env = {
|
|
4248
4264
|
...process.env,
|
|
4265
|
+
PWD: taskDir,
|
|
4249
4266
|
CONDUCTOR_PROJECT_ID: projectId,
|
|
4250
4267
|
CONDUCTOR_TASK_ID: taskId,
|
|
4251
4268
|
CONDUCTOR_LAUNCHED_BY_DAEMON: "1",
|
package/src/version-check.js
CHANGED
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
* Shared version-check utilities used by both `conductor update` and the daemon auto-update flow.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import fs from "node:fs";
|
|
5
6
|
import http from "node:http";
|
|
6
7
|
import https from "node:https";
|
|
7
8
|
import path from "node:path";
|
|
8
9
|
import { execFileSync, execSync } from "node:child_process";
|
|
9
10
|
|
|
10
11
|
export const PACKAGE_NAME = "@love-moon/conductor-cli";
|
|
12
|
+
export const DEFAULT_HOMEBREW_FORMULA = "lovemoon-ai/tap/conductor";
|
|
11
13
|
const DEFAULT_UPDATE_WINDOW = { startMinutes: 120, endMinutes: 240 };
|
|
12
14
|
const REQUEST_TIMEOUT_MS = 10_000;
|
|
15
|
+
const INSTALL_METHOD_FILENAME = ".install-method";
|
|
13
16
|
|
|
14
17
|
function resolveTimeoutMs(value) {
|
|
15
18
|
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
@@ -100,6 +103,50 @@ function getRegistryBaseUrl(overrideRegistryUrl) {
|
|
|
100
103
|
return candidate;
|
|
101
104
|
}
|
|
102
105
|
|
|
106
|
+
function normalizeInstallMethod(value) {
|
|
107
|
+
if (typeof value !== "string") {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const normalized = value.trim().toLowerCase();
|
|
111
|
+
return normalized || null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function resolveHomebrewFormula(env = process.env) {
|
|
115
|
+
const configured = typeof env?.CONDUCTOR_HOMEBREW_FORMULA === "string"
|
|
116
|
+
? env.CONDUCTOR_HOMEBREW_FORMULA.trim()
|
|
117
|
+
: "";
|
|
118
|
+
return configured || DEFAULT_HOMEBREW_FORMULA;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function resolveInstallMethod(options = {}) {
|
|
122
|
+
const env = options.env || process.env;
|
|
123
|
+
const envMethod = normalizeInstallMethod(env?.CONDUCTOR_INSTALL_METHOD);
|
|
124
|
+
if (envMethod) {
|
|
125
|
+
return envMethod;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const packageRoot = typeof options.packageRoot === "string" ? options.packageRoot.trim() : "";
|
|
129
|
+
if (!packageRoot) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const readFileSyncFn = options.readFileSync || fs.readFileSync;
|
|
134
|
+
const installMethodPath = path.join(path.resolve(packageRoot), INSTALL_METHOD_FILENAME);
|
|
135
|
+
try {
|
|
136
|
+
return normalizeInstallMethod(readFileSyncFn(installMethodPath, "utf-8"));
|
|
137
|
+
} catch {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function buildUpgradeCommand(options = {}) {
|
|
143
|
+
const installMethod = resolveInstallMethod(options);
|
|
144
|
+
if (installMethod === "homebrew") {
|
|
145
|
+
return `brew upgrade ${resolveHomebrewFormula(options.env || process.env)}`;
|
|
146
|
+
}
|
|
147
|
+
return "conductor update";
|
|
148
|
+
}
|
|
149
|
+
|
|
103
150
|
/**
|
|
104
151
|
* Compare two semver-like version strings.
|
|
105
152
|
* Returns `true` when `latest` is strictly newer than `current`.
|
|
@@ -217,7 +264,10 @@ export function parseUpdateWindow(str) {
|
|
|
217
264
|
* Auto-update should only mutate managed/global installs. Local repo runs and pnpm-linked
|
|
218
265
|
* worktrees are treated as development installs and are skipped by default.
|
|
219
266
|
*/
|
|
220
|
-
export function isManagedInstallPath(packageRoot) {
|
|
267
|
+
export function isManagedInstallPath(packageRoot, options = {}) {
|
|
268
|
+
if (resolveInstallMethod({ ...options, packageRoot }) === "homebrew") {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
221
271
|
if (typeof packageRoot !== "string" || !packageRoot.trim()) {
|
|
222
272
|
return false;
|
|
223
273
|
}
|