@bagdock/cli 0.2.0 → 0.4.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 CHANGED
@@ -2085,31 +2085,87 @@ var require_commander = __commonJS((exports) => {
2085
2085
  import { homedir } from "os";
2086
2086
  import { join } from "path";
2087
2087
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
2088
+ function setProfileOverride(name) {
2089
+ profileOverride = name;
2090
+ }
2088
2091
  function ensureConfigDir() {
2089
2092
  if (!existsSync(CONFIG_DIR)) {
2090
2093
  mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
2091
2094
  }
2092
2095
  }
2093
- function loadCredentials() {
2096
+ function loadStore() {
2094
2097
  try {
2095
- if (!existsSync(CREDENTIALS_FILE))
2096
- return null;
2097
- const raw = readFileSync(CREDENTIALS_FILE, "utf-8");
2098
- return JSON.parse(raw);
2098
+ if (!existsSync(CREDENTIALS_FILE)) {
2099
+ return { activeProfile: "default", profiles: {} };
2100
+ }
2101
+ const raw = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
2102
+ if (raw.accessToken && !raw.profiles) {
2103
+ const migrated = {
2104
+ activeProfile: "default",
2105
+ profiles: { default: raw }
2106
+ };
2107
+ saveStore(migrated);
2108
+ return migrated;
2109
+ }
2110
+ return raw;
2099
2111
  } catch {
2100
- return null;
2112
+ return { activeProfile: "default", profiles: {} };
2101
2113
  }
2102
2114
  }
2103
- function saveCredentials(creds) {
2115
+ function saveStore(store) {
2104
2116
  ensureConfigDir();
2105
- writeFileSync(CREDENTIALS_FILE, JSON.stringify(creds, null, 2), { mode: 384 });
2117
+ writeFileSync(CREDENTIALS_FILE, JSON.stringify(store, null, 2), { mode: 384 });
2118
+ }
2119
+ function resolveProfile() {
2120
+ if (profileOverride)
2121
+ return profileOverride;
2122
+ if (process.env.BAGDOCK_PROFILE)
2123
+ return process.env.BAGDOCK_PROFILE;
2124
+ return loadStore().activeProfile;
2125
+ }
2126
+ function loadCredentials() {
2127
+ const store = loadStore();
2128
+ const name = resolveProfile();
2129
+ return store.profiles[name] ?? null;
2130
+ }
2131
+ function saveCredentials(creds, profileName) {
2132
+ const store = loadStore();
2133
+ const name = profileName ?? resolveProfile();
2134
+ store.profiles[name] = creds;
2135
+ if (!store.activeProfile || Object.keys(store.profiles).length === 1) {
2136
+ store.activeProfile = name;
2137
+ }
2138
+ saveStore(store);
2106
2139
  }
2107
2140
  function clearCredentials() {
2108
- try {
2109
- if (existsSync(CREDENTIALS_FILE)) {
2110
- writeFileSync(CREDENTIALS_FILE, "{}", { mode: 384 });
2111
- }
2112
- } catch {}
2141
+ const store = loadStore();
2142
+ const name = resolveProfile();
2143
+ delete store.profiles[name];
2144
+ if (store.activeProfile === name) {
2145
+ const remaining = Object.keys(store.profiles);
2146
+ store.activeProfile = remaining[0] ?? "default";
2147
+ }
2148
+ saveStore(store);
2149
+ }
2150
+ function listProfiles() {
2151
+ const store = loadStore();
2152
+ return Object.entries(store.profiles).map(([name, creds]) => ({
2153
+ name,
2154
+ email: creds.email,
2155
+ operatorId: creds.operatorId,
2156
+ active: name === store.activeProfile
2157
+ }));
2158
+ }
2159
+ function switchProfile(name) {
2160
+ const store = loadStore();
2161
+ if (!store.profiles[name])
2162
+ return false;
2163
+ store.activeProfile = name;
2164
+ saveStore(store);
2165
+ return true;
2166
+ }
2167
+ function getActiveProfileName() {
2168
+ return resolveProfile();
2113
2169
  }
2114
2170
  function loadBagdockJson(dir) {
2115
2171
  const file = join(dir, "bagdock.json");
@@ -2121,7 +2177,7 @@ function loadBagdockJson(dir) {
2121
2177
  return null;
2122
2178
  }
2123
2179
  }
2124
- var CONFIG_DIR, CREDENTIALS_FILE, API_BASE, DASHBOARD_BASE;
2180
+ var CONFIG_DIR, CREDENTIALS_FILE, API_BASE, DASHBOARD_BASE, profileOverride;
2125
2181
  var init_config = __esm(() => {
2126
2182
  CONFIG_DIR = join(homedir(), ".bagdock");
2127
2183
  CREDENTIALS_FILE = join(CONFIG_DIR, "credentials.json");
@@ -2618,6 +2674,59 @@ var init_source = __esm(() => {
2618
2674
  source_default = chalk;
2619
2675
  });
2620
2676
 
2677
+ // src/output.ts
2678
+ function setOutputMode(opts) {
2679
+ quiet = !!opts.quiet;
2680
+ forceJson = !!opts.json || !!opts.quiet;
2681
+ }
2682
+ function isJsonMode() {
2683
+ return forceJson || !process.stdout.isTTY;
2684
+ }
2685
+ function isQuiet() {
2686
+ return quiet;
2687
+ }
2688
+ function outputSuccess(data) {
2689
+ if (isJsonMode()) {
2690
+ process.stdout.write(JSON.stringify(data, null, 2) + `
2691
+ `);
2692
+ }
2693
+ }
2694
+ function outputError(code, message, details) {
2695
+ if (isJsonMode()) {
2696
+ const err = { error: { code, message } };
2697
+ if (details)
2698
+ err.error.details = details;
2699
+ process.stderr.write(JSON.stringify(err) + `
2700
+ `);
2701
+ process.exit(1);
2702
+ } else {
2703
+ console.error(source_default.red(`Error [${code}]:`), message);
2704
+ if (details)
2705
+ console.error(details);
2706
+ process.exit(1);
2707
+ }
2708
+ }
2709
+ function outputList(objectType, data, hasMore) {
2710
+ if (isJsonMode()) {
2711
+ process.stdout.write(JSON.stringify({ object: "list", data, has_more: hasMore }, null, 2) + `
2712
+ `);
2713
+ }
2714
+ }
2715
+ function status(message) {
2716
+ if (!isQuiet() && !isJsonMode()) {
2717
+ console.log(source_default.dim(message));
2718
+ }
2719
+ }
2720
+ function success(message) {
2721
+ if (!isQuiet() && !isJsonMode()) {
2722
+ console.log(source_default.green(message));
2723
+ }
2724
+ }
2725
+ var forceJson = false, quiet = false;
2726
+ var init_output = __esm(() => {
2727
+ init_source();
2728
+ });
2729
+
2621
2730
  // node_modules/is-docker/index.js
2622
2731
  import fs from "node:fs";
2623
2732
  function hasDockerEnv() {
@@ -3172,6 +3281,18 @@ function getAuthToken() {
3172
3281
  const creds = loadCredentials();
3173
3282
  return creds?.accessToken ?? null;
3174
3283
  }
3284
+ function getAuthSource() {
3285
+ if (apiKeyOverride)
3286
+ return { token: apiKeyOverride, source: "flag" };
3287
+ if (process.env.BAGDOCK_API_KEY)
3288
+ return { token: process.env.BAGDOCK_API_KEY, source: "env (BAGDOCK_API_KEY)" };
3289
+ if (process.env.BAGDOCK_TOKEN)
3290
+ return { token: process.env.BAGDOCK_TOKEN, source: "env (BAGDOCK_TOKEN)" };
3291
+ const creds = loadCredentials();
3292
+ if (creds?.accessToken)
3293
+ return { token: creds.accessToken, source: `config (${getActiveProfileName()})` };
3294
+ return { token: null, source: "none" };
3295
+ }
3175
3296
  async function login() {
3176
3297
  const existing = loadCredentials();
3177
3298
  if (existing?.accessToken && existing.expiresAt && existing.expiresAt > Date.now()) {
@@ -3228,12 +3349,12 @@ Requesting device authorization...
3228
3349
  console.log(" Email:", source_default.bold(tokens.email));
3229
3350
  if (tokens.operator_id)
3230
3351
  console.log(" Operator:", source_default.bold(tokens.operator_id));
3352
+ console.log(" Profile:", source_default.bold(getActiveProfileName()));
3231
3353
  return;
3232
3354
  }
3233
3355
  const error = await tokenRes.json().catch(() => ({ error: "unknown" }));
3234
- if (error.error === "authorization_pending") {
3356
+ if (error.error === "authorization_pending")
3235
3357
  continue;
3236
- }
3237
3358
  if (error.error === "slow_down") {
3238
3359
  await sleep(pollInterval);
3239
3360
  continue;
@@ -3258,7 +3379,7 @@ Requesting device authorization...
3258
3379
  }
3259
3380
  async function logout() {
3260
3381
  clearCredentials();
3261
- console.log(source_default.green("Logged out."));
3382
+ console.log(source_default.green(`Logged out of profile "${getActiveProfileName()}".`));
3262
3383
  }
3263
3384
  async function whoami() {
3264
3385
  const token = getAuthToken();
@@ -3275,16 +3396,92 @@ async function whoami() {
3275
3396
  process.exit(1);
3276
3397
  }
3277
3398
  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);
3399
+ if (isJsonMode()) {
3400
+ outputSuccess({ ...user, profile: getActiveProfileName() });
3401
+ } else {
3402
+ console.log(source_default.green("Logged in as"), source_default.bold(user.email));
3403
+ if (user.operator_id)
3404
+ console.log("Operator:", source_default.bold(user.operator_id));
3405
+ if (user.name)
3406
+ console.log("Name:", user.name);
3407
+ console.log("Profile:", source_default.bold(getActiveProfileName()));
3408
+ }
3283
3409
  } catch (err) {
3284
3410
  console.log(source_default.red("Failed to reach API:"), err.message);
3285
3411
  process.exit(1);
3286
3412
  }
3287
3413
  }
3414
+ async function authList() {
3415
+ const profiles = listProfiles();
3416
+ if (isJsonMode()) {
3417
+ outputSuccess({ profiles });
3418
+ return;
3419
+ }
3420
+ if (!profiles.length) {
3421
+ console.log(source_default.yellow(`
3422
+ No profiles found.`), "Run", source_default.cyan("bagdock login"), `to create one.
3423
+ `);
3424
+ return;
3425
+ }
3426
+ console.log();
3427
+ for (const p of profiles) {
3428
+ const marker = p.active ? source_default.green("* ") : " ";
3429
+ const label = p.active ? source_default.bold(p.name) : p.name;
3430
+ const email = p.email ? source_default.dim(` (${p.email})`) : "";
3431
+ const op = p.operatorId ? source_default.dim(` [${p.operatorId}]`) : "";
3432
+ console.log(` ${marker}${label}${email}${op}`);
3433
+ }
3434
+ console.log();
3435
+ }
3436
+ async function authSwitch(name) {
3437
+ const profiles = listProfiles();
3438
+ if (!profiles.length) {
3439
+ console.log(source_default.yellow("No profiles found."), "Run", source_default.cyan("bagdock login"), "first.");
3440
+ process.exit(1);
3441
+ }
3442
+ if (name) {
3443
+ if (switchProfile(name)) {
3444
+ if (isJsonMode()) {
3445
+ outputSuccess({ active_profile: name });
3446
+ } else {
3447
+ console.log(source_default.green(`Switched to profile "${name}".`));
3448
+ }
3449
+ } else {
3450
+ outputError("NOT_FOUND", `Profile "${name}" not found. Available: ${profiles.map((p) => p.name).join(", ")}`);
3451
+ }
3452
+ return;
3453
+ }
3454
+ if (!process.stdout.isTTY) {
3455
+ outputError("MISSING_PROFILE", "Pass a profile name in non-interactive mode: bagdock auth switch <name>");
3456
+ }
3457
+ console.log(source_default.cyan(`
3458
+ Available profiles:
3459
+ `));
3460
+ profiles.forEach((p, i) => {
3461
+ const marker = p.active ? source_default.green("* ") : " ";
3462
+ const email = p.email ? source_default.dim(` (${p.email})`) : "";
3463
+ console.log(` ${marker}${i + 1}. ${p.name}${email}`);
3464
+ });
3465
+ const readline = await import("readline");
3466
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
3467
+ const answer = await new Promise((resolve) => {
3468
+ rl.question(source_default.cyan(`
3469
+ Enter profile number or name: `), resolve);
3470
+ });
3471
+ rl.close();
3472
+ const idx = parseInt(answer, 10);
3473
+ const target = idx > 0 && idx <= profiles.length ? profiles[idx - 1].name : answer.trim();
3474
+ if (switchProfile(target)) {
3475
+ console.log(source_default.green(`
3476
+ Switched to profile "${target}".
3477
+ `));
3478
+ } else {
3479
+ console.log(source_default.red(`
3480
+ Profile "${target}" not found.
3481
+ `));
3482
+ process.exit(1);
3483
+ }
3484
+ }
3288
3485
  function sleep(ms) {
3289
3486
  return new Promise((resolve) => setTimeout(resolve, ms));
3290
3487
  }
@@ -3292,64 +3489,136 @@ var apiKeyOverride, CLIENT_ID = "bagdock-cli", MAX_POLL_DURATION_MS = 300000;
3292
3489
  var init_auth = __esm(() => {
3293
3490
  init_config();
3294
3491
  init_source();
3492
+ init_output();
3295
3493
  });
3296
3494
 
3297
- // src/output.ts
3298
- function setOutputMode(opts) {
3299
- if (opts.quiet) {
3300
- quiet = true;
3301
- forceJson = true;
3495
+ // src/doctor.ts
3496
+ var exports_doctor = {};
3497
+ __export(exports_doctor, {
3498
+ doctor: () => doctor
3499
+ });
3500
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
3501
+ import { join as join3, dirname } from "path";
3502
+ import { homedir as homedir2, platform as platform2 } from "os";
3503
+ import { fileURLToPath as fileURLToPath2 } from "url";
3504
+ function maskKey(key) {
3505
+ if (key.length <= 12)
3506
+ return key.slice(0, 4) + "...";
3507
+ return key.slice(0, 8) + "..." + key.slice(-4);
3508
+ }
3509
+ function getLocalVersion() {
3510
+ try {
3511
+ const pkgPath = join3(dirname(fileURLToPath2(import.meta.url)), "..", "package.json");
3512
+ const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
3513
+ return pkg.version ?? "unknown";
3514
+ } catch {
3515
+ return "unknown";
3302
3516
  }
3303
- if (opts.json) {
3304
- forceJson = true;
3517
+ }
3518
+ async function checkVersion() {
3519
+ const local = getLocalVersion();
3520
+ try {
3521
+ const res = await fetch("https://registry.npmjs.org/@bagdock/cli/latest", {
3522
+ signal: AbortSignal.timeout(5000)
3523
+ });
3524
+ if (res.ok) {
3525
+ const data = await res.json();
3526
+ if (data.version === local) {
3527
+ return { name: "CLI Version", status: "pass", message: `v${local} (latest)` };
3528
+ }
3529
+ return { name: "CLI Version", status: "warn", message: `v${local} (update available: v${data.version})` };
3530
+ }
3531
+ } catch {}
3532
+ return { name: "CLI Version", status: "pass", message: `v${local} (registry unreachable)` };
3533
+ }
3534
+ function checkAuth() {
3535
+ const { token, source } = getAuthSource();
3536
+ if (!token) {
3537
+ return { name: "API Key", status: "fail", message: "No API key found. Run `bagdock login` or set BAGDOCK_API_KEY." };
3305
3538
  }
3539
+ return { name: "API Key", status: "pass", message: `${maskKey(token)} (source: ${source})` };
3306
3540
  }
3307
- function isJsonMode() {
3308
- return forceJson || !process.stdout.isTTY;
3541
+ function checkProjectConfig() {
3542
+ const config = loadBagdockJson(process.cwd());
3543
+ if (!config) {
3544
+ return { name: "Project Config", status: "warn", message: "No bagdock.json found in current directory" };
3545
+ }
3546
+ const issues = [];
3547
+ if (!config.slug)
3548
+ issues.push("missing slug");
3549
+ if (!config.type)
3550
+ issues.push("missing type");
3551
+ if (!config.main)
3552
+ issues.push("missing main");
3553
+ if (issues.length) {
3554
+ return { name: "Project Config", status: "warn", message: `bagdock.json has issues: ${issues.join(", ")}` };
3555
+ }
3556
+ return { name: "Project Config", status: "pass", message: `${config.slug} (${config.type}/${config.kind ?? config.category})` };
3309
3557
  }
3310
- function isQuiet() {
3311
- return quiet;
3558
+ function checkAgents() {
3559
+ const detected = [];
3560
+ const home = homedir2();
3561
+ if (existsSync3(join3(home, ".cursor")))
3562
+ detected.push("Cursor");
3563
+ if (existsSync3(join3(home, ".windsurf")))
3564
+ detected.push("Windsurf");
3565
+ if (existsSync3(join3(home, "clawd", "skills")) || existsSync3(join3(home, ".codex")))
3566
+ detected.push("OpenClaw/Codex");
3567
+ const p = platform2();
3568
+ if (p === "darwin" && existsSync3(join3(home, "Library", "Application Support", "Claude", "claude_desktop_config.json"))) {
3569
+ detected.push("Claude Desktop");
3570
+ } else if (p === "win32" && existsSync3(join3(process.env.APPDATA ?? "", "Claude", "claude_desktop_config.json"))) {
3571
+ detected.push("Claude Desktop");
3572
+ } else if (p === "linux" && existsSync3(join3(home, ".config", "Claude", "claude_desktop_config.json"))) {
3573
+ detected.push("Claude Desktop");
3574
+ }
3575
+ if (existsSync3(join3(process.cwd(), ".vscode", "mcp.json")))
3576
+ detected.push("VS Code");
3577
+ if (!detected.length) {
3578
+ return { name: "AI Agents", status: "pass", message: "None detected" };
3579
+ }
3580
+ return { name: "AI Agents", status: "pass", message: `Detected: ${detected.join(", ")}` };
3312
3581
  }
3313
- function outputSuccess(data) {
3582
+ async function doctor() {
3583
+ const checks = [];
3314
3584
  if (isJsonMode()) {
3315
- process.stdout.write(JSON.stringify(data, null, 2) + `
3316
- `);
3585
+ checks.push(await checkVersion());
3586
+ checks.push(checkAuth());
3587
+ checks.push(checkProjectConfig());
3588
+ checks.push(checkAgents());
3589
+ const ok = checks.every((c) => c.status !== "fail");
3590
+ outputSuccess({ ok, checks });
3591
+ return;
3317
3592
  }
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) + `
3593
+ console.log(source_default.bold(`
3594
+ Bagdock Doctor
3595
+ `));
3596
+ process.stdout.write(" Checking CLI version...");
3597
+ const versionCheck = await checkVersion();
3598
+ checks.push(versionCheck);
3599
+ process.stdout.write(`\r ${ICON[versionCheck.status]} ${versionCheck.name}: ${versionCheck.message}
3325
3600
  `);
3601
+ const authCheck = checkAuth();
3602
+ checks.push(authCheck);
3603
+ console.log(` ${ICON[authCheck.status]} ${authCheck.name}: ${authCheck.message}`);
3604
+ const configCheck = checkProjectConfig();
3605
+ checks.push(configCheck);
3606
+ console.log(` ${ICON[configCheck.status]} ${configCheck.name}: ${configCheck.message}`);
3607
+ const agentCheck = checkAgents();
3608
+ checks.push(agentCheck);
3609
+ console.log(` ${ICON[agentCheck.status]} ${agentCheck.name}: ${agentCheck.message}`);
3610
+ console.log();
3611
+ const hasFail = checks.some((c) => c.status === "fail");
3612
+ if (hasFail)
3326
3613
  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
3614
  }
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(() => {
3615
+ var ICON;
3616
+ var init_doctor = __esm(() => {
3352
3617
  init_source();
3618
+ init_auth();
3619
+ init_config();
3620
+ init_output();
3621
+ ICON = { pass: source_default.green("✔"), warn: source_default.yellow("!"), fail: source_default.red("✘") };
3353
3622
  });
3354
3623
 
3355
3624
  // src/dev.ts
@@ -3357,8 +3626,8 @@ var exports_dev = {};
3357
3626
  __export(exports_dev, {
3358
3627
  dev: () => dev
3359
3628
  });
3360
- import { existsSync as existsSync3, writeFileSync as writeFileSync3 } from "fs";
3361
- import { join as join3 } from "path";
3629
+ import { existsSync as existsSync4, writeFileSync as writeFileSync3 } from "fs";
3630
+ import { join as join4 } from "path";
3362
3631
  import { spawn } from "child_process";
3363
3632
  async function dev(opts) {
3364
3633
  const cwd = process.cwd();
@@ -3369,11 +3638,11 @@ async function dev(opts) {
3369
3638
  }
3370
3639
  const port = opts.port ?? "8787";
3371
3640
  const wranglerToml = generateWranglerToml(config, port);
3372
- const wranglerPath = join3(cwd, "wrangler.toml");
3641
+ const wranglerPath = join4(cwd, "wrangler.toml");
3373
3642
  writeFileSync3(wranglerPath, wranglerToml);
3374
3643
  console.log(source_default.green("Generated"), source_default.cyan("wrangler.toml"), source_default.green("from bagdock.json"));
3375
- const devVarsPath = join3(cwd, ".dev.vars");
3376
- if (!existsSync3(devVarsPath) && config.env) {
3644
+ const devVarsPath = join4(cwd, ".dev.vars");
3645
+ if (!existsSync4(devVarsPath) && config.env) {
3377
3646
  const hint = Object.entries(config.env).map(([key, meta]) => `# ${meta.description ?? key}${meta.required ? " (required)" : ""}
3378
3647
  ${key}=`).join(`
3379
3648
  `);
@@ -3434,8 +3703,8 @@ var exports_deploy = {};
3434
3703
  __export(exports_deploy, {
3435
3704
  deploy: () => deploy
3436
3705
  });
3437
- import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
3438
- import { join as join4 } from "path";
3706
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
3707
+ import { join as join5 } from "path";
3439
3708
  import { execSync } from "child_process";
3440
3709
  import { createInterface } from "readline";
3441
3710
  async function deploy(opts) {
@@ -3476,8 +3745,8 @@ async function deploy(opts) {
3476
3745
  console.log(source_default.cyan(`
3477
3746
  Deploying ${config.slug}@${config.version} → ${envLabel}
3478
3747
  `));
3479
- const outDir = join4(cwd, ".bagdock");
3480
- const outFile = join4(outDir, "worker.mjs");
3748
+ const outDir = join5(cwd, ".bagdock");
3749
+ const outFile = join5(outDir, "worker.mjs");
3481
3750
  console.log(source_default.dim(" Building worker bundle..."));
3482
3751
  execSync(`mkdir -p ${outDir}`, { cwd });
3483
3752
  try {
@@ -3493,11 +3762,11 @@ Deploying ${config.slug}@${config.version} → ${envLabel}
3493
3762
  process.exit(1);
3494
3763
  }
3495
3764
  }
3496
- if (!existsSync4(outFile)) {
3765
+ if (!existsSync5(outFile)) {
3497
3766
  console.error(source_default.red(" Build output not found at"), outFile);
3498
3767
  process.exit(1);
3499
3768
  }
3500
- const scriptContent = readFileSync2(outFile);
3769
+ const scriptContent = readFileSync3(outFile);
3501
3770
  const sizeKb = (scriptContent.length / 1024).toFixed(1);
3502
3771
  console.log(source_default.dim(` Bundle size: ${sizeKb} KB`));
3503
3772
  console.log(source_default.dim(" Uploading to Bagdock platform..."));
@@ -3625,14 +3894,430 @@ var init_submit = __esm(() => {
3625
3894
  init_auth();
3626
3895
  });
3627
3896
 
3897
+ // src/link.ts
3898
+ var exports_link = {};
3899
+ __export(exports_link, {
3900
+ resolveSlug: () => resolveSlug,
3901
+ requireSlug: () => requireSlug,
3902
+ link: () => link
3903
+ });
3904
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
3905
+ import { join as join6 } from "path";
3906
+ function resolveSlug() {
3907
+ const config = loadBagdockJson(process.cwd());
3908
+ if (config?.slug)
3909
+ return config.slug;
3910
+ const linkPath = join6(process.cwd(), LINK_DIR, LINK_FILE);
3911
+ if (existsSync6(linkPath)) {
3912
+ try {
3913
+ const data = JSON.parse(readFileSync4(linkPath, "utf-8"));
3914
+ return data.slug ?? null;
3915
+ } catch {
3916
+ return null;
3917
+ }
3918
+ }
3919
+ return null;
3920
+ }
3921
+ function requireSlug(slugArg) {
3922
+ const slug = slugArg ?? resolveSlug();
3923
+ if (!slug) {
3924
+ if (isJsonMode()) {
3925
+ outputError("no_project", "No project found. Pass --slug, add bagdock.json, or run bagdock link.");
3926
+ }
3927
+ console.error(source_default.red("No project found."), "Pass a slug, create bagdock.json, or run", source_default.cyan("bagdock link"));
3928
+ process.exit(1);
3929
+ }
3930
+ return slug;
3931
+ }
3932
+ async function link(opts) {
3933
+ let slug = opts.slug;
3934
+ if (!slug) {
3935
+ const config = loadBagdockJson(process.cwd());
3936
+ if (config?.slug) {
3937
+ slug = config.slug;
3938
+ status(`Found bagdock.json — linking to ${slug}`);
3939
+ }
3940
+ }
3941
+ if (!slug && process.stdout.isTTY && !isJsonMode()) {
3942
+ const token = getAuthToken();
3943
+ if (!token) {
3944
+ console.error(source_default.red("Not authenticated."), "Run", source_default.cyan("bagdock login"), "first.");
3945
+ process.exit(1);
3946
+ }
3947
+ status("Fetching your apps...");
3948
+ try {
3949
+ const res = await fetch(`${API_BASE}/v1/developer/apps`, {
3950
+ headers: { Authorization: `Bearer ${token}` }
3951
+ });
3952
+ if (!res.ok)
3953
+ throw new Error(`API returned ${res.status}`);
3954
+ const { data } = await res.json();
3955
+ if (!data?.length) {
3956
+ console.error(source_default.yellow("No apps found."), "Create one with", source_default.cyan("bagdock init"));
3957
+ process.exit(1);
3958
+ }
3959
+ console.log(source_default.bold(`
3960
+ Your apps:
3961
+ `));
3962
+ data.forEach((app, i) => console.log(` ${source_default.cyan(i + 1)} ${app.name} ${source_default.dim(`(${app.slug})`)}`));
3963
+ console.log();
3964
+ const readline = await import("readline");
3965
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
3966
+ const answer = await new Promise((resolve) => rl.question("Select app number: ", resolve));
3967
+ rl.close();
3968
+ const idx = parseInt(answer, 10) - 1;
3969
+ if (isNaN(idx) || idx < 0 || idx >= data.length) {
3970
+ console.error(source_default.red("Invalid selection"));
3971
+ process.exit(1);
3972
+ }
3973
+ slug = data[idx].slug;
3974
+ } catch (err) {
3975
+ console.error(source_default.red("Failed to fetch apps:"), err.message);
3976
+ process.exit(1);
3977
+ }
3978
+ }
3979
+ if (!slug) {
3980
+ outputError("missing_slug", "Slug required. Pass --slug in non-interactive mode.");
3981
+ process.exit(1);
3982
+ }
3983
+ const dir = join6(process.cwd(), LINK_DIR);
3984
+ if (!existsSync6(dir))
3985
+ mkdirSync3(dir, { recursive: true });
3986
+ const linkData = { slug, linkedAt: new Date().toISOString() };
3987
+ writeFileSync4(join6(dir, LINK_FILE), JSON.stringify(linkData, null, 2));
3988
+ if (isJsonMode()) {
3989
+ outputSuccess({ slug, path: join6(dir, LINK_FILE) });
3990
+ } else {
3991
+ console.log(source_default.green(`Linked to ${source_default.bold(slug)}`));
3992
+ console.log(source_default.dim(` Stored in ${LINK_DIR}/${LINK_FILE}`));
3993
+ }
3994
+ }
3995
+ var LINK_DIR = ".bagdock", LINK_FILE = "link.json";
3996
+ var init_link = __esm(() => {
3997
+ init_source();
3998
+ init_config();
3999
+ init_auth();
4000
+ init_output();
4001
+ });
4002
+
4003
+ // src/validate.ts
4004
+ var exports_validate = {};
4005
+ __export(exports_validate, {
4006
+ validate: () => validate
4007
+ });
4008
+ import { existsSync as existsSync7, statSync } from "fs";
4009
+ import { join as join7 } from "path";
4010
+ async function validate() {
4011
+ const checks = [];
4012
+ const dir = process.cwd();
4013
+ const config = loadBagdockJson(dir);
4014
+ if (!config) {
4015
+ checks.push({ name: "bagdock.json", status: "fail", message: "Not found or invalid JSON" });
4016
+ return finish(checks);
4017
+ }
4018
+ checks.push({ name: "bagdock.json", status: "pass", message: "Found and parsed" });
4019
+ const required = ["name", "slug", "version", "type", "category", "main"];
4020
+ const missing = required.filter((f) => !config[f]);
4021
+ if (missing.length) {
4022
+ checks.push({ name: "Required fields", status: "fail", message: `Missing: ${missing.join(", ")}` });
4023
+ } else {
4024
+ checks.push({ name: "Required fields", status: "pass", message: "All present" });
4025
+ }
4026
+ if (!VALID_TYPES.includes(config.type)) {
4027
+ checks.push({ name: "Type", status: "fail", message: `Invalid type "${config.type}". Must be: ${VALID_TYPES.join(", ")}` });
4028
+ } else {
4029
+ checks.push({ name: "Type", status: "pass", message: config.type });
4030
+ }
4031
+ if (config.kind && !VALID_KINDS.includes(config.kind)) {
4032
+ checks.push({ name: "Kind", status: "warn", message: `Unknown kind "${config.kind}". Expected: ${VALID_KINDS.join(", ")}` });
4033
+ }
4034
+ const entryPath = join7(dir, config.main);
4035
+ if (!existsSync7(entryPath)) {
4036
+ checks.push({ name: "Entry point", status: "fail", message: `File not found: ${config.main}` });
4037
+ } else {
4038
+ const size = statSync(entryPath).size;
4039
+ checks.push({ name: "Entry point", status: "pass", message: `${config.main} (${(size / 1024).toFixed(1)} KB)` });
4040
+ if (size > MAX_BUNDLE_BYTES) {
4041
+ checks.push({ name: "Bundle size", status: "fail", message: `${(size / 1024 / 1024).toFixed(1)} MB exceeds ${MAX_BUNDLE_BYTES / 1024 / 1024} MB limit` });
4042
+ } else if (size > MAX_BUNDLE_BYTES * 0.8) {
4043
+ checks.push({ name: "Bundle size", status: "warn", message: `${(size / 1024 / 1024).toFixed(1)} MB — approaching limit` });
4044
+ } else {
4045
+ checks.push({ name: "Bundle size", status: "pass", message: `${(size / 1024).toFixed(1)} KB` });
4046
+ }
4047
+ }
4048
+ const linked = resolveSlug();
4049
+ if (linked && linked !== config.slug) {
4050
+ checks.push({ name: "Project link", status: "warn", message: `bagdock.json slug "${config.slug}" differs from linked project "${linked}"` });
4051
+ }
4052
+ return finish(checks);
4053
+ }
4054
+ function finish(checks) {
4055
+ const hasFail = checks.some((c) => c.status === "fail");
4056
+ const hasWarn = checks.some((c) => c.status === "warn");
4057
+ if (isJsonMode()) {
4058
+ outputSuccess({ ok: !hasFail, checks });
4059
+ if (hasFail)
4060
+ process.exit(1);
4061
+ return;
4062
+ }
4063
+ console.log(source_default.bold(`
4064
+ Bagdock Validate
4065
+ `));
4066
+ for (const c of checks) {
4067
+ const icon = c.status === "pass" ? source_default.green("✔") : c.status === "warn" ? source_default.yellow("⚠") : source_default.red("✖");
4068
+ console.log(` ${icon} ${c.name}: ${c.message}`);
4069
+ }
4070
+ console.log();
4071
+ if (hasFail) {
4072
+ console.log(source_default.red(` Validation failed. Fix errors before submitting.
4073
+ `));
4074
+ process.exit(1);
4075
+ } else if (hasWarn) {
4076
+ console.log(source_default.yellow(` Passed with warnings.
4077
+ `));
4078
+ } else {
4079
+ console.log(source_default.green(` All checks passed. Ready to submit.
4080
+ `));
4081
+ }
4082
+ }
4083
+ var VALID_TYPES, VALID_KINDS, MAX_BUNDLE_BYTES;
4084
+ var init_validate = __esm(() => {
4085
+ init_source();
4086
+ init_config();
4087
+ init_output();
4088
+ init_link();
4089
+ VALID_TYPES = ["edge", "app"];
4090
+ VALID_KINDS = ["adapter", "comms", "webhook", "ui-extension", "microfrontend"];
4091
+ MAX_BUNDLE_BYTES = 10 * 1024 * 1024;
4092
+ });
4093
+
4094
+ // src/submission.ts
4095
+ var exports_submission = {};
4096
+ __export(exports_submission, {
4097
+ submissionWithdraw: () => submissionWithdraw,
4098
+ submissionStatus: () => submissionStatus,
4099
+ submissionList: () => submissionList
4100
+ });
4101
+ function requireAuth() {
4102
+ const token = getAuthToken();
4103
+ if (!token) {
4104
+ outputError("auth_error", "Not authenticated. Run bagdock login or set BAGDOCK_API_KEY.");
4105
+ process.exit(1);
4106
+ }
4107
+ return token;
4108
+ }
4109
+ async function submissionList(opts) {
4110
+ const token = requireAuth();
4111
+ const slug = requireSlug(opts.app);
4112
+ status(`Fetching submissions for ${slug}...`);
4113
+ try {
4114
+ const res = await fetch(`${API_BASE}/v1/developer/apps/${slug}/submissions`, {
4115
+ headers: { Authorization: `Bearer ${token}` }
4116
+ });
4117
+ if (res.status === 404) {
4118
+ outputError("not_found", `App "${slug}" not found or no submissions exist.`);
4119
+ }
4120
+ if (!res.ok) {
4121
+ outputError("api_error", `API returned ${res.status}`);
4122
+ }
4123
+ const { data } = await res.json();
4124
+ if (isJsonMode()) {
4125
+ outputList("submission", data, false);
4126
+ return;
4127
+ }
4128
+ if (!data?.length) {
4129
+ console.log(source_default.yellow("No submissions found for this app."));
4130
+ console.log("Submit with", source_default.cyan("bagdock submit"));
4131
+ return;
4132
+ }
4133
+ console.log(source_default.bold(`
4134
+ Submissions for ${slug}:
4135
+ `));
4136
+ console.log(` ${"ID".padEnd(22)} ${"Version".padEnd(10)} ${"Reason".padEnd(30)} ${"Date"}`);
4137
+ console.log(source_default.dim(" " + "─".repeat(80)));
4138
+ for (const s of data) {
4139
+ console.log(` ${source_default.cyan(s.id.padEnd(22))} ${(s.version ?? "").padEnd(10)} ${(s.change_reason ?? "").slice(0, 30).padEnd(30)} ${source_default.dim(new Date(s.created_at).toLocaleDateString())}`);
4140
+ }
4141
+ console.log();
4142
+ } catch (err) {
4143
+ outputError("network_error", err.message);
4144
+ }
4145
+ }
4146
+ async function submissionStatus(id, opts) {
4147
+ const token = requireAuth();
4148
+ const slug = requireSlug(opts.app);
4149
+ status(`Fetching submission ${id}...`);
4150
+ try {
4151
+ const res = await fetch(`${API_BASE}/v1/developer/apps/${slug}/submissions/${id}`, {
4152
+ headers: { Authorization: `Bearer ${token}` }
4153
+ });
4154
+ if (res.status === 404) {
4155
+ outputError("not_found", `Submission "${id}" not found.`);
4156
+ }
4157
+ if (!res.ok) {
4158
+ outputError("api_error", `API returned ${res.status}`);
4159
+ }
4160
+ const { data } = await res.json();
4161
+ if (isJsonMode()) {
4162
+ outputSuccess(data);
4163
+ return;
4164
+ }
4165
+ console.log(source_default.bold(`
4166
+ Submission ${source_default.cyan(data.id)}
4167
+ `));
4168
+ const fields = [
4169
+ ["App", `${data.name} (${data.slug})`],
4170
+ ["Version", data.version],
4171
+ ["Review Status", data.review_status],
4172
+ ["Type", data.type],
4173
+ ["Visibility", data.visibility],
4174
+ ["Reason", data.change_reason],
4175
+ ["Submitted by", data.changed_by],
4176
+ ["Date", new Date(data.created_at).toLocaleString()]
4177
+ ];
4178
+ for (const [label, value] of fields) {
4179
+ console.log(` ${source_default.dim(label.padEnd(16))} ${value ?? source_default.dim("—")}`);
4180
+ }
4181
+ console.log();
4182
+ } catch (err) {
4183
+ outputError("network_error", err.message);
4184
+ }
4185
+ }
4186
+ async function submissionWithdraw(id, opts) {
4187
+ const token = requireAuth();
4188
+ const slug = requireSlug(opts.app);
4189
+ status(`Withdrawing submission ${id}...`);
4190
+ try {
4191
+ const res = await fetch(`${API_BASE}/v1/developer/apps/${slug}/submissions/${id}/withdraw`, {
4192
+ method: "POST",
4193
+ headers: { Authorization: `Bearer ${token}` }
4194
+ });
4195
+ if (res.status === 404) {
4196
+ outputError("not_found", `App "${slug}" not found.`);
4197
+ }
4198
+ if (res.status === 409) {
4199
+ const body = await res.json();
4200
+ outputError(body.code ?? "invalid_status", body.message);
4201
+ }
4202
+ if (!res.ok) {
4203
+ outputError("api_error", `API returned ${res.status}`);
4204
+ }
4205
+ const { data } = await res.json();
4206
+ if (isJsonMode()) {
4207
+ outputSuccess(data);
4208
+ return;
4209
+ }
4210
+ console.log(source_default.green(`Submission withdrawn.`), source_default.dim(`Status is now: ${data.review_status}`));
4211
+ console.log("You can re-submit with", source_default.cyan("bagdock submit"));
4212
+ } catch (err) {
4213
+ outputError("network_error", err.message);
4214
+ }
4215
+ }
4216
+ var init_submission = __esm(() => {
4217
+ init_source();
4218
+ init_config();
4219
+ init_auth();
4220
+ init_output();
4221
+ init_link();
4222
+ });
4223
+
4224
+ // src/open.ts
4225
+ var exports_open2 = {};
4226
+ __export(exports_open2, {
4227
+ open: () => open2
4228
+ });
4229
+ async function open2(slugArg) {
4230
+ const slug = requireSlug(slugArg);
4231
+ const url = `${DASHBOARD_BASE}/developer/apps/${slug}`;
4232
+ if (isJsonMode()) {
4233
+ outputSuccess({ url, slug });
4234
+ return;
4235
+ }
4236
+ status(`Opening ${url}`);
4237
+ await open_default(url);
4238
+ console.log(source_default.green("Opened"), source_default.cyan(url));
4239
+ }
4240
+ var init_open2 = __esm(() => {
4241
+ init_source();
4242
+ init_open();
4243
+ init_config();
4244
+ init_output();
4245
+ init_link();
4246
+ });
4247
+
4248
+ // src/inspect.ts
4249
+ var exports_inspect = {};
4250
+ __export(exports_inspect, {
4251
+ inspect: () => inspect
4252
+ });
4253
+ async function inspect(slugArg) {
4254
+ const token = getAuthToken();
4255
+ if (!token) {
4256
+ outputError("auth_error", "Not authenticated. Run bagdock login or set BAGDOCK_API_KEY.");
4257
+ process.exit(1);
4258
+ }
4259
+ const slug = requireSlug(slugArg);
4260
+ status(`Inspecting ${slug}...`);
4261
+ try {
4262
+ const res = await fetch(`${API_BASE}/v1/developer/apps/${slug}`, {
4263
+ headers: { Authorization: `Bearer ${token}` }
4264
+ });
4265
+ if (res.status === 404)
4266
+ outputError("not_found", `App "${slug}" not found.`);
4267
+ if (!res.ok)
4268
+ outputError("api_error", `API returned ${res.status}`);
4269
+ const { data } = await res.json();
4270
+ if (isJsonMode()) {
4271
+ outputSuccess(data);
4272
+ return;
4273
+ }
4274
+ console.log(source_default.bold(`
4275
+ ${data.name} ${source_default.dim(`(${data.slug})`)}
4276
+ `));
4277
+ const fields = [
4278
+ ["ID", data.id],
4279
+ ["Type", data.type],
4280
+ ["Category", data.category],
4281
+ ["Version", data.version],
4282
+ ["Maintainer", data.maintainer],
4283
+ ["Visibility", data.visibility],
4284
+ ["Review Status", data.review_status],
4285
+ ["Active", data.is_active ? "yes" : "no"],
4286
+ ["Worker URL", data.worker_url],
4287
+ ["Namespace", data.worker_namespace],
4288
+ ["Created", data.created_at ? new Date(data.created_at).toLocaleString() : undefined],
4289
+ ["Updated", data.updated_at ? new Date(data.updated_at).toLocaleString() : undefined],
4290
+ ["Published", data.published_at ? new Date(data.published_at).toLocaleString() : undefined]
4291
+ ];
4292
+ for (const [label, value] of fields) {
4293
+ if (value !== undefined && value !== null) {
4294
+ console.log(` ${source_default.dim(label.padEnd(16))} ${value}`);
4295
+ }
4296
+ }
4297
+ console.log();
4298
+ } catch (err) {
4299
+ outputError("network_error", err.message);
4300
+ }
4301
+ }
4302
+ var init_inspect = __esm(() => {
4303
+ init_source();
4304
+ init_config();
4305
+ init_auth();
4306
+ init_output();
4307
+ init_link();
4308
+ });
4309
+
3628
4310
  // src/env-cmd.ts
3629
4311
  var exports_env_cmd = {};
3630
4312
  __export(exports_env_cmd, {
3631
4313
  envSet: () => envSet,
3632
4314
  envRemove: () => envRemove,
4315
+ envPull: () => envPull,
3633
4316
  envList: () => envList
3634
4317
  });
3635
- function requireAuth() {
4318
+ import { writeFileSync as writeFileSync5 } from "fs";
4319
+ import { resolve } from "path";
4320
+ function requireAuth2() {
3636
4321
  const token = getAuthToken();
3637
4322
  if (!token) {
3638
4323
  console.error(source_default.red("Not authenticated. Run"), source_default.cyan("bagdock login"), source_default.red("or set BAGDOCK_API_KEY."));
@@ -3649,7 +4334,7 @@ function requireConfig() {
3649
4334
  return config;
3650
4335
  }
3651
4336
  async function envList() {
3652
- const token = requireAuth();
4337
+ const token = requireAuth2();
3653
4338
  const config = requireConfig();
3654
4339
  try {
3655
4340
  const res = await fetch(`${API_BASE}/v1/developer/apps/${config.slug}/env`, {
@@ -3678,7 +4363,7 @@ Environment variables for ${config.slug}:
3678
4363
  }
3679
4364
  }
3680
4365
  async function envSet(key, value) {
3681
- const token = requireAuth();
4366
+ const token = requireAuth2();
3682
4367
  const config = requireConfig();
3683
4368
  try {
3684
4369
  const res = await fetch(`${API_BASE}/v1/developer/apps/${config.slug}/env`, {
@@ -3701,7 +4386,7 @@ async function envSet(key, value) {
3701
4386
  }
3702
4387
  }
3703
4388
  async function envRemove(key) {
3704
- const token = requireAuth();
4389
+ const token = requireAuth2();
3705
4390
  const config = requireConfig();
3706
4391
  try {
3707
4392
  const res = await fetch(`${API_BASE}/v1/developer/apps/${config.slug}/env/${key}`, {
@@ -3718,10 +4403,52 @@ async function envRemove(key) {
3718
4403
  process.exit(1);
3719
4404
  }
3720
4405
  }
4406
+ async function envPull(file) {
4407
+ const token = requireAuth2();
4408
+ const slug = requireSlug();
4409
+ const target = resolve(file ?? ".env.local");
4410
+ status(`Pulling env vars for ${slug}...`);
4411
+ try {
4412
+ const res = await fetch(`${API_BASE}/v1/developer/apps/${slug}/env`, {
4413
+ headers: { Authorization: `Bearer ${token}` }
4414
+ });
4415
+ if (!res.ok) {
4416
+ outputError("api_error", `Failed to pull env vars (${res.status})`);
4417
+ process.exit(1);
4418
+ }
4419
+ const { data } = await res.json();
4420
+ if (isJsonMode()) {
4421
+ outputSuccess({ file: target, keys: data.map((v) => v.key) });
4422
+ return;
4423
+ }
4424
+ if (!data?.length) {
4425
+ console.log(source_default.yellow("No environment variables set."));
4426
+ return;
4427
+ }
4428
+ const lines = [
4429
+ `# Pulled from Bagdock — ${slug}`,
4430
+ `# ${new Date().toISOString()}`,
4431
+ `# Values are placeholders — the API does not expose secrets.`,
4432
+ `# Fill in real values for local development.`,
4433
+ "",
4434
+ ...data.map((v) => `${v.key}=`),
4435
+ ""
4436
+ ];
4437
+ writeFileSync5(target, lines.join(`
4438
+ `));
4439
+ console.log(source_default.green(`Wrote ${data.length} keys to ${target}`));
4440
+ console.log(source_default.yellow("Note:"), "Values are empty — fill them in for local dev.");
4441
+ } catch (err) {
4442
+ console.error(source_default.red("Failed:"), err.message);
4443
+ process.exit(1);
4444
+ }
4445
+ }
3721
4446
  var init_env_cmd = __esm(() => {
3722
4447
  init_source();
3723
4448
  init_config();
3724
4449
  init_auth();
4450
+ init_output();
4451
+ init_link();
3725
4452
  });
3726
4453
 
3727
4454
  // src/keys.ts
@@ -3940,7 +4667,7 @@ async function apiRequest3(method, path2) {
3940
4667
  headers: { Authorization: `Bearer ${token}` }
3941
4668
  });
3942
4669
  }
3943
- function resolveSlug(slug) {
4670
+ function resolveSlug2(slug) {
3944
4671
  if (slug)
3945
4672
  return slug;
3946
4673
  const config = loadBagdockJson(process.cwd());
@@ -3950,7 +4677,7 @@ function resolveSlug(slug) {
3950
4677
  return "";
3951
4678
  }
3952
4679
  async function logsList(opts) {
3953
- const slug = resolveSlug(opts.app);
4680
+ const slug = resolveSlug2(opts.app);
3954
4681
  const limit = opts.limit || "50";
3955
4682
  status(`Fetching logs for ${slug}...`);
3956
4683
  const res = await apiRequest3("GET", `/api/v1/developer/apps/${slug}/logs?limit=${limit}`);
@@ -3981,7 +4708,7 @@ async function logsList(opts) {
3981
4708
  }
3982
4709
  }
3983
4710
  async function logsGet(id, opts) {
3984
- const slug = resolveSlug(opts.app);
4711
+ const slug = resolveSlug2(opts.app);
3985
4712
  status(`Fetching log entry ${id}...`);
3986
4713
  const res = await apiRequest3("GET", `/api/v1/developer/apps/${slug}/logs/${id}`);
3987
4714
  if (!res.ok) {
@@ -3996,7 +4723,7 @@ async function logsGet(id, opts) {
3996
4723
  }
3997
4724
  }
3998
4725
  async function logsTail(opts) {
3999
- const slug = resolveSlug(opts.app);
4726
+ const slug = resolveSlug2(opts.app);
4000
4727
  if (isJsonMode()) {
4001
4728
  outputError("UNSUPPORTED", "Log tailing is not supported in JSON mode. Use `logs list` instead.");
4002
4729
  }
@@ -4304,16 +5031,26 @@ function toPascalCase(s) {
4304
5031
 
4305
5032
  // bin/bagdock.ts
4306
5033
  init_output();
5034
+ init_config();
4307
5035
  var program2 = new Command;
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) => {
5036
+ program2.name("bagdock").description("Bagdock developer CLI — built for humans, AI agents, and CI/CD pipelines").version("0.4.0").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").option("-p, --profile <name>", "Profile to use (overrides BAGDOCK_PROFILE)").hook("preAction", (_thisCommand, actionCommand) => {
4309
5037
  const opts = program2.opts();
4310
5038
  setOutputMode({ json: opts.json, quiet: opts.quiet });
4311
5039
  if (opts.apiKey)
4312
5040
  setApiKeyOverride(opts.apiKey);
5041
+ if (opts.profile)
5042
+ setProfileOverride(opts.profile);
4313
5043
  });
4314
5044
  program2.command("login").description("Authenticate with Bagdock (opens browser)").action(login);
4315
5045
  program2.command("logout").description("Clear stored credentials").action(logout);
4316
5046
  program2.command("whoami").description("Show current authenticated user").action(whoami);
5047
+ var authCmd = program2.command("auth").description("Manage authentication profiles");
5048
+ authCmd.command("list").description("List all stored profiles").action(authList);
5049
+ authCmd.command("switch [name]").description("Switch active profile").action(async (name) => authSwitch(name));
5050
+ program2.command("doctor").description("Run environment diagnostics").action(async () => {
5051
+ const { doctor: doctor2 } = await Promise.resolve().then(() => (init_doctor(), exports_doctor));
5052
+ await doctor2();
5053
+ });
4317
5054
  program2.command("init [dir]").description("Scaffold a new project with bagdock.json").option("-t, --type <type>", "Project type (edge, app)").option("-k, --kind <kind>", "Project kind (adapter, comms, webhook, ui-extension, microfrontend)").option("-c, --category <category>", "Category").option("-s, --slug <slug>", "Unique project slug").option("-n, --name <name>", "Display name").action((dir, opts) => init(dir ?? ".", opts));
4318
5055
  program2.command("dev").description("Start local dev server").option("-p, --port <port>", "Local dev port", "8787").action(async (opts) => {
4319
5056
  const { dev: dev2 } = await Promise.resolve().then(() => (init_dev(), exports_dev));
@@ -4327,6 +5064,35 @@ program2.command("submit").description("Submit app for Bagdock marketplace revie
4327
5064
  const { submit: submit2 } = await Promise.resolve().then(() => (init_submit(), exports_submit));
4328
5065
  await submit2();
4329
5066
  });
5067
+ program2.command("validate").description("Run local pre-submission checks on bagdock.json and bundle").action(async () => {
5068
+ const { validate: validate2 } = await Promise.resolve().then(() => (init_validate(), exports_validate));
5069
+ await validate2();
5070
+ });
5071
+ var subCmd = program2.command("submission").description("Track marketplace submission status");
5072
+ subCmd.command("list").description("List submission history for the current app").option("--app <slug>", "App slug (defaults to bagdock.json or linked project)").action(async (opts) => {
5073
+ const { submissionList: submissionList2 } = await Promise.resolve().then(() => (init_submission(), exports_submission));
5074
+ await submissionList2(opts);
5075
+ });
5076
+ subCmd.command("status <id>").description("Fetch detailed review state for a submission").option("--app <slug>", "App slug").action(async (id, opts) => {
5077
+ const { submissionStatus: submissionStatus2 } = await Promise.resolve().then(() => (init_submission(), exports_submission));
5078
+ await submissionStatus2(id, opts);
5079
+ });
5080
+ subCmd.command("withdraw <id>").description("Cancel a pending submission before approval").option("--app <slug>", "App slug").action(async (id, opts) => {
5081
+ const { submissionWithdraw: submissionWithdraw2 } = await Promise.resolve().then(() => (init_submission(), exports_submission));
5082
+ await submissionWithdraw2(id, opts);
5083
+ });
5084
+ program2.command("open [slug]").description("Open project in the Bagdock dashboard").action(async (slug) => {
5085
+ const { open: open3 } = await Promise.resolve().then(() => (init_open2(), exports_open2));
5086
+ await open3(slug);
5087
+ });
5088
+ program2.command("inspect [slug]").description("Show deployment details and status for an app").action(async (slug) => {
5089
+ const { inspect: inspect2 } = await Promise.resolve().then(() => (init_inspect(), exports_inspect));
5090
+ await inspect2(slug);
5091
+ });
5092
+ program2.command("link").description("Link current directory to a Bagdock app or edge").option("--slug <slug>", "Project slug (required in non-interactive mode)").action(async (opts) => {
5093
+ const { link: link2 } = await Promise.resolve().then(() => (init_link(), exports_link));
5094
+ await link2(opts);
5095
+ });
4330
5096
  var envCmd = program2.command("env").description("Manage app environment variables");
4331
5097
  envCmd.command("list").description("List environment variables for this app").action(async () => {
4332
5098
  const { envList: envList2 } = await Promise.resolve().then(() => (init_env_cmd(), exports_env_cmd));
@@ -4340,6 +5106,10 @@ envCmd.command("remove <key>").description("Remove an environment variable").act
4340
5106
  const { envRemove: envRemove2 } = await Promise.resolve().then(() => (init_env_cmd(), exports_env_cmd));
4341
5107
  await envRemove2(key);
4342
5108
  });
5109
+ envCmd.command("pull [file]").description("Pull remote env var keys to a local .env file").action(async (file) => {
5110
+ const { envPull: envPull2 } = await Promise.resolve().then(() => (init_env_cmd(), exports_env_cmd));
5111
+ await envPull2(file);
5112
+ });
4343
5113
  var keysCmd = program2.command("keys").description("Manage operator API keys");
4344
5114
  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
5115
  const { keysCreate: keysCreate2 } = await Promise.resolve().then(() => (init_keys(), exports_keys));