@bagdock/cli 0.1.4 → 0.3.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() {
@@ -3158,13 +3267,367 @@ var init_open = __esm(() => {
3158
3267
  open_default = open;
3159
3268
  });
3160
3269
 
3270
+ // src/auth.ts
3271
+ function setApiKeyOverride(key) {
3272
+ apiKeyOverride = key;
3273
+ }
3274
+ function getAuthToken() {
3275
+ if (apiKeyOverride)
3276
+ return apiKeyOverride;
3277
+ if (process.env.BAGDOCK_API_KEY)
3278
+ return process.env.BAGDOCK_API_KEY;
3279
+ if (process.env.BAGDOCK_TOKEN)
3280
+ return process.env.BAGDOCK_TOKEN;
3281
+ const creds = loadCredentials();
3282
+ return creds?.accessToken ?? null;
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
+ }
3296
+ async function login() {
3297
+ const existing = loadCredentials();
3298
+ if (existing?.accessToken && existing.expiresAt && existing.expiresAt > Date.now()) {
3299
+ console.log(source_default.green("Already logged in as"), source_default.bold(existing.email ?? "unknown"));
3300
+ console.log("Run", source_default.cyan("bagdock logout"), "to sign out first.");
3301
+ return;
3302
+ }
3303
+ console.log(source_default.cyan(`
3304
+ Requesting device authorization...
3305
+ `));
3306
+ const deviceRes = await fetch(`${API_BASE}/oauth2/device/authorize`, {
3307
+ method: "POST",
3308
+ headers: { "Content-Type": "application/json" },
3309
+ body: JSON.stringify({ client_id: CLIENT_ID, scope: "developer:read developer:write" })
3310
+ });
3311
+ if (!deviceRes.ok) {
3312
+ const err = await deviceRes.text();
3313
+ console.log(source_default.red("Failed to start login:"), err);
3314
+ process.exit(1);
3315
+ }
3316
+ const device = await deviceRes.json();
3317
+ const pollInterval = (device.interval ?? 5) * 1000;
3318
+ console.log(` Visit ${source_default.bold(device.verification_uri)} and enter code:
3319
+ `);
3320
+ console.log(` ${source_default.bold.cyan(device.user_code)}
3321
+ `);
3322
+ const open2 = (await Promise.resolve().then(() => (init_open(), exports_open))).default;
3323
+ await open2(device.verification_uri_complete).catch(() => {});
3324
+ console.log(source_default.dim(" Waiting for authorization..."));
3325
+ const startedAt = Date.now();
3326
+ while (Date.now() - startedAt < MAX_POLL_DURATION_MS) {
3327
+ await sleep(pollInterval);
3328
+ const tokenRes = await fetch(`${API_BASE}/oauth2/token`, {
3329
+ method: "POST",
3330
+ headers: { "Content-Type": "application/json" },
3331
+ body: JSON.stringify({
3332
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
3333
+ client_id: CLIENT_ID,
3334
+ device_code: device.device_code
3335
+ })
3336
+ });
3337
+ if (tokenRes.ok) {
3338
+ const tokens = await tokenRes.json();
3339
+ saveCredentials({
3340
+ accessToken: tokens.access_token,
3341
+ refreshToken: tokens.refresh_token,
3342
+ expiresAt: tokens.expires_in ? Date.now() + tokens.expires_in * 1000 : undefined,
3343
+ email: tokens.email,
3344
+ operatorId: tokens.operator_id
3345
+ });
3346
+ console.log(source_default.green(`
3347
+ Logged in successfully!`));
3348
+ if (tokens.email)
3349
+ console.log(" Email:", source_default.bold(tokens.email));
3350
+ if (tokens.operator_id)
3351
+ console.log(" Operator:", source_default.bold(tokens.operator_id));
3352
+ console.log(" Profile:", source_default.bold(getActiveProfileName()));
3353
+ return;
3354
+ }
3355
+ const error = await tokenRes.json().catch(() => ({ error: "unknown" }));
3356
+ if (error.error === "authorization_pending")
3357
+ continue;
3358
+ if (error.error === "slow_down") {
3359
+ await sleep(pollInterval);
3360
+ continue;
3361
+ }
3362
+ if (error.error === "expired_token") {
3363
+ console.log(source_default.red(`
3364
+ Device code expired. Please try again.`));
3365
+ process.exit(1);
3366
+ }
3367
+ if (error.error === "access_denied") {
3368
+ console.log(source_default.red(`
3369
+ Authorization denied.`));
3370
+ process.exit(1);
3371
+ }
3372
+ console.log(source_default.red(`
3373
+ Login failed:`), error.error_description ?? error.error);
3374
+ process.exit(1);
3375
+ }
3376
+ console.log(source_default.red(`
3377
+ Login timed out. Please try again.`));
3378
+ process.exit(1);
3379
+ }
3380
+ async function logout() {
3381
+ clearCredentials();
3382
+ console.log(source_default.green(`Logged out of profile "${getActiveProfileName()}".`));
3383
+ }
3384
+ async function whoami() {
3385
+ const token = getAuthToken();
3386
+ if (!token) {
3387
+ console.log(source_default.yellow("Not logged in."), "Run", source_default.cyan("bagdock login"));
3388
+ process.exit(1);
3389
+ }
3390
+ try {
3391
+ const res = await fetch(`${API_BASE}/v1/auth/me`, {
3392
+ headers: { Authorization: `Bearer ${token}` }
3393
+ });
3394
+ if (!res.ok) {
3395
+ console.log(source_default.red("Session expired or invalid."), "Run", source_default.cyan("bagdock login"));
3396
+ process.exit(1);
3397
+ }
3398
+ const user = await res.json();
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
+ }
3409
+ } catch (err) {
3410
+ console.log(source_default.red("Failed to reach API:"), err.message);
3411
+ process.exit(1);
3412
+ }
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
+ }
3485
+ function sleep(ms) {
3486
+ return new Promise((resolve) => setTimeout(resolve, ms));
3487
+ }
3488
+ var apiKeyOverride, CLIENT_ID = "bagdock-cli", MAX_POLL_DURATION_MS = 300000;
3489
+ var init_auth = __esm(() => {
3490
+ init_config();
3491
+ init_source();
3492
+ init_output();
3493
+ });
3494
+
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";
3516
+ }
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." };
3538
+ }
3539
+ return { name: "API Key", status: "pass", message: `${maskKey(token)} (source: ${source})` };
3540
+ }
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})` };
3557
+ }
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(", ")}` };
3581
+ }
3582
+ async function doctor() {
3583
+ const checks = [];
3584
+ if (isJsonMode()) {
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;
3592
+ }
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}
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)
3613
+ process.exit(1);
3614
+ }
3615
+ var ICON;
3616
+ var init_doctor = __esm(() => {
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("✘") };
3622
+ });
3623
+
3161
3624
  // src/dev.ts
