@notionhq/workers 0.3.0 → 0.4.0
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/ai_connector.js +1 -1
- package/dist/capabilities/automation.js +1 -1
- package/dist/capabilities/context.d.ts +2 -1
- package/dist/capabilities/context.d.ts.map +1 -1
- package/dist/capabilities/context.js +21 -3
- package/dist/capabilities/sync.d.ts.map +1 -1
- package/dist/capabilities/sync.js +25 -5
- package/dist/capabilities/tool.d.ts +21 -0
- package/dist/capabilities/tool.d.ts.map +1 -1
- package/dist/capabilities/tool.js +3 -2
- package/dist/capabilities/webhook.js +1 -1
- package/dist/error.d.ts +36 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +14 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/capabilities/ai_connector.ts +1 -1
- package/src/capabilities/automation.ts +1 -1
- package/src/capabilities/context.ts +45 -3
- package/src/capabilities/sync.test.ts +108 -1
- package/src/capabilities/sync.ts +40 -5
- package/src/capabilities/tool.test.ts +63 -0
- package/src/capabilities/tool.ts +23 -1
- package/src/capabilities/webhook.ts +1 -1
- package/src/error.ts +40 -0
- package/src/index.ts +6 -1
- package/src/worker.ts +2 -0
|
@@ -19,7 +19,7 @@ function createAiConnectorCapability(key, configuration, pacerDeclarations) {
|
|
|
19
19
|
...configuration.schedule === void 0 ? {} : { schedule: parseSchedule(configuration.schedule) }
|
|
20
20
|
},
|
|
21
21
|
async handler(runtimeContext, options) {
|
|
22
|
-
const capabilityContext = createCapabilityContext();
|
|
22
|
+
const capabilityContext = createCapabilityContext("ai_connector");
|
|
23
23
|
const state = runtimeContext?.state ?? runtimeContext?.userContext;
|
|
24
24
|
initPacerState(runtimeContext?.pacers, pacerDeclarations);
|
|
25
25
|
let executionResult;
|
|
@@ -11,7 +11,7 @@ function createAutomationCapability(key, config) {
|
|
|
11
11
|
},
|
|
12
12
|
async handler(event, options) {
|
|
13
13
|
try {
|
|
14
|
-
const capabilityContext = createCapabilityContext();
|
|
14
|
+
const capabilityContext = createCapabilityContext("automation");
|
|
15
15
|
await config.execute(event, capabilityContext);
|
|
16
16
|
if (options?.concreteOutput) {
|
|
17
17
|
return { status: "success" };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Client } from "@notionhq/client";
|
|
2
|
+
import type { CapabilityType } from "../worker.js";
|
|
2
3
|
export type CapabilityContext = {
|
|
3
4
|
/** Notion API SDK client for this execution. */
|
|
4
5
|
notion: Client;
|
|
5
6
|
};
|
|
6
|
-
export declare function createCapabilityContext(): CapabilityContext;
|
|
7
|
+
export declare function createCapabilityContext(capabilityType: CapabilityType): CapabilityContext;
|
|
7
8
|
//# sourceMappingURL=context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/capabilities/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/capabilities/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,MAAM,iBAAiB,GAAG;IAC/B,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;CACf,CAAC;AAwCF,wBAAgB,uBAAuB,CACtC,cAAc,EAAE,cAAc,GAC5B,iBAAiB,CAenB"}
|
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
import { Client } from "@notionhq/client";
|
|
2
|
-
|
|
2
|
+
const FIX_STEPS = "\n\nTo fix this:\n1. Create an internal integration at https://www.notion.so/profile/integrations/internal\n2. Give the integration access to the relevant pages/databases\n3. Add NOTION_API_TOKEN=<your-token> to your .env file\n4. For deployed workers, run: ntn workers env push";
|
|
3
|
+
function missingTokenMessage(capabilityType) {
|
|
4
|
+
if (capabilityType === "tool") {
|
|
5
|
+
return "NOTION_API_TOKEN is not set. When tools are called through a Custom Agent, the platform sets NOTION_API_TOKEN automatically, using the permissions of the Custom Agent. Outside of that context (e.g. ntn workers exec), you must set it yourself." + FIX_STEPS;
|
|
6
|
+
}
|
|
7
|
+
return `NOTION_API_TOKEN is not set. context.notion requires an API token to make requests in ${capabilityType} capabilities to Notion.` + FIX_STEPS;
|
|
8
|
+
}
|
|
9
|
+
function createUnauthenticatedNotionProxy(capabilityType) {
|
|
10
|
+
return new Proxy({}, {
|
|
11
|
+
get(_target, prop) {
|
|
12
|
+
if (prop === "then" || typeof prop === "symbol") {
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
throw new Error(missingTokenMessage(capabilityType));
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function createCapabilityContext(capabilityType) {
|
|
3
20
|
const options = {};
|
|
4
21
|
if (process.env.NOTION_API_BASE_URL) {
|
|
5
22
|
options.baseUrl = process.env.NOTION_API_BASE_URL;
|
|
6
23
|
}
|
|
7
|
-
if (process.env.NOTION_API_TOKEN) {
|
|
8
|
-
|
|
24
|
+
if (!process.env.NOTION_API_TOKEN) {
|
|
25
|
+
return { notion: createUnauthenticatedNotionProxy(capabilityType) };
|
|
9
26
|
}
|
|
27
|
+
options.auth = process.env.NOTION_API_TOKEN;
|
|
10
28
|
const notion = new Client(options);
|
|
11
29
|
return { notion };
|
|
12
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/capabilities/sync.ts"],"names":[],"mappings":"AACA,OAAO,EAGN,KAAK,gBAAgB,EACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EACX,qBAAqB,EACrB,cAAc,EACd,MAAM,EACN,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EACX,cAAc,EACd,IAAI,EACJ,WAAW,EACX,UAAU,EACV,aAAa,EACb,QAAQ,EACR,SAAS,EACT,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGtD,OAAO,EAEN,KAAK,sBAAsB,EAC3B,KAAK,cAAc,IAAI,sBAAsB,EAC7C,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/capabilities/sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ExecutionError } from "../error.js";
|
|
1
|
+
import { ExecutionError, RateLimitError } from "../error.js";
|
|
2
2
|
import {
|
|
3
3
|
getPacerState,
|
|
4
4
|
initPacerState
|
|
@@ -19,7 +19,7 @@ function createSyncCapability(key, syncConfiguration, pacerDeclarations) {
|
|
|
19
19
|
schedule: syncConfiguration.schedule ? parseSchedule(syncConfiguration.schedule) : void 0
|
|
20
20
|
},
|
|
21
21
|
async handler(runtimeContext, options) {
|
|
22
|
-
const capabilityContext = createCapabilityContext();
|
|
22
|
+
const capabilityContext = createCapabilityContext("sync");
|
|
23
23
|
const state = runtimeContext?.state ?? runtimeContext?.userContext;
|
|
24
24
|
initPacerState(runtimeContext?.pacers, pacerDeclarations);
|
|
25
25
|
let executionResult;
|
|
@@ -29,12 +29,32 @@ function createSyncCapability(key, syncConfiguration, pacerDeclarations) {
|
|
|
29
29
|
capabilityContext
|
|
30
30
|
);
|
|
31
31
|
} catch (err) {
|
|
32
|
+
if (err instanceof RateLimitError) {
|
|
33
|
+
if (!options?.concreteOutput) {
|
|
34
|
+
const envelope = {
|
|
35
|
+
_tag: "error",
|
|
36
|
+
error: {
|
|
37
|
+
_tag: "rate_limit",
|
|
38
|
+
name: err.name,
|
|
39
|
+
message: err.message,
|
|
40
|
+
...err.retryAfter !== void 0 ? { retryAfter: err.retryAfter } : {}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
writeOutput(envelope);
|
|
44
|
+
}
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
32
47
|
const error = new ExecutionError(err);
|
|
33
48
|
if (!options?.concreteOutput) {
|
|
34
|
-
|
|
49
|
+
const envelope = {
|
|
35
50
|
_tag: "error",
|
|
36
|
-
error: {
|
|
37
|
-
|
|
51
|
+
error: {
|
|
52
|
+
_tag: "generic",
|
|
53
|
+
name: error.name,
|
|
54
|
+
message: error.message
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
writeOutput(envelope);
|
|
38
58
|
}
|
|
39
59
|
throw error;
|
|
40
60
|
}
|
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
import type { SchemaBuilder } from "../schema-builder.js";
|
|
2
2
|
import type { HandlerOptions, JSONValue } from "../types.js";
|
|
3
3
|
import type { CapabilityContext } from "./context.js";
|
|
4
|
+
/**
|
|
5
|
+
* Hints describing a tool's behavior. Advisory metadata read by the platform
|
|
6
|
+
* (not the model) to decide whether a call is safe to auto-execute or
|
|
7
|
+
* requires user confirmation.
|
|
8
|
+
*
|
|
9
|
+
* Field names mirror the MCP tool annotation shape
|
|
10
|
+
* (`McpServerToolAnnotations`) so worker tools flow through the same
|
|
11
|
+
* confirmation / auto-execute machinery as MCP tools.
|
|
12
|
+
*
|
|
13
|
+
* Only hints with active consumers in the Notion client are included.
|
|
14
|
+
*/
|
|
15
|
+
export interface ToolHints {
|
|
16
|
+
/**
|
|
17
|
+
* The tool only reads state and has no side effects. Safe to call
|
|
18
|
+
* repeatedly; safe to auto-execute under default policy. Tools without
|
|
19
|
+
* this hint are treated as write tools and prompt for confirmation.
|
|
20
|
+
*/
|
|
21
|
+
readOnlyHint?: boolean;
|
|
22
|
+
}
|
|
4
23
|
export interface ToolConfiguration<I extends JSONValue, O extends JSONValue = JSONValue> {
|
|
5
24
|
title: string;
|
|
6
25
|
description: string;
|
|
7
26
|
schema: SchemaBuilder<I>;
|
|
8
27
|
outputSchema?: SchemaBuilder<O>;
|
|
28
|
+
hints?: ToolHints;
|
|
9
29
|
execute: (input: I, context: CapabilityContext) => O | Promise<O>;
|
|
10
30
|
}
|
|
11
31
|
/**
|
|
@@ -56,6 +76,7 @@ export declare function createToolCapability<I extends JSONValue, O extends JSON
|
|
|
56
76
|
description: string;
|
|
57
77
|
schema: import("../json-schema.js").JSONSchema<I>;
|
|
58
78
|
outputSchema: import("../json-schema.js").JSONSchema<O> | undefined;
|
|
79
|
+
hints: ToolHints | undefined;
|
|
59
80
|
};
|
|
60
81
|
handler: {
|
|
61
82
|
(input: JSONValue, options: HandlerOptions): Promise<O>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/capabilities/tool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAkEtD,MAAM,WAAW,iBAAiB,CACjC,CAAC,SAAS,SAAS,EACnB,CAAC,SAAS,SAAS,GAAG,SAAS;IAE/B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACzB,YAAY,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAChC,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,iBAAiB,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAClE;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;IAK3B,MAAM;;;;;CAON;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;IAK3B,MAAM;;;;;CAON;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;IAK3B,MAAM;;;;;CAON;AAED,MAAM,MAAM,cAAc,CACzB,CAAC,SAAS,SAAS,EACnB,CAAC,SAAS,SAAS,GAAG,SAAS,IAC5B,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAElD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CACnC,CAAC,SAAS,SAAS,EACnB,CAAC,SAAS,SAAS,GAAG,SAAS,EAC9B,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC
|
|
1
|
+
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/capabilities/tool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAkEtD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,SAAS;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB,CACjC,CAAC,SAAS,SAAS,EACnB,CAAC,SAAS,SAAS,GAAG,SAAS;IAE/B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACzB,YAAY,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,iBAAiB,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAClE;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;IAK3B,MAAM;;;;;CAON;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;IAK3B,MAAM;;;;;CAON;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;IAK3B,MAAM;;;;;CAON;AAED,MAAM,MAAM,cAAc,CACzB,CAAC,SAAS,SAAS,EACnB,CAAC,SAAS,SAAS,GAAG,SAAS,IAC5B,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAElD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CACnC,CAAC,SAAS,SAAS,EACnB,CAAC,SAAS,SAAS,GAAG,SAAS,EAC9B,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC;;;;;;;;;;;gBAiBf,SAAS,WAAW,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;gBAC/C,SAAS,GAAG,OAAO,CAC9C;YACA,IAAI,EAAE,SAAS,CAAC;YAChB,KAAK,EAAE,CAAC,CAAC;SACR,GACD;YACA,IAAI,EAAE,OAAO,CAAC;YACd,KAAK,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,OAAO,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;aAAE,CAAC;SACnE,CACH;;EA6GD"}
|
|
@@ -114,7 +114,7 @@ function createToolCapability(key, config) {
|
|
|
114
114
|
return result;
|
|
115
115
|
}
|
|
116
116
|
try {
|
|
117
|
-
const capabilityContext = createCapabilityContext();
|
|
117
|
+
const capabilityContext = createCapabilityContext("tool");
|
|
118
118
|
const result = await config.execute(input, capabilityContext);
|
|
119
119
|
if (validateOutput && !validateOutput(result)) {
|
|
120
120
|
const error = new InvalidToolOutputError(
|
|
@@ -164,7 +164,8 @@ function createToolCapability(key, config) {
|
|
|
164
164
|
title: config.title,
|
|
165
165
|
description: config.description,
|
|
166
166
|
schema: inputSchema,
|
|
167
|
-
outputSchema
|
|
167
|
+
outputSchema,
|
|
168
|
+
hints: config.hints
|
|
168
169
|
},
|
|
169
170
|
handler
|
|
170
171
|
};
|
|
@@ -17,7 +17,7 @@ function createWebhookCapability(key, config) {
|
|
|
17
17
|
},
|
|
18
18
|
async handler(events, options) {
|
|
19
19
|
try {
|
|
20
|
-
const capabilityContext = createCapabilityContext();
|
|
20
|
+
const capabilityContext = createCapabilityContext("webhook");
|
|
21
21
|
await config.execute(events, capabilityContext);
|
|
22
22
|
if (options?.concreteOutput) {
|
|
23
23
|
return { status: "success" };
|
package/dist/error.d.ts
CHANGED
|
@@ -5,6 +5,42 @@ export declare class ExecutionError extends Error {
|
|
|
5
5
|
readonly cause: unknown;
|
|
6
6
|
constructor(cause: unknown);
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Throw this error from your sync execute handler to signal that the
|
|
10
|
+
* external API returned a rate-limit response (e.g. HTTP 429).
|
|
11
|
+
*
|
|
12
|
+
* When the platform receives this error it applies exponential backoff
|
|
13
|
+
* instead of retrying immediately. If `retryAfter` is provided, the
|
|
14
|
+
* platform will wait at least that many seconds before the next attempt.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* worker.sync("issues", {
|
|
19
|
+
* database: issuesDb,
|
|
20
|
+
* execute: async (state, ctx) => {
|
|
21
|
+
* const res = await fetch("https://api.example.com/issues");
|
|
22
|
+
* if (res.status === 429) {
|
|
23
|
+
* const retryAfter = Number(res.headers.get("Retry-After"));
|
|
24
|
+
* throw new RateLimitError({
|
|
25
|
+
* retryAfter: Number.isFinite(retryAfter) ? retryAfter : undefined,
|
|
26
|
+
* });
|
|
27
|
+
* }
|
|
28
|
+
* // ...
|
|
29
|
+
* },
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare class RateLimitError extends ExecutionError {
|
|
34
|
+
/**
|
|
35
|
+
* Optional number of seconds the external API asked us to wait
|
|
36
|
+
* before retrying. The platform takes the max of this value and its
|
|
37
|
+
* own exponential-backoff delay.
|
|
38
|
+
*/
|
|
39
|
+
readonly retryAfter: number | undefined;
|
|
40
|
+
constructor(options?: {
|
|
41
|
+
retryAfter?: number;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
8
44
|
/**
|
|
9
45
|
* Helper for exhaustive switch statements. TypeScript will error if a case is not handled.
|
|
10
46
|
*/
|
package/dist/error.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACxC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;gBAEZ,KAAK,EAAE,OAAO;CAK1B;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAE/C"}
|
|
1
|
+
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACxC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;gBAEZ,KAAK,EAAE,OAAO;CAK1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,cAAe,SAAQ,cAAc;IACjD;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;gBAE5B,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;CAK7C;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAE/C"}
|
package/dist/error.js
CHANGED
|
@@ -6,10 +6,24 @@ class ExecutionError extends Error {
|
|
|
6
6
|
this.cause = cause;
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
|
+
class RateLimitError extends ExecutionError {
|
|
10
|
+
/**
|
|
11
|
+
* Optional number of seconds the external API asked us to wait
|
|
12
|
+
* before retrying. The platform takes the max of this value and its
|
|
13
|
+
* own exponential-backoff delay.
|
|
14
|
+
*/
|
|
15
|
+
retryAfter;
|
|
16
|
+
constructor(options) {
|
|
17
|
+
super("Rate limited by external API");
|
|
18
|
+
this.name = "RateLimitError";
|
|
19
|
+
this.retryAfter = options?.retryAfter;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
9
22
|
function unreachable(value) {
|
|
10
23
|
throw new Error(`Unreachable: ${value}`);
|
|
11
24
|
}
|
|
12
25
|
export {
|
|
13
26
|
ExecutionError,
|
|
27
|
+
RateLimitError,
|
|
14
28
|
unreachable
|
|
15
29
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -4,9 +4,10 @@ export type { AutomationCapability, AutomationConfiguration, AutomationEvent, Pa
|
|
|
4
4
|
export type { CapabilityContext } from "./capabilities/context.js";
|
|
5
5
|
export type { NotionManagedOAuthConfiguration, OAuthCapability, OAuthConfiguration, UserManagedOAuthConfiguration, } from "./capabilities/oauth.js";
|
|
6
6
|
export type { SyncCapability, SyncChange, SyncChangeDelete, SyncChangeUpsert, SyncConfiguration, SyncExecutionResult, SyncMode, } from "./capabilities/sync.js";
|
|
7
|
-
export type { ToolCapability, ToolConfiguration } from "./capabilities/tool.js";
|
|
7
|
+
export type { ToolCapability, ToolConfiguration, ToolHints, } from "./capabilities/tool.js";
|
|
8
8
|
export type { WebhookCapability, WebhookConfiguration, WebhookEvent, } from "./capabilities/webhook.js";
|
|
9
9
|
export { WebhookVerificationError } from "./capabilities/webhook.js";
|
|
10
|
+
export { RateLimitError } from "./error.js";
|
|
10
11
|
export type { AnyJSONSchema, JSONSchema } from "./json-schema.js";
|
|
11
12
|
export type { Infer, SchemaBuilder } from "./schema-builder.js";
|
|
12
13
|
export { getSchema, j } from "./schema-builder.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACvE,YAAY,EACX,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,uBAAuB,EACvB,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,EAC1B,eAAe,EACf,uBAAuB,EACvB,wBAAwB,EACxB,4BAA4B,GAC5B,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACX,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,EACf,kBAAkB,GAClB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,YAAY,EACX,+BAA+B,EAC/B,eAAe,EACf,kBAAkB,EAClB,6BAA6B,GAC7B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACX,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,QAAQ,GACR,MAAM,wBAAwB,CAAC;AAChC,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACvE,YAAY,EACX,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,uBAAuB,EACvB,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,EAC1B,eAAe,EACf,uBAAuB,EACvB,wBAAwB,EACxB,4BAA4B,GAC5B,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACX,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,EACf,kBAAkB,GAClB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,YAAY,EACX,+BAA+B,EAC/B,eAAe,EACf,kBAAkB,EAClB,6BAA6B,GAC7B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACX,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,QAAQ,GACR,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACX,cAAc,EACd,iBAAiB,EACjB,SAAS,GACT,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACX,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,GACZ,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAClE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EACX,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,WAAW,EACX,UAAU,EACV,QAAQ,GACR,MAAM,YAAY,CAAC;AACpB,YAAY,EACX,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,cAAc,GACd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { emojiIcon, imageIcon, notionIcon, place } from "./builder.js";
|
|
2
2
|
import { WebhookVerificationError } from "./capabilities/webhook.js";
|
|
3
|
+
import { RateLimitError } from "./error.js";
|
|
3
4
|
import { getSchema, j } from "./schema-builder.js";
|
|
4
5
|
import { Worker } from "./worker.js";
|
|
5
6
|
export {
|
|
7
|
+
RateLimitError,
|
|
6
8
|
WebhookVerificationError,
|
|
7
9
|
Worker,
|
|
8
10
|
emojiIcon,
|
package/dist/worker.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type { HandlerOptions, JSONValue } from "./types.js";
|
|
|
12
12
|
export type { AutomationConfiguration, AutomationEvent, AiConnectorConfiguration, CapabilityContext, OAuthConfiguration, NotionManagedOAuthConfiguration, UserManagedOAuthConfiguration, SyncConfiguration, ToolConfiguration, WebhookConfiguration, WebhookEvent, };
|
|
13
13
|
export { WebhookVerificationError };
|
|
14
14
|
type Capability = SyncCapability | AiConnectorCapability | ToolCapability<any, any> | AutomationCapability | OAuthCapability | WebhookCapability;
|
|
15
|
+
export type CapabilityType = Capability["_tag"];
|
|
15
16
|
/**
|
|
16
17
|
* Configuration for a database that Notion creates and manages on behalf
|
|
17
18
|
* of the worker. The database is created on first deploy and its schema
|
package/dist/worker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@notionhq/workers",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "An SDK for building workers for the Notion Workers platform",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"vitest": "^4.0.8"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"@notionhq/client": "
|
|
73
|
+
"@notionhq/client": ">=2.2.15"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
76
|
"ajv": "^8.17.1",
|
|
@@ -154,7 +154,7 @@ export function createAiConnectorCapability<
|
|
|
154
154
|
runtimeContext?: StatefulRuntimeContext<State>,
|
|
155
155
|
options?: HandlerOptions,
|
|
156
156
|
) {
|
|
157
|
-
const capabilityContext = createCapabilityContext();
|
|
157
|
+
const capabilityContext = createCapabilityContext("ai_connector");
|
|
158
158
|
const state = runtimeContext?.state ?? runtimeContext?.userContext;
|
|
159
159
|
|
|
160
160
|
initPacerState(runtimeContext?.pacers, pacerDeclarations);
|
|
@@ -105,7 +105,7 @@ export function createAutomationCapability(
|
|
|
105
105
|
options?: HandlerOptions,
|
|
106
106
|
): Promise<{ status: "success" } | undefined> {
|
|
107
107
|
try {
|
|
108
|
-
const capabilityContext = createCapabilityContext();
|
|
108
|
+
const capabilityContext = createCapabilityContext("automation");
|
|
109
109
|
await config.execute(event, capabilityContext);
|
|
110
110
|
|
|
111
111
|
if (options?.concreteOutput) {
|
|
@@ -1,22 +1,64 @@
|
|
|
1
1
|
import { Client } from "@notionhq/client";
|
|
2
2
|
import type { ClientOptions } from "@notionhq/client/build/src/Client.js";
|
|
3
|
+
import type { CapabilityType } from "../worker.js";
|
|
3
4
|
|
|
4
5
|
export type CapabilityContext = {
|
|
5
6
|
/** Notion API SDK client for this execution. */
|
|
6
7
|
notion: Client;
|
|
7
8
|
};
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
const FIX_STEPS =
|
|
11
|
+
"\n\nTo fix this:\n" +
|
|
12
|
+
"1. Create an internal integration at https://www.notion.so/profile/integrations/internal\n" +
|
|
13
|
+
"2. Give the integration access to the relevant pages/databases\n" +
|
|
14
|
+
"3. Add NOTION_API_TOKEN=<your-token> to your .env file\n" +
|
|
15
|
+
"4. For deployed workers, run: ntn workers env push";
|
|
16
|
+
|
|
17
|
+
function missingTokenMessage(capabilityType: CapabilityType): string {
|
|
18
|
+
if (capabilityType === "tool") {
|
|
19
|
+
return (
|
|
20
|
+
"NOTION_API_TOKEN is not set. " +
|
|
21
|
+
"When tools are called through a Custom Agent, the platform sets NOTION_API_TOKEN automatically, " +
|
|
22
|
+
"using the permissions of the Custom Agent. " +
|
|
23
|
+
"Outside of that context (e.g. ntn workers exec), you must set it yourself." +
|
|
24
|
+
FIX_STEPS
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
"NOTION_API_TOKEN is not set. " +
|
|
30
|
+
`context.notion requires an API token to make requests in ${capabilityType} capabilities to Notion.` +
|
|
31
|
+
FIX_STEPS
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function createUnauthenticatedNotionProxy(
|
|
36
|
+
capabilityType: CapabilityType,
|
|
37
|
+
): Client {
|
|
38
|
+
return new Proxy({} as Client, {
|
|
39
|
+
get(_target, prop) {
|
|
40
|
+
if (prop === "then" || typeof prop === "symbol") {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
throw new Error(missingTokenMessage(capabilityType));
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function createCapabilityContext(
|
|
49
|
+
capabilityType: CapabilityType,
|
|
50
|
+
): CapabilityContext {
|
|
10
51
|
const options: ClientOptions = {};
|
|
11
52
|
|
|
12
53
|
if (process.env.NOTION_API_BASE_URL) {
|
|
13
54
|
options.baseUrl = process.env.NOTION_API_BASE_URL;
|
|
14
55
|
}
|
|
15
56
|
|
|
16
|
-
if (process.env.NOTION_API_TOKEN) {
|
|
17
|
-
|
|
57
|
+
if (!process.env.NOTION_API_TOKEN) {
|
|
58
|
+
return { notion: createUnauthenticatedNotionProxy(capabilityType) };
|
|
18
59
|
}
|
|
19
60
|
|
|
61
|
+
options.auth = process.env.NOTION_API_TOKEN;
|
|
20
62
|
const notion = new Client(options);
|
|
21
63
|
|
|
22
64
|
return { notion };
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
vi,
|
|
9
9
|
} from "vitest";
|
|
10
10
|
import * as Builder from "../builder.js";
|
|
11
|
-
import { ExecutionError } from "../error.js";
|
|
11
|
+
import { ExecutionError, RateLimitError } from "../error.js";
|
|
12
12
|
import { getPacerState } from "../pacer_internal.js";
|
|
13
13
|
import * as Schema from "../schema.js";
|
|
14
14
|
import { Worker } from "../worker.js";
|
|
@@ -251,6 +251,113 @@ describe("Worker.sync", () => {
|
|
|
251
251
|
);
|
|
252
252
|
});
|
|
253
253
|
|
|
254
|
+
it("wraps RateLimitError in error envelope with _tag rate_limit", async () => {
|
|
255
|
+
const worker = new Worker();
|
|
256
|
+
const db = worker.database("test", {
|
|
257
|
+
type: "managed",
|
|
258
|
+
initialTitle: "Test",
|
|
259
|
+
primaryKeyProperty: "ID",
|
|
260
|
+
schema: {
|
|
261
|
+
properties: {
|
|
262
|
+
ID: Schema.title(),
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const capability = createSyncCapability("rateLimitSync", {
|
|
268
|
+
database: db,
|
|
269
|
+
execute: async () => {
|
|
270
|
+
throw new RateLimitError();
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
await expect(capability.handler()).rejects.toThrow(RateLimitError);
|
|
275
|
+
|
|
276
|
+
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
277
|
+
expect.stringContaining(`"_tag":"rate_limit"`),
|
|
278
|
+
);
|
|
279
|
+
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
280
|
+
expect.stringContaining(`"name":"RateLimitError"`),
|
|
281
|
+
);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it("includes retryAfter in rate_limit error envelope when provided", async () => {
|
|
285
|
+
const worker = new Worker();
|
|
286
|
+
const db = worker.database("test", {
|
|
287
|
+
type: "managed",
|
|
288
|
+
initialTitle: "Test",
|
|
289
|
+
primaryKeyProperty: "ID",
|
|
290
|
+
schema: {
|
|
291
|
+
properties: {
|
|
292
|
+
ID: Schema.title(),
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
const capability = createSyncCapability("rateLimitSync", {
|
|
298
|
+
database: db,
|
|
299
|
+
execute: async () => {
|
|
300
|
+
throw new RateLimitError({ retryAfter: 30 });
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
await expect(capability.handler()).rejects.toThrow(RateLimitError);
|
|
305
|
+
|
|
306
|
+
const output = stdoutSpy.mock.calls
|
|
307
|
+
.map((call) => String(call[0]))
|
|
308
|
+
.find((s) => s.includes(`"_tag":"rate_limit"`)) as string;
|
|
309
|
+
const parsed = JSON.parse(
|
|
310
|
+
output.replace(
|
|
311
|
+
/\n<__notion_output__>(.*)<\/__notion_output__>\n/,
|
|
312
|
+
"$1",
|
|
313
|
+
),
|
|
314
|
+
);
|
|
315
|
+
expect(parsed).toEqual({
|
|
316
|
+
_tag: "error",
|
|
317
|
+
error: {
|
|
318
|
+
_tag: "rate_limit",
|
|
319
|
+
name: "RateLimitError",
|
|
320
|
+
message:
|
|
321
|
+
"Error during worker execution: Rate limited by external API",
|
|
322
|
+
retryAfter: 30,
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it("wraps RateLimitError without retryAfter correctly", async () => {
|
|
328
|
+
const worker = new Worker();
|
|
329
|
+
const db = worker.database("test", {
|
|
330
|
+
type: "managed",
|
|
331
|
+
initialTitle: "Test",
|
|
332
|
+
primaryKeyProperty: "ID",
|
|
333
|
+
schema: {
|
|
334
|
+
properties: {
|
|
335
|
+
ID: Schema.title(),
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
const capability = createSyncCapability("rateLimitSync", {
|
|
341
|
+
database: db,
|
|
342
|
+
execute: async () => {
|
|
343
|
+
throw new RateLimitError();
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
await expect(capability.handler()).rejects.toThrow(RateLimitError);
|
|
348
|
+
|
|
349
|
+
const output = stdoutSpy.mock.calls
|
|
350
|
+
.map((call) => String(call[0]))
|
|
351
|
+
.find((s) => s.includes(`"_tag":"rate_limit"`)) as string;
|
|
352
|
+
const parsed = JSON.parse(
|
|
353
|
+
output.replace(
|
|
354
|
+
/\n<__notion_output__>(.*)<\/__notion_output__>\n/,
|
|
355
|
+
"$1",
|
|
356
|
+
),
|
|
357
|
+
);
|
|
358
|
+
expect(parsed.error.retryAfter).toBeUndefined();
|
|
359
|
+
});
|
|
360
|
+
|
|
254
361
|
it("emits sync output with key and default targetDatabaseKey", async () => {
|
|
255
362
|
const worker = new Worker();
|
|
256
363
|
const tasks = worker.database("tasks", {
|
package/src/capabilities/sync.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ExecutionError } from "../error.js";
|
|
1
|
+
import { ExecutionError, RateLimitError } from "../error.js";
|
|
2
2
|
import {
|
|
3
3
|
getPacerState,
|
|
4
4
|
initPacerState,
|
|
@@ -28,6 +28,18 @@ import {
|
|
|
28
28
|
type RuntimeContext as StatefulRuntimeContext,
|
|
29
29
|
} from "./stateful-capability.js";
|
|
30
30
|
|
|
31
|
+
type SyncErrorEnvelope =
|
|
32
|
+
| { _tag: "error"; error: { _tag: "generic"; name: string; message: string } }
|
|
33
|
+
| {
|
|
34
|
+
_tag: "error";
|
|
35
|
+
error: {
|
|
36
|
+
_tag: "rate_limit";
|
|
37
|
+
name: string;
|
|
38
|
+
message: string;
|
|
39
|
+
retryAfter?: number;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
|
|
31
43
|
/**
|
|
32
44
|
* Maps a property configuration to its corresponding value type.
|
|
33
45
|
*/
|
|
@@ -239,7 +251,7 @@ export function createSyncCapability<
|
|
|
239
251
|
runtimeContext?: StatefulRuntimeContext<Context>,
|
|
240
252
|
options?: HandlerOptions,
|
|
241
253
|
) {
|
|
242
|
-
const capabilityContext = createCapabilityContext();
|
|
254
|
+
const capabilityContext = createCapabilityContext("sync");
|
|
243
255
|
const state = runtimeContext?.state ?? runtimeContext?.userContext;
|
|
244
256
|
|
|
245
257
|
initPacerState(runtimeContext?.pacers, pacerDeclarations);
|
|
@@ -251,12 +263,35 @@ export function createSyncCapability<
|
|
|
251
263
|
capabilityContext,
|
|
252
264
|
);
|
|
253
265
|
} catch (err) {
|
|
266
|
+
if (err instanceof RateLimitError) {
|
|
267
|
+
if (!options?.concreteOutput) {
|
|
268
|
+
const envelope: SyncErrorEnvelope = {
|
|
269
|
+
_tag: "error",
|
|
270
|
+
error: {
|
|
271
|
+
_tag: "rate_limit",
|
|
272
|
+
name: err.name,
|
|
273
|
+
message: err.message,
|
|
274
|
+
...(err.retryAfter !== undefined
|
|
275
|
+
? { retryAfter: err.retryAfter }
|
|
276
|
+
: {}),
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
writeOutput(envelope);
|
|
280
|
+
}
|
|
281
|
+
throw err;
|
|
282
|
+
}
|
|
283
|
+
|
|
254
284
|
const error = new ExecutionError(err);
|
|
255
285
|
if (!options?.concreteOutput) {
|
|
256
|
-
|
|
286
|
+
const envelope: SyncErrorEnvelope = {
|
|
257
287
|
_tag: "error",
|
|
258
|
-
error: {
|
|
259
|
-
|
|
288
|
+
error: {
|
|
289
|
+
_tag: "generic",
|
|
290
|
+
name: error.name,
|
|
291
|
+
message: error.message,
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
writeOutput(envelope);
|
|
260
295
|
}
|
|
261
296
|
throw error;
|
|
262
297
|
}
|
|
@@ -222,6 +222,69 @@ describe("Worker.tool", () => {
|
|
|
222
222
|
expect(capability.config.schema).toBeDefined();
|
|
223
223
|
});
|
|
224
224
|
|
|
225
|
+
describe("hints", () => {
|
|
226
|
+
it("omits hints from config when not specified (existing tools unchanged)", () => {
|
|
227
|
+
const capability = createToolCapability<{ name: string }, string>(
|
|
228
|
+
"noHints",
|
|
229
|
+
{
|
|
230
|
+
title: "No Hints",
|
|
231
|
+
description: "A tool without hints",
|
|
232
|
+
schema: j.object({ name: j.string() }),
|
|
233
|
+
execute: ({ name }) => `Hello, ${name}`,
|
|
234
|
+
},
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
expect(capability.config.hints).toBeUndefined();
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it("flows readOnlyHint=true through to config", () => {
|
|
241
|
+
const capability = createToolCapability<{ query: string }, string>(
|
|
242
|
+
"readOnly",
|
|
243
|
+
{
|
|
244
|
+
title: "Read Only",
|
|
245
|
+
description: "A read-only tool",
|
|
246
|
+
schema: j.object({ query: j.string() }),
|
|
247
|
+
hints: { readOnlyHint: true },
|
|
248
|
+
execute: ({ query }) => query,
|
|
249
|
+
},
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
expect(capability.config.hints).toEqual({ readOnlyHint: true });
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("flows readOnlyHint=false through to config", () => {
|
|
256
|
+
const capability = createToolCapability<{ pageId: string }, string>(
|
|
257
|
+
"writeTool",
|
|
258
|
+
{
|
|
259
|
+
title: "Write Tool",
|
|
260
|
+
description: "A write tool",
|
|
261
|
+
schema: j.object({ pageId: j.string() }),
|
|
262
|
+
hints: { readOnlyHint: false },
|
|
263
|
+
execute: ({ pageId }) => pageId,
|
|
264
|
+
},
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
expect(capability.config.hints).toEqual({ readOnlyHint: false });
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it("does not affect execution behavior", async () => {
|
|
271
|
+
const capability = createToolCapability<{ x: number }, number>(
|
|
272
|
+
"doubleReadOnly",
|
|
273
|
+
{
|
|
274
|
+
title: "Double",
|
|
275
|
+
description: "Doubles a number",
|
|
276
|
+
schema: j.object({ x: j.number() }),
|
|
277
|
+
hints: { readOnlyHint: true },
|
|
278
|
+
execute: ({ x }) => x * 2,
|
|
279
|
+
},
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
const result = await capability.handler({ x: 7 });
|
|
283
|
+
|
|
284
|
+
expect(result).toEqual({ _tag: "success", value: 14 });
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
225
288
|
it("invalid output with custom output schema", async () => {
|
|
226
289
|
const capability = createToolCapability<
|
|
227
290
|
{ value: number },
|
package/src/capabilities/tool.ts
CHANGED
|
@@ -70,6 +70,26 @@ function validateRequiredProperties(
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Hints describing a tool's behavior. Advisory metadata read by the platform
|
|
75
|
+
* (not the model) to decide whether a call is safe to auto-execute or
|
|
76
|
+
* requires user confirmation.
|
|
77
|
+
*
|
|
78
|
+
* Field names mirror the MCP tool annotation shape
|
|
79
|
+
* (`McpServerToolAnnotations`) so worker tools flow through the same
|
|
80
|
+
* confirmation / auto-execute machinery as MCP tools.
|
|
81
|
+
*
|
|
82
|
+
* Only hints with active consumers in the Notion client are included.
|
|
83
|
+
*/
|
|
84
|
+
export interface ToolHints {
|
|
85
|
+
/**
|
|
86
|
+
* The tool only reads state and has no side effects. Safe to call
|
|
87
|
+
* repeatedly; safe to auto-execute under default policy. Tools without
|
|
88
|
+
* this hint are treated as write tools and prompt for confirmation.
|
|
89
|
+
*/
|
|
90
|
+
readOnlyHint?: boolean;
|
|
91
|
+
}
|
|
92
|
+
|
|
73
93
|
export interface ToolConfiguration<
|
|
74
94
|
I extends JSONValue,
|
|
75
95
|
O extends JSONValue = JSONValue,
|
|
@@ -78,6 +98,7 @@ export interface ToolConfiguration<
|
|
|
78
98
|
description: string;
|
|
79
99
|
schema: SchemaBuilder<I>;
|
|
80
100
|
outputSchema?: SchemaBuilder<O>;
|
|
101
|
+
hints?: ToolHints;
|
|
81
102
|
execute: (input: I, context: CapabilityContext) => O | Promise<O>;
|
|
82
103
|
}
|
|
83
104
|
|
|
@@ -218,7 +239,7 @@ export function createToolCapability<
|
|
|
218
239
|
}
|
|
219
240
|
|
|
220
241
|
try {
|
|
221
|
-
const capabilityContext = createCapabilityContext();
|
|
242
|
+
const capabilityContext = createCapabilityContext("tool");
|
|
222
243
|
const result = await config.execute(input, capabilityContext);
|
|
223
244
|
if (validateOutput && !validateOutput(result)) {
|
|
224
245
|
const error = new InvalidToolOutputError(
|
|
@@ -281,6 +302,7 @@ export function createToolCapability<
|
|
|
281
302
|
description: config.description,
|
|
282
303
|
schema: inputSchema,
|
|
283
304
|
outputSchema: outputSchema,
|
|
305
|
+
hints: config.hints,
|
|
284
306
|
},
|
|
285
307
|
handler,
|
|
286
308
|
};
|
|
@@ -118,7 +118,7 @@ export function createWebhookCapability(
|
|
|
118
118
|
options?: HandlerOptions,
|
|
119
119
|
): Promise<{ status: "success" } | undefined> {
|
|
120
120
|
try {
|
|
121
|
-
const capabilityContext = createCapabilityContext();
|
|
121
|
+
const capabilityContext = createCapabilityContext("webhook");
|
|
122
122
|
await config.execute(events, capabilityContext);
|
|
123
123
|
|
|
124
124
|
if (options?.concreteOutput) {
|
package/src/error.ts
CHANGED
|
@@ -11,6 +11,46 @@ export class ExecutionError extends Error {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Throw this error from your sync execute handler to signal that the
|
|
16
|
+
* external API returned a rate-limit response (e.g. HTTP 429).
|
|
17
|
+
*
|
|
18
|
+
* When the platform receives this error it applies exponential backoff
|
|
19
|
+
* instead of retrying immediately. If `retryAfter` is provided, the
|
|
20
|
+
* platform will wait at least that many seconds before the next attempt.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* worker.sync("issues", {
|
|
25
|
+
* database: issuesDb,
|
|
26
|
+
* execute: async (state, ctx) => {
|
|
27
|
+
* const res = await fetch("https://api.example.com/issues");
|
|
28
|
+
* if (res.status === 429) {
|
|
29
|
+
* const retryAfter = Number(res.headers.get("Retry-After"));
|
|
30
|
+
* throw new RateLimitError({
|
|
31
|
+
* retryAfter: Number.isFinite(retryAfter) ? retryAfter : undefined,
|
|
32
|
+
* });
|
|
33
|
+
* }
|
|
34
|
+
* // ...
|
|
35
|
+
* },
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export class RateLimitError extends ExecutionError {
|
|
40
|
+
/**
|
|
41
|
+
* Optional number of seconds the external API asked us to wait
|
|
42
|
+
* before retrying. The platform takes the max of this value and its
|
|
43
|
+
* own exponential-backoff delay.
|
|
44
|
+
*/
|
|
45
|
+
readonly retryAfter: number | undefined;
|
|
46
|
+
|
|
47
|
+
constructor(options?: { retryAfter?: number }) {
|
|
48
|
+
super("Rate limited by external API");
|
|
49
|
+
this.name = "RateLimitError";
|
|
50
|
+
this.retryAfter = options?.retryAfter;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
14
54
|
/**
|
|
15
55
|
* Helper for exhaustive switch statements. TypeScript will error if a case is not handled.
|
|
16
56
|
*/
|
package/src/index.ts
CHANGED
|
@@ -37,13 +37,18 @@ export type {
|
|
|
37
37
|
SyncExecutionResult,
|
|
38
38
|
SyncMode,
|
|
39
39
|
} from "./capabilities/sync.js";
|
|
40
|
-
export type {
|
|
40
|
+
export type {
|
|
41
|
+
ToolCapability,
|
|
42
|
+
ToolConfiguration,
|
|
43
|
+
ToolHints,
|
|
44
|
+
} from "./capabilities/tool.js";
|
|
41
45
|
export type {
|
|
42
46
|
WebhookCapability,
|
|
43
47
|
WebhookConfiguration,
|
|
44
48
|
WebhookEvent,
|
|
45
49
|
} from "./capabilities/webhook.js";
|
|
46
50
|
export { WebhookVerificationError } from "./capabilities/webhook.js";
|
|
51
|
+
export { RateLimitError } from "./error.js";
|
|
47
52
|
export type { AnyJSONSchema, JSONSchema } from "./json-schema.js";
|
|
48
53
|
export type { Infer, SchemaBuilder } from "./schema-builder.js";
|
|
49
54
|
export { getSchema, j } from "./schema-builder.js";
|
package/src/worker.ts
CHANGED
|
@@ -65,6 +65,8 @@ type Capability =
|
|
|
65
65
|
| OAuthCapability
|
|
66
66
|
| WebhookCapability;
|
|
67
67
|
|
|
68
|
+
export type CapabilityType = Capability["_tag"];
|
|
69
|
+
|
|
68
70
|
/**
|
|
69
71
|
* Configuration for a database that Notion creates and manages on behalf
|
|
70
72
|
* of the worker. The database is created on first deploy and its schema
|