@diegoaltoworks/chatter 0.11.0 → 0.13.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.
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // src/auth/apikeys.ts
5
+ var import_node_crypto = require("node:crypto");
6
+ var import_jose = require("jose");
7
+ var ApiKeyManager = class {
8
+ constructor(secret) {
9
+ this.secret = secret;
10
+ if (!secret) {
11
+ throw new Error("API key secret is required");
12
+ }
13
+ this.secretBytes = new TextEncoder().encode(secret);
14
+ }
15
+ secretBytes;
16
+ /**
17
+ * Create a new API key
18
+ *
19
+ * @param options - API key options
20
+ * @returns JWT token string
21
+ */
22
+ async create(options = {}) {
23
+ const keyId = (0, import_node_crypto.randomUUID)();
24
+ const keyName = options.name || "api-key";
25
+ const expiresIn = options.expiresIn || "365d";
26
+ const expirationMs = this.parseExpiration(expiresIn);
27
+ const expiresAt = new Date(Date.now() + expirationMs);
28
+ const jwt = await new import_jose.SignJWT({
29
+ name: keyName,
30
+ type: "api_key",
31
+ ...options.claims
32
+ }).setProtectedHeader({ alg: "HS256" }).setSubject(keyId).setIssuedAt().setExpirationTime(expiresAt).sign(this.secretBytes);
33
+ return jwt;
34
+ }
35
+ /**
36
+ * Verify an API key
37
+ *
38
+ * @param token - JWT token to verify
39
+ * @returns Verification result with payload if valid
40
+ */
41
+ async verify(token) {
42
+ try {
43
+ const { payload } = await (0, import_jose.jwtVerify)(token, this.secretBytes);
44
+ if (payload.type !== "api_key") {
45
+ return {
46
+ valid: false,
47
+ error: "Token is not an API key"
48
+ };
49
+ }
50
+ return {
51
+ valid: true,
52
+ payload
53
+ };
54
+ } catch (error) {
55
+ return {
56
+ valid: false,
57
+ error: error instanceof Error ? error.message : "Verification failed"
58
+ };
59
+ }
60
+ }
61
+ /**
62
+ * Parse expiration string to milliseconds
63
+ *
64
+ * @param expiresIn - Time string (e.g., "1y", "30d", "12h")
65
+ * @returns Milliseconds
66
+ */
67
+ parseExpiration(expiresIn) {
68
+ const match = expiresIn.match(/^(\d+)([smhdMy])$/);
69
+ if (!match) {
70
+ throw new Error(
71
+ `Invalid expiration format: ${expiresIn}. Supported: s (seconds), m (minutes), h (hours), d (days), M (months), y (years)`
72
+ );
73
+ }
74
+ const value = Number.parseInt(match[1], 10);
75
+ const unit = match[2];
76
+ switch (unit) {
77
+ case "s":
78
+ return value * 1e3;
79
+ case "m":
80
+ return value * 60 * 1e3;
81
+ case "h":
82
+ return value * 60 * 60 * 1e3;
83
+ case "d":
84
+ return value * 24 * 60 * 60 * 1e3;
85
+ case "M":
86
+ return value * 30 * 24 * 60 * 60 * 1e3;
87
+ // Approximate 30 days
88
+ case "y":
89
+ return value * 365 * 24 * 60 * 60 * 1e3;
90
+ // Approximate 365 days
91
+ default:
92
+ throw new Error(`Unknown time unit: ${unit}`);
93
+ }
94
+ }
95
+ /**
96
+ * Get information about a token without verifying signature
97
+ * Useful for debugging
98
+ *
99
+ * @param token - JWT token
100
+ * @returns Decoded payload (unverified)
101
+ */
102
+ decode(token) {
103
+ try {
104
+ const parts = token.split(".");
105
+ if (parts.length !== 3) {
106
+ return null;
107
+ }
108
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf-8"));
109
+ return payload;
110
+ } catch {
111
+ return null;
112
+ }
113
+ }
114
+ };
115
+
116
+ // bin/create-apikey.ts
117
+ async function main() {
118
+ const args = process.argv.slice(2);
119
+ const options = {};
120
+ for (let i = 0; i < args.length; i++) {
121
+ if (args[i] === "--name" && args[i + 1]) {
122
+ options.name = args[i + 1];
123
+ i++;
124
+ } else if (args[i] === "--expires-in" && args[i + 1]) {
125
+ options.expiresIn = args[i + 1];
126
+ i++;
127
+ } else if (args[i] === "--help" || args[i] === "-h") {
128
+ console.log(`
129
+ Chatter API Key Generator
130
+
131
+ Usage:
132
+ npx chatter create-apikey [options]
133
+
134
+ Options:
135
+ --name <name> Name/label for the API key (default: "api-key")
136
+ --expires-in <time> Expiration time (default: "365d")
137
+ Formats: s=seconds, m=minutes, h=hours, d=days, M=months, y=years
138
+ Examples: 30d, 12h, 1y, 6M
139
+
140
+ Environment:
141
+ CHATTER_SECRET Required. Secret key for signing JWT tokens
142
+
143
+ Examples:
144
+ npx chatter create-apikey --name "mobile-app" --expires-in 365d
145
+ npx chatter create-apikey --name "test-key" --expires-in 1h
146
+ `);
147
+ process.exit(0);
148
+ }
149
+ }
150
+ const secret = process.env.CHATTER_SECRET;
151
+ if (!secret) {
152
+ console.error("\u274C Error: CHATTER_SECRET environment variable is not set");
153
+ console.error("\n Please set it before running this command:");
154
+ console.error(" export CHATTER_SECRET=your-secret-key-here");
155
+ console.error("\n Or prefix the command:");
156
+ console.error(" CHATTER_SECRET=your-secret npx chatter create-apikey\n");
157
+ process.exit(1);
158
+ }
159
+ const manager = new ApiKeyManager(secret);
160
+ const keyName = options.name || "api-key";
161
+ const expiresIn = options.expiresIn || "365d";
162
+ try {
163
+ const apiKey = await manager.create({
164
+ name: keyName,
165
+ expiresIn
166
+ });
167
+ const payload = manager.decode(apiKey);
168
+ if (!payload) {
169
+ throw new Error("Failed to decode generated API key");
170
+ }
171
+ const expiresAt = new Date((payload.exp || 0) * 1e3);
172
+ const issuedAt = new Date((payload.iat || 0) * 1e3);
173
+ console.log("\n\u2705 API Key generated successfully!\n");
174
+ console.log(` Name: ${keyName}`);
175
+ console.log(` ID: ${payload.sub}`);
176
+ console.log(` Issued: ${issuedAt.toISOString()}`);
177
+ console.log(` Expires: ${expiresAt.toISOString()} (${expiresIn})`);
178
+ console.log(`
179
+ API Key:
180
+ ${apiKey}
181
+ `);
182
+ console.log(" Usage:");
183
+ console.log(" curl -X POST https://your-bot.example.com/api/public/chat \\");
184
+ console.log(` -H "x-api-key: ${apiKey.slice(0, 50)}..." \\`);
185
+ console.log(' -H "Content-Type: application/json" \\');
186
+ console.log(` -d '{"message": "Hello"}'`);
187
+ console.log("");
188
+ } catch (error) {
189
+ console.error("\u274C Error creating API key:", error);
190
+ process.exit(1);
191
+ }
192
+ }
193
+ main();
package/dist/index.js CHANGED
@@ -7659,22 +7659,38 @@ function resolveStatic(configStaticDir) {
7659
7659
  }
