@cloudwerk/cli 0.9.0 → 0.11.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.
Files changed (2) hide show
  1. package/dist/index.js +1759 -33
  2. package/package.json +6 -4
package/dist/index.js CHANGED
@@ -324,7 +324,8 @@ async function build(pathArg, options) {
324
324
  hydrationEndpoint: "/__cloudwerk",
325
325
  renderer,
326
326
  publicDir: cloudwerkConfig.publicDir ?? "public",
327
- root: cwd
327
+ root: cwd,
328
+ isProduction: true
328
329
  });
329
330
  const tempEntryPath = path2.join(tempDir, "_server-entry.ts");
330
331
  fs2.writeFileSync(tempEntryPath, serverEntryCode);
@@ -468,13 +469,102 @@ function formatSize(bytes) {
468
469
  return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
469
470
  }
470
471
 
472
+ // src/commands/deploy.ts
473
+ import * as path3 from "path";
474
+ import * as fs3 from "fs";
475
+ import { spawn } from "child_process";
476
+ async function deploy(pathArg, options) {
477
+ const verbose = options.verbose ?? false;
478
+ const logger = createLogger(verbose);
479
+ try {
480
+ const cwd = pathArg ? path3.resolve(process.cwd(), pathArg) : process.cwd();
481
+ if (!fs3.existsSync(cwd)) {
482
+ throw new CliError(
483
+ `Directory does not exist: ${cwd}`,
484
+ "ENOENT",
485
+ `Make sure the path exists and try again.`
486
+ );
487
+ }
488
+ logger.debug(`Working directory: ${cwd}`);
489
+ const wranglerPath = path3.join(cwd, "wrangler.toml");
490
+ if (!fs3.existsSync(wranglerPath)) {
491
+ throw new CliError(
492
+ `wrangler.toml not found in ${cwd}`,
493
+ "ENOENT",
494
+ `Create a wrangler.toml file or run this command from a directory containing one.`
495
+ );
496
+ }
497
+ logger.debug(`Found wrangler.toml: ${wranglerPath}`);
498
+ if (!options.skipBuild) {
499
+ logger.info("Building project...");
500
+ await build(pathArg, {
501
+ config: options.config,
502
+ verbose: options.verbose
503
+ });
504
+ console.log();
505
+ }
506
+ const args = ["wrangler", "deploy"];
507
+ if (options.env) {
508
+ args.push("--env", options.env);
509
+ }
510
+ if (options.dryRun) {
511
+ args.push("--dry-run");
512
+ }
513
+ const envLabel = options.env ? ` to ${options.env}` : "";
514
+ const dryRunLabel = options.dryRun ? " (dry run)" : "";
515
+ logger.info(`Deploying${envLabel}${dryRunLabel}...`);
516
+ logger.debug(`Running: npx ${args.join(" ")}`);
517
+ const exitCode = await runCommand(args, cwd);
518
+ if (exitCode !== 0) {
519
+ throw new CliError(
520
+ `Deployment failed with exit code ${exitCode}`,
521
+ "EDEPLOY",
522
+ `Check the wrangler output above for details.`
523
+ );
524
+ }
525
+ if (!options.dryRun) {
526
+ logger.success("Deployment complete!");
527
+ } else {
528
+ logger.success("Dry run complete!");
529
+ }
530
+ } catch (error) {
531
+ if (error instanceof CliError) {
532
+ printError(error.message, error.suggestion);
533
+ process.exit(1);
534
+ }
535
+ if (error instanceof Error) {
536
+ printError(error.message);
537
+ if (verbose && error.stack) {
538
+ console.log(error.stack);
539
+ }
540
+ process.exit(1);
541
+ }
542
+ printError(String(error));
543
+ process.exit(1);
544
+ }
545
+ }
546
+ function runCommand(args, cwd) {
547
+ return new Promise((resolve4, reject) => {
548
+ const child = spawn("npx", args, {
549
+ cwd,
550
+ stdio: "inherit"
551
+ });
552
+ child.on("error", (error) => {
553
+ reject(error);
554
+ });
555
+ child.on("close", (code) => {
556
+ resolve4(code ?? 0);
557
+ });
558
+ });
559
+ }
560
+
471
561
  // src/commands/config.ts
472
562
  import * as readline from "readline";
473
563
  import pc2 from "picocolors";
474
564
 
475
565
  // src/utils/configWriter.ts
476
- import * as fs3 from "fs";
477
- import * as path3 from "path";
566
+ import * as fs4 from "fs";
567
+ import * as path4 from "path";
478
568
  import { loadConfig as loadConfig2 } from "@cloudwerk/core/build";
