nexarch 0.9.4 → 0.9.5
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/commands/init-project.js +90 -17
- package/dist/index.js +1 -0
- package/package.json +1 -1
|
@@ -964,12 +964,34 @@ async function promptApplicationChoice(matches, allApps, suggested) {
|
|
|
964
964
|
export async function initProject(args) {
|
|
965
965
|
const asJson = parseFlag(args, "--json");
|
|
966
966
|
const dryRun = parseFlag(args, "--dry-run");
|
|
967
|
+
const profileEnabled = parseFlag(args, "--profile");
|
|
967
968
|
const commandStartedAt = Date.now();
|
|
969
|
+
const profile = {
|
|
970
|
+
enabled: profileEnabled,
|
|
971
|
+
startedAt: new Date(commandStartedAt).toISOString(),
|
|
972
|
+
events: [],
|
|
973
|
+
mcpCalls: [],
|
|
974
|
+
upsertBatches: {
|
|
975
|
+
entities: [],
|
|
976
|
+
relationships: [],
|
|
977
|
+
},
|
|
978
|
+
};
|
|
968
979
|
const logProgress = (phase, details) => {
|
|
969
|
-
const
|
|
980
|
+
const atMs = Date.now() - commandStartedAt;
|
|
981
|
+
if (profileEnabled)
|
|
982
|
+
profile.events.push({ phase, atMs, details });
|
|
983
|
+
const elapsed = formatMs(atMs);
|
|
970
984
|
const line = details ? `[init-project +${elapsed}] ${phase} — ${details}` : `[init-project +${elapsed}] ${phase}`;
|
|
971
985
|
process.stderr.write(`${line}\n`);
|
|
972
986
|
};
|
|
987
|
+
const callMcpProfiled = async (toolName, input, meta) => {
|
|
988
|
+
const startedAt = Date.now();
|
|
989
|
+
const result = await callMcpTool(toolName, input, mcpOpts);
|
|
990
|
+
if (profileEnabled) {
|
|
991
|
+
profile.mcpCalls.push({ tool: toolName, durationMs: Date.now() - startedAt, meta });
|
|
992
|
+
}
|
|
993
|
+
return result;
|
|
994
|
+
};
|
|
973
995
|
const dirArg = parseOptionValue(args, "--dir") ?? process.cwd();
|
|
974
996
|
const dir = resolvePath(dirArg);
|
|
975
997
|
const nameOverride = parseOptionValue(args, "--name");
|
|
@@ -1030,7 +1052,7 @@ export async function initProject(args) {
|
|
|
1030
1052
|
for (let i = 0; i < detectedNames.length; i += BATCH_SIZE) {
|
|
1031
1053
|
const batch = detectedNames.slice(i, i + BATCH_SIZE);
|
|
1032
1054
|
logProgress("resolve.batch", `${Math.floor(i / BATCH_SIZE) + 1}/${Math.ceil(detectedNames.length / BATCH_SIZE)} size=${batch.length}`);
|
|
1033
|
-
const raw = await
|
|
1055
|
+
const raw = await callMcpProfiled("nexarch_resolve_reference", { names: batch, companyId: creds.companyId }, { batchSize: batch.length });
|
|
1034
1056
|
const data = parseToolText(raw);
|
|
1035
1057
|
allResolveResults.push(...data.results);
|
|
1036
1058
|
}
|
|
@@ -1070,7 +1092,7 @@ export async function initProject(args) {
|
|
|
1070
1092
|
return;
|
|
1071
1093
|
}
|
|
1072
1094
|
logProgress("preflight.onboarding.check");
|
|
1073
|
-
const onboardingRaw = await
|
|
1095
|
+
const onboardingRaw = await callMcpProfiled("nexarch_get_company_onboarding", {});
|
|
1074
1096
|
const onboarding = parseToolText(onboardingRaw);
|
|
1075
1097
|
if (onboarding.isComplete !== true) {
|
|
1076
1098
|
const message = "Company onboarding is incomplete. Complete onboarding before running init-project.";
|
|
@@ -1083,7 +1105,7 @@ export async function initProject(args) {
|
|
|
1083
1105
|
}
|
|
1084
1106
|
// Policy bootstrap
|
|
1085
1107
|
logProgress("preflight.policies.check");
|
|
1086
|
-
const policiesRaw = await
|
|
1108
|
+
const policiesRaw = await callMcpProfiled("nexarch_get_applied_policies", {});
|
|
1087
1109
|
const policies = parseToolText(policiesRaw);
|
|
1088
1110
|
const policyBundleHash = policies.policyBundleHash ?? null;
|
|
1089
1111
|
if (!policyBundleHash) {
|
|
@@ -1108,7 +1130,7 @@ export async function initProject(args) {
|
|
|
1108
1130
|
console.log(`\nUsing --application-ref target: ${projectExternalKey}`);
|
|
1109
1131
|
}
|
|
1110
1132
|
else {
|
|
1111
|
-
const appsRaw = await
|
|
1133
|
+
const appsRaw = await callMcpProfiled("nexarch_list_entities", { entityTypeCode: "application", status: "active", limit: 500, companyId: creds.companyId }, { entityTypeCode: "application", limit: 500 });
|
|
1112
1134
|
const appsData = parseToolText(appsRaw);
|
|
1113
1135
|
const apps = (appsData.entities ?? []).filter((e) => (e.entityRef ?? e.externalKey));
|
|
1114
1136
|
if (apps.length > 0) {
|
|
@@ -1125,18 +1147,40 @@ export async function initProject(args) {
|
|
|
1125
1147
|
console.log(`\nAuto-mapped to existing application: ${suggested.name} (${projectExternalKey})`);
|
|
1126
1148
|
}
|
|
1127
1149
|
else if (!interactiveAllowed) {
|
|
1128
|
-
|
|
1129
|
-
|
|
1150
|
+
const message = "Application mapping requires explicit choice in non-interactive mode. Pass --application-ref <entityRef> or --create-application (or run interactively).";
|
|
1151
|
+
const existingApplications = apps.slice(0, 25).map((a) => ({
|
|
1152
|
+
entityRef: a.entityRef ?? a.externalKey,
|
|
1153
|
+
name: a.name,
|
|
1154
|
+
entityTypeCode: a.entityTypeCode,
|
|
1155
|
+
}));
|
|
1156
|
+
if (asJson) {
|
|
1157
|
+
process.stdout.write(`${JSON.stringify({
|
|
1158
|
+
ok: true,
|
|
1159
|
+
status: "input_required",
|
|
1160
|
+
code: "APPLICATION_MAPPING_REQUIRED",
|
|
1161
|
+
message,
|
|
1162
|
+
suggested: suggested ?? null,
|
|
1163
|
+
candidates: matches.slice(0, 10),
|
|
1164
|
+
existingApplications,
|
|
1165
|
+
requiredInput: {
|
|
1166
|
+
applicationRefOption: "--application-ref <entityRef>",
|
|
1167
|
+
createOption: "--create-application",
|
|
1168
|
+
},
|
|
1169
|
+
}, null, 2)}\n`);
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
console.log("\nInput required before continuing:");
|
|
1173
|
+
console.log(` ${message}`);
|
|
1174
|
+
if (suggested) {
|
|
1175
|
+
console.log(` Suggested: ${suggested.name} (${suggested.entityRef}) score=${suggested.score.toFixed(2)}`);
|
|
1130
1176
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
process.exitCode = 1;
|
|
1136
|
-
return;
|
|
1177
|
+
if (existingApplications.length > 0) {
|
|
1178
|
+
console.log(" Existing applications:");
|
|
1179
|
+
for (const app of existingApplications) {
|
|
1180
|
+
console.log(` - ${app.name} (${app.entityRef})`);
|
|
1137
1181
|
}
|
|
1138
|
-
throw new Error(message);
|
|
1139
1182
|
}
|
|
1183
|
+
return;
|
|
1140
1184
|
}
|
|
1141
1185
|
else {
|
|
1142
1186
|
const chosen = await promptApplicationChoice(matches, apps, highConfidence ? suggested : null);
|
|
@@ -1299,7 +1343,7 @@ export async function initProject(args) {
|
|
|
1299
1343
|
// Company org is accountable_for the top-level project entity
|
|
1300
1344
|
let orgExternalKey = `organisation:${normalizeToken(creds.companyId)}`;
|
|
1301
1345
|
try {
|
|
1302
|
-
const orgRaw = await
|
|
1346
|
+
const orgRaw = await callMcpProfiled("nexarch_list_entities", { entityTypeCode: "organisation", status: "active", limit: 1, companyId: creds.companyId }, { entityTypeCode: "organisation", limit: 1 });
|
|
1303
1347
|
const orgData = parseToolText(orgRaw);
|
|
1304
1348
|
const org = (orgData.entities ?? [])[0];
|
|
1305
1349
|
if (org?.entityRef || org?.externalKey)
|
|
@@ -1331,8 +1375,18 @@ export async function initProject(args) {
|
|
|
1331
1375
|
for (let i = 0; i < entityChunks.length; i += 1) {
|
|
1332
1376
|
const chunk = entityChunks[i];
|
|
1333
1377
|
logProgress("upsert.entities.batch.start", `${i + 1}/${entityChunks.length} size=${chunk.length}`);
|
|
1334
|
-
const
|
|
1378
|
+
const entityBatchStartedAt = Date.now();
|
|
1379
|
+
const entitiesRaw = await callMcpProfiled("nexarch_upsert_entities", { entities: chunk, agentContext, policyContext }, { batchIndex: i + 1, totalBatches: entityChunks.length, batchSize: chunk.length });
|
|
1335
1380
|
const chunkResult = parseToolText(entitiesRaw);
|
|
1381
|
+
if (profileEnabled) {
|
|
1382
|
+
profile.upsertBatches.entities.push({
|
|
1383
|
+
index: i + 1,
|
|
1384
|
+
size: chunk.length,
|
|
1385
|
+
durationMs: Date.now() - entityBatchStartedAt,
|
|
1386
|
+
succeeded: Number(chunkResult.summary?.succeeded ?? 0),
|
|
1387
|
+
failed: Number(chunkResult.summary?.failed ?? 0),
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1336
1390
|
entitiesResult.summary.requested = Number(entitiesResult.summary.requested ?? 0) + Number(chunkResult.summary?.requested ?? chunk.length);
|
|
1337
1391
|
entitiesResult.summary.succeeded = Number(entitiesResult.summary.succeeded ?? 0) + Number(chunkResult.summary?.succeeded ?? 0);
|
|
1338
1392
|
entitiesResult.summary.failed = Number(entitiesResult.summary.failed ?? 0) + Number(chunkResult.summary?.failed ?? 0);
|
|
@@ -1350,8 +1404,18 @@ export async function initProject(args) {
|
|
|
1350
1404
|
for (let i = 0; i < relationshipChunks.length; i += 1) {
|
|
1351
1405
|
const chunk = relationshipChunks[i];
|
|
1352
1406
|
logProgress("upsert.relationships.batch.start", `${i + 1}/${relationshipChunks.length} size=${chunk.length}`);
|
|
1353
|
-
const
|
|
1407
|
+
const relBatchStartedAt = Date.now();
|
|
1408
|
+
const relsRaw = await callMcpProfiled("nexarch_upsert_relationships", { relationships: chunk, agentContext, policyContext }, { batchIndex: i + 1, totalBatches: relationshipChunks.length, batchSize: chunk.length });
|
|
1354
1409
|
const chunkResult = parseToolText(relsRaw);
|
|
1410
|
+
if (profileEnabled) {
|
|
1411
|
+
profile.upsertBatches.relationships.push({
|
|
1412
|
+
index: i + 1,
|
|
1413
|
+
size: chunk.length,
|
|
1414
|
+
durationMs: Date.now() - relBatchStartedAt,
|
|
1415
|
+
succeeded: Number(chunkResult.summary?.succeeded ?? 0),
|
|
1416
|
+
failed: Number(chunkResult.summary?.failed ?? 0),
|
|
1417
|
+
});
|
|
1418
|
+
}
|
|
1355
1419
|
relsResult.summary.requested = Number(relsResult.summary.requested ?? 0) + Number(chunkResult.summary?.requested ?? chunk.length);
|
|
1356
1420
|
relsResult.summary.succeeded = Number(relsResult.summary.succeeded ?? 0) + Number(chunkResult.summary?.succeeded ?? 0);
|
|
1357
1421
|
relsResult.summary.failed = Number(relsResult.summary.failed ?? 0) + Number(chunkResult.summary?.failed ?? 0);
|
|
@@ -1609,6 +1673,15 @@ ${subPkgSection}${adrSection}${gapCheckSection}`;
|
|
|
1609
1673
|
entityErrors: entitiesResult.errors ?? [],
|
|
1610
1674
|
relationshipErrors: relsResult?.errors ?? [],
|
|
1611
1675
|
enrichmentTask,
|
|
1676
|
+
profile: profileEnabled
|
|
1677
|
+
? {
|
|
1678
|
+
startedAt: profile.startedAt,
|
|
1679
|
+
totalDurationMs: Date.now() - commandStartedAt,
|
|
1680
|
+
events: profile.events,
|
|
1681
|
+
mcpCalls: profile.mcpCalls,
|
|
1682
|
+
upsertBatches: profile.upsertBatches,
|
|
1683
|
+
}
|
|
1684
|
+
: undefined,
|
|
1612
1685
|
};
|
|
1613
1686
|
if (asJson) {
|
|
1614
1687
|
logProgress("complete", `ok=${output.ok}`);
|
package/dist/index.js
CHANGED
|
@@ -95,6 +95,7 @@ Usage:
|
|
|
95
95
|
--auto-map-application auto-map only when high confidence
|
|
96
96
|
--non-interactive fail on ambiguous mapping
|
|
97
97
|
--batch-size <n> upsert batch size (default: 10)
|
|
98
|
+
--profile include timing/profile data in JSON output
|
|
98
99
|
--dry-run preview without writing
|
|
99
100
|
--json
|
|
100
101
|
nexarch update-entity
|