@bagdock/cli 0.6.1 → 0.7.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/bagdock.js +153 -39
- package/package.json +1 -1
package/dist/bagdock.js
CHANGED
|
@@ -4638,16 +4638,40 @@ function requireConfig() {
|
|
|
4638
4638
|
}
|
|
4639
4639
|
return config;
|
|
4640
4640
|
}
|
|
4641
|
-
|
|
4641
|
+
function parseTarget(target) {
|
|
4642
|
+
if (!target)
|
|
4643
|
+
return;
|
|
4644
|
+
const t = target.toLowerCase();
|
|
4645
|
+
if (t === "staging")
|
|
4646
|
+
return ["staging"];
|
|
4647
|
+
if (t === "production")
|
|
4648
|
+
return ["production"];
|
|
4649
|
+
if (t === "both")
|
|
4650
|
+
return ["staging", "production"];
|
|
4651
|
+
console.error(source_default.red(`Invalid --target: ${target}. Use staging, production, or both.`));
|
|
4652
|
+
process.exit(1);
|
|
4653
|
+
}
|
|
4654
|
+
async function envList(opts) {
|
|
4642
4655
|
const config = requireConfig();
|
|
4643
4656
|
try {
|
|
4657
|
+
if (opts?.reconcile) {
|
|
4658
|
+
status("Reconciling with Cloudflare...");
|
|
4659
|
+
const reconcileRes = await apiFetchJson(`/api/v1/developer/apps/${config.slug}/env/reconcile`, "POST", {});
|
|
4660
|
+
if (!reconcileRes.ok) {
|
|
4661
|
+
console.error(source_default.yellow(`Reconcile failed (${reconcileRes.status}) — showing cached data`));
|
|
4662
|
+
}
|
|
4663
|
+
}
|
|
4644
4664
|
const res = await apiFetch(`/api/v1/developer/apps/${config.slug}/env`);
|
|
4645
4665
|
if (!res.ok) {
|
|
4646
4666
|
console.error(source_default.red(`Failed to list env vars (${res.status})`));
|
|
4647
4667
|
process.exit(1);
|
|
4648
4668
|
}
|
|
4649
|
-
const
|
|
4650
|
-
if (
|
|
4669
|
+
const body = await res.json();
|
|
4670
|
+
if (isJsonMode()) {
|
|
4671
|
+
outputSuccess(body);
|
|
4672
|
+
return;
|
|
4673
|
+
}
|
|
4674
|
+
if (!body.data.length) {
|
|
4651
4675
|
console.log(source_default.yellow("No environment variables set."));
|
|
4652
4676
|
console.log("Use", source_default.cyan("bagdock env set <KEY> <VALUE>"), "to add one.");
|
|
4653
4677
|
return;
|
|
@@ -4655,8 +4679,13 @@ async function envList() {
|
|
|
4655
4679
|
console.log(source_default.bold(`
|
|
4656
4680
|
Environment variables for ${config.slug}:
|
|
4657
4681
|
`));
|
|
4658
|
-
for (const v of data) {
|
|
4659
|
-
|
|
4682
|
+
for (const v of body.data) {
|
|
4683
|
+
const envLabel = v.environments?.length ? source_default.dim(`[${v.environments.join(", ")}]`) : source_default.dim("[no target]");
|
|
4684
|
+
console.log(` ${source_default.cyan(v.key)} ${envLabel} ${source_default.dim(`updated ${v.updatedAt}`)}`);
|
|
4685
|
+
}
|
|
4686
|
+
if (body.last_reconciled_at) {
|
|
4687
|
+
console.log(source_default.dim(`
|
|
4688
|
+
Last synced with Cloudflare: ${body.last_reconciled_at}`));
|
|
4660
4689
|
}
|
|
4661
4690
|
console.log();
|
|
4662
4691
|
} catch (err) {
|
|
@@ -4664,22 +4693,40 @@ Environment variables for ${config.slug}:
|
|
|
4664
4693
|
process.exit(1);
|
|
4665
4694
|
}
|
|
4666
4695
|
}
|
|
4667
|
-
async function envSet(key, value) {
|
|
4696
|
+
async function envSet(key, value, opts) {
|
|
4668
4697
|
const config = requireConfig();
|
|
4698
|
+
const environments = parseTarget(opts?.target);
|
|
4669
4699
|
try {
|
|
4670
|
-
const
|
|
4700
|
+
const payload = { key, value };
|
|
4701
|
+
if (environments)
|
|
4702
|
+
payload.environments = environments;
|
|
4703
|
+
const res = await apiFetchJson(`/api/v1/developer/apps/${config.slug}/env`, "PUT", payload);
|
|
4671
4704
|
if (!res.ok) {
|
|
4672
4705
|
const body = await res.text();
|
|
4673
4706
|
console.error(source_default.red(`Failed to set ${key} (${res.status}):`), body.slice(0, 200));
|
|
4674
4707
|
process.exit(1);
|
|
4675
4708
|
}
|
|
4676
|
-
|
|
4709
|
+
const result = await res.json();
|
|
4710
|
+
if (isJsonMode()) {
|
|
4711
|
+
outputSuccess(result);
|
|
4712
|
+
return;
|
|
4713
|
+
}
|
|
4714
|
+
if (result.status === "partial") {
|
|
4715
|
+
console.log(source_default.yellow(`Partially set ${key}:`));
|
|
4716
|
+
for (const [env2, status2] of Object.entries(result.environments ?? {})) {
|
|
4717
|
+
const icon = status2 === "ok" ? source_default.green("✓") : source_default.red("✗");
|
|
4718
|
+
console.log(` ${icon} ${env2}: ${status2}`);
|
|
4719
|
+
}
|
|
4720
|
+
} else {
|
|
4721
|
+
const targetLabel = environments ? ` (${environments.join(", ")})` : "";
|
|
4722
|
+
console.log(source_default.green(`Set ${key}${targetLabel}`));
|
|
4723
|
+
}
|
|
4677
4724
|
} catch (err) {
|
|
4678
4725
|
console.error(source_default.red("Failed:"), err.message);
|
|
4679
4726
|
process.exit(1);
|
|
4680
4727
|
}
|
|
4681
4728
|
}
|
|
4682
|
-
async function envRemove(key) {
|
|
4729
|
+
async function envRemove(key, opts) {
|
|
4683
4730
|
const config = requireConfig();
|
|
4684
4731
|
try {
|
|
4685
4732
|
const res = await apiFetch(`/api/v1/developer/apps/${config.slug}/env/${key}`, { method: "DELETE" });
|
|
@@ -4687,7 +4734,12 @@ async function envRemove(key) {
|
|
|
4687
4734
|
console.error(source_default.red(`Failed to remove ${key} (${res.status})`));
|
|
4688
4735
|
process.exit(1);
|
|
4689
4736
|
}
|
|
4690
|
-
|
|
4737
|
+
if (isJsonMode()) {
|
|
4738
|
+
const result = await res.json();
|
|
4739
|
+
outputSuccess(result);
|
|
4740
|
+
return;
|
|
4741
|
+
}
|
|
4742
|
+
console.log(source_default.green(`Removed ${key}`));
|
|
4691
4743
|
} catch (err) {
|
|
4692
4744
|
console.error(source_default.red("Failed:"), err.message);
|
|
4693
4745
|
process.exit(1);
|
|
@@ -5081,8 +5133,19 @@ async function init(dir, opts) {
|
|
|
5081
5133
|
const template = selectTemplate(type, kind, slug);
|
|
5082
5134
|
writeFileSync2(entryFile, template);
|
|
5083
5135
|
}
|
|
5136
|
+
if (type === "edge" && kind === "comms") {
|
|
5137
|
+
const typesFile = join2(srcDir, "types.ts");
|
|
5138
|
+
if (!existsSync2(typesFile)) {
|
|
5139
|
+
writeFileSync2(typesFile, COMMS_TYPES_TEMPLATE());
|
|
5140
|
+
}
|
|
5141
|
+
const verifyFile = join2(srcDir, "verify.ts");
|
|
5142
|
+
if (!existsSync2(verifyFile)) {
|
|
5143
|
+
writeFileSync2(verifyFile, COMMS_VERIFY_TEMPLATE());
|
|
5144
|
+
}
|
|
5145
|
+
}
|
|
5084
5146
|
const pkgFile = join2(projectDir, "package.json");
|
|
5085
5147
|
if (!existsSync2(pkgFile)) {
|
|
5148
|
+
const deps = {};
|
|
5086
5149
|
const devDeps = {
|
|
5087
5150
|
"@cloudflare/workers-types": "^4.20240909.0",
|
|
5088
5151
|
typescript: "^5.3.3"
|
|
@@ -5090,11 +5153,15 @@ async function init(dir, opts) {
|
|
|
5090
5153
|
if (type === "edge" && kind === "adapter") {
|
|
5091
5154
|
devDeps["@bagdock/adapter-worker-template"] = "workspace:*";
|
|
5092
5155
|
}
|
|
5156
|
+
if (type === "edge" && kind === "comms") {
|
|
5157
|
+
deps["@bagdock/worker-sdk"] = "^0.1.0";
|
|
5158
|
+
}
|
|
5093
5159
|
writeFileSync2(pkgFile, JSON.stringify({
|
|
5094
5160
|
name: slug,
|
|
5095
5161
|
version: "0.1.0",
|
|
5096
5162
|
private: true,
|
|
5097
5163
|
main: "src/index.ts",
|
|
5164
|
+
...Object.keys(deps).length ? { dependencies: deps } : {},
|
|
5098
5165
|
devDependencies: devDeps
|
|
5099
5166
|
}, null, 2));
|
|
5100
5167
|
}
|
|
@@ -5103,12 +5170,17 @@ async function init(dir, opts) {
|
|
|
5103
5170
|
Initialised Bagdock ${label} project!
|
|
5104
5171
|
`));
|
|
5105
5172
|
console.log("Created:");
|
|
5106
|
-
console.log(` ${source_default.cyan("bagdock.json")}
|
|
5107
|
-
console.log(` ${source_default.cyan("src/index.ts")}
|
|
5173
|
+
console.log(` ${source_default.cyan("bagdock.json")} — project config`);
|
|
5174
|
+
console.log(` ${source_default.cyan("src/index.ts")} — worker entry point`);
|
|
5175
|
+
if (type === "edge" && kind === "comms") {
|
|
5176
|
+
console.log(` ${source_default.cyan("src/types.ts")} — environment bindings`);
|
|
5177
|
+
console.log(` ${source_default.cyan("src/verify.ts")} — webhook verification (customise for your vendor)`);
|
|
5178
|
+
}
|
|
5108
5179
|
console.log();
|
|
5109
5180
|
console.log("Next steps:");
|
|
5110
|
-
console.log(` 1. ${source_default.cyan("
|
|
5111
|
-
console.log(` 2. ${source_default.cyan("bagdock
|
|
5181
|
+
console.log(` 1. ${source_default.cyan("bun install")} — install dependencies`);
|
|
5182
|
+
console.log(` 2. ${source_default.cyan("bagdock dev")} — start local dev server`);
|
|
5183
|
+
console.log(` 3. ${source_default.cyan("bagdock deploy")} — deploy to Bagdock platform`);
|
|
5112
5184
|
}
|
|
5113
5185
|
function resolveKind(type, kindOpt) {
|
|
5114
5186
|
if (kindOpt)
|
|
@@ -5154,33 +5226,75 @@ export default createAdapterWorker(new ${toPascalCase(slug)}Adapter())
|
|
|
5154
5226
|
`;
|
|
5155
5227
|
}
|
|
5156
5228
|
function COMMS_TEMPLATE(slug) {
|
|
5157
|
-
return
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
*/
|
|
5229
|
+
return `import { createCommsWorker } from '@bagdock/worker-sdk'
|
|
5230
|
+
import type { HandlerContext } from '@bagdock/worker-sdk'
|
|
5231
|
+
import type { Env } from './types'
|
|
5232
|
+
import { vendorWebhookVerify } from './verify'
|
|
5162
5233
|
|
|
5163
|
-
|
|
5164
|
-
|
|
5234
|
+
async function handleSmsSend(ctx: HandlerContext<Env>): Promise<Response> {
|
|
5235
|
+
const { to, body } = await ctx.request.json() as { to: string; body: string }
|
|
5236
|
+
// TODO: call your vendor's SMS API using ctx.env for secrets
|
|
5237
|
+
return Response.json({ id: crypto.randomUUID(), status: 'queued', provider: '${slug}', from: '', to })
|
|
5165
5238
|
}
|
|
5166
5239
|
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5240
|
+
async function handleSmsWebhook(ctx: HandlerContext<Env>): Promise<Response> {
|
|
5241
|
+
const payload = await ctx.request.json()
|
|
5242
|
+
ctx.logger.info('webhook.sms', { payload })
|
|
5243
|
+
return Response.json({ received: true })
|
|
5244
|
+
}
|
|
5171
5245
|
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
}
|
|
5246
|
+
export default createCommsWorker<Env, readonly ['sms']>({
|
|
5247
|
+
version: '0.1.0',
|
|
5248
|
+
capabilities: ['sms'],
|
|
5176
5249
|
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
}
|
|
5250
|
+
async onInstall(ctx) {
|
|
5251
|
+
// TODO: provision vendor resources
|
|
5252
|
+
ctx.logger.info('lifecycle.install', { operatorId: ctx.operatorId })
|
|
5253
|
+
return { installation_state: { provisioned: true } }
|
|
5254
|
+
},
|
|
5255
|
+
|
|
5256
|
+
async onUninstall(ctx) {
|
|
5257
|
+
// TODO: clean up vendor resources
|
|
5258
|
+
ctx.logger.info('lifecycle.uninstall', { operatorId: ctx.operatorId })
|
|
5259
|
+
},
|
|
5180
5260
|
|
|
5181
|
-
|
|
5261
|
+
routes: {
|
|
5262
|
+
'sms/send': handleSmsSend,
|
|
5263
|
+
'webhooks/sms': { handler: handleSmsWebhook, verify: vendorWebhookVerify },
|
|
5182
5264
|
},
|
|
5265
|
+
})
|
|
5266
|
+
`;
|
|
5183
5267
|
}
|
|
5268
|
+
function COMMS_TYPES_TEMPLATE() {
|
|
5269
|
+
return `import type { BaseEnv } from '@bagdock/worker-sdk'
|
|
5270
|
+
|
|
5271
|
+
export interface Env extends BaseEnv {
|
|
5272
|
+
ADAPTER_NAME: string
|
|
5273
|
+
PROVIDER_SLUG: string
|
|
5274
|
+
API_KEY: string
|
|
5275
|
+
WEBHOOK_SECRET: string
|
|
5276
|
+
OPERATOR_CONFIG?: KVNamespace
|
|
5277
|
+
}
|
|
5278
|
+
`;
|
|
5279
|
+
}
|
|
5280
|
+
function COMMS_VERIFY_TEMPLATE() {
|
|
5281
|
+
return `import { hmacSha256Verify } from '@bagdock/worker-sdk'
|
|
5282
|
+
import type { VerifyFunction } from '@bagdock/worker-sdk'
|
|
5283
|
+
import type { Env } from './types'
|
|
5284
|
+
|
|
5285
|
+
/**
|
|
5286
|
+
* Adapter-local webhook verification.
|
|
5287
|
+
*
|
|
5288
|
+
* Replace with your vendor's signing method. See the @bagdock/worker-sdk
|
|
5289
|
+
* README for examples using vendor SDKs or the ed25519Verify primitive.
|
|
5290
|
+
*/
|
|
5291
|
+
export const vendorWebhookVerify: VerifyFunction<Env> = (request, env, rawBody) =>
|
|
5292
|
+
hmacSha256Verify({
|
|
5293
|
+
signature: request.headers.get('x-webhook-signature'),
|
|
5294
|
+
secret: env.WEBHOOK_SECRET,
|
|
5295
|
+
signingString: rawBody,
|
|
5296
|
+
timestamp: request.headers.get('x-webhook-timestamp'),
|
|
5297
|
+
})
|
|
5184
5298
|
`;
|
|
5185
5299
|
}
|
|
5186
5300
|
function WEBHOOK_TEMPLATE(slug) {
|
|
@@ -5358,17 +5472,17 @@ program2.command("link").description("Link current directory to a Bagdock app or
|
|
|
5358
5472
|
await link2(opts);
|
|
5359
5473
|
});
|
|
5360
5474
|
var envCmd = program2.command("env").description("Manage app environment variables");
|
|
5361
|
-
envCmd.command("list").description("List environment variables for this app").action(async () => {
|
|
5475
|
+
envCmd.command("list").description("List environment variables for this app").option("--reconcile", "Force sync with Cloudflare before listing").action(async (opts) => {
|
|
5362
5476
|
const { envList: envList2 } = await Promise.resolve().then(() => (init_env_cmd(), exports_env_cmd));
|
|
5363
|
-
await envList2();
|
|
5477
|
+
await envList2({ reconcile: opts.reconcile });
|
|
5364
5478
|
});
|
|
5365
|
-
envCmd.command("set <key> <value>").description("Set an environment variable").action(async (key, value) => {
|
|
5479
|
+
envCmd.command("set <key> <value>").description("Set an environment variable").option("--target <target>", "Target namespace: staging, production, or both (default: both)").action(async (key, value, opts) => {
|
|
5366
5480
|
const { envSet: envSet2 } = await Promise.resolve().then(() => (init_env_cmd(), exports_env_cmd));
|
|
5367
|
-
await envSet2(key, value);
|
|
5481
|
+
await envSet2(key, value, { target: opts.target });
|
|
5368
5482
|
});
|
|
5369
|
-
envCmd.command("remove <key>").description("Remove an environment variable").action(async (key) => {
|
|
5483
|
+
envCmd.command("remove <key>").description("Remove an environment variable").option("--target <target>", "Target namespace: staging, production, or both (default: both)").action(async (key, opts) => {
|
|
5370
5484
|
const { envRemove: envRemove2 } = await Promise.resolve().then(() => (init_env_cmd(), exports_env_cmd));
|
|
5371
|
-
await envRemove2(key);
|
|
5485
|
+
await envRemove2(key, { target: opts.target });
|
|
5372
5486
|
});
|
|
5373
5487
|
envCmd.command("pull [file]").description("Pull remote env var keys to a local .env file").action(async (file) => {
|
|
5374
5488
|
const { envPull: envPull2 } = await Promise.resolve().then(() => (init_env_cmd(), exports_env_cmd));
|