@bluealba/platform-cli 1.2.0-alpha.2 → 1.2.0-alpha.3
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/index.js +264 -157
- package/package.json +1 -1
- package/skills/ba-platform/platform-add-feature-flag.skill.md +1 -1
- package/skills/ba-platform/platform-add-menu-item.skill.md +1 -1
- package/skills/ba-platform/platform-add-operation.skill.md +1 -1
- package/skills/ba-platform/platform-add-presence.skill.md +1 -1
- package/skills/ba-platform/platform-add-scheduled-job.skill.md +1 -1
- package/skills/ba-platform/platform-cli.skill.md +1 -1
- package/skills/ba-platform/platform-extend-shell.skill.md +1 -1
- package/skills/ba-platform/platform-scaffold-module.skill.md +1 -1
- package/skills/ba-platform/platform.skill.md +63 -20
- package/templates/application-monorepo-template/CLAUDE.md +36 -0
- package/templates/platform-init-template/{{platformName}}-core/CLAUDE.md +34 -0
package/dist/index.js
CHANGED
|
@@ -204,7 +204,7 @@ function ConfirmPrompt({ value }) {
|
|
|
204
204
|
import { Fzf } from "fzf";
|
|
205
205
|
|
|
206
206
|
// src/commands/create-application/create-application.command.ts
|
|
207
|
-
import { join as
|
|
207
|
+
import { join as join13, resolve as resolve2 } from "path";
|
|
208
208
|
import { cwd as cwd4 } from "process";
|
|
209
209
|
import { mkdir as mkdir3 } from "fs/promises";
|
|
210
210
|
|
|
@@ -730,6 +730,15 @@ async function getDefaults() {
|
|
|
730
730
|
const cache = await readCacheFile();
|
|
731
731
|
return cache.defaults;
|
|
732
732
|
}
|
|
733
|
+
var SENSITIVE_KEYS = /* @__PURE__ */ new Set(["clientSecret"]);
|
|
734
|
+
function maskSensitiveFields(obj) {
|
|
735
|
+
return JSON.parse(
|
|
736
|
+
JSON.stringify(
|
|
737
|
+
obj,
|
|
738
|
+
(key, value) => SENSITIVE_KEYS.has(key) && typeof value === "string" && value.length > 0 ? "***" : value
|
|
739
|
+
)
|
|
740
|
+
);
|
|
741
|
+
}
|
|
733
742
|
var LEGACY_CONFIG_FILE = "standalone.json";
|
|
734
743
|
async function readLegacyConfig(appDir) {
|
|
735
744
|
try {
|
|
@@ -742,6 +751,50 @@ async function readLegacyConfig(appDir) {
|
|
|
742
751
|
}
|
|
743
752
|
}
|
|
744
753
|
|
|
754
|
+
// src/utils/workspace-claude-md.ts
|
|
755
|
+
import { join as join12 } from "path";
|
|
756
|
+
import { writeFile as writeFile6 } from "fs/promises";
|
|
757
|
+
async function writeWorkspaceClaudeMd(rootDir, manifest) {
|
|
758
|
+
const { product, applications } = manifest;
|
|
759
|
+
const coreDirName = `${product.name}-core`;
|
|
760
|
+
const appLines = applications.map(
|
|
761
|
+
(app) => `- \`${product.name}-${app.name}/\` \u2014 ${app.displayName}: ${app.description}`
|
|
762
|
+
);
|
|
763
|
+
const appSection = appLines.length > 0 ? `
|
|
764
|
+
### Applications
|
|
765
|
+
|
|
766
|
+
${appLines.join("\n")}
|
|
767
|
+
` : "";
|
|
768
|
+
const content = `# ${product.displayName} Platform
|
|
769
|
+
|
|
770
|
+
This is a **Blue Alba Platform** workspace for \`@${product.organization}\`.
|
|
771
|
+
|
|
772
|
+
For any platform-related task, use the \`/platform\` skill \u2014 it routes to the correct specialized skill automatically.
|
|
773
|
+
|
|
774
|
+
## Workspace Layout
|
|
775
|
+
|
|
776
|
+
### Core
|
|
777
|
+
|
|
778
|
+
- \`${coreDirName}/\` \u2014 Core monorepo (bootstrap service, customization UI, local dev environment)
|
|
779
|
+
- \`${coreDirName}/local/\` \u2014 Docker Compose files, .env, SSL certificates
|
|
780
|
+
- \`${coreDirName}/local/platform-docker-compose.yml\` \u2014 Platform infrastructure (nginx, postgres, gateway)
|
|
781
|
+
- \`${coreDirName}/local/${product.name}-core-docker-compose.yml\` \u2014 Core application services
|
|
782
|
+
- \`${coreDirName}/local/.env\` \u2014 Environment variables (shared by all applications)
|
|
783
|
+
${appSection}
|
|
784
|
+
## Common Commands
|
|
785
|
+
|
|
786
|
+
\`\`\`sh
|
|
787
|
+
platform install # Install all monorepos
|
|
788
|
+
platform start # Start the full platform locally via Docker
|
|
789
|
+
platform stop # Stop all running containers
|
|
790
|
+
platform status # Show platform health and service status
|
|
791
|
+
platform create-application # Add a new application to this platform
|
|
792
|
+
platform standalone # Run a single application in isolation
|
|
793
|
+
\`\`\`
|
|
794
|
+
`;
|
|
795
|
+
await writeFile6(join12(rootDir, "CLAUDE.md"), content, "utf-8");
|
|
796
|
+
}
|
|
797
|
+
|
|
745
798
|
// src/commands/create-application/create-application.command.ts
|
|
746
799
|
var CREATE_APPLICATION_COMMAND_NAME = "create-application";
|
|
747
800
|
var createApplicationCommand = {
|
|
@@ -779,7 +832,7 @@ async function createApplication(params, logger) {
|
|
|
779
832
|
platformName = manifest.product.name;
|
|
780
833
|
organizationName = manifest.product.organization;
|
|
781
834
|
const localPath = `../${platformName}-${applicationName}`;
|
|
782
|
-
applicationDir = resolve2(
|
|
835
|
+
applicationDir = resolve2(join13(rootDir, coreDirName), localPath);
|
|
783
836
|
} else {
|
|
784
837
|
if (!params.platformName || !params.organizationName) {
|
|
785
838
|
logger.log("Error: platformName and organizationName are required when creating an application outside a platform.");
|
|
@@ -787,10 +840,10 @@ async function createApplication(params, logger) {
|
|
|
787
840
|
}
|
|
788
841
|
platformName = params.platformName;
|
|
789
842
|
organizationName = params.organizationName;
|
|
790
|
-
applicationDir =
|
|
843
|
+
applicationDir = join13(cwd4(), `${platformName}-${applicationName}`);
|
|
791
844
|
}
|
|
792
845
|
const bootstrapServiceName = `${platformName}-${applicationName}-bootstrap-service`;
|
|
793
|
-
const bootstrapServiceDir =
|
|
846
|
+
const bootstrapServiceDir = join13(applicationDir, "services", bootstrapServiceName);
|
|
794
847
|
logger.log(`Creating application monorepo "${applicationName}"...`);
|
|
795
848
|
try {
|
|
796
849
|
await scaffoldApplicationMonorepo(applicationDir, organizationName, platformName, applicationName, logger);
|
|
@@ -798,8 +851,8 @@ async function createApplication(params, logger) {
|
|
|
798
851
|
logger.log(`Error: Could not scaffold application monorepo \u2014 ${formatError(err)}`);
|
|
799
852
|
return;
|
|
800
853
|
}
|
|
801
|
-
await mkdir3(
|
|
802
|
-
await mkdir3(
|
|
854
|
+
await mkdir3(join13(applicationDir, "services"), { recursive: true });
|
|
855
|
+
await mkdir3(join13(applicationDir, "ui"), { recursive: true });
|
|
803
856
|
logger.log(`Creating bootstrap service "${bootstrapServiceName}"...`);
|
|
804
857
|
const bootstrapServiceBaseDir = `${platformName}-${applicationName}/services`;
|
|
805
858
|
try {
|
|
@@ -833,7 +886,7 @@ async function createApplication(params, logger) {
|
|
|
833
886
|
}
|
|
834
887
|
if (hasUserInterface) {
|
|
835
888
|
const uiName = `${platformName}-${applicationName}-ui`;
|
|
836
|
-
const uiDir =
|
|
889
|
+
const uiDir = join13(applicationDir, "ui", uiName);
|
|
837
890
|
const uiBaseDir = `${platformName}-${applicationName}/ui`;
|
|
838
891
|
try {
|
|
839
892
|
await scaffoldUiModule(uiDir, organizationName, platformName, applicationName, applicationDisplayName, uiBaseDir, logger);
|
|
@@ -844,7 +897,7 @@ async function createApplication(params, logger) {
|
|
|
844
897
|
}
|
|
845
898
|
if (hasBackendService) {
|
|
846
899
|
const serviceName = `${platformName}-${applicationName}-service`;
|
|
847
|
-
const serviceDir =
|
|
900
|
+
const serviceDir = join13(applicationDir, "services", serviceName);
|
|
848
901
|
const serviceBaseDir = `${platformName}-${applicationName}/services`;
|
|
849
902
|
try {
|
|
850
903
|
await scaffoldNestjsService(serviceDir, organizationName, platformName, applicationName, applicationDisplayName, serviceBaseDir, logger);
|
|
@@ -853,7 +906,7 @@ async function createApplication(params, logger) {
|
|
|
853
906
|
return;
|
|
854
907
|
}
|
|
855
908
|
}
|
|
856
|
-
const dockerComposePath =
|
|
909
|
+
const dockerComposePath = join13(applicationDir, "docker-compose.yml");
|
|
857
910
|
let uiPort;
|
|
858
911
|
let servicePort;
|
|
859
912
|
if (insidePlatform && localDir && manifest) {
|
|
@@ -889,6 +942,12 @@ async function createApplication(params, logger) {
|
|
|
889
942
|
} catch (err) {
|
|
890
943
|
logger.log(`Warning: Could not update product manifest \u2014 ${formatError(err)}`);
|
|
891
944
|
}
|
|
945
|
+
try {
|
|
946
|
+
await writeWorkspaceClaudeMd(rootDir, updatedManifest);
|
|
947
|
+
logger.log(`Updated workspace CLAUDE.md.`);
|
|
948
|
+
} catch (err) {
|
|
949
|
+
logger.log(`Warning: Could not update workspace CLAUDE.md \u2014 ${formatError(err)}`);
|
|
950
|
+
}
|
|
892
951
|
} else {
|
|
893
952
|
try {
|
|
894
953
|
await saveDefaults({ platformName, organizationName });
|
|
@@ -906,7 +965,7 @@ async function createApplication(params, logger) {
|
|
|
906
965
|
}
|
|
907
966
|
|
|
908
967
|
// src/commands/init/init.command.ts
|
|
909
|
-
import { join as
|
|
968
|
+
import { join as join19 } from "path";
|
|
910
969
|
import { cwd as cwd5 } from "process";
|
|
911
970
|
|
|
912
971
|
// src/utils/string.ts
|
|
@@ -916,8 +975,8 @@ function camelize(name) {
|
|
|
916
975
|
|
|
917
976
|
// src/commands/init/scaffold-platform.ts
|
|
918
977
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
919
|
-
import { join as
|
|
920
|
-
var templateDir =
|
|
978
|
+
import { join as join14, dirname as dirname8 } from "path";
|
|
979
|
+
var templateDir = join14(
|
|
921
980
|
dirname8(fileURLToPath6(import.meta.url)),
|
|
922
981
|
"..",
|
|
923
982
|
"templates",
|
|
@@ -929,8 +988,8 @@ async function scaffoldPlatform(outputDir, variables, logger) {
|
|
|
929
988
|
|
|
930
989
|
// src/commands/init/scaffold-platform-bootstrap.ts
|
|
931
990
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
932
|
-
import { join as
|
|
933
|
-
var templateDir2 =
|
|
991
|
+
import { join as join15, dirname as dirname9 } from "path";
|
|
992
|
+
var templateDir2 = join15(
|
|
934
993
|
dirname9(fileURLToPath7(import.meta.url)),
|
|
935
994
|
"..",
|
|
936
995
|
"templates",
|
|
@@ -942,8 +1001,8 @@ async function scaffoldPlatformBootstrap(outputDir, variables, logger) {
|
|
|
942
1001
|
|
|
943
1002
|
// src/commands/init/scaffold-customization-ui.ts
|
|
944
1003
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
945
|
-
import { join as
|
|
946
|
-
var templateDir3 =
|
|
1004
|
+
import { join as join16, dirname as dirname10 } from "path";
|
|
1005
|
+
var templateDir3 = join16(
|
|
947
1006
|
dirname10(fileURLToPath8(import.meta.url)),
|
|
948
1007
|
"..",
|
|
949
1008
|
"templates",
|
|
@@ -954,10 +1013,10 @@ async function scaffoldCustomizationUi(outputDir, variables, logger) {
|
|
|
954
1013
|
}
|
|
955
1014
|
|
|
956
1015
|
// src/commands/init/register-customization-module.ts
|
|
957
|
-
import { join as
|
|
958
|
-
import { readFile as readFile6, writeFile as
|
|
1016
|
+
import { join as join17 } from "path";
|
|
1017
|
+
import { readFile as readFile6, writeFile as writeFile7 } from "fs/promises";
|
|
959
1018
|
async function registerCustomizationModule(bootstrapServiceDir, organizationName, logger, platformName = "platform") {
|
|
960
|
-
const modulesJsonPath =
|
|
1019
|
+
const modulesJsonPath = join17(bootstrapServiceDir, "src", "data", "platform", "modules.json");
|
|
961
1020
|
const customizationUiName = `${platformName}-customization-ui`;
|
|
962
1021
|
const entry = {
|
|
963
1022
|
name: `@${organizationName}/${customizationUiName}`,
|
|
@@ -975,13 +1034,13 @@ async function registerCustomizationModule(bootstrapServiceDir, organizationName
|
|
|
975
1034
|
};
|
|
976
1035
|
const existing = JSON.parse(await readFile6(modulesJsonPath, "utf-8"));
|
|
977
1036
|
existing.push(entry);
|
|
978
|
-
await
|
|
1037
|
+
await writeFile7(modulesJsonPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
|
|
979
1038
|
logger.log(`Registered customization module in ${modulesJsonPath}`);
|
|
980
1039
|
}
|
|
981
1040
|
|
|
982
1041
|
// src/commands/init/generate-local-env.ts
|
|
983
|
-
import { readFile as readFile7, writeFile as
|
|
984
|
-
import { join as
|
|
1042
|
+
import { readFile as readFile7, writeFile as writeFile8 } from "fs/promises";
|
|
1043
|
+
import { join as join18 } from "path";
|
|
985
1044
|
|
|
986
1045
|
// src/utils/random.ts
|
|
987
1046
|
import { randomBytes } from "crypto";
|
|
@@ -991,14 +1050,14 @@ function generateRandomSecret() {
|
|
|
991
1050
|
|
|
992
1051
|
// src/commands/init/generate-local-env.ts
|
|
993
1052
|
async function generateLocalEnv(outputDir, logger, coreDirName = "core") {
|
|
994
|
-
const examplePath =
|
|
995
|
-
const envPath =
|
|
1053
|
+
const examplePath = join18(outputDir, coreDirName, "local", ".env.example");
|
|
1054
|
+
const envPath = join18(outputDir, coreDirName, "local", ".env");
|
|
996
1055
|
logger.log(`Generating ${coreDirName}/local/.env with random secrets...`);
|
|
997
1056
|
const content = await readFile7(examplePath, "utf-8");
|
|
998
1057
|
const result = content.replace(/^(PAE_AUTH_JWT_SECRET|PAE_GATEWAY_SERVICE_ACCESS_SECRET|PAE_DB_PASSWORD)=$/gm, (_, key) => {
|
|
999
1058
|
return `${key}=${generateRandomSecret()}`;
|
|
1000
1059
|
});
|
|
1001
|
-
await
|
|
1060
|
+
await writeFile8(envPath, result, "utf-8");
|
|
1002
1061
|
}
|
|
1003
1062
|
|
|
1004
1063
|
// src/commands/init/init.command.ts
|
|
@@ -1044,17 +1103,24 @@ async function init(params, logger) {
|
|
|
1044
1103
|
logger.log(`Error: Could not generate ${coreDirName}/local/.env \u2014 ${formatError(err)}`);
|
|
1045
1104
|
return;
|
|
1046
1105
|
}
|
|
1106
|
+
let manifest;
|
|
1047
1107
|
try {
|
|
1048
|
-
|
|
1108
|
+
manifest = createInitialManifest({ organizationName, platformName, platformDisplayName });
|
|
1049
1109
|
await writeManifest(manifest, outputDir, coreDirName);
|
|
1050
1110
|
logger.log(`Created product manifest: ${coreDirName}/product.manifest.json`);
|
|
1051
1111
|
} catch (err) {
|
|
1052
1112
|
logger.log(`Error: Could not write product manifest \u2014 ${formatError(err)}`);
|
|
1053
1113
|
return;
|
|
1054
1114
|
}
|
|
1115
|
+
try {
|
|
1116
|
+
await writeWorkspaceClaudeMd(outputDir, manifest);
|
|
1117
|
+
logger.log(`Created workspace CLAUDE.md`);
|
|
1118
|
+
} catch (err) {
|
|
1119
|
+
logger.log(`Warning: Could not write workspace CLAUDE.md \u2014 ${formatError(err)}`);
|
|
1120
|
+
}
|
|
1055
1121
|
try {
|
|
1056
1122
|
await scaffoldPlatformBootstrap(
|
|
1057
|
-
|
|
1123
|
+
join19(outputDir, coreDirName, "services", bootstrapServiceName),
|
|
1058
1124
|
variables,
|
|
1059
1125
|
logger
|
|
1060
1126
|
);
|
|
@@ -1064,7 +1130,7 @@ async function init(params, logger) {
|
|
|
1064
1130
|
}
|
|
1065
1131
|
try {
|
|
1066
1132
|
await scaffoldCustomizationUi(
|
|
1067
|
-
|
|
1133
|
+
join19(outputDir, coreDirName, "ui", customizationUiName),
|
|
1068
1134
|
variables,
|
|
1069
1135
|
logger
|
|
1070
1136
|
);
|
|
@@ -1074,7 +1140,7 @@ async function init(params, logger) {
|
|
|
1074
1140
|
}
|
|
1075
1141
|
try {
|
|
1076
1142
|
await registerCustomizationModule(
|
|
1077
|
-
|
|
1143
|
+
join19(outputDir, coreDirName, "services", bootstrapServiceName),
|
|
1078
1144
|
organizationName,
|
|
1079
1145
|
logger,
|
|
1080
1146
|
platformName
|
|
@@ -1087,7 +1153,7 @@ async function init(params, logger) {
|
|
|
1087
1153
|
}
|
|
1088
1154
|
|
|
1089
1155
|
// src/commands/configure-idp/configure-idp.command.ts
|
|
1090
|
-
import { join as
|
|
1156
|
+
import { join as join20 } from "path";
|
|
1091
1157
|
import { fetch as undiciFetch, Agent } from "undici";
|
|
1092
1158
|
|
|
1093
1159
|
// src/utils/env-reader.ts
|
|
@@ -1171,7 +1237,7 @@ async function configureIdp(params, logger) {
|
|
|
1171
1237
|
logger.log("Error: Cannot configure an IDP \u2014 no platform initialized in this directory.");
|
|
1172
1238
|
return;
|
|
1173
1239
|
}
|
|
1174
|
-
const envPath =
|
|
1240
|
+
const envPath = join20(layout.localDir, ".env");
|
|
1175
1241
|
let env;
|
|
1176
1242
|
try {
|
|
1177
1243
|
env = await readEnvFile(envPath);
|
|
@@ -1223,13 +1289,13 @@ async function configureIdp(params, logger) {
|
|
|
1223
1289
|
}
|
|
1224
1290
|
|
|
1225
1291
|
// src/commands/create-service-module/create-service-module.command.ts
|
|
1226
|
-
import { join as
|
|
1292
|
+
import { join as join22, resolve as resolve3 } from "path";
|
|
1227
1293
|
import { access as access4 } from "fs/promises";
|
|
1228
1294
|
|
|
1229
1295
|
// src/commands/create-service-module/scaffold-service-module.ts
|
|
1230
1296
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
1231
|
-
import { join as
|
|
1232
|
-
var nestjsServiceModuleTemplateDir2 =
|
|
1297
|
+
import { join as join21, dirname as dirname11 } from "path";
|
|
1298
|
+
var nestjsServiceModuleTemplateDir2 = join21(
|
|
1233
1299
|
dirname11(fileURLToPath9(import.meta.url)),
|
|
1234
1300
|
"..",
|
|
1235
1301
|
"templates",
|
|
@@ -1264,7 +1330,7 @@ function buildCustomServiceModuleEntry(organizationName, serviceName, serviceDis
|
|
|
1264
1330
|
}
|
|
1265
1331
|
|
|
1266
1332
|
// src/commands/create-service-module/append-docker-compose.ts
|
|
1267
|
-
import { readFile as readFile9, writeFile as
|
|
1333
|
+
import { readFile as readFile9, writeFile as writeFile9 } from "fs/promises";
|
|
1268
1334
|
async function appendServiceToDockerCompose(dockerComposePath, serviceName, platformName, applicationName, port, logger) {
|
|
1269
1335
|
const block = `
|
|
1270
1336
|
${serviceName}:
|
|
@@ -1280,7 +1346,7 @@ async function appendServiceToDockerCompose(dockerComposePath, serviceName, plat
|
|
|
1280
1346
|
`;
|
|
1281
1347
|
try {
|
|
1282
1348
|
const existing = await readFile9(dockerComposePath, "utf-8");
|
|
1283
|
-
await
|
|
1349
|
+
await writeFile9(dockerComposePath, existing + block, "utf-8");
|
|
1284
1350
|
logger.log(`Updated docker-compose: ${dockerComposePath}`);
|
|
1285
1351
|
} catch (err) {
|
|
1286
1352
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -1316,7 +1382,7 @@ async function createServiceModule(params, logger) {
|
|
|
1316
1382
|
logger.log(`Error: The specified application "${applicationName}" is not registered in the product manifest.`);
|
|
1317
1383
|
return;
|
|
1318
1384
|
}
|
|
1319
|
-
const applicationDir = resolve3(
|
|
1385
|
+
const applicationDir = resolve3(join22(rootDir, coreDirName), appEntry.localPath);
|
|
1320
1386
|
try {
|
|
1321
1387
|
await access4(applicationDir);
|
|
1322
1388
|
} catch {
|
|
@@ -1325,7 +1391,7 @@ async function createServiceModule(params, logger) {
|
|
|
1325
1391
|
}
|
|
1326
1392
|
const suffix = serviceNameSuffix === void 0 ? "service" : serviceNameSuffix;
|
|
1327
1393
|
const fullServiceName = suffix ? `${platformName}-${serviceName}-${suffix}` : `${platformName}-${serviceName}`;
|
|
1328
|
-
const serviceDir =
|
|
1394
|
+
const serviceDir = join22(applicationDir, "services", fullServiceName);
|
|
1329
1395
|
try {
|
|
1330
1396
|
await access4(serviceDir);
|
|
1331
1397
|
logger.log(`Error: A service named "${fullServiceName}" already exists in application "${applicationName}".`);
|
|
@@ -1346,21 +1412,21 @@ async function createServiceModule(params, logger) {
|
|
|
1346
1412
|
logger.log(`Error: Could not scaffold NestJS service \u2014 ${formatError(err)}`);
|
|
1347
1413
|
return;
|
|
1348
1414
|
}
|
|
1349
|
-
const bootstrapServiceDir =
|
|
1415
|
+
const bootstrapServiceDir = join22(applicationDir, "services", `${platformName}-${applicationName}-bootstrap-service`);
|
|
1350
1416
|
const moduleEntry = buildCustomServiceModuleEntry(organizationName, fullServiceName, serviceDisplayName);
|
|
1351
1417
|
await addModuleEntry(bootstrapServiceDir, applicationName, moduleEntry, logger);
|
|
1352
|
-
const dockerComposePath =
|
|
1418
|
+
const dockerComposePath = join22(localDir, `${platformName}-${applicationName}-docker-compose.yml`);
|
|
1353
1419
|
const port = await getNextAvailablePort(localDir);
|
|
1354
1420
|
await appendServiceToDockerCompose(dockerComposePath, fullServiceName, platformName, applicationName, port, logger);
|
|
1355
1421
|
logger.log(`Done! Service module "${fullServiceName}" added to application "${applicationName}".`);
|
|
1356
1422
|
}
|
|
1357
1423
|
|
|
1358
1424
|
// src/commands/create-ui-module/create-ui-module.command.ts
|
|
1359
|
-
import { join as
|
|
1425
|
+
import { join as join23, resolve as resolve4 } from "path";
|
|
1360
1426
|
import { access as access5, readdir as readdir4 } from "fs/promises";
|
|
1361
1427
|
|
|
1362
1428
|
// src/commands/create-ui-module/append-ui-docker-compose.ts
|
|
1363
|
-
import { readFile as readFile10, writeFile as
|
|
1429
|
+
import { readFile as readFile10, writeFile as writeFile10 } from "fs/promises";
|
|
1364
1430
|
async function appendUiToDockerCompose(dockerComposePath, platformName, applicationName, port, logger, uiNameOverride) {
|
|
1365
1431
|
const uiName = uiNameOverride ?? `${platformName}-${applicationName}-ui`;
|
|
1366
1432
|
const block = `
|
|
@@ -1375,7 +1441,7 @@ async function appendUiToDockerCompose(dockerComposePath, platformName, applicat
|
|
|
1375
1441
|
`;
|
|
1376
1442
|
try {
|
|
1377
1443
|
const existing = await readFile10(dockerComposePath, "utf-8");
|
|
1378
|
-
await
|
|
1444
|
+
await writeFile10(dockerComposePath, existing + block, "utf-8");
|
|
1379
1445
|
logger.log(`Updated docker-compose: ${dockerComposePath}`);
|
|
1380
1446
|
} catch (err) {
|
|
1381
1447
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -1411,14 +1477,14 @@ async function createUiModule(params, logger) {
|
|
|
1411
1477
|
logger.log(`Error: The specified application "${applicationName}" is not registered in the product manifest.`);
|
|
1412
1478
|
return;
|
|
1413
1479
|
}
|
|
1414
|
-
const applicationDir = resolve4(
|
|
1480
|
+
const applicationDir = resolve4(join23(rootDir, coreDirName), appEntry.localPath);
|
|
1415
1481
|
try {
|
|
1416
1482
|
await access5(applicationDir);
|
|
1417
1483
|
} catch {
|
|
1418
1484
|
logger.log(`Error: The specified application "${applicationName}" does not exist in the platform.`);
|
|
1419
1485
|
return;
|
|
1420
1486
|
}
|
|
1421
|
-
const uiDir =
|
|
1487
|
+
const uiDir = join23(applicationDir, "ui");
|
|
1422
1488
|
try {
|
|
1423
1489
|
const uiEntries = await readdir4(uiDir);
|
|
1424
1490
|
const existingUiModules = uiEntries.filter((e) => e !== ".gitkeep");
|
|
@@ -1430,7 +1496,7 @@ async function createUiModule(params, logger) {
|
|
|
1430
1496
|
}
|
|
1431
1497
|
const uiSuffix = uiModuleSuffix === void 0 ? "ui" : uiModuleSuffix;
|
|
1432
1498
|
const uiName = uiSuffix ? `${platformName}-${applicationName}-${uiSuffix}` : `${platformName}-${applicationName}`;
|
|
1433
|
-
const uiOutputDir =
|
|
1499
|
+
const uiOutputDir = join23(uiDir, uiName);
|
|
1434
1500
|
const uiBaseDir = `${platformName}-${applicationName}/ui`;
|
|
1435
1501
|
try {
|
|
1436
1502
|
await scaffoldUiModule(uiOutputDir, organizationName, platformName, applicationName, applicationDisplayName, uiBaseDir, logger);
|
|
@@ -1438,10 +1504,10 @@ async function createUiModule(params, logger) {
|
|
|
1438
1504
|
logger.log(`Error: Could not scaffold UI module \u2014 ${formatError(err)}`);
|
|
1439
1505
|
return;
|
|
1440
1506
|
}
|
|
1441
|
-
const bootstrapServiceDir =
|
|
1507
|
+
const bootstrapServiceDir = join23(applicationDir, "services", `${platformName}-${applicationName}-bootstrap-service`);
|
|
1442
1508
|
const moduleEntry = buildUiModuleEntry(organizationName, platformName, applicationName, applicationDisplayName, uiName);
|
|
1443
1509
|
await addModuleEntry(bootstrapServiceDir, applicationName, moduleEntry, logger);
|
|
1444
|
-
const dockerComposePath =
|
|
1510
|
+
const dockerComposePath = join23(localDir, `${platformName}-${applicationName}-docker-compose.yml`);
|
|
1445
1511
|
const port = await getNextAvailablePort(localDir);
|
|
1446
1512
|
await appendUiToDockerCompose(dockerComposePath, platformName, applicationName, port, logger, uiName);
|
|
1447
1513
|
logger.log(`Done! UI module "${uiName}" added to application "${applicationName}".`);
|
|
@@ -1450,13 +1516,13 @@ async function createUiModule(params, logger) {
|
|
|
1450
1516
|
// src/commands/status/status-checks.ts
|
|
1451
1517
|
import { spawn as spawn3 } from "child_process";
|
|
1452
1518
|
import { access as access8 } from "fs/promises";
|
|
1453
|
-
import { join as
|
|
1519
|
+
import { join as join26, resolve as resolve7 } from "path";
|
|
1454
1520
|
import { fetch as undiciFetch2, Agent as Agent2 } from "undici";
|
|
1455
1521
|
|
|
1456
1522
|
// src/commands/local-scripts/docker-compose-orchestrator.ts
|
|
1457
1523
|
import { spawn } from "child_process";
|
|
1458
|
-
import { access as access6 } from "fs/promises";
|
|
1459
|
-
import { join as
|
|
1524
|
+
import { access as access6, readFile as readFile11, writeFile as writeFile11 } from "fs/promises";
|
|
1525
|
+
import { dirname as dirname12, join as join24, resolve as resolve5 } from "path";
|
|
1460
1526
|
function runDockerCompose(args2, logger, rootDir, signal) {
|
|
1461
1527
|
return new Promise((resolvePromise) => {
|
|
1462
1528
|
const child = spawn("docker", ["compose", ...args2], {
|
|
@@ -1519,17 +1585,30 @@ function captureDockerCompose(args2, rootDir) {
|
|
|
1519
1585
|
child.on("error", (err) => reject(err));
|
|
1520
1586
|
});
|
|
1521
1587
|
}
|
|
1588
|
+
async function generateProxyAppCompose(appDir, localDir, platformName, applicationName) {
|
|
1589
|
+
const appComposePath = join24(appDir, "docker-compose.yml");
|
|
1590
|
+
const content = await readFile11(appComposePath, "utf-8");
|
|
1591
|
+
const withAbsContexts = content.replace(
|
|
1592
|
+
/^(\s+context:\s+)\.\/(.*)$/gm,
|
|
1593
|
+
`$1${appDir}/$2`
|
|
1594
|
+
);
|
|
1595
|
+
const appParentDir = dirname12(appDir);
|
|
1596
|
+
const rewritten = withAbsContexts.replace(/\$\{PWD\}/g, appParentDir);
|
|
1597
|
+
const proxyPath = join24(localDir, `${platformName}-${applicationName}-docker-compose.yml`);
|
|
1598
|
+
await writeFile11(proxyPath, rewritten, "utf-8");
|
|
1599
|
+
return proxyPath;
|
|
1600
|
+
}
|
|
1522
1601
|
async function getAppComposePaths(localDir, coreDir, platformName, manifest, logger) {
|
|
1523
1602
|
const results = [];
|
|
1524
1603
|
for (const app of manifest.applications) {
|
|
1525
|
-
const appDir = resolve5(
|
|
1526
|
-
const newPath =
|
|
1527
|
-
const legacyPrefixedPath =
|
|
1528
|
-
const legacyUnprefixedPath =
|
|
1604
|
+
const appDir = resolve5(join24(coreDir), app.localPath);
|
|
1605
|
+
const newPath = join24(appDir, "docker-compose.yml");
|
|
1606
|
+
const legacyPrefixedPath = join24(localDir, `${platformName}-${app.name}-docker-compose.yml`);
|
|
1607
|
+
const legacyUnprefixedPath = join24(localDir, `${app.name}-docker-compose.yml`);
|
|
1529
1608
|
let resolved = null;
|
|
1530
1609
|
try {
|
|
1531
1610
|
await access6(newPath);
|
|
1532
|
-
resolved =
|
|
1611
|
+
resolved = await generateProxyAppCompose(appDir, localDir, platformName, app.name);
|
|
1533
1612
|
} catch {
|
|
1534
1613
|
try {
|
|
1535
1614
|
await access6(legacyPrefixedPath);
|
|
@@ -1553,8 +1632,8 @@ async function getAppComposePaths(localDir, coreDir, platformName, manifest, log
|
|
|
1553
1632
|
async function buildFullComposeArgs(layout, manifest, logger) {
|
|
1554
1633
|
const { coreDirName, localDir, coreDir } = layout;
|
|
1555
1634
|
const platformName = manifest.product.name;
|
|
1556
|
-
const prefixedCoreCompose =
|
|
1557
|
-
const unprefixedCoreCompose =
|
|
1635
|
+
const prefixedCoreCompose = join24(localDir, `${coreDirName}-docker-compose.yml`);
|
|
1636
|
+
const unprefixedCoreCompose = join24(localDir, "core-docker-compose.yml");
|
|
1558
1637
|
let coreComposePath;
|
|
1559
1638
|
try {
|
|
1560
1639
|
await access6(prefixedCoreCompose);
|
|
@@ -1564,7 +1643,7 @@ async function buildFullComposeArgs(layout, manifest, logger) {
|
|
|
1564
1643
|
}
|
|
1565
1644
|
const fileArgs = [
|
|
1566
1645
|
"-f",
|
|
1567
|
-
|
|
1646
|
+
join24(localDir, "platform-docker-compose.yml"),
|
|
1568
1647
|
"-f",
|
|
1569
1648
|
coreComposePath
|
|
1570
1649
|
];
|
|
@@ -1584,8 +1663,8 @@ async function buildSelectedComposeFiles(layout, selectedManifest, includeCore)
|
|
|
1584
1663
|
const platformName = selectedManifest.product.name;
|
|
1585
1664
|
const files = [];
|
|
1586
1665
|
if (includeCore) {
|
|
1587
|
-
const prefixedCoreCompose =
|
|
1588
|
-
const unprefixedCoreCompose =
|
|
1666
|
+
const prefixedCoreCompose = join24(localDir, `${coreDirName}-docker-compose.yml`);
|
|
1667
|
+
const unprefixedCoreCompose = join24(localDir, "core-docker-compose.yml");
|
|
1589
1668
|
let coreComposePath;
|
|
1590
1669
|
try {
|
|
1591
1670
|
await access6(prefixedCoreCompose);
|
|
@@ -1593,7 +1672,7 @@ async function buildSelectedComposeFiles(layout, selectedManifest, includeCore)
|
|
|
1593
1672
|
} catch {
|
|
1594
1673
|
coreComposePath = unprefixedCoreCompose;
|
|
1595
1674
|
}
|
|
1596
|
-
files.push(
|
|
1675
|
+
files.push(join24(localDir, "platform-docker-compose.yml"), coreComposePath);
|
|
1597
1676
|
}
|
|
1598
1677
|
const appEntries = await getAppComposePaths(localDir, coreDir, platformName, selectedManifest);
|
|
1599
1678
|
for (const { composePath } of appEntries) {
|
|
@@ -1637,7 +1716,7 @@ async function getServicesFromComposeFiles(selectedFiles, allFiles, rootDir) {
|
|
|
1637
1716
|
async function startEnvironment(layout, manifest, logger, signal, includeCore = true, fullManifest) {
|
|
1638
1717
|
const { rootDir, localDir } = layout;
|
|
1639
1718
|
const platformName = manifest.product.name;
|
|
1640
|
-
const envFile =
|
|
1719
|
+
const envFile = join24(localDir, ".env");
|
|
1641
1720
|
const isSelective = fullManifest !== void 0;
|
|
1642
1721
|
const fileArgs = await buildFullComposeArgs(layout, isSelective ? fullManifest : manifest, logger);
|
|
1643
1722
|
const projectArgs = ["-p", `${platformName}-platform`, "--project-directory", localDir, "--env-file", envFile];
|
|
@@ -1659,7 +1738,7 @@ async function startEnvironment(layout, manifest, logger, signal, includeCore =
|
|
|
1659
1738
|
async function stopEnvironment(layout, manifest, logger, signal, includeCore = true, fullManifest) {
|
|
1660
1739
|
const { rootDir, localDir } = layout;
|
|
1661
1740
|
const platformName = manifest.product.name;
|
|
1662
|
-
const envFile =
|
|
1741
|
+
const envFile = join24(localDir, ".env");
|
|
1663
1742
|
const isSelective = fullManifest !== void 0;
|
|
1664
1743
|
const fileArgs = await buildFullComposeArgs(layout, isSelective ? fullManifest : manifest, logger);
|
|
1665
1744
|
const projectArgs = ["-p", `${platformName}-platform`, "--project-directory", localDir, "--env-file", envFile];
|
|
@@ -1681,7 +1760,7 @@ async function stopEnvironment(layout, manifest, logger, signal, includeCore = t
|
|
|
1681
1760
|
async function resetEnvironment(layout, manifest, logger, signal) {
|
|
1682
1761
|
const { rootDir, localDir } = layout;
|
|
1683
1762
|
const platformName = manifest.product.name;
|
|
1684
|
-
const envFile =
|
|
1763
|
+
const envFile = join24(localDir, ".env");
|
|
1685
1764
|
const fileArgs = await buildFullComposeArgs(layout, manifest, logger);
|
|
1686
1765
|
const projectArgs = ["-p", `${platformName}-platform`, "--project-directory", localDir, "--env-file", envFile];
|
|
1687
1766
|
await runDockerCompose([...projectArgs, ...fileArgs, "down", "-v"], logger, rootDir, signal);
|
|
@@ -1689,7 +1768,7 @@ async function resetEnvironment(layout, manifest, logger, signal) {
|
|
|
1689
1768
|
async function destroyEnvironment(layout, manifest, logger, signal, includeCore = true, fullManifest, removeImages = true) {
|
|
1690
1769
|
const { rootDir, localDir } = layout;
|
|
1691
1770
|
const platformName = manifest.product.name;
|
|
1692
|
-
const envFile =
|
|
1771
|
+
const envFile = join24(localDir, ".env");
|
|
1693
1772
|
const isSelective = fullManifest !== void 0;
|
|
1694
1773
|
const fileArgs = await buildFullComposeArgs(layout, isSelective ? fullManifest : manifest, logger);
|
|
1695
1774
|
const projectArgs = ["-p", `${platformName}-platform`, "--project-directory", localDir, "--env-file", envFile];
|
|
@@ -1716,7 +1795,7 @@ async function destroyEnvironment(layout, manifest, logger, signal, includeCore
|
|
|
1716
1795
|
// src/commands/local-scripts/npm-orchestrator.ts
|
|
1717
1796
|
import { spawn as spawn2 } from "child_process";
|
|
1718
1797
|
import { access as access7 } from "fs/promises";
|
|
1719
|
-
import { resolve as resolve6, join as
|
|
1798
|
+
import { resolve as resolve6, join as join25 } from "path";
|
|
1720
1799
|
function runCommand(command, args2, workDir, logger, signal) {
|
|
1721
1800
|
return new Promise((resolvePromise) => {
|
|
1722
1801
|
const child = spawn2(command, args2, {
|
|
@@ -1773,7 +1852,7 @@ async function installDependencies(layout, manifest, logger, signal, includeCore
|
|
|
1773
1852
|
const { coreDir, coreDirName } = layout;
|
|
1774
1853
|
const appDirs = [];
|
|
1775
1854
|
for (const app of manifest.applications) {
|
|
1776
|
-
const appDir = resolve6(
|
|
1855
|
+
const appDir = resolve6(join25(coreDir), app.localPath);
|
|
1777
1856
|
if (!await dirExists(appDir)) {
|
|
1778
1857
|
logger.log(`Warning: Application directory "${app.name}" not found at ${appDir} \u2014 skipping install.`);
|
|
1779
1858
|
continue;
|
|
@@ -1799,7 +1878,7 @@ async function buildAll(layout, manifest, logger, signal, includeCore = true) {
|
|
|
1799
1878
|
}
|
|
1800
1879
|
const appDirs = [];
|
|
1801
1880
|
for (const app of manifest.applications) {
|
|
1802
|
-
const appDir = resolve6(
|
|
1881
|
+
const appDir = resolve6(join25(coreDir), app.localPath);
|
|
1803
1882
|
if (!await dirExists(appDir)) {
|
|
1804
1883
|
logger.log(`Warning: Application directory "${app.name}" not found at ${appDir} \u2014 skipping build.`);
|
|
1805
1884
|
continue;
|
|
@@ -1980,13 +2059,13 @@ async function checkInstalled(layout, manifest) {
|
|
|
1980
2059
|
const results = [];
|
|
1981
2060
|
const coreCheck = {
|
|
1982
2061
|
name: layout.coreDirName,
|
|
1983
|
-
path:
|
|
1984
|
-
exists: await pathExists(
|
|
2062
|
+
path: join26(layout.coreDir, "node_modules"),
|
|
2063
|
+
exists: await pathExists(join26(layout.coreDir, "node_modules"))
|
|
1985
2064
|
};
|
|
1986
2065
|
results.push(coreCheck);
|
|
1987
2066
|
for (const app of manifest.applications) {
|
|
1988
2067
|
const appDir = resolve7(layout.coreDir, app.localPath);
|
|
1989
|
-
const nodeModulesPath =
|
|
2068
|
+
const nodeModulesPath = join26(appDir, "node_modules");
|
|
1990
2069
|
results.push({
|
|
1991
2070
|
name: app.name,
|
|
1992
2071
|
path: nodeModulesPath,
|
|
@@ -1997,7 +2076,7 @@ async function checkInstalled(layout, manifest) {
|
|
|
1997
2076
|
}
|
|
1998
2077
|
async function checkBuilt(layout, manifest) {
|
|
1999
2078
|
const results = [];
|
|
2000
|
-
const coreTurboPath =
|
|
2079
|
+
const coreTurboPath = join26(layout.coreDir, ".turbo");
|
|
2001
2080
|
results.push({
|
|
2002
2081
|
name: layout.coreDirName,
|
|
2003
2082
|
path: coreTurboPath,
|
|
@@ -2005,7 +2084,7 @@ async function checkBuilt(layout, manifest) {
|
|
|
2005
2084
|
});
|
|
2006
2085
|
for (const app of manifest.applications) {
|
|
2007
2086
|
const appDir = resolve7(layout.coreDir, app.localPath);
|
|
2008
|
-
const appTurboPath =
|
|
2087
|
+
const appTurboPath = join26(appDir, ".turbo");
|
|
2009
2088
|
results.push({
|
|
2010
2089
|
name: app.name,
|
|
2011
2090
|
path: appTurboPath,
|
|
@@ -2017,13 +2096,13 @@ async function checkBuilt(layout, manifest) {
|
|
|
2017
2096
|
async function resolveComposeFiles(layout, manifest) {
|
|
2018
2097
|
const { localDir, coreDirName } = layout;
|
|
2019
2098
|
const platformName = manifest.product.name;
|
|
2020
|
-
const files = [
|
|
2021
|
-
const prefixedCore =
|
|
2022
|
-
const unprefixedCore =
|
|
2099
|
+
const files = [join26(localDir, "platform-docker-compose.yml")];
|
|
2100
|
+
const prefixedCore = join26(localDir, `${coreDirName}-docker-compose.yml`);
|
|
2101
|
+
const unprefixedCore = join26(localDir, "core-docker-compose.yml");
|
|
2023
2102
|
files.push(await pathExists(prefixedCore) ? prefixedCore : unprefixedCore);
|
|
2024
2103
|
for (const app of manifest.applications) {
|
|
2025
|
-
const prefixed =
|
|
2026
|
-
const unprefixed =
|
|
2104
|
+
const prefixed = join26(localDir, `${platformName}-${app.name}-docker-compose.yml`);
|
|
2105
|
+
const unprefixed = join26(localDir, `${app.name}-docker-compose.yml`);
|
|
2027
2106
|
if (await pathExists(prefixed)) files.push(prefixed);
|
|
2028
2107
|
else if (await pathExists(unprefixed)) files.push(unprefixed);
|
|
2029
2108
|
}
|
|
@@ -2129,15 +2208,15 @@ async function getServicesForComposeFiles(selectedFiles, allFiles, rootDir) {
|
|
|
2129
2208
|
async function checkContainersPerApp(layout, manifest, allContainers) {
|
|
2130
2209
|
const { localDir, coreDirName, rootDir } = layout;
|
|
2131
2210
|
const platformName = manifest.product.name;
|
|
2132
|
-
const prefixedCore =
|
|
2133
|
-
const unprefixedCore =
|
|
2211
|
+
const prefixedCore = join26(localDir, `${coreDirName}-docker-compose.yml`);
|
|
2212
|
+
const unprefixedCore = join26(localDir, "core-docker-compose.yml");
|
|
2134
2213
|
const coreComposePath = await pathExists(prefixedCore) ? prefixedCore : unprefixedCore;
|
|
2135
|
-
const platformComposePath =
|
|
2214
|
+
const platformComposePath = join26(localDir, "platform-docker-compose.yml");
|
|
2136
2215
|
const allFiles = [platformComposePath, coreComposePath];
|
|
2137
2216
|
const appComposeMap = /* @__PURE__ */ new Map();
|
|
2138
2217
|
for (const app of manifest.applications) {
|
|
2139
|
-
const prefixed =
|
|
2140
|
-
const unprefixed =
|
|
2218
|
+
const prefixed = join26(localDir, `${platformName}-${app.name}-docker-compose.yml`);
|
|
2219
|
+
const unprefixed = join26(localDir, `${app.name}-docker-compose.yml`);
|
|
2141
2220
|
if (await pathExists(prefixed)) {
|
|
2142
2221
|
appComposeMap.set(app.name, prefixed);
|
|
2143
2222
|
allFiles.push(prefixed);
|
|
@@ -2184,7 +2263,7 @@ async function checkContainersPerApp(layout, manifest, allContainers) {
|
|
|
2184
2263
|
return groups;
|
|
2185
2264
|
}
|
|
2186
2265
|
async function checkIdpProviders(layout) {
|
|
2187
|
-
const envPath =
|
|
2266
|
+
const envPath = join26(layout.localDir, ".env");
|
|
2188
2267
|
let env;
|
|
2189
2268
|
try {
|
|
2190
2269
|
env = await readEnvFile(envPath);
|
|
@@ -2303,7 +2382,7 @@ var statusCommand = {
|
|
|
2303
2382
|
};
|
|
2304
2383
|
|
|
2305
2384
|
// src/commands/manage-platform-admins/manage-platform-admins.command.ts
|
|
2306
|
-
import { join as
|
|
2385
|
+
import { join as join27 } from "path";
|
|
2307
2386
|
import { fetch as undiciFetch3, Agent as Agent3 } from "undici";
|
|
2308
2387
|
var MANAGE_PLATFORM_ADMINS_COMMAND_NAME = "manage-platform-admins";
|
|
2309
2388
|
var managePlatformAdminsCommand = {
|
|
@@ -2317,7 +2396,7 @@ async function getGatewayConfig(logger) {
|
|
|
2317
2396
|
logger.log("Error: Cannot manage platform admins \u2014 no platform initialized in this directory.");
|
|
2318
2397
|
return null;
|
|
2319
2398
|
}
|
|
2320
|
-
const envPath =
|
|
2399
|
+
const envPath = join27(layout.localDir, ".env");
|
|
2321
2400
|
let env;
|
|
2322
2401
|
try {
|
|
2323
2402
|
env = await readEnvFile(envPath);
|
|
@@ -2439,59 +2518,59 @@ async function removePlatformAdmin(ruleId, logger) {
|
|
|
2439
2518
|
var baPlatformPlugin = {
|
|
2440
2519
|
name: "ba-platform-plugin",
|
|
2441
2520
|
description: "Blue Alba Platform knowledge and CLI skills for AI assistants",
|
|
2442
|
-
version: "1.
|
|
2521
|
+
version: "1.2.0",
|
|
2443
2522
|
skills: [
|
|
2444
2523
|
{
|
|
2445
2524
|
name: "platform",
|
|
2446
|
-
description: "
|
|
2525
|
+
description: "Blue Alba Platform orchestrator \u2014 routes to the correct specialized skill or answers general architecture questions. Use this skill for ANY platform-related request.",
|
|
2447
2526
|
type: "skill",
|
|
2448
2527
|
sourceFile: "skills/ba-platform/platform.skill.md"
|
|
2449
2528
|
},
|
|
2450
2529
|
{
|
|
2451
2530
|
name: "platform-cli",
|
|
2452
|
-
description: "Blue Alba Platform CLI commands and
|
|
2531
|
+
description: "Blue Alba Platform CLI commands, usage, installation, and headless mode reference",
|
|
2453
2532
|
type: "skill",
|
|
2454
2533
|
sourceFile: "skills/ba-platform/platform-cli.skill.md"
|
|
2455
2534
|
},
|
|
2456
2535
|
{
|
|
2457
2536
|
name: "platform-add-operation",
|
|
2458
|
-
description: "
|
|
2537
|
+
description: "Adds an authorization operation (permission/RBAC guard) to a bootstrap service and enforces it in UI or backend routes",
|
|
2459
2538
|
type: "skill",
|
|
2460
2539
|
sourceFile: "skills/ba-platform/platform-add-operation.skill.md"
|
|
2461
2540
|
},
|
|
2462
2541
|
{
|
|
2463
2542
|
name: "platform-add-feature-flag",
|
|
2464
|
-
description: "Declares a feature flag in
|
|
2543
|
+
description: "Declares a feature flag in a bootstrap service and gates a React component or NestJS endpoint behind it",
|
|
2465
2544
|
type: "skill",
|
|
2466
2545
|
sourceFile: "skills/ba-platform/platform-add-feature-flag.skill.md"
|
|
2467
2546
|
},
|
|
2468
2547
|
{
|
|
2469
2548
|
name: "platform-add-scheduled-job",
|
|
2470
|
-
description: "Adds a scheduled job to a
|
|
2549
|
+
description: "Adds a cron-scheduled job to a bootstrap service to run a backend endpoint on a recurring schedule",
|
|
2471
2550
|
type: "skill",
|
|
2472
2551
|
sourceFile: "skills/ba-platform/platform-add-scheduled-job.skill.md"
|
|
2473
2552
|
},
|
|
2474
2553
|
{
|
|
2475
2554
|
name: "platform-add-menu-item",
|
|
2476
|
-
description: "
|
|
2555
|
+
description: "Creates a new page component and wires it as a menu item/route in a single-spa UI module",
|
|
2477
2556
|
type: "skill",
|
|
2478
2557
|
sourceFile: "skills/ba-platform/platform-add-menu-item.skill.md"
|
|
2479
2558
|
},
|
|
2480
2559
|
{
|
|
2481
2560
|
name: "platform-extend-shell",
|
|
2482
|
-
description: "Injects a custom component into a
|
|
2561
|
+
description: "Injects a custom component into a shell extension point (navbar, branding, user section)",
|
|
2483
2562
|
type: "skill",
|
|
2484
2563
|
sourceFile: "skills/ba-platform/platform-extend-shell.skill.md"
|
|
2485
2564
|
},
|
|
2486
2565
|
{
|
|
2487
2566
|
name: "platform-add-presence",
|
|
2488
|
-
description: "Adds real-time user presence
|
|
2567
|
+
description: "Adds real-time user presence (live avatars, who-is-viewing) to a component using the Rooms API",
|
|
2489
2568
|
type: "skill",
|
|
2490
2569
|
sourceFile: "skills/ba-platform/platform-add-presence.skill.md"
|
|
2491
2570
|
},
|
|
2492
2571
|
{
|
|
2493
2572
|
name: "platform-scaffold-module",
|
|
2494
|
-
description: "Scaffolds a new UI module or service module using the
|
|
2573
|
+
description: "Scaffolds a new UI module or NestJS service module in a platform monorepo using the CLI",
|
|
2495
2574
|
type: "skill",
|
|
2496
2575
|
sourceFile: "skills/ba-platform/platform-scaffold-module.skill.md"
|
|
2497
2576
|
}
|
|
@@ -3180,20 +3259,20 @@ async function installAiPluginService(params, context) {
|
|
|
3180
3259
|
|
|
3181
3260
|
// src/commands/install-ai-plugin/providers/claude.provider.ts
|
|
3182
3261
|
import { homedir as homedir3 } from "os";
|
|
3183
|
-
import { join as
|
|
3184
|
-
import { readFile as
|
|
3262
|
+
import { join as join28 } from "path";
|
|
3263
|
+
import { readFile as readFile12, writeFile as writeFile12, mkdir as mkdir4, rm } from "fs/promises";
|
|
3185
3264
|
var ClaudeProvider = class {
|
|
3186
3265
|
name = "claude";
|
|
3187
|
-
baseDir =
|
|
3266
|
+
baseDir = join28(homedir3(), ".claude");
|
|
3188
3267
|
getInstallPath(skillName) {
|
|
3189
|
-
return
|
|
3268
|
+
return join28(this.baseDir, "skills", skillName, "SKILL.md");
|
|
3190
3269
|
}
|
|
3191
3270
|
manifestPath(pluginName) {
|
|
3192
|
-
return
|
|
3271
|
+
return join28(this.baseDir, `${pluginName}.manifest.json`);
|
|
3193
3272
|
}
|
|
3194
3273
|
async readManifest(pluginName) {
|
|
3195
3274
|
try {
|
|
3196
|
-
const content = await
|
|
3275
|
+
const content = await readFile12(this.manifestPath(pluginName), "utf-8");
|
|
3197
3276
|
return JSON.parse(content);
|
|
3198
3277
|
} catch {
|
|
3199
3278
|
return null;
|
|
@@ -3201,7 +3280,7 @@ var ClaudeProvider = class {
|
|
|
3201
3280
|
}
|
|
3202
3281
|
async writeManifest(manifest) {
|
|
3203
3282
|
await mkdir4(this.baseDir, { recursive: true });
|
|
3204
|
-
await
|
|
3283
|
+
await writeFile12(
|
|
3205
3284
|
this.manifestPath(manifest.plugin),
|
|
3206
3285
|
JSON.stringify(manifest, null, 2),
|
|
3207
3286
|
"utf-8"
|
|
@@ -3221,16 +3300,16 @@ var ClaudeProvider = class {
|
|
|
3221
3300
|
}
|
|
3222
3301
|
async installSkill(skill, docsSource, logger) {
|
|
3223
3302
|
const sourcePath = docsSource.resolve(skill.sourceFile);
|
|
3224
|
-
let content = await
|
|
3303
|
+
let content = await readFile12(sourcePath, "utf-8");
|
|
3225
3304
|
const docsPath = docsSource.resolve("docs");
|
|
3226
3305
|
content = content.replaceAll("{{docsPath}}", docsPath);
|
|
3227
3306
|
const installPath = this.getInstallPath(skill.name);
|
|
3228
|
-
await mkdir4(
|
|
3229
|
-
await
|
|
3307
|
+
await mkdir4(join28(installPath, ".."), { recursive: true });
|
|
3308
|
+
await writeFile12(installPath, content, "utf-8");
|
|
3230
3309
|
logger.log(` Installed skill: ${installPath}`);
|
|
3231
3310
|
}
|
|
3232
3311
|
async removeSkill(skillName, logger) {
|
|
3233
|
-
const skillDir =
|
|
3312
|
+
const skillDir = join28(this.baseDir, "skills", skillName);
|
|
3234
3313
|
try {
|
|
3235
3314
|
await rm(skillDir, { recursive: true, force: true });
|
|
3236
3315
|
logger.log(` Removed skill: ${skillDir}`);
|
|
@@ -3238,11 +3317,11 @@ var ClaudeProvider = class {
|
|
|
3238
3317
|
}
|
|
3239
3318
|
}
|
|
3240
3319
|
async updatePermissions(docsSource, logger) {
|
|
3241
|
-
const settingsPath =
|
|
3320
|
+
const settingsPath = join28(this.baseDir, "settings.json");
|
|
3242
3321
|
const docsPath = docsSource.resolve("docs");
|
|
3243
3322
|
let settings = {};
|
|
3244
3323
|
try {
|
|
3245
|
-
const content = await
|
|
3324
|
+
const content = await readFile12(settingsPath, "utf-8");
|
|
3246
3325
|
settings = JSON.parse(content);
|
|
3247
3326
|
} catch {
|
|
3248
3327
|
}
|
|
@@ -3254,7 +3333,7 @@ var ClaudeProvider = class {
|
|
|
3254
3333
|
permissions.additionalDirectories = [...additionalDirectories, docsPath];
|
|
3255
3334
|
settings.permissions = permissions;
|
|
3256
3335
|
await mkdir4(this.baseDir, { recursive: true });
|
|
3257
|
-
await
|
|
3336
|
+
await writeFile12(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
3258
3337
|
logger.log(` Granted read access to docs: ${docsPath}`);
|
|
3259
3338
|
}
|
|
3260
3339
|
};
|
|
@@ -3274,11 +3353,11 @@ function getProvider(name) {
|
|
|
3274
3353
|
|
|
3275
3354
|
// src/commands/install-ai-plugin/docs-source/local.docs-source.ts
|
|
3276
3355
|
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
3277
|
-
import { join as
|
|
3278
|
-
var packageRoot =
|
|
3356
|
+
import { join as join29, dirname as dirname13 } from "path";
|
|
3357
|
+
var packageRoot = join29(dirname13(fileURLToPath10(import.meta.url)), "..");
|
|
3279
3358
|
var LocalDocsSource = class {
|
|
3280
3359
|
resolve(relativePath) {
|
|
3281
|
-
return
|
|
3360
|
+
return join29(packageRoot, relativePath);
|
|
3282
3361
|
}
|
|
3283
3362
|
};
|
|
3284
3363
|
|
|
@@ -3322,17 +3401,17 @@ async function installAiPluginUiController(ctx) {
|
|
|
3322
3401
|
|
|
3323
3402
|
// src/controllers/ui/standalone.ui-controller.ts
|
|
3324
3403
|
import { rm as rm3 } from "fs/promises";
|
|
3325
|
-
import { join as
|
|
3404
|
+
import { join as join33 } from "path";
|
|
3326
3405
|
import { cwd as cwd7 } from "process";
|
|
3327
3406
|
import { exec } from "child_process";
|
|
3328
3407
|
|
|
3329
3408
|
// src/commands/standalone/app-monorepo-check.ts
|
|
3330
3409
|
import { access as access9, readdir as readdir5 } from "fs/promises";
|
|
3331
|
-
import { join as
|
|
3410
|
+
import { join as join30 } from "path";
|
|
3332
3411
|
import { cwd as cwd6 } from "process";
|
|
3333
3412
|
async function findAppMonorepoLayout(startDir = cwd6()) {
|
|
3334
|
-
const composePath =
|
|
3335
|
-
const packageJsonPath =
|
|
3413
|
+
const composePath = join30(startDir, "docker-compose.yml");
|
|
3414
|
+
const packageJsonPath = join30(startDir, "package.json");
|
|
3336
3415
|
try {
|
|
3337
3416
|
await access9(composePath);
|
|
3338
3417
|
await access9(packageJsonPath);
|
|
@@ -3350,7 +3429,7 @@ async function hasCoreChildDir(dir) {
|
|
|
3350
3429
|
for (const entry of entries) {
|
|
3351
3430
|
if (entry.isDirectory() && (entry.name.endsWith("-core") || entry.name === "core")) {
|
|
3352
3431
|
try {
|
|
3353
|
-
await access9(
|
|
3432
|
+
await access9(join30(dir, entry.name, "product.manifest.json"));
|
|
3354
3433
|
return true;
|
|
3355
3434
|
} catch {
|
|
3356
3435
|
}
|
|
@@ -3365,15 +3444,15 @@ async function isInAppMonorepo() {
|
|
|
3365
3444
|
}
|
|
3366
3445
|
|
|
3367
3446
|
// src/commands/standalone/standalone-auto-detect.ts
|
|
3368
|
-
import { readdir as readdir6, readFile as
|
|
3369
|
-
import { join as
|
|
3447
|
+
import { readdir as readdir6, readFile as readFile13 } from "fs/promises";
|
|
3448
|
+
import { join as join31, basename as basename2 } from "path";
|
|
3370
3449
|
async function autoDetectAppIdentity(appDir) {
|
|
3371
3450
|
const fromBootstrap = await detectFromBootstrap(appDir);
|
|
3372
3451
|
if (fromBootstrap) return fromBootstrap;
|
|
3373
3452
|
return detectFromPackageJson(appDir);
|
|
3374
3453
|
}
|
|
3375
3454
|
async function detectFromBootstrap(appDir) {
|
|
3376
|
-
const servicesDir =
|
|
3455
|
+
const servicesDir = join31(appDir, "services");
|
|
3377
3456
|
let entries;
|
|
3378
3457
|
try {
|
|
3379
3458
|
entries = await readdir6(servicesDir);
|
|
@@ -3383,7 +3462,7 @@ async function detectFromBootstrap(appDir) {
|
|
|
3383
3462
|
const bootstrapDirs = entries.filter((e) => e.endsWith("-bootstrap-service"));
|
|
3384
3463
|
if (bootstrapDirs.length === 0) return null;
|
|
3385
3464
|
for (const bootstrapDirName of bootstrapDirs) {
|
|
3386
|
-
const dataDir =
|
|
3465
|
+
const dataDir = join31(servicesDir, bootstrapDirName, "src", "data");
|
|
3387
3466
|
let dataDirEntries;
|
|
3388
3467
|
try {
|
|
3389
3468
|
dataDirEntries = await readdir6(dataDir);
|
|
@@ -3392,9 +3471,9 @@ async function detectFromBootstrap(appDir) {
|
|
|
3392
3471
|
}
|
|
3393
3472
|
for (const subDir of dataDirEntries) {
|
|
3394
3473
|
if (subDir === "platform") continue;
|
|
3395
|
-
const appJsonPath =
|
|
3474
|
+
const appJsonPath = join31(dataDir, subDir, "application.json");
|
|
3396
3475
|
try {
|
|
3397
|
-
const content = await
|
|
3476
|
+
const content = await readFile13(appJsonPath, "utf-8");
|
|
3398
3477
|
const appData = JSON.parse(content);
|
|
3399
3478
|
if (!appData.name) continue;
|
|
3400
3479
|
const applicationName = appData.name;
|
|
@@ -3412,7 +3491,7 @@ async function detectFromBootstrap(appDir) {
|
|
|
3412
3491
|
}
|
|
3413
3492
|
async function detectFromPackageJson(appDir) {
|
|
3414
3493
|
try {
|
|
3415
|
-
const content = await
|
|
3494
|
+
const content = await readFile13(join31(appDir, "package.json"), "utf-8");
|
|
3416
3495
|
const pkg = JSON.parse(content);
|
|
3417
3496
|
const rawName = pkg.name ?? "";
|
|
3418
3497
|
const applicationName = rawName.includes("/") ? rawName.split("/")[1] ?? rawName : rawName;
|
|
@@ -3435,23 +3514,24 @@ function inferPlatformNameFromAppDir(appDir, applicationName) {
|
|
|
3435
3514
|
}
|
|
3436
3515
|
|
|
3437
3516
|
// src/commands/standalone/standalone-orchestrator.ts
|
|
3438
|
-
import { join as
|
|
3439
|
-
import { mkdir as mkdir5, rm as rm2, readdir as readdir7, access as access10, writeFile as
|
|
3517
|
+
import { join as join32, dirname as dirname14 } from "path";
|
|
3518
|
+
import { mkdir as mkdir5, rm as rm2, readdir as readdir7, access as access10, writeFile as writeFile13, readFile as readFile14, open } from "fs/promises";
|
|
3519
|
+
import { constants } from "fs";
|
|
3440
3520
|
import { fetch as undiciFetch4, Agent as Agent4 } from "undici";
|
|
3441
3521
|
var TMP_BASE = "/tmp";
|
|
3442
3522
|
var STANDALONE_PREFIX = "platform-standalone-";
|
|
3443
3523
|
var DETACH_MARKER = ".standalone-running";
|
|
3444
3524
|
function getTmpDir(platformName) {
|
|
3445
|
-
return
|
|
3525
|
+
return join32(TMP_BASE, `${STANDALONE_PREFIX}${platformName}`);
|
|
3446
3526
|
}
|
|
3447
3527
|
async function findRunningSingleton() {
|
|
3448
3528
|
try {
|
|
3449
3529
|
const entries = await readdir7(TMP_BASE, { withFileTypes: true });
|
|
3450
3530
|
for (const entry of entries) {
|
|
3451
3531
|
if (entry.isDirectory() && entry.name.startsWith(STANDALONE_PREFIX)) {
|
|
3452
|
-
const candidateDir =
|
|
3532
|
+
const candidateDir = join32(TMP_BASE, entry.name);
|
|
3453
3533
|
try {
|
|
3454
|
-
await access10(
|
|
3534
|
+
await access10(join32(candidateDir, DETACH_MARKER));
|
|
3455
3535
|
return candidateDir;
|
|
3456
3536
|
} catch {
|
|
3457
3537
|
}
|
|
@@ -3495,7 +3575,7 @@ async function configureIdpFromConfig(config, localDir, logger) {
|
|
|
3495
3575
|
logger.log("No IDP configured \u2014 run 'platform standalone-config set' to add an IDP. Skipping IDP setup.");
|
|
3496
3576
|
return;
|
|
3497
3577
|
}
|
|
3498
|
-
const envPath =
|
|
3578
|
+
const envPath = join32(localDir, ".env");
|
|
3499
3579
|
let env;
|
|
3500
3580
|
try {
|
|
3501
3581
|
env = await readEnvFile(envPath);
|
|
@@ -3550,7 +3630,7 @@ async function configureAdminUsersFromConfig(config, localDir, logger) {
|
|
|
3550
3630
|
if (!config.adminUsers || config.adminUsers.length === 0) {
|
|
3551
3631
|
return;
|
|
3552
3632
|
}
|
|
3553
|
-
const envPath =
|
|
3633
|
+
const envPath = join32(localDir, ".env");
|
|
3554
3634
|
let env;
|
|
3555
3635
|
try {
|
|
3556
3636
|
env = await readEnvFile(envPath);
|
|
@@ -3593,19 +3673,19 @@ async function configureAdminUsersFromConfig(config, localDir, logger) {
|
|
|
3593
3673
|
}
|
|
3594
3674
|
}
|
|
3595
3675
|
function buildLayout(tmpDir, coreDirName) {
|
|
3596
|
-
const coreDir =
|
|
3676
|
+
const coreDir = join32(tmpDir, coreDirName);
|
|
3597
3677
|
return {
|
|
3598
3678
|
rootDir: tmpDir,
|
|
3599
3679
|
coreDir,
|
|
3600
3680
|
coreDirName,
|
|
3601
|
-
localDir:
|
|
3681
|
+
localDir: join32(coreDir, "local")
|
|
3602
3682
|
};
|
|
3603
3683
|
}
|
|
3604
|
-
async function
|
|
3605
|
-
const appComposePath =
|
|
3684
|
+
async function generateProxyAppCompose2(appDir, localDir, platformName, applicationName, logger) {
|
|
3685
|
+
const appComposePath = join32(appDir, "docker-compose.yml");
|
|
3606
3686
|
let content;
|
|
3607
3687
|
try {
|
|
3608
|
-
content = await
|
|
3688
|
+
content = await readFile14(appComposePath, "utf-8");
|
|
3609
3689
|
} catch {
|
|
3610
3690
|
logger.log(`Warning: No docker-compose.yml found at ${appComposePath} \u2014 app services won't start.`);
|
|
3611
3691
|
return;
|
|
@@ -3614,10 +3694,10 @@ async function generateProxyAppCompose(appDir, localDir, platformName, applicati
|
|
|
3614
3694
|
/^(\s+context:\s+)\.\/(.*)$/gm,
|
|
3615
3695
|
`$1${appDir}/$2`
|
|
3616
3696
|
);
|
|
3617
|
-
const appParentDir =
|
|
3697
|
+
const appParentDir = dirname14(appDir);
|
|
3618
3698
|
const rewritten = withAbsContexts.replace(/\$\{PWD\}/g, appParentDir);
|
|
3619
|
-
const proxyPath =
|
|
3620
|
-
await
|
|
3699
|
+
const proxyPath = join32(localDir, `${platformName}-${applicationName}-docker-compose.yml`);
|
|
3700
|
+
await writeFile13(proxyPath, rewritten, "utf-8");
|
|
3621
3701
|
logger.log(`Generated app compose with absolute build paths.`);
|
|
3622
3702
|
}
|
|
3623
3703
|
function buildComposeManifest(manifest) {
|
|
@@ -3643,7 +3723,23 @@ async function startStandalone(config, appDir, logger, signal, detach = false) {
|
|
|
3643
3723
|
}
|
|
3644
3724
|
logger.log(`Starting standalone platform for "${applicationDisplayName}"...`);
|
|
3645
3725
|
await mkdir5(tmpDir, { recursive: true });
|
|
3646
|
-
|
|
3726
|
+
let markerFd;
|
|
3727
|
+
try {
|
|
3728
|
+
markerFd = await open(
|
|
3729
|
+
join32(tmpDir, DETACH_MARKER),
|
|
3730
|
+
constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY
|
|
3731
|
+
);
|
|
3732
|
+
await markerFd.writeFile(platformName, "utf-8");
|
|
3733
|
+
} catch (err) {
|
|
3734
|
+
if (err.code === "EEXIST") {
|
|
3735
|
+
logger.log(`Error: A standalone platform is already starting at ${tmpDir}.`);
|
|
3736
|
+
logger.log(`Run "platform standalone-stop" to stop it before starting a new one.`);
|
|
3737
|
+
return;
|
|
3738
|
+
}
|
|
3739
|
+
throw err;
|
|
3740
|
+
} finally {
|
|
3741
|
+
await markerFd?.close();
|
|
3742
|
+
}
|
|
3647
3743
|
const variables = {
|
|
3648
3744
|
organizationName,
|
|
3649
3745
|
platformName,
|
|
@@ -3668,9 +3764,9 @@ async function startStandalone(config, appDir, logger, signal, detach = false) {
|
|
|
3668
3764
|
}
|
|
3669
3765
|
);
|
|
3670
3766
|
await writeManifest(npmManifest, tmpDir, coreDirName);
|
|
3671
|
-
const bootstrapServiceDir =
|
|
3767
|
+
const bootstrapServiceDir = join32(tmpDir, coreDirName, "services", bootstrapServiceName);
|
|
3672
3768
|
await scaffoldPlatformBootstrap(bootstrapServiceDir, variables, logger);
|
|
3673
|
-
const customizationUiDir =
|
|
3769
|
+
const customizationUiDir = join32(tmpDir, coreDirName, "ui", customizationUiName);
|
|
3674
3770
|
await scaffoldCustomizationUi(customizationUiDir, variables, logger);
|
|
3675
3771
|
await registerCustomizationModule(bootstrapServiceDir, organizationName, logger, platformName);
|
|
3676
3772
|
const layout = buildLayout(tmpDir, coreDirName);
|
|
@@ -3680,7 +3776,7 @@ async function startStandalone(config, appDir, logger, signal, detach = false) {
|
|
|
3680
3776
|
logger.log("Building...");
|
|
3681
3777
|
await buildAll(layout, npmManifest, logger, signal, true);
|
|
3682
3778
|
if (signal?.aborted) return;
|
|
3683
|
-
await
|
|
3779
|
+
await generateProxyAppCompose2(appDir, layout.localDir, platformName, applicationName, logger);
|
|
3684
3780
|
const composeManifest = buildComposeManifest(npmManifest);
|
|
3685
3781
|
await writeManifest(composeManifest, tmpDir, coreDirName);
|
|
3686
3782
|
logger.log("Resetting previous environment (removing stale containers and volumes)...");
|
|
@@ -3689,7 +3785,7 @@ async function startStandalone(config, appDir, logger, signal, detach = false) {
|
|
|
3689
3785
|
logger.log("Starting containers...");
|
|
3690
3786
|
await startEnvironment(layout, composeManifest, logger, signal);
|
|
3691
3787
|
if (signal?.aborted) return;
|
|
3692
|
-
const env = await readEnvFile(
|
|
3788
|
+
const env = await readEnvFile(join32(layout.localDir, ".env")).catch(() => /* @__PURE__ */ new Map());
|
|
3693
3789
|
const gatewayUrl = env.get("PAE_GATEWAY_HOST_URL") ?? "https://localhost:443";
|
|
3694
3790
|
const gatewayReady = await waitForGateway(gatewayUrl, logger);
|
|
3695
3791
|
if (!gatewayReady) {
|
|
@@ -3709,16 +3805,27 @@ Standalone platform is running in the background.`);
|
|
|
3709
3805
|
logger.log("\nPlatform is running. Press Ctrl+C to stop and clean up.");
|
|
3710
3806
|
const keepAlive = setInterval(() => {
|
|
3711
3807
|
}, 60 * 6e4);
|
|
3808
|
+
let cleanupInProgress = false;
|
|
3712
3809
|
const shutdown = () => {
|
|
3810
|
+
if (cleanupInProgress) {
|
|
3811
|
+
logger.log("\nForce exiting. Containers may need manual cleanup:");
|
|
3812
|
+
logger.log(` docker compose -p ${platformName}-platform down -v --remove-orphans`);
|
|
3813
|
+
process.exit(1);
|
|
3814
|
+
}
|
|
3815
|
+
cleanupInProgress = true;
|
|
3713
3816
|
clearInterval(keepAlive);
|
|
3714
|
-
|
|
3817
|
+
(async () => {
|
|
3715
3818
|
logger.log("\nShutting down standalone platform...");
|
|
3716
3819
|
await destroyEnvironment(layout, composeManifest, logger);
|
|
3717
3820
|
await cleanupTmpDir(tmpDir, logger);
|
|
3718
|
-
|
|
3821
|
+
process.exit(0);
|
|
3822
|
+
})().catch((err) => {
|
|
3823
|
+
logger.log(`Error during cleanup: ${err.message}`);
|
|
3824
|
+
process.exit(1);
|
|
3825
|
+
});
|
|
3719
3826
|
};
|
|
3720
|
-
process.
|
|
3721
|
-
process.
|
|
3827
|
+
process.on("SIGINT", shutdown);
|
|
3828
|
+
process.on("SIGTERM", shutdown);
|
|
3722
3829
|
}
|
|
3723
3830
|
async function stopStandalone(logger) {
|
|
3724
3831
|
const runningDir = await findRunningSingleton();
|
|
@@ -3732,7 +3839,7 @@ async function stopStandalone(logger) {
|
|
|
3732
3839
|
for (const entry of entries) {
|
|
3733
3840
|
if (entry.isDirectory() && entry.name.endsWith("-core")) {
|
|
3734
3841
|
try {
|
|
3735
|
-
await access10(
|
|
3842
|
+
await access10(join32(runningDir, entry.name, "product.manifest.json"));
|
|
3736
3843
|
coreDirName = entry.name;
|
|
3737
3844
|
break;
|
|
3738
3845
|
} catch {
|
|
@@ -3847,7 +3954,7 @@ async function standaloneUiController(ctx) {
|
|
|
3847
3954
|
const deleteLegacy = await ctx.confirm("Delete the local standalone.json?", true);
|
|
3848
3955
|
if (deleteLegacy) {
|
|
3849
3956
|
try {
|
|
3850
|
-
await rm3(
|
|
3957
|
+
await rm3(join33(appDir, "standalone.json"));
|
|
3851
3958
|
ctx.log("Deleted standalone.json.");
|
|
3852
3959
|
} catch {
|
|
3853
3960
|
ctx.log("Warning: Could not delete standalone.json.");
|
|
@@ -3979,7 +4086,7 @@ async function standaloneConfigShowUiController(ctx) {
|
|
|
3979
4086
|
if (config) {
|
|
3980
4087
|
ctx.log(`Standalone config for ${detected.platformName}/${detected.applicationName}:
|
|
3981
4088
|
`);
|
|
3982
|
-
ctx.log(JSON.stringify(config, null, 2));
|
|
4089
|
+
ctx.log(JSON.stringify(maskSensitiveFields(config), null, 2));
|
|
3983
4090
|
} else {
|
|
3984
4091
|
ctx.log(`No config found for ${detected.platformName}/${detected.applicationName}.`);
|
|
3985
4092
|
ctx.log(`Run 'platform standalone' or 'platform standalone-config set' to configure.`);
|
|
@@ -3998,7 +4105,7 @@ async function showAll(ctx) {
|
|
|
3998
4105
|
if (Object.keys(defaults).length === 0) {
|
|
3999
4106
|
ctx.log("(none)");
|
|
4000
4107
|
} else {
|
|
4001
|
-
ctx.log(JSON.stringify(defaults, null, 2));
|
|
4108
|
+
ctx.log(JSON.stringify(maskSensitiveFields(defaults), null, 2));
|
|
4002
4109
|
}
|
|
4003
4110
|
ctx.log("\n--- Apps ---");
|
|
4004
4111
|
if (apps.length === 0) {
|
|
@@ -4904,7 +5011,7 @@ async function standaloneConfigShowCliController(args2) {
|
|
|
4904
5011
|
const [pn, an] = parts;
|
|
4905
5012
|
const config = await resolveAppConfig(pn, an);
|
|
4906
5013
|
if (config) {
|
|
4907
|
-
console.log(JSON.stringify(config, null, 2));
|
|
5014
|
+
console.log(JSON.stringify(maskSensitiveFields(config), null, 2));
|
|
4908
5015
|
} else {
|
|
4909
5016
|
console.error(`No config found for ${appArg}.`);
|
|
4910
5017
|
process.exit(1);
|
|
@@ -4915,12 +5022,12 @@ async function standaloneConfigShowCliController(args2) {
|
|
|
4915
5022
|
if (detected) {
|
|
4916
5023
|
const config = await resolveAppConfig(detected.platformName, detected.applicationName);
|
|
4917
5024
|
if (config) {
|
|
4918
|
-
console.log(JSON.stringify(config, null, 2));
|
|
5025
|
+
console.log(JSON.stringify(maskSensitiveFields(config), null, 2));
|
|
4919
5026
|
return;
|
|
4920
5027
|
}
|
|
4921
5028
|
}
|
|
4922
5029
|
const cache = await readCacheFile();
|
|
4923
|
-
console.log(JSON.stringify(cache, null, 2));
|
|
5030
|
+
console.log(JSON.stringify(maskSensitiveFields(cache), null, 2));
|
|
4924
5031
|
}
|
|
4925
5032
|
async function standaloneConfigSetCliController(args2) {
|
|
4926
5033
|
if (args2["defaults"] === "true") {
|