@massa-ai/opencode-plugin 1.46.0 → 1.47.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.
- package/dist/config-cli.js +483 -101
- package/dist/index.js +77 -3
- package/package.json +3 -3
package/dist/config-cli.js
CHANGED
|
@@ -401,9 +401,16 @@ 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, 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
|
+
];
|
|
407
414
|
defaultMassaAiConfig = {
|
|
408
415
|
database: {
|
|
409
416
|
url: ""
|
|
@@ -734,11 +741,13 @@ try {
|
|
|
734
741
|
// ../../packages/shared/dist/config/index.js
|
|
735
742
|
init_config_loader();
|
|
736
743
|
init_massa_ai_config();
|
|
744
|
+
init_massa_ai_config();
|
|
737
745
|
init_config_loader();
|
|
738
746
|
import path4 from "path";
|
|
739
747
|
|
|
740
748
|
// ../../packages/shared/dist/config/config-writer.js
|
|
741
749
|
init_config_loader();
|
|
750
|
+
init_massa_ai_config();
|
|
742
751
|
|
|
743
752
|
// ../../packages/shared/dist/config/index.js
|
|
744
753
|
init_xdg();
|
|
@@ -778,6 +787,49 @@ function envList(key, fallback) {
|
|
|
778
787
|
const parsed = s.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
|
|
779
788
|
return parsed.length > 0 ? parsed : fallback;
|
|
780
789
|
}
|
|
790
|
+
function readSchedulerConfig(rawFileConfig) {
|
|
791
|
+
try {
|
|
792
|
+
const cfg = rawFileConfig;
|
|
793
|
+
return cfg?.scheduler ?? {};
|
|
794
|
+
} catch {
|
|
795
|
+
return {};
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
var SCHEDULER_JOB_ENV = {
|
|
799
|
+
"memory-consolidation": {
|
|
800
|
+
enabledVar: "MASSA_AI_SCHEDULER_CONSOLIDATION_ENABLED",
|
|
801
|
+
intervalVar: "MASSA_AI_SCHEDULER_CONSOLIDATION_INTERVAL_MS",
|
|
802
|
+
defaultIntervalMs: 30 * 60 * 1000
|
|
803
|
+
},
|
|
804
|
+
"decay-sweep": {
|
|
805
|
+
enabledVar: "MASSA_AI_SCHEDULER_DECAY_ENABLED",
|
|
806
|
+
intervalVar: "MASSA_AI_SCHEDULER_DECAY_INTERVAL_MS",
|
|
807
|
+
defaultIntervalMs: 60 * 60 * 1000
|
|
808
|
+
},
|
|
809
|
+
"auto-improve": {
|
|
810
|
+
enabledVar: "MASSA_AI_SCHEDULER_AUTO_IMPROVE_ENABLED",
|
|
811
|
+
intervalVar: "MASSA_AI_SCHEDULER_AUTO_IMPROVE_INTERVAL_MS",
|
|
812
|
+
defaultIntervalMs: 30 * 60 * 1000
|
|
813
|
+
},
|
|
814
|
+
"observation-bridge": {
|
|
815
|
+
enabledVar: "MASSA_AI_SCHEDULER_OBSERVATION_BRIDGE_ENABLED",
|
|
816
|
+
intervalVar: "MASSA_AI_SCHEDULER_OBSERVATION_BRIDGE_INTERVAL_MS",
|
|
817
|
+
defaultIntervalMs: 30 * 60 * 1000
|
|
818
|
+
},
|
|
819
|
+
"checkpoint-purge": {
|
|
820
|
+
enabledVar: "MASSA_AI_SCHEDULER_CHECKPOINT_PURGE_ENABLED",
|
|
821
|
+
intervalVar: "MASSA_AI_SCHEDULER_CHECKPOINT_PURGE_INTERVAL_MS",
|
|
822
|
+
defaultIntervalMs: 60 * 60 * 1000
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
function resolveSchedulerJob(kind, fileJobs) {
|
|
826
|
+
const meta = SCHEDULER_JOB_ENV[kind];
|
|
827
|
+
const fileJob = fileJobs?.[kind];
|
|
828
|
+
return {
|
|
829
|
+
enabled: envBool(meta.enabledVar, fileJob?.enabled ?? false),
|
|
830
|
+
intervalMs: envNum(meta.intervalVar, fileJob?.intervalMs ?? meta.defaultIntervalMs)
|
|
831
|
+
};
|
|
832
|
+
}
|
|
781
833
|
var MAX_IGNORE_PATTERNS = 1024;
|
|
782
834
|
function validateCapturePolicyConfig(raw) {
|
|
783
835
|
if (!raw || typeof raw !== "object")
|
|
@@ -881,10 +933,11 @@ var DEFAULT_ALLOWED_EXTENSIONS = [
|
|
|
881
933
|
var fileConfig = loadConfigSafe();
|
|
882
934
|
var fileCacheL1Bytes = fileConfig.cache?.l1MaxSizeMB ? fileConfig.cache.l1MaxSizeMB * 1024 * 1024 : undefined;
|
|
883
935
|
var fileCacheL2Bytes = fileConfig.cache?.l2MaxSizeMB ? fileConfig.cache.l2MaxSizeMB * 1024 * 1024 : undefined;
|
|
936
|
+
var resolvedDataDir = getGlobalDataDir();
|
|
884
937
|
var defaultConfig = {
|
|
885
938
|
name: "massa-ai-server",
|
|
886
939
|
version: "1.0.0",
|
|
887
|
-
dataDir:
|
|
940
|
+
dataDir: resolvedDataDir,
|
|
888
941
|
cache: {
|
|
889
942
|
l1: {
|
|
890
943
|
maxSize: envNum("L1_CACHE_MAX_SIZE", fileCacheL1Bytes ?? 100 * 1024 * 1024),
|
|
@@ -1008,8 +1061,24 @@ var defaultConfig = {
|
|
|
1008
1061
|
logging: {
|
|
1009
1062
|
level: process.env.LOG_LEVEL || fileConfig.logging?.level || "info",
|
|
1010
1063
|
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 ||
|
|
1064
|
+
file: process.env.MASSA_AI_LOG_FILE || fileConfig.logging?.file || path4.join(resolvedDataDir, "logs", "massa-ai.log"),
|
|
1065
|
+
enableFileSink: envBool("MASSA_AI_LOG_ENABLE_FILE_SINK", fileConfig.logging?.enableFileSink ?? true),
|
|
1066
|
+
bufferSize: envNum("MASSA_AI_LOG_BUFFER_SIZE", fileConfig.logging?.bufferSize ?? 2000),
|
|
1067
|
+
maxFileSizeMb: envNum("MASSA_AI_LOG_MAX_FILE_SIZE_MB", fileConfig.logging?.maxFileSizeMb ?? 32),
|
|
1068
|
+
maxFiles: envNum("MASSA_AI_LOG_MAX_FILES", fileConfig.logging?.maxFiles ?? 5)
|
|
1012
1069
|
},
|
|
1070
|
+
scheduler: (() => {
|
|
1071
|
+
const fileScheduler = readSchedulerConfig(fileConfig);
|
|
1072
|
+
return {
|
|
1073
|
+
enabled: envBool("MASSA_AI_SCHEDULER_ENABLED", fileScheduler.enabled ?? false),
|
|
1074
|
+
tickMs: envNum("MASSA_AI_SCHEDULER_TICK_MS", fileScheduler.tickMs ?? 60000),
|
|
1075
|
+
maxConcurrent: envNum("MASSA_AI_SCHEDULER_MAX_CONCURRENT", fileScheduler.maxConcurrent ?? 2),
|
|
1076
|
+
jobs: Object.fromEntries(SCHEDULER_JOB_KINDS.map((kind) => [
|
|
1077
|
+
kind,
|
|
1078
|
+
resolveSchedulerJob(kind, fileScheduler.jobs)
|
|
1079
|
+
]))
|
|
1080
|
+
};
|
|
1081
|
+
})(),
|
|
1013
1082
|
synapse: {
|
|
1014
1083
|
enabled: process.env.SYNAPSE_ENABLED !== "false",
|
|
1015
1084
|
inhibition: {
|
|
@@ -1124,6 +1193,11 @@ class Config {
|
|
|
1124
1193
|
rateLimit: { ...defaults.rateLimit, ...overrides.rateLimit },
|
|
1125
1194
|
security: { ...defaults.security, ...overrides.security },
|
|
1126
1195
|
logging: { ...defaults.logging, ...overrides.logging },
|
|
1196
|
+
scheduler: {
|
|
1197
|
+
...defaults.scheduler,
|
|
1198
|
+
...overrides.scheduler,
|
|
1199
|
+
jobs: { ...defaults.scheduler.jobs, ...overrides.scheduler?.jobs }
|
|
1200
|
+
},
|
|
1127
1201
|
synapse: {
|
|
1128
1202
|
...defaults.synapse,
|
|
1129
1203
|
...overrides.synapse,
|
|
@@ -1288,8 +1362,165 @@ var TaskStatus;
|
|
|
1288
1362
|
TaskStatus2["FAILED"] = "failed";
|
|
1289
1363
|
TaskStatus2["PAUSED"] = "paused";
|
|
1290
1364
|
})(TaskStatus || (TaskStatus = {}));
|
|
1291
|
-
// ../../packages/shared/dist/utils/
|
|
1365
|
+
// ../../packages/shared/dist/utils/log-sink.js
|
|
1292
1366
|
import fs2 from "fs";
|
|
1367
|
+
import path5 from "path";
|
|
1368
|
+
var STAT_DELTA_THRESHOLD_BYTES = 1024 * 1024;
|
|
1369
|
+
var ensuredDirs = new Set;
|
|
1370
|
+
var pathState = new Map;
|
|
1371
|
+
var lastError;
|
|
1372
|
+
function getState(filePath) {
|
|
1373
|
+
let state = pathState.get(filePath);
|
|
1374
|
+
if (!state) {
|
|
1375
|
+
state = { trackedSize: 0, deltaSinceStat: 0, primed: false };
|
|
1376
|
+
pathState.set(filePath, state);
|
|
1377
|
+
}
|
|
1378
|
+
return state;
|
|
1379
|
+
}
|
|
1380
|
+
function ensureDir(filePath) {
|
|
1381
|
+
const dir = path5.dirname(filePath);
|
|
1382
|
+
if (ensuredDirs.has(dir))
|
|
1383
|
+
return;
|
|
1384
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
1385
|
+
ensuredDirs.add(dir);
|
|
1386
|
+
}
|
|
1387
|
+
function restat(filePath, state) {
|
|
1388
|
+
try {
|
|
1389
|
+
state.trackedSize = fs2.statSync(filePath).size;
|
|
1390
|
+
} catch {
|
|
1391
|
+
state.trackedSize = 0;
|
|
1392
|
+
}
|
|
1393
|
+
state.deltaSinceStat = 0;
|
|
1394
|
+
state.primed = true;
|
|
1395
|
+
}
|
|
1396
|
+
function rotate(filePath, maxFiles) {
|
|
1397
|
+
if (maxFiles <= 0) {
|
|
1398
|
+
if (fs2.existsSync(filePath))
|
|
1399
|
+
fs2.unlinkSync(filePath);
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
const oldest = `${filePath}.${maxFiles}`;
|
|
1403
|
+
if (fs2.existsSync(oldest))
|
|
1404
|
+
fs2.unlinkSync(oldest);
|
|
1405
|
+
for (let n = maxFiles - 1;n >= 1; n--) {
|
|
1406
|
+
const src = `${filePath}.${n}`;
|
|
1407
|
+
const dest = `${filePath}.${n + 1}`;
|
|
1408
|
+
if (fs2.existsSync(src))
|
|
1409
|
+
fs2.renameSync(src, dest);
|
|
1410
|
+
}
|
|
1411
|
+
if (fs2.existsSync(filePath))
|
|
1412
|
+
fs2.renameSync(filePath, `${filePath}.1`);
|
|
1413
|
+
}
|
|
1414
|
+
function appendLine(opts, line) {
|
|
1415
|
+
const { filePath, maxFileSizeBytes, maxFiles } = opts;
|
|
1416
|
+
try {
|
|
1417
|
+
ensureDir(filePath);
|
|
1418
|
+
const state = getState(filePath);
|
|
1419
|
+
const lineBytes = Buffer.byteLength(line + `
|
|
1420
|
+
`, "utf8");
|
|
1421
|
+
if (!state.primed || state.deltaSinceStat >= STAT_DELTA_THRESHOLD_BYTES || state.trackedSize + state.deltaSinceStat >= maxFileSizeBytes) {
|
|
1422
|
+
restat(filePath, state);
|
|
1423
|
+
}
|
|
1424
|
+
if (maxFileSizeBytes > 0 && state.trackedSize >= maxFileSizeBytes) {
|
|
1425
|
+
rotate(filePath, maxFiles);
|
|
1426
|
+
state.trackedSize = 0;
|
|
1427
|
+
state.deltaSinceStat = 0;
|
|
1428
|
+
state.primed = true;
|
|
1429
|
+
}
|
|
1430
|
+
fs2.appendFileSync(filePath, line + `
|
|
1431
|
+
`);
|
|
1432
|
+
state.trackedSize += lineBytes;
|
|
1433
|
+
state.deltaSinceStat += lineBytes;
|
|
1434
|
+
} catch (err) {
|
|
1435
|
+
lastError = err;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
// ../../packages/shared/dist/utils/log-buffer.js
|
|
1440
|
+
var DEFAULT_CAPACITY = 2000;
|
|
1441
|
+
|
|
1442
|
+
class LogBufferImpl {
|
|
1443
|
+
capacity = DEFAULT_CAPACITY;
|
|
1444
|
+
entries = [];
|
|
1445
|
+
subscribers = new Set;
|
|
1446
|
+
nextSeq = 0;
|
|
1447
|
+
dispatching = false;
|
|
1448
|
+
pending = [];
|
|
1449
|
+
push(entry) {
|
|
1450
|
+
if (this.dispatching) {
|
|
1451
|
+
this.pending.push(entry);
|
|
1452
|
+
return;
|
|
1453
|
+
}
|
|
1454
|
+
this.dispatching = true;
|
|
1455
|
+
try {
|
|
1456
|
+
this.pushOne(entry);
|
|
1457
|
+
while (this.pending.length > 0) {
|
|
1458
|
+
const next = this.pending.shift();
|
|
1459
|
+
this.pushOne(next);
|
|
1460
|
+
}
|
|
1461
|
+
} finally {
|
|
1462
|
+
this.dispatching = false;
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
pushOne(entry) {
|
|
1466
|
+
const full = { ...entry, seq: this.nextSeq++ };
|
|
1467
|
+
this.entries.push(full);
|
|
1468
|
+
while (this.entries.length > this.capacity) {
|
|
1469
|
+
this.entries.shift();
|
|
1470
|
+
}
|
|
1471
|
+
for (const fn of this.subscribers) {
|
|
1472
|
+
try {
|
|
1473
|
+
fn(full);
|
|
1474
|
+
} catch {}
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
snapshot(opts) {
|
|
1478
|
+
const from = opts?.from;
|
|
1479
|
+
const to = opts?.to;
|
|
1480
|
+
const level = opts?.level;
|
|
1481
|
+
const q = opts?.q?.toLowerCase();
|
|
1482
|
+
const result = [];
|
|
1483
|
+
for (let i = this.entries.length - 1;i >= 0; i--) {
|
|
1484
|
+
const e = this.entries[i];
|
|
1485
|
+
if (from !== undefined && e.seq < from)
|
|
1486
|
+
continue;
|
|
1487
|
+
if (to !== undefined && e.seq > to)
|
|
1488
|
+
continue;
|
|
1489
|
+
if (level !== undefined && e.level !== level)
|
|
1490
|
+
continue;
|
|
1491
|
+
if (q !== undefined && !e.message.toLowerCase().includes(q))
|
|
1492
|
+
continue;
|
|
1493
|
+
result.push(e);
|
|
1494
|
+
}
|
|
1495
|
+
return result;
|
|
1496
|
+
}
|
|
1497
|
+
subscribe(fn) {
|
|
1498
|
+
this.subscribers.add(fn);
|
|
1499
|
+
return () => {
|
|
1500
|
+
this.subscribers.delete(fn);
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
setCapacity(n) {
|
|
1504
|
+
this.capacity = n > 0 ? n : 0;
|
|
1505
|
+
while (this.entries.length > this.capacity) {
|
|
1506
|
+
this.entries.shift();
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
size() {
|
|
1510
|
+
return this.entries.length;
|
|
1511
|
+
}
|
|
1512
|
+
_resetForTesting() {
|
|
1513
|
+
this.entries = [];
|
|
1514
|
+
this.subscribers.clear();
|
|
1515
|
+
this.nextSeq = 0;
|
|
1516
|
+
this.dispatching = false;
|
|
1517
|
+
this.pending = [];
|
|
1518
|
+
this.capacity = DEFAULT_CAPACITY;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
var logBuffer = new LogBufferImpl;
|
|
1522
|
+
|
|
1523
|
+
// ../../packages/shared/dist/utils/logger.js
|
|
1293
1524
|
var LogLevel;
|
|
1294
1525
|
(function(LogLevel2) {
|
|
1295
1526
|
LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
|
|
@@ -1297,11 +1528,26 @@ var LogLevel;
|
|
|
1297
1528
|
LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
|
|
1298
1529
|
LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
|
|
1299
1530
|
})(LogLevel || (LogLevel = {}));
|
|
1531
|
+
var LOG_LEVEL_LABELS = {
|
|
1532
|
+
[LogLevel.DEBUG]: "DEBUG",
|
|
1533
|
+
[LogLevel.INFO]: "INFO",
|
|
1534
|
+
[LogLevel.WARN]: "WARN",
|
|
1535
|
+
[LogLevel.ERROR]: "ERROR"
|
|
1536
|
+
};
|
|
1537
|
+
var LOG_LEVEL_BUFFER_TAGS = {
|
|
1538
|
+
[LogLevel.DEBUG]: "debug",
|
|
1539
|
+
[LogLevel.INFO]: "info",
|
|
1540
|
+
[LogLevel.WARN]: "warn",
|
|
1541
|
+
[LogLevel.ERROR]: "error"
|
|
1542
|
+
};
|
|
1300
1543
|
|
|
1301
1544
|
class Logger {
|
|
1302
1545
|
_level;
|
|
1303
1546
|
_enableMetrics;
|
|
1304
1547
|
_logFilePath;
|
|
1548
|
+
_enableFileSink;
|
|
1549
|
+
_maxFileSizeBytes;
|
|
1550
|
+
_maxFiles;
|
|
1305
1551
|
_initialized = false;
|
|
1306
1552
|
constructor() {}
|
|
1307
1553
|
ensureInitialized() {
|
|
@@ -1311,10 +1557,18 @@ class Logger {
|
|
|
1311
1557
|
this._level = this.parseLogLevel(loggingConfig.level);
|
|
1312
1558
|
this._enableMetrics = loggingConfig.enableMetrics;
|
|
1313
1559
|
this._logFilePath = loggingConfig.file;
|
|
1560
|
+
this._enableFileSink = loggingConfig.enableFileSink;
|
|
1561
|
+
this._maxFileSizeBytes = loggingConfig.maxFileSizeMb * 1024 * 1024;
|
|
1562
|
+
this._maxFiles = loggingConfig.maxFiles;
|
|
1563
|
+
logBuffer.setCapacity(loggingConfig.bufferSize);
|
|
1314
1564
|
} catch {
|
|
1315
1565
|
this._level = LogLevel.INFO;
|
|
1316
1566
|
this._enableMetrics = false;
|
|
1317
1567
|
this._logFilePath = undefined;
|
|
1568
|
+
this._enableFileSink = false;
|
|
1569
|
+
this._maxFileSizeBytes = 32 * 1024 * 1024;
|
|
1570
|
+
this._maxFiles = 5;
|
|
1571
|
+
logBuffer.setCapacity(2000);
|
|
1318
1572
|
}
|
|
1319
1573
|
this._initialized = true;
|
|
1320
1574
|
}
|
|
@@ -1331,6 +1585,18 @@ class Logger {
|
|
|
1331
1585
|
this.ensureInitialized();
|
|
1332
1586
|
return this._logFilePath;
|
|
1333
1587
|
}
|
|
1588
|
+
get enableFileSink() {
|
|
1589
|
+
this.ensureInitialized();
|
|
1590
|
+
return this._enableFileSink;
|
|
1591
|
+
}
|
|
1592
|
+
get maxFileSizeBytes() {
|
|
1593
|
+
this.ensureInitialized();
|
|
1594
|
+
return this._maxFileSizeBytes;
|
|
1595
|
+
}
|
|
1596
|
+
get maxFiles() {
|
|
1597
|
+
this.ensureInitialized();
|
|
1598
|
+
return this._maxFiles;
|
|
1599
|
+
}
|
|
1334
1600
|
parseLogLevel(level) {
|
|
1335
1601
|
const levels = {
|
|
1336
1602
|
debug: LogLevel.DEBUG,
|
|
@@ -1343,34 +1609,40 @@ class Logger {
|
|
|
1343
1609
|
shouldLog(level) {
|
|
1344
1610
|
return level >= this.level;
|
|
1345
1611
|
}
|
|
1346
|
-
formatMessage(level, message, meta) {
|
|
1347
|
-
const timestamp = new Date().toISOString();
|
|
1612
|
+
formatMessage(level, message, meta, timestamp = new Date().toISOString()) {
|
|
1348
1613
|
const metaStr = meta ? ` ${JSON.stringify(meta)}` : "";
|
|
1349
1614
|
return `[${timestamp}] [${level}] ${message}${metaStr}`;
|
|
1350
1615
|
}
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
const
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1616
|
+
emit(level, message, meta) {
|
|
1617
|
+
const ts = new Date().toISOString();
|
|
1618
|
+
const line = this.formatMessage(LOG_LEVEL_LABELS[level], message, meta, ts);
|
|
1619
|
+
console.error(line);
|
|
1620
|
+
if (this.enableFileSink) {
|
|
1621
|
+
const filePath = this.logFilePath;
|
|
1622
|
+
if (filePath) {
|
|
1623
|
+
appendLine({ filePath, maxFileSizeBytes: this.maxFileSizeBytes, maxFiles: this.maxFiles }, line);
|
|
1624
|
+
}
|
|
1359
1625
|
}
|
|
1626
|
+
logBuffer.push({
|
|
1627
|
+
ts,
|
|
1628
|
+
level: LOG_LEVEL_BUFFER_TAGS[level],
|
|
1629
|
+
message,
|
|
1630
|
+
...meta ? { meta } : {}
|
|
1631
|
+
});
|
|
1360
1632
|
}
|
|
1361
1633
|
debug(message, meta) {
|
|
1362
1634
|
if (this.shouldLog(LogLevel.DEBUG)) {
|
|
1363
|
-
this.
|
|
1635
|
+
this.emit(LogLevel.DEBUG, message, meta);
|
|
1364
1636
|
}
|
|
1365
1637
|
}
|
|
1366
1638
|
info(message, meta) {
|
|
1367
1639
|
if (this.shouldLog(LogLevel.INFO)) {
|
|
1368
|
-
this.
|
|
1640
|
+
this.emit(LogLevel.INFO, message, meta);
|
|
1369
1641
|
}
|
|
1370
1642
|
}
|
|
1371
1643
|
warn(message, meta) {
|
|
1372
1644
|
if (this.shouldLog(LogLevel.WARN)) {
|
|
1373
|
-
this.
|
|
1645
|
+
this.emit(LogLevel.WARN, message, meta);
|
|
1374
1646
|
}
|
|
1375
1647
|
}
|
|
1376
1648
|
error(message, error, meta) {
|
|
@@ -1383,7 +1655,7 @@ class Logger {
|
|
|
1383
1655
|
stack: error.stack
|
|
1384
1656
|
}
|
|
1385
1657
|
} : meta;
|
|
1386
|
-
this.
|
|
1658
|
+
this.emit(LogLevel.ERROR, message, errorMeta);
|
|
1387
1659
|
}
|
|
1388
1660
|
}
|
|
1389
1661
|
metric(name, value, unit) {
|
|
@@ -1507,7 +1779,7 @@ class SmartRateLimiter {
|
|
|
1507
1779
|
var rateLimiter = new SmartRateLimiter;
|
|
1508
1780
|
// ../../packages/shared/dist/profile-switch/hosts.js
|
|
1509
1781
|
import os3 from "os";
|
|
1510
|
-
import
|
|
1782
|
+
import path6 from "path";
|
|
1511
1783
|
var HOSTS = ["claude", "codex", "cursor", "opencode"];
|
|
1512
1784
|
function isHost(v) {
|
|
1513
1785
|
return typeof v === "string" && HOSTS.includes(v);
|
|
@@ -1520,7 +1792,7 @@ function fileLayout(host, activeDir, activeGlob, variantsRoot) {
|
|
|
1520
1792
|
activeDir,
|
|
1521
1793
|
activeGlob,
|
|
1522
1794
|
variantsRoot,
|
|
1523
|
-
variantDir: (profile) =>
|
|
1795
|
+
variantDir: (profile) => path6.join(variantsRoot, profile)
|
|
1524
1796
|
};
|
|
1525
1797
|
}
|
|
1526
1798
|
function resolveHostLayout(host, opts = {}) {
|
|
@@ -1530,25 +1802,37 @@ function resolveHostLayout(host, opts = {}) {
|
|
|
1530
1802
|
case "cursor":
|
|
1531
1803
|
return { host, route: "skip", reason: CURSOR_SKIP_REASON };
|
|
1532
1804
|
case "claude": {
|
|
1533
|
-
const
|
|
1534
|
-
|
|
1805
|
+
const marketplaceRoot = opts.marketplaceRoot?.claude;
|
|
1806
|
+
if (override === undefined && marketplaceRoot !== undefined) {
|
|
1807
|
+
return fileLayout(host, path6.join(marketplaceRoot, "agents"), "massa-ai-*.md", path6.join(marketplaceRoot, "agent-profiles"));
|
|
1808
|
+
}
|
|
1809
|
+
const root = override ?? path6.join(targetHome, ".claude");
|
|
1810
|
+
return fileLayout(host, path6.join(root, "agents"), "massa-ai-*.md", path6.join(root, "massa-ai", "agent-profiles"));
|
|
1535
1811
|
}
|
|
1536
1812
|
case "codex": {
|
|
1537
|
-
const root = override ??
|
|
1538
|
-
return fileLayout(host,
|
|
1813
|
+
const root = override ?? path6.join(targetHome, ".codex");
|
|
1814
|
+
return fileLayout(host, path6.join(root, "agents"), "massa-ai-*.toml", path6.join(root, "massa-ai", "agent-profiles"));
|
|
1539
1815
|
}
|
|
1540
1816
|
case "opencode": {
|
|
1541
|
-
const root = override ??
|
|
1542
|
-
const pluginsDir =
|
|
1543
|
-
return fileLayout(host,
|
|
1817
|
+
const root = override ?? path6.join(targetHome, ".config", "opencode");
|
|
1818
|
+
const pluginsDir = path6.join(root, "plugins", "massa-ai");
|
|
1819
|
+
return fileLayout(host, path6.join(root, "agents"), "massa-ai-*.md", path6.join(pluginsDir, "agent-profiles"));
|
|
1544
1820
|
}
|
|
1545
1821
|
}
|
|
1546
1822
|
}
|
|
1547
|
-
function detectRoute(platform) {
|
|
1823
|
+
function detectRoute(platform, host) {
|
|
1548
1824
|
const route = platform?.installRoute;
|
|
1549
1825
|
if (route === "file")
|
|
1550
1826
|
return { kind: "proceed" };
|
|
1551
1827
|
if (route === "marketplace") {
|
|
1828
|
+
if (host === "claude")
|
|
1829
|
+
return { kind: "proceed" };
|
|
1830
|
+
if (host === "codex") {
|
|
1831
|
+
return {
|
|
1832
|
+
kind: "refuse",
|
|
1833
|
+
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."
|
|
1834
|
+
};
|
|
1835
|
+
}
|
|
1552
1836
|
return {
|
|
1553
1837
|
kind: "refuse",
|
|
1554
1838
|
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 +1845,7 @@ function detectRoute(platform) {
|
|
|
1561
1845
|
}
|
|
1562
1846
|
// ../../packages/shared/dist/profile-switch/state.js
|
|
1563
1847
|
import fs3 from "fs";
|
|
1564
|
-
import
|
|
1848
|
+
import path7 from "path";
|
|
1565
1849
|
|
|
1566
1850
|
class InstallStateError extends Error {
|
|
1567
1851
|
constructor(message) {
|
|
@@ -1617,7 +1901,7 @@ function writeInstallState(filePath, state) {
|
|
|
1617
1901
|
const text = `${JSON.stringify(validated, null, 2)}
|
|
1618
1902
|
`;
|
|
1619
1903
|
try {
|
|
1620
|
-
fs3.mkdirSync(
|
|
1904
|
+
fs3.mkdirSync(path7.dirname(filePath), { recursive: true });
|
|
1621
1905
|
fs3.writeFileSync(filePath, text);
|
|
1622
1906
|
} catch (err) {
|
|
1623
1907
|
throw UnwritableInstallStateError(filePath, err.message);
|
|
@@ -1636,7 +1920,7 @@ function updatePlatform(filePath, host, patch) {
|
|
|
1636
1920
|
}
|
|
1637
1921
|
// ../../packages/shared/dist/profile-switch/lock.js
|
|
1638
1922
|
import fs4 from "fs";
|
|
1639
|
-
import
|
|
1923
|
+
import path8 from "path";
|
|
1640
1924
|
import os4 from "os";
|
|
1641
1925
|
import crypto2 from "crypto";
|
|
1642
1926
|
import { execFileSync } from "child_process";
|
|
@@ -1693,7 +1977,7 @@ function releaseIfOwned(lockDir, ownerPath, token) {
|
|
|
1693
1977
|
}
|
|
1694
1978
|
function acquireLock(stateFilePath, options = {}) {
|
|
1695
1979
|
const lockDir = `${stateFilePath}.switch.lock`;
|
|
1696
|
-
const ownerPath =
|
|
1980
|
+
const ownerPath = path8.join(lockDir, "owner.json");
|
|
1697
1981
|
const clock = options.clock ?? DEFAULT_CLOCK;
|
|
1698
1982
|
const identity = options.identity ?? DEFAULT_IDENTITY;
|
|
1699
1983
|
const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
|
|
@@ -1713,7 +1997,7 @@ function acquireLock(stateFilePath, options = {}) {
|
|
|
1713
1997
|
token,
|
|
1714
1998
|
timestamp: clock.now()
|
|
1715
1999
|
};
|
|
1716
|
-
fs4.mkdirSync(
|
|
2000
|
+
fs4.mkdirSync(path8.dirname(ownerPath), { recursive: true });
|
|
1717
2001
|
fs4.writeFileSync(ownerPath, JSON.stringify(record));
|
|
1718
2002
|
return { lockDir, release: () => releaseIfOwned(lockDir, ownerPath, token) };
|
|
1719
2003
|
};
|
|
@@ -1741,10 +2025,60 @@ function acquireLock(stateFilePath, options = {}) {
|
|
|
1741
2025
|
}
|
|
1742
2026
|
}
|
|
1743
2027
|
// ../../packages/shared/dist/profile-switch/engine.js
|
|
2028
|
+
import fs6 from "fs";
|
|
2029
|
+
import path10 from "path";
|
|
2030
|
+
import os6 from "os";
|
|
2031
|
+
import crypto3 from "crypto";
|
|
2032
|
+
|
|
2033
|
+
// ../../packages/shared/dist/profile-switch/claude-marketplace.js
|
|
1744
2034
|
import fs5 from "fs";
|
|
1745
|
-
import path8 from "path";
|
|
1746
2035
|
import os5 from "os";
|
|
1747
|
-
import
|
|
2036
|
+
import path9 from "path";
|
|
2037
|
+
var DEFAULT_PLUGIN_KEY = "massa-ai@massa-ai";
|
|
2038
|
+
function selectRecord(records) {
|
|
2039
|
+
if (records.length === 0)
|
|
2040
|
+
return;
|
|
2041
|
+
const userScoped = records.filter((r) => r.scope === "user");
|
|
2042
|
+
const pool = userScoped.length > 0 ? userScoped : records;
|
|
2043
|
+
let best;
|
|
2044
|
+
let bestTime = -Infinity;
|
|
2045
|
+
for (const record of pool) {
|
|
2046
|
+
const parsed = record.lastUpdated ? Date.parse(record.lastUpdated) : NaN;
|
|
2047
|
+
if (Number.isFinite(parsed) && parsed >= bestTime) {
|
|
2048
|
+
best = record;
|
|
2049
|
+
bestTime = parsed;
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
return best ?? pool[pool.length - 1];
|
|
2053
|
+
}
|
|
2054
|
+
function resolveClaudeMarketplaceRoot(opts = {}) {
|
|
2055
|
+
const targetHome = opts.targetHome ?? os5.homedir();
|
|
2056
|
+
const pluginKey = opts.pluginKey ?? DEFAULT_PLUGIN_KEY;
|
|
2057
|
+
const registryPath = path9.join(targetHome, ".claude", "plugins", "installed_plugins.json");
|
|
2058
|
+
let records;
|
|
2059
|
+
try {
|
|
2060
|
+
const raw = fs5.readFileSync(registryPath, "utf8");
|
|
2061
|
+
const parsed = JSON.parse(raw);
|
|
2062
|
+
records = parsed?.plugins?.[pluginKey];
|
|
2063
|
+
} catch {
|
|
2064
|
+
return null;
|
|
2065
|
+
}
|
|
2066
|
+
if (!Array.isArray(records) || records.length === 0)
|
|
2067
|
+
return null;
|
|
2068
|
+
const selected = selectRecord(records);
|
|
2069
|
+
const installPath = selected?.installPath;
|
|
2070
|
+
if (!installPath)
|
|
2071
|
+
return null;
|
|
2072
|
+
try {
|
|
2073
|
+
if (!fs5.existsSync(installPath))
|
|
2074
|
+
return null;
|
|
2075
|
+
} catch {
|
|
2076
|
+
return null;
|
|
2077
|
+
}
|
|
2078
|
+
return installPath;
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
// ../../packages/shared/dist/profile-switch/engine.js
|
|
1748
2082
|
class SwitchEngineError extends Error {
|
|
1749
2083
|
constructor(message) {
|
|
1750
2084
|
super(message);
|
|
@@ -1759,19 +2093,39 @@ function namedError3(name, message) {
|
|
|
1759
2093
|
var UnknownProfileError = (profile, known) => namedError3("UnknownProfileError", `unknown profile "${profile}" \u2014 installed: ${known.length > 0 ? known.join(", ") : "none"}`);
|
|
1760
2094
|
var NoHostsDetectedError = () => namedError3("NoHostsDetectedError", "no installed hosts found");
|
|
1761
2095
|
function defaultStatePath(targetHome) {
|
|
1762
|
-
return
|
|
2096
|
+
return path10.join(targetHome, ".config", "massa-ai", "install-state.json");
|
|
1763
2097
|
}
|
|
1764
2098
|
function resolveCommon(opts) {
|
|
1765
|
-
const targetHome = opts.targetHome ??
|
|
2099
|
+
const targetHome = opts.targetHome ?? os6.homedir();
|
|
1766
2100
|
const stateFilePath = opts.stateFilePath ?? defaultStatePath(targetHome);
|
|
1767
2101
|
return { targetHome, stateFilePath };
|
|
1768
2102
|
}
|
|
2103
|
+
function marketplaceRoots(targetHome, state) {
|
|
2104
|
+
return state.platforms.claude?.installRoute === "marketplace" ? { claude: resolveClaudeMarketplaceRoot({ targetHome }) ?? undefined } : {};
|
|
2105
|
+
}
|
|
2106
|
+
function claudeMarketplaceUnresolvedReason(targetHome) {
|
|
2107
|
+
const registryPath = path10.join(targetHome, ".claude", "plugins", "installed_plugins.json");
|
|
2108
|
+
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";
|
|
2109
|
+
}
|
|
1769
2110
|
function listProfiles(opts = {}) {
|
|
1770
2111
|
const { targetHome, stateFilePath } = resolveCommon(opts);
|
|
1771
2112
|
const state = readInstallState(stateFilePath);
|
|
2113
|
+
const roots = marketplaceRoots(targetHome, state);
|
|
1772
2114
|
const universe = opts.hosts ?? HOSTS;
|
|
1773
2115
|
const hosts = universe.map((host) => {
|
|
1774
|
-
|
|
2116
|
+
if (host === "claude" && state.platforms.claude?.installRoute === "marketplace" && roots.claude === undefined) {
|
|
2117
|
+
const platform2 = state.platforms.claude;
|
|
2118
|
+
return {
|
|
2119
|
+
host,
|
|
2120
|
+
installed: false,
|
|
2121
|
+
skipped: false,
|
|
2122
|
+
skipReason: null,
|
|
2123
|
+
activeProfile: platform2.modelProfile?.profile ?? opts.hostDefaults?.[host] ?? "balanced",
|
|
2124
|
+
bundleVersion: platform2.plugin?.version ?? null,
|
|
2125
|
+
availableProfiles: []
|
|
2126
|
+
};
|
|
2127
|
+
}
|
|
2128
|
+
const layout = resolveHostLayout(host, { targetHome, projectRoot: opts.projectRoot, marketplaceRoot: roots });
|
|
1775
2129
|
if (layout.route === "skip") {
|
|
1776
2130
|
return {
|
|
1777
2131
|
host,
|
|
@@ -1783,7 +2137,7 @@ function listProfiles(opts = {}) {
|
|
|
1783
2137
|
availableProfiles: []
|
|
1784
2138
|
};
|
|
1785
2139
|
}
|
|
1786
|
-
const installed =
|
|
2140
|
+
const installed = fs6.existsSync(layout.activeDir);
|
|
1787
2141
|
const availableProfiles = listVariantProfiles(layout);
|
|
1788
2142
|
const platform = state.platforms[host];
|
|
1789
2143
|
return {
|
|
@@ -1799,9 +2153,9 @@ function listProfiles(opts = {}) {
|
|
|
1799
2153
|
return { hosts };
|
|
1800
2154
|
}
|
|
1801
2155
|
function listVariantProfiles(layout) {
|
|
1802
|
-
if (!
|
|
2156
|
+
if (!fs6.existsSync(layout.variantsRoot))
|
|
1803
2157
|
return [];
|
|
1804
|
-
return
|
|
2158
|
+
return fs6.readdirSync(layout.variantsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
1805
2159
|
}
|
|
1806
2160
|
function matchesGlob(filename, glob) {
|
|
1807
2161
|
const starIdx = glob.indexOf("*");
|
|
@@ -1812,50 +2166,50 @@ function matchesGlob(filename, glob) {
|
|
|
1812
2166
|
return filename.length >= prefix.length + suffix.length && filename.startsWith(prefix) && filename.endsWith(suffix);
|
|
1813
2167
|
}
|
|
1814
2168
|
function assertStateWritable(stateFilePath) {
|
|
1815
|
-
const dir =
|
|
2169
|
+
const dir = path10.dirname(stateFilePath);
|
|
1816
2170
|
try {
|
|
1817
|
-
|
|
2171
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
1818
2172
|
} catch (err) {
|
|
1819
2173
|
throw UnwritableInstallStateError(stateFilePath, err.message);
|
|
1820
2174
|
}
|
|
1821
|
-
const checkPath =
|
|
2175
|
+
const checkPath = fs6.existsSync(stateFilePath) ? stateFilePath : dir;
|
|
1822
2176
|
try {
|
|
1823
|
-
|
|
2177
|
+
fs6.accessSync(checkPath, fs6.constants.W_OK);
|
|
1824
2178
|
} catch (err) {
|
|
1825
2179
|
throw UnwritableInstallStateError(stateFilePath, err.message);
|
|
1826
2180
|
}
|
|
1827
2181
|
}
|
|
1828
2182
|
function copyFileRouteVariant(layout, variantDir) {
|
|
1829
|
-
|
|
2183
|
+
fs6.mkdirSync(layout.activeDir, { recursive: true });
|
|
1830
2184
|
let changed = 0;
|
|
1831
|
-
for (const entry of
|
|
2185
|
+
for (const entry of fs6.readdirSync(variantDir, { withFileTypes: true })) {
|
|
1832
2186
|
if (!entry.isFile() || !matchesGlob(entry.name, layout.activeGlob))
|
|
1833
2187
|
continue;
|
|
1834
|
-
|
|
2188
|
+
fs6.copyFileSync(path10.join(variantDir, entry.name), path10.join(layout.activeDir, entry.name));
|
|
1835
2189
|
changed++;
|
|
1836
2190
|
}
|
|
1837
2191
|
return changed;
|
|
1838
2192
|
}
|
|
1839
2193
|
function repointOpencodeVariant(layout, variantDir) {
|
|
1840
|
-
|
|
2194
|
+
fs6.mkdirSync(layout.activeDir, { recursive: true });
|
|
1841
2195
|
let changed = 0;
|
|
1842
|
-
for (const entry of
|
|
2196
|
+
for (const entry of fs6.readdirSync(variantDir, { withFileTypes: true })) {
|
|
1843
2197
|
if (!entry.isFile() || !matchesGlob(entry.name, layout.activeGlob))
|
|
1844
2198
|
continue;
|
|
1845
|
-
const dest =
|
|
1846
|
-
const target =
|
|
2199
|
+
const dest = path10.join(layout.activeDir, entry.name);
|
|
2200
|
+
const target = path10.resolve(path10.join(variantDir, entry.name));
|
|
1847
2201
|
let destExists = true;
|
|
1848
2202
|
let destIsSymlink = false;
|
|
1849
2203
|
try {
|
|
1850
|
-
destIsSymlink =
|
|
2204
|
+
destIsSymlink = fs6.lstatSync(dest).isSymbolicLink();
|
|
1851
2205
|
} catch {
|
|
1852
2206
|
destExists = false;
|
|
1853
2207
|
}
|
|
1854
2208
|
if (destExists && !destIsSymlink)
|
|
1855
2209
|
continue;
|
|
1856
2210
|
const tmp = `${dest}.massa-ai-switch.${crypto3.randomUUID()}`;
|
|
1857
|
-
|
|
1858
|
-
|
|
2211
|
+
fs6.symlinkSync(target, tmp);
|
|
2212
|
+
fs6.renameSync(tmp, dest);
|
|
1859
2213
|
changed++;
|
|
1860
2214
|
}
|
|
1861
2215
|
return changed;
|
|
@@ -1871,19 +2225,37 @@ function switchProfile(opts) {
|
|
|
1871
2225
|
const state = readInstallState(stateFilePath);
|
|
1872
2226
|
if (!dryRun)
|
|
1873
2227
|
assertStateWritable(stateFilePath);
|
|
1874
|
-
const
|
|
1875
|
-
const
|
|
2228
|
+
const roots = marketplaceRoots(targetHome, state);
|
|
2229
|
+
const unresolvedRows = [];
|
|
2230
|
+
const resolvableUniverse = universe.filter((host) => {
|
|
2231
|
+
if (host !== "claude")
|
|
2232
|
+
return true;
|
|
2233
|
+
if (state.platforms.claude?.installRoute !== "marketplace")
|
|
2234
|
+
return true;
|
|
2235
|
+
if (roots.claude !== undefined)
|
|
2236
|
+
return true;
|
|
2237
|
+
unresolvedRows.push({ host, status: "failed", reason: claudeMarketplaceUnresolvedReason(targetHome) });
|
|
2238
|
+
return false;
|
|
2239
|
+
});
|
|
2240
|
+
const layouts = resolvableUniverse.map((host) => ({
|
|
2241
|
+
host,
|
|
2242
|
+
layout: resolveHostLayout(host, { targetHome, projectRoot: opts.projectRoot, marketplaceRoot: roots })
|
|
2243
|
+
}));
|
|
2244
|
+
const skipRows = [
|
|
2245
|
+
...unresolvedRows,
|
|
2246
|
+
...layouts.filter((l) => l.layout.route === "skip").map((l) => ({ host: l.host, status: "skipped", reason: l.layout.reason }))
|
|
2247
|
+
];
|
|
1876
2248
|
const fileHosts = layouts.filter((l) => l.layout.route === "files");
|
|
1877
2249
|
if (fileHosts.length === 0) {
|
|
1878
2250
|
return { profile: opts.profile, dryRun, hosts: orderRows(universe, skipRows), restartRequired: false };
|
|
1879
2251
|
}
|
|
1880
|
-
const installedFileHosts = fileHosts.filter((h) =>
|
|
2252
|
+
const installedFileHosts = fileHosts.filter((h) => fs6.existsSync(h.layout.activeDir));
|
|
1881
2253
|
if (installedFileHosts.length === 0)
|
|
1882
2254
|
throw NoHostsDetectedError();
|
|
1883
2255
|
const withAvailability = fileHosts.map((h) => {
|
|
1884
|
-
const variantsRootExists =
|
|
2256
|
+
const variantsRootExists = fs6.existsSync(h.layout.variantsRoot);
|
|
1885
2257
|
const variantDir = h.layout.variantDir(opts.profile);
|
|
1886
|
-
const available = variantsRootExists &&
|
|
2258
|
+
const available = variantsRootExists && fs6.existsSync(variantDir) && fs6.statSync(variantDir).isDirectory();
|
|
1887
2259
|
return { ...h, variantsRootExists, variantDir, available };
|
|
1888
2260
|
});
|
|
1889
2261
|
if (!withAvailability.some((h) => h.available)) {
|
|
@@ -1913,7 +2285,7 @@ function switchProfile(opts) {
|
|
|
1913
2285
|
});
|
|
1914
2286
|
continue;
|
|
1915
2287
|
}
|
|
1916
|
-
const route = detectRoute(state.platforms[h.host]);
|
|
2288
|
+
const route = detectRoute(state.platforms[h.host], h.host);
|
|
1917
2289
|
if (route.kind === "refuse") {
|
|
1918
2290
|
rows.push({ host: h.host, status: "failed", reason: route.reason });
|
|
1919
2291
|
continue;
|
|
@@ -1948,19 +2320,26 @@ function reportSucceeded(report) {
|
|
|
1948
2320
|
return report.hosts.every((h) => h.status === "switched" || h.status === "skipped");
|
|
1949
2321
|
}
|
|
1950
2322
|
// ../../packages/shared/dist/profile-switch/variant-sync.js
|
|
1951
|
-
import
|
|
1952
|
-
import
|
|
2323
|
+
import fs7 from "fs";
|
|
2324
|
+
import path11 from "path";
|
|
2325
|
+
import os7 from "os";
|
|
1953
2326
|
import crypto4 from "crypto";
|
|
2327
|
+
function defaultStatePath2(targetHome) {
|
|
2328
|
+
return path11.join(targetHome, ".config", "massa-ai", "install-state.json");
|
|
2329
|
+
}
|
|
2330
|
+
function marketplaceRoots2(targetHome, state) {
|
|
2331
|
+
return state.platforms.claude?.installRoute === "marketplace" ? { claude: resolveClaudeMarketplaceRoot({ targetHome }) ?? undefined } : {};
|
|
2332
|
+
}
|
|
1954
2333
|
var tempFileCounter2 = 0;
|
|
1955
2334
|
function writeFileIntoDirAtomically(destDir, destName, content) {
|
|
1956
2335
|
const unique = `${process.pid}.${++tempFileCounter2}.${crypto4.randomBytes(6).toString("hex")}`;
|
|
1957
|
-
const tempFile =
|
|
2336
|
+
const tempFile = path11.join(destDir, `.${destName}.${unique}.tmp`);
|
|
1958
2337
|
try {
|
|
1959
|
-
|
|
1960
|
-
|
|
2338
|
+
fs7.writeFileSync(tempFile, content);
|
|
2339
|
+
fs7.renameSync(tempFile, path11.join(destDir, destName));
|
|
1961
2340
|
} catch (error) {
|
|
1962
2341
|
try {
|
|
1963
|
-
|
|
2342
|
+
fs7.unlinkSync(tempFile);
|
|
1964
2343
|
} catch {}
|
|
1965
2344
|
throw error;
|
|
1966
2345
|
}
|
|
@@ -1968,20 +2347,20 @@ function writeFileIntoDirAtomically(destDir, destName, content) {
|
|
|
1968
2347
|
function isSafeDirName(name) {
|
|
1969
2348
|
if (name === "." || name === "..")
|
|
1970
2349
|
return false;
|
|
1971
|
-
if (name.includes("/") || name.includes("\\") || name.includes(
|
|
2350
|
+
if (name.includes("/") || name.includes("\\") || name.includes(path11.sep))
|
|
1972
2351
|
return false;
|
|
1973
|
-
return
|
|
2352
|
+
return path11.basename(name) === name;
|
|
1974
2353
|
}
|
|
1975
|
-
function syncHost(host, sourceRoot, targetHome) {
|
|
1976
|
-
const layout = resolveHostLayout(host, { targetHome });
|
|
2354
|
+
function syncHost(host, sourceRoot, targetHome, marketplaceRoot) {
|
|
2355
|
+
const layout = resolveHostLayout(host, { targetHome, marketplaceRoot });
|
|
1977
2356
|
if (layout.route === "skip") {
|
|
1978
2357
|
return { host, status: "skipped", profiles: [], retained: [], files: 0, reason: layout.reason };
|
|
1979
2358
|
}
|
|
1980
|
-
const srcDir =
|
|
1981
|
-
if (!
|
|
2359
|
+
const srcDir = path11.join(sourceRoot, "apps", `${host}-plugin`, "agent-profiles");
|
|
2360
|
+
if (!fs7.existsSync(srcDir) || !fs7.statSync(srcDir).isDirectory()) {
|
|
1982
2361
|
return { host, status: "skipped", profiles: [], retained: [], files: 0, reason: "no generated variants for this host" };
|
|
1983
2362
|
}
|
|
1984
|
-
if (!
|
|
2363
|
+
if (!fs7.existsSync(layout.variantsRoot)) {
|
|
1985
2364
|
return {
|
|
1986
2365
|
host,
|
|
1987
2366
|
status: "skipped",
|
|
@@ -1993,24 +2372,24 @@ function syncHost(host, sourceRoot, targetHome) {
|
|
|
1993
2372
|
}
|
|
1994
2373
|
const profiles = [];
|
|
1995
2374
|
let files = 0;
|
|
1996
|
-
for (const entry of
|
|
2375
|
+
for (const entry of fs7.readdirSync(srcDir, { withFileTypes: true })) {
|
|
1997
2376
|
if (!entry.isDirectory())
|
|
1998
2377
|
continue;
|
|
1999
2378
|
if (!isSafeDirName(entry.name))
|
|
2000
2379
|
continue;
|
|
2001
|
-
const srcProfileDir =
|
|
2002
|
-
const destProfileDir =
|
|
2003
|
-
|
|
2004
|
-
for (const fileEntry of
|
|
2380
|
+
const srcProfileDir = path11.join(srcDir, entry.name);
|
|
2381
|
+
const destProfileDir = path11.join(layout.variantsRoot, entry.name);
|
|
2382
|
+
fs7.mkdirSync(destProfileDir, { recursive: true });
|
|
2383
|
+
for (const fileEntry of fs7.readdirSync(srcProfileDir, { withFileTypes: true })) {
|
|
2005
2384
|
if (!fileEntry.isFile())
|
|
2006
2385
|
continue;
|
|
2007
|
-
const content =
|
|
2386
|
+
const content = fs7.readFileSync(path11.join(srcProfileDir, fileEntry.name));
|
|
2008
2387
|
writeFileIntoDirAtomically(destProfileDir, fileEntry.name, content);
|
|
2009
2388
|
files++;
|
|
2010
2389
|
}
|
|
2011
2390
|
profiles.push(entry.name);
|
|
2012
2391
|
}
|
|
2013
|
-
const retained =
|
|
2392
|
+
const retained = fs7.readdirSync(layout.variantsRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && !profiles.includes(e.name)).map((e) => e.name).sort();
|
|
2014
2393
|
return { host, status: "synced", profiles: profiles.sort(), retained, files };
|
|
2015
2394
|
}
|
|
2016
2395
|
function syncGeneratedVariants(opts) {
|
|
@@ -2026,23 +2405,26 @@ function syncGeneratedVariants(opts) {
|
|
|
2026
2405
|
}));
|
|
2027
2406
|
}
|
|
2028
2407
|
const sourceRoot = opts.sourceRoot;
|
|
2408
|
+
const targetHome = opts.targetHome ?? os7.homedir();
|
|
2409
|
+
const state = readInstallState(defaultStatePath2(targetHome));
|
|
2410
|
+
const roots = marketplaceRoots2(targetHome, state);
|
|
2029
2411
|
return hosts.map((host) => {
|
|
2030
2412
|
try {
|
|
2031
|
-
return syncHost(host, sourceRoot, opts.targetHome);
|
|
2413
|
+
return syncHost(host, sourceRoot, opts.targetHome, roots);
|
|
2032
2414
|
} catch (err) {
|
|
2033
2415
|
return { host, status: "failed", profiles: [], retained: [], files: 0, error: err.message };
|
|
2034
2416
|
}
|
|
2035
2417
|
});
|
|
2036
2418
|
}
|
|
2037
2419
|
// ../../packages/shared/dist/profile-switch/repo-root.js
|
|
2038
|
-
import
|
|
2039
|
-
import
|
|
2420
|
+
import fs8 from "fs";
|
|
2421
|
+
import path12 from "path";
|
|
2040
2422
|
function findRepoRootWithMarker(startDir, marker, maxLevels) {
|
|
2041
2423
|
let dir = startDir;
|
|
2042
2424
|
for (let i = 0;i <= maxLevels; i++) {
|
|
2043
|
-
if (
|
|
2425
|
+
if (fs8.existsSync(path12.join(dir, marker)))
|
|
2044
2426
|
return dir;
|
|
2045
|
-
const parent =
|
|
2427
|
+
const parent = path12.dirname(dir);
|
|
2046
2428
|
if (parent === dir)
|
|
2047
2429
|
break;
|
|
2048
2430
|
dir = parent;
|
|
@@ -2050,11 +2432,11 @@ function findRepoRootWithMarker(startDir, marker, maxLevels) {
|
|
|
2050
2432
|
return null;
|
|
2051
2433
|
}
|
|
2052
2434
|
// src/config-cli.ts
|
|
2053
|
-
import { promises as
|
|
2054
|
-
import
|
|
2055
|
-
import
|
|
2435
|
+
import { promises as fs9 } from "fs";
|
|
2436
|
+
import path13 from "path";
|
|
2437
|
+
import os8 from "os";
|
|
2056
2438
|
import { fileURLToPath } from "url";
|
|
2057
|
-
var __dirname2 =
|
|
2439
|
+
var __dirname2 = path13.dirname(fileURLToPath(import.meta.url));
|
|
2058
2440
|
var GENERATOR_MARKER = "scripts/generate-subagent-artifacts.ts";
|
|
2059
2441
|
var GENERATOR_MARKER_MAX_LEVELS = 6;
|
|
2060
2442
|
function formatVariantSync(results) {
|
|
@@ -2265,18 +2647,18 @@ Using defaults:`);
|
|
|
2265
2647
|
return 1;
|
|
2266
2648
|
}
|
|
2267
2649
|
const scope = typeof options.project === "boolean" ? "project" : "user";
|
|
2268
|
-
const agentsDir = scope === "project" ?
|
|
2269
|
-
const sourceAgentsDir =
|
|
2650
|
+
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");
|
|
2651
|
+
const sourceAgentsDir = path13.resolve(__dirname2, "..", "agents");
|
|
2270
2652
|
if (subcommand === "install") {
|
|
2271
|
-
await
|
|
2653
|
+
await fs9.mkdir(agentsDir, { recursive: true });
|
|
2272
2654
|
let count = 0;
|
|
2273
|
-
const entries = await
|
|
2655
|
+
const entries = await fs9.readdir(sourceAgentsDir);
|
|
2274
2656
|
for (const entry of entries) {
|
|
2275
2657
|
if (!entry.startsWith("massa-ai-") || !entry.endsWith(".md"))
|
|
2276
2658
|
continue;
|
|
2277
|
-
const src =
|
|
2278
|
-
const dest =
|
|
2279
|
-
await
|
|
2659
|
+
const src = path13.join(sourceAgentsDir, entry);
|
|
2660
|
+
const dest = path13.join(agentsDir, entry);
|
|
2661
|
+
await fs9.copyFile(src, dest);
|
|
2280
2662
|
count++;
|
|
2281
2663
|
}
|
|
2282
2664
|
console.log(`+ ${count} subagent specialists (generated from skills/agents/*/SKILL.md)`);
|
|
@@ -2284,14 +2666,14 @@ Using defaults:`);
|
|
|
2284
2666
|
} else {
|
|
2285
2667
|
let removed = 0;
|
|
2286
2668
|
try {
|
|
2287
|
-
const entries = await
|
|
2669
|
+
const entries = await fs9.readdir(agentsDir);
|
|
2288
2670
|
for (const entry of entries) {
|
|
2289
2671
|
if (!entry.startsWith("massa-ai-") || !entry.endsWith(".md"))
|
|
2290
2672
|
continue;
|
|
2291
|
-
const filePath =
|
|
2292
|
-
const content = await
|
|
2673
|
+
const filePath = path13.join(agentsDir, entry);
|
|
2674
|
+
const content = await fs9.readFile(filePath, "utf8");
|
|
2293
2675
|
if (content.includes("massa-ai-owned: true")) {
|
|
2294
|
-
await
|
|
2676
|
+
await fs9.unlink(filePath);
|
|
2295
2677
|
removed++;
|
|
2296
2678
|
}
|
|
2297
2679
|
}
|
package/dist/index.js
CHANGED
|
@@ -400,9 +400,16 @@ var init_xdg = () => {};
|
|
|
400
400
|
|
|
401
401
|
// ../../packages/shared/dist/config/massa-ai-config.js
|
|
402
402
|
import path2 from "path";
|
|
403
|
-
var defaultMassaAiConfig;
|
|
403
|
+
var SCHEDULER_JOB_KINDS, defaultMassaAiConfig;
|
|
404
404
|
var init_massa_ai_config = __esm(() => {
|
|
405
405
|
init_xdg();
|
|
406
|
+
SCHEDULER_JOB_KINDS = [
|
|
407
|
+
"memory-consolidation",
|
|
408
|
+
"decay-sweep",
|
|
409
|
+
"auto-improve",
|
|
410
|
+
"observation-bridge",
|
|
411
|
+
"checkpoint-purge"
|
|
412
|
+
];
|
|
406
413
|
defaultMassaAiConfig = {
|
|
407
414
|
database: {
|
|
408
415
|
url: ""
|
|
@@ -733,11 +740,13 @@ try {
|
|
|
733
740
|
// ../../packages/shared/dist/config/index.js
|
|
734
741
|
init_config_loader();
|
|
735
742
|
init_massa_ai_config();
|
|
743
|
+
init_massa_ai_config();
|
|
736
744
|
init_config_loader();
|
|
737
745
|
import path4 from "path";
|
|
738
746
|
|
|
739
747
|
// ../../packages/shared/dist/config/config-writer.js
|
|
740
748
|
init_config_loader();
|
|
749
|
+
init_massa_ai_config();
|
|
741
750
|
|
|
742
751
|
// ../../packages/shared/dist/config/index.js
|
|
743
752
|
init_xdg();
|
|
@@ -777,6 +786,49 @@ function envList(key, fallback) {
|
|
|
777
786
|
const parsed = s.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
|
|
778
787
|
return parsed.length > 0 ? parsed : fallback;
|
|
779
788
|
}
|
|
789
|
+
function readSchedulerConfig(rawFileConfig) {
|
|
790
|
+
try {
|
|
791
|
+
const cfg = rawFileConfig;
|
|
792
|
+
return cfg?.scheduler ?? {};
|
|
793
|
+
} catch {
|
|
794
|
+
return {};
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
var SCHEDULER_JOB_ENV = {
|
|
798
|
+
"memory-consolidation": {
|
|
799
|
+
enabledVar: "MASSA_AI_SCHEDULER_CONSOLIDATION_ENABLED",
|
|
800
|
+
intervalVar: "MASSA_AI_SCHEDULER_CONSOLIDATION_INTERVAL_MS",
|
|
801
|
+
defaultIntervalMs: 30 * 60 * 1000
|
|
802
|
+
},
|
|
803
|
+
"decay-sweep": {
|
|
804
|
+
enabledVar: "MASSA_AI_SCHEDULER_DECAY_ENABLED",
|
|
805
|
+
intervalVar: "MASSA_AI_SCHEDULER_DECAY_INTERVAL_MS",
|
|
806
|
+
defaultIntervalMs: 60 * 60 * 1000
|
|
807
|
+
},
|
|
808
|
+
"auto-improve": {
|
|
809
|
+
enabledVar: "MASSA_AI_SCHEDULER_AUTO_IMPROVE_ENABLED",
|
|
810
|
+
intervalVar: "MASSA_AI_SCHEDULER_AUTO_IMPROVE_INTERVAL_MS",
|
|
811
|
+
defaultIntervalMs: 30 * 60 * 1000
|
|
812
|
+
},
|
|
813
|
+
"observation-bridge": {
|
|
814
|
+
enabledVar: "MASSA_AI_SCHEDULER_OBSERVATION_BRIDGE_ENABLED",
|
|
815
|
+
intervalVar: "MASSA_AI_SCHEDULER_OBSERVATION_BRIDGE_INTERVAL_MS",
|
|
816
|
+
defaultIntervalMs: 30 * 60 * 1000
|
|
817
|
+
},
|
|
818
|
+
"checkpoint-purge": {
|
|
819
|
+
enabledVar: "MASSA_AI_SCHEDULER_CHECKPOINT_PURGE_ENABLED",
|
|
820
|
+
intervalVar: "MASSA_AI_SCHEDULER_CHECKPOINT_PURGE_INTERVAL_MS",
|
|
821
|
+
defaultIntervalMs: 60 * 60 * 1000
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
function resolveSchedulerJob(kind, fileJobs) {
|
|
825
|
+
const meta = SCHEDULER_JOB_ENV[kind];
|
|
826
|
+
const fileJob = fileJobs?.[kind];
|
|
827
|
+
return {
|
|
828
|
+
enabled: envBool(meta.enabledVar, fileJob?.enabled ?? false),
|
|
829
|
+
intervalMs: envNum(meta.intervalVar, fileJob?.intervalMs ?? meta.defaultIntervalMs)
|
|
830
|
+
};
|
|
831
|
+
}
|
|
780
832
|
var MAX_IGNORE_PATTERNS = 1024;
|
|
781
833
|
function validateCapturePolicyConfig(raw) {
|
|
782
834
|
if (!raw || typeof raw !== "object")
|
|
@@ -880,10 +932,11 @@ var DEFAULT_ALLOWED_EXTENSIONS = [
|
|
|
880
932
|
var fileConfig = loadConfigSafe();
|
|
881
933
|
var fileCacheL1Bytes = fileConfig.cache?.l1MaxSizeMB ? fileConfig.cache.l1MaxSizeMB * 1024 * 1024 : undefined;
|
|
882
934
|
var fileCacheL2Bytes = fileConfig.cache?.l2MaxSizeMB ? fileConfig.cache.l2MaxSizeMB * 1024 * 1024 : undefined;
|
|
935
|
+
var resolvedDataDir = getGlobalDataDir();
|
|
883
936
|
var defaultConfig = {
|
|
884
937
|
name: "massa-ai-server",
|
|
885
938
|
version: "1.0.0",
|
|
886
|
-
dataDir:
|
|
939
|
+
dataDir: resolvedDataDir,
|
|
887
940
|
cache: {
|
|
888
941
|
l1: {
|
|
889
942
|
maxSize: envNum("L1_CACHE_MAX_SIZE", fileCacheL1Bytes ?? 100 * 1024 * 1024),
|
|
@@ -1007,8 +1060,24 @@ var defaultConfig = {
|
|
|
1007
1060
|
logging: {
|
|
1008
1061
|
level: process.env.LOG_LEVEL || fileConfig.logging?.level || "info",
|
|
1009
1062
|
enableMetrics: process.env.ENABLE_METRICS === "true" || process.env.ENABLE_METRICS === undefined && !!fileConfig.logging?.enableMetrics,
|
|
1010
|
-
file: process.env.MASSA_AI_LOG_FILE || fileConfig.logging?.file ||
|
|
1063
|
+
file: process.env.MASSA_AI_LOG_FILE || fileConfig.logging?.file || path4.join(resolvedDataDir, "logs", "massa-ai.log"),
|
|
1064
|
+
enableFileSink: envBool("MASSA_AI_LOG_ENABLE_FILE_SINK", fileConfig.logging?.enableFileSink ?? true),
|
|
1065
|
+
bufferSize: envNum("MASSA_AI_LOG_BUFFER_SIZE", fileConfig.logging?.bufferSize ?? 2000),
|
|
1066
|
+
maxFileSizeMb: envNum("MASSA_AI_LOG_MAX_FILE_SIZE_MB", fileConfig.logging?.maxFileSizeMb ?? 32),
|
|
1067
|
+
maxFiles: envNum("MASSA_AI_LOG_MAX_FILES", fileConfig.logging?.maxFiles ?? 5)
|
|
1011
1068
|
},
|
|
1069
|
+
scheduler: (() => {
|
|
1070
|
+
const fileScheduler = readSchedulerConfig(fileConfig);
|
|
1071
|
+
return {
|
|
1072
|
+
enabled: envBool("MASSA_AI_SCHEDULER_ENABLED", fileScheduler.enabled ?? false),
|
|
1073
|
+
tickMs: envNum("MASSA_AI_SCHEDULER_TICK_MS", fileScheduler.tickMs ?? 60000),
|
|
1074
|
+
maxConcurrent: envNum("MASSA_AI_SCHEDULER_MAX_CONCURRENT", fileScheduler.maxConcurrent ?? 2),
|
|
1075
|
+
jobs: Object.fromEntries(SCHEDULER_JOB_KINDS.map((kind) => [
|
|
1076
|
+
kind,
|
|
1077
|
+
resolveSchedulerJob(kind, fileScheduler.jobs)
|
|
1078
|
+
]))
|
|
1079
|
+
};
|
|
1080
|
+
})(),
|
|
1012
1081
|
synapse: {
|
|
1013
1082
|
enabled: process.env.SYNAPSE_ENABLED !== "false",
|
|
1014
1083
|
inhibition: {
|
|
@@ -1123,6 +1192,11 @@ class Config {
|
|
|
1123
1192
|
rateLimit: { ...defaults.rateLimit, ...overrides.rateLimit },
|
|
1124
1193
|
security: { ...defaults.security, ...overrides.security },
|
|
1125
1194
|
logging: { ...defaults.logging, ...overrides.logging },
|
|
1195
|
+
scheduler: {
|
|
1196
|
+
...defaults.scheduler,
|
|
1197
|
+
...overrides.scheduler,
|
|
1198
|
+
jobs: { ...defaults.scheduler.jobs, ...overrides.scheduler?.jobs }
|
|
1199
|
+
},
|
|
1126
1200
|
synapse: {
|
|
1127
1201
|
...defaults.synapse,
|
|
1128
1202
|
...overrides.synapse,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@massa-ai/opencode-plugin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.47.0",
|
|
4
4
|
"description": "massa-ai plugin for OpenCode - Semantic code search, memory, and context compression",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@opencode-ai/plugin": "^1.2.15",
|
|
26
26
|
"@opencode-ai/sdk": "^1.2.15",
|
|
27
|
-
"@massa-ai/core": "^1.
|
|
28
|
-
"@massa-ai/shared": "^1.
|
|
27
|
+
"@massa-ai/core": "^1.47.0",
|
|
28
|
+
"@massa-ai/shared": "^1.47.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^22.10.5",
|