@codemation/cli 0.0.14 → 0.0.16

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.
@@ -1,17 +1,17 @@
1
1
  import { createRequire } from "node:module";
2
2
  import process$1 from "node:process";
3
- import { AppConfigLoader, CodemationConsumerConfigLoader, CodemationFrontendAuthSnapshotFactory, CodemationPluginDiscovery, FrontendAppConfigJsonCodec, WorkflowDiscoveryPathSegmentsComputer, WorkflowModulePathFinder } from "@codemation/host/server";
4
- import { ApiPaths, AppContainerFactory, AppContainerLifecycle, ApplicationTokens, CodemationFrontendAuthSnapshotFactory as CodemationFrontendAuthSnapshotFactory$1, DatabaseMigrations, FrontendAppConfigJsonCodec as FrontendAppConfigJsonCodec$1, ListUserAccountsQuery, PrismaClient, UpsertLocalBootstrapUserCommand, WorkerRuntime } from "@codemation/host";
3
+ import { AppConfigLoader, CodemationConsumerConfigLoader, CodemationPluginDiscovery, WorkflowDiscoveryPathSegmentsComputer, WorkflowModulePathFinder } from "@codemation/host/server";
4
+ import { ApiPaths, AppContainerFactory, AppContainerLifecycle, ApplicationTokens, CodemationPluginPackageMetadata, DatabaseMigrations, ListUserAccountsQuery, PrismaClient, UpsertLocalBootstrapUserCommand, WorkerRuntime } from "@codemation/host";
5
5
  import { AppContainerFactory as AppContainerFactory$1, AppContainerLifecycle as AppContainerLifecycle$1, CodemationHonoApiApp, CodemationPluginListMerger, FrontendRuntime, ServerLoggerFactory, logLevelPolicyFactory } from "@codemation/host/next/server";
