@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/command-registry.js
CHANGED
|
@@ -31,6 +31,12 @@ export const CLI_ALIASES = Object.freeze({
|
|
|
31
31
|
process: ["workflow"],
|
|
32
32
|
project: ["org"],
|
|
33
33
|
});
|
|
34
|
+
const releaseReadFlag = (description, required = false) => flag("release", description, { acceptsValue: true, ...(required ? { required: true } : {}), valueType: "string" });
|
|
35
|
+
const releaseEnvironmentReadFlag = (description) => flag("environment", description, { acceptsValue: true, valueType: "string" });
|
|
36
|
+
const releaseSnapshotReadFlags = (releaseDescription, environmentDescription) => [
|
|
37
|
+
releaseReadFlag(releaseDescription),
|
|
38
|
+
releaseEnvironmentReadFlag(environmentDescription)
|
|
39
|
+
];
|
|
34
40
|
const RAW_CLI_COMMANDS = [
|
|
35
41
|
command(["help"], "Show human or machine-readable help for a command path.", {
|
|
36
42
|
labels: ["read", "chat-read", "help"],
|
|
@@ -64,13 +70,14 @@ const RAW_CLI_COMMANDS = [
|
|
|
64
70
|
command(["auth", "whoami"], "Show the current authenticated user and selected organization.", {
|
|
65
71
|
labels: ["local", "auth"]
|
|
66
72
|
}),
|
|
67
|
-
command(["auth", "login"], "
|
|
73
|
+
command(["auth", "login"], "Log in and select the active organization. Uses browser device approval with --device or when the terminal is not interactive.", {
|
|
68
74
|
labels: ["credential", "auth"],
|
|
69
75
|
flags: [
|
|
70
76
|
flag("base-url", "Platform base URL for this login.", {
|
|
71
77
|
acceptsValue: true,
|
|
72
78
|
valueType: "string"
|
|
73
|
-
})
|
|
79
|
+
}),
|
|
80
|
+
flag("device", "Use the browser device-approval flow instead of interactive email/password login.")
|
|
74
81
|
]
|
|
75
82
|
}),
|
|
76
83
|
command(["auth", "signup"], "Create a local account and start a CLI session.", {
|
|
@@ -182,52 +189,37 @@ const RAW_CLI_COMMANDS = [
|
|
|
182
189
|
labels: ["read", "activity", "chat-read"],
|
|
183
190
|
requiresActiveOrg: true
|
|
184
191
|
}),
|
|
185
|
-
command(["workflow", "list"], "List workflows.", {
|
|
186
|
-
labels: ["read", "workflow"],
|
|
187
|
-
|
|
188
|
-
flags:
|
|
189
|
-
acceptsValue: true,
|
|
190
|
-
valueType: "string"
|
|
191
|
-
})],
|
|
192
|
+
command(["workflow", "list"], "List live deployed workflows, filter live deployments with --environment, or list workflows from a release with --release.", {
|
|
193
|
+
labels: ["read", "workflow"], aliases: [["process", "list"]],
|
|
194
|
+
examples: ["kora workflow list", "kora workflow list --environment <environment>", "kora workflow list --release <release>"],
|
|
195
|
+
flags: releaseSnapshotReadFlags("Read workflows from an immutable release instead of live deployments.", "Filter live deployed workflows to one environment's live deployment."),
|
|
192
196
|
requiresActiveOrg: true
|
|
193
197
|
}),
|
|
194
198
|
command(["workflow", "get"], "Show workflow detail.", {
|
|
195
199
|
labels: ["read", "workflow"],
|
|
196
200
|
aliases: [["process", "get"]],
|
|
197
201
|
args: [arg("name", "Workflow name.")],
|
|
198
|
-
flags:
|
|
199
|
-
acceptsValue: true,
|
|
200
|
-
valueType: "string"
|
|
201
|
-
})],
|
|
202
|
+
flags: releaseSnapshotReadFlags("Read workflow detail from a release.", "Read workflow detail from an environment's live deployment release."),
|
|
202
203
|
requiresActiveOrg: true
|
|
203
204
|
}),
|
|
204
205
|
command(["workflow", "version", "get"], "Show one workflow version.", {
|
|
205
206
|
labels: ["read", "workflow"],
|
|
206
207
|
aliases: [["process", "version", "get"]],
|
|
207
208
|
args: [arg("name", "Workflow name."), arg("version", "Workflow version.")],
|
|
208
|
-
flags:
|
|
209
|
-
acceptsValue: true,
|
|
210
|
-
valueType: "string"
|
|
211
|
-
})],
|
|
209
|
+
flags: releaseSnapshotReadFlags("Read workflow version from a release.", "Read workflow version from an environment's live deployment release."),
|
|
212
210
|
requiresActiveOrg: true
|
|
213
211
|
}),
|
|
214
212
|
command(["workflow", "dependencies"], "Show workflow dependency detail.", {
|
|
215
213
|
labels: ["read", "workflow"],
|
|
216
214
|
aliases: [["process", "dependencies"]],
|
|
217
215
|
args: [arg("name", "Workflow name."), arg("version", "Workflow version.")],
|
|
218
|
-
flags:
|
|
219
|
-
acceptsValue: true,
|
|
220
|
-
valueType: "string"
|
|
221
|
-
})],
|
|
216
|
+
flags: releaseSnapshotReadFlags("Read workflow dependencies from a release.", "Read workflow dependencies from an environment's live deployment release."),
|
|
222
217
|
requiresActiveOrg: true
|
|
223
218
|
}),
|
|
224
219
|
command(["workflow", "context"], "Show workflow inspection context.", {
|
|
225
220
|
labels: ["read", "workflow"],
|
|
226
221
|
aliases: [["process", "context"]],
|
|
227
|
-
flags:
|
|
228
|
-
acceptsValue: true,
|
|
229
|
-
valueType: "string"
|
|
230
|
-
})],
|
|
222
|
+
flags: releaseSnapshotReadFlags("Read workflow context from a release.", "Read workflow context from an environment's live deployment release."),
|
|
231
223
|
requiresActiveOrg: true
|
|
232
224
|
}),
|
|
233
225
|
command(["workflow", "start"], "Start a workflow.", {
|
|
@@ -294,10 +286,7 @@ const RAW_CLI_COMMANDS = [
|
|
|
294
286
|
command(["release", "create"], "Create an immutable release artifact from source files; this mutates Platform state and is not a test command.", {
|
|
295
287
|
labels: ["write", "chat-write", "release"],
|
|
296
288
|
args: [arg("workspace", "Path to the source folder or zip archive.")],
|
|
297
|
-
flags: [
|
|
298
|
-
acceptsValue: true,
|
|
299
|
-
valueType: "string"
|
|
300
|
-
})],
|
|
289
|
+
flags: [],
|
|
301
290
|
requiresActiveOrg: true
|
|
302
291
|
}),
|
|
303
292
|
command(["release", "validate"], "Validate release readiness without deploying.", {
|
|
@@ -701,51 +690,68 @@ const RAW_CLI_COMMANDS = [
|
|
|
701
690
|
requiresActiveOrg: true
|
|
702
691
|
}),
|
|
703
692
|
command(["org-model", "people", "list"], "List modeled people.", {
|
|
693
|
+
flags: releaseSnapshotReadFlags("Read modeled people from a release.", "Read modeled people from an environment's live deployment release."),
|
|
704
694
|
labels: ["read", "org-model"], requiresActiveOrg: true
|
|
705
695
|
}),
|
|
706
696
|
command(["org-model", "people", "get"], "Show one modeled person.", {
|
|
707
697
|
labels: ["read", "org-model"],
|
|
708
698
|
args: [arg("name", "Person name.")],
|
|
699
|
+
flags: releaseSnapshotReadFlags("Read modeled person detail from a release.", "Read modeled person detail from an environment's live deployment release."),
|
|
709
700
|
requiresActiveOrg: true
|
|
710
701
|
}),
|
|
711
702
|
command(["org-model", "agents", "list"], "List modeled agents.", {
|
|
712
|
-
|
|
703
|
+
flags: releaseSnapshotReadFlags("Read modeled agents from a release.", "Read modeled agents from an environment's live deployment release."),
|
|
704
|
+
labels: ["read", "org-model"],
|
|
705
|
+
requiresActiveOrg: true
|
|
713
706
|
}),
|
|
714
707
|
command(["org-model", "agents", "get"], "Show one modeled agent.", {
|
|
715
708
|
labels: ["read", "org-model"],
|
|
716
709
|
args: [arg("name", "Agent name.")],
|
|
710
|
+
flags: releaseSnapshotReadFlags("Read modeled agent detail from a release.", "Read modeled agent detail from an environment's live deployment release."),
|
|
717
711
|
requiresActiveOrg: true
|
|
718
712
|
}),
|
|
719
713
|
command(["org-model", "roles", "list"], "List modeled roles.", {
|
|
720
|
-
|
|
714
|
+
flags: releaseSnapshotReadFlags("Read modeled roles from a release.", "Read modeled roles from an environment's live deployment release."),
|
|
715
|
+
labels: ["read", "org-model"],
|
|
716
|
+
requiresActiveOrg: true
|
|
721
717
|
}),
|
|
722
718
|
command(["org-model", "roles", "get"], "Show one modeled role.", {
|
|
723
719
|
labels: ["read", "org-model"],
|
|
724
720
|
args: [arg("name", "Role name.")],
|
|
721
|
+
flags: releaseSnapshotReadFlags("Read modeled role detail from a release.", "Read modeled role detail from an environment's live deployment release."),
|
|
725
722
|
requiresActiveOrg: true
|
|
726
723
|
}),
|
|
727
724
|
command(["org-model", "assignments", "list"], "List modeled assignments.", {
|
|
728
|
-
|
|
725
|
+
flags: releaseSnapshotReadFlags("Read modeled assignments from a release.", "Read modeled assignments from an environment's live deployment release."),
|
|
726
|
+
labels: ["read", "org-model"],
|
|
727
|
+
requiresActiveOrg: true
|
|
729
728
|
}),
|
|
730
729
|
command(["org-model", "assignments", "get"], "Show one modeled assignment group by role.", {
|
|
731
730
|
labels: ["read", "org-model"],
|
|
732
731
|
args: [arg("role-name", "Role name.")],
|
|
732
|
+
flags: releaseSnapshotReadFlags("Read modeled assignment detail from a release.", "Read modeled assignment detail from an environment's live deployment release."),
|
|
733
733
|
requiresActiveOrg: true
|
|
734
734
|
}),
|
|
735
735
|
command(["org-model", "capabilities", "list"], "List capabilities.", {
|
|
736
|
-
|
|
736
|
+
flags: releaseSnapshotReadFlags("Read capabilities from a release.", "Read capabilities from an environment's live deployment release."),
|
|
737
|
+
labels: ["read", "org-model"],
|
|
738
|
+
requiresActiveOrg: true
|
|
737
739
|
}),
|
|
738
740
|
command(["org-model", "capabilities", "get"], "Show one capability.", {
|
|
739
741
|
labels: ["read", "org-model"],
|
|
740
742
|
args: [arg("name", "Capability name.")],
|
|
743
|
+
flags: releaseSnapshotReadFlags("Read capability detail from a release.", "Read capability detail from an environment's live deployment release."),
|
|
741
744
|
requiresActiveOrg: true
|
|
742
745
|
}),
|
|
743
746
|
command(["org-model", "operations", "list"], "List operations.", {
|
|
744
|
-
|
|
747
|
+
flags: releaseSnapshotReadFlags("Read operations from a release.", "Read operations from an environment's live deployment release."),
|
|
748
|
+
labels: ["read", "org-model"],
|
|
749
|
+
requiresActiveOrg: true
|
|
745
750
|
}),
|
|
746
751
|
command(["org-model", "operations", "get"], "Show one operation.", {
|
|
747
752
|
labels: ["read", "org-model"],
|
|
748
753
|
args: [arg("name", "Operation name.")],
|
|
754
|
+
flags: releaseSnapshotReadFlags("Read operation detail from a release.", "Read operation detail from an environment's live deployment release."),
|
|
749
755
|
requiresActiveOrg: true
|
|
750
756
|
}),
|
|
751
757
|
command(["access", "members", "list"], "List organization members.", {
|
package/dist/commands.js
CHANGED
|
@@ -421,58 +421,98 @@ async function executeActivityOptions(parsed, context, api) {
|
|
|
421
421
|
meta: { command: "activity options", orgId: org.id }
|
|
422
422
|
};
|
|
423
423
|
}
|
|
424
|
+
function readOptionalReleaseSnapshotSelector(parsed) {
|
|
425
|
+
const releaseId = readOptionalStringFlag(parsed, "release");
|
|
426
|
+
const environment = readCliEnvironmentFlag(parsed);
|
|
427
|
+
if (releaseId && environment) {
|
|
428
|
+
throw usageProblem("Use either --release or --environment, not both.", parsed.definition.id);
|
|
429
|
+
}
|
|
430
|
+
if (releaseId) {
|
|
431
|
+
return { releaseId };
|
|
432
|
+
}
|
|
433
|
+
if (environment) {
|
|
434
|
+
return { environment };
|
|
435
|
+
}
|
|
436
|
+
return undefined;
|
|
437
|
+
}
|
|
438
|
+
function readRequiredReleaseSnapshotSelector(parsed) {
|
|
439
|
+
const selector = readOptionalReleaseSnapshotSelector(parsed);
|
|
440
|
+
if (!selector) {
|
|
441
|
+
throw usageProblem("Provide either --release or --environment.", parsed.definition.id);
|
|
442
|
+
}
|
|
443
|
+
return selector;
|
|
444
|
+
}
|
|
445
|
+
function releaseSnapshotSelectorMeta(selector) {
|
|
446
|
+
if (!selector) {
|
|
447
|
+
return {};
|
|
448
|
+
}
|
|
449
|
+
if (typeof selector.releaseId === "string") {
|
|
450
|
+
return { releaseId: selector.releaseId };
|
|
451
|
+
}
|
|
452
|
+
if (typeof selector.environment === "string") {
|
|
453
|
+
return { environment: selector.environment };
|
|
454
|
+
}
|
|
455
|
+
return {};
|
|
456
|
+
}
|
|
424
457
|
async function executeWorkflowList(parsed, context, api) {
|
|
425
458
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
426
|
-
const
|
|
459
|
+
const selector = readOptionalReleaseSnapshotSelector(parsed);
|
|
460
|
+
const data = await api.listWorkflows(session, org.id, selector ?? { live: true });
|
|
427
461
|
return {
|
|
428
462
|
data,
|
|
429
463
|
human: renderTable(data.processes, [
|
|
430
464
|
{ key: "name", label: "Name" },
|
|
465
|
+
{ key: "environmentKey", label: "Environment" },
|
|
466
|
+
{ key: "liveReleaseId", label: "Live release" },
|
|
431
467
|
{ key: "latestVersion", label: "Latest" },
|
|
432
468
|
{ key: "deployedVersion", label: "Deployed" }
|
|
433
469
|
]),
|
|
434
470
|
kind: "authoring_workflow_list",
|
|
435
|
-
meta: { command: "workflow list", orgId: org.id }
|
|
471
|
+
meta: { command: "workflow list", orgId: org.id, ...releaseSnapshotSelectorMeta(selector) }
|
|
436
472
|
};
|
|
437
473
|
}
|
|
438
474
|
async function executeWorkflowGet(parsed, context, api) {
|
|
439
475
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
440
|
-
const
|
|
476
|
+
const selector = readRequiredReleaseSnapshotSelector(parsed);
|
|
477
|
+
const data = await api.getWorkflow(session, org.id, readRequiredArg(parsed, "name"), selector);
|
|
441
478
|
return {
|
|
442
479
|
data,
|
|
443
480
|
human: renderPrettyJson(data.process),
|
|
444
481
|
kind: "authoring_workflow_get",
|
|
445
|
-
meta: { command: "workflow get", orgId: org.id }
|
|
482
|
+
meta: { command: "workflow get", orgId: org.id, ...releaseSnapshotSelectorMeta(selector) }
|
|
446
483
|
};
|
|
447
484
|
}
|
|
448
485
|
async function executeWorkflowVersionGet(parsed, context, api) {
|
|
449
486
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
450
|
-
const
|
|
487
|
+
const selector = readRequiredReleaseSnapshotSelector(parsed);
|
|
488
|
+
const data = await api.getWorkflowVersion(session, org.id, readRequiredArg(parsed, "name"), readRequiredIntegerArg(parsed, "version"), selector);
|
|
451
489
|
return {
|
|
452
490
|
data,
|
|
453
491
|
human: renderPrettyJson(data.process),
|
|
454
492
|
kind: "authoring_workflow_version_get",
|
|
455
|
-
meta: { command: "workflow version get", orgId: org.id }
|
|
493
|
+
meta: { command: "workflow version get", orgId: org.id, ...releaseSnapshotSelectorMeta(selector) }
|
|
456
494
|
};
|
|
457
495
|
}
|
|
458
496
|
async function executeWorkflowDependencies(parsed, context, api) {
|
|
459
497
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
460
|
-
const
|
|
498
|
+
const selector = readRequiredReleaseSnapshotSelector(parsed);
|
|
499
|
+
const data = await api.getWorkflowDependencies(session, org.id, readRequiredArg(parsed, "name"), readRequiredIntegerArg(parsed, "version"), selector);
|
|
461
500
|
return {
|
|
462
501
|
data,
|
|
463
502
|
human: renderPrettyJson(data.dependencies),
|
|
464
503
|
kind: "authoring_workflow_dependencies",
|
|
465
|
-
meta: { command: "workflow dependencies", orgId: org.id }
|
|
504
|
+
meta: { command: "workflow dependencies", orgId: org.id, ...releaseSnapshotSelectorMeta(selector) }
|
|
466
505
|
};
|
|
467
506
|
}
|
|
468
507
|
async function executeWorkflowContext(parsed, context, api) {
|
|
469
508
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
470
|
-
const
|
|
509
|
+
const selector = readRequiredReleaseSnapshotSelector(parsed);
|
|
510
|
+
const data = await api.getWorkflowContext(session, org.id, selector);
|
|
471
511
|
return {
|
|
472
512
|
data,
|
|
473
513
|
human: renderPrettyJson(data.context),
|
|
474
514
|
kind: "authoring_workflow_context",
|
|
475
|
-
meta: { command: "workflow context", orgId: org.id }
|
|
515
|
+
meta: { command: "workflow context", orgId: org.id, ...releaseSnapshotSelectorMeta(selector) }
|
|
476
516
|
};
|
|
477
517
|
}
|
|
478
518
|
async function executeWorkflowStart(parsed, context, api) {
|
|
@@ -483,7 +523,7 @@ async function executeWorkflowStart(parsed, context, api) {
|
|
|
483
523
|
}
|
|
484
524
|
const inputSpecifier = readOptionalStringFlag(parsed, "input");
|
|
485
525
|
const inputData = inputSpecifier
|
|
486
|
-
? await readJsonInputSpecifier(inputSpecifier, context.stdin, "workflow start")
|
|
526
|
+
? await readJsonInputSpecifier(inputSpecifier, context.stdin, "workflow start", { flag: "input" })
|
|
487
527
|
: undefined;
|
|
488
528
|
const workflowName = readRequiredArg(parsed, "name");
|
|
489
529
|
const resolvedEnvironment = resolveCliEnvironment(parsed, session);
|
|
@@ -513,7 +553,7 @@ async function executeWorkflowNodeTest(parsed, context, api) {
|
|
|
513
553
|
const environment = readOptionalStringFlag(parsed, "environment");
|
|
514
554
|
const inputSpecifier = readOptionalStringFlag(parsed, "input");
|
|
515
555
|
const inputData = inputSpecifier
|
|
516
|
-
? await readJsonInputSpecifier(inputSpecifier, context.stdin, "test node")
|
|
556
|
+
? await readJsonInputSpecifier(inputSpecifier, context.stdin, "test node", { flag: "input" })
|
|
517
557
|
: undefined;
|
|
518
558
|
const data = await api.testWorkflowNode(session, org.id, workflowName, nodeId, {
|
|
519
559
|
...(environment ? { environment } : {}),
|
|
@@ -645,11 +685,8 @@ async function executeReleaseSource(parsed, context, api) {
|
|
|
645
685
|
async function executeReleaseCreate(parsed, context, api) {
|
|
646
686
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
647
687
|
const workspacePath = readRequiredArg(parsed, "workspace");
|
|
648
|
-
const label = readOptionalStringFlag(parsed, "label");
|
|
649
688
|
if (isZipArchivePath(workspacePath)) {
|
|
650
|
-
const data = await api.createReleaseArchive(session, org.id, await readArchiveBytes(workspacePath, "release create")
|
|
651
|
-
...(label ? { label } : {})
|
|
652
|
-
});
|
|
689
|
+
const data = await api.createReleaseArchive(session, org.id, await readArchiveBytes(workspacePath, "release create"));
|
|
653
690
|
const summary = summarizeReleaseCreation(data.created.release.id);
|
|
654
691
|
return {
|
|
655
692
|
data,
|
|
@@ -660,8 +697,7 @@ async function executeReleaseCreate(parsed, context, api) {
|
|
|
660
697
|
}
|
|
661
698
|
const files = await readImportEntries(workspacePath);
|
|
662
699
|
const data = await api.createRelease(session, org.id, {
|
|
663
|
-
files
|
|
664
|
-
...(label ? { label } : {})
|
|
700
|
+
files
|
|
665
701
|
});
|
|
666
702
|
const summary = summarizeReleaseCreation(data.created.release.id);
|
|
667
703
|
return {
|
|
@@ -702,7 +738,7 @@ async function executeEnvironment(parsed, context, api) {
|
|
|
702
738
|
const data = await api.getEnvironment(session, org.id, readRequiredArg(parsed, "environment"));
|
|
703
739
|
return {
|
|
704
740
|
data,
|
|
705
|
-
human: renderPrettyJson(data
|
|
741
|
+
human: renderPrettyJson(data),
|
|
706
742
|
kind: "environment_get",
|
|
707
743
|
meta: { command: "environment get", orgId: org.id }
|
|
708
744
|
};
|
|
@@ -942,7 +978,7 @@ async function executeTaskGet(parsed, context, api) {
|
|
|
942
978
|
}
|
|
943
979
|
async function executeTaskComplete(parsed, context, api) {
|
|
944
980
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
945
|
-
const outputData = await readJsonInputSpecifier(String(parsed.flags.output), context.stdin, "task complete");
|
|
981
|
+
const outputData = await readJsonInputSpecifier(String(parsed.flags.output), context.stdin, "task complete", { flag: "output" });
|
|
946
982
|
const data = await api.completeTask(session, org.id, readRequiredArg(parsed, "task-id"), {
|
|
947
983
|
outputData,
|
|
948
984
|
workflowId: String(parsed.flags.run)
|
|
@@ -958,8 +994,9 @@ async function executeOrgModel(parsed, context, api) {
|
|
|
958
994
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
959
995
|
const section = parsed.definition.path[1] ?? "";
|
|
960
996
|
const verb = parsed.definition.path[2] ?? "";
|
|
997
|
+
const selector = readRequiredReleaseSnapshotSelector(parsed);
|
|
961
998
|
if (section === "operations") {
|
|
962
|
-
const editor = (await api.getOperationsEditor(session, org.id)).editor;
|
|
999
|
+
const editor = (await api.getOperationsEditor(session, org.id, selector)).editor;
|
|
963
1000
|
const record = verb === "get"
|
|
964
1001
|
? requireFound(editor.operations.find((entry) => entry.name === parsed.args.name), `Operation '${parsed.args.name}' was not found.`, parsed.definition.id)
|
|
965
1002
|
: undefined;
|
|
@@ -972,17 +1009,17 @@ async function executeOrgModel(parsed, context, api) {
|
|
|
972
1009
|
{ key: "command", label: "Command" }
|
|
973
1010
|
]),
|
|
974
1011
|
kind: `authoring_${sanitizeKind(section)}_${verb}`,
|
|
975
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1012
|
+
meta: { command: parsed.definition.path.join(" "), orgId: org.id, ...releaseSnapshotSelectorMeta(selector) }
|
|
976
1013
|
};
|
|
977
1014
|
}
|
|
978
|
-
const management = (await api.getOrganizationManagementContext(session, org.id)).context;
|
|
1015
|
+
const management = (await api.getOrganizationManagementContext(session, org.id, selector)).context;
|
|
979
1016
|
if (section === "capabilities" && verb === "get") {
|
|
980
|
-
const detail = await api.getCapabilityEditor(session, org.id, readRequiredArg(parsed, "name"));
|
|
1017
|
+
const detail = await api.getCapabilityEditor(session, org.id, readRequiredArg(parsed, "name"), selector);
|
|
981
1018
|
return {
|
|
982
1019
|
data: detail,
|
|
983
1020
|
human: renderPrettyJson(detail.editor),
|
|
984
1021
|
kind: "authoring_capabilities_get",
|
|
985
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1022
|
+
meta: { command: parsed.definition.path.join(" "), orgId: org.id, ...releaseSnapshotSelectorMeta(selector) }
|
|
986
1023
|
};
|
|
987
1024
|
}
|
|
988
1025
|
const selection = selectOrgModelSection(management, section, verb === "get" ? parsed.args[section === "assignments" ? "role-name" : "name"] : undefined);
|
|
@@ -990,7 +1027,7 @@ async function executeOrgModel(parsed, context, api) {
|
|
|
990
1027
|
data: selection.data,
|
|
991
1028
|
human: selection.human,
|
|
992
1029
|
kind: `authoring_${sanitizeKind(section)}_${verb}`,
|
|
993
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1030
|
+
meta: { command: parsed.definition.path.join(" "), orgId: org.id, ...releaseSnapshotSelectorMeta(selector) }
|
|
994
1031
|
};
|
|
995
1032
|
}
|
|
996
1033
|
async function executeAccess(parsed, context, api) {
|
|
@@ -1189,7 +1226,7 @@ async function executeEnv(parsed, context, api) {
|
|
|
1189
1226
|
}
|
|
1190
1227
|
case "env.replace": {
|
|
1191
1228
|
const environment = readRequiredCliEnvironmentFlag(parsed);
|
|
1192
|
-
const body = await readJsonInputSpecifier(String(parsed.flags.file), context.stdin, parsed.definition.id);
|
|
1229
|
+
const body = await readJsonInputSpecifier(String(parsed.flags.file), context.stdin, parsed.definition.id, { flag: "file" });
|
|
1193
1230
|
const rawVariables = body.variables;
|
|
1194
1231
|
if (!Array.isArray(rawVariables)) {
|
|
1195
1232
|
throw usageProblem("Environment replace payload must contain a variables array.", parsed.definition.id);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const PUBLIC_ERROR_CODE_PATTERN = /^[a-z][a-z0-9_]*(?:\/[a-z][a-z0-9_]*)?$/u;
|
|
2
|
+
export function normalizePublicErrorCode(code) {
|
|
3
|
+
return PUBLIC_ERROR_CODE_PATTERN.test(code) ? code : "internal/error";
|
|
4
|
+
}
|
|
5
|
+
export function readPublicErrorCodeFromType(type) {
|
|
6
|
+
return type.startsWith("https://errors.kora.dev/")
|
|
7
|
+
? type.slice("https://errors.kora.dev/".length)
|
|
8
|
+
: "internal/error";
|
|
9
|
+
}
|
|
@@ -395,11 +395,11 @@ async function readOptionalPermissions(parsed, context) {
|
|
|
395
395
|
if (!specifier) {
|
|
396
396
|
return undefined;
|
|
397
397
|
}
|
|
398
|
-
return normalizePermissions(await readJsonInputSpecifier(specifier, context.stdin, parsed.definition.id), parsed.definition.id);
|
|
398
|
+
return normalizePermissions(await readJsonInputSpecifier(specifier, context.stdin, parsed.definition.id, { flag: "permissions" }), parsed.definition.id);
|
|
399
399
|
}
|
|
400
400
|
async function readRequiredPermissions(parsed, context) {
|
|
401
401
|
const specifier = readRequiredStringFlag(parsed, "permissions");
|
|
402
|
-
return normalizePermissions(await readJsonInputSpecifier(specifier, context.stdin, parsed.definition.id), parsed.definition.id);
|
|
402
|
+
return normalizePermissions(await readJsonInputSpecifier(specifier, context.stdin, parsed.definition.id, { flag: "permissions" }), parsed.definition.id);
|
|
403
403
|
}
|
|
404
404
|
function normalizePermissions(input, instance) {
|
|
405
405
|
const permissions = input.grantedPermissions && isRecord(input.grantedPermissions)
|
package/dist/files.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
1
2
|
import type { Readable } from "node:stream";
|
|
2
|
-
|
|
3
|
+
interface ReadStructuredInputOptions {
|
|
4
|
+
flag?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function readJsonInputSpecifier(specifier: string, stdin: Readable, instance: string, options?: ReadStructuredInputOptions): Promise<Record<string, unknown>>;
|
|
3
7
|
export declare function readTextInputSpecifier(specifier: string, stdin: Readable, instance: string): Promise<string>;
|
|
4
8
|
export declare function readImportEntries(pathValue: string): Promise<Array<{
|
|
5
9
|
content: string;
|
|
@@ -7,6 +11,12 @@ export declare function readImportEntries(pathValue: string): Promise<Array<{
|
|
|
7
11
|
}>>;
|
|
8
12
|
export declare function isZipArchivePath(pathValue: string): boolean;
|
|
9
13
|
export declare function readArchiveBytes(pathValue: string, instance: string): Promise<Uint8Array>;
|
|
14
|
+
export declare function readLocalFileBytes(pathValue: string, instance: string, options?: {
|
|
15
|
+
regularFileMessage?: string;
|
|
16
|
+
}): Promise<{
|
|
17
|
+
absolutePath: string;
|
|
18
|
+
bytes: Buffer;
|
|
19
|
+
}>;
|
|
10
20
|
export declare function readWorkspaceTestEntries(pathValue: string): Promise<Array<{
|
|
11
21
|
content: string;
|
|
12
22
|
path: string;
|
|
@@ -38,3 +48,4 @@ export declare function readPackageFileEntries(pathValue: string, instance: stri
|
|
|
38
48
|
contentBase64: string;
|
|
39
49
|
path: string;
|
|
40
50
|
}>>;
|
|
51
|
+
export {};
|
package/dist/files.js
CHANGED
|
@@ -7,14 +7,38 @@ const MAX_IMPORT_FILE_COUNT = 500;
|
|
|
7
7
|
const MAX_IMPORT_FILE_BYTES = 1_000_000;
|
|
8
8
|
const MAX_PACKAGE_FILE_BYTES = 2_000_000;
|
|
9
9
|
const MAX_IMPORT_TOTAL_BYTES = 10_000_000;
|
|
10
|
-
export async function readJsonInputSpecifier(specifier, stdin, instance) {
|
|
10
|
+
export async function readJsonInputSpecifier(specifier, stdin, instance, options = {}) {
|
|
11
11
|
if (specifier === "-") {
|
|
12
|
-
return readJsonObject(await readStream(stdin), instance
|
|
12
|
+
return readJsonObject(await readStream(stdin), instance, {
|
|
13
|
+
source: "stdin",
|
|
14
|
+
...options
|
|
15
|
+
});
|
|
13
16
|
}
|
|
14
17
|
if (!specifier.startsWith("@")) {
|
|
15
|
-
throw usageProblem("Structured JSON input must use @file.json or - for stdin.", instance
|
|
18
|
+
throw usageProblem("Structured JSON input must use @file.json or - for stdin.", instance, {
|
|
19
|
+
...(options.flag ? { flag: options.flag } : {})
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
const filePath = resolve(specifier.slice(1));
|
|
23
|
+
let source;
|
|
24
|
+
try {
|
|
25
|
+
source = await readFile(filePath, "utf8");
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
const nativeCode = readNodeErrorCode(error);
|
|
29
|
+
if (nativeCode && isLocalPathReadErrorCode(nativeCode)) {
|
|
30
|
+
throw usageProblem(`Structured JSON input file ${filePath} could not be read: ${nativeCode}.`, instance, {
|
|
31
|
+
filePath,
|
|
32
|
+
...(options.flag ? { flag: options.flag } : {}),
|
|
33
|
+
nativeCode
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
throw error;
|
|
16
37
|
}
|
|
17
|
-
return readJsonObject(
|
|
38
|
+
return readJsonObject(source, instance, {
|
|
39
|
+
filePath,
|
|
40
|
+
...options
|
|
41
|
+
});
|
|
18
42
|
}
|
|
19
43
|
export async function readTextInputSpecifier(specifier, stdin, instance) {
|
|
20
44
|
if (specifier === "-") {
|
|
@@ -35,12 +59,26 @@ export function isZipArchivePath(pathValue) {
|
|
|
35
59
|
return extname(pathValue).toLowerCase() === ".zip";
|
|
36
60
|
}
|
|
37
61
|
export async function readArchiveBytes(pathValue, instance) {
|
|
62
|
+
const file = await readLocalFileBytes(pathValue, instance, {
|
|
63
|
+
regularFileMessage: "Archive path must be a regular .zip file, not a directory or symbolic link."
|
|
64
|
+
});
|
|
65
|
+
return file.bytes;
|
|
66
|
+
}
|
|
67
|
+
export async function readLocalFileBytes(pathValue, instance, options = {}) {
|
|
38
68
|
const absolutePath = resolve(pathValue);
|
|
39
|
-
const pathStat = await
|
|
69
|
+
const pathStat = await readLocalPathStat(absolutePath, instance);
|
|
40
70
|
if (pathStat.isSymbolicLink() || !pathStat.isFile()) {
|
|
41
|
-
throw usageProblem("
|
|
71
|
+
throw usageProblem(options.regularFileMessage ?? "Path must be a regular file, not a directory or symbolic link.", instance);
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
return {
|
|
75
|
+
absolutePath,
|
|
76
|
+
bytes: await readFile(absolutePath)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
throwLocalPathReadProblem(error, absolutePath, instance);
|
|
42
81
|
}
|
|
43
|
-
return await readFile(absolutePath);
|
|
44
82
|
}
|
|
45
83
|
export async function readWorkspaceTestEntries(pathValue) {
|
|
46
84
|
return await readUtf8Entries(pathValue, {
|
|
@@ -50,7 +88,7 @@ export async function readWorkspaceTestEntries(pathValue) {
|
|
|
50
88
|
}
|
|
51
89
|
async function readUtf8Entries(pathValue, options) {
|
|
52
90
|
const absolutePath = resolve(pathValue);
|
|
53
|
-
const pathStat = await
|
|
91
|
+
const pathStat = await readLocalPathStat(absolutePath, options.instance);
|
|
54
92
|
if (pathStat.isSymbolicLink()) {
|
|
55
93
|
throw usageProblem("Import path must be a regular file or directory, not a symbolic link.", options.instance);
|
|
56
94
|
}
|
|
@@ -151,7 +189,7 @@ export async function writePackageExport(outPath, envelope) {
|
|
|
151
189
|
}
|
|
152
190
|
export async function readPackageFileEntries(pathValue, instance) {
|
|
153
191
|
const absolutePath = resolve(pathValue);
|
|
154
|
-
const pathStat = await
|
|
192
|
+
const pathStat = await readLocalPathStat(absolutePath, instance);
|
|
155
193
|
if (pathStat.isSymbolicLink()) {
|
|
156
194
|
throw usageProblem("Package path must be a regular file or directory, not a symbolic link.", instance);
|
|
157
195
|
}
|
|
@@ -161,7 +199,9 @@ export async function readPackageFileEntries(pathValue, instance) {
|
|
|
161
199
|
totalBytes: 0
|
|
162
200
|
}, instance);
|
|
163
201
|
return [{
|
|
164
|
-
contentBase64: (await
|
|
202
|
+
contentBase64: (await readLocalFileBytes(absolutePath, instance, {
|
|
203
|
+
regularFileMessage: "Package path must be a regular file or directory, not a symbolic link."
|
|
204
|
+
})).bytes.toString("base64"),
|
|
165
205
|
path: basename(absolutePath)
|
|
166
206
|
}];
|
|
167
207
|
}
|
|
@@ -173,16 +213,24 @@ export async function readPackageFileEntries(pathValue, instance) {
|
|
|
173
213
|
entries.sort((left, right) => left.path.localeCompare(right.path));
|
|
174
214
|
return entries;
|
|
175
215
|
}
|
|
176
|
-
function readJsonObject(source, instance) {
|
|
216
|
+
function readJsonObject(source, instance, options) {
|
|
177
217
|
let parsed;
|
|
178
218
|
try {
|
|
179
219
|
parsed = JSON.parse(source);
|
|
180
220
|
}
|
|
181
221
|
catch {
|
|
182
|
-
|
|
222
|
+
const sourceLabel = "filePath" in options ? `file ${options.filePath}` : "from stdin";
|
|
223
|
+
throw usageProblem(`Structured JSON input ${sourceLabel} must be valid JSON.`, instance, {
|
|
224
|
+
...("filePath" in options ? { filePath: options.filePath } : { source: options.source }),
|
|
225
|
+
...(options.flag ? { flag: options.flag } : {})
|
|
226
|
+
});
|
|
183
227
|
}
|
|
184
228
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
185
|
-
|
|
229
|
+
const sourceLabel = "filePath" in options ? `file ${options.filePath}` : "from stdin";
|
|
230
|
+
throw usageProblem(`Structured JSON input ${sourceLabel} must be a JSON object.`, instance, {
|
|
231
|
+
...("filePath" in options ? { filePath: options.filePath } : { source: options.source }),
|
|
232
|
+
...(options.flag ? { flag: options.flag } : {})
|
|
233
|
+
});
|
|
186
234
|
}
|
|
187
235
|
return parsed;
|
|
188
236
|
}
|
|
@@ -232,10 +280,10 @@ async function walkPackageDirectory(root, current, entries, counters, instance)
|
|
|
232
280
|
if (!child.isFile()) {
|
|
233
281
|
continue;
|
|
234
282
|
}
|
|
235
|
-
const childStat = await
|
|
283
|
+
const childStat = await readLocalPathStat(childPath, instance);
|
|
236
284
|
assertPackageFileCanBeRead(childStat.size, relativePath, counters, instance);
|
|
237
285
|
entries.push({
|
|
238
|
-
contentBase64: (await
|
|
286
|
+
contentBase64: (await readLocalFileBytes(childPath, instance)).bytes.toString("base64"),
|
|
239
287
|
path: relativePath
|
|
240
288
|
});
|
|
241
289
|
}
|
|
@@ -289,7 +337,13 @@ async function readLimitedUtf8File(filePath, displayPath, counters, options) {
|
|
|
289
337
|
if (counters.fileCount >= MAX_IMPORT_FILE_COUNT) {
|
|
290
338
|
throw usageProblem(`Import has more than ${String(MAX_IMPORT_FILE_COUNT)} files.`, options.instance);
|
|
291
339
|
}
|
|
292
|
-
|
|
340
|
+
let content;
|
|
341
|
+
try {
|
|
342
|
+
content = await readFile(filePath, "utf8");
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
throwLocalPathReadProblem(error, filePath, options.instance);
|
|
346
|
+
}
|
|
293
347
|
const bytes = Buffer.byteLength(content, "utf8");
|
|
294
348
|
if (bytes > options.maxFileBytes) {
|
|
295
349
|
throw usageProblem(`Import file ${displayPath} exceeds the per-file byte limit.`, options.instance);
|
|
@@ -320,3 +374,35 @@ function isNodeErrorWithCode(error, code) {
|
|
|
320
374
|
"code" in error &&
|
|
321
375
|
error.code === code;
|
|
322
376
|
}
|
|
377
|
+
function readNodeErrorCode(error) {
|
|
378
|
+
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
379
|
+
return undefined;
|
|
380
|
+
}
|
|
381
|
+
const code = error.code;
|
|
382
|
+
return typeof code === "string" && code.length > 0 ? code : undefined;
|
|
383
|
+
}
|
|
384
|
+
function isLocalPathReadErrorCode(code) {
|
|
385
|
+
return code === "EACCES" ||
|
|
386
|
+
code === "EISDIR" ||
|
|
387
|
+
code === "ENOENT" ||
|
|
388
|
+
code === "ENOTDIR" ||
|
|
389
|
+
code === "EPERM";
|
|
390
|
+
}
|
|
391
|
+
async function readLocalPathStat(absolutePath, instance) {
|
|
392
|
+
try {
|
|
393
|
+
return await lstat(absolutePath);
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
throwLocalPathReadProblem(error, absolutePath, instance);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
function throwLocalPathReadProblem(error, absolutePath, instance) {
|
|
400
|
+
const nativeCode = readNodeErrorCode(error);
|
|
401
|
+
if (nativeCode && isLocalPathReadErrorCode(nativeCode)) {
|
|
402
|
+
throw usageProblem(`Local path ${absolutePath} could not be read: ${nativeCode}.`, instance, {
|
|
403
|
+
nativeCode,
|
|
404
|
+
path: absolutePath
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
throw error;
|
|
408
|
+
}
|