@spec0/cli 0.1.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/AGENTS.md +67 -0
- package/LICENSE +21 -0
- package/README.md +235 -0
- package/dist/commands/api/changelog.d.ts +9 -0
- package/dist/commands/api/changelog.d.ts.map +1 -0
- package/dist/commands/api/changelog.js +144 -0
- package/dist/commands/api/changelog.js.map +1 -0
- package/dist/commands/api/index.d.ts +6 -0
- package/dist/commands/api/index.d.ts.map +1 -0
- package/dist/commands/api/index.js +13 -0
- package/dist/commands/api/index.js.map +1 -0
- package/dist/commands/api/list.d.ts +10 -0
- package/dist/commands/api/list.d.ts.map +1 -0
- package/dist/commands/api/list.js +79 -0
- package/dist/commands/api/list.js.map +1 -0
- package/dist/commands/api/show.d.ts +9 -0
- package/dist/commands/api/show.d.ts.map +1 -0
- package/dist/commands/api/show.js +88 -0
- package/dist/commands/api/show.js.map +1 -0
- package/dist/commands/auth.d.ts +6 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +166 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/ci/generate.d.ts +10 -0
- package/dist/commands/ci/generate.d.ts.map +1 -0
- package/dist/commands/ci/generate.js +72 -0
- package/dist/commands/ci/generate.js.map +1 -0
- package/dist/commands/ci/index.d.ts +3 -0
- package/dist/commands/ci/index.d.ts.map +1 -0
- package/dist/commands/ci/index.js +6 -0
- package/dist/commands/ci/index.js.map +1 -0
- package/dist/commands/commands.d.ts +19 -0
- package/dist/commands/commands.d.ts.map +1 -0
- package/dist/commands/commands.js +127 -0
- package/dist/commands/commands.js.map +1 -0
- package/dist/commands/diff.d.ts +6 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +102 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/doctor.d.ts +11 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +54 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +67 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/lint.d.ts +6 -0
- package/dist/commands/lint.d.ts.map +1 -0
- package/dist/commands/lint.js +109 -0
- package/dist/commands/lint.js.map +1 -0
- package/dist/commands/log.d.ts +6 -0
- package/dist/commands/log.d.ts.map +1 -0
- package/dist/commands/log.js +95 -0
- package/dist/commands/log.js.map +1 -0
- package/dist/commands/mcp.d.ts +6 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +59 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/mock/create.d.ts +14 -0
- package/dist/commands/mock/create.d.ts.map +1 -0
- package/dist/commands/mock/create.js +75 -0
- package/dist/commands/mock/create.js.map +1 -0
- package/dist/commands/mock/index.d.ts +9 -0
- package/dist/commands/mock/index.d.ts.map +1 -0
- package/dist/commands/mock/index.js +18 -0
- package/dist/commands/mock/index.js.map +1 -0
- package/dist/commands/mock/list.d.ts +8 -0
- package/dist/commands/mock/list.d.ts.map +1 -0
- package/dist/commands/mock/list.js +52 -0
- package/dist/commands/mock/list.js.map +1 -0
- package/dist/commands/mock/show.d.ts +9 -0
- package/dist/commands/mock/show.d.ts.map +1 -0
- package/dist/commands/mock/show.js +68 -0
- package/dist/commands/mock/show.js.map +1 -0
- package/dist/commands/mock/url.d.ts +10 -0
- package/dist/commands/mock/url.d.ts.map +1 -0
- package/dist/commands/mock/url.js +42 -0
- package/dist/commands/mock/url.js.map +1 -0
- package/dist/commands/mock.d.ts +6 -0
- package/dist/commands/mock.d.ts.map +1 -0
- package/dist/commands/mock.js +116 -0
- package/dist/commands/mock.js.map +1 -0
- package/dist/commands/publish.d.ts +11 -0
- package/dist/commands/publish.d.ts.map +1 -0
- package/dist/commands/publish.js +242 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/pull.d.ts +6 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +62 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/push.d.ts +18 -0
- package/dist/commands/push.d.ts.map +1 -0
- package/dist/commands/push.js +302 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/register.d.ts +6 -0
- package/dist/commands/register.d.ts.map +1 -0
- package/dist/commands/register.js +213 -0
- package/dist/commands/register.js.map +1 -0
- package/dist/commands/search.d.ts +6 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +58 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +86 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync-status.d.ts +14 -0
- package/dist/commands/sync-status.d.ts.map +1 -0
- package/dist/commands/sync-status.js +119 -0
- package/dist/commands/sync-status.js.map +1 -0
- package/dist/commands/team.d.ts +6 -0
- package/dist/commands/team.d.ts.map +1 -0
- package/dist/commands/team.js +30 -0
- package/dist/commands/team.js.map +1 -0
- package/dist/commands/version.d.ts +6 -0
- package/dist/commands/version.d.ts.map +1 -0
- package/dist/commands/version.js +31 -0
- package/dist/commands/version.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/agent-mode.d.ts +25 -0
- package/dist/lib/agent-mode.d.ts.map +1 -0
- package/dist/lib/agent-mode.js +36 -0
- package/dist/lib/agent-mode.js.map +1 -0
- package/dist/lib/api-client.d.ts +37 -0
- package/dist/lib/api-client.d.ts.map +1 -0
- package/dist/lib/api-client.js +144 -0
- package/dist/lib/api-client.js.map +1 -0
- package/dist/lib/auth-context.d.ts +18 -0
- package/dist/lib/auth-context.d.ts.map +1 -0
- package/dist/lib/auth-context.js +48 -0
- package/dist/lib/auth-context.js.map +1 -0
- package/dist/lib/breaking-change.d.ts +12 -0
- package/dist/lib/breaking-change.d.ts.map +1 -0
- package/dist/lib/breaking-change.js +10 -0
- package/dist/lib/breaking-change.js.map +1 -0
- package/dist/lib/ci-detect.d.ts +33 -0
- package/dist/lib/ci-detect.d.ts.map +1 -0
- package/dist/lib/ci-detect.js +136 -0
- package/dist/lib/ci-detect.js.map +1 -0
- package/dist/lib/cli-spec-path.d.ts +6 -0
- package/dist/lib/cli-spec-path.d.ts.map +1 -0
- package/dist/lib/cli-spec-path.js +34 -0
- package/dist/lib/cli-spec-path.js.map +1 -0
- package/dist/lib/codeowners.d.ts +11 -0
- package/dist/lib/codeowners.d.ts.map +1 -0
- package/dist/lib/codeowners.js +43 -0
- package/dist/lib/codeowners.js.map +1 -0
- package/dist/lib/config.d.ts +24 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +49 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/deprecation.d.ts +17 -0
- package/dist/lib/deprecation.d.ts.map +1 -0
- package/dist/lib/deprecation.js +22 -0
- package/dist/lib/deprecation.js.map +1 -0
- package/dist/lib/doctor.d.ts +40 -0
- package/dist/lib/doctor.d.ts.map +1 -0
- package/dist/lib/doctor.js +107 -0
- package/dist/lib/doctor.js.map +1 -0
- package/dist/lib/exit-codes.d.ts +51 -0
- package/dist/lib/exit-codes.d.ts.map +1 -0
- package/dist/lib/exit-codes.js +78 -0
- package/dist/lib/exit-codes.js.map +1 -0
- package/dist/lib/lint.d.ts +18 -0
- package/dist/lib/lint.d.ts.map +1 -0
- package/dist/lib/lint.js +41 -0
- package/dist/lib/lint.js.map +1 -0
- package/dist/lib/local-config-yaml.d.ts +14 -0
- package/dist/lib/local-config-yaml.d.ts.map +1 -0
- package/dist/lib/local-config-yaml.js +29 -0
- package/dist/lib/local-config-yaml.js.map +1 -0
- package/dist/lib/output/index.d.ts +69 -0
- package/dist/lib/output/index.d.ts.map +1 -0
- package/dist/lib/output/index.js +120 -0
- package/dist/lib/output/index.js.map +1 -0
- package/dist/lib/output/table.d.ts +19 -0
- package/dist/lib/output/table.d.ts.map +1 -0
- package/dist/lib/output/table.js +48 -0
- package/dist/lib/output/table.js.map +1 -0
- package/dist/lib/output.d.ts +32 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +64 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/platform-defaults.d.ts +27 -0
- package/dist/lib/platform-defaults.d.ts.map +1 -0
- package/dist/lib/platform-defaults.js +36 -0
- package/dist/lib/platform-defaults.js.map +1 -0
- package/dist/lib/ref-resolver.d.ts +37 -0
- package/dist/lib/ref-resolver.d.ts.map +1 -0
- package/dist/lib/ref-resolver.js +56 -0
- package/dist/lib/ref-resolver.js.map +1 -0
- package/dist/lib/registry-ref.d.ts +10 -0
- package/dist/lib/registry-ref.d.ts.map +1 -0
- package/dist/lib/registry-ref.js +18 -0
- package/dist/lib/registry-ref.js.map +1 -0
- package/dist/lib/spec-finder.d.ts +6 -0
- package/dist/lib/spec-finder.d.ts.map +1 -0
- package/dist/lib/spec-finder.js +42 -0
- package/dist/lib/spec-finder.js.map +1 -0
- package/dist/lib/update-check.d.ts +12 -0
- package/dist/lib/update-check.d.ts.map +1 -0
- package/dist/lib/update-check.js +62 -0
- package/dist/lib/update-check.js.map +1 -0
- package/dist/lib/version.d.ts +17 -0
- package/dist/lib/version.d.ts.map +1 -0
- package/dist/lib/version.js +33 -0
- package/dist/lib/version.js.map +1 -0
- package/dist/lib/winspect-yaml.d.ts +14 -0
- package/dist/lib/winspect-yaml.d.ts.map +1 -0
- package/dist/lib/winspect-yaml.js +28 -0
- package/dist/lib/winspect-yaml.js.map +1 -0
- package/dist/types.d.ts +664 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +87 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Agent notes — Spec0 CLI
|
|
2
|
+
|
|
3
|
+
This repository is the **Node.js** CLI (`@spec0/cli`).
|
|
4
|
+
|
|
5
|
+
## Environment
|
|
6
|
+
|
|
7
|
+
- **Node:** `>=20` (see `package.json` `engines`).
|
|
8
|
+
- **Install deps:** `npm ci` (CI) or `npm install` (local).
|
|
9
|
+
|
|
10
|
+
## Use the CLI locally before pushing (global `spec0`)
|
|
11
|
+
|
|
12
|
+
To exercise the same global **`spec0`** command users get, from the repo root after dependencies are installed:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm run build
|
|
16
|
+
npm link
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then `spec0` on your PATH runs this checkout’s `dist/index.js`. **Rebuild after every change** to `src/`: `npm run build`.
|
|
20
|
+
|
|
21
|
+
To remove the global link: run **`npm unlink`** from the repo root (undoes `npm link`), or `npm unlink -g @spec0/cli` / `npm uninstall -g @spec0/cli`.
|
|
22
|
+
|
|
23
|
+
**Alternative:** `npm install -g .` after each build (heavier than `npm link`).
|
|
24
|
+
|
|
25
|
+
## Verify changes
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm run build
|
|
29
|
+
node dist/index.js --help
|
|
30
|
+
node dist/index.js version
|
|
31
|
+
node dist/index.js version --json
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or `npm run cli -- <args>` (runs `node dist/index.js`).
|
|
35
|
+
|
|
36
|
+
**Tests:** `npm test` runs `pretest` (which runs `npm run build`) then Jest with `NODE_OPTIONS=--experimental-vm-modules`. A global `spec0` from `npm link` matches the working tree only **after** the latest `npm run build`.
|
|
37
|
+
|
|
38
|
+
## Multi-branch / multi-worktree
|
|
39
|
+
|
|
40
|
+
- Prefer **`node /absolute/path/to/checkout/dist/index.js`** so each branch or git worktree uses its own build.
|
|
41
|
+
- Optionally set **`SPEC0_CLI_GIT_REF`** (e.g. `$(git rev-parse --abbrev-ref HEAD)`) when comparing `spec0 version` output across checkouts.
|
|
42
|
+
- **`npm link`** only maps one checkout to the global `spec0` name; it is a poor fit for comparing two branches.
|
|
43
|
+
|
|
44
|
+
## Platform URLs
|
|
45
|
+
|
|
46
|
+
- **`SPEC0_APP_URL`** — Web app: **browser auth** (`spec0 auth login` → `/cli-auth`) and **register output links** to the dashboard (`specUrl`). Not used for REST calls.
|
|
47
|
+
- **`SPEC0_API_URL`** — Spring backend: **every** programmatic HTTP call (register, pull, mocks, lint, search, …). Stored as `org.apiUrl` on login.
|
|
48
|
+
|
|
49
|
+
Defaults in `src/lib/platform-defaults.ts`. **`SPEC0_API_URL` in the environment overrides** stored `apiUrl` for all commands (see `src/lib/auth-context.ts`). Re-login to persist a new API base in `~/.spec0/config.json`.
|
|
50
|
+
|
|
51
|
+
Legacy `PLATFORM_*` variants are still accepted for backwards compatibility; they will be removed in the next major.
|
|
52
|
+
|
|
53
|
+
## API clients
|
|
54
|
+
|
|
55
|
+
Implementation lives under `src/` (Commander commands, `src/lib/*`). Follow existing patterns when adding HTTP calls or config.
|
|
56
|
+
|
|
57
|
+
## Refreshing the vendored OpenAPI spec
|
|
58
|
+
|
|
59
|
+
The CLI's own OpenAPI contract lives at `openapi-spec/cli-api-spec.yaml` and is published to the platform as `spec0/cli-api`. To refresh it after the backend surface changes:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm run sync:spec
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
That runs `spec0 pull spec0/cli-api -o openapi-spec/cli-api-spec.yaml` and regenerates `src/types.ts`. Commit the updated YAML + types alongside the feature that consumes them.
|
|
66
|
+
|
|
67
|
+
`sync:spec` calls into `dist/index.js`, so run `npm run build` first on fresh clones. Auth via `SPEC0_TOKEN` + `SPEC0_ORG_ID` env vars, or `spec0 auth login` for interactive use.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 spec0
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# @spec0/cli
|
|
2
|
+
|
|
3
|
+
The scriptable control plane for the [Spec0](https://spec0.io) API platform — designed for humans at the terminal, CI pipelines, **and AI agents**.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install -g @spec0/cli
|
|
7
|
+
spec0 --version
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Requires **Node.js 20+**.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## What is Spec0?
|
|
15
|
+
|
|
16
|
+
Spec0 is a platform for managing the full lifecycle of an API contract:
|
|
17
|
+
|
|
18
|
+
- **Registry** — a searchable catalogue of every API in your organisation, versioned, team-owned, and permission-scoped.
|
|
19
|
+
- **Publish & pull** — push an OpenAPI spec; pull it back anywhere from CI, a browser, or another service.
|
|
20
|
+
- **Hosted mocks** — every published spec gets a mock server that serves realistic responses for the operations in the spec.
|
|
21
|
+
- **Quality gates** — Spectral-based linting with org-wide rulesets, semver-aware diffing (via oasdiff), breaking-change detection.
|
|
22
|
+
- **Discoverable contracts** — MCP server for AI tools, changelogs, subscriptions, and a dashboard web UI.
|
|
23
|
+
|
|
24
|
+
The CLI is the **scriptable bridge** between that platform and the systems that consume it. Anything you can do in the web UI for API + mock management, you can do from a shell.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Who this is for
|
|
29
|
+
|
|
30
|
+
Three first-class audiences, one command surface:
|
|
31
|
+
|
|
32
|
+
### Humans
|
|
33
|
+
|
|
34
|
+
At the terminal or in a dev loop.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
spec0 lint openapi.yaml --min-score 80
|
|
38
|
+
spec0 publish openapi.yaml --semver
|
|
39
|
+
spec0 mock show my-api
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Colorised output, sensible defaults, interactive `auth login`.
|
|
43
|
+
|
|
44
|
+
### CI / CD pipelines
|
|
45
|
+
|
|
46
|
+
Non-interactive, deterministic, stable exit codes.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
export SPEC0_TOKEN=${{ secrets.SPEC0_TOKEN }}
|
|
50
|
+
export SPEC0_ORG_ID=${{ secrets.SPEC0_ORG_ID }}
|
|
51
|
+
spec0 sync-status my-api --output=json | jq -e '.needsPublish' || exit 0
|
|
52
|
+
spec0 publish openapi.yaml --semver
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
See [`docs/guides/ci-github-actions.md`](docs/guides/ci-github-actions.md).
|
|
56
|
+
|
|
57
|
+
### AI agents
|
|
58
|
+
|
|
59
|
+
The CLI is **self-describing**: every command emits machine-readable output (`--output=json`), and `spec0 commands --output=json` returns a full capability manifest — every command, every flag, every exit code — so an agent can discover and compose operations without hardcoding.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
spec0 commands --output=json | jq '.commands[].name'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
See [`docs/guides/ai-agents.md`](docs/guides/ai-agents.md) for the full agent playbook, including:
|
|
66
|
+
|
|
67
|
+
- How to discover capabilities at runtime.
|
|
68
|
+
- Decision trees for common tasks ("user wants to publish a spec → ...").
|
|
69
|
+
- Structured error-handling and exit-code reactions.
|
|
70
|
+
- MCP integration for semantic search over the org's APIs.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 60-second quickstart
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# 1. authenticate
|
|
78
|
+
spec0 auth login
|
|
79
|
+
|
|
80
|
+
# 2. from a repo containing an OpenAPI file
|
|
81
|
+
spec0 init # writes .spec0.yaml
|
|
82
|
+
spec0 lint openapi.yaml --min-score 80 # optional quality gate
|
|
83
|
+
spec0 publish openapi.yaml --semver # public registry, auto-bumped
|
|
84
|
+
|
|
85
|
+
# 3. spin up a mock server
|
|
86
|
+
spec0 mock create --api my-api
|
|
87
|
+
spec0 mock show my-api
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Not sure it's configured right?
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
spec0 doctor
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Prints which source each setting resolved from (env var, config, or default).
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Commands
|
|
101
|
+
|
|
102
|
+
Every command supports `--output=text|json|yaml`. Progress / logs go to stderr, structured results go to stdout — safe to pipe.
|
|
103
|
+
|
|
104
|
+
### Auth & diagnostics
|
|
105
|
+
|
|
106
|
+
| Command | Description |
|
|
107
|
+
| ------------------- | ----------------------------------------------------------------------- |
|
|
108
|
+
| `spec0 auth login` | Browser-based login; stores API key locally. |
|
|
109
|
+
| `spec0 auth logout` | Clear the locally stored token. |
|
|
110
|
+
| `spec0 auth status` | Print the active org + key metadata. |
|
|
111
|
+
| `spec0 auth switch` | Switch the default org (multi-tenant setups). |
|
|
112
|
+
| `spec0 whoami` | One-line org + user summary. |
|
|
113
|
+
| `spec0 doctor` | Print which source each setting resolved from (env / config / default). |
|
|
114
|
+
|
|
115
|
+
### Spec lifecycle
|
|
116
|
+
|
|
117
|
+
| Command | Description |
|
|
118
|
+
| -------------------- | ----------------------------------------------------------------------- |
|
|
119
|
+
| `spec0 init` | Detect `openapi.yaml` in cwd, write `.spec0.yaml`. |
|
|
120
|
+
| `spec0 push` | Upload spec to the team-scoped workspace (private). |
|
|
121
|
+
| `spec0 publish` | Publish to the public registry; `--semver` auto-bumps based on oasdiff. |
|
|
122
|
+
| `spec0 lint` | Spectral lint; `--org-ruleset`, `--save-ruleset <file>`, `--min-score`. |
|
|
123
|
+
| `spec0 pull <ref>` | Download a published spec (`acme/orders@v1.2.0`). |
|
|
124
|
+
| `spec0 diff <a> <b>` | Diff specs; both sides can be file paths or registry refs. |
|
|
125
|
+
| `spec0 log <ref>` | Version history for a published API. |
|
|
126
|
+
| `spec0 search` | Semantic search across the org's APIs. |
|
|
127
|
+
|
|
128
|
+
### API management
|
|
129
|
+
|
|
130
|
+
| Command | Description |
|
|
131
|
+
| --------------------------- | ------------------------------------------------------------------ |
|
|
132
|
+
| `spec0 api list` | Catalogue view with `--team / --status / --search` filters. |
|
|
133
|
+
| `spec0 api show <ref>` | Single-API summary (counts, team, status). |
|
|
134
|
+
| `spec0 api changelog <ref>` | Diff between published versions; `--from / --to`, markdown output. |
|
|
135
|
+
|
|
136
|
+
### Mock servers
|
|
137
|
+
|
|
138
|
+
| Command | Description |
|
|
139
|
+
| ----------------------- | ----------------------------------------------------------- |
|
|
140
|
+
| `spec0 mock create` | Provision (or fetch) the default mock; prints one-time key. |
|
|
141
|
+
| `spec0 mock list` | Table of every mock in the org. |
|
|
142
|
+
| `spec0 mock show <api>` | Structured view (URL + metadata) for one mock. |
|
|
143
|
+
| `spec0 mock url <api>` | Pipe-friendly single-line URL (for `$(…)` substitution). |
|
|
144
|
+
|
|
145
|
+
### CI helpers
|
|
146
|
+
|
|
147
|
+
| Command | Description |
|
|
148
|
+
| -------------------------- | -------------------------------------------------------------- |
|
|
149
|
+
| `spec0 sync-status <ref>` | Has the spec changed since last publish? Auto-detects git SHA. |
|
|
150
|
+
| `spec0 ci generate github` | Emit a ready-to-commit `.github/workflows/spec0-publish.yml`. |
|
|
151
|
+
| `spec0 status` | Org overview: API count, mocks, teams, plan. |
|
|
152
|
+
|
|
153
|
+
### Agent & introspection
|
|
154
|
+
|
|
155
|
+
| Command | Description |
|
|
156
|
+
| -------------------------- | ------------------------------------------------------------------------ |
|
|
157
|
+
| `spec0 commands` | List every command with its flags, args, and exit codes (JSON-friendly). |
|
|
158
|
+
| `spec0 commands <pattern>` | Filter the manifest by substring. |
|
|
159
|
+
| `spec0 mcp url` | Print the MCP server URL for Cursor / Claude. |
|
|
160
|
+
|
|
161
|
+
### Other
|
|
162
|
+
|
|
163
|
+
- `spec0 version` — CLI + Node version.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Authentication
|
|
168
|
+
|
|
169
|
+
Two paths:
|
|
170
|
+
|
|
171
|
+
1. **Interactive**: `spec0 auth login` writes `~/.config/spec0/config.json`.
|
|
172
|
+
2. **Non-interactive**: export `SPEC0_TOKEN` and `SPEC0_ORG_ID`.
|
|
173
|
+
|
|
174
|
+
| Variable | Purpose | Default |
|
|
175
|
+
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- |
|
|
176
|
+
| `SPEC0_TOKEN` | Bearer token sent on every request. | _required_ for non-auth cmds |
|
|
177
|
+
| `SPEC0_ORG_ID` | UUID of the org the CLI acts against. | _required_ |
|
|
178
|
+
| `SPEC0_API_URL` | Platform backend base URL. | `https://api.spec0.io` |
|
|
179
|
+
| `SPEC0_APP_URL` | Platform web app (used for auth callback + dashboard links). | `https://spec0.io` |
|
|
180
|
+
| `SPEC0_MODE` | Set to `agent` to flip every default for machine callers: JSON output, no colour, no spinners, no update banner. See the [agent guide](docs/guides/ai-agents.md). | _unset_ (human mode) |
|
|
181
|
+
|
|
182
|
+
`PLATFORM_*` variants are accepted for backwards compatibility; they'll be removed in the next major. Use `spec0 doctor` to see which source each value is resolving from.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Exit codes
|
|
187
|
+
|
|
188
|
+
Stable forever — CI pipelines and agents depend on them.
|
|
189
|
+
|
|
190
|
+
| Code | Meaning |
|
|
191
|
+
| ---- | --------------------------------------------------- |
|
|
192
|
+
| 0 | success |
|
|
193
|
+
| 1 | generic / unclassified failure |
|
|
194
|
+
| 2 | usage error (bad flags, missing args) |
|
|
195
|
+
| 3 | not authenticated (no token / token expired) |
|
|
196
|
+
| 4 | permission denied (403) |
|
|
197
|
+
| 5 | resource not found (404) |
|
|
198
|
+
| 6 | conflict (409 — e.g. name already taken) |
|
|
199
|
+
| 7 | validation failed (422 — e.g. spec below min score) |
|
|
200
|
+
| 8 | rate limited (429) |
|
|
201
|
+
| 9 | upstream server error (5xx) |
|
|
202
|
+
| 10 | network error (unreachable, timeout) |
|
|
203
|
+
|
|
204
|
+
The same table is emitted by `spec0 commands --output=json`.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Reference
|
|
209
|
+
|
|
210
|
+
- [**Full command reference**](docs/commands/README.md) — auto-generated per-command pages with every flag, argument, and exit code. Regenerated from the live binary via `npm run docs`.
|
|
211
|
+
|
|
212
|
+
## Guides
|
|
213
|
+
|
|
214
|
+
- [Getting started](docs/guides/getting-started.md) — zero to published.
|
|
215
|
+
- [GitHub Actions CI](docs/guides/ci-github-actions.md) — publish-on-change workflow.
|
|
216
|
+
- [Mock server lifecycle](docs/guides/mock-server.md) — create → show → probe.
|
|
217
|
+
- [**AI agents**](docs/guides/ai-agents.md) — how agents discover, compose, and react to commands.
|
|
218
|
+
|
|
219
|
+
### Recipes
|
|
220
|
+
|
|
221
|
+
Task-shaped, copy-paste-friendly:
|
|
222
|
+
|
|
223
|
+
- [Gate merges on breaking changes](docs/recipes/block-on-breaking-changes.md)
|
|
224
|
+
- [Refresh a mock after every publish](docs/recipes/refresh-mock-after-publish.md)
|
|
225
|
+
- [Let an agent publish on your behalf](docs/recipes/agent-publish-flow.md)
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Contributing
|
|
230
|
+
|
|
231
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md). Typos and small fixes welcome via PR; for anything larger, open an issue first. Security reports go via [`SECURITY.md`](SECURITY.md).
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
MIT. See [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spec0 api changelog <ref> — show what changed between published versions.
|
|
3
|
+
*
|
|
4
|
+
* Defaults to "latest vs previous". Pass --from / --to to diff specific tags.
|
|
5
|
+
* Backed by /api-management/cli/v1/versions/{apiId}/diff.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
export declare function registerApiChangelogCommand(api: Command): void;
|
|
9
|
+
//# sourceMappingURL=changelog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../../../src/commands/api/changelog.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqCpC,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,OAAO,QA0EvD"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spec0 api changelog <ref> — show what changed between published versions.
|
|
3
|
+
*
|
|
4
|
+
* Defaults to "latest vs previous". Pass --from / --to to diff specific tags.
|
|
5
|
+
* Backed by /api-management/cli/v1/versions/{apiId}/diff.
|
|
6
|
+
*/
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import { createOrgApiClient, is401 } from "../../lib/api-client.js";
|
|
9
|
+
import { requireOrgContext } from "../../lib/auth-context.js";
|
|
10
|
+
import { ExitCode } from "../../lib/exit-codes.js";
|
|
11
|
+
import { emit, fail, resolveOutputContext, } from "../../lib/output/index.js";
|
|
12
|
+
import { resolveRef, resolveApiId } from "../../lib/ref-resolver.js";
|
|
13
|
+
import { getDefaultOrgId, getOrgConfig } from "../../lib/config.js";
|
|
14
|
+
export function registerApiChangelogCommand(api) {
|
|
15
|
+
api
|
|
16
|
+
.command("changelog <ref>")
|
|
17
|
+
.description("Show changes between published versions of an API.")
|
|
18
|
+
.option("--from <tag>", "Earlier version tag (default: previous published)")
|
|
19
|
+
.option("--to <tag>", "Later version tag (default: latest published)")
|
|
20
|
+
.option("--org <uuid>", "Org id override")
|
|
21
|
+
.option("--output <format>", "Output format: text, json, markdown, or yaml (default: text)")
|
|
22
|
+
.action(async (ref, opts) => {
|
|
23
|
+
const outCtx = resolveOutputContext(opts);
|
|
24
|
+
let authCtx;
|
|
25
|
+
try {
|
|
26
|
+
authCtx = requireOrgContext(opts.org);
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
fail(outCtx, ExitCode.AUTH_MISSING, e.message, {
|
|
30
|
+
hint: "Set SPEC0_TOKEN + SPEC0_ORG_ID, or run 'spec0 auth login'.",
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const defaultOrg = (() => {
|
|
34
|
+
const id = process.env.PLATFORM_ORG_ID ?? getDefaultOrgId();
|
|
35
|
+
return id ? getOrgConfig(id)?.name : undefined;
|
|
36
|
+
})();
|
|
37
|
+
let parsed;
|
|
38
|
+
try {
|
|
39
|
+
parsed = resolveRef(ref, { defaultOrg });
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
fail(outCtx, ExitCode.USAGE, e.message);
|
|
43
|
+
}
|
|
44
|
+
const client = createOrgApiClient(authCtx);
|
|
45
|
+
try {
|
|
46
|
+
const apiId = await resolveApiId(client, parsed);
|
|
47
|
+
const { fromTag, toTag, apiName } = await pickTags(outCtx, client, apiId, parsed, opts);
|
|
48
|
+
const params = new URLSearchParams({ fromTag, toTag });
|
|
49
|
+
const res = (await client.getJson(`/api-management/cli/v1/versions/${encodeURIComponent(apiId)}/diff?${params.toString()}`));
|
|
50
|
+
const payload = {
|
|
51
|
+
apiName,
|
|
52
|
+
fromTag,
|
|
53
|
+
toTag,
|
|
54
|
+
hasBreakingChanges: Boolean(res.hasBreakingChanges),
|
|
55
|
+
changes: normaliseChanges(res),
|
|
56
|
+
};
|
|
57
|
+
// Format `markdown` is treated as `text` with a markdown body.
|
|
58
|
+
if ((opts.output ?? "").toLowerCase() === "markdown") {
|
|
59
|
+
process.stdout.write(renderChangelogMarkdown(payload) + "\n");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
emit(outCtx, payload, renderChangelogText);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (is401(err)) {
|
|
66
|
+
fail(outCtx, ExitCode.AUTH_MISSING, "Token invalid or expired.", {
|
|
67
|
+
hint: "Run 'spec0 auth login' or refresh SPEC0_TOKEN.",
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (err.message?.includes("No API named")) {
|
|
71
|
+
fail(outCtx, ExitCode.NOT_FOUND, err.message, {
|
|
72
|
+
hint: "Run 'spec0 api list' to see what exists in this org.",
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
fail(outCtx, ExitCode.GENERIC, `api changelog failed: ${err.message}`);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
async function pickTags(ctx, client, apiId, parsed, opts) {
|
|
80
|
+
if (opts.from && opts.to) {
|
|
81
|
+
const apiName = parsed.kind === "name" ? parsed.api : apiId;
|
|
82
|
+
return { fromTag: opts.from, toTag: opts.to, apiName };
|
|
83
|
+
}
|
|
84
|
+
// Fall back to the registry to pull the version list. Need org slug + api name
|
|
85
|
+
// for the registry path.
|
|
86
|
+
if (parsed.kind !== "name" || !parsed.org) {
|
|
87
|
+
fail(ctx, ExitCode.USAGE, "Pass --from <tag> --to <tag>, or use a ref of the form '<org>/<api>' so we can list versions.");
|
|
88
|
+
}
|
|
89
|
+
const path = `/registry/${encodeURIComponent(parsed.org)}/${encodeURIComponent(parsed.api)}/versions`;
|
|
90
|
+
const versions = (await client.getJson(path));
|
|
91
|
+
if (versions.length < 2) {
|
|
92
|
+
fail(ctx, ExitCode.NOT_FOUND, `Need at least two published versions to diff. ${parsed.api} has ${versions.length}.`);
|
|
93
|
+
}
|
|
94
|
+
// versions are sorted newest-first by the platform.
|
|
95
|
+
const toTag = opts.to ?? versions[0].tag;
|
|
96
|
+
const fromTag = opts.from ?? versions[1].tag;
|
|
97
|
+
if (!toTag || !fromTag) {
|
|
98
|
+
fail(ctx, ExitCode.GENERIC, "Could not determine version tags from the registry.");
|
|
99
|
+
}
|
|
100
|
+
return { fromTag, toTag, apiName: parsed.api };
|
|
101
|
+
}
|
|
102
|
+
function normaliseChanges(res) {
|
|
103
|
+
const list = res.breakingChanges ?? res.changes ?? [];
|
|
104
|
+
return list.map((c) => ({
|
|
105
|
+
id: String(c.id ?? c.text ?? ""),
|
|
106
|
+
level: String(c.level ?? "info"),
|
|
107
|
+
message: String(c.message ?? c.text ?? ""),
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
function renderChangelogText(p) {
|
|
111
|
+
const lines = [];
|
|
112
|
+
const header = `${p.apiName}: ${p.fromTag} → ${p.toTag}`;
|
|
113
|
+
lines.push(chalk.bold(header));
|
|
114
|
+
lines.push(p.hasBreakingChanges
|
|
115
|
+
? chalk.red(` ✗ ${p.changes.length} breaking change${p.changes.length === 1 ? "" : "s"}`)
|
|
116
|
+
: chalk.green(` ✓ no breaking changes`));
|
|
117
|
+
if (p.changes.length) {
|
|
118
|
+
lines.push("");
|
|
119
|
+
for (const c of p.changes) {
|
|
120
|
+
const tag = c.level === "error" ? chalk.red("[breaking]") : chalk.gray(`[${c.level}]`);
|
|
121
|
+
lines.push(` ${tag} ${c.id}${c.message ? `: ${c.message}` : ""}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return lines.join("\n");
|
|
125
|
+
}
|
|
126
|
+
function renderChangelogMarkdown(p) {
|
|
127
|
+
const lines = [];
|
|
128
|
+
lines.push(`## ${p.apiName} — ${p.fromTag} → ${p.toTag}`);
|
|
129
|
+
lines.push("");
|
|
130
|
+
if (p.hasBreakingChanges) {
|
|
131
|
+
lines.push(`**${p.changes.length} breaking change(s).**`);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
lines.push("No breaking changes.");
|
|
135
|
+
}
|
|
136
|
+
if (p.changes.length) {
|
|
137
|
+
lines.push("");
|
|
138
|
+
for (const c of p.changes) {
|
|
139
|
+
lines.push(`- \`${c.id}\` — ${c.message || "(no description)"}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return lines.join("\n");
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=changelog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changelog.js","sourceRoot":"","sources":["../../../src/commands/api/changelog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/api/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,QAKnD"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spec0 api … — API management subcommands.
|
|
3
|
+
*/
|
|
4
|
+
import { registerApiListCommand } from "./list.js";
|
|
5
|
+
import { registerApiShowCommand } from "./show.js";
|
|
6
|
+
import { registerApiChangelogCommand } from "./changelog.js";
|
|
7
|
+
export function registerApiCommands(program) {
|
|
8
|
+
const api = program.command("api").description("Manage APIs in your organisation");
|
|
9
|
+
registerApiListCommand(api);
|
|
10
|
+
registerApiShowCommand(api);
|
|
11
|
+
registerApiChangelogCommand(api);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/api/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,2BAA2B,EAAE,MAAM,gBAAgB,CAAC;AAE7D,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,kCAAkC,CAAC,CAAC;IACnF,sBAAsB,CAAC,GAAG,CAAC,CAAC;IAC5B,sBAAsB,CAAC,GAAG,CAAC,CAAC;IAC5B,2BAA2B,CAAC,GAAG,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spec0 api list — catalogue view of APIs in the org.
|
|
3
|
+
*
|
|
4
|
+
* Backed by GET /apis/summary?organisation-id=<orgId>. Includes counts
|
|
5
|
+
* (operations, environments, subscribers) so a single round trip is
|
|
6
|
+
* enough for a useful overview.
|
|
7
|
+
*/
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
export declare function registerApiListCommand(api: Command): void;
|
|
10
|
+
//# sourceMappingURL=list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/api/list.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0BpC,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,QAgDlD"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spec0 api list — catalogue view of APIs in the org.
|
|
3
|
+
*
|
|
4
|
+
* Backed by GET /apis/summary?organisation-id=<orgId>. Includes counts
|
|
5
|
+
* (operations, environments, subscribers) so a single round trip is
|
|
6
|
+
* enough for a useful overview.
|
|
7
|
+
*/
|
|
8
|
+
import { createOrgApiClient, is401 } from "../../lib/api-client.js";
|
|
9
|
+
import { requireOrgContext } from "../../lib/auth-context.js";
|
|
10
|
+
import { ExitCode } from "../../lib/exit-codes.js";
|
|
11
|
+
import { emit, fail, resolveOutputContext } from "../../lib/output/index.js";
|
|
12
|
+
import { renderTable } from "../../lib/output/table.js";
|
|
13
|
+
export function registerApiListCommand(api) {
|
|
14
|
+
api
|
|
15
|
+
.command("list")
|
|
16
|
+
.description("List APIs in your organisation (catalogue view).")
|
|
17
|
+
.option("--team <team>", "Filter by team name (case-insensitive)")
|
|
18
|
+
.option("--status <status>", "Filter by status (e.g. ACTIVE, DEPRECATED)")
|
|
19
|
+
.option("--search <query>", "Filter by name substring (case-insensitive)")
|
|
20
|
+
.option("--org <uuid>", "Org id override")
|
|
21
|
+
.option("--output <format>", "Output format: text, json, or yaml (default: text)")
|
|
22
|
+
.action(async (opts) => {
|
|
23
|
+
const outCtx = resolveOutputContext(opts);
|
|
24
|
+
let authCtx;
|
|
25
|
+
try {
|
|
26
|
+
authCtx = requireOrgContext(opts.org);
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
fail(outCtx, ExitCode.AUTH_MISSING, e.message, {
|
|
30
|
+
hint: "Set SPEC0_TOKEN + SPEC0_ORG_ID, or run 'spec0 auth login'.",
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const client = createOrgApiClient(authCtx);
|
|
34
|
+
try {
|
|
35
|
+
const params = new URLSearchParams({ "organisation-id": authCtx.orgId });
|
|
36
|
+
const res = (await client.getJson(`/apis/summary?${params.toString()}`));
|
|
37
|
+
const filtered = filterApis(res.apis ?? [], opts);
|
|
38
|
+
emit(outCtx, filtered, renderApiListText);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
if (is401(err)) {
|
|
42
|
+
fail(outCtx, ExitCode.AUTH_MISSING, "Token invalid or expired.", {
|
|
43
|
+
hint: "Run 'spec0 auth login' or refresh SPEC0_TOKEN.",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
fail(outCtx, ExitCode.GENERIC, `api list failed: ${err.message}`);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function filterApis(rows, opts) {
|
|
51
|
+
let out = rows;
|
|
52
|
+
if (opts.team) {
|
|
53
|
+
const t = opts.team.toLowerCase();
|
|
54
|
+
out = out.filter((r) => (r.teamName ?? "").toLowerCase() === t);
|
|
55
|
+
}
|
|
56
|
+
if (opts.status) {
|
|
57
|
+
const s = opts.status.toUpperCase();
|
|
58
|
+
out = out.filter((r) => (r.status ?? "").toUpperCase() === s);
|
|
59
|
+
}
|
|
60
|
+
if (opts.search) {
|
|
61
|
+
const q = opts.search.toLowerCase();
|
|
62
|
+
out = out.filter((r) => (r.apiName ?? "").toLowerCase().includes(q));
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
function renderApiListText(rows) {
|
|
67
|
+
if (!rows.length)
|
|
68
|
+
return "No APIs found. Use `spec0 push` to add one.";
|
|
69
|
+
return renderTable(rows, [
|
|
70
|
+
{ key: "apiName", header: "Name" },
|
|
71
|
+
{ key: "version", header: "Ver" },
|
|
72
|
+
{ key: "status", header: "Status" },
|
|
73
|
+
{ key: "teamName", header: "Team" },
|
|
74
|
+
{ key: "operationCount", header: "Ops" },
|
|
75
|
+
{ key: "environmentCount", header: "Envs" },
|
|
76
|
+
{ key: "subscriberCount", header: "Subs" },
|
|
77
|
+
]);
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/commands/api/list.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,oBAAoB,EAAsB,MAAM,2BAA2B,CAAC;AACjG,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAqBxD,MAAM,UAAU,sBAAsB,CAAC,GAAY;IACjD,GAAG;SACA,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,kDAAkD,CAAC;SAC/D,MAAM,CAAC,eAAe,EAAE,wCAAwC,CAAC;SACjE,MAAM,CAAC,mBAAmB,EAAE,4CAA4C,CAAC;SACzE,MAAM,CAAC,kBAAkB,EAAE,6CAA6C,CAAC;SACzE,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC;SACzC,MAAM,CAAC,mBAAmB,EAAE,oDAAoD,CAAC;SACjF,MAAM,CACL,KAAK,EACH,IAKC,EACD,EAAE;QACF,MAAM,MAAM,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAE1C,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,YAAY,EAAG,CAAW,CAAC,OAAO,EAAE;gBACxD,IAAI,EAAE,4DAA4D;aACnE,CAAC,CAAC;QACL,CAAC;QAED,MAAM,MAAM,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,iBAAiB,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;YACzE,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,OAAO,CAC/B,iBAAiB,MAAM,CAAC,QAAQ,EAAE,EAAE,CACrC,CAA2B,CAAC;YAE7B,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;YAClD,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,YAAY,EAAE,2BAA2B,EAAE;oBAC/D,IAAI,EAAE,gDAAgD;iBACvD,CAAC,CAAC;YACL,CAAC;YACD,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,EAAE,oBAAqB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC,CACF,CAAC;AACN,CAAC;AAED,SAAS,UAAU,CACjB,IAAmB,EACnB,IAAyD;IAEzD,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAClC,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QACpC,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QACpC,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAmB;IAC5C,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,OAAO,6CAA6C,CAAC;IACvE,OAAO,WAAW,CAAC,IAA4C,EAAE;QAC/D,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE;QAClC,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE;QACjC,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE;QACnC,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE;QACnC,EAAE,GAAG,EAAE,gBAAgB,EAAE,MAAM,EAAE,KAAK,EAAE;QACxC,EAAE,GAAG,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,EAAE;QAC3C,EAAE,GAAG,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE;KAC3C,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spec0 api show <ref> — single-API metadata.
|
|
3
|
+
*
|
|
4
|
+
* Backed by GET /apis/{apiId}/summary. Resolves <ref> via ref-resolver:
|
|
5
|
+
* accepts <org>/<api>, bare <api>, or a UUID.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
export declare function registerApiShowCommand(api: Command): void;
|
|
9
|
+
//# sourceMappingURL=show.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"show.d.ts","sourceRoot":"","sources":["../../../src/commands/api/show.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgCpC,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,QAsDlD"}
|