@utdk/cli 0.1.0-dev.646adf4

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.
@@ -0,0 +1,317 @@
1
+ /**
2
+ * Provider and operation discovery for @utdk/cli.
3
+ *
4
+ * Scans the utdk package directory for providers and reads their OpenAPI
5
+ * specifications to enumerate operations. Name transformations mirror
6
+ * those in packages/utdk/client.ts so CLI operation names match the
7
+ * accessor paths on the created client.
8
+ *
9
+ * Also builds ToolRuntimeMetadataMap entries for path/query/header/body
10
+ * parameter routing, so the generated client can correctly assemble HTTP
11
+ * requests even when the provider's hand-written metadata.ts is empty.
12
+ */
13
+ import { existsSync, readdirSync, readFileSync } from "fs";
14
+ import { dirname, join, resolve } from "path";
15
+ import { fileURLToPath } from "url";
16
+ const __dirname = dirname(fileURLToPath(import.meta.url));
17
+ // Compiled to packages/utdk-cli/dist/; navigate to packages/utdk/
18
+ export const UTDK_ROOT = resolve(__dirname, "..", "..", "utdk");
19
+ // ---------------------------------------------------------------------------
20
+ // Name transformation (mirrors client.ts identically)
21
+ // ---------------------------------------------------------------------------
22
+ function sanitizeIdentifier(name) {
23
+ return name.replace(/[^a-zA-Z0-9_]/g, "_").replace(/^[0-9]/, "_$&");
24
+ }
25
+ function splitIdentifierWords(name) {
26
+ return name
27
+ .replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2")
28
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
29
+ .replace(/([A-Za-z])([0-9])/g, "$1 $2")
30
+ .replace(/([0-9])([A-Za-z])/g, "$1 $2")
31
+ .replace(/[^a-zA-Z0-9]+/g, " ")
32
+ .trim()
33
+ .split(/\s+/)
34
+ .filter(Boolean);
35
+ }
36
+ export function toCamelCase(name) {
37
+ const cleaned = splitIdentifierWords(name);
38
+ if (cleaned.length === 0)
39
+ return "_";
40
+ const [first = "_", ...rest] = cleaned;
41
+ return sanitizeIdentifier(first.toLowerCase() +
42
+ rest.map((s) => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase()).join(""));
43
+ }
44
+ /** Converts an OpenAPI operationId to a client access path (same logic as client.ts). */
45
+ function operationIdToBasePath(operationId) {
46
+ const segments = operationId.split("/").filter(Boolean);
47
+ const path = (segments.length > 0 ? segments : [operationId]).map(toCamelCase);
48
+ // Collapse duplicate consecutive leaf (e.g. a/a → ['a','call'])
49
+ if (path.length > 1 && path[path.length - 1] === path[path.length - 2]) {
50
+ path[path.length - 1] = "call";
51
+ }
52
+ return path;
53
+ }
54
+ function hasPathConflict(candidate, used) {
55
+ const key = candidate.join(".");
56
+ for (const u of used) {
57
+ if (u === key || u.startsWith(`${key}.`) || key.startsWith(`${u}.`))
58
+ return true;
59
+ }
60
+ return false;
61
+ }
62
+ function resolveAccessPath(basePath, used) {
63
+ const baseLeaf = basePath[basePath.length - 1] ?? "_";
64
+ let path = [...basePath];
65
+ let suffix = 2;
66
+ while (hasPathConflict(path, used)) {
67
+ path = [...basePath.slice(0, -1), `${baseLeaf}_${suffix}`];
68
+ suffix++;
69
+ }
70
+ used.add(path.join("."));
71
+ return path;
72
+ }
73
+ // ---------------------------------------------------------------------------
74
+ // Provider listing
75
+ // ---------------------------------------------------------------------------
76
+ /** Returns all provider names found in the utdk package directory. */
77
+ export function listProviders() {
78
+ if (!existsSync(UTDK_ROOT))
79
+ return [];
80
+ const entries = readdirSync(UTDK_ROOT, { withFileTypes: true });
81
+ return entries
82
+ .filter((e) => e.isDirectory() && existsSync(join(UTDK_ROOT, e.name, "openapi.json")))
83
+ .map((e) => e.name)
84
+ .sort();
85
+ }
86
+ /** Returns metadata for a single provider, or undefined if not found. */
87
+ export function getProviderInfo(providerName) {
88
+ const providerDir = join(UTDK_ROOT, providerName);
89
+ if (!existsSync(providerDir))
90
+ return undefined;
91
+ const pkgPath = join(providerDir, "package.json");
92
+ try {
93
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
94
+ return {
95
+ name: providerName,
96
+ description: pkg.description ?? pkg.utdk?.openapi?.title,
97
+ auth: pkg.utdk?.auth ?? [],
98
+ };
99
+ }
100
+ catch {
101
+ return { name: providerName, auth: [] };
102
+ }
103
+ }
104
+ const HTTP_METHODS = new Set(["get", "post", "put", "patch", "delete", "head", "options"]);
105
+ const JSON_CONTENT_TYPES = new Set([
106
+ "application/json",
107
+ "application/json-patch+json",
108
+ "application/merge-patch+json",
109
+ "application/vnd.api+json",
110
+ ]);
111
+ /** Resolve a JSON $ref to a schema object within the document. */
112
+ function resolveRef(doc, ref) {
113
+ if (!ref.startsWith("#/"))
114
+ return undefined;
115
+ const parts = ref.slice(2).split("/");
116
+ let current = doc;
117
+ for (const part of parts) {
118
+ if (current == null || typeof current !== "object")
119
+ return undefined;
120
+ current = current[part.replace(/~1/g, "/").replace(/~0/g, "~")];
121
+ }
122
+ return current;
123
+ }
124
+ function resolveSchemaRef(doc, schema) {
125
+ if (schema.$ref) {
126
+ return resolveRef(doc, schema.$ref) ?? schema;
127
+ }
128
+ return schema;
129
+ }
130
+ /** Resolve a $ref parameter to a concrete OpenApiParameter, or return the input if already concrete. */
131
+ function resolveParameter(doc, param) {
132
+ if (!("name" in param)) {
133
+ // It's a $ref
134
+ const resolved = resolveRef(doc, param.$ref);
135
+ if (resolved && "name" in resolved) {
136
+ return resolved;
137
+ }
138
+ return undefined;
139
+ }
140
+ return param;
141
+ }
142
+ /** Extract BodyInfo from an OpenAPI 3.x requestBody or Swagger 2 body param. */
143
+ function extractBodyInfo(doc, operation) {
144
+ // Check requestBody (OpenAPI 3.x)
145
+ if (operation.requestBody) {
146
+ let rb = operation.requestBody;
147
+ if ("$ref" in rb) {
148
+ const resolved = resolveRef(doc, rb.$ref);
149
+ if (resolved)
150
+ rb = resolved;
151
+ else
152
+ return { kind: "none", propertyKeys: [] };
153
+ }
154
+ const content = rb.content;
155
+ if (!content)
156
+ return { kind: "raw", propertyKeys: [] };
157
+ // Prefer JSON content types
158
+ const jsonEntry = Object.entries(content).find(([ct]) => JSON_CONTENT_TYPES.has(ct) || ct.includes("json"));
159
+ const [contentType, mediaType] = jsonEntry ?? Object.entries(content)[0] ?? [];
160
+ if (!contentType || !mediaType)
161
+ return { kind: "none", propertyKeys: [] };
162
+ const rawSchema = mediaType.schema;
163
+ if (!rawSchema)
164
+ return { kind: "raw", propertyKeys: [], contentType };
165
+ const schema = resolveSchemaRef(doc, rawSchema);
166
+ if (schema.type === "object" || schema.properties) {
167
+ const keys = Object.keys(schema.properties ?? {});
168
+ const allowsAdditional = schema.additionalProperties === true ||
169
+ (typeof schema.additionalProperties === "object" && schema.additionalProperties !== null);
170
+ return {
171
+ kind: "properties",
172
+ propertyKeys: keys,
173
+ contentType,
174
+ allowsAdditionalProperties: allowsAdditional || undefined,
175
+ };
176
+ }
177
+ return { kind: "raw", propertyKeys: [], contentType };
178
+ }
179
+ // Swagger 2: check for body or formData parameters
180
+ const params = (operation.parameters ?? []);
181
+ const bodyParam = params.find((p) => "name" in p && (p.in === "body" || p.in === "formData"));
182
+ if (!bodyParam)
183
+ return { kind: "none", propertyKeys: [] };
184
+ const schema = bodyParam.schema ? resolveSchemaRef(doc, bodyParam.schema) : undefined;
185
+ if (!schema)
186
+ return { kind: "raw", propertyKeys: [], contentType: "application/json" };
187
+ if (schema.type === "object" || schema.properties) {
188
+ const keys = Object.keys(schema.properties ?? {});
189
+ return { kind: "properties", propertyKeys: keys, contentType: "application/json" };
190
+ }
191
+ return { kind: "raw", propertyKeys: [], contentType: "application/json" };
192
+ }
193
+ // ---------------------------------------------------------------------------
194
+ // Operation listing
195
+ // ---------------------------------------------------------------------------
196
+ /** Parses the OpenAPI spec for a provider and returns all operations with their metadata. */
197
+ export function listOperations(providerName) {
198
+ const openApiPath = join(UTDK_ROOT, providerName, "openapi.json");
199
+ if (!existsSync(openApiPath))
200
+ return [];
201
+ let doc;
202
+ try {
203
+ doc = JSON.parse(readFileSync(openApiPath, "utf-8"));
204
+ }
205
+ catch {
206
+ return [];
207
+ }
208
+ const operations = [];
209
+ const usedPaths = new Set();
210
+ for (const [apiPath, pathItem] of Object.entries(doc.paths ?? {})) {
211
+ for (const [method, operation] of Object.entries(pathItem)) {
212
+ if (!HTTP_METHODS.has(method) || !operation)
213
+ continue;
214
+ const operationId = operation.operationId ??
215
+ `${method}${apiPath.replace(/[^a-zA-Z0-9]/g, "_")}`;
216
+ const basePath = operationIdToBasePath(operationId);
217
+ const accessPath = resolveAccessPath(basePath, usedPaths);
218
+ // Merge path-item-level params (shared across all methods) with operation params.
219
+ // Operation params override path-item params with the same name.
220
+ const pathItemParams = pathItem["parameters"] ?? [];
221
+ const rawParams = [
222
+ ...pathItemParams,
223
+ ...(operation.parameters ?? []),
224
+ ];
225
+ // Deduplicate: operation-level params override path-level params of the same name.
226
+ const seen = new Set();
227
+ const dedupedParams = [];
228
+ // Process operation params first (higher priority), then path params.
229
+ const operationParams = (operation.parameters ?? []);
230
+ for (const p of [...operationParams, ...pathItemParams]) {
231
+ const resolved = resolveParameter(doc, p);
232
+ if (!resolved)
233
+ continue;
234
+ if (!seen.has(resolved.name)) {
235
+ seen.add(resolved.name);
236
+ dedupedParams.push(resolved);
237
+ }
238
+ }
239
+ const parameters = dedupedParams
240
+ .map((p) => resolveParameter(doc, p))
241
+ .filter((p) => p !== undefined && p.in !== "body" && p.in !== "formData")
242
+ .map((p) => ({
243
+ name: p.name,
244
+ in: p.in ?? "query",
245
+ required: p.required ?? false,
246
+ description: p.description,
247
+ type: p.schema?.type,
248
+ }));
249
+ void rawParams; // used above via pathItemParams/operationParams
250
+ const body = extractBodyInfo(doc, operation);
251
+ operations.push({
252
+ accessPath,
253
+ operationId,
254
+ method: method.toUpperCase(),
255
+ path: apiPath,
256
+ summary: operation.summary,
257
+ description: operation.description,
258
+ parameters,
259
+ body,
260
+ });
261
+ }
262
+ }
263
+ return operations;
264
+ }
265
+ /** Returns the OperationInfo for a specific access-path string like "users.getByUsername". */
266
+ export function findOperation(providerName, operationPath) {
267
+ const ops = listOperations(providerName);
268
+ return ops.find((op) => op.accessPath.join(".") === operationPath ||
269
+ op.accessPath.join(".").toLowerCase() === operationPath.toLowerCase() ||
270
+ op.operationId === operationPath);
271
+ }
272
+ // ---------------------------------------------------------------------------
273
+ // Tool metadata builder
274
+ // ---------------------------------------------------------------------------
275
+ /**
276
+ * Build a ToolRuntimeMetadataMap from the provider's OpenAPI spec.
277
+ *
278
+ * The map keys equal the bare operationId (which is what OpenApiConverter
279
+ * uses as tool.name). Providing this map to createClient() gives the
280
+ * request assembler correct path/query/header/body routing without
281
+ * relying on the provider's (often empty) hand-written metadata.ts.
282
+ */
283
+ export function buildToolMetadata(ops) {
284
+ const map = {};
285
+ for (const op of ops) {
286
+ // tool.name from OpenApiConverter equals the bare operationId (no provider prefix).
287
+ const toolName = op.operationId;
288
+ const pathKeys = op.parameters
289
+ .filter((p) => p.in === "path")
290
+ .map((p) => p.name);
291
+ const queryKeys = op.parameters
292
+ .filter((p) => p.in === "query")
293
+ .map((p) => p.name);
294
+ const headerKeys = op.parameters
295
+ .filter((p) => p.in === "header")
296
+ .map((p) => p.name);
297
+ const meta = {
298
+ accessPath: op.accessPath,
299
+ bodyKind: op.body.kind,
300
+ bodyPropertyKeys: op.body.propertyKeys,
301
+ ...(op.body.allowsAdditionalProperties
302
+ ? { bodyAllowsAdditionalProperties: true }
303
+ : {}),
304
+ ...(op.body.contentType ? { contentType: op.body.contentType } : {}),
305
+ headerParameterKeys: headerKeys,
306
+ method: op.method,
307
+ routeTemplate: op.path,
308
+ pathConflictKeys: [],
309
+ pathParameterKeys: pathKeys,
310
+ queryConflictKeys: [],
311
+ queryParameterKeys: queryKeys,
312
+ };
313
+ map[toolName] = meta;
314
+ }
315
+ return map;
316
+ }
317
+ //# sourceMappingURL=providers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.js","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@utdk/cli",
3
+ "version": "0.1.0-dev.646adf4",
4
+ "type": "module",
5
+ "description": "Agent-native CLI wrapper for all @utdk provider operations",
6
+ "keywords": [
7
+ "utdk",
8
+ "cli",
9
+ "agent",
10
+ "openapi"
11
+ ],
12
+ "license": "MIT",
13
+ "bin": {
14
+ "utdk": "./dist/bin.js"
15
+ },
16
+ "dependencies": {
17
+ "utdk": "0.1.0-dev.646adf4"
18
+ },
19
+ "devDependencies": {
20
+ "@types/node": "^25.5.0",
21
+ "typescript": "^5.7.3"
22
+ },
23
+ "engines": {
24
+ "node": ">=20.0.0"
25
+ },
26
+ "scripts": {
27
+ "build": "tsc -p tsconfig.json",
28
+ "check-types": "tsc -p tsconfig.json --noEmit",
29
+ "typecheck": "tsc -p tsconfig.json --noEmit",
30
+ "clean": "rm -rf dist"
31
+ }
32
+ }
package/src/auth.ts ADDED
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Auth resolution for @utdk/cli.
3
+ *
4
+ * Reads a provider's `utdk.auth` config from its package.json and resolves
5
+ * credentials from environment variables. Resolution order:
6
+ * 1. UTDK_GATEWAY_URL + UTDK_GATEWAY_TOKEN (gateway proxy mode)
7
+ * 2. api_key template env var (e.g. GITHUB_TOKEN from "Bearer ${GITHUB_TOKEN}")
8
+ * 3. ${PROVIDER_NAME}_TOKEN (generic bearer fallback, also covers oauth2 flows)
9
+ */
10
+
11
+ import type { AuthConfig } from "./providers.js";
12
+
13
+ /** Minimal interface matching AuthProvider in @utdk/common/auth */
14
+ export type AuthProvider = {
15
+ authenticate(headers: Record<string, string>): Promise<void>;
16
+ };
17
+
18
+ function bearerAuth(token: string): AuthProvider {
19
+ return {
20
+ async authenticate(headers: Record<string, string>): Promise<void> {
21
+ headers["Authorization"] = `Bearer ${token}`;
22
+ },
23
+ };
24
+ }
25
+
26
+ function apiKeyAuth(headerName: string, value: string): AuthProvider {
27
+ return {
28
+ async authenticate(headers: Record<string, string>): Promise<void> {
29
+ headers[headerName] = value;
30
+ },
31
+ };
32
+ }
33
+
34
+ /** Extract the first `${VAR_NAME}` placeholder from a template string. */
35
+ function extractEnvVar(template: string): string | undefined {
36
+ const m = /\$\{([^}]+)\}/.exec(template);
37
+ return m?.[1];
38
+ }
39
+
40
+ /** Substitute `${VAR_NAME}` placeholders in a template with their env values. */
41
+ function resolveTemplate(template: string): string | undefined {
42
+ return template.replace(/\$\{([^}]+)\}/g, (_, varName: string) => {
43
+ const val = process.env[varName];
44
+ if (!val) throw new Error(`missing`);
45
+ return val;
46
+ });
47
+ }
48
+
49
+ /**
50
+ * Build an AuthProvider from the provider's auth configs and env vars.
51
+ * Returns undefined if no credentials are available (unauthenticated request).
52
+ */
53
+ export function resolveAuth(
54
+ providerName: string,
55
+ authConfigs: AuthConfig[],
56
+ ): AuthProvider | undefined {
57
+ // 1. Gateway proxy mode
58
+ const gatewayUrl = process.env["UTDK_GATEWAY_URL"];
59
+ const gatewayToken = process.env["UTDK_GATEWAY_TOKEN"];
60
+ if (gatewayUrl && gatewayToken) {
61
+ return bearerAuth(gatewayToken);
62
+ }
63
+
64
+ // 2. Per-config resolution
65
+ for (const config of authConfigs) {
66
+ if (config.auth_type === "api_key") {
67
+ const template = config.api_key ?? "";
68
+ const varName = config.var_name ?? "Authorization";
69
+
70
+ try {
71
+ const resolved = resolveTemplate(template);
72
+ if (resolved) {
73
+ if (resolved.startsWith("Bearer ")) {
74
+ return bearerAuth(resolved.slice(7));
75
+ }
76
+ return apiKeyAuth(varName, resolved);
77
+ }
78
+ } catch {
79
+ // env var not set — try next config
80
+ }
81
+ } else if (config.auth_type === "oauth2") {
82
+ // CLI can't drive an interactive OAuth flow; accept a pre-issued token.
83
+ const clientIdEnv = config.client_id ? extractEnvVar(config.client_id) : undefined;
84
+ const prefix = clientIdEnv
85
+ ? clientIdEnv.replace(/_CLIENT_ID$/i, "")
86
+ : providerName.toUpperCase().replace(/-/g, "_");
87
+ const token = process.env[`${prefix}_TOKEN`] ?? process.env[`${prefix}_ACCESS_TOKEN`];
88
+ if (token) return bearerAuth(token);
89
+ }
90
+ }
91
+
92
+ // 3. Generic fallback: ${PROVIDER}_TOKEN
93
+ const upperProvider = providerName.toUpperCase().replace(/-/g, "_");
94
+ const fallback =
95
+ process.env[`${upperProvider}_TOKEN`] ??
96
+ process.env[`${upperProvider}_ACCESS_TOKEN`] ??
97
+ process.env[`${upperProvider}_API_KEY`];
98
+ if (fallback) return bearerAuth(fallback);
99
+
100
+ return undefined;
101
+ }
102
+
103
+ /**
104
+ * Returns the list of env var names a user should set for this provider.
105
+ * Used in the --help output.
106
+ */
107
+ export function authEnvVars(providerName: string, authConfigs: AuthConfig[]): string[] {
108
+ const vars: string[] = [];
109
+ for (const config of authConfigs) {
110
+ if (config.auth_type === "api_key" && config.api_key) {
111
+ const v = extractEnvVar(config.api_key);
112
+ if (v) vars.push(v);
113
+ } else if (config.auth_type === "oauth2") {
114
+ const upper = providerName.toUpperCase().replace(/-/g, "_");
115
+ vars.push(`${upper}_TOKEN`);
116
+ }
117
+ }
118
+ // Also add the generic fallback so help is always useful even for unknown configs
119
+ const upper = providerName.toUpperCase().replace(/-/g, "_");
120
+ const generic = `${upper}_TOKEN`;
121
+ if (!vars.includes(generic)) vars.push(generic);
122
+ return [...new Set(vars)];
123
+ }
package/src/bin.ts ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @utdk/cli entry point
4
+ *
5
+ * Thin shim that boots the CLI and maps the resolved exit code to
6
+ * process.exit(). All logic lives in cli.ts.
7
+ */
8
+ import { runCli } from "./cli.js";
9
+
10
+ const exitCode = await runCli(process.argv.slice(2));
11
+ process.exit(exitCode);