@dharta/cli 0.6.77 → 0.6.78
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/LICENSE +1 -1
- package/README.md +215 -11
- package/dist/agent.js +3 -2
- package/dist/agent.js.map +1 -1
- package/dist/api-error.js +6 -6
- package/dist/api-error.js.map +1 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +860 -149
- package/dist/cli.js.map +1 -1
- package/dist/command-tree.d.ts +3 -0
- package/dist/command-tree.js +10 -0
- package/dist/command-tree.js.map +1 -0
- package/dist/commands/agents.d.ts +54 -17
- package/dist/commands/agents.js +246 -136
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/analytics.js +6 -5
- package/dist/commands/analytics.js.map +1 -1
- package/dist/commands/api.js +71 -86
- package/dist/commands/api.js.map +1 -1
- package/dist/commands/appearance.js +5 -6
- package/dist/commands/appearance.js.map +1 -1
- package/dist/commands/artifact.js +7 -6
- package/dist/commands/artifact.js.map +1 -1
- package/dist/commands/audit-log.js +2 -1
- package/dist/commands/audit-log.js.map +1 -1
- package/dist/commands/auth.js +26 -6
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/caps.js +11 -15
- package/dist/commands/caps.js.map +1 -1
- package/dist/commands/deploy.js +25 -10
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts +10 -1
- package/dist/commands/dev.js +56 -25
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +60 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/embed-keys.js +4 -13
- package/dist/commands/embed-keys.js.map +1 -1
- package/dist/commands/env.js +4 -7
- package/dist/commands/env.js.map +1 -1
- package/dist/commands/files.js +3 -4
- package/dist/commands/files.js.map +1 -1
- package/dist/commands/fleet.d.ts +129 -0
- package/dist/commands/fleet.js +408 -0
- package/dist/commands/fleet.js.map +1 -0
- package/dist/commands/genui.d.ts +2 -0
- package/dist/commands/genui.js +53 -0
- package/dist/commands/genui.js.map +1 -0
- package/dist/commands/git-credential.js +2 -3
- package/dist/commands/git-credential.js.map +1 -1
- package/dist/commands/kartas.d.ts +9 -0
- package/dist/commands/kartas.js +33 -11
- package/dist/commands/kartas.js.map +1 -1
- package/dist/commands/keys.d.ts +17 -18
- package/dist/commands/keys.js +108 -19
- package/dist/commands/keys.js.map +1 -1
- package/dist/commands/login.js +2 -1
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/members.d.ts +10 -0
- package/dist/commands/members.js +20 -6
- package/dist/commands/members.js.map +1 -1
- package/dist/commands/model-connections.d.ts +44 -0
- package/dist/commands/model-connections.js +435 -0
- package/dist/commands/model-connections.js.map +1 -0
- package/dist/commands/open.d.ts +2 -2
- package/dist/commands/open.js +4 -3
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/org.d.ts +9 -0
- package/dist/commands/org.js +42 -0
- package/dist/commands/org.js.map +1 -0
- package/dist/commands/organization.d.ts +16 -0
- package/dist/commands/organization.js +118 -0
- package/dist/commands/organization.js.map +1 -0
- package/dist/commands/parity.js +1 -3
- package/dist/commands/parity.js.map +1 -1
- package/dist/commands/repos.d.ts +4 -0
- package/dist/commands/repos.js +17 -0
- package/dist/commands/repos.js.map +1 -0
- package/dist/commands/rollback.js +2 -3
- package/dist/commands/rollback.js.map +1 -1
- package/dist/commands/schedules.js +26 -21
- package/dist/commands/schedules.js.map +1 -1
- package/dist/commands/scopes.js +8 -1
- package/dist/commands/scopes.js.map +1 -1
- package/dist/commands/sessions.js +9 -3
- package/dist/commands/sessions.js.map +1 -1
- package/dist/commands/setup.js +5 -4
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/update.js +24 -11
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/webhooks.d.ts +4 -1
- package/dist/commands/webhooks.js +21 -16
- package/dist/commands/webhooks.js.map +1 -1
- package/dist/completions.js +24 -13
- package/dist/completions.js.map +1 -1
- package/dist/config.d.ts +12 -0
- package/dist/config.js +121 -7
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +2 -0
- package/dist/context.js +23 -7
- package/dist/context.js.map +1 -1
- package/dist/docs/cli-reference.d.ts +2 -0
- package/dist/docs/cli-reference.js +191 -0
- package/dist/docs/cli-reference.js.map +1 -0
- package/dist/exit-codes.d.ts +25 -0
- package/dist/exit-codes.js +107 -0
- package/dist/exit-codes.js.map +1 -0
- package/dist/genui.d.ts +178 -0
- package/dist/genui.js +1130 -0
- package/dist/genui.js.map +1 -0
- package/dist/harnesses.js +2 -1
- package/dist/harnesses.js.map +1 -1
- package/dist/jq.d.ts +4 -0
- package/dist/jq.js +91 -0
- package/dist/jq.js.map +1 -0
- package/dist/login-offer.js +6 -2
- package/dist/login-offer.js.map +1 -1
- package/dist/manifest.d.ts +12 -0
- package/dist/manifest.js +126 -0
- package/dist/manifest.js.map +1 -1
- package/dist/model-auth.d.ts +29 -0
- package/dist/model-auth.js +117 -0
- package/dist/model-auth.js.map +1 -0
- package/dist/model-settings.d.ts +23 -0
- package/dist/model-settings.js +186 -0
- package/dist/model-settings.js.map +1 -0
- package/dist/money.js +2 -1
- package/dist/money.js.map +1 -1
- package/dist/output.d.ts +6 -0
- package/dist/output.js +54 -0
- package/dist/output.js.map +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +1 -1
- package/dist/scaffold.js +2 -1
- package/dist/scaffold.js.map +1 -1
- package/dist/validators.js +17 -11
- package/dist/validators.js.map +1 -1
- package/dist/version-check.d.ts +1 -1
- package/dist/version-check.js +1 -1
- package/dist/version-check.js.map +1 -1
- package/node_modules/@dharta/contracts/LICENSE +21 -0
- package/node_modules/@dharta/contracts/README.md +45 -0
- package/node_modules/@dharta/contracts/artifact-runtime.json +750 -0
- package/node_modules/@dharta/contracts/dist/activity.d.ts +62 -0
- package/node_modules/@dharta/contracts/dist/activity.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/activity.js +178 -0
- package/node_modules/@dharta/contracts/dist/activity.js.map +1 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime-formats.d.ts +20 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime-formats.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime-formats.js +53 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime-formats.js.map +1 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime-validators.generated.d.ts +37 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime-validators.generated.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime-validators.generated.js +5107 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime-validators.generated.js.map +1 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime.d.ts +154 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime.generated.d.ts +1670 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime.generated.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime.generated.js +1903 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime.generated.js.map +1 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime.js +377 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime.js.map +1 -0
- package/node_modules/@dharta/contracts/dist/index.d.ts +6 -0
- package/node_modules/@dharta/contracts/dist/index.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/index.js +6 -0
- package/node_modules/@dharta/contracts/dist/index.js.map +1 -0
- package/node_modules/@dharta/contracts/dist/model-auth.d.ts +31 -0
- package/node_modules/@dharta/contracts/dist/model-auth.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/model-auth.js +45 -0
- package/node_modules/@dharta/contracts/dist/model-auth.js.map +1 -0
- package/node_modules/@dharta/contracts/dist/tasks.d.ts +44 -0
- package/node_modules/@dharta/contracts/dist/tasks.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/tasks.js +127 -0
- package/node_modules/@dharta/contracts/dist/tasks.js.map +1 -0
- package/node_modules/@dharta/contracts/dist/wire-events.d.ts +71 -0
- package/node_modules/@dharta/contracts/dist/wire-events.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/wire-events.js +48 -0
- package/node_modules/@dharta/contracts/dist/wire-events.js.map +1 -0
- package/node_modules/@dharta/contracts/model-auth-capabilities-v1.json +168 -0
- package/node_modules/@dharta/contracts/model-auth-reasons-v1.json +38 -0
- package/node_modules/@dharta/contracts/package.json +44 -0
- package/node_modules/@dharta/contracts/src/activity.ts +215 -0
- package/node_modules/@dharta/contracts/src/artifact-runtime-formats.ts +54 -0
- package/node_modules/@dharta/contracts/src/artifact-runtime-validators.generated.ts +29 -0
- package/node_modules/@dharta/contracts/src/artifact-runtime.generated.ts +2093 -0
- package/node_modules/@dharta/contracts/src/artifact-runtime.ts +520 -0
- package/node_modules/@dharta/contracts/src/index.ts +5 -0
- package/node_modules/@dharta/contracts/src/model-auth.ts +82 -0
- package/node_modules/@dharta/contracts/src/tasks.ts +142 -0
- package/node_modules/@dharta/contracts/src/wire-events.ts +150 -0
- package/node_modules/@dharta/contracts/wire-events.json +29 -0
- package/node_modules/@dharta/sdk/LICENSE +1 -1
- package/node_modules/@dharta/sdk/README.md +22 -8
- package/node_modules/@dharta/sdk/dist/errors.d.ts +9 -7
- package/node_modules/@dharta/sdk/dist/errors.d.ts.map +1 -1
- package/node_modules/@dharta/sdk/dist/errors.js +36 -18
- package/node_modules/@dharta/sdk/dist/errors.js.map +1 -1
- package/node_modules/@dharta/sdk/dist/fleet.d.ts +69 -0
- package/node_modules/@dharta/sdk/dist/fleet.d.ts.map +1 -0
- package/node_modules/@dharta/sdk/dist/fleet.js +240 -0
- package/node_modules/@dharta/sdk/dist/fleet.js.map +1 -0
- package/node_modules/@dharta/sdk/dist/http.d.ts +6 -2
- package/node_modules/@dharta/sdk/dist/http.d.ts.map +1 -1
- package/node_modules/@dharta/sdk/dist/http.js +71 -12
- package/node_modules/@dharta/sdk/dist/http.js.map +1 -1
- package/node_modules/@dharta/sdk/dist/idempotency.d.ts +12 -0
- package/node_modules/@dharta/sdk/dist/idempotency.d.ts.map +1 -0
- package/node_modules/@dharta/sdk/dist/idempotency.js +72 -0
- package/node_modules/@dharta/sdk/dist/idempotency.js.map +1 -0
- package/node_modules/@dharta/sdk/dist/index.d.ts +20 -6
- package/node_modules/@dharta/sdk/dist/index.d.ts.map +1 -1
- package/node_modules/@dharta/sdk/dist/index.js +27 -4
- package/node_modules/@dharta/sdk/dist/index.js.map +1 -1
- package/node_modules/@dharta/sdk/dist/resources.d.ts +108 -13
- package/node_modules/@dharta/sdk/dist/resources.d.ts.map +1 -1
- package/node_modules/@dharta/sdk/dist/resources.js +403 -57
- package/node_modules/@dharta/sdk/dist/resources.js.map +1 -1
- package/node_modules/@dharta/sdk/dist/types.d.ts +436 -16
- package/node_modules/@dharta/sdk/dist/types.d.ts.map +1 -1
- package/node_modules/@dharta/sdk/dist/types.js +31 -2
- package/node_modules/@dharta/sdk/dist/types.js.map +1 -1
- package/node_modules/@dharta/sdk/package.json +9 -3
- package/node_modules/@dharta/sdk/src/errors.ts +41 -18
- package/node_modules/@dharta/sdk/src/fleet.ts +339 -0
- package/node_modules/@dharta/sdk/src/http.ts +110 -17
- package/node_modules/@dharta/sdk/src/idempotency.ts +101 -0
- package/node_modules/@dharta/sdk/src/index.ts +50 -11
- package/node_modules/@dharta/sdk/src/resources.ts +549 -68
- package/node_modules/@dharta/sdk/src/types.ts +629 -20
- package/package.json +16 -6
- package/src/agent.ts +3 -2
- package/src/api-error.ts +6 -5
- package/src/cli.ts +978 -163
- package/src/command-tree.ts +13 -0
- package/src/commands/agents.ts +365 -167
- package/src/commands/analytics.ts +6 -5
- package/src/commands/api.ts +73 -92
- package/src/commands/appearance.ts +5 -6
- package/src/commands/artifact.ts +7 -6
- package/src/commands/audit-log.ts +2 -1
- package/src/commands/auth.ts +25 -5
- package/src/commands/caps.ts +11 -15
- package/src/commands/deploy.ts +18 -4
- package/src/commands/dev.ts +86 -25
- package/src/commands/doctor.ts +69 -0
- package/src/commands/embed-keys.ts +4 -14
- package/src/commands/env.ts +4 -7
- package/src/commands/files.ts +3 -4
- package/src/commands/fleet.ts +672 -0
- package/src/commands/genui.ts +59 -0
- package/src/commands/git-credential.ts +2 -3
- package/src/commands/kartas.ts +45 -11
- package/src/commands/keys.ts +152 -35
- package/src/commands/login.ts +2 -1
- package/src/commands/members.ts +31 -6
- package/src/commands/model-connections.ts +547 -0
- package/src/commands/open.ts +5 -4
- package/src/commands/org.ts +57 -0
- package/src/commands/organization.ts +153 -0
- package/src/commands/parity.ts +1 -3
- package/src/commands/repos.ts +23 -0
- package/src/commands/rollback.ts +2 -3
- package/src/commands/schedules.ts +33 -22
- package/src/commands/scopes.ts +8 -1
- package/src/commands/sessions.ts +9 -3
- package/src/commands/setup.ts +5 -4
- package/src/commands/update.ts +28 -12
- package/src/commands/webhooks.ts +30 -19
- package/src/completions.ts +25 -13
- package/src/config.ts +141 -9
- package/src/context.ts +34 -7
- package/src/docs/cli-reference.ts +206 -0
- package/src/exit-codes.ts +122 -0
- package/src/genui.ts +1398 -0
- package/src/harnesses.ts +2 -1
- package/src/jq.ts +109 -0
- package/src/login-offer.ts +6 -2
- package/src/manifest.ts +134 -0
- package/src/model-auth.ts +173 -0
- package/src/model-settings.ts +222 -0
- package/src/money.ts +5 -1
- package/src/output.ts +60 -0
- package/src/runtime.ts +1 -1
- package/src/scaffold.ts +2 -1
- package/src/validators.ts +18 -11
- package/src/version-check.ts +1 -1
- package/dist/commands/model-keys.d.ts +0 -22
- package/dist/commands/model-keys.js +0 -222
- package/dist/commands/model-keys.js.map +0 -1
- package/src/commands/model-keys.ts +0 -278
package/src/cli.ts
CHANGED
|
@@ -3,9 +3,14 @@ import { readFileSync, realpathSync } from "node:fs";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import * as readline from "node:readline";
|
|
5
5
|
import { Argument, Command, InvalidArgumentError, Option } from "commander";
|
|
6
|
-
import { API_KEY_SCOPES, type ApiKeyScope, type
|
|
6
|
+
import { API_KEY_SCOPES, type ApiKeyScope, type ModelConnectionProvider } from "@dharta/sdk";
|
|
7
7
|
import { buildContext } from "./context.js";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
requestedProfileName,
|
|
10
|
+
resolveBaseUrl,
|
|
11
|
+
setRequestedIdempotencyKey,
|
|
12
|
+
setRequestedProfile,
|
|
13
|
+
} from "./config.js";
|
|
9
14
|
import { inferAgentSlug, validateAgentSelectorOptions, validateAgentSlug } from "./agent.js";
|
|
10
15
|
import { login } from "./commands/login.js";
|
|
11
16
|
import {
|
|
@@ -28,28 +33,62 @@ import {
|
|
|
28
33
|
type AnalyticsView,
|
|
29
34
|
} from "./commands/analytics.js";
|
|
30
35
|
import {
|
|
36
|
+
keysBulk,
|
|
37
|
+
keysBulkData,
|
|
31
38
|
keysCreate,
|
|
32
39
|
keysCreateData,
|
|
40
|
+
keysDisable,
|
|
41
|
+
keysDisableData,
|
|
42
|
+
keysEnable,
|
|
43
|
+
keysEnableData,
|
|
33
44
|
keysList,
|
|
34
45
|
keysListData,
|
|
35
46
|
keysRename,
|
|
36
47
|
keysRenameData,
|
|
37
48
|
keysRevoke,
|
|
38
49
|
keysRevokeData,
|
|
50
|
+
keysRotate,
|
|
51
|
+
keysRotateData,
|
|
52
|
+
KEY_BULK_ACTIONS,
|
|
53
|
+
KEY_ROTATION_GRACE_PERIODS,
|
|
54
|
+
validateKeysBulkInput,
|
|
39
55
|
validateKeysCreateInput,
|
|
56
|
+
validateKeysRotateInput,
|
|
40
57
|
} from "./commands/keys.js";
|
|
41
58
|
import {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
59
|
+
modelConnectionsConfigureModels,
|
|
60
|
+
modelConnectionsConfigureModelsData,
|
|
61
|
+
modelConnectionsCreate,
|
|
62
|
+
modelConnectionsCreateData,
|
|
63
|
+
modelConnectionsList,
|
|
64
|
+
modelConnectionsListData,
|
|
65
|
+
modelConnectionsModels,
|
|
66
|
+
modelConnectionsModelsData,
|
|
67
|
+
modelConnectionsModelsWritesOffers,
|
|
68
|
+
modelConnectionsRevoke,
|
|
69
|
+
modelConnectionsRevokeData,
|
|
70
|
+
modelConnectionsRotate,
|
|
71
|
+
modelConnectionsRotateData,
|
|
72
|
+
prepareModelConnectionsCreateOptions,
|
|
73
|
+
prepareModelConnectionsModelsOptions,
|
|
74
|
+
prepareModelConnectionsRotateOptions,
|
|
49
75
|
MODEL_API_FORMATS,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
76
|
+
type ModelConnectionModelsOptions,
|
|
77
|
+
type ModelConnectionRotateOptions,
|
|
78
|
+
validateModelConnectionReference,
|
|
79
|
+
validateModelConnectionsCreateOptions,
|
|
80
|
+
validateModelConnectionsModelsOptions,
|
|
81
|
+
validateModelConnectionsRotateOptions,
|
|
82
|
+
} from "./commands/model-connections.js";
|
|
83
|
+
import {
|
|
84
|
+
type OrgModelSetOptions,
|
|
85
|
+
ORG_ENDPOINT_KINDS,
|
|
86
|
+
orgModelSet,
|
|
87
|
+
orgModelSetData,
|
|
88
|
+
orgModelShow,
|
|
89
|
+
orgModelShowData,
|
|
90
|
+
validateOrgModelSetOptions,
|
|
91
|
+
} from "./commands/organization.js";
|
|
53
92
|
import {
|
|
54
93
|
membersInvite,
|
|
55
94
|
membersInviteData,
|
|
@@ -57,10 +96,24 @@ import {
|
|
|
57
96
|
membersListData,
|
|
58
97
|
membersRemove,
|
|
59
98
|
membersRemoveData,
|
|
99
|
+
membersResendInvitation,
|
|
100
|
+
membersResendInvitationData,
|
|
101
|
+
membersRevokeInvitation,
|
|
102
|
+
membersRevokeInvitationData,
|
|
60
103
|
membersSetRole,
|
|
61
104
|
membersSetRoleData,
|
|
62
105
|
validateMembersInviteInput,
|
|
63
106
|
} from "./commands/members.js";
|
|
107
|
+
import {
|
|
108
|
+
orgRename,
|
|
109
|
+
orgRenameData,
|
|
110
|
+
orgSupportAccess,
|
|
111
|
+
orgSupportAccessData,
|
|
112
|
+
orgTranscriptAccess,
|
|
113
|
+
orgTranscriptAccessData,
|
|
114
|
+
normalizeOrganizationName,
|
|
115
|
+
} from "./commands/org.js";
|
|
116
|
+
import { reposDisconnectGithub, reposDisconnectGithubData } from "./commands/repos.js";
|
|
64
117
|
import {
|
|
65
118
|
kartaMembersGrant,
|
|
66
119
|
kartaMembersGrantData,
|
|
@@ -88,21 +141,41 @@ import {
|
|
|
88
141
|
kartaEnvSetData,
|
|
89
142
|
kartaEnvUnset,
|
|
90
143
|
kartaEnvUnsetData,
|
|
144
|
+
kartaShare,
|
|
145
|
+
kartaShareData,
|
|
146
|
+
kartaUnshare,
|
|
147
|
+
kartaUnshareData,
|
|
91
148
|
validateKartaCreateOptions,
|
|
92
149
|
validateKartaConfigureOptions,
|
|
93
150
|
validateKartaNameOptions,
|
|
94
151
|
} from "./commands/kartas.js";
|
|
95
152
|
import { usage as usageCmd, usageData } from "./commands/usage.js";
|
|
96
153
|
import {
|
|
154
|
+
type AgentCreateFromTemplateOptions,
|
|
155
|
+
type AgentCreateOptions,
|
|
97
156
|
type AgentModelSetOptions,
|
|
98
157
|
agentsActivateRelease,
|
|
99
158
|
agentsActivateReleaseData,
|
|
159
|
+
agentsCreate,
|
|
160
|
+
agentsCreateData,
|
|
161
|
+
agentsCreateFromTemplate,
|
|
162
|
+
agentsCreateFromTemplateData,
|
|
100
163
|
agentsDashboardEditsSet,
|
|
101
164
|
agentsDashboardEditsSetData,
|
|
102
165
|
agentsDelete,
|
|
103
166
|
agentsDeleteData,
|
|
167
|
+
agentsDurableWorkspaceSet,
|
|
168
|
+
agentsDurableWorkspaceSetData,
|
|
169
|
+
agentsGithubRepos,
|
|
170
|
+
agentsGithubReposData,
|
|
104
171
|
agentsList,
|
|
105
172
|
agentsListData,
|
|
173
|
+
agentsRedeploy,
|
|
174
|
+
agentsRedeployData,
|
|
175
|
+
agentsRename,
|
|
176
|
+
agentsRenameData,
|
|
177
|
+
agentsRetryRelease,
|
|
178
|
+
agentsRetryReleaseData,
|
|
106
179
|
agentsModelSet,
|
|
107
180
|
agentsModelSetData,
|
|
108
181
|
agentsReleases,
|
|
@@ -118,6 +191,8 @@ import {
|
|
|
118
191
|
agentsWorkspaceAccessSet,
|
|
119
192
|
agentsWorkspaceAccessSetData,
|
|
120
193
|
parseWorkspaceAccessAssignments,
|
|
194
|
+
validateAgentCreateFromTemplateInput,
|
|
195
|
+
validateAgentCreateInput,
|
|
121
196
|
validateAgentModelSetOptions,
|
|
122
197
|
} from "./commands/agents.js";
|
|
123
198
|
import {
|
|
@@ -185,6 +260,58 @@ import {
|
|
|
185
260
|
directorySessionsData,
|
|
186
261
|
validateDirectoryOptions,
|
|
187
262
|
} from "./commands/directories.js";
|
|
263
|
+
import {
|
|
264
|
+
type AnalyticsSaveOptions,
|
|
265
|
+
type AssignOptions,
|
|
266
|
+
type ClassifyOptions,
|
|
267
|
+
type ExportCreateOptions,
|
|
268
|
+
type OperationCreateOptions,
|
|
269
|
+
type RunbookOptions,
|
|
270
|
+
type SnoozeOptions,
|
|
271
|
+
type ViewWriteOptions,
|
|
272
|
+
FLEET_ENTITY_TYPES,
|
|
273
|
+
FLEET_SELECTION_MODES,
|
|
274
|
+
FLEET_SNOOZE_DURATIONS,
|
|
275
|
+
FLEET_VIEW_VISIBILITIES,
|
|
276
|
+
fleetAnalyticsSave,
|
|
277
|
+
fleetAnalyticsSaveData,
|
|
278
|
+
fleetAnalyticsUnpin,
|
|
279
|
+
fleetAnalyticsUnpinData,
|
|
280
|
+
fleetExportCreate,
|
|
281
|
+
fleetExportCreateData,
|
|
282
|
+
fleetFinding,
|
|
283
|
+
fleetFindingAssign,
|
|
284
|
+
fleetFindingAssignData,
|
|
285
|
+
fleetFindingClassify,
|
|
286
|
+
fleetFindingClassifyData,
|
|
287
|
+
fleetFindingData,
|
|
288
|
+
fleetFindingSnooze,
|
|
289
|
+
fleetFindingSnoozeData,
|
|
290
|
+
fleetFindingUpdate,
|
|
291
|
+
fleetFindingUpdateData,
|
|
292
|
+
fleetOperationCancel,
|
|
293
|
+
fleetOperationCancelData,
|
|
294
|
+
fleetOperationConfirm,
|
|
295
|
+
fleetOperationConfirmData,
|
|
296
|
+
fleetOperationCreate,
|
|
297
|
+
fleetOperationCreateData,
|
|
298
|
+
fleetOperationRetry,
|
|
299
|
+
fleetOperationRetryData,
|
|
300
|
+
fleetRollout,
|
|
301
|
+
fleetRolloutData,
|
|
302
|
+
fleetRolloutRollback,
|
|
303
|
+
fleetRolloutRollbackData,
|
|
304
|
+
fleetViewCreate,
|
|
305
|
+
fleetViewCreateData,
|
|
306
|
+
fleetViewDelete,
|
|
307
|
+
fleetViewDeleteData,
|
|
308
|
+
fleetViewFavorite,
|
|
309
|
+
fleetViewFavoriteData,
|
|
310
|
+
fleetViewUnfavorite,
|
|
311
|
+
fleetViewUnfavoriteData,
|
|
312
|
+
fleetViewUpdate,
|
|
313
|
+
fleetViewUpdateData,
|
|
314
|
+
} from "./commands/fleet.js";
|
|
188
315
|
import {
|
|
189
316
|
parseEvents,
|
|
190
317
|
webhooksCreate,
|
|
@@ -195,6 +322,8 @@ import {
|
|
|
195
322
|
webhooksEnableData,
|
|
196
323
|
webhooksList,
|
|
197
324
|
webhooksListData,
|
|
325
|
+
webhooksTest,
|
|
326
|
+
webhooksTestData,
|
|
198
327
|
webhooksUpdate,
|
|
199
328
|
webhooksUpdateData,
|
|
200
329
|
validateWebhooksCreateInput,
|
|
@@ -211,6 +340,8 @@ import { offerLogin } from "./login-offer.js";
|
|
|
211
340
|
import { doctor } from "./commands/doctor.js";
|
|
212
341
|
import { dev as devCmd } from "./commands/dev.js";
|
|
213
342
|
import { registerArtifactCommands } from "./commands/artifact.js";
|
|
343
|
+
import { registerGenuiCommands } from "./commands/genui.js";
|
|
344
|
+
import { flushJsonPayload, isJson, printJson, resetJsonPayload } from "./output.js";
|
|
214
345
|
import {
|
|
215
346
|
isHandoffTarget,
|
|
216
347
|
open as openCmd,
|
|
@@ -251,13 +382,15 @@ import { update } from "./commands/update.js";
|
|
|
251
382
|
import { filesLs, filesLsData, filesGet } from "./commands/files.js";
|
|
252
383
|
import { gitCredential } from "./commands/git-credential.js";
|
|
253
384
|
import { bashCompletion, zshCompletion } from "./completions.js";
|
|
385
|
+
import { visibleSubcommands } from "./command-tree.js";
|
|
386
|
+
import { CliUsageError, exitCodeForError, isCommanderError, isNotAuthenticatedError } from "./exit-codes.js";
|
|
387
|
+
import { armJqFilter } from "./jq.js";
|
|
254
388
|
import { closestChoice, normalizeHttpUrl, normalizeNonBlank, parsePositiveInteger, parseReleaseVersion } from "./validators.js";
|
|
389
|
+
import { DEV_MODEL_AUTH_VALUES, MODEL_CONNECTION_PROVIDERS, type DevModelAuth } from "./model-auth.js";
|
|
255
390
|
|
|
256
391
|
const MEMBER_ROLES = ["owner", "admin", "developer", "billing", "viewer"] as const;
|
|
257
392
|
const DHARTA_ACCESS_AUDIENCES = ["members", "anonymous", "off"] as const;
|
|
258
393
|
const DHARTA_MEMBER_ROLES = ["viewer", "participant", "manager"] as const;
|
|
259
|
-
const MODEL_KEY_AUTH_TYPES = ["api_key", "oauth_subscription"] as const;
|
|
260
|
-
const AGENT_MODEL_SOURCES = ["inherit", "platform"] as const;
|
|
261
394
|
const CLI_API_KEY_SCOPES = API_KEY_SCOPES;
|
|
262
395
|
const STATE_HELP = "State values: on/off, enabled/disabled, true/false.";
|
|
263
396
|
|
|
@@ -275,29 +408,6 @@ interface DirectorySelectorOptions extends AgentSelectorOptions {
|
|
|
275
408
|
limit?: string;
|
|
276
409
|
}
|
|
277
410
|
|
|
278
|
-
// --json output (RFC 0013 §3): every command that produces a result
|
|
279
|
-
// payload prints it as JSON when --json is set. Progress narration
|
|
280
|
-
// (device-flow codes, download logs) moves to stderr so stdout stays
|
|
281
|
-
// parseable. Commands whose stdout is a stream, a REPL, a script, or a
|
|
282
|
-
// protocol rather than a result payload (dev, logs, completion,
|
|
283
|
-
// git-credential, and `deploy --mode git` whose output is the live
|
|
284
|
-
// push sideband) reject --json. `deploy --mode folder` returns a
|
|
285
|
-
// single payload, so it supports --json.
|
|
286
|
-
//
|
|
287
|
-
// Mechanics: the flag is registered on every node of the command tree
|
|
288
|
-
// so commander's
|
|
289
|
-
// outermost option scan consumes it wherever it appears on the line -
|
|
290
|
-
// so actions read it through optsWithGlobals() on their Command. Commands
|
|
291
|
-
// that always reject JSON keep a hidden parser option so help/completions
|
|
292
|
-
// do not advertise an invalid mode.
|
|
293
|
-
function isJson(cmd: Command): boolean {
|
|
294
|
-
return Boolean(cmd.optsWithGlobals().json);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
function printJson(payload: unknown): void {
|
|
298
|
-
console.log(JSON.stringify(payload, null, 2));
|
|
299
|
-
}
|
|
300
|
-
|
|
301
411
|
function commandOpts<T extends object>(cmd: Command): T {
|
|
302
412
|
return cmd.optsWithGlobals() as T;
|
|
303
413
|
}
|
|
@@ -306,7 +416,7 @@ function validatePositionalAgentSelector(slug: string | undefined, opts: Pick<Ag
|
|
|
306
416
|
if (slug === undefined) return;
|
|
307
417
|
validateAgentSlug(slug, "agent slug");
|
|
308
418
|
if (opts.agent !== undefined || opts.project !== undefined) {
|
|
309
|
-
throw new
|
|
419
|
+
throw new CliUsageError("Use either the positional agent slug or --agent/--project, not both.");
|
|
310
420
|
}
|
|
311
421
|
}
|
|
312
422
|
|
|
@@ -341,11 +451,75 @@ async function resolveAgentOptions<T extends AgentSelectorOptions>(
|
|
|
341
451
|
}
|
|
342
452
|
|
|
343
453
|
function rejectJson(cmd: Command, name: string): void {
|
|
344
|
-
if (isJson(cmd))
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
454
|
+
if (!isJson(cmd)) return;
|
|
455
|
+
// --jq is rejected wherever --json is, and for the same reason: there is no
|
|
456
|
+
// payload to filter. Name the flag the user actually passed.
|
|
457
|
+
const flag = cmd.optsWithGlobals().jq !== undefined ? "--jq" : "--json";
|
|
458
|
+
throw new CliUsageError(
|
|
459
|
+
`${flag} is not supported for \`${name}\` - its output is not a single payload. Run it without ${flag}.`,
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// A command group prints help and issues no request, so an Idempotency-Key on
|
|
464
|
+
// one is a mistake with nowhere to land. Read-only LEAF commands are not
|
|
465
|
+
// rejected: see the P9 entry in the RFC 0048 implementation log for why.
|
|
466
|
+
function rejectIdempotencyKey(cmd: Command, name: string): void {
|
|
467
|
+
if (cmd.optsWithGlobals().idempotencyKey === undefined) return;
|
|
468
|
+
throw new CliUsageError(
|
|
469
|
+
`--idempotency-key is not supported for \`${name}\` - it prints help and sends no request. ` +
|
|
470
|
+
"Put it on the command that performs the mutation.",
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// `--idempotency-key` names an `Idempotency-Key` for a write to the Dharta API.
|
|
475
|
+
// These commands perform no mutation the key could protect, so accepting one
|
|
476
|
+
// would silently ignore it, which is precisely the outcome a caller reaching for
|
|
477
|
+
// the flag is trying to avoid.
|
|
478
|
+
//
|
|
479
|
+
// Several of them DO talk to Dharta, and the message must not claim otherwise:
|
|
480
|
+
//
|
|
481
|
+
// - `login` runs before a credential exists. Its device-flow POSTs carry no
|
|
482
|
+
// key and the server installs no idempotency filter on them, and it then
|
|
483
|
+
// reads `/me` to learn who the new key belongs to. Reads and unprotected
|
|
484
|
+
// POSTs, no mutation.
|
|
485
|
+
// - `create` fetches `/agent_templates` and writes the result to disk.
|
|
486
|
+
// - `setup` writes files, and with `--login` it runs the login flow above.
|
|
487
|
+
// - `auth token` and `auth logout` read and rewrite the local profile.
|
|
488
|
+
// - `genui` builds a bundle, `artifact` spawns a local helper.
|
|
489
|
+
// - `doctor` and `update` talk to the package registry, never to Dharta.
|
|
490
|
+
// - `completion` prints a shell script.
|
|
491
|
+
//
|
|
492
|
+
// Read-only Dharta commands (`ps`, `usage`, `webhooks list`) perform no mutation
|
|
493
|
+
// either and are deliberately NOT refused. They are steps in a run that writes,
|
|
494
|
+
// the header rides along and is ignored server-side, and a script that sets one
|
|
495
|
+
// flag for a whole pipeline should not have to special-case them. The commands
|
|
496
|
+
// below are not that: a caller who puts the key on `dharta login` is expressing
|
|
497
|
+
// an intent it can never honour, so saying so beats accepting it.
|
|
498
|
+
const NON_MUTATING_COMMAND_PATHS = [
|
|
499
|
+
"dharta login",
|
|
500
|
+
"dharta auth token",
|
|
501
|
+
"dharta auth logout",
|
|
502
|
+
"dharta create",
|
|
503
|
+
"dharta setup",
|
|
504
|
+
"dharta genui",
|
|
505
|
+
"dharta artifact",
|
|
506
|
+
"dharta doctor",
|
|
507
|
+
"dharta update",
|
|
508
|
+
"dharta completion",
|
|
509
|
+
];
|
|
510
|
+
|
|
511
|
+
export function isNonMutatingCommandPath(path: string): boolean {
|
|
512
|
+
return NON_MUTATING_COMMAND_PATHS.some((entry) => path === entry || path.startsWith(`${entry} `));
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function rejectNonMutatingIdempotencyKey(cmd: Command, key: string | undefined): void {
|
|
516
|
+
if (key === undefined) return;
|
|
517
|
+
const path = commandPath(cmd);
|
|
518
|
+
if (!isNonMutatingCommandPath(path)) return;
|
|
519
|
+
throw new CliUsageError(
|
|
520
|
+
`--idempotency-key is not supported for \`${path}\` - it performs no mutation the key could protect. ` +
|
|
521
|
+
"The flag applies to commands that write to the Dharta API.",
|
|
522
|
+
);
|
|
349
523
|
}
|
|
350
524
|
|
|
351
525
|
function helpOnlyAction(name: string) {
|
|
@@ -363,6 +537,7 @@ function helpOnlyAction(name: string) {
|
|
|
363
537
|
);
|
|
364
538
|
}
|
|
365
539
|
rejectJson(cmd, name);
|
|
540
|
+
rejectIdempotencyKey(cmd, name);
|
|
366
541
|
cmd.outputHelp();
|
|
367
542
|
};
|
|
368
543
|
}
|
|
@@ -385,27 +560,14 @@ function closestCommand(input: string, commands: string[]): string | null {
|
|
|
385
560
|
|
|
386
561
|
function commandSuggestionTokens(cmd: Command): string[] {
|
|
387
562
|
return Array.from(
|
|
388
|
-
new Set(
|
|
389
|
-
cmd.commands
|
|
390
|
-
.filter((child) => !(child as unknown as { _hidden?: boolean })._hidden)
|
|
391
|
-
.flatMap((child) => [child.name(), ...child.aliases()]),
|
|
392
|
-
),
|
|
563
|
+
new Set(visibleSubcommands(cmd).flatMap((child) => [child.name(), ...child.aliases()])),
|
|
393
564
|
);
|
|
394
565
|
}
|
|
395
566
|
|
|
396
567
|
export function buildProgram(): Command {
|
|
397
568
|
const program = new Command();
|
|
398
|
-
// Commander 12 has no native help groups, so render the top-level command
|
|
399
|
-
// list grouped (addHelpText below) and suppress commander's default flat
|
|
400
|
-
// list on the ROOT only - subcommand help (e.g. `dharta agent --help`) keeps
|
|
401
|
-
// commander's default rendering. `defaultHelp` is captured before the
|
|
402
|
-
// override so it supplies the unmodified list for subcommands.
|
|
403
|
-
const defaultHelp = program.createHelp();
|
|
404
569
|
program
|
|
405
570
|
.name("dharta")
|
|
406
|
-
.configureHelp({
|
|
407
|
-
visibleCommands: (cmd) => (cmd.parent === null ? [] : defaultHelp.visibleCommands(cmd)),
|
|
408
|
-
})
|
|
409
571
|
.description(
|
|
410
572
|
"Build, run, and deploy AI agents on dharta.ai - one tool from create to open.\n" +
|
|
411
573
|
"Agents are harness-native folders (Claude Code, OpenCode, DeepAgents, Goose, Codex CLI) plus dharta.toml; `dharta deploy` ships one selected folder.",
|
|
@@ -419,52 +581,9 @@ export function buildProgram(): Command {
|
|
|
419
581
|
.addHelpText(
|
|
420
582
|
"after",
|
|
421
583
|
[
|
|
422
|
-
"",
|
|
423
|
-
"Agent lifecycle",
|
|
424
|
-
" create Scaffold a brand-new agent",
|
|
425
|
-
" setup Configure an agent here; write dharta.toml",
|
|
426
|
-
" dev Preview locally as a user would (hot reload)",
|
|
427
|
-
" deploy Deploy this agent folder",
|
|
428
|
-
" open Open the live hosted page",
|
|
429
|
-
" status Every agent here and its live release",
|
|
430
|
-
"",
|
|
431
|
-
"Operate",
|
|
432
|
-
" sessions Inspect an agent's sessions",
|
|
433
|
-
" session View one session transcript",
|
|
434
|
-
" ps Sessions active right now",
|
|
435
|
-
" logs Stream runtime logs",
|
|
436
|
-
" files Read a karta's workspace files",
|
|
437
|
-
" send One-shot message to a deployed agent",
|
|
438
|
-
" rollback Roll back to a previous release",
|
|
439
|
-
" env Environment bindings",
|
|
440
|
-
"",
|
|
441
|
-
"Manage",
|
|
442
|
-
" agent List and show your agents",
|
|
443
|
-
" artifact Designate durable workspace work product",
|
|
444
|
-
"",
|
|
445
|
-
"Account",
|
|
446
|
-
" login Sign in (device flow)",
|
|
447
|
-
" whoami Show the authenticated principal",
|
|
448
|
-
" auth Status, tokens, logout",
|
|
449
|
-
" audit-log Export audit events",
|
|
450
|
-
" analytics Usage and cost analytics",
|
|
451
|
-
" keys API keys",
|
|
452
|
-
" model-keys BYOK provider credentials",
|
|
453
|
-
" members Organization members and invitations",
|
|
454
|
-
" kartas Durable Dharta identity, URLs, and ACLs",
|
|
455
|
-
" api Authenticated public API passthrough",
|
|
456
|
-
" embed-keys Publishable widget keys",
|
|
457
|
-
" caps Spend caps and monthly budget",
|
|
458
|
-
" appearance Agent appearance theme",
|
|
459
|
-
" schedules Recurring agent runs",
|
|
460
|
-
" directories Agent-scoped kartas, users, sessions",
|
|
461
|
-
" webhooks Event delivery endpoints",
|
|
462
|
-
"",
|
|
463
|
-
"Utilities",
|
|
464
|
-
" usage · doctor · update · completion",
|
|
465
584
|
"",
|
|
466
585
|
"Golden path: dharta login -> create -> setup -> dev -> deploy -> open",
|
|
467
|
-
"Run `
|
|
586
|
+
"Run `dharta <command> --help` for details. Docs: https://docs.dharta.ai",
|
|
468
587
|
].join("\n"),
|
|
469
588
|
)
|
|
470
589
|
.action(helpOnlyAction("dharta"));
|
|
@@ -477,25 +596,23 @@ export function buildProgram(): Command {
|
|
|
477
596
|
.option("--with-token", "Read an API key from stdin instead (CI, air-gapped)")
|
|
478
597
|
.option("--base-url <url>", "Override the API base URL")
|
|
479
598
|
.option("--git-base-url <url>", "Override the git server base URL")
|
|
480
|
-
.option("--profile <name>", "Named profile (default: default)")
|
|
481
599
|
.action(
|
|
482
600
|
async (
|
|
483
601
|
opts: {
|
|
484
602
|
withToken?: boolean;
|
|
485
603
|
baseUrl?: string;
|
|
486
604
|
gitBaseUrl?: string;
|
|
487
|
-
profile?: string;
|
|
488
605
|
},
|
|
489
606
|
cmd: Command,
|
|
490
607
|
) => {
|
|
491
608
|
if (opts.baseUrl !== undefined) normalizeHttpUrl(opts.baseUrl, "--base-url");
|
|
492
609
|
if (opts.gitBaseUrl !== undefined) normalizeHttpUrl(opts.gitBaseUrl, "--git-base-url");
|
|
493
|
-
if (opts.profile !== undefined) normalizeNonBlank(opts.profile, "--profile");
|
|
494
610
|
const json = isJson(cmd);
|
|
495
611
|
const result = await login({
|
|
496
612
|
baseUrl: opts.baseUrl,
|
|
497
613
|
gitBaseUrl: opts.gitBaseUrl,
|
|
498
|
-
profile
|
|
614
|
+
// The global --profile names the profile this login writes.
|
|
615
|
+
profile: requestedProfileName(),
|
|
499
616
|
apiKey: opts.withToken ? await readStdin() : undefined,
|
|
500
617
|
// In JSON mode the device-flow narration (code, approve URL)
|
|
501
618
|
// still has to reach the human - on stderr.
|
|
@@ -536,11 +653,14 @@ export function buildProgram(): Command {
|
|
|
536
653
|
});
|
|
537
654
|
auth
|
|
538
655
|
.command("logout")
|
|
539
|
-
.description(
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
656
|
+
.description(
|
|
657
|
+
"Remove the stored credential (the key stays revocable on the dashboard). Removes the default profile unless --profile names another.",
|
|
658
|
+
)
|
|
659
|
+
.action(async (_opts: unknown, cmd: Command) => {
|
|
660
|
+
// The global --profile names the profile this removes.
|
|
661
|
+
const profile = requestedProfileName();
|
|
662
|
+
if (isJson(cmd)) printJson(await authLogoutData(profile));
|
|
663
|
+
else console.log(await authLogout(profile));
|
|
544
664
|
});
|
|
545
665
|
|
|
546
666
|
program
|
|
@@ -598,7 +718,7 @@ export function buildProgram(): Command {
|
|
|
598
718
|
) => {
|
|
599
719
|
const json = isJson(cmd);
|
|
600
720
|
if (json && opts.login) {
|
|
601
|
-
throw new
|
|
721
|
+
throw new CliUsageError("--login is not supported with `dharta setup --json`. Run `dharta setup --json` first, then `dharta login`.");
|
|
602
722
|
}
|
|
603
723
|
const interactive = !json && !opts.yes && process.stdin.isTTY;
|
|
604
724
|
const rl = interactive
|
|
@@ -665,7 +785,7 @@ export function buildProgram(): Command {
|
|
|
665
785
|
}, cmd: Command) => {
|
|
666
786
|
validateAuditLogInput(opts);
|
|
667
787
|
if (opts.csv && isJson(cmd)) {
|
|
668
|
-
throw new
|
|
788
|
+
throw new CliUsageError("Use either --csv or --json for audit-log, not both.");
|
|
669
789
|
}
|
|
670
790
|
const ctx = await buildContext();
|
|
671
791
|
if (isJson(cmd)) printJson(await auditLogData(ctx, opts));
|
|
@@ -717,7 +837,10 @@ export function buildProgram(): Command {
|
|
|
717
837
|
|
|
718
838
|
program
|
|
719
839
|
.command("api [method] [path]")
|
|
720
|
-
.description(
|
|
840
|
+
.description(
|
|
841
|
+
"Call any public JSON API endpoint with the active profile. Unstable escape hatch: the curated commands are the stable contract, and this passthrough follows the API. " +
|
|
842
|
+
"Reachable paths start with /api/, /me, /version, /api_keys, /model_connections, or /agent_templates; what the request may do is decided server-side by the key's scopes.",
|
|
843
|
+
)
|
|
721
844
|
.option("-F, --field <key=value>", "Add a JSON-coerced field to the request body", collect, [])
|
|
722
845
|
.option("-f, --raw-field <key=value>", "Add a string field to the request body", collect, [])
|
|
723
846
|
.option("-H, --header <name:value>", "Add a safe request header", collect, [])
|
|
@@ -782,47 +905,195 @@ export function buildProgram(): Command {
|
|
|
782
905
|
if (isJson(cmd)) printJson(await keysRevokeData(ctx, id));
|
|
783
906
|
else console.log(await keysRevoke(ctx, id));
|
|
784
907
|
});
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
908
|
+
keys
|
|
909
|
+
.command("rotate <id>")
|
|
910
|
+
.description("Replace a key with a new one carrying the same scopes, then retire the original.")
|
|
911
|
+
.addOption(
|
|
912
|
+
choiceOption(
|
|
913
|
+
"--grace-period <window>",
|
|
914
|
+
"How long the rotated key keeps working (default 24h)",
|
|
915
|
+
KEY_ROTATION_GRACE_PERIODS,
|
|
916
|
+
),
|
|
917
|
+
)
|
|
918
|
+
.action(async (id: string, opts: { gracePeriod?: string }, cmd: Command) => {
|
|
919
|
+
validateKeysRotateInput(id, opts);
|
|
920
|
+
const ctx = await buildContext();
|
|
921
|
+
if (isJson(cmd)) printJson(await keysRotateData(ctx, id, opts));
|
|
922
|
+
else console.log(await keysRotate(ctx, id, opts));
|
|
923
|
+
});
|
|
924
|
+
keys
|
|
925
|
+
.command("disable <id>")
|
|
926
|
+
.description("Suspend an API key without revoking it.")
|
|
927
|
+
.action(async (id: string, _opts: unknown, cmd: Command) => {
|
|
928
|
+
normalizeNonBlank(id, "API key id");
|
|
929
|
+
const ctx = await buildContext();
|
|
930
|
+
if (isJson(cmd)) printJson(await keysDisableData(ctx, id));
|
|
931
|
+
else console.log(await keysDisable(ctx, id));
|
|
932
|
+
});
|
|
933
|
+
keys
|
|
934
|
+
.command("enable <id>")
|
|
935
|
+
.description("Return a disabled API key to service.")
|
|
936
|
+
.action(async (id: string, _opts: unknown, cmd: Command) => {
|
|
937
|
+
normalizeNonBlank(id, "API key id");
|
|
938
|
+
const ctx = await buildContext();
|
|
939
|
+
if (isJson(cmd)) printJson(await keysEnableData(ctx, id));
|
|
940
|
+
else console.log(await keysEnable(ctx, id));
|
|
941
|
+
});
|
|
942
|
+
keys
|
|
943
|
+
.command("bulk")
|
|
944
|
+
.description("Apply one state change to several API keys atomically.")
|
|
945
|
+
.addArgument(choiceArgument("<action>", "state change to apply", KEY_BULK_ACTIONS))
|
|
946
|
+
.argument("<id...>", "API key ids")
|
|
947
|
+
.action(async (action: string, ids: string[], _opts: unknown, cmd: Command) => {
|
|
948
|
+
validateKeysBulkInput(action, ids);
|
|
949
|
+
const ctx = await buildContext();
|
|
950
|
+
if (isJson(cmd)) printJson(await keysBulkData(ctx, action, ids));
|
|
951
|
+
else console.log(await keysBulk(ctx, action, ids));
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
const models = program
|
|
955
|
+
.command("models")
|
|
956
|
+
.description("Manage model configuration and authentication.")
|
|
957
|
+
.action(helpOnlyAction("dharta models"));
|
|
958
|
+
const modelConnections = models
|
|
959
|
+
.command("auth")
|
|
960
|
+
.description("Manage model provider connections.")
|
|
961
|
+
.action(helpOnlyAction("dharta models auth"));
|
|
962
|
+
modelConnections
|
|
788
963
|
.command("list")
|
|
789
964
|
.description("List model provider credentials.")
|
|
790
965
|
.action(async (_opts: unknown, cmd: Command) => {
|
|
791
966
|
const ctx = await buildContext();
|
|
792
|
-
if (isJson(cmd)) printJson(await
|
|
793
|
-
else console.log(await
|
|
967
|
+
if (isJson(cmd)) printJson(await modelConnectionsListData(ctx));
|
|
968
|
+
else console.log(await modelConnectionsList(ctx));
|
|
794
969
|
});
|
|
795
|
-
|
|
970
|
+
modelConnections
|
|
796
971
|
.command("create")
|
|
797
972
|
.description("Store an encrypted model provider credential.")
|
|
798
|
-
.addArgument(choiceArgument("<provider>", "provider",
|
|
973
|
+
.addArgument(choiceArgument("<provider>", "provider", MODEL_CONNECTION_PROVIDERS))
|
|
799
974
|
.argument("<name>", "connector name")
|
|
800
975
|
.option("--key <plaintext>", "Provider API key plaintext")
|
|
801
976
|
.option("--key-stdin", "Read provider API key plaintext from stdin")
|
|
802
|
-
.
|
|
977
|
+
.option("--slug <slug>", "Connector slug used by --connector and model references (default: derived from the name)")
|
|
803
978
|
.option("--endpoint-url <url>", "Provider endpoint URL override")
|
|
804
979
|
.addOption(choiceOption("--api-format <format>", "Explicit provider API format", MODEL_API_FORMATS))
|
|
805
|
-
.action(async (provider:
|
|
980
|
+
.action(async (provider: ModelConnectionProvider, name: string, opts: {
|
|
806
981
|
key?: string;
|
|
807
982
|
keyStdin?: boolean;
|
|
808
|
-
|
|
983
|
+
slug?: string;
|
|
809
984
|
endpointUrl?: string;
|
|
810
985
|
apiFormat?: string;
|
|
811
986
|
}, cmd: Command) => {
|
|
812
|
-
|
|
813
|
-
const preparedOpts = await
|
|
987
|
+
validateModelConnectionsCreateOptions(provider, name, opts);
|
|
988
|
+
const preparedOpts = await prepareModelConnectionsCreateOptions(provider, name, opts);
|
|
989
|
+
const ctx = await buildContext();
|
|
990
|
+
if (isJson(cmd)) printJson(await modelConnectionsCreateData(ctx, provider, name, preparedOpts));
|
|
991
|
+
else console.log(await modelConnectionsCreate(ctx, provider, name, preparedOpts));
|
|
992
|
+
});
|
|
993
|
+
modelConnections
|
|
994
|
+
.command("rotate <id|slug>")
|
|
995
|
+
.description("Replace a connector's stored credential, keeping its slug and every selection that names it.")
|
|
996
|
+
.option("--key <plaintext>", "New provider API key plaintext")
|
|
997
|
+
.option("--key-stdin", "Read the new provider API key plaintext from stdin")
|
|
998
|
+
.option("--name <name>", "Rename the connector while rotating it")
|
|
999
|
+
.action(async (reference: string, opts: ModelConnectionRotateOptions, cmd: Command) => {
|
|
1000
|
+
validateModelConnectionsRotateOptions(reference, opts);
|
|
1001
|
+
const preparedOpts = await prepareModelConnectionsRotateOptions(reference, opts);
|
|
1002
|
+
const ctx = await buildContext();
|
|
1003
|
+
if (isJson(cmd)) printJson(await modelConnectionsRotateData(ctx, reference, preparedOpts));
|
|
1004
|
+
else console.log(await modelConnectionsRotate(ctx, reference, preparedOpts));
|
|
1005
|
+
});
|
|
1006
|
+
modelConnections
|
|
1007
|
+
.command("models <id|slug>")
|
|
1008
|
+
.description("Show the models a connector serves, or replace its enabled set and default.")
|
|
1009
|
+
.option("--enable <models>", "Comma-separated model ids to enable; this replaces the whole enabled set")
|
|
1010
|
+
.option("--enable-stdin", "Read the model ids to enable from stdin")
|
|
1011
|
+
.option("--default <model>", "Connector default model; must be in the enabled set")
|
|
1012
|
+
.option("--clear-default", "Leave the connector with no default model")
|
|
1013
|
+
.action(async (reference: string, opts: ModelConnectionModelsOptions, cmd: Command) => {
|
|
1014
|
+
validateModelConnectionsModelsOptions(reference, opts);
|
|
1015
|
+
if (!modelConnectionsModelsWritesOffers(opts)) {
|
|
1016
|
+
validateModelConnectionReference(reference);
|
|
1017
|
+
const readCtx = await buildContext();
|
|
1018
|
+
if (isJson(cmd)) printJson(await modelConnectionsModelsData(readCtx, reference));
|
|
1019
|
+
else console.log(await modelConnectionsModels(readCtx, reference));
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
const preparedOpts = await prepareModelConnectionsModelsOptions(reference, opts);
|
|
814
1023
|
const ctx = await buildContext();
|
|
815
|
-
if (isJson(cmd)) printJson(await
|
|
816
|
-
else console.log(await
|
|
1024
|
+
if (isJson(cmd)) printJson(await modelConnectionsConfigureModelsData(ctx, reference, preparedOpts));
|
|
1025
|
+
else console.log(await modelConnectionsConfigureModels(ctx, reference, preparedOpts));
|
|
817
1026
|
});
|
|
818
|
-
|
|
819
|
-
.command("revoke <id>")
|
|
1027
|
+
modelConnections
|
|
1028
|
+
.command("revoke <id|slug>")
|
|
820
1029
|
.description("Revoke a model provider credential.")
|
|
821
|
-
.action(async (
|
|
822
|
-
|
|
1030
|
+
.action(async (reference: string, _opts: unknown, cmd: Command) => {
|
|
1031
|
+
validateModelConnectionReference(reference);
|
|
823
1032
|
const ctx = await buildContext();
|
|
824
|
-
if (isJson(cmd)) printJson(await
|
|
825
|
-
else console.log(await
|
|
1033
|
+
if (isJson(cmd)) printJson(await modelConnectionsRevokeData(ctx, reference));
|
|
1034
|
+
else console.log(await modelConnectionsRevoke(ctx, reference));
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
const org = program
|
|
1038
|
+
.command("org")
|
|
1039
|
+
.description("Manage organization settings.")
|
|
1040
|
+
.action(helpOnlyAction("dharta org"));
|
|
1041
|
+
const orgModel = org
|
|
1042
|
+
.command("model")
|
|
1043
|
+
.description("Show the organization connector order, model reference, parameters, and what they resolve to.")
|
|
1044
|
+
.action(async (_opts: unknown, cmd: Command) => {
|
|
1045
|
+
const ctx = await buildContext();
|
|
1046
|
+
if (isJson(cmd)) printJson(await orgModelShowData(ctx));
|
|
1047
|
+
else console.log(await orgModelShow(ctx));
|
|
1048
|
+
});
|
|
1049
|
+
orgModel
|
|
1050
|
+
.command("set")
|
|
1051
|
+
.description("Update the organization connector order, model reference, endpoint override, and portable model parameters.")
|
|
1052
|
+
.option("--connector <slugs>", "Ordered connector slugs from `dharta models auth list`, or an empty string to unset")
|
|
1053
|
+
.option("--model-ref <reference>", "Model reference: <model>, connector:<slug>/<model>, or an empty string to unset")
|
|
1054
|
+
.option("--endpoint-url <url>", "Public HTTPS endpoint to call instead of the provider's standard API, or an empty string to clear")
|
|
1055
|
+
.addOption(choiceOption("--endpoint-kind <kind>", "Wire protocol for --endpoint-url", ORG_ENDPOINT_KINDS))
|
|
1056
|
+
.option("--temperature <number>", "Sampling temperature from 0 to 2")
|
|
1057
|
+
.option("--clear-temperature", "Clear the organization temperature")
|
|
1058
|
+
.option("--top-p <number>", "Nucleus sampling probability above 0 and at most 1")
|
|
1059
|
+
.option("--clear-top-p", "Clear the organization top-p")
|
|
1060
|
+
.option("--max-output-tokens <number>", "Positive output token limit")
|
|
1061
|
+
.option("--clear-max-output-tokens", "Clear the organization output token limit")
|
|
1062
|
+
.addOption(choiceOption("--reasoning-effort <level>", "Reasoning effort", ["low", "medium", "high"]))
|
|
1063
|
+
.option("--clear-reasoning-effort", "Clear the organization reasoning effort")
|
|
1064
|
+
.action(async (opts: OrgModelSetOptions, cmd: Command) => {
|
|
1065
|
+
validateOrgModelSetOptions(opts);
|
|
1066
|
+
const ctx = await buildContext();
|
|
1067
|
+
if (isJson(cmd)) printJson(await orgModelSetData(ctx, opts));
|
|
1068
|
+
else console.log(await orgModelSet(ctx, opts));
|
|
1069
|
+
});
|
|
1070
|
+
|
|
1071
|
+
org
|
|
1072
|
+
.command("rename <name>")
|
|
1073
|
+
.description("Change the organization display name. The slug never changes.")
|
|
1074
|
+
.action(async (name: string, _opts: unknown, cmd: Command) => {
|
|
1075
|
+
normalizeOrganizationName(name);
|
|
1076
|
+
const ctx = await buildContext();
|
|
1077
|
+
if (isJson(cmd)) printJson(await orgRenameData(ctx, name));
|
|
1078
|
+
else console.log(await orgRename(ctx, name));
|
|
1079
|
+
});
|
|
1080
|
+
org
|
|
1081
|
+
.command("support-access <state>")
|
|
1082
|
+
.description(`Allow or block Dharta support access to session content. ${STATE_HELP}`)
|
|
1083
|
+
.action(async (state: string, _opts: unknown, cmd: Command) => {
|
|
1084
|
+
const allowed = parseOnOff(state, "state");
|
|
1085
|
+
const ctx = await buildContext();
|
|
1086
|
+
if (isJson(cmd)) printJson(await orgSupportAccessData(ctx, allowed));
|
|
1087
|
+
else console.log(await orgSupportAccess(ctx, allowed));
|
|
1088
|
+
});
|
|
1089
|
+
org
|
|
1090
|
+
.command("transcript-access <state>")
|
|
1091
|
+
.description(`Allow or block API keys from reading end-user transcripts. ${STATE_HELP}`)
|
|
1092
|
+
.action(async (state: string, _opts: unknown, cmd: Command) => {
|
|
1093
|
+
const enabled = parseOnOff(state, "state");
|
|
1094
|
+
const ctx = await buildContext();
|
|
1095
|
+
if (isJson(cmd)) printJson(await orgTranscriptAccessData(ctx, enabled));
|
|
1096
|
+
else console.log(await orgTranscriptAccess(ctx, enabled));
|
|
826
1097
|
});
|
|
827
1098
|
|
|
828
1099
|
const members = program.command("members").description("Manage organization members.").action(helpOnlyAction("dharta members"));
|
|
@@ -840,7 +1111,7 @@ export function buildProgram(): Command {
|
|
|
840
1111
|
.addOption(choiceOption("--role <role>", "owner, admin, developer, billing, or viewer", MEMBER_ROLES))
|
|
841
1112
|
.action(async (email: string, opts: { role?: string }, cmd: Command) => {
|
|
842
1113
|
if (opts.role === undefined) {
|
|
843
|
-
throw new
|
|
1114
|
+
throw new CliUsageError("--role is required. Usage: dharta members invite <email> --role <role>");
|
|
844
1115
|
}
|
|
845
1116
|
validateMembersInviteInput(email);
|
|
846
1117
|
const ctx = await buildContext();
|
|
@@ -853,7 +1124,7 @@ export function buildProgram(): Command {
|
|
|
853
1124
|
.addOption(choiceOption("--role <role>", "owner, admin, developer, billing, or viewer", MEMBER_ROLES))
|
|
854
1125
|
.action(async (id: string, opts: { role?: string }, cmd: Command) => {
|
|
855
1126
|
if (opts.role === undefined) {
|
|
856
|
-
throw new
|
|
1127
|
+
throw new CliUsageError("--role is required. Usage: dharta members set-role <id> --role <role>");
|
|
857
1128
|
}
|
|
858
1129
|
parsePositiveInteger(id, "member id");
|
|
859
1130
|
const ctx = await buildContext();
|
|
@@ -869,6 +1140,35 @@ export function buildProgram(): Command {
|
|
|
869
1140
|
if (isJson(cmd)) printJson(await membersRemoveData(ctx, id));
|
|
870
1141
|
else console.log(await membersRemove(ctx, id));
|
|
871
1142
|
});
|
|
1143
|
+
members
|
|
1144
|
+
.command("revoke-invitation <id>")
|
|
1145
|
+
.description("Revoke a pending invitation.")
|
|
1146
|
+
.action(async (id: string, _opts: unknown, cmd: Command) => {
|
|
1147
|
+
parsePositiveInteger(id, "invitation id");
|
|
1148
|
+
const ctx = await buildContext();
|
|
1149
|
+
if (isJson(cmd)) printJson(await membersRevokeInvitationData(ctx, id));
|
|
1150
|
+
else console.log(await membersRevokeInvitation(ctx, id));
|
|
1151
|
+
});
|
|
1152
|
+
members
|
|
1153
|
+
.command("resend-invitation <id>")
|
|
1154
|
+
.description("Re-send a pending invitation, keeping the original link.")
|
|
1155
|
+
.action(async (id: string, _opts: unknown, cmd: Command) => {
|
|
1156
|
+
parsePositiveInteger(id, "invitation id");
|
|
1157
|
+
const ctx = await buildContext();
|
|
1158
|
+
if (isJson(cmd)) printJson(await membersResendInvitationData(ctx, id));
|
|
1159
|
+
else console.log(await membersResendInvitation(ctx, id));
|
|
1160
|
+
});
|
|
1161
|
+
|
|
1162
|
+
const repos = program.command("repos").description("Manage hosted source repositories.").action(helpOnlyAction("dharta repos"));
|
|
1163
|
+
repos
|
|
1164
|
+
.command("disconnect-github <slug>")
|
|
1165
|
+
.description("Stop a GitHub repository from producing releases. Live releases are untouched.")
|
|
1166
|
+
.action(async (slug: string, _opts: unknown, cmd: Command) => {
|
|
1167
|
+
validateAgentSlug(slug, "source repo slug");
|
|
1168
|
+
const ctx = await buildContext();
|
|
1169
|
+
if (isJson(cmd)) printJson(await reposDisconnectGithubData(ctx, slug));
|
|
1170
|
+
else console.log(await reposDisconnectGithub(ctx, slug));
|
|
1171
|
+
});
|
|
872
1172
|
|
|
873
1173
|
const kartas = program.command("kartas").description("Manage durable kartas and virtual employees.").action(helpOnlyAction("dharta kartas"));
|
|
874
1174
|
kartas
|
|
@@ -993,12 +1293,31 @@ export function buildProgram(): Command {
|
|
|
993
1293
|
.addOption(choiceOption("--role <role>", "viewer, participant, or manager", DHARTA_MEMBER_ROLES))
|
|
994
1294
|
.action(async (id: string, membershipId: string, opts: { role?: string }, cmd: Command) => {
|
|
995
1295
|
if (opts.role === undefined) {
|
|
996
|
-
throw new
|
|
1296
|
+
throw new CliUsageError("--role is required. Usage: dharta kartas set-role <id> <membership-id> --role <role>");
|
|
997
1297
|
}
|
|
998
1298
|
const ctx = await buildContext();
|
|
999
1299
|
if (isJson(cmd)) printJson(await kartaMembersSetRoleData(ctx, id, membershipId, opts.role));
|
|
1000
1300
|
else console.log(await kartaMembersSetRole(ctx, id, membershipId, opts.role));
|
|
1001
1301
|
});
|
|
1302
|
+
kartas
|
|
1303
|
+
.command("share <id>")
|
|
1304
|
+
.description("Mint a no-sign-in link to a karta and reveal it once.")
|
|
1305
|
+
.action(async (id: string, _opts: unknown, cmd: Command) => {
|
|
1306
|
+
normalizeNonBlank(id, "karta id");
|
|
1307
|
+
const ctx = await buildContext();
|
|
1308
|
+
if (isJson(cmd)) printJson(await kartaShareData(ctx, id));
|
|
1309
|
+
else console.log(await kartaShare(ctx, id));
|
|
1310
|
+
});
|
|
1311
|
+
kartas
|
|
1312
|
+
.command("unshare <id> <share-id>")
|
|
1313
|
+
.description("Revoke a karta share link.")
|
|
1314
|
+
.action(async (id: string, shareId: string, _opts: unknown, cmd: Command) => {
|
|
1315
|
+
normalizeNonBlank(id, "karta id");
|
|
1316
|
+
normalizeNonBlank(shareId, "share id");
|
|
1317
|
+
const ctx = await buildContext();
|
|
1318
|
+
if (isJson(cmd)) printJson(await kartaUnshareData(ctx, id, shareId));
|
|
1319
|
+
else console.log(await kartaUnshare(ctx, id, shareId));
|
|
1320
|
+
});
|
|
1002
1321
|
kartas
|
|
1003
1322
|
.command("revoke <id> <membership-id>")
|
|
1004
1323
|
.description("Remove a person's access to a dharta.")
|
|
@@ -1210,7 +1529,7 @@ export function buildProgram(): Command {
|
|
|
1210
1529
|
else console.log(await appearanceGet(ctx, resolved));
|
|
1211
1530
|
});
|
|
1212
1531
|
appearanceOpts(appearance.command("set"))
|
|
1213
|
-
.description("Merge appearance JSON from a local file.")
|
|
1532
|
+
.description("Merge appearance JSON from a local file. A token set to null in the file is cleared and falls back to the preset.")
|
|
1214
1533
|
.option("--file <file>", "JSON file containing appearance tokens")
|
|
1215
1534
|
.action(async (_opts: { agent?: string; project?: string; dir?: string; file?: string }, cmd: Command) => {
|
|
1216
1535
|
const opts = commandOpts<{ agent?: string; project?: string; dir?: string; file?: string }>(cmd);
|
|
@@ -1342,6 +1661,232 @@ export function buildProgram(): Command {
|
|
|
1342
1661
|
else console.log(await directorySessions(ctx, resolved));
|
|
1343
1662
|
});
|
|
1344
1663
|
|
|
1664
|
+
// `fleet` mirrors the fleet console's write side. Reads live under
|
|
1665
|
+
// `directories` and `analytics`; everything here changes something.
|
|
1666
|
+
const fleet = program.command("fleet").description("Run fleet-wide changes, triage, and exports.").action(helpOnlyAction("dharta fleet"));
|
|
1667
|
+
|
|
1668
|
+
const fleetOperations = fleet.command("operations").description("Preview and apply bulk fleet changes.").action(helpOnlyAction("dharta fleet operations"));
|
|
1669
|
+
fleetOperations
|
|
1670
|
+
.command("create")
|
|
1671
|
+
.description("Freeze a selection into a draft operation. Nothing is applied until you confirm it.")
|
|
1672
|
+
.option("--action <key>", "Action to run, for example set_lifecycle or pause_serving")
|
|
1673
|
+
.option("--entity-type <type>", `Entity type (${FLEET_ENTITY_TYPES.join(", ")})`)
|
|
1674
|
+
.option("--selection-mode <mode>", `How targets are chosen (${FLEET_SELECTION_MODES.join(", ")})`)
|
|
1675
|
+
.option("--id <id...>", "Target row id; repeat for more")
|
|
1676
|
+
.option("--saved-view <id>", "Take the targets from a saved view")
|
|
1677
|
+
.option("--filter <query>", "Take the targets from a catalog filter")
|
|
1678
|
+
.option("--param <key=value...>", "Action parameter; repeat for more")
|
|
1679
|
+
.action(async (opts: OperationCreateOptions, cmd: Command) => {
|
|
1680
|
+
const ctx = await buildContext();
|
|
1681
|
+
if (isJson(cmd)) printJson(await fleetOperationCreateData(ctx, opts));
|
|
1682
|
+
else console.log(await fleetOperationCreate(ctx, opts));
|
|
1683
|
+
});
|
|
1684
|
+
fleetOperations
|
|
1685
|
+
.command("confirm <id>")
|
|
1686
|
+
.description("Apply a draft operation. Needs fleet:operations:write.")
|
|
1687
|
+
.option("--phrase <phrase>", "The confirmation phrase the draft returned")
|
|
1688
|
+
.action(async (id: string, opts: { phrase?: string }, cmd: Command) => {
|
|
1689
|
+
const ctx = await buildContext();
|
|
1690
|
+
if (isJson(cmd)) printJson(await fleetOperationConfirmData(ctx, id, opts.phrase));
|
|
1691
|
+
else console.log(await fleetOperationConfirm(ctx, id, opts.phrase));
|
|
1692
|
+
});
|
|
1693
|
+
fleetOperations
|
|
1694
|
+
.command("cancel <id>")
|
|
1695
|
+
.description("Stop an operation's remaining targets. Needs fleet:operations:write.")
|
|
1696
|
+
.action(async (id: string, _opts: unknown, cmd: Command) => {
|
|
1697
|
+
const ctx = await buildContext();
|
|
1698
|
+
if (isJson(cmd)) printJson(await fleetOperationCancelData(ctx, id));
|
|
1699
|
+
else console.log(await fleetOperationCancel(ctx, id));
|
|
1700
|
+
});
|
|
1701
|
+
fleetOperations
|
|
1702
|
+
.command("retry <id>")
|
|
1703
|
+
.description("Requeue only the failed targets. Needs fleet:operations:write.")
|
|
1704
|
+
.action(async (id: string, _opts: unknown, cmd: Command) => {
|
|
1705
|
+
const ctx = await buildContext();
|
|
1706
|
+
if (isJson(cmd)) printJson(await fleetOperationRetryData(ctx, id));
|
|
1707
|
+
else console.log(await fleetOperationRetry(ctx, id));
|
|
1708
|
+
});
|
|
1709
|
+
|
|
1710
|
+
const fleetRollouts = fleet.command("rollouts").description("Drive a staged release rollout.").action(helpOnlyAction("dharta fleet rollouts"));
|
|
1711
|
+
// Every rollout verb needs fleet:operations:write, not just the two the
|
|
1712
|
+
// console gates with a step-up: pausing or resuming a release mid-flight
|
|
1713
|
+
// moves live releases the same way advancing it does.
|
|
1714
|
+
for (const [verb, description] of [
|
|
1715
|
+
["advance", "Move the rollout to its next ring. Needs fleet:operations:write."],
|
|
1716
|
+
["pause", "Hold the rollout where it is. Needs fleet:operations:write."],
|
|
1717
|
+
["resume", "Resume a paused rollout. Needs fleet:operations:write."],
|
|
1718
|
+
[
|
|
1719
|
+
"evaluate",
|
|
1720
|
+
"Run the health evaluation now instead of waiting for the window. Needs fleet:operations:write.",
|
|
1721
|
+
],
|
|
1722
|
+
] as const) {
|
|
1723
|
+
fleetRollouts
|
|
1724
|
+
.command(`${verb} <id>`)
|
|
1725
|
+
.description(description)
|
|
1726
|
+
.action(async (id: string, _opts: unknown, cmd: Command) => {
|
|
1727
|
+
const ctx = await buildContext();
|
|
1728
|
+
if (isJson(cmd)) printJson(await fleetRolloutData(ctx, verb, id));
|
|
1729
|
+
else console.log(await fleetRollout(ctx, verb, id));
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1732
|
+
fleetRollouts
|
|
1733
|
+
.command("rollback <id>")
|
|
1734
|
+
.description("Revert every target to the previous release. Needs fleet:operations:write.")
|
|
1735
|
+
.action(async (id: string, _opts: unknown, cmd: Command) => {
|
|
1736
|
+
const ctx = await buildContext();
|
|
1737
|
+
if (isJson(cmd)) printJson(await fleetRolloutRollbackData(ctx, id));
|
|
1738
|
+
else console.log(await fleetRolloutRollback(ctx, id));
|
|
1739
|
+
});
|
|
1740
|
+
|
|
1741
|
+
const fleetFindings = fleet.command("findings").description("Triage anomaly findings.").action(helpOnlyAction("dharta fleet findings"));
|
|
1742
|
+
for (const [verb, description] of [
|
|
1743
|
+
["acknowledge", "Take ownership of a finding."],
|
|
1744
|
+
["resolve", "Close a finding."],
|
|
1745
|
+
["reopen", "Reopen a resolved or snoozed finding."],
|
|
1746
|
+
["promote", "Raise a finding to an incident."],
|
|
1747
|
+
] as const) {
|
|
1748
|
+
fleetFindings
|
|
1749
|
+
.command(`${verb} <id>`)
|
|
1750
|
+
.description(description)
|
|
1751
|
+
.action(async (id: string, _opts: unknown, cmd: Command) => {
|
|
1752
|
+
const ctx = await buildContext();
|
|
1753
|
+
if (isJson(cmd)) printJson(await fleetFindingData(ctx, verb, id));
|
|
1754
|
+
else console.log(await fleetFinding(ctx, verb, id));
|
|
1755
|
+
});
|
|
1756
|
+
}
|
|
1757
|
+
fleetFindings
|
|
1758
|
+
.command("snooze <id>")
|
|
1759
|
+
.description("Hide a finding until the window is up.")
|
|
1760
|
+
.option("--duration <window>", `Snooze window (${FLEET_SNOOZE_DURATIONS.join(", ")})`)
|
|
1761
|
+
.action(async (id: string, opts: SnoozeOptions, cmd: Command) => {
|
|
1762
|
+
const ctx = await buildContext();
|
|
1763
|
+
if (isJson(cmd)) printJson(await fleetFindingSnoozeData(ctx, id, opts));
|
|
1764
|
+
else console.log(await fleetFindingSnooze(ctx, id, opts));
|
|
1765
|
+
});
|
|
1766
|
+
fleetFindings
|
|
1767
|
+
.command("classify <id>")
|
|
1768
|
+
.description("Record what a finding turned out to be.")
|
|
1769
|
+
.option("--as <classification>", "Classification to record")
|
|
1770
|
+
.action(async (id: string, opts: ClassifyOptions, cmd: Command) => {
|
|
1771
|
+
const ctx = await buildContext();
|
|
1772
|
+
if (isJson(cmd)) printJson(await fleetFindingClassifyData(ctx, id, opts));
|
|
1773
|
+
else console.log(await fleetFindingClassify(ctx, id, opts));
|
|
1774
|
+
});
|
|
1775
|
+
fleetFindings
|
|
1776
|
+
.command("assign <id>")
|
|
1777
|
+
.description("Assign a finding to a member, or clear the owner.")
|
|
1778
|
+
.option("--membership-id <id>", "Organization membership id to assign")
|
|
1779
|
+
.option("--unassign", "Clear the current owner")
|
|
1780
|
+
.action(async (id: string, opts: AssignOptions, cmd: Command) => {
|
|
1781
|
+
const ctx = await buildContext();
|
|
1782
|
+
if (isJson(cmd)) printJson(await fleetFindingAssignData(ctx, id, opts));
|
|
1783
|
+
else console.log(await fleetFindingAssign(ctx, id, opts));
|
|
1784
|
+
});
|
|
1785
|
+
fleetFindings
|
|
1786
|
+
.command("update <id>")
|
|
1787
|
+
.description("Set or clear a finding's runbook link.")
|
|
1788
|
+
.option("--runbook-url <url>", "Runbook URL; pass an empty string to clear")
|
|
1789
|
+
.action(async (id: string, opts: RunbookOptions, cmd: Command) => {
|
|
1790
|
+
const ctx = await buildContext();
|
|
1791
|
+
if (isJson(cmd)) printJson(await fleetFindingUpdateData(ctx, id, opts));
|
|
1792
|
+
else console.log(await fleetFindingUpdate(ctx, id, opts));
|
|
1793
|
+
});
|
|
1794
|
+
|
|
1795
|
+
const fleetViews = fleet.command("views").description("Manage saved fleet views.").action(helpOnlyAction("dharta fleet views"));
|
|
1796
|
+
const viewWriteOpts = (cmd: ReturnType<typeof fleetViews.command>) =>
|
|
1797
|
+
cmd
|
|
1798
|
+
.option("--entity-type <type>", "Entity type the view lists")
|
|
1799
|
+
.option("--name <name>", "View name")
|
|
1800
|
+
.option("--definition <json>", "Filters, columns and sort, as a JSON object")
|
|
1801
|
+
.option("--description <text>", "What the view is for")
|
|
1802
|
+
.option("--visibility <level>", `Who can see it (${FLEET_VIEW_VISIBILITIES.join(", ")})`)
|
|
1803
|
+
.option("--team-id <id>", "Owning team, for team visibility")
|
|
1804
|
+
.option("--organization-default", "Make it the organization's default view");
|
|
1805
|
+
viewWriteOpts(fleetViews.command("create"))
|
|
1806
|
+
.description("Create a saved view.")
|
|
1807
|
+
.action(async (opts: ViewWriteOptions, cmd: Command) => {
|
|
1808
|
+
const ctx = await buildContext();
|
|
1809
|
+
if (isJson(cmd)) printJson(await fleetViewCreateData(ctx, opts));
|
|
1810
|
+
else console.log(await fleetViewCreate(ctx, opts));
|
|
1811
|
+
});
|
|
1812
|
+
viewWriteOpts(fleetViews.command("update <id>"))
|
|
1813
|
+
.description("Change a saved view.")
|
|
1814
|
+
.action(async (id: string, opts: ViewWriteOptions, cmd: Command) => {
|
|
1815
|
+
const ctx = await buildContext();
|
|
1816
|
+
if (isJson(cmd)) printJson(await fleetViewUpdateData(ctx, id, opts));
|
|
1817
|
+
else console.log(await fleetViewUpdate(ctx, id, opts));
|
|
1818
|
+
});
|
|
1819
|
+
fleetViews
|
|
1820
|
+
.command("delete <id>")
|
|
1821
|
+
.alias("rm")
|
|
1822
|
+
.description("Delete a saved view.")
|
|
1823
|
+
.action(async (id: string, _opts: unknown, cmd: Command) => {
|
|
1824
|
+
const ctx = await buildContext();
|
|
1825
|
+
if (isJson(cmd)) printJson(await fleetViewDeleteData(ctx, id));
|
|
1826
|
+
else console.log(await fleetViewDelete(ctx, id));
|
|
1827
|
+
});
|
|
1828
|
+
fleetViews
|
|
1829
|
+
.command("favorite <id>")
|
|
1830
|
+
.description("Favorite a saved view for the key's owner.")
|
|
1831
|
+
.action(async (id: string, _opts: unknown, cmd: Command) => {
|
|
1832
|
+
const ctx = await buildContext();
|
|
1833
|
+
if (isJson(cmd)) printJson(await fleetViewFavoriteData(ctx, id));
|
|
1834
|
+
else console.log(await fleetViewFavorite(ctx, id));
|
|
1835
|
+
});
|
|
1836
|
+
fleetViews
|
|
1837
|
+
.command("unfavorite <id>")
|
|
1838
|
+
.description("Remove a saved view from the key owner's favorites.")
|
|
1839
|
+
.action(async (id: string, _opts: unknown, cmd: Command) => {
|
|
1840
|
+
const ctx = await buildContext();
|
|
1841
|
+
if (isJson(cmd)) printJson(await fleetViewUnfavoriteData(ctx, id));
|
|
1842
|
+
else console.log(await fleetViewUnfavorite(ctx, id));
|
|
1843
|
+
});
|
|
1844
|
+
|
|
1845
|
+
const fleetAnalytics = fleet.command("analytics").description("Save and unpin Analytics charts.").action(helpOnlyAction("dharta fleet analytics"));
|
|
1846
|
+
fleetAnalytics
|
|
1847
|
+
.command("save")
|
|
1848
|
+
.description("Save an explorer chart and pin it to your Analytics list.")
|
|
1849
|
+
.option("--name <name>", "Chart name")
|
|
1850
|
+
.option("--metric <metric>", "Metric to chart. Defaults to turns.")
|
|
1851
|
+
.option("--description <text>", "What the chart shows")
|
|
1852
|
+
.option("--range <range>", "Time range, for example 30d")
|
|
1853
|
+
.option("--starts-at <timestamp>", "Custom range start")
|
|
1854
|
+
.option("--ends-at <timestamp>", "Custom range end")
|
|
1855
|
+
.option("--scope-kind <kind>", "Scope the chart to a kind of row")
|
|
1856
|
+
.option("--scope-ids <ids>", "Comma-separated scope ids")
|
|
1857
|
+
.option("--bucket <bucket>", "Time bucket, for example day")
|
|
1858
|
+
.option("--aggregation <aggregation>", "How values are combined")
|
|
1859
|
+
.option("--comparison <comparison>", "Comparison series to draw")
|
|
1860
|
+
.option("--comparison-saved-view <id>", "Saved cohort to compare against")
|
|
1861
|
+
.option("--breakdown <field...>", "Break the series down by a field; repeat for more")
|
|
1862
|
+
.option("--event <type...>", "Event type to include; repeat for more")
|
|
1863
|
+
.option("--filter <key=value...>", "Filter; repeat for more")
|
|
1864
|
+
.action(async (opts: AnalyticsSaveOptions, cmd: Command) => {
|
|
1865
|
+
const ctx = await buildContext();
|
|
1866
|
+
if (isJson(cmd)) printJson(await fleetAnalyticsSaveData(ctx, opts));
|
|
1867
|
+
else console.log(await fleetAnalyticsSave(ctx, opts));
|
|
1868
|
+
});
|
|
1869
|
+
fleetAnalytics
|
|
1870
|
+
.command("unpin <id>")
|
|
1871
|
+
.description("Remove a chart from your Analytics list. The saved view survives.")
|
|
1872
|
+
.action(async (id: string, _opts: unknown, cmd: Command) => {
|
|
1873
|
+
const ctx = await buildContext();
|
|
1874
|
+
if (isJson(cmd)) printJson(await fleetAnalyticsUnpinData(ctx, id));
|
|
1875
|
+
else console.log(await fleetAnalyticsUnpin(ctx, id));
|
|
1876
|
+
});
|
|
1877
|
+
|
|
1878
|
+
const fleetExports = fleet.command("exports").description("Export catalog rows as CSV.").action(helpOnlyAction("dharta fleet exports"));
|
|
1879
|
+
fleetExports
|
|
1880
|
+
.command("create")
|
|
1881
|
+
.description("Write the named rows to stdout as CSV.")
|
|
1882
|
+
.option("--entity-type <type>", `Entity type (${FLEET_ENTITY_TYPES.join(", ")})`)
|
|
1883
|
+
.option("--id <id...>", "Row id to export; repeat for more")
|
|
1884
|
+
.action(async (opts: ExportCreateOptions, cmd: Command) => {
|
|
1885
|
+
const ctx = await buildContext();
|
|
1886
|
+
if (isJson(cmd)) printJson(await fleetExportCreateData(ctx, opts));
|
|
1887
|
+
else console.log(await fleetExportCreate(ctx, opts));
|
|
1888
|
+
});
|
|
1889
|
+
|
|
1345
1890
|
const webhooks = program.command("webhooks").description("Manage webhook endpoints.").action(helpOnlyAction("dharta webhooks"));
|
|
1346
1891
|
webhooks
|
|
1347
1892
|
.command("list")
|
|
@@ -1384,6 +1929,15 @@ export function buildProgram(): Command {
|
|
|
1384
1929
|
if (isJson(cmd)) printJson(await webhooksEnableData(ctx, id));
|
|
1385
1930
|
else console.log(await webhooksEnable(ctx, id));
|
|
1386
1931
|
});
|
|
1932
|
+
webhooks
|
|
1933
|
+
.command("test <id>")
|
|
1934
|
+
.description("Send one test event through the real delivery path.")
|
|
1935
|
+
.action(async (id: string, _opts: unknown, cmd: Command) => {
|
|
1936
|
+
normalizeNonBlank(id, "Webhook endpoint id");
|
|
1937
|
+
const ctx = await buildContext();
|
|
1938
|
+
if (isJson(cmd)) printJson(await webhooksTestData(ctx, id));
|
|
1939
|
+
else console.log(await webhooksTest(ctx, id));
|
|
1940
|
+
});
|
|
1387
1941
|
webhooks
|
|
1388
1942
|
.command("delete <id>")
|
|
1389
1943
|
.alias("rm")
|
|
@@ -1438,6 +1992,7 @@ export function buildProgram(): Command {
|
|
|
1438
1992
|
.option("--user <id>", "Simulate a verified end user (per-user memory behavior)")
|
|
1439
1993
|
.option("--port <port>", "Serve the local agent API on a stable port (for widgets/frontends)")
|
|
1440
1994
|
.option("--verbose", "Stream agent-runtime output and parity details")
|
|
1995
|
+
.addOption(choiceOption("--model-auth <method>", "Anthropic authentication: api-key or claude", DEV_MODEL_AUTH_VALUES))
|
|
1441
1996
|
.option("--release <vN>", "Run the exact built tree prod serves (audited archive pull)")
|
|
1442
1997
|
.addOption(choiceOption("--env <mode>", "Bind prod env vars instead of .env", ["remote"]))
|
|
1443
1998
|
.option("--include-secrets", "With --env remote: fetch secret values (requires env:secrets:read; audited)")
|
|
@@ -1451,6 +2006,7 @@ export function buildProgram(): Command {
|
|
|
1451
2006
|
user?: string;
|
|
1452
2007
|
port?: string;
|
|
1453
2008
|
verbose?: boolean;
|
|
2009
|
+
modelAuth?: DevModelAuth;
|
|
1454
2010
|
release?: string;
|
|
1455
2011
|
env?: string;
|
|
1456
2012
|
includeSecrets?: boolean;
|
|
@@ -1461,7 +2017,7 @@ export function buildProgram(): Command {
|
|
|
1461
2017
|
) => {
|
|
1462
2018
|
rejectJson(cmd, "dharta dev");
|
|
1463
2019
|
if (pathArg !== undefined && opts.dir !== undefined) {
|
|
1464
|
-
throw new
|
|
2020
|
+
throw new CliUsageError("Use either [path] or --dir, not both.");
|
|
1465
2021
|
}
|
|
1466
2022
|
const dir = pathArg !== undefined
|
|
1467
2023
|
? normalizeNonBlank(pathArg, "path")
|
|
@@ -1470,7 +2026,7 @@ export function buildProgram(): Command {
|
|
|
1470
2026
|
: undefined;
|
|
1471
2027
|
if (opts.release !== undefined) parseReleaseVersion(opts.release, "--release");
|
|
1472
2028
|
if (opts.includeSecrets && opts.env !== "remote") {
|
|
1473
|
-
throw new
|
|
2029
|
+
throw new CliUsageError("--include-secrets requires --env remote.");
|
|
1474
2030
|
}
|
|
1475
2031
|
validateAgentSelectorOptions({ agent: opts.agent, project: opts.project });
|
|
1476
2032
|
await devCmd({
|
|
@@ -1478,6 +2034,7 @@ export function buildProgram(): Command {
|
|
|
1478
2034
|
message: opts.message,
|
|
1479
2035
|
user: opts.user,
|
|
1480
2036
|
verbose: opts.verbose,
|
|
2037
|
+
modelAuth: opts.modelAuth,
|
|
1481
2038
|
port: opts.port ? parsePort(opts.port) : undefined,
|
|
1482
2039
|
release: opts.release,
|
|
1483
2040
|
envRemote: opts.env === "remote",
|
|
@@ -1488,6 +2045,7 @@ export function buildProgram(): Command {
|
|
|
1488
2045
|
);
|
|
1489
2046
|
|
|
1490
2047
|
registerArtifactCommands(program);
|
|
2048
|
+
registerGenuiCommands(program);
|
|
1491
2049
|
|
|
1492
2050
|
const sessionsOpts = <T extends Command>(cmd: T): T =>
|
|
1493
2051
|
cmd
|
|
@@ -1809,9 +2367,8 @@ export function buildProgram(): Command {
|
|
|
1809
2367
|
.option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
|
|
1810
2368
|
.option("-p, --project <slug>", "Alias of --agent")
|
|
1811
2369
|
.option("--dir <dir>", "Directory to resolve the agent from (default: current directory)")
|
|
1812
|
-
.
|
|
1813
|
-
.option("--
|
|
1814
|
-
.option("--model <model>", "Provider model id")
|
|
2370
|
+
.option("--connector <slugs>", "Ordered connection slugs from `dharta models auth list`; use karta for the platform connection or an empty string to inherit")
|
|
2371
|
+
.option("--model-ref <reference>", "Model reference: <model>, connector:<slug>/<model>, or an empty string for the connector default")
|
|
1815
2372
|
.option("--temperature <number>", "Sampling temperature from 0 to 2")
|
|
1816
2373
|
.option("--clear-temperature", "Inherit temperature")
|
|
1817
2374
|
.option("--top-p <number>", "Nucleus sampling probability above 0 and at most 1")
|
|
@@ -1896,6 +2453,132 @@ export function buildProgram(): Command {
|
|
|
1896
2453
|
if (isJson(cmd)) printJson(await agentsWorkspaceAccessSetData(ctx, resolved, patch));
|
|
1897
2454
|
else console.log(await agentsWorkspaceAccessSet(ctx, resolved, patch));
|
|
1898
2455
|
});
|
|
2456
|
+
agent
|
|
2457
|
+
.command("rename <name> [slug]")
|
|
2458
|
+
.description("Change an agent's display name; the slug stays as it is.")
|
|
2459
|
+
.option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
|
|
2460
|
+
.option("-p, --project <slug>", "Alias of --agent")
|
|
2461
|
+
.option("--dir <dir>", "Agent directory (default: current directory)")
|
|
2462
|
+
.action(async (
|
|
2463
|
+
name: string,
|
|
2464
|
+
slugArg: string | undefined,
|
|
2465
|
+
_opts: { agent?: string; project?: string; dir?: string },
|
|
2466
|
+
cmd: Command,
|
|
2467
|
+
) => {
|
|
2468
|
+
const opts = commandOpts<{ agent?: string; project?: string; dir?: string }>(cmd);
|
|
2469
|
+
const resolvedOpts = await resolveAgentOptions(opts, slugArg);
|
|
2470
|
+
const ctx = await buildContext();
|
|
2471
|
+
const resolved = resolvedOpts.agent;
|
|
2472
|
+
if (isJson(cmd)) printJson(await agentsRenameData(ctx, resolved, name));
|
|
2473
|
+
else console.log(await agentsRename(ctx, resolved, name));
|
|
2474
|
+
});
|
|
2475
|
+
agent
|
|
2476
|
+
.command("durable-workspace <state> [slug]")
|
|
2477
|
+
.description(
|
|
2478
|
+
`Enable or disable the durable workspace for an agent's kartas - a cost setting, since durable storage is billed per GB. ${STATE_HELP}`,
|
|
2479
|
+
)
|
|
2480
|
+
.option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
|
|
2481
|
+
.option("-p, --project <slug>", "Alias of --agent")
|
|
2482
|
+
.option("--dir <dir>", "Agent directory (default: current directory)")
|
|
2483
|
+
.action(async (
|
|
2484
|
+
state: string,
|
|
2485
|
+
slugArg: string | undefined,
|
|
2486
|
+
_opts: { agent?: string; project?: string; dir?: string },
|
|
2487
|
+
cmd: Command,
|
|
2488
|
+
) => {
|
|
2489
|
+
const opts = commandOpts<{ agent?: string; project?: string; dir?: string }>(cmd);
|
|
2490
|
+
const enabled = parseOnOff(state, "state");
|
|
2491
|
+
const resolvedOpts = await resolveAgentOptions(opts, slugArg);
|
|
2492
|
+
const ctx = await buildContext();
|
|
2493
|
+
const resolved = resolvedOpts.agent;
|
|
2494
|
+
if (isJson(cmd)) printJson(await agentsDurableWorkspaceSetData(ctx, resolved, enabled));
|
|
2495
|
+
else console.log(await agentsDurableWorkspaceSet(ctx, resolved, enabled));
|
|
2496
|
+
});
|
|
2497
|
+
agent
|
|
2498
|
+
.command("create [name]")
|
|
2499
|
+
.description(
|
|
2500
|
+
"Create a hosted agent, or link an installed GitHub repository as its source with --github-repo. `dharta create` scaffolds locally instead.",
|
|
2501
|
+
)
|
|
2502
|
+
.option("--slug <slug>", "Agent or source-repo slug (default: derived from the name or repository)")
|
|
2503
|
+
.option(
|
|
2504
|
+
"--github-repo <repo>",
|
|
2505
|
+
"Installed GitHub repository to link as the source of truth, by id or owner/name (see `dharta agent github-repos`)",
|
|
2506
|
+
)
|
|
2507
|
+
.option("--branch <branch>", "Deploy branch for the linked repository; needs --github-repo")
|
|
2508
|
+
.action(async (name: string | undefined, _opts: AgentCreateOptions, cmd: Command) => {
|
|
2509
|
+
const opts = commandOpts<AgentCreateOptions>(cmd);
|
|
2510
|
+
validateAgentCreateInput(name, opts);
|
|
2511
|
+
const ctx = await buildContext();
|
|
2512
|
+
if (isJson(cmd)) printJson(await agentsCreateData(ctx, name, opts));
|
|
2513
|
+
else console.log(await agentsCreate(ctx, name, opts));
|
|
2514
|
+
});
|
|
2515
|
+
agent
|
|
2516
|
+
.command("github-repos")
|
|
2517
|
+
.description("List the GitHub repositories the Dharta App is installed on, with the ids `--github-repo` takes.")
|
|
2518
|
+
.action(async (_opts: Record<string, never>, cmd: Command) => {
|
|
2519
|
+
const ctx = await buildContext();
|
|
2520
|
+
if (isJson(cmd)) printJson(await agentsGithubReposData(ctx));
|
|
2521
|
+
else console.log(await agentsGithubRepos(ctx));
|
|
2522
|
+
});
|
|
2523
|
+
agent
|
|
2524
|
+
.command("create-from-template <template> <name>")
|
|
2525
|
+
.description(
|
|
2526
|
+
"Create a hosted agent from a built-in template and publish its first release. `dharta create` scaffolds locally instead.",
|
|
2527
|
+
)
|
|
2528
|
+
.option("--slug <slug>", "Agent slug (default: derived from the name)")
|
|
2529
|
+
.option("--description <text>", "Short description")
|
|
2530
|
+
.option("--harness <type>", "Harness slug (default: Dharta's curated default)")
|
|
2531
|
+
.option("--harness-version <version>", "Harness version; needs --harness")
|
|
2532
|
+
.action(async (
|
|
2533
|
+
template: string,
|
|
2534
|
+
name: string,
|
|
2535
|
+
_opts: AgentCreateFromTemplateOptions,
|
|
2536
|
+
cmd: Command,
|
|
2537
|
+
) => {
|
|
2538
|
+
const opts = commandOpts<AgentCreateFromTemplateOptions>(cmd);
|
|
2539
|
+
validateAgentCreateFromTemplateInput(template, name, opts);
|
|
2540
|
+
const ctx = await buildContext();
|
|
2541
|
+
if (isJson(cmd)) printJson(await agentsCreateFromTemplateData(ctx, template, name, opts));
|
|
2542
|
+
else console.log(await agentsCreateFromTemplate(ctx, template, name, opts));
|
|
2543
|
+
});
|
|
2544
|
+
agent
|
|
2545
|
+
.command("redeploy [slug]")
|
|
2546
|
+
.description("Re-sync a GitHub-connected agent and rebuild it from deploy-branch HEAD.")
|
|
2547
|
+
.option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
|
|
2548
|
+
.option("-p, --project <slug>", "Alias of --agent")
|
|
2549
|
+
.option("--dir <dir>", "Agent directory (default: current directory)")
|
|
2550
|
+
.action(async (
|
|
2551
|
+
slugArg: string | undefined,
|
|
2552
|
+
_opts: { agent?: string; project?: string; dir?: string },
|
|
2553
|
+
cmd: Command,
|
|
2554
|
+
) => {
|
|
2555
|
+
const opts = commandOpts<{ agent?: string; project?: string; dir?: string }>(cmd);
|
|
2556
|
+
const resolvedOpts = await resolveAgentOptions(opts, slugArg);
|
|
2557
|
+
const ctx = await buildContext();
|
|
2558
|
+
const resolved = resolvedOpts.agent;
|
|
2559
|
+
if (isJson(cmd)) printJson(await agentsRedeployData(ctx, resolved));
|
|
2560
|
+
else console.log(await agentsRedeploy(ctx, resolved));
|
|
2561
|
+
});
|
|
2562
|
+
agent
|
|
2563
|
+
.command("retry <version> [slug]")
|
|
2564
|
+
.description("Retry a failed browser-created release; it republishes as the next version.")
|
|
2565
|
+
.option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
|
|
2566
|
+
.option("-p, --project <slug>", "Alias of --agent")
|
|
2567
|
+
.option("--dir <dir>", "Agent directory (default: current directory)")
|
|
2568
|
+
.action(async (
|
|
2569
|
+
version: string,
|
|
2570
|
+
slugArg: string | undefined,
|
|
2571
|
+
_opts: { agent?: string; project?: string; dir?: string },
|
|
2572
|
+
cmd: Command,
|
|
2573
|
+
) => {
|
|
2574
|
+
const opts = commandOpts<{ agent?: string; project?: string; dir?: string }>(cmd);
|
|
2575
|
+
parseVersion(version);
|
|
2576
|
+
const resolvedOpts = await resolveAgentOptions(opts, slugArg);
|
|
2577
|
+
const ctx = await buildContext();
|
|
2578
|
+
const resolved = resolvedOpts.agent;
|
|
2579
|
+
if (isJson(cmd)) printJson(await agentsRetryReleaseData(ctx, resolved, version));
|
|
2580
|
+
else console.log(await agentsRetryRelease(ctx, resolved, version));
|
|
2581
|
+
});
|
|
1899
2582
|
agent
|
|
1900
2583
|
.command("delete [slug]")
|
|
1901
2584
|
.description("Archive an agent.")
|
|
@@ -1943,7 +2626,7 @@ export function buildProgram(): Command {
|
|
|
1943
2626
|
// owner of target/config resolution and can narrate legacy inference.
|
|
1944
2627
|
const mode = rawMode ?? (await inferMode(dir));
|
|
1945
2628
|
if (opts.reconcileSource && mode !== "git") {
|
|
1946
|
-
throw new
|
|
2629
|
+
throw new CliUsageError(
|
|
1947
2630
|
"--reconcile-source is only valid for git deploys. " +
|
|
1948
2631
|
"Remove it, or use `--mode git` to reconcile Dharta's hosted source branch.",
|
|
1949
2632
|
);
|
|
@@ -1951,7 +2634,7 @@ export function buildProgram(): Command {
|
|
|
1951
2634
|
// git mode's output is the live push sideband, not a payload.
|
|
1952
2635
|
const json = mode === "folder" && isJson(cmd);
|
|
1953
2636
|
if (mode === "git" && isJson(cmd)) {
|
|
1954
|
-
throw new
|
|
2637
|
+
throw new CliUsageError(
|
|
1955
2638
|
"--json is only supported for `dharta deploy --mode folder`; git deploy streams git push output. Run `dharta deploy` without --json or pass `--mode folder`.",
|
|
1956
2639
|
);
|
|
1957
2640
|
}
|
|
@@ -1978,7 +2661,7 @@ export function buildProgram(): Command {
|
|
|
1978
2661
|
if (opts.agent !== undefined || opts.project !== undefined) {
|
|
1979
2662
|
const slug = opts.agent ?? opts.project ?? "";
|
|
1980
2663
|
if (slug.trim()) validateAgentSlug(slug, opts.agent !== undefined ? "--agent" : "--project");
|
|
1981
|
-
throw new
|
|
2664
|
+
throw new CliUsageError("`dharta status` is repo-wide. Use `dharta agent show <slug>` for one remote agent, or `dharta status --dir <path>` for a local repo.");
|
|
1982
2665
|
}
|
|
1983
2666
|
validateAgentSelectorOptions(opts);
|
|
1984
2667
|
let ctx: Awaited<ReturnType<typeof buildContext>> | null = null;
|
|
@@ -1986,7 +2669,7 @@ export function buildProgram(): Command {
|
|
|
1986
2669
|
try {
|
|
1987
2670
|
ctx = await buildContext();
|
|
1988
2671
|
} catch (err) {
|
|
1989
|
-
if (!
|
|
2672
|
+
if (!isNotAuthenticatedError(err)) throw err;
|
|
1990
2673
|
authMessage = err instanceof Error ? err.message : String(err);
|
|
1991
2674
|
}
|
|
1992
2675
|
if (isJson(cmd)) printJson(await statusData(ctx, { dir: opts.dir, authMessage }));
|
|
@@ -2062,26 +2745,151 @@ export function buildProgram(): Command {
|
|
|
2062
2745
|
// commands and help-only command groups keep a hidden parser option
|
|
2063
2746
|
// because they reject --json.
|
|
2064
2747
|
for (const cmd of allCommands(program)) {
|
|
2748
|
+
const jq = new Option("--jq <expr>", "Filter the JSON result through jq (implies --json)");
|
|
2065
2749
|
if (hidesJsonHelp(cmd)) {
|
|
2066
2750
|
cmd.addOption(new Option("--json", "Print the result as JSON (machine-readable)").hideHelp());
|
|
2751
|
+
cmd.addOption(jq.hideHelp());
|
|
2067
2752
|
} else {
|
|
2068
2753
|
cmd.option("--json", jsonHelpDescription(cmd));
|
|
2754
|
+
cmd.addOption(jq);
|
|
2069
2755
|
}
|
|
2756
|
+
// --profile rides the same mechanic: registered on every node so the flag
|
|
2757
|
+
// parses wherever it appears on the line, read back through
|
|
2758
|
+
// optsWithGlobals(). Hidden on the pure command groups, which only print
|
|
2759
|
+
// help and never resolve a credential.
|
|
2760
|
+
const profile = new Option(
|
|
2761
|
+
"--profile <name>",
|
|
2762
|
+
"Profile in ~/.dharta/config.toml to use (env: DHARTA_PROFILE)",
|
|
2763
|
+
);
|
|
2764
|
+
if (HELP_ONLY_COMMAND_PATHS.has(commandPath(cmd)) && cmd.parent !== null) profile.hideHelp();
|
|
2765
|
+
cmd.addOption(profile);
|
|
2766
|
+
// --idempotency-key rides the same mechanic for the same reason: it has to
|
|
2767
|
+
// parse wherever it sits on the line, and one flag has to reach every
|
|
2768
|
+
// request the run makes. Hidden on the help-only groups, which issue none.
|
|
2769
|
+
const idempotencyKey = new Option(
|
|
2770
|
+
"--idempotency-key <key>",
|
|
2771
|
+
"Send this Idempotency-Key on every request the command makes",
|
|
2772
|
+
);
|
|
2773
|
+
if (HELP_ONLY_COMMAND_PATHS.has(commandPath(cmd)) && cmd.parent !== null) idempotencyKey.hideHelp();
|
|
2774
|
+
cmd.addOption(idempotencyKey);
|
|
2070
2775
|
}
|
|
2776
|
+
|
|
2777
|
+
// One place decides the active profile for the whole run. preAction hooks are
|
|
2778
|
+
// inherited by every subcommand, so this fires exactly once, before the
|
|
2779
|
+
// action, with the flag resolved from wherever it sat on the command line.
|
|
2780
|
+
program.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
2781
|
+
const opts = actionCommand.optsWithGlobals();
|
|
2782
|
+
setRequestedProfile(opts.profile as string | undefined);
|
|
2783
|
+
rejectNonMutatingIdempotencyKey(actionCommand, opts.idempotencyKey as string | undefined);
|
|
2784
|
+
setRequestedIdempotencyKey(opts.idempotencyKey as string | undefined);
|
|
2785
|
+
resetJsonPayload();
|
|
2786
|
+
// Compile the filter before the action runs, so a typo costs nothing.
|
|
2787
|
+
await armJqFilter(opts.jq as string | undefined);
|
|
2788
|
+
});
|
|
2789
|
+
|
|
2790
|
+
// The filtered payload is written after the action resolves. postAction runs
|
|
2791
|
+
// for every entry point (parseAsync from the bin, or straight from a test),
|
|
2792
|
+
// and does not run when the action threw - which is right, because a failed
|
|
2793
|
+
// command has no payload to filter.
|
|
2794
|
+
program.hook("postAction", async () => {
|
|
2795
|
+
await flushJsonPayload();
|
|
2796
|
+
});
|
|
2797
|
+
assignTopLevelHelpGroups(program);
|
|
2071
2798
|
installMissingArgumentChoiceHints(program);
|
|
2072
2799
|
installTopLevelUnknownCommandGuard(program);
|
|
2800
|
+
installExitOverride(program);
|
|
2073
2801
|
|
|
2074
2802
|
return program;
|
|
2075
2803
|
}
|
|
2076
2804
|
|
|
2077
|
-
|
|
2078
|
-
|
|
2805
|
+
// The root help used to carry a hand-written, hand-ordered list of commands in
|
|
2806
|
+
// addHelpText, with commander's own list suppressed - two surfaces to keep in
|
|
2807
|
+
// step, and the list drifted (`files` and `artifact` were never in it).
|
|
2808
|
+
// Commander 15 groups natively, so the groups are declared here, on the tree,
|
|
2809
|
+
// and both `dharta --help` and the generated docs reference read them from it.
|
|
2810
|
+
export const TOP_LEVEL_HELP_GROUPS = [
|
|
2811
|
+
["Setup and auth", ["login", "auth", "whoami", "create", "setup"]],
|
|
2812
|
+
["Local and deploy", ["dev", "deploy", "status", "rollback", "logs"]],
|
|
2813
|
+
["Runtime operations", ["open", "sessions", "session", "ps", "send", "files", "artifact", "schedules"]],
|
|
2814
|
+
["Agent configuration", ["agent", "env", "appearance", "genui"]],
|
|
2815
|
+
[
|
|
2816
|
+
"Management",
|
|
2817
|
+
[
|
|
2818
|
+
"usage",
|
|
2819
|
+
"analytics",
|
|
2820
|
+
"directories",
|
|
2821
|
+
"fleet",
|
|
2822
|
+
"kartas",
|
|
2823
|
+
"caps",
|
|
2824
|
+
"embed-keys",
|
|
2825
|
+
"webhooks",
|
|
2826
|
+
"audit-log",
|
|
2827
|
+
"keys",
|
|
2828
|
+
"models",
|
|
2829
|
+
"org",
|
|
2830
|
+
"members",
|
|
2831
|
+
"repos",
|
|
2832
|
+
"api",
|
|
2833
|
+
],
|
|
2834
|
+
],
|
|
2835
|
+
["Utilities", ["doctor", "update", "completion"]],
|
|
2836
|
+
] as const satisfies ReadonlyArray<readonly [string, readonly string[]]>;
|
|
2837
|
+
|
|
2838
|
+
function assignTopLevelHelpGroups(program: Command): void {
|
|
2839
|
+
const order = new Map<string, number>();
|
|
2840
|
+
for (const [group, names] of TOP_LEVEL_HELP_GROUPS) {
|
|
2841
|
+
for (const name of names) {
|
|
2842
|
+
const cmd = program.commands.find((candidate) => candidate.name() === name);
|
|
2843
|
+
if (!cmd) {
|
|
2844
|
+
throw new Error(`Help group '${group}' names '${name}', which is not a top-level command.`);
|
|
2845
|
+
}
|
|
2846
|
+
cmd.helpGroup(`${group}:`);
|
|
2847
|
+
order.set(name, order.size);
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
// Commander discovers group headings in registration order, so the array
|
|
2851
|
+
// itself has to be in group order for the help to read top to bottom.
|
|
2852
|
+
// Ungrouped (hidden) commands keep their place at the end. The array is
|
|
2853
|
+
// typed readonly but is the live list commander renders from, so sorting it
|
|
2854
|
+
// is the ordering hook - there is no public setter.
|
|
2855
|
+
(program.commands as Command[]).sort(
|
|
2856
|
+
(a, b) => (order.get(a.name()) ?? Number.MAX_SAFE_INTEGER) - (order.get(b.name()) ?? Number.MAX_SAFE_INTEGER),
|
|
2857
|
+
);
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
// Commander calls process.exit(1) itself for a bad flag, which flattens every
|
|
2861
|
+
// usage mistake into the same code as a failed API call. exitOverride makes it
|
|
2862
|
+
// throw instead, so the single catch in the bin block maps it to EXIT_USAGE.
|
|
2863
|
+
// The setting is per-command, not inherited, so every node gets it.
|
|
2864
|
+
function installExitOverride(program: Command): void {
|
|
2865
|
+
for (const cmd of allCommands(program)) cmd.exitOverride();
|
|
2079
2866
|
}
|
|
2080
2867
|
|
|
2081
2868
|
export async function main(argv: string[]): Promise<void> {
|
|
2082
2869
|
await buildProgram().parseAsync(argv);
|
|
2083
2870
|
}
|
|
2084
2871
|
|
|
2872
|
+
// The single place a failure turns into an exit status. Returns the code so
|
|
2873
|
+
// the bin block can `process.exit` with it; commander has already printed its
|
|
2874
|
+
// own message, so only CLI-raised errors are narrated here.
|
|
2875
|
+
export function reportCliFailure(err: unknown, write: (line: string) => void = (line) => process.stderr.write(line)): number {
|
|
2876
|
+
const code = exitCodeForError(err);
|
|
2877
|
+
if (!isCommanderError(err)) {
|
|
2878
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2879
|
+
write(`dharta: ${msg}${retryAdvice(err)}\n`);
|
|
2880
|
+
}
|
|
2881
|
+
return code;
|
|
2882
|
+
}
|
|
2883
|
+
|
|
2884
|
+
// A rate-limited response carries how long to wait in `Retry-After`, and the
|
|
2885
|
+
// server's own message never repeats it. Without this the user is told they
|
|
2886
|
+
// were throttled and left to guess for how long.
|
|
2887
|
+
function retryAdvice(err: unknown): string {
|
|
2888
|
+
const seconds = (err as { retryAfterSeconds?: unknown })?.retryAfterSeconds;
|
|
2889
|
+
if (typeof seconds !== "number" || !Number.isFinite(seconds) || seconds < 0) return "";
|
|
2890
|
+
return ` Retry after ${seconds} seconds.`;
|
|
2891
|
+
}
|
|
2892
|
+
|
|
2085
2893
|
function allCommands(root: Command): Command[] {
|
|
2086
2894
|
const out: Command[] = [];
|
|
2087
2895
|
const visit = (cmd: Command): void => {
|
|
@@ -2098,8 +2906,11 @@ function hidesJsonHelp(cmd: Command): boolean {
|
|
|
2098
2906
|
"dharta auth",
|
|
2099
2907
|
"dharta analytics",
|
|
2100
2908
|
"dharta keys",
|
|
2101
|
-
"dharta
|
|
2909
|
+
"dharta models",
|
|
2910
|
+
"dharta models auth",
|
|
2911
|
+
"dharta org",
|
|
2102
2912
|
"dharta members",
|
|
2913
|
+
"dharta repos",
|
|
2103
2914
|
"dharta embed-keys",
|
|
2104
2915
|
"dharta caps",
|
|
2105
2916
|
"dharta appearance",
|
|
@@ -2108,6 +2919,7 @@ function hidesJsonHelp(cmd: Command): boolean {
|
|
|
2108
2919
|
"dharta webhooks",
|
|
2109
2920
|
"dharta env",
|
|
2110
2921
|
"dharta agent",
|
|
2922
|
+
"dharta genui",
|
|
2111
2923
|
"dharta git-credential",
|
|
2112
2924
|
"dharta dev",
|
|
2113
2925
|
"dharta logs",
|
|
@@ -2141,8 +2953,11 @@ const HELP_ONLY_COMMAND_PATHS = new Set([
|
|
|
2141
2953
|
"dharta auth",
|
|
2142
2954
|
"dharta analytics",
|
|
2143
2955
|
"dharta keys",
|
|
2144
|
-
"dharta
|
|
2956
|
+
"dharta models",
|
|
2957
|
+
"dharta models auth",
|
|
2958
|
+
"dharta org",
|
|
2145
2959
|
"dharta members",
|
|
2960
|
+
"dharta repos",
|
|
2146
2961
|
"dharta embed-keys",
|
|
2147
2962
|
"dharta caps",
|
|
2148
2963
|
"dharta caps budget",
|
|
@@ -2152,6 +2967,7 @@ const HELP_ONLY_COMMAND_PATHS = new Set([
|
|
|
2152
2967
|
"dharta webhooks",
|
|
2153
2968
|
"dharta env",
|
|
2154
2969
|
"dharta agent",
|
|
2970
|
+
"dharta genui",
|
|
2155
2971
|
]);
|
|
2156
2972
|
|
|
2157
2973
|
function rejectUnknownTopLevelCommand(
|
|
@@ -2173,8 +2989,9 @@ function rejectUnknownTopLevelCommand(
|
|
|
2173
2989
|
const path = commandPath(cursor);
|
|
2174
2990
|
if (!HELP_ONLY_COMMAND_PATHS.has(path)) return;
|
|
2175
2991
|
|
|
2176
|
-
const
|
|
2177
|
-
|
|
2992
|
+
const child = visibleSubcommands(cursor).find(
|
|
2993
|
+
(cmd) => cmd.name() === token || cmd.aliases().includes(token),
|
|
2994
|
+
);
|
|
2178
2995
|
if (child) {
|
|
2179
2996
|
cursor = child as Command;
|
|
2180
2997
|
continue;
|
|
@@ -2265,7 +3082,7 @@ function parseOnOff(value: string, name: string): boolean {
|
|
|
2265
3082
|
if (["on", "true", "enabled", "enable", "resume", "resumed"].includes(normalized)) return true;
|
|
2266
3083
|
if (["off", "false", "disabled", "disable", "pause", "paused"].includes(normalized)) return false;
|
|
2267
3084
|
const suggestion = closestChoice(normalized, ["on", "off", "enabled", "disabled", "true", "false"]);
|
|
2268
|
-
throw new
|
|
3085
|
+
throw new CliUsageError(
|
|
2269
3086
|
`${name} must be on/off, enabled/disabled, or true/false.` +
|
|
2270
3087
|
(suggestion ? ` Did you mean ${suggestion}?` : ""),
|
|
2271
3088
|
);
|
|
@@ -2275,7 +3092,7 @@ export async function readStdin(): Promise<string> {
|
|
|
2275
3092
|
if (process.stdin.isTTY) {
|
|
2276
3093
|
// Reading a TTY would block silently until Ctrl-D — fail with the
|
|
2277
3094
|
// usage instead (gh parity: --with-token requires piped input).
|
|
2278
|
-
throw new
|
|
3095
|
+
throw new CliUsageError(
|
|
2279
3096
|
"No piped input. Usage: echo $DHARTA_API_KEY | dharta login --with-token",
|
|
2280
3097
|
);
|
|
2281
3098
|
}
|
|
@@ -2283,7 +3100,7 @@ export async function readStdin(): Promise<string> {
|
|
|
2283
3100
|
for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
2284
3101
|
const token = Buffer.concat(chunks).toString("utf8").trim();
|
|
2285
3102
|
if (!token) {
|
|
2286
|
-
throw new
|
|
3103
|
+
throw new CliUsageError("No token on stdin. Usage: echo $DHARTA_API_KEY | dharta login --with-token");
|
|
2287
3104
|
}
|
|
2288
3105
|
return token;
|
|
2289
3106
|
}
|
|
@@ -2293,26 +3110,26 @@ function parseVersion(input: string): number {
|
|
|
2293
3110
|
try {
|
|
2294
3111
|
return parseReleaseVersion(input, "version");
|
|
2295
3112
|
} catch {
|
|
2296
|
-
throw new
|
|
3113
|
+
throw new CliUsageError(`Invalid version: ${input.trim()}. Use e.g. v3 or 3.`);
|
|
2297
3114
|
}
|
|
2298
3115
|
}
|
|
2299
3116
|
|
|
2300
3117
|
function parseRollbackTarget(input: string | undefined): number {
|
|
2301
3118
|
if (input === undefined) {
|
|
2302
|
-
throw new
|
|
3119
|
+
throw new CliUsageError("Pass --to <version>. Usage: dharta rollback [slug] --to v3");
|
|
2303
3120
|
}
|
|
2304
3121
|
return parseVersion(input);
|
|
2305
3122
|
}
|
|
2306
3123
|
|
|
2307
3124
|
function parsePort(input: string): number {
|
|
2308
3125
|
const normalized = input.trim();
|
|
2309
|
-
if (!normalized) throw new
|
|
3126
|
+
if (!normalized) throw new CliUsageError("--port cannot be blank.");
|
|
2310
3127
|
if (!/^[1-9]\d*$/.test(normalized)) {
|
|
2311
|
-
throw new
|
|
3128
|
+
throw new CliUsageError(`--port must be an integer between 1 and 65535.`);
|
|
2312
3129
|
}
|
|
2313
3130
|
const port = Number(normalized);
|
|
2314
3131
|
if (port > 65535) {
|
|
2315
|
-
throw new
|
|
3132
|
+
throw new CliUsageError(`--port must be an integer between 1 and 65535.`);
|
|
2316
3133
|
}
|
|
2317
3134
|
return port;
|
|
2318
3135
|
}
|
|
@@ -2332,8 +3149,6 @@ const invokedAsBin = (() => {
|
|
|
2332
3149
|
})();
|
|
2333
3150
|
if (invokedAsBin) {
|
|
2334
3151
|
main(process.argv).catch((err) => {
|
|
2335
|
-
|
|
2336
|
-
process.stderr.write(`dharta: ${msg}\n`);
|
|
2337
|
-
process.exit(1);
|
|
3152
|
+
process.exit(reportCliFailure(err));
|
|
2338
3153
|
});
|
|
2339
3154
|
}
|