@quantbrasil/cli 0.1.0-beta.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 +272 -0
- package/bin/quantbrasil.js +4 -0
- package/dist/cli/auth.d.ts +20 -0
- package/dist/cli/auth.d.ts.map +1 -0
- package/dist/cli/auth.js +76 -0
- package/dist/cli/client.d.ts +17 -0
- package/dist/cli/client.d.ts.map +1 -0
- package/dist/cli/client.js +112 -0
- package/dist/cli/config.d.ts +33 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +148 -0
- package/dist/cli/errors.d.ts +40 -0
- package/dist/cli/errors.d.ts.map +1 -0
- package/dist/cli/errors.js +122 -0
- package/dist/cli/index.d.ts +30 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +112 -0
- package/dist/cli/prompt.d.ts +2 -0
- package/dist/cli/prompt.d.ts.map +1 -0
- package/dist/cli/prompt.js +40 -0
- package/dist/cli/skills.d.ts +26 -0
- package/dist/cli/skills.d.ts.map +1 -0
- package/dist/cli/skills.js +94 -0
- package/dist/cli/terminal.d.ts +18 -0
- package/dist/cli/terminal.d.ts.map +1 -0
- package/dist/cli/terminal.js +43 -0
- package/dist/commands/analytics.d.ts +131 -0
- package/dist/commands/analytics.d.ts.map +1 -0
- package/dist/commands/analytics.js +291 -0
- package/dist/commands/assets.d.ts +69 -0
- package/dist/commands/assets.d.ts.map +1 -0
- package/dist/commands/assets.js +350 -0
- package/dist/commands/auth.d.ts +17 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +48 -0
- package/dist/commands/capabilities.d.ts +25 -0
- package/dist/commands/capabilities.d.ts.map +1 -0
- package/dist/commands/capabilities.js +61 -0
- package/dist/commands/init.d.ts +17 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +89 -0
- package/dist/commands/market.d.ts +45 -0
- package/dist/commands/market.d.ts.map +1 -0
- package/dist/commands/market.js +162 -0
- package/dist/commands/portfolios.d.ts +60 -0
- package/dist/commands/portfolios.d.ts.map +1 -0
- package/dist/commands/portfolios.js +298 -0
- package/dist/commands/skills.d.ts +17 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +38 -0
- package/dist/commands/status.d.ts +15 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +52 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/vendor/core/capabilities/analytics.d.ts +187 -0
- package/dist/vendor/core/capabilities/analytics.d.ts.map +1 -0
- package/dist/vendor/core/capabilities/analytics.js +214 -0
- package/dist/vendor/core/capabilities/assets.d.ts +48 -0
- package/dist/vendor/core/capabilities/assets.d.ts.map +1 -0
- package/dist/vendor/core/capabilities/assets.js +66 -0
- package/dist/vendor/core/capabilities/index.d.ts +8 -0
- package/dist/vendor/core/capabilities/index.d.ts.map +1 -0
- package/dist/vendor/core/capabilities/index.js +7 -0
- package/dist/vendor/core/capabilities/market.d.ts +90 -0
- package/dist/vendor/core/capabilities/market.d.ts.map +1 -0
- package/dist/vendor/core/capabilities/market.js +114 -0
- package/dist/vendor/core/capabilities/portfolios.d.ts +224 -0
- package/dist/vendor/core/capabilities/portfolios.d.ts.map +1 -0
- package/dist/vendor/core/capabilities/portfolios.js +244 -0
- package/dist/vendor/core/capabilities/registry.d.ts +1083 -0
- package/dist/vendor/core/capabilities/registry.d.ts.map +1 -0
- package/dist/vendor/core/capabilities/registry.js +14 -0
- package/dist/vendor/core/capabilities/shared.d.ts +3 -0
- package/dist/vendor/core/capabilities/shared.d.ts.map +1 -0
- package/dist/vendor/core/capabilities/shared.js +2 -0
- package/dist/vendor/core/capabilities/types.d.ts +75 -0
- package/dist/vendor/core/capabilities/types.d.ts.map +1 -0
- package/dist/vendor/core/capabilities/types.js +1 -0
- package/dist/vendor/core/errors.d.ts +22 -0
- package/dist/vendor/core/errors.d.ts.map +1 -0
- package/dist/vendor/core/errors.js +42 -0
- package/dist/vendor/core/http/client.d.ts +45 -0
- package/dist/vendor/core/http/client.d.ts.map +1 -0
- package/dist/vendor/core/http/client.js +170 -0
- package/dist/vendor/core/http/index.d.ts +2 -0
- package/dist/vendor/core/http/index.d.ts.map +1 -0
- package/dist/vendor/core/http/index.js +1 -0
- package/dist/vendor/core/index.d.ts +5 -0
- package/dist/vendor/core/index.d.ts.map +1 -0
- package/dist/vendor/core/index.js +4 -0
- package/dist/vendor/core/invoke.d.ts +17 -0
- package/dist/vendor/core/invoke.d.ts.map +1 -0
- package/dist/vendor/core/invoke.js +70 -0
- package/package.json +57 -0
- package/skills/quantbrasil/SKILL.md +29 -0
- package/skills/quantbrasil/references/cli.md +77 -0
- package/skills/quantbrasil/references/costs.md +30 -0
- package/skills/quantbrasil/references/errors.md +128 -0
- package/skills/quantbrasil/references/unsupported.md +20 -0
- package/skills/quantbrasil/references/workflows.md +99 -0
package/README.md
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# QuantBrasil CLI
|
|
2
|
+
|
|
3
|
+
Public QuantBrasil CLI for deterministic operations.
|
|
4
|
+
|
|
5
|
+
## Running locally
|
|
6
|
+
|
|
7
|
+
The local package binary lives at `bin/quantbrasil.js`.
|
|
8
|
+
|
|
9
|
+
### From the package directory
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# from monorepo/packages/cli
|
|
13
|
+
pnpm build
|
|
14
|
+
node ./bin/quantbrasil.js --status
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### From the repository root
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm --dir monorepo/packages/cli build
|
|
21
|
+
pnpm --dir monorepo/packages/cli exec node ./bin/quantbrasil.js --status
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`node ./bin/quantbrasil.js ...` does not work from the repository root because
|
|
25
|
+
that file only exists inside this package.
|
|
26
|
+
|
|
27
|
+
## Local authentication flow
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# from monorepo/packages/cli
|
|
31
|
+
node ./bin/quantbrasil.js auth login --api-key qb_live_<id>.<secret>
|
|
32
|
+
node ./bin/quantbrasil.js skills install --all
|
|
33
|
+
node ./bin/quantbrasil.js --status
|
|
34
|
+
node ./bin/quantbrasil.js auth logout
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The `auth login` command validates two things before saving:
|
|
38
|
+
|
|
39
|
+
1. the key format locally (`qb_live_<id>.<secret>`)
|
|
40
|
+
2. the key against the target backend with an authenticated read request
|
|
41
|
+
|
|
42
|
+
If the target backend does not expose `/api/desk/api-keys/me` yet, login fails with
|
|
43
|
+
a remote validation error. During development, point `QB_BACKEND_URL` to a
|
|
44
|
+
compatible local or staging backend.
|
|
45
|
+
|
|
46
|
+
## First read command
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
quantbrasil market assets
|
|
50
|
+
quantbrasil market assets --type B3
|
|
51
|
+
quantbrasil market assets --json
|
|
52
|
+
quantbrasil market price PETR4
|
|
53
|
+
quantbrasil market price PETR4 --date 2026-04-10
|
|
54
|
+
quantbrasil market price PETR4 --json
|
|
55
|
+
quantbrasil assets overview PETR4 --sections price,performance
|
|
56
|
+
quantbrasil assets overview PETR4 --sections price,technicals
|
|
57
|
+
quantbrasil assets overview PETR4 --sections price,fundamentals --json
|
|
58
|
+
quantbrasil portfolios list
|
|
59
|
+
quantbrasil portfolios get 93
|
|
60
|
+
quantbrasil portfolios get 93 --json
|
|
61
|
+
quantbrasil portfolios create "Dividendos"
|
|
62
|
+
quantbrasil portfolios rename 93 "Longo Prazo"
|
|
63
|
+
quantbrasil portfolios add-assets 93 PETR4 VALE3
|
|
64
|
+
quantbrasil portfolios remove-assets 93 PETR4
|
|
65
|
+
quantbrasil analytics historical-return --portfolio 93 --from 2025-01-01 --to 2026-01-01
|
|
66
|
+
quantbrasil analytics beta --portfolio 93 --years 3
|
|
67
|
+
quantbrasil analytics var --portfolio 93 --years 1 --confidence 95
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Local config
|
|
71
|
+
|
|
72
|
+
Config path:
|
|
73
|
+
|
|
74
|
+
- `$XDG_CONFIG_HOME/quantbrasil/config.json`
|
|
75
|
+
- fallback: `~/.config/quantbrasil/config.json`
|
|
76
|
+
|
|
77
|
+
Current shape:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"apiKey": "qb_live_<id>.<secret>"
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Target backend
|
|
86
|
+
|
|
87
|
+
Published default:
|
|
88
|
+
|
|
89
|
+
- `https://api.quantbrasil.com.br`
|
|
90
|
+
|
|
91
|
+
Internal override for local or staging development:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
QB_BACKEND_URL=http://127.0.0.1:8000 node ./bin/quantbrasil.js --status
|
|
95
|
+
QB_BACKEND_URL=https://staging-api.quantbrasil.com.br node ./bin/quantbrasil.js --status
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Usage after publish/install
|
|
99
|
+
|
|
100
|
+
After publish and global install, the binary becomes:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
quantbrasil init
|
|
104
|
+
quantbrasil --status
|
|
105
|
+
quantbrasil auth login --api-key qb_live_<id>.<secret>
|
|
106
|
+
quantbrasil skills install --all
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Publishing
|
|
110
|
+
|
|
111
|
+
Publish from `monorepo/packages/cli`. The package build vendors the private
|
|
112
|
+
workspace core into `dist/vendor/core`, so the published package does not
|
|
113
|
+
depend on unpublished workspace packages.
|
|
114
|
+
|
|
115
|
+
Before publishing, run the packed-package smoke test:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
pnpm pack:smoke
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
That command builds the package, packs it into a local tarball, installs the
|
|
122
|
+
temporary tarball in a clean temporary environment, and verifies:
|
|
123
|
+
|
|
124
|
+
- `quantbrasil --status`
|
|
125
|
+
- `quantbrasil capabilities --json`
|
|
126
|
+
- `quantbrasil skills install --all`
|
|
127
|
+
|
|
128
|
+
To create a tarball artifact without publishing:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
npm version 0.1.0-beta.0 --no-git-tag-version
|
|
132
|
+
pnpm build
|
|
133
|
+
pnpm pack --pack-destination ./artifacts
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The tarball can be tested on another machine with:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npm exec --yes --package ./artifacts/quantbrasil-cli-0.1.0-beta.0.tgz -- quantbrasil --status
|
|
140
|
+
npm exec --yes --package ./artifacts/quantbrasil-cli-0.1.0-beta.0.tgz -- quantbrasil init
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Manual publish:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
npm version 0.1.0-beta.0 --no-git-tag-version
|
|
147
|
+
pnpm pack:smoke
|
|
148
|
+
pnpm publish --access public --tag beta --no-git-checks
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
After publishing with the `beta` tag:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
npx -y @quantbrasil/cli@beta init
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
After promoting to `latest`:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
npx -y @quantbrasil/cli@latest init
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The GitHub `Publish CLI` workflow runs the same package validation and supports
|
|
164
|
+
dry-run publishes before releasing to npm. It requires an `NPM_TOKEN` repository
|
|
165
|
+
secret with publish access to the `@quantbrasil` npm scope.
|
|
166
|
+
|
|
167
|
+
## Error output
|
|
168
|
+
|
|
169
|
+
Human errors include a stable code:
|
|
170
|
+
|
|
171
|
+
```text
|
|
172
|
+
[auth_failed] Authentication failed for the target backend. Run quantbrasil auth login again or update QUANTBRASIL_API_KEY.
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
For commands that expose `--json`, failures are written to stderr as JSON:
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"ok": false,
|
|
180
|
+
"error": {
|
|
181
|
+
"code": "auth_failed",
|
|
182
|
+
"message": "Authentication failed for the target backend. Run quantbrasil auth login again or update QUANTBRASIL_API_KEY.",
|
|
183
|
+
"category": "auth",
|
|
184
|
+
"exit_code": 1,
|
|
185
|
+
"status": 401,
|
|
186
|
+
"path": "/api/desk/tools/portfolio/list",
|
|
187
|
+
"request_id": "req_..."
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Scripts and agents should branch on `error.code`, not message text.
|
|
193
|
+
|
|
194
|
+
Stable codes:
|
|
195
|
+
|
|
196
|
+
- `auth_required`
|
|
197
|
+
- `auth_failed`
|
|
198
|
+
- `validation_error`
|
|
199
|
+
- `not_found`
|
|
200
|
+
- `rate_limited`
|
|
201
|
+
- `mutation_failed`
|
|
202
|
+
- `backend_error`
|
|
203
|
+
- `backend_unavailable`
|
|
204
|
+
- `network_error`
|
|
205
|
+
- `config_error`
|
|
206
|
+
- `unexpected_error`
|
|
207
|
+
|
|
208
|
+
## One-command setup
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
quantbrasil init
|
|
212
|
+
quantbrasil init --api-key qb_live_<id>.<secret>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
`quantbrasil init`:
|
|
216
|
+
|
|
217
|
+
- uses existing authentication if already configured
|
|
218
|
+
- otherwise prompts for an API key or accepts `--api-key`
|
|
219
|
+
- validates and stores the key locally
|
|
220
|
+
- installs the bundled public skill into supported agent clients
|
|
221
|
+
- prints a final ready summary
|
|
222
|
+
|
|
223
|
+
## Skill install
|
|
224
|
+
|
|
225
|
+
`quantbrasil skills install --all` installs the bundled public QuantBrasil skill
|
|
226
|
+
into supported user-level agent skill directories.
|
|
227
|
+
|
|
228
|
+
Bundled source of truth:
|
|
229
|
+
|
|
230
|
+
- `monorepo/packages/cli/skills/quantbrasil/`
|
|
231
|
+
|
|
232
|
+
Targets:
|
|
233
|
+
|
|
234
|
+
- `~/.agents/skills/quantbrasil` as the shared Agent Skills path
|
|
235
|
+
- `~/.codex/skills/quantbrasil` if `~/.codex` already exists
|
|
236
|
+
- `~/.claude/skills/quantbrasil` if `~/.claude` already exists
|
|
237
|
+
|
|
238
|
+
Installer behavior:
|
|
239
|
+
|
|
240
|
+
1. resolve the bundled skill from the installed CLI package
|
|
241
|
+
2. install the shared cross-client copy into `~/.agents/skills/quantbrasil`
|
|
242
|
+
3. if `~/.codex` exists, also install a compatibility mirror into
|
|
243
|
+
`~/.codex/skills/quantbrasil`
|
|
244
|
+
4. if `~/.claude` exists, also install a compatibility mirror into
|
|
245
|
+
`~/.claude/skills/quantbrasil`
|
|
246
|
+
5. replace any existing `quantbrasil` target directory before copying
|
|
247
|
+
|
|
248
|
+
Important:
|
|
249
|
+
|
|
250
|
+
- the installer does **not** create fake client homes like `~/.codex` or
|
|
251
|
+
`~/.claude`
|
|
252
|
+
- it **does** create `~/.agents/skills/quantbrasil` as the shared interop path
|
|
253
|
+
- it overwrites only the `quantbrasil` skill directory, not the whole
|
|
254
|
+
surrounding `skills/` tree
|
|
255
|
+
|
|
256
|
+
Precedence:
|
|
257
|
+
|
|
258
|
+
- the installer only manages **user-level** paths
|
|
259
|
+
- it does **not** install project-local skills such as
|
|
260
|
+
`<project>/.agents/skills/quantbrasil`
|
|
261
|
+
- project-level skills should override user-level skills when names collide
|
|
262
|
+
- for user-level installs, precedence between `~/.agents/skills/quantbrasil`
|
|
263
|
+
and client-specific mirrors should not matter for QuantBrasil because the
|
|
264
|
+
installer copies the same content to every target
|
|
265
|
+
|
|
266
|
+
Scope limits:
|
|
267
|
+
|
|
268
|
+
- no remote skill downloads
|
|
269
|
+
- no GitHub-based skill installation
|
|
270
|
+
- no arbitrary `skills add <url>`
|
|
271
|
+
- no project-local installation
|
|
272
|
+
- no per-client custom variants of the QuantBrasil skill
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface VerifyCliApiKeyOptions {
|
|
2
|
+
env?: NodeJS.ProcessEnv;
|
|
3
|
+
fetch?: typeof fetch;
|
|
4
|
+
}
|
|
5
|
+
export interface VerifiedCliApiKey {
|
|
6
|
+
ok: true;
|
|
7
|
+
callerType: "api_key";
|
|
8
|
+
userId: number;
|
|
9
|
+
email: string;
|
|
10
|
+
name: string;
|
|
11
|
+
isPremium: boolean;
|
|
12
|
+
apiKey: {
|
|
13
|
+
id: number;
|
|
14
|
+
name: string;
|
|
15
|
+
keyPrefix: string;
|
|
16
|
+
scopes: string[];
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export declare function verifyCliApiKey(apiKey: string, options?: VerifyCliApiKeyOptions): Promise<VerifiedCliApiKey>;
|
|
20
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/cli/auth.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,sBAAsB;IACrC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,IAAI,CAAC;IACT,UAAU,EAAE,SAAS,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE;QACN,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;CACH;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,iBAAiB,CAAC,CA2G5B"}
|
package/dist/cli/auth.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createDeskHttpClient, DeskHttpError } from "../vendor/core/index.js";
|
|
2
|
+
import { normalizeCliApiKey, resolveCliBackendUrl } from "./config.js";
|
|
3
|
+
import { CliError } from "./errors.js";
|
|
4
|
+
export async function verifyCliApiKey(apiKey, options = {}) {
|
|
5
|
+
const normalizedApiKey = normalizeCliApiKey(apiKey);
|
|
6
|
+
const client = createDeskHttpClient({
|
|
7
|
+
baseUrl: resolveCliBackendUrl(options.env ?? process.env),
|
|
8
|
+
fetch: options.fetch,
|
|
9
|
+
});
|
|
10
|
+
try {
|
|
11
|
+
const response = await client.request({
|
|
12
|
+
method: "GET",
|
|
13
|
+
path: "/api/desk/api-keys/me",
|
|
14
|
+
authHeaders: {
|
|
15
|
+
authorization: `Bearer ${normalizedApiKey}`,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
ok: true,
|
|
20
|
+
callerType: response.data.caller_type,
|
|
21
|
+
userId: response.data.user_id,
|
|
22
|
+
email: response.data.email,
|
|
23
|
+
name: response.data.name,
|
|
24
|
+
isPremium: response.data.is_premium,
|
|
25
|
+
apiKey: {
|
|
26
|
+
id: response.data.api_key.id,
|
|
27
|
+
name: response.data.api_key.name,
|
|
28
|
+
keyPrefix: response.data.api_key.key_prefix,
|
|
29
|
+
scopes: response.data.api_key.scopes,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (error instanceof CliError) {
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
if (error instanceof DeskHttpError) {
|
|
38
|
+
if (error.status === 401 || error.status === 403) {
|
|
39
|
+
throw new CliError("auth_failed", "Invalid API key or backend access denied.", {
|
|
40
|
+
category: "auth",
|
|
41
|
+
status: error.status,
|
|
42
|
+
path: error.path,
|
|
43
|
+
requestId: error.requestId,
|
|
44
|
+
cause: error,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (error.status === 404) {
|
|
48
|
+
throw new CliError("not_found", "The target backend does not expose /api/desk/api-keys/me yet. Use QB_BACKEND_URL to point to a compatible backend.", {
|
|
49
|
+
category: "backend",
|
|
50
|
+
status: error.status,
|
|
51
|
+
path: error.path,
|
|
52
|
+
requestId: error.requestId,
|
|
53
|
+
cause: error,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
throw new CliError(error.status === 429
|
|
57
|
+
? "rate_limited"
|
|
58
|
+
: error.status >= 502 && error.status <= 504
|
|
59
|
+
? "backend_unavailable"
|
|
60
|
+
: "backend_error", `Could not validate the API key against the backend (${error.status}).`, {
|
|
61
|
+
category: "backend",
|
|
62
|
+
status: error.status,
|
|
63
|
+
path: error.path,
|
|
64
|
+
requestId: error.requestId,
|
|
65
|
+
cause: error,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (error instanceof Error) {
|
|
69
|
+
throw new CliError("network_error", `Could not validate the API key: ${error.message}`, {
|
|
70
|
+
category: "network",
|
|
71
|
+
cause: error,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DeskHttpError, type CapabilityId, type JsonValue } from "../vendor/core/index.js";
|
|
2
|
+
import { type ResolvedCliAuth } from "./config.js";
|
|
3
|
+
import { CliError } from "./errors.js";
|
|
4
|
+
export interface CliInvokeContext {
|
|
5
|
+
env?: NodeJS.ProcessEnv;
|
|
6
|
+
fetch?: typeof fetch;
|
|
7
|
+
}
|
|
8
|
+
export interface CliCapabilityInvocationOptions {
|
|
9
|
+
capability: CapabilityId;
|
|
10
|
+
input?: JsonValue;
|
|
11
|
+
env?: NodeJS.ProcessEnv;
|
|
12
|
+
fetch?: typeof fetch;
|
|
13
|
+
}
|
|
14
|
+
export declare function resolveRequiredCliAuth(env?: NodeJS.ProcessEnv): Promise<ResolvedCliAuth>;
|
|
15
|
+
export declare function invokeCliCapability<T extends JsonValue = JsonValue>(options: CliCapabilityInvocationOptions): Promise<import("../vendor/core/index.js").CapabilityInvocationResult<T>>;
|
|
16
|
+
export declare function mapCliDeskHttpError(error: DeskHttpError): CliError;
|
|
17
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/cli/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,aAAa,EAEb,KAAK,YAAY,EACjB,KAAK,SAAS,EACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,QAAQ,EAA8B,MAAM,aAAa,CAAC;AAEnE,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,8BAA8B;IAC7C,UAAU,EAAE,YAAY,CAAC;IACzB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,wBAAsB,sBAAsB,CAC1C,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,eAAe,CAAC,CAU1B;AAED,wBAAsB,mBAAmB,CAAC,CAAC,SAAS,SAAS,GAAG,SAAS,EACvE,OAAO,EAAE,8BAA8B,+DAyBxC;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,GAAG,QAAQ,CAgElE"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { createDeskHttpClient, DeskHttpError, invokeCapability, } from "../vendor/core/index.js";
|
|
2
|
+
import { resolveCliAuth, resolveCliBackendUrl, } from "./config.js";
|
|
3
|
+
import { CliError, createCliAuthRequiredError } from "./errors.js";
|
|
4
|
+
export async function resolveRequiredCliAuth(env = process.env) {
|
|
5
|
+
const resolvedAuth = await resolveCliAuth(env);
|
|
6
|
+
if (!resolvedAuth) {
|
|
7
|
+
throw createCliAuthRequiredError("Authentication not configured. Run quantbrasil auth login --api-key <key> or set QUANTBRASIL_API_KEY.");
|
|
8
|
+
}
|
|
9
|
+
return resolvedAuth;
|
|
10
|
+
}
|
|
11
|
+
export async function invokeCliCapability(options) {
|
|
12
|
+
const env = options.env ?? process.env;
|
|
13
|
+
const auth = await resolveRequiredCliAuth(env);
|
|
14
|
+
const client = createDeskHttpClient({
|
|
15
|
+
baseUrl: resolveCliBackendUrl(env),
|
|
16
|
+
fetch: options.fetch,
|
|
17
|
+
});
|
|
18
|
+
try {
|
|
19
|
+
return await invokeCapability({
|
|
20
|
+
capability: options.capability,
|
|
21
|
+
input: options.input,
|
|
22
|
+
client,
|
|
23
|
+
authHeaders: {
|
|
24
|
+
authorization: `Bearer ${auth.apiKey}`,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (error instanceof DeskHttpError) {
|
|
30
|
+
throw mapCliDeskHttpError(error);
|
|
31
|
+
}
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function mapCliDeskHttpError(error) {
|
|
36
|
+
const detail = resolveErrorDetail(error.body);
|
|
37
|
+
if (error.status === 401) {
|
|
38
|
+
return new CliError("auth_failed", "Authentication failed for the target backend. Run quantbrasil auth login again or update QUANTBRASIL_API_KEY.", buildHttpErrorOptions(error, "auth"));
|
|
39
|
+
}
|
|
40
|
+
if (error.status === 403) {
|
|
41
|
+
return new CliError("backend_error", detail ?? "Access denied by the target backend.", buildHttpErrorOptions(error, "backend"));
|
|
42
|
+
}
|
|
43
|
+
if (error.status === 404) {
|
|
44
|
+
return new CliError("not_found", detail ?? `Resource not found for backend path ${error.path}.`, buildHttpErrorOptions(error, "backend"));
|
|
45
|
+
}
|
|
46
|
+
if (error.status === 422 && detail) {
|
|
47
|
+
return new CliError("validation_error", `Invalid command input: ${detail}`, buildHttpErrorOptions(error, "validation"));
|
|
48
|
+
}
|
|
49
|
+
if (error.status === 400 && detail) {
|
|
50
|
+
return createHttpValidationError(error, detail);
|
|
51
|
+
}
|
|
52
|
+
if (error.status === 429) {
|
|
53
|
+
return new CliError("rate_limited", detail ?? "Rate limit exceeded. Wait before retrying.", buildHttpErrorOptions(error, "backend"));
|
|
54
|
+
}
|
|
55
|
+
if (detail) {
|
|
56
|
+
return new CliError(error.status >= 502 && error.status <= 504
|
|
57
|
+
? "backend_unavailable"
|
|
58
|
+
: "backend_error", detail, buildHttpErrorOptions(error, "backend"));
|
|
59
|
+
}
|
|
60
|
+
return new CliError(error.status >= 502 && error.status <= 504
|
|
61
|
+
? "backend_unavailable"
|
|
62
|
+
: "backend_error", `The backend request failed with status ${error.status} for ${error.path}.`, buildHttpErrorOptions(error, "backend"));
|
|
63
|
+
}
|
|
64
|
+
function createHttpValidationError(error, detail) {
|
|
65
|
+
const message = detail.trim() || `Invalid command input for backend path ${error.path}.`;
|
|
66
|
+
return new CliError("validation_error", message, buildHttpErrorOptions(error, "validation"));
|
|
67
|
+
}
|
|
68
|
+
function buildHttpErrorOptions(error, category) {
|
|
69
|
+
return {
|
|
70
|
+
category,
|
|
71
|
+
status: error.status,
|
|
72
|
+
path: error.path,
|
|
73
|
+
requestId: error.requestId,
|
|
74
|
+
cause: error,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function resolveErrorDetail(payload) {
|
|
78
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const detail = payload.detail;
|
|
82
|
+
const message = payload.message;
|
|
83
|
+
if (typeof detail === "string" && detail.trim()) {
|
|
84
|
+
return detail;
|
|
85
|
+
}
|
|
86
|
+
if (Array.isArray(detail)) {
|
|
87
|
+
const validationDetails = detail
|
|
88
|
+
.map(formatValidationDetail)
|
|
89
|
+
.filter(Boolean);
|
|
90
|
+
if (validationDetails.length > 0) {
|
|
91
|
+
return validationDetails.join("; ");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (typeof message === "string" && message.trim()) {
|
|
95
|
+
return message;
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
function formatValidationDetail(value) {
|
|
100
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const detail = value;
|
|
104
|
+
const message = typeof detail.msg === "string" ? detail.msg : null;
|
|
105
|
+
const loc = Array.isArray(detail.loc)
|
|
106
|
+
? detail.loc.filter(item => typeof item === "string").join(".")
|
|
107
|
+
: null;
|
|
108
|
+
if (message && loc) {
|
|
109
|
+
return `${loc}: ${message}`;
|
|
110
|
+
}
|
|
111
|
+
return message;
|
|
112
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare const DEFAULT_PUBLIC_BACKEND_URL = "https://api.quantbrasil.com.br";
|
|
2
|
+
export declare const CLI_CONFIG_FILENAME = "config.json";
|
|
3
|
+
export declare const CLI_API_KEY_PREFIX_FAMILY = "qb_live_";
|
|
4
|
+
export type CliAuthSource = "env" | "config" | "none";
|
|
5
|
+
export type CliBackendSource = "default" | "override";
|
|
6
|
+
export interface CliStoredConfig {
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ResolvedCliAuth {
|
|
10
|
+
apiKey: string;
|
|
11
|
+
source: Exclude<CliAuthSource, "none">;
|
|
12
|
+
}
|
|
13
|
+
export interface CliStatusSnapshot {
|
|
14
|
+
backendUrl: string;
|
|
15
|
+
backendSource: CliBackendSource;
|
|
16
|
+
configPath: string;
|
|
17
|
+
storedApiKey: boolean;
|
|
18
|
+
authSource: CliAuthSource;
|
|
19
|
+
isReady: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function resolveCliConfigPath(env?: NodeJS.ProcessEnv): string;
|
|
22
|
+
export declare function resolveCliBackendUrl(env?: NodeJS.ProcessEnv): string;
|
|
23
|
+
export declare function resolveCliBackendSource(env?: NodeJS.ProcessEnv): CliBackendSource;
|
|
24
|
+
export declare function readCliConfig(env?: NodeJS.ProcessEnv): Promise<CliStoredConfig>;
|
|
25
|
+
export declare function saveCliApiKey(apiKey: string, env?: NodeJS.ProcessEnv): Promise<string>;
|
|
26
|
+
export declare function clearCliStoredAuth(env?: NodeJS.ProcessEnv): Promise<{
|
|
27
|
+
configPath: string;
|
|
28
|
+
removed: boolean;
|
|
29
|
+
}>;
|
|
30
|
+
export declare function resolveCliAuth(env?: NodeJS.ProcessEnv): Promise<ResolvedCliAuth | null>;
|
|
31
|
+
export declare function resolveCliStatusSnapshot(env?: NodeJS.ProcessEnv): Promise<CliStatusSnapshot>;
|
|
32
|
+
export declare function normalizeCliApiKey(apiKey: string): string;
|
|
33
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,0BAA0B,mCAAmC,CAAC;AAC3E,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AACjD,eAAO,MAAM,yBAAyB,aAAa,CAAC;AAEpD,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AACtD,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,UAAU,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,gBAAgB,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,aAAa,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,oBAAoB,CAClC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAGR;AAED,wBAAgB,oBAAoB,CAClC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAGR;AAED,wBAAgB,uBAAuB,CACrC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,gBAAgB,CAElB;AAED,wBAAsB,aAAa,CACjC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,eAAe,CAAC,CAgC1B;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,MAAM,CAAC,CAkBjB;AAED,wBAAsB,kBAAkB,CACtC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAUnD;AAED,wBAAsB,cAAc,CAClC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAkBjC;AAED,wBAAsB,wBAAwB,CAC5C,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,iBAAiB,CAAC,CAa5B;AA2BD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAmBzD"}
|