@love-moon/conductor-cli 0.2.31 → 0.2.33
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 +233 -70
- package/package.json +4 -4
- package/src/daemon.js +837 -56
- package/src/fire/resume.js +67 -7
- package/src/runtime-backends.js +315 -4
package/bin/conductor-fire.js
CHANGED
|
@@ -19,7 +19,7 @@ import yargs from "yargs/yargs";
|
|
|
19
19
|
import { hideBin } from "yargs/helpers";
|
|
20
20
|
import yaml from "js-yaml";
|
|
21
21
|
import { createAiSession } from "@love-moon/ai-sdk";
|
|
22
|
-
import { ConductorClient, loadConfig } from "@love-moon/conductor-sdk";
|
|
22
|
+
import { ConductorClient, ProjectContext, loadConfig } from "@love-moon/conductor-sdk";
|
|
23
23
|
import {
|
|
24
24
|
buildResumeArgsForBackend as buildCliResumeArgsForBackend,
|
|
25
25
|
resolveResumeContext as resolveCliResumeContext,
|
|
@@ -28,8 +28,9 @@ import {
|
|
|
28
28
|
} from "../src/fire/resume.js";
|
|
29
29
|
import {
|
|
30
30
|
filterRuntimeSupportedAllowCliList,
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
isBuiltInRuntimeBackend,
|
|
32
|
+
listAdvertisedBackends,
|
|
33
|
+
resolveConfiguredRuntimeBackend,
|
|
33
34
|
normalizeRuntimeBackendAlias,
|
|
34
35
|
normalizeRuntimeBackendName,
|
|
35
36
|
} from "../src/runtime-backends.js";
|
|
@@ -66,6 +67,20 @@ export function buildConductorConnectHeaders(version = pkgJson.version) {
|
|
|
66
67
|
};
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
export function shouldRunReconnectRecovery({
|
|
71
|
+
isReconnect,
|
|
72
|
+
fireShuttingDown = false,
|
|
73
|
+
runner = null,
|
|
74
|
+
} = {}) {
|
|
75
|
+
if (!isReconnect || fireShuttingDown) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (!runner || typeof runner.shouldSuppressReconnectRecovery !== "function") {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
return !runner.shouldSuppressReconnectRecovery();
|
|
82
|
+
}
|
|
83
|
+
|
|
69
84
|
// Load allow_cli_list from config file (no defaults - must be configured)
|
|
70
85
|
async function loadAllowCliList(configFilePath) {
|
|
71
86
|
const home = os.homedir();
|
|
@@ -85,37 +100,46 @@ async function loadAllowCliList(configFilePath) {
|
|
|
85
100
|
return {};
|
|
86
101
|
}
|
|
87
102
|
|
|
88
|
-
export function resolveAiSessionCommandLine(backend, allowCliList, env = process.env) {
|
|
103
|
+
export function resolveAiSessionCommandLine(backend, allowCliList, env = process.env, sessionBackend = backend) {
|
|
89
104
|
const normalizedBackend = normalizeRuntimeBackendName(backend);
|
|
105
|
+
const normalizedSessionBackend = normalizeRuntimeBackendName(sessionBackend);
|
|
90
106
|
const envKeyByBackend = {
|
|
107
|
+
codex: "CONDUCTOR_CODEX_APP_SERVER_COMMAND",
|
|
91
108
|
opencode: "CONDUCTOR_OPENCODE_COMMAND",
|
|
92
109
|
kimi: "CONDUCTOR_KIMI_COMMAND",
|
|
93
110
|
};
|
|
94
|
-
const envKey = envKeyByBackend[
|
|
95
|
-
if (!envKey) {
|
|
96
|
-
return "";
|
|
97
|
-
}
|
|
111
|
+
const envKey = envKeyByBackend[normalizedSessionBackend];
|
|
98
112
|
|
|
99
|
-
const preferredEnvCommand = typeof env?.[envKey] === "string" ? env[envKey].trim() : "";
|
|
113
|
+
const preferredEnvCommand = envKey && typeof env?.[envKey] === "string" ? env[envKey].trim() : "";
|
|
100
114
|
if (preferredEnvCommand) {
|
|
101
115
|
return preferredEnvCommand;
|
|
102
116
|
}
|
|
103
117
|
|
|
104
118
|
const configuredCommand =
|
|
105
|
-
allowCliList && typeof allowCliList === "object"
|
|
106
|
-
? allowCliList[normalizedBackend]
|
|
119
|
+
allowCliList && typeof allowCliList === "object"
|
|
120
|
+
? typeof allowCliList[normalizedBackend] === "string"
|
|
121
|
+
? allowCliList[normalizedBackend].trim()
|
|
122
|
+
: typeof allowCliList[normalizedSessionBackend] === "string"
|
|
123
|
+
? allowCliList[normalizedSessionBackend].trim()
|
|
124
|
+
: ""
|
|
107
125
|
: "";
|
|
108
|
-
if (configuredCommand) {
|
|
109
|
-
return configuredCommand;
|
|
110
|
-
}
|
|
111
126
|
|
|
112
127
|
const daemonCommand =
|
|
113
128
|
typeof env?.CONDUCTOR_CLI_COMMAND === "string" ? env.CONDUCTOR_CLI_COMMAND.trim() : "";
|
|
114
|
-
|
|
115
|
-
|
|
129
|
+
|
|
130
|
+
const resolvedCommand = configuredCommand || daemonCommand;
|
|
131
|
+
if (!resolvedCommand) {
|
|
132
|
+
return "";
|
|
116
133
|
}
|
|
117
134
|
|
|
118
|
-
|
|
135
|
+
if (normalizedSessionBackend === "codex") {
|
|
136
|
+
if (/\bapp-server\b/.test(resolvedCommand)) {
|
|
137
|
+
return resolvedCommand;
|
|
138
|
+
}
|
|
139
|
+
return `${resolvedCommand} app-server --listen stdio://`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return resolvedCommand;
|
|
119
143
|
}
|
|
120
144
|
|
|
121
145
|
const DEFAULT_POLL_INTERVAL_MS = parseInt(
|
|
@@ -429,12 +453,15 @@ async function main() {
|
|
|
429
453
|
}
|
|
430
454
|
|
|
431
455
|
const allowCliList = await loadAllowCliList(cliArgs.configFile);
|
|
432
|
-
const supportedBackends =
|
|
433
|
-
|
|
434
|
-
|
|
456
|
+
const { supportedBackends, externalBackends, discoveryError } = await listAdvertisedBackends(allowCliList, {
|
|
457
|
+
configFilePath: cliArgs.configFile,
|
|
458
|
+
});
|
|
435
459
|
|
|
436
460
|
if (cliArgs.listBackends) {
|
|
437
461
|
if (supportedBackends.length === 0 && externalBackends.length === 0) {
|
|
462
|
+
if (discoveryError) {
|
|
463
|
+
throw discoveryError;
|
|
464
|
+
}
|
|
438
465
|
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`);
|
|
439
466
|
} else {
|
|
440
467
|
if (supportedBackends.length > 0) {
|
|
@@ -457,7 +484,7 @@ async function main() {
|
|
|
457
484
|
let resumeContext = null;
|
|
458
485
|
if (cliArgs.resumeSessionId) {
|
|
459
486
|
const bootstrap = await bootstrapResumeContextForFire({
|
|
460
|
-
backend: cliArgs.backend,
|
|
487
|
+
backend: cliArgs.sessionBackend || cliArgs.backend,
|
|
461
488
|
configFile: cliArgs.configFile,
|
|
462
489
|
resumeSessionId: cliArgs.resumeSessionId,
|
|
463
490
|
});
|
|
@@ -485,7 +512,13 @@ async function main() {
|
|
|
485
512
|
fireWatchdog.start();
|
|
486
513
|
|
|
487
514
|
const scheduleReconnectRecovery = ({ isReconnect }) => {
|
|
488
|
-
if (
|
|
515
|
+
if (
|
|
516
|
+
!shouldRunReconnectRecovery({
|
|
517
|
+
isReconnect,
|
|
518
|
+
fireShuttingDown,
|
|
519
|
+
runner: reconnectRunner,
|
|
520
|
+
})
|
|
521
|
+
) {
|
|
489
522
|
return;
|
|
490
523
|
}
|
|
491
524
|
log("Conductor connection restored");
|
|
@@ -591,6 +624,7 @@ async function main() {
|
|
|
591
624
|
requestedTitle: requestedTaskTitle,
|
|
592
625
|
backend: cliArgs.backend,
|
|
593
626
|
daemonName: configuredDaemonName,
|
|
627
|
+
projectPath: runtimeProjectPath,
|
|
594
628
|
});
|
|
595
629
|
injectResolvedTaskId(taskContext.taskId);
|
|
596
630
|
injectResolvedTaskId(taskContext.taskId, env);
|
|
@@ -621,9 +655,14 @@ async function main() {
|
|
|
621
655
|
|
|
622
656
|
const resolvedResumeSessionId = cliArgs.resumeSessionId;
|
|
623
657
|
|
|
624
|
-
const sessionCommandLine = resolveAiSessionCommandLine(
|
|
658
|
+
const sessionCommandLine = resolveAiSessionCommandLine(
|
|
659
|
+
cliArgs.backend,
|
|
660
|
+
allowCliList,
|
|
661
|
+
process.env,
|
|
662
|
+
cliArgs.sessionBackend,
|
|
663
|
+
);
|
|
625
664
|
|
|
626
|
-
backendSession = createAiSession(cliArgs.backend, {
|
|
665
|
+
backendSession = createAiSession(cliArgs.sessionBackend || cliArgs.backend, {
|
|
627
666
|
initialImages: cliArgs.initialImages,
|
|
628
667
|
cwd: runtimeProjectPath,
|
|
629
668
|
resumeSessionId: resolvedResumeSessionId,
|
|
@@ -708,8 +747,8 @@ async function main() {
|
|
|
708
747
|
|
|
709
748
|
let runnerError = null;
|
|
710
749
|
try {
|
|
711
|
-
if (!resolvedResumeSessionId && String(cliArgs.backend).trim().toLowerCase() === "codex") {
|
|
712
|
-
await withFreshSessionBootstrapLock(cliArgs.backend, runtimeProjectPath, async () => {
|
|
750
|
+
if (!resolvedResumeSessionId && String(cliArgs.sessionBackend || cliArgs.backend).trim().toLowerCase() === "codex") {
|
|
751
|
+
await withFreshSessionBootstrapLock(cliArgs.sessionBackend || cliArgs.backend, runtimeProjectPath, async () => {
|
|
713
752
|
await runner.announceBackendSession();
|
|
714
753
|
});
|
|
715
754
|
}
|
|
@@ -742,7 +781,13 @@ async function main() {
|
|
|
742
781
|
summary: "conductor fire exited",
|
|
743
782
|
};
|
|
744
783
|
try {
|
|
745
|
-
await conductor.sendTaskStatus(taskContext.taskId, finalStatus);
|
|
784
|
+
const statusResult = await conductor.sendTaskStatus(taskContext.taskId, finalStatus);
|
|
785
|
+
if (statusResult?.pending && typeof conductor.flushPendingUpstreamEvents === "function") {
|
|
786
|
+
await conductor.flushPendingUpstreamEvents({
|
|
787
|
+
timeoutMs: 5_000,
|
|
788
|
+
retryIntervalMs: 250,
|
|
789
|
+
});
|
|
790
|
+
}
|
|
746
791
|
} catch (error) {
|
|
747
792
|
log(`Failed to report task status (${finalStatus.status}): ${error?.message || error}`);
|
|
748
793
|
}
|
|
@@ -864,9 +909,9 @@ export async function parseCliArgs(argvInput = process.argv) {
|
|
|
864
909
|
|
|
865
910
|
const configFileFromArgs = extractConfigFileFromArgv(argv);
|
|
866
911
|
const allowCliList = await loadAllowCliList(configFileFromArgs);
|
|
867
|
-
const supportedBackends =
|
|
868
|
-
|
|
869
|
-
|
|
912
|
+
const { supportedBackends, externalBackends, discoveryError } = await listAdvertisedBackends(allowCliList, {
|
|
913
|
+
configFilePath: configFileFromArgs,
|
|
914
|
+
});
|
|
870
915
|
|
|
871
916
|
const conductorArgs = yargs(conductorArgv)
|
|
872
917
|
.scriptName(CLI_NAME)
|
|
@@ -968,21 +1013,36 @@ Environment:
|
|
|
968
1013
|
})
|
|
969
1014
|
.parse();
|
|
970
1015
|
|
|
971
|
-
const
|
|
972
|
-
?
|
|
1016
|
+
const requestedBackend = conductorArgs.backend
|
|
1017
|
+
? normalizeRuntimeBackendName(conductorArgs.backend)
|
|
973
1018
|
: supportedBackends[0] || externalBackends[0];
|
|
1019
|
+
const configuredBackend = await resolveConfiguredRuntimeBackend(requestedBackend, allowCliList, {
|
|
1020
|
+
configFilePath: configFileFromArgs,
|
|
1021
|
+
});
|
|
1022
|
+
const backend = configuredBackend?.requestedBackend || requestedBackend;
|
|
1023
|
+
const sessionBackend =
|
|
1024
|
+
configuredBackend?.runtimeBackend ||
|
|
1025
|
+
(backend ? await normalizeRuntimeBackendAlias(backend, { configFilePath: configFileFromArgs }) : "");
|
|
974
1026
|
const shouldRequireBackend =
|
|
975
1027
|
!Boolean(conductorArgs.listBackends) &&
|
|
976
1028
|
!listBackendsWithoutSeparator &&
|
|
977
1029
|
!Boolean(conductorArgs.version) &&
|
|
978
1030
|
!versionWithoutSeparator;
|
|
979
|
-
const runtimeSupportedBackends = new Set(
|
|
980
|
-
|
|
1031
|
+
const runtimeSupportedBackends = new Set(supportedBackends);
|
|
1032
|
+
const advertisedExternalBackends = new Set(externalBackends);
|
|
1033
|
+
const hasConfiguredEntry = Boolean(configuredBackend?.commandLine);
|
|
1034
|
+
const isAllowedExternalBackend =
|
|
1035
|
+
!isBuiltInRuntimeBackend(sessionBackend) &&
|
|
1036
|
+
advertisedExternalBackends.has(sessionBackend);
|
|
1037
|
+
if (backend && shouldRequireBackend && !hasConfiguredEntry && !isAllowedExternalBackend) {
|
|
981
1038
|
throw new Error(
|
|
982
1039
|
`Unsupported backend "${backend}". Supported backends: ${[...runtimeSupportedBackends].join(", ") || "none configured"}.`,
|
|
983
1040
|
);
|
|
984
1041
|
}
|
|
985
1042
|
if (!backend && shouldRequireBackend) {
|
|
1043
|
+
if (discoveryError) {
|
|
1044
|
+
throw discoveryError;
|
|
1045
|
+
}
|
|
986
1046
|
throw new Error("No supported backends configured. Add allow_cli_list entries or set AISDK_PROVIDER_PATH for external providers.");
|
|
987
1047
|
}
|
|
988
1048
|
|
|
@@ -1010,6 +1070,7 @@ Environment:
|
|
|
1010
1070
|
taskTitle: typeof conductorArgs.title === "string" ? conductorArgs.title.trim() : "",
|
|
1011
1071
|
hasExplicitTaskTitle: typeof conductorArgs.title === "string" && Boolean(conductorArgs.title.trim()),
|
|
1012
1072
|
configFile: conductorArgs.configFile,
|
|
1073
|
+
sessionBackend,
|
|
1013
1074
|
resumeSessionId,
|
|
1014
1075
|
showVersion: Boolean(conductorArgs.version) || versionWithoutSeparator,
|
|
1015
1076
|
listBackends: Boolean(conductorArgs.listBackends) || listBackendsWithoutSeparator,
|
|
@@ -1126,7 +1187,10 @@ async function ensureTaskContext(conductor, opts) {
|
|
|
1126
1187
|
};
|
|
1127
1188
|
}
|
|
1128
1189
|
|
|
1129
|
-
const projectId = await resolveProjectId(conductor, opts.requestedProjectId
|
|
1190
|
+
const projectId = await resolveProjectId(conductor, opts.requestedProjectId, {
|
|
1191
|
+
daemonName: opts.daemonName,
|
|
1192
|
+
projectPath: opts.projectPath,
|
|
1193
|
+
});
|
|
1130
1194
|
const payload = {
|
|
1131
1195
|
project_id: projectId,
|
|
1132
1196
|
task_title: deriveTaskTitle(opts.initialPrompt, opts.requestedTitle, opts.backend),
|
|
@@ -1141,15 +1205,6 @@ async function ensureTaskContext(conductor, opts) {
|
|
|
1141
1205
|
|
|
1142
1206
|
const session = await conductor.createTaskSession(payload);
|
|
1143
1207
|
|
|
1144
|
-
// Auto-bind current path to project if not already bound
|
|
1145
|
-
try {
|
|
1146
|
-
await conductor.bindProjectPath(projectId);
|
|
1147
|
-
log(`Bound current path to project ${projectId}`);
|
|
1148
|
-
} catch (error) {
|
|
1149
|
-
// Ignore binding errors - it's not critical
|
|
1150
|
-
log(`Note: Could not bind path to project: ${error.message}`);
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
1208
|
return {
|
|
1154
1209
|
taskId: session.task_id,
|
|
1155
1210
|
appUrl: session.app_url || null,
|
|
@@ -1158,51 +1213,155 @@ async function ensureTaskContext(conductor, opts) {
|
|
|
1158
1213
|
};
|
|
1159
1214
|
}
|
|
1160
1215
|
|
|
1161
|
-
async function resolveProjectId(conductor, explicit) {
|
|
1216
|
+
export async function resolveProjectId(conductor, explicit, opts = {}) {
|
|
1162
1217
|
if (explicit) {
|
|
1163
1218
|
return explicit;
|
|
1164
1219
|
}
|
|
1165
1220
|
|
|
1166
|
-
|
|
1221
|
+
const daemonHost = resolveDaemonHost(opts.daemonName);
|
|
1222
|
+
const projectPath = typeof opts.projectPath === "string" && opts.projectPath.trim() ? opts.projectPath.trim() : process.cwd();
|
|
1223
|
+
|
|
1224
|
+
if (!daemonHost) {
|
|
1225
|
+
return resolveDefaultProjectId(conductor);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
const exists = await isExistingDirectory(projectPath);
|
|
1229
|
+
if (!exists) {
|
|
1230
|
+
throw new Error(`Workspace path does not exist: ${projectPath}`);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
const snapshot = resolveWorkspaceSnapshot(projectPath);
|
|
1234
|
+
const projectName = deriveProjectName(snapshot);
|
|
1235
|
+
|
|
1167
1236
|
try {
|
|
1168
|
-
const matchResult = await conductor.matchProjectByPath(
|
|
1237
|
+
const matchResult = await conductor.matchProjectByPath({
|
|
1238
|
+
daemon_host: daemonHost,
|
|
1239
|
+
project_path: snapshot.projectRoot,
|
|
1240
|
+
});
|
|
1169
1241
|
if (matchResult?.project_id) {
|
|
1170
1242
|
log(`Matched project ${matchResult.project_name || matchResult.project_id} by path ${matchResult.matched_path}`);
|
|
1171
|
-
|
|
1243
|
+
let resolvedProjectId = matchResult.project_id;
|
|
1244
|
+
try {
|
|
1245
|
+
const bindResult = await conductor.bindProjectPath(matchResult.project_id, {
|
|
1246
|
+
daemon_host: daemonHost,
|
|
1247
|
+
project_path: snapshot.projectRoot,
|
|
1248
|
+
});
|
|
1249
|
+
if (typeof bindResult?.project_id === "string" && bindResult.project_id.trim()) {
|
|
1250
|
+
resolvedProjectId = bindResult.project_id.trim();
|
|
1251
|
+
}
|
|
1252
|
+
} catch (error) {
|
|
1253
|
+
log(`Unable to backfill bound workspace path: ${error.message}`);
|
|
1254
|
+
try {
|
|
1255
|
+
const rebound = await conductor.matchProjectByPath({
|
|
1256
|
+
daemon_host: daemonHost,
|
|
1257
|
+
project_path: snapshot.projectRoot,
|
|
1258
|
+
});
|
|
1259
|
+
if (rebound?.project_id) {
|
|
1260
|
+
resolvedProjectId = rebound.project_id;
|
|
1261
|
+
}
|
|
1262
|
+
} catch {
|
|
1263
|
+
// ignore retry match failures
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
return resolvedProjectId;
|
|
1172
1267
|
}
|
|
1173
1268
|
} catch (error) {
|
|
1174
1269
|
log(`Unable to match project by path: ${error.message}`);
|
|
1175
1270
|
}
|
|
1176
1271
|
|
|
1177
1272
|
try {
|
|
1178
|
-
const
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
return record.project_id;
|
|
1192
|
-
}
|
|
1273
|
+
const created = await conductor.createProject({
|
|
1274
|
+
name: projectName,
|
|
1275
|
+
bindingConfirmed: true,
|
|
1276
|
+
daemonHost,
|
|
1277
|
+
workspacePath: snapshot.projectRoot,
|
|
1278
|
+
repoRoot: snapshot.repoRoot,
|
|
1279
|
+
worktreeBranch: snapshot.worktreeBranch,
|
|
1280
|
+
lastCommit: snapshot.lastCommit,
|
|
1281
|
+
fileCount: snapshot.fileCount,
|
|
1282
|
+
});
|
|
1283
|
+
if (created?.id) {
|
|
1284
|
+
log(`Created bound project ${created.name || created.id} for ${daemonHost}:${snapshot.projectRoot}`);
|
|
1285
|
+
return created.id;
|
|
1193
1286
|
}
|
|
1287
|
+
throw new Error("create_project returned no id");
|
|
1194
1288
|
} catch (error) {
|
|
1195
|
-
log(`Unable to
|
|
1289
|
+
log(`Unable to create bound project: ${error.message}`);
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
try {
|
|
1293
|
+
const retryMatch = await conductor.matchProjectByPath({
|
|
1294
|
+
daemon_host: daemonHost,
|
|
1295
|
+
project_path: snapshot.projectRoot,
|
|
1296
|
+
});
|
|
1297
|
+
if (retryMatch?.project_id) {
|
|
1298
|
+
return retryMatch.project_id;
|
|
1299
|
+
}
|
|
1300
|
+
} catch {
|
|
1301
|
+
// ignore retry match failures
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
log(`Unable to resolve bound project for ${daemonHost}:${snapshot.projectRoot}, falling back to default`);
|
|
1305
|
+
return resolveDefaultProjectId(conductor);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
function resolveDaemonHost(daemonName) {
|
|
1309
|
+
if (typeof daemonName === "string" && daemonName.trim()) {
|
|
1310
|
+
return daemonName.trim();
|
|
1311
|
+
}
|
|
1312
|
+
const fromEnv = typeof process.env.CONDUCTOR_DAEMON_NAME === "string" ? process.env.CONDUCTOR_DAEMON_NAME.trim() : "";
|
|
1313
|
+
if (fromEnv) {
|
|
1314
|
+
return fromEnv;
|
|
1196
1315
|
}
|
|
1316
|
+
const fromAgent = typeof process.env.CONDUCTOR_AGENT_NAME === "string" ? process.env.CONDUCTOR_AGENT_NAME.trim() : "";
|
|
1317
|
+
if (fromAgent) {
|
|
1318
|
+
return fromAgent;
|
|
1319
|
+
}
|
|
1320
|
+
try {
|
|
1321
|
+
return os.hostname();
|
|
1322
|
+
} catch {
|
|
1323
|
+
return "";
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
function resolveWorkspaceSnapshot(projectPath) {
|
|
1328
|
+
try {
|
|
1329
|
+
const context = new ProjectContext(projectPath);
|
|
1330
|
+
return context.snapshot();
|
|
1331
|
+
} catch {
|
|
1332
|
+
return {
|
|
1333
|
+
projectRoot: path.resolve(projectPath),
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
function deriveProjectName(snapshot) {
|
|
1339
|
+
const basePath = snapshot.repoRoot || snapshot.projectRoot;
|
|
1340
|
+
const name = basePath ? path.basename(basePath) : "";
|
|
1341
|
+
const baseName = name || "New Project";
|
|
1342
|
+
const digest = createHash("sha1").update(basePath || baseName).digest("hex").slice(0, 8);
|
|
1343
|
+
return `${baseName}-${digest}`;
|
|
1344
|
+
}
|
|
1197
1345
|
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1346
|
+
async function resolveDefaultProjectId(conductor) {
|
|
1347
|
+
try {
|
|
1348
|
+
const listing = await conductor.listProjects();
|
|
1349
|
+
const defaultProject = Array.isArray(listing?.projects)
|
|
1350
|
+
? listing.projects.find((project) => Boolean(project?.isDefault))
|
|
1351
|
+
: null;
|
|
1352
|
+
if (defaultProject?.id) {
|
|
1353
|
+
return defaultProject.id;
|
|
1354
|
+
}
|
|
1355
|
+
} catch {
|
|
1356
|
+
// ignore list failures
|
|
1202
1357
|
}
|
|
1203
|
-
|
|
1358
|
+
|
|
1359
|
+
log("No bound daemon available; creating default project...");
|
|
1204
1360
|
try {
|
|
1205
|
-
const created = await conductor.createProject(
|
|
1361
|
+
const created = await conductor.createProject({
|
|
1362
|
+
name: "Default Project",
|
|
1363
|
+
isDefault: true,
|
|
1364
|
+
});
|
|
1206
1365
|
if (created?.id) {
|
|
1207
1366
|
log(`Created default project ${created.id}`);
|
|
1208
1367
|
return created.id;
|
|
@@ -1644,6 +1803,10 @@ export class BridgeRunner {
|
|
|
1644
1803
|
this.needsReconnectRecovery = true;
|
|
1645
1804
|
}
|
|
1646
1805
|
|
|
1806
|
+
shouldSuppressReconnectRecovery() {
|
|
1807
|
+
return this.stopped || Boolean(this.remoteStopInfo);
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1647
1810
|
getRemoteStopSummary() {
|
|
1648
1811
|
if (!this.remoteStopInfo) {
|
|
1649
1812
|
return null;
|
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.33",
|
|
4
|
+
"gitCommitId": "db7f9bf",
|
|
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.33",
|
|
22
|
+
"@love-moon/conductor-sdk": "0.2.33",
|
|
23
23
|
"chrome-launcher": "^1.2.1",
|
|
24
24
|
"chrome-remote-interface": "^0.33.0",
|
|
25
25
|
"dotenv": "^16.4.5",
|