@bluealba/platform-cli 0.3.1-feature-platform-cli-223 → 0.3.1-feature-platform-cli-224
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 +262 -66
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -5,8 +5,8 @@ import { render } from "ink";
|
|
|
5
5
|
|
|
6
6
|
// src/app.tsx
|
|
7
7
|
import { createRequire } from "module";
|
|
8
|
-
import { useState as useState3, useCallback as useCallback2 } from "react";
|
|
9
|
-
import { Box as
|
|
8
|
+
import { useState as useState3, useCallback as useCallback2, useEffect as useEffect2 } from "react";
|
|
9
|
+
import { Box as Box6, Text as Text9, useApp, useInput } from "ink";
|
|
10
10
|
|
|
11
11
|
// src/components/prompt.tsx
|
|
12
12
|
import { Box, Text } from "ink";
|
|
@@ -143,6 +143,31 @@ function Spinner({ label }) {
|
|
|
143
143
|
] });
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
// src/components/select-list.tsx
|
|
147
|
+
import React4 from "react";
|
|
148
|
+
import { Box as Box4, Text as Text7 } from "ink";
|
|
149
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
150
|
+
var SelectList = React4.memo(function SelectList2({ options, selectedIndex }) {
|
|
151
|
+
if (options.length === 0) {
|
|
152
|
+
return /* @__PURE__ */ jsx7(Box4, { paddingLeft: 2, children: /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "No options available" }) });
|
|
153
|
+
}
|
|
154
|
+
return /* @__PURE__ */ jsx7(Box4, { flexDirection: "column", paddingLeft: 2, children: options.map((opt, i) => {
|
|
155
|
+
const isSelected = i === selectedIndex;
|
|
156
|
+
return /* @__PURE__ */ jsx7(Box4, { children: /* @__PURE__ */ jsx7(Text7, { color: "cyan", bold: isSelected, inverse: isSelected, children: opt.label }) }, opt.value);
|
|
157
|
+
}) });
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// src/components/confirm-prompt.tsx
|
|
161
|
+
import { Box as Box5, Text as Text8 } from "ink";
|
|
162
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
163
|
+
function ConfirmPrompt({ value }) {
|
|
164
|
+
return /* @__PURE__ */ jsxs6(Box5, { gap: 2, paddingLeft: 2, children: [
|
|
165
|
+
/* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: value, inverse: value, children: "Yes" }),
|
|
166
|
+
/* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: !value, inverse: !value, children: "No" }),
|
|
167
|
+
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "(\u2190/\u2192 to toggle, Enter to confirm)" })
|
|
168
|
+
] });
|
|
169
|
+
}
|
|
170
|
+
|
|
146
171
|
// src/commands/registry.ts
|
|
147
172
|
import { Fzf } from "fzf";
|
|
148
173
|
|
|
@@ -358,8 +383,8 @@ function buildServiceModuleEntry(organizationName, platformName, applicationName
|
|
|
358
383
|
dependsOn: []
|
|
359
384
|
};
|
|
360
385
|
}
|
|
361
|
-
function buildUiModuleEntry(organizationName, platformName, applicationName, applicationDisplayName) {
|
|
362
|
-
const uiName = `${platformName}-${applicationName}-ui`;
|
|
386
|
+
function buildUiModuleEntry(organizationName, platformName, applicationName, applicationDisplayName, uiNameOverride) {
|
|
387
|
+
const uiName = uiNameOverride ?? `${platformName}-${applicationName}-ui`;
|
|
363
388
|
return {
|
|
364
389
|
name: `@${organizationName}/${uiName}`,
|
|
365
390
|
displayName: applicationDisplayName,
|
|
@@ -791,7 +816,8 @@ async function generateLocalEnv(outputDir, logger, coreDirName = "core") {
|
|
|
791
816
|
var INIT_COMMAND_NAME = "init";
|
|
792
817
|
var initCommand = {
|
|
793
818
|
name: INIT_COMMAND_NAME,
|
|
794
|
-
description: "Initialize a new platform"
|
|
819
|
+
description: "Initialize a new platform",
|
|
820
|
+
hidden: ({ platformInitialized }) => platformInitialized
|
|
795
821
|
};
|
|
796
822
|
async function init(params, logger) {
|
|
797
823
|
if (await isPlatformInitialized()) {
|
|
@@ -803,8 +829,10 @@ async function init(params, logger) {
|
|
|
803
829
|
const outputDir = cwd4();
|
|
804
830
|
logger.log(`Initializing ${platformTitle} platform for @${organizationName}...`);
|
|
805
831
|
const coreDirName = `${platformName}-core`;
|
|
806
|
-
const
|
|
807
|
-
const
|
|
832
|
+
const bootstrapSuffix = params.bootstrapServiceSuffix ?? "bootstrap-service";
|
|
833
|
+
const customizationUiSuffix = params.customizationUiSuffix ?? "customization-ui";
|
|
834
|
+
const bootstrapServiceName = `${platformName}-${bootstrapSuffix}`;
|
|
835
|
+
const customizationUiName = `${platformName}-${customizationUiSuffix}`;
|
|
808
836
|
const variables = {
|
|
809
837
|
organizationName,
|
|
810
838
|
platformName,
|
|
@@ -1077,7 +1105,7 @@ var createServiceModuleCommand = {
|
|
|
1077
1105
|
description: "Add a new service module to an existing application"
|
|
1078
1106
|
};
|
|
1079
1107
|
async function createServiceModule(params, logger) {
|
|
1080
|
-
const { applicationName, serviceName, serviceDisplayName } = params;
|
|
1108
|
+
const { applicationName, serviceName, serviceDisplayName, serviceNameSuffix } = params;
|
|
1081
1109
|
const layout = await findPlatformLayout();
|
|
1082
1110
|
if (!layout) {
|
|
1083
1111
|
logger.log("Error: Cannot create a service module \u2014 no platform initialized in this directory.");
|
|
@@ -1104,7 +1132,8 @@ async function createServiceModule(params, logger) {
|
|
|
1104
1132
|
logger.log(`Error: The specified application "${applicationName}" does not exist in the platform.`);
|
|
1105
1133
|
return;
|
|
1106
1134
|
}
|
|
1107
|
-
const
|
|
1135
|
+
const suffix = serviceNameSuffix === void 0 ? "service" : serviceNameSuffix;
|
|
1136
|
+
const fullServiceName = suffix ? `${platformName}-${serviceName}-${suffix}` : `${platformName}-${serviceName}`;
|
|
1108
1137
|
const serviceDir = join20(applicationDir, "services", fullServiceName);
|
|
1109
1138
|
try {
|
|
1110
1139
|
await access3(serviceDir);
|
|
@@ -1141,8 +1170,8 @@ import { access as access4, readdir as readdir4 } from "fs/promises";
|
|
|
1141
1170
|
|
|
1142
1171
|
// src/commands/create-ui-module/append-ui-docker-compose.ts
|
|
1143
1172
|
import { readFile as readFile9, writeFile as writeFile8 } from "fs/promises";
|
|
1144
|
-
async function appendUiToDockerCompose(dockerComposePath, platformName, applicationName, port, logger) {
|
|
1145
|
-
const uiName = `${platformName}-${applicationName}-ui`;
|
|
1173
|
+
async function appendUiToDockerCompose(dockerComposePath, platformName, applicationName, port, logger, uiNameOverride) {
|
|
1174
|
+
const uiName = uiNameOverride ?? `${platformName}-${applicationName}-ui`;
|
|
1146
1175
|
const block = `
|
|
1147
1176
|
${uiName}:
|
|
1148
1177
|
build:
|
|
@@ -1170,7 +1199,7 @@ var createUiModuleCommand = {
|
|
|
1170
1199
|
description: "Add a UI module to an existing application"
|
|
1171
1200
|
};
|
|
1172
1201
|
async function createUiModule(params, logger) {
|
|
1173
|
-
const { applicationName, applicationDisplayName } = params;
|
|
1202
|
+
const { applicationName, applicationDisplayName, uiModuleSuffix } = params;
|
|
1174
1203
|
const layout = await findPlatformLayout();
|
|
1175
1204
|
if (!layout) {
|
|
1176
1205
|
logger.log("Error: Cannot create a UI module \u2014 no platform initialized in this directory.");
|
|
@@ -1207,7 +1236,8 @@ async function createUiModule(params, logger) {
|
|
|
1207
1236
|
}
|
|
1208
1237
|
} catch {
|
|
1209
1238
|
}
|
|
1210
|
-
const
|
|
1239
|
+
const uiSuffix = uiModuleSuffix === void 0 ? "ui" : uiModuleSuffix;
|
|
1240
|
+
const uiName = uiSuffix ? `${platformName}-${applicationName}-${uiSuffix}` : `${platformName}-${applicationName}`;
|
|
1211
1241
|
const uiOutputDir = join21(uiDir, uiName);
|
|
1212
1242
|
const uiBaseDir = `${platformName}-${applicationName}/ui`;
|
|
1213
1243
|
try {
|
|
@@ -1217,11 +1247,11 @@ async function createUiModule(params, logger) {
|
|
|
1217
1247
|
return;
|
|
1218
1248
|
}
|
|
1219
1249
|
const bootstrapServiceDir = join21(applicationDir, "services", `${platformName}-${applicationName}-bootstrap-service`);
|
|
1220
|
-
const moduleEntry = buildUiModuleEntry(organizationName, platformName, applicationName, applicationDisplayName);
|
|
1250
|
+
const moduleEntry = buildUiModuleEntry(organizationName, platformName, applicationName, applicationDisplayName, uiName);
|
|
1221
1251
|
await addModuleEntry(bootstrapServiceDir, applicationName, moduleEntry, logger);
|
|
1222
1252
|
const dockerComposePath = join21(localDir, `${platformName}-${applicationName}-docker-compose.yml`);
|
|
1223
1253
|
const port = await getNextAvailablePort(localDir);
|
|
1224
|
-
await appendUiToDockerCompose(dockerComposePath, platformName, applicationName, port, logger);
|
|
1254
|
+
await appendUiToDockerCompose(dockerComposePath, platformName, applicationName, port, logger, uiName);
|
|
1225
1255
|
logger.log(`Done! UI module "${uiName}" added to application "${applicationName}".`);
|
|
1226
1256
|
}
|
|
1227
1257
|
|
|
@@ -1245,6 +1275,14 @@ var CommandRegistry = class {
|
|
|
1245
1275
|
});
|
|
1246
1276
|
return fzf.find(query).map((result) => result.item);
|
|
1247
1277
|
}
|
|
1278
|
+
searchVisible(query, visibilityCtx) {
|
|
1279
|
+
const visible = this.getAll().filter((cmd) => !cmd.hidden?.(visibilityCtx));
|
|
1280
|
+
if (!query) return visible;
|
|
1281
|
+
const fzf = new Fzf(visible, {
|
|
1282
|
+
selector: (cmd) => `${cmd.name} ${cmd.description}`
|
|
1283
|
+
});
|
|
1284
|
+
return fzf.find(query).map((result) => result.item);
|
|
1285
|
+
}
|
|
1248
1286
|
};
|
|
1249
1287
|
var registry = new CommandRegistry();
|
|
1250
1288
|
registry.register(createApplicationCommand);
|
|
@@ -1282,23 +1320,15 @@ async function createApplicationUiController(ctx) {
|
|
|
1282
1320
|
}
|
|
1283
1321
|
const applicationDisplayName = await ctx.prompt("Application display name:");
|
|
1284
1322
|
const applicationDescription = await ctx.prompt("Application description:");
|
|
1285
|
-
const
|
|
1286
|
-
|
|
1287
|
-
ctx.log(`Error: Please answer "yes" or "no".`);
|
|
1288
|
-
return;
|
|
1289
|
-
}
|
|
1290
|
-
const hasBackendServiceAnswer = await ctx.prompt("Does this application have a backend service? (yes/no):");
|
|
1291
|
-
if (hasBackendServiceAnswer !== "yes" && hasBackendServiceAnswer !== "no") {
|
|
1292
|
-
ctx.log(`Error: Please answer "yes" or "no".`);
|
|
1293
|
-
return;
|
|
1294
|
-
}
|
|
1323
|
+
const hasUserInterface = await ctx.confirm("Does this application have a user interface?");
|
|
1324
|
+
const hasBackendService = await ctx.confirm("Does this application have a backend service?");
|
|
1295
1325
|
await createApplicationService(
|
|
1296
1326
|
{
|
|
1297
1327
|
applicationName,
|
|
1298
1328
|
applicationDisplayName,
|
|
1299
1329
|
applicationDescription,
|
|
1300
|
-
hasUserInterface
|
|
1301
|
-
hasBackendService
|
|
1330
|
+
hasUserInterface,
|
|
1331
|
+
hasBackendService
|
|
1302
1332
|
},
|
|
1303
1333
|
ctx
|
|
1304
1334
|
);
|
|
@@ -1318,7 +1348,32 @@ async function initUiController(ctx) {
|
|
|
1318
1348
|
const organizationName = await ctx.prompt("Organization name:");
|
|
1319
1349
|
const platformName = await ctx.prompt("Platform name:");
|
|
1320
1350
|
const platformDisplayName = await ctx.prompt("Platform display name:");
|
|
1321
|
-
|
|
1351
|
+
const defaultBootstrapSuffix = "bootstrap-service";
|
|
1352
|
+
const defaultCustomizationUiSuffix = "customization-ui";
|
|
1353
|
+
ctx.log(`Default artifact names:`);
|
|
1354
|
+
ctx.log(` Bootstrap service: ${platformName}-${defaultBootstrapSuffix}`);
|
|
1355
|
+
ctx.log(` Customization UI: ${platformName}-${defaultCustomizationUiSuffix}`);
|
|
1356
|
+
const customize = await ctx.confirm("Customize artifact names?", false);
|
|
1357
|
+
let bootstrapServiceSuffix = defaultBootstrapSuffix;
|
|
1358
|
+
let customizationUiSuffix = defaultCustomizationUiSuffix;
|
|
1359
|
+
if (customize) {
|
|
1360
|
+
const bsSuffix = await ctx.prompt(`Bootstrap service suffix (${platformName}-...):`, defaultBootstrapSuffix);
|
|
1361
|
+
if (!/^[a-z0-9-]+$/.test(bsSuffix)) {
|
|
1362
|
+
ctx.log(`Error: Suffix "${bsSuffix}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
bootstrapServiceSuffix = bsSuffix;
|
|
1366
|
+
const cuiSuffix = await ctx.prompt(`Customization UI suffix (${platformName}-...):`, defaultCustomizationUiSuffix);
|
|
1367
|
+
if (!/^[a-z0-9-]+$/.test(cuiSuffix)) {
|
|
1368
|
+
ctx.log(`Error: Suffix "${cuiSuffix}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
customizationUiSuffix = cuiSuffix;
|
|
1372
|
+
}
|
|
1373
|
+
await initService(
|
|
1374
|
+
{ organizationName, platformName, platformDisplayName, bootstrapServiceSuffix, customizationUiSuffix },
|
|
1375
|
+
ctx
|
|
1376
|
+
);
|
|
1322
1377
|
}
|
|
1323
1378
|
|
|
1324
1379
|
// src/services/configure-idp.service.ts
|
|
@@ -1333,14 +1388,11 @@ async function configureIdpUiController(ctx) {
|
|
|
1333
1388
|
return;
|
|
1334
1389
|
}
|
|
1335
1390
|
const providers = getAllProviders();
|
|
1336
|
-
const
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
return;
|
|
1342
|
-
}
|
|
1343
|
-
const provider = providers[index];
|
|
1391
|
+
const providerType = await ctx.select(
|
|
1392
|
+
"Select IDP type:",
|
|
1393
|
+
providers.map((p) => ({ label: p.displayName, value: p.type }))
|
|
1394
|
+
);
|
|
1395
|
+
const provider = providers.find((p) => p.type === providerType);
|
|
1344
1396
|
const name = await ctx.prompt("Provider name:");
|
|
1345
1397
|
const issuer = await ctx.prompt("Issuer URL:");
|
|
1346
1398
|
const clientId = await ctx.prompt("Client ID:");
|
|
@@ -1366,19 +1418,38 @@ async function createServiceModuleUiController(ctx) {
|
|
|
1366
1418
|
ctx.log("Error: Cannot create a service module \u2014 no platform initialized in this directory.");
|
|
1367
1419
|
return;
|
|
1368
1420
|
}
|
|
1369
|
-
const
|
|
1370
|
-
if (
|
|
1371
|
-
ctx.log(
|
|
1421
|
+
const layout = await findPlatformLayout();
|
|
1422
|
+
if (!layout) {
|
|
1423
|
+
ctx.log("Error: Cannot create a service module \u2014 no platform initialized in this directory.");
|
|
1372
1424
|
return;
|
|
1373
1425
|
}
|
|
1426
|
+
let manifest = await readManifest(layout.rootDir, layout.coreDirName);
|
|
1427
|
+
if (manifest.applications.length === 0) {
|
|
1428
|
+
ctx.log("No applications found in this platform.");
|
|
1429
|
+
const createFirst = await ctx.confirm("Would you like to create an application first?");
|
|
1430
|
+
if (!createFirst) return;
|
|
1431
|
+
await createApplicationUiController(ctx);
|
|
1432
|
+
manifest = await readManifest(layout.rootDir, layout.coreDirName);
|
|
1433
|
+
if (manifest.applications.length === 0) {
|
|
1434
|
+
ctx.log("Error: No applications available after creation attempt.");
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
const applicationName = await ctx.select(
|
|
1439
|
+
"Select application:",
|
|
1440
|
+
manifest.applications.map((a) => ({ label: `${a.name} \u2014 ${a.displayName}`, value: a.name }))
|
|
1441
|
+
);
|
|
1374
1442
|
const serviceName = await ctx.prompt("Service name:");
|
|
1375
1443
|
if (!/^[a-z0-9-]+$/.test(serviceName)) {
|
|
1376
1444
|
ctx.log(`Error: Service name "${serviceName}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
|
|
1377
1445
|
return;
|
|
1378
1446
|
}
|
|
1447
|
+
const platformName = manifest.product.name;
|
|
1448
|
+
const includeSuffix = await ctx.confirm(`Include "-service" suffix? (${platformName}-${serviceName}-service)`, true);
|
|
1449
|
+
const serviceNameSuffix = includeSuffix ? "service" : null;
|
|
1379
1450
|
const serviceDisplayName = await ctx.prompt("Service display name:");
|
|
1380
1451
|
await createServiceModuleService(
|
|
1381
|
-
{ applicationName, serviceName, serviceDisplayName },
|
|
1452
|
+
{ applicationName, serviceName, serviceDisplayName, serviceNameSuffix },
|
|
1382
1453
|
ctx
|
|
1383
1454
|
);
|
|
1384
1455
|
}
|
|
@@ -1394,14 +1465,33 @@ async function createUiModuleUiController(ctx) {
|
|
|
1394
1465
|
ctx.log("Error: Cannot create a UI module \u2014 no platform initialized in this directory.");
|
|
1395
1466
|
return;
|
|
1396
1467
|
}
|
|
1397
|
-
const
|
|
1398
|
-
if (
|
|
1399
|
-
ctx.log(
|
|
1468
|
+
const layout = await findPlatformLayout();
|
|
1469
|
+
if (!layout) {
|
|
1470
|
+
ctx.log("Error: Cannot create a UI module \u2014 no platform initialized in this directory.");
|
|
1400
1471
|
return;
|
|
1401
1472
|
}
|
|
1473
|
+
let manifest = await readManifest(layout.rootDir, layout.coreDirName);
|
|
1474
|
+
if (manifest.applications.length === 0) {
|
|
1475
|
+
ctx.log("No applications found in this platform.");
|
|
1476
|
+
const createFirst = await ctx.confirm("Would you like to create an application first?");
|
|
1477
|
+
if (!createFirst) return;
|
|
1478
|
+
await createApplicationUiController(ctx);
|
|
1479
|
+
manifest = await readManifest(layout.rootDir, layout.coreDirName);
|
|
1480
|
+
if (manifest.applications.length === 0) {
|
|
1481
|
+
ctx.log("Error: No applications available after creation attempt.");
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
const applicationName = await ctx.select(
|
|
1486
|
+
"Select application:",
|
|
1487
|
+
manifest.applications.map((a) => ({ label: `${a.name} \u2014 ${a.displayName}`, value: a.name }))
|
|
1488
|
+
);
|
|
1489
|
+
const platformName = manifest.product.name;
|
|
1490
|
+
const includeSuffix = await ctx.confirm(`Include "-ui" suffix? (${platformName}-${applicationName}-ui)`, true);
|
|
1491
|
+
const uiModuleSuffix = includeSuffix ? "ui" : null;
|
|
1402
1492
|
const applicationDisplayName = await ctx.prompt("Application display name:");
|
|
1403
1493
|
await createUiModuleService(
|
|
1404
|
-
{ applicationName, applicationDisplayName },
|
|
1494
|
+
{ applicationName, applicationDisplayName, uiModuleSuffix },
|
|
1405
1495
|
ctx
|
|
1406
1496
|
);
|
|
1407
1497
|
}
|
|
@@ -1416,16 +1506,20 @@ var uiControllers = /* @__PURE__ */ new Map([
|
|
|
1416
1506
|
]);
|
|
1417
1507
|
|
|
1418
1508
|
// src/hooks/use-command-runner.ts
|
|
1419
|
-
function useCommandRunner({ appendStaticItem, setState }) {
|
|
1509
|
+
function useCommandRunner({ appendStaticItem, setState, onCommandComplete }) {
|
|
1420
1510
|
const outputCountRef = useRef(0);
|
|
1421
1511
|
const abortControllerRef = useRef(null);
|
|
1422
1512
|
const promptResolveRef = useRef(null);
|
|
1423
1513
|
const [promptMessage, setPromptMessage] = useState2("");
|
|
1424
1514
|
const [promptValue, setPromptValue] = useState2("");
|
|
1515
|
+
const [promptMode, setPromptMode] = useState2({ kind: "text" });
|
|
1516
|
+
const [selectIndex, setSelectIndex] = useState2(0);
|
|
1517
|
+
const [confirmValue, setConfirmValue] = useState2(true);
|
|
1425
1518
|
const abortExecution = useCallback(() => {
|
|
1426
1519
|
abortControllerRef.current?.abort();
|
|
1427
1520
|
abortControllerRef.current = null;
|
|
1428
1521
|
promptResolveRef.current = null;
|
|
1522
|
+
setPromptMode({ kind: "text" });
|
|
1429
1523
|
}, []);
|
|
1430
1524
|
const runCommand2 = useCallback(
|
|
1431
1525
|
(cmd) => {
|
|
@@ -1440,14 +1534,15 @@ function useCommandRunner({ appendStaticItem, setState }) {
|
|
|
1440
1534
|
appendStaticItem({ kind: "output", id, line: message });
|
|
1441
1535
|
}
|
|
1442
1536
|
},
|
|
1443
|
-
prompt(message) {
|
|
1537
|
+
prompt(message, defaultValue) {
|
|
1444
1538
|
return new Promise((resolve5, reject) => {
|
|
1445
1539
|
if (controller.signal.aborted) {
|
|
1446
1540
|
reject(new DOMException("Aborted", "AbortError"));
|
|
1447
1541
|
return;
|
|
1448
1542
|
}
|
|
1449
1543
|
setPromptMessage(message);
|
|
1450
|
-
setPromptValue("");
|
|
1544
|
+
setPromptValue(defaultValue ?? "");
|
|
1545
|
+
setPromptMode({ kind: "text" });
|
|
1451
1546
|
promptResolveRef.current = resolve5;
|
|
1452
1547
|
setState(APP_STATE.PROMPTING);
|
|
1453
1548
|
controller.signal.addEventListener(
|
|
@@ -1456,6 +1551,42 @@ function useCommandRunner({ appendStaticItem, setState }) {
|
|
|
1456
1551
|
{ once: true }
|
|
1457
1552
|
);
|
|
1458
1553
|
});
|
|
1554
|
+
},
|
|
1555
|
+
select(message, options) {
|
|
1556
|
+
return new Promise((resolve5, reject) => {
|
|
1557
|
+
if (controller.signal.aborted) {
|
|
1558
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
setPromptMessage(message);
|
|
1562
|
+
setPromptMode({ kind: "select", options });
|
|
1563
|
+
setSelectIndex(0);
|
|
1564
|
+
promptResolveRef.current = resolve5;
|
|
1565
|
+
setState(APP_STATE.PROMPTING);
|
|
1566
|
+
controller.signal.addEventListener(
|
|
1567
|
+
"abort",
|
|
1568
|
+
() => reject(new DOMException("Aborted", "AbortError")),
|
|
1569
|
+
{ once: true }
|
|
1570
|
+
);
|
|
1571
|
+
});
|
|
1572
|
+
},
|
|
1573
|
+
confirm(message, defaultValue) {
|
|
1574
|
+
return new Promise((resolve5, reject) => {
|
|
1575
|
+
if (controller.signal.aborted) {
|
|
1576
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
setPromptMessage(message);
|
|
1580
|
+
setPromptMode({ kind: "confirm" });
|
|
1581
|
+
setConfirmValue(defaultValue ?? true);
|
|
1582
|
+
promptResolveRef.current = (value) => resolve5(value === "true");
|
|
1583
|
+
setState(APP_STATE.PROMPTING);
|
|
1584
|
+
controller.signal.addEventListener(
|
|
1585
|
+
"abort",
|
|
1586
|
+
() => reject(new DOMException("Aborted", "AbortError")),
|
|
1587
|
+
{ once: true }
|
|
1588
|
+
);
|
|
1589
|
+
});
|
|
1459
1590
|
}
|
|
1460
1591
|
};
|
|
1461
1592
|
const uiController = uiControllers.get(cmd.name);
|
|
@@ -1468,20 +1599,23 @@ function useCommandRunner({ appendStaticItem, setState }) {
|
|
|
1468
1599
|
uiController(ctx).then(() => {
|
|
1469
1600
|
if (!controller.signal.aborted) {
|
|
1470
1601
|
setState(APP_STATE.IDLE);
|
|
1602
|
+
onCommandComplete?.();
|
|
1471
1603
|
}
|
|
1472
1604
|
}).catch((err) => {
|
|
1473
1605
|
if (controller.signal.aborted) return;
|
|
1474
1606
|
const id = `output-${outputCountRef.current++}`;
|
|
1475
1607
|
appendStaticItem({ kind: "output", id, line: `Error: ${formatError(err)}` });
|
|
1476
1608
|
setState(APP_STATE.IDLE);
|
|
1609
|
+
onCommandComplete?.();
|
|
1477
1610
|
});
|
|
1478
1611
|
},
|
|
1479
|
-
[appendStaticItem, setState]
|
|
1612
|
+
[appendStaticItem, setState, onCommandComplete]
|
|
1480
1613
|
);
|
|
1481
1614
|
const handlePromptSubmit = useCallback(
|
|
1482
1615
|
(value) => {
|
|
1483
1616
|
const resolve5 = promptResolveRef.current;
|
|
1484
1617
|
promptResolveRef.current = null;
|
|
1618
|
+
setPromptMode({ kind: "text" });
|
|
1485
1619
|
setState(APP_STATE.EXECUTING);
|
|
1486
1620
|
resolve5?.(value);
|
|
1487
1621
|
},
|
|
@@ -1493,12 +1627,17 @@ function useCommandRunner({ appendStaticItem, setState }) {
|
|
|
1493
1627
|
abortExecution,
|
|
1494
1628
|
promptMessage,
|
|
1495
1629
|
promptValue,
|
|
1496
|
-
setPromptValue
|
|
1630
|
+
setPromptValue,
|
|
1631
|
+
promptMode,
|
|
1632
|
+
selectIndex,
|
|
1633
|
+
setSelectIndex,
|
|
1634
|
+
confirmValue,
|
|
1635
|
+
setConfirmValue
|
|
1497
1636
|
};
|
|
1498
1637
|
}
|
|
1499
1638
|
|
|
1500
1639
|
// src/app.tsx
|
|
1501
|
-
import { jsx as
|
|
1640
|
+
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1502
1641
|
var require2 = createRequire(import.meta.url);
|
|
1503
1642
|
var { version } = require2("../package.json");
|
|
1504
1643
|
function App() {
|
|
@@ -1507,12 +1646,33 @@ function App() {
|
|
|
1507
1646
|
const [inputValue, setInputValue] = useState3("");
|
|
1508
1647
|
const [selectedIndex, setSelectedIndex] = useState3(0);
|
|
1509
1648
|
const [staticItems, setStaticItems] = useState3([{ kind: "banner", id: "banner" }]);
|
|
1649
|
+
const [platformInitialized, setPlatformInitialized] = useState3(false);
|
|
1650
|
+
useEffect2(() => {
|
|
1651
|
+
isPlatformInitialized().then(setPlatformInitialized).catch(() => {
|
|
1652
|
+
});
|
|
1653
|
+
}, []);
|
|
1510
1654
|
const appendStaticItem = useCallback2((item) => {
|
|
1511
1655
|
setStaticItems((prev) => [...prev, item]);
|
|
1512
1656
|
}, []);
|
|
1513
|
-
const
|
|
1657
|
+
const handleCommandComplete = useCallback2(() => {
|
|
1658
|
+
isPlatformInitialized().then(setPlatformInitialized).catch(() => {
|
|
1659
|
+
});
|
|
1660
|
+
}, []);
|
|
1661
|
+
const {
|
|
1662
|
+
runCommand: runCommand2,
|
|
1663
|
+
handlePromptSubmit,
|
|
1664
|
+
abortExecution,
|
|
1665
|
+
promptMessage,
|
|
1666
|
+
promptValue,
|
|
1667
|
+
setPromptValue,
|
|
1668
|
+
promptMode,
|
|
1669
|
+
selectIndex,
|
|
1670
|
+
setSelectIndex,
|
|
1671
|
+
confirmValue,
|
|
1672
|
+
setConfirmValue
|
|
1673
|
+
} = useCommandRunner({ appendStaticItem, setState, onCommandComplete: handleCommandComplete });
|
|
1514
1674
|
const query = inputValue.startsWith("/") ? inputValue.slice(1) : "";
|
|
1515
|
-
const filteredCommands = registry.
|
|
1675
|
+
const filteredCommands = registry.searchVisible(query, { platformInitialized });
|
|
1516
1676
|
useInput(
|
|
1517
1677
|
(input, key) => {
|
|
1518
1678
|
if (key.ctrl && input === "c") {
|
|
@@ -1524,6 +1684,40 @@ function App() {
|
|
|
1524
1684
|
setState(APP_STATE.IDLE);
|
|
1525
1685
|
return;
|
|
1526
1686
|
}
|
|
1687
|
+
if (state === APP_STATE.PROMPTING) {
|
|
1688
|
+
if (promptMode.kind === "select") {
|
|
1689
|
+
if (key.upArrow) {
|
|
1690
|
+
setSelectIndex((prev) => Math.max(0, prev - 1));
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
if (key.downArrow) {
|
|
1694
|
+
setSelectIndex((prev) => Math.min(promptMode.options.length - 1, prev + 1));
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1697
|
+
if (key.return) {
|
|
1698
|
+
const selected = promptMode.options[selectIndex];
|
|
1699
|
+
if (selected) handlePromptSubmit(selected.value);
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1702
|
+
return;
|
|
1703
|
+
}
|
|
1704
|
+
if (promptMode.kind === "confirm") {
|
|
1705
|
+
if (key.leftArrow || key.upArrow) {
|
|
1706
|
+
setConfirmValue(true);
|
|
1707
|
+
return;
|
|
1708
|
+
}
|
|
1709
|
+
if (key.rightArrow || key.downArrow) {
|
|
1710
|
+
setConfirmValue(false);
|
|
1711
|
+
return;
|
|
1712
|
+
}
|
|
1713
|
+
if (key.return) {
|
|
1714
|
+
handlePromptSubmit(confirmValue ? "true" : "false");
|
|
1715
|
+
return;
|
|
1716
|
+
}
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1527
1721
|
if (state === APP_STATE.PALETTE) {
|
|
1528
1722
|
if (key.escape) {
|
|
1529
1723
|
setState(APP_STATE.IDLE);
|
|
@@ -1585,17 +1779,19 @@ function App() {
|
|
|
1585
1779
|
function renderActiveArea() {
|
|
1586
1780
|
switch (state) {
|
|
1587
1781
|
case APP_STATE.EXECUTING:
|
|
1588
|
-
return /* @__PURE__ */
|
|
1589
|
-
/* @__PURE__ */
|
|
1590
|
-
/* @__PURE__ */
|
|
1782
|
+
return /* @__PURE__ */ jsxs7(Box6, { flexDirection: "row", gap: 1, children: [
|
|
1783
|
+
/* @__PURE__ */ jsx9(Spinner, { label: "Running\u2026" }),
|
|
1784
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "(esc to cancel)" })
|
|
1591
1785
|
] });
|
|
1592
1786
|
case APP_STATE.PROMPTING:
|
|
1593
|
-
return /* @__PURE__ */
|
|
1594
|
-
/* @__PURE__ */
|
|
1595
|
-
/* @__PURE__ */
|
|
1787
|
+
return /* @__PURE__ */ jsxs7(Box6, { flexDirection: "column", children: [
|
|
1788
|
+
/* @__PURE__ */ jsx9(Text9, { color: "cyan", children: promptMessage }),
|
|
1789
|
+
promptMode.kind === "select" && /* @__PURE__ */ jsx9(SelectList, { options: promptMode.options, selectedIndex: selectIndex }),
|
|
1790
|
+
promptMode.kind === "confirm" && /* @__PURE__ */ jsx9(ConfirmPrompt, { value: confirmValue }),
|
|
1791
|
+
promptMode.kind === "text" && /* @__PURE__ */ jsx9(Prompt, { value: promptValue, onChange: setPromptValue, onSubmit: handlePromptSubmit })
|
|
1596
1792
|
] });
|
|
1597
1793
|
default:
|
|
1598
|
-
return /* @__PURE__ */
|
|
1794
|
+
return /* @__PURE__ */ jsx9(
|
|
1599
1795
|
Prompt,
|
|
1600
1796
|
{
|
|
1601
1797
|
value: inputValue,
|
|
@@ -1608,10 +1804,10 @@ function App() {
|
|
|
1608
1804
|
);
|
|
1609
1805
|
}
|
|
1610
1806
|
}
|
|
1611
|
-
return /* @__PURE__ */
|
|
1612
|
-
/* @__PURE__ */
|
|
1807
|
+
return /* @__PURE__ */ jsxs7(Box6, { flexDirection: "column", children: [
|
|
1808
|
+
/* @__PURE__ */ jsx9(ScrollbackHistory, { items: staticItems, version }),
|
|
1613
1809
|
renderActiveArea(),
|
|
1614
|
-
state === APP_STATE.PALETTE && /* @__PURE__ */
|
|
1810
|
+
state === APP_STATE.PALETTE && /* @__PURE__ */ jsx9(CommandPalette, { commands: filteredCommands, selectedIndex })
|
|
1615
1811
|
] });
|
|
1616
1812
|
}
|
|
1617
1813
|
|
|
@@ -1654,13 +1850,13 @@ async function initCliController(args2) {
|
|
|
1654
1850
|
console.error("Error: Cannot initialize a new platform \u2014 a platform is already initialized in this directory.");
|
|
1655
1851
|
process.exit(1);
|
|
1656
1852
|
}
|
|
1657
|
-
const { organizationName, platformName, platformDisplayName } = args2;
|
|
1853
|
+
const { organizationName, platformName, platformDisplayName, bootstrapServiceSuffix, customizationUiSuffix } = args2;
|
|
1658
1854
|
if (!organizationName || !platformName || !platformDisplayName) {
|
|
1659
1855
|
console.error("Error: organizationName, platformName, and platformDisplayName are required.");
|
|
1660
1856
|
process.exit(1);
|
|
1661
1857
|
}
|
|
1662
1858
|
await initService(
|
|
1663
|
-
{ organizationName, platformName, platformDisplayName },
|
|
1859
|
+
{ organizationName, platformName, platformDisplayName, bootstrapServiceSuffix, customizationUiSuffix },
|
|
1664
1860
|
{ log: console.log }
|
|
1665
1861
|
);
|
|
1666
1862
|
}
|
|
@@ -2132,10 +2328,10 @@ function parseKeyValueArgs(args2) {
|
|
|
2132
2328
|
}
|
|
2133
2329
|
|
|
2134
2330
|
// src/index.tsx
|
|
2135
|
-
import { jsx as
|
|
2331
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
2136
2332
|
var args = process.argv.slice(2);
|
|
2137
2333
|
if (args.length === 0) {
|
|
2138
|
-
render(/* @__PURE__ */
|
|
2334
|
+
render(/* @__PURE__ */ jsx10(App, {}));
|
|
2139
2335
|
} else {
|
|
2140
2336
|
const commandName = args[0];
|
|
2141
2337
|
const params = parseKeyValueArgs(args.slice(1));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bluealba/platform-cli",
|
|
3
|
-
"version": "0.3.1-feature-platform-cli-
|
|
3
|
+
"version": "0.3.1-feature-platform-cli-224",
|
|
4
4
|
"description": "Blue Alba Platform CLI",
|
|
5
5
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
6
6
|
"type": "module",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"scripts": {
|
|
15
15
|
"build": "tsup",
|
|
16
16
|
"dev": "tsup --watch",
|
|
17
|
-
"start": "node dist/index.js"
|
|
17
|
+
"start": "node dist/index.js",
|
|
18
|
+
"install-local": "bash scripts/install-local.sh"
|
|
18
19
|
},
|
|
19
20
|
"dependencies": {
|
|
20
21
|
"chalk": "^5.3.0",
|