@h1veframework/cli 0.2.0 → 0.3.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 +5 -0
- package/dist/index.js +21 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,9 +25,14 @@ nf --version
|
|
|
25
25
|
```bash
|
|
26
26
|
export H1VE_API_URL="https://app.h1ve.org"
|
|
27
27
|
export H1VE_API_KEY="nf_pat_..." # seu PAT
|
|
28
|
+
export H1VE_PROJECT_ID="..." # OBRIGATÓRIO só se você tem 2+ projetos (o id do projeto)
|
|
28
29
|
nf health # testa a conexão
|
|
29
30
|
```
|
|
30
31
|
|
|
32
|
+
> **`H1VE_PROJECT_ID`** (SPEC-076): com **um** projeto, não precisa — o servidor resolve sozinho.
|
|
33
|
+
> Com **2+ projetos** (plano pago), defina o id do projeto — senão o `nf start`/`nf health` responde
|
|
34
|
+
> *"Você tem mais de um projeto. Defina H1VE_PROJECT_ID."* (o id fica na app, no projeto).
|
|
35
|
+
|
|
31
36
|
> Coloque os `export` no seu `~/.zshrc` / `~/.bashrc` para não repetir a cada sessão.
|
|
32
37
|
> "Nenhum snapshot registrado" no `nf health` é **sucesso** (conectou; projeto sem métricas ainda).
|
|
33
38
|
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,13 @@ var NexusApiError = class extends Error {
|
|
|
13
13
|
}
|
|
14
14
|
code;
|
|
15
15
|
};
|
|
16
|
-
function errorForStatus(status2) {
|
|
16
|
+
function errorForStatus(status2, code) {
|
|
17
|
+
if (code === "NO_PROJECT") {
|
|
18
|
+
return new NexusApiError(
|
|
19
|
+
"NO_PROJECT",
|
|
20
|
+
"Voc\xEA tem mais de um projeto. Defina H1VE_PROJECT_ID (o id do projeto) na config do CLI/MCP."
|
|
21
|
+
);
|
|
22
|
+
}
|
|
17
23
|
switch (status2) {
|
|
18
24
|
case 401:
|
|
19
25
|
return new NexusApiError("UNAUTHENTICATED", "H1VE_API_KEY ausente ou inv\xE1lida.");
|
|
@@ -68,7 +74,13 @@ function rootUrl(baseUrl) {
|
|
|
68
74
|
}
|
|
69
75
|
function createClient(config) {
|
|
70
76
|
const doFetch = config.fetchImpl ?? fetch;
|
|
71
|
-
const authHeaders = {
|
|
77
|
+
const authHeaders = {
|
|
78
|
+
"x-api-key": config.apiKey,
|
|
79
|
+
accept: "application/json",
|
|
80
|
+
// SPEC-076: envia o projeto ativo quando configurado. O servidor valida a membership
|
|
81
|
+
// do id e ignora o header onde não escopa — então mandar sempre é simples e seguro.
|
|
82
|
+
...config.projectId ? { "x-project-id": config.projectId } : {}
|
|
83
|
+
};
|
|
72
84
|
async function postAction(featureId, action, body) {
|
|
73
85
|
const url = `${rootUrl(config.baseUrl)}/api/features/${encodeURIComponent(featureId)}/${action}`;
|
|
74
86
|
const res = await doFetch(url, {
|
|
@@ -83,19 +95,19 @@ function createClient(config) {
|
|
|
83
95
|
async getContext(branch) {
|
|
84
96
|
const url = `${rootUrl(config.baseUrl)}/api/context?branch=${encodeURIComponent(branch)}`;
|
|
85
97
|
const res = await doFetch(url, { headers: authHeaders });
|
|
86
|
-
if (!res.ok) throw errorForStatus(res.status);
|
|
98
|
+
if (!res.ok) throw errorForStatus(res.status, await readErrorCode(res));
|
|
87
99
|
return await res.json();
|
|
88
100
|
},
|
|
89
101
|
async listHealth() {
|
|
90
102
|
const url = `${rootUrl(config.baseUrl)}/api/health`;
|
|
91
103
|
const res = await doFetch(url, { headers: authHeaders });
|
|
92
|
-
if (!res.ok) throw errorForStatus(res.status);
|
|
104
|
+
if (!res.ok) throw errorForStatus(res.status, await readErrorCode(res));
|
|
93
105
|
return await res.json();
|
|
94
106
|
},
|
|
95
107
|
async listFeatures() {
|
|
96
108
|
const url = `${rootUrl(config.baseUrl)}/api/features`;
|
|
97
109
|
const res = await doFetch(url, { headers: authHeaders });
|
|
98
|
-
if (!res.ok) throw errorForStatus(res.status);
|
|
110
|
+
if (!res.ok) throw errorForStatus(res.status, await readErrorCode(res));
|
|
99
111
|
return await res.json();
|
|
100
112
|
},
|
|
101
113
|
startFeature(featureId, slug) {
|
|
@@ -113,7 +125,7 @@ function createClient(config) {
|
|
|
113
125
|
async listProjects() {
|
|
114
126
|
const url = `${rootUrl(config.baseUrl)}/api/projects`;
|
|
115
127
|
const res = await doFetch(url, { headers: authHeaders });
|
|
116
|
-
if (!res.ok) throw errorForStatus(res.status);
|
|
128
|
+
if (!res.ok) throw errorForStatus(res.status, await readErrorCode(res));
|
|
117
129
|
return await res.json();
|
|
118
130
|
},
|
|
119
131
|
async createConnection(projectId, input) {
|
|
@@ -260,16 +272,17 @@ var CliError = class extends Error {
|
|
|
260
272
|
function readConfig(env = process.env) {
|
|
261
273
|
const baseUrl = env.H1VE_API_URL?.trim() || env.NEXUS_FLOW_API_URL?.trim();
|
|
262
274
|
const apiKey = env.H1VE_API_KEY?.trim() || env.NEXUS_FLOW_API_KEY?.trim();
|
|
275
|
+
const projectId = env.H1VE_PROJECT_ID?.trim() || env.NEXUS_FLOW_PROJECT_ID?.trim();
|
|
263
276
|
if (!baseUrl || !apiKey) {
|
|
264
277
|
throw new CliError(
|
|
265
278
|
"Defina H1VE_API_URL e H1VE_API_KEY no ambiente.\n export H1VE_API_URL=https://app.h1ve.org\n export H1VE_API_KEY=nf_pat_... (crie um token em /api-tokens)\n (os nomes legados NEXUS_FLOW_API_URL/KEY ainda s\xE3o aceitos)",
|
|
266
279
|
2
|
|
267
280
|
);
|
|
268
281
|
}
|
|
269
|
-
return { baseUrl, apiKey };
|
|
282
|
+
return { baseUrl, apiKey, projectId };
|
|
270
283
|
}
|
|
271
284
|
function clientFromConfig(cfg) {
|
|
272
|
-
return createClient({ baseUrl: cfg.baseUrl, apiKey: cfg.apiKey });
|
|
285
|
+
return createClient({ baseUrl: cfg.baseUrl, apiKey: cfg.apiKey, projectId: cfg.projectId });
|
|
273
286
|
}
|
|
274
287
|
|
|
275
288
|
// src/format.ts
|
package/package.json
CHANGED