@massa-ai/opencode-plugin 1.25.0 → 1.26.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/agent-profiles/balanced/massa-ai-architecture-specialist.md +64 -0
- package/agent-profiles/balanced/massa-ai-audit-specialist.md +80 -0
- package/agent-profiles/balanced/massa-ai-builder.md +66 -0
- package/agent-profiles/balanced/massa-ai-context-curator.md +66 -0
- package/agent-profiles/balanced/massa-ai-documentation-agent.md +64 -0
- package/agent-profiles/balanced/massa-ai-furps-analyst.md +70 -0
- package/agent-profiles/balanced/massa-ai-investigator.md +67 -0
- package/agent-profiles/balanced/massa-ai-judge.md +101 -0
- package/agent-profiles/balanced/massa-ai-meta-judge.md +88 -0
- package/agent-profiles/balanced/massa-ai-mobile-specialist.md +81 -0
- package/agent-profiles/balanced/massa-ai-navigator.md +74 -0
- package/agent-profiles/balanced/massa-ai-plan-critic.md +89 -0
- package/agent-profiles/balanced/massa-ai-planner.md +64 -0
- package/agent-profiles/balanced/massa-ai-requirements-analyst.md +63 -0
- package/agent-profiles/balanced/massa-ai-reviewer.md +65 -0
- package/agent-profiles/balanced/massa-ai-test-engineer.md +65 -0
- package/agent-profiles/balanced/massa-ai-verification-agent.md +64 -0
- package/agent-profiles/cheap/massa-ai-architecture-specialist.md +64 -0
- package/agent-profiles/cheap/massa-ai-audit-specialist.md +80 -0
- package/agent-profiles/cheap/massa-ai-builder.md +66 -0
- package/agent-profiles/cheap/massa-ai-context-curator.md +66 -0
- package/agent-profiles/cheap/massa-ai-documentation-agent.md +64 -0
- package/agent-profiles/cheap/massa-ai-furps-analyst.md +70 -0
- package/agent-profiles/cheap/massa-ai-investigator.md +67 -0
- package/agent-profiles/cheap/massa-ai-judge.md +101 -0
- package/agent-profiles/cheap/massa-ai-meta-judge.md +88 -0
- package/agent-profiles/cheap/massa-ai-mobile-specialist.md +81 -0
- package/agent-profiles/cheap/massa-ai-navigator.md +74 -0
- package/agent-profiles/cheap/massa-ai-plan-critic.md +89 -0
- package/agent-profiles/cheap/massa-ai-planner.md +64 -0
- package/agent-profiles/cheap/massa-ai-requirements-analyst.md +63 -0
- package/agent-profiles/cheap/massa-ai-reviewer.md +65 -0
- package/agent-profiles/cheap/massa-ai-test-engineer.md +65 -0
- package/agent-profiles/cheap/massa-ai-verification-agent.md +64 -0
- package/agent-profiles/heavy/massa-ai-architecture-specialist.md +64 -0
- package/agent-profiles/heavy/massa-ai-audit-specialist.md +80 -0
- package/agent-profiles/heavy/massa-ai-builder.md +66 -0
- package/agent-profiles/heavy/massa-ai-context-curator.md +66 -0
- package/agent-profiles/heavy/massa-ai-documentation-agent.md +64 -0
- package/agent-profiles/heavy/massa-ai-furps-analyst.md +70 -0
- package/agent-profiles/heavy/massa-ai-investigator.md +67 -0
- package/agent-profiles/heavy/massa-ai-judge.md +101 -0
- package/agent-profiles/heavy/massa-ai-meta-judge.md +88 -0
- package/agent-profiles/heavy/massa-ai-mobile-specialist.md +81 -0
- package/agent-profiles/heavy/massa-ai-navigator.md +74 -0
- package/agent-profiles/heavy/massa-ai-plan-critic.md +89 -0
- package/agent-profiles/heavy/massa-ai-planner.md +64 -0
- package/agent-profiles/heavy/massa-ai-requirements-analyst.md +63 -0
- package/agent-profiles/heavy/massa-ai-reviewer.md +65 -0
- package/agent-profiles/heavy/massa-ai-test-engineer.md +65 -0
- package/agent-profiles/heavy/massa-ai-verification-agent.md +64 -0
- package/agent-profiles/home/massa-ai-architecture-specialist.md +64 -0
- package/agent-profiles/home/massa-ai-audit-specialist.md +80 -0
- package/agent-profiles/home/massa-ai-builder.md +66 -0
- package/agent-profiles/home/massa-ai-context-curator.md +66 -0
- package/agent-profiles/home/massa-ai-documentation-agent.md +64 -0
- package/agent-profiles/home/massa-ai-furps-analyst.md +70 -0
- package/agent-profiles/home/massa-ai-investigator.md +67 -0
- package/agent-profiles/home/massa-ai-judge.md +101 -0
- package/agent-profiles/home/massa-ai-meta-judge.md +88 -0
- package/agent-profiles/home/massa-ai-mobile-specialist.md +81 -0
- package/agent-profiles/home/massa-ai-navigator.md +74 -0
- package/agent-profiles/home/massa-ai-plan-critic.md +89 -0
- package/agent-profiles/home/massa-ai-planner.md +64 -0
- package/agent-profiles/home/massa-ai-requirements-analyst.md +63 -0
- package/agent-profiles/home/massa-ai-reviewer.md +65 -0
- package/agent-profiles/home/massa-ai-test-engineer.md +65 -0
- package/agent-profiles/home/massa-ai-verification-agent.md +64 -0
- package/agent-profiles/local_models/massa-ai-architecture-specialist.md +64 -0
- package/agent-profiles/local_models/massa-ai-audit-specialist.md +80 -0
- package/agent-profiles/local_models/massa-ai-builder.md +66 -0
- package/agent-profiles/local_models/massa-ai-context-curator.md +66 -0
- package/agent-profiles/local_models/massa-ai-documentation-agent.md +64 -0
- package/agent-profiles/local_models/massa-ai-furps-analyst.md +70 -0
- package/agent-profiles/local_models/massa-ai-investigator.md +67 -0
- package/agent-profiles/local_models/massa-ai-judge.md +101 -0
- package/agent-profiles/local_models/massa-ai-meta-judge.md +88 -0
- package/agent-profiles/local_models/massa-ai-mobile-specialist.md +81 -0
- package/agent-profiles/local_models/massa-ai-navigator.md +74 -0
- package/agent-profiles/local_models/massa-ai-plan-critic.md +89 -0
- package/agent-profiles/local_models/massa-ai-planner.md +64 -0
- package/agent-profiles/local_models/massa-ai-requirements-analyst.md +63 -0
- package/agent-profiles/local_models/massa-ai-reviewer.md +65 -0
- package/agent-profiles/local_models/massa-ai-test-engineer.md +65 -0
- package/agent-profiles/local_models/massa-ai-verification-agent.md +64 -0
- package/agent-profiles/open_models/massa-ai-architecture-specialist.md +64 -0
- package/agent-profiles/open_models/massa-ai-audit-specialist.md +80 -0
- package/agent-profiles/open_models/massa-ai-builder.md +66 -0
- package/agent-profiles/open_models/massa-ai-context-curator.md +66 -0
- package/agent-profiles/open_models/massa-ai-documentation-agent.md +64 -0
- package/agent-profiles/open_models/massa-ai-furps-analyst.md +70 -0
- package/agent-profiles/open_models/massa-ai-investigator.md +67 -0
- package/agent-profiles/open_models/massa-ai-judge.md +101 -0
- package/agent-profiles/open_models/massa-ai-meta-judge.md +88 -0
- package/agent-profiles/open_models/massa-ai-mobile-specialist.md +81 -0
- package/agent-profiles/open_models/massa-ai-navigator.md +74 -0
- package/agent-profiles/open_models/massa-ai-plan-critic.md +89 -0
- package/agent-profiles/open_models/massa-ai-planner.md +64 -0
- package/agent-profiles/open_models/massa-ai-requirements-analyst.md +63 -0
- package/agent-profiles/open_models/massa-ai-reviewer.md +65 -0
- package/agent-profiles/open_models/massa-ai-test-engineer.md +65 -0
- package/agent-profiles/open_models/massa-ai-verification-agent.md +64 -0
- package/agent-profiles/work/massa-ai-architecture-specialist.md +64 -0
- package/agent-profiles/work/massa-ai-audit-specialist.md +80 -0
- package/agent-profiles/work/massa-ai-builder.md +66 -0
- package/agent-profiles/work/massa-ai-context-curator.md +66 -0
- package/agent-profiles/work/massa-ai-documentation-agent.md +64 -0
- package/agent-profiles/work/massa-ai-furps-analyst.md +70 -0
- package/agent-profiles/work/massa-ai-investigator.md +67 -0
- package/agent-profiles/work/massa-ai-judge.md +101 -0
- package/agent-profiles/work/massa-ai-meta-judge.md +88 -0
- package/agent-profiles/work/massa-ai-mobile-specialist.md +81 -0
- package/agent-profiles/work/massa-ai-navigator.md +74 -0
- package/agent-profiles/work/massa-ai-plan-critic.md +89 -0
- package/agent-profiles/work/massa-ai-planner.md +64 -0
- package/agent-profiles/work/massa-ai-requirements-analyst.md +63 -0
- package/agent-profiles/work/massa-ai-reviewer.md +65 -0
- package/agent-profiles/work/massa-ai-test-engineer.md +65 -0
- package/agent-profiles/work/massa-ai-verification-agent.md +64 -0
- package/dist/config-cli.js +821 -15
- package/dist/index.js +24 -0
- package/package.json +5 -4
package/dist/config-cli.js
CHANGED
|
@@ -1180,13 +1180,751 @@ class Config {
|
|
|
1180
1180
|
}
|
|
1181
1181
|
}
|
|
1182
1182
|
var config = new Config;
|
|
1183
|
+
// ../../packages/shared/dist/types/structural-transport.js
|
|
1184
|
+
var STRUCTURAL_SYMBOL_KINDS = [
|
|
1185
|
+
"module",
|
|
1186
|
+
"namespace",
|
|
1187
|
+
"class",
|
|
1188
|
+
"interface",
|
|
1189
|
+
"trait",
|
|
1190
|
+
"enum",
|
|
1191
|
+
"function",
|
|
1192
|
+
"method",
|
|
1193
|
+
"constructor",
|
|
1194
|
+
"property",
|
|
1195
|
+
"field",
|
|
1196
|
+
"variable",
|
|
1197
|
+
"constant",
|
|
1198
|
+
"type",
|
|
1199
|
+
"type_parameter",
|
|
1200
|
+
"export",
|
|
1201
|
+
"heading",
|
|
1202
|
+
"key"
|
|
1203
|
+
];
|
|
1204
|
+
var STRUCTURAL_SYMBOL_KINDS_DESCRIPTION = `Canonical graph schema v2 symbol kind. One of: ${STRUCTURAL_SYMBOL_KINDS.join(", ")}.`;
|
|
1205
|
+
var STRUCTURAL_SYMBOL_KIND_SCHEMA = Object.freeze({
|
|
1206
|
+
type: "string",
|
|
1207
|
+
enum: STRUCTURAL_SYMBOL_KINDS,
|
|
1208
|
+
description: STRUCTURAL_SYMBOL_KINDS_DESCRIPTION
|
|
1209
|
+
});
|
|
1183
1210
|
|
|
1184
|
-
//
|
|
1185
|
-
|
|
1186
|
-
|
|
1211
|
+
// ../../packages/shared/dist/types/index.js
|
|
1212
|
+
var MemoryLevel;
|
|
1213
|
+
(function(MemoryLevel2) {
|
|
1214
|
+
MemoryLevel2[MemoryLevel2["PERSISTENT"] = 0] = "PERSISTENT";
|
|
1215
|
+
MemoryLevel2[MemoryLevel2["PROJECT"] = 1] = "PROJECT";
|
|
1216
|
+
MemoryLevel2[MemoryLevel2["USER"] = 2] = "USER";
|
|
1217
|
+
MemoryLevel2[MemoryLevel2["SESSION"] = 3] = "SESSION";
|
|
1218
|
+
MemoryLevel2[MemoryLevel2["WORKING"] = 4] = "WORKING";
|
|
1219
|
+
})(MemoryLevel || (MemoryLevel = {}));
|
|
1220
|
+
var MemoryType;
|
|
1221
|
+
(function(MemoryType2) {
|
|
1222
|
+
MemoryType2["CRITICAL"] = "critical";
|
|
1223
|
+
MemoryType2["CONVERSATION"] = "conversation";
|
|
1224
|
+
MemoryType2["CODE"] = "code";
|
|
1225
|
+
MemoryType2["DECISION"] = "decision";
|
|
1226
|
+
MemoryType2["PATTERN"] = "pattern";
|
|
1227
|
+
})(MemoryType || (MemoryType = {}));
|
|
1228
|
+
var CacheLevel;
|
|
1229
|
+
(function(CacheLevel2) {
|
|
1230
|
+
CacheLevel2[CacheLevel2["L1"] = 1] = "L1";
|
|
1231
|
+
CacheLevel2[CacheLevel2["L2"] = 2] = "L2";
|
|
1232
|
+
})(CacheLevel || (CacheLevel = {}));
|
|
1233
|
+
var CompressionStrategy;
|
|
1234
|
+
(function(CompressionStrategy2) {
|
|
1235
|
+
CompressionStrategy2["CODE_STRUCTURE"] = "code_structure";
|
|
1236
|
+
CompressionStrategy2["CONVERSATION_SUMMARY"] = "conversation_summary";
|
|
1237
|
+
CompressionStrategy2["SEMANTIC_DEDUP"] = "semantic_dedup";
|
|
1238
|
+
CompressionStrategy2["HIERARCHICAL"] = "hierarchical";
|
|
1239
|
+
})(CompressionStrategy || (CompressionStrategy = {}));
|
|
1240
|
+
var SearchSource;
|
|
1241
|
+
(function(SearchSource2) {
|
|
1242
|
+
SearchSource2["VECTOR"] = "vector";
|
|
1243
|
+
SearchSource2["KEYWORD"] = "keyword";
|
|
1244
|
+
SearchSource2["HYBRID"] = "hybrid";
|
|
1245
|
+
SearchSource2["CACHE"] = "cache";
|
|
1246
|
+
})(SearchSource || (SearchSource = {}));
|
|
1247
|
+
var Permission;
|
|
1248
|
+
(function(Permission2) {
|
|
1249
|
+
Permission2["READ"] = "read";
|
|
1250
|
+
Permission2["WRITE"] = "write";
|
|
1251
|
+
Permission2["DELETE"] = "delete";
|
|
1252
|
+
Permission2["ADMIN"] = "admin";
|
|
1253
|
+
})(Permission || (Permission = {}));
|
|
1254
|
+
var MemoryRelationType;
|
|
1255
|
+
(function(MemoryRelationType2) {
|
|
1256
|
+
MemoryRelationType2["DERIVED_FROM"] = "DERIVED_FROM";
|
|
1257
|
+
MemoryRelationType2["CONTRADICTS"] = "CONTRADICTS";
|
|
1258
|
+
MemoryRelationType2["SUPPORTS"] = "SUPPORTS";
|
|
1259
|
+
MemoryRelationType2["RELATES_TO"] = "RELATES_TO";
|
|
1260
|
+
MemoryRelationType2["SUPERSEDES"] = "SUPERSEDES";
|
|
1261
|
+
MemoryRelationType2["CAUSES"] = "CAUSES";
|
|
1262
|
+
MemoryRelationType2["RESOLVES"] = "RESOLVES";
|
|
1263
|
+
})(MemoryRelationType || (MemoryRelationType = {}));
|
|
1264
|
+
var CheckpointType;
|
|
1265
|
+
(function(CheckpointType2) {
|
|
1266
|
+
CheckpointType2["AUTO"] = "auto";
|
|
1267
|
+
CheckpointType2["MANUAL"] = "manual";
|
|
1268
|
+
CheckpointType2["MILESTONE"] = "milestone";
|
|
1269
|
+
})(CheckpointType || (CheckpointType = {}));
|
|
1270
|
+
var TaskStatus;
|
|
1271
|
+
(function(TaskStatus2) {
|
|
1272
|
+
TaskStatus2["PENDING"] = "pending";
|
|
1273
|
+
TaskStatus2["IN_PROGRESS"] = "in_progress";
|
|
1274
|
+
TaskStatus2["COMPLETED"] = "completed";
|
|
1275
|
+
TaskStatus2["FAILED"] = "failed";
|
|
1276
|
+
TaskStatus2["PAUSED"] = "paused";
|
|
1277
|
+
})(TaskStatus || (TaskStatus = {}));
|
|
1278
|
+
// ../../packages/shared/dist/utils/logger.js
|
|
1279
|
+
var LogLevel;
|
|
1280
|
+
(function(LogLevel2) {
|
|
1281
|
+
LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
|
|
1282
|
+
LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
|
|
1283
|
+
LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
|
|
1284
|
+
LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
|
|
1285
|
+
})(LogLevel || (LogLevel = {}));
|
|
1286
|
+
|
|
1287
|
+
class Logger {
|
|
1288
|
+
_level;
|
|
1289
|
+
_enableMetrics;
|
|
1290
|
+
_initialized = false;
|
|
1291
|
+
constructor() {}
|
|
1292
|
+
ensureInitialized() {
|
|
1293
|
+
if (!this._initialized) {
|
|
1294
|
+
try {
|
|
1295
|
+
const configLevel = config.get("logging").level;
|
|
1296
|
+
this._level = this.parseLogLevel(configLevel);
|
|
1297
|
+
this._enableMetrics = config.get("logging").enableMetrics;
|
|
1298
|
+
} catch {
|
|
1299
|
+
this._level = LogLevel.INFO;
|
|
1300
|
+
this._enableMetrics = false;
|
|
1301
|
+
}
|
|
1302
|
+
this._initialized = true;
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
get level() {
|
|
1306
|
+
this.ensureInitialized();
|
|
1307
|
+
return this._level;
|
|
1308
|
+
}
|
|
1309
|
+
get enableMetrics() {
|
|
1310
|
+
this.ensureInitialized();
|
|
1311
|
+
return this._enableMetrics;
|
|
1312
|
+
}
|
|
1313
|
+
parseLogLevel(level) {
|
|
1314
|
+
const levels = {
|
|
1315
|
+
debug: LogLevel.DEBUG,
|
|
1316
|
+
info: LogLevel.INFO,
|
|
1317
|
+
warn: LogLevel.WARN,
|
|
1318
|
+
error: LogLevel.ERROR
|
|
1319
|
+
};
|
|
1320
|
+
return levels[level.toLowerCase()] ?? LogLevel.INFO;
|
|
1321
|
+
}
|
|
1322
|
+
shouldLog(level) {
|
|
1323
|
+
return level >= this.level;
|
|
1324
|
+
}
|
|
1325
|
+
formatMessage(level, message, meta) {
|
|
1326
|
+
const timestamp = new Date().toISOString();
|
|
1327
|
+
const metaStr = meta ? ` ${JSON.stringify(meta)}` : "";
|
|
1328
|
+
return `[${timestamp}] [${level}] ${message}${metaStr}`;
|
|
1329
|
+
}
|
|
1330
|
+
write(message, _level) {
|
|
1331
|
+
console.error(message);
|
|
1332
|
+
}
|
|
1333
|
+
debug(message, meta) {
|
|
1334
|
+
if (this.shouldLog(LogLevel.DEBUG)) {
|
|
1335
|
+
this.write(this.formatMessage("DEBUG", message, meta), LogLevel.DEBUG);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
info(message, meta) {
|
|
1339
|
+
if (this.shouldLog(LogLevel.INFO)) {
|
|
1340
|
+
this.write(this.formatMessage("INFO", message, meta), LogLevel.INFO);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
warn(message, meta) {
|
|
1344
|
+
if (this.shouldLog(LogLevel.WARN)) {
|
|
1345
|
+
this.write(this.formatMessage("WARN", message, meta), LogLevel.WARN);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
error(message, error, meta) {
|
|
1349
|
+
if (this.shouldLog(LogLevel.ERROR)) {
|
|
1350
|
+
const errorMeta = error ? {
|
|
1351
|
+
...meta,
|
|
1352
|
+
error: {
|
|
1353
|
+
name: error.name,
|
|
1354
|
+
message: error.message,
|
|
1355
|
+
stack: error.stack
|
|
1356
|
+
}
|
|
1357
|
+
} : meta;
|
|
1358
|
+
this.write(this.formatMessage("ERROR", message, errorMeta), LogLevel.ERROR);
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
metric(name, value, unit) {
|
|
1362
|
+
if (this.enableMetrics) {
|
|
1363
|
+
this.info(`METRIC: ${name}`, { value, unit });
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
child(context) {
|
|
1367
|
+
const childLogger = new Logger;
|
|
1368
|
+
const originalDebug = childLogger.debug.bind(childLogger);
|
|
1369
|
+
const originalInfo = childLogger.info.bind(childLogger);
|
|
1370
|
+
const originalWarn = childLogger.warn.bind(childLogger);
|
|
1371
|
+
const originalError = childLogger.error.bind(childLogger);
|
|
1372
|
+
childLogger.debug = (msg, meta) => {
|
|
1373
|
+
originalDebug(msg, { ...context, ...meta });
|
|
1374
|
+
};
|
|
1375
|
+
childLogger.info = (msg, meta) => {
|
|
1376
|
+
originalInfo(msg, { ...context, ...meta });
|
|
1377
|
+
};
|
|
1378
|
+
childLogger.warn = (msg, meta) => {
|
|
1379
|
+
originalWarn(msg, { ...context, ...meta });
|
|
1380
|
+
};
|
|
1381
|
+
childLogger.error = (msg, err, meta) => {
|
|
1382
|
+
originalError(msg, err, { ...context, ...meta });
|
|
1383
|
+
};
|
|
1384
|
+
return childLogger;
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
var logger = new Logger;
|
|
1388
|
+
// ../../packages/shared/dist/utils/rate-limiter.js
|
|
1389
|
+
class RateLimiter {
|
|
1390
|
+
tokens;
|
|
1391
|
+
lastRefill;
|
|
1392
|
+
maxTokens;
|
|
1393
|
+
refillRate;
|
|
1394
|
+
constructor(maxTokens, refillRate) {
|
|
1395
|
+
this.maxTokens = maxTokens;
|
|
1396
|
+
this.refillRate = refillRate;
|
|
1397
|
+
this.tokens = maxTokens;
|
|
1398
|
+
this.lastRefill = Date.now();
|
|
1399
|
+
}
|
|
1400
|
+
refill() {
|
|
1401
|
+
const now = Date.now();
|
|
1402
|
+
const timePassed = (now - this.lastRefill) / 1000;
|
|
1403
|
+
const tokensToAdd = timePassed * this.refillRate;
|
|
1404
|
+
this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd);
|
|
1405
|
+
this.lastRefill = now;
|
|
1406
|
+
}
|
|
1407
|
+
tryConsume(tokens = 1) {
|
|
1408
|
+
this.refill();
|
|
1409
|
+
if (this.tokens >= tokens) {
|
|
1410
|
+
this.tokens -= tokens;
|
|
1411
|
+
return true;
|
|
1412
|
+
}
|
|
1413
|
+
return false;
|
|
1414
|
+
}
|
|
1415
|
+
async consume(tokens = 1) {
|
|
1416
|
+
while (!this.tryConsume(tokens)) {
|
|
1417
|
+
const waitTime = (tokens - this.tokens) / this.refillRate * 1000;
|
|
1418
|
+
await this.sleep(Math.max(100, waitTime));
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
getAvailableTokens() {
|
|
1422
|
+
this.refill();
|
|
1423
|
+
return this.tokens;
|
|
1424
|
+
}
|
|
1425
|
+
reset() {
|
|
1426
|
+
this.tokens = this.maxTokens;
|
|
1427
|
+
this.lastRefill = Date.now();
|
|
1428
|
+
}
|
|
1429
|
+
sleep(ms) {
|
|
1430
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
class SmartRateLimiter {
|
|
1435
|
+
requestLimiter;
|
|
1436
|
+
tokenLimiter;
|
|
1437
|
+
constructor() {
|
|
1438
|
+
const limits = config.get("rateLimit");
|
|
1439
|
+
this.requestLimiter = new RateLimiter(limits.requestsPerMinute, limits.requestsPerMinute / 60);
|
|
1440
|
+
this.tokenLimiter = new RateLimiter(limits.tokensPerMinute, limits.tokensPerMinute / 60);
|
|
1441
|
+
logger.info("Smart Rate Limiter initialized", {
|
|
1442
|
+
requestsPerMinute: limits.requestsPerMinute,
|
|
1443
|
+
tokensPerMinute: limits.tokensPerMinute
|
|
1444
|
+
});
|
|
1445
|
+
}
|
|
1446
|
+
async checkRequest(estimatedTokens = 1000) {
|
|
1447
|
+
const hasRequestCapacity = this.requestLimiter.tryConsume(1);
|
|
1448
|
+
const hasTokenCapacity = this.tokenLimiter.tryConsume(estimatedTokens);
|
|
1449
|
+
if (!hasRequestCapacity) {
|
|
1450
|
+
logger.warn("Request rate limit exceeded");
|
|
1451
|
+
return false;
|
|
1452
|
+
}
|
|
1453
|
+
if (!hasTokenCapacity) {
|
|
1454
|
+
logger.warn("Token rate limit exceeded", { estimatedTokens });
|
|
1455
|
+
return false;
|
|
1456
|
+
}
|
|
1457
|
+
return true;
|
|
1458
|
+
}
|
|
1459
|
+
async waitForCapacity(estimatedTokens = 1000) {
|
|
1460
|
+
await Promise.all([
|
|
1461
|
+
this.requestLimiter.consume(1),
|
|
1462
|
+
this.tokenLimiter.consume(estimatedTokens)
|
|
1463
|
+
]);
|
|
1464
|
+
}
|
|
1465
|
+
getStatus() {
|
|
1466
|
+
return {
|
|
1467
|
+
requestsAvailable: Math.floor(this.requestLimiter.getAvailableTokens()),
|
|
1468
|
+
tokensAvailable: Math.floor(this.tokenLimiter.getAvailableTokens()),
|
|
1469
|
+
requestsMax: config.get("rateLimit").requestsPerMinute,
|
|
1470
|
+
tokensMax: config.get("rateLimit").tokensPerMinute
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
reset() {
|
|
1474
|
+
this.requestLimiter.reset();
|
|
1475
|
+
this.tokenLimiter.reset();
|
|
1476
|
+
logger.info("Rate limiters reset");
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
var rateLimiter = new SmartRateLimiter;
|
|
1480
|
+
// ../../packages/shared/dist/profile-switch/hosts.js
|
|
1187
1481
|
import os3 from "os";
|
|
1482
|
+
import path5 from "path";
|
|
1483
|
+
var HOSTS = ["claude", "codex", "cursor", "opencode"];
|
|
1484
|
+
function isHost(v) {
|
|
1485
|
+
return typeof v === "string" && HOSTS.includes(v);
|
|
1486
|
+
}
|
|
1487
|
+
var CURSOR_SKIP_REASON = "all tiers inherit \u2014 Cursor publishes no resolvable model IDs";
|
|
1488
|
+
function fileLayout(host, activeDir, activeGlob, variantsRoot) {
|
|
1489
|
+
return {
|
|
1490
|
+
host,
|
|
1491
|
+
route: "files",
|
|
1492
|
+
activeDir,
|
|
1493
|
+
activeGlob,
|
|
1494
|
+
variantsRoot,
|
|
1495
|
+
variantDir: (profile) => path5.join(variantsRoot, profile)
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
function resolveHostLayout(host, opts = {}) {
|
|
1499
|
+
const targetHome = opts.targetHome ?? os3.homedir();
|
|
1500
|
+
const override = opts.projectRoot?.[host];
|
|
1501
|
+
switch (host) {
|
|
1502
|
+
case "cursor":
|
|
1503
|
+
return { host, route: "skip", reason: CURSOR_SKIP_REASON };
|
|
1504
|
+
case "claude": {
|
|
1505
|
+
const root = override ?? path5.join(targetHome, ".claude");
|
|
1506
|
+
return fileLayout(host, path5.join(root, "agents"), "massa-ai-*.md", path5.join(root, "massa-ai", "agent-profiles"));
|
|
1507
|
+
}
|
|
1508
|
+
case "codex": {
|
|
1509
|
+
const root = override ?? path5.join(targetHome, ".codex");
|
|
1510
|
+
return fileLayout(host, path5.join(root, "agents"), "massa-ai-*.toml", path5.join(root, "massa-ai", "agent-profiles"));
|
|
1511
|
+
}
|
|
1512
|
+
case "opencode": {
|
|
1513
|
+
const root = override ?? path5.join(targetHome, ".config", "opencode");
|
|
1514
|
+
const pluginsDir = path5.join(root, "plugins", "massa-ai");
|
|
1515
|
+
return fileLayout(host, path5.join(root, "agents"), "massa-ai-*.md", path5.join(pluginsDir, "agent-profiles"));
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
function detectRoute(platform) {
|
|
1520
|
+
const route = platform?.installRoute;
|
|
1521
|
+
if (route === "file")
|
|
1522
|
+
return { kind: "proceed" };
|
|
1523
|
+
if (route === "marketplace") {
|
|
1524
|
+
return {
|
|
1525
|
+
kind: "refuse",
|
|
1526
|
+
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."
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
return {
|
|
1530
|
+
kind: "refuse",
|
|
1531
|
+
reason: "no install route recorded \u2014 re-run the installer to record the install route"
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
// ../../packages/shared/dist/profile-switch/state.js
|
|
1535
|
+
import fs2 from "fs";
|
|
1536
|
+
import path6 from "path";
|
|
1537
|
+
|
|
1538
|
+
class InstallStateError extends Error {
|
|
1539
|
+
constructor(message) {
|
|
1540
|
+
super(message);
|
|
1541
|
+
this.name = "InstallStateError";
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
function namedError(name, message) {
|
|
1545
|
+
const err = new InstallStateError(message);
|
|
1546
|
+
err.name = name;
|
|
1547
|
+
return err;
|
|
1548
|
+
}
|
|
1549
|
+
var CorruptInstallStateError = (filePath, cause) => namedError("CorruptInstallStateError", `install-state.json at ${filePath} is corrupt: ${cause}`);
|
|
1550
|
+
var UnwritableInstallStateError = (filePath, cause) => namedError("UnwritableInstallStateError", `could not write install-state.json at ${filePath}: ${cause}`);
|
|
1551
|
+
var DEFAULT_STATE = () => ({ version: 2, platforms: {} });
|
|
1552
|
+
function isPlainObject(v) {
|
|
1553
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1554
|
+
}
|
|
1555
|
+
function validateShape(raw, filePath) {
|
|
1556
|
+
if (!isPlainObject(raw)) {
|
|
1557
|
+
throw CorruptInstallStateError(filePath, "root is not a JSON object");
|
|
1558
|
+
}
|
|
1559
|
+
if (raw.platforms !== undefined && !isPlainObject(raw.platforms)) {
|
|
1560
|
+
throw CorruptInstallStateError(filePath, '"platforms" is present but not an object');
|
|
1561
|
+
}
|
|
1562
|
+
const platforms = isPlainObject(raw.platforms) ? raw.platforms : {};
|
|
1563
|
+
return {
|
|
1564
|
+
...raw,
|
|
1565
|
+
version: typeof raw.version === "number" ? raw.version : 2,
|
|
1566
|
+
platforms
|
|
1567
|
+
};
|
|
1568
|
+
}
|
|
1569
|
+
function readInstallState(filePath) {
|
|
1570
|
+
let text;
|
|
1571
|
+
try {
|
|
1572
|
+
text = fs2.readFileSync(filePath, "utf-8");
|
|
1573
|
+
} catch (err) {
|
|
1574
|
+
const code = err.code;
|
|
1575
|
+
if (code === "ENOENT")
|
|
1576
|
+
return DEFAULT_STATE();
|
|
1577
|
+
throw CorruptInstallStateError(filePath, `could not read file: ${err.message}`);
|
|
1578
|
+
}
|
|
1579
|
+
let raw;
|
|
1580
|
+
try {
|
|
1581
|
+
raw = JSON.parse(text);
|
|
1582
|
+
} catch (err) {
|
|
1583
|
+
throw CorruptInstallStateError(filePath, `invalid JSON: ${err.message}`);
|
|
1584
|
+
}
|
|
1585
|
+
return validateShape(raw, filePath);
|
|
1586
|
+
}
|
|
1587
|
+
function writeInstallState(filePath, state) {
|
|
1588
|
+
const validated = validateShape(state, filePath);
|
|
1589
|
+
const text = `${JSON.stringify(validated, null, 2)}
|
|
1590
|
+
`;
|
|
1591
|
+
try {
|
|
1592
|
+
fs2.mkdirSync(path6.dirname(filePath), { recursive: true });
|
|
1593
|
+
fs2.writeFileSync(filePath, text);
|
|
1594
|
+
} catch (err) {
|
|
1595
|
+
throw UnwritableInstallStateError(filePath, err.message);
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
function updatePlatform(filePath, host, patch) {
|
|
1599
|
+
const state = readInstallState(filePath);
|
|
1600
|
+
const existing = isPlainObject(state.platforms[host]) ? state.platforms[host] : { root: "", skills: [], skillsOwner: "plugin" };
|
|
1601
|
+
const merged = { ...existing, ...patch };
|
|
1602
|
+
const next = {
|
|
1603
|
+
...state,
|
|
1604
|
+
platforms: { ...state.platforms, [host]: merged }
|
|
1605
|
+
};
|
|
1606
|
+
writeInstallState(filePath, next);
|
|
1607
|
+
return next;
|
|
1608
|
+
}
|
|
1609
|
+
// ../../packages/shared/dist/profile-switch/lock.js
|
|
1610
|
+
import fs3 from "fs";
|
|
1611
|
+
import path7 from "path";
|
|
1612
|
+
import os4 from "os";
|
|
1613
|
+
import crypto2 from "crypto";
|
|
1614
|
+
import { execFileSync } from "child_process";
|
|
1615
|
+
|
|
1616
|
+
class LockError extends Error {
|
|
1617
|
+
constructor(message) {
|
|
1618
|
+
super(message);
|
|
1619
|
+
this.name = "LockError";
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
function namedError2(name, message) {
|
|
1623
|
+
const err = new LockError(message);
|
|
1624
|
+
err.name = name;
|
|
1625
|
+
return err;
|
|
1626
|
+
}
|
|
1627
|
+
var LockHeldError = (lockDir) => namedError2("LockHeldError", `another switch is running (lock held at ${lockDir})`);
|
|
1628
|
+
var LockAcquireError = (lockDir, cause) => namedError2("LockAcquireError", `could not acquire switch lock at ${lockDir}: ${cause}`);
|
|
1629
|
+
var DEFAULT_STALE_AFTER_MS = 5 * 60 * 1000;
|
|
1630
|
+
var DEFAULT_CLOCK = { now: () => Date.now() };
|
|
1631
|
+
var DEFAULT_IDENTITY = {
|
|
1632
|
+
pid: () => process.pid,
|
|
1633
|
+
hostname: () => os4.hostname(),
|
|
1634
|
+
processStart: (pid) => {
|
|
1635
|
+
try {
|
|
1636
|
+
const out = execFileSync("ps", ["-p", String(pid), "-o", "lstart="], {
|
|
1637
|
+
encoding: "utf-8"
|
|
1638
|
+
}).trim();
|
|
1639
|
+
return out.length > 0 ? out : null;
|
|
1640
|
+
} catch {
|
|
1641
|
+
return null;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
};
|
|
1645
|
+
function readOwner(ownerPath) {
|
|
1646
|
+
let raw;
|
|
1647
|
+
try {
|
|
1648
|
+
raw = JSON.parse(fs3.readFileSync(ownerPath, "utf-8"));
|
|
1649
|
+
} catch {
|
|
1650
|
+
return null;
|
|
1651
|
+
}
|
|
1652
|
+
if (typeof raw !== "object" || raw === null)
|
|
1653
|
+
return null;
|
|
1654
|
+
const r = raw;
|
|
1655
|
+
if (typeof r.host === "string" && typeof r.pid === "number" && typeof r.processStart === "string" && typeof r.token === "string" && typeof r.timestamp === "number") {
|
|
1656
|
+
return r;
|
|
1657
|
+
}
|
|
1658
|
+
return null;
|
|
1659
|
+
}
|
|
1660
|
+
function releaseIfOwned(lockDir, ownerPath, token) {
|
|
1661
|
+
const owner = readOwner(ownerPath);
|
|
1662
|
+
if (owner === null || owner.token !== token)
|
|
1663
|
+
return;
|
|
1664
|
+
fs3.rmSync(lockDir, { recursive: true, force: true });
|
|
1665
|
+
}
|
|
1666
|
+
function acquireLock(stateFilePath, options = {}) {
|
|
1667
|
+
const lockDir = `${stateFilePath}.switch.lock`;
|
|
1668
|
+
const ownerPath = path7.join(lockDir, "owner.json");
|
|
1669
|
+
const clock = options.clock ?? DEFAULT_CLOCK;
|
|
1670
|
+
const identity = options.identity ?? DEFAULT_IDENTITY;
|
|
1671
|
+
const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
|
|
1672
|
+
const createFresh = () => {
|
|
1673
|
+
fs3.mkdirSync(lockDir);
|
|
1674
|
+
const pid = identity.pid();
|
|
1675
|
+
const startedAt = identity.processStart(pid);
|
|
1676
|
+
if (startedAt == null) {
|
|
1677
|
+
fs3.rmSync(lockDir, { recursive: true, force: true });
|
|
1678
|
+
throw LockAcquireError(lockDir, "could not determine this process's start-time identity");
|
|
1679
|
+
}
|
|
1680
|
+
const token = crypto2.randomUUID();
|
|
1681
|
+
const record = {
|
|
1682
|
+
host: identity.hostname(),
|
|
1683
|
+
pid,
|
|
1684
|
+
processStart: startedAt,
|
|
1685
|
+
token,
|
|
1686
|
+
timestamp: clock.now()
|
|
1687
|
+
};
|
|
1688
|
+
fs3.mkdirSync(path7.dirname(ownerPath), { recursive: true });
|
|
1689
|
+
fs3.writeFileSync(ownerPath, JSON.stringify(record));
|
|
1690
|
+
return { lockDir, release: () => releaseIfOwned(lockDir, ownerPath, token) };
|
|
1691
|
+
};
|
|
1692
|
+
try {
|
|
1693
|
+
return createFresh();
|
|
1694
|
+
} catch (err) {
|
|
1695
|
+
if (err.code !== "EEXIST")
|
|
1696
|
+
throw err;
|
|
1697
|
+
}
|
|
1698
|
+
const owner = readOwner(ownerPath);
|
|
1699
|
+
const provenDead = owner !== null && clock.now() - owner.timestamp >= staleAfterMs && identity.processStart(owner.pid) !== owner.processStart;
|
|
1700
|
+
if (!provenDead)
|
|
1701
|
+
throw LockHeldError(lockDir);
|
|
1702
|
+
const reclaimDir = `${lockDir}.reclaim.${owner.token}`;
|
|
1703
|
+
try {
|
|
1704
|
+
fs3.renameSync(lockDir, reclaimDir);
|
|
1705
|
+
} catch {
|
|
1706
|
+
throw LockHeldError(lockDir);
|
|
1707
|
+
}
|
|
1708
|
+
fs3.rmSync(reclaimDir, { recursive: true, force: true });
|
|
1709
|
+
try {
|
|
1710
|
+
return createFresh();
|
|
1711
|
+
} catch {
|
|
1712
|
+
throw LockHeldError(lockDir);
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
// ../../packages/shared/dist/profile-switch/engine.js
|
|
1716
|
+
import fs4 from "fs";
|
|
1717
|
+
import path8 from "path";
|
|
1718
|
+
import os5 from "os";
|
|
1719
|
+
import crypto3 from "crypto";
|
|
1720
|
+
class SwitchEngineError extends Error {
|
|
1721
|
+
constructor(message) {
|
|
1722
|
+
super(message);
|
|
1723
|
+
this.name = "SwitchEngineError";
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
function namedError3(name, message) {
|
|
1727
|
+
const err = new SwitchEngineError(message);
|
|
1728
|
+
err.name = name;
|
|
1729
|
+
return err;
|
|
1730
|
+
}
|
|
1731
|
+
var UnknownProfileError = (profile, known) => namedError3("UnknownProfileError", `unknown profile "${profile}" \u2014 installed: ${known.length > 0 ? known.join(", ") : "none"}`);
|
|
1732
|
+
var NoHostsDetectedError = () => namedError3("NoHostsDetectedError", "no installed hosts found");
|
|
1733
|
+
function defaultStatePath(targetHome) {
|
|
1734
|
+
return path8.join(targetHome, ".config", "massa-ai", "install-state.json");
|
|
1735
|
+
}
|
|
1736
|
+
function resolveCommon(opts) {
|
|
1737
|
+
const targetHome = opts.targetHome ?? os5.homedir();
|
|
1738
|
+
const stateFilePath = opts.stateFilePath ?? defaultStatePath(targetHome);
|
|
1739
|
+
return { targetHome, stateFilePath };
|
|
1740
|
+
}
|
|
1741
|
+
function listProfiles(opts = {}) {
|
|
1742
|
+
const { targetHome, stateFilePath } = resolveCommon(opts);
|
|
1743
|
+
const state = readInstallState(stateFilePath);
|
|
1744
|
+
const universe = opts.hosts ?? HOSTS;
|
|
1745
|
+
const hosts = universe.map((host) => {
|
|
1746
|
+
const layout = resolveHostLayout(host, { targetHome, projectRoot: opts.projectRoot });
|
|
1747
|
+
if (layout.route === "skip") {
|
|
1748
|
+
return {
|
|
1749
|
+
host,
|
|
1750
|
+
installed: false,
|
|
1751
|
+
skipped: true,
|
|
1752
|
+
skipReason: layout.reason,
|
|
1753
|
+
activeProfile: null,
|
|
1754
|
+
bundleVersion: null,
|
|
1755
|
+
availableProfiles: []
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
const installed = fs4.existsSync(layout.activeDir);
|
|
1759
|
+
const availableProfiles = listVariantProfiles(layout);
|
|
1760
|
+
const platform = state.platforms[host];
|
|
1761
|
+
return {
|
|
1762
|
+
host,
|
|
1763
|
+
installed,
|
|
1764
|
+
skipped: false,
|
|
1765
|
+
skipReason: null,
|
|
1766
|
+
activeProfile: platform?.modelProfile?.profile ?? "balanced",
|
|
1767
|
+
bundleVersion: platform?.plugin?.version ?? null,
|
|
1768
|
+
availableProfiles
|
|
1769
|
+
};
|
|
1770
|
+
});
|
|
1771
|
+
return { hosts };
|
|
1772
|
+
}
|
|
1773
|
+
function listVariantProfiles(layout) {
|
|
1774
|
+
if (!fs4.existsSync(layout.variantsRoot))
|
|
1775
|
+
return [];
|
|
1776
|
+
return fs4.readdirSync(layout.variantsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
1777
|
+
}
|
|
1778
|
+
function matchesGlob(filename, glob) {
|
|
1779
|
+
const starIdx = glob.indexOf("*");
|
|
1780
|
+
if (starIdx === -1)
|
|
1781
|
+
return filename === glob;
|
|
1782
|
+
const prefix = glob.slice(0, starIdx);
|
|
1783
|
+
const suffix = glob.slice(starIdx + 1);
|
|
1784
|
+
return filename.length >= prefix.length + suffix.length && filename.startsWith(prefix) && filename.endsWith(suffix);
|
|
1785
|
+
}
|
|
1786
|
+
function assertStateWritable(stateFilePath) {
|
|
1787
|
+
const dir = path8.dirname(stateFilePath);
|
|
1788
|
+
try {
|
|
1789
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
1790
|
+
} catch (err) {
|
|
1791
|
+
throw UnwritableInstallStateError(stateFilePath, err.message);
|
|
1792
|
+
}
|
|
1793
|
+
const checkPath = fs4.existsSync(stateFilePath) ? stateFilePath : dir;
|
|
1794
|
+
try {
|
|
1795
|
+
fs4.accessSync(checkPath, fs4.constants.W_OK);
|
|
1796
|
+
} catch (err) {
|
|
1797
|
+
throw UnwritableInstallStateError(stateFilePath, err.message);
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
function copyFileRouteVariant(layout, variantDir) {
|
|
1801
|
+
fs4.mkdirSync(layout.activeDir, { recursive: true });
|
|
1802
|
+
let changed = 0;
|
|
1803
|
+
for (const entry of fs4.readdirSync(variantDir, { withFileTypes: true })) {
|
|
1804
|
+
if (!entry.isFile() || !matchesGlob(entry.name, layout.activeGlob))
|
|
1805
|
+
continue;
|
|
1806
|
+
fs4.copyFileSync(path8.join(variantDir, entry.name), path8.join(layout.activeDir, entry.name));
|
|
1807
|
+
changed++;
|
|
1808
|
+
}
|
|
1809
|
+
return changed;
|
|
1810
|
+
}
|
|
1811
|
+
function repointOpencodeVariant(layout, variantDir) {
|
|
1812
|
+
fs4.mkdirSync(layout.activeDir, { recursive: true });
|
|
1813
|
+
let changed = 0;
|
|
1814
|
+
for (const entry of fs4.readdirSync(variantDir, { withFileTypes: true })) {
|
|
1815
|
+
if (!entry.isFile() || !matchesGlob(entry.name, layout.activeGlob))
|
|
1816
|
+
continue;
|
|
1817
|
+
const dest = path8.join(layout.activeDir, entry.name);
|
|
1818
|
+
const target = path8.resolve(path8.join(variantDir, entry.name));
|
|
1819
|
+
let destExists = true;
|
|
1820
|
+
let destIsSymlink = false;
|
|
1821
|
+
try {
|
|
1822
|
+
destIsSymlink = fs4.lstatSync(dest).isSymbolicLink();
|
|
1823
|
+
} catch {
|
|
1824
|
+
destExists = false;
|
|
1825
|
+
}
|
|
1826
|
+
if (destExists && !destIsSymlink)
|
|
1827
|
+
continue;
|
|
1828
|
+
const tmp = `${dest}.massa-ai-switch.${crypto3.randomUUID()}`;
|
|
1829
|
+
fs4.symlinkSync(target, tmp);
|
|
1830
|
+
fs4.renameSync(tmp, dest);
|
|
1831
|
+
changed++;
|
|
1832
|
+
}
|
|
1833
|
+
return changed;
|
|
1834
|
+
}
|
|
1835
|
+
function copyVariant(host, layout, variantDir) {
|
|
1836
|
+
return host === "opencode" ? repointOpencodeVariant(layout, variantDir) : copyFileRouteVariant(layout, variantDir);
|
|
1837
|
+
}
|
|
1838
|
+
function switchProfile(opts) {
|
|
1839
|
+
const { targetHome, stateFilePath } = resolveCommon(opts);
|
|
1840
|
+
const dryRun = opts.dryRun ?? false;
|
|
1841
|
+
const requested = opts.host ? [opts.host] : opts.hosts ?? HOSTS;
|
|
1842
|
+
const universe = HOSTS.filter((h) => requested.includes(h));
|
|
1843
|
+
const state = readInstallState(stateFilePath);
|
|
1844
|
+
if (!dryRun)
|
|
1845
|
+
assertStateWritable(stateFilePath);
|
|
1846
|
+
const layouts = universe.map((host) => ({ host, layout: resolveHostLayout(host, { targetHome, projectRoot: opts.projectRoot }) }));
|
|
1847
|
+
const skipRows = layouts.filter((l) => l.layout.route === "skip").map((l) => ({ host: l.host, status: "skipped", reason: l.layout.reason }));
|
|
1848
|
+
const fileHosts = layouts.filter((l) => l.layout.route === "files");
|
|
1849
|
+
if (fileHosts.length === 0) {
|
|
1850
|
+
return { profile: opts.profile, dryRun, hosts: orderRows(universe, skipRows), restartRequired: false };
|
|
1851
|
+
}
|
|
1852
|
+
const installedFileHosts = fileHosts.filter((h) => fs4.existsSync(h.layout.activeDir));
|
|
1853
|
+
if (installedFileHosts.length === 0)
|
|
1854
|
+
throw NoHostsDetectedError();
|
|
1855
|
+
const withAvailability = fileHosts.map((h) => {
|
|
1856
|
+
const variantsRootExists = fs4.existsSync(h.layout.variantsRoot);
|
|
1857
|
+
const variantDir = h.layout.variantDir(opts.profile);
|
|
1858
|
+
const available = variantsRootExists && fs4.existsSync(variantDir) && fs4.statSync(variantDir).isDirectory();
|
|
1859
|
+
return { ...h, variantsRootExists, variantDir, available };
|
|
1860
|
+
});
|
|
1861
|
+
if (!withAvailability.some((h) => h.available)) {
|
|
1862
|
+
const known = new Set;
|
|
1863
|
+
for (const h of withAvailability) {
|
|
1864
|
+
if (!h.variantsRootExists)
|
|
1865
|
+
continue;
|
|
1866
|
+
for (const name of listVariantProfiles(h.layout))
|
|
1867
|
+
known.add(name);
|
|
1868
|
+
}
|
|
1869
|
+
throw UnknownProfileError(opts.profile, [...known].sort());
|
|
1870
|
+
}
|
|
1871
|
+
const lock = dryRun ? null : acquireLock(stateFilePath, opts.lock);
|
|
1872
|
+
try {
|
|
1873
|
+
const rows = [...skipRows];
|
|
1874
|
+
for (const h of withAvailability) {
|
|
1875
|
+
if (!h.variantsRootExists) {
|
|
1876
|
+
rows.push({ host: h.host, status: "unsupported", reason: "bundle has no variants \u2014 upgrade plugin" });
|
|
1877
|
+
continue;
|
|
1878
|
+
}
|
|
1879
|
+
if (!h.available) {
|
|
1880
|
+
const supported = listVariantProfiles(h.layout);
|
|
1881
|
+
rows.push({
|
|
1882
|
+
host: h.host,
|
|
1883
|
+
status: "unsupported",
|
|
1884
|
+
reason: `profile "${opts.profile}" not supported on ${h.host} (supports: ${supported.join(", ") || "none"})`
|
|
1885
|
+
});
|
|
1886
|
+
continue;
|
|
1887
|
+
}
|
|
1888
|
+
const route = detectRoute(state.platforms[h.host]);
|
|
1889
|
+
if (route.kind === "refuse") {
|
|
1890
|
+
rows.push({ host: h.host, status: "failed", reason: route.reason });
|
|
1891
|
+
continue;
|
|
1892
|
+
}
|
|
1893
|
+
if (dryRun) {
|
|
1894
|
+
rows.push({ host: h.host, status: "switched" });
|
|
1895
|
+
continue;
|
|
1896
|
+
}
|
|
1897
|
+
try {
|
|
1898
|
+
const filesChanged = copyVariant(h.host, h.layout, h.variantDir);
|
|
1899
|
+
updatePlatform(stateFilePath, h.host, {
|
|
1900
|
+
modelProfile: { profile: opts.profile, switchedAt: new Date().toISOString() }
|
|
1901
|
+
});
|
|
1902
|
+
rows.push({ host: h.host, status: "switched", filesChanged });
|
|
1903
|
+
} catch (err) {
|
|
1904
|
+
rows.push({ host: h.host, status: "failed", reason: err.message });
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
const ordered = orderRows(universe, rows);
|
|
1908
|
+
const restartRequired = !dryRun && ordered.some((r) => r.status === "switched");
|
|
1909
|
+
return { profile: opts.profile, dryRun, hosts: ordered, restartRequired };
|
|
1910
|
+
} finally {
|
|
1911
|
+
lock?.release();
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
function orderRows(universe, rows) {
|
|
1915
|
+
const byHost = new Map(rows.map((r) => [r.host, r]));
|
|
1916
|
+
return HOSTS.filter((h) => universe.includes(h) && byHost.has(h)).map((h) => byHost.get(h));
|
|
1917
|
+
}
|
|
1918
|
+
// ../../packages/shared/dist/profile-switch/report.js
|
|
1919
|
+
function reportSucceeded(report) {
|
|
1920
|
+
return report.hosts.every((h) => h.status === "switched" || h.status === "skipped");
|
|
1921
|
+
}
|
|
1922
|
+
// src/config-cli.ts
|
|
1923
|
+
import { promises as fs5 } from "fs";
|
|
1924
|
+
import path9 from "path";
|
|
1925
|
+
import os6 from "os";
|
|
1188
1926
|
import { fileURLToPath } from "url";
|
|
1189
|
-
var __dirname2 =
|
|
1927
|
+
var __dirname2 = path9.dirname(fileURLToPath(import.meta.url));
|
|
1190
1928
|
function help() {
|
|
1191
1929
|
console.log(`
|
|
1192
1930
|
massa-ai-config - Configuration manager for massa-ai
|
|
@@ -1212,6 +1950,11 @@ Commands:
|
|
|
1212
1950
|
agents install [--user|--project] Write 17 agent .md files
|
|
1213
1951
|
agents uninstall [--user|--project] Remove only massa-ai-owned agents
|
|
1214
1952
|
|
|
1953
|
+
profile list List shipped model profiles + per-host active profile
|
|
1954
|
+
profile show Same as 'profile list'
|
|
1955
|
+
profile set <name> [--host <h>] [--dry-run]
|
|
1956
|
+
Switch installed agents to a profile (restart required after)
|
|
1957
|
+
|
|
1215
1958
|
Examples:
|
|
1216
1959
|
massa-ai-config init
|
|
1217
1960
|
massa-ai-config init --mistral your-api-key
|
|
@@ -1219,6 +1962,7 @@ Examples:
|
|
|
1219
1962
|
massa-ai-config use mistral --api-key your-key
|
|
1220
1963
|
massa-ai-config set embedding.dimensions 1024
|
|
1221
1964
|
massa-ai-config agents install --user
|
|
1965
|
+
massa-ai-config profile set work --dry-run
|
|
1222
1966
|
`);
|
|
1223
1967
|
}
|
|
1224
1968
|
function parseOptions(args) {
|
|
@@ -1236,6 +1980,30 @@ function parseOptions(args) {
|
|
|
1236
1980
|
}
|
|
1237
1981
|
return options;
|
|
1238
1982
|
}
|
|
1983
|
+
function formatProfileInventory(inventory) {
|
|
1984
|
+
for (const h of inventory.hosts) {
|
|
1985
|
+
if (h.skipped) {
|
|
1986
|
+
console.log(` ${h.host}: skipped (${h.skipReason})`);
|
|
1987
|
+
continue;
|
|
1988
|
+
}
|
|
1989
|
+
if (!h.installed) {
|
|
1990
|
+
console.log(` ${h.host}: not installed`);
|
|
1991
|
+
continue;
|
|
1992
|
+
}
|
|
1993
|
+
console.log(` ${h.host}: active=${h.activeProfile} bundle=${h.bundleVersion ?? "unknown"} supports=${h.availableProfiles.length > 0 ? h.availableProfiles.join(",") : "none"}`);
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
function formatSwitchReport(report) {
|
|
1997
|
+
console.log(`profile: ${report.profile}${report.dryRun ? " (dry run \u2014 no files changed)" : ""}`);
|
|
1998
|
+
for (const row of report.hosts) {
|
|
1999
|
+
const detail = row.reason ? `: ${row.reason}` : row.filesChanged !== undefined ? ` (${row.filesChanged} files changed)` : "";
|
|
2000
|
+
console.log(` ${row.host}: ${row.status}${detail}`);
|
|
2001
|
+
}
|
|
2002
|
+
if (report.restartRequired) {
|
|
2003
|
+
console.log(`
|
|
2004
|
+
A host session restart is required for the change to take effect.`);
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
1239
2007
|
async function runCli(argv) {
|
|
1240
2008
|
const args = argv;
|
|
1241
2009
|
const command = args[0];
|
|
@@ -1358,18 +2126,18 @@ Using defaults:`);
|
|
|
1358
2126
|
return 1;
|
|
1359
2127
|
}
|
|
1360
2128
|
const scope = typeof options.project === "boolean" ? "project" : "user";
|
|
1361
|
-
const agentsDir = scope === "project" ?
|
|
1362
|
-
const sourceAgentsDir =
|
|
2129
|
+
const agentsDir = scope === "project" ? path9.join(process.cwd(), ".opencode/agents") : path9.join(process.env.XDG_CONFIG_HOME && process.env.XDG_CONFIG_HOME.trim() || path9.join(os6.homedir(), ".config"), "opencode", "agents");
|
|
2130
|
+
const sourceAgentsDir = path9.resolve(__dirname2, "..", "agents");
|
|
1363
2131
|
if (subcommand === "install") {
|
|
1364
|
-
await
|
|
2132
|
+
await fs5.mkdir(agentsDir, { recursive: true });
|
|
1365
2133
|
let count = 0;
|
|
1366
|
-
const entries = await
|
|
2134
|
+
const entries = await fs5.readdir(sourceAgentsDir);
|
|
1367
2135
|
for (const entry of entries) {
|
|
1368
2136
|
if (!entry.startsWith("massa-ai-") || !entry.endsWith(".md"))
|
|
1369
2137
|
continue;
|
|
1370
|
-
const src =
|
|
1371
|
-
const dest =
|
|
1372
|
-
await
|
|
2138
|
+
const src = path9.join(sourceAgentsDir, entry);
|
|
2139
|
+
const dest = path9.join(agentsDir, entry);
|
|
2140
|
+
await fs5.copyFile(src, dest);
|
|
1373
2141
|
count++;
|
|
1374
2142
|
}
|
|
1375
2143
|
console.log(`+ ${count} subagent specialists (generated from skills/agents/*/SKILL.md)`);
|
|
@@ -1377,14 +2145,14 @@ Using defaults:`);
|
|
|
1377
2145
|
} else {
|
|
1378
2146
|
let removed = 0;
|
|
1379
2147
|
try {
|
|
1380
|
-
const entries = await
|
|
2148
|
+
const entries = await fs5.readdir(agentsDir);
|
|
1381
2149
|
for (const entry of entries) {
|
|
1382
2150
|
if (!entry.startsWith("massa-ai-") || !entry.endsWith(".md"))
|
|
1383
2151
|
continue;
|
|
1384
|
-
const filePath =
|
|
1385
|
-
const content = await
|
|
2152
|
+
const filePath = path9.join(agentsDir, entry);
|
|
2153
|
+
const content = await fs5.readFile(filePath, "utf8");
|
|
1386
2154
|
if (content.includes("massa-ai-owned: true")) {
|
|
1387
|
-
await
|
|
2155
|
+
await fs5.unlink(filePath);
|
|
1388
2156
|
removed++;
|
|
1389
2157
|
}
|
|
1390
2158
|
}
|
|
@@ -1397,6 +2165,44 @@ Using defaults:`);
|
|
|
1397
2165
|
}
|
|
1398
2166
|
break;
|
|
1399
2167
|
}
|
|
2168
|
+
case "profile": {
|
|
2169
|
+
const subcommand = args[1];
|
|
2170
|
+
if (subcommand === "list" || subcommand === "show") {
|
|
2171
|
+
try {
|
|
2172
|
+
formatProfileInventory(listProfiles());
|
|
2173
|
+
} catch (e) {
|
|
2174
|
+
console.error(`Error: ${e.message}`);
|
|
2175
|
+
return 1;
|
|
2176
|
+
}
|
|
2177
|
+
return 0;
|
|
2178
|
+
}
|
|
2179
|
+
if (subcommand === "set") {
|
|
2180
|
+
const name = args[2];
|
|
2181
|
+
if (!name) {
|
|
2182
|
+
console.error("Usage: massa-ai-config profile set <name> [--host <h>] [--dry-run]");
|
|
2183
|
+
return 1;
|
|
2184
|
+
}
|
|
2185
|
+
const hostOpt = typeof options.host === "string" ? options.host : undefined;
|
|
2186
|
+
if (hostOpt !== undefined && !isHost(hostOpt)) {
|
|
2187
|
+
console.error(`Error: unknown host "${hostOpt}"`);
|
|
2188
|
+
return 1;
|
|
2189
|
+
}
|
|
2190
|
+
try {
|
|
2191
|
+
const report = switchProfile({
|
|
2192
|
+
profile: name,
|
|
2193
|
+
host: hostOpt,
|
|
2194
|
+
dryRun: options["dry-run"] === true
|
|
2195
|
+
});
|
|
2196
|
+
formatSwitchReport(report);
|
|
2197
|
+
return reportSucceeded(report) ? 0 : 1;
|
|
2198
|
+
} catch (e) {
|
|
2199
|
+
console.error(`Error: ${e.message}`);
|
|
2200
|
+
return 1;
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
console.error("Usage: massa-ai-config profile <list|show|set> ...");
|
|
2204
|
+
return 1;
|
|
2205
|
+
}
|
|
1400
2206
|
default:
|
|
1401
2207
|
console.error(`Unknown command: ${command}`);
|
|
1402
2208
|
help();
|