chatwoot-router 0.1.0 → 0.3.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/CHANGELOG.md +74 -1
- package/README.md +178 -208
- package/lib/packages/chatwoot-router/scripts/store-config.js +18 -17
- package/lib/packages/chatwoot-router/scripts/stored-config.js +18 -17
- package/lib/packages/chatwoot-router/src/config.d.ts +4 -9
- package/lib/packages/chatwoot-router/src/coordinator.d.ts +10 -0
- package/lib/packages/chatwoot-router/src/env.d.ts +4 -2
- package/lib/packages/chatwoot-router/src/index.d.ts +1 -0
- package/lib/packages/chatwoot-router/src/index.js +746 -397
- package/lib/packages/chatwoot-router/src/reply.d.ts +5 -0
- package/lib/packages/chatwoot-router/src/router.d.ts +3 -6
- package/lib/packages/chatwoot-router/src/routing.d.ts +0 -4
- package/lib/packages/chatwoot-router/src/turn.d.ts +118 -0
- package/lib/packages/chatwoot-router/src/webhook.d.ts +5 -0
- package/lib/shared/alarm.d.ts +2 -0
- package/lib/shared/budget.d.ts +15 -2
- package/lib/shared/chatwoot/api.d.ts +60 -7
- package/lib/shared/concurrent.d.ts +4 -0
- package/lib/shared/deadline.d.ts +6 -0
- package/lib/shared/rate-limit.d.ts +5 -0
- package/lib/shared/store.d.ts +11 -4
- package/lib/shared/types.d.ts +4 -0
- package/package.json +1 -1
- package/lib/packages/chatwoot-router/src/effects.d.ts +0 -14
- package/lib/shared/attributes.d.ts +0 -6
- package/lib/shared/sweep.d.ts +0 -11
|
@@ -92,18 +92,16 @@ var reconcileSchema = z.strictObject({
|
|
|
92
92
|
var accountId = z2.string().regex(/^[1-9]\d*$/, "must be a Chatwoot account id").refine((value) => Number.isSafeInteger(Number(value)), "must be a safe integer");
|
|
93
93
|
var configSchema = z2.strictObject({
|
|
94
94
|
chatwoot: z2.strictObject({ baseUrl: z2.url({ protocol: /^https?$/ }) }),
|
|
95
|
-
|
|
96
|
-
startAfterConversationId: z2.record(accountId, z2.number().int().min(0)).default({}),
|
|
97
|
-
subrequestBudget: z2.number().int().min(20).max(1e3).default(45),
|
|
95
|
+
subrequestBudget: z2.number().int().min(45).max(1e3).default(45),
|
|
98
96
|
routing: z2.strictObject({
|
|
97
|
+
/** TypeSafe's System One API (/v1/systemone), or another deployment such as a proxy or gateway. */
|
|
98
|
+
endpoint: z2.url({ protocol: /^https$/ }).default("https://api.typesafe.ai/v1/systemone"),
|
|
99
|
+
/** Model sent to TypeSafe's System One API; Jev by default. */
|
|
99
100
|
model: z2.string().min(1).default("jev-1.13.0"),
|
|
100
101
|
/** Jev's probability an answer needs before it is applied. */
|
|
101
102
|
minConfidence: z2.number().min(0.5).max(1).default(0.7),
|
|
102
|
-
/**
|
|
103
|
-
|
|
104
|
-
* their next message (see src/routing.ts).
|
|
105
|
-
*/
|
|
106
|
-
snoozeUnclear: z2.boolean().default(false),
|
|
103
|
+
/** The account's native Chatwoot agent bot. */
|
|
104
|
+
botIds: z2.record(accountId, z2.number().int().positive()),
|
|
107
105
|
/** Per Chatwoot account id: the owners Jev chooses from, by a short name. */
|
|
108
106
|
accounts: z2.record(
|
|
109
107
|
accountId,
|
|
@@ -135,7 +133,7 @@ var configSchema = z2.strictObject({
|
|
|
135
133
|
covers: z2.string().min(1).max(1e3),
|
|
136
134
|
/**
|
|
137
135
|
* Short code of the account's Chatwoot canned response sent to the customer once, as
|
|
138
|
-
* the account's agent bot (
|
|
136
|
+
* the account's agent bot (CHATWOOT_AGENT_BOT_TOKENS). Missing responses hand off to people.
|
|
139
137
|
*/
|
|
140
138
|
cannedResponse: z2.string().trim().min(1).max(255).optional(),
|
|
141
139
|
/**
|
|
@@ -148,23 +146,26 @@ var configSchema = z2.strictObject({
|
|
|
148
146
|
)
|
|
149
147
|
).optional()
|
|
150
148
|
})
|
|
151
|
-
}).refine(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
149
|
+
}).refine(
|
|
150
|
+
(config) => {
|
|
151
|
+
const accounts = Object.keys(config.routing.accounts);
|
|
152
|
+
return accounts.length > 0 && accounts.length === Object.keys(config.routing.botIds).length && accounts.every((id) => config.routing.botIds[id] !== void 0);
|
|
153
|
+
},
|
|
154
|
+
{ path: ["routing", "botIds"], message: "must name exactly the routed accounts" }
|
|
155
|
+
).refine((config) => Object.keys(config.routing.kinds ?? {}).every((id) => config.routing.accounts[id]), {
|
|
155
156
|
path: ["routing", "kinds"],
|
|
156
157
|
message: "must only name routed accounts"
|
|
157
158
|
}).refine(
|
|
158
|
-
(config) => Object.values(config.routing
|
|
159
|
-
(kinds) => Object.keys(kinds).every((kind) => !Object.hasOwn(config.routing
|
|
159
|
+
(config) => Object.values(config.routing.kinds ?? {}).every(
|
|
160
|
+
(kinds) => Object.keys(kinds).every((kind) => !Object.hasOwn(config.routing.topics ?? {}, kind))
|
|
160
161
|
),
|
|
161
162
|
{ path: ["routing", "kinds"], message: "a kind cannot be named as a topic: they are labels of two families" }
|
|
162
163
|
);
|
|
163
164
|
var secretsSchema = z2.object({
|
|
164
165
|
CHATWOOT_TOKEN: z2.string().min(1),
|
|
165
|
-
|
|
166
|
+
CHATWOOT_AGENT_BOT_SECRETS: jsonRecord,
|
|
166
167
|
TYPESAFE_API_KEY: z2.string().min(1),
|
|
167
|
-
|
|
168
|
+
CHATWOOT_AGENT_BOT_TOKENS: jsonRecord
|
|
168
169
|
});
|
|
169
170
|
|
|
170
171
|
// scripts/stored-config.ts
|
|
@@ -44,18 +44,16 @@ var reconcileSchema = z.strictObject({
|
|
|
44
44
|
var accountId = z2.string().regex(/^[1-9]\d*$/, "must be a Chatwoot account id").refine((value) => Number.isSafeInteger(Number(value)), "must be a safe integer");
|
|
45
45
|
var configSchema = z2.strictObject({
|
|
46
46
|
chatwoot: z2.strictObject({ baseUrl: z2.url({ protocol: /^https?$/ }) }),
|
|
47
|
-
|
|
48
|
-
startAfterConversationId: z2.record(accountId, z2.number().int().min(0)).default({}),
|
|
49
|
-
subrequestBudget: z2.number().int().min(20).max(1e3).default(45),
|
|
47
|
+
subrequestBudget: z2.number().int().min(45).max(1e3).default(45),
|
|
50
48
|
routing: z2.strictObject({
|
|
49
|
+
/** TypeSafe's System One API (/v1/systemone), or another deployment such as a proxy or gateway. */
|
|
50
|
+
endpoint: z2.url({ protocol: /^https$/ }).default("https://api.typesafe.ai/v1/systemone"),
|
|
51
|
+
/** Model sent to TypeSafe's System One API; Jev by default. */
|
|
51
52
|
model: z2.string().min(1).default("jev-1.13.0"),
|
|
52
53
|
/** Jev's probability an answer needs before it is applied. */
|
|
53
54
|
minConfidence: z2.number().min(0.5).max(1).default(0.7),
|
|
54
|
-
/**
|
|
55
|
-
|
|
56
|
-
* their next message (see src/routing.ts).
|
|
57
|
-
*/
|
|
58
|
-
snoozeUnclear: z2.boolean().default(false),
|
|
55
|
+
/** The account's native Chatwoot agent bot. */
|
|
56
|
+
botIds: z2.record(accountId, z2.number().int().positive()),
|
|
59
57
|
/** Per Chatwoot account id: the owners Jev chooses from, by a short name. */
|
|
60
58
|
accounts: z2.record(
|
|
61
59
|
accountId,
|
|
@@ -87,7 +85,7 @@ var configSchema = z2.strictObject({
|
|
|
87
85
|
covers: z2.string().min(1).max(1e3),
|
|
88
86
|
/**
|
|
89
87
|
* Short code of the account's Chatwoot canned response sent to the customer once, as
|
|
90
|
-
* the account's agent bot (
|
|
88
|
+
* the account's agent bot (CHATWOOT_AGENT_BOT_TOKENS). Missing responses hand off to people.
|
|
91
89
|
*/
|
|
92
90
|
cannedResponse: z2.string().trim().min(1).max(255).optional(),
|
|
93
91
|
/**
|
|
@@ -100,23 +98,26 @@ var configSchema = z2.strictObject({
|
|
|
100
98
|
)
|
|
101
99
|
).optional()
|
|
102
100
|
})
|
|
103
|
-
}).refine(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
}).refine(
|
|
102
|
+
(config) => {
|
|
103
|
+
const accounts = Object.keys(config.routing.accounts);
|
|
104
|
+
return accounts.length > 0 && accounts.length === Object.keys(config.routing.botIds).length && accounts.every((id) => config.routing.botIds[id] !== void 0);
|
|
105
|
+
},
|
|
106
|
+
{ path: ["routing", "botIds"], message: "must name exactly the routed accounts" }
|
|
107
|
+
).refine((config) => Object.keys(config.routing.kinds ?? {}).every((id) => config.routing.accounts[id]), {
|
|
107
108
|
path: ["routing", "kinds"],
|
|
108
109
|
message: "must only name routed accounts"
|
|
109
110
|
}).refine(
|
|
110
|
-
(config) => Object.values(config.routing
|
|
111
|
-
(kinds) => Object.keys(kinds).every((kind) => !Object.hasOwn(config.routing
|
|
111
|
+
(config) => Object.values(config.routing.kinds ?? {}).every(
|
|
112
|
+
(kinds) => Object.keys(kinds).every((kind) => !Object.hasOwn(config.routing.topics ?? {}, kind))
|
|
112
113
|
),
|
|
113
114
|
{ path: ["routing", "kinds"], message: "a kind cannot be named as a topic: they are labels of two families" }
|
|
114
115
|
);
|
|
115
116
|
var secretsSchema = z2.object({
|
|
116
117
|
CHATWOOT_TOKEN: z2.string().min(1),
|
|
117
|
-
|
|
118
|
+
CHATWOOT_AGENT_BOT_SECRETS: jsonRecord,
|
|
118
119
|
TYPESAFE_API_KEY: z2.string().min(1),
|
|
119
|
-
|
|
120
|
+
CHATWOOT_AGENT_BOT_TOKENS: jsonRecord
|
|
120
121
|
});
|
|
121
122
|
|
|
122
123
|
// scripts/stored-config.ts
|
|
@@ -4,16 +4,12 @@ export declare const configSchema: z.ZodObject<{
|
|
|
4
4
|
chatwoot: z.ZodObject<{
|
|
5
5
|
baseUrl: z.ZodURL;
|
|
6
6
|
}, z.core.$strict>;
|
|
7
|
-
reconcile: z.ZodPrefault<z.ZodObject<{
|
|
8
|
-
lookbackSeconds: z.ZodDefault<z.ZodNumber>;
|
|
9
|
-
maxCatchUpSeconds: z.ZodDefault<z.ZodNumber>;
|
|
10
|
-
}, z.core.$strict>>;
|
|
11
|
-
startAfterConversationId: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
12
7
|
subrequestBudget: z.ZodDefault<z.ZodNumber>;
|
|
13
8
|
routing: z.ZodObject<{
|
|
9
|
+
endpoint: z.ZodDefault<z.ZodURL>;
|
|
14
10
|
model: z.ZodDefault<z.ZodString>;
|
|
15
11
|
minConfidence: z.ZodDefault<z.ZodNumber>;
|
|
16
|
-
|
|
12
|
+
botIds: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
17
13
|
accounts: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
18
14
|
assignee: z.ZodNumber;
|
|
19
15
|
covers: z.ZodString;
|
|
@@ -31,16 +27,15 @@ export declare const configSchema: z.ZodObject<{
|
|
|
31
27
|
}, z.core.$strict>;
|
|
32
28
|
export declare const secretsSchema: z.ZodObject<{
|
|
33
29
|
CHATWOOT_TOKEN: z.ZodString;
|
|
34
|
-
|
|
30
|
+
CHATWOOT_AGENT_BOT_SECRETS: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<unknown, string>>, z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
35
31
|
TYPESAFE_API_KEY: z.ZodString;
|
|
36
|
-
|
|
32
|
+
CHATWOOT_AGENT_BOT_TOKENS: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<unknown, string>>, z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
37
33
|
}, z.core.$strip>;
|
|
38
34
|
type Config = z.infer<typeof configSchema>;
|
|
39
35
|
type Secrets = z.infer<typeof secretsSchema>;
|
|
40
36
|
export interface Settings {
|
|
41
37
|
config: Config;
|
|
42
38
|
secrets: Secrets;
|
|
43
|
-
botToken(accountId: number): string | undefined;
|
|
44
39
|
}
|
|
45
40
|
export declare function parseSettings(rawConfig: unknown, rawSecrets: object): Settings;
|
|
46
41
|
export declare function buildSettings(config: Config, secrets: Secrets): Settings;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { DurableObject } from "cloudflare:workers";
|
|
2
|
+
import type { Env } from "./env.ts";
|
|
3
|
+
export declare const COORDINATOR_NAME = "global";
|
|
4
|
+
/** Lists one page per alarm. Conversation state belongs exclusively to the conversation's Router. */
|
|
5
|
+
export declare class Coordinator extends DurableObject<Env> {
|
|
6
|
+
private readonly store;
|
|
7
|
+
constructor(ctx: DurableObjectState, env: Env);
|
|
8
|
+
requestSweep(): Promise<void>;
|
|
9
|
+
alarm(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { ConfigEnv } from "../../../shared/settings.ts";
|
|
2
|
+
import type { Coordinator } from "./coordinator.ts";
|
|
2
3
|
import type { Router } from "./router.ts";
|
|
3
4
|
export interface Env extends ConfigEnv {
|
|
5
|
+
COORDINATOR: DurableObjectNamespace<Coordinator>;
|
|
4
6
|
ROUTER: DurableObjectNamespace<Router>;
|
|
5
7
|
CHATWOOT_TOKEN: string;
|
|
6
|
-
|
|
8
|
+
CHATWOOT_AGENT_BOT_SECRETS: string;
|
|
7
9
|
TYPESAFE_API_KEY: string;
|
|
8
|
-
|
|
10
|
+
CHATWOOT_AGENT_BOT_TOKENS: string;
|
|
9
11
|
}
|