@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/cache.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { connect } from "node:net";
|
|
2
2
|
|
|
3
|
-
import { createClient, type Client } from "@connectrpc/connect";
|
|
3
|
+
import { createClient, type Client, type Interceptor } from "@connectrpc/connect";
|
|
4
4
|
import { createGrpcTransport } from "@connectrpc/connect-node";
|
|
5
5
|
|
|
6
6
|
import { Cache as CacheService } from "../gen/v1/cache_pb.ts";
|
|
7
7
|
import { RuntimeProvider, type RuntimeProviderOptions } from "./provider.ts";
|
|
8
8
|
import type { MaybePromise } from "./api.ts";
|
|
9
9
|
|
|
10
|
-
const ENV_CACHE_SOCKET = "GESTALT_CACHE_SOCKET";
|
|
10
|
+
export const ENV_CACHE_SOCKET = "GESTALT_CACHE_SOCKET";
|
|
11
|
+
const CACHE_SOCKET_TOKEN_SUFFIX = "_TOKEN";
|
|
12
|
+
const CACHE_RELAY_TOKEN_HEADER = "x-gestalt-host-service-relay-token";
|
|
13
|
+
export const ENV_CACHE_SOCKET_TOKEN = `${ENV_CACHE_SOCKET}${CACHE_SOCKET_TOKEN_SUFFIX}`;
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
16
|
* Single cache entry used by batch cache APIs.
|
|
@@ -55,6 +58,49 @@ export function cacheSocketEnv(name?: string): string {
|
|
|
55
58
|
return `${ENV_CACHE_SOCKET}_${trimmed.replace(/[^A-Za-z0-9]/gu, "_").toUpperCase()}`;
|
|
56
59
|
}
|
|
57
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Returns the environment variable name used to discover a cache relay token.
|
|
63
|
+
*/
|
|
64
|
+
export function cacheSocketTokenEnv(name?: string): string {
|
|
65
|
+
return `${cacheSocketEnv(name)}${CACHE_SOCKET_TOKEN_SUFFIX}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function cacheTransportOptions(rawTarget: string): {
|
|
69
|
+
baseUrl: string;
|
|
70
|
+
nodeOptions?: { path: string };
|
|
71
|
+
} {
|
|
72
|
+
const target = rawTarget.trim();
|
|
73
|
+
if (!target) {
|
|
74
|
+
throw new Error("cache transport target is required");
|
|
75
|
+
}
|
|
76
|
+
if (target.startsWith("tcp://")) {
|
|
77
|
+
const address = target.slice("tcp://".length).trim();
|
|
78
|
+
if (!address) {
|
|
79
|
+
throw new Error(`cache tcp target ${JSON.stringify(rawTarget)} is missing host:port`);
|
|
80
|
+
}
|
|
81
|
+
return { baseUrl: `http://${address}` };
|
|
82
|
+
}
|
|
83
|
+
if (target.startsWith("tls://")) {
|
|
84
|
+
const address = target.slice("tls://".length).trim();
|
|
85
|
+
if (!address) {
|
|
86
|
+
throw new Error(`cache tls target ${JSON.stringify(rawTarget)} is missing host:port`);
|
|
87
|
+
}
|
|
88
|
+
return { baseUrl: `https://${address}` };
|
|
89
|
+
}
|
|
90
|
+
if (target.startsWith("unix://")) {
|
|
91
|
+
const socketPath = target.slice("unix://".length).trim();
|
|
92
|
+
if (!socketPath) {
|
|
93
|
+
throw new Error(`cache unix target ${JSON.stringify(rawTarget)} is missing a socket path`);
|
|
94
|
+
}
|
|
95
|
+
return { baseUrl: "http://localhost", nodeOptions: { path: socketPath } };
|
|
96
|
+
}
|
|
97
|
+
if (target.includes("://")) {
|
|
98
|
+
const parsed = new URL(target);
|
|
99
|
+
throw new Error(`Unsupported cache target scheme ${JSON.stringify(parsed.protocol.replace(/:$/, ""))}`);
|
|
100
|
+
}
|
|
101
|
+
return { baseUrl: "http://localhost", nodeOptions: { path: target } };
|
|
102
|
+
}
|
|
103
|
+
|
|
58
104
|
/**
|
|
59
105
|
* Client for invoking a host-provided cache over the Gestalt transport.
|
|
60
106
|
*
|
|
@@ -71,16 +117,30 @@ export class Cache {
|
|
|
71
117
|
|
|
72
118
|
constructor(name?: string) {
|
|
73
119
|
const envName = cacheSocketEnv(name);
|
|
74
|
-
const
|
|
75
|
-
if (!
|
|
120
|
+
const target = process.env[envName];
|
|
121
|
+
if (!target) {
|
|
76
122
|
throw new Error(`cache: ${envName} is not set`);
|
|
77
123
|
}
|
|
78
|
-
|
|
124
|
+
const token = process.env[cacheSocketTokenEnv(name)]?.trim() ?? "";
|
|
125
|
+
const transportOptions = cacheTransportOptions(target);
|
|
126
|
+
const interceptors: Interceptor[] = token
|
|
127
|
+
? [
|
|
128
|
+
(next) => async (req) => {
|
|
129
|
+
req.header.set(CACHE_RELAY_TOKEN_HEADER, token);
|
|
130
|
+
return await next(req);
|
|
131
|
+
},
|
|
132
|
+
]
|
|
133
|
+
: [];
|
|
79
134
|
const transport = createGrpcTransport({
|
|
80
|
-
|
|
81
|
-
nodeOptions
|
|
82
|
-
|
|
83
|
-
|
|
135
|
+
...transportOptions,
|
|
136
|
+
...(transportOptions.nodeOptions
|
|
137
|
+
? {
|
|
138
|
+
nodeOptions: {
|
|
139
|
+
createConnection: () => connect(transportOptions.nodeOptions!.path),
|
|
140
|
+
},
|
|
141
|
+
}
|
|
142
|
+
: {}),
|
|
143
|
+
interceptors,
|
|
84
144
|
});
|
|
85
145
|
this.client = createClient(CacheService, transport);
|
|
86
146
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { Access, Credential, MaybePromise, Subject } from "./api.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Verified hosted HTTP request metadata passed into optional plugin-local
|
|
5
|
+
* subject resolution hooks before normal operation dispatch.
|
|
6
|
+
*/
|
|
7
|
+
export interface HTTPSubjectRequest {
|
|
8
|
+
binding: string;
|
|
9
|
+
method: string;
|
|
10
|
+
path: string;
|
|
11
|
+
contentType: string;
|
|
12
|
+
headers: Record<string, string[]>;
|
|
13
|
+
query: Record<string, string[]>;
|
|
14
|
+
params: Record<string, unknown>;
|
|
15
|
+
rawBody: Uint8Array;
|
|
16
|
+
securityScheme: string;
|
|
17
|
+
verifiedSubject: string;
|
|
18
|
+
verifiedClaims: Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Request-scoped caller context available while resolving the concrete subject
|
|
23
|
+
* for a hosted HTTP request.
|
|
24
|
+
*/
|
|
25
|
+
export interface HTTPSubjectResolutionContext {
|
|
26
|
+
subject: Subject;
|
|
27
|
+
credential: Credential;
|
|
28
|
+
access: Access;
|
|
29
|
+
workflow: Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Explicit HTTP rejection surfaced from a hosted HTTP subject resolver.
|
|
34
|
+
*/
|
|
35
|
+
export class HTTPSubjectResolutionError extends Error {
|
|
36
|
+
readonly status: number;
|
|
37
|
+
|
|
38
|
+
constructor(status: number, message: string) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.name = "HTTPSubjectResolutionError";
|
|
41
|
+
this.status = status;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Creates an explicit hosted HTTP subject-resolution rejection.
|
|
47
|
+
*/
|
|
48
|
+
export function httpSubjectError(
|
|
49
|
+
status: number,
|
|
50
|
+
message: string,
|
|
51
|
+
): HTTPSubjectResolutionError {
|
|
52
|
+
return new HTTPSubjectResolutionError(status, message);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Optional hook that maps a verified hosted HTTP request to a concrete Gestalt
|
|
57
|
+
* subject before the target operation is authorized and executed.
|
|
58
|
+
*/
|
|
59
|
+
export type HTTPSubjectResolver = (
|
|
60
|
+
request: HTTPSubjectRequest,
|
|
61
|
+
context: HTTPSubjectResolutionContext,
|
|
62
|
+
) => MaybePromise<Subject | null | undefined>;
|
|
63
|
+
|
|
64
|
+
export function cloneHTTPSubjectRequest(
|
|
65
|
+
input: HTTPSubjectRequest,
|
|
66
|
+
): HTTPSubjectRequest {
|
|
67
|
+
return {
|
|
68
|
+
binding: input.binding,
|
|
69
|
+
method: input.method,
|
|
70
|
+
path: input.path,
|
|
71
|
+
contentType: input.contentType,
|
|
72
|
+
headers: cloneStringLists(input.headers),
|
|
73
|
+
query: cloneStringLists(input.query),
|
|
74
|
+
params: {
|
|
75
|
+
...input.params,
|
|
76
|
+
},
|
|
77
|
+
rawBody: new Uint8Array(input.rawBody),
|
|
78
|
+
securityScheme: input.securityScheme,
|
|
79
|
+
verifiedSubject: input.verifiedSubject,
|
|
80
|
+
verifiedClaims: {
|
|
81
|
+
...input.verifiedClaims,
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function cloneHTTPSubjectResolutionContext(
|
|
87
|
+
input: HTTPSubjectResolutionContext,
|
|
88
|
+
): HTTPSubjectResolutionContext {
|
|
89
|
+
return {
|
|
90
|
+
subject: {
|
|
91
|
+
...input.subject,
|
|
92
|
+
},
|
|
93
|
+
credential: {
|
|
94
|
+
...input.credential,
|
|
95
|
+
},
|
|
96
|
+
access: {
|
|
97
|
+
...input.access,
|
|
98
|
+
},
|
|
99
|
+
workflow: {
|
|
100
|
+
...input.workflow,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function cloneStringLists(
|
|
106
|
+
input: Record<string, string[]>,
|
|
107
|
+
): Record<string, string[]> {
|
|
108
|
+
const output: Record<string, string[]> = {};
|
|
109
|
+
for (const [key, value] of Object.entries(input)) {
|
|
110
|
+
output[key] = [...value];
|
|
111
|
+
}
|
|
112
|
+
return output;
|
|
113
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -28,6 +28,23 @@
|
|
|
28
28
|
* import { parseRuntimeArgs, serve } from "@valon-technologies/gestalt/runtime";
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
|
+
export {
|
|
32
|
+
Authorization,
|
|
33
|
+
AuthorizationClient,
|
|
34
|
+
ENV_AUTHORIZATION_SOCKET,
|
|
35
|
+
ENV_AUTHORIZATION_SOCKET_TOKEN,
|
|
36
|
+
type AuthorizationActionSearchMessage,
|
|
37
|
+
type AuthorizationDecisionMessage,
|
|
38
|
+
type AuthorizationEvaluateInput,
|
|
39
|
+
type AuthorizationMetadataMessage,
|
|
40
|
+
type AuthorizationReadRelationshipsInput,
|
|
41
|
+
type AuthorizationReadRelationshipsMessage,
|
|
42
|
+
type AuthorizationResourceSearchMessage,
|
|
43
|
+
type AuthorizationSearchActionsInput,
|
|
44
|
+
type AuthorizationSearchResourcesInput,
|
|
45
|
+
type AuthorizationSearchSubjectsInput,
|
|
46
|
+
type AuthorizationSubjectSearchMessage,
|
|
47
|
+
} from "./authorization.ts";
|
|
31
48
|
export {
|
|
32
49
|
connectionParam,
|
|
33
50
|
ok,
|
|
@@ -42,6 +59,13 @@ export {
|
|
|
42
59
|
type Response,
|
|
43
60
|
type Subject,
|
|
44
61
|
} from "./api.ts";
|
|
62
|
+
export {
|
|
63
|
+
type HTTPSubjectRequest,
|
|
64
|
+
type HTTPSubjectResolutionContext,
|
|
65
|
+
HTTPSubjectResolutionError,
|
|
66
|
+
type HTTPSubjectResolver,
|
|
67
|
+
httpSubjectError,
|
|
68
|
+
} from "./http-subject.ts";
|
|
45
69
|
export {
|
|
46
70
|
catalogToJson,
|
|
47
71
|
catalogToYaml,
|
|
@@ -76,20 +100,47 @@ export {
|
|
|
76
100
|
} from "./build.ts";
|
|
77
101
|
export {
|
|
78
102
|
ENV_PLUGIN_INVOKER_SOCKET,
|
|
103
|
+
ENV_PLUGIN_INVOKER_SOCKET_TOKEN,
|
|
79
104
|
PluginInvoker,
|
|
80
105
|
type PluginGraphQLInvokeOptions,
|
|
81
106
|
type PluginInvocationGrant,
|
|
82
107
|
type PluginInvokeOptions,
|
|
83
108
|
} from "./invoker.ts";
|
|
109
|
+
export {
|
|
110
|
+
ENV_AGENT_MANAGER_SOCKET,
|
|
111
|
+
ENV_AGENT_MANAGER_SOCKET_TOKEN,
|
|
112
|
+
AgentManager,
|
|
113
|
+
type AgentManagerCancelTurnInput,
|
|
114
|
+
type AgentManagerCreateSessionInput,
|
|
115
|
+
type AgentManagerCreateTurnInput,
|
|
116
|
+
type AgentManagerGetSessionInput,
|
|
117
|
+
type AgentManagerGetTurnInput,
|
|
118
|
+
type AgentManagerListInteractionsInput,
|
|
119
|
+
type AgentManagerListSessionsInput,
|
|
120
|
+
type AgentManagerListTurnEventsInput,
|
|
121
|
+
type AgentManagerListTurnsInput,
|
|
122
|
+
type AgentManagerResolveInteractionInput,
|
|
123
|
+
type AgentManagerUpdateSessionInput,
|
|
124
|
+
} from "./agent-manager.ts";
|
|
84
125
|
export {
|
|
85
126
|
ENV_WORKFLOW_MANAGER_SOCKET,
|
|
127
|
+
ENV_WORKFLOW_MANAGER_SOCKET_TOKEN,
|
|
86
128
|
WorkflowManager,
|
|
129
|
+
type ManagedWorkflowEventTriggerMessage,
|
|
87
130
|
type ManagedWorkflowScheduleMessage,
|
|
131
|
+
type WorkflowEventMessage,
|
|
132
|
+
type WorkflowManagerCreateTriggerInput,
|
|
88
133
|
type WorkflowManagerCreateScheduleInput,
|
|
134
|
+
type WorkflowManagerDeleteTriggerInput,
|
|
89
135
|
type WorkflowManagerDeleteScheduleInput,
|
|
136
|
+
type WorkflowManagerGetTriggerInput,
|
|
90
137
|
type WorkflowManagerGetScheduleInput,
|
|
138
|
+
type WorkflowManagerPauseTriggerInput,
|
|
91
139
|
type WorkflowManagerPauseScheduleInput,
|
|
140
|
+
type WorkflowManagerPublishEventInput,
|
|
141
|
+
type WorkflowManagerResumeTriggerInput,
|
|
92
142
|
type WorkflowManagerResumeScheduleInput,
|
|
143
|
+
type WorkflowManagerUpdateTriggerInput,
|
|
93
144
|
type WorkflowManagerUpdateScheduleInput,
|
|
94
145
|
} from "./workflow-manager.ts";
|
|
95
146
|
export {
|
|
@@ -107,8 +158,11 @@ export {
|
|
|
107
158
|
Cache,
|
|
108
159
|
CacheProvider,
|
|
109
160
|
cacheSocketEnv,
|
|
161
|
+
cacheSocketTokenEnv,
|
|
110
162
|
defineCacheProvider,
|
|
111
163
|
isCacheProvider,
|
|
164
|
+
ENV_CACHE_SOCKET,
|
|
165
|
+
ENV_CACHE_SOCKET_TOKEN,
|
|
112
166
|
type CacheEntry,
|
|
113
167
|
type CacheProviderOptions,
|
|
114
168
|
type CacheSetOptions,
|
|
@@ -119,6 +173,7 @@ export {
|
|
|
119
173
|
type SecretsProviderOptions,
|
|
120
174
|
} from "./secrets.ts";
|
|
121
175
|
export {
|
|
176
|
+
type ConnectedToken,
|
|
122
177
|
PluginProvider,
|
|
123
178
|
connectionModeToProtoValue,
|
|
124
179
|
connectionParamToProto,
|
|
@@ -129,6 +184,7 @@ export {
|
|
|
129
184
|
type ConnectionParamDefinition,
|
|
130
185
|
type OperationDefinition,
|
|
131
186
|
type OperationOptions,
|
|
187
|
+
type PostConnectHandler,
|
|
132
188
|
type PluginDefinitionOptions,
|
|
133
189
|
type SessionCatalog,
|
|
134
190
|
type SessionCatalogHandler,
|
|
@@ -200,6 +256,7 @@ export {
|
|
|
200
256
|
AlreadyExistsError,
|
|
201
257
|
ColumnType,
|
|
202
258
|
indexedDBSocketEnv,
|
|
259
|
+
indexedDBSocketTokenEnv,
|
|
203
260
|
type Record,
|
|
204
261
|
type KeyRange,
|
|
205
262
|
type ColumnSchema,
|
|
@@ -218,7 +275,10 @@ export {
|
|
|
218
275
|
createS3Service,
|
|
219
276
|
defineS3Provider,
|
|
220
277
|
isS3Provider,
|
|
278
|
+
ENV_S3_SOCKET,
|
|
279
|
+
ENV_S3_SOCKET_TOKEN,
|
|
221
280
|
s3SocketEnv,
|
|
281
|
+
s3SocketTokenEnv,
|
|
222
282
|
type ByteRange,
|
|
223
283
|
type CopyOptions,
|
|
224
284
|
type ListOptions,
|
|
@@ -234,6 +294,50 @@ export {
|
|
|
234
294
|
type S3ProviderOptions,
|
|
235
295
|
type WriteOptions,
|
|
236
296
|
} from "./s3.ts";
|
|
297
|
+
export {
|
|
298
|
+
ENV_AGENT_HOST_SOCKET,
|
|
299
|
+
AgentHost,
|
|
300
|
+
AgentExecutionStatus,
|
|
301
|
+
AgentInteractionState,
|
|
302
|
+
AgentInteractionType,
|
|
303
|
+
AgentMessagePartType,
|
|
304
|
+
AgentProvider,
|
|
305
|
+
AgentSessionState,
|
|
306
|
+
AgentToolSourceMode,
|
|
307
|
+
createAgentProviderService,
|
|
308
|
+
defineAgentProvider,
|
|
309
|
+
isAgentProvider,
|
|
310
|
+
type AgentActor,
|
|
311
|
+
type AgentInteraction,
|
|
312
|
+
type AgentMessage,
|
|
313
|
+
type AgentMessagePart,
|
|
314
|
+
type AgentMessagePartImageRef,
|
|
315
|
+
type AgentMessagePartToolCall,
|
|
316
|
+
type AgentMessagePartToolResult,
|
|
317
|
+
type AgentProviderCapabilities,
|
|
318
|
+
type AgentProviderOptions,
|
|
319
|
+
type AgentSession,
|
|
320
|
+
type AgentToolRef,
|
|
321
|
+
type AgentTurn,
|
|
322
|
+
type AgentTurnEvent,
|
|
323
|
+
type BoundAgentToolTarget,
|
|
324
|
+
type CancelAgentProviderTurnRequest,
|
|
325
|
+
type CreateAgentProviderSessionRequest,
|
|
326
|
+
type CreateAgentProviderTurnRequest,
|
|
327
|
+
type ExecuteAgentToolRequest,
|
|
328
|
+
type ExecuteAgentToolResponse,
|
|
329
|
+
type GetAgentProviderCapabilitiesRequest,
|
|
330
|
+
type GetAgentProviderInteractionRequest,
|
|
331
|
+
type GetAgentProviderSessionRequest,
|
|
332
|
+
type GetAgentProviderTurnRequest,
|
|
333
|
+
type ListAgentProviderInteractionsRequest,
|
|
334
|
+
type ListAgentProviderSessionsRequest,
|
|
335
|
+
type ListAgentProviderTurnEventsRequest,
|
|
336
|
+
type ListAgentProviderTurnsRequest,
|
|
337
|
+
type ResolvedAgentTool,
|
|
338
|
+
type ResolveAgentProviderInteractionRequest,
|
|
339
|
+
type UpdateAgentProviderSessionRequest,
|
|
340
|
+
} from "./agent.ts";
|
|
237
341
|
export {
|
|
238
342
|
ENV_WORKFLOW_HOST_SOCKET,
|
|
239
343
|
WorkflowHost,
|
package/src/indexeddb.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createClient, type Client } from "@connectrpc/connect";
|
|
1
|
+
import { createClient, type Client, type Interceptor } from "@connectrpc/connect";
|
|
2
2
|
import { createGrpcTransport } from "@connectrpc/connect-node";
|
|
3
3
|
import {
|
|
4
4
|
IndexedDB as IndexedDBService,
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
} from "../gen/v1/datastore_pb";
|
|
7
7
|
|
|
8
8
|
const ENV_INDEXEDDB_SOCKET = "GESTALT_INDEXEDDB_SOCKET";
|
|
9
|
+
const INDEXEDDB_SOCKET_TOKEN_SUFFIX = "_TOKEN";
|
|
10
|
+
const INDEXEDDB_RELAY_TOKEN_HEADER = "x-gestalt-host-service-relay-token";
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* Returns the environment variable name used to discover an IndexedDB socket.
|
|
@@ -16,6 +18,49 @@ export function indexedDBSocketEnv(name?: string): string {
|
|
|
16
18
|
return `${ENV_INDEXEDDB_SOCKET}_${trimmed.replace(/[^A-Za-z0-9]/g, "_").toUpperCase()}`;
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Returns the environment variable name used to discover an IndexedDB relay token.
|
|
23
|
+
*/
|
|
24
|
+
export function indexedDBSocketTokenEnv(name?: string): string {
|
|
25
|
+
return `${indexedDBSocketEnv(name)}${INDEXEDDB_SOCKET_TOKEN_SUFFIX}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function indexedDBTransportOptions(rawTarget: string): {
|
|
29
|
+
baseUrl: string;
|
|
30
|
+
nodeOptions?: { path: string };
|
|
31
|
+
} {
|
|
32
|
+
const target = rawTarget.trim();
|
|
33
|
+
if (!target) {
|
|
34
|
+
throw new Error("IndexedDB transport target is required");
|
|
35
|
+
}
|
|
36
|
+
if (target.startsWith("tcp://")) {
|
|
37
|
+
const address = target.slice("tcp://".length).trim();
|
|
38
|
+
if (!address) {
|
|
39
|
+
throw new Error(`IndexedDB tcp target ${JSON.stringify(rawTarget)} is missing host:port`);
|
|
40
|
+
}
|
|
41
|
+
return { baseUrl: `http://${address}` };
|
|
42
|
+
}
|
|
43
|
+
if (target.startsWith("tls://")) {
|
|
44
|
+
const address = target.slice("tls://".length).trim();
|
|
45
|
+
if (!address) {
|
|
46
|
+
throw new Error(`IndexedDB tls target ${JSON.stringify(rawTarget)} is missing host:port`);
|
|
47
|
+
}
|
|
48
|
+
return { baseUrl: `https://${address}` };
|
|
49
|
+
}
|
|
50
|
+
if (target.startsWith("unix://")) {
|
|
51
|
+
const socketPath = target.slice("unix://".length).trim();
|
|
52
|
+
if (!socketPath) {
|
|
53
|
+
throw new Error(`IndexedDB unix target ${JSON.stringify(rawTarget)} is missing a socket path`);
|
|
54
|
+
}
|
|
55
|
+
return { baseUrl: "http://localhost", nodeOptions: { path: socketPath } };
|
|
56
|
+
}
|
|
57
|
+
if (target.includes("://")) {
|
|
58
|
+
const parsed = new URL(target);
|
|
59
|
+
throw new Error(`Unsupported IndexedDB target scheme ${JSON.stringify(parsed.protocol.replace(/:$/, ""))}`);
|
|
60
|
+
}
|
|
61
|
+
return { baseUrl: "http://localhost", nodeOptions: { path: target } };
|
|
62
|
+
}
|
|
63
|
+
|
|
19
64
|
class AsyncQueue<T> implements AsyncIterable<T> {
|
|
20
65
|
private queue: T[] = [];
|
|
21
66
|
private waiting: ((result: IteratorResult<T>) => void) | null = null;
|
|
@@ -447,15 +492,16 @@ export interface ObjectStoreSchema {
|
|
|
447
492
|
export class IndexedDB {
|
|
448
493
|
private client: Client<typeof IndexedDBService>;
|
|
449
494
|
|
|
450
|
-
|
|
495
|
+
constructor(name?: string) {
|
|
451
496
|
const envName = indexedDBSocketEnv(name);
|
|
452
|
-
const
|
|
453
|
-
if (!
|
|
497
|
+
const target = process.env[envName];
|
|
498
|
+
if (!target) {
|
|
454
499
|
throw new Error(`${envName} is not set`);
|
|
455
500
|
}
|
|
501
|
+
const token = process.env[indexedDBSocketTokenEnv(name)]?.trim() ?? "";
|
|
456
502
|
const transport = createGrpcTransport({
|
|
457
|
-
|
|
458
|
-
|
|
503
|
+
...indexedDBTransportOptions(target),
|
|
504
|
+
interceptors: token ? [indexedDBRelayTokenInterceptor(token)] : [],
|
|
459
505
|
});
|
|
460
506
|
this.client = createClient(IndexedDBService, transport);
|
|
461
507
|
}
|
|
@@ -498,6 +544,13 @@ export class IndexedDB {
|
|
|
498
544
|
}
|
|
499
545
|
}
|
|
500
546
|
|
|
547
|
+
function indexedDBRelayTokenInterceptor(token: string): Interceptor {
|
|
548
|
+
return (next) => async (req) => {
|
|
549
|
+
req.header.set(INDEXEDDB_RELAY_TOKEN_HEADER, token);
|
|
550
|
+
return next(req);
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
|
|
501
554
|
/**
|
|
502
555
|
* Object store client used for primary-key operations.
|
|
503
556
|
*/
|
package/src/invoker.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { connect } from "node:net";
|
|
2
|
-
|
|
3
1
|
import type { JsonObject, JsonValue } from "@bufbuild/protobuf";
|
|
4
|
-
import { createClient, type Client } from "@connectrpc/connect";
|
|
2
|
+
import { createClient, type Client, type Interceptor } from "@connectrpc/connect";
|
|
5
3
|
import { createGrpcTransport } from "@connectrpc/connect-node";
|
|
6
4
|
|
|
7
5
|
import { PluginInvoker as PluginInvokerService } from "../gen/v1/plugin_pb.ts";
|
|
8
6
|
import type { OperationResult, Request } from "./api.ts";
|
|
9
7
|
|
|
10
8
|
export const ENV_PLUGIN_INVOKER_SOCKET = "GESTALT_PLUGIN_INVOKER_SOCKET";
|
|
9
|
+
export const ENV_PLUGIN_INVOKER_SOCKET_TOKEN = `${ENV_PLUGIN_INVOKER_SOCKET}_TOKEN`;
|
|
10
|
+
const PLUGIN_INVOKER_RELAY_TOKEN_HEADER = "x-gestalt-host-service-relay-token";
|
|
11
11
|
|
|
12
12
|
export interface PluginInvokeOptions {
|
|
13
13
|
connection?: string;
|
|
@@ -38,12 +38,11 @@ export class PluginInvoker {
|
|
|
38
38
|
if (!socketPath) {
|
|
39
39
|
throw new Error(`plugin invoker: ${ENV_PLUGIN_INVOKER_SOCKET} is not set`);
|
|
40
40
|
}
|
|
41
|
+
const relayToken = process.env[ENV_PLUGIN_INVOKER_SOCKET_TOKEN]?.trim() ?? "";
|
|
41
42
|
|
|
42
43
|
const transport = createGrpcTransport({
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
createConnection: () => connect(socketPath),
|
|
46
|
-
},
|
|
44
|
+
...pluginInvokerTransportOptions(socketPath),
|
|
45
|
+
interceptors: relayToken ? [pluginInvokerRelayTokenInterceptor(relayToken)] : [],
|
|
47
46
|
});
|
|
48
47
|
this.client = createClient(PluginInvokerService, transport);
|
|
49
48
|
}
|
|
@@ -118,6 +117,49 @@ export class PluginInvoker {
|
|
|
118
117
|
}
|
|
119
118
|
}
|
|
120
119
|
|
|
120
|
+
function pluginInvokerTransportOptions(rawTarget: string): {
|
|
121
|
+
baseUrl: string;
|
|
122
|
+
nodeOptions?: { path: string };
|
|
123
|
+
} {
|
|
124
|
+
const target = rawTarget.trim();
|
|
125
|
+
if (!target) {
|
|
126
|
+
throw new Error("plugin invoker: transport target is required");
|
|
127
|
+
}
|
|
128
|
+
if (target.startsWith("tcp://")) {
|
|
129
|
+
const address = target.slice("tcp://".length).trim();
|
|
130
|
+
if (!address) {
|
|
131
|
+
throw new Error(`plugin invoker: tcp target ${JSON.stringify(rawTarget)} is missing host:port`);
|
|
132
|
+
}
|
|
133
|
+
return { baseUrl: `http://${address}` };
|
|
134
|
+
}
|
|
135
|
+
if (target.startsWith("tls://")) {
|
|
136
|
+
const address = target.slice("tls://".length).trim();
|
|
137
|
+
if (!address) {
|
|
138
|
+
throw new Error(`plugin invoker: tls target ${JSON.stringify(rawTarget)} is missing host:port`);
|
|
139
|
+
}
|
|
140
|
+
return { baseUrl: `https://${address}` };
|
|
141
|
+
}
|
|
142
|
+
if (target.startsWith("unix://")) {
|
|
143
|
+
const socketPath = target.slice("unix://".length).trim();
|
|
144
|
+
if (!socketPath) {
|
|
145
|
+
throw new Error(`plugin invoker: unix target ${JSON.stringify(rawTarget)} is missing a socket path`);
|
|
146
|
+
}
|
|
147
|
+
return { baseUrl: "http://localhost", nodeOptions: { path: socketPath } };
|
|
148
|
+
}
|
|
149
|
+
if (target.includes("://")) {
|
|
150
|
+
const parsed = new URL(target);
|
|
151
|
+
throw new Error(`plugin invoker: unsupported target scheme ${JSON.stringify(parsed.protocol.replace(/:$/, ""))}`);
|
|
152
|
+
}
|
|
153
|
+
return { baseUrl: "http://localhost", nodeOptions: { path: target } };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function pluginInvokerRelayTokenInterceptor(token: string): Interceptor {
|
|
157
|
+
return (next) => async (req) => {
|
|
158
|
+
req.header.set(PLUGIN_INVOKER_RELAY_TOKEN_HEADER, token);
|
|
159
|
+
return next(req);
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
121
163
|
function normalizeInvocationToken(requestOrToken: Request | string): string {
|
|
122
164
|
const invocationToken =
|
|
123
165
|
typeof requestOrToken === "string"
|
package/src/manifest-metadata.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { writeFileSync } from "node:fs";
|
|
|
3
3
|
import YAML from "yaml";
|
|
4
4
|
|
|
5
5
|
export type HTTPSecuritySchemeType =
|
|
6
|
-
| "
|
|
6
|
+
| "hmac"
|
|
7
7
|
| "apiKey"
|
|
8
8
|
| "http"
|
|
9
9
|
| "none";
|
|
@@ -20,6 +20,11 @@ export interface HTTPSecretRef {
|
|
|
20
20
|
export interface HTTPSecurityScheme {
|
|
21
21
|
type?: HTTPSecuritySchemeType;
|
|
22
22
|
description?: string;
|
|
23
|
+
signatureHeader?: string;
|
|
24
|
+
signaturePrefix?: string;
|
|
25
|
+
payloadTemplate?: string;
|
|
26
|
+
timestampHeader?: string;
|
|
27
|
+
maxAgeSeconds?: number;
|
|
23
28
|
name?: string;
|
|
24
29
|
in?: HTTPIn;
|
|
25
30
|
scheme?: HTTPAuthScheme;
|
|
@@ -42,6 +47,7 @@ export interface HTTPAck {
|
|
|
42
47
|
export interface HTTPBinding {
|
|
43
48
|
path: string;
|
|
44
49
|
method: string;
|
|
50
|
+
credentialMode?: "none" | "user";
|
|
45
51
|
requestBody?: HTTPRequestBody;
|
|
46
52
|
security: string;
|
|
47
53
|
target: string;
|