3162
3625
  var exports_dev = {};
3163
3626
  __export(exports_dev, {
3164
3627
  dev: () => dev
3165
3628
  });
3166
- import { existsSync as existsSync3, writeFileSync as writeFileSync3 } from "fs";
3167
- import { join as join3 } from "path";
3629
+ import { existsSync as existsSync4, writeFileSync as writeFileSync3 } from "fs";
3630
+ import { join as join4 } from "path";
3168
3631
  import { spawn } from "child_process";
3169
3632
  async function dev(opts) {
3170
3633
  const cwd = process.cwd();
@@ -3175,11 +3638,11 @@ async function dev(opts) {
3175
3638
  }
3176
3639
  const port = opts.port ?? "8787";
3177
3640
  const wranglerToml = generateWranglerToml(config, port);
3178
- const wranglerPath = join3(cwd, "wrangler.toml");
3641
+ const wranglerPath = join4(cwd, "wrangler.toml");
3179
3642
  writeFileSync3(wranglerPath, wranglerToml);
3180
3643
  console.log(source_default.green("Generated"), source_default.cyan("wrangler.toml"), source_default.green("from bagdock.json"));
3181
- const devVarsPath = join3(cwd, ".dev.vars");
3182
- if (!existsSync3(devVarsPath) && config.env) {
3644
+ const devVarsPath = join4(cwd, ".dev.vars");
3645
+ if (!existsSync4(devVarsPath) && config.env) {
3183
3646
  const hint = Object.entries(config.env).map(([key, meta]) => `# ${meta.description ?? key}${meta.required ? " (required)" : ""}
3184
3647
  ${key}=`).join(`
3185
3648
  `);
@@ -3240,8 +3703,8 @@ var exports_deploy = {};
3240
3703
  __export(exports_deploy, {
3241
3704
  deploy: () => deploy
3242
3705
  });
3243
- import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
3244
- import { join as join4 } from "path";
3706
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
3707
+ import { join as join5 } from "path";
3245
3708
  import { execSync } from "child_process";
3246
3709
  import { createInterface } from "readline";
3247
3710
  async function deploy(opts) {
@@ -3251,9 +3714,9 @@ async function deploy(opts) {
3251
3714
  console.error(source_default.red("No bagdock.json found. Run"), source_default.cyan("bagdock init"), source_default.red("first."));
3252
3715
  process.exit(1);
3253
3716
  }
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."));
3717
+ const token = getAuthToken();
3718
+ if (!token) {
3719
+ console.error(source_default.red("Not authenticated. Run"), source_default.cyan("bagdock login"), source_default.red("or set BAGDOCK_API_KEY."));
3257
3720
  process.exit(1);
3258
3721
  }
3259
3722
  let environment = opts.env ?? "staging";
@@ -3282,8 +3745,8 @@ async function deploy(opts) {
3282
3745
  console.log(source_default.cyan(`
3283
3746
  Deploying ${config.slug}@${config.version} → ${envLabel}
3284
3747
  `));
3285
- const outDir = join4(cwd, ".bagdock");
3286
- const outFile = join4(outDir, "worker.mjs");
3748
+ const outDir = join5(cwd, ".bagdock");
3749
+ const outFile = join5(outDir, "worker.mjs");
3287
3750
  console.log(source_default.dim(" Building worker bundle..."));
3288
3751
  execSync(`mkdir -p ${outDir}`, { cwd });
3289
3752
  try {
@@ -3299,11 +3762,11 @@ Deploying ${config.slug}@${config.version} → ${envLabel}
3299
3762
  process.exit(1);
3300
3763
  }
3301
3764
  }
3302
- if (!existsSync4(outFile)) {
3765
+ if (!existsSync5(outFile)) {
3303
3766
  console.error(source_default.red(" Build output not found at"), outFile);
3304
3767
  process.exit(1);
3305
3768
  }
3306
- const scriptContent = readFileSync2(outFile);
3769
+ const scriptContent = readFileSync3(outFile);
3307
3770
  const sizeKb = (scriptContent.length / 1024).toFixed(1);
3308
3771
  console.log(source_default.dim(` Bundle size: ${sizeKb} KB`));
3309
3772
  console.log(source_default.dim(" Uploading to Bagdock platform..."));
@@ -3318,7 +3781,7 @@ Deploying ${config.slug}@${config.version} → ${envLabel}
3318
3781
  const res = await fetch(`${API_BASE}/api/v1/developer/apps/${config.slug}/deploy`, {
3319
3782
  method: "POST",
3320
3783
  headers: {
3321
- Authorization: `Bearer ${creds.accessToken}`
3784
+ Authorization: `Bearer ${token}`
3322
3785
  },
3323
3786
  body: formData
3324
3787
  });
@@ -3374,6 +3837,7 @@ function confirm(prompt) {
3374
3837
  var init_deploy = __esm(() => {
3375
3838
  init_source();
3376
3839
  init_config();
3840
+ init_auth();
3377
3841
  });
3378
3842
 
3379
3843
  // src/submit.ts
@@ -3387,9 +3851,9 @@ async function submit() {
3387
3851
  console.error(source_default.red("No bagdock.json found. Run"), source_default.cyan("bagdock init"), source_default.red("first."));
3388
3852
  process.exit(1);
3389
3853
  }
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."));
3854
+ const token = getAuthToken();
3855
+ if (!token) {
3856
+ console.error(source_default.red("Not authenticated. Run"), source_default.cyan("bagdock login"), source_default.red("or set BAGDOCK_API_KEY."));
3393
3857
  process.exit(1);
3394
3858
  }
3395
3859
  console.log(source_default.cyan(`
@@ -3399,7 +3863,7 @@ Submitting ${source_default.bold(config.slug)} for marketplace review...
3399
3863
  const res = await fetch(`${API_BASE}/api/v1/developer/apps/${config.slug}/submit`, {
3400
3864
  method: "POST",
3401
3865
  headers: {
3402
- Authorization: `Bearer ${creds.accessToken}`,
3866
+ Authorization: `Bearer ${token}`,
3403
3867
  "Content-Type": "application/json"
3404
3868
  }
3405
3869
  });
@@ -3427,6 +3891,7 @@ Submitting ${source_default.bold(config.slug)} for marketplace review...
3427
3891
  var init_submit = __esm(() => {
3428
3892
  init_source();
3429
3893
  init_config();
3894
+ init_auth();
3430
3895
  });
3431
3896
 
3432
3897
  // src/env-cmd.ts
@@ -3437,12 +3902,12 @@ __export(exports_env_cmd, {
3437
3902
  envList: () => envList
3438
3903
  });
3439
3904
  function requireAuth() {
3440
- const creds = loadCredentials();
3441
- if (!creds?.accessToken) {
3442
- console.error(source_default.red("Not authenticated. Run"), source_default.cyan("bagdock login"));
3905
+ const token = getAuthToken();
3906
+ if (!token) {
3907
+ console.error(source_default.red("Not authenticated. Run"), source_default.cyan("bagdock login"), source_default.red("or set BAGDOCK_API_KEY."));
3443
3908
  process.exit(1);
3444
3909
  }
3445
- return creds;
3910
+ return token;
3446
3911
  }
3447
3912
  function requireConfig() {
3448
3913
  const config = loadBagdockJson(process.cwd());
@@ -3453,11 +3918,11 @@ function requireConfig() {
3453
3918
  return config;
3454
3919
  }
3455
3920
  async function envList() {
3456
- const creds = requireAuth();
3921
+ const token = requireAuth();
3457
3922
  const config = requireConfig();
3458
3923
  try {
3459
3924
  const res = await fetch(`${API_BASE}/v1/developer/apps/${config.slug}/env`, {
3460
- headers: { Authorization: `Bearer ${creds.accessToken}` }
3925
+ headers: { Authorization: `Bearer ${token}` }
3461
3926
  });
3462
3927
  if (!res.ok) {
3463
3928
  console.error(source_default.red(`Failed to list env vars (${res.status})`));
@@ -3482,14 +3947,14 @@ Environment variables for ${config.slug}:
3482
3947
  }
3483
3948
  }
3484
3949
  async function envSet(key, value) {
3485
- const creds = requireAuth();
3950
+ const token = requireAuth();
3486
3951
  const config = requireConfig();
3487
3952
  try {
3488
3953
  const res = await fetch(`${API_BASE}/v1/developer/apps/${config.slug}/env`, {
3489
3954
  method: "PUT",
3490
3955
  headers: {
3491
3956
  "Content-Type": "application/json",
3492
- Authorization: `Bearer ${creds.accessToken}`
3957
+ Authorization: `Bearer ${token}`
3493
3958
  },
3494
3959
  body: JSON.stringify({ key, value })
3495
3960
  });
@@ -3505,12 +3970,12 @@ async function envSet(key, value) {
3505
3970
  }
3506
3971
  }
3507
3972
  async function envRemove(key) {
3508
- const creds = requireAuth();
3973
+ const token = requireAuth();
3509
3974
  const config = requireConfig();
3510
3975
  try {
3511
3976
  const res = await fetch(`${API_BASE}/v1/developer/apps/${config.slug}/env/${key}`, {
3512
3977
  method: "DELETE",
3513
- headers: { Authorization: `Bearer ${creds.accessToken}` }
3978
+ headers: { Authorization: `Bearer ${token}` }
3514
3979
  });
3515
3980
  if (!res.ok) {
3516
3981
  console.error(source_default.red(`Failed to remove ${key} (${res.status})`));
@@ -3525,146 +3990,339 @@ async function envRemove(key) {
3525
3990
  var init_env_cmd = __esm(() => {
3526
3991
  init_source();
3527
3992
  init_config();
3993
+ init_auth();
3528
3994
  });
3529
3995
 
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;
3996
+ // src/keys.ts
3997
+ var exports_keys = {};
3998
+ __export(exports_keys, {
3999
+ keysList: () => keysList,
4000
+ keysDelete: () => keysDelete,
4001
+ keysCreate: () => keysCreate
4002
+ });
4003
+ async function apiRequest(method, path2, body) {
4004
+ const token = getAuthToken();
4005
+ if (!token) {
4006
+ outputError("UNAUTHENTICATED", "Not logged in. Run `bagdock login` or set BAGDOCK_API_KEY.");
3557
4007
  }
3558
- console.log(source_default.cyan(`
3559
- Requesting device authorization...
4008
+ const headers = { Authorization: `Bearer ${token}` };
4009
+ const init2 = { method, headers };
4010
+ if (body) {
4011
+ headers["Content-Type"] = "application/json";
4012
+ init2.body = JSON.stringify(body);
4013
+ }
4014
+ const res = await fetch(`${API_BASE}${path2}`, init2);
4015
+ return res;
4016
+ }
4017
+ async function keysCreate(opts) {
4018
+ status("Creating API key...");
4019
+ const res = await apiRequest("POST", "/api/v1/operator/api-keys", {
4020
+ name: opts.name,
4021
+ key_type: opts.type || "secret",
4022
+ key_category: opts.category || "standard",
4023
+ environment: opts.environment || "live",
4024
+ scopes: opts.scopes || []
4025
+ });
4026
+ if (!res.ok) {
4027
+ const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
4028
+ outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
4029
+ }
4030
+ const data = await res.json();
4031
+ if (isJsonMode()) {
4032
+ outputSuccess(data);
4033
+ } else {
4034
+ console.log(source_default.green(`
4035
+ API key created successfully!
3560
4036
  `));
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" })
4037
+ console.log(` ${source_default.bold("Name:")} ${data.name}`);
4038
+ console.log(` ${source_default.bold("Key:")} ${source_default.yellow(data.key)}`);
4039
+ console.log(` ${source_default.bold("ID:")} ${data.id}`);
4040
+ console.log(` ${source_default.bold("Environment:")} ${data.environment}`);
4041
+ console.log(` ${source_default.bold("Type:")} ${data.key_type}`);
4042
+ console.log(` ${source_default.bold("Category:")} ${data.key_category}`);
4043
+ if (data.scopes?.length) {
4044
+ console.log(` ${source_default.bold("Scopes:")} ${data.scopes.join(", ")}`);
4045
+ }
4046
+ console.log();
4047
+ console.log(source_default.yellow(" Save this key — it will not be shown again."));
4048
+ console.log();
4049
+ }
4050
+ }
4051
+ async function keysList(opts) {
4052
+ status("Fetching API keys...");
4053
+ let path2 = "/api/v1/operator/api-keys";
4054
+ if (opts.environment)
4055
+ path2 += `?environment=${opts.environment}`;
4056
+ const res = await apiRequest("GET", path2);
4057
+ if (!res.ok) {
4058
+ const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
4059
+ outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
4060
+ }
4061
+ const result = await res.json();
4062
+ if (isJsonMode()) {
4063
+ outputList("api_key", result.data, result.has_more);
4064
+ } else {
4065
+ if (!result.data.length) {
4066
+ console.log(source_default.yellow(`
4067
+ No API keys found.
4068
+ `));
4069
+ return;
4070
+ }
4071
+ console.log();
4072
+ for (const key of result.data) {
4073
+ const usedAt = key.last_used_at ? new Date(key.last_used_at).toLocaleDateString() : "never";
4074
+ console.log(` ${source_default.bold(key.name)}`);
4075
+ console.log(` ${source_default.dim("Prefix:")} ${key.key_prefix}... ${source_default.dim("Env:")} ${key.environment} ${source_default.dim("Last used:")} ${usedAt}`);
4076
+ console.log(` ${source_default.dim("ID:")} ${key.id} ${source_default.dim("Category:")} ${key.key_category}`);
4077
+ console.log();
4078
+ }
4079
+ }
4080
+ }
4081
+ async function keysDelete(id, opts) {
4082
+ if (!opts.yes && !process.stdout.isTTY) {
4083
+ outputError("CONFIRMATION_REQUIRED", "Pass --yes to confirm deletion in non-interactive mode.");
4084
+ }
4085
+ status(`Revoking API key ${id}...`);
4086
+ const res = await apiRequest("DELETE", `/api/v1/operator/api-keys/${id}`, {
4087
+ reason: opts.reason
3565
4088
  });
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);
4089
+ if (!res.ok) {
4090
+ const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
4091
+ outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
3570
4092
  }
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:
4093
+ if (isJsonMode()) {
4094
+ outputSuccess({ id, status: "revoked" });
4095
+ } else {
4096
+ success(`
4097
+ API key ${id} revoked.
3574
4098
  `);
3575
- console.log(` ${source_default.bold.cyan(device.user_code)}
4099
+ }
4100
+ }
4101
+ var init_keys = __esm(() => {
4102
+ init_source();
4103
+ init_config();
4104
+ init_auth();
4105
+ init_output();
4106
+ });
4107
+
4108
+ // src/apps.ts
4109
+ var exports_apps = {};
4110
+ __export(exports_apps, {
4111
+ appsList: () => appsList,
4112
+ appsGet: () => appsGet
4113
+ });
4114
+ async function apiRequest2(method, path2) {
4115
+ const token = getAuthToken();
4116
+ if (!token) {
4117
+ outputError("UNAUTHENTICATED", "Not logged in. Run `bagdock login` or set BAGDOCK_API_KEY.");
4118
+ }
4119
+ return fetch(`${API_BASE}${path2}`, {
4120
+ method,
4121
+ headers: { Authorization: `Bearer ${token}` }
4122
+ });
4123
+ }
4124
+ async function appsList() {
4125
+ status("Fetching apps...");
4126
+ const res = await apiRequest2("GET", "/api/v1/developer/apps");
4127
+ if (!res.ok) {
4128
+ const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
4129
+ outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
4130
+ }
4131
+ const result = await res.json();
4132
+ if (isJsonMode()) {
4133
+ outputList("app", result.data, false);
4134
+ } else {
4135
+ if (!result.data.length) {
4136
+ console.log(source_default.yellow(`
4137
+ No apps found. Create one with`), source_default.cyan("bagdock init"), `
3576
4138
  `);
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
4139
  return;
3608
4140
  }
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);
4141
+ console.log();
4142
+ for (const app of result.data) {
4143
+ const status2 = app.is_active ? source_default.green("active") : source_default.dim("inactive");
4144
+ const review = app.review_status ? source_default.dim(`[${app.review_status}]`) : "";
4145
+ console.log(` ${source_default.bold(app.name)} ${source_default.dim(`(${app.slug})`)} ${status2} ${review}`);
4146
+ console.log(` ${source_default.dim("Type:")} ${app.type}/${app.category} ${source_default.dim("Version:")} ${app.version}`);
4147
+ if (app.worker_url) {
4148
+ console.log(` ${source_default.dim("URL:")} ${app.worker_url}`);
4149
+ }
4150
+ console.log();
3626
4151
  }
3627
- console.log(source_default.red(`
3628
- Login failed:`), error.error_description ?? error.error);
3629
- process.exit(1);
3630
4152
  }
3631
- console.log(source_default.red(`
3632
- Login timed out. Please try again.`));
3633
- process.exit(1);
3634
4153
  }
3635
- async function logout() {
3636
- clearCredentials();
3637
- console.log(source_default.green("Logged out."));
4154
+ async function appsGet(slug) {
4155
+ status(`Fetching app ${slug}...`);
4156
+ const res = await apiRequest2("GET", `/api/v1/developer/apps/${slug}`);
4157
+ if (!res.ok) {
4158
+ if (res.status === 404) {
4159
+ outputError("NOT_FOUND", `App "${slug}" not found`);
4160
+ }
4161
+ const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
4162
+ outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
4163
+ }
4164
+ const result = await res.json();
4165
+ const app = result.data;
4166
+ if (isJsonMode()) {
4167
+ outputSuccess(app);
4168
+ } else {
4169
+ console.log();
4170
+ console.log(` ${source_default.bold(app.name)} ${source_default.dim(`(${app.slug})`)}`);
4171
+ console.log(` ${source_default.dim("ID:")} ${app.id}`);
4172
+ console.log(` ${source_default.dim("Type:")} ${app.type}`);
4173
+ console.log(` ${source_default.dim("Category:")} ${app.category}`);
4174
+ console.log(` ${source_default.dim("Kind:")} ${app.kind ?? "-"}`);
4175
+ console.log(` ${source_default.dim("Version:")} ${app.version}`);
4176
+ console.log(` ${source_default.dim("Visibility:")} ${app.visibility}`);
4177
+ console.log(` ${source_default.dim("Maintainer:")} ${app.maintainer}`);
4178
+ console.log(` ${source_default.dim("Review:")} ${app.review_status ?? "draft"}`);
4179
+ console.log(` ${source_default.dim("Active:")} ${app.is_active ? "yes" : "no"}`);
4180
+ if (app.worker_url) {
4181
+ console.log(` ${source_default.dim("Worker URL:")} ${app.worker_url}`);
4182
+ }
4183
+ console.log(` ${source_default.dim("Created:")} ${app.created_at}`);
4184
+ console.log(` ${source_default.dim("Updated:")} ${app.updated_at}`);
4185
+ console.log();
4186
+ }
3638
4187
  }
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);
4188
+ var init_apps = __esm(() => {
4189
+ init_source();
4190
+ init_config();
4191
+ init_auth();
4192
+ init_output();
4193
+ });
4194
+
4195
+ // src/logs.ts
4196
+ var exports_logs = {};
4197
+ __export(exports_logs, {
4198
+ logsTail: () => logsTail,
4199
+ logsList: () => logsList,
4200
+ logsGet: () => logsGet
4201
+ });
4202
+ async function apiRequest3(method, path2) {
4203
+ const token = getAuthToken();
4204
+ if (!token) {
4205
+ outputError("UNAUTHENTICATED", "Not logged in. Run `bagdock login` or set BAGDOCK_API_KEY.");
3645
4206
  }
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);
4207
+ return fetch(`${API_BASE}${path2}`, {
4208
+ method,
4209
+ headers: { Authorization: `Bearer ${token}` }
4210
+ });
4211
+ }
4212
+ function resolveSlug(slug) {
4213
+ if (slug)
4214
+ return slug;
4215
+ const config = loadBagdockJson(process.cwd());
4216
+ if (config?.slug)
4217
+ return config.slug;
4218
+ outputError("MISSING_CONFIG", "No slug specified and no bagdock.json found. Pass --app <slug> or run from a project directory.");
4219
+ return "";
4220
+ }
4221
+ async function logsList(opts) {
4222
+ const slug = resolveSlug(opts.app);
4223
+ const limit = opts.limit || "50";
4224
+ status(`Fetching logs for ${slug}...`);
4225
+ const res = await apiRequest3("GET", `/api/v1/developer/apps/${slug}/logs?limit=${limit}`);
4226
+ if (!res.ok) {
4227
+ if (res.status === 404) {
4228
+ outputError("NOT_FOUND", `App "${slug}" not found or no logs available`);
4229
+ }
4230
+ const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
4231
+ outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
4232
+ }
4233
+ const result = await res.json();
4234
+ if (isJsonMode()) {
4235
+ outputList("log_entry", result.data, false);
4236
+ } else {
4237
+ if (!result.data?.length) {
4238
+ console.log(source_default.yellow(`
4239
+ No logs found for ${slug}.
4240
+ `));
4241
+ return;
3653
4242
  }
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);
4243
+ console.log();
4244
+ for (const entry of result.data) {
4245
+ const ts = source_default.dim(new Date(entry.timestamp).toISOString());
4246
+ const level = entry.level === "error" ? source_default.red(entry.level) : source_default.dim(entry.level);
4247
+ console.log(` ${ts} ${level} ${entry.message}`);
4248
+ }
4249
+ console.log();
3663
4250
  }
3664
4251
  }
3665
- function sleep(ms) {
3666
- return new Promise((resolve) => setTimeout(resolve, ms));
4252
+ async function logsGet(id, opts) {
4253
+ const slug = resolveSlug(opts.app);
4254
+ status(`Fetching log entry ${id}...`);
4255
+ const res = await apiRequest3("GET", `/api/v1/developer/apps/${slug}/logs/${id}`);
4256
+ if (!res.ok) {
4257
+ const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
4258
+ outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
4259
+ }
4260
+ const data = await res.json();
4261
+ if (isJsonMode()) {
4262
+ outputSuccess(data);
4263
+ } else {
4264
+ console.log(JSON.stringify(data, null, 2));
4265
+ }
3667
4266
  }
4267
+ async function logsTail(opts) {
4268
+ const slug = resolveSlug(opts.app);
4269
+ if (isJsonMode()) {
4270
+ outputError("UNSUPPORTED", "Log tailing is not supported in JSON mode. Use `logs list` instead.");
4271
+ }
4272
+ console.log(source_default.cyan(`
4273
+ Tailing logs for ${slug}... (Ctrl+C to stop)
4274
+ `));
4275
+ let lastTimestamp = new Date().toISOString();
4276
+ const poll = async () => {
4277
+ try {
4278
+ const res = await apiRequest3("GET", `/api/v1/developer/apps/${slug}/logs?since=${encodeURIComponent(lastTimestamp)}&limit=100`);
4279
+ if (res.ok) {
4280
+ const result = await res.json();
4281
+ for (const entry of result.data || []) {
4282
+ const ts = source_default.dim(new Date(entry.timestamp).toISOString());
4283
+ const level = entry.level === "error" ? source_default.red(entry.level) : source_default.dim(entry.level);
4284
+ console.log(` ${ts} ${level} ${entry.message}`);
4285
+ lastTimestamp = entry.timestamp;
4286
+ }
4287
+ }
4288
+ } catch {}
4289
+ };
4290
+ const interval = setInterval(poll, 2000);
4291
+ await poll();
4292
+ process.on("SIGINT", () => {
4293
+ clearInterval(interval);
4294
+ console.log(source_default.dim(`
4295
+ Stopped tailing.
4296
+ `));
4297
+ process.exit(0);
4298
+ });
4299
+ await new Promise(() => {});
4300
+ }
4301
+ var init_logs = __esm(() => {
4302
+ init_source();
4303
+ init_config();
4304
+ init_auth();
4305
+ init_output();
4306
+ });
4307
+
4308
+ // node_modules/commander/esm.mjs
4309
+ var import__ = __toESM(require_commander(), 1);
4310
+ var {
4311
+ program,
4312
+ createCommand,
4313
+ createArgument,
4314
+ createOption,
4315
+ CommanderError,
4316
+ InvalidArgumentError,
4317
+ InvalidOptionArgumentError,
4318
+ Command,
4319
+ Argument,
4320
+ Option,
4321
+ Help
4322
+ } = import__.default;
4323
+
4324
+ // bin/bagdock.ts
4325
+ init_auth();
3668
4326
 
3669
4327
  // src/init.ts
3670
4328
  init_source();
@@ -3914,17 +4572,33 @@ function toPascalCase(s) {
3914
4572
  }
3915
4573
 
3916
4574
  // bin/bagdock.ts
4575
+ init_output();
4576
+ init_config();
3917
4577
  var program2 = new Command;
3918
- program2.name("bagdock").description("Bagdock developer CLI").version("0.1.0");
4578
+ program2.name("bagdock").description("Bagdock developer CLI — built for humans, AI agents, and CI/CD pipelines").version("0.3.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) => {
4579
+ const opts = program2.opts();
4580
+ setOutputMode({ json: opts.json, quiet: opts.quiet });
4581
+ if (opts.apiKey)
4582
+ setApiKeyOverride(opts.apiKey);
4583
+ if (opts.profile)
4584
+ setProfileOverride(opts.profile);
4585
+ });
3919
4586
  program2.command("login").description("Authenticate with Bagdock (opens browser)").action(login);
3920
4587
  program2.command("logout").description("Clear stored credentials").action(logout);
3921
4588
  program2.command("whoami").description("Show current authenticated user").action(whoami);
4589
+ var authCmd = program2.command("auth").description("Manage authentication profiles");
4590
+ authCmd.command("list").description("List all stored profiles").action(authList);
4591
+ authCmd.command("switch [name]").description("Switch active profile").action(async (name) => authSwitch(name));
4592
+ program2.command("doctor").description("Run environment diagnostics").action(async () => {
4593
+ const { doctor: doctor2 } = await Promise.resolve().then(() => (init_doctor(), exports_doctor));
4594
+ await doctor2();
4595
+ });
3922
4596
  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));
3923
4597
  program2.command("dev").description("Start local dev server").option("-p, --port <port>", "Local dev port", "8787").action(async (opts) => {
3924
4598
  const { dev: dev2 } = await Promise.resolve().then(() => (init_dev(), exports_dev));
3925
4599
  await dev2(opts);
3926
4600
  });
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) => {
4601
+ 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
4602
  const { deploy: deploy2 } = await Promise.resolve().then(() => (init_deploy(), exports_deploy));
3929
4603
  await deploy2(opts);
3930
4604
  });
@@ -3945,4 +4619,39 @@ envCmd.command("remove <key>").description("Remove an environment variable").act
3945
4619
  const { envRemove: envRemove2 } = await Promise.resolve().then(() => (init_env_cmd(), exports_env_cmd));
3946
4620
  await envRemove2(key);
3947
4621
  });
4622
+ var keysCmd = program2.command("keys").description("Manage operator API keys");
4623
+ 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) => {
4624
+ const { keysCreate: keysCreate2 } = await Promise.resolve().then(() => (init_keys(), exports_keys));
4625
+ await keysCreate2(opts);
4626
+ });
4627
+ keysCmd.command("list").description("List API keys").option("--environment <env>", "Filter by environment (live, test)").action(async (opts) => {
4628
+ const { keysList: keysList2 } = await Promise.resolve().then(() => (init_keys(), exports_keys));
4629
+ await keysList2(opts);
4630
+ });
4631
+ 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) => {
4632
+ const { keysDelete: keysDelete2 } = await Promise.resolve().then(() => (init_keys(), exports_keys));
4633
+ await keysDelete2(id, opts);
4634
+ });
4635
+ var appsCmd = program2.command("apps").description("List and inspect deployed apps and edges");
4636
+ appsCmd.command("list").description("List all apps for the authenticated operator").action(async () => {
4637
+ const { appsList: appsList2 } = await Promise.resolve().then(() => (init_apps(), exports_apps));
4638
+ await appsList2();
4639
+ });
4640
+ appsCmd.command("get <slug>").description("Get details for a specific app").action(async (slug) => {
4641
+ const { appsGet: appsGet2 } = await Promise.resolve().then(() => (init_apps(), exports_apps));
4642
+ await appsGet2(slug);
4643
+ });
4644
+ var logsCmd = program2.command("logs").description("View execution logs for deployed apps");
4645
+ 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) => {
4646
+ const { logsList: logsList2 } = await Promise.resolve().then(() => (init_logs(), exports_logs));
4647
+ await logsList2(opts);
4648
+ });
4649
+ logsCmd.command("get <id>").description("Get a specific log entry").option("--app <slug>", "App slug (defaults to bagdock.json slug)").action(async (id, opts) => {
4650
+ const { logsGet: logsGet2 } = await Promise.resolve().then(() => (init_logs(), exports_logs));
4651
+ await logsGet2(id, opts);
4652
+ });
4653
+ logsCmd.command("tail").description("Stream logs in real-time").option("--app <slug>", "App slug (defaults to bagdock.json slug)").action(async (opts) => {
4654
+ const { logsTail: logsTail2 } = await Promise.resolve().then(() => (init_logs(), exports_logs));
4655
+ await logsTail2(opts);
4656
+ });
3948
4657
  program2.parse();