7660
7660
 
7661
7661
  // src/middleware/cors.ts
7662
- var cors = () => async (c, next) => {
7663
- c.header("access-control-allow-origin", "*");
7664
- c.header("access-control-allow-headers", "content-type, x-api-key, authorization");
7665
- c.header("access-control-allow-methods", "POST, GET, OPTIONS");
7662
+ var ALLOWED_HEADERS = "content-type, x-api-key, authorization";
7663
+ var ALLOWED_METHODS = "POST, GET, OPTIONS";
7664
+ var cors = (allowedOrigins) => async (c, next) => {
7665
+ const requestOrigin = c.req.header("origin");
7666
+ const allowAll = !allowedOrigins || allowedOrigins.length === 0 || allowedOrigins.includes("*");
7667
+ const originHeader = resolveOrigin(requestOrigin, allowedOrigins, allowAll);
7666
7668
  if (c.req.method === "OPTIONS") {
7667
7669
  return new Response(null, {
7668
7670
  status: 204,
7669
7671
  headers: {
7670
- "access-control-allow-origin": "*",
7671
- "access-control-allow-headers": "content-type, x-api-key, authorization",
7672
- "access-control-allow-methods": "POST, GET, OPTIONS"
7672
+ "access-control-allow-origin": originHeader,
7673
+ "access-control-allow-headers": ALLOWED_HEADERS,
7674
+ "access-control-allow-methods": ALLOWED_METHODS,
7675
+ ...allowAll ? {} : { vary: "Origin" }
7673
7676
  }
7674
7677
  });
7675
7678
  }
7679
+ c.header("access-control-allow-origin", originHeader);
7680
+ c.header("access-control-allow-headers", ALLOWED_HEADERS);
7681
+ c.header("access-control-allow-methods", ALLOWED_METHODS);
7682
+ if (!allowAll) {
7683
+ c.header("vary", "Origin");
7684
+ }
7676
7685
  await next();
7677
7686
  };
7687
+ function resolveOrigin(requestOrigin, allowedOrigins, allowAll) {
7688
+ if (allowAll) return "*";
7689
+ if (requestOrigin && allowedOrigins.includes(requestOrigin)) {
7690
+ return requestOrigin;
7691
+ }
7692
+ return allowedOrigins[0];
7693
+ }
7678
7694
 
7679
7695
  // src/routes/demo.ts
7680
7696
  var import_hono = require("hono");
@@ -7784,14 +7800,8 @@ function getActiveSessions() {
7784
7800
  }
7785
7801
 
7786
7802
  // src/routes/demo.ts
7787
- function checkDemoOrigin() {
7788
- const allowedOrigins = [
7789
- "https://bot.diegoalto.app",
7790
- "https://diegoalto.co.uk",
7791
- "http://diegoalto.co.uk",
7792
- "https://diegoalto.works",
7793
- "http://diegoalto.works"
7794
- ];
7803
+ function checkDemoOrigin(allowedOrigins) {
7804
+ const allowAll = allowedOrigins.length === 0 || allowedOrigins.includes("*");
7795
7805
  return async (c, next) => {
7796
7806
  const origin = c.req.header("origin");
7797
7807
  const referer = c.req.header("referer");
@@ -7799,6 +7809,9 @@ function checkDemoOrigin() {
7799
7809
  if (host.startsWith("localhost:") || host.startsWith("127.0.0.1:")) {
7800
7810
  return next();
7801
7811
  }
7812
+ if (allowAll) {
7813
+ return next();
7814
+ }
7802
7815
  if (origin) {
7803
7816
  const allowed = allowedOrigins.some((allowed2) => origin.startsWith(allowed2));
7804
7817
  if (allowed) return next();
@@ -7868,9 +7881,10 @@ function limitDemo() {
7868
7881
  };
7869
7882
  }
7870
7883
  function demoRoutes(deps) {
7871
- const { client, store, prompts } = deps;
7884
+ const { client, store, config: config2, prompts } = deps;
7872
7885
  const app = new import_hono.Hono();
7873
- app.get("/api/demo/session", checkDemoOrigin(), limitSessionCreation(), (c) => {
7886
+ const demoOrigins = config2.server?.allowedOrigins ?? [];
7887
+ app.get("/api/demo/session", checkDemoOrigin(demoOrigins), limitSessionCreation(), (c) => {
7874
7888
  const session = createSession({
7875
7889
  maxRequests: 20,
7876
7890
  ttl: 3600
@@ -8213,12 +8227,10 @@ function publicRoutes(deps) {
8213
8227
  const app = new import_hono3.Hono();
8214
8228
  const requirePublicKey = createAuthMiddleware(deps);
8215
8229
  const { limitPublic: limitPublic2 } = createRateLimiter(config2);
8230
+ const referrerOrigins = config2.server?.allowedOrigins?.length ? config2.server.allowedOrigins.filter((o) => o !== "*") : [config2.bot.publicUrl, "http://localhost:8181", "http://127.0.0.1:8181"];
8216
8231
  app.use("/api/public/*", validateSessionKey());
8217
8232
  app.use("/api/public/*", requirePublicKey);
8218
- app.use(
8219
- "/api/public/*",
8220
- requireReferrer([config2.bot.publicUrl, "http://localhost:8181", "http://127.0.0.1:8181"])
8221
- );
8233
+ app.use("/api/public/*", requireReferrer(referrerOrigins));
8222
8234
  app.use("/api/public/*", limitPublic2());
8223
8235
  app.post("/api/public/chat", async (c) => {
8224
8236
  const body = await c.req.json().catch(() => ({}));
@@ -8292,7 +8304,7 @@ async function createServer(config2) {
8292
8304
  }
8293
8305
  const app = new import_hono4.Hono();
8294
8306
  if (config2.server?.cors !== false) {
8295
- app.use("*", cors());
8307
+ app.use("*", cors(config2.server?.allowedOrigins));
8296
8308
  }
8297
8309
  app.get("/healthz", (c) => c.text("ok"));
8298
8310
  app.get(
package/dist/index.mjs CHANGED
@@ -7630,22 +7630,38 @@ function resolveStatic(configStaticDir) {
7630
7630
  }
7631
7631
 
7632
7632
  // src/middleware/cors.ts
7633
- var cors = () => async (c, next) => {
7634
- c.header("access-control-allow-origin", "*");
7635
- c.header("access-control-allow-headers", "content-type, x-api-key, authorization");
7636
- c.header("access-control-allow-methods", "POST, GET, OPTIONS");
7633
+ var ALLOWED_HEADERS = "content-type, x-api-key, authorization";
7634
+ var ALLOWED_METHODS = "POST, GET, OPTIONS";
7635
+ var cors = (allowedOrigins) => async (c, next) => {
7636
+ const requestOrigin = c.req.header("origin");
7637
+ const allowAll = !allowedOrigins || allowedOrigins.length === 0 || allowedOrigins.includes("*");
7638
+ const originHeader = resolveOrigin(requestOrigin, allowedOrigins, allowAll);
7637
7639
  if (c.req.method === "OPTIONS") {
7638
7640
  return new Response(null, {
7639
7641
  status: 204,
7640
7642
  headers: {
7641
- "access-control-allow-origin": "*",
7642
- "access-control-allow-headers": "content-type, x-api-key, authorization",
7643
- "access-control-allow-methods": "POST, GET, OPTIONS"
7643
+ "access-control-allow-origin": originHeader,
7644
+ "access-control-allow-headers": ALLOWED_HEADERS,
7645
+ "access-control-allow-methods": ALLOWED_METHODS,
7646
+ ...allowAll ? {} : { vary: "Origin" }
7644
7647
  }
7645
7648
  });
7646
7649
  }
7650
+ c.header("access-control-allow-origin", originHeader);
7651
+ c.header("access-control-allow-headers", ALLOWED_HEADERS);
7652
+ c.header("access-control-allow-methods", ALLOWED_METHODS);
7653
+ if (!allowAll) {
7654
+ c.header("vary", "Origin");
7655
+ }
7647
7656
  await next();
7648
7657
  };
7658
+ function resolveOrigin(requestOrigin, allowedOrigins, allowAll) {
7659
+ if (allowAll) return "*";
7660
+ if (requestOrigin && allowedOrigins.includes(requestOrigin)) {
7661
+ return requestOrigin;
7662
+ }
7663
+ return allowedOrigins[0];
7664
+ }
7649
7665
 
7650
7666
  // src/routes/demo.ts
7651
7667
  import { Hono } from "hono";
@@ -7755,14 +7771,8 @@ function getActiveSessions() {
7755
7771
  }
7756
7772
 
7757
7773
  // src/routes/demo.ts
7758
- function checkDemoOrigin() {
7759
- const allowedOrigins = [
7760
- "https://bot.diegoalto.app",
7761
- "https://diegoalto.co.uk",
7762
- "http://diegoalto.co.uk",
7763
- "https://diegoalto.works",
7764
- "http://diegoalto.works"
7765
- ];
7774
+ function checkDemoOrigin(allowedOrigins) {
7775
+ const allowAll = allowedOrigins.length === 0 || allowedOrigins.includes("*");
7766
7776
  return async (c, next) => {
7767
7777
  const origin = c.req.header("origin");
7768
7778
  const referer = c.req.header("referer");
@@ -7770,6 +7780,9 @@ function checkDemoOrigin() {
7770
7780
  if (host.startsWith("localhost:") || host.startsWith("127.0.0.1:")) {
7771
7781
  return next();
7772
7782
  }
7783
+ if (allowAll) {
7784
+ return next();
7785
+ }
7773
7786
  if (origin) {
7774
7787
  const allowed = allowedOrigins.some((allowed2) => origin.startsWith(allowed2));
7775
7788
  if (allowed) return next();
@@ -7839,9 +7852,10 @@ function limitDemo() {
7839
7852
  };
7840
7853
  }
7841
7854
  function demoRoutes(deps) {
7842
- const { client, store, prompts } = deps;
7855
+ const { client, store, config: config2, prompts } = deps;
7843
7856
  const app = new Hono();
7844
- app.get("/api/demo/session", checkDemoOrigin(), limitSessionCreation(), (c) => {
7857
+ const demoOrigins = config2.server?.allowedOrigins ?? [];
7858
+ app.get("/api/demo/session", checkDemoOrigin(demoOrigins), limitSessionCreation(), (c) => {
7845
7859
  const session = createSession({
7846
7860
  maxRequests: 20,
7847
7861
  ttl: 3600
@@ -8188,12 +8202,10 @@ function publicRoutes(deps) {
8188
8202
  const app = new Hono3();
8189
8203
  const requirePublicKey = createAuthMiddleware(deps);
8190
8204
  const { limitPublic: limitPublic2 } = createRateLimiter(config2);
8205
+ const referrerOrigins = config2.server?.allowedOrigins?.length ? config2.server.allowedOrigins.filter((o) => o !== "*") : [config2.bot.publicUrl, "http://localhost:8181", "http://127.0.0.1:8181"];
8191
8206
  app.use("/api/public/*", validateSessionKey());
8192
8207
  app.use("/api/public/*", requirePublicKey);
8193
- app.use(
8194
- "/api/public/*",
8195
- requireReferrer([config2.bot.publicUrl, "http://localhost:8181", "http://127.0.0.1:8181"])
8196
- );
8208
+ app.use("/api/public/*", requireReferrer(referrerOrigins));
8197
8209
  app.use("/api/public/*", limitPublic2());
8198
8210
  app.post("/api/public/chat", async (c) => {
8199
8211
  const body = await c.req.json().catch(() => ({}));
@@ -8267,7 +8279,7 @@ async function createServer(config2) {
8267
8279
  }
8268
8280
  const app = new Hono4();
8269
8281
  if (config2.server?.cors !== false) {
8270
- app.use("*", cors());
8282
+ app.use("*", cors(config2.server?.allowedOrigins));
8271
8283
  }
8272
8284
  app.get("/healthz", (c) => c.text("ok"));
8273
8285
  app.get(
@@ -1,3 +1,10 @@
1
1
  import type { Context, Next } from "hono";
2
- export declare const cors: () => (c: Context, next: Next) => Promise<Response | undefined>;
2
+ /**
3
+ * CORS middleware that validates requests against allowed origins.
4
+ *
5
+ * @param allowedOrigins - Array of permitted origins. When the array contains
6
+ * `"*"` (or is empty/undefined), all origins are allowed. Otherwise only
7
+ * the listed origins receive CORS headers.
8
+ */
9
+ export declare const cors: (allowedOrigins?: string[]) => (c: Context, next: Next) => Promise<Response | undefined>;
3
10
  //# sourceMappingURL=cors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../src/middleware/cors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE1C,eAAO,MAAM,IAAI,SAAgB,GAAG,OAAO,EAAE,MAAM,IAAI,kCAetD,CAAC"}
1
+ {"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../src/middleware/cors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAK1C;;;;;;GAMG;AACH,eAAO,MAAM,IAAI,GAAI,iBAAiB,MAAM,EAAE,MAAY,GAAG,OAAO,EAAE,MAAM,IAAI,kCAyB/E,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"demo.d.ts","sourceRoot":"","sources":["../../src/routes/demo.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAoHnD,wBAAgB,UAAU,CAAC,IAAI,EAAE,kBAAkB,8EAkGlD"}
1
+ {"version":3,"file":"demo.d.ts","sourceRoot":"","sources":["../../src/routes/demo.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAsHnD,wBAAgB,UAAU,CAAC,IAAI,EAAE,kBAAkB,8EAqGlD"}
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/routes/public.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAO5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AASnD,wBAAgB,YAAY,CAAC,IAAI,EAAE,kBAAkB,8EA+DpD"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/routes/public.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAO5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AASnD,wBAAgB,YAAY,CAAC,IAAI,EAAE,kBAAkB,8EAkEpD"}
package/dist/types.d.ts CHANGED
@@ -121,6 +121,15 @@ export interface ChatterConfig {
121
121
  port?: number;
122
122
  /** Enable CORS. Default: true */
123
123
  cors?: boolean;
124
+ /**
125
+ * Allowed origins for CORS and referrer checks.
126
+ * Requests from these domains will be permitted.
127
+ *
128
+ * @example ["https://myapp.com", "https://staging.myapp.com"]
129
+ *
130
+ * When not set, defaults to `["*"]` (all origins allowed).
131
+ */
132
+ allowedOrigins?: string[];
124
133
  };
125
134
  /** Custom route handler for advanced use cases */
126
135
  customRoutes?: (app: Hono, deps: ServerDependencies) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,4CAA4C;IAC5C,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,mCAAmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oCAAoC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAE5B,GAAG,EAAE,WAAW,CAAC;IACjB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,IAAI,CAAC,EAAE,aAAa,CAAC;IAGrB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,MAAM,EAAE;QACN,qBAAqB;QACrB,MAAM,EAAE,MAAM,CAAC;QACf,yCAAyC;QACzC,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IAGF,QAAQ,EAAE;QACR,yBAAyB;QACzB,GAAG,EAAE,MAAM,CAAC;QACZ,uBAAuB;QACvB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IAGF,IAAI,CAAC,EAAE;QACL,mDAAmD;QACnD,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,qEAAqE;QACrE,aAAa,CAAC,EAAE;YACd,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;gBAAE,KAAK,EAAE,OAAO,CAAC;gBAAC,OAAO,CAAC,EAAE,OAAO,CAAC;gBAAC,KAAK,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC,CAAC;SAC3F,CAAC;QACF,6CAA6C;QAC7C,GAAG,CAAC,EAAE;YACJ,oCAAoC;YACpC,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,0BAA0B;YAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;YAChB,4BAA4B;YAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;YAClB,2CAA2C;YAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;SACvB,CAAC;QACF,0BAA0B;QAC1B,KAAK,CAAC,EAAE;YACN,4BAA4B;YAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;YACxB,6BAA6B;YAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;SACtB,CAAC;KACH,CAAC;IAGF,QAAQ,CAAC,EAAE;QACT,iDAAiD;QACjD,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,kDAAkD;QAClD,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,mDAAmD;QACnD,gBAAgB,CAAC,EAAE,OAAO,CAAC;KAC5B,CAAC;IAGF,SAAS,CAAC,EAAE;QACV,2DAA2D;QAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,6DAA6D;QAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IAGF,MAAM,CAAC,EAAE;QACP,iCAAiC;QACjC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,iCAAiC;QACjC,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,CAAC;IAGF,kDAAkD;IAClD,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;CAC9D;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,4BAA4B;IAC5B,KAAK,EAAE,WAAW,CAAC;IACnB,4BAA4B;IAC5B,MAAM,EAAE,aAAa,CAAC;IACtB,6BAA6B;IAC7B,OAAO,EAAE,YAAY,CAAC;IACtB,+CAA+C;IAC/C,aAAa,CAAC,EAAE;QACd,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;YAAE,KAAK,EAAE,OAAO,CAAC;YAAC,OAAO,CAAC,EAAE,OAAO,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAC3F,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,wBAAwB;IACxB,QAAQ,EAAE;QACR,uBAAuB;QACvB,MAAM,EAAE,MAAM,CAAC;QACf,2CAA2C;QAC3C,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;QACrC,8BAA8B;QAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,qBAAqB;IACrB,QAAQ,EAAE;QACR,uBAAuB;QACvB,MAAM,EAAE,MAAM,CAAC;QACf,qBAAqB;QACrB,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;QACrC,8BAA8B;QAC9B,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;CACH"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@diegoaltoworks/chatter",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "Embeddable AI chatbot framework with RAG, authentication, and customizable widgets",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
8
8
  "bin": {
9
- "chatter": "./bin/create-apikey.ts"
9
+ "chatter": "./dist/bin/create-apikey.js"
10
10
  },
11
11
  "exports": {
12
12
  ".": {
@@ -30,15 +30,16 @@
30
30
  "require": "./dist/mcp-server.js"
31
31
  }
32
32
  },
33
- "files": ["dist", "bin", "README.md", "LICENSE"],
33
+ "files": ["dist", "README.md", "LICENSE"],
34
34
  "scripts": {
35
35
  "dev": "bun --watch src/index.ts",
36
- "build": "bun run build:clean && bun run build:types && bun run build:esm && bun run build:cjs && bun run build:mcp && bun run build:client && bun run build:copy-client",
36
+ "build": "bun run build:clean && bun run build:types && bun run build:esm && bun run build:cjs && bun run build:mcp && bun run build:bin && bun run build:client && bun run build:copy-client",
37
37
  "build:clean": "rm -rf dist",
38
38
  "build:types": "tsc --emitDeclarationOnly --outDir dist",
39
39
  "build:esm": "esbuild src/index.ts --bundle --format=esm --outfile=dist/index.mjs --external:hono --external:openai --external:@libsql/client --external:jose --platform=node --target=node24",
40
40
  "build:cjs": "esbuild src/index.ts --bundle --format=cjs --outfile=dist/index.js --external:hono --external:openai --external:@libsql/client --external:jose --platform=node --target=node24",
41
41
  "build:mcp": "esbuild src/mcp-server.ts --bundle --format=esm --outfile=dist/mcp-server.mjs --external:@modelcontextprotocol/sdk --external:openai --external:@libsql/client --external:zod --platform=node --target=node24 && esbuild src/mcp-server.ts --bundle --format=cjs --outfile=dist/mcp-server.js --external:@modelcontextprotocol/sdk --external:openai --external:@libsql/client --external:zod --platform=node --target=node24",
42
+ "build:bin": "esbuild bin/create-apikey.ts --bundle --format=cjs --outfile=dist/bin/create-apikey.js --external:jose --platform=node --target=node18 --banner:js='#!/usr/bin/env node'",
42
43
  "build:client": "cd src/client && bun run build",
43
44
  "build:copy-client": "mkdir -p dist/static && cp src/client/dist/chatter.js src/client/dist/chatter.css dist/static/",
44
45
  "test": "bun test",
@@ -1,101 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * CLI tool for creating Chatter API keys
4
- *
5
- * Usage:
6
- * npx chatter create-apikey --name "my-key" --expires-in 365d
7
- * bun run bin/create-apikey.ts --name "test" --expires-in 1y
8
- */
9
-
10
- import { ApiKeyManager } from "../src/auth/apikeys";
11
-
12
- async function main() {
13
- // Parse command line arguments
14
- const args = process.argv.slice(2);
15
- const options: { name?: string; expiresIn?: string } = {};
16
-
17
- for (let i = 0; i < args.length; i++) {
18
- if (args[i] === "--name" && args[i + 1]) {
19
- options.name = args[i + 1];
20
- i++;
21
- } else if (args[i] === "--expires-in" && args[i + 1]) {
22
- options.expiresIn = args[i + 1];
23
- i++;
24
- } else if (args[i] === "--help" || args[i] === "-h") {
25
- console.log(`
26
- Chatter API Key Generator
27
-
28
- Usage:
29
- npx chatter create-apikey [options]
30
-
31
- Options:
32
- --name <name> Name/label for the API key (default: "api-key")
33
- --expires-in <time> Expiration time (default: "365d")
34
- Formats: s=seconds, m=minutes, h=hours, d=days, M=months, y=years
35
- Examples: 30d, 12h, 1y, 6M
36
-
37
- Environment:
38
- CHATTER_SECRET Required. Secret key for signing JWT tokens
39
-
40
- Examples:
41
- npx chatter create-apikey --name "mobile-app" --expires-in 365d
42
- npx chatter create-apikey --name "test-key" --expires-in 1h
43
- `);
44
- process.exit(0);
45
- }
46
- }
47
-
48
- // Check for CHATTER_SECRET
49
- const secret = process.env.CHATTER_SECRET;
50
- if (!secret) {
51
- console.error("❌ Error: CHATTER_SECRET environment variable is not set");
52
- console.error("\n Please set it before running this command:");
53
- console.error(" export CHATTER_SECRET=your-secret-key-here");
54
- console.error("\n Or prefix the command:");
55
- console.error(" CHATTER_SECRET=your-secret npx chatter create-apikey\n");
56
- process.exit(1);
57
- }
58
-
59
- // Create API key manager
60
- const manager = new ApiKeyManager(secret);
61
-
62
- // Default values
63
- const keyName = options.name || "api-key";
64
- const expiresIn = options.expiresIn || "365d";
65
-
66
- try {
67
- // Create the API key
68
- const apiKey = await manager.create({
69
- name: keyName,
70
- expiresIn,
71
- });
72
-
73
- // Decode to show details (without verification for display)
74
- const payload = manager.decode(apiKey);
75
- if (!payload) {
76
- throw new Error("Failed to decode generated API key");
77
- }
78
-
79
- const expiresAt = new Date((payload.exp || 0) * 1000);
80
- const issuedAt = new Date((payload.iat || 0) * 1000);
81
-
82
- // Output the result
83
- console.log("\n✅ API Key generated successfully!\n");
84
- console.log(` Name: ${keyName}`);
85
- console.log(` ID: ${payload.sub}`);
86
- console.log(` Issued: ${issuedAt.toISOString()}`);
87
- console.log(` Expires: ${expiresAt.toISOString()} (${expiresIn})`);
88
- console.log(`\n API Key:\n ${apiKey}\n`);
89
- console.log(" Usage:");
90
- console.log(" curl -X POST https://your-bot.example.com/api/public/chat \\");
91
- console.log(` -H "x-api-key: ${apiKey.slice(0, 50)}..." \\`);
92
- console.log(' -H "Content-Type: application/json" \\');
93
- console.log(' -d \'{"message": "Hello"}\'');
94
- console.log("");
95
- } catch (error) {
96
- console.error("❌ Error creating API key:", error);
97
- process.exit(1);
98
- }
99
- }
100
-
101
- main();