@kora-platform/cli 0.8.0-rc3 → 0.8.0-rc7
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 +32 -13
- package/dist/api-client.js +28 -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-flags.d.ts +1 -0
- package/dist/command-flags.js +7 -0
- package/dist/command-registry.js +62 -39
- package/dist/commands.js +89 -28
- 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 +14 -5
- package/dist/session-store.js +80 -0
- package/dist/transport-refresh.d.ts +10 -0
- package/dist/transport-refresh.js +51 -0
- package/dist/transport.d.ts +21 -0
- package/dist/transport.js +80 -36
- package/package.json +1 -1
package/dist/api-client.d.ts
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import type { AbortRuntimeRunResult, AgentUsageReport, ApiKeyRecord, AuditEventListResult, AuditEventRecord, AuditOutcome, AuthoringOrganizationManagementContext, CapabilityEditorRecord, ChatHealth, ChatSessionRecord, ChatSessionSummary, CreateReleaseResult, CompleteRuntimeTaskResult, DeletedOrganizationDetail, DeletedOrganizationSummary, EnvironmentDeploymentRecord, EnvironmentDetailRecord, EnvironmentRecord, BuiltInExtensionRecord, ExtensionInstallDetailRecord, ExtensionInstallRecord, ExtensionInstallUpdatePlan, ExtensionPackageExportEnvelope, ExtensionPackageFileInput, ExtensionPackageRecord, ExtensionPackageRevisionRecord, ExtensionPackageValidationResult, ExtensionPermissions, InviteRecord, MembershipRecord, OperationsEditorRecord, WorkflowNodeTestResult, OrganizationRecord, OrganizationWithRoleRecord, OrgSecretRecord, OrgSettingsRecord, ProcessDefinitionDetail, ProcessDefinitionRecord, ProcessDefinitionSummary, ReleaseSourceSnapshotRecord, ReleaseValidationResult, ReleaseSummaryRecord, RuntimeControlActivityFocusType, RuntimeControlActivityReadResult, RuntimeControlOverviewRecord, RuntimeControlRunDetailRecord, RuntimeControlRunRecord, RuntimeControlRunStateRecord, RuntimeControlRunStepRecord, RuntimeControlStartProcessResult, RuntimeControlTaskRecord, RuntimeVariableRecord, WorkflowDependencySummary, WorkflowInspectionContext } from "./api-types.js";
|
|
2
2
|
import type { CliSessionState } from "./transport.js";
|
|
3
3
|
import { createPlatformTransport } from "./transport.js";
|
|
4
|
+
export type ReleaseSnapshotSelectorInput = {
|
|
5
|
+
environment: string;
|
|
6
|
+
releaseId?: never;
|
|
7
|
+
} | {
|
|
8
|
+
environment?: never;
|
|
9
|
+
releaseId: string;
|
|
10
|
+
};
|
|
4
11
|
export interface PlatformApiClientInput {
|
|
5
12
|
now?: () => number;
|
|
6
13
|
sessionStore: Parameters<typeof createPlatformTransport>[0]["sessionStore"];
|
|
7
14
|
}
|
|
8
15
|
export declare function createPlatformApiClient(input: PlatformApiClientInput): {
|
|
16
|
+
claimDeviceLogin: (claim: {
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
deviceCode: string;
|
|
19
|
+
}) => Promise<import("./transport.js").CliDeviceLoginClaim>;
|
|
9
20
|
getAuthSettings: (baseUrl: string) => Promise<import("./transport.js").CliAuthSettings>;
|
|
10
21
|
login: (credentials: {
|
|
11
22
|
baseUrl: string;
|
|
@@ -19,6 +30,7 @@ export declare function createPlatformApiClient(input: PlatformApiClientInput):
|
|
|
19
30
|
name: string;
|
|
20
31
|
password: string;
|
|
21
32
|
}) => Promise<CliSessionState>;
|
|
33
|
+
startDeviceLogin: (baseUrl: string) => Promise<import("./transport.js").CliDeviceLoginStart>;
|
|
22
34
|
getMe(session: CliSessionState): Promise<{
|
|
23
35
|
user: {
|
|
24
36
|
email: string;
|
|
@@ -84,6 +96,13 @@ export declare function createPlatformApiClient(input: PlatformApiClientInput):
|
|
|
84
96
|
}): Promise<{
|
|
85
97
|
variables: RuntimeVariableRecord[];
|
|
86
98
|
}>;
|
|
99
|
+
upsertRuntimeVariable(session: CliSessionState, orgId: string, inputData: {
|
|
100
|
+
environment: string;
|
|
101
|
+
name: string;
|
|
102
|
+
value: string;
|
|
103
|
+
}): Promise<{
|
|
104
|
+
variable: RuntimeVariableRecord;
|
|
105
|
+
}>;
|
|
87
106
|
listOrgSecrets(session: CliSessionState, orgId: string, inputData?: {
|
|
88
107
|
environment?: string;
|
|
89
108
|
}): Promise<{
|
|
@@ -216,19 +235,23 @@ export declare function createPlatformApiClient(input: PlatformApiClientInput):
|
|
|
216
235
|
deleteExtensionInstall(session: CliSessionState, orgId: string, installRef: string, inputData: {
|
|
217
236
|
environment: string;
|
|
218
237
|
}): Promise<void>;
|
|
219
|
-
listWorkflows(session: CliSessionState, orgId: string,
|
|
238
|
+
listWorkflows(session: CliSessionState, orgId: string, inputData: {
|
|
239
|
+
environment?: never;
|
|
240
|
+
live: true;
|
|
241
|
+
releaseId?: never;
|
|
242
|
+
} | ReleaseSnapshotSelectorInput): Promise<{
|
|
220
243
|
processes: ProcessDefinitionSummary[];
|
|
221
244
|
}>;
|
|
222
|
-
getWorkflow(session: CliSessionState, orgId: string, name: string,
|
|
245
|
+
getWorkflow(session: CliSessionState, orgId: string, name: string, selector: ReleaseSnapshotSelectorInput): Promise<{
|
|
223
246
|
process: ProcessDefinitionDetail;
|
|
224
247
|
}>;
|
|
225
|
-
getWorkflowVersion(session: CliSessionState, orgId: string, name: string, version: number,
|
|
248
|
+
getWorkflowVersion(session: CliSessionState, orgId: string, name: string, version: number, selector: ReleaseSnapshotSelectorInput): Promise<{
|
|
226
249
|
process: ProcessDefinitionRecord;
|
|
227
250
|
}>;
|
|
228
|
-
getWorkflowDependencies(session: CliSessionState, orgId: string, name: string, version: number,
|
|
251
|
+
getWorkflowDependencies(session: CliSessionState, orgId: string, name: string, version: number, selector: ReleaseSnapshotSelectorInput): Promise<{
|
|
229
252
|
dependencies: WorkflowDependencySummary;
|
|
230
253
|
}>;
|
|
231
|
-
getWorkflowContext(session: CliSessionState, orgId: string,
|
|
254
|
+
getWorkflowContext(session: CliSessionState, orgId: string, selector: ReleaseSnapshotSelectorInput): Promise<{
|
|
232
255
|
context: WorkflowInspectionContext;
|
|
233
256
|
}>;
|
|
234
257
|
startWorkflow(session: CliSessionState, orgId: string, name: string, inputData?: {
|
|
@@ -242,13 +265,13 @@ export declare function createPlatformApiClient(input: PlatformApiClientInput):
|
|
|
242
265
|
}): Promise<{
|
|
243
266
|
started: RuntimeControlStartProcessResult;
|
|
244
267
|
}>;
|
|
245
|
-
getOrganizationManagementContext(session: CliSessionState, orgId: string): Promise<{
|
|
268
|
+
getOrganizationManagementContext(session: CliSessionState, orgId: string, selector: ReleaseSnapshotSelectorInput): Promise<{
|
|
246
269
|
context: AuthoringOrganizationManagementContext;
|
|
247
270
|
}>;
|
|
248
|
-
getCapabilityEditor(session: CliSessionState, orgId: string, name: string): Promise<{
|
|
271
|
+
getCapabilityEditor(session: CliSessionState, orgId: string, name: string, selector: ReleaseSnapshotSelectorInput): Promise<{
|
|
249
272
|
editor: CapabilityEditorRecord;
|
|
250
273
|
}>;
|
|
251
|
-
getOperationsEditor(session: CliSessionState, orgId: string): Promise<{
|
|
274
|
+
getOperationsEditor(session: CliSessionState, orgId: string, selector: ReleaseSnapshotSelectorInput): Promise<{
|
|
252
275
|
editor: OperationsEditorRecord;
|
|
253
276
|
}>;
|
|
254
277
|
getOverview(session: CliSessionState, orgId: string): Promise<{
|
|
@@ -341,9 +364,7 @@ export declare function createPlatformApiClient(input: PlatformApiClientInput):
|
|
|
341
364
|
listEnvironments(session: CliSessionState, orgId: string): Promise<{
|
|
342
365
|
environments: EnvironmentDetailRecord[];
|
|
343
366
|
}>;
|
|
344
|
-
getEnvironment(session: CliSessionState, orgId: string, environment: string): Promise<
|
|
345
|
-
environment: EnvironmentDetailRecord;
|
|
346
|
-
}>;
|
|
367
|
+
getEnvironment(session: CliSessionState, orgId: string, environment: string): Promise<EnvironmentDetailRecord>;
|
|
347
368
|
createEnvironment(session: CliSessionState, orgId: string, inputData: {
|
|
348
369
|
copyFromEnvironmentId?: string;
|
|
349
370
|
key: string;
|
|
@@ -386,12 +407,10 @@ export declare function createPlatformApiClient(input: PlatformApiClientInput):
|
|
|
386
407
|
content: string;
|
|
387
408
|
path: string;
|
|
388
409
|
}>;
|
|
389
|
-
label?: string;
|
|
390
410
|
}): Promise<{
|
|
391
411
|
created: CreateReleaseResult;
|
|
392
412
|
}>;
|
|
393
413
|
createReleaseArchive(session: CliSessionState, orgId: string, archive: Uint8Array, inputData?: {
|
|
394
|
-
label?: string;
|
|
395
414
|
subdir?: string;
|
|
396
415
|
}): Promise<{
|
|
397
416
|
created: CreateReleaseResult;
|
package/dist/api-client.js
CHANGED
|
@@ -5,10 +5,12 @@ export function createPlatformApiClient(input) {
|
|
|
5
5
|
const artifactApi = createArtifactApiClient(transport);
|
|
6
6
|
return {
|
|
7
7
|
...artifactApi,
|
|
8
|
+
claimDeviceLogin: transport.claimDeviceLogin,
|
|
8
9
|
getAuthSettings: transport.getAuthSettings,
|
|
9
10
|
login: transport.login,
|
|
10
11
|
refreshSession: transport.refreshSession,
|
|
11
12
|
signup: transport.signup,
|
|
13
|
+
startDeviceLogin: transport.startDeviceLogin,
|
|
12
14
|
async getMe(session) {
|
|
13
15
|
return transport.requestJson({
|
|
14
16
|
path: "/api/v1/auth/me",
|
|
@@ -96,6 +98,15 @@ export function createPlatformApiClient(input) {
|
|
|
96
98
|
session
|
|
97
99
|
});
|
|
98
100
|
},
|
|
101
|
+
async upsertRuntimeVariable(session, orgId, inputData) {
|
|
102
|
+
const { environment, name, value } = inputData;
|
|
103
|
+
return transport.requestJson({
|
|
104
|
+
body: { value },
|
|
105
|
+
method: "PUT",
|
|
106
|
+
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/runtime-variables/${encodeURIComponent(name)}`, { environment }),
|
|
107
|
+
session
|
|
108
|
+
});
|
|
109
|
+
},
|
|
99
110
|
async listOrgSecrets(session, orgId, inputData = {}) {
|
|
100
111
|
return transport.requestJson({
|
|
101
112
|
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/secrets`, inputData),
|
|
@@ -255,37 +266,36 @@ export function createPlatformApiClient(input) {
|
|
|
255
266
|
session
|
|
256
267
|
});
|
|
257
268
|
},
|
|
258
|
-
async listWorkflows(session, orgId,
|
|
269
|
+
async listWorkflows(session, orgId, inputData) {
|
|
259
270
|
return transport.requestJson({
|
|
260
271
|
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/processes`, {
|
|
261
|
-
|
|
272
|
+
environment: inputData.environment,
|
|
273
|
+
releaseId: inputData.releaseId
|
|
262
274
|
}),
|
|
263
275
|
session
|
|
264
276
|
});
|
|
265
277
|
},
|
|
266
|
-
async getWorkflow(session, orgId, name,
|
|
278
|
+
async getWorkflow(session, orgId, name, selector) {
|
|
267
279
|
return transport.requestJson({
|
|
268
|
-
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/processes/${encodeURIComponent(name)}`,
|
|
280
|
+
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/processes/${encodeURIComponent(name)}`, selector),
|
|
269
281
|
session
|
|
270
282
|
});
|
|
271
283
|
},
|
|
272
|
-
async getWorkflowVersion(session, orgId, name, version,
|
|
284
|
+
async getWorkflowVersion(session, orgId, name, version, selector) {
|
|
273
285
|
return transport.requestJson({
|
|
274
|
-
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/processes/${encodeURIComponent(name)}/versions/${String(version)}`,
|
|
286
|
+
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/processes/${encodeURIComponent(name)}/versions/${String(version)}`, selector),
|
|
275
287
|
session
|
|
276
288
|
});
|
|
277
289
|
},
|
|
278
|
-
async getWorkflowDependencies(session, orgId, name, version,
|
|
290
|
+
async getWorkflowDependencies(session, orgId, name, version, selector) {
|
|
279
291
|
return transport.requestJson({
|
|
280
|
-
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/processes/${encodeURIComponent(name)}/versions/${String(version)}/dependencies`,
|
|
292
|
+
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/processes/${encodeURIComponent(name)}/versions/${String(version)}/dependencies`, selector),
|
|
281
293
|
session
|
|
282
294
|
});
|
|
283
295
|
},
|
|
284
|
-
async getWorkflowContext(session, orgId,
|
|
296
|
+
async getWorkflowContext(session, orgId, selector) {
|
|
285
297
|
return transport.requestJson({
|
|
286
|
-
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/workflows/context`,
|
|
287
|
-
releaseId
|
|
288
|
-
}),
|
|
298
|
+
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/workflows/context`, selector),
|
|
289
299
|
session
|
|
290
300
|
});
|
|
291
301
|
},
|
|
@@ -297,21 +307,21 @@ export function createPlatformApiClient(input) {
|
|
|
297
307
|
session
|
|
298
308
|
});
|
|
299
309
|
},
|
|
300
|
-
async getOrganizationManagementContext(session, orgId) {
|
|
310
|
+
async getOrganizationManagementContext(session, orgId, selector) {
|
|
301
311
|
return transport.requestJson({
|
|
302
|
-
path: `/api/v1/orgs/${encodeURIComponent(orgId)}/organization/management`,
|
|
312
|
+
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/organization/management`, selector),
|
|
303
313
|
session
|
|
304
314
|
});
|
|
305
315
|
},
|
|
306
|
-
async getCapabilityEditor(session, orgId, name) {
|
|
316
|
+
async getCapabilityEditor(session, orgId, name, selector) {
|
|
307
317
|
return transport.requestJson({
|
|
308
|
-
path: `/api/v1/orgs/${encodeURIComponent(orgId)}/capabilities/editor/${encodeURIComponent(name)}`,
|
|
318
|
+
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/capabilities/editor/${encodeURIComponent(name)}`, selector),
|
|
309
319
|
session
|
|
310
320
|
});
|
|
311
321
|
},
|
|
312
|
-
async getOperationsEditor(session, orgId) {
|
|
322
|
+
async getOperationsEditor(session, orgId, selector) {
|
|
313
323
|
return transport.requestJson({
|
|
314
|
-
path: `/api/v1/orgs/${encodeURIComponent(orgId)}/operations/editor`,
|
|
324
|
+
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/operations/editor`, selector),
|
|
315
325
|
session
|
|
316
326
|
});
|
|
317
327
|
},
|
package/dist/api-types.d.ts
CHANGED
|
@@ -290,6 +290,13 @@ export interface AgentUsageReport extends CliRecord {
|
|
|
290
290
|
}
|
|
291
291
|
export interface RuntimeControlDeploymentRecord extends CliRecord {
|
|
292
292
|
}
|
|
293
|
+
export interface WorkflowFailureSummary extends CliRecord {
|
|
294
|
+
causeChain: string[];
|
|
295
|
+
rootCause: string;
|
|
296
|
+
rootCauseCode?: string;
|
|
297
|
+
summary: string;
|
|
298
|
+
summaryCode?: string;
|
|
299
|
+
}
|
|
293
300
|
export interface EnvironmentRecord extends CliRecord {
|
|
294
301
|
currentDeploymentId?: string | null;
|
|
295
302
|
id: string;
|
|
@@ -319,9 +326,22 @@ export interface EnvironmentDetailRecord extends CliRecord {
|
|
|
319
326
|
}
|
|
320
327
|
export interface RuntimeControlOverviewRecord extends CliRecord {
|
|
321
328
|
}
|
|
322
|
-
export interface RuntimeControlRunDetailRecord extends CliRecord {
|
|
323
|
-
}
|
|
324
329
|
export interface RuntimeControlRunRecord extends CliRecord {
|
|
330
|
+
closeTime?: string | null;
|
|
331
|
+
deploymentId?: string | null;
|
|
332
|
+
environmentKey?: string | null;
|
|
333
|
+
failure?: WorkflowFailureSummary;
|
|
334
|
+
releaseId?: string | null;
|
|
335
|
+
startTime?: string;
|
|
336
|
+
status: string;
|
|
337
|
+
workflowId: string;
|
|
338
|
+
}
|
|
339
|
+
export interface RuntimeControlRunDetailRecord extends RuntimeControlRunRecord {
|
|
340
|
+
failureLookupError?: string;
|
|
341
|
+
historyLength?: number;
|
|
342
|
+
historySizeBytes?: number;
|
|
343
|
+
state?: CliRecord | null;
|
|
344
|
+
stateQueryError?: string;
|
|
325
345
|
}
|
|
326
346
|
export interface RuntimeControlRunStateRecord extends CliRecord {
|
|
327
347
|
}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
2
|
import { basename, resolve } from "node:path";
|
|
3
3
|
import { TextDecoder } from "node:util";
|
|
4
4
|
import { genericProblem, usageProblem } from "./cli-errors.js";
|
|
5
5
|
import { readOptionalNumberFlag, readOptionalStringFlag, readRequiredStringFlag } from "./command-flags.js";
|
|
6
6
|
import { renderKeyValue, renderSuccess, renderTable } from "./format.js";
|
|
7
|
+
import { readLocalFileBytes } from "./files.js";
|
|
7
8
|
import { confirmDestructive } from "./interaction.js";
|
|
8
9
|
export async function executeArtifactUpload(parsed, context, api, resolveOrgScope) {
|
|
9
10
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
10
11
|
const pathValue = readRequiredArg(parsed, "path");
|
|
11
|
-
const filePath =
|
|
12
|
-
|
|
12
|
+
const { absolutePath: filePath, bytes: fileBytes } = await readLocalFileBytes(pathValue, parsed.definition.id, {
|
|
13
|
+
regularFileMessage: "Artifact upload path must be a regular file, not a directory or symbolic link."
|
|
14
|
+
});
|
|
13
15
|
const data = await api.uploadArtifact(session, org.id, {
|
|
14
16
|
body: new Uint8Array(fileBytes),
|
|
15
17
|
...(readOptionalStringFlag(parsed, "sha256") ? { expectedSha256: readOptionalStringFlag(parsed, "sha256") } : {}),
|
package/dist/auth-commands.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { authProblem, toProblem } from "./cli-errors.js";
|
|
1
|
+
import { authProblem, toProblem, usageProblem } from "./cli-errors.js";
|
|
2
2
|
import { readOptionalStringFlag } from "./command-flags.js";
|
|
3
3
|
import { writeCliConfig, readCliConfig, findRepoConfigPath } from "./config.js";
|
|
4
4
|
import { renderKeyValue, renderSuccess } from "./format.js";
|
|
5
5
|
import { chooseOrganization, ensureInteractive } from "./interaction.js";
|
|
6
|
-
import { promptPassword, promptText } from "./prompts.js";
|
|
6
|
+
import { isInteractive, promptPassword, promptText } from "./prompts.js";
|
|
7
7
|
export async function executeAuthLogin(parsed, context, api) {
|
|
8
8
|
const prompts = parsed.json
|
|
9
9
|
? {
|
|
@@ -11,6 +11,9 @@ export async function executeAuthLogin(parsed, context, api) {
|
|
|
11
11
|
stdout: context.stderr
|
|
12
12
|
}
|
|
13
13
|
: context;
|
|
14
|
+
if (parsed.flags.device === true || !isInteractive(prompts)) {
|
|
15
|
+
return executeDeviceLogin(parsed, context, api, prompts);
|
|
16
|
+
}
|
|
14
17
|
ensureInteractive(prompts, "auth login");
|
|
15
18
|
const resolvedBaseUrl = await resolveAuthBaseUrl(parsed, context, prompts);
|
|
16
19
|
const { baseUrl } = resolvedBaseUrl;
|
|
@@ -25,8 +28,8 @@ export async function executeAuthLogin(parsed, context, api) {
|
|
|
25
28
|
},
|
|
26
29
|
human: [
|
|
27
30
|
"SSO is required for this deployment.",
|
|
28
|
-
`
|
|
29
|
-
|
|
31
|
+
"Run `kora auth login --device` to approve this login in the browser instead.",
|
|
32
|
+
`SSO sign-in URL: ${oidcLoginUrl}`
|
|
30
33
|
].join("\n"),
|
|
31
34
|
kind: "access_auth_login_sso_required",
|
|
32
35
|
meta: {
|
|
@@ -90,8 +93,8 @@ export async function executeAuthSignup(parsed, context, api) {
|
|
|
90
93
|
},
|
|
91
94
|
human: [
|
|
92
95
|
"Local account signup is disabled for this deployment.",
|
|
93
|
-
`
|
|
94
|
-
"
|
|
96
|
+
`Account creation is managed by SSO. Sign in once in a browser: ${oidcLoginUrl}`,
|
|
97
|
+
"Then run `kora auth login --device` to approve a CLI login in the browser."
|
|
95
98
|
].join("\n"),
|
|
96
99
|
kind: "access_auth_signup_sso_required",
|
|
97
100
|
meta: {
|
|
@@ -136,13 +139,86 @@ export async function executeAuthSignup(parsed, context, api) {
|
|
|
136
139
|
}
|
|
137
140
|
};
|
|
138
141
|
}
|
|
139
|
-
async function
|
|
142
|
+
async function executeDeviceLogin(parsed, context, api, prompts) {
|
|
143
|
+
const resolvedBaseUrl = await resolveAuthBaseUrl(parsed, context, prompts, {
|
|
144
|
+
allowPrompt: isInteractive(prompts)
|
|
145
|
+
});
|
|
146
|
+
const { baseUrl } = resolvedBaseUrl;
|
|
147
|
+
const started = await api.startDeviceLogin(baseUrl);
|
|
148
|
+
const verificationUrl = buildDeviceVerificationUrl(baseUrl, started.verificationPath, started.userCode);
|
|
149
|
+
prompts.stdout.write(`Open this URL in a browser to approve the login:\n\n ${verificationUrl}\n\n`);
|
|
150
|
+
prompts.stdout.write(`Confirmation code: ${started.userCode}\n`);
|
|
151
|
+
prompts.stdout.write("Waiting for browser approval...\n");
|
|
152
|
+
const expiresAtMs = Date.parse(started.expiresAt);
|
|
153
|
+
const pollIntervalMs = Math.max(0, started.pollIntervalSeconds * 1000);
|
|
154
|
+
let session = null;
|
|
155
|
+
while (Date.now() < expiresAtMs) {
|
|
156
|
+
const claim = await api.claimDeviceLogin({ baseUrl, deviceCode: started.deviceCode });
|
|
157
|
+
if (claim.status === "approved") {
|
|
158
|
+
session = claim.session;
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
if (claim.status === "denied") {
|
|
162
|
+
throw authProblem("Device login was denied in the browser.", "auth login");
|
|
163
|
+
}
|
|
164
|
+
if (claim.status === "expired") {
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
await sleep(pollIntervalMs);
|
|
168
|
+
}
|
|
169
|
+
if (!session) {
|
|
170
|
+
throw authProblem("Device login expired before it was approved. Run `kora auth login` again.", "auth login");
|
|
171
|
+
}
|
|
172
|
+
const nextSession = await writeSessionWithSelectedOrg({
|
|
173
|
+
allowInteractiveOrgChoice: isInteractive(prompts),
|
|
174
|
+
api,
|
|
175
|
+
context,
|
|
176
|
+
parsed,
|
|
177
|
+
prompts,
|
|
178
|
+
session
|
|
179
|
+
});
|
|
180
|
+
if (resolvedBaseUrl.shouldPersistGlobalBaseUrl) {
|
|
181
|
+
await writeCliConfig(context.configPath, {
|
|
182
|
+
...resolvedBaseUrl.persistedGlobalConfig,
|
|
183
|
+
baseUrl
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
data: {
|
|
188
|
+
activeOrg: nextSession.activeOrg,
|
|
189
|
+
method: "device",
|
|
190
|
+
user: nextSession.user
|
|
191
|
+
},
|
|
192
|
+
human: renderSuccess(nextSession.activeOrg
|
|
193
|
+
? `Logged in as ${nextSession.user.email}. Active org: ${nextSession.activeOrg.slug}.`
|
|
194
|
+
: `Logged in as ${nextSession.user.email}. No active organization selected; use \`kora org select <org>\` or \`kora org create\`.`),
|
|
195
|
+
kind: "access_auth_login",
|
|
196
|
+
meta: {
|
|
197
|
+
command: "auth login",
|
|
198
|
+
orgId: nextSession.activeOrg?.id ?? null
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function buildDeviceVerificationUrl(baseUrl, verificationPath, userCode) {
|
|
203
|
+
const normalizedBaseUrl = baseUrl.replace(/\/+$/u, "");
|
|
204
|
+
const normalizedPath = verificationPath.startsWith("/") ? verificationPath : `/${verificationPath}`;
|
|
205
|
+
return `${normalizedBaseUrl}${normalizedPath}?code=${encodeURIComponent(userCode)}`;
|
|
206
|
+
}
|
|
207
|
+
function sleep(ms) {
|
|
208
|
+
return new Promise((resolve) => {
|
|
209
|
+
setTimeout(resolve, ms);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
async function resolveAuthBaseUrl(parsed, context, prompts, options = { allowPrompt: true }) {
|
|
140
213
|
const persistedGlobalConfig = await readCliConfig(context.configPath);
|
|
141
214
|
const repoConfigPath = await findRepoConfigPath(context.cwd);
|
|
142
215
|
const repoConfig = repoConfigPath ? await readCliConfig(repoConfigPath) : {};
|
|
143
216
|
const flagBaseUrl = readOptionalStringFlag(parsed, "base-url");
|
|
144
217
|
const envBaseUrl = context.env.KORA_BASE_URL;
|
|
145
218
|
const configuredBaseUrl = flagBaseUrl ?? envBaseUrl ?? repoConfig.baseUrl ?? persistedGlobalConfig.baseUrl;
|
|
219
|
+
if (!configuredBaseUrl && !options.allowPrompt) {
|
|
220
|
+
throw usageProblem("Pass --base-url <url> or set KORA_BASE_URL to log in from a non-interactive shell.", "auth login");
|
|
221
|
+
}
|
|
146
222
|
const baseUrl = configuredBaseUrl ?? await promptText("Platform base URL: ", prompts);
|
|
147
223
|
return {
|
|
148
224
|
baseUrl,
|
|
@@ -154,7 +230,7 @@ async function writeSessionWithSelectedOrg(input) {
|
|
|
154
230
|
const organizations = (await input.api.listOrganizations(input.session)).organizations;
|
|
155
231
|
const activeOrg = organizations.length === 1
|
|
156
232
|
? organizations[0] ?? null
|
|
157
|
-
: organizations.length > 1
|
|
233
|
+
: organizations.length > 1 && (input.allowInteractiveOrgChoice ?? true)
|
|
158
234
|
? await chooseOrganization(organizations, input.prompts, input.parsed)
|
|
159
235
|
: null;
|
|
160
236
|
const latestSession = await readLatestSessionForWrite(input.context.sessionStore, input.session);
|
package/dist/cli-errors.d.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { ApiError } from "./transport.js";
|
|
2
|
+
type CliProblemDetails = Record<string, unknown>;
|
|
2
3
|
export declare class CliProblem extends Error {
|
|
4
|
+
readonly code: string;
|
|
3
5
|
readonly detail: string;
|
|
6
|
+
readonly details?: CliProblemDetails;
|
|
4
7
|
readonly exitCode: number;
|
|
5
8
|
readonly instance: string;
|
|
6
9
|
readonly status: number;
|
|
7
10
|
readonly title: string;
|
|
8
11
|
readonly type: string;
|
|
9
12
|
constructor(input: {
|
|
13
|
+
code: string;
|
|
10
14
|
detail: string;
|
|
15
|
+
details?: CliProblemDetails;
|
|
11
16
|
exitCode: number;
|
|
12
17
|
instance: string;
|
|
13
18
|
status: number;
|
|
@@ -15,9 +20,10 @@ export declare class CliProblem extends Error {
|
|
|
15
20
|
type: string;
|
|
16
21
|
});
|
|
17
22
|
}
|
|
18
|
-
export declare function usageProblem(detail: string, instance: string): CliProblem;
|
|
23
|
+
export declare function usageProblem(detail: string, instance: string, details?: CliProblemDetails): CliProblem;
|
|
19
24
|
export declare function authProblem(detail: string, instance: string): CliProblem;
|
|
20
25
|
export declare function notFoundProblem(detail: string, instance: string): CliProblem;
|
|
21
26
|
export declare function genericProblem(detail: string, instance: string): CliProblem;
|
|
22
27
|
export declare function toProblem(error: unknown, instance: string): CliProblem | ApiError;
|
|
23
28
|
export declare function exitCodeFromError(error: CliProblem | ApiError): number;
|
|
29
|
+
export {};
|
package/dist/cli-errors.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ApiError } from "./transport.js";
|
|
2
2
|
export class CliProblem extends Error {
|
|
3
|
+
code;
|
|
3
4
|
detail;
|
|
5
|
+
details;
|
|
4
6
|
exitCode;
|
|
5
7
|
instance;
|
|
6
8
|
status;
|
|
@@ -9,7 +11,11 @@ export class CliProblem extends Error {
|
|
|
9
11
|
constructor(input) {
|
|
10
12
|
super(input.detail);
|
|
11
13
|
this.name = "CliProblem";
|
|
14
|
+
this.code = input.code;
|
|
12
15
|
this.detail = input.detail;
|
|
16
|
+
if (input.details) {
|
|
17
|
+
this.details = input.details;
|
|
18
|
+
}
|
|
13
19
|
this.exitCode = input.exitCode;
|
|
14
20
|
this.instance = input.instance;
|
|
15
21
|
this.status = input.status;
|
|
@@ -17,9 +23,11 @@ export class CliProblem extends Error {
|
|
|
17
23
|
this.type = input.type;
|
|
18
24
|
}
|
|
19
25
|
}
|
|
20
|
-
export function usageProblem(detail, instance) {
|
|
26
|
+
export function usageProblem(detail, instance, details) {
|
|
21
27
|
return new CliProblem({
|
|
28
|
+
code: "cli/usage",
|
|
22
29
|
detail,
|
|
30
|
+
...(details ? { details } : {}),
|
|
23
31
|
exitCode: 2,
|
|
24
32
|
instance,
|
|
25
33
|
status: 400,
|
|
@@ -29,6 +37,7 @@ export function usageProblem(detail, instance) {
|
|
|
29
37
|
}
|
|
30
38
|
export function authProblem(detail, instance) {
|
|
31
39
|
return new CliProblem({
|
|
40
|
+
code: "cli/auth",
|
|
32
41
|
detail,
|
|
33
42
|
exitCode: 3,
|
|
34
43
|
instance,
|
|
@@ -39,6 +48,7 @@ export function authProblem(detail, instance) {
|
|
|
39
48
|
}
|
|
40
49
|
export function notFoundProblem(detail, instance) {
|
|
41
50
|
return new CliProblem({
|
|
51
|
+
code: "cli/not_found",
|
|
42
52
|
detail,
|
|
43
53
|
exitCode: 4,
|
|
44
54
|
instance,
|
|
@@ -49,6 +59,7 @@ export function notFoundProblem(detail, instance) {
|
|
|
49
59
|
}
|
|
50
60
|
export function genericProblem(detail, instance) {
|
|
51
61
|
return new CliProblem({
|
|
62
|
+
code: "cli/error",
|
|
52
63
|
detail,
|
|
53
64
|
exitCode: 1,
|
|
54
65
|
instance,
|
package/dist/command-flags.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ParsedCommand } from "./runner.js";
|
|
2
2
|
export declare function readOptionalStringFlag(parsed: ParsedCommand, name: string): string | undefined;
|
|
3
3
|
export declare function readRequiredStringFlag(parsed: ParsedCommand, name: string): string;
|
|
4
|
+
export declare function readRequiredStringFlagPreservingEmpty(parsed: ParsedCommand, name: string): string;
|
|
4
5
|
export declare function readOptionalNumberFlag(parsed: ParsedCommand, name: string): number | undefined;
|
package/dist/command-flags.js
CHANGED
|
@@ -10,6 +10,13 @@ export function readRequiredStringFlag(parsed, name) {
|
|
|
10
10
|
}
|
|
11
11
|
return value;
|
|
12
12
|
}
|
|
13
|
+
export function readRequiredStringFlagPreservingEmpty(parsed, name) {
|
|
14
|
+
const value = parsed.flags[name];
|
|
15
|
+
if (typeof value !== "string") {
|
|
16
|
+
throw usageProblem(`Missing required flag --${name}.`, parsed.definition.id);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
13
20
|
export function readOptionalNumberFlag(parsed, name) {
|
|
14
21
|
const value = parsed.flags[name];
|
|
15
22
|
return typeof value === "number" ? value : undefined;
|