@jay-framework/jay-stack-cli 0.17.2 → 0.17.4
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/agent-kit-template/designer/jay-html-components.md +2 -0
- package/agent-kit-template/designer/jay-html-syntax.md +1 -1
- package/agent-kit-template/designer/project-structure.md +5 -0
- package/agent-kit-template/developer/cli-commands.md +5 -1
- package/agent-kit-template/developer/component-refs.md +7 -3
- package/agent-kit-template/developer/project-structure.md +5 -0
- package/agent-kit-template/devops/INSTRUCTIONS.md +23 -0
- package/agent-kit-template/devops/fetch-handler.md +122 -0
- package/agent-kit-template/devops/invalidation.md +77 -0
- package/agent-kit-template/devops/production-build.md +80 -0
- package/agent-kit-template/devops/serving-modes.md +91 -0
- package/agent-kit-template/plugin/INSTRUCTIONS.md +1 -0
- package/agent-kit-template/plugin/component-refs.md +7 -3
- package/agent-kit-template/plugin/plugin-structure.md +12 -0
- package/agent-kit-template/plugin/webhooks-guide.md +124 -0
- package/dist/index.js +559 -445
- package/package.json +11 -11
package/dist/index.js
CHANGED
|
@@ -7,18 +7,18 @@ import getPort from "get-port";
|
|
|
7
7
|
import path from "path";
|
|
8
8
|
import fs, { promises } from "fs";
|
|
9
9
|
import YAML from "yaml";
|
|
10
|
-
import { getLogger,
|
|
10
|
+
import { getLogger, setDevLogger, createDevLogger } from "@jay-framework/logger";
|
|
11
11
|
import { parseJayFile, JAY_IMPORT_RESOLVER, generateElementDefinitionFile, ContractTagType, parseContract, generateElementFile, generateServerElementFile, htmlElementTagNameMap } from "@jay-framework/compiler-jay-html";
|
|
12
12
|
import { JAY_CONTRACT_EXTENSION, JAY_EXTENSION, resolvePluginManifest, LOCAL_PLUGIN_PATH, JayAtomicType, JayEnumType, loadPluginManifest, RuntimeMode, GenerateTarget } from "@jay-framework/compiler-shared";
|
|
13
|
-
import { scanPlugins as scanPlugins$1
|
|
13
|
+
import { listContracts, materializeContracts, scanPlugins as scanPlugins$1 } from "@jay-framework/stack-server-runtime";
|
|
14
14
|
import { listContracts as listContracts2, materializeContracts as materializeContracts2 } from "@jay-framework/stack-server-runtime";
|
|
15
15
|
import { Command } from "commander";
|
|
16
16
|
import chalk from "chalk";
|
|
17
|
+
import path$1 from "node:path";
|
|
18
|
+
import fs$1 from "node:fs/promises";
|
|
17
19
|
import { createRequire } from "module";
|
|
18
20
|
import { glob } from "glob";
|
|
19
21
|
import { parse } from "node-html-parser";
|
|
20
|
-
import path$1 from "node:path";
|
|
21
|
-
import fs$1 from "node:fs/promises";
|
|
22
22
|
import fsSync from "node:fs";
|
|
23
23
|
import { fileURLToPath } from "node:url";
|
|
24
24
|
const DEFAULT_CONFIG = {
|
|
@@ -2583,6 +2583,177 @@ async function startDevServer(options = {}) {
|
|
|
2583
2583
|
process.on("SIGTERM", shutdown);
|
|
2584
2584
|
process.on("SIGINT", shutdown);
|
|
2585
2585
|
}
|
|
2586
|
+
async function initializeServicesForCli(projectRoot, viteServer) {
|
|
2587
|
+
const path2 = await import("node:path");
|
|
2588
|
+
const fs2 = await import("node:fs");
|
|
2589
|
+
const {
|
|
2590
|
+
runInitCallbacks,
|
|
2591
|
+
getServiceRegistry,
|
|
2592
|
+
discoverPluginsWithInit,
|
|
2593
|
+
sortPluginsByDependencies,
|
|
2594
|
+
executePluginServerInits
|
|
2595
|
+
} = await import("@jay-framework/stack-server-runtime");
|
|
2596
|
+
let initErrors = /* @__PURE__ */ new Map();
|
|
2597
|
+
try {
|
|
2598
|
+
const discoveredPlugins = await discoverPluginsWithInit({
|
|
2599
|
+
projectRoot,
|
|
2600
|
+
verbose: false
|
|
2601
|
+
});
|
|
2602
|
+
const pluginsWithInit = sortPluginsByDependencies(discoveredPlugins);
|
|
2603
|
+
try {
|
|
2604
|
+
initErrors = await executePluginServerInits(pluginsWithInit, viteServer, false);
|
|
2605
|
+
} catch (error) {
|
|
2606
|
+
getLogger().warn(chalk.yellow(`⚠️ Plugin initialization skipped: ${error.message}`));
|
|
2607
|
+
}
|
|
2608
|
+
const initPathTs = path2.join(projectRoot, "src", "init.ts");
|
|
2609
|
+
const initPathJs = path2.join(projectRoot, "src", "init.js");
|
|
2610
|
+
let initModule;
|
|
2611
|
+
if (fs2.existsSync(initPathTs) && viteServer) {
|
|
2612
|
+
initModule = await viteServer.ssrLoadModule(initPathTs);
|
|
2613
|
+
} else if (fs2.existsSync(initPathJs)) {
|
|
2614
|
+
initModule = await import(initPathJs);
|
|
2615
|
+
}
|
|
2616
|
+
if (initModule?.init?._serverInit) {
|
|
2617
|
+
await initModule.init._serverInit();
|
|
2618
|
+
}
|
|
2619
|
+
await runInitCallbacks();
|
|
2620
|
+
} catch (error) {
|
|
2621
|
+
getLogger().warn(chalk.yellow(`⚠️ Service initialization failed: ${error.message}`));
|
|
2622
|
+
getLogger().warn(chalk.gray(" Static contracts will still be listed."));
|
|
2623
|
+
}
|
|
2624
|
+
return { services: getServiceRegistry(), initErrors };
|
|
2625
|
+
}
|
|
2626
|
+
async function runDev(projectPath, options) {
|
|
2627
|
+
const logLevel = options.quiet ? "silent" : options.verbose ? "verbose" : "info";
|
|
2628
|
+
setDevLogger(createDevLogger(logLevel));
|
|
2629
|
+
const testMode = options.testMode || options.timeout !== void 0;
|
|
2630
|
+
await startDevServer({
|
|
2631
|
+
projectPath: projectPath || process.cwd(),
|
|
2632
|
+
testMode,
|
|
2633
|
+
timeout: options.timeout,
|
|
2634
|
+
logLevel
|
|
2635
|
+
});
|
|
2636
|
+
}
|
|
2637
|
+
async function resolveProductionContext(projectPath, versionOverride) {
|
|
2638
|
+
const resolvedPath = path$1.resolve(projectPath || process.cwd());
|
|
2639
|
+
const jayConfigPath = path$1.join(resolvedPath, ".jay");
|
|
2640
|
+
let pagesBase = "./src/pages";
|
|
2641
|
+
try {
|
|
2642
|
+
const jayConfig = YAML.parse(await fs$1.readFile(jayConfigPath, "utf-8"));
|
|
2643
|
+
pagesBase = jayConfig?.devServer?.pagesBase || pagesBase;
|
|
2644
|
+
} catch {
|
|
2645
|
+
}
|
|
2646
|
+
const version = versionOverride ? parseInt(versionOverride, 10) : await resolveVersionFromPackageJson(resolvedPath);
|
|
2647
|
+
return {
|
|
2648
|
+
resolvedPath,
|
|
2649
|
+
pagesRoot: path$1.resolve(resolvedPath, pagesBase),
|
|
2650
|
+
buildRoot: path$1.join(resolvedPath, "build"),
|
|
2651
|
+
version,
|
|
2652
|
+
tsConfigFilePath: path$1.join(resolvedPath, "tsconfig.json")
|
|
2653
|
+
};
|
|
2654
|
+
}
|
|
2655
|
+
async function resolveVersionFromPackageJson(projectRoot) {
|
|
2656
|
+
try {
|
|
2657
|
+
const pkgJson = JSON.parse(
|
|
2658
|
+
await fs$1.readFile(path$1.join(projectRoot, "package.json"), "utf-8")
|
|
2659
|
+
);
|
|
2660
|
+
if (pkgJson.version) {
|
|
2661
|
+
const major = parseInt(pkgJson.version.split(".")[0], 10);
|
|
2662
|
+
const minor = parseInt(pkgJson.version.split(".")[1] || "0", 10);
|
|
2663
|
+
const patch = parseInt(pkgJson.version.split(".")[2] || "0", 10);
|
|
2664
|
+
return major * 1e4 + minor * 100 + patch;
|
|
2665
|
+
}
|
|
2666
|
+
} catch {
|
|
2667
|
+
}
|
|
2668
|
+
return 1;
|
|
2669
|
+
}
|
|
2670
|
+
function initLogger(verbose) {
|
|
2671
|
+
const logLevel = verbose ? "verbose" : "info";
|
|
2672
|
+
setDevLogger(createDevLogger(logLevel));
|
|
2673
|
+
}
|
|
2674
|
+
async function runBuild(projectPath, options) {
|
|
2675
|
+
initLogger(options.verbose);
|
|
2676
|
+
const ctx = await resolveProductionContext(projectPath, options.version);
|
|
2677
|
+
const { buildVersion } = await import("@jay-framework/production-server");
|
|
2678
|
+
await buildVersion({
|
|
2679
|
+
version: ctx.version,
|
|
2680
|
+
projectRoot: ctx.resolvedPath,
|
|
2681
|
+
pagesRoot: ctx.pagesRoot,
|
|
2682
|
+
buildRoot: ctx.buildRoot,
|
|
2683
|
+
concurrency: 4,
|
|
2684
|
+
tsConfigFilePath: ctx.tsConfigFilePath,
|
|
2685
|
+
minify: options.minify
|
|
2686
|
+
});
|
|
2687
|
+
}
|
|
2688
|
+
async function runServe(projectPath, options) {
|
|
2689
|
+
initLogger(options.verbose);
|
|
2690
|
+
const ctx = await resolveProductionContext(projectPath, options.version);
|
|
2691
|
+
if (options.role === "renderer") {
|
|
2692
|
+
const { startRendererServer } = await import("@jay-framework/production-server");
|
|
2693
|
+
await startRendererServer({
|
|
2694
|
+
buildRoot: ctx.buildRoot,
|
|
2695
|
+
version: ctx.version,
|
|
2696
|
+
port: parseInt(options.port, 10),
|
|
2697
|
+
projectRoot: ctx.resolvedPath,
|
|
2698
|
+
pagesRoot: ctx.pagesRoot,
|
|
2699
|
+
tsConfigFilePath: ctx.tsConfigFilePath
|
|
2700
|
+
});
|
|
2701
|
+
} else {
|
|
2702
|
+
const { startMainServer } = await import("@jay-framework/production-server");
|
|
2703
|
+
await startMainServer({
|
|
2704
|
+
buildRoot: ctx.buildRoot,
|
|
2705
|
+
version: ctx.version,
|
|
2706
|
+
port: parseInt(options.port, 10),
|
|
2707
|
+
testMode: options.testMode,
|
|
2708
|
+
publicBasePath: options.staticBaseUrl,
|
|
2709
|
+
serveStatic: options.serveStatic
|
|
2710
|
+
});
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
async function runRebuild(projectPath, options) {
|
|
2714
|
+
initLogger(options.verbose);
|
|
2715
|
+
const ctx = await resolveProductionContext(projectPath, options.version);
|
|
2716
|
+
let params;
|
|
2717
|
+
if (options.params) {
|
|
2718
|
+
params = JSON.parse(options.params);
|
|
2719
|
+
}
|
|
2720
|
+
const { rebuild } = await import("@jay-framework/production-server");
|
|
2721
|
+
let target;
|
|
2722
|
+
if (options.contract) {
|
|
2723
|
+
target = { mode: "contract", contractName: options.contract, params };
|
|
2724
|
+
} else if (options.route) {
|
|
2725
|
+
target = { mode: "route", routePattern: options.route, params };
|
|
2726
|
+
} else if (options.url) {
|
|
2727
|
+
target = { mode: "url", url: options.url };
|
|
2728
|
+
} else {
|
|
2729
|
+
getLogger().error(chalk.red("One of --contract, --route, or --url is required"));
|
|
2730
|
+
process.exit(1);
|
|
2731
|
+
}
|
|
2732
|
+
const result = await rebuild({
|
|
2733
|
+
projectRoot: ctx.resolvedPath,
|
|
2734
|
+
pagesRoot: ctx.pagesRoot,
|
|
2735
|
+
buildRoot: ctx.buildRoot,
|
|
2736
|
+
version: ctx.version,
|
|
2737
|
+
target,
|
|
2738
|
+
tsConfigFilePath: ctx.tsConfigFilePath
|
|
2739
|
+
});
|
|
2740
|
+
if (result.errors.length > 0) {
|
|
2741
|
+
for (const err of result.errors) {
|
|
2742
|
+
getLogger().error(
|
|
2743
|
+
chalk.red(` Error: ${err.route} ${JSON.stringify(err.params)}: ${err.error}`)
|
|
2744
|
+
);
|
|
2745
|
+
}
|
|
2746
|
+
process.exit(1);
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
const runProduction = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
2750
|
+
__proto__: null,
|
|
2751
|
+
initLogger,
|
|
2752
|
+
resolveProductionContext,
|
|
2753
|
+
runBuild,
|
|
2754
|
+
runRebuild,
|
|
2755
|
+
runServe
|
|
2756
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
2586
2757
|
const s = createRequire(import.meta.url), e = s("typescript"), u = e;
|
|
2587
2758
|
new Proxy(e, {
|
|
2588
2759
|
get(t, r) {
|
|
@@ -4260,96 +4431,370 @@ function printJayValidationResult(result, options) {
|
|
|
4260
4431
|
}
|
|
4261
4432
|
}
|
|
4262
4433
|
}
|
|
4263
|
-
async function
|
|
4264
|
-
const
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
}
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4434
|
+
async function runValidate(scanPath, options) {
|
|
4435
|
+
const result = await validateJayFiles({
|
|
4436
|
+
path: scanPath,
|
|
4437
|
+
verbose: options.verbose,
|
|
4438
|
+
json: options.json
|
|
4439
|
+
});
|
|
4440
|
+
printJayValidationResult(result, options);
|
|
4441
|
+
if (!result.valid) {
|
|
4442
|
+
process.exit(1);
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4445
|
+
async function runValidatePlugin(pluginPath, options) {
|
|
4446
|
+
const result = await validatePlugin({
|
|
4447
|
+
pluginPath: pluginPath || process.cwd(),
|
|
4448
|
+
local: options.local,
|
|
4449
|
+
verbose: options.verbose,
|
|
4450
|
+
strict: options.strict,
|
|
4451
|
+
generateTypes: options.generateTypes
|
|
4452
|
+
});
|
|
4453
|
+
printValidationResult(result, options.verbose ?? false);
|
|
4454
|
+
if (!result.valid || options.strict && result.warnings.length > 0) {
|
|
4455
|
+
process.exit(1);
|
|
4456
|
+
}
|
|
4457
|
+
}
|
|
4458
|
+
function printValidationResult(result, verbose) {
|
|
4459
|
+
const logger = getLogger();
|
|
4460
|
+
if (result.valid && result.warnings.length === 0) {
|
|
4461
|
+
logger.important(chalk.green("Plugin validation successful!\n"));
|
|
4462
|
+
if (verbose) {
|
|
4463
|
+
logger.important("Plugin: " + result.pluginName);
|
|
4464
|
+
logger.important(" plugin.yaml valid");
|
|
4465
|
+
logger.important(` ${result.contractsChecked} contracts validated`);
|
|
4466
|
+
if (result.typesGenerated) {
|
|
4467
|
+
logger.important(` ${result.typesGenerated} type definitions generated`);
|
|
4468
|
+
}
|
|
4469
|
+
logger.important(` ${result.componentsChecked} components validated`);
|
|
4470
|
+
if (result.packageJsonChecked) {
|
|
4471
|
+
logger.important(" package.json valid");
|
|
4472
|
+
}
|
|
4473
|
+
logger.important("\nNo errors found.");
|
|
4284
4474
|
}
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4475
|
+
} else if (result.valid && result.warnings.length > 0) {
|
|
4476
|
+
logger.important(chalk.yellow("Plugin validation passed with warnings\n"));
|
|
4477
|
+
logger.important("Warnings:");
|
|
4478
|
+
result.warnings.forEach((warning) => {
|
|
4479
|
+
logger.important(chalk.yellow(` ${warning.message}`));
|
|
4480
|
+
if (warning.location) {
|
|
4481
|
+
logger.important(chalk.gray(` Location: ${warning.location}`));
|
|
4482
|
+
}
|
|
4483
|
+
if (warning.suggestion) {
|
|
4484
|
+
logger.important(chalk.gray(` ${warning.suggestion}`));
|
|
4485
|
+
}
|
|
4486
|
+
logger.important("");
|
|
4487
|
+
});
|
|
4488
|
+
logger.important(chalk.gray("Use --strict to treat warnings as errors."));
|
|
4489
|
+
} else {
|
|
4490
|
+
logger.important(chalk.red("Plugin validation failed\n"));
|
|
4491
|
+
logger.important("Errors:");
|
|
4492
|
+
result.errors.forEach((error) => {
|
|
4493
|
+
logger.important(chalk.red(` ${error.message}`));
|
|
4494
|
+
if (error.location) {
|
|
4495
|
+
logger.important(chalk.gray(` Location: ${error.location}`));
|
|
4496
|
+
}
|
|
4497
|
+
if (error.suggestion) {
|
|
4498
|
+
logger.important(chalk.gray(` ${error.suggestion}`));
|
|
4499
|
+
}
|
|
4500
|
+
logger.important("");
|
|
4501
|
+
});
|
|
4502
|
+
logger.important(chalk.red(`${result.errors.length} errors found.`));
|
|
4503
|
+
}
|
|
4504
|
+
}
|
|
4505
|
+
const ALL_ROLES = ["designer", "developer", "plugin"];
|
|
4506
|
+
async function runAgentKit(options) {
|
|
4507
|
+
const projectRoot = process.cwd();
|
|
4508
|
+
const { initErrors, viteServer } = await runMaterialize(
|
|
4509
|
+
projectRoot,
|
|
4510
|
+
options,
|
|
4511
|
+
"agent-kit/materialized-contracts",
|
|
4512
|
+
true
|
|
4513
|
+
);
|
|
4514
|
+
try {
|
|
4515
|
+
if (!options.list) {
|
|
4516
|
+
await ensureAgentKitDocs(projectRoot, options.force, options.mode);
|
|
4517
|
+
await mergePluginAgentKitGuides(projectRoot, options.mode);
|
|
4518
|
+
if (options.references !== false) {
|
|
4519
|
+
await generatePluginReferences(projectRoot, options, initErrors, viteServer);
|
|
4520
|
+
}
|
|
4292
4521
|
}
|
|
4293
|
-
|
|
4294
|
-
|
|
4522
|
+
} finally {
|
|
4523
|
+
if (viteServer) {
|
|
4524
|
+
await viteServer.close();
|
|
4295
4525
|
}
|
|
4296
|
-
await runInitCallbacks();
|
|
4297
|
-
} catch (error) {
|
|
4298
|
-
getLogger().warn(chalk.yellow(`⚠️ Service initialization failed: ${error.message}`));
|
|
4299
|
-
getLogger().warn(chalk.gray(" Static contracts will still be listed."));
|
|
4300
4526
|
}
|
|
4301
|
-
return { services: getServiceRegistry(), initErrors };
|
|
4302
4527
|
}
|
|
4303
|
-
async function
|
|
4528
|
+
async function runMaterialize(projectRoot, options, defaultOutputRelative, keepViteAlive = false) {
|
|
4529
|
+
const outputDir = options.output ?? path$1.join(projectRoot, defaultOutputRelative);
|
|
4304
4530
|
let viteServer;
|
|
4531
|
+
let initErrors = /* @__PURE__ */ new Map();
|
|
4305
4532
|
try {
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4533
|
+
if (options.list) {
|
|
4534
|
+
const index = await listContracts({
|
|
4535
|
+
projectRoot,
|
|
4536
|
+
dynamicOnly: options.dynamicOnly,
|
|
4537
|
+
pluginFilter: options.plugin
|
|
4538
|
+
});
|
|
4539
|
+
if (options.yaml) {
|
|
4540
|
+
getLogger().important(YAML.stringify(index));
|
|
4541
|
+
} else {
|
|
4542
|
+
printContractList(index);
|
|
4543
|
+
}
|
|
4544
|
+
return { initErrors };
|
|
4312
4545
|
}
|
|
4313
|
-
const actionExport = actionRef.substring(slashIndex + 1);
|
|
4314
|
-
const input = options.input ? JSON.parse(options.input) : {};
|
|
4315
4546
|
if (options.verbose) {
|
|
4316
4547
|
getLogger().info("Starting Vite for TypeScript support...");
|
|
4317
4548
|
}
|
|
4318
4549
|
viteServer = await createViteForCli({ projectRoot });
|
|
4319
|
-
await
|
|
4320
|
-
const { discoverAndRegisterActions, discoverAllPluginActions, ActionRegistry } = await import("@jay-framework/stack-server-runtime");
|
|
4321
|
-
const registry = new ActionRegistry();
|
|
4322
|
-
await discoverAndRegisterActions({
|
|
4323
|
-
projectRoot,
|
|
4324
|
-
registry,
|
|
4325
|
-
verbose: options.verbose,
|
|
4326
|
-
viteServer
|
|
4327
|
-
});
|
|
4328
|
-
await discoverAllPluginActions({
|
|
4550
|
+
const { services, initErrors: errors } = await initializeServicesForCli(
|
|
4329
4551
|
projectRoot,
|
|
4330
|
-
registry,
|
|
4331
|
-
verbose: options.verbose,
|
|
4332
4552
|
viteServer
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
const
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4553
|
+
);
|
|
4554
|
+
initErrors = errors;
|
|
4555
|
+
const result = await materializeContracts(
|
|
4556
|
+
{
|
|
4557
|
+
projectRoot,
|
|
4558
|
+
outputDir,
|
|
4559
|
+
force: options.force,
|
|
4560
|
+
dynamicOnly: options.dynamicOnly,
|
|
4561
|
+
pluginFilter: options.plugin,
|
|
4562
|
+
verbose: options.verbose,
|
|
4563
|
+
viteServer
|
|
4564
|
+
},
|
|
4565
|
+
services
|
|
4566
|
+
);
|
|
4567
|
+
if (options.yaml) {
|
|
4568
|
+
getLogger().important(YAML.stringify(result.pluginsIndex));
|
|
4569
|
+
} else {
|
|
4570
|
+
const totalContracts = result.pluginsIndex.plugins.reduce(
|
|
4571
|
+
(sum, p) => sum + p.contracts.length,
|
|
4572
|
+
0
|
|
4573
|
+
);
|
|
4574
|
+
getLogger().important(chalk.green(`
|
|
4575
|
+
Materialized ${totalContracts} contracts`));
|
|
4576
|
+
getLogger().important(` Static: ${result.staticCount}`);
|
|
4577
|
+
getLogger().important(` Dynamic: ${result.dynamicCount}`);
|
|
4578
|
+
getLogger().important(` Output: ${result.outputDir}`);
|
|
4344
4579
|
}
|
|
4580
|
+
return { initErrors, viteServer: keepViteAlive ? viteServer : void 0 };
|
|
4581
|
+
} catch (error) {
|
|
4582
|
+
getLogger().error(chalk.red("Failed to materialize contracts:") + " " + error.message);
|
|
4345
4583
|
if (options.verbose) {
|
|
4346
|
-
getLogger().
|
|
4347
|
-
getLogger().info(`Input: ${JSON.stringify(input)}`);
|
|
4584
|
+
getLogger().error(error.stack);
|
|
4348
4585
|
}
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4586
|
+
process.exit(1);
|
|
4587
|
+
} finally {
|
|
4588
|
+
if (viteServer && !keepViteAlive) {
|
|
4589
|
+
await viteServer.close();
|
|
4590
|
+
}
|
|
4591
|
+
}
|
|
4592
|
+
return { initErrors };
|
|
4593
|
+
}
|
|
4594
|
+
async function ensureAgentKitDocs(projectRoot, _force, mode) {
|
|
4595
|
+
const agentKitDir = path$1.join(projectRoot, "agent-kit");
|
|
4596
|
+
const thisDir = path$1.dirname(fileURLToPath(import.meta.url));
|
|
4597
|
+
const templateDir = path$1.resolve(thisDir, "..", "agent-kit-template");
|
|
4598
|
+
const roles = mode && ALL_ROLES.includes(mode) ? [mode] : ALL_ROLES;
|
|
4599
|
+
for (const role of roles) {
|
|
4600
|
+
const roleTemplateDir = path$1.join(templateDir, role);
|
|
4601
|
+
const roleOutputDir = path$1.join(agentKitDir, role);
|
|
4602
|
+
let files;
|
|
4603
|
+
try {
|
|
4604
|
+
files = (await fs$1.readdir(roleTemplateDir)).filter((f) => f.endsWith(".md"));
|
|
4605
|
+
} catch {
|
|
4606
|
+
continue;
|
|
4607
|
+
}
|
|
4608
|
+
await fs$1.mkdir(roleOutputDir, { recursive: true });
|
|
4609
|
+
for (const filename of files) {
|
|
4610
|
+
await fs$1.copyFile(
|
|
4611
|
+
path$1.join(roleTemplateDir, filename),
|
|
4612
|
+
path$1.join(roleOutputDir, filename)
|
|
4613
|
+
);
|
|
4614
|
+
getLogger().info(chalk.gray(` Created agent-kit/${role}/${filename}`));
|
|
4615
|
+
}
|
|
4616
|
+
}
|
|
4617
|
+
}
|
|
4618
|
+
async function mergePluginAgentKitGuides(projectRoot, mode) {
|
|
4619
|
+
const plugins = await scanPlugins$1({ projectRoot });
|
|
4620
|
+
const agentKitDir = path$1.join(projectRoot, "agent-kit");
|
|
4621
|
+
const roles = mode && ALL_ROLES.includes(mode) ? [mode] : ALL_ROLES;
|
|
4622
|
+
const copiedPerRole = /* @__PURE__ */ new Map();
|
|
4623
|
+
for (const [, plugin] of plugins) {
|
|
4624
|
+
const pluginAgentKitDir = path$1.join(plugin.pluginPath, "agent-kit");
|
|
4625
|
+
if (!fsSync.existsSync(pluginAgentKitDir))
|
|
4626
|
+
continue;
|
|
4627
|
+
for (const role of roles) {
|
|
4628
|
+
const roleSourceDir = path$1.join(pluginAgentKitDir, role);
|
|
4629
|
+
let files;
|
|
4630
|
+
try {
|
|
4631
|
+
files = (await fs$1.readdir(roleSourceDir)).filter(
|
|
4632
|
+
(f) => f.endsWith(".md") && f !== "INSTRUCTIONS.md"
|
|
4633
|
+
);
|
|
4634
|
+
} catch {
|
|
4635
|
+
continue;
|
|
4636
|
+
}
|
|
4637
|
+
if (files.length === 0)
|
|
4638
|
+
continue;
|
|
4639
|
+
const roleOutputDir = path$1.join(agentKitDir, role);
|
|
4640
|
+
await fs$1.mkdir(roleOutputDir, { recursive: true });
|
|
4641
|
+
for (const filename of files) {
|
|
4642
|
+
const sourcePath = path$1.join(roleSourceDir, filename);
|
|
4643
|
+
await fs$1.copyFile(sourcePath, path$1.join(roleOutputDir, filename));
|
|
4644
|
+
let description = "";
|
|
4645
|
+
try {
|
|
4646
|
+
const content = await fs$1.readFile(sourcePath, "utf-8");
|
|
4647
|
+
const lines = content.split("\n");
|
|
4648
|
+
let pastHeading = false;
|
|
4649
|
+
for (const line of lines) {
|
|
4650
|
+
if (line.startsWith("# ")) {
|
|
4651
|
+
pastHeading = true;
|
|
4652
|
+
continue;
|
|
4653
|
+
}
|
|
4654
|
+
if (pastHeading && line.trim()) {
|
|
4655
|
+
description = line.trim();
|
|
4656
|
+
break;
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4659
|
+
} catch {
|
|
4660
|
+
}
|
|
4661
|
+
if (!copiedPerRole.has(role))
|
|
4662
|
+
copiedPerRole.set(role, []);
|
|
4663
|
+
copiedPerRole.get(role).push({ filename, pluginName: plugin.name, description });
|
|
4664
|
+
getLogger().info(
|
|
4665
|
+
chalk.gray(
|
|
4666
|
+
` Copied agent-kit/${role}/${filename} from plugin "${plugin.name}"`
|
|
4667
|
+
)
|
|
4668
|
+
);
|
|
4669
|
+
}
|
|
4670
|
+
}
|
|
4671
|
+
}
|
|
4672
|
+
for (const [role, entries] of copiedPerRole) {
|
|
4673
|
+
const instructionsPath = path$1.join(agentKitDir, role, "INSTRUCTIONS.md");
|
|
4674
|
+
if (!fsSync.existsSync(instructionsPath))
|
|
4675
|
+
continue;
|
|
4676
|
+
const lines = [
|
|
4677
|
+
"",
|
|
4678
|
+
"## Plugin-Contributed Guides",
|
|
4679
|
+
"",
|
|
4680
|
+
"| File | Plugin | Description |",
|
|
4681
|
+
"| --- | --- | --- |"
|
|
4682
|
+
];
|
|
4683
|
+
for (const { filename, pluginName, description } of entries) {
|
|
4684
|
+
lines.push(`| [${filename}](${filename}) | ${pluginName} | ${description} |`);
|
|
4685
|
+
}
|
|
4686
|
+
lines.push("");
|
|
4687
|
+
await fs$1.appendFile(instructionsPath, lines.join("\n"));
|
|
4688
|
+
}
|
|
4689
|
+
}
|
|
4690
|
+
async function generatePluginReferences(projectRoot, options, initErrors, viteServer) {
|
|
4691
|
+
const { discoverPluginsWithReferences, executePluginReferences } = await import("@jay-framework/stack-server-runtime");
|
|
4692
|
+
const plugins = await discoverPluginsWithReferences({
|
|
4693
|
+
projectRoot,
|
|
4694
|
+
verbose: options.verbose,
|
|
4695
|
+
pluginFilter: options.plugin
|
|
4696
|
+
});
|
|
4697
|
+
if (plugins.length === 0)
|
|
4698
|
+
return;
|
|
4699
|
+
const logger = getLogger();
|
|
4700
|
+
logger.important("");
|
|
4701
|
+
logger.important(chalk.bold("Generating plugin references..."));
|
|
4702
|
+
for (const plugin of plugins) {
|
|
4703
|
+
const pluginInitError = initErrors.get(plugin.name);
|
|
4704
|
+
if (pluginInitError) {
|
|
4705
|
+
logger.warn(
|
|
4706
|
+
chalk.yellow(
|
|
4707
|
+
` ${plugin.name}: references skipped — init failed: ${pluginInitError.message}`
|
|
4708
|
+
)
|
|
4709
|
+
);
|
|
4710
|
+
continue;
|
|
4711
|
+
}
|
|
4712
|
+
try {
|
|
4713
|
+
const result = await executePluginReferences(plugin, {
|
|
4714
|
+
projectRoot,
|
|
4715
|
+
force: options.force ?? false,
|
|
4716
|
+
viteServer,
|
|
4717
|
+
verbose: options.verbose
|
|
4718
|
+
});
|
|
4719
|
+
if (result.referencesCreated.length > 0) {
|
|
4720
|
+
logger.important(chalk.green(` ${plugin.name}:`));
|
|
4721
|
+
for (const ref of result.referencesCreated) {
|
|
4722
|
+
logger.important(chalk.gray(` ${ref}`));
|
|
4723
|
+
}
|
|
4724
|
+
if (result.message) {
|
|
4725
|
+
logger.important(chalk.gray(` ${result.message}`));
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4728
|
+
} catch (error) {
|
|
4729
|
+
logger.warn(chalk.yellow(` ${plugin.name}: references skipped — ${error.message}`));
|
|
4730
|
+
}
|
|
4731
|
+
}
|
|
4732
|
+
}
|
|
4733
|
+
function printContractList(index) {
|
|
4734
|
+
const logger = getLogger();
|
|
4735
|
+
logger.important("\nAvailable Contracts:\n");
|
|
4736
|
+
for (const plugin of index.plugins) {
|
|
4737
|
+
logger.important(chalk.bold(plugin.name));
|
|
4738
|
+
for (const contract of plugin.contracts) {
|
|
4739
|
+
const typeIcon = contract.type === "static" ? "static" : "dynamic";
|
|
4740
|
+
logger.important(` [${typeIcon}] ${contract.name}`);
|
|
4741
|
+
}
|
|
4742
|
+
logger.important("");
|
|
4743
|
+
}
|
|
4744
|
+
if (index.plugins.length === 0) {
|
|
4745
|
+
logger.important(chalk.gray("No contracts found."));
|
|
4746
|
+
}
|
|
4747
|
+
}
|
|
4748
|
+
async function runAction(actionRef, options, projectRoot, initializeServices) {
|
|
4749
|
+
let viteServer;
|
|
4750
|
+
try {
|
|
4751
|
+
const slashIndex = actionRef.indexOf("/");
|
|
4752
|
+
if (slashIndex === -1) {
|
|
4753
|
+
getLogger().error(
|
|
4754
|
+
chalk.red("❌ Invalid action reference. Use format: <plugin>/<action>")
|
|
4755
|
+
);
|
|
4756
|
+
process.exit(1);
|
|
4757
|
+
}
|
|
4758
|
+
const actionExport = actionRef.substring(slashIndex + 1);
|
|
4759
|
+
const input = options.input ? JSON.parse(options.input) : {};
|
|
4760
|
+
if (options.verbose) {
|
|
4761
|
+
getLogger().info("Starting Vite for TypeScript support...");
|
|
4762
|
+
}
|
|
4763
|
+
viteServer = await createViteForCli({ projectRoot });
|
|
4764
|
+
await initializeServices(projectRoot, viteServer);
|
|
4765
|
+
const { discoverAndRegisterActions, discoverAllPluginActions, ActionRegistry } = await import("@jay-framework/stack-server-runtime");
|
|
4766
|
+
const registry = new ActionRegistry();
|
|
4767
|
+
await discoverAndRegisterActions({
|
|
4768
|
+
projectRoot,
|
|
4769
|
+
registry,
|
|
4770
|
+
verbose: options.verbose,
|
|
4771
|
+
viteServer
|
|
4772
|
+
});
|
|
4773
|
+
await discoverAllPluginActions({
|
|
4774
|
+
projectRoot,
|
|
4775
|
+
registry,
|
|
4776
|
+
verbose: options.verbose,
|
|
4777
|
+
viteServer
|
|
4778
|
+
});
|
|
4779
|
+
const allNames = registry.getNames();
|
|
4780
|
+
const matchedName = allNames.find((name) => name === actionExport) || allNames.find((name) => name.endsWith("." + actionExport)) || allNames.find((name) => name === actionRef);
|
|
4781
|
+
if (!matchedName) {
|
|
4782
|
+
getLogger().error(chalk.red(`❌ Action "${actionExport}" not found.`));
|
|
4783
|
+
if (allNames.length > 0) {
|
|
4784
|
+
getLogger().error(` Available actions: ${allNames.join(", ")}`);
|
|
4785
|
+
} else {
|
|
4786
|
+
getLogger().error(" No actions registered. Does the plugin have actions?");
|
|
4787
|
+
}
|
|
4788
|
+
process.exit(1);
|
|
4789
|
+
}
|
|
4790
|
+
if (options.verbose) {
|
|
4791
|
+
getLogger().info(`Executing action: ${matchedName}`);
|
|
4792
|
+
getLogger().info(`Input: ${JSON.stringify(input)}`);
|
|
4793
|
+
}
|
|
4794
|
+
const result = await registry.execute(matchedName, input);
|
|
4795
|
+
if (result.success) {
|
|
4796
|
+
if (options.yaml) {
|
|
4797
|
+
getLogger().important(YAML.stringify(result.data));
|
|
4353
4798
|
} else {
|
|
4354
4799
|
getLogger().important(JSON.stringify(result.data, null, 2));
|
|
4355
4800
|
}
|
|
@@ -4586,45 +5031,17 @@ async function runSetup(pluginFilter, options, projectRoot, initializeServices)
|
|
|
4586
5031
|
}
|
|
4587
5032
|
const program = new Command();
|
|
4588
5033
|
program.name("jay-stack").description("Jay Stack CLI - Development server and plugin validation").version("0.9.0");
|
|
4589
|
-
program.command("dev
|
|
5034
|
+
program.command("dev").description("Start the Jay Stack development server").option("-p, --path <path>", "Project root (default: cwd)").option("-v, --verbose", "Enable verbose logging output").option("-q, --quiet", "Suppress all non-error output").option("--test-mode", "Enable test endpoints (/_jay/health, /_jay/shutdown)").option("--timeout <seconds>", "Auto-shutdown after N seconds (implies --test-mode)", parseInt).action(async (options) => {
|
|
4590
5035
|
try {
|
|
4591
|
-
|
|
4592
|
-
setDevLogger(createDevLogger(logLevel));
|
|
4593
|
-
const testMode = options.testMode || options.timeout !== void 0;
|
|
4594
|
-
await startDevServer({
|
|
4595
|
-
projectPath: path2 || process.cwd(),
|
|
4596
|
-
testMode,
|
|
4597
|
-
timeout: options.timeout,
|
|
4598
|
-
logLevel
|
|
4599
|
-
});
|
|
5036
|
+
await runDev(options.path, options);
|
|
4600
5037
|
} catch (error) {
|
|
4601
5038
|
getLogger().error(chalk.red("Error starting dev server:") + " " + error.message);
|
|
4602
5039
|
process.exit(1);
|
|
4603
5040
|
}
|
|
4604
5041
|
});
|
|
4605
|
-
program.command("build
|
|
5042
|
+
program.command("build").description("Build production artifacts").option("-p, --path <path>", "Project root (default: cwd)").option("--version <n>", "Build version number (default: from package.json)").option("--no-minify", "Disable minification (useful for debugging)").option("-v, --verbose", "Enable verbose logging output").action(async (options) => {
|
|
4606
5043
|
try {
|
|
4607
|
-
|
|
4608
|
-
setDevLogger(createDevLogger(logLevel));
|
|
4609
|
-
const resolvedPath = path$1.resolve(projectPath || process.cwd());
|
|
4610
|
-
const jayConfigPath = path$1.join(resolvedPath, ".jay");
|
|
4611
|
-
let pagesBase = "./src/pages";
|
|
4612
|
-
try {
|
|
4613
|
-
const jayConfig = YAML.parse(await fs$1.readFile(jayConfigPath, "utf-8"));
|
|
4614
|
-
pagesBase = jayConfig?.devServer?.pagesBase || pagesBase;
|
|
4615
|
-
} catch {
|
|
4616
|
-
}
|
|
4617
|
-
const { buildVersion } = await import("@jay-framework/production-server");
|
|
4618
|
-
await buildVersion({
|
|
4619
|
-
version: parseInt(options.version, 10),
|
|
4620
|
-
projectRoot: resolvedPath,
|
|
4621
|
-
pagesRoot: path$1.resolve(resolvedPath, pagesBase),
|
|
4622
|
-
buildRoot: path$1.join(resolvedPath, "build"),
|
|
4623
|
-
publicBasePath: "/",
|
|
4624
|
-
concurrency: 4,
|
|
4625
|
-
tsConfigFilePath: path$1.join(resolvedPath, "tsconfig.json"),
|
|
4626
|
-
minify: options.minify
|
|
4627
|
-
});
|
|
5044
|
+
await runBuild(options.path, options);
|
|
4628
5045
|
} catch (error) {
|
|
4629
5046
|
getLogger().error(chalk.red("Build failed:") + " " + error.message);
|
|
4630
5047
|
if (error.stack)
|
|
@@ -4632,17 +5049,9 @@ program.command("build [path]").description("Build production artifacts").option
|
|
|
4632
5049
|
process.exit(1);
|
|
4633
5050
|
}
|
|
4634
5051
|
});
|
|
4635
|
-
program.command("serve
|
|
5052
|
+
program.command("serve").description("Start production server").option("-p, --path <path>", "Project root (default: cwd)").option("--version <n>", "Build version to serve (default: from package.json)").option("--port <n>", "Port number", "3000").option("--role <role>", "Server role: main (default) or renderer", "main").option("--static-base-url <url>", "Base URL for browser-facing assets (default: /)").option("--no-serve-static", "Disable serving static files from frontend/").option("--test-mode", "Enable test endpoints (/_jay/health, /_jay/shutdown)").option("-v, --verbose", "Enable verbose logging output").action(async (options) => {
|
|
4636
5053
|
try {
|
|
4637
|
-
|
|
4638
|
-
const resolvedPath = path$1.resolve(projectPath || process.cwd());
|
|
4639
|
-
const { startMainServer } = await import("@jay-framework/production-server");
|
|
4640
|
-
await startMainServer({
|
|
4641
|
-
buildRoot: path$1.join(resolvedPath, "build"),
|
|
4642
|
-
version: parseInt(options.version, 10),
|
|
4643
|
-
port: parseInt(options.port, 10),
|
|
4644
|
-
publicBasePath: "/"
|
|
4645
|
-
});
|
|
5054
|
+
await runServe(options.path, options);
|
|
4646
5055
|
} catch (error) {
|
|
4647
5056
|
getLogger().error(chalk.red("Server failed:") + " " + error.message);
|
|
4648
5057
|
if (error.stack)
|
|
@@ -4650,17 +5059,31 @@ program.command("serve [path]").description("Start production server").option("-
|
|
|
4650
5059
|
process.exit(1);
|
|
4651
5060
|
}
|
|
4652
5061
|
});
|
|
4653
|
-
program.command("
|
|
5062
|
+
program.command("rebuild").description("Rebuild instances by contract, route, or URL").option("--contract <name>", "Rebuild by contract name (e.g., product-page)").option("--route <pattern>", "Rebuild by route pattern (e.g., /products/[slug])").option("--url <url>", "Rebuild by URL (e.g., /products/blue-widget)").option("--params <json>", `JSON params to rebuild specific instance (e.g., '{"slug":"x"}')`).option("--version <n>", "Build version (default: from package.json)").option("-p, --path <path>", "Project root (default: cwd)").option("-v, --verbose", "Enable verbose logging output").action(async (options) => {
|
|
4654
5063
|
try {
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
5064
|
+
await runRebuild(options.path, options);
|
|
5065
|
+
} catch (error) {
|
|
5066
|
+
getLogger().error(chalk.red("Rebuild failed:") + " " + error.message);
|
|
5067
|
+
if (error.stack)
|
|
5068
|
+
getLogger().error(error.stack);
|
|
5069
|
+
process.exit(1);
|
|
5070
|
+
}
|
|
5071
|
+
});
|
|
5072
|
+
program.command("cleanup").description("Delete orphaned files from previous rebuilds").option("--version <n>", "Build version (default: from package.json)").option("-p, --path <path>", "Project root (default: cwd)").action(async (options) => {
|
|
5073
|
+
try {
|
|
5074
|
+
const { initLogger: initLogger2, resolveProductionContext: resolveProductionContext2 } = await Promise.resolve().then(() => runProduction);
|
|
5075
|
+
initLogger2();
|
|
5076
|
+
const ctx = await resolveProductionContext2(options.path, options.version);
|
|
5077
|
+
const { cleanupOrphanedFiles } = await import("@jay-framework/production-server");
|
|
5078
|
+
await cleanupOrphanedFiles(ctx.buildRoot, ctx.version);
|
|
5079
|
+
} catch (error) {
|
|
5080
|
+
getLogger().error(chalk.red("Cleanup failed:") + " " + error.message);
|
|
5081
|
+
process.exit(1);
|
|
5082
|
+
}
|
|
5083
|
+
});
|
|
5084
|
+
program.command("validate").description("Validate all .jay-html and .jay-contract files").option("-p, --path <path>", "Scan root (default: cwd)").option("-v, --verbose", "Show per-file validation status").option("--json", "Output results as JSON").action(async (options) => {
|
|
5085
|
+
try {
|
|
5086
|
+
await runValidate(options.path, options);
|
|
4664
5087
|
} catch (error) {
|
|
4665
5088
|
if (options.json) {
|
|
4666
5089
|
getLogger().important(
|
|
@@ -4672,342 +5095,33 @@ program.command("validate [path]").description("Validate all .jay-html and .jay-
|
|
|
4672
5095
|
process.exit(1);
|
|
4673
5096
|
}
|
|
4674
5097
|
});
|
|
4675
|
-
program.command("validate-plugin
|
|
5098
|
+
program.command("validate-plugin").description("Validate a Jay Stack plugin package").option("-p, --path <path>", "Plugin root (default: cwd)").option("--local", "Validate local plugins in src/plugins/").option("-v, --verbose", "Show detailed validation output").option("--strict", "Treat warnings as errors (for CI)").option("--generate-types", "Generate .d.ts files for contracts").action(async (options) => {
|
|
4676
5099
|
try {
|
|
4677
|
-
|
|
4678
|
-
pluginPath: pluginPath || process.cwd(),
|
|
4679
|
-
local: options.local,
|
|
4680
|
-
verbose: options.verbose,
|
|
4681
|
-
strict: options.strict,
|
|
4682
|
-
generateTypes: options.generateTypes
|
|
4683
|
-
});
|
|
4684
|
-
printValidationResult(result, options.verbose);
|
|
4685
|
-
if (!result.valid || options.strict && result.warnings.length > 0) {
|
|
4686
|
-
process.exit(1);
|
|
4687
|
-
}
|
|
5100
|
+
await runValidatePlugin(options.path, options);
|
|
4688
5101
|
} catch (error) {
|
|
4689
5102
|
getLogger().error(chalk.red("Validation error:") + " " + error.message);
|
|
4690
5103
|
process.exit(1);
|
|
4691
5104
|
}
|
|
4692
5105
|
});
|
|
4693
|
-
|
|
4694
|
-
async function ensureAgentKitDocs(projectRoot, _force, mode) {
|
|
4695
|
-
const agentKitDir = path$1.join(projectRoot, "agent-kit");
|
|
4696
|
-
const thisDir = path$1.dirname(fileURLToPath(import.meta.url));
|
|
4697
|
-
const templateDir = path$1.resolve(thisDir, "..", "agent-kit-template");
|
|
4698
|
-
const roles = mode && ALL_ROLES.includes(mode) ? [mode] : ALL_ROLES;
|
|
4699
|
-
for (const role of roles) {
|
|
4700
|
-
const roleTemplateDir = path$1.join(templateDir, role);
|
|
4701
|
-
const roleOutputDir = path$1.join(agentKitDir, role);
|
|
4702
|
-
let files;
|
|
4703
|
-
try {
|
|
4704
|
-
files = (await fs$1.readdir(roleTemplateDir)).filter((f) => f.endsWith(".md"));
|
|
4705
|
-
} catch {
|
|
4706
|
-
continue;
|
|
4707
|
-
}
|
|
4708
|
-
await fs$1.mkdir(roleOutputDir, { recursive: true });
|
|
4709
|
-
for (const filename of files) {
|
|
4710
|
-
await fs$1.copyFile(
|
|
4711
|
-
path$1.join(roleTemplateDir, filename),
|
|
4712
|
-
path$1.join(roleOutputDir, filename)
|
|
4713
|
-
);
|
|
4714
|
-
getLogger().info(chalk.gray(` Created agent-kit/${role}/${filename}`));
|
|
4715
|
-
}
|
|
4716
|
-
}
|
|
4717
|
-
}
|
|
4718
|
-
async function mergePluginAgentKitGuides(projectRoot, mode) {
|
|
4719
|
-
const plugins = await scanPlugins$1({ projectRoot });
|
|
4720
|
-
const agentKitDir = path$1.join(projectRoot, "agent-kit");
|
|
4721
|
-
const roles = mode && ALL_ROLES.includes(mode) ? [mode] : ALL_ROLES;
|
|
4722
|
-
const copiedPerRole = /* @__PURE__ */ new Map();
|
|
4723
|
-
for (const [, plugin] of plugins) {
|
|
4724
|
-
const pluginAgentKitDir = path$1.join(plugin.pluginPath, "agent-kit");
|
|
4725
|
-
if (!fsSync.existsSync(pluginAgentKitDir))
|
|
4726
|
-
continue;
|
|
4727
|
-
for (const role of roles) {
|
|
4728
|
-
const roleSourceDir = path$1.join(pluginAgentKitDir, role);
|
|
4729
|
-
let files;
|
|
4730
|
-
try {
|
|
4731
|
-
files = (await fs$1.readdir(roleSourceDir)).filter(
|
|
4732
|
-
(f) => f.endsWith(".md") && f !== "INSTRUCTIONS.md"
|
|
4733
|
-
);
|
|
4734
|
-
} catch {
|
|
4735
|
-
continue;
|
|
4736
|
-
}
|
|
4737
|
-
if (files.length === 0)
|
|
4738
|
-
continue;
|
|
4739
|
-
const roleOutputDir = path$1.join(agentKitDir, role);
|
|
4740
|
-
await fs$1.mkdir(roleOutputDir, { recursive: true });
|
|
4741
|
-
for (const filename of files) {
|
|
4742
|
-
const sourcePath = path$1.join(roleSourceDir, filename);
|
|
4743
|
-
await fs$1.copyFile(sourcePath, path$1.join(roleOutputDir, filename));
|
|
4744
|
-
let description = "";
|
|
4745
|
-
try {
|
|
4746
|
-
const content = await fs$1.readFile(sourcePath, "utf-8");
|
|
4747
|
-
const lines = content.split("\n");
|
|
4748
|
-
let pastHeading = false;
|
|
4749
|
-
for (const line of lines) {
|
|
4750
|
-
if (line.startsWith("# ")) {
|
|
4751
|
-
pastHeading = true;
|
|
4752
|
-
continue;
|
|
4753
|
-
}
|
|
4754
|
-
if (pastHeading && line.trim()) {
|
|
4755
|
-
description = line.trim();
|
|
4756
|
-
break;
|
|
4757
|
-
}
|
|
4758
|
-
}
|
|
4759
|
-
} catch {
|
|
4760
|
-
}
|
|
4761
|
-
if (!copiedPerRole.has(role))
|
|
4762
|
-
copiedPerRole.set(role, []);
|
|
4763
|
-
copiedPerRole.get(role).push({ filename, pluginName: plugin.name, description });
|
|
4764
|
-
getLogger().info(
|
|
4765
|
-
chalk.gray(
|
|
4766
|
-
` Copied agent-kit/${role}/${filename} from plugin "${plugin.name}"`
|
|
4767
|
-
)
|
|
4768
|
-
);
|
|
4769
|
-
}
|
|
4770
|
-
}
|
|
4771
|
-
}
|
|
4772
|
-
for (const [role, entries] of copiedPerRole) {
|
|
4773
|
-
const instructionsPath = path$1.join(agentKitDir, role, "INSTRUCTIONS.md");
|
|
4774
|
-
if (!fsSync.existsSync(instructionsPath))
|
|
4775
|
-
continue;
|
|
4776
|
-
const lines = [
|
|
4777
|
-
"",
|
|
4778
|
-
"## Plugin-Contributed Guides",
|
|
4779
|
-
"",
|
|
4780
|
-
"| File | Plugin | Description |",
|
|
4781
|
-
"| --- | --- | --- |"
|
|
4782
|
-
];
|
|
4783
|
-
for (const { filename, pluginName, description } of entries) {
|
|
4784
|
-
lines.push(`| [${filename}](${filename}) | ${pluginName} | ${description} |`);
|
|
4785
|
-
}
|
|
4786
|
-
lines.push("");
|
|
4787
|
-
await fs$1.appendFile(instructionsPath, lines.join("\n"));
|
|
4788
|
-
}
|
|
4789
|
-
}
|
|
4790
|
-
async function generatePluginReferences(projectRoot, options, initErrors, viteServer) {
|
|
4791
|
-
const { discoverPluginsWithReferences, executePluginReferences } = await import("@jay-framework/stack-server-runtime");
|
|
4792
|
-
const plugins = await discoverPluginsWithReferences({
|
|
4793
|
-
projectRoot,
|
|
4794
|
-
verbose: options.verbose,
|
|
4795
|
-
pluginFilter: options.plugin
|
|
4796
|
-
});
|
|
4797
|
-
if (plugins.length === 0)
|
|
4798
|
-
return;
|
|
4799
|
-
const logger = getLogger();
|
|
4800
|
-
logger.important("");
|
|
4801
|
-
logger.important(chalk.bold("📚 Generating plugin references..."));
|
|
4802
|
-
for (const plugin of plugins) {
|
|
4803
|
-
const pluginInitError = initErrors.get(plugin.name);
|
|
4804
|
-
if (pluginInitError) {
|
|
4805
|
-
logger.warn(
|
|
4806
|
-
chalk.yellow(
|
|
4807
|
-
` ⚠️ ${plugin.name}: references skipped — init failed: ${pluginInitError.message}`
|
|
4808
|
-
)
|
|
4809
|
-
);
|
|
4810
|
-
continue;
|
|
4811
|
-
}
|
|
4812
|
-
try {
|
|
4813
|
-
const result = await executePluginReferences(plugin, {
|
|
4814
|
-
projectRoot,
|
|
4815
|
-
force: options.force ?? false,
|
|
4816
|
-
viteServer,
|
|
4817
|
-
verbose: options.verbose
|
|
4818
|
-
});
|
|
4819
|
-
if (result.referencesCreated.length > 0) {
|
|
4820
|
-
logger.important(chalk.green(` ✅ ${plugin.name}:`));
|
|
4821
|
-
for (const ref of result.referencesCreated) {
|
|
4822
|
-
logger.important(chalk.gray(` ${ref}`));
|
|
4823
|
-
}
|
|
4824
|
-
if (result.message) {
|
|
4825
|
-
logger.important(chalk.gray(` ${result.message}`));
|
|
4826
|
-
}
|
|
4827
|
-
}
|
|
4828
|
-
} catch (error) {
|
|
4829
|
-
logger.warn(
|
|
4830
|
-
chalk.yellow(` ⚠️ ${plugin.name}: references skipped — ${error.message}`)
|
|
4831
|
-
);
|
|
4832
|
-
}
|
|
4833
|
-
}
|
|
4834
|
-
}
|
|
4835
|
-
async function runMaterialize(projectRoot, options, defaultOutputRelative, keepViteAlive = false) {
|
|
4836
|
-
const path2 = await import("node:path");
|
|
4837
|
-
const outputDir = options.output ?? path2.join(projectRoot, defaultOutputRelative);
|
|
4838
|
-
let viteServer;
|
|
4839
|
-
let initErrors = /* @__PURE__ */ new Map();
|
|
4840
|
-
try {
|
|
4841
|
-
if (options.list) {
|
|
4842
|
-
const index = await listContracts({
|
|
4843
|
-
projectRoot,
|
|
4844
|
-
dynamicOnly: options.dynamicOnly,
|
|
4845
|
-
pluginFilter: options.plugin
|
|
4846
|
-
});
|
|
4847
|
-
if (options.yaml) {
|
|
4848
|
-
getLogger().important(YAML.stringify(index));
|
|
4849
|
-
} else {
|
|
4850
|
-
printContractList(index);
|
|
4851
|
-
}
|
|
4852
|
-
return { initErrors };
|
|
4853
|
-
}
|
|
4854
|
-
if (options.verbose) {
|
|
4855
|
-
getLogger().info("Starting Vite for TypeScript support...");
|
|
4856
|
-
}
|
|
4857
|
-
viteServer = await createViteForCli({ projectRoot });
|
|
4858
|
-
const { services, initErrors: errors } = await initializeServicesForCli(
|
|
4859
|
-
projectRoot,
|
|
4860
|
-
viteServer
|
|
4861
|
-
);
|
|
4862
|
-
initErrors = errors;
|
|
4863
|
-
const result = await materializeContracts(
|
|
4864
|
-
{
|
|
4865
|
-
projectRoot,
|
|
4866
|
-
outputDir,
|
|
4867
|
-
force: options.force,
|
|
4868
|
-
dynamicOnly: options.dynamicOnly,
|
|
4869
|
-
pluginFilter: options.plugin,
|
|
4870
|
-
verbose: options.verbose,
|
|
4871
|
-
viteServer
|
|
4872
|
-
},
|
|
4873
|
-
services
|
|
4874
|
-
);
|
|
4875
|
-
if (options.yaml) {
|
|
4876
|
-
getLogger().important(YAML.stringify(result.pluginsIndex));
|
|
4877
|
-
} else {
|
|
4878
|
-
const totalContracts = result.pluginsIndex.plugins.reduce(
|
|
4879
|
-
(sum, p) => sum + p.contracts.length,
|
|
4880
|
-
0
|
|
4881
|
-
);
|
|
4882
|
-
getLogger().important(chalk.green(`
|
|
4883
|
-
✅ Materialized ${totalContracts} contracts`));
|
|
4884
|
-
getLogger().important(` Static: ${result.staticCount}`);
|
|
4885
|
-
getLogger().important(` Dynamic: ${result.dynamicCount}`);
|
|
4886
|
-
getLogger().important(` Output: ${result.outputDir}`);
|
|
4887
|
-
}
|
|
4888
|
-
return { initErrors, viteServer: keepViteAlive ? viteServer : void 0 };
|
|
4889
|
-
} catch (error) {
|
|
4890
|
-
getLogger().error(chalk.red("❌ Failed to materialize contracts:") + " " + error.message);
|
|
4891
|
-
if (options.verbose) {
|
|
4892
|
-
getLogger().error(error.stack);
|
|
4893
|
-
}
|
|
4894
|
-
process.exit(1);
|
|
4895
|
-
} finally {
|
|
4896
|
-
if (viteServer && !keepViteAlive) {
|
|
4897
|
-
await viteServer.close();
|
|
4898
|
-
}
|
|
4899
|
-
}
|
|
4900
|
-
return { initErrors };
|
|
4901
|
-
}
|
|
4902
|
-
program.command("setup [plugin]").description(
|
|
4903
|
-
"Run plugin setup: create config templates, validate credentials, generate reference data"
|
|
4904
|
-
).option("--force", "Force re-run (overwrite config templates and regenerate references)").option("-v, --verbose", "Show detailed output").action(async (plugin, options) => {
|
|
5106
|
+
program.command("setup [plugin]").description("Run plugin setup: config templates, credential validation, reference data").option("--force", "Force re-run (overwrite config templates and regenerate references)").option("-v, --verbose", "Show detailed output").action(async (plugin, options) => {
|
|
4905
5107
|
await runSetup(plugin, options, process.cwd(), initializeServicesForCli);
|
|
4906
5108
|
});
|
|
4907
|
-
program.command("agent-kit").description(
|
|
4908
|
-
"Prepare the agent kit: materialize contracts, generate references, and write docs to agent-kit/"
|
|
4909
|
-
).option("-o, --output <dir>", "Output directory (default: agent-kit/materialized-contracts)").option("--yaml", "Output contract index as YAML to stdout").option("--list", "List contracts without writing files").option("--plugin <name>", "Filter to specific plugin").option("--dynamic-only", "Only process dynamic contracts").option("--force", "Force re-materialization").option("--no-references", "Skip reference data generation").option(
|
|
5109
|
+
program.command("agent-kit").description("Prepare agent kit: materialize contracts, generate references, write docs").option("-o, --output <dir>", "Output directory (default: agent-kit/materialized-contracts)").option("--yaml", "Output contract index as YAML to stdout").option("--list", "List contracts without writing files").option("--plugin <name>", "Filter to specific plugin").option("--dynamic-only", "Only process dynamic contracts").option("--force", "Force re-materialization").option("--no-references", "Skip reference data generation").option(
|
|
4910
5110
|
"-m, --mode <role>",
|
|
4911
5111
|
"Generate guides for a specific role: designer, developer, or plugin (default: all)"
|
|
4912
5112
|
).option("-v, --verbose", "Show detailed output").action(async (options) => {
|
|
4913
|
-
|
|
4914
|
-
const { initErrors, viteServer } = await runMaterialize(
|
|
4915
|
-
projectRoot,
|
|
4916
|
-
options,
|
|
4917
|
-
"agent-kit/materialized-contracts",
|
|
4918
|
-
/* keepViteAlive */
|
|
4919
|
-
true
|
|
4920
|
-
);
|
|
4921
|
-
try {
|
|
4922
|
-
if (!options.list) {
|
|
4923
|
-
await ensureAgentKitDocs(projectRoot, options.force, options.mode);
|
|
4924
|
-
await mergePluginAgentKitGuides(projectRoot, options.mode);
|
|
4925
|
-
if (options.references !== false) {
|
|
4926
|
-
await generatePluginReferences(projectRoot, options, initErrors, viteServer);
|
|
4927
|
-
}
|
|
4928
|
-
}
|
|
4929
|
-
} finally {
|
|
4930
|
-
if (viteServer) {
|
|
4931
|
-
await viteServer.close();
|
|
4932
|
-
}
|
|
4933
|
-
}
|
|
5113
|
+
await runAgentKit(options);
|
|
4934
5114
|
});
|
|
4935
|
-
program.command("action <plugin/action>").description(
|
|
4936
|
-
`Run a plugin action (e.g., jay-stack action wix-stores/searchProducts --input '{"query":""}')`
|
|
4937
|
-
).option("--input <json>", "JSON input for the action (default: {})").option("--yaml", "Output result as YAML instead of JSON").option("-v, --verbose", "Show detailed output").action(async (actionRef, options) => {
|
|
5115
|
+
program.command("action <plugin/action>").description("Run a plugin action (e.g., jay-stack action wix-stores/searchProducts)").option("--input <json>", "JSON input for the action (default: {})").option("--yaml", "Output result as YAML instead of JSON").option("-v, --verbose", "Show detailed output").action(async (actionRef, options) => {
|
|
4938
5116
|
await runAction(actionRef, options, process.cwd(), initializeServicesForCli);
|
|
4939
5117
|
});
|
|
4940
|
-
program.command("params <plugin/contract>").description(
|
|
4941
|
-
"Discover load param values for a contract. Streams results as NDJSON (one JSON object per line) or YAML."
|
|
4942
|
-
).option("--yaml", "Output as YAML instead of NDJSON").option("-v, --verbose", "Show detailed output").action(async (contractRef, options) => {
|
|
5118
|
+
program.command("params <plugin/contract>").description("Discover load param values for a contract (NDJSON or YAML)").option("--yaml", "Output as YAML instead of NDJSON").option("-v, --verbose", "Show detailed output").action(async (contractRef, options) => {
|
|
4943
5119
|
await runParams(contractRef, options, process.cwd(), initializeServicesForCli);
|
|
4944
5120
|
});
|
|
4945
5121
|
program.parse(process.argv);
|
|
4946
5122
|
if (!process.argv.slice(2).length) {
|
|
4947
5123
|
program.outputHelp();
|
|
4948
5124
|
}
|
|
4949
|
-
function printValidationResult(result, verbose) {
|
|
4950
|
-
const logger = getLogger();
|
|
4951
|
-
if (result.valid && result.warnings.length === 0) {
|
|
4952
|
-
logger.important(chalk.green("✅ Plugin validation successful!\n"));
|
|
4953
|
-
if (verbose) {
|
|
4954
|
-
logger.important("Plugin: " + result.pluginName);
|
|
4955
|
-
logger.important(" ✅ plugin.yaml valid");
|
|
4956
|
-
logger.important(` ✅ ${result.contractsChecked} contracts validated`);
|
|
4957
|
-
if (result.typesGenerated) {
|
|
4958
|
-
logger.important(` ✅ ${result.typesGenerated} type definitions generated`);
|
|
4959
|
-
}
|
|
4960
|
-
logger.important(` ✅ ${result.componentsChecked} components validated`);
|
|
4961
|
-
if (result.packageJsonChecked) {
|
|
4962
|
-
logger.important(" ✅ package.json valid");
|
|
4963
|
-
}
|
|
4964
|
-
logger.important("\nNo errors found.");
|
|
4965
|
-
}
|
|
4966
|
-
} else if (result.valid && result.warnings.length > 0) {
|
|
4967
|
-
logger.important(chalk.yellow("⚠️ Plugin validation passed with warnings\n"));
|
|
4968
|
-
logger.important("Warnings:");
|
|
4969
|
-
result.warnings.forEach((warning) => {
|
|
4970
|
-
logger.important(chalk.yellow(` ⚠️ ${warning.message}`));
|
|
4971
|
-
if (warning.location) {
|
|
4972
|
-
logger.important(chalk.gray(` Location: ${warning.location}`));
|
|
4973
|
-
}
|
|
4974
|
-
if (warning.suggestion) {
|
|
4975
|
-
logger.important(chalk.gray(` → ${warning.suggestion}`));
|
|
4976
|
-
}
|
|
4977
|
-
logger.important("");
|
|
4978
|
-
});
|
|
4979
|
-
logger.important(chalk.gray("Use --strict to treat warnings as errors."));
|
|
4980
|
-
} else {
|
|
4981
|
-
logger.important(chalk.red("❌ Plugin validation failed\n"));
|
|
4982
|
-
logger.important("Errors:");
|
|
4983
|
-
result.errors.forEach((error) => {
|
|
4984
|
-
logger.important(chalk.red(` ❌ ${error.message}`));
|
|
4985
|
-
if (error.location) {
|
|
4986
|
-
logger.important(chalk.gray(` Location: ${error.location}`));
|
|
4987
|
-
}
|
|
4988
|
-
if (error.suggestion) {
|
|
4989
|
-
logger.important(chalk.gray(` → ${error.suggestion}`));
|
|
4990
|
-
}
|
|
4991
|
-
logger.important("");
|
|
4992
|
-
});
|
|
4993
|
-
logger.important(chalk.red(`${result.errors.length} errors found.`));
|
|
4994
|
-
}
|
|
4995
|
-
}
|
|
4996
|
-
function printContractList(index) {
|
|
4997
|
-
const logger = getLogger();
|
|
4998
|
-
logger.important("\nAvailable Contracts:\n");
|
|
4999
|
-
for (const plugin of index.plugins) {
|
|
5000
|
-
logger.important(chalk.bold(`📦 ${plugin.name}`));
|
|
5001
|
-
for (const contract of plugin.contracts) {
|
|
5002
|
-
const typeIcon = contract.type === "static" ? "📄" : "⚡";
|
|
5003
|
-
logger.important(` ${typeIcon} ${contract.name}`);
|
|
5004
|
-
}
|
|
5005
|
-
logger.important("");
|
|
5006
|
-
}
|
|
5007
|
-
if (index.plugins.length === 0) {
|
|
5008
|
-
logger.important(chalk.gray("No contracts found."));
|
|
5009
|
-
}
|
|
5010
|
-
}
|
|
5011
5125
|
export {
|
|
5012
5126
|
createEditorHandlers,
|
|
5013
5127
|
getConfigWithDefaults,
|