@valon-technologies/gestalt 0.0.1-alpha.11 → 0.0.1-alpha.13
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 +1 -1
- package/gen/v1/agent_pb.ts +1916 -0
- package/gen/v1/authentication_pb.ts +1 -1
- package/gen/v1/authorization_pb.ts +28 -28
- package/gen/v1/cache_pb.ts +4 -4
- package/gen/v1/datastore_pb.ts +10 -10
- package/gen/v1/external_credential_pb.ts +274 -0
- package/gen/v1/plugin_pb.ts +196 -36
- package/gen/v1/pluginruntime_pb.ts +593 -0
- package/gen/v1/runtime_pb.ts +18 -3
- package/gen/v1/s3_pb.ts +19 -19
- package/gen/v1/secrets_pb.ts +1 -1
- package/gen/v1/workflow_pb.ts +501 -68
- package/package.json +1 -1
- package/src/agent-manager.ts +247 -0
- package/src/agent.ts +492 -0
- package/src/authorization.ts +208 -0
- package/src/cache.ts +69 -9
- package/src/http-subject.ts +113 -0
- package/src/index.ts +104 -0
- package/src/indexeddb.ts +59 -6
- package/src/invoker.ts +49 -7
- package/src/manifest-metadata.ts +7 -1
- package/src/plugin.ts +125 -4
- package/src/provider-kind.ts +6 -0
- package/src/provider.ts +12 -1
- package/src/runtime.ts +233 -44
- package/src/s3.ts +90 -32
- package/src/workflow-manager.ts +162 -9
package/src/plugin.ts
CHANGED
|
@@ -23,8 +23,20 @@ import {
|
|
|
23
23
|
type Request,
|
|
24
24
|
responseBrand,
|
|
25
25
|
type Response,
|
|
26
|
+
type Subject,
|
|
26
27
|
} from "./api.ts";
|
|
27
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
cloneHTTPSubjectRequest,
|
|
30
|
+
cloneHTTPSubjectResolutionContext,
|
|
31
|
+
type HTTPSubjectRequest,
|
|
32
|
+
type HTTPSubjectResolutionContext,
|
|
33
|
+
type HTTPSubjectResolver,
|
|
34
|
+
} from "./http-subject.ts";
|
|
35
|
+
import {
|
|
36
|
+
isRuntimeProvider,
|
|
37
|
+
RuntimeProvider,
|
|
38
|
+
type RuntimeProviderOptions,
|
|
39
|
+
} from "./provider.ts";
|
|
28
40
|
import type { Schema } from "./schema.ts";
|
|
29
41
|
|
|
30
42
|
/**
|
|
@@ -84,6 +96,34 @@ export type SessionCatalogHandler = (
|
|
|
84
96
|
request: Request,
|
|
85
97
|
) => MaybePromise<SessionCatalog | null | undefined>;
|
|
86
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Host-managed connection payload passed into a provider post-connect hook.
|
|
101
|
+
*/
|
|
102
|
+
export interface ConnectedToken {
|
|
103
|
+
id: string;
|
|
104
|
+
subjectId: string;
|
|
105
|
+
integration: string;
|
|
106
|
+
connection: string;
|
|
107
|
+
instance: string;
|
|
108
|
+
accessToken: string;
|
|
109
|
+
refreshToken: string;
|
|
110
|
+
scopes: string;
|
|
111
|
+
expiresAt?: Date | undefined;
|
|
112
|
+
lastRefreshedAt?: Date | undefined;
|
|
113
|
+
refreshErrorCount: number;
|
|
114
|
+
metadataJson: string;
|
|
115
|
+
metadata: Record<string, string>;
|
|
116
|
+
createdAt?: Date | undefined;
|
|
117
|
+
updatedAt?: Date | undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Callback used to add derived metadata after a connection is established.
|
|
122
|
+
*/
|
|
123
|
+
export type PostConnectHandler = (
|
|
124
|
+
token: ConnectedToken,
|
|
125
|
+
) => MaybePromise<Record<string, string> | null | undefined>;
|
|
126
|
+
|
|
87
127
|
/**
|
|
88
128
|
* Runtime hooks required to implement a plugin provider.
|
|
89
129
|
*/
|
|
@@ -93,6 +133,8 @@ export interface PluginDefinitionOptions extends RuntimeProviderOptions {
|
|
|
93
133
|
connectionParams?: Record<string, ConnectionParamDefinition>;
|
|
94
134
|
securitySchemes?: Record<string, HTTPSecurityScheme>;
|
|
95
135
|
http?: Record<string, HTTPBinding>;
|
|
136
|
+
resolveHTTPSubject?: HTTPSubjectResolver;
|
|
137
|
+
postConnect?: PostConnectHandler;
|
|
96
138
|
iconSvg?: string;
|
|
97
139
|
operations: Array<OperationDefinition<any, any>>;
|
|
98
140
|
sessionCatalog?: SessionCatalogHandler;
|
|
@@ -149,6 +191,8 @@ export class PluginProvider extends RuntimeProvider {
|
|
|
149
191
|
readonly http: Record<string, HTTPBinding>;
|
|
150
192
|
|
|
151
193
|
private readonly sessionCatalogHandler: SessionCatalogHandler | undefined;
|
|
194
|
+
private readonly httpSubjectResolver: HTTPSubjectResolver | undefined;
|
|
195
|
+
private readonly postConnectHandler: PostConnectHandler | undefined;
|
|
152
196
|
private readonly operations = new Map<string, OperationDefinition<any, any>>();
|
|
153
197
|
|
|
154
198
|
constructor(options: PluginDefinitionOptions) {
|
|
@@ -159,6 +203,8 @@ export class PluginProvider extends RuntimeProvider {
|
|
|
159
203
|
this.connectionParams = normalizeConnectionParams(options.connectionParams);
|
|
160
204
|
this.securitySchemes = normalizeHTTPSecuritySchemes(options.securitySchemes);
|
|
161
205
|
this.http = normalizeHTTPBindings(options.http);
|
|
206
|
+
this.httpSubjectResolver = options.resolveHTTPSubject;
|
|
207
|
+
this.postConnectHandler = options.postConnect;
|
|
162
208
|
this.sessionCatalogHandler = options.sessionCatalog;
|
|
163
209
|
|
|
164
210
|
for (const rawEntry of options.operations) {
|
|
@@ -189,6 +235,36 @@ export class PluginProvider extends RuntimeProvider {
|
|
|
189
235
|
return await this.sessionCatalogHandler?.(request);
|
|
190
236
|
}
|
|
191
237
|
|
|
238
|
+
/**
|
|
239
|
+
* Reports whether the provider exposes a connect-time metadata hook.
|
|
240
|
+
*/
|
|
241
|
+
supportsPostConnect(): boolean {
|
|
242
|
+
return this.postConnectHandler !== undefined;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Computes additional connection metadata after a successful connect flow.
|
|
247
|
+
*/
|
|
248
|
+
async postConnectMetadata(
|
|
249
|
+
token: ConnectedToken,
|
|
250
|
+
): Promise<Record<string, string> | null | undefined> {
|
|
251
|
+
return await this.postConnectHandler?.(cloneConnectedToken(token));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Resolves the concrete Gestalt subject for a verified hosted HTTP request,
|
|
256
|
+
* if the plugin opts into subject resolution.
|
|
257
|
+
*/
|
|
258
|
+
async resolveHTTPSubject(
|
|
259
|
+
request: HTTPSubjectRequest,
|
|
260
|
+
context: HTTPSubjectResolutionContext,
|
|
261
|
+
): Promise<Subject | null | undefined> {
|
|
262
|
+
return await this.httpSubjectResolver?.(
|
|
263
|
+
cloneHTTPSubjectRequest(request),
|
|
264
|
+
cloneHTTPSubjectResolutionContext(context),
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
192
268
|
/**
|
|
193
269
|
* Returns the static catalog emitted during provider startup.
|
|
194
270
|
*/
|
|
@@ -361,12 +437,27 @@ export function isPluginProvider(
|
|
|
361
437
|
): value is PluginProvider {
|
|
362
438
|
return (
|
|
363
439
|
value instanceof PluginProvider ||
|
|
364
|
-
(
|
|
365
|
-
value !== null &&
|
|
440
|
+
(isRuntimeProvider(value) &&
|
|
366
441
|
"kind" in value &&
|
|
367
442
|
(value as { kind?: unknown }).kind === "integration" &&
|
|
368
443
|
"staticCatalog" in value &&
|
|
369
|
-
"
|
|
444
|
+
typeof (value as { staticCatalog?: unknown }).staticCatalog === "function" &&
|
|
445
|
+
"execute" in value &&
|
|
446
|
+
typeof (value as { execute?: unknown }).execute === "function" &&
|
|
447
|
+
"supportsSessionCatalog" in value &&
|
|
448
|
+
typeof (value as { supportsSessionCatalog?: unknown }).supportsSessionCatalog === "function" &&
|
|
449
|
+
"catalogForRequest" in value &&
|
|
450
|
+
typeof (value as { catalogForRequest?: unknown }).catalogForRequest === "function" &&
|
|
451
|
+
"supportsManifestMetadata" in value &&
|
|
452
|
+
typeof (value as { supportsManifestMetadata?: unknown }).supportsManifestMetadata === "function" &&
|
|
453
|
+
"writeManifestMetadata" in value &&
|
|
454
|
+
typeof (value as { writeManifestMetadata?: unknown }).writeManifestMetadata === "function" &&
|
|
455
|
+
"supportsPostConnect" in value &&
|
|
456
|
+
typeof (value as { supportsPostConnect?: unknown }).supportsPostConnect === "function" &&
|
|
457
|
+
"postConnectMetadata" in value &&
|
|
458
|
+
typeof (value as { postConnectMetadata?: unknown }).postConnectMetadata === "function" &&
|
|
459
|
+
"resolveHTTPSubject" in value &&
|
|
460
|
+
typeof (value as { resolveHTTPSubject?: unknown }).resolveHTTPSubject === "function")
|
|
370
461
|
);
|
|
371
462
|
}
|
|
372
463
|
|
|
@@ -396,6 +487,21 @@ function normalizeConnectionParams(
|
|
|
396
487
|
return output;
|
|
397
488
|
}
|
|
398
489
|
|
|
490
|
+
function cloneConnectedToken(token: ConnectedToken): ConnectedToken {
|
|
491
|
+
return {
|
|
492
|
+
...token,
|
|
493
|
+
metadata: {
|
|
494
|
+
...(token.metadata ?? {}),
|
|
495
|
+
},
|
|
496
|
+
expiresAt: token.expiresAt ? new Date(token.expiresAt) : undefined,
|
|
497
|
+
lastRefreshedAt: token.lastRefreshedAt
|
|
498
|
+
? new Date(token.lastRefreshedAt)
|
|
499
|
+
: undefined,
|
|
500
|
+
createdAt: token.createdAt ? new Date(token.createdAt) : undefined,
|
|
501
|
+
updatedAt: token.updatedAt ? new Date(token.updatedAt) : undefined,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
|
|
399
505
|
function normalizeHTTPSecuritySchemes(
|
|
400
506
|
input: Record<string, HTTPSecurityScheme> | undefined,
|
|
401
507
|
): Record<string, HTTPSecurityScheme> {
|
|
@@ -424,6 +530,21 @@ function cloneHTTPSecurityScheme(value: HTTPSecurityScheme): HTTPSecurityScheme
|
|
|
424
530
|
if (value.description !== undefined) {
|
|
425
531
|
output.description = value.description;
|
|
426
532
|
}
|
|
533
|
+
if (value.signatureHeader !== undefined) {
|
|
534
|
+
output.signatureHeader = value.signatureHeader;
|
|
535
|
+
}
|
|
536
|
+
if (value.signaturePrefix !== undefined) {
|
|
537
|
+
output.signaturePrefix = value.signaturePrefix;
|
|
538
|
+
}
|
|
539
|
+
if (value.payloadTemplate !== undefined) {
|
|
540
|
+
output.payloadTemplate = value.payloadTemplate;
|
|
541
|
+
}
|
|
542
|
+
if (value.timestampHeader !== undefined) {
|
|
543
|
+
output.timestampHeader = value.timestampHeader;
|
|
544
|
+
}
|
|
545
|
+
if (value.maxAgeSeconds !== undefined) {
|
|
546
|
+
output.maxAgeSeconds = value.maxAgeSeconds;
|
|
547
|
+
}
|
|
427
548
|
if (value.name !== undefined) {
|
|
428
549
|
output.name = value.name;
|
|
429
550
|
}
|
package/src/provider-kind.ts
CHANGED
|
@@ -44,6 +44,12 @@ const PROVIDER_KIND_DEFINITIONS = {
|
|
|
44
44
|
defaultExportNames: ["workflow", "provider"],
|
|
45
45
|
label: "workflow provider",
|
|
46
46
|
},
|
|
47
|
+
agent: {
|
|
48
|
+
tokens: ["agent"],
|
|
49
|
+
formatToken: "agent",
|
|
50
|
+
defaultExportNames: ["agent", "provider"],
|
|
51
|
+
label: "agent provider",
|
|
52
|
+
},
|
|
47
53
|
telemetry: {
|
|
48
54
|
tokens: ["telemetry"],
|
|
49
55
|
formatToken: "telemetry",
|
package/src/provider.ts
CHANGED
|
@@ -10,6 +10,7 @@ export type ProviderKind =
|
|
|
10
10
|
| "secrets"
|
|
11
11
|
| "s3"
|
|
12
12
|
| "workflow"
|
|
13
|
+
| "agent"
|
|
13
14
|
| "telemetry";
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -151,7 +152,17 @@ export function isRuntimeProvider(value: unknown): value is RuntimeProvider {
|
|
|
151
152
|
value !== null &&
|
|
152
153
|
"kind" in value &&
|
|
153
154
|
"resolveName" in value &&
|
|
154
|
-
"
|
|
155
|
+
typeof (value as { resolveName?: unknown }).resolveName === "function" &&
|
|
156
|
+
"configureProvider" in value &&
|
|
157
|
+
typeof (value as { configureProvider?: unknown }).configureProvider === "function" &&
|
|
158
|
+
"supportsHealthCheck" in value &&
|
|
159
|
+
typeof (value as { supportsHealthCheck?: unknown }).supportsHealthCheck === "function" &&
|
|
160
|
+
"healthCheck" in value &&
|
|
161
|
+
typeof (value as { healthCheck?: unknown }).healthCheck === "function" &&
|
|
162
|
+
"warnings" in value &&
|
|
163
|
+
typeof (value as { warnings?: unknown }).warnings === "function" &&
|
|
164
|
+
"closeProvider" in value &&
|
|
165
|
+
typeof (value as { closeProvider?: unknown }).closeProvider === "function")
|
|
155
166
|
);
|
|
156
167
|
}
|
|
157
168
|
|
package/src/runtime.ts
CHANGED
|
@@ -12,6 +12,9 @@ import {
|
|
|
12
12
|
} from "@connectrpc/connect";
|
|
13
13
|
import { connectNodeAdapter } from "@connectrpc/connect-node";
|
|
14
14
|
|
|
15
|
+
import {
|
|
16
|
+
AgentProvider as AgentProviderService,
|
|
17
|
+
} from "../gen/v1/agent_pb.ts";
|
|
15
18
|
import {
|
|
16
19
|
AuthenticationProvider as AuthenticationProviderService,
|
|
17
20
|
AuthSessionSettingsSchema,
|
|
@@ -40,9 +43,14 @@ import {
|
|
|
40
43
|
CatalogSchema as ProtoCatalogSchema,
|
|
41
44
|
ConnectionMode as ProviderConnectionMode,
|
|
42
45
|
GetSessionCatalogResponseSchema,
|
|
46
|
+
PostConnectResponseSchema,
|
|
47
|
+
ResolveHTTPSubjectResponseSchema,
|
|
43
48
|
OperationResultSchema,
|
|
44
49
|
ProviderMetadataSchema,
|
|
50
|
+
type HTTPSubjectRequest as ProtoHTTPSubjectRequest,
|
|
51
|
+
type IntegrationToken as ProtoIntegrationToken,
|
|
45
52
|
type RequestContext as ProtoRequestContext,
|
|
53
|
+
type ResolveHTTPSubjectRequest as ProtoResolveHTTPSubjectRequest,
|
|
46
54
|
IntegrationProvider as IntegrationProviderService,
|
|
47
55
|
StartProviderResponseSchema,
|
|
48
56
|
type ExecuteRequest,
|
|
@@ -60,6 +68,11 @@ import {
|
|
|
60
68
|
import { S3 as S3Service } from "../gen/v1/s3_pb.ts";
|
|
61
69
|
import { WorkflowProvider as WorkflowProviderService } from "../gen/v1/workflow_pb.ts";
|
|
62
70
|
import { errorMessage, type Request } from "./api.ts";
|
|
71
|
+
import {
|
|
72
|
+
AgentProvider,
|
|
73
|
+
createAgentProviderService,
|
|
74
|
+
isAgentProvider,
|
|
75
|
+
} from "./agent.ts";
|
|
63
76
|
import {
|
|
64
77
|
AuthenticationProvider,
|
|
65
78
|
isAuthenticationProvider,
|
|
@@ -69,6 +82,12 @@ import { CacheProvider, isCacheProvider } from "./cache.ts";
|
|
|
69
82
|
import { SecretsProvider, isSecretsProvider } from "./secrets.ts";
|
|
70
83
|
import { catalogToYaml, type Catalog } from "./catalog.ts";
|
|
71
84
|
import {
|
|
85
|
+
HTTPSubjectResolutionError,
|
|
86
|
+
type HTTPSubjectRequest,
|
|
87
|
+
type HTTPSubjectResolutionContext,
|
|
88
|
+
} from "./http-subject.ts";
|
|
89
|
+
import {
|
|
90
|
+
type ConnectedToken,
|
|
72
91
|
PluginProvider,
|
|
73
92
|
connectionModeToProtoValue,
|
|
74
93
|
connectionParamToProto,
|
|
@@ -119,6 +138,7 @@ export const CURRENT_PROTOCOL_VERSION = 3;
|
|
|
119
138
|
* Command-line usage for the runtime entrypoint.
|
|
120
139
|
*/
|
|
121
140
|
export const USAGE = "usage: bun run runtime.ts ROOT PROVIDER_TARGET";
|
|
141
|
+
export { createAgentProviderService } from "./agent.ts";
|
|
122
142
|
export { createWorkflowProviderService } from "./workflow.ts";
|
|
123
143
|
|
|
124
144
|
/**
|
|
@@ -138,6 +158,7 @@ export type LoadedProvider =
|
|
|
138
158
|
| CacheProvider
|
|
139
159
|
| SecretsProvider
|
|
140
160
|
| S3Provider
|
|
161
|
+
| AgentProvider
|
|
141
162
|
| WorkflowProvider;
|
|
142
163
|
|
|
143
164
|
type ProviderRuntimeEntry = {
|
|
@@ -197,6 +218,16 @@ const PROVIDER_RUNTIME_ENTRIES: Partial<
|
|
|
197
218
|
router.service(S3Service, createS3Service(provider as S3Provider));
|
|
198
219
|
},
|
|
199
220
|
},
|
|
221
|
+
agent: {
|
|
222
|
+
isProvider: isAgentProvider as (value: unknown) => value is LoadedProvider,
|
|
223
|
+
protoKind: ProtoProviderKind.AGENT,
|
|
224
|
+
registerService(router, provider) {
|
|
225
|
+
router.service(
|
|
226
|
+
AgentProviderService,
|
|
227
|
+
createAgentProviderService(provider as AgentProvider),
|
|
228
|
+
);
|
|
229
|
+
},
|
|
230
|
+
},
|
|
200
231
|
workflow: {
|
|
201
232
|
isProvider: isWorkflowProvider as (value: unknown) => value is LoadedProvider,
|
|
202
233
|
protoKind: ProtoProviderKind.WORKFLOW,
|
|
@@ -404,16 +435,20 @@ export function createRuntimeService(
|
|
|
404
435
|
): Partial<ServiceImpl<typeof ProviderLifecycle>> {
|
|
405
436
|
return {
|
|
406
437
|
async getProviderIdentity() {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
438
|
+
try {
|
|
439
|
+
return create(ProviderIdentitySchema, {
|
|
440
|
+
kind: providerRuntimeEntry(provider.kind).protoKind,
|
|
441
|
+
name: provider.name,
|
|
442
|
+
displayName: provider.displayName,
|
|
443
|
+
description: provider.description,
|
|
444
|
+
version: provider.version,
|
|
445
|
+
warnings: await provider.warnings(),
|
|
446
|
+
minProtocolVersion: CURRENT_PROTOCOL_VERSION,
|
|
447
|
+
maxProtocolVersion: CURRENT_PROTOCOL_VERSION,
|
|
448
|
+
});
|
|
449
|
+
} catch (error) {
|
|
450
|
+
throw providerRuntimeError("provider identity", error);
|
|
451
|
+
}
|
|
417
452
|
},
|
|
418
453
|
async configureProvider(request: ConfigureProviderRequest) {
|
|
419
454
|
assertProtocolVersion(request.protocolVersion);
|
|
@@ -423,10 +458,7 @@ export function createRuntimeService(
|
|
|
423
458
|
objectFromUnknown(request.config),
|
|
424
459
|
);
|
|
425
460
|
} catch (error) {
|
|
426
|
-
throw
|
|
427
|
-
`configure provider: ${errorMessage(error)}`,
|
|
428
|
-
Code.Unknown,
|
|
429
|
-
);
|
|
461
|
+
throw providerRuntimeError("configure provider", error);
|
|
430
462
|
}
|
|
431
463
|
return create(ConfigureProviderResponseSchema, {
|
|
432
464
|
protocolVersion: CURRENT_PROTOCOL_VERSION,
|
|
@@ -465,27 +497,31 @@ export function createProviderService(
|
|
|
465
497
|
throw new Error("provider is not a plugin provider");
|
|
466
498
|
}
|
|
467
499
|
return {
|
|
468
|
-
getMetadata() {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
Object.
|
|
479
|
-
key,
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
500
|
+
async getMetadata() {
|
|
501
|
+
try {
|
|
502
|
+
return create(ProviderMetadataSchema, {
|
|
503
|
+
name: provider.name,
|
|
504
|
+
displayName: provider.displayName,
|
|
505
|
+
description: provider.description,
|
|
506
|
+
connectionMode: connectionModeToProtoValue(
|
|
507
|
+
provider.connectionMode,
|
|
508
|
+
) as ProviderConnectionMode,
|
|
509
|
+
authTypes: [...provider.authTypes],
|
|
510
|
+
connectionParams: Object.fromEntries(
|
|
511
|
+
Object.entries(provider.connectionParams).map(([key, value]) => [
|
|
512
|
+
key,
|
|
513
|
+
connectionParamToProto(value),
|
|
514
|
+
]),
|
|
515
|
+
),
|
|
516
|
+
staticCatalog: catalogToProto(provider.staticCatalog()),
|
|
517
|
+
supportsSessionCatalog: provider.supportsSessionCatalog(),
|
|
518
|
+
supportsPostConnect: provider.supportsPostConnect(),
|
|
519
|
+
minProtocolVersion: CURRENT_PROTOCOL_VERSION,
|
|
520
|
+
maxProtocolVersion: CURRENT_PROTOCOL_VERSION,
|
|
521
|
+
});
|
|
522
|
+
} catch (error) {
|
|
523
|
+
throw providerRuntimeError("provider metadata", error);
|
|
524
|
+
}
|
|
489
525
|
},
|
|
490
526
|
async startProvider(request: StartProviderRequest) {
|
|
491
527
|
assertProtocolVersion(request.protocolVersion);
|
|
@@ -495,10 +531,7 @@ export function createProviderService(
|
|
|
495
531
|
objectFromUnknown(request.config),
|
|
496
532
|
);
|
|
497
533
|
} catch (error) {
|
|
498
|
-
throw
|
|
499
|
-
`configure provider: ${errorMessage(error)}`,
|
|
500
|
-
Code.Unknown,
|
|
501
|
-
);
|
|
534
|
+
throw providerRuntimeError("configure provider", error);
|
|
502
535
|
}
|
|
503
536
|
return create(StartProviderResponseSchema, {
|
|
504
537
|
protocolVersion: CURRENT_PROTOCOL_VERSION,
|
|
@@ -519,6 +552,36 @@ export function createProviderService(
|
|
|
519
552
|
),
|
|
520
553
|
);
|
|
521
554
|
},
|
|
555
|
+
async resolveHTTPSubject(request: ProtoResolveHTTPSubjectRequest) {
|
|
556
|
+
let subject;
|
|
557
|
+
try {
|
|
558
|
+
subject = await provider.resolveHTTPSubject(
|
|
559
|
+
providerHTTPSubjectRequest(request.request),
|
|
560
|
+
providerHTTPSubjectResolutionContext(request.context),
|
|
561
|
+
);
|
|
562
|
+
} catch (error) {
|
|
563
|
+
if (error instanceof HTTPSubjectResolutionError) {
|
|
564
|
+
return create(ResolveHTTPSubjectResponseSchema, {
|
|
565
|
+
rejectStatus: error.status,
|
|
566
|
+
rejectMessage: error.message,
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
throw new ConnectError(
|
|
570
|
+
`resolve http subject: ${errorMessage(error)}`,
|
|
571
|
+
Code.Unknown,
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
return create(ResolveHTTPSubjectResponseSchema, subject
|
|
575
|
+
? {
|
|
576
|
+
subject: {
|
|
577
|
+
id: subject.id,
|
|
578
|
+
kind: subject.kind,
|
|
579
|
+
displayName: subject.displayName,
|
|
580
|
+
authSource: subject.authSource,
|
|
581
|
+
},
|
|
582
|
+
}
|
|
583
|
+
: {});
|
|
584
|
+
},
|
|
522
585
|
async getSessionCatalog(request: GetSessionCatalogRequest) {
|
|
523
586
|
let catalog: Catalog | Record<string, unknown> | null | undefined;
|
|
524
587
|
try {
|
|
@@ -545,11 +608,29 @@ export function createProviderService(
|
|
|
545
608
|
catalog: catalogToProto(catalog),
|
|
546
609
|
});
|
|
547
610
|
},
|
|
548
|
-
async postConnect() {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
611
|
+
async postConnect(request) {
|
|
612
|
+
if (!provider.supportsPostConnect()) {
|
|
613
|
+
throw new ConnectError(
|
|
614
|
+
"provider does not support post connect",
|
|
615
|
+
Code.Unimplemented,
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
let metadata: Record<string, string> | null | undefined;
|
|
619
|
+
try {
|
|
620
|
+
metadata = await provider.postConnectMetadata(
|
|
621
|
+
providerConnectedToken(request.token),
|
|
622
|
+
);
|
|
623
|
+
} catch (error) {
|
|
624
|
+
throw new ConnectError(
|
|
625
|
+
`post connect: ${errorMessage(error)}`,
|
|
626
|
+
Code.Unknown,
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
return create(PostConnectResponseSchema, {
|
|
630
|
+
metadata: {
|
|
631
|
+
...(metadata ?? {}),
|
|
632
|
+
},
|
|
633
|
+
});
|
|
553
634
|
},
|
|
554
635
|
};
|
|
555
636
|
}
|
|
@@ -752,6 +833,71 @@ function providerRequest(
|
|
|
752
833
|
};
|
|
753
834
|
}
|
|
754
835
|
|
|
836
|
+
function providerHTTPSubjectRequest(
|
|
837
|
+
request?: ProtoHTTPSubjectRequest,
|
|
838
|
+
): HTTPSubjectRequest {
|
|
839
|
+
return {
|
|
840
|
+
binding: request?.binding ?? "",
|
|
841
|
+
method: request?.method ?? "",
|
|
842
|
+
path: request?.path ?? "",
|
|
843
|
+
contentType: request?.contentType ?? "",
|
|
844
|
+
headers: providerStringLists(request?.headers),
|
|
845
|
+
query: providerStringLists(request?.query),
|
|
846
|
+
params: objectFromUnknown(request?.params),
|
|
847
|
+
rawBody: new Uint8Array(request?.rawBody ?? new Uint8Array()),
|
|
848
|
+
securityScheme: request?.securityScheme ?? "",
|
|
849
|
+
verifiedSubject: request?.verifiedSubject ?? "",
|
|
850
|
+
verifiedClaims: {
|
|
851
|
+
...(request?.verifiedClaims ?? {}),
|
|
852
|
+
},
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
function providerHTTPSubjectResolutionContext(
|
|
857
|
+
requestContext?: ProtoRequestContext,
|
|
858
|
+
): HTTPSubjectResolutionContext {
|
|
859
|
+
const request = providerRequest("", {}, requestContext);
|
|
860
|
+
return {
|
|
861
|
+
subject: request.subject,
|
|
862
|
+
credential: request.credential,
|
|
863
|
+
access: request.access,
|
|
864
|
+
workflow: request.workflow,
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
function providerConnectedToken(
|
|
869
|
+
token?: ProtoIntegrationToken,
|
|
870
|
+
): ConnectedToken {
|
|
871
|
+
const metadataJson = token?.metadataJson ?? "";
|
|
872
|
+
return {
|
|
873
|
+
id: token?.id ?? "",
|
|
874
|
+
subjectId: token?.userId ?? "",
|
|
875
|
+
integration: token?.integration ?? "",
|
|
876
|
+
connection: token?.connection ?? "",
|
|
877
|
+
instance: token?.instance ?? "",
|
|
878
|
+
accessToken: token?.accessToken ?? "",
|
|
879
|
+
refreshToken: token?.refreshToken ?? "",
|
|
880
|
+
scopes: token?.scopes ?? "",
|
|
881
|
+
expiresAt: timestampToDate(token?.expiresAt),
|
|
882
|
+
lastRefreshedAt: timestampToDate(token?.lastRefreshedAt),
|
|
883
|
+
refreshErrorCount: token?.refreshErrorCount ?? 0,
|
|
884
|
+
metadataJson,
|
|
885
|
+
metadata: stringRecordFromJSON(metadataJson),
|
|
886
|
+
createdAt: timestampToDate(token?.createdAt),
|
|
887
|
+
updatedAt: timestampToDate(token?.updatedAt),
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function providerStringLists(
|
|
892
|
+
input: Record<string, { values?: string[] }> | undefined,
|
|
893
|
+
): Record<string, string[]> {
|
|
894
|
+
const output: Record<string, string[]> = {};
|
|
895
|
+
for (const [key, value] of Object.entries(input ?? {})) {
|
|
896
|
+
output[key] = [...(value.values ?? [])];
|
|
897
|
+
}
|
|
898
|
+
return output;
|
|
899
|
+
}
|
|
900
|
+
|
|
755
901
|
function providerRuntimeEntry(
|
|
756
902
|
kind: ProviderKind,
|
|
757
903
|
): ProviderRuntimeEntry {
|
|
@@ -764,6 +910,10 @@ function providerRuntimeEntry(
|
|
|
764
910
|
return entry;
|
|
765
911
|
}
|
|
766
912
|
|
|
913
|
+
function providerRuntimeError(label: string, error: unknown): ConnectError {
|
|
914
|
+
return new ConnectError(`${label}: ${errorMessage(error)}`, Code.Unknown);
|
|
915
|
+
}
|
|
916
|
+
|
|
767
917
|
function resolveLoadedProvider(
|
|
768
918
|
candidate: unknown,
|
|
769
919
|
kind: ProviderKind,
|
|
@@ -850,6 +1000,45 @@ function normalizeBigInt(value: number | bigint): bigint {
|
|
|
850
1000
|
return BigInt(Math.max(0, Math.trunc(value)));
|
|
851
1001
|
}
|
|
852
1002
|
|
|
1003
|
+
function timestampToDate(
|
|
1004
|
+
value: { seconds: bigint; nanos: number } | undefined,
|
|
1005
|
+
): Date | undefined {
|
|
1006
|
+
if (!value) {
|
|
1007
|
+
return undefined;
|
|
1008
|
+
}
|
|
1009
|
+
const seconds = Number(value.seconds ?? 0n);
|
|
1010
|
+
const nanos = Number(value.nanos ?? 0);
|
|
1011
|
+
if (!Number.isFinite(seconds) || !Number.isFinite(nanos)) {
|
|
1012
|
+
return undefined;
|
|
1013
|
+
}
|
|
1014
|
+
const millis = (seconds * 1000) + Math.trunc(nanos / 1_000_000);
|
|
1015
|
+
if (!Number.isFinite(millis)) {
|
|
1016
|
+
return undefined;
|
|
1017
|
+
}
|
|
1018
|
+
return new Date(millis);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
function stringRecordFromJSON(value: string): Record<string, string> {
|
|
1022
|
+
if (!value.trim()) {
|
|
1023
|
+
return {};
|
|
1024
|
+
}
|
|
1025
|
+
try {
|
|
1026
|
+
const parsed = JSON.parse(value) as unknown;
|
|
1027
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1028
|
+
return {};
|
|
1029
|
+
}
|
|
1030
|
+
const output: Record<string, string> = {};
|
|
1031
|
+
for (const [key, entry] of Object.entries(parsed)) {
|
|
1032
|
+
if (typeof entry === "string") {
|
|
1033
|
+
output[key] = entry;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
return output;
|
|
1037
|
+
} catch {
|
|
1038
|
+
return {};
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
853
1042
|
function cloneUint8Array(value: Uint8Array | undefined): Uint8Array {
|
|
854
1043
|
if (!value) {
|
|
855
1044
|
return new Uint8Array();
|