@dxos/functions 0.8.4-main.e8ec1fe → 0.8.4-main.ef1bc66f44
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/dist/lib/{browser → neutral}/index.mjs +450 -148
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/types/src/errors.d.ts +24 -32
- package/dist/types/src/errors.d.ts.map +1 -1
- package/dist/types/src/operation-compatibility.test.d.ts +2 -0
- package/dist/types/src/operation-compatibility.test.d.ts.map +1 -0
- package/dist/types/src/protocol/functions-ai-http-client.d.ts +12 -0
- package/dist/types/src/protocol/functions-ai-http-client.d.ts.map +1 -0
- package/dist/types/src/protocol/protocol.d.ts.map +1 -1
- package/dist/types/src/sdk.d.ts +27 -2
- package/dist/types/src/sdk.d.ts.map +1 -1
- package/dist/types/src/services/credentials.d.ts +6 -4
- package/dist/types/src/services/credentials.d.ts.map +1 -1
- package/dist/types/src/services/event-logger.d.ts +25 -31
- package/dist/types/src/services/event-logger.d.ts.map +1 -1
- package/dist/types/src/services/function-invocation-service.d.ts +5 -0
- package/dist/types/src/services/function-invocation-service.d.ts.map +1 -1
- package/dist/types/src/services/index.d.ts +0 -1
- package/dist/types/src/services/index.d.ts.map +1 -1
- package/dist/types/src/services/queues.d.ts +4 -4
- package/dist/types/src/services/queues.d.ts.map +1 -1
- package/dist/types/src/services/tracing.d.ts +37 -3
- package/dist/types/src/services/tracing.d.ts.map +1 -1
- package/dist/types/src/types/Function.d.ts +40 -46
- package/dist/types/src/types/Function.d.ts.map +1 -1
- package/dist/types/src/types/Script.d.ts +9 -16
- package/dist/types/src/types/Script.d.ts.map +1 -1
- package/dist/types/src/types/Trigger.d.ts +58 -76
- package/dist/types/src/types/Trigger.d.ts.map +1 -1
- package/dist/types/src/types/TriggerEvent.d.ts +43 -13
- package/dist/types/src/types/TriggerEvent.d.ts.map +1 -1
- package/dist/types/src/types/url.d.ts +4 -3
- package/dist/types/src/types/url.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +23 -17
- package/src/errors.ts +4 -4
- package/src/operation-compatibility.test.ts +185 -0
- package/src/protocol/functions-ai-http-client.ts +67 -0
- package/src/protocol/protocol.ts +184 -67
- package/src/sdk.ts +68 -5
- package/src/services/credentials.ts +31 -15
- package/src/services/event-logger.ts +2 -2
- package/src/services/function-invocation-service.ts +14 -0
- package/src/services/index.ts +0 -2
- package/src/services/queues.ts +5 -7
- package/src/services/tracing.ts +63 -4
- package/src/types/Function.ts +28 -8
- package/src/types/Script.ts +8 -7
- package/src/types/Trigger.ts +18 -14
- package/src/types/TriggerEvent.ts +29 -29
- package/src/types/url.ts +4 -3
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/node-esm/index.mjs +0 -928
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
package/src/sdk.ts
CHANGED
|
@@ -8,8 +8,9 @@ import * as Schema from 'effect/Schema';
|
|
|
8
8
|
|
|
9
9
|
import { type AiService } from '@dxos/ai';
|
|
10
10
|
import { Obj, Type } from '@dxos/echo';
|
|
11
|
-
import { type
|
|
11
|
+
import { type Database } from '@dxos/echo';
|
|
12
12
|
import { assertArgument, failedInvariant } from '@dxos/invariant';
|
|
13
|
+
import { Operation } from '@dxos/operation';
|
|
13
14
|
|
|
14
15
|
import {
|
|
15
16
|
type CredentialsService,
|
|
@@ -37,7 +38,7 @@ export type FunctionServices =
|
|
|
37
38
|
| InvocationServices
|
|
38
39
|
| AiService.AiService
|
|
39
40
|
| CredentialsService
|
|
40
|
-
|
|
|
41
|
+
| Database.Service
|
|
41
42
|
| QueueService
|
|
42
43
|
| FunctionInvocationService;
|
|
43
44
|
|
|
@@ -75,6 +76,12 @@ export type FunctionDefinition<T = any, O = any, S extends FunctionServices = Fu
|
|
|
75
76
|
inputSchema: Schema.Schema<T, any>;
|
|
76
77
|
outputSchema?: Schema.Schema<O, any>;
|
|
77
78
|
|
|
79
|
+
/**
|
|
80
|
+
* List of types the function uses.
|
|
81
|
+
* This is used to ensure that the types are available when the function is executed.
|
|
82
|
+
*/
|
|
83
|
+
types: readonly Type.Entity.Any[];
|
|
84
|
+
|
|
78
85
|
/**
|
|
79
86
|
* Keys of the required services.
|
|
80
87
|
*/
|
|
@@ -108,6 +115,12 @@ export type FunctionProps<T, O> = {
|
|
|
108
115
|
description?: string;
|
|
109
116
|
inputSchema: Schema.Schema<T, any>;
|
|
110
117
|
outputSchema?: Schema.Schema<O, any>;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* List of types the function uses.
|
|
121
|
+
* This is used to ensure that the types are available when the function is executed.
|
|
122
|
+
*/
|
|
123
|
+
types?: readonly Type.Entity.Any[];
|
|
111
124
|
// TODO(dmaretskyi): This currently doesn't cause a compile-time error if the handler requests a service that is not specified
|
|
112
125
|
services?: readonly Context.Tag<any, any>[];
|
|
113
126
|
|
|
@@ -117,7 +130,7 @@ export type FunctionProps<T, O> = {
|
|
|
117
130
|
// TODO(dmaretskyi): Output type doesn't get typechecked.
|
|
118
131
|
export const defineFunction: {
|
|
119
132
|
<I, O>(params: FunctionProps<I, O>): FunctionDefinition<I, O, FunctionServices>;
|
|
120
|
-
} = ({ key, name, description, inputSchema, outputSchema = Schema.Any, handler, services }) => {
|
|
133
|
+
} = ({ key, name, description, inputSchema, outputSchema = Schema.Any, handler, types, services }) => {
|
|
121
134
|
if (!Schema.isSchema(inputSchema)) {
|
|
122
135
|
throw new Error('Input schema must be a valid schema');
|
|
123
136
|
}
|
|
@@ -162,6 +175,7 @@ export const defineFunction: {
|
|
|
162
175
|
inputSchema,
|
|
163
176
|
outputSchema,
|
|
164
177
|
handler: handlerWithSpan,
|
|
178
|
+
types: types ?? [],
|
|
165
179
|
services: !services ? [] : getServiceKeys(services),
|
|
166
180
|
} satisfies FunctionDefinition.Any;
|
|
167
181
|
};
|
|
@@ -171,11 +185,58 @@ const getServiceKeys = (services: readonly Context.Tag<any, any>[]) => {
|
|
|
171
185
|
if (typeof tag.key === 'string') {
|
|
172
186
|
return tag.key;
|
|
173
187
|
}
|
|
174
|
-
console.log(tag);
|
|
175
188
|
failedInvariant();
|
|
176
189
|
});
|
|
177
190
|
};
|
|
178
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Converts a FunctionDefinition to an OperationDefinition with handler.
|
|
194
|
+
* The function handler is adapted to the OperationHandler format.
|
|
195
|
+
*
|
|
196
|
+
* Note: FunctionDefinition stores service keys as strings, not Tag types,
|
|
197
|
+
* so we can't use Operation.withHandler's type inference here.
|
|
198
|
+
*/
|
|
199
|
+
export const toOperation = <T, O, S extends FunctionServices = FunctionServices>(
|
|
200
|
+
functionDef: FunctionDefinition<T, O, S>,
|
|
201
|
+
): Operation.Definition<T, O> & { handler: Operation.Handler<T, O, any, S> } => {
|
|
202
|
+
const op = Operation.make({
|
|
203
|
+
schema: {
|
|
204
|
+
input: functionDef.inputSchema,
|
|
205
|
+
output: functionDef.outputSchema ?? Schema.Any,
|
|
206
|
+
},
|
|
207
|
+
meta: {
|
|
208
|
+
key: functionDef.key,
|
|
209
|
+
name: functionDef.name,
|
|
210
|
+
description: functionDef.description,
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Adapt FunctionHandler signature to OperationHandler format.
|
|
215
|
+
// FunctionHandler expects { context, data }, OperationHandler expects just input.
|
|
216
|
+
const operationHandler: Operation.Handler<T, O, any, S> = (input: T) => {
|
|
217
|
+
const result = functionDef.handler({
|
|
218
|
+
context: {} as FunctionContext,
|
|
219
|
+
data: input,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Convert Promise or plain value to Effect.
|
|
223
|
+
if (Effect.isEffect(result)) {
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
if (result instanceof Promise) {
|
|
227
|
+
return Effect.tryPromise(() => result);
|
|
228
|
+
}
|
|
229
|
+
return Effect.succeed(result as O);
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// Manually attach handler since FunctionDefinition stores service keys as strings,
|
|
233
|
+
// not Tag types, so withHandler's type inference doesn't apply.
|
|
234
|
+
return {
|
|
235
|
+
...op,
|
|
236
|
+
handler: operationHandler,
|
|
237
|
+
};
|
|
238
|
+
};
|
|
239
|
+
|
|
179
240
|
export const FunctionDefinition = {
|
|
180
241
|
make: defineFunction,
|
|
181
242
|
isFunction: (value: unknown): value is FunctionDefinition.Any => {
|
|
@@ -189,6 +250,7 @@ export const FunctionDefinition = {
|
|
|
189
250
|
assertArgument(Obj.instanceOf(Function.Function, functionObj), 'functionObj');
|
|
190
251
|
return deserializeFunction(functionObj);
|
|
191
252
|
},
|
|
253
|
+
toOperation,
|
|
192
254
|
};
|
|
193
255
|
|
|
194
256
|
export const serializeFunction = (functionDef: FunctionDefinition.Any): Function.Function => {
|
|
@@ -202,7 +264,7 @@ export const serializeFunction = (functionDef: FunctionDefinition.Any): Function
|
|
|
202
264
|
services: functionDef.services,
|
|
203
265
|
});
|
|
204
266
|
if (functionDef.meta?.deployedFunctionId) {
|
|
205
|
-
setUserFunctionIdInMetadata(Obj.getMeta(fn), functionDef.meta
|
|
267
|
+
Obj.change(fn, (fn) => setUserFunctionIdInMetadata(Obj.getMeta(fn), functionDef.meta!.deployedFunctionId!));
|
|
206
268
|
}
|
|
207
269
|
return fn;
|
|
208
270
|
};
|
|
@@ -219,6 +281,7 @@ export const deserializeFunction = (functionObj: Function.Function): FunctionDef
|
|
|
219
281
|
// TODO(dmaretskyi): This should throw error.
|
|
220
282
|
handler: () => {},
|
|
221
283
|
services: functionObj.services ?? [],
|
|
284
|
+
types: [],
|
|
222
285
|
meta: {
|
|
223
286
|
deployedFunctionId: getUserFunctionIdInMetadata(Obj.getMeta(functionObj)),
|
|
224
287
|
},
|
|
@@ -11,7 +11,7 @@ import * as Layer from 'effect/Layer';
|
|
|
11
11
|
import * as Redacted from 'effect/Redacted';
|
|
12
12
|
|
|
13
13
|
import { Query } from '@dxos/echo';
|
|
14
|
-
import {
|
|
14
|
+
import { Database } from '@dxos/echo';
|
|
15
15
|
import { AccessToken } from '@dxos/types';
|
|
16
16
|
|
|
17
17
|
export type CredentialQuery = {
|
|
@@ -60,7 +60,12 @@ export class CredentialsService extends Context.Tag('@dxos/functions/Credentials
|
|
|
60
60
|
static configuredLayer = (credentials: ServiceCredential[]) =>
|
|
61
61
|
Layer.succeed(CredentialsService, new ConfiguredCredentialsService(credentials));
|
|
62
62
|
|
|
63
|
-
static layerConfig = (
|
|
63
|
+
static layerConfig = (
|
|
64
|
+
credentials: {
|
|
65
|
+
service: string;
|
|
66
|
+
apiKey: Config.Config<Redacted.Redacted<string>>;
|
|
67
|
+
}[],
|
|
68
|
+
) =>
|
|
64
69
|
Layer.effect(
|
|
65
70
|
CredentialsService,
|
|
66
71
|
Effect.gen(function* () {
|
|
@@ -77,20 +82,34 @@ export class CredentialsService extends Context.Tag('@dxos/functions/Credentials
|
|
|
77
82
|
}),
|
|
78
83
|
);
|
|
79
84
|
|
|
80
|
-
static layerFromDatabase = () =>
|
|
85
|
+
static layerFromDatabase = ({ caching = false }: { caching?: boolean } = {}) =>
|
|
81
86
|
Layer.effect(
|
|
82
87
|
CredentialsService,
|
|
83
88
|
Effect.gen(function* () {
|
|
84
|
-
const dbService = yield*
|
|
89
|
+
const dbService = yield* Database.Service;
|
|
90
|
+
const cache = new Map<string, ServiceCredential[]>();
|
|
91
|
+
|
|
85
92
|
const queryCredentials = async (query: CredentialQuery): Promise<ServiceCredential[]> => {
|
|
86
|
-
const
|
|
87
|
-
|
|
93
|
+
const cacheKey = JSON.stringify(query);
|
|
94
|
+
if (caching && cache.has(cacheKey)) {
|
|
95
|
+
return cache.get(cacheKey)!;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const accessTokens = await dbService.db.query(Query.type(AccessToken.AccessToken)).run();
|
|
99
|
+
const credentials = accessTokens
|
|
88
100
|
.filter((accessToken) => accessToken.source === query.service)
|
|
89
101
|
.map((accessToken) => ({
|
|
90
102
|
service: accessToken.source,
|
|
91
103
|
apiKey: accessToken.token,
|
|
92
104
|
}));
|
|
105
|
+
|
|
106
|
+
if (caching) {
|
|
107
|
+
cache.set(cacheKey, credentials);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return credentials;
|
|
93
111
|
};
|
|
112
|
+
|
|
94
113
|
return {
|
|
95
114
|
getCredential: async (query) => {
|
|
96
115
|
const credentials = await queryCredentials(query);
|
|
@@ -131,13 +150,10 @@ export class ConfiguredCredentialsService implements Context.Tag.Service<Credent
|
|
|
131
150
|
}
|
|
132
151
|
|
|
133
152
|
/**
|
|
134
|
-
* Maps the request to include the
|
|
153
|
+
* Maps the request to include the given token in the Authorization header.
|
|
135
154
|
*/
|
|
136
|
-
export const withAuthorization = (
|
|
137
|
-
HttpClient.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return HttpClientRequest.setHeader(request, 'Authorization', authorization);
|
|
142
|
-
}),
|
|
143
|
-
);
|
|
155
|
+
export const withAuthorization = (token: string, kind?: 'Bearer' | 'Basic') =>
|
|
156
|
+
HttpClient.mapRequest((request) => {
|
|
157
|
+
const authorization = kind ? `${kind} ${token}` : token;
|
|
158
|
+
return HttpClientRequest.setHeader(request, 'Authorization', authorization);
|
|
159
|
+
});
|
|
@@ -52,7 +52,7 @@ export type ComputeEventPayload = Schema.Schema.Type<typeof ComputeEventPayload>
|
|
|
52
52
|
|
|
53
53
|
export const ComputeEvent = Schema.Struct({
|
|
54
54
|
payload: ComputeEventPayload,
|
|
55
|
-
}).pipe(Type.
|
|
55
|
+
}).pipe(Type.object({ typename: 'dxos.org/type/ComputeEvent', version: '0.1.0' }));
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* Logs event for the compute workflows.
|
|
@@ -75,7 +75,7 @@ export class ComputeEventLogger extends Context.Tag('@dxos/functions/ComputeEven
|
|
|
75
75
|
const tracing = yield* TracingService;
|
|
76
76
|
return {
|
|
77
77
|
log: (event: ComputeEventPayload) => {
|
|
78
|
-
tracing.write(Obj.make(ComputeEvent, { payload: event }));
|
|
78
|
+
tracing.write(Obj.make(ComputeEvent, { payload: event }), tracing.getTraceContext());
|
|
79
79
|
},
|
|
80
80
|
nodeId: undefined,
|
|
81
81
|
};
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
//
|
|
4
4
|
import * as Context from 'effect/Context';
|
|
5
5
|
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as Layer from 'effect/Layer';
|
|
6
7
|
|
|
8
|
+
import type { FunctionNotFoundError } from '../errors';
|
|
7
9
|
import { type FunctionDefinition, type InvocationServices } from '../sdk';
|
|
8
10
|
|
|
9
11
|
export class FunctionInvocationService extends Context.Tag('@dxos/functions/FunctionInvocationService')<
|
|
@@ -13,11 +15,23 @@ export class FunctionInvocationService extends Context.Tag('@dxos/functions/Func
|
|
|
13
15
|
functionDef: FunctionDefinition<I, O, any>,
|
|
14
16
|
input: I,
|
|
15
17
|
): Effect.Effect<O, never, InvocationServices>;
|
|
18
|
+
|
|
19
|
+
resolveFunction(key: string): Effect.Effect<FunctionDefinition.Any, FunctionNotFoundError>;
|
|
16
20
|
}
|
|
17
21
|
>() {
|
|
22
|
+
static layerNotAvailable = Layer.succeed(FunctionInvocationService, {
|
|
23
|
+
invokeFunction: () => Effect.die('FunctionInvocationService is not avaialble.'),
|
|
24
|
+
resolveFunction: () => Effect.die('FunctionInvocationService is not available.'),
|
|
25
|
+
});
|
|
26
|
+
|
|
18
27
|
static invokeFunction = <I, O>(
|
|
19
28
|
functionDef: FunctionDefinition<I, O, any>,
|
|
20
29
|
input: I,
|
|
21
30
|
): Effect.Effect<O, never, FunctionInvocationService | InvocationServices> =>
|
|
22
31
|
Effect.serviceFunctionEffect(FunctionInvocationService, (service) => service.invokeFunction)(functionDef, input);
|
|
32
|
+
|
|
33
|
+
static resolveFunction = (
|
|
34
|
+
key: string,
|
|
35
|
+
): Effect.Effect<FunctionDefinition.Any, FunctionNotFoundError, FunctionInvocationService> =>
|
|
36
|
+
Effect.serviceFunctionEffect(FunctionInvocationService, (service) => service.resolveFunction)(key);
|
|
23
37
|
}
|
package/src/services/index.ts
CHANGED
package/src/services/queues.ts
CHANGED
|
@@ -6,7 +6,7 @@ import * as Context from 'effect/Context';
|
|
|
6
6
|
import * as Effect from 'effect/Effect';
|
|
7
7
|
import * as Layer from 'effect/Layer';
|
|
8
8
|
|
|
9
|
-
import type {
|
|
9
|
+
import type { Entity } from '@dxos/echo';
|
|
10
10
|
import type { Queue, QueueAPI, QueueFactory } from '@dxos/echo-db';
|
|
11
11
|
import type { DXN, QueueSubspaceTag } from '@dxos/keys';
|
|
12
12
|
|
|
@@ -53,22 +53,20 @@ export class QueueService extends Context.Tag('@dxos/functions/QueueService')<
|
|
|
53
53
|
/**
|
|
54
54
|
* Gets a queue by its DXN.
|
|
55
55
|
*/
|
|
56
|
-
static getQueue = <T extends
|
|
56
|
+
static getQueue = <T extends Entity.Unknown = Entity.Unknown>(
|
|
57
57
|
dxn: DXN,
|
|
58
58
|
): Effect.Effect<Queue<T>, never, QueueService> => QueueService.pipe(Effect.map(({ queues }) => queues.get<T>(dxn)));
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
61
|
* Creates a new queue.
|
|
62
62
|
*/
|
|
63
|
-
static createQueue = <T extends
|
|
63
|
+
static createQueue = <T extends Entity.Unknown = Entity.Unknown>(options?: {
|
|
64
64
|
subspaceTag?: QueueSubspaceTag;
|
|
65
65
|
}): Effect.Effect<Queue<T>, never, QueueService> =>
|
|
66
66
|
QueueService.pipe(Effect.map(({ queues }) => queues.create<T>(options)));
|
|
67
67
|
|
|
68
|
-
static append = <T extends
|
|
69
|
-
queue
|
|
70
|
-
objects: T[],
|
|
71
|
-
): Effect.Effect<void> => Effect.promise(() => queue.append(objects));
|
|
68
|
+
static append = <T extends Entity.Unknown = Entity.Unknown>(queue: Queue<T>, objects: T[]): Effect.Effect<void> =>
|
|
69
|
+
Effect.promise(() => queue.append(objects));
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
/**
|
package/src/services/tracing.ts
CHANGED
|
@@ -7,10 +7,12 @@ import * as Effect from 'effect/Effect';
|
|
|
7
7
|
import * as Layer from 'effect/Layer';
|
|
8
8
|
|
|
9
9
|
import { AgentStatus } from '@dxos/ai';
|
|
10
|
-
import { Obj } from '@dxos/echo';
|
|
11
|
-
import
|
|
10
|
+
import { type DXN, Obj } from '@dxos/echo';
|
|
11
|
+
import { ObjectId } from '@dxos/keys';
|
|
12
12
|
import { Message } from '@dxos/types';
|
|
13
13
|
|
|
14
|
+
import type { Trigger } from '../types';
|
|
15
|
+
|
|
14
16
|
/**
|
|
15
17
|
* Provides a way for compute primitives (functions, workflows, tools)
|
|
16
18
|
* to emit an execution trace as a series of structured ECHO objects.
|
|
@@ -27,15 +29,35 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
|
|
|
27
29
|
* Write an event to the tracing queue.
|
|
28
30
|
* @param event - The event to write. Must be an a typed object.
|
|
29
31
|
*/
|
|
30
|
-
write: (event: Obj.
|
|
32
|
+
write: (event: Obj.Unknown, traceContext: TracingService.TraceContext) => void;
|
|
33
|
+
|
|
34
|
+
traceInvocationStart({
|
|
35
|
+
payload,
|
|
36
|
+
target,
|
|
37
|
+
}: {
|
|
38
|
+
payload: TracingService.FunctionInvocationPayload;
|
|
39
|
+
target?: DXN;
|
|
40
|
+
}): Effect.Effect<TracingService.InvocationTraceData>;
|
|
41
|
+
|
|
42
|
+
traceInvocationEnd({
|
|
43
|
+
trace,
|
|
44
|
+
exception,
|
|
45
|
+
}: {
|
|
46
|
+
trace: TracingService.InvocationTraceData;
|
|
47
|
+
exception?: any;
|
|
48
|
+
}): Effect.Effect<void>;
|
|
31
49
|
}
|
|
32
50
|
>() {
|
|
33
51
|
static noop: Context.Tag.Service<TracingService> = {
|
|
34
52
|
getTraceContext: () => ({}),
|
|
35
53
|
write: () => {},
|
|
54
|
+
traceInvocationStart: () =>
|
|
55
|
+
Effect.sync(() => ({ invocationId: ObjectId.random(), invocationTraceQueue: undefined })),
|
|
56
|
+
traceInvocationEnd: () => Effect.sync(() => {}),
|
|
36
57
|
};
|
|
37
58
|
|
|
38
59
|
static layerNoop: Layer.Layer<TracingService> = Layer.succeed(TracingService, TracingService.noop);
|
|
60
|
+
|
|
39
61
|
/**
|
|
40
62
|
* Creates a TracingService layer that emits events to the parent tracing service.
|
|
41
63
|
*/
|
|
@@ -46,12 +68,25 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
|
|
|
46
68
|
const tracing = yield* TracingService;
|
|
47
69
|
const context = mapContext(tracing.getTraceContext());
|
|
48
70
|
return {
|
|
49
|
-
write: (event) => tracing.write(event),
|
|
71
|
+
write: (event, context) => tracing.write(event, context),
|
|
50
72
|
getTraceContext: () => context,
|
|
73
|
+
traceInvocationStart: () => Effect.die('Tracing invocation inside another invocation is not supported.'),
|
|
74
|
+
traceInvocationEnd: () => Effect.die('Tracing invocation inside another invocation is not supported.'),
|
|
51
75
|
};
|
|
52
76
|
}),
|
|
53
77
|
);
|
|
54
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Create sublayer to trace an invocation.
|
|
81
|
+
* @param data
|
|
82
|
+
* @returns
|
|
83
|
+
*/
|
|
84
|
+
static layerInvocation = (data: TracingService.InvocationTraceData) =>
|
|
85
|
+
TracingService.layerSubframe((context) => ({
|
|
86
|
+
...context,
|
|
87
|
+
currentInvocation: data,
|
|
88
|
+
}));
|
|
89
|
+
|
|
55
90
|
/**
|
|
56
91
|
* Emit the current human-readable execution status.
|
|
57
92
|
*/
|
|
@@ -66,6 +101,7 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
|
|
|
66
101
|
created: new Date().toISOString(),
|
|
67
102
|
...data,
|
|
68
103
|
}),
|
|
104
|
+
tracing.getTraceContext(),
|
|
69
105
|
);
|
|
70
106
|
});
|
|
71
107
|
|
|
@@ -82,12 +118,15 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
|
|
|
82
118
|
...data.properties,
|
|
83
119
|
},
|
|
84
120
|
}),
|
|
121
|
+
tracing.getTraceContext(),
|
|
85
122
|
);
|
|
86
123
|
});
|
|
87
124
|
}
|
|
88
125
|
|
|
89
126
|
export namespace TracingService {
|
|
90
127
|
export interface TraceContext {
|
|
128
|
+
currentInvocation?: InvocationTraceData;
|
|
129
|
+
|
|
91
130
|
/**
|
|
92
131
|
* If this thread sprung from a tool call, this is the ID of the message containing the tool call.
|
|
93
132
|
*/
|
|
@@ -100,6 +139,26 @@ export namespace TracingService {
|
|
|
100
139
|
|
|
101
140
|
debugInfo?: unknown;
|
|
102
141
|
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Trace data for a function/trigger invocation.
|
|
145
|
+
*/
|
|
146
|
+
export interface InvocationTraceData {
|
|
147
|
+
invocationId: ObjectId;
|
|
148
|
+
invocationTraceQueue?: DXN.String;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Payload for a function/trigger invocation.
|
|
153
|
+
*/
|
|
154
|
+
export interface FunctionInvocationPayload {
|
|
155
|
+
data?: any;
|
|
156
|
+
inputNodeId?: string;
|
|
157
|
+
trigger?: {
|
|
158
|
+
id: string;
|
|
159
|
+
kind: Trigger.Kind;
|
|
160
|
+
};
|
|
161
|
+
}
|
|
103
162
|
}
|
|
104
163
|
|
|
105
164
|
/**
|
package/src/types/Function.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import * as Schema from 'effect/Schema';
|
|
6
6
|
|
|
7
|
-
import { Obj, Type } from '@dxos/echo';
|
|
8
|
-
import {
|
|
7
|
+
import { Annotation, JsonSchema, Obj, Type } from '@dxos/echo';
|
|
8
|
+
import { SystemTypeAnnotation } from '@dxos/echo/internal';
|
|
9
9
|
|
|
10
10
|
import { Script } from './Script';
|
|
11
11
|
|
|
@@ -24,7 +24,6 @@ export const Function = Schema.Struct({
|
|
|
24
24
|
description: 'Unique registration key for the blueprint',
|
|
25
25
|
}),
|
|
26
26
|
|
|
27
|
-
// TODO(burdon): Rename to id/uri?
|
|
28
27
|
name: Schema.NonEmptyString,
|
|
29
28
|
version: Schema.String,
|
|
30
29
|
|
|
@@ -37,10 +36,10 @@ export const Function = Schema.Struct({
|
|
|
37
36
|
|
|
38
37
|
// Reference to a source script if it exists within ECHO.
|
|
39
38
|
// TODO(burdon): Don't ref ScriptType directly (core).
|
|
40
|
-
source: Schema.optional(Ref(Script)),
|
|
39
|
+
source: Schema.optional(Type.Ref(Script)),
|
|
41
40
|
|
|
42
|
-
inputSchema: Schema.optional(
|
|
43
|
-
outputSchema: Schema.optional(
|
|
41
|
+
inputSchema: Schema.optional(JsonSchema.JsonSchema),
|
|
42
|
+
outputSchema: Schema.optional(JsonSchema.JsonSchema),
|
|
44
43
|
|
|
45
44
|
/**
|
|
46
45
|
* List of required services.
|
|
@@ -51,12 +50,33 @@ export const Function = Schema.Struct({
|
|
|
51
50
|
// Local binding to a function name.
|
|
52
51
|
binding: Schema.optional(Schema.String),
|
|
53
52
|
}).pipe(
|
|
54
|
-
Type.
|
|
53
|
+
Type.object({
|
|
55
54
|
typename: 'dxos.org/type/Function',
|
|
56
55
|
version: '0.1.0',
|
|
57
56
|
}),
|
|
58
|
-
LabelAnnotation.set(['name']),
|
|
57
|
+
Annotation.LabelAnnotation.set(['name']),
|
|
58
|
+
SystemTypeAnnotation.set(true),
|
|
59
59
|
);
|
|
60
|
+
|
|
60
61
|
export interface Function extends Schema.Schema.Type<typeof Function> {}
|
|
61
62
|
|
|
62
63
|
export const make = (props: Obj.MakeProps<typeof Function>) => Obj.make(Function, props);
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Copies properties from source to target.
|
|
67
|
+
* @param target - Target object to copy properties to.
|
|
68
|
+
* @param source - Source object to copy properties from.
|
|
69
|
+
*/
|
|
70
|
+
export const setFrom = (target: Function, source: Function) => {
|
|
71
|
+
Obj.change(target, (t) => {
|
|
72
|
+
t.key = source.key ?? target.key;
|
|
73
|
+
t.name = source.name ?? target.name;
|
|
74
|
+
t.version = source.version;
|
|
75
|
+
t.description = source.description;
|
|
76
|
+
t.updated = source.updated;
|
|
77
|
+
// TODO(dmaretskyi): A workaround for an ECHO bug.
|
|
78
|
+
t.inputSchema = source.inputSchema ? JSON.parse(JSON.stringify(source.inputSchema)) : undefined;
|
|
79
|
+
t.outputSchema = source.outputSchema ? JSON.parse(JSON.stringify(source.outputSchema)) : undefined;
|
|
80
|
+
Obj.getMeta(t).keys = JSON.parse(JSON.stringify(Obj.getMeta(source).keys));
|
|
81
|
+
});
|
|
82
|
+
};
|
package/src/types/Script.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import * as Schema from 'effect/Schema';
|
|
6
6
|
|
|
7
|
-
import { Obj, Ref, Type } from '@dxos/echo';
|
|
8
|
-
import {
|
|
7
|
+
import { Annotation, Obj, Ref, Type } from '@dxos/echo';
|
|
8
|
+
import { FormInputAnnotation } from '@dxos/echo/internal';
|
|
9
9
|
import { Text } from '@dxos/schema';
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -16,18 +16,19 @@ export const Script = Schema.Struct({
|
|
|
16
16
|
description: Schema.String.pipe(Schema.optional),
|
|
17
17
|
// TODO(burdon): Change to hash of deployed content.
|
|
18
18
|
// Whether source has changed since last deploy.
|
|
19
|
-
changed: Schema.Boolean.pipe(
|
|
20
|
-
source: Type.Ref(Text.Text).pipe(
|
|
19
|
+
changed: Schema.Boolean.pipe(FormInputAnnotation.set(false), Schema.optional),
|
|
20
|
+
source: Type.Ref(Text.Text).pipe(FormInputAnnotation.set(false)),
|
|
21
21
|
}).pipe(
|
|
22
|
-
Type.
|
|
22
|
+
Type.object({
|
|
23
23
|
typename: 'dxos.org/type/Script',
|
|
24
24
|
version: '0.1.0',
|
|
25
25
|
}),
|
|
26
|
-
LabelAnnotation.set(['name']),
|
|
26
|
+
Annotation.LabelAnnotation.set(['name']),
|
|
27
27
|
);
|
|
28
|
+
|
|
28
29
|
export interface Script extends Schema.Schema.Type<typeof Script> {}
|
|
29
30
|
|
|
30
31
|
type Props = Omit<Obj.MakeProps<typeof Script>, 'source'> & { source?: string };
|
|
31
32
|
|
|
32
|
-
export const make = ({ source = '', ...props }: Props = {}) =>
|
|
33
|
+
export const make = ({ source = '', ...props }: Props = {}): Script =>
|
|
33
34
|
Obj.make(Script, { ...props, source: Ref.make(Text.make(source)) });
|
package/src/types/Trigger.ts
CHANGED
|
@@ -6,8 +6,9 @@ import * as Schema from 'effect/Schema';
|
|
|
6
6
|
import * as SchemaAST from 'effect/SchemaAST';
|
|
7
7
|
|
|
8
8
|
import { Obj, QueryAST, Type } from '@dxos/echo';
|
|
9
|
-
import {
|
|
9
|
+
import { OptionsAnnotationId, SystemTypeAnnotation } from '@dxos/echo/internal';
|
|
10
10
|
import { DXN } from '@dxos/keys';
|
|
11
|
+
import { Expando } from '@dxos/schema';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Type discriminator for TriggerType.
|
|
@@ -21,7 +22,7 @@ const kindLiteralAnnotations = { title: 'Kind' };
|
|
|
21
22
|
|
|
22
23
|
export const EmailSpec = Schema.Struct({
|
|
23
24
|
kind: Schema.Literal('email').annotations(kindLiteralAnnotations),
|
|
24
|
-
})
|
|
25
|
+
});
|
|
25
26
|
export type EmailSpec = Schema.Schema.Type<typeof EmailSpec>;
|
|
26
27
|
|
|
27
28
|
export const QueueSpec = Schema.Struct({
|
|
@@ -29,7 +30,7 @@ export const QueueSpec = Schema.Struct({
|
|
|
29
30
|
|
|
30
31
|
// TODO(dmaretskyi): Change to a reference.
|
|
31
32
|
queue: DXN.Schema,
|
|
32
|
-
})
|
|
33
|
+
});
|
|
33
34
|
export type QueueSpec = Schema.Schema.Type<typeof QueueSpec>;
|
|
34
35
|
|
|
35
36
|
/**
|
|
@@ -40,7 +41,7 @@ export const SubscriptionSpec = Schema.Struct({
|
|
|
40
41
|
query: Schema.Struct({
|
|
41
42
|
raw: Schema.optional(Schema.String.annotations({ title: 'Query' })),
|
|
42
43
|
ast: QueryAST.Query,
|
|
43
|
-
})
|
|
44
|
+
}),
|
|
44
45
|
options: Schema.optional(
|
|
45
46
|
Schema.Struct({
|
|
46
47
|
// Watch changes to object (not just creation).
|
|
@@ -49,7 +50,7 @@ export const SubscriptionSpec = Schema.Struct({
|
|
|
49
50
|
delay: Schema.optional(Schema.Number.annotations({ title: 'Delay' })),
|
|
50
51
|
}).annotations({ title: 'Options' }),
|
|
51
52
|
),
|
|
52
|
-
})
|
|
53
|
+
});
|
|
53
54
|
export type SubscriptionSpec = Schema.Schema.Type<typeof SubscriptionSpec>;
|
|
54
55
|
|
|
55
56
|
/**
|
|
@@ -61,7 +62,7 @@ export const TimerSpec = Schema.Struct({
|
|
|
61
62
|
title: 'Cron',
|
|
62
63
|
[SchemaAST.ExamplesAnnotationId]: ['0 0 * * *'],
|
|
63
64
|
}),
|
|
64
|
-
})
|
|
65
|
+
});
|
|
65
66
|
export type TimerSpec = Schema.Schema.Type<typeof TimerSpec>;
|
|
66
67
|
|
|
67
68
|
/**
|
|
@@ -80,7 +81,7 @@ export const WebhookSpec = Schema.Struct({
|
|
|
80
81
|
title: 'Port',
|
|
81
82
|
}),
|
|
82
83
|
),
|
|
83
|
-
})
|
|
84
|
+
});
|
|
84
85
|
export type WebhookSpec = Schema.Schema.Type<typeof WebhookSpec>;
|
|
85
86
|
|
|
86
87
|
/**
|
|
@@ -96,12 +97,12 @@ export type Spec = Schema.Schema.Type<typeof Spec>;
|
|
|
96
97
|
* Function is invoked with the `payload` passed as input data.
|
|
97
98
|
* The event that triggers the function is available in the function context.
|
|
98
99
|
*/
|
|
99
|
-
const
|
|
100
|
+
const TriggerSchema = Schema.Struct({
|
|
100
101
|
/**
|
|
101
102
|
* Function or workflow to invoke.
|
|
102
103
|
*/
|
|
103
104
|
// TODO(dmaretskyi): Can be a Ref(FunctionType) or Ref(ComputeGraphType).
|
|
104
|
-
function: Schema.optional(Ref(Expando).annotations({ title: 'Function' })),
|
|
105
|
+
function: Schema.optional(Type.Ref(Expando.Expando).annotations({ title: 'Function' })),
|
|
105
106
|
|
|
106
107
|
/**
|
|
107
108
|
* Only used for workflowSchema.
|
|
@@ -110,6 +111,8 @@ const Trigger_ = Schema.Struct({
|
|
|
110
111
|
*/
|
|
111
112
|
inputNodeId: Schema.optional(Schema.String.annotations({ title: 'Input Node ID' })),
|
|
112
113
|
|
|
114
|
+
// TODO(burdon): NO BOOLEAN PROPERTIES (enabld/disabled/paused, etc.)
|
|
115
|
+
// Need lint rule; or agent rule to require PR review for "boolean" key word.
|
|
113
116
|
enabled: Schema.optional(Schema.Boolean.annotations({ title: 'Enabled' })),
|
|
114
117
|
|
|
115
118
|
spec: Schema.optional(Spec),
|
|
@@ -125,15 +128,16 @@ const Trigger_ = Schema.Struct({
|
|
|
125
128
|
* mailbox: { '/': 'dxn:echo:AAA:ZZZ' }
|
|
126
129
|
* }
|
|
127
130
|
*/
|
|
128
|
-
input: Schema.optional(Schema.
|
|
131
|
+
input: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Any })),
|
|
129
132
|
}).pipe(
|
|
130
|
-
Type.
|
|
133
|
+
Type.object({
|
|
131
134
|
typename: 'dxos.org/type/Trigger',
|
|
132
135
|
version: '0.1.0',
|
|
133
136
|
}),
|
|
137
|
+
SystemTypeAnnotation.set(true),
|
|
134
138
|
);
|
|
135
|
-
|
|
136
|
-
export interface
|
|
137
|
-
export const Trigger:
|
|
139
|
+
|
|
140
|
+
export interface Trigger extends Schema.Schema.Type<typeof TriggerSchema> {}
|
|
141
|
+
export const Trigger: Type.Obj<Trigger> = TriggerSchema as any;
|
|
138
142
|
|
|
139
143
|
export const make = (props: Obj.MakeProps<typeof Trigger>) => Obj.make(Trigger, props);
|