@bagdock/cli 0.3.0 → 0.5.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
@@ -2088,6 +2088,12 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
2088
2088
  function setProfileOverride(name) {
2089
2089
  profileOverride = name;
2090
2090
  }
2091
+ function setEnvironmentOverride(env) {
2092
+ envOverride = env;
2093
+ }
2094
+ function getEnvironmentOverride() {
2095
+ return envOverride;
2096
+ }
2091
2097
  function ensureConfigDir() {
2092
2098
  if (!existsSync(CONFIG_DIR)) {
2093
2099
  mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
@@ -2153,6 +2159,8 @@ function listProfiles() {
2153
2159
  name,
2154
2160
  email: creds.email,
2155
2161
  operatorId: creds.operatorId,
2162
+ operatorSlug: creds.operatorSlug,
2163
+ environment: creds.environment,
2156
2164
  active: name === store.activeProfile
2157
2165
  }));
2158
2166
  }
@@ -2167,6 +2175,46 @@ function switchProfile(name) {
2167
2175
  function getActiveProfileName() {
2168
2176
  return resolveProfile();
2169
2177
  }
2178
+ function resolveLinkEnvironment() {
2179
+ try {
2180
+ const p = join(process.cwd(), ".bagdock", "link.json");
2181
+ if (!existsSync(p))
2182
+ return;
2183
+ const data = JSON.parse(readFileSync(p, "utf-8"));
2184
+ if (data.environment === "live" || data.environment === "test")
2185
+ return data.environment;
2186
+ return;
2187
+ } catch {
2188
+ return;
2189
+ }
2190
+ }
2191
+ function resolveEnvironment() {
2192
+ if (envOverride)
2193
+ return envOverride;
2194
+ const envVar = process.env.BAGDOCK_ENV;
2195
+ if (envVar === "test" || envVar === "live")
2196
+ return envVar;
2197
+ const creds = loadCredentials();
2198
+ return creds?.environment ?? "live";
2199
+ }
2200
+ function resolveOperatorSlug() {
2201
+ const envVar = process.env.BAGDOCK_OPERATOR;
2202
+ if (envVar)
2203
+ return envVar;
2204
+ const creds = loadCredentials();
2205
+ return creds?.operatorSlug;
2206
+ }
2207
+ function updateProfileContext(operatorId, operatorSlug, environment) {
2208
+ const creds = loadCredentials();
2209
+ if (!creds)
2210
+ return;
2211
+ saveCredentials({
2212
+ ...creds,
2213
+ operatorId,
2214
+ operatorSlug,
2215
+ environment
2216
+ });
2217
+ }
2170
2218
  function loadBagdockJson(dir) {
2171
2219
  const file = join(dir, "bagdock.json");
2172
2220
  if (!existsSync(file))
@@ -2177,7 +2225,7 @@ function loadBagdockJson(dir) {
2177
2225
  return null;
2178
2226
  }
2179
2227
  }
2180
- var CONFIG_DIR, CREDENTIALS_FILE, API_BASE, DASHBOARD_BASE, profileOverride;
2228
+ var CONFIG_DIR, CREDENTIALS_FILE, API_BASE, DASHBOARD_BASE, profileOverride, envOverride;
2181
2229
  var init_config = __esm(() => {
2182
2230
  CONFIG_DIR = join(homedir(), ".bagdock");
2183
2231
  CREDENTIALS_FILE = join(CONFIG_DIR, "credentials.json");
@@ -3341,15 +3389,27 @@ Requesting device authorization...
3341
3389
  refreshToken: tokens.refresh_token,
3342
3390
  expiresAt: tokens.expires_in ? Date.now() + tokens.expires_in * 1000 : undefined,
3343
3391
  email: tokens.email,
3344
- operatorId: tokens.operator_id
3392
+ operatorId: tokens.operator_id,
3393
+ operatorSlug: tokens.operator_slug,
3394
+ environment: "live"
3345
3395
  });
3346
3396
  console.log(source_default.green(`
3347
3397
  Logged in successfully!`));
3348
3398
  if (tokens.email)
3349
3399
  console.log(" Email:", source_default.bold(tokens.email));
3350
- if (tokens.operator_id)
3351
- console.log(" Operator:", source_default.bold(tokens.operator_id));
3400
+ if (tokens.operator_slug) {
3401
+ console.log(" Operator:", source_default.bold(tokens.operator_slug));
3402
+ } else if (tokens.operator_id) {
3403
+ console.log(" Operator ID:", source_default.bold(tokens.operator_id));
3404
+ }
3405
+ console.log(" Environment:", source_default.bold("live"));
3352
3406
  console.log(" Profile:", source_default.bold(getActiveProfileName()));
3407
+ if (!tokens.operator_slug) {
3408
+ await resolveOperatorAfterLogin(tokens.access_token);
3409
+ } else {
3410
+ console.log();
3411
+ console.log(source_default.dim(" Tip: run"), source_default.cyan("bagdock switch"), source_default.dim("to change operator or environment."));
3412
+ }
3353
3413
  return;
3354
3414
  }
3355
3415
  const error = await tokenRes.json().catch(() => ({ error: "unknown" }));
@@ -3396,12 +3456,22 @@ async function whoami() {
3396
3456
  process.exit(1);
3397
3457
  }
3398
3458
  const user = await res.json();
3459
+ const creds = loadCredentials();
3399
3460
  if (isJsonMode()) {
3400
- outputSuccess({ ...user, profile: getActiveProfileName() });
3461
+ outputSuccess({
3462
+ ...user,
3463
+ profile: getActiveProfileName(),
3464
+ operator_slug: creds?.operatorSlug,
3465
+ environment: creds?.environment ?? "live"
3466
+ });
3401
3467
  } else {
3402
3468
  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));
3469
+ if (creds?.operatorSlug) {
3470
+ console.log("Operator:", source_default.bold(creds.operatorSlug));
3471
+ } else if (user.operator_id) {
3472
+ console.log("Operator ID:", source_default.bold(user.operator_id));
3473
+ }
3474
+ console.log("Environment:", source_default.bold(creds?.environment ?? "live"));
3405
3475
  if (user.name)
3406
3476
  console.log("Name:", user.name);
3407
3477
  console.log("Profile:", source_default.bold(getActiveProfileName()));
