@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.
- package/LICENSE +21 -0
- package/README.md +167 -0
- package/dist/agent.d.ts +17 -0
- package/dist/agent.js +134 -0
- package/dist/agent.js.map +1 -0
- package/dist/api-error.d.ts +3 -0
- package/dist/api-error.js +80 -0
- package/dist/api-error.js.map +1 -0
- package/dist/browser.d.ts +1 -0
- package/dist/browser.js +19 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +1978 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/agents.d.ts +131 -0
- package/dist/commands/agents.js +439 -0
- package/dist/commands/agents.js.map +1 -0
- package/dist/commands/analytics.d.ts +25 -0
- package/dist/commands/analytics.js +166 -0
- package/dist/commands/analytics.js.map +1 -0
- package/dist/commands/api.d.ts +19 -0
- package/dist/commands/api.js +278 -0
- package/dist/commands/api.js.map +1 -0
- package/dist/commands/appearance.d.ts +19 -0
- package/dist/commands/appearance.js +63 -0
- package/dist/commands/appearance.js.map +1 -0
- package/dist/commands/artifact.d.ts +33 -0
- package/dist/commands/artifact.js +235 -0
- package/dist/commands/artifact.js.map +1 -0
- package/dist/commands/audit-log.d.ts +32 -0
- package/dist/commands/audit-log.js +102 -0
- package/dist/commands/audit-log.js.map +1 -0
- package/dist/commands/auth.d.ts +47 -0
- package/dist/commands/auth.js +117 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/caps.d.ts +82 -0
- package/dist/commands/caps.js +213 -0
- package/dist/commands/caps.js.map +1 -0
- package/dist/commands/create.d.ts +22 -0
- package/dist/commands/create.js +76 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/deploy.d.ts +45 -0
- package/dist/commands/deploy.js +892 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/dev.d.ts +33 -0
- package/dist/commands/dev.js +655 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/directories.d.ts +60 -0
- package/dist/commands/directories.js +83 -0
- package/dist/commands/directories.js.map +1 -0
- package/dist/commands/doctor.d.ts +13 -0
- package/dist/commands/doctor.js +123 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/embed-keys.d.ts +61 -0
- package/dist/commands/embed-keys.js +165 -0
- package/dist/commands/embed-keys.js.map +1 -0
- package/dist/commands/env.d.ts +59 -0
- package/dist/commands/env.js +300 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/files.d.ts +14 -0
- package/dist/commands/files.js +84 -0
- package/dist/commands/files.js.map +1 -0
- package/dist/commands/git-credential.d.ts +1 -0
- package/dist/commands/git-credential.js +70 -0
- package/dist/commands/git-credential.js.map +1 -0
- package/dist/commands/kartas.d.ts +101 -0
- package/dist/commands/kartas.js +289 -0
- package/dist/commands/kartas.js.map +1 -0
- package/dist/commands/keys.d.ts +33 -0
- package/dist/commands/keys.js +141 -0
- package/dist/commands/keys.js.map +1 -0
- package/dist/commands/login.d.ts +22 -0
- package/dist/commands/login.js +78 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logs.d.ts +14 -0
- package/dist/commands/logs.js +89 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/members.d.ts +32 -0
- package/dist/commands/members.js +77 -0
- package/dist/commands/members.js.map +1 -0
- package/dist/commands/model-keys.d.ts +22 -0
- package/dist/commands/model-keys.js +222 -0
- package/dist/commands/model-keys.js.map +1 -0
- package/dist/commands/open.d.ts +29 -0
- package/dist/commands/open.js +119 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/parity.d.ts +15 -0
- package/dist/commands/parity.js +73 -0
- package/dist/commands/parity.js.map +1 -0
- package/dist/commands/rollback.d.ts +21 -0
- package/dist/commands/rollback.js +26 -0
- package/dist/commands/rollback.js.map +1 -0
- package/dist/commands/schedules.d.ts +52 -0
- package/dist/commands/schedules.js +231 -0
- package/dist/commands/schedules.js.map +1 -0
- package/dist/commands/scopes.d.ts +3 -0
- package/dist/commands/scopes.js +21 -0
- package/dist/commands/scopes.js.map +1 -0
- package/dist/commands/sessions.d.ts +53 -0
- package/dist/commands/sessions.js +142 -0
- package/dist/commands/sessions.js.map +1 -0
- package/dist/commands/setup.d.ts +28 -0
- package/dist/commands/setup.js +949 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/status.d.ts +19 -0
- package/dist/commands/status.js +63 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/update.d.ts +14 -0
- package/dist/commands/update.js +120 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/usage.d.ts +3 -0
- package/dist/commands/usage.js +37 -0
- package/dist/commands/usage.js.map +1 -0
- package/dist/commands/webhooks.d.ts +47 -0
- package/dist/commands/webhooks.js +156 -0
- package/dist/commands/webhooks.js.map +1 -0
- package/dist/commands/whoami.d.ts +7 -0
- package/dist/commands/whoami.js +17 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/completions.d.ts +13 -0
- package/dist/completions.js +120 -0
- package/dist/completions.js.map +1 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.js +98 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +14 -0
- package/dist/context.js +38 -0
- package/dist/context.js.map +1 -0
- package/dist/device-flow.d.ts +9 -0
- package/dist/device-flow.js +127 -0
- package/dist/device-flow.js.map +1 -0
- package/dist/harness-preflight.d.ts +24 -0
- package/dist/harness-preflight.js +252 -0
- package/dist/harness-preflight.js.map +1 -0
- package/dist/harnesses.d.ts +11 -0
- package/dist/harnesses.js +112 -0
- package/dist/harnesses.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/login-offer.d.ts +9 -0
- package/dist/login-offer.js +36 -0
- package/dist/login-offer.js.map +1 -0
- package/dist/manifest.d.ts +15 -0
- package/dist/manifest.js +142 -0
- package/dist/manifest.js.map +1 -0
- package/dist/money.d.ts +1 -0
- package/dist/money.js +16 -0
- package/dist/money.js.map +1 -0
- package/dist/output.d.ts +2 -0
- package/dist/output.js +20 -0
- package/dist/output.js.map +1 -0
- package/dist/runtime.d.ts +19 -0
- package/dist/runtime.js +95 -0
- package/dist/runtime.js.map +1 -0
- package/dist/scaffold.d.ts +16 -0
- package/dist/scaffold.js +74 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/setup-scan.d.ts +21 -0
- package/dist/setup-scan.js +127 -0
- package/dist/setup-scan.js.map +1 -0
- package/dist/templates.d.ts +14 -0
- package/dist/templates.js +65 -0
- package/dist/templates.js.map +1 -0
- package/dist/validators.d.ts +10 -0
- package/dist/validators.js +116 -0
- package/dist/validators.js.map +1 -0
- package/dist/version-check.d.ts +25 -0
- package/dist/version-check.js +68 -0
- package/dist/version-check.js.map +1 -0
- package/node_modules/@dharta/sdk/LICENSE +21 -0
- package/node_modules/@dharta/sdk/README.md +87 -0
- package/node_modules/@dharta/sdk/dist/errors.d.ts +20 -0
- package/node_modules/@dharta/sdk/dist/errors.d.ts.map +1 -0
- package/node_modules/@dharta/sdk/dist/errors.js +51 -0
- package/node_modules/@dharta/sdk/dist/errors.js.map +1 -0
- package/node_modules/@dharta/sdk/dist/http.d.ts +26 -0
- package/node_modules/@dharta/sdk/dist/http.d.ts.map +1 -0
- package/node_modules/@dharta/sdk/dist/http.js +136 -0
- package/node_modules/@dharta/sdk/dist/http.js.map +1 -0
- package/node_modules/@dharta/sdk/dist/index.d.ts +15 -0
- package/node_modules/@dharta/sdk/dist/index.d.ts.map +1 -0
- package/node_modules/@dharta/sdk/dist/index.js +21 -0
- package/node_modules/@dharta/sdk/dist/index.js.map +1 -0
- package/node_modules/@dharta/sdk/dist/resources.d.ts +48 -0
- package/node_modules/@dharta/sdk/dist/resources.d.ts.map +1 -0
- package/node_modules/@dharta/sdk/dist/resources.js +150 -0
- package/node_modules/@dharta/sdk/dist/resources.js.map +1 -0
- package/node_modules/@dharta/sdk/dist/types.d.ts +94 -0
- package/node_modules/@dharta/sdk/dist/types.d.ts.map +1 -0
- package/node_modules/@dharta/sdk/dist/types.js +59 -0
- package/node_modules/@dharta/sdk/dist/types.js.map +1 -0
- package/node_modules/@dharta/sdk/package.json +32 -0
- package/node_modules/@dharta/sdk/src/errors.ts +58 -0
- package/node_modules/@dharta/sdk/src/http.ts +166 -0
- package/node_modules/@dharta/sdk/src/index.ts +36 -0
- package/node_modules/@dharta/sdk/src/resources.ts +197 -0
- package/node_modules/@dharta/sdk/src/types.ts +179 -0
- package/package.json +38 -0
- package/src/agent.ts +147 -0
- package/src/api-error.ts +88 -0
- package/src/browser.ts +20 -0
- package/src/cli.ts +2339 -0
- package/src/commands/agents.ts +635 -0
- package/src/commands/analytics.ts +181 -0
- package/src/commands/api.ts +297 -0
- package/src/commands/appearance.ts +79 -0
- package/src/commands/artifact.ts +331 -0
- package/src/commands/audit-log.ts +125 -0
- package/src/commands/auth.ts +186 -0
- package/src/commands/caps.ts +302 -0
- package/src/commands/create.ts +114 -0
- package/src/commands/deploy.ts +1122 -0
- package/src/commands/dev.ts +792 -0
- package/src/commands/directories.ts +136 -0
- package/src/commands/doctor.ts +138 -0
- package/src/commands/embed-keys.ts +237 -0
- package/src/commands/env.ts +369 -0
- package/src/commands/files.ts +124 -0
- package/src/commands/git-credential.ts +78 -0
- package/src/commands/kartas.ts +439 -0
- package/src/commands/keys.ts +206 -0
- package/src/commands/login.ts +115 -0
- package/src/commands/logs.ts +111 -0
- package/src/commands/members.ts +109 -0
- package/src/commands/model-keys.ts +278 -0
- package/src/commands/open.ts +173 -0
- package/src/commands/parity.ts +96 -0
- package/src/commands/rollback.ts +45 -0
- package/src/commands/schedules.ts +290 -0
- package/src/commands/scopes.ts +22 -0
- package/src/commands/sessions.ts +225 -0
- package/src/commands/setup.ts +1176 -0
- package/src/commands/status.ts +94 -0
- package/src/commands/update.ts +157 -0
- package/src/commands/usage.ts +41 -0
- package/src/commands/webhooks.ts +200 -0
- package/src/commands/whoami.ts +24 -0
- package/src/completions.ts +168 -0
- package/src/config.ts +143 -0
- package/src/context.ts +59 -0
- package/src/device-flow.ts +174 -0
- package/src/harness-preflight.ts +293 -0
- package/src/harnesses.ts +117 -0
- package/src/index.ts +3 -0
- package/src/login-offer.ts +49 -0
- package/src/manifest.ts +153 -0
- package/src/money.ts +15 -0
- package/src/output.ts +22 -0
- package/src/runtime.ts +127 -0
- package/src/scaffold.ts +102 -0
- package/src/setup-scan.ts +154 -0
- package/src/templates.ts +85 -0
- package/src/validators.ts +124 -0
- package/src/version-check.ts +75 -0
- package/templates/blank/CLAUDE.md +21 -0
- package/templates/blank/dharta.toml +11 -0
- package/templates/faq-agent/CLAUDE.md +51 -0
- 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
|