@kora-platform/cli 0.8.0-rc3 → 0.8.0-rc6
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/api-client.d.ts +25 -13
- package/dist/api-client.js +19 -18
- package/dist/api-types.d.ts +22 -2
- package/dist/artifact-commands.js +5 -3
- package/dist/auth-commands.js +84 -8
- package/dist/cli-errors.d.ts +7 -1
- package/dist/cli-errors.js +12 -1
- package/dist/command-registry.js +40 -34
- package/dist/commands.js +64 -27
- package/dist/error-code.d.ts +2 -0
- package/dist/error-code.js +9 -0
- package/dist/extension-commands.js +2 -2
- package/dist/files.d.ts +12 -1
- package/dist/files.js +102 -16
- package/dist/format.d.ts +1 -0
- package/dist/format.js +11 -6
- package/dist/runner.js +1 -0
- package/dist/transport.d.ts +20 -0
- package/dist/transport.js +54 -2
- package/package.json +1 -1
package/dist/format.d.ts
CHANGED
package/dist/format.js
CHANGED
|
@@ -7,12 +7,17 @@ export function renderJsonEnvelope(input) {
|
|
|
7
7
|
}
|
|
8
8
|
export function renderProblemJson(input) {
|
|
9
9
|
return `${JSON.stringify({
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
error: {
|
|
11
|
+
code: input.code,
|
|
12
|
+
details: {
|
|
13
|
+
...(input.details ?? {}),
|
|
14
|
+
instance: input.instance,
|
|
15
|
+
status: input.status,
|
|
16
|
+
title: input.title,
|
|
17
|
+
type: input.type
|
|
18
|
+
},
|
|
19
|
+
message: input.detail
|
|
20
|
+
}
|
|
16
21
|
}, null, 2)}\n`;
|
|
17
22
|
}
|
|
18
23
|
export function renderTable(rows, columns) {
|
package/dist/runner.js
CHANGED
|
@@ -89,6 +89,7 @@ export async function runCli(argv, input = {}) {
|
|
|
89
89
|
: `${problem.title}: ${formatProblemDetail(problem.detail, details)}`}\n`,
|
|
90
90
|
stdout: wantsJson
|
|
91
91
|
? renderProblemJson({
|
|
92
|
+
code: problem.code,
|
|
92
93
|
detail: problem.detail,
|
|
93
94
|
...(details ? { details } : {}),
|
|
94
95
|
instance: problem.instance,
|
package/dist/transport.d.ts
CHANGED
|
@@ -10,6 +10,19 @@ export interface CliAuthSettings {
|
|
|
10
10
|
oidcEnabled: boolean;
|
|
11
11
|
selfServiceOrgCreationEnabled: boolean;
|
|
12
12
|
}
|
|
13
|
+
export interface CliDeviceLoginStart {
|
|
14
|
+
deviceCode: string;
|
|
15
|
+
expiresAt: string;
|
|
16
|
+
pollIntervalSeconds: number;
|
|
17
|
+
userCode: string;
|
|
18
|
+
verificationPath: string;
|
|
19
|
+
}
|
|
20
|
+
export type CliDeviceLoginClaim = {
|
|
21
|
+
session: CliSessionState;
|
|
22
|
+
status: "approved";
|
|
23
|
+
} | {
|
|
24
|
+
status: "denied" | "expired" | "pending";
|
|
25
|
+
};
|
|
13
26
|
export type ApiErrorDetails = Record<string, unknown>;
|
|
14
27
|
type RequestBody = unknown | ((session: CliSessionState) => unknown);
|
|
15
28
|
type BytesRequest = {
|
|
@@ -26,6 +39,7 @@ type BytesRequest = {
|
|
|
26
39
|
validateHeaders?: (headers: Headers) => void;
|
|
27
40
|
};
|
|
28
41
|
export declare class ApiError extends Error {
|
|
42
|
+
readonly code: string;
|
|
29
43
|
readonly detail: string;
|
|
30
44
|
readonly details?: ApiErrorDetails;
|
|
31
45
|
readonly instance: string;
|
|
@@ -34,6 +48,7 @@ export declare class ApiError extends Error {
|
|
|
34
48
|
readonly title: string;
|
|
35
49
|
readonly type: string;
|
|
36
50
|
constructor(input: {
|
|
51
|
+
code?: string;
|
|
37
52
|
detail: string;
|
|
38
53
|
details?: ApiErrorDetails;
|
|
39
54
|
instance: string;
|
|
@@ -59,6 +74,11 @@ export declare function createPlatformTransport(input: {
|
|
|
59
74
|
name: string;
|
|
60
75
|
password: string;
|
|
61
76
|
}): Promise<CliSessionState>;
|
|
77
|
+
startDeviceLogin(baseUrl: string): Promise<CliDeviceLoginStart>;
|
|
78
|
+
claimDeviceLogin(claim: {
|
|
79
|
+
baseUrl: string;
|
|
80
|
+
deviceCode: string;
|
|
81
|
+
}): Promise<CliDeviceLoginClaim>;
|
|
62
82
|
refreshSession(session: CliSessionState): Promise<CliSessionState>;
|
|
63
83
|
requestJson<T>(request: {
|
|
64
84
|
body?: RequestBody;
|
package/dist/transport.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { normalizePublicErrorCode, readPublicErrorCodeFromType } from "./error-code.js";
|
|
1
2
|
import { extractRefreshTokenFromHeaders } from "./session.js";
|
|
2
3
|
export class ApiError extends Error {
|
|
4
|
+
code;
|
|
3
5
|
detail;
|
|
4
6
|
details;
|
|
5
7
|
instance;
|
|
@@ -10,6 +12,7 @@ export class ApiError extends Error {
|
|
|
10
12
|
constructor(input) {
|
|
11
13
|
super(input.detail);
|
|
12
14
|
this.name = "ApiError";
|
|
15
|
+
this.code = normalizePublicErrorCode(input.code ?? readPublicErrorCodeFromType(input.type));
|
|
13
16
|
this.detail = input.detail;
|
|
14
17
|
if (input.details) {
|
|
15
18
|
this.details = input.details;
|
|
@@ -66,6 +69,52 @@ export function createPlatformTransport(input) {
|
|
|
66
69
|
await input.sessionStore.write(session);
|
|
67
70
|
return session;
|
|
68
71
|
},
|
|
72
|
+
async startDeviceLogin(baseUrl) {
|
|
73
|
+
const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
|
|
74
|
+
const path = "/api/v1/auth/device/start";
|
|
75
|
+
const response = await fetch(joinBaseUrl(normalizedBaseUrl, path), {
|
|
76
|
+
method: "POST"
|
|
77
|
+
});
|
|
78
|
+
return handleJsonResponse(response, path);
|
|
79
|
+
},
|
|
80
|
+
async claimDeviceLogin(claim) {
|
|
81
|
+
const normalizedBaseUrl = normalizeBaseUrl(claim.baseUrl);
|
|
82
|
+
const path = "/api/v1/auth/device/claim";
|
|
83
|
+
const response = await fetch(joinBaseUrl(normalizedBaseUrl, path), {
|
|
84
|
+
body: JSON.stringify({
|
|
85
|
+
deviceCode: claim.deviceCode
|
|
86
|
+
}),
|
|
87
|
+
headers: {
|
|
88
|
+
"content-type": "application/json"
|
|
89
|
+
},
|
|
90
|
+
method: "POST"
|
|
91
|
+
});
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
throw await toApiError(response, path);
|
|
94
|
+
}
|
|
95
|
+
const payload = await response.json();
|
|
96
|
+
if (payload.status !== "approved") {
|
|
97
|
+
return { status: payload.status };
|
|
98
|
+
}
|
|
99
|
+
if (!payload.tokens || !payload.user) {
|
|
100
|
+
throw new Error("Device login approval response did not include a session.");
|
|
101
|
+
}
|
|
102
|
+
const refreshToken = extractRefreshTokenFromHeaders(response.headers);
|
|
103
|
+
if (!refreshToken) {
|
|
104
|
+
throw new Error("Refresh token cookie was not returned by the Platform API.");
|
|
105
|
+
}
|
|
106
|
+
const session = {
|
|
107
|
+
accessToken: payload.tokens.accessToken,
|
|
108
|
+
accessTokenPayload: payload.tokens.accessTokenPayload,
|
|
109
|
+
activeOrg: null,
|
|
110
|
+
baseUrl: normalizedBaseUrl,
|
|
111
|
+
refreshToken,
|
|
112
|
+
refreshTokenExpiresAt: payload.tokens.refreshTokenExpiresAt,
|
|
113
|
+
user: payload.user
|
|
114
|
+
};
|
|
115
|
+
await input.sessionStore.write(session);
|
|
116
|
+
return { session, status: "approved" };
|
|
117
|
+
},
|
|
69
118
|
async refreshSession(session) {
|
|
70
119
|
const response = await fetch(joinBaseUrl(session.baseUrl, "/api/v1/auth/refresh"), {
|
|
71
120
|
body: JSON.stringify({
|
|
@@ -243,11 +292,12 @@ async function readResponseBytes(response, request) {
|
|
|
243
292
|
}
|
|
244
293
|
function createMaxBytesError(request, actualBytes, maxBytes) {
|
|
245
294
|
return request.createMaxBytesError?.({ actualBytes, maxBytes }) ?? new ApiError({
|
|
295
|
+
code: "request/too_large",
|
|
246
296
|
detail: `Response body is ${String(actualBytes)} bytes, which exceeds the limit of ${String(maxBytes)} bytes.`,
|
|
247
297
|
instance: request.path,
|
|
248
298
|
status: 413,
|
|
249
299
|
title: "Payload Too Large",
|
|
250
|
-
type: "https://errors.kora.dev/request/
|
|
300
|
+
type: "https://errors.kora.dev/request/too_large"
|
|
251
301
|
});
|
|
252
302
|
}
|
|
253
303
|
function concatBytes(chunks, totalBytes) {
|
|
@@ -271,10 +321,11 @@ async function toApiError(response, path) {
|
|
|
271
321
|
if (contentType.includes("application/json")) {
|
|
272
322
|
const rawBody = await response.text();
|
|
273
323
|
const parsed = tryParseJsonErrorBody(rawBody);
|
|
274
|
-
const code = parsed?.error?.code ?? "internal/error";
|
|
324
|
+
const code = normalizePublicErrorCode(parsed?.error?.code ?? "internal/error");
|
|
275
325
|
const rawDetail = (parsed?.error?.message ?? rawBody) || response.statusText;
|
|
276
326
|
const detail = relabelBackendText(rawDetail || response.statusText);
|
|
277
327
|
return new ApiError({
|
|
328
|
+
code,
|
|
278
329
|
detail,
|
|
279
330
|
...(parsed?.error?.details ? { details: parsed.error.details } : {}),
|
|
280
331
|
instance: path,
|
|
@@ -286,6 +337,7 @@ async function toApiError(response, path) {
|
|
|
286
337
|
}
|
|
287
338
|
const rawDetail = await response.text();
|
|
288
339
|
return new ApiError({
|
|
340
|
+
code: "internal/error",
|
|
289
341
|
detail: relabelBackendText(rawDetail),
|
|
290
342
|
instance: path,
|
|
291
343
|
...(rawDetail ? { rawDetail } : {}),
|