@mondaydotcomorg/atp-server 0.17.14
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/README.md +489 -0
- package/dist/aggregator/index.d.ts +59 -0
- package/dist/aggregator/index.d.ts.map +1 -0
- package/dist/aggregator/index.js +171 -0
- package/dist/aggregator/index.js.map +1 -0
- package/dist/callback/index.d.ts +98 -0
- package/dist/callback/index.d.ts.map +1 -0
- package/dist/callback/index.js +136 -0
- package/dist/callback/index.js.map +1 -0
- package/dist/client-sessions.d.ts +82 -0
- package/dist/client-sessions.d.ts.map +1 -0
- package/dist/client-sessions.js +174 -0
- package/dist/client-sessions.js.map +1 -0
- package/dist/controllers/definitions.controller.d.ts +4 -0
- package/dist/controllers/definitions.controller.d.ts.map +1 -0
- package/dist/controllers/definitions.controller.js +11 -0
- package/dist/controllers/definitions.controller.js.map +1 -0
- package/dist/controllers/execute.controller.d.ts +18 -0
- package/dist/controllers/execute.controller.d.ts.map +1 -0
- package/dist/controllers/execute.controller.js +122 -0
- package/dist/controllers/execute.controller.js.map +1 -0
- package/dist/controllers/info.controller.d.ts +3 -0
- package/dist/controllers/info.controller.d.ts.map +1 -0
- package/dist/controllers/info.controller.js +13 -0
- package/dist/controllers/info.controller.js.map +1 -0
- package/dist/controllers/resume.controller.d.ts +11 -0
- package/dist/controllers/resume.controller.d.ts.map +1 -0
- package/dist/controllers/resume.controller.js +61 -0
- package/dist/controllers/resume.controller.js.map +1 -0
- package/dist/controllers/search.controller.d.ts +4 -0
- package/dist/controllers/search.controller.d.ts.map +1 -0
- package/dist/controllers/search.controller.js +7 -0
- package/dist/controllers/search.controller.js.map +1 -0
- package/dist/controllers/stream.controller.d.ts +19 -0
- package/dist/controllers/stream.controller.d.ts.map +1 -0
- package/dist/controllers/stream.controller.js +141 -0
- package/dist/controllers/stream.controller.js.map +1 -0
- package/dist/core/config.d.ts +161 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +7 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/http.d.ts +4 -0
- package/dist/core/http.d.ts.map +1 -0
- package/dist/core/http.js +17 -0
- package/dist/core/http.js.map +1 -0
- package/dist/create-server.d.ts +120 -0
- package/dist/create-server.d.ts.map +1 -0
- package/dist/create-server.js +423 -0
- package/dist/create-server.js.map +1 -0
- package/dist/execution-state/index.d.ts +95 -0
- package/dist/execution-state/index.d.ts.map +1 -0
- package/dist/execution-state/index.js +128 -0
- package/dist/execution-state/index.js.map +1 -0
- package/dist/executor/ast-provenance-bridge.d.ts +12 -0
- package/dist/executor/ast-provenance-bridge.d.ts.map +1 -0
- package/dist/executor/ast-provenance-bridge.js +66 -0
- package/dist/executor/ast-provenance-bridge.js.map +1 -0
- package/dist/executor/ast-tracking-runtime.d.ts +7 -0
- package/dist/executor/ast-tracking-runtime.d.ts.map +1 -0
- package/dist/executor/ast-tracking-runtime.js +559 -0
- package/dist/executor/ast-tracking-runtime.js.map +1 -0
- package/dist/executor/bootstrap-generated.d.ts +32 -0
- package/dist/executor/bootstrap-generated.d.ts.map +1 -0
- package/dist/executor/bootstrap-generated.js +90 -0
- package/dist/executor/bootstrap-generated.js.map +1 -0
- package/dist/executor/compiler-config.d.ts +32 -0
- package/dist/executor/compiler-config.d.ts.map +1 -0
- package/dist/executor/compiler-config.js +99 -0
- package/dist/executor/compiler-config.js.map +1 -0
- package/dist/executor/constants.d.ts +4 -0
- package/dist/executor/constants.d.ts.map +1 -0
- package/dist/executor/constants.js +4 -0
- package/dist/executor/constants.js.map +1 -0
- package/dist/executor/error-handler.d.ts +9 -0
- package/dist/executor/error-handler.d.ts.map +1 -0
- package/dist/executor/error-handler.js +95 -0
- package/dist/executor/error-handler.js.map +1 -0
- package/dist/executor/execution-error-handler.d.ts +7 -0
- package/dist/executor/execution-error-handler.d.ts.map +1 -0
- package/dist/executor/execution-error-handler.js +136 -0
- package/dist/executor/execution-error-handler.js.map +1 -0
- package/dist/executor/executor.d.ts +20 -0
- package/dist/executor/executor.d.ts.map +1 -0
- package/dist/executor/executor.js +452 -0
- package/dist/executor/executor.js.map +1 -0
- package/dist/executor/index.d.ts +4 -0
- package/dist/executor/index.d.ts.map +1 -0
- package/dist/executor/index.js +3 -0
- package/dist/executor/index.js.map +1 -0
- package/dist/executor/resume-handler.d.ts +9 -0
- package/dist/executor/resume-handler.d.ts.map +1 -0
- package/dist/executor/resume-handler.js +22 -0
- package/dist/executor/resume-handler.js.map +1 -0
- package/dist/executor/sandbox-builder.d.ts +29 -0
- package/dist/executor/sandbox-builder.d.ts.map +1 -0
- package/dist/executor/sandbox-builder.js +538 -0
- package/dist/executor/sandbox-builder.js.map +1 -0
- package/dist/executor/sandbox-injector.d.ts +7 -0
- package/dist/executor/sandbox-injector.d.ts.map +1 -0
- package/dist/executor/sandbox-injector.js +293 -0
- package/dist/executor/sandbox-injector.js.map +1 -0
- package/dist/executor/types.d.ts +21 -0
- package/dist/executor/types.d.ts.map +1 -0
- package/dist/executor/types.js +2 -0
- package/dist/executor/types.js.map +1 -0
- package/dist/explorer/index.d.ts +69 -0
- package/dist/explorer/index.d.ts.map +1 -0
- package/dist/explorer/index.js +228 -0
- package/dist/explorer/index.js.map +1 -0
- package/dist/handlers/definitions.handler.d.ts +3 -0
- package/dist/handlers/definitions.handler.d.ts.map +1 -0
- package/dist/handlers/definitions.handler.js +11 -0
- package/dist/handlers/definitions.handler.js.map +1 -0
- package/dist/handlers/execute.handler.d.ts +7 -0
- package/dist/handlers/execute.handler.d.ts.map +1 -0
- package/dist/handlers/execute.handler.js +225 -0
- package/dist/handlers/execute.handler.js.map +1 -0
- package/dist/handlers/explorer.handler.d.ts +4 -0
- package/dist/handlers/explorer.handler.d.ts.map +1 -0
- package/dist/handlers/explorer.handler.js +10 -0
- package/dist/handlers/explorer.handler.js.map +1 -0
- package/dist/handlers/init.handler.d.ts +5 -0
- package/dist/handlers/init.handler.d.ts.map +1 -0
- package/dist/handlers/init.handler.js +41 -0
- package/dist/handlers/init.handler.js.map +1 -0
- package/dist/handlers/resume.handler.d.ts +6 -0
- package/dist/handlers/resume.handler.d.ts.map +1 -0
- package/dist/handlers/resume.handler.js +256 -0
- package/dist/handlers/resume.handler.js.map +1 -0
- package/dist/handlers/search.handler.d.ts +5 -0
- package/dist/handlers/search.handler.d.ts.map +1 -0
- package/dist/handlers/search.handler.js +11 -0
- package/dist/handlers/search.handler.js.map +1 -0
- package/dist/http/request-handler.d.ts +15 -0
- package/dist/http/request-handler.d.ts.map +1 -0
- package/dist/http/request-handler.js +94 -0
- package/dist/http/request-handler.js.map +1 -0
- package/dist/http/router.d.ts +4 -0
- package/dist/http/router.d.ts.map +1 -0
- package/dist/http/router.js +32 -0
- package/dist/http/router.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/instrumentation/index.d.ts +5 -0
- package/dist/instrumentation/index.d.ts.map +1 -0
- package/dist/instrumentation/index.js +5 -0
- package/dist/instrumentation/index.js.map +1 -0
- package/dist/instrumentation/serializer.d.ts +61 -0
- package/dist/instrumentation/serializer.d.ts.map +1 -0
- package/dist/instrumentation/serializer.js +334 -0
- package/dist/instrumentation/serializer.js.map +1 -0
- package/dist/instrumentation/state-manager.d.ts +61 -0
- package/dist/instrumentation/state-manager.d.ts.map +1 -0
- package/dist/instrumentation/state-manager.js +205 -0
- package/dist/instrumentation/state-manager.js.map +1 -0
- package/dist/instrumentation/transformer.d.ts +9 -0
- package/dist/instrumentation/transformer.d.ts.map +1 -0
- package/dist/instrumentation/transformer.js +70 -0
- package/dist/instrumentation/transformer.js.map +1 -0
- package/dist/instrumentation/types.d.ts +59 -0
- package/dist/instrumentation/types.d.ts.map +1 -0
- package/dist/instrumentation/types.js +5 -0
- package/dist/instrumentation/types.js.map +1 -0
- package/dist/middleware/audit.d.ts +18 -0
- package/dist/middleware/audit.d.ts.map +1 -0
- package/dist/middleware/audit.js +76 -0
- package/dist/middleware/audit.js.map +1 -0
- package/dist/openapi/index.d.ts +133 -0
- package/dist/openapi/index.d.ts.map +1 -0
- package/dist/openapi/index.js +235 -0
- package/dist/openapi/index.js.map +1 -0
- package/dist/openapi-loader.d.ts +87 -0
- package/dist/openapi-loader.d.ts.map +1 -0
- package/dist/openapi-loader.js +491 -0
- package/dist/openapi-loader.js.map +1 -0
- package/dist/routes/index.d.ts +21 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +47 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/search/index.d.ts +48 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +156 -0
- package/dist/search/index.js.map +1 -0
- package/dist/security/index.d.ts +2 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +2 -0
- package/dist/security/index.js.map +1 -0
- package/dist/shutdown.d.ts +19 -0
- package/dist/shutdown.d.ts.map +1 -0
- package/dist/shutdown.js +87 -0
- package/dist/shutdown.js.map +1 -0
- package/dist/utils/banner.d.ts +12 -0
- package/dist/utils/banner.d.ts.map +1 -0
- package/dist/utils/banner.js +18 -0
- package/dist/utils/banner.js.map +1 -0
- package/dist/utils/context.d.ts +16 -0
- package/dist/utils/context.d.ts.map +1 -0
- package/dist/utils/context.js +44 -0
- package/dist/utils/context.js.map +1 -0
- package/dist/utils/error.d.ts +8 -0
- package/dist/utils/error.d.ts.map +1 -0
- package/dist/utils/error.js +17 -0
- package/dist/utils/error.js.map +1 -0
- package/dist/utils/hint-based-instrumentation.d.ts +14 -0
- package/dist/utils/hint-based-instrumentation.d.ts.map +1 -0
- package/dist/utils/hint-based-instrumentation.js +84 -0
- package/dist/utils/hint-based-instrumentation.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/info.d.ts +20 -0
- package/dist/utils/info.d.ts.map +1 -0
- package/dist/utils/info.js +15 -0
- package/dist/utils/info.js.map +1 -0
- package/dist/utils/provenance-reattachment.d.ts +32 -0
- package/dist/utils/provenance-reattachment.d.ts.map +1 -0
- package/dist/utils/provenance-reattachment.js +115 -0
- package/dist/utils/provenance-reattachment.js.map +1 -0
- package/dist/utils/request.d.ts +21 -0
- package/dist/utils/request.d.ts.map +1 -0
- package/dist/utils/request.js +44 -0
- package/dist/utils/request.js.map +1 -0
- package/dist/utils/response.d.ts +30 -0
- package/dist/utils/response.d.ts.map +1 -0
- package/dist/utils/response.js +53 -0
- package/dist/utils/response.js.map +1 -0
- package/dist/utils/runtime-types.d.ts +6 -0
- package/dist/utils/runtime-types.d.ts.map +1 -0
- package/dist/utils/runtime-types.js +14 -0
- package/dist/utils/runtime-types.js.map +1 -0
- package/dist/utils/schema.d.ts +9 -0
- package/dist/utils/schema.d.ts.map +1 -0
- package/dist/utils/schema.js +13 -0
- package/dist/utils/schema.js.map +1 -0
- package/dist/utils/token-emitter.d.ts +21 -0
- package/dist/utils/token-emitter.d.ts.map +1 -0
- package/dist/utils/token-emitter.js +129 -0
- package/dist/utils/token-emitter.js.map +1 -0
- package/dist/validator/index.d.ts +36 -0
- package/dist/validator/index.d.ts.map +1 -0
- package/dist/validator/index.js +224 -0
- package/dist/validator/index.js.map +1 -0
- package/package.json +68 -0
- package/src/aggregator/index.ts +207 -0
- package/src/callback/index.ts +191 -0
- package/src/client-sessions.ts +234 -0
- package/src/controllers/definitions.controller.ts +19 -0
- package/src/controllers/execute.controller.ts +166 -0
- package/src/controllers/info.controller.ts +14 -0
- package/src/controllers/resume.controller.ts +92 -0
- package/src/controllers/search.controller.ts +16 -0
- package/src/controllers/stream.controller.ts +190 -0
- package/src/core/config.ts +180 -0
- package/src/core/http.ts +21 -0
- package/src/create-server.ts +536 -0
- package/src/execution-state/index.ts +204 -0
- package/src/executor/ast-provenance-bridge.ts +80 -0
- package/src/executor/ast-tracking-runtime.ts +558 -0
- package/src/executor/bootstrap-generated.ts +90 -0
- package/src/executor/compiler-config.ts +146 -0
- package/src/executor/constants.ts +5 -0
- package/src/executor/error-handler.ts +118 -0
- package/src/executor/execution-error-handler.ts +178 -0
- package/src/executor/executor.ts +631 -0
- package/src/executor/index.ts +3 -0
- package/src/executor/resume-handler.ts +39 -0
- package/src/executor/sandbox-builder.ts +684 -0
- package/src/executor/sandbox-injector.ts +345 -0
- package/src/executor/types.ts +22 -0
- package/src/explorer/index.ts +297 -0
- package/src/handlers/definitions.handler.ts +13 -0
- package/src/handlers/execute.handler.ts +286 -0
- package/src/handlers/explorer.handler.ts +18 -0
- package/src/handlers/init.handler.ts +53 -0
- package/src/handlers/resume.handler.ts +316 -0
- package/src/handlers/search.handler.ts +32 -0
- package/src/http/request-handler.ts +117 -0
- package/src/http/router.ts +29 -0
- package/src/index.ts +60 -0
- package/src/instrumentation/index.ts +4 -0
- package/src/instrumentation/serializer.ts +421 -0
- package/src/instrumentation/state-manager.ts +237 -0
- package/src/instrumentation/transformer.ts +84 -0
- package/src/instrumentation/types.ts +76 -0
- package/src/middleware/audit.ts +101 -0
- package/src/openapi/index.ts +378 -0
- package/src/openapi-loader.ts +744 -0
- package/src/routes/index.ts +93 -0
- package/src/search/index.ts +216 -0
- package/src/security/index.ts +1 -0
- package/src/shutdown.ts +108 -0
- package/src/utils/banner.ts +25 -0
- package/src/utils/context.ts +58 -0
- package/src/utils/error.ts +25 -0
- package/src/utils/hint-based-instrumentation.ts +99 -0
- package/src/utils/index.ts +15 -0
- package/src/utils/info.ts +31 -0
- package/src/utils/provenance-reattachment.ts +144 -0
- package/src/utils/request.ts +53 -0
- package/src/utils/response.ts +69 -0
- package/src/utils/runtime-types.ts +14 -0
- package/src/utils/schema.ts +18 -0
- package/src/utils/token-emitter.ts +182 -0
- package/src/validator/index.ts +253 -0
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
APIGroupConfig,
|
|
3
|
+
CustomFunctionDef,
|
|
4
|
+
AuthProvider,
|
|
5
|
+
AuthConfig,
|
|
6
|
+
BearerAuthConfig,
|
|
7
|
+
BasicAuthConfig,
|
|
8
|
+
APIKeyAuthConfig,
|
|
9
|
+
} from '@mondaydotcomorg/atp-protocol';
|
|
10
|
+
import { readFile } from 'node:fs/promises';
|
|
11
|
+
import yaml from 'js-yaml';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Base HTTP API specification (common to both OpenAPI and Swagger)
|
|
15
|
+
*/
|
|
16
|
+
interface BaseAPISpec {
|
|
17
|
+
info: {
|
|
18
|
+
title: string;
|
|
19
|
+
version: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
};
|
|
22
|
+
paths: Record<string, Record<string, OpenAPIOperation>>;
|
|
23
|
+
security?: Array<Record<string, string[]>>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* OpenAPI 3.0+ specification structure
|
|
28
|
+
*/
|
|
29
|
+
interface OpenAPISpec extends BaseAPISpec {
|
|
30
|
+
openapi: string;
|
|
31
|
+
servers?: Array<{ url: string; description?: string }>;
|
|
32
|
+
components?: {
|
|
33
|
+
schemas?: Record<string, OpenAPISchema>;
|
|
34
|
+
securitySchemes?: Record<string, OpenAPISecurityScheme>;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Swagger 2.0 specification structure
|
|
40
|
+
*/
|
|
41
|
+
interface Swagger2Spec extends BaseAPISpec {
|
|
42
|
+
swagger: string;
|
|
43
|
+
host?: string;
|
|
44
|
+
basePath?: string;
|
|
45
|
+
schemes?: Array<'http' | 'https' | 'ws' | 'wss'>;
|
|
46
|
+
consumes?: string[];
|
|
47
|
+
produces?: string[];
|
|
48
|
+
definitions?: Record<string, OpenAPISchema>;
|
|
49
|
+
securityDefinitions?: Record<string, OpenAPISecurityScheme>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Union type for all supported API specification formats
|
|
54
|
+
*/
|
|
55
|
+
type APISpec = OpenAPISpec | Swagger2Spec;
|
|
56
|
+
|
|
57
|
+
interface OpenAPIOperation {
|
|
58
|
+
operationId?: string;
|
|
59
|
+
summary?: string;
|
|
60
|
+
description?: string;
|
|
61
|
+
tags?: string[];
|
|
62
|
+
deprecated?: boolean;
|
|
63
|
+
parameters?: Array<OpenAPIParameter>;
|
|
64
|
+
requestBody?: OpenAPIRequestBody;
|
|
65
|
+
responses?: Record<string, OpenAPIResponse>;
|
|
66
|
+
security?: Array<Record<string, string[]>>;
|
|
67
|
+
'x-destructive'?: boolean;
|
|
68
|
+
'x-requires-approval'?: boolean;
|
|
69
|
+
'x-risk-level'?: 'low' | 'medium' | 'high' | 'critical';
|
|
70
|
+
'x-confirm-prompt'?: string;
|
|
71
|
+
[key: string]: unknown;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface OpenAPIParameter {
|
|
75
|
+
name: string;
|
|
76
|
+
in: 'query' | 'header' | 'path' | 'cookie';
|
|
77
|
+
required?: boolean;
|
|
78
|
+
schema?: OpenAPISchema;
|
|
79
|
+
description?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
interface OpenAPIRequestBody {
|
|
83
|
+
required?: boolean;
|
|
84
|
+
content?: Record<string, { schema?: OpenAPISchema }>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
interface OpenAPIResponse {
|
|
88
|
+
description?: string;
|
|
89
|
+
content?: Record<string, { schema?: OpenAPISchema }>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
interface OpenAPISchema {
|
|
93
|
+
type?: string;
|
|
94
|
+
properties?: Record<string, OpenAPISchema>;
|
|
95
|
+
items?: OpenAPISchema;
|
|
96
|
+
required?: string[];
|
|
97
|
+
enum?: string[];
|
|
98
|
+
$ref?: string;
|
|
99
|
+
description?: string;
|
|
100
|
+
[key: string]: unknown;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface OpenAPISecurityScheme {
|
|
104
|
+
type: 'apiKey' | 'http' | 'oauth2' | 'openIdConnect';
|
|
105
|
+
description?: string;
|
|
106
|
+
name?: string;
|
|
107
|
+
in?: 'query' | 'header' | 'cookie';
|
|
108
|
+
scheme?: string;
|
|
109
|
+
bearerFormat?: string;
|
|
110
|
+
flows?: Record<string, unknown>;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Options for loading OpenAPI spec
|
|
115
|
+
*/
|
|
116
|
+
export interface LoadOpenAPIOptions {
|
|
117
|
+
/** API group name */
|
|
118
|
+
name?: string;
|
|
119
|
+
|
|
120
|
+
/** Filter operations */
|
|
121
|
+
filter?: {
|
|
122
|
+
/** Include only these tags */
|
|
123
|
+
tags?: string[];
|
|
124
|
+
/** Include only paths matching these patterns */
|
|
125
|
+
paths?: string[];
|
|
126
|
+
/** Exclude paths matching these patterns */
|
|
127
|
+
exclude?: string[];
|
|
128
|
+
/** Include only these HTTP methods */
|
|
129
|
+
methods?: string[];
|
|
130
|
+
/** Custom filter function */
|
|
131
|
+
operation?: (op: OpenAPIOperation, path: string, method: string) => boolean;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/** Override descriptions for better LLM understanding */
|
|
135
|
+
descriptions?: Record<string, string>;
|
|
136
|
+
|
|
137
|
+
/** Annotation mapping */
|
|
138
|
+
annotations?: {
|
|
139
|
+
/** Map OpenAPI extensions to annotations */
|
|
140
|
+
fromExtensions?: Record<string, string>;
|
|
141
|
+
/** Global annotations for all operations */
|
|
142
|
+
global?: Record<string, unknown>;
|
|
143
|
+
/** Per-operation annotations */
|
|
144
|
+
operations?: Record<string, Record<string, unknown>>;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/** Auth provider (optional, uses server's if not provided) */
|
|
148
|
+
authProvider?: AuthProvider;
|
|
149
|
+
|
|
150
|
+
/** Base URL override (if different from spec servers) */
|
|
151
|
+
baseURL?: string;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Type guard to check if spec is OpenAPI 3.0+
|
|
156
|
+
*/
|
|
157
|
+
function isOpenAPI3(spec: APISpec): spec is OpenAPISpec {
|
|
158
|
+
return 'openapi' in spec;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Type guard to check if spec is Swagger 2.0
|
|
163
|
+
*/
|
|
164
|
+
function isSwagger2(spec: APISpec): spec is Swagger2Spec {
|
|
165
|
+
return 'swagger' in spec;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Load OpenAPI specification and convert to ATP API group
|
|
170
|
+
*/
|
|
171
|
+
export async function loadOpenAPI(
|
|
172
|
+
source: string,
|
|
173
|
+
options: LoadOpenAPIOptions = {}
|
|
174
|
+
): Promise<APIGroupConfig> {
|
|
175
|
+
const spec = await loadSpec(source);
|
|
176
|
+
|
|
177
|
+
const name = options.name || spec.info.title.toLowerCase().replace(/\s+/g, '-');
|
|
178
|
+
|
|
179
|
+
let baseURL = options.baseURL;
|
|
180
|
+
if (!baseURL) {
|
|
181
|
+
if (isOpenAPI3(spec) && spec.servers && spec.servers[0]) {
|
|
182
|
+
baseURL = spec.servers[0].url;
|
|
183
|
+
}
|
|
184
|
+
else if (isSwagger2(spec) && spec.host) {
|
|
185
|
+
const scheme = spec.schemes?.[0] || 'https';
|
|
186
|
+
const host = spec.host;
|
|
187
|
+
const basePath = spec.basePath || '';
|
|
188
|
+
baseURL = `${scheme}://${host}${basePath}`;
|
|
189
|
+
} else {
|
|
190
|
+
baseURL = '';
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Detect auth first so we can pass it to handlers
|
|
195
|
+
const auth = detectAuth(spec, options.authProvider);
|
|
196
|
+
|
|
197
|
+
const functions: CustomFunctionDef[] = [];
|
|
198
|
+
|
|
199
|
+
for (const [path, methods] of Object.entries(spec.paths)) {
|
|
200
|
+
for (const [method, operation] of Object.entries(methods)) {
|
|
201
|
+
if (['parameters', 'servers', 'summary', 'description'].includes(method)) {
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!shouldIncludeOperation(operation, path, method, options.filter)) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const func = convertOperation(path, method, operation, spec, baseURL, options, auth);
|
|
210
|
+
|
|
211
|
+
if (func) {
|
|
212
|
+
functions.push(func);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
name,
|
|
219
|
+
type: 'openapi',
|
|
220
|
+
functions,
|
|
221
|
+
auth,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Load OpenAPI spec from file or URL
|
|
227
|
+
*/
|
|
228
|
+
async function loadSpec(source: string): Promise<APISpec> {
|
|
229
|
+
let content: string;
|
|
230
|
+
let isYaml = false;
|
|
231
|
+
|
|
232
|
+
if (source.startsWith('http://') || source.startsWith('https://')) {
|
|
233
|
+
const response = await fetch(source);
|
|
234
|
+
if (!response.ok) {
|
|
235
|
+
throw new Error(`Failed to load OpenAPI spec from ${source}: ${response.statusText}`);
|
|
236
|
+
}
|
|
237
|
+
content = await response.text();
|
|
238
|
+
const contentType = response.headers.get('content-type');
|
|
239
|
+
isYaml =
|
|
240
|
+
contentType?.includes('yaml') ||
|
|
241
|
+
contentType?.includes('yml') ||
|
|
242
|
+
source.endsWith('.yaml') ||
|
|
243
|
+
source.endsWith('.yml');
|
|
244
|
+
} else {
|
|
245
|
+
content = await readFile(source, 'utf-8');
|
|
246
|
+
isYaml = source.endsWith('.yaml') || source.endsWith('.yml');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
if (isYaml) {
|
|
251
|
+
return yaml.load(content) as OpenAPISpec;
|
|
252
|
+
} else {
|
|
253
|
+
try {
|
|
254
|
+
return JSON.parse(content);
|
|
255
|
+
} catch {
|
|
256
|
+
return yaml.load(content) as OpenAPISpec;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
} catch (error) {
|
|
260
|
+
throw new Error(`Failed to parse OpenAPI spec: ${(error as Error).message}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Check if operation should be included based on filters
|
|
266
|
+
*/
|
|
267
|
+
function shouldIncludeOperation(
|
|
268
|
+
operation: OpenAPIOperation,
|
|
269
|
+
path: string,
|
|
270
|
+
method: string,
|
|
271
|
+
filter?: LoadOpenAPIOptions['filter']
|
|
272
|
+
): boolean {
|
|
273
|
+
if (!filter) return true;
|
|
274
|
+
|
|
275
|
+
if (filter.tags && filter.tags.length > 0) {
|
|
276
|
+
if (!operation.tags || !operation.tags.some((t) => filter.tags!.includes(t))) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (filter.paths && filter.paths.length > 0) {
|
|
282
|
+
if (!filter.paths.some((pattern) => matchPathPattern(path, pattern))) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (filter.exclude && filter.exclude.length > 0) {
|
|
288
|
+
if (filter.exclude.some((pattern) => matchPathPattern(path, pattern))) {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (filter.methods && filter.methods.length > 0) {
|
|
294
|
+
if (!filter.methods.includes(method.toUpperCase())) {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (operation.deprecated) {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (filter.operation) {
|
|
304
|
+
return filter.operation(operation, path, method);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Match path against pattern (supports wildcards)
|
|
312
|
+
*/
|
|
313
|
+
function matchPathPattern(path: string, pattern: string): boolean {
|
|
314
|
+
const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*');
|
|
315
|
+
|
|
316
|
+
return new RegExp(`^${regexPattern}$`).test(path);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Convert OpenAPI operation to ATP function
|
|
321
|
+
*/
|
|
322
|
+
function convertOperation(
|
|
323
|
+
path: string,
|
|
324
|
+
method: string,
|
|
325
|
+
operation: OpenAPIOperation,
|
|
326
|
+
spec: APISpec,
|
|
327
|
+
baseURL: string,
|
|
328
|
+
options: LoadOpenAPIOptions,
|
|
329
|
+
auth?: AuthConfig
|
|
330
|
+
): CustomFunctionDef | null {
|
|
331
|
+
const functionName = operation.operationId || `${method}_${path.replace(/[^a-zA-Z0-9]/g, '_')}`;
|
|
332
|
+
|
|
333
|
+
const operationKey = `${method.toUpperCase()} ${path}`;
|
|
334
|
+
const description =
|
|
335
|
+
options.descriptions?.[operationKey] ||
|
|
336
|
+
operation.summary ||
|
|
337
|
+
operation.description ||
|
|
338
|
+
`${method.toUpperCase()} ${path}`;
|
|
339
|
+
|
|
340
|
+
const inputSchema = buildInputSchema(operation, spec) as any;
|
|
341
|
+
const outputSchema = buildOutputSchema(operation, spec) as any;
|
|
342
|
+
|
|
343
|
+
const annotations = extractAnnotations(operation, operationKey, options.annotations);
|
|
344
|
+
|
|
345
|
+
const handler = async (params: unknown) => {
|
|
346
|
+
// Build the actual HTTP request
|
|
347
|
+
const input = (params as Record<string, any>) || {};
|
|
348
|
+
let requestPath = path;
|
|
349
|
+
const queryParams: Record<string, string> = {};
|
|
350
|
+
let body: any = undefined;
|
|
351
|
+
const headers: Record<string, string> = {
|
|
352
|
+
'Content-Type': 'application/json',
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
// Add authentication
|
|
356
|
+
if (auth) {
|
|
357
|
+
if (auth.scheme === 'bearer' && auth.envVar) {
|
|
358
|
+
// Try authProvider first, fallback to process.env
|
|
359
|
+
let token: string | null = null;
|
|
360
|
+
if (options.authProvider) {
|
|
361
|
+
token = await options.authProvider.getCredential(auth.envVar);
|
|
362
|
+
console.log(
|
|
363
|
+
`[AUTH DEBUG] Got token from authProvider for ${auth.envVar}: ${token ? 'YES (' + token.substring(0, 20) + '...)' : 'NO'}`
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
if (!token) {
|
|
367
|
+
token = process.env[auth.envVar] || null;
|
|
368
|
+
console.log(
|
|
369
|
+
`[AUTH DEBUG] Got token from process.env[${auth.envVar}]: ${token ? 'YES (' + token.substring(0, 20) + '...)' : 'NO'}`
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (token) {
|
|
374
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
375
|
+
} else {
|
|
376
|
+
console.warn(
|
|
377
|
+
`[AUTH WARNING] ${auth.envVar} not found! Set it in authProvider or environment.`
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
} else if (auth.scheme === 'basic') {
|
|
381
|
+
let username: string | null = null;
|
|
382
|
+
let password: string | null = null;
|
|
383
|
+
|
|
384
|
+
if (options.authProvider && auth.usernameEnvVar && auth.passwordEnvVar) {
|
|
385
|
+
username = await options.authProvider.getCredential(auth.usernameEnvVar);
|
|
386
|
+
password = await options.authProvider.getCredential(auth.passwordEnvVar);
|
|
387
|
+
}
|
|
388
|
+
if (!username && auth.usernameEnvVar) {
|
|
389
|
+
username = process.env[auth.usernameEnvVar] || null;
|
|
390
|
+
}
|
|
391
|
+
if (!password && auth.passwordEnvVar) {
|
|
392
|
+
password = process.env[auth.passwordEnvVar] || null;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (username && password) {
|
|
396
|
+
const credentials = Buffer.from(`${username}:${password}`).toString('base64');
|
|
397
|
+
headers['Authorization'] = `Basic ${credentials}`;
|
|
398
|
+
}
|
|
399
|
+
} else if (auth.scheme === 'apiKey') {
|
|
400
|
+
let apiKey: string | null = null;
|
|
401
|
+
const apiKeyEnvVar = auth.envVar || 'API_KEY';
|
|
402
|
+
if (options.authProvider) {
|
|
403
|
+
apiKey = await options.authProvider.getCredential(apiKeyEnvVar);
|
|
404
|
+
}
|
|
405
|
+
if (!apiKey) {
|
|
406
|
+
apiKey = process.env[apiKeyEnvVar] || null;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (apiKey) {
|
|
410
|
+
if (auth.in === 'header') {
|
|
411
|
+
headers[auth.name] = apiKey;
|
|
412
|
+
} else if (auth.in === 'query') {
|
|
413
|
+
queryParams[auth.name] = apiKey;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Replace path parameters
|
|
420
|
+
if (operation.parameters) {
|
|
421
|
+
for (const param of operation.parameters) {
|
|
422
|
+
if (param.in === 'path' && input[param.name]) {
|
|
423
|
+
requestPath = requestPath.replace(
|
|
424
|
+
`{${param.name}}`,
|
|
425
|
+
encodeURIComponent(String(input[param.name]))
|
|
426
|
+
);
|
|
427
|
+
} else if (param.in === 'query' && input[param.name] !== undefined) {
|
|
428
|
+
queryParams[param.name] = String(input[param.name]);
|
|
429
|
+
} else if (param.in === 'header' && input[param.name]) {
|
|
430
|
+
headers[param.name] = String(input[param.name]);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Add request body if present
|
|
436
|
+
if (operation.requestBody && ['post', 'put', 'patch'].includes(method.toLowerCase())) {
|
|
437
|
+
// Collect body properties
|
|
438
|
+
const bodyParams: Record<string, any> = {};
|
|
439
|
+
if (operation.parameters) {
|
|
440
|
+
const paramNames = operation.parameters.map((p) => p.name);
|
|
441
|
+
for (const key in input) {
|
|
442
|
+
if (!paramNames.includes(key)) {
|
|
443
|
+
bodyParams[key] = input[key];
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
} else {
|
|
447
|
+
Object.assign(bodyParams, input);
|
|
448
|
+
}
|
|
449
|
+
if (Object.keys(bodyParams).length > 0) {
|
|
450
|
+
body = bodyParams;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Build URL with query params
|
|
455
|
+
if (!baseURL) {
|
|
456
|
+
throw new Error(
|
|
457
|
+
`No baseURL configured for OpenAPI spec. Check that the spec has a 'servers' section with a valid URL.`
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const baseUrlObj = new URL(baseURL);
|
|
462
|
+
const basePath = baseUrlObj.pathname.replace(/\/$/, '');
|
|
463
|
+
const fullPath = basePath + requestPath;
|
|
464
|
+
const url = new URL(fullPath, baseUrlObj.origin);
|
|
465
|
+
|
|
466
|
+
for (const [key, value] of Object.entries(queryParams)) {
|
|
467
|
+
url.searchParams.append(key, value);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Make the HTTP request
|
|
471
|
+
try {
|
|
472
|
+
const response = await fetch(url.toString(), {
|
|
473
|
+
method: method.toUpperCase(),
|
|
474
|
+
headers,
|
|
475
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
if (!response.ok) {
|
|
479
|
+
const errorText = await response.text();
|
|
480
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Handle 204 No Content
|
|
484
|
+
if (response.status === 204) {
|
|
485
|
+
return { success: true };
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const contentType = response.headers.get('content-type');
|
|
489
|
+
if (contentType?.includes('application/json')) {
|
|
490
|
+
const text = await response.text();
|
|
491
|
+
// Handle empty response body
|
|
492
|
+
if (!text || text.trim() === '') {
|
|
493
|
+
return { success: true };
|
|
494
|
+
}
|
|
495
|
+
return JSON.parse(text);
|
|
496
|
+
} else {
|
|
497
|
+
return await response.text();
|
|
498
|
+
}
|
|
499
|
+
} catch (error: any) {
|
|
500
|
+
throw new Error(`Failed to execute ${method.toUpperCase()} ${path}: ${error.message}`);
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
return {
|
|
505
|
+
name: functionName,
|
|
506
|
+
description,
|
|
507
|
+
inputSchema,
|
|
508
|
+
outputSchema,
|
|
509
|
+
handler,
|
|
510
|
+
keywords: operation.tags || [],
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Build input JSON schema from parameters and requestBody
|
|
516
|
+
*/
|
|
517
|
+
function buildInputSchema(operation: OpenAPIOperation, spec: APISpec): unknown {
|
|
518
|
+
const properties: Record<string, unknown> = {};
|
|
519
|
+
const required: string[] = [];
|
|
520
|
+
|
|
521
|
+
if (operation.parameters) {
|
|
522
|
+
for (const param of operation.parameters) {
|
|
523
|
+
if (param.schema) {
|
|
524
|
+
properties[param.name] = resolveSchema(param.schema, spec);
|
|
525
|
+
if (param.required) {
|
|
526
|
+
required.push(param.name);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (operation.requestBody?.content?.['application/json']?.schema) {
|
|
533
|
+
const bodySchema = resolveSchema(
|
|
534
|
+
operation.requestBody.content['application/json'].schema,
|
|
535
|
+
spec
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
if (typeof bodySchema === 'object' && bodySchema !== null && 'properties' in bodySchema) {
|
|
539
|
+
Object.assign(properties, (bodySchema as any).properties);
|
|
540
|
+
if ('required' in bodySchema && Array.isArray((bodySchema as any).required)) {
|
|
541
|
+
required.push(...(bodySchema as any).required);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
return {
|
|
547
|
+
type: 'object',
|
|
548
|
+
properties,
|
|
549
|
+
required: required.length > 0 ? required : undefined,
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Build output JSON schema from responses
|
|
555
|
+
*/
|
|
556
|
+
function buildOutputSchema(operation: OpenAPIOperation, spec: APISpec): unknown | undefined {
|
|
557
|
+
const successResponse =
|
|
558
|
+
operation.responses?.['200'] ||
|
|
559
|
+
operation.responses?.['201'] ||
|
|
560
|
+
operation.responses?.['default'];
|
|
561
|
+
|
|
562
|
+
if (!successResponse?.content?.['application/json']?.schema) {
|
|
563
|
+
return undefined;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return resolveSchema(successResponse.content['application/json'].schema, spec, new Set());
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Resolve schema references ($ref) with circular reference detection
|
|
571
|
+
*/
|
|
572
|
+
function resolveSchema(
|
|
573
|
+
schema: OpenAPISchema,
|
|
574
|
+
spec: APISpec,
|
|
575
|
+
visited: Set<string> = new Set()
|
|
576
|
+
): unknown {
|
|
577
|
+
if (schema.$ref) {
|
|
578
|
+
// Check for circular reference
|
|
579
|
+
if (visited.has(schema.$ref)) {
|
|
580
|
+
// Return a placeholder for circular references
|
|
581
|
+
return { type: 'object', description: 'Circular reference: ' + schema.$ref };
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const refPath = schema.$ref.split('/').slice(1);
|
|
585
|
+
let resolved: unknown = spec;
|
|
586
|
+
|
|
587
|
+
for (const part of refPath) {
|
|
588
|
+
resolved = (resolved as Record<string, unknown>)?.[part];
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (resolved) {
|
|
592
|
+
visited.add(schema.$ref);
|
|
593
|
+
const result = resolveSchema(resolved as OpenAPISchema, spec, visited);
|
|
594
|
+
visited.delete(schema.$ref);
|
|
595
|
+
return result;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
const jsonSchema: Record<string, unknown> = { type: schema.type || 'object' };
|
|
600
|
+
|
|
601
|
+
if (schema.properties) {
|
|
602
|
+
const properties: Record<string, unknown> = {};
|
|
603
|
+
for (const [key, value] of Object.entries(schema.properties)) {
|
|
604
|
+
properties[key] = resolveSchema(value, spec, visited);
|
|
605
|
+
}
|
|
606
|
+
jsonSchema.properties = properties;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (schema.items) {
|
|
610
|
+
jsonSchema.items = resolveSchema(schema.items, spec, visited);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
if (schema.required) {
|
|
614
|
+
jsonSchema.required = schema.required;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (schema.enum) {
|
|
618
|
+
jsonSchema.enum = schema.enum;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if (schema.description) {
|
|
622
|
+
jsonSchema.description = schema.description;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
return jsonSchema;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Extract annotations from OpenAPI extensions
|
|
630
|
+
*/
|
|
631
|
+
function extractAnnotations(
|
|
632
|
+
operation: OpenAPIOperation,
|
|
633
|
+
operationKey: string,
|
|
634
|
+
annotationOptions?: LoadOpenAPIOptions['annotations']
|
|
635
|
+
): Record<string, unknown> {
|
|
636
|
+
const annotations: Record<string, unknown> = {};
|
|
637
|
+
|
|
638
|
+
if (annotationOptions?.global) {
|
|
639
|
+
Object.assign(annotations, annotationOptions.global);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (annotationOptions?.operations?.[operationKey]) {
|
|
643
|
+
Object.assign(annotations, annotationOptions.operations[operationKey]);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (annotationOptions?.fromExtensions) {
|
|
647
|
+
for (const [extensionKey, annotationKey] of Object.entries(annotationOptions.fromExtensions)) {
|
|
648
|
+
if (extensionKey in operation) {
|
|
649
|
+
annotations[annotationKey] = operation[extensionKey];
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
} else {
|
|
653
|
+
if (operation['x-destructive']) {
|
|
654
|
+
annotations.destructive = operation['x-destructive'];
|
|
655
|
+
}
|
|
656
|
+
if (operation['x-requires-approval']) {
|
|
657
|
+
annotations.requiresApproval = operation['x-requires-approval'];
|
|
658
|
+
}
|
|
659
|
+
if (operation['x-risk-level']) {
|
|
660
|
+
annotations.risk = operation['x-risk-level'];
|
|
661
|
+
}
|
|
662
|
+
if (operation['x-confirm-prompt']) {
|
|
663
|
+
annotations.confirmPrompt = operation['x-confirm-prompt'];
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
return annotations;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Detect authentication from OpenAPI securitySchemes or Swagger securityDefinitions
|
|
672
|
+
*/
|
|
673
|
+
function detectAuth(spec: APISpec, authProvider?: AuthProvider): AuthConfig | undefined {
|
|
674
|
+
let schemeName: string | undefined;
|
|
675
|
+
|
|
676
|
+
// Try to get scheme from security requirements
|
|
677
|
+
if (spec.security && spec.security.length > 0) {
|
|
678
|
+
const securityReq = spec.security[0];
|
|
679
|
+
if (securityReq) {
|
|
680
|
+
schemeName = Object.keys(securityReq)[0];
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
const securitySchemes = isOpenAPI3(spec)
|
|
685
|
+
? spec.components?.securitySchemes
|
|
686
|
+
: isSwagger2(spec)
|
|
687
|
+
? spec.securityDefinitions
|
|
688
|
+
: undefined;
|
|
689
|
+
|
|
690
|
+
if (!schemeName && securitySchemes) {
|
|
691
|
+
const schemes = Object.keys(securitySchemes);
|
|
692
|
+
if (schemes.length > 0) {
|
|
693
|
+
schemeName = schemes[0];
|
|
694
|
+
console.log(
|
|
695
|
+
`[AUTH] No security requirements found, using first securityScheme: ${schemeName}`
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
if (!schemeName || !securitySchemes) {
|
|
701
|
+
return undefined;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const scheme = securitySchemes[schemeName];
|
|
705
|
+
if (!scheme) {
|
|
706
|
+
return undefined;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Get API name for environment variable prefix
|
|
710
|
+
const apiName = spec.info.title.toUpperCase().replace(/[^A-Z0-9]/g, '_');
|
|
711
|
+
|
|
712
|
+
switch (scheme.type) {
|
|
713
|
+
case 'http':
|
|
714
|
+
if (scheme.scheme === 'bearer') {
|
|
715
|
+
const authConfig: BearerAuthConfig = {
|
|
716
|
+
scheme: 'bearer',
|
|
717
|
+
envVar: `${apiName}_TOKEN`,
|
|
718
|
+
};
|
|
719
|
+
console.log(`[AUTH] Detected Bearer token auth: envVar=${authConfig.envVar}`);
|
|
720
|
+
return authConfig;
|
|
721
|
+
} else if (scheme.scheme === 'basic') {
|
|
722
|
+
const authConfig: BasicAuthConfig = {
|
|
723
|
+
scheme: 'basic',
|
|
724
|
+
usernameEnvVar: `${apiName}_USERNAME`,
|
|
725
|
+
passwordEnvVar: `${apiName}_PASSWORD`,
|
|
726
|
+
};
|
|
727
|
+
console.log(
|
|
728
|
+
`[AUTH] Detected Basic auth: username=${authConfig.usernameEnvVar}, password=${authConfig.passwordEnvVar}`
|
|
729
|
+
);
|
|
730
|
+
return authConfig;
|
|
731
|
+
}
|
|
732
|
+
break;
|
|
733
|
+
case 'apiKey': {
|
|
734
|
+
const authConfig: APIKeyAuthConfig = {
|
|
735
|
+
scheme: 'apiKey',
|
|
736
|
+
in: scheme.in === 'query' ? 'query' : 'header',
|
|
737
|
+
name: scheme.name || 'X-API-Key',
|
|
738
|
+
envVar: `${apiName}_API_KEY`,
|
|
739
|
+
};
|
|
740
|
+
return authConfig;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
return undefined;
|
|
744
|
+
}
|