@bagdock/cli 0.1.4 → 0.2.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/bagdock.js +571 -141
  2. package/package.json +1 -1
package/dist/bagdock.js CHANGED
@@ -3158,6 +3158,200 @@ var init_open = __esm(() => {
3158
3158
  open_default = open;
3159
3159
  });
3160
3160
 
3161
+ // src/auth.ts
3162
+ function setApiKeyOverride(key) {
3163
+ apiKeyOverride = key;
3164
+ }
3165
+ function getAuthToken() {
3166
+ if (apiKeyOverride)
3167
+ return apiKeyOverride;
3168
+ if (process.env.BAGDOCK_API_KEY)
3169
+ return process.env.BAGDOCK_API_KEY;
3170
+ if (process.env.BAGDOCK_TOKEN)
3171
+ return process.env.BAGDOCK_TOKEN;
3172
+ const creds = loadCredentials();
3173
+ return creds?.accessToken ?? null;
3174
+ }
3175
+ async function login() {
3176
+ const existing = loadCredentials();
3177
+ if (existing?.accessToken && existing.expiresAt && existing.expiresAt > Date.now()) {
3178
+ console.log(source_default.green("Already logged in as"), source_default.bold(existing.email ?? "unknown"));
3179
+ console.log("Run", source_default.cyan("bagdock logout"), "to sign out first.");
3180
+ return;
3181
+ }
3182
+ console.log(source_default.cyan(`
3183
+ Requesting device authorization...
3184
+ `));
3185
+ const deviceRes = await fetch(`${API_BASE}/oauth2/device/authorize`, {
3186
+ method: "POST",
3187
+ headers: { "Content-Type": "application/json" },
3188
+ body: JSON.stringify({ client_id: CLIENT_ID, scope: "developer:read developer:write" })
3189
+ });
3190
+ if (!deviceRes.ok) {
3191
+ const err = await deviceRes.text();
3192
+ console.log(source_default.red("Failed to start login:"), err);
3193
+ process.exit(1);
3194
+ }
3195
+ const device = await deviceRes.json();
3196
+ const pollInterval = (device.interval ?? 5) * 1000;
3197
+ console.log(` Visit ${source_default.bold(device.verification_uri)} and enter code:
3198
+ `);
3199
+ console.log(` ${source_default.bold.cyan(device.user_code)}
3200
+ `);
3201
+ const open2 = (await Promise.resolve().then(() => (init_open(), exports_open))).default;
3202
+ await open2(device.verification_uri_complete).catch(() => {});
3203
+ console.log(source_default.dim(" Waiting for authorization..."));
3204
+ const startedAt = Date.now();
3205
+ while (Date.now() - startedAt < MAX_POLL_DURATION_MS) {
3206
+ await sleep(pollInterval);
3207
+ const tokenRes = await fetch(`${API_BASE}/oauth2/token`, {
3208
+ method: "POST",
3209
+ headers: { "Content-Type": "application/json" },
3210
+ body: JSON.stringify({
3211
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
3212
+ client_id: CLIENT_ID,
3213
+ device_code: device.device_code
3214
+ })
3215
+ });
3216
+ if (tokenRes.ok) {
3217
+ const tokens = await tokenRes.json();
3218
+ saveCredentials({
3219
+ accessToken: tokens.access_token,
3220
+ refreshToken: tokens.refresh_token,
3221
+ expiresAt: tokens.expires_in ? Date.now() + tokens.expires_in * 1000 : undefined,
3222
+ email: tokens.email,
3223
+ operatorId: tokens.operator_id
3224
+ });
3225
+ console.log(source_default.green(`
3226
+ Logged in successfully!`));
3227
+ if (tokens.email)
3228
+ console.log(" Email:", source_default.bold(tokens.email));
3229
+ if (tokens.operator_id)
3230
+ console.log(" Operator:", source_default.bold(tokens.operator_id));
3231
+ return;
3232
+ }
3233
+ const error = await tokenRes.json().catch(() => ({ error: "unknown" }));
3234
+ if (error.error === "authorization_pending") {
3235
+ continue;
3236
+ }
3237
+ if (error.error === "slow_down") {
3238
+ await sleep(pollInterval);
3239
+ continue;
3240
+ }
3241
+ if (error.error === "expired_token") {
3242
+ console.log(source_default.red(`
3243
+ Device code expired. Please try again.`));
3244
+ process.exit(1);
3245
+ }
3246
+ if (error.error === "access_denied") {
3247
+ console.log(source_default.red(`
3248
+ Authorization denied.`));
3249
+ process.exit(1);
3250
+ }
3251
+ console.log(source_default.red(`
3252
+ Login failed:`), error.error_description ?? error.error);
3253
+ process.exit(1);
3254
+ }
3255
+ console.log(source_default.red(`
3256
+ Login timed out. Please try again.`));
3257
+ process.exit(1);
3258
+ }
3259
+ async function logout() {
3260
+ clearCredentials();
3261
+ console.log(source_default.green("Logged out."));
3262
+ }
3263
+ async function whoami() {
3264
+ const token = getAuthToken();
3265
+ if (!token) {
3266
+ console.log(source_default.yellow("Not logged in."), "Run", source_default.cyan("bagdock login"));
3267
+ process.exit(1);
3268
+ }
3269
+ try {
3270
+ const res = await fetch(`${API_BASE}/v1/auth/me`, {
3271
+ headers: { Authorization: `Bearer ${token}` }
3272
+ });
3273
+ if (!res.ok) {
3274
+ console.log(source_default.red("Session expired or invalid."), "Run", source_default.cyan("bagdock login"));
3275
+ process.exit(1);
3276
+ }
3277
+ const user = await res.json();
3278
+ console.log(source_default.green("Logged in as"), source_default.bold(user.email));
3279
+ if (user.operator_id)
3280
+ console.log("Operator:", source_default.bold(user.operator_id));
3281
+ if (user.name)
3282
+ console.log("Name:", user.name);
3283
+ } catch (err) {
3284
+ console.log(source_default.red("Failed to reach API:"), err.message);
3285
+ process.exit(1);
3286
+ }
3287
+ }
3288
+ function sleep(ms) {
3289
+ return new Promise((resolve) => setTimeout(resolve, ms));
3290
+ }
3291
+ var apiKeyOverride, CLIENT_ID = "bagdock-cli", MAX_POLL_DURATION_MS = 300000;
3292
+ var init_auth = __esm(() => {
3293
+ init_config();
3294
+ init_source();
3295
+ });
3296
+
3297
+ // src/output.ts
3298
+ function setOutputMode(opts) {
3299
+ if (opts.quiet) {
3300
+ quiet = true;
3301
+ forceJson = true;
3302
+ }
3303
+ if (opts.json) {
3304
+ forceJson = true;
3305
+ }
3306
+ }
3307
+ function isJsonMode() {
3308
+ return forceJson || !process.stdout.isTTY;
3309
+ }
3310
+ function isQuiet() {
3311
+ return quiet;
3312
+ }
3313
+ function outputSuccess(data) {
3314
+ if (isJsonMode()) {
3315
+ process.stdout.write(JSON.stringify(data, null, 2) + `
3316
+ `);
3317
+ }
3318
+ }
3319
+ function outputError(code, message, details) {
3320
+ if (isJsonMode()) {
3321
+ const err = { error: { code, message } };
3322
+ if (details)
3323
+ err.error.details = details;
3324
+ process.stderr.write(JSON.stringify(err) + `
3325
+ `);
3326
+ process.exit(1);
3327
+ } else {
3328
+ console.error(source_default.red(`Error [${code}]:`), message);
3329
+ if (details)
3330
+ console.error(details);
3331
+ process.exit(1);
3332
+ }
3333
+ }
3334
+ function outputList(objectType, data, hasMore) {
3335
+ if (isJsonMode()) {
3336
+ process.stdout.write(JSON.stringify({ object: "list", data, has_more: hasMore }, null, 2) + `
3337
+ `);
3338
+ }
3339
+ }
3340
+ function status(message) {
3341
+ if (!isQuiet() && !isJsonMode()) {
3342
+ console.log(source_default.dim(message));
3343
+ }
3344
+ }
3345
+ function success(message) {
3346
+ if (!isQuiet() && !isJsonMode()) {
3347
+ console.log(source_default.green(message));
3348
+ }
3349
+ }
3350
+ var forceJson = false, quiet = false;
3351
+ var init_output = __esm(() => {
3352
+ init_source();
3353
+ });
3354
+
3161
3355
  // src/dev.ts