@@ -3428,8 +3498,9 @@ async function authList() {
3428
3498
  const marker = p.active ? source_default.green("* ") : " ";
3429
3499
  const label = p.active ? source_default.bold(p.name) : p.name;
3430
3500
  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}`);
3501
+ const operator = p.operatorSlug ? source_default.dim(` ${p.operatorSlug}`) : p.operatorId ? source_default.dim(` ${p.operatorId}`) : "";
3502
+ const env2 = p.environment ? source_default.dim(` [${p.environment}]`) : "";
3503
+ console.log(` ${marker}${label}${email}${operator}${env2}`);
3433
3504
  }
3434
3505
  console.log();
3435
3506
  }
@@ -3482,6 +3553,28 @@ Available profiles:
3482
3553
  process.exit(1);
3483
3554
  }
3484
3555
  }
3556
+ async function resolveOperatorAfterLogin(accessToken) {
3557
+ try {
3558
+ const res = await fetch(`${API_BASE}/api/v1/me/operators`, {
3559
+ headers: { Authorization: `Bearer ${accessToken}` }
3560
+ });
3561
+ if (!res.ok)
3562
+ return;
3563
+ const body = await res.json();
3564
+ const operators = body.data ?? [];
3565
+ if (operators.length === 1) {
3566
+ const op = operators[0];
3567
+ updateProfileContext(op.id, op.slug, "live");
3568
+ console.log(" Operator:", source_default.bold(`${op.name} (${op.slug})`));
3569
+ console.log();
3570
+ console.log(source_default.dim(" Tip: run"), source_default.cyan("bagdock switch"), source_default.dim("to change environment to test/sandbox."));
3571
+ } else if (operators.length > 1) {
3572
+ console.log();
3573
+ console.log(source_default.dim(` You have access to ${operators.length} operators.`));
3574
+ console.log(source_default.dim(" Run"), source_default.cyan("bagdock switch"), source_default.dim("to select one."));
3575
+ }
3576
+ } catch {}
3577
+ }
3485
3578
  function sleep(ms) {
3486
3579
  return new Promise((resolve) => setTimeout(resolve, ms));
3487
3580
  }
@@ -3492,6 +3585,175 @@ var init_auth = __esm(() => {
3492
3585
  init_output();
3493
3586
  });
3494
3587
 
3588
+ // src/api.ts
3589
+ function resolveFullEnvironment() {
3590
+ const flagOverride = getEnvironmentOverride();
3591
+ if (flagOverride)
3592
+ return flagOverride;
3593
+ const linkEnv = resolveLinkEnvironment();
3594
+ if (linkEnv)
3595
+ return linkEnv;
3596
+ return resolveEnvironment();
3597
+ }
3598
+ async function apiFetch(path2, init2) {
3599
+ const token = getAuthToken();
3600
+ if (!token) {
3601
+ outputError("auth_error", "Not authenticated. Run bagdock login or set BAGDOCK_API_KEY.");
3602
+ process.exit(1);
3603
+ }
3604
+ const env2 = resolveFullEnvironment();
3605
+ const opSlug = resolveOperatorSlug();
3606
+ const headers = new Headers(init2?.headers);
3607
+ headers.set("Authorization", `Bearer ${token}`);
3608
+ headers.set("X-Environment", env2);
3609
+ if (opSlug)
3610
+ headers.set("X-Operator-Slug", opSlug);
3611
+ return fetch(`${API_BASE}${path2}`, { ...init2, headers });
3612
+ }
3613
+ async function apiFetchJson(path2, method, body) {
3614
+ return apiFetch(path2, {
3615
+ method,
3616
+ headers: { "Content-Type": "application/json" },
3617
+ body: JSON.stringify(body)
3618
+ });
3619
+ }
3620
+ var init_api = __esm(() => {
3621
+ init_config();
3622
+ init_auth();
3623
+ init_output();
3624
+ });
3625
+
3626
+ // src/switch.ts
3627
+ var exports_switch = {};
3628
+ __export(exports_switch, {
3629
+ switchContext: () => switchContext
3630
+ });
3631
+ async function fetchOperators() {
3632
+ const res = await apiFetch("/api/v1/me/operators");
3633
+ if (!res.ok) {
3634
+ const err = await res.json().catch(() => ({}));
3635
+ throw new Error(err?.error?.message || `API returned ${res.status}`);
3636
+ }
3637
+ const body = await res.json();
3638
+ return body.data ?? [];
3639
+ }
3640
+ async function fetchSandboxes() {
3641
+ const res = await apiFetch("/api/v1/me/sandboxes");
3642
+ if (!res.ok)
3643
+ return [];
3644
+ const body = await res.json();
3645
+ return body.data ?? [];
3646
+ }
3647
+ function prompt(question) {
3648
+ const readline = __require("readline");
3649
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
3650
+ return new Promise((resolve) => rl.question(question, (answer) => {
3651
+ rl.close();
3652
+ resolve(answer.trim());
3653
+ }));
3654
+ }
3655
+ async function switchContext(opts) {
3656
+ status("Fetching your operators...");
3657
+ let operators;
3658
+ try {
3659
+ operators = await fetchOperators();
3660
+ } catch (err) {
3661
+ outputError("api_error", `Failed to list operators: ${err.message}`);
3662
+ return;
3663
+ }
3664
+ if (!operators.length) {
3665
+ outputError("no_operators", "No operators found for your account.");
3666
+ return;
3667
+ }
3668
+ let selected;
3669
+ if (opts.operator) {
3670
+ const match = operators.find((o) => o.slug === opts.operator || o.id === opts.operator);
3671
+ if (!match) {
3672
+ outputError("not_found", `Operator "${opts.operator}" not found. Available: ${operators.map((o) => o.slug).join(", ")}`);
3673
+ return;
3674
+ }
3675
+ selected = match;
3676
+ } else if (operators.length === 1) {
3677
+ selected = operators[0];
3678
+ if (!isJsonMode()) {
3679
+ console.log(source_default.dim(` Auto-selected: ${selected.name} (${selected.slug})`));
3680
+ }
3681
+ } else if (!process.stdout.isTTY || isJsonMode()) {
3682
+ outputError("operator_required", `Multiple operators available. Pass --operator <slug>. Available: ${operators.map((o) => o.slug).join(", ")}`);
3683
+ return;
3684
+ } else {
3685
+ console.log(source_default.bold(`
3686
+ Select operator:
3687
+ `));
3688
+ operators.forEach((op, i) => {
3689
+ console.log(` ${source_default.cyan(String(i + 1))} ${op.name} ${source_default.dim(`(${op.slug})`)}`);
3690
+ });
3691
+ console.log();
3692
+ const answer = await prompt(" > ");
3693
+ const idx = parseInt(answer, 10) - 1;
3694
+ if (isNaN(idx) || idx < 0 || idx >= operators.length) {
3695
+ outputError("invalid_selection", "Invalid selection.");
3696
+ return;
3697
+ }
3698
+ selected = operators[idx];
3699
+ }
3700
+ let environment = "live";
3701
+ if (opts.env) {
3702
+ if (opts.env !== "live" && opts.env !== "test") {
3703
+ outputError("invalid_env", 'Environment must be "live" or "test".');
3704
+ return;
3705
+ }
3706
+ environment = opts.env;
3707
+ } else if (process.stdout.isTTY && !isJsonMode()) {
3708
+ status("Fetching sandboxes...");
3709
+ updateProfileContext(selected.id, selected.slug, "live");
3710
+ const sandboxes = await fetchSandboxes();
3711
+ console.log(source_default.bold(`
3712
+ Select environment:
3713
+ `));
3714
+ console.log(` ${source_default.cyan("1")} Live`);
3715
+ if (sandboxes.length > 0) {
3716
+ sandboxes.forEach((sb, i) => {
3717
+ const tag = sb.is_default ? source_default.dim(" (default)") : "";
3718
+ console.log(` ${source_default.cyan(String(i + 2))} Sandbox: ${sb.name}${tag}`);
3719
+ });
3720
+ } else {
3721
+ console.log(` ${source_default.cyan("2")} Test ${source_default.dim("(default sandbox)")}`);
3722
+ }
3723
+ console.log();
3724
+ const envAnswer = await prompt(" > ");
3725
+ const envIdx = parseInt(envAnswer, 10);
3726
+ if (envIdx === 1) {
3727
+ environment = "live";
3728
+ } else if (sandboxes.length > 0 && envIdx >= 2 && envIdx <= sandboxes.length + 1) {
3729
+ environment = "test";
3730
+ } else if (envIdx === 2 && sandboxes.length === 0) {
3731
+ environment = "test";
3732
+ } else {
3733
+ outputError("invalid_selection", "Invalid selection.");
3734
+ return;
3735
+ }
3736
+ }
3737
+ updateProfileContext(selected.id, selected.slug, environment);
3738
+ if (isJsonMode()) {
3739
+ outputSuccess({
3740
+ operator: { id: selected.id, slug: selected.slug, name: selected.name },
3741
+ environment
3742
+ });
3743
+ } else {
3744
+ const envLabel = environment === "test" ? source_default.yellow("test") : source_default.green("live");
3745
+ console.log();
3746
+ console.log(source_default.green(" Switched to"), source_default.bold(selected.name), source_default.dim(`(${selected.slug})`), `[${envLabel}]`);
3747
+ console.log();
3748
+ }
3749
+ }
3750
+ var init_switch = __esm(() => {
3751
+ init_source();
3752
+ init_config();
3753
+ init_api();
3754
+ init_output();
3755
+ });
3756
+
3495
3757
  // src/doctor.ts
3496
3758
  var exports_doctor = {};
3497
3759
  __export(exports_doctor, {
@@ -3579,11 +3841,30 @@ function checkAgents() {
3579
3841
  }
3580
3842
  return { name: "AI Agents", status: "pass", message: `Detected: ${detected.join(", ")}` };
3581
3843
  }
3844
+ function checkOperatorContext() {
3845
+ const creds = loadCredentials();
3846
+ const slug = resolveOperatorSlug();
3847
+ const env2 = resolveEnvironment();
3848
+ const profile = getActiveProfileName();
3849
+ if (!slug) {
3850
+ return {
3851
+ name: "Operator Context",
3852
+ status: "warn",
3853
+ message: `No operator selected (profile: ${profile}). Run \`bagdock switch\` to set one.`
3854
+ };
3855
+ }
3856
+ return {
3857
+ name: "Operator Context",
3858
+ status: "pass",
3859
+ message: `${slug} [${env2}] (profile: ${profile})`
3860
+ };
3861
+ }
3582
3862
  async function doctor() {
3583
3863
  const checks = [];
3584
3864
  if (isJsonMode()) {
3585
3865
  checks.push(await checkVersion());
3586
3866
  checks.push(checkAuth());
3867
+ checks.push(checkOperatorContext());
3587
3868
  checks.push(checkProjectConfig());
3588
3869
  checks.push(checkAgents());
3589
3870
  const ok = checks.every((c) => c.status !== "fail");
@@ -3601,6 +3882,9 @@ async function doctor() {
3601
3882
  const authCheck = checkAuth();
3602
3883
  checks.push(authCheck);
3603
3884
  console.log(` ${ICON[authCheck.status]} ${authCheck.name}: ${authCheck.message}`);
3885
+ const contextCheck = checkOperatorContext();
3886
+ checks.push(contextCheck);
3887
+ console.log(` ${ICON[contextCheck.status]} ${contextCheck.name}: ${contextCheck.message}`);
3604
3888
  const configCheck = checkProjectConfig();
3605
3889
  checks.push(configCheck);
3606
3890
  console.log(` ${ICON[configCheck.status]} ${configCheck.name}: ${configCheck.message}`);
@@ -3825,10 +4109,10 @@ Deploying ${config.slug}@${config.version} → ${envLabel}
3825
4109
  process.exit(1);
3826
4110
  }
3827
4111
  }
3828
- function confirm(prompt) {
4112
+ function confirm(prompt2) {
3829
4113
  return new Promise((resolve) => {
3830
4114
  const rl = createInterface({ input: process.stdin, output: process.stdout });
3831
- rl.question(prompt, (answer) => {
4115
+ rl.question(prompt2, (answer) => {
3832
4116
  rl.close();
3833
4117
  resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
3834
4118
  });
@@ -3894,21 +4178,412 @@ var init_submit = __esm(() => {
3894
4178
  init_auth();
3895
4179
  });
3896
4180
 
4181
+ // src/link.ts
4182
+ var exports_link = {};
4183
+ __export(exports_link, {
4184
+ resolveSlug: () => resolveSlug,
4185
+ requireSlug: () => requireSlug,
4186
+ link: () => link
4187
+ });
4188
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
4189
+ import { join as join6 } from "path";
4190
+ function resolveSlug() {
4191
+ const config = loadBagdockJson(process.cwd());
4192
+ if (config?.slug)
4193
+ return config.slug;
4194
+ const linkPath = join6(process.cwd(), LINK_DIR, LINK_FILE);
4195
+ if (existsSync6(linkPath)) {
4196
+ try {
4197
+ const data = JSON.parse(readFileSync4(linkPath, "utf-8"));
4198
+ return data.slug ?? null;
4199
+ } catch {
4200
+ return null;
4201
+ }
4202
+ }
4203
+ return null;
4204
+ }
4205
+ function requireSlug(slugArg) {
4206
+ const slug = slugArg ?? resolveSlug();
4207
+ if (!slug) {
4208
+ if (isJsonMode()) {
4209
+ outputError("no_project", "No project found. Pass --slug, add bagdock.json, or run bagdock link.");
4210
+ }
4211
+ console.error(source_default.red("No project found."), "Pass a slug, create bagdock.json, or run", source_default.cyan("bagdock link"));
4212
+ process.exit(1);
4213
+ }
4214
+ return slug;
4215
+ }
4216
+ async function link(opts) {
4217
+ let slug = opts.slug;
4218
+ const linkEnv = opts.env === "test" || opts.env === "live" ? opts.env : undefined;
4219
+ if (!slug) {
4220
+ const config = loadBagdockJson(process.cwd());
4221
+ if (config?.slug) {
4222
+ slug = config.slug;
4223
+ status(`Found bagdock.json — linking to ${slug}`);
4224
+ }
4225
+ }
4226
+ if (!slug && process.stdout.isTTY && !isJsonMode()) {
4227
+ const token = getAuthToken();
4228
+ if (!token) {
4229
+ console.error(source_default.red("Not authenticated."), "Run", source_default.cyan("bagdock login"), "first.");
4230
+ process.exit(1);
4231
+ }
4232
+ status("Fetching your apps...");
4233
+ try {
4234
+ const res = await apiFetch("/api/v1/developer/apps");
4235
+ if (!res.ok)
4236
+ throw new Error(`API returned ${res.status}`);
4237
+ const { data } = await res.json();
4238
+ if (!data?.length) {
4239
+ console.error(source_default.yellow("No apps found."), "Create one with", source_default.cyan("bagdock init"));
4240
+ process.exit(1);
4241
+ }
4242
+ console.log(source_default.bold(`
4243
+ Your apps:
4244
+ `));
4245
+ data.forEach((app, i) => console.log(` ${source_default.cyan(i + 1)} ${app.name} ${source_default.dim(`(${app.slug})`)}`));
4246
+ console.log();
4247
+ const readline = await import("readline");
4248
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
4249
+ const answer = await new Promise((resolve) => rl.question("Select app number: ", resolve));
4250
+ rl.close();
4251
+ const idx = parseInt(answer, 10) - 1;
4252
+ if (isNaN(idx) || idx < 0 || idx >= data.length) {
4253
+ console.error(source_default.red("Invalid selection"));
4254
+ process.exit(1);
4255
+ }
4256
+ slug = data[idx].slug;
4257
+ } catch (err) {
4258
+ console.error(source_default.red("Failed to fetch apps:"), err.message);
4259
+ process.exit(1);
4260
+ }
4261
+ }
4262
+ if (!slug) {
4263
+ outputError("missing_slug", "Slug required. Pass --slug in non-interactive mode.");
4264
+ process.exit(1);
4265
+ }
4266
+ const dir = join6(process.cwd(), LINK_DIR);
4267
+ if (!existsSync6(dir))
4268
+ mkdirSync3(dir, { recursive: true });
4269
+ const linkData = { slug, environment: linkEnv, linkedAt: new Date().toISOString() };
4270
+ writeFileSync4(join6(dir, LINK_FILE), JSON.stringify(linkData, null, 2));
4271
+ if (isJsonMode()) {
4272
+ outputSuccess({ slug, environment: linkEnv, path: join6(dir, LINK_FILE) });
4273
+ } else {
4274
+ const envLabel = linkEnv ? ` [${linkEnv}]` : "";
4275
+ console.log(source_default.green(`Linked to ${source_default.bold(slug)}${envLabel}`));
4276
+ console.log(source_default.dim(` Stored in ${LINK_DIR}/${LINK_FILE}`));
4277
+ }
4278
+ }
4279
+ var LINK_DIR = ".bagdock", LINK_FILE = "link.json";
4280
+ var init_link = __esm(() => {
4281
+ init_source();
4282
+ init_config();
4283
+ init_auth();
4284
+ init_api();
4285
+ init_output();
4286
+ });
4287
+
4288
+ // src/validate.ts
4289
+ var exports_validate = {};
4290
+ __export(exports_validate, {
4291
+ validate: () => validate
4292
+ });
4293
+ import { existsSync as existsSync7, statSync } from "fs";
4294
+ import { join as join7 } from "path";
4295
+ async function validate() {
4296
+ const checks = [];
4297
+ const dir = process.cwd();
4298
+ const config = loadBagdockJson(dir);
4299
+ if (!config) {
4300
+ checks.push({ name: "bagdock.json", status: "fail", message: "Not found or invalid JSON" });
4301
+ return finish(checks);
4302
+ }
4303
+ checks.push({ name: "bagdock.json", status: "pass", message: "Found and parsed" });
4304
+ const required = ["name", "slug", "version", "type", "category", "main"];
4305
+ const missing = required.filter((f) => !config[f]);
4306
+ if (missing.length) {
4307
+ checks.push({ name: "Required fields", status: "fail", message: `Missing: ${missing.join(", ")}` });
4308
+ } else {
4309
+ checks.push({ name: "Required fields", status: "pass", message: "All present" });
4310
+ }
4311
+ if (!VALID_TYPES.includes(config.type)) {
4312
+ checks.push({ name: "Type", status: "fail", message: `Invalid type "${config.type}". Must be: ${VALID_TYPES.join(", ")}` });
4313
+ } else {
4314
+ checks.push({ name: "Type", status: "pass", message: config.type });
4315
+ }
4316
+ if (config.kind && !VALID_KINDS.includes(config.kind)) {
4317
+ checks.push({ name: "Kind", status: "warn", message: `Unknown kind "${config.kind}". Expected: ${VALID_KINDS.join(", ")}` });
4318
+ }
4319
+ const entryPath = join7(dir, config.main);
4320
+ if (!existsSync7(entryPath)) {
4321
+ checks.push({ name: "Entry point", status: "fail", message: `File not found: ${config.main}` });
4322
+ } else {
4323
+ const size = statSync(entryPath).size;
4324
+ checks.push({ name: "Entry point", status: "pass", message: `${config.main} (${(size / 1024).toFixed(1)} KB)` });
4325
+ if (size > MAX_BUNDLE_BYTES) {
4326
+ checks.push({ name: "Bundle size", status: "fail", message: `${(size / 1024 / 1024).toFixed(1)} MB exceeds ${MAX_BUNDLE_BYTES / 1024 / 1024} MB limit` });
4327
+ } else if (size > MAX_BUNDLE_BYTES * 0.8) {
4328
+ checks.push({ name: "Bundle size", status: "warn", message: `${(size / 1024 / 1024).toFixed(1)} MB — approaching limit` });
4329
+ } else {
4330
+ checks.push({ name: "Bundle size", status: "pass", message: `${(size / 1024).toFixed(1)} KB` });
4331
+ }
4332
+ }
4333
+ const linked = resolveSlug();
4334
+ if (linked && linked !== config.slug) {
4335
+ checks.push({ name: "Project link", status: "warn", message: `bagdock.json slug "${config.slug}" differs from linked project "${linked}"` });
4336
+ }
4337
+ return finish(checks);
4338
+ }
4339
+ function finish(checks) {
4340
+ const hasFail = checks.some((c) => c.status === "fail");
4341
+ const hasWarn = checks.some((c) => c.status === "warn");
4342
+ if (isJsonMode()) {
4343
+ outputSuccess({ ok: !hasFail, checks });
4344
+ if (hasFail)
4345
+ process.exit(1);
4346
+ return;
4347
+ }
4348
+ console.log(source_default.bold(`
4349
+ Bagdock Validate
4350
+ `));
4351
+ for (const c of checks) {
4352
+ const icon = c.status === "pass" ? source_default.green("✔") : c.status === "warn" ? source_default.yellow("⚠") : source_default.red("✖");
4353
+ console.log(` ${icon} ${c.name}: ${c.message}`);
4354
+ }
4355
+ console.log();
4356
+ if (hasFail) {
4357
+ console.log(source_default.red(` Validation failed. Fix errors before submitting.
4358
+ `));
4359
+ process.exit(1);
4360
+ } else if (hasWarn) {
4361
+ console.log(source_default.yellow(` Passed with warnings.
4362
+ `));
4363
+ } else {
4364
+ console.log(source_default.green(` All checks passed. Ready to submit.
4365
+ `));
4366
+ }
4367
+ }
4368
+ var VALID_TYPES, VALID_KINDS, MAX_BUNDLE_BYTES;
4369
+ var init_validate = __esm(() => {
4370
+ init_source();
4371
+ init_config();
4372
+ init_output();
4373
+ init_link();
4374
+ VALID_TYPES = ["edge", "app"];
4375
+ VALID_KINDS = ["adapter", "comms", "webhook", "ui-extension", "microfrontend"];
4376
+ MAX_BUNDLE_BYTES = 10 * 1024 * 1024;
4377
+ });
4378
+
4379
+ // src/submission.ts
4380
+ var exports_submission = {};
4381
+ __export(exports_submission, {
4382
+ submissionWithdraw: () => submissionWithdraw,
4383
+ submissionStatus: () => submissionStatus,
4384
+ submissionList: () => submissionList
4385
+ });
4386
+ async function submissionList(opts) {
4387
+ const slug = requireSlug(opts.app);
4388
+ status(`Fetching submissions for ${slug}...`);
4389
+ try {
4390
+ const res = await apiFetch(`/api/v1/developer/apps/${slug}/submissions`);
4391
+ if (res.status === 404) {
4392
+ outputError("not_found", `App "${slug}" not found or no submissions exist.`);
4393
+ }
4394
+ if (!res.ok) {
4395
+ outputError("api_error", `API returned ${res.status}`);
4396
+ }
4397
+ const { data } = await res.json();
4398
+ if (isJsonMode()) {
4399
+ outputList("submission", data, false);
4400
+ return;
4401
+ }
4402
+ if (!data?.length) {
4403
+ console.log(source_default.yellow("No submissions found for this app."));
4404
+ console.log("Submit with", source_default.cyan("bagdock submit"));
4405
+ return;
4406
+ }
4407
+ console.log(source_default.bold(`
4408
+ Submissions for ${slug}:
4409
+ `));
4410
+ console.log(` ${"ID".padEnd(22)} ${"Version".padEnd(10)} ${"Reason".padEnd(30)} ${"Date"}`);
4411
+ console.log(source_default.dim(" " + "─".repeat(80)));
4412
+ for (const s of data) {
4413
+ 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())}`);
4414
+ }
4415
+ console.log();
4416
+ } catch (err) {
4417
+ outputError("network_error", err.message);
4418
+ }
4419
+ }
4420
+ async function submissionStatus(id, opts) {
4421
+ const slug = requireSlug(opts.app);
4422
+ status(`Fetching submission ${id}...`);
4423
+ try {
4424
+ const res = await apiFetch(`/api/v1/developer/apps/${slug}/submissions/${id}`);
4425
+ if (res.status === 404) {
4426
+ outputError("not_found", `Submission "${id}" not found.`);
4427
+ }
4428
+ if (!res.ok) {
4429
+ outputError("api_error", `API returned ${res.status}`);
4430
+ }
4431
+ const { data } = await res.json();
4432
+ if (isJsonMode()) {
4433
+ outputSuccess(data);
4434
+ return;
4435
+ }
4436
+ console.log(source_default.bold(`
4437
+ Submission ${source_default.cyan(data.id)}
4438
+ `));
4439
+ const fields = [
4440
+ ["App", `${data.name} (${data.slug})`],
4441
+ ["Version", data.version],
4442
+ ["Review Status", data.review_status],
4443
+ ["Type", data.type],
4444
+ ["Visibility", data.visibility],
4445
+ ["Reason", data.change_reason],
4446
+ ["Submitted by", data.changed_by],
4447
+ ["Date", new Date(data.created_at).toLocaleString()]
4448
+ ];
4449
+ for (const [label, value] of fields) {
4450
+ console.log(` ${source_default.dim(label.padEnd(16))} ${value ?? source_default.dim("—")}`);
4451
+ }
4452
+ console.log();
4453
+ } catch (err) {
4454
+ outputError("network_error", err.message);
4455
+ }
4456
+ }
4457
+ async function submissionWithdraw(id, opts) {
4458
+ const slug = requireSlug(opts.app);
4459
+ status(`Withdrawing submission ${id}...`);
4460
+ try {
4461
+ const res = await apiFetch(`/api/v1/developer/apps/${slug}/submissions/${id}/withdraw`, {
4462
+ method: "POST"
4463
+ });
4464
+ if (res.status === 404) {
4465
+ outputError("not_found", `App "${slug}" not found.`);
4466
+ }
4467
+ if (res.status === 409) {
4468
+ const body = await res.json();
4469
+ outputError(body.code ?? "invalid_status", body.message);
4470
+ }
4471
+ if (!res.ok) {
4472
+ outputError("api_error", `API returned ${res.status}`);
4473
+ }
4474
+ const { data } = await res.json();
4475
+ if (isJsonMode()) {
4476
+ outputSuccess(data);
4477
+ return;
4478
+ }
4479
+ console.log(source_default.green(`Submission withdrawn.`), source_default.dim(`Status is now: ${data.review_status}`));
4480
+ console.log("You can re-submit with", source_default.cyan("bagdock submit"));
4481
+ } catch (err) {
4482
+ outputError("network_error", err.message);
4483
+ }
4484
+ }
4485
+ var init_submission = __esm(() => {
4486
+ init_source();
4487
+ init_api();
4488
+ init_output();
4489
+ init_link();
4490
+ });
4491
+
4492
+ // src/open.ts
4493
+ var exports_open2 = {};
4494
+ __export(exports_open2, {
4495
+ open: () => open2
4496
+ });
4497
+ async function open2(slugArg) {
4498
+ const slug = requireSlug(slugArg);
4499
+ const operatorSlug = resolveOperatorSlug();
4500
+ const env2 = resolveEnvironment();
4501
+ if (!operatorSlug) {
4502
+ outputError("no_operator", "No operator context. Run bagdock switch or bagdock login to set one.");
4503
+ return;
4504
+ }
4505
+ const envSegment = env2 === "test" ? "/test" : "";
4506
+ const url = `${DASHBOARD_BASE}/${operatorSlug}${envSegment}/developer/apps/${slug}`;
4507
+ if (isJsonMode()) {
4508
+ outputSuccess({ url, slug, operator: operatorSlug, environment: env2 });
4509
+ return;
4510
+ }
4511
+ status(`Opening ${url}`);
4512
+ await open_default(url);
4513
+ console.log(source_default.green("Opened"), source_default.cyan(url));
4514
+ }
4515
+ var init_open2 = __esm(() => {
4516
+ init_source();
4517
+ init_open();
4518
+ init_config();
4519
+ init_output();
4520
+ init_link();
4521
+ });
4522
+
4523
+ // src/inspect.ts
4524
+ var exports_inspect = {};
4525
+ __export(exports_inspect, {
4526
+ inspect: () => inspect
4527
+ });
4528
+ async function inspect(slugArg) {
4529
+ const slug = requireSlug(slugArg);
4530
+ status(`Inspecting ${slug}...`);
4531
+ try {
4532
+ const res = await apiFetch(`/api/v1/developer/apps/${slug}`);
4533
+ if (res.status === 404)
4534
+ outputError("not_found", `App "${slug}" not found.`);
4535
+ if (!res.ok)
4536
+ outputError("api_error", `API returned ${res.status}`);
4537
+ const { data } = await res.json();
4538
+ if (isJsonMode()) {
4539
+ outputSuccess(data);
4540
+ return;
4541
+ }
4542
+ console.log(source_default.bold(`
4543
+ ${data.name} ${source_default.dim(`(${data.slug})`)}
4544
+ `));
4545
+ const fields = [
4546
+ ["ID", data.id],
4547
+ ["Type", data.type],
4548
+ ["Category", data.category],
4549
+ ["Version", data.version],
4550
+ ["Maintainer", data.maintainer],
4551
+ ["Visibility", data.visibility],
4552
+ ["Review Status", data.review_status],
4553
+ ["Active", data.is_active ? "yes" : "no"],
4554
+ ["Worker URL", data.worker_url],
4555
+ ["Namespace", data.worker_namespace],
4556
+ ["Created", data.created_at ? new Date(data.created_at).toLocaleString() : undefined],
4557
+ ["Updated", data.updated_at ? new Date(data.updated_at).toLocaleString() : undefined],
4558
+ ["Published", data.published_at ? new Date(data.published_at).toLocaleString() : undefined]
4559
+ ];
4560
+ for (const [label, value] of fields) {
4561
+ if (value !== undefined && value !== null) {
4562
+ console.log(` ${source_default.dim(label.padEnd(16))} ${value}`);
4563
+ }
4564
+ }
4565
+ console.log();
4566
+ } catch (err) {
4567
+ outputError("network_error", err.message);
4568
+ }
4569
+ }
4570
+ var init_inspect = __esm(() => {
4571
+ init_source();
4572
+ init_api();
4573
+ init_output();
4574
+ init_link();
4575
+ });
4576
+
3897
4577
  // src/env-cmd.ts
3898
4578
  var exports_env_cmd = {};
3899
4579
  __export(exports_env_cmd, {
3900
4580
  envSet: () => envSet,
3901
4581
  envRemove: () => envRemove,
4582
+ envPull: () => envPull,
3902
4583
  envList: () => envList
3903
4584
  });
3904
- function requireAuth() {
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."));
3908
- process.exit(1);
3909
- }
3910
- return token;
3911
- }
4585
+ import { writeFileSync as writeFileSync5 } from "fs";
4586
+ import { resolve } from "path";
3912
4587
  function requireConfig() {
3913
4588
  const config = loadBagdockJson(process.cwd());
3914
4589
  if (!config) {
@@ -3918,12 +4593,9 @@ function requireConfig() {
3918
4593
  return config;
3919
4594
  }
3920
4595
  async function envList() {
3921
- const token = requireAuth();
3922
4596
  const config = requireConfig();
3923
4597
  try {
3924
- const res = await fetch(`${API_BASE}/v1/developer/apps/${config.slug}/env`, {
3925
- headers: { Authorization: `Bearer ${token}` }
3926
- });
4598
+ const res = await apiFetch(`/api/v1/developer/apps/${config.slug}/env`);
3927
4599
  if (!res.ok) {
3928
4600
  console.error(source_default.red(`Failed to list env vars (${res.status})`));
3929
4601
  process.exit(1);
@@ -3947,17 +4619,9 @@ Environment variables for ${config.slug}:
3947
4619
  }
3948
4620
  }
3949
4621
  async function envSet(key, value) {
3950
- const token = requireAuth();
3951
4622
  const config = requireConfig();
3952
4623
  try {
3953
- const res = await fetch(`${API_BASE}/v1/developer/apps/${config.slug}/env`, {
3954
- method: "PUT",
3955
- headers: {
3956
- "Content-Type": "application/json",
3957
- Authorization: `Bearer ${token}`
3958
- },
3959
- body: JSON.stringify({ key, value })
3960
- });
4624
+ const res = await apiFetchJson(`/api/v1/developer/apps/${config.slug}/env`, "PUT", { key, value });
3961
4625
  if (!res.ok) {
3962
4626
  const body = await res.text();
3963
4627
  console.error(source_default.red(`Failed to set ${key} (${res.status}):`), body.slice(0, 200));
@@ -3970,13 +4634,9 @@ async function envSet(key, value) {
3970
4634
  }
3971
4635
  }
3972
4636
  async function envRemove(key) {
3973
- const token = requireAuth();
3974
4637
  const config = requireConfig();
3975
4638
  try {
3976
- const res = await fetch(`${API_BASE}/v1/developer/apps/${config.slug}/env/${key}`, {
3977
- method: "DELETE",
3978
- headers: { Authorization: `Bearer ${token}` }
3979
- });
4639
+ const res = await apiFetch(`/api/v1/developer/apps/${config.slug}/env/${key}`, { method: "DELETE" });
3980
4640
  if (!res.ok) {
3981
4641
  console.error(source_default.red(`Failed to remove ${key} (${res.status})`));
3982
4642
  process.exit(1);
@@ -3987,10 +4647,49 @@ async function envRemove(key) {
3987
4647
  process.exit(1);
3988
4648
  }
3989
4649
  }
4650
+ async function envPull(file) {
4651
+ const slug = requireSlug();
4652
+ const target = resolve(file ?? ".env.local");
4653
+ status(`Pulling env vars for ${slug}...`);
4654
+ try {
4655
+ const res = await apiFetch(`/api/v1/developer/apps/${slug}/env`);
4656
+ if (!res.ok) {
4657
+ outputError("api_error", `Failed to pull env vars (${res.status})`);
4658
+ process.exit(1);
4659
+ }
4660
+ const { data } = await res.json();
4661
+ if (isJsonMode()) {
4662
+ outputSuccess({ file: target, keys: data.map((v) => v.key) });
4663
+ return;
4664
+ }
4665
+ if (!data?.length) {
4666
+ console.log(source_default.yellow("No environment variables set."));
4667
+ return;
4668
+ }
4669
+ const lines = [
4670
+ `# Pulled from Bagdock — ${slug}`,
4671
+ `# ${new Date().toISOString()}`,
4672
+ `# Values are placeholders — the API does not expose secrets.`,
4673
+ `# Fill in real values for local development.`,
4674
+ "",
4675
+ ...data.map((v) => `${v.key}=`),
4676
+ ""
4677
+ ];
4678
+ writeFileSync5(target, lines.join(`
4679
+ `));
4680
+ console.log(source_default.green(`Wrote ${data.length} keys to ${target}`));
4681
+ console.log(source_default.yellow("Note:"), "Values are empty — fill them in for local dev.");
4682
+ } catch (err) {
4683
+ console.error(source_default.red("Failed:"), err.message);
4684
+ process.exit(1);
4685
+ }
4686
+ }
3990
4687
  var init_env_cmd = __esm(() => {
3991
4688
  init_source();
3992
4689
  init_config();
3993
- init_auth();
4690
+ init_api();
4691
+ init_output();
4692
+ init_link();
3994
4693
  });
3995
4694
 
3996
4695
  // src/keys.ts
@@ -4000,23 +4699,9 @@ __export(exports_keys, {
4000
4699
  keysDelete: () => keysDelete,
4001
4700
  keysCreate: () => keysCreate
4002
4701
  });
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.");
4007
- }
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
4702
  async function keysCreate(opts) {
4018
4703
  status("Creating API key...");
4019
- const res = await apiRequest("POST", "/api/v1/operator/api-keys", {
4704
+ const res = await apiFetchJson("/api/v1/operator/api-keys", "POST", {
4020
4705
  name: opts.name,
4021
4706
  key_type: opts.type || "secret",
4022
4707
  key_category: opts.category || "standard",
@@ -4053,7 +4738,7 @@ async function keysList(opts) {
4053
4738
  let path2 = "/api/v1/operator/api-keys";
4054
4739
  if (opts.environment)
4055
4740
  path2 += `?environment=${opts.environment}`;
4056
- const res = await apiRequest("GET", path2);
4741
+ const res = await apiFetch(path2);
4057
4742
  if (!res.ok) {
4058
4743
  const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
4059
4744
  outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
@@ -4083,7 +4768,7 @@ async function keysDelete(id, opts) {
4083
4768
  outputError("CONFIRMATION_REQUIRED", "Pass --yes to confirm deletion in non-interactive mode.");
4084
4769
  }
4085
4770
  status(`Revoking API key ${id}...`);
4086
- const res = await apiRequest("DELETE", `/api/v1/operator/api-keys/${id}`, {
4771
+ const res = await apiFetchJson(`/api/v1/operator/api-keys/${id}`, "DELETE", {
4087
4772
  reason: opts.reason
4088
4773
  });
4089
4774
  if (!res.ok) {
@@ -4100,8 +4785,7 @@ async function keysDelete(id, opts) {
4100
4785
  }
4101
4786
  var init_keys = __esm(() => {
4102
4787
  init_source();
4103
- init_config();
4104
- init_auth();
4788
+ init_api();
4105
4789
  init_output();
4106
4790
  });
4107
4791
 
@@ -4111,19 +4795,9 @@ __export(exports_apps, {
4111
4795
  appsList: () => appsList,
4112
4796
  appsGet: () => appsGet
4113
4797
  });
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
4798
  async function appsList() {
4125
4799
  status("Fetching apps...");
4126
- const res = await apiRequest2("GET", "/api/v1/developer/apps");
4800
+ const res = await apiFetch("/api/v1/developer/apps");
4127
4801
  if (!res.ok) {
4128
4802
  const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
4129
4803
  outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
@@ -4153,7 +4827,7 @@ async function appsList() {
4153
4827
  }
4154
4828
  async function appsGet(slug) {
4155
4829
  status(`Fetching app ${slug}...`);
4156
- const res = await apiRequest2("GET", `/api/v1/developer/apps/${slug}`);
4830
+ const res = await apiFetch(`/api/v1/developer/apps/${slug}`);
4157
4831
  if (!res.ok) {
4158
4832
  if (res.status === 404) {
4159
4833
  outputError("NOT_FOUND", `App "${slug}" not found`);
@@ -4187,8 +4861,7 @@ async function appsGet(slug) {
4187
4861
  }
4188
4862
  var init_apps = __esm(() => {
4189
4863
  init_source();
4190
- init_config();
4191
- init_auth();
4864
+ init_api();
4192
4865
  init_output();
4193
4866
  });
4194
4867
 
@@ -4199,17 +4872,7 @@ __export(exports_logs, {
4199
4872
  logsList: () => logsList,
4200
4873
  logsGet: () => logsGet
4201
4874
  });
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.");
4206
- }
4207
- return fetch(`${API_BASE}${path2}`, {
4208
- method,
4209
- headers: { Authorization: `Bearer ${token}` }
4210
- });
4211
- }
4212
- function resolveSlug(slug) {
4875
+ function resolveSlug2(slug) {
4213
4876
  if (slug)
4214
4877
  return slug;
4215
4878
  const config = loadBagdockJson(process.cwd());
@@ -4219,10 +4882,10 @@ function resolveSlug(slug) {
4219
4882
  return "";
4220
4883
  }
4221
4884
  async function logsList(opts) {
4222
- const slug = resolveSlug(opts.app);
4885
+ const slug = resolveSlug2(opts.app);
4223
4886
  const limit = opts.limit || "50";
4224
4887
  status(`Fetching logs for ${slug}...`);
4225
- const res = await apiRequest3("GET", `/api/v1/developer/apps/${slug}/logs?limit=${limit}`);
4888
+ const res = await apiFetch(`/api/v1/developer/apps/${slug}/logs?limit=${limit}`);
4226
4889
  if (!res.ok) {
4227
4890
  if (res.status === 404) {
4228
4891
  outputError("NOT_FOUND", `App "${slug}" not found or no logs available`);
@@ -4250,9 +4913,9 @@ async function logsList(opts) {
4250
4913
  }
4251
4914
  }
4252
4915
  async function logsGet(id, opts) {
4253
- const slug = resolveSlug(opts.app);
4916
+ const slug = resolveSlug2(opts.app);
4254
4917
  status(`Fetching log entry ${id}...`);
4255
- const res = await apiRequest3("GET", `/api/v1/developer/apps/${slug}/logs/${id}`);
4918
+ const res = await apiFetch(`/api/v1/developer/apps/${slug}/logs/${id}`);
4256
4919
  if (!res.ok) {
4257
4920
  const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
4258
4921
  outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
@@ -4265,7 +4928,7 @@ async function logsGet(id, opts) {
4265
4928
  }
4266
4929
  }
4267
4930
  async function logsTail(opts) {
4268
- const slug = resolveSlug(opts.app);
4931
+ const slug = resolveSlug2(opts.app);
4269
4932
  if (isJsonMode()) {
4270
4933
  outputError("UNSUPPORTED", "Log tailing is not supported in JSON mode. Use `logs list` instead.");
4271
4934
  }
@@ -4275,7 +4938,7 @@ async function logsTail(opts) {
4275
4938
  let lastTimestamp = new Date().toISOString();
4276
4939
  const poll = async () => {
4277
4940
  try {
4278
- const res = await apiRequest3("GET", `/api/v1/developer/apps/${slug}/logs?since=${encodeURIComponent(lastTimestamp)}&limit=100`);
4941
+ const res = await apiFetch(`/api/v1/developer/apps/${slug}/logs?since=${encodeURIComponent(lastTimestamp)}&limit=100`);
4279
4942
  if (res.ok) {
4280
4943
  const result = await res.json();
4281
4944
  for (const entry of result.data || []) {
@@ -4301,7 +4964,7 @@ async function logsTail(opts) {
4301
4964
  var init_logs = __esm(() => {
4302
4965
  init_source();
4303
4966
  init_config();
4304
- init_auth();
4967
+ init_api();
4305
4968
  init_output();
4306
4969
  });
4307
4970
 
@@ -4575,13 +5238,20 @@ function toPascalCase(s) {
4575
5238
  init_output();
4576
5239
  init_config();
4577
5240
  var program2 = new Command;
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) => {
5241
+ program2.name("bagdock").description("Bagdock developer CLI — built for humans, AI agents, and CI/CD pipelines").version("0.5.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)").option("--env <environment>", "Override environment for this invocation (live, test)").hook("preAction", (_thisCommand, actionCommand) => {
4579
5242
  const opts = program2.opts();
4580
5243
  setOutputMode({ json: opts.json, quiet: opts.quiet });
4581
5244
  if (opts.apiKey)
4582
5245
  setApiKeyOverride(opts.apiKey);
4583
5246
  if (opts.profile)
4584
5247
  setProfileOverride(opts.profile);
5248
+ if (opts.env) {
5249
+ if (opts.env !== "live" && opts.env !== "test") {
5250
+ console.error('Error: --env must be "live" or "test"');
5251
+ process.exit(1);
5252
+ }
5253
+ setEnvironmentOverride(opts.env);
5254
+ }
4585
5255
  });
4586
5256
  program2.command("login").description("Authenticate with Bagdock (opens browser)").action(login);
4587
5257
  program2.command("logout").description("Clear stored credentials").action(logout);
@@ -4589,6 +5259,10 @@ program2.command("whoami").description("Show current authenticated user").action
4589
5259
  var authCmd = program2.command("auth").description("Manage authentication profiles");
4590
5260
  authCmd.command("list").description("List all stored profiles").action(authList);
4591
5261
  authCmd.command("switch [name]").description("Switch active profile").action(async (name) => authSwitch(name));
5262
+ program2.command("switch").description("Switch operator and environment context (live/test)").option("--operator <slug>", "Operator slug (required in non-interactive mode)").option("--env <environment>", "Environment: live or test").action(async (opts) => {
5263
+ const { switchContext: switchContext2 } = await Promise.resolve().then(() => (init_switch(), exports_switch));
5264
+ await switchContext2(opts);
5265
+ });
4592
5266
  program2.command("doctor").description("Run environment diagnostics").action(async () => {
4593
5267
  const { doctor: doctor2 } = await Promise.resolve().then(() => (init_doctor(), exports_doctor));
4594
5268
  await doctor2();
@@ -4598,7 +5272,7 @@ program2.command("dev").description("Start local dev server").option("-p, --port
4598
5272
  const { dev: dev2 } = await Promise.resolve().then(() => (init_dev(), exports_dev));
4599
5273
  await dev2(opts);
4600
5274
  });
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) => {
5275
+ program2.command("deploy").description("Build locally and deploy via Bagdock API").option("--target <target>", "Deploy target (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) => {
4602
5276
  const { deploy: deploy2 } = await Promise.resolve().then(() => (init_deploy(), exports_deploy));
4603
5277
  await deploy2(opts);
4604
5278
  });
@@ -4606,6 +5280,35 @@ program2.command("submit").description("Submit app for Bagdock marketplace revie
4606
5280
  const { submit: submit2 } = await Promise.resolve().then(() => (init_submit(), exports_submit));
4607
5281
  await submit2();
4608
5282
  });
5283
+ program2.command("validate").description("Run local pre-submission checks on bagdock.json and bundle").action(async () => {
5284
+ const { validate: validate2 } = await Promise.resolve().then(() => (init_validate(), exports_validate));
5285
+ await validate2();
5286
+ });
5287
+ var subCmd = program2.command("submission").description("Track marketplace submission status");
5288
+ 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) => {
5289
+ const { submissionList: submissionList2 } = await Promise.resolve().then(() => (init_submission(), exports_submission));
5290
+ await submissionList2(opts);
5291
+ });
5292
+ subCmd.command("status <id>").description("Fetch detailed review state for a submission").option("--app <slug>", "App slug").action(async (id, opts) => {
5293
+ const { submissionStatus: submissionStatus2 } = await Promise.resolve().then(() => (init_submission(), exports_submission));
5294
+ await submissionStatus2(id, opts);
5295
+ });
5296
+ subCmd.command("withdraw <id>").description("Cancel a pending submission before approval").option("--app <slug>", "App slug").action(async (id, opts) => {
5297
+ const { submissionWithdraw: submissionWithdraw2 } = await Promise.resolve().then(() => (init_submission(), exports_submission));
5298
+ await submissionWithdraw2(id, opts);
5299
+ });
5300
+ program2.command("open [slug]").description("Open project in the Bagdock dashboard").action(async (slug) => {
5301
+ const { open: open3 } = await Promise.resolve().then(() => (init_open2(), exports_open2));
5302
+ await open3(slug);
5303
+ });
5304
+ program2.command("inspect [slug]").description("Show deployment details and status for an app").action(async (slug) => {
5305
+ const { inspect: inspect2 } = await Promise.resolve().then(() => (init_inspect(), exports_inspect));
5306
+ await inspect2(slug);
5307
+ });
5308
+ program2.command("link").description("Link current directory to a Bagdock app or edge").option("--slug <slug>", "App slug (required in non-interactive mode)").option("--env <environment>", "Default environment for this link (live, test)").action(async (opts) => {
5309
+ const { link: link2 } = await Promise.resolve().then(() => (init_link(), exports_link));
5310
+ await link2(opts);
5311
+ });
4609
5312
  var envCmd = program2.command("env").description("Manage app environment variables");
4610
5313
  envCmd.command("list").description("List environment variables for this app").action(async () => {
4611
5314
  const { envList: envList2 } = await Promise.resolve().then(() => (init_env_cmd(), exports_env_cmd));
@@ -4619,6 +5322,10 @@ envCmd.command("remove <key>").description("Remove an environment variable").act
4619
5322
  const { envRemove: envRemove2 } = await Promise.resolve().then(() => (init_env_cmd(), exports_env_cmd));
4620
5323
  await envRemove2(key);
4621
5324
  });
5325
+ envCmd.command("pull [file]").description("Pull remote env var keys to a local .env file").action(async (file) => {
5326
+ const { envPull: envPull2 } = await Promise.resolve().then(() => (init_env_cmd(), exports_env_cmd));
5327
+ await envPull2(file);
5328
+ });
4622
5329
  var keysCmd = program2.command("keys").description("Manage operator API keys");
4623
5330
  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
5331
  const { keysCreate: keysCreate2 } = await Promise.resolve().then(() => (init_keys(), exports_keys));