@cloudwerk/cli 0.10.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.
- package/dist/index.js +1635 -0
- package/package.json +6 -4
package/dist/index.js
CHANGED
|
@@ -1148,6 +1148,1636 @@ async function askConfirmation(question, defaultValue) {
|
|
|
1148
1148
|
});
|
|
1149
1149
|
}
|
|
1150
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
|
+
|
|
1151
2781
|
// src/index.ts
|
|
1152
2782
|
program.name("cloudwerk").description("Cloudwerk CLI - Build and deploy full-stack apps to Cloudflare").version(VERSION);
|
|
1153
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);
|
|
@@ -1156,4 +2786,9 @@ program.command("deploy [path]").description("Deploy to Cloudflare Workers").opt
|
|
|
1156
2786
|
var configCmd = program.command("config").description("Manage Cloudwerk configuration");
|
|
1157
2787
|
configCmd.command("get <key>").description("Get a configuration value").action(configGet);
|
|
1158
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);
|
|
1159
2794
|
program.parse();
|