@jay-framework/jay-stack-cli 0.24.1 → 0.24.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3,21 +3,26 @@ import express from "express";
3
3
  import http from "node:http";
4
4
  import { mkDevServer, createViteForCli } from "@jay-framework/dev-server";
5
5
  import getPort from "get-port";
6
- import path from "path";
7
- import fs, { promises } from "fs";
6
+ import path$1 from "path";
7
+ import fs$1, { promises } from "fs";
8
8
  import YAML from "yaml";
9
9
  import { getLogger, setDevLogger, createDevLogger } from "@jay-framework/logger";
10
- import { parseJayFile, JAY_IMPORT_RESOLVER, generateElementDefinitionFile, parseContract, generateElementFile, generateServerElementFile, htmlElementTagNameMap, loadLinkedContract, getLinkedContractDir } from "@jay-framework/compiler-jay-html";
11
- import { scanPlugins, listContracts, materializeContracts, SetupNeedsAnswerError, discoverPluginsWithSetup, sortPluginsByDependencies, discoverPluginsWithInit, executePluginSetup, executePluginServerInits, runInitCallbacks } from "@jay-framework/stack-server-runtime";
12
- import { listContracts as listContracts2, materializeContracts as materializeContracts2 } from "@jay-framework/stack-server-runtime";
10
+ import { parseJayFile, JAY_IMPORT_RESOLVER, generateElementDefinitionFile, parseAction, parseContract, generateElementFile, generateServerElementFile, htmlElementTagNameMap, loadLinkedContract, getLinkedContractDir } from "@jay-framework/compiler-jay-html";
11
+ import fs$2 from "node:fs/promises";
12
+ import * as path from "node:path";
13
+ import path__default from "node:path";
14
+ import { createRequire } from "module";
15
+ import jsBeautify from "js-beautify";
16
+ import { scanPlugins, sortPluginsByDependencies, getServiceRegistry, actionRegistry, resolveServices, ActionRegistry, discoverPluginsWithInit, executePluginServerInits, runInitCallbacks, registerService } from "@jay-framework/stack-server-runtime";
17
+ import "node:crypto";
18
+ import * as fs from "node:fs";
19
+ import fs__default from "node:fs";
20
+ import { createRequire as createRequire$1 } from "node:module";
21
+ import { isJayAction, isJayStreamAction, isJayCliCommand, CONSOLE_CONTEXT } from "@jay-framework/fullstack-component";
13
22
  import { Command } from "commander";
14
23
  import chalk from "chalk";
15
- import path$1 from "node:path";
16
- import fs$1 from "node:fs/promises";
17
- import { loadPluginManifest, JAY_EXTENSION, RuntimeMode, GenerateTarget, JAY_CONTRACT_EXTENSION, findDynamicContract } from "@jay-framework/compiler-shared";
18
- import { createRequire } from "module";
24
+ import { loadPluginManifest as loadPluginManifest$1, normalizeActionEntry as normalizeActionEntry$1, JAY_EXTENSION, RuntimeMode as RuntimeMode$1, GenerateTarget, JAY_CONTRACT_EXTENSION, findDynamicContract } from "@jay-framework/compiler-shared";
19
25
  import { glob } from "glob";
20
- import fsSync from "node:fs";
21
26
  import { fileURLToPath } from "node:url";
22
27
  import { select, confirm, input } from "@inquirer/prompts";
23
28
  const DEFAULT_CONFIG = {
@@ -29,13 +34,13 @@ const DEFAULT_CONFIG = {
29
34
  configBase: "./config"
30
35
  }
31
36
  };
