@vincentt-xr/cli 0.12.0 → 0.12.1
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/dist/lib/backend.js
CHANGED
|
@@ -6,6 +6,7 @@ import { promises as fs } from "node:fs";
|
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { execFile } from "node:child_process";
|
|
8
8
|
import { promisify } from "node:util";
|
|
9
|
+
import { clientHeaders } from "../shared/clientHeader.js";
|
|
9
10
|
import { authFailureMessage } from "../shared/authError.js";
|
|
10
11
|
const execFileAsync = promisify(execFile);
|
|
11
12
|
export class BackendError extends Error {
|
|
@@ -19,7 +20,11 @@ export class BackendError extends Error {
|
|
|
19
20
|
async function authFetch(cfg, pathname, init = {}) {
|
|
20
21
|
return fetch(`${cfg.apiUrl}${pathname}`, {
|
|
21
22
|
...init,
|
|
22
|
-
headers: {
|
|
23
|
+
headers: {
|
|
24
|
+
...(init.headers ?? {}),
|
|
25
|
+
...clientHeaders(),
|
|
26
|
+
authorization: `Bearer ${cfg.pat}`,
|
|
27
|
+
},
|
|
23
28
|
});
|
|
24
29
|
}
|
|
25
30
|
async function safeText(res) {
|
package/dist/library/client.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
// CLI-supplied URL reaches a fetch. That is a security control, not a style choice
|
|
13
13
|
// (D-add-prints-urls degraded the no-invented-URLs rule from prevention to
|
|
14
14
|
// detection, and this is the one arm that stayed prevention).
|
|
15
|
+
import { clientHeaders } from "../shared/clientHeader.js";
|
|
15
16
|
import { parseBatchResponse, parseFetchResponse, parseSearchResponse, } from "./wire.js";
|
|
16
17
|
/** The API said no, with a code we can act on. */
|
|
17
18
|
export class LibraryError extends Error {
|
|
@@ -61,7 +62,7 @@ async function readJson(res) {
|
|
|
61
62
|
*/
|
|
62
63
|
export function httpLibraryClient(cfg) {
|
|
63
64
|
const base = cfg.apiUrl.replace(/\/$/, "");
|
|
64
|
-
const auth = { authorization: `Bearer ${cfg.pat}` };
|
|
65
|
+
const auth = { ...clientHeaders(), authorization: `Bearer ${cfg.pat}` };
|
|
65
66
|
const call = async (pathname, init) => {
|
|
66
67
|
try {
|
|
67
68
|
return await fetch(`${base}${pathname}`, {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// POST /access-tokens/exchange {code, code_verifier} → {token} (raw PAT, once, in body)
|
|
8
8
|
// GET /me Bearer <PAT> → identity + orgs (confirm)
|
|
9
9
|
import { authFailureMessage } from "../shared/authError.js";
|
|
10
|
+
import { clientHeaders } from "../shared/clientHeader.js";
|
|
10
11
|
class AuthError extends Error {
|
|
11
12
|
status;
|
|
12
13
|
constructor(message, status) {
|
|
@@ -30,7 +31,7 @@ export function httpAuthBackend(apiBase) {
|
|
|
30
31
|
async exchange(code, codeVerifier) {
|
|
31
32
|
const res = await fetch(`${base}/access-tokens/exchange`, {
|
|
32
33
|
method: "POST",
|
|
33
|
-
headers: { "content-type": "application/json" },
|
|
34
|
+
headers: { ...clientHeaders(), "content-type": "application/json" },
|
|
34
35
|
body: JSON.stringify({ code, code_verifier: codeVerifier }),
|
|
35
36
|
});
|
|
36
37
|
if (!res.ok) {
|
|
@@ -43,7 +44,7 @@ export function httpAuthBackend(apiBase) {
|
|
|
43
44
|
},
|
|
44
45
|
async me(pat) {
|
|
45
46
|
const res = await fetch(`${base}/me`, {
|
|
46
|
-
headers: { authorization: `Bearer ${pat}` },
|
|
47
|
+
headers: { ...clientHeaders(), authorization: `Bearer ${pat}` },
|
|
47
48
|
});
|
|
48
49
|
if (!res.ok) {
|
|
49
50
|
throw new AuthError(authFailureMessage("Identity check failed", res.status, await safeText(res)), res.status);
|
package/dist/publish/api.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
//
|
|
6
6
|
// The bytes do NOT transit the API. Open returns one presigned PUT per file,
|
|
7
7
|
// each signed for one exact key; the CLI PUTs direct to S3 and then commits.
|
|
8
|
+
import { clientHeaders } from "../shared/clientHeader.js";
|
|
8
9
|
export class PublishApiError extends Error {
|
|
9
10
|
status;
|
|
10
11
|
code;
|
|
@@ -31,7 +32,7 @@ async function readError(res) {
|
|
|
31
32
|
return new PublishApiError(res.status, body.code ?? `http_${res.status}`, body.message ?? `request failed (${res.status})`, body.detail ?? {}, retryAfter ? Number(retryAfter) : undefined);
|
|
32
33
|
}
|
|
33
34
|
function authHeaders(cfg) {
|
|
34
|
-
return { authorization: `Bearer ${cfg.pat}` };
|
|
35
|
+
return { ...clientHeaders(), authorization: `Bearer ${cfg.pat}` };
|
|
35
36
|
}
|
|
36
37
|
/** The ONE call before the checklist. liveVersion + livePublishedAt ride it, by design. */
|
|
37
38
|
export async function readProject(cfg, projectId, fetchImpl = fetch) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** The header name. Canonical casing; HTTP header matching is case-insensitive. */
|
|
2
|
+
export declare const CLIENT_HEADER = "X-Vincentt-Client";
|
|
3
|
+
export declare function clientVersion(): string;
|
|
4
|
+
/**
|
|
5
|
+
* The header value: `cli/<version>`.
|
|
6
|
+
*
|
|
7
|
+
* The `cli/` prefix is what keeps the field readable when a second first-party
|
|
8
|
+
* client (the harness, a future console agent) starts reporting on the same
|
|
9
|
+
* header — a bare version would be ambiguous the moment it is not alone.
|
|
10
|
+
*/
|
|
11
|
+
export declare function clientHeaderValue(): string;
|
|
12
|
+
/**
|
|
13
|
+
* The header, as a spreadable object. Callers merge this into their existing
|
|
14
|
+
* headers rather than assembling the name themselves, so the format stays in one
|
|
15
|
+
* place.
|
|
16
|
+
*/
|
|
17
|
+
export declare function clientHeaders(): Record<string, string>;
|
|
18
|
+
/** Reset the memo. Test seam only. */
|
|
19
|
+
export declare function resetClientVersionCache(): void;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The client-identity header every API request carries.
|
|
3
|
+
*
|
|
4
|
+
* The platform could not previously tell which CLI was calling it. That makes
|
|
5
|
+
* two ordinary questions unanswerable: how far behind the installed base
|
|
6
|
+
* actually is, and whether a wire change is safe to make yet. Both are questions
|
|
7
|
+
* about a population, and no amount of reading one creator's terminal answers
|
|
8
|
+
* them.
|
|
9
|
+
*
|
|
10
|
+
* It is also the seam a minimum-version refusal would need. That is deliberately
|
|
11
|
+
* NOT built here — a floor that strands someone mid-publish is worth having only
|
|
12
|
+
* once a contract has actually broken, and it needs this measurement first to
|
|
13
|
+
* know where to sit.
|
|
14
|
+
*
|
|
15
|
+
* `X-Vincentt-Client` is the platform's namespace, which the EDGE strips from
|
|
16
|
+
* viewer traffic (see isViewerRoutingHeader in the api repo): a device must not
|
|
17
|
+
* be able to forge what a creator's tooling reports about itself. This is the
|
|
18
|
+
* other side of that rule — the CLI is a first-party client talking to the API
|
|
19
|
+
* directly, not a viewer, so it is the legitimate origin of the value rather
|
|
20
|
+
* than something to be filtered out.
|
|
21
|
+
*/
|
|
22
|
+
import { createRequire } from "node:module";
|
|
23
|
+
/** The header name. Canonical casing; HTTP header matching is case-insensitive. */
|
|
24
|
+
export const CLIENT_HEADER = "X-Vincentt-Client";
|
|
25
|
+
/**
|
|
26
|
+
* The running package's version, read from the manifest rather than baked in by
|
|
27
|
+
* the build — a hardcoded string is one forgotten edit away from a CLI that
|
|
28
|
+
* misreports itself, which is worse than reporting nothing. `src/shared/` and
|
|
29
|
+
* the emitted `dist/shared/` sit at the same depth, so one relative path serves
|
|
30
|
+
* both.
|
|
31
|
+
*/
|
|
32
|
+
function readVersion() {
|
|
33
|
+
try {
|
|
34
|
+
const manifest = createRequire(import.meta.url)("../../package.json");
|
|
35
|
+
return manifest.version ?? "unknown";
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return "unknown";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Cached: the manifest cannot change within a process, and this is read on every
|
|
43
|
+
* outbound request.
|
|
44
|
+
*/
|
|
45
|
+
let cached;
|
|
46
|
+
export function clientVersion() {
|
|
47
|
+
if (cached === undefined)
|
|
48
|
+
cached = readVersion();
|
|
49
|
+
return cached;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The header value: `cli/<version>`.
|
|
53
|
+
*
|
|
54
|
+
* The `cli/` prefix is what keeps the field readable when a second first-party
|
|
55
|
+
* client (the harness, a future console agent) starts reporting on the same
|
|
56
|
+
* header — a bare version would be ambiguous the moment it is not alone.
|
|
57
|
+
*/
|
|
58
|
+
export function clientHeaderValue() {
|
|
59
|
+
return `cli/${clientVersion()}`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The header, as a spreadable object. Callers merge this into their existing
|
|
63
|
+
* headers rather than assembling the name themselves, so the format stays in one
|
|
64
|
+
* place.
|
|
65
|
+
*/
|
|
66
|
+
export function clientHeaders() {
|
|
67
|
+
return { [CLIENT_HEADER]: clientHeaderValue() };
|
|
68
|
+
}
|
|
69
|
+
/** Reset the memo. Test seam only. */
|
|
70
|
+
export function resetClientVersionCache() {
|
|
71
|
+
cached = undefined;
|
|
72
|
+
}
|
package/package.json
CHANGED