@oriva/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/README.md +117 -0
- package/bin/oriva.js +15 -0
- package/dist/auth.d.ts +23 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +49 -0
- package/dist/cli.d.ts +42 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +177 -0
- package/dist/coerce.d.ts +8 -0
- package/dist/coerce.d.ts.map +1 -0
- package/dist/coerce.js +45 -0
- package/dist/help.d.ts +13 -0
- package/dist/help.d.ts.map +1 -0
- package/dist/help.js +121 -0
- package/dist/output.d.ts +29 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +36 -0
- package/dist/parseArgs.d.ts +55 -0
- package/dist/parseArgs.d.ts.map +1 -0
- package/dist/parseArgs.js +150 -0
- package/dist/readBody.d.ts +3 -0
- package/dist/readBody.d.ts.map +1 -0
- package/dist/readBody.js +35 -0
- package/dist/shared/httpExecutor.d.ts +23 -0
- package/dist/shared/httpExecutor.d.ts.map +1 -0
- package/dist/shared/httpExecutor.js +56 -0
- package/dist/shared/loadSpec.d.ts +24 -0
- package/dist/shared/loadSpec.d.ts.map +1 -0
- package/dist/shared/loadSpec.js +33 -0
- package/dist/shared/toolGenerator.d.ts +15 -0
- package/dist/shared/toolGenerator.d.ts.map +1 -0
- package/dist/shared/toolGenerator.js +89 -0
- package/dist/shared/types.d.ts +33 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +1 -0
- package/openapi-snapshot.json +1 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# @oriva/cli
|
|
2
|
+
|
|
3
|
+
Spec-driven CLI for the Oriva public API. Every endpoint in the OpenAPI spec becomes an `oriva <command>` subcommand — zero CLI code changes when new endpoints ship.
|
|
4
|
+
|
|
5
|
+
For shell scripts, Claude Code agents, ops one-offs, and humans who'd rather not write Node TypeScript to hit the API.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm i -g @oriva/cli
|
|
11
|
+
# or
|
|
12
|
+
npx @oriva/cli <command>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Requires Node ≥ 18.
|
|
16
|
+
|
|
17
|
+
## Auth
|
|
18
|
+
|
|
19
|
+
The CLI looks for an API key in this order (highest precedence first):
|
|
20
|
+
|
|
21
|
+
1. `--api-key=<value>` flag
|
|
22
|
+
2. `ORIVA_API_KEY` env var
|
|
23
|
+
3. `~/.config/oriva/config.json` → `profiles[<active>].apiKey`
|
|
24
|
+
|
|
25
|
+
For most uses, export the env var:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
export ORIVA_API_KEY=oriva_pk_live_…
|
|
29
|
+
oriva getCurrentUser
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
For multi-environment work, write `~/.config/oriva/config.json`:
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"activeProfile": "default",
|
|
37
|
+
"profiles": {
|
|
38
|
+
"default": { "apiKey": "oriva_pk_live_…" },
|
|
39
|
+
"staging": {
|
|
40
|
+
"apiKey": "oriva_pk_test_…",
|
|
41
|
+
"baseUrl": "https://staging.api.oriva.io"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Switch with `--profile=staging` or `ORIVA_PROFILE=staging`.
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
oriva --help # List all commands (grouped by OpenAPI tag)
|
|
53
|
+
oriva <command> --help # Per-command help: required + optional params
|
|
54
|
+
oriva --version # CLI + spec version
|
|
55
|
+
|
|
56
|
+
oriva getCurrentUser # GET — pretty JSON
|
|
57
|
+
oriva getCurrentUser --json # Structured envelope { ok, status, data, error, request_id }
|
|
58
|
+
oriva listProfiles --json | jq .
|
|
59
|
+
|
|
60
|
+
# Request bodies — three input modes
|
|
61
|
+
oriva createDeveloperApp --body='{"name":"my app"}'
|
|
62
|
+
oriva createDeveloperApp --body=@app.json
|
|
63
|
+
echo '{"name":"my app"}' | oriva createDeveloperApp --body=-
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Global flags
|
|
67
|
+
|
|
68
|
+
| Flag | Purpose |
|
|
69
|
+
| -------------------- | -------------------------------------------------------------------- |
|
|
70
|
+
| `--api-key=<value>` | One-off override of `ORIVA_API_KEY` |
|
|
71
|
+
| `--profile=<name>` | Use a different config-file profile |
|
|
72
|
+
| `--base-url=<url>` | Override the API base URL |
|
|
73
|
+
| `--spec=<url\|path>` | Use a different OpenAPI spec (default: bundled snapshot) |
|
|
74
|
+
| `--json` | Emit `{ ok, status, data, error, request_id }` envelope (for agents) |
|
|
75
|
+
| `--raw` | Print body unchanged — no JSON pretty-print |
|
|
76
|
+
| `--show-status` | Print `HTTP <code> (request_id=<id>)` to stderr |
|
|
77
|
+
| `--quiet` | Suppress stderr progress lines |
|
|
78
|
+
| `--help`, `-h` | Show this help (or per-command) |
|
|
79
|
+
| `--version`, `-V` | Print CLI + spec version |
|
|
80
|
+
|
|
81
|
+
## Exit codes
|
|
82
|
+
|
|
83
|
+
| Code | Meaning |
|
|
84
|
+
| ---- | --------------------------- |
|
|
85
|
+
| 0 | HTTP 2xx |
|
|
86
|
+
| 1 | HTTP 4xx (client error) |
|
|
87
|
+
| 2 | HTTP 5xx or network failure |
|
|
88
|
+
| 3 | Usage/parse error |
|
|
89
|
+
| 4 | Spec load failure |
|
|
90
|
+
|
|
91
|
+
## Error envelope (with `--json`)
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"ok": false,
|
|
96
|
+
"status": 401,
|
|
97
|
+
"data": null,
|
|
98
|
+
"error": { "code": "unauthenticated", "message": "..." },
|
|
99
|
+
"request_id": "req_ABC"
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Always parseable — same shape regardless of success/failure. Network errors return `status: 0`.
|
|
104
|
+
|
|
105
|
+
## CLI vs SDK
|
|
106
|
+
|
|
107
|
+
| Use case | Tool |
|
|
108
|
+
| ---------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
109
|
+
| Node/TypeScript application code | [`@oriva/sdk`](https://www.npmjs.com/package/@oriva/sdk) — typed client, IDE autocomplete |
|
|
110
|
+
| Shell scripts, CI/CD, ops one-offs | `@oriva/cli` — no Node project needed |
|
|
111
|
+
| Claude Code agents, MCP servers | Either — CLI for general invocation, SDK for typed Node code |
|
|
112
|
+
|
|
113
|
+
The CLI bundles its own OpenAPI snapshot so it works offline with sub-second cold start. The `peerDependency` on `@oriva/sdk` is optional and only declared for version-pinning hints — the CLI doesn't import the SDK at runtime.
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT
|
package/bin/oriva.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `oriva` CLI bin shim.
|
|
4
|
+
*
|
|
5
|
+
* The compiled entry lives in dist/cli.js (ESM). We dynamic-import it so
|
|
6
|
+
* the shim itself stays valid CJS-safe Node script — works whether the
|
|
7
|
+
* installed Node uses ESM or CJS resolution.
|
|
8
|
+
*/
|
|
9
|
+
import('../dist/cli.js')
|
|
10
|
+
.then(({ run }) => run())
|
|
11
|
+
.then((code) => process.exit(code))
|
|
12
|
+
.catch((err) => {
|
|
13
|
+
process.stderr.write(`[oriva] fatal: ${err && err.message ? err.message : err}\n`);
|
|
14
|
+
process.exit(2);
|
|
15
|
+
});
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface ResolvedAuth {
|
|
2
|
+
apiKey?: string;
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
/** Provenance for diagnostics — "flag" | "env" | "config:<profile>" | "none". */
|
|
5
|
+
source: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ConfigProfile {
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ConfigFile {
|
|
12
|
+
activeProfile?: string;
|
|
13
|
+
profiles?: Record<string, ConfigProfile>;
|
|
14
|
+
}
|
|
15
|
+
export interface ResolveAuthOptions {
|
|
16
|
+
flagApiKey?: string;
|
|
17
|
+
envApiKey?: string;
|
|
18
|
+
profile?: string;
|
|
19
|
+
/** Custom config path for tests. Defaults to ~/.config/oriva/config.json. */
|
|
20
|
+
configPath?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function resolveAuth(opts: ResolveAuthOptions): Promise<ResolvedAuth>;
|
|
23
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AA2BA,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAqBjF"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API-key resolution for the `oriva` CLI.
|
|
3
|
+
*
|
|
4
|
+
* Precedence (highest wins):
|
|
5
|
+
* 1. --api-key=<key> (flag)
|
|
6
|
+
* 2. ORIVA_API_KEY env var
|
|
7
|
+
* 3. ~/.config/oriva/config.json
|
|
8
|
+
* - if --profile=<name> set: profiles[<name>].apiKey
|
|
9
|
+
* - else: profiles[activeProfile].apiKey
|
|
10
|
+
* - else: profiles.default.apiKey
|
|
11
|
+
* 4. undefined (caller decides whether to warn)
|
|
12
|
+
*
|
|
13
|
+
* Config-file schema:
|
|
14
|
+
* {
|
|
15
|
+
* "activeProfile": "staging",
|
|
16
|
+
* "profiles": {
|
|
17
|
+
* "default": { "apiKey": "oriva_pk_live_..." },
|
|
18
|
+
* "staging": { "apiKey": "oriva_pk_test_...", "baseUrl": "https://staging.api.oriva.io" }
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* `baseUrl` can also live on a profile and is returned alongside the key.
|
|
23
|
+
*/
|
|
24
|
+
import { readFile } from 'node:fs/promises';
|
|
25
|
+
import { homedir } from 'node:os';
|
|
26
|
+
import { resolve } from 'node:path';
|
|
27
|
+
export async function resolveAuth(opts) {
|
|
28
|
+
if (opts.flagApiKey)
|
|
29
|
+
return { apiKey: opts.flagApiKey, source: 'flag' };
|
|
30
|
+
if (opts.envApiKey)
|
|
31
|
+
return { apiKey: opts.envApiKey, source: 'env' };
|
|
32
|
+
const cfgPath = opts.configPath ?? resolve(homedir(), '.config', 'oriva', 'config.json');
|
|
33
|
+
let cfg;
|
|
34
|
+
try {
|
|
35
|
+
const text = await readFile(cfgPath, 'utf8');
|
|
36
|
+
cfg = JSON.parse(text);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// No config file or unreadable — fall through to "none".
|
|
40
|
+
return { source: 'none' };
|
|
41
|
+
}
|
|
42
|
+
const profiles = cfg.profiles ?? {};
|
|
43
|
+
const want = opts.profile ?? cfg.activeProfile ?? 'default';
|
|
44
|
+
const picked = profiles[want] ?? profiles.default;
|
|
45
|
+
if (picked?.apiKey) {
|
|
46
|
+
return { apiKey: picked.apiKey, baseUrl: picked.baseUrl, source: `config:${want}` };
|
|
47
|
+
}
|
|
48
|
+
return { source: 'none' };
|
|
49
|
+
}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `oriva` CLI entry point.
|
|
3
|
+
*
|
|
4
|
+
* Boot sequence:
|
|
5
|
+
* 1. parseArgs(process.argv.slice(2))
|
|
6
|
+
* 2. resolveAuth (flag → env → config-file)
|
|
7
|
+
* 3. Load OpenAPI spec (--spec / ORIVA_API_SPEC / bundled snapshot)
|
|
8
|
+
* 4. Extract operations
|
|
9
|
+
* 5. Dispatch:
|
|
10
|
+
* - help → top-level help
|
|
11
|
+
* - command-help → per-command help
|
|
12
|
+
* - version → CLI + spec version
|
|
13
|
+
* - command → coerce flags, read body, executeOperation, print result
|
|
14
|
+
*
|
|
15
|
+
* Exit codes:
|
|
16
|
+
* 0 2xx response (or help/version)
|
|
17
|
+
* 1 4xx response
|
|
18
|
+
* 2 5xx response or network failure
|
|
19
|
+
* 3 usage/parse error (bad flags, unknown command, missing required input)
|
|
20
|
+
* 4 spec load failure
|
|
21
|
+
*
|
|
22
|
+
* Ported from ultra-cli (~/ultra-network/packages/ultra-cli/src/cli.ts).
|
|
23
|
+
* Adds: --json envelope output, --profile + --api-key, config-file auth,
|
|
24
|
+
* bundled-snapshot default spec source.
|
|
25
|
+
*/
|
|
26
|
+
import { type MinimalOpenApiDoc } from './shared/loadSpec.js';
|
|
27
|
+
import { renderEnvelope } from './output.js';
|
|
28
|
+
export interface RunDeps {
|
|
29
|
+
argv?: string[];
|
|
30
|
+
env?: NodeJS.ProcessEnv;
|
|
31
|
+
stdin?: NodeJS.ReadableStream;
|
|
32
|
+
stdout?: NodeJS.WritableStream;
|
|
33
|
+
stderr?: NodeJS.WritableStream;
|
|
34
|
+
fetchImpl?: typeof fetch;
|
|
35
|
+
/** Inject a spec for tests; skips loadSpec. */
|
|
36
|
+
specOverride?: MinimalOpenApiDoc;
|
|
37
|
+
/** Override the config-file path for tests. */
|
|
38
|
+
configPath?: string;
|
|
39
|
+
}
|
|
40
|
+
export declare function run(deps?: RunDeps): Promise<number>;
|
|
41
|
+
export { renderEnvelope };
|
|
42
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAiC,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAS7F,OAAO,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAC;AAK3D,MAAM,WAAW,OAAO;IACtB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,+CAA+C;IAC/C,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,GAAG,CAAC,IAAI,GAAE,OAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAuI7D;AA6BD,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `oriva` CLI entry point.
|
|
3
|
+
*
|
|
4
|
+
* Boot sequence:
|
|
5
|
+
* 1. parseArgs(process.argv.slice(2))
|
|
6
|
+
* 2. resolveAuth (flag → env → config-file)
|
|
7
|
+
* 3. Load OpenAPI spec (--spec / ORIVA_API_SPEC / bundled snapshot)
|
|
8
|
+
* 4. Extract operations
|
|
9
|
+
* 5. Dispatch:
|
|
10
|
+
* - help → top-level help
|
|
11
|
+
* - command-help → per-command help
|
|
12
|
+
* - version → CLI + spec version
|
|
13
|
+
* - command → coerce flags, read body, executeOperation, print result
|
|
14
|
+
*
|
|
15
|
+
* Exit codes:
|
|
16
|
+
* 0 2xx response (or help/version)
|
|
17
|
+
* 1 4xx response
|
|
18
|
+
* 2 5xx response or network failure
|
|
19
|
+
* 3 usage/parse error (bad flags, unknown command, missing required input)
|
|
20
|
+
* 4 spec load failure
|
|
21
|
+
*
|
|
22
|
+
* Ported from ultra-cli (~/ultra-network/packages/ultra-cli/src/cli.ts).
|
|
23
|
+
* Adds: --json envelope output, --profile + --api-key, config-file auth,
|
|
24
|
+
* bundled-snapshot default spec source.
|
|
25
|
+
*/
|
|
26
|
+
import { loadSpec, bundledSnapshotPath } from './shared/loadSpec.js';
|
|
27
|
+
import { extractOperations } from './shared/toolGenerator.js';
|
|
28
|
+
import { executeOperation } from './shared/httpExecutor.js';
|
|
29
|
+
import { parseArgs } from './parseArgs.js';
|
|
30
|
+
import { renderTopLevelHelp, renderCommandHelp, renderVersion } from './help.js';
|
|
31
|
+
import { coerceArgs } from './coerce.js';
|
|
32
|
+
import { readBody } from './readBody.js';
|
|
33
|
+
import { resolveAuth } from './auth.js';
|
|
34
|
+
import { renderOutput, renderEnvelope } from './output.js';
|
|
35
|
+
const CLI_VERSION = '0.1.0';
|
|
36
|
+
const DEFAULT_BASE_URL = 'https://api.oriva.io';
|
|
37
|
+
export async function run(deps = {}) {
|
|
38
|
+
const argv = deps.argv ?? process.argv.slice(2);
|
|
39
|
+
const env = deps.env ?? process.env;
|
|
40
|
+
const stdout = deps.stdout ?? process.stdout;
|
|
41
|
+
const stderr = deps.stderr ?? process.stderr;
|
|
42
|
+
const stdin = deps.stdin ?? process.stdin;
|
|
43
|
+
let parsed;
|
|
44
|
+
try {
|
|
45
|
+
parsed = parseArgs(argv);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
stderr.write(`Error: ${err.message}\n`);
|
|
49
|
+
return 3;
|
|
50
|
+
}
|
|
51
|
+
// Spec source precedence: --spec > ORIVA_API_SPEC > bundled snapshot.
|
|
52
|
+
const specSource = parsed.global.spec || (env.ORIVA_API_SPEC || '').trim() || bundledSnapshotPath();
|
|
53
|
+
const tagFilter = (env.ORIVA_API_TAGS || '')
|
|
54
|
+
.split(',')
|
|
55
|
+
.map((t) => t.trim())
|
|
56
|
+
.filter(Boolean);
|
|
57
|
+
let doc;
|
|
58
|
+
try {
|
|
59
|
+
doc = deps.specOverride ?? (await loadSpec(specSource));
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
stderr.write(`Failed to load OpenAPI spec from ${specSource}: ${err.message}\n`);
|
|
63
|
+
return 4;
|
|
64
|
+
}
|
|
65
|
+
const operations = extractOperations(doc, { tagFilter });
|
|
66
|
+
const opsByName = new Map(operations.map((o) => [o.name, o]));
|
|
67
|
+
if (parsed.kind === 'help') {
|
|
68
|
+
stdout.write(renderTopLevelHelp(operations));
|
|
69
|
+
stdout.write('\n');
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
if (parsed.kind === 'version') {
|
|
73
|
+
stdout.write(renderVersion(CLI_VERSION, doc.info?.version));
|
|
74
|
+
stdout.write('\n');
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
if (parsed.kind === 'command-help') {
|
|
78
|
+
const op = opsByName.get(parsed.command);
|
|
79
|
+
if (!op)
|
|
80
|
+
return unknownCommand(parsed.command, operations, stderr);
|
|
81
|
+
stdout.write(renderCommandHelp(op));
|
|
82
|
+
stdout.write('\n');
|
|
83
|
+
return 0;
|
|
84
|
+
}
|
|
85
|
+
// kind === 'command'
|
|
86
|
+
const op = opsByName.get(parsed.command);
|
|
87
|
+
if (!op)
|
|
88
|
+
return unknownCommand(parsed.command, operations, stderr);
|
|
89
|
+
// Resolve auth: flag > env > config-file.
|
|
90
|
+
const auth = await resolveAuth({
|
|
91
|
+
flagApiKey: parsed.global.apiKey,
|
|
92
|
+
envApiKey: (env.ORIVA_API_KEY || '').trim() || undefined,
|
|
93
|
+
profile: parsed.global.profile,
|
|
94
|
+
configPath: deps.configPath,
|
|
95
|
+
});
|
|
96
|
+
if (!auth.apiKey && !parsed.global.quiet) {
|
|
97
|
+
stderr.write('[oriva] WARNING: no API key resolved (flag → ORIVA_API_KEY → ~/.config/oriva/config.json) — call will likely 401.\n');
|
|
98
|
+
}
|
|
99
|
+
let args;
|
|
100
|
+
try {
|
|
101
|
+
args = coerceArgs(parsed.flags, op.inputSchema);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
stderr.write(`Argument error: ${err.message}\n`);
|
|
105
|
+
return 3;
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const body = await readBody(parsed.bodySource, stdin);
|
|
109
|
+
if (body !== undefined)
|
|
110
|
+
args.body = body;
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
stderr.write(`Body error: ${err.message}\n`);
|
|
114
|
+
return 3;
|
|
115
|
+
}
|
|
116
|
+
if (op.hasBody && args.body === undefined && requiresBody(op)) {
|
|
117
|
+
stderr.write(`This command requires --body=<json|@file|->. Run \`oriva ${op.name} --help\`.\n`);
|
|
118
|
+
return 3;
|
|
119
|
+
}
|
|
120
|
+
const baseUrl = parsed.global.baseUrl ||
|
|
121
|
+
(env.ORIVA_API_BASE_URL || '').trim() ||
|
|
122
|
+
auth.baseUrl ||
|
|
123
|
+
doc.servers?.[0]?.url ||
|
|
124
|
+
DEFAULT_BASE_URL;
|
|
125
|
+
let result;
|
|
126
|
+
try {
|
|
127
|
+
result = await executeOperation(op, args, { baseUrl, apiKey: auth.apiKey, userAgent: `oriva-cli/${CLI_VERSION}` }, deps.fetchImpl);
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
if (parsed.global.json) {
|
|
131
|
+
// Emit envelope-shaped error so agents can parse network failures uniformly.
|
|
132
|
+
const envelope = {
|
|
133
|
+
ok: false,
|
|
134
|
+
status: 0,
|
|
135
|
+
data: null,
|
|
136
|
+
error: { message: err.message, kind: 'network' },
|
|
137
|
+
request_id: undefined,
|
|
138
|
+
};
|
|
139
|
+
stdout.write(JSON.stringify(envelope, null, 2));
|
|
140
|
+
stdout.write('\n');
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
stderr.write(`Network error: ${err.message}\n`);
|
|
144
|
+
}
|
|
145
|
+
return 2;
|
|
146
|
+
}
|
|
147
|
+
if (parsed.global.showStatus && !parsed.global.quiet) {
|
|
148
|
+
stderr.write(`HTTP ${result.status}${result.request_id ? ` (request_id=${result.request_id})` : ''}\n`);
|
|
149
|
+
}
|
|
150
|
+
stdout.write(renderOutput(result, { raw: parsed.global.raw, json: parsed.global.json }));
|
|
151
|
+
stdout.write('\n');
|
|
152
|
+
if (result.status >= 500)
|
|
153
|
+
return 2;
|
|
154
|
+
if (result.status >= 400)
|
|
155
|
+
return 1;
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
function unknownCommand(name, operations, stderr) {
|
|
159
|
+
stderr.write(`Unknown command: ${name}\n`);
|
|
160
|
+
const suggestions = operations
|
|
161
|
+
.map((o) => o.name)
|
|
162
|
+
.filter((n) => n
|
|
163
|
+
.toLowerCase()
|
|
164
|
+
.startsWith(name.toLowerCase().slice(0, Math.max(3, Math.floor(name.length / 2)))))
|
|
165
|
+
.slice(0, 5);
|
|
166
|
+
if (suggestions.length) {
|
|
167
|
+
stderr.write(`Did you mean: ${suggestions.join(', ')}?\n`);
|
|
168
|
+
}
|
|
169
|
+
stderr.write('Run `oriva --help` to list commands.\n');
|
|
170
|
+
return 3;
|
|
171
|
+
}
|
|
172
|
+
function requiresBody(op) {
|
|
173
|
+
const required = op.inputSchema.required ?? [];
|
|
174
|
+
return required.includes('body');
|
|
175
|
+
}
|
|
176
|
+
// Re-export for tests + bin shim.
|
|
177
|
+
export { renderEnvelope };
|
package/dist/coerce.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coerce string CLI flags to the JSON types the API expects.
|
|
3
|
+
*
|
|
4
|
+
* Ported verbatim from ultra-cli (~/ultra-network/packages/ultra-cli/src/coerce.ts) —
|
|
5
|
+
* type-coercion semantics are universal.
|
|
6
|
+
*/
|
|
7
|
+
export declare function coerceArgs(rawFlags: Record<string, string | string[] | boolean>, inputSchema: Record<string, unknown>): Record<string, unknown>;
|
|
8
|
+
//# sourceMappingURL=coerce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coerce.d.ts","sourceRoot":"","sources":["../src/coerce.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,EACrD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzB"}
|
package/dist/coerce.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coerce string CLI flags to the JSON types the API expects.
|
|
3
|
+
*
|
|
4
|
+
* Ported verbatim from ultra-cli (~/ultra-network/packages/ultra-cli/src/coerce.ts) —
|
|
5
|
+
* type-coercion semantics are universal.
|
|
6
|
+
*/
|
|
7
|
+
export function coerceArgs(rawFlags, inputSchema) {
|
|
8
|
+
const props = inputSchema.properties ?? {};
|
|
9
|
+
const out = {};
|
|
10
|
+
for (const [k, v] of Object.entries(rawFlags)) {
|
|
11
|
+
const schema = props[k];
|
|
12
|
+
out[k] = coerceOne(v, schema);
|
|
13
|
+
}
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
16
|
+
function coerceOne(v, schema) {
|
|
17
|
+
if (typeof v === 'boolean')
|
|
18
|
+
return v;
|
|
19
|
+
if (Array.isArray(v))
|
|
20
|
+
return v.map((x) => coerceScalar(x, schema?.items ?? schema));
|
|
21
|
+
return coerceScalar(v, schema);
|
|
22
|
+
}
|
|
23
|
+
function coerceScalar(s, schema) {
|
|
24
|
+
const t = schema?.type;
|
|
25
|
+
if (t === 'integer') {
|
|
26
|
+
const n = Number.parseInt(s, 10);
|
|
27
|
+
if (Number.isNaN(n))
|
|
28
|
+
throw new Error(`Expected integer, got: ${s}`);
|
|
29
|
+
return n;
|
|
30
|
+
}
|
|
31
|
+
if (t === 'number') {
|
|
32
|
+
const n = Number(s);
|
|
33
|
+
if (Number.isNaN(n))
|
|
34
|
+
throw new Error(`Expected number, got: ${s}`);
|
|
35
|
+
return n;
|
|
36
|
+
}
|
|
37
|
+
if (t === 'boolean') {
|
|
38
|
+
if (s === 'true' || s === '1')
|
|
39
|
+
return true;
|
|
40
|
+
if (s === 'false' || s === '0')
|
|
41
|
+
return false;
|
|
42
|
+
throw new Error(`Expected boolean, got: ${s}`);
|
|
43
|
+
}
|
|
44
|
+
return s;
|
|
45
|
+
}
|
package/dist/help.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Help text rendering for the `oriva` CLI.
|
|
3
|
+
*
|
|
4
|
+
* Ported from ultra-cli (~/ultra-network/packages/ultra-cli/src/help.ts);
|
|
5
|
+
* key deviation: group commands by OpenAPI `tags[0]` instead of path segment.
|
|
6
|
+
* The Oriva spec has 13 clean tags (Auth/Profiles/Groups/Entries/…) that map
|
|
7
|
+
* cleaner than path roots (which all start with `/api/v1/...`).
|
|
8
|
+
*/
|
|
9
|
+
import type { ExtractedOperation } from './shared/types.js';
|
|
10
|
+
export declare function renderTopLevelHelp(operations: ExtractedOperation[]): string;
|
|
11
|
+
export declare function renderCommandHelp(op: ExtractedOperation): string;
|
|
12
|
+
export declare function renderVersion(cliVersion: string, specVersion: string | undefined): string;
|
|
13
|
+
//# sourceMappingURL=help.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AA8B5D,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,kBAAkB,EAAE,GAAG,MAAM,CA4B3E;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,kBAAkB,GAAG,MAAM,CA2ChE;AAsBD,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAEzF"}
|
package/dist/help.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Help text rendering for the `oriva` CLI.
|
|
3
|
+
*
|
|
4
|
+
* Ported from ultra-cli (~/ultra-network/packages/ultra-cli/src/help.ts);
|
|
5
|
+
* key deviation: group commands by OpenAPI `tags[0]` instead of path segment.
|
|
6
|
+
* The Oriva spec has 13 clean tags (Auth/Profiles/Groups/Entries/…) that map
|
|
7
|
+
* cleaner than path roots (which all start with `/api/v1/...`).
|
|
8
|
+
*/
|
|
9
|
+
const HEADER = `oriva — Oriva public API CLI
|
|
10
|
+
|
|
11
|
+
A spec-driven wrapper over every operation in the @oriva/sdk surface. Every
|
|
12
|
+
endpoint becomes a subcommand. Use \`oriva <command> --help\` for per-command
|
|
13
|
+
flags. Command names match @oriva/sdk function names 1:1.
|
|
14
|
+
|
|
15
|
+
ENVIRONMENT
|
|
16
|
+
ORIVA_API_KEY Bearer key (oriva_pk_live_… / oriva_pk_test_…)
|
|
17
|
+
ORIVA_API_SPEC OpenAPI source (default: bundled snapshot)
|
|
18
|
+
ORIVA_API_BASE_URL Override server base URL (default: https://api.oriva.io)
|
|
19
|
+
ORIVA_API_TAGS CSV tag filter for which operations are exposed
|
|
20
|
+
|
|
21
|
+
CONFIG FILE
|
|
22
|
+
~/.config/oriva/config.json Multi-profile auth — see README
|
|
23
|
+
|
|
24
|
+
GLOBAL FLAGS
|
|
25
|
+
--api-key=<key> Override ORIVA_API_KEY for this invocation
|
|
26
|
+
--profile=<name> Select a config-file profile (default: activeProfile)
|
|
27
|
+
--spec=<url|path> Override the spec source for this invocation
|
|
28
|
+
--base-url=<url> Override the base URL for this invocation
|
|
29
|
+
--show-status Print HTTP status + request_id to stderr
|
|
30
|
+
--raw Print response body unchanged (no JSON pretty-print)
|
|
31
|
+
--json Emit { ok, status, data, error, request_id } envelope
|
|
32
|
+
--quiet Suppress progress lines on stderr
|
|
33
|
+
--help, -h Show this help (or per-command help)
|
|
34
|
+
--version, -V Print CLI + spec version
|
|
35
|
+
`;
|
|
36
|
+
export function renderTopLevelHelp(operations) {
|
|
37
|
+
const out = [HEADER, 'COMMANDS'];
|
|
38
|
+
if (operations.length === 0) {
|
|
39
|
+
out.push(' (no operations available — check ORIVA_API_SPEC)');
|
|
40
|
+
return out.join('\n');
|
|
41
|
+
}
|
|
42
|
+
// Group by first OpenAPI tag; ops without tags go to 'misc'.
|
|
43
|
+
const groups = new Map();
|
|
44
|
+
for (const op of operations) {
|
|
45
|
+
const group = op.tags?.[0] || 'misc';
|
|
46
|
+
if (!groups.has(group))
|
|
47
|
+
groups.set(group, []);
|
|
48
|
+
groups.get(group).push(op);
|
|
49
|
+
}
|
|
50
|
+
const nameWidth = Math.min(40, Math.max(...operations.map((o) => o.name.length)));
|
|
51
|
+
for (const [group, ops] of [...groups.entries()].sort()) {
|
|
52
|
+
out.push('');
|
|
53
|
+
out.push(` ${group}`);
|
|
54
|
+
for (const op of ops.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
55
|
+
const padded = op.name.padEnd(nameWidth);
|
|
56
|
+
out.push(` ${padded} ${op.description}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
out.push('');
|
|
60
|
+
out.push('Run `oriva <command> --help` for input details.');
|
|
61
|
+
return out.join('\n');
|
|
62
|
+
}
|
|
63
|
+
export function renderCommandHelp(op) {
|
|
64
|
+
const out = [];
|
|
65
|
+
out.push(`oriva ${op.name}`);
|
|
66
|
+
out.push('');
|
|
67
|
+
out.push(` ${op.description}`);
|
|
68
|
+
out.push(` ${op.method.toUpperCase()} ${op.pathTemplate}`);
|
|
69
|
+
if (op.tags?.length) {
|
|
70
|
+
out.push(` tags: ${op.tags.join(', ')}`);
|
|
71
|
+
}
|
|
72
|
+
out.push('');
|
|
73
|
+
const schema = op.inputSchema;
|
|
74
|
+
const required = new Set(schema.required ?? []);
|
|
75
|
+
const props = schema.properties ?? {};
|
|
76
|
+
if (op.pathParams.length) {
|
|
77
|
+
out.push('PATH PARAMETERS (required)');
|
|
78
|
+
for (const name of op.pathParams) {
|
|
79
|
+
out.push(formatParam(name, props[name], required.has(name)));
|
|
80
|
+
}
|
|
81
|
+
out.push('');
|
|
82
|
+
}
|
|
83
|
+
if (op.queryParams.length) {
|
|
84
|
+
out.push('QUERY PARAMETERS');
|
|
85
|
+
for (const name of op.queryParams) {
|
|
86
|
+
out.push(formatParam(name, props[name], required.has(name)));
|
|
87
|
+
}
|
|
88
|
+
out.push('');
|
|
89
|
+
}
|
|
90
|
+
if (op.hasBody) {
|
|
91
|
+
out.push('REQUEST BODY');
|
|
92
|
+
out.push(` --body=<json> | --body=@path/to/file.json | --body=- (stdin)`);
|
|
93
|
+
out.push(` Content-Type: ${op.bodyContentType || 'application/json'}`);
|
|
94
|
+
if (required.has('body'))
|
|
95
|
+
out.push(' (required)');
|
|
96
|
+
out.push('');
|
|
97
|
+
}
|
|
98
|
+
out.push('EXAMPLE');
|
|
99
|
+
out.push(formatExample(op));
|
|
100
|
+
return out.join('\n');
|
|
101
|
+
}
|
|
102
|
+
function formatParam(name, schema, isRequired) {
|
|
103
|
+
const tag = isRequired ? ' (required)' : '';
|
|
104
|
+
const type = schema?.type ? ` <${schema.type}>` : '';
|
|
105
|
+
const desc = schema?.description ? ` — ${schema.description}` : '';
|
|
106
|
+
const enumVals = schema?.enum?.length ? ` [${schema.enum.join('|')}]` : '';
|
|
107
|
+
return ` --${name}${type}${tag}${enumVals}${desc}`;
|
|
108
|
+
}
|
|
109
|
+
function formatExample(op) {
|
|
110
|
+
const parts = [' oriva', op.name];
|
|
111
|
+
for (const p of op.pathParams)
|
|
112
|
+
parts.push(`--${p}=<${p}>`);
|
|
113
|
+
for (const q of op.queryParams.slice(0, 2))
|
|
114
|
+
parts.push(`--${q}=<value>`);
|
|
115
|
+
if (op.hasBody)
|
|
116
|
+
parts.push(`--body='{"…":"…"}'`);
|
|
117
|
+
return parts.join(' ');
|
|
118
|
+
}
|
|
119
|
+
export function renderVersion(cliVersion, specVersion) {
|
|
120
|
+
return `oriva ${cliVersion}\nspec ${specVersion ?? '(unknown)'}`;
|
|
121
|
+
}
|
package/dist/output.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output rendering for the `oriva` CLI.
|
|
3
|
+
*
|
|
4
|
+
* Three modes:
|
|
5
|
+
* default: pretty-printed JSON of the response body
|
|
6
|
+
* --raw: response body unchanged (no re-serialization)
|
|
7
|
+
* --json: agent-friendly envelope
|
|
8
|
+
* { ok, status, data, error, request_id, url?, method? }
|
|
9
|
+
*
|
|
10
|
+
* The envelope is always parseable regardless of HTTP status — agents calling
|
|
11
|
+
* the CLI in tight loops want `JSON.parse(stdout).ok` not `if (exit === 0) ...`.
|
|
12
|
+
*/
|
|
13
|
+
import type { ExecuteResult } from './shared/httpExecutor.js';
|
|
14
|
+
export interface OutputOptions {
|
|
15
|
+
raw?: boolean;
|
|
16
|
+
json?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface Envelope {
|
|
19
|
+
ok: boolean;
|
|
20
|
+
status: number;
|
|
21
|
+
data: unknown;
|
|
22
|
+
error: unknown;
|
|
23
|
+
request_id?: string;
|
|
24
|
+
url?: string;
|
|
25
|
+
method?: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function renderEnvelope(result: ExecuteResult): Envelope;
|
|
28
|
+
export declare function renderOutput(result: ExecuteResult, opts: OutputOptions): string;
|
|
29
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,QAAQ,CAyB9D;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,GAAG,MAAM,CAQ/E"}
|