32
- function loadConfig() {
33
- const configPath = path.resolve(".jay");
34
- if (!fs.existsSync(configPath)) {
37
+ function loadConfig(projectRoot) {
38
+ const configPath = projectRoot ? path$1.resolve(projectRoot, ".jay") : path$1.resolve(".jay");
39
+ if (!fs$1.existsSync(configPath)) {
35
40
  return DEFAULT_CONFIG;
36
41
  }
37
42
  try {
38
- const configContent = fs.readFileSync(configPath, "utf-8");
43
+ const configContent = fs$1.readFileSync(configPath, "utf-8");
39
44
  const userConfig = YAML.parse(configContent);
40
45
  return {
41
46
  devServer: {
@@ -62,7 +67,7 @@ function getConfigWithDefaults(config) {
62
67
  };
63
68
  }
64
69
  function updateConfig(updates) {
65
- const configPath = path.resolve(".jay");
70
+ const configPath = path$1.resolve(".jay");
66
71
  try {
67
72
  const existingConfig = loadConfig();
68
73
  const updatedConfig = {
@@ -78,7 +83,7 @@ function updateConfig(updates) {
78
83
  }
79
84
  };
80
85
  const yamlContent = YAML.stringify(updatedConfig, { indent: 2 });
81
- fs.writeFileSync(configPath, yamlContent);
86
+ fs$1.writeFileSync(configPath, yamlContent);
82
87
  } catch (error) {
83
88
  getLogger().warn(`Failed to update .jay config file: ${error}`);
84
89
  }
@@ -86,14 +91,14 @@ function updateConfig(updates) {
86
91
  async function generatePageDefinitionFiles(routes, tsConfigPath, projectRoot) {
87
92
  for (const route of routes) {
88
93
  const jayHtmlPath = route.fsRoute.jayHtmlPath;
89
- if (!fs.existsSync(jayHtmlPath)) {
94
+ if (!fs$1.existsSync(jayHtmlPath)) {
90
95
  continue;
91
96
  }
92
97
  const definitionFilePath = jayHtmlPath + ".d.ts";
93
98
  try {
94
99
  const [sourceStats, defStats] = await Promise.all([
95
- fs.promises.stat(jayHtmlPath),
96
- fs.promises.stat(definitionFilePath).catch(() => null)
100
+ fs$1.promises.stat(jayHtmlPath),
101
+ fs$1.promises.stat(definitionFilePath).catch(() => null)
97
102
  ]);
98
103
  if (defStats && defStats.mtime >= sourceStats.mtime) {
99
104
  continue;
@@ -101,9 +106,9 @@ async function generatePageDefinitionFiles(routes, tsConfigPath, projectRoot) {
101
106
  } catch (error) {
102
107
  }
103
108
  try {
104
- const jayHtml = await fs.promises.readFile(jayHtmlPath, "utf-8");
105
- const filename = path.basename(jayHtmlPath);
106
- const dirname = path.dirname(jayHtmlPath);
109
+ const jayHtml = await fs$1.promises.readFile(jayHtmlPath, "utf-8");
110
+ const filename = path$1.basename(jayHtmlPath);
111
+ const dirname = path$1.dirname(jayHtmlPath);
107
112
  const parsedJayHtml = await parseJayFile(
108
113
  jayHtml,
109
114
  filename,
@@ -119,7 +124,7 @@ async function generatePageDefinitionFiles(routes, tsConfigPath, projectRoot) {
119
124
  );
120
125
  } else {
121
126
  const definitionFilePath2 = jayHtmlPath + ".d.ts";
122
- await fs.promises.writeFile(definitionFilePath2, definitionFile.val, "utf-8");
127
+ await fs$1.promises.writeFile(definitionFilePath2, definitionFile.val, "utf-8");
123
128
  getLogger().info(`šŸ“¦ Generated definition file: ${definitionFilePath2}`);
124
129
  }
125
130
  } catch (error) {
@@ -143,7 +148,7 @@ async function startDevServer(options = {}) {
143
148
  const devServerPort = await getPort({ port: resolvedConfig.devServer.portRange });
144
149
  const log = getLogger();
145
150
  const { server, viteServer, routes, service } = await mkDevServer({
146
- pagesRootFolder: path.resolve(resolvedConfig.devServer.pagesBase),
151
+ pagesRootFolder: path$1.resolve(resolvedConfig.devServer.pagesBase),
147
152
  projectRootFolder: process.cwd(),
148
153
  publicBaseUrlPath: "/",
149
154
  jayRollupConfig: jayOptions,
@@ -151,15 +156,15 @@ async function startDevServer(options = {}) {
151
156
  httpServer
152
157
  });
153
158
  app.use(server);
154
- const publicPath = path.resolve(resolvedConfig.devServer.publicFolder);
155
- if (!fs.existsSync(path.join(publicPath, "sitemap.xml"))) {
159
+ const publicPath = path$1.resolve(resolvedConfig.devServer.publicFolder);
160
+ if (!fs$1.existsSync(path$1.join(publicPath, "sitemap.xml"))) {
156
161
  app.get("/sitemap.xml", (_req, res) => {
157
162
  res.type("application/xml").send(
158
163
  '<?xml version="1.0" encoding="UTF-8"?>\n<!-- Sitemap is generated by the production server from the route manifest. -->\n<!-- Run jay-stack build && jay-stack serve to see the full sitemap. -->\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" />\n'
159
164
  );
160
165
  });
161
166
  }
162
- if (fs.existsSync(publicPath)) {
167
+ if (fs$1.existsSync(publicPath)) {
163
168
  app.use(express.static(publicPath));
164
169
  } else {
165
170
  log.important(`āš ļø Public folder not found: ${resolvedConfig.devServer.publicFolder}`);
@@ -177,7 +182,7 @@ async function startDevServer(options = {}) {
177
182
  log.important(`šŸš€ Jay Stack dev server started successfully!`);
178
183
  log.important(`šŸ“± Dev Server: http://localhost:${devServerPort}`);
179
184
  log.important(`šŸ“ Pages directory: ${resolvedConfig.devServer.pagesBase}`);
180
- if (fs.existsSync(publicPath)) {
185
+ if (fs$1.existsSync(publicPath)) {
181
186
  log.important(`šŸ“ Public folder: ${resolvedConfig.devServer.publicFolder}`);
182
187
  }
183
188
  if (options.testMode) {
@@ -222,12 +227,1326 @@ async function startDevServer(options = {}) {
222
227
  process.on("SIGTERM", shutdown);
223
228
  process.on("SIGINT", shutdown);
224
229
  }
230
+ createRequire(import.meta.url);
231
+ var __defProp2 = Object.defineProperty;
232
+ var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
233
+ var __publicField2 = (obj, key, value) => __defNormalProp2(obj, key + "", value);
234
+ class JayAtomicType {
235
+ constructor(name) {
236
+ __publicField2(this, "kind", 0);
237
+ this.name = name;
238
+ }
239
+ }
240
+ new JayAtomicType("string");
241
+ new JayAtomicType("string");
242
+ new JayAtomicType("number");
243
+ new JayAtomicType("boolean");
244
+ new JayAtomicType("Date");
245
+ new JayAtomicType("file");
246
+ new JayAtomicType("Unknown");
247
+ class JayObjectType {
248
+ constructor(name, props) {
249
+ __publicField2(this, "kind", 8);
250
+ this.name = name;
251
+ this.props = props;
252
+ }
253
+ }
254
+ new JayObjectType("Error", {
255
+ message: new JayAtomicType("string"),
256
+ name: new JayAtomicType("string"),
257
+ stack: new JayAtomicType("string")
258
+ });
259
+ function isOptionalType(aType) {
260
+ return aType.kind === 14;
261
+ }
262
+ function isAtomicType(aType) {
263
+ return aType.kind === 0;
264
+ }
265
+ function isEnumType(aType) {
266
+ return aType.kind === 2;
267
+ }
268
+ function isImportedType(aType) {
269
+ return aType.kind === 4;
270
+ }
271
+ function isObjectType(aType) {
272
+ return aType.kind === 8;
273
+ }
274
+ function isArrayType(aType) {
275
+ return aType.kind === 9;
276
+ }
277
+ function isRecordType(aType) {
278
+ return aType.kind === 11;
279
+ }
280
+ function jayTypeToJsonSchema(type) {
281
+ if (isOptionalType(type)) {
282
+ return jayTypeToJsonSchema(type.innerType);
283
+ }
284
+ if (isAtomicType(type)) {
285
+ const name = type.name.toLowerCase();
286
+ if (name === "string" || name === "number" || name === "boolean") {
287
+ return { type: name };
288
+ }
289
+ if (name === "file") {
290
+ return { type: "string", description: "Binary file upload (JayFile)" };
291
+ }
292
+ return { type: "string" };
293
+ }
294
+ if (isEnumType(type)) {
295
+ return { type: "string", enum: type.values };
296
+ }
297
+ if (isImportedType(type)) {
298
+ return { type: "object", description: `Contract: ${type.name}` };
299
+ }
300
+ if (isArrayType(type)) {
301
+ const itemSchema = jayTypeToJsonSchema(type.itemType);
302
+ if (itemSchema) {
303
+ return { type: "array", items: itemSchema };
304
+ }
305
+ return { type: "array" };
306
+ }
307
+ if (isRecordType(type)) {
308
+ const valueSchema = jayTypeToJsonSchema(type.itemType);
309
+ if (valueSchema) {
310
+ return { type: "object", additionalProperties: valueSchema };
311
+ }
312
+ return { type: "object" };
313
+ }
314
+ if (isObjectType(type)) {
315
+ const properties = {};
316
+ const required = [];
317
+ for (const [key, propType] of Object.entries(type.props)) {
318
+ const isOpt = isOptionalType(propType);
319
+ const schema = jayTypeToJsonSchema(propType);
320
+ if (schema) {
321
+ properties[key] = schema;
322
+ if (!isOpt) {
323
+ required.push(key);
324
+ }
325
+ }
326
+ }
327
+ return {
328
+ type: "object",
329
+ properties,
330
+ ...required.length > 0 && { required }
331
+ };
332
+ }
333
+ return null;
334
+ }
335
+ var RuntimeMode = /* @__PURE__ */ ((RuntimeMode2) => {
336
+ RuntimeMode2["MainTrusted"] = "mainTrusted";
337
+ RuntimeMode2["MainSandbox"] = "mainSandbox";
338
+ RuntimeMode2["WorkerTrusted"] = "workerTrusted";
339
+ RuntimeMode2["WorkerSandbox"] = "workerSandbox";
340
+ return RuntimeMode2;
341
+ })(RuntimeMode || {});
342
+ const TS_EXTENSION = ".ts";
343
+ const JAY_QUERY_PREFIX = "?jay-";
344
+ const JAY_QUERY_HYDRATE = `${JAY_QUERY_PREFIX}hydrate`;
345
+ [
346
+ // Hydrate target
347
+ {
348
+ pattern: JAY_QUERY_HYDRATE,
349
+ buildEnv: "client",
350
+ isHydrate: true
351
+ },
352
+ // Build environments
353
+ {
354
+ pattern: `${JAY_QUERY_PREFIX}${"client"}`,
355
+ buildEnv: "client"
356
+ /* Client */
357
+ },
358
+ {
359
+ pattern: `${JAY_QUERY_PREFIX}${"server"}`,
360
+ buildEnv: "server"
361
+ /* Server */
362
+ },
363
+ // Runtime modes (with .ts suffix)
364
+ {
365
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.MainSandbox}${TS_EXTENSION}`,
366
+ runtimeMode: RuntimeMode.MainSandbox
367
+ },
368
+ {
369
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.WorkerTrusted}${TS_EXTENSION}`,
370
+ runtimeMode: RuntimeMode.WorkerTrusted
371
+ },
372
+ {
373
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.WorkerSandbox}${TS_EXTENSION}`,
374
+ runtimeMode: RuntimeMode.WorkerSandbox
375
+ },
376
+ // Runtime modes (without .ts suffix)
377
+ {
378
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.MainSandbox}`,
379
+ runtimeMode: RuntimeMode.MainSandbox
380
+ },
381
+ {
382
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.WorkerTrusted}`,
383
+ runtimeMode: RuntimeMode.WorkerTrusted
384
+ },
385
+ {
386
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.WorkerSandbox}`,
387
+ runtimeMode: RuntimeMode.WorkerSandbox
388
+ }
389
+ ];
390
+ const { html: htmlBeautify } = jsBeautify;
391
+ createRequire(import.meta.url);
392
+ function normalizeActionEntry(entry) {
393
+ if (typeof entry === "string") {
394
+ return { name: entry };
395
+ }
396
+ return { name: entry.name, action: entry.action, devOnly: entry.devOnly };
397
+ }
398
+ function loadPluginManifest(pluginDir) {
399
+ const pluginYamlPath = path$1.join(pluginDir, "plugin.yaml");
400
+ if (!fs$1.existsSync(pluginYamlPath)) {
401
+ return null;
402
+ }
403
+ try {
404
+ const yamlContent = fs$1.readFileSync(pluginYamlPath, "utf-8");
405
+ return YAML.parse(yamlContent);
406
+ } catch (error) {
407
+ return null;
408
+ }
409
+ }
410
+ const s$1 = createRequire(import.meta.url), e$1 = s$1("typescript");
411
+ new Proxy(e$1, {
412
+ get(t, r) {
413
+ return t[r];
414
+ }
415
+ });
416
+ function parseActionMetadata(yamlContent, fileName) {
417
+ try {
418
+ const parsed = parseAction(yamlContent, fileName);
419
+ if (parsed.validations.length > 0) {
420
+ getLogger().warn(
421
+ `[ActionMetadata] ${fileName}: validation errors: ${parsed.validations.join(", ")}`
422
+ );
423
+ return null;
424
+ }
425
+ if (!parsed.val) {
426
+ getLogger().warn(`[ActionMetadata] ${fileName}: parsing returned no result`);
427
+ return null;
428
+ }
429
+ const action = parsed.val;
430
+ const inputJsonSchema = jayTypeToJsonSchema(action.inputType);
431
+ let inputSchema;
432
+ if (inputJsonSchema && inputJsonSchema.type === "object") {
433
+ inputSchema = {
434
+ type: "object",
435
+ properties: inputJsonSchema.properties || {},
436
+ ...inputJsonSchema.required && inputJsonSchema.required.length > 0 && { required: inputJsonSchema.required }
437
+ };
438
+ } else {
439
+ inputSchema = { type: "object", properties: {} };
440
+ }
441
+ const metadata = {
442
+ name: action.name,
443
+ description: action.description,
444
+ inputSchema
445
+ };
446
+ if (action.outputType) {
447
+ const outputJsonSchema = jayTypeToJsonSchema(action.outputType);
448
+ if (outputJsonSchema) {
449
+ metadata.outputSchema = outputJsonSchema;
450
+ }
451
+ }
452
+ return metadata;
453
+ } catch (error) {
454
+ getLogger().error(
455
+ `[ActionMetadata] Failed to parse ${fileName}: ${error instanceof Error ? error.message : error}`
456
+ );
457
+ return null;
458
+ }
459
+ }
460
+ function loadActionMetadata(filePath) {
461
+ if (!fs.existsSync(filePath)) {
462
+ getLogger().warn(`[ActionMetadata] File not found: ${filePath}`);
463
+ return null;
464
+ }
465
+ const yamlContent = fs.readFileSync(filePath, "utf-8");
466
+ const fileName = path.basename(filePath);
467
+ return parseActionMetadata(yamlContent, fileName);
468
+ }
469
+ function resolveActionMetadataPath(actionPath, pluginDir) {
470
+ return path.resolve(pluginDir, actionPath);
471
+ }
472
+ const require$2 = createRequire$1(import.meta.url);
473
+ async function discoverAndRegisterActions(options) {
474
+ const {
475
+ projectRoot,
476
+ actionsDir = "src/actions",
477
+ registry = actionRegistry,
478
+ verbose = false,
479
+ viteServer
480
+ } = options;
481
+ const result = {
482
+ actionCount: 0,
483
+ actionNames: [],
484
+ scannedFiles: []
485
+ };
486
+ const actionsPath = path.resolve(projectRoot, actionsDir);
487
+ if (!fs.existsSync(actionsPath)) {
488
+ if (verbose) {
489
+ getLogger().info(`[Actions] No actions directory found at ${actionsPath}`);
490
+ }
491
+ return result;
492
+ }
493
+ const actionFiles = await findActionFiles(actionsPath);
494
+ if (verbose) {
495
+ getLogger().info(`[Actions] Found ${actionFiles.length} action file(s)`);
496
+ }
497
+ for (const filePath of actionFiles) {
498
+ result.scannedFiles.push(filePath);
499
+ try {
500
+ let module;
501
+ if (viteServer) {
502
+ module = await viteServer.ssrLoadModule(filePath);
503
+ } else {
504
+ module = await import(filePath);
505
+ }
506
+ for (const [exportName, exportValue] of Object.entries(module)) {
507
+ if (isJayAction(exportValue)) {
508
+ registry.register(exportValue);
509
+ result.actionNames.push(exportValue.actionName);
510
+ result.actionCount++;
511
+ if (verbose) {
512
+ getLogger().info(
513
+ `[Actions] Registered: ${exportValue.actionName}`
514
+ );
515
+ }
516
+ } else if (isJayStreamAction(exportValue)) {
517
+ registry.registerStream(exportValue);
518
+ result.actionNames.push(exportValue.actionName);
519
+ result.actionCount++;
520
+ if (verbose) {
521
+ getLogger().info(
522
+ `[Actions] Registered stream: ${exportValue.actionName}`
523
+ );
524
+ }
525
+ }
526
+ }
527
+ } catch (error) {
528
+ getLogger().error(`[Actions] Failed to import ${filePath}: ${error}`);
529
+ }
530
+ }
531
+ return result;
532
+ }
533
+ async function findActionFiles(dir) {
534
+ const files = [];
535
+ const entries = await fs.promises.readdir(dir, { withFileTypes: true });
536
+ for (const entry of entries) {
537
+ const fullPath = path.join(dir, entry.name);
538
+ if (entry.isDirectory()) {
539
+ const subFiles = await findActionFiles(fullPath);
540
+ files.push(...subFiles);
541
+ } else if (entry.isFile() && entry.name.endsWith(".actions.ts")) {
542
+ files.push(fullPath);
543
+ }
544
+ }
545
+ return files;
546
+ }
547
+ async function discoverAllPluginActions(options) {
548
+ const { projectRoot, registry = actionRegistry, verbose = false, viteServer } = options;
549
+ const allActions = [];
550
+ const localPluginsPath = path.join(projectRoot, "src/plugins");
551
+ if (fs.existsSync(localPluginsPath)) {
552
+ const pluginDirs = await fs.promises.readdir(localPluginsPath, { withFileTypes: true });
553
+ for (const entry of pluginDirs) {
554
+ if (entry.isDirectory()) {
555
+ const pluginPath = path.join(localPluginsPath, entry.name);
556
+ const actions = await discoverPluginActions(
557
+ pluginPath,
558
+ projectRoot,
559
+ registry,
560
+ verbose,
561
+ viteServer
562
+ );
563
+ allActions.push(...actions);
564
+ }
565
+ }
566
+ }
567
+ const npmActions = await discoverNpmPluginActions(projectRoot, registry, verbose, viteServer);
568
+ allActions.push(...npmActions);
569
+ return allActions;
570
+ }
571
+ async function discoverNpmPluginActions(projectRoot, registry, verbose, viteServer) {
572
+ const allActions = [];
573
+ const packageJsonPath = path.join(projectRoot, "package.json");
574
+ if (!fs.existsSync(packageJsonPath)) {
575
+ return allActions;
576
+ }
577
+ try {
578
+ const packageJson = JSON.parse(await fs.promises.readFile(packageJsonPath, "utf-8"));
579
+ const dependencies = {
580
+ ...packageJson.dependencies,
581
+ ...packageJson.devDependencies
582
+ };
583
+ for (const packageName of Object.keys(dependencies)) {
584
+ try {
585
+ const pluginYamlPath = tryResolvePluginYaml(packageName, projectRoot);
586
+ if (!pluginYamlPath) {
587
+ continue;
588
+ }
589
+ const pluginDir = path.dirname(pluginYamlPath);
590
+ const pluginConfig = loadPluginManifest(pluginDir);
591
+ if (!pluginConfig || !pluginConfig.actions || !Array.isArray(pluginConfig.actions)) {
592
+ continue;
593
+ }
594
+ if (verbose) {
595
+ getLogger().info(
596
+ `[Actions] NPM plugin "${packageName}" declares actions: ${JSON.stringify(pluginConfig.actions)}`
597
+ );
598
+ }
599
+ const actions = await registerNpmPluginActions(
600
+ packageName,
601
+ pluginConfig,
602
+ pluginDir,
603
+ registry,
604
+ verbose,
605
+ viteServer
606
+ );
607
+ allActions.push(...actions);
608
+ } catch {
609
+ continue;
610
+ }
611
+ }
612
+ } catch (error) {
613
+ getLogger().error(`[Actions] Failed to read project package.json: ${error}`);
614
+ }
615
+ return allActions;
616
+ }
617
+ function tryResolvePluginYaml(packageName, projectRoot) {
618
+ try {
619
+ return require$2.resolve(`${packageName}/plugin.yaml`, {
620
+ paths: [projectRoot]
621
+ });
622
+ } catch {
623
+ return null;
624
+ }
625
+ }
626
+ function resolveNpmActionMetadataPath(actionPath, packageName, pluginDir) {
627
+ if (!actionPath.startsWith(".")) {
628
+ try {
629
+ return require$2.resolve(`${packageName}/${actionPath}`, {
630
+ paths: [pluginDir]
631
+ });
632
+ } catch {
633
+ }
634
+ }
635
+ const resolved = resolveActionMetadataPath(actionPath, pluginDir);
636
+ if (fs.existsSync(resolved)) {
637
+ return resolved;
638
+ }
639
+ getLogger().warn(
640
+ `[Actions] Could not resolve .jay-action file "${actionPath}" for package "${packageName}"`
641
+ );
642
+ return null;
643
+ }
644
+ async function registerNpmPluginActions(packageName, pluginConfig, pluginDir, registry, verbose, viteServer) {
645
+ const registeredActions = [];
646
+ try {
647
+ let pluginModule;
648
+ if (viteServer) {
649
+ pluginModule = await viteServer.ssrLoadModule(packageName);
650
+ } else {
651
+ pluginModule = await import(packageName);
652
+ }
653
+ let toolsModule;
654
+ const loadToolsModule = async () => {
655
+ if (!toolsModule) {
656
+ const toolsEntry = `${packageName}/tools`;
657
+ toolsModule = viteServer ? await viteServer.ssrLoadModule(toolsEntry) : await import(toolsEntry);
658
+ }
659
+ return toolsModule;
660
+ };
661
+ for (const entry of pluginConfig.actions) {
662
+ const { name: actionName, action: actionPath, devOnly } = normalizeActionEntry(entry);
663
+ const sourceModule = devOnly ? await loadToolsModule() : pluginModule;
664
+ const actionExport = sourceModule[actionName];
665
+ if (actionExport && isJayAction(actionExport)) {
666
+ registry.register(actionExport);
667
+ const registeredName = actionExport.actionName;
668
+ registeredActions.push(registeredName);
669
+ if (actionPath) {
670
+ const metadataFilePath = resolveNpmActionMetadataPath(
671
+ actionPath,
672
+ packageName,
673
+ pluginDir
674
+ );
675
+ if (metadataFilePath) {
676
+ const metadata = loadActionMetadata(metadataFilePath);
677
+ if (metadata) {
678
+ registry.setMetadata(registeredName, metadata);
679
+ if (verbose) {
680
+ getLogger().info(
681
+ `[Actions] Loaded metadata for "${registeredName}" from ${actionPath}`
682
+ );
683
+ }
684
+ }
685
+ }
686
+ }
687
+ if (verbose) {
688
+ getLogger().info(`[Actions] Registered NPM plugin action: ${registeredName}`);
689
+ }
690
+ } else if (actionExport && isJayStreamAction(actionExport)) {
691
+ registry.registerStream(actionExport);
692
+ const registeredName = actionExport.actionName;
693
+ registeredActions.push(registeredName);
694
+ if (verbose) {
695
+ getLogger().info(`[Actions] Registered NPM plugin stream: ${registeredName}`);
696
+ }
697
+ } else {
698
+ getLogger().warn(
699
+ `[Actions] NPM plugin "${packageName}" declares action "${actionName}" but it's not exported or not a JayAction`
700
+ );
701
+ }
702
+ }
703
+ } catch (importError) {
704
+ getLogger().error(`[Actions] Failed to import NPM plugin "${packageName}": ${importError}`);
705
+ }
706
+ return registeredActions;
707
+ }
708
+ async function discoverPluginActions(pluginPath, projectRoot, registry = actionRegistry, verbose = false, viteServer) {
709
+ const pluginConfig = loadPluginManifest(pluginPath);
710
+ if (!pluginConfig) {
711
+ return [];
712
+ }
713
+ if (!pluginConfig.actions || !Array.isArray(pluginConfig.actions)) {
714
+ return [];
715
+ }
716
+ const registeredActions = [];
717
+ const pluginName = pluginConfig.name || path.basename(pluginPath);
718
+ if (verbose) {
719
+ getLogger().info(
720
+ `[Actions] Plugin "${pluginName}" declares actions: ${JSON.stringify(pluginConfig.actions)}`
721
+ );
722
+ }
723
+ let modulePath = pluginConfig.module ? path.join(pluginPath, pluginConfig.module) : path.join(pluginPath, "index.ts");
724
+ if (!fs.existsSync(modulePath)) {
725
+ const tsPath = modulePath + ".ts";
726
+ const jsPath = modulePath + ".js";
727
+ if (fs.existsSync(tsPath)) {
728
+ modulePath = tsPath;
729
+ } else if (fs.existsSync(jsPath)) {
730
+ modulePath = jsPath;
731
+ } else {
732
+ getLogger().warn(`[Actions] Plugin "${pluginName}" module not found at ${modulePath}`);
733
+ return [];
734
+ }
735
+ }
736
+ const resolveLocalToolsModulePath = () => {
737
+ for (const candidate of ["tools.ts", "tools.js"]) {
738
+ const candidatePath = path.join(pluginPath, candidate);
739
+ if (fs.existsSync(candidatePath)) return candidatePath;
740
+ }
741
+ return null;
742
+ };
743
+ try {
744
+ let pluginModule;
745
+ if (viteServer) {
746
+ pluginModule = await viteServer.ssrLoadModule(modulePath);
747
+ } else {
748
+ pluginModule = await import(modulePath);
749
+ }
750
+ let toolsModule;
751
+ const loadToolsModule = async () => {
752
+ if (toolsModule) return toolsModule;
753
+ const toolsPath = resolveLocalToolsModulePath();
754
+ if (!toolsPath) {
755
+ getLogger().warn(
756
+ `[Actions] Plugin "${pluginName}" declares a devOnly action but has no tools.ts/tools.js module`
757
+ );
758
+ return void 0;
759
+ }
760
+ toolsModule = viteServer ? await viteServer.ssrLoadModule(toolsPath) : await import(toolsPath);
761
+ return toolsModule;
762
+ };
763
+ for (const entry of pluginConfig.actions) {
764
+ const { name: actionName, action: actionPath, devOnly } = normalizeActionEntry(entry);
765
+ const sourceModule = devOnly ? await loadToolsModule() : pluginModule;
766
+ const actionExport = sourceModule?.[actionName];
767
+ if (actionExport && isJayAction(actionExport)) {
768
+ registry.register(actionExport);
769
+ const registeredName = actionExport.actionName;
770
+ registeredActions.push(registeredName);
771
+ if (actionPath) {
772
+ const metadataFilePath = resolveActionMetadataPath(actionPath, pluginPath);
773
+ const metadata = loadActionMetadata(metadataFilePath);
774
+ if (metadata) {
775
+ registry.setMetadata(registeredName, metadata);
776
+ if (verbose) {
777
+ getLogger().info(
778
+ `[Actions] Loaded metadata for "${registeredName}" from ${actionPath}`
779
+ );
780
+ }
781
+ }
782
+ }
783
+ if (verbose) {
784
+ getLogger().info(`[Actions] Registered plugin action: ${registeredName}`);
785
+ }
786
+ } else if (actionExport && isJayStreamAction(actionExport)) {
787
+ registry.registerStream(actionExport);
788
+ const registeredName = actionExport.actionName;
789
+ registeredActions.push(registeredName);
790
+ if (verbose) {
791
+ getLogger().info(`[Actions] Registered plugin stream: ${registeredName}`);
792
+ }
793
+ } else {
794
+ getLogger().warn(
795
+ `[Actions] Plugin "${pluginName}" declares action "${actionName}" but it's not exported or not a JayAction`
796
+ );
797
+ }
798
+ }
799
+ } catch (importError) {
800
+ getLogger().error(
801
+ `[Actions] Failed to import plugin module at ${modulePath}: ${importError}`
802
+ );
803
+ }
804
+ return registeredActions;
805
+ }
806
+ const require$1 = createRequire(import.meta.url);
807
+ async function executeDynamicGenerator(plugin, config, projectRoot, services, verbose, viteServer) {
808
+ const { pluginPath, name: pluginName, isLocal, packageName } = plugin;
809
+ if (!config.generator) {
810
+ throw new Error(
811
+ `Plugin "${pluginName}" has dynamic_contracts entry but no generator specified`
812
+ );
813
+ }
814
+ const isFilePath = config.generator.startsWith("./") || config.generator.startsWith("/") || config.generator.includes(".ts") || config.generator.includes(".js");
815
+ let generator;
816
+ if (isFilePath) {
817
+ let generatorPath;
818
+ if (!isLocal) {
819
+ try {
820
+ generatorPath = require$1.resolve(`${packageName}/${config.generator}`, {
821
+ paths: [projectRoot]
822
+ });
823
+ } catch {
824
+ generatorPath = path.join(pluginPath, config.generator);
825
+ }
826
+ } else {
827
+ generatorPath = path.join(pluginPath, config.generator);
828
+ }
829
+ if (!fs.existsSync(generatorPath)) {
830
+ const withTs = generatorPath + ".ts";
831
+ const withJs = generatorPath + ".js";
832
+ if (fs.existsSync(withTs)) {
833
+ generatorPath = withTs;
834
+ } else if (fs.existsSync(withJs)) {
835
+ generatorPath = withJs;
836
+ }
837
+ }
838
+ if (!fs.existsSync(generatorPath)) {
839
+ throw new Error(
840
+ `Generator file not found for plugin "${pluginName}": ${config.generator}`
841
+ );
842
+ }
843
+ if (verbose) {
844
+ getLogger().info(` Loading generator from file: ${generatorPath}`);
845
+ }
846
+ let generatorModule;
847
+ if (viteServer) {
848
+ generatorModule = await viteServer.ssrLoadModule(generatorPath);
849
+ } else {
850
+ generatorModule = await import(generatorPath);
851
+ }
852
+ generator = generatorModule.generator || generatorModule.default;
853
+ } else {
854
+ if (verbose) {
855
+ getLogger().info(
856
+ ` Loading generator export: ${config.generator} from ${packageName}`
857
+ );
858
+ }
859
+ let pluginModule;
860
+ if (viteServer) {
861
+ pluginModule = await viteServer.ssrLoadModule(packageName);
862
+ } else {
863
+ pluginModule = await import(packageName);
864
+ }
865
+ generator = pluginModule[config.generator];
866
+ if (!generator) {
867
+ throw new Error(
868
+ `Generator "${config.generator}" not exported from plugin "${pluginName}". Ensure it's exported from the package's index.ts`
869
+ );
870
+ }
871
+ }
872
+ if (!generator || typeof generator.generate !== "function") {
873
+ throw new Error(
874
+ `Generator "${config.generator}" for plugin "${pluginName}" must have a 'generate' function. Use makeContractGenerator() to create valid generators.`
875
+ );
876
+ }
877
+ const resolvedServices = [];
878
+ for (const marker of generator.services) {
879
+ const service = services.get(marker);
880
+ if (!service) {
881
+ const markerName = marker.description ?? "unknown";
882
+ throw new Error(
883
+ `Service "${markerName}" required by ${pluginName} generator not found. Ensure it's registered in init.ts`
884
+ );
885
+ }
886
+ resolvedServices.push(service);
887
+ }
888
+ if (verbose) {
889
+ getLogger().info(` Executing generator...`);
890
+ }
891
+ return await generator.generate(...resolvedServices);
892
+ }
893
+ function resolveStaticContractPath(plugin, contractSpec, projectRoot) {
894
+ const { pluginPath, isLocal, packageName } = plugin;
895
+ if (!isLocal) {
896
+ try {
897
+ return require$1.resolve(`${packageName}/${contractSpec}`, {
898
+ paths: [projectRoot]
899
+ });
900
+ } catch {
901
+ const possiblePaths = [
902
+ path.join(pluginPath, "dist", contractSpec),
903
+ path.join(pluginPath, "lib", contractSpec),
904
+ path.join(pluginPath, contractSpec)
905
+ ];
906
+ const found = possiblePaths.find((p) => fs.existsSync(p));
907
+ return found || possiblePaths[0];
908
+ }
909
+ } else {
910
+ return path.join(pluginPath, contractSpec);
911
+ }
912
+ }
913
+ function resolveActionFilePath(actionPath, packageName, pluginPath, isLocal, projectRoot) {
914
+ if (!isLocal && !actionPath.startsWith(".")) {
915
+ try {
916
+ return require$1.resolve(`${packageName}/${actionPath}`, {
917
+ paths: [projectRoot]
918
+ });
919
+ } catch {
920
+ const possiblePaths = [
921
+ path.join(pluginPath, "dist", actionPath),
922
+ path.join(pluginPath, "lib", actionPath),
923
+ path.join(pluginPath, actionPath)
924
+ ];
925
+ const found = possiblePaths.find((p) => fs.existsSync(p));
926
+ return found || null;
927
+ }
928
+ }
929
+ const resolved = resolveActionMetadataPath(actionPath, pluginPath);
930
+ return fs.existsSync(resolved) ? resolved : null;
931
+ }
932
+ function toKebabCase(str) {
933
+ return str.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "");
934
+ }
935
+ async function materializeContracts(options, services = /* @__PURE__ */ new Map()) {
936
+ const {
937
+ projectRoot,
938
+ outputDir = path.join(projectRoot, "agent-kit", "materialized-contracts"),
939
+ dynamicOnly = false,
940
+ pluginFilter,
941
+ verbose = false,
942
+ viteServer
943
+ } = options;
944
+ const pluginsIndexMap = /* @__PURE__ */ new Map();
945
+ let staticCount = 0;
946
+ let dynamicCount = 0;
947
+ if (verbose) {
948
+ getLogger().info("Scanning for plugins...");
949
+ }
950
+ const plugins = await scanPlugins({
951
+ projectRoot,
952
+ verbose,
953
+ includeDevDeps: true
954
+ // Include dev deps for contract discovery
955
+ });
956
+ if (verbose) {
957
+ getLogger().info(`Found ${plugins.size} plugin(s)`);
958
+ }
959
+ for (const [pluginKey, plugin] of plugins) {
960
+ if (pluginFilter && plugin.name !== pluginFilter && pluginKey !== pluginFilter) continue;
961
+ if (verbose) {
962
+ getLogger().info(`
963
+ šŸ“¦ Processing plugin: ${plugin.name}`);
964
+ }
965
+ const { manifest } = plugin;
966
+ const pluginRelPath = path.relative(projectRoot, plugin.pluginPath);
967
+ if (!pluginsIndexMap.has(plugin.name)) {
968
+ const entry = {
969
+ path: "./" + pluginRelPath.replace(/\\/g, "/"),
970
+ contracts: [],
971
+ actions: []
972
+ };
973
+ if (manifest.services?.length) {
974
+ entry.services = manifest.services.map((s2) => {
975
+ const docPath = s2.doc ? "./" + path.relative(projectRoot, path.resolve(plugin.pluginPath, s2.doc)) : void 0;
976
+ return {
977
+ name: s2.name,
978
+ marker: s2.marker,
979
+ ...s2.description && { description: s2.description },
980
+ ...docPath && { doc: docPath }
981
+ };
982
+ });
983
+ }
984
+ if (manifest.contexts?.length) {
985
+ entry.contexts = manifest.contexts.map((c) => {
986
+ const docPath = c.doc ? "./" + path.relative(projectRoot, path.resolve(plugin.pluginPath, c.doc)) : void 0;
987
+ return {
988
+ name: c.name,
989
+ marker: c.marker,
990
+ ...c.description && { description: c.description },
991
+ ...docPath && { doc: docPath }
992
+ };
993
+ });
994
+ }
995
+ if (manifest.routes?.length) {
996
+ entry.routes = manifest.routes.map((r) => ({
997
+ path: r.path,
998
+ ...r.description && { description: r.description }
999
+ }));
1000
+ }
1001
+ if (manifest.commands?.length) {
1002
+ entry.commands = manifest.commands.map((c) => {
1003
+ let description;
1004
+ if (c.command) {
1005
+ try {
1006
+ const cmdPath = path.resolve(plugin.pluginPath, c.command);
1007
+ const cmdContent = fs.readFileSync(cmdPath, "utf-8");
1008
+ const parsed = YAML.parse(cmdContent);
1009
+ description = parsed?.description;
1010
+ } catch {
1011
+ }
1012
+ }
1013
+ return {
1014
+ name: c.name,
1015
+ ...description && { description }
1016
+ };
1017
+ });
1018
+ }
1019
+ pluginsIndexMap.set(plugin.name, entry);
1020
+ }
1021
+ if (!dynamicOnly && manifest.contracts) {
1022
+ for (const contract of manifest.contracts) {
1023
+ const contractPath = resolveStaticContractPath(
1024
+ plugin,
1025
+ contract.contract,
1026
+ projectRoot
1027
+ );
1028
+ const relativePath = path.relative(projectRoot, contractPath);
1029
+ let description = contract.description;
1030
+ if (!description) {
1031
+ try {
1032
+ const contractContent = fs.readFileSync(contractPath, "utf-8");
1033
+ const parsed = YAML.parse(contractContent);
1034
+ if (parsed?.description && typeof parsed.description === "string") {
1035
+ description = parsed.description;
1036
+ }
1037
+ } catch {
1038
+ }
1039
+ }
1040
+ pluginsIndexMap.get(plugin.name).contracts.push({
1041
+ name: contract.name,
1042
+ ...description && { description },
1043
+ type: "static",
1044
+ path: "./" + relativePath
1045
+ });
1046
+ staticCount++;
1047
+ if (verbose) {
1048
+ getLogger().info(` šŸ“„ Static: ${contract.name}`);
1049
+ }
1050
+ }
1051
+ }
1052
+ if (manifest.dynamic_contracts) {
1053
+ const dynamicConfigs = Array.isArray(manifest.dynamic_contracts) ? manifest.dynamic_contracts : [manifest.dynamic_contracts];
1054
+ const pluginOutputDir = path.join(outputDir, plugin.name.replace(/[@/]/g, "_"));
1055
+ fs.mkdirSync(pluginOutputDir, { recursive: true });
1056
+ for (const config of dynamicConfigs) {
1057
+ if (verbose) {
1058
+ getLogger().info(` ⚔ Dynamic contracts (prefix: ${config.prefix})`);
1059
+ }
1060
+ try {
1061
+ const generatedContracts = await executeDynamicGenerator(
1062
+ plugin,
1063
+ config,
1064
+ projectRoot,
1065
+ services,
1066
+ verbose,
1067
+ viteServer
1068
+ );
1069
+ const prefix = config.prefix;
1070
+ if (generatedContracts.length > 1) {
1071
+ const missing = generatedContracts.filter((c) => !c.name);
1072
+ if (missing.length > 0) {
1073
+ getLogger().error(
1074
+ ` āŒ Dynamic contract generator for "${prefix}" returned ${generatedContracts.length} contracts but ${missing.length} are missing a name. Names are required when a generator returns multiple contracts.`
1075
+ );
1076
+ continue;
1077
+ }
1078
+ }
1079
+ for (const generated of generatedContracts) {
1080
+ const kebabName = generated.name ? toKebabCase(generated.name) : null;
1081
+ const fullName = kebabName ? `${prefix}/${kebabName}` : prefix;
1082
+ const fileName = kebabName ? `${prefix}-${kebabName}.jay-contract` : `${prefix}.jay-contract`;
1083
+ const filePath = path.join(pluginOutputDir, fileName);
1084
+ fs.writeFileSync(filePath, generated.yaml, "utf-8");
1085
+ const relativePath = path.relative(projectRoot, filePath);
1086
+ let dynDescription;
1087
+ try {
1088
+ const parsedYaml = YAML.parse(generated.yaml);
1089
+ if (parsedYaml?.description && typeof parsedYaml.description === "string") {
1090
+ dynDescription = parsedYaml.description;
1091
+ }
1092
+ } catch {
1093
+ }
1094
+ const contractEntry = {
1095
+ name: fullName,
1096
+ ...dynDescription && { description: dynDescription },
1097
+ type: "dynamic",
1098
+ path: "./" + relativePath,
1099
+ ...generated.metadata && { metadata: generated.metadata }
1100
+ };
1101
+ pluginsIndexMap.get(plugin.name).contracts.push(contractEntry);
1102
+ dynamicCount++;
1103
+ if (verbose) {
1104
+ getLogger().info(` ⚔ Materialized: ${fullName}`);
1105
+ }
1106
+ }
1107
+ } catch (error) {
1108
+ getLogger().error(
1109
+ ` āŒ Failed to materialize dynamic contracts for ${plugin.name} (${config.prefix}): ${error}`
1110
+ );
1111
+ }
1112
+ }
1113
+ }
1114
+ if (manifest.actions && Array.isArray(manifest.actions)) {
1115
+ for (const entry of manifest.actions) {
1116
+ const { name: actionName, action: actionPath } = normalizeActionEntry(entry);
1117
+ if (!actionPath) continue;
1118
+ const metadataFilePath = resolveActionFilePath(
1119
+ actionPath,
1120
+ plugin.packageName,
1121
+ plugin.pluginPath,
1122
+ plugin.isLocal,
1123
+ projectRoot
1124
+ );
1125
+ if (!metadataFilePath) continue;
1126
+ const metadata = loadActionMetadata(metadataFilePath);
1127
+ if (!metadata) continue;
1128
+ const actionRelPath = path.relative(projectRoot, metadataFilePath);
1129
+ const pluginEntry = pluginsIndexMap.get(plugin.name);
1130
+ if (!pluginEntry.actions) pluginEntry.actions = [];
1131
+ pluginEntry.actions.push({
1132
+ name: metadata.name,
1133
+ description: metadata.description,
1134
+ path: "./" + actionRelPath.replace(/\\/g, "/")
1135
+ });
1136
+ if (verbose) {
1137
+ getLogger().info(` šŸ”§ Action: ${metadata.name} (${actionPath})`);
1138
+ }
1139
+ }
1140
+ }
1141
+ }
1142
+ const pluginsIndex = {
1143
+ plugins: Array.from(pluginsIndexMap.entries()).map(([name, data]) => ({
1144
+ name,
1145
+ path: data.path,
1146
+ contracts: data.contracts,
1147
+ ...data.actions && data.actions.length > 0 && { actions: data.actions },
1148
+ ...data.services?.length && { services: data.services },
1149
+ ...data.contexts?.length && { contexts: data.contexts },
1150
+ ...data.routes?.length && { routes: data.routes },
1151
+ ...data.commands?.length && { commands: data.commands }
1152
+ }))
1153
+ };
1154
+ fs.mkdirSync(outputDir, { recursive: true });
1155
+ const agentKitDir = path.dirname(outputDir);
1156
+ const pluginsIndexPath = path.join(agentKitDir, "plugins-index.yaml");
1157
+ fs.writeFileSync(pluginsIndexPath, YAML.stringify(pluginsIndex), "utf-8");
1158
+ if (verbose) {
1159
+ getLogger().info(`
1160
+ āœ… Plugins index written to: ${pluginsIndexPath}`);
1161
+ }
1162
+ return {
1163
+ pluginsIndex,
1164
+ staticCount,
1165
+ dynamicCount,
1166
+ outputDir
1167
+ };
1168
+ }
1169
+ async function listContracts(options) {
1170
+ const { projectRoot, dynamicOnly = false, pluginFilter } = options;
1171
+ const pluginsMap = /* @__PURE__ */ new Map();
1172
+ const plugins = await scanPlugins({
1173
+ projectRoot,
1174
+ includeDevDeps: true
1175
+ });
1176
+ for (const [pluginKey, plugin] of plugins) {
1177
+ if (pluginFilter && plugin.name !== pluginFilter && pluginKey !== pluginFilter) continue;
1178
+ const { manifest } = plugin;
1179
+ const pluginRelPath = path.relative(projectRoot, plugin.pluginPath);
1180
+ if (!pluginsMap.has(plugin.name)) {
1181
+ const entry = {
1182
+ path: "./" + pluginRelPath.replace(/\\/g, "/"),
1183
+ contracts: []
1184
+ };
1185
+ if (manifest.services?.length) {
1186
+ entry.services = manifest.services.map((s2) => {
1187
+ const docPath = s2.doc ? "./" + path.relative(projectRoot, path.resolve(plugin.pluginPath, s2.doc)) : void 0;
1188
+ return {
1189
+ name: s2.name,
1190
+ marker: s2.marker,
1191
+ ...s2.description && { description: s2.description },
1192
+ ...docPath && { doc: docPath }
1193
+ };
1194
+ });
1195
+ }
1196
+ if (manifest.contexts?.length) {
1197
+ entry.contexts = manifest.contexts.map((c) => {
1198
+ const docPath = c.doc ? "./" + path.relative(projectRoot, path.resolve(plugin.pluginPath, c.doc)) : void 0;
1199
+ return {
1200
+ name: c.name,
1201
+ marker: c.marker,
1202
+ ...c.description && { description: c.description },
1203
+ ...docPath && { doc: docPath }
1204
+ };
1205
+ });
1206
+ }
1207
+ if (manifest.routes?.length) {
1208
+ entry.routes = manifest.routes.map((r) => ({
1209
+ path: r.path,
1210
+ ...r.description && { description: r.description }
1211
+ }));
1212
+ }
1213
+ if (manifest.commands?.length) {
1214
+ entry.commands = manifest.commands.map((c) => {
1215
+ let description;
1216
+ if (c.command) {
1217
+ try {
1218
+ const cmdPath = path.resolve(plugin.pluginPath, c.command);
1219
+ const cmdContent = fs.readFileSync(cmdPath, "utf-8");
1220
+ const parsed = YAML.parse(cmdContent);
1221
+ description = parsed?.description;
1222
+ } catch {
1223
+ }
1224
+ }
1225
+ return {
1226
+ name: c.name,
1227
+ ...description && { description }
1228
+ };
1229
+ });
1230
+ }
1231
+ pluginsMap.set(plugin.name, entry);
1232
+ }
1233
+ if (!dynamicOnly && manifest.contracts) {
1234
+ for (const contract of manifest.contracts) {
1235
+ const contractPath = resolveStaticContractPath(
1236
+ plugin,
1237
+ contract.contract,
1238
+ projectRoot
1239
+ );
1240
+ const relativePath = path.relative(projectRoot, contractPath);
1241
+ let listDescription = contract.description;
1242
+ if (!listDescription) {
1243
+ try {
1244
+ const contractContent = fs.readFileSync(contractPath, "utf-8");
1245
+ const parsed = YAML.parse(contractContent);
1246
+ if (parsed?.description && typeof parsed.description === "string") {
1247
+ listDescription = parsed.description;
1248
+ }
1249
+ } catch {
1250
+ }
1251
+ }
1252
+ pluginsMap.get(plugin.name).contracts.push({
1253
+ name: contract.name,
1254
+ ...listDescription && { description: listDescription },
1255
+ type: "static",
1256
+ path: "./" + relativePath
1257
+ });
1258
+ }
1259
+ }
1260
+ if (manifest.dynamic_contracts) {
1261
+ const dynamicConfigs = Array.isArray(manifest.dynamic_contracts) ? manifest.dynamic_contracts : [manifest.dynamic_contracts];
1262
+ for (const config of dynamicConfigs) {
1263
+ pluginsMap.get(plugin.name).contracts.push({
1264
+ name: `${config.prefix}/*`,
1265
+ type: "dynamic",
1266
+ path: "(run materialization to generate)"
1267
+ });
1268
+ }
1269
+ }
1270
+ }
1271
+ return {
1272
+ plugins: Array.from(pluginsMap.entries()).map(([name, data]) => ({
1273
+ name,
1274
+ path: data.path,
1275
+ contracts: data.contracts,
1276
+ ...data.services?.length && { services: data.services },
1277
+ ...data.contexts?.length && { contexts: data.contexts },
1278
+ ...data.routes?.length && { routes: data.routes },
1279
+ ...data.commands?.length && { commands: data.commands }
1280
+ }))
1281
+ };
1282
+ }
1283
+ async function discoverPluginCommands(options) {
1284
+ const { projectRoot, verbose, pluginFilter } = options;
1285
+ const allPlugins = await scanPlugins({
1286
+ projectRoot,
1287
+ verbose,
1288
+ discoverTransitive: true,
1289
+ includeDevDeps: true
1290
+ });
1291
+ const commands = [];
1292
+ for (const [packageName, plugin] of allPlugins) {
1293
+ if (!plugin.manifest.commands || plugin.manifest.commands.length === 0) continue;
1294
+ if (pluginFilter && plugin.name !== pluginFilter && packageName !== pluginFilter) {
1295
+ continue;
1296
+ }
1297
+ for (const cmd of plugin.manifest.commands) {
1298
+ let metadata;
1299
+ let metadataPath;
1300
+ if (cmd.command) {
1301
+ metadataPath = path.resolve(plugin.pluginPath, cmd.command);
1302
+ metadata = loadCommandMetadata(metadataPath);
1303
+ }
1304
+ commands.push({
1305
+ pluginName: plugin.name,
1306
+ pluginPath: plugin.pluginPath,
1307
+ packageName: plugin.packageName,
1308
+ isLocal: plugin.isLocal,
1309
+ commandName: cmd.name,
1310
+ handlerExport: cmd.name,
1311
+ pluginModule: plugin.manifest.module,
1312
+ metadata,
1313
+ metadataPath
1314
+ });
1315
+ if (verbose) {
1316
+ getLogger().info(`[Commands] Found ${plugin.name}/${cmd.name}`);
1317
+ }
1318
+ }
1319
+ }
1320
+ return commands;
1321
+ }
1322
+ function loadCommandMetadata(filePath) {
1323
+ try {
1324
+ const content = fs.readFileSync(filePath, "utf-8");
1325
+ return YAML.parse(content);
1326
+ } catch {
1327
+ return void 0;
1328
+ }
1329
+ }
1330
+ function commandSchemaToFlags(inputSchema) {
1331
+ const flags = [];
1332
+ for (const [field, type] of Object.entries(inputSchema)) {
1333
+ const isOptional = field.endsWith("?");
1334
+ const cleanName = isOptional ? field.slice(0, -1) : field;
1335
+ const kebabName = camelToKebab(cleanName);
1336
+ const cleanType = type.toLowerCase().trim();
1337
+ const isBoolean = cleanType === "boolean";
1338
+ flags.push({
1339
+ flag: isBoolean ? `--${kebabName}` : `--${kebabName} <value>`,
1340
+ description: "",
1341
+ required: !isOptional,
1342
+ type: cleanType === "number" ? "number" : isBoolean ? "boolean" : "string"
1343
+ });
1344
+ }
1345
+ return flags;
1346
+ }
1347
+ function camelToKebab(str) {
1348
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
1349
+ }
1350
+ function parseInputFromFlags(rawOptions, schema) {
1351
+ const input2 = {};
1352
+ for (const [field, type] of Object.entries(schema)) {
1353
+ const isOptional = field.endsWith("?");
1354
+ const cleanName = isOptional ? field.slice(0, -1) : field;
1355
+ const kebabName = camelToKebab(cleanName);
1356
+ const value = rawOptions[kebabName];
1357
+ if (value === void 0) {
1358
+ if (!isOptional) {
1359
+ throw new Error(`Missing required flag: --${kebabName}`);
1360
+ }
1361
+ continue;
1362
+ }
1363
+ const cleanType = type.toLowerCase().trim();
1364
+ if (cleanType === "number") {
1365
+ const parsed = Number(value);
1366
+ if (isNaN(parsed)) throw new Error(`Flag --${kebabName} must be a number`);
1367
+ input2[cleanName] = parsed;
1368
+ } else if (cleanType === "boolean") {
1369
+ input2[cleanName] = value === true || value === "true";
1370
+ } else {
1371
+ input2[cleanName] = String(value);
1372
+ }
1373
+ }
1374
+ return input2;
1375
+ }
1376
+ async function executePluginCommand(command, input2, viteServer) {
1377
+ const cliCommand = await loadCommandHandler(command, viteServer);
1378
+ const services = resolveServices(cliCommand.services);
1379
+ return cliCommand.handler(input2, ...services);
1380
+ }
1381
+ async function loadCommandHandler(command, viteServer) {
1382
+ let module;
1383
+ if (command.isLocal) {
1384
+ const moduleFile = command.pluginModule || "index";
1385
+ const modulePath = path.resolve(command.pluginPath, moduleFile);
1386
+ if (viteServer) {
1387
+ module = await viteServer.ssrLoadModule(modulePath);
1388
+ } else {
1389
+ module = await import(modulePath);
1390
+ }
1391
+ } else {
1392
+ const toolsEntry = `${command.packageName}/tools`;
1393
+ if (viteServer) {
1394
+ module = await viteServer.ssrLoadModule(toolsEntry);
1395
+ } else {
1396
+ module = await import(toolsEntry);
1397
+ }
1398
+ }
1399
+ for (const [, exported] of Object.entries(module)) {
1400
+ if (isJayCliCommand(exported) && exported.commandName === command.commandName) {
1401
+ return exported;
1402
+ }
1403
+ }
1404
+ const byName = module[command.handlerExport];
1405
+ if (byName && isJayCliCommand(byName)) {
1406
+ return byName;
1407
+ }
1408
+ throw new Error(
1409
+ `CLI command "${command.commandName}" not found as export in "${command.isLocal ? command.pluginPath : command.packageName}". Available exports: ${Object.keys(module).join(", ")}`
1410
+ );
1411
+ }
1412
+ class SetupNeedsAnswerError extends Error {
1413
+ constructor(plugin, key, type, promptMessage, choices) {
1414
+ super(`Setup needs answer for "${key}": ${promptMessage}`);
1415
+ this.plugin = plugin;
1416
+ this.key = key;
1417
+ this.type = type;
1418
+ this.promptMessage = promptMessage;
1419
+ this.choices = choices;
1420
+ this.name = "SetupNeedsAnswerError";
1421
+ }
1422
+ }
1423
+ async function discoverPluginsWithSetup(options) {
1424
+ const { projectRoot, verbose, pluginFilter } = options;
1425
+ const allPlugins = await scanPlugins({
1426
+ projectRoot,
1427
+ verbose,
1428
+ includeDevDeps: true,
1429
+ discoverTransitive: true
1430
+ });
1431
+ const pluginsWithSetup = [];
1432
+ for (const [packageName, plugin] of allPlugins) {
1433
+ if (!plugin.manifest.setup) continue;
1434
+ if (pluginFilter && plugin.name !== pluginFilter && packageName !== pluginFilter) {
1435
+ continue;
1436
+ }
1437
+ pluginsWithSetup.push({
1438
+ name: plugin.name,
1439
+ pluginPath: plugin.pluginPath,
1440
+ packageName: plugin.packageName,
1441
+ isLocal: plugin.isLocal,
1442
+ setupHandler: plugin.manifest.setup,
1443
+ setupDescription: plugin.manifest.description,
1444
+ dependencies: plugin.dependencies
1445
+ });
1446
+ if (verbose) {
1447
+ getLogger().info(`[Setup] Found plugin with setup: ${plugin.name}`);
1448
+ }
1449
+ }
1450
+ return sortPluginsByDependencies(pluginsWithSetup);
1451
+ }
1452
+ async function discoverPluginsWithAgentKit(options) {
1453
+ const { projectRoot, verbose, pluginFilter } = options;
1454
+ const allPlugins = await scanPlugins({
1455
+ projectRoot,
1456
+ verbose,
1457
+ includeDevDeps: true,
1458
+ discoverTransitive: true
1459
+ });
1460
+ const pluginsWithAgentKit = [];
1461
+ for (const [packageName, plugin] of allPlugins) {
1462
+ if (!plugin.manifest.agentkit) continue;
1463
+ if (pluginFilter && plugin.name !== pluginFilter && packageName !== pluginFilter) {
1464
+ continue;
1465
+ }
1466
+ pluginsWithAgentKit.push({
1467
+ name: plugin.name,
1468
+ pluginPath: plugin.pluginPath,
1469
+ packageName: plugin.packageName,
1470
+ isLocal: plugin.isLocal,
1471
+ agentKitHandler: plugin.manifest.agentkit,
1472
+ dependencies: plugin.dependencies
1473
+ });
1474
+ if (verbose) {
1475
+ getLogger().info(`[AgentKit] Found plugin with agent-kit handler: ${plugin.name}`);
1476
+ }
1477
+ }
1478
+ return sortPluginsByDependencies(pluginsWithAgentKit);
1479
+ }
1480
+ async function executePluginSetup(plugin, options) {
1481
+ const { projectRoot, configDir, force, interactive, prompt, initError, viteServer } = options;
1482
+ const context = {
1483
+ pluginName: plugin.name,
1484
+ projectRoot,
1485
+ configDir,
1486
+ services: getServiceRegistry(),
1487
+ initError,
1488
+ force,
1489
+ interactive,
1490
+ prompt
1491
+ };
1492
+ const handler = await loadHandler(plugin, plugin.setupHandler, viteServer);
1493
+ return handler(context);
1494
+ }
1495
+ async function executePluginAgentKit(plugin, options) {
1496
+ const { projectRoot, force, initError, viteServer } = options;
1497
+ const referencesDir = path.join(projectRoot, "agent-kit", "references", plugin.name);
1498
+ const context = {
1499
+ pluginName: plugin.name,
1500
+ projectRoot,
1501
+ referencesDir,
1502
+ services: getServiceRegistry(),
1503
+ initError,
1504
+ force
1505
+ };
1506
+ const handler = await loadHandler(
1507
+ plugin,
1508
+ plugin.agentKitHandler,
1509
+ viteServer
1510
+ );
1511
+ return handler(context);
1512
+ }
1513
+ async function loadHandler(plugin, handlerName, viteServer) {
1514
+ let module;
1515
+ if (plugin.isLocal) {
1516
+ const handlerPath = path.resolve(plugin.pluginPath, handlerName);
1517
+ if (viteServer) {
1518
+ module = await viteServer.ssrLoadModule(handlerPath);
1519
+ } else {
1520
+ module = await import(handlerPath);
1521
+ }
1522
+ if (typeof module[handlerName] === "function") return module[handlerName];
1523
+ if (typeof module.agentkit === "function") return module.agentkit;
1524
+ if (typeof module.setup === "function") return module.setup;
1525
+ if (typeof module.default === "function") return module.default;
1526
+ throw new Error(
1527
+ `Handler "${handlerName}" not found in "${plugin.pluginPath}". Available exports: ${Object.keys(module).join(", ")}`
1528
+ );
1529
+ } else {
1530
+ const toolsEntry = `${plugin.packageName}/tools`;
1531
+ if (viteServer) {
1532
+ module = await viteServer.ssrLoadModule(toolsEntry);
1533
+ } else {
1534
+ module = await import(toolsEntry);
1535
+ }
1536
+ if (typeof module[handlerName] !== "function") {
1537
+ throw new Error(
1538
+ `Handler "${handlerName}" not found as export in "${toolsEntry}". Available exports: ${Object.keys(module).join(", ")}`
1539
+ );
1540
+ }
1541
+ return module[handlerName];
1542
+ }
1543
+ }
225
1544
  async function initializeServicesForCli(projectRoot, viteServer, quiet = false) {
226
1545
  const path2 = await import("node:path");
227
1546
  const fs2 = await import("node:fs");
228
1547
  const {
229
1548
  runInitCallbacks: runInitCallbacks2,
230
- getServiceRegistry,
1549
+ getServiceRegistry: getServiceRegistry2,
231
1550
  discoverPluginsWithInit: discoverPluginsWithInit2,
232
1551
  sortPluginsByDependencies: sortPluginsByDependencies2,
233
1552
  executePluginServerInits: executePluginServerInits2
@@ -260,7 +1579,7 @@ async function initializeServicesForCli(projectRoot, viteServer, quiet = false)
260
1579
  getLogger().warn(chalk.yellow(`āš ļø Service initialization failed: ${error.message}`));
261
1580
  getLogger().warn(chalk.gray(" Static contracts will still be listed."));
262
1581
  }
263
- return { services: getServiceRegistry(), initErrors };
1582
+ return { services: getServiceRegistry2(), initErrors };
264
1583
  }
265
1584
  async function runDev(projectPath, options) {
266
1585
  const logLevel = options.quiet ? "silent" : options.verbose ? "verbose" : "info";
@@ -274,12 +1593,12 @@ async function runDev(projectPath, options) {
274
1593
  });
275
1594
  }
276
1595
  async function resolveProductionContext(projectPath, versionOverride) {
277
- const resolvedPath = path$1.resolve(projectPath || process.cwd());
278
- const jayConfigPath = path$1.join(resolvedPath, ".jay");
1596
+ const resolvedPath = path__default.resolve(projectPath || process.cwd());
1597
+ const jayConfigPath = path__default.join(resolvedPath, ".jay");
279
1598
  let pagesBase = "./src/pages";
280
1599
  let siteBaseUrl;
281
1600
  try {
282
- const jayConfig = YAML.parse(await fs$1.readFile(jayConfigPath, "utf-8"));
1601
+ const jayConfig = YAML.parse(await fs$2.readFile(jayConfigPath, "utf-8"));
283
1602
  pagesBase = jayConfig?.devServer?.pagesBase || pagesBase;
284
1603
  siteBaseUrl = jayConfig?.site?.baseUrl;
285
1604
  } catch {
@@ -287,17 +1606,17 @@ async function resolveProductionContext(projectPath, versionOverride) {
287
1606
  const version = versionOverride || await resolveVersionFromPackageJson(resolvedPath);
288
1607
  return {
289
1608
  resolvedPath,
290
- pagesRoot: path$1.resolve(resolvedPath, pagesBase),
291
- buildRoot: path$1.join(resolvedPath, "build"),
1609
+ pagesRoot: path__default.resolve(resolvedPath, pagesBase),
1610
+ buildRoot: path__default.join(resolvedPath, "build"),
292
1611
  version,
293
- tsConfigFilePath: path$1.join(resolvedPath, "tsconfig.json"),
1612
+ tsConfigFilePath: path__default.join(resolvedPath, "tsconfig.json"),
294
1613
  siteBaseUrl
295
1614
  };
296
1615
  }
297
1616
  async function resolveVersionFromPackageJson(projectRoot) {
298
1617
  try {
299
1618
  const pkgJson = JSON.parse(
300
- await fs$1.readFile(path$1.join(projectRoot, "package.json"), "utf-8")
1619
+ await fs$2.readFile(path__default.join(projectRoot, "package.json"), "utf-8")
301
1620
  );
302
1621
  if (pkgJson.version) {
303
1622
  return pkgJson.version;
@@ -313,7 +1632,7 @@ function initLogger(verbose) {
313
1632
  async function runBuild(projectPath, options) {
314
1633
  initLogger(options.verbose);
315
1634
  const ctx = await resolveProductionContext(projectPath, options.version);
316
- const { buildVersion } = await import("@jay-framework/production-server");
1635
+ const { buildVersion } = await import("./index-B9vCxAZ1.js");
317
1636
  await buildVersion({
318
1637
  version: ctx.version,
319
1638
  projectRoot: ctx.resolvedPath,
@@ -1158,7 +2477,7 @@ function isRelativeHandlerRef(value) {
1158
2477
  function resolveModulePath$1(basePath) {
1159
2478
  for (const ext of ["", ".ts", ".js", "/index.ts", "/index.js"]) {
1160
2479
  const candidate = basePath + ext;
1161
- if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
2480
+ if (fs$1.existsSync(candidate) && fs$1.statSync(candidate).isFile()) {
1162
2481
  return candidate;
1163
2482
  }
1164
2483
  }
@@ -1166,10 +2485,10 @@ function resolveModulePath$1(basePath) {
1166
2485
  }
1167
2486
  function collectTypeScriptFiles(dir, depth = 0) {
1168
2487
  if (depth > 4) return [];
1169
- const entries = fs.readdirSync(dir, { withFileTypes: true });
2488
+ const entries = fs$1.readdirSync(dir, { withFileTypes: true });
1170
2489
  const files = [];
1171
2490
  for (const entry of entries) {
1172
- const fullPath = path.join(dir, entry.name);
2491
+ const fullPath = path$1.join(dir, entry.name);
1173
2492
  if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "test") {
1174
2493
  files.push(...collectTypeScriptFiles(fullPath, depth + 1));
1175
2494
  } else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js"))) {
@@ -1180,13 +2499,13 @@ function collectTypeScriptFiles(dir, depth = 0) {
1180
2499
  }
1181
2500
  function resolveHandlerSourceFile(pluginPath, handlerRef, isNpmPackage) {
1182
2501
  if (isRelativeHandlerRef(handlerRef)) {
1183
- return resolveModulePath$1(path.join(pluginPath, handlerRef)) ?? null;
2502
+ return resolveModulePath$1(path$1.join(pluginPath, handlerRef)) ?? null;
1184
2503
  }
1185
- const searchRoots = isNpmPackage ? [path.join(pluginPath, "lib"), path.join(pluginPath, "dist")] : [pluginPath];
2504
+ const searchRoots = isNpmPackage ? [path$1.join(pluginPath, "lib"), path$1.join(pluginPath, "dist")] : [pluginPath];
1186
2505
  for (const root of searchRoots) {
1187
- if (!fs.existsSync(root)) continue;
2506
+ if (!fs$1.existsSync(root)) continue;
1188
2507
  for (const file of collectTypeScriptFiles(root)) {
1189
- const content = fs.readFileSync(file, "utf-8");
2508
+ const content = fs$1.readFileSync(file, "utf-8");
1190
2509
  const definesHandler = new RegExp(
1191
2510
  `export\\s+(?:async\\s+)?function\\s+${handlerRef}\\b`
1192
2511
  ).test(content);
@@ -1203,7 +2522,7 @@ function resolveHandlerSourceFile(pluginPath, handlerRef, isNpmPackage) {
1203
2522
  );
1204
2523
  if (reExportMatch) {
1205
2524
  const importSpec = reExportMatch[1].replace(/\.js$/, "");
1206
- const resolved = resolveModulePath$1(path.resolve(path.dirname(file), importSpec));
2525
+ const resolved = resolveModulePath$1(path$1.resolve(path$1.dirname(file), importSpec));
1207
2526
  if (resolved) return resolved;
1208
2527
  }
1209
2528
  }
@@ -1279,7 +2598,7 @@ function handlerBodyWritesAddMenuCatalog(body) {
1279
2598
  function resolveSetupHandlerFunctionBody(pluginPath, handlerRef, isNpmPackage) {
1280
2599
  const sourceFile = resolveHandlerSourceFile(pluginPath, handlerRef, isNpmPackage);
1281
2600
  if (!sourceFile) return null;
1282
- const source = fs.readFileSync(sourceFile, "utf-8");
2601
+ const source = fs$1.readFileSync(sourceFile, "utf-8");
1283
2602
  if (isRelativeHandlerRef(handlerRef)) {
1284
2603
  return extractDefaultExportFunctionBody(source) ?? extractFunctionBody(source, "setup") ?? extractFunctionBody(source, handlerRef);
1285
2604
  }
@@ -1312,7 +2631,7 @@ function mapLintFinding(finding, catalogPath, severity) {
1312
2631
  }
1313
2632
  function pluginShipsAddMenuCatalog(context) {
1314
2633
  return ADD_MENU_CATALOG_REL_PATHS.some(
1315
- (relPath) => fs.existsSync(path.join(context.pluginPath, relPath))
2634
+ (relPath) => fs$1.existsSync(path$1.join(context.pluginPath, relPath))
1316
2635
  );
1317
2636
  }
1318
2637
  function validateAddMenuAgentKitHandler(context, result) {
@@ -1346,7 +2665,7 @@ function validateAddMenuAgentKitHandler(context, result) {
1346
2665
  async function validateAddMenuCatalogFileAtPath(catalogPath, relPath, result) {
1347
2666
  let parsed;
1348
2667
  try {
1349
- const content = await fs.promises.readFile(catalogPath, "utf-8");
2668
+ const content = await fs$1.promises.readFile(catalogPath, "utf-8");
1350
2669
  parsed = YAML.parse(content);
1351
2670
  } catch (error) {
1352
2671
  const message = error instanceof Error ? error.message : String(error);
@@ -1375,8 +2694,8 @@ async function validateAddMenuCatalogFileAtPath(catalogPath, relPath, result) {
1375
2694
  async function validateAddMenuCatalog(context, result) {
1376
2695
  validateAddMenuAgentKitHandler(context, result);
1377
2696
  for (const relPath of ADD_MENU_CATALOG_REL_PATHS) {
1378
- const catalogPath = path.join(context.pluginPath, relPath);
1379
- if (!fs.existsSync(catalogPath)) continue;
2697
+ const catalogPath = path$1.join(context.pluginPath, relPath);
2698
+ if (!fs$1.existsSync(catalogPath)) continue;
1380
2699
  await validateAddMenuCatalogFileAtPath(catalogPath, relPath, result);
1381
2700
  }
1382
2701
  }
@@ -1493,7 +2812,7 @@ function mapSchemaError(error, relPath) {
1493
2812
  function validateSettingsTemplateAtPath(catalogPath, relPath, result, manifest) {
1494
2813
  let parsed;
1495
2814
  try {
1496
- parsed = YAML.parse(fs.readFileSync(catalogPath, "utf-8"));
2815
+ parsed = YAML.parse(fs$1.readFileSync(catalogPath, "utf-8"));
1497
2816
  } catch (err) {
1498
2817
  result.errors.push({
1499
2818
  type: "schema",
@@ -1527,8 +2846,8 @@ function validateSettingsTemplateAtPath(catalogPath, relPath, result, manifest)
1527
2846
  }
1528
2847
  }
1529
2848
  async function validateAiditorSettings(context, result) {
1530
- const templatePath = path.join(context.pluginPath, AIDITOR_SETTINGS_TEMPLATE_REL_PATH);
1531
- if (!fs.existsSync(templatePath)) {
2849
+ const templatePath = path$1.join(context.pluginPath, AIDITOR_SETTINGS_TEMPLATE_REL_PATH);
2850
+ if (!fs$1.existsSync(templatePath)) {
1532
2851
  return;
1533
2852
  }
1534
2853
  validateSettingsTemplateAtPath(
@@ -1563,10 +2882,10 @@ async function validatePluginPackage(pluginPath, options) {
1563
2882
  contractsChecked: 0,
1564
2883
  componentsChecked: 0
1565
2884
  };
1566
- const pluginYamlPath = path.join(pluginPath, "plugin.yaml");
1567
- const pluginManifest = loadPluginManifest(pluginPath);
2885
+ const pluginYamlPath = path$1.join(pluginPath, "plugin.yaml");
2886
+ const pluginManifest = loadPluginManifest$1(pluginPath);
1568
2887
  if (!pluginManifest) {
1569
- if (!fs.existsSync(pluginYamlPath)) {
2888
+ if (!fs$1.existsSync(pluginYamlPath)) {
1570
2889
  result.errors.push({
1571
2890
  type: "file-missing",
1572
2891
  message: "plugin.yaml not found",
@@ -1587,7 +2906,7 @@ async function validatePluginPackage(pluginPath, options) {
1587
2906
  const context = {
1588
2907
  manifest: pluginManifest,
1589
2908
  pluginPath,
1590
- isNpmPackage: fs.existsSync(path.join(pluginPath, "package.json"))
2909
+ isNpmPackage: fs$1.existsSync(path$1.join(pluginPath, "package.json"))
1591
2910
  };
1592
2911
  await validateSchema(context, result);
1593
2912
  if (pluginManifest.contracts) {
@@ -1615,12 +2934,13 @@ async function validatePluginPackage(pluginPath, options) {
1615
2934
  }
1616
2935
  await validateAddMenuCatalog(context, result);
1617
2936
  await validateAiditorSettings(context, result);
2937
+ validateNoCompilerLeak(context, result);
1618
2938
  result.valid = result.errors.length === 0;
1619
2939
  return result;
1620
2940
  }
1621
2941
  async function validateLocalPlugins(projectPath, options) {
1622
- const pluginsPath = path.join(projectPath, "src/plugins");
1623
- if (!fs.existsSync(pluginsPath)) {
2942
+ const pluginsPath = path$1.join(projectPath, "src/plugins");
2943
+ if (!fs$1.existsSync(pluginsPath)) {
1624
2944
  return {
1625
2945
  valid: false,
1626
2946
  errors: [
@@ -1634,10 +2954,10 @@ async function validateLocalPlugins(projectPath, options) {
1634
2954
  warnings: []
1635
2955
  };
1636
2956
  }
1637
- const pluginDirs = fs.readdirSync(pluginsPath, { withFileTypes: true }).filter((d) => d.isDirectory());
2957
+ const pluginDirs = fs$1.readdirSync(pluginsPath, { withFileTypes: true }).filter((d) => d.isDirectory());
1638
2958
  const allResults = [];
1639
2959
  for (const pluginDir of pluginDirs) {
1640
- const pluginPath = path.join(pluginsPath, pluginDir.name);
2960
+ const pluginPath = path$1.join(pluginsPath, pluginDir.name);
1641
2961
  const result = await validatePluginPackage(pluginPath, options);
1642
2962
  allResults.push(result);
1643
2963
  }
@@ -1651,8 +2971,8 @@ async function validateLocalPlugins(projectPath, options) {
1651
2971
  };
1652
2972
  }
1653
2973
  function validateDocFile(docPath, label, context, result) {
1654
- const resolvedPath = path.join(context.pluginPath, docPath);
1655
- if (!fs.existsSync(resolvedPath)) {
2974
+ const resolvedPath = path$1.join(context.pluginPath, docPath);
2975
+ if (!fs$1.existsSync(resolvedPath)) {
1656
2976
  result.errors.push({
1657
2977
  type: "file-missing",
1658
2978
  message: `Doc file for ${label} not found: ${docPath}`,
@@ -1662,9 +2982,9 @@ function validateDocFile(docPath, label, context, result) {
1662
2982
  return;
1663
2983
  }
1664
2984
  if (context.isNpmPackage) {
1665
- const packageJsonPath = path.join(context.pluginPath, "package.json");
2985
+ const packageJsonPath = path$1.join(context.pluginPath, "package.json");
1666
2986
  try {
1667
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
2987
+ const packageJson = JSON.parse(fs$1.readFileSync(packageJsonPath, "utf-8"));
1668
2988
  if (packageJson.exports) {
1669
2989
  const exportKey = "./" + docPath.replace(/^\.\//, "");
1670
2990
  if (!packageJson.exports[exportKey]) {
@@ -1788,13 +3108,28 @@ async function validateSchema(context, result) {
1788
3108
  }
1789
3109
  }
1790
3110
  }
1791
- if (!manifest.contracts && !manifest.dynamic_contracts) {
1792
- result.warnings.push({
1793
- type: "schema",
1794
- message: "Plugin has no contracts or dynamic_contracts defined",
1795
- location: "plugin.yaml",
1796
- suggestion: 'Add either "contracts" or "dynamic_contracts" to expose functionality'
1797
- });
3111
+ const hasCapability = Boolean(
3112
+ manifest.contracts || manifest.dynamic_contracts || manifest.actions || manifest.validators || manifest.routes || manifest.services || manifest.contexts || manifest.init || manifest.setup || manifest.agentkit || manifest.commands
3113
+ );
3114
+ if (!hasCapability) {
3115
+ if (manifest.global === true) {
3116
+ const hasGlobalEntry = !context.isNpmPackage || checkExportExists("init", context, ".") || checkExportExists("setup", context, ".");
3117
+ if (!hasGlobalEntry) {
3118
+ result.errors.push({
3119
+ type: "export-mismatch",
3120
+ message: 'Plugin declares "global: true" but exports no init/setup handler to run on each page',
3121
+ location: "plugin.yaml",
3122
+ suggestion: 'Export an "init" (or "setup") handler from the package entry, or declare a capability'
3123
+ });
3124
+ }
3125
+ } else {
3126
+ result.warnings.push({
3127
+ type: "schema",
3128
+ message: "Plugin declares no capabilities (contracts, dynamic_contracts, actions, validators, routes, services, contexts, init, setup, agentkit, commands)",
3129
+ location: "plugin.yaml",
3130
+ suggestion: "Declare at least one capability. See agent-kit/plugin/plugin-structure.md"
3131
+ });
3132
+ }
1798
3133
  }
1799
3134
  if (manifest.services) {
1800
3135
  if (!Array.isArray(manifest.services)) {
@@ -1858,14 +3193,22 @@ async function validateSchema(context, result) {
1858
3193
  }
1859
3194
  if (manifest.actions) {
1860
3195
  for (const entry of manifest.actions) {
1861
- const exportName = typeof entry === "string" ? entry : entry.name;
3196
+ if (typeof entry === "object" && entry.devOnly !== void 0 && typeof entry.devOnly !== "boolean") {
3197
+ result.errors.push({
3198
+ type: "schema",
3199
+ message: `Action "${entry.name}" devOnly must be a boolean`,
3200
+ location: "plugin.yaml actions"
3201
+ });
3202
+ }
3203
+ const { name: exportName, devOnly } = normalizeActionEntry$1(entry);
1862
3204
  if (exportName) {
1863
3205
  validateHandlerRef(
1864
3206
  exportName,
1865
3207
  `Action "${exportName}"`,
1866
3208
  "plugin.yaml actions",
1867
3209
  context,
1868
- result
3210
+ result,
3211
+ devOnly ? "./tools" : "."
1869
3212
  );
1870
3213
  }
1871
3214
  }
@@ -1910,7 +3253,8 @@ async function validateSchema(context, result) {
1910
3253
  `Route "${route.path}" component`,
1911
3254
  `plugin.yaml routes`,
1912
3255
  context,
1913
- result
3256
+ result,
3257
+ route.devOnly ? "./tools" : "."
1914
3258
  );
1915
3259
  }
1916
3260
  if (route.jayHtml) {
@@ -1964,7 +3308,8 @@ async function validateSchema(context, result) {
1964
3308
  `Validator "${validator.name}" handler`,
1965
3309
  "plugin.yaml validators",
1966
3310
  context,
1967
- result
3311
+ result,
3312
+ "./tools"
1968
3313
  );
1969
3314
  }
1970
3315
  });
@@ -1984,7 +3329,8 @@ async function validateSchema(context, result) {
1984
3329
  "Setup handler",
1985
3330
  "plugin.yaml setup",
1986
3331
  context,
1987
- result
3332
+ result,
3333
+ "./tools"
1988
3334
  );
1989
3335
  }
1990
3336
  }
@@ -1994,30 +3340,31 @@ async function validateSchema(context, result) {
1994
3340
  "Agent-kit handler",
1995
3341
  "plugin.yaml agentkit",
1996
3342
  context,
1997
- result
3343
+ result,
3344
+ "./tools"
1998
3345
  );
1999
3346
  }
2000
3347
  }
2001
- function checkExportExists(exportName, context) {
2002
- const packageJsonPath = path.join(context.pluginPath, "package.json");
2003
- if (!fs.existsSync(packageJsonPath)) return true;
3348
+ function checkExportExists(exportName, context, exportKey = ".") {
3349
+ const packageJsonPath = path$1.join(context.pluginPath, "package.json");
3350
+ if (!fs$1.existsSync(packageJsonPath)) return true;
2004
3351
  let mainPath;
2005
3352
  try {
2006
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
2007
- if (packageJson.exports?.["."]) {
2008
- const entry = packageJson.exports["."];
3353
+ const packageJson = JSON.parse(fs$1.readFileSync(packageJsonPath, "utf-8"));
3354
+ if (packageJson.exports?.[exportKey]) {
3355
+ const entry = packageJson.exports[exportKey];
2009
3356
  const entryPath = typeof entry === "string" ? entry : entry.default || entry.import;
2010
- if (entryPath) mainPath = path.join(context.pluginPath, entryPath);
3357
+ if (entryPath) mainPath = path$1.join(context.pluginPath, entryPath);
2011
3358
  }
2012
- if (!mainPath && packageJson.main) {
2013
- mainPath = path.join(context.pluginPath, packageJson.main);
3359
+ if (!mainPath && exportKey === "." && packageJson.main) {
3360
+ mainPath = path$1.join(context.pluginPath, packageJson.main);
2014
3361
  }
2015
3362
  } catch {
2016
3363
  return true;
2017
3364
  }
2018
- if (!mainPath || !fs.existsSync(mainPath)) return true;
3365
+ if (!mainPath || !fs$1.existsSync(mainPath)) return true;
2019
3366
  try {
2020
- const content = fs.readFileSync(mainPath, "utf-8");
3367
+ const content = fs$1.readFileSync(mainPath, "utf-8");
2021
3368
  const patterns = [
2022
3369
  new RegExp(`export\\s*\\{[^}]*\\b${exportName}\\b[^}]*\\}`, "m"),
2023
3370
  new RegExp(`export\\s+(?:async\\s+)?function\\s+${exportName}\\b`),
@@ -2031,7 +3378,63 @@ function checkExportExists(exportName, context) {
2031
3378
  function isRelativePath(value) {
2032
3379
  return value.startsWith("./") || value.startsWith("../");
2033
3380
  }
2034
- function validateHandlerRef(value, label, location, context, result) {
3381
+ function resolveEntryFile(context, exportKey) {
3382
+ const packageJsonPath = path$1.join(context.pluginPath, "package.json");
3383
+ if (!fs$1.existsSync(packageJsonPath)) return void 0;
3384
+ try {
3385
+ const packageJson = JSON.parse(fs$1.readFileSync(packageJsonPath, "utf-8"));
3386
+ const entry = packageJson.exports?.[exportKey];
3387
+ const entryPath = typeof entry === "string" ? entry : entry?.default || entry?.import || void 0;
3388
+ const resolved = entryPath || (exportKey === "." ? packageJson.main : void 0) ? path$1.join(context.pluginPath, entryPath || packageJson.main) : void 0;
3389
+ return resolved && fs$1.existsSync(resolved) ? resolved : void 0;
3390
+ } catch {
3391
+ return void 0;
3392
+ }
3393
+ }
3394
+ function hasComponentCapability(context) {
3395
+ const m = context.manifest;
3396
+ return Boolean(m.contracts || m.dynamic_contracts || m.routes);
3397
+ }
3398
+ function needsToolsEntry(manifest) {
3399
+ if (manifest.validators || manifest.commands || manifest.agentkit || manifest.setup) {
3400
+ return true;
3401
+ }
3402
+ if (manifest.actions) {
3403
+ return manifest.actions.some((entry) => normalizeActionEntry$1(entry).devOnly === true);
3404
+ }
3405
+ return false;
3406
+ }
3407
+ function detectInteractivePhase(context) {
3408
+ if (!context.isNpmPackage) return "unknown";
3409
+ const entryFile = resolveEntryFile(context, ".");
3410
+ if (!entryFile) return "unknown";
3411
+ try {
3412
+ const content = fs$1.readFileSync(entryFile, "utf-8");
3413
+ return content.includes("withInteractiveMark(");
3414
+ } catch {
3415
+ return "unknown";
3416
+ }
3417
+ }
3418
+ function validateNoCompilerLeak(context, result) {
3419
+ if (!context.isNpmPackage) return;
3420
+ const entryFile = resolveEntryFile(context, ".");
3421
+ if (!entryFile) return;
3422
+ let content;
3423
+ try {
3424
+ content = fs$1.readFileSync(entryFile, "utf-8");
3425
+ } catch {
3426
+ return;
3427
+ }
3428
+ if (content.includes("@jay-framework/compiler-")) {
3429
+ result.errors.push({
3430
+ type: "compiler-leak",
3431
+ message: 'Serve entry "." (dist/index.js) imports "@jay-framework/compiler-…" — the compiler must not reach the production serve bundle',
3432
+ location: entryFile,
3433
+ suggestion: 'Move the compiler-using handler (validator, agentkit, setup, or a devOnly action) to lib/tools.ts (the "./tools" export) and remove its re-export from lib/index.ts. A compiler-using action is really a command or a devOnly action (DL#179/#180).'
3434
+ });
3435
+ }
3436
+ }
3437
+ function validateHandlerRef(value, label, location, context, result, exportKey = ".") {
2035
3438
  if (context.isNpmPackage) {
2036
3439
  if (isRelativePath(value)) {
2037
3440
  result.errors.push({
@@ -2040,18 +3443,19 @@ function validateHandlerRef(value, label, location, context, result) {
2040
3443
  location,
2041
3444
  suggestion: `Export the function from the package entry point and use the export name instead of a path`
2042
3445
  });
2043
- } else if (!checkExportExists(value, context)) {
3446
+ } else if (!checkExportExists(value, context, exportKey)) {
3447
+ const entryFile = exportKey === "./tools" ? "lib/tools.ts" : exportKey === "./client" ? "lib/index.client.ts" : "lib/index.ts";
2044
3448
  result.errors.push({
2045
3449
  type: "export-mismatch",
2046
- message: `${label} "${value}" is not exported from the package`,
3450
+ message: `${label} "${value}" is not exported from the "${exportKey}" entry`,
2047
3451
  location,
2048
- suggestion: `Add "export { ${value} } from '...'" to the package entry point`
3452
+ suggestion: `Add "export { ${value} } from '...'" to ${entryFile} (the "${exportKey}" export)`
2049
3453
  });
2050
3454
  }
2051
3455
  } else if (isRelativePath(value)) {
2052
- const handlerPath = path.join(context.pluginPath, value);
3456
+ const handlerPath = path$1.join(context.pluginPath, value);
2053
3457
  const extensions = ["", ".ts", ".js", "/index.ts", "/index.js"];
2054
- const found = extensions.some((ext) => fs.existsSync(handlerPath + ext));
3458
+ const found = extensions.some((ext) => fs$1.existsSync(handlerPath + ext));
2055
3459
  if (!found) {
2056
3460
  result.errors.push({
2057
3461
  type: "file-missing",
@@ -2064,18 +3468,18 @@ function validateHandlerRef(value, label, location, context, result) {
2064
3468
  }
2065
3469
  function resolveContractFile(contractSpec, context) {
2066
3470
  if (context.isNpmPackage) {
2067
- const packageJsonPath = path.join(context.pluginPath, "package.json");
2068
- if (fs.existsSync(packageJsonPath)) {
3471
+ const packageJsonPath = path$1.join(context.pluginPath, "package.json");
3472
+ if (fs$1.existsSync(packageJsonPath)) {
2069
3473
  try {
2070
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
3474
+ const packageJson = JSON.parse(fs$1.readFileSync(packageJsonPath, "utf-8"));
2071
3475
  if (packageJson.exports) {
2072
3476
  const exportKey = "./" + contractSpec;
2073
3477
  const exportValue = packageJson.exports[exportKey];
2074
3478
  if (exportValue) {
2075
3479
  const resolvedPath = typeof exportValue === "string" ? exportValue : exportValue.default || exportValue.import || exportValue.require;
2076
3480
  if (resolvedPath) {
2077
- const fullPath = path.join(context.pluginPath, resolvedPath);
2078
- if (fs.existsSync(fullPath)) return fullPath;
3481
+ const fullPath = path$1.join(context.pluginPath, resolvedPath);
3482
+ if (fs$1.existsSync(fullPath)) return fullPath;
2079
3483
  }
2080
3484
  }
2081
3485
  }
@@ -2083,13 +3487,13 @@ function resolveContractFile(contractSpec, context) {
2083
3487
  }
2084
3488
  }
2085
3489
  for (const dir of ["dist", "lib", ""]) {
2086
- const candidate = path.join(context.pluginPath, dir, contractSpec);
2087
- if (fs.existsSync(candidate)) return candidate;
3490
+ const candidate = path$1.join(context.pluginPath, dir, contractSpec);
3491
+ if (fs$1.existsSync(candidate)) return candidate;
2088
3492
  }
2089
3493
  return void 0;
2090
3494
  } else {
2091
- const candidate = path.join(context.pluginPath, contractSpec);
2092
- return fs.existsSync(candidate) ? candidate : void 0;
3495
+ const candidate = path$1.join(context.pluginPath, contractSpec);
3496
+ return fs$1.existsSync(candidate) ? candidate : void 0;
2093
3497
  }
2094
3498
  }
2095
3499
  async function validateContract(contract, index, context, generateTypes, result) {
@@ -2100,12 +3504,12 @@ async function validateContract(contract, index, context, generateTypes, result)
2100
3504
  type: "file-missing",
2101
3505
  message: `Contract file not found: ${contract.contract}`,
2102
3506
  location: `plugin.yaml contracts[${index}]`,
2103
- suggestion: context.isNpmPackage ? `Ensure the contract is exported in package.json and the file exists` : `Create the contract file at ${path.join(context.pluginPath, contract.contract)}`
3507
+ suggestion: context.isNpmPackage ? `Ensure the contract is exported in package.json and the file exists` : `Create the contract file at ${path$1.join(context.pluginPath, contract.contract)}`
2104
3508
  });
2105
3509
  return;
2106
3510
  }
2107
3511
  try {
2108
- const contractContent = await fs.promises.readFile(contractPath, "utf-8");
3512
+ const contractContent = await fs$1.promises.readFile(contractPath, "utf-8");
2109
3513
  const parsedContract = YAML.parse(contractContent);
2110
3514
  if (!parsedContract.name) {
2111
3515
  result.errors.push({
@@ -2172,21 +3576,21 @@ function hasExportModifier(node) {
2172
3576
  function resolveModulePath(basePath) {
2173
3577
  for (const ext of ["", ".ts", ".js", "/index.ts", "/index.js"]) {
2174
3578
  const candidate = basePath + ext;
2175
- if (fs.existsSync(candidate)) return candidate;
3579
+ if (fs$1.existsSync(candidate)) return candidate;
2176
3580
  }
2177
3581
  return void 0;
2178
3582
  }
2179
3583
  function resolveComponentSourcePath(componentName, context) {
2180
3584
  const modulePath = context.manifest.module || "index";
2181
- const entryBase = path.join(context.pluginPath, modulePath);
3585
+ const entryBase = path$1.join(context.pluginPath, modulePath);
2182
3586
  const entryFile = resolveModulePath(entryBase);
2183
- const libEntryFile = !entryFile ? resolveModulePath(path.join(context.pluginPath, "lib", modulePath)) : void 0;
3587
+ const libEntryFile = !entryFile ? resolveModulePath(path$1.join(context.pluginPath, "lib", modulePath)) : void 0;
2184
3588
  const sourceEntry = entryFile || libEntryFile;
2185
3589
  if (!sourceEntry) return void 0;
2186
3590
  if (!sourceEntry.endsWith(".ts")) return void 0;
2187
3591
  let sourceCode;
2188
3592
  try {
2189
- sourceCode = fs.readFileSync(sourceEntry, "utf-8");
3593
+ sourceCode = fs$1.readFileSync(sourceEntry, "utf-8");
2190
3594
  } catch {
2191
3595
  return void 0;
2192
3596
  }
@@ -2212,7 +3616,7 @@ function resolveComponentSourcePath(componentName, context) {
2212
3616
  for (const element of exportClause.elements) {
2213
3617
  const exportedName = element.name.text;
2214
3618
  if (exportedName === componentName) {
2215
- const resolvedBase = path.resolve(path.dirname(sourceEntry), moduleSpec);
3619
+ const resolvedBase = path$1.resolve(path$1.dirname(sourceEntry), moduleSpec);
2216
3620
  return resolveModulePath(resolvedBase);
2217
3621
  }
2218
3622
  }
@@ -2220,11 +3624,11 @@ function resolveComponentSourcePath(componentName, context) {
2220
3624
  }
2221
3625
  for (const moduleSpec of starReexportModules) {
2222
3626
  if (!moduleSpec.startsWith(".")) continue;
2223
- const resolvedBase = path.resolve(path.dirname(sourceEntry), moduleSpec);
3627
+ const resolvedBase = path$1.resolve(path$1.dirname(sourceEntry), moduleSpec);
2224
3628
  const resolvedPath = resolveModulePath(resolvedBase);
2225
3629
  if (!resolvedPath || !resolvedPath.endsWith(".ts")) continue;
2226
3630
  try {
2227
- const modSource = fs.readFileSync(resolvedPath, "utf-8");
3631
+ const modSource = fs$1.readFileSync(resolvedPath, "utf-8");
2228
3632
  const modFile = u.createSourceFile(
2229
3633
  resolvedPath,
2230
3634
  modSource,
@@ -2263,15 +3667,15 @@ async function checkComponentContractConsistency(contract, context, result) {
2263
3667
  if (!contractPath) return;
2264
3668
  let contractContent;
2265
3669
  try {
2266
- contractContent = await fs.promises.readFile(contractPath, "utf-8");
3670
+ contractContent = await fs$1.promises.readFile(contractPath, "utf-8");
2267
3671
  } catch {
2268
3672
  return;
2269
3673
  }
2270
- const parsed = parseContract(contractContent, path.basename(contractPath));
3674
+ const parsed = parseContract(contractContent, path$1.basename(contractPath));
2271
3675
  if (parsed.validations.length > 0) return;
2272
3676
  let sourceCode;
2273
3677
  try {
2274
- sourceCode = await fs.promises.readFile(sourcePath, "utf-8");
3678
+ sourceCode = await fs$1.promises.readFile(sourcePath, "utf-8");
2275
3679
  } catch {
2276
3680
  return;
2277
3681
  }
@@ -2290,8 +3694,8 @@ async function checkComponentContractConsistency(contract, context, result) {
2290
3694
  result.warnings.push(...checkResult.warnings);
2291
3695
  }
2292
3696
  async function validatePackageJson(context, result) {
2293
- const packageJsonPath = path.join(context.pluginPath, "package.json");
2294
- if (!fs.existsSync(packageJsonPath)) {
3697
+ const packageJsonPath = path$1.join(context.pluginPath, "package.json");
3698
+ if (!fs$1.existsSync(packageJsonPath)) {
2295
3699
  result.warnings.push({
2296
3700
  type: "file-missing",
2297
3701
  message: "package.json not found",
@@ -2301,7 +3705,7 @@ async function validatePackageJson(context, result) {
2301
3705
  return;
2302
3706
  }
2303
3707
  try {
2304
- const packageJson = JSON.parse(await fs.promises.readFile(packageJsonPath, "utf-8"));
3708
+ const packageJson = JSON.parse(await fs$1.promises.readFile(packageJsonPath, "utf-8"));
2305
3709
  if (!packageJson.exports) {
2306
3710
  result.warnings.push({
2307
3711
  type: "export-mismatch",
@@ -2319,11 +3723,30 @@ async function validatePackageJson(context, result) {
2319
3723
  });
2320
3724
  }
2321
3725
  if (!packageJson.exports["./client"]) {
2322
- result.warnings.push({
3726
+ const interactivity = detectInteractivePhase(context);
3727
+ const needsClient = context.manifest.contexts !== void 0 || interactivity === true;
3728
+ if (needsClient) {
3729
+ result.errors.push({
3730
+ type: "export-mismatch",
3731
+ message: 'package.json exports missing "./client" entry point, but the plugin ' + (context.manifest.contexts !== void 0 ? "declares contexts (client-side by definition)" : "provides an interactive component"),
3732
+ location: packageJsonPath,
3733
+ suggestion: 'Add "./client": "./dist/index.client.js" to exports. The client bundle provides components for hydration and client-side contexts. Build with: vite build (client) + vite build --ssr (server)'
3734
+ });
3735
+ } else if (interactivity === "unknown" && hasComponentCapability(context)) {
3736
+ result.warnings.push({
3737
+ type: "export-mismatch",
3738
+ message: 'package.json exports missing "./client" entry point; could not determine whether any component is interactive (build the plugin before validating)',
3739
+ location: packageJsonPath,
3740
+ suggestion: 'If any component declares an interactive phase, add "./client": "./dist/index.client.js"'
3741
+ });
3742
+ }
3743
+ }
3744
+ if (!packageJson.exports["./tools"] && needsToolsEntry(context.manifest)) {
3745
+ result.errors.push({
2323
3746
  type: "export-mismatch",
2324
- message: 'package.json exports missing "./client" entry point',
3747
+ message: 'package.json exports missing "./tools" entry point, but the plugin declares tools capabilities (validators, commands, agentkit, setup, or devOnly actions)',
2325
3748
  location: packageJsonPath,
2326
- suggestion: 'Add "./client": "./dist/index.client.js" to exports. The client bundle provides components for hydration and client-side contexts. Build with: vite build (client) + vite build --ssr (server)'
3749
+ suggestion: 'Add "./tools": "./dist/tools.js" to exports and re-export those handlers from lib/tools.ts'
2327
3750
  });
2328
3751
  }
2329
3752
  if (context.manifest.contracts) {
@@ -2365,8 +3788,8 @@ async function validatePackageJson(context, result) {
2365
3788
  suggestion: 'Add "./plugin.yaml": "./plugin.yaml" to exports field'
2366
3789
  });
2367
3790
  }
2368
- const agentKitDir = path.join(context.pluginPath, "agent-kit");
2369
- if (fs.existsSync(agentKitDir) && fs.statSync(agentKitDir).isDirectory()) {
3791
+ const agentKitDir = path$1.join(context.pluginPath, "agent-kit");
3792
+ if (fs$1.existsSync(agentKitDir) && fs$1.statSync(agentKitDir).isDirectory()) {
2370
3793
  const filesArray = packageJson.files;
2371
3794
  if (!filesArray || !filesArray.includes("agent-kit")) {
2372
3795
  result.warnings.push({
@@ -2390,7 +3813,7 @@ function isBareFunctionExport(exportName, context) {
2390
3813
  if (!sourcePath) return false;
2391
3814
  let sourceCode;
2392
3815
  try {
2393
- sourceCode = fs.readFileSync(sourcePath, "utf-8");
3816
+ sourceCode = fs$1.readFileSync(sourcePath, "utf-8");
2394
3817
  } catch {
2395
3818
  return false;
2396
3819
  }
@@ -2418,8 +3841,8 @@ function resolveModulePathWithJsToTs(basePath) {
2418
3841
  }
2419
3842
  function resolveExportSourceFile(exportName, context) {
2420
3843
  const modulePath = context.manifest.module || "index";
2421
- const entryBase = path.join(context.pluginPath, modulePath);
2422
- const libEntryBase = path.join(context.pluginPath, "lib", modulePath);
3844
+ const entryBase = path$1.join(context.pluginPath, modulePath);
3845
+ const libEntryBase = path$1.join(context.pluginPath, "lib", modulePath);
2423
3846
  const sourceEntry = resolveModulePath(entryBase) || resolveModulePath(libEntryBase);
2424
3847
  if (!sourceEntry || !sourceEntry.endsWith(".ts")) return void 0;
2425
3848
  return followExportChain(exportName, sourceEntry);
@@ -2427,7 +3850,7 @@ function resolveExportSourceFile(exportName, context) {
2427
3850
  function followExportChain(exportName, filePath) {
2428
3851
  let sourceCode;
2429
3852
  try {
2430
- sourceCode = fs.readFileSync(filePath, "utf-8");
3853
+ sourceCode = fs$1.readFileSync(filePath, "utf-8");
2431
3854
  } catch {
2432
3855
  return void 0;
2433
3856
  }
@@ -2450,7 +3873,7 @@ function followExportChain(exportName, filePath) {
2450
3873
  if (u.isNamedExports(statement.exportClause)) {
2451
3874
  for (const element of statement.exportClause.elements) {
2452
3875
  if (element.name.text === exportName) {
2453
- const resolvedBase = path.resolve(path.dirname(filePath), moduleSpec);
3876
+ const resolvedBase = path$1.resolve(path$1.dirname(filePath), moduleSpec);
2454
3877
  return resolveModulePathWithJsToTs(resolvedBase);
2455
3878
  }
2456
3879
  }
@@ -2467,7 +3890,7 @@ function followExportChain(exportName, filePath) {
2467
3890
  }
2468
3891
  for (const moduleSpec of starReexportModules) {
2469
3892
  if (!moduleSpec.startsWith(".")) continue;
2470
- const resolvedBase = path.resolve(path.dirname(filePath), moduleSpec);
3893
+ const resolvedBase = path$1.resolve(path$1.dirname(filePath), moduleSpec);
2471
3894
  const resolved = resolveModulePathWithJsToTs(resolvedBase);
2472
3895
  if (!resolved) continue;
2473
3896
  const found = followExportChain(exportName, resolved);
@@ -2484,11 +3907,11 @@ async function validateDynamicContracts(context, result) {
2484
3907
  if (config.generator) {
2485
3908
  const isFilePath = config.generator.startsWith("./") || config.generator.startsWith("/") || config.generator.includes(".ts") || config.generator.includes(".js");
2486
3909
  if (isFilePath) {
2487
- const generatorPath = path.join(context.pluginPath, config.generator);
3910
+ const generatorPath = path$1.join(context.pluginPath, config.generator);
2488
3911
  const possibleExtensions = ["", ".ts", ".js", "/index.ts", "/index.js"];
2489
3912
  let found = false;
2490
3913
  for (const ext of possibleExtensions) {
2491
- if (fs.existsSync(generatorPath + ext)) {
3914
+ if (fs$1.existsSync(generatorPath + ext)) {
2492
3915
  found = true;
2493
3916
  break;
2494
3917
  }
@@ -2513,11 +3936,11 @@ async function validateDynamicContracts(context, result) {
2513
3936
  if (config.component) {
2514
3937
  const isFilePath = config.component.startsWith("./") || config.component.startsWith("/") || config.component.includes(".ts") || config.component.includes(".js");
2515
3938
  if (isFilePath) {
2516
- const componentPath = path.join(context.pluginPath, config.component);
3939
+ const componentPath = path$1.join(context.pluginPath, config.component);
2517
3940
  const possibleExtensions = ["", ".ts", ".js", "/index.ts", "/index.js"];
2518
3941
  let found = false;
2519
3942
  for (const ext of possibleExtensions) {
2520
- if (fs.existsSync(componentPath + ext)) {
3943
+ if (fs$1.existsSync(componentPath + ext)) {
2521
3944
  found = true;
2522
3945
  break;
2523
3946
  }
@@ -2838,12 +4261,12 @@ function checkRefElementTypes(jayHtml, file) {
2838
4261
  return warnings;
2839
4262
  }
2840
4263
  function checkPageComponentExport(jayHtmlPath) {
2841
- const dirname = path.dirname(jayHtmlPath);
2842
- const compPath = path.join(dirname, "page.ts");
2843
- if (!fs.existsSync(compPath)) return null;
4264
+ const dirname = path$1.dirname(jayHtmlPath);
4265
+ const compPath = path$1.join(dirname, "page.ts");
4266
+ if (!fs$1.existsSync(compPath)) return null;
2844
4267
  let content;
2845
4268
  try {
2846
- content = fs.readFileSync(compPath, "utf-8");
4269
+ content = fs$1.readFileSync(compPath, "utf-8");
2847
4270
  } catch {
2848
4271
  return null;
2849
4272
  }
@@ -2854,7 +4277,7 @@ function checkPageComponentExport(jayHtmlPath) {
2854
4277
  new RegExp(`export\\s+(?:const|let|var)\\s+${exportName}\\b`)
2855
4278
  ];
2856
4279
  if (patterns.some((p) => p.test(content))) return null;
2857
- return `${path.relative(dirname, compPath)} exists but does not export "${exportName}". Remove the file or add the export.`;
4280
+ return `${path$1.relative(dirname, compPath)} exists but does not export "${exportName}". Remove the file or add the export.`;
2858
4281
  }
2859
4282
  const DOCUMENT_ACCESS_PATTERNS = [
2860
4283
  /document\.getElementById\b/,
@@ -2867,15 +4290,15 @@ const DOCUMENT_ACCESS_PATTERNS = [
2867
4290
  ];
2868
4291
  const DOM_SUPPRESS_COMMENT = "jay-dom: allow";
2869
4292
  function checkDirectDocumentAccess(jayHtmlPath) {
2870
- const dirname = path.dirname(jayHtmlPath);
2871
- const basename = path.basename(jayHtmlPath, JAY_EXTENSION);
2872
- const candidates = [path.join(dirname, `${basename}.ts`), path.join(dirname, "page.ts")];
2873
- const compPath = candidates.find((p) => fs.existsSync(p));
4293
+ const dirname = path$1.dirname(jayHtmlPath);
4294
+ const basename = path$1.basename(jayHtmlPath, JAY_EXTENSION);
4295
+ const candidates = [path$1.join(dirname, `${basename}.ts`), path$1.join(dirname, "page.ts")];
4296
+ const compPath = candidates.find((p) => fs$1.existsSync(p));
2874
4297
  if (!compPath) return [];
2875
- const compName = path.basename(compPath);
4298
+ const compName = path$1.basename(compPath);
2876
4299
  let content;
2877
4300
  try {
2878
- content = fs.readFileSync(compPath, "utf-8");
4301
+ content = fs$1.readFileSync(compPath, "utf-8");
2879
4302
  } catch {
2880
4303
  return [];
2881
4304
  }
@@ -2898,8 +4321,8 @@ function checkDirectDocumentAccess(jayHtmlPath) {
2898
4321
  }
2899
4322
  const PARSE_PARAM = /^\[(\[)?(\.\.\.)?([^\]]+)\]?\]$/;
2900
4323
  function extractRouteParams(filePath, pagesBase) {
2901
- const relative = path.relative(pagesBase, filePath);
2902
- const segments = relative.split(path.sep);
4324
+ const relative = path$1.relative(pagesBase, filePath);
4325
+ const segments = relative.split(path$1.sep);
2903
4326
  const params = /* @__PURE__ */ new Set();
2904
4327
  for (const segment of segments) {
2905
4328
  const match = PARSE_PARAM.exec(segment);
@@ -3109,7 +4532,7 @@ function resolveLinkedTags(tags, contractDir) {
3109
4532
  }
3110
4533
  function resolveContractLinks(contract, contractPath) {
3111
4534
  if (!contractPath) return contract;
3112
- const contractDir = path.dirname(contractPath);
4535
+ const contractDir = path$1.dirname(contractPath);
3113
4536
  return { ...contract, tags: resolveLinkedTags(contract.tags, contractDir) };
3114
4537
  }
3115
4538
  async function runPluginValidators(projectRoot, parsedFiles, errors, warnings) {
@@ -3123,10 +4546,10 @@ async function runPluginValidators(projectRoot, parsedFiles, errors, warnings) {
3123
4546
  try {
3124
4547
  let handlerModule;
3125
4548
  if (plugin.isLocal) {
3126
- const handlerPath = path.resolve(plugin.pluginPath, validatorDef.handler);
4549
+ const handlerPath = path$1.resolve(plugin.pluginPath, validatorDef.handler);
3127
4550
  handlerModule = await import(handlerPath);
3128
4551
  } else {
3129
- handlerModule = await import(plugin.packageName);
4552
+ handlerModule = await import(`${plugin.packageName}/tools`);
3130
4553
  }
3131
4554
  validatorFn = plugin.isLocal ? handlerModule.validate ?? handlerModule.default : handlerModule[validatorDef.handler];
3132
4555
  if (typeof validatorFn !== "function") {
@@ -3151,8 +4574,8 @@ async function runPluginValidators(projectRoot, parsedFiles, errors, warnings) {
3151
4574
  }
3152
4575
  loadedValidators.push(source);
3153
4576
  for (const { relativePath, parsed } of parsedFiles) {
3154
- const pageContractPath = parsed.contractRef ? path.resolve(
3155
- path.dirname(path.resolve(projectRoot, relativePath)),
4577
+ const pageContractPath = parsed.contractRef ? path$1.resolve(
4578
+ path$1.dirname(path$1.resolve(projectRoot, relativePath)),
3156
4579
  parsed.contractRef
3157
4580
  ) : void 0;
3158
4581
  const resolvedPageContract = parsed.contract ? resolveContractLinks(parsed.contract, pageContractPath) : void 0;
@@ -3196,7 +4619,8 @@ async function runPluginValidators(projectRoot, parsedFiles, errors, warnings) {
3196
4619
  providedHeadTags
3197
4620
  };
3198
4621
  }),
3199
- projectRoot
4622
+ projectRoot,
4623
+ validationOverrides: parsed.validationOverrides
3200
4624
  };
3201
4625
  try {
3202
4626
  const findings = await validatorFn(ctx);
@@ -3232,11 +4656,11 @@ async function runPluginValidators(projectRoot, parsedFiles, errors, warnings) {
3232
4656
  return loadedValidators;
3233
4657
  }
3234
4658
  async function validateJayFiles(options = {}) {
3235
- const config = loadConfig();
3236
- const resolvedConfig = getConfigWithDefaults(config);
3237
4659
  const projectRoot = options.projectRoot ?? process.cwd();
3238
- const scanDir = options.path ? path.resolve(options.path) : path.resolve(resolvedConfig.devServer.pagesBase);
3239
- const componentsDir = path.resolve(resolvedConfig.devServer.componentsBase);
4660
+ const config = loadConfig(projectRoot);
4661
+ const resolvedConfig = getConfigWithDefaults(config);
4662
+ const scanDir = options.path ? path$1.resolve(options.path) : path$1.resolve(resolvedConfig.devServer.pagesBase);
4663
+ const componentsDir = path$1.resolve(resolvedConfig.devServer.componentsBase);
3240
4664
  const errors = [];
3241
4665
  const warnings = [];
3242
4666
  const coverage = [];
@@ -3255,10 +4679,10 @@ async function validateJayFiles(options = {}) {
3255
4679
  `));
3256
4680
  }
3257
4681
  for (const contractFile of contractFiles) {
3258
- const relativePath = path.relative(projectRoot, contractFile);
4682
+ const relativePath = path$1.relative(projectRoot, contractFile);
3259
4683
  try {
3260
4684
  const content = await promises.readFile(contractFile, "utf-8");
3261
- const result = parseContract(content, path.basename(contractFile));
4685
+ const result = parseContract(content, path$1.basename(contractFile));
3262
4686
  if (result.validations.length > 0) {
3263
4687
  for (const validation of result.validations) {
3264
4688
  errors.push({
@@ -3285,9 +4709,9 @@ async function validateJayFiles(options = {}) {
3285
4709
  }
3286
4710
  }
3287
4711
  for (const jayFile of jayHtmlFiles) {
3288
- const relativePath = path.relative(projectRoot, jayFile);
3289
- const filename = path.basename(jayFile.replace(JAY_EXTENSION, ""));
3290
- const dirname = path.dirname(jayFile);
4712
+ const relativePath = path$1.relative(projectRoot, jayFile);
4713
+ const filename = path$1.basename(jayFile.replace(JAY_EXTENSION, ""));
4714
+ const dirname = path$1.dirname(jayFile);
3291
4715
  try {
3292
4716
  const content = await promises.readFile(jayFile, "utf-8");
3293
4717
  const parsedFile = await parseJayFile(
@@ -3313,9 +4737,10 @@ async function validateJayFiles(options = {}) {
3313
4737
  }
3314
4738
  parsedFiles.push({ relativePath, parsed: parsedFile.val });
3315
4739
  if (content.includes("application/jay-params")) {
3316
- warnings.push({
4740
+ errors.push({
3317
4741
  file: relativePath,
3318
- message: '<script type="application/jay-params"> is deprecated. Move the values into the YAML body of the headless component that uses them. See agent-kit/developer/routing.md for details.'
4742
+ message: '<script type="application/jay-params"> is no longer supported and is ignored. Move the values into the YAML body of the headless component that uses them. See agent-kit/developer/routing.md for details.',
4743
+ stage: "parse"
3319
4744
  });
3320
4745
  }
3321
4746
  const pageExportError = checkPageComponentExport(jayFile);
@@ -3346,17 +4771,34 @@ async function validateJayFiles(options = {}) {
3346
4771
  for (const msg of refTypeErrors) {
3347
4772
  errors.push({ file: relativePath, message: msg, stage: "generate" });
3348
4773
  }
3349
- const headlessPropWarnings = checkHeadlessInstanceProps(parsedFile.val, relativePath);
3350
- for (const msg of headlessPropWarnings) {
3351
- warnings.push({ file: relativePath, message: msg });
4774
+ const headlessPropResults = checkHeadlessInstanceProps(parsedFile.val, relativePath);
4775
+ for (const msg of headlessPropResults) {
4776
+ if (msg.includes("is missing required prop") || msg.includes("source phase must be")) {
4777
+ errors.push({ file: relativePath, message: msg, stage: "generate" });
4778
+ } else {
4779
+ warnings.push({ file: relativePath, message: msg });
4780
+ }
3352
4781
  }
3353
4782
  const fileCoverage = analyzeTagCoverage(parsedFile.val, relativePath);
3354
4783
  if (fileCoverage) {
3355
4784
  coverage.push(fileCoverage);
4785
+ const allowedUnused = parsedFile.val.validationOverrides?.["jay-stack"]?.["allow-unused-tags"];
4786
+ const allowedSet = new Set(Array.isArray(allowedUnused) ? allowedUnused : []);
4787
+ for (const contract of fileCoverage.contracts) {
4788
+ for (const tag of contract.requiredUnusedTags) {
4789
+ const qualifiedTag = contract.key ? `${contract.key}.${tag}` : tag;
4790
+ if (allowedSet.has(qualifiedTag) || allowedSet.has(tag)) continue;
4791
+ const label = contract.key ? `${contract.key} (${contract.contractName})` : contract.contractName;
4792
+ warnings.push({
4793
+ file: relativePath,
4794
+ message: `Required tag "${tag}" from contract "${label}" is not used in the template. Suppress with jay-stack: { allow-unused-tags: ["${qualifiedTag}"] } in <script type="application/jay-validations">. See agent-kit/designer/validation-guide.md`
4795
+ });
4796
+ }
4797
+ }
3356
4798
  }
3357
4799
  const generatedFile = generateElementFile(
3358
4800
  parsedFile.val,
3359
- RuntimeMode.MainTrusted,
4801
+ RuntimeMode$1.MainTrusted,
3360
4802
  GenerateTarget.jay
3361
4803
  );
3362
4804
  if (generatedFile.validations.length > 0) {
@@ -3394,8 +4836,8 @@ async function validateJayFiles(options = {}) {
3394
4836
  }
3395
4837
  }
3396
4838
  }
3397
- const robotsTxtPath = path.resolve(projectRoot, "public/robots.txt");
3398
- if (!fs.existsSync(robotsTxtPath)) {
4839
+ const robotsTxtPath = path$1.resolve(projectRoot, "public/robots.txt");
4840
+ if (!fs$1.existsSync(robotsTxtPath)) {
3399
4841
  warnings.push({
3400
4842
  file: "public/robots.txt",
3401
4843
  message: "public/robots.txt not found — search engines may crawl pages you don't intend to expose.",
@@ -3406,7 +4848,7 @@ async function validateJayFiles(options = {}) {
3406
4848
  warnings.push({
3407
4849
  file: ".jay",
3408
4850
  message: "site.baseUrl not configured — sitemap.xml will not be generated in production.",
3409
- suggestion: "Add to .jay config: site:\n baseUrl: https://your-domain.com"
4851
+ suggestion: "Add to .jay config:\n site:\n baseUrl: https://your-domain.com"
3410
4852
  });
3411
4853
  }
3412
4854
  const pluginValidators = await runPluginValidators(projectRoot, parsedFiles, errors, warnings);
@@ -3491,7 +4933,7 @@ function printJayValidationResult(result, options) {
3491
4933
  }
3492
4934
  }
3493
4935
  }
3494
- if (result.coverage.length > 0) {
4936
+ if (options.verbose && result.coverage.length > 0) {
3495
4937
  logger.important("");
3496
4938
  logger.important(chalk.bold("šŸ“¦ Tag Coverage"));
3497
4939
  for (const fileCov of result.coverage) {
@@ -3506,23 +4948,26 @@ function printJayValidationResult(result, options) {
3506
4948
  chalk.gray(` Unused: ${contract.unusedTags.join(", ")}`)
3507
4949
  );
3508
4950
  }
3509
- if (contract.requiredUnusedTags.length > 0) {
3510
- logger.important(
3511
- chalk.yellow(
3512
- ` ⚠ Required unused: ${contract.requiredUnusedTags.join(", ")}`
3513
- )
3514
- );
3515
- }
3516
4951
  }
3517
4952
  }
3518
4953
  }
3519
4954
  logger.important("");
3520
- if (result.valid) {
4955
+ if (result.valid && result.warnings.length === 0) {
3521
4956
  logger.important(chalk.green("Validation passed."));
4957
+ } else if (result.valid) {
4958
+ logger.important(
4959
+ chalk.yellow(
4960
+ `Validation passed with ${result.warnings.length} warning(s). Warnings must be fixed or explicitly suppressed — do not ignore them.`
4961
+ )
4962
+ );
3522
4963
  } else {
3523
- logger.important(chalk.red(`Validation failed — ${result.errors.length} error(s).`));
4964
+ logger.important(
4965
+ chalk.red(
4966
+ `Validation failed — ${result.errors.length} error(s)` + (result.warnings.length > 0 ? `, ${result.warnings.length} warning(s). Errors must be fixed. Warnings must be fixed or explicitly suppressed.` : ".")
4967
+ )
4968
+ );
3524
4969
  }
3525
- const totalIssues = result.errors.length + result.warnings.length + result.coverage.length;
4970
+ const totalIssues = result.errors.length + result.warnings.length;
3526
4971
  if (totalIssues > 0) {
3527
4972
  logger.important(
3528
4973
  chalk.gray(
@@ -3626,7 +5071,7 @@ async function runAgentKit(options) {
3626
5071
  }
3627
5072
  }
3628
5073
  async function runMaterialize(projectRoot, options, defaultOutputRelative, keepViteAlive = false) {
3629
- const outputDir = options.output ?? path$1.join(projectRoot, defaultOutputRelative);
5074
+ const outputDir = options.output ?? path__default.join(projectRoot, defaultOutputRelative);
3630
5075
  let viteServer;
3631
5076
  let initErrors = /* @__PURE__ */ new Map();
3632
5077
  try {
@@ -3692,81 +5137,81 @@ Materialized ${totalContracts} contracts`));
3692
5137
  return { initErrors };
3693
5138
  }
3694
5139
  async function ensureAgentKitDocs(projectRoot, _force, mode) {
3695
- const agentKitDir = path$1.join(projectRoot, "agent-kit");
3696
- const thisDir = path$1.dirname(fileURLToPath(import.meta.url));
3697
- const templateDir = path$1.resolve(thisDir, "..", "agent-kit-template");
5140
+ const agentKitDir = path__default.join(projectRoot, "agent-kit");
5141
+ const thisDir = path__default.dirname(fileURLToPath(import.meta.url));
5142
+ const templateDir = path__default.resolve(thisDir, "..", "agent-kit-template");
3698
5143
  const roles = mode && ALL_ROLES.includes(mode) ? [mode] : ALL_ROLES;
3699
5144
  for (const role of roles) {
3700
- const roleTemplateDir = path$1.join(templateDir, role);
3701
- const roleOutputDir = path$1.join(agentKitDir, role);
5145
+ const roleTemplateDir = path__default.join(templateDir, role);
5146
+ const roleOutputDir = path__default.join(agentKitDir, role);
3702
5147
  let files;
3703
5148
  try {
3704
- files = (await fs$1.readdir(roleTemplateDir)).filter((f) => f.endsWith(".md"));
5149
+ files = (await fs$2.readdir(roleTemplateDir)).filter((f) => f.endsWith(".md"));
3705
5150
  } catch {
3706
5151
  continue;
3707
5152
  }
3708
- await fs$1.mkdir(roleOutputDir, { recursive: true });
5153
+ await fs$2.mkdir(roleOutputDir, { recursive: true });
3709
5154
  for (const filename of files) {
3710
- await fs$1.copyFile(
3711
- path$1.join(roleTemplateDir, filename),
3712
- path$1.join(roleOutputDir, filename)
5155
+ await fs$2.copyFile(
5156
+ path__default.join(roleTemplateDir, filename),
5157
+ path__default.join(roleOutputDir, filename)
3713
5158
  );
3714
5159
  getLogger().info(chalk.gray(` Created agent-kit/${role}/${filename}`));
3715
5160
  }
3716
5161
  }
3717
- const topLevelFiles = (await fs$1.readdir(templateDir)).filter((f) => f.endsWith(".md"));
5162
+ const topLevelFiles = (await fs$2.readdir(templateDir)).filter((f) => f.endsWith(".md"));
3718
5163
  for (const filename of topLevelFiles) {
3719
- await fs$1.copyFile(path$1.join(templateDir, filename), path$1.join(agentKitDir, filename));
5164
+ await fs$2.copyFile(path__default.join(templateDir, filename), path__default.join(agentKitDir, filename));
3720
5165
  getLogger().info(chalk.gray(` Created agent-kit/${filename}`));
3721
5166
  }
3722
5167
  const sharedDirs = ["contracts"];
3723
5168
  for (const dir of sharedDirs) {
3724
- const srcDir = path$1.join(templateDir, dir);
3725
- if (!fsSync.existsSync(srcDir)) continue;
3726
- await copyDirRecursive(srcDir, path$1.join(agentKitDir, dir));
5169
+ const srcDir = path__default.join(templateDir, dir);
5170
+ if (!fs__default.existsSync(srcDir)) continue;
5171
+ await copyDirRecursive(srcDir, path__default.join(agentKitDir, dir));
3727
5172
  getLogger().info(chalk.gray(` Created agent-kit/${dir}/`));
3728
5173
  }
3729
5174
  }
3730
5175
  async function copyDirRecursive(src, dest) {
3731
- await fs$1.mkdir(dest, { recursive: true });
3732
- const entries = await fs$1.readdir(src, { withFileTypes: true });
5176
+ await fs$2.mkdir(dest, { recursive: true });
5177
+ const entries = await fs$2.readdir(src, { withFileTypes: true });
3733
5178
  for (const entry of entries) {
3734
- const srcPath = path$1.join(src, entry.name);
3735
- const destPath = path$1.join(dest, entry.name);
5179
+ const srcPath = path__default.join(src, entry.name);
5180
+ const destPath = path__default.join(dest, entry.name);
3736
5181
  if (entry.isDirectory()) {
3737
5182
  await copyDirRecursive(srcPath, destPath);
3738
5183
  } else if (entry.name.endsWith(".md")) {
3739
- await fs$1.copyFile(srcPath, destPath);
5184
+ await fs$2.copyFile(srcPath, destPath);
3740
5185
  }
3741
5186
  }
3742
5187
  }
3743
5188
  async function mergePluginAgentKitGuides(projectRoot, mode) {
3744
5189
  const plugins = await scanPlugins({ projectRoot, includeDevDeps: true });
3745
- const agentKitDir = path$1.join(projectRoot, "agent-kit");
5190
+ const agentKitDir = path__default.join(projectRoot, "agent-kit");
3746
5191
  const roles = mode && ALL_ROLES.includes(mode) ? [mode] : ALL_ROLES;
3747
5192
  const copiedPerRole = /* @__PURE__ */ new Map();
3748
5193
  for (const [, plugin] of plugins) {
3749
- const pluginAgentKitDir = path$1.join(plugin.pluginPath, "agent-kit");
3750
- if (!fsSync.existsSync(pluginAgentKitDir)) continue;
5194
+ const pluginAgentKitDir = path__default.join(plugin.pluginPath, "agent-kit");
5195
+ if (!fs__default.existsSync(pluginAgentKitDir)) continue;
3751
5196
  for (const role of roles) {
3752
- const roleSourceDir = path$1.join(pluginAgentKitDir, role);
5197
+ const roleSourceDir = path__default.join(pluginAgentKitDir, role);
3753
5198
  let files;
3754
5199
  try {
3755
- files = (await fs$1.readdir(roleSourceDir)).filter(
5200
+ files = (await fs$2.readdir(roleSourceDir)).filter(
3756
5201
  (f) => f.endsWith(".md") && f !== "INSTRUCTIONS.md"
3757
5202
  );
3758
5203
  } catch {
3759
5204
  continue;
3760
5205
  }
3761
5206
  if (files.length === 0) continue;
3762
- const roleOutputDir = path$1.join(agentKitDir, role);
3763
- await fs$1.mkdir(roleOutputDir, { recursive: true });
5207
+ const roleOutputDir = path__default.join(agentKitDir, role);
5208
+ await fs$2.mkdir(roleOutputDir, { recursive: true });
3764
5209
  for (const filename of files) {
3765
- const sourcePath = path$1.join(roleSourceDir, filename);
3766
- await fs$1.copyFile(sourcePath, path$1.join(roleOutputDir, filename));
5210
+ const sourcePath = path__default.join(roleSourceDir, filename);
5211
+ await fs$2.copyFile(sourcePath, path__default.join(roleOutputDir, filename));
3767
5212
  let description = "";
3768
5213
  try {
3769
- const content = await fs$1.readFile(sourcePath, "utf-8");
5214
+ const content = await fs$2.readFile(sourcePath, "utf-8");
3770
5215
  const lines = content.split("\n");
3771
5216
  let pastHeading = false;
3772
5217
  for (const line of lines) {
@@ -3792,8 +5237,8 @@ async function mergePluginAgentKitGuides(projectRoot, mode) {
3792
5237
  }
3793
5238
  }
3794
5239
  for (const [role, entries] of copiedPerRole) {
3795
- const instructionsPath = path$1.join(agentKitDir, role, "INSTRUCTIONS.md");
3796
- if (!fsSync.existsSync(instructionsPath)) continue;
5240
+ const instructionsPath = path__default.join(agentKitDir, role, "INSTRUCTIONS.md");
5241
+ if (!fs__default.existsSync(instructionsPath)) continue;
3797
5242
  const lines = [
3798
5243
  "",
3799
5244
  "## Plugin-Contributed Guides",
@@ -3805,11 +5250,10 @@ async function mergePluginAgentKitGuides(projectRoot, mode) {
3805
5250
  lines.push(`| [${filename}](${filename}) | ${pluginName} | ${description} |`);
3806
5251
  }
3807
5252
  lines.push("");
3808
- await fs$1.appendFile(instructionsPath, lines.join("\n"));
5253
+ await fs$2.appendFile(instructionsPath, lines.join("\n"));
3809
5254
  }
3810
5255
  }
3811
5256
  async function generatePluginAgentKit(projectRoot, options, initErrors, viteServer) {
3812
- const { discoverPluginsWithAgentKit, executePluginAgentKit } = await import("@jay-framework/stack-server-runtime");
3813
5257
  const plugins = await discoverPluginsWithAgentKit({
3814
5258
  projectRoot,
3815
5259
  verbose: options.verbose,
@@ -3882,7 +5326,6 @@ async function runAction(actionRef, options, projectRoot, initializeServices) {
3882
5326
  }
3883
5327
  viteServer = await createViteForCli({ projectRoot });
3884
5328
  await initializeServices(projectRoot, viteServer);
3885
- const { discoverAndRegisterActions, discoverAllPluginActions, ActionRegistry } = await import("@jay-framework/stack-server-runtime");
3886
5329
  const registry = new ActionRegistry();
3887
5330
  await discoverAndRegisterActions({
3888
5331
  projectRoot,
@@ -3994,8 +5437,8 @@ async function runParams(contractRef, options, projectRoot, initializeServices)
3994
5437
  );
3995
5438
  process.exit(1);
3996
5439
  }
3997
- const { resolveServices } = await import("@jay-framework/stack-server-runtime");
3998
- const resolvedServices = resolveServices(component.services || []);
5440
+ const { resolveServices: resolveServices2 } = await import("@jay-framework/stack-server-runtime");
5441
+ const resolvedServices = resolveServices2(component.services || []);
3999
5442
  const paramsGenerator = component.loadParams(resolvedServices);
4000
5443
  let total = 0;
4001
5444
  for await (const batch of paramsGenerator) {
@@ -4089,7 +5532,7 @@ async function runSetup(pluginFilter, options, projectRoot) {
4089
5532
  try {
4090
5533
  const logger = getLogger();
4091
5534
  const jayConfig = loadConfig();
4092
- const configDir = path$1.resolve(projectRoot, jayConfig.devServer?.configBase || "./config");
5535
+ const configDir = path__default.resolve(projectRoot, jayConfig.devServer?.configBase || "./config");
4093
5536
  logger.important(chalk.bold("\nšŸ”§ Setting up plugins...\n"));
4094
5537
  if (options.verbose) {
4095
5538
  logger.info("Starting Vite for TypeScript support...");
@@ -4121,7 +5564,7 @@ async function runSetup(pluginFilter, options, projectRoot) {
4121
5564
  const interactive = options.interactive === true;
4122
5565
  let answersMap;
4123
5566
  if (options.answers) {
4124
- answersMap = YAML.parse(fsSync.readFileSync(options.answers, "utf-8")) || {};
5567
+ answersMap = YAML.parse(fs__default.readFileSync(options.answers, "utf-8")) || {};
4125
5568
  }
4126
5569
  let configured = 0;
4127
5570
  let needsConfig = 0;
@@ -4248,12 +5691,12 @@ async function initPlugin(pluginName, allPluginsWithInit, viteServer, logger) {
4248
5691
  }
4249
5692
  async function runProjectInit(projectRoot, viteServer) {
4250
5693
  try {
4251
- const initPathTs = path$1.join(projectRoot, "src", "init.ts");
4252
- const initPathJs = path$1.join(projectRoot, "src", "init.js");
5694
+ const initPathTs = path__default.join(projectRoot, "src", "init.ts");
5695
+ const initPathJs = path__default.join(projectRoot, "src", "init.js");
4253
5696
  let initModule;
4254
- if (fsSync.existsSync(initPathTs) && viteServer) {
5697
+ if (fs__default.existsSync(initPathTs) && viteServer) {
4255
5698
  initModule = await viteServer.ssrLoadModule(initPathTs);
4256
- } else if (fsSync.existsSync(initPathJs)) {
5699
+ } else if (fs__default.existsSync(initPathJs)) {
4257
5700
  initModule = await import(initPathJs);
4258
5701
  }
4259
5702
  if (initModule?.init?._serverInit) {
@@ -4266,12 +5709,6 @@ async function runProjectInit(projectRoot, viteServer) {
4266
5709
  async function runCommand(commandRef, rawArgs, options, projectRoot, initializeServices) {
4267
5710
  let viteServer;
4268
5711
  try {
4269
- const {
4270
- discoverPluginCommands,
4271
- commandSchemaToFlags,
4272
- parseInputFromFlags,
4273
- executePluginCommand
4274
- } = await import("@jay-framework/stack-server-runtime");
4275
5712
  const commands = await discoverPluginCommands({
4276
5713
  projectRoot,
4277
5714
  verbose: options.verbose
@@ -4327,21 +5764,19 @@ async function runCommand(commandRef, rawArgs, options, projectRoot, initializeS
4327
5764
  }
4328
5765
  viteServer = await createViteForCli({ projectRoot });
4329
5766
  await initializeServices(projectRoot, viteServer);
4330
- const { registerService } = await import("@jay-framework/stack-server-runtime");
4331
- const { CONSOLE_CONTEXT } = await import("@jay-framework/fullstack-component");
4332
5767
  const jayConfig = loadConfig();
4333
- const publicFolder = path$1.resolve(
5768
+ const publicFolder = path__default.resolve(
4334
5769
  projectRoot,
4335
5770
  jayConfig.devServer?.publicFolder || "public"
4336
5771
  );
4337
5772
  const version = await resolveVersionFromPackageJson(projectRoot);
4338
- const buildRoot = path$1.resolve(projectRoot, `build/v${version}`);
5773
+ const buildRoot = path__default.resolve(projectRoot, `build/v${version}`);
4339
5774
  registerService(CONSOLE_CONTEXT, {
4340
5775
  projectRoot,
4341
5776
  publicFolder,
4342
5777
  build: {
4343
- frontend: path$1.join(buildRoot, "frontend"),
4344
- backend: path$1.join(buildRoot, "backend")
5778
+ frontend: path__default.join(buildRoot, "frontend"),
5779
+ backend: path__default.join(buildRoot, "backend")
4345
5780
  },
4346
5781
  verbose: options.verbose ?? false,
4347
5782
  log: (msg) => getLogger().important(msg),
@@ -4515,9 +5950,9 @@ if (!process.argv.slice(2).length) {
4515
5950
  }
4516
5951
  export {
4517
5952
  getConfigWithDefaults,
4518
- listContracts2 as listContracts,
5953
+ listContracts,
4519
5954
  loadConfig,
4520
- materializeContracts2 as materializeContracts,
5955
+ materializeContracts,
4521
5956
  startDevServer,
4522
5957
  updateConfig
4523
5958
  };