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.
@@ -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
- reconcile: reconcileSchema,
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
- * Snooze a ticket Jev cannot assign yet, and in which the customer asked for nothing yet, until
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 (CHATWOOT_BOT_TOKENS). None is sent while it does not exist.
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((config) => Object.keys(config.startAfterConversationId).every((id) => config.routing.accounts[id]), {
152
- path: ["startAfterConversationId"],
153
- message: "must only name routed accounts"
154
- }).refine((config) => Object.keys(config.routing?.kinds ?? {}).every((id) => config.routing?.accounts[id]), {
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?.kinds ?? {}).every(
159
- (kinds) => Object.keys(kinds).every((kind) => !Object.hasOwn(config.routing?.topics ?? {}, kind))
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
- CHATWOOT_WEBHOOK_SECRETS: jsonRecord,
166
+ CHATWOOT_AGENT_BOT_SECRETS: jsonRecord,
166
167
  TYPESAFE_API_KEY: z2.string().min(1),
167
- CHATWOOT_BOT_TOKENS: jsonRecord.default({})
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
- reconcile: reconcileSchema,
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
- * Snooze a ticket Jev cannot assign yet, and in which the customer asked for nothing yet, until
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 (CHATWOOT_BOT_TOKENS). None is sent while it does not exist.
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((config) => Object.keys(config.startAfterConversationId).every((id) => config.routing.accounts[id]), {
104
- path: ["startAfterConversationId"],
105
- message: "must only name routed accounts"
106
- }).refine((config) => Object.keys(config.routing?.kinds ?? {}).every((id) => config.routing?.accounts[id]), {
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?.kinds ?? {}).every(
111
- (kinds) => Object.keys(kinds).every((kind) => !Object.hasOwn(config.routing?.topics ?? {}, kind))
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
- CHATWOOT_WEBHOOK_SECRETS: jsonRecord,
118
+ CHATWOOT_AGENT_BOT_SECRETS: jsonRecord,
118
119
  TYPESAFE_API_KEY: z2.string().min(1),
119
- CHATWOOT_BOT_TOKENS: jsonRecord.default({})
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
- snoozeUnclear: z.ZodDefault<z.ZodBoolean>;
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
- CHATWOOT_WEBHOOK_SECRETS: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<unknown, string>>, z.ZodRecord<z.ZodString, z.ZodString>>;
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
- CHATWOOT_BOT_TOKENS: z.ZodDefault<z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<unknown, string>>, z.ZodRecord<z.ZodString, z.ZodString>>>;
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
- CHATWOOT_WEBHOOK_SECRETS: string;
8
+ CHATWOOT_AGENT_BOT_SECRETS: string;
7
9
  TYPESAFE_API_KEY: string;
8
- CHATWOOT_BOT_TOKENS?: string;
10
+ CHATWOOT_AGENT_BOT_TOKENS: string;
9
11
  }
@@ -4,4 +4,5 @@ declare const _default: {
4
4
  scheduled(_controller: ScheduledController, env: Env, context: ExecutionContext<unknown>): Promise<void>;
5
5
  };
6
6
  export default _default;
7
+ export { Coordinator } from "./coordinator.ts";
7
8
  export { Router } from "./router.ts";