@bluealba/platform-cli 0.3.1-feature-platform-cli-224 → 0.3.1-feature-platform-cli-225
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 +826 -430
- package/package.json +1 -1
- package/templates/bootstrap-service-template/Dockerfile +1 -1
- package/templates/bootstrap-service-template/Dockerfile.development +1 -1
- package/templates/bootstrap-service-template/README.md +1 -1
- package/templates/bootstrap-service-template/package.json +1 -1
- package/templates/platform-init-template/{{platformName}}-core/local/{{platformName}}-core-docker-compose.yml +5 -5
package/dist/index.js
CHANGED
|
@@ -279,7 +279,12 @@ async function scaffoldBootstrap(bootstrapServiceDir, organizationName, bootstra
|
|
|
279
279
|
{
|
|
280
280
|
templateDir: bootstrapTemplateDir,
|
|
281
281
|
outputDir: bootstrapServiceDir,
|
|
282
|
-
variables: {
|
|
282
|
+
variables: {
|
|
283
|
+
organizationName,
|
|
284
|
+
bootstrapName,
|
|
285
|
+
bootstrapServiceName: `${bootstrapName}-bootstrap-service`,
|
|
286
|
+
bootstrapServiceDir: bootstrapServiceDir_var
|
|
287
|
+
},
|
|
283
288
|
exclude: ["src/data/shared-libraries.json", "src/data/platform/modules-config.json"]
|
|
284
289
|
},
|
|
285
290
|
(message) => logger.log(message)
|
|
@@ -839,6 +844,8 @@ async function init(params, logger) {
|
|
|
839
844
|
platformTitle,
|
|
840
845
|
platformDisplayName,
|
|
841
846
|
bootstrapName: platformName,
|
|
847
|
+
bootstrapServiceName,
|
|
848
|
+
customizationUiName,
|
|
842
849
|
bootstrapServiceDir: `${coreDirName}/services`
|
|
843
850
|
};
|
|
844
851
|
try {
|
|
@@ -1255,6 +1262,267 @@ async function createUiModule(params, logger) {
|
|
|
1255
1262
|
logger.log(`Done! UI module "${uiName}" added to application "${applicationName}".`);
|
|
1256
1263
|
}
|
|
1257
1264
|
|
|
1265
|
+
// src/commands/status/status-checks.ts
|
|
1266
|
+
import { spawn } from "child_process";
|
|
1267
|
+
import { access as access5 } from "fs/promises";
|
|
1268
|
+
import { join as join22, resolve as resolve4 } from "path";
|
|
1269
|
+
function spawnCapture(cmd, args2, cwd5) {
|
|
1270
|
+
return new Promise((resolvePromise) => {
|
|
1271
|
+
const child = spawn(cmd, args2, {
|
|
1272
|
+
cwd: cwd5,
|
|
1273
|
+
shell: false,
|
|
1274
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1275
|
+
});
|
|
1276
|
+
let stdout = "";
|
|
1277
|
+
let stderr = "";
|
|
1278
|
+
child.stdout.on("data", (data) => {
|
|
1279
|
+
stdout += data.toString();
|
|
1280
|
+
});
|
|
1281
|
+
child.stderr.on("data", (data) => {
|
|
1282
|
+
stderr += data.toString();
|
|
1283
|
+
});
|
|
1284
|
+
child.on("close", (code) => {
|
|
1285
|
+
resolvePromise({ code: code ?? 1, stdout, stderr });
|
|
1286
|
+
});
|
|
1287
|
+
child.on("error", () => {
|
|
1288
|
+
resolvePromise({ code: 1, stdout, stderr });
|
|
1289
|
+
});
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
async function pathExists(p) {
|
|
1293
|
+
try {
|
|
1294
|
+
await access5(p);
|
|
1295
|
+
return true;
|
|
1296
|
+
} catch {
|
|
1297
|
+
return false;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
async function checkNodeVersion() {
|
|
1301
|
+
const { code, stdout } = await spawnCapture("node", ["--version"]);
|
|
1302
|
+
if (code !== 0 || !stdout.trim()) {
|
|
1303
|
+
return { name: "Node.js", available: false, version: null, versionOk: false, detail: "not found in PATH" };
|
|
1304
|
+
}
|
|
1305
|
+
const version2 = stdout.trim();
|
|
1306
|
+
const match = version2.match(/^v(\d+)/);
|
|
1307
|
+
const major = match ? parseInt(match[1], 10) : 0;
|
|
1308
|
+
const versionOk = major >= 22;
|
|
1309
|
+
return {
|
|
1310
|
+
name: "Node.js",
|
|
1311
|
+
available: true,
|
|
1312
|
+
version: version2,
|
|
1313
|
+
versionOk,
|
|
1314
|
+
detail: versionOk ? void 0 : `>=22 required, found ${version2}`
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1317
|
+
async function checkDocker() {
|
|
1318
|
+
const { code, stdout } = await spawnCapture("docker", ["info", "--format", "{{.ServerVersion}}"]);
|
|
1319
|
+
if (code === 0 && stdout.trim()) {
|
|
1320
|
+
return { name: "Docker", available: true, version: stdout.trim(), versionOk: true };
|
|
1321
|
+
}
|
|
1322
|
+
const { code: vCode, stdout: vOut } = await spawnCapture("docker", ["--version"]);
|
|
1323
|
+
if (vCode === 0 && vOut.trim()) {
|
|
1324
|
+
return {
|
|
1325
|
+
name: "Docker",
|
|
1326
|
+
available: false,
|
|
1327
|
+
version: null,
|
|
1328
|
+
versionOk: false,
|
|
1329
|
+
detail: "installed but daemon is not running"
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
return { name: "Docker", available: false, version: null, versionOk: false, detail: "not found in PATH" };
|
|
1333
|
+
}
|
|
1334
|
+
async function checkInstalled(layout, manifest) {
|
|
1335
|
+
const results = [];
|
|
1336
|
+
const coreCheck = {
|
|
1337
|
+
name: layout.coreDirName,
|
|
1338
|
+
path: join22(layout.coreDir, "node_modules"),
|
|
1339
|
+
exists: await pathExists(join22(layout.coreDir, "node_modules"))
|
|
1340
|
+
};
|
|
1341
|
+
results.push(coreCheck);
|
|
1342
|
+
for (const app of manifest.applications) {
|
|
1343
|
+
const appDir = resolve4(layout.coreDir, app.localPath);
|
|
1344
|
+
const nodeModulesPath = join22(appDir, "node_modules");
|
|
1345
|
+
results.push({
|
|
1346
|
+
name: app.name,
|
|
1347
|
+
path: nodeModulesPath,
|
|
1348
|
+
exists: await pathExists(nodeModulesPath)
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
return results;
|
|
1352
|
+
}
|
|
1353
|
+
async function checkBuilt(layout, manifest) {
|
|
1354
|
+
const results = [];
|
|
1355
|
+
const coreTurboPath = join22(layout.coreDir, ".turbo");
|
|
1356
|
+
results.push({
|
|
1357
|
+
name: layout.coreDirName,
|
|
1358
|
+
path: coreTurboPath,
|
|
1359
|
+
exists: await pathExists(coreTurboPath)
|
|
1360
|
+
});
|
|
1361
|
+
for (const app of manifest.applications) {
|
|
1362
|
+
const appDir = resolve4(layout.coreDir, app.localPath);
|
|
1363
|
+
const appTurboPath = join22(appDir, ".turbo");
|
|
1364
|
+
results.push({
|
|
1365
|
+
name: app.name,
|
|
1366
|
+
path: appTurboPath,
|
|
1367
|
+
exists: await pathExists(appTurboPath)
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1370
|
+
return results;
|
|
1371
|
+
}
|
|
1372
|
+
async function resolveComposeFiles(layout, manifest) {
|
|
1373
|
+
const { localDir, coreDirName } = layout;
|
|
1374
|
+
const platformName = manifest.product.name;
|
|
1375
|
+
const files = [join22(localDir, "platform-docker-compose.yml")];
|
|
1376
|
+
const prefixedCore = join22(localDir, `${coreDirName}-docker-compose.yml`);
|
|
1377
|
+
const unprefixedCore = join22(localDir, "core-docker-compose.yml");
|
|
1378
|
+
files.push(await pathExists(prefixedCore) ? prefixedCore : unprefixedCore);
|
|
1379
|
+
for (const app of manifest.applications) {
|
|
1380
|
+
const prefixed = join22(localDir, `${platformName}-${app.name}-docker-compose.yml`);
|
|
1381
|
+
const unprefixed = join22(localDir, `${app.name}-docker-compose.yml`);
|
|
1382
|
+
if (await pathExists(prefixed)) files.push(prefixed);
|
|
1383
|
+
else if (await pathExists(unprefixed)) files.push(unprefixed);
|
|
1384
|
+
}
|
|
1385
|
+
return files;
|
|
1386
|
+
}
|
|
1387
|
+
async function checkContainers(layout, manifest) {
|
|
1388
|
+
const platformName = manifest.product.name;
|
|
1389
|
+
const projectName = `${platformName}-platform`;
|
|
1390
|
+
const composeFiles = await resolveComposeFiles(layout, manifest);
|
|
1391
|
+
const fileArgs = composeFiles.flatMap((f) => ["-f", f]);
|
|
1392
|
+
const { code: cfgCode, stdout: cfgOut } = await spawnCapture(
|
|
1393
|
+
"docker",
|
|
1394
|
+
["compose", "-p", projectName, ...fileArgs, "config", "--services"],
|
|
1395
|
+
layout.rootDir
|
|
1396
|
+
);
|
|
1397
|
+
const expectedServices = cfgCode === 0 ? cfgOut.trim().split("\n").map((s) => s.trim()).filter(Boolean) : [];
|
|
1398
|
+
const { stdout: psOut } = await spawnCapture(
|
|
1399
|
+
"docker",
|
|
1400
|
+
["compose", "-p", projectName, ...fileArgs, "ps", "--format", "json"],
|
|
1401
|
+
layout.rootDir
|
|
1402
|
+
);
|
|
1403
|
+
const runningByService = /* @__PURE__ */ new Map();
|
|
1404
|
+
for (const line of psOut.trim().split("\n")) {
|
|
1405
|
+
const trimmed = line.trim();
|
|
1406
|
+
if (!trimmed) continue;
|
|
1407
|
+
try {
|
|
1408
|
+
const obj = JSON.parse(trimmed);
|
|
1409
|
+
const service = String(obj["Service"] ?? obj["Name"] ?? "");
|
|
1410
|
+
if (service) {
|
|
1411
|
+
runningByService.set(service, {
|
|
1412
|
+
state: String(obj["State"] ?? "unknown").toLowerCase(),
|
|
1413
|
+
ports: String(obj["Publishers"] ? formatPublishers(obj["Publishers"]) : obj["Ports"] ?? "")
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1416
|
+
} catch {
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
if (expectedServices.length > 0) {
|
|
1420
|
+
return expectedServices.map((service) => {
|
|
1421
|
+
const actual = runningByService.get(service);
|
|
1422
|
+
return {
|
|
1423
|
+
name: service,
|
|
1424
|
+
state: actual ? actual.state : "not started",
|
|
1425
|
+
ports: actual ? actual.ports : ""
|
|
1426
|
+
};
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
return Array.from(runningByService.entries()).map(([name, info]) => ({
|
|
1430
|
+
name,
|
|
1431
|
+
...info
|
|
1432
|
+
}));
|
|
1433
|
+
}
|
|
1434
|
+
function formatPublishers(publishers) {
|
|
1435
|
+
if (!Array.isArray(publishers)) return "";
|
|
1436
|
+
return publishers.map((p) => {
|
|
1437
|
+
if (typeof p !== "object" || p === null) return "";
|
|
1438
|
+
const pub = p;
|
|
1439
|
+
const host = pub["URL"] ?? "0.0.0.0";
|
|
1440
|
+
const published = pub["PublishedPort"];
|
|
1441
|
+
const target = pub["TargetPort"];
|
|
1442
|
+
const proto = pub["Protocol"] ?? "tcp";
|
|
1443
|
+
if (!published || !target) return "";
|
|
1444
|
+
return `${host}:${published}->${target}/${proto}`;
|
|
1445
|
+
}).filter(Boolean).join(", ");
|
|
1446
|
+
}
|
|
1447
|
+
function computeNextStep(result) {
|
|
1448
|
+
if (!result.lifecycle.initialized) return "init";
|
|
1449
|
+
if (!result.lifecycle.installed) return "install";
|
|
1450
|
+
if (!result.lifecycle.built) return "build";
|
|
1451
|
+
if (!result.lifecycle.running) return "start";
|
|
1452
|
+
return null;
|
|
1453
|
+
}
|
|
1454
|
+
async function gatherStatus() {
|
|
1455
|
+
const layout = await findPlatformLayout();
|
|
1456
|
+
const [nodeCheck, dockerCheck] = await Promise.all([checkNodeVersion(), checkDocker()]);
|
|
1457
|
+
const prerequisites = [nodeCheck, dockerCheck];
|
|
1458
|
+
if (!layout) {
|
|
1459
|
+
return {
|
|
1460
|
+
projectInfo: null,
|
|
1461
|
+
prerequisites,
|
|
1462
|
+
lifecycle: {
|
|
1463
|
+
initialized: false,
|
|
1464
|
+
installed: false,
|
|
1465
|
+
installedDetails: [],
|
|
1466
|
+
built: false,
|
|
1467
|
+
builtDetails: [],
|
|
1468
|
+
running: false
|
|
1469
|
+
},
|
|
1470
|
+
containers: []
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
let manifest;
|
|
1474
|
+
try {
|
|
1475
|
+
manifest = await readManifest(layout.rootDir, layout.coreDirName);
|
|
1476
|
+
} catch {
|
|
1477
|
+
return {
|
|
1478
|
+
projectInfo: null,
|
|
1479
|
+
prerequisites,
|
|
1480
|
+
lifecycle: {
|
|
1481
|
+
initialized: true,
|
|
1482
|
+
installed: false,
|
|
1483
|
+
installedDetails: [],
|
|
1484
|
+
built: false,
|
|
1485
|
+
builtDetails: [],
|
|
1486
|
+
running: false
|
|
1487
|
+
},
|
|
1488
|
+
containers: []
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
const [installedDetails, builtDetails, containers] = await Promise.all([
|
|
1492
|
+
checkInstalled(layout, manifest),
|
|
1493
|
+
checkBuilt(layout, manifest),
|
|
1494
|
+
checkContainers(layout, manifest)
|
|
1495
|
+
]);
|
|
1496
|
+
const projectInfo = {
|
|
1497
|
+
productName: manifest.product.name,
|
|
1498
|
+
displayName: manifest.product.displayName,
|
|
1499
|
+
organization: manifest.product.organization,
|
|
1500
|
+
scope: manifest.product.scope,
|
|
1501
|
+
applicationCount: manifest.applications.length,
|
|
1502
|
+
applicationNames: manifest.applications.map((a) => a.name)
|
|
1503
|
+
};
|
|
1504
|
+
return {
|
|
1505
|
+
projectInfo,
|
|
1506
|
+
prerequisites,
|
|
1507
|
+
lifecycle: {
|
|
1508
|
+
initialized: true,
|
|
1509
|
+
installed: installedDetails.every((d) => d.exists),
|
|
1510
|
+
installedDetails,
|
|
1511
|
+
built: builtDetails.every((d) => d.exists),
|
|
1512
|
+
builtDetails,
|
|
1513
|
+
running: containers.length > 0 && containers.every((c) => c.state === "running")
|
|
1514
|
+
},
|
|
1515
|
+
containers
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
// src/commands/status/status.command.ts
|
|
1520
|
+
var STATUS_COMMAND_NAME = "status";
|
|
1521
|
+
var statusCommand = {
|
|
1522
|
+
name: STATUS_COMMAND_NAME,
|
|
1523
|
+
description: "Show platform status and health checks"
|
|
1524
|
+
};
|
|
1525
|
+
|
|
1258
1526
|
// src/commands/registry.ts
|
|
1259
1527
|
var CommandRegistry = class {
|
|
1260
1528
|
commands = /* @__PURE__ */ new Map();
|
|
@@ -1290,6 +1558,7 @@ registry.register(initCommand);
|
|
|
1290
1558
|
registry.register(configureIdpCommand);
|
|
1291
1559
|
registry.register(createServiceModuleCommand);
|
|
1292
1560
|
registry.register(createUiModuleCommand);
|
|
1561
|
+
registry.register(statusCommand);
|
|
1293
1562
|
|
|
1294
1563
|
// src/app-state.ts
|
|
1295
1564
|
var APP_STATE = {
|
|
@@ -1496,410 +1765,96 @@ async function createUiModuleUiController(ctx) {
|
|
|
1496
1765
|
);
|
|
1497
1766
|
}
|
|
1498
1767
|
|
|
1499
|
-
// src/
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1768
|
+
// src/services/status.service.ts
|
|
1769
|
+
async function statusService() {
|
|
1770
|
+
return gatherStatus();
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
// src/utils/theme.ts
|
|
1774
|
+
import chalk from "chalk";
|
|
1775
|
+
var theme = {
|
|
1776
|
+
prompt: chalk.green,
|
|
1777
|
+
commandName: chalk.cyan,
|
|
1778
|
+
commandDescription: chalk.gray,
|
|
1779
|
+
selected: chalk.bgBlue.white,
|
|
1780
|
+
output: chalk.white,
|
|
1781
|
+
muted: chalk.dim,
|
|
1782
|
+
error: chalk.red,
|
|
1783
|
+
success: chalk.green,
|
|
1784
|
+
warning: chalk.yellow,
|
|
1785
|
+
info: chalk.cyan,
|
|
1786
|
+
section: chalk.bold.white,
|
|
1787
|
+
label: chalk.dim
|
|
1788
|
+
};
|
|
1507
1789
|
|
|
1508
|
-
// src/
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
const
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
}
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
1541
|
-
return;
|
|
1542
|
-
}
|
|
1543
|
-
setPromptMessage(message);
|
|
1544
|
-
setPromptValue(defaultValue ?? "");
|
|
1545
|
-
setPromptMode({ kind: "text" });
|
|
1546
|
-
promptResolveRef.current = resolve5;
|
|
1547
|
-
setState(APP_STATE.PROMPTING);
|
|
1548
|
-
controller.signal.addEventListener(
|
|
1549
|
-
"abort",
|
|
1550
|
-
() => reject(new DOMException("Aborted", "AbortError")),
|
|
1551
|
-
{ once: true }
|
|
1552
|
-
);
|
|
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
|
-
});
|
|
1590
|
-
}
|
|
1591
|
-
};
|
|
1592
|
-
const uiController = uiControllers.get(cmd.name);
|
|
1593
|
-
if (!uiController) {
|
|
1594
|
-
const id = `output-${outputCountRef.current++}`;
|
|
1595
|
-
appendStaticItem({ kind: "output", id, line: `Error: No controller found for "${cmd.name}"` });
|
|
1596
|
-
setState(APP_STATE.IDLE);
|
|
1597
|
-
return;
|
|
1790
|
+
// src/commands/status/status-formatter.ts
|
|
1791
|
+
var CHECK = theme.success("\u2713");
|
|
1792
|
+
var CROSS = theme.error("\u2717");
|
|
1793
|
+
function tick(ok) {
|
|
1794
|
+
return ok ? CHECK : CROSS;
|
|
1795
|
+
}
|
|
1796
|
+
function formatStatusLines(result) {
|
|
1797
|
+
const lines = [];
|
|
1798
|
+
if (result.projectInfo) {
|
|
1799
|
+
const p = result.projectInfo;
|
|
1800
|
+
lines.push(theme.info("\u25B8 ") + theme.section(p.displayName) + theme.label(` (${p.productName})`));
|
|
1801
|
+
lines.push(theme.label(" Organization: ") + p.organization + theme.label(` (${p.scope})`));
|
|
1802
|
+
const appList = p.applicationNames.length > 0 ? p.applicationNames.join(", ") : "(none)";
|
|
1803
|
+
lines.push(theme.label(" Applications: ") + `${p.applicationCount} \u2014 ` + appList);
|
|
1804
|
+
lines.push("");
|
|
1805
|
+
}
|
|
1806
|
+
lines.push(theme.section("Prerequisites"));
|
|
1807
|
+
for (const pre of result.prerequisites) {
|
|
1808
|
+
const ok = pre.available && pre.versionOk;
|
|
1809
|
+
const versionStr = pre.version ? theme.label(` ${pre.version}`) : "";
|
|
1810
|
+
const detailStr = pre.detail ? theme.warning(` \u2014 ${pre.detail}`) : "";
|
|
1811
|
+
lines.push(` ${tick(ok)} ${pre.name}${versionStr}${detailStr}`);
|
|
1812
|
+
}
|
|
1813
|
+
lines.push("");
|
|
1814
|
+
lines.push(theme.section("Lifecycle"));
|
|
1815
|
+
lines.push(` ${tick(result.lifecycle.initialized)} Platform initialized`);
|
|
1816
|
+
const installed = result.lifecycle.installed;
|
|
1817
|
+
lines.push(` ${tick(installed)} Dependencies installed`);
|
|
1818
|
+
if (!installed && result.lifecycle.installedDetails.length > 0) {
|
|
1819
|
+
for (const d of result.lifecycle.installedDetails) {
|
|
1820
|
+
if (!d.exists) {
|
|
1821
|
+
lines.push(` ${CROSS} ${theme.label(d.name)} ${theme.warning("(missing node_modules/)")}`);
|
|
1598
1822
|
}
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
const
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
[setState]
|
|
1623
|
-
);
|
|
1624
|
-
return {
|
|
1625
|
-
runCommand: runCommand2,
|
|
1626
|
-
handlePromptSubmit,
|
|
1627
|
-
abortExecution,
|
|
1628
|
-
promptMessage,
|
|
1629
|
-
promptValue,
|
|
1630
|
-
setPromptValue,
|
|
1631
|
-
promptMode,
|
|
1632
|
-
selectIndex,
|
|
1633
|
-
setSelectIndex,
|
|
1634
|
-
confirmValue,
|
|
1635
|
-
setConfirmValue
|
|
1636
|
-
};
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
const built = result.lifecycle.built;
|
|
1826
|
+
lines.push(` ${tick(built)} Build completed`);
|
|
1827
|
+
if (!built && result.lifecycle.builtDetails.length > 0) {
|
|
1828
|
+
for (const d of result.lifecycle.builtDetails) {
|
|
1829
|
+
if (!d.exists) {
|
|
1830
|
+
lines.push(` ${CROSS} ${theme.label(d.name)} ${theme.warning("(missing .turbo/)")}`);
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
lines.push(` ${tick(result.lifecycle.running)} Environment running`);
|
|
1835
|
+
if (result.containers.length > 0) {
|
|
1836
|
+
lines.push("");
|
|
1837
|
+
lines.push(theme.section("Containers"));
|
|
1838
|
+
for (const c of result.containers) {
|
|
1839
|
+
const ok = c.state === "running";
|
|
1840
|
+
const stateStr = ok ? theme.success(c.state) : c.state === "not started" ? theme.label(c.state) : theme.error(c.state);
|
|
1841
|
+
const ports = c.ports ? theme.label(` ${c.ports}`) : "";
|
|
1842
|
+
lines.push(` ${tick(ok)} ${c.name.padEnd(35)} ${stateStr}${ports}`);
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
return lines;
|
|
1637
1846
|
}
|
|
1638
1847
|
|
|
1639
|
-
// src/
|
|
1640
|
-
import {
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
function
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
const [platformInitialized, setPlatformInitialized] = useState3(false);
|
|
1650
|
-
useEffect2(() => {
|
|
1651
|
-
isPlatformInitialized().then(setPlatformInitialized).catch(() => {
|
|
1652
|
-
});
|
|
1653
|
-
}, []);
|
|
1654
|
-
const appendStaticItem = useCallback2((item) => {
|
|
1655
|
-
setStaticItems((prev) => [...prev, item]);
|
|
1656
|
-
}, []);
|
|
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 });
|
|
1674
|
-
const query = inputValue.startsWith("/") ? inputValue.slice(1) : "";
|
|
1675
|
-
const filteredCommands = registry.searchVisible(query, { platformInitialized });
|
|
1676
|
-
useInput(
|
|
1677
|
-
(input, key) => {
|
|
1678
|
-
if (key.ctrl && input === "c") {
|
|
1679
|
-
exit();
|
|
1680
|
-
return;
|
|
1681
|
-
}
|
|
1682
|
-
if ((state === APP_STATE.EXECUTING || state === APP_STATE.PROMPTING) && key.escape) {
|
|
1683
|
-
abortExecution();
|
|
1684
|
-
setState(APP_STATE.IDLE);
|
|
1685
|
-
return;
|
|
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
|
-
}
|
|
1721
|
-
if (state === APP_STATE.PALETTE) {
|
|
1722
|
-
if (key.escape) {
|
|
1723
|
-
setState(APP_STATE.IDLE);
|
|
1724
|
-
setInputValue("");
|
|
1725
|
-
return;
|
|
1726
|
-
}
|
|
1727
|
-
if (key.upArrow) {
|
|
1728
|
-
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
1729
|
-
return;
|
|
1730
|
-
}
|
|
1731
|
-
if (key.downArrow) {
|
|
1732
|
-
setSelectedIndex((prev) => Math.min(filteredCommands.length - 1, prev + 1));
|
|
1733
|
-
return;
|
|
1734
|
-
}
|
|
1735
|
-
if (key.return) {
|
|
1736
|
-
const cmd = filteredCommands[selectedIndex];
|
|
1737
|
-
if (cmd) {
|
|
1738
|
-
setInputValue("");
|
|
1739
|
-
runCommand2(cmd);
|
|
1740
|
-
}
|
|
1741
|
-
return;
|
|
1742
|
-
}
|
|
1743
|
-
if (key.backspace || key.delete) {
|
|
1744
|
-
setInputValue((prev) => {
|
|
1745
|
-
const next = prev.slice(0, -1);
|
|
1746
|
-
if (!next.startsWith("/")) {
|
|
1747
|
-
setState(APP_STATE.IDLE);
|
|
1748
|
-
return "";
|
|
1749
|
-
}
|
|
1750
|
-
return next;
|
|
1751
|
-
});
|
|
1752
|
-
setSelectedIndex(0);
|
|
1753
|
-
return;
|
|
1754
|
-
}
|
|
1755
|
-
if (input && !key.ctrl && !key.meta) {
|
|
1756
|
-
setInputValue((prev) => prev + input);
|
|
1757
|
-
setSelectedIndex(0);
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
},
|
|
1761
|
-
{ isActive: true }
|
|
1762
|
-
);
|
|
1763
|
-
const handleIdleInputChange = useCallback2((value) => {
|
|
1764
|
-
setInputValue(value);
|
|
1765
|
-
if (value.startsWith("/")) {
|
|
1766
|
-
setState(APP_STATE.PALETTE);
|
|
1767
|
-
setSelectedIndex(0);
|
|
1768
|
-
}
|
|
1769
|
-
}, []);
|
|
1770
|
-
const handleIdleSubmit = useCallback2(
|
|
1771
|
-
(value) => {
|
|
1772
|
-
if (!value.startsWith("/")) return;
|
|
1773
|
-
const name = value.slice(1).trim();
|
|
1774
|
-
const cmd = registry.get(name);
|
|
1775
|
-
if (cmd) runCommand2(cmd);
|
|
1776
|
-
},
|
|
1777
|
-
[runCommand2]
|
|
1778
|
-
);
|
|
1779
|
-
function renderActiveArea() {
|
|
1780
|
-
switch (state) {
|
|
1781
|
-
case APP_STATE.EXECUTING:
|
|
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)" })
|
|
1785
|
-
] });
|
|
1786
|
-
case APP_STATE.PROMPTING:
|
|
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 })
|
|
1792
|
-
] });
|
|
1793
|
-
default:
|
|
1794
|
-
return /* @__PURE__ */ jsx9(
|
|
1795
|
-
Prompt,
|
|
1796
|
-
{
|
|
1797
|
-
value: inputValue,
|
|
1798
|
-
onChange: state === APP_STATE.IDLE ? handleIdleInputChange : () => {
|
|
1799
|
-
},
|
|
1800
|
-
onSubmit: state === APP_STATE.IDLE ? handleIdleSubmit : () => {
|
|
1801
|
-
},
|
|
1802
|
-
disabled: state === APP_STATE.PALETTE
|
|
1803
|
-
}
|
|
1804
|
-
);
|
|
1805
|
-
}
|
|
1806
|
-
}
|
|
1807
|
-
return /* @__PURE__ */ jsxs7(Box6, { flexDirection: "column", children: [
|
|
1808
|
-
/* @__PURE__ */ jsx9(ScrollbackHistory, { items: staticItems, version }),
|
|
1809
|
-
renderActiveArea(),
|
|
1810
|
-
state === APP_STATE.PALETTE && /* @__PURE__ */ jsx9(CommandPalette, { commands: filteredCommands, selectedIndex })
|
|
1811
|
-
] });
|
|
1812
|
-
}
|
|
1813
|
-
|
|
1814
|
-
// src/controllers/cli/create-application.cli-controller.ts
|
|
1815
|
-
async function createApplicationCliController(args2) {
|
|
1816
|
-
if (!await isPlatformInitialized()) {
|
|
1817
|
-
console.error("Error: Cannot create an application \u2014 no platform initialized in this directory.");
|
|
1818
|
-
process.exit(1);
|
|
1819
|
-
}
|
|
1820
|
-
const {
|
|
1821
|
-
applicationName,
|
|
1822
|
-
applicationDisplayName,
|
|
1823
|
-
applicationDescription,
|
|
1824
|
-
hasUserInterface,
|
|
1825
|
-
hasBackendService
|
|
1826
|
-
} = args2;
|
|
1827
|
-
if (!applicationName || !applicationDisplayName || !applicationDescription) {
|
|
1828
|
-
console.error("Error: applicationName, applicationDisplayName, and applicationDescription are required.");
|
|
1829
|
-
process.exit(1);
|
|
1830
|
-
}
|
|
1831
|
-
if (!/^[a-z0-9-]+$/.test(applicationName)) {
|
|
1832
|
-
console.error(`Error: Application name "${applicationName}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
|
|
1833
|
-
process.exit(1);
|
|
1834
|
-
}
|
|
1835
|
-
await createApplicationService(
|
|
1836
|
-
{
|
|
1837
|
-
applicationName,
|
|
1838
|
-
applicationDisplayName,
|
|
1839
|
-
applicationDescription,
|
|
1840
|
-
hasUserInterface: hasUserInterface === "yes",
|
|
1841
|
-
hasBackendService: hasBackendService === "yes"
|
|
1842
|
-
},
|
|
1843
|
-
{ log: console.log }
|
|
1844
|
-
);
|
|
1845
|
-
}
|
|
1846
|
-
|
|
1847
|
-
// src/controllers/cli/init.cli-controller.ts
|
|
1848
|
-
async function initCliController(args2) {
|
|
1849
|
-
if (await isPlatformInitialized()) {
|
|
1850
|
-
console.error("Error: Cannot initialize a new platform \u2014 a platform is already initialized in this directory.");
|
|
1851
|
-
process.exit(1);
|
|
1852
|
-
}
|
|
1853
|
-
const { organizationName, platformName, platformDisplayName, bootstrapServiceSuffix, customizationUiSuffix } = args2;
|
|
1854
|
-
if (!organizationName || !platformName || !platformDisplayName) {
|
|
1855
|
-
console.error("Error: organizationName, platformName, and platformDisplayName are required.");
|
|
1856
|
-
process.exit(1);
|
|
1857
|
-
}
|
|
1858
|
-
await initService(
|
|
1859
|
-
{ organizationName, platformName, platformDisplayName, bootstrapServiceSuffix, customizationUiSuffix },
|
|
1860
|
-
{ log: console.log }
|
|
1861
|
-
);
|
|
1862
|
-
}
|
|
1863
|
-
|
|
1864
|
-
// src/controllers/cli/configure-idp.cli-controller.ts
|
|
1865
|
-
async function configureIdpCliController(args2) {
|
|
1866
|
-
const logger = { log: console.log };
|
|
1867
|
-
if (!await isPlatformInitialized()) {
|
|
1868
|
-
console.error("Error: Cannot configure an IDP \u2014 no platform initialized in this directory.");
|
|
1869
|
-
process.exit(1);
|
|
1870
|
-
}
|
|
1871
|
-
const { providerType, name, issuer, clientId, clientSecret } = args2;
|
|
1872
|
-
if (!providerType || !name || !issuer || !clientId || !clientSecret) {
|
|
1873
|
-
logger.log("Error: Missing required arguments: providerType, name, issuer, clientId, clientSecret");
|
|
1874
|
-
process.exit(1);
|
|
1875
|
-
}
|
|
1876
|
-
const provider = idpProviderRegistry.get(providerType);
|
|
1877
|
-
if (!provider) {
|
|
1878
|
-
logger.log(`Error: Unknown IDP type "${providerType}"`);
|
|
1879
|
-
process.exit(1);
|
|
1880
|
-
}
|
|
1881
|
-
const extras = {};
|
|
1882
|
-
for (const field of provider.fields) {
|
|
1883
|
-
const value = args2[field.key];
|
|
1884
|
-
if (!value) {
|
|
1885
|
-
logger.log(`Error: Missing required argument for ${providerType}: ${field.key}`);
|
|
1886
|
-
process.exit(1);
|
|
1887
|
-
}
|
|
1888
|
-
extras[field.key] = value;
|
|
1889
|
-
}
|
|
1890
|
-
await configureIdpService({ providerType, name, issuer, clientId, clientSecret, extras }, logger);
|
|
1891
|
-
}
|
|
1892
|
-
|
|
1893
|
-
// src/commands/local-scripts/docker-compose-orchestrator.ts
|
|
1894
|
-
import { spawn } from "child_process";
|
|
1895
|
-
import { access as access5 } from "fs/promises";
|
|
1896
|
-
import { join as join22 } from "path";
|
|
1897
|
-
function runDockerCompose(args2, logger, rootDir, signal) {
|
|
1898
|
-
return new Promise((resolvePromise) => {
|
|
1899
|
-
const child = spawn("docker", ["compose", ...args2], {
|
|
1900
|
-
shell: false,
|
|
1901
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
1902
|
-
cwd: rootDir
|
|
1848
|
+
// src/commands/local-scripts/docker-compose-orchestrator.ts
|
|
1849
|
+
import { spawn as spawn2 } from "child_process";
|
|
1850
|
+
import { access as access6 } from "fs/promises";
|
|
1851
|
+
import { join as join23 } from "path";
|
|
1852
|
+
function runDockerCompose(args2, logger, rootDir, signal) {
|
|
1853
|
+
return new Promise((resolvePromise) => {
|
|
1854
|
+
const child = spawn2("docker", ["compose", ...args2], {
|
|
1855
|
+
shell: false,
|
|
1856
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1857
|
+
cwd: rootDir
|
|
1903
1858
|
});
|
|
1904
1859
|
const onAbort = () => {
|
|
1905
1860
|
child.kill("SIGTERM");
|
|
@@ -1941,15 +1896,15 @@ function runDockerCompose(args2, logger, rootDir, signal) {
|
|
|
1941
1896
|
async function getAppComposePaths(localDir, platformName, manifest) {
|
|
1942
1897
|
const results = [];
|
|
1943
1898
|
for (const app of manifest.applications) {
|
|
1944
|
-
const prefixedPath =
|
|
1945
|
-
const unprefixedPath =
|
|
1899
|
+
const prefixedPath = join23(localDir, `${platformName}-${app.name}-docker-compose.yml`);
|
|
1900
|
+
const unprefixedPath = join23(localDir, `${app.name}-docker-compose.yml`);
|
|
1946
1901
|
let resolved = null;
|
|
1947
1902
|
try {
|
|
1948
|
-
await
|
|
1903
|
+
await access6(prefixedPath);
|
|
1949
1904
|
resolved = prefixedPath;
|
|
1950
1905
|
} catch {
|
|
1951
1906
|
try {
|
|
1952
|
-
await
|
|
1907
|
+
await access6(unprefixedPath);
|
|
1953
1908
|
resolved = unprefixedPath;
|
|
1954
1909
|
} catch {
|
|
1955
1910
|
}
|
|
@@ -1963,18 +1918,18 @@ async function getAppComposePaths(localDir, platformName, manifest) {
|
|
|
1963
1918
|
async function buildAllComposeArgs(layout, manifest, logger) {
|
|
1964
1919
|
const { rootDir, coreDirName, localDir } = layout;
|
|
1965
1920
|
const platformName = manifest.product.name;
|
|
1966
|
-
const prefixedCoreCompose =
|
|
1967
|
-
const unprefixedCoreCompose =
|
|
1921
|
+
const prefixedCoreCompose = join23(localDir, `${coreDirName}-docker-compose.yml`);
|
|
1922
|
+
const unprefixedCoreCompose = join23(localDir, "core-docker-compose.yml");
|
|
1968
1923
|
let coreComposePath;
|
|
1969
1924
|
try {
|
|
1970
|
-
await
|
|
1925
|
+
await access6(prefixedCoreCompose);
|
|
1971
1926
|
coreComposePath = prefixedCoreCompose;
|
|
1972
1927
|
} catch {
|
|
1973
1928
|
coreComposePath = unprefixedCoreCompose;
|
|
1974
1929
|
}
|
|
1975
1930
|
const fileArgs = [
|
|
1976
1931
|
"-f",
|
|
1977
|
-
|
|
1932
|
+
join23(localDir, "platform-docker-compose.yml"),
|
|
1978
1933
|
"-f",
|
|
1979
1934
|
coreComposePath
|
|
1980
1935
|
];
|
|
@@ -1992,7 +1947,7 @@ async function buildAllComposeArgs(layout, manifest, logger) {
|
|
|
1992
1947
|
async function startEnvironment(layout, manifest, logger, signal) {
|
|
1993
1948
|
const { rootDir, localDir } = layout;
|
|
1994
1949
|
const platformName = manifest.product.name;
|
|
1995
|
-
const envFile =
|
|
1950
|
+
const envFile = join23(localDir, ".env");
|
|
1996
1951
|
const fileArgs = await buildAllComposeArgs(layout, manifest, logger);
|
|
1997
1952
|
logger.log("Starting environment...");
|
|
1998
1953
|
await runDockerCompose(
|
|
@@ -2017,7 +1972,7 @@ async function startEnvironment(layout, manifest, logger, signal) {
|
|
|
2017
1972
|
async function stopEnvironment(layout, manifest, logger, signal) {
|
|
2018
1973
|
const { rootDir, localDir } = layout;
|
|
2019
1974
|
const platformName = manifest.product.name;
|
|
2020
|
-
const envFile =
|
|
1975
|
+
const envFile = join23(localDir, ".env");
|
|
2021
1976
|
const fileArgs = await buildAllComposeArgs(layout, manifest, logger);
|
|
2022
1977
|
logger.log("Stopping environment...");
|
|
2023
1978
|
await runDockerCompose(
|
|
@@ -2039,7 +1994,7 @@ async function stopEnvironment(layout, manifest, logger, signal) {
|
|
|
2039
1994
|
async function destroyEnvironment(layout, manifest, logger, signal) {
|
|
2040
1995
|
const { rootDir, localDir } = layout;
|
|
2041
1996
|
const platformName = manifest.product.name;
|
|
2042
|
-
const envFile =
|
|
1997
|
+
const envFile = join23(localDir, ".env");
|
|
2043
1998
|
const fileArgs = await buildAllComposeArgs(layout, manifest, logger);
|
|
2044
1999
|
logger.log("Destroying environment...");
|
|
2045
2000
|
await runDockerCompose(
|
|
@@ -2063,12 +2018,12 @@ async function destroyEnvironment(layout, manifest, logger, signal) {
|
|
|
2063
2018
|
}
|
|
2064
2019
|
|
|
2065
2020
|
// src/commands/local-scripts/npm-orchestrator.ts
|
|
2066
|
-
import { spawn as
|
|
2067
|
-
import { access as
|
|
2068
|
-
import { resolve as
|
|
2021
|
+
import { spawn as spawn3 } from "child_process";
|
|
2022
|
+
import { access as access7 } from "fs/promises";
|
|
2023
|
+
import { resolve as resolve5, join as join24 } from "path";
|
|
2069
2024
|
function runCommand(command, args2, workDir, logger, signal) {
|
|
2070
2025
|
return new Promise((resolvePromise) => {
|
|
2071
|
-
const child =
|
|
2026
|
+
const child = spawn3(command, args2, {
|
|
2072
2027
|
cwd: workDir,
|
|
2073
2028
|
shell: false,
|
|
2074
2029
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2112,7 +2067,7 @@ function runCommand(command, args2, workDir, logger, signal) {
|
|
|
2112
2067
|
}
|
|
2113
2068
|
async function dirExists(dirPath) {
|
|
2114
2069
|
try {
|
|
2115
|
-
await
|
|
2070
|
+
await access7(dirPath);
|
|
2116
2071
|
return true;
|
|
2117
2072
|
} catch {
|
|
2118
2073
|
return false;
|
|
@@ -2122,7 +2077,7 @@ async function installDependencies(layout, manifest, logger, signal) {
|
|
|
2122
2077
|
const { coreDir, coreDirName } = layout;
|
|
2123
2078
|
const appDirs = [];
|
|
2124
2079
|
for (const app of manifest.applications) {
|
|
2125
|
-
const appDir =
|
|
2080
|
+
const appDir = resolve5(join24(coreDir), app.localPath);
|
|
2126
2081
|
if (!await dirExists(appDir)) {
|
|
2127
2082
|
logger.log(`Warning: Application directory "${app.name}" not found at ${appDir} \u2014 skipping install.`);
|
|
2128
2083
|
continue;
|
|
@@ -2146,7 +2101,7 @@ async function buildAll(layout, manifest, logger, signal) {
|
|
|
2146
2101
|
if (signal?.aborted) return;
|
|
2147
2102
|
const appDirs = [];
|
|
2148
2103
|
for (const app of manifest.applications) {
|
|
2149
|
-
const appDir =
|
|
2104
|
+
const appDir = resolve5(join24(coreDir), app.localPath);
|
|
2150
2105
|
if (!await dirExists(appDir)) {
|
|
2151
2106
|
logger.log(`Warning: Application directory "${app.name}" not found at ${appDir} \u2014 skipping build.`);
|
|
2152
2107
|
continue;
|
|
@@ -2210,18 +2165,443 @@ async function localScriptService(scriptName, logger, signal) {
|
|
|
2210
2165
|
await runLocalScript(scriptName, logger, signal);
|
|
2211
2166
|
}
|
|
2212
2167
|
|
|
2213
|
-
// src/
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2168
|
+
// src/controllers/ui/status.ui-controller.ts
|
|
2169
|
+
async function statusUiController(ctx) {
|
|
2170
|
+
while (true) {
|
|
2171
|
+
const result = await statusService();
|
|
2172
|
+
for (const line of formatStatusLines(result)) {
|
|
2173
|
+
ctx.log(line);
|
|
2174
|
+
}
|
|
2175
|
+
const next = computeNextStep(result);
|
|
2176
|
+
if (next === null) {
|
|
2177
|
+
ctx.log("");
|
|
2178
|
+
ctx.log("All checks passed!");
|
|
2179
|
+
return;
|
|
2180
|
+
}
|
|
2181
|
+
const shouldRun = await ctx.confirm(`Next step: "${next}". Run it now?`, true);
|
|
2182
|
+
if (!shouldRun) return;
|
|
2183
|
+
if (next === "init") {
|
|
2184
|
+
await initUiController(ctx);
|
|
2185
|
+
} else {
|
|
2186
|
+
await localScriptService(next, ctx, ctx.signal);
|
|
2187
|
+
}
|
|
2188
|
+
const resultAfter = await statusService();
|
|
2189
|
+
const nextAfter = computeNextStep(resultAfter);
|
|
2190
|
+
if (nextAfter === next) {
|
|
2191
|
+
ctx.log("");
|
|
2192
|
+
ctx.log(`"${next}" did not complete successfully. Check the output above for errors.`);
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2222
2195
|
}
|
|
2223
|
-
|
|
2224
|
-
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
// src/controllers/ui/registry.ts
|
|
2199
|
+
var uiControllers = /* @__PURE__ */ new Map([
|
|
2200
|
+
[CREATE_APPLICATION_COMMAND_NAME, createApplicationUiController],
|
|
2201
|
+
[INIT_COMMAND_NAME, initUiController],
|
|
2202
|
+
[CONFIGURE_IDP_COMMAND_NAME, configureIdpUiController],
|
|
2203
|
+
[CREATE_SERVICE_MODULE_COMMAND_NAME, createServiceModuleUiController],
|
|
2204
|
+
[CREATE_UI_MODULE_COMMAND_NAME, createUiModuleUiController],
|
|
2205
|
+
[STATUS_COMMAND_NAME, statusUiController]
|
|
2206
|
+
]);
|
|
2207
|
+
|
|
2208
|
+
// src/hooks/use-command-runner.ts
|
|
2209
|
+
function useCommandRunner({ appendStaticItem, setState, onCommandComplete }) {
|
|
2210
|
+
const outputCountRef = useRef(0);
|
|
2211
|
+
const abortControllerRef = useRef(null);
|
|
2212
|
+
const promptResolveRef = useRef(null);
|
|
2213
|
+
const [promptMessage, setPromptMessage] = useState2("");
|
|
2214
|
+
const [promptValue, setPromptValue] = useState2("");
|
|
2215
|
+
const [promptMode, setPromptMode] = useState2({ kind: "text" });
|
|
2216
|
+
const [selectIndex, setSelectIndex] = useState2(0);
|
|
2217
|
+
const [confirmValue, setConfirmValue] = useState2(true);
|
|
2218
|
+
const abortExecution = useCallback(() => {
|
|
2219
|
+
abortControllerRef.current?.abort();
|
|
2220
|
+
abortControllerRef.current = null;
|
|
2221
|
+
promptResolveRef.current = null;
|
|
2222
|
+
setPromptMode({ kind: "text" });
|
|
2223
|
+
}, []);
|
|
2224
|
+
const runCommand2 = useCallback(
|
|
2225
|
+
(cmd) => {
|
|
2226
|
+
const controller = new AbortController();
|
|
2227
|
+
abortControllerRef.current = controller;
|
|
2228
|
+
setState(APP_STATE.EXECUTING);
|
|
2229
|
+
const ctx = {
|
|
2230
|
+
signal: controller.signal,
|
|
2231
|
+
log(message) {
|
|
2232
|
+
if (!controller.signal.aborted) {
|
|
2233
|
+
const id = `output-${outputCountRef.current++}`;
|
|
2234
|
+
appendStaticItem({ kind: "output", id, line: message });
|
|
2235
|
+
}
|
|
2236
|
+
},
|
|
2237
|
+
prompt(message, defaultValue) {
|
|
2238
|
+
return new Promise((resolve6, reject) => {
|
|
2239
|
+
if (controller.signal.aborted) {
|
|
2240
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
2241
|
+
return;
|
|
2242
|
+
}
|
|
2243
|
+
setPromptMessage(message);
|
|
2244
|
+
setPromptValue(defaultValue ?? "");
|
|
2245
|
+
setPromptMode({ kind: "text" });
|
|
2246
|
+
promptResolveRef.current = resolve6;
|
|
2247
|
+
setState(APP_STATE.PROMPTING);
|
|
2248
|
+
controller.signal.addEventListener(
|
|
2249
|
+
"abort",
|
|
2250
|
+
() => reject(new DOMException("Aborted", "AbortError")),
|
|
2251
|
+
{ once: true }
|
|
2252
|
+
);
|
|
2253
|
+
});
|
|
2254
|
+
},
|
|
2255
|
+
select(message, options) {
|
|
2256
|
+
return new Promise((resolve6, reject) => {
|
|
2257
|
+
if (controller.signal.aborted) {
|
|
2258
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
2259
|
+
return;
|
|
2260
|
+
}
|
|
2261
|
+
setPromptMessage(message);
|
|
2262
|
+
setPromptMode({ kind: "select", options });
|
|
2263
|
+
setSelectIndex(0);
|
|
2264
|
+
promptResolveRef.current = resolve6;
|
|
2265
|
+
setState(APP_STATE.PROMPTING);
|
|
2266
|
+
controller.signal.addEventListener(
|
|
2267
|
+
"abort",
|
|
2268
|
+
() => reject(new DOMException("Aborted", "AbortError")),
|
|
2269
|
+
{ once: true }
|
|
2270
|
+
);
|
|
2271
|
+
});
|
|
2272
|
+
},
|
|
2273
|
+
confirm(message, defaultValue) {
|
|
2274
|
+
return new Promise((resolve6, reject) => {
|
|
2275
|
+
if (controller.signal.aborted) {
|
|
2276
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
2277
|
+
return;
|
|
2278
|
+
}
|
|
2279
|
+
setPromptMessage(message);
|
|
2280
|
+
setPromptMode({ kind: "confirm" });
|
|
2281
|
+
setConfirmValue(defaultValue ?? true);
|
|
2282
|
+
promptResolveRef.current = (value) => resolve6(value === "true");
|
|
2283
|
+
setState(APP_STATE.PROMPTING);
|
|
2284
|
+
controller.signal.addEventListener(
|
|
2285
|
+
"abort",
|
|
2286
|
+
() => reject(new DOMException("Aborted", "AbortError")),
|
|
2287
|
+
{ once: true }
|
|
2288
|
+
);
|
|
2289
|
+
});
|
|
2290
|
+
}
|
|
2291
|
+
};
|
|
2292
|
+
const uiController = uiControllers.get(cmd.name);
|
|
2293
|
+
if (!uiController) {
|
|
2294
|
+
const id = `output-${outputCountRef.current++}`;
|
|
2295
|
+
appendStaticItem({ kind: "output", id, line: `Error: No controller found for "${cmd.name}"` });
|
|
2296
|
+
setState(APP_STATE.IDLE);
|
|
2297
|
+
return;
|
|
2298
|
+
}
|
|
2299
|
+
uiController(ctx).then(() => {
|
|
2300
|
+
if (!controller.signal.aborted) {
|
|
2301
|
+
setState(APP_STATE.IDLE);
|
|
2302
|
+
onCommandComplete?.();
|
|
2303
|
+
}
|
|
2304
|
+
}).catch((err) => {
|
|
2305
|
+
if (controller.signal.aborted) return;
|
|
2306
|
+
const id = `output-${outputCountRef.current++}`;
|
|
2307
|
+
appendStaticItem({ kind: "output", id, line: `Error: ${formatError(err)}` });
|
|
2308
|
+
setState(APP_STATE.IDLE);
|
|
2309
|
+
onCommandComplete?.();
|
|
2310
|
+
});
|
|
2311
|
+
},
|
|
2312
|
+
[appendStaticItem, setState, onCommandComplete]
|
|
2313
|
+
);
|
|
2314
|
+
const handlePromptSubmit = useCallback(
|
|
2315
|
+
(value) => {
|
|
2316
|
+
const resolve6 = promptResolveRef.current;
|
|
2317
|
+
promptResolveRef.current = null;
|
|
2318
|
+
setPromptMode({ kind: "text" });
|
|
2319
|
+
setState(APP_STATE.EXECUTING);
|
|
2320
|
+
resolve6?.(value);
|
|
2321
|
+
},
|
|
2322
|
+
[setState]
|
|
2323
|
+
);
|
|
2324
|
+
return {
|
|
2325
|
+
runCommand: runCommand2,
|
|
2326
|
+
handlePromptSubmit,
|
|
2327
|
+
abortExecution,
|
|
2328
|
+
promptMessage,
|
|
2329
|
+
promptValue,
|
|
2330
|
+
setPromptValue,
|
|
2331
|
+
promptMode,
|
|
2332
|
+
selectIndex,
|
|
2333
|
+
setSelectIndex,
|
|
2334
|
+
confirmValue,
|
|
2335
|
+
setConfirmValue
|
|
2336
|
+
};
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
// src/app.tsx
|
|
2340
|
+
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2341
|
+
var require2 = createRequire(import.meta.url);
|
|
2342
|
+
var { version } = require2("../package.json");
|
|
2343
|
+
function App() {
|
|
2344
|
+
const { exit } = useApp();
|
|
2345
|
+
const [state, setState] = useState3(APP_STATE.IDLE);
|
|
2346
|
+
const [inputValue, setInputValue] = useState3("");
|
|
2347
|
+
const [selectedIndex, setSelectedIndex] = useState3(0);
|
|
2348
|
+
const [staticItems, setStaticItems] = useState3([{ kind: "banner", id: "banner" }]);
|
|
2349
|
+
const [platformInitialized, setPlatformInitialized] = useState3(false);
|
|
2350
|
+
useEffect2(() => {
|
|
2351
|
+
isPlatformInitialized().then(setPlatformInitialized).catch(() => {
|
|
2352
|
+
});
|
|
2353
|
+
}, []);
|
|
2354
|
+
const appendStaticItem = useCallback2((item) => {
|
|
2355
|
+
setStaticItems((prev) => [...prev, item]);
|
|
2356
|
+
}, []);
|
|
2357
|
+
const handleCommandComplete = useCallback2(() => {
|
|
2358
|
+
isPlatformInitialized().then(setPlatformInitialized).catch(() => {
|
|
2359
|
+
});
|
|
2360
|
+
}, []);
|
|
2361
|
+
const {
|
|
2362
|
+
runCommand: runCommand2,
|
|
2363
|
+
handlePromptSubmit,
|
|
2364
|
+
abortExecution,
|
|
2365
|
+
promptMessage,
|
|
2366
|
+
promptValue,
|
|
2367
|
+
setPromptValue,
|
|
2368
|
+
promptMode,
|
|
2369
|
+
selectIndex,
|
|
2370
|
+
setSelectIndex,
|
|
2371
|
+
confirmValue,
|
|
2372
|
+
setConfirmValue
|
|
2373
|
+
} = useCommandRunner({ appendStaticItem, setState, onCommandComplete: handleCommandComplete });
|
|
2374
|
+
const query = inputValue.startsWith("/") ? inputValue.slice(1) : "";
|
|
2375
|
+
const filteredCommands = registry.searchVisible(query, { platformInitialized });
|
|
2376
|
+
useInput(
|
|
2377
|
+
(input, key) => {
|
|
2378
|
+
if (key.ctrl && input === "c") {
|
|
2379
|
+
exit();
|
|
2380
|
+
return;
|
|
2381
|
+
}
|
|
2382
|
+
if ((state === APP_STATE.EXECUTING || state === APP_STATE.PROMPTING) && key.escape) {
|
|
2383
|
+
abortExecution();
|
|
2384
|
+
setState(APP_STATE.IDLE);
|
|
2385
|
+
return;
|
|
2386
|
+
}
|
|
2387
|
+
if (state === APP_STATE.PROMPTING) {
|
|
2388
|
+
if (promptMode.kind === "select") {
|
|
2389
|
+
if (key.upArrow) {
|
|
2390
|
+
setSelectIndex((prev) => Math.max(0, prev - 1));
|
|
2391
|
+
return;
|
|
2392
|
+
}
|
|
2393
|
+
if (key.downArrow) {
|
|
2394
|
+
setSelectIndex((prev) => Math.min(promptMode.options.length - 1, prev + 1));
|
|
2395
|
+
return;
|
|
2396
|
+
}
|
|
2397
|
+
if (key.return) {
|
|
2398
|
+
const selected = promptMode.options[selectIndex];
|
|
2399
|
+
if (selected) handlePromptSubmit(selected.value);
|
|
2400
|
+
return;
|
|
2401
|
+
}
|
|
2402
|
+
return;
|
|
2403
|
+
}
|
|
2404
|
+
if (promptMode.kind === "confirm") {
|
|
2405
|
+
if (key.leftArrow || key.upArrow) {
|
|
2406
|
+
setConfirmValue(true);
|
|
2407
|
+
return;
|
|
2408
|
+
}
|
|
2409
|
+
if (key.rightArrow || key.downArrow) {
|
|
2410
|
+
setConfirmValue(false);
|
|
2411
|
+
return;
|
|
2412
|
+
}
|
|
2413
|
+
if (key.return) {
|
|
2414
|
+
handlePromptSubmit(confirmValue ? "true" : "false");
|
|
2415
|
+
return;
|
|
2416
|
+
}
|
|
2417
|
+
return;
|
|
2418
|
+
}
|
|
2419
|
+
return;
|
|
2420
|
+
}
|
|
2421
|
+
if (state === APP_STATE.PALETTE) {
|
|
2422
|
+
if (key.escape) {
|
|
2423
|
+
setState(APP_STATE.IDLE);
|
|
2424
|
+
setInputValue("");
|
|
2425
|
+
return;
|
|
2426
|
+
}
|
|
2427
|
+
if (key.upArrow) {
|
|
2428
|
+
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
2429
|
+
return;
|
|
2430
|
+
}
|
|
2431
|
+
if (key.downArrow) {
|
|
2432
|
+
setSelectedIndex((prev) => Math.min(filteredCommands.length - 1, prev + 1));
|
|
2433
|
+
return;
|
|
2434
|
+
}
|
|
2435
|
+
if (key.return) {
|
|
2436
|
+
const cmd = filteredCommands[selectedIndex];
|
|
2437
|
+
if (cmd) {
|
|
2438
|
+
setInputValue("");
|
|
2439
|
+
runCommand2(cmd);
|
|
2440
|
+
}
|
|
2441
|
+
return;
|
|
2442
|
+
}
|
|
2443
|
+
if (key.backspace || key.delete) {
|
|
2444
|
+
setInputValue((prev) => {
|
|
2445
|
+
const next = prev.slice(0, -1);
|
|
2446
|
+
if (!next.startsWith("/")) {
|
|
2447
|
+
setState(APP_STATE.IDLE);
|
|
2448
|
+
return "";
|
|
2449
|
+
}
|
|
2450
|
+
return next;
|
|
2451
|
+
});
|
|
2452
|
+
setSelectedIndex(0);
|
|
2453
|
+
return;
|
|
2454
|
+
}
|
|
2455
|
+
if (input && !key.ctrl && !key.meta) {
|
|
2456
|
+
setInputValue((prev) => prev + input);
|
|
2457
|
+
setSelectedIndex(0);
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
},
|
|
2461
|
+
{ isActive: true }
|
|
2462
|
+
);
|
|
2463
|
+
const handleIdleInputChange = useCallback2((value) => {
|
|
2464
|
+
setInputValue(value);
|
|
2465
|
+
if (value.startsWith("/")) {
|
|
2466
|
+
setState(APP_STATE.PALETTE);
|
|
2467
|
+
setSelectedIndex(0);
|
|
2468
|
+
}
|
|
2469
|
+
}, []);
|
|
2470
|
+
const handleIdleSubmit = useCallback2(
|
|
2471
|
+
(value) => {
|
|
2472
|
+
if (!value.startsWith("/")) return;
|
|
2473
|
+
const name = value.slice(1).trim();
|
|
2474
|
+
const cmd = registry.get(name);
|
|
2475
|
+
if (cmd) runCommand2(cmd);
|
|
2476
|
+
},
|
|
2477
|
+
[runCommand2]
|
|
2478
|
+
);
|
|
2479
|
+
function renderActiveArea() {
|
|
2480
|
+
switch (state) {
|
|
2481
|
+
case APP_STATE.EXECUTING:
|
|
2482
|
+
return /* @__PURE__ */ jsxs7(Box6, { flexDirection: "row", gap: 1, children: [
|
|
2483
|
+
/* @__PURE__ */ jsx9(Spinner, { label: "Running\u2026" }),
|
|
2484
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "(esc to cancel)" })
|
|
2485
|
+
] });
|
|
2486
|
+
case APP_STATE.PROMPTING:
|
|
2487
|
+
return /* @__PURE__ */ jsxs7(Box6, { flexDirection: "column", children: [
|
|
2488
|
+
/* @__PURE__ */ jsx9(Text9, { color: "cyan", children: promptMessage }),
|
|
2489
|
+
promptMode.kind === "select" && /* @__PURE__ */ jsx9(SelectList, { options: promptMode.options, selectedIndex: selectIndex }),
|
|
2490
|
+
promptMode.kind === "confirm" && /* @__PURE__ */ jsx9(ConfirmPrompt, { value: confirmValue }),
|
|
2491
|
+
promptMode.kind === "text" && /* @__PURE__ */ jsx9(Prompt, { value: promptValue, onChange: setPromptValue, onSubmit: handlePromptSubmit })
|
|
2492
|
+
] });
|
|
2493
|
+
default:
|
|
2494
|
+
return /* @__PURE__ */ jsx9(
|
|
2495
|
+
Prompt,
|
|
2496
|
+
{
|
|
2497
|
+
value: inputValue,
|
|
2498
|
+
onChange: state === APP_STATE.IDLE ? handleIdleInputChange : () => {
|
|
2499
|
+
},
|
|
2500
|
+
onSubmit: state === APP_STATE.IDLE ? handleIdleSubmit : () => {
|
|
2501
|
+
},
|
|
2502
|
+
disabled: state === APP_STATE.PALETTE
|
|
2503
|
+
}
|
|
2504
|
+
);
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
return /* @__PURE__ */ jsxs7(Box6, { flexDirection: "column", children: [
|
|
2508
|
+
/* @__PURE__ */ jsx9(ScrollbackHistory, { items: staticItems, version }),
|
|
2509
|
+
renderActiveArea(),
|
|
2510
|
+
state === APP_STATE.PALETTE && /* @__PURE__ */ jsx9(CommandPalette, { commands: filteredCommands, selectedIndex })
|
|
2511
|
+
] });
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
// src/controllers/cli/create-application.cli-controller.ts
|
|
2515
|
+
async function createApplicationCliController(args2) {
|
|
2516
|
+
if (!await isPlatformInitialized()) {
|
|
2517
|
+
console.error("Error: Cannot create an application \u2014 no platform initialized in this directory.");
|
|
2518
|
+
process.exit(1);
|
|
2519
|
+
}
|
|
2520
|
+
const {
|
|
2521
|
+
applicationName,
|
|
2522
|
+
applicationDisplayName,
|
|
2523
|
+
applicationDescription,
|
|
2524
|
+
hasUserInterface,
|
|
2525
|
+
hasBackendService
|
|
2526
|
+
} = args2;
|
|
2527
|
+
if (!applicationName || !applicationDisplayName || !applicationDescription) {
|
|
2528
|
+
console.error("Error: applicationName, applicationDisplayName, and applicationDescription are required.");
|
|
2529
|
+
process.exit(1);
|
|
2530
|
+
}
|
|
2531
|
+
if (!/^[a-z0-9-]+$/.test(applicationName)) {
|
|
2532
|
+
console.error(`Error: Application name "${applicationName}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
|
|
2533
|
+
process.exit(1);
|
|
2534
|
+
}
|
|
2535
|
+
await createApplicationService(
|
|
2536
|
+
{
|
|
2537
|
+
applicationName,
|
|
2538
|
+
applicationDisplayName,
|
|
2539
|
+
applicationDescription,
|
|
2540
|
+
hasUserInterface: hasUserInterface === "yes",
|
|
2541
|
+
hasBackendService: hasBackendService === "yes"
|
|
2542
|
+
},
|
|
2543
|
+
{ log: console.log }
|
|
2544
|
+
);
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
// src/controllers/cli/init.cli-controller.ts
|
|
2548
|
+
async function initCliController(args2) {
|
|
2549
|
+
if (await isPlatformInitialized()) {
|
|
2550
|
+
console.error("Error: Cannot initialize a new platform \u2014 a platform is already initialized in this directory.");
|
|
2551
|
+
process.exit(1);
|
|
2552
|
+
}
|
|
2553
|
+
const { organizationName, platformName, platformDisplayName, bootstrapServiceSuffix, customizationUiSuffix } = args2;
|
|
2554
|
+
if (!organizationName || !platformName || !platformDisplayName) {
|
|
2555
|
+
console.error("Error: organizationName, platformName, and platformDisplayName are required.");
|
|
2556
|
+
process.exit(1);
|
|
2557
|
+
}
|
|
2558
|
+
await initService(
|
|
2559
|
+
{ organizationName, platformName, platformDisplayName, bootstrapServiceSuffix, customizationUiSuffix },
|
|
2560
|
+
{ log: console.log }
|
|
2561
|
+
);
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2564
|
+
// src/controllers/cli/configure-idp.cli-controller.ts
|
|
2565
|
+
async function configureIdpCliController(args2) {
|
|
2566
|
+
const logger = { log: console.log };
|
|
2567
|
+
if (!await isPlatformInitialized()) {
|
|
2568
|
+
console.error("Error: Cannot configure an IDP \u2014 no platform initialized in this directory.");
|
|
2569
|
+
process.exit(1);
|
|
2570
|
+
}
|
|
2571
|
+
const { providerType, name, issuer, clientId, clientSecret } = args2;
|
|
2572
|
+
if (!providerType || !name || !issuer || !clientId || !clientSecret) {
|
|
2573
|
+
logger.log("Error: Missing required arguments: providerType, name, issuer, clientId, clientSecret");
|
|
2574
|
+
process.exit(1);
|
|
2575
|
+
}
|
|
2576
|
+
const provider = idpProviderRegistry.get(providerType);
|
|
2577
|
+
if (!provider) {
|
|
2578
|
+
logger.log(`Error: Unknown IDP type "${providerType}"`);
|
|
2579
|
+
process.exit(1);
|
|
2580
|
+
}
|
|
2581
|
+
const extras = {};
|
|
2582
|
+
for (const field of provider.fields) {
|
|
2583
|
+
const value = args2[field.key];
|
|
2584
|
+
if (!value) {
|
|
2585
|
+
logger.log(`Error: Missing required argument for ${providerType}: ${field.key}`);
|
|
2586
|
+
process.exit(1);
|
|
2587
|
+
}
|
|
2588
|
+
extras[field.key] = value;
|
|
2589
|
+
}
|
|
2590
|
+
await configureIdpService({ providerType, name, issuer, clientId, clientSecret, extras }, logger);
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2593
|
+
// src/utils/cli-spinner.ts
|
|
2594
|
+
var FRAMES2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
2595
|
+
var INTERVAL_MS2 = 80;
|
|
2596
|
+
function createCliSpinner(label) {
|
|
2597
|
+
let frame = 0;
|
|
2598
|
+
let intervalId = null;
|
|
2599
|
+
function render2() {
|
|
2600
|
+
process.stdout.write(`\r${FRAMES2[frame]} ${label}`);
|
|
2601
|
+
frame = (frame + 1) % FRAMES2.length;
|
|
2602
|
+
}
|
|
2603
|
+
function clearLine() {
|
|
2604
|
+
process.stdout.write("\r\x1B[K");
|
|
2225
2605
|
}
|
|
2226
2606
|
return {
|
|
2227
2607
|
start() {
|
|
@@ -2299,6 +2679,21 @@ async function createUiModuleCliController(args2) {
|
|
|
2299
2679
|
);
|
|
2300
2680
|
}
|
|
2301
2681
|
|
|
2682
|
+
// src/controllers/cli/status.cli-controller.ts
|
|
2683
|
+
var statusCliController = async (_args) => {
|
|
2684
|
+
const result = await statusService();
|
|
2685
|
+
const lines = formatStatusLines(result);
|
|
2686
|
+
for (const line of lines) {
|
|
2687
|
+
console.log(line);
|
|
2688
|
+
}
|
|
2689
|
+
const next = computeNextStep(result);
|
|
2690
|
+
if (next !== null) {
|
|
2691
|
+
console.log("");
|
|
2692
|
+
console.log(`Hint: Run "platform ${next}" to continue.`);
|
|
2693
|
+
process.exit(1);
|
|
2694
|
+
}
|
|
2695
|
+
};
|
|
2696
|
+
|
|
2302
2697
|
// src/controllers/cli/registry.ts
|
|
2303
2698
|
var cliControllers = /* @__PURE__ */ new Map([
|
|
2304
2699
|
[CREATE_APPLICATION_COMMAND_NAME, createApplicationCliController],
|
|
@@ -2306,6 +2701,7 @@ var cliControllers = /* @__PURE__ */ new Map([
|
|
|
2306
2701
|
[CONFIGURE_IDP_COMMAND_NAME, configureIdpCliController],
|
|
2307
2702
|
[CREATE_SERVICE_MODULE_COMMAND_NAME, createServiceModuleCliController],
|
|
2308
2703
|
[CREATE_UI_MODULE_COMMAND_NAME, createUiModuleCliController],
|
|
2704
|
+
[STATUS_COMMAND_NAME, statusCliController],
|
|
2309
2705
|
[INSTALL_COMMAND_NAME, createLocalScriptCliController(INSTALL_COMMAND_NAME)],
|
|
2310
2706
|
[BUILD_COMMAND_NAME, createLocalScriptCliController(BUILD_COMMAND_NAME)],
|
|
2311
2707
|
[START_COMMAND_NAME, createLocalScriptCliController(START_COMMAND_NAME)],
|