@standardagents/builder 0.10.1-next.bbd142a → 0.11.0-next.99fb790
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/LICENSE.txt +48 -0
- package/dist/built-in-routes.js +361 -84
- package/dist/built-in-routes.js.map +1 -1
- package/dist/client/assets/index.css +1 -1
- package/dist/client/index.js +25 -19
- package/dist/client/vendor.js +1 -1
- package/dist/client/vue.js +1 -1
- package/dist/image-processing.d.ts +10 -6
- package/dist/image-processing.js +11 -138
- package/dist/image-processing.js.map +1 -1
- package/dist/index.d.ts +441 -802
- package/dist/index.js +4901 -3979
- package/dist/index.js.map +1 -1
- package/dist/plugin.d.ts +1 -0
- package/dist/plugin.js +115 -72
- package/dist/plugin.js.map +1 -1
- package/package.json +21 -22
package/dist/built-in-routes.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import '@standardagents/spec';
|
|
1
2
|
import { z } from 'zod';
|
|
2
3
|
|
|
3
4
|
// ../../node_modules/.pnpm/rou3@0.7.10/node_modules/rou3/dist/index.mjs
|
|
@@ -77,8 +78,6 @@ function getParamRegexp(segment) {
|
|
|
77
78
|
const regex = segment.replace(/:(\w+)/g, (_, id) => `(?<${id}>[^/]+)`).replace(/\./g, "\\.");
|
|
78
79
|
return /* @__PURE__ */ new RegExp(`^${regex}$`);
|
|
79
80
|
}
|
|
80
|
-
|
|
81
|
-
// src/router/index.ts
|
|
82
81
|
function defineController(controller) {
|
|
83
82
|
return controller;
|
|
84
83
|
}
|
|
@@ -145,8 +144,8 @@ var agents_get_default = defineController(async ({ agents, agentNames, prompts,
|
|
|
145
144
|
side_a_stop_on_response: definition.sideA?.stopOnResponse !== void 0 ? definition.sideA.stopOnResponse : true,
|
|
146
145
|
side_a_stop_tool: definition.sideA?.stopTool || null,
|
|
147
146
|
side_a_stop_tool_response_property: definition.sideA?.stopToolResponseProperty || null,
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
side_a_max_steps: definition.sideA?.maxSteps || null,
|
|
148
|
+
side_a_end_session_tool: definition.sideA?.endSessionTool || null,
|
|
150
149
|
side_a_manual_stop_condition: definition.sideA?.manualStopCondition || false,
|
|
151
150
|
// Side B configuration (if dual_ai)
|
|
152
151
|
side_b_label: definition.sideB?.label || null,
|
|
@@ -155,16 +154,14 @@ var agents_get_default = defineController(async ({ agents, agentNames, prompts,
|
|
|
155
154
|
side_b_stop_on_response: definition.sideB?.stopOnResponse !== void 0 ? definition.sideB.stopOnResponse : true,
|
|
156
155
|
side_b_stop_tool: definition.sideB?.stopTool || null,
|
|
157
156
|
side_b_stop_tool_response_property: definition.sideB?.stopToolResponseProperty || null,
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
side_b_max_steps: definition.sideB?.maxSteps || null,
|
|
158
|
+
side_b_end_session_tool: definition.sideB?.endSessionTool || null,
|
|
160
159
|
side_b_manual_stop_condition: definition.sideB?.manualStopCondition || false,
|
|
161
160
|
// Session configuration
|
|
162
161
|
max_session_turns: definition.maxSessionTurns || null,
|
|
163
162
|
// Tool exposure
|
|
164
163
|
expose_as_tool: definition.exposeAsTool || false,
|
|
165
164
|
tool_description: definition.toolDescription || null,
|
|
166
|
-
// Tags
|
|
167
|
-
tags: definition.tags || [],
|
|
168
165
|
created_at: Math.floor(Date.now() / 1e3)
|
|
169
166
|
};
|
|
170
167
|
} catch (error) {
|
|
@@ -733,10 +730,7 @@ var prompts_get_default = defineController(async ({ prompts, promptNames, models
|
|
|
733
730
|
include_past_tools: definition.includePastTools || false,
|
|
734
731
|
parallel_tool_calls: definition.parallelToolCalls || false,
|
|
735
732
|
tool_choice: definition.toolChoice || "auto",
|
|
736
|
-
before_tool: definition.beforeTool || null,
|
|
737
|
-
after_tool: definition.afterTool || null,
|
|
738
733
|
tools,
|
|
739
|
-
prompts: definition.handoffAgents || [],
|
|
740
734
|
reasoning: definition.reasoning || null,
|
|
741
735
|
created_at: Math.floor(Date.now() / 1e3)
|
|
742
736
|
};
|
|
@@ -777,6 +771,11 @@ var providers_get_default = defineController(async ({ env }) => {
|
|
|
777
771
|
|
|
778
772
|
// src/api/threads/index.post.ts
|
|
779
773
|
var index_post_default3 = defineController(async ({ req, env }) => {
|
|
774
|
+
const authResult = await requireAuth(req, env);
|
|
775
|
+
if (authResult instanceof Response) {
|
|
776
|
+
return authResult;
|
|
777
|
+
}
|
|
778
|
+
const auth = authResult;
|
|
780
779
|
let body;
|
|
781
780
|
try {
|
|
782
781
|
body = await req.json();
|
|
@@ -786,7 +785,8 @@ var index_post_default3 = defineController(async ({ req, env }) => {
|
|
|
786
785
|
{ status: 400 }
|
|
787
786
|
);
|
|
788
787
|
}
|
|
789
|
-
const { agent_id,
|
|
788
|
+
const { agent_id, initial_messages, data, tags } = body;
|
|
789
|
+
const user_id = auth.authType === "super_admin" ? null : auth.user.id;
|
|
790
790
|
if (!agent_id) {
|
|
791
791
|
return Response.json(
|
|
792
792
|
{ error: "Missing required field: agent_id" },
|
|
@@ -828,7 +828,7 @@ var index_post_default3 = defineController(async ({ req, env }) => {
|
|
|
828
828
|
try {
|
|
829
829
|
const thread = await agentBuilder.createThread({
|
|
830
830
|
agent_name: agent_id,
|
|
831
|
-
user_id,
|
|
831
|
+
user_id: user_id ?? void 0,
|
|
832
832
|
tags
|
|
833
833
|
});
|
|
834
834
|
return Response.json({
|
|
@@ -848,16 +848,32 @@ var index_post_default3 = defineController(async ({ req, env }) => {
|
|
|
848
848
|
});
|
|
849
849
|
|
|
850
850
|
// src/api/threads/index.ts
|
|
851
|
+
function resolveIconUrl(icon, origin) {
|
|
852
|
+
if (!icon) return void 0;
|
|
853
|
+
if (icon.startsWith("http://") || icon.startsWith("https://")) {
|
|
854
|
+
return icon;
|
|
855
|
+
}
|
|
856
|
+
if (icon.startsWith("/")) {
|
|
857
|
+
return `${origin}${icon}`;
|
|
858
|
+
}
|
|
859
|
+
return icon;
|
|
860
|
+
}
|
|
851
861
|
var threads_default = defineController(async ({ req, env }) => {
|
|
852
862
|
if (req.method !== "GET") {
|
|
853
863
|
return new Response("Method Not Allowed", { status: 405 });
|
|
854
864
|
}
|
|
865
|
+
const authResult = await requireAuth(req, env);
|
|
866
|
+
if (authResult instanceof Response) {
|
|
867
|
+
return authResult;
|
|
868
|
+
}
|
|
869
|
+
const auth = authResult;
|
|
855
870
|
try {
|
|
856
871
|
const url = new URL(req.url);
|
|
872
|
+
const origin = url.origin;
|
|
857
873
|
const limit = parseInt(url.searchParams.get("limit") || "50", 10);
|
|
858
874
|
const offset = parseInt(url.searchParams.get("offset") || "0", 10);
|
|
859
875
|
const agentName = url.searchParams.get("agent_id");
|
|
860
|
-
const userId =
|
|
876
|
+
const userId = auth.user.role === "admin" ? void 0 : auth.user.id;
|
|
861
877
|
const agentBuilderId = env.AGENT_BUILDER.idFromName("singleton");
|
|
862
878
|
const agentBuilder = env.AGENT_BUILDER.get(agentBuilderId);
|
|
863
879
|
const result = await agentBuilder.listThreads({
|
|
@@ -872,11 +888,14 @@ var threads_default = defineController(async ({ req, env }) => {
|
|
|
872
888
|
try {
|
|
873
889
|
const agentDef = await agentBuilder.loadAgent(thread.agent_name);
|
|
874
890
|
agentDetails = {
|
|
875
|
-
|
|
876
|
-
|
|
891
|
+
name: thread.agent_name,
|
|
892
|
+
title: agentDef.title || thread.agent_name,
|
|
893
|
+
type: agentDef.type || "ai_human",
|
|
894
|
+
description: agentDef.description,
|
|
895
|
+
icon: resolveIconUrl(agentDef.icon, origin)
|
|
877
896
|
};
|
|
878
897
|
} catch {
|
|
879
|
-
agentDetails = { title: thread.agent_name, type: "unknown" };
|
|
898
|
+
agentDetails = { name: thread.agent_name, title: thread.agent_name, type: "unknown" };
|
|
880
899
|
}
|
|
881
900
|
return {
|
|
882
901
|
id: thread.id,
|
|
@@ -1065,6 +1084,7 @@ var index_get_default2 = defineController(async ({ req, env }) => {
|
|
|
1065
1084
|
});
|
|
1066
1085
|
|
|
1067
1086
|
// src/api/users/index.post.ts
|
|
1087
|
+
var VALID_ROLES = ["admin", "user"];
|
|
1068
1088
|
var index_post_default4 = defineController(async ({ req, env }) => {
|
|
1069
1089
|
try {
|
|
1070
1090
|
const authResult = await requireAdmin(req, env);
|
|
@@ -1072,7 +1092,7 @@ var index_post_default4 = defineController(async ({ req, env }) => {
|
|
|
1072
1092
|
return authResult;
|
|
1073
1093
|
}
|
|
1074
1094
|
const body = await req.json();
|
|
1075
|
-
const { username, password, role = "
|
|
1095
|
+
const { username, password, role = "user" } = body;
|
|
1076
1096
|
if (!username || !password) {
|
|
1077
1097
|
return Response.json(
|
|
1078
1098
|
{ error: "Username and password are required" },
|
|
@@ -1097,6 +1117,12 @@ var index_post_default4 = defineController(async ({ req, env }) => {
|
|
|
1097
1117
|
{ status: 400 }
|
|
1098
1118
|
);
|
|
1099
1119
|
}
|
|
1120
|
+
if (!VALID_ROLES.includes(role)) {
|
|
1121
|
+
return Response.json(
|
|
1122
|
+
{ error: `Invalid role. Must be one of: ${VALID_ROLES.join(", ")}` },
|
|
1123
|
+
{ status: 400 }
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1100
1126
|
const agentBuilderId = env.AGENT_BUILDER.idFromName("singleton");
|
|
1101
1127
|
const agentBuilder = env.AGENT_BUILDER.get(agentBuilderId);
|
|
1102
1128
|
const existingUser = await agentBuilder.getUserByUsername(username);
|
|
@@ -1168,8 +1194,8 @@ var name_get_default = defineController(async ({ params, agents, prompts, prompt
|
|
|
1168
1194
|
side_a_stop_on_response: definition.sideA?.stopOnResponse !== void 0 ? definition.sideA.stopOnResponse : true,
|
|
1169
1195
|
side_a_stop_tool: definition.sideA?.stopTool || null,
|
|
1170
1196
|
side_a_stop_tool_response_property: definition.sideA?.stopToolResponseProperty || null,
|
|
1171
|
-
|
|
1172
|
-
|
|
1197
|
+
side_a_max_steps: definition.sideA?.maxSteps || null,
|
|
1198
|
+
side_a_end_session_tool: definition.sideA?.endSessionTool || null,
|
|
1173
1199
|
side_a_manual_stop_condition: definition.sideA?.manualStopCondition || false,
|
|
1174
1200
|
// Side B configuration (if dual_ai)
|
|
1175
1201
|
side_b_label: definition.sideB?.label || null,
|
|
@@ -1178,16 +1204,14 @@ var name_get_default = defineController(async ({ params, agents, prompts, prompt
|
|
|
1178
1204
|
side_b_stop_on_response: definition.sideB?.stopOnResponse !== void 0 ? definition.sideB.stopOnResponse : true,
|
|
1179
1205
|
side_b_stop_tool: definition.sideB?.stopTool || null,
|
|
1180
1206
|
side_b_stop_tool_response_property: definition.sideB?.stopToolResponseProperty || null,
|
|
1181
|
-
|
|
1182
|
-
|
|
1207
|
+
side_b_max_steps: definition.sideB?.maxSteps || null,
|
|
1208
|
+
side_b_end_session_tool: definition.sideB?.endSessionTool || null,
|
|
1183
1209
|
side_b_manual_stop_condition: definition.sideB?.manualStopCondition || false,
|
|
1184
1210
|
// Session configuration
|
|
1185
1211
|
max_session_turns: definition.maxSessionTurns || null,
|
|
1186
1212
|
// Tool exposure
|
|
1187
1213
|
expose_as_tool: definition.exposeAsTool || false,
|
|
1188
1214
|
tool_description: definition.toolDescription || null,
|
|
1189
|
-
// Tags
|
|
1190
|
-
tags: definition.tags || [],
|
|
1191
1215
|
created_at: Math.floor(Date.now() / 1e3)
|
|
1192
1216
|
};
|
|
1193
1217
|
return Response.json({ agent });
|
|
@@ -1300,10 +1324,7 @@ var name_get_default2 = defineController(async ({ params, prompts, models, model
|
|
|
1300
1324
|
include_past_tools: definition.includePastTools || false,
|
|
1301
1325
|
parallel_tool_calls: definition.parallelToolCalls || false,
|
|
1302
1326
|
tool_choice: definition.toolChoice || "auto",
|
|
1303
|
-
before_tool: definition.beforeTool || null,
|
|
1304
|
-
after_tool: definition.afterTool || null,
|
|
1305
1327
|
tools,
|
|
1306
|
-
prompts: definition.handoffAgents || [],
|
|
1307
1328
|
reasoning: definition.reasoning || null,
|
|
1308
1329
|
created_at: Math.floor(Date.now() / 1e3)
|
|
1309
1330
|
};
|
|
@@ -1317,8 +1338,24 @@ var name_get_default2 = defineController(async ({ params, prompts, models, model
|
|
|
1317
1338
|
}
|
|
1318
1339
|
});
|
|
1319
1340
|
|
|
1341
|
+
// src/utils/permissions.ts
|
|
1342
|
+
function canAccessThread(auth, thread) {
|
|
1343
|
+
if (auth.user.role === "admin") {
|
|
1344
|
+
return true;
|
|
1345
|
+
}
|
|
1346
|
+
if (thread.user_id === null) {
|
|
1347
|
+
return false;
|
|
1348
|
+
}
|
|
1349
|
+
return thread.user_id === auth.user.id;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1320
1352
|
// src/api/threads/[id].delete.ts
|
|
1321
1353
|
var id_delete_default2 = defineController(async ({ req, env, params }) => {
|
|
1354
|
+
const authResult = await requireAuth(req, env);
|
|
1355
|
+
if (authResult instanceof Response) {
|
|
1356
|
+
return authResult;
|
|
1357
|
+
}
|
|
1358
|
+
const auth = authResult;
|
|
1322
1359
|
const threadId = params.id;
|
|
1323
1360
|
if (!threadId) {
|
|
1324
1361
|
return Response.json({ error: "Missing thread ID" }, { status: 400 });
|
|
@@ -1332,6 +1369,12 @@ var id_delete_default2 = defineController(async ({ req, env, params }) => {
|
|
|
1332
1369
|
{ status: 404 }
|
|
1333
1370
|
);
|
|
1334
1371
|
}
|
|
1372
|
+
if (!canAccessThread(auth, thread)) {
|
|
1373
|
+
return Response.json(
|
|
1374
|
+
{ error: "Forbidden: You don't have access to this thread" },
|
|
1375
|
+
{ status: 403 }
|
|
1376
|
+
);
|
|
1377
|
+
}
|
|
1335
1378
|
try {
|
|
1336
1379
|
await agentBuilder.deleteThread(threadId);
|
|
1337
1380
|
const durableId = env.AGENT_BUILDER_THREAD.idFromName(threadId);
|
|
@@ -1359,6 +1402,11 @@ var id_patch_default = defineController(async ({ req, env, params }) => {
|
|
|
1359
1402
|
if (req.method !== "PATCH") {
|
|
1360
1403
|
return new Response("Method Not Allowed", { status: 405 });
|
|
1361
1404
|
}
|
|
1405
|
+
const authResult = await requireAuth(req, env);
|
|
1406
|
+
if (authResult instanceof Response) {
|
|
1407
|
+
return authResult;
|
|
1408
|
+
}
|
|
1409
|
+
const auth = authResult;
|
|
1362
1410
|
const threadId = params?.id;
|
|
1363
1411
|
if (!threadId) {
|
|
1364
1412
|
return Response.json({ error: "Thread ID required" }, { status: 400 });
|
|
@@ -1375,6 +1423,12 @@ var id_patch_default = defineController(async ({ req, env, params }) => {
|
|
|
1375
1423
|
{ status: 404 }
|
|
1376
1424
|
);
|
|
1377
1425
|
}
|
|
1426
|
+
if (!canAccessThread(auth, existingThread)) {
|
|
1427
|
+
return Response.json(
|
|
1428
|
+
{ error: "Forbidden: You don't have access to this thread" },
|
|
1429
|
+
{ status: 403 }
|
|
1430
|
+
);
|
|
1431
|
+
}
|
|
1378
1432
|
const updates = {};
|
|
1379
1433
|
if (tags !== void 0) {
|
|
1380
1434
|
if (!Array.isArray(tags)) {
|
|
@@ -1447,12 +1501,42 @@ var id_patch_default = defineController(async ({ req, env, params }) => {
|
|
|
1447
1501
|
});
|
|
1448
1502
|
|
|
1449
1503
|
// src/api/threads/[id].ts
|
|
1504
|
+
function resolveIconUrl2(icon, origin) {
|
|
1505
|
+
if (!icon) return void 0;
|
|
1506
|
+
if (icon.startsWith("http://") || icon.startsWith("https://")) {
|
|
1507
|
+
return icon;
|
|
1508
|
+
}
|
|
1509
|
+
if (icon.startsWith("/")) {
|
|
1510
|
+
return `${origin}${icon}`;
|
|
1511
|
+
}
|
|
1512
|
+
return icon;
|
|
1513
|
+
}
|
|
1450
1514
|
var id_default = defineController(async ({ req, params, env }) => {
|
|
1515
|
+
const authResult = await requireAuth(req, env);
|
|
1516
|
+
if (authResult instanceof Response) {
|
|
1517
|
+
return authResult;
|
|
1518
|
+
}
|
|
1519
|
+
const auth = authResult;
|
|
1451
1520
|
const threadId = params.id;
|
|
1452
1521
|
if (!threadId) {
|
|
1453
1522
|
return Response.json({ error: "Thread ID required" }, { status: 400 });
|
|
1454
1523
|
}
|
|
1455
1524
|
try {
|
|
1525
|
+
const agentBuilderId = env.AGENT_BUILDER.idFromName("singleton");
|
|
1526
|
+
const agentBuilder = env.AGENT_BUILDER.get(agentBuilderId);
|
|
1527
|
+
const thread = await agentBuilder.getThread(threadId);
|
|
1528
|
+
if (!thread) {
|
|
1529
|
+
return Response.json(
|
|
1530
|
+
{ error: `Thread not found: ${threadId}` },
|
|
1531
|
+
{ status: 404 }
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1534
|
+
if (!canAccessThread(auth, thread)) {
|
|
1535
|
+
return Response.json(
|
|
1536
|
+
{ error: "Forbidden: You don't have access to this thread" },
|
|
1537
|
+
{ status: 403 }
|
|
1538
|
+
);
|
|
1539
|
+
}
|
|
1456
1540
|
const durableId = env.AGENT_BUILDER_THREAD.idFromName(threadId);
|
|
1457
1541
|
const stub = env.AGENT_BUILDER_THREAD.get(durableId);
|
|
1458
1542
|
const doData = await stub.getThreadMeta(threadId);
|
|
@@ -1462,6 +1546,10 @@ var id_default = defineController(async ({ req, params, env }) => {
|
|
|
1462
1546
|
{ status: 404 }
|
|
1463
1547
|
);
|
|
1464
1548
|
}
|
|
1549
|
+
if (doData.agent?.icon) {
|
|
1550
|
+
const origin = new URL(req.url).origin;
|
|
1551
|
+
doData.agent.icon = resolveIconUrl2(doData.agent.icon, origin);
|
|
1552
|
+
}
|
|
1465
1553
|
return Response.json(doData);
|
|
1466
1554
|
} catch (error) {
|
|
1467
1555
|
console.error(`Error fetching thread ${threadId}:`, error);
|
|
@@ -1624,7 +1712,7 @@ var login_post_default = defineController(async ({ req, env }) => {
|
|
|
1624
1712
|
{ status: 400 }
|
|
1625
1713
|
);
|
|
1626
1714
|
}
|
|
1627
|
-
if (env.SUPER_ADMIN_PASSWORD && password === env.SUPER_ADMIN_PASSWORD) {
|
|
1715
|
+
if (env.SUPER_ADMIN_PASSWORD && username === "admin" && password === env.SUPER_ADMIN_PASSWORD) {
|
|
1628
1716
|
if (!env.ENCRYPTION_KEY) {
|
|
1629
1717
|
return Response.json(
|
|
1630
1718
|
{ error: "Server misconfigured: ENCRYPTION_KEY required for super admin" },
|
|
@@ -2077,7 +2165,48 @@ var logs_get_default = defineController(async ({ req, params, env }) => {
|
|
|
2077
2165
|
}
|
|
2078
2166
|
});
|
|
2079
2167
|
|
|
2080
|
-
// src/api/threads/[id]/
|
|
2168
|
+
// src/api/threads/[id]/messages.get.ts
|
|
2169
|
+
var messages_get_default = defineController(async ({ req, params, env }) => {
|
|
2170
|
+
const url = new URL(req.url);
|
|
2171
|
+
const threadId = params.id;
|
|
2172
|
+
if (!threadId) {
|
|
2173
|
+
return Response.json({ error: "Thread ID required" }, { status: 400 });
|
|
2174
|
+
}
|
|
2175
|
+
try {
|
|
2176
|
+
const agentBuilderId = env.AGENT_BUILDER.idFromName("singleton");
|
|
2177
|
+
const agentBuilder = env.AGENT_BUILDER.get(agentBuilderId);
|
|
2178
|
+
const thread = await agentBuilder.getThread(threadId);
|
|
2179
|
+
if (!thread) {
|
|
2180
|
+
return Response.json(
|
|
2181
|
+
{ error: `Thread not found: ${threadId}` },
|
|
2182
|
+
{ status: 404 }
|
|
2183
|
+
);
|
|
2184
|
+
}
|
|
2185
|
+
const limit = parseInt(url.searchParams.get("limit") || "100", 10);
|
|
2186
|
+
const offset = parseInt(url.searchParams.get("offset") || "0", 10);
|
|
2187
|
+
const order = url.searchParams.get("order") === "asc" ? "ASC" : "DESC";
|
|
2188
|
+
const includeSilent = url.searchParams.get("includeSilent") === "true";
|
|
2189
|
+
const maxDepth = parseInt(url.searchParams.get("depth") || "0", 10);
|
|
2190
|
+
const durableId = env.AGENT_BUILDER_THREAD.idFromName(threadId);
|
|
2191
|
+
const stub = env.AGENT_BUILDER_THREAD.get(durableId);
|
|
2192
|
+
const result = await stub.getMessages(
|
|
2193
|
+
limit,
|
|
2194
|
+
offset,
|
|
2195
|
+
order,
|
|
2196
|
+
includeSilent,
|
|
2197
|
+
maxDepth
|
|
2198
|
+
);
|
|
2199
|
+
return Response.json(result);
|
|
2200
|
+
} catch (error) {
|
|
2201
|
+
console.error(`Error fetching messages for thread ${threadId}:`, error);
|
|
2202
|
+
return Response.json(
|
|
2203
|
+
{ error: error.message || "Failed to fetch messages" },
|
|
2204
|
+
{ status: 500 }
|
|
2205
|
+
);
|
|
2206
|
+
}
|
|
2207
|
+
});
|
|
2208
|
+
|
|
2209
|
+
// src/api/threads/[id]/messages.post.ts
|
|
2081
2210
|
function needsProcessing(base64Data, mimeType) {
|
|
2082
2211
|
const binarySize = Math.ceil(base64Data.length * 3 / 4);
|
|
2083
2212
|
const MAX_SIZE = 2 * 1024 * 1024;
|
|
@@ -2089,7 +2218,7 @@ function needsProcessing(base64Data, mimeType) {
|
|
|
2089
2218
|
}
|
|
2090
2219
|
return false;
|
|
2091
2220
|
}
|
|
2092
|
-
var
|
|
2221
|
+
var messages_post_default = defineController(async ({ req, params, env }) => {
|
|
2093
2222
|
const threadId = params.id;
|
|
2094
2223
|
if (!threadId) {
|
|
2095
2224
|
return Response.json({ error: "Thread ID required" }, { status: 400 });
|
|
@@ -2148,9 +2277,6 @@ var message_post_default = defineController(async ({ req, params, env }) => {
|
|
|
2148
2277
|
}
|
|
2149
2278
|
const ext = mimeType === "image/png" ? "png" : mimeType === "image/jpeg" ? "jpg" : attachment.name.split(".").pop() || "bin";
|
|
2150
2279
|
const path = `/attachments/${timestamp}-${attachmentId}.${ext}`;
|
|
2151
|
-
const metadata = {};
|
|
2152
|
-
if (width) metadata.width = width;
|
|
2153
|
-
if (height) metadata.height = height;
|
|
2154
2280
|
const base64Size = fileData.length;
|
|
2155
2281
|
const binarySize = Math.ceil(base64Size * 3 / 4);
|
|
2156
2282
|
console.log(`[writeFile] Storing ${attachment.name}: ${(binarySize / 1024 / 1024).toFixed(2)}MB binary (${(base64Size / 1024 / 1024).toFixed(2)}MB base64) to ${path}`);
|
|
@@ -2159,8 +2285,9 @@ var message_post_default = defineController(async ({ req, params, env }) => {
|
|
|
2159
2285
|
fileData,
|
|
2160
2286
|
mimeType,
|
|
2161
2287
|
{
|
|
2162
|
-
|
|
2163
|
-
|
|
2288
|
+
thumbnail: attachment.thumbnail,
|
|
2289
|
+
width,
|
|
2290
|
+
height
|
|
2164
2291
|
}
|
|
2165
2292
|
);
|
|
2166
2293
|
console.log(`[writeFile] Result:`, result.success ? "success" : result.error);
|
|
@@ -2191,47 +2318,6 @@ var message_post_default = defineController(async ({ req, params, env }) => {
|
|
|
2191
2318
|
}
|
|
2192
2319
|
});
|
|
2193
2320
|
|
|
2194
|
-
// src/api/threads/[id]/messages.get.ts
|
|
2195
|
-
var messages_get_default = defineController(async ({ req, params, env }) => {
|
|
2196
|
-
const url = new URL(req.url);
|
|
2197
|
-
const threadId = params.id;
|
|
2198
|
-
if (!threadId) {
|
|
2199
|
-
return Response.json({ error: "Thread ID required" }, { status: 400 });
|
|
2200
|
-
}
|
|
2201
|
-
try {
|
|
2202
|
-
const agentBuilderId = env.AGENT_BUILDER.idFromName("singleton");
|
|
2203
|
-
const agentBuilder = env.AGENT_BUILDER.get(agentBuilderId);
|
|
2204
|
-
const thread = await agentBuilder.getThread(threadId);
|
|
2205
|
-
if (!thread) {
|
|
2206
|
-
return Response.json(
|
|
2207
|
-
{ error: `Thread not found: ${threadId}` },
|
|
2208
|
-
{ status: 404 }
|
|
2209
|
-
);
|
|
2210
|
-
}
|
|
2211
|
-
const limit = parseInt(url.searchParams.get("limit") || "100", 10);
|
|
2212
|
-
const offset = parseInt(url.searchParams.get("offset") || "0", 10);
|
|
2213
|
-
const order = url.searchParams.get("order") === "asc" ? "ASC" : "DESC";
|
|
2214
|
-
const includeSilent = url.searchParams.get("includeSilent") === "true";
|
|
2215
|
-
const maxDepth = parseInt(url.searchParams.get("depth") || "0", 10);
|
|
2216
|
-
const durableId = env.AGENT_BUILDER_THREAD.idFromName(threadId);
|
|
2217
|
-
const stub = env.AGENT_BUILDER_THREAD.get(durableId);
|
|
2218
|
-
const result = await stub.getMessages(
|
|
2219
|
-
limit,
|
|
2220
|
-
offset,
|
|
2221
|
-
order,
|
|
2222
|
-
includeSilent,
|
|
2223
|
-
maxDepth
|
|
2224
|
-
);
|
|
2225
|
-
return Response.json(result);
|
|
2226
|
-
} catch (error) {
|
|
2227
|
-
console.error(`Error fetching messages for thread ${threadId}:`, error);
|
|
2228
|
-
return Response.json(
|
|
2229
|
-
{ error: error.message || "Failed to fetch messages" },
|
|
2230
|
-
{ status: 500 }
|
|
2231
|
-
);
|
|
2232
|
-
}
|
|
2233
|
-
});
|
|
2234
|
-
|
|
2235
2321
|
// src/api/threads/[id]/rpc.post.ts
|
|
2236
2322
|
var rpc_post_default = defineController(async ({ req, params, env }) => {
|
|
2237
2323
|
const threadId = params.id;
|
|
@@ -2323,6 +2409,23 @@ var stream_default = defineController(async ({ req, params, env }) => {
|
|
|
2323
2409
|
});
|
|
2324
2410
|
|
|
2325
2411
|
// src/api/threads/[id]/fs/[...path].ts
|
|
2412
|
+
var CHUNK_SIZE = 1.75 * 1024 * 1024;
|
|
2413
|
+
function toAttachmentRef(file) {
|
|
2414
|
+
const metadata = file.metadata;
|
|
2415
|
+
const width = file.width ?? metadata?.width;
|
|
2416
|
+
const height = file.height ?? metadata?.height;
|
|
2417
|
+
return {
|
|
2418
|
+
id: file.path,
|
|
2419
|
+
// Use path as unique ID
|
|
2420
|
+
type: "file",
|
|
2421
|
+
path: file.path,
|
|
2422
|
+
name: file.name,
|
|
2423
|
+
mimeType: file.mimeType,
|
|
2424
|
+
size: file.size,
|
|
2425
|
+
...width && { width },
|
|
2426
|
+
...height && { height }
|
|
2427
|
+
};
|
|
2428
|
+
}
|
|
2326
2429
|
var path_default = defineController(async ({ req, params, env }) => {
|
|
2327
2430
|
console.log("[fs] params received:", JSON.stringify(params));
|
|
2328
2431
|
const threadId = params.id;
|
|
@@ -2336,11 +2439,30 @@ var path_default = defineController(async ({ req, params, env }) => {
|
|
|
2336
2439
|
try {
|
|
2337
2440
|
const durableId = env.AGENT_BUILDER_THREAD.idFromName(threadId);
|
|
2338
2441
|
const stub = env.AGENT_BUILDER_THREAD.get(durableId);
|
|
2442
|
+
const uploadMatch = path.match(/^(.+)\/upload\/(start|complete|chunk\/(\d+))$/);
|
|
2443
|
+
if (uploadMatch) {
|
|
2444
|
+
const filePath = uploadMatch[1];
|
|
2445
|
+
const action = uploadMatch[2];
|
|
2446
|
+
const chunkIndex = uploadMatch[3] ? parseInt(uploadMatch[3], 10) : void 0;
|
|
2447
|
+
if (action === "start" && req.method === "POST") {
|
|
2448
|
+
return handleChunkedUploadStart(stub, filePath, req);
|
|
2449
|
+
}
|
|
2450
|
+
if (action === "complete" && req.method === "POST") {
|
|
2451
|
+
return handleChunkedUploadComplete(stub, filePath, req);
|
|
2452
|
+
}
|
|
2453
|
+
if (action.startsWith("chunk/") && req.method === "PUT" && chunkIndex !== void 0) {
|
|
2454
|
+
return handleChunkedUploadChunk(stub, filePath, chunkIndex, req);
|
|
2455
|
+
}
|
|
2456
|
+
return Response.json(
|
|
2457
|
+
{ error: `Invalid upload action: ${action} with method ${req.method}` },
|
|
2458
|
+
{ status: 400 }
|
|
2459
|
+
);
|
|
2460
|
+
}
|
|
2339
2461
|
switch (req.method) {
|
|
2340
2462
|
case "GET":
|
|
2341
2463
|
return handleGet(stub, path, url);
|
|
2342
|
-
case "
|
|
2343
|
-
return
|
|
2464
|
+
case "POST":
|
|
2465
|
+
return handlePost(stub, path, req);
|
|
2344
2466
|
case "DELETE":
|
|
2345
2467
|
return handleDelete(stub, path);
|
|
2346
2468
|
default:
|
|
@@ -2407,6 +2529,9 @@ async function handleGet(stub, path, url) {
|
|
|
2407
2529
|
if (acceptHeader === "json") {
|
|
2408
2530
|
return Response.json({ file });
|
|
2409
2531
|
}
|
|
2532
|
+
if (file.isChunked && file.chunkCount) {
|
|
2533
|
+
return streamChunkedFile(stub, path, file);
|
|
2534
|
+
}
|
|
2410
2535
|
const result = await stub.readFile(path);
|
|
2411
2536
|
if (!result.success) {
|
|
2412
2537
|
return Response.json({ error: result.error }, { status: 404 });
|
|
@@ -2424,7 +2549,40 @@ async function handleGet(stub, path, url) {
|
|
|
2424
2549
|
}
|
|
2425
2550
|
});
|
|
2426
2551
|
}
|
|
2427
|
-
|
|
2552
|
+
function streamChunkedFile(stub, path, file) {
|
|
2553
|
+
const { readable, writable } = new TransformStream();
|
|
2554
|
+
const writer = writable.getWriter();
|
|
2555
|
+
(async () => {
|
|
2556
|
+
try {
|
|
2557
|
+
for (let i = 0; i < file.chunkCount; i++) {
|
|
2558
|
+
const result = await stub.readFileChunk(path, i);
|
|
2559
|
+
if (!result.success) {
|
|
2560
|
+
console.error(`Failed to read chunk ${i}:`, result.error);
|
|
2561
|
+
break;
|
|
2562
|
+
}
|
|
2563
|
+
const binary = atob(result.data);
|
|
2564
|
+
const bytes = new Uint8Array(binary.length);
|
|
2565
|
+
for (let j = 0; j < binary.length; j++) {
|
|
2566
|
+
bytes[j] = binary.charCodeAt(j);
|
|
2567
|
+
}
|
|
2568
|
+
await writer.write(bytes);
|
|
2569
|
+
}
|
|
2570
|
+
} catch (error) {
|
|
2571
|
+
console.error("Error streaming chunked file:", error);
|
|
2572
|
+
} finally {
|
|
2573
|
+
await writer.close();
|
|
2574
|
+
}
|
|
2575
|
+
})();
|
|
2576
|
+
return new Response(readable, {
|
|
2577
|
+
headers: {
|
|
2578
|
+
"Content-Type": file.mimeType,
|
|
2579
|
+
"Content-Length": String(file.size),
|
|
2580
|
+
"Content-Disposition": `inline; filename="${file.name}"`
|
|
2581
|
+
// Note: We set Content-Length, but also use chunked transfer internally
|
|
2582
|
+
}
|
|
2583
|
+
});
|
|
2584
|
+
}
|
|
2585
|
+
async function handlePost(stub, path, req) {
|
|
2428
2586
|
const contentType = req.headers.get("Content-Type") || "";
|
|
2429
2587
|
if (contentType.includes("application/json")) {
|
|
2430
2588
|
const body = await req.json();
|
|
@@ -2444,7 +2602,7 @@ async function handlePut(stub, path, req) {
|
|
|
2444
2602
|
if (!result2.success) {
|
|
2445
2603
|
return Response.json({ error: result2.error }, { status: 400 });
|
|
2446
2604
|
}
|
|
2447
|
-
return Response.json(
|
|
2605
|
+
return Response.json(toAttachmentRef(result2.file), { status: 201 });
|
|
2448
2606
|
}
|
|
2449
2607
|
if (body.data) {
|
|
2450
2608
|
const result2 = await stub.writeFile(path, body.data, body.mimeType || "application/octet-stream", {
|
|
@@ -2454,12 +2612,16 @@ async function handlePut(stub, path, req) {
|
|
|
2454
2612
|
if (!result2.success) {
|
|
2455
2613
|
return Response.json({ error: result2.error }, { status: 400 });
|
|
2456
2614
|
}
|
|
2457
|
-
return Response.json(
|
|
2615
|
+
return Response.json(toAttachmentRef(result2.file), { status: 201 });
|
|
2458
2616
|
}
|
|
2459
2617
|
return Response.json({ error: "Invalid request body" }, { status: 400 });
|
|
2460
2618
|
}
|
|
2461
|
-
const data = await req.arrayBuffer();
|
|
2462
2619
|
const mimeType = contentType.split(";")[0].trim() || "application/octet-stream";
|
|
2620
|
+
const contentLength = parseInt(req.headers.get("Content-Length") || "0", 10);
|
|
2621
|
+
if (contentLength > CHUNK_SIZE && req.body) {
|
|
2622
|
+
return handleStreamingUpload(stub, path, mimeType, contentLength, req);
|
|
2623
|
+
}
|
|
2624
|
+
const data = await req.arrayBuffer();
|
|
2463
2625
|
const bytes = new Uint8Array(data);
|
|
2464
2626
|
let binary = "";
|
|
2465
2627
|
for (let i = 0; i < bytes.length; i++) {
|
|
@@ -2470,7 +2632,65 @@ async function handlePut(stub, path, req) {
|
|
|
2470
2632
|
if (!result.success) {
|
|
2471
2633
|
return Response.json({ error: result.error }, { status: 400 });
|
|
2472
2634
|
}
|
|
2473
|
-
return Response.json(
|
|
2635
|
+
return Response.json(toAttachmentRef(result.file), { status: 201 });
|
|
2636
|
+
}
|
|
2637
|
+
async function handleStreamingUpload(stub, path, mimeType, totalSize, req) {
|
|
2638
|
+
const startResult = await stub.startChunkedUpload(path, totalSize, mimeType, {});
|
|
2639
|
+
if (!startResult.success) {
|
|
2640
|
+
return Response.json({ error: startResult.error }, { status: 400 });
|
|
2641
|
+
}
|
|
2642
|
+
const reader = req.body.getReader();
|
|
2643
|
+
let chunkIndex = 0;
|
|
2644
|
+
let buffer = new Uint8Array(0);
|
|
2645
|
+
try {
|
|
2646
|
+
while (true) {
|
|
2647
|
+
const { done, value } = await reader.read();
|
|
2648
|
+
if (value) {
|
|
2649
|
+
const newBuffer = new Uint8Array(buffer.length + value.length);
|
|
2650
|
+
newBuffer.set(buffer);
|
|
2651
|
+
newBuffer.set(value, buffer.length);
|
|
2652
|
+
buffer = newBuffer;
|
|
2653
|
+
while (buffer.length >= CHUNK_SIZE) {
|
|
2654
|
+
const chunk = buffer.slice(0, CHUNK_SIZE);
|
|
2655
|
+
const base64 = arrayBufferToBase64(chunk);
|
|
2656
|
+
const chunkResult = await stub.writeFileChunk(path, chunkIndex, base64);
|
|
2657
|
+
if (!chunkResult.success) {
|
|
2658
|
+
return Response.json({ error: chunkResult.error }, { status: 400 });
|
|
2659
|
+
}
|
|
2660
|
+
chunkIndex++;
|
|
2661
|
+
buffer = buffer.slice(CHUNK_SIZE);
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
if (done) {
|
|
2665
|
+
if (buffer.length > 0) {
|
|
2666
|
+
const base64 = arrayBufferToBase64(buffer);
|
|
2667
|
+
const chunkResult = await stub.writeFileChunk(path, chunkIndex, base64);
|
|
2668
|
+
if (!chunkResult.success) {
|
|
2669
|
+
return Response.json({ error: chunkResult.error }, { status: 400 });
|
|
2670
|
+
}
|
|
2671
|
+
chunkIndex++;
|
|
2672
|
+
}
|
|
2673
|
+
break;
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
const completeResult = await stub.completeChunkedUpload(path, chunkIndex, {});
|
|
2677
|
+
if (!completeResult.success) {
|
|
2678
|
+
return Response.json({ error: completeResult.error }, { status: 400 });
|
|
2679
|
+
}
|
|
2680
|
+
return Response.json(toAttachmentRef(completeResult.file), { status: 201 });
|
|
2681
|
+
} catch (error) {
|
|
2682
|
+
console.error("Streaming upload failed:", error);
|
|
2683
|
+
return Response.json({ error: error.message || "Upload failed" }, { status: 500 });
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
function arrayBufferToBase64(bytes) {
|
|
2687
|
+
const BATCH_SIZE = 32768;
|
|
2688
|
+
let binary = "";
|
|
2689
|
+
for (let i = 0; i < bytes.length; i += BATCH_SIZE) {
|
|
2690
|
+
const slice = bytes.subarray(i, Math.min(i + BATCH_SIZE, bytes.length));
|
|
2691
|
+
binary += String.fromCharCode.apply(null, slice);
|
|
2692
|
+
}
|
|
2693
|
+
return btoa(binary);
|
|
2474
2694
|
}
|
|
2475
2695
|
async function handleDelete(stub, path) {
|
|
2476
2696
|
const statResult = await stub.statFile(path);
|
|
@@ -2491,6 +2711,61 @@ async function handleDelete(stub, path) {
|
|
|
2491
2711
|
}
|
|
2492
2712
|
return new Response(null, { status: 204 });
|
|
2493
2713
|
}
|
|
2714
|
+
async function handleChunkedUploadStart(stub, filePath, req) {
|
|
2715
|
+
const body = await req.json();
|
|
2716
|
+
if (!body.totalSize || !body.mimeType) {
|
|
2717
|
+
return Response.json(
|
|
2718
|
+
{ error: "totalSize and mimeType are required" },
|
|
2719
|
+
{ status: 400 }
|
|
2720
|
+
);
|
|
2721
|
+
}
|
|
2722
|
+
const result = await stub.startChunkedUpload(filePath, body.totalSize, body.mimeType, {
|
|
2723
|
+
metadata: body.metadata,
|
|
2724
|
+
width: body.width,
|
|
2725
|
+
height: body.height
|
|
2726
|
+
});
|
|
2727
|
+
if (!result.success) {
|
|
2728
|
+
return Response.json({ error: result.error }, { status: 400 });
|
|
2729
|
+
}
|
|
2730
|
+
return Response.json({
|
|
2731
|
+
chunkSize: result.chunkSize,
|
|
2732
|
+
expectedChunks: result.expectedChunks,
|
|
2733
|
+
path: filePath
|
|
2734
|
+
});
|
|
2735
|
+
}
|
|
2736
|
+
async function handleChunkedUploadChunk(stub, filePath, chunkIndex, req) {
|
|
2737
|
+
const data = await req.arrayBuffer();
|
|
2738
|
+
const bytes = new Uint8Array(data);
|
|
2739
|
+
let binary = "";
|
|
2740
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2741
|
+
binary += String.fromCharCode(bytes[i]);
|
|
2742
|
+
}
|
|
2743
|
+
const base64 = btoa(binary);
|
|
2744
|
+
const result = await stub.writeFileChunk(filePath, chunkIndex, base64);
|
|
2745
|
+
if (!result.success) {
|
|
2746
|
+
return Response.json({ error: result.error }, { status: 400 });
|
|
2747
|
+
}
|
|
2748
|
+
return Response.json({
|
|
2749
|
+
chunkIndex,
|
|
2750
|
+
received: data.byteLength
|
|
2751
|
+
});
|
|
2752
|
+
}
|
|
2753
|
+
async function handleChunkedUploadComplete(stub, filePath, req) {
|
|
2754
|
+
const body = await req.json();
|
|
2755
|
+
if (typeof body.expectedChunks !== "number") {
|
|
2756
|
+
return Response.json(
|
|
2757
|
+
{ error: "expectedChunks is required" },
|
|
2758
|
+
{ status: 400 }
|
|
2759
|
+
);
|
|
2760
|
+
}
|
|
2761
|
+
const result = await stub.completeChunkedUpload(filePath, body.expectedChunks, {
|
|
2762
|
+
thumbnail: body.thumbnail
|
|
2763
|
+
});
|
|
2764
|
+
if (!result.success) {
|
|
2765
|
+
return Response.json({ error: result.error }, { status: 400 });
|
|
2766
|
+
}
|
|
2767
|
+
return Response.json(toAttachmentRef(result.file), { status: 201 });
|
|
2768
|
+
}
|
|
2494
2769
|
|
|
2495
2770
|
// src/api/threads/[id]/logs/[logId].get.ts
|
|
2496
2771
|
var logId_get_default = defineController(async ({ req, params, env }) => {
|
|
@@ -2654,11 +2929,13 @@ var routeHandlers = {
|
|
|
2654
2929
|
"GET:/threads/:id/cost": cost_get_default,
|
|
2655
2930
|
"GET:/threads/:id/fs": fs_default,
|
|
2656
2931
|
"GET:/threads/:id/logs": logs_get_default,
|
|
2657
|
-
"POST:/threads/:id/message": message_post_default,
|
|
2658
2932
|
"GET:/threads/:id/messages": messages_get_default,
|
|
2933
|
+
"POST:/threads/:id/messages": messages_post_default,
|
|
2659
2934
|
"POST:/threads/:id/rpc": rpc_post_default,
|
|
2660
2935
|
"POST:/threads/:id/stop": stop_post_default,
|
|
2661
2936
|
"GET:/threads/:id/stream": stream_default,
|
|
2937
|
+
"DELETE:/threads/:id/fs/**": path_default,
|
|
2938
|
+
"POST:/threads/:id/fs/**": path_default,
|
|
2662
2939
|
"GET:/threads/:id/fs/**": path_default,
|
|
2663
2940
|
"GET:/threads/:id/logs/:logId": logId_get_default,
|
|
2664
2941
|
"DELETE:/threads/:id/messages/:messageId": messageId_delete_default,
|