@dharta/cli 0.6.77

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.
Files changed (259) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +167 -0
  3. package/dist/agent.d.ts +17 -0
  4. package/dist/agent.js +134 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/api-error.d.ts +3 -0
  7. package/dist/api-error.js +80 -0
  8. package/dist/api-error.js.map +1 -0
  9. package/dist/browser.d.ts +1 -0
  10. package/dist/browser.js +19 -0
  11. package/dist/browser.js.map +1 -0
  12. package/dist/cli.d.ts +5 -0
  13. package/dist/cli.js +1978 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/commands/agents.d.ts +131 -0
  16. package/dist/commands/agents.js +439 -0
  17. package/dist/commands/agents.js.map +1 -0
  18. package/dist/commands/analytics.d.ts +25 -0
  19. package/dist/commands/analytics.js +166 -0
  20. package/dist/commands/analytics.js.map +1 -0
  21. package/dist/commands/api.d.ts +19 -0
  22. package/dist/commands/api.js +278 -0
  23. package/dist/commands/api.js.map +1 -0
  24. package/dist/commands/appearance.d.ts +19 -0
  25. package/dist/commands/appearance.js +63 -0
  26. package/dist/commands/appearance.js.map +1 -0
  27. package/dist/commands/artifact.d.ts +33 -0
  28. package/dist/commands/artifact.js +235 -0
  29. package/dist/commands/artifact.js.map +1 -0
  30. package/dist/commands/audit-log.d.ts +32 -0
  31. package/dist/commands/audit-log.js +102 -0
  32. package/dist/commands/audit-log.js.map +1 -0
  33. package/dist/commands/auth.d.ts +47 -0
  34. package/dist/commands/auth.js +117 -0
  35. package/dist/commands/auth.js.map +1 -0
  36. package/dist/commands/caps.d.ts +82 -0
  37. package/dist/commands/caps.js +213 -0
  38. package/dist/commands/caps.js.map +1 -0
  39. package/dist/commands/create.d.ts +22 -0
  40. package/dist/commands/create.js +76 -0
  41. package/dist/commands/create.js.map +1 -0
  42. package/dist/commands/deploy.d.ts +45 -0
  43. package/dist/commands/deploy.js +892 -0
  44. package/dist/commands/deploy.js.map +1 -0
  45. package/dist/commands/dev.d.ts +33 -0
  46. package/dist/commands/dev.js +655 -0
  47. package/dist/commands/dev.js.map +1 -0
  48. package/dist/commands/directories.d.ts +60 -0
  49. package/dist/commands/directories.js +83 -0
  50. package/dist/commands/directories.js.map +1 -0
  51. package/dist/commands/doctor.d.ts +13 -0
  52. package/dist/commands/doctor.js +123 -0
  53. package/dist/commands/doctor.js.map +1 -0
  54. package/dist/commands/embed-keys.d.ts +61 -0
  55. package/dist/commands/embed-keys.js +165 -0
  56. package/dist/commands/embed-keys.js.map +1 -0
  57. package/dist/commands/env.d.ts +59 -0
  58. package/dist/commands/env.js +300 -0
  59. package/dist/commands/env.js.map +1 -0
  60. package/dist/commands/files.d.ts +14 -0
  61. package/dist/commands/files.js +84 -0
  62. package/dist/commands/files.js.map +1 -0
  63. package/dist/commands/git-credential.d.ts +1 -0
  64. package/dist/commands/git-credential.js +70 -0
  65. package/dist/commands/git-credential.js.map +1 -0
  66. package/dist/commands/kartas.d.ts +101 -0
  67. package/dist/commands/kartas.js +289 -0
  68. package/dist/commands/kartas.js.map +1 -0
  69. package/dist/commands/keys.d.ts +33 -0
  70. package/dist/commands/keys.js +141 -0
  71. package/dist/commands/keys.js.map +1 -0
  72. package/dist/commands/login.d.ts +22 -0
  73. package/dist/commands/login.js +78 -0
  74. package/dist/commands/login.js.map +1 -0
  75. package/dist/commands/logs.d.ts +14 -0
  76. package/dist/commands/logs.js +89 -0
  77. package/dist/commands/logs.js.map +1 -0
  78. package/dist/commands/members.d.ts +32 -0
  79. package/dist/commands/members.js +77 -0
  80. package/dist/commands/members.js.map +1 -0
  81. package/dist/commands/model-keys.d.ts +22 -0
  82. package/dist/commands/model-keys.js +222 -0
  83. package/dist/commands/model-keys.js.map +1 -0
  84. package/dist/commands/open.d.ts +29 -0
  85. package/dist/commands/open.js +119 -0
  86. package/dist/commands/open.js.map +1 -0
  87. package/dist/commands/parity.d.ts +15 -0
  88. package/dist/commands/parity.js +73 -0
  89. package/dist/commands/parity.js.map +1 -0
  90. package/dist/commands/rollback.d.ts +21 -0
  91. package/dist/commands/rollback.js +26 -0
  92. package/dist/commands/rollback.js.map +1 -0
  93. package/dist/commands/schedules.d.ts +52 -0
  94. package/dist/commands/schedules.js +231 -0
  95. package/dist/commands/schedules.js.map +1 -0
  96. package/dist/commands/scopes.d.ts +3 -0
  97. package/dist/commands/scopes.js +21 -0
  98. package/dist/commands/scopes.js.map +1 -0
  99. package/dist/commands/sessions.d.ts +53 -0
  100. package/dist/commands/sessions.js +142 -0
  101. package/dist/commands/sessions.js.map +1 -0
  102. package/dist/commands/setup.d.ts +28 -0
  103. package/dist/commands/setup.js +949 -0
  104. package/dist/commands/setup.js.map +1 -0
  105. package/dist/commands/status.d.ts +19 -0
  106. package/dist/commands/status.js +63 -0
  107. package/dist/commands/status.js.map +1 -0
  108. package/dist/commands/update.d.ts +14 -0
  109. package/dist/commands/update.js +120 -0
  110. package/dist/commands/update.js.map +1 -0
  111. package/dist/commands/usage.d.ts +3 -0
  112. package/dist/commands/usage.js +37 -0
  113. package/dist/commands/usage.js.map +1 -0
  114. package/dist/commands/webhooks.d.ts +47 -0
  115. package/dist/commands/webhooks.js +156 -0
  116. package/dist/commands/webhooks.js.map +1 -0
  117. package/dist/commands/whoami.d.ts +7 -0
  118. package/dist/commands/whoami.js +17 -0
  119. package/dist/commands/whoami.js.map +1 -0
  120. package/dist/completions.d.ts +13 -0
  121. package/dist/completions.js +120 -0
  122. package/dist/completions.js.map +1 -0
  123. package/dist/config.d.ts +20 -0
  124. package/dist/config.js +98 -0
  125. package/dist/config.js.map +1 -0
  126. package/dist/context.d.ts +14 -0
  127. package/dist/context.js +38 -0
  128. package/dist/context.js.map +1 -0
  129. package/dist/device-flow.d.ts +9 -0
  130. package/dist/device-flow.js +127 -0
  131. package/dist/device-flow.js.map +1 -0
  132. package/dist/harness-preflight.d.ts +24 -0
  133. package/dist/harness-preflight.js +252 -0
  134. package/dist/harness-preflight.js.map +1 -0
  135. package/dist/harnesses.d.ts +11 -0
  136. package/dist/harnesses.js +112 -0
  137. package/dist/harnesses.js.map +1 -0
  138. package/dist/index.d.ts +3 -0
  139. package/dist/index.js +3 -0
  140. package/dist/index.js.map +1 -0
  141. package/dist/login-offer.d.ts +9 -0
  142. package/dist/login-offer.js +36 -0
  143. package/dist/login-offer.js.map +1 -0
  144. package/dist/manifest.d.ts +15 -0
  145. package/dist/manifest.js +142 -0
  146. package/dist/manifest.js.map +1 -0
  147. package/dist/money.d.ts +1 -0
  148. package/dist/money.js +16 -0
  149. package/dist/money.js.map +1 -0
  150. package/dist/output.d.ts +2 -0
  151. package/dist/output.js +20 -0
  152. package/dist/output.js.map +1 -0
  153. package/dist/runtime.d.ts +19 -0
  154. package/dist/runtime.js +95 -0
  155. package/dist/runtime.js.map +1 -0
  156. package/dist/scaffold.d.ts +16 -0
  157. package/dist/scaffold.js +74 -0
  158. package/dist/scaffold.js.map +1 -0
  159. package/dist/setup-scan.d.ts +21 -0
  160. package/dist/setup-scan.js +127 -0
  161. package/dist/setup-scan.js.map +1 -0
  162. package/dist/templates.d.ts +14 -0
  163. package/dist/templates.js +65 -0
  164. package/dist/templates.js.map +1 -0
  165. package/dist/validators.d.ts +10 -0
  166. package/dist/validators.js +116 -0
  167. package/dist/validators.js.map +1 -0
  168. package/dist/version-check.d.ts +25 -0
  169. package/dist/version-check.js +68 -0
  170. package/dist/version-check.js.map +1 -0
  171. package/node_modules/@dharta/sdk/LICENSE +21 -0
  172. package/node_modules/@dharta/sdk/README.md +87 -0
  173. package/node_modules/@dharta/sdk/dist/errors.d.ts +20 -0
  174. package/node_modules/@dharta/sdk/dist/errors.d.ts.map +1 -0
  175. package/node_modules/@dharta/sdk/dist/errors.js +51 -0
  176. package/node_modules/@dharta/sdk/dist/errors.js.map +1 -0
  177. package/node_modules/@dharta/sdk/dist/http.d.ts +26 -0
  178. package/node_modules/@dharta/sdk/dist/http.d.ts.map +1 -0
  179. package/node_modules/@dharta/sdk/dist/http.js +136 -0
  180. package/node_modules/@dharta/sdk/dist/http.js.map +1 -0
  181. package/node_modules/@dharta/sdk/dist/index.d.ts +15 -0
  182. package/node_modules/@dharta/sdk/dist/index.d.ts.map +1 -0
  183. package/node_modules/@dharta/sdk/dist/index.js +21 -0
  184. package/node_modules/@dharta/sdk/dist/index.js.map +1 -0
  185. package/node_modules/@dharta/sdk/dist/resources.d.ts +48 -0
  186. package/node_modules/@dharta/sdk/dist/resources.d.ts.map +1 -0
  187. package/node_modules/@dharta/sdk/dist/resources.js +150 -0
  188. package/node_modules/@dharta/sdk/dist/resources.js.map +1 -0
  189. package/node_modules/@dharta/sdk/dist/types.d.ts +94 -0
  190. package/node_modules/@dharta/sdk/dist/types.d.ts.map +1 -0
  191. package/node_modules/@dharta/sdk/dist/types.js +59 -0
  192. package/node_modules/@dharta/sdk/dist/types.js.map +1 -0
  193. package/node_modules/@dharta/sdk/package.json +32 -0
  194. package/node_modules/@dharta/sdk/src/errors.ts +58 -0
  195. package/node_modules/@dharta/sdk/src/http.ts +166 -0
  196. package/node_modules/@dharta/sdk/src/index.ts +36 -0
  197. package/node_modules/@dharta/sdk/src/resources.ts +197 -0
  198. package/node_modules/@dharta/sdk/src/types.ts +179 -0
  199. package/package.json +38 -0
  200. package/src/agent.ts +147 -0
  201. package/src/api-error.ts +88 -0
  202. package/src/browser.ts +20 -0
  203. package/src/cli.ts +2339 -0
  204. package/src/commands/agents.ts +635 -0
  205. package/src/commands/analytics.ts +181 -0
  206. package/src/commands/api.ts +297 -0
  207. package/src/commands/appearance.ts +79 -0
  208. package/src/commands/artifact.ts +331 -0
  209. package/src/commands/audit-log.ts +125 -0
  210. package/src/commands/auth.ts +186 -0
  211. package/src/commands/caps.ts +302 -0
  212. package/src/commands/create.ts +114 -0
  213. package/src/commands/deploy.ts +1122 -0
  214. package/src/commands/dev.ts +792 -0
  215. package/src/commands/directories.ts +136 -0
  216. package/src/commands/doctor.ts +138 -0
  217. package/src/commands/embed-keys.ts +237 -0
  218. package/src/commands/env.ts +369 -0
  219. package/src/commands/files.ts +124 -0
  220. package/src/commands/git-credential.ts +78 -0
  221. package/src/commands/kartas.ts +439 -0
  222. package/src/commands/keys.ts +206 -0
  223. package/src/commands/login.ts +115 -0
  224. package/src/commands/logs.ts +111 -0
  225. package/src/commands/members.ts +109 -0
  226. package/src/commands/model-keys.ts +278 -0
  227. package/src/commands/open.ts +173 -0
  228. package/src/commands/parity.ts +96 -0
  229. package/src/commands/rollback.ts +45 -0
  230. package/src/commands/schedules.ts +290 -0
  231. package/src/commands/scopes.ts +22 -0
  232. package/src/commands/sessions.ts +225 -0
  233. package/src/commands/setup.ts +1176 -0
  234. package/src/commands/status.ts +94 -0
  235. package/src/commands/update.ts +157 -0
  236. package/src/commands/usage.ts +41 -0
  237. package/src/commands/webhooks.ts +200 -0
  238. package/src/commands/whoami.ts +24 -0
  239. package/src/completions.ts +168 -0
  240. package/src/config.ts +143 -0
  241. package/src/context.ts +59 -0
  242. package/src/device-flow.ts +174 -0
  243. package/src/harness-preflight.ts +293 -0
  244. package/src/harnesses.ts +117 -0
  245. package/src/index.ts +3 -0
  246. package/src/login-offer.ts +49 -0
  247. package/src/manifest.ts +153 -0
  248. package/src/money.ts +15 -0
  249. package/src/output.ts +22 -0
  250. package/src/runtime.ts +127 -0
  251. package/src/scaffold.ts +102 -0
  252. package/src/setup-scan.ts +154 -0
  253. package/src/templates.ts +85 -0
  254. package/src/validators.ts +124 -0
  255. package/src/version-check.ts +75 -0
  256. package/templates/blank/CLAUDE.md +21 -0
  257. package/templates/blank/dharta.toml +11 -0
  258. package/templates/faq-agent/CLAUDE.md +51 -0
  259. package/templates/faq-agent/dharta.toml +11 -0
