@corvio/cli 0.1.0-beta.2 → 0.1.0-beta.3
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 +1 -1
- package/dist/cli.js +1 -1
- package/dist/core.js +112 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Official CLI for a user's Corvio Workspace. It supports an existing `CORVIO_API_KEY` and a browser device-link login.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install --global @corvio/cli
|
|
6
|
+
npm install --global @corvio/cli@beta
|
|
7
7
|
export CORVIO_API_KEY='cvk_...'
|
|
8
8
|
corvio auth status
|
|
9
9
|
corvio ask --prompt 'Summarize recurring launch risks' --json
|
package/dist/cli.js
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
writeTextFile,
|
|
22
22
|
} from "./core.js";
|
|
23
23
|
|
|
24
|
-
const VERSION = "0.1.0-beta.
|
|
24
|
+
const VERSION = "0.1.0-beta.3";
|
|
25
25
|
const DEFAULT_API_BASE = "https://api.corvio.ai/v1";
|
|
26
26
|
const DEFAULT_APP_BASE = "https://corvio.ai";
|
|
27
27
|
const USER_AGENT = `corvio-cli/${VERSION}`;
|
package/dist/core.js
CHANGED
|
@@ -163,6 +163,51 @@ export class ApiClient {
|
|
|
163
163
|
}
|
|
164
164
|
return payload;
|
|
165
165
|
}
|
|
166
|
+
|
|
167
|
+
async requestBytes(path, { query, headers = {}, auth = true } = {}) {
|
|
168
|
+
const url = new URL(`${this.baseUrl}${path.startsWith("/") ? path : `/${path}`}`);
|
|
169
|
+
for (const [key, value] of Object.entries(query || {})) {
|
|
170
|
+
if (value !== undefined && value !== null && value !== "") url.searchParams.set(key, String(value));
|
|
171
|
+
}
|
|
172
|
+
const requestHeaders = {
|
|
173
|
+
accept: "application/zip, application/octet-stream",
|
|
174
|
+
"user-agent": this.userAgent,
|
|
175
|
+
...headers,
|
|
176
|
+
};
|
|
177
|
+
if (auth) {
|
|
178
|
+
if (!this.credential) {
|
|
179
|
+
throw new CliError("Corvio authentication is required.", { code: "auth_required", exitCode: EXIT.AUTH });
|
|
180
|
+
}
|
|
181
|
+
requestHeaders.authorization = `Bearer ${this.credential}`;
|
|
182
|
+
}
|
|
183
|
+
let response;
|
|
184
|
+
try {
|
|
185
|
+
response = await this.fetchImpl(url, { method: "GET", headers: requestHeaders });
|
|
186
|
+
} catch (cause) {
|
|
187
|
+
throw new CliError(`Unable to reach Corvio: ${cause instanceof Error ? cause.message : String(cause)}`, {
|
|
188
|
+
code: "network_error",
|
|
189
|
+
exitCode: EXIT.REMOTE,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
193
|
+
if (!response.ok) {
|
|
194
|
+
const text = new TextDecoder().decode(bytes);
|
|
195
|
+
let payload = {};
|
|
196
|
+
try {
|
|
197
|
+
payload = JSON.parse(text);
|
|
198
|
+
} catch {
|
|
199
|
+
payload = { message: text.slice(0, 500) };
|
|
200
|
+
}
|
|
201
|
+
const envelope = payload?.error || payload?.detail || payload || {};
|
|
202
|
+
throw new CliError(String(envelope.message || envelope.code || `Corvio request failed (${response.status}).`), {
|
|
203
|
+
code: String(envelope.code || `http_${response.status}`),
|
|
204
|
+
status: response.status,
|
|
205
|
+
exitCode: exitCodeForStatus(response.status),
|
|
206
|
+
details: envelope.details || null,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
return bytes;
|
|
210
|
+
}
|
|
166
211
|
}
|
|
167
212
|
|
|
168
213
|
function configRoot(env = process.env) {
|
|
@@ -175,6 +220,10 @@ export function workspaceCredentialPath(env = process.env) {
|
|
|
175
220
|
return join(configRoot(env), "credentials.json");
|
|
176
221
|
}
|
|
177
222
|
|
|
223
|
+
export function adminCredentialPath(env = process.env) {
|
|
224
|
+
return join(configRoot(env), "admin-credentials.json");
|
|
225
|
+
}
|
|
226
|
+
|
|
178
227
|
export async function readWorkspaceCredential(env = process.env) {
|
|
179
228
|
const environmentKey = String(env.CORVIO_API_KEY || "").trim();
|
|
180
229
|
if (environmentKey) {
|
|
@@ -241,16 +290,66 @@ export async function clearWorkspaceCredential(env = process.env) {
|
|
|
241
290
|
return path;
|
|
242
291
|
}
|
|
243
292
|
|
|
244
|
-
export function readAdminCredential(env = process.env) {
|
|
293
|
+
export async function readAdminCredential(env = process.env) {
|
|
245
294
|
const key = String(env.CORVIO_ADMIN_API_KEY || "").trim();
|
|
246
|
-
if (
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
295
|
+
if (key) {
|
|
296
|
+
if (!key.startsWith("cva_")) {
|
|
297
|
+
throw new CliError("CORVIO_ADMIN_API_KEY must be a platform administrator cva_ key.", {
|
|
298
|
+
code: "invalid_admin_key",
|
|
299
|
+
exitCode: EXIT.AUTH,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
return { key, source: "environment", metadata: null };
|
|
303
|
+
}
|
|
304
|
+
const path = adminCredentialPath(env);
|
|
305
|
+
let parsed;
|
|
306
|
+
try {
|
|
307
|
+
parsed = JSON.parse(await readFile(path, "utf8"));
|
|
308
|
+
} catch (cause) {
|
|
309
|
+
if (cause?.code === "ENOENT") return null;
|
|
310
|
+
throw new CliError("Stored Corvio administrator credentials are unreadable.", {
|
|
311
|
+
code: "admin_credential_store_invalid",
|
|
312
|
+
exitCode: EXIT.AUTH,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
const storedKey = String(parsed.api_key || "").trim();
|
|
316
|
+
if (!storedKey.startsWith("cva_")) {
|
|
317
|
+
throw new CliError("Stored Corvio administrator credentials are invalid; run `corvio-admin auth login` again.", {
|
|
318
|
+
code: "admin_credential_store_invalid",
|
|
250
319
|
exitCode: EXIT.AUTH,
|
|
251
320
|
});
|
|
252
321
|
}
|
|
253
|
-
return { key, source: "
|
|
322
|
+
return { key: storedKey, source: "device_link", metadata: parsed };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export async function storeAdminCredential(payload, env = process.env) {
|
|
326
|
+
const path = adminCredentialPath(env);
|
|
327
|
+
const root = dirname(path);
|
|
328
|
+
await mkdir(root, { recursive: true, mode: 0o700 });
|
|
329
|
+
await chmod(root, 0o700).catch(() => {});
|
|
330
|
+
const document = {
|
|
331
|
+
version: 1,
|
|
332
|
+
api_key: payload.secret,
|
|
333
|
+
key_id: payload.key_id,
|
|
334
|
+
audience: payload.audience,
|
|
335
|
+
api_base: payload.api_base,
|
|
336
|
+
scopes: payload.scopes || [],
|
|
337
|
+
expires_at: payload.expires_at,
|
|
338
|
+
created_at: payload.created_at || new Date().toISOString(),
|
|
339
|
+
source: "device_link",
|
|
340
|
+
};
|
|
341
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
342
|
+
await writeFile(temporary, `${JSON.stringify(document, null, 2)}\n`, { mode: 0o600, flag: "wx" });
|
|
343
|
+
await chmod(temporary, 0o600);
|
|
344
|
+
await rename(temporary, path);
|
|
345
|
+
await chmod(path, 0o600);
|
|
346
|
+
return { path };
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export async function clearAdminCredential(env = process.env) {
|
|
350
|
+
const path = adminCredentialPath(env);
|
|
351
|
+
await rm(path, { force: true });
|
|
352
|
+
return path;
|
|
254
353
|
}
|
|
255
354
|
|
|
256
355
|
export function writeResult(value, { json = false, stdout = process.stdout } = {}) {
|
|
@@ -313,6 +412,13 @@ export async function writeTextFile(path, content) {
|
|
|
313
412
|
return target;
|
|
314
413
|
}
|
|
315
414
|
|
|
415
|
+
export async function writeBinaryFile(path, content) {
|
|
416
|
+
const target = resolve(String(path));
|
|
417
|
+
await mkdir(dirname(target), { recursive: true });
|
|
418
|
+
await writeFile(target, content);
|
|
419
|
+
return target;
|
|
420
|
+
}
|
|
421
|
+
|
|
316
422
|
export function requireConfirmation(options, action) {
|
|
317
423
|
if (!options.yes) {
|
|
318
424
|
throw new CliError(`${action} requires --yes.`, { code: "confirmation_required" });
|