weifuwu 0.25.0 → 0.25.1
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/dist/index.d.ts +1 -0
- package/dist/index.js +122 -81
- package/dist/opencode/rest.d.ts +2 -1
- package/dist/opencode/ws.d.ts +2 -2
- package/dist/queue/types.d.ts +0 -1
- package/dist/rate-limit.d.ts +10 -5
- package/dist/redis/client.d.ts +2 -0
- package/dist/redis/index.d.ts +2 -3
- package/dist/serve.d.ts +1 -1
- package/dist/session.d.ts +20 -0
- package/dist/types.d.ts +13 -0
- package/dist/user/client.d.ts +6 -1
- package/dist/validate.d.ts +16 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type { Context, Handler, Middleware, ErrorHandler } from './types.ts';
|
|
2
|
+
export { HttpError } from './types.ts';
|
|
2
3
|
export { currentTraceId, currentTrace, runWithTrace, traceElapsed, trace } from './trace.ts';
|
|
3
4
|
export type { TraceContext, TraceInjected, TraceOptions } from './trace.ts';
|
|
4
5
|
export { loadEnv, isDev, isProd, isBundled, getPublicEnv, env } from './env.ts';
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,16 @@ var __export = (target, all) => {
|
|
|
4
4
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
+
// types.ts
|
|
8
|
+
var HttpError = class extends Error {
|
|
9
|
+
status;
|
|
10
|
+
constructor(message, status) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "HttpError";
|
|
13
|
+
this.status = status;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
7
17
|
// trace.ts
|
|
8
18
|
import crypto2 from "node:crypto";
|
|
9
19
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -110,14 +120,6 @@ function env() {
|
|
|
110
120
|
|
|
111
121
|
// serve.ts
|
|
112
122
|
import http from "node:http";
|
|
113
|
-
var HttpError = class extends Error {
|
|
114
|
-
status;
|
|
115
|
-
constructor(message, status) {
|
|
116
|
-
super(message);
|
|
117
|
-
this.status = status;
|
|
118
|
-
this.name = "HttpError";
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
123
|
var DEFAULT_MAX_BODY = 10 * 1024 * 1024;
|
|
122
124
|
async function readBody(req, maxSize) {
|
|
123
125
|
const limit = maxSize ?? DEFAULT_MAX_BODY;
|
|
@@ -1271,7 +1273,7 @@ function parseBody(text2, ct) {
|
|
|
1271
1273
|
return text2;
|
|
1272
1274
|
}
|
|
1273
1275
|
function validate(schemas) {
|
|
1274
|
-
|
|
1276
|
+
const mw = async (req, ctx, next) => {
|
|
1275
1277
|
const parsed = {};
|
|
1276
1278
|
const issues = [];
|
|
1277
1279
|
if (schemas?.params) {
|
|
@@ -1358,6 +1360,8 @@ function validate(schemas) {
|
|
|
1358
1360
|
ctx.parsed = { ...ctx.parsed, ...parsed };
|
|
1359
1361
|
return next(req, ctx);
|
|
1360
1362
|
};
|
|
1363
|
+
mw.__meta = { injects: ["parsed"], depends: [] };
|
|
1364
|
+
return mw;
|
|
1361
1365
|
}
|
|
1362
1366
|
|
|
1363
1367
|
// cookie.ts
|
|
@@ -1461,7 +1465,7 @@ function detectMimeFromExtension(filename) {
|
|
|
1461
1465
|
}
|
|
1462
1466
|
function upload(options) {
|
|
1463
1467
|
const saveDir = options?.dir;
|
|
1464
|
-
|
|
1468
|
+
const mw = async (req, ctx, next) => {
|
|
1465
1469
|
const ct = req.headers.get("content-type") ?? "";
|
|
1466
1470
|
if (!ct.includes("multipart/form-data")) return next(req, ctx);
|
|
1467
1471
|
try {
|
|
@@ -1517,6 +1521,8 @@ function upload(options) {
|
|
|
1517
1521
|
ctx.parsed = { ...ctx.parsed, files, fields };
|
|
1518
1522
|
return next(req, ctx);
|
|
1519
1523
|
};
|
|
1524
|
+
mw.__meta = { injects: ["parsed"], depends: [] };
|
|
1525
|
+
return mw;
|
|
1520
1526
|
}
|
|
1521
1527
|
|
|
1522
1528
|
// rate-limit.ts
|
|
@@ -1601,7 +1607,7 @@ function rateLimit(options) {
|
|
|
1601
1607
|
return addRateLimitHeaders(res, max, remaining, reset);
|
|
1602
1608
|
};
|
|
1603
1609
|
mw.__meta = { injects: [], depends: [] };
|
|
1604
|
-
mw.close = () => {
|
|
1610
|
+
mw.close = async () => {
|
|
1605
1611
|
if (interval) clearInterval(interval);
|
|
1606
1612
|
hits.clear();
|
|
1607
1613
|
};
|
|
@@ -1728,7 +1734,7 @@ import crypto3 from "node:crypto";
|
|
|
1728
1734
|
function requestId(options) {
|
|
1729
1735
|
const header = options?.header ?? "X-Request-ID";
|
|
1730
1736
|
const gen = options?.generator ?? (() => crypto3.randomUUID());
|
|
1731
|
-
|
|
1737
|
+
const mw = async (req, ctx, next) => {
|
|
1732
1738
|
const existing = req.headers.get(header);
|
|
1733
1739
|
const id2 = existing ?? gen();
|
|
1734
1740
|
ctx.requestId = id2;
|
|
@@ -1738,6 +1744,8 @@ function requestId(options) {
|
|
|
1738
1744
|
h.set(header, id2);
|
|
1739
1745
|
return new Response(res.body, { status: res.status, statusText: res.statusText, headers: h });
|
|
1740
1746
|
};
|
|
1747
|
+
mw.__meta = { injects: ["requestId"], depends: [] };
|
|
1748
|
+
return mw;
|
|
1741
1749
|
}
|
|
1742
1750
|
|
|
1743
1751
|
// sse.ts
|
|
@@ -1833,6 +1841,7 @@ var TestRequest = class {
|
|
|
1833
1841
|
}
|
|
1834
1842
|
/** Shortcut: set ctx.user */
|
|
1835
1843
|
withUser(user2) {
|
|
1844
|
+
;
|
|
1836
1845
|
this.ctxMixin.user = user2;
|
|
1837
1846
|
return this;
|
|
1838
1847
|
}
|
|
@@ -3749,6 +3758,7 @@ var BUILTIN_PROVIDERS = {
|
|
|
3749
3758
|
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
3750
3759
|
userUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
|
|
3751
3760
|
scope: "openid email profile",
|
|
3761
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3752
3762
|
parseUser: (data) => ({
|
|
3753
3763
|
id: data.id,
|
|
3754
3764
|
email: data.email,
|
|
@@ -3761,6 +3771,7 @@ var BUILTIN_PROVIDERS = {
|
|
|
3761
3771
|
tokenUrl: "https://github.com/login/oauth/access_token",
|
|
3762
3772
|
userUrl: "https://api.github.com/user",
|
|
3763
3773
|
scope: "read:user user:email",
|
|
3774
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3764
3775
|
parseUser: (data) => ({
|
|
3765
3776
|
id: String(data.id),
|
|
3766
3777
|
email: data.email ?? "",
|
|
@@ -3857,8 +3868,9 @@ function registerOAuthLoginRoutes(router, deps, providers) {
|
|
|
3857
3868
|
const state = crypto5.randomUUID();
|
|
3858
3869
|
const redirectUri = new URL(req.url);
|
|
3859
3870
|
redirectUri.pathname = redirectUri.pathname.replace(/\/[^/]+$/, "/") + providerName + "/callback";
|
|
3860
|
-
|
|
3861
|
-
|
|
3871
|
+
const sess = ctx.session;
|
|
3872
|
+
if (sess) {
|
|
3873
|
+
sess.oauthState = { state, provider: providerName };
|
|
3862
3874
|
}
|
|
3863
3875
|
const scope = config.scope ?? meta.scope;
|
|
3864
3876
|
const params = new URLSearchParams({
|
|
@@ -3885,11 +3897,12 @@ function registerOAuthLoginRoutes(router, deps, providers) {
|
|
|
3885
3897
|
if (!code || !state) {
|
|
3886
3898
|
return Response.json({ error: "Missing code or state parameter" }, { status: 400 });
|
|
3887
3899
|
}
|
|
3888
|
-
const
|
|
3900
|
+
const sess = ctx.session;
|
|
3901
|
+
const savedState = sess?.oauthState;
|
|
3889
3902
|
if (!savedState || savedState.state !== state || savedState.provider !== providerName) {
|
|
3890
3903
|
return Response.json({ error: "Invalid state \u2014 possible CSRF attack" }, { status: 403 });
|
|
3891
3904
|
}
|
|
3892
|
-
if (
|
|
3905
|
+
if (sess) delete sess.oauthState;
|
|
3893
3906
|
const redirectUri = url.origin + url.pathname.replace(/\/callback$/, "");
|
|
3894
3907
|
let tokenRes;
|
|
3895
3908
|
try {
|
|
@@ -3947,9 +3960,10 @@ function registerOAuthLoginRoutes(router, deps, providers) {
|
|
|
3947
3960
|
return Response.json({ error: "Failed to create/link user" }, { status: 500 });
|
|
3948
3961
|
}
|
|
3949
3962
|
const token = signToken(user2);
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3963
|
+
const sess2 = ctx.session;
|
|
3964
|
+
if (sess2) {
|
|
3965
|
+
sess2.userId = user2.id;
|
|
3966
|
+
sess2.role = user2.role;
|
|
3953
3967
|
}
|
|
3954
3968
|
const accept = req.headers.get("accept") ?? "";
|
|
3955
3969
|
if (accept.includes("application/json")) {
|
|
@@ -4147,9 +4161,7 @@ function user(options) {
|
|
|
4147
4161
|
const { email, password, name } = RegisterSchema.parse(data);
|
|
4148
4162
|
const existing = await findByEmail(email);
|
|
4149
4163
|
if (existing) {
|
|
4150
|
-
|
|
4151
|
-
err.status = 409;
|
|
4152
|
-
throw err;
|
|
4164
|
+
throw new HttpError("Email already registered", 409);
|
|
4153
4165
|
}
|
|
4154
4166
|
const hashed = hashPassword(password);
|
|
4155
4167
|
const row = await _users.insert({ email, password: hashed, name });
|
|
@@ -4162,14 +4174,10 @@ function user(options) {
|
|
|
4162
4174
|
const { data: rows } = await _users.readMany({ email });
|
|
4163
4175
|
const row = rows[0];
|
|
4164
4176
|
if (!row) {
|
|
4165
|
-
|
|
4166
|
-
err.status = 401;
|
|
4167
|
-
throw err;
|
|
4177
|
+
throw new HttpError("Invalid email or password", 401);
|
|
4168
4178
|
}
|
|
4169
4179
|
if (!verifyPassword(password, row.password)) {
|
|
4170
|
-
|
|
4171
|
-
err.status = 401;
|
|
4172
|
-
throw err;
|
|
4180
|
+
throw new HttpError("Invalid email or password", 401);
|
|
4173
4181
|
}
|
|
4174
4182
|
const userData = row;
|
|
4175
4183
|
const token = signToken(userData);
|
|
@@ -4359,7 +4367,7 @@ function user(options) {
|
|
|
4359
4367
|
return null;
|
|
4360
4368
|
}
|
|
4361
4369
|
function middleware() {
|
|
4362
|
-
|
|
4370
|
+
const mw = async (req, ctx, next) => {
|
|
4363
4371
|
const userData = await resolveUser(req, ctx);
|
|
4364
4372
|
if (userData) {
|
|
4365
4373
|
ctx.user = userData;
|
|
@@ -4370,9 +4378,11 @@ function user(options) {
|
|
|
4370
4378
|
headers: headerName.toLowerCase() === "authorization" ? { "WWW-Authenticate": "Bearer" } : void 0
|
|
4371
4379
|
});
|
|
4372
4380
|
};
|
|
4381
|
+
mw.__meta = { injects: ["user"], depends: [] };
|
|
4382
|
+
return mw;
|
|
4373
4383
|
}
|
|
4374
4384
|
function middlewareOptional(_opts) {
|
|
4375
|
-
|
|
4385
|
+
const mw = async (req, ctx, next) => {
|
|
4376
4386
|
const userData = await resolveUser(req, ctx);
|
|
4377
4387
|
if (userData) {
|
|
4378
4388
|
;
|
|
@@ -4380,6 +4390,8 @@ function user(options) {
|
|
|
4380
4390
|
}
|
|
4381
4391
|
return next(req, ctx);
|
|
4382
4392
|
};
|
|
4393
|
+
mw.__meta = { injects: ["user"], depends: [] };
|
|
4394
|
+
return mw;
|
|
4383
4395
|
}
|
|
4384
4396
|
async function parseBody2(req) {
|
|
4385
4397
|
const ct = req.headers.get("content-type") || "";
|
|
@@ -4404,8 +4416,9 @@ function user(options) {
|
|
|
4404
4416
|
if (err instanceof z2.ZodError) {
|
|
4405
4417
|
return Response.json({ error: "Validation failed", issues: err.issues }, { status: 400 });
|
|
4406
4418
|
}
|
|
4407
|
-
const status = err.status
|
|
4408
|
-
|
|
4419
|
+
const status = err instanceof HttpError ? err.status : 500;
|
|
4420
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4421
|
+
return Response.json({ error: message }, { status });
|
|
4409
4422
|
}
|
|
4410
4423
|
});
|
|
4411
4424
|
r.post("/login", async (req, ctx) => {
|
|
@@ -4426,8 +4439,9 @@ function user(options) {
|
|
|
4426
4439
|
if (err instanceof z2.ZodError) {
|
|
4427
4440
|
return Response.json({ error: "Validation failed", issues: err.issues }, { status: 400 });
|
|
4428
4441
|
}
|
|
4429
|
-
const status = err.status
|
|
4430
|
-
|
|
4442
|
+
const status = err instanceof HttpError ? err.status : 500;
|
|
4443
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4444
|
+
return Response.json({ error: message }, { status });
|
|
4431
4445
|
}
|
|
4432
4446
|
});
|
|
4433
4447
|
}
|
|
@@ -4446,7 +4460,8 @@ function user(options) {
|
|
|
4446
4460
|
if (err instanceof z2.ZodError) {
|
|
4447
4461
|
return Response.json({ error: "Validation failed", issues: err.issues }, { status: 400 });
|
|
4448
4462
|
}
|
|
4449
|
-
|
|
4463
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4464
|
+
return Response.json({ error: message }, { status: 500 });
|
|
4450
4465
|
}
|
|
4451
4466
|
});
|
|
4452
4467
|
r.delete("/api-keys/:id", middleware(), async (req, ctx) => {
|
|
@@ -4522,7 +4537,7 @@ function user(options) {
|
|
|
4522
4537
|
return mod;
|
|
4523
4538
|
}
|
|
4524
4539
|
|
|
4525
|
-
// redis/
|
|
4540
|
+
// redis/client.ts
|
|
4526
4541
|
import { Redis as IORedis } from "ioredis";
|
|
4527
4542
|
function redis(opts) {
|
|
4528
4543
|
const options = typeof opts === "string" ? { url: opts } : opts ?? {};
|
|
@@ -4718,15 +4733,12 @@ function createMemoryQueue(opts) {
|
|
|
4718
4733
|
running = true;
|
|
4719
4734
|
poll();
|
|
4720
4735
|
};
|
|
4721
|
-
mw.
|
|
4736
|
+
mw.close = async function close() {
|
|
4722
4737
|
running = false;
|
|
4723
4738
|
if (pollTimer) {
|
|
4724
4739
|
clearTimeout(pollTimer);
|
|
4725
4740
|
pollTimer = null;
|
|
4726
4741
|
}
|
|
4727
|
-
};
|
|
4728
|
-
mw.close = async function close() {
|
|
4729
|
-
mw.stop();
|
|
4730
4742
|
while (inflight > 0) await new Promise((r) => setTimeout(r, 50));
|
|
4731
4743
|
};
|
|
4732
4744
|
mw.jobs = async function(limit) {
|
|
@@ -4891,15 +4903,12 @@ function createPgQueue(opts) {
|
|
|
4891
4903
|
running = true;
|
|
4892
4904
|
poll();
|
|
4893
4905
|
};
|
|
4894
|
-
mw.
|
|
4906
|
+
mw.close = async function close() {
|
|
4895
4907
|
running = false;
|
|
4896
4908
|
if (pollTimer) {
|
|
4897
4909
|
clearTimeout(pollTimer);
|
|
4898
4910
|
pollTimer = null;
|
|
4899
4911
|
}
|
|
4900
|
-
};
|
|
4901
|
-
mw.close = async function close() {
|
|
4902
|
-
mw.stop();
|
|
4903
4912
|
while (inflight > 0) await new Promise((r) => setTimeout(r, 50));
|
|
4904
4913
|
};
|
|
4905
4914
|
mw.jobs = async function jobs(limit) {
|
|
@@ -5058,16 +5067,13 @@ function createRedisQueue(opts) {
|
|
|
5058
5067
|
running = true;
|
|
5059
5068
|
poll();
|
|
5060
5069
|
};
|
|
5061
|
-
mw.
|
|
5070
|
+
mw.close = async function close() {
|
|
5062
5071
|
running = false;
|
|
5063
5072
|
epoch++;
|
|
5064
5073
|
if (pollTimer) {
|
|
5065
5074
|
clearTimeout(pollTimer);
|
|
5066
5075
|
pollTimer = null;
|
|
5067
5076
|
}
|
|
5068
|
-
};
|
|
5069
|
-
mw.close = async function close() {
|
|
5070
|
-
mw.stop();
|
|
5071
5077
|
while (inflight > 0) await new Promise((r) => setTimeout(r, 50));
|
|
5072
5078
|
redis2.disconnect();
|
|
5073
5079
|
};
|
|
@@ -6065,7 +6071,7 @@ function tenant(options) {
|
|
|
6065
6071
|
);
|
|
6066
6072
|
}
|
|
6067
6073
|
function middleware() {
|
|
6068
|
-
|
|
6074
|
+
const mw = async (req, ctx, next) => {
|
|
6069
6075
|
const user2 = ctx.user;
|
|
6070
6076
|
if (!user2) {
|
|
6071
6077
|
return new Response("Unauthorized", { status: 401 });
|
|
@@ -6101,6 +6107,8 @@ function tenant(options) {
|
|
|
6101
6107
|
ctx.tenant = { id: member.id, name: member.name, role: member.role };
|
|
6102
6108
|
return next(req, ctx);
|
|
6103
6109
|
};
|
|
6110
|
+
mw.__meta = { injects: ["tenant"], depends: ["user"] };
|
|
6111
|
+
return mw;
|
|
6104
6112
|
}
|
|
6105
6113
|
const r = buildRouter(sql2, usersTable);
|
|
6106
6114
|
const mod = r;
|
|
@@ -6179,17 +6187,13 @@ function buildRouter2(deps) {
|
|
|
6179
6187
|
if (!body.input && !body.messages) {
|
|
6180
6188
|
return Response.json({ error: "input or messages is required" }, { status: 400 });
|
|
6181
6189
|
}
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
});
|
|
6188
|
-
}
|
|
6189
|
-
return Response.json(result);
|
|
6190
|
-
} catch (err) {
|
|
6191
|
-
return Response.json({ error: err.message }, { status: 500 });
|
|
6190
|
+
const result = await runner.run(id2, body);
|
|
6191
|
+
if ("stream" in result) {
|
|
6192
|
+
return new Response(result.stream, {
|
|
6193
|
+
headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache" }
|
|
6194
|
+
});
|
|
6192
6195
|
}
|
|
6196
|
+
return Response.json(result);
|
|
6193
6197
|
});
|
|
6194
6198
|
r.get("/agents/:id/runs", async (_req, ctx) => {
|
|
6195
6199
|
const agentId = parseInt(ctx.params.id, 10);
|
|
@@ -6220,13 +6224,26 @@ function buildRouter2(deps) {
|
|
|
6220
6224
|
{ orderBy: { created_at: "desc" } }
|
|
6221
6225
|
);
|
|
6222
6226
|
const total = rows.length;
|
|
6223
|
-
const success = rows.filter(
|
|
6227
|
+
const success = rows.filter(
|
|
6228
|
+
(r2) => r2.status === "success" || r2.status === "stream"
|
|
6229
|
+
).length;
|
|
6224
6230
|
const error = rows.filter((r2) => r2.status === "error").length;
|
|
6225
|
-
const totalTokensIn = rows.reduce(
|
|
6226
|
-
|
|
6227
|
-
|
|
6231
|
+
const totalTokensIn = rows.reduce(
|
|
6232
|
+
(sum, r2) => sum + (r2.tokens_in || 0),
|
|
6233
|
+
0
|
|
6234
|
+
);
|
|
6235
|
+
const totalTokensOut = rows.reduce(
|
|
6236
|
+
(sum, r2) => sum + (r2.tokens_out || 0),
|
|
6237
|
+
0
|
|
6238
|
+
);
|
|
6239
|
+
const totalElapsed = rows.reduce(
|
|
6240
|
+
(sum, r2) => sum + (r2.elapsed_ms || 0),
|
|
6241
|
+
0
|
|
6242
|
+
);
|
|
6228
6243
|
const avgElapsed = total > 0 ? Math.round(totalElapsed / total) : 0;
|
|
6229
|
-
const sorted = [...rows].sort(
|
|
6244
|
+
const sorted = [...rows].sort(
|
|
6245
|
+
(a, b) => (a.elapsed_ms || 0) - (b.elapsed_ms || 0)
|
|
6246
|
+
);
|
|
6230
6247
|
const p95Idx = Math.ceil(sorted.length * 0.95) - 1;
|
|
6231
6248
|
const p95Elapsed = sorted.length > 0 ? sorted[p95Idx]?.elapsed_ms || 0 : 0;
|
|
6232
6249
|
return Response.json({
|
|
@@ -6252,7 +6269,10 @@ function buildRouter2(deps) {
|
|
|
6252
6269
|
const doc = await runner.addKnowledge(agentId, body.title || "", body.content);
|
|
6253
6270
|
return Response.json(doc, { status: 201 });
|
|
6254
6271
|
} catch (err) {
|
|
6255
|
-
return Response.json(
|
|
6272
|
+
return Response.json(
|
|
6273
|
+
{ error: err instanceof Error ? err.message : String(err) },
|
|
6274
|
+
{ status: 500 }
|
|
6275
|
+
);
|
|
6256
6276
|
}
|
|
6257
6277
|
});
|
|
6258
6278
|
r.get("/agents/:id/knowledge", async (_req, ctx) => {
|
|
@@ -6294,6 +6314,7 @@ function chunkContent(content, chunkSize, overlap) {
|
|
|
6294
6314
|
// agent/run.ts
|
|
6295
6315
|
function hasKnowledgeDocs(sql2, agentId) {
|
|
6296
6316
|
return sql2`SELECT 1 FROM "_knowledge_documents" WHERE agent_id = ${agentId} LIMIT 1`.then(
|
|
6317
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6297
6318
|
(r) => r.length > 0
|
|
6298
6319
|
);
|
|
6299
6320
|
}
|
|
@@ -6304,7 +6325,12 @@ async function searchKnowledge(sql2, provider, agentId, query, limit = 5) {
|
|
|
6304
6325
|
`SELECT id, title, content, metadata, embedding <=> $1::vector AS _score FROM "_knowledge_documents" WHERE agent_id = $2 ORDER BY embedding <=> $1::vector LIMIT $3`,
|
|
6305
6326
|
[vec, agentId, limit]
|
|
6306
6327
|
);
|
|
6307
|
-
return docs.map((d) => ({
|
|
6328
|
+
return docs.map((d) => ({
|
|
6329
|
+
id: d.id,
|
|
6330
|
+
title: d.title,
|
|
6331
|
+
content: d.content,
|
|
6332
|
+
score: d._score
|
|
6333
|
+
}));
|
|
6308
6334
|
}
|
|
6309
6335
|
async function loadAgent(agents, agentId) {
|
|
6310
6336
|
const row = await agents.read(agentId);
|
|
@@ -7734,11 +7760,12 @@ function buildHeadPayload(opts) {
|
|
|
7734
7760
|
flash: ctx.flash,
|
|
7735
7761
|
loaderData
|
|
7736
7762
|
};
|
|
7737
|
-
|
|
7763
|
+
const rawUser = ctx.user;
|
|
7764
|
+
if (rawUser && typeof rawUser === "object") {
|
|
7738
7765
|
const safeUser = {};
|
|
7739
7766
|
for (const k of ["id", "name", "email", "role", "avatar"]) {
|
|
7740
|
-
if (k in
|
|
7741
|
-
safeUser[k] =
|
|
7767
|
+
if (k in rawUser) {
|
|
7768
|
+
safeUser[k] = rawUser[k];
|
|
7742
7769
|
}
|
|
7743
7770
|
}
|
|
7744
7771
|
ctxData.user = safeUser;
|
|
@@ -9250,17 +9277,17 @@ async function buildRouter4(deps) {
|
|
|
9250
9277
|
FROM "_opencode_messages"
|
|
9251
9278
|
WHERE session_id = ${sessionId}
|
|
9252
9279
|
`;
|
|
9253
|
-
const
|
|
9280
|
+
const raw = rows[0] || {
|
|
9254
9281
|
message_count: 0,
|
|
9255
9282
|
total_tokens_in: 0,
|
|
9256
9283
|
total_tokens_out: 0
|
|
9257
9284
|
};
|
|
9258
9285
|
return Response.json({
|
|
9259
9286
|
session_id: sessionId,
|
|
9260
|
-
message_count:
|
|
9261
|
-
tokens_in:
|
|
9262
|
-
tokens_out:
|
|
9263
|
-
tokens_total:
|
|
9287
|
+
message_count: raw.message_count,
|
|
9288
|
+
tokens_in: raw.total_tokens_in,
|
|
9289
|
+
tokens_out: raw.total_tokens_out,
|
|
9290
|
+
tokens_total: Number(raw.total_tokens_in) + Number(raw.total_tokens_out)
|
|
9264
9291
|
});
|
|
9265
9292
|
});
|
|
9266
9293
|
try {
|
|
@@ -9316,8 +9343,13 @@ function createWSHandler2(deps) {
|
|
|
9316
9343
|
client.mountPath
|
|
9317
9344
|
);
|
|
9318
9345
|
ws.send(JSON.stringify({ type: "session_created", session: session2 }));
|
|
9319
|
-
} catch (
|
|
9320
|
-
ws.send(
|
|
9346
|
+
} catch (err) {
|
|
9347
|
+
ws.send(
|
|
9348
|
+
JSON.stringify({
|
|
9349
|
+
type: "error",
|
|
9350
|
+
error: err instanceof Error ? err.message : String(err)
|
|
9351
|
+
})
|
|
9352
|
+
);
|
|
9321
9353
|
}
|
|
9322
9354
|
break;
|
|
9323
9355
|
}
|
|
@@ -9371,9 +9403,9 @@ function createWSHandler2(deps) {
|
|
|
9371
9403
|
break;
|
|
9372
9404
|
}
|
|
9373
9405
|
}
|
|
9374
|
-
} catch (
|
|
9375
|
-
if (
|
|
9376
|
-
ws.send(JSON.stringify({ type: "error", error:
|
|
9406
|
+
} catch (err) {
|
|
9407
|
+
if (err instanceof Error && err.name !== "AbortError") {
|
|
9408
|
+
ws.send(JSON.stringify({ type: "error", error: err.message }));
|
|
9377
9409
|
}
|
|
9378
9410
|
}
|
|
9379
9411
|
break;
|
|
@@ -9856,6 +9888,7 @@ function theme(options) {
|
|
|
9856
9888
|
};
|
|
9857
9889
|
return next(req, ctx);
|
|
9858
9890
|
};
|
|
9891
|
+
mw.__meta = { injects: ["theme"], depends: [] };
|
|
9859
9892
|
class ThemeRouter extends Router {
|
|
9860
9893
|
middleware() {
|
|
9861
9894
|
return mw;
|
|
@@ -9957,6 +9990,7 @@ function i18n(options) {
|
|
|
9957
9990
|
};
|
|
9958
9991
|
return next(req, ctx);
|
|
9959
9992
|
};
|
|
9993
|
+
mw.__meta = { injects: ["i18n"], depends: [] };
|
|
9960
9994
|
class I18nRouter extends Router {
|
|
9961
9995
|
middleware() {
|
|
9962
9996
|
return mw;
|
|
@@ -10001,7 +10035,7 @@ function makeSetFlash(name, location) {
|
|
|
10001
10035
|
}
|
|
10002
10036
|
function flash(options) {
|
|
10003
10037
|
const name = options?.name ?? "flash";
|
|
10004
|
-
|
|
10038
|
+
const mw = async (req, ctx, next) => {
|
|
10005
10039
|
const raw = getCookies(req)[name] ?? null;
|
|
10006
10040
|
const referer = req.headers.get("referer") || "/";
|
|
10007
10041
|
let value = void 0;
|
|
@@ -10024,6 +10058,8 @@ function flash(options) {
|
|
|
10024
10058
|
}
|
|
10025
10059
|
return res;
|
|
10026
10060
|
};
|
|
10061
|
+
mw.__meta = { injects: ["flash"], depends: [] };
|
|
10062
|
+
return mw;
|
|
10027
10063
|
}
|
|
10028
10064
|
|
|
10029
10065
|
// seo.ts
|
|
@@ -10206,7 +10242,7 @@ function csrf(options) {
|
|
|
10206
10242
|
const headerName = options?.header ?? "x-csrf-token";
|
|
10207
10243
|
const bodyKey = options?.key ?? "_csrf";
|
|
10208
10244
|
const excluded = new Set(options?.excludeMethods ?? ["GET", "HEAD", "OPTIONS"]);
|
|
10209
|
-
|
|
10245
|
+
const mw = async (req, ctx, next) => {
|
|
10210
10246
|
const method = req.method.toUpperCase();
|
|
10211
10247
|
if (excluded.has(method)) {
|
|
10212
10248
|
let token = getCookies(req)[cookieName];
|
|
@@ -10243,6 +10279,8 @@ function csrf(options) {
|
|
|
10243
10279
|
}
|
|
10244
10280
|
return next(req, ctx);
|
|
10245
10281
|
};
|
|
10282
|
+
mw.__meta = { injects: ["csrf"], depends: [] };
|
|
10283
|
+
return mw;
|
|
10246
10284
|
}
|
|
10247
10285
|
|
|
10248
10286
|
// logdb/rest.ts
|
|
@@ -11880,6 +11918,7 @@ function s3(options) {
|
|
|
11880
11918
|
mw.url = url;
|
|
11881
11919
|
mw.list = list;
|
|
11882
11920
|
mw.client = client;
|
|
11921
|
+
mw.__meta = { injects: ["s3"], depends: [] };
|
|
11883
11922
|
return mw;
|
|
11884
11923
|
}
|
|
11885
11924
|
|
|
@@ -12142,6 +12181,7 @@ function permissions(options) {
|
|
|
12142
12181
|
mw.requireRole = requireRole;
|
|
12143
12182
|
mw.requirePermission = requirePermission;
|
|
12144
12183
|
mw.migrate = migrate;
|
|
12184
|
+
mw.__meta = { injects: ["permissions"], depends: ["user"] };
|
|
12145
12185
|
return mw;
|
|
12146
12186
|
}
|
|
12147
12187
|
|
|
@@ -12615,6 +12655,7 @@ function notifier(opts) {
|
|
|
12615
12655
|
}
|
|
12616
12656
|
export {
|
|
12617
12657
|
DEFAULT_MAX_BODY,
|
|
12658
|
+
HttpError,
|
|
12618
12659
|
MIGRATIONS_TABLE,
|
|
12619
12660
|
MemoryCache,
|
|
12620
12661
|
MemoryStore,
|
package/dist/opencode/rest.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Router } from '../router.ts';
|
|
2
2
|
import type { LanguageModel } from 'ai';
|
|
3
|
+
import type { SqlClient } from '../vendor.ts';
|
|
3
4
|
import type { SkillDef, SkillRegistry, OpencodePermissions, PendingQuestion } from './types.ts';
|
|
4
5
|
interface RestDeps {
|
|
5
|
-
sql:
|
|
6
|
+
sql: SqlClient;
|
|
6
7
|
model: LanguageModel;
|
|
7
8
|
workspace: string;
|
|
8
9
|
systemPrompt?: string;
|
package/dist/opencode/ws.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { WebSocket } from '../vendor.ts';
|
|
1
|
+
import type { WebSocket, SqlClient } from '../vendor.ts';
|
|
2
2
|
import type { LanguageModel } from 'ai';
|
|
3
3
|
import type { Context } from '../types.ts';
|
|
4
4
|
import type { PendingQuestion, SkillDef, SkillRegistry, OpencodePermissions } from './types.ts';
|
|
5
5
|
interface WsDeps {
|
|
6
|
-
sql:
|
|
6
|
+
sql: SqlClient;
|
|
7
7
|
model: LanguageModel;
|
|
8
8
|
workspace: string;
|
|
9
9
|
systemPrompt?: string;
|
package/dist/queue/types.d.ts
CHANGED
|
@@ -43,7 +43,6 @@ export interface Queue extends Middleware<Context, Context & QueueInjected>, Clo
|
|
|
43
43
|
}): Promise<string>;
|
|
44
44
|
process<T>(type: string, handler: (job: QueueJob<T>) => Promise<void>): void;
|
|
45
45
|
run(): Promise<void>;
|
|
46
|
-
stop(): void;
|
|
47
46
|
stats(): {
|
|
48
47
|
running: boolean;
|
|
49
48
|
inflight: number;
|
package/dist/rate-limit.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Redis } from './vendor.ts';
|
|
2
|
-
import type { Context, Middleware } from './types.ts';
|
|
2
|
+
import type { Context, Middleware, Closeable } from './types.ts';
|
|
3
3
|
/** Options for {@link rateLimit}. */
|
|
4
4
|
export interface RateLimitOptions {
|
|
5
5
|
/** Maximum requests within the window (default: 100). */
|
|
@@ -17,6 +17,14 @@ export interface RateLimitOptions {
|
|
|
17
17
|
/** Redis key prefix (default: `'ratelimit:'`). */
|
|
18
18
|
prefix?: string;
|
|
19
19
|
}
|
|
20
|
+
/** Rate limit module — middleware + stats. */
|
|
21
|
+
export interface RateLimitModule extends Middleware<Context, Context>, Closeable {
|
|
22
|
+
stats(): {
|
|
23
|
+
store: string;
|
|
24
|
+
entries?: number;
|
|
25
|
+
maxEntries: number;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
20
28
|
/**
|
|
21
29
|
* Rate limiting middleware (in-memory or Redis-backed).
|
|
22
30
|
*
|
|
@@ -34,7 +42,4 @@ export interface RateLimitOptions {
|
|
|
34
42
|
* app.use(rateLimit({ store: 'redis', redis: new Redis(), max: 100 }))
|
|
35
43
|
* ```
|
|
36
44
|
*/
|
|
37
|
-
export declare function rateLimit(options?: RateLimitOptions):
|
|
38
|
-
close: () => void;
|
|
39
|
-
stop?: () => void;
|
|
40
|
-
};
|
|
45
|
+
export declare function rateLimit(options?: RateLimitOptions): RateLimitModule;
|
package/dist/redis/index.d.ts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export {
|
|
3
|
-
export declare function redis(opts?: string | RedisOptions): RedisClient;
|
|
1
|
+
export { redis } from './client.ts';
|
|
2
|
+
export type { RedisOptions, RedisClient, RedisInjected } from './types.ts';
|
package/dist/serve.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type IncomingMessage, type ServerResponse } from 'node:http';
|
|
2
2
|
import type { Duplex } from 'node:stream';
|
|
3
|
-
import type
|
|
3
|
+
import { type Handler } from './types.ts';
|
|
4
4
|
export interface ServeOptions {
|
|
5
5
|
port?: number;
|
|
6
6
|
hostname?: string;
|
package/dist/session.d.ts
CHANGED
|
@@ -80,6 +80,10 @@ export declare class MemoryStore implements SessionStore {
|
|
|
80
80
|
/** Testing only: return approximate count. */
|
|
81
81
|
get size(): number;
|
|
82
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Redis-backed session store.
|
|
85
|
+
* Pass to `session({ store: new RedisStore({ redis }) })`.
|
|
86
|
+
*/
|
|
83
87
|
export declare class RedisStore implements SessionStore {
|
|
84
88
|
private redis;
|
|
85
89
|
private prefix;
|
|
@@ -90,6 +94,22 @@ export declare class RedisStore implements SessionStore {
|
|
|
90
94
|
destroy(sid: string): Promise<void>;
|
|
91
95
|
close(): Promise<void>;
|
|
92
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Session middleware. Injects `ctx.session` with a persistent key-value store
|
|
99
|
+
* scoped to the request. Data is automatically saved to the store on response.
|
|
100
|
+
*
|
|
101
|
+
* Defaults to memory store. Use `{ store: 'redis', redis }` for multi-process setups.
|
|
102
|
+
*
|
|
103
|
+
* ```ts
|
|
104
|
+
* import { session } from 'weifuwu'
|
|
105
|
+
* app.use(session())
|
|
106
|
+
*
|
|
107
|
+
* app.get('/visit', (req, ctx) => {
|
|
108
|
+
* ctx.session.count = (ctx.session.count ?? 0) + 1
|
|
109
|
+
* return Response.json({ visits: ctx.session.count })
|
|
110
|
+
* })
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
93
113
|
export declare function session(options?: SessionOptions): Middleware<Context, Context & SessionInjected> & {
|
|
94
114
|
close: () => Promise<void>;
|
|
95
115
|
store: SessionStore;
|
package/dist/types.d.ts
CHANGED
|
@@ -19,3 +19,16 @@ export interface Closeable {
|
|
|
19
19
|
/** Release all resources. Call once when shutting down. */
|
|
20
20
|
close(): Promise<void>;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* HTTP error with an explicit status code.
|
|
24
|
+
* Throw from a handler or middleware to return a non-200 response.
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* if (!resource) throw new HttpError('Not found', 404)
|
|
28
|
+
* serve() catches it and returns the status code.
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare class HttpError extends Error {
|
|
32
|
+
status: number;
|
|
33
|
+
constructor(message: string, status: number);
|
|
34
|
+
}
|
package/dist/user/client.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type { UserOptions, UserModule } from './types.ts';
|
|
1
|
+
import type { UserOptions, UserData, UserModule } from './types.ts';
|
|
2
|
+
declare module '../types.ts' {
|
|
3
|
+
interface Context {
|
|
4
|
+
user: UserData;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
2
7
|
/**
|
|
3
8
|
* User authentication module — local register/login, JWT verification, OAuth2 server, social login.
|
|
4
9
|
* Supports DB-less auth via tokens/verify/proxy options.
|
package/dist/validate.d.ts
CHANGED
|
@@ -11,4 +11,20 @@ export interface ValidationSchemas {
|
|
|
11
11
|
params?: ZodSchema;
|
|
12
12
|
headers?: ZodSchema;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Request validation middleware using Zod schemas.
|
|
16
|
+
*
|
|
17
|
+
* Validates `params`, `query`, `body`, and/or `headers` against schemas.
|
|
18
|
+
* Returns 422 with error details on mismatch.
|
|
19
|
+
* Injects `ctx.parsed` with validated-and-transformed values.
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { z } from 'zod'
|
|
23
|
+
*
|
|
24
|
+
* app.get('/users/:id', validate({
|
|
25
|
+
* params: z.object({ id: z.string() }),
|
|
26
|
+
* query: z.object({ include: z.string().optional() }),
|
|
27
|
+
* }), handler)
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
14
30
|
export declare function validate(schemas?: ValidationSchemas): Middleware;
|