6
- import { randomUUID } from "node:crypto";
7
- import { access, copyFile, cp, mkdir, open, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
8
- import path from "node:path";
9
- import { fileURLToPath, pathToFileURL } from "node:url";
10
6
  import { execFile, spawn } from "node:child_process";
7
+ import path from "node:path";
11
8
  import { existsSync, readFileSync } from "node:fs";
12
9
  import { config, parse } from "dotenv";
13
10
  import { watch } from "chokidar";
11
+ import { randomUUID } from "node:crypto";
12
+ import { access, copyFile, cp, mkdir, open, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
14
13
  import ts from "typescript";
14
+ import { fileURLToPath } from "node:url";
15
15
  import { PrismaMigrationDeployer } from "@codemation/host/persistence";
16
16
  import boxen from "boxen";
17
17
  import chalk from "chalk";
@@ -25,52 +25,6 @@ import { setTimeout as setTimeout$1 } from "node:timers/promises";
25
25
  import { createServer as createServer$1 } from "node:net";
26
26
  import { Command } from "commander";
27
27
 
28
- //#region src/build/ConsumerBuildArtifactsPublisher.ts
29
- var ConsumerBuildArtifactsPublisher = class {
30
- async publish(snapshot, discoveredPlugins) {
31
- const pluginEntryPath = await this.writeDiscoveredPluginsOutput(snapshot, discoveredPlugins);
32
- return await this.writeBuildManifest(snapshot, pluginEntryPath);
33
- }
34
- async writeDiscoveredPluginsOutput(snapshot, discoveredPlugins) {
35
- const outputPath = path.resolve(snapshot.emitOutputRoot, "plugins.js");
36
- await mkdir(path.dirname(outputPath), { recursive: true });
37
- const outputLines = ["const codemationDiscoveredPlugins = [];", ""];
38
- discoveredPlugins.forEach((discoveredPlugin, index) => {
39
- const pluginFileUrl = pathToFileURL(path.resolve(discoveredPlugin.packageRoot, discoveredPlugin.manifest.entry)).href;
40
- const exportNameAccessor = discoveredPlugin.manifest.exportName ? `pluginModule${index}[${JSON.stringify(discoveredPlugin.manifest.exportName)}]` : `pluginModule${index}.default ?? pluginModule${index}.codemationPlugin`;
41
- outputLines.push(`const pluginModule${index} = await import(${JSON.stringify(pluginFileUrl)});`);
42
- outputLines.push(`const pluginValue${index} = ${exportNameAccessor};`);
43
- outputLines.push(`if (pluginValue${index} && typeof pluginValue${index}.register === "function") {`);
44
- outputLines.push(` codemationDiscoveredPlugins.push(pluginValue${index});`);
45
- outputLines.push(`} else if (typeof pluginValue${index} === "function" && pluginValue${index}.prototype && typeof pluginValue${index}.prototype.register === "function") {`);
46
- outputLines.push(` codemationDiscoveredPlugins.push(new pluginValue${index}());`);
47
- outputLines.push("}");
48
- outputLines.push("");
49
- });
50
- outputLines.push("export { codemationDiscoveredPlugins };");
51
- outputLines.push("export default codemationDiscoveredPlugins;");
52
- outputLines.push("");
53
- await writeFile(outputPath, outputLines.join("\n"), "utf8");
54
- return outputPath;
55
- }
56
- async writeBuildManifest(snapshot, pluginEntryPath) {
57
- const manifest = {
58
- buildVersion: snapshot.buildVersion,
59
- consumerRoot: snapshot.consumerRoot,
60
- entryPath: snapshot.outputEntryPath,
61
- manifestPath: snapshot.manifestPath,
62
- pluginEntryPath,
63
- workflowSourcePaths: snapshot.workflowSourcePaths
64
- };
65
- await mkdir(path.dirname(snapshot.manifestPath), { recursive: true });
66
- const temporaryManifestPath = `${snapshot.manifestPath}.${snapshot.buildVersion}.${randomUUID()}.tmp`;
67
- await writeFile(temporaryManifestPath, JSON.stringify(manifest, null, 2), "utf8");
68
- await rename(temporaryManifestPath, snapshot.manifestPath);
69
- return manifest;
70
- }
71
- };
72
-
73
- //#endregion
74
28
  //#region src/build/ConsumerBuildOptionsParser.ts
75
29
  var ConsumerBuildOptionsParser = class {
76
30
  parse(args) {
@@ -90,23 +44,18 @@ var ConsumerBuildOptionsParser = class {
90
44
  //#endregion
91
45
  //#region src/commands/BuildCommand.ts
92
46
  var BuildCommand = class {
93
- constructor(cliLogger, pathResolver, pluginDiscovery, artifactsPublisher, tsRuntime, outputBuilderLoader) {
47
+ constructor(cliLogger, pathResolver, consumerOutputBuilderFactory, tsRuntime) {
94
48
  this.cliLogger = cliLogger;
95
49
  this.pathResolver = pathResolver;
96
- this.pluginDiscovery = pluginDiscovery;
97
- this.artifactsPublisher = artifactsPublisher;
50
+ this.consumerOutputBuilderFactory = consumerOutputBuilderFactory;
98
51
  this.tsRuntime = tsRuntime;
99
- this.outputBuilderLoader = outputBuilderLoader;
100
52
  }
101
53
  async execute(consumerRoot, buildOptions) {
102
54
  const paths = await this.pathResolver.resolve(consumerRoot);
103
55
  this.tsRuntime.configure(paths.repoRoot);
104
- const snapshot = await this.outputBuilderLoader.create(paths.consumerRoot, buildOptions).ensureBuilt();
105
- const discoveredPlugins = await this.pluginDiscovery.discover(paths.consumerRoot);
106
- const manifest = await this.artifactsPublisher.publish(snapshot, discoveredPlugins);
56
+ const snapshot = await this.consumerOutputBuilderFactory.create(paths.consumerRoot, { buildOptions }).ensureBuilt();
107
57
  this.cliLogger.info(`Built consumer output: ${snapshot.outputEntryPath}`);
108
- this.cliLogger.info(`Discovered plugins: ${discoveredPlugins.length}`);
109
- this.cliLogger.info(`Published build: ${manifest.buildVersion}`);
58
+ this.cliLogger.info(`Workflow modules emitted: ${snapshot.workflowSourcePaths.length}`);
110
59
  }
111
60
  };
112
61
 
@@ -125,7 +74,7 @@ var DbMigrateCommand = class {
125
74
  //#region src/commands/DevCommand.ts
126
75
  var DevCommand = class {
127
76
  require = createRequire(import.meta.url);
128
- constructor(pathResolver, tsRuntime, devLockFactory, devSourceWatcherFactory, cliLogger, session, databaseMigrationsApplyService, devBootstrapSummaryFetcher, devCliBannerRenderer, devConsumerPublishBootstrap, consumerEnvDotenvFilePredicate, devTrackedProcessTreeKiller, nextHostConsumerServerCommandFactory, devApiRuntimeFactory, cliDevProxyServerFactory, devRebuildQueueFactory) {
77
+ constructor(pathResolver, tsRuntime, devLockFactory, devSourceWatcherFactory, cliLogger, session, databaseMigrationsApplyService, devBootstrapSummaryFetcher, devCliBannerRenderer, consumerEnvDotenvFilePredicate, devTrackedProcessTreeKiller, nextHostConsumerServerCommandFactory, devApiRuntimeFactory, cliDevProxyServerFactory, devRebuildQueueFactory) {
129
78
  this.pathResolver = pathResolver;
130
79
  this.tsRuntime = tsRuntime;
131
80
  this.devLockFactory = devLockFactory;
@@ -135,7 +84,6 @@ var DevCommand = class {
135
84
  this.databaseMigrationsApplyService = databaseMigrationsApplyService;
136
85
  this.devBootstrapSummaryFetcher = devBootstrapSummaryFetcher;
137
86
  this.devCliBannerRenderer = devCliBannerRenderer;
138
- this.devConsumerPublishBootstrap = devConsumerPublishBootstrap;
139
87
  this.consumerEnvDotenvFilePredicate = consumerEnvDotenvFilePredicate;
140
88
  this.devTrackedProcessTreeKiller = devTrackedProcessTreeKiller;
141
89
  this.nextHostConsumerServerCommandFactory = nextHostConsumerServerCommandFactory;
@@ -145,10 +93,10 @@ var DevCommand = class {
145
93
  }
146
94
  async execute(args) {
147
95
  const paths = await this.pathResolver.resolve(args.consumerRoot);
96
+ const commandName = args.commandName ?? "dev";
148
97
  this.devCliBannerRenderer.renderBrandHeader();
149
98
  this.tsRuntime.configure(paths.repoRoot);
150
- await this.databaseMigrationsApplyService.applyForConsumer(paths.consumerRoot);
151
- await this.devConsumerPublishBootstrap.ensurePublished(paths);
99
+ await this.databaseMigrationsApplyService.applyForConsumer(paths.consumerRoot, { configPath: args.configPathOverride });
152
100
  const devMode = this.resolveDevMode(args);
153
101
  const { nextPort, gatewayPort } = await this.session.sessionPorts.resolve({
154
102
  devMode,
@@ -160,24 +108,28 @@ var DevCommand = class {
160
108
  consumerRoot: paths.consumerRoot,
161
109
  nextPort: devMode === "watch-framework" ? nextPort : gatewayPort
162
110
  });
163
- const authSettings = await this.session.devAuthLoader.loadForConsumer(paths.consumerRoot);
111
+ const authSettings = await this.session.nextHostEdgeSeedLoader.loadForConsumer(paths.consumerRoot, { configPathOverride: args.configPathOverride });
164
112
  const watcher = this.devSourceWatcherFactory.create();
165
113
  const processState = this.createInitialProcessState();
166
114
  let proxyServer = null;
167
115
  try {
168
- const prepared = await this.prepareDevRuntime(paths, devMode, nextPort, gatewayPort, authSettings);
116
+ const prepared = await this.prepareDevRuntime(paths, devMode, nextPort, gatewayPort, authSettings, args.configPathOverride);
169
117
  const stopPromise = this.wireStopPromise(processState);
170
- const uiProxyBase = await this.startPackagedUiWhenNeeded(prepared, processState);
118
+ const uiProxyBase = await this.preparePackagedUiBaseUrlWhenNeeded(prepared, processState);
171
119
  proxyServer = await this.startProxyServer(prepared.gatewayPort, uiProxyBase);
172
120
  const gatewayBaseUrl = this.gatewayBaseHttpUrl(gatewayPort);
173
121
  await this.bootInitialRuntime(prepared, processState, proxyServer);
174
122
  await this.session.devHttpProbe.waitUntilBootstrapSummaryReady(gatewayBaseUrl);
175
123
  const initialSummary = await this.devBootstrapSummaryFetcher.fetch(gatewayBaseUrl);
176
124
  if (initialSummary) this.devCliBannerRenderer.renderRuntimeSummary(initialSummary);
125
+ await this.startPackagedUiWhenNeeded(prepared, processState, uiProxyBase);
177
126
  this.bindShutdownSignalsToChildProcesses(processState, proxyServer);
178
127
  await this.spawnDevUiWhenNeeded(prepared, processState, gatewayBaseUrl);
179
- await this.startWatcherForSourceRestart(prepared, processState, watcher, devMode, gatewayBaseUrl, proxyServer);
180
- this.logPackagedUiDevHintWhenNeeded(devMode, gatewayPort);
128
+ await this.startWatcherForSourceRestart(prepared, processState, watcher, devMode, gatewayBaseUrl, proxyServer, {
129
+ commandName,
130
+ configPathOverride: args.configPathOverride
131
+ });
132
+ this.logPackagedUiDevHintWhenNeeded(devMode, gatewayPort, commandName);
181
133
  await stopPromise;
182
134
  } finally {
183
135
  processState.stopRequested = true;
@@ -190,14 +142,15 @@ var DevCommand = class {
190
142
  if (args.watchFramework === true || process$1.env.CODEMATION_DEV_MODE === "framework") return "watch-framework";
191
143
  return "packaged-ui";
192
144
  }
193
- async prepareDevRuntime(paths, devMode, nextPort, gatewayPort, authSettings) {
145
+ async prepareDevRuntime(paths, devMode, nextPort, gatewayPort, authSettings, configPathOverride) {
194
146
  return {
195
147
  paths,
148
+ configPathOverride,
196
149
  devMode,
197
150
  nextPort,
198
151
  gatewayPort,
199
152
  authSettings,
200
- developmentServerToken: this.session.devAuthLoader.resolveDevelopmentServerToken(process$1.env.CODEMATION_DEV_SERVER_TOKEN),
153
+ developmentServerToken: this.session.nextHostEdgeSeedLoader.resolveDevelopmentServerToken(process$1.env.CODEMATION_DEV_SERVER_TOKEN),
201
154
  consumerEnv: this.session.consumerEnvLoader.load(paths.consumerRoot)
202
155
  };
203
156
  }
@@ -222,30 +175,30 @@ var DevCommand = class {
222
175
  gatewayBaseHttpUrl(gatewayPort) {
223
176
  return `http://127.0.0.1:${gatewayPort}`;
224
177
  }
225
- async startPackagedUiWhenNeeded(prepared, state) {
178
+ async preparePackagedUiBaseUrlWhenNeeded(prepared, state) {
226
179
  if (prepared.devMode !== "packaged-ui") return "";
227
- const websocketPort = prepared.gatewayPort;
228
180
  const uiProxyBase = state.currentPackagedUiBaseUrl ?? `http://127.0.0.1:${await this.session.loopbackPortAllocator.allocate()}`;
229
181
  state.currentPackagedUiBaseUrl = uiProxyBase;
230
- await this.spawnPackagedUi(prepared, state, prepared.authSettings, websocketPort, uiProxyBase);
231
182
  return uiProxyBase;
232
183
  }
184
+ async startPackagedUiWhenNeeded(prepared, state, uiProxyBase) {
185
+ if (prepared.devMode !== "packaged-ui" || uiProxyBase.length === 0) return;
186
+ await this.spawnPackagedUi(prepared, state, prepared.authSettings, prepared.gatewayPort, uiProxyBase);
187
+ }
233
188
  async spawnPackagedUi(prepared, state, authSettings, websocketPort, uiProxyBase) {
234
189
  const nextHostPackageJsonPath = this.require.resolve("@codemation/next-host/package.json");
235
190
  const nextHostRoot = path.dirname(nextHostPackageJsonPath);
236
191
  const nextHostCommand = await this.nextHostConsumerServerCommandFactory.create({ nextHostRoot });
237
- const consumerOutputManifestPath = path.resolve(prepared.paths.consumerRoot, ".codemation", "output", "current.json");
238
192
  const uiPort = Number(new URL(uiProxyBase).port);
239
193
  const nextHostEnvironment = this.session.nextHostEnvBuilder.buildConsumerUiProxy({
240
- authConfigJson: authSettings.authConfigJson,
241
194
  authSecret: authSettings.authSecret,
195
+ configPathOverride: prepared.configPathOverride,
242
196
  consumerRoot: prepared.paths.consumerRoot,
243
- consumerOutputManifestPath,
244
197
  developmentServerToken: prepared.developmentServerToken,
245
198
  nextPort: uiPort,
246
199
  publicBaseUrl: this.gatewayBaseHttpUrl(prepared.gatewayPort),
247
200
  runtimeDevUrl: this.gatewayBaseHttpUrl(prepared.gatewayPort),
248
- skipUiAuth: authSettings.skipUiAuth,
201
+ skipUiAuth: !authSettings.uiAuthEnabled,
249
202
  websocketPort
250
203
  });
251
204
  state.currentPackagedUi = spawn(nextHostCommand.command, nextHostCommand.args, {
@@ -319,11 +272,12 @@ var DevCommand = class {
319
272
  const nextHostPackageJsonPath = this.require.resolve("@codemation/next-host/package.json");
320
273
  const nextHostRoot = path.dirname(nextHostPackageJsonPath);
321
274
  const nextHostEnvironment = this.session.nextHostEnvBuilder.build({
322
- authConfigJson: authSettings.authConfigJson,
275
+ authSecret: authSettings.authSecret,
276
+ configPathOverride: prepared.configPathOverride,
323
277
  consumerRoot: prepared.paths.consumerRoot,
324
278
  developmentServerToken: prepared.developmentServerToken,
325
279
  nextPort: prepared.nextPort,
326
- skipUiAuth: authSettings.skipUiAuth,
280
+ skipUiAuth: !authSettings.uiAuthEnabled,
327
281
  websocketPort,
328
282
  runtimeDevUrl: gatewayBaseUrl
329
283
  });
@@ -354,7 +308,7 @@ var DevCommand = class {
354
308
  });
355
309
  await this.session.devHttpProbe.waitUntilUrlRespondsOk(`http://127.0.0.1:${prepared.nextPort}/`);
356
310
  }
357
- async startWatcherForSourceRestart(prepared, state, watcher, devMode, gatewayBaseUrl, proxyServer) {
311
+ async startWatcherForSourceRestart(prepared, state, watcher, devMode, gatewayBaseUrl, proxyServer, options) {
358
312
  const rebuildQueue = this.devRebuildQueueFactory.create({ run: async (request) => {
359
313
  await this.runQueuedRebuild(prepared, state, gatewayBaseUrl, proxyServer, request);
360
314
  } });
@@ -370,18 +324,14 @@ var DevCommand = class {
370
324
  return;
371
325
  }
372
326
  try {
373
- const shouldRepublishConsumerOutput = this.session.sourceChangeClassifier.shouldRepublishConsumerOutput({
374
- changedPaths,
375
- consumerRoot: prepared.paths.consumerRoot
376
- });
377
327
  const shouldRestartUi = this.session.sourceChangeClassifier.requiresUiRestart({
378
328
  changedPaths,
379
329
  consumerRoot: prepared.paths.consumerRoot
380
330
  });
381
- process$1.stdout.write(shouldRestartUi ? "\n[codemation] Source change detected — rebuilding consumer, restarting the runtime, and restarting the UI…\n" : "\n[codemation] Source change detected — rebuilding consumer and restarting the runtime…\n");
331
+ process$1.stdout.write(shouldRestartUi ? `\n[codemation] Source change detected — rebuilding for \`${options.commandName}\`, restarting the runtime, and restarting the UI…\n` : `\n[codemation] Source change detected — rebuilding for \`${options.commandName}\` and restarting the runtime…\n`);
382
332
  await rebuildQueue.enqueue({
383
333
  changedPaths,
384
- shouldRepublishConsumerOutput,
334
+ configPathOverride: options.configPathOverride,
385
335
  shouldRestartUi
386
336
  });
387
337
  } catch (error) {
@@ -395,7 +345,6 @@ var DevCommand = class {
395
345
  proxyServer.setBuildStatus("building");
396
346
  proxyServer.broadcastBuildStarted();
397
347
  try {
398
- if (request.shouldRepublishConsumerOutput) await this.devConsumerPublishBootstrap.ensurePublished(prepared.paths);
399
348
  await this.stopCurrentRuntime(state, proxyServer);
400
349
  process$1.stdout.write("[codemation] Waiting for runtime to accept traffic…\n");
401
350
  const runtime = await this.createRuntime(prepared);
@@ -418,7 +367,7 @@ var DevCommand = class {
418
367
  }
419
368
  }
420
369
  async restartUiAfterSourceChange(prepared, state, gatewayBaseUrl) {
421
- const refreshedAuthSettings = await this.session.devAuthLoader.loadForConsumer(prepared.paths.consumerRoot);
370
+ const refreshedAuthSettings = await this.session.nextHostEdgeSeedLoader.loadForConsumer(prepared.paths.consumerRoot, { configPathOverride: prepared.configPathOverride });
422
371
  process$1.stdout.write("[codemation] Restarting the UI process to apply source changes…\n");
423
372
  state.isRestartingUi = true;
424
373
  try {
@@ -465,6 +414,7 @@ var DevCommand = class {
465
414
  async createRuntime(prepared) {
466
415
  const runtimeEnvironment = this.session.consumerEnvLoader.mergeIntoProcessEnvironment(process$1.env, prepared.consumerEnv);
467
416
  return await this.devApiRuntimeFactory.create({
417
+ configPathOverride: prepared.configPathOverride,
468
418
  consumerRoot: prepared.paths.consumerRoot,
469
419
  runtimeWorkingDirectory: process$1.cwd(),
470
420
  env: {
@@ -477,9 +427,27 @@ var DevCommand = class {
477
427
  }
478
428
  });
479
429
  }
480
- logPackagedUiDevHintWhenNeeded(devMode, gatewayPort) {
430
+ logPackagedUiDevHintWhenNeeded(devMode, gatewayPort, commandName) {
481
431
  if (devMode !== "packaged-ui") return;
482
- this.cliLogger.info(`codemation dev: open http://127.0.0.1:${gatewayPort} — this uses the packaged @codemation/next-host UI. Use \`codemation dev --watch-framework\` only when working on the framework UI itself.`);
432
+ this.cliLogger.info(`codemation ${commandName}: open http://127.0.0.1:${gatewayPort} — this uses the packaged @codemation/next-host UI. Use \`codemation ${commandName} --watch-framework\` only when working on the framework UI itself.`);
433
+ }
434
+ };
435
+
436
+ //#endregion
437
+ //#region src/commands/DevPluginCommand.ts
438
+ var DevPluginCommand = class {
439
+ constructor(pluginDevConfigFactory, devCommand) {
440
+ this.pluginDevConfigFactory = pluginDevConfigFactory;
441
+ this.devCommand = devCommand;
442
+ }
443
+ async execute(args) {
444
+ const pluginConfig = await this.pluginDevConfigFactory.prepare(args.pluginRoot);
445
+ await this.devCommand.execute({
446
+ commandName: "dev:plugin",
447
+ configPathOverride: pluginConfig.configPath,
448
+ consumerRoot: args.pluginRoot,
449
+ watchFramework: args.watchFramework
450
+ });
483
451
  }
484
452
  };
485
453
 
@@ -487,38 +455,22 @@ var DevCommand = class {
487
455
  //#region src/commands/ServeWebCommand.ts
488
456
  var ServeWebCommand = class {
489
457
  require = createRequire(import.meta.url);
490
- constructor(pathResolver, configLoader, pluginDiscovery, artifactsPublisher, tsRuntime, sourceMapNodeOptions, outputBuilderLoader, envLoader, listenPortResolver, nextHostConsumerServerCommandFactory, frontendAuthSnapshotFactory, frontendAppConfigJsonCodec) {
458
+ constructor(pathResolver, configLoader, tsRuntime, sourceMapNodeOptions, envLoader, listenPortResolver, nextHostConsumerServerCommandFactory) {
491
459
  this.pathResolver = pathResolver;
492
460
  this.configLoader = configLoader;
493
- this.pluginDiscovery = pluginDiscovery;
494
- this.artifactsPublisher = artifactsPublisher;
495
461
  this.tsRuntime = tsRuntime;
496
462
  this.sourceMapNodeOptions = sourceMapNodeOptions;
497
- this.outputBuilderLoader = outputBuilderLoader;
498
463
  this.envLoader = envLoader;
499
464
  this.listenPortResolver = listenPortResolver;
500
465
  this.nextHostConsumerServerCommandFactory = nextHostConsumerServerCommandFactory;
501
- this.frontendAuthSnapshotFactory = frontendAuthSnapshotFactory;
502
- this.frontendAppConfigJsonCodec = frontendAppConfigJsonCodec;
503
466
  }
504
467
  async execute(consumerRoot, buildOptions) {
505
468
  const paths = await this.pathResolver.resolve(consumerRoot);
506
469
  this.tsRuntime.configure(paths.repoRoot);
507
- const snapshot = await this.outputBuilderLoader.create(paths.consumerRoot, buildOptions).ensureBuilt();
508
- const discoveredPlugins = await this.pluginDiscovery.discover(paths.consumerRoot);
509
- const manifest = await this.artifactsPublisher.publish(snapshot, discoveredPlugins);
510
470
  const nextHostRoot = path.dirname(this.require.resolve("@codemation/next-host/package.json"));
511
471
  const nextHostCommand = await this.nextHostConsumerServerCommandFactory.create({ nextHostRoot });
512
472
  const consumerEnv = this.envLoader.load(paths.consumerRoot);
513
473
  const configResolution = await this.configLoader.load({ consumerRoot: paths.consumerRoot });
514
- const frontendAuthSnapshot = this.frontendAuthSnapshotFactory.createFromResolvedInputs({
515
- authConfig: configResolution.config.auth,
516
- env: {
517
- ...process$1.env,
518
- ...consumerEnv
519
- },
520
- uiAuthEnabled: !(consumerEnv.NODE_ENV !== "production" && configResolution.config.auth?.allowUnauthenticatedInDevelopment === true)
521
- });
522
474
  const nextPort = this.listenPortResolver.resolvePrimaryApplicationPort(process$1.env.PORT);
523
475
  const websocketPort = this.listenPortResolver.resolveWebsocketPortRelativeToHttp({
524
476
  nextPort,
@@ -532,13 +484,8 @@ var ServeWebCommand = class {
532
484
  ...process$1.env,
533
485
  ...consumerEnv,
534
486
  PORT: String(nextPort),
535
- CODEMATION_FRONTEND_APP_CONFIG_JSON: this.frontendAppConfigJsonCodec.serialize({
536
- auth: frontendAuthSnapshot,
537
- productName: "Codemation",
538
- logoUrl: null
539
- }),
540
- CODEMATION_CONSUMER_OUTPUT_MANIFEST_PATH: manifest.manifestPath,
541
487
  CODEMATION_CONSUMER_ROOT: paths.consumerRoot,
488
+ CODEMATION_UI_AUTH_ENABLED: String(!(consumerEnv.NODE_ENV !== "production" && configResolution.config.auth?.allowUnauthenticatedInDevelopment === true)),
542
489
  CODEMATION_WS_PORT: String(websocketPort),
543
490
  NEXT_PUBLIC_CODEMATION_WS_PORT: String(websocketPort),
544
491
  NODE_OPTIONS: this.sourceMapNodeOptions.appendToNodeOptions(process$1.env.NODE_OPTIONS),
@@ -978,6 +925,7 @@ var ConsumerOutputBuilder = class ConsumerOutputBuilder {
978
925
  sourcePath
979
926
  });
980
927
  }
928
+ await this.emitConfigSourceFile(outputAppRoot, configSourcePath, runtimeSourcePaths);
981
929
  }
982
930
  });
983
931
  }
@@ -1036,6 +984,14 @@ var ConsumerOutputBuilder = class ConsumerOutputBuilder {
1036
984
  await writeFile(outputPath, rewrittenOutputText, "utf8");
1037
985
  if (transpiledOutput.sourceMapText) await writeFile(`${outputPath}.map`, transpiledOutput.sourceMapText, "utf8");
1038
986
  }
987
+ async emitConfigSourceFile(outputAppRoot, configSourcePath, runtimeSourcePaths) {
988
+ const normalizedConfigSourcePath = path.resolve(configSourcePath);
989
+ if (runtimeSourcePaths.some((sourcePath) => path.resolve(sourcePath) === normalizedConfigSourcePath)) return;
990
+ await this.emitSourceFile({
991
+ outputAppRoot,
992
+ sourcePath: normalizedConfigSourcePath
993
+ });
994
+ }
1039
995
  createCompilerOptions() {
1040
996
  const scriptTarget = this.buildOptions.target === "es2020" ? ts.ScriptTarget.ES2020 : ts.ScriptTarget.ES2022;
1041
997
  return {
@@ -1289,10 +1245,10 @@ var ConsumerOutputBuilder = class ConsumerOutputBuilder {
1289
1245
  };
1290
1246
 
1291
1247
  //#endregion
1292
- //#region src/consumer/Loader.ts
1293
- var ConsumerOutputBuilderLoader = class {
1294
- create(consumerRoot, buildOptions) {
1295
- return new ConsumerOutputBuilder(consumerRoot, void 0, buildOptions);
1248
+ //#region src/consumer/ConsumerOutputBuilderFactory.ts
1249
+ var ConsumerOutputBuilderFactory = class {
1250
+ create(consumerRoot, args) {
1251
+ return new ConsumerOutputBuilder(consumerRoot, args?.logger, args?.buildOptions);
1296
1252
  }
1297
1253
  };
1298
1254
 
@@ -1464,8 +1420,9 @@ var DevCliBannerRenderer = class {
1464
1420
  title: chalk.bold("Runtime"),
1465
1421
  titleAlignment: "center"
1466
1422
  });
1423
+ const pluginsSection = this.buildPluginsSection(summary);
1467
1424
  const activeSection = this.buildActiveWorkflowsSection(summary);
1468
- process.stdout.write(`${detailBox}\n${activeSection}\n`);
1425
+ process.stdout.write(`${detailBox}\n${pluginsSection}\n${activeSection}\n`);
1469
1426
  }
1470
1427
  renderFull(summary) {
1471
1428
  this.renderBrandHeader();
@@ -1492,8 +1449,9 @@ var DevCliBannerRenderer = class {
1492
1449
  title: chalk.bold("Runtime (updated)"),
1493
1450
  titleAlignment: "center"
1494
1451
  });
1452
+ const pluginsSection = this.buildPluginsSection(summary);
1495
1453
  const activeSection = this.buildActiveWorkflowsSection(summary);
1496
- process.stdout.write(`\n${detailBox}\n${activeSection}\n`);
1454
+ process.stdout.write(`\n${detailBox}\n${pluginsSection}\n${activeSection}\n`);
1497
1455
  }
1498
1456
  renderFigletTitle() {
1499
1457
  try {
@@ -1532,28 +1490,23 @@ var DevCliBannerRenderer = class {
1532
1490
  titleAlignment: "left"
1533
1491
  });
1534
1492
  }
1535
- };
1536
-
1537
- //#endregion
1538
- //#region src/dev/DevConsumerPublishBootstrap.ts
1539
- /**
1540
- * Ensures `.codemation/output/current.json` and transpiled consumer config exist before the Next host boots.
1541
- * Without this, `codemation dev` can serve a stale built `codemation.config.js` (e.g. missing whitelabel).
1542
- */
1543
- var DevConsumerPublishBootstrap = class {
1544
- constructor(cliLogger, pluginDiscovery, artifactsPublisher, outputBuilderLoader, buildOptionsParser) {
1545
- this.cliLogger = cliLogger;
1546
- this.pluginDiscovery = pluginDiscovery;
1547
- this.artifactsPublisher = artifactsPublisher;
1548
- this.outputBuilderLoader = outputBuilderLoader;
1549
- this.buildOptionsParser = buildOptionsParser;
1550
- }
1551
- async ensurePublished(paths) {
1552
- const buildOptions = this.buildOptionsParser.parse({});
1553
- const snapshot = await this.outputBuilderLoader.create(paths.consumerRoot, buildOptions).ensureBuilt();
1554
- const discoveredPlugins = await this.pluginDiscovery.discover(paths.consumerRoot);
1555
- await this.artifactsPublisher.publish(snapshot, discoveredPlugins);
1556
- this.cliLogger.debug(`Dev: consumer output published (${snapshot.buildVersion}).`);
1493
+ buildPluginsSection(summary) {
1494
+ return boxen((summary.plugins.length === 0 ? [chalk.dim(" (none discovered or configured)")] : summary.plugins.map((plugin) => `${chalk.whiteBright(` • ${plugin.packageName} `)}${chalk.dim(`(${plugin.source})`)}`)).join("\n"), {
1495
+ padding: {
1496
+ top: 0,
1497
+ bottom: 0,
1498
+ left: 0,
1499
+ right: 0
1500
+ },
1501
+ margin: {
1502
+ top: 1,
1503
+ bottom: 0
1504
+ },
1505
+ borderStyle: "single",
1506
+ borderColor: "cyan",
1507
+ title: chalk.bold("Plugins"),
1508
+ titleAlignment: "left"
1509
+ });
1557
1510
  }
1558
1511
  };
1559
1512
 
@@ -2089,7 +2042,8 @@ var CodemationTsyringeTypeInfoRegistrar = class {
2089
2042
  //#endregion
2090
2043
  //#region src/dev/DevApiRuntimeHost.ts
2091
2044
  var DevApiRuntimeHost = class {
2092
- pluginListMerger = new CodemationPluginListMerger();
2045
+ pluginPackageMetadata = new CodemationPluginPackageMetadata();
2046
+ pluginListMerger = new CodemationPluginListMerger(this.pluginPackageMetadata);
2093
2047
  contextPromise = null;
2094
2048
  constructor(configLoader, pluginDiscovery, args) {
2095
2049
  this.configLoader = configLoader;
@@ -2118,14 +2072,17 @@ var DevApiRuntimeHost = class {
2118
2072
  env.CODEMATION_CONSUMER_ROOT = consumerRoot;
2119
2073
  const configResolution = await this.configLoader.load({
2120
2074
  consumerRoot,
2075
+ configPathOverride: this.args.configPathOverride,
2121
2076
  repoRoot,
2122
2077
  env
2123
2078
  });
2124
2079
  const discoveredPlugins = await this.loadDiscoveredPlugins(consumerRoot);
2080
+ const mergedPlugins = discoveredPlugins.length > 0 ? this.pluginListMerger.merge(configResolution.appConfig.plugins, discoveredPlugins) : configResolution.appConfig.plugins;
2125
2081
  const appConfig = {
2126
2082
  ...configResolution.appConfig,
2127
2083
  env,
2128
- plugins: discoveredPlugins.length > 0 ? this.pluginListMerger.merge(configResolution.appConfig.plugins, discoveredPlugins) : configResolution.appConfig.plugins
2084
+ plugins: mergedPlugins,
2085
+ pluginLoadSummary: this.createPluginLoadSummary(configResolution.appConfig.plugins, discoveredPlugins, mergedPlugins)
2129
2086
  };
2130
2087
  const container = await new AppContainerFactory$1().create({
2131
2088
  appConfig,
@@ -2145,6 +2102,20 @@ var DevApiRuntimeHost = class {
2145
2102
  async loadDiscoveredPlugins(consumerRoot) {
2146
2103
  return (await this.pluginDiscovery.resolvePlugins(consumerRoot)).map((resolvedPackage) => resolvedPackage.plugin);
2147
2104
  }
2105
+ createPluginLoadSummary(configuredPlugins, discoveredPlugins, mergedPlugins) {
2106
+ const configuredPluginSet = new Set(configuredPlugins);
2107
+ const discoveredPluginSet = new Set(discoveredPlugins);
2108
+ const summaries = [];
2109
+ for (const plugin of mergedPlugins) {
2110
+ const packageName = this.pluginPackageMetadata.readPackageName(plugin);
2111
+ if (!packageName) continue;
2112
+ summaries.push({
2113
+ packageName,
2114
+ source: configuredPluginSet.has(plugin) || !discoveredPluginSet.has(plugin) ? "configured" : "discovered"
2115
+ });
2116
+ }
2117
+ return summaries;
2118
+ }
2148
2119
  async detectWorkspaceRoot(startDirectory) {
2149
2120
  let currentDirectory = path.resolve(startDirectory);
2150
2121
  while (true) {
@@ -2262,6 +2233,7 @@ var DevApiRuntimeFactory = class {
2262
2233
  const httpPort = await this.portAllocator.allocate();
2263
2234
  const workflowWebSocketPort = await this.portAllocator.allocate();
2264
2235
  const runtime = new DevApiRuntimeServer(httpPort, workflowWebSocketPort, new DevApiRuntimeHost(this.configLoader, this.pluginDiscovery, {
2236
+ configPathOverride: args.configPathOverride,
2265
2237
  consumerRoot: args.consumerRoot,
2266
2238
  env: {
2267
2239
  ...args.env,
@@ -2318,7 +2290,7 @@ var DevRebuildQueue = class {
2318
2290
  };
2319
2291
  return {
2320
2292
  changedPaths: [...new Set([...current.changedPaths, ...next.changedPaths])],
2321
- shouldRepublishConsumerOutput: current.shouldRepublishConsumerOutput || next.shouldRepublishConsumerOutput,
2293
+ configPathOverride: next.configPathOverride ?? current.configPathOverride,
2322
2294
  shouldRestartUi: current.shouldRestartUi || next.shouldRestartUi
2323
2295
  };
2324
2296
  }
@@ -2366,34 +2338,6 @@ var SourceMapNodeOptions = class {
2366
2338
  }
2367
2339
  };
2368
2340
 
2369
- //#endregion
2370
- //#region src/dev/DevAuthSettingsLoader.ts
2371
- var DevAuthSettingsLoader = class DevAuthSettingsLoader {
2372
- static defaultDevelopmentAuthSecret = "codemation-dev-auth-secret-not-for-production";
2373
- constructor(configLoader, consumerEnvLoader) {
2374
- this.configLoader = configLoader;
2375
- this.consumerEnvLoader = consumerEnvLoader;
2376
- }
2377
- resolveDevelopmentServerToken(rawToken) {
2378
- if (rawToken && rawToken.trim().length > 0) return rawToken;
2379
- return randomUUID();
2380
- }
2381
- async loadForConsumer(consumerRoot) {
2382
- const resolution = await this.configLoader.load({ consumerRoot });
2383
- const envForAuthSecret = this.consumerEnvLoader.mergeConsumerRootIntoProcessEnvironment(consumerRoot, process.env);
2384
- return {
2385
- authConfigJson: JSON.stringify(resolution.config.auth ?? null),
2386
- authSecret: this.resolveDevelopmentAuthSecret(envForAuthSecret),
2387
- skipUiAuth: resolution.config.auth?.allowUnauthenticatedInDevelopment === true
2388
- };
2389
- }
2390
- resolveDevelopmentAuthSecret(env) {
2391
- const configuredSecret = env.AUTH_SECRET;
2392
- if (configuredSecret && configuredSecret.trim().length > 0) return configuredSecret;
2393
- return DevAuthSettingsLoader.defaultDevelopmentAuthSecret;
2394
- }
2395
- };
2396
-
2397
2341
  //#endregion
2398
2342
  //#region src/dev/DevHttpProbe.ts
2399
2343
  var DevHttpProbe = class {
@@ -2438,24 +2382,21 @@ var DevHttpProbe = class {
2438
2382
  //#endregion
2439
2383
  //#region src/dev/DevNextHostEnvironmentBuilder.ts
2440
2384
  var DevNextHostEnvironmentBuilder = class {
2441
- constructor(consumerEnvLoader, sourceMapNodeOptions, frontendAuthSnapshotFactory = new CodemationFrontendAuthSnapshotFactory$1(), frontendAppConfigJsonCodec = new FrontendAppConfigJsonCodec$1()) {
2385
+ constructor(consumerEnvLoader, sourceMapNodeOptions) {
2442
2386
  this.consumerEnvLoader = consumerEnvLoader;
2443
2387
  this.sourceMapNodeOptions = sourceMapNodeOptions;
2444
- this.frontendAuthSnapshotFactory = frontendAuthSnapshotFactory;
2445
- this.frontendAppConfigJsonCodec = frontendAppConfigJsonCodec;
2446
2388
  }
2447
2389
  buildConsumerUiProxy(args) {
2448
2390
  return {
2449
2391
  ...this.build({
2450
- authConfigJson: args.authConfigJson,
2451
2392
  authSecret: args.authSecret,
2393
+ configPathOverride: args.configPathOverride,
2452
2394
  consumerRoot: args.consumerRoot,
2453
2395
  developmentServerToken: args.developmentServerToken,
2454
2396
  nextPort: args.nextPort,
2455
2397
  runtimeDevUrl: args.runtimeDevUrl,
2456
2398
  skipUiAuth: args.skipUiAuth,
2457
- websocketPort: args.websocketPort,
2458
- consumerOutputManifestPath: args.consumerOutputManifestPath
2399
+ websocketPort: args.websocketPort
2459
2400
  }),
2460
2401
  HOSTNAME: "127.0.0.1",
2461
2402
  AUTH_SECRET: args.authSecret,
@@ -2463,27 +2404,11 @@ var DevNextHostEnvironmentBuilder = class {
2463
2404
  };
2464
2405
  }
2465
2406
  build(args) {
2466
- const merged = this.consumerEnvLoader.mergeConsumerRootIntoProcessEnvironment(args.consumerRoot, process$1.env);
2467
- const manifestPath = args.consumerOutputManifestPath ?? path.resolve(args.consumerRoot, ".codemation", "output", "current.json");
2468
- const authSecret = args.authSecret ?? merged.AUTH_SECRET;
2469
- const authSnapshot = this.frontendAuthSnapshotFactory.createFromResolvedInputs({
2470
- authConfig: this.parseAuthConfig(args.authConfigJson),
2471
- env: {
2472
- ...merged,
2473
- ...typeof authSecret === "string" && authSecret.trim().length > 0 ? { AUTH_SECRET: authSecret } : {}
2474
- },
2475
- uiAuthEnabled: !args.skipUiAuth
2476
- });
2477
2407
  return {
2478
- ...merged,
2408
+ ...this.consumerEnvLoader.mergeConsumerRootIntoProcessEnvironment(args.consumerRoot, process$1.env),
2479
2409
  PORT: String(args.nextPort),
2480
2410
  CODEMATION_CONSUMER_ROOT: args.consumerRoot,
2481
- CODEMATION_CONSUMER_OUTPUT_MANIFEST_PATH: manifestPath,
2482
- CODEMATION_FRONTEND_APP_CONFIG_JSON: this.frontendAppConfigJsonCodec.serialize({
2483
- auth: authSnapshot,
2484
- productName: "Codemation",
2485
- logoUrl: null
2486
- }),
2411
+ CODEMATION_UI_AUTH_ENABLED: String(!args.skipUiAuth),
2487
2412
  CODEMATION_WS_PORT: String(args.websocketPort),
2488
2413
  NEXT_PUBLIC_CODEMATION_WS_PORT: String(args.websocketPort),
2489
2414
  CODEMATION_DEV_SERVER_TOKEN: args.developmentServerToken,
@@ -2491,13 +2416,11 @@ var DevNextHostEnvironmentBuilder = class {
2491
2416
  NODE_OPTIONS: this.sourceMapNodeOptions.appendToNodeOptions(process$1.env.NODE_OPTIONS),
2492
2417
  WS_NO_BUFFER_UTIL: "1",
2493
2418
  WS_NO_UTF_8_VALIDATE: "1",
2419
+ ...args.authSecret && args.authSecret.trim().length > 0 ? { AUTH_SECRET: args.authSecret.trim() } : {},
2420
+ ...args.configPathOverride && args.configPathOverride.trim().length > 0 ? { CODEMATION_CONFIG_PATH: args.configPathOverride } : {},
2494
2421
  ...args.runtimeDevUrl !== void 0 && args.runtimeDevUrl.trim().length > 0 ? { CODEMATION_RUNTIME_DEV_URL: args.runtimeDevUrl.trim() } : {}
2495
2422
  };
2496
2423
  }
2497
- parseAuthConfig(authConfigJson) {
2498
- if (authConfigJson.trim().length === 0) return;
2499
- return JSON.parse(authConfigJson) ?? void 0;
2500
- }
2501
2424
  };
2502
2425
 
2503
2426
  //#endregion
@@ -2522,13 +2445,13 @@ var DevSessionPortsResolver = class {
2522
2445
  * Bundles dependencies for {@link DevCommand} so the command stays a thin orchestrator.
2523
2446
  */
2524
2447
  var DevSessionServices = class {
2525
- constructor(consumerEnvLoader, sourceMapNodeOptions, sessionPorts, loopbackPortAllocator, devHttpProbe, devAuthLoader, nextHostEnvBuilder, watchRootsResolver, sourceChangeClassifier) {
2448
+ constructor(consumerEnvLoader, sourceMapNodeOptions, sessionPorts, loopbackPortAllocator, devHttpProbe, nextHostEdgeSeedLoader, nextHostEnvBuilder, watchRootsResolver, sourceChangeClassifier) {
2526
2449
  this.consumerEnvLoader = consumerEnvLoader;
2527
2450
  this.sourceMapNodeOptions = sourceMapNodeOptions;
2528
2451
  this.sessionPorts = sessionPorts;
2529
2452
  this.loopbackPortAllocator = loopbackPortAllocator;
2530
2453
  this.devHttpProbe = devHttpProbe;
2531
- this.devAuthLoader = devAuthLoader;
2454
+ this.nextHostEdgeSeedLoader = nextHostEdgeSeedLoader;
2532
2455
  this.nextHostEnvBuilder = nextHostEnvBuilder;
2533
2456
  this.watchRootsResolver = watchRootsResolver;
2534
2457
  this.sourceChangeClassifier = sourceChangeClassifier;
@@ -2538,15 +2461,16 @@ var DevSessionServices = class {
2538
2461
  //#endregion
2539
2462
  //#region src/dev/DevSourceChangeClassifier.ts
2540
2463
  var DevSourceChangeClassifier = class DevSourceChangeClassifier {
2541
- static configFileNames = new Set([
2464
+ static uiConfigFileNames = new Set([
2542
2465
  "codemation.config.ts",
2543
2466
  "codemation.config.js",
2544
2467
  "codemation.config.mjs"
2545
2468
  ]);
2546
- shouldRepublishConsumerOutput(args) {
2547
- const resolvedConsumerRoot = path.resolve(args.consumerRoot);
2548
- return args.changedPaths.some((changedPath) => this.isPathInsideDirectory(changedPath, resolvedConsumerRoot));
2549
- }
2469
+ static pluginConfigFileNames = new Set([
2470
+ "codemation.plugin.ts",
2471
+ "codemation.plugin.js",
2472
+ "codemation.plugin.mjs"
2473
+ ]);
2550
2474
  requiresUiRestart(args) {
2551
2475
  const resolvedConsumerRoot = path.resolve(args.consumerRoot);
2552
2476
  return args.changedPaths.some((changedPath) => this.pathRequiresUiRestart(path.resolve(changedPath), resolvedConsumerRoot));
@@ -2559,7 +2483,9 @@ var DevSourceChangeClassifier = class DevSourceChangeClassifier {
2559
2483
  pathRequiresUiRestart(resolvedPath, consumerRoot) {
2560
2484
  if (!this.isPathInsideDirectory(resolvedPath, consumerRoot)) return false;
2561
2485
  const relativePath = path.relative(consumerRoot, resolvedPath);
2562
- if (DevSourceChangeClassifier.configFileNames.has(path.basename(relativePath))) return true;
2486
+ const fileName = path.basename(relativePath);
2487
+ if (DevSourceChangeClassifier.uiConfigFileNames.has(fileName)) return true;
2488
+ if (DevSourceChangeClassifier.pluginConfigFileNames.has(fileName)) return false;
2563
2489
  if (relativePath.startsWith(path.join("src", "workflows"))) return false;
2564
2490
  if (relativePath.startsWith(path.join("src", "plugins"))) return false;
2565
2491
  if (relativePath.includes("credential")) return true;
@@ -2588,6 +2514,36 @@ var LoopbackPortAllocator = class {
2588
2514
  }
2589
2515
  };
2590
2516
 
2517
+ //#endregion
2518
+ //#region src/dev/NextHostEdgeSeedLoader.ts
2519
+ var NextHostEdgeSeedLoader = class NextHostEdgeSeedLoader {
2520
+ static defaultDevelopmentAuthSecret = "codemation-dev-auth-secret-not-for-production";
2521
+ constructor(configLoader, consumerEnvLoader) {
2522
+ this.configLoader = configLoader;
2523
+ this.consumerEnvLoader = consumerEnvLoader;
2524
+ }
2525
+ resolveDevelopmentServerToken(rawToken) {
2526
+ if (rawToken && rawToken.trim().length > 0) return rawToken;
2527
+ return randomUUID();
2528
+ }
2529
+ async loadForConsumer(consumerRoot, options) {
2530
+ const resolution = await this.configLoader.load({
2531
+ consumerRoot,
2532
+ configPathOverride: options?.configPathOverride
2533
+ });
2534
+ const envForAuthSecret = this.consumerEnvLoader.mergeConsumerRootIntoProcessEnvironment(consumerRoot, process.env);
2535
+ return {
2536
+ authSecret: this.resolveDevelopmentAuthSecret(envForAuthSecret),
2537
+ uiAuthEnabled: resolution.config.auth?.allowUnauthenticatedInDevelopment !== true
2538
+ };
2539
+ }
2540
+ resolveDevelopmentAuthSecret(env) {
2541
+ const configuredSecret = env.AUTH_SECRET;
2542
+ if (configuredSecret && configuredSecret.trim().length > 0) return configuredSecret;
2543
+ return NextHostEdgeSeedLoader.defaultDevelopmentAuthSecret;
2544
+ }
2545
+ };
2546
+
2591
2547
  //#endregion
2592
2548
  //#region src/dev/WatchRootsResolver.ts
2593
2549
  var WatchRootsResolver = class {
@@ -2614,7 +2570,57 @@ var DevSessionServicesBuilder = class {
2614
2570
  const sourceMapNodeOptions = new SourceMapNodeOptions();
2615
2571
  const listenPortResolver = new ListenPortResolver();
2616
2572
  const loopbackPortAllocator = new LoopbackPortAllocator();
2617
- return new DevSessionServices(consumerEnvLoader, sourceMapNodeOptions, new DevSessionPortsResolver(listenPortResolver, loopbackPortAllocator), loopbackPortAllocator, new DevHttpProbe(), new DevAuthSettingsLoader(new CodemationConsumerConfigLoader(), consumerEnvLoader), new DevNextHostEnvironmentBuilder(consumerEnvLoader, sourceMapNodeOptions), new WatchRootsResolver(), new DevSourceChangeClassifier());
2573
+ return new DevSessionServices(consumerEnvLoader, sourceMapNodeOptions, new DevSessionPortsResolver(listenPortResolver, loopbackPortAllocator), loopbackPortAllocator, new DevHttpProbe(), new NextHostEdgeSeedLoader(new CodemationConsumerConfigLoader(), consumerEnvLoader), new DevNextHostEnvironmentBuilder(consumerEnvLoader, sourceMapNodeOptions), new WatchRootsResolver(), new DevSourceChangeClassifier());
2574
+ }
2575
+ };
2576
+
2577
+ //#endregion
2578
+ //#region src/dev/PluginDevConfigFactory.ts
2579
+ var PluginDevConfigFactory = class {
2580
+ async prepare(pluginRoot) {
2581
+ const pluginEntryPath = await this.resolvePluginEntryPath(pluginRoot);
2582
+ const configPath = path.resolve(pluginRoot, ".codemation", "plugin-dev", "codemation.config.ts");
2583
+ await mkdir(path.dirname(configPath), { recursive: true });
2584
+ await writeFile(configPath, this.createConfigSource(configPath, pluginEntryPath), "utf8");
2585
+ return { configPath };
2586
+ }
2587
+ async resolvePluginEntryPath(pluginRoot) {
2588
+ const candidates = [
2589
+ path.resolve(pluginRoot, "codemation.plugin.ts"),
2590
+ path.resolve(pluginRoot, "codemation.plugin.js"),
2591
+ path.resolve(pluginRoot, "src", "codemation.plugin.ts"),
2592
+ path.resolve(pluginRoot, "src", "codemation.plugin.js")
2593
+ ];
2594
+ for (const candidate of candidates) if (await this.exists(candidate)) return candidate;
2595
+ throw new Error("Plugin config not found. Expected \"codemation.plugin.ts\" in the plugin root or \"src/\".");
2596
+ }
2597
+ createConfigSource(configPath, pluginEntryPath) {
2598
+ const relativeImportPath = this.toRelativeImportPath(configPath, pluginEntryPath);
2599
+ return [
2600
+ "import type { CodemationConfig } from \"@codemation/host\";",
2601
+ `import plugin from ${JSON.stringify(relativeImportPath)};`,
2602
+ "",
2603
+ "const sandbox = plugin.sandbox ?? {};",
2604
+ "const config: CodemationConfig = {",
2605
+ " ...sandbox,",
2606
+ " plugins: [...(sandbox.plugins ?? []), plugin],",
2607
+ "};",
2608
+ "",
2609
+ "export default config;",
2610
+ ""
2611
+ ].join("\n");
2612
+ }
2613
+ toRelativeImportPath(fromPath, targetPath) {
2614
+ const relativePath = path.relative(path.dirname(fromPath), targetPath).replace(/\\/g, "/");
2615
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
2616
+ }
2617
+ async exists(filePath) {
2618
+ try {
2619
+ await access(filePath);
2620
+ return true;
2621
+ } catch {
2622
+ return false;
2623
+ }
2618
2624
  }
2619
2625
  };
2620
2626
 
@@ -2870,10 +2876,11 @@ var DevSourceWatcherFactory = class {
2870
2876
  //#endregion
2871
2877
  //#region src/Program.ts
2872
2878
  var CliProgram = class {
2873
- constructor(buildOptionsParser, buildCommand, devCommand, serveWebCommand, serveWorkerCommand, dbMigrateCommand, userCreateCommand, userListCommand) {
2879
+ constructor(buildOptionsParser, buildCommand, devCommand, devPluginCommand, serveWebCommand, serveWorkerCommand, dbMigrateCommand, userCreateCommand, userListCommand) {
2874
2880
  this.buildOptionsParser = buildOptionsParser;
2875
2881
  this.buildCommand = buildCommand;
2876
2882
  this.devCommand = devCommand;
2883
+ this.devPluginCommand = devPluginCommand;
2877
2884
  this.serveWebCommand = serveWebCommand;
2878
2885
  this.serveWorkerCommand = serveWorkerCommand;
2879
2886
  this.dbMigrateCommand = dbMigrateCommand;
@@ -2893,6 +2900,9 @@ var CliProgram = class {
2893
2900
  watchFramework: opts.watchFramework === true
2894
2901
  });
2895
2902
  });
2903
+ program.command("dev:plugin").description("Start plugin sandbox development using `codemation.plugin.ts`.").option("--plugin-root <path>", "Path to the plugin project root (defaults to cwd)").action(async (opts) => {
2904
+ await this.devPluginCommand.execute({ pluginRoot: resolveConsumerRoot(opts.pluginRoot) });
2905
+ });
2896
2906
  const serve$1 = program.command("serve").description("Run production web or worker processes (no dev watchers).");
2897
2907
  serve$1.command("web").description("Start the packaged Codemation web host.").option("--consumer-root <path>", "Path to the consumer project root (defaults to cwd)").option("--no-source-maps", "Disable .js.map files for emitted workflow modules when this command runs the consumer build step.").option("--target <es2020|es2022>", "ECMAScript language version for emitted workflow JavaScript when building consumer output (default: es2022).", "es2022").action(async (opts) => {
2898
2908
  await this.serveWebCommand.execute(resolveConsumerRoot(opts.consumerRoot), this.buildOptionsParser.parse(opts));
@@ -3160,9 +3170,7 @@ var CliProgramFactory = class {
3160
3170
  const appConfigLoader = new AppConfigLoader();
3161
3171
  const pathResolver = new CliPathResolver();
3162
3172
  const pluginDiscovery = new CodemationPluginDiscovery();
3163
- const artifactsPublisher = new ConsumerBuildArtifactsPublisher();
3164
3173
  const tsRuntime = new TypeScriptRuntimeConfigurator();
3165
- const outputBuilderLoader = new ConsumerOutputBuilderLoader();
3166
3174
  const sourceMapNodeOptions = new SourceMapNodeOptions();
3167
3175
  const nextHostConsumerServerCommandFactory = new NextHostConsumerServerCommandFactory();
3168
3176
  const devSessionServices = new DevSessionServicesBuilder().build();
@@ -3172,8 +3180,8 @@ var CliProgramFactory = class {
3172
3180
  const userAdminCliOptionsParser = new UserAdminCliOptionsParser();
3173
3181
  const databaseMigrationsApplyService = new DatabaseMigrationsApplyService(cliLogger, new UserAdminConsumerDotenvLoader(), tsconfigPreparation, new CodemationConsumerConfigLoader(), new ConsumerDatabaseConnectionResolver(), new CliDatabaseUrlDescriptor(), hostPackageRoot, new PrismaMigrationDeployer());
3174
3182
  const buildOptionsParser = new ConsumerBuildOptionsParser();
3175
- const devConsumerPublishBootstrap = new DevConsumerPublishBootstrap(cliLogger, pluginDiscovery, artifactsPublisher, outputBuilderLoader, buildOptionsParser);
3176
- return new CliProgram(buildOptionsParser, new BuildCommand(cliLogger, pathResolver, pluginDiscovery, artifactsPublisher, tsRuntime, outputBuilderLoader), new DevCommand(pathResolver, tsRuntime, new DevLockFactory(), new DevSourceWatcherFactory(), cliLogger, devSessionServices, databaseMigrationsApplyService, new DevBootstrapSummaryFetcher(), new DevCliBannerRenderer(), devConsumerPublishBootstrap, new ConsumerEnvDotenvFilePredicate(), new DevTrackedProcessTreeKiller(), nextHostConsumerServerCommandFactory, new DevApiRuntimeFactory(devSessionServices.loopbackPortAllocator, appConfigLoader, pluginDiscovery), new CliDevProxyServerFactory(), new DevRebuildQueueFactory()), new ServeWebCommand(pathResolver, new CodemationConsumerConfigLoader(), pluginDiscovery, artifactsPublisher, tsRuntime, sourceMapNodeOptions, outputBuilderLoader, new ConsumerEnvLoader(), new ListenPortResolver(), nextHostConsumerServerCommandFactory, new CodemationFrontendAuthSnapshotFactory(), new FrontendAppConfigJsonCodec()), new ServeWorkerCommand(pathResolver, appConfigLoader, new AppContainerFactory()), new DbMigrateCommand(databaseMigrationsApplyService), new UserCreateCommand(new LocalUserCreator(userAdminBootstrap), userAdminCliOptionsParser), new UserListCommand(cliLogger, userAdminBootstrap, new CliDatabaseUrlDescriptor(), userAdminCliOptionsParser));
3183
+ const devCommand = new DevCommand(pathResolver, tsRuntime, new DevLockFactory(), new DevSourceWatcherFactory(), cliLogger, devSessionServices, databaseMigrationsApplyService, new DevBootstrapSummaryFetcher(), new DevCliBannerRenderer(), new ConsumerEnvDotenvFilePredicate(), new DevTrackedProcessTreeKiller(), nextHostConsumerServerCommandFactory, new DevApiRuntimeFactory(devSessionServices.loopbackPortAllocator, appConfigLoader, pluginDiscovery), new CliDevProxyServerFactory(), new DevRebuildQueueFactory());
3184
+ return new CliProgram(buildOptionsParser, new BuildCommand(cliLogger, pathResolver, new ConsumerOutputBuilderFactory(), tsRuntime), devCommand, new DevPluginCommand(new PluginDevConfigFactory(), devCommand), new ServeWebCommand(pathResolver, new CodemationConsumerConfigLoader(), tsRuntime, sourceMapNodeOptions, new ConsumerEnvLoader(), new ListenPortResolver(), nextHostConsumerServerCommandFactory), new ServeWorkerCommand(pathResolver, appConfigLoader, new AppContainerFactory()), new DbMigrateCommand(databaseMigrationsApplyService), new UserCreateCommand(new LocalUserCreator(userAdminBootstrap), userAdminCliOptionsParser), new UserListCommand(cliLogger, userAdminBootstrap, new CliDatabaseUrlDescriptor(), userAdminCliOptionsParser));
3177
3185
  }
3178
3186
  };
3179
3187