@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/README.md +237 -1
- package/dist/bagdock.js +798 -91
- package/package.json +1 -1
- package/skill-evals/bagdock-cli/evals.json +99 -1
- package/skills/bagdock-cli/SKILL.md +50 -1
- package/skills/bagdock-cli/references/app-management.md +63 -0
- package/skills/bagdock-cli/references/marketplace.md +69 -0
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.
|
|
3351
|
-
console.log(" Operator:", source_default.bold(tokens.
|
|
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({
|
|
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 (
|
|
3404
|
-
console.log("Operator:", source_default.bold(
|
|
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
|
|
3432
|
-
|
|
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(
|
|
4112
|
+
function confirm(prompt2) {
|
|
3829
4113
|
return new Promise((resolve) => {
|
|
3830
4114
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
3831
|
-
rl.question(
|
|
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
|
-
|
|
3905
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
4885
|
+
const slug = resolveSlug2(opts.app);
|
|
4223
4886
|
const limit = opts.limit || "50";
|
|
4224
4887
|
status(`Fetching logs for ${slug}...`);
|
|
4225
|
-
const res = await
|
|
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 =
|
|
4916
|
+
const slug = resolveSlug2(opts.app);
|
|
4254
4917
|
status(`Fetching log entry ${id}...`);
|
|
4255
|
-
const res = await
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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.
|
|
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("--
|
|
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));
|