479
569
  var CONFIG_FILE_NAMES = [
480
570
  "cloudwerk.config.ts",
@@ -483,8 +573,8 @@ var CONFIG_FILE_NAMES = [
483
573
  ];
484
574
  function findConfigFile(cwd) {
485
575
  for (const filename of CONFIG_FILE_NAMES) {
486
- const configPath = path3.join(cwd, filename);
487
- if (fs3.existsSync(configPath)) {
576
+ const configPath = path4.join(cwd, filename);
577
+ if (fs4.existsSync(configPath)) {
488
578
  return configPath;
489
579
  }
490
580
  }
@@ -495,7 +585,7 @@ function readCloudwerkConfig(cwd) {
495
585
  if (!configPath) {
496
586
  return {};
497
587
  }
498
- const content = fs3.readFileSync(configPath, "utf-8");
588
+ const content = fs4.readFileSync(configPath, "utf-8");
499
589
  return parseConfigContent(content);
500
590
  }
501
591
  function parseConfigContent(content) {
@@ -516,16 +606,16 @@ function parseConfigContent(content) {
516
606
  function writeCloudwerkConfig(cwd, updates) {
517
607
  const configPath = findConfigFile(cwd);
518
608
  if (!configPath) {
519
- const newConfigPath = path3.join(cwd, "cloudwerk.config.ts");
609
+ const newConfigPath = path4.join(cwd, "cloudwerk.config.ts");
520
610
  const content2 = generateMinimalConfig(updates);
521
- fs3.writeFileSync(newConfigPath, content2, "utf-8");
611
+ fs4.writeFileSync(newConfigPath, content2, "utf-8");
522
612
  return true;
523
613
  }
524
- let content = fs3.readFileSync(configPath, "utf-8");
614
+ let content = fs4.readFileSync(configPath, "utf-8");
525
615
  if (updates.renderer !== void 0) {
526
616
  content = updateRenderer(content, updates.renderer);
527
617
  }
528
- fs3.writeFileSync(configPath, content, "utf-8");
618
+ fs4.writeFileSync(configPath, content, "utf-8");
529
619
  return true;
530
620
  }
531
621
  function updateRenderer(content, renderer) {
@@ -594,11 +684,11 @@ function generateMinimalConfig(updates) {
594
684
  }
595
685
 
596
686
  // src/utils/tsconfigWriter.ts
597
- import * as fs4 from "fs";
598
- import * as path4 from "path";
687
+ import * as fs5 from "fs";
688
+ import * as path5 from "path";
599
689
  function findTsConfig(cwd) {
600
- const tsconfigPath = path4.join(cwd, "tsconfig.json");
601
- if (fs4.existsSync(tsconfigPath)) {
690
+ const tsconfigPath = path5.join(cwd, "tsconfig.json");
691
+ if (fs5.existsSync(tsconfigPath)) {
602
692
  return tsconfigPath;
603
693
  }
604
694
  return null;
@@ -609,7 +699,7 @@ function readTsConfig(cwd) {
609
699
  return {};
610
700
  }
611
701
  try {
612
- const content = fs4.readFileSync(tsconfigPath, "utf-8");
702
+ const content = fs5.readFileSync(tsconfigPath, "utf-8");
613
703
  const config = JSON.parse(content);
614
704
  return {
615
705
  jsxImportSource: config.compilerOptions?.jsxImportSource,
@@ -625,7 +715,7 @@ function updateTsConfig(cwd, updates) {
625
715
  return false;
626
716
  }
627
717
  try {
628
- const content = fs4.readFileSync(tsconfigPath, "utf-8");
718
+ const content = fs5.readFileSync(tsconfigPath, "utf-8");
629
719
  const config = JSON.parse(content);
630
720
  if (!config.compilerOptions) {
631
721
  config.compilerOptions = {};
@@ -633,7 +723,7 @@ function updateTsConfig(cwd, updates) {
633
723
  if (updates.jsxImportSource !== void 0) {
634
724
  config.compilerOptions.jsxImportSource = updates.jsxImportSource;
635
725
  }
636
- fs4.writeFileSync(tsconfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
726
+ fs5.writeFileSync(tsconfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
637
727
  return true;
638
728
  } catch {
639
729
  return false;
@@ -641,8 +731,8 @@ function updateTsConfig(cwd, updates) {
641
731
  }
642
732
 
643
733
  // src/utils/dependencyManager.ts
644
- import * as fs5 from "fs";
645
- import * as path5 from "path";
734
+ import * as fs6 from "fs";
735
+ import * as path6 from "path";
646
736
  import { execFileSync } from "child_process";
647
737
  var REACT_SPECIFIC_LIBRARIES = [
648
738
  "@tanstack/react-query",
@@ -683,22 +773,22 @@ var REACT_SPECIFIC_LIBRARIES = [
683
773
  "@use-gesture/react"
684
774
  ];
685
775
  function detectPackageManager(cwd) {
686
- if (fs5.existsSync(path5.join(cwd, "pnpm-lock.yaml"))) {
776
+ if (fs6.existsSync(path6.join(cwd, "pnpm-lock.yaml"))) {
687
777
  return "pnpm";
688
778
  }
689
- if (fs5.existsSync(path5.join(cwd, "yarn.lock"))) {
779
+ if (fs6.existsSync(path6.join(cwd, "yarn.lock"))) {
690
780
  return "yarn";
691
781
  }
692
- if (fs5.existsSync(path5.join(cwd, "bun.lockb"))) {
782
+ if (fs6.existsSync(path6.join(cwd, "bun.lockb"))) {
693
783
  return "bun";
694
784
  }
695
- if (fs5.existsSync(path5.join(cwd, "package-lock.json"))) {
785
+ if (fs6.existsSync(path6.join(cwd, "package-lock.json"))) {
696
786
  return "npm";
697
787
  }
698
- const packageJsonPath = path5.join(cwd, "package.json");
699
- if (fs5.existsSync(packageJsonPath)) {
788
+ const packageJsonPath = path6.join(cwd, "package.json");
789
+ if (fs6.existsSync(packageJsonPath)) {
700
790
  try {
701
- const packageJson = JSON.parse(fs5.readFileSync(packageJsonPath, "utf-8"));
791
+ const packageJson = JSON.parse(fs6.readFileSync(packageJsonPath, "utf-8"));
702
792
  const pm = packageJson.packageManager;
703
793
  if (pm) {
704
794
  if (pm.startsWith("pnpm")) return "pnpm";
@@ -712,15 +802,15 @@ function detectPackageManager(cwd) {
712
802
  return "npm";
713
803
  }
714
804
  function getInstalledDependencies(cwd) {
715
- const packageJsonPath = path5.join(cwd, "package.json");
716
- if (!fs5.existsSync(packageJsonPath)) {
805
+ const packageJsonPath = path6.join(cwd, "package.json");
806
+ if (!fs6.existsSync(packageJsonPath)) {
717
807
  return {
718
808
  dependencies: {},
719
809
  devDependencies: {}
720
810
  };
721
811
  }
722
812
  try {
723
- const content = fs5.readFileSync(packageJsonPath, "utf-8");
813
+ const content = fs6.readFileSync(packageJsonPath, "utf-8");
724
814
  const packageJson = JSON.parse(content);
725
815
  return {
726
816
  dependencies: packageJson.dependencies || {},
@@ -1043,26 +1133,1662 @@ async function askConfirmation(question, defaultValue) {
1043
1133
  output: process.stdout
1044
1134
  });
1045
1135
  const hint = defaultValue ? "Y/n" : "y/N";
1046
- return new Promise((resolve3) => {
1136
+ return new Promise((resolve4) => {
1047
1137
  rl.question(pc2.cyan("? ") + question + pc2.dim(` (${hint}) `), (answer) => {
1048
1138
  rl.close();
1049
1139
  const normalized = answer.trim().toLowerCase();
1050
1140
  if (normalized === "") {
1051
- resolve3(defaultValue);
1141
+ resolve4(defaultValue);
1052
1142
  } else if (normalized === "y" || normalized === "yes") {
1053
- resolve3(true);
1143
+ resolve4(true);
1054
1144
  } else {
1055
- resolve3(false);
1145
+ resolve4(false);
1056
1146
  }
1057
1147
  });
1058
1148
  });
1059
1149
  }
1060
1150
 
1151
+ // src/commands/bindings.ts
1152
+ import * as path8 from "path";
1153
+ import pc4 from "picocolors";
1154
+
1155
+ // src/utils/command-error-handler.ts
1156
+ import pc3 from "picocolors";
1157
+ function handleCommandError(error, verbose = false) {
1158
+ if (error instanceof CliError) {
1159
+ printError(error.message, error.suggestion);
1160
+ process.exit(1);
1161
+ }
1162
+ if (error instanceof Error && (error.message.includes("User force closed") || error.name === "ExitPromptError")) {
1163
+ console.log();
1164
+ console.log(pc3.dim("Cancelled."));
1165
+ process.exit(0);
1166
+ }
1167
+ if (error instanceof Error) {
1168
+ printError(error.message);
1169
+ if (verbose && error.stack) {
1170
+ console.log(error.stack);
1171
+ }
1172
+ process.exit(1);
1173
+ }
1174
+ printError(String(error));
1175
+ process.exit(1);
1176
+ }
1177
+
1178
+ // src/utils/wrangler-toml.ts
1179
+ import * as fs7 from "fs";
1180
+ import * as path7 from "path";
1181
+ import TOML from "@iarna/toml";
1182
+ function findWranglerToml(cwd) {
1183
+ const wranglerPath = path7.join(cwd, "wrangler.toml");
1184
+ if (fs7.existsSync(wranglerPath)) {
1185
+ return wranglerPath;
1186
+ }
1187
+ const wranglerJsonPath = path7.join(cwd, "wrangler.json");
1188
+ if (fs7.existsSync(wranglerJsonPath)) {
1189
+ return wranglerJsonPath;
1190
+ }
1191
+ return null;
1192
+ }
1193
+ function readWranglerToml(cwd) {
1194
+ const configPath = findWranglerToml(cwd);
1195
+ if (!configPath) {
1196
+ return {};
1197
+ }
1198
+ const content = fs7.readFileSync(configPath, "utf-8");
1199
+ if (configPath.endsWith(".json")) {
1200
+ return JSON.parse(content);
1201
+ }
1202
+ return TOML.parse(content);
1203
+ }
1204
+ function writeWranglerToml(cwd, config) {
1205
+ const configPath = path7.join(cwd, "wrangler.toml");
1206
+ const content = TOML.stringify(config);
1207
+ fs7.writeFileSync(configPath, content, "utf-8");
1208
+ }
1209
+ function getEnvConfig(config, env) {
1210
+ if (!env || !config.env?.[env]) {
1211
+ return config;
1212
+ }
1213
+ return {
1214
+ ...config,
1215
+ ...config.env[env]
1216
+ };
1217
+ }
1218
+ function extractBindings(config, env) {
1219
+ const envConfig = getEnvConfig(config, env);
1220
+ const bindings2 = [];
1221
+ if (envConfig.d1_databases) {
1222
+ for (const db of envConfig.d1_databases) {
1223
+ bindings2.push({
1224
+ type: "d1",
1225
+ name: db.binding,
1226
+ resourceId: db.database_id,
1227
+ resourceName: db.database_name
1228
+ });
1229
+ }
1230
+ }
1231
+ if (envConfig.kv_namespaces) {
1232
+ for (const kv of envConfig.kv_namespaces) {
1233
+ bindings2.push({
1234
+ type: "kv",
1235
+ name: kv.binding,
1236
+ resourceId: kv.id
1237
+ });
1238
+ }
1239
+ }
1240
+ if (envConfig.r2_buckets) {
1241
+ for (const r2 of envConfig.r2_buckets) {
1242
+ bindings2.push({
1243
+ type: "r2",
1244
+ name: r2.binding,
1245
+ resourceName: r2.bucket_name
1246
+ });
1247
+ }
1248
+ }
1249
+ if (envConfig.queues?.producers) {
1250
+ for (const queue of envConfig.queues.producers) {
1251
+ bindings2.push({
1252
+ type: "queue",
1253
+ name: queue.binding,
1254
+ resourceName: queue.queue
1255
+ });
1256
+ }
1257
+ }
1258
+ if (envConfig.durable_objects?.bindings) {
1259
+ for (const doBinding of envConfig.durable_objects.bindings) {
1260
+ bindings2.push({
1261
+ type: "do",
1262
+ name: doBinding.name,
1263
+ resourceName: doBinding.class_name,
1264
+ extra: doBinding.script_name ? { script_name: doBinding.script_name } : void 0
1265
+ });
1266
+ }
1267
+ }
1268
+ if (envConfig.services) {
1269
+ for (const service of envConfig.services) {
1270
+ bindings2.push({
1271
+ type: "service",
1272
+ name: service.binding,
1273
+ resourceName: service.service,
1274
+ extra: service.environment ? { environment: service.environment } : void 0
1275
+ });
1276
+ }
1277
+ }
1278
+ if (envConfig.vars) {
1279
+ for (const [name] of Object.entries(envConfig.vars)) {
1280
+ bindings2.push({
1281
+ type: "secret",
1282
+ name
1283
+ });
1284
+ }
1285
+ }
1286
+ if (envConfig.ai) {
1287
+ bindings2.push({
1288
+ type: "ai",
1289
+ name: envConfig.ai.binding
1290
+ });
1291
+ }
1292
+ if (envConfig.vectorize) {
1293
+ for (const vec of envConfig.vectorize) {
1294
+ bindings2.push({
1295
+ type: "vectorize",
1296
+ name: vec.binding,
1297
+ resourceName: vec.index_name
1298
+ });
1299
+ }
1300
+ }
1301
+ if (envConfig.hyperdrive) {
1302
+ for (const hd of envConfig.hyperdrive) {
1303
+ bindings2.push({
1304
+ type: "hyperdrive",
1305
+ name: hd.binding,
1306
+ resourceId: hd.id
1307
+ });
1308
+ }
1309
+ }
1310
+ return bindings2;
1311
+ }
1312
+ function getEnvironments(config) {
1313
+ if (!config.env) {
1314
+ return [];
1315
+ }
1316
+ return Object.keys(config.env);
1317
+ }
1318
+ function addD1Binding(cwd, bindingName, databaseName, databaseId, env) {
1319
+ const config = readWranglerToml(cwd);
1320
+ const newBinding = {
1321
+ binding: bindingName,
1322
+ database_name: databaseName,
1323
+ database_id: databaseId
1324
+ };
1325
+ if (env) {
1326
+ if (!config.env) config.env = {};
1327
+ if (!config.env[env]) config.env[env] = {};
1328
+ if (!config.env[env].d1_databases) config.env[env].d1_databases = [];
1329
+ config.env[env].d1_databases.push(newBinding);
1330
+ } else {
1331
+ if (!config.d1_databases) config.d1_databases = [];
1332
+ config.d1_databases.push(newBinding);
1333
+ }
1334
+ writeWranglerToml(cwd, config);
1335
+ }
1336
+ function addKVBinding(cwd, bindingName, namespaceId, previewId, env) {
1337
+ const config = readWranglerToml(cwd);
1338
+ const newBinding = {
1339
+ binding: bindingName,
1340
+ id: namespaceId
1341
+ };
1342
+ if (previewId) {
1343
+ newBinding.preview_id = previewId;
1344
+ }
1345
+ if (env) {
1346
+ if (!config.env) config.env = {};
1347
+ if (!config.env[env]) config.env[env] = {};
1348
+ if (!config.env[env].kv_namespaces) config.env[env].kv_namespaces = [];
1349
+ config.env[env].kv_namespaces.push(newBinding);
1350
+ } else {
1351
+ if (!config.kv_namespaces) config.kv_namespaces = [];
1352
+ config.kv_namespaces.push(newBinding);
1353
+ }
1354
+ writeWranglerToml(cwd, config);
1355
+ }
1356
+ function addR2Binding(cwd, bindingName, bucketName, env) {
1357
+ const config = readWranglerToml(cwd);
1358
+ const newBinding = {
1359
+ binding: bindingName,
1360
+ bucket_name: bucketName
1361
+ };
1362
+ if (env) {
1363
+ if (!config.env) config.env = {};
1364
+ if (!config.env[env]) config.env[env] = {};
1365
+ if (!config.env[env].r2_buckets) config.env[env].r2_buckets = [];
1366
+ config.env[env].r2_buckets.push(newBinding);
1367
+ } else {
1368
+ if (!config.r2_buckets) config.r2_buckets = [];
1369
+ config.r2_buckets.push(newBinding);
1370
+ }
1371
+ writeWranglerToml(cwd, config);
1372
+ }
1373
+ function addQueueBinding(cwd, bindingName, queueName, env) {
1374
+ const config = readWranglerToml(cwd);
1375
+ const newBinding = {
1376
+ binding: bindingName,
1377
+ queue: queueName
1378
+ };
1379
+ if (env) {
1380
+ if (!config.env) config.env = {};
1381
+ if (!config.env[env]) config.env[env] = {};
1382
+ if (!config.env[env].queues) config.env[env].queues = {};
1383
+ if (!config.env[env].queues.producers)
1384
+ config.env[env].queues.producers = [];
1385
+ config.env[env].queues.producers.push(newBinding);
1386
+ } else {
1387
+ if (!config.queues) config.queues = {};
1388
+ if (!config.queues.producers) config.queues.producers = [];
1389
+ config.queues.producers.push(newBinding);
1390
+ }
1391
+ writeWranglerToml(cwd, config);
1392
+ }
1393
+ function addDurableObjectBinding(cwd, bindingName, className, scriptName, env) {
1394
+ const config = readWranglerToml(cwd);
1395
+ const newBinding = {
1396
+ name: bindingName,
1397
+ class_name: className
1398
+ };
1399
+ if (scriptName) {
1400
+ newBinding.script_name = scriptName;
1401
+ }
1402
+ if (env) {
1403
+ if (!config.env) config.env = {};
1404
+ if (!config.env[env]) config.env[env] = {};
1405
+ if (!config.env[env].durable_objects)
1406
+ config.env[env].durable_objects = { bindings: [] };
1407
+ config.env[env].durable_objects.bindings.push(newBinding);
1408
+ } else {
1409
+ if (!config.durable_objects) config.durable_objects = { bindings: [] };
1410
+ if (!config.durable_objects.bindings) config.durable_objects.bindings = [];
1411
+ config.durable_objects.bindings.push(newBinding);
1412
+ }
1413
+ writeWranglerToml(cwd, config);
1414
+ }
1415
+ function addSecretBinding(cwd, name, value, env) {
1416
+ const config = readWranglerToml(cwd);
1417
+ if (env) {
1418
+ if (!config.env) config.env = {};
1419
+ if (!config.env[env]) config.env[env] = {};
1420
+ if (!config.env[env].vars) config.env[env].vars = {};
1421
+ config.env[env].vars[name] = value;
1422
+ } else {
1423
+ if (!config.vars) config.vars = {};
1424
+ config.vars[name] = value;
1425
+ }
1426
+ writeWranglerToml(cwd, config);
1427
+ }
1428
+ function removeBinding(cwd, bindingName, env) {
1429
+ const config = readWranglerToml(cwd);
1430
+ let removed = false;
1431
+ const removeFromConfig = (cfg) => {
1432
+ let found = false;
1433
+ if (cfg.d1_databases) {
1434
+ const idx = cfg.d1_databases.findIndex((b) => b.binding === bindingName);
1435
+ if (idx !== -1) {
1436
+ cfg.d1_databases.splice(idx, 1);
1437
+ if (cfg.d1_databases.length === 0) delete cfg.d1_databases;
1438
+ found = true;
1439
+ }
1440
+ }
1441
+ if (cfg.kv_namespaces) {
1442
+ const idx = cfg.kv_namespaces.findIndex((b) => b.binding === bindingName);
1443
+ if (idx !== -1) {
1444
+ cfg.kv_namespaces.splice(idx, 1);
1445
+ if (cfg.kv_namespaces.length === 0) delete cfg.kv_namespaces;
1446
+ found = true;
1447
+ }
1448
+ }
1449
+ if (cfg.r2_buckets) {
1450
+ const idx = cfg.r2_buckets.findIndex((b) => b.binding === bindingName);
1451
+ if (idx !== -1) {
1452
+ cfg.r2_buckets.splice(idx, 1);
1453
+ if (cfg.r2_buckets.length === 0) delete cfg.r2_buckets;
1454
+ found = true;
1455
+ }
1456
+ }
1457
+ if (cfg.queues?.producers) {
1458
+ const idx = cfg.queues.producers.findIndex(
1459
+ (b) => b.binding === bindingName
1460
+ );
1461
+ if (idx !== -1) {
1462
+ cfg.queues.producers.splice(idx, 1);
1463
+ if (cfg.queues.producers.length === 0) delete cfg.queues.producers;
1464
+ if (!cfg.queues.producers && !cfg.queues.consumers) delete cfg.queues;
1465
+ found = true;
1466
+ }
1467
+ }
1468
+ if (cfg.durable_objects?.bindings) {
1469
+ const idx = cfg.durable_objects.bindings.findIndex(
1470
+ (b) => b.name === bindingName
1471
+ );
1472
+ if (idx !== -1) {
1473
+ cfg.durable_objects.bindings.splice(idx, 1);
1474
+ if (cfg.durable_objects.bindings.length === 0)
1475
+ delete cfg.durable_objects;
1476
+ found = true;
1477
+ }
1478
+ }
1479
+ if (cfg.services) {
1480
+ const idx = cfg.services.findIndex((b) => b.binding === bindingName);
1481
+ if (idx !== -1) {
1482
+ cfg.services.splice(idx, 1);
1483
+ if (cfg.services.length === 0) delete cfg.services;
1484
+ found = true;
1485
+ }
1486
+ }
1487
+ if (cfg.vars && bindingName in cfg.vars) {
1488
+ delete cfg.vars[bindingName];
1489
+ if (Object.keys(cfg.vars).length === 0) delete cfg.vars;
1490
+ found = true;
1491
+ }
1492
+ if (cfg.ai && cfg.ai.binding === bindingName) {
1493
+ delete cfg.ai;
1494
+ found = true;
1495
+ }
1496
+ if (cfg.vectorize) {
1497
+ const idx = cfg.vectorize.findIndex((b) => b.binding === bindingName);
1498
+ if (idx !== -1) {
1499
+ cfg.vectorize.splice(idx, 1);
1500
+ if (cfg.vectorize.length === 0) delete cfg.vectorize;
1501
+ found = true;
1502
+ }
1503
+ }
1504
+ if (cfg.hyperdrive) {
1505
+ const idx = cfg.hyperdrive.findIndex((b) => b.binding === bindingName);
1506
+ if (idx !== -1) {
1507
+ cfg.hyperdrive.splice(idx, 1);
1508
+ if (cfg.hyperdrive.length === 0) delete cfg.hyperdrive;
1509
+ found = true;
1510
+ }
1511
+ }
1512
+ return found;
1513
+ };
1514
+ if (env && config.env?.[env]) {
1515
+ removed = removeFromConfig(config.env[env]);
1516
+ } else {
1517
+ removed = removeFromConfig(config);
1518
+ }
1519
+ if (removed) {
1520
+ writeWranglerToml(cwd, config);
1521
+ }
1522
+ return removed;
1523
+ }
1524
+ function bindingExists(config, bindingName, env) {
1525
+ const bindings2 = extractBindings(config, env);
1526
+ return bindings2.some((b) => b.name === bindingName);
1527
+ }
1528
+ function getBindingTypeName(type) {
1529
+ switch (type) {
1530
+ case "d1":
1531
+ return "D1";
1532
+ case "kv":
1533
+ return "KV";
1534
+ case "r2":
1535
+ return "R2";
1536
+ case "queue":
1537
+ return "Queue";
1538
+ case "do":
1539
+ return "Durable Object";
1540
+ case "service":
1541
+ return "Service";
1542
+ case "secret":
1543
+ return "Secret/Var";
1544
+ case "ai":
1545
+ return "AI";
1546
+ case "vectorize":
1547
+ return "Vectorize";
1548
+ case "hyperdrive":
1549
+ return "Hyperdrive";
1550
+ default:
1551
+ return type;
1552
+ }
1553
+ }
1554
+ function truncateId(id, maxLen = 12) {
1555
+ if (id.length <= maxLen) return id;
1556
+ return id.slice(0, maxLen - 3) + "...";
1557
+ }
1558
+
1559
+ // src/commands/bindings.ts
1560
+ async function bindings(options) {
1561
+ const verbose = options.verbose ?? false;
1562
+ const logger = createLogger(verbose);
1563
+ const env = options.env;
1564
+ try {
1565
+ const cwd = process.cwd();
1566
+ const wranglerPath = findWranglerToml(cwd);
1567
+ if (!wranglerPath) {
1568
+ throw new CliError(
1569
+ "wrangler.toml not found",
1570
+ "ENOENT",
1571
+ "Create a wrangler.toml file or run this command from a Cloudwerk project directory."
1572
+ );
1573
+ }
1574
+ logger.debug(`Found wrangler config: ${wranglerPath}`);
1575
+ const config = readWranglerToml(cwd);
1576
+ const projectName = config.name || path8.basename(cwd);
1577
+ const bindingsList = extractBindings(config, env);
1578
+ const environments = getEnvironments(config);
1579
+ console.log();
1580
+ const envLabel = env ? env : "production";
1581
+ console.log(
1582
+ pc4.bold(`Bindings for ${projectName}`) + pc4.dim(` (${envLabel}):`)
1583
+ );
1584
+ console.log();
1585
+ if (bindingsList.length === 0) {
1586
+ console.log(pc4.dim(" No bindings configured."));
1587
+ console.log();
1588
+ console.log(
1589
+ pc4.dim(
1590
+ ` Use 'cloudwerk bindings add' to add a new binding${env ? ` --env ${env}` : ""}.`
1591
+ )
1592
+ );
1593
+ } else {
1594
+ console.log(
1595
+ pc4.dim(" Type Binding Resource")
1596
+ );
1597
+ console.log(
1598
+ pc4.dim(" " + "\u2500".repeat(14) + " " + "\u2500".repeat(13) + " " + "\u2500".repeat(30))
1599
+ );
1600
+ for (const binding of bindingsList) {
1601
+ const typeName = getBindingTypeName(binding.type).padEnd(14);
1602
+ const bindingName = binding.name.padEnd(13);
1603
+ let resource = "";
1604
+ if (binding.resourceName && binding.resourceId) {
1605
+ resource = `${binding.resourceName} (${truncateId(binding.resourceId)})`;
1606
+ } else if (binding.resourceName) {
1607
+ resource = binding.resourceName;
1608
+ } else if (binding.resourceId) {
1609
+ resource = `(${truncateId(binding.resourceId)})`;
1610
+ } else {
1611
+ resource = pc4.dim("(configured)");
1612
+ }
1613
+ console.log(` ${pc4.cyan(typeName)} ${pc4.bold(bindingName)} ${resource}`);
1614
+ }
1615
+ }
1616
+ console.log();
1617
+ if (environments.length > 0 && !env) {
1618
+ console.log(
1619
+ pc4.dim("Environments: ") + environments.join(", ")
1620
+ );
1621
+ console.log();
1622
+ }
1623
+ const envFlag = env ? ` --env ${env}` : "";
1624
+ console.log(pc4.dim(`Use 'cloudwerk bindings add${envFlag}' to add a new binding.`));
1625
+ if (!env && environments.length > 0) {
1626
+ console.log(
1627
+ pc4.dim(`Use 'cloudwerk bindings --env <name>' to view environment bindings.`)
1628
+ );
1629
+ }
1630
+ console.log();
1631
+ } catch (error) {
1632
+ handleCommandError(error, verbose);
1633
+ }
1634
+ }
1635
+
1636
+ // src/commands/bindings/add.ts
1637
+ import { spawn as spawn2 } from "child_process";
1638
+ import pc5 from "picocolors";
1639
+ import { select, input, confirm } from "@inquirer/prompts";
1640
+
1641
+ // src/utils/env-types.ts
1642
+ import * as fs8 from "fs";
1643
+ import * as path9 from "path";
1644
+ var TYPE_MAPPINGS = {
1645
+ d1: "D1Database",
1646
+ kv: "KVNamespace",
1647
+ r2: "R2Bucket",
1648
+ queue: "Queue",
1649
+ do: "DurableObjectNamespace",
1650
+ service: "Fetcher",
1651
+ secret: "string",
1652
+ ai: "Ai",
1653
+ vectorize: "VectorizeIndex",
1654
+ hyperdrive: "Hyperdrive"
1655
+ };
1656
+ function getTypeForBinding(type) {
1657
+ return TYPE_MAPPINGS[type] || "unknown";
1658
+ }
1659
+ function generateEnvTypes(cwd, bindings2, options = {}) {
1660
+ const outputPath = options.outputPath || path9.join(cwd, "env.d.ts");
1661
+ const includeTimestamp = options.includeTimestamp ?? true;
1662
+ const bindingsByType = /* @__PURE__ */ new Map();
1663
+ for (const binding of bindings2) {
1664
+ const existing = bindingsByType.get(binding.type) || [];
1665
+ existing.push(binding);
1666
+ bindingsByType.set(binding.type, existing);
1667
+ }
1668
+ const lines = [];
1669
+ lines.push("// Auto-generated by cloudwerk bindings - DO NOT EDIT");
1670
+ if (includeTimestamp) {
1671
+ lines.push(`// Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}`);
1672
+ }
1673
+ lines.push("");
1674
+ lines.push("interface CloudflareBindings {");
1675
+ const typeOrder = [
1676
+ "d1",
1677
+ "kv",
1678
+ "r2",
1679
+ "queue",
1680
+ "do",
1681
+ "service",
1682
+ "ai",
1683
+ "vectorize",
1684
+ "hyperdrive",
1685
+ "secret"
1686
+ ];
1687
+ const resultBindings = [];
1688
+ let firstSection = true;
1689
+ for (const type of typeOrder) {
1690
+ const typeBindings = bindingsByType.get(type);
1691
+ if (!typeBindings || typeBindings.length === 0) continue;
1692
+ const tsType = getTypeForBinding(type);
1693
+ const sectionName = getSectionName(type);
1694
+ if (!firstSection) {
1695
+ lines.push("");
1696
+ }
1697
+ lines.push(` // ${sectionName}`);
1698
+ firstSection = false;
1699
+ for (const binding of typeBindings) {
1700
+ lines.push(` ${binding.name}: ${tsType}`);
1701
+ resultBindings.push({ name: binding.name, type: tsType });
1702
+ }
1703
+ }
1704
+ lines.push("}");
1705
+ lines.push("");
1706
+ lines.push("// Re-export for convenience");
1707
+ lines.push("type Env = CloudflareBindings");
1708
+ lines.push("export type { Env, CloudflareBindings }");
1709
+ lines.push("");
1710
+ fs8.writeFileSync(outputPath, lines.join("\n"), "utf-8");
1711
+ return {
1712
+ outputPath,
1713
+ bindingCount: bindings2.length,
1714
+ bindings: resultBindings
1715
+ };
1716
+ }
1717
+ function getSectionName(type) {
1718
+ switch (type) {
1719
+ case "d1":
1720
+ return "D1 Databases";
1721
+ case "kv":
1722
+ return "KV Namespaces";
1723
+ case "r2":
1724
+ return "R2 Buckets";
1725
+ case "queue":
1726
+ return "Queues";
1727
+ case "do":
1728
+ return "Durable Objects";
1729
+ case "service":
1730
+ return "Services";
1731
+ case "secret":
1732
+ return "Environment Variables";
1733
+ case "ai":
1734
+ return "AI";
1735
+ case "vectorize":
1736
+ return "Vectorize Indexes";
1737
+ case "hyperdrive":
1738
+ return "Hyperdrive";
1739
+ default:
1740
+ return "Other";
1741
+ }
1742
+ }
1743
+
1744
+ // src/commands/bindings/add.ts
1745
+ async function bindingsAdd(bindingType, options) {
1746
+ const verbose = options.verbose ?? false;
1747
+ const logger = createLogger(verbose);
1748
+ const env = options.env;
1749
+ try {
1750
+ const cwd = process.cwd();
1751
+ const wranglerPath = findWranglerToml(cwd);
1752
+ if (!wranglerPath) {
1753
+ throw new CliError(
1754
+ "wrangler.toml not found",
1755
+ "ENOENT",
1756
+ "Create a wrangler.toml file or run this command from a Cloudwerk project directory."
1757
+ );
1758
+ }
1759
+ logger.debug(`Found wrangler config: ${wranglerPath}`);
1760
+ const type = bindingType || await promptForBindingType();
1761
+ const envLabel = env ? ` (${env})` : "";
1762
+ console.log();
1763
+ logger.info(`Adding ${type.toUpperCase()} binding${envLabel}...`);
1764
+ switch (type.toLowerCase()) {
1765
+ case "d1":
1766
+ await addD1(cwd, options);
1767
+ break;
1768
+ case "kv":
1769
+ await addKV(cwd, options);
1770
+ break;
1771
+ case "r2":
1772
+ await addR2(cwd, options);
1773
+ break;
1774
+ case "queue":
1775
+ await addQueue(cwd, options);
1776
+ break;
1777
+ case "do":
1778
+ await addDurableObject(cwd, options);
1779
+ break;
1780
+ case "secret":
1781
+ await addSecret(cwd, options);
1782
+ break;
1783
+ default:
1784
+ throw new CliError(
1785
+ `Unknown binding type: ${type}`,
1786
+ "EINVAL",
1787
+ "Valid types are: d1, kv, r2, queue, do, secret"
1788
+ );
1789
+ }
1790
+ if (!options.skipTypes) {
1791
+ await regenerateTypes(cwd);
1792
+ }
1793
+ console.log();
1794
+ logger.success("Binding added successfully!");
1795
+ console.log();
1796
+ } catch (error) {
1797
+ handleCommandError(error, verbose);
1798
+ }
1799
+ }
1800
+ async function promptForBindingType() {
1801
+ return select({
1802
+ message: "What type of binding do you want to add?",
1803
+ choices: [
1804
+ { name: "D1 Database", value: "d1" },
1805
+ { name: "KV Namespace", value: "kv" },
1806
+ { name: "R2 Bucket", value: "r2" },
1807
+ { name: "Queue", value: "queue" },
1808
+ { name: "Durable Object", value: "do" },
1809
+ { name: "Secret / Environment Variable", value: "secret" }
1810
+ ]
1811
+ });
1812
+ }
1813
+ async function addD1(cwd, options) {
1814
+ const config = readWranglerToml(cwd);
1815
+ const env = options.env;
1816
+ const bindingName = await input({
1817
+ message: "Binding name (e.g., DB):",
1818
+ validate: (value) => {
1819
+ if (!value.trim()) return "Binding name is required";
1820
+ if (!/^[A-Z_][A-Z0-9_]*$/i.test(value))
1821
+ return "Binding name must be alphanumeric with underscores";
1822
+ if (bindingExists(config, value, env))
1823
+ return `Binding "${value}" already exists`;
1824
+ return true;
1825
+ }
1826
+ });
1827
+ const databaseName = await input({
1828
+ message: "Database name:",
1829
+ default: `${config.name || "my-app"}-db${env ? `-${env}` : ""}`,
1830
+ validate: (value) => {
1831
+ if (!value.trim()) return "Database name is required";
1832
+ return true;
1833
+ }
1834
+ });
1835
+ const createNew = await confirm({
1836
+ message: "Create a new D1 database?",
1837
+ default: true
1838
+ });
1839
+ let databaseId;
1840
+ if (createNew) {
1841
+ console.log();
1842
+ console.log(pc5.dim(`Creating D1 database "${databaseName}"...`));
1843
+ const result = await runWranglerCommand(["d1", "create", databaseName]);
1844
+ const idMatch = result.match(/database_id\s*=\s*"([^"]+)"/);
1845
+ if (!idMatch) {
1846
+ throw new CliError(
1847
+ "Failed to parse database ID from wrangler output",
1848
+ "EPARSE",
1849
+ "Try creating the database manually with: wrangler d1 create " + databaseName
1850
+ );
1851
+ }
1852
+ databaseId = idMatch[1];
1853
+ console.log(pc5.green("\u2713") + ` Created database: ${databaseId}`);
1854
+ } else {
1855
+ const databases = await listD1Databases();
1856
+ if (databases.length === 0) {
1857
+ throw new CliError(
1858
+ "No D1 databases found",
1859
+ "ENOENT",
1860
+ "Create a database first with: wrangler d1 create <name>"
1861
+ );
1862
+ }
1863
+ const selected = await select({
1864
+ message: "Select an existing database:",
1865
+ choices: databases.map((db) => ({
1866
+ name: `${db.name} (${db.uuid.slice(0, 8)}...)`,
1867
+ value: db.uuid
1868
+ }))
1869
+ });
1870
+ databaseId = selected;
1871
+ }
1872
+ addD1Binding(cwd, bindingName, databaseName, databaseId, env);
1873
+ console.log(pc5.green("\u2713") + " Updated wrangler.toml");
1874
+ showTypeHint(bindingName, "d1");
1875
+ }
1876
+ async function listD1Databases() {
1877
+ const output = await runWranglerCommand(["d1", "list", "--json"]);
1878
+ return JSON.parse(output);
1879
+ }
1880
+ async function addKV(cwd, options) {
1881
+ const config = readWranglerToml(cwd);
1882
+ const env = options.env;
1883
+ const bindingName = await input({
1884
+ message: "Binding name (e.g., CACHE):",
1885
+ validate: (value) => {
1886
+ if (!value.trim()) return "Binding name is required";
1887
+ if (!/^[A-Z_][A-Z0-9_]*$/i.test(value))
1888
+ return "Binding name must be alphanumeric with underscores";
1889
+ if (bindingExists(config, value, env))
1890
+ return `Binding "${value}" already exists`;
1891
+ return true;
1892
+ }
1893
+ });
1894
+ const createNew = await confirm({
1895
+ message: "Create a new KV namespace?",
1896
+ default: true
1897
+ });
1898
+ let namespaceId;
1899
+ if (createNew) {
1900
+ console.log();
1901
+ console.log(pc5.dim(`Creating KV namespace "${bindingName}"...`));
1902
+ const result = await runWranglerCommand([
1903
+ "kv",
1904
+ "namespace",
1905
+ "create",
1906
+ bindingName
1907
+ ]);
1908
+ const idMatch = result.match(/id\s*=\s*"([^"]+)"/);
1909
+ if (!idMatch) {
1910
+ throw new CliError(
1911
+ "Failed to parse namespace ID from wrangler output",
1912
+ "EPARSE",
1913
+ "Try creating the namespace manually with: wrangler kv namespace create " + bindingName
1914
+ );
1915
+ }
1916
+ namespaceId = idMatch[1];
1917
+ console.log(pc5.green("\u2713") + ` Created namespace: ${namespaceId}`);
1918
+ } else {
1919
+ const namespaces = await listKVNamespaces();
1920
+ if (namespaces.length === 0) {
1921
+ throw new CliError(
1922
+ "No KV namespaces found",
1923
+ "ENOENT",
1924
+ "Create a namespace first with: wrangler kv namespace create <name>"
1925
+ );
1926
+ }
1927
+ const selected = await select({
1928
+ message: "Select an existing namespace:",
1929
+ choices: namespaces.map((ns) => ({
1930
+ name: `${ns.title} (${ns.id.slice(0, 8)}...)`,
1931
+ value: ns.id
1932
+ }))
1933
+ });
1934
+ namespaceId = selected;
1935
+ }
1936
+ addKVBinding(cwd, bindingName, namespaceId, void 0, env);
1937
+ console.log(pc5.green("\u2713") + " Updated wrangler.toml");
1938
+ showTypeHint(bindingName, "kv");
1939
+ }
1940
+ async function listKVNamespaces() {
1941
+ const output = await runWranglerCommand(["kv", "namespace", "list", "--json"]);
1942
+ return JSON.parse(output);
1943
+ }
1944
+ async function addR2(cwd, options) {
1945
+ const config = readWranglerToml(cwd);
1946
+ const env = options.env;
1947
+ const bindingName = await input({
1948
+ message: "Binding name (e.g., STORAGE):",
1949
+ validate: (value) => {
1950
+ if (!value.trim()) return "Binding name is required";
1951
+ if (!/^[A-Z_][A-Z0-9_]*$/i.test(value))
1952
+ return "Binding name must be alphanumeric with underscores";
1953
+ if (bindingExists(config, value, env))
1954
+ return `Binding "${value}" already exists`;
1955
+ return true;
1956
+ }
1957
+ });
1958
+ const createNew = await confirm({
1959
+ message: "Create a new R2 bucket?",
1960
+ default: true
1961
+ });
1962
+ let bucketName;
1963
+ if (createNew) {
1964
+ bucketName = await input({
1965
+ message: "Bucket name:",
1966
+ default: `${config.name || "my-app"}-bucket${env ? `-${env}` : ""}`,
1967
+ validate: (value) => {
1968
+ if (!value.trim()) return "Bucket name is required";
1969
+ return true;
1970
+ }
1971
+ });
1972
+ console.log();
1973
+ console.log(pc5.dim(`Creating R2 bucket "${bucketName}"...`));
1974
+ await runWranglerCommand(["r2", "bucket", "create", bucketName]);
1975
+ console.log(pc5.green("\u2713") + ` Created bucket: ${bucketName}`);
1976
+ } else {
1977
+ const buckets = await listR2Buckets();
1978
+ if (buckets.length === 0) {
1979
+ throw new CliError(
1980
+ "No R2 buckets found",
1981
+ "ENOENT",
1982
+ "Create a bucket first with: wrangler r2 bucket create <name>"
1983
+ );
1984
+ }
1985
+ const selected = await select({
1986
+ message: "Select an existing bucket:",
1987
+ choices: buckets.map((bucket) => ({
1988
+ name: bucket.name,
1989
+ value: bucket.name
1990
+ }))
1991
+ });
1992
+ bucketName = selected;
1993
+ }
1994
+ addR2Binding(cwd, bindingName, bucketName, env);
1995
+ console.log(pc5.green("\u2713") + " Updated wrangler.toml");
1996
+ showTypeHint(bindingName, "r2");
1997
+ }
1998
+ async function listR2Buckets() {
1999
+ const output = await runWranglerCommand(["r2", "bucket", "list", "--json"]);
2000
+ return JSON.parse(output);
2001
+ }
2002
+ async function addQueue(cwd, options) {
2003
+ const config = readWranglerToml(cwd);
2004
+ const env = options.env;
2005
+ const bindingName = await input({
2006
+ message: "Binding name (e.g., JOBS):",
2007
+ validate: (value) => {
2008
+ if (!value.trim()) return "Binding name is required";
2009
+ if (!/^[A-Z_][A-Z0-9_]*$/i.test(value))
2010
+ return "Binding name must be alphanumeric with underscores";
2011
+ if (bindingExists(config, value, env))
2012
+ return `Binding "${value}" already exists`;
2013
+ return true;
2014
+ }
2015
+ });
2016
+ const createNew = await confirm({
2017
+ message: "Create a new queue?",
2018
+ default: true
2019
+ });
2020
+ let queueName;
2021
+ if (createNew) {
2022
+ queueName = await input({
2023
+ message: "Queue name:",
2024
+ default: `${config.name || "my-app"}-queue${env ? `-${env}` : ""}`,
2025
+ validate: (value) => {
2026
+ if (!value.trim()) return "Queue name is required";
2027
+ return true;
2028
+ }
2029
+ });
2030
+ console.log();
2031
+ console.log(pc5.dim(`Creating queue "${queueName}"...`));
2032
+ await runWranglerCommand(["queues", "create", queueName]);
2033
+ console.log(pc5.green("\u2713") + ` Created queue: ${queueName}`);
2034
+ } else {
2035
+ const queues = await listQueues();
2036
+ if (queues.length === 0) {
2037
+ throw new CliError(
2038
+ "No queues found",
2039
+ "ENOENT",
2040
+ "Create a queue first with: wrangler queues create <name>"
2041
+ );
2042
+ }
2043
+ const selected = await select({
2044
+ message: "Select an existing queue:",
2045
+ choices: queues.map((queue) => ({
2046
+ name: queue.queue_name,
2047
+ value: queue.queue_name
2048
+ }))
2049
+ });
2050
+ queueName = selected;
2051
+ }
2052
+ addQueueBinding(cwd, bindingName, queueName, env);
2053
+ console.log(pc5.green("\u2713") + " Updated wrangler.toml");
2054
+ showTypeHint(bindingName, "queue");
2055
+ }
2056
+ async function listQueues() {
2057
+ const output = await runWranglerCommand(["queues", "list", "--json"]);
2058
+ return JSON.parse(output);
2059
+ }
2060
+ async function addDurableObject(cwd, options) {
2061
+ const config = readWranglerToml(cwd);
2062
+ const env = options.env;
2063
+ const bindingName = await input({
2064
+ message: "Binding name (e.g., COUNTER):",
2065
+ validate: (value) => {
2066
+ if (!value.trim()) return "Binding name is required";
2067
+ if (!/^[A-Z_][A-Z0-9_]*$/i.test(value))
2068
+ return "Binding name must be alphanumeric with underscores";
2069
+ if (bindingExists(config, value, env))
2070
+ return `Binding "${value}" already exists`;
2071
+ return true;
2072
+ }
2073
+ });
2074
+ const className = await input({
2075
+ message: "Durable Object class name:",
2076
+ default: bindingName.split("_").map((s) => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase()).join(""),
2077
+ validate: (value) => {
2078
+ if (!value.trim()) return "Class name is required";
2079
+ return true;
2080
+ }
2081
+ });
2082
+ const isExternal = await confirm({
2083
+ message: "Is this a Durable Object from another Worker?",
2084
+ default: false
2085
+ });
2086
+ let scriptName;
2087
+ if (isExternal) {
2088
+ scriptName = await input({
2089
+ message: "Worker script name:",
2090
+ validate: (value) => {
2091
+ if (!value.trim()) return "Script name is required";
2092
+ return true;
2093
+ }
2094
+ });
2095
+ }
2096
+ addDurableObjectBinding(cwd, bindingName, className, scriptName, env);
2097
+ console.log(pc5.green("\u2713") + " Updated wrangler.toml");
2098
+ showTypeHint(bindingName, "do");
2099
+ if (!isExternal) {
2100
+ console.log();
2101
+ console.log(pc5.dim("Remember to export your Durable Object class:"));
2102
+ console.log(pc5.dim(` export class ${className} extends DurableObject { ... }`));
2103
+ }
2104
+ }
2105
+ async function addSecret(cwd, options) {
2106
+ const config = readWranglerToml(cwd);
2107
+ const env = options.env;
2108
+ const varName = await input({
2109
+ message: "Variable name (e.g., API_KEY):",
2110
+ validate: (value) => {
2111
+ if (!value.trim()) return "Variable name is required";
2112
+ if (!/^[A-Z_][A-Z0-9_]*$/i.test(value))
2113
+ return "Variable name must be alphanumeric with underscores";
2114
+ if (bindingExists(config, value, env))
2115
+ return `Variable "${value}" already exists`;
2116
+ return true;
2117
+ }
2118
+ });
2119
+ const isSecret = await confirm({
2120
+ message: "Is this a secret (sensitive value)?",
2121
+ default: true
2122
+ });
2123
+ if (isSecret) {
2124
+ console.log();
2125
+ console.log(pc5.dim("Enter the secret value (will be hidden):"));
2126
+ const varValue = await input({
2127
+ message: "Secret value:"
2128
+ });
2129
+ console.log();
2130
+ console.log(pc5.dim(`Setting secret "${varName}"...`));
2131
+ const args = ["secret", "put", varName];
2132
+ if (env) args.push("--env", env);
2133
+ await runWranglerCommandWithInput(args, varValue);
2134
+ console.log(pc5.green("\u2713") + ` Secret "${varName}" set`);
2135
+ console.log();
2136
+ console.log(
2137
+ pc5.dim("Note: Secrets are stored securely and not written to wrangler.toml.")
2138
+ );
2139
+ } else {
2140
+ const varValue = await input({
2141
+ message: "Variable value:",
2142
+ validate: (value) => {
2143
+ if (!value.trim()) return "Value is required";
2144
+ return true;
2145
+ }
2146
+ });
2147
+ addSecretBinding(cwd, varName, varValue, env);
2148
+ console.log(pc5.green("\u2713") + " Updated wrangler.toml");
2149
+ }
2150
+ showTypeHint(varName, "secret");
2151
+ }
2152
+ async function regenerateTypes(cwd) {
2153
+ const config = readWranglerToml(cwd);
2154
+ const bindings2 = extractBindings(config);
2155
+ if (bindings2.length === 0) {
2156
+ return;
2157
+ }
2158
+ const result = generateEnvTypes(cwd, bindings2);
2159
+ console.log(pc5.green("\u2713") + ` Updated env.d.ts with ${result.bindingCount} binding(s)`);
2160
+ }
2161
+ function showTypeHint(bindingName, type) {
2162
+ const tsType = getTypeForBinding(type);
2163
+ console.log();
2164
+ console.log(pc5.dim(`TypeScript type: ${bindingName}: ${tsType}`));
2165
+ }
2166
+ function runWranglerCommand(args) {
2167
+ return new Promise((resolve4, reject) => {
2168
+ let stdout = "";
2169
+ let stderr = "";
2170
+ const child = spawn2("npx", ["wrangler", ...args], {
2171
+ stdio: ["pipe", "pipe", "pipe"]
2172
+ });
2173
+ child.stdout?.on("data", (data) => {
2174
+ stdout += data.toString();
2175
+ });
2176
+ child.stderr?.on("data", (data) => {
2177
+ stderr += data.toString();
2178
+ });
2179
+ child.on("error", (error) => {
2180
+ reject(error);
2181
+ });
2182
+ child.on("close", (code) => {
2183
+ if (code !== 0) {
2184
+ reject(new Error(stderr || `wrangler exited with code ${code}`));
2185
+ } else {
2186
+ resolve4(stdout);
2187
+ }
2188
+ });
2189
+ });
2190
+ }
2191
+ function runWranglerCommandWithInput(args, inputData) {
2192
+ return new Promise((resolve4, reject) => {
2193
+ let stdout = "";
2194
+ let stderr = "";
2195
+ const child = spawn2("npx", ["wrangler", ...args], {
2196
+ stdio: ["pipe", "pipe", "pipe"]
2197
+ });
2198
+ child.stdout?.on("data", (data) => {
2199
+ stdout += data.toString();
2200
+ });
2201
+ child.stderr?.on("data", (data) => {
2202
+ stderr += data.toString();
2203
+ });
2204
+ child.on("error", (error) => {
2205
+ reject(error);
2206
+ });
2207
+ child.on("close", (code) => {
2208
+ if (code !== 0) {
2209
+ reject(new Error(stderr || `wrangler exited with code ${code}`));
2210
+ } else {
2211
+ resolve4(stdout);
2212
+ }
2213
+ });
2214
+ child.stdin?.write(inputData);
2215
+ child.stdin?.end();
2216
+ });
2217
+ }
2218
+
2219
+ // src/commands/bindings/remove.ts
2220
+ import pc6 from "picocolors";
2221
+ import { confirm as confirm2, select as select2 } from "@inquirer/prompts";
2222
+ async function bindingsRemove(bindingName, options) {
2223
+ const verbose = options.verbose ?? false;
2224
+ const logger = createLogger(verbose);
2225
+ const env = options.env;
2226
+ try {
2227
+ const cwd = process.cwd();
2228
+ const wranglerPath = findWranglerToml(cwd);
2229
+ if (!wranglerPath) {
2230
+ throw new CliError(
2231
+ "wrangler.toml not found",
2232
+ "ENOENT",
2233
+ "Create a wrangler.toml file or run this command from a Cloudwerk project directory."
2234
+ );
2235
+ }
2236
+ logger.debug(`Found wrangler config: ${wranglerPath}`);
2237
+ const config = readWranglerToml(cwd);
2238
+ const bindings2 = extractBindings(config, env);
2239
+ if (bindings2.length === 0) {
2240
+ const envLabel = env ? ` in ${env}` : "";
2241
+ throw new CliError(
2242
+ `No bindings found${envLabel}`,
2243
+ "ENOENT",
2244
+ `Use 'cloudwerk bindings add' to add a binding first.`
2245
+ );
2246
+ }
2247
+ let targetBinding = bindingName;
2248
+ if (!targetBinding) {
2249
+ targetBinding = await select2({
2250
+ message: "Select a binding to remove:",
2251
+ choices: bindings2.map((b) => ({
2252
+ name: `${b.name} (${getBindingTypeName(b.type)})`,
2253
+ value: b.name
2254
+ }))
2255
+ });
2256
+ }
2257
+ const binding = bindings2.find((b) => b.name === targetBinding);
2258
+ if (!binding) {
2259
+ const envLabel = env ? ` in ${env}` : "";
2260
+ throw new CliError(
2261
+ `Binding "${targetBinding}" not found${envLabel}`,
2262
+ "ENOENT",
2263
+ `Use 'cloudwerk bindings' to see available bindings.`
2264
+ );
2265
+ }
2266
+ if (!options.force) {
2267
+ const confirmed = await confirm2({
2268
+ message: `Remove binding "${targetBinding}" (${getBindingTypeName(binding.type)})?`,
2269
+ default: false
2270
+ });
2271
+ if (!confirmed) {
2272
+ console.log(pc6.dim("Cancelled."));
2273
+ return;
2274
+ }
2275
+ }
2276
+ console.log();
2277
+ const removed = removeBinding(cwd, targetBinding, env);
2278
+ if (!removed) {
2279
+ throw new CliError(
2280
+ `Failed to remove binding "${targetBinding}"`,
2281
+ "EREMOVE",
2282
+ "The binding may have already been removed."
2283
+ );
2284
+ }
2285
+ console.log(pc6.green("\u2713") + ` Removed binding "${targetBinding}" from wrangler.toml`);
2286
+ if (!options.skipTypes) {
2287
+ const updatedConfig = readWranglerToml(cwd);
2288
+ const updatedBindings = extractBindings(updatedConfig);
2289
+ if (updatedBindings.length > 0) {
2290
+ const result = generateEnvTypes(cwd, updatedBindings);
2291
+ console.log(
2292
+ pc6.green("\u2713") + ` Updated env.d.ts with ${result.bindingCount} binding(s)`
2293
+ );
2294
+ } else {
2295
+ console.log(
2296
+ pc6.dim("Note: No bindings remain. Consider removing env.d.ts manually.")
2297
+ );
2298
+ }
2299
+ }
2300
+ console.log();
2301
+ logger.success("Binding removed successfully!");
2302
+ console.log();
2303
+ console.log(
2304
+ pc6.dim(
2305
+ "Note: The Cloudflare resource itself was not deleted. Use wrangler to delete the resource if needed."
2306
+ )
2307
+ );
2308
+ console.log();
2309
+ } catch (error) {
2310
+ handleCommandError(error, verbose);
2311
+ }
2312
+ }
2313
+
2314
+ // src/commands/bindings/update.ts
2315
+ import { spawn as spawn3 } from "child_process";
2316
+ import pc7 from "picocolors";
2317
+ import { input as input2, select as select3 } from "@inquirer/prompts";
2318
+ async function bindingsUpdate(bindingName, options) {
2319
+ const verbose = options.verbose ?? false;
2320
+ const logger = createLogger(verbose);
2321
+ const env = options.env;
2322
+ try {
2323
+ const cwd = process.cwd();
2324
+ const wranglerPath = findWranglerToml(cwd);
2325
+ if (!wranglerPath) {
2326
+ throw new CliError(
2327
+ "wrangler.toml not found",
2328
+ "ENOENT",
2329
+ "Create a wrangler.toml file or run this command from a Cloudwerk project directory."
2330
+ );
2331
+ }
2332
+ logger.debug(`Found wrangler config: ${wranglerPath}`);
2333
+ const config = readWranglerToml(cwd);
2334
+ const bindings2 = extractBindings(config, env);
2335
+ if (bindings2.length === 0) {
2336
+ const envLabel = env ? ` in ${env}` : "";
2337
+ throw new CliError(
2338
+ `No bindings found${envLabel}`,
2339
+ "ENOENT",
2340
+ `Use 'cloudwerk bindings add' to add a binding first.`
2341
+ );
2342
+ }
2343
+ let targetBinding = bindingName;
2344
+ if (!targetBinding) {
2345
+ targetBinding = await select3({
2346
+ message: "Select a binding to update:",
2347
+ choices: bindings2.map((b) => ({
2348
+ name: `${b.name} (${getBindingTypeName(b.type)})`,
2349
+ value: b.name
2350
+ }))
2351
+ });
2352
+ }
2353
+ const binding = bindings2.find((b) => b.name === targetBinding);
2354
+ if (!binding) {
2355
+ const envLabel = env ? ` in ${env}` : "";
2356
+ throw new CliError(
2357
+ `Binding "${targetBinding}" not found${envLabel}`,
2358
+ "ENOENT",
2359
+ `Use 'cloudwerk bindings' to see available bindings.`
2360
+ );
2361
+ }
2362
+ console.log();
2363
+ logger.info(
2364
+ `Updating ${getBindingTypeName(binding.type)} binding "${targetBinding}"...`
2365
+ );
2366
+ const updated = await updateBinding(cwd, config, binding, env);
2367
+ if (!updated) {
2368
+ console.log(pc7.dim("No changes made."));
2369
+ return;
2370
+ }
2371
+ if (!options.skipTypes) {
2372
+ const updatedConfig = readWranglerToml(cwd);
2373
+ const updatedBindings = extractBindings(updatedConfig);
2374
+ if (updatedBindings.length > 0) {
2375
+ const result = generateEnvTypes(cwd, updatedBindings);
2376
+ console.log(
2377
+ pc7.green("\u2713") + ` Updated env.d.ts with ${result.bindingCount} binding(s)`
2378
+ );
2379
+ }
2380
+ }
2381
+ console.log();
2382
+ logger.success("Binding updated successfully!");
2383
+ console.log();
2384
+ } catch (error) {
2385
+ handleCommandError(error, verbose);
2386
+ }
2387
+ }
2388
+ async function updateBinding(cwd, config, binding, env) {
2389
+ const updateOptions = getUpdateOptions(binding);
2390
+ if (updateOptions.length === 0) {
2391
+ console.log(pc7.dim("This binding type has no updatable fields."));
2392
+ return false;
2393
+ }
2394
+ const field = await select3({
2395
+ message: "What do you want to update?",
2396
+ choices: updateOptions
2397
+ });
2398
+ const targetConfig = env ? config.env?.[env] ?? config : config;
2399
+ switch (binding.type) {
2400
+ case "d1":
2401
+ return updateD1(cwd, config, targetConfig, binding.name, field);
2402
+ case "kv":
2403
+ return updateKV(cwd, config, targetConfig, binding.name, field);
2404
+ case "r2":
2405
+ return updateR2(cwd, config, targetConfig, binding.name, field);
2406
+ case "queue":
2407
+ return updateQueue(cwd, config, targetConfig, binding.name, field);
2408
+ case "do":
2409
+ return updateDurableObject(cwd, config, targetConfig, binding.name, field);
2410
+ case "secret":
2411
+ return updateSecret(cwd, config, targetConfig, binding.name, field, env);
2412
+ default:
2413
+ console.log(pc7.dim(`Updating ${binding.type} bindings is not yet supported.`));
2414
+ return false;
2415
+ }
2416
+ }
2417
+ function getUpdateOptions(binding) {
2418
+ switch (binding.type) {
2419
+ case "d1":
2420
+ return [
2421
+ { name: "Binding name", value: "name" },
2422
+ { name: "Database name", value: "database_name" },
2423
+ { name: "Database ID", value: "database_id" }
2424
+ ];
2425
+ case "kv":
2426
+ return [
2427
+ { name: "Binding name", value: "name" },
2428
+ { name: "Namespace ID", value: "id" },
2429
+ { name: "Preview ID", value: "preview_id" }
2430
+ ];
2431
+ case "r2":
2432
+ return [
2433
+ { name: "Binding name", value: "name" },
2434
+ { name: "Bucket name", value: "bucket_name" }
2435
+ ];
2436
+ case "queue":
2437
+ return [
2438
+ { name: "Binding name", value: "name" },
2439
+ { name: "Queue name", value: "queue" }
2440
+ ];
2441
+ case "do":
2442
+ return [
2443
+ { name: "Binding name", value: "name" },
2444
+ { name: "Class name", value: "class_name" },
2445
+ { name: "Script name", value: "script_name" }
2446
+ ];
2447
+ case "secret":
2448
+ return [
2449
+ { name: "Variable name", value: "name" },
2450
+ { name: "Value", value: "value" }
2451
+ ];
2452
+ default:
2453
+ return [];
2454
+ }
2455
+ }
2456
+ async function updateD1(cwd, config, targetConfig, bindingName, field) {
2457
+ const bindings2 = targetConfig.d1_databases;
2458
+ if (!bindings2) return false;
2459
+ const binding = bindings2.find((b) => b.binding === bindingName);
2460
+ if (!binding) return false;
2461
+ switch (field) {
2462
+ case "name": {
2463
+ const newName = await input2({
2464
+ message: "New binding name:",
2465
+ default: binding.binding
2466
+ });
2467
+ if (newName === binding.binding) return false;
2468
+ binding.binding = newName;
2469
+ break;
2470
+ }
2471
+ case "database_name": {
2472
+ const newName = await input2({
2473
+ message: "New database name:",
2474
+ default: binding.database_name
2475
+ });
2476
+ if (newName === binding.database_name) return false;
2477
+ binding.database_name = newName;
2478
+ break;
2479
+ }
2480
+ case "database_id": {
2481
+ const newId = await input2({
2482
+ message: "New database ID:",
2483
+ default: binding.database_id
2484
+ });
2485
+ if (newId === binding.database_id) return false;
2486
+ binding.database_id = newId;
2487
+ break;
2488
+ }
2489
+ }
2490
+ writeWranglerToml(cwd, config);
2491
+ console.log(pc7.green("\u2713") + " Updated wrangler.toml");
2492
+ return true;
2493
+ }
2494
+ async function updateKV(cwd, config, targetConfig, bindingName, field) {
2495
+ const bindings2 = targetConfig.kv_namespaces;
2496
+ if (!bindings2) return false;
2497
+ const binding = bindings2.find((b) => b.binding === bindingName);
2498
+ if (!binding) return false;
2499
+ switch (field) {
2500
+ case "name": {
2501
+ const newName = await input2({
2502
+ message: "New binding name:",
2503
+ default: binding.binding
2504
+ });
2505
+ if (newName === binding.binding) return false;
2506
+ binding.binding = newName;
2507
+ break;
2508
+ }
2509
+ case "id": {
2510
+ const newId = await input2({
2511
+ message: "New namespace ID:",
2512
+ default: binding.id
2513
+ });
2514
+ if (newId === binding.id) return false;
2515
+ binding.id = newId;
2516
+ break;
2517
+ }
2518
+ case "preview_id": {
2519
+ const newId = await input2({
2520
+ message: "New preview ID (leave empty to remove):",
2521
+ default: binding.preview_id || ""
2522
+ });
2523
+ if (newId === (binding.preview_id || "")) return false;
2524
+ if (newId) {
2525
+ binding.preview_id = newId;
2526
+ } else {
2527
+ delete binding.preview_id;
2528
+ }
2529
+ break;
2530
+ }
2531
+ }
2532
+ writeWranglerToml(cwd, config);
2533
+ console.log(pc7.green("\u2713") + " Updated wrangler.toml");
2534
+ return true;
2535
+ }
2536
+ async function updateR2(cwd, config, targetConfig, bindingName, field) {
2537
+ const bindings2 = targetConfig.r2_buckets;
2538
+ if (!bindings2) return false;
2539
+ const binding = bindings2.find((b) => b.binding === bindingName);
2540
+ if (!binding) return false;
2541
+ switch (field) {
2542
+ case "name": {
2543
+ const newName = await input2({
2544
+ message: "New binding name:",
2545
+ default: binding.binding
2546
+ });
2547
+ if (newName === binding.binding) return false;
2548
+ binding.binding = newName;
2549
+ break;
2550
+ }
2551
+ case "bucket_name": {
2552
+ const newName = await input2({
2553
+ message: "New bucket name:",
2554
+ default: binding.bucket_name
2555
+ });
2556
+ if (newName === binding.bucket_name) return false;
2557
+ binding.bucket_name = newName;
2558
+ break;
2559
+ }
2560
+ }
2561
+ writeWranglerToml(cwd, config);
2562
+ console.log(pc7.green("\u2713") + " Updated wrangler.toml");
2563
+ return true;
2564
+ }
2565
+ async function updateQueue(cwd, config, targetConfig, bindingName, field) {
2566
+ const bindings2 = targetConfig.queues?.producers;
2567
+ if (!bindings2) return false;
2568
+ const binding = bindings2.find((b) => b.binding === bindingName);
2569
+ if (!binding) return false;
2570
+ switch (field) {
2571
+ case "name": {
2572
+ const newName = await input2({
2573
+ message: "New binding name:",
2574
+ default: binding.binding
2575
+ });
2576
+ if (newName === binding.binding) return false;
2577
+ binding.binding = newName;
2578
+ break;
2579
+ }
2580
+ case "queue": {
2581
+ const newName = await input2({
2582
+ message: "New queue name:",
2583
+ default: binding.queue
2584
+ });
2585
+ if (newName === binding.queue) return false;
2586
+ binding.queue = newName;
2587
+ break;
2588
+ }
2589
+ }
2590
+ writeWranglerToml(cwd, config);
2591
+ console.log(pc7.green("\u2713") + " Updated wrangler.toml");
2592
+ return true;
2593
+ }
2594
+ async function updateDurableObject(cwd, config, targetConfig, bindingName, field) {
2595
+ const bindings2 = targetConfig.durable_objects?.bindings;
2596
+ if (!bindings2) return false;
2597
+ const binding = bindings2.find((b) => b.name === bindingName);
2598
+ if (!binding) return false;
2599
+ switch (field) {
2600
+ case "name": {
2601
+ const newName = await input2({
2602
+ message: "New binding name:",
2603
+ default: binding.name
2604
+ });
2605
+ if (newName === binding.name) return false;
2606
+ binding.name = newName;
2607
+ break;
2608
+ }
2609
+ case "class_name": {
2610
+ const newName = await input2({
2611
+ message: "New class name:",
2612
+ default: binding.class_name
2613
+ });
2614
+ if (newName === binding.class_name) return false;
2615
+ binding.class_name = newName;
2616
+ break;
2617
+ }
2618
+ case "script_name": {
2619
+ const newName = await input2({
2620
+ message: "New script name (leave empty to remove):",
2621
+ default: binding.script_name || ""
2622
+ });
2623
+ if (newName === (binding.script_name || "")) return false;
2624
+ if (newName) {
2625
+ binding.script_name = newName;
2626
+ } else {
2627
+ delete binding.script_name;
2628
+ }
2629
+ break;
2630
+ }
2631
+ }
2632
+ writeWranglerToml(cwd, config);
2633
+ console.log(pc7.green("\u2713") + " Updated wrangler.toml");
2634
+ return true;
2635
+ }
2636
+ async function updateSecret(cwd, config, targetConfig, varName, field, env) {
2637
+ const vars = targetConfig.vars;
2638
+ const isInToml = vars && varName in vars;
2639
+ if (!isInToml) {
2640
+ if (field === "name") {
2641
+ console.log();
2642
+ console.log(
2643
+ pc7.yellow("\u26A0") + " Secrets managed by wrangler cannot be renamed directly."
2644
+ );
2645
+ console.log(
2646
+ pc7.dim(
2647
+ " To rename, delete the old secret and create a new one with:"
2648
+ )
2649
+ );
2650
+ console.log(pc7.dim(` wrangler secret delete ${varName}`));
2651
+ console.log(pc7.dim(` wrangler secret put <new-name>`));
2652
+ return false;
2653
+ }
2654
+ console.log();
2655
+ console.log(
2656
+ pc7.dim("This is a secret managed by wrangler. Enter the new value:")
2657
+ );
2658
+ const newValue = await input2({
2659
+ message: "New secret value:"
2660
+ });
2661
+ if (!newValue.trim()) {
2662
+ console.log(pc7.dim("No value provided."));
2663
+ return false;
2664
+ }
2665
+ const args = ["secret", "put", varName];
2666
+ if (env) args.push("--env", env);
2667
+ console.log();
2668
+ console.log(pc7.dim(`Updating secret "${varName}"...`));
2669
+ await runWranglerCommandWithInput2(args, newValue);
2670
+ console.log(pc7.green("\u2713") + ` Secret "${varName}" updated`);
2671
+ return true;
2672
+ }
2673
+ switch (field) {
2674
+ case "name": {
2675
+ const newName = await input2({
2676
+ message: "New variable name:",
2677
+ default: varName
2678
+ });
2679
+ if (newName === varName) return false;
2680
+ const value = vars[varName];
2681
+ delete vars[varName];
2682
+ vars[newName] = value;
2683
+ break;
2684
+ }
2685
+ case "value": {
2686
+ const newValue = await input2({
2687
+ message: "New value:",
2688
+ default: vars[varName]
2689
+ });
2690
+ if (newValue === vars[varName]) return false;
2691
+ vars[varName] = newValue;
2692
+ break;
2693
+ }
2694
+ }
2695
+ writeWranglerToml(cwd, config);
2696
+ console.log(pc7.green("\u2713") + " Updated wrangler.toml");
2697
+ return true;
2698
+ }
2699
+ function runWranglerCommandWithInput2(args, inputData) {
2700
+ return new Promise((resolve4, reject) => {
2701
+ let stdout = "";
2702
+ let stderr = "";
2703
+ const child = spawn3("npx", ["wrangler", ...args], {
2704
+ stdio: ["pipe", "pipe", "pipe"]
2705
+ });
2706
+ child.stdout?.on("data", (data) => {
2707
+ stdout += data.toString();
2708
+ });
2709
+ child.stderr?.on("data", (data) => {
2710
+ stderr += data.toString();
2711
+ });
2712
+ child.on("error", (error) => {
2713
+ reject(error);
2714
+ });
2715
+ child.on("close", (code) => {
2716
+ if (code !== 0) {
2717
+ reject(new Error(stderr || `wrangler exited with code ${code}`));
2718
+ } else {
2719
+ resolve4(stdout);
2720
+ }
2721
+ });
2722
+ child.stdin?.write(inputData);
2723
+ child.stdin?.end();
2724
+ });
2725
+ }
2726
+
2727
+ // src/commands/bindings/generate-types.ts
2728
+ import pc8 from "picocolors";
2729
+ async function bindingsGenerateTypes(options) {
2730
+ const verbose = options.verbose ?? false;
2731
+ const logger = createLogger(verbose);
2732
+ try {
2733
+ const cwd = process.cwd();
2734
+ const wranglerPath = findWranglerToml(cwd);
2735
+ if (!wranglerPath) {
2736
+ throw new CliError(
2737
+ "wrangler.toml not found",
2738
+ "ENOENT",
2739
+ "Create a wrangler.toml file or run this command from a Cloudwerk project directory."
2740
+ );
2741
+ }
2742
+ logger.debug(`Found wrangler config: ${wranglerPath}`);
2743
+ const config = readWranglerToml(cwd);
2744
+ const bindings2 = extractBindings(config);
2745
+ if (bindings2.length === 0) {
2746
+ console.log();
2747
+ console.log(pc8.yellow("No bindings found in wrangler.toml."));
2748
+ console.log();
2749
+ console.log(
2750
+ pc8.dim(`Use 'cloudwerk bindings add' to add a binding first.`)
2751
+ );
2752
+ console.log();
2753
+ return;
2754
+ }
2755
+ console.log();
2756
+ logger.info("Generating TypeScript types...");
2757
+ const result = generateEnvTypes(cwd, bindings2);
2758
+ console.log();
2759
+ console.log(
2760
+ pc8.green("\u2713") + ` Updated ${pc8.bold("env.d.ts")} with ${result.bindingCount} binding(s):`
2761
+ );
2762
+ console.log();
2763
+ for (const binding of result.bindings) {
2764
+ console.log(` ${pc8.cyan(binding.name)}: ${pc8.dim(binding.type)}`);
2765
+ }
2766
+ console.log();
2767
+ logger.success("Types generated successfully!");
2768
+ console.log();
2769
+ console.log(
2770
+ pc8.dim("Make sure env.d.ts is included in your tsconfig.json:")
2771
+ );
2772
+ console.log(
2773
+ pc8.dim(' "include": ["env.d.ts", "app/**/*"]')
2774
+ );
2775
+ console.log();
2776
+ } catch (error) {
2777
+ handleCommandError(error, verbose);
2778
+ }
2779
+ }
2780
+
1061
2781
  // src/index.ts
1062
2782
  program.name("cloudwerk").description("Cloudwerk CLI - Build and deploy full-stack apps to Cloudflare").version(VERSION);
1063
2783
  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);
1064
2784
  program.command("build [path]").description("Build project for production deployment to Cloudflare Workers").option("-o, --output <dir>", "Output directory", "./dist").option("--ssg", "Generate static pages for routes with rendering: static").option("--minify", "Minify bundles (default: true)").option("--no-minify", "Disable minification").option("--sourcemap", "Generate source maps").option("-c, --config <path>", "Path to config file").option("--verbose", "Enable verbose logging").action(build);
2785
+ program.command("deploy [path]").description("Deploy to Cloudflare Workers").option("-e, --env <environment>", "Environment to deploy to").option("--dry-run", "Preview deployment without executing").option("--skip-build", "Skip the build step").option("-c, --config <path>", "Path to config file").option("--verbose", "Enable verbose logging").action(deploy);
1065
2786
  var configCmd = program.command("config").description("Manage Cloudwerk configuration");
1066
2787
  configCmd.command("get <key>").description("Get a configuration value").action(configGet);
1067
2788
  configCmd.command("set <key> <value>").description("Set a configuration value").action(configSet);
2789
+ var bindingsCmd = program.command("bindings").description("Manage Cloudflare bindings (D1, KV, R2, Queues, etc.)").option("-e, --env <environment>", "Environment to operate on").option("--verbose", "Enable verbose logging").action(bindings);
2790
+ bindingsCmd.command("add [type]").description("Add a new binding (d1, kv, r2, queue, do, secret)").option("-e, --env <environment>", "Environment to add binding to").option("--skip-types", "Skip TypeScript type generation").option("--verbose", "Enable verbose logging").action(bindingsAdd);
2791
+ bindingsCmd.command("remove [name]").description("Remove a binding").option("-e, --env <environment>", "Environment to remove binding from").option("-f, --force", "Skip confirmation prompt").option("--skip-types", "Skip TypeScript type generation").option("--verbose", "Enable verbose logging").action(bindingsRemove);
2792
+ bindingsCmd.command("update [name]").description("Update an existing binding").option("-e, --env <environment>", "Environment to update binding in").option("--skip-types", "Skip TypeScript type generation").option("--verbose", "Enable verbose logging").action(bindingsUpdate);
2793
+ bindingsCmd.command("generate-types").description("Regenerate TypeScript type definitions").option("--verbose", "Enable verbose logging").action(bindingsGenerateTypes);
1068
2794
  program.parse();