@love-moon/conductor-cli 0.2.29 → 0.2.31
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-config.js +2 -1
- package/bin/conductor-fire.js +85 -34
- package/package.json +4 -4
- package/src/daemon.js +359 -249
- package/src/fire/resume.js +101 -1
- package/src/native-deps.js +9 -7
- package/src/runtime-backends.js +219 -9
package/bin/conductor-config.js
CHANGED
|
@@ -257,7 +257,8 @@ async function main() {
|
|
|
257
257
|
"# envs:",
|
|
258
258
|
"# http_proxy: http://127.0.0.1:7890",
|
|
259
259
|
"# https_proxy: http://127.0.0.1:7890",
|
|
260
|
-
"# all_proxy: socks5://127.0.0.1:7890"
|
|
260
|
+
"# all_proxy: socks5://127.0.0.1:7890",
|
|
261
|
+
"# AISDK_PROVIDER_PATH: path-to-private-aisdk-provider.js"
|
|
261
262
|
);
|
|
262
263
|
|
|
263
264
|
fs.writeFileSync(CONFIG_FILE, lines.join("\n"), "utf-8");
|
package/bin/conductor-fire.js
CHANGED
|
@@ -26,7 +26,13 @@ import {
|
|
|
26
26
|
resolveSessionRunDirectory as resolveCliSessionRunDirectory,
|
|
27
27
|
resumeProviderForBackend as resumeProviderForCliBackend,
|
|
28
28
|
} from "../src/fire/resume.js";
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
filterRuntimeSupportedAllowCliList,
|
|
31
|
+
RUNTIME_SUPPORTED_BACKENDS,
|
|
32
|
+
listRuntimeSupportedBackends,
|
|
33
|
+
normalizeRuntimeBackendAlias,
|
|
34
|
+
normalizeRuntimeBackendName,
|
|
35
|
+
} from "../src/runtime-backends.js";
|
|
30
36
|
|
|
31
37
|
const __filename = fileURLToPath(import.meta.url);
|
|
32
38
|
const __dirname = path.dirname(__filename);
|
|
@@ -34,7 +40,19 @@ const PKG_ROOT = path.join(__dirname, "..");
|
|
|
34
40
|
const INITIAL_CLI_PROJECT_PATH = process.cwd();
|
|
35
41
|
const FIRE_LOG_PATH = path.join(INITIAL_CLI_PROJECT_PATH, "conductor.log");
|
|
36
42
|
const FIRE_TASK_MARKER_PREFIX = "active-fire";
|
|
37
|
-
|
|
43
|
+
|
|
44
|
+
export function isLaunchedByDaemon(env = process.env) {
|
|
45
|
+
const daemonHostedValue =
|
|
46
|
+
typeof env?.CONDUCTOR_LAUNCHED_BY_DAEMON === "string" ? env.CONDUCTOR_LAUNCHED_BY_DAEMON.trim().toLowerCase() : "";
|
|
47
|
+
if (daemonHostedValue && daemonHostedValue !== "0" && daemonHostedValue !== "false" && daemonHostedValue !== "no") {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
return Boolean(
|
|
51
|
+
typeof env?.CONDUCTOR_CLI_COMMAND === "string" && env.CONDUCTOR_CLI_COMMAND.trim(),
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const ENABLE_FIRE_LOCAL_LOG = !isLaunchedByDaemon(process.env);
|
|
38
56
|
|
|
39
57
|
const pkgJson = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, "package.json"), "utf-8"));
|
|
40
58
|
const CLI_NAME = (process.env.CONDUCTOR_CLI_NAME || path.basename(process.argv[1] || "conductor-fire")).replace(
|
|
@@ -49,20 +67,21 @@ export function buildConductorConnectHeaders(version = pkgJson.version) {
|
|
|
49
67
|
}
|
|
50
68
|
|
|
51
69
|
// Load allow_cli_list from config file (no defaults - must be configured)
|
|
52
|
-
function loadAllowCliList(configFilePath) {
|
|
70
|
+
async function loadAllowCliList(configFilePath) {
|
|
71
|
+
const home = os.homedir();
|
|
72
|
+
const configPath = configFilePath || process.env.CONDUCTOR_CONFIG || path.join(home, ".conductor", "config.yaml");
|
|
73
|
+
let parsed = null;
|
|
53
74
|
try {
|
|
54
|
-
const home = os.homedir();
|
|
55
|
-
const configPath = configFilePath || process.env.CONDUCTOR_CONFIG || path.join(home, ".conductor", "config.yaml");
|
|
56
75
|
if (fs.existsSync(configPath)) {
|
|
57
76
|
const content = fs.readFileSync(configPath, "utf8");
|
|
58
|
-
|
|
59
|
-
if (parsed && typeof parsed === "object" && parsed.allow_cli_list) {
|
|
60
|
-
return filterRuntimeSupportedAllowCliList(parsed.allow_cli_list);
|
|
61
|
-
}
|
|
77
|
+
parsed = yaml.load(content);
|
|
62
78
|
}
|
|
63
79
|
} catch (error) {
|
|
64
80
|
// ignore error
|
|
65
81
|
}
|
|
82
|
+
if (parsed && typeof parsed === "object" && parsed.allow_cli_list) {
|
|
83
|
+
return await filterRuntimeSupportedAllowCliList(parsed.allow_cli_list, { configFilePath: configPath });
|
|
84
|
+
}
|
|
66
85
|
return {};
|
|
67
86
|
}
|
|
68
87
|
|
|
@@ -396,7 +415,7 @@ export class FireWatchdog {
|
|
|
396
415
|
}
|
|
397
416
|
|
|
398
417
|
async function main() {
|
|
399
|
-
const cliArgs = parseCliArgs();
|
|
418
|
+
const cliArgs = await parseCliArgs();
|
|
400
419
|
let runtimeProjectPath = process.cwd();
|
|
401
420
|
let backendSession = null;
|
|
402
421
|
|
|
@@ -409,18 +428,28 @@ async function main() {
|
|
|
409
428
|
return;
|
|
410
429
|
}
|
|
411
430
|
|
|
412
|
-
const allowCliList = loadAllowCliList(cliArgs.configFile);
|
|
431
|
+
const allowCliList = await loadAllowCliList(cliArgs.configFile);
|
|
413
432
|
const supportedBackends = Object.keys(allowCliList);
|
|
433
|
+
const discoveredBackends = await listRuntimeSupportedBackends({ configFilePath: cliArgs.configFile });
|
|
434
|
+
const externalBackends = discoveredBackends.filter((backend) => !RUNTIME_SUPPORTED_BACKENDS.includes(backend));
|
|
414
435
|
|
|
415
436
|
if (cliArgs.listBackends) {
|
|
416
|
-
if (supportedBackends.length === 0) {
|
|
437
|
+
if (supportedBackends.length === 0 && externalBackends.length === 0) {
|
|
417
438
|
process.stdout.write(`No supported backends configured.\n\nAdd allow_cli_list to your config file (~/.conductor/config.yaml):\n allow_cli_list:\n codex: codex --dangerously-bypass-approvals-and-sandbox\n claude: claude --dangerously-skip-permissions\n kimi: kimi\n opencode: opencode\n`);
|
|
418
439
|
} else {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
440
|
+
if (supportedBackends.length > 0) {
|
|
441
|
+
process.stdout.write(`Supported backends (from config):\n`);
|
|
442
|
+
for (const [name, command] of Object.entries(allowCliList)) {
|
|
443
|
+
process.stdout.write(` ${name}: ${command}\n`);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (externalBackends.length > 0) {
|
|
447
|
+
process.stdout.write(`${supportedBackends.length > 0 ? "\n" : ""}External backends (from AISDK_PROVIDER_PATH):\n`);
|
|
448
|
+
for (const name of externalBackends) {
|
|
449
|
+
process.stdout.write(` ${name}\n`);
|
|
450
|
+
}
|
|
422
451
|
}
|
|
423
|
-
process.stdout.write(`\nDefault: ${supportedBackends[0]}\n`);
|
|
452
|
+
process.stdout.write(`\nDefault: ${supportedBackends[0] || externalBackends[0]}\n`);
|
|
424
453
|
}
|
|
425
454
|
return;
|
|
426
455
|
}
|
|
@@ -429,6 +458,7 @@ async function main() {
|
|
|
429
458
|
if (cliArgs.resumeSessionId) {
|
|
430
459
|
const bootstrap = await bootstrapResumeContextForFire({
|
|
431
460
|
backend: cliArgs.backend,
|
|
461
|
+
configFile: cliArgs.configFile,
|
|
432
462
|
resumeSessionId: cliArgs.resumeSessionId,
|
|
433
463
|
});
|
|
434
464
|
resumeContext = bootstrap.resumeContext;
|
|
@@ -436,7 +466,7 @@ async function main() {
|
|
|
436
466
|
}
|
|
437
467
|
|
|
438
468
|
const env = buildEnv();
|
|
439
|
-
const launchedByDaemon =
|
|
469
|
+
const launchedByDaemon = isLaunchedByDaemon(process.env);
|
|
440
470
|
let reconnectRunner = null;
|
|
441
471
|
let reconnectTaskId = null;
|
|
442
472
|
let pendingRemoteStopEvent = null;
|
|
@@ -815,7 +845,7 @@ function stripConductorArgsFromArgv(argv = []) {
|
|
|
815
845
|
return backendArgs;
|
|
816
846
|
}
|
|
817
847
|
|
|
818
|
-
export function parseCliArgs(argvInput = process.argv) {
|
|
848
|
+
export async function parseCliArgs(argvInput = process.argv) {
|
|
819
849
|
const rawArgv = Array.isArray(argvInput) ? argvInput : process.argv;
|
|
820
850
|
const argv = hideBin(rawArgv);
|
|
821
851
|
const separatorIndex = argv.indexOf("--");
|
|
@@ -833,8 +863,10 @@ export function parseCliArgs(argvInput = process.argv) {
|
|
|
833
863
|
}
|
|
834
864
|
|
|
835
865
|
const configFileFromArgs = extractConfigFileFromArgv(argv);
|
|
836
|
-
const allowCliList = loadAllowCliList(configFileFromArgs);
|
|
866
|
+
const allowCliList = await loadAllowCliList(configFileFromArgs);
|
|
837
867
|
const supportedBackends = Object.keys(allowCliList);
|
|
868
|
+
const discoveredBackends = await listRuntimeSupportedBackends({ configFilePath: configFileFromArgs });
|
|
869
|
+
const externalBackends = discoveredBackends.filter((backend) => !RUNTIME_SUPPORTED_BACKENDS.includes(backend));
|
|
838
870
|
|
|
839
871
|
const conductorArgs = yargs(conductorArgv)
|
|
840
872
|
.scriptName(CLI_NAME)
|
|
@@ -886,14 +918,14 @@ export function parseCliArgs(argvInput = process.argv) {
|
|
|
886
918
|
|
|
887
919
|
// Handle help early
|
|
888
920
|
if (helpWithoutSeparator) {
|
|
889
|
-
const defaultBackend = supportedBackends[0] || "none";
|
|
921
|
+
const defaultBackend = supportedBackends[0] || externalBackends[0] || "none";
|
|
890
922
|
process.stdout.write(`${CLI_NAME} - Conductor-aware AI coding agent runner
|
|
891
923
|
|
|
892
924
|
Usage: ${CLI_NAME} [options] -- [backend options and prompt]
|
|
893
925
|
|
|
894
926
|
Options:
|
|
895
|
-
-b, --backend <name> Backend to use (from config: ${supportedBackends.join(", ") || "none configured"}) [default: ${defaultBackend}]
|
|
896
|
-
--list-backends List available
|
|
927
|
+
-b, --backend <name> Backend to use (from config or external providers: ${supportedBackends.join(", ") || externalBackends.join(", ") || "none configured"}) [default: ${defaultBackend}]
|
|
928
|
+
--list-backends List available configured and external backends
|
|
897
929
|
--config-file <path> Path to Conductor config file
|
|
898
930
|
--poll-interval <ms> Polling interval when waiting for Conductor messages
|
|
899
931
|
-t, --title <text> Optional task title shown in the app task list
|
|
@@ -937,20 +969,21 @@ Environment:
|
|
|
937
969
|
.parse();
|
|
938
970
|
|
|
939
971
|
const backend = conductorArgs.backend
|
|
940
|
-
?
|
|
941
|
-
: supportedBackends[0];
|
|
972
|
+
? await normalizeRuntimeBackendAlias(conductorArgs.backend, { configFilePath: configFileFromArgs })
|
|
973
|
+
: supportedBackends[0] || externalBackends[0];
|
|
942
974
|
const shouldRequireBackend =
|
|
943
975
|
!Boolean(conductorArgs.listBackends) &&
|
|
944
976
|
!listBackendsWithoutSeparator &&
|
|
945
977
|
!Boolean(conductorArgs.version) &&
|
|
946
978
|
!versionWithoutSeparator;
|
|
947
|
-
|
|
979
|
+
const runtimeSupportedBackends = new Set(discoveredBackends);
|
|
980
|
+
if (backend && !runtimeSupportedBackends.has(backend) && shouldRequireBackend) {
|
|
948
981
|
throw new Error(
|
|
949
|
-
`Unsupported backend "${backend}". Supported backends: ${
|
|
982
|
+
`Unsupported backend "${backend}". Supported backends: ${[...runtimeSupportedBackends].join(", ") || "none configured"}.`,
|
|
950
983
|
);
|
|
951
984
|
}
|
|
952
985
|
if (!backend && shouldRequireBackend) {
|
|
953
|
-
throw new Error("No supported backends configured. Add
|
|
986
|
+
throw new Error("No supported backends configured. Add allow_cli_list entries or set AISDK_PROVIDER_PATH for external providers.");
|
|
954
987
|
}
|
|
955
988
|
|
|
956
989
|
const prompt = (backendArgs._ || []).map((part) => String(part)).join(" ").trim();
|
|
@@ -1225,6 +1258,7 @@ export async function resolveResumeContext(backend, sessionId, options = {}) {
|
|
|
1225
1258
|
|
|
1226
1259
|
export async function bootstrapResumeContextForFire({
|
|
1227
1260
|
backend,
|
|
1261
|
+
configFile,
|
|
1228
1262
|
resumeSessionId,
|
|
1229
1263
|
env = process.env,
|
|
1230
1264
|
resolveResumeContextFn = resolveResumeContext,
|
|
@@ -1247,10 +1281,11 @@ export async function bootstrapResumeContextForFire({
|
|
|
1247
1281
|
return { resumeContext, runtimeProjectPath };
|
|
1248
1282
|
}
|
|
1249
1283
|
|
|
1250
|
-
resumeContext = await resolveResumeContextFn(backend, resumeSessionId
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1284
|
+
resumeContext = await resolveResumeContextFn(backend, resumeSessionId, {
|
|
1285
|
+
configFilePath: configFile,
|
|
1286
|
+
});
|
|
1287
|
+
const sessionLocation = resumeContext.sessionPath ? ` at ${resumeContext.sessionPath}` : "";
|
|
1288
|
+
logger(`Validated --resume ${resumeContext.sessionId} (${resumeContext.provider})${sessionLocation}`);
|
|
1254
1289
|
logger(`Resume will run backend from ${resumeContext.cwd}`);
|
|
1255
1290
|
runtimeProjectPath = await applyWorkingDirectoryFn(resumeContext.cwd);
|
|
1256
1291
|
logger(`Switched working directory to ${runtimeProjectPath} before Conductor connect`);
|
|
@@ -1459,10 +1494,24 @@ export class BridgeRunner {
|
|
|
1459
1494
|
const fallbackSessionId = this.resumeSessionId;
|
|
1460
1495
|
const sessionId = discoveredSessionId || fallbackSessionId;
|
|
1461
1496
|
const sessionFilePath = sessionInfo?.sessionFilePath ? String(sessionInfo.sessionFilePath).trim() : "";
|
|
1497
|
+
const sessionModel =
|
|
1498
|
+
sessionInfo?.model && String(sessionInfo.model).trim()
|
|
1499
|
+
? String(sessionInfo.model).trim()
|
|
1500
|
+
: this.backendSession.threadOptions?.model || this.backendName;
|
|
1501
|
+
const sessionModelProvider =
|
|
1502
|
+
sessionInfo?.modelProvider && String(sessionInfo.modelProvider).trim()
|
|
1503
|
+
? String(sessionInfo.modelProvider).trim()
|
|
1504
|
+
: this.backendSession.threadOptions?.modelProvider || undefined;
|
|
1462
1505
|
const hasRealSessionId = Boolean(sessionId);
|
|
1506
|
+
const messageSuffix = [
|
|
1507
|
+
sessionModel ? `model=${sessionModel}` : "",
|
|
1508
|
+
sessionModelProvider ? `provider=${sessionModelProvider}` : "",
|
|
1509
|
+
]
|
|
1510
|
+
.filter(Boolean)
|
|
1511
|
+
.join(" ");
|
|
1463
1512
|
const message = hasRealSessionId
|
|
1464
|
-
? `${this.backendName} session started: ${sessionId}`
|
|
1465
|
-
: `${this.backendName} session started`;
|
|
1513
|
+
? `${this.backendName} session started: ${sessionId}${messageSuffix ? ` (${messageSuffix})` : ""}`
|
|
1514
|
+
: `${this.backendName} session started${messageSuffix ? ` (${messageSuffix})` : ""}`;
|
|
1466
1515
|
if (hasRealSessionId) {
|
|
1467
1516
|
await this.persistTaskSessionBinding({
|
|
1468
1517
|
sessionId,
|
|
@@ -1472,6 +1521,8 @@ export class BridgeRunner {
|
|
|
1472
1521
|
try {
|
|
1473
1522
|
await this.conductor.sendMessage(this.taskId, message, {
|
|
1474
1523
|
backend: this.backendName,
|
|
1524
|
+
model: sessionModel || undefined,
|
|
1525
|
+
model_provider: sessionModelProvider || undefined,
|
|
1475
1526
|
thread_id: hasRealSessionId ? sessionId : undefined,
|
|
1476
1527
|
session_id: hasRealSessionId ? sessionId : undefined,
|
|
1477
1528
|
session_file_path: sessionFilePath || undefined,
|
|
@@ -1897,7 +1948,7 @@ export class BridgeRunner {
|
|
|
1897
1948
|
this.copilotLog(
|
|
1898
1949
|
`runtime replyTo=${replyTo || "latest"} state=${runtime.state || ""} phase=${runtime.phase || ""} inProgress=${Boolean(
|
|
1899
1950
|
runtime.reply_in_progress,
|
|
1900
|
-
)} status="${sanitizeForLog(runtime.status_line || "", 120)}" done="${sanitizeForLog(runtime.status_done_line || "", 120)}" preview="${sanitizeForLog(runtime.reply_preview || "", 120)}"`,
|
|
1951
|
+
)} status="${sanitizeForLog(runtime.status_line || "", 120)}" done="${sanitizeForLog(runtime.status_done_line || "", 120)}" preview="${sanitizeForLog(runtime.reply_preview || "", 120)}" model="${sanitizeForLog(this.backendSession.threadOptions?.model || this.backendName, 80)}" provider="${sanitizeForLog(this.backendSession.threadOptions?.modelProvider || this.backendName, 80)}"`,
|
|
1901
1952
|
);
|
|
1902
1953
|
|
|
1903
1954
|
try {
|
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.31",
|
|
4
|
+
"gitCommitId": "7e0bd83",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"conductor": "bin/conductor.js"
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@love-moon/ai-bridge": "0.1.4",
|
|
21
|
-
"@love-moon/ai-sdk": "0.2.
|
|
22
|
-
"@love-moon/conductor-sdk": "0.2.
|
|
21
|
+
"@love-moon/ai-sdk": "0.2.31",
|
|
22
|
+
"@love-moon/conductor-sdk": "0.2.31",
|
|
23
23
|
"chrome-launcher": "^1.2.1",
|
|
24
24
|
"chrome-remote-interface": "^0.33.0",
|
|
25
25
|
"dotenv": "^16.4.5",
|