@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.
@@ -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 path from 'node:path';
5
- import process from 'node:process';
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
- dryRun: false,
186
- noState: false,
187
- json: false,
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 'dry-run':
221
- args.dryRun = true;
222
- break;
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(' --json JSON output for agent integration');
314
- console.log(' --run-id <id> Target async run_id (for get/pause/resume/cancel)');
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 dataDir = path.join(process.cwd(), '.boss-chat');
1517
- await mkdir(dataDir, { recursive: true });
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
- outputCommandResult(args, payload);
1556
- if (payload?.status === 'FAILED') {
1557
- process.exitCode = 1;
1558
- }
1559
- }
1560
-
1561
- export const __testables = {
1562
- parseArgs,
1563
- connectBossChatPage,
1564
- hasHydratedChatShell,
1565
- promptRunProfile,
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
  };