@bluealba/platform-cli 1.2.0-alpha.1 → 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 +299 -150
- package/package.json +1 -1
- package/skills/ba-platform/platform-add-feature-flag.skill.md +110 -0
- package/skills/ba-platform/platform-add-menu-item.skill.md +80 -0
- package/skills/ba-platform/platform-add-operation.skill.md +103 -0
- package/skills/ba-platform/platform-add-presence.skill.md +84 -0
- package/skills/ba-platform/platform-add-scheduled-job.skill.md +65 -0
- package/skills/ba-platform/platform-cli.skill.md +5 -0
- package/skills/ba-platform/platform-extend-shell.skill.md +78 -0
- package/skills/ba-platform/platform-scaffold-module.skill.md +70 -0
- package/skills/ba-platform/platform.skill.md +67 -16
- 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,19 +2518,61 @@ 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"
|
|
2534
|
+
},
|
|
2535
|
+
{
|
|
2536
|
+
name: "platform-add-operation",
|
|
2537
|
+
description: "Adds an authorization operation (permission/RBAC guard) to a bootstrap service and enforces it in UI or backend routes",
|
|
2538
|
+
type: "skill",
|
|
2539
|
+
sourceFile: "skills/ba-platform/platform-add-operation.skill.md"
|
|
2540
|
+
},
|
|
2541
|
+
{
|
|
2542
|
+
name: "platform-add-feature-flag",
|
|
2543
|
+
description: "Declares a feature flag in a bootstrap service and gates a React component or NestJS endpoint behind it",
|
|
2544
|
+
type: "skill",
|
|
2545
|
+
sourceFile: "skills/ba-platform/platform-add-feature-flag.skill.md"
|
|
2546
|
+
},
|
|
2547
|
+
{
|
|
2548
|
+
name: "platform-add-scheduled-job",
|
|
2549
|
+
description: "Adds a cron-scheduled job to a bootstrap service to run a backend endpoint on a recurring schedule",
|
|
2550
|
+
type: "skill",
|
|
2551
|
+
sourceFile: "skills/ba-platform/platform-add-scheduled-job.skill.md"
|
|
2552
|
+
},
|
|
2553
|
+
{
|
|
2554
|
+
name: "platform-add-menu-item",
|
|
2555
|
+
description: "Creates a new page component and wires it as a menu item/route in a single-spa UI module",
|
|
2556
|
+
type: "skill",
|
|
2557
|
+
sourceFile: "skills/ba-platform/platform-add-menu-item.skill.md"
|
|
2558
|
+
},
|
|
2559
|
+
{
|
|
2560
|
+
name: "platform-extend-shell",
|
|
2561
|
+
description: "Injects a custom component into a shell extension point (navbar, branding, user section)",
|
|
2562
|
+
type: "skill",
|
|
2563
|
+
sourceFile: "skills/ba-platform/platform-extend-shell.skill.md"
|
|
2564
|
+
},
|
|
2565
|
+
{
|
|
2566
|
+
name: "platform-add-presence",
|
|
2567
|
+
description: "Adds real-time user presence (live avatars, who-is-viewing) to a component using the Rooms API",
|
|
2568
|
+
type: "skill",
|
|
2569
|
+
sourceFile: "skills/ba-platform/platform-add-presence.skill.md"
|
|
2570
|
+
},
|
|
2571
|
+
{
|
|
2572
|
+
name: "platform-scaffold-module",
|
|
2573
|
+
description: "Scaffolds a new UI module or NestJS service module in a platform monorepo using the CLI",
|
|
2574
|
+
type: "skill",
|
|
2575
|
+
sourceFile: "skills/ba-platform/platform-scaffold-module.skill.md"
|
|
2455
2576
|
}
|
|
2456
2577
|
]
|
|
2457
2578
|
};
|
|
@@ -3138,20 +3259,20 @@ async function installAiPluginService(params, context) {
|
|
|
3138
3259
|
|
|
3139
3260
|
// src/commands/install-ai-plugin/providers/claude.provider.ts
|
|
3140
3261
|
import { homedir as homedir3 } from "os";
|
|
3141
|
-
import { join as
|
|
3142
|
-
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";
|
|
3143
3264
|
var ClaudeProvider = class {
|
|
3144
3265
|
name = "claude";
|
|
3145
|
-
baseDir =
|
|
3266
|
+
baseDir = join28(homedir3(), ".claude");
|
|
3146
3267
|
getInstallPath(skillName) {
|
|
3147
|
-
return
|
|
3268
|
+
return join28(this.baseDir, "skills", skillName, "SKILL.md");
|
|
3148
3269
|
}
|
|
3149
3270
|
manifestPath(pluginName) {
|
|
3150
|
-
return
|
|
3271
|
+
return join28(this.baseDir, `${pluginName}.manifest.json`);
|
|
3151
3272
|
}
|
|
3152
3273
|
async readManifest(pluginName) {
|
|
3153
3274
|
try {
|
|
3154
|
-
const content = await
|
|
3275
|
+
const content = await readFile12(this.manifestPath(pluginName), "utf-8");
|
|
3155
3276
|
return JSON.parse(content);
|
|
3156
3277
|
} catch {
|
|
3157
3278
|
return null;
|
|
@@ -3159,7 +3280,7 @@ var ClaudeProvider = class {
|
|
|
3159
3280
|
}
|
|
3160
3281
|
async writeManifest(manifest) {
|
|
3161
3282
|
await mkdir4(this.baseDir, { recursive: true });
|
|
3162
|
-
await
|
|
3283
|
+
await writeFile12(
|
|
3163
3284
|
this.manifestPath(manifest.plugin),
|
|
3164
3285
|
JSON.stringify(manifest, null, 2),
|
|
3165
3286
|
"utf-8"
|
|
@@ -3179,16 +3300,16 @@ var ClaudeProvider = class {
|
|
|
3179
3300
|
}
|
|
3180
3301
|
async installSkill(skill, docsSource, logger) {
|
|
3181
3302
|
const sourcePath = docsSource.resolve(skill.sourceFile);
|
|
3182
|
-
let content = await
|
|
3303
|
+
let content = await readFile12(sourcePath, "utf-8");
|
|
3183
3304
|
const docsPath = docsSource.resolve("docs");
|
|
3184
3305
|
content = content.replaceAll("{{docsPath}}", docsPath);
|
|
3185
3306
|
const installPath = this.getInstallPath(skill.name);
|
|
3186
|
-
await mkdir4(
|
|
3187
|
-
await
|
|
3307
|
+
await mkdir4(join28(installPath, ".."), { recursive: true });
|
|
3308
|
+
await writeFile12(installPath, content, "utf-8");
|
|
3188
3309
|
logger.log(` Installed skill: ${installPath}`);
|
|
3189
3310
|
}
|
|
3190
3311
|
async removeSkill(skillName, logger) {
|
|
3191
|
-
const skillDir =
|
|
3312
|
+
const skillDir = join28(this.baseDir, "skills", skillName);
|
|
3192
3313
|
try {
|
|
3193
3314
|
await rm(skillDir, { recursive: true, force: true });
|
|
3194
3315
|
logger.log(` Removed skill: ${skillDir}`);
|
|
@@ -3196,11 +3317,11 @@ var ClaudeProvider = class {
|
|
|
3196
3317
|
}
|
|
3197
3318
|
}
|
|
3198
3319
|
async updatePermissions(docsSource, logger) {
|
|
3199
|
-
const settingsPath =
|
|
3320
|
+
const settingsPath = join28(this.baseDir, "settings.json");
|
|
3200
3321
|
const docsPath = docsSource.resolve("docs");
|
|
3201
3322
|
let settings = {};
|
|
3202
3323
|
try {
|
|
3203
|
-
const content = await
|
|
3324
|
+
const content = await readFile12(settingsPath, "utf-8");
|
|
3204
3325
|
settings = JSON.parse(content);
|
|
3205
3326
|
} catch {
|
|
3206
3327
|
}
|
|
@@ -3212,7 +3333,7 @@ var ClaudeProvider = class {
|
|
|
3212
3333
|
permissions.additionalDirectories = [...additionalDirectories, docsPath];
|
|
3213
3334
|
settings.permissions = permissions;
|
|
3214
3335
|
await mkdir4(this.baseDir, { recursive: true });
|
|
3215
|
-
await
|
|
3336
|
+
await writeFile12(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
3216
3337
|
logger.log(` Granted read access to docs: ${docsPath}`);
|
|
3217
3338
|
}
|
|
3218
3339
|
};
|
|
@@ -3232,11 +3353,11 @@ function getProvider(name) {
|
|
|
3232
3353
|
|
|
3233
3354
|
// src/commands/install-ai-plugin/docs-source/local.docs-source.ts
|
|
3234
3355
|
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
3235
|
-
import { join as
|
|
3236
|
-
var packageRoot =
|
|
3356
|
+
import { join as join29, dirname as dirname13 } from "path";
|
|
3357
|
+
var packageRoot = join29(dirname13(fileURLToPath10(import.meta.url)), "..");
|
|
3237
3358
|
var LocalDocsSource = class {
|
|
3238
3359
|
resolve(relativePath) {
|
|
3239
|
-
return
|
|
3360
|
+
return join29(packageRoot, relativePath);
|
|
3240
3361
|
}
|
|
3241
3362
|
};
|
|
3242
3363
|
|
|
@@ -3280,17 +3401,17 @@ async function installAiPluginUiController(ctx) {
|
|
|
3280
3401
|
|
|
3281
3402
|
// src/controllers/ui/standalone.ui-controller.ts
|
|
3282
3403
|
import { rm as rm3 } from "fs/promises";
|
|
3283
|
-
import { join as
|
|
3404
|
+
import { join as join33 } from "path";
|
|
3284
3405
|
import { cwd as cwd7 } from "process";
|
|
3285
3406
|
import { exec } from "child_process";
|
|
3286
3407
|
|
|
3287
3408
|
// src/commands/standalone/app-monorepo-check.ts
|
|
3288
3409
|
import { access as access9, readdir as readdir5 } from "fs/promises";
|
|
3289
|
-
import { join as
|
|
3410
|
+
import { join as join30 } from "path";
|
|
3290
3411
|
import { cwd as cwd6 } from "process";
|
|
3291
3412
|
async function findAppMonorepoLayout(startDir = cwd6()) {
|
|
3292
|
-
const composePath =
|
|
3293
|
-
const packageJsonPath =
|
|
3413
|
+
const composePath = join30(startDir, "docker-compose.yml");
|
|
3414
|
+
const packageJsonPath = join30(startDir, "package.json");
|
|
3294
3415
|
try {
|
|
3295
3416
|
await access9(composePath);
|
|
3296
3417
|
await access9(packageJsonPath);
|
|
@@ -3308,7 +3429,7 @@ async function hasCoreChildDir(dir) {
|
|
|
3308
3429
|
for (const entry of entries) {
|
|
3309
3430
|
if (entry.isDirectory() && (entry.name.endsWith("-core") || entry.name === "core")) {
|
|
3310
3431
|
try {
|
|
3311
|
-
await access9(
|
|
3432
|
+
await access9(join30(dir, entry.name, "product.manifest.json"));
|
|
3312
3433
|
return true;
|
|
3313
3434
|
} catch {
|
|
3314
3435
|
}
|
|
@@ -3323,15 +3444,15 @@ async function isInAppMonorepo() {
|
|
|
3323
3444
|
}
|
|
3324
3445
|
|
|
3325
3446
|
// src/commands/standalone/standalone-auto-detect.ts
|
|
3326
|
-
import { readdir as readdir6, readFile as
|
|
3327
|
-
import { join as
|
|
3447
|
+
import { readdir as readdir6, readFile as readFile13 } from "fs/promises";
|
|
3448
|
+
import { join as join31, basename as basename2 } from "path";
|
|
3328
3449
|
async function autoDetectAppIdentity(appDir) {
|
|
3329
3450
|
const fromBootstrap = await detectFromBootstrap(appDir);
|
|
3330
3451
|
if (fromBootstrap) return fromBootstrap;
|
|
3331
3452
|
return detectFromPackageJson(appDir);
|
|
3332
3453
|
}
|
|
3333
3454
|
async function detectFromBootstrap(appDir) {
|
|
3334
|
-
const servicesDir =
|
|
3455
|
+
const servicesDir = join31(appDir, "services");
|
|
3335
3456
|
let entries;
|
|
3336
3457
|
try {
|
|
3337
3458
|
entries = await readdir6(servicesDir);
|
|
@@ -3341,7 +3462,7 @@ async function detectFromBootstrap(appDir) {
|
|
|
3341
3462
|
const bootstrapDirs = entries.filter((e) => e.endsWith("-bootstrap-service"));
|
|
3342
3463
|
if (bootstrapDirs.length === 0) return null;
|
|
3343
3464
|
for (const bootstrapDirName of bootstrapDirs) {
|
|
3344
|
-
const dataDir =
|
|
3465
|
+
const dataDir = join31(servicesDir, bootstrapDirName, "src", "data");
|
|
3345
3466
|
let dataDirEntries;
|
|
3346
3467
|
try {
|
|
3347
3468
|
dataDirEntries = await readdir6(dataDir);
|
|
@@ -3350,9 +3471,9 @@ async function detectFromBootstrap(appDir) {
|
|
|
3350
3471
|
}
|
|
3351
3472
|
for (const subDir of dataDirEntries) {
|
|
3352
3473
|
if (subDir === "platform") continue;
|
|
3353
|
-
const appJsonPath =
|
|
3474
|
+
const appJsonPath = join31(dataDir, subDir, "application.json");
|
|
3354
3475
|
try {
|
|
3355
|
-
const content = await
|
|
3476
|
+
const content = await readFile13(appJsonPath, "utf-8");
|
|
3356
3477
|
const appData = JSON.parse(content);
|
|
3357
3478
|
if (!appData.name) continue;
|
|
3358
3479
|
const applicationName = appData.name;
|
|
@@ -3370,7 +3491,7 @@ async function detectFromBootstrap(appDir) {
|
|
|
3370
3491
|
}
|
|
3371
3492
|
async function detectFromPackageJson(appDir) {
|
|
3372
3493
|
try {
|
|
3373
|
-
const content = await
|
|
3494
|
+
const content = await readFile13(join31(appDir, "package.json"), "utf-8");
|
|
3374
3495
|
const pkg = JSON.parse(content);
|
|
3375
3496
|
const rawName = pkg.name ?? "";
|
|
3376
3497
|
const applicationName = rawName.includes("/") ? rawName.split("/")[1] ?? rawName : rawName;
|
|
@@ -3393,23 +3514,24 @@ function inferPlatformNameFromAppDir(appDir, applicationName) {
|
|
|
3393
3514
|
}
|
|
3394
3515
|
|
|
3395
3516
|
// src/commands/standalone/standalone-orchestrator.ts
|
|
3396
|
-
import { join as
|
|
3397
|
-
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";
|
|
3398
3520
|
import { fetch as undiciFetch4, Agent as Agent4 } from "undici";
|
|
3399
3521
|
var TMP_BASE = "/tmp";
|
|
3400
3522
|
var STANDALONE_PREFIX = "platform-standalone-";
|
|
3401
3523
|
var DETACH_MARKER = ".standalone-running";
|
|
3402
3524
|
function getTmpDir(platformName) {
|
|
3403
|
-
return
|
|
3525
|
+
return join32(TMP_BASE, `${STANDALONE_PREFIX}${platformName}`);
|
|
3404
3526
|
}
|
|
3405
3527
|
async function findRunningSingleton() {
|
|
3406
3528
|
try {
|
|
3407
3529
|
const entries = await readdir7(TMP_BASE, { withFileTypes: true });
|
|
3408
3530
|
for (const entry of entries) {
|
|
3409
3531
|
if (entry.isDirectory() && entry.name.startsWith(STANDALONE_PREFIX)) {
|
|
3410
|
-
const candidateDir =
|
|
3532
|
+
const candidateDir = join32(TMP_BASE, entry.name);
|
|
3411
3533
|
try {
|
|
3412
|
-
await access10(
|
|
3534
|
+
await access10(join32(candidateDir, DETACH_MARKER));
|
|
3413
3535
|
return candidateDir;
|
|
3414
3536
|
} catch {
|
|
3415
3537
|
}
|
|
@@ -3453,7 +3575,7 @@ async function configureIdpFromConfig(config, localDir, logger) {
|
|
|
3453
3575
|
logger.log("No IDP configured \u2014 run 'platform standalone-config set' to add an IDP. Skipping IDP setup.");
|
|
3454
3576
|
return;
|
|
3455
3577
|
}
|
|
3456
|
-
const envPath =
|
|
3578
|
+
const envPath = join32(localDir, ".env");
|
|
3457
3579
|
let env;
|
|
3458
3580
|
try {
|
|
3459
3581
|
env = await readEnvFile(envPath);
|
|
@@ -3508,7 +3630,7 @@ async function configureAdminUsersFromConfig(config, localDir, logger) {
|
|
|
3508
3630
|
if (!config.adminUsers || config.adminUsers.length === 0) {
|
|
3509
3631
|
return;
|
|
3510
3632
|
}
|
|
3511
|
-
const envPath =
|
|
3633
|
+
const envPath = join32(localDir, ".env");
|
|
3512
3634
|
let env;
|
|
3513
3635
|
try {
|
|
3514
3636
|
env = await readEnvFile(envPath);
|
|
@@ -3551,19 +3673,19 @@ async function configureAdminUsersFromConfig(config, localDir, logger) {
|
|
|
3551
3673
|
}
|
|
3552
3674
|
}
|
|
3553
3675
|
function buildLayout(tmpDir, coreDirName) {
|
|
3554
|
-
const coreDir =
|
|
3676
|
+
const coreDir = join32(tmpDir, coreDirName);
|
|
3555
3677
|
return {
|
|
3556
3678
|
rootDir: tmpDir,
|
|
3557
3679
|
coreDir,
|
|
3558
3680
|
coreDirName,
|
|
3559
|
-
localDir:
|
|
3681
|
+
localDir: join32(coreDir, "local")
|
|
3560
3682
|
};
|
|
3561
3683
|
}
|
|
3562
|
-
async function
|
|
3563
|
-
const appComposePath =
|
|
3684
|
+
async function generateProxyAppCompose2(appDir, localDir, platformName, applicationName, logger) {
|
|
3685
|
+
const appComposePath = join32(appDir, "docker-compose.yml");
|
|
3564
3686
|
let content;
|
|
3565
3687
|
try {
|
|
3566
|
-
content = await
|
|
3688
|
+
content = await readFile14(appComposePath, "utf-8");
|
|
3567
3689
|
} catch {
|
|
3568
3690
|
logger.log(`Warning: No docker-compose.yml found at ${appComposePath} \u2014 app services won't start.`);
|
|
3569
3691
|
return;
|
|
@@ -3572,10 +3694,10 @@ async function generateProxyAppCompose(appDir, localDir, platformName, applicati
|
|
|
3572
3694
|
/^(\s+context:\s+)\.\/(.*)$/gm,
|
|
3573
3695
|
`$1${appDir}/$2`
|
|
3574
3696
|
);
|
|
3575
|
-
const appParentDir =
|
|
3697
|
+
const appParentDir = dirname14(appDir);
|
|
3576
3698
|
const rewritten = withAbsContexts.replace(/\$\{PWD\}/g, appParentDir);
|
|
3577
|
-
const proxyPath =
|
|
3578
|
-
await
|
|
3699
|
+
const proxyPath = join32(localDir, `${platformName}-${applicationName}-docker-compose.yml`);
|
|
3700
|
+
await writeFile13(proxyPath, rewritten, "utf-8");
|
|
3579
3701
|
logger.log(`Generated app compose with absolute build paths.`);
|
|
3580
3702
|
}
|
|
3581
3703
|
function buildComposeManifest(manifest) {
|
|
@@ -3601,7 +3723,23 @@ async function startStandalone(config, appDir, logger, signal, detach = false) {
|
|
|
3601
3723
|
}
|
|
3602
3724
|
logger.log(`Starting standalone platform for "${applicationDisplayName}"...`);
|
|
3603
3725
|
await mkdir5(tmpDir, { recursive: true });
|
|
3604
|
-
|
|
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
|
+
}
|
|
3605
3743
|
const variables = {
|
|
3606
3744
|
organizationName,
|
|
3607
3745
|
platformName,
|
|
@@ -3626,9 +3764,9 @@ async function startStandalone(config, appDir, logger, signal, detach = false) {
|
|
|
3626
3764
|
}
|
|
3627
3765
|
);
|
|
3628
3766
|
await writeManifest(npmManifest, tmpDir, coreDirName);
|
|
3629
|
-
const bootstrapServiceDir =
|
|
3767
|
+
const bootstrapServiceDir = join32(tmpDir, coreDirName, "services", bootstrapServiceName);
|
|
3630
3768
|
await scaffoldPlatformBootstrap(bootstrapServiceDir, variables, logger);
|
|
3631
|
-
const customizationUiDir =
|
|
3769
|
+
const customizationUiDir = join32(tmpDir, coreDirName, "ui", customizationUiName);
|
|
3632
3770
|
await scaffoldCustomizationUi(customizationUiDir, variables, logger);
|
|
3633
3771
|
await registerCustomizationModule(bootstrapServiceDir, organizationName, logger, platformName);
|
|
3634
3772
|
const layout = buildLayout(tmpDir, coreDirName);
|
|
@@ -3638,7 +3776,7 @@ async function startStandalone(config, appDir, logger, signal, detach = false) {
|
|
|
3638
3776
|
logger.log("Building...");
|
|
3639
3777
|
await buildAll(layout, npmManifest, logger, signal, true);
|
|
3640
3778
|
if (signal?.aborted) return;
|
|
3641
|
-
await
|
|
3779
|
+
await generateProxyAppCompose2(appDir, layout.localDir, platformName, applicationName, logger);
|
|
3642
3780
|
const composeManifest = buildComposeManifest(npmManifest);
|
|
3643
3781
|
await writeManifest(composeManifest, tmpDir, coreDirName);
|
|
3644
3782
|
logger.log("Resetting previous environment (removing stale containers and volumes)...");
|
|
@@ -3647,7 +3785,7 @@ async function startStandalone(config, appDir, logger, signal, detach = false) {
|
|
|
3647
3785
|
logger.log("Starting containers...");
|
|
3648
3786
|
await startEnvironment(layout, composeManifest, logger, signal);
|
|
3649
3787
|
if (signal?.aborted) return;
|
|
3650
|
-
const env = await readEnvFile(
|
|
3788
|
+
const env = await readEnvFile(join32(layout.localDir, ".env")).catch(() => /* @__PURE__ */ new Map());
|
|
3651
3789
|
const gatewayUrl = env.get("PAE_GATEWAY_HOST_URL") ?? "https://localhost:443";
|
|
3652
3790
|
const gatewayReady = await waitForGateway(gatewayUrl, logger);
|
|
3653
3791
|
if (!gatewayReady) {
|
|
@@ -3667,16 +3805,27 @@ Standalone platform is running in the background.`);
|
|
|
3667
3805
|
logger.log("\nPlatform is running. Press Ctrl+C to stop and clean up.");
|
|
3668
3806
|
const keepAlive = setInterval(() => {
|
|
3669
3807
|
}, 60 * 6e4);
|
|
3808
|
+
let cleanupInProgress = false;
|
|
3670
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;
|
|
3671
3816
|
clearInterval(keepAlive);
|
|
3672
|
-
|
|
3817
|
+
(async () => {
|
|
3673
3818
|
logger.log("\nShutting down standalone platform...");
|
|
3674
3819
|
await destroyEnvironment(layout, composeManifest, logger);
|
|
3675
3820
|
await cleanupTmpDir(tmpDir, logger);
|
|
3676
|
-
|
|
3821
|
+
process.exit(0);
|
|
3822
|
+
})().catch((err) => {
|
|
3823
|
+
logger.log(`Error during cleanup: ${err.message}`);
|
|
3824
|
+
process.exit(1);
|
|
3825
|
+
});
|
|
3677
3826
|
};
|
|
3678
|
-
process.
|
|
3679
|
-
process.
|
|
3827
|
+
process.on("SIGINT", shutdown);
|
|
3828
|
+
process.on("SIGTERM", shutdown);
|
|
3680
3829
|
}
|
|
3681
3830
|
async function stopStandalone(logger) {
|
|
3682
3831
|
const runningDir = await findRunningSingleton();
|
|
@@ -3690,7 +3839,7 @@ async function stopStandalone(logger) {
|
|
|
3690
3839
|
for (const entry of entries) {
|
|
3691
3840
|
if (entry.isDirectory() && entry.name.endsWith("-core")) {
|
|
3692
3841
|
try {
|
|
3693
|
-
await access10(
|
|
3842
|
+
await access10(join32(runningDir, entry.name, "product.manifest.json"));
|
|
3694
3843
|
coreDirName = entry.name;
|
|
3695
3844
|
break;
|
|
3696
3845
|
} catch {
|
|
@@ -3805,7 +3954,7 @@ async function standaloneUiController(ctx) {
|
|
|
3805
3954
|
const deleteLegacy = await ctx.confirm("Delete the local standalone.json?", true);
|
|
3806
3955
|
if (deleteLegacy) {
|
|
3807
3956
|
try {
|
|
3808
|
-
await rm3(
|
|
3957
|
+
await rm3(join33(appDir, "standalone.json"));
|
|
3809
3958
|
ctx.log("Deleted standalone.json.");
|
|
3810
3959
|
} catch {
|
|
3811
3960
|
ctx.log("Warning: Could not delete standalone.json.");
|
|
@@ -3937,7 +4086,7 @@ async function standaloneConfigShowUiController(ctx) {
|
|
|
3937
4086
|
if (config) {
|
|
3938
4087
|
ctx.log(`Standalone config for ${detected.platformName}/${detected.applicationName}:
|
|
3939
4088
|
`);
|
|
3940
|
-
ctx.log(JSON.stringify(config, null, 2));
|
|
4089
|
+
ctx.log(JSON.stringify(maskSensitiveFields(config), null, 2));
|
|
3941
4090
|
} else {
|
|
3942
4091
|
ctx.log(`No config found for ${detected.platformName}/${detected.applicationName}.`);
|
|
3943
4092
|
ctx.log(`Run 'platform standalone' or 'platform standalone-config set' to configure.`);
|
|
@@ -3956,7 +4105,7 @@ async function showAll(ctx) {
|
|
|
3956
4105
|
if (Object.keys(defaults).length === 0) {
|
|
3957
4106
|
ctx.log("(none)");
|
|
3958
4107
|
} else {
|
|
3959
|
-
ctx.log(JSON.stringify(defaults, null, 2));
|
|
4108
|
+
ctx.log(JSON.stringify(maskSensitiveFields(defaults), null, 2));
|
|
3960
4109
|
}
|
|
3961
4110
|
ctx.log("\n--- Apps ---");
|
|
3962
4111
|
if (apps.length === 0) {
|
|
@@ -4862,7 +5011,7 @@ async function standaloneConfigShowCliController(args2) {
|
|
|
4862
5011
|
const [pn, an] = parts;
|
|
4863
5012
|
const config = await resolveAppConfig(pn, an);
|
|
4864
5013
|
if (config) {
|
|
4865
|
-
console.log(JSON.stringify(config, null, 2));
|
|
5014
|
+
console.log(JSON.stringify(maskSensitiveFields(config), null, 2));
|
|
4866
5015
|
} else {
|
|
4867
5016
|
console.error(`No config found for ${appArg}.`);
|
|
4868
5017
|
process.exit(1);
|
|
@@ -4873,12 +5022,12 @@ async function standaloneConfigShowCliController(args2) {
|
|
|
4873
5022
|
if (detected) {
|
|
4874
5023
|
const config = await resolveAppConfig(detected.platformName, detected.applicationName);
|
|
4875
5024
|
if (config) {
|
|
4876
|
-
console.log(JSON.stringify(config, null, 2));
|
|
5025
|
+
console.log(JSON.stringify(maskSensitiveFields(config), null, 2));
|
|
4877
5026
|
return;
|
|
4878
5027
|
}
|
|
4879
5028
|
}
|
|
4880
5029
|
const cache = await readCacheFile();
|
|
4881
|
-
console.log(JSON.stringify(cache, null, 2));
|
|
5030
|
+
console.log(JSON.stringify(maskSensitiveFields(cache), null, 2));
|
|
4882
5031
|
}
|
|
4883
5032
|
async function standaloneConfigSetCliController(args2) {
|
|
4884
5033
|
if (args2["defaults"] === "true") {
|