@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.
@@ -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;
@@ -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.37",
4
- "gitCommitId": "c656a7d",
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.37",
22
- "@love-moon/ai-sdk": "0.2.37",
23
- "@love-moon/conductor-sdk": "0.2.37",
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
- return `New conductor version available: ${currentVersion} -> ${latestVersion}. Run: conductor update`;
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({ currentVersion, latestVersion: cache.latestVersion }));
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
- writeNotice(buildUpdateNotice({ currentVersion, latestVersion: versionToNotify }));
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
- autoUpdateForceLocal || isManagedInstallPathFn(installedPackageRoot);
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
- log("[auto-update] Disabled for local/dev install; set CONDUCTOR_AUTO_UPDATE_FORCE_LOCAL=true to override");
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",
@@ -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
  }