@bluealba/platform-cli 1.0.1 → 1.0.2

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.
Files changed (42) hide show
  1. package/dist/index.js +277 -9
  2. package/docs/404.mdx +5 -0
  3. package/docs/architecture/api-explorer.mdx +478 -0
  4. package/docs/architecture/architecture-diagrams.mdx +12 -0
  5. package/docs/architecture/authentication-system.mdx +903 -0
  6. package/docs/architecture/authorization-system.mdx +886 -0
  7. package/docs/architecture/bootstrap.mdx +1442 -0
  8. package/docs/architecture/gateway-architecture.mdx +845 -0
  9. package/docs/architecture/multi-tenancy.mdx +1150 -0
  10. package/docs/architecture/overview.mdx +776 -0
  11. package/docs/architecture/scheduler.mdx +818 -0
  12. package/docs/architecture/shell.mdx +885 -0
  13. package/docs/architecture/ui-extension-points.mdx +781 -0
  14. package/docs/architecture/user-states.mdx +794 -0
  15. package/docs/development/overview.mdx +21 -0
  16. package/docs/development/workflow.mdx +914 -0
  17. package/docs/getting-started/core-concepts.mdx +892 -0
  18. package/docs/getting-started/installation.mdx +780 -0
  19. package/docs/getting-started/overview.mdx +83 -0
  20. package/docs/getting-started/quick-start.mdx +940 -0
  21. package/docs/guides/adding-documentation-sites.mdx +1367 -0
  22. package/docs/guides/creating-services.mdx +1736 -0
  23. package/docs/guides/creating-ui-modules.mdx +1860 -0
  24. package/docs/guides/identity-providers.mdx +1007 -0
  25. package/docs/guides/mermaid-diagrams.mdx +212 -0
  26. package/docs/guides/using-feature-flags.mdx +1059 -0
  27. package/docs/guides/working-with-rooms.mdx +566 -0
  28. package/docs/index.mdx +57 -0
  29. package/docs/platform-cli/commands.mdx +604 -0
  30. package/docs/platform-cli/overview.mdx +195 -0
  31. package/package.json +5 -2
  32. package/skills/ba-platform/platform-cli.skill.md +26 -0
  33. package/skills/ba-platform/platform.skill.md +35 -0
  34. package/templates/application-monorepo-template/gitignore +95 -0
  35. package/templates/bootstrap-service-template/gitignore +57 -0
  36. package/templates/bootstrap-service-template/src/main.ts +6 -16
  37. package/templates/customization-ui-module-template/gitignore +73 -0
  38. package/templates/nestjs-service-module-template/gitignore +56 -0
  39. package/templates/platform-init-template/{{platformName}}-core/gitignore +97 -0
  40. package/templates/react-ui-module-template/Dockerfile +1 -1
  41. package/templates/react-ui-module-template/caddy/Caddyfile +1 -1
  42. package/templates/react-ui-module-template/gitignore +72 -0
package/dist/index.js CHANGED
@@ -213,7 +213,7 @@ import { join as join2, dirname as dirname2 } from "path";
213
213
 
214
214
  // src/utils/template-engine.ts
215
215
  import { readdir, readFile, writeFile, mkdir, copyFile, stat, chmod } from "fs/promises";
216
- import { join, dirname, extname } from "path";
216
+ import { join, dirname, extname, basename } from "path";
217
217
  var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
218
218
  ".pem",
219
219
  ".crt",
@@ -239,6 +239,13 @@ function applyVariables(text, variables) {
239
239
  }
240
240
  return result;
241
241
  }
242
+ function transformFilename(relativePath) {
243
+ const base = basename(relativePath);
244
+ if (base === "gitignore") {
245
+ return join(dirname(relativePath), ".gitignore");
246
+ }
247
+ return relativePath;
248
+ }
242
249
  function isBinary(filePath) {
243
250
  return BINARY_EXTENSIONS.has(extname(filePath).toLowerCase());
244
251
  }
