@dharta/cli 0.6.77 → 0.6.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +215 -11
- package/dist/agent.js +3 -2
- package/dist/agent.js.map +1 -1
- package/dist/api-error.js +6 -6
- package/dist/api-error.js.map +1 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +860 -149
- package/dist/cli.js.map +1 -1
- package/dist/command-tree.d.ts +3 -0
- package/dist/command-tree.js +10 -0
- package/dist/command-tree.js.map +1 -0
- package/dist/commands/agents.d.ts +54 -17
- package/dist/commands/agents.js +246 -136
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/analytics.js +6 -5
- package/dist/commands/analytics.js.map +1 -1
- package/dist/commands/api.js +71 -86
- package/dist/commands/api.js.map +1 -1
- package/dist/commands/appearance.js +5 -6
- package/dist/commands/appearance.js.map +1 -1
- package/dist/commands/artifact.js +7 -6
- package/dist/commands/artifact.js.map +1 -1
- package/dist/commands/audit-log.js +2 -1
- package/dist/commands/audit-log.js.map +1 -1
- package/dist/commands/auth.js +26 -6
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/caps.js +11 -15
- package/dist/commands/caps.js.map +1 -1
- package/dist/commands/deploy.js +25 -10
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts +10 -1
- package/dist/commands/dev.js +56 -25
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +60 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/embed-keys.js +4 -13
- package/dist/commands/embed-keys.js.map +1 -1
- package/dist/commands/env.js +4 -7
- package/dist/commands/env.js.map +1 -1
- package/dist/commands/files.js +3 -4
- package/dist/commands/files.js.map +1 -1
- package/dist/commands/fleet.d.ts +129 -0
- package/dist/commands/fleet.js +408 -0
- package/dist/commands/fleet.js.map +1 -0
- package/dist/commands/genui.d.ts +2 -0
- package/dist/commands/genui.js +53 -0
- package/dist/commands/genui.js.map +1 -0
- package/dist/commands/git-credential.js +2 -3
- package/dist/commands/git-credential.js.map +1 -1
- package/dist/commands/kartas.d.ts +9 -0
- package/dist/commands/kartas.js +33 -11
- package/dist/commands/kartas.js.map +1 -1
- package/dist/commands/keys.d.ts +17 -18
- package/dist/commands/keys.js +108 -19
- package/dist/commands/keys.js.map +1 -1
- package/dist/commands/login.js +2 -1
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/members.d.ts +10 -0
- package/dist/commands/members.js +20 -6
- package/dist/commands/members.js.map +1 -1
- package/dist/commands/model-connections.d.ts +44 -0
- package/dist/commands/model-connections.js +435 -0
- package/dist/commands/model-connections.js.map +1 -0
- package/dist/commands/open.d.ts +2 -2
- package/dist/commands/open.js +4 -3
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/org.d.ts +9 -0
- package/dist/commands/org.js +42 -0
- package/dist/commands/org.js.map +1 -0
- package/dist/commands/organization.d.ts +16 -0
- package/dist/commands/organization.js +118 -0
- package/dist/commands/organization.js.map +1 -0
- package/dist/commands/parity.js +1 -3
- package/dist/commands/parity.js.map +1 -1
- package/dist/commands/repos.d.ts +4 -0
- package/dist/commands/repos.js +17 -0
- package/dist/commands/repos.js.map +1 -0
- package/dist/commands/rollback.js +2 -3
- package/dist/commands/rollback.js.map +1 -1
- package/dist/commands/schedules.js +26 -21
- package/dist/commands/schedules.js.map +1 -1
- package/dist/commands/scopes.js +8 -1
- package/dist/commands/scopes.js.map +1 -1
- package/dist/commands/sessions.js +9 -3
- package/dist/commands/sessions.js.map +1 -1
- package/dist/commands/setup.js +5 -4
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/update.js +24 -11
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/webhooks.d.ts +4 -1
- package/dist/commands/webhooks.js +21 -16
- package/dist/commands/webhooks.js.map +1 -1
- package/dist/completions.js +24 -13
- package/dist/completions.js.map +1 -1
- package/dist/config.d.ts +12 -0
- package/dist/config.js +121 -7
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +2 -0
- package/dist/context.js +23 -7
- package/dist/context.js.map +1 -1
- package/dist/docs/cli-reference.d.ts +2 -0
- package/dist/docs/cli-reference.js +191 -0
- package/dist/docs/cli-reference.js.map +1 -0
- package/dist/exit-codes.d.ts +25 -0
- package/dist/exit-codes.js +107 -0
- package/dist/exit-codes.js.map +1 -0
- package/dist/genui.d.ts +178 -0
- package/dist/genui.js +1130 -0
- package/dist/genui.js.map +1 -0
- package/dist/harnesses.js +2 -1
- package/dist/harnesses.js.map +1 -1
- package/dist/jq.d.ts +4 -0
- package/dist/jq.js +91 -0
- package/dist/jq.js.map +1 -0
- package/dist/login-offer.js +6 -2
- package/dist/login-offer.js.map +1 -1
- package/dist/manifest.d.ts +12 -0
- package/dist/manifest.js +126 -0
- package/dist/manifest.js.map +1 -1
- package/dist/model-auth.d.ts +29 -0
- package/dist/model-auth.js +117 -0
- package/dist/model-auth.js.map +1 -0
- package/dist/model-settings.d.ts +23 -0
- package/dist/model-settings.js +186 -0
- package/dist/model-settings.js.map +1 -0
- package/dist/money.js +2 -1
- package/dist/money.js.map +1 -1
- package/dist/output.d.ts +6 -0
- package/dist/output.js +54 -0
- package/dist/output.js.map +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +1 -1
- package/dist/scaffold.js +2 -1
- package/dist/scaffold.js.map +1 -1
- package/dist/validators.js +17 -11
- package/dist/validators.js.map +1 -1
- package/dist/version-check.d.ts +1 -1
- package/dist/version-check.js +1 -1
- package/dist/version-check.js.map +1 -1
- package/node_modules/@dharta/contracts/LICENSE +21 -0
- package/node_modules/@dharta/contracts/README.md +45 -0
- package/node_modules/@dharta/contracts/artifact-runtime.json +750 -0
- package/node_modules/@dharta/contracts/dist/activity.d.ts +62 -0
- package/node_modules/@dharta/contracts/dist/activity.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/activity.js +178 -0
- package/node_modules/@dharta/contracts/dist/activity.js.map +1 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime-formats.d.ts +20 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime-formats.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime-formats.js +53 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime-formats.js.map +1 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime-validators.generated.d.ts +37 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime-validators.generated.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime-validators.generated.js +5107 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime-validators.generated.js.map +1 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime.d.ts +154 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime.generated.d.ts +1670 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime.generated.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime.generated.js +1903 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime.generated.js.map +1 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime.js +377 -0
- package/node_modules/@dharta/contracts/dist/artifact-runtime.js.map +1 -0
- package/node_modules/@dharta/contracts/dist/index.d.ts +6 -0
- package/node_modules/@dharta/contracts/dist/index.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/index.js +6 -0
- package/node_modules/@dharta/contracts/dist/index.js.map +1 -0
- package/node_modules/@dharta/contracts/dist/model-auth.d.ts +31 -0
- package/node_modules/@dharta/contracts/dist/model-auth.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/model-auth.js +45 -0
- package/node_modules/@dharta/contracts/dist/model-auth.js.map +1 -0
- package/node_modules/@dharta/contracts/dist/tasks.d.ts +44 -0
- package/node_modules/@dharta/contracts/dist/tasks.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/tasks.js +127 -0
- package/node_modules/@dharta/contracts/dist/tasks.js.map +1 -0
- package/node_modules/@dharta/contracts/dist/wire-events.d.ts +71 -0
- package/node_modules/@dharta/contracts/dist/wire-events.d.ts.map +1 -0
- package/node_modules/@dharta/contracts/dist/wire-events.js +48 -0
- package/node_modules/@dharta/contracts/dist/wire-events.js.map +1 -0
- package/node_modules/@dharta/contracts/model-auth-capabilities-v1.json +168 -0
- package/node_modules/@dharta/contracts/model-auth-reasons-v1.json +38 -0
- package/node_modules/@dharta/contracts/package.json +44 -0
- package/node_modules/@dharta/contracts/src/activity.ts +215 -0
- package/node_modules/@dharta/contracts/src/artifact-runtime-formats.ts +54 -0
- package/node_modules/@dharta/contracts/src/artifact-runtime-validators.generated.ts +29 -0
- package/node_modules/@dharta/contracts/src/artifact-runtime.generated.ts +2093 -0
- package/node_modules/@dharta/contracts/src/artifact-runtime.ts +520 -0
- package/node_modules/@dharta/contracts/src/index.ts +5 -0
- package/node_modules/@dharta/contracts/src/model-auth.ts +82 -0
- package/node_modules/@dharta/contracts/src/tasks.ts +142 -0
- package/node_modules/@dharta/contracts/src/wire-events.ts +150 -0
- package/node_modules/@dharta/contracts/wire-events.json +29 -0
- package/node_modules/@dharta/sdk/LICENSE +1 -1
- package/node_modules/@dharta/sdk/README.md +22 -8
- package/node_modules/@dharta/sdk/dist/errors.d.ts +9 -7
- package/node_modules/@dharta/sdk/dist/errors.d.ts.map +1 -1
- package/node_modules/@dharta/sdk/dist/errors.js +36 -18
- package/node_modules/@dharta/sdk/dist/errors.js.map +1 -1
- package/node_modules/@dharta/sdk/dist/fleet.d.ts +69 -0
- package/node_modules/@dharta/sdk/dist/fleet.d.ts.map +1 -0
- package/node_modules/@dharta/sdk/dist/fleet.js +240 -0
- package/node_modules/@dharta/sdk/dist/fleet.js.map +1 -0
- package/node_modules/@dharta/sdk/dist/http.d.ts +6 -2
- package/node_modules/@dharta/sdk/dist/http.d.ts.map +1 -1
- package/node_modules/@dharta/sdk/dist/http.js +71 -12
- package/node_modules/@dharta/sdk/dist/http.js.map +1 -1
- package/node_modules/@dharta/sdk/dist/idempotency.d.ts +12 -0
- package/node_modules/@dharta/sdk/dist/idempotency.d.ts.map +1 -0
- package/node_modules/@dharta/sdk/dist/idempotency.js +72 -0
- package/node_modules/@dharta/sdk/dist/idempotency.js.map +1 -0
- package/node_modules/@dharta/sdk/dist/index.d.ts +20 -6
- package/node_modules/@dharta/sdk/dist/index.d.ts.map +1 -1
- package/node_modules/@dharta/sdk/dist/index.js +27 -4
- package/node_modules/@dharta/sdk/dist/index.js.map +1 -1
- package/node_modules/@dharta/sdk/dist/resources.d.ts +108 -13
- package/node_modules/@dharta/sdk/dist/resources.d.ts.map +1 -1
- package/node_modules/@dharta/sdk/dist/resources.js +403 -57
- package/node_modules/@dharta/sdk/dist/resources.js.map +1 -1
- package/node_modules/@dharta/sdk/dist/types.d.ts +436 -16
- package/node_modules/@dharta/sdk/dist/types.d.ts.map +1 -1
- package/node_modules/@dharta/sdk/dist/types.js +31 -2
- package/node_modules/@dharta/sdk/dist/types.js.map +1 -1
- package/node_modules/@dharta/sdk/package.json +9 -3
- package/node_modules/@dharta/sdk/src/errors.ts +41 -18
- package/node_modules/@dharta/sdk/src/fleet.ts +339 -0
- package/node_modules/@dharta/sdk/src/http.ts +110 -17
- package/node_modules/@dharta/sdk/src/idempotency.ts +101 -0
- package/node_modules/@dharta/sdk/src/index.ts +50 -11
- package/node_modules/@dharta/sdk/src/resources.ts +549 -68
- package/node_modules/@dharta/sdk/src/types.ts +629 -20
- package/package.json +16 -6
- package/src/agent.ts +3 -2
- package/src/api-error.ts +6 -5
- package/src/cli.ts +978 -163
- package/src/command-tree.ts +13 -0
- package/src/commands/agents.ts +365 -167
- package/src/commands/analytics.ts +6 -5
- package/src/commands/api.ts +73 -92
- package/src/commands/appearance.ts +5 -6
- package/src/commands/artifact.ts +7 -6
- package/src/commands/audit-log.ts +2 -1
- package/src/commands/auth.ts +25 -5
- package/src/commands/caps.ts +11 -15
- package/src/commands/deploy.ts +18 -4
- package/src/commands/dev.ts +86 -25
- package/src/commands/doctor.ts +69 -0
- package/src/commands/embed-keys.ts +4 -14
- package/src/commands/env.ts +4 -7
- package/src/commands/files.ts +3 -4
- package/src/commands/fleet.ts +672 -0
- package/src/commands/genui.ts +59 -0
- package/src/commands/git-credential.ts +2 -3
- package/src/commands/kartas.ts +45 -11
- package/src/commands/keys.ts +152 -35
- package/src/commands/login.ts +2 -1
- package/src/commands/members.ts +31 -6
- package/src/commands/model-connections.ts +547 -0
- package/src/commands/open.ts +5 -4
- package/src/commands/org.ts +57 -0
- package/src/commands/organization.ts +153 -0
- package/src/commands/parity.ts +1 -3
- package/src/commands/repos.ts +23 -0
- package/src/commands/rollback.ts +2 -3
- package/src/commands/schedules.ts +33 -22
- package/src/commands/scopes.ts +8 -1
- package/src/commands/sessions.ts +9 -3
- package/src/commands/setup.ts +5 -4
- package/src/commands/update.ts +28 -12
- package/src/commands/webhooks.ts +30 -19
- package/src/completions.ts +25 -13
- package/src/config.ts +141 -9
- package/src/context.ts +34 -7
- package/src/docs/cli-reference.ts +206 -0
- package/src/exit-codes.ts +122 -0
- package/src/genui.ts +1398 -0
- package/src/harnesses.ts +2 -1
- package/src/jq.ts +109 -0
- package/src/login-offer.ts +6 -2
- package/src/manifest.ts +134 -0
- package/src/model-auth.ts +173 -0
- package/src/model-settings.ts +222 -0
- package/src/money.ts +5 -1
- package/src/output.ts +60 -0
- package/src/runtime.ts +1 -1
- package/src/scaffold.ts +2 -1
- package/src/validators.ts +18 -11
- package/src/version-check.ts +1 -1
- package/dist/commands/model-keys.d.ts +0 -22
- package/dist/commands/model-keys.js +0 -222
- package/dist/commands/model-keys.js.map +0 -1
- package/src/commands/model-keys.ts +0 -278
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
// RFC 0070 model-selection inputs shared by `dharta agent model` and
|
|
2
|
+
// `dharta org model set`. Both commands accept the same connector order,
|
|
3
|
+
// model reference, and portable parameter flags, so the parsing, the
|
|
4
|
+
// validation, and the request patch live here once.
|
|
5
|
+
//
|
|
6
|
+
// Like validators.ts, everything here rejects a value the user typed, so every
|
|
7
|
+
// failure is a usage error and exits 2 (see exit-codes.ts).
|
|
8
|
+
|
|
9
|
+
import { CliUsageError } from "./exit-codes.js";
|
|
10
|
+
import { closestChoice, normalizeNonBlank, parsePositiveInteger } from "./validators.js";
|
|
11
|
+
|
|
12
|
+
export const MAX_OUTPUT_TOKENS = 1_000_000;
|
|
13
|
+
export const REASONING_EFFORTS = ["low", "medium", "high"] as const;
|
|
14
|
+
|
|
15
|
+
// The synthetic platform connector. It is orderable like a stored connector
|
|
16
|
+
// but has no credential of its own.
|
|
17
|
+
export const PLATFORM_CONNECTOR_SLUG = "karta";
|
|
18
|
+
// At most this many connectors in one order, matching the server.
|
|
19
|
+
const MAX_CONNECTOR_ORDER = 32;
|
|
20
|
+
// A connector slug is a lowercase DNS label of at most 63 characters.
|
|
21
|
+
const CONNECTOR_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
22
|
+
// `connector:` and `route:` introduce a model reference, so neither word can
|
|
23
|
+
// also name a connector.
|
|
24
|
+
const RESERVED_CONNECTOR_SLUGS = ["connector", "route"] as const;
|
|
25
|
+
|
|
26
|
+
export interface ModelParameterOptions {
|
|
27
|
+
temperature?: string;
|
|
28
|
+
clearTemperature?: boolean;
|
|
29
|
+
topP?: string;
|
|
30
|
+
clearTopP?: boolean;
|
|
31
|
+
maxOutputTokens?: string;
|
|
32
|
+
clearMaxOutputTokens?: boolean;
|
|
33
|
+
reasoningEffort?: string;
|
|
34
|
+
clearReasoningEffort?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Validate a slug that names a stored connector, as `--slug` does on create.
|
|
38
|
+
// The platform slug is rejected here: it is orderable, not storable.
|
|
39
|
+
export function normalizeStoredConnectorSlug(value: string, flag: string): string {
|
|
40
|
+
const normalized = normalizeConnectorSlug(value, flag);
|
|
41
|
+
if (normalized === PLATFORM_CONNECTOR_SLUG) {
|
|
42
|
+
throw new CliUsageError(`${flag} cannot be ${PLATFORM_CONNECTOR_SLUG}: that slug names the platform connector.`);
|
|
43
|
+
}
|
|
44
|
+
return normalized;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Validate a slug that can appear in a connector order, where the platform
|
|
48
|
+
// slug is allowed.
|
|
49
|
+
export function normalizeConnectorSlug(value: string, flag: string): string {
|
|
50
|
+
const normalized = normalizeNonBlank(value, flag);
|
|
51
|
+
if (!CONNECTOR_SLUG_PATTERN.test(normalized)) {
|
|
52
|
+
throw new CliUsageError(
|
|
53
|
+
`${flag} must be a lowercase DNS label of 1 to 63 characters: letters, numbers, and hyphens, ` +
|
|
54
|
+
"starting and ending with a letter or number, for example openai-primary.",
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
// An all-digit path segment is always read as a connector id, so an
|
|
58
|
+
// all-digit slug could never be addressed by slug. Refuse to mint one.
|
|
59
|
+
if (/^\d+$/.test(normalized)) {
|
|
60
|
+
throw new CliUsageError(
|
|
61
|
+
`${flag} cannot be all digits: an all-digit reference is always read as a connector id.`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
if ((RESERVED_CONNECTOR_SLUGS as readonly string[]).includes(normalized)) {
|
|
65
|
+
throw new CliUsageError(`${flag} cannot be ${normalized}: that word is reserved for model references.`);
|
|
66
|
+
}
|
|
67
|
+
return normalized;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Parse `--connector <slug[,slug...]>` into the ordered slug list the API
|
|
71
|
+
// stores. An empty value produces an empty list, which means "inherit".
|
|
72
|
+
export function parseConnectorOrder(value: string, flag: string): string[] {
|
|
73
|
+
const slugs = value.split(/[\s,]+/).map((part) => part.trim()).filter(Boolean);
|
|
74
|
+
if (slugs.length > MAX_CONNECTOR_ORDER) {
|
|
75
|
+
throw new CliUsageError(`${flag} accepts at most ${MAX_CONNECTOR_ORDER} connectors.`);
|
|
76
|
+
}
|
|
77
|
+
const normalized = slugs.map((slug) => normalizeConnectorSlug(slug, flag));
|
|
78
|
+
const seen = new Set<string>();
|
|
79
|
+
for (const slug of normalized) {
|
|
80
|
+
if (seen.has(slug)) throw new CliUsageError(`${flag} lists ${slug} more than once.`);
|
|
81
|
+
seen.add(slug);
|
|
82
|
+
}
|
|
83
|
+
return normalized;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Normalize `--model-ref <reference>`. The server owns the grammar; the CLI
|
|
87
|
+
// only rejects shapes that can never resolve, so a typo fails before a
|
|
88
|
+
// round trip. An empty value clears the reference.
|
|
89
|
+
//
|
|
90
|
+
// A structured reference is rebuilt from its validated components rather than
|
|
91
|
+
// passed through, so `connector: openai-primary / gpt-5.2` cannot reach the
|
|
92
|
+
// server with the whitespace still embedded in the slug or the model id.
|
|
93
|
+
export function normalizeModelReference(value: string, flag: string): string {
|
|
94
|
+
const normalized = value.trim();
|
|
95
|
+
if (!normalized) return "";
|
|
96
|
+
if (normalized.startsWith("connector:")) {
|
|
97
|
+
const rest = normalized.slice("connector:".length);
|
|
98
|
+
const separator = rest.indexOf("/");
|
|
99
|
+
if (separator === -1) {
|
|
100
|
+
throw new CliUsageError(`${flag} connector reference needs a model: connector:<slug>/<model>.`);
|
|
101
|
+
}
|
|
102
|
+
const slug = normalizeConnectorSlug(rest.slice(0, separator), `${flag} connector slug`);
|
|
103
|
+
const model = rest.slice(separator + 1).trim();
|
|
104
|
+
if (!model) {
|
|
105
|
+
throw new CliUsageError(`${flag} connector reference needs a model: connector:<slug>/<model>.`);
|
|
106
|
+
}
|
|
107
|
+
return `connector:${slug}/${model}`;
|
|
108
|
+
}
|
|
109
|
+
if (normalized.startsWith("route:")) {
|
|
110
|
+
const route = normalized.slice("route:".length).trim();
|
|
111
|
+
if (!route) {
|
|
112
|
+
throw new CliUsageError(`${flag} route reference needs a name: route:<name>.`);
|
|
113
|
+
}
|
|
114
|
+
// A route name uses the same grammar as a connector slug.
|
|
115
|
+
return `route:${normalizeConnectorSlug(route, `${flag} route name`)}`;
|
|
116
|
+
}
|
|
117
|
+
return normalized;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function modelParameterOptionPresent(opts: ModelParameterOptions): boolean {
|
|
121
|
+
return opts.temperature !== undefined || opts.clearTemperature === true ||
|
|
122
|
+
opts.topP !== undefined || opts.clearTopP === true ||
|
|
123
|
+
opts.maxOutputTokens !== undefined || opts.clearMaxOutputTokens === true ||
|
|
124
|
+
opts.reasoningEffort !== undefined || opts.clearReasoningEffort === true;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function validateModelParameterOptions(opts: ModelParameterOptions): void {
|
|
128
|
+
validateParameterPair(opts.temperature, opts.clearTemperature, "--temperature", "--clear-temperature");
|
|
129
|
+
validateParameterPair(opts.topP, opts.clearTopP, "--top-p", "--clear-top-p");
|
|
130
|
+
validateParameterPair(
|
|
131
|
+
opts.maxOutputTokens,
|
|
132
|
+
opts.clearMaxOutputTokens,
|
|
133
|
+
"--max-output-tokens",
|
|
134
|
+
"--clear-max-output-tokens",
|
|
135
|
+
);
|
|
136
|
+
validateParameterPair(
|
|
137
|
+
opts.reasoningEffort,
|
|
138
|
+
opts.clearReasoningEffort,
|
|
139
|
+
"--reasoning-effort",
|
|
140
|
+
"--clear-reasoning-effort",
|
|
141
|
+
);
|
|
142
|
+
if (opts.temperature !== undefined) parseBoundedNumber(opts.temperature, "--temperature", 0, 2, false);
|
|
143
|
+
if (opts.topP !== undefined) parseBoundedNumber(opts.topP, "--top-p", 0, 1, true);
|
|
144
|
+
if (opts.maxOutputTokens !== undefined) {
|
|
145
|
+
const value = parsePositiveInteger(opts.maxOutputTokens, "--max-output-tokens");
|
|
146
|
+
if (value > MAX_OUTPUT_TOKENS) {
|
|
147
|
+
throw new CliUsageError(`--max-output-tokens must be at most ${MAX_OUTPUT_TOKENS}.`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (opts.reasoningEffort !== undefined && !(REASONING_EFFORTS as readonly string[]).includes(opts.reasoningEffort)) {
|
|
151
|
+
const suggestion = closestChoice(opts.reasoningEffort, REASONING_EFFORTS);
|
|
152
|
+
throw new CliUsageError(
|
|
153
|
+
"--reasoning-effort must be low, medium, or high." +
|
|
154
|
+
(suggestion ? ` Did you mean ${suggestion}?` : ""),
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Build the `model_parameters` patch. A null clears one parameter and leaves
|
|
160
|
+
// the rest inherited.
|
|
161
|
+
export function modelParameterPatch(opts: ModelParameterOptions): Record<string, number | string | null> {
|
|
162
|
+
const patch: Record<string, number | string | null> = {};
|
|
163
|
+
if (opts.temperature !== undefined) patch.temperature = Number(opts.temperature);
|
|
164
|
+
if (opts.clearTemperature) patch.temperature = null;
|
|
165
|
+
if (opts.topP !== undefined) patch.top_p = Number(opts.topP);
|
|
166
|
+
if (opts.clearTopP) patch.top_p = null;
|
|
167
|
+
if (opts.maxOutputTokens !== undefined) patch.max_output_tokens = Number(opts.maxOutputTokens);
|
|
168
|
+
if (opts.clearMaxOutputTokens) patch.max_output_tokens = null;
|
|
169
|
+
if (opts.reasoningEffort !== undefined) patch.reasoning_effort = opts.reasoningEffort;
|
|
170
|
+
if (opts.clearReasoningEffort) patch.reasoning_effort = null;
|
|
171
|
+
return patch;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function normalizeModelParameterOptions<T extends ModelParameterOptions>(opts: T): T {
|
|
175
|
+
return {
|
|
176
|
+
...opts,
|
|
177
|
+
temperature: normalizeOptionalFlag(opts.temperature, "--temperature"),
|
|
178
|
+
topP: normalizeOptionalFlag(opts.topP, "--top-p"),
|
|
179
|
+
maxOutputTokens: normalizeOptionalFlag(opts.maxOutputTokens, "--max-output-tokens"),
|
|
180
|
+
reasoningEffort: normalizeOptionalFlag(opts.reasoningEffort, "--reasoning-effort"),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Trim a flag that may be absent. A flag present but blank is an error,
|
|
185
|
+
// except where a command gives the empty string its own meaning.
|
|
186
|
+
export function normalizeOptionalFlag(value: string | undefined, flag: string): string | undefined {
|
|
187
|
+
if (value === undefined) return undefined;
|
|
188
|
+
const normalized = value.trim();
|
|
189
|
+
if (normalized.length === 0) throw new CliUsageError(`${flag} cannot be blank.`);
|
|
190
|
+
return normalized;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function formatModelParameters(parameters: object | undefined): string {
|
|
194
|
+
const entries = Object.entries(parameters ?? {});
|
|
195
|
+
if (entries.length === 0) return "";
|
|
196
|
+
return entries.map(([name, value]) => `${name}=${JSON.stringify(value)}`).join(", ");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function validateParameterPair(
|
|
200
|
+
value: string | undefined,
|
|
201
|
+
clear: boolean | undefined,
|
|
202
|
+
setFlag: string,
|
|
203
|
+
clearFlag: string,
|
|
204
|
+
): void {
|
|
205
|
+
if (value !== undefined && clear) throw new CliUsageError(`Use either ${setFlag} or ${clearFlag}, not both.`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function parseBoundedNumber(
|
|
209
|
+
value: string,
|
|
210
|
+
flag: string,
|
|
211
|
+
minimum: number,
|
|
212
|
+
maximum: number,
|
|
213
|
+
exclusiveMinimum: boolean,
|
|
214
|
+
): number {
|
|
215
|
+
const parsed = Number(value);
|
|
216
|
+
const minimumOk = exclusiveMinimum ? parsed > minimum : parsed >= minimum;
|
|
217
|
+
if (!Number.isFinite(parsed) || !minimumOk || parsed > maximum) {
|
|
218
|
+
const lower = exclusiveMinimum ? `greater than ${minimum}` : `at least ${minimum}`;
|
|
219
|
+
throw new CliUsageError(`${flag} must be a finite number ${lower} and at most ${maximum}.`);
|
|
220
|
+
}
|
|
221
|
+
return parsed;
|
|
222
|
+
}
|
package/src/money.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { CliUsageError } from "./exit-codes.js";
|
|
2
|
+
|
|
1
3
|
export function parseDollarAmountToCents(value: string, flag: string): number {
|
|
2
4
|
const trimmed = value.trim();
|
|
3
5
|
const match = trimmed.match(/^(?:(\d+)(?:\.(\d{1,2}))?|\.(\d{1,2}))$/);
|
|
@@ -11,5 +13,7 @@ export function parseDollarAmountToCents(value: string, flag: string): number {
|
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
function amountError(flag: string): Error {
|
|
14
|
-
return new
|
|
16
|
+
return new CliUsageError(
|
|
17
|
+
`${flag} must be a dollar amount greater than 0 with at most two decimal places.`,
|
|
18
|
+
);
|
|
15
19
|
}
|
package/src/output.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
|
|
3
|
+
import { applyJqFilter, jqExpression } from "./jq.js";
|
|
4
|
+
|
|
1
5
|
// Output helpers. Tiny on purpose — the CLI's UX is "narrate one line
|
|
2
6
|
// per row + occasional table"; pulling in cli-table or chalk costs
|
|
3
7
|
// more than it buys at this scale.
|
|
@@ -20,3 +24,59 @@ export function table(rows: Array<Record<string, string>>): string {
|
|
|
20
24
|
export function fmtTs(ts: string | null | undefined): string {
|
|
21
25
|
return ts ?? "—";
|
|
22
26
|
}
|
|
27
|
+
|
|
28
|
+
// --json output (RFC 0013 §3), and the --jq filter layered on it. These live
|
|
29
|
+
// here rather than in cli.ts because a command module cannot import cli.ts -
|
|
30
|
+
// cli.ts imports the command modules - and `dharta genui` needs them.
|
|
31
|
+
//
|
|
32
|
+
// --json output: every command that produces a result
|
|
33
|
+
// payload prints it as JSON when --json is set. Progress narration
|
|
34
|
+
// (device-flow codes, download logs) moves to stderr so stdout stays
|
|
35
|
+
// parseable. Commands whose stdout is a stream, a REPL, a script, or a
|
|
36
|
+
// protocol rather than a result payload (dev, logs, completion,
|
|
37
|
+
// git-credential, and `deploy --mode git` whose output is the live
|
|
38
|
+
// push sideband) reject --json. `deploy --mode folder` returns a
|
|
39
|
+
// single payload, so it supports --json.
|
|
40
|
+
//
|
|
41
|
+
// Mechanics: the flag is registered on every node of the command tree
|
|
42
|
+
// so commander's
|
|
43
|
+
// outermost option scan consumes it wherever it appears on the line -
|
|
44
|
+
// so actions read it through optsWithGlobals() on their Command. Commands
|
|
45
|
+
// that always reject JSON keep a hidden parser option so help/completions
|
|
46
|
+
// do not advertise an invalid mode.
|
|
47
|
+
export function isJson(cmd: Command): boolean {
|
|
48
|
+
const opts = cmd.optsWithGlobals();
|
|
49
|
+
// --jq implies --json: there is nothing to filter but the JSON payload, and
|
|
50
|
+
// requiring both flags is the papercut `gh --jq` deliberately avoids.
|
|
51
|
+
return Boolean(opts.json) || opts.jq !== undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// The payload a command produced, held until the run finishes. With --jq armed
|
|
55
|
+
// the filter is async and printing has to wait for it; without it the payload
|
|
56
|
+
// is written straight through. Buffering in both cases keeps one code path,
|
|
57
|
+
// and every command produces at most one payload.
|
|
58
|
+
let jsonPayload: { value: unknown } | null = null;
|
|
59
|
+
|
|
60
|
+
export function printJson(payload: unknown): void {
|
|
61
|
+
if (jqExpression() === null) {
|
|
62
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
jsonPayload = { value: payload };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Drop any held payload. cli.ts calls this before each run's action. */
|
|
69
|
+
export function resetJsonPayload(): void {
|
|
70
|
+
jsonPayload = null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function flushJsonPayload(): Promise<void> {
|
|
74
|
+
const held = jsonPayload;
|
|
75
|
+
jsonPayload = null;
|
|
76
|
+
if (held === null) return;
|
|
77
|
+
const filtered = await applyJqFilter(held.value);
|
|
78
|
+
// A filter that matches nothing prints nothing, the way jq and `gh --jq` do.
|
|
79
|
+
// A blank line would look like an empty result to a reader and would be one
|
|
80
|
+
// more line for `wc -l` to count.
|
|
81
|
+
if (filtered !== "") console.log(filtered);
|
|
82
|
+
}
|
package/src/runtime.ts
CHANGED
|
@@ -11,7 +11,7 @@ import * as path from "node:path";
|
|
|
11
11
|
import { spawn } from "node:child_process";
|
|
12
12
|
|
|
13
13
|
// Ships with the CLI release; bump together with dharta-runtime.
|
|
14
|
-
export const DHARTA_RUNTIME_PIN = "0.6.
|
|
14
|
+
export const DHARTA_RUNTIME_PIN = "0.6.78";
|
|
15
15
|
|
|
16
16
|
export const UV_INSTALL_HINT =
|
|
17
17
|
"Install uv first: curl -LsSf https://astral.sh/uv/install.sh | sh (or: brew install uv)\n" +
|
package/src/scaffold.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { existsSync, promises as fs } from "node:fs";
|
|
9
9
|
import * as path from "node:path";
|
|
10
10
|
import { resolveTemplates } from "./templates.js";
|
|
11
|
+
import { CliUsageError } from "./exit-codes.js";
|
|
11
12
|
import { closestChoice, normalizeNonBlank } from "./validators.js";
|
|
12
13
|
import {
|
|
13
14
|
DEFAULT_CREATE_HARNESS,
|
|
@@ -46,7 +47,7 @@ export async function scaffoldTemplate(target: string, opts: ScaffoldOptions): P
|
|
|
46
47
|
if (!template) {
|
|
47
48
|
const keys = resolved.templates.map((t) => t.key);
|
|
48
49
|
const suggestion = closestChoice(templateKey, keys);
|
|
49
|
-
throw new
|
|
50
|
+
throw new CliUsageError(
|
|
50
51
|
`Unknown template '${templateKey}'. Available: ${keys.join(", ")}.` +
|
|
51
52
|
(suggestion ? ` Did you mean ${suggestion}?` : ""),
|
|
52
53
|
);
|
package/src/validators.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
// Shared input validators. Everything here rejects a value the user typed, so
|
|
2
|
+
// every failure is a usage error and exits 2 (see exit-codes.ts). Validators
|
|
3
|
+
// living elsewhere - agent.ts, model-settings.ts, commands/org.ts - follow the
|
|
4
|
+
// same rule: if the value came from the command line, throw CliUsageError.
|
|
5
|
+
// Reserve a plain Error for a failure the user's arguments did not cause.
|
|
6
|
+
import { CliUsageError } from "./exit-codes.js";
|
|
7
|
+
|
|
1
8
|
export function validateDateOnly(value: string, flag: string): void {
|
|
2
9
|
normalizeDateOnly(value, flag);
|
|
3
10
|
}
|
|
@@ -5,7 +12,7 @@ export function validateDateOnly(value: string, flag: string): void {
|
|
|
5
12
|
export function normalizeDateOnly(value: string, flag: string): string {
|
|
6
13
|
const normalized = value.trim();
|
|
7
14
|
if (!isDateOnly(normalized)) {
|
|
8
|
-
throw new
|
|
15
|
+
throw new CliUsageError(`${flag} must use YYYY-MM-DD, e.g. 2026-05-18.`);
|
|
9
16
|
}
|
|
10
17
|
return normalized;
|
|
11
18
|
}
|
|
@@ -29,14 +36,14 @@ export function normalizeIsoTimestamp(value: string, flag: string): string {
|
|
|
29
36
|
|
|
30
37
|
export function parseReleaseVersion(value: string, flag: string): number {
|
|
31
38
|
const normalized = value.trim();
|
|
32
|
-
if (!normalized) throw new
|
|
39
|
+
if (!normalized) throw new CliUsageError(`${flag} cannot be blank.`);
|
|
33
40
|
const stripped = normalized.startsWith("v") ? normalized.slice(1) : normalized;
|
|
34
41
|
if (!/^[1-9]\d*$/.test(stripped)) {
|
|
35
|
-
throw new
|
|
42
|
+
throw new CliUsageError(`${flag} must be a release version like v3 or 3.`);
|
|
36
43
|
}
|
|
37
44
|
const parsed = Number(stripped);
|
|
38
45
|
if (!Number.isSafeInteger(parsed)) {
|
|
39
|
-
throw new
|
|
46
|
+
throw new CliUsageError(`${flag} must be a release version like v3 or 3.`);
|
|
40
47
|
}
|
|
41
48
|
return parsed;
|
|
42
49
|
}
|
|
@@ -44,18 +51,18 @@ export function parseReleaseVersion(value: string, flag: string): number {
|
|
|
44
51
|
export function parsePositiveInteger(value: string, label: string): number {
|
|
45
52
|
const normalized = value.trim();
|
|
46
53
|
if (!/^[1-9]\d*$/.test(normalized)) {
|
|
47
|
-
throw new
|
|
54
|
+
throw new CliUsageError(`${label} must be a positive whole number.`);
|
|
48
55
|
}
|
|
49
56
|
const parsed = Number(normalized);
|
|
50
57
|
if (!Number.isSafeInteger(parsed)) {
|
|
51
|
-
throw new
|
|
58
|
+
throw new CliUsageError(`${label} must be a positive whole number.`);
|
|
52
59
|
}
|
|
53
60
|
return parsed;
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
export function normalizeNonBlank(value: string, label: string): string {
|
|
57
64
|
const normalized = value.trim();
|
|
58
|
-
if (!normalized) throw new
|
|
65
|
+
if (!normalized) throw new CliUsageError(`${label} cannot be blank.`);
|
|
59
66
|
return normalized;
|
|
60
67
|
}
|
|
61
68
|
|
|
@@ -65,10 +72,10 @@ export function normalizeHttpUrl(value: string, flag: string): string {
|
|
|
65
72
|
try {
|
|
66
73
|
parsed = new URL(normalized);
|
|
67
74
|
} catch {
|
|
68
|
-
throw new
|
|
75
|
+
throw new CliUsageError(`${flag} must be an absolute http(s) URL.`);
|
|
69
76
|
}
|
|
70
77
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
71
|
-
throw new
|
|
78
|
+
throw new CliUsageError(`${flag} must be an absolute http(s) URL.`);
|
|
72
79
|
}
|
|
73
80
|
return normalized;
|
|
74
81
|
}
|
|
@@ -78,7 +85,7 @@ export function normalizeTimezone(value: string, flag: string): string {
|
|
|
78
85
|
try {
|
|
79
86
|
Intl.DateTimeFormat("en-US", { timeZone: normalized }).format(new Date());
|
|
80
87
|
} catch {
|
|
81
|
-
throw new
|
|
88
|
+
throw new CliUsageError(`${flag} must be a valid IANA timezone, e.g. UTC or America/Los_Angeles.`);
|
|
82
89
|
}
|
|
83
90
|
return normalized;
|
|
84
91
|
}
|
|
@@ -102,7 +109,7 @@ function isDateOnly(value: string): boolean {
|
|
|
102
109
|
}
|
|
103
110
|
|
|
104
111
|
function isoTimestampError(flag: string): Error {
|
|
105
|
-
return new
|
|
112
|
+
return new CliUsageError(`${flag} must be an ISO timestamp, e.g. 2026-05-18T00:00:00Z.`);
|
|
106
113
|
}
|
|
107
114
|
|
|
108
115
|
function levenshtein(a: string, b: string): number {
|
package/src/version-check.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
|
|
4
|
-
export const NPM_LATEST_URL = "https://registry.npmjs.org/@dharta
|
|
4
|
+
export const NPM_LATEST_URL = "https://registry.npmjs.org/@dharta%2fcli/latest";
|
|
5
5
|
|
|
6
6
|
export interface VersionCheckOptions {
|
|
7
7
|
root: string;
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { CliContext } from "../context.js";
|
|
2
|
-
import type { ModelKey, ModelKeyProvider } from "@dharta/sdk";
|
|
3
|
-
export declare const MODEL_API_FORMATS: readonly ["anthropic_messages", "openai_chat_completions", "openai_responses", "bedrock_converse", "vertex_generate_content"];
|
|
4
|
-
export interface ModelKeyCreateOptions {
|
|
5
|
-
key?: string;
|
|
6
|
-
keyStdin?: boolean;
|
|
7
|
-
authType?: string;
|
|
8
|
-
endpointUrl?: string;
|
|
9
|
-
apiFormat?: string;
|
|
10
|
-
}
|
|
11
|
-
export declare function modelKeysListData(ctx: CliContext): Promise<ModelKey[]>;
|
|
12
|
-
export declare function modelKeysList(ctx: CliContext): Promise<string>;
|
|
13
|
-
export declare function modelKeysCreateData(ctx: CliContext, provider: ModelKeyProvider, name: string, opts?: ModelKeyCreateOptions): Promise<ModelKey>;
|
|
14
|
-
export declare function modelKeysCreate(ctx: CliContext, provider: ModelKeyProvider, name: string, opts?: ModelKeyCreateOptions): Promise<string>;
|
|
15
|
-
export declare function validateModelKeysCreateOptions(provider: ModelKeyProvider, name: string, opts?: ModelKeyCreateOptions): void;
|
|
16
|
-
export declare function prepareModelKeysCreateOptions(provider: ModelKeyProvider, name: string, opts?: ModelKeyCreateOptions, read?: () => Promise<string>): Promise<ModelKeyCreateOptions>;
|
|
17
|
-
export declare function modelKeysRevokeData(ctx: CliContext, id: string): Promise<{
|
|
18
|
-
id: number;
|
|
19
|
-
revoked: boolean;
|
|
20
|
-
}>;
|
|
21
|
-
export declare function modelKeysRevoke(ctx: CliContext, id: string): Promise<string>;
|
|
22
|
-
export declare function validateModelKeyId(id: string): void;
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { fmtTs, table } from "../output.js";
|
|
3
|
-
import { closestChoice, normalizeHttpUrl, normalizeNonBlank, parsePositiveInteger } from "../validators.js";
|
|
4
|
-
const MODEL_KEY_PROVIDERS = ["anthropic", "openai", "bedrock", "vertex", "openrouter", "xai", "google"];
|
|
5
|
-
export const MODEL_API_FORMATS = [
|
|
6
|
-
"anthropic_messages",
|
|
7
|
-
"openai_chat_completions",
|
|
8
|
-
"openai_responses",
|
|
9
|
-
"bedrock_converse",
|
|
10
|
-
"vertex_generate_content",
|
|
11
|
-
];
|
|
12
|
-
const MODEL_KEY_AUTH_TYPES = ["api_key", "oauth_subscription"];
|
|
13
|
-
export async function modelKeysListData(ctx) {
|
|
14
|
-
const res = await ctx.http.request({ path: "/model_keys" });
|
|
15
|
-
return res.data;
|
|
16
|
-
}
|
|
17
|
-
export async function modelKeysList(ctx) {
|
|
18
|
-
const keys = await modelKeysListData(ctx);
|
|
19
|
-
if (keys.length === 0)
|
|
20
|
-
return "No model keys. Store one with: dharta model-keys create <provider> <name> --key-stdin";
|
|
21
|
-
return table(keys.map((key) => ({
|
|
22
|
-
id: String(key.id),
|
|
23
|
-
provider: key.provider,
|
|
24
|
-
name: key.name,
|
|
25
|
-
tail: key.tail,
|
|
26
|
-
created: fmtTs(key.created_at),
|
|
27
|
-
revoked: fmtTs(key.revoked_at),
|
|
28
|
-
})));
|
|
29
|
-
}
|
|
30
|
-
export async function modelKeysCreateData(ctx, provider, name, opts = {}) {
|
|
31
|
-
validateModelKeysCreateOptions(provider, name, opts);
|
|
32
|
-
const normalizedProvider = normalizeProvider(provider);
|
|
33
|
-
const normalizedName = normalizeModelKeyName(name);
|
|
34
|
-
const plaintext = await resolvePlaintext(opts);
|
|
35
|
-
const normalizedAuthType = opts.authType === undefined ? undefined : normalizeAuthType(opts.authType);
|
|
36
|
-
const body = { provider: normalizedProvider, name: normalizedName, plaintext };
|
|
37
|
-
if (normalizedAuthType)
|
|
38
|
-
body.auth_type = normalizedAuthType;
|
|
39
|
-
if (opts.endpointUrl)
|
|
40
|
-
body.endpoint_url = normalizeEndpointUrl(opts.endpointUrl);
|
|
41
|
-
if (opts.apiFormat)
|
|
42
|
-
body.api_format = normalizeApiFormat(opts.apiFormat);
|
|
43
|
-
try {
|
|
44
|
-
const res = await ctx.http.request({
|
|
45
|
-
method: "POST",
|
|
46
|
-
path: "/model_keys",
|
|
47
|
-
body,
|
|
48
|
-
headers: { "Idempotency-Key": randomUUID() },
|
|
49
|
-
});
|
|
50
|
-
return res.model_key;
|
|
51
|
-
}
|
|
52
|
-
catch (error) {
|
|
53
|
-
throw addModelKeysWriteScopeGuidance(error);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
export async function modelKeysCreate(ctx, provider, name, opts = {}) {
|
|
57
|
-
const key = await modelKeysCreateData(ctx, provider, name, opts);
|
|
58
|
-
return `Stored ${key.provider} connector ${key.name} (${key.id}).`;
|
|
59
|
-
}
|
|
60
|
-
export function validateModelKeysCreateOptions(provider, name, opts = {}) {
|
|
61
|
-
const normalizedProvider = normalizeProvider(provider);
|
|
62
|
-
normalizeModelKeyName(name);
|
|
63
|
-
const normalizedAuthType = opts.authType === undefined ? undefined : normalizeAuthType(opts.authType);
|
|
64
|
-
if (normalizedAuthType === "oauth_subscription" && normalizedProvider !== "anthropic") {
|
|
65
|
-
throw new Error("--auth-type oauth_subscription is only supported for the anthropic provider.");
|
|
66
|
-
}
|
|
67
|
-
if (opts.key !== undefined && opts.keyStdin) {
|
|
68
|
-
throw new Error("Use either --key or --key-stdin, not both.");
|
|
69
|
-
}
|
|
70
|
-
if (opts.key !== undefined) {
|
|
71
|
-
normalizePlaintext(opts.key, "--key");
|
|
72
|
-
}
|
|
73
|
-
else if (!opts.keyStdin) {
|
|
74
|
-
throw new Error("Model key plaintext is required. Pass --key or --key-stdin.");
|
|
75
|
-
}
|
|
76
|
-
else if (process.stdin.isTTY) {
|
|
77
|
-
throw new Error("No piped input. Usage: echo $PROVIDER_API_KEY | dharta model-keys create anthropic Claude --key-stdin");
|
|
78
|
-
}
|
|
79
|
-
if (opts.endpointUrl !== undefined) {
|
|
80
|
-
normalizeEndpointUrl(opts.endpointUrl);
|
|
81
|
-
}
|
|
82
|
-
if (opts.apiFormat !== undefined) {
|
|
83
|
-
normalizeApiFormat(opts.apiFormat);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
function normalizeProvider(provider) {
|
|
87
|
-
const normalized = normalizeNonBlank(provider, "Model key provider");
|
|
88
|
-
if (MODEL_KEY_PROVIDERS.includes(normalized)) {
|
|
89
|
-
return normalized;
|
|
90
|
-
}
|
|
91
|
-
const suggestion = closestChoice(normalized, MODEL_KEY_PROVIDERS);
|
|
92
|
-
throw new Error(`Unknown model key provider ${normalized}. Allowed providers: ${MODEL_KEY_PROVIDERS.join(", ")}.` +
|
|
93
|
-
(suggestion ? ` Did you mean ${suggestion}?` : ""));
|
|
94
|
-
}
|
|
95
|
-
function normalizeAuthType(authType) {
|
|
96
|
-
const normalized = normalizeNonBlank(authType, "--auth-type");
|
|
97
|
-
if (MODEL_KEY_AUTH_TYPES.includes(normalized)) {
|
|
98
|
-
return normalized;
|
|
99
|
-
}
|
|
100
|
-
const suggestion = closestChoice(normalized, MODEL_KEY_AUTH_TYPES);
|
|
101
|
-
throw new Error(`Unknown --auth-type ${normalized}. Allowed auth types: ${MODEL_KEY_AUTH_TYPES.join(", ")}.` +
|
|
102
|
-
(suggestion ? ` Did you mean ${suggestion}?` : ""));
|
|
103
|
-
}
|
|
104
|
-
function normalizeApiFormat(apiFormat) {
|
|
105
|
-
const normalized = normalizeNonBlank(apiFormat, "--api-format");
|
|
106
|
-
if (MODEL_API_FORMATS.includes(normalized)) {
|
|
107
|
-
return normalized;
|
|
108
|
-
}
|
|
109
|
-
const suggestion = closestChoice(normalized, MODEL_API_FORMATS);
|
|
110
|
-
throw new Error(`Unknown --api-format ${normalized}. Allowed API formats: ${MODEL_API_FORMATS.join(", ")}.` +
|
|
111
|
-
(suggestion ? ` Did you mean ${suggestion}?` : ""));
|
|
112
|
-
}
|
|
113
|
-
export async function prepareModelKeysCreateOptions(provider, name, opts = {}, read = readModelKeyStdin) {
|
|
114
|
-
validateModelKeysCreateOptions(provider, name, opts);
|
|
115
|
-
if (!opts.keyStdin)
|
|
116
|
-
return opts;
|
|
117
|
-
return { ...opts, key: normalizeModelKeyStdin(await read()), keyStdin: false };
|
|
118
|
-
}
|
|
119
|
-
export async function modelKeysRevokeData(ctx, id) {
|
|
120
|
-
const normalizedId = normalizeNonBlank(id, "Model key id");
|
|
121
|
-
const numericId = parseModelKeyId(normalizedId);
|
|
122
|
-
try {
|
|
123
|
-
await ctx.http.request({
|
|
124
|
-
method: "DELETE",
|
|
125
|
-
path: `/model_keys/${encodeURIComponent(normalizedId)}`,
|
|
126
|
-
headers: { "Idempotency-Key": randomUUID() },
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
throw addModelKeysWriteScopeGuidance(error);
|
|
131
|
-
}
|
|
132
|
-
return { id: numericId, revoked: true };
|
|
133
|
-
}
|
|
134
|
-
export async function modelKeysRevoke(ctx, id) {
|
|
135
|
-
const res = await modelKeysRevokeData(ctx, id);
|
|
136
|
-
return `Revoked ${res.id}.`;
|
|
137
|
-
}
|
|
138
|
-
export function validateModelKeyId(id) {
|
|
139
|
-
normalizeModelKeyId(id);
|
|
140
|
-
}
|
|
141
|
-
async function resolvePlaintext(opts) {
|
|
142
|
-
if (opts.key !== undefined && opts.keyStdin) {
|
|
143
|
-
throw new Error("Use either --key or --key-stdin, not both.");
|
|
144
|
-
}
|
|
145
|
-
if (opts.key !== undefined)
|
|
146
|
-
return normalizePlaintext(opts.key, "--key");
|
|
147
|
-
if (opts.keyStdin) {
|
|
148
|
-
return readModelKeyStdin();
|
|
149
|
-
}
|
|
150
|
-
throw new Error("Model key plaintext is required. Pass --key or --key-stdin.");
|
|
151
|
-
}
|
|
152
|
-
async function readModelKeyStdin() {
|
|
153
|
-
if (process.stdin.isTTY) {
|
|
154
|
-
throw new Error("No piped input. Usage: echo $PROVIDER_API_KEY | dharta model-keys create anthropic Claude --key-stdin");
|
|
155
|
-
}
|
|
156
|
-
const chunks = [];
|
|
157
|
-
for await (const chunk of process.stdin)
|
|
158
|
-
chunks.push(Buffer.from(chunk));
|
|
159
|
-
return normalizeModelKeyStdin(Buffer.concat(chunks).toString("utf8"));
|
|
160
|
-
}
|
|
161
|
-
function normalizeModelKeyStdin(value) {
|
|
162
|
-
const text = value.trim();
|
|
163
|
-
if (text)
|
|
164
|
-
return text;
|
|
165
|
-
throw new Error("No model key on stdin. Usage: echo $PROVIDER_API_KEY | dharta model-keys create anthropic Claude --key-stdin");
|
|
166
|
-
}
|
|
167
|
-
function normalizeModelKeyName(name) {
|
|
168
|
-
const normalized = name.trim();
|
|
169
|
-
if (!normalized) {
|
|
170
|
-
throw new Error("Model key name cannot be blank.");
|
|
171
|
-
}
|
|
172
|
-
return normalized;
|
|
173
|
-
}
|
|
174
|
-
function normalizeModelKeyId(id) {
|
|
175
|
-
const normalized = normalizeNonBlank(id, "Model key id");
|
|
176
|
-
parseModelKeyId(normalized);
|
|
177
|
-
return normalized;
|
|
178
|
-
}
|
|
179
|
-
function parseModelKeyId(id) {
|
|
180
|
-
return parsePositiveInteger(id, "Model key id");
|
|
181
|
-
}
|
|
182
|
-
function normalizePlaintext(value, flag) {
|
|
183
|
-
const normalized = value.trim();
|
|
184
|
-
if (!normalized) {
|
|
185
|
-
throw new Error(`${flag} cannot be blank.`);
|
|
186
|
-
}
|
|
187
|
-
return normalized;
|
|
188
|
-
}
|
|
189
|
-
function normalizeEndpointUrl(value) {
|
|
190
|
-
const normalized = normalizeHttpUrl(value, "--endpoint-url");
|
|
191
|
-
const parsed = new URL(normalized);
|
|
192
|
-
if (parsed.protocol !== "https:") {
|
|
193
|
-
throw new Error("--endpoint-url must use https.");
|
|
194
|
-
}
|
|
195
|
-
if (parsed.username || parsed.password || parsed.search || parsed.hash) {
|
|
196
|
-
throw new Error("--endpoint-url cannot include credentials, a query string, or a fragment.");
|
|
197
|
-
}
|
|
198
|
-
return normalized;
|
|
199
|
-
}
|
|
200
|
-
// Storing or revoking a connector needs `model_keys:write`, a tenant-admin
|
|
201
|
-
// scope that the `dharta login` device-flow credential deliberately never
|
|
202
|
-
// carries (that key is long-lived and non-admin, so it can't hold a
|
|
203
|
-
// forced-expiry admin scope). A CLI login therefore always 403s here. The
|
|
204
|
-
// generic scope hint says "manage key scopes", but the login key's scopes are
|
|
205
|
-
// fixed — you cannot add the scope to it — so name the real path: store the
|
|
206
|
-
// connector in the dashboard, or mint a bounded key and re-auth with a token.
|
|
207
|
-
function addModelKeysWriteScopeGuidance(error) {
|
|
208
|
-
if (!(error instanceof Error))
|
|
209
|
-
return new Error(String(error));
|
|
210
|
-
const status = error.status;
|
|
211
|
-
if (status === 403 &&
|
|
212
|
-
/model_keys:write/.test(error.message) &&
|
|
213
|
-
!error.message.includes("dharta login --with-token")) {
|
|
214
|
-
error.message +=
|
|
215
|
-
`${error.message.endsWith(".") ? "" : "."} ` +
|
|
216
|
-
"The `dharta login` credential cannot hold model_keys:write. Store the connector from the dashboard " +
|
|
217
|
-
"(`dharta open model-keys`), or mint a bounded key with model_keys:write there (`dharta open api-keys`) " +
|
|
218
|
-
"and re-authenticate with `dharta login --with-token`.";
|
|
219
|
-
}
|
|
220
|
-
return error;
|
|
221
|
-
}
|
|
222
|
-
//# sourceMappingURL=model-keys.js.map
|