3162
3356
  var exports_dev = {};
3163
3357
  __export(exports_dev, {
@@ -3251,9 +3445,9 @@ async function deploy(opts) {
3251
3445
  console.error(source_default.red("No bagdock.json found. Run"), source_default.cyan("bagdock init"), source_default.red("first."));
3252
3446
  process.exit(1);
3253
3447
  }
3254
- const creds = loadCredentials();
3255
- if (!creds?.accessToken) {
3256
- console.error(source_default.red("Not authenticated. Run"), source_default.cyan("bagdock login"), source_default.red("first."));
3448
+ const token = getAuthToken();
3449
+ if (!token) {
3450
+ console.error(source_default.red("Not authenticated. Run"), source_default.cyan("bagdock login"), source_default.red("or set BAGDOCK_API_KEY."));
3257
3451
  process.exit(1);
3258
3452
  }
3259
3453
  let environment = opts.env ?? "staging";
@@ -3318,7 +3512,7 @@ Deploying ${config.slug}@${config.version} → ${envLabel}
3318
3512
  const res = await fetch(`${API_BASE}/api/v1/developer/apps/${config.slug}/deploy`, {
3319
3513
  method: "POST",
3320
3514
  headers: {
3321
- Authorization: `Bearer ${creds.accessToken}`
3515
+ Authorization: `Bearer ${token}`
3322
3516
  },
3323
3517
  body: formData
3324
3518
  });
@@ -3374,6 +3568,7 @@ function confirm(prompt) {
3374
3568
  var init_deploy = __esm(() => {
3375
3569
  init_source();
3376
3570
  init_config();
3571
+ init_auth();
3377
3572
  });
3378
3573
 
3379
3574
  // src/submit.ts
@@ -3387,9 +3582,9 @@ async function submit() {
3387
3582
  console.error(source_default.red("No bagdock.json found. Run"), source_default.cyan("bagdock init"), source_default.red("first."));
3388
3583
  process.exit(1);
3389
3584
  }
3390
- const creds = loadCredentials();
3391
- if (!creds?.accessToken) {
3392
- console.error(source_default.red("Not authenticated. Run"), source_default.cyan("bagdock login"), source_default.red("first."));
3585
+ const token = getAuthToken();
3586
+ if (!token) {
3587
+ console.error(source_default.red("Not authenticated. Run"), source_default.cyan("bagdock login"), source_default.red("or set BAGDOCK_API_KEY."));
3393
3588
  process.exit(1);
3394
3589
  }
3395
3590
  console.log(source_default.cyan(`
@@ -3399,7 +3594,7 @@ Submitting ${source_default.bold(config.slug)} for marketplace review...
3399
3594
  const res = await fetch(`${API_BASE}/api/v1/developer/apps/${config.slug}/submit`, {
3400
3595
  method: "POST",
3401
3596
  headers: {
3402
- Authorization: `Bearer ${creds.accessToken}`,
3597
+ Authorization: `Bearer ${token}`,
3403
3598
  "Content-Type": "application/json"
3404
3599
  }
3405
3600
  });
@@ -3427,6 +3622,7 @@ Submitting ${source_default.bold(config.slug)} for marketplace review...
3427
3622
  var init_submit = __esm(() => {
3428
3623
  init_source();
3429
3624
  init_config();
3625
+ init_auth();
3430
3626
  });
3431
3627
 
3432
3628
  // src/env-cmd.ts
@@ -3437,12 +3633,12 @@ __export(exports_env_cmd, {
3437
3633
  envList: () => envList
3438
3634
  });
3439
3635
  function requireAuth() {
3440
- const creds = loadCredentials();
3441
- if (!creds?.accessToken) {
3442
- console.error(source_default.red("Not authenticated. Run"), source_default.cyan("bagdock login"));
3636
+ const token = getAuthToken();
3637
+ if (!token) {
3638
+ console.error(source_default.red("Not authenticated. Run"), source_default.cyan("bagdock login"), source_default.red("or set BAGDOCK_API_KEY."));
3443
3639
  process.exit(1);
3444
3640
  }
3445
- return creds;
3641
+ return token;
3446
3642
  }
3447
3643
  function requireConfig() {
3448
3644
  const config = loadBagdockJson(process.cwd());
@@ -3453,11 +3649,11 @@ function requireConfig() {
3453
3649
  return config;
3454
3650
  }
3455
3651
  async function envList() {
3456
- const creds = requireAuth();
3652
+ const token = requireAuth();
3457
3653
  const config = requireConfig();
3458
3654
  try {
3459
3655
  const res = await fetch(`${API_BASE}/v1/developer/apps/${config.slug}/env`, {
3460
- headers: { Authorization: `Bearer ${creds.accessToken}` }
3656
+ headers: { Authorization: `Bearer ${token}` }
3461
3657
  });
3462
3658
  if (!res.ok) {
3463
3659
  console.error(source_default.red(`Failed to list env vars (${res.status})`));
@@ -3482,14 +3678,14 @@ Environment variables for ${config.slug}:
3482
3678
  }
3483
3679
  }
3484
3680
  async function envSet(key, value) {
3485
- const creds = requireAuth();
3681
+ const token = requireAuth();
3486
3682
  const config = requireConfig();
3487
3683
  try {
3488
3684
  const res = await fetch(`${API_BASE}/v1/developer/apps/${config.slug}/env`, {
3489
3685
  method: "PUT",
3490
3686
  headers: {
3491
3687
  "Content-Type": "application/json",
3492
- Authorization: `Bearer ${creds.accessToken}`
3688
+ Authorization: `Bearer ${token}`
3493
3689
  },
3494
3690
  body: JSON.stringify({ key, value })
3495
3691
  });
@@ -3505,12 +3701,12 @@ async function envSet(key, value) {
3505
3701
  }
3506
3702
  }
3507
3703
  async function envRemove(key) {
3508
- const creds = requireAuth();
3704
+ const token = requireAuth();
3509
3705
  const config = requireConfig();
3510
3706
  try {
3511
3707
  const res = await fetch(`${API_BASE}/v1/developer/apps/${config.slug}/env/${key}`, {
3512
3708
  method: "DELETE",
3513
- headers: { Authorization: `Bearer ${creds.accessToken}` }
3709
+ headers: { Authorization: `Bearer ${token}` }
3514
3710
  });
3515
3711
  if (!res.ok) {
3516
3712
  console.error(source_default.red(`Failed to remove ${key} (${res.status})`));
@@ -3525,146 +3721,339 @@ async function envRemove(key) {
3525
3721
  var init_env_cmd = __esm(() => {
3526
3722
  init_source();
3527
3723
  init_config();
3724
+ init_auth();
3528
3725
  });
3529
3726
 
3530
- // node_modules/commander/esm.mjs
3531
- var import__ = __toESM(require_commander(), 1);
3532
- var {
3533
- program,
3534
- createCommand,
3535
- createArgument,
3536
- createOption,
3537
- CommanderError,
3538
- InvalidArgumentError,
3539
- InvalidOptionArgumentError,
3540
- Command,
3541
- Argument,
3542
- Option,
3543
- Help
3544
- } = import__.default;
3545
-
3546
- // src/auth.ts
3547
- init_config();
3548
- init_source();
3549
- var CLIENT_ID = "bagdock-cli";
3550
- var MAX_POLL_DURATION_MS = 300000;
3551
- async function login() {
3552
- const existing = loadCredentials();
3553
- if (existing?.accessToken && existing.expiresAt && existing.expiresAt > Date.now()) {
3554
- console.log(source_default.green("Already logged in as"), source_default.bold(existing.email ?? "unknown"));
3555
- console.log("Run", source_default.cyan("bagdock logout"), "to sign out first.");
3556
- return;
3727
+ // src/keys.ts
3728
+ var exports_keys = {};
3729
+ __export(exports_keys, {
3730
+ keysList: () => keysList,
3731
+ keysDelete: () => keysDelete,
3732
+ keysCreate: () => keysCreate
3733
+ });
3734
+ async function apiRequest(method, path2, body) {
3735
+ const token = getAuthToken();
3736
+ if (!token) {
3737
+ outputError("UNAUTHENTICATED", "Not logged in. Run `bagdock login` or set BAGDOCK_API_KEY.");
3557
3738
  }
3558
- console.log(source_default.cyan(`
3559
- Requesting device authorization...
3739
+ const headers = { Authorization: `Bearer ${token}` };
3740
+ const init2 = { method, headers };
3741
+ if (body) {
3742
+ headers["Content-Type"] = "application/json";
3743
+ init2.body = JSON.stringify(body);
3744
+ }
3745
+ const res = await fetch(`${API_BASE}${path2}`, init2);
3746
+ return res;
3747
+ }
3748
+ async function keysCreate(opts) {
3749
+ status("Creating API key...");
3750
+ const res = await apiRequest("POST", "/api/v1/operator/api-keys", {
3751
+ name: opts.name,
3752
+ key_type: opts.type || "secret",
3753
+ key_category: opts.category || "standard",
3754
+ environment: opts.environment || "live",
3755
+ scopes: opts.scopes || []
3756
+ });
3757
+ if (!res.ok) {
3758
+ const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
3759
+ outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
3760
+ }
3761
+ const data = await res.json();
3762
+ if (isJsonMode()) {
3763
+ outputSuccess(data);
3764
+ } else {
3765
+ console.log(source_default.green(`
3766
+ API key created successfully!
3560
3767
  `));
3561
- const deviceRes = await fetch(`${API_BASE}/oauth2/device/authorize`, {
3562
- method: "POST",
3563
- headers: { "Content-Type": "application/json" },
3564
- body: JSON.stringify({ client_id: CLIENT_ID, scope: "developer:read developer:write" })
3768
+ console.log(` ${source_default.bold("Name:")} ${data.name}`);
3769
+ console.log(` ${source_default.bold("Key:")} ${source_default.yellow(data.key)}`);
3770
+ console.log(` ${source_default.bold("ID:")} ${data.id}`);
3771
+ console.log(` ${source_default.bold("Environment:")} ${data.environment}`);
3772
+ console.log(` ${source_default.bold("Type:")} ${data.key_type}`);
3773
+ console.log(` ${source_default.bold("Category:")} ${data.key_category}`);
3774
+ if (data.scopes?.length) {
3775
+ console.log(` ${source_default.bold("Scopes:")} ${data.scopes.join(", ")}`);
3776
+ }
3777
+ console.log();
3778
+ console.log(source_default.yellow(" Save this key — it will not be shown again."));
3779
+ console.log();
3780
+ }
3781
+ }
3782
+ async function keysList(opts) {
3783
+ status("Fetching API keys...");
3784
+ let path2 = "/api/v1/operator/api-keys";
3785
+ if (opts.environment)
3786
+ path2 += `?environment=${opts.environment}`;
3787
+ const res = await apiRequest("GET", path2);
3788
+ if (!res.ok) {
3789
+ const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
3790
+ outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
3791
+ }
3792
+ const result = await res.json();
3793
+ if (isJsonMode()) {
3794
+ outputList("api_key", result.data, result.has_more);
3795
+ } else {
3796
+ if (!result.data.length) {
3797
+ console.log(source_default.yellow(`
3798
+ No API keys found.
3799
+ `));
3800
+ return;
3801
+ }
3802
+ console.log();
3803
+ for (const key of result.data) {
3804
+ const usedAt = key.last_used_at ? new Date(key.last_used_at).toLocaleDateString() : "never";
3805
+ console.log(` ${source_default.bold(key.name)}`);
3806
+ console.log(` ${source_default.dim("Prefix:")} ${key.key_prefix}... ${source_default.dim("Env:")} ${key.environment} ${source_default.dim("Last used:")} ${usedAt}`);
3807
+ console.log(` ${source_default.dim("ID:")} ${key.id} ${source_default.dim("Category:")} ${key.key_category}`);
3808
+ console.log();
3809
+ }
3810
+ }
3811
+ }
3812
+ async function keysDelete(id, opts) {
3813
+ if (!opts.yes && !process.stdout.isTTY) {
3814
+ outputError("CONFIRMATION_REQUIRED", "Pass --yes to confirm deletion in non-interactive mode.");
3815
+ }
3816
+ status(`Revoking API key ${id}...`);
3817
+ const res = await apiRequest("DELETE", `/api/v1/operator/api-keys/${id}`, {
3818
+ reason: opts.reason
3565
3819
  });
3566
- if (!deviceRes.ok) {
3567
- const err = await deviceRes.text();
3568
- console.log(source_default.red("Failed to start login:"), err);
3569
- process.exit(1);
3820
+ if (!res.ok) {
3821
+ const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
3822
+ outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
3570
3823
  }
3571
- const device = await deviceRes.json();
3572
- const pollInterval = (device.interval ?? 5) * 1000;
3573
- console.log(` Visit ${source_default.bold(device.verification_uri)} and enter code:
3824
+ if (isJsonMode()) {
3825
+ outputSuccess({ id, status: "revoked" });
3826
+ } else {
3827
+ success(`
3828
+ API key ${id} revoked.
3574
3829
  `);
3575
- console.log(` ${source_default.bold.cyan(device.user_code)}
3830
+ }
3831
+ }
3832
+ var init_keys = __esm(() => {
3833
+ init_source();
3834
+ init_config();
3835
+ init_auth();
3836
+ init_output();
3837
+ });
3838
+
3839
+ // src/apps.ts
3840
+ var exports_apps = {};
3841
+ __export(exports_apps, {
3842
+ appsList: () => appsList,
3843
+ appsGet: () => appsGet
3844
+ });
3845
+ async function apiRequest2(method, path2) {
3846
+ const token = getAuthToken();
3847
+ if (!token) {
3848
+ outputError("UNAUTHENTICATED", "Not logged in. Run `bagdock login` or set BAGDOCK_API_KEY.");
3849
+ }
3850
+ return fetch(`${API_BASE}${path2}`, {
3851
+ method,
3852
+ headers: { Authorization: `Bearer ${token}` }
3853
+ });
3854
+ }
3855
+ async function appsList() {
3856
+ status("Fetching apps...");
3857
+ const res = await apiRequest2("GET", "/api/v1/developer/apps");
3858
+ if (!res.ok) {
3859
+ const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
3860
+ outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
3861
+ }
3862
+ const result = await res.json();
3863
+ if (isJsonMode()) {
3864
+ outputList("app", result.data, false);
3865
+ } else {
3866
+ if (!result.data.length) {
3867
+ console.log(source_default.yellow(`
3868
+ No apps found. Create one with`), source_default.cyan("bagdock init"), `
3576
3869
  `);
3577
- const open2 = (await Promise.resolve().then(() => (init_open(), exports_open))).default;
3578
- await open2(device.verification_uri_complete).catch(() => {});
3579
- console.log(source_default.dim(" Waiting for authorization..."));
3580
- const startedAt = Date.now();
3581
- while (Date.now() - startedAt < MAX_POLL_DURATION_MS) {
3582
- await sleep(pollInterval);
3583
- const tokenRes = await fetch(`${API_BASE}/oauth2/token`, {
3584
- method: "POST",
3585
- headers: { "Content-Type": "application/json" },
3586
- body: JSON.stringify({
3587
- grant_type: "urn:ietf:params:oauth:grant-type:device_code",
3588
- client_id: CLIENT_ID,
3589
- device_code: device.device_code
3590
- })
3591
- });
3592
- if (tokenRes.ok) {
3593
- const tokens = await tokenRes.json();
3594
- saveCredentials({
3595
- accessToken: tokens.access_token,
3596
- refreshToken: tokens.refresh_token,
3597
- expiresAt: tokens.expires_in ? Date.now() + tokens.expires_in * 1000 : undefined,
3598
- email: tokens.email,
3599
- operatorId: tokens.operator_id
3600
- });
3601
- console.log(source_default.green(`
3602
- Logged in successfully!`));
3603
- if (tokens.email)
3604
- console.log(" Email:", source_default.bold(tokens.email));
3605
- if (tokens.operator_id)
3606
- console.log(" Operator:", source_default.bold(tokens.operator_id));
3607
3870
  return;
3608
3871
  }
3609
- const error = await tokenRes.json().catch(() => ({ error: "unknown" }));
3610
- if (error.error === "authorization_pending") {
3611
- continue;
3612
- }
3613
- if (error.error === "slow_down") {
3614
- await sleep(pollInterval);
3615
- continue;
3616
- }
3617
- if (error.error === "expired_token") {
3618
- console.log(source_default.red(`
3619
- Device code expired. Please try again.`));
3620
- process.exit(1);
3621
- }
3622
- if (error.error === "access_denied") {
3623
- console.log(source_default.red(`
3624
- Authorization denied.`));
3625
- process.exit(1);
3872
+ console.log();
3873
+ for (const app of result.data) {
3874
+ const status2 = app.is_active ? source_default.green("active") : source_default.dim("inactive");
3875
+ const review = app.review_status ? source_default.dim(`[${app.review_status}]`) : "";
3876
+ console.log(` ${source_default.bold(app.name)} ${source_default.dim(`(${app.slug})`)} ${status2} ${review}`);
3877
+ console.log(` ${source_default.dim("Type:")} ${app.type}/${app.category} ${source_default.dim("Version:")} ${app.version}`);
3878
+ if (app.worker_url) {
3879
+ console.log(` ${source_default.dim("URL:")} ${app.worker_url}`);
3880
+ }
3881
+ console.log();
3626
3882
  }
3627
- console.log(source_default.red(`
3628
- Login failed:`), error.error_description ?? error.error);
3629
- process.exit(1);
3630
3883
  }
3631
- console.log(source_default.red(`
3632
- Login timed out. Please try again.`));
3633
- process.exit(1);
3634
3884
  }
3635
- async function logout() {
3636
- clearCredentials();
3637
- console.log(source_default.green("Logged out."));
3885
+ async function appsGet(slug) {
3886
+ status(`Fetching app ${slug}...`);
3887
+ const res = await apiRequest2("GET", `/api/v1/developer/apps/${slug}`);
3888
+ if (!res.ok) {
3889
+ if (res.status === 404) {
3890
+ outputError("NOT_FOUND", `App "${slug}" not found`);
3891
+ }
3892
+ const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
3893
+ outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
3894
+ }
3895
+ const result = await res.json();
3896
+ const app = result.data;
3897
+ if (isJsonMode()) {
3898
+ outputSuccess(app);
3899
+ } else {
3900
+ console.log();
3901
+ console.log(` ${source_default.bold(app.name)} ${source_default.dim(`(${app.slug})`)}`);
3902
+ console.log(` ${source_default.dim("ID:")} ${app.id}`);
3903
+ console.log(` ${source_default.dim("Type:")} ${app.type}`);
3904
+ console.log(` ${source_default.dim("Category:")} ${app.category}`);
3905
+ console.log(` ${source_default.dim("Kind:")} ${app.kind ?? "-"}`);
3906
+ console.log(` ${source_default.dim("Version:")} ${app.version}`);
3907
+ console.log(` ${source_default.dim("Visibility:")} ${app.visibility}`);
3908
+ console.log(` ${source_default.dim("Maintainer:")} ${app.maintainer}`);
3909
+ console.log(` ${source_default.dim("Review:")} ${app.review_status ?? "draft"}`);
3910
+ console.log(` ${source_default.dim("Active:")} ${app.is_active ? "yes" : "no"}`);
3911
+ if (app.worker_url) {
3912
+ console.log(` ${source_default.dim("Worker URL:")} ${app.worker_url}`);
3913
+ }
3914
+ console.log(` ${source_default.dim("Created:")} ${app.created_at}`);
3915
+ console.log(` ${source_default.dim("Updated:")} ${app.updated_at}`);
3916
+ console.log();
3917
+ }
3638
3918
  }
3639
- async function whoami() {
3640
- const envToken = process.env.BAGDOCK_TOKEN;
3641
- const creds = envToken ? { accessToken: envToken, email: "(M2M token)" } : loadCredentials();
3642
- if (!creds?.accessToken) {
3643
- console.log(source_default.yellow("Not logged in."), "Run", source_default.cyan("bagdock login"));
3644
- process.exit(1);
3919
+ var init_apps = __esm(() => {
3920
+ init_source();
3921
+ init_config();
3922
+ init_auth();
3923
+ init_output();
3924
+ });
3925
+
3926
+ // src/logs.ts
3927
+ var exports_logs = {};
3928
+ __export(exports_logs, {
3929
+ logsTail: () => logsTail,
3930
+ logsList: () => logsList,
3931
+ logsGet: () => logsGet
3932
+ });
3933
+ async function apiRequest3(method, path2) {
3934
+ const token = getAuthToken();
3935
+ if (!token) {
3936
+ outputError("UNAUTHENTICATED", "Not logged in. Run `bagdock login` or set BAGDOCK_API_KEY.");
3645
3937
  }
3646
- try {
3647
- const res = await fetch(`${API_BASE}/v1/auth/me`, {
3648
- headers: { Authorization: `Bearer ${creds.accessToken}` }
3649
- });
3650
- if (!res.ok) {
3651
- console.log(source_default.red("Session expired or invalid."), "Run", source_default.cyan("bagdock login"));
3652
- process.exit(1);
3938
+ return fetch(`${API_BASE}${path2}`, {
3939
+ method,
3940
+ headers: { Authorization: `Bearer ${token}` }
3941
+ });
3942
+ }
3943
+ function resolveSlug(slug) {
3944
+ if (slug)
3945
+ return slug;
3946
+ const config = loadBagdockJson(process.cwd());
3947
+ if (config?.slug)
3948
+ return config.slug;
3949
+ outputError("MISSING_CONFIG", "No slug specified and no bagdock.json found. Pass --app <slug> or run from a project directory.");
3950
+ return "";
3951
+ }
3952
+ async function logsList(opts) {
3953
+ const slug = resolveSlug(opts.app);
3954
+ const limit = opts.limit || "50";
3955
+ status(`Fetching logs for ${slug}...`);
3956
+ const res = await apiRequest3("GET", `/api/v1/developer/apps/${slug}/logs?limit=${limit}`);
3957
+ if (!res.ok) {
3958
+ if (res.status === 404) {
3959
+ outputError("NOT_FOUND", `App "${slug}" not found or no logs available`);
3960
+ }
3961
+ const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
3962
+ outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
3963
+ }
3964
+ const result = await res.json();
3965
+ if (isJsonMode()) {
3966
+ outputList("log_entry", result.data, false);
3967
+ } else {
3968
+ if (!result.data?.length) {
3969
+ console.log(source_default.yellow(`
3970
+ No logs found for ${slug}.
3971
+ `));
3972
+ return;
3653
3973
  }
3654
- const user = await res.json();
3655
- console.log(source_default.green("Logged in as"), source_default.bold(user.email));
3656
- if (user.operator_id)
3657
- console.log("Operator:", source_default.bold(user.operator_id));
3658
- if (user.name)
3659
- console.log("Name:", user.name);
3660
- } catch (err) {
3661
- console.log(source_default.red("Failed to reach API:"), err.message);
3662
- process.exit(1);
3974
+ console.log();
3975
+ for (const entry of result.data) {
3976
+ const ts = source_default.dim(new Date(entry.timestamp).toISOString());
3977
+ const level = entry.level === "error" ? source_default.red(entry.level) : source_default.dim(entry.level);
3978
+ console.log(` ${ts} ${level} ${entry.message}`);
3979
+ }
3980
+ console.log();
3663
3981
  }
3664
3982
  }
3665
- function sleep(ms) {
3666
- return new Promise((resolve) => setTimeout(resolve, ms));
3983
+ async function logsGet(id, opts) {
3984
+ const slug = resolveSlug(opts.app);
3985
+ status(`Fetching log entry ${id}...`);
3986
+ const res = await apiRequest3("GET", `/api/v1/developer/apps/${slug}/logs/${id}`);
3987
+ if (!res.ok) {
3988
+ const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
3989
+ outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
3990
+ }
3991
+ const data = await res.json();
3992
+ if (isJsonMode()) {
3993
+ outputSuccess(data);
3994
+ } else {
3995
+ console.log(JSON.stringify(data, null, 2));
3996
+ }
3667
3997
  }
3998
+ async function logsTail(opts) {
3999
+ const slug = resolveSlug(opts.app);
4000
+ if (isJsonMode()) {
4001
+ outputError("UNSUPPORTED", "Log tailing is not supported in JSON mode. Use `logs list` instead.");
4002
+ }
4003
+ console.log(source_default.cyan(`
4004
+ Tailing logs for ${slug}... (Ctrl+C to stop)
4005
+ `));
4006
+ let lastTimestamp = new Date().toISOString();
4007
+ const poll = async () => {
4008
+ try {
4009
+ const res = await apiRequest3("GET", `/api/v1/developer/apps/${slug}/logs?since=${encodeURIComponent(lastTimestamp)}&limit=100`);
4010
+ if (res.ok) {
4011
+ const result = await res.json();
4012
+ for (const entry of result.data || []) {
4013
+ const ts = source_default.dim(new Date(entry.timestamp).toISOString());
4014
+ const level = entry.level === "error" ? source_default.red(entry.level) : source_default.dim(entry.level);
4015
+ console.log(` ${ts} ${level} ${entry.message}`);
4016
+ lastTimestamp = entry.timestamp;
4017
+ }
4018
+ }
4019
+ } catch {}
4020
+ };
4021
+ const interval = setInterval(poll, 2000);
4022
+ await poll();
4023
+ process.on("SIGINT", () => {
4024
+ clearInterval(interval);
4025
+ console.log(source_default.dim(`
4026
+ Stopped tailing.
4027
+ `));
4028
+ process.exit(0);
4029
+ });
4030
+ await new Promise(() => {});
4031
+ }
4032
+ var init_logs = __esm(() => {
4033
+ init_source();
4034
+ init_config();
4035
+ init_auth();
4036
+ init_output();
4037
+ });
4038
+
4039
+ // node_modules/commander/esm.mjs
4040
+ var import__ = __toESM(require_commander(), 1);
4041
+ var {
4042
+ program,
4043
+ createCommand,
4044
+ createArgument,
4045
+ createOption,
4046
+ CommanderError,
4047
+ InvalidArgumentError,
4048
+ InvalidOptionArgumentError,
4049
+ Command,
4050
+ Argument,
4051
+ Option,
4052
+ Help
4053
+ } = import__.default;
4054
+
4055
+ // bin/bagdock.ts
4056
+ init_auth();
3668
4057
 
3669
4058
  // src/init.ts
3670
4059
  init_source();
@@ -3914,8 +4303,14 @@ function toPascalCase(s) {
3914
4303
  }
3915
4304
 
3916
4305
  // bin/bagdock.ts
4306
+ init_output();
3917
4307
  var program2 = new Command;
3918
- program2.name("bagdock").description("Bagdock developer CLI").version("0.1.0");
4308
+ program2.name("bagdock").description("Bagdock developer CLI").version("0.1.4").option("--json", "Force JSON output (auto-enabled in non-TTY)").option("-q, --quiet", "Suppress status messages (implies --json)").option("--api-key <key>", "API key to use for this invocation").hook("preAction", (_thisCommand, actionCommand) => {
4309
+ const opts = program2.opts();
4310
+ setOutputMode({ json: opts.json, quiet: opts.quiet });
4311
+ if (opts.apiKey)
4312
+ setApiKeyOverride(opts.apiKey);
4313
+ });
3919
4314
  program2.command("login").description("Authenticate with Bagdock (opens browser)").action(login);
3920
4315
  program2.command("logout").description("Clear stored credentials").action(logout);
3921
4316
  program2.command("whoami").description("Show current authenticated user").action(whoami);
@@ -3924,7 +4319,7 @@ program2.command("dev").description("Start local dev server").option("-p, --port
3924
4319
  const { dev: dev2 } = await Promise.resolve().then(() => (init_dev(), exports_dev));
3925
4320
  await dev2(opts);
3926
4321
  });
3927
- program2.command("deploy").description("Build locally and deploy via Bagdock API → CF Workers for Platforms").option("--env <environment>", "Target environment (preview, staging, production)", "staging").option("--preview", "Deploy an ephemeral preview ({slug}-{hash}.pre.bdok.dev)").option("--production", "Deploy to production ({slug}.bdok.dev)").option("-y, --yes", "Skip confirmation prompts").action(async (opts) => {
4322
+ program2.command("deploy").description("Build locally and deploy via Bagdock API").option("--env <environment>", "Target environment (preview, staging, production)", "staging").option("--preview", "Deploy an ephemeral preview ({slug}-{hash}.pre.bdok.dev)").option("--production", "Deploy to production ({slug}.bdok.dev)").option("-y, --yes", "Skip confirmation prompts").action(async (opts) => {
3928
4323
  const { deploy: deploy2 } = await Promise.resolve().then(() => (init_deploy(), exports_deploy));
3929
4324
  await deploy2(opts);
3930
4325
  });
@@ -3945,4 +4340,39 @@ envCmd.command("remove <key>").description("Remove an environment variable").act
3945
4340
  const { envRemove: envRemove2 } = await Promise.resolve().then(() => (init_env_cmd(), exports_env_cmd));
3946
4341
  await envRemove2(key);
3947
4342
  });
4343
+ var keysCmd = program2.command("keys").description("Manage operator API keys");
4344
+ keysCmd.command("create").description("Create a new API key (raw key shown once)").requiredOption("--name <name>", "Key name").option("--type <type>", "Key type (secret, publishable)", "secret").option("--category <category>", "Key category (standard, restricted, personal)", "standard").option("--environment <env>", "Environment (live, test)", "live").option("--scopes <scopes...>", "Permission scopes").action(async (opts) => {
4345
+ const { keysCreate: keysCreate2 } = await Promise.resolve().then(() => (init_keys(), exports_keys));
4346
+ await keysCreate2(opts);
4347
+ });
4348
+ keysCmd.command("list").description("List API keys").option("--environment <env>", "Filter by environment (live, test)").action(async (opts) => {
4349
+ const { keysList: keysList2 } = await Promise.resolve().then(() => (init_keys(), exports_keys));
4350
+ await keysList2(opts);
4351
+ });
4352
+ keysCmd.command("delete <id>").description("Revoke an API key").option("-y, --yes", "Skip confirmation (required in non-TTY)").option("--reason <reason>", "Revocation reason").action(async (id, opts) => {
4353
+ const { keysDelete: keysDelete2 } = await Promise.resolve().then(() => (init_keys(), exports_keys));
4354
+ await keysDelete2(id, opts);
4355
+ });
4356
+ var appsCmd = program2.command("apps").description("List and inspect deployed apps and edges");
4357
+ appsCmd.command("list").description("List all apps for the authenticated operator").action(async () => {
4358
+ const { appsList: appsList2 } = await Promise.resolve().then(() => (init_apps(), exports_apps));
4359
+ await appsList2();
4360
+ });
4361
+ appsCmd.command("get <slug>").description("Get details for a specific app").action(async (slug) => {
4362
+ const { appsGet: appsGet2 } = await Promise.resolve().then(() => (init_apps(), exports_apps));
4363
+ await appsGet2(slug);
4364
+ });
4365
+ var logsCmd = program2.command("logs").description("View execution logs for deployed apps");
4366
+ logsCmd.command("list").description("List recent log entries").option("--app <slug>", "App slug (defaults to bagdock.json slug)").option("--limit <n>", "Number of entries", "50").action(async (opts) => {
4367
+ const { logsList: logsList2 } = await Promise.resolve().then(() => (init_logs(), exports_logs));
4368
+ await logsList2(opts);
4369
+ });
4370
+ logsCmd.command("get <id>").description("Get a specific log entry").option("--app <slug>", "App slug (defaults to bagdock.json slug)").action(async (id, opts) => {
4371
+ const { logsGet: logsGet2 } = await Promise.resolve().then(() => (init_logs(), exports_logs));
4372
+ await logsGet2(id, opts);
4373
+ });
4374
+ logsCmd.command("tail").description("Stream logs in real-time").option("--app <slug>", "App slug (defaults to bagdock.json slug)").action(async (opts) => {
4375
+ const { logsTail: logsTail2 } = await Promise.resolve().then(() => (init_logs(), exports_logs));
4376
+ await logsTail2(opts);
4377
+ });
3948
4378
  program2.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bagdock/cli",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "description": "Bagdock developer CLI — build, test, and deploy apps and edges on the Bagdock platform",
5
5
  "keywords": [
6
6
  "bagdock",