package/dist/cli.js ADDED
@@ -0,0 +1,1978 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync, realpathSync } from "node:fs";
3
+ import { fileURLToPath } from "node:url";
4
+ import * as readline from "node:readline";
5
+ import { Argument, Command, InvalidArgumentError, Option } from "commander";
6
+ import { API_KEY_SCOPES } from "@dharta/sdk";
7
+ import { buildContext } from "./context.js";
8
+ import { resolveBaseUrl } from "./config.js";
9
+ import { inferAgentSlug, validateAgentSelectorOptions, validateAgentSlug } from "./agent.js";
10
+ import { login } from "./commands/login.js";
11
+ import { authLogout, authLogoutData, authStatus, authStatusData, authToken, } from "./commands/auth.js";
12
+ import { whoami, whoamiData } from "./commands/whoami.js";
13
+ import { api as apiCmd, formatApiResult, prepareApiOptions, validateApiRequest } from "./commands/api.js";
14
+ import { auditLog, auditLogData, validateAuditLogInput } from "./commands/audit-log.js";
15
+ import { COST_ANALYTICS_GROUPS, COST_ANALYTICS_RANGES, ENTITY_ANALYTICS_RANGES, analytics, analyticsData, normalizeAnalyticsOptions, } from "./commands/analytics.js";
16
+ import { keysCreate, keysCreateData, keysList, keysListData, keysRename, keysRenameData, keysRevoke, keysRevokeData, validateKeysCreateInput, } from "./commands/keys.js";
17
+ import { modelKeysCreate, modelKeysCreateData, modelKeysList, modelKeysListData, modelKeysRevoke, modelKeysRevokeData, prepareModelKeysCreateOptions, MODEL_API_FORMATS, validateModelKeyId, validateModelKeysCreateOptions, } from "./commands/model-keys.js";
18
+ import { membersInvite, membersInviteData, membersList, membersListData, membersRemove, membersRemoveData, membersSetRole, membersSetRoleData, validateMembersInviteInput, } from "./commands/members.js";
19
+ import { kartaMembersGrant, kartaMembersGrantData, kartaMembersRevoke, kartaMembersRevokeData, kartaMembersSetRole, kartaMembersSetRoleData, kartasArchive, kartasArchiveData, kartasConfigure, kartasConfigureData, kartasRestore, kartasRestoreData, kartasCreate, kartasCreateData, kartasList, kartasListData, kartasName, kartasNameData, kartasShow, kartasShowData, kartaEnvList, kartaEnvListData, kartaEnvSet, kartaEnvSetData, kartaEnvUnset, kartaEnvUnsetData, validateKartaCreateOptions, validateKartaConfigureOptions, validateKartaNameOptions, } from "./commands/kartas.js";
20
+ import { usage as usageCmd, usageData } from "./commands/usage.js";
21
+ import { agentsActivateRelease, agentsActivateReleaseData, agentsDashboardEditsSet, agentsDashboardEditsSetData, agentsDelete, agentsDeleteData, agentsList, agentsListData, agentsModelSet, agentsModelSetData, agentsReleases, agentsReleasesData, agentsShow, agentsShowData, agentsServingSet, agentsServingSetData, agentsWorkspaceInspectableSet, agentsWorkspaceInspectableSetData, agentsWorkspaceAccessShow, agentsWorkspaceAccessShowData, agentsWorkspaceAccessSet, agentsWorkspaceAccessSetData, parseWorkspaceAccessAssignments, validateAgentModelSetOptions, } from "./commands/agents.js";
22
+ import { embedKeysClearIdentity, embedKeysClearIdentityData, embedKeysCreate, embedKeysCreateData, embedKeysList, embedKeysListData, embedKeysRevoke, embedKeysRevokeData, embedKeysRotate, embedKeysRotateData, embedKeysSetIdentity, embedKeysSetIdentityData, embedKeysToggle, embedKeysToggleData, parseOrigins, validateEmbedKeysCreateInput, } from "./commands/embed-keys.js";
23
+ import { capsEndUser, capsEndUserData, capsEndUserSet, capsEndUserSetData, capsBudget, capsBudgetData, capsBudgetSet, capsBudgetSetData, capsInstance, capsInstanceData, capsInstanceSet, capsInstanceSetData, capsSeat, capsSeatData, capsSeatSet, capsSeatSetData, validateBudgetSetOptions, validateCapSetOptions, } from "./commands/caps.js";
24
+ import { appearanceGet, appearanceGetData, appearanceSet, appearanceSetData, validateAppearanceSetInput, } from "./commands/appearance.js";
25
+ import { schedulesAction, schedulesActionData, schedulesCreate, schedulesCreateData, schedulesDelete, schedulesDeleteData, schedulesList, schedulesListData, validateSchedulesCreateInput, } from "./commands/schedules.js";
26
+ import { directoryEndUsers, directoryEndUsersData, directoryInstances, directoryInstancesData, directorySessions, directorySessionsData, validateDirectoryOptions, } from "./commands/directories.js";
27
+ import { parseEvents, webhooksCreate, webhooksCreateData, webhooksDelete, webhooksDeleteData, webhooksEnable, webhooksEnableData, webhooksList, webhooksListData, webhooksUpdate, webhooksUpdateData, validateWebhooksCreateInput, validateWebhooksUpdateInput, } from "./commands/webhooks.js";
28
+ import { rollback, rollbackData } from "./commands/rollback.js";
29
+ import { deploy, inferMode, validateDeployDir } from "./commands/deploy.js";
30
+ import { status, statusData } from "./commands/status.js";
31
+ import { logsTail, validateLogsOptions } from "./commands/logs.js";
32
+ import { create } from "./commands/create.js";
33
+ import { setup } from "./commands/setup.js";
34
+ import { HARNESS_TYPES } from "./harnesses.js";
35
+ import { offerLogin } from "./login-offer.js";
36
+ import { doctor } from "./commands/doctor.js";
37
+ import { dev as devCmd } from "./commands/dev.js";
38
+ import { registerArtifactCommands } from "./commands/artifact.js";
39
+ import { isHandoffTarget, open as openCmd, openData, openHandoff, openHandoffData, OPEN_TARGETS_HELP, normalizeOpenTarget, validateOpenTarget, } from "./commands/open.js";
40
+ import { envDiff, envDiffData, envList, envListData, envPull, envPullData, envSet, envSetData, envUnset, envUnsetData, validateEnvAssignment, validateEnvDiffOptions, validateEnvName, validateEnvPullOptions, } from "./commands/env.js";
41
+ import { ps as psCmd, psData, run as runCmd, sessionsList, sessionsListData, sessionsShow, sessionsShowData, validateSessionsListOptions, } from "./commands/sessions.js";
42
+ import { update } from "./commands/update.js";
43
+ import { filesLs, filesLsData, filesGet } from "./commands/files.js";
44
+ import { gitCredential } from "./commands/git-credential.js";
45
+ import { bashCompletion, zshCompletion } from "./completions.js";
46
+ import { closestChoice, normalizeHttpUrl, normalizeNonBlank, parsePositiveInteger, parseReleaseVersion } from "./validators.js";
47
+ const MEMBER_ROLES = ["owner", "admin", "developer", "billing", "viewer"];
48
+ const DHARTA_ACCESS_AUDIENCES = ["members", "anonymous", "off"];
49
+ const DHARTA_MEMBER_ROLES = ["viewer", "participant", "manager"];
50
+ const MODEL_KEY_AUTH_TYPES = ["api_key", "oauth_subscription"];
51
+ const AGENT_MODEL_SOURCES = ["inherit", "platform"];
52
+ const CLI_API_KEY_SCOPES = API_KEY_SCOPES;
53
+ const STATE_HELP = "State values: on/off, enabled/disabled, true/false.";
54
+ // --json output (RFC 0013 §3): every command that produces a result
55
+ // payload prints it as JSON when --json is set. Progress narration
56
+ // (device-flow codes, download logs) moves to stderr so stdout stays
57
+ // parseable. Commands whose stdout is a stream, a REPL, a script, or a
58
+ // protocol rather than a result payload (dev, logs, completion,
59
+ // git-credential, and `deploy --mode git` whose output is the live
60
+ // push sideband) reject --json. `deploy --mode folder` returns a
61
+ // single payload, so it supports --json.
62
+ //
63
+ // Mechanics: the flag is registered on every node of the command tree
64
+ // so commander's
65
+ // outermost option scan consumes it wherever it appears on the line -
66
+ // so actions read it through optsWithGlobals() on their Command. Commands
67
+ // that always reject JSON keep a hidden parser option so help/completions
68
+ // do not advertise an invalid mode.
69
+ function isJson(cmd) {
70
+ return Boolean(cmd.optsWithGlobals().json);
71
+ }
72
+ function printJson(payload) {
73
+ console.log(JSON.stringify(payload, null, 2));
74
+ }
75
+ function commandOpts(cmd) {
76
+ return cmd.optsWithGlobals();
77
+ }
78
+ function validatePositionalAgentSelector(slug, opts) {
79
+ if (slug === undefined)
80
+ return;
81
+ validateAgentSlug(slug, "agent slug");
82
+ if (opts.agent !== undefined || opts.project !== undefined) {
83
+ throw new Error("Use either the positional agent slug or --agent/--project, not both.");
84
+ }
85
+ }
86
+ function validateCreateInput(pathArg, opts) {
87
+ if (pathArg !== undefined)
88
+ normalizeNonBlank(pathArg, "path");
89
+ if (opts.name !== undefined)
90
+ normalizeNonBlank(opts.name, "--name");
91
+ if (opts.template !== undefined)
92
+ normalizeNonBlank(opts.template, "--template");
93
+ if (opts.harnessVersion !== undefined)
94
+ normalizeNonBlank(opts.harnessVersion, "--harness-version");
95
+ }
96
+ async function resolveAgentOptions(opts, positionalSlug) {
97
+ validatePositionalAgentSelector(positionalSlug, opts);
98
+ validateAgentSelectorOptions(opts);
99
+ const agent = await inferAgentSlug({
100
+ agent: positionalSlug ?? opts.agent,
101
+ project: positionalSlug === undefined ? opts.project : undefined,
102
+ dir: opts.dir,
103
+ });
104
+ return { ...opts, agent, project: undefined };
105
+ }
106
+ function rejectJson(cmd, name) {
107
+ if (isJson(cmd)) {
108
+ throw new Error(`--json is not supported for \`${name}\` - its output is not a single payload. Run it without --json.`);
109
+ }
110
+ }
111
+ function helpOnlyAction(name) {
112
+ return (...args) => {
113
+ const cmd = args[args.length - 1];
114
+ const extra = cmd.args.filter((arg) => typeof arg === "string");
115
+ if (extra.length > 0) {
116
+ const unknown = extra.join(" ");
117
+ const path = commandPath(cmd);
118
+ const suggestion = closestCommand(extra[0], commandSuggestionTokens(cmd));
119
+ cmd.error(`error: unknown command '${unknown}'${path === "dharta" ? "" : ` for ${path}`}. ` +
120
+ `Run \`${path} --help\` for available commands.` +
121
+ (suggestion ? ` Did you mean \`${suggestion}\`?` : ""));
122
+ }
123
+ rejectJson(cmd, name);
124
+ cmd.outputHelp();
125
+ };
126
+ }
127
+ const logToStderr = (line) => process.stderr.write(line + "\n");
128
+ function commandPath(cmd) {
129
+ const names = [];
130
+ let cursor = cmd;
131
+ while (cursor) {
132
+ names.unshift(cursor.name());
133
+ cursor = cursor.parent;
134
+ }
135
+ return names.join(" ");
136
+ }
137
+ function closestCommand(input, commands) {
138
+ return closestChoice(input, commands);
139
+ }
140
+ function commandSuggestionTokens(cmd) {
141
+ return Array.from(new Set(cmd.commands
142
+ .filter((child) => !child._hidden)
143
+ .flatMap((child) => [child.name(), ...child.aliases()])));
144
+ }
145
+ export function buildProgram() {
146
+ const program = new Command();
147
+ // Commander 12 has no native help groups, so render the top-level command
148
+ // list grouped (addHelpText below) and suppress commander's default flat
149
+ // list on the ROOT only - subcommand help (e.g. `dharta agent --help`) keeps
150
+ // commander's default rendering. `defaultHelp` is captured before the
151
+ // override so it supplies the unmodified list for subcommands.
152
+ const defaultHelp = program.createHelp();
153
+ program
154
+ .name("dharta")
155
+ .configureHelp({
156
+ visibleCommands: (cmd) => (cmd.parent === null ? [] : defaultHelp.visibleCommands(cmd)),
157
+ })
158
+ .description("Build, run, and deploy AI agents on dharta.ai - one tool from create to open.\n" +
159
+ "Agents are harness-native folders (Claude Code, OpenCode, DeepAgents, Goose, Codex CLI) plus dharta.toml; `dharta deploy` ships one selected folder.")
160
+ // Read from package.json so `--version` can never drift from the published
161
+ // package or the release pin-pair (the 0.3.0-vs-0.3.1 mismatch that shipped).
162
+ .version(JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"))
163
+ .version)
164
+ .addHelpText("after", [
165
+ "",
166
+ "Agent lifecycle",
167
+ " create Scaffold a brand-new agent",
168
+ " setup Configure an agent here; write dharta.toml",
169
+ " dev Preview locally as a user would (hot reload)",
170
+ " deploy Deploy this agent folder",
171
+ " open Open the live hosted page",
172
+ " status Every agent here and its live release",
173
+ "",
174
+ "Operate",
175
+ " sessions Inspect an agent's sessions",
176
+ " session View one session transcript",
177
+ " ps Sessions active right now",
178
+ " logs Stream runtime logs",
179
+ " files Read a karta's workspace files",
180
+ " send One-shot message to a deployed agent",
181
+ " rollback Roll back to a previous release",
182
+ " env Environment bindings",
183
+ "",
184
+ "Manage",
185
+ " agent List and show your agents",
186
+ " artifact Designate durable workspace work product",
187
+ "",
188
+ "Account",
189
+ " login Sign in (device flow)",
190
+ " whoami Show the authenticated principal",
191
+ " auth Status, tokens, logout",
192
+ " audit-log Export audit events",
193
+ " analytics Usage and cost analytics",
194
+ " keys API keys",
195
+ " model-keys BYOK provider credentials",
196
+ " members Organization members and invitations",
197
+ " kartas Durable Dharta identity, URLs, and ACLs",
198
+ " api Authenticated public API passthrough",
199
+ " embed-keys Publishable widget keys",
200
+ " caps Spend caps and monthly budget",
201
+ " appearance Agent appearance theme",
202
+ " schedules Recurring agent runs",
203
+ " directories Agent-scoped kartas, users, sessions",
204
+ " webhooks Event delivery endpoints",
205
+ "",
206
+ "Utilities",
207
+ " usage · doctor · update · completion",
208
+ "",
209
+ "Golden path: dharta login -> create -> setup -> dev -> deploy -> open",
210
+ "Run `karta <command> --help` for details. Docs: https://docs.dharta.ai",
211
+ ].join("\n"))
212
+ .action(helpOnlyAction("dharta"));
213
+ program
214
+ .command("login")
215
+ .description("Sign in via your browser (device flow) and store the credential in ~/.dharta/config.toml")
216
+ .option("--with-token", "Read an API key from stdin instead (CI, air-gapped)")
217
+ .option("--base-url <url>", "Override the API base URL")
218
+ .option("--git-base-url <url>", "Override the git server base URL")
219
+ .option("--profile <name>", "Named profile (default: default)")
220
+ .action(async (opts, cmd) => {
221
+ if (opts.baseUrl !== undefined)
222
+ normalizeHttpUrl(opts.baseUrl, "--base-url");
223
+ if (opts.gitBaseUrl !== undefined)
224
+ normalizeHttpUrl(opts.gitBaseUrl, "--git-base-url");
225
+ if (opts.profile !== undefined)
226
+ normalizeNonBlank(opts.profile, "--profile");
227
+ const json = isJson(cmd);
228
+ const result = await login({
229
+ baseUrl: opts.baseUrl,
230
+ gitBaseUrl: opts.gitBaseUrl,
231
+ profile: opts.profile,
232
+ apiKey: opts.withToken ? await readStdin() : undefined,
233
+ // In JSON mode the device-flow narration (code, approve URL)
234
+ // still has to reach the human - on stderr.
235
+ log: json ? logToStderr : undefined,
236
+ });
237
+ if (json) {
238
+ printJson(result);
239
+ return;
240
+ }
241
+ // keyName is "CLI - <hostname>" from the browser flow; show just
242
+ // the machine name. Token-flow keys keep their given name.
243
+ const identity = result.keyName.replace(/^CLI - /, "");
244
+ console.log(`✓ Logged in to ${result.organization.name} as ${identity}`);
245
+ console.log(result.helperInstalled
246
+ ? "✓ git push dharta - enabled"
247
+ : "✗ git push dharta - not enabled (is git installed?)\n" +
248
+ ' git push will prompt: username "dharta", password = your API key');
249
+ });
250
+ const auth = program.command("auth").description("Manage CLI authentication.").action(helpOnlyAction("dharta auth"));
251
+ auth
252
+ .command("status")
253
+ .description("Show who you are, per profile.")
254
+ .action(async (_opts, cmd) => {
255
+ if (isJson(cmd))
256
+ printJson(await authStatusData());
257
+ else
258
+ console.log(await authStatus());
259
+ });
260
+ auth
261
+ .command("token")
262
+ .description("Print the active API token (for piping).")
263
+ .action(async (_opts, cmd) => {
264
+ const token = await authToken();
265
+ if (isJson(cmd))
266
+ printJson({ token });
267
+ else
268
+ console.log(token);
269
+ });
270
+ auth
271
+ .command("logout")
272
+ .description("Remove the stored credential (the key stays revocable on the dashboard).")
273
+ .option("--profile <name>", "Profile to remove (default: the default profile)")
274
+ .action(async (opts, cmd) => {
275
+ if (isJson(cmd))
276
+ printJson(await authLogoutData(opts.profile));
277
+ else
278
+ console.log(await authLogout(opts.profile));
279
+ });
280
+ program
281
+ .command("create [path]")
282
+ .description("Scaffold a brand-new agent in a supported harness format: a starter template + a default dharta.toml (deploy off). Configure delivery + the deploy gate afterward with `dharta setup`. Local only.")
283
+ .option("--name <name>", "agent name (default: the folder's slug)")
284
+ .option("--template <key>", "starter template (default: blank); see /agent_templates")
285
+ .addOption(choiceOption("--harness <type>", "harness format to scaffold", HARNESS_TYPES).default("claude-code"))
286
+ .option("--harness-version <version>", "optional harness version/range pin; omit for Dharta's curated default")
287
+ .action(async (pathArg, opts, cmd) => {
288
+ validateCreateInput(pathArg, opts);
289
+ const json = isJson(cmd);
290
+ const result = await create(pathArg, {
291
+ name: opts.name,
292
+ template: opts.template,
293
+ harness: opts.harness,
294
+ harnessVersion: opts.harnessVersion,
295
+ baseUrl: await resolveBaseUrl(),
296
+ log: json ? logToStderr : (line) => console.log(line),
297
+ });
298
+ if (json)
299
+ printJson(result);
300
+ });
301
+ program
302
+ .command("setup [path]")
303
+ .description("Configure an existing agent (name, deploy gate, persisted git/folder method, optional CI) and write a self-documenting dharta.toml. Destination-agnostic - does NOT deploy.")
304
+ .option("--name <name>", "agent name (default: the folder's slug)")
305
+ .option("--enable", "set deploy = true (allow this agent to deploy)")
306
+ .option("--disable", "set deploy = false (configured, not deployed)")
307
+ .addOption(choiceOption("--method <m>", "persisted transport: git | folder (`github` remains a compatibility alias for folder + --ci github)", ["git", "folder", "github"]))
308
+ .addOption(choiceOption("--ci <provider>", "optional automation provider: github", ["github"]))
309
+ .option("--login", "also log in (device flow) - optional; setup works without it")
310
+ .option("-y, --yes", "non-interactive: take defaults, skip confirmations (including git init)")
311
+ .action(async (pathArg, opts, cmd) => {
312
+ const json = isJson(cmd);
313
+ if (json && opts.login) {
314
+ throw new Error("--login is not supported with `dharta setup --json`. Run `dharta setup --json` first, then `dharta login`.");
315
+ }
316
+ const interactive = !json && !opts.yes && process.stdin.isTTY;
317
+ const rl = interactive
318
+ ? readline.createInterface({ input: process.stdin, output: process.stderr })
319
+ : null;
320
+ const ask = rl
321
+ ? (q) => new Promise((resolve) => rl.question(q, resolve))
322
+ : undefined;
323
+ try {
324
+ const result = await setup(pathArg, {
325
+ name: opts.name,
326
+ enable: opts.enable,
327
+ disable: opts.disable,
328
+ method: opts.method,
329
+ ci: opts.ci,
330
+ yes: opts.yes,
331
+ ask,
332
+ log: json ? logToStderr : (line) => console.log(line),
333
+ });
334
+ if (json)
335
+ printJson(result);
336
+ // Optional login (DEP-9): the bridge to `dharta deploy`. Never blocks setup.
337
+ await offerLogin({
338
+ force: opts.login,
339
+ ask,
340
+ log: json ? logToStderr : (line) => console.log(line),
341
+ });
342
+ }
343
+ finally {
344
+ rl?.close();
345
+ }
346
+ });
347
+ // Git credential helper protocol endpoint, wired by `dharta login`
348
+ // (credential.<git host>.helper = !dharta git-credential). Hidden:
349
+ // humans never call it.
350
+ program
351
+ .command("git-credential <operation>", { hidden: true })
352
+ .description("Git credential helper (installed by `dharta login`).")
353
+ .action(async (operation, _opts, cmd) => {
354
+ rejectJson(cmd, "dharta git-credential");
355
+ const chunks = [];
356
+ for await (const chunk of process.stdin)
357
+ chunks.push(Buffer.from(chunk));
358
+ process.stdout.write(await gitCredential(operation, Buffer.concat(chunks).toString("utf8")));
359
+ });
360
+ program
361
+ .command("audit-log")
362
+ .description("Export audit events (requires audit_log:export).")
363
+ .option("--csv", "Print CSV instead of a table")
364
+ .option("--action <action>", "Filter by audit action")
365
+ .option("--user-id <id>", "Filter by actor user id")
366
+ .option("--from <date>", "Filter from date (YYYY-MM-DD)")
367
+ .option("--to <date>", "Filter to date (YYYY-MM-DD)")
368
+ .option("--limit <n>", "Maximum rows to return")
369
+ .action(async (opts, cmd) => {
370
+ validateAuditLogInput(opts);
371
+ if (opts.csv && isJson(cmd)) {
372
+ throw new Error("Use either --csv or --json for audit-log, not both.");
373
+ }
374
+ const ctx = await buildContext();
375
+ if (isJson(cmd))
376
+ printJson(await auditLogData(ctx, opts));
377
+ else
378
+ console.log(await auditLog(ctx, opts));
379
+ });
380
+ const analyticsCmd = program.command("analytics").description("Show usage and cost analytics.").action(helpOnlyAction("dharta analytics"));
381
+ const analyticsAction = (view) => async (opts, cmd) => {
382
+ normalizeAnalyticsOptions(view, opts);
383
+ const ctx = await buildContext();
384
+ if (isJson(cmd))
385
+ printJson(await analyticsData(ctx, view, opts));
386
+ else
387
+ console.log(await analytics(ctx, view, opts));
388
+ };
389
+ for (const view of ["agents", "users", "instances", "sessions", "models"]) {
390
+ analyticsCmd
391
+ .command(view)
392
+ .description(`Show ${view} analytics.`)
393
+ .addOption(choiceOption("--range <range>", "Window: 7d, 30d, or 90d", ENTITY_ANALYTICS_RANGES))
394
+ .option("--limit <n>", "Maximum rows")
395
+ .action(analyticsAction(view));
396
+ }
397
+ analyticsCmd
398
+ .command("cost")
399
+ .description("Show cost analytics.")
400
+ .addOption(choiceOption("--range <range>", "Window: mtd, 7d, 30d, 90d, last_month, or custom", COST_ANALYTICS_RANGES))
401
+ .addOption(choiceOption("--group-by <field>", "agent, karta, session, user, or model", COST_ANALYTICS_GROUPS))
402
+ .option("--from <date>", "Custom start date (YYYY-MM-DD; implies --range custom)")
403
+ .option("--to <date>", "Custom end date (YYYY-MM-DD; implies --range custom)")
404
+ .option("--limit <n>", "Maximum rows")
405
+ .action(analyticsAction("cost"));
406
+ program
407
+ .command("whoami")
408
+ .description("Show the currently authenticated principal.")
409
+ .action(async (_opts, cmd) => {
410
+ const ctx = await buildContext();
411
+ if (isJson(cmd))
412
+ printJson(await whoamiData(ctx));
413
+ else
414
+ console.log(await whoami(ctx));
415
+ });
416
+ program
417
+ .command("api [method] [path]")
418
+ .description("Call an allowlisted public JSON API endpoint with the active profile.")
419
+ .option("-F, --field <key=value>", "Add a JSON-coerced field to the request body", collect, [])
420
+ .option("-f, --raw-field <key=value>", "Add a string field to the request body", collect, [])
421
+ .option("-H, --header <name:value>", "Add a safe request header", collect, [])
422
+ .option("--body-stdin", "Read the raw request body from stdin")
423
+ .action(async (method, path, opts, cmd) => {
424
+ validateApiRequest(method, path, opts);
425
+ const preparedOpts = await prepareApiOptions(opts);
426
+ const ctx = await buildContext();
427
+ const result = await apiCmd(ctx, method, path, preparedOpts);
428
+ if (isJson(cmd))
429
+ printJson(result.body);
430
+ else {
431
+ const output = formatApiResult(result);
432
+ if (output)
433
+ console.log(output);
434
+ }
435
+ });
436
+ const keys = program.command("keys").description("Manage API keys.").action(helpOnlyAction("dharta keys"));
437
+ keys
438
+ .command("list")
439
+ .description("List API keys for the current organization (requires keys:read or keys:write).")
440
+ .action(async (_opts, cmd) => {
441
+ const ctx = await buildContext();
442
+ if (isJson(cmd))
443
+ printJson(await keysListData(ctx));
444
+ else
445
+ console.log(await keysList(ctx));
446
+ });
447
+ keys
448
+ .command("create <name>")
449
+ .description("Mint a new API key.")
450
+ .addOption(choiceListOption("--scope <scope...>", "operation scopes", CLI_API_KEY_SCOPES)
451
+ .default([]))
452
+ .option("--expires-at <iso>", "Expiry timestamp; required within 90 days for sensitive scopes")
453
+ .action(async (name, opts, cmd) => {
454
+ validateKeysCreateInput(name, opts.scope, { expiresAt: opts.expiresAt });
455
+ const ctx = await buildContext();
456
+ const keyOpts = { expiresAt: opts.expiresAt };
457
+ if (isJson(cmd))
458
+ printJson(await keysCreateData(ctx, name, opts.scope, keyOpts));
459
+ else
460
+ console.log(await keysCreate(ctx, name, opts.scope, keyOpts));
461
+ });
462
+ keys
463
+ .command("rename <id> <name>")
464
+ .description("Rename an API key by id.")
465
+ .action(async (id, name, _opts, cmd) => {
466
+ normalizeNonBlank(id, "API key id");
467
+ normalizeNonBlank(name, "API key name");
468
+ const ctx = await buildContext();
469
+ if (isJson(cmd))
470
+ printJson(await keysRenameData(ctx, id, name));
471
+ else
472
+ console.log(await keysRename(ctx, id, name));
473
+ });
474
+ keys
475
+ .command("revoke <id>")
476
+ .description("Revoke an API key by id.")
477
+ .action(async (id, _opts, cmd) => {
478
+ normalizeNonBlank(id, "API key id");
479
+ const ctx = await buildContext();
480
+ if (isJson(cmd))
481
+ printJson(await keysRevokeData(ctx, id));
482
+ else
483
+ console.log(await keysRevoke(ctx, id));
484
+ });
485
+ const modelKeys = program.command("model-keys").description("Manage BYOK provider credentials.").action(helpOnlyAction("dharta model-keys"));
486
+ modelKeys
487
+ .command("list")
488
+ .description("List model provider credentials.")
489
+ .action(async (_opts, cmd) => {
490
+ const ctx = await buildContext();
491
+ if (isJson(cmd))
492
+ printJson(await modelKeysListData(ctx));
493
+ else
494
+ console.log(await modelKeysList(ctx));
495
+ });
496
+ modelKeys
497
+ .command("create")
498
+ .description("Store an encrypted model provider credential.")
499
+ .addArgument(choiceArgument("<provider>", "provider", ["anthropic", "openai", "bedrock", "vertex", "openrouter", "xai", "google"]))
500
+ .argument("<name>", "connector name")
501
+ .option("--key <plaintext>", "Provider API key plaintext")
502
+ .option("--key-stdin", "Read provider API key plaintext from stdin")
503
+ .addOption(choiceOption("--auth-type <type>", "api_key or oauth_subscription", MODEL_KEY_AUTH_TYPES))
504
+ .option("--endpoint-url <url>", "Provider endpoint URL override")
505
+ .addOption(choiceOption("--api-format <format>", "Explicit provider API format", MODEL_API_FORMATS))
506
+ .action(async (provider, name, opts, cmd) => {
507
+ validateModelKeysCreateOptions(provider, name, opts);
508
+ const preparedOpts = await prepareModelKeysCreateOptions(provider, name, opts);
509
+ const ctx = await buildContext();
510
+ if (isJson(cmd))
511
+ printJson(await modelKeysCreateData(ctx, provider, name, preparedOpts));
512
+ else
513
+ console.log(await modelKeysCreate(ctx, provider, name, preparedOpts));
514
+ });
515
+ modelKeys
516
+ .command("revoke <id>")
517
+ .description("Revoke a model provider credential.")
518
+ .action(async (id, _opts, cmd) => {
519
+ validateModelKeyId(id);
520
+ const ctx = await buildContext();
521
+ if (isJson(cmd))
522
+ printJson(await modelKeysRevokeData(ctx, id));
523
+ else
524
+ console.log(await modelKeysRevoke(ctx, id));
525
+ });
526
+ const members = program.command("members").description("Manage organization members.").action(helpOnlyAction("dharta members"));
527
+ members
528
+ .command("list")
529
+ .description("List members and pending invitations.")
530
+ .action(async (_opts, cmd) => {
531
+ const ctx = await buildContext();
532
+ if (isJson(cmd))
533
+ printJson(await membersListData(ctx));
534
+ else
535
+ console.log(await membersList(ctx));
536
+ });
537
+ members
538
+ .command("invite <email>")
539
+ .description("Invite a member.")
540
+ .addOption(choiceOption("--role <role>", "owner, admin, developer, billing, or viewer", MEMBER_ROLES))
541
+ .action(async (email, opts, cmd) => {
542
+ if (opts.role === undefined) {
543
+ throw new Error("--role is required. Usage: dharta members invite <email> --role <role>");
544
+ }
545
+ validateMembersInviteInput(email);
546
+ const ctx = await buildContext();
547
+ if (isJson(cmd))
548
+ printJson(await membersInviteData(ctx, email, opts.role));
549
+ else
550
+ console.log(await membersInvite(ctx, email, opts.role));
551
+ });
552
+ members
553
+ .command("set-role <id>")
554
+ .description("Change a member role.")
555
+ .addOption(choiceOption("--role <role>", "owner, admin, developer, billing, or viewer", MEMBER_ROLES))
556
+ .action(async (id, opts, cmd) => {
557
+ if (opts.role === undefined) {
558
+ throw new Error("--role is required. Usage: dharta members set-role <id> --role <role>");
559
+ }
560
+ parsePositiveInteger(id, "member id");
561
+ const ctx = await buildContext();
562
+ if (isJson(cmd))
563
+ printJson(await membersSetRoleData(ctx, id, opts.role));
564
+ else
565
+ console.log(await membersSetRole(ctx, id, opts.role));
566
+ });
567
+ members
568
+ .command("remove <id>")
569
+ .description("Remove a member.")
570
+ .action(async (id, _opts, cmd) => {
571
+ parsePositiveInteger(id, "member id");
572
+ const ctx = await buildContext();
573
+ if (isJson(cmd))
574
+ printJson(await membersRemoveData(ctx, id));
575
+ else
576
+ console.log(await membersRemove(ctx, id));
577
+ });
578
+ const kartas = program.command("kartas").description("Manage durable kartas and virtual employees.").action(helpOnlyAction("dharta kartas"));
579
+ kartas
580
+ .command("create <name>")
581
+ .description("Create a named karta with a stable URL and managed access.")
582
+ .option("--agent <slug>", "Owning agent slug")
583
+ .option("--slug <slug>", "Vanity slug beneath the agent URL")
584
+ .addOption(choiceOption("--access <audience>", "members (specific people), anonymous (anyone with link), or off", DHARTA_ACCESS_AUDIENCES).default("members"))
585
+ .option("--member <email>", "Organization user to add; repeat for more", collect, [])
586
+ .addOption(choiceOption("--member-role <role>", "Role for people added with --member", DHARTA_MEMBER_ROLES).default("participant"))
587
+ .action(async (name, opts, cmd) => {
588
+ const options = { ...opts, members: opts.member };
589
+ validateKartaCreateOptions(name, options);
590
+ const ctx = await buildContext();
591
+ if (isJson(cmd))
592
+ printJson(await kartasCreateData(ctx, name, options));
593
+ else
594
+ console.log(await kartasCreate(ctx, name, options));
595
+ });
596
+ kartas
597
+ .command("list")
598
+ .description("List durable kartas and their stable URLs.")
599
+ .option("--agent <slug>", "Limit results to one owning agent")
600
+ .option("--named", "Show only kartas with a name and vanity slug")
601
+ .option("--archived", "Show archived kartas instead of active ones")
602
+ .action(async (opts, cmd) => {
603
+ const ctx = await buildContext();
604
+ if (isJson(cmd))
605
+ printJson(await kartasListData(ctx, opts));
606
+ else
607
+ console.log(await kartasList(ctx, opts));
608
+ });
609
+ kartas
610
+ .command("show <id>")
611
+ .description("Show one karta, its stable URL, and how access is managed.")
612
+ .action(async (id, _opts, cmd) => {
613
+ normalizeNonBlank(id, "dharta id");
614
+ const ctx = await buildContext();
615
+ if (isJson(cmd))
616
+ printJson(await kartasShowData(ctx, id));
617
+ else
618
+ console.log(await kartasShow(ctx, id));
619
+ });
620
+ kartas
621
+ .command("name <id>")
622
+ .description("Give an existing karta a name and vanity slug without changing access.")
623
+ .option("--name <name>", "Display name")
624
+ .option("--slug <slug>", "Vanity slug beneath the agent URL")
625
+ .action(async (id, opts, cmd) => {
626
+ validateKartaNameOptions(opts);
627
+ const ctx = await buildContext();
628
+ if (isJson(cmd))
629
+ printJson(await kartasNameData(ctx, id, opts));
630
+ else
631
+ console.log(await kartasName(ctx, id, opts));
632
+ });
633
+ kartas
634
+ .command("configure <id>")
635
+ .description("Set a karta name, vanity slug, and stable URL access.")
636
+ .option("--name <name>", "Display name")
637
+ .option("--slug <slug>", "Vanity slug beneath the agent URL")
638
+ .addOption(choiceOption("--access <audience>", "members (specific people), anonymous (anyone with link), or off", DHARTA_ACCESS_AUDIENCES))
639
+ .action(async (id, opts, cmd) => {
640
+ validateKartaConfigureOptions(opts);
641
+ const ctx = await buildContext();
642
+ if (isJson(cmd))
643
+ printJson(await kartasConfigureData(ctx, id, opts));
644
+ else
645
+ console.log(await kartasConfigure(ctx, id, opts));
646
+ });
647
+ kartas
648
+ .command("archive <id>")
649
+ .alias("delete")
650
+ .description("Retire a karta (reversible; its files and history are kept).")
651
+ .action(async (id, _opts, cmd) => {
652
+ normalizeNonBlank(id, "dharta id");
653
+ const ctx = await buildContext();
654
+ if (isJson(cmd))
655
+ printJson(await kartasArchiveData(ctx, id));
656
+ else
657
+ console.log(await kartasArchive(ctx, id));
658
+ });
659
+ kartas
660
+ .command("restore <id>")
661
+ .description("Bring an archived karta back, with its files and history.")
662
+ .action(async (id, _opts, cmd) => {
663
+ normalizeNonBlank(id, "dharta id");
664
+ const ctx = await buildContext();
665
+ if (isJson(cmd))
666
+ printJson(await kartasRestoreData(ctx, id));
667
+ else
668
+ console.log(await kartasRestore(ctx, id));
669
+ });
670
+ const kartaEnv = kartas
671
+ .command("env")
672
+ .description("Configure ONE karta's environment (its own credentials and settings).")
673
+ .action(helpOnlyAction("dharta kartas env"));
674
+ kartaEnv
675
+ .command("list <agent> <karta>")
676
+ .description("List a karta's environment variables. Secret values are never shown.")
677
+ .action(async (agent, karta, _opts, cmd) => {
678
+ const ctx = await buildContext();
679
+ if (isJson(cmd))
680
+ printJson(await kartaEnvListData(ctx, agent, karta));
681
+ else
682
+ console.log(await kartaEnvList(ctx, agent, karta));
683
+ });
684
+ kartaEnv
685
+ .command("set <agent> <karta> <name> <value>")
686
+ .description("Set one variable on one dharta.")
687
+ .option("--plain", "Store as a readable value (default: secret)")
688
+ .action(async (agent, karta, name, value, opts, cmd) => {
689
+ const ctx = await buildContext();
690
+ const options = { secret: opts.plain ? false : undefined };
691
+ if (isJson(cmd))
692
+ printJson(await kartaEnvSetData(ctx, agent, karta, name, value, options));
693
+ else
694
+ console.log(await kartaEnvSet(ctx, agent, karta, name, value, options));
695
+ });
696
+ kartaEnv
697
+ .command("unset <agent> <karta> <name>")
698
+ .description("Remove one variable from one dharta.")
699
+ .action(async (agent, karta, name, _opts, cmd) => {
700
+ const ctx = await buildContext();
701
+ if (isJson(cmd))
702
+ printJson(await kartaEnvUnsetData(ctx, agent, karta, name));
703
+ else
704
+ console.log(await kartaEnvUnset(ctx, agent, karta, name));
705
+ });
706
+ kartas
707
+ .command("grant <id> <email>")
708
+ .description("Add an organization user to a karta with managed access.")
709
+ .addOption(choiceOption("--role <role>", "viewer, participant, or manager", DHARTA_MEMBER_ROLES).default("participant"))
710
+ .action(async (id, email, opts, cmd) => {
711
+ const ctx = await buildContext();
712
+ if (isJson(cmd))
713
+ printJson(await kartaMembersGrantData(ctx, id, email, opts.role));
714
+ else
715
+ console.log(await kartaMembersGrant(ctx, id, email, opts.role));
716
+ });
717
+ kartas
718
+ .command("set-role <id> <membership-id>")
719
+ .description("Change a person's role on a karta with managed access.")
720
+ .addOption(choiceOption("--role <role>", "viewer, participant, or manager", DHARTA_MEMBER_ROLES))
721
+ .action(async (id, membershipId, opts, cmd) => {
722
+ if (opts.role === undefined) {
723
+ throw new Error("--role is required. Usage: dharta kartas set-role <id> <membership-id> --role <role>");
724
+ }
725
+ const ctx = await buildContext();
726
+ if (isJson(cmd))
727
+ printJson(await kartaMembersSetRoleData(ctx, id, membershipId, opts.role));
728
+ else
729
+ console.log(await kartaMembersSetRole(ctx, id, membershipId, opts.role));
730
+ });
731
+ kartas
732
+ .command("revoke <id> <membership-id>")
733
+ .description("Remove a person's access to a dharta.")
734
+ .action(async (id, membershipId, _opts, cmd) => {
735
+ const ctx = await buildContext();
736
+ if (isJson(cmd))
737
+ printJson(await kartaMembersRevokeData(ctx, id, membershipId));
738
+ else
739
+ console.log(await kartaMembersRevoke(ctx, id, membershipId));
740
+ });
741
+ const embedKeys = program.command("embed-keys").description("Manage publishable widget embed keys.").action(helpOnlyAction("dharta embed-keys"));
742
+ const embedKeyOpts = (cmd) => cmd
743
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
744
+ .option("-p, --project <slug>", "Alias of --agent")
745
+ .option("--dir <dir>", "Agent directory (default: current directory)");
746
+ embedKeyOpts(embedKeys.command("list"))
747
+ .description("List publishable embed keys for an agent.")
748
+ .action(async (_opts, cmd) => {
749
+ const opts = commandOpts(cmd);
750
+ const resolved = await resolveAgentOptions(opts);
751
+ const ctx = await buildContext();
752
+ if (isJson(cmd))
753
+ printJson(await embedKeysListData(ctx, resolved));
754
+ else
755
+ console.log(await embedKeysList(ctx, resolved));
756
+ });
757
+ embedKeyOpts(embedKeys.command("create <name>"))
758
+ .description("Create a publishable embed key and reveal its secret once.")
759
+ .option("--origin <origin...>", "Allowed browser origin; repeat or comma-separate")
760
+ .action(async (name, _opts, cmd) => {
761
+ const opts = commandOpts(cmd);
762
+ const origins = opts.origin === undefined ? undefined : parseOrigins(opts.origin);
763
+ validateEmbedKeysCreateInput(name, { origins });
764
+ const resolved = await resolveAgentOptions({ ...opts, origins });
765
+ const ctx = await buildContext();
766
+ if (isJson(cmd))
767
+ printJson(await embedKeysCreateData(ctx, name, resolved));
768
+ else
769
+ console.log(await embedKeysCreate(ctx, name, resolved));
770
+ });
771
+ embedKeyOpts(embedKeys.command("rotate <id>"))
772
+ .description("Rotate an embed key and reveal the replacement secret once.")
773
+ .action(async (id, _opts, cmd) => {
774
+ const opts = commandOpts(cmd);
775
+ normalizeNonBlank(id, "Embed key id");
776
+ const resolved = await resolveAgentOptions(opts);
777
+ const ctx = await buildContext();
778
+ if (isJson(cmd))
779
+ printJson(await embedKeysRotateData(ctx, id, resolved));
780
+ else
781
+ console.log(await embedKeysRotate(ctx, id, resolved));
782
+ });
783
+ for (const action of ["enable", "disable"]) {
784
+ embedKeyOpts(embedKeys.command(`${action} <id>`))
785
+ .description(`${action === "enable" ? "Enable" : "Disable"} an embed key.`)
786
+ .action(async (id, _opts, cmd) => {
787
+ const opts = commandOpts(cmd);
788
+ normalizeNonBlank(id, "Embed key id");
789
+ const resolved = await resolveAgentOptions(opts);
790
+ const ctx = await buildContext();
791
+ if (isJson(cmd))
792
+ printJson(await embedKeysToggleData(ctx, id, action, resolved));
793
+ else
794
+ console.log(await embedKeysToggle(ctx, id, action, resolved));
795
+ });
796
+ }
797
+ embedKeyOpts(embedKeys.command("revoke <id>"))
798
+ .description("Revoke an embed key permanently.")
799
+ .action(async (id, _opts, cmd) => {
800
+ const opts = commandOpts(cmd);
801
+ normalizeNonBlank(id, "Embed key id");
802
+ const resolved = await resolveAgentOptions(opts);
803
+ const ctx = await buildContext();
804
+ if (isJson(cmd))
805
+ printJson(await embedKeysRevokeData(ctx, id, resolved));
806
+ else
807
+ console.log(await embedKeysRevoke(ctx, id, resolved));
808
+ });
809
+ embedKeyOpts(embedKeys.command("enable-identity <id>"))
810
+ .description("Enable verified widget identity and reveal the signing secret once (re-run to rotate it).")
811
+ .action(async (id, _opts, cmd) => {
812
+ const opts = commandOpts(cmd);
813
+ normalizeNonBlank(id, "Embed key id");
814
+ const resolved = await resolveAgentOptions(opts);
815
+ const ctx = await buildContext();
816
+ if (isJson(cmd))
817
+ printJson(await embedKeysSetIdentityData(ctx, id, resolved));
818
+ else
819
+ console.log(await embedKeysSetIdentity(ctx, id, resolved));
820
+ });
821
+ embedKeyOpts(embedKeys.command("disable-identity <id>"))
822
+ .description("Disable verified widget identity for an embed key.")
823
+ .action(async (id, _opts, cmd) => {
824
+ const opts = commandOpts(cmd);
825
+ normalizeNonBlank(id, "Embed key id");
826
+ const resolved = await resolveAgentOptions(opts);
827
+ const ctx = await buildContext();
828
+ if (isJson(cmd))
829
+ printJson(await embedKeysClearIdentityData(ctx, id, resolved));
830
+ else
831
+ console.log(await embedKeysClearIdentity(ctx, id, resolved));
832
+ });
833
+ const caps = program.command("caps").description("Manage spend caps and the monthly budget.").action(helpOnlyAction("dharta caps"));
834
+ const addBudgetSetCommand = (cmd, description = "Set the monthly budget amount.") => cmd
835
+ .description(description)
836
+ .option("--amount <dollars>", "Budget amount in dollars")
837
+ .option("--hard-stop", "Stop usage when the budget is reached")
838
+ .option("--soft", "Track spend and alerts without stopping usage")
839
+ .option("--enable-alerts", "Enable threshold alerts")
840
+ .option("--disable-alerts", "Disable threshold alerts")
841
+ .option("--alert-threshold <percent>", "Alert threshold percent")
842
+ .option("--recipient-user-id <id...>", "Additional alert recipient user id")
843
+ .action(async (opts, cmd) => {
844
+ validateBudgetSetOptions(opts);
845
+ const ctx = await buildContext();
846
+ if (isJson(cmd))
847
+ printJson(await capsBudgetSetData(ctx, opts));
848
+ else
849
+ console.log(await capsBudgetSet(ctx, opts));
850
+ });
851
+ const capsBudgetCmd = caps
852
+ .command("budget")
853
+ .description("Show the monthly budget and current spend.")
854
+ .action(async (_opts, cmd) => {
855
+ const ctx = await buildContext();
856
+ if (isJson(cmd))
857
+ printJson(await capsBudgetData(ctx));
858
+ else
859
+ console.log(await capsBudget(ctx));
860
+ });
861
+ addBudgetSetCommand(capsBudgetCmd.command("set"));
862
+ addBudgetSetCommand(caps.command("set"), "Set the monthly budget amount (alias of `dharta caps budget set`).");
863
+ caps
864
+ .command("instance <id>")
865
+ .description("Show the spend cap for a durable karta.")
866
+ .action(async (id, _opts, cmd) => {
867
+ normalizeNonBlank(id, "instance id");
868
+ const ctx = await buildContext();
869
+ if (isJson(cmd))
870
+ printJson(await capsInstanceData(ctx, id));
871
+ else
872
+ console.log(await capsInstance(ctx, id));
873
+ });
874
+ caps
875
+ .command("instance-set <id>")
876
+ .description("Set or clear a durable karta spend cap.")
877
+ .option("--amount <dollars>", "Cap amount in dollars")
878
+ .option("--clear", "Clear the cap")
879
+ .action(async (id, opts, cmd) => {
880
+ normalizeNonBlank(id, "instance id");
881
+ validateCapSetOptions(opts);
882
+ const ctx = await buildContext();
883
+ if (isJson(cmd))
884
+ printJson(await capsInstanceSetData(ctx, id, opts));
885
+ else
886
+ console.log(await capsInstanceSet(ctx, id, opts));
887
+ });
888
+ caps
889
+ .command("end-user <id>")
890
+ .description("Show the global spend cap for a verified end-user.")
891
+ .action(async (id, _opts, cmd) => {
892
+ normalizeNonBlank(id, "end-user id");
893
+ const ctx = await buildContext();
894
+ if (isJson(cmd))
895
+ printJson(await capsEndUserData(ctx, id));
896
+ else
897
+ console.log(await capsEndUser(ctx, id));
898
+ });
899
+ caps
900
+ .command("end-user-set <id>")
901
+ .description("Set or clear the global spend cap for a verified end-user.")
902
+ .option("--amount <dollars>", "Cap amount in dollars")
903
+ .option("--clear", "Clear the cap")
904
+ .action(async (id, opts, cmd) => {
905
+ normalizeNonBlank(id, "end-user id");
906
+ validateCapSetOptions(opts);
907
+ const ctx = await buildContext();
908
+ if (isJson(cmd))
909
+ printJson(await capsEndUserSetData(ctx, id, opts));
910
+ else
911
+ console.log(await capsEndUserSet(ctx, id, opts));
912
+ });
913
+ caps
914
+ .command("seat <instance> <end-user-id>")
915
+ .description("Show the per-seat spend cap for an end-user on a durable karta.")
916
+ .action(async (instance, endUserId, _opts, cmd) => {
917
+ normalizeNonBlank(instance, "instance id");
918
+ normalizeNonBlank(endUserId, "end-user id");
919
+ const ctx = await buildContext();
920
+ if (isJson(cmd))
921
+ printJson(await capsSeatData(ctx, instance, endUserId));
922
+ else
923
+ console.log(await capsSeat(ctx, instance, endUserId));
924
+ });
925
+ caps
926
+ .command("seat-set <instance> <end-user-id>")
927
+ .description("Set or clear the per-seat spend cap for an end-user on a durable karta.")
928
+ .option("--amount <dollars>", "Cap amount in dollars")
929
+ .option("--clear", "Clear the cap")
930
+ .action(async (instance, endUserId, opts, cmd) => {
931
+ normalizeNonBlank(instance, "instance id");
932
+ normalizeNonBlank(endUserId, "end-user id");
933
+ validateCapSetOptions(opts);
934
+ const ctx = await buildContext();
935
+ if (isJson(cmd))
936
+ printJson(await capsSeatSetData(ctx, instance, endUserId, opts));
937
+ else
938
+ console.log(await capsSeatSet(ctx, instance, endUserId, opts));
939
+ });
940
+ const appearance = program.command("appearance").description("Get or update an agent's appearance theme.").action(helpOnlyAction("dharta appearance"));
941
+ const appearanceOpts = (cmd) => cmd
942
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
943
+ .option("-p, --project <slug>", "Alias of --agent")
944
+ .option("--dir <dir>", "Agent directory (default: current directory)");
945
+ appearanceOpts(appearance.command("get"))
946
+ .description("Print the stored appearance JSON for an agent.")
947
+ .action(async (_opts, cmd) => {
948
+ const opts = commandOpts(cmd);
949
+ const resolved = await resolveAgentOptions(opts);
950
+ const ctx = await buildContext();
951
+ if (isJson(cmd))
952
+ printJson(await appearanceGetData(ctx, resolved));
953
+ else
954
+ console.log(await appearanceGet(ctx, resolved));
955
+ });
956
+ appearanceOpts(appearance.command("set"))
957
+ .description("Merge appearance JSON from a local file.")
958
+ .option("--file <file>", "JSON file containing appearance tokens")
959
+ .action(async (_opts, cmd) => {
960
+ const opts = commandOpts(cmd);
961
+ await validateAppearanceSetInput(opts);
962
+ const resolved = await resolveAgentOptions(opts);
963
+ const ctx = await buildContext();
964
+ if (isJson(cmd))
965
+ printJson(await appearanceSetData(ctx, resolved));
966
+ else
967
+ console.log(await appearanceSet(ctx, resolved));
968
+ });
969
+ const schedules = program.command("schedules").description("Manage recurring agent runs.").action(helpOnlyAction("dharta schedules"));
970
+ const scheduleOpts = (cmd) => cmd
971
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
972
+ .option("-p, --project <slug>", "Alias of --agent")
973
+ .option("--dir <dir>", "Agent directory (default: current directory)");
974
+ scheduleOpts(schedules.command("list"))
975
+ .description("List recurring runs for an agent.")
976
+ .action(async (_opts, cmd) => {
977
+ const opts = commandOpts(cmd);
978
+ const resolved = await resolveAgentOptions(opts);
979
+ const ctx = await buildContext();
980
+ if (isJson(cmd))
981
+ printJson(await schedulesListData(ctx, resolved));
982
+ else
983
+ console.log(await schedulesList(ctx, resolved));
984
+ });
985
+ scheduleOpts(schedules.command("create"))
986
+ .description("Create a recurring run.")
987
+ .option("--cron <expr>", "Cron expression")
988
+ .option("--timezone <tz>", "Timezone (default: UTC)")
989
+ .option("--message <text>", "Initial message")
990
+ .option("--budget <dollars>", "Run budget in dollars")
991
+ .option("--budget-cents <cents>", "Run budget in integer cents")
992
+ .option("--release-version <version>", "Pin runs to a release version")
993
+ .option("--end-user-id <id>", "End-user id to run as")
994
+ .option("--run-now", "Queue one run immediately after creating the recurring run")
995
+ .action(async (_opts, cmd) => {
996
+ const opts = commandOpts(cmd);
997
+ validateSchedulesCreateInput(opts);
998
+ const resolved = await resolveAgentOptions(opts);
999
+ const ctx = await buildContext();
1000
+ if (isJson(cmd))
1001
+ printJson(await schedulesCreateData(ctx, resolved));
1002
+ else
1003
+ console.log(await schedulesCreate(ctx, resolved));
1004
+ });
1005
+ for (const action of ["pause", "resume", "run"]) {
1006
+ scheduleOpts(schedules.command(`${action} <id>`))
1007
+ .description(`${action === "run" ? "Queue" : action === "pause" ? "Pause" : "Resume"} a recurring run.`)
1008
+ .action(async (id, _opts, cmd) => {
1009
+ const opts = commandOpts(cmd);
1010
+ parsePositiveInteger(normalizeNonBlank(id, "Schedule id"), "Schedule id");
1011
+ const resolved = await resolveAgentOptions(opts);
1012
+ const ctx = await buildContext();
1013
+ if (isJson(cmd))
1014
+ printJson(await schedulesActionData(ctx, id, action, resolved));
1015
+ else
1016
+ console.log(await schedulesAction(ctx, id, action, resolved));
1017
+ });
1018
+ }
1019
+ scheduleOpts(schedules.command("delete <id>").alias("rm"))
1020
+ .description("Delete a recurring run.")
1021
+ .action(async (id, _opts, cmd) => {
1022
+ const opts = commandOpts(cmd);
1023
+ parsePositiveInteger(normalizeNonBlank(id, "Schedule id"), "Schedule id");
1024
+ const resolved = await resolveAgentOptions(opts);
1025
+ const ctx = await buildContext();
1026
+ if (isJson(cmd))
1027
+ printJson(await schedulesDeleteData(ctx, id, resolved));
1028
+ else
1029
+ console.log(await schedulesDelete(ctx, id, resolved));
1030
+ });
1031
+ const directories = program.command("directories").description("Read agent-scoped kartas, users, and sessions.").action(helpOnlyAction("dharta directories"));
1032
+ const directoryOpts = (cmd) => cmd
1033
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
1034
+ .option("-p, --project <slug>", "Alias of --agent")
1035
+ .option("--dir <dir>", "Agent directory (default: current directory)")
1036
+ .option("--limit <n>", "Maximum rows to return");
1037
+ directoryOpts(directories.command("instances"))
1038
+ .description("List durable kartas for an agent.")
1039
+ .action(async (_opts, cmd) => {
1040
+ const opts = commandOpts(cmd);
1041
+ validateDirectoryOptions(opts);
1042
+ const resolved = await resolveAgentOptions(opts);
1043
+ const ctx = await buildContext();
1044
+ if (isJson(cmd))
1045
+ printJson(await directoryInstancesData(ctx, resolved));
1046
+ else
1047
+ console.log(await directoryInstances(ctx, resolved));
1048
+ });
1049
+ directoryOpts(directories.command("end-users").alias("users"))
1050
+ .description("List verified users for an agent.")
1051
+ .action(async (_opts, cmd) => {
1052
+ const opts = commandOpts(cmd);
1053
+ validateDirectoryOptions(opts);
1054
+ const resolved = await resolveAgentOptions(opts);
1055
+ const ctx = await buildContext();
1056
+ if (isJson(cmd))
1057
+ printJson(await directoryEndUsersData(ctx, resolved));
1058
+ else
1059
+ console.log(await directoryEndUsers(ctx, resolved));
1060
+ });
1061
+ directoryOpts(directories.command("sessions"))
1062
+ .description("List sessions for an agent.")
1063
+ .action(async (_opts, cmd) => {
1064
+ const opts = commandOpts(cmd);
1065
+ validateDirectoryOptions(opts);
1066
+ const resolved = await resolveAgentOptions(opts);
1067
+ const ctx = await buildContext();
1068
+ if (isJson(cmd))
1069
+ printJson(await directorySessionsData(ctx, resolved));
1070
+ else
1071
+ console.log(await directorySessions(ctx, resolved));
1072
+ });
1073
+ const webhooks = program.command("webhooks").description("Manage webhook endpoints.").action(helpOnlyAction("dharta webhooks"));
1074
+ webhooks
1075
+ .command("list")
1076
+ .description("List webhook endpoints.")
1077
+ .action(async (_opts, cmd) => {
1078
+ const ctx = await buildContext();
1079
+ if (isJson(cmd))
1080
+ printJson(await webhooksListData(ctx));
1081
+ else
1082
+ console.log(await webhooksList(ctx));
1083
+ });
1084
+ webhooks
1085
+ .command("create <name> <url>")
1086
+ .description("Create a webhook endpoint and reveal its signing secret once.")
1087
+ .option("--event <event...>", "Subscribed event type; repeat or comma-separate")
1088
+ .action(async (name, url, opts, cmd) => {
1089
+ const resolved = { event: parseEvents(opts.event) };
1090
+ validateWebhooksCreateInput(name, url, resolved);
1091
+ const ctx = await buildContext();
1092
+ if (isJson(cmd))
1093
+ printJson(await webhooksCreateData(ctx, name, url, resolved));
1094
+ else
1095
+ console.log(await webhooksCreate(ctx, name, url, resolved));
1096
+ });
1097
+ webhooks
1098
+ .command("update <id>")
1099
+ .description("Update a webhook endpoint.")
1100
+ .option("--name <name>", "Endpoint name")
1101
+ .option("--url <url>", "Destination URL")
1102
+ .option("--event <event...>", "Subscribed event type; repeat or comma-separate")
1103
+ .action(async (id, opts, cmd) => {
1104
+ const resolved = { name: opts.name, url: opts.url, event: opts.event ? parseEvents(opts.event) : undefined };
1105
+ validateWebhooksUpdateInput(id, resolved);
1106
+ const ctx = await buildContext();
1107
+ if (isJson(cmd))
1108
+ printJson(await webhooksUpdateData(ctx, id, resolved));
1109
+ else
1110
+ console.log(await webhooksUpdate(ctx, id, resolved));
1111
+ });
1112
+ webhooks
1113
+ .command("enable <id>")
1114
+ .description("Re-enable a disabled webhook endpoint.")
1115
+ .action(async (id, _opts, cmd) => {
1116
+ normalizeNonBlank(id, "Webhook endpoint id");
1117
+ const ctx = await buildContext();
1118
+ if (isJson(cmd))
1119
+ printJson(await webhooksEnableData(ctx, id));
1120
+ else
1121
+ console.log(await webhooksEnable(ctx, id));
1122
+ });
1123
+ webhooks
1124
+ .command("delete <id>")
1125
+ .alias("rm")
1126
+ .description("Delete a webhook endpoint.")
1127
+ .action(async (id, _opts, cmd) => {
1128
+ normalizeNonBlank(id, "Webhook endpoint id");
1129
+ const ctx = await buildContext();
1130
+ if (isJson(cmd))
1131
+ printJson(await webhooksDeleteData(ctx, id));
1132
+ else
1133
+ console.log(await webhooksDelete(ctx, id));
1134
+ });
1135
+ program
1136
+ .command("open [target]")
1137
+ .description(`Open an agent page or browser handoff target. Targets: ${OPEN_TARGETS_HELP}.`)
1138
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)").option("-p, --project <slug>", "Alias of --agent")
1139
+ .option("--dir <dir>", "Agent directory (default: current directory)")
1140
+ .action(async (target, opts, cmd) => {
1141
+ const normalizedTarget = normalizeOpenTarget(target);
1142
+ validateOpenTarget(normalizedTarget);
1143
+ if (isHandoffTarget(normalizedTarget)) {
1144
+ const baseUrl = await resolveBaseUrl();
1145
+ if (isJson(cmd))
1146
+ printJson(openHandoffData(baseUrl, normalizedTarget));
1147
+ else
1148
+ console.log(await openHandoff(baseUrl, normalizedTarget));
1149
+ return;
1150
+ }
1151
+ const needsAgent = normalizedTarget !== "console" ||
1152
+ opts.agent !== undefined ||
1153
+ opts.project !== undefined ||
1154
+ opts.dir !== undefined;
1155
+ const resolved = needsAgent ? await resolveAgentOptions(opts) : opts;
1156
+ const ctx = await buildContext();
1157
+ // --json prints the URL object and does NOT open the browser.
1158
+ if (isJson(cmd))
1159
+ printJson(await openData(ctx, normalizedTarget, resolved));
1160
+ else
1161
+ console.log(await openCmd(ctx, normalizedTarget, resolved));
1162
+ });
1163
+ program
1164
+ .command("dev [path]")
1165
+ .description("Preview your agent locally the way a USER experiences it - behind the same consumer session API production serves (widget parity), with hot reload. Distinct from running `claude` in the folder, which is authoring. [path] (or cwd) selects the agent; in a multi-agent repo it picks the sole one or asks.")
1166
+ .option("--dir <dir>", "Agent directory (alias for [path]; default: current directory)")
1167
+ .option("-m, --message <text>", "Send one message and exit")
1168
+ .option("--user <id>", "Simulate a verified end user (per-user memory behavior)")
1169
+ .option("--port <port>", "Serve the local agent API on a stable port (for widgets/frontends)")
1170
+ .option("--verbose", "Stream agent-runtime output and parity details")
1171
+ .option("--release <vN>", "Run the exact built tree prod serves (audited archive pull)")
1172
+ .addOption(choiceOption("--env <mode>", "Bind prod env vars instead of .env", ["remote"]))
1173
+ .option("--include-secrets", "With --env remote: fetch secret values (requires env:secrets:read; audited)")
1174
+ .option("-a, --agent <slug>", "Agent for --release/--env (default: the agent here)").option("-p, --project <slug>", "Alias of --agent")
1175
+ .action(async (pathArg, opts, cmd) => {
1176
+ rejectJson(cmd, "dharta dev");
1177
+ if (pathArg !== undefined && opts.dir !== undefined) {
1178
+ throw new Error("Use either [path] or --dir, not both.");
1179
+ }
1180
+ const dir = pathArg !== undefined
1181
+ ? normalizeNonBlank(pathArg, "path")
1182
+ : opts.dir !== undefined
1183
+ ? normalizeNonBlank(opts.dir, "--dir")
1184
+ : undefined;
1185
+ if (opts.release !== undefined)
1186
+ parseReleaseVersion(opts.release, "--release");
1187
+ if (opts.includeSecrets && opts.env !== "remote") {
1188
+ throw new Error("--include-secrets requires --env remote.");
1189
+ }
1190
+ validateAgentSelectorOptions({ agent: opts.agent, project: opts.project });
1191
+ await devCmd({
1192
+ dir,
1193
+ message: opts.message,
1194
+ user: opts.user,
1195
+ verbose: opts.verbose,
1196
+ port: opts.port ? parsePort(opts.port) : undefined,
1197
+ release: opts.release,
1198
+ envRemote: opts.env === "remote",
1199
+ includeSecrets: opts.includeSecrets,
1200
+ project: opts.agent ?? opts.project,
1201
+ });
1202
+ });
1203
+ registerArtifactCommands(program);
1204
+ const sessionsOpts = (cmd) => cmd
1205
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
1206
+ .option("-p, --project <slug>", "Alias of --agent")
1207
+ .option("--dir <dir>", "Agent directory (default: current directory)");
1208
+ const sessionsListOpts = (cmd) => sessionsOpts(cmd)
1209
+ .option("--limit <n>", "Maximum rows to display");
1210
+ const sessionListSelector = async (cmd, slugArg) => {
1211
+ const opts = commandOpts(cmd);
1212
+ validateSessionsListOptions(opts);
1213
+ return resolveAgentOptions(opts, slugArg);
1214
+ };
1215
+ const sessionsListAction = async (cmd, slugArg) => {
1216
+ const opts = await sessionListSelector(cmd, slugArg);
1217
+ const ctx = await buildContext();
1218
+ if (isJson(cmd))
1219
+ printJson(await sessionsListData(ctx, opts));
1220
+ else
1221
+ console.log(await sessionsList(ctx, opts));
1222
+ };
1223
+ const sessionsShowAction = async (id, cmd, slugArg) => {
1224
+ normalizeNonBlank(id, "Session id");
1225
+ const opts = await resolveAgentOptions(commandOpts(cmd), slugArg);
1226
+ const ctx = await buildContext();
1227
+ if (isJson(cmd))
1228
+ printJson(await sessionsShowData(ctx, id, opts));
1229
+ else
1230
+ console.log(await sessionsShow(ctx, id, opts));
1231
+ };
1232
+ const sessions = program
1233
+ .command("sessions")
1234
+ .description("List transcript sessions for a deployed agent; use `show`/`view` for transcripts.")
1235
+ .action(async (_opts, cmd) => sessionsListAction(cmd));
1236
+ sessionsListOpts(sessions);
1237
+ sessionsOpts(sessions
1238
+ .command("show <id>")
1239
+ .alias("view")
1240
+ .description("Render a session transcript (requires the org's transcript-access setting)."))
1241
+ .action(async (id, _opts, cmd) => sessionsShowAction(id, cmd));
1242
+ sessionsOpts(program
1243
+ .command("session <id>")
1244
+ .description("Render a session transcript; alias of `dharta sessions view`."))
1245
+ .action(async (id, _opts, cmd) => sessionsShowAction(id, cmd));
1246
+ program
1247
+ .command("ps")
1248
+ .description("Sessions active right now: count, age, model, tokens.")
1249
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)").option("-p, --project <slug>", "Alias of --agent")
1250
+ .option("--dir <dir>", "Agent directory (default: current directory)")
1251
+ .option("--limit <n>", "Maximum rows to display")
1252
+ .action(async (_opts, cmd) => {
1253
+ const opts = commandOpts(cmd);
1254
+ validateSessionsListOptions(opts);
1255
+ const resolved = await resolveAgentOptions(opts);
1256
+ const ctx = await buildContext();
1257
+ if (isJson(cmd))
1258
+ printJson(await psData(ctx, resolved));
1259
+ else
1260
+ console.log(await psCmd(ctx, resolved));
1261
+ });
1262
+ const files = program
1263
+ .command("files")
1264
+ .description("Read a karta's durable workspace files (owner channel, RFC 0060).")
1265
+ .action(helpOnlyAction("dharta files"));
1266
+ files
1267
+ .command("ls <agent> <karta>")
1268
+ .description("List a karta's durable workspace files (path and size).")
1269
+ .action(async (agent, karta, _opts, cmd) => {
1270
+ const ctx = await buildContext();
1271
+ if (isJson(cmd))
1272
+ printJson(await filesLsData(ctx, agent, karta));
1273
+ else
1274
+ console.log(await filesLs(ctx, agent, karta));
1275
+ });
1276
+ files
1277
+ .command("get <agent> <karta> <path>")
1278
+ .description("Fetch one workspace file; writes to --out, or streams bytes to stdout.")
1279
+ .option("-o, --out <file>", "Write the file's bytes to this path instead of stdout")
1280
+ .action(async (agent, karta, path, opts, cmd) => {
1281
+ // Raw bytes, not a single JSON payload — --json has no meaning here.
1282
+ rejectJson(cmd, "dharta files get");
1283
+ const out = opts.out !== undefined ? normalizeNonBlank(opts.out, "--out") : undefined;
1284
+ const ctx = await buildContext();
1285
+ const msg = await filesGet(ctx, agent, karta, path, out);
1286
+ if (msg)
1287
+ console.log(msg);
1288
+ });
1289
+ program
1290
+ .command("send <message>")
1291
+ .alias("run")
1292
+ .description("Send a message to a DEPLOYED agent and print its reply (prod smoke test).")
1293
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
1294
+ .option("-p, --project <slug>", "Alias of --agent")
1295
+ .option("--dir <dir>", "Directory to resolve the agent from (default: current directory)")
1296
+ .option("--session-id <id>", "Continue an existing session instead of opening a new one")
1297
+ .action(async (message, _opts, cmd) => {
1298
+ const opts = commandOpts(cmd);
1299
+ normalizeNonBlank(message, "message");
1300
+ if (opts.sessionId !== undefined)
1301
+ normalizeNonBlank(opts.sessionId, "Session id");
1302
+ const resolved = await resolveAgentOptions(opts);
1303
+ const ctx = await buildContext();
1304
+ const result = await runCmd(ctx, message, { ...resolved, json: isJson(cmd) });
1305
+ if (isJson(cmd))
1306
+ printJson(result);
1307
+ });
1308
+ const env = program
1309
+ .command("env")
1310
+ .description("Manage the agent's environment bindings (declare-or-bind).")
1311
+ .action(helpOnlyAction("dharta env"));
1312
+ const projectOpts = (cmd) => cmd
1313
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)").option("-p, --project <slug>", "Alias of --agent")
1314
+ .option("--dir <dir>", "Agent directory (default: current directory)");
1315
+ projectOpts(env.command("list"))
1316
+ .description("List prod env bindings (values masked; --reveal to show).")
1317
+ .option("--reveal", "Print full values instead of masking them")
1318
+ .action(async (_opts, cmd) => {
1319
+ const opts = commandOpts(cmd);
1320
+ const resolved = await resolveAgentOptions(opts);
1321
+ const ctx = await buildContext();
1322
+ // --json returns raw values (machine-readable, opt-in); the human table
1323
+ // masks by default so a routine `env list` never leaks a token.
1324
+ if (isJson(cmd))
1325
+ printJson(await envListData(ctx, resolved));
1326
+ else
1327
+ console.log(await envList(ctx, { ...resolved, reveal: opts.reveal === true }));
1328
+ });
1329
+ projectOpts(env.command("set <assignment>"))
1330
+ .description("Bind NAME=value in prod (--secret excludes it from pull).")
1331
+ .option("--secret", "Mark the value secret (cannot be unmarked without deleting)")
1332
+ .action(async (assignment, _opts, cmd) => {
1333
+ const opts = commandOpts(cmd);
1334
+ validateEnvAssignment(assignment);
1335
+ const resolved = await resolveAgentOptions(opts);
1336
+ const ctx = await buildContext();
1337
+ if (isJson(cmd))
1338
+ printJson(await envSetData(ctx, assignment, resolved));
1339
+ else
1340
+ console.log(await envSet(ctx, assignment, resolved));
1341
+ });
1342
+ projectOpts(env.command("unset <name>"))
1343
+ .description("Remove a prod env binding.")
1344
+ .action(async (name, _opts, cmd) => {
1345
+ const opts = commandOpts(cmd);
1346
+ validateEnvName(name);
1347
+ const resolved = await resolveAgentOptions(opts);
1348
+ const ctx = await buildContext();
1349
+ if (isJson(cmd))
1350
+ printJson(await envUnsetData(ctx, name, resolved));
1351
+ else
1352
+ console.log(await envUnset(ctx, name, resolved));
1353
+ });
1354
+ projectOpts(env.command("pull"))
1355
+ .description("Hydrate .env from prod values (audited; secrets excluded by default).")
1356
+ .option("--force", "Overwrite an existing .env")
1357
+ .option("--include-secrets", "Also fetch secret-marked values (requires env:secrets:read; audited)")
1358
+ .action(async (_opts, cmd) => {
1359
+ const opts = commandOpts(cmd);
1360
+ await validateEnvPullOptions(opts);
1361
+ const resolved = await resolveAgentOptions(opts);
1362
+ const ctx = await buildContext();
1363
+ if (isJson(cmd))
1364
+ printJson(await envPullData(ctx, resolved));
1365
+ else
1366
+ console.log(await envPull(ctx, resolved));
1367
+ });
1368
+ projectOpts(env.command("diff"))
1369
+ .description("Report drift between .env, prod bindings, and dharta.toml [env].")
1370
+ .action(async (_opts, cmd) => {
1371
+ const opts = commandOpts(cmd);
1372
+ await validateEnvDiffOptions(opts);
1373
+ const resolved = await resolveAgentOptions(opts);
1374
+ const ctx = await buildContext();
1375
+ if (isJson(cmd))
1376
+ printJson(await envDiffData(ctx, resolved));
1377
+ else
1378
+ console.log(await envDiff(ctx, resolved));
1379
+ });
1380
+ program
1381
+ .command("update")
1382
+ .description("Self-update (standalone installs; npm installs update via npm).")
1383
+ .action(async (_opts, cmd) => {
1384
+ if (isJson(cmd))
1385
+ printJson({ message: await update({ log: logToStderr }) });
1386
+ else
1387
+ console.log(await update());
1388
+ });
1389
+ program
1390
+ .command("doctor")
1391
+ .description("Diagnose the local setup: PATH shadowing, runtime bootstrap, login state.")
1392
+ .action(async (_opts, cmd) => {
1393
+ const report = await doctor();
1394
+ if (isJson(cmd))
1395
+ printJson({ ok: report.ok, checks: report.lines });
1396
+ else
1397
+ console.log(report.lines.join("\n"));
1398
+ if (!report.ok)
1399
+ process.exitCode = 1;
1400
+ });
1401
+ program
1402
+ .command("usage")
1403
+ .description("Show usage totals and budget for the current period.")
1404
+ .action(async (_opts, cmd) => {
1405
+ const ctx = await buildContext();
1406
+ if (isJson(cmd))
1407
+ printJson(await usageData(ctx));
1408
+ else
1409
+ console.log(await usageCmd(ctx));
1410
+ });
1411
+ const agent = program.command("agent").description("Inspect and manage your agents.").action(helpOnlyAction("dharta agent"));
1412
+ agent
1413
+ .command("list")
1414
+ .description("List all agents in your org.")
1415
+ .action(async (_opts, cmd) => {
1416
+ const ctx = await buildContext();
1417
+ if (isJson(cmd))
1418
+ printJson(await agentsListData(ctx));
1419
+ else
1420
+ console.log(await agentsList(ctx));
1421
+ });
1422
+ agent
1423
+ .command("show [slug]")
1424
+ .description("Show an agent + its recent releases (the agent in this folder if no slug given).")
1425
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
1426
+ .option("-p, --project <slug>", "Alias of --agent")
1427
+ .option("--dir <dir>", "Directory to resolve the agent from (default: current directory)")
1428
+ .action(async (slug, _opts, cmd) => {
1429
+ const opts = commandOpts(cmd);
1430
+ const resolvedOpts = await resolveAgentOptions(opts, slug);
1431
+ const ctx = await buildContext();
1432
+ const resolved = resolvedOpts.agent;
1433
+ if (isJson(cmd))
1434
+ printJson(await agentsShowData(ctx, resolved));
1435
+ else
1436
+ console.log(await agentsShow(ctx, resolved));
1437
+ });
1438
+ const agentSessions = agent
1439
+ .command("sessions [slug]")
1440
+ .description("List transcript sessions for an agent; use `view <id>` for transcripts.")
1441
+ .action(async (slug, _opts, cmd) => sessionsListAction(cmd, slug));
1442
+ sessionsListOpts(agentSessions);
1443
+ sessionsOpts(agentSessions
1444
+ .command("show <id> [slug]")
1445
+ .alias("view")
1446
+ .description("Render a session transcript; alias of `dharta sessions show`."))
1447
+ .action(async (id, slug, _opts, cmd) => sessionsShowAction(id, cmd, slug));
1448
+ sessionsOpts(agent
1449
+ .command("session <id> [slug]")
1450
+ .description("Render a session transcript; alias of `dharta sessions view`."))
1451
+ .action(async (id, slug, _opts, cmd) => sessionsShowAction(id, cmd, slug));
1452
+ agent
1453
+ .command("releases [slug]")
1454
+ .description("List an agent's releases.")
1455
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
1456
+ .option("-p, --project <slug>", "Alias of --agent")
1457
+ .option("--dir <dir>", "Directory to resolve the agent from (default: current directory)")
1458
+ .action(async (slug, _opts, cmd) => {
1459
+ const opts = commandOpts(cmd);
1460
+ const resolvedOpts = await resolveAgentOptions(opts, slug);
1461
+ const ctx = await buildContext();
1462
+ const resolved = resolvedOpts.agent;
1463
+ if (isJson(cmd))
1464
+ printJson(await agentsReleasesData(ctx, resolved));
1465
+ else
1466
+ console.log(await agentsReleases(ctx, resolved));
1467
+ });
1468
+ agent
1469
+ .command("activate <version> [slug]")
1470
+ .description("Activate an existing production release.")
1471
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
1472
+ .option("-p, --project <slug>", "Alias of --agent")
1473
+ .option("--dir <dir>", "Directory to resolve the agent from (default: current directory)")
1474
+ .action(async (version, slug, _opts, cmd) => {
1475
+ const opts = commandOpts(cmd);
1476
+ parseVersion(version);
1477
+ const resolvedOpts = await resolveAgentOptions(opts, slug);
1478
+ const ctx = await buildContext();
1479
+ const resolved = resolvedOpts.agent;
1480
+ if (isJson(cmd))
1481
+ printJson(await agentsActivateReleaseData(ctx, resolved, version));
1482
+ else
1483
+ console.log(await agentsActivateRelease(ctx, resolved, version));
1484
+ });
1485
+ agent
1486
+ .command("serving <state> [slug]")
1487
+ .description(`Resume or pause serving for an agent. ${STATE_HELP}`)
1488
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
1489
+ .option("-p, --project <slug>", "Alias of --agent")
1490
+ .option("--dir <dir>", "Directory to resolve the agent from (default: current directory)")
1491
+ .action(async (state, slug, _opts, cmd) => {
1492
+ const opts = commandOpts(cmd);
1493
+ const enabled = parseOnOff(state, "state");
1494
+ const resolvedOpts = await resolveAgentOptions(opts, slug);
1495
+ const ctx = await buildContext();
1496
+ const resolved = resolvedOpts.agent;
1497
+ if (isJson(cmd))
1498
+ printJson(await agentsServingSetData(ctx, resolved, enabled));
1499
+ else
1500
+ console.log(await agentsServingSet(ctx, resolved, enabled));
1501
+ });
1502
+ agent
1503
+ .command("dashboard-edits <state> [slug]")
1504
+ .description(`Enable or disable browser-console edits for an agent. ${STATE_HELP}`)
1505
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
1506
+ .option("-p, --project <slug>", "Alias of --agent")
1507
+ .option("--dir <dir>", "Directory to resolve the agent from (default: current directory)")
1508
+ .action(async (state, slug, _opts, cmd) => {
1509
+ const opts = commandOpts(cmd);
1510
+ const enabled = parseOnOff(state, "state");
1511
+ const resolvedOpts = await resolveAgentOptions(opts, slug);
1512
+ const ctx = await buildContext();
1513
+ const resolved = resolvedOpts.agent;
1514
+ if (isJson(cmd))
1515
+ printJson(await agentsDashboardEditsSetData(ctx, resolved, enabled));
1516
+ else
1517
+ console.log(await agentsDashboardEditsSet(ctx, resolved, enabled));
1518
+ });
1519
+ agent
1520
+ .command("model [slug]")
1521
+ .description("Set an agent model override.")
1522
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
1523
+ .option("-p, --project <slug>", "Alias of --agent")
1524
+ .option("--dir <dir>", "Directory to resolve the agent from (default: current directory)")
1525
+ .addOption(choiceOption("--source <source>", "inherit or platform; omit when using --connector-id", AGENT_MODEL_SOURCES))
1526
+ .option("--connector-id <id>", "Numeric BYOK connector id from `dharta model-keys list`; omit --source")
1527
+ .option("--model <model>", "Provider model id")
1528
+ .option("--temperature <number>", "Sampling temperature from 0 to 2")
1529
+ .option("--clear-temperature", "Inherit temperature")
1530
+ .option("--top-p <number>", "Nucleus sampling probability above 0 and at most 1")
1531
+ .option("--clear-top-p", "Inherit top-p")
1532
+ .option("--max-output-tokens <number>", "Positive output token limit")
1533
+ .option("--clear-max-output-tokens", "Inherit the output token limit")
1534
+ .addOption(choiceOption("--reasoning-effort <level>", "Reasoning effort", ["low", "medium", "high"]))
1535
+ .option("--clear-reasoning-effort", "Inherit reasoning effort")
1536
+ .action(async (slug, _opts, cmd) => {
1537
+ const opts = commandOpts(cmd);
1538
+ validateAgentModelSetOptions(opts);
1539
+ const resolvedOpts = await resolveAgentOptions(opts, slug);
1540
+ const ctx = await buildContext();
1541
+ const resolved = resolvedOpts.agent;
1542
+ if (isJson(cmd))
1543
+ printJson(await agentsModelSetData(ctx, resolved, opts));
1544
+ else
1545
+ console.log(await agentsModelSet(ctx, resolved, opts));
1546
+ });
1547
+ agent
1548
+ .command("workspace-inspectable <state> [slug]")
1549
+ .description(`Enable or disable durable workspace inspection for an agent. ${STATE_HELP}`)
1550
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
1551
+ .option("-p, --project <slug>", "Alias of --agent")
1552
+ .option("--dir <dir>", "Agent directory (default: current directory)")
1553
+ .action(async (state, slugArg, _opts, cmd) => {
1554
+ const opts = commandOpts(cmd);
1555
+ const enabled = parseOnOff(state, "state");
1556
+ const resolvedOpts = await resolveAgentOptions(opts, slugArg);
1557
+ const ctx = await buildContext();
1558
+ const resolved = resolvedOpts.agent;
1559
+ if (isJson(cmd))
1560
+ printJson(await agentsWorkspaceInspectableSetData(ctx, resolved, enabled));
1561
+ else
1562
+ console.log(await agentsWorkspaceInspectableSet(ctx, resolved, enabled));
1563
+ });
1564
+ const workspaceAccess = agent
1565
+ .command("workspace-access")
1566
+ .description("Show or set an agent's workspace access grants (RFC 0060).");
1567
+ workspaceAccess
1568
+ .command("show [slug]")
1569
+ .description("Show the audience x channel workspace access grant matrix.")
1570
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
1571
+ .option("-p, --project <slug>", "Alias of --agent")
1572
+ .option("--dir <dir>", "Agent directory (default: current directory)")
1573
+ .action(async (slugArg, _opts, cmd) => {
1574
+ const opts = commandOpts(cmd);
1575
+ const resolvedOpts = await resolveAgentOptions(opts, slugArg);
1576
+ const ctx = await buildContext();
1577
+ const resolved = resolvedOpts.agent;
1578
+ if (isJson(cmd))
1579
+ printJson(await agentsWorkspaceAccessShowData(ctx, resolved));
1580
+ else
1581
+ console.log(await agentsWorkspaceAccessShow(ctx, resolved));
1582
+ });
1583
+ workspaceAccess
1584
+ .command("set <grant> [slug]")
1585
+ .description("Grant or revoke one access path (<path>=<on|off>). Paths: owners.git, owners.api, users.widget, users.agent_definition. e.g. dharta agent workspace-access set users.widget=on")
1586
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
1587
+ .option("-p, --project <slug>", "Alias of --agent")
1588
+ .option("--dir <dir>", "Agent directory (default: current directory)")
1589
+ .action(async (grant, slugArg, _opts, cmd) => {
1590
+ const opts = commandOpts(cmd);
1591
+ const patch = parseWorkspaceAccessAssignments([grant]);
1592
+ const resolvedOpts = await resolveAgentOptions(opts, slugArg);
1593
+ const ctx = await buildContext();
1594
+ const resolved = resolvedOpts.agent;
1595
+ if (isJson(cmd))
1596
+ printJson(await agentsWorkspaceAccessSetData(ctx, resolved, patch));
1597
+ else
1598
+ console.log(await agentsWorkspaceAccessSet(ctx, resolved, patch));
1599
+ });
1600
+ agent
1601
+ .command("delete [slug]")
1602
+ .description("Archive an agent.")
1603
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)")
1604
+ .option("-p, --project <slug>", "Alias of --agent")
1605
+ .option("--dir <dir>", "Agent directory (default: current directory)")
1606
+ .action(async (slugArg, _opts, cmd) => {
1607
+ const opts = commandOpts(cmd);
1608
+ const resolvedOpts = await resolveAgentOptions(opts, slugArg);
1609
+ const ctx = await buildContext();
1610
+ const resolved = resolvedOpts.agent;
1611
+ if (isJson(cmd))
1612
+ printJson(await agentsDeleteData(ctx, resolved));
1613
+ else
1614
+ console.log(await agentsDelete(ctx, resolved));
1615
+ });
1616
+ program
1617
+ .command("deploy [slug]")
1618
+ .description("Deploy exactly one agent: the dharta.toml at/above --dir (default: cwd). Its deploy_method selects git or folder; --mode is a one-run override.")
1619
+ .option("--dir <dir>", "Agent directory, or a subdirectory inside it (default: cwd)")
1620
+ .option("--reconcile-source", "git only: intentionally align Dharta's hosted source branch to local HEAD after inspecting divergent history")
1621
+ .addOption(choiceOption("--mode <mode>", "one-run override: git (agent-targeted push) or folder (selected-folder upload). Default: deploy_method in dharta.toml.", ["git", "folder"]))
1622
+ .action(async (slug, opts, cmd) => {
1623
+ const rawMode = opts.mode;
1624
+ if (slug !== undefined)
1625
+ validateAgentSlug(slug, "agent slug");
1626
+ const dir = normalizeNonBlank(opts.dir ?? ".", "--dir");
1627
+ validateDeployDir(dir);
1628
+ // Resolve the effective mode here only to enforce the JSON contract.
1629
+ // deploy() receives only an explicit override so it remains the one
1630
+ // owner of target/config resolution and can narrate legacy inference.
1631
+ const mode = rawMode ?? (await inferMode(dir));
1632
+ if (opts.reconcileSource && mode !== "git") {
1633
+ throw new Error("--reconcile-source is only valid for git deploys. " +
1634
+ "Remove it, or use `--mode git` to reconcile Dharta's hosted source branch.");
1635
+ }
1636
+ // git mode's output is the live push sideband, not a payload.
1637
+ const json = mode === "folder" && isJson(cmd);
1638
+ if (mode === "git" && isJson(cmd)) {
1639
+ throw new Error("--json is only supported for `dharta deploy --mode folder`; git deploy streams git push output. Run `dharta deploy` without --json or pass `--mode folder`.");
1640
+ }
1641
+ const ctx = await buildContext();
1642
+ const result = await deploy(ctx, slug, {
1643
+ dir,
1644
+ mode: rawMode,
1645
+ reconcileSource: opts.reconcileSource,
1646
+ log: json ? logToStderr : undefined,
1647
+ });
1648
+ if (json)
1649
+ printJson(result);
1650
+ });
1651
+ program
1652
+ .command("status")
1653
+ .description("Show every agent in this repo (resolved from the git root) and its live release on the active profile.")
1654
+ .option("--dir <dir>", "Directory to scan (default: cwd; the whole repo when inside one)")
1655
+ .addOption(new Option("-a, --agent <slug>").hideHelp())
1656
+ .addOption(new Option("-p, --project <slug>").hideHelp())
1657
+ .action(async (opts, cmd) => {
1658
+ if (opts.agent !== undefined || opts.project !== undefined) {
1659
+ const slug = opts.agent ?? opts.project ?? "";
1660
+ if (slug.trim())
1661
+ validateAgentSlug(slug, opts.agent !== undefined ? "--agent" : "--project");
1662
+ throw new Error("`dharta status` is repo-wide. Use `dharta agent show <slug>` for one remote agent, or `dharta status --dir <path>` for a local repo.");
1663
+ }
1664
+ validateAgentSelectorOptions(opts);
1665
+ let ctx = null;
1666
+ let authMessage;
1667
+ try {
1668
+ ctx = await buildContext();
1669
+ }
1670
+ catch (err) {
1671
+ if (!isNotLoggedInError(err))
1672
+ throw err;
1673
+ authMessage = err instanceof Error ? err.message : String(err);
1674
+ }
1675
+ if (isJson(cmd))
1676
+ printJson(await statusData(ctx, { dir: opts.dir, authMessage }));
1677
+ else
1678
+ console.log(await status(ctx, { dir: opts.dir, authMessage }));
1679
+ });
1680
+ program
1681
+ .command("rollback [slug]")
1682
+ .description("Roll back to a previous release (requires releases:write).")
1683
+ .option("--to <version>", "target release, e.g. v3 or 3")
1684
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)").option("-p, --project <slug>", "Alias of --agent")
1685
+ .option("--dir <dir>", "Agent directory (default: current directory)")
1686
+ .action(async (slug, _opts, cmd) => {
1687
+ const opts = commandOpts(cmd);
1688
+ const v = parseRollbackTarget(opts.to);
1689
+ const resolvedOpts = await resolveAgentOptions(opts, slug);
1690
+ const ctx = await buildContext();
1691
+ const resolved = resolvedOpts.agent;
1692
+ if (isJson(cmd))
1693
+ printJson(await rollbackData(ctx, resolved, v));
1694
+ else
1695
+ console.log(await rollback(ctx, resolved, v));
1696
+ });
1697
+ program
1698
+ .command("logs [slug]")
1699
+ .description("Stream or poll agent logs (slug inferred from this folder if omitted).")
1700
+ .option("--tail", "Stream logs as they arrive (SSE if available)")
1701
+ .option("--since <iso>", "Only show logs after this timestamp (polling fallback)")
1702
+ .option("-a, --agent <slug>", "Agent slug (default: the agent in this folder)").option("-p, --project <slug>", "Alias of --agent")
1703
+ .option("--dir <dir>", "Agent directory (default: current directory)")
1704
+ .action(async (slug, _opts, cmd) => {
1705
+ const opts = commandOpts(cmd);
1706
+ rejectJson(cmd, "dharta logs");
1707
+ validateLogsOptions(opts);
1708
+ const resolvedOpts = await resolveAgentOptions(opts, slug);
1709
+ const ctx = await buildContext();
1710
+ const resolved = resolvedOpts.agent;
1711
+ const emitted = await logsTail(ctx, resolved, { tail: opts.tail, since: opts.since }, (line) => process.stdout.write(line + "\n"));
1712
+ if (!opts.tail && emitted === 0) {
1713
+ const window = opts.since?.trim() ? `since ${opts.since.trim()}` : "in the last minute";
1714
+ console.log(`No log entries for ${resolved} ${window}.`);
1715
+ }
1716
+ });
1717
+ program
1718
+ .command("completion")
1719
+ .description("Print a shell completion script (pipe it into your shell's setup).")
1720
+ .addArgument(choiceArgument("<shell>", "target shell", ["bash", "zsh"]))
1721
+ .action((shell, _opts, cmd) => {
1722
+ rejectJson(cmd, "dharta completion");
1723
+ console.log(shell === "zsh" ? zshCompletion(program) : bashCompletion(program));
1724
+ });
1725
+ // RFC 0013 §3: --json on every command. Registered on every node of
1726
+ // the command tree so the outermost scan consumes the flag and
1727
+ // isJson() reads it back through optsWithGlobals(). Stream/protocol
1728
+ // commands and help-only command groups keep a hidden parser option
1729
+ // because they reject --json.
1730
+ for (const cmd of allCommands(program)) {
1731
+ if (hidesJsonHelp(cmd)) {
1732
+ cmd.addOption(new Option("--json", "Print the result as JSON (machine-readable)").hideHelp());
1733
+ }
1734
+ else {
1735
+ cmd.option("--json", jsonHelpDescription(cmd));
1736
+ }
1737
+ }
1738
+ installMissingArgumentChoiceHints(program);
1739
+ installTopLevelUnknownCommandGuard(program);
1740
+ return program;
1741
+ }
1742
+ function isNotLoggedInError(err) {
1743
+ return err instanceof Error && /^Not logged in\./.test(err.message);
1744
+ }
1745
+ export async function main(argv) {
1746
+ await buildProgram().parseAsync(argv);
1747
+ }
1748
+ function allCommands(root) {
1749
+ const out = [];
1750
+ const visit = (cmd) => {
1751
+ out.push(cmd);
1752
+ for (const sub of cmd.commands)
1753
+ visit(sub);
1754
+ };
1755
+ visit(root);
1756
+ return out;
1757
+ }
1758
+ function hidesJsonHelp(cmd) {
1759
+ return [
1760
+ "dharta",
1761
+ "dharta auth",
1762
+ "dharta analytics",
1763
+ "dharta keys",
1764
+ "dharta model-keys",
1765
+ "dharta members",
1766
+ "dharta embed-keys",
1767
+ "dharta caps",
1768
+ "dharta appearance",
1769
+ "dharta schedules",
1770
+ "dharta directories",
1771
+ "dharta webhooks",
1772
+ "dharta env",
1773
+ "dharta agent",
1774
+ "dharta git-credential",
1775
+ "dharta dev",
1776
+ "dharta logs",
1777
+ "dharta completion",
1778
+ ].includes(commandPath(cmd));
1779
+ }
1780
+ function jsonHelpDescription(cmd) {
1781
+ if (commandPath(cmd) === "dharta deploy") {
1782
+ return "Print JSON for --mode folder uploads; git mode streams git push output";
1783
+ }
1784
+ return "Print the result as JSON (machine-readable)";
1785
+ }
1786
+ function installTopLevelUnknownCommandGuard(program) {
1787
+ const originalParseAsync = program.parseAsync.bind(program);
1788
+ program.parseAsync = async (argv, options) => {
1789
+ rejectUnknownTopLevelCommand(program, argv, options);
1790
+ return originalParseAsync(argv, options);
1791
+ };
1792
+ const originalParse = program.parse.bind(program);
1793
+ program.parse = (argv, options) => {
1794
+ rejectUnknownTopLevelCommand(program, argv, options);
1795
+ return originalParse(argv, options);
1796
+ };
1797
+ }
1798
+ const HELP_ONLY_COMMAND_PATHS = new Set([
1799
+ "dharta",
1800
+ "dharta auth",
1801
+ "dharta analytics",
1802
+ "dharta keys",
1803
+ "dharta model-keys",
1804
+ "dharta members",
1805
+ "dharta embed-keys",
1806
+ "dharta caps",
1807
+ "dharta caps budget",
1808
+ "dharta appearance",
1809
+ "dharta schedules",
1810
+ "dharta directories",
1811
+ "dharta webhooks",
1812
+ "dharta env",
1813
+ "dharta agent",
1814
+ ]);
1815
+ function rejectUnknownTopLevelCommand(program, argv, options) {
1816
+ const args = argv ? [...argv].slice(options?.from === "user" ? 0 : 2) : process.argv.slice(2);
1817
+ let cursor = program;
1818
+ for (let index = 0; index < args.length; index += 1) {
1819
+ const token = args[index] ?? "";
1820
+ if (token === "--")
1821
+ return;
1822
+ if (token.startsWith("-")) {
1823
+ const option = commandOption(cursor, token);
1824
+ if (!option)
1825
+ return;
1826
+ if (!token.includes("=") && optionTakesValue(option))
1827
+ index += 1;
1828
+ continue;
1829
+ }
1830
+ const path = commandPath(cursor);
1831
+ if (!HELP_ONLY_COMMAND_PATHS.has(path))
1832
+ return;
1833
+ const visible = cursor.commands.filter((cmd) => !cmd._hidden);
1834
+ const child = visible.find((cmd) => cmd.name() === token || cmd.aliases().includes(token));
1835
+ if (child) {
1836
+ cursor = child;
1837
+ continue;
1838
+ }
1839
+ const suggestion = closestCommand(token, commandSuggestionTokens(cursor));
1840
+ cursor.error(`error: unknown command '${token}'${path === "dharta" ? "" : ` for ${path}`}. ` +
1841
+ `Run \`${path} --help\` for available commands.` +
1842
+ (suggestion ? ` Did you mean \`${suggestion}\`?` : ""));
1843
+ }
1844
+ }
1845
+ function commandOption(cmd, token) {
1846
+ const flag = token.includes("=") ? token.slice(0, token.indexOf("=")) : token;
1847
+ return cmd.options.find((option) => option.long === flag || option.short === flag);
1848
+ }
1849
+ function optionTakesValue(option) {
1850
+ return Boolean(option.required ||
1851
+ option.optional);
1852
+ }
1853
+ function collect(value, previous) {
1854
+ previous.push(value);
1855
+ return previous;
1856
+ }
1857
+ function installMissingArgumentChoiceHints(root) {
1858
+ for (const cmd of allCommands(root)) {
1859
+ const missingArgument = cmd.missingArgument;
1860
+ if (!missingArgument)
1861
+ continue;
1862
+ cmd.missingArgument = (name) => {
1863
+ const arg = cmd.registeredArguments.find((candidate) => candidate.name() === name);
1864
+ const choices = arg?.argChoices ?? [];
1865
+ const choicesHint = choices.length > 0 ? ` Allowed choices are ${choices.join(", ")}.` : "";
1866
+ cmd.error(`error: missing required argument '${name}'.${choicesHint} Usage: ${formatCommandUsage(cmd)}`, { code: "commander.missingArgument" });
1867
+ };
1868
+ }
1869
+ }
1870
+ function formatCommandUsage(cmd) {
1871
+ return `${commandPath(cmd)} ${cmd.usage()}`.trim();
1872
+ }
1873
+ function choiceOption(flags, description, allowed) {
1874
+ return new Option(flags, description).choices([...allowed]).argParser(choiceValue(allowed));
1875
+ }
1876
+ function choiceListOption(flags, description, allowed) {
1877
+ let sawExplicitValue = false;
1878
+ const parseChoice = choiceValue(allowed);
1879
+ return new Option(flags, description)
1880
+ .choices([...allowed])
1881
+ .argParser((value, previous = []) => {
1882
+ const explicitValues = sawExplicitValue ? previous : [];
1883
+ sawExplicitValue = true;
1884
+ const values = value.split(/[,\s]+/).map((part) => part.trim()).filter(Boolean);
1885
+ if (values.length === 0)
1886
+ throw new InvalidArgumentError(`Allowed choices are ${allowed.join(", ")}.`);
1887
+ return [...explicitValues, ...values.map(parseChoice)];
1888
+ });
1889
+ }
1890
+ function choiceArgument(name, description, allowed) {
1891
+ return new Argument(name, description).choices([...allowed]).argParser(choiceValue(allowed));
1892
+ }
1893
+ function choiceValue(allowed) {
1894
+ return (value) => {
1895
+ const normalized = value.trim();
1896
+ if (allowed.includes(normalized))
1897
+ return normalized;
1898
+ const suggestion = closestChoice(normalized, allowed);
1899
+ throw new InvalidArgumentError(`Allowed choices are ${allowed.join(", ")}.` +
1900
+ (suggestion ? ` Did you mean ${suggestion}?` : ""));
1901
+ };
1902
+ }
1903
+ function parseOnOff(value, name) {
1904
+ const normalized = value.trim().toLowerCase();
1905
+ if (["on", "true", "enabled", "enable", "resume", "resumed"].includes(normalized))
1906
+ return true;
1907
+ if (["off", "false", "disabled", "disable", "pause", "paused"].includes(normalized))
1908
+ return false;
1909
+ const suggestion = closestChoice(normalized, ["on", "off", "enabled", "disabled", "true", "false"]);
1910
+ throw new Error(`${name} must be on/off, enabled/disabled, or true/false.` +
1911
+ (suggestion ? ` Did you mean ${suggestion}?` : ""));
1912
+ }
1913
+ export async function readStdin() {
1914
+ if (process.stdin.isTTY) {
1915
+ // Reading a TTY would block silently until Ctrl-D — fail with the
1916
+ // usage instead (gh parity: --with-token requires piped input).
1917
+ throw new Error("No piped input. Usage: echo $DHARTA_API_KEY | dharta login --with-token");
1918
+ }
1919
+ const chunks = [];
1920
+ for await (const chunk of process.stdin)
1921
+ chunks.push(Buffer.from(chunk));
1922
+ const token = Buffer.concat(chunks).toString("utf8").trim();
1923
+ if (!token) {
1924
+ throw new Error("No token on stdin. Usage: echo $DHARTA_API_KEY | dharta login --with-token");
1925
+ }
1926
+ return token;
1927
+ }
1928
+ function parseVersion(input) {
1929
+ normalizeNonBlank(input, "version");
1930
+ try {
1931
+ return parseReleaseVersion(input, "version");
1932
+ }
1933
+ catch {
1934
+ throw new Error(`Invalid version: ${input.trim()}. Use e.g. v3 or 3.`);
1935
+ }
1936
+ }
1937
+ function parseRollbackTarget(input) {
1938
+ if (input === undefined) {
1939
+ throw new Error("Pass --to <version>. Usage: dharta rollback [slug] --to v3");
1940
+ }
1941
+ return parseVersion(input);
1942
+ }
1943
+ function parsePort(input) {
1944
+ const normalized = input.trim();
1945
+ if (!normalized)
1946
+ throw new Error("--port cannot be blank.");
1947
+ if (!/^[1-9]\d*$/.test(normalized)) {
1948
+ throw new Error(`--port must be an integer between 1 and 65535.`);
1949
+ }
1950
+ const port = Number(normalized);
1951
+ if (port > 65535) {
1952
+ throw new Error(`--port must be an integer between 1 and 65535.`);
1953
+ }
1954
+ return port;
1955
+ }
1956
+ // Only run when invoked as a bin — vitest imports this module without
1957
+ // driving it. argv[1] is realpath'd because the installed bin is a
1958
+ // SYMLINK (npm link / npm i -g puts <prefix>/bin/dharta -> dist/cli.js):
1959
+ // a filename regex on argv[1] never matches the symlink name, so the
1960
+ // CLI would exit 0 silently for every global invocation.
1961
+ const invokedAsBin = (() => {
1962
+ if (typeof process === "undefined" || !process.argv[1])
1963
+ return false;
1964
+ try {
1965
+ return realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
1966
+ }
1967
+ catch {
1968
+ return false;
1969
+ }
1970
+ })();
1971
+ if (invokedAsBin) {
1972
+ main(process.argv).catch((err) => {
1973
+ const msg = err instanceof Error ? err.message : String(err);
1974
+ process.stderr.write(`dharta: ${msg}\n`);
1975
+ process.exit(1);
1976
+ });
1977
+ }
1978
+ //# sourceMappingURL=cli.js.map