@bagdock/cli 0.4.0 → 0.6.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 +92 -0
- package/dist/bagdock.js +496 -240
- package/package.json +2 -2
- package/skill-evals/bagdock-cli/evals.json +43 -1
- package/skills/bagdock-cli/SKILL.md +12 -1
package/README.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
```
|
|
2
|
+
----++ ----++ ---+++
|
|
3
|
+
---+++ ---++ ---++
|
|
4
|
+
----+--- ----- --------- --------++ ------ ----- ----++-----
|
|
5
|
+
---------+ --------++----------++--------+++--------+ --------++---++---++++
|
|
6
|
+
---+++---++ ++++---++---+++---++---+++---++---+++---++---++---++------++++
|
|
7
|
+
----++ ---++--------++---++----++---++ ---++---++ ---+---++ -------++
|
|
8
|
+
----++----+---+++---++---++----++---++----++---++---+++--++ --------+---++
|
|
9
|
+
---------++--------+++--------+++--------++ -------+++ -------++---++----++
|
|
10
|
+
+++++++++ +++++++++- +++---++ ++++++++ ++++++ ++++++ ++++ ++++
|
|
11
|
+
--------+++
|
|
12
|
+
+++++++
|
|
13
|
+
```
|
|
14
|
+
|
|
1
15
|
# @bagdock/cli
|
|
2
16
|
|
|
3
17
|
The official CLI for Bagdock. Built for humans, AI agents, and CI/CD pipelines.
|
|
@@ -160,6 +174,42 @@ The CLI resolves your API key using the following priority chain:
|
|
|
160
174
|
|
|
161
175
|
If no key is found from any source, the CLI errors with code `auth_error`.
|
|
162
176
|
|
|
177
|
+
## Environment context
|
|
178
|
+
|
|
179
|
+
The CLI supports Stripe-style live/test mode switching. Login is universal — you authenticate once, then select which operator and environment to target.
|
|
180
|
+
|
|
181
|
+
### Operator + environment resolution
|
|
182
|
+
|
|
183
|
+
| Priority | Source | How to set |
|
|
184
|
+
|----------|--------|-----------|
|
|
185
|
+
| 1 (highest) | `--env` global flag | `bagdock --env test deploy` |
|
|
186
|
+
| 2 | `.bagdock/link.json` | `bagdock link --env test` |
|
|
187
|
+
| 3 | Profile stored value | `bagdock switch` |
|
|
188
|
+
| 4 (lowest) | Default | `live` |
|
|
189
|
+
|
|
190
|
+
For operator slug:
|
|
191
|
+
|
|
192
|
+
| Priority | Source | How to set |
|
|
193
|
+
|----------|--------|-----------|
|
|
194
|
+
| 1 (highest) | `BAGDOCK_OPERATOR` env var | `export BAGDOCK_OPERATOR=wisestorage` |
|
|
195
|
+
| 2 (lowest) | Profile stored value | `bagdock switch` or `bagdock login` |
|
|
196
|
+
|
|
197
|
+
### Typical workflow
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
# Login (universal identity)
|
|
201
|
+
bagdock login
|
|
202
|
+
|
|
203
|
+
# Select operator and environment
|
|
204
|
+
bagdock switch
|
|
205
|
+
|
|
206
|
+
# Or override per-command
|
|
207
|
+
bagdock --env test deploy --target staging
|
|
208
|
+
bagdock --env live apps list
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
All API requests include `X-Environment` and `X-Operator-Slug` headers, ensuring the backend resolves the correct tenant database.
|
|
212
|
+
|
|
163
213
|
## Commands
|
|
164
214
|
|
|
165
215
|
### `bagdock login`
|
|
@@ -246,6 +296,7 @@ bagdock doctor
|
|
|
246
296
|
|-------|------|------|------|
|
|
247
297
|
| CLI Version | Running latest | Update available or registry unreachable | — |
|
|
248
298
|
| API Key | Key found (shows masked key + source) | — | No key found |
|
|
299
|
+
| Operator Context | Operator + environment set | No operator selected | — |
|
|
249
300
|
| Project Config | Valid `bagdock.json` found | No config or incomplete | — |
|
|
250
301
|
| AI Agents | Lists detected agents (or none) | — | — |
|
|
251
302
|
|
|
@@ -300,6 +351,46 @@ Exits `0` when all checks pass or warn. Exits `1` if any check fails.
|
|
|
300
351
|
|
|
301
352
|
---
|
|
302
353
|
|
|
354
|
+
### `bagdock switch`
|
|
355
|
+
|
|
356
|
+
Switch operator and environment context. After login, use this to select which operator (live or sandbox) to target.
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
bagdock switch
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
#### Interactive mode
|
|
363
|
+
|
|
364
|
+
```
|
|
365
|
+
? Select operator:
|
|
366
|
+
1. WiseStorage (wisestorage)
|
|
367
|
+
2. Ardran REIT (ardran-reit)
|
|
368
|
+
> 1
|
|
369
|
+
|
|
370
|
+
? Select environment:
|
|
371
|
+
1. Live
|
|
372
|
+
2. Sandbox: crm-integration (default)
|
|
373
|
+
3. Sandbox: access-testing
|
|
374
|
+
> 2
|
|
375
|
+
|
|
376
|
+
Switched to wisestorage [test] (sandbox: crm-integration)
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
#### Non-interactive mode (CI/CD)
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
bagdock switch --operator wisestorage --env test
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
#### JSON output
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
bagdock switch --operator wisestorage --env live --json
|
|
389
|
+
# => {"operator":{"id":"opreg_xxx","slug":"wisestorage","name":"WiseStorage"},"environment":"live"}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
303
394
|
### Switch between profiles
|
|
304
395
|
|
|
305
396
|
If you work across multiple Bagdock operators, the CLI supports named profiles.
|
|
@@ -700,6 +791,7 @@ bagdock [global options] <command> [command options]
|
|
|
700
791
|
|------|-------------|
|
|
701
792
|
| `--api-key <key>` | Override API key for this invocation (takes highest priority) |
|
|
702
793
|
| `-p, --profile <name>` | Profile to use (overrides `BAGDOCK_PROFILE` env var) |
|
|
794
|
+
| `--env <live\|test>` | Override environment for this invocation |
|
|
703
795
|
| `--json` | Force JSON output even in interactive terminals |
|
|
704
796
|
| `-q, --quiet` | Suppress spinners and status output (implies `--json`) |
|
|
705
797
|
| `--version` | Print version and exit |
|
package/dist/bagdock.js
CHANGED
|
@@ -2081,110 +2081,6 @@ var require_commander = __commonJS((exports) => {
|
|
|
2081
2081
|
exports.InvalidOptionArgumentError = InvalidArgumentError;
|
|
2082
2082
|
});
|
|
2083
2083
|
|
|
2084
|
-
// src/config.ts
|
|
2085
|
-
import { homedir } from "os";
|
|
2086
|
-
import { join } from "path";
|
|
2087
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
2088
|
-
function setProfileOverride(name) {
|
|
2089
|
-
profileOverride = name;
|
|
2090
|
-
}
|
|
2091
|
-
function ensureConfigDir() {
|
|
2092
|
-
if (!existsSync(CONFIG_DIR)) {
|
|
2093
|
-
mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
2094
|
-
}
|
|
2095
|
-
}
|
|
2096
|
-
function loadStore() {
|
|
2097
|
-
try {
|
|
2098
|
-
if (!existsSync(CREDENTIALS_FILE)) {
|
|
2099
|
-
return { activeProfile: "default", profiles: {} };
|
|
2100
|
-
}
|
|
2101
|
-
const raw = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
|
|
2102
|
-
if (raw.accessToken && !raw.profiles) {
|
|
2103
|
-
const migrated = {
|
|
2104
|
-
activeProfile: "default",
|
|
2105
|
-
profiles: { default: raw }
|
|
2106
|
-
};
|
|
2107
|
-
saveStore(migrated);
|
|
2108
|
-
return migrated;
|
|
2109
|
-
}
|
|
2110
|
-
return raw;
|
|
2111
|
-
} catch {
|
|
2112
|
-
return { activeProfile: "default", profiles: {} };
|
|
2113
|
-
}
|
|
2114
|
-
}
|
|
2115
|
-
function saveStore(store) {
|
|
2116
|
-
ensureConfigDir();
|
|
2117
|
-
writeFileSync(CREDENTIALS_FILE, JSON.stringify(store, null, 2), { mode: 384 });
|
|
2118
|
-
}
|
|
2119
|
-
function resolveProfile() {
|
|
2120
|
-
if (profileOverride)
|
|
2121
|
-
return profileOverride;
|
|
2122
|
-
if (process.env.BAGDOCK_PROFILE)
|
|
2123
|
-
return process.env.BAGDOCK_PROFILE;
|
|
2124
|
-
return loadStore().activeProfile;
|
|
2125
|
-
}
|
|
2126
|
-
function loadCredentials() {
|
|
2127
|
-
const store = loadStore();
|
|
2128
|
-
const name = resolveProfile();
|
|
2129
|
-
return store.profiles[name] ?? null;
|
|
2130
|
-
}
|
|
2131
|
-
function saveCredentials(creds, profileName) {
|
|
2132
|
-
const store = loadStore();
|
|
2133
|
-
const name = profileName ?? resolveProfile();
|
|
2134
|
-
store.profiles[name] = creds;
|
|
2135
|
-
if (!store.activeProfile || Object.keys(store.profiles).length === 1) {
|
|
2136
|
-
store.activeProfile = name;
|
|
2137
|
-
}
|
|
2138
|
-
saveStore(store);
|
|
2139
|
-
}
|
|
2140
|
-
function clearCredentials() {
|
|
2141
|
-
const store = loadStore();
|
|
2142
|
-
const name = resolveProfile();
|
|
2143
|
-
delete store.profiles[name];
|
|
2144
|
-
if (store.activeProfile === name) {
|
|
2145
|
-
const remaining = Object.keys(store.profiles);
|
|
2146
|
-
store.activeProfile = remaining[0] ?? "default";
|
|
2147
|
-
}
|
|
2148
|
-
saveStore(store);
|
|
2149
|
-
}
|
|
2150
|
-
function listProfiles() {
|
|
2151
|
-
const store = loadStore();
|
|
2152
|
-
return Object.entries(store.profiles).map(([name, creds]) => ({
|
|
2153
|
-
name,
|
|
2154
|
-
email: creds.email,
|
|
2155
|
-
operatorId: creds.operatorId,
|
|
2156
|
-
active: name === store.activeProfile
|
|
2157
|
-
}));
|
|
2158
|
-
}
|
|
2159
|
-
function switchProfile(name) {
|
|
2160
|
-
const store = loadStore();
|
|
2161
|
-
if (!store.profiles[name])
|
|
2162
|
-
return false;
|
|
2163
|
-
store.activeProfile = name;
|
|
2164
|
-
saveStore(store);
|
|
2165
|
-
return true;
|
|
2166
|
-
}
|
|
2167
|
-
function getActiveProfileName() {
|
|
2168
|
-
return resolveProfile();
|
|
2169
|
-
}
|
|
2170
|
-
function loadBagdockJson(dir) {
|
|
2171
|
-
const file = join(dir, "bagdock.json");
|
|
2172
|
-
if (!existsSync(file))
|
|
2173
|
-
return null;
|
|
2174
|
-
try {
|
|
2175
|
-
return JSON.parse(readFileSync(file, "utf-8"));
|
|
2176
|
-
} catch {
|
|
2177
|
-
return null;
|
|
2178
|
-
}
|
|
2179
|
-
}
|
|
2180
|
-
var CONFIG_DIR, CREDENTIALS_FILE, API_BASE, DASHBOARD_BASE, profileOverride;
|
|
2181
|
-
var init_config = __esm(() => {
|
|
2182
|
-
CONFIG_DIR = join(homedir(), ".bagdock");
|
|
2183
|
-
CREDENTIALS_FILE = join(CONFIG_DIR, "credentials.json");
|
|
2184
|
-
API_BASE = process.env.BAGDOCK_API_URL ?? "https://api.bagdock.com";
|
|
2185
|
-
DASHBOARD_BASE = process.env.BAGDOCK_DASHBOARD_URL ?? "https://dashboard.bagdock.com";
|
|
2186
|
-
});
|
|
2187
|
-
|
|
2188
2084
|
// node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
2189
2085
|
function assembleStyles() {
|
|
2190
2086
|
const codes = new Map;
|
|
@@ -2674,6 +2570,196 @@ var init_source = __esm(() => {
|
|
|
2674
2570
|
source_default = chalk;
|
|
2675
2571
|
});
|
|
2676
2572
|
|
|
2573
|
+
// src/config.ts
|
|
2574
|
+
import { homedir } from "os";
|
|
2575
|
+
import { join } from "path";
|
|
2576
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
2577
|
+
function getApiBase() {
|
|
2578
|
+
return _apiBase;
|
|
2579
|
+
}
|
|
2580
|
+
function getDashboardBase() {
|
|
2581
|
+
return _dashboardBase;
|
|
2582
|
+
}
|
|
2583
|
+
function loadLocalEnv() {
|
|
2584
|
+
const envPath = join(process.cwd(), ".env.local");
|
|
2585
|
+
if (!existsSync(envPath)) {
|
|
2586
|
+
console.error(source_default.red("No .env.local found in"), source_default.bold(process.cwd()));
|
|
2587
|
+
console.error(source_default.dim("Create one with BAGDOCK_API_URL=https://your-ngrok-url"));
|
|
2588
|
+
process.exit(1);
|
|
2589
|
+
}
|
|
2590
|
+
const vars = {};
|
|
2591
|
+
const lines = readFileSync(envPath, "utf-8").split(`
|
|
2592
|
+
`);
|
|
2593
|
+
for (const line of lines) {
|
|
2594
|
+
const trimmed = line.trim();
|
|
2595
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
2596
|
+
continue;
|
|
2597
|
+
const eq = trimmed.indexOf("=");
|
|
2598
|
+
if (eq === -1)
|
|
2599
|
+
continue;
|
|
2600
|
+
vars[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1).trim();
|
|
2601
|
+
}
|
|
2602
|
+
const apiUrl = vars["BAGDOCK_API_URL"];
|
|
2603
|
+
if (!apiUrl) {
|
|
2604
|
+
console.error(source_default.red("BAGDOCK_API_URL not found in .env.local"));
|
|
2605
|
+
console.error(source_default.dim("Add BAGDOCK_API_URL=https://your-ngrok-url to .env.local"));
|
|
2606
|
+
process.exit(1);
|
|
2607
|
+
}
|
|
2608
|
+
_apiBase = apiUrl;
|
|
2609
|
+
if (vars["BAGDOCK_DASHBOARD_URL"]) {
|
|
2610
|
+
_dashboardBase = vars["BAGDOCK_DASHBOARD_URL"];
|
|
2611
|
+
}
|
|
2612
|
+
console.log(source_default.dim(`Using local env → ${_apiBase}`));
|
|
2613
|
+
}
|
|
2614
|
+
function setProfileOverride(name) {
|
|
2615
|
+
profileOverride = name;
|
|
2616
|
+
}
|
|
2617
|
+
function setEnvironmentOverride(env2) {
|
|
2618
|
+
envOverride = env2;
|
|
2619
|
+
}
|
|
2620
|
+
function getEnvironmentOverride() {
|
|
2621
|
+
return envOverride;
|
|
2622
|
+
}
|
|
2623
|
+
function ensureConfigDir() {
|
|
2624
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
2625
|
+
mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
function loadStore() {
|
|
2629
|
+
try {
|
|
2630
|
+
if (!existsSync(CREDENTIALS_FILE)) {
|
|
2631
|
+
return { activeProfile: "default", profiles: {} };
|
|
2632
|
+
}
|
|
2633
|
+
const raw = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
|
|
2634
|
+
if (raw.accessToken && !raw.profiles) {
|
|
2635
|
+
const migrated = {
|
|
2636
|
+
activeProfile: "default",
|
|
2637
|
+
profiles: { default: raw }
|
|
2638
|
+
};
|
|
2639
|
+
saveStore(migrated);
|
|
2640
|
+
return migrated;
|
|
2641
|
+
}
|
|
2642
|
+
return raw;
|
|
2643
|
+
} catch {
|
|
2644
|
+
return { activeProfile: "default", profiles: {} };
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
function saveStore(store) {
|
|
2648
|
+
ensureConfigDir();
|
|
2649
|
+
writeFileSync(CREDENTIALS_FILE, JSON.stringify(store, null, 2), { mode: 384 });
|
|
2650
|
+
}
|
|
2651
|
+
function resolveProfile() {
|
|
2652
|
+
if (profileOverride)
|
|
2653
|
+
return profileOverride;
|
|
2654
|
+
if (process.env.BAGDOCK_PROFILE)
|
|
2655
|
+
return process.env.BAGDOCK_PROFILE;
|
|
2656
|
+
return loadStore().activeProfile;
|
|
2657
|
+
}
|
|
2658
|
+
function loadCredentials() {
|
|
2659
|
+
const store = loadStore();
|
|
2660
|
+
const name = resolveProfile();
|
|
2661
|
+
return store.profiles[name] ?? null;
|
|
2662
|
+
}
|
|
2663
|
+
function saveCredentials(creds, profileName) {
|
|
2664
|
+
const store = loadStore();
|
|
2665
|
+
const name = profileName ?? resolveProfile();
|
|
2666
|
+
store.profiles[name] = creds;
|
|
2667
|
+
if (!store.activeProfile || Object.keys(store.profiles).length === 1) {
|
|
2668
|
+
store.activeProfile = name;
|
|
2669
|
+
}
|
|
2670
|
+
saveStore(store);
|
|
2671
|
+
}
|
|
2672
|
+
function clearCredentials() {
|
|
2673
|
+
const store = loadStore();
|
|
2674
|
+
const name = resolveProfile();
|
|
2675
|
+
delete store.profiles[name];
|
|
2676
|
+
if (store.activeProfile === name) {
|
|
2677
|
+
const remaining = Object.keys(store.profiles);
|
|
2678
|
+
store.activeProfile = remaining[0] ?? "default";
|
|
2679
|
+
}
|
|
2680
|
+
saveStore(store);
|
|
2681
|
+
}
|
|
2682
|
+
function listProfiles() {
|
|
2683
|
+
const store = loadStore();
|
|
2684
|
+
return Object.entries(store.profiles).map(([name, creds]) => ({
|
|
2685
|
+
name,
|
|
2686
|
+
email: creds.email,
|
|
2687
|
+
operatorId: creds.operatorId,
|
|
2688
|
+
operatorSlug: creds.operatorSlug,
|
|
2689
|
+
environment: creds.environment,
|
|
2690
|
+
active: name === store.activeProfile
|
|
2691
|
+
}));
|
|
2692
|
+
}
|
|
2693
|
+
function switchProfile(name) {
|
|
2694
|
+
const store = loadStore();
|
|
2695
|
+
if (!store.profiles[name])
|
|
2696
|
+
return false;
|
|
2697
|
+
store.activeProfile = name;
|
|
2698
|
+
saveStore(store);
|
|
2699
|
+
return true;
|
|
2700
|
+
}
|
|
2701
|
+
function getActiveProfileName() {
|
|
2702
|
+
return resolveProfile();
|
|
2703
|
+
}
|
|
2704
|
+
function resolveLinkEnvironment() {
|
|
2705
|
+
try {
|
|
2706
|
+
const p = join(process.cwd(), ".bagdock", "link.json");
|
|
2707
|
+
if (!existsSync(p))
|
|
2708
|
+
return;
|
|
2709
|
+
const data = JSON.parse(readFileSync(p, "utf-8"));
|
|
2710
|
+
if (data.environment === "live" || data.environment === "test")
|
|
2711
|
+
return data.environment;
|
|
2712
|
+
return;
|
|
2713
|
+
} catch {
|
|
2714
|
+
return;
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
function resolveEnvironment() {
|
|
2718
|
+
if (envOverride)
|
|
2719
|
+
return envOverride;
|
|
2720
|
+
const envVar = process.env.BAGDOCK_ENV;
|
|
2721
|
+
if (envVar === "test" || envVar === "live")
|
|
2722
|
+
return envVar;
|
|
2723
|
+
const creds = loadCredentials();
|
|
2724
|
+
return creds?.environment ?? "live";
|
|
2725
|
+
}
|
|
2726
|
+
function resolveOperatorSlug() {
|
|
2727
|
+
const envVar = process.env.BAGDOCK_OPERATOR;
|
|
2728
|
+
if (envVar)
|
|
2729
|
+
return envVar;
|
|
2730
|
+
const creds = loadCredentials();
|
|
2731
|
+
return creds?.operatorSlug;
|
|
2732
|
+
}
|
|
2733
|
+
function updateProfileContext(operatorId, operatorSlug, environment) {
|
|
2734
|
+
const creds = loadCredentials();
|
|
2735
|
+
if (!creds)
|
|
2736
|
+
return;
|
|
2737
|
+
saveCredentials({
|
|
2738
|
+
...creds,
|
|
2739
|
+
operatorId,
|
|
2740
|
+
operatorSlug,
|
|
2741
|
+
environment
|
|
2742
|
+
});
|
|
2743
|
+
}
|
|
2744
|
+
function loadBagdockJson(dir) {
|
|
2745
|
+
const file = join(dir, "bagdock.json");
|
|
2746
|
+
if (!existsSync(file))
|
|
2747
|
+
return null;
|
|
2748
|
+
try {
|
|
2749
|
+
return JSON.parse(readFileSync(file, "utf-8"));
|
|
2750
|
+
} catch {
|
|
2751
|
+
return null;
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
var CONFIG_DIR, CREDENTIALS_FILE, _apiBase, _dashboardBase, profileOverride, envOverride;
|
|
2755
|
+
var init_config = __esm(() => {
|
|
2756
|
+
init_source();
|
|
2757
|
+
CONFIG_DIR = join(homedir(), ".bagdock");
|
|
2758
|
+
CREDENTIALS_FILE = join(CONFIG_DIR, "credentials.json");
|
|
2759
|
+
_apiBase = process.env.BAGDOCK_API_URL ?? "https://api.bagdock.com";
|
|
2760
|
+
_dashboardBase = process.env.BAGDOCK_DASHBOARD_URL ?? "https://dashboard.bagdock.com";
|
|
2761
|
+
});
|
|
2762
|
+
|
|
2677
2763
|
// src/output.ts
|
|
2678
2764
|
function setOutputMode(opts) {
|
|
2679
2765
|
quiet = !!opts.quiet;
|
|
@@ -3303,7 +3389,7 @@ async function login() {
|
|
|
3303
3389
|
console.log(source_default.cyan(`
|
|
3304
3390
|
Requesting device authorization...
|
|
3305
3391
|
`));
|
|
3306
|
-
const deviceRes = await fetch(`${
|
|
3392
|
+
const deviceRes = await fetch(`${getApiBase()}/oauth2/device/authorize`, {
|
|
3307
3393
|
method: "POST",
|
|
3308
3394
|
headers: { "Content-Type": "application/json" },
|
|
3309
3395
|
body: JSON.stringify({ client_id: CLIENT_ID, scope: "developer:read developer:write" })
|
|
@@ -3325,7 +3411,7 @@ Requesting device authorization...
|
|
|
3325
3411
|
const startedAt = Date.now();
|
|
3326
3412
|
while (Date.now() - startedAt < MAX_POLL_DURATION_MS) {
|
|
3327
3413
|
await sleep(pollInterval);
|
|
3328
|
-
const tokenRes = await fetch(`${
|
|
3414
|
+
const tokenRes = await fetch(`${getApiBase()}/oauth2/token`, {
|
|
3329
3415
|
method: "POST",
|
|
3330
3416
|
headers: { "Content-Type": "application/json" },
|
|
3331
3417
|
body: JSON.stringify({
|
|
@@ -3341,15 +3427,27 @@ Requesting device authorization...
|
|
|
3341
3427
|
refreshToken: tokens.refresh_token,
|
|
3342
3428
|
expiresAt: tokens.expires_in ? Date.now() + tokens.expires_in * 1000 : undefined,
|
|
3343
3429
|
email: tokens.email,
|
|
3344
|
-
operatorId: tokens.operator_id
|
|
3430
|
+
operatorId: tokens.operator_id,
|
|
3431
|
+
operatorSlug: tokens.operator_slug,
|
|
3432
|
+
environment: "live"
|
|
3345
3433
|
});
|
|
3346
3434
|
console.log(source_default.green(`
|
|
3347
3435
|
Logged in successfully!`));
|
|
3348
3436
|
if (tokens.email)
|
|
3349
3437
|
console.log(" Email:", source_default.bold(tokens.email));
|
|
3350
|
-
if (tokens.
|
|
3351
|
-
console.log(" Operator:", source_default.bold(tokens.
|
|
3438
|
+
if (tokens.operator_slug) {
|
|
3439
|
+
console.log(" Operator:", source_default.bold(tokens.operator_slug));
|
|
3440
|
+
} else if (tokens.operator_id) {
|
|
3441
|
+
console.log(" Operator ID:", source_default.bold(tokens.operator_id));
|
|
3442
|
+
}
|
|
3443
|
+
console.log(" Environment:", source_default.bold("live"));
|
|
3352
3444
|
console.log(" Profile:", source_default.bold(getActiveProfileName()));
|
|
3445
|
+
if (!tokens.operator_slug) {
|
|
3446
|
+
await resolveOperatorAfterLogin(tokens.access_token);
|
|
3447
|
+
} else {
|
|
3448
|
+
console.log();
|
|
3449
|
+
console.log(source_default.dim(" Tip: run"), source_default.cyan("bagdock switch"), source_default.dim("to change operator or environment."));
|
|
3450
|
+
}
|
|
3353
3451
|
return;
|
|
3354
3452
|
}
|
|
3355
3453
|
const error = await tokenRes.json().catch(() => ({ error: "unknown" }));
|
|
@@ -3388,7 +3486,7 @@ async function whoami() {
|
|
|
3388
3486
|
process.exit(1);
|
|
3389
3487
|
}
|
|
3390
3488
|
try {
|
|
3391
|
-
const res = await fetch(`${
|
|
3489
|
+
const res = await fetch(`${getApiBase()}/v1/auth/me`, {
|
|
3392
3490
|
headers: { Authorization: `Bearer ${token}` }
|
|
3393
3491
|
});
|
|
3394
3492
|
if (!res.ok) {
|
|
@@ -3396,12 +3494,22 @@ async function whoami() {
|
|
|
3396
3494
|
process.exit(1);
|
|
3397
3495
|
}
|
|
3398
3496
|
const user = await res.json();
|
|
3497
|
+
const creds = loadCredentials();
|
|
3399
3498
|
if (isJsonMode()) {
|
|
3400
|
-
outputSuccess({
|
|
3499
|
+
outputSuccess({
|
|
3500
|
+
...user,
|
|
3501
|
+
profile: getActiveProfileName(),
|
|
3502
|
+
operator_slug: creds?.operatorSlug,
|
|
3503
|
+
environment: creds?.environment ?? "live"
|
|
3504
|
+
});
|
|
3401
3505
|
} else {
|
|
3402
3506
|
console.log(source_default.green("Logged in as"), source_default.bold(user.email));
|
|
3403
|
-
if (
|
|
3404
|
-
console.log("Operator:", source_default.bold(
|
|
3507
|
+
if (creds?.operatorSlug) {
|
|
3508
|
+
console.log("Operator:", source_default.bold(creds.operatorSlug));
|
|
3509
|
+
} else if (user.operator_id) {
|
|
3510
|
+
console.log("Operator ID:", source_default.bold(user.operator_id));
|
|
3511
|
+
}
|
|
3512
|
+
console.log("Environment:", source_default.bold(creds?.environment ?? "live"));
|
|
3405
3513
|
if (user.name)
|
|
3406
3514
|
console.log("Name:", user.name);
|
|
3407
3515
|
console.log("Profile:", source_default.bold(getActiveProfileName()));
|
|
@@ -3428,8 +3536,9 @@ async function authList() {
|
|
|
3428
3536
|
const marker = p.active ? source_default.green("* ") : " ";
|
|
3429
3537
|
const label = p.active ? source_default.bold(p.name) : p.name;
|
|
3430
3538
|
const email = p.email ? source_default.dim(` (${p.email})`) : "";
|
|
3431
|
-
const
|
|
3432
|
-
|
|
3539
|
+
const operator = p.operatorSlug ? source_default.dim(` ${p.operatorSlug}`) : p.operatorId ? source_default.dim(` ${p.operatorId}`) : "";
|
|
3540
|
+
const env2 = p.environment ? source_default.dim(` [${p.environment}]`) : "";
|
|
3541
|
+
console.log(` ${marker}${label}${email}${operator}${env2}`);
|
|
3433
3542
|
}
|
|
3434
3543
|
console.log();
|
|
3435
3544
|
}
|
|
@@ -3482,6 +3591,28 @@ Available profiles:
|
|
|
3482
3591
|
process.exit(1);
|
|
3483
3592
|
}
|
|
3484
3593
|
}
|
|
3594
|
+
async function resolveOperatorAfterLogin(accessToken) {
|
|
3595
|
+
try {
|
|
3596
|
+
const res = await fetch(`${getApiBase()}/api/v1/me/operators`, {
|
|
3597
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
3598
|
+
});
|
|
3599
|
+
if (!res.ok)
|
|
3600
|
+
return;
|
|
3601
|
+
const body = await res.json();
|
|
3602
|
+
const operators = body.data ?? [];
|
|
3603
|
+
if (operators.length === 1) {
|
|
3604
|
+
const op = operators[0];
|
|
3605
|
+
updateProfileContext(op.id, op.slug, "live");
|
|
3606
|
+
console.log(" Operator:", source_default.bold(`${op.name} (${op.slug})`));
|
|
3607
|
+
console.log();
|
|
3608
|
+
console.log(source_default.dim(" Tip: run"), source_default.cyan("bagdock switch"), source_default.dim("to change environment to test/sandbox."));
|
|
3609
|
+
} else if (operators.length > 1) {
|
|
3610
|
+
console.log();
|
|
3611
|
+
console.log(source_default.dim(` You have access to ${operators.length} operators.`));
|
|
3612
|
+
console.log(source_default.dim(" Run"), source_default.cyan("bagdock switch"), source_default.dim("to select one."));
|
|
3613
|
+
}
|
|
3614
|
+
} catch {}
|
|
3615
|
+
}
|
|
3485
3616
|
function sleep(ms) {
|
|
3486
3617
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3487
3618
|
}
|
|
@@ -3492,6 +3623,175 @@ var init_auth = __esm(() => {
|
|
|
3492
3623
|
init_output();
|
|
3493
3624
|
});
|
|
3494
3625
|
|
|
3626
|
+
// src/api.ts
|
|
3627
|
+
function resolveFullEnvironment() {
|
|
3628
|
+
const flagOverride = getEnvironmentOverride();
|
|
3629
|
+
if (flagOverride)
|
|
3630
|
+
return flagOverride;
|
|
3631
|
+
const linkEnv = resolveLinkEnvironment();
|
|
3632
|
+
if (linkEnv)
|
|
3633
|
+
return linkEnv;
|
|
3634
|
+
return resolveEnvironment();
|
|
3635
|
+
}
|
|
3636
|
+
async function apiFetch(path2, init2) {
|
|
3637
|
+
const token = getAuthToken();
|
|
3638
|
+
if (!token) {
|
|
3639
|
+
outputError("auth_error", "Not authenticated. Run bagdock login or set BAGDOCK_API_KEY.");
|
|
3640
|
+
process.exit(1);
|
|
3641
|
+
}
|
|
3642
|
+
const env2 = resolveFullEnvironment();
|
|
3643
|
+
const opSlug = resolveOperatorSlug();
|
|
3644
|
+
const headers = new Headers(init2?.headers);
|
|
3645
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
3646
|
+
headers.set("X-Environment", env2);
|
|
3647
|
+
if (opSlug)
|
|
3648
|
+
headers.set("X-Operator-Slug", opSlug);
|
|
3649
|
+
return fetch(`${getApiBase()}${path2}`, { ...init2, headers });
|
|
3650
|
+
}
|
|
3651
|
+
async function apiFetchJson(path2, method, body) {
|
|
3652
|
+
return apiFetch(path2, {
|
|
3653
|
+
method,
|
|
3654
|
+
headers: { "Content-Type": "application/json" },
|
|
3655
|
+
body: JSON.stringify(body)
|
|
3656
|
+
});
|
|
3657
|
+
}
|
|
3658
|
+
var init_api = __esm(() => {
|
|
3659
|
+
init_config();
|
|
3660
|
+
init_auth();
|
|
3661
|
+
init_output();
|
|
3662
|
+
});
|
|
3663
|
+
|
|
3664
|
+
// src/switch.ts
|
|
3665
|
+
var exports_switch = {};
|
|
3666
|
+
__export(exports_switch, {
|
|
3667
|
+
switchContext: () => switchContext
|
|
3668
|
+
});
|
|
3669
|
+
async function fetchOperators() {
|
|
3670
|
+
const res = await apiFetch("/api/v1/me/operators");
|
|
3671
|
+
if (!res.ok) {
|
|
3672
|
+
const err = await res.json().catch(() => ({}));
|
|
3673
|
+
throw new Error(err?.error?.message || `API returned ${res.status}`);
|
|
3674
|
+
}
|
|
3675
|
+
const body = await res.json();
|
|
3676
|
+
return body.data ?? [];
|
|
3677
|
+
}
|
|
3678
|
+
async function fetchSandboxes() {
|
|
3679
|
+
const res = await apiFetch("/api/v1/me/sandboxes");
|
|
3680
|
+
if (!res.ok)
|
|
3681
|
+
return [];
|
|
3682
|
+
const body = await res.json();
|
|
3683
|
+
return body.data ?? [];
|
|
3684
|
+
}
|
|
3685
|
+
function prompt(question) {
|
|
3686
|
+
const readline = __require("readline");
|
|
3687
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
3688
|
+
return new Promise((resolve) => rl.question(question, (answer) => {
|
|
3689
|
+
rl.close();
|
|
3690
|
+
resolve(answer.trim());
|
|
3691
|
+
}));
|
|
3692
|
+
}
|
|
3693
|
+
async function switchContext(opts) {
|
|
3694
|
+
status("Fetching your operators...");
|
|
3695
|
+
let operators;
|
|
3696
|
+
try {
|
|
3697
|
+
operators = await fetchOperators();
|
|
3698
|
+
} catch (err) {
|
|
3699
|
+
outputError("api_error", `Failed to list operators: ${err.message}`);
|
|
3700
|
+
return;
|
|
3701
|
+
}
|
|
3702
|
+
if (!operators.length) {
|
|
3703
|
+
outputError("no_operators", "No operators found for your account.");
|
|
3704
|
+
return;
|
|
3705
|
+
}
|
|
3706
|
+
let selected;
|
|
3707
|
+
if (opts.operator) {
|
|
3708
|
+
const match = operators.find((o) => o.slug === opts.operator || o.id === opts.operator);
|
|
3709
|
+
if (!match) {
|
|
3710
|
+
outputError("not_found", `Operator "${opts.operator}" not found. Available: ${operators.map((o) => o.slug).join(", ")}`);
|
|
3711
|
+
return;
|
|
3712
|
+
}
|
|
3713
|
+
selected = match;
|
|
3714
|
+
} else if (operators.length === 1) {
|
|
3715
|
+
selected = operators[0];
|
|
3716
|
+
if (!isJsonMode()) {
|
|
3717
|
+
console.log(source_default.dim(` Auto-selected: ${selected.name} (${selected.slug})`));
|
|
3718
|
+
}
|
|
3719
|
+
} else if (!process.stdout.isTTY || isJsonMode()) {
|
|
3720
|
+
outputError("operator_required", `Multiple operators available. Pass --operator <slug>. Available: ${operators.map((o) => o.slug).join(", ")}`);
|
|
3721
|
+
return;
|
|
3722
|
+
} else {
|
|
3723
|
+
console.log(source_default.bold(`
|
|
3724
|
+
Select operator:
|
|
3725
|
+
`));
|
|
3726
|
+
operators.forEach((op, i) => {
|
|
3727
|
+
console.log(` ${source_default.cyan(String(i + 1))} ${op.name} ${source_default.dim(`(${op.slug})`)}`);
|
|
3728
|
+
});
|
|
3729
|
+
console.log();
|
|
3730
|
+
const answer = await prompt(" > ");
|
|
3731
|
+
const idx = parseInt(answer, 10) - 1;
|
|
3732
|
+
if (isNaN(idx) || idx < 0 || idx >= operators.length) {
|
|
3733
|
+
outputError("invalid_selection", "Invalid selection.");
|
|
3734
|
+
return;
|
|
3735
|
+
}
|
|
3736
|
+
selected = operators[idx];
|
|
3737
|
+
}
|
|
3738
|
+
let environment = "live";
|
|
3739
|
+
if (opts.env) {
|
|
3740
|
+
if (opts.env !== "live" && opts.env !== "test") {
|
|
3741
|
+
outputError("invalid_env", 'Environment must be "live" or "test".');
|
|
3742
|
+
return;
|
|
3743
|
+
}
|
|
3744
|
+
environment = opts.env;
|
|
3745
|
+
} else if (process.stdout.isTTY && !isJsonMode()) {
|
|
3746
|
+
status("Fetching sandboxes...");
|
|
3747
|
+
updateProfileContext(selected.id, selected.slug, "live");
|
|
3748
|
+
const sandboxes = await fetchSandboxes();
|
|
3749
|
+
console.log(source_default.bold(`
|
|
3750
|
+
Select environment:
|
|
3751
|
+
`));
|
|
3752
|
+
console.log(` ${source_default.cyan("1")} Live`);
|
|
3753
|
+
if (sandboxes.length > 0) {
|
|
3754
|
+
sandboxes.forEach((sb, i) => {
|
|
3755
|
+
const tag = sb.is_default ? source_default.dim(" (default)") : "";
|
|
3756
|
+
console.log(` ${source_default.cyan(String(i + 2))} Sandbox: ${sb.name}${tag}`);
|
|
3757
|
+
});
|
|
3758
|
+
} else {
|
|
3759
|
+
console.log(` ${source_default.cyan("2")} Test ${source_default.dim("(default sandbox)")}`);
|
|
3760
|
+
}
|
|
3761
|
+
console.log();
|
|
3762
|
+
const envAnswer = await prompt(" > ");
|
|
3763
|
+
const envIdx = parseInt(envAnswer, 10);
|
|
3764
|
+
if (envIdx === 1) {
|
|
3765
|
+
environment = "live";
|
|
3766
|
+
} else if (sandboxes.length > 0 && envIdx >= 2 && envIdx <= sandboxes.length + 1) {
|
|
3767
|
+
environment = "test";
|
|
3768
|
+
} else if (envIdx === 2 && sandboxes.length === 0) {
|
|
3769
|
+
environment = "test";
|
|
3770
|
+
} else {
|
|
3771
|
+
outputError("invalid_selection", "Invalid selection.");
|
|
3772
|
+
return;
|
|
3773
|
+
}
|
|
3774
|
+
}
|
|
3775
|
+
updateProfileContext(selected.id, selected.slug, environment);
|
|
3776
|
+
if (isJsonMode()) {
|
|
3777
|
+
outputSuccess({
|
|
3778
|
+
operator: { id: selected.id, slug: selected.slug, name: selected.name },
|
|
3779
|
+
environment
|
|
3780
|
+
});
|
|
3781
|
+
} else {
|
|
3782
|
+
const envLabel = environment === "test" ? source_default.yellow("test") : source_default.green("live");
|
|
3783
|
+
console.log();
|
|
3784
|
+
console.log(source_default.green(" Switched to"), source_default.bold(selected.name), source_default.dim(`(${selected.slug})`), `[${envLabel}]`);
|
|
3785
|
+
console.log();
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
var init_switch = __esm(() => {
|
|
3789
|
+
init_source();
|
|
3790
|
+
init_config();
|
|
3791
|
+
init_api();
|
|
3792
|
+
init_output();
|
|
3793
|
+
});
|
|
3794
|
+
|
|
3495
3795
|
// src/doctor.ts
|
|
3496
3796
|
var exports_doctor = {};
|
|
3497
3797
|
__export(exports_doctor, {
|
|
@@ -3579,11 +3879,30 @@ function checkAgents() {
|
|
|
3579
3879
|
}
|
|
3580
3880
|
return { name: "AI Agents", status: "pass", message: `Detected: ${detected.join(", ")}` };
|
|
3581
3881
|
}
|
|
3882
|
+
function checkOperatorContext() {
|
|
3883
|
+
const creds = loadCredentials();
|
|
3884
|
+
const slug = resolveOperatorSlug();
|
|
3885
|
+
const env2 = resolveEnvironment();
|
|
3886
|
+
const profile = getActiveProfileName();
|
|
3887
|
+
if (!slug) {
|
|
3888
|
+
return {
|
|
3889
|
+
name: "Operator Context",
|
|
3890
|
+
status: "warn",
|
|
3891
|
+
message: `No operator selected (profile: ${profile}). Run \`bagdock switch\` to set one.`
|
|
3892
|
+
};
|
|
3893
|
+
}
|
|
3894
|
+
return {
|
|
3895
|
+
name: "Operator Context",
|
|
3896
|
+
status: "pass",
|
|
3897
|
+
message: `${slug} [${env2}] (profile: ${profile})`
|
|
3898
|
+
};
|
|
3899
|
+
}
|
|
3582
3900
|
async function doctor() {
|
|
3583
3901
|
const checks = [];
|
|
3584
3902
|
if (isJsonMode()) {
|
|
3585
3903
|
checks.push(await checkVersion());
|
|
3586
3904
|
checks.push(checkAuth());
|
|
3905
|
+
checks.push(checkOperatorContext());
|
|
3587
3906
|
checks.push(checkProjectConfig());
|
|
3588
3907
|
checks.push(checkAgents());
|
|
3589
3908
|
const ok = checks.every((c) => c.status !== "fail");
|
|
@@ -3601,6 +3920,9 @@ async function doctor() {
|
|
|
3601
3920
|
const authCheck = checkAuth();
|
|
3602
3921
|
checks.push(authCheck);
|
|
3603
3922
|
console.log(` ${ICON[authCheck.status]} ${authCheck.name}: ${authCheck.message}`);
|
|
3923
|
+
const contextCheck = checkOperatorContext();
|
|
3924
|
+
checks.push(contextCheck);
|
|
3925
|
+
console.log(` ${ICON[contextCheck.status]} ${contextCheck.name}: ${contextCheck.message}`);
|
|
3604
3926
|
const configCheck = checkProjectConfig();
|
|
3605
3927
|
checks.push(configCheck);
|
|
3606
3928
|
console.log(` ${ICON[configCheck.status]} ${configCheck.name}: ${configCheck.message}`);
|
|
@@ -3778,7 +4100,7 @@ Deploying ${config.slug}@${config.version} → ${envLabel}
|
|
|
3778
4100
|
compatibilityDate: config.compatibilityDate ?? "2024-09-23"
|
|
3779
4101
|
}));
|
|
3780
4102
|
try {
|
|
3781
|
-
const res = await fetch(`${
|
|
4103
|
+
const res = await fetch(`${getApiBase()}/api/v1/developer/apps/${config.slug}/deploy`, {
|
|
3782
4104
|
method: "POST",
|
|
3783
4105
|
headers: {
|
|
3784
4106
|
Authorization: `Bearer ${token}`
|
|
@@ -3825,10 +4147,10 @@ Deploying ${config.slug}@${config.version} → ${envLabel}
|
|
|
3825
4147
|
process.exit(1);
|
|
3826
4148
|
}
|
|
3827
4149
|
}
|
|
3828
|
-
function confirm(
|
|
4150
|
+
function confirm(prompt2) {
|
|
3829
4151
|
return new Promise((resolve) => {
|
|
3830
4152
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
3831
|
-
rl.question(
|
|
4153
|
+
rl.question(prompt2, (answer) => {
|
|
3832
4154
|
rl.close();
|
|
3833
4155
|
resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
3834
4156
|
});
|
|
@@ -3860,7 +4182,7 @@ async function submit() {
|
|
|
3860
4182
|
Submitting ${source_default.bold(config.slug)} for marketplace review...
|
|
3861
4183
|
`));
|
|
3862
4184
|
try {
|
|
3863
|
-
const res = await fetch(`${
|
|
4185
|
+
const res = await fetch(`${getApiBase()}/api/v1/developer/apps/${config.slug}/submit`, {
|
|
3864
4186
|
method: "POST",
|
|
3865
4187
|
headers: {
|
|
3866
4188
|
Authorization: `Bearer ${token}`,
|
|
@@ -3931,6 +4253,7 @@ function requireSlug(slugArg) {
|
|
|
3931
4253
|
}
|
|
3932
4254
|
async function link(opts) {
|
|
3933
4255
|
let slug = opts.slug;
|
|
4256
|
+
const linkEnv = opts.env === "test" || opts.env === "live" ? opts.env : undefined;
|
|
3934
4257
|
if (!slug) {
|
|
3935
4258
|
const config = loadBagdockJson(process.cwd());
|
|
3936
4259
|
if (config?.slug) {
|
|
@@ -3946,9 +4269,7 @@ async function link(opts) {
|
|
|
3946
4269
|
}
|
|
3947
4270
|
status("Fetching your apps...");
|
|
3948
4271
|
try {
|
|
3949
|
-
const res = await
|
|
3950
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
3951
|
-
});
|
|
4272
|
+
const res = await apiFetch("/api/v1/developer/apps");
|
|
3952
4273
|
if (!res.ok)
|
|
3953
4274
|
throw new Error(`API returned ${res.status}`);
|
|
3954
4275
|
const { data } = await res.json();
|
|
@@ -3983,12 +4304,13 @@ Your apps:
|
|
|
3983
4304
|
const dir = join6(process.cwd(), LINK_DIR);
|
|
3984
4305
|
if (!existsSync6(dir))
|
|
3985
4306
|
mkdirSync3(dir, { recursive: true });
|
|
3986
|
-
const linkData = { slug, linkedAt: new Date().toISOString() };
|
|
4307
|
+
const linkData = { slug, environment: linkEnv, linkedAt: new Date().toISOString() };
|
|
3987
4308
|
writeFileSync4(join6(dir, LINK_FILE), JSON.stringify(linkData, null, 2));
|
|
3988
4309
|
if (isJsonMode()) {
|
|
3989
|
-
outputSuccess({ slug, path: join6(dir, LINK_FILE) });
|
|
4310
|
+
outputSuccess({ slug, environment: linkEnv, path: join6(dir, LINK_FILE) });
|
|
3990
4311
|
} else {
|
|
3991
|
-
|
|
4312
|
+
const envLabel = linkEnv ? ` [${linkEnv}]` : "";
|
|
4313
|
+
console.log(source_default.green(`Linked to ${source_default.bold(slug)}${envLabel}`));
|
|
3992
4314
|
console.log(source_default.dim(` Stored in ${LINK_DIR}/${LINK_FILE}`));
|
|
3993
4315
|
}
|
|
3994
4316
|
}
|
|
@@ -3997,6 +4319,7 @@ var init_link = __esm(() => {
|
|
|
3997
4319
|
init_source();
|
|
3998
4320
|
init_config();
|
|
3999
4321
|
init_auth();
|
|
4322
|
+
init_api();
|
|
4000
4323
|
init_output();
|
|
4001
4324
|
});
|
|
4002
4325
|
|
|
@@ -4098,22 +4421,11 @@ __export(exports_submission, {
|
|
|
4098
4421
|
submissionStatus: () => submissionStatus,
|
|
4099
4422
|
submissionList: () => submissionList
|
|
4100
4423
|
});
|
|
4101
|
-
function requireAuth() {
|
|
4102
|
-
const token = getAuthToken();
|
|
4103
|
-
if (!token) {
|
|
4104
|
-
outputError("auth_error", "Not authenticated. Run bagdock login or set BAGDOCK_API_KEY.");
|
|
4105
|
-
process.exit(1);
|
|
4106
|
-
}
|
|
4107
|
-
return token;
|
|
4108
|
-
}
|
|
4109
4424
|
async function submissionList(opts) {
|
|
4110
|
-
const token = requireAuth();
|
|
4111
4425
|
const slug = requireSlug(opts.app);
|
|
4112
4426
|
status(`Fetching submissions for ${slug}...`);
|
|
4113
4427
|
try {
|
|
4114
|
-
const res = await
|
|
4115
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
4116
|
-
});
|
|
4428
|
+
const res = await apiFetch(`/api/v1/developer/apps/${slug}/submissions`);
|
|
4117
4429
|
if (res.status === 404) {
|
|
4118
4430
|
outputError("not_found", `App "${slug}" not found or no submissions exist.`);
|
|
4119
4431
|
}
|
|
@@ -4144,13 +4456,10 @@ Submissions for ${slug}:
|
|
|
4144
4456
|
}
|
|
4145
4457
|
}
|
|
4146
4458
|
async function submissionStatus(id, opts) {
|
|
4147
|
-
const token = requireAuth();
|
|
4148
4459
|
const slug = requireSlug(opts.app);
|
|
4149
4460
|
status(`Fetching submission ${id}...`);
|
|
4150
4461
|
try {
|
|
4151
|
-
const res = await
|
|
4152
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
4153
|
-
});
|
|
4462
|
+
const res = await apiFetch(`/api/v1/developer/apps/${slug}/submissions/${id}`);
|
|
4154
4463
|
if (res.status === 404) {
|
|
4155
4464
|
outputError("not_found", `Submission "${id}" not found.`);
|
|
4156
4465
|
}
|
|
@@ -4184,13 +4493,11 @@ async function submissionStatus(id, opts) {
|
|
|
4184
4493
|
}
|
|
4185
4494
|
}
|
|
4186
4495
|
async function submissionWithdraw(id, opts) {
|
|
4187
|
-
const token = requireAuth();
|
|
4188
4496
|
const slug = requireSlug(opts.app);
|
|
4189
4497
|
status(`Withdrawing submission ${id}...`);
|
|
4190
4498
|
try {
|
|
4191
|
-
const res = await
|
|
4192
|
-
method: "POST"
|
|
4193
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
4499
|
+
const res = await apiFetch(`/api/v1/developer/apps/${slug}/submissions/${id}/withdraw`, {
|
|
4500
|
+
method: "POST"
|
|
4194
4501
|
});
|
|
4195
4502
|
if (res.status === 404) {
|
|
4196
4503
|
outputError("not_found", `App "${slug}" not found.`);
|
|
@@ -4215,8 +4522,7 @@ async function submissionWithdraw(id, opts) {
|
|
|
4215
4522
|
}
|
|
4216
4523
|
var init_submission = __esm(() => {
|
|
4217
4524
|
init_source();
|
|
4218
|
-
|
|
4219
|
-
init_auth();
|
|
4525
|
+
init_api();
|
|
4220
4526
|
init_output();
|
|
4221
4527
|
init_link();
|
|
4222
4528
|
});
|
|
@@ -4228,9 +4534,16 @@ __export(exports_open2, {
|
|
|
4228
4534
|
});
|
|
4229
4535
|
async function open2(slugArg) {
|
|
4230
4536
|
const slug = requireSlug(slugArg);
|
|
4231
|
-
const
|
|
4537
|
+
const operatorSlug = resolveOperatorSlug();
|
|
4538
|
+
const env2 = resolveEnvironment();
|
|
4539
|
+
if (!operatorSlug) {
|
|
4540
|
+
outputError("no_operator", "No operator context. Run bagdock switch or bagdock login to set one.");
|
|
4541
|
+
return;
|
|
4542
|
+
}
|
|
4543
|
+
const envSegment = env2 === "test" ? "/test" : "";
|
|
4544
|
+
const url = `${getDashboardBase()}/${operatorSlug}${envSegment}/developer/apps/${slug}`;
|
|
4232
4545
|
if (isJsonMode()) {
|
|
4233
|
-
outputSuccess({ url, slug });
|
|
4546
|
+
outputSuccess({ url, slug, operator: operatorSlug, environment: env2 });
|
|
4234
4547
|
return;
|
|
4235
4548
|
}
|
|
4236
4549
|
status(`Opening ${url}`);
|
|
@@ -4251,17 +4564,10 @@ __export(exports_inspect, {
|
|
|
4251
4564
|
inspect: () => inspect
|
|
4252
4565
|
});
|
|
4253
4566
|
async function inspect(slugArg) {
|
|
4254
|
-
const token = getAuthToken();
|
|
4255
|
-
if (!token) {
|
|
4256
|
-
outputError("auth_error", "Not authenticated. Run bagdock login or set BAGDOCK_API_KEY.");
|
|
4257
|
-
process.exit(1);
|
|
4258
|
-
}
|
|
4259
4567
|
const slug = requireSlug(slugArg);
|
|
4260
4568
|
status(`Inspecting ${slug}...`);
|
|
4261
4569
|
try {
|
|
4262
|
-
const res = await
|
|
4263
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
4264
|
-
});
|
|
4570
|
+
const res = await apiFetch(`/api/v1/developer/apps/${slug}`);
|
|
4265
4571
|
if (res.status === 404)
|
|
4266
4572
|
outputError("not_found", `App "${slug}" not found.`);
|
|
4267
4573
|
if (!res.ok)
|
|
@@ -4301,8 +4607,7 @@ async function inspect(slugArg) {
|
|
|
4301
4607
|
}
|
|
4302
4608
|
var init_inspect = __esm(() => {
|
|
4303
4609
|
init_source();
|
|
4304
|
-
|
|
4305
|
-
init_auth();
|
|
4610
|
+
init_api();
|
|
4306
4611
|
init_output();
|
|
4307
4612
|
init_link();
|
|
4308
4613
|
});
|
|
@@ -4317,14 +4622,6 @@ __export(exports_env_cmd, {
|
|
|
4317
4622
|
});
|
|
4318
4623
|
import { writeFileSync as writeFileSync5 } from "fs";
|
|
4319
4624
|
import { resolve } from "path";
|
|
4320
|
-
function requireAuth2() {
|
|
4321
|
-
const token = getAuthToken();
|
|
4322
|
-
if (!token) {
|
|
4323
|
-
console.error(source_default.red("Not authenticated. Run"), source_default.cyan("bagdock login"), source_default.red("or set BAGDOCK_API_KEY."));
|
|
4324
|
-
process.exit(1);
|
|
4325
|
-
}
|
|
4326
|
-
return token;
|
|
4327
|
-
}
|
|
4328
4625
|
function requireConfig() {
|
|
4329
4626
|
const config = loadBagdockJson(process.cwd());
|
|
4330
4627
|
if (!config) {
|
|
@@ -4334,12 +4631,9 @@ function requireConfig() {
|
|
|
4334
4631
|
return config;
|
|
4335
4632
|
}
|
|
4336
4633
|
async function envList() {
|
|
4337
|
-
const token = requireAuth2();
|
|
4338
4634
|
const config = requireConfig();
|
|
4339
4635
|
try {
|
|
4340
|
-
const res = await
|
|
4341
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
4342
|
-
});
|
|
4636
|
+
const res = await apiFetch(`/api/v1/developer/apps/${config.slug}/env`);
|
|
4343
4637
|
if (!res.ok) {
|
|
4344
4638
|
console.error(source_default.red(`Failed to list env vars (${res.status})`));
|
|
4345
4639
|
process.exit(1);
|
|
@@ -4363,17 +4657,9 @@ Environment variables for ${config.slug}:
|
|
|
4363
4657
|
}
|
|
4364
4658
|
}
|
|
4365
4659
|
async function envSet(key, value) {
|
|
4366
|
-
const token = requireAuth2();
|
|
4367
4660
|
const config = requireConfig();
|
|
4368
4661
|
try {
|
|
4369
|
-
const res = await
|
|
4370
|
-
method: "PUT",
|
|
4371
|
-
headers: {
|
|
4372
|
-
"Content-Type": "application/json",
|
|
4373
|
-
Authorization: `Bearer ${token}`
|
|
4374
|
-
},
|
|
4375
|
-
body: JSON.stringify({ key, value })
|
|
4376
|
-
});
|
|
4662
|
+
const res = await apiFetchJson(`/api/v1/developer/apps/${config.slug}/env`, "PUT", { key, value });
|
|
4377
4663
|
if (!res.ok) {
|
|
4378
4664
|
const body = await res.text();
|
|
4379
4665
|
console.error(source_default.red(`Failed to set ${key} (${res.status}):`), body.slice(0, 200));
|
|
@@ -4386,13 +4672,9 @@ async function envSet(key, value) {
|
|
|
4386
4672
|
}
|
|
4387
4673
|
}
|
|
4388
4674
|
async function envRemove(key) {
|
|
4389
|
-
const token = requireAuth2();
|
|
4390
4675
|
const config = requireConfig();
|
|
4391
4676
|
try {
|
|
4392
|
-
const res = await
|
|
4393
|
-
method: "DELETE",
|
|
4394
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
4395
|
-
});
|
|
4677
|
+
const res = await apiFetch(`/api/v1/developer/apps/${config.slug}/env/${key}`, { method: "DELETE" });
|
|
4396
4678
|
if (!res.ok) {
|
|
4397
4679
|
console.error(source_default.red(`Failed to remove ${key} (${res.status})`));
|
|
4398
4680
|
process.exit(1);
|
|
@@ -4404,14 +4686,11 @@ async function envRemove(key) {
|
|
|
4404
4686
|
}
|
|
4405
4687
|
}
|
|
4406
4688
|
async function envPull(file) {
|
|
4407
|
-
const token = requireAuth2();
|
|
4408
4689
|
const slug = requireSlug();
|
|
4409
4690
|
const target = resolve(file ?? ".env.local");
|
|
4410
4691
|
status(`Pulling env vars for ${slug}...`);
|
|
4411
4692
|
try {
|
|
4412
|
-
const res = await
|
|
4413
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
4414
|
-
});
|
|
4693
|
+
const res = await apiFetch(`/api/v1/developer/apps/${slug}/env`);
|
|
4415
4694
|
if (!res.ok) {
|
|
4416
4695
|
outputError("api_error", `Failed to pull env vars (${res.status})`);
|
|
4417
4696
|
process.exit(1);
|
|
@@ -4446,7 +4725,7 @@ async function envPull(file) {
|
|
|
4446
4725
|
var init_env_cmd = __esm(() => {
|
|
4447
4726
|
init_source();
|
|
4448
4727
|
init_config();
|
|
4449
|
-
|
|
4728
|
+
init_api();
|
|
4450
4729
|
init_output();
|
|
4451
4730
|
init_link();
|
|
4452
4731
|
});
|
|
@@ -4458,23 +4737,9 @@ __export(exports_keys, {
|
|
|
4458
4737
|
keysDelete: () => keysDelete,
|
|
4459
4738
|
keysCreate: () => keysCreate
|
|
4460
4739
|
});
|
|
4461
|
-
async function apiRequest(method, path2, body) {
|
|
4462
|
-
const token = getAuthToken();
|
|
4463
|
-
if (!token) {
|
|
4464
|
-
outputError("UNAUTHENTICATED", "Not logged in. Run `bagdock login` or set BAGDOCK_API_KEY.");
|
|
4465
|
-
}
|
|
4466
|
-
const headers = { Authorization: `Bearer ${token}` };
|
|
4467
|
-
const init2 = { method, headers };
|
|
4468
|
-
if (body) {
|
|
4469
|
-
headers["Content-Type"] = "application/json";
|
|
4470
|
-
init2.body = JSON.stringify(body);
|
|
4471
|
-
}
|
|
4472
|
-
const res = await fetch(`${API_BASE}${path2}`, init2);
|
|
4473
|
-
return res;
|
|
4474
|
-
}
|
|
4475
4740
|
async function keysCreate(opts) {
|
|
4476
4741
|
status("Creating API key...");
|
|
4477
|
-
const res = await
|
|
4742
|
+
const res = await apiFetchJson("/api/v1/operator/api-keys", "POST", {
|
|
4478
4743
|
name: opts.name,
|
|
4479
4744
|
key_type: opts.type || "secret",
|
|
4480
4745
|
key_category: opts.category || "standard",
|
|
@@ -4511,7 +4776,7 @@ async function keysList(opts) {
|
|
|
4511
4776
|
let path2 = "/api/v1/operator/api-keys";
|
|
4512
4777
|
if (opts.environment)
|
|
4513
4778
|
path2 += `?environment=${opts.environment}`;
|
|
4514
|
-
const res = await
|
|
4779
|
+
const res = await apiFetch(path2);
|
|
4515
4780
|
if (!res.ok) {
|
|
4516
4781
|
const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
|
|
4517
4782
|
outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
|
|
@@ -4541,7 +4806,7 @@ async function keysDelete(id, opts) {
|
|
|
4541
4806
|
outputError("CONFIRMATION_REQUIRED", "Pass --yes to confirm deletion in non-interactive mode.");
|
|
4542
4807
|
}
|
|
4543
4808
|
status(`Revoking API key ${id}...`);
|
|
4544
|
-
const res = await
|
|
4809
|
+
const res = await apiFetchJson(`/api/v1/operator/api-keys/${id}`, "DELETE", {
|
|
4545
4810
|
reason: opts.reason
|
|
4546
4811
|
});
|
|
4547
4812
|
if (!res.ok) {
|
|
@@ -4558,8 +4823,7 @@ async function keysDelete(id, opts) {
|
|
|
4558
4823
|
}
|
|
4559
4824
|
var init_keys = __esm(() => {
|
|
4560
4825
|
init_source();
|
|
4561
|
-
|
|
4562
|
-
init_auth();
|
|
4826
|
+
init_api();
|
|
4563
4827
|
init_output();
|
|
4564
4828
|
});
|
|
4565
4829
|
|
|
@@ -4569,19 +4833,9 @@ __export(exports_apps, {
|
|
|
4569
4833
|
appsList: () => appsList,
|
|
4570
4834
|
appsGet: () => appsGet
|
|
4571
4835
|
});
|
|
4572
|
-
async function apiRequest2(method, path2) {
|
|
4573
|
-
const token = getAuthToken();
|
|
4574
|
-
if (!token) {
|
|
4575
|
-
outputError("UNAUTHENTICATED", "Not logged in. Run `bagdock login` or set BAGDOCK_API_KEY.");
|
|
4576
|
-
}
|
|
4577
|
-
return fetch(`${API_BASE}${path2}`, {
|
|
4578
|
-
method,
|
|
4579
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
4580
|
-
});
|
|
4581
|
-
}
|
|
4582
4836
|
async function appsList() {
|
|
4583
4837
|
status("Fetching apps...");
|
|
4584
|
-
const res = await
|
|
4838
|
+
const res = await apiFetch("/api/v1/developer/apps");
|
|
4585
4839
|
if (!res.ok) {
|
|
4586
4840
|
const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
|
|
4587
4841
|
outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
|
|
@@ -4611,7 +4865,7 @@ async function appsList() {
|
|
|
4611
4865
|
}
|
|
4612
4866
|
async function appsGet(slug) {
|
|
4613
4867
|
status(`Fetching app ${slug}...`);
|
|
4614
|
-
const res = await
|
|
4868
|
+
const res = await apiFetch(`/api/v1/developer/apps/${slug}`);
|
|
4615
4869
|
if (!res.ok) {
|
|
4616
4870
|
if (res.status === 404) {
|
|
4617
4871
|
outputError("NOT_FOUND", `App "${slug}" not found`);
|
|
@@ -4645,8 +4899,7 @@ async function appsGet(slug) {
|
|
|
4645
4899
|
}
|
|
4646
4900
|
var init_apps = __esm(() => {
|
|
4647
4901
|
init_source();
|
|
4648
|
-
|
|
4649
|
-
init_auth();
|
|
4902
|
+
init_api();
|
|
4650
4903
|
init_output();
|
|
4651
4904
|
});
|
|
4652
4905
|
|
|
@@ -4657,16 +4910,6 @@ __export(exports_logs, {
|
|
|
4657
4910
|
logsList: () => logsList,
|
|
4658
4911
|
logsGet: () => logsGet
|
|
4659
4912
|
});
|
|
4660
|
-
async function apiRequest3(method, path2) {
|
|
4661
|
-
const token = getAuthToken();
|
|
4662
|
-
if (!token) {
|
|
4663
|
-
outputError("UNAUTHENTICATED", "Not logged in. Run `bagdock login` or set BAGDOCK_API_KEY.");
|
|
4664
|
-
}
|
|
4665
|
-
return fetch(`${API_BASE}${path2}`, {
|
|
4666
|
-
method,
|
|
4667
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
4668
|
-
});
|
|
4669
|
-
}
|
|
4670
4913
|
function resolveSlug2(slug) {
|
|
4671
4914
|
if (slug)
|
|
4672
4915
|
return slug;
|
|
@@ -4680,7 +4923,7 @@ async function logsList(opts) {
|
|
|
4680
4923
|
const slug = resolveSlug2(opts.app);
|
|
4681
4924
|
const limit = opts.limit || "50";
|
|
4682
4925
|
status(`Fetching logs for ${slug}...`);
|
|
4683
|
-
const res = await
|
|
4926
|
+
const res = await apiFetch(`/api/v1/developer/apps/${slug}/logs?limit=${limit}`);
|
|
4684
4927
|
if (!res.ok) {
|
|
4685
4928
|
if (res.status === 404) {
|
|
4686
4929
|
outputError("NOT_FOUND", `App "${slug}" not found or no logs available`);
|
|
@@ -4710,7 +4953,7 @@ async function logsList(opts) {
|
|
|
4710
4953
|
async function logsGet(id, opts) {
|
|
4711
4954
|
const slug = resolveSlug2(opts.app);
|
|
4712
4955
|
status(`Fetching log entry ${id}...`);
|
|
4713
|
-
const res = await
|
|
4956
|
+
const res = await apiFetch(`/api/v1/developer/apps/${slug}/logs/${id}`);
|
|
4714
4957
|
if (!res.ok) {
|
|
4715
4958
|
const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
|
|
4716
4959
|
outputError(err.error?.code || "API_ERROR", err.error?.message || `HTTP ${res.status}`);
|
|
@@ -4733,7 +4976,7 @@ async function logsTail(opts) {
|
|
|
4733
4976
|
let lastTimestamp = new Date().toISOString();
|
|
4734
4977
|
const poll = async () => {
|
|
4735
4978
|
try {
|
|
4736
|
-
const res = await
|
|
4979
|
+
const res = await apiFetch(`/api/v1/developer/apps/${slug}/logs?since=${encodeURIComponent(lastTimestamp)}&limit=100`);
|
|
4737
4980
|
if (res.ok) {
|
|
4738
4981
|
const result = await res.json();
|
|
4739
4982
|
for (const entry of result.data || []) {
|
|
@@ -4759,7 +5002,7 @@ async function logsTail(opts) {
|
|
|
4759
5002
|
var init_logs = __esm(() => {
|
|
4760
5003
|
init_source();
|
|
4761
5004
|
init_config();
|
|
4762
|
-
|
|
5005
|
+
init_api();
|
|
4763
5006
|
init_output();
|
|
4764
5007
|
});
|
|
4765
5008
|
|
|
@@ -5033,13 +5276,22 @@ function toPascalCase(s) {
|
|
|
5033
5276
|
init_output();
|
|
5034
5277
|
init_config();
|
|
5035
5278
|
var program2 = new Command;
|
|
5036
|
-
program2.name("bagdock").description("Bagdock developer CLI — built for humans, AI agents, and CI/CD pipelines").version("0.
|
|
5279
|
+
program2.name("bagdock").description("Bagdock developer CLI — built for humans, AI agents, and CI/CD pipelines").version("0.6.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)").option("--ngrok", "Use API URLs from .env.local (for ngrok tunnels)").hook("preAction", (_thisCommand, actionCommand) => {
|
|
5037
5280
|
const opts = program2.opts();
|
|
5281
|
+
if (opts.ngrok)
|
|
5282
|
+
loadLocalEnv();
|
|
5038
5283
|
setOutputMode({ json: opts.json, quiet: opts.quiet });
|
|
5039
5284
|
if (opts.apiKey)
|
|
5040
5285
|
setApiKeyOverride(opts.apiKey);
|
|
5041
5286
|
if (opts.profile)
|
|
5042
5287
|
setProfileOverride(opts.profile);
|
|
5288
|
+
if (opts.env) {
|
|
5289
|
+
if (opts.env !== "live" && opts.env !== "test") {
|
|
5290
|
+
console.error('Error: --env must be "live" or "test"');
|
|
5291
|
+
process.exit(1);
|
|
5292
|
+
}
|
|
5293
|
+
setEnvironmentOverride(opts.env);
|
|
5294
|
+
}
|
|
5043
5295
|
});
|
|
5044
5296
|
program2.command("login").description("Authenticate with Bagdock (opens browser)").action(login);
|
|
5045
5297
|
program2.command("logout").description("Clear stored credentials").action(logout);
|
|
@@ -5047,6 +5299,10 @@ program2.command("whoami").description("Show current authenticated user").action
|
|
|
5047
5299
|
var authCmd = program2.command("auth").description("Manage authentication profiles");
|
|
5048
5300
|
authCmd.command("list").description("List all stored profiles").action(authList);
|
|
5049
5301
|
authCmd.command("switch [name]").description("Switch active profile").action(async (name) => authSwitch(name));
|
|
5302
|
+
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) => {
|
|
5303
|
+
const { switchContext: switchContext2 } = await Promise.resolve().then(() => (init_switch(), exports_switch));
|
|
5304
|
+
await switchContext2(opts);
|
|
5305
|
+
});
|
|
5050
5306
|
program2.command("doctor").description("Run environment diagnostics").action(async () => {
|
|
5051
5307
|
const { doctor: doctor2 } = await Promise.resolve().then(() => (init_doctor(), exports_doctor));
|
|
5052
5308
|
await doctor2();
|
|
@@ -5056,7 +5312,7 @@ program2.command("dev").description("Start local dev server").option("-p, --port
|
|
|
5056
5312
|
const { dev: dev2 } = await Promise.resolve().then(() => (init_dev(), exports_dev));
|
|
5057
5313
|
await dev2(opts);
|
|
5058
5314
|
});
|
|
5059
|
-
program2.command("deploy").description("Build locally and deploy via Bagdock API").option("--
|
|
5315
|
+
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) => {
|
|
5060
5316
|
const { deploy: deploy2 } = await Promise.resolve().then(() => (init_deploy(), exports_deploy));
|
|
5061
5317
|
await deploy2(opts);
|
|
5062
5318
|
});
|
|
@@ -5089,7 +5345,7 @@ program2.command("inspect [slug]").description("Show deployment details and stat
|
|
|
5089
5345
|
const { inspect: inspect2 } = await Promise.resolve().then(() => (init_inspect(), exports_inspect));
|
|
5090
5346
|
await inspect2(slug);
|
|
5091
5347
|
});
|
|
5092
|
-
program2.command("link").description("Link current directory to a Bagdock app or edge").option("--slug <slug>", "
|
|
5348
|
+
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) => {
|
|
5093
5349
|
const { link: link2 } = await Promise.resolve().then(() => (init_link(), exports_link));
|
|
5094
5350
|
await link2(opts);
|
|
5095
5351
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bagdock/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Bagdock developer CLI — build, test, and deploy apps and edges on the Bagdock platform",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bagdock",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"open": "^10.1.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@types/bun": "
|
|
56
|
+
"@types/bun": "^1.3.10",
|
|
57
57
|
"typescript": "^5.3.3",
|
|
58
58
|
"vitest": "^3.2.4"
|
|
59
59
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"skill": "bagdock-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"evals": [
|
|
5
5
|
{
|
|
6
6
|
"id": "login-flow",
|
|
@@ -203,6 +203,48 @@
|
|
|
203
203
|
"input": "Link this directory to my smart-entry adapter",
|
|
204
204
|
"expected_commands": ["bagdock link --slug smart-entry"],
|
|
205
205
|
"expected_behavior": "Creates .bagdock/link.json with slug, other commands use it as fallback"
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"id": "switch-operator-sandbox",
|
|
209
|
+
"description": "Agent should switch to sandbox environment for testing",
|
|
210
|
+
"input": "Switch to test mode for the WiseStorage operator",
|
|
211
|
+
"expected_commands": ["bagdock switch --operator wisestorage --env test"],
|
|
212
|
+
"expected_behavior": "Updates active profile with operatorSlug=wisestorage and environment=test"
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"id": "switch-interactive",
|
|
216
|
+
"description": "Agent should use interactive switch when operator unknown",
|
|
217
|
+
"input": "I want to switch to a different operator",
|
|
218
|
+
"expected_commands": ["bagdock switch"],
|
|
219
|
+
"expected_behavior": "Lists available operators, prompts selection, then prompts environment choice"
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"id": "deploy-to-test",
|
|
223
|
+
"description": "Agent should deploy to sandbox environment",
|
|
224
|
+
"input": "Deploy my adapter to the sandbox for testing",
|
|
225
|
+
"expected_commands": ["bagdock --env test deploy --target staging --yes"],
|
|
226
|
+
"expected_behavior": "Deploys to staging target within the test/sandbox environment"
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"id": "inspect-test-env",
|
|
230
|
+
"description": "Agent should inspect an app in test environment",
|
|
231
|
+
"input": "Show me the deployment details of my app in the sandbox",
|
|
232
|
+
"expected_commands": ["bagdock --env test inspect --json"],
|
|
233
|
+
"expected_behavior": "Returns app details from the sandbox tenant database"
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"id": "link-with-env",
|
|
237
|
+
"description": "Agent should link directory with specific environment",
|
|
238
|
+
"input": "Link this directory to smart-entry in test mode",
|
|
239
|
+
"expected_commands": ["bagdock link --slug smart-entry --env test"],
|
|
240
|
+
"expected_behavior": "Creates .bagdock/link.json with slug and environment=test"
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"id": "env-override-per-command",
|
|
244
|
+
"description": "Agent should use --env flag to override environment for a single command",
|
|
245
|
+
"input": "List apps on the live environment even though I'm currently in test mode",
|
|
246
|
+
"expected_commands": ["bagdock --env live apps list --json"],
|
|
247
|
+
"expected_behavior": "Lists apps from the live tenant database, does not change profile"
|
|
206
248
|
}
|
|
207
249
|
]
|
|
208
250
|
}
|
|
@@ -45,9 +45,19 @@ For CI/CD, set `BAGDOCK_API_KEY` in your environment. For interactive use, run `
|
|
|
45
45
|
| `-q, --quiet` | Suppress status messages (implies `--json`) |
|
|
46
46
|
| `--api-key <key>` | Override auth for this invocation |
|
|
47
47
|
| `-p, --profile <name>` | Use a named profile (overrides `BAGDOCK_PROFILE`) |
|
|
48
|
+
| `--env <live\|test>` | Override environment for this invocation |
|
|
48
49
|
| `-V, --version` | Print version |
|
|
49
50
|
| `-h, --help` | Print help |
|
|
50
51
|
|
|
52
|
+
## Environment Context
|
|
53
|
+
|
|
54
|
+
All API calls include `X-Environment` and `X-Operator-Slug` headers. Resolution:
|
|
55
|
+
|
|
56
|
+
- **Environment**: `--env` flag > `.bagdock/link.json` > profile > `BAGDOCK_ENV` > `live`
|
|
57
|
+
- **Operator**: `BAGDOCK_OPERATOR` env var > profile stored value
|
|
58
|
+
|
|
59
|
+
Use `bagdock switch` to select operator and environment interactively, or pass `--env test` to any command.
|
|
60
|
+
|
|
51
61
|
## Available Commands
|
|
52
62
|
|
|
53
63
|
| Command | Description |
|
|
@@ -73,8 +83,9 @@ For CI/CD, set `BAGDOCK_API_KEY` in your environment. For interactive use, run `
|
|
|
73
83
|
| `open [slug]` | Open project in Bagdock dashboard |
|
|
74
84
|
| `inspect [slug]` | Show deployment details and status |
|
|
75
85
|
| `link` | Link directory to a Bagdock app or edge |
|
|
86
|
+
| `switch` | Switch operator and environment context (live/test) |
|
|
76
87
|
| `doctor` | Run environment diagnostics (version, auth, config, agents) |
|
|
77
|
-
| `auth list` | List stored profiles |
|
|
88
|
+
| `auth list` | List stored profiles (with operator + env) |
|
|
78
89
|
| `auth switch [name]` | Switch active profile |
|
|
79
90
|
| `apps list` | List deployed applications |
|
|
80
91
|
| `apps get <slug>` | Show details for an application |
|