@cloudwerk/cli 0.13.0 → 0.15.0

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
@@ -7,9 +7,11 @@ import { program } from "commander";
7
7
  import * as path from "path";
8
8
  import * as fs from "fs";
9
9
  import * as os from "os";
10
- import { createServer } from "vite";
10
+ import { createServer, mergeConfig as mergeViteConfig } from "vite";
11
11
  import devServer from "@hono/vite-dev-server";
12
+ import cloudflareAdapter from "@hono/vite-dev-server/cloudflare";
12
13
  import cloudwerk from "@cloudwerk/vite-plugin";
14
+ import { loadConfig } from "@cloudwerk/core";
13
15
 
14
16
  // src/types.ts
15
17
  var CliError = class extends Error {
@@ -138,7 +140,9 @@ async function dev(pathArg, options) {
138
140
  "Port must be a number between 1 and 65535"
139
141
  );
140
142
  }
141
- const viteConfig = {
143
+ const config = await loadConfig(cwd);
144
+ logger.debug(`Loaded cloudwerk config`);
145
+ const baseViteConfig = {
142
146
  root: cwd,
143
147
  mode: "development",
144
148
  server: {
@@ -151,6 +155,7 @@ async function dev(pathArg, options) {
151
155
  verbose
152
156
  }),
153
157
  devServer({
158
+ adapter: cloudflareAdapter,
154
159
  entry: "virtual:cloudwerk/server-entry"
155
160
  })
156
161
  ],
@@ -158,6 +163,15 @@ async function dev(pathArg, options) {
158
163
  logLevel: verbose ? "info" : "warn",
159
164
  clearScreen: false
160
165
  };
166
+ let viteConfig = baseViteConfig;
167
+ if (config.vite) {
168
+ const userPlugins = config.vite.plugins;
169
+ const { plugins: _, ...userConfigWithoutPlugins } = config.vite;
170
+ viteConfig = mergeViteConfig(baseViteConfig, userConfigWithoutPlugins);
171
+ if (userPlugins) {
172
+ viteConfig.plugins = [...userPlugins, ...baseViteConfig.plugins ?? []];
173
+ }
174
+ }
161
175
  logger.debug(`Starting Vite dev server...`);
162
176
  const server = await createServer(viteConfig);
163
177
  await server.listen();
@@ -257,14 +271,15 @@ function setupGracefulShutdown(server, logger) {
257
271
  import { builtinModules } from "module";
258
272
  import * as path2 from "path";
259
273
  import * as fs2 from "fs";
260
- import { build as viteBuild } from "vite";
274
+ import { build as viteBuild, mergeConfig as mergeViteConfig2 } from "vite";
275
+ import { getPlatformProxy } from "wrangler";
261
276
  import cloudwerk2, { generateServerEntry } from "@cloudwerk/vite-plugin";
262
277
  import {
263
278
  scanRoutes,
264
279
  buildRouteManifest,
265
280
  resolveLayouts,
266
281
  resolveMiddleware,
267
- loadConfig,
282
+ loadConfig as loadConfig2,
268
283
  resolveRoutesPath,
269
284
  scanQueues,
270
285
  buildQueueManifest,
@@ -304,7 +319,7 @@ async function build(pathArg, options) {
304
319
  if (!fs2.existsSync(tempDir)) {
305
320
  fs2.mkdirSync(tempDir, { recursive: true });
306
321
  }
307
- const cloudwerkConfig = await loadConfig(cwd);
322
+ const cloudwerkConfig = await loadConfig2(cwd);
308
323
  const appDir = cloudwerkConfig.appDir;
309
324
  const routesDir = cloudwerkConfig.routesDir ?? "routes";
310
325
  const routesPath = resolveRoutesPath(routesDir, appDir, cwd);
@@ -364,7 +379,7 @@ async function build(pathArg, options) {
364
379
  fs2.writeFileSync(tempEntryPath, serverEntryCode);
365
380
  logger.debug(`Generated temp entry: ${tempEntryPath}`);
366
381
  logger.debug(`Building client assets...`);
367
- const clientConfig = {
382
+ const baseClientConfig = {
368
383
  root: cwd,
369
384
  mode: "production",
370
385
  logLevel: verbose ? "info" : "warn",
@@ -386,6 +401,15 @@ async function build(pathArg, options) {
386
401
  }
387
402
  }
388
403
  };
404
+ let clientConfig = baseClientConfig;
405
+ if (cloudwerkConfig.vite) {
406
+ const userPlugins = cloudwerkConfig.vite.plugins;
407
+ const { plugins: _, ...userConfigWithoutPlugins } = cloudwerkConfig.vite;
408
+ clientConfig = mergeViteConfig2(baseClientConfig, userConfigWithoutPlugins);
409
+ if (userPlugins) {
410
+ clientConfig.plugins = [...userPlugins, ...baseClientConfig.plugins ?? []];
411
+ }
412
+ }
389
413
  try {
390
414
  await viteBuild(clientConfig);
391
415
  logger.debug(`Client assets built successfully`);
@@ -395,7 +419,7 @@ async function build(pathArg, options) {
395
419
  }
396
420
  }
397
421
  logger.debug(`Building server bundle...`);
398
- const serverConfig = {
422
+ const baseServerConfig = {
399
423
  root: cwd,
400
424
  mode: "production",
401
425
  logLevel: verbose ? "info" : "warn",
@@ -430,8 +454,31 @@ async function build(pathArg, options) {
430
454
  conditions: ["workerd", "worker", "browser", "import", "module", "default"]
431
455
  }
432
456
  };
457
+ let serverConfig = baseServerConfig;
458
+ if (cloudwerkConfig.vite) {
459
+ const userPlugins = cloudwerkConfig.vite.plugins;
460
+ const { plugins: _, ...userConfigWithoutPlugins } = cloudwerkConfig.vite;
461
+ serverConfig = mergeViteConfig2(baseServerConfig, userConfigWithoutPlugins);
462
+ if (userPlugins) {
463
+ serverConfig.plugins = [...userPlugins, ...baseServerConfig.plugins ?? []];
464
+ }
465
+ }
433
466
  await viteBuild(serverConfig);
434
467
  logger.debug(`Server bundle built successfully`);
468
+ let ssgPaths = [];
469
+ if (options.ssg) {
470
+ logger.info(`Generating static pages...`);
471
+ ssgPaths = await generateStaticPages(
472
+ manifest,
473
+ cwd,
474
+ outputDir,
475
+ logger,
476
+ verbose
477
+ );
478
+ if (ssgPaths.length > 0) {
479
+ logger.debug(`Generated ${ssgPaths.length} static page(s)`);
480
+ }
481
+ }
435
482
  const buildDuration = Date.now() - startTime;
436
483
  const serverBundlePath = path2.join(outputDir, "index.js");
437
484
  const serverSize = fs2.existsSync(serverBundlePath) ? fs2.statSync(serverBundlePath).size : 0;
@@ -448,7 +495,7 @@ async function build(pathArg, options) {
448
495
  }
449
496
  }
450
497
  console.log();
451
- printBuildSummary(serverSize, clientSize, buildDuration, logger);
498
+ printBuildSummary(serverSize, clientSize, ssgPaths.length, buildDuration, logger);
452
499
  console.log();
453
500
  logger.success(`Build completed in ${buildDuration}ms`);
454
501
  } catch (error) {
@@ -477,7 +524,7 @@ async function build(pathArg, options) {
477
524
  }
478
525
  }
479
526
  }
480
- function printBuildSummary(serverSize, clientSize, buildDuration, logger) {
527
+ function printBuildSummary(serverSize, clientSize, ssgPageCount, buildDuration, logger) {
481
528
  logger.log("Build Output:");
482
529
  logger.log("");
483
530
  logger.log(" Server:");
@@ -488,6 +535,11 @@ function printBuildSummary(serverSize, clientSize, buildDuration, logger) {
488
535
  logger.log(` Total: ${formatSize(clientSize)}`);
489
536
  logger.log("");
490
537
  }
538
+ if (ssgPageCount > 0) {
539
+ logger.log(" Static Pages:");
540
+ logger.log(` Generated: ${ssgPageCount}`);
541
+ logger.log("");
542
+ }
491
543
  const totalSize = serverSize + clientSize;
492
544
  logger.log(` Total: ${formatSize(totalSize)}`);
493
545
  logger.log(` Duration: ${buildDuration}ms`);
@@ -501,6 +553,76 @@ function formatSize(bytes) {
501
553
  }
502
554
  return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
503
555
  }
556
+ async function generateStaticPages(_manifest, cwd, outputDir, logger, verbose) {
557
+ const generatedPaths = [];
558
+ const { env, dispose } = await getPlatformProxy({
559
+ configPath: path2.join(cwd, "wrangler.toml")
560
+ });
561
+ if (verbose) {
562
+ logger.debug(`SSG env bindings: ${Object.keys(env).join(", ")}`);
563
+ }
564
+ try {
565
+ const { createServer: createServer2 } = await import("vite");
566
+ const { toSSG } = await import("./ssg-DR2ZOVQ7.js");
567
+ const fsPromises = await import("fs/promises");
568
+ const vite = await createServer2({
569
+ root: cwd,
570
+ server: { middlewareMode: true },
571
+ appType: "custom",
572
+ logLevel: verbose ? "info" : "warn",
573
+ plugins: [cloudwerk2({ verbose })]
574
+ });
575
+ try {
576
+ const tempEntryPath = path2.join(cwd, ".cloudwerk-build", "_server-entry.ts");
577
+ const appModule = await vite.ssrLoadModule(
578
+ fs2.existsSync(tempEntryPath) ? tempEntryPath : "virtual:cloudwerk/server-entry"
579
+ );
580
+ const app = appModule.default;
581
+ if (!app || typeof app.fetch !== "function") {
582
+ logger.debug("No valid Hono app found for SSG");
583
+ return generatedPaths;
584
+ }
585
+ const originalFetch = app.fetch.bind(app);
586
+ app.fetch = (request, passedEnv, executionCtx) => {
587
+ const mergedEnv = { ...env, ...passedEnv };
588
+ if (verbose) {
589
+ const envKeys = Object.keys(mergedEnv);
590
+ logger.debug(`SSG fetch ${request.url} with env: ${envKeys.join(", ")}`);
591
+ }
592
+ return originalFetch(request, mergedEnv, executionCtx);
593
+ };
594
+ const staticDir = path2.join(outputDir, "static");
595
+ const result = await toSSG(app, fsPromises, {
596
+ dir: staticDir
597
+ // Only generate pages that have ssgParams middleware
598
+ // (pages with generateStaticParams export)
599
+ });
600
+ if (result.files) {
601
+ for (const file of result.files) {
602
+ const relativePath = path2.relative(staticDir, file);
603
+ const urlPath = "/" + relativePath.replace(/\/index\.html$/, "").replace(/\.html$/, "");
604
+ generatedPaths.push(urlPath || "/");
605
+ if (verbose) {
606
+ logger.debug(`Generated: ${urlPath || "/"} -> ${relativePath}`);
607
+ }
608
+ }
609
+ }
610
+ if (generatedPaths.length > 0) {
611
+ logger.info(`Generated ${generatedPaths.length} static page(s)`);
612
+ }
613
+ } finally {
614
+ await vite.close();
615
+ }
616
+ } catch (error) {
617
+ if (verbose) {
618
+ logger.debug(`SSG error: ${error instanceof Error ? error.message : String(error)}`);
619
+ }
620
+ logger.warn(`Static generation failed: ${error instanceof Error ? error.message : String(error)}`);
621
+ } finally {
622
+ await dispose();
623
+ }
624
+ return generatedPaths;
625
+ }
504
626
 
505
627
  // src/commands/deploy.ts
506
628
  import * as path3 from "path";
@@ -598,7 +720,7 @@ import pc2 from "picocolors";
598
720
  // src/utils/configWriter.ts
599
721
  import * as fs4 from "fs";
600
722
  import * as path4 from "path";
601
- import { loadConfig as loadConfig2 } from "@cloudwerk/core/build";
723
+ import { loadConfig as loadConfig3 } from "@cloudwerk/core/build";
602
724
  var CONFIG_FILE_NAMES = [
603
725
  "cloudwerk.config.ts",
604
726
  "cloudwerk.config.js",
@@ -3189,7 +3311,7 @@ async function bindingsGenerateTypes(options) {
3189
3311
  // src/commands/triggers.ts
3190
3312
  import pc9 from "picocolors";
3191
3313
  import {
3192
- loadConfig as loadConfig3,
3314
+ loadConfig as loadConfig4,
3193
3315
  scanTriggers,
3194
3316
  buildTriggerManifest,
3195
3317
  getTriggerSummary
@@ -3213,7 +3335,7 @@ async function triggers(options = {}) {
3213
3335
  try {
3214
3336
  const cwd = process.cwd();
3215
3337
  logger.debug("Loading configuration...");
3216
- const config = await loadConfig3(cwd);
3338
+ const config = await loadConfig4(cwd);
3217
3339
  const appDir = config.appDir;
3218
3340
  logger.debug(`Scanning for triggers in ${appDir}/triggers/...`);
3219
3341
  const scanResult = await scanTriggers(appDir, {
@@ -3309,7 +3431,7 @@ function getSourceInfo(trigger) {
3309
3431
  // src/commands/triggers/list.ts
3310
3432
  import pc10 from "picocolors";
3311
3433
  import {
3312
- loadConfig as loadConfig4,
3434
+ loadConfig as loadConfig5,
3313
3435
  scanTriggers as scanTriggers2,
3314
3436
  buildTriggerManifest as buildTriggerManifest2
3315
3437
  } from "@cloudwerk/core/build";
@@ -3328,7 +3450,7 @@ async function triggersList(options = {}) {
3328
3450
  try {
3329
3451
  const cwd = process.cwd();
3330
3452
  logger.debug("Loading configuration...");
3331
- const config = await loadConfig4(cwd);
3453
+ const config = await loadConfig5(cwd);
3332
3454
  const appDir = config.appDir;
3333
3455
  logger.debug(`Scanning for triggers in ${appDir}/triggers/...`);
3334
3456
  const scanResult = await scanTriggers2(appDir, {
@@ -3431,7 +3553,7 @@ function formatTriggerJson(trigger) {
3431
3553
  // src/commands/triggers/validate.ts
3432
3554
  import pc11 from "picocolors";
3433
3555
  import {
3434
- loadConfig as loadConfig5,
3556
+ loadConfig as loadConfig6,
3435
3557
  scanTriggers as scanTriggers3,
3436
3558
  buildTriggerManifest as buildTriggerManifest3,
3437
3559
  hasTriggerErrors,
@@ -3444,7 +3566,7 @@ async function triggersValidate(options = {}) {
3444
3566
  try {
3445
3567
  const cwd = process.cwd();
3446
3568
  logger.debug("Loading configuration...");
3447
- const config = await loadConfig5(cwd);
3569
+ const config = await loadConfig6(cwd);
3448
3570
  const appDir = config.appDir;
3449
3571
  logger.debug(`Scanning for triggers in ${appDir}/triggers/...`);
3450
3572
  const scanResult = await scanTriggers3(appDir, {
@@ -3519,7 +3641,7 @@ async function triggersValidate(options = {}) {
3519
3641
  // src/commands/triggers/generate.ts
3520
3642
  import pc12 from "picocolors";
3521
3643
  import {
3522
- loadConfig as loadConfig6,
3644
+ loadConfig as loadConfig7,
3523
3645
  scanTriggers as scanTriggers4,
3524
3646
  buildTriggerManifest as buildTriggerManifest4
3525
3647
  } from "@cloudwerk/core/build";
@@ -3881,7 +4003,7 @@ async function triggersGenerate(options = {}) {
3881
4003
  try {
3882
4004
  const cwd = process.cwd();
3883
4005
  logger.debug("Loading configuration...");
3884
- const config = await loadConfig6(cwd);
4006
+ const config = await loadConfig7(cwd);
3885
4007
  const appDir = config.appDir;
3886
4008
  logger.debug(`Scanning for triggers in ${appDir}/triggers/...`);
3887
4009
  const scanResult = await scanTriggers4(appDir, {
@@ -3977,6 +4099,1689 @@ async function triggersGenerate(options = {}) {
3977
4099
  }
3978
4100
  }
3979
4101
 
4102
+ // src/commands/objects.ts
4103
+ import pc13 from "picocolors";
4104
+ import {
4105
+ loadConfig as loadConfig8,
4106
+ scanDurableObjects,
4107
+ buildDurableObjectManifest
4108
+ } from "@cloudwerk/core/build";
4109
+ async function objects(options = {}) {
4110
+ const verbose = options.verbose ?? false;
4111
+ const logger = createLogger(verbose);
4112
+ try {
4113
+ const cwd = process.cwd();
4114
+ logger.debug("Loading configuration...");
4115
+ const config = await loadConfig8(cwd);
4116
+ const appDir = config.appDir;
4117
+ logger.debug(`Scanning for durable objects in ${appDir}/objects/...`);
4118
+ const scanResult = await scanDurableObjects(appDir, {
4119
+ extensions: config.extensions
4120
+ });
4121
+ const manifest = buildDurableObjectManifest(scanResult, appDir);
4122
+ console.log();
4123
+ console.log(pc13.bold("Cloudwerk Durable Objects"));
4124
+ console.log();
4125
+ const sqliteCount = manifest.durableObjects.filter((o) => o.sqlite).length;
4126
+ const regularCount = manifest.durableObjects.length - sqliteCount;
4127
+ console.log(pc13.dim(` Found ${manifest.durableObjects.length} durable objects:`));
4128
+ if (manifest.durableObjects.length > 0) {
4129
+ console.log(pc13.dim(` - ${sqliteCount} with SQLite storage`));
4130
+ console.log(pc13.dim(` - ${regularCount} with KV storage`));
4131
+ }
4132
+ console.log();
4133
+ if (manifest.durableObjects.length > 0) {
4134
+ for (const obj of manifest.durableObjects) {
4135
+ const storage = obj.sqlite ? pc13.yellow("SQLite") : pc13.green("KV");
4136
+ const methods = obj.methodNames.length > 0 ? pc13.dim(`${obj.methodNames.length} methods`) : pc13.dim("no methods");
4137
+ console.log(
4138
+ ` ${pc13.cyan(obj.className)} ${pc13.dim("(")}${storage}${pc13.dim(", ")}${methods}${pc13.dim(")")}`
4139
+ );
4140
+ }
4141
+ console.log();
4142
+ }
4143
+ console.log(pc13.bold("Commands:"));
4144
+ console.log();
4145
+ console.log(pc13.dim(" cloudwerk objects list ") + "List all durable objects");
4146
+ console.log(pc13.dim(" cloudwerk objects info <name> ") + "Show durable object details");
4147
+ console.log(pc13.dim(" cloudwerk objects migrations ") + "Show migration history");
4148
+ console.log(pc13.dim(" cloudwerk objects generate ") + "Regenerate wrangler config");
4149
+ console.log();
4150
+ if (manifest.durableObjects.length === 0) {
4151
+ console.log(pc13.bold("Quick Start:"));
4152
+ console.log();
4153
+ console.log(pc13.dim(" Create a durable object at app/objects/counter.ts:"));
4154
+ console.log();
4155
+ console.log(pc13.cyan(" import { defineDurableObject } from '@cloudwerk/durable-object'"));
4156
+ console.log();
4157
+ console.log(pc13.cyan(" interface CounterState { value: number }"));
4158
+ console.log();
4159
+ console.log(pc13.cyan(" export default defineDurableObject<CounterState>({"));
4160
+ console.log(pc13.cyan(" init: () => ({ value: 0 }),"));
4161
+ console.log();
4162
+ console.log(pc13.cyan(" methods: {"));
4163
+ console.log(pc13.cyan(" async increment(amount = 1) {"));
4164
+ console.log(pc13.cyan(" this.state.value += amount"));
4165
+ console.log(pc13.cyan(" return this.state.value"));
4166
+ console.log(pc13.cyan(" }"));
4167
+ console.log(pc13.cyan(" }"));
4168
+ console.log(pc13.cyan(" })"));
4169
+ console.log();
4170
+ console.log(pc13.dim(" Then use it in your routes:"));
4171
+ console.log();
4172
+ console.log(pc13.cyan(" import { durableObjects } from '@cloudwerk/core/bindings'"));
4173
+ console.log();
4174
+ console.log(pc13.cyan(" export async function POST(request, { params }) {"));
4175
+ console.log(pc13.cyan(" const id = durableObjects.Counter.idFromName(params.id)"));
4176
+ console.log(pc13.cyan(" const counter = durableObjects.Counter.get(id)"));
4177
+ console.log(pc13.cyan(" const value = await counter.increment(1)"));
4178
+ console.log(pc13.cyan(" return Response.json({ value })"));
4179
+ console.log(pc13.cyan(" }"));
4180
+ console.log();
4181
+ }
4182
+ } catch (error) {
4183
+ handleCommandError(error, verbose);
4184
+ }
4185
+ }
4186
+
4187
+ // src/commands/objects/list.ts
4188
+ import pc14 from "picocolors";
4189
+ import {
4190
+ loadConfig as loadConfig9,
4191
+ scanDurableObjects as scanDurableObjects2,
4192
+ buildDurableObjectManifest as buildDurableObjectManifest2
4193
+ } from "@cloudwerk/core/build";
4194
+ async function objectsList(options = {}) {
4195
+ const verbose = options.verbose ?? false;
4196
+ const format = options.format ?? "table";
4197
+ const logger = createLogger(verbose);
4198
+ try {
4199
+ const cwd = process.cwd();
4200
+ logger.debug("Loading configuration...");
4201
+ const config = await loadConfig9(cwd);
4202
+ const appDir = config.appDir;
4203
+ logger.debug(`Scanning for durable objects in ${appDir}/objects/...`);
4204
+ const scanResult = await scanDurableObjects2(appDir, {
4205
+ extensions: config.extensions
4206
+ });
4207
+ const manifest = buildDurableObjectManifest2(scanResult, appDir);
4208
+ if (format === "json") {
4209
+ console.log(JSON.stringify(manifest, null, 2));
4210
+ return;
4211
+ }
4212
+ console.log();
4213
+ console.log(
4214
+ pc14.bold("Durable Objects") + pc14.dim(` (${manifest.durableObjects.length} found):`)
4215
+ );
4216
+ console.log();
4217
+ if (manifest.durableObjects.length === 0) {
4218
+ console.log(pc14.dim(" No durable objects found."));
4219
+ console.log();
4220
+ console.log(pc14.dim(" Create a durable object at app/objects/counter.ts:"));
4221
+ console.log();
4222
+ console.log(pc14.dim(" import { defineDurableObject } from '@cloudwerk/durable-object'"));
4223
+ console.log(pc14.dim(" export default defineDurableObject({"));
4224
+ console.log(pc14.dim(" methods: {"));
4225
+ console.log(pc14.dim(" async increment(amount) { ... }"));
4226
+ console.log(pc14.dim(" }"));
4227
+ console.log(pc14.dim(" })"));
4228
+ console.log();
4229
+ return;
4230
+ }
4231
+ console.log(
4232
+ pc14.dim(
4233
+ " Class Binding Storage Methods Handlers"
4234
+ )
4235
+ );
4236
+ console.log(
4237
+ pc14.dim(
4238
+ " " + "\u2500".repeat(18) + " " + "\u2500".repeat(18) + " " + "\u2500".repeat(7) + " " + "\u2500".repeat(7) + " " + "\u2500".repeat(20)
4239
+ )
4240
+ );
4241
+ for (const obj of manifest.durableObjects) {
4242
+ const className = obj.className.padEnd(18);
4243
+ const binding = obj.bindingName.padEnd(18);
4244
+ const storage = obj.sqlite ? pc14.yellow("SQLite ") : pc14.green("KV ");
4245
+ const methods = obj.methodNames.length.toString().padEnd(7);
4246
+ const handlers = [];
4247
+ if (obj.hasFetch) handlers.push("fetch");
4248
+ if (obj.hasAlarm) handlers.push("alarm");
4249
+ if (obj.hasWebSocket) handlers.push("websocket");
4250
+ const handlersStr = handlers.length > 0 ? handlers.join(", ") : pc14.dim("none");
4251
+ console.log(
4252
+ ` ${pc14.cyan(className)} ${pc14.dim(binding)} ${storage} ${methods} ${handlersStr}`
4253
+ );
4254
+ }
4255
+ console.log();
4256
+ if (manifest.errors.length > 0) {
4257
+ console.log(pc14.red("Errors:"));
4258
+ for (const error of manifest.errors) {
4259
+ console.log(pc14.red(` - ${error.file}: ${error.message}`));
4260
+ }
4261
+ console.log();
4262
+ }
4263
+ if (manifest.warnings.length > 0) {
4264
+ console.log(pc14.yellow("Warnings:"));
4265
+ for (const warning of manifest.warnings) {
4266
+ console.log(pc14.yellow(` - ${warning.file}: ${warning.message}`));
4267
+ }
4268
+ console.log();
4269
+ }
4270
+ console.log(pc14.dim("Use 'cloudwerk objects info <name>' for details."));
4271
+ console.log(pc14.dim("Use 'cloudwerk objects generate' to update wrangler.toml."));
4272
+ console.log();
4273
+ } catch (error) {
4274
+ handleCommandError(error, verbose);
4275
+ }
4276
+ }
4277
+
4278
+ // src/commands/objects/info.ts
4279
+ import pc15 from "picocolors";
4280
+ import {
4281
+ loadConfig as loadConfig10,
4282
+ scanDurableObjects as scanDurableObjects3,
4283
+ buildDurableObjectManifest as buildDurableObjectManifest3
4284
+ } from "@cloudwerk/core/build";
4285
+ async function objectsInfo(name, options = {}) {
4286
+ const verbose = options.verbose ?? false;
4287
+ const format = options.format ?? "table";
4288
+ const logger = createLogger(verbose);
4289
+ try {
4290
+ const cwd = process.cwd();
4291
+ logger.debug("Loading configuration...");
4292
+ const config = await loadConfig10(cwd);
4293
+ const appDir = config.appDir;
4294
+ logger.debug(`Scanning for durable objects in ${appDir}/objects/...`);
4295
+ const scanResult = await scanDurableObjects3(appDir, {
4296
+ extensions: config.extensions
4297
+ });
4298
+ const manifest = buildDurableObjectManifest3(scanResult, appDir);
4299
+ const obj = manifest.durableObjects.find(
4300
+ (o) => o.name === name || o.className === name || o.bindingName === name
4301
+ );
4302
+ if (!obj) {
4303
+ console.log();
4304
+ console.log(pc15.red(`Durable object '${name}' not found.`));
4305
+ console.log();
4306
+ if (manifest.durableObjects.length > 0) {
4307
+ console.log(pc15.dim("Available durable objects:"));
4308
+ for (const o of manifest.durableObjects) {
4309
+ console.log(pc15.dim(` - ${o.className} (${o.name})`));
4310
+ }
4311
+ } else {
4312
+ console.log(pc15.dim("No durable objects found in app/objects/."));
4313
+ }
4314
+ console.log();
4315
+ return;
4316
+ }
4317
+ if (format === "json") {
4318
+ console.log(JSON.stringify(obj, null, 2));
4319
+ return;
4320
+ }
4321
+ console.log();
4322
+ console.log(pc15.bold(`Durable Object: ${obj.className}`));
4323
+ console.log();
4324
+ console.log(pc15.dim(" Configuration:"));
4325
+ console.log(` Name: ${pc15.cyan(obj.name)}`);
4326
+ console.log(` Class: ${pc15.cyan(obj.className)}`);
4327
+ console.log(` Binding: ${pc15.yellow(obj.bindingName)}`);
4328
+ console.log(` File: ${pc15.dim(obj.filePath)}`);
4329
+ console.log(
4330
+ ` Storage: ${obj.sqlite ? pc15.yellow("SQLite") : pc15.green("KV")}`
4331
+ );
4332
+ console.log();
4333
+ console.log(pc15.dim(" Handlers:"));
4334
+ console.log(
4335
+ ` fetch: ${obj.hasFetch ? pc15.green("yes") : pc15.dim("no")}`
4336
+ );
4337
+ console.log(
4338
+ ` alarm: ${obj.hasAlarm ? pc15.green("yes") : pc15.dim("no")}`
4339
+ );
4340
+ console.log(
4341
+ ` webSocket: ${obj.hasWebSocket ? pc15.green("yes") : pc15.dim("no")}`
4342
+ );
4343
+ console.log();
4344
+ console.log(pc15.dim(" RPC Methods:"));
4345
+ if (obj.methodNames.length > 0) {
4346
+ for (const method of obj.methodNames) {
4347
+ console.log(` - ${pc15.cyan(method)}()`);
4348
+ }
4349
+ } else {
4350
+ console.log(pc15.dim(" No RPC methods defined"));
4351
+ }
4352
+ console.log();
4353
+ console.log(pc15.dim(" wrangler.toml binding:"));
4354
+ console.log();
4355
+ console.log(pc15.cyan(" [durable_objects]"));
4356
+ console.log(pc15.cyan(" bindings = ["));
4357
+ console.log(
4358
+ pc15.cyan(
4359
+ ` { name = "${obj.bindingName}", class_name = "${obj.className}" }`
4360
+ )
4361
+ );
4362
+ console.log(pc15.cyan(" ]"));
4363
+ console.log();
4364
+ console.log(pc15.cyan(" [[migrations]]"));
4365
+ console.log(pc15.cyan(' tag = "v1"'));
4366
+ if (obj.sqlite) {
4367
+ console.log(pc15.cyan(` new_sqlite_classes = ["${obj.className}"]`));
4368
+ } else {
4369
+ console.log(pc15.cyan(` new_classes = ["${obj.className}"]`));
4370
+ }
4371
+ console.log();
4372
+ console.log(pc15.dim(" Usage in routes:"));
4373
+ console.log();
4374
+ console.log(pc15.cyan(" import { durableObjects } from '@cloudwerk/core/bindings'"));
4375
+ console.log();
4376
+ console.log(pc15.cyan(" export async function POST(request, { params }) {"));
4377
+ console.log(
4378
+ pc15.cyan(` const id = durableObjects.${obj.className}.idFromName(params.id)`)
4379
+ );
4380
+ console.log(
4381
+ pc15.cyan(` const stub = durableObjects.${obj.className}.get(id)`)
4382
+ );
4383
+ if (obj.methodNames.length > 0) {
4384
+ console.log(
4385
+ pc15.cyan(` const result = await stub.${obj.methodNames[0]}()`)
4386
+ );
4387
+ } else if (obj.hasFetch) {
4388
+ console.log(pc15.cyan(" const response = await stub.fetch(request)"));
4389
+ }
4390
+ console.log(pc15.cyan(" return Response.json({ result })"));
4391
+ console.log(pc15.cyan(" }"));
4392
+ console.log();
4393
+ } catch (error) {
4394
+ handleCommandError(error, verbose);
4395
+ }
4396
+ }
4397
+
4398
+ // src/commands/objects/migrations.ts
4399
+ import pc16 from "picocolors";
4400
+ import {
4401
+ loadConfig as loadConfig11,
4402
+ scanDurableObjects as scanDurableObjects4,
4403
+ buildDurableObjectManifest as buildDurableObjectManifest4
4404
+ } from "@cloudwerk/core/build";
4405
+
4406
+ // src/utils/durable-object-wrangler.ts
4407
+ import * as fs13 from "fs";
4408
+ import * as path14 from "path";
4409
+ function generateBindingsToml(entries, includeComments) {
4410
+ const lines = [];
4411
+ if (includeComments) {
4412
+ lines.push("# Durable object bindings");
4413
+ }
4414
+ lines.push("[durable_objects]");
4415
+ lines.push("bindings = [");
4416
+ for (let i = 0; i < entries.length; i++) {
4417
+ const entry = entries[i];
4418
+ const comma = i < entries.length - 1 ? "," : "";
4419
+ if (includeComments) {
4420
+ lines.push(` # ${entry.name} (from app/objects/${entry.filePath})`);
4421
+ }
4422
+ lines.push(` { name = "${entry.bindingName}", class_name = "${entry.className}" }${comma}`);
4423
+ }
4424
+ lines.push("]");
4425
+ return lines.join("\n");
4426
+ }
4427
+ function generateMigrationToml(entries, migrationTag, includeComments) {
4428
+ const sqliteClasses = entries.filter((e) => e.sqlite).map((e) => e.className);
4429
+ const regularClasses = entries.filter((e) => !e.sqlite).map((e) => e.className);
4430
+ if (sqliteClasses.length === 0 && regularClasses.length === 0) {
4431
+ return "";
4432
+ }
4433
+ const lines = [];
4434
+ if (includeComments) {
4435
+ lines.push("# Durable object migrations");
4436
+ }
4437
+ lines.push("[[migrations]]");
4438
+ lines.push(`tag = "${migrationTag}"`);
4439
+ if (sqliteClasses.length > 0) {
4440
+ const classList = sqliteClasses.map((c) => `"${c}"`).join(", ");
4441
+ lines.push(`new_sqlite_classes = [${classList}]`);
4442
+ }
4443
+ if (regularClasses.length > 0) {
4444
+ const classList = regularClasses.map((c) => `"${c}"`).join(", ");
4445
+ lines.push(`new_classes = [${classList}]`);
4446
+ }
4447
+ return lines.join("\n");
4448
+ }
4449
+ function generateDurableObjectToml(manifest, includeComments = true, migrationTag) {
4450
+ if (manifest.durableObjects.length === 0) {
4451
+ return "";
4452
+ }
4453
+ const lines = [];
4454
+ if (includeComments) {
4455
+ lines.push("# ============================================================================");
4456
+ lines.push("# Cloudwerk Durable Objects - Auto-generated from app/objects/");
4457
+ lines.push("# ============================================================================");
4458
+ lines.push("");
4459
+ }
4460
+ lines.push(generateBindingsToml(manifest.durableObjects, includeComments));
4461
+ lines.push("");
4462
+ const tag = migrationTag || generateMigrationTag();
4463
+ const migrationToml = generateMigrationToml(manifest.durableObjects, tag, includeComments);
4464
+ if (migrationToml) {
4465
+ lines.push(migrationToml);
4466
+ lines.push("");
4467
+ }
4468
+ return lines.join("\n").trim();
4469
+ }
4470
+ function generateMigrationTag() {
4471
+ const now = /* @__PURE__ */ new Date();
4472
+ const year = now.getFullYear();
4473
+ const month = String(now.getMonth() + 1).padStart(2, "0");
4474
+ const day = String(now.getDate()).padStart(2, "0");
4475
+ const hour = String(now.getHours()).padStart(2, "0");
4476
+ const min = String(now.getMinutes()).padStart(2, "0");
4477
+ return `v${year}${month}${day}${hour}${min}`;
4478
+ }
4479
+ function extractExistingMigrationTags(content) {
4480
+ const tags = [];
4481
+ const regex = /^\s*tag\s*=\s*"([^"]+)"/gm;
4482
+ let match;
4483
+ while ((match = regex.exec(content)) !== null) {
4484
+ tags.push(match[1]);
4485
+ }
4486
+ return tags;
4487
+ }
4488
+ var DO_SECTION_START = "# ============================================================================";
4489
+ var DO_SECTION_MARKER = "# Cloudwerk Durable Objects - Auto-generated";
4490
+ function hasDurableObjectSection(content) {
4491
+ return content.includes(DO_SECTION_MARKER);
4492
+ }
4493
+ function removeDurableObjectSection(content) {
4494
+ const startIndex = content.indexOf(DO_SECTION_START);
4495
+ if (startIndex === -1 || !content.includes(DO_SECTION_MARKER)) {
4496
+ return content;
4497
+ }
4498
+ const lines = content.split("\n");
4499
+ let sectionStartLine = -1;
4500
+ let sectionEndLine = lines.length;
4501
+ for (let i = 0; i < lines.length; i++) {
4502
+ if (lines[i].includes(DO_SECTION_MARKER)) {
4503
+ sectionStartLine = i > 0 && lines[i - 1].includes("===") ? i - 1 : i;
4504
+ break;
4505
+ }
4506
+ }
4507
+ if (sectionStartLine === -1) {
4508
+ return content;
4509
+ }
4510
+ for (let i = sectionStartLine + 2; i < lines.length; i++) {
4511
+ const line = lines[i].trim();
4512
+ if (line === "" || line.startsWith("#")) {
4513
+ continue;
4514
+ }
4515
+ if (line.startsWith("[[") && !line.includes("durable_objects") && !line.includes("migrations")) {
4516
+ sectionEndLine = i;
4517
+ break;
4518
+ }
4519
+ if (line.startsWith("[") && !line.startsWith("[[") && !line.includes("durable_objects")) {
4520
+ sectionEndLine = i;
4521
+ break;
4522
+ }
4523
+ }
4524
+ const before = lines.slice(0, sectionStartLine);
4525
+ const after = lines.slice(sectionEndLine);
4526
+ while (before.length > 0 && before[before.length - 1].trim() === "") {
4527
+ before.pop();
4528
+ }
4529
+ return [...before, "", ...after].join("\n");
4530
+ }
4531
+ function generateDurableObjectWrangler(cwd, manifest, options = {}) {
4532
+ const { dryRun = false, includeComments = true, migrationTag } = options;
4533
+ const wranglerPath = findWranglerToml(cwd) || path14.join(cwd, "wrangler.toml");
4534
+ const generatedToml = generateDurableObjectToml(manifest, includeComments, migrationTag);
4535
+ const result = {
4536
+ wranglerPath,
4537
+ changed: false,
4538
+ durableObjects: manifest.durableObjects.map((obj) => ({
4539
+ name: obj.name,
4540
+ bindingName: obj.bindingName,
4541
+ className: obj.className
4542
+ })),
4543
+ generatedToml
4544
+ };
4545
+ if (manifest.durableObjects.length === 0) {
4546
+ return result;
4547
+ }
4548
+ if (dryRun) {
4549
+ result.changed = true;
4550
+ return result;
4551
+ }
4552
+ let content = "";
4553
+ if (fs13.existsSync(wranglerPath)) {
4554
+ content = readWranglerTomlRaw(cwd);
4555
+ }
4556
+ if (hasDurableObjectSection(content)) {
4557
+ content = removeDurableObjectSection(content);
4558
+ }
4559
+ const newContent = content.trim() + "\n\n" + generatedToml + "\n";
4560
+ writeWranglerTomlRaw(cwd, newContent);
4561
+ result.changed = true;
4562
+ return result;
4563
+ }
4564
+
4565
+ // src/commands/objects/migrations.ts
4566
+ async function objectsMigrations(options = {}) {
4567
+ const verbose = options.verbose ?? false;
4568
+ const logger = createLogger(verbose);
4569
+ try {
4570
+ const cwd = process.cwd();
4571
+ logger.debug("Loading configuration...");
4572
+ const config = await loadConfig11(cwd);
4573
+ const appDir = config.appDir;
4574
+ logger.debug(`Scanning for durable objects in ${appDir}/objects/...`);
4575
+ const scanResult = await scanDurableObjects4(appDir, {
4576
+ extensions: config.extensions
4577
+ });
4578
+ const manifest = buildDurableObjectManifest4(scanResult, appDir);
4579
+ console.log();
4580
+ console.log(pc16.bold("Durable Object Migrations"));
4581
+ console.log();
4582
+ const wranglerPath = findWranglerToml(cwd);
4583
+ if (!wranglerPath) {
4584
+ console.log(pc16.dim(" No wrangler.toml found."));
4585
+ console.log();
4586
+ console.log(pc16.dim(" Run 'cloudwerk objects generate' to create configuration."));
4587
+ console.log();
4588
+ return;
4589
+ }
4590
+ const content = readWranglerTomlRaw(cwd);
4591
+ const tags = extractExistingMigrationTags(content);
4592
+ if (tags.length === 0) {
4593
+ console.log(pc16.dim(" No migrations found in wrangler.toml."));
4594
+ console.log();
4595
+ if (manifest.durableObjects.length > 0) {
4596
+ console.log(pc16.dim(" Run 'cloudwerk objects generate' to create migrations."));
4597
+ console.log();
4598
+ }
4599
+ return;
4600
+ }
4601
+ console.log(pc16.dim(` Found ${tags.length} migration(s):`));
4602
+ console.log();
4603
+ for (let i = 0; i < tags.length; i++) {
4604
+ const tag = tags[i];
4605
+ const isLatest = i === tags.length - 1;
4606
+ console.log(
4607
+ ` ${isLatest ? pc16.green("\u25CF") : pc16.dim("\u25CB")} ${pc16.cyan(tag)}${isLatest ? pc16.green(" (current)") : ""}`
4608
+ );
4609
+ }
4610
+ console.log();
4611
+ console.log(pc16.dim(" Current durable objects:"));
4612
+ console.log();
4613
+ if (manifest.durableObjects.length === 0) {
4614
+ console.log(pc16.dim(" No durable objects defined."));
4615
+ } else {
4616
+ for (const obj of manifest.durableObjects) {
4617
+ const storage = obj.sqlite ? pc16.yellow("SQLite") : pc16.green("KV");
4618
+ console.log(` ${pc16.cyan(obj.className)} ${pc16.dim("(")}${storage}${pc16.dim(")")}`);
4619
+ }
4620
+ }
4621
+ console.log();
4622
+ console.log(pc16.dim(" Tips:"));
4623
+ console.log(pc16.dim(" - Migrations are applied automatically on deploy"));
4624
+ console.log(pc16.dim(" - Each tag should be unique and incremental"));
4625
+ console.log(
4626
+ pc16.dim(" - Deleting a class will delete all data for that class")
4627
+ );
4628
+ console.log();
4629
+ } catch (error) {
4630
+ handleCommandError(error, verbose);
4631
+ }
4632
+ }
4633
+
4634
+ // src/commands/objects/generate.ts
4635
+ import pc17 from "picocolors";
4636
+ import {
4637
+ loadConfig as loadConfig12,
4638
+ scanDurableObjects as scanDurableObjects5,
4639
+ buildDurableObjectManifest as buildDurableObjectManifest5
4640
+ } from "@cloudwerk/core/build";
4641
+
4642
+ // src/utils/durable-object-type-generator.ts
4643
+ import * as fs14 from "fs";
4644
+ import * as path15 from "path";
4645
+ var CLOUDWERK_TYPES_DIR3 = ".cloudwerk/types";
4646
+ var DURABLE_OBJECTS_DTS = "durable-objects.d.ts";
4647
+ function generateDurableObjectTypes(cwd, manifest, options = {}) {
4648
+ const includeTimestamp = options.includeTimestamp ?? true;
4649
+ const typesDir = path15.join(cwd, CLOUDWERK_TYPES_DIR3);
4650
+ fs14.mkdirSync(typesDir, { recursive: true });
4651
+ const dtsPath = path15.join(typesDir, DURABLE_OBJECTS_DTS);
4652
+ const dtsContent = generateDurableObjectsDts(manifest.durableObjects, includeTimestamp);
4653
+ fs14.writeFileSync(dtsPath, dtsContent, "utf-8");
4654
+ const objectInfo = manifest.durableObjects.map((obj) => ({
4655
+ name: obj.name,
4656
+ bindingName: obj.bindingName,
4657
+ className: obj.className
4658
+ }));
4659
+ return {
4660
+ typesDir,
4661
+ file: dtsPath,
4662
+ objectCount: manifest.durableObjects.length,
4663
+ durableObjects: objectInfo
4664
+ };
4665
+ }
4666
+ function generateDurableObjectsDts(durableObjects, includeTimestamp) {
4667
+ const lines = [];
4668
+ lines.push("// Auto-generated by cloudwerk objects - DO NOT EDIT");
4669
+ if (includeTimestamp) {
4670
+ lines.push(`// Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}`);
4671
+ }
4672
+ lines.push("//");
4673
+ lines.push("// This file provides type information for durable object namespaces");
4674
+ lines.push('// Add ".cloudwerk/types" to your tsconfig.json include array');
4675
+ lines.push("");
4676
+ lines.push("import type { DurableObjectNamespace, DurableObjectStub } from '@cloudwerk/core/bindings'");
4677
+ lines.push("");
4678
+ lines.push("// ============================================================================");
4679
+ lines.push("// Durable Object Stub Interfaces");
4680
+ lines.push("// ============================================================================");
4681
+ lines.push("//");
4682
+ lines.push("// Define method signatures for your durable objects.");
4683
+ lines.push("// The RPC methods from your defineDurableObject() will be available on stubs.");
4684
+ lines.push("//");
4685
+ lines.push("");
4686
+ for (const obj of durableObjects) {
4687
+ lines.push(`/** Stub interface for the '${obj.name}' durable object */`);
4688
+ lines.push(`interface ${obj.className}Stub extends DurableObjectStub {`);
4689
+ if (obj.methodNames.length > 0) {
4690
+ lines.push(" // RPC methods defined in app/objects/" + obj.filePath);
4691
+ for (const methodName of obj.methodNames) {
4692
+ lines.push(` ${methodName}(...args: unknown[]): Promise<unknown>`);
4693
+ }
4694
+ } else {
4695
+ lines.push(" // No RPC methods defined - add methods to defineDurableObject({ methods: { ... } })");
4696
+ }
4697
+ lines.push("}");
4698
+ lines.push("");
4699
+ }
4700
+ lines.push("// ============================================================================");
4701
+ lines.push("// Durable Object Namespace Interfaces");
4702
+ lines.push("// ============================================================================");
4703
+ lines.push("");
4704
+ for (const obj of durableObjects) {
4705
+ lines.push(`/** Namespace for the '${obj.name}' durable object (binding: ${obj.bindingName}) */`);
4706
+ lines.push(`interface ${obj.className}Namespace extends DurableObjectNamespace<${obj.className}Stub> {`);
4707
+ lines.push(` get(id: import('@cloudwerk/core/bindings').DurableObjectId): ${obj.className}Stub`);
4708
+ lines.push("}");
4709
+ lines.push("");
4710
+ }
4711
+ lines.push("// ============================================================================");
4712
+ lines.push("// Combined Durable Objects Interface");
4713
+ lines.push("// ============================================================================");
4714
+ lines.push("");
4715
+ lines.push("interface CloudwerkDurableObjects {");
4716
+ for (const obj of durableObjects) {
4717
+ lines.push(` /** Durable object namespace for '${obj.name}' (binding: ${obj.bindingName}) */`);
4718
+ lines.push(` ${obj.className}: ${obj.className}Namespace`);
4719
+ }
4720
+ lines.push("}");
4721
+ lines.push("");
4722
+ lines.push("// ============================================================================");
4723
+ lines.push("// Module Augmentation");
4724
+ lines.push("// ============================================================================");
4725
+ lines.push("");
4726
+ lines.push("declare module '@cloudwerk/core/bindings' {");
4727
+ lines.push(" /** Typed durable object namespaces based on app/objects/ definitions */");
4728
+ lines.push(" export const durableObjects: CloudwerkDurableObjects");
4729
+ lines.push("");
4730
+ lines.push(" /** Get a typed durable object namespace by name */");
4731
+ lines.push(" export function getDurableObject<K extends keyof CloudwerkDurableObjects>(");
4732
+ lines.push(" name: K");
4733
+ lines.push(" ): CloudwerkDurableObjects[K]");
4734
+ lines.push("}");
4735
+ lines.push("");
4736
+ lines.push("// ============================================================================");
4737
+ lines.push("// Env Type Extension");
4738
+ lines.push("// ============================================================================");
4739
+ lines.push("//");
4740
+ lines.push("// Extend your Env type with these bindings:");
4741
+ lines.push("//");
4742
+ lines.push("// interface Env {");
4743
+ for (const obj of durableObjects) {
4744
+ lines.push(`// ${obj.bindingName}: DurableObjectNamespace<${obj.className}Stub>`);
4745
+ }
4746
+ lines.push("// }");
4747
+ lines.push("");
4748
+ return lines.join("\n");
4749
+ }
4750
+
4751
+ // src/commands/objects/generate.ts
4752
+ async function objectsGenerate(options = {}) {
4753
+ const verbose = options.verbose ?? false;
4754
+ const dryRun = options.dryRun ?? false;
4755
+ const skipTypes = options.skipTypes ?? false;
4756
+ const logger = createLogger(verbose);
4757
+ try {
4758
+ const cwd = process.cwd();
4759
+ logger.debug("Loading configuration...");
4760
+ const config = await loadConfig12(cwd);
4761
+ const appDir = config.appDir;
4762
+ logger.debug(`Scanning for durable objects in ${appDir}/objects/...`);
4763
+ const scanResult = await scanDurableObjects5(appDir, {
4764
+ extensions: config.extensions
4765
+ });
4766
+ const manifest = buildDurableObjectManifest5(scanResult, appDir);
4767
+ console.log();
4768
+ console.log(pc17.bold("Generating Durable Object Configuration"));
4769
+ console.log();
4770
+ if (manifest.durableObjects.length === 0) {
4771
+ console.log(pc17.dim(" No durable objects found."));
4772
+ console.log();
4773
+ console.log(pc17.dim(" Create a durable object at app/objects/counter.ts first."));
4774
+ console.log();
4775
+ return;
4776
+ }
4777
+ console.log(pc17.dim(` Found ${manifest.durableObjects.length} durable object(s):`));
4778
+ for (const obj of manifest.durableObjects) {
4779
+ const storage = obj.sqlite ? pc17.yellow("SQLite") : pc17.green("KV");
4780
+ console.log(` - ${pc17.cyan(obj.className)} ${pc17.dim("(")}${storage}${pc17.dim(")")}`);
4781
+ }
4782
+ console.log();
4783
+ if (dryRun) {
4784
+ console.log(pc17.yellow(" Dry run - no files will be written."));
4785
+ console.log();
4786
+ }
4787
+ console.log(pc17.dim(" Generating wrangler.toml bindings..."));
4788
+ const wranglerResult = generateDurableObjectWrangler(cwd, manifest, {
4789
+ dryRun,
4790
+ includeComments: true
4791
+ });
4792
+ if (wranglerResult.changed) {
4793
+ console.log(pc17.green(` \u2713 Updated ${wranglerResult.wranglerPath}`));
4794
+ } else {
4795
+ console.log(pc17.dim(" No changes needed"));
4796
+ }
4797
+ if (!skipTypes) {
4798
+ console.log();
4799
+ console.log(pc17.dim(" Generating TypeScript types..."));
4800
+ if (!dryRun) {
4801
+ const typesResult = generateDurableObjectTypes(cwd, manifest);
4802
+ console.log(pc17.green(` \u2713 Generated ${typesResult.file}`));
4803
+ } else {
4804
+ console.log(pc17.dim(" Would generate .cloudwerk/types/durable-objects.d.ts"));
4805
+ }
4806
+ }
4807
+ console.log();
4808
+ if (verbose || dryRun) {
4809
+ console.log(pc17.dim(" Generated wrangler.toml section:"));
4810
+ console.log();
4811
+ for (const line of wranglerResult.generatedToml.split("\n")) {
4812
+ console.log(pc17.cyan(` ${line}`));
4813
+ }
4814
+ console.log();
4815
+ }
4816
+ if (manifest.errors.length > 0) {
4817
+ console.log(pc17.red(" Errors:"));
4818
+ for (const error of manifest.errors) {
4819
+ console.log(pc17.red(` - ${error.file}: ${error.message}`));
4820
+ }
4821
+ console.log();
4822
+ }
4823
+ if (manifest.warnings.length > 0) {
4824
+ console.log(pc17.yellow(" Warnings:"));
4825
+ for (const warning of manifest.warnings) {
4826
+ console.log(pc17.yellow(` - ${warning.file}: ${warning.message}`));
4827
+ }
4828
+ console.log();
4829
+ }
4830
+ if (!dryRun) {
4831
+ console.log(pc17.green(" Configuration generated successfully!"));
4832
+ console.log();
4833
+ console.log(pc17.dim(" Next steps:"));
4834
+ console.log(pc17.dim(" 1. Review wrangler.toml for correctness"));
4835
+ console.log(pc17.dim(" 2. Add .cloudwerk/types to your tsconfig.json include"));
4836
+ console.log(pc17.dim(" 3. Run 'pnpm dev' to test locally"));
4837
+ console.log(pc17.dim(" 4. Run 'pnpm deploy' to deploy to Cloudflare"));
4838
+ console.log();
4839
+ }
4840
+ } catch (error) {
4841
+ handleCommandError(error, verbose);
4842
+ }
4843
+ }
4844
+
4845
+ // src/commands/services.ts
4846
+ import pc18 from "picocolors";
4847
+ import { loadConfig as loadConfig13, scanServices as scanServices2, buildServiceManifest as buildServiceManifest2 } from "@cloudwerk/core/build";
4848
+ async function services(options = {}) {
4849
+ const verbose = options.verbose ?? false;
4850
+ const logger = createLogger(verbose);
4851
+ try {
4852
+ const cwd = process.cwd();
4853
+ logger.debug("Loading configuration...");
4854
+ const config = await loadConfig13(cwd);
4855
+ const appDir = config.appDir;
4856
+ logger.debug(`Scanning for services in ${appDir}/services/...`);
4857
+ const scanResult = await scanServices2(appDir, { extensions: config.extensions });
4858
+ const manifest = buildServiceManifest2(scanResult, appDir);
4859
+ console.log();
4860
+ console.log(pc18.bold("Cloudwerk Services"));
4861
+ console.log();
4862
+ const localCount = manifest.services.filter((s) => s.mode === "local").length;
4863
+ const extractedCount = manifest.services.filter((s) => s.mode === "extracted").length;
4864
+ console.log(pc18.dim(` Found ${manifest.services.length} services:`));
4865
+ if (manifest.services.length > 0) {
4866
+ console.log(pc18.dim(` - ${localCount} local (direct calls)`));
4867
+ console.log(pc18.dim(` - ${extractedCount} extracted (separate Workers)`));
4868
+ }
4869
+ console.log();
4870
+ if (manifest.services.length > 0) {
4871
+ for (const service of manifest.services) {
4872
+ const mode = service.mode === "extracted" ? pc18.yellow("extracted") : pc18.green("local");
4873
+ console.log(` ${pc18.cyan(service.name)} ${pc18.dim("(")}${mode}${pc18.dim(")")}`);
4874
+ }
4875
+ console.log();
4876
+ }
4877
+ console.log(pc18.bold("Commands:"));
4878
+ console.log();
4879
+ console.log(pc18.dim(" cloudwerk services list ") + "List all services");
4880
+ console.log(pc18.dim(" cloudwerk services info <name> ") + "Show service details");
4881
+ console.log(pc18.dim(" cloudwerk services extract <name> ") + "Extract to separate Worker");
4882
+ console.log(pc18.dim(" cloudwerk services inline <name> ") + "Convert back to local mode");
4883
+ console.log(pc18.dim(" cloudwerk services deploy <name> ") + "Deploy extracted service");
4884
+ console.log(pc18.dim(" cloudwerk services status ") + "Show all services status");
4885
+ console.log();
4886
+ if (manifest.services.length === 0) {
4887
+ console.log(pc18.bold("Quick Start:"));
4888
+ console.log();
4889
+ console.log(pc18.dim(" Create a service at app/services/<name>/service.ts:"));
4890
+ console.log();
4891
+ console.log(pc18.cyan(" import { defineService } from '@cloudwerk/service'"));
4892
+ console.log();
4893
+ console.log(pc18.cyan(" export default defineService({"));
4894
+ console.log(pc18.cyan(" methods: {"));
4895
+ console.log(pc18.cyan(" async send({ to, subject, body }) {"));
4896
+ console.log(pc18.cyan(" // Your service logic here"));
4897
+ console.log(pc18.cyan(" return { success: true }"));
4898
+ console.log(pc18.cyan(" }"));
4899
+ console.log(pc18.cyan(" }"));
4900
+ console.log(pc18.cyan(" })"));
4901
+ console.log();
4902
+ console.log(pc18.dim(" Then use it in your routes:"));
4903
+ console.log();
4904
+ console.log(pc18.cyan(" import { services } from '@cloudwerk/core/bindings'"));
4905
+ console.log();
4906
+ console.log(pc18.cyan(" export async function POST() {"));
4907
+ console.log(pc18.cyan(" const result = await services.email.send({ to: '...' })"));
4908
+ console.log(pc18.cyan(" return json(result)"));
4909
+ console.log(pc18.cyan(" }"));
4910
+ console.log();
4911
+ }
4912
+ } catch (error) {
4913
+ handleCommandError(error, verbose);
4914
+ }
4915
+ }
4916
+
4917
+ // src/commands/services/list.ts
4918
+ import pc19 from "picocolors";
4919
+ import { loadConfig as loadConfig14, scanServices as scanServices3, buildServiceManifest as buildServiceManifest3 } from "@cloudwerk/core/build";
4920
+ async function servicesList(options = {}) {
4921
+ const verbose = options.verbose ?? false;
4922
+ const format = options.format ?? "table";
4923
+ const logger = createLogger(verbose);
4924
+ try {
4925
+ const cwd = process.cwd();
4926
+ logger.debug("Loading configuration...");
4927
+ const config = await loadConfig14(cwd);
4928
+ const appDir = config.appDir;
4929
+ logger.debug(`Scanning for services in ${appDir}/services/...`);
4930
+ const scanResult = await scanServices3(appDir, { extensions: config.extensions });
4931
+ const manifest = buildServiceManifest3(scanResult, appDir);
4932
+ if (format === "json") {
4933
+ console.log(JSON.stringify(manifest, null, 2));
4934
+ return;
4935
+ }
4936
+ console.log();
4937
+ console.log(pc19.bold("Services") + pc19.dim(` (${manifest.services.length} found):`));
4938
+ console.log();
4939
+ if (manifest.services.length === 0) {
4940
+ console.log(pc19.dim(" No services found."));
4941
+ console.log();
4942
+ console.log(pc19.dim(" Create a service at app/services/<name>/service.ts:"));
4943
+ console.log();
4944
+ console.log(pc19.dim(" import { defineService } from '@cloudwerk/service'"));
4945
+ console.log(pc19.dim(" export default defineService({"));
4946
+ console.log(pc19.dim(" methods: {"));
4947
+ console.log(pc19.dim(" async myMethod(params) { ... }"));
4948
+ console.log(pc19.dim(" }"));
4949
+ console.log(pc19.dim(" })"));
4950
+ console.log();
4951
+ return;
4952
+ }
4953
+ console.log(pc19.dim(" Name Mode Methods Bindings"));
4954
+ console.log(pc19.dim(" " + "\u2500".repeat(16) + " " + "\u2500".repeat(9) + " " + "\u2500".repeat(7) + " " + "\u2500".repeat(20)));
4955
+ for (const service of manifest.services) {
4956
+ const name = service.name.padEnd(16);
4957
+ const mode = service.mode === "extracted" ? pc19.yellow("extracted") : pc19.green("local ");
4958
+ const methods = service.methodNames.length.toString().padEnd(7);
4959
+ const bindings2 = service.requiredBindings.length > 0 ? service.requiredBindings.join(", ") : pc19.dim("none");
4960
+ console.log(` ${pc19.cyan(name)} ${mode} ${methods} ${bindings2}`);
4961
+ }
4962
+ console.log();
4963
+ if (manifest.errors.length > 0) {
4964
+ console.log(pc19.red("Errors:"));
4965
+ for (const error of manifest.errors) {
4966
+ console.log(pc19.red(` - ${error.file}: ${error.message}`));
4967
+ }
4968
+ console.log();
4969
+ }
4970
+ if (manifest.warnings.length > 0) {
4971
+ console.log(pc19.yellow("Warnings:"));
4972
+ for (const warning of manifest.warnings) {
4973
+ console.log(pc19.yellow(` - ${warning.file}: ${warning.message}`));
4974
+ }
4975
+ console.log();
4976
+ }
4977
+ console.log(pc19.dim("Use 'cloudwerk services info <name>' for details."));
4978
+ console.log(pc19.dim("Use 'cloudwerk services extract <name>' to extract a service."));
4979
+ console.log();
4980
+ } catch (error) {
4981
+ handleCommandError(error, verbose);
4982
+ }
4983
+ }
4984
+
4985
+ // src/commands/services/info.ts
4986
+ import pc20 from "picocolors";
4987
+ import { loadConfig as loadConfig15, scanServices as scanServices4, buildServiceManifest as buildServiceManifest4 } from "@cloudwerk/core/build";
4988
+ async function servicesInfo(serviceName, options = {}) {
4989
+ const verbose = options.verbose ?? false;
4990
+ const format = options.format ?? "text";
4991
+ const logger = createLogger(verbose);
4992
+ try {
4993
+ const cwd = process.cwd();
4994
+ logger.debug("Loading configuration...");
4995
+ const config = await loadConfig15(cwd);
4996
+ const appDir = config.appDir;
4997
+ logger.debug(`Scanning for services in ${appDir}/services/...`);
4998
+ const scanResult = await scanServices4(appDir, { extensions: config.extensions });
4999
+ const manifest = buildServiceManifest4(scanResult, appDir);
5000
+ const service = manifest.services.find((s) => s.name === serviceName);
5001
+ if (!service) {
5002
+ const available = manifest.services.map((s) => s.name);
5003
+ throw new CliError(
5004
+ `Service '${serviceName}' not found`,
5005
+ "ENOENT",
5006
+ available.length > 0 ? `Available services: ${available.join(", ")}` : "No services found in app/services/"
5007
+ );
5008
+ }
5009
+ if (format === "json") {
5010
+ console.log(JSON.stringify(service, null, 2));
5011
+ return;
5012
+ }
5013
+ console.log();
5014
+ console.log(pc20.bold(`Service: ${service.name}`));
5015
+ console.log();
5016
+ console.log(pc20.dim(" Location: ") + service.filePath);
5017
+ console.log(pc20.dim(" Mode: ") + (service.mode === "extracted" ? pc20.yellow("extracted") : pc20.green("local")));
5018
+ console.log(pc20.dim(" Binding: ") + service.bindingName);
5019
+ console.log(pc20.dim(" Worker Name: ") + service.workerName);
5020
+ console.log(pc20.dim(" Entrypoint: ") + service.entrypointClass);
5021
+ console.log();
5022
+ console.log(pc20.dim(" Methods:"));
5023
+ if (service.methodNames.length > 0) {
5024
+ for (const method of service.methodNames) {
5025
+ console.log(` - ${pc20.cyan(method)}()`);
5026
+ }
5027
+ } else {
5028
+ console.log(pc20.dim(" (methods will be detected after service is loaded)"));
5029
+ }
5030
+ console.log();
5031
+ console.log(pc20.dim(" Required Bindings:"));
5032
+ if (service.requiredBindings.length > 0) {
5033
+ for (const binding of service.requiredBindings) {
5034
+ console.log(` - ${binding}`);
5035
+ }
5036
+ } else {
5037
+ console.log(pc20.dim(" none configured"));
5038
+ }
5039
+ console.log();
5040
+ console.log(pc20.dim(" Hooks: ") + (service.hasHooks ? pc20.green("yes") : pc20.dim("no")));
5041
+ console.log();
5042
+ if (service.mode === "local") {
5043
+ console.log(pc20.dim("Actions:"));
5044
+ console.log(pc20.dim(` Extract: cloudwerk services extract ${serviceName}`));
5045
+ } else {
5046
+ console.log(pc20.dim("Actions:"));
5047
+ console.log(pc20.dim(` Inline: cloudwerk services inline ${serviceName}`));
5048
+ console.log(pc20.dim(` Deploy: cloudwerk services deploy ${serviceName}`));
5049
+ }
5050
+ console.log();
5051
+ } catch (error) {
5052
+ handleCommandError(error, verbose);
5053
+ }
5054
+ }
5055
+
5056
+ // src/commands/services/extract.ts
5057
+ import pc21 from "picocolors";
5058
+ import { loadConfig as loadConfig16, scanServices as scanServices5, buildServiceManifest as buildServiceManifest5 } from "@cloudwerk/core/build";
5059
+
5060
+ // src/utils/service-worker-generator.ts
5061
+ import * as fs15 from "fs";
5062
+ import * as path16 from "path";
5063
+ var EXTRACTED_DIR = ".cloudwerk/extracted";
5064
+ function generateServiceWorker(cwd, service, options = {}) {
5065
+ const includeTimestamp = options.includeTimestamp ?? true;
5066
+ const workerDir = path16.join(cwd, EXTRACTED_DIR, service.workerName);
5067
+ fs15.mkdirSync(workerDir, { recursive: true });
5068
+ const workerPath = path16.join(workerDir, "worker.ts");
5069
+ const workerContent = generateWorkerEntrypoint(cwd, service, includeTimestamp);
5070
+ fs15.writeFileSync(workerPath, workerContent, "utf-8");
5071
+ const wranglerPath = path16.join(workerDir, "wrangler.toml");
5072
+ const wranglerContent = generateWorkerWranglerToml(service, includeTimestamp);
5073
+ fs15.writeFileSync(wranglerPath, wranglerContent, "utf-8");
5074
+ return {
5075
+ workerDir,
5076
+ workerFile: workerPath,
5077
+ wranglerFile: wranglerPath,
5078
+ service
5079
+ };
5080
+ }
5081
+ function generateWorkerEntrypoint(cwd, service, includeTimestamp) {
5082
+ const lines = [];
5083
+ const workerDir = path16.join(cwd, EXTRACTED_DIR, service.workerName);
5084
+ const relativePath = path16.relative(workerDir, service.absolutePath).replace(/\\/g, "/").replace(/\.tsx?$/, "");
5085
+ lines.push("// Auto-generated by cloudwerk services - DO NOT EDIT");
5086
+ if (includeTimestamp) {
5087
+ lines.push(`// Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}`);
5088
+ }
5089
+ lines.push("//");
5090
+ lines.push(`// WorkerEntrypoint wrapper for ${service.name} service`);
5091
+ lines.push(`// Source: ${service.filePath}`);
5092
+ lines.push("");
5093
+ lines.push("import { WorkerEntrypoint } from 'cloudflare:workers'");
5094
+ lines.push(`import service from '${relativePath}'`);
5095
+ lines.push("");
5096
+ lines.push("// Environment bindings for this service");
5097
+ lines.push("interface Env {");
5098
+ if (service.requiredBindings.length > 0) {
5099
+ for (const binding of service.requiredBindings) {
5100
+ lines.push(` ${binding}: unknown`);
5101
+ }
5102
+ } else {
5103
+ lines.push(" // Add binding types here");
5104
+ }
5105
+ lines.push("}");
5106
+ lines.push("");
5107
+ lines.push("/**");
5108
+ lines.push(` * ${service.entrypointClass} - WorkerEntrypoint for ${service.name} service`);
5109
+ lines.push(" *");
5110
+ lines.push(" * This class wraps the service methods and handles lifecycle hooks.");
5111
+ lines.push(" * Cloudflare's service binding RPC will call methods directly on this class.");
5112
+ lines.push(" */");
5113
+ lines.push(`export class ${service.entrypointClass} extends WorkerEntrypoint<Env> {`);
5114
+ lines.push(" private initialized = false");
5115
+ lines.push("");
5116
+ lines.push(" /**");
5117
+ lines.push(" * Initialize the service (runs onInit hook once).");
5118
+ lines.push(" */");
5119
+ lines.push(" private async ensureInitialized(): Promise<void> {");
5120
+ lines.push(" if (this.initialized) return");
5121
+ lines.push(" this.initialized = true");
5122
+ lines.push("");
5123
+ lines.push(" if (service.hooks?.onInit) {");
5124
+ lines.push(" await service.hooks.onInit()");
5125
+ lines.push(" }");
5126
+ lines.push(" }");
5127
+ lines.push("");
5128
+ if (service.methodNames.length > 0) {
5129
+ for (const methodName of service.methodNames) {
5130
+ lines.push(` /**`);
5131
+ lines.push(` * ${methodName} - wrapped service method`);
5132
+ lines.push(" */");
5133
+ lines.push(` async ${methodName}(...args: unknown[]): Promise<unknown> {`);
5134
+ lines.push(" await this.ensureInitialized()");
5135
+ lines.push("");
5136
+ lines.push(" // Run onBefore hook");
5137
+ lines.push(" if (service.hooks?.onBefore) {");
5138
+ lines.push(` await service.hooks.onBefore('${methodName}', args)`);
5139
+ lines.push(" }");
5140
+ lines.push("");
5141
+ lines.push(" try {");
5142
+ lines.push(" // Call the actual service method");
5143
+ lines.push(` const method = service.methods.${methodName}`);
5144
+ lines.push(" const result = await method.apply({ env: this.env }, args)");
5145
+ lines.push("");
5146
+ lines.push(" // Run onAfter hook");
5147
+ lines.push(" if (service.hooks?.onAfter) {");
5148
+ lines.push(` await service.hooks.onAfter('${methodName}', result)`);
5149
+ lines.push(" }");
5150
+ lines.push("");
5151
+ lines.push(" return result");
5152
+ lines.push(" } catch (error) {");
5153
+ lines.push(" // Run onError hook");
5154
+ lines.push(" if (service.hooks?.onError) {");
5155
+ lines.push(` await service.hooks.onError('${methodName}', error as Error)`);
5156
+ lines.push(" }");
5157
+ lines.push(" throw error");
5158
+ lines.push(" }");
5159
+ lines.push(" }");
5160
+ lines.push("");
5161
+ }
5162
+ } else {
5163
+ lines.push(" // Method wrappers will be generated once service is loaded");
5164
+ lines.push(" // Run `cloudwerk services extract` after defining methods");
5165
+ lines.push("");
5166
+ }
5167
+ lines.push("}");
5168
+ lines.push("");
5169
+ lines.push("// Default export for direct worker access");
5170
+ lines.push("export default {");
5171
+ lines.push(" async fetch(): Promise<Response> {");
5172
+ lines.push(` return new Response('${service.name} service is running. Use service binding RPC to call methods.')`);
5173
+ lines.push(" },");
5174
+ lines.push("}");
5175
+ lines.push("");
5176
+ return lines.join("\n");
5177
+ }
5178
+ function generateWorkerWranglerToml(service, includeTimestamp) {
5179
+ const lines = [];
5180
+ lines.push("# Auto-generated by cloudwerk services - DO NOT EDIT");
5181
+ if (includeTimestamp) {
5182
+ lines.push(`# Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}`);
5183
+ }
5184
+ lines.push("#");
5185
+ lines.push(`# Wrangler configuration for ${service.name} service`);
5186
+ lines.push(`# Source: ${service.filePath}`);
5187
+ lines.push("");
5188
+ lines.push(`name = "${service.workerName}"`);
5189
+ lines.push('main = "worker.ts"');
5190
+ lines.push('compatibility_date = "2024-09-23"');
5191
+ lines.push("");
5192
+ if (service.requiredBindings.length > 0) {
5193
+ lines.push("# Bindings required by this service");
5194
+ lines.push("# Update these with your actual binding configurations");
5195
+ lines.push("");
5196
+ for (const binding of service.requiredBindings) {
5197
+ if (binding.includes("DB") || binding.includes("D1")) {
5198
+ lines.push("[[d1_databases]]");
5199
+ lines.push(`binding = "${binding}"`);
5200
+ lines.push(`database_name = "\${${binding}_NAME}"`);
5201
+ lines.push(`database_id = "\${${binding}_ID}"`);
5202
+ } else if (binding.includes("KV") || binding.includes("CACHE")) {
5203
+ lines.push("[[kv_namespaces]]");
5204
+ lines.push(`binding = "${binding}"`);
5205
+ lines.push(`id = "\${${binding}_ID}"`);
5206
+ } else if (binding.includes("R2") || binding.includes("BUCKET") || binding.includes("STORAGE")) {
5207
+ lines.push("[[r2_buckets]]");
5208
+ lines.push(`binding = "${binding}"`);
5209
+ lines.push(`bucket_name = "\${${binding}_NAME}"`);
5210
+ } else if (binding.includes("QUEUE")) {
5211
+ lines.push("[[queues.producers]]");
5212
+ lines.push(`queue = "\${${binding}_NAME}"`);
5213
+ lines.push(`binding = "${binding}"`);
5214
+ } else {
5215
+ lines.push("[vars]");
5216
+ lines.push(`# ${binding} = "..."`);
5217
+ }
5218
+ lines.push("");
5219
+ }
5220
+ } else {
5221
+ lines.push("# No bindings configured");
5222
+ lines.push("# Add bindings in your service definition:");
5223
+ lines.push("# config: {");
5224
+ lines.push("# extraction: {");
5225
+ lines.push("# bindings: ['DB', 'CACHE'],");
5226
+ lines.push("# }");
5227
+ lines.push("# }");
5228
+ lines.push("");
5229
+ }
5230
+ return lines.join("\n");
5231
+ }
5232
+ function serviceWorkerExists(cwd, serviceName) {
5233
+ const workerDir = path16.join(cwd, EXTRACTED_DIR, `${serviceName}-service`);
5234
+ return fs15.existsSync(workerDir);
5235
+ }
5236
+ function deleteServiceWorker(cwd, serviceName) {
5237
+ const workerDir = path16.join(cwd, EXTRACTED_DIR, `${serviceName}-service`);
5238
+ if (fs15.existsSync(workerDir)) {
5239
+ fs15.rmSync(workerDir, { recursive: true, force: true });
5240
+ return true;
5241
+ }
5242
+ return false;
5243
+ }
5244
+ function getExtractedDir(cwd) {
5245
+ return path16.join(cwd, EXTRACTED_DIR);
5246
+ }
5247
+ function getExtractedServiceDirs(cwd) {
5248
+ const extractedDir = path16.join(cwd, EXTRACTED_DIR);
5249
+ if (!fs15.existsSync(extractedDir)) {
5250
+ return [];
5251
+ }
5252
+ return fs15.readdirSync(extractedDir).map((name) => path16.join(extractedDir, name)).filter((p) => fs15.statSync(p).isDirectory());
5253
+ }
5254
+
5255
+ // src/utils/service-wrangler.ts
5256
+ import * as fs16 from "fs";
5257
+ import * as path17 from "path";
5258
+ function generateServiceBindingToml(service, includeComments) {
5259
+ const lines = [];
5260
+ if (includeComments) {
5261
+ lines.push(`# Service binding for '${service.name}' (from app/services/${service.filePath})`);
5262
+ }
5263
+ lines.push("[[services]]");
5264
+ lines.push(`binding = "${service.bindingName}"`);
5265
+ lines.push(`service = "${service.workerName}"`);
5266
+ lines.push(`entrypoint = "${service.entrypointClass}"`);
5267
+ return lines.join("\n");
5268
+ }
5269
+ function generateServiceToml(manifest, includeComments = true) {
5270
+ const extractedServices = manifest.services.filter((s) => s.mode === "extracted");
5271
+ if (extractedServices.length === 0) {
5272
+ return "";
5273
+ }
5274
+ const lines = [];
5275
+ if (includeComments) {
5276
+ lines.push("# ============================================================================");
5277
+ lines.push("# Cloudwerk Services - Auto-generated from app/services/");
5278
+ lines.push("# ============================================================================");
5279
+ lines.push("#");
5280
+ lines.push("# These service bindings connect to extracted Workers.");
5281
+ lines.push("# Each service runs as a separate Worker and is called via RPC.");
5282
+ lines.push("#");
5283
+ lines.push("# To deploy extracted services, run:");
5284
+ lines.push("# cloudwerk services deploy <name>");
5285
+ lines.push("#");
5286
+ lines.push("");
5287
+ }
5288
+ for (const service of extractedServices) {
5289
+ lines.push(generateServiceBindingToml(service, includeComments));
5290
+ lines.push("");
5291
+ }
5292
+ return lines.join("\n").trim();
5293
+ }
5294
+ var SERVICE_SECTION_START = "# ============================================================================";
5295
+ var SERVICE_SECTION_MARKER = "# Cloudwerk Services - Auto-generated";
5296
+ function hasServiceSection(content) {
5297
+ return content.includes(SERVICE_SECTION_MARKER);
5298
+ }
5299
+ function removeServiceSection(content) {
5300
+ const startIndex = content.indexOf(SERVICE_SECTION_START);
5301
+ if (startIndex === -1 || !content.includes(SERVICE_SECTION_MARKER)) {
5302
+ return content;
5303
+ }
5304
+ const lines = content.split("\n");
5305
+ let sectionStartLine = -1;
5306
+ let sectionEndLine = lines.length;
5307
+ for (let i = 0; i < lines.length; i++) {
5308
+ if (lines[i].includes(SERVICE_SECTION_MARKER)) {
5309
+ sectionStartLine = i > 0 && lines[i - 1].includes("===") ? i - 1 : i;
5310
+ break;
5311
+ }
5312
+ }
5313
+ if (sectionStartLine === -1) {
5314
+ return content;
5315
+ }
5316
+ for (let i = sectionStartLine + 2; i < lines.length; i++) {
5317
+ const line = lines[i].trim();
5318
+ if (line === "" || line.startsWith("#")) {
5319
+ continue;
5320
+ }
5321
+ if (line.startsWith("[[") && !line.includes("services]]")) {
5322
+ sectionEndLine = i;
5323
+ break;
5324
+ }
5325
+ if (line.startsWith("[") && !line.startsWith("[[") && !line.includes("services")) {
5326
+ sectionEndLine = i;
5327
+ break;
5328
+ }
5329
+ }
5330
+ const before = lines.slice(0, sectionStartLine);
5331
+ const after = lines.slice(sectionEndLine);
5332
+ while (before.length > 0 && before[before.length - 1].trim() === "") {
5333
+ before.pop();
5334
+ }
5335
+ return [...before, "", ...after].join("\n");
5336
+ }
5337
+ function generateServiceWrangler(cwd, manifest, options = {}) {
5338
+ const { dryRun = false, includeComments = true } = options;
5339
+ const wranglerPath = findWranglerToml(cwd) || path17.join(cwd, "wrangler.toml");
5340
+ const generatedToml = generateServiceToml(manifest, includeComments);
5341
+ const extractedServices = manifest.services.filter((s) => s.mode === "extracted");
5342
+ const result = {
5343
+ wranglerPath,
5344
+ changed: false,
5345
+ services: extractedServices.map((s) => ({
5346
+ name: s.name,
5347
+ workerName: s.workerName,
5348
+ bindingName: s.bindingName,
5349
+ mode: s.mode
5350
+ })),
5351
+ generatedToml
5352
+ };
5353
+ if (extractedServices.length === 0) {
5354
+ return result;
5355
+ }
5356
+ if (dryRun) {
5357
+ result.changed = true;
5358
+ return result;
5359
+ }
5360
+ let content = "";
5361
+ if (fs16.existsSync(wranglerPath)) {
5362
+ content = readWranglerTomlRaw(cwd);
5363
+ }
5364
+ if (hasServiceSection(content)) {
5365
+ content = removeServiceSection(content);
5366
+ }
5367
+ const newContent = content.trim() + "\n\n" + generatedToml + "\n";
5368
+ writeWranglerTomlRaw(cwd, newContent);
5369
+ result.changed = true;
5370
+ return result;
5371
+ }
5372
+ function removeServiceWrangler(cwd) {
5373
+ const wranglerPath = findWranglerToml(cwd);
5374
+ if (!wranglerPath) {
5375
+ return false;
5376
+ }
5377
+ const content = readWranglerTomlRaw(cwd);
5378
+ if (!hasServiceSection(content)) {
5379
+ return false;
5380
+ }
5381
+ const newContent = removeServiceSection(content);
5382
+ writeWranglerTomlRaw(cwd, newContent);
5383
+ return true;
5384
+ }
5385
+ function hasServiceInWrangler(cwd, serviceName) {
5386
+ const wranglerPath = findWranglerToml(cwd);
5387
+ if (!wranglerPath) {
5388
+ return false;
5389
+ }
5390
+ const content = readWranglerTomlRaw(cwd);
5391
+ const bindingPattern = new RegExp(`binding\\s*=\\s*["']${serviceName.toUpperCase()}_SERVICE["']`);
5392
+ return bindingPattern.test(content);
5393
+ }
5394
+
5395
+ // src/utils/service-type-generator.ts
5396
+ import * as fs17 from "fs";
5397
+ import * as path18 from "path";
5398
+ var CLOUDWERK_TYPES_DIR4 = ".cloudwerk/types";
5399
+ var SERVICES_DTS = "services.d.ts";
5400
+ function generateServiceTypes(cwd, manifest, options = {}) {
5401
+ const includeTimestamp = options.includeTimestamp ?? true;
5402
+ const typesDir = path18.join(cwd, CLOUDWERK_TYPES_DIR4);
5403
+ fs17.mkdirSync(typesDir, { recursive: true });
5404
+ const servicesPath = path18.join(typesDir, SERVICES_DTS);
5405
+ const servicesContent = generateServicesDts(manifest.services, includeTimestamp);
5406
+ fs17.writeFileSync(servicesPath, servicesContent, "utf-8");
5407
+ const serviceInfo = manifest.services.map((s) => ({
5408
+ name: s.name,
5409
+ bindingName: s.bindingName,
5410
+ mode: s.mode
5411
+ }));
5412
+ return {
5413
+ typesDir,
5414
+ file: servicesPath,
5415
+ serviceCount: manifest.services.length,
5416
+ services: serviceInfo
5417
+ };
5418
+ }
5419
+ function generateServicesDts(services2, includeTimestamp) {
5420
+ const lines = [];
5421
+ lines.push("// Auto-generated by cloudwerk services - DO NOT EDIT");
5422
+ if (includeTimestamp) {
5423
+ lines.push(`// Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}`);
5424
+ }
5425
+ lines.push("//");
5426
+ lines.push("// This file provides type information for service interfaces");
5427
+ lines.push('// Add ".cloudwerk/types" to your tsconfig.json include array');
5428
+ lines.push("");
5429
+ lines.push("// ============================================================================");
5430
+ lines.push("// Service Interfaces");
5431
+ lines.push("// ============================================================================");
5432
+ lines.push("//");
5433
+ lines.push("// Define your service interfaces below or in separate type files.");
5434
+ lines.push("// These interfaces should match the methods defined in your service.ts files.");
5435
+ lines.push("//");
5436
+ lines.push("// Example:");
5437
+ lines.push("// interface EmailServiceMethods {");
5438
+ lines.push("// send(params: { to: string; subject: string; body: string }): Promise<{ success: boolean }>");
5439
+ lines.push("// sendBatch(emails: Array<{ to: string; subject: string }>): Promise<{ sent: number }>");
5440
+ lines.push("// }");
5441
+ lines.push("//");
5442
+ lines.push("");
5443
+ for (const service of services2) {
5444
+ const interfaceName = `${capitalizeFirst(service.name)}ServiceMethods`;
5445
+ const modeComment = service.mode === "extracted" ? " (extracted)" : " (local)";
5446
+ lines.push(`/** Methods for the '${service.name}' service${modeComment} */`);
5447
+ lines.push(`// eslint-disable-next-line @typescript-eslint/no-empty-interface`);
5448
+ lines.push(`interface ${interfaceName} {`);
5449
+ if (service.methodNames.length > 0) {
5450
+ for (const methodName of service.methodNames) {
5451
+ lines.push(` /** Method: ${methodName} */`);
5452
+ lines.push(` ${methodName}(...args: unknown[]): Promise<unknown>`);
5453
+ }
5454
+ } else {
5455
+ lines.push(" // Add method signatures here");
5456
+ }
5457
+ lines.push("}");
5458
+ lines.push("");
5459
+ }
5460
+ lines.push("// ============================================================================");
5461
+ lines.push("// Services Interface");
5462
+ lines.push("// ============================================================================");
5463
+ lines.push("");
5464
+ lines.push("interface CloudwerkServices {");
5465
+ for (const service of services2) {
5466
+ const interfaceName = `${capitalizeFirst(service.name)}ServiceMethods`;
5467
+ const modeComment = service.mode === "extracted" ? `extracted (binding: ${service.bindingName})` : "local (direct calls)";
5468
+ lines.push(` /** Service '${service.name}' - ${modeComment} */`);
5469
+ lines.push(` ${service.name}: ${interfaceName}`);
5470
+ }
5471
+ lines.push("}");
5472
+ lines.push("");
5473
+ lines.push("// ============================================================================");
5474
+ lines.push("// Module Augmentation");
5475
+ lines.push("// ============================================================================");
5476
+ lines.push("");
5477
+ lines.push("declare module '@cloudwerk/core/bindings' {");
5478
+ lines.push(" /** Typed services based on app/services/ definitions */");
5479
+ lines.push(" export const services: CloudwerkServices");
5480
+ lines.push("");
5481
+ lines.push(" /** Get a typed service by name */");
5482
+ lines.push(" export function getService<K extends keyof CloudwerkServices>(");
5483
+ lines.push(" name: K");
5484
+ lines.push(" ): CloudwerkServices[K]");
5485
+ lines.push("}");
5486
+ lines.push("");
5487
+ return lines.join("\n");
5488
+ }
5489
+ function capitalizeFirst(str) {
5490
+ return str.charAt(0).toUpperCase() + str.slice(1);
5491
+ }
5492
+
5493
+ // src/commands/services/extract.ts
5494
+ async function servicesExtract(serviceName, options = {}) {
5495
+ const verbose = options.verbose ?? false;
5496
+ const dryRun = options.dryRun ?? false;
5497
+ const logger = createLogger(verbose);
5498
+ try {
5499
+ const cwd = process.cwd();
5500
+ logger.debug("Loading configuration...");
5501
+ const config = await loadConfig16(cwd);
5502
+ const appDir = config.appDir;
5503
+ logger.debug(`Scanning for services in ${appDir}/services/...`);
5504
+ const scanResult = await scanServices5(appDir, { extensions: config.extensions });
5505
+ const serviceModes = { [serviceName]: "extracted" };
5506
+ const manifest = buildServiceManifest5(scanResult, appDir, {
5507
+ defaultMode: "local",
5508
+ serviceModes
5509
+ });
5510
+ const service = manifest.services.find((s) => s.name === serviceName);
5511
+ if (!service) {
5512
+ const available = manifest.services.map((s) => s.name);
5513
+ throw new CliError(
5514
+ `Service '${serviceName}' not found`,
5515
+ "ENOENT",
5516
+ available.length > 0 ? `Available services: ${available.join(", ")}` : "No services found in app/services/"
5517
+ );
5518
+ }
5519
+ console.log();
5520
+ console.log(pc21.bold(`Extracting service: ${serviceName}`));
5521
+ console.log();
5522
+ if (dryRun) {
5523
+ console.log(pc21.yellow("Dry run - no changes will be made"));
5524
+ console.log();
5525
+ }
5526
+ logger.debug("Generating WorkerEntrypoint wrapper...");
5527
+ const workerResult = generateServiceWorker(cwd, service, { includeTimestamp: true });
5528
+ console.log(` ${pc21.green("\u2713")} Worker generated: ${workerResult.workerFile}`);
5529
+ console.log(` ${pc21.green("\u2713")} Wrangler config: ${workerResult.wranglerFile}`);
5530
+ if (!dryRun) {
5531
+ logger.debug("Updating wrangler.toml with service binding...");
5532
+ const wranglerResult = generateServiceWrangler(cwd, manifest);
5533
+ if (wranglerResult.changed) {
5534
+ console.log(` ${pc21.green("\u2713")} Service binding added to ${wranglerResult.wranglerPath}`);
5535
+ }
5536
+ }
5537
+ if (!dryRun) {
5538
+ logger.debug("Updating type definitions...");
5539
+ const typesResult = generateServiceTypes(cwd, manifest);
5540
+ console.log(` ${pc21.green("\u2713")} Types updated: ${typesResult.file}`);
5541
+ }
5542
+ console.log();
5543
+ console.log(pc21.bold("Next steps:"));
5544
+ console.log();
5545
+ console.log(pc21.dim(` 1. Review the generated files in .cloudwerk/extracted/${service.workerName}/`));
5546
+ console.log(pc21.dim(` 2. Update the wrangler.toml with your binding configurations`));
5547
+ console.log(pc21.dim(` 3. Deploy the extracted service:`));
5548
+ console.log();
5549
+ console.log(` cloudwerk services deploy ${serviceName}`);
5550
+ console.log();
5551
+ console.log(pc21.dim(` 4. Deploy the main worker to use the service binding:`));
5552
+ console.log();
5553
+ console.log(" cloudwerk deploy");
5554
+ console.log();
5555
+ } catch (error) {
5556
+ handleCommandError(error, verbose);
5557
+ }
5558
+ }
5559
+
5560
+ // src/commands/services/inline.ts
5561
+ import pc22 from "picocolors";
5562
+ import { loadConfig as loadConfig17, scanServices as scanServices6, buildServiceManifest as buildServiceManifest6 } from "@cloudwerk/core/build";
5563
+ async function servicesInline(serviceName, options = {}) {
5564
+ const verbose = options.verbose ?? false;
5565
+ const logger = createLogger(verbose);
5566
+ try {
5567
+ const cwd = process.cwd();
5568
+ logger.debug("Loading configuration...");
5569
+ const config = await loadConfig17(cwd);
5570
+ const appDir = config.appDir;
5571
+ logger.debug(`Scanning for services in ${appDir}/services/...`);
5572
+ const scanResult = await scanServices6(appDir, { extensions: config.extensions });
5573
+ const manifest = buildServiceManifest6(scanResult, appDir, {
5574
+ defaultMode: "local"
5575
+ });
5576
+ const service = manifest.services.find((s) => s.name === serviceName);
5577
+ if (!service) {
5578
+ const available = manifest.services.map((s) => s.name);
5579
+ throw new CliError(
5580
+ `Service '${serviceName}' not found`,
5581
+ "ENOENT",
5582
+ available.length > 0 ? `Available services: ${available.join(", ")}` : "No services found in app/services/"
5583
+ );
5584
+ }
5585
+ console.log();
5586
+ console.log(pc22.bold(`Converting service to local mode: ${serviceName}`));
5587
+ console.log();
5588
+ const hasExtracted = serviceWorkerExists(cwd, serviceName);
5589
+ if (hasExtracted) {
5590
+ logger.debug("Removing extracted worker files...");
5591
+ const deleted = deleteServiceWorker(cwd, serviceName);
5592
+ if (deleted) {
5593
+ console.log(` ${pc22.green("\u2713")} Removed extracted worker files`);
5594
+ }
5595
+ } else {
5596
+ console.log(` ${pc22.dim("\u2713")} No extracted worker files to remove`);
5597
+ }
5598
+ logger.debug("Updating wrangler.toml...");
5599
+ const removed = removeServiceWrangler(cwd);
5600
+ if (removed) {
5601
+ console.log(` ${pc22.green("\u2713")} Removed service binding from wrangler.toml`);
5602
+ } else {
5603
+ console.log(` ${pc22.dim("\u2713")} No service binding to remove`);
5604
+ }
5605
+ logger.debug("Updating type definitions...");
5606
+ const typesResult = generateServiceTypes(cwd, manifest);
5607
+ console.log(` ${pc22.green("\u2713")} Types updated: ${typesResult.file}`);
5608
+ console.log();
5609
+ console.log(pc22.green(`Service '${serviceName}' is now running in local mode.`));
5610
+ console.log();
5611
+ console.log(pc22.dim("The service will be called directly within your main Worker."));
5612
+ console.log(pc22.dim("Deploy your main Worker to apply the changes:"));
5613
+ console.log();
5614
+ console.log(" cloudwerk deploy");
5615
+ console.log();
5616
+ } catch (error) {
5617
+ handleCommandError(error, verbose);
5618
+ }
5619
+ }
5620
+
5621
+ // src/commands/services/deploy.ts
5622
+ import * as path19 from "path";
5623
+ import { spawn as spawn4 } from "child_process";
5624
+ import pc23 from "picocolors";
5625
+ import { loadConfig as loadConfig18, scanServices as scanServices7, buildServiceManifest as buildServiceManifest7 } from "@cloudwerk/core/build";
5626
+ async function servicesDeploy(serviceName, options = {}) {
5627
+ const verbose = options.verbose ?? false;
5628
+ const dryRun = options.dryRun ?? false;
5629
+ const logger = createLogger(verbose);
5630
+ try {
5631
+ const cwd = process.cwd();
5632
+ logger.debug("Loading configuration...");
5633
+ const config = await loadConfig18(cwd);
5634
+ const appDir = config.appDir;
5635
+ logger.debug(`Scanning for services in ${appDir}/services/...`);
5636
+ const scanResult = await scanServices7(appDir, { extensions: config.extensions });
5637
+ const manifest = buildServiceManifest7(scanResult, appDir);
5638
+ const service = manifest.services.find((s) => s.name === serviceName);
5639
+ if (!service) {
5640
+ const available = manifest.services.map((s) => s.name);
5641
+ throw new CliError(
5642
+ `Service '${serviceName}' not found`,
5643
+ "ENOENT",
5644
+ available.length > 0 ? `Available services: ${available.join(", ")}` : "No services found in app/services/"
5645
+ );
5646
+ }
5647
+ const hasExtracted = serviceWorkerExists(cwd, serviceName);
5648
+ if (!hasExtracted) {
5649
+ throw new CliError(
5650
+ `Service '${serviceName}' is not extracted`,
5651
+ "EINVAL",
5652
+ `Run 'cloudwerk services extract ${serviceName}' first.`
5653
+ );
5654
+ }
5655
+ const workerDir = path19.join(getExtractedDir(cwd), service.workerName);
5656
+ console.log();
5657
+ console.log(pc23.bold(`Deploying service: ${serviceName}`));
5658
+ console.log();
5659
+ console.log(pc23.dim(` Worker: ${service.workerName}`));
5660
+ console.log(pc23.dim(` Location: ${workerDir}`));
5661
+ console.log();
5662
+ if (dryRun) {
5663
+ console.log(pc23.yellow("Dry run - would run:"));
5664
+ console.log(` cd ${workerDir} && wrangler deploy`);
5665
+ console.log();
5666
+ return;
5667
+ }
5668
+ console.log(pc23.dim("Running wrangler deploy..."));
5669
+ console.log();
5670
+ await new Promise((resolve4, reject) => {
5671
+ const wrangler = spawn4("wrangler", ["deploy"], {
5672
+ cwd: workerDir,
5673
+ stdio: "inherit",
5674
+ shell: true
5675
+ });
5676
+ wrangler.on("close", (code) => {
5677
+ if (code === 0) {
5678
+ resolve4();
5679
+ } else {
5680
+ reject(new CliError(
5681
+ `wrangler deploy failed with code ${code}`,
5682
+ "EEXEC",
5683
+ "Check the output above for details."
5684
+ ));
5685
+ }
5686
+ });
5687
+ wrangler.on("error", (err) => {
5688
+ reject(new CliError(
5689
+ `Failed to run wrangler: ${err.message}`,
5690
+ "EEXEC",
5691
+ "Make sure wrangler is installed: npm install -g wrangler"
5692
+ ));
5693
+ });
5694
+ });
5695
+ console.log();
5696
+ console.log(pc23.green(`Service '${serviceName}' deployed successfully!`));
5697
+ console.log();
5698
+ console.log(pc23.dim("Next steps:"));
5699
+ console.log(pc23.dim(" 1. Deploy your main worker to use the service binding:"));
5700
+ console.log();
5701
+ console.log(" cloudwerk deploy");
5702
+ console.log();
5703
+ } catch (error) {
5704
+ handleCommandError(error, verbose);
5705
+ }
5706
+ }
5707
+
5708
+ // src/commands/services/status.ts
5709
+ import pc24 from "picocolors";
5710
+ import { loadConfig as loadConfig19, scanServices as scanServices8, buildServiceManifest as buildServiceManifest8 } from "@cloudwerk/core/build";
5711
+ async function servicesStatus(options = {}) {
5712
+ const verbose = options.verbose ?? false;
5713
+ const logger = createLogger(verbose);
5714
+ try {
5715
+ const cwd = process.cwd();
5716
+ logger.debug("Loading configuration...");
5717
+ const config = await loadConfig19(cwd);
5718
+ const appDir = config.appDir;
5719
+ logger.debug(`Scanning for services in ${appDir}/services/...`);
5720
+ const scanResult = await scanServices8(appDir, { extensions: config.extensions });
5721
+ const manifest = buildServiceManifest8(scanResult, appDir);
5722
+ console.log();
5723
+ console.log(pc24.bold("Services Status"));
5724
+ console.log();
5725
+ if (manifest.services.length === 0) {
5726
+ console.log(pc24.dim(" No services found in app/services/"));
5727
+ console.log();
5728
+ return;
5729
+ }
5730
+ const localCount = manifest.services.filter((s) => s.mode === "local").length;
5731
+ const extractedCount = manifest.services.filter((s) => s.mode === "extracted").length;
5732
+ console.log(pc24.dim(` Total: ${manifest.services.length} services`));
5733
+ console.log(pc24.dim(` Local: ${localCount}`));
5734
+ console.log(pc24.dim(` Extracted: ${extractedCount}`));
5735
+ console.log();
5736
+ console.log(pc24.dim(" Service Mode Worker Files Binding"));
5737
+ console.log(pc24.dim(" " + "\u2500".repeat(16) + " " + "\u2500".repeat(9) + " " + "\u2500".repeat(12) + " " + "\u2500".repeat(10)));
5738
+ for (const service of manifest.services) {
5739
+ const name = service.name.padEnd(16);
5740
+ const mode = service.mode === "extracted" ? pc24.yellow("extracted") : pc24.green("local ");
5741
+ const hasFiles = serviceWorkerExists(cwd, service.name);
5742
+ const filesStatus = hasFiles ? pc24.green("\u2713 generated ") : pc24.dim("- not needed");
5743
+ const hasBinding = hasServiceInWrangler(cwd, service.name);
5744
+ const bindingStatus = service.mode === "extracted" ? hasBinding ? pc24.green("\u2713 yes") : pc24.yellow("\u2717 missing") : pc24.dim("n/a ");
5745
+ console.log(` ${pc24.cyan(name)} ${mode} ${filesStatus} ${bindingStatus}`);
5746
+ }
5747
+ console.log();
5748
+ const extractedDirs = getExtractedServiceDirs(cwd);
5749
+ const serviceWorkerNames = manifest.services.map((s) => s.workerName);
5750
+ const orphanedDirs = extractedDirs.filter((dir) => {
5751
+ const dirName = dir.split("/").pop() || "";
5752
+ return !serviceWorkerNames.includes(dirName);
5753
+ });
5754
+ if (orphanedDirs.length > 0) {
5755
+ console.log(pc24.yellow("Orphaned extracted workers (no matching service):"));
5756
+ for (const dir of orphanedDirs) {
5757
+ console.log(pc24.yellow(` - ${dir}`));
5758
+ }
5759
+ console.log();
5760
+ console.log(pc24.dim(" These can be safely deleted if no longer needed."));
5761
+ console.log();
5762
+ }
5763
+ if (manifest.errors.length > 0) {
5764
+ console.log(pc24.red("Errors:"));
5765
+ for (const error of manifest.errors) {
5766
+ console.log(pc24.red(` - ${error.file}: ${error.message}`));
5767
+ }
5768
+ console.log();
5769
+ }
5770
+ if (manifest.warnings.length > 0) {
5771
+ console.log(pc24.yellow("Warnings:"));
5772
+ for (const warning of manifest.warnings) {
5773
+ console.log(pc24.yellow(` - ${warning.file}: ${warning.message}`));
5774
+ }
5775
+ console.log();
5776
+ }
5777
+ console.log(pc24.dim("Use 'cloudwerk services list' for a simple list."));
5778
+ console.log(pc24.dim("Use 'cloudwerk services info <name>' for details on a specific service."));
5779
+ console.log();
5780
+ } catch (error) {
5781
+ handleCommandError(error, verbose);
5782
+ }
5783
+ }
5784
+
3980
5785
  // src/index.ts
3981
5786
  program.name("cloudwerk").description("Cloudwerk CLI - Build and deploy full-stack apps to Cloudflare").version(VERSION).enablePositionalOptions();
3982
5787
  program.command("dev [path]").description("Start development server").option("-p, --port <number>", "Port to listen on", String(DEFAULT_PORT)).option("-H, --host <host>", "Host to bind", DEFAULT_HOST).option("-c, --config <path>", "Path to config file").option("--verbose", "Enable verbose logging").action(dev);
@@ -3994,4 +5799,16 @@ var triggersCmd = program.command("triggers").description("Manage Cloudwerk trig
3994
5799
  triggersCmd.command("list").description("List all triggers with details").option("-t, --type <type>", "Filter by source type (scheduled, queue, r2, webhook, email)").option("--json", "Output as JSON").option("--verbose", "Enable verbose logging").action(triggersList);
3995
5800
  triggersCmd.command("validate").description("Validate trigger configurations").option("-s, --strict", "Exit with error code if warnings are present").option("--json", "Output as JSON").option("--verbose", "Enable verbose logging").action(triggersValidate);
3996
5801
  triggersCmd.command("generate").description("Regenerate wrangler.toml and TypeScript types").option("--wrangler", "Only generate wrangler.toml config").option("--types", "Only generate TypeScript types").option("--dry-run", "Show what would be generated without writing").option("--json", "Output as JSON").option("--verbose", "Enable verbose logging").action(triggersGenerate);
5802
+ var objectsCmd = program.command("objects").description("Manage Cloudwerk Durable Objects").enablePositionalOptions().passThroughOptions().option("--verbose", "Enable verbose logging").action(objects);
5803
+ objectsCmd.command("list").description("List all durable objects with details").option("--json", "Output as JSON").option("--verbose", "Enable verbose logging").action(objectsList);
5804
+ objectsCmd.command("info <name>").description("Show durable object details").option("--json", "Output as JSON").option("--verbose", "Enable verbose logging").action(objectsInfo);
5805
+ objectsCmd.command("migrations").description("Show migration history for SQLite durable objects").option("--json", "Output as JSON").option("--verbose", "Enable verbose logging").action(objectsMigrations);
5806
+ objectsCmd.command("generate").description("Regenerate wrangler.toml and TypeScript types").option("--wrangler", "Only generate wrangler.toml config").option("--types", "Only generate TypeScript types").option("--dry-run", "Show what would be generated without writing").option("--json", "Output as JSON").option("--verbose", "Enable verbose logging").action(objectsGenerate);
5807
+ var servicesCmd = program.command("services").description("Manage Cloudwerk services (RPC bindings)").enablePositionalOptions().passThroughOptions().option("--verbose", "Enable verbose logging").action(services);
5808
+ servicesCmd.command("list").description("List all services with details").option("--json", "Output as JSON").option("--verbose", "Enable verbose logging").action(servicesList);
5809
+ servicesCmd.command("info <name>").description("Show service details").option("--json", "Output as JSON").option("--verbose", "Enable verbose logging").action(servicesInfo);
5810
+ servicesCmd.command("extract <name>").description("Extract service to separate Worker").option("--verbose", "Enable verbose logging").action(servicesExtract);
5811
+ servicesCmd.command("inline <name>").description("Convert extracted service back to local mode").option("--verbose", "Enable verbose logging").action(servicesInline);
5812
+ servicesCmd.command("deploy <name>").description("Deploy extracted service").option("-e, --env <environment>", "Environment to deploy to").option("--verbose", "Enable verbose logging").action(servicesDeploy);
5813
+ servicesCmd.command("status").description("Show status of all services").option("--json", "Output as JSON").option("--verbose", "Enable verbose logging").action(servicesStatus);
3997
5814
  program.parse();