@aprovan/chat-backend 0.1.0-dev.4d82df8 → 0.1.0-dev.78c0b14
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/.turbo/turbo-build.log +7 -7
- package/dist/index.d.ts +0 -3
- package/dist/index.js +122 -294
- package/dist/index.js.map +1 -1
- package/dist/lambda.js +122 -294
- package/dist/lambda.js.map +1 -1
- package/package.json +1 -1
- package/src/app.ts +3 -5
- package/src/env.ts +0 -1
- package/src/gateway-session.ts +1 -38
- package/src/middleware/workspace.ts +19 -56
- package/src/routes/chat.ts +8 -28
- package/test/app.test.ts +0 -3
- package/test/gateway-session.test.ts +0 -79
- package/test/middleware/workspace.test.ts +45 -66
- package/test/routes/chat.test.ts +0 -99
- package/src/routes/workspaces.ts +0 -89
- package/src/session.ts +0 -80
- package/test/routes/workspaces.test.ts +0 -164
- package/test/session.test.ts +0 -56
package/.turbo/turbo-build.log
CHANGED
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
[34mCLI[39m Target: node20
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mESM[39m Build start
|
|
12
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
13
|
-
[32mESM[39m [1mdist/lambda.js [22m[
|
|
14
|
-
[32mESM[39m [1mdist/index.js.map [22m[
|
|
15
|
-
[32mESM[39m [1mdist/lambda.js.map [22m[
|
|
16
|
-
[32mESM[39m ⚡️ Build success in
|
|
12
|
+
[32mESM[39m [1mdist/index.js [22m[32m32.89 KB[39m
|
|
13
|
+
[32mESM[39m [1mdist/lambda.js [22m[32m32.57 KB[39m
|
|
14
|
+
[32mESM[39m [1mdist/index.js.map [22m[32m59.37 KB[39m
|
|
15
|
+
[32mESM[39m [1mdist/lambda.js.map [22m[32m58.51 KB[39m
|
|
16
|
+
[32mESM[39m ⚡️ Build success in 123ms
|
|
17
17
|
[34mDTS[39m Build start
|
|
18
|
-
[32mDTS[39m ⚡️ Build success in
|
|
19
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m2.
|
|
18
|
+
[32mDTS[39m ⚡️ Build success in 14161ms
|
|
19
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m2.55 KB[39m
|
|
20
20
|
[32mDTS[39m [1mdist/lambda.d.ts [22m[32m171.00 B[39m
|
package/dist/index.d.ts
CHANGED
|
@@ -38,7 +38,6 @@ declare const envSchema: z.ZodObject<{
|
|
|
38
38
|
AWS_REGION: z.ZodDefault<z.ZodString>;
|
|
39
39
|
WORKSPACE_TABLE_NAME: z.ZodString;
|
|
40
40
|
MEMBERSHIPS_TABLE_NAME: z.ZodString;
|
|
41
|
-
USER_SESSIONS_TABLE_NAME: z.ZodString;
|
|
42
41
|
OPENROUTER_SECRET_ARN: z.ZodString;
|
|
43
42
|
GATEWAY_URL: z.ZodString;
|
|
44
43
|
POSTHOG_PROJECT_API_KEY: z.ZodOptional<z.ZodString>;
|
|
@@ -52,7 +51,6 @@ declare const envSchema: z.ZodObject<{
|
|
|
52
51
|
AWS_REGION: string;
|
|
53
52
|
WORKSPACE_TABLE_NAME: string;
|
|
54
53
|
MEMBERSHIPS_TABLE_NAME: string;
|
|
55
|
-
USER_SESSIONS_TABLE_NAME: string;
|
|
56
54
|
OPENROUTER_SECRET_ARN: string;
|
|
57
55
|
GATEWAY_URL: string;
|
|
58
56
|
POSTHOG_HOST: string;
|
|
@@ -63,7 +61,6 @@ declare const envSchema: z.ZodObject<{
|
|
|
63
61
|
COGNITO_CLIENT_ID: string;
|
|
64
62
|
WORKSPACE_TABLE_NAME: string;
|
|
65
63
|
MEMBERSHIPS_TABLE_NAME: string;
|
|
66
|
-
USER_SESSIONS_TABLE_NAME: string;
|
|
67
64
|
OPENROUTER_SECRET_ARN: string;
|
|
68
65
|
GATEWAY_URL: string;
|
|
69
66
|
NODE_ENV?: "development" | "production" | "test" | undefined;
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { serve } from "@hono/node-server";
|
|
3
3
|
|
|
4
4
|
// src/app.ts
|
|
5
|
-
import { Hono as
|
|
5
|
+
import { Hono as Hono6 } from "hono";
|
|
6
6
|
|
|
7
7
|
// src/middleware/auth.ts
|
|
8
8
|
import { CognitoJwtVerifier } from "aws-jwt-verify";
|
|
@@ -32,11 +32,11 @@ var authMiddleware = async (c, next) => {
|
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
-
// src/middleware/
|
|
35
|
+
// src/middleware/workspace.ts
|
|
36
36
|
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
37
|
-
import { DynamoDBDocumentClient,
|
|
38
|
-
var
|
|
39
|
-
var
|
|
37
|
+
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";
|
|
38
|
+
var MEMBERSHIP_CACHE_TTL_MS = 3e5;
|
|
39
|
+
var membershipCache = /* @__PURE__ */ new Map();
|
|
40
40
|
var ddbClient = null;
|
|
41
41
|
function getDdb() {
|
|
42
42
|
if (!ddbClient) {
|
|
@@ -46,13 +46,57 @@ function getDdb() {
|
|
|
46
46
|
}
|
|
47
47
|
return ddbClient;
|
|
48
48
|
}
|
|
49
|
+
async function resolveWorkspaceId(userSub) {
|
|
50
|
+
const now = Date.now();
|
|
51
|
+
const cached = membershipCache.get(userSub);
|
|
52
|
+
if (cached && now - cached.fetchedAt < MEMBERSHIP_CACHE_TTL_MS) {
|
|
53
|
+
return cached.workspaceId;
|
|
54
|
+
}
|
|
55
|
+
const result = await getDdb().send(
|
|
56
|
+
new QueryCommand({
|
|
57
|
+
TableName: process.env["MEMBERSHIPS_TABLE_NAME"],
|
|
58
|
+
IndexName: "ByUserSub",
|
|
59
|
+
KeyConditionExpression: "userSub = :sub",
|
|
60
|
+
ExpressionAttributeValues: { ":sub": userSub },
|
|
61
|
+
Limit: 1
|
|
62
|
+
})
|
|
63
|
+
);
|
|
64
|
+
const item = result.Items?.[0];
|
|
65
|
+
if (!item) return null;
|
|
66
|
+
membershipCache.set(userSub, { workspaceId: item.workspaceId, fetchedAt: now });
|
|
67
|
+
return item.workspaceId;
|
|
68
|
+
}
|
|
69
|
+
var workspaceMiddleware = async (c, next) => {
|
|
70
|
+
const claims = c.get("claims");
|
|
71
|
+
const workspaceId = await resolveWorkspaceId(claims.sub);
|
|
72
|
+
if (!workspaceId) {
|
|
73
|
+
return c.json({ error: "No workspace membership" }, 403);
|
|
74
|
+
}
|
|
75
|
+
c.set("workspaceId", workspaceId);
|
|
76
|
+
return next();
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// src/middleware/plan.ts
|
|
80
|
+
import { DynamoDBClient as DynamoDBClient2 } from "@aws-sdk/client-dynamodb";
|
|
81
|
+
import { DynamoDBDocumentClient as DynamoDBDocumentClient2, GetCommand } from "@aws-sdk/lib-dynamodb";
|
|
82
|
+
var WORKSPACE_CACHE_TTL_MS = 6e4;
|
|
83
|
+
var workspaceCache = /* @__PURE__ */ new Map();
|
|
84
|
+
var ddbClient2 = null;
|
|
85
|
+
function getDdb2() {
|
|
86
|
+
if (!ddbClient2) {
|
|
87
|
+
ddbClient2 = DynamoDBDocumentClient2.from(
|
|
88
|
+
new DynamoDBClient2({ region: process.env["AWS_REGION"] })
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return ddbClient2;
|
|
92
|
+
}
|
|
49
93
|
async function getWorkspace(workspaceId) {
|
|
50
94
|
const now = Date.now();
|
|
51
95
|
const cached = workspaceCache.get(workspaceId);
|
|
52
96
|
if (cached && now - cached.fetchedAt < WORKSPACE_CACHE_TTL_MS) {
|
|
53
97
|
return cached.workspace;
|
|
54
98
|
}
|
|
55
|
-
const result = await
|
|
99
|
+
const result = await getDdb2().send(
|
|
56
100
|
new GetCommand({
|
|
57
101
|
TableName: process.env["WORKSPACE_TABLE_NAME"],
|
|
58
102
|
Key: { workspaceId }
|
|
@@ -79,116 +123,10 @@ var planMiddleware = async (c, next) => {
|
|
|
79
123
|
return next();
|
|
80
124
|
};
|
|
81
125
|
|
|
82
|
-
// src/
|
|
83
|
-
import {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
// src/session.ts
|
|
87
|
-
import { DynamoDBClient as DynamoDBClient2 } from "@aws-sdk/client-dynamodb";
|
|
88
|
-
import {
|
|
89
|
-
DynamoDBDocumentClient as DynamoDBDocumentClient2,
|
|
90
|
-
GetCommand as GetCommand2,
|
|
91
|
-
PutCommand
|
|
92
|
-
} from "@aws-sdk/lib-dynamodb";
|
|
93
|
-
var SESSION_CACHE_TTL_MS = 15e3;
|
|
94
|
-
var sessionCache = /* @__PURE__ */ new Map();
|
|
95
|
-
var ddbClient2 = null;
|
|
96
|
-
function getDdb2() {
|
|
97
|
-
if (!ddbClient2) {
|
|
98
|
-
ddbClient2 = DynamoDBDocumentClient2.from(
|
|
99
|
-
new DynamoDBClient2({ region: process.env["AWS_REGION"] })
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
return ddbClient2;
|
|
103
|
-
}
|
|
104
|
-
function tableName() {
|
|
105
|
-
return process.env["USER_SESSIONS_TABLE_NAME"];
|
|
106
|
-
}
|
|
107
|
-
async function getSessionWorkspaceId(userSub) {
|
|
108
|
-
const now = Date.now();
|
|
109
|
-
const cached = sessionCache.get(userSub);
|
|
110
|
-
if (cached && now - cached.fetchedAt < SESSION_CACHE_TTL_MS) {
|
|
111
|
-
return cached.activeWorkspaceId;
|
|
112
|
-
}
|
|
113
|
-
const result = await getDdb2().send(
|
|
114
|
-
new GetCommand2({
|
|
115
|
-
TableName: tableName(),
|
|
116
|
-
Key: { userSub }
|
|
117
|
-
})
|
|
118
|
-
);
|
|
119
|
-
const activeWorkspaceId = result.Item?.activeWorkspaceId ?? null;
|
|
120
|
-
sessionCache.set(userSub, { activeWorkspaceId, fetchedAt: now });
|
|
121
|
-
return activeWorkspaceId;
|
|
122
|
-
}
|
|
123
|
-
async function setSessionWorkspaceId(userSub, workspaceId) {
|
|
124
|
-
await getDdb2().send(
|
|
125
|
-
new PutCommand({
|
|
126
|
-
TableName: tableName(),
|
|
127
|
-
Item: {
|
|
128
|
-
userSub,
|
|
129
|
-
activeWorkspaceId: workspaceId,
|
|
130
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
131
|
-
}
|
|
132
|
-
})
|
|
133
|
-
);
|
|
134
|
-
sessionCache.set(userSub, {
|
|
135
|
-
activeWorkspaceId: workspaceId,
|
|
136
|
-
fetchedAt: Date.now()
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// src/middleware/workspace.ts
|
|
141
|
-
var MEMBERSHIP_CACHE_TTL_MS = 3e5;
|
|
142
|
-
var membershipCache = /* @__PURE__ */ new Map();
|
|
143
|
-
var ddbClient3 = null;
|
|
144
|
-
function getDdb3() {
|
|
145
|
-
if (!ddbClient3) {
|
|
146
|
-
ddbClient3 = DynamoDBDocumentClient3.from(
|
|
147
|
-
new DynamoDBClient3({ region: process.env["AWS_REGION"] })
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
return ddbClient3;
|
|
151
|
-
}
|
|
152
|
-
async function listWorkspaceMemberships(userSub) {
|
|
153
|
-
const now = Date.now();
|
|
154
|
-
const cached = membershipCache.get(userSub);
|
|
155
|
-
if (cached && now - cached.fetchedAt < MEMBERSHIP_CACHE_TTL_MS) {
|
|
156
|
-
return cached.workspaceIds;
|
|
157
|
-
}
|
|
158
|
-
const result = await getDdb3().send(
|
|
159
|
-
new QueryCommand({
|
|
160
|
-
TableName: process.env["MEMBERSHIPS_TABLE_NAME"],
|
|
161
|
-
IndexName: "ByUserSub",
|
|
162
|
-
KeyConditionExpression: "userSub = :sub",
|
|
163
|
-
ExpressionAttributeValues: { ":sub": userSub }
|
|
164
|
-
})
|
|
165
|
-
);
|
|
166
|
-
const workspaceIds = (result.Items ?? []).map(
|
|
167
|
-
(item) => item.workspaceId
|
|
168
|
-
);
|
|
169
|
-
membershipCache.set(userSub, { workspaceIds, fetchedAt: now });
|
|
170
|
-
return workspaceIds;
|
|
171
|
-
}
|
|
172
|
-
async function resolveWorkspaceId(userSub) {
|
|
173
|
-
const [sessionWsId, workspaceIds] = await Promise.all([
|
|
174
|
-
getSessionWorkspaceId(userSub),
|
|
175
|
-
listWorkspaceMemberships(userSub)
|
|
176
|
-
]);
|
|
177
|
-
if (workspaceIds.length === 0) return null;
|
|
178
|
-
if (sessionWsId && workspaceIds.includes(sessionWsId)) {
|
|
179
|
-
return sessionWsId;
|
|
180
|
-
}
|
|
181
|
-
return workspaceIds[0] ?? null;
|
|
182
|
-
}
|
|
183
|
-
var workspaceMiddleware = async (c, next) => {
|
|
184
|
-
const claims = c.get("claims");
|
|
185
|
-
const workspaceId = await resolveWorkspaceId(claims.sub);
|
|
186
|
-
if (!workspaceId) {
|
|
187
|
-
return c.json({ error: "No workspace membership" }, 403);
|
|
188
|
-
}
|
|
189
|
-
c.set("workspaceId", workspaceId);
|
|
190
|
-
return next();
|
|
191
|
-
};
|
|
126
|
+
// src/routes/health.ts
|
|
127
|
+
import { Hono } from "hono";
|
|
128
|
+
var health = new Hono();
|
|
129
|
+
health.get("/health", (c) => c.json({ status: "ok" }));
|
|
192
130
|
|
|
193
131
|
// src/routes/chat.ts
|
|
194
132
|
import { withTracing } from "@posthog/ai";
|
|
@@ -199,7 +137,7 @@ import {
|
|
|
199
137
|
stepCountIs,
|
|
200
138
|
jsonSchema
|
|
201
139
|
} from "ai";
|
|
202
|
-
import { Hono } from "hono";
|
|
140
|
+
import { Hono as Hono2 } from "hono";
|
|
203
141
|
import { z } from "zod";
|
|
204
142
|
|
|
205
143
|
// src/fallback-prompts.ts
|
|
@@ -553,14 +491,7 @@ var CHAT_PROMPT_ALLOWLIST = /* @__PURE__ */ new Set(["chat-patchwork-widget", "c
|
|
|
553
491
|
var EDIT_PROMPT_ID = "edit-patchwork-widget";
|
|
554
492
|
|
|
555
493
|
// src/gateway-session.ts
|
|
556
|
-
var
|
|
557
|
-
var toolsCache = /* @__PURE__ */ new Map();
|
|
558
|
-
function getCachedTools(sub) {
|
|
559
|
-
return toolsCache.get(sub);
|
|
560
|
-
}
|
|
561
|
-
function setCachedTools(sub, tools) {
|
|
562
|
-
toolsCache.set(sub, tools);
|
|
563
|
-
}
|
|
494
|
+
var sessionCache = /* @__PURE__ */ new Map();
|
|
564
495
|
function gatewayUrl() {
|
|
565
496
|
const url = process.env["GATEWAY_URL"];
|
|
566
497
|
if (!url) throw new Error("GATEWAY_URL is not set");
|
|
@@ -569,22 +500,16 @@ function gatewayUrl() {
|
|
|
569
500
|
async function getGatewaySession(claims, workspaceId, cognitoToken) {
|
|
570
501
|
const sub = claims.sub;
|
|
571
502
|
const now = Math.floor(Date.now() / 1e3);
|
|
572
|
-
const cached =
|
|
503
|
+
const cached = sessionCache.get(sub);
|
|
573
504
|
if (cached && cached.expires_at > now + 60) {
|
|
574
505
|
return cached;
|
|
575
506
|
}
|
|
576
507
|
const entry = await exchangeSession(cognitoToken, workspaceId, claims.exp);
|
|
577
|
-
|
|
508
|
+
sessionCache.set(sub, entry);
|
|
578
509
|
return entry;
|
|
579
510
|
}
|
|
580
511
|
async function exchangeSession(cognitoToken, workspaceId, exp) {
|
|
581
|
-
|
|
582
|
-
try {
|
|
583
|
-
res = await callAuthSessions(cognitoToken, workspaceId);
|
|
584
|
-
} catch {
|
|
585
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
586
|
-
res = await callAuthSessions(cognitoToken, workspaceId);
|
|
587
|
-
}
|
|
512
|
+
const res = await callAuthSessions(cognitoToken, workspaceId);
|
|
588
513
|
if (res.status === 401) {
|
|
589
514
|
const retryRes = await callAuthSessions(cognitoToken, workspaceId);
|
|
590
515
|
if (!retryRes.ok) {
|
|
@@ -592,14 +517,6 @@ async function exchangeSession(cognitoToken, workspaceId, exp) {
|
|
|
592
517
|
}
|
|
593
518
|
return { token: cognitoToken, expires_at: exp };
|
|
594
519
|
}
|
|
595
|
-
if (res.status >= 500) {
|
|
596
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
597
|
-
const retryRes = await callAuthSessions(cognitoToken, workspaceId);
|
|
598
|
-
if (!retryRes.ok) {
|
|
599
|
-
throw new GatewaySessionError(retryRes.status, await retryRes.text());
|
|
600
|
-
}
|
|
601
|
-
return { token: cognitoToken, expires_at: exp };
|
|
602
|
-
}
|
|
603
520
|
if (!res.ok) {
|
|
604
521
|
throw new GatewaySessionError(res.status, await res.text());
|
|
605
522
|
}
|
|
@@ -616,8 +533,7 @@ async function callAuthSessions(cognitoToken, workspaceId) {
|
|
|
616
533
|
});
|
|
617
534
|
}
|
|
618
535
|
function evictGatewaySession(sub) {
|
|
619
|
-
|
|
620
|
-
toolsCache.delete(sub);
|
|
536
|
+
sessionCache.delete(sub);
|
|
621
537
|
}
|
|
622
538
|
var GatewaySessionError = class extends Error {
|
|
623
539
|
constructor(status, message) {
|
|
@@ -758,7 +674,6 @@ var chatBodySchema = z.object({
|
|
|
758
674
|
id: z.string(),
|
|
759
675
|
messages: z.array(z.any()),
|
|
760
676
|
trigger: z.string(),
|
|
761
|
-
model: z.string().optional(),
|
|
762
677
|
metadata: z.unknown().optional(),
|
|
763
678
|
prompt: z.object({
|
|
764
679
|
id: z.string(),
|
|
@@ -778,7 +693,7 @@ var retryAtStartMiddleware = {
|
|
|
778
693
|
}
|
|
779
694
|
}
|
|
780
695
|
};
|
|
781
|
-
var chatRoute = new
|
|
696
|
+
var chatRoute = new Hono2();
|
|
782
697
|
async function fetchGatewayTools(gatewayUrl2, bearerToken) {
|
|
783
698
|
const res = await fetch(`${gatewayUrl2}/tools`, {
|
|
784
699
|
headers: { Authorization: `Bearer ${bearerToken}` }
|
|
@@ -824,16 +739,10 @@ chatRoute.post("/", async (c) => {
|
|
|
824
739
|
if (!parsed.success) {
|
|
825
740
|
return c.json({ error: "Invalid request body" }, 400);
|
|
826
741
|
}
|
|
827
|
-
const { messages,
|
|
742
|
+
const { messages, prompt: promptBody } = parsed.data;
|
|
828
743
|
const claims = c.get("claims");
|
|
829
744
|
const workspaceId = c.get("workspaceId");
|
|
830
745
|
const workspace = c.get("workspace");
|
|
831
|
-
if (requestedModel !== void 0 && !workspace.limits.maxModels.includes(requestedModel)) {
|
|
832
|
-
return c.json(
|
|
833
|
-
{ error: "Model not allowed on your current plan", model: requestedModel },
|
|
834
|
-
403
|
|
835
|
-
);
|
|
836
|
-
}
|
|
837
746
|
const promptId = promptBody?.id ?? "chat-patchwork-widget";
|
|
838
747
|
if (!CHAT_PROMPT_ALLOWLIST.has(promptId)) {
|
|
839
748
|
return c.json({ error: "Unknown prompt id" }, 400);
|
|
@@ -851,18 +760,11 @@ chatRoute.post("/", async (c) => {
|
|
|
851
760
|
}
|
|
852
761
|
let gatewayTools = [];
|
|
853
762
|
if (sessionToken && gatewayUrl2) {
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
() => []
|
|
860
|
-
);
|
|
861
|
-
if (gatewayTools.length === 0) {
|
|
862
|
-
evictGatewaySession(claims.sub);
|
|
863
|
-
} else {
|
|
864
|
-
setCachedTools(claims.sub, gatewayTools);
|
|
865
|
-
}
|
|
763
|
+
gatewayTools = await fetchGatewayTools(gatewayUrl2, sessionToken).catch(
|
|
764
|
+
() => []
|
|
765
|
+
);
|
|
766
|
+
if (gatewayTools.length === 0) {
|
|
767
|
+
evictGatewaySession(claims.sub);
|
|
866
768
|
}
|
|
867
769
|
}
|
|
868
770
|
const tools = sessionToken && gatewayUrl2 ? buildTools(gatewayTools, gatewayUrl2, sessionToken) : {};
|
|
@@ -878,7 +780,7 @@ chatRoute.post("/", async (c) => {
|
|
|
878
780
|
});
|
|
879
781
|
const apiKey = await getOpenRouterKey();
|
|
880
782
|
const provider = createOpenRouterProvider(apiKey);
|
|
881
|
-
const modelId =
|
|
783
|
+
const modelId = workspace.limits.maxModels[0] ?? "openrouter/auto";
|
|
882
784
|
const baseModel = provider(modelId);
|
|
883
785
|
const phClient = getPostHogClient();
|
|
884
786
|
const tracedModel = phClient && promptResult.source !== "code_fallback" ? withTracing(baseModel, phClient, {
|
|
@@ -906,14 +808,14 @@ chatRoute.post("/", async (c) => {
|
|
|
906
808
|
// src/routes/edit.ts
|
|
907
809
|
import { withTracing as withTracing2 } from "@posthog/ai";
|
|
908
810
|
import { streamText as streamText2 } from "ai";
|
|
909
|
-
import { Hono as
|
|
811
|
+
import { Hono as Hono3 } from "hono";
|
|
910
812
|
import { z as z2 } from "zod";
|
|
911
813
|
var editBodySchema = z2.object({
|
|
912
814
|
code: z2.string(),
|
|
913
815
|
prompt: z2.string()
|
|
914
816
|
});
|
|
915
817
|
var MODEL_ID = "openrouter/auto";
|
|
916
|
-
var editRoute = new
|
|
818
|
+
var editRoute = new Hono3();
|
|
917
819
|
editRoute.post("/", async (c) => {
|
|
918
820
|
const body = await c.req.json().catch(() => null);
|
|
919
821
|
const parsed = editBodySchema.safeParse(body);
|
|
@@ -943,19 +845,12 @@ editRoute.post("/", async (c) => {
|
|
|
943
845
|
return result.toTextStreamResponse();
|
|
944
846
|
});
|
|
945
847
|
|
|
946
|
-
// src/routes/
|
|
947
|
-
import { Hono as Hono3 } from "hono";
|
|
948
|
-
var health = new Hono3();
|
|
949
|
-
health.get("/health", (c) => c.json({ status: "ok" }));
|
|
950
|
-
|
|
951
|
-
// src/routes/proxy.ts
|
|
848
|
+
// src/routes/services.ts
|
|
952
849
|
import { Hono as Hono4 } from "hono";
|
|
953
|
-
var
|
|
954
|
-
|
|
850
|
+
var services = new Hono4();
|
|
851
|
+
services.get("/", async (c) => {
|
|
955
852
|
const claims = c.get("claims");
|
|
956
853
|
const workspaceId = c.get("workspaceId");
|
|
957
|
-
const ns = c.req.param("ns");
|
|
958
|
-
const proc = c.req.param("proc");
|
|
959
854
|
const authHeader = c.req.header("Authorization");
|
|
960
855
|
const cognitoToken = authHeader.slice("Bearer ".length);
|
|
961
856
|
let sessionToken;
|
|
@@ -968,34 +863,37 @@ proxy.post("/:ns/:proc{.*}", async (c) => {
|
|
|
968
863
|
}
|
|
969
864
|
return c.json({ error: "Failed to connect to gateway" }, 502);
|
|
970
865
|
}
|
|
971
|
-
let body = {};
|
|
972
|
-
try {
|
|
973
|
-
body = await c.req.json();
|
|
974
|
-
} catch {
|
|
975
|
-
}
|
|
976
866
|
const gatewayUrl2 = process.env["GATEWAY_URL"].replace(/\/$/, "");
|
|
977
|
-
const res = await fetch(`${gatewayUrl2}/tools
|
|
978
|
-
|
|
979
|
-
headers: {
|
|
980
|
-
"Content-Type": "application/json",
|
|
981
|
-
Authorization: `Bearer ${sessionToken}`
|
|
982
|
-
},
|
|
983
|
-
body: JSON.stringify(body)
|
|
867
|
+
const res = await fetch(`${gatewayUrl2}/tools`, {
|
|
868
|
+
headers: { Authorization: `Bearer ${sessionToken}` }
|
|
984
869
|
});
|
|
985
870
|
if (res.status === 401) {
|
|
986
871
|
evictGatewaySession(claims.sub);
|
|
987
872
|
return c.json({ error: "Gateway authentication failed" }, 502);
|
|
988
873
|
}
|
|
989
|
-
|
|
990
|
-
|
|
874
|
+
if (!res.ok) {
|
|
875
|
+
return c.json({ error: "Gateway tools fetch failed" }, 502);
|
|
876
|
+
}
|
|
877
|
+
const data = await res.json();
|
|
878
|
+
const serviceList = data.tools.map((t) => ({
|
|
879
|
+
namespace: t.provider,
|
|
880
|
+
name: t.name,
|
|
881
|
+
procedure: t.operation,
|
|
882
|
+
description: t.description ?? "",
|
|
883
|
+
parameters: t.inputSchema
|
|
884
|
+
}));
|
|
885
|
+
const namespaces = Array.from(new Set(data.tools.map((t) => t.provider)));
|
|
886
|
+
return c.json({ namespaces, services: serviceList });
|
|
991
887
|
});
|
|
992
888
|
|
|
993
|
-
// src/routes/
|
|
889
|
+
// src/routes/proxy.ts
|
|
994
890
|
import { Hono as Hono5 } from "hono";
|
|
995
|
-
var
|
|
996
|
-
|
|
891
|
+
var proxy = new Hono5();
|
|
892
|
+
proxy.post("/:ns/:proc{.*}", async (c) => {
|
|
997
893
|
const claims = c.get("claims");
|
|
998
894
|
const workspaceId = c.get("workspaceId");
|
|
895
|
+
const ns = c.req.param("ns");
|
|
896
|
+
const proc = c.req.param("proc");
|
|
999
897
|
const authHeader = c.req.header("Authorization");
|
|
1000
898
|
const cognitoToken = authHeader.slice("Bearer ".length);
|
|
1001
899
|
let sessionToken;
|
|
@@ -1008,99 +906,31 @@ services.get("/", async (c) => {
|
|
|
1008
906
|
}
|
|
1009
907
|
return c.json({ error: "Failed to connect to gateway" }, 502);
|
|
1010
908
|
}
|
|
909
|
+
let body = {};
|
|
910
|
+
try {
|
|
911
|
+
body = await c.req.json();
|
|
912
|
+
} catch {
|
|
913
|
+
}
|
|
1011
914
|
const gatewayUrl2 = process.env["GATEWAY_URL"].replace(/\/$/, "");
|
|
1012
|
-
const res = await fetch(`${gatewayUrl2}/tools`, {
|
|
1013
|
-
|
|
915
|
+
const res = await fetch(`${gatewayUrl2}/tools/${ns}/${proc}`, {
|
|
916
|
+
method: "POST",
|
|
917
|
+
headers: {
|
|
918
|
+
"Content-Type": "application/json",
|
|
919
|
+
Authorization: `Bearer ${sessionToken}`
|
|
920
|
+
},
|
|
921
|
+
body: JSON.stringify(body)
|
|
1014
922
|
});
|
|
1015
923
|
if (res.status === 401) {
|
|
1016
924
|
evictGatewaySession(claims.sub);
|
|
1017
925
|
return c.json({ error: "Gateway authentication failed" }, 502);
|
|
1018
926
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
}
|
|
1022
|
-
const data = await res.json();
|
|
1023
|
-
const serviceList = data.tools.map((t) => ({
|
|
1024
|
-
namespace: t.provider,
|
|
1025
|
-
name: t.name,
|
|
1026
|
-
procedure: t.operation,
|
|
1027
|
-
description: t.description ?? "",
|
|
1028
|
-
parameters: t.inputSchema
|
|
1029
|
-
}));
|
|
1030
|
-
const namespaces = Array.from(new Set(data.tools.map((t) => t.provider)));
|
|
1031
|
-
return c.json({ namespaces, services: serviceList });
|
|
1032
|
-
});
|
|
1033
|
-
|
|
1034
|
-
// src/routes/workspaces.ts
|
|
1035
|
-
import { DynamoDBClient as DynamoDBClient4 } from "@aws-sdk/client-dynamodb";
|
|
1036
|
-
import { BatchGetCommand, DynamoDBDocumentClient as DynamoDBDocumentClient4 } from "@aws-sdk/lib-dynamodb";
|
|
1037
|
-
import { zValidator } from "@hono/zod-validator";
|
|
1038
|
-
import { Hono as Hono6 } from "hono";
|
|
1039
|
-
import { z as z3 } from "zod";
|
|
1040
|
-
var ddbClient4 = null;
|
|
1041
|
-
function getDdb4() {
|
|
1042
|
-
if (!ddbClient4) {
|
|
1043
|
-
ddbClient4 = DynamoDBDocumentClient4.from(
|
|
1044
|
-
new DynamoDBClient4({ region: process.env["AWS_REGION"] })
|
|
1045
|
-
);
|
|
1046
|
-
}
|
|
1047
|
-
return ddbClient4;
|
|
1048
|
-
}
|
|
1049
|
-
async function batchGetWorkspaces(workspaceIds) {
|
|
1050
|
-
if (workspaceIds.length === 0) return [];
|
|
1051
|
-
const tableName2 = process.env["WORKSPACE_TABLE_NAME"];
|
|
1052
|
-
const result = await getDdb4().send(
|
|
1053
|
-
new BatchGetCommand({
|
|
1054
|
-
RequestItems: {
|
|
1055
|
-
[tableName2]: { Keys: workspaceIds.map((id) => ({ workspaceId: id })) }
|
|
1056
|
-
}
|
|
1057
|
-
})
|
|
1058
|
-
);
|
|
1059
|
-
return result.Responses?.[tableName2] ?? [];
|
|
1060
|
-
}
|
|
1061
|
-
var workspacesRoute = new Hono6();
|
|
1062
|
-
workspacesRoute.get("/", async (c) => {
|
|
1063
|
-
const claims = c.get("claims");
|
|
1064
|
-
const userSub = claims.sub;
|
|
1065
|
-
const [workspaceIds, activeWorkspaceId] = await Promise.all([
|
|
1066
|
-
listWorkspaceMemberships(userSub),
|
|
1067
|
-
getSessionWorkspaceId(userSub)
|
|
1068
|
-
]);
|
|
1069
|
-
const workspaces = await batchGetWorkspaces(workspaceIds);
|
|
1070
|
-
const effectiveActiveId = activeWorkspaceId && workspaceIds.includes(activeWorkspaceId) ? activeWorkspaceId : workspaceIds[0] ?? null;
|
|
1071
|
-
const sorted = workspaceIds.map((id) => workspaces.find((w) => w.workspaceId === id)).filter((w) => w !== void 0);
|
|
1072
|
-
return c.json({
|
|
1073
|
-
workspaces: sorted.map((w) => ({
|
|
1074
|
-
workspaceId: w.workspaceId,
|
|
1075
|
-
name: w.name,
|
|
1076
|
-
plan: w.plan,
|
|
1077
|
-
active: w.workspaceId === effectiveActiveId
|
|
1078
|
-
})),
|
|
1079
|
-
activeWorkspaceId: effectiveActiveId
|
|
1080
|
-
});
|
|
1081
|
-
});
|
|
1082
|
-
var setActiveSchema = z3.object({
|
|
1083
|
-
workspaceId: z3.string().min(1)
|
|
927
|
+
const responseData = await res.json();
|
|
928
|
+
return c.json(responseData, res.status);
|
|
1084
929
|
});
|
|
1085
|
-
workspacesRoute.put(
|
|
1086
|
-
"/active",
|
|
1087
|
-
zValidator("json", setActiveSchema),
|
|
1088
|
-
async (c) => {
|
|
1089
|
-
const claims = c.get("claims");
|
|
1090
|
-
const userSub = claims.sub;
|
|
1091
|
-
const { workspaceId } = c.req.valid("json");
|
|
1092
|
-
const workspaceIds = await listWorkspaceMemberships(userSub);
|
|
1093
|
-
if (!workspaceIds.includes(workspaceId)) {
|
|
1094
|
-
return c.json({ error: "Not a member of this workspace" }, 403);
|
|
1095
|
-
}
|
|
1096
|
-
await setSessionWorkspaceId(userSub, workspaceId);
|
|
1097
|
-
return c.json({ activeWorkspaceId: workspaceId });
|
|
1098
|
-
}
|
|
1099
|
-
);
|
|
1100
930
|
|
|
1101
931
|
// src/app.ts
|
|
1102
932
|
function createChatApp() {
|
|
1103
|
-
const app2 = new
|
|
933
|
+
const app2 = new Hono6();
|
|
1104
934
|
app2.route("/", health);
|
|
1105
935
|
const api = app2.basePath("/api");
|
|
1106
936
|
api.use(authMiddleware, workspaceMiddleware, planMiddleware);
|
|
@@ -1108,27 +938,25 @@ function createChatApp() {
|
|
|
1108
938
|
api.route("/edit", editRoute);
|
|
1109
939
|
api.route("/services", services);
|
|
1110
940
|
api.route("/proxy", proxy);
|
|
1111
|
-
api.route("/workspaces", workspacesRoute);
|
|
1112
941
|
return app2;
|
|
1113
942
|
}
|
|
1114
943
|
|
|
1115
944
|
// src/env.ts
|
|
1116
|
-
import { z as
|
|
1117
|
-
var envSchema =
|
|
1118
|
-
NODE_ENV:
|
|
1119
|
-
PORT:
|
|
1120
|
-
COGNITO_USER_POOL_ID:
|
|
1121
|
-
COGNITO_CLIENT_ID:
|
|
1122
|
-
AWS_REGION:
|
|
1123
|
-
WORKSPACE_TABLE_NAME:
|
|
1124
|
-
MEMBERSHIPS_TABLE_NAME:
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
GATEWAY_URL: z4.string().url(),
|
|
945
|
+
import { z as z3 } from "zod";
|
|
946
|
+
var envSchema = z3.object({
|
|
947
|
+
NODE_ENV: z3.enum(["development", "production", "test"]).default("development"),
|
|
948
|
+
PORT: z3.coerce.number().default(3001),
|
|
949
|
+
COGNITO_USER_POOL_ID: z3.string().min(1),
|
|
950
|
+
COGNITO_CLIENT_ID: z3.string().min(1),
|
|
951
|
+
AWS_REGION: z3.string().default("us-east-1"),
|
|
952
|
+
WORKSPACE_TABLE_NAME: z3.string().min(1),
|
|
953
|
+
MEMBERSHIPS_TABLE_NAME: z3.string().min(1),
|
|
954
|
+
OPENROUTER_SECRET_ARN: z3.string().min(1),
|
|
955
|
+
GATEWAY_URL: z3.string().url(),
|
|
1128
956
|
// PostHog prompt management (optional — falls back to code prompts when absent)
|
|
1129
|
-
POSTHOG_PROJECT_API_KEY:
|
|
1130
|
-
POSTHOG_PERSONAL_API_KEY:
|
|
1131
|
-
POSTHOG_HOST:
|
|
957
|
+
POSTHOG_PROJECT_API_KEY: z3.string().optional(),
|
|
958
|
+
POSTHOG_PERSONAL_API_KEY: z3.string().optional(),
|
|
959
|
+
POSTHOG_HOST: z3.string().default("https://us.posthog.com")
|
|
1132
960
|
});
|
|
1133
961
|
function parseEnv(raw) {
|
|
1134
962
|
return envSchema.parse(raw);
|