@project-ajax/sdk 0.0.68 → 0.0.69
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/capabilities/automation.d.ts +8 -28
- package/dist/capabilities/automation.d.ts.map +1 -1
- package/dist/capabilities/automation.js +3 -2
- package/dist/capabilities/oauth.d.ts +6 -31
- package/dist/capabilities/oauth.d.ts.map +1 -1
- package/dist/capabilities/oauth.js +4 -2
- package/dist/capabilities/sync.d.ts +4 -2
- package/dist/capabilities/sync.d.ts.map +1 -1
- package/dist/capabilities/sync.js +3 -2
- package/dist/capabilities/tool.d.ts +5 -25
- package/dist/capabilities/tool.d.ts.map +1 -1
- package/dist/capabilities/tool.js +3 -2
- package/dist/index.d.ts +5 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -9
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/worker.d.ts +175 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +218 -0
- package/package.json +1 -5
- package/src/capabilities/automation.test.ts +17 -23
- package/src/capabilities/automation.ts +14 -30
- package/src/capabilities/oauth.test.ts +16 -15
- package/src/capabilities/oauth.ts +7 -31
- package/src/capabilities/sync.ts +6 -3
- package/src/capabilities/tool.test.ts +105 -89
- package/src/capabilities/tool.ts +12 -31
- package/src/index.ts +5 -4
- package/src/types.ts +11 -0
- package/src/worker.ts +278 -0
|
@@ -8,13 +8,13 @@ import {
|
|
|
8
8
|
vi,
|
|
9
9
|
} from "vitest";
|
|
10
10
|
import {
|
|
11
|
+
createToolCapability,
|
|
11
12
|
InvalidToolInputError,
|
|
12
13
|
InvalidToolOutputError,
|
|
13
14
|
ToolExecutionError,
|
|
14
|
-
tool,
|
|
15
15
|
} from "./tool.js";
|
|
16
16
|
|
|
17
|
-
describe("tool", () => {
|
|
17
|
+
describe("Worker.tool", () => {
|
|
18
18
|
let stdoutSpy: Mock<typeof process.stdout.write>;
|
|
19
19
|
|
|
20
20
|
beforeEach(() => {
|
|
@@ -28,28 +28,28 @@ describe("tool", () => {
|
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
it("sync execution", async () => {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
const capability = createToolCapability<{ name: string }, string>(
|
|
32
|
+
"sayHello",
|
|
33
|
+
{
|
|
34
|
+
title: "Say Hello",
|
|
35
|
+
description: "Greet a user",
|
|
36
|
+
schema: {
|
|
37
|
+
type: "object",
|
|
38
|
+
properties: {
|
|
39
|
+
name: { type: "string" },
|
|
40
|
+
},
|
|
41
|
+
required: ["name"],
|
|
42
|
+
additionalProperties: false,
|
|
43
|
+
},
|
|
44
|
+
execute: (input: { name: string }) => {
|
|
45
|
+
return `Hello, ${input.name}!`;
|
|
38
46
|
},
|
|
39
|
-
required: ["name"],
|
|
40
|
-
additionalProperties: false,
|
|
41
|
-
},
|
|
42
|
-
execute: (input) => {
|
|
43
|
-
return `Hello, ${input.name}!`;
|
|
44
47
|
},
|
|
45
|
-
|
|
48
|
+
);
|
|
46
49
|
|
|
47
|
-
const result = await
|
|
50
|
+
const result = await capability.handler({ name: "Alice" });
|
|
48
51
|
|
|
49
|
-
expect(result
|
|
50
|
-
if (result._tag === "success") {
|
|
51
|
-
expect(result.value).toBe("Hello, Alice!");
|
|
52
|
-
}
|
|
52
|
+
expect(result).toEqual({ _tag: "success", value: "Hello, Alice!" });
|
|
53
53
|
|
|
54
54
|
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
55
55
|
`\n<output>"Hello, Alice!"</output>\n`,
|
|
@@ -57,30 +57,33 @@ describe("tool", () => {
|
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
it("async execution", async () => {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
const capability = createToolCapability<{ id: number }, { data: string }>(
|
|
61
|
+
"fetchData",
|
|
62
|
+
{
|
|
63
|
+
title: "Fetch Data",
|
|
64
|
+
description: "Fetch data asynchronously",
|
|
65
|
+
schema: {
|
|
66
|
+
type: "object",
|
|
67
|
+
properties: {
|
|
68
|
+
id: { type: "number" },
|
|
69
|
+
},
|
|
70
|
+
required: ["id"],
|
|
71
|
+
additionalProperties: false,
|
|
72
|
+
},
|
|
73
|
+
execute: async (input: { id: number }) => {
|
|
74
|
+
// Simulate async operation
|
|
75
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
76
|
+
return { data: `Data for ID ${input.id}` };
|
|
67
77
|
},
|
|
68
|
-
required: ["id"],
|
|
69
|
-
additionalProperties: false,
|
|
70
|
-
},
|
|
71
|
-
execute: async (input) => {
|
|
72
|
-
// Simulate async operation
|
|
73
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
74
|
-
return { data: `Data for ID ${input.id}` };
|
|
75
78
|
},
|
|
76
|
-
|
|
79
|
+
);
|
|
77
80
|
|
|
78
|
-
const result = await
|
|
81
|
+
const result = await capability.handler({ id: 42 });
|
|
79
82
|
|
|
80
|
-
expect(result
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
83
|
+
expect(result).toEqual({
|
|
84
|
+
_tag: "success",
|
|
85
|
+
value: { data: "Data for ID 42" },
|
|
86
|
+
});
|
|
84
87
|
|
|
85
88
|
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
86
89
|
`\n<output>{"data":"Data for ID 42"}</output>\n`,
|
|
@@ -88,27 +91,31 @@ describe("tool", () => {
|
|
|
88
91
|
});
|
|
89
92
|
|
|
90
93
|
it("execution error", async () => {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
const capability = createToolCapability<Record<string, never>, string>(
|
|
95
|
+
"throwError",
|
|
96
|
+
{
|
|
97
|
+
title: "Throw Error",
|
|
98
|
+
description: "Throws an error",
|
|
99
|
+
schema: {
|
|
100
|
+
type: "object",
|
|
101
|
+
properties: {},
|
|
102
|
+
required: [],
|
|
103
|
+
additionalProperties: false,
|
|
104
|
+
},
|
|
105
|
+
execute: () => {
|
|
106
|
+
throw new Error("Something went wrong");
|
|
107
|
+
},
|
|
102
108
|
},
|
|
103
|
-
|
|
109
|
+
);
|
|
104
110
|
|
|
105
|
-
const result = await
|
|
111
|
+
const result = (await capability.handler({})) as {
|
|
112
|
+
_tag: "error";
|
|
113
|
+
error: ToolExecutionError;
|
|
114
|
+
};
|
|
106
115
|
|
|
107
116
|
expect(result._tag).toBe("error");
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
expect(result.error.message).toBe("Something went wrong");
|
|
111
|
-
}
|
|
117
|
+
expect(result.error).toBeInstanceOf(ToolExecutionError);
|
|
118
|
+
expect(result.error.message).toBe("Something went wrong");
|
|
112
119
|
|
|
113
120
|
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
114
121
|
`\n<output>{"_tag":"error","error":{"name":"ToolExecutionError","message":"Something went wrong"}}</output>\n`,
|
|
@@ -116,29 +123,33 @@ describe("tool", () => {
|
|
|
116
123
|
});
|
|
117
124
|
|
|
118
125
|
it("invalid input", async () => {
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
+
const capability = createToolCapability<{ name: string }, string>(
|
|
127
|
+
"sayHelloStrict",
|
|
128
|
+
{
|
|
129
|
+
title: "Say Hello",
|
|
130
|
+
description: "Requires a name property",
|
|
131
|
+
schema: {
|
|
132
|
+
type: "object",
|
|
133
|
+
properties: {
|
|
134
|
+
name: { type: "string" },
|
|
135
|
+
},
|
|
136
|
+
required: ["name"],
|
|
137
|
+
additionalProperties: false,
|
|
138
|
+
},
|
|
139
|
+
execute: (input: { name: string }) => {
|
|
140
|
+
return `Hello, ${input.name}!`;
|
|
126
141
|
},
|
|
127
|
-
required: ["name"],
|
|
128
|
-
additionalProperties: false,
|
|
129
|
-
},
|
|
130
|
-
execute: (input) => {
|
|
131
|
-
return `Hello, ${input.name}!`;
|
|
132
142
|
},
|
|
133
|
-
|
|
143
|
+
);
|
|
134
144
|
|
|
135
|
-
const result = await
|
|
145
|
+
const result = (await capability.handler({ age: 25 })) as {
|
|
146
|
+
_tag: "error";
|
|
147
|
+
error: InvalidToolInputError;
|
|
148
|
+
};
|
|
136
149
|
|
|
137
150
|
expect(result._tag).toBe("error");
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
expect(result.error.message).toContain("name");
|
|
141
|
-
}
|
|
151
|
+
expect(result.error).toBeInstanceOf(InvalidToolInputError);
|
|
152
|
+
expect(result.error.message).toContain("name");
|
|
142
153
|
|
|
143
154
|
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
144
155
|
expect.stringContaining(
|
|
@@ -148,7 +159,10 @@ describe("tool", () => {
|
|
|
148
159
|
});
|
|
149
160
|
|
|
150
161
|
it("invalid output", async () => {
|
|
151
|
-
const
|
|
162
|
+
const capability = createToolCapability<
|
|
163
|
+
Record<string, never>,
|
|
164
|
+
{ result: string }
|
|
165
|
+
>("invalidOutput", {
|
|
152
166
|
title: "Return Invalid Output",
|
|
153
167
|
description: "Returns output that doesn't match schema",
|
|
154
168
|
schema: {
|
|
@@ -171,13 +185,14 @@ describe("tool", () => {
|
|
|
171
185
|
},
|
|
172
186
|
});
|
|
173
187
|
|
|
174
|
-
const result = await
|
|
188
|
+
const result = (await capability.handler({})) as {
|
|
189
|
+
_tag: "error";
|
|
190
|
+
error: InvalidToolOutputError;
|
|
191
|
+
};
|
|
175
192
|
|
|
176
193
|
expect(result._tag).toBe("error");
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
expect(result.error.message).toContain("result");
|
|
180
|
-
}
|
|
194
|
+
expect(result.error).toBeInstanceOf(InvalidToolOutputError);
|
|
195
|
+
expect(result.error.message).toContain("result");
|
|
181
196
|
|
|
182
197
|
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
183
198
|
expect.stringContaining(
|
|
@@ -187,10 +202,10 @@ describe("tool", () => {
|
|
|
187
202
|
});
|
|
188
203
|
|
|
189
204
|
it("invalid output with custom output schema", async () => {
|
|
190
|
-
const
|
|
205
|
+
const capability = createToolCapability<
|
|
191
206
|
{ value: number },
|
|
192
207
|
{ doubled: number; message: string }
|
|
193
|
-
>({
|
|
208
|
+
>("customOutput", {
|
|
194
209
|
title: "Custom Output",
|
|
195
210
|
description: "Has custom output schema",
|
|
196
211
|
schema: {
|
|
@@ -211,20 +226,21 @@ describe("tool", () => {
|
|
|
211
226
|
additionalProperties: false,
|
|
212
227
|
},
|
|
213
228
|
// @ts-expect-error Testing invalid output - intentionally missing message property
|
|
214
|
-
execute: (input) => {
|
|
229
|
+
execute: (input: { value: number }) => {
|
|
215
230
|
return {
|
|
216
231
|
doubled: input.value * 2,
|
|
217
232
|
};
|
|
218
233
|
},
|
|
219
234
|
});
|
|
220
235
|
|
|
221
|
-
const result = await
|
|
236
|
+
const result = (await capability.handler({ value: 5 })) as {
|
|
237
|
+
_tag: "error";
|
|
238
|
+
error: InvalidToolOutputError;
|
|
239
|
+
};
|
|
222
240
|
|
|
223
241
|
expect(result._tag).toBe("error");
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
expect(result.error.message).toContain("message");
|
|
227
|
-
}
|
|
242
|
+
expect(result.error).toBeInstanceOf(InvalidToolOutputError);
|
|
243
|
+
expect(result.error.message).toContain("message");
|
|
228
244
|
|
|
229
245
|
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
230
246
|
expect.stringContaining(
|
package/src/capabilities/tool.ts
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import { Ajv, type JSONSchemaType } from "ajv";
|
|
2
|
-
|
|
3
|
-
type JSONValue =
|
|
4
|
-
| string
|
|
5
|
-
| number
|
|
6
|
-
| boolean
|
|
7
|
-
| null
|
|
8
|
-
| JSONValue[]
|
|
9
|
-
| { [key: string]: JSONValue };
|
|
2
|
+
import type { JSONValue } from "../types.js";
|
|
10
3
|
|
|
11
4
|
export interface ToolConfiguration<
|
|
12
5
|
I extends JSONValue,
|
|
@@ -70,34 +63,21 @@ export class ToolExecutionError extends Error {
|
|
|
70
63
|
}
|
|
71
64
|
}
|
|
72
65
|
|
|
66
|
+
export type ToolCapability<
|
|
67
|
+
I extends JSONValue,
|
|
68
|
+
O extends JSONValue = JSONValue,
|
|
69
|
+
> = ReturnType<typeof createToolCapability<I, O>>;
|
|
70
|
+
|
|
73
71
|
/**
|
|
74
72
|
* Creates a capability definition for a tool to be used by an agent.
|
|
75
73
|
*
|
|
76
|
-
* Example:
|
|
77
|
-
*
|
|
78
|
-
* ```ts
|
|
79
|
-
* tool<{ name: string }>({
|
|
80
|
-
* title: "Say Hello",
|
|
81
|
-
* description: "Say hello to the user",
|
|
82
|
-
* schema: {
|
|
83
|
-
* type: "object",
|
|
84
|
-
* properties: {
|
|
85
|
-
* name: { type: "string" },
|
|
86
|
-
* },
|
|
87
|
-
* required: ["name"],
|
|
88
|
-
* },
|
|
89
|
-
* execute: ({ name }) => {
|
|
90
|
-
* return `Hello, ${name}!`;
|
|
91
|
-
* },
|
|
92
|
-
* })
|
|
93
|
-
* ```
|
|
94
|
-
*
|
|
95
74
|
* @param config - The configuration for the tool.
|
|
96
75
|
* @returns A capability definition for the tool.
|
|
97
76
|
*/
|
|
98
|
-
export function
|
|
99
|
-
|
|
100
|
-
|
|
77
|
+
export function createToolCapability<
|
|
78
|
+
I extends JSONValue,
|
|
79
|
+
O extends JSONValue = JSONValue,
|
|
80
|
+
>(key: string, config: ToolConfiguration<I, O>) {
|
|
101
81
|
const ajv = new Ajv();
|
|
102
82
|
const validateInput = ajv.compile(config.schema);
|
|
103
83
|
const validateOutput = config.outputSchema
|
|
@@ -105,7 +85,8 @@ export function tool<I extends JSONValue, O extends JSONValue = JSONValue>(
|
|
|
105
85
|
: null;
|
|
106
86
|
|
|
107
87
|
return {
|
|
108
|
-
_tag: "tool",
|
|
88
|
+
_tag: "tool" as const,
|
|
89
|
+
key,
|
|
109
90
|
config: {
|
|
110
91
|
title: config.title,
|
|
111
92
|
description: config.description,
|
package/src/index.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
export { emojiIcon, imageIcon, notionIcon, place } from "./builder.js";
|
|
2
2
|
export type {
|
|
3
|
+
AutomationCapability,
|
|
3
4
|
AutomationConfiguration,
|
|
4
5
|
AutomationContext,
|
|
5
6
|
PageObjectResponse,
|
|
6
7
|
} from "./capabilities/automation.js";
|
|
7
|
-
export { automation } from "./capabilities/automation.js";
|
|
8
8
|
export type {
|
|
9
9
|
NotionManagedOAuthConfiguration,
|
|
10
|
+
OAuthCapability,
|
|
10
11
|
OAuthConfiguration,
|
|
11
12
|
UserManagedOAuthConfiguration,
|
|
12
13
|
} from "./capabilities/oauth.js";
|
|
13
|
-
export { oauth } from "./capabilities/oauth.js";
|
|
14
14
|
export type {
|
|
15
|
+
SyncCapability,
|
|
15
16
|
SyncConfiguration,
|
|
16
17
|
SyncExecutionResult,
|
|
17
18
|
SyncedObject,
|
|
18
19
|
} from "./capabilities/sync.js";
|
|
19
|
-
export {
|
|
20
|
-
export { tool } from "./capabilities/tool.js";
|
|
20
|
+
export type { ToolCapability, ToolConfiguration } from "./capabilities/tool.js";
|
|
21
21
|
export type {
|
|
22
22
|
Icon,
|
|
23
23
|
ImageIcon,
|
|
@@ -26,3 +26,4 @@ export type {
|
|
|
26
26
|
PlaceValue,
|
|
27
27
|
Schedule,
|
|
28
28
|
} from "./types.js";
|
|
29
|
+
export { Worker } from "./worker.js";
|
package/src/types.ts
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A JSON value is a string, number, boolean, null, array, or object.
|
|
3
|
+
*/
|
|
4
|
+
export type JSONValue =
|
|
5
|
+
| string
|
|
6
|
+
| number
|
|
7
|
+
| boolean
|
|
8
|
+
| null
|
|
9
|
+
| JSONValue[]
|
|
10
|
+
| { [key: string]: JSONValue };
|
|
11
|
+
|
|
1
12
|
/**
|
|
2
13
|
* A text token is a tuple where the first element is the text content
|
|
3
14
|
* and optional subsequent elements are formatting annotations.
|
package/src/worker.ts
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AutomationCapability,
|
|
3
|
+
AutomationConfiguration,
|
|
4
|
+
AutomationContext,
|
|
5
|
+
} from "./capabilities/automation.js";
|
|
6
|
+
import { createAutomationCapability } from "./capabilities/automation.js";
|
|
7
|
+
import type {
|
|
8
|
+
NotionManagedOAuthConfiguration,
|
|
9
|
+
OAuthCapability,
|
|
10
|
+
OAuthConfiguration,
|
|
11
|
+
UserManagedOAuthConfiguration,
|
|
12
|
+
} from "./capabilities/oauth.js";
|
|
13
|
+
import { createOAuthCapability } from "./capabilities/oauth.js";
|
|
14
|
+
import type { SyncCapability, SyncConfiguration } from "./capabilities/sync.js";
|
|
15
|
+
import { createSyncCapability } from "./capabilities/sync.js";
|
|
16
|
+
import type { ToolCapability, ToolConfiguration } from "./capabilities/tool.js";
|
|
17
|
+
import { createToolCapability } from "./capabilities/tool.js";
|
|
18
|
+
import type { Schema } from "./schema.js";
|
|
19
|
+
import type { JSONValue } from "./types.js";
|
|
20
|
+
|
|
21
|
+
// Re-export types for convenience
|
|
22
|
+
export type {
|
|
23
|
+
AutomationConfiguration,
|
|
24
|
+
AutomationContext,
|
|
25
|
+
OAuthConfiguration,
|
|
26
|
+
NotionManagedOAuthConfiguration,
|
|
27
|
+
UserManagedOAuthConfiguration,
|
|
28
|
+
SyncConfiguration,
|
|
29
|
+
ToolConfiguration,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// Capability types
|
|
34
|
+
// ============================================================================
|
|
35
|
+
|
|
36
|
+
type Capability =
|
|
37
|
+
| SyncCapability
|
|
38
|
+
// biome-ignore lint/suspicious/noExplicitAny: any is used to allow any input and output types
|
|
39
|
+
| ToolCapability<any, any>
|
|
40
|
+
| AutomationCapability
|
|
41
|
+
| OAuthCapability;
|
|
42
|
+
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// Worker class
|
|
45
|
+
// ============================================================================
|
|
46
|
+
|
|
47
|
+
export class Worker {
|
|
48
|
+
#capabilities: Map<string, Capability> = new Map();
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Register a sync capability.
|
|
52
|
+
*
|
|
53
|
+
* Example:
|
|
54
|
+
*
|
|
55
|
+
* ```ts
|
|
56
|
+
* import { Worker } from "@project-ajax/sdk";
|
|
57
|
+
* import * as Builder from "@project-ajax/sdk/builder";
|
|
58
|
+
* import * as Schema from "@project-ajax/sdk/schema";
|
|
59
|
+
*
|
|
60
|
+
* const worker = new Worker();
|
|
61
|
+
* export default worker;
|
|
62
|
+
*
|
|
63
|
+
* worker.sync("tasksSync", {
|
|
64
|
+
* primaryKeyProperty: "Task ID",
|
|
65
|
+
* schema: {
|
|
66
|
+
* defaultName: "Tasks",
|
|
67
|
+
* properties: {
|
|
68
|
+
* "Task Name": Schema.title(),
|
|
69
|
+
* "Task ID": Schema.richText(),
|
|
70
|
+
* Status: Schema.select([
|
|
71
|
+
* { name: "Open", color: "default" },
|
|
72
|
+
* { name: "Done", color: "green" },
|
|
73
|
+
* ]),
|
|
74
|
+
* },
|
|
75
|
+
* },
|
|
76
|
+
* execute: async () => {
|
|
77
|
+
* const objects = [
|
|
78
|
+
* {
|
|
79
|
+
* key: "task-1",
|
|
80
|
+
* properties: {
|
|
81
|
+
* "Task Name": Builder.title("Write docs"),
|
|
82
|
+
* "Task ID": Builder.richText("task-1"),
|
|
83
|
+
* Status: Builder.select("Open"),
|
|
84
|
+
* },
|
|
85
|
+
* },
|
|
86
|
+
* ];
|
|
87
|
+
*
|
|
88
|
+
* return { objects, done: true };
|
|
89
|
+
* },
|
|
90
|
+
* });
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* @param key - The unique key for this capability.
|
|
94
|
+
* @param config - The sync configuration.
|
|
95
|
+
* @returns The capability object.
|
|
96
|
+
*/
|
|
97
|
+
sync<PK extends string, S extends Schema<PK>, Context = unknown>(
|
|
98
|
+
key: string,
|
|
99
|
+
config: SyncConfiguration<PK, S, Context>,
|
|
100
|
+
): SyncCapability {
|
|
101
|
+
this.#validateUniqueKey(key);
|
|
102
|
+
const capability = createSyncCapability(key, config);
|
|
103
|
+
this.#capabilities.set(key, capability);
|
|
104
|
+
return capability;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Register a tool capability.
|
|
109
|
+
*
|
|
110
|
+
* Example:
|
|
111
|
+
*
|
|
112
|
+
* ```ts
|
|
113
|
+
* worker.tool<{ name: string }, string>("sayHello", {
|
|
114
|
+
* title: "Say Hello",
|
|
115
|
+
* description: "Say hello to the user",
|
|
116
|
+
* schema: {
|
|
117
|
+
* type: "object",
|
|
118
|
+
* properties: {
|
|
119
|
+
* name: { type: "string" },
|
|
120
|
+
* },
|
|
121
|
+
* required: ["name"],
|
|
122
|
+
* },
|
|
123
|
+
* execute: ({ name }) => {
|
|
124
|
+
* return `Hello, ${name}!`;
|
|
125
|
+
* },
|
|
126
|
+
* })
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
*
|
|
130
|
+
* @param key - The unique key for this capability.
|
|
131
|
+
* @param config - The tool configuration.
|
|
132
|
+
* @returns The capability object.
|
|
133
|
+
*/
|
|
134
|
+
tool<I extends JSONValue, O extends JSONValue = JSONValue>(
|
|
135
|
+
key: string,
|
|
136
|
+
config: ToolConfiguration<I, O>,
|
|
137
|
+
): ToolCapability<I, O> {
|
|
138
|
+
this.#validateUniqueKey(key);
|
|
139
|
+
const capability = createToolCapability(key, config);
|
|
140
|
+
// biome-ignore lint/suspicious/noExplicitAny: any is used to allow any input and output types
|
|
141
|
+
this.#capabilities.set(key, capability as ToolCapability<any, any>);
|
|
142
|
+
return capability;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Register an automation capability.
|
|
147
|
+
*
|
|
148
|
+
* Example:
|
|
149
|
+
*
|
|
150
|
+
* ```ts
|
|
151
|
+
* const worker = new Worker();
|
|
152
|
+
* export default worker;
|
|
153
|
+
*
|
|
154
|
+
* worker.automation("sendWelcomeEmail", {
|
|
155
|
+
* title: "Send Welcome Email",
|
|
156
|
+
* description: "Sends a welcome email when a new user is added",
|
|
157
|
+
* execute: async (context) => {
|
|
158
|
+
* const { pageId, pageData } = context;
|
|
159
|
+
*
|
|
160
|
+
* // Access page properties from the Public API format
|
|
161
|
+
* if (pageData) {
|
|
162
|
+
* const name = pageData.properties.Name; // Access any property
|
|
163
|
+
* const status = pageData.properties.Status;
|
|
164
|
+
* console.log(`Processing: ${name}`);
|
|
165
|
+
* }
|
|
166
|
+
*
|
|
167
|
+
* // Your automation logic here
|
|
168
|
+
* await sendEmail(pageId);
|
|
169
|
+
* },
|
|
170
|
+
* })
|
|
171
|
+
* ```
|
|
172
|
+
*
|
|
173
|
+
* @param key - The unique key for this capability.
|
|
174
|
+
* @param config - The automation configuration.
|
|
175
|
+
* @returns The capability object.
|
|
176
|
+
*/
|
|
177
|
+
automation(
|
|
178
|
+
key: string,
|
|
179
|
+
config: AutomationConfiguration,
|
|
180
|
+
): AutomationCapability {
|
|
181
|
+
this.#validateUniqueKey(key);
|
|
182
|
+
const capability = createAutomationCapability(key, config);
|
|
183
|
+
this.#capabilities.set(key, capability);
|
|
184
|
+
return capability;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Register an OAuth capability.
|
|
189
|
+
*
|
|
190
|
+
* There are two ways to configure OAuth:
|
|
191
|
+
*
|
|
192
|
+
* 1. Notion-managed providers:
|
|
193
|
+
* ```ts
|
|
194
|
+
* const worker = new Worker();
|
|
195
|
+
* export default worker;
|
|
196
|
+
*
|
|
197
|
+
* worker.oauth("googleAuth", {
|
|
198
|
+
* type: "notion_managed",
|
|
199
|
+
* name: "my-google-auth",
|
|
200
|
+
* provider: "google"
|
|
201
|
+
* })
|
|
202
|
+
* ```
|
|
203
|
+
*
|
|
204
|
+
* 2. User-managed OAuth configuration:
|
|
205
|
+
* ```ts
|
|
206
|
+
* const worker = new Worker();
|
|
207
|
+
* export default worker;
|
|
208
|
+
*
|
|
209
|
+
* worker.oauth("myCustomAuth", {
|
|
210
|
+
* type: "user_managed",
|
|
211
|
+
* name: "my-custom-oauth",
|
|
212
|
+
* authorizationEndpoint: "https://provider.com/oauth/authorize",
|
|
213
|
+
* tokenEndpoint: "https://provider.com/oauth/token",
|
|
214
|
+
* scope: "read write",
|
|
215
|
+
* clientId: process.env.CLIENT_ID,
|
|
216
|
+
* clientSecret: process.env.CLIENT_SECRET,
|
|
217
|
+
* authorizationParams: {
|
|
218
|
+
* access_type: "offline",
|
|
219
|
+
* prompt: "consent"
|
|
220
|
+
* }
|
|
221
|
+
* })
|
|
222
|
+
* ```
|
|
223
|
+
*
|
|
224
|
+
* @param key - The unique key used to register this OAuth capability.
|
|
225
|
+
* @param config - The OAuth configuration (Notion-managed or user-managed) for this capability.
|
|
226
|
+
* @returns The registered OAuth capability.
|
|
227
|
+
*/
|
|
228
|
+
oauth(key: string, config: OAuthConfiguration): OAuthCapability {
|
|
229
|
+
this.#validateUniqueKey(key);
|
|
230
|
+
const capability = createOAuthCapability(key, config);
|
|
231
|
+
this.#capabilities.set(key, capability);
|
|
232
|
+
return capability;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Get all registered capabilities (for discovery) without their handlers.
|
|
237
|
+
*/
|
|
238
|
+
get capabilities(): Pick<Capability, "_tag" | "key" | "config">[] {
|
|
239
|
+
return Array.from(this.#capabilities.values()).map((c) => ({
|
|
240
|
+
_tag: c._tag,
|
|
241
|
+
key: c.key,
|
|
242
|
+
config: c.config,
|
|
243
|
+
}));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Execute a capability by key.
|
|
248
|
+
*
|
|
249
|
+
* @param key - The key of the capability to execute.
|
|
250
|
+
* @param context - The context to pass to the capability.
|
|
251
|
+
* @returns The result of the capability execution.
|
|
252
|
+
*/
|
|
253
|
+
async run(key: string, context: unknown): Promise<unknown> {
|
|
254
|
+
const capability = this.#capabilities.get(key);
|
|
255
|
+
|
|
256
|
+
if (!capability) {
|
|
257
|
+
throw new Error(`Capability "${key}" not found`);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (capability._tag === "oauth") {
|
|
261
|
+
throw new Error(
|
|
262
|
+
`Cannot run OAuth capability "${key}" - OAuth capabilities only provide configuration`,
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// biome-ignore lint/suspicious/noExplicitAny: context is unknown, passed by external non-typed code.
|
|
267
|
+
return capability.handler(context as any);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
#validateUniqueKey(key: string): void {
|
|
271
|
+
if (!key || typeof key !== "string") {
|
|
272
|
+
throw new Error("Capability key must be a non-empty string");
|
|
273
|
+
}
|
|
274
|
+
if (this.#capabilities.has(key)) {
|
|
275
|
+
throw new Error(`Capability with key "${key}" already registered`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|