@primeuicom/mcp 0.1.16 → 0.1.18
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/service.js +356 -55
- package/dist/service.js.map +1 -1
- package/package.json +1 -1
package/dist/service.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/service.ts
|
|
4
|
-
import
|
|
4
|
+
import path8 from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
5
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
7
|
|
|
7
8
|
// src/lib/project-link-config.ts
|
|
@@ -85,6 +86,9 @@ function toUniqueResolvedDirs(directories) {
|
|
|
85
86
|
}
|
|
86
87
|
return [...resolved];
|
|
87
88
|
}
|
|
89
|
+
function buildPrimeUiProjectSearchRoots(options) {
|
|
90
|
+
return toUniqueResolvedDirs([options.cwd, ...options.fallbackCwds ?? []]);
|
|
91
|
+
}
|
|
88
92
|
async function resolvePrimeUiProjectConfig(options) {
|
|
89
93
|
const envProjectRoot = options.projectRootFromEnv?.trim();
|
|
90
94
|
let projectConfigPath;
|
|
@@ -94,10 +98,10 @@ async function resolvePrimeUiProjectConfig(options) {
|
|
|
94
98
|
PRIMEUI_PROJECT_CONFIG_RELATIVE_PATH
|
|
95
99
|
);
|
|
96
100
|
} else {
|
|
97
|
-
const searchRoots =
|
|
98
|
-
options.cwd,
|
|
99
|
-
|
|
100
|
-
|
|
101
|
+
const searchRoots = buildPrimeUiProjectSearchRoots({
|
|
102
|
+
cwd: options.cwd,
|
|
103
|
+
fallbackCwds: options.fallbackCwds
|
|
104
|
+
});
|
|
101
105
|
for (const searchRoot of searchRoots) {
|
|
102
106
|
const foundProjectConfigPath = await findPrimeUiProjectConfigPath(searchRoot);
|
|
103
107
|
if (foundProjectConfigPath) {
|
|
@@ -264,6 +268,33 @@ var inspectPageReportRowSchema = z2.object({
|
|
|
264
268
|
title: z2.string().nullable().describe("Title extracted by MCP from component props."),
|
|
265
269
|
description: z2.string().nullable().describe("Description extracted by MCP from component props.")
|
|
266
270
|
});
|
|
271
|
+
var healthOptionSchema = z2.object({
|
|
272
|
+
key: z2.string().describe("Environment variable or diagnostic option name."),
|
|
273
|
+
description: z2.string().describe("Human-readable explanation of what this option controls."),
|
|
274
|
+
currentValue: z2.string().nullable().describe(
|
|
275
|
+
"Current effective value for this option. Null when not set or intentionally hidden."
|
|
276
|
+
)
|
|
277
|
+
});
|
|
278
|
+
var healthRuntimeSchema = z2.object({
|
|
279
|
+
cwd: z2.string().describe("process.cwd() observed by the MCP server process."),
|
|
280
|
+
initCwd: z2.string().nullable().describe("INIT_CWD environment value, if provided by the client runtime."),
|
|
281
|
+
pwd: z2.string().nullable().describe("PWD environment value, if provided by the client runtime."),
|
|
282
|
+
searchRoots: z2.array(z2.string()).describe(
|
|
283
|
+
"Resolved directories used when searching for .primeui/project.json."
|
|
284
|
+
),
|
|
285
|
+
rootsListFallbackCwds: z2.array(z2.string()).describe(
|
|
286
|
+
"File-system roots derived from MCP roots/list and used as search fallbacks."
|
|
287
|
+
)
|
|
288
|
+
});
|
|
289
|
+
var healthConfigSchema = z2.object({
|
|
290
|
+
found: z2.boolean().describe("True when .primeui/project.json was found and parsed successfully."),
|
|
291
|
+
projectRoot: z2.string().nullable().describe("Resolved PrimeUI project root directory (parent of .primeui)."),
|
|
292
|
+
projectConfigPath: z2.string().nullable().describe("Absolute path to .primeui/project.json when found."),
|
|
293
|
+
targetProjectPath: z2.string().nullable().describe("targetProjectPath value loaded from project config."),
|
|
294
|
+
targetProjectRoot: z2.string().nullable().describe("Absolute target project root derived from targetProjectPath."),
|
|
295
|
+
source: z2.enum(["env.PRIMEUI_PROJECT_ROOT", "search"]).describe("How project config lookup was resolved."),
|
|
296
|
+
error: z2.string().nullable().describe("Human-readable resolution error when config is not available.")
|
|
297
|
+
});
|
|
267
298
|
var TRIGGER_PHRASES = [
|
|
268
299
|
"import page from PrimeUI",
|
|
269
300
|
"add page from PrimeUI",
|
|
@@ -294,6 +325,7 @@ ${WORKFLOW_SUMMARY}
|
|
|
294
325
|
|
|
295
326
|
CRITICAL RULES FOR PAGE IMPORT:
|
|
296
327
|
- Import is PAGE-BY-PAGE only. Never import the entire project at once.
|
|
328
|
+
- Use health_check to inspect runtime path/config resolution when setup issues occur.
|
|
297
329
|
- Always call clear_temp at the very beginning of a new import flow to avoid stale export state.
|
|
298
330
|
- After get_project_info, always compare PrimeUI pages against the user's existing local project pages before proposing imports.
|
|
299
331
|
- Reconciliation report MUST include: page presence match, slug/pagePath/componentsPath path match, and proposed action per page.
|
|
@@ -324,6 +356,34 @@ CRITICAL RULES FOR PAGE IMPORT:
|
|
|
324
356
|
- If integration pattern is obvious, apply it and explicitly mark it in the report with "\u26A0 integration update".
|
|
325
357
|
- If integration pattern is unclear, ask the user before editing integration files.
|
|
326
358
|
`.trim();
|
|
359
|
+
var toolHealthCheck = {
|
|
360
|
+
title: "PrimeUI Health Check",
|
|
361
|
+
description: `Diagnostic tool for MCP runtime setup. This tool MUST NOT fail: it always returns structured diagnostics even when project config is missing.
|
|
362
|
+
|
|
363
|
+
WHEN TO USE:
|
|
364
|
+
- Use this first when PrimeUI MCP tools fail due to project path/config issues.
|
|
365
|
+
- Use this to verify cwd/env/roots behavior in different MCP clients (Codex, Claude Code, Inspector).
|
|
366
|
+
|
|
367
|
+
RETURNS:
|
|
368
|
+
- Runtime paths (cwd, INIT_CWD, PWD, search roots),
|
|
369
|
+
- roots/list fallback paths from client (if available),
|
|
370
|
+
- Whether .primeui/project.json was found and parsed,
|
|
371
|
+
- Effective resolved project root/config path (when available),
|
|
372
|
+
- Current option values and fallback guidance.`,
|
|
373
|
+
inputSchema: {},
|
|
374
|
+
outputSchema: {
|
|
375
|
+
status: z2.enum(["ok", "degraded"]).describe("Overall setup health. 'degraded' means config is unresolved."),
|
|
376
|
+
runtime: healthRuntimeSchema,
|
|
377
|
+
config: healthConfigSchema,
|
|
378
|
+
options: z2.array(healthOptionSchema).describe("Environment and runtime options relevant for configuration.")
|
|
379
|
+
},
|
|
380
|
+
annotations: {
|
|
381
|
+
readOnlyHint: true,
|
|
382
|
+
destructiveHint: false,
|
|
383
|
+
idempotentHint: true,
|
|
384
|
+
openWorldHint: false
|
|
385
|
+
}
|
|
386
|
+
};
|
|
327
387
|
var toolGetProjectInfo = {
|
|
328
388
|
title: "PrimeUI Project Info",
|
|
329
389
|
description: `ENTRY POINT for all PrimeUI import operations. Always start here. Returns project metadata and the full list of available pages.
|
|
@@ -651,6 +711,38 @@ function createPrimeUiMcpServer(source) {
|
|
|
651
711
|
instructions: initialInstructions
|
|
652
712
|
}
|
|
653
713
|
);
|
|
714
|
+
server.registerTool(
|
|
715
|
+
"health_check",
|
|
716
|
+
toolHealthCheck,
|
|
717
|
+
async () => {
|
|
718
|
+
try {
|
|
719
|
+
const health = await source.healthCheck();
|
|
720
|
+
return okResult("health_check", health);
|
|
721
|
+
} catch (error) {
|
|
722
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
723
|
+
return okResult("health_check", {
|
|
724
|
+
status: "degraded",
|
|
725
|
+
runtime: {
|
|
726
|
+
cwd: process.cwd(),
|
|
727
|
+
initCwd: process.env.INIT_CWD?.trim() || null,
|
|
728
|
+
pwd: process.env.PWD?.trim() || null,
|
|
729
|
+
searchRoots: [process.cwd()],
|
|
730
|
+
rootsListFallbackCwds: []
|
|
731
|
+
},
|
|
732
|
+
config: {
|
|
733
|
+
found: false,
|
|
734
|
+
projectRoot: null,
|
|
735
|
+
projectConfigPath: null,
|
|
736
|
+
targetProjectPath: null,
|
|
737
|
+
targetProjectRoot: null,
|
|
738
|
+
source: "search",
|
|
739
|
+
error: `health_check fallback: ${message}`
|
|
740
|
+
},
|
|
741
|
+
options: []
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
);
|
|
654
746
|
server.registerTool(
|
|
655
747
|
"get_project_info",
|
|
656
748
|
toolGetProjectInfo,
|
|
@@ -741,13 +833,118 @@ function createPrimeUiMcpServer(source) {
|
|
|
741
833
|
return server;
|
|
742
834
|
}
|
|
743
835
|
|
|
836
|
+
// src/services/health-check-service.ts
|
|
837
|
+
import path2 from "path";
|
|
838
|
+
function normalizeOptional(value) {
|
|
839
|
+
const trimmed = value?.trim();
|
|
840
|
+
return trimmed ? trimmed : null;
|
|
841
|
+
}
|
|
842
|
+
function buildHealthOptions(input) {
|
|
843
|
+
return [
|
|
844
|
+
{
|
|
845
|
+
key: "PRIMEUI_PROJECT_ROOT",
|
|
846
|
+
description: "Optional absolute root containing .primeui/project.json. Use as explicit override.",
|
|
847
|
+
currentValue: input.projectRootFromEnv
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
key: "PRIMEUI_API_BASE_URL",
|
|
851
|
+
description: "Optional PrimeUI API base URL override.",
|
|
852
|
+
currentValue: input.apiBaseUrlFromEnv
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
key: "PRIMEUI_API_KEY",
|
|
856
|
+
description: "Optional API key override. If missing, MCP uses apiKey from .primeui/project.json.",
|
|
857
|
+
currentValue: input.apiKeyFromEnv ? "set" : null
|
|
858
|
+
},
|
|
859
|
+
{
|
|
860
|
+
key: "Fallback search",
|
|
861
|
+
description: "When PRIMEUI_PROJECT_ROOT is not set, MCP searches from roots/list file roots, INIT_CWD, PWD, then process.cwd().",
|
|
862
|
+
currentValue: null
|
|
863
|
+
}
|
|
864
|
+
];
|
|
865
|
+
}
|
|
866
|
+
async function runPrimeUiHealthCheck(input) {
|
|
867
|
+
const projectRootFromEnv = normalizeOptional(input.projectRootFromEnv);
|
|
868
|
+
const apiBaseUrlFromEnv = normalizeOptional(input.apiBaseUrlFromEnv);
|
|
869
|
+
const apiKeyFromEnv = normalizeOptional(input.apiKeyFromEnv);
|
|
870
|
+
const initCwd = normalizeOptional(input.initCwd);
|
|
871
|
+
const pwd = normalizeOptional(input.pwd);
|
|
872
|
+
const rootsListFallbackCwds = (input.rootsListFallbackCwds ?? []).map((value) => value.trim()).filter(Boolean).map((value) => path2.resolve(value));
|
|
873
|
+
const fallbackCwds = (input.fallbackCwds ?? []).map((value) => value.trim()).filter(Boolean);
|
|
874
|
+
const searchRoots = buildPrimeUiProjectSearchRoots({
|
|
875
|
+
cwd: input.cwd,
|
|
876
|
+
fallbackCwds
|
|
877
|
+
});
|
|
878
|
+
try {
|
|
879
|
+
const resolvedProjectConfig = await resolvePrimeUiProjectConfig({
|
|
880
|
+
cwd: input.cwd,
|
|
881
|
+
projectRootFromEnv: projectRootFromEnv ?? void 0,
|
|
882
|
+
fallbackCwds
|
|
883
|
+
});
|
|
884
|
+
const targetProjectPath = resolvedProjectConfig.projectConfig.targetProjectPath;
|
|
885
|
+
return {
|
|
886
|
+
status: "ok",
|
|
887
|
+
runtime: {
|
|
888
|
+
cwd: path2.resolve(input.cwd),
|
|
889
|
+
initCwd,
|
|
890
|
+
pwd,
|
|
891
|
+
searchRoots,
|
|
892
|
+
rootsListFallbackCwds
|
|
893
|
+
},
|
|
894
|
+
config: {
|
|
895
|
+
found: true,
|
|
896
|
+
projectRoot: resolvedProjectConfig.projectRoot,
|
|
897
|
+
projectConfigPath: resolvedProjectConfig.projectConfigPath,
|
|
898
|
+
targetProjectPath,
|
|
899
|
+
targetProjectRoot: path2.resolve(
|
|
900
|
+
resolvedProjectConfig.projectRoot,
|
|
901
|
+
targetProjectPath
|
|
902
|
+
),
|
|
903
|
+
source: projectRootFromEnv ? "env.PRIMEUI_PROJECT_ROOT" : "search",
|
|
904
|
+
error: null
|
|
905
|
+
},
|
|
906
|
+
options: buildHealthOptions({
|
|
907
|
+
projectRootFromEnv,
|
|
908
|
+
apiBaseUrlFromEnv,
|
|
909
|
+
apiKeyFromEnv
|
|
910
|
+
})
|
|
911
|
+
};
|
|
912
|
+
} catch (error) {
|
|
913
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
914
|
+
return {
|
|
915
|
+
status: "degraded",
|
|
916
|
+
runtime: {
|
|
917
|
+
cwd: path2.resolve(input.cwd),
|
|
918
|
+
initCwd,
|
|
919
|
+
pwd,
|
|
920
|
+
searchRoots,
|
|
921
|
+
rootsListFallbackCwds
|
|
922
|
+
},
|
|
923
|
+
config: {
|
|
924
|
+
found: false,
|
|
925
|
+
projectRoot: null,
|
|
926
|
+
projectConfigPath: null,
|
|
927
|
+
targetProjectPath: null,
|
|
928
|
+
targetProjectRoot: null,
|
|
929
|
+
source: projectRootFromEnv ? "env.PRIMEUI_PROJECT_ROOT" : "search",
|
|
930
|
+
error: message
|
|
931
|
+
},
|
|
932
|
+
options: buildHealthOptions({
|
|
933
|
+
projectRootFromEnv,
|
|
934
|
+
apiBaseUrlFromEnv,
|
|
935
|
+
apiKeyFromEnv
|
|
936
|
+
})
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
744
941
|
// src/services/project-sync-service.ts
|
|
745
|
-
import
|
|
942
|
+
import path6 from "path";
|
|
746
943
|
import { readFile as readFile4 } from "fs/promises";
|
|
747
944
|
|
|
748
945
|
// src/lib/fs.ts
|
|
749
946
|
import { mkdir, rm, writeFile } from "fs/promises";
|
|
750
|
-
import
|
|
947
|
+
import path3 from "path";
|
|
751
948
|
import extractZipArchive from "extract-zip";
|
|
752
949
|
async function ensureDir(dirPath) {
|
|
753
950
|
await mkdir(dirPath, { recursive: true });
|
|
@@ -757,7 +954,7 @@ async function resetDir(dirPath) {
|
|
|
757
954
|
await mkdir(dirPath, { recursive: true });
|
|
758
955
|
}
|
|
759
956
|
async function writeUtf8(filePath, content) {
|
|
760
|
-
await ensureDir(
|
|
957
|
+
await ensureDir(path3.dirname(filePath));
|
|
761
958
|
await writeFile(filePath, content, "utf-8");
|
|
762
959
|
}
|
|
763
960
|
async function extractZip(zipPath, targetDir) {
|
|
@@ -772,12 +969,12 @@ async function extractZip(zipPath, targetDir) {
|
|
|
772
969
|
|
|
773
970
|
// src/services/page-copy-service.ts
|
|
774
971
|
import { readFile as readFile3, readdir, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
775
|
-
import
|
|
972
|
+
import path5 from "path";
|
|
776
973
|
|
|
777
974
|
// src/lib/import-graph.ts
|
|
778
975
|
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
779
976
|
import { builtinModules } from "module";
|
|
780
|
-
import
|
|
977
|
+
import path4 from "path";
|
|
781
978
|
var INTERNAL_EXTENSIONS = [
|
|
782
979
|
".ts",
|
|
783
980
|
".tsx",
|
|
@@ -853,7 +1050,7 @@ async function pathExists(filePath) {
|
|
|
853
1050
|
}
|
|
854
1051
|
}
|
|
855
1052
|
async function resolveFileCandidate(candidateBase) {
|
|
856
|
-
const ext =
|
|
1053
|
+
const ext = path4.extname(candidateBase);
|
|
857
1054
|
if (ext) {
|
|
858
1055
|
if (await pathExists(candidateBase)) {
|
|
859
1056
|
return candidateBase;
|
|
@@ -873,7 +1070,7 @@ async function resolveFileCandidate(candidateBase) {
|
|
|
873
1070
|
}
|
|
874
1071
|
if (stats.isDirectory()) {
|
|
875
1072
|
for (const extension of INTERNAL_EXTENSIONS) {
|
|
876
|
-
const indexCandidate =
|
|
1073
|
+
const indexCandidate = path4.join(candidateBase, `index${extension}`);
|
|
877
1074
|
if (await pathExists(indexCandidate)) {
|
|
878
1075
|
return indexCandidate;
|
|
879
1076
|
}
|
|
@@ -895,13 +1092,13 @@ async function resolveImportToFile(projectRoot, importerFilePath, specifier) {
|
|
|
895
1092
|
}
|
|
896
1093
|
let candidateBase = null;
|
|
897
1094
|
if (specifier.startsWith("@/")) {
|
|
898
|
-
candidateBase =
|
|
1095
|
+
candidateBase = path4.join(projectRoot, "src", specifier.slice(2));
|
|
899
1096
|
} else if (specifier.startsWith("@root/")) {
|
|
900
|
-
candidateBase =
|
|
1097
|
+
candidateBase = path4.join(projectRoot, specifier.slice(6));
|
|
901
1098
|
} else if (specifier.startsWith(".")) {
|
|
902
|
-
candidateBase =
|
|
1099
|
+
candidateBase = path4.resolve(path4.dirname(importerFilePath), specifier);
|
|
903
1100
|
} else if (specifier.startsWith("/")) {
|
|
904
|
-
candidateBase =
|
|
1101
|
+
candidateBase = path4.join(projectRoot, specifier.slice(1));
|
|
905
1102
|
} else {
|
|
906
1103
|
return { kind: "unknown" };
|
|
907
1104
|
}
|
|
@@ -915,12 +1112,12 @@ async function resolveImportToFile(projectRoot, importerFilePath, specifier) {
|
|
|
915
1112
|
};
|
|
916
1113
|
}
|
|
917
1114
|
function shouldParseFile(filePath) {
|
|
918
|
-
return PARSEABLE_EXTENSIONS.has(
|
|
1115
|
+
return PARSEABLE_EXTENSIONS.has(path4.extname(filePath).toLowerCase());
|
|
919
1116
|
}
|
|
920
1117
|
async function buildImportGraph(input) {
|
|
921
1118
|
const shouldFollowInternalImports = input.followInternalImports ?? true;
|
|
922
|
-
const projectRoot =
|
|
923
|
-
const queue = input.entryFiles.map((filePath) =>
|
|
1119
|
+
const projectRoot = path4.resolve(input.projectRoot);
|
|
1120
|
+
const queue = input.entryFiles.map((filePath) => path4.resolve(filePath));
|
|
924
1121
|
const visited = /* @__PURE__ */ new Set();
|
|
925
1122
|
const internalFiles = /* @__PURE__ */ new Set();
|
|
926
1123
|
const externalPackages = /* @__PURE__ */ new Set();
|
|
@@ -1173,10 +1370,10 @@ function normalizeSlug2(slug) {
|
|
|
1173
1370
|
return withLeadingSlash.replace(/\/+$/, "") || "/";
|
|
1174
1371
|
}
|
|
1175
1372
|
function toPosixPath(value) {
|
|
1176
|
-
return value.split(
|
|
1373
|
+
return value.split(path5.sep).join("/");
|
|
1177
1374
|
}
|
|
1178
1375
|
function toProjectRelative(rootPath, absolutePath) {
|
|
1179
|
-
return toPosixPath(
|
|
1376
|
+
return toPosixPath(path5.relative(rootPath, absolutePath));
|
|
1180
1377
|
}
|
|
1181
1378
|
function escapeRegExp(value) {
|
|
1182
1379
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -1233,7 +1430,7 @@ function buildPlannedSourceBuffer(sourceBuffer, sourceFilePath, importRewritePla
|
|
|
1233
1430
|
if (!importRewritePlan) {
|
|
1234
1431
|
return sourceBuffer;
|
|
1235
1432
|
}
|
|
1236
|
-
const extension =
|
|
1433
|
+
const extension = path5.extname(sourceFilePath).toLowerCase();
|
|
1237
1434
|
if (!REWRITABLE_IMPORT_EXTENSIONS.has(extension)) {
|
|
1238
1435
|
return sourceBuffer;
|
|
1239
1436
|
}
|
|
@@ -1335,9 +1532,9 @@ async function resolveManifestCandidateFiles(input) {
|
|
|
1335
1532
|
);
|
|
1336
1533
|
}
|
|
1337
1534
|
const normalizedRelative = toPosixPath(trimmed).replace(/^\.\/+/, "");
|
|
1338
|
-
const absolutePath =
|
|
1339
|
-
const relative =
|
|
1340
|
-
if (relative.startsWith("..") ||
|
|
1535
|
+
const absolutePath = path5.resolve(input.exportPath, normalizedRelative);
|
|
1536
|
+
const relative = path5.relative(input.exportPath, absolutePath);
|
|
1537
|
+
if (relative.startsWith("..") || path5.isAbsolute(relative)) {
|
|
1341
1538
|
throw new Error(
|
|
1342
1539
|
`Export manifest for page "${input.pageSlug}" contains path outside export root: ${trimmed}`
|
|
1343
1540
|
);
|
|
@@ -1368,12 +1565,12 @@ async function resolveSingleExportDirectory(exportsRoot) {
|
|
|
1368
1565
|
const exportId = exportDirectories[0] ?? "";
|
|
1369
1566
|
return {
|
|
1370
1567
|
exportId,
|
|
1371
|
-
exportPath:
|
|
1568
|
+
exportPath: path5.join(exportsRoot, exportId)
|
|
1372
1569
|
};
|
|
1373
1570
|
}
|
|
1374
1571
|
function ensureSafeTargetPath(projectRoot, targetPath) {
|
|
1375
|
-
const relative =
|
|
1376
|
-
if (relative.startsWith("..") ||
|
|
1572
|
+
const relative = path5.relative(projectRoot, targetPath);
|
|
1573
|
+
if (relative.startsWith("..") || path5.isAbsolute(relative)) {
|
|
1377
1574
|
throw new Error(
|
|
1378
1575
|
`Refusing to write outside project root. Computed target: ${targetPath}`
|
|
1379
1576
|
);
|
|
@@ -1431,19 +1628,19 @@ function resolveTargetFilePath(input) {
|
|
|
1431
1628
|
if (sourceFilePath === sourcePagePath) {
|
|
1432
1629
|
return targetPagePath;
|
|
1433
1630
|
}
|
|
1434
|
-
const componentsPrefix = `${sourceComponentsPath}${
|
|
1631
|
+
const componentsPrefix = `${sourceComponentsPath}${path5.sep}`;
|
|
1435
1632
|
if (sourceFilePath === sourceComponentsPath || sourceFilePath.startsWith(componentsPrefix)) {
|
|
1436
|
-
const relativeToComponents =
|
|
1633
|
+
const relativeToComponents = path5.relative(
|
|
1437
1634
|
sourceComponentsPath,
|
|
1438
1635
|
sourceFilePath
|
|
1439
1636
|
);
|
|
1440
|
-
return
|
|
1637
|
+
return path5.join(targetComponentsPath, relativeToComponents);
|
|
1441
1638
|
}
|
|
1442
|
-
const relativeToExport =
|
|
1443
|
-
if (relativeToExport.startsWith("..") ||
|
|
1639
|
+
const relativeToExport = path5.relative(exportRoot, sourceFilePath);
|
|
1640
|
+
if (relativeToExport.startsWith("..") || path5.isAbsolute(relativeToExport)) {
|
|
1444
1641
|
throw new Error(`Source file is outside export root: ${sourceFilePath}`);
|
|
1445
1642
|
}
|
|
1446
|
-
return
|
|
1643
|
+
return path5.join(projectRoot, relativeToExport);
|
|
1447
1644
|
}
|
|
1448
1645
|
async function copyPageFromExport(input) {
|
|
1449
1646
|
const normalizedOriginSlug = normalizeSlug2(input.originPageSlug);
|
|
@@ -1454,7 +1651,7 @@ async function copyPageFromExport(input) {
|
|
|
1454
1651
|
const { exportId, exportPath } = await resolveSingleExportDirectory(
|
|
1455
1652
|
input.exportsRoot
|
|
1456
1653
|
);
|
|
1457
|
-
const manifestPath =
|
|
1654
|
+
const manifestPath = path5.join(
|
|
1458
1655
|
input.exportsRoot,
|
|
1459
1656
|
`${exportId}.manifest.json`
|
|
1460
1657
|
);
|
|
@@ -1477,8 +1674,8 @@ async function copyPageFromExport(input) {
|
|
|
1477
1674
|
`Page not found in manifest for slug: ${normalizedOriginSlug}`
|
|
1478
1675
|
);
|
|
1479
1676
|
}
|
|
1480
|
-
const sourcePagePath =
|
|
1481
|
-
const sourceComponentsPath =
|
|
1677
|
+
const sourcePagePath = path5.join(exportPath, page.pagePath);
|
|
1678
|
+
const sourceComponentsPath = path5.join(exportPath, page.componentsPath);
|
|
1482
1679
|
const sourcePageStats = await stat3(sourcePagePath).catch(() => null);
|
|
1483
1680
|
if (!sourcePageStats?.isFile()) {
|
|
1484
1681
|
throw new Error(`Source page file not found: ${sourcePagePath}`);
|
|
@@ -1498,8 +1695,8 @@ async function copyPageFromExport(input) {
|
|
|
1498
1695
|
pageType: page.pageType,
|
|
1499
1696
|
slug: normalizedActualSlug
|
|
1500
1697
|
});
|
|
1501
|
-
const targetPagePath =
|
|
1502
|
-
const targetComponentsPath =
|
|
1698
|
+
const targetPagePath = path5.join(input.projectRoot, targetPaths.pagePath);
|
|
1699
|
+
const targetComponentsPath = path5.join(
|
|
1503
1700
|
input.projectRoot,
|
|
1504
1701
|
targetPaths.componentsPath
|
|
1505
1702
|
);
|
|
@@ -1545,7 +1742,7 @@ async function copyPageFromExport(input) {
|
|
|
1545
1742
|
const sourceRelative = toProjectRelative(exportPath, sourceFilePath);
|
|
1546
1743
|
const targetRelative = toProjectRelative(input.projectRoot, targetFilePath);
|
|
1547
1744
|
if (!targetBuffer) {
|
|
1548
|
-
await ensureDir(
|
|
1745
|
+
await ensureDir(path5.dirname(targetFilePath));
|
|
1549
1746
|
await writeFile2(targetFilePath, plannedSourceBuffer);
|
|
1550
1747
|
newFiles.push({
|
|
1551
1748
|
sourcePath: sourceRelative,
|
|
@@ -1574,8 +1771,8 @@ async function copyPageFromExport(input) {
|
|
|
1574
1771
|
isBinary
|
|
1575
1772
|
});
|
|
1576
1773
|
}
|
|
1577
|
-
const exportPackageJsonPath =
|
|
1578
|
-
const userPackageJsonPath =
|
|
1774
|
+
const exportPackageJsonPath = path5.join(exportPath, "package.json");
|
|
1775
|
+
const userPackageJsonPath = path5.join(input.projectRoot, "package.json");
|
|
1579
1776
|
if (!await fileExists2(exportPackageJsonPath)) {
|
|
1580
1777
|
throw new Error(`Export package.json not found: ${exportPackageJsonPath}`);
|
|
1581
1778
|
}
|
|
@@ -1781,7 +1978,7 @@ function isExportPage(value) {
|
|
|
1781
1978
|
return typeof maybe.id === "string" && (typeof title === "string" || typeof title === "undefined") && typeof maybe.slug === "string" && typeof maybe.pageType === "string" && maybe.isReadyToExport === true && typeof maybe.pagePath === "string" && typeof maybe.componentsPath === "string" && isExportPageManifest(maybe.manifest);
|
|
1782
1979
|
}
|
|
1783
1980
|
function buildManifestPath(exportsRoot, exportId) {
|
|
1784
|
-
return
|
|
1981
|
+
return path6.join(exportsRoot, `${exportId}.manifest.json`);
|
|
1785
1982
|
}
|
|
1786
1983
|
function parseExportManifest(value) {
|
|
1787
1984
|
if (!value || typeof value !== "object") {
|
|
@@ -1825,9 +2022,9 @@ var ProjectSyncService = class {
|
|
|
1825
2022
|
this.projectRoot = options.projectRoot;
|
|
1826
2023
|
this.targetProjectRoot = options.targetProjectRoot;
|
|
1827
2024
|
this.provider = options.provider;
|
|
1828
|
-
this.primeUiRoot =
|
|
1829
|
-
this.tempRoot =
|
|
1830
|
-
this.exportsRoot =
|
|
2025
|
+
this.primeUiRoot = path6.join(this.projectRoot, ".primeui");
|
|
2026
|
+
this.tempRoot = path6.join(this.primeUiRoot, "temp");
|
|
2027
|
+
this.exportsRoot = path6.join(this.tempRoot, "exports");
|
|
1831
2028
|
}
|
|
1832
2029
|
async getProjectInfo() {
|
|
1833
2030
|
await this.ensureTempLayout();
|
|
@@ -1852,8 +2049,8 @@ var ProjectSyncService = class {
|
|
|
1852
2049
|
if (!selected) {
|
|
1853
2050
|
throw new Error(`Export not found: ${id}`);
|
|
1854
2051
|
}
|
|
1855
|
-
const targetZipPath =
|
|
1856
|
-
const targetProjectPath =
|
|
2052
|
+
const targetZipPath = path6.join(this.exportsRoot, `${id}.zip`);
|
|
2053
|
+
const targetProjectPath = path6.join(this.exportsRoot, id);
|
|
1857
2054
|
const manifestPath = buildManifestPath(this.exportsRoot, id);
|
|
1858
2055
|
let manifest;
|
|
1859
2056
|
try {
|
|
@@ -1906,6 +2103,47 @@ var ProjectSyncService = class {
|
|
|
1906
2103
|
await resetDir(this.tempRoot);
|
|
1907
2104
|
await ensureDir(this.exportsRoot);
|
|
1908
2105
|
}
|
|
2106
|
+
async healthCheck() {
|
|
2107
|
+
return {
|
|
2108
|
+
status: "ok",
|
|
2109
|
+
runtime: {
|
|
2110
|
+
cwd: process.cwd(),
|
|
2111
|
+
initCwd: process.env.INIT_CWD?.trim() || null,
|
|
2112
|
+
pwd: process.env.PWD?.trim() || null,
|
|
2113
|
+
searchRoots: [process.cwd()],
|
|
2114
|
+
rootsListFallbackCwds: []
|
|
2115
|
+
},
|
|
2116
|
+
config: {
|
|
2117
|
+
found: true,
|
|
2118
|
+
projectRoot: this.projectRoot,
|
|
2119
|
+
projectConfigPath: path6.join(this.projectRoot, ".primeui", "project.json"),
|
|
2120
|
+
targetProjectPath: path6.relative(
|
|
2121
|
+
this.projectRoot,
|
|
2122
|
+
this.targetProjectRoot
|
|
2123
|
+
).startsWith(".") ? "./" : `./${path6.relative(this.projectRoot, this.targetProjectRoot)}`,
|
|
2124
|
+
targetProjectRoot: this.targetProjectRoot,
|
|
2125
|
+
source: process.env.PRIMEUI_PROJECT_ROOT?.trim() ? "env.PRIMEUI_PROJECT_ROOT" : "search",
|
|
2126
|
+
error: null
|
|
2127
|
+
},
|
|
2128
|
+
options: [
|
|
2129
|
+
{
|
|
2130
|
+
key: "PRIMEUI_PROJECT_ROOT",
|
|
2131
|
+
description: "Optional absolute root containing .primeui/project.json. Use as explicit override.",
|
|
2132
|
+
currentValue: process.env.PRIMEUI_PROJECT_ROOT?.trim() || null
|
|
2133
|
+
},
|
|
2134
|
+
{
|
|
2135
|
+
key: "PRIMEUI_API_BASE_URL",
|
|
2136
|
+
description: "Optional PrimeUI API base URL override.",
|
|
2137
|
+
currentValue: process.env.PRIMEUI_API_BASE_URL?.trim() || null
|
|
2138
|
+
},
|
|
2139
|
+
{
|
|
2140
|
+
key: "PRIMEUI_API_KEY",
|
|
2141
|
+
description: "Optional API key override. If missing, MCP uses apiKey from .primeui/project.json.",
|
|
2142
|
+
currentValue: process.env.PRIMEUI_API_KEY?.trim() ? "set" : null
|
|
2143
|
+
}
|
|
2144
|
+
]
|
|
2145
|
+
};
|
|
2146
|
+
}
|
|
1909
2147
|
async ensureTempLayout() {
|
|
1910
2148
|
await ensureDir(this.primeUiRoot);
|
|
1911
2149
|
await ensureDir(this.tempRoot);
|
|
@@ -1916,7 +2154,7 @@ var ProjectSyncService = class {
|
|
|
1916
2154
|
// src/sources/api-provider.ts
|
|
1917
2155
|
import { createWriteStream } from "fs";
|
|
1918
2156
|
import { unlink } from "fs/promises";
|
|
1919
|
-
import
|
|
2157
|
+
import path7 from "path";
|
|
1920
2158
|
import { Readable, Transform } from "stream";
|
|
1921
2159
|
import { pipeline } from "stream/promises";
|
|
1922
2160
|
import { z as z3 } from "zod";
|
|
@@ -2140,7 +2378,7 @@ var ApiProjectDataProvider = class {
|
|
|
2140
2378
|
if (!response.body) {
|
|
2141
2379
|
throw new PrimeUiApiContractError(endpoint, "response body is empty");
|
|
2142
2380
|
}
|
|
2143
|
-
await ensureDir(
|
|
2381
|
+
await ensureDir(path7.dirname(destinationPath));
|
|
2144
2382
|
const zipStream = Readable.fromWeb(
|
|
2145
2383
|
response.body
|
|
2146
2384
|
);
|
|
@@ -2230,17 +2468,22 @@ var ApiProjectDataProvider = class {
|
|
|
2230
2468
|
function getProjectConfigFallbackCwds() {
|
|
2231
2469
|
return [process.env.INIT_CWD, process.env.PWD].map((value) => value?.trim()).filter((value) => Boolean(value));
|
|
2232
2470
|
}
|
|
2233
|
-
async function createProjectSyncService() {
|
|
2471
|
+
async function createProjectSyncService(rootsFallbackCwds) {
|
|
2234
2472
|
const resolvedProjectConfig = await resolvePrimeUiProjectConfig({
|
|
2235
2473
|
cwd: process.cwd(),
|
|
2236
2474
|
projectRootFromEnv: process.env.PRIMEUI_PROJECT_ROOT,
|
|
2237
|
-
fallbackCwds: getProjectConfigFallbackCwds()
|
|
2475
|
+
fallbackCwds: [...getProjectConfigFallbackCwds(), ...rootsFallbackCwds]
|
|
2238
2476
|
});
|
|
2239
2477
|
const projectRoot = resolvedProjectConfig.projectRoot;
|
|
2240
|
-
const targetProjectRoot =
|
|
2478
|
+
const targetProjectRoot = path8.resolve(
|
|
2241
2479
|
projectRoot,
|
|
2242
2480
|
resolvedProjectConfig.projectConfig.targetProjectPath
|
|
2243
2481
|
);
|
|
2482
|
+
console.error("[primeui-mcp] RESOLVED_PROJECT_ROOT:", projectRoot);
|
|
2483
|
+
console.error(
|
|
2484
|
+
"[primeui-mcp] RESOLVED_PROJECT_CONFIG:",
|
|
2485
|
+
resolvedProjectConfig.projectConfigPath
|
|
2486
|
+
);
|
|
2244
2487
|
const apiKey = await resolvePrimeUiApiKey({
|
|
2245
2488
|
projectConfig: resolvedProjectConfig.projectConfig,
|
|
2246
2489
|
apiKeyFromEnv: process.env.PRIMEUI_API_KEY
|
|
@@ -2256,8 +2499,11 @@ async function createProjectSyncService() {
|
|
|
2256
2499
|
});
|
|
2257
2500
|
}
|
|
2258
2501
|
var LazyProjectSyncSource = class {
|
|
2502
|
+
constructor(getRootsFallbackCwds) {
|
|
2503
|
+
this.getRootsFallbackCwds = getRootsFallbackCwds;
|
|
2504
|
+
}
|
|
2259
2505
|
async withService(operation) {
|
|
2260
|
-
const service = await createProjectSyncService();
|
|
2506
|
+
const service = await createProjectSyncService(this.getRootsFallbackCwds());
|
|
2261
2507
|
return operation(service);
|
|
2262
2508
|
}
|
|
2263
2509
|
async getProjectInfo() {
|
|
@@ -2283,14 +2529,69 @@ var LazyProjectSyncSource = class {
|
|
|
2283
2529
|
async clearTemp() {
|
|
2284
2530
|
return this.withService((service) => service.clearTemp());
|
|
2285
2531
|
}
|
|
2532
|
+
async healthCheck() {
|
|
2533
|
+
const rootsFallbackCwds = this.getRootsFallbackCwds();
|
|
2534
|
+
const result = await runPrimeUiHealthCheck({
|
|
2535
|
+
cwd: process.cwd(),
|
|
2536
|
+
initCwd: process.env.INIT_CWD,
|
|
2537
|
+
pwd: process.env.PWD,
|
|
2538
|
+
projectRootFromEnv: process.env.PRIMEUI_PROJECT_ROOT,
|
|
2539
|
+
apiBaseUrlFromEnv: process.env.PRIMEUI_API_BASE_URL,
|
|
2540
|
+
apiKeyFromEnv: process.env.PRIMEUI_API_KEY,
|
|
2541
|
+
fallbackCwds: [...getProjectConfigFallbackCwds(), ...rootsFallbackCwds],
|
|
2542
|
+
rootsListFallbackCwds: rootsFallbackCwds
|
|
2543
|
+
});
|
|
2544
|
+
if (result.config.found) {
|
|
2545
|
+
console.error("[primeui-mcp] RESOLVED_PROJECT_ROOT:", result.config.projectRoot);
|
|
2546
|
+
console.error(
|
|
2547
|
+
"[primeui-mcp] RESOLVED_PROJECT_CONFIG:",
|
|
2548
|
+
result.config.projectConfigPath
|
|
2549
|
+
);
|
|
2550
|
+
} else {
|
|
2551
|
+
console.error("[primeui-mcp] RESOLUTION_FAILED:", result.config.error);
|
|
2552
|
+
}
|
|
2553
|
+
return result;
|
|
2554
|
+
}
|
|
2286
2555
|
};
|
|
2556
|
+
function toFileRootPath(rootUri) {
|
|
2557
|
+
try {
|
|
2558
|
+
const parsed = new URL(rootUri);
|
|
2559
|
+
if (parsed.protocol !== "file:") {
|
|
2560
|
+
return null;
|
|
2561
|
+
}
|
|
2562
|
+
return fileURLToPath(parsed);
|
|
2563
|
+
} catch {
|
|
2564
|
+
return null;
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
async function loadClientRootFallbackCwds(server) {
|
|
2568
|
+
try {
|
|
2569
|
+
const response = await server.server.listRoots(void 0, { timeout: 1500 });
|
|
2570
|
+
const roots = response.roots.map((root) => root.uri);
|
|
2571
|
+
const resolvedRoots = roots.map((rootUri) => toFileRootPath(rootUri)).filter((rootPath) => Boolean(rootPath));
|
|
2572
|
+
console.error("[primeui-mcp] roots/list supported:", JSON.stringify(roots));
|
|
2573
|
+
console.error(
|
|
2574
|
+
"[primeui-mcp] roots/list file roots:",
|
|
2575
|
+
JSON.stringify(resolvedRoots)
|
|
2576
|
+
);
|
|
2577
|
+
return resolvedRoots;
|
|
2578
|
+
} catch (error) {
|
|
2579
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2580
|
+
console.error("[primeui-mcp] roots/list unavailable:", message);
|
|
2581
|
+
return [];
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2287
2584
|
async function main() {
|
|
2288
2585
|
console.error("CWD:", process.cwd());
|
|
2289
2586
|
console.error("INIT_CWD:", process.env.INIT_CWD);
|
|
2290
2587
|
console.error("PWD:", process.env.PWD);
|
|
2291
|
-
|
|
2588
|
+
let rootsFallbackCwds = [];
|
|
2589
|
+
const server = createPrimeUiMcpServer(
|
|
2590
|
+
new LazyProjectSyncSource(() => rootsFallbackCwds)
|
|
2591
|
+
);
|
|
2292
2592
|
const transport = new StdioServerTransport();
|
|
2293
2593
|
await server.connect(transport);
|
|
2594
|
+
rootsFallbackCwds = await loadClientRootFallbackCwds(server);
|
|
2294
2595
|
}
|
|
2295
2596
|
main().catch((error) => {
|
|
2296
2597
|
console.error("[primeui-mcp] failed to start", error);
|