@massa-ai/opencode-plugin 1.46.0 → 1.48.0

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.
@@ -401,9 +401,64 @@ var init_xdg = () => {};
401
401
 
402
402
  // ../../packages/shared/dist/config/massa-ai-config.js
403
403
  import path2 from "path";
404
- var defaultMassaAiConfig;
404
+ var SCHEDULER_JOB_KINDS, MAX_MATCH_WORK = 1e5, MAX_IGNORE_PATTERNS = 1024, DEFAULT_CAPTURE_POLICY, DEFAULT_SCHEDULER_CONFIG, defaultMassaAiConfig;
405
405
  var init_massa_ai_config = __esm(() => {
406
406
  init_xdg();
407
+ SCHEDULER_JOB_KINDS = [
408
+ "memory-consolidation",
409
+ "decay-sweep",
410
+ "auto-improve",
411
+ "observation-bridge",
412
+ "checkpoint-purge"
413
+ ];
414
+ DEFAULT_CAPTURE_POLICY = {
415
+ rules: [
416
+ { pattern: "**/node_modules/**", disposition: "Drop" },
417
+ { pattern: "**/.git/**", disposition: "Drop" },
418
+ { pattern: "**/dist/**", disposition: "Drop" },
419
+ { pattern: "**/build/**", disposition: "Drop" },
420
+ { pattern: "**/coverage/**", disposition: "Drop" },
421
+ { pattern: ".env", disposition: "Drop" },
422
+ { pattern: ".env.*", disposition: "Drop" },
423
+ { pattern: "**/generated/**", disposition: "Drop" },
424
+ { pattern: "**/*.generated.*", disposition: "Drop" },
425
+ { pattern: "**/*.d.ts", disposition: "Drop" },
426
+ { pattern: "**/__tests__/**", disposition: "Drop" },
427
+ { pattern: "**/tests/**", disposition: "Drop" },
428
+ { pattern: "**/*.test.ts", disposition: "Drop" },
429
+ { pattern: "**/*.test.tsx", disposition: "Drop" },
430
+ { pattern: "**/*.test.js", disposition: "Drop" },
431
+ { pattern: "**/*.test.jsx", disposition: "Drop" },
432
+ { pattern: "**/*.spec.ts", disposition: "Drop" },
433
+ { pattern: "**/*.spec.tsx", disposition: "Drop" },
434
+ { pattern: "**/*.spec.js", disposition: "Drop" },
435
+ { pattern: "**/*.spec.jsx", disposition: "Drop" },
436
+ { pattern: "**/benchmarks/**", disposition: "Drop" },
437
+ { pattern: "**/fixtures/**", disposition: "Drop" },
438
+ { pattern: "**/*.wasm*", disposition: "Drop" },
439
+ { pattern: "**/*.min.*", disposition: "Drop" },
440
+ { pattern: "**/*.map", disposition: "Drop" },
441
+ { pattern: "**/lock.yaml", disposition: "Drop" },
442
+ { pattern: "**/pnpm-lock.yaml", disposition: "Drop" },
443
+ { pattern: "**/package-lock.json", disposition: "Drop" },
444
+ { pattern: "**/bun.lockb", disposition: "Drop" },
445
+ { pattern: "**/yarn.lock", disposition: "Drop" }
446
+ ],
447
+ maxMatchWork: MAX_MATCH_WORK,
448
+ maxIgnorePatterns: MAX_IGNORE_PATTERNS
449
+ };
450
+ DEFAULT_SCHEDULER_CONFIG = {
451
+ enabled: false,
452
+ tickMs: 60000,
453
+ maxConcurrent: 2,
454
+ jobs: {
455
+ "memory-consolidation": { enabled: false, intervalMs: 30 * 60 * 1000 },
456
+ "decay-sweep": { enabled: false, intervalMs: 60 * 60 * 1000 },
457
+ "auto-improve": { enabled: false, intervalMs: 30 * 60 * 1000 },
458
+ "observation-bridge": { enabled: false, intervalMs: 30 * 60 * 1000 },
459
+ "checkpoint-purge": { enabled: false, intervalMs: 60 * 60 * 1000 }
460
+ }
461
+ };
407
462
  defaultMassaAiConfig = {
408
463
  database: {
409
464
  url: ""
@@ -422,7 +477,7 @@ var init_massa_ai_config = __esm(() => {
422
477
  impact: {
423
478
  bfsCteEnabled: false
424
479
  },
425
- capturePolicy: undefined,
480
+ capturePolicy: DEFAULT_CAPTURE_POLICY,
426
481
  cache: {
427
482
  enabled: true,
428
483
  l1MaxSizeMB: 100,
@@ -545,7 +600,8 @@ var init_massa_ai_config = __esm(() => {
545
600
  },
546
601
  security: {
547
602
  corsOrigins: []
548
- }
603
+ },
604
+ scheduler: DEFAULT_SCHEDULER_CONFIG
549
605
  };
550
606
  });
551
607
 
@@ -555,6 +611,8 @@ __export(exports_config_loader, {
555
611
  writeFileAtomically: () => writeFileAtomically,
556
612
  saveConfig: () => saveConfig,
557
613
  migrateDataDirOnce: () => migrateDataDirOnce,
614
+ mergeSchedulerSection: () => mergeSchedulerSection,
615
+ loadRawUserConfig: () => loadRawUserConfig,
558
616
  loadConfigSafe: () => loadConfigSafe,
559
617
  loadConfig: () => loadConfig,
560
618
  initConfig: () => initConfig,
@@ -577,6 +635,22 @@ function getConfigPath() {
577
635
  function configExists() {
578
636
  return fs.existsSync(CONFIG_FILE);
579
637
  }
638
+ function mergeSchedulerJobs(base, incoming) {
639
+ const defaults = defaultMassaAiConfig.scheduler?.jobs ?? {};
640
+ const merged = {};
641
+ for (const kind of Object.keys(defaults)) {
642
+ merged[kind] = { ...defaults[kind], ...base?.[kind], ...incoming?.[kind] };
643
+ }
644
+ return merged;
645
+ }
646
+ function mergeSchedulerSection(base, incoming) {
647
+ return {
648
+ ...defaultMassaAiConfig.scheduler,
649
+ ...base,
650
+ ...incoming,
651
+ jobs: mergeSchedulerJobs(base?.jobs, incoming?.jobs)
652
+ };
653
+ }
580
654
  function loadConfig() {
581
655
  if (!fs.existsSync(CONFIG_FILE)) {
582
656
  return defaultMassaAiConfig;
@@ -597,13 +671,27 @@ function loadConfig() {
597
671
  memory: { ...defaultMassaAiConfig.memory, ...userConfig.memory },
598
672
  hooks: { ...defaultMassaAiConfig.hooks, ...userConfig.hooks },
599
673
  handoffs: { ...defaultMassaAiConfig.handoffs, ...userConfig.handoffs },
600
- security: { ...defaultMassaAiConfig.security, ...userConfig.security }
674
+ security: { ...defaultMassaAiConfig.security, ...userConfig.security },
675
+ scheduler: mergeSchedulerSection(undefined, userConfig.scheduler),
676
+ capturePolicy: userConfig.capturePolicy ?? defaultMassaAiConfig.capturePolicy
601
677
  };
602
678
  } catch (error) {
603
679
  console.error(`Error loading config from ${CONFIG_FILE}:`, error);
604
680
  return defaultMassaAiConfig;
605
681
  }
606
682
  }
683
+ function loadRawUserConfig() {
684
+ try {
685
+ if (!fs.existsSync(CONFIG_FILE))
686
+ return {};
687
+ const parsed = JSON.parse(fs.readFileSync(CONFIG_FILE, "utf-8"));
688
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
689
+ return {};
690
+ return parsed;
691
+ } catch {
692
+ return {};
693
+ }
694
+ }
607
695
  function loadConfigSafe() {
608
696
  try {
609
697
  return loadConfig();
@@ -734,11 +822,14 @@ try {
734
822
  // ../../packages/shared/dist/config/index.js
735
823
  init_config_loader();
736
824
  init_massa_ai_config();
825
+ init_massa_ai_config();
826
+ init_massa_ai_config();
737
827
  init_config_loader();
738
828
  import path4 from "path";
739
829
 
740
830
  // ../../packages/shared/dist/config/config-writer.js
741
831
  init_config_loader();
832
+ init_massa_ai_config();
742
833
 
743
834
  // ../../packages/shared/dist/config/index.js
744
835
  init_xdg();
@@ -778,7 +869,49 @@ function envList(key, fallback) {
778
869
  const parsed = s.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
779
870
  return parsed.length > 0 ? parsed : fallback;
780
871
  }
781
- var MAX_IGNORE_PATTERNS = 1024;
872
+ function readSchedulerConfig(rawFileConfig) {
873
+ try {
874
+ const cfg = rawFileConfig;
875
+ return cfg?.scheduler ?? {};
876
+ } catch {
877
+ return {};
878
+ }
879
+ }
880
+ var SCHEDULER_JOB_ENV = {
881
+ "memory-consolidation": {
882
+ enabledVar: "MASSA_AI_SCHEDULER_CONSOLIDATION_ENABLED",
883
+ intervalVar: "MASSA_AI_SCHEDULER_CONSOLIDATION_INTERVAL_MS",
884
+ defaultIntervalMs: 30 * 60 * 1000
885
+ },
886
+ "decay-sweep": {
887
+ enabledVar: "MASSA_AI_SCHEDULER_DECAY_ENABLED",
888
+ intervalVar: "MASSA_AI_SCHEDULER_DECAY_INTERVAL_MS",
889
+ defaultIntervalMs: 60 * 60 * 1000
890
+ },
891
+ "auto-improve": {
892
+ enabledVar: "MASSA_AI_SCHEDULER_AUTO_IMPROVE_ENABLED",
893
+ intervalVar: "MASSA_AI_SCHEDULER_AUTO_IMPROVE_INTERVAL_MS",
894
+ defaultIntervalMs: 30 * 60 * 1000
895
+ },
896
+ "observation-bridge": {
897
+ enabledVar: "MASSA_AI_SCHEDULER_OBSERVATION_BRIDGE_ENABLED",
898
+ intervalVar: "MASSA_AI_SCHEDULER_OBSERVATION_BRIDGE_INTERVAL_MS",
899
+ defaultIntervalMs: 30 * 60 * 1000
900
+ },
901
+ "checkpoint-purge": {
902
+ enabledVar: "MASSA_AI_SCHEDULER_CHECKPOINT_PURGE_ENABLED",
903
+ intervalVar: "MASSA_AI_SCHEDULER_CHECKPOINT_PURGE_INTERVAL_MS",
904
+ defaultIntervalMs: 60 * 60 * 1000
905
+ }
906
+ };
907
+ function resolveSchedulerJob(kind, fileJobs) {
908
+ const meta = SCHEDULER_JOB_ENV[kind];
909
+ const fileJob = fileJobs?.[kind];
910
+ return {
911
+ enabled: envBool(meta.enabledVar, fileJob?.enabled ?? false),
912
+ intervalMs: envNum(meta.intervalVar, fileJob?.intervalMs ?? meta.defaultIntervalMs)
913
+ };
914
+ }
782
915
  function validateCapturePolicyConfig(raw) {
783
916
  if (!raw || typeof raw !== "object")
784
917
  throw new TypeError("capturePolicy must be an object");
@@ -881,10 +1014,11 @@ var DEFAULT_ALLOWED_EXTENSIONS = [
881
1014
  var fileConfig = loadConfigSafe();
882
1015
  var fileCacheL1Bytes = fileConfig.cache?.l1MaxSizeMB ? fileConfig.cache.l1MaxSizeMB * 1024 * 1024 : undefined;
883
1016
  var fileCacheL2Bytes = fileConfig.cache?.l2MaxSizeMB ? fileConfig.cache.l2MaxSizeMB * 1024 * 1024 : undefined;
1017
+ var resolvedDataDir = getGlobalDataDir();
884
1018
  var defaultConfig = {
885
1019
  name: "massa-ai-server",
886
1020
  version: "1.0.0",
887
- dataDir: getGlobalDataDir(),
1021
+ dataDir: resolvedDataDir,
888
1022
  cache: {
889
1023
  l1: {
890
1024
  maxSize: envNum("L1_CACHE_MAX_SIZE", fileCacheL1Bytes ?? 100 * 1024 * 1024),
@@ -1008,8 +1142,24 @@ var defaultConfig = {
1008
1142
  logging: {
1009
1143
  level: process.env.LOG_LEVEL || fileConfig.logging?.level || "info",
1010
1144
  enableMetrics: process.env.ENABLE_METRICS === "true" || process.env.ENABLE_METRICS === undefined && !!fileConfig.logging?.enableMetrics,
1011
- file: process.env.MASSA_AI_LOG_FILE || fileConfig.logging?.file || undefined
1145
+ file: process.env.MASSA_AI_LOG_FILE || fileConfig.logging?.file || path4.join(resolvedDataDir, "logs", "massa-ai.log"),
1146
+ enableFileSink: envBool("MASSA_AI_LOG_ENABLE_FILE_SINK", fileConfig.logging?.enableFileSink ?? true),
1147
+ bufferSize: envNum("MASSA_AI_LOG_BUFFER_SIZE", fileConfig.logging?.bufferSize ?? 2000),
1148
+ maxFileSizeMb: envNum("MASSA_AI_LOG_MAX_FILE_SIZE_MB", fileConfig.logging?.maxFileSizeMb ?? 32),
1149
+ maxFiles: envNum("MASSA_AI_LOG_MAX_FILES", fileConfig.logging?.maxFiles ?? 5)
1012
1150
  },
1151
+ scheduler: (() => {
1152
+ const fileScheduler = readSchedulerConfig(fileConfig);
1153
+ return {
1154
+ enabled: envBool("MASSA_AI_SCHEDULER_ENABLED", fileScheduler.enabled ?? false),
1155
+ tickMs: envNum("MASSA_AI_SCHEDULER_TICK_MS", fileScheduler.tickMs ?? 60000),
1156
+ maxConcurrent: envNum("MASSA_AI_SCHEDULER_MAX_CONCURRENT", fileScheduler.maxConcurrent ?? 2),
1157
+ jobs: Object.fromEntries(SCHEDULER_JOB_KINDS.map((kind) => [
1158
+ kind,
1159
+ resolveSchedulerJob(kind, fileScheduler.jobs)
1160
+ ]))
1161
+ };
1162
+ })(),
1013
1163
  synapse: {
1014
1164
  enabled: process.env.SYNAPSE_ENABLED !== "false",
1015
1165
  inhibition: {
@@ -1124,6 +1274,11 @@ class Config {
1124
1274
  rateLimit: { ...defaults.rateLimit, ...overrides.rateLimit },
1125
1275
  security: { ...defaults.security, ...overrides.security },
1126
1276
  logging: { ...defaults.logging, ...overrides.logging },
1277
+ scheduler: {
1278
+ ...defaults.scheduler,
1279
+ ...overrides.scheduler,
1280
+ jobs: { ...defaults.scheduler.jobs, ...overrides.scheduler?.jobs }
1281
+ },
1127
1282
  synapse: {
1128
1283
  ...defaults.synapse,
1129
1284
  ...overrides.synapse,
@@ -1288,8 +1443,165 @@ var TaskStatus;
1288
1443
  TaskStatus2["FAILED"] = "failed";
1289
1444
  TaskStatus2["PAUSED"] = "paused";
1290
1445
  })(TaskStatus || (TaskStatus = {}));
1291
- // ../../packages/shared/dist/utils/logger.js
1446
+ // ../../packages/shared/dist/utils/log-sink.js
1292
1447
  import fs2 from "fs";
1448
+ import path5 from "path";
1449
+ var STAT_DELTA_THRESHOLD_BYTES = 1024 * 1024;
1450
+ var ensuredDirs = new Set;
1451
+ var pathState = new Map;
1452
+ var lastError;
1453
+ function getState(filePath) {
1454
+ let state = pathState.get(filePath);
1455
+ if (!state) {
1456
+ state = { trackedSize: 0, deltaSinceStat: 0, primed: false };
1457
+ pathState.set(filePath, state);
1458
+ }
1459
+ return state;
1460
+ }
1461
+ function ensureDir(filePath) {
1462
+ const dir = path5.dirname(filePath);
1463
+ if (ensuredDirs.has(dir))
1464
+ return;
1465
+ fs2.mkdirSync(dir, { recursive: true });
1466
+ ensuredDirs.add(dir);
1467
+ }
1468
+ function restat(filePath, state) {
1469
+ try {
1470
+ state.trackedSize = fs2.statSync(filePath).size;
1471
+ } catch {
1472
+ state.trackedSize = 0;
1473
+ }
1474
+ state.deltaSinceStat = 0;
1475
+ state.primed = true;
1476
+ }
1477
+ function rotate(filePath, maxFiles) {
1478
+ if (maxFiles <= 0) {
1479
+ if (fs2.existsSync(filePath))
1480
+ fs2.unlinkSync(filePath);
1481
+ return;
1482
+ }
1483
+ const oldest = `${filePath}.${maxFiles}`;
1484
+ if (fs2.existsSync(oldest))
1485
+ fs2.unlinkSync(oldest);
1486
+ for (let n = maxFiles - 1;n >= 1; n--) {
1487
+ const src = `${filePath}.${n}`;
1488
+ const dest = `${filePath}.${n + 1}`;
1489
+ if (fs2.existsSync(src))
1490
+ fs2.renameSync(src, dest);
1491
+ }
1492
+ if (fs2.existsSync(filePath))
1493
+ fs2.renameSync(filePath, `${filePath}.1`);
1494
+ }
1495
+ function appendLine(opts, line) {
1496
+ const { filePath, maxFileSizeBytes, maxFiles } = opts;
1497
+ try {
1498
+ ensureDir(filePath);
1499
+ const state = getState(filePath);
1500
+ const lineBytes = Buffer.byteLength(line + `
1501
+ `, "utf8");
1502
+ if (!state.primed || state.deltaSinceStat >= STAT_DELTA_THRESHOLD_BYTES || state.trackedSize + state.deltaSinceStat >= maxFileSizeBytes) {
1503
+ restat(filePath, state);
1504
+ }
1505
+ if (maxFileSizeBytes > 0 && state.trackedSize >= maxFileSizeBytes) {
1506
+ rotate(filePath, maxFiles);
1507
+ state.trackedSize = 0;
1508
+ state.deltaSinceStat = 0;
1509
+ state.primed = true;
1510
+ }
1511
+ fs2.appendFileSync(filePath, line + `
1512
+ `);
1513
+ state.trackedSize += lineBytes;
1514
+ state.deltaSinceStat += lineBytes;
1515
+ } catch (err) {
1516
+ lastError = err;
1517
+ }
1518
+ }
1519
+
1520
+ // ../../packages/shared/dist/utils/log-buffer.js
1521
+ var DEFAULT_CAPACITY = 2000;
1522
+
1523
+ class LogBufferImpl {
1524
+ capacity = DEFAULT_CAPACITY;
1525
+ entries = [];
1526
+ subscribers = new Set;
1527
+ nextSeq = 0;
1528
+ dispatching = false;
1529
+ pending = [];
1530
+ push(entry) {
1531
+ if (this.dispatching) {
1532
+ this.pending.push(entry);
1533
+ return;
1534
+ }
1535
+ this.dispatching = true;
1536
+ try {
1537
+ this.pushOne(entry);
1538
+ while (this.pending.length > 0) {
1539
+ const next = this.pending.shift();
1540
+ this.pushOne(next);
1541
+ }
1542
+ } finally {
1543
+ this.dispatching = false;
1544
+ }
1545
+ }
1546
+ pushOne(entry) {
1547
+ const full = { ...entry, seq: this.nextSeq++ };
1548
+ this.entries.push(full);
1549
+ while (this.entries.length > this.capacity) {
1550
+ this.entries.shift();
1551
+ }
1552
+ for (const fn of this.subscribers) {
1553
+ try {
1554
+ fn(full);
1555
+ } catch {}
1556
+ }
1557
+ }
1558
+ snapshot(opts) {
1559
+ const from = opts?.from;
1560
+ const to = opts?.to;
1561
+ const level = opts?.level;
1562
+ const q = opts?.q?.toLowerCase();
1563
+ const result = [];
1564
+ for (let i = this.entries.length - 1;i >= 0; i--) {
1565
+ const e = this.entries[i];
1566
+ if (from !== undefined && e.seq < from)
1567
+ continue;
1568
+ if (to !== undefined && e.seq > to)
1569
+ continue;
1570
+ if (level !== undefined && e.level !== level)
1571
+ continue;
1572
+ if (q !== undefined && !e.message.toLowerCase().includes(q))
1573
+ continue;
1574
+ result.push(e);
1575
+ }
1576
+ return result;
1577
+ }
1578
+ subscribe(fn) {
1579
+ this.subscribers.add(fn);
1580
+ return () => {
1581
+ this.subscribers.delete(fn);
1582
+ };
1583
+ }
1584
+ setCapacity(n) {
1585
+ this.capacity = n > 0 ? n : 0;
1586
+ while (this.entries.length > this.capacity) {
1587
+ this.entries.shift();
1588
+ }
1589
+ }
1590
+ size() {
1591
+ return this.entries.length;
1592
+ }
1593
+ _resetForTesting() {
1594
+ this.entries = [];
1595
+ this.subscribers.clear();
1596
+ this.nextSeq = 0;
1597
+ this.dispatching = false;
1598
+ this.pending = [];
1599
+ this.capacity = DEFAULT_CAPACITY;
1600
+ }
1601
+ }
1602
+ var logBuffer = new LogBufferImpl;
1603
+
1604
+ // ../../packages/shared/dist/utils/logger.js
1293
1605
  var LogLevel;
1294
1606
  (function(LogLevel2) {
1295
1607
  LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
@@ -1297,11 +1609,26 @@ var LogLevel;
1297
1609
  LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
1298
1610
  LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
1299
1611
  })(LogLevel || (LogLevel = {}));
1612
+ var LOG_LEVEL_LABELS = {
1613
+ [LogLevel.DEBUG]: "DEBUG",
1614
+ [LogLevel.INFO]: "INFO",
1615
+ [LogLevel.WARN]: "WARN",
1616
+ [LogLevel.ERROR]: "ERROR"
1617
+ };
1618
+ var LOG_LEVEL_BUFFER_TAGS = {
1619
+ [LogLevel.DEBUG]: "debug",
1620
+ [LogLevel.INFO]: "info",
1621
+ [LogLevel.WARN]: "warn",
1622
+ [LogLevel.ERROR]: "error"
1623
+ };
1300
1624
 
1301
1625
  class Logger {
1302
1626
  _level;
1303
1627
  _enableMetrics;
1304
1628
  _logFilePath;
1629
+ _enableFileSink;
1630
+ _maxFileSizeBytes;
1631
+ _maxFiles;
1305
1632
  _initialized = false;
1306
1633
  constructor() {}
1307
1634
  ensureInitialized() {
@@ -1311,10 +1638,18 @@ class Logger {
1311
1638
  this._level = this.parseLogLevel(loggingConfig.level);
1312
1639
  this._enableMetrics = loggingConfig.enableMetrics;
1313
1640
  this._logFilePath = loggingConfig.file;
1641
+ this._enableFileSink = loggingConfig.enableFileSink;
1642
+ this._maxFileSizeBytes = loggingConfig.maxFileSizeMb * 1024 * 1024;
1643
+ this._maxFiles = loggingConfig.maxFiles;
1644
+ logBuffer.setCapacity(loggingConfig.bufferSize);
1314
1645
  } catch {
1315
1646
  this._level = LogLevel.INFO;
1316
1647
  this._enableMetrics = false;
1317
1648
  this._logFilePath = undefined;
1649
+ this._enableFileSink = false;
1650
+ this._maxFileSizeBytes = 32 * 1024 * 1024;
1651
+ this._maxFiles = 5;
1652
+ logBuffer.setCapacity(2000);
1318
1653
  }
1319
1654
  this._initialized = true;
1320
1655
  }
@@ -1331,6 +1666,18 @@ class Logger {
1331
1666
  this.ensureInitialized();
1332
1667
  return this._logFilePath;
1333
1668
  }
1669
+ get enableFileSink() {
1670
+ this.ensureInitialized();
1671
+ return this._enableFileSink;
1672
+ }
1673
+ get maxFileSizeBytes() {
1674
+ this.ensureInitialized();
1675
+ return this._maxFileSizeBytes;
1676
+ }
1677
+ get maxFiles() {
1678
+ this.ensureInitialized();
1679
+ return this._maxFiles;
1680
+ }
1334
1681
  parseLogLevel(level) {
1335
1682
  const levels = {
1336
1683
  debug: LogLevel.DEBUG,
@@ -1343,34 +1690,40 @@ class Logger {
1343
1690
  shouldLog(level) {
1344
1691
  return level >= this.level;
1345
1692
  }
1346
- formatMessage(level, message, meta) {
1347
- const timestamp = new Date().toISOString();
1693
+ formatMessage(level, message, meta, timestamp = new Date().toISOString()) {
1348
1694
  const metaStr = meta ? ` ${JSON.stringify(meta)}` : "";
1349
1695
  return `[${timestamp}] [${level}] ${message}${metaStr}`;
1350
1696
  }
1351
- write(message, _level) {
1352
- console.error(message);
1353
- const filePath = this.logFilePath;
1354
- if (filePath) {
1355
- try {
1356
- fs2.appendFileSync(filePath, message + `
1357
- `);
1358
- } catch {}
1697
+ emit(level, message, meta) {
1698
+ const ts = new Date().toISOString();
1699
+ const line = this.formatMessage(LOG_LEVEL_LABELS[level], message, meta, ts);
1700
+ console.error(line);
1701
+ if (this.enableFileSink) {
1702
+ const filePath = this.logFilePath;
1703
+ if (filePath) {
1704
+ appendLine({ filePath, maxFileSizeBytes: this.maxFileSizeBytes, maxFiles: this.maxFiles }, line);
1705
+ }
1359
1706
  }
1707
+ logBuffer.push({
1708
+ ts,
1709
+ level: LOG_LEVEL_BUFFER_TAGS[level],
1710
+ message,
1711
+ ...meta ? { meta } : {}
1712
+ });
1360
1713
  }
1361
1714
  debug(message, meta) {
1362
1715
  if (this.shouldLog(LogLevel.DEBUG)) {
1363
- this.write(this.formatMessage("DEBUG", message, meta), LogLevel.DEBUG);
1716
+ this.emit(LogLevel.DEBUG, message, meta);
1364
1717
  }
1365
1718
  }
1366
1719
  info(message, meta) {
1367
1720
  if (this.shouldLog(LogLevel.INFO)) {
1368
- this.write(this.formatMessage("INFO", message, meta), LogLevel.INFO);
1721
+ this.emit(LogLevel.INFO, message, meta);
1369
1722
  }
1370
1723
  }
1371
1724
  warn(message, meta) {
1372
1725
  if (this.shouldLog(LogLevel.WARN)) {
1373
- this.write(this.formatMessage("WARN", message, meta), LogLevel.WARN);
1726
+ this.emit(LogLevel.WARN, message, meta);
1374
1727
  }
1375
1728
  }
1376
1729
  error(message, error, meta) {
@@ -1383,7 +1736,7 @@ class Logger {
1383
1736
  stack: error.stack
1384
1737
  }
1385
1738
  } : meta;
1386
- this.write(this.formatMessage("ERROR", message, errorMeta), LogLevel.ERROR);
1739
+ this.emit(LogLevel.ERROR, message, errorMeta);
1387
1740
  }
1388
1741
  }
1389
1742
  metric(name, value, unit) {
@@ -1507,7 +1860,7 @@ class SmartRateLimiter {
1507
1860
  var rateLimiter = new SmartRateLimiter;
1508
1861
  // ../../packages/shared/dist/profile-switch/hosts.js
1509
1862
  import os3 from "os";
1510
- import path5 from "path";
1863
+ import path6 from "path";
1511
1864
  var HOSTS = ["claude", "codex", "cursor", "opencode"];
1512
1865
  function isHost(v) {
1513
1866
  return typeof v === "string" && HOSTS.includes(v);
@@ -1520,7 +1873,7 @@ function fileLayout(host, activeDir, activeGlob, variantsRoot) {
1520
1873
  activeDir,
1521
1874
  activeGlob,
1522
1875
  variantsRoot,
1523
- variantDir: (profile) => path5.join(variantsRoot, profile)
1876
+ variantDir: (profile) => path6.join(variantsRoot, profile)
1524
1877
  };
1525
1878
  }
1526
1879
  function resolveHostLayout(host, opts = {}) {
@@ -1530,25 +1883,37 @@ function resolveHostLayout(host, opts = {}) {
1530
1883
  case "cursor":
1531
1884
  return { host, route: "skip", reason: CURSOR_SKIP_REASON };
1532
1885
  case "claude": {
1533
- const root = override ?? path5.join(targetHome, ".claude");
1534
- return fileLayout(host, path5.join(root, "agents"), "massa-ai-*.md", path5.join(root, "massa-ai", "agent-profiles"));
1886
+ const marketplaceRoot = opts.marketplaceRoot?.claude;
1887
+ if (override === undefined && marketplaceRoot !== undefined) {
1888
+ return fileLayout(host, path6.join(marketplaceRoot, "agents"), "massa-ai-*.md", path6.join(marketplaceRoot, "agent-profiles"));
1889
+ }
1890
+ const root = override ?? path6.join(targetHome, ".claude");
1891
+ return fileLayout(host, path6.join(root, "agents"), "massa-ai-*.md", path6.join(root, "massa-ai", "agent-profiles"));
1535
1892
  }
1536
1893
  case "codex": {
1537
- const root = override ?? path5.join(targetHome, ".codex");
1538
- return fileLayout(host, path5.join(root, "agents"), "massa-ai-*.toml", path5.join(root, "massa-ai", "agent-profiles"));
1894
+ const root = override ?? path6.join(targetHome, ".codex");
1895
+ return fileLayout(host, path6.join(root, "agents"), "massa-ai-*.toml", path6.join(root, "massa-ai", "agent-profiles"));
1539
1896
  }
1540
1897
  case "opencode": {
1541
- const root = override ?? path5.join(targetHome, ".config", "opencode");
1542
- const pluginsDir = path5.join(root, "plugins", "massa-ai");
1543
- return fileLayout(host, path5.join(root, "agents"), "massa-ai-*.md", path5.join(pluginsDir, "agent-profiles"));
1898
+ const root = override ?? path6.join(targetHome, ".config", "opencode");
1899
+ const pluginsDir = path6.join(root, "plugins", "massa-ai");
1900
+ return fileLayout(host, path6.join(root, "agents"), "massa-ai-*.md", path6.join(pluginsDir, "agent-profiles"));
1544
1901
  }
1545
1902
  }
1546
1903
  }
1547
- function detectRoute(platform) {
1904
+ function detectRoute(platform, host) {
1548
1905
  const route = platform?.installRoute;
1549
1906
  if (route === "file")
1550
1907
  return { kind: "proceed" };
1551
1908
  if (route === "marketplace") {
1909
+ if (host === "claude")
1910
+ return { kind: "proceed" };
1911
+ if (host === "codex") {
1912
+ return {
1913
+ kind: "refuse",
1914
+ reason: "codex marketplace-route installs are refused (in-place bundle rewrite would dirty a checkout " + "and break the drift gate) \u2014 use the dev path: MASSA_AI_MODEL_PROFILE + regenerate."
1915
+ };
1916
+ }
1552
1917
  return {
1553
1918
  kind: "refuse",
1554
1919
  reason: "claude/codex marketplace-route installs are refused (in-place bundle rewrite would dirty a checkout " + "and break the drift gate) \u2014 use the dev path: MASSA_AI_MODEL_PROFILE + regenerate."
@@ -1561,7 +1926,7 @@ function detectRoute(platform) {
1561
1926
  }
1562
1927
  // ../../packages/shared/dist/profile-switch/state.js
1563
1928
  import fs3 from "fs";
1564
- import path6 from "path";
1929
+ import path7 from "path";
1565
1930
 
1566
1931
  class InstallStateError extends Error {
1567
1932
  constructor(message) {
@@ -1617,7 +1982,7 @@ function writeInstallState(filePath, state) {
1617
1982
  const text = `${JSON.stringify(validated, null, 2)}
1618
1983
  `;
1619
1984
  try {
1620
- fs3.mkdirSync(path6.dirname(filePath), { recursive: true });
1985
+ fs3.mkdirSync(path7.dirname(filePath), { recursive: true });
1621
1986
  fs3.writeFileSync(filePath, text);
1622
1987
  } catch (err) {
1623
1988
  throw UnwritableInstallStateError(filePath, err.message);
@@ -1636,7 +2001,7 @@ function updatePlatform(filePath, host, patch) {
1636
2001
  }
1637
2002
  // ../../packages/shared/dist/profile-switch/lock.js
1638
2003
  import fs4 from "fs";
1639
- import path7 from "path";
2004
+ import path8 from "path";
1640
2005
  import os4 from "os";
1641
2006
  import crypto2 from "crypto";
1642
2007
  import { execFileSync } from "child_process";
@@ -1693,7 +2058,7 @@ function releaseIfOwned(lockDir, ownerPath, token) {
1693
2058
  }
1694
2059
  function acquireLock(stateFilePath, options = {}) {
1695
2060
  const lockDir = `${stateFilePath}.switch.lock`;
1696
- const ownerPath = path7.join(lockDir, "owner.json");
2061
+ const ownerPath = path8.join(lockDir, "owner.json");
1697
2062
  const clock = options.clock ?? DEFAULT_CLOCK;
1698
2063
  const identity = options.identity ?? DEFAULT_IDENTITY;
1699
2064
  const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
@@ -1713,7 +2078,7 @@ function acquireLock(stateFilePath, options = {}) {
1713
2078
  token,
1714
2079
  timestamp: clock.now()
1715
2080
  };
1716
- fs4.mkdirSync(path7.dirname(ownerPath), { recursive: true });
2081
+ fs4.mkdirSync(path8.dirname(ownerPath), { recursive: true });
1717
2082
  fs4.writeFileSync(ownerPath, JSON.stringify(record));
1718
2083
  return { lockDir, release: () => releaseIfOwned(lockDir, ownerPath, token) };
1719
2084
  };
@@ -1741,10 +2106,60 @@ function acquireLock(stateFilePath, options = {}) {
1741
2106
  }
1742
2107
  }
1743
2108
  // ../../packages/shared/dist/profile-switch/engine.js
2109
+ import fs6 from "fs";
2110
+ import path10 from "path";
2111
+ import os6 from "os";
2112
+ import crypto3 from "crypto";
2113
+
2114
+ // ../../packages/shared/dist/profile-switch/claude-marketplace.js
1744
2115
  import fs5 from "fs";
1745
- import path8 from "path";
1746
2116
  import os5 from "os";
1747
- import crypto3 from "crypto";
2117
+ import path9 from "path";
2118
+ var DEFAULT_PLUGIN_KEY = "massa-ai@massa-ai";
2119
+ function selectRecord(records) {
2120
+ if (records.length === 0)
2121
+ return;
2122
+ const userScoped = records.filter((r) => r.scope === "user");
2123
+ const pool = userScoped.length > 0 ? userScoped : records;
2124
+ let best;
2125
+ let bestTime = -Infinity;
2126
+ for (const record of pool) {
2127
+ const parsed = record.lastUpdated ? Date.parse(record.lastUpdated) : NaN;
2128
+ if (Number.isFinite(parsed) && parsed >= bestTime) {
2129
+ best = record;
2130
+ bestTime = parsed;
2131
+ }
2132
+ }
2133
+ return best ?? pool[pool.length - 1];
2134
+ }
2135
+ function resolveClaudeMarketplaceRoot(opts = {}) {
2136
+ const targetHome = opts.targetHome ?? os5.homedir();
2137
+ const pluginKey = opts.pluginKey ?? DEFAULT_PLUGIN_KEY;
2138
+ const registryPath = path9.join(targetHome, ".claude", "plugins", "installed_plugins.json");
2139
+ let records;
2140
+ try {
2141
+ const raw = fs5.readFileSync(registryPath, "utf8");
2142
+ const parsed = JSON.parse(raw);
2143
+ records = parsed?.plugins?.[pluginKey];
2144
+ } catch {
2145
+ return null;
2146
+ }
2147
+ if (!Array.isArray(records) || records.length === 0)
2148
+ return null;
2149
+ const selected = selectRecord(records);
2150
+ const installPath = selected?.installPath;
2151
+ if (!installPath)
2152
+ return null;
2153
+ try {
2154
+ if (!fs5.existsSync(installPath))
2155
+ return null;
2156
+ } catch {
2157
+ return null;
2158
+ }
2159
+ return installPath;
2160
+ }
2161
+
2162
+ // ../../packages/shared/dist/profile-switch/engine.js
1748
2163
  class SwitchEngineError extends Error {
1749
2164
  constructor(message) {
1750
2165
  super(message);
@@ -1759,19 +2174,39 @@ function namedError3(name, message) {
1759
2174
  var UnknownProfileError = (profile, known) => namedError3("UnknownProfileError", `unknown profile "${profile}" \u2014 installed: ${known.length > 0 ? known.join(", ") : "none"}`);
1760
2175
  var NoHostsDetectedError = () => namedError3("NoHostsDetectedError", "no installed hosts found");
1761
2176
  function defaultStatePath(targetHome) {
1762
- return path8.join(targetHome, ".config", "massa-ai", "install-state.json");
2177
+ return path10.join(targetHome, ".config", "massa-ai", "install-state.json");
1763
2178
  }
1764
2179
  function resolveCommon(opts) {
1765
- const targetHome = opts.targetHome ?? os5.homedir();
2180
+ const targetHome = opts.targetHome ?? os6.homedir();
1766
2181
  const stateFilePath = opts.stateFilePath ?? defaultStatePath(targetHome);
1767
2182
  return { targetHome, stateFilePath };
1768
2183
  }
2184
+ function marketplaceRoots(targetHome, state) {
2185
+ return state.platforms.claude?.installRoute === "marketplace" ? { claude: resolveClaudeMarketplaceRoot({ targetHome }) ?? undefined } : {};
2186
+ }
2187
+ function claudeMarketplaceUnresolvedReason(targetHome) {
2188
+ const registryPath = path10.join(targetHome, ".claude", "plugins", "installed_plugins.json");
2189
+ return `claude installRoute is "marketplace" but no install root could be resolved from ${registryPath} ` + "\u2014 re-run the Claude plugin installer, or verify the plugin registry file";
2190
+ }
1769
2191
  function listProfiles(opts = {}) {
1770
2192
  const { targetHome, stateFilePath } = resolveCommon(opts);
1771
2193
  const state = readInstallState(stateFilePath);
2194
+ const roots = marketplaceRoots(targetHome, state);
1772
2195
  const universe = opts.hosts ?? HOSTS;
1773
2196
  const hosts = universe.map((host) => {
1774
- const layout = resolveHostLayout(host, { targetHome, projectRoot: opts.projectRoot });
2197
+ if (host === "claude" && state.platforms.claude?.installRoute === "marketplace" && roots.claude === undefined) {
2198
+ const platform2 = state.platforms.claude;
2199
+ return {
2200
+ host,
2201
+ installed: false,
2202
+ skipped: false,
2203
+ skipReason: null,
2204
+ activeProfile: platform2.modelProfile?.profile ?? opts.hostDefaults?.[host] ?? "balanced",
2205
+ bundleVersion: platform2.plugin?.version ?? null,
2206
+ availableProfiles: []
2207
+ };
2208
+ }
2209
+ const layout = resolveHostLayout(host, { targetHome, projectRoot: opts.projectRoot, marketplaceRoot: roots });
1775
2210
  if (layout.route === "skip") {
1776
2211
  return {
1777
2212
  host,
@@ -1783,7 +2218,7 @@ function listProfiles(opts = {}) {
1783
2218
  availableProfiles: []
1784
2219
  };
1785
2220
  }
1786
- const installed = fs5.existsSync(layout.activeDir);
2221
+ const installed = fs6.existsSync(layout.activeDir);
1787
2222
  const availableProfiles = listVariantProfiles(layout);
1788
2223
  const platform = state.platforms[host];
1789
2224
  return {
@@ -1799,9 +2234,9 @@ function listProfiles(opts = {}) {
1799
2234
  return { hosts };
1800
2235
  }
1801
2236
  function listVariantProfiles(layout) {
1802
- if (!fs5.existsSync(layout.variantsRoot))
2237
+ if (!fs6.existsSync(layout.variantsRoot))
1803
2238
  return [];
1804
- return fs5.readdirSync(layout.variantsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
2239
+ return fs6.readdirSync(layout.variantsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
1805
2240
  }
1806
2241
  function matchesGlob(filename, glob) {
1807
2242
  const starIdx = glob.indexOf("*");
@@ -1812,50 +2247,50 @@ function matchesGlob(filename, glob) {
1812
2247
  return filename.length >= prefix.length + suffix.length && filename.startsWith(prefix) && filename.endsWith(suffix);
1813
2248
  }
1814
2249
  function assertStateWritable(stateFilePath) {
1815
- const dir = path8.dirname(stateFilePath);
2250
+ const dir = path10.dirname(stateFilePath);
1816
2251
  try {
1817
- fs5.mkdirSync(dir, { recursive: true });
2252
+ fs6.mkdirSync(dir, { recursive: true });
1818
2253
  } catch (err) {
1819
2254
  throw UnwritableInstallStateError(stateFilePath, err.message);
1820
2255
  }
1821
- const checkPath = fs5.existsSync(stateFilePath) ? stateFilePath : dir;
2256
+ const checkPath = fs6.existsSync(stateFilePath) ? stateFilePath : dir;
1822
2257
  try {
1823
- fs5.accessSync(checkPath, fs5.constants.W_OK);
2258
+ fs6.accessSync(checkPath, fs6.constants.W_OK);
1824
2259
  } catch (err) {
1825
2260
  throw UnwritableInstallStateError(stateFilePath, err.message);
1826
2261
  }
1827
2262
  }
1828
2263
  function copyFileRouteVariant(layout, variantDir) {
1829
- fs5.mkdirSync(layout.activeDir, { recursive: true });
2264
+ fs6.mkdirSync(layout.activeDir, { recursive: true });
1830
2265
  let changed = 0;
1831
- for (const entry of fs5.readdirSync(variantDir, { withFileTypes: true })) {
2266
+ for (const entry of fs6.readdirSync(variantDir, { withFileTypes: true })) {
1832
2267
  if (!entry.isFile() || !matchesGlob(entry.name, layout.activeGlob))
1833
2268
  continue;
1834
- fs5.copyFileSync(path8.join(variantDir, entry.name), path8.join(layout.activeDir, entry.name));
2269
+ fs6.copyFileSync(path10.join(variantDir, entry.name), path10.join(layout.activeDir, entry.name));
1835
2270
  changed++;
1836
2271
  }
1837
2272
  return changed;
1838
2273
  }
1839
2274
  function repointOpencodeVariant(layout, variantDir) {
1840
- fs5.mkdirSync(layout.activeDir, { recursive: true });
2275
+ fs6.mkdirSync(layout.activeDir, { recursive: true });
1841
2276
  let changed = 0;
1842
- for (const entry of fs5.readdirSync(variantDir, { withFileTypes: true })) {
2277
+ for (const entry of fs6.readdirSync(variantDir, { withFileTypes: true })) {
1843
2278
  if (!entry.isFile() || !matchesGlob(entry.name, layout.activeGlob))
1844
2279
  continue;
1845
- const dest = path8.join(layout.activeDir, entry.name);
1846
- const target = path8.resolve(path8.join(variantDir, entry.name));
2280
+ const dest = path10.join(layout.activeDir, entry.name);
2281
+ const target = path10.resolve(path10.join(variantDir, entry.name));
1847
2282
  let destExists = true;
1848
2283
  let destIsSymlink = false;
1849
2284
  try {
1850
- destIsSymlink = fs5.lstatSync(dest).isSymbolicLink();
2285
+ destIsSymlink = fs6.lstatSync(dest).isSymbolicLink();
1851
2286
  } catch {
1852
2287
  destExists = false;
1853
2288
  }
1854
2289
  if (destExists && !destIsSymlink)
1855
2290
  continue;
1856
2291
  const tmp = `${dest}.massa-ai-switch.${crypto3.randomUUID()}`;
1857
- fs5.symlinkSync(target, tmp);
1858
- fs5.renameSync(tmp, dest);
2292
+ fs6.symlinkSync(target, tmp);
2293
+ fs6.renameSync(tmp, dest);
1859
2294
  changed++;
1860
2295
  }
1861
2296
  return changed;
@@ -1871,19 +2306,37 @@ function switchProfile(opts) {
1871
2306
  const state = readInstallState(stateFilePath);
1872
2307
  if (!dryRun)
1873
2308
  assertStateWritable(stateFilePath);
1874
- const layouts = universe.map((host) => ({ host, layout: resolveHostLayout(host, { targetHome, projectRoot: opts.projectRoot }) }));
1875
- const skipRows = layouts.filter((l) => l.layout.route === "skip").map((l) => ({ host: l.host, status: "skipped", reason: l.layout.reason }));
2309
+ const roots = marketplaceRoots(targetHome, state);
2310
+ const unresolvedRows = [];
2311
+ const resolvableUniverse = universe.filter((host) => {
2312
+ if (host !== "claude")
2313
+ return true;
2314
+ if (state.platforms.claude?.installRoute !== "marketplace")
2315
+ return true;
2316
+ if (roots.claude !== undefined)
2317
+ return true;
2318
+ unresolvedRows.push({ host, status: "failed", reason: claudeMarketplaceUnresolvedReason(targetHome) });
2319
+ return false;
2320
+ });
2321
+ const layouts = resolvableUniverse.map((host) => ({
2322
+ host,
2323
+ layout: resolveHostLayout(host, { targetHome, projectRoot: opts.projectRoot, marketplaceRoot: roots })
2324
+ }));
2325
+ const skipRows = [
2326
+ ...unresolvedRows,
2327
+ ...layouts.filter((l) => l.layout.route === "skip").map((l) => ({ host: l.host, status: "skipped", reason: l.layout.reason }))
2328
+ ];
1876
2329
  const fileHosts = layouts.filter((l) => l.layout.route === "files");
1877
2330
  if (fileHosts.length === 0) {
1878
2331
  return { profile: opts.profile, dryRun, hosts: orderRows(universe, skipRows), restartRequired: false };
1879
2332
  }
1880
- const installedFileHosts = fileHosts.filter((h) => fs5.existsSync(h.layout.activeDir));
2333
+ const installedFileHosts = fileHosts.filter((h) => fs6.existsSync(h.layout.activeDir));
1881
2334
  if (installedFileHosts.length === 0)
1882
2335
  throw NoHostsDetectedError();
1883
2336
  const withAvailability = fileHosts.map((h) => {
1884
- const variantsRootExists = fs5.existsSync(h.layout.variantsRoot);
2337
+ const variantsRootExists = fs6.existsSync(h.layout.variantsRoot);
1885
2338
  const variantDir = h.layout.variantDir(opts.profile);
1886
- const available = variantsRootExists && fs5.existsSync(variantDir) && fs5.statSync(variantDir).isDirectory();
2339
+ const available = variantsRootExists && fs6.existsSync(variantDir) && fs6.statSync(variantDir).isDirectory();
1887
2340
  return { ...h, variantsRootExists, variantDir, available };
1888
2341
  });
1889
2342
  if (!withAvailability.some((h) => h.available)) {
@@ -1913,7 +2366,7 @@ function switchProfile(opts) {
1913
2366
  });
1914
2367
  continue;
1915
2368
  }
1916
- const route = detectRoute(state.platforms[h.host]);
2369
+ const route = detectRoute(state.platforms[h.host], h.host);
1917
2370
  if (route.kind === "refuse") {
1918
2371
  rows.push({ host: h.host, status: "failed", reason: route.reason });
1919
2372
  continue;
@@ -1948,19 +2401,26 @@ function reportSucceeded(report) {
1948
2401
  return report.hosts.every((h) => h.status === "switched" || h.status === "skipped");
1949
2402
  }
1950
2403
  // ../../packages/shared/dist/profile-switch/variant-sync.js
1951
- import fs6 from "fs";
1952
- import path9 from "path";
2404
+ import fs7 from "fs";
2405
+ import path11 from "path";
2406
+ import os7 from "os";
1953
2407
  import crypto4 from "crypto";
2408
+ function defaultStatePath2(targetHome) {
2409
+ return path11.join(targetHome, ".config", "massa-ai", "install-state.json");
2410
+ }
2411
+ function marketplaceRoots2(targetHome, state) {
2412
+ return state.platforms.claude?.installRoute === "marketplace" ? { claude: resolveClaudeMarketplaceRoot({ targetHome }) ?? undefined } : {};
2413
+ }
1954
2414
  var tempFileCounter2 = 0;
1955
2415
  function writeFileIntoDirAtomically(destDir, destName, content) {
1956
2416
  const unique = `${process.pid}.${++tempFileCounter2}.${crypto4.randomBytes(6).toString("hex")}`;
1957
- const tempFile = path9.join(destDir, `.${destName}.${unique}.tmp`);
2417
+ const tempFile = path11.join(destDir, `.${destName}.${unique}.tmp`);
1958
2418
  try {
1959
- fs6.writeFileSync(tempFile, content);
1960
- fs6.renameSync(tempFile, path9.join(destDir, destName));
2419
+ fs7.writeFileSync(tempFile, content);
2420
+ fs7.renameSync(tempFile, path11.join(destDir, destName));
1961
2421
  } catch (error) {
1962
2422
  try {
1963
- fs6.unlinkSync(tempFile);
2423
+ fs7.unlinkSync(tempFile);
1964
2424
  } catch {}
1965
2425
  throw error;
1966
2426
  }
@@ -1968,20 +2428,20 @@ function writeFileIntoDirAtomically(destDir, destName, content) {
1968
2428
  function isSafeDirName(name) {
1969
2429
  if (name === "." || name === "..")
1970
2430
  return false;
1971
- if (name.includes("/") || name.includes("\\") || name.includes(path9.sep))
2431
+ if (name.includes("/") || name.includes("\\") || name.includes(path11.sep))
1972
2432
  return false;
1973
- return path9.basename(name) === name;
2433
+ return path11.basename(name) === name;
1974
2434
  }
1975
- function syncHost(host, sourceRoot, targetHome) {
1976
- const layout = resolveHostLayout(host, { targetHome });
2435
+ function syncHost(host, sourceRoot, targetHome, marketplaceRoot) {
2436
+ const layout = resolveHostLayout(host, { targetHome, marketplaceRoot });
1977
2437
  if (layout.route === "skip") {
1978
2438
  return { host, status: "skipped", profiles: [], retained: [], files: 0, reason: layout.reason };
1979
2439
  }
1980
- const srcDir = path9.join(sourceRoot, "apps", `${host}-plugin`, "agent-profiles");
1981
- if (!fs6.existsSync(srcDir) || !fs6.statSync(srcDir).isDirectory()) {
2440
+ const srcDir = path11.join(sourceRoot, "apps", `${host}-plugin`, "agent-profiles");
2441
+ if (!fs7.existsSync(srcDir) || !fs7.statSync(srcDir).isDirectory()) {
1982
2442
  return { host, status: "skipped", profiles: [], retained: [], files: 0, reason: "no generated variants for this host" };
1983
2443
  }
1984
- if (!fs6.existsSync(layout.variantsRoot)) {
2444
+ if (!fs7.existsSync(layout.variantsRoot)) {
1985
2445
  return {
1986
2446
  host,
1987
2447
  status: "skipped",
@@ -1993,24 +2453,24 @@ function syncHost(host, sourceRoot, targetHome) {
1993
2453
  }
1994
2454
  const profiles = [];
1995
2455
  let files = 0;
1996
- for (const entry of fs6.readdirSync(srcDir, { withFileTypes: true })) {
2456
+ for (const entry of fs7.readdirSync(srcDir, { withFileTypes: true })) {
1997
2457
  if (!entry.isDirectory())
1998
2458
  continue;
1999
2459
  if (!isSafeDirName(entry.name))
2000
2460
  continue;
2001
- const srcProfileDir = path9.join(srcDir, entry.name);
2002
- const destProfileDir = path9.join(layout.variantsRoot, entry.name);
2003
- fs6.mkdirSync(destProfileDir, { recursive: true });
2004
- for (const fileEntry of fs6.readdirSync(srcProfileDir, { withFileTypes: true })) {
2461
+ const srcProfileDir = path11.join(srcDir, entry.name);
2462
+ const destProfileDir = path11.join(layout.variantsRoot, entry.name);
2463
+ fs7.mkdirSync(destProfileDir, { recursive: true });
2464
+ for (const fileEntry of fs7.readdirSync(srcProfileDir, { withFileTypes: true })) {
2005
2465
  if (!fileEntry.isFile())
2006
2466
  continue;
2007
- const content = fs6.readFileSync(path9.join(srcProfileDir, fileEntry.name));
2467
+ const content = fs7.readFileSync(path11.join(srcProfileDir, fileEntry.name));
2008
2468
  writeFileIntoDirAtomically(destProfileDir, fileEntry.name, content);
2009
2469
  files++;
2010
2470
  }
2011
2471
  profiles.push(entry.name);
2012
2472
  }
2013
- const retained = fs6.readdirSync(layout.variantsRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && !profiles.includes(e.name)).map((e) => e.name).sort();
2473
+ const retained = fs7.readdirSync(layout.variantsRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && !profiles.includes(e.name)).map((e) => e.name).sort();
2014
2474
  return { host, status: "synced", profiles: profiles.sort(), retained, files };
2015
2475
  }
2016
2476
  function syncGeneratedVariants(opts) {
@@ -2026,23 +2486,26 @@ function syncGeneratedVariants(opts) {
2026
2486
  }));
2027
2487
  }
2028
2488
  const sourceRoot = opts.sourceRoot;
2489
+ const targetHome = opts.targetHome ?? os7.homedir();
2490
+ const state = readInstallState(defaultStatePath2(targetHome));
2491
+ const roots = marketplaceRoots2(targetHome, state);
2029
2492
  return hosts.map((host) => {
2030
2493
  try {
2031
- return syncHost(host, sourceRoot, opts.targetHome);
2494
+ return syncHost(host, sourceRoot, opts.targetHome, roots);
2032
2495
  } catch (err) {
2033
2496
  return { host, status: "failed", profiles: [], retained: [], files: 0, error: err.message };
2034
2497
  }
2035
2498
  });
2036
2499
  }
2037
2500
  // ../../packages/shared/dist/profile-switch/repo-root.js
2038
- import fs7 from "fs";
2039
- import path10 from "path";
2501
+ import fs8 from "fs";
2502
+ import path12 from "path";
2040
2503
  function findRepoRootWithMarker(startDir, marker, maxLevels) {
2041
2504
  let dir = startDir;
2042
2505
  for (let i = 0;i <= maxLevels; i++) {
2043
- if (fs7.existsSync(path10.join(dir, marker)))
2506
+ if (fs8.existsSync(path12.join(dir, marker)))
2044
2507
  return dir;
2045
- const parent = path10.dirname(dir);
2508
+ const parent = path12.dirname(dir);
2046
2509
  if (parent === dir)
2047
2510
  break;
2048
2511
  dir = parent;
@@ -2050,11 +2513,11 @@ function findRepoRootWithMarker(startDir, marker, maxLevels) {
2050
2513
  return null;
2051
2514
  }
2052
2515
  // src/config-cli.ts
2053
- import { promises as fs8 } from "fs";
2054
- import path11 from "path";
2055
- import os6 from "os";
2516
+ import { promises as fs9 } from "fs";
2517
+ import path13 from "path";
2518
+ import os8 from "os";
2056
2519
  import { fileURLToPath } from "url";
2057
- var __dirname2 = path11.dirname(fileURLToPath(import.meta.url));
2520
+ var __dirname2 = path13.dirname(fileURLToPath(import.meta.url));
2058
2521
  var GENERATOR_MARKER = "scripts/generate-subagent-artifacts.ts";
2059
2522
  var GENERATOR_MARKER_MAX_LEVELS = 6;
2060
2523
  function formatVariantSync(results) {
@@ -2097,7 +2560,7 @@ Commands:
2097
2560
  Examples:
2098
2561
  massa-ai-config init
2099
2562
  massa-ai-config init --mistral your-api-key
2100
- massa-ai-config use ollama --model nomic-embed-text:latest
2563
+ massa-ai-config use ollama --model qwen3-embedding:4b
2101
2564
  massa-ai-config use mistral --api-key your-key
2102
2565
  massa-ai-config set embedding.dimensions 1024
2103
2566
  massa-ai-config agents install --user
@@ -2226,9 +2689,9 @@ Using defaults:`);
2226
2689
  if (provider === "ollama") {
2227
2690
  config2.embedding = {
2228
2691
  provider: "ollama",
2229
- model: options.model || "nomic-embed-text:latest",
2692
+ model: options.model || "qwen3-embedding:4b",
2230
2693
  baseURL: options["base-url"] || "http://localhost:11434",
2231
- dimensions: 768
2694
+ dimensions: 2560
2232
2695
  };
2233
2696
  } else if (provider === "mistral") {
2234
2697
  if (!options["api-key"]) {
@@ -2265,18 +2728,18 @@ Using defaults:`);
2265
2728
  return 1;
2266
2729
  }
2267
2730
  const scope = typeof options.project === "boolean" ? "project" : "user";
2268
- const agentsDir = scope === "project" ? path11.join(process.cwd(), ".opencode/agents") : path11.join(process.env.XDG_CONFIG_HOME && process.env.XDG_CONFIG_HOME.trim() || path11.join(os6.homedir(), ".config"), "opencode", "agents");
2269
- const sourceAgentsDir = path11.resolve(__dirname2, "..", "agents");
2731
+ const agentsDir = scope === "project" ? path13.join(process.cwd(), ".opencode/agents") : path13.join(process.env.XDG_CONFIG_HOME && process.env.XDG_CONFIG_HOME.trim() || path13.join(os8.homedir(), ".config"), "opencode", "agents");
2732
+ const sourceAgentsDir = path13.resolve(__dirname2, "..", "agents");
2270
2733
  if (subcommand === "install") {
2271
- await fs8.mkdir(agentsDir, { recursive: true });
2734
+ await fs9.mkdir(agentsDir, { recursive: true });
2272
2735
  let count = 0;
2273
- const entries = await fs8.readdir(sourceAgentsDir);
2736
+ const entries = await fs9.readdir(sourceAgentsDir);
2274
2737
  for (const entry of entries) {
2275
2738
  if (!entry.startsWith("massa-ai-") || !entry.endsWith(".md"))
2276
2739
  continue;
2277
- const src = path11.join(sourceAgentsDir, entry);
2278
- const dest = path11.join(agentsDir, entry);
2279
- await fs8.copyFile(src, dest);
2740
+ const src = path13.join(sourceAgentsDir, entry);
2741
+ const dest = path13.join(agentsDir, entry);
2742
+ await fs9.copyFile(src, dest);
2280
2743
  count++;
2281
2744
  }
2282
2745
  console.log(`+ ${count} subagent specialists (generated from skills/agents/*/SKILL.md)`);
@@ -2284,14 +2747,14 @@ Using defaults:`);
2284
2747
  } else {
2285
2748
  let removed = 0;
2286
2749
  try {
2287
- const entries = await fs8.readdir(agentsDir);
2750
+ const entries = await fs9.readdir(agentsDir);
2288
2751
  for (const entry of entries) {
2289
2752
  if (!entry.startsWith("massa-ai-") || !entry.endsWith(".md"))
2290
2753
  continue;
2291
- const filePath = path11.join(agentsDir, entry);
2292
- const content = await fs8.readFile(filePath, "utf8");
2754
+ const filePath = path13.join(agentsDir, entry);
2755
+ const content = await fs9.readFile(filePath, "utf8");
2293
2756
  if (content.includes("massa-ai-owned: true")) {
2294
- await fs8.unlink(filePath);
2757
+ await fs9.unlink(filePath);
2295
2758
  removed++;
2296
2759
  }
2297
2760
  }