@reconcrap/boss-recommend-mcp 1.3.34 → 1.3.36
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/README.md +14 -7
- package/package.json +1 -1
- package/src/boss-chat.js +383 -120
- package/src/cli.js +34 -24
- package/src/test-boss-chat.js +242 -84
- package/vendor/boss-chat-cli/src/cli.js +160 -45
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { spawn } from 'node:child_process';
|
|
3
|
-
import { appendFile, mkdir } from 'node:fs/promises';
|
|
4
|
-
import
|
|
5
|
-
import
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { appendFile, mkdir } from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import process from 'node:process';
|
|
6
7
|
import * as readlineCore from 'node:readline';
|
|
7
8
|
import readline from 'node:readline/promises';
|
|
8
9
|
import util from 'node:util';
|
|
@@ -178,13 +179,14 @@ function parseTargetCount(value) {
|
|
|
178
179
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
179
180
|
}
|
|
180
181
|
|
|
181
|
-
function parseArgs(argv) {
|
|
182
|
-
const args = {
|
|
183
|
-
command: 'run',
|
|
184
|
-
profile: 'default',
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
182
|
+
function parseArgs(argv) {
|
|
183
|
+
const args = {
|
|
184
|
+
command: 'run',
|
|
185
|
+
profile: 'default',
|
|
186
|
+
dataDir: '',
|
|
187
|
+
dryRun: false,
|
|
188
|
+
noState: false,
|
|
189
|
+
json: false,
|
|
188
190
|
runId: '',
|
|
189
191
|
detachedWorker: false,
|
|
190
192
|
overrides: {
|
|
@@ -213,13 +215,17 @@ function parseArgs(argv) {
|
|
|
213
215
|
index += 1;
|
|
214
216
|
}
|
|
215
217
|
|
|
216
|
-
switch (name) {
|
|
217
|
-
case 'profile':
|
|
218
|
-
args.profile = value || args.profile;
|
|
219
|
-
break;
|
|
220
|
-
case '
|
|
221
|
-
|
|
222
|
-
|
|
218
|
+
switch (name) {
|
|
219
|
+
case 'profile':
|
|
220
|
+
args.profile = value || args.profile;
|
|
221
|
+
break;
|
|
222
|
+
case 'data-dir':
|
|
223
|
+
case 'dataDir':
|
|
224
|
+
args.dataDir = String(value || '').trim();
|
|
225
|
+
break;
|
|
226
|
+
case 'dry-run':
|
|
227
|
+
args.dryRun = true;
|
|
228
|
+
break;
|
|
223
229
|
case 'no-state':
|
|
224
230
|
args.noState = true;
|
|
225
231
|
break;
|
|
@@ -293,8 +299,86 @@ function parseArgs(argv) {
|
|
|
293
299
|
if (positionals.length > 0) {
|
|
294
300
|
args.command = positionals[0];
|
|
295
301
|
}
|
|
296
|
-
return args;
|
|
297
|
-
}
|
|
302
|
+
return args;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function isRootDirectory(targetPath) {
|
|
306
|
+
const resolved = path.resolve(String(targetPath || ''));
|
|
307
|
+
const parsed = path.parse(resolved);
|
|
308
|
+
return resolved.toLowerCase() === String(parsed.root || '').toLowerCase();
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function isSystemDirectoryPath(targetPath) {
|
|
312
|
+
const resolved = path.resolve(String(targetPath || ''));
|
|
313
|
+
const normalized = resolved.replace(/\\/g, '/').toLowerCase();
|
|
314
|
+
if (process.platform === 'win32') {
|
|
315
|
+
return (
|
|
316
|
+
normalized.endsWith('/windows')
|
|
317
|
+
|| normalized.endsWith('/windows/system32')
|
|
318
|
+
|| normalized.endsWith('/windows/syswow64')
|
|
319
|
+
|| normalized.endsWith('/program files')
|
|
320
|
+
|| normalized.endsWith('/program files (x86)')
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
return (
|
|
324
|
+
normalized === '/system'
|
|
325
|
+
|| normalized.startsWith('/system/')
|
|
326
|
+
|| normalized === '/usr'
|
|
327
|
+
|| normalized.startsWith('/usr/')
|
|
328
|
+
|| normalized === '/bin'
|
|
329
|
+
|| normalized.startsWith('/bin/')
|
|
330
|
+
|| normalized === '/sbin'
|
|
331
|
+
|| normalized.startsWith('/sbin/')
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function resolveDefaultDataDir(env = process.env) {
|
|
336
|
+
const stateHomeRaw = String(env?.BOSS_RECOMMEND_HOME || '').trim();
|
|
337
|
+
const stateHome = stateHomeRaw
|
|
338
|
+
? path.resolve(stateHomeRaw)
|
|
339
|
+
: path.join(os.homedir(), '.boss-recommend-mcp');
|
|
340
|
+
const source = stateHomeRaw
|
|
341
|
+
? 'default:env:BOSS_RECOMMEND_HOME'
|
|
342
|
+
: 'default:user_home';
|
|
343
|
+
return {
|
|
344
|
+
path: path.join(stateHome, 'boss-chat'),
|
|
345
|
+
source,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function resolveDataDirDetails(args = {}, env = process.env, cwd = process.cwd()) {
|
|
350
|
+
const explicit = String(args?.dataDir || '').trim();
|
|
351
|
+
if (explicit) {
|
|
352
|
+
return {
|
|
353
|
+
path: path.resolve(explicit),
|
|
354
|
+
source: 'arg:data-dir',
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
const fromEnv = String(env?.BOSS_CHAT_HOME || '').trim();
|
|
358
|
+
if (fromEnv) {
|
|
359
|
+
return {
|
|
360
|
+
path: path.resolve(fromEnv),
|
|
361
|
+
source: 'env:BOSS_CHAT_HOME',
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
return resolveDefaultDataDir(env);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function resolveDataDir(args = {}, env = process.env, cwd = process.cwd()) {
|
|
368
|
+
return resolveDataDirDetails(args, env, cwd).path;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function validateDataDir(targetPath) {
|
|
372
|
+
const resolved = path.resolve(String(targetPath || ''));
|
|
373
|
+
if (isRootDirectory(resolved) || isSystemDirectoryPath(resolved)) {
|
|
374
|
+
return {
|
|
375
|
+
ok: false,
|
|
376
|
+
code: 'UNSAFE_DATA_DIR',
|
|
377
|
+
message: `Refusing unsafe boss-chat data dir: ${resolved}. Please use --data-dir or BOSS_CHAT_HOME to a writable user directory.`,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
return { ok: true };
|
|
381
|
+
}
|
|
298
382
|
|
|
299
383
|
function printUsage() {
|
|
300
384
|
console.log('Usage: boss-chat <command> [options]');
|
|
@@ -308,11 +392,12 @@ function printUsage() {
|
|
|
308
392
|
console.log(' resume-run Resume paused async run');
|
|
309
393
|
console.log(' cancel-run Cancel async run');
|
|
310
394
|
console.log('');
|
|
311
|
-
console.log('Common options:');
|
|
312
|
-
console.log(' --profile <name> Profile name (default: default)');
|
|
313
|
-
console.log(' --
|
|
314
|
-
console.log(' --
|
|
315
|
-
console.log('');
|
|
395
|
+
console.log('Common options:');
|
|
396
|
+
console.log(' --profile <name> Profile name (default: default)');
|
|
397
|
+
console.log(' --data-dir <path> Runtime data dir (default: $BOSS_CHAT_HOME or ~/.boss-recommend-mcp/boss-chat)');
|
|
398
|
+
console.log(' --json JSON output for agent integration');
|
|
399
|
+
console.log(' --run-id <id> Target async run_id (for get/pause/resume/cancel)');
|
|
400
|
+
console.log('');
|
|
316
401
|
console.log('Run options:');
|
|
317
402
|
console.log(' --dry-run Evaluate and click, but do not request resume');
|
|
318
403
|
console.log(' --no-state Disable in-run candidate deduplication');
|
|
@@ -1511,10 +1596,30 @@ async function executeRunCommand(args, dataDir) {
|
|
|
1511
1596
|
}
|
|
1512
1597
|
}
|
|
1513
1598
|
|
|
1514
|
-
async function main() {
|
|
1515
|
-
const args = parseArgs(process.argv.slice(2));
|
|
1516
|
-
const
|
|
1517
|
-
|
|
1599
|
+
async function main() {
|
|
1600
|
+
const args = parseArgs(process.argv.slice(2));
|
|
1601
|
+
const resolvedDataDir = resolveDataDirDetails(args);
|
|
1602
|
+
const dataDir = resolvedDataDir.path;
|
|
1603
|
+
const dataDirValidation = validateDataDir(dataDir);
|
|
1604
|
+
if (!dataDirValidation.ok) {
|
|
1605
|
+
const failedPayload = {
|
|
1606
|
+
status: 'FAILED',
|
|
1607
|
+
error: {
|
|
1608
|
+
code: dataDirValidation.code,
|
|
1609
|
+
message: dataDirValidation.message,
|
|
1610
|
+
},
|
|
1611
|
+
data_dir: dataDir,
|
|
1612
|
+
data_dir_source: resolvedDataDir.source,
|
|
1613
|
+
};
|
|
1614
|
+
if (args.json) {
|
|
1615
|
+
console.log(JSON.stringify(failedPayload));
|
|
1616
|
+
} else {
|
|
1617
|
+
console.error(failedPayload.error.message);
|
|
1618
|
+
}
|
|
1619
|
+
process.exitCode = 1;
|
|
1620
|
+
return;
|
|
1621
|
+
}
|
|
1622
|
+
await mkdir(dataDir, { recursive: true });
|
|
1518
1623
|
|
|
1519
1624
|
if (args.command === 'help') {
|
|
1520
1625
|
printUsage();
|
|
@@ -1548,21 +1653,31 @@ async function main() {
|
|
|
1548
1653
|
break;
|
|
1549
1654
|
default:
|
|
1550
1655
|
printUsage();
|
|
1551
|
-
process.exitCode = 1;
|
|
1552
|
-
return;
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1656
|
+
process.exitCode = 1;
|
|
1657
|
+
return;
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
if (payload && typeof payload === 'object') {
|
|
1661
|
+
payload = {
|
|
1662
|
+
...payload,
|
|
1663
|
+
data_dir: dataDir,
|
|
1664
|
+
data_dir_source: resolvedDataDir.source,
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
outputCommandResult(args, payload);
|
|
1668
|
+
if (payload?.status === 'FAILED') {
|
|
1669
|
+
process.exitCode = 1;
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
export const __testables = {
|
|
1674
|
+
parseArgs,
|
|
1675
|
+
resolveDataDirDetails,
|
|
1676
|
+
resolveDataDir,
|
|
1677
|
+
validateDataDir,
|
|
1678
|
+
connectBossChatPage,
|
|
1679
|
+
hasHydratedChatShell,
|
|
1680
|
+
promptRunProfile,
|
|
1566
1681
|
resolveJobsWithRetry,
|
|
1567
1682
|
waitForChatShellHydration,
|
|
1568
1683
|
};
|