@@ -263,7 +270,7 @@ async function applyTemplate(config, onProgress) {
263
270
  const relativePath = srcPath.slice(templateDir4.length + 1);
264
271
  if (relativePath.endsWith(".gitkeep")) continue;
265
272
  if (exclude.includes(relativePath)) continue;
266
- const transformedRelative = applyVariables(relativePath, variables);
273
+ const transformedRelative = transformFilename(applyVariables(relativePath, variables));
267
274
  const destPath = join(outputDir, transformedRelative);
268
275
  await mkdir(dirname(destPath), { recursive: true });
269
276
  if (isBinary(srcPath)) {
@@ -2047,8 +2054,8 @@ async function checkIdpProviders(layout) {
2047
2054
  return { configured: false, providers: [], error: `gateway responded with ${response.status}` };
2048
2055
  }
2049
2056
  const data = await response.json();
2050
- const providers = data.map((p) => ({ name: p.name, type: p.type, active: p.active }));
2051
- return { configured: providers.length > 0, providers };
2057
+ const providers2 = data.map((p) => ({ name: p.name, type: p.type, active: p.active }));
2058
+ return { configured: providers2.length > 0, providers: providers2 };
2052
2059
  }
2053
2060
  async function gatherStatus() {
2054
2061
  const layout = await findPlatformLayout();
@@ -2268,6 +2275,91 @@ async function removePlatformAdmin(ruleId, logger) {
2268
2275
  return true;
2269
2276
  }
2270
2277
 
2278
+ // src/commands/install-ai-plugin/plugins/ba-platform/index.ts
2279
+ var baPlatformPlugin = {
2280
+ name: "ba-platform-plugin",
2281
+ description: "Blue Alba Platform knowledge and CLI skills for AI assistants",
2282
+ version: "1.0.0",
2283
+ skills: [
2284
+ {
2285
+ name: "platform",
2286
+ description: "Comprehensive Blue Alba Platform architecture and concepts knowledge",
2287
+ type: "skill",
2288
+ sourceFile: "skills/ba-platform/platform.skill.md"
2289
+ },
2290
+ {
2291
+ name: "platform-cli",
2292
+ description: "Blue Alba Platform CLI commands and usage knowledge",
2293
+ type: "skill",
2294
+ sourceFile: "skills/ba-platform/platform-cli.skill.md"
2295
+ }
2296
+ ]
2297
+ };
2298
+
2299
+ // src/commands/install-ai-plugin/plugins/registry.ts
2300
+ var pluginCatalog = [baPlatformPlugin];
2301
+ function findPlugin(name) {
2302
+ return pluginCatalog.find((p) => p.name === name);
2303
+ }
2304
+
2305
+ // src/commands/install-ai-plugin/install-ai-plugin.command.ts
2306
+ var INSTALL_AI_PLUGIN_COMMAND_NAME = "install-ai-plugin";
2307
+ var installAiPluginCommand = {
2308
+ name: INSTALL_AI_PLUGIN_COMMAND_NAME,
2309
+ description: "Install AI assistant plugins (skills, MCPs, hooks) for your AI provider"
2310
+ };
2311
+ async function installAiPlugin(params, context) {
2312
+ const { provider, docsSource, logger, confirmUpdate } = context;
2313
+ for (const pluginName of params.plugins) {
2314
+ const plugin = findPlugin(pluginName);
2315
+ if (!plugin) {
2316
+ logger.log(`Error: Plugin "${pluginName}" not found in catalog.`);
2317
+ continue;
2318
+ }
2319
+ const manifest = await provider.readManifest(pluginName);
2320
+ const diff = provider.calculateDiff(manifest, plugin);
2321
+ const hasStructuralChanges = diff.versionChanged || diff.newSkills.length > 0 || diff.removedSkills.length > 0;
2322
+ if (manifest && hasStructuralChanges) {
2323
+ logger.log(
2324
+ `Plugin "${pluginName}" is already installed (v${diff.currentVersion} \u2192 v${diff.targetVersion}).`
2325
+ );
2326
+ if (diff.newSkills.length > 0) {
2327
+ logger.log(` New skills: ${diff.newSkills.map((s) => s.name).join(", ")}`);
2328
+ }
2329
+ if (diff.removedSkills.length > 0) {
2330
+ logger.log(` Removed skills: ${diff.removedSkills.join(", ")}`);
2331
+ }
2332
+ if (diff.existingSkills.length > 0) {
2333
+ logger.log(` Updated skills: ${diff.existingSkills.map((s) => s.name).join(", ")}`);
2334
+ }
2335
+ if (!params.force && confirmUpdate) {
2336
+ const proceed = await confirmUpdate(diff, plugin);
2337
+ if (!proceed) {
2338
+ logger.log(`Skipped "${pluginName}".`);
2339
+ continue;
2340
+ }
2341
+ }
2342
+ for (const skillName of diff.removedSkills) {
2343
+ await provider.removeSkill(skillName, logger);
2344
+ }
2345
+ } else if (!manifest) {
2346
+ logger.log(`Installing plugin "${pluginName}" v${plugin.version}...`);
2347
+ }
2348
+ for (const skill of plugin.skills) {
2349
+ await provider.installSkill(skill, docsSource, logger);
2350
+ }
2351
+ await provider.writeManifest({
2352
+ plugin: pluginName,
2353
+ version: plugin.version,
2354
+ provider: provider.name,
2355
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
2356
+ skills: plugin.skills.map((s) => s.name)
2357
+ });
2358
+ await provider.updatePermissions(docsSource, logger);
2359
+ logger.log(`Plugin "${pluginName}" v${plugin.version} installed successfully.`);
2360
+ }
2361
+ }
2362
+
2271
2363
  // src/commands/registry.ts
2272
2364
  var CommandRegistry = class {
2273
2365
  commands = /* @__PURE__ */ new Map();
@@ -2308,6 +2400,7 @@ for (const cmd of localScriptCommands) {
2308
2400
  }
2309
2401
  registry.register(statusCommand);
2310
2402
  registry.register(managePlatformAdminsCommand);
2403
+ registry.register(installAiPluginCommand);
2311
2404
 
2312
2405
  // src/app-state.ts
2313
2406
  var APP_STATE = {
@@ -2405,12 +2498,12 @@ async function configureIdpUiController(ctx) {
2405
2498
  ctx.log("Error: Cannot configure an IDP \u2014 no platform initialized in this directory.");
2406
2499
  return;
2407
2500
  }
2408
- const providers = getAllProviders();
2501
+ const providers2 = getAllProviders();
2409
2502
  const providerType = await ctx.select(
2410
2503
  "Select IDP type:",
2411
- providers.map((p) => ({ label: p.displayName, value: p.type }))
2504
+ providers2.map((p) => ({ label: p.displayName, value: p.type }))
2412
2505
  );
2413
- const provider = providers.find((p) => p.type === providerType);
2506
+ const provider = providers2.find((p) => p.type === providerType);
2414
2507
  const name = await ctx.prompt("Provider name:");
2415
2508
  const issuer = await ctx.prompt("Issuer URL:");
2416
2509
  const clientId = await ctx.prompt("Client ID:");
@@ -2819,6 +2912,153 @@ async function handleRemove(ctx) {
2819
2912
  ctx.log(`Successfully removed ${successCount} of ${selected.length} admin(s).`);
2820
2913
  }
2821
2914
 
2915
+ // src/services/install-ai-plugin.service.ts
2916
+ async function installAiPluginService(params, context) {
2917
+ await installAiPlugin(params, context);
2918
+ }
2919
+
2920
+ // src/commands/install-ai-plugin/providers/claude.provider.ts
2921
+ import { homedir as homedir2 } from "os";
2922
+ import { join as join26 } from "path";
2923
+ import { readFile as readFile10, writeFile as writeFile9, mkdir as mkdir3, rm } from "fs/promises";
2924
+ var ClaudeProvider = class {
2925
+ name = "claude";
2926
+ baseDir = join26(homedir2(), ".claude");
2927
+ getInstallPath(skillName) {
2928
+ return join26(this.baseDir, "skills", skillName, "SKILL.md");
2929
+ }
2930
+ manifestPath(pluginName) {
2931
+ return join26(this.baseDir, `${pluginName}.manifest.json`);
2932
+ }
2933
+ async readManifest(pluginName) {
2934
+ try {
2935
+ const content = await readFile10(this.manifestPath(pluginName), "utf-8");
2936
+ return JSON.parse(content);
2937
+ } catch {
2938
+ return null;
2939
+ }
2940
+ }
2941
+ async writeManifest(manifest) {
2942
+ await mkdir3(this.baseDir, { recursive: true });
2943
+ await writeFile9(
2944
+ this.manifestPath(manifest.plugin),
2945
+ JSON.stringify(manifest, null, 2),
2946
+ "utf-8"
2947
+ );
2948
+ }
2949
+ calculateDiff(manifest, plugin) {
2950
+ const installedSkills = manifest?.skills ?? [];
2951
+ const targetSkillNames = plugin.skills.map((s) => s.name);
2952
+ return {
2953
+ newSkills: plugin.skills.filter((s) => !installedSkills.includes(s.name)),
2954
+ removedSkills: installedSkills.filter((name) => !targetSkillNames.includes(name)),
2955
+ existingSkills: plugin.skills.filter((s) => installedSkills.includes(s.name)),
2956
+ versionChanged: manifest?.version !== plugin.version,
2957
+ currentVersion: manifest?.version ?? null,
2958
+ targetVersion: plugin.version
2959
+ };
2960
+ }
2961
+ async installSkill(skill, docsSource, logger) {
2962
+ const sourcePath = docsSource.resolve(skill.sourceFile);
2963
+ let content = await readFile10(sourcePath, "utf-8");
2964
+ const docsPath = docsSource.resolve("docs");
2965
+ content = content.replaceAll("{{docsPath}}", docsPath);
2966
+ const installPath = this.getInstallPath(skill.name);
2967
+ await mkdir3(join26(installPath, ".."), { recursive: true });
2968
+ await writeFile9(installPath, content, "utf-8");
2969
+ logger.log(` Installed skill: ${installPath}`);
2970
+ }
2971
+ async removeSkill(skillName, logger) {
2972
+ const skillDir = join26(this.baseDir, "skills", skillName);
2973
+ try {
2974
+ await rm(skillDir, { recursive: true, force: true });
2975
+ logger.log(` Removed skill: ${skillDir}`);
2976
+ } catch {
2977
+ }
2978
+ }
2979
+ async updatePermissions(docsSource, logger) {
2980
+ const settingsPath = join26(this.baseDir, "settings.json");
2981
+ const docsPath = docsSource.resolve("docs");
2982
+ let settings = {};
2983
+ try {
2984
+ const content = await readFile10(settingsPath, "utf-8");
2985
+ settings = JSON.parse(content);
2986
+ } catch {
2987
+ }
2988
+ const permissions = settings.permissions ?? {};
2989
+ const additionalDirectories = permissions.additionalDirectories ?? [];
2990
+ if (additionalDirectories.includes(docsPath)) {
2991
+ return;
2992
+ }
2993
+ permissions.additionalDirectories = [...additionalDirectories, docsPath];
2994
+ settings.permissions = permissions;
2995
+ await mkdir3(this.baseDir, { recursive: true });
2996
+ await writeFile9(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
2997
+ logger.log(` Granted read access to docs: ${docsPath}`);
2998
+ }
2999
+ };
3000
+
3001
+ // src/commands/install-ai-plugin/providers/index.ts
3002
+ var providers = {
3003
+ claude: () => new ClaudeProvider()
3004
+ };
3005
+ var providerNames = Object.keys(providers);
3006
+ function getProvider(name) {
3007
+ const factory = providers[name];
3008
+ if (!factory) {
3009
+ throw new Error(`Unknown AI provider: "${name}". Available: ${providerNames.join(", ")}`);
3010
+ }
3011
+ return factory();
3012
+ }
3013
+
3014
+ // src/commands/install-ai-plugin/docs-source/local.docs-source.ts
3015
+ import { fileURLToPath as fileURLToPath10 } from "url";
3016
+ import { join as join27, dirname as dirname12 } from "path";
3017
+ var packageRoot = join27(dirname12(fileURLToPath10(import.meta.url)), "..");
3018
+ var LocalDocsSource = class {
3019
+ resolve(relativePath) {
3020
+ return join27(packageRoot, relativePath);
3021
+ }
3022
+ };
3023
+
3024
+ // src/controllers/ui/install-ai-plugin.ui-controller.ts
3025
+ async function installAiPluginUiController(ctx) {
3026
+ const providerName = await ctx.select(
3027
+ "Select AI provider:",
3028
+ providerNames.map((name) => ({
3029
+ label: name.charAt(0).toUpperCase() + name.slice(1),
3030
+ value: name
3031
+ }))
3032
+ );
3033
+ const selectedPlugins = await ctx.multiselect(
3034
+ "Select plugins to install:",
3035
+ pluginCatalog.map((p) => ({
3036
+ label: `${p.name} \u2014 ${p.description}`,
3037
+ value: p.name
3038
+ }))
3039
+ );
3040
+ if (selectedPlugins.length === 0) {
3041
+ ctx.log("No plugins selected.");
3042
+ return;
3043
+ }
3044
+ const provider = getProvider(providerName);
3045
+ const docsSource = new LocalDocsSource();
3046
+ await installAiPluginService(
3047
+ { provider: providerName, plugins: selectedPlugins },
3048
+ {
3049
+ provider,
3050
+ docsSource,
3051
+ logger: ctx,
3052
+ confirmUpdate: async (diff, plugin) => {
3053
+ return ctx.confirm(
3054
+ `Update "${plugin.name}" from v${diff.currentVersion} to v${diff.targetVersion}?`,
3055
+ true
3056
+ );
3057
+ }
3058
+ }
3059
+ );
3060
+ }
3061
+
2822
3062
  // src/controllers/ui/registry.ts
2823
3063
  var uiControllers = /* @__PURE__ */ new Map([
2824
3064
  [CREATE_APPLICATION_COMMAND_NAME, createApplicationUiController],
@@ -2832,7 +3072,8 @@ var uiControllers = /* @__PURE__ */ new Map([
2832
3072
  [START_COMMAND_NAME, createLocalScriptUiController(START_COMMAND_NAME)],
2833
3073
  [STOP_COMMAND_NAME, createLocalScriptUiController(STOP_COMMAND_NAME)],
2834
3074
  [DESTROY_COMMAND_NAME, createLocalScriptUiController(DESTROY_COMMAND_NAME)],
2835
- [MANAGE_PLATFORM_ADMINS_COMMAND_NAME, managePlatformAdminsUiController]
3075
+ [MANAGE_PLATFORM_ADMINS_COMMAND_NAME, managePlatformAdminsUiController],
3076
+ [INSTALL_AI_PLUGIN_COMMAND_NAME, installAiPluginUiController]
2836
3077
  ]);
2837
3078
 
2838
3079
  // src/hooks/use-command-runner.ts
@@ -3463,6 +3704,32 @@ async function managePlatformAdminsCliController(args2) {
3463
3704
  }
3464
3705
  }
3465
3706
 
3707
+ // src/controllers/cli/install-ai-plugin.cli-controller.ts
3708
+ async function installAiPluginCliController(args2) {
3709
+ const { provider: providerName, plugins: pluginsArg, force } = args2;
3710
+ if (!providerName) {
3711
+ console.error('Error: "provider" argument is required (e.g., provider=claude).');
3712
+ process.exit(1);
3713
+ }
3714
+ if (!pluginsArg) {
3715
+ console.error(
3716
+ 'Error: "plugins" argument is required (e.g., plugins=ba-platform-plugin).'
3717
+ );
3718
+ process.exit(1);
3719
+ }
3720
+ const plugins = pluginsArg.split(",").map((s) => s.trim());
3721
+ const provider = getProvider(providerName);
3722
+ const docsSource = new LocalDocsSource();
3723
+ await installAiPluginService(
3724
+ { provider: providerName, plugins, force: force === "true" },
3725
+ {
3726
+ provider,
3727
+ docsSource,
3728
+ logger: { log: console.log }
3729
+ }
3730
+ );
3731
+ }
3732
+
3466
3733
  // src/controllers/cli/registry.ts
3467
3734
  var cliControllers = /* @__PURE__ */ new Map([
3468
3735
  [CREATE_APPLICATION_COMMAND_NAME, createApplicationCliController],
@@ -3476,7 +3743,8 @@ var cliControllers = /* @__PURE__ */ new Map([
3476
3743
  [START_COMMAND_NAME, createLocalScriptCliController(START_COMMAND_NAME)],
3477
3744
  [STOP_COMMAND_NAME, createLocalScriptCliController(STOP_COMMAND_NAME)],
3478
3745
  [DESTROY_COMMAND_NAME, createLocalScriptCliController(DESTROY_COMMAND_NAME)],
3479
- [MANAGE_PLATFORM_ADMINS_COMMAND_NAME, managePlatformAdminsCliController]
3746
+ [MANAGE_PLATFORM_ADMINS_COMMAND_NAME, managePlatformAdminsCliController],
3747
+ [INSTALL_AI_PLUGIN_COMMAND_NAME, installAiPluginCliController]
3480
3748
  ]);
3481
3749
 
3482
3750
  // src/utils/parse-args.ts
package/docs/404.mdx ADDED
@@ -0,0 +1,5 @@
1
+ ---
2
+ title: Page Not Found
3
+ ---
4
+
5
+ Sorry, this page doesn't exist. Use the navigation to find what you're looking for.