@arkveil/cli 1.0.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 ADDED
@@ -0,0 +1,426 @@
1
+ # arkveil-cli
2
+
3
+ A command-line interface for the **Arkveil Kernel API** — navigation
4
+ trees, actions, targets, policies, tags, access tests, attribute schemas, and
5
+ ABAC (attribute-based access control) operations.
6
+
7
+ The CLI is generated against the API's OpenAPI 3.1 specification, so its request
8
+ and response types are fully derived from the spec.
9
+
10
+ ---
11
+
12
+ ## Table of contents
13
+
14
+ - [Install](#install)
15
+ - [Quick start](#quick-start)
16
+ - [Authentication](#authentication)
17
+ - [Global flags](#global-flags)
18
+ - [Configuration & environment](#configuration--environment)
19
+ - [Exit codes](#exit-codes)
20
+ - [Commands](#commands)
21
+ - [JSON output & scripting](#json-output--scripting)
22
+ - [Development](#development)
23
+
24
+ ---
25
+
26
+ ## Install
27
+
28
+ Requires **Node.js ≥ 20**.
29
+
30
+ ```bash
31
+ # from a published package
32
+ npm install -g arkveil-cli
33
+
34
+ # or run without installing
35
+ npx arkveil-cli --help
36
+ ```
37
+
38
+ From source:
39
+
40
+ ```bash
41
+ pnpm install
42
+ pnpm build
43
+ node bin/cli.js --help
44
+ # or link it onto your PATH:
45
+ npm link
46
+ arkveil --help
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Quick start
52
+
53
+ ```bash
54
+ # 1. Point at your API (default is https://api.arkveil.com)
55
+ export ARKVEIL_BASE_URL="https://kernel.example.com"
56
+
57
+ # 2. Authenticate (opens your browser to approve)
58
+ arkveil auth login
59
+
60
+ # 3. Confirm who you are
61
+ arkveil auth whoami
62
+
63
+ # 4. Use it
64
+ arkveil health
65
+ arkveil trees forest
66
+ arkveil tags list
67
+ arkveil eval explain -a orders:read --user '{"role":"admin"}' --context '{}'
68
+ ```
69
+
70
+ ---
71
+
72
+ ## Authentication
73
+
74
+ The CLI authenticates with the **OAuth 2.0 Device Authorization Grant**
75
+ (RFC 8628), as implemented by the backend's **better-auth Device Authorization**
76
+ plugin.
77
+
78
+ ```bash
79
+ arkveil auth login # request a device code, open the browser, poll for the token
80
+ arkveil auth login --no-browser # print the URL/code instead of opening a browser
81
+ arkveil auth logout # remove stored credentials
82
+ arkveil auth whoami # show current auth state (verifies the token by default)
83
+ arkveil auth whoami --no-verify # skip the verification API call
84
+ ```
85
+
86
+ `login` requests a device code, shows you a verification URL and short user code,
87
+ opens your browser, and then polls until you approve — handling
88
+ `authorization_pending`, `slow_down`, `expired_token`, and `access_denied`.
89
+
90
+ The resulting token is sent as `Authorization: Bearer <token>` on every request.
91
+
92
+ ### Credential storage
93
+
94
+ Tokens are stored in the **OS keychain** when [`keytar`](https://www.npmjs.com/package/keytar)
95
+ is available; otherwise they fall back to `~/.config/arkveil/credentials.json`,
96
+ written with `0600` permissions. `arkveil auth whoami` reports which storage
97
+ backend is in use. Use `arkveil logout` to clear both.
98
+
99
+ You can bypass stored credentials entirely with an explicit token:
100
+
101
+ ```bash
102
+ arkveil --api-key "$TOKEN" tags list
103
+ # or
104
+ ARKVEIL_TOKEN="$TOKEN" arkveil tags list
105
+ ```
106
+
107
+ > **Note on the spec.** The bundled OpenAPI document defines no security scheme or
108
+ > device-flow endpoints. The device-flow endpoints are therefore derived from
109
+ > configuration and default to better-auth conventions
110
+ > (`<auth-base-url>/device/code` and `/device/token`, where `auth-base-url`
111
+ > defaults to `<base-url>/api/auth`). Override them via the environment if your
112
+ > deployment differs (see below). The API also exposes a **Workspace API Keys**
113
+ > resource (`arkveil keys …`) that you can use to mint long-lived keys to pass via
114
+ > `--api-key`.
115
+
116
+ ---
117
+
118
+ ## Global flags
119
+
120
+ These apply to **every** command:
121
+
122
+ | Flag | Description |
123
+ | -------------------- | ------------------------------------------------------------------------- |
124
+ | `--json` | Emit machine-readable JSON to stdout; disables spinners and color. |
125
+ | `-q, --quiet` | Suppress non-essential status messages. |
126
+ | `-v, --verbose` | Print transport diagnostics (method, URL, request id, retries) to stderr. |
127
+ | `--no-color` | Disable ANSI color. |
128
+ | `--base-url <url>` | Override the API base URL. |
129
+ | `--api-key <token>` | Bearer token to use, overriding stored credentials. |
130
+ | `--config-dir <dir>` | Directory for config and credentials. |
131
+ | `--timeout <ms>` | Per-request timeout in milliseconds. |
132
+ | `-V, --version` | Print the CLI version. |
133
+ | `-h, --help` | Show help (available on every command and subcommand). |
134
+
135
+ Color and spinners are also disabled automatically when stdout is **not a TTY**
136
+ (e.g. piped) or when `NO_COLOR` is set.
137
+
138
+ ---
139
+
140
+ ## Configuration & environment
141
+
142
+ Configuration is resolved with the precedence: **CLI flags > environment
143
+ variables > config file > built-in defaults**.
144
+
145
+ ### Config file
146
+
147
+ `~/.config/arkveil/config.json` (or `$XDG_CONFIG_HOME/arkveil/config.json`, or a
148
+ `--config-dir`). Validated at runtime; unknown keys are rejected.
149
+
150
+ ```json
151
+ {
152
+ "baseUrl": "https://kernel.example.com",
153
+ "authBaseUrl": "https://auth.example.com/api/auth",
154
+ "clientId": "arkveil-cli",
155
+ "deviceCodePath": "/device/code",
156
+ "deviceTokenPath": "/device/token",
157
+ "timeoutMs": 30000,
158
+ "retries": 2
159
+ }
160
+ ```
161
+
162
+ ### Environment variables
163
+
164
+ | Variable | Purpose | Default |
165
+ | ----------------------- | ------------------------------------------- | ------------------------- |
166
+ | `ARKVEIL_BASE_URL` | API base URL | `https://api.arkveil.com` |
167
+ | `ARKVEIL_AUTH_BASE_URL` | better-auth mount point | `<base-url>/api/auth` |
168
+ | `ARKVEIL_CLIENT_ID` | Device-flow client id | `arkveil-cli` |
169
+ | `ARKVEIL_SCOPE` | Optional OAuth scope | _(unset)_ |
170
+ | `ARKVEIL_TOKEN` | Bearer token (overrides stored credentials) | _(unset)_ |
171
+ | `ARKVEIL_TIMEOUT` | Request timeout (ms) | `30000` |
172
+ | `ARKVEIL_RETRIES` | Retry attempts for idempotent requests | `2` |
173
+ | `ARKVEIL_CONFIG_DIR` | Config/credentials directory | `~/.config/arkveil` |
174
+ | `NO_COLOR` | Disable color when set | _(unset)_ |
175
+
176
+ ### Networking behavior
177
+
178
+ - **Timeouts**: every request is bounded by `--timeout` (default 30s).
179
+ - **Retries**: idempotent requests (`GET`/`PUT`/`DELETE`) are retried with
180
+ exponential backoff on `429`/`502`/`503`/`504` and network errors, honoring
181
+ `Retry-After`. `POST`/`PATCH` are never retried.
182
+ - **Request IDs**: every request carries an `x-request-id` header, shown under
183
+ `--verbose`.
184
+ - **Pagination**: the API returns full collections (no pagination parameters), so
185
+ list commands return complete results.
186
+
187
+ ---
188
+
189
+ ## Exit codes
190
+
191
+ | Code | Meaning |
192
+ | ----- | ------------------------------------ |
193
+ | `0` | Success |
194
+ | `1` | Generic / unexpected error |
195
+ | `2` | Usage error (bad flags or arguments) |
196
+ | `3` | Authentication required or rejected |
197
+ | `4` | Resource not found (404) |
198
+ | `5` | Network failure or timeout |
199
+ | `6` | API returned an error response |
200
+ | `7` | Invalid local configuration |
201
+ | `130` | Cancelled at an interactive prompt |
202
+
203
+ Errors are printed as a one-line message plus an actionable hint — never a raw
204
+ stack trace. Re-run with `--verbose` for the underlying cause.
205
+
206
+ ---
207
+
208
+ ## Commands
209
+
210
+ Run `arkveil <group> --help` or `arkveil <group> <command> --help` for full,
211
+ auto-generated usage with examples.
212
+
213
+ ### `auth` — authentication
214
+
215
+ ```bash
216
+ arkveil auth login [--no-browser]
217
+ arkveil auth logout
218
+ arkveil auth whoami [--no-verify]
219
+ ```
220
+
221
+ ### `health` — connectivity check
222
+
223
+ ```bash
224
+ arkveil health
225
+ ```
226
+
227
+ ### `keys` — workspace API keys
228
+
229
+ ```bash
230
+ arkveil keys list
231
+ arkveil keys create # secret is shown once
232
+ ```
233
+
234
+ ### `tags`
235
+
236
+ ```bash
237
+ arkveil tags list
238
+ arkveil tags get <id>
239
+ arkveil tags create --slug pii --color '#e11' [--tooltip <t>] [--description <d>]
240
+ arkveil tags update <id> --color '#f00' [--tooltip <t>] [--description <d>]
241
+ arkveil tags delete <id> [--yes]
242
+ ```
243
+
244
+ ### `trees` — navigation trees (read-only)
245
+
246
+ ```bash
247
+ arkveil trees forest
248
+ arkveil trees tests
249
+ arkveil trees data-policies
250
+ arkveil trees actions
251
+ arkveil trees action-policies
252
+ ```
253
+
254
+ ### `folders`
255
+
256
+ ```bash
257
+ arkveil folders create --parent <id> --title <t> [--description <d>]
258
+ arkveil folders update <folderId> --title <t> [--description <d>]
259
+ arkveil folders delete <folderId> [--yes]
260
+ ```
261
+
262
+ ### `actions`
263
+
264
+ ```bash
265
+ arkveil actions create --parent <id> --service <svc> --name <n> --title <t> \
266
+ [--tag <slug> ...] [--description <d>] [--request-schema <json|@file|->]
267
+ arkveil actions update <actionNodeId> --title <t> [--tag <slug> ...] \
268
+ [--description <d>] [--request-schema <json|@file|->]
269
+ arkveil actions delete <actionNodeId> [--yes]
270
+ ```
271
+
272
+ ### `targets`
273
+
274
+ ```bash
275
+ arkveil targets create --parent <id> --type ACTION|DATA --mode INDIVIDUAL|CUSTOM|ALL \
276
+ --title <t> [--action-code <code>] [--dataset-id <id>] [--condition <dsl>] \
277
+ [--request-schema <json|@file|->]
278
+ arkveil targets update <targetNodeId> --title <t> [--condition <dsl>] [--request-schema <...>]
279
+ arkveil targets delete <targetNodeId> [--yes]
280
+ arkveil targets suggest --condition '<dsl>' # suggest a request schema from a condition
281
+ ```
282
+
283
+ ### `policies` (attached to a target)
284
+
285
+ ```bash
286
+ arkveil policies create <targetNodeId> --type PERMISSION|READ|WRITE|INVARIANT|PROJECTION \
287
+ --status ENABLED|DISABLED|DRAFT|DELETED --title <t> \
288
+ [--condition <dsl>] [--filter <dsl>] [--projection <json|@file|->]
289
+ arkveil policies update <targetNodeId> <policyId> --status <s> --title <t> [...]
290
+ arkveil policies delete <targetNodeId> <policyId> [--yes]
291
+ ```
292
+
293
+ ### `tests` — access tests
294
+
295
+ ```bash
296
+ arkveil tests create --parent <id> --name <n> --status DRAFT \
297
+ --selector-type ACTION_SET|FORMULA|ALL_ACTIONS --expected-access GRANTED|DENIED \
298
+ [--action-code <code> ...] [--formula <dsl>] \
299
+ [--user '<json>'] [--context '<json>'] [--tag <slug> ...] \
300
+ [--must-be-granted-by <policyId> ...]
301
+ arkveil tests update <testNodeId> --name <n> --status <s> --selector-type <t> --expected-access <a> [...]
302
+ arkveil tests set-status <testNodeId> --status ENABLED
303
+ arkveil tests delete <testNodeId> [--yes]
304
+ arkveil tests run <testId>
305
+ arkveil tests run-all
306
+ arkveil tests history [testId] # per-test runs, or aggregate when no id given
307
+ arkveil tests run-info <runId> # a single run with per-action results
308
+ ```
309
+
310
+ ### `settings` — user settings
311
+
312
+ ```bash
313
+ arkveil settings get
314
+ arkveil settings set [--theme LIGHT|DARK|SYSTEM] [--ui-mode SIMPLE|STRUCTURED]
315
+ ```
316
+
317
+ ### `schemas` — attribute JSON schemas
318
+
319
+ ```bash
320
+ arkveil schemas get <user|context|action>
321
+ arkveil schemas set <user|context|action> --data @schema.json
322
+ ```
323
+
324
+ ### `formula` — formula DSL
325
+
326
+ ```bash
327
+ arkveil formula parse --context ACTION_PERMISSION --dsl 'user.role == "admin"'
328
+ ```
329
+
330
+ ### `eval` — explain access decisions
331
+
332
+ ```bash
333
+ arkveil eval explain -a orders:read --user '{"role":"admin"}' --context '{}' [--request '<json>']
334
+ ```
335
+
336
+ ### `abac` — ABAC SDK operations
337
+
338
+ ```bash
339
+ arkveil abac check --code orders:read --user '<json>' --context '<json>' [--request '<json>']
340
+ arkveil abac read --dataset-id <id> --user '<json>' --context '<json>' [--alias t]
341
+ arkveil abac write --dataset-id <id> --user '<json>' --context '<json>' [--id <rowId> ...]
342
+ arkveil abac action-data <service> <name>
343
+ ```
344
+
345
+ ### `admin` — workspace administration
346
+
347
+ ```bash
348
+ arkveil admin seed-demo # idempotent; preserves existing entities
349
+ arkveil admin reset-demo [--yes] # DESTRUCTIVE: wipes all authz data, then reseeds
350
+ ```
351
+
352
+ ### JSON payloads (`--data`, `--request-schema`, `--projection`, …)
353
+
354
+ Flags that accept JSON take one of three forms:
355
+
356
+ ```bash
357
+ --data '{"a":1}' # inline JSON
358
+ --data @payload.json # read from a file
359
+ --data - # read from stdin
360
+ echo '{"a":1}' | arkveil schemas set user --data -
361
+ ```
362
+
363
+ ---
364
+
365
+ ## JSON output & scripting
366
+
367
+ Add `--json` to any command to emit the raw API payload (or a small status object
368
+ for actions like `delete`/`login`) as JSON on stdout. In JSON mode, spinners and
369
+ color are disabled and status text is suppressed, so stdout is always valid JSON:
370
+
371
+ ```bash
372
+ arkveil tags list --json | jq '.[].slug'
373
+ arkveil eval explain -a orders:read --user '{"role":"admin"}' --context '{}' --json | jq .granted
374
+ ```
375
+
376
+ Errors in `--json` mode are emitted to **stderr** as a JSON object
377
+ (`{"error":{"message":…,"hint":…,"exitCode":…}}`) while the process exit code
378
+ still reflects the failure category (see [Exit codes](#exit-codes)).
379
+
380
+ Destructive commands (`delete`, `admin reset-demo`) prompt for confirmation when
381
+ interactive and **refuse** to run non-interactively unless `--yes` is passed — so
382
+ piped/CI usage never hangs.
383
+
384
+ ---
385
+
386
+ ## Development
387
+
388
+ ```bash
389
+ pnpm install
390
+ pnpm gen:api # regenerate the typed client from .docs/api.yaml
391
+ pnpm dev -- --help
392
+ pnpm typecheck # tsc --noEmit
393
+ pnpm lint # eslint
394
+ pnpm test # vitest (network is mocked; no live calls)
395
+ pnpm build # tsup -> dist/ (publishable ESM + d.ts)
396
+ ```
397
+
398
+ ### Project layout
399
+
400
+ ```
401
+ bin/cli.js # shebang wrapper -> dist/index.js
402
+ src/index.ts # entry: program wiring + global error handler
403
+ src/commands/<resource>/<action> # one file per command
404
+ src/lib/
405
+ api-client.ts # typed openapi-fetch client + unwrap()
406
+ generated/schema.d.ts # openapi-typescript output (do not edit)
407
+ config.ts # config precedence + validation
408
+ context.ts # per-invocation context (config + output + client)
409
+ auth.ts # device flow + credential store
410
+ http.ts # timeout, retry/backoff, request ids
411
+ output.ts # JSON/human renderers, color/spinner gating
412
+ errors.ts # typed errors + exit codes
413
+ input.ts # JSON payload parsing (--data/@file/-)
414
+ types.ts # friendly aliases for generated schema types
415
+ tests/ # vitest: config, output, api-client, http, auth
416
+ ```
417
+
418
+ ### Publishing
419
+
420
+ ```bash
421
+ pnpm version <patch|minor|major>
422
+ pnpm publish # runs prepublishOnly: typecheck + test + build
423
+ ```
424
+
425
+ The package ships only `dist/`, `bin/`, and `README.md` (see `package.json`
426
+ `files`), with `bin.arkveil` pointing at `bin/cli.js`.
package/bin/cli.js ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ // Thin shebang wrapper. All logic lives in the compiled entry point.
3
+ import "../dist/index.js";
@@ -0,0 +1,6 @@
1
+ import { Command } from 'commander';
2
+
3
+ declare function buildProgram(): Command;
4
+ declare function main(argv?: string[]): Promise<void>;
5
+
6
+ export { buildProgram, main };