@kora-platform/cli 0.8.0-rc1 → 0.8.0-rc10
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 +29 -18
- package/dist/api-client.js +24 -23
- 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 +64 -51
- package/dist/commands.js +90 -48
- package/dist/error-code.d.ts +2 -0
- package/dist/error-code.js +9 -0
- package/dist/extension-commands.js +4 -4
- package/dist/files.d.ts +13 -6
- package/dist/files.js +125 -37
- package/dist/format.d.ts +1 -0
- package/dist/format.js +11 -6
- package/dist/runner.js +14 -5
- package/dist/schema-registry-data.d.ts +71 -27
- package/dist/schema-registry-data.js +87 -36
- 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/dotenv.d.ts +0 -1
- package/dist/dotenv.js +0 -26
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,13 +96,12 @@ export declare function createPlatformApiClient(input: PlatformApiClientInput):
|
|
|
84
96
|
}): Promise<{
|
|
85
97
|
variables: RuntimeVariableRecord[];
|
|
86
98
|
}>;
|
|
87
|
-
|
|
88
|
-
content: string;
|
|
99
|
+
upsertRuntimeVariable(session: CliSessionState, orgId: string, inputData: {
|
|
89
100
|
environment: string;
|
|
90
|
-
|
|
101
|
+
name: string;
|
|
102
|
+
value: string;
|
|
91
103
|
}): Promise<{
|
|
92
|
-
|
|
93
|
-
variables: RuntimeVariableRecord[];
|
|
104
|
+
variable: RuntimeVariableRecord;
|
|
94
105
|
}>;
|
|
95
106
|
listOrgSecrets(session: CliSessionState, orgId: string, inputData?: {
|
|
96
107
|
environment?: string;
|
|
@@ -224,19 +235,23 @@ export declare function createPlatformApiClient(input: PlatformApiClientInput):
|
|
|
224
235
|
deleteExtensionInstall(session: CliSessionState, orgId: string, installRef: string, inputData: {
|
|
225
236
|
environment: string;
|
|
226
237
|
}): Promise<void>;
|
|
227
|
-
listWorkflows(session: CliSessionState, orgId: string,
|
|
238
|
+
listWorkflows(session: CliSessionState, orgId: string, inputData: {
|
|
239
|
+
environment?: never;
|
|
240
|
+
live: true;
|
|
241
|
+
releaseId?: never;
|
|
242
|
+
} | ReleaseSnapshotSelectorInput): Promise<{
|
|
228
243
|
processes: ProcessDefinitionSummary[];
|
|
229
244
|
}>;
|
|
230
|
-
getWorkflow(session: CliSessionState, orgId: string, name: string,
|
|
245
|
+
getWorkflow(session: CliSessionState, orgId: string, name: string, selector: ReleaseSnapshotSelectorInput): Promise<{
|
|
231
246
|
process: ProcessDefinitionDetail;
|
|
232
247
|
}>;
|
|
233
|
-
getWorkflowVersion(session: CliSessionState, orgId: string, name: string, version: number,
|
|
248
|
+
getWorkflowVersion(session: CliSessionState, orgId: string, name: string, version: number, selector: ReleaseSnapshotSelectorInput): Promise<{
|
|
234
249
|
process: ProcessDefinitionRecord;
|
|
235
250
|
}>;
|
|
236
|
-
getWorkflowDependencies(session: CliSessionState, orgId: string, name: string, version: number,
|
|
251
|
+
getWorkflowDependencies(session: CliSessionState, orgId: string, name: string, version: number, selector: ReleaseSnapshotSelectorInput): Promise<{
|
|
237
252
|
dependencies: WorkflowDependencySummary;
|
|
238
253
|
}>;
|
|
239
|
-
getWorkflowContext(session: CliSessionState, orgId: string,
|
|
254
|
+
getWorkflowContext(session: CliSessionState, orgId: string, selector: ReleaseSnapshotSelectorInput): Promise<{
|
|
240
255
|
context: WorkflowInspectionContext;
|
|
241
256
|
}>;
|
|
242
257
|
startWorkflow(session: CliSessionState, orgId: string, name: string, inputData?: {
|
|
@@ -250,13 +265,13 @@ export declare function createPlatformApiClient(input: PlatformApiClientInput):
|
|
|
250
265
|
}): Promise<{
|
|
251
266
|
started: RuntimeControlStartProcessResult;
|
|
252
267
|
}>;
|
|
253
|
-
getOrganizationManagementContext(session: CliSessionState, orgId: string): Promise<{
|
|
268
|
+
getOrganizationManagementContext(session: CliSessionState, orgId: string, selector: ReleaseSnapshotSelectorInput): Promise<{
|
|
254
269
|
context: AuthoringOrganizationManagementContext;
|
|
255
270
|
}>;
|
|
256
|
-
getCapabilityEditor(session: CliSessionState, orgId: string, name: string): Promise<{
|
|
271
|
+
getCapabilityEditor(session: CliSessionState, orgId: string, name: string, selector: ReleaseSnapshotSelectorInput): Promise<{
|
|
257
272
|
editor: CapabilityEditorRecord;
|
|
258
273
|
}>;
|
|
259
|
-
getOperationsEditor(session: CliSessionState, orgId: string): Promise<{
|
|
274
|
+
getOperationsEditor(session: CliSessionState, orgId: string, selector: ReleaseSnapshotSelectorInput): Promise<{
|
|
260
275
|
editor: OperationsEditorRecord;
|
|
261
276
|
}>;
|
|
262
277
|
getOverview(session: CliSessionState, orgId: string): Promise<{
|
|
@@ -349,9 +364,7 @@ export declare function createPlatformApiClient(input: PlatformApiClientInput):
|
|
|
349
364
|
listEnvironments(session: CliSessionState, orgId: string): Promise<{
|
|
350
365
|
environments: EnvironmentDetailRecord[];
|
|
351
366
|
}>;
|
|
352
|
-
getEnvironment(session: CliSessionState, orgId: string, environment: string): Promise<
|
|
353
|
-
environment: EnvironmentDetailRecord;
|
|
354
|
-
}>;
|
|
367
|
+
getEnvironment(session: CliSessionState, orgId: string, environment: string): Promise<EnvironmentDetailRecord>;
|
|
355
368
|
createEnvironment(session: CliSessionState, orgId: string, inputData: {
|
|
356
369
|
copyFromEnvironmentId?: string;
|
|
357
370
|
key: string;
|
|
@@ -394,12 +407,10 @@ export declare function createPlatformApiClient(input: PlatformApiClientInput):
|
|
|
394
407
|
content: string;
|
|
395
408
|
path: string;
|
|
396
409
|
}>;
|
|
397
|
-
label?: string;
|
|
398
410
|
}): Promise<{
|
|
399
411
|
created: CreateReleaseResult;
|
|
400
412
|
}>;
|
|
401
413
|
createReleaseArchive(session: CliSessionState, orgId: string, archive: Uint8Array, inputData?: {
|
|
402
|
-
label?: string;
|
|
403
414
|
subdir?: string;
|
|
404
415
|
}): Promise<{
|
|
405
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,12 +98,12 @@ export function createPlatformApiClient(input) {
|
|
|
96
98
|
session
|
|
97
99
|
});
|
|
98
100
|
},
|
|
99
|
-
async
|
|
100
|
-
const { environment,
|
|
101
|
+
async upsertRuntimeVariable(session, orgId, inputData) {
|
|
102
|
+
const { environment, name, value } = inputData;
|
|
101
103
|
return transport.requestJson({
|
|
102
|
-
body,
|
|
103
|
-
method: "
|
|
104
|
-
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/runtime-variables
|
|
104
|
+
body: { value },
|
|
105
|
+
method: "PUT",
|
|
106
|
+
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/runtime-variables/${encodeURIComponent(name)}`, { environment }),
|
|
105
107
|
session
|
|
106
108
|
});
|
|
107
109
|
},
|
|
@@ -264,37 +266,36 @@ export function createPlatformApiClient(input) {
|
|
|
264
266
|
session
|
|
265
267
|
});
|
|
266
268
|
},
|
|
267
|
-
async listWorkflows(session, orgId,
|
|
269
|
+
async listWorkflows(session, orgId, inputData) {
|
|
268
270
|
return transport.requestJson({
|
|
269
271
|
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/processes`, {
|
|
270
|
-
|
|
272
|
+
environment: inputData.environment,
|
|
273
|
+
releaseId: inputData.releaseId
|
|
271
274
|
}),
|
|
272
275
|
session
|
|
273
276
|
});
|
|
274
277
|
},
|
|
275
|
-
async getWorkflow(session, orgId, name,
|
|
278
|
+
async getWorkflow(session, orgId, name, selector) {
|
|
276
279
|
return transport.requestJson({
|
|
277
|
-
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/processes/${encodeURIComponent(name)}`,
|
|
280
|
+
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/processes/${encodeURIComponent(name)}`, selector),
|
|
278
281
|
session
|
|
279
282
|
});
|
|
280
283
|
},
|
|
281
|
-
async getWorkflowVersion(session, orgId, name, version,
|
|
284
|
+
async getWorkflowVersion(session, orgId, name, version, selector) {
|
|
282
285
|
return transport.requestJson({
|
|
283
|
-
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),
|
|
284
287
|
session
|
|
285
288
|
});
|
|
286
289
|
},
|
|
287
|
-
async getWorkflowDependencies(session, orgId, name, version,
|
|
290
|
+
async getWorkflowDependencies(session, orgId, name, version, selector) {
|
|
288
291
|
return transport.requestJson({
|
|
289
|
-
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),
|
|
290
293
|
session
|
|
291
294
|
});
|
|
292
295
|
},
|
|
293
|
-
async getWorkflowContext(session, orgId,
|
|
296
|
+
async getWorkflowContext(session, orgId, selector) {
|
|
294
297
|
return transport.requestJson({
|
|
295
|
-
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/workflows/context`,
|
|
296
|
-
releaseId
|
|
297
|
-
}),
|
|
298
|
+
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/workflows/context`, selector),
|
|
298
299
|
session
|
|
299
300
|
});
|
|
300
301
|
},
|
|
@@ -306,21 +307,21 @@ export function createPlatformApiClient(input) {
|
|
|
306
307
|
session
|
|
307
308
|
});
|
|
308
309
|
},
|
|
309
|
-
async getOrganizationManagementContext(session, orgId) {
|
|
310
|
+
async getOrganizationManagementContext(session, orgId, selector) {
|
|
310
311
|
return transport.requestJson({
|
|
311
|
-
path: `/api/v1/orgs/${encodeURIComponent(orgId)}/organization/management`,
|
|
312
|
+
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/organization/management`, selector),
|
|
312
313
|
session
|
|
313
314
|
});
|
|
314
315
|
},
|
|
315
|
-
async getCapabilityEditor(session, orgId, name) {
|
|
316
|
+
async getCapabilityEditor(session, orgId, name, selector) {
|
|
316
317
|
return transport.requestJson({
|
|
317
|
-
path: `/api/v1/orgs/${encodeURIComponent(orgId)}/capabilities/editor/${encodeURIComponent(name)}`,
|
|
318
|
+
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/capabilities/editor/${encodeURIComponent(name)}`, selector),
|
|
318
319
|
session
|
|
319
320
|
});
|
|
320
321
|
},
|
|
321
|
-
async getOperationsEditor(session, orgId) {
|
|
322
|
+
async getOperationsEditor(session, orgId, selector) {
|
|
322
323
|
return transport.requestJson({
|
|
323
|
-
path: `/api/v1/orgs/${encodeURIComponent(orgId)}/operations/editor`,
|
|
324
|
+
path: withQuery(`/api/v1/orgs/${encodeURIComponent(orgId)}/operations/editor`, selector),
|
|
324
325
|
session
|
|
325
326
|
});
|
|
326
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;
|