@kora-platform/cli 0.7.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/README.md +21 -0
- package/dist/api-client.d.ts +274 -106
- package/dist/api-client.js +192 -167
- package/dist/api-types.d.ts +301 -163
- package/dist/artifact-api-client.d.ts +28 -1
- package/dist/artifact-api-client.js +33 -0
- package/dist/artifact-commands.d.ts +5 -0
- package/dist/artifact-commands.js +177 -4
- package/dist/audit-commands.d.ts +12 -0
- package/dist/audit-commands.js +74 -0
- package/dist/auth-commands.d.ts +1 -0
- package/dist/auth-commands.js +195 -32
- package/dist/cli-errors.d.ts +7 -1
- package/dist/cli-errors.js +12 -1
- package/dist/command-builders.d.ts +1 -0
- package/dist/command-builders.js +1 -0
- package/dist/command-flags.d.ts +1 -0
- package/dist/command-flags.js +7 -0
- package/dist/command-groups.js +10 -12
- package/dist/command-registry.js +595 -277
- package/dist/commands.js +728 -636
- package/dist/environment-context.d.ts +9 -0
- package/dist/environment-context.js +32 -0
- package/dist/error-code.d.ts +2 -0
- package/dist/error-code.js +9 -0
- package/dist/{integration-commands.d.ts → extension-commands.d.ts} +3 -2
- package/dist/extension-commands.js +446 -0
- package/dist/files.d.ts +44 -4
- package/dist/files.js +349 -26
- package/dist/format.d.ts +6 -0
- package/dist/format.js +83 -1
- package/dist/runner.js +28 -10
- package/dist/schema-registry-data.d.ts +318 -571
- package/dist/schema-registry-data.js +356 -698
- package/dist/session-store.js +80 -0
- package/dist/session.d.ts +1 -0
- package/dist/transport-refresh.d.ts +10 -0
- package/dist/transport-refresh.js +51 -0
- package/dist/transport.d.ts +31 -0
- package/dist/transport.js +102 -36
- package/dist/types.d.ts +2 -1
- package/dist/workspace-source.d.ts +1 -0
- package/dist/workspace-source.js +13 -0
- package/package.json +2 -1
- package/dist/dotenv.d.ts +0 -1
- package/dist/dotenv.js +0 -26
- package/dist/integration-api-client.d.ts +0 -29
- package/dist/integration-api-client.js +0 -50
- package/dist/integration-commands.js +0 -208
package/dist/commands.js
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
-
import { dirname, resolve } from "node:path";
|
|
3
1
|
import { renderCompletionScript, resolveCompletionCandidates } from "./completion.js";
|
|
4
|
-
import { executeAuthLogin, executeAuthLogout, executeAuthWhoami } from "./auth-commands.js";
|
|
2
|
+
import { executeAuthLogin, executeAuthLogout, executeAuthSignup, executeAuthWhoami } from "./auth-commands.js";
|
|
5
3
|
import { getCliSchema, listCliSchemas } from "./schema-registry.js";
|
|
6
4
|
import { createPlatformApiClient } from "./api-client.js";
|
|
7
5
|
import { authProblem, genericProblem, notFoundProblem, usageProblem } from "./cli-errors.js";
|
|
8
|
-
import { readOptionalNumberFlag, readOptionalStringFlag, readRequiredStringFlag } from "./command-flags.js";
|
|
9
|
-
import { executeArtifactDownload, executeArtifactList, executeArtifactRead, executeArtifactUpload } from "./artifact-commands.js";
|
|
10
|
-
import {
|
|
6
|
+
import { readOptionalNumberFlag, readOptionalStringFlag, readRequiredStringFlag, readRequiredStringFlagPreservingEmpty } from "./command-flags.js";
|
|
7
|
+
import { executeArtifactDownload, executeArtifactArchive, executeArtifactInspect, executeArtifactInventory, executeArtifactList, executeArtifactPurge, executeArtifactRead, executeArtifactRestore, executeArtifactUpload } from "./artifact-commands.js";
|
|
8
|
+
import { executeAudit } from "./audit-commands.js";
|
|
9
|
+
import { isZipArchivePath, readArchiveBytes, readImportEntries, readJsonInputSpecifier, readWorkspaceTestEntries, readTextInputSpecifier, writeReleaseSourceFiles } from "./files.js";
|
|
11
10
|
import { renderDiffSummary, renderKeyValue, renderPrettyJson, renderSuccess, renderTable } from "./format.js";
|
|
12
11
|
import { buildTaskDetailPresentation } from "./task-detail.js";
|
|
13
12
|
import { confirmDestructive } from "./interaction.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { isInteractive } from "./prompts.js";
|
|
13
|
+
import { executeExtensions } from "./extension-commands.js";
|
|
14
|
+
import { readCliEnvironmentFlag, readRequiredCliEnvironmentFlag, resolveCliEnvironment } from "./environment-context.js";
|
|
17
15
|
export async function executeParsedCommand(parsed, context) {
|
|
18
16
|
const api = createPlatformApiClient({
|
|
19
17
|
sessionStore: context.sessionStore
|
|
@@ -21,6 +19,8 @@ export async function executeParsedCommand(parsed, context) {
|
|
|
21
19
|
switch (parsed.definition.id) {
|
|
22
20
|
case "auth.login":
|
|
23
21
|
return executeAuthLogin(parsed, context, api);
|
|
22
|
+
case "auth.signup":
|
|
23
|
+
return executeAuthSignup(parsed, context, api);
|
|
24
24
|
case "auth.logout":
|
|
25
25
|
return executeAuthLogout(context, api);
|
|
26
26
|
case "auth.whoami":
|
|
@@ -41,10 +41,6 @@ export async function executeParsedCommand(parsed, context) {
|
|
|
41
41
|
return executeOrgSelect(parsed, context, api);
|
|
42
42
|
case "org.settings.get":
|
|
43
43
|
return executeOrgSettingsGet(parsed, context, api);
|
|
44
|
-
case "org.import":
|
|
45
|
-
return executeOrgImport(parsed, context, api);
|
|
46
|
-
case "org.bundle.show":
|
|
47
|
-
return executeOrgBundleShow(parsed, context, api);
|
|
48
44
|
case "org.reset":
|
|
49
45
|
return executeOrgReset(parsed, context, api);
|
|
50
46
|
case "status":
|
|
@@ -65,24 +61,29 @@ export async function executeParsedCommand(parsed, context) {
|
|
|
65
61
|
return executeWorkflowContext(parsed, context, api);
|
|
66
62
|
case "workflow.start":
|
|
67
63
|
return executeWorkflowStart(parsed, context, api);
|
|
64
|
+
case "test.node":
|
|
65
|
+
return executeWorkflowNodeTest(parsed, context, api);
|
|
68
66
|
case "release.list":
|
|
69
67
|
return executeReleaseList(parsed, context, api);
|
|
70
68
|
case "release.get":
|
|
71
69
|
return executeReleaseGet(parsed, context, api);
|
|
72
|
-
case "release.
|
|
73
|
-
return
|
|
74
|
-
case "release.
|
|
75
|
-
return
|
|
76
|
-
case "release.
|
|
77
|
-
return
|
|
78
|
-
case "
|
|
79
|
-
|
|
80
|
-
case "
|
|
81
|
-
|
|
82
|
-
case "
|
|
83
|
-
|
|
84
|
-
case "
|
|
85
|
-
return
|
|
70
|
+
case "release.create":
|
|
71
|
+
return executeReleaseCreate(parsed, context, api);
|
|
72
|
+
case "release.validate":
|
|
73
|
+
return executeReleaseValidate(parsed, context, api);
|
|
74
|
+
case "release.source":
|
|
75
|
+
return executeReleaseSource(parsed, context, api);
|
|
76
|
+
case "environment.list":
|
|
77
|
+
case "environment.get":
|
|
78
|
+
case "environment.create":
|
|
79
|
+
case "environment.rename":
|
|
80
|
+
case "environment.archive":
|
|
81
|
+
case "environment.deploy":
|
|
82
|
+
case "environment.undeploy":
|
|
83
|
+
return executeEnvironment(parsed, context, api);
|
|
84
|
+
case "deployment.list":
|
|
85
|
+
case "deployment.get":
|
|
86
|
+
return executeDeployment(parsed, context, api);
|
|
86
87
|
case "run.list":
|
|
87
88
|
return executeRunList(parsed, context, api);
|
|
88
89
|
case "run.get":
|
|
@@ -93,14 +94,27 @@ export async function executeParsedCommand(parsed, context) {
|
|
|
93
94
|
return executeRunSteps(parsed, context, api);
|
|
94
95
|
case "run.abort":
|
|
95
96
|
return executeRunAbort(parsed, context, api);
|
|
97
|
+
case "audit.list":
|
|
98
|
+
case "audit.get":
|
|
99
|
+
return executeAudit(parsed, context, api, resolveOrgScope);
|
|
96
100
|
case "artifact.upload":
|
|
97
101
|
return executeArtifactUpload(parsed, context, api, resolveOrgScope);
|
|
98
102
|
case "artifact.list":
|
|
99
103
|
return executeArtifactList(parsed, context, api, resolveOrgScope);
|
|
104
|
+
case "artifact.inventory":
|
|
105
|
+
return executeArtifactInventory(parsed, context, api, resolveOrgScope);
|
|
106
|
+
case "artifact.inspect":
|
|
107
|
+
return executeArtifactInspect(parsed, context, api, resolveOrgScope);
|
|
100
108
|
case "artifact.download":
|
|
101
109
|
return executeArtifactDownload(parsed, context, api, resolveOrgScope);
|
|
102
110
|
case "artifact.read":
|
|
103
111
|
return executeArtifactRead(parsed, context, api, resolveOrgScope);
|
|
112
|
+
case "artifact.archive":
|
|
113
|
+
return executeArtifactArchive(parsed, context, api, resolveOrgScope);
|
|
114
|
+
case "artifact.restore":
|
|
115
|
+
return executeArtifactRestore(parsed, context, api, resolveOrgScope);
|
|
116
|
+
case "artifact.purge":
|
|
117
|
+
return executeArtifactPurge(parsed, context, api, resolveOrgScope);
|
|
104
118
|
case "task.list":
|
|
105
119
|
return executeTaskList(parsed, context, api);
|
|
106
120
|
case "task.get":
|
|
@@ -119,10 +133,6 @@ export async function executeParsedCommand(parsed, context) {
|
|
|
119
133
|
case "org-model.capabilities.get":
|
|
120
134
|
case "org-model.operations.list":
|
|
121
135
|
case "org-model.operations.get":
|
|
122
|
-
case "org-model.connectors.list":
|
|
123
|
-
case "org-model.connectors.get":
|
|
124
|
-
case "org-model.mcp-servers.list":
|
|
125
|
-
case "org-model.mcp-servers.get":
|
|
126
136
|
return executeOrgModel(parsed, context, api);
|
|
127
137
|
case "access.members.list":
|
|
128
138
|
case "access.members.get":
|
|
@@ -137,36 +147,25 @@ export async function executeParsedCommand(parsed, context) {
|
|
|
137
147
|
case "access.api-keys.create":
|
|
138
148
|
case "access.api-keys.revoke":
|
|
139
149
|
return executeAccess(parsed, context, api);
|
|
140
|
-
case "
|
|
141
|
-
case "
|
|
142
|
-
case "
|
|
143
|
-
case "
|
|
144
|
-
case "
|
|
145
|
-
case "
|
|
146
|
-
|
|
147
|
-
case "
|
|
148
|
-
case "
|
|
149
|
-
case "
|
|
150
|
-
case "
|
|
151
|
-
case "
|
|
152
|
-
case "
|
|
153
|
-
case "
|
|
154
|
-
|
|
155
|
-
case "mcp.oauth-status":
|
|
156
|
-
case "mcp.disconnect":
|
|
157
|
-
return executeMcp(parsed, context, api);
|
|
158
|
-
case "skills.list":
|
|
159
|
-
case "skills.get":
|
|
160
|
-
case "skills.create":
|
|
161
|
-
case "skills.upload":
|
|
162
|
-
case "skills.download":
|
|
163
|
-
case "skills.update":
|
|
164
|
-
case "skills.delete":
|
|
165
|
-
return executeSkills(parsed, context, api);
|
|
150
|
+
case "extensions.export":
|
|
151
|
+
case "extensions.validate":
|
|
152
|
+
case "extensions.publish":
|
|
153
|
+
case "extensions.install":
|
|
154
|
+
case "extensions.grant":
|
|
155
|
+
case "extensions.update":
|
|
156
|
+
case "extensions.built-ins.list":
|
|
157
|
+
case "extensions.built-ins.install":
|
|
158
|
+
case "extensions.list":
|
|
159
|
+
case "extensions.inspect":
|
|
160
|
+
case "extensions.detail":
|
|
161
|
+
case "extensions.disable":
|
|
162
|
+
case "extensions.enable":
|
|
163
|
+
case "extensions.delete":
|
|
164
|
+
return executeExtensions(parsed, context, api, resolveOrgScope);
|
|
166
165
|
case "env.list":
|
|
167
166
|
case "env.get":
|
|
167
|
+
case "env.set":
|
|
168
168
|
case "env.replace":
|
|
169
|
-
case "env.import":
|
|
170
169
|
return executeEnv(parsed, context, api);
|
|
171
170
|
case "secrets.list":
|
|
172
171
|
case "secrets.set":
|
|
@@ -176,12 +175,6 @@ export async function executeParsedCommand(parsed, context) {
|
|
|
176
175
|
case "chat.sessions.list":
|
|
177
176
|
case "chat.sessions.get":
|
|
178
177
|
case "chat.sessions.delete":
|
|
179
|
-
case "chat.changes":
|
|
180
|
-
case "chat.validation":
|
|
181
|
-
case "chat.draft.get":
|
|
182
|
-
case "chat.draft.apply":
|
|
183
|
-
case "chat.draft.refresh":
|
|
184
|
-
case "chat.workspace-state":
|
|
185
178
|
return executeChat(parsed, context, api);
|
|
186
179
|
case "completion.bash":
|
|
187
180
|
case "completion.zsh":
|
|
@@ -191,6 +184,10 @@ export async function executeParsedCommand(parsed, context) {
|
|
|
191
184
|
return executeSchemaList();
|
|
192
185
|
case "schema.get":
|
|
193
186
|
return executeSchemaGet(parsed);
|
|
187
|
+
case "admin.usage.summary":
|
|
188
|
+
case "admin.usage.workflows":
|
|
189
|
+
case "admin.usage.tools":
|
|
190
|
+
case "admin.usage.events":
|
|
194
191
|
case "admin.org.deleted.list":
|
|
195
192
|
case "admin.org.deleted.get":
|
|
196
193
|
case "admin.org.deleted.restore":
|
|
@@ -360,35 +357,13 @@ async function executeOrgSettingsGet(parsed, context, api) {
|
|
|
360
357
|
meta: { command: "org settings get", orgId: org.id }
|
|
361
358
|
};
|
|
362
359
|
}
|
|
363
|
-
async function executeOrgImport(parsed, context, api) {
|
|
364
|
-
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
365
|
-
const files = await readImportEntries(readRequiredArg(parsed, "path"));
|
|
366
|
-
const data = await api.importProject(session, org.id, { files });
|
|
367
|
-
return {
|
|
368
|
-
data,
|
|
369
|
-
human: renderSuccess(`Imported ${files.length} files into ${org.slug}.`),
|
|
370
|
-
kind: "authoring_import_apply",
|
|
371
|
-
meta: { command: "org import", orgId: org.id }
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
async function executeOrgBundleShow(parsed, context, api) {
|
|
375
|
-
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
376
|
-
const releaseId = readOptionalStringFlag(parsed, "release");
|
|
377
|
-
const data = await api.getProjectWorkspace(session, org.id, releaseId);
|
|
378
|
-
return {
|
|
379
|
-
data,
|
|
380
|
-
human: renderPrettyJson(data.project),
|
|
381
|
-
kind: "authoring_bundle_show",
|
|
382
|
-
meta: { command: "org bundle show", orgId: org.id }
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
360
|
async function executeOrgReset(parsed, context, api) {
|
|
386
361
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
387
|
-
await confirmDestructive(parsed, context, `Reset
|
|
362
|
+
await confirmDestructive(parsed, context, `Reset release, runtime, project, and chat state for ${org.slug}?`);
|
|
388
363
|
await api.resetOrganizationProject(session, org.id);
|
|
389
364
|
return {
|
|
390
365
|
data: { reset: true },
|
|
391
|
-
human: renderSuccess(`Reset
|
|
366
|
+
human: renderSuccess(`Reset project state for ${org.slug}.`),
|
|
392
367
|
kind: "authoring_project_reset",
|
|
393
368
|
meta: { command: "org reset", orgId: org.id }
|
|
394
369
|
};
|
|
@@ -399,8 +374,6 @@ async function executeStatus(parsed, context, api) {
|
|
|
399
374
|
return {
|
|
400
375
|
data,
|
|
401
376
|
human: renderKeyValue([
|
|
402
|
-
{ label: "Draft changes", value: data.overview.draftChangeCount },
|
|
403
|
-
{ label: "Draft has changes", value: data.overview.draftHasChanges },
|
|
404
377
|
{ label: "Pending tasks", value: data.overview.pendingTaskCount },
|
|
405
378
|
{ label: "Runs in flight", value: data.overview.runsInFlight },
|
|
406
379
|
{ label: "Workflow count", value: data.overview.workflowCount }
|
|
@@ -411,12 +384,14 @@ async function executeStatus(parsed, context, api) {
|
|
|
411
384
|
}
|
|
412
385
|
async function executeActivityList(parsed, context, api) {
|
|
413
386
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
387
|
+
const { environment } = resolveCliEnvironment(parsed, session);
|
|
414
388
|
const focusType = readOptionalStringFlag(parsed, "focus-type");
|
|
415
389
|
const focusValue = readOptionalStringFlag(parsed, "focus-value");
|
|
416
390
|
if ((focusType && !focusValue) || (!focusType && focusValue)) {
|
|
417
391
|
throw usageProblem("Use --focus-type and --focus-value together.", "activity list");
|
|
418
392
|
}
|
|
419
393
|
const data = await api.getActivity(session, org.id, {
|
|
394
|
+
environment,
|
|
420
395
|
...(focusType ? { focusType: focusType } : {}),
|
|
421
396
|
...(focusValue ? { focusValue } : {}),
|
|
422
397
|
...optionalNumberValue("limit", readOptionalNumberFlag(parsed, "limit")),
|
|
@@ -429,11 +404,12 @@ async function executeActivityList(parsed, context, api) {
|
|
|
429
404
|
{ key: "occurredAt", label: "Occurred" },
|
|
430
405
|
{ key: "kind", label: "Kind" },
|
|
431
406
|
{ key: "status", label: "Status" },
|
|
407
|
+
{ key: "environmentKey", label: "Environment" },
|
|
432
408
|
{ key: "processName", label: "Workflow" },
|
|
433
409
|
{ key: "summary", label: "Summary" }
|
|
434
410
|
]),
|
|
435
411
|
kind: "runtime_activity_list",
|
|
436
|
-
meta: { command: "activity list", orgId: org.id }
|
|
412
|
+
meta: { command: "activity list", environment, orgId: org.id }
|
|
437
413
|
};
|
|
438
414
|
}
|
|
439
415
|
async function executeActivityOptions(parsed, context, api) {
|
|
@@ -446,58 +422,98 @@ async function executeActivityOptions(parsed, context, api) {
|
|
|
446
422
|
meta: { command: "activity options", orgId: org.id }
|
|
447
423
|
};
|
|
448
424
|
}
|
|
425
|
+
function readOptionalReleaseSnapshotSelector(parsed) {
|
|
426
|
+
const releaseId = readOptionalStringFlag(parsed, "release");
|
|
427
|
+
const environment = readCliEnvironmentFlag(parsed);
|
|
428
|
+
if (releaseId && environment) {
|
|
429
|
+
throw usageProblem("Use either --release or --environment, not both.", parsed.definition.id);
|
|
430
|
+
}
|
|
431
|
+
if (releaseId) {
|
|
432
|
+
return { releaseId };
|
|
433
|
+
}
|
|
434
|
+
if (environment) {
|
|
435
|
+
return { environment };
|
|
436
|
+
}
|
|
437
|
+
return undefined;
|
|
438
|
+
}
|
|
439
|
+
function readRequiredReleaseSnapshotSelector(parsed) {
|
|
440
|
+
const selector = readOptionalReleaseSnapshotSelector(parsed);
|
|
441
|
+
if (!selector) {
|
|
442
|
+
throw usageProblem("Provide either --release or --environment.", parsed.definition.id);
|
|
443
|
+
}
|
|
444
|
+
return selector;
|
|
445
|
+
}
|
|
446
|
+
function releaseSnapshotSelectorMeta(selector) {
|
|
447
|
+
if (!selector) {
|
|
448
|
+
return {};
|
|
449
|
+
}
|
|
450
|
+
if (typeof selector.releaseId === "string") {
|
|
451
|
+
return { releaseId: selector.releaseId };
|
|
452
|
+
}
|
|
453
|
+
if (typeof selector.environment === "string") {
|
|
454
|
+
return { environment: selector.environment };
|
|
455
|
+
}
|
|
456
|
+
return {};
|
|
457
|
+
}
|
|
449
458
|
async function executeWorkflowList(parsed, context, api) {
|
|
450
459
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
451
|
-
const
|
|
460
|
+
const selector = readOptionalReleaseSnapshotSelector(parsed);
|
|
461
|
+
const data = await api.listWorkflows(session, org.id, selector ?? { live: true });
|
|
452
462
|
return {
|
|
453
463
|
data,
|
|
454
464
|
human: renderTable(data.processes, [
|
|
455
465
|
{ key: "name", label: "Name" },
|
|
466
|
+
{ key: "environmentKey", label: "Environment" },
|
|
467
|
+
{ key: "liveReleaseId", label: "Live release" },
|
|
456
468
|
{ key: "latestVersion", label: "Latest" },
|
|
457
|
-
{ key: "
|
|
469
|
+
{ key: "deployedVersion", label: "Deployed" }
|
|
458
470
|
]),
|
|
459
471
|
kind: "authoring_workflow_list",
|
|
460
|
-
meta: { command: "workflow list", orgId: org.id }
|
|
472
|
+
meta: { command: "workflow list", orgId: org.id, ...releaseSnapshotSelectorMeta(selector) }
|
|
461
473
|
};
|
|
462
474
|
}
|
|
463
475
|
async function executeWorkflowGet(parsed, context, api) {
|
|
464
476
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
465
|
-
const
|
|
477
|
+
const selector = readRequiredReleaseSnapshotSelector(parsed);
|
|
478
|
+
const data = await api.getWorkflow(session, org.id, readRequiredArg(parsed, "name"), selector);
|
|
466
479
|
return {
|
|
467
480
|
data,
|
|
468
481
|
human: renderPrettyJson(data.process),
|
|
469
482
|
kind: "authoring_workflow_get",
|
|
470
|
-
meta: { command: "workflow get", orgId: org.id }
|
|
483
|
+
meta: { command: "workflow get", orgId: org.id, ...releaseSnapshotSelectorMeta(selector) }
|
|
471
484
|
};
|
|
472
485
|
}
|
|
473
486
|
async function executeWorkflowVersionGet(parsed, context, api) {
|
|
474
487
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
475
|
-
const
|
|
488
|
+
const selector = readRequiredReleaseSnapshotSelector(parsed);
|
|
489
|
+
const data = await api.getWorkflowVersion(session, org.id, readRequiredArg(parsed, "name"), readRequiredIntegerArg(parsed, "version"), selector);
|
|
476
490
|
return {
|
|
477
491
|
data,
|
|
478
492
|
human: renderPrettyJson(data.process),
|
|
479
493
|
kind: "authoring_workflow_version_get",
|
|
480
|
-
meta: { command: "workflow version get", orgId: org.id }
|
|
494
|
+
meta: { command: "workflow version get", orgId: org.id, ...releaseSnapshotSelectorMeta(selector) }
|
|
481
495
|
};
|
|
482
496
|
}
|
|
483
497
|
async function executeWorkflowDependencies(parsed, context, api) {
|
|
484
498
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
485
|
-
const
|
|
499
|
+
const selector = readRequiredReleaseSnapshotSelector(parsed);
|
|
500
|
+
const data = await api.getWorkflowDependencies(session, org.id, readRequiredArg(parsed, "name"), readRequiredIntegerArg(parsed, "version"), selector);
|
|
486
501
|
return {
|
|
487
502
|
data,
|
|
488
503
|
human: renderPrettyJson(data.dependencies),
|
|
489
504
|
kind: "authoring_workflow_dependencies",
|
|
490
|
-
meta: { command: "workflow dependencies", orgId: org.id }
|
|
505
|
+
meta: { command: "workflow dependencies", orgId: org.id, ...releaseSnapshotSelectorMeta(selector) }
|
|
491
506
|
};
|
|
492
507
|
}
|
|
493
508
|
async function executeWorkflowContext(parsed, context, api) {
|
|
494
509
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
495
|
-
const
|
|
510
|
+
const selector = readRequiredReleaseSnapshotSelector(parsed);
|
|
511
|
+
const data = await api.getWorkflowContext(session, org.id, selector);
|
|
496
512
|
return {
|
|
497
513
|
data,
|
|
498
514
|
human: renderPrettyJson(data.context),
|
|
499
515
|
kind: "authoring_workflow_context",
|
|
500
|
-
meta: { command: "workflow context", orgId: org.id }
|
|
516
|
+
meta: { command: "workflow context", orgId: org.id, ...releaseSnapshotSelectorMeta(selector) }
|
|
501
517
|
};
|
|
502
518
|
}
|
|
503
519
|
async function executeWorkflowStart(parsed, context, api) {
|
|
@@ -508,10 +524,13 @@ async function executeWorkflowStart(parsed, context, api) {
|
|
|
508
524
|
}
|
|
509
525
|
const inputSpecifier = readOptionalStringFlag(parsed, "input");
|
|
510
526
|
const inputData = inputSpecifier
|
|
511
|
-
? await readJsonInputSpecifier(inputSpecifier, context.stdin, "workflow start")
|
|
527
|
+
? await readJsonInputSpecifier(inputSpecifier, context.stdin, "workflow start", { flag: "input" })
|
|
512
528
|
: undefined;
|
|
513
529
|
const workflowName = readRequiredArg(parsed, "name");
|
|
530
|
+
const resolvedEnvironment = resolveCliEnvironment(parsed, session);
|
|
531
|
+
announceResolvedEnvironment(parsed, context, resolvedEnvironment.environment);
|
|
514
532
|
const data = await api.startWorkflow(session, org.id, workflowName, {
|
|
533
|
+
environment: resolvedEnvironment.environment,
|
|
515
534
|
...(inputData ? { inputData } : {}),
|
|
516
535
|
startEvent: {
|
|
517
536
|
...(readOptionalStringFlag(parsed, "start-event-name")
|
|
@@ -522,24 +541,69 @@ async function executeWorkflowStart(parsed, context, api) {
|
|
|
522
541
|
});
|
|
523
542
|
return {
|
|
524
543
|
data,
|
|
525
|
-
human: renderSuccess(`Started workflow ${workflowName} as ${data.started.workflowId}.`),
|
|
544
|
+
human: renderSuccess(`Started workflow ${workflowName} in ${data.started.environmentKey ?? resolvedEnvironment.environment} as ${data.started.workflowId}.`),
|
|
526
545
|
kind: "runtime_workflow_start",
|
|
527
|
-
meta: { command: "workflow start", orgId: org.id }
|
|
546
|
+
meta: { command: "workflow start", environment: data.started.environmentKey ?? resolvedEnvironment.environment, orgId: org.id }
|
|
528
547
|
};
|
|
529
548
|
}
|
|
530
|
-
async function
|
|
549
|
+
async function executeWorkflowNodeTest(parsed, context, api) {
|
|
531
550
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
|
|
551
|
+
const workflowName = readRequiredArg(parsed, "workflow-name");
|
|
552
|
+
const nodeId = readRequiredArg(parsed, "node-id");
|
|
553
|
+
const workspacePath = readOptionalStringFlag(parsed, "workspace") ?? context.cwd;
|
|
554
|
+
const environment = readOptionalStringFlag(parsed, "environment");
|
|
555
|
+
const inputSpecifier = readOptionalStringFlag(parsed, "input");
|
|
556
|
+
const inputData = inputSpecifier
|
|
557
|
+
? await readJsonInputSpecifier(inputSpecifier, context.stdin, "test node", { flag: "input" })
|
|
558
|
+
: undefined;
|
|
559
|
+
const data = await api.testWorkflowNode(session, org.id, workflowName, nodeId, {
|
|
560
|
+
...(environment ? { environment } : {}),
|
|
561
|
+
files: await readWorkspaceTestEntries(workspacePath),
|
|
562
|
+
...(inputData ? { input: inputData } : {})
|
|
535
563
|
});
|
|
564
|
+
const test = data.test;
|
|
536
565
|
return {
|
|
537
566
|
data,
|
|
538
|
-
|
|
567
|
+
exitCode: test.status === "passed" ? 0 : 1,
|
|
568
|
+
human: renderWorkflowNodeTestHuman(test),
|
|
569
|
+
kind: "workflow_node_test",
|
|
570
|
+
meta: withCliSummary({
|
|
571
|
+
command: "test node",
|
|
572
|
+
nodeId,
|
|
573
|
+
orgId: org.id,
|
|
574
|
+
workflowName
|
|
575
|
+
}, summarizeWorkflowNodeTest(test))
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
async function executeReleaseList(parsed, context, api) {
|
|
579
|
+
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
580
|
+
const [data, deploymentData] = await Promise.all([
|
|
581
|
+
api.listReleases(session, org.id, {
|
|
582
|
+
...optionalNumberValue("limit", readOptionalNumberFlag(parsed, "limit"))
|
|
583
|
+
}),
|
|
584
|
+
api.listEnvironmentDeployments(session, org.id, { limit: 100 })
|
|
585
|
+
]);
|
|
586
|
+
return {
|
|
587
|
+
data: { ...data, deployments: deploymentData.deployments },
|
|
588
|
+
human: renderTable(data.releases.map((release) => {
|
|
589
|
+
const deploymentsForRelease = deploymentData.deployments.filter((deployment) => deployment.releaseId === release.id);
|
|
590
|
+
const latestDeployment = deploymentsForRelease[0] ?? null;
|
|
591
|
+
return {
|
|
592
|
+
createdAt: release.createdAt,
|
|
593
|
+
id: release.id,
|
|
594
|
+
state: typeof release.state === "string" ? release.state : "",
|
|
595
|
+
latestDeployment: latestDeployment ? `${latestDeployment.environmentKey}:${latestDeployment.status}` : "",
|
|
596
|
+
liveEnvironments: deploymentsForRelease
|
|
597
|
+
.filter((deployment) => deployment.isLive)
|
|
598
|
+
.map((deployment) => deployment.environmentKey)
|
|
599
|
+
.join(", ")
|
|
600
|
+
};
|
|
601
|
+
}), [
|
|
539
602
|
{ key: "id", label: "Release" },
|
|
540
|
-
{ key: "
|
|
541
|
-
{ key: "
|
|
542
|
-
{ key: "
|
|
603
|
+
{ key: "state", label: "Artifact state" },
|
|
604
|
+
{ key: "liveEnvironments", label: "Live environments" },
|
|
605
|
+
{ key: "latestDeployment", label: "Latest deployment" },
|
|
606
|
+
{ key: "createdAt", label: "Created" }
|
|
543
607
|
]),
|
|
544
608
|
kind: "releases_release_list",
|
|
545
609
|
meta: { command: "release list", orgId: org.id }
|
|
@@ -555,88 +619,265 @@ async function executeReleaseGet(parsed, context, api) {
|
|
|
555
619
|
meta: { command: "release get", orgId: org.id }
|
|
556
620
|
};
|
|
557
621
|
}
|
|
558
|
-
async function
|
|
559
|
-
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
560
|
-
const data = await api.getDraftChanges(session, org.id);
|
|
561
|
-
return {
|
|
562
|
-
data,
|
|
563
|
-
human: renderTable(data.changes, [
|
|
564
|
-
{ key: "changedAt", label: "Changed" },
|
|
565
|
-
{ key: "action", label: "Action" },
|
|
566
|
-
{ key: "entityType", label: "Entity" },
|
|
567
|
-
{ key: "entityKey", label: "Key" }
|
|
568
|
-
]),
|
|
569
|
-
kind: "releases_release_changes",
|
|
570
|
-
meta: { command: "release changes", orgId: org.id }
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
|
-
async function executeReleaseDeploymentGet(parsed, context, api) {
|
|
622
|
+
async function executeReleaseValidate(parsed, context, api) {
|
|
574
623
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
575
|
-
const
|
|
624
|
+
const releaseId = readRequiredArg(parsed, "release-id");
|
|
625
|
+
const environment = readOptionalStringFlag(parsed, "environment");
|
|
626
|
+
const data = await api.validateRelease(session, org.id, {
|
|
627
|
+
...(environment ? { environment } : {}),
|
|
628
|
+
releaseId
|
|
629
|
+
});
|
|
630
|
+
const validation = data.validation;
|
|
631
|
+
const human = validation.releaseReady
|
|
632
|
+
? renderSuccess(`Release ${validation.releaseId} passed release validation${environment ? ` for ${environment}` : ""}.`)
|
|
633
|
+
: [
|
|
634
|
+
`Release ${validation.releaseId} did not pass release validation${environment ? ` for ${environment}` : ""}.`,
|
|
635
|
+
renderTable(validation.diagnostics, [
|
|
636
|
+
{ key: "gate", label: "Gate" },
|
|
637
|
+
{ key: "severity", label: "Severity" },
|
|
638
|
+
{ key: "code", label: "Code" },
|
|
639
|
+
{ key: "path", label: "Path" },
|
|
640
|
+
{ key: "message", label: "Message" }
|
|
641
|
+
])
|
|
642
|
+
].join("\n");
|
|
576
643
|
return {
|
|
577
644
|
data,
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
645
|
+
...(validation.releaseReady ? {} : { exitCode: 1 }),
|
|
646
|
+
human,
|
|
647
|
+
kind: "releases_release_validate",
|
|
648
|
+
meta: withCliSummary({ command: "release validate", ...(environment ? { environment } : {}), orgId: org.id }, summarizeReleaseValidation(validation))
|
|
581
649
|
};
|
|
582
650
|
}
|
|
583
|
-
async function
|
|
651
|
+
async function executeReleaseSource(parsed, context, api) {
|
|
584
652
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
585
|
-
const
|
|
653
|
+
const releaseId = readRequiredArg(parsed, "release-id");
|
|
654
|
+
const outPath = readRequiredStringFlag(parsed, "out");
|
|
655
|
+
const data = await api.getReleaseSource(session, org.id, releaseId);
|
|
656
|
+
const files = [
|
|
657
|
+
...data.source.files.map((file) => ({
|
|
658
|
+
content: file.content,
|
|
659
|
+
path: file.path
|
|
660
|
+
})),
|
|
661
|
+
...data.source.assets.map((asset) => ({
|
|
662
|
+
content: asset.content,
|
|
663
|
+
path: asset.path
|
|
664
|
+
}))
|
|
665
|
+
];
|
|
666
|
+
await writeReleaseSourceFiles(outPath, {
|
|
667
|
+
files,
|
|
668
|
+
metadata: {
|
|
669
|
+
fileCount: files.length,
|
|
670
|
+
releaseId,
|
|
671
|
+
source: "release"
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
const summary = `Release ${releaseId} source written to ${outPath}.`;
|
|
586
675
|
return {
|
|
587
|
-
data
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
676
|
+
data: {
|
|
677
|
+
fileCount: files.length,
|
|
678
|
+
releaseId,
|
|
679
|
+
out: outPath
|
|
680
|
+
},
|
|
681
|
+
human: renderSuccess(summary),
|
|
682
|
+
kind: "releases_release_source",
|
|
683
|
+
meta: withCliSummary({ command: "release source", orgId: org.id, releaseId }, summary)
|
|
591
684
|
};
|
|
592
685
|
}
|
|
593
|
-
async function
|
|
686
|
+
async function executeReleaseCreate(parsed, context, api) {
|
|
594
687
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
595
|
-
const
|
|
688
|
+
const workspacePath = readRequiredArg(parsed, "workspace");
|
|
689
|
+
if (isZipArchivePath(workspacePath)) {
|
|
690
|
+
const data = await api.createReleaseArchive(session, org.id, await readArchiveBytes(workspacePath, "release create"));
|
|
691
|
+
const summary = summarizeReleaseCreation(data.created.release.id);
|
|
692
|
+
return {
|
|
693
|
+
data,
|
|
694
|
+
human: renderSuccess(`${summary} Source: ${workspacePath}.`),
|
|
695
|
+
kind: "releases_release_create",
|
|
696
|
+
meta: withCliSummary({ command: "release create", orgId: org.id, releaseId: data.created.release.id }, summary)
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
const files = await readImportEntries(workspacePath);
|
|
700
|
+
const data = await api.createRelease(session, org.id, {
|
|
701
|
+
files
|
|
702
|
+
});
|
|
703
|
+
const summary = summarizeReleaseCreation(data.created.release.id);
|
|
596
704
|
return {
|
|
597
705
|
data,
|
|
598
|
-
human: renderSuccess(
|
|
599
|
-
kind: "
|
|
600
|
-
meta: { command: "release
|
|
706
|
+
human: renderSuccess(`${summary} Source: ${workspacePath}.`),
|
|
707
|
+
kind: "releases_release_create",
|
|
708
|
+
meta: withCliSummary({ command: "release create", orgId: org.id, releaseId: data.created.release.id }, summary)
|
|
601
709
|
};
|
|
602
710
|
}
|
|
603
|
-
async function
|
|
711
|
+
async function executeEnvironment(parsed, context, api) {
|
|
604
712
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
713
|
+
switch (parsed.definition.id) {
|
|
714
|
+
case "environment.list": {
|
|
715
|
+
const data = await api.listEnvironments(session, org.id);
|
|
716
|
+
return {
|
|
717
|
+
data,
|
|
718
|
+
human: renderTable(data.environments.map((entry) => ({
|
|
719
|
+
currentDeploymentId: entry.environment.currentDeploymentId,
|
|
720
|
+
isProductionTarget: entry.environment.isProductionTarget,
|
|
721
|
+
key: entry.environment.key,
|
|
722
|
+
latestStatus: entry.latestDeployment?.status ?? "",
|
|
723
|
+
liveDeploymentId: entry.liveDeployment?.id ?? "",
|
|
724
|
+
name: entry.environment.name,
|
|
725
|
+
status: entry.environment.status
|
|
726
|
+
})), [
|
|
727
|
+
{ key: "key", label: "Environment" },
|
|
728
|
+
{ key: "name", label: "Name" },
|
|
729
|
+
{ key: "status", label: "Status" },
|
|
730
|
+
{ key: "isProductionTarget", label: "Production" },
|
|
731
|
+
{ key: "liveDeploymentId", label: "Live deployment" },
|
|
732
|
+
{ key: "latestStatus", label: "Latest" }
|
|
733
|
+
]),
|
|
734
|
+
kind: "environment_list",
|
|
735
|
+
meta: { command: "environment list", orgId: org.id }
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
case "environment.get": {
|
|
739
|
+
const data = await api.getEnvironment(session, org.id, readRequiredArg(parsed, "environment"));
|
|
740
|
+
return {
|
|
741
|
+
data,
|
|
742
|
+
human: renderPrettyJson(data),
|
|
743
|
+
kind: "environment_get",
|
|
744
|
+
meta: { command: "environment get", orgId: org.id }
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
case "environment.create": {
|
|
748
|
+
const key = readRequiredArg(parsed, "environment");
|
|
749
|
+
const copyFrom = readOptionalStringFlag(parsed, "copy-from");
|
|
750
|
+
const copyFromEnvironmentId = copyFrom
|
|
751
|
+
? await resolveEnvironmentIdForCli(session, org.id, copyFrom, api)
|
|
752
|
+
: undefined;
|
|
753
|
+
const data = await api.createEnvironment(session, org.id, {
|
|
754
|
+
...(copyFromEnvironmentId ? { copyFromEnvironmentId } : {}),
|
|
755
|
+
key,
|
|
756
|
+
...(readOptionalStringFlag(parsed, "name") ? { name: readOptionalStringFlag(parsed, "name") } : {})
|
|
757
|
+
});
|
|
758
|
+
return {
|
|
759
|
+
data,
|
|
760
|
+
human: renderSuccess(`Created environment ${data.environment.key}${copyFrom ? ` from ${copyFrom}` : ""}.`),
|
|
761
|
+
kind: "environment_create",
|
|
762
|
+
meta: {
|
|
763
|
+
command: "environment create",
|
|
764
|
+
...(copyFrom ? { copyFrom } : {}),
|
|
765
|
+
environment: data.environment.key,
|
|
766
|
+
orgId: org.id
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
case "environment.rename": {
|
|
771
|
+
const data = await api.renameEnvironment(session, org.id, readRequiredArg(parsed, "environment"), {
|
|
772
|
+
name: readRequiredArg(parsed, "name")
|
|
773
|
+
});
|
|
774
|
+
return {
|
|
775
|
+
data,
|
|
776
|
+
human: renderSuccess(`Renamed environment ${data.environment.key}.`),
|
|
777
|
+
kind: "environment_rename",
|
|
778
|
+
meta: { command: "environment rename", environment: data.environment.key, orgId: org.id }
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
case "environment.archive": {
|
|
782
|
+
const environment = readRequiredArg(parsed, "environment");
|
|
783
|
+
await confirmDestructive(parsed, context, `Archive environment ${environment}?`);
|
|
784
|
+
const data = await api.archiveEnvironment(session, org.id, environment);
|
|
785
|
+
return {
|
|
786
|
+
data,
|
|
787
|
+
human: renderSuccess(`Archived environment ${data.environment.key}.`),
|
|
788
|
+
kind: "environment_archive",
|
|
789
|
+
meta: { command: "environment archive", environment: data.environment.key, orgId: org.id }
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
case "environment.deploy": {
|
|
793
|
+
const environment = readRequiredArg(parsed, "environment");
|
|
794
|
+
const releaseId = readRequiredArg(parsed, "release");
|
|
795
|
+
const policy = readOptionalStringFlag(parsed, "policy");
|
|
796
|
+
const data = await api.deployEnvironment(session, org.id, environment, {
|
|
797
|
+
...(policy ? { policy } : {}),
|
|
798
|
+
releaseId
|
|
799
|
+
});
|
|
800
|
+
return {
|
|
801
|
+
data,
|
|
802
|
+
human: renderSuccess(`Deployed release ${releaseId} to ${environment} as ${data.deployment.id}.`),
|
|
803
|
+
kind: "environment_deploy",
|
|
804
|
+
meta: { command: "environment deploy", deploymentId: data.deployment.id, environment, orgId: org.id, releaseId }
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
case "environment.undeploy": {
|
|
808
|
+
const environment = readRequiredArg(parsed, "environment");
|
|
809
|
+
await confirmDestructive(parsed, context, `Undeploy the live deployment from ${environment}?`);
|
|
810
|
+
const policy = readOptionalStringFlag(parsed, "policy");
|
|
811
|
+
const data = await api.undeployEnvironment(session, org.id, environment, {
|
|
812
|
+
...(policy ? { policy } : {})
|
|
813
|
+
});
|
|
814
|
+
return {
|
|
815
|
+
data,
|
|
816
|
+
human: renderSuccess(`Undeployed environment ${environment}.`),
|
|
817
|
+
kind: "environment_undeploy",
|
|
818
|
+
meta: { command: "environment undeploy", deploymentId: data.deployment.id, environment, orgId: org.id }
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
default:
|
|
822
|
+
throw genericProblem(`Unhandled environment command ${parsed.definition.id}.`, parsed.definition.id);
|
|
823
|
+
}
|
|
613
824
|
}
|
|
614
|
-
async function
|
|
825
|
+
async function executeDeployment(parsed, context, api) {
|
|
615
826
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
827
|
+
switch (parsed.definition.id) {
|
|
828
|
+
case "deployment.list": {
|
|
829
|
+
const { environment } = resolveCliEnvironment(parsed, session);
|
|
830
|
+
const data = await api.listEnvironmentDeployments(session, org.id, {
|
|
831
|
+
environment,
|
|
832
|
+
...optionalNumberValue("limit", readOptionalNumberFlag(parsed, "limit"))
|
|
833
|
+
});
|
|
834
|
+
return {
|
|
835
|
+
data,
|
|
836
|
+
human: renderTable(data.deployments.map((deployment) => ({
|
|
837
|
+
...deployment,
|
|
838
|
+
live: deployment.isLive ? "yes" : ""
|
|
839
|
+
})), [
|
|
840
|
+
{ key: "id", label: "Deployment" },
|
|
841
|
+
{ key: "environmentKey", label: "Environment" },
|
|
842
|
+
{ key: "live", label: "Live" },
|
|
843
|
+
{ key: "releaseId", label: "Release" },
|
|
844
|
+
{ key: "status", label: "Status" },
|
|
845
|
+
{ key: "coreDeploymentId", label: "Core deployment" },
|
|
846
|
+
{ key: "updatedAt", label: "Updated" }
|
|
847
|
+
]),
|
|
848
|
+
kind: "deployment_list",
|
|
849
|
+
meta: { command: "deployment list", environment, orgId: org.id }
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
case "deployment.get": {
|
|
853
|
+
const environment = readOptionalStringFlag(parsed, "environment");
|
|
854
|
+
const data = await api.getEnvironmentDeployment(session, org.id, readRequiredArg(parsed, "deployment"), {
|
|
855
|
+
...(environment ? { environment } : {})
|
|
856
|
+
});
|
|
857
|
+
return {
|
|
858
|
+
data,
|
|
859
|
+
human: renderPrettyJson(data.deployment),
|
|
860
|
+
kind: "deployment_get",
|
|
861
|
+
meta: { command: "deployment get", deploymentId: data.deployment.id, ...(environment ? { environment } : {}), orgId: org.id }
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
default:
|
|
865
|
+
throw genericProblem(`Unhandled deployment command ${parsed.definition.id}.`, parsed.definition.id);
|
|
866
|
+
}
|
|
624
867
|
}
|
|
625
|
-
async function
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
human: renderSuccess(`Deactivated release ${parsed.args["release-id"]}.`),
|
|
633
|
-
kind: "releases_release_deactivate",
|
|
634
|
-
meta: { command: "release deactivate", orgId: org.id }
|
|
635
|
-
};
|
|
868
|
+
async function resolveEnvironmentIdForCli(session, orgId, environment, api) {
|
|
869
|
+
const data = await api.listEnvironments(session, orgId);
|
|
870
|
+
const match = data.environments.find((entry) => entry.environment.id === environment || entry.environment.key === environment);
|
|
871
|
+
if (!match) {
|
|
872
|
+
throw notFoundProblem(`Environment '${environment}' was not found.`, "environment create");
|
|
873
|
+
}
|
|
874
|
+
return match.environment.id;
|
|
636
875
|
}
|
|
637
876
|
async function executeRunList(parsed, context, api) {
|
|
638
877
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
878
|
+
const resolvedEnvironment = resolveCliEnvironment(parsed, session);
|
|
639
879
|
const data = await api.listRuns(session, org.id, {
|
|
880
|
+
environment: resolvedEnvironment.environment,
|
|
640
881
|
...optionalNumberValue("limit", readOptionalNumberFlag(parsed, "limit")),
|
|
641
882
|
...(readOptionalStringFlag(parsed, "status") ? { status: readOptionalStringFlag(parsed, "status") } : {})
|
|
642
883
|
});
|
|
@@ -645,11 +886,12 @@ async function executeRunList(parsed, context, api) {
|
|
|
645
886
|
human: renderTable(data.runs, [
|
|
646
887
|
{ key: "workflowId", label: "Workflow ID" },
|
|
647
888
|
{ key: "status", label: "Status" },
|
|
889
|
+
{ key: "environmentKey", label: "Environment" },
|
|
648
890
|
{ key: "releaseId", label: "Release" },
|
|
649
891
|
{ key: "startTime", label: "Started" }
|
|
650
892
|
]),
|
|
651
893
|
kind: "runtime_run_list",
|
|
652
|
-
meta: { command: "run list", orgId: org.id }
|
|
894
|
+
meta: { command: "run list", environment: resolvedEnvironment.environment, orgId: org.id }
|
|
653
895
|
};
|
|
654
896
|
}
|
|
655
897
|
async function executeRunGet(parsed, context, api) {
|
|
@@ -737,7 +979,7 @@ async function executeTaskGet(parsed, context, api) {
|
|
|
737
979
|
}
|
|
738
980
|
async function executeTaskComplete(parsed, context, api) {
|
|
739
981
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
740
|
-
const outputData = await readJsonInputSpecifier(String(parsed.flags.output), context.stdin, "task complete");
|
|
982
|
+
const outputData = await readJsonInputSpecifier(String(parsed.flags.output), context.stdin, "task complete", { flag: "output" });
|
|
741
983
|
const data = await api.completeTask(session, org.id, readRequiredArg(parsed, "task-id"), {
|
|
742
984
|
outputData,
|
|
743
985
|
workflowId: String(parsed.flags.run)
|
|
@@ -753,8 +995,9 @@ async function executeOrgModel(parsed, context, api) {
|
|
|
753
995
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
754
996
|
const section = parsed.definition.path[1] ?? "";
|
|
755
997
|
const verb = parsed.definition.path[2] ?? "";
|
|
998
|
+
const selector = readRequiredReleaseSnapshotSelector(parsed);
|
|
756
999
|
if (section === "operations") {
|
|
757
|
-
const editor = (await api.getOperationsEditor(session, org.id)).editor;
|
|
1000
|
+
const editor = (await api.getOperationsEditor(session, org.id, selector)).editor;
|
|
758
1001
|
const record = verb === "get"
|
|
759
1002
|
? requireFound(editor.operations.find((entry) => entry.name === parsed.args.name), `Operation '${parsed.args.name}' was not found.`, parsed.definition.id)
|
|
760
1003
|
: undefined;
|
|
@@ -764,56 +1007,20 @@ async function executeOrgModel(parsed, context, api) {
|
|
|
764
1007
|
? renderPrettyJson(record)
|
|
765
1008
|
: renderTable(editor.operations, [
|
|
766
1009
|
{ key: "name", label: "Name" },
|
|
767
|
-
{ key: "
|
|
768
|
-
{ key: "action", label: "Action" }
|
|
769
|
-
]),
|
|
770
|
-
kind: `authoring_${sanitizeKind(section)}_${verb}`,
|
|
771
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
772
|
-
};
|
|
773
|
-
}
|
|
774
|
-
if (section === "connectors") {
|
|
775
|
-
const editor = (await api.getConnectorsEditor(session, org.id)).editor;
|
|
776
|
-
const record = verb === "get"
|
|
777
|
-
? requireFound(editor.connectors.find((entry) => entry.name === parsed.args.name), `Connector '${parsed.args.name}' was not found.`, parsed.definition.id)
|
|
778
|
-
: undefined;
|
|
779
|
-
return {
|
|
780
|
-
data: verb === "get" ? { connector: record } : { connectors: editor.connectors },
|
|
781
|
-
human: verb === "get"
|
|
782
|
-
? renderPrettyJson(record)
|
|
783
|
-
: renderTable(editor.connectors, [
|
|
784
|
-
{ key: "name", label: "Name" },
|
|
785
|
-
{ key: "type", label: "Type" }
|
|
786
|
-
]),
|
|
787
|
-
kind: `authoring_${sanitizeKind(section)}_${verb}`,
|
|
788
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
789
|
-
};
|
|
790
|
-
}
|
|
791
|
-
if (section === "mcp-servers") {
|
|
792
|
-
const editor = (await api.getMcpServersEditor(session, org.id)).editor;
|
|
793
|
-
const record = verb === "get"
|
|
794
|
-
? requireFound(editor.servers.find((entry) => entry.name === parsed.args.name), `MCP server '${parsed.args.name}' was not found.`, parsed.definition.id)
|
|
795
|
-
: undefined;
|
|
796
|
-
return {
|
|
797
|
-
data: verb === "get" ? { server: record } : { servers: editor.servers },
|
|
798
|
-
human: verb === "get"
|
|
799
|
-
? renderPrettyJson(record)
|
|
800
|
-
: renderTable(editor.servers, [
|
|
801
|
-
{ key: "name", label: "Name" },
|
|
802
|
-
{ key: "transport", label: "Transport" },
|
|
803
|
-
{ key: "url", label: "URL" }
|
|
1010
|
+
{ key: "command", label: "Command" }
|
|
804
1011
|
]),
|
|
805
1012
|
kind: `authoring_${sanitizeKind(section)}_${verb}`,
|
|
806
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1013
|
+
meta: { command: parsed.definition.path.join(" "), orgId: org.id, ...releaseSnapshotSelectorMeta(selector) }
|
|
807
1014
|
};
|
|
808
1015
|
}
|
|
809
|
-
const management = (await api.getOrganizationManagementContext(session, org.id)).context;
|
|
1016
|
+
const management = (await api.getOrganizationManagementContext(session, org.id, selector)).context;
|
|
810
1017
|
if (section === "capabilities" && verb === "get") {
|
|
811
|
-
const detail = await api.getCapabilityEditor(session, org.id, readRequiredArg(parsed, "name"));
|
|
1018
|
+
const detail = await api.getCapabilityEditor(session, org.id, readRequiredArg(parsed, "name"), selector);
|
|
812
1019
|
return {
|
|
813
1020
|
data: detail,
|
|
814
1021
|
human: renderPrettyJson(detail.editor),
|
|
815
1022
|
kind: "authoring_capabilities_get",
|
|
816
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1023
|
+
meta: { command: parsed.definition.path.join(" "), orgId: org.id, ...releaseSnapshotSelectorMeta(selector) }
|
|
817
1024
|
};
|
|
818
1025
|
}
|
|
819
1026
|
const selection = selectOrgModelSection(management, section, verb === "get" ? parsed.args[section === "assignments" ? "role-name" : "name"] : undefined);
|
|
@@ -821,7 +1028,7 @@ async function executeOrgModel(parsed, context, api) {
|
|
|
821
1028
|
data: selection.data,
|
|
822
1029
|
human: selection.human,
|
|
823
1030
|
kind: `authoring_${sanitizeKind(section)}_${verb}`,
|
|
824
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1031
|
+
meta: { command: parsed.definition.path.join(" "), orgId: org.id, ...releaseSnapshotSelectorMeta(selector) }
|
|
825
1032
|
};
|
|
826
1033
|
}
|
|
827
1034
|
async function executeAccess(parsed, context, api) {
|
|
@@ -985,354 +1192,65 @@ async function executeAccess(parsed, context, api) {
|
|
|
985
1192
|
throw genericProblem(`Unhandled access command ${parsed.definition.id}.`, parsed.definition.id);
|
|
986
1193
|
}
|
|
987
1194
|
}
|
|
988
|
-
async function executeMcp(parsed, context, api) {
|
|
989
|
-
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
990
|
-
switch (parsed.definition.id) {
|
|
991
|
-
case "mcp.list": {
|
|
992
|
-
const data = await api.listOrgMcpServers(session, org.id);
|
|
993
|
-
return {
|
|
994
|
-
data,
|
|
995
|
-
human: renderTable(data.servers, [
|
|
996
|
-
{ key: "slug", label: "Slug" },
|
|
997
|
-
{ key: "displayName", label: "Name" },
|
|
998
|
-
{ key: "status", label: "Status" },
|
|
999
|
-
{ key: "authMode", label: "Auth" },
|
|
1000
|
-
{ key: "url", label: "URL" }
|
|
1001
|
-
]),
|
|
1002
|
-
kind: "mcp_servers_list",
|
|
1003
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1004
|
-
};
|
|
1005
|
-
}
|
|
1006
|
-
case "mcp.get": {
|
|
1007
|
-
const data = await api.getOrgMcpServer(session, org.id, readRequiredArg(parsed, "server"));
|
|
1008
|
-
return {
|
|
1009
|
-
data,
|
|
1010
|
-
human: renderPrettyJson(data),
|
|
1011
|
-
kind: "mcp_servers_get",
|
|
1012
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1013
|
-
};
|
|
1014
|
-
}
|
|
1015
|
-
case "mcp.create": {
|
|
1016
|
-
const authMode = readOptionalMcpAuthMode(parsed);
|
|
1017
|
-
const timeoutMs = readOptionalNumberFlag(parsed, "timeout-ms");
|
|
1018
|
-
const input = {
|
|
1019
|
-
displayName: readRequiredStringFlag(parsed, "name"),
|
|
1020
|
-
slug: readRequiredStringFlag(parsed, "slug"),
|
|
1021
|
-
url: readRequiredStringFlag(parsed, "url"),
|
|
1022
|
-
...(authMode !== undefined ? { authMode } : {}),
|
|
1023
|
-
...(timeoutMs !== undefined ? { timeoutMs } : {})
|
|
1024
|
-
};
|
|
1025
|
-
const data = await api.createOrgMcpServer(session, org.id, input);
|
|
1026
|
-
return {
|
|
1027
|
-
data,
|
|
1028
|
-
human: renderSuccess(`Created MCP server ${data.server.slug}.`),
|
|
1029
|
-
kind: "mcp_servers_create",
|
|
1030
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1031
|
-
};
|
|
1032
|
-
}
|
|
1033
|
-
case "mcp.update": {
|
|
1034
|
-
const authMode = readOptionalMcpAuthMode(parsed);
|
|
1035
|
-
const displayName = readOptionalStringFlag(parsed, "name");
|
|
1036
|
-
const timeoutMs = readOptionalNumberFlag(parsed, "timeout-ms");
|
|
1037
|
-
const url = readOptionalStringFlag(parsed, "url");
|
|
1038
|
-
const disable = parsed.flags.disable === true;
|
|
1039
|
-
const enable = parsed.flags.enable === true;
|
|
1040
|
-
if (disable && enable) {
|
|
1041
|
-
throw usageProblem("Use either --disable or --enable, not both.", parsed.definition.id);
|
|
1042
|
-
}
|
|
1043
|
-
const input = {
|
|
1044
|
-
...(displayName !== undefined ? { displayName } : {}),
|
|
1045
|
-
...(url !== undefined ? { url } : {}),
|
|
1046
|
-
...(authMode !== undefined ? { authMode } : {}),
|
|
1047
|
-
...(disable || enable ? { disabled: disable } : {}),
|
|
1048
|
-
...(timeoutMs !== undefined ? { timeoutMs } : {})
|
|
1049
|
-
};
|
|
1050
|
-
if (Object.keys(input).length === 0) {
|
|
1051
|
-
throw usageProblem("Provide at least one MCP server field to update.", parsed.definition.id);
|
|
1052
|
-
}
|
|
1053
|
-
const data = await api.updateOrgMcpServer(session, org.id, readRequiredArg(parsed, "server"), input);
|
|
1054
|
-
return {
|
|
1055
|
-
data,
|
|
1056
|
-
human: renderSuccess(`Updated MCP server ${data.server.slug}.`),
|
|
1057
|
-
kind: "mcp_servers_update",
|
|
1058
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1059
|
-
};
|
|
1060
|
-
}
|
|
1061
|
-
case "mcp.delete": {
|
|
1062
|
-
const serverRef = readRequiredArg(parsed, "server");
|
|
1063
|
-
await confirmDestructive(parsed, context, `Delete MCP server ${serverRef}?`);
|
|
1064
|
-
await api.deleteOrgMcpServer(session, org.id, serverRef);
|
|
1065
|
-
return {
|
|
1066
|
-
data: {},
|
|
1067
|
-
human: renderSuccess(`Deleted MCP server ${serverRef}.`),
|
|
1068
|
-
kind: "mcp_servers_delete",
|
|
1069
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1070
|
-
};
|
|
1071
|
-
}
|
|
1072
|
-
case "mcp.test": {
|
|
1073
|
-
const data = await api.testOrgMcpServer(session, org.id, readRequiredArg(parsed, "server"));
|
|
1074
|
-
if (!data.ok) {
|
|
1075
|
-
throw genericProblem(data.server.lastErrorMessage ?? `MCP server ${data.server.slug} test failed.`, parsed.definition.id);
|
|
1076
|
-
}
|
|
1077
|
-
return {
|
|
1078
|
-
data,
|
|
1079
|
-
human: renderSuccess(`MCP server ${data.server.slug} is reachable.`),
|
|
1080
|
-
kind: "mcp_servers_test",
|
|
1081
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1082
|
-
};
|
|
1083
|
-
}
|
|
1084
|
-
case "mcp.discover-tools": {
|
|
1085
|
-
const data = await api.discoverOrgMcpServerTools(session, org.id, readRequiredArg(parsed, "server"));
|
|
1086
|
-
return {
|
|
1087
|
-
data,
|
|
1088
|
-
human: data.tools.length === 0
|
|
1089
|
-
? renderSuccess(`Discovered no tools for ${data.server.slug}.`)
|
|
1090
|
-
: renderTable(data.tools, [
|
|
1091
|
-
{ key: "toolName", label: "Tool" },
|
|
1092
|
-
{ key: "description", label: "Description" },
|
|
1093
|
-
{ key: "schemaSha256", label: "Schema" }
|
|
1094
|
-
]),
|
|
1095
|
-
kind: "mcp_servers_discover_tools",
|
|
1096
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1097
|
-
};
|
|
1098
|
-
}
|
|
1099
|
-
case "mcp.connect": {
|
|
1100
|
-
const serverRef = readRequiredArg(parsed, "server");
|
|
1101
|
-
const data = await api.connectOrgMcpServerOAuth(session, org.id, serverRef);
|
|
1102
|
-
const explicitOpen = parsed.flags.open === true;
|
|
1103
|
-
const shouldOpen = shouldAutoOpenBrowser({
|
|
1104
|
-
env: context.env,
|
|
1105
|
-
explicitNoOpen: parsed.flags["no-open"] === true,
|
|
1106
|
-
explicitOpen,
|
|
1107
|
-
interactive: isInteractive(context),
|
|
1108
|
-
openBrowserConfig: context.config.openBrowser
|
|
1109
|
-
});
|
|
1110
|
-
let opened = false;
|
|
1111
|
-
if (shouldOpen) {
|
|
1112
|
-
opened = await openBrowser(data.authorizationUrl);
|
|
1113
|
-
if (explicitOpen && !opened) {
|
|
1114
|
-
throw genericProblem(`Could not open a browser automatically. Visit ${data.authorizationUrl}`, parsed.definition.id);
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
const finalSession = parsed.flags.wait === true
|
|
1118
|
-
? await pollMcpOAuthSession(api, session, org.id, serverRef, data.sessionId)
|
|
1119
|
-
: null;
|
|
1120
|
-
if (finalSession && isFailedMcpOAuthSession(finalSession.session.status)) {
|
|
1121
|
-
throw genericProblem(finalSession.session.errorMessage
|
|
1122
|
-
?? `MCP OAuth connection ${finalSession.session.status}.`, parsed.definition.id);
|
|
1123
|
-
}
|
|
1124
|
-
return {
|
|
1125
|
-
data: finalSession ? { ...data, session: finalSession.session } : data,
|
|
1126
|
-
human: finalSession
|
|
1127
|
-
? renderMcpOAuthSessionHuman(data.authorizationUrl, finalSession.session.status)
|
|
1128
|
-
: [
|
|
1129
|
-
"MCP OAuth authorization started.",
|
|
1130
|
-
`Open: ${data.authorizationUrl}`,
|
|
1131
|
-
`Session: ${data.sessionId}`
|
|
1132
|
-
].join("\n"),
|
|
1133
|
-
kind: "mcp_servers_oauth_connect",
|
|
1134
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1135
|
-
};
|
|
1136
|
-
}
|
|
1137
|
-
case "mcp.disconnect": {
|
|
1138
|
-
const serverRef = readRequiredArg(parsed, "server");
|
|
1139
|
-
await confirmDestructive(parsed, context, `Disconnect OAuth for MCP server ${serverRef}?`);
|
|
1140
|
-
const data = await api.disconnectOrgMcpServerOAuth(session, org.id, serverRef);
|
|
1141
|
-
return {
|
|
1142
|
-
data,
|
|
1143
|
-
human: renderSuccess(`Disconnected OAuth for MCP server ${data.server.slug}.`),
|
|
1144
|
-
kind: "mcp_servers_oauth_disconnect",
|
|
1145
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1146
|
-
};
|
|
1147
|
-
}
|
|
1148
|
-
case "mcp.oauth-status": {
|
|
1149
|
-
const data = await api.getOrgMcpServerOAuthStatus(session, org.id, readRequiredArg(parsed, "server"));
|
|
1150
|
-
return {
|
|
1151
|
-
data,
|
|
1152
|
-
human: renderPrettyJson(data.oauth),
|
|
1153
|
-
kind: "mcp_servers_oauth_status",
|
|
1154
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1155
|
-
};
|
|
1156
|
-
}
|
|
1157
|
-
default:
|
|
1158
|
-
throw genericProblem(`Unhandled MCP command ${parsed.definition.id}.`, parsed.definition.id);
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
async function executeSkills(parsed, context, api) {
|
|
1162
|
-
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
1163
|
-
switch (parsed.definition.id) {
|
|
1164
|
-
case "skills.list": {
|
|
1165
|
-
const data = await api.listOrgSkillPackages(session, org.id);
|
|
1166
|
-
return {
|
|
1167
|
-
data,
|
|
1168
|
-
human: renderTable(data.skills, [
|
|
1169
|
-
{ key: "slug", label: "Slug" },
|
|
1170
|
-
{ key: "displayName", label: "Name" },
|
|
1171
|
-
{ key: "status", label: "Status" },
|
|
1172
|
-
{ key: "currentRevision", label: "Revision" },
|
|
1173
|
-
{ key: "currentFileCount", label: "Files" },
|
|
1174
|
-
{ key: "currentPackageSha256", label: "Package SHA" },
|
|
1175
|
-
{ key: "updatedAt", label: "Updated" }
|
|
1176
|
-
]),
|
|
1177
|
-
kind: "skills_list",
|
|
1178
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1179
|
-
};
|
|
1180
|
-
}
|
|
1181
|
-
case "skills.get": {
|
|
1182
|
-
const data = await api.getOrgSkillPackage(session, org.id, readRequiredArg(parsed, "skill"));
|
|
1183
|
-
return {
|
|
1184
|
-
data,
|
|
1185
|
-
human: renderPrettyJson(data),
|
|
1186
|
-
kind: "skills_get",
|
|
1187
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1188
|
-
};
|
|
1189
|
-
}
|
|
1190
|
-
case "skills.create": {
|
|
1191
|
-
const description = readOptionalStringFlag(parsed, "description");
|
|
1192
|
-
const dir = readOptionalStringFlag(parsed, "dir");
|
|
1193
|
-
const files = dir ? await readImportEntries(dir) : undefined;
|
|
1194
|
-
const data = await api.createOrgSkillPackage(session, org.id, {
|
|
1195
|
-
...(description !== undefined ? { description } : {}),
|
|
1196
|
-
displayName: readRequiredStringFlag(parsed, "name"),
|
|
1197
|
-
...(files ? { files } : {}),
|
|
1198
|
-
slug: readRequiredStringFlag(parsed, "slug")
|
|
1199
|
-
});
|
|
1200
|
-
return {
|
|
1201
|
-
data,
|
|
1202
|
-
human: renderSuccess(`Created skill package ${data.skill.slug}.`),
|
|
1203
|
-
kind: "skills_create",
|
|
1204
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1205
|
-
};
|
|
1206
|
-
}
|
|
1207
|
-
case "skills.upload": {
|
|
1208
|
-
const skillRef = readRequiredArg(parsed, "skill");
|
|
1209
|
-
const files = await readImportEntries(readRequiredStringFlag(parsed, "dir"));
|
|
1210
|
-
const data = await api.uploadOrgSkillPackageRevision(session, org.id, skillRef, { files });
|
|
1211
|
-
return {
|
|
1212
|
-
data,
|
|
1213
|
-
human: renderSuccess(`Uploaded skill package ${skillRef} revision ${String(data.revision.revision)}.`),
|
|
1214
|
-
kind: "skills_upload",
|
|
1215
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1216
|
-
};
|
|
1217
|
-
}
|
|
1218
|
-
case "skills.download": {
|
|
1219
|
-
const skillRef = readRequiredArg(parsed, "skill");
|
|
1220
|
-
const revision = readOptionalNumberFlag(parsed, "revision");
|
|
1221
|
-
const targetDir = readRequiredStringFlag(parsed, "dir");
|
|
1222
|
-
const data = revision === undefined
|
|
1223
|
-
? await api.getOrgSkillPackage(session, org.id, skillRef)
|
|
1224
|
-
: await api.getOrgSkillPackageRevision(session, org.id, skillRef, revision);
|
|
1225
|
-
await writeSkillPackageFiles(targetDir, data.files);
|
|
1226
|
-
return {
|
|
1227
|
-
data,
|
|
1228
|
-
human: renderSuccess(`Downloaded ${data.files.length} skill package files to ${resolve(targetDir)}.`),
|
|
1229
|
-
kind: "skills_download",
|
|
1230
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1231
|
-
};
|
|
1232
|
-
}
|
|
1233
|
-
case "skills.update": {
|
|
1234
|
-
const displayName = readOptionalStringFlag(parsed, "name");
|
|
1235
|
-
const description = readOptionalStringFlag(parsed, "description");
|
|
1236
|
-
const disable = parsed.flags.disable === true;
|
|
1237
|
-
const enable = parsed.flags.enable === true;
|
|
1238
|
-
if (disable && enable) {
|
|
1239
|
-
throw usageProblem("Use either --disable or --enable, not both.", parsed.definition.id);
|
|
1240
|
-
}
|
|
1241
|
-
const input = {
|
|
1242
|
-
...(displayName !== undefined ? { displayName } : {}),
|
|
1243
|
-
...(description !== undefined ? { description } : {}),
|
|
1244
|
-
...(disable || enable ? { disabled: disable } : {})
|
|
1245
|
-
};
|
|
1246
|
-
if (Object.keys(input).length === 0) {
|
|
1247
|
-
throw usageProblem("Provide at least one skill package field to update.", parsed.definition.id);
|
|
1248
|
-
}
|
|
1249
|
-
const data = await api.updateOrgSkillPackage(session, org.id, readRequiredArg(parsed, "skill"), input);
|
|
1250
|
-
return {
|
|
1251
|
-
data,
|
|
1252
|
-
human: renderSuccess(`Updated skill package ${data.skill.slug}.`),
|
|
1253
|
-
kind: "skills_update",
|
|
1254
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1255
|
-
};
|
|
1256
|
-
}
|
|
1257
|
-
case "skills.delete": {
|
|
1258
|
-
const skillRef = readRequiredArg(parsed, "skill");
|
|
1259
|
-
await confirmDestructive(parsed, context, `Delete skill package ${skillRef}?`);
|
|
1260
|
-
await api.deleteOrgSkillPackage(session, org.id, skillRef);
|
|
1261
|
-
return {
|
|
1262
|
-
data: {},
|
|
1263
|
-
human: renderSuccess(`Deleted skill package ${skillRef}.`),
|
|
1264
|
-
kind: "skills_delete",
|
|
1265
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1266
|
-
};
|
|
1267
|
-
}
|
|
1268
|
-
default:
|
|
1269
|
-
throw genericProblem(`Unhandled skills command ${parsed.definition.id}.`, parsed.definition.id);
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
async function writeSkillPackageFiles(targetDir, files) {
|
|
1273
|
-
const root = resolve(targetDir);
|
|
1274
|
-
for (const file of files) {
|
|
1275
|
-
const targetPath = resolve(root, file.path);
|
|
1276
|
-
if (!targetPath.startsWith(`${root}/`) && targetPath !== root) {
|
|
1277
|
-
throw usageProblem(`Refusing to write skill package path '${file.path}' outside destination.`, "skills.download");
|
|
1278
|
-
}
|
|
1279
|
-
await mkdir(dirname(targetPath), { recursive: true });
|
|
1280
|
-
await writeFile(targetPath, file.content, "utf8");
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
async function pollMcpOAuthSession(api, session, orgId, serverRef, oauthSessionId) {
|
|
1284
|
-
for (let attempt = 0; attempt < 60; attempt += 1) {
|
|
1285
|
-
const result = await api.getOrgMcpServerOAuthSession(session, orgId, serverRef, oauthSessionId);
|
|
1286
|
-
if (result.session.status !== "pending") {
|
|
1287
|
-
return result;
|
|
1288
|
-
}
|
|
1289
|
-
await new Promise((resolve) => setTimeout(resolve, 2_000));
|
|
1290
|
-
}
|
|
1291
|
-
return await api.getOrgMcpServerOAuthSession(session, orgId, serverRef, oauthSessionId);
|
|
1292
|
-
}
|
|
1293
|
-
function renderMcpOAuthSessionHuman(authorizationUrl, status) {
|
|
1294
|
-
if (status === "connected") {
|
|
1295
|
-
return renderSuccess("MCP OAuth connection completed.");
|
|
1296
|
-
}
|
|
1297
|
-
if (isFailedMcpOAuthSession(status)) {
|
|
1298
|
-
return "MCP OAuth connection failed.";
|
|
1299
|
-
}
|
|
1300
|
-
return [
|
|
1301
|
-
"MCP OAuth authorization is still pending.",
|
|
1302
|
-
`Open: ${authorizationUrl}`
|
|
1303
|
-
].join("\n");
|
|
1304
|
-
}
|
|
1305
|
-
function isFailedMcpOAuthSession(status) {
|
|
1306
|
-
return status === "error" || status === "expired";
|
|
1307
|
-
}
|
|
1308
1195
|
async function executeEnv(parsed, context, api) {
|
|
1309
1196
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
1310
1197
|
switch (parsed.definition.id) {
|
|
1311
1198
|
case "env.list": {
|
|
1312
|
-
const
|
|
1199
|
+
const environment = readCliEnvironmentFlag(parsed);
|
|
1200
|
+
const data = await api.listRuntimeVariables(session, org.id, {
|
|
1201
|
+
...(environment ? { environment } : {})
|
|
1202
|
+
});
|
|
1313
1203
|
return {
|
|
1314
1204
|
data,
|
|
1315
1205
|
human: renderTable(data.variables, [
|
|
1316
1206
|
{ key: "name", label: "Name" },
|
|
1317
1207
|
{ key: "value", label: "Value" },
|
|
1208
|
+
{ key: "environmentKey", label: "Environment" },
|
|
1318
1209
|
{ key: "updatedAt", label: "Updated" }
|
|
1319
1210
|
]),
|
|
1320
1211
|
kind: "authoring_env_list",
|
|
1321
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1212
|
+
meta: { command: parsed.definition.path.join(" "), ...(environment ? { environment } : {}), orgId: org.id }
|
|
1322
1213
|
};
|
|
1323
1214
|
}
|
|
1324
1215
|
case "env.get": {
|
|
1325
|
-
const
|
|
1216
|
+
const environment = readRequiredCliEnvironmentFlag(parsed);
|
|
1217
|
+
const variables = (await api.listRuntimeVariables(session, org.id, {
|
|
1218
|
+
environment
|
|
1219
|
+
})).variables;
|
|
1326
1220
|
const variable = requireFound(variables.find((entry) => entry.name === readRequiredArg(parsed, "name")) ?? null, `Environment variable '${parsed.args.name}' was not found.`, parsed.definition.id);
|
|
1327
1221
|
return {
|
|
1328
1222
|
data: { variable },
|
|
1329
1223
|
human: renderPrettyJson(variable),
|
|
1330
1224
|
kind: "authoring_env_get",
|
|
1331
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1225
|
+
meta: { command: parsed.definition.path.join(" "), environment, orgId: org.id }
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
case "env.set": {
|
|
1229
|
+
const environment = readRequiredCliEnvironmentFlag(parsed);
|
|
1230
|
+
const name = readRequiredArg(parsed, "name");
|
|
1231
|
+
announceResolvedEnvironment(parsed, context, environment);
|
|
1232
|
+
const data = await api.upsertRuntimeVariable(session, org.id, {
|
|
1233
|
+
environment,
|
|
1234
|
+
name,
|
|
1235
|
+
value: readRequiredStringFlagPreservingEmpty(parsed, "value")
|
|
1236
|
+
});
|
|
1237
|
+
return {
|
|
1238
|
+
data: {
|
|
1239
|
+
variable: {
|
|
1240
|
+
environmentKey: data.variable.environmentKey,
|
|
1241
|
+
name: data.variable.name,
|
|
1242
|
+
status: "defined",
|
|
1243
|
+
updatedAt: data.variable.updatedAt
|
|
1244
|
+
}
|
|
1245
|
+
},
|
|
1246
|
+
human: renderSuccess(`Saved runtime variable '${data.variable.name}' in ${environment}.`),
|
|
1247
|
+
kind: "authoring_env_set",
|
|
1248
|
+
meta: { command: parsed.definition.path.join(" "), environment, orgId: org.id }
|
|
1332
1249
|
};
|
|
1333
1250
|
}
|
|
1334
1251
|
case "env.replace": {
|
|
1335
|
-
const
|
|
1252
|
+
const environment = readRequiredCliEnvironmentFlag(parsed);
|
|
1253
|
+
const body = await readJsonInputSpecifier(String(parsed.flags.file), context.stdin, parsed.definition.id, { flag: "file" });
|
|
1336
1254
|
const rawVariables = body.variables;
|
|
1337
1255
|
if (!Array.isArray(rawVariables)) {
|
|
1338
1256
|
throw usageProblem("Environment replace payload must contain a variables array.", parsed.definition.id);
|
|
@@ -1346,33 +1264,24 @@ async function executeEnv(parsed, context, api) {
|
|
|
1346
1264
|
value: String(entry.value)
|
|
1347
1265
|
};
|
|
1348
1266
|
});
|
|
1349
|
-
const current = (await api.listRuntimeVariables(session, org.id
|
|
1267
|
+
const current = (await api.listRuntimeVariables(session, org.id, {
|
|
1268
|
+
environment
|
|
1269
|
+
})).variables;
|
|
1350
1270
|
const diff = diffVariables(current, nextVariables);
|
|
1271
|
+
announceResolvedEnvironment(parsed, context, environment);
|
|
1351
1272
|
if (!parsed.json) {
|
|
1352
1273
|
context.stdout.write(`${renderDiffSummary(diff)}\n`);
|
|
1353
1274
|
}
|
|
1354
|
-
await confirmDestructive(parsed, context, `Replace
|
|
1355
|
-
const data = await api.replaceRuntimeVariables(session, org.id, {
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
human: renderSuccess(`Replaced ${data.variables.length} environment variables.`),
|
|
1359
|
-
kind: "authoring_env_replace",
|
|
1360
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1361
|
-
};
|
|
1362
|
-
}
|
|
1363
|
-
case "env.import": {
|
|
1364
|
-
const filePath = readRequiredArg(parsed, "path-to-.env");
|
|
1365
|
-
const content = await readTextInputSpecifier(`@${filePath}`, context.stdin, parsed.definition.id);
|
|
1366
|
-
const preview = await parseEnvFile(filePath);
|
|
1367
|
-
const data = await api.importRuntimeVariables(session, org.id, {
|
|
1368
|
-
content,
|
|
1369
|
-
fileName: filePath
|
|
1275
|
+
await confirmDestructive(parsed, context, `Replace runtime variables for ${environment} in ${org.slug}?`);
|
|
1276
|
+
const data = await api.replaceRuntimeVariables(session, org.id, {
|
|
1277
|
+
environment,
|
|
1278
|
+
variables: nextVariables
|
|
1370
1279
|
});
|
|
1371
1280
|
return {
|
|
1372
1281
|
data,
|
|
1373
|
-
human: renderSuccess(`
|
|
1374
|
-
kind: "
|
|
1375
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1282
|
+
human: renderSuccess(`Replaced ${data.variables.length} environment variables in ${environment}.`),
|
|
1283
|
+
kind: "authoring_env_replace",
|
|
1284
|
+
meta: { command: parsed.definition.path.join(" "), environment, orgId: org.id }
|
|
1376
1285
|
};
|
|
1377
1286
|
}
|
|
1378
1287
|
default:
|
|
@@ -1383,40 +1292,50 @@ async function executeSecrets(parsed, context, api) {
|
|
|
1383
1292
|
const { org, session } = await resolveOrgScope(parsed, context, api);
|
|
1384
1293
|
switch (parsed.definition.id) {
|
|
1385
1294
|
case "secrets.list": {
|
|
1386
|
-
const
|
|
1295
|
+
const environment = readCliEnvironmentFlag(parsed);
|
|
1296
|
+
const data = await api.listOrgSecrets(session, org.id, {
|
|
1297
|
+
...(environment ? { environment } : {})
|
|
1298
|
+
});
|
|
1387
1299
|
return {
|
|
1388
1300
|
data,
|
|
1389
1301
|
human: renderTable(data.secrets, [
|
|
1390
1302
|
{ key: "name", label: "Name" },
|
|
1391
1303
|
{ key: "hasValue", label: "Defined" },
|
|
1304
|
+
{ key: "environmentKey", label: "Environment" },
|
|
1392
1305
|
{ key: "updatedAt", label: "Updated" }
|
|
1393
1306
|
]),
|
|
1394
1307
|
kind: "authoring_secrets_list",
|
|
1395
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1308
|
+
meta: { command: parsed.definition.path.join(" "), ...(environment ? { environment } : {}), orgId: org.id }
|
|
1396
1309
|
};
|
|
1397
1310
|
}
|
|
1398
1311
|
case "secrets.set": {
|
|
1312
|
+
const environment = readRequiredCliEnvironmentFlag(parsed);
|
|
1399
1313
|
const value = await readTextInputSpecifier("-", context.stdin, parsed.definition.id);
|
|
1314
|
+
announceResolvedEnvironment(parsed, context, environment);
|
|
1400
1315
|
const data = await api.upsertOrgSecret(session, org.id, {
|
|
1316
|
+
environment,
|
|
1401
1317
|
name: readRequiredArg(parsed, "name"),
|
|
1402
1318
|
value: value.replace(/\r?\n$/u, "")
|
|
1403
1319
|
});
|
|
1404
1320
|
return {
|
|
1405
1321
|
data,
|
|
1406
|
-
human: renderSuccess(`Saved secret '${data.secret.name}'.`),
|
|
1322
|
+
human: renderSuccess(`Saved secret '${data.secret.name}' in ${environment}.`),
|
|
1407
1323
|
kind: "authoring_secrets_set",
|
|
1408
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1324
|
+
meta: { command: parsed.definition.path.join(" "), environment, orgId: org.id }
|
|
1409
1325
|
};
|
|
1410
1326
|
}
|
|
1411
1327
|
case "secrets.delete": {
|
|
1328
|
+
const environment = readRequiredCliEnvironmentFlag(parsed);
|
|
1412
1329
|
const name = readRequiredArg(parsed, "name");
|
|
1413
|
-
await confirmDestructive(parsed, context, `Delete secret ${name} from ${org.slug}?`);
|
|
1414
|
-
const data = await api.deleteOrgSecret(session, org.id, name
|
|
1330
|
+
await confirmDestructive(parsed, context, `Delete secret ${name} from ${environment} in ${org.slug}?`);
|
|
1331
|
+
const data = await api.deleteOrgSecret(session, org.id, name, {
|
|
1332
|
+
environment
|
|
1333
|
+
});
|
|
1415
1334
|
return {
|
|
1416
1335
|
data,
|
|
1417
|
-
human: renderSuccess(data.deleted ? `Deleted secret '${name}'.` : `Secret '${name}' was not defined.`),
|
|
1336
|
+
human: renderSuccess(data.deleted ? `Deleted secret '${name}' from ${environment}.` : `Secret '${name}' was not defined in ${environment}.`),
|
|
1418
1337
|
kind: "authoring_secrets_delete",
|
|
1419
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1338
|
+
meta: { command: parsed.definition.path.join(" "), environment, orgId: org.id }
|
|
1420
1339
|
};
|
|
1421
1340
|
}
|
|
1422
1341
|
default:
|
|
@@ -1467,62 +1386,6 @@ async function executeChat(parsed, context, api) {
|
|
|
1467
1386
|
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1468
1387
|
};
|
|
1469
1388
|
}
|
|
1470
|
-
case "chat.changes": {
|
|
1471
|
-
const data = await api.getChatChanges(session, org.id, readRequiredArg(parsed, "session-id"));
|
|
1472
|
-
return {
|
|
1473
|
-
data,
|
|
1474
|
-
human: renderPrettyJson(data.changes),
|
|
1475
|
-
kind: "chat_changes_get",
|
|
1476
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1477
|
-
};
|
|
1478
|
-
}
|
|
1479
|
-
case "chat.validation": {
|
|
1480
|
-
const data = await api.getChatValidation(session, org.id, readRequiredArg(parsed, "session-id"));
|
|
1481
|
-
return {
|
|
1482
|
-
data,
|
|
1483
|
-
human: renderPrettyJson(data.validation),
|
|
1484
|
-
kind: "chat_validation_get",
|
|
1485
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1486
|
-
};
|
|
1487
|
-
}
|
|
1488
|
-
case "chat.draft.get": {
|
|
1489
|
-
const data = await api.getChatDraft(session, org.id, readRequiredArg(parsed, "session-id"));
|
|
1490
|
-
return {
|
|
1491
|
-
data,
|
|
1492
|
-
human: renderPrettyJson(data.draft),
|
|
1493
|
-
kind: "chat_draft_get",
|
|
1494
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1495
|
-
};
|
|
1496
|
-
}
|
|
1497
|
-
case "chat.draft.apply": {
|
|
1498
|
-
await confirmDestructive(parsed, context, `Apply draft for session ${parsed.args["session-id"]}?`);
|
|
1499
|
-
const data = await api.applyChatDraft(session, org.id, readRequiredArg(parsed, "session-id"));
|
|
1500
|
-
return {
|
|
1501
|
-
data,
|
|
1502
|
-
human: renderSuccess(`Applied draft for session ${parsed.args["session-id"]}.`),
|
|
1503
|
-
kind: "chat_draft_apply",
|
|
1504
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1505
|
-
};
|
|
1506
|
-
}
|
|
1507
|
-
case "chat.draft.refresh": {
|
|
1508
|
-
await confirmDestructive(parsed, context, `Refresh draft for session ${parsed.args["session-id"]}?`);
|
|
1509
|
-
const data = await api.refreshChatDraft(session, org.id, readRequiredArg(parsed, "session-id"));
|
|
1510
|
-
return {
|
|
1511
|
-
data,
|
|
1512
|
-
human: renderSuccess(`Refreshed draft for session ${parsed.args["session-id"]}.`),
|
|
1513
|
-
kind: "chat_draft_refresh",
|
|
1514
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1515
|
-
};
|
|
1516
|
-
}
|
|
1517
|
-
case "chat.workspace-state": {
|
|
1518
|
-
const data = await api.getChatWorkspaceState(session, org.id, readRequiredArg(parsed, "session-id"));
|
|
1519
|
-
return {
|
|
1520
|
-
data,
|
|
1521
|
-
human: renderPrettyJson(data.workspaceState),
|
|
1522
|
-
kind: "chat_workspace_state_get",
|
|
1523
|
-
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1524
|
-
};
|
|
1525
|
-
}
|
|
1526
1389
|
default:
|
|
1527
1390
|
throw genericProblem(`Unhandled chat command ${parsed.definition.id}.`, parsed.definition.id);
|
|
1528
1391
|
}
|
|
@@ -1569,9 +1432,10 @@ async function executeCompletion(parsed) {
|
|
|
1569
1432
|
}
|
|
1570
1433
|
}
|
|
1571
1434
|
async function executeSchemaGet(parsed) {
|
|
1572
|
-
const
|
|
1435
|
+
const resourceName = readRequiredArg(parsed, "resource-name");
|
|
1436
|
+
const schema = getCliSchema(resourceName);
|
|
1573
1437
|
if (!schema) {
|
|
1574
|
-
throw notFoundProblem(`Schema '${
|
|
1438
|
+
throw notFoundProblem(`Schema '${resourceName}' was not found. Use lowercase schema names, not YAML kind names. Run \`schema list\` to see all schemas. Known names include: ${schemaNamePreview()}.`, "schema get");
|
|
1575
1439
|
}
|
|
1576
1440
|
return {
|
|
1577
1441
|
data: { schema },
|
|
@@ -1589,9 +1453,99 @@ async function executeSchemaGet(parsed) {
|
|
|
1589
1453
|
meta: { command: "schema get" }
|
|
1590
1454
|
};
|
|
1591
1455
|
}
|
|
1456
|
+
function schemaNamePreview() {
|
|
1457
|
+
const names = new Set(listCliSchemas().map((schema) => schema.name));
|
|
1458
|
+
return ["process", "operation", "capability", "decision", "manifest", "organization", "assignments"]
|
|
1459
|
+
.filter((name) => names.has(name))
|
|
1460
|
+
.join(", ");
|
|
1461
|
+
}
|
|
1592
1462
|
async function executeAdmin(parsed, context, api) {
|
|
1593
1463
|
const session = requireStoredSession(context.session, parsed.definition.id);
|
|
1594
1464
|
switch (parsed.definition.id) {
|
|
1465
|
+
case "admin.usage.summary": {
|
|
1466
|
+
const { org, session: scopedSession } = await resolveOrgScope(parsed, context, api);
|
|
1467
|
+
const data = await api.getAgentUsage(scopedSession, org.id, readAgentUsageQuery(parsed));
|
|
1468
|
+
return {
|
|
1469
|
+
data,
|
|
1470
|
+
human: renderAgentUsageSummary(org.id, data.usage),
|
|
1471
|
+
kind: "admin_usage_summary",
|
|
1472
|
+
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
case "admin.usage.workflows": {
|
|
1476
|
+
const { org, session: scopedSession } = await resolveOrgScope(parsed, context, api);
|
|
1477
|
+
const data = await api.getAgentUsage(scopedSession, org.id, readAgentUsageQuery(parsed));
|
|
1478
|
+
return {
|
|
1479
|
+
data,
|
|
1480
|
+
human: renderTable(data.usage.workflows.map((workflow) => ({
|
|
1481
|
+
cost: formatUsdMicros(workflow.usdMicros),
|
|
1482
|
+
executions: formatInteger(workflow.agentExecutionCount),
|
|
1483
|
+
model: formatProviderModel(workflow.provider, workflow.model),
|
|
1484
|
+
tokensIn: formatInteger(workflow.tokensIn),
|
|
1485
|
+
tokensOut: formatInteger(workflow.tokensOut),
|
|
1486
|
+
toolCalls: formatInteger(workflow.toolCallCount),
|
|
1487
|
+
workflow: workflow.processName
|
|
1488
|
+
})), [
|
|
1489
|
+
{ key: "workflow", label: "Workflow" },
|
|
1490
|
+
{ key: "model", label: "Model" },
|
|
1491
|
+
{ key: "executions", label: "Executions" },
|
|
1492
|
+
{ key: "tokensIn", label: "Tokens in" },
|
|
1493
|
+
{ key: "tokensOut", label: "Tokens out" },
|
|
1494
|
+
{ key: "toolCalls", label: "Tool calls" },
|
|
1495
|
+
{ key: "cost", label: "Cost" }
|
|
1496
|
+
]),
|
|
1497
|
+
kind: "admin_usage_workflows",
|
|
1498
|
+
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1499
|
+
};
|
|
1500
|
+
}
|
|
1501
|
+
case "admin.usage.tools": {
|
|
1502
|
+
const { org, session: scopedSession } = await resolveOrgScope(parsed, context, api);
|
|
1503
|
+
const data = await api.getAgentUsage(scopedSession, org.id, readAgentUsageQuery(parsed));
|
|
1504
|
+
return {
|
|
1505
|
+
data,
|
|
1506
|
+
human: renderTable(data.usage.tools.map((tool) => ({
|
|
1507
|
+
calls: formatInteger(tool.callCount),
|
|
1508
|
+
failed: formatInteger(tool.failureCount),
|
|
1509
|
+
succeeded: formatInteger(tool.successCount),
|
|
1510
|
+
tool: tool.toolName
|
|
1511
|
+
})), [
|
|
1512
|
+
{ key: "tool", label: "Tool" },
|
|
1513
|
+
{ key: "calls", label: "Calls" },
|
|
1514
|
+
{ key: "succeeded", label: "Succeeded" },
|
|
1515
|
+
{ key: "failed", label: "Failed" }
|
|
1516
|
+
]),
|
|
1517
|
+
kind: "admin_usage_tools",
|
|
1518
|
+
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
case "admin.usage.events": {
|
|
1522
|
+
const { org, session: scopedSession } = await resolveOrgScope(parsed, context, api);
|
|
1523
|
+
const data = await api.getAgentUsage(scopedSession, org.id, readAgentUsageQuery(parsed));
|
|
1524
|
+
return {
|
|
1525
|
+
data,
|
|
1526
|
+
human: renderTable(data.usage.recentEvents.map((event) => ({
|
|
1527
|
+
cost: formatUsdMicros(event.usdMicros),
|
|
1528
|
+
event: event.eventId,
|
|
1529
|
+
model: formatProviderModel(event.provider, event.model),
|
|
1530
|
+
occurredAt: event.occurredAt,
|
|
1531
|
+
status: event.sourceStatus === "event_only" ? `${event.status} (event only)` : event.status,
|
|
1532
|
+
tokens: formatInteger(event.tokensIn + event.tokensOut),
|
|
1533
|
+
toolCalls: formatInteger(event.toolCallCount),
|
|
1534
|
+
workflow: event.processName
|
|
1535
|
+
})), [
|
|
1536
|
+
{ key: "event", label: "Event" },
|
|
1537
|
+
{ key: "workflow", label: "Workflow" },
|
|
1538
|
+
{ key: "model", label: "Model" },
|
|
1539
|
+
{ key: "status", label: "Status" },
|
|
1540
|
+
{ key: "tokens", label: "Tokens" },
|
|
1541
|
+
{ key: "toolCalls", label: "Tool calls" },
|
|
1542
|
+
{ key: "cost", label: "Cost" },
|
|
1543
|
+
{ key: "occurredAt", label: "Occurred at" }
|
|
1544
|
+
]),
|
|
1545
|
+
kind: "admin_usage_events",
|
|
1546
|
+
meta: { command: parsed.definition.path.join(" "), orgId: org.id }
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1595
1549
|
case "admin.org.deleted.list": {
|
|
1596
1550
|
const data = await api.listDeletedOrganizations(session);
|
|
1597
1551
|
return {
|
|
@@ -1638,6 +1592,46 @@ async function executeAdmin(parsed, context, api) {
|
|
|
1638
1592
|
throw genericProblem(`Unhandled admin command ${parsed.definition.id}.`, parsed.definition.id);
|
|
1639
1593
|
}
|
|
1640
1594
|
}
|
|
1595
|
+
function readAgentUsageQuery(parsed) {
|
|
1596
|
+
const from = readOptionalStringFlag(parsed, "from");
|
|
1597
|
+
const to = readOptionalStringFlag(parsed, "to");
|
|
1598
|
+
return {
|
|
1599
|
+
...(from ? { from } : {}),
|
|
1600
|
+
...optionalNumberValue("limit", readOptionalNumberFlag(parsed, "limit")),
|
|
1601
|
+
...(to ? { to } : {})
|
|
1602
|
+
};
|
|
1603
|
+
}
|
|
1604
|
+
function renderAgentUsageSummary(orgId, usage) {
|
|
1605
|
+
const summary = usage.summary;
|
|
1606
|
+
return renderKeyValue([
|
|
1607
|
+
{ label: "Organization", value: orgId },
|
|
1608
|
+
{ label: "From", value: summary.from },
|
|
1609
|
+
{ label: "To", value: summary.to },
|
|
1610
|
+
{ label: "Agent executions", value: formatInteger(summary.agentExecutionCount) },
|
|
1611
|
+
{ label: "Tokens in", value: formatInteger(summary.tokensIn) },
|
|
1612
|
+
{ label: "Tokens out", value: formatInteger(summary.tokensOut) },
|
|
1613
|
+
{ label: "Tool calls", value: formatInteger(summary.toolCallCount) },
|
|
1614
|
+
{ label: "Cost", value: formatUsdMicros(summary.usdMicros) },
|
|
1615
|
+
{ label: "Event-only records", value: formatInteger(summary.sourceEventOnlyCount) }
|
|
1616
|
+
]);
|
|
1617
|
+
}
|
|
1618
|
+
function formatProviderModel(provider, model) {
|
|
1619
|
+
if (provider && model) {
|
|
1620
|
+
return `${provider}/${model}`;
|
|
1621
|
+
}
|
|
1622
|
+
return provider ?? model ?? "";
|
|
1623
|
+
}
|
|
1624
|
+
function formatInteger(value) {
|
|
1625
|
+
return new Intl.NumberFormat(undefined, { maximumFractionDigits: 0 }).format(value);
|
|
1626
|
+
}
|
|
1627
|
+
function formatUsdMicros(value) {
|
|
1628
|
+
return new Intl.NumberFormat(undefined, {
|
|
1629
|
+
currency: "USD",
|
|
1630
|
+
maximumFractionDigits: 4,
|
|
1631
|
+
minimumFractionDigits: value > 0 && value < 10_000 ? 4 : 2,
|
|
1632
|
+
style: "currency"
|
|
1633
|
+
}).format(value / 1_000_000);
|
|
1634
|
+
}
|
|
1641
1635
|
async function resolveOrgScope(parsed, context, api) {
|
|
1642
1636
|
const session = requireStoredSession(context.session, parsed.definition.id);
|
|
1643
1637
|
const selector = readOptionalStringFlag(parsed, "org");
|
|
@@ -1675,16 +1669,109 @@ function isSameSessionIdentity(candidate, session) {
|
|
|
1675
1669
|
&& candidate.user.id === session.user.id
|
|
1676
1670
|
&& candidate.accessTokenPayload.sub === session.accessTokenPayload.sub;
|
|
1677
1671
|
}
|
|
1678
|
-
function readOptionalMcpAuthMode(parsed) {
|
|
1679
|
-
const value = readOptionalStringFlag(parsed, "auth-mode");
|
|
1680
|
-
if (value === undefined || value === "none" || value === "oauth") {
|
|
1681
|
-
return value;
|
|
1682
|
-
}
|
|
1683
|
-
throw usageProblem("MCP auth mode must be 'none' or 'oauth'.", parsed.definition.id);
|
|
1684
|
-
}
|
|
1685
1672
|
function optionalNumberValue(key, value) {
|
|
1686
1673
|
return value !== undefined ? { [key]: value } : {};
|
|
1687
1674
|
}
|
|
1675
|
+
function withCliSummary(meta, summary) {
|
|
1676
|
+
const normalized = normalizeCliSummary(summary);
|
|
1677
|
+
return normalized ? { ...meta, summary: normalized } : meta;
|
|
1678
|
+
}
|
|
1679
|
+
function normalizeCliSummary(value) {
|
|
1680
|
+
const trimmed = value?.trim();
|
|
1681
|
+
return trimmed && trimmed.length > 0 ? trimmed : null;
|
|
1682
|
+
}
|
|
1683
|
+
function summarizeWorkflowNodeTest(test) {
|
|
1684
|
+
if (test.status === "passed") {
|
|
1685
|
+
const capturedArtifacts = Array.isArray(test.capturedArtifacts)
|
|
1686
|
+
? test.capturedArtifacts.length
|
|
1687
|
+
: 0;
|
|
1688
|
+
return `Workflow-node test passed${capturedArtifacts > 0 ? ` with ${String(capturedArtifacts)} captured artifact${capturedArtifacts === 1 ? "" : "s"}` : ""}.`;
|
|
1689
|
+
}
|
|
1690
|
+
const label = test.status === "invalid" ? "invalid" : "failed";
|
|
1691
|
+
const error = recordField(test, "error");
|
|
1692
|
+
const message = typeof error.message === "string" && error.message.trim().length > 0
|
|
1693
|
+
? truncateSummaryText(error.message)
|
|
1694
|
+
: firstDiagnosticMessage(readSummaryDiagnostics(test));
|
|
1695
|
+
return `Workflow-node test ${label}${message ? `: ${message}` : ""}.`;
|
|
1696
|
+
}
|
|
1697
|
+
function renderWorkflowNodeTestHuman(test) {
|
|
1698
|
+
const summary = summarizeWorkflowNodeTest(test);
|
|
1699
|
+
const capturedArtifacts = Array.isArray(test.capturedArtifacts)
|
|
1700
|
+
? test.capturedArtifacts.length
|
|
1701
|
+
: 0;
|
|
1702
|
+
const logs = Array.isArray(test.logs)
|
|
1703
|
+
? test.logs.length
|
|
1704
|
+
: 0;
|
|
1705
|
+
const rows = [
|
|
1706
|
+
{ label: "Status", value: test.status },
|
|
1707
|
+
{ label: "Workflow", value: test.workflowName },
|
|
1708
|
+
{ label: "Node", value: test.nodeId },
|
|
1709
|
+
...(typeof test.operationName === "string"
|
|
1710
|
+
? [{ label: "Operation", value: test.operationName }]
|
|
1711
|
+
: []),
|
|
1712
|
+
{ label: "Captured artifacts", value: capturedArtifacts },
|
|
1713
|
+
{ label: "Logs", value: logs > 0 ? `${String(logs)} stream${logs === 1 ? "" : "s"} available` : "none" }
|
|
1714
|
+
];
|
|
1715
|
+
const diagnostics = readSummaryDiagnostics(test);
|
|
1716
|
+
const error = recordField(test, "error");
|
|
1717
|
+
const actionable = typeof error.message === "string" && error.message.trim().length > 0
|
|
1718
|
+
? truncateSummaryText(error.message)
|
|
1719
|
+
: firstDiagnosticMessage(diagnostics);
|
|
1720
|
+
return [
|
|
1721
|
+
summary,
|
|
1722
|
+
renderKeyValue(rows),
|
|
1723
|
+
...(actionable && test.status !== "passed" ? [`Action: ${actionable}`] : [])
|
|
1724
|
+
].filter((entry) => entry.trim().length > 0).join("\n");
|
|
1725
|
+
}
|
|
1726
|
+
function summarizeReleaseValidation(validation) {
|
|
1727
|
+
const diagnostics = readSummaryDiagnostics(validation);
|
|
1728
|
+
return validation.releaseReady === true
|
|
1729
|
+
? `Release validation passed${formatDiagnosticSummarySuffix(diagnostics)}.`
|
|
1730
|
+
: `Release validation failed${formatDiagnosticSummarySuffix(diagnostics)}.`;
|
|
1731
|
+
}
|
|
1732
|
+
function summarizeReleaseCreation(releaseId) {
|
|
1733
|
+
return `Release ${releaseId} created. It is not live yet; review and deploy it from /app/releases/${releaseId}.`;
|
|
1734
|
+
}
|
|
1735
|
+
function readSummaryDiagnostics(record) {
|
|
1736
|
+
const diagnostics = record.diagnostics;
|
|
1737
|
+
return Array.isArray(diagnostics)
|
|
1738
|
+
? diagnostics.filter((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry))
|
|
1739
|
+
: [];
|
|
1740
|
+
}
|
|
1741
|
+
function formatDiagnosticSummarySuffix(diagnostics) {
|
|
1742
|
+
const errorCount = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
|
|
1743
|
+
const warningCount = diagnostics.length - errorCount;
|
|
1744
|
+
const parts = [
|
|
1745
|
+
...(errorCount > 0 ? [formatDiagnosticCount(errorCount, "error")] : []),
|
|
1746
|
+
...(warningCount > 0 ? [formatDiagnosticCount(warningCount, "warning")] : [])
|
|
1747
|
+
];
|
|
1748
|
+
if (parts.length === 0) {
|
|
1749
|
+
return "";
|
|
1750
|
+
}
|
|
1751
|
+
const message = firstDiagnosticMessage(diagnostics);
|
|
1752
|
+
return ` with ${parts.join(" and ")}${message ? `: ${message}` : ""}`;
|
|
1753
|
+
}
|
|
1754
|
+
function formatDiagnosticCount(count, label) {
|
|
1755
|
+
return `${String(count)} ${label}${count === 1 ? "" : "s"}`;
|
|
1756
|
+
}
|
|
1757
|
+
function firstDiagnosticMessage(diagnostics) {
|
|
1758
|
+
for (const diagnostic of diagnostics) {
|
|
1759
|
+
if (typeof diagnostic.message === "string" && diagnostic.message.trim().length > 0) {
|
|
1760
|
+
return truncateSummaryText(diagnostic.message);
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
return null;
|
|
1764
|
+
}
|
|
1765
|
+
function truncateSummaryText(value) {
|
|
1766
|
+
const trimmed = value.trim();
|
|
1767
|
+
return trimmed.length <= 180 ? trimmed : `${trimmed.slice(0, 177)}...`;
|
|
1768
|
+
}
|
|
1769
|
+
function recordField(result, key) {
|
|
1770
|
+
const value = result[key];
|
|
1771
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
1772
|
+
? value
|
|
1773
|
+
: {};
|
|
1774
|
+
}
|
|
1688
1775
|
function readRequiredArg(parsed, name) {
|
|
1689
1776
|
const value = parsed.args[name];
|
|
1690
1777
|
if (!value) {
|
|
@@ -1692,6 +1779,11 @@ function readRequiredArg(parsed, name) {
|
|
|
1692
1779
|
}
|
|
1693
1780
|
return value;
|
|
1694
1781
|
}
|
|
1782
|
+
function announceResolvedEnvironment(parsed, context, environment) {
|
|
1783
|
+
if (!parsed.json) {
|
|
1784
|
+
context.stdout.write(`Environment: ${environment}\n`);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1695
1787
|
function readRequiredIntegerArg(parsed, name) {
|
|
1696
1788
|
const raw = readRequiredArg(parsed, name);
|
|
1697
1789
|
const parsedValue = Number(raw);
|