@nikelyh/metamorph 2.1.1 → 2.2.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/README.md +27 -3
- package/dist/ProjectDetector-TPATF25O.js +16 -0
- package/dist/chunk-4G3X6IOY.js +533 -0
- package/dist/{chunk-L7B6KUCW.js → chunk-6PDOFLME.js} +276 -25
- package/dist/{chunk-VEAYUIOZ.js → chunk-7LIGC5J3.js} +257 -0
- package/dist/chunk-BWUBT277.js +181 -0
- package/dist/{chunk-GKRBTA6E.js → chunk-BWZEGLCH.js} +7 -1
- package/dist/index.js +4471 -2741
- package/dist/projectConfigAdapter-Q62XAAF6.js +14 -0
- package/dist/public/assets/index-3jZMGi8l.css +1 -0
- package/dist/public/assets/index-BiBex3Xf.js +63 -0
- package/dist/public/index.html +2 -2
- package/dist/{runtime-L7VOQROE.js → runtime-UX3643CQ.js} +2 -1
- package/dist/{src-JHOGUCRS.js → src-CWC7KTYT.js} +18 -4
- package/dist/src-YET7DW3S.js +72 -0
- package/package.json +5 -5
- package/dist/NextMigrationHints-BGO2W3K4.js +0 -32
- package/dist/TechDetector-5YP44RVR.js +0 -6
- package/dist/chunk-3QN2Q75J.js +0 -101
- package/dist/chunk-H2LZEPNI.js +0 -352
- package/dist/public/assets/index-C0AxAuiv.css +0 -1
- package/dist/public/assets/index-DtMWFmXl.js +0 -63
- package/dist/src-UC3SN46X.js +0 -40
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
DEFAULT_METAMORPH_CONFIG,
|
|
3
|
+
createEmptyMigrationCostSummary,
|
|
4
|
+
resolveMigrationCatalog,
|
|
5
|
+
validateDisabledAgents
|
|
6
|
+
} from "./chunk-7LIGC5J3.js";
|
|
4
7
|
|
|
5
8
|
// ../infrastructure/src/db/SQLiteStateStore.ts
|
|
6
9
|
import { DatabaseSync } from "node:sqlite";
|
|
@@ -62,6 +65,10 @@ var SQLiteStateStore = class {
|
|
|
62
65
|
this.db.exec("ALTER TABLE plans ADD COLUMN applied_branch TEXT");
|
|
63
66
|
} catch (e) {
|
|
64
67
|
}
|
|
68
|
+
try {
|
|
69
|
+
this.db.exec("ALTER TABLE plans ADD COLUMN package_manager TEXT DEFAULT 'npm'");
|
|
70
|
+
} catch (e) {
|
|
71
|
+
}
|
|
65
72
|
this.db.exec(`
|
|
66
73
|
CREATE TABLE IF NOT EXISTS tasks (
|
|
67
74
|
plan_id TEXT,
|
|
@@ -81,11 +88,29 @@ var SQLiteStateStore = class {
|
|
|
81
88
|
timestamp TEXT
|
|
82
89
|
);
|
|
83
90
|
`);
|
|
91
|
+
this.db.exec(`
|
|
92
|
+
CREATE TABLE IF NOT EXISTS token_usage (
|
|
93
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
94
|
+
run_id TEXT NOT NULL,
|
|
95
|
+
plan_id TEXT NOT NULL,
|
|
96
|
+
agent_role TEXT NOT NULL,
|
|
97
|
+
model_id TEXT NOT NULL,
|
|
98
|
+
prompt_tokens INTEGER NOT NULL,
|
|
99
|
+
completion_tokens INTEGER NOT NULL,
|
|
100
|
+
total_tokens INTEGER NOT NULL,
|
|
101
|
+
cost_usd REAL NOT NULL,
|
|
102
|
+
timestamp TEXT NOT NULL
|
|
103
|
+
);
|
|
104
|
+
`);
|
|
105
|
+
try {
|
|
106
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_token_usage_run ON token_usage(run_id);");
|
|
107
|
+
} catch (e) {
|
|
108
|
+
}
|
|
84
109
|
}
|
|
85
110
|
async savePlan(plan) {
|
|
86
111
|
const stmtPlan = this.db.prepare(`
|
|
87
|
-
INSERT OR REPLACE INTO plans (id, run_id, target_path, source_framework, target_framework, rules_json, created_at, integration_rounds, phase, outcome, applied_at, applied_branch)
|
|
88
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
112
|
+
INSERT OR REPLACE INTO plans (id, run_id, target_path, source_framework, target_framework, rules_json, created_at, integration_rounds, phase, outcome, applied_at, applied_branch, package_manager)
|
|
113
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
89
114
|
`);
|
|
90
115
|
stmtPlan.run(
|
|
91
116
|
plan.id,
|
|
@@ -99,7 +124,8 @@ var SQLiteStateStore = class {
|
|
|
99
124
|
plan.phase || "files",
|
|
100
125
|
plan.outcome || null,
|
|
101
126
|
plan.appliedAt ? plan.appliedAt.toISOString() : null,
|
|
102
|
-
plan.appliedBranch || null
|
|
127
|
+
plan.appliedBranch || null,
|
|
128
|
+
plan.packageManager || "npm"
|
|
103
129
|
);
|
|
104
130
|
const stmtTask = this.db.prepare(`
|
|
105
131
|
INSERT OR REPLACE INTO tasks (plan_id, file_path, status, dependencies_json, error)
|
|
@@ -120,6 +146,7 @@ var SQLiteStateStore = class {
|
|
|
120
146
|
id: planRow.id,
|
|
121
147
|
runId: planRow.run_id,
|
|
122
148
|
targetPath: planRow.target_path,
|
|
149
|
+
packageManager: planRow.package_manager || "npm",
|
|
123
150
|
profile: {
|
|
124
151
|
source: planRow.source_framework,
|
|
125
152
|
target: planRow.target_framework,
|
|
@@ -185,10 +212,65 @@ var SQLiteStateStore = class {
|
|
|
185
212
|
timestamp: new Date(row.timestamp)
|
|
186
213
|
}));
|
|
187
214
|
}
|
|
215
|
+
async recordTokenUsage(record) {
|
|
216
|
+
const stmt = this.db.prepare(`
|
|
217
|
+
INSERT INTO token_usage (run_id, plan_id, agent_role, model_id, prompt_tokens, completion_tokens, total_tokens, cost_usd, timestamp)
|
|
218
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
219
|
+
`);
|
|
220
|
+
const timestamp = (record.timestamp || /* @__PURE__ */ new Date()).toISOString();
|
|
221
|
+
stmt.run(
|
|
222
|
+
record.runId,
|
|
223
|
+
record.planId,
|
|
224
|
+
record.agentRole,
|
|
225
|
+
record.modelId,
|
|
226
|
+
record.promptTokens,
|
|
227
|
+
record.completionTokens,
|
|
228
|
+
record.totalTokens,
|
|
229
|
+
record.costUsd,
|
|
230
|
+
timestamp
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
async getCostSummary(runId) {
|
|
234
|
+
const stmt = this.db.prepare(`
|
|
235
|
+
SELECT * FROM token_usage WHERE run_id = ? ORDER BY id ASC
|
|
236
|
+
`);
|
|
237
|
+
const rows = stmt.all(runId);
|
|
238
|
+
const planId = rows.length > 0 ? rows[0].plan_id : "";
|
|
239
|
+
const summary = createEmptyMigrationCostSummary(runId, planId);
|
|
240
|
+
for (const row of rows) {
|
|
241
|
+
summary.totalTokens += row.total_tokens;
|
|
242
|
+
summary.promptTokens += row.prompt_tokens;
|
|
243
|
+
summary.completionTokens += row.completion_tokens;
|
|
244
|
+
summary.totalCostUsd += row.cost_usd;
|
|
245
|
+
const role = row.agent_role;
|
|
246
|
+
if (summary.byAgentRole[role]) {
|
|
247
|
+
summary.byAgentRole[role].tokens += row.total_tokens;
|
|
248
|
+
summary.byAgentRole[role].promptTokens += row.prompt_tokens;
|
|
249
|
+
summary.byAgentRole[role].completionTokens += row.completion_tokens;
|
|
250
|
+
summary.byAgentRole[role].costUsd += row.cost_usd;
|
|
251
|
+
summary.byAgentRole[role].executions += 1;
|
|
252
|
+
}
|
|
253
|
+
if (!summary.byModel[row.model_id]) {
|
|
254
|
+
summary.byModel[row.model_id] = {
|
|
255
|
+
promptTokens: 0,
|
|
256
|
+
completionTokens: 0,
|
|
257
|
+
totalTokens: 0,
|
|
258
|
+
costUsd: 0
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
summary.byModel[row.model_id].promptTokens += row.prompt_tokens;
|
|
262
|
+
summary.byModel[row.model_id].completionTokens += row.completion_tokens;
|
|
263
|
+
summary.byModel[row.model_id].totalTokens += row.total_tokens;
|
|
264
|
+
summary.byModel[row.model_id].costUsd += row.cost_usd;
|
|
265
|
+
}
|
|
266
|
+
summary.totalCostUsd = Math.round(summary.totalCostUsd * 1e6) / 1e6;
|
|
267
|
+
return summary;
|
|
268
|
+
}
|
|
188
269
|
async reset() {
|
|
189
270
|
this.db.exec(`DELETE FROM tasks`);
|
|
190
271
|
this.db.exec(`DELETE FROM plans`);
|
|
191
272
|
this.db.exec(`DELETE FROM events`);
|
|
273
|
+
this.db.exec(`DELETE FROM token_usage`);
|
|
192
274
|
}
|
|
193
275
|
};
|
|
194
276
|
|
|
@@ -300,9 +382,9 @@ function createRenameFileTool(sandboxDir) {
|
|
|
300
382
|
if (!fsExists(resolvedOld)) {
|
|
301
383
|
return { error: `File not found: ${resolvedOld}` };
|
|
302
384
|
}
|
|
303
|
-
const { mkdirSync:
|
|
304
|
-
const { dirname } = await import("path");
|
|
305
|
-
|
|
385
|
+
const { mkdirSync: mkdirSync3 } = await import("fs");
|
|
386
|
+
const { dirname: dirname2 } = await import("path");
|
|
387
|
+
mkdirSync3(dirname2(resolvedNew), { recursive: true });
|
|
306
388
|
renameSync(resolvedOld, resolvedNew);
|
|
307
389
|
const sourceFile = project.getSourceFile(resolvedOld);
|
|
308
390
|
if (sourceFile) {
|
|
@@ -332,10 +414,10 @@ function createCreateFileTool(sandboxDir) {
|
|
|
332
414
|
strict: true,
|
|
333
415
|
invoke: async ({ filePath, content }) => {
|
|
334
416
|
const resolvedPath = assertSandbox(filePath, sandboxDir);
|
|
335
|
-
const { writeFileSync, mkdirSync:
|
|
336
|
-
const { dirname } = await import("path");
|
|
337
|
-
|
|
338
|
-
|
|
417
|
+
const { writeFileSync: writeFileSync2, mkdirSync: mkdirSync3 } = await import("fs");
|
|
418
|
+
const { dirname: dirname2 } = await import("path");
|
|
419
|
+
mkdirSync3(dirname2(resolvedPath), { recursive: true });
|
|
420
|
+
writeFileSync2(resolvedPath, content, "utf-8");
|
|
339
421
|
project.addSourceFileAtPath(resolvedPath);
|
|
340
422
|
return { success: true, message: `File created at ${resolvedPath}.` };
|
|
341
423
|
}
|
|
@@ -670,15 +752,28 @@ async function createApiServer(store, migrationRunner) {
|
|
|
670
752
|
}
|
|
671
753
|
}
|
|
672
754
|
});
|
|
755
|
+
app.get("/api/cost/:runId", async (req, res) => {
|
|
756
|
+
try {
|
|
757
|
+
const runId = String(req.params.runId);
|
|
758
|
+
const summary = await store.getCostSummary(runId);
|
|
759
|
+
res.json(summary);
|
|
760
|
+
} catch (error) {
|
|
761
|
+
if (error instanceof Error) {
|
|
762
|
+
res.status(500).json({ error: error.message });
|
|
763
|
+
} else {
|
|
764
|
+
res.status(500).json({ error: String(error) });
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
});
|
|
673
768
|
app.get("/api/browse", async (req, res) => {
|
|
674
769
|
try {
|
|
675
770
|
const { readdirSync, statSync: statSync2 } = await import("fs");
|
|
676
|
-
const { resolve: resolve5, join:
|
|
771
|
+
const { resolve: resolve5, join: join8 } = await import("path");
|
|
677
772
|
const targetPath = req.query.path ? resolve5(String(req.query.path)) : resolve5(".");
|
|
678
773
|
const entries = readdirSync(targetPath).filter(
|
|
679
774
|
(name) => !name.startsWith(".") && name !== "node_modules" && name !== "dist" && name !== "build"
|
|
680
775
|
).map((name) => {
|
|
681
|
-
const full =
|
|
776
|
+
const full = join8(targetPath, name);
|
|
682
777
|
try {
|
|
683
778
|
const stat = statSync2(full);
|
|
684
779
|
return {
|
|
@@ -705,12 +800,16 @@ async function createApiServer(store, migrationRunner) {
|
|
|
705
800
|
});
|
|
706
801
|
app.get("/api/detect", async (req, res) => {
|
|
707
802
|
try {
|
|
708
|
-
const { detectTechnologies } = await import("./
|
|
803
|
+
const { detectTechnologies: detectTechnologies2, inspectProject: inspectProject2, detectMonorepo } = await import("./ProjectDetector-TPATF25O.js");
|
|
709
804
|
const targetPath = req.query.path ? String(req.query.path) : ".";
|
|
710
|
-
const
|
|
805
|
+
const monorepo = detectMonorepo(targetPath);
|
|
806
|
+
const profiles = inspectProject2(targetPath);
|
|
807
|
+
const detected = detectTechnologies2(targetPath);
|
|
711
808
|
res.json({
|
|
712
809
|
detected,
|
|
713
|
-
|
|
810
|
+
profiles,
|
|
811
|
+
monorepo: monorepo.isMonorepo ? monorepo : null,
|
|
812
|
+
primary: profiles.length > 0 ? profiles[0].framework : null
|
|
714
813
|
});
|
|
715
814
|
} catch (error) {
|
|
716
815
|
if (error instanceof Error) {
|
|
@@ -825,16 +924,16 @@ async function createApiServer(store, migrationRunner) {
|
|
|
825
924
|
}
|
|
826
925
|
});
|
|
827
926
|
}
|
|
828
|
-
const
|
|
829
|
-
const
|
|
830
|
-
let publicDir =
|
|
831
|
-
if (!
|
|
832
|
-
publicDir =
|
|
927
|
+
const path6 = await import("path");
|
|
928
|
+
const fs5 = await import("fs");
|
|
929
|
+
let publicDir = path6.join(import.meta.dirname, "public");
|
|
930
|
+
if (!fs5.existsSync(publicDir)) {
|
|
931
|
+
publicDir = path6.join(import.meta.dirname, "../public");
|
|
833
932
|
}
|
|
834
|
-
if (
|
|
933
|
+
if (fs5.existsSync(publicDir)) {
|
|
835
934
|
app.use(express.static(publicDir));
|
|
836
935
|
app.get(/^(.*)$/, (_req, res) => {
|
|
837
|
-
res.sendFile(
|
|
936
|
+
res.sendFile(path6.join(publicDir, "index.html"));
|
|
838
937
|
});
|
|
839
938
|
}
|
|
840
939
|
return app;
|
|
@@ -997,6 +1096,156 @@ ${result.output}`;
|
|
|
997
1096
|
}
|
|
998
1097
|
});
|
|
999
1098
|
|
|
1099
|
+
// ../infrastructure/src/config/ConfigStore.ts
|
|
1100
|
+
import * as fs4 from "fs";
|
|
1101
|
+
import * as path5 from "path";
|
|
1102
|
+
import * as os from "os";
|
|
1103
|
+
var CONFIG_FILE_NAME = ".metamorphrc.json";
|
|
1104
|
+
var ConfigStore = class {
|
|
1105
|
+
/**
|
|
1106
|
+
* Retrieves the absolute path to the local or global configuration file.
|
|
1107
|
+
*/
|
|
1108
|
+
static getConfigPath(isGlobal = false, cwd = process.cwd()) {
|
|
1109
|
+
return isGlobal ? path5.join(os.homedir(), CONFIG_FILE_NAME) : path5.join(cwd, CONFIG_FILE_NAME);
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Reads a JSON configuration file safely.
|
|
1113
|
+
*/
|
|
1114
|
+
static readConfigFile(filePath) {
|
|
1115
|
+
if (!fs4.existsSync(filePath)) {
|
|
1116
|
+
return null;
|
|
1117
|
+
}
|
|
1118
|
+
try {
|
|
1119
|
+
const raw = fs4.readFileSync(filePath, "utf-8");
|
|
1120
|
+
const parsed = JSON.parse(raw);
|
|
1121
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1122
|
+
return null;
|
|
1123
|
+
}
|
|
1124
|
+
if (Array.isArray(parsed.disabledAgents)) {
|
|
1125
|
+
const validation = validateDisabledAgents(parsed.disabledAgents);
|
|
1126
|
+
if (validation.valid) {
|
|
1127
|
+
parsed.disabledAgents = validation.disabled;
|
|
1128
|
+
} else {
|
|
1129
|
+
delete parsed.disabledAgents;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
return parsed;
|
|
1133
|
+
} catch {
|
|
1134
|
+
return null;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
/**
|
|
1138
|
+
* Writes a JSON configuration file safely, creating parent folders if necessary.
|
|
1139
|
+
*/
|
|
1140
|
+
static writeConfigFile(filePath, config) {
|
|
1141
|
+
const dir = path5.dirname(filePath);
|
|
1142
|
+
if (!fs4.existsSync(dir)) {
|
|
1143
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
1144
|
+
}
|
|
1145
|
+
fs4.writeFileSync(filePath, JSON.stringify(config, null, 2), "utf-8");
|
|
1146
|
+
}
|
|
1147
|
+
/**
|
|
1148
|
+
* Reads the local configuration file (.metamorphrc.json) from the target directory.
|
|
1149
|
+
*/
|
|
1150
|
+
static loadLocalConfig(cwd = process.cwd()) {
|
|
1151
|
+
return this.readConfigFile(this.getConfigPath(false, cwd));
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* Reads the global configuration file (~/.metamorphrc.json) from the user's home directory.
|
|
1155
|
+
*/
|
|
1156
|
+
static loadGlobalConfig() {
|
|
1157
|
+
return this.readConfigFile(this.getConfigPath(true));
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Persists a key-value update into the local configuration file.
|
|
1161
|
+
*/
|
|
1162
|
+
static saveLocalConfig(config, cwd = process.cwd()) {
|
|
1163
|
+
const existing = this.loadLocalConfig(cwd) || {};
|
|
1164
|
+
const updated = { ...existing, ...config };
|
|
1165
|
+
this.writeConfigFile(this.getConfigPath(false, cwd), updated);
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Persists a key-value update into the global configuration file.
|
|
1169
|
+
*/
|
|
1170
|
+
static saveGlobalConfig(config) {
|
|
1171
|
+
const existing = this.loadGlobalConfig() || {};
|
|
1172
|
+
const updated = { ...existing, ...config };
|
|
1173
|
+
this.writeConfigFile(this.getConfigPath(true), updated);
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Extracts environment variable configurations if present.
|
|
1177
|
+
*/
|
|
1178
|
+
static loadEnvConfig(env = process.env) {
|
|
1179
|
+
const envConfig = {};
|
|
1180
|
+
if (env.METAMORPH_MODEL) {
|
|
1181
|
+
envConfig.model = env.METAMORPH_MODEL;
|
|
1182
|
+
}
|
|
1183
|
+
if (env.METAMORPH_REVIEWER_MODEL) {
|
|
1184
|
+
envConfig.reviewerModel = env.METAMORPH_REVIEWER_MODEL;
|
|
1185
|
+
}
|
|
1186
|
+
if (env.METAMORPH_CONCURRENCY) {
|
|
1187
|
+
const parsed = parseInt(env.METAMORPH_CONCURRENCY, 10);
|
|
1188
|
+
if (!Number.isNaN(parsed) && parsed > 0) {
|
|
1189
|
+
envConfig.concurrency = parsed;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
if (env.METAMORPH_INFERENCE_TIMEOUT_MS) {
|
|
1193
|
+
const parsed = parseInt(env.METAMORPH_INFERENCE_TIMEOUT_MS, 10);
|
|
1194
|
+
if (!Number.isNaN(parsed) && parsed > 0) {
|
|
1195
|
+
envConfig.inferenceTimeoutMs = parsed;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
if (env.METAMORPH_MAX_RETRIES) {
|
|
1199
|
+
const parsed = parseInt(env.METAMORPH_MAX_RETRIES, 10);
|
|
1200
|
+
if (!Number.isNaN(parsed) && parsed >= 0) {
|
|
1201
|
+
envConfig.maxRetries = parsed;
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
if (env.METAMORPH_INTEGRATION_ROUNDS) {
|
|
1205
|
+
const parsed = parseInt(env.METAMORPH_INTEGRATION_ROUNDS, 10);
|
|
1206
|
+
if (!Number.isNaN(parsed) && parsed > 0) {
|
|
1207
|
+
envConfig.maxIntegrationRounds = parsed;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
if (env.METAMORPH_DISABLED_AGENTS) {
|
|
1211
|
+
const parts = env.METAMORPH_DISABLED_AGENTS.split(",").map((s) => s.trim());
|
|
1212
|
+
const validation = validateDisabledAgents(parts);
|
|
1213
|
+
if (validation.valid) {
|
|
1214
|
+
envConfig.disabledAgents = validation.disabled;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
return envConfig;
|
|
1218
|
+
}
|
|
1219
|
+
/**
|
|
1220
|
+
* Resolves the final effective MetamorphConfig applying the cascading precedence:
|
|
1221
|
+
* CLI Flags > Environment Variables > Local Config > Global Config > Defaults.
|
|
1222
|
+
*/
|
|
1223
|
+
static resolveConfig(options = {}) {
|
|
1224
|
+
const cwd = options.cwd || process.cwd();
|
|
1225
|
+
const globalConfig = this.loadGlobalConfig() || {};
|
|
1226
|
+
const localConfig = this.loadLocalConfig(cwd) || {};
|
|
1227
|
+
const envConfig = this.loadEnvConfig(options.env || process.env);
|
|
1228
|
+
const cliFlags = options.cliFlags || {};
|
|
1229
|
+
const clean = (obj) => {
|
|
1230
|
+
const res = {};
|
|
1231
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
1232
|
+
if (v !== void 0 && v !== null) {
|
|
1233
|
+
res[k] = v;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
return res;
|
|
1237
|
+
};
|
|
1238
|
+
const merged = {
|
|
1239
|
+
...DEFAULT_METAMORPH_CONFIG,
|
|
1240
|
+
...clean(globalConfig),
|
|
1241
|
+
...clean(localConfig),
|
|
1242
|
+
...clean(envConfig),
|
|
1243
|
+
...clean(cliFlags)
|
|
1244
|
+
};
|
|
1245
|
+
return merged;
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
|
|
1000
1249
|
export {
|
|
1001
1250
|
SQLiteStateStore,
|
|
1002
1251
|
assertSandbox,
|
|
@@ -1014,5 +1263,7 @@ export {
|
|
|
1014
1263
|
createCheckProjectDiagnosticsTool,
|
|
1015
1264
|
runInShadowWorkspace,
|
|
1016
1265
|
parseImplicatedFiles,
|
|
1017
|
-
createRunBuildTool
|
|
1266
|
+
createRunBuildTool,
|
|
1267
|
+
CONFIG_FILE_NAME,
|
|
1268
|
+
ConfigStore
|
|
1018
1269
|
};
|
|
@@ -1,3 +1,41 @@
|
|
|
1
|
+
// ../domain/src/entities/PackageManagerCommands.ts
|
|
2
|
+
function resolvePackageManagerCommands(pm) {
|
|
3
|
+
switch (pm) {
|
|
4
|
+
case "pnpm":
|
|
5
|
+
return {
|
|
6
|
+
name: "pnpm",
|
|
7
|
+
install: "pnpm install",
|
|
8
|
+
cleanInstall: "pnpm install",
|
|
9
|
+
runBuild: "pnpm run build",
|
|
10
|
+
runDev: "pnpm dev"
|
|
11
|
+
};
|
|
12
|
+
case "yarn":
|
|
13
|
+
return {
|
|
14
|
+
name: "yarn",
|
|
15
|
+
install: "yarn install",
|
|
16
|
+
cleanInstall: "yarn install",
|
|
17
|
+
runBuild: "yarn build",
|
|
18
|
+
runDev: "yarn dev"
|
|
19
|
+
};
|
|
20
|
+
case "bun":
|
|
21
|
+
return {
|
|
22
|
+
name: "bun",
|
|
23
|
+
install: "bun install",
|
|
24
|
+
cleanInstall: "bun install",
|
|
25
|
+
runBuild: "bun run build",
|
|
26
|
+
runDev: "bun dev"
|
|
27
|
+
};
|
|
28
|
+
default:
|
|
29
|
+
return {
|
|
30
|
+
name: "npm",
|
|
31
|
+
install: "npm install",
|
|
32
|
+
cleanInstall: "npm install --no-fund --no-audit",
|
|
33
|
+
runBuild: "npm run build",
|
|
34
|
+
runDev: "npm run dev"
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
1
39
|
// ../domain/src/entities/catalogs/backendCatalog.ts
|
|
2
40
|
var backendCatalog = [
|
|
3
41
|
{
|
|
@@ -65,10 +103,14 @@ export function createAppRouter(appService: AppService): Router {
|
|
|
65
103
|
"@nestjs/cli": "latest",
|
|
66
104
|
"@nestjs/schematics": "latest",
|
|
67
105
|
"@nestjs/testing": "latest",
|
|
106
|
+
"@types/node": "^20.0.0",
|
|
68
107
|
"@types/supertest": "^2.0.12",
|
|
69
108
|
"source-map-support": "^0.5.21",
|
|
70
109
|
"supertest": "^6.3.3"
|
|
71
110
|
},
|
|
111
|
+
scriptsToUpdate: {
|
|
112
|
+
"build": "tsc"
|
|
113
|
+
},
|
|
72
114
|
architecturalRules: [
|
|
73
115
|
"Express routing logic must be encapsulated in classes decorated with @Controller().",
|
|
74
116
|
"Business logic must be encapsulated in classes decorated with @Injectable() (Providers/Services).",
|
|
@@ -263,10 +305,15 @@ app.listen(3000);`
|
|
|
263
305
|
"@nestjs/cli": "latest",
|
|
264
306
|
"@nestjs/schematics": "latest",
|
|
265
307
|
"@nestjs/testing": "latest",
|
|
308
|
+
"@types/node": "^20.0.0",
|
|
266
309
|
"@types/supertest": "^2.0.12",
|
|
267
310
|
"source-map-support": "^0.5.21",
|
|
268
311
|
"supertest": "^6.3.3"
|
|
269
312
|
},
|
|
313
|
+
scriptsToUpdate: {
|
|
314
|
+
"build": "tsc"
|
|
315
|
+
},
|
|
316
|
+
scriptsToRemove: ["dev:start", "watch:ts"],
|
|
270
317
|
architecturalRules: [
|
|
271
318
|
"Fastify routing logic must be encapsulated in classes decorated with @Controller().",
|
|
272
319
|
"Business logic must be encapsulated in classes decorated with @Injectable() (Providers/Services).",
|
|
@@ -1218,6 +1265,198 @@ function findMigrationCatalogEntry(source, target) {
|
|
|
1218
1265
|
return resolveMigrationCatalog(source, target);
|
|
1219
1266
|
}
|
|
1220
1267
|
|
|
1268
|
+
// ../domain/src/entities/MetamorphConfig.ts
|
|
1269
|
+
var DEFAULT_METAMORPH_CONFIG = {
|
|
1270
|
+
model: "gpt-5.4",
|
|
1271
|
+
concurrency: 3,
|
|
1272
|
+
inferenceTimeoutMs: 12e4,
|
|
1273
|
+
maxRetries: 2,
|
|
1274
|
+
maxIntegrationRounds: 4,
|
|
1275
|
+
disabledAgents: []
|
|
1276
|
+
};
|
|
1277
|
+
var MOZAIK_BUNDLED_MODELS = [
|
|
1278
|
+
// OpenAI
|
|
1279
|
+
"gpt-5.4",
|
|
1280
|
+
"gpt-5.4-mini",
|
|
1281
|
+
"gpt-5.4-nano",
|
|
1282
|
+
"gpt-5.5",
|
|
1283
|
+
// Anthropic
|
|
1284
|
+
"claude-sonnet-4-6",
|
|
1285
|
+
"claude-haiku-4-5",
|
|
1286
|
+
"claude-opus-4-7",
|
|
1287
|
+
"claude-opus-4-8"
|
|
1288
|
+
];
|
|
1289
|
+
function isMozaikBundledModel(model) {
|
|
1290
|
+
return MOZAIK_BUNDLED_MODELS.includes(model);
|
|
1291
|
+
}
|
|
1292
|
+
function isMetamorphConfigKey(key) {
|
|
1293
|
+
return [
|
|
1294
|
+
"model",
|
|
1295
|
+
"reviewerModel",
|
|
1296
|
+
"concurrency",
|
|
1297
|
+
"inferenceTimeoutMs",
|
|
1298
|
+
"maxRetries",
|
|
1299
|
+
"maxIntegrationRounds",
|
|
1300
|
+
"disabledAgents"
|
|
1301
|
+
].includes(key);
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
// ../domain/src/entities/AgentTaxonomy.ts
|
|
1305
|
+
var CORE_AGENTS = [
|
|
1306
|
+
"coordinator",
|
|
1307
|
+
"mapper",
|
|
1308
|
+
"worker",
|
|
1309
|
+
"packagemanager",
|
|
1310
|
+
"integration"
|
|
1311
|
+
];
|
|
1312
|
+
var AUXILIARY_AGENTS = [
|
|
1313
|
+
"reporter",
|
|
1314
|
+
"reviewer",
|
|
1315
|
+
"accounting"
|
|
1316
|
+
];
|
|
1317
|
+
var ALL_SWARM_AGENTS = [
|
|
1318
|
+
...CORE_AGENTS,
|
|
1319
|
+
...AUXILIARY_AGENTS
|
|
1320
|
+
];
|
|
1321
|
+
function isCoreAgentId(id) {
|
|
1322
|
+
return CORE_AGENTS.includes(id.toLowerCase());
|
|
1323
|
+
}
|
|
1324
|
+
function isAuxiliaryAgentId(id) {
|
|
1325
|
+
return AUXILIARY_AGENTS.includes(id.toLowerCase());
|
|
1326
|
+
}
|
|
1327
|
+
function validateDisabledAgents(agentNames) {
|
|
1328
|
+
const normalized = [];
|
|
1329
|
+
for (const rawName of agentNames) {
|
|
1330
|
+
const clean = rawName.trim().toLowerCase();
|
|
1331
|
+
if (!clean) continue;
|
|
1332
|
+
if (isCoreAgentId(clean)) {
|
|
1333
|
+
return {
|
|
1334
|
+
valid: false,
|
|
1335
|
+
disabled: [],
|
|
1336
|
+
error: `Cannot disable core agent '${clean}'. Only auxiliary agents [${AUXILIARY_AGENTS.join(", ")}] can be disabled.`
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
if (!isAuxiliaryAgentId(clean)) {
|
|
1340
|
+
return {
|
|
1341
|
+
valid: false,
|
|
1342
|
+
disabled: [],
|
|
1343
|
+
error: `Unknown agent '${clean}'. Available auxiliary agents to disable: [${AUXILIARY_AGENTS.join(", ")}].`
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
if (!normalized.includes(clean)) {
|
|
1347
|
+
normalized.push(clean);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
return {
|
|
1351
|
+
valid: true,
|
|
1352
|
+
disabled: normalized
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
// ../domain/src/entities/TokenUsage.ts
|
|
1357
|
+
function createEmptyAgentRoleCost() {
|
|
1358
|
+
return {
|
|
1359
|
+
tokens: 0,
|
|
1360
|
+
promptTokens: 0,
|
|
1361
|
+
completionTokens: 0,
|
|
1362
|
+
costUsd: 0,
|
|
1363
|
+
executions: 0
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
function createEmptyMigrationCostSummary(runId = "", planId = "") {
|
|
1367
|
+
return {
|
|
1368
|
+
runId,
|
|
1369
|
+
planId,
|
|
1370
|
+
totalTokens: 0,
|
|
1371
|
+
promptTokens: 0,
|
|
1372
|
+
completionTokens: 0,
|
|
1373
|
+
totalCostUsd: 0,
|
|
1374
|
+
byAgentRole: {
|
|
1375
|
+
worker: createEmptyAgentRoleCost(),
|
|
1376
|
+
reviewer: createEmptyAgentRoleCost(),
|
|
1377
|
+
integration: createEmptyAgentRoleCost(),
|
|
1378
|
+
coordinator: createEmptyAgentRoleCost(),
|
|
1379
|
+
mapper: createEmptyAgentRoleCost()
|
|
1380
|
+
},
|
|
1381
|
+
byModel: {}
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
// ../domain/src/entities/PricingCatalog.ts
|
|
1386
|
+
var BUNDLED_MODEL_PRICING = {
|
|
1387
|
+
// OpenAI
|
|
1388
|
+
"gpt-5.4": {
|
|
1389
|
+
modelId: "gpt-5.4",
|
|
1390
|
+
inputPricePerMillion: 2.5,
|
|
1391
|
+
outputPricePerMillion: 10,
|
|
1392
|
+
currency: "USD"
|
|
1393
|
+
},
|
|
1394
|
+
"gpt-5.4-mini": {
|
|
1395
|
+
modelId: "gpt-5.4-mini",
|
|
1396
|
+
inputPricePerMillion: 0.15,
|
|
1397
|
+
outputPricePerMillion: 0.6,
|
|
1398
|
+
currency: "USD"
|
|
1399
|
+
},
|
|
1400
|
+
"gpt-5.4-nano": {
|
|
1401
|
+
modelId: "gpt-5.4-nano",
|
|
1402
|
+
inputPricePerMillion: 0.05,
|
|
1403
|
+
outputPricePerMillion: 0.2,
|
|
1404
|
+
currency: "USD"
|
|
1405
|
+
},
|
|
1406
|
+
"gpt-5.5": {
|
|
1407
|
+
modelId: "gpt-5.5",
|
|
1408
|
+
inputPricePerMillion: 3,
|
|
1409
|
+
outputPricePerMillion: 12,
|
|
1410
|
+
currency: "USD"
|
|
1411
|
+
},
|
|
1412
|
+
// Anthropic
|
|
1413
|
+
"claude-sonnet-4-6": {
|
|
1414
|
+
modelId: "claude-sonnet-4-6",
|
|
1415
|
+
inputPricePerMillion: 3,
|
|
1416
|
+
outputPricePerMillion: 15,
|
|
1417
|
+
currency: "USD"
|
|
1418
|
+
},
|
|
1419
|
+
"claude-haiku-4-5": {
|
|
1420
|
+
modelId: "claude-haiku-4-5",
|
|
1421
|
+
inputPricePerMillion: 0.8,
|
|
1422
|
+
outputPricePerMillion: 4,
|
|
1423
|
+
currency: "USD"
|
|
1424
|
+
},
|
|
1425
|
+
"claude-opus-4-7": {
|
|
1426
|
+
modelId: "claude-opus-4-7",
|
|
1427
|
+
inputPricePerMillion: 15,
|
|
1428
|
+
outputPricePerMillion: 75,
|
|
1429
|
+
currency: "USD"
|
|
1430
|
+
},
|
|
1431
|
+
"claude-opus-4-8": {
|
|
1432
|
+
modelId: "claude-opus-4-8",
|
|
1433
|
+
inputPricePerMillion: 15,
|
|
1434
|
+
outputPricePerMillion: 75,
|
|
1435
|
+
currency: "USD"
|
|
1436
|
+
}
|
|
1437
|
+
};
|
|
1438
|
+
function resolvePricingTier(modelId, customPricing) {
|
|
1439
|
+
if (customPricing && customPricing[modelId]) {
|
|
1440
|
+
return customPricing[modelId];
|
|
1441
|
+
}
|
|
1442
|
+
if (modelId in BUNDLED_MODEL_PRICING) {
|
|
1443
|
+
return BUNDLED_MODEL_PRICING[modelId];
|
|
1444
|
+
}
|
|
1445
|
+
return {
|
|
1446
|
+
modelId,
|
|
1447
|
+
inputPricePerMillion: 0,
|
|
1448
|
+
outputPricePerMillion: 0,
|
|
1449
|
+
currency: "USD"
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
function calculateTokenCost(modelId, promptTokens, completionTokens, customPricing) {
|
|
1453
|
+
const tier = resolvePricingTier(modelId, customPricing);
|
|
1454
|
+
const inputCost = promptTokens / 1e6 * tier.inputPricePerMillion;
|
|
1455
|
+
const outputCost = completionTokens / 1e6 * tier.outputPricePerMillion;
|
|
1456
|
+
const totalCost = inputCost + outputCost;
|
|
1457
|
+
return Math.round(totalCost * 1e6) / 1e6;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1221
1460
|
// ../domain/src/events/SemanticEvents.ts
|
|
1222
1461
|
var SemanticEventName = /* @__PURE__ */ ((SemanticEventName2) => {
|
|
1223
1462
|
SemanticEventName2["MIGRATION_STARTED"] = "migration.started";
|
|
@@ -1230,11 +1469,14 @@ var SemanticEventName = /* @__PURE__ */ ((SemanticEventName2) => {
|
|
|
1230
1469
|
SemanticEventName2["PHASE_PACKAGES_READY"] = "phase.packages_ready";
|
|
1231
1470
|
SemanticEventName2["PHASE_INTEGRATION_STARTED"] = "phase.integration_started";
|
|
1232
1471
|
SemanticEventName2["MIGRATION_COMPLETED"] = "migration.completed";
|
|
1472
|
+
SemanticEventName2["TOKENS_REPORTED"] = "tokens.reported";
|
|
1473
|
+
SemanticEventName2["TOKENS_CONSUMED"] = "tokens.consumed";
|
|
1233
1474
|
SemanticEventName2["SYSTEM_LOG"] = "system.log";
|
|
1234
1475
|
return SemanticEventName2;
|
|
1235
1476
|
})(SemanticEventName || {});
|
|
1236
1477
|
|
|
1237
1478
|
export {
|
|
1479
|
+
resolvePackageManagerCommands,
|
|
1238
1480
|
FRAMEWORK_PACKS,
|
|
1239
1481
|
rulesForFramework,
|
|
1240
1482
|
packForFramework,
|
|
@@ -1252,5 +1494,20 @@ export {
|
|
|
1252
1494
|
resolveMigrationCatalog,
|
|
1253
1495
|
MigrationCatalog,
|
|
1254
1496
|
findMigrationCatalogEntry,
|
|
1497
|
+
DEFAULT_METAMORPH_CONFIG,
|
|
1498
|
+
MOZAIK_BUNDLED_MODELS,
|
|
1499
|
+
isMozaikBundledModel,
|
|
1500
|
+
isMetamorphConfigKey,
|
|
1501
|
+
CORE_AGENTS,
|
|
1502
|
+
AUXILIARY_AGENTS,
|
|
1503
|
+
ALL_SWARM_AGENTS,
|
|
1504
|
+
isCoreAgentId,
|
|
1505
|
+
isAuxiliaryAgentId,
|
|
1506
|
+
validateDisabledAgents,
|
|
1507
|
+
createEmptyAgentRoleCost,
|
|
1508
|
+
createEmptyMigrationCostSummary,
|
|
1509
|
+
BUNDLED_MODEL_PRICING,
|
|
1510
|
+
resolvePricingTier,
|
|
1511
|
+
calculateTokenCost,
|
|
1255
1512
|
SemanticEventName
|
|
1256
1513
|
};
|