@bagdock/cli 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -1
- package/dist/bagdock.js +500 -9
- package/package.json +1 -1
- package/skill-evals/bagdock-cli/evals.json +57 -1
- package/skills/bagdock-cli/SKILL.md +38 -0
- package/skills/bagdock-cli/references/app-management.md +63 -0
- package/skills/bagdock-cli/references/marketplace.md +69 -0
package/README.md
CHANGED
|
@@ -16,6 +16,12 @@ curl -fsSL https://bdok.dev/install.sh | bash
|
|
|
16
16
|
irm https://bdok.dev/install.ps1 | iex
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
### Homebrew (macOS / Linux)
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
brew install bagdock/cli/bagdock
|
|
23
|
+
```
|
|
24
|
+
|
|
19
25
|
### Node.js
|
|
20
26
|
|
|
21
27
|
```bash
|
|
@@ -42,12 +48,16 @@ pnpm dlx @bagdock/cli --help
|
|
|
42
48
|
|
|
43
49
|
### Agent skills
|
|
44
50
|
|
|
45
|
-
This CLI ships with an agent skill that teaches AI coding agents (Cursor, Claude Code,
|
|
51
|
+
This CLI ships with an agent skill that teaches AI coding agents (Cursor, Claude Code, Codex, Conductor, etc.) how to use the Bagdock CLI effectively — including non-interactive flags, output formats, and common pitfalls.
|
|
52
|
+
|
|
53
|
+
To install skills for Bagdock's full platform (API, CLI, adapters) from the central skills repository:
|
|
46
54
|
|
|
47
55
|
```bash
|
|
48
56
|
npx skills add bagdock/bagdock-skills
|
|
49
57
|
```
|
|
50
58
|
|
|
59
|
+
See [bagdock/bagdock-skills](https://github.com/bagdock/bagdock-skills) for all available skills and plugin manifests.
|
|
60
|
+
|
|
51
61
|
## Local development
|
|
52
62
|
|
|
53
63
|
Use this when you want to change the CLI and run your build locally.
|
|
@@ -383,6 +393,129 @@ bagdock submit
|
|
|
383
393
|
|
|
384
394
|
---
|
|
385
395
|
|
|
396
|
+
### `bagdock validate`
|
|
397
|
+
|
|
398
|
+
Run local pre-submission checks on `bagdock.json` and your bundle before submitting.
|
|
399
|
+
|
|
400
|
+
```bash
|
|
401
|
+
bagdock validate
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Checks performed:
|
|
405
|
+
|
|
406
|
+
| Check | Pass | Warn | Fail |
|
|
407
|
+
|-------|------|------|------|
|
|
408
|
+
| bagdock.json | Found and parsed | — | Missing or invalid JSON |
|
|
409
|
+
| Required fields | All present | — | Missing name, slug, version, type, category, or main |
|
|
410
|
+
| Type | Valid type | — | Invalid type value |
|
|
411
|
+
| Kind | — | Unknown kind | — |
|
|
412
|
+
| Entry point | File exists (shows size) | — | File not found |
|
|
413
|
+
| Bundle size | Under 10 MB | Approaching limit (>80%) | Over 10 MB |
|
|
414
|
+
| Project link | — | Slug mismatch with linked project | — |
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
# JSON output
|
|
418
|
+
bagdock validate --json
|
|
419
|
+
# => {"ok":true,"checks":[...]}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Exit code `0` if all checks pass or warn. Exit code `1` if any check fails.
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
### `bagdock submission list`
|
|
427
|
+
|
|
428
|
+
List submission history for the current app.
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
bagdock submission list
|
|
432
|
+
bagdock submission list --app my-adapter --json
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
| Flag | Description |
|
|
436
|
+
|------|-------------|
|
|
437
|
+
| `--app <slug>` | App slug (defaults to `bagdock.json` or linked project) |
|
|
438
|
+
|
|
439
|
+
### `bagdock submission status <id>`
|
|
440
|
+
|
|
441
|
+
Fetch detailed review state for a specific submission.
|
|
442
|
+
|
|
443
|
+
```bash
|
|
444
|
+
bagdock submission status iadpv_abc123
|
|
445
|
+
bagdock submission status iadpv_abc123 --json
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
| Flag | Description |
|
|
449
|
+
|------|-------------|
|
|
450
|
+
| `--app <slug>` | App slug |
|
|
451
|
+
|
|
452
|
+
### `bagdock submission withdraw <id>`
|
|
453
|
+
|
|
454
|
+
Cancel a pending submission before approval. Only works when `review_status` is `submitted`.
|
|
455
|
+
|
|
456
|
+
```bash
|
|
457
|
+
bagdock submission withdraw iadpv_abc123
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
| Flag | Description |
|
|
461
|
+
|------|-------------|
|
|
462
|
+
| `--app <slug>` | App slug |
|
|
463
|
+
|
|
464
|
+
#### Error codes
|
|
465
|
+
|
|
466
|
+
| Code | Cause |
|
|
467
|
+
|------|-------|
|
|
468
|
+
| `not_found` | Submission or app not found |
|
|
469
|
+
| `invalid_status` | App is not in `submitted` state |
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
### `bagdock open [slug]`
|
|
474
|
+
|
|
475
|
+
Open the current project in the Bagdock dashboard.
|
|
476
|
+
|
|
477
|
+
```bash
|
|
478
|
+
bagdock open
|
|
479
|
+
bagdock open my-adapter
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
Reads the slug from `bagdock.json`, linked project, or the argument.
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
### `bagdock inspect [slug]`
|
|
487
|
+
|
|
488
|
+
Show deployment details and status for an app.
|
|
489
|
+
|
|
490
|
+
```bash
|
|
491
|
+
bagdock inspect
|
|
492
|
+
bagdock inspect my-adapter --json
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
Displays: name, slug, type, version, review status, worker URL, namespace, timestamps.
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
### `bagdock link`
|
|
500
|
+
|
|
501
|
+
Link the current directory to a Bagdock app or edge. Other commands use the linked slug as a fallback.
|
|
502
|
+
|
|
503
|
+
```bash
|
|
504
|
+
# Interactive: select from your apps
|
|
505
|
+
bagdock link
|
|
506
|
+
|
|
507
|
+
# Non-interactive
|
|
508
|
+
bagdock link --slug my-adapter
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
| Flag | Description |
|
|
512
|
+
|------|-------------|
|
|
513
|
+
| `--slug <slug>` | Project slug (required in non-interactive mode) |
|
|
514
|
+
|
|
515
|
+
Stores the link in `.bagdock/link.json` in the current directory.
|
|
516
|
+
|
|
517
|
+
---
|
|
518
|
+
|
|
386
519
|
### `bagdock env list`
|
|
387
520
|
|
|
388
521
|
List environment variables for the current app.
|
|
@@ -408,6 +541,17 @@ Remove an environment variable.
|
|
|
408
541
|
bagdock env remove VENDOR_API_KEY
|
|
409
542
|
```
|
|
410
543
|
|
|
544
|
+
### `bagdock env pull [file]`
|
|
545
|
+
|
|
546
|
+
Pull remote env var keys to a local `.env` file for development.
|
|
547
|
+
|
|
548
|
+
```bash
|
|
549
|
+
bagdock env pull
|
|
550
|
+
bagdock env pull .env.development
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
The API does not expose secret values. The file is created with keys and empty values — fill them in for local dev.
|
|
554
|
+
|
|
411
555
|
---
|
|
412
556
|
|
|
413
557
|
### `bagdock keys create`
|
package/dist/bagdock.js
CHANGED
|
@@ -3894,14 +3894,430 @@ var init_submit = __esm(() => {
|
|
|
3894
3894
|
init_auth();
|
|
3895
3895
|
});
|
|
3896
3896
|
|
|
3897
|
+
// src/link.ts
|
|
3898
|
+
var exports_link = {};
|
|
3899
|
+
__export(exports_link, {
|
|
3900
|
+
resolveSlug: () => resolveSlug,
|
|
3901
|
+
requireSlug: () => requireSlug,
|
|
3902
|
+
link: () => link
|
|
3903
|
+
});
|
|
3904
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
3905
|
+
import { join as join6 } from "path";
|
|
3906
|
+
function resolveSlug() {
|
|
3907
|
+
const config = loadBagdockJson(process.cwd());
|
|
3908
|
+
if (config?.slug)
|
|
3909
|
+
return config.slug;
|
|
3910
|
+
const linkPath = join6(process.cwd(), LINK_DIR, LINK_FILE);
|
|
3911
|
+
if (existsSync6(linkPath)) {
|
|
3912
|
+
try {
|
|
3913
|
+
const data = JSON.parse(readFileSync4(linkPath, "utf-8"));
|
|
3914
|
+
return data.slug ?? null;
|
|
3915
|
+
} catch {
|
|
3916
|
+
return null;
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3919
|
+
return null;
|
|
3920
|
+
}
|
|
3921
|
+
function requireSlug(slugArg) {
|
|
3922
|
+
const slug = slugArg ?? resolveSlug();
|
|
3923
|
+
if (!slug) {
|
|
3924
|
+
if (isJsonMode()) {
|
|
3925
|
+
outputError("no_project", "No project found. Pass --slug, add bagdock.json, or run bagdock link.");
|
|
3926
|
+
}
|
|
3927
|
+
console.error(source_default.red("No project found."), "Pass a slug, create bagdock.json, or run", source_default.cyan("bagdock link"));
|
|
3928
|
+
process.exit(1);
|
|
3929
|
+
}
|
|
3930
|
+
return slug;
|
|
3931
|
+
}
|
|
3932
|
+
async function link(opts) {
|
|
3933
|
+
let slug = opts.slug;
|
|
3934
|
+
if (!slug) {
|
|
3935
|
+
const config = loadBagdockJson(process.cwd());
|
|
3936
|
+
if (config?.slug) {
|
|
3937
|
+
slug = config.slug;
|
|
3938
|
+
status(`Found bagdock.json — linking to ${slug}`);
|
|
3939
|
+
}
|
|
3940
|
+
}
|
|
3941
|
+
if (!slug && process.stdout.isTTY && !isJsonMode()) {
|
|
3942
|
+
const token = getAuthToken();
|
|
3943
|
+
if (!token) {
|
|
3944
|
+
console.error(source_default.red("Not authenticated."), "Run", source_default.cyan("bagdock login"), "first.");
|
|
3945
|
+
process.exit(1);
|
|
3946
|
+
}
|
|
3947
|
+
status("Fetching your apps...");
|
|
3948
|
+
try {
|
|
3949
|
+
const res = await fetch(`${API_BASE}/v1/developer/apps`, {
|
|
3950
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
3951
|
+
});
|
|
3952
|
+
if (!res.ok)
|
|
3953
|
+
throw new Error(`API returned ${res.status}`);
|
|
3954
|
+
const { data } = await res.json();
|
|
3955
|
+
if (!data?.length) {
|
|
3956
|
+
console.error(source_default.yellow("No apps found."), "Create one with", source_default.cyan("bagdock init"));
|
|
3957
|
+
process.exit(1);
|
|
3958
|
+
}
|
|
3959
|
+
console.log(source_default.bold(`
|
|
3960
|
+
Your apps:
|
|
3961
|
+
`));
|
|
3962
|
+
data.forEach((app, i) => console.log(` ${source_default.cyan(i + 1)} ${app.name} ${source_default.dim(`(${app.slug})`)}`));
|
|
3963
|
+
console.log();
|
|
3964
|
+
const readline = await import("readline");
|
|
3965
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
3966
|
+
const answer = await new Promise((resolve) => rl.question("Select app number: ", resolve));
|
|
3967
|
+
rl.close();
|
|
3968
|
+
const idx = parseInt(answer, 10) - 1;
|
|
3969
|
+
if (isNaN(idx) || idx < 0 || idx >= data.length) {
|
|
3970
|
+
console.error(source_default.red("Invalid selection"));
|
|
3971
|
+
process.exit(1);
|
|
3972
|
+
}
|
|
3973
|
+
slug = data[idx].slug;
|
|
3974
|
+
} catch (err) {
|
|
3975
|
+
console.error(source_default.red("Failed to fetch apps:"), err.message);
|
|
3976
|
+
process.exit(1);
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3979
|
+
if (!slug) {
|
|
3980
|
+
outputError("missing_slug", "Slug required. Pass --slug in non-interactive mode.");
|
|
3981
|
+
process.exit(1);
|
|
3982
|
+
}
|
|
3983
|
+
const dir = join6(process.cwd(), LINK_DIR);
|
|
3984
|
+
if (!existsSync6(dir))
|
|
3985
|
+
mkdirSync3(dir, { recursive: true });
|
|
3986
|
+
const linkData = { slug, linkedAt: new Date().toISOString() };
|
|
3987
|
+
writeFileSync4(join6(dir, LINK_FILE), JSON.stringify(linkData, null, 2));
|
|
3988
|
+
if (isJsonMode()) {
|
|
3989
|
+
outputSuccess({ slug, path: join6(dir, LINK_FILE) });
|
|
3990
|
+
} else {
|
|
3991
|
+
console.log(source_default.green(`Linked to ${source_default.bold(slug)}`));
|
|
3992
|
+
console.log(source_default.dim(` Stored in ${LINK_DIR}/${LINK_FILE}`));
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
var LINK_DIR = ".bagdock", LINK_FILE = "link.json";
|
|
3996
|
+
var init_link = __esm(() => {
|
|
3997
|
+
init_source();
|
|
3998
|
+
init_config();
|
|
3999
|
+
init_auth();
|
|
4000
|
+
init_output();
|
|
4001
|
+
});
|
|
4002
|
+
|
|
4003
|
+
// src/validate.ts
|
|
4004
|
+
var exports_validate = {};
|
|
4005
|
+
__export(exports_validate, {
|
|
4006
|
+
validate: () => validate
|
|
4007
|
+
});
|
|
4008
|
+
import { existsSync as existsSync7, statSync } from "fs";
|
|
4009
|
+
import { join as join7 } from "path";
|
|
4010
|
+
async function validate() {
|
|
4011
|
+
const checks = [];
|
|
4012
|
+
const dir = process.cwd();
|
|
4013
|
+
const config = loadBagdockJson(dir);
|
|
4014
|
+
if (!config) {
|
|
4015
|
+
checks.push({ name: "bagdock.json", status: "fail", message: "Not found or invalid JSON" });
|
|
4016
|
+
return finish(checks);
|
|
4017
|
+
}
|
|
4018
|
+
checks.push({ name: "bagdock.json", status: "pass", message: "Found and parsed" });
|
|
4019
|
+
const required = ["name", "slug", "version", "type", "category", "main"];
|
|
4020
|
+
const missing = required.filter((f) => !config[f]);
|
|
4021
|
+
if (missing.length) {
|
|
4022
|
+
checks.push({ name: "Required fields", status: "fail", message: `Missing: ${missing.join(", ")}` });
|
|
4023
|
+
} else {
|
|
4024
|
+
checks.push({ name: "Required fields", status: "pass", message: "All present" });
|
|
4025
|
+
}
|
|
4026
|
+
if (!VALID_TYPES.includes(config.type)) {
|
|
4027
|
+
checks.push({ name: "Type", status: "fail", message: `Invalid type "${config.type}". Must be: ${VALID_TYPES.join(", ")}` });
|
|
4028
|
+
} else {
|
|
4029
|
+
checks.push({ name: "Type", status: "pass", message: config.type });
|
|
4030
|
+
}
|
|
4031
|
+
if (config.kind && !VALID_KINDS.includes(config.kind)) {
|
|
4032
|
+
checks.push({ name: "Kind", status: "warn", message: `Unknown kind "${config.kind}". Expected: ${VALID_KINDS.join(", ")}` });
|
|
4033
|
+
}
|
|
4034
|
+
const entryPath = join7(dir, config.main);
|
|
4035
|
+
if (!existsSync7(entryPath)) {
|
|
4036
|
+
checks.push({ name: "Entry point", status: "fail", message: `File not found: ${config.main}` });
|
|
4037
|
+
} else {
|
|
4038
|
+
const size = statSync(entryPath).size;
|
|
4039
|
+
checks.push({ name: "Entry point", status: "pass", message: `${config.main} (${(size / 1024).toFixed(1)} KB)` });
|
|
4040
|
+
if (size > MAX_BUNDLE_BYTES) {
|
|
4041
|
+
checks.push({ name: "Bundle size", status: "fail", message: `${(size / 1024 / 1024).toFixed(1)} MB exceeds ${MAX_BUNDLE_BYTES / 1024 / 1024} MB limit` });
|
|
4042
|
+
} else if (size > MAX_BUNDLE_BYTES * 0.8) {
|
|
4043
|
+
checks.push({ name: "Bundle size", status: "warn", message: `${(size / 1024 / 1024).toFixed(1)} MB — approaching limit` });
|
|
4044
|
+
} else {
|
|
4045
|
+
checks.push({ name: "Bundle size", status: "pass", message: `${(size / 1024).toFixed(1)} KB` });
|
|
4046
|
+
}
|
|
4047
|
+
}
|
|
4048
|
+
const linked = resolveSlug();
|
|
4049
|
+
if (linked && linked !== config.slug) {
|
|
4050
|
+
checks.push({ name: "Project link", status: "warn", message: `bagdock.json slug "${config.slug}" differs from linked project "${linked}"` });
|
|
4051
|
+
}
|
|
4052
|
+
return finish(checks);
|
|
4053
|
+
}
|
|
4054
|
+
function finish(checks) {
|
|
4055
|
+
const hasFail = checks.some((c) => c.status === "fail");
|
|
4056
|
+
const hasWarn = checks.some((c) => c.status === "warn");
|
|
4057
|
+
if (isJsonMode()) {
|
|
4058
|
+
outputSuccess({ ok: !hasFail, checks });
|
|
4059
|
+
if (hasFail)
|
|
4060
|
+
process.exit(1);
|
|
4061
|
+
return;
|
|
4062
|
+
}
|
|
4063
|
+
console.log(source_default.bold(`
|
|
4064
|
+
Bagdock Validate
|
|
4065
|
+
`));
|
|
4066
|
+
for (const c of checks) {
|
|
4067
|
+
const icon = c.status === "pass" ? source_default.green("✔") : c.status === "warn" ? source_default.yellow("⚠") : source_default.red("✖");
|
|
4068
|
+
console.log(` ${icon} ${c.name}: ${c.message}`);
|
|
4069
|
+
}
|
|
4070
|
+
console.log();
|
|
4071
|
+
if (hasFail) {
|
|
4072
|
+
console.log(source_default.red(` Validation failed. Fix errors before submitting.
|
|
4073
|
+
`));
|
|
4074
|
+
process.exit(1);
|
|
4075
|
+
} else if (hasWarn) {
|
|
4076
|
+
console.log(source_default.yellow(` Passed with warnings.
|
|
4077
|
+
`));
|
|
4078
|
+
} else {
|
|
4079
|
+
console.log(source_default.green(` All checks passed. Ready to submit.
|
|
4080
|
+
`));
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
4083
|
+
var VALID_TYPES, VALID_KINDS, MAX_BUNDLE_BYTES;
|
|
4084
|
+
var init_validate = __esm(() => {
|
|
4085
|
+
init_source();
|
|
4086
|
+
init_config();
|
|
4087
|
+
init_output();
|
|
4088
|
+
init_link();
|
|
4089
|
+
VALID_TYPES = ["edge", "app"];
|
|
4090
|
+
VALID_KINDS = ["adapter", "comms", "webhook", "ui-extension", "microfrontend"];
|
|
4091
|
+
MAX_BUNDLE_BYTES = 10 * 1024 * 1024;
|
|
4092
|
+
});
|
|
4093
|
+
|
|
4094
|
+
// src/submission.ts
|
|
4095
|
+
var exports_submission = {};
|
|
4096
|
+
__export(exports_submission, {
|
|
4097
|
+
submissionWithdraw: () => submissionWithdraw,
|
|
4098
|
+
submissionStatus: () => submissionStatus,
|
|
4099
|
+
submissionList: () => submissionList
|
|
4100
|
+
});
|
|
4101
|
+
function requireAuth() {
|
|
4102
|
+
const token = getAuthToken();
|
|
4103
|
+
if (!token) {
|
|
4104
|
+
outputError("auth_error", "Not authenticated. Run bagdock login or set BAGDOCK_API_KEY.");
|
|
4105
|
+
process.exit(1);
|
|
4106
|
+
}
|
|
4107
|
+
return token;
|
|
4108
|
+
}
|
|
4109
|
+
async function submissionList(opts) {
|
|
4110
|
+
const token = requireAuth();
|
|
4111
|
+
const slug = requireSlug(opts.app);
|
|
4112
|
+
status(`Fetching submissions for ${slug}...`);
|
|
4113
|
+
try {
|
|
4114
|
+
const res = await fetch(`${API_BASE}/v1/developer/apps/${slug}/submissions`, {
|
|
4115
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
4116
|
+
});
|
|
4117
|
+
if (res.status === 404) {
|
|
4118
|
+
outputError("not_found", `App "${slug}" not found or no submissions exist.`);
|
|
4119
|
+
}
|
|
4120
|
+
if (!res.ok) {
|
|
4121
|
+
outputError("api_error", `API returned ${res.status}`);
|
|
4122
|
+
}
|
|
4123
|
+
const { data } = await res.json();
|
|
4124
|
+
if (isJsonMode()) {
|
|
4125
|
+
outputList("submission", data, false);
|
|
4126
|
+
return;
|
|
4127
|
+
}
|
|
4128
|
+
if (!data?.length) {
|
|
4129
|
+
console.log(source_default.yellow("No submissions found for this app."));
|
|
4130
|
+
console.log("Submit with", source_default.cyan("bagdock submit"));
|
|
4131
|
+
return;
|
|
4132
|
+
}
|
|
4133
|
+
console.log(source_default.bold(`
|
|
4134
|
+
Submissions for ${slug}:
|
|
4135
|
+
`));
|
|
4136
|
+
console.log(` ${"ID".padEnd(22)} ${"Version".padEnd(10)} ${"Reason".padEnd(30)} ${"Date"}`);
|
|
4137
|
+
console.log(source_default.dim(" " + "─".repeat(80)));
|
|
4138
|
+
for (const s of data) {
|
|
4139
|
+
console.log(` ${source_default.cyan(s.id.padEnd(22))} ${(s.version ?? "").padEnd(10)} ${(s.change_reason ?? "").slice(0, 30).padEnd(30)} ${source_default.dim(new Date(s.created_at).toLocaleDateString())}`);
|
|
4140
|
+
}
|
|
4141
|
+
console.log();
|
|
4142
|
+
} catch (err) {
|
|
4143
|
+
outputError("network_error", err.message);
|
|
4144
|
+
}
|
|
4145
|
+
}
|
|
4146
|
+
async function submissionStatus(id, opts) {
|
|
4147
|
+
const token = requireAuth();
|
|
4148
|
+
const slug = requireSlug(opts.app);
|
|
4149
|
+
status(`Fetching submission ${id}...`);
|
|
4150
|
+
try {
|
|
4151
|
+
const res = await fetch(`${API_BASE}/v1/developer/apps/${slug}/submissions/${id}`, {
|
|
4152
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
4153
|
+
});
|
|
4154
|
+
if (res.status === 404) {
|
|
4155
|
+
outputError("not_found", `Submission "${id}" not found.`);
|
|
4156
|
+
}
|
|
4157
|
+
if (!res.ok) {
|
|
4158
|
+
outputError("api_error", `API returned ${res.status}`);
|
|
4159
|
+
}
|
|
4160
|
+
const { data } = await res.json();
|
|
4161
|
+
if (isJsonMode()) {
|
|
4162
|
+
outputSuccess(data);
|
|
4163
|
+
return;
|
|
4164
|
+
}
|
|
4165
|
+
console.log(source_default.bold(`
|
|
4166
|
+
Submission ${source_default.cyan(data.id)}
|
|
4167
|
+
`));
|
|
4168
|
+
const fields = [
|
|
4169
|
+
["App", `${data.name} (${data.slug})`],
|
|
4170
|
+
["Version", data.version],
|
|
4171
|
+
["Review Status", data.review_status],
|
|
4172
|
+
["Type", data.type],
|
|
4173
|
+
["Visibility", data.visibility],
|
|
4174
|
+
["Reason", data.change_reason],
|
|
4175
|
+
["Submitted by", data.changed_by],
|
|
4176
|
+
["Date", new Date(data.created_at).toLocaleString()]
|
|
4177
|
+
];
|
|
4178
|
+
for (const [label, value] of fields) {
|
|
4179
|
+
console.log(` ${source_default.dim(label.padEnd(16))} ${value ?? source_default.dim("—")}`);
|
|
4180
|
+
}
|
|
4181
|
+
console.log();
|
|
4182
|
+
} catch (err) {
|
|
4183
|
+
outputError("network_error", err.message);
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
async function submissionWithdraw(id, opts) {
|
|
4187
|
+
const token = requireAuth();
|
|
4188
|
+
const slug = requireSlug(opts.app);
|
|
4189
|
+
status(`Withdrawing submission ${id}...`);
|
|
4190
|
+
try {
|
|
4191
|
+
const res = await fetch(`${API_BASE}/v1/developer/apps/${slug}/submissions/${id}/withdraw`, {
|
|
4192
|
+
method: "POST",
|
|
4193
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
4194
|
+
});
|
|
4195
|
+
if (res.status === 404) {
|
|
4196
|
+
outputError("not_found", `App "${slug}" not found.`);
|
|
4197
|
+
}
|
|
4198
|
+
if (res.status === 409) {
|
|
4199
|
+
const body = await res.json();
|
|
4200
|
+
outputError(body.code ?? "invalid_status", body.message);
|
|
4201
|
+
}
|
|
4202
|
+
if (!res.ok) {
|
|
4203
|
+
outputError("api_error", `API returned ${res.status}`);
|
|
4204
|
+
}
|
|
4205
|
+
const { data } = await res.json();
|
|
4206
|
+
if (isJsonMode()) {
|
|
4207
|
+
outputSuccess(data);
|
|
4208
|
+
return;
|
|
4209
|
+
}
|
|
4210
|
+
console.log(source_default.green(`Submission withdrawn.`), source_default.dim(`Status is now: ${data.review_status}`));
|
|
4211
|
+
console.log("You can re-submit with", source_default.cyan("bagdock submit"));
|
|
4212
|
+
} catch (err) {
|
|
4213
|
+
outputError("network_error", err.message);
|
|
4214
|
+
}
|
|
4215
|
+
}
|
|
4216
|
+
var init_submission = __esm(() => {
|
|
4217
|
+
init_source();
|
|
4218
|
+
init_config();
|
|
4219
|
+
init_auth();
|
|
4220
|
+
init_output();
|
|
4221
|
+
init_link();
|
|
4222
|
+
});
|
|
4223
|
+
|
|
4224
|
+
// src/open.ts
|
|
4225
|
+
var exports_open2 = {};
|
|
4226
|
+
__export(exports_open2, {
|
|
4227
|
+
open: () => open2
|
|
4228
|
+
});
|
|
4229
|
+
async function open2(slugArg) {
|
|
4230
|
+
const slug = requireSlug(slugArg);
|
|
4231
|
+
const url = `${DASHBOARD_BASE}/developer/apps/${slug}`;
|
|
4232
|
+
if (isJsonMode()) {
|
|
4233
|
+
outputSuccess({ url, slug });
|
|
4234
|
+
return;
|
|
4235
|
+
}
|
|
4236
|
+
status(`Opening ${url}`);
|
|
4237
|
+
await open_default(url);
|
|
4238
|
+
console.log(source_default.green("Opened"), source_default.cyan(url));
|
|
4239
|
+
}
|
|
4240
|
+
var init_open2 = __esm(() => {
|
|
4241
|
+
init_source();
|
|
4242
|
+
init_open();
|
|
4243
|
+
init_config();
|
|
4244
|
+
init_output();
|
|
4245
|
+
init_link();
|
|
4246
|
+
});
|
|
4247
|
+
|
|
4248
|
+
// src/inspect.ts
|
|
4249
|
+
var exports_inspect = {};
|
|
4250
|
+
__export(exports_inspect, {
|
|
4251
|
+
inspect: () => inspect
|
|
4252
|
+
});
|
|
4253
|
+
async function inspect(slugArg) {
|
|
4254
|
+
const token = getAuthToken();
|
|
4255
|
+
if (!token) {
|
|
4256
|
+
outputError("auth_error", "Not authenticated. Run bagdock login or set BAGDOCK_API_KEY.");
|
|
4257
|
+
process.exit(1);
|
|
4258
|
+
}
|
|
4259
|
+
const slug = requireSlug(slugArg);
|
|
4260
|
+
status(`Inspecting ${slug}...`);
|
|
4261
|
+
try {
|
|
4262
|
+
const res = await fetch(`${API_BASE}/v1/developer/apps/${slug}`, {
|
|
4263
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
4264
|
+
});
|
|
4265
|
+
if (res.status === 404)
|
|
4266
|
+
outputError("not_found", `App "${slug}" not found.`);
|
|
4267
|
+
if (!res.ok)
|
|
4268
|
+
outputError("api_error", `API returned ${res.status}`);
|
|
4269
|
+
const { data } = await res.json();
|
|
4270
|
+
if (isJsonMode()) {
|
|
4271
|
+
outputSuccess(data);
|
|
4272
|
+
return;
|
|
4273
|
+
}
|
|
4274
|
+
console.log(source_default.bold(`
|
|
4275
|
+
${data.name} ${source_default.dim(`(${data.slug})`)}
|
|
4276
|
+
`));
|
|
4277
|
+
const fields = [
|
|
4278
|
+
["ID", data.id],
|
|
4279
|
+
["Type", data.type],
|
|
4280
|
+
["Category", data.category],
|
|
4281
|
+
["Version", data.version],
|
|
4282
|
+
["Maintainer", data.maintainer],
|
|
4283
|
+
["Visibility", data.visibility],
|
|
4284
|
+
["Review Status", data.review_status],
|
|
4285
|
+
["Active", data.is_active ? "yes" : "no"],
|
|
4286
|
+
["Worker URL", data.worker_url],
|
|
4287
|
+
["Namespace", data.worker_namespace],
|
|
4288
|
+
["Created", data.created_at ? new Date(data.created_at).toLocaleString() : undefined],
|
|
4289
|
+
["Updated", data.updated_at ? new Date(data.updated_at).toLocaleString() : undefined],
|
|
4290
|
+
["Published", data.published_at ? new Date(data.published_at).toLocaleString() : undefined]
|
|
4291
|
+
];
|
|
4292
|
+
for (const [label, value] of fields) {
|
|
4293
|
+
if (value !== undefined && value !== null) {
|
|
4294
|
+
console.log(` ${source_default.dim(label.padEnd(16))} ${value}`);
|
|
4295
|
+
}
|
|
4296
|
+
}
|
|
4297
|
+
console.log();
|
|
4298
|
+
} catch (err) {
|
|
4299
|
+
outputError("network_error", err.message);
|
|
4300
|
+
}
|
|
4301
|
+
}
|
|
4302
|
+
var init_inspect = __esm(() => {
|
|
4303
|
+
init_source();
|
|
4304
|
+
init_config();
|
|
4305
|
+
init_auth();
|
|
4306
|
+
init_output();
|
|
4307
|
+
init_link();
|
|
4308
|
+
});
|
|
4309
|
+
|
|
3897
4310
|
// src/env-cmd.ts
|
|
3898
4311
|
var exports_env_cmd = {};
|
|
3899
4312
|
__export(exports_env_cmd, {
|
|
3900
4313
|
envSet: () => envSet,
|
|
3901
4314
|
envRemove: () => envRemove,
|
|
4315
|
+
envPull: () => envPull,
|
|
3902
4316
|
envList: () => envList
|
|
3903
4317
|
});
|
|
3904
|
-
|
|
4318
|
+
import { writeFileSync as writeFileSync5 } from "fs";
|
|
4319
|
+
import { resolve } from "path";
|
|
4320
|
+
function requireAuth2() {
|
|
3905
4321
|
const token = getAuthToken();
|
|
3906
4322
|
if (!token) {
|
|
3907
4323
|
console.error(source_default.red("Not authenticated. Run"), source_default.cyan("bagdock login"), source_default.red("or set BAGDOCK_API_KEY."));
|
|
@@ -3918,7 +4334,7 @@ function requireConfig() {
|
|
|
3918
4334
|
return config;
|
|
3919
4335
|
}
|
|
3920
4336
|
async function envList() {
|
|
3921
|
-
const token =
|
|
4337
|
+
const token = requireAuth2();
|
|
3922
4338
|
const config = requireConfig();
|
|
3923
4339
|
try {
|
|
3924
4340
|
const res = await fetch(`${API_BASE}/v1/developer/apps/${config.slug}/env`, {
|
|
@@ -3947,7 +4363,7 @@ Environment variables for ${config.slug}:
|
|
|
3947
4363
|
}
|
|
3948
4364
|
}
|
|
3949
4365
|
async function envSet(key, value) {
|
|
3950
|
-
const token =
|
|
4366
|
+
const token = requireAuth2();
|
|
3951
4367
|
const config = requireConfig();
|
|
3952
4368
|
try {
|
|
3953
4369
|
const res = await fetch(`${API_BASE}/v1/developer/apps/${config.slug}/env`, {
|
|
@@ -3970,7 +4386,7 @@ async function envSet(key, value) {
|
|
|
3970
4386
|
}
|
|
3971
4387
|
}
|
|
3972
4388
|
async function envRemove(key) {
|
|
3973
|
-
const token =
|
|
4389
|
+
const token = requireAuth2();
|
|
3974
4390
|
const config = requireConfig();
|
|
3975
4391
|
try {
|
|
3976
4392
|
const res = await fetch(`${API_BASE}/v1/developer/apps/${config.slug}/env/${key}`, {
|
|
@@ -3987,10 +4403,52 @@ async function envRemove(key) {
|
|
|
3987
4403
|
process.exit(1);
|
|
3988
4404
|
}
|
|
3989
4405
|
}
|
|
4406
|
+
async function envPull(file) {
|
|
4407
|
+
const token = requireAuth2();
|
|
4408
|
+
const slug = requireSlug();
|
|
4409
|
+
const target = resolve(file ?? ".env.local");
|
|
4410
|
+
status(`Pulling env vars for ${slug}...`);
|
|
4411
|
+
try {
|
|
4412
|
+
const res = await fetch(`${API_BASE}/v1/developer/apps/${slug}/env`, {
|
|
4413
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
4414
|
+
});
|
|
4415
|
+
if (!res.ok) {
|
|
4416
|
+
outputError("api_error", `Failed to pull env vars (${res.status})`);
|
|
4417
|
+
process.exit(1);
|
|
4418
|
+
}
|
|
4419
|
+
const { data } = await res.json();
|
|
4420
|
+
if (isJsonMode()) {
|
|
4421
|
+
outputSuccess({ file: target, keys: data.map((v) => v.key) });
|
|
4422
|
+
return;
|
|
4423
|
+
}
|
|
4424
|
+
if (!data?.length) {
|
|
4425
|
+
console.log(source_default.yellow("No environment variables set."));
|
|
4426
|
+
return;
|
|
4427
|
+
}
|
|
4428
|
+
const lines = [
|
|
4429
|
+
`# Pulled from Bagdock — ${slug}`,
|
|
4430
|
+
`# ${new Date().toISOString()}`,
|
|
4431
|
+
`# Values are placeholders — the API does not expose secrets.`,
|
|
4432
|
+
`# Fill in real values for local development.`,
|
|
4433
|
+
"",
|
|
4434
|
+
...data.map((v) => `${v.key}=`),
|
|
4435
|
+
""
|
|
4436
|
+
];
|
|
4437
|
+
writeFileSync5(target, lines.join(`
|
|
4438
|
+
`));
|
|
4439
|
+
console.log(source_default.green(`Wrote ${data.length} keys to ${target}`));
|
|
4440
|
+
console.log(source_default.yellow("Note:"), "Values are empty — fill them in for local dev.");
|
|
4441
|
+
} catch (err) {
|
|
4442
|
+
console.error(source_default.red("Failed:"), err.message);
|
|
4443
|
+
process.exit(1);
|
|
4444
|
+
}
|
|
4445
|
+
}
|
|
3990
4446
|
var init_env_cmd = __esm(() => {
|
|
3991
4447
|
init_source();
|
|
3992
4448
|
init_config();
|
|
3993
4449
|
init_auth();
|
|
4450
|
+
init_output();
|
|
4451
|
+
init_link();
|
|
3994
4452
|
});
|
|
3995
4453
|
|
|
3996
4454
|
// src/keys.ts
|
|
@@ -4209,7 +4667,7 @@ async function apiRequest3(method, path2) {
|
|
|
4209
4667
|
headers: { Authorization: `Bearer ${token}` }
|
|
4210
4668
|
});
|
|
4211
4669
|
}
|
|
4212
|
-
function
|
|
4670
|
+
function resolveSlug2(slug) {
|
|
4213
4671
|
if (slug)
|
|
4214
4672
|
return slug;
|
|
4215
4673
|
const config = loadBagdockJson(process.cwd());
|
|
@@ -4219,7 +4677,7 @@ function resolveSlug(slug) {
|
|
|
4219
4677
|
return "";
|
|
4220
4678
|
}
|
|
4221
4679
|
async function logsList(opts) {
|
|
4222
|
-
const slug =
|
|
4680
|
+
const slug = resolveSlug2(opts.app);
|
|
4223
4681
|
const limit = opts.limit || "50";
|
|
4224
4682
|
status(`Fetching logs for ${slug}...`);
|
|
4225
4683
|
const res = await apiRequest3("GET", `/api/v1/developer/apps/${slug}/logs?limit=${limit}`);
|
|
@@ -4250,7 +4708,7 @@ async function logsList(opts) {
|
|
|
4250
4708
|
}
|
|
4251
4709
|
}
|
|
4252
4710
|
async function logsGet(id, opts) {
|
|
4253
|
-
const slug =
|
|
4711
|
+
const slug = resolveSlug2(opts.app);
|
|
4254
4712
|
status(`Fetching log entry ${id}...`);
|
|
4255
4713
|
const res = await apiRequest3("GET", `/api/v1/developer/apps/${slug}/logs/${id}`);
|
|
4256
4714
|
if (!res.ok) {
|
|
@@ -4265,7 +4723,7 @@ async function logsGet(id, opts) {
|
|
|
4265
4723
|
}
|
|
4266
4724
|
}
|
|
4267
4725
|
async function logsTail(opts) {
|
|
4268
|
-
const slug =
|
|
4726
|
+
const slug = resolveSlug2(opts.app);
|
|
4269
4727
|
if (isJsonMode()) {
|
|
4270
4728
|
outputError("UNSUPPORTED", "Log tailing is not supported in JSON mode. Use `logs list` instead.");
|
|
4271
4729
|
}
|
|
@@ -4575,7 +5033,7 @@ function toPascalCase(s) {
|
|
|
4575
5033
|
init_output();
|
|
4576
5034
|
init_config();
|
|
4577
5035
|
var program2 = new Command;
|
|
4578
|
-
program2.name("bagdock").description("Bagdock developer CLI — built for humans, AI agents, and CI/CD pipelines").version("0.
|
|
5036
|
+
program2.name("bagdock").description("Bagdock developer CLI — built for humans, AI agents, and CI/CD pipelines").version("0.4.0").option("--json", "Force JSON output (auto-enabled in non-TTY)").option("-q, --quiet", "Suppress status messages (implies --json)").option("--api-key <key>", "API key to use for this invocation").option("-p, --profile <name>", "Profile to use (overrides BAGDOCK_PROFILE)").hook("preAction", (_thisCommand, actionCommand) => {
|
|
4579
5037
|
const opts = program2.opts();
|
|
4580
5038
|
setOutputMode({ json: opts.json, quiet: opts.quiet });
|
|
4581
5039
|
if (opts.apiKey)
|
|
@@ -4606,6 +5064,35 @@ program2.command("submit").description("Submit app for Bagdock marketplace revie
|
|
|
4606
5064
|
const { submit: submit2 } = await Promise.resolve().then(() => (init_submit(), exports_submit));
|
|
4607
5065
|
await submit2();
|
|
4608
5066
|
});
|
|
5067
|
+
program2.command("validate").description("Run local pre-submission checks on bagdock.json and bundle").action(async () => {
|
|
5068
|
+
const { validate: validate2 } = await Promise.resolve().then(() => (init_validate(), exports_validate));
|
|
5069
|
+
await validate2();
|
|
5070
|
+
});
|
|
5071
|
+
var subCmd = program2.command("submission").description("Track marketplace submission status");
|
|
5072
|
+
subCmd.command("list").description("List submission history for the current app").option("--app <slug>", "App slug (defaults to bagdock.json or linked project)").action(async (opts) => {
|
|
5073
|
+
const { submissionList: submissionList2 } = await Promise.resolve().then(() => (init_submission(), exports_submission));
|
|
5074
|
+
await submissionList2(opts);
|
|
5075
|
+
});
|
|
5076
|
+
subCmd.command("status <id>").description("Fetch detailed review state for a submission").option("--app <slug>", "App slug").action(async (id, opts) => {
|
|
5077
|
+
const { submissionStatus: submissionStatus2 } = await Promise.resolve().then(() => (init_submission(), exports_submission));
|
|
5078
|
+
await submissionStatus2(id, opts);
|
|
5079
|
+
});
|
|
5080
|
+
subCmd.command("withdraw <id>").description("Cancel a pending submission before approval").option("--app <slug>", "App slug").action(async (id, opts) => {
|
|
5081
|
+
const { submissionWithdraw: submissionWithdraw2 } = await Promise.resolve().then(() => (init_submission(), exports_submission));
|
|
5082
|
+
await submissionWithdraw2(id, opts);
|
|
5083
|
+
});
|
|
5084
|
+
program2.command("open [slug]").description("Open project in the Bagdock dashboard").action(async (slug) => {
|
|
5085
|
+
const { open: open3 } = await Promise.resolve().then(() => (init_open2(), exports_open2));
|
|
5086
|
+
await open3(slug);
|
|
5087
|
+
});
|
|
5088
|
+
program2.command("inspect [slug]").description("Show deployment details and status for an app").action(async (slug) => {
|
|
5089
|
+
const { inspect: inspect2 } = await Promise.resolve().then(() => (init_inspect(), exports_inspect));
|
|
5090
|
+
await inspect2(slug);
|
|
5091
|
+
});
|
|
5092
|
+
program2.command("link").description("Link current directory to a Bagdock app or edge").option("--slug <slug>", "Project slug (required in non-interactive mode)").action(async (opts) => {
|
|
5093
|
+
const { link: link2 } = await Promise.resolve().then(() => (init_link(), exports_link));
|
|
5094
|
+
await link2(opts);
|
|
5095
|
+
});
|
|
4609
5096
|
var envCmd = program2.command("env").description("Manage app environment variables");
|
|
4610
5097
|
envCmd.command("list").description("List environment variables for this app").action(async () => {
|
|
4611
5098
|
const { envList: envList2 } = await Promise.resolve().then(() => (init_env_cmd(), exports_env_cmd));
|
|
@@ -4619,6 +5106,10 @@ envCmd.command("remove <key>").description("Remove an environment variable").act
|
|
|
4619
5106
|
const { envRemove: envRemove2 } = await Promise.resolve().then(() => (init_env_cmd(), exports_env_cmd));
|
|
4620
5107
|
await envRemove2(key);
|
|
4621
5108
|
});
|
|
5109
|
+
envCmd.command("pull [file]").description("Pull remote env var keys to a local .env file").action(async (file) => {
|
|
5110
|
+
const { envPull: envPull2 } = await Promise.resolve().then(() => (init_env_cmd(), exports_env_cmd));
|
|
5111
|
+
await envPull2(file);
|
|
5112
|
+
});
|
|
4622
5113
|
var keysCmd = program2.command("keys").description("Manage operator API keys");
|
|
4623
5114
|
keysCmd.command("create").description("Create a new API key (raw key shown once)").requiredOption("--name <name>", "Key name").option("--type <type>", "Key type (secret, publishable)", "secret").option("--category <category>", "Key category (standard, restricted, personal)", "standard").option("--environment <env>", "Environment (live, test)", "live").option("--scopes <scopes...>", "Permission scopes").action(async (opts) => {
|
|
4624
5115
|
const { keysCreate: keysCreate2 } = await Promise.resolve().then(() => (init_keys(), exports_keys));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"skill": "bagdock-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"evals": [
|
|
5
5
|
{
|
|
6
6
|
"id": "login-flow",
|
|
@@ -147,6 +147,62 @@
|
|
|
147
147
|
"input": "What apps do I have deployed?",
|
|
148
148
|
"expected_commands": ["bagdock apps list --json"],
|
|
149
149
|
"expected_behavior": "Lists all deployed apps with slugs, types, and status"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"id": "validate-before-submit",
|
|
153
|
+
"description": "Agent should validate before submitting",
|
|
154
|
+
"input": "Check if my adapter is ready to submit to the marketplace",
|
|
155
|
+
"expected_commands": ["bagdock validate --json"],
|
|
156
|
+
"expected_behavior": "Runs local checks on bagdock.json and bundle, returns pass/warn/fail"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"id": "submission-list",
|
|
160
|
+
"description": "Agent should list submissions",
|
|
161
|
+
"input": "Show me all my marketplace submissions for this app",
|
|
162
|
+
"expected_commands": ["bagdock submission list --json"],
|
|
163
|
+
"expected_behavior": "Lists submission history with IDs, versions, and dates"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"id": "submission-status",
|
|
167
|
+
"description": "Agent should check submission status",
|
|
168
|
+
"input": "What's the review status of submission iadpv_abc123?",
|
|
169
|
+
"expected_commands": ["bagdock submission status iadpv_abc123 --json"],
|
|
170
|
+
"expected_behavior": "Returns detailed review state including status, reason, and timestamps"
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"id": "submission-withdraw",
|
|
174
|
+
"description": "Agent should withdraw a pending submission",
|
|
175
|
+
"input": "Withdraw my pending submission iadpv_abc123, I found a bug",
|
|
176
|
+
"expected_commands": ["bagdock submission withdraw iadpv_abc123"],
|
|
177
|
+
"expected_behavior": "Cancels submission, sets review_status back to draft"
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"id": "open-dashboard",
|
|
181
|
+
"description": "Agent should open project in dashboard",
|
|
182
|
+
"input": "Open my adapter in the Bagdock dashboard",
|
|
183
|
+
"expected_commands": ["bagdock open"],
|
|
184
|
+
"expected_behavior": "Opens browser to dashboard URL for the current project"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"id": "inspect-app",
|
|
188
|
+
"description": "Agent should inspect app deployment details",
|
|
189
|
+
"input": "Show me the deployment details for smart-entry",
|
|
190
|
+
"expected_commands": ["bagdock inspect smart-entry --json"],
|
|
191
|
+
"expected_behavior": "Returns app details including worker URL, version, review status"
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"id": "env-pull",
|
|
195
|
+
"description": "Agent should pull env vars for local development",
|
|
196
|
+
"input": "Pull the env var keys from my deployed adapter to a local .env file",
|
|
197
|
+
"expected_commands": ["bagdock env pull .env.local"],
|
|
198
|
+
"expected_behavior": "Creates .env.local with keys and empty values, warns that values need filling"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"id": "link-project",
|
|
202
|
+
"description": "Agent should link directory to a project",
|
|
203
|
+
"input": "Link this directory to my smart-entry adapter",
|
|
204
|
+
"expected_commands": ["bagdock link --slug smart-entry"],
|
|
205
|
+
"expected_behavior": "Creates .bagdock/link.json with slug, other commands use it as fallback"
|
|
150
206
|
}
|
|
151
207
|
]
|
|
152
208
|
}
|
|
@@ -62,9 +62,17 @@ For CI/CD, set `BAGDOCK_API_KEY` in your environment. For interactive use, run `
|
|
|
62
62
|
| `env list` | List app environment variables |
|
|
63
63
|
| `env set <key> <value>` | Set an environment variable |
|
|
64
64
|
| `env remove <key>` | Remove an environment variable |
|
|
65
|
+
| `env pull [file]` | Pull remote env var keys to local .env file |
|
|
65
66
|
| `keys create` | Create a new API key (raw key shown once) |
|
|
66
67
|
| `keys list` | List API keys |
|
|
67
68
|
| `keys delete <id>` | Revoke an API key |
|
|
69
|
+
| `validate` | Run local pre-submission checks on bagdock.json and bundle |
|
|
70
|
+
| `submission list` | List marketplace submission history |
|
|
71
|
+
| `submission status <id>` | Fetch detailed review state for a submission |
|
|
72
|
+
| `submission withdraw <id>` | Cancel a pending submission |
|
|
73
|
+
| `open [slug]` | Open project in Bagdock dashboard |
|
|
74
|
+
| `inspect [slug]` | Show deployment details and status |
|
|
75
|
+
| `link` | Link directory to a Bagdock app or edge |
|
|
68
76
|
| `doctor` | Run environment diagnostics (version, auth, config, agents) |
|
|
69
77
|
| `auth list` | List stored profiles |
|
|
70
78
|
| `auth switch [name]` | Switch active profile |
|
|
@@ -112,6 +120,34 @@ bagdock keys list --json
|
|
|
112
120
|
3. **Missing `bagdock.json`** — `deploy`, `submit`, and `env` commands require a `bagdock.json` in the current directory. Run `bagdock init` first.
|
|
113
121
|
4. **Expired session** — If `whoami` fails, run `bagdock login` again. Sessions expire after 8 hours.
|
|
114
122
|
|
|
123
|
+
### Validate before submitting
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
bagdock validate
|
|
127
|
+
bagdock submit
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Check submission status
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
bagdock submission list --json
|
|
134
|
+
bagdock submission status iadpv_xxx
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Link a directory and inspect
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
bagdock link --slug my-adapter
|
|
141
|
+
bagdock inspect
|
|
142
|
+
bagdock open
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Pull env vars for local dev
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
bagdock env pull .env.local
|
|
149
|
+
```
|
|
150
|
+
|
|
115
151
|
## When to Load References
|
|
116
152
|
|
|
117
153
|
Load specific reference files when the task involves:
|
|
@@ -121,3 +157,5 @@ Load specific reference files when the task involves:
|
|
|
121
157
|
- **Environment variables** → `references/env.md`
|
|
122
158
|
- **Local development** → `references/dev.md`
|
|
123
159
|
- **Error codes or troubleshooting** → `references/error-codes.md`
|
|
160
|
+
- **Marketplace submission lifecycle** → `references/marketplace.md`
|
|
161
|
+
- **App management (open, inspect, link)** → `references/app-management.md`
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# App Management Commands
|
|
2
|
+
|
|
3
|
+
Commands for managing the relationship between your local directory and a Bagdock app or edge.
|
|
4
|
+
|
|
5
|
+
## `bagdock link`
|
|
6
|
+
|
|
7
|
+
Links the current directory to a Bagdock app or edge. Once linked, other commands (deploy, env, open, inspect, submission) use the linked slug as a fallback — no `bagdock.json` required.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Interactive: select from your apps
|
|
11
|
+
bagdock link
|
|
12
|
+
|
|
13
|
+
# Non-interactive (CI/agents)
|
|
14
|
+
bagdock link --slug my-adapter
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Stores the link in `.bagdock/link.json`:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"slug": "my-adapter",
|
|
22
|
+
"linkedAt": "2026-04-05T00:00:00.000Z"
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Slug Resolution Order
|
|
27
|
+
|
|
28
|
+
1. Explicit `--slug` or `--app` argument
|
|
29
|
+
2. `bagdock.json` in current directory
|
|
30
|
+
3. `.bagdock/link.json` (linked app)
|
|
31
|
+
|
|
32
|
+
## `bagdock open [slug]`
|
|
33
|
+
|
|
34
|
+
Opens the project in the Bagdock dashboard. Uses the slug resolution order above.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
bagdock open
|
|
38
|
+
bagdock open my-adapter
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
In JSON mode, returns the URL instead of opening the browser:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
bagdock open --json
|
|
45
|
+
# => {"url":"https://dashboard.bagdock.com/developer/apps/my-adapter","slug":"my-adapter"}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## `bagdock inspect [slug]`
|
|
49
|
+
|
|
50
|
+
Shows deployment details for an app.
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
bagdock inspect
|
|
54
|
+
bagdock inspect my-adapter --json
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Displays:
|
|
58
|
+
- Name, slug, ID
|
|
59
|
+
- Type, category
|
|
60
|
+
- Version, maintainer, visibility
|
|
61
|
+
- Review status
|
|
62
|
+
- Worker URL, namespace
|
|
63
|
+
- Created, updated, published timestamps
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Marketplace Submission Lifecycle
|
|
2
|
+
|
|
3
|
+
The Bagdock marketplace uses a reviewed submission model. Developers submit, Bagdock reviews, then approves or rejects. `bagdock publish` is intentionally not a CLI command — final publication is handled by Bagdock staff.
|
|
4
|
+
|
|
5
|
+
## Workflow
|
|
6
|
+
|
|
7
|
+
1. `bagdock validate` — Run local checks before uploading
|
|
8
|
+
2. `bagdock submit` — Upload bundle and request review (draft -> submitted)
|
|
9
|
+
3. `bagdock submission list` — View all submissions
|
|
10
|
+
4. `bagdock submission status <id>` — Check review progress
|
|
11
|
+
5. `bagdock submission withdraw <id>` — Cancel before approval (submitted -> draft)
|
|
12
|
+
|
|
13
|
+
## Commands
|
|
14
|
+
|
|
15
|
+
### `bagdock validate`
|
|
16
|
+
|
|
17
|
+
Local-only checks — no API call needed. Validates:
|
|
18
|
+
|
|
19
|
+
- `bagdock.json` exists and parses
|
|
20
|
+
- Required fields present (name, slug, version, type, category, main)
|
|
21
|
+
- Type is `edge` or `app`
|
|
22
|
+
- Entry point file exists
|
|
23
|
+
- Bundle size under 10 MB
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
bagdock validate
|
|
27
|
+
bagdock validate --json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Exit code 0 = pass/warn, 1 = fail.
|
|
31
|
+
|
|
32
|
+
### `bagdock submission list`
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
bagdock submission list --app my-adapter
|
|
36
|
+
bagdock submission list --json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### `bagdock submission status <id>`
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
bagdock submission status iadpv_abc123 --json
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Returns: app name, version, review_status, type, visibility, change_reason, submitted_by, date.
|
|
46
|
+
|
|
47
|
+
### `bagdock submission withdraw <id>`
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
bagdock submission withdraw iadpv_abc123
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Only works when `review_status` is `submitted`. Returns the app to `draft` status so you can make changes and re-submit.
|
|
54
|
+
|
|
55
|
+
## Status Values
|
|
56
|
+
|
|
57
|
+
| Status | Meaning |
|
|
58
|
+
|--------|---------|
|
|
59
|
+
| `draft` | Not yet submitted |
|
|
60
|
+
| `submitted` | Under review |
|
|
61
|
+
| `approved` | Cleared for production |
|
|
62
|
+
| `rejected` | Changes requested |
|
|
63
|
+
|
|
64
|
+
## API Endpoints
|
|
65
|
+
|
|
66
|
+
- `POST /v1/developer/apps/:slug/submit` — Submit
|
|
67
|
+
- `GET /v1/developer/apps/:slug/submissions` — List
|
|
68
|
+
- `GET /v1/developer/apps/:slug/submissions/:id` — Detail
|
|
69
|
+
- `POST /v1/developer/apps/:slug/submissions/:id/withdraw` — Withdraw
|