@love-moon/conductor-cli 0.5.1 → 0.6.1
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/CHANGELOG.md +19 -0
- package/package.json +5 -5
- package/src/daemon.js +125 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @love-moon/conductor-cli
|
|
2
2
|
|
|
3
|
+
## 0.6.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [650fc55]
|
|
8
|
+
- @love-moon/ai-sdk@0.6.1
|
|
9
|
+
- @love-moon/conductor-sdk@0.6.1
|
|
10
|
+
|
|
11
|
+
## 0.6.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- bcc80b5: Initialize Git submodules automatically when preparing task worktrees.
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- @love-moon/conductor-sdk@0.6.0
|
|
20
|
+
- @love-moon/ai-sdk@0.6.0
|
|
21
|
+
|
|
3
22
|
## 0.5.1
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@love-moon/conductor-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"gitCommitId": "
|
|
3
|
+
"version": "0.6.1",
|
|
4
|
+
"gitCommitId": "707fffe",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/lovemoon-ai/conductor.git"
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"test": "node --test test/*.test.js"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@love-moon/ai-sdk": "0.
|
|
27
|
-
"@love-moon/conductor-sdk": "0.
|
|
26
|
+
"@love-moon/ai-sdk": "0.6.1",
|
|
27
|
+
"@love-moon/conductor-sdk": "0.6.1",
|
|
28
28
|
"@github/copilot-sdk": "^0.3.0",
|
|
29
29
|
"chrome-launcher": "^1.2.1",
|
|
30
30
|
"chrome-remote-interface": "^0.33.0",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"optionalDependencies": {
|
|
39
39
|
"@roamhq/wrtc": "^0.10.0",
|
|
40
|
-
"@love-moon/chat-web": "0.
|
|
40
|
+
"@love-moon/chat-web": "0.6.1"
|
|
41
41
|
},
|
|
42
42
|
"pnpm": {
|
|
43
43
|
"onlyBuiltDependencies": [
|
package/src/daemon.js
CHANGED
|
@@ -1256,6 +1256,8 @@ export function startDaemon(config = {}, deps = {}) {
|
|
|
1256
1256
|
"",
|
|
1257
1257
|
"worktree:",
|
|
1258
1258
|
" sync_branch: false",
|
|
1259
|
+
" sync_submodules: true",
|
|
1260
|
+
" # When .gitmodules exists, initialize/update submodules in task worktrees.",
|
|
1259
1261
|
" symlink: []",
|
|
1260
1262
|
" # Example: symlink paths from the parent workspace into each worktree",
|
|
1261
1263
|
" # symlink:",
|
|
@@ -1264,6 +1266,72 @@ export function startDaemon(config = {}, deps = {}) {
|
|
|
1264
1266
|
"",
|
|
1265
1267
|
].join("\n");
|
|
1266
1268
|
|
|
1269
|
+
const MAX_PROJECT_ICON_IMAGE_BYTES = 128 * 1024;
|
|
1270
|
+
const PROJECT_ICON_MIME_BY_EXTENSION = {
|
|
1271
|
+
".svg": "image/svg+xml",
|
|
1272
|
+
".png": "image/png",
|
|
1273
|
+
".jpg": "image/jpeg",
|
|
1274
|
+
".jpeg": "image/jpeg",
|
|
1275
|
+
".gif": "image/gif",
|
|
1276
|
+
".webp": "image/webp",
|
|
1277
|
+
".ico": "image/x-icon",
|
|
1278
|
+
".avif": "image/avif",
|
|
1279
|
+
};
|
|
1280
|
+
|
|
1281
|
+
function getProjectSettingsCandidates(projectWorkspacePath) {
|
|
1282
|
+
return [
|
|
1283
|
+
path.join(projectWorkspacePath, ".conductor", "settings.yaml"),
|
|
1284
|
+
path.join(projectWorkspacePath, ".conductor", "settings.yml"),
|
|
1285
|
+
path.join(projectWorkspacePath, ".conductor", "setttings.yaml"),
|
|
1286
|
+
path.join(projectWorkspacePath, ".conductor", "setttings.yml"),
|
|
1287
|
+
];
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
function extractProjectIconFromSettings(parsed) {
|
|
1291
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1292
|
+
return null;
|
|
1293
|
+
}
|
|
1294
|
+
const direct = normalizeOptionalString(parsed.icon);
|
|
1295
|
+
if (direct) return direct;
|
|
1296
|
+
const projectSettings = parsed.project;
|
|
1297
|
+
if (
|
|
1298
|
+
projectSettings &&
|
|
1299
|
+
typeof projectSettings === "object" &&
|
|
1300
|
+
!Array.isArray(projectSettings)
|
|
1301
|
+
) {
|
|
1302
|
+
return normalizeOptionalString(projectSettings.icon);
|
|
1303
|
+
}
|
|
1304
|
+
return null;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
function projectIconLooksLikeFilesystemPath(iconValue) {
|
|
1308
|
+
if (/^(https?:\/\/|data:)/i.test(iconValue)) return false;
|
|
1309
|
+
if (iconValue.startsWith("/") || iconValue.startsWith("./") || iconValue.startsWith("../")) {
|
|
1310
|
+
return true;
|
|
1311
|
+
}
|
|
1312
|
+
if (!iconValue.includes("/")) return false;
|
|
1313
|
+
const ext = path.extname(iconValue).toLowerCase();
|
|
1314
|
+
return Object.prototype.hasOwnProperty.call(PROJECT_ICON_MIME_BY_EXTENSION, ext);
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
function readProjectIconAsDataUri(settingsDir, iconValue) {
|
|
1318
|
+
const resolvedPath = path.isAbsolute(iconValue)
|
|
1319
|
+
? iconValue
|
|
1320
|
+
: path.resolve(settingsDir, iconValue);
|
|
1321
|
+
const ext = path.extname(resolvedPath).toLowerCase();
|
|
1322
|
+
const mime = PROJECT_ICON_MIME_BY_EXTENSION[ext];
|
|
1323
|
+
if (!mime) return null;
|
|
1324
|
+
try {
|
|
1325
|
+
const stat = statSyncFn(resolvedPath);
|
|
1326
|
+
if (!stat.isFile() || stat.size === 0 || stat.size > MAX_PROJECT_ICON_IMAGE_BYTES) {
|
|
1327
|
+
return null;
|
|
1328
|
+
}
|
|
1329
|
+
return `data:${mime};base64,${readFileSyncFn(resolvedPath).toString("base64")}`;
|
|
1330
|
+
} catch {
|
|
1331
|
+
return null;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1267
1335
|
function ensureProjectSettingsTemplate(projectWorkspacePath) {
|
|
1268
1336
|
const settingsPath = path.join(projectWorkspacePath, ".conductor", "settings.yaml");
|
|
1269
1337
|
if (existsSyncFn(settingsPath)) {
|
|
@@ -1277,15 +1345,28 @@ export function startDaemon(config = {}, deps = {}) {
|
|
|
1277
1345
|
}
|
|
1278
1346
|
}
|
|
1279
1347
|
|
|
1280
|
-
function
|
|
1281
|
-
const
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1348
|
+
function readProjectIconSetting(projectWorkspacePath) {
|
|
1349
|
+
for (const settingsPath of getProjectSettingsCandidates(projectWorkspacePath)) {
|
|
1350
|
+
if (!existsSyncFn(settingsPath)) {
|
|
1351
|
+
continue;
|
|
1352
|
+
}
|
|
1353
|
+
let rawIcon = null;
|
|
1354
|
+
try {
|
|
1355
|
+
rawIcon = extractProjectIconFromSettings(yaml.load(readFileSyncFn(settingsPath, "utf8")));
|
|
1356
|
+
} catch {
|
|
1357
|
+
return null;
|
|
1358
|
+
}
|
|
1359
|
+
if (!rawIcon) return null;
|
|
1360
|
+
if (!projectIconLooksLikeFilesystemPath(rawIcon)) {
|
|
1361
|
+
return rawIcon;
|
|
1362
|
+
}
|
|
1363
|
+
return readProjectIconAsDataUri(path.dirname(settingsPath), rawIcon);
|
|
1364
|
+
}
|
|
1365
|
+
return null;
|
|
1366
|
+
}
|
|
1287
1367
|
|
|
1288
|
-
|
|
1368
|
+
function readProjectWorktreeSettings(projectWorkspacePath) {
|
|
1369
|
+
for (const settingsPath of getProjectSettingsCandidates(projectWorkspacePath)) {
|
|
1289
1370
|
if (!existsSyncFn(settingsPath)) {
|
|
1290
1371
|
continue;
|
|
1291
1372
|
}
|
|
@@ -1299,6 +1380,7 @@ export function startDaemon(config = {}, deps = {}) {
|
|
|
1299
1380
|
return {
|
|
1300
1381
|
symlinkPaths: normalizeConfiguredPathList(worktreeSettings.symlink, projectWorkspacePath),
|
|
1301
1382
|
syncBranch: worktreeSettings.sync_branch === true || worktreeSettings.syncBranch === true,
|
|
1383
|
+
syncSubmodules: worktreeSettings.sync_submodules !== false && worktreeSettings.syncSubmodules !== false,
|
|
1302
1384
|
settingsPath,
|
|
1303
1385
|
};
|
|
1304
1386
|
} catch (error) {
|
|
@@ -1309,6 +1391,7 @@ export function startDaemon(config = {}, deps = {}) {
|
|
|
1309
1391
|
return {
|
|
1310
1392
|
symlinkPaths: [],
|
|
1311
1393
|
syncBranch: false,
|
|
1394
|
+
syncSubmodules: true,
|
|
1312
1395
|
settingsPath: null,
|
|
1313
1396
|
};
|
|
1314
1397
|
}
|
|
@@ -1378,6 +1461,28 @@ export function startDaemon(config = {}, deps = {}) {
|
|
|
1378
1461
|
}
|
|
1379
1462
|
}
|
|
1380
1463
|
|
|
1464
|
+
async function ensureTaskWorktreeSubmodules({ taskId, projectWorkspacePath, worktreeRoot }) {
|
|
1465
|
+
const { syncSubmodules } = readProjectWorktreeSettings(projectWorkspacePath);
|
|
1466
|
+
if (!syncSubmodules || !existsSyncFn(path.join(worktreeRoot, ".gitmodules"))) {
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
try {
|
|
1471
|
+
await runSpawnProcess(
|
|
1472
|
+
"git",
|
|
1473
|
+
["-C", worktreeRoot, "submodule", "sync", "--recursive"],
|
|
1474
|
+
{ cwd: worktreeRoot, timeoutMs: WORKTREE_SUBMODULE_SYNC_TIMEOUT_MS },
|
|
1475
|
+
);
|
|
1476
|
+
await runSpawnProcess(
|
|
1477
|
+
"git",
|
|
1478
|
+
["-C", worktreeRoot, "submodule", "update", "--init", "--recursive"],
|
|
1479
|
+
{ cwd: worktreeRoot, timeoutMs: WORKTREE_SUBMODULE_SYNC_TIMEOUT_MS },
|
|
1480
|
+
);
|
|
1481
|
+
} catch (error) {
|
|
1482
|
+
throw new Error(`Failed to sync git submodules for ${taskId}: ${error?.message || error}`);
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1381
1486
|
async function runSpawnProcess(command, args, options = {}) {
|
|
1382
1487
|
let child;
|
|
1383
1488
|
const { timeoutMs, ...spawnOptions } = options || {};
|
|
@@ -1566,6 +1671,11 @@ export function startDaemon(config = {}, deps = {}) {
|
|
|
1566
1671
|
}
|
|
1567
1672
|
}
|
|
1568
1673
|
|
|
1674
|
+
await ensureTaskWorktreeSubmodules({
|
|
1675
|
+
taskId,
|
|
1676
|
+
projectWorkspacePath: worktreeConfig.projectWorkspacePath,
|
|
1677
|
+
worktreeRoot,
|
|
1678
|
+
});
|
|
1569
1679
|
mkdirSyncFn(finalCwd, { recursive: true });
|
|
1570
1680
|
await ensureTaskWorktreeSymlinks({
|
|
1571
1681
|
projectRepoRoot: worktreeConfig.projectRepoRoot,
|
|
@@ -1601,6 +1711,10 @@ export function startDaemon(config = {}, deps = {}) {
|
|
|
1601
1711
|
process.env.CONDUCTOR_WORKTREE_SYNC_TIMEOUT_MS,
|
|
1602
1712
|
5_000,
|
|
1603
1713
|
);
|
|
1714
|
+
const WORKTREE_SUBMODULE_SYNC_TIMEOUT_MS = parsePositiveInt(
|
|
1715
|
+
process.env.CONDUCTOR_WORKTREE_SUBMODULE_SYNC_TIMEOUT_MS,
|
|
1716
|
+
120_000,
|
|
1717
|
+
);
|
|
1604
1718
|
const SHUTDOWN_STATUS_REPORT_TIMEOUT_MS = parsePositiveInt(
|
|
1605
1719
|
process.env.CONDUCTOR_SHUTDOWN_STATUS_REPORT_TIMEOUT_MS,
|
|
1606
1720
|
1000,
|
|
@@ -4338,6 +4452,7 @@ export function startDaemon(config = {}, deps = {}) {
|
|
|
4338
4452
|
lastCommit: null,
|
|
4339
4453
|
lastCommitAt: null,
|
|
4340
4454
|
fileCount: null,
|
|
4455
|
+
icon: null,
|
|
4341
4456
|
error: null,
|
|
4342
4457
|
errorCode: null,
|
|
4343
4458
|
validatedAt,
|
|
@@ -4391,6 +4506,7 @@ export function startDaemon(config = {}, deps = {}) {
|
|
|
4391
4506
|
typeof snapshot?.fileCount === "number" && Number.isInteger(snapshot.fileCount)
|
|
4392
4507
|
? snapshot.fileCount
|
|
4393
4508
|
: null,
|
|
4509
|
+
icon: readProjectIconSetting(effectiveWorkspace),
|
|
4394
4510
|
};
|
|
4395
4511
|
}
|
|
4396
4512
|
} catch (error) {
|
|
@@ -4414,6 +4530,7 @@ export function startDaemon(config = {}, deps = {}) {
|
|
|
4414
4530
|
last_commit_at: result.lastCommitAt,
|
|
4415
4531
|
git_remote_url: result.gitRemoteUrl,
|
|
4416
4532
|
file_count: result.fileCount,
|
|
4533
|
+
icon: result.icon,
|
|
4417
4534
|
error: result.error,
|
|
4418
4535
|
error_code: result.errorCode,
|
|
4419
4536
|
validated_at: result.validatedAt,
|