@alter-ai/cli 0.3.0 → 0.3.2
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 +26 -188
- package/dist/cli.js +193 -44
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,208 +1,46 @@
|
|
|
1
|
-
# `@alter-ai/cli`
|
|
1
|
+
# `@alter-ai/cli`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Command-line client for the Alter Vault dev portal. Authenticates with a Personal Access Token (PAT) and exposes the dashboard's resource model as scriptable commands.
|
|
4
|
+
|
|
5
|
+
📖 **Full docs:** [docs.alterauth.com/reference/cli](https://docs.alterauth.com/reference/cli)
|
|
4
6
|
|
|
5
7
|
## Install
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
10
|
npm install -g @alter-ai/cli
|
|
9
|
-
# verify
|
|
10
11
|
alter --version
|
|
11
12
|
```
|
|
12
13
|
|
|
13
|
-
##
|
|
14
|
-
|
|
15
|
-
The fastest interactive sign-in is the browser-dance flow — `alter auth login` opens the dashboard, you click **Authorize**, and the CLI receives the freshly-minted token on a localhost listener:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
alter auth login
|
|
19
|
-
# alter: opening browser at https://portal.alterauth.com/cli-auth
|
|
20
|
-
# (waiting up to 2 minutes for you to approve)…
|
|
21
|
-
# alter: signed in via browser-dance flow.
|
|
22
|
-
|
|
23
|
-
alter auth status
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
For headless / CI environments, mint a PAT manually from the dashboard (**Settings → Personal Access Tokens → New token**) and feed it to the CLI through one of the channels below:
|
|
27
|
-
|
|
28
|
-
**Token file (recommended for local headless use):** keeps the value out of `process.argv` (visible to other users via `ps`) and out of shell history.
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
umask 077 && echo "alter_pat_xxxxxxxxxxxxxxxxxxxxxxxx_yyyyyy" > ~/alter-pat.txt
|
|
32
|
-
alter auth login --token-file ~/alter-pat.txt
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
**Stdin:**
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
pbpaste | alter auth login --token-stdin # macOS
|
|
39
|
-
xclip -o -selection clipboard | alter auth login --token-stdin # Linux
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
**Env var (CI):** skip persistence entirely. The SDK reads `ALTER_PAT` directly; it never lands in argv.
|
|
14
|
+
## Sign in
|
|
43
15
|
|
|
44
16
|
```bash
|
|
45
|
-
|
|
46
|
-
alter apps list
|
|
17
|
+
alter auth login # interactive browser flow
|
|
18
|
+
ALTER_PAT=alter_pat_... alter apps list # headless / CI
|
|
47
19
|
```
|
|
48
20
|
|
|
49
|
-
**Inline `--token <pat>` is supported but discouraged** — the value lands in `process.argv` (visible to other local users via `ps aux`) and shell history. Use only on single-user machines and rotate afterwards.
|
|
50
|
-
|
|
51
21
|
## Commands
|
|
52
22
|
|
|
53
|
-
The CLI mirrors the dashboard's resource model. Every namespace lives at `alter <namespace> <verb>`:
|
|
54
|
-
|
|
55
|
-
```
|
|
56
|
-
auth login | status | logout Sign in / out + token introspection
|
|
57
|
-
apps list | create | show | update | delete Manage applications
|
|
58
|
-
keys list | mint | show | rotate | revoke | rename Manage runtime API keys
|
|
59
|
-
agents list | create | show | update | revoke Managed-agent identities (+ mint-key, list-keys, revoke-key)
|
|
60
|
-
providers list | list-catalog | create | show | update | delete OAuth provider integrations per app
|
|
61
|
-
managed-secrets
|
|
62
|
-
templates | list | show | create | delete | rotate | access | users |
|
|
63
|
-
grants {list, list-for-agent, create, update, revoke} |
|
|
64
|
-
groups {list, show} Managed-secret credentials, grants, and access (CRUD + autocomplete helpers)
|
|
65
|
-
policy show-app View app-level policy (org-wide policy is dashboard-only)
|
|
66
|
-
audit list | show | portal-actions | grant-events | traces Dev-portal audit log
|
|
67
|
-
pats whoami Same as `auth status`, under the `pats` namespace
|
|
68
|
-
link <app-id> | --status Pin an app to the current directory tree
|
|
69
|
-
unlink Clear the workspace pin
|
|
70
|
-
completion install | print Generate shell completions (bash/zsh/fish)
|
|
71
|
-
self-update --to <v> | --dry-run Upgrade the CLI via npm
|
|
72
|
-
sdk-passthrough request <method> <path> [...] Raw authenticated request (escape hatch for routes the CLI doesn't model)
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
PAT lifecycle (mint, revoke) remains **dashboard-only** in v1 — a PAT cannot mint or revoke another PAT, including its own. Operators use the dashboard for those actions. Org-wide policy and identity-provider configuration are also dashboard-only per the destructive-action policy in CLAUDE.md.
|
|
76
|
-
|
|
77
|
-
Every list / show command accepts `--output=json|table|jsonl` (default: `json` for pipelines, `table` for interactive). Object commands default to `json`. Use `--fields a,b,c` at the top level to project to specific keys (see [Field selection](#field-selection---fields)).
|
|
78
|
-
|
|
79
|
-
## Authentication
|
|
80
|
-
|
|
81
|
-
The CLI resolves credentials in this order (highest precedence first):
|
|
82
|
-
|
|
83
|
-
1. **`ALTER_PAT` environment variable** — canonical CI / headless source.
|
|
84
|
-
2. **OS keychain** (macOS Keychain, Linux Secret Service / `gnome-keyring`, Windows Credential Manager) via the optional `keytar` native module. This is the default location after a successful `alter auth login`.
|
|
85
|
-
3. **Plaintext file** `~/.config/alter/auth.toml` (XDG-compliant, mode `0600`). Used as the fallback when `keytar` failed to build on the host (e.g. missing `libsecret-1-dev` on Linux). The CLI prints a warning at login time when it falls back to this path.
|
|
86
|
-
|
|
87
|
-
The `--base-url` flag and the `ALTER_BASE_URL` environment variable both require an `https://` URL — non-HTTPS schemes (`http`, `file`, `gopher`, etc.) are rejected at login time so a misconfigured backend URL cannot exfiltrate the PAT in clear text or to an unintended target.
|
|
88
|
-
|
|
89
|
-
If keytar isn't loading on your host, install the native build tools and re-install:
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
# macOS
|
|
93
|
-
xcode-select --install
|
|
94
|
-
# Linux (Debian/Ubuntu — adjust for your distro)
|
|
95
|
-
sudo apt install libsecret-1-dev gnome-keyring
|
|
96
|
-
# Windows — install windows-build-tools or VS Build Tools
|
|
97
|
-
|
|
98
|
-
npm install -g @alter-ai/cli
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
## Workspace config (`alter link`)
|
|
102
|
-
|
|
103
|
-
If you work primarily on one app, run `alter link <app-id>` once in the project root. The CLI writes a tiny `.alter/config.yaml` that pins the default app for every subsequent `alter keys`, `alter agents`, `alter providers`, and `alter policy show-app` invocation in that directory tree. No more retyping the UUID.
|
|
104
|
-
|
|
105
|
-
```bash
|
|
106
|
-
cd ~/code/my-product
|
|
107
|
-
alter link app_abc123
|
|
108
|
-
# alter: pinned app_id=app_abc123 in /Users/me/code/my-product/.alter/config.yaml
|
|
109
|
-
# alter: appended `.alter/` to .gitignore so the pin isn't committed.
|
|
110
|
-
|
|
111
|
-
# from anywhere in this tree, --app becomes optional:
|
|
112
|
-
alter keys list
|
|
113
|
-
alter agents create --name worker --type service
|
|
114
|
-
alter policy show-app
|
|
115
|
-
|
|
116
|
-
# show the current pin
|
|
117
|
-
alter link --status
|
|
118
|
-
|
|
119
|
-
# clear the pin
|
|
120
|
-
alter unlink
|
|
121
23
|
```
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
```bash
|
|
139
|
-
# List apps, keep only id + name
|
|
140
|
-
alter apps list --fields id,name
|
|
141
|
-
# [
|
|
142
|
-
# { "id": "app_abc", "name": "demo" },
|
|
143
|
-
# ...
|
|
144
|
-
# ]
|
|
145
|
-
|
|
146
|
-
# Single object — same projection rule
|
|
147
|
-
alter apps show app_abc --fields id,name,environment
|
|
148
|
-
|
|
149
|
-
# JSONL — one projected object per line
|
|
150
|
-
alter audit list --fields timestamp,action --output=jsonl
|
|
24
|
+
auth login | status | logout
|
|
25
|
+
apps list | create | show | update | archive | unarchive | delete
|
|
26
|
+
keys list | mint | show | rotate | revoke | rename
|
|
27
|
+
agents list | create | show | update | revoke (+ mint-key, list-keys, revoke-key)
|
|
28
|
+
providers list | list-catalog | create | show | update | delete
|
|
29
|
+
managed-secrets list | show | create | rotate | delete | templates | access |
|
|
30
|
+
set-delegation-policy | users |
|
|
31
|
+
grants {list, list-for-agent, create, update, revoke} |
|
|
32
|
+
groups {list, show}
|
|
33
|
+
policy show-app
|
|
34
|
+
audit list | show | portal-actions | grant-events | traces
|
|
35
|
+
pats whoami
|
|
36
|
+
link / unlink pin an app to the current directory
|
|
37
|
+
completion install | print
|
|
38
|
+
self-update --to <v>
|
|
39
|
+
sdk-passthrough request <grant-id> --url <url>
|
|
151
40
|
```
|
|
152
41
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
## Exit codes
|
|
156
|
-
|
|
157
|
-
`alter` returns a structured exit code so scripts can branch on the failure mode without parsing stderr text. Codes are stable contract — operators can rely on them across releases.
|
|
158
|
-
|
|
159
|
-
| Code | Name | Meaning |
|
|
160
|
-
|------|------------------|-----------------------------------------------------------------------------------------------|
|
|
161
|
-
| 0 | OK | Command succeeded. |
|
|
162
|
-
| 1 | ERROR | Generic runtime failure (uncategorized — including unknown SDK / network errors). |
|
|
163
|
-
| 2 | USAGE | Bad flag, arg, or input format. Always paired with a stderr line naming the offending input. |
|
|
164
|
-
| 3 | AUTH | Not signed in, or PAT revoked / expired. **Remediation:** re-run `alter auth login`. |
|
|
165
|
-
| 4 | NOT_FOUND | Resource not found — 404 from the backend, or a referenced local file is missing. |
|
|
166
|
-
| 5 | CONFLICT | 409 from the backend — most commonly a type-to-confirm mismatch or dependent-resource block. |
|
|
167
|
-
| 6 | RATE_LIMIT | 429 from the backend — retry with backoff. |
|
|
168
|
-
| 7 | FORBIDDEN | 403 from the backend — PAT is valid but lacks the required scope. **Remediation:** re-mint the PAT with broader scopes (or switch PATs); `alter auth login` alone does NOT help. |
|
|
169
|
-
| 8 | CANCELLED | Operator declined an interactive prompt (type-to-confirm mismatch on a destructive action, "no" at a y/N gate). Distinct from `ERROR` (1) — the CLI did nothing wrong, the operator chose not to proceed. Pass `--yes` or `--confirm <name>` in CI to skip the prompt. |
|
|
170
|
-
|
|
171
|
-
Example:
|
|
172
|
-
|
|
173
|
-
```bash
|
|
174
|
-
# Probe whether an app exists without erroring on the not-found case.
|
|
175
|
-
# Capture $? into a local variable BEFORE running anything else; ``$?``
|
|
176
|
-
# is clobbered by every command, so a stray ``log_attempt`` between the
|
|
177
|
-
# ``if`` and ``elif`` would silently break the not-found branch.
|
|
178
|
-
alter apps show "$APP_ID" --output=json > /dev/null 2>&1
|
|
179
|
-
status=$?
|
|
180
|
-
if [ "$status" -eq 0 ]; then
|
|
181
|
-
echo "app exists"
|
|
182
|
-
elif [ "$status" -eq 4 ]; then
|
|
183
|
-
echo "app not found"
|
|
184
|
-
elif [ "$status" -eq 7 ]; then
|
|
185
|
-
echo "PAT lacks dashboard_apps:read — re-mint with broader scopes"
|
|
186
|
-
else
|
|
187
|
-
echo "unexpected error" && exit 1
|
|
188
|
-
fi
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
Backend-thrown errors flow through `withClient` and get mapped from HTTP status to exit code automatically (401 → 3, 403 → 7, 404 → 4, 409 → 5, 429 → 6, everything else → 1). Validation errors raised by the CLI itself (e.g. malformed `--limit`) exit 2.
|
|
192
|
-
|
|
193
|
-
## Scope
|
|
194
|
-
|
|
195
|
-
This CLI ships the full dev-portal command surface — `auth`, `apps`, `keys`, `agents`, `providers`, `managed-secrets`, `policy` (read-only at the app level), `audit`, `pats`, `link` / `unlink`, `completion`, `self-update`, and `sdk-passthrough` as a typed-route escape hatch. Backend routes are PAT-callable via `dashboard_*` scopes (see the [scope catalog](https://docs.alterauth.com/api-reference/scopes) for the full list).
|
|
196
|
-
|
|
197
|
-
**Managed secrets — destructive verb tier:** `alter managed-secrets delete <secret-id>` cascade-revokes every grant and delegation tied to the secret, removes the stored credential from secret storage, and writes cascade audit log entries — irrecoverable. The route is gated by `dashboard_secrets:delete` (NOT bundled into `:write` or `:admin`) AND requires `?confirm=<slug>` matching the target secret's slug. The CLI prompts interactively when stdin is a TTY; CI must pass `--confirm <slug>` explicitly. Mirrors `alter apps delete`. Soft-delete operations on grants (`grants revoke`) use the recoverable `:write` tier.
|
|
198
|
-
|
|
199
|
-
**Managed secrets — credential intake:** `create` and `rotate` accept the credential value through three channels, in decreasing safety order: `--credential-value -` reads one line from stdin (preferred for CI piping), `--credential-value @/path/to/file` reads from a file, and `--credential-value <value>` accepts the value inline with a stderr warning about shell-history leakage. Multi-field templates (those whose backend Pydantic model requires more than a single primary credential string) take `--credentials @file.json` (a JSON object of string fields, takes precedence) or repeated `--credential-field key=value` flags.
|
|
200
|
-
|
|
201
|
-
**Dashboard-only operations** (intentional, not scope gaps):
|
|
42
|
+
All commands accept `--output=json|table|jsonl` and `--fields a,b,c`. See [scripting](https://docs.alterauth.com/reference/cli/scripting) for exit codes and CI patterns, and [authentication](https://docs.alterauth.com/reference/cli/authentication) for token storage details.
|
|
202
43
|
|
|
203
|
-
|
|
204
|
-
- Org-wide key policy (`/organizations/current/key-policy`) — reading the response body is a security-posture fingerprint, so the route refuses PAT auth on both reads and writes.
|
|
205
|
-
- Identity-provider configuration — affects every grant in the org; never settable by a single scripted call.
|
|
206
|
-
- App-level policy *writes* / *deletes* — only `policy show-app` is exposed by the CLI; mutations remain dashboard-only until a CLI use case emerges.
|
|
44
|
+
## License
|
|
207
45
|
|
|
208
|
-
|
|
46
|
+
MIT
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// src/cli.ts
|
|
4
|
-
import { Command as Command14 } from "commander";
|
|
5
|
-
|
|
6
3
|
// src/exit-codes.ts
|
|
7
4
|
var EXIT_ERROR = 1;
|
|
8
5
|
var EXIT_USAGE = 2;
|
|
@@ -176,7 +173,7 @@ async function maybePrintUpdateBanner(currentVersion, argv2 = process.argv.slice
|
|
|
176
173
|
// package.json
|
|
177
174
|
var package_default = {
|
|
178
175
|
name: "@alter-ai/cli",
|
|
179
|
-
version: "0.3.
|
|
176
|
+
version: "0.3.2",
|
|
180
177
|
description: "Command-line interface for the Alter Vault dev portal \u2014 scripted dashboard automation.",
|
|
181
178
|
type: "module",
|
|
182
179
|
bin: {
|
|
@@ -192,7 +189,9 @@ var package_default = {
|
|
|
192
189
|
dev: "tsx src/cli.ts",
|
|
193
190
|
test: "vitest run",
|
|
194
191
|
"test:watch": "vitest",
|
|
195
|
-
|
|
192
|
+
"docs:check": "tsx scripts/check-docs-drift.ts",
|
|
193
|
+
"docs:print": "tsx scripts/check-docs-drift.ts --print",
|
|
194
|
+
typecheck: "tsc --noEmit && tsc -p tsconfig.scripts.json",
|
|
196
195
|
lint: "eslint src/ tests/ --ext .ts",
|
|
197
196
|
format: "prettier --write 'src/**/*.ts' 'tests/**/*.ts'"
|
|
198
197
|
},
|
|
@@ -224,6 +223,9 @@ var package_default = {
|
|
|
224
223
|
}
|
|
225
224
|
};
|
|
226
225
|
|
|
226
|
+
// src/program.ts
|
|
227
|
+
import { Command as Command14 } from "commander";
|
|
228
|
+
|
|
227
229
|
// src/commands/agents.ts
|
|
228
230
|
import { Command } from "commander";
|
|
229
231
|
|
|
@@ -6227,6 +6229,21 @@ var Agent = class _Agent {
|
|
|
6227
6229
|
async connect(options) {
|
|
6228
6230
|
return this.#client.connect(options);
|
|
6229
6231
|
}
|
|
6232
|
+
/**
|
|
6233
|
+
* Poll a Connect session to completion. See
|
|
6234
|
+
* {@link _VaultClient.pollConnectSession} for the full signature.
|
|
6235
|
+
*/
|
|
6236
|
+
async pollConnectSession(...args) {
|
|
6237
|
+
return this.#client.pollConnectSession(...args);
|
|
6238
|
+
}
|
|
6239
|
+
/**
|
|
6240
|
+
* Mint a recovery Connect session from a typed error. See
|
|
6241
|
+
* {@link _VaultClient.createConnectSessionForError} for the full
|
|
6242
|
+
* signature.
|
|
6243
|
+
*/
|
|
6244
|
+
async createConnectSessionForError(...args) {
|
|
6245
|
+
return this.#client.createConnectSessionForError(...args);
|
|
6246
|
+
}
|
|
6230
6247
|
// ── Delegation self-revoke ─────────────────────────────────────────────
|
|
6231
6248
|
/**
|
|
6232
6249
|
* Opt out of this agent's delegation on an OAuth grant.
|
|
@@ -6406,6 +6423,21 @@ var App = class _App {
|
|
|
6406
6423
|
async connect(options) {
|
|
6407
6424
|
return this.#client.connect(options);
|
|
6408
6425
|
}
|
|
6426
|
+
/**
|
|
6427
|
+
* Poll a Connect session to completion. See
|
|
6428
|
+
* {@link _VaultClient.pollConnectSession} for the full signature.
|
|
6429
|
+
*/
|
|
6430
|
+
async pollConnectSession(...args) {
|
|
6431
|
+
return this.#client.pollConnectSession(...args);
|
|
6432
|
+
}
|
|
6433
|
+
/**
|
|
6434
|
+
* Mint a recovery Connect session from a typed error. See
|
|
6435
|
+
* {@link _VaultClient.createConnectSessionForError} for the full
|
|
6436
|
+
* signature.
|
|
6437
|
+
*/
|
|
6438
|
+
async createConnectSessionForError(...args) {
|
|
6439
|
+
return this.#client.createConnectSessionForError(...args);
|
|
6440
|
+
}
|
|
6409
6441
|
// ── Auth (operator-only) ───────────────────────────────────────────────
|
|
6410
6442
|
async authenticate(options) {
|
|
6411
6443
|
return this.#client.authenticate(options);
|
|
@@ -6839,7 +6871,7 @@ var DEFAULT_BASE_URL = "https://backend.alterauth.com";
|
|
|
6839
6871
|
var PAT_API_PREFIX = "/api/v1/dev-portal";
|
|
6840
6872
|
var HTTP_ERROR_THRESHOLD = 400;
|
|
6841
6873
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
6842
|
-
var CLI_VERSION = "0.3.
|
|
6874
|
+
var CLI_VERSION = "0.3.2";
|
|
6843
6875
|
var USER_AGENT = buildUserAgent();
|
|
6844
6876
|
function buildUserAgent() {
|
|
6845
6877
|
let osTag = "";
|
|
@@ -7770,6 +7802,31 @@ var ManagedSecretsNamespace = class {
|
|
|
7770
7802
|
);
|
|
7771
7803
|
return expectDict(body, "managed_secrets.rotate", 200);
|
|
7772
7804
|
}
|
|
7805
|
+
/**
|
|
7806
|
+
* Set the per-secret user → agent delegation policy. Requires
|
|
7807
|
+
* ``dashboard_secrets:write``. Controls whether a group-typed grant on
|
|
7808
|
+
* this secret may be delegated to an agent (``allow_group_source``) and
|
|
7809
|
+
* the per-delegation TTL ceiling (``max_delegation_ttl_days``).
|
|
7810
|
+
*
|
|
7811
|
+
* REPLACE semantics — the backend overwrites the whole policy object,
|
|
7812
|
+
* so the command always sends the full intended state (omitting
|
|
7813
|
+
* ``max_delegation_ttl_days`` lets the backend apply its 90-day default).
|
|
7814
|
+
*/
|
|
7815
|
+
async setDelegationPolicy(appId, secretId, options) {
|
|
7816
|
+
const app = encodePathParam(appId, "appId");
|
|
7817
|
+
const secret = encodePathParam(secretId, "secretId");
|
|
7818
|
+
const payload = filterUndefined({
|
|
7819
|
+
allow_group_source: options.allow_group_source,
|
|
7820
|
+
max_delegation_ttl_days: options.max_delegation_ttl_days
|
|
7821
|
+
});
|
|
7822
|
+
const body = await this.#client._call(
|
|
7823
|
+
"PUT",
|
|
7824
|
+
`/apps/${app}/managed-secrets/${secret}/delegation-policy`,
|
|
7825
|
+
"managed_secrets.set_delegation_policy",
|
|
7826
|
+
{ jsonBody: payload }
|
|
7827
|
+
);
|
|
7828
|
+
return expectDict(body, "managed_secrets.set_delegation_policy", 200);
|
|
7829
|
+
}
|
|
7773
7830
|
/**
|
|
7774
7831
|
* List grants on a managed secret. Requires ``dashboard_secrets:read``.
|
|
7775
7832
|
*
|
|
@@ -10686,7 +10743,7 @@ function buildKeysCommand() {
|
|
|
10686
10743
|
}
|
|
10687
10744
|
);
|
|
10688
10745
|
keys.command("rotate").description(
|
|
10689
|
-
"Rotate a key (new plaintext returned ONCE; old key enters grace until revoke). Requires dashboard_keys:
|
|
10746
|
+
"Rotate a key (new plaintext returned ONCE; old key enters grace until revoke). Requires dashboard_keys:write scope."
|
|
10690
10747
|
).option("--app <app-id>", "App ID. Falls back to ALTER_APP_ID env or .alter/config.yaml").requiredOption("--key <key-id>", "Key ID", parseUuidArgument("--key")).option(
|
|
10691
10748
|
"--scopes <list>",
|
|
10692
10749
|
"Optional new scope set (defaults to the key's current scopes)"
|
|
@@ -11695,7 +11752,14 @@ function buildManagedSecretsCommand() {
|
|
|
11695
11752
|
).option(
|
|
11696
11753
|
"--injection-rule <@file.json>",
|
|
11697
11754
|
"Path-prefixed JSON file containing the additional_injections array"
|
|
11698
|
-
).option("--label <label>", "Base-grant display label").option("--account-identifier <id>", "Optional account identifier metadata").option("--account-display-name <name>", "Optional account display name metadata").option(
|
|
11755
|
+
).option("--label <label>", "Base-grant display label").option("--account-identifier <id>", "Optional account identifier metadata").option("--account-display-name <name>", "Optional account display name metadata").option(
|
|
11756
|
+
"--allow-group-delegation",
|
|
11757
|
+
"Allow members of a group-typed grant on this secret to delegate it to an agent (default: off)"
|
|
11758
|
+
).option(
|
|
11759
|
+
"--max-delegation-ttl-days <days>",
|
|
11760
|
+
"Cap on a single delegation's lifetime, in days (1..1825, default 90)",
|
|
11761
|
+
parseBoundedInt("--max-delegation-ttl-days", 1, 5 * 365)
|
|
11762
|
+
).option("--output <format>", "Output format: json|table (default: json)", "json").action(
|
|
11699
11763
|
async (options) => {
|
|
11700
11764
|
const format = coerceOutputFormat(options.output);
|
|
11701
11765
|
const appId = resolveAppIdOrExit(options.app);
|
|
@@ -11718,7 +11782,9 @@ function buildManagedSecretsCommand() {
|
|
|
11718
11782
|
"injectionRule",
|
|
11719
11783
|
"label",
|
|
11720
11784
|
"accountIdentifier",
|
|
11721
|
-
"accountDisplayName"
|
|
11785
|
+
"accountDisplayName",
|
|
11786
|
+
"allowGroupDelegation",
|
|
11787
|
+
"maxDelegationTtlDays"
|
|
11722
11788
|
].filter((k) => {
|
|
11723
11789
|
if (k === "credentialType") return options.credentialType !== "bearer_token";
|
|
11724
11790
|
return options[k] !== void 0;
|
|
@@ -11804,6 +11870,16 @@ function buildManagedSecretsCommand() {
|
|
|
11804
11870
|
if (options.accountDisplayName !== void 0) {
|
|
11805
11871
|
body.account_display_name = options.accountDisplayName;
|
|
11806
11872
|
}
|
|
11873
|
+
if (options.allowGroupDelegation !== void 0 || options.maxDelegationTtlDays !== void 0) {
|
|
11874
|
+
const delegationPolicy = {};
|
|
11875
|
+
if (options.allowGroupDelegation !== void 0) {
|
|
11876
|
+
delegationPolicy.allow_group_source = options.allowGroupDelegation;
|
|
11877
|
+
}
|
|
11878
|
+
if (options.maxDelegationTtlDays !== void 0) {
|
|
11879
|
+
delegationPolicy.max_delegation_ttl_days = options.maxDelegationTtlDays;
|
|
11880
|
+
}
|
|
11881
|
+
body.delegation_policy = delegationPolicy;
|
|
11882
|
+
}
|
|
11807
11883
|
await withClient(async (client) => {
|
|
11808
11884
|
const row = await client.managedSecrets.create(
|
|
11809
11885
|
resolveAppIdOrExit(options.app),
|
|
@@ -11813,6 +11889,29 @@ function buildManagedSecretsCommand() {
|
|
|
11813
11889
|
});
|
|
11814
11890
|
}
|
|
11815
11891
|
);
|
|
11892
|
+
root.command("set-delegation-policy <secret-id>").description(
|
|
11893
|
+
"Set the user \u2192 agent delegation policy on a managed secret. REPLACES the current policy (omitting --allow-group-delegation turns group delegation OFF). Requires dashboard_secrets:write."
|
|
11894
|
+
).option("--app <app-id>", "App ID. Falls back to ALTER_APP_ID env or .alter/config.yaml").option(
|
|
11895
|
+
"--allow-group-delegation",
|
|
11896
|
+
"Allow members of a group-typed grant on this secret to delegate it to an agent (default: off)"
|
|
11897
|
+
).option(
|
|
11898
|
+
"--max-delegation-ttl-days <days>",
|
|
11899
|
+
"Cap on a single delegation's lifetime, in days (1..1825; omit for the 90-day default)",
|
|
11900
|
+
parseBoundedInt("--max-delegation-ttl-days", 1, 5 * 365)
|
|
11901
|
+
).option("--output <format>", "Output format: json|table (default: json)", "json").action(
|
|
11902
|
+
async (rawSecretId, options) => {
|
|
11903
|
+
const format = coerceOutputFormat(options.output);
|
|
11904
|
+
const appId = resolveAppIdOrExit(options.app);
|
|
11905
|
+
const secretId = parseUuidArgument("<secret-id>")(rawSecretId);
|
|
11906
|
+
await withClient(async (client) => {
|
|
11907
|
+
const row = await client.managedSecrets.setDelegationPolicy(appId, secretId, {
|
|
11908
|
+
allow_group_source: options.allowGroupDelegation === true,
|
|
11909
|
+
max_delegation_ttl_days: options.maxDelegationTtlDays
|
|
11910
|
+
});
|
|
11911
|
+
emit(format, row);
|
|
11912
|
+
});
|
|
11913
|
+
}
|
|
11914
|
+
);
|
|
11816
11915
|
root.command("delete <secret-id>").description(
|
|
11817
11916
|
"Cascade-delete a managed secret. Requires dashboard_secrets:delete scope (NOT bundled into :write). Cascade-revokes grants + delegations + audit anchors and vault-deletes the credential."
|
|
11818
11917
|
).option("--app <app-id>", "App ID").option(
|
|
@@ -12092,6 +12191,46 @@ function collectRedirectUris(value, previous = []) {
|
|
|
12092
12191
|
const next = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
12093
12192
|
return [...previous, ...next];
|
|
12094
12193
|
}
|
|
12194
|
+
function sanitizeStderrText(value) {
|
|
12195
|
+
return value.replace(/[\u0000-\u001f\u007f-\u009f]+/g, " ").trim();
|
|
12196
|
+
}
|
|
12197
|
+
function surfaceProviderResponse(row) {
|
|
12198
|
+
if (typeof row !== "object" || row === null) return;
|
|
12199
|
+
const r = row;
|
|
12200
|
+
const preflight = r.preflight;
|
|
12201
|
+
if (preflight && typeof preflight === "object") {
|
|
12202
|
+
const status = preflight.status;
|
|
12203
|
+
const msg = typeof preflight.message === "string" ? sanitizeStderrText(preflight.message) : "";
|
|
12204
|
+
if (status === "passed") {
|
|
12205
|
+
process.stderr.write("alter: preflight: passed\n");
|
|
12206
|
+
} else if (status === "failed") {
|
|
12207
|
+
process.stderr.write(
|
|
12208
|
+
`alter: preflight: failed${msg ? ` \u2014 ${msg}` : ""}
|
|
12209
|
+
`
|
|
12210
|
+
);
|
|
12211
|
+
} else if (status === "inconclusive") {
|
|
12212
|
+
process.stderr.write(
|
|
12213
|
+
`alter: preflight: inconclusive${msg ? ` \u2014 ${msg}` : ""} (save was allowed)
|
|
12214
|
+
`
|
|
12215
|
+
);
|
|
12216
|
+
}
|
|
12217
|
+
}
|
|
12218
|
+
const bc = r.breaking_changes;
|
|
12219
|
+
if (Array.isArray(bc) && bc.length > 0) {
|
|
12220
|
+
process.stderr.write(
|
|
12221
|
+
`alter: warning: ${bc.length} breaking change(s) \u2014 ${sanitizeStderrText(JSON.stringify(bc))}
|
|
12222
|
+
`
|
|
12223
|
+
);
|
|
12224
|
+
}
|
|
12225
|
+
const warning = r.warning;
|
|
12226
|
+
if (typeof warning === "string") {
|
|
12227
|
+
const sanitized = sanitizeStderrText(warning);
|
|
12228
|
+
if (sanitized.length > 0) {
|
|
12229
|
+
process.stderr.write(`alter: warning: ${sanitized}
|
|
12230
|
+
`);
|
|
12231
|
+
}
|
|
12232
|
+
}
|
|
12233
|
+
}
|
|
12095
12234
|
function buildProvidersCommand() {
|
|
12096
12235
|
const providers = new Command11("providers").description(
|
|
12097
12236
|
"Manage OAuth provider configs"
|
|
@@ -12182,6 +12321,7 @@ function buildProvidersCommand() {
|
|
|
12182
12321
|
appId,
|
|
12183
12322
|
body
|
|
12184
12323
|
);
|
|
12324
|
+
surfaceProviderResponse(row);
|
|
12185
12325
|
emit(format, row);
|
|
12186
12326
|
});
|
|
12187
12327
|
return;
|
|
@@ -12221,6 +12361,7 @@ function buildProvidersCommand() {
|
|
|
12221
12361
|
scopes,
|
|
12222
12362
|
redirect_uris: redirects
|
|
12223
12363
|
});
|
|
12364
|
+
surfaceProviderResponse(row);
|
|
12224
12365
|
emit(format, row);
|
|
12225
12366
|
});
|
|
12226
12367
|
}
|
|
@@ -12267,6 +12408,7 @@ function buildProvidersCommand() {
|
|
|
12267
12408
|
options.provider,
|
|
12268
12409
|
body
|
|
12269
12410
|
);
|
|
12411
|
+
surfaceProviderResponse(row);
|
|
12270
12412
|
emit(format, row);
|
|
12271
12413
|
});
|
|
12272
12414
|
return;
|
|
@@ -12293,6 +12435,7 @@ function buildProvidersCommand() {
|
|
|
12293
12435
|
redirect_uris: redirects,
|
|
12294
12436
|
status: providerStatus
|
|
12295
12437
|
});
|
|
12438
|
+
surfaceProviderResponse(row);
|
|
12296
12439
|
emit(format, row);
|
|
12297
12440
|
});
|
|
12298
12441
|
}
|
|
@@ -12565,6 +12708,46 @@ function buildSelfUpdateCommand() {
|
|
|
12565
12708
|
});
|
|
12566
12709
|
}
|
|
12567
12710
|
|
|
12711
|
+
// src/program.ts
|
|
12712
|
+
function buildProgram() {
|
|
12713
|
+
const program2 = new Command14();
|
|
12714
|
+
program2.name("alter").description("Alter Vault command-line interface").version(package_default.version);
|
|
12715
|
+
program2.option(
|
|
12716
|
+
"--fields <list>",
|
|
12717
|
+
"Comma-separated top-level keys to keep in JSON output (e.g. ``--fields id,name``). Inert with --output=table."
|
|
12718
|
+
);
|
|
12719
|
+
program2.hook("preAction", (thisCommand) => {
|
|
12720
|
+
setGlobalFields(void 0);
|
|
12721
|
+
const raw = thisCommand.opts().fields;
|
|
12722
|
+
if (raw !== void 0) {
|
|
12723
|
+
try {
|
|
12724
|
+
setGlobalFields(parseFieldsList(raw));
|
|
12725
|
+
} catch (e) {
|
|
12726
|
+
process.stderr.write(
|
|
12727
|
+
`alter: ${e instanceof Error ? e.message : String(e)}
|
|
12728
|
+
`
|
|
12729
|
+
);
|
|
12730
|
+
process.exit(EXIT_USAGE);
|
|
12731
|
+
}
|
|
12732
|
+
}
|
|
12733
|
+
});
|
|
12734
|
+
program2.addCommand(buildAuthCommand());
|
|
12735
|
+
program2.addCommand(buildAppsCommand());
|
|
12736
|
+
program2.addCommand(buildKeysCommand());
|
|
12737
|
+
program2.addCommand(buildAgentsCommand());
|
|
12738
|
+
program2.addCommand(buildProvidersCommand());
|
|
12739
|
+
program2.addCommand(buildManagedSecretsCommand());
|
|
12740
|
+
program2.addCommand(buildPolicyCommand());
|
|
12741
|
+
program2.addCommand(buildAuditCommand());
|
|
12742
|
+
program2.addCommand(buildPatsCommand());
|
|
12743
|
+
program2.addCommand(buildLinkCommand());
|
|
12744
|
+
program2.addCommand(buildUnlinkCommand());
|
|
12745
|
+
program2.addCommand(buildCompletionCommand());
|
|
12746
|
+
program2.addCommand(buildSdkPassthroughCommand());
|
|
12747
|
+
program2.addCommand(buildSelfUpdateCommand());
|
|
12748
|
+
return program2;
|
|
12749
|
+
}
|
|
12750
|
+
|
|
12568
12751
|
// src/cli.ts
|
|
12569
12752
|
var COMMANDER_USAGE_CODES = /* @__PURE__ */ new Set([
|
|
12570
12753
|
"commander.missingArgument",
|
|
@@ -12628,41 +12811,7 @@ function rewriteLegacySelfUpdateVersionFlag(argv2) {
|
|
|
12628
12811
|
}
|
|
12629
12812
|
return rewritten;
|
|
12630
12813
|
}
|
|
12631
|
-
var program =
|
|
12632
|
-
program.name("alter").description("Alter Vault command-line interface").version(package_default.version);
|
|
12633
|
-
program.option(
|
|
12634
|
-
"--fields <list>",
|
|
12635
|
-
"Comma-separated top-level keys to keep in JSON output (e.g. ``--fields id,name``). Inert with --output=table."
|
|
12636
|
-
);
|
|
12637
|
-
program.hook("preAction", (thisCommand) => {
|
|
12638
|
-
setGlobalFields(void 0);
|
|
12639
|
-
const raw = thisCommand.opts().fields;
|
|
12640
|
-
if (raw !== void 0) {
|
|
12641
|
-
try {
|
|
12642
|
-
setGlobalFields(parseFieldsList(raw));
|
|
12643
|
-
} catch (e) {
|
|
12644
|
-
process.stderr.write(
|
|
12645
|
-
`alter: ${e instanceof Error ? e.message : String(e)}
|
|
12646
|
-
`
|
|
12647
|
-
);
|
|
12648
|
-
process.exit(EXIT_USAGE);
|
|
12649
|
-
}
|
|
12650
|
-
}
|
|
12651
|
-
});
|
|
12652
|
-
program.addCommand(buildAuthCommand());
|
|
12653
|
-
program.addCommand(buildAppsCommand());
|
|
12654
|
-
program.addCommand(buildKeysCommand());
|
|
12655
|
-
program.addCommand(buildAgentsCommand());
|
|
12656
|
-
program.addCommand(buildProvidersCommand());
|
|
12657
|
-
program.addCommand(buildManagedSecretsCommand());
|
|
12658
|
-
program.addCommand(buildPolicyCommand());
|
|
12659
|
-
program.addCommand(buildAuditCommand());
|
|
12660
|
-
program.addCommand(buildPatsCommand());
|
|
12661
|
-
program.addCommand(buildLinkCommand());
|
|
12662
|
-
program.addCommand(buildUnlinkCommand());
|
|
12663
|
-
program.addCommand(buildCompletionCommand());
|
|
12664
|
-
program.addCommand(buildSdkPassthroughCommand());
|
|
12665
|
-
program.addCommand(buildSelfUpdateCommand());
|
|
12814
|
+
var program = buildProgram();
|
|
12666
12815
|
applyExitOverride(program);
|
|
12667
12816
|
var argv = rewriteLegacySelfUpdateVersionFlag(process.argv);
|
|
12668
12817
|
async function main() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alter-ai/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Command-line interface for the Alter Vault dev portal — scripted dashboard automation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
"dev": "tsx src/cli.ts",
|
|
17
17
|
"test": "vitest run",
|
|
18
18
|
"test:watch": "vitest",
|
|
19
|
-
"
|
|
19
|
+
"docs:check": "tsx scripts/check-docs-drift.ts",
|
|
20
|
+
"docs:print": "tsx scripts/check-docs-drift.ts --print",
|
|
21
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.scripts.json",
|
|
20
22
|
"lint": "eslint src/ tests/ --ext .ts",
|
|
21
23
|
"format": "prettier --write 'src/**/*.ts' 'tests/**/*.ts'"
|
|
22
24
|
},
|