@very_aq/codex-cli-web 0.0.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/LICENSE +21 -0
- package/README.md +228 -0
- package/README.zh-CN.md +228 -0
- package/package.json +38 -0
- package/server/dist/admin/userAdminRoutes.d.ts +7 -0
- package/server/dist/admin/userAdminRoutes.js +91 -0
- package/server/dist/admin/userAdminRoutes.js.map +1 -0
- package/server/dist/app.d.ts +22 -0
- package/server/dist/app.js +993 -0
- package/server/dist/app.js.map +1 -0
- package/server/dist/auth/adminInit.d.ts +41 -0
- package/server/dist/auth/adminInit.js +126 -0
- package/server/dist/auth/adminInit.js.map +1 -0
- package/server/dist/auth/bootstrapAdmin.d.ts +6 -0
- package/server/dist/auth/bootstrapAdmin.js +11 -0
- package/server/dist/auth/bootstrapAdmin.js.map +1 -0
- package/server/dist/auth/httpAuth.d.ts +16 -0
- package/server/dist/auth/httpAuth.js +31 -0
- package/server/dist/auth/httpAuth.js.map +1 -0
- package/server/dist/auth/password.d.ts +2 -0
- package/server/dist/auth/password.js +68 -0
- package/server/dist/auth/password.js.map +1 -0
- package/server/dist/auth/requireAdmin.d.ts +2 -0
- package/server/dist/auth/requireAdmin.js +14 -0
- package/server/dist/auth/requireAdmin.js.map +1 -0
- package/server/dist/auth/roles.d.ts +3 -0
- package/server/dist/auth/roles.js +13 -0
- package/server/dist/auth/roles.js.map +1 -0
- package/server/dist/auth/session.d.ts +24 -0
- package/server/dist/auth/session.js +127 -0
- package/server/dist/auth/session.js.map +1 -0
- package/server/dist/auth/sqlite/authDb.d.ts +18 -0
- package/server/dist/auth/sqlite/authDb.js +26 -0
- package/server/dist/auth/sqlite/authDb.js.map +1 -0
- package/server/dist/auth/sqlite/legacyImport.d.ts +22 -0
- package/server/dist/auth/sqlite/legacyImport.js +208 -0
- package/server/dist/auth/sqlite/legacyImport.js.map +1 -0
- package/server/dist/auth/sqlite/schema.d.ts +11 -0
- package/server/dist/auth/sqlite/schema.js +47 -0
- package/server/dist/auth/sqlite/schema.js.map +1 -0
- package/server/dist/auth/sqlite/sqliteUserStore.d.ts +12 -0
- package/server/dist/auth/sqlite/sqliteUserStore.js +194 -0
- package/server/dist/auth/sqlite/sqliteUserStore.js.map +1 -0
- package/server/dist/auth/userStore.d.ts +19 -0
- package/server/dist/auth/userStore.js +26 -0
- package/server/dist/auth/userStore.js.map +1 -0
- package/server/dist/auth/userTypes.d.ts +13 -0
- package/server/dist/auth/userTypes.js +3 -0
- package/server/dist/auth/userTypes.js.map +1 -0
- package/server/dist/chat/attachmentPathRedaction.d.ts +5 -0
- package/server/dist/chat/attachmentPathRedaction.js +67 -0
- package/server/dist/chat/attachmentPathRedaction.js.map +1 -0
- package/server/dist/chat/chatItemEnricher.d.ts +5 -0
- package/server/dist/chat/chatItemEnricher.js +40 -0
- package/server/dist/chat/chatItemEnricher.js.map +1 -0
- package/server/dist/chat/codexEventProjector.d.ts +33 -0
- package/server/dist/chat/codexEventProjector.js +482 -0
- package/server/dist/chat/codexEventProjector.js.map +1 -0
- package/server/dist/chat/contextUsageProjector.d.ts +10 -0
- package/server/dist/chat/contextUsageProjector.js +472 -0
- package/server/dist/chat/contextUsageProjector.js.map +1 -0
- package/server/dist/chat/fileChangeExtractor.d.ts +5 -0
- package/server/dist/chat/fileChangeExtractor.js +121 -0
- package/server/dist/chat/fileChangeExtractor.js.map +1 -0
- package/server/dist/chat/markdown/markdownAst.d.ts +5 -0
- package/server/dist/chat/markdown/markdownAst.js +154 -0
- package/server/dist/chat/markdown/markdownAst.js.map +1 -0
- package/server/dist/chat/systemToolCallSummary.d.ts +10 -0
- package/server/dist/chat/systemToolCallSummary.js +112 -0
- package/server/dist/chat/systemToolCallSummary.js.map +1 -0
- package/server/dist/chat/terminal/terminalPlainText.d.ts +14 -0
- package/server/dist/chat/terminal/terminalPlainText.js +139 -0
- package/server/dist/chat/terminal/terminalPlainText.js.map +1 -0
- package/server/dist/chat/textMetrics.d.ts +9 -0
- package/server/dist/chat/textMetrics.js +24 -0
- package/server/dist/chat/textMetrics.js.map +1 -0
- package/server/dist/chat/threadTurnsProjector.d.ts +12 -0
- package/server/dist/chat/threadTurnsProjector.js +292 -0
- package/server/dist/chat/threadTurnsProjector.js.map +1 -0
- package/server/dist/chat/todoPlanProjector.d.ts +8 -0
- package/server/dist/chat/todoPlanProjector.js +94 -0
- package/server/dist/chat/todoPlanProjector.js.map +1 -0
- package/server/dist/chat/todoPlanTypes.d.ts +21 -0
- package/server/dist/chat/todoPlanTypes.js +3 -0
- package/server/dist/chat/todoPlanTypes.js.map +1 -0
- package/server/dist/chat/types.d.ts +138 -0
- package/server/dist/chat/types.js +3 -0
- package/server/dist/chat/types.js.map +1 -0
- package/server/dist/cli/configArg.d.ts +21 -0
- package/server/dist/cli/configArg.js +70 -0
- package/server/dist/cli/configArg.js.map +1 -0
- package/server/dist/codex/appServerProcess.d.ts +24 -0
- package/server/dist/codex/appServerProcess.js +56 -0
- package/server/dist/codex/appServerProcess.js.map +1 -0
- package/server/dist/codex/cliArgs.d.ts +17 -0
- package/server/dist/codex/cliArgs.js +34 -0
- package/server/dist/codex/cliArgs.js.map +1 -0
- package/server/dist/codex/codexAppServer.d.ts +103 -0
- package/server/dist/codex/codexAppServer.js +206 -0
- package/server/dist/codex/codexAppServer.js.map +1 -0
- package/server/dist/codex/jsonl.d.ts +4 -0
- package/server/dist/codex/jsonl.js +23 -0
- package/server/dist/codex/jsonl.js.map +1 -0
- package/server/dist/codex/jsonrpc.d.ts +43 -0
- package/server/dist/codex/jsonrpc.js +96 -0
- package/server/dist/codex/jsonrpc.js.map +1 -0
- package/server/dist/config/serverConfig.d.ts +150 -0
- package/server/dist/config/serverConfig.js +64 -0
- package/server/dist/config/serverConfig.js.map +1 -0
- package/server/dist/env.d.ts +101 -0
- package/server/dist/env.js +523 -0
- package/server/dist/env.js.map +1 -0
- package/server/dist/history/http/historyRoutes.d.ts +18 -0
- package/server/dist/history/http/historyRoutes.js +67 -0
- package/server/dist/history/http/historyRoutes.js.map +1 -0
- package/server/dist/history/index.d.ts +24 -0
- package/server/dist/history/index.js +30 -0
- package/server/dist/history/index.js.map +1 -0
- package/server/dist/history/ingest/historyIngestService.d.ts +15 -0
- package/server/dist/history/ingest/historyIngestService.js +42 -0
- package/server/dist/history/ingest/historyIngestService.js.map +1 -0
- package/server/dist/history/projector/extractIds.d.ts +36 -0
- package/server/dist/history/projector/extractIds.js +111 -0
- package/server/dist/history/projector/extractIds.js.map +1 -0
- package/server/dist/history/projector/projectCodexEvent.d.ts +27 -0
- package/server/dist/history/projector/projectCodexEvent.js +845 -0
- package/server/dist/history/projector/projectCodexEvent.js.map +1 -0
- package/server/dist/history/query/historyQueryService.d.ts +34 -0
- package/server/dist/history/query/historyQueryService.js +170 -0
- package/server/dist/history/query/historyQueryService.js.map +1 -0
- package/server/dist/history/sqlite/schema.d.ts +11 -0
- package/server/dist/history/sqlite/schema.js +34 -0
- package/server/dist/history/sqlite/schema.js.map +1 -0
- package/server/dist/history/sqlite/sqliteHistoryStore.d.ts +69 -0
- package/server/dist/history/sqlite/sqliteHistoryStore.js +206 -0
- package/server/dist/history/sqlite/sqliteHistoryStore.js.map +1 -0
- package/server/dist/history/types.d.ts +29 -0
- package/server/dist/history/types.js +3 -0
- package/server/dist/history/types.js.map +1 -0
- package/server/dist/index.d.ts +1 -0
- package/server/dist/index.js +166 -0
- package/server/dist/index.js.map +1 -0
- package/server/dist/security/executionPolicy.d.ts +33 -0
- package/server/dist/security/executionPolicy.js +72 -0
- package/server/dist/security/executionPolicy.js.map +1 -0
- package/server/dist/security/origin.d.ts +11 -0
- package/server/dist/security/origin.js +40 -0
- package/server/dist/security/origin.js.map +1 -0
- package/server/dist/settings/sqliteUserSettingsStore.d.ts +12 -0
- package/server/dist/settings/sqliteUserSettingsStore.js +62 -0
- package/server/dist/settings/sqliteUserSettingsStore.js.map +1 -0
- package/server/dist/settings/userSettingsRoutes.d.ts +8 -0
- package/server/dist/settings/userSettingsRoutes.js +55 -0
- package/server/dist/settings/userSettingsRoutes.js.map +1 -0
- package/server/dist/settings/userSettingsStore.d.ts +19 -0
- package/server/dist/settings/userSettingsStore.js +26 -0
- package/server/dist/settings/userSettingsStore.js.map +1 -0
- package/server/dist/settings/userSettingsTypes.d.ts +70 -0
- package/server/dist/settings/userSettingsTypes.js +196 -0
- package/server/dist/settings/userSettingsTypes.js.map +1 -0
- package/server/dist/status/codexTaskTracker.d.ts +21 -0
- package/server/dist/status/codexTaskTracker.js +123 -0
- package/server/dist/status/codexTaskTracker.js.map +1 -0
- package/server/dist/status/threadContextUsage.d.ts +19 -0
- package/server/dist/status/threadContextUsage.js +229 -0
- package/server/dist/status/threadContextUsage.js.map +1 -0
- package/server/dist/threadList/threadListServerCache.d.ts +10 -0
- package/server/dist/threadList/threadListServerCache.js +42 -0
- package/server/dist/threadList/threadListServerCache.js.map +1 -0
- package/server/dist/tools/cwdSuggest.d.ts +11 -0
- package/server/dist/tools/cwdSuggest.js +128 -0
- package/server/dist/tools/cwdSuggest.js.map +1 -0
- package/server/dist/workspace/accessControl.d.ts +16 -0
- package/server/dist/workspace/accessControl.js +82 -0
- package/server/dist/workspace/accessControl.js.map +1 -0
- package/server/dist/workspace/sqliteUserWorkspaceStore.d.ts +12 -0
- package/server/dist/workspace/sqliteUserWorkspaceStore.js +82 -0
- package/server/dist/workspace/sqliteUserWorkspaceStore.js.map +1 -0
- package/server/dist/workspace/threadAccess.d.ts +19 -0
- package/server/dist/workspace/threadAccess.js +22 -0
- package/server/dist/workspace/threadAccess.js.map +1 -0
- package/server/dist/workspace/threadListVisibility.d.ts +25 -0
- package/server/dist/workspace/threadListVisibility.js +104 -0
- package/server/dist/workspace/threadListVisibility.js.map +1 -0
- package/server/dist/workspace/userWorkspaceRoutes.d.ts +7 -0
- package/server/dist/workspace/userWorkspaceRoutes.js +124 -0
- package/server/dist/workspace/userWorkspaceRoutes.js.map +1 -0
- package/server/dist/workspace/userWorkspaceStore.d.ts +12 -0
- package/server/dist/workspace/userWorkspaceStore.js +23 -0
- package/server/dist/workspace/userWorkspaceStore.js.map +1 -0
- package/server/dist/ws/socketIoBridge.d.ts +32 -0
- package/server/dist/ws/socketIoBridge.js +194 -0
- package/server/dist/ws/socketIoBridge.js.map +1 -0
- package/server/dist/ws/types.d.ts +113 -0
- package/server/dist/ws/types.js +3 -0
- package/server/dist/ws/types.js.map +1 -0
- package/server/dist/ws/wsHub.d.ts +119 -0
- package/server/dist/ws/wsHub.js +1259 -0
- package/server/dist/ws/wsHub.js.map +1 -0
- package/web/dist/assets/index-CY6cnwQz.js +174 -0
- package/web/dist/assets/index-DI7kJHr2.css +32 -0
- package/web/dist/favicon-mask.svg +9 -0
- package/web/dist/favicon.svg +26 -0
- package/web/dist/index.html +75 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const http_1 = __importDefault(require("http"));
|
|
7
|
+
const node_net_1 = __importDefault(require("node:net"));
|
|
8
|
+
const socket_io_1 = require("socket.io");
|
|
9
|
+
const ws_1 = require("ws");
|
|
10
|
+
const bootstrapAdmin_1 = require("./auth/bootstrapAdmin");
|
|
11
|
+
const adminInit_1 = require("./auth/adminInit");
|
|
12
|
+
const userStore_1 = require("./auth/userStore");
|
|
13
|
+
const codexAppServer_1 = require("./codex/codexAppServer");
|
|
14
|
+
const env_1 = require("./env");
|
|
15
|
+
const app_1 = require("./app");
|
|
16
|
+
const authDb_1 = require("./auth/sqlite/authDb");
|
|
17
|
+
const legacyImport_1 = require("./auth/sqlite/legacyImport");
|
|
18
|
+
const history_1 = require("./history");
|
|
19
|
+
const codexTaskTracker_1 = require("./status/codexTaskTracker");
|
|
20
|
+
const wsHub_1 = require("./ws/wsHub");
|
|
21
|
+
const socketIoBridge_1 = require("./ws/socketIoBridge");
|
|
22
|
+
const userWorkspaceStore_1 = require("./workspace/userWorkspaceStore");
|
|
23
|
+
const userSettingsStore_1 = require("./settings/userSettingsStore");
|
|
24
|
+
const configArg_1 = require("./cli/configArg");
|
|
25
|
+
async function main() {
|
|
26
|
+
// 启动最早阶段先处理命令行 config 参数,确保后续 env 读取拿到最终配置。
|
|
27
|
+
// Parse config arg at startup earliest stage so env readers see final value.
|
|
28
|
+
(0, configArg_1.applyConfigArgToEnv)(process.argv.slice(2));
|
|
29
|
+
const host = (0, env_1.getHost)();
|
|
30
|
+
const port = (0, env_1.getPort)();
|
|
31
|
+
const sessionSecret = (0, env_1.getSessionToken)();
|
|
32
|
+
const startedAtMs = Date.now();
|
|
33
|
+
// 认证配置统一写入 auth.db,避免多 JSON 文件分散维护。
|
|
34
|
+
const authDb = (0, authDb_1.createAuthDb)({ dbPath: (0, env_1.getCodexWebAuthDbPath)() });
|
|
35
|
+
// DB 为空时尝试从 legacy JSON 导入,保证升级不丢历史账号与工作区配置。
|
|
36
|
+
await (0, legacyImport_1.maybeImportLegacyJsonSettings)({
|
|
37
|
+
authDb,
|
|
38
|
+
usersFilePath: (0, env_1.getCodexWebUsersFile)(),
|
|
39
|
+
userWorkspacesFilePath: (0, env_1.getCodexWebUserWorkspacesFile)(),
|
|
40
|
+
});
|
|
41
|
+
const userStore = await (0, userStore_1.createUserStore)({ authDb });
|
|
42
|
+
// 当监听非 loopback 时,阻止“默认口令”对外暴露(误部署防呆)。
|
|
43
|
+
await assertSafeAdminPasswordForHost(host, userStore);
|
|
44
|
+
// 用户工作目录仓库:与鉴权 workspaces 分离,避免语义耦合。
|
|
45
|
+
const userWorkspaceStore = await (0, userWorkspaceStore_1.createUserWorkspaceStore)({ authDb });
|
|
46
|
+
// 用户配置仓库:用于跨端同步主题/模型等偏好设置。
|
|
47
|
+
const userSettingsStore = await (0, userSettingsStore_1.createUserSettingsStore)({ authDb });
|
|
48
|
+
await (0, bootstrapAdmin_1.bootstrapAdmin)(userStore);
|
|
49
|
+
const codex = new codexAppServer_1.CodexAppServer({
|
|
50
|
+
codexBin: (0, env_1.getCodexBin)(),
|
|
51
|
+
cwd: env_1.WORKSPACE_ROOT,
|
|
52
|
+
historyPersistence: (0, env_1.getCodexHistoryPersistence)(),
|
|
53
|
+
disableResponseStorage: (0, env_1.getCodexDisableResponseStorage)(),
|
|
54
|
+
});
|
|
55
|
+
const codexTasks = new codexTaskTracker_1.CodexTaskTracker();
|
|
56
|
+
const unsubscribeCodexTasks = codex.onNotification((n) => codexTasks.onNotification(n));
|
|
57
|
+
// 按模式决定是否启用历史写入运行时。
|
|
58
|
+
const historyRuntime = (0, history_1.createWebHistoryRuntime)({
|
|
59
|
+
mode: (0, env_1.getWebHistoryMode)(),
|
|
60
|
+
dbPath: (0, env_1.getWebHistoryDbPath)(),
|
|
61
|
+
});
|
|
62
|
+
let getWsClients = () => 0;
|
|
63
|
+
const getStatusSnapshot = () => ({
|
|
64
|
+
codex: codex.getStatus(),
|
|
65
|
+
codexTask: codexTasks.getSnapshot(),
|
|
66
|
+
ws: { clients: getWsClients() },
|
|
67
|
+
});
|
|
68
|
+
const app = (0, app_1.createApp)({
|
|
69
|
+
sessionSecret,
|
|
70
|
+
startedAtMs,
|
|
71
|
+
getStatusSnapshot,
|
|
72
|
+
codex,
|
|
73
|
+
userStore,
|
|
74
|
+
userWorkspaceStore,
|
|
75
|
+
userSettingsStore,
|
|
76
|
+
historyQuery: historyRuntime?.query ?? null,
|
|
77
|
+
});
|
|
78
|
+
const server = http_1.default.createServer(app);
|
|
79
|
+
/**
|
|
80
|
+
* 注意:不要用 `new WebSocketServer({ server, path: "/ws" })`。
|
|
81
|
+
* 因为 ws 库在 path 不匹配时会对所有 upgrade 请求直接回 400(Bad Request),
|
|
82
|
+
* 会误伤 Socket.IO 的 websocket upgrade(路径为 `/socket.io`)。
|
|
83
|
+
*
|
|
84
|
+
* 这里使用 `noServer` 模式,仅在 `/ws` 路径手动接管 upgrade。
|
|
85
|
+
*/
|
|
86
|
+
const wss = new ws_1.WebSocketServer({ noServer: true });
|
|
87
|
+
server.on("upgrade", (req, socket, head) => {
|
|
88
|
+
try {
|
|
89
|
+
const url = new URL(req.url ?? "", "http://localhost");
|
|
90
|
+
if (url.pathname !== "/ws")
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
97
|
+
wss.emit("connection", ws, req);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
getWsClients = () => wss.clients.size;
|
|
101
|
+
const io = new socket_io_1.Server(server, {
|
|
102
|
+
path: "/socket.io",
|
|
103
|
+
transports: ["websocket", "polling"],
|
|
104
|
+
});
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
106
|
+
const hub = new wsHub_1.WsHub(wss, codex, sessionSecret, getStatusSnapshot, {
|
|
107
|
+
getActiveTurnIds: (threadId) => codexTasks.getActiveTurnIds(threadId),
|
|
108
|
+
userStore,
|
|
109
|
+
historyIngest: historyRuntime?.ingest ?? null,
|
|
110
|
+
});
|
|
111
|
+
const socketIoBridge = (0, socketIoBridge_1.createSocketIoBridge)({
|
|
112
|
+
ioServer: io,
|
|
113
|
+
upstreamWsUrlFactory: ({ host: bridgeHost, port: bridgePort, rawWsPath }) => `ws://${bridgeHost}:${bridgePort}${rawWsPath}`,
|
|
114
|
+
});
|
|
115
|
+
server.listen(port, host, () => {
|
|
116
|
+
// eslint-disable-next-line no-console
|
|
117
|
+
console.log(`codex-cli-web server listening on http://${host}:${port}`);
|
|
118
|
+
});
|
|
119
|
+
function shutdown(signal) {
|
|
120
|
+
// eslint-disable-next-line no-console
|
|
121
|
+
console.log(`shutting down (${signal})`);
|
|
122
|
+
socketIoBridge.dispose();
|
|
123
|
+
io.close();
|
|
124
|
+
wss.close();
|
|
125
|
+
server.close(() => process.exit(0));
|
|
126
|
+
unsubscribeCodexTasks();
|
|
127
|
+
hub.dispose();
|
|
128
|
+
void historyRuntime?.close();
|
|
129
|
+
codex.dispose();
|
|
130
|
+
}
|
|
131
|
+
process.on("SIGINT", shutdown);
|
|
132
|
+
process.on("SIGTERM", shutdown);
|
|
133
|
+
}
|
|
134
|
+
main().catch((err) => {
|
|
135
|
+
// eslint-disable-next-line no-console
|
|
136
|
+
console.error(err);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
});
|
|
139
|
+
/**
|
|
140
|
+
* 判断监听地址是否为 loopback(仅本机可访问)。
|
|
141
|
+
*/
|
|
142
|
+
function isLoopbackHost(host) {
|
|
143
|
+
const normalized = String(host ?? "").trim().toLowerCase();
|
|
144
|
+
if (!normalized)
|
|
145
|
+
return true;
|
|
146
|
+
if (normalized === "localhost")
|
|
147
|
+
return true;
|
|
148
|
+
const ipVersion = node_net_1.default.isIP(normalized);
|
|
149
|
+
if (ipVersion === 4)
|
|
150
|
+
return normalized.startsWith("127.");
|
|
151
|
+
if (ipVersion === 6)
|
|
152
|
+
return normalized === "::1";
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* 当服务监听非 loopback 时,要求管理员初始化已完成,避免未初始化实例对外暴露。
|
|
157
|
+
*/
|
|
158
|
+
async function assertSafeAdminPasswordForHost(host, userStore) {
|
|
159
|
+
if (isLoopbackHost(host))
|
|
160
|
+
return;
|
|
161
|
+
const state = await (0, adminInit_1.readAdminInitState)(userStore);
|
|
162
|
+
if (!state.requiresSetup)
|
|
163
|
+
return;
|
|
164
|
+
throw new Error("Refusing to bind to a non-loopback host before admin initialization is completed on loopback.");
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { UserRole } from "../auth/userTypes";
|
|
2
|
+
/**
|
|
3
|
+
* Codex 执行审批策略:
|
|
4
|
+
* - `never` 风险最高(不做审批);
|
|
5
|
+
* - 其余策略均比 `never` 更严格。
|
|
6
|
+
*/
|
|
7
|
+
export type ApprovalPolicy = "untrusted" | "on-failure" | "on-request" | "never";
|
|
8
|
+
/**
|
|
9
|
+
* Codex 沙箱策略:
|
|
10
|
+
* - `danger-full-access` 风险最高(不受沙箱限制);
|
|
11
|
+
* - `workspace-write`/`read-only` 属于受控范围。
|
|
12
|
+
*/
|
|
13
|
+
export type SandboxMode = "read-only" | "workspace-write" | "danger-full-access";
|
|
14
|
+
/**
|
|
15
|
+
* 按用户角色收敛 approvalPolicy:
|
|
16
|
+
* - admin:允许使用请求值(若合法),否则使用 fallback;
|
|
17
|
+
* - member:仅允许 MEMBER_ALLOWED_APPROVAL_POLICIES;请求值或 fallback 不安全则回退到 MEMBER_FALLBACK_APPROVAL_POLICY。
|
|
18
|
+
*/
|
|
19
|
+
export declare function coerceApprovalPolicyForUser(opts: {
|
|
20
|
+
userRole: UserRole;
|
|
21
|
+
requested: unknown;
|
|
22
|
+
fallback: ApprovalPolicy;
|
|
23
|
+
}): ApprovalPolicy;
|
|
24
|
+
/**
|
|
25
|
+
* 按用户角色收敛 sandbox:
|
|
26
|
+
* - admin:允许使用请求值(若合法),否则使用 fallback;
|
|
27
|
+
* - member:仅允许 MEMBER_ALLOWED_SANDBOX_MODES;请求值或 fallback 不安全则回退到 MEMBER_FALLBACK_SANDBOX_MODE。
|
|
28
|
+
*/
|
|
29
|
+
export declare function coerceSandboxModeForUser(opts: {
|
|
30
|
+
userRole: UserRole;
|
|
31
|
+
requested: unknown;
|
|
32
|
+
fallback: SandboxMode;
|
|
33
|
+
}): SandboxMode;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.coerceApprovalPolicyForUser = coerceApprovalPolicyForUser;
|
|
4
|
+
exports.coerceSandboxModeForUser = coerceSandboxModeForUser;
|
|
5
|
+
const roles_1 = require("../auth/roles");
|
|
6
|
+
/**
|
|
7
|
+
* member 允许的审批策略集合(显式禁用 `never`)。
|
|
8
|
+
*/
|
|
9
|
+
const MEMBER_ALLOWED_APPROVAL_POLICIES = new Set(["untrusted", "on-failure", "on-request"]);
|
|
10
|
+
/**
|
|
11
|
+
* member 允许的沙箱策略集合(显式禁用 `danger-full-access`)。
|
|
12
|
+
*/
|
|
13
|
+
const MEMBER_ALLOWED_SANDBOX_MODES = new Set(["read-only", "workspace-write"]);
|
|
14
|
+
/**
|
|
15
|
+
* 当请求值/默认值不安全时,member 的审批策略兜底值。
|
|
16
|
+
*/
|
|
17
|
+
const MEMBER_FALLBACK_APPROVAL_POLICY = "on-request";
|
|
18
|
+
/**
|
|
19
|
+
* 当请求值/默认值不安全时,member 的沙箱策略兜底值。
|
|
20
|
+
*/
|
|
21
|
+
const MEMBER_FALLBACK_SANDBOX_MODE = "workspace-write";
|
|
22
|
+
/**
|
|
23
|
+
* 解析客户端上报的 approvalPolicy;非法值返回 null。
|
|
24
|
+
*/
|
|
25
|
+
function parseApprovalPolicy(requested) {
|
|
26
|
+
const raw = typeof requested === "string" ? requested.trim() : "";
|
|
27
|
+
if (raw === "untrusted" || raw === "on-failure" || raw === "on-request" || raw === "never")
|
|
28
|
+
return raw;
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 解析客户端上报的 sandbox;非法值返回 null。
|
|
33
|
+
*/
|
|
34
|
+
function parseSandboxMode(requested) {
|
|
35
|
+
const raw = typeof requested === "string" ? requested.trim() : "";
|
|
36
|
+
if (raw === "read-only" || raw === "workspace-write" || raw === "danger-full-access")
|
|
37
|
+
return raw;
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 按用户角色收敛 approvalPolicy:
|
|
42
|
+
* - admin:允许使用请求值(若合法),否则使用 fallback;
|
|
43
|
+
* - member:仅允许 MEMBER_ALLOWED_APPROVAL_POLICIES;请求值或 fallback 不安全则回退到 MEMBER_FALLBACK_APPROVAL_POLICY。
|
|
44
|
+
*/
|
|
45
|
+
function coerceApprovalPolicyForUser(opts) {
|
|
46
|
+
const requestedPolicy = parseApprovalPolicy(opts.requested);
|
|
47
|
+
if ((0, roles_1.isAdminRole)(opts.userRole)) {
|
|
48
|
+
return requestedPolicy ?? opts.fallback;
|
|
49
|
+
}
|
|
50
|
+
if (requestedPolicy && MEMBER_ALLOWED_APPROVAL_POLICIES.has(requestedPolicy))
|
|
51
|
+
return requestedPolicy;
|
|
52
|
+
if (MEMBER_ALLOWED_APPROVAL_POLICIES.has(opts.fallback))
|
|
53
|
+
return opts.fallback;
|
|
54
|
+
return MEMBER_FALLBACK_APPROVAL_POLICY;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 按用户角色收敛 sandbox:
|
|
58
|
+
* - admin:允许使用请求值(若合法),否则使用 fallback;
|
|
59
|
+
* - member:仅允许 MEMBER_ALLOWED_SANDBOX_MODES;请求值或 fallback 不安全则回退到 MEMBER_FALLBACK_SANDBOX_MODE。
|
|
60
|
+
*/
|
|
61
|
+
function coerceSandboxModeForUser(opts) {
|
|
62
|
+
const requestedSandbox = parseSandboxMode(opts.requested);
|
|
63
|
+
if ((0, roles_1.isAdminRole)(opts.userRole)) {
|
|
64
|
+
return requestedSandbox ?? opts.fallback;
|
|
65
|
+
}
|
|
66
|
+
if (requestedSandbox && MEMBER_ALLOWED_SANDBOX_MODES.has(requestedSandbox))
|
|
67
|
+
return requestedSandbox;
|
|
68
|
+
if (MEMBER_ALLOWED_SANDBOX_MODES.has(opts.fallback))
|
|
69
|
+
return opts.fallback;
|
|
70
|
+
return MEMBER_FALLBACK_SANDBOX_MODE;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=executionPolicy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executionPolicy.js","sourceRoot":"","sources":["../../src/security/executionPolicy.ts"],"names":[],"mappings":";;AA4DA,kEASC;AAOD,4DASC;AArFD,yCAA4C;AAiB5C;;GAEG;AACH,MAAM,gCAAgC,GAAG,IAAI,GAAG,CAAiB,CAAC,WAAW,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;AAE5G;;GAEG;AACH,MAAM,4BAA4B,GAAG,IAAI,GAAG,CAAc,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC,CAAC;AAE5F;;GAEG;AACH,MAAM,+BAA+B,GAAmB,YAAY,CAAC;AAErE;;GAEG;AACH,MAAM,4BAA4B,GAAgB,iBAAiB,CAAC;AAEpE;;GAEG;AACH,SAAS,mBAAmB,CAAC,SAAkB;IAC7C,MAAM,GAAG,GAAG,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAClE,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,YAAY,IAAI,GAAG,KAAK,YAAY,IAAI,GAAG,KAAK,OAAO;QAAE,OAAO,GAAG,CAAC;IACvG,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,SAAkB;IAC1C,MAAM,GAAG,GAAG,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAClE,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,iBAAiB,IAAI,GAAG,KAAK,oBAAoB;QAAE,OAAO,GAAG,CAAC;IACjG,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,SAAgB,2BAA2B,CAAC,IAA0E;IACpH,MAAM,eAAe,GAAG,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC5D,IAAI,IAAA,mBAAW,EAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,OAAO,eAAe,IAAI,IAAI,CAAC,QAAQ,CAAC;IAC1C,CAAC;IAED,IAAI,eAAe,IAAI,gCAAgC,CAAC,GAAG,CAAC,eAAe,CAAC;QAAE,OAAO,eAAe,CAAC;IACrG,IAAI,gCAAgC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC,QAAQ,CAAC;IAC9E,OAAO,+BAA+B,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACH,SAAgB,wBAAwB,CAAC,IAAuE;IAC9G,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC1D,IAAI,IAAA,mBAAW,EAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,OAAO,gBAAgB,IAAI,IAAI,CAAC,QAAQ,CAAC;IAC3C,CAAC;IAED,IAAI,gBAAgB,IAAI,4BAA4B,CAAC,GAAG,CAAC,gBAAgB,CAAC;QAAE,OAAO,gBAAgB,CAAC;IACpG,IAAI,4BAA4B,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC,QAAQ,CAAC;IAC1E,OAAO,4BAA4B,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function isLoopbackAddress(address: string | undefined): boolean;
|
|
2
|
+
export type OriginPolicy = {
|
|
3
|
+
strict: boolean;
|
|
4
|
+
allowedOrigins: string[];
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* 判断请求 Origin 是否允许。
|
|
8
|
+
* - strict=false:不做限制;
|
|
9
|
+
* - strict=true:若存在 Origin,则必须命中 allowlist;Origin 为空时放行(兼容非浏览器客户端)。
|
|
10
|
+
*/
|
|
11
|
+
export declare function isAllowedOrigin(origin: string | undefined, policy: OriginPolicy): boolean;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isLoopbackAddress = isLoopbackAddress;
|
|
4
|
+
exports.isAllowedOrigin = isAllowedOrigin;
|
|
5
|
+
function isLoopbackAddress(address) {
|
|
6
|
+
if (!address)
|
|
7
|
+
return false;
|
|
8
|
+
if (address === "127.0.0.1" || address === "::1")
|
|
9
|
+
return true;
|
|
10
|
+
if (address.startsWith("::ffff:127."))
|
|
11
|
+
return true;
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
function normalizeOriginHeader(origin) {
|
|
15
|
+
const raw = String(origin ?? "").trim();
|
|
16
|
+
if (!raw)
|
|
17
|
+
return null;
|
|
18
|
+
if (raw.toLowerCase() === "null")
|
|
19
|
+
return null;
|
|
20
|
+
try {
|
|
21
|
+
return new URL(raw).origin;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 判断请求 Origin 是否允许。
|
|
29
|
+
* - strict=false:不做限制;
|
|
30
|
+
* - strict=true:若存在 Origin,则必须命中 allowlist;Origin 为空时放行(兼容非浏览器客户端)。
|
|
31
|
+
*/
|
|
32
|
+
function isAllowedOrigin(origin, policy) {
|
|
33
|
+
if (!policy.strict)
|
|
34
|
+
return true;
|
|
35
|
+
const normalized = normalizeOriginHeader(origin);
|
|
36
|
+
if (!normalized)
|
|
37
|
+
return true;
|
|
38
|
+
return policy.allowedOrigins.includes(normalized);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=origin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"origin.js","sourceRoot":"","sources":["../../src/security/origin.ts"],"names":[],"mappings":";;AAAA,8CAKC;AAuBD,0CAKC;AAjCD,SAAgB,iBAAiB,CAAC,OAA2B;IAC3D,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAC3B,IAAI,OAAO,KAAK,WAAW,IAAI,OAAO,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IAC9D,IAAI,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC;QAAE,OAAO,IAAI,CAAC;IACnD,OAAO,KAAK,CAAC;AACf,CAAC;AAOD,SAAS,qBAAqB,CAAC,MAA0B;IACvD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IAC9C,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAgB,eAAe,CAAC,MAA0B,EAAE,MAAoB;IAC9E,IAAI,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAChC,MAAM,UAAU,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACjD,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAC7B,OAAO,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AuthDb } from "../auth/sqlite/authDb";
|
|
2
|
+
import type { UserSettingsStore } from "./userSettingsStore";
|
|
3
|
+
/**
|
|
4
|
+
* SQLite 用户配置仓库创建参数。
|
|
5
|
+
*/
|
|
6
|
+
export type CreateSqliteUserSettingsStoreOptions = {
|
|
7
|
+
authDb: AuthDb;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* 基于 SQLite 创建用户配置仓库。
|
|
11
|
+
*/
|
|
12
|
+
export declare function createSqliteUserSettingsStore(options: CreateSqliteUserSettingsStoreOptions): UserSettingsStore;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSqliteUserSettingsStore = createSqliteUserSettingsStore;
|
|
4
|
+
const userSettingsTypes_1 = require("./userSettingsTypes");
|
|
5
|
+
/**
|
|
6
|
+
* 规范化用户名输入。
|
|
7
|
+
*/
|
|
8
|
+
function normalizeUsername(username) {
|
|
9
|
+
return String(username ?? "").trim();
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 基于 SQLite 创建用户配置仓库。
|
|
13
|
+
*/
|
|
14
|
+
function createSqliteUserSettingsStore(options) {
|
|
15
|
+
const db = options.authDb.db;
|
|
16
|
+
const getSettingsByUsernameStatement = db.prepare(`
|
|
17
|
+
SELECT settings_json
|
|
18
|
+
FROM auth_user_settings
|
|
19
|
+
WHERE username = ?
|
|
20
|
+
LIMIT 1
|
|
21
|
+
`);
|
|
22
|
+
const upsertSettingsStatement = db.prepare(`
|
|
23
|
+
INSERT INTO auth_user_settings (
|
|
24
|
+
username, settings_json, updated_at_ms
|
|
25
|
+
) VALUES (
|
|
26
|
+
@username, @settingsJson, @updatedAtMs
|
|
27
|
+
)
|
|
28
|
+
ON CONFLICT(username) DO UPDATE SET
|
|
29
|
+
settings_json = excluded.settings_json,
|
|
30
|
+
updated_at_ms = excluded.updated_at_ms
|
|
31
|
+
`);
|
|
32
|
+
return {
|
|
33
|
+
async getUserSettings(usernameRaw) {
|
|
34
|
+
const username = normalizeUsername(usernameRaw);
|
|
35
|
+
if (!username)
|
|
36
|
+
return (0, userSettingsTypes_1.normalizeUserSettings)(null);
|
|
37
|
+
const row = getSettingsByUsernameStatement.get(username);
|
|
38
|
+
if (!row)
|
|
39
|
+
return (0, userSettingsTypes_1.normalizeUserSettings)(null);
|
|
40
|
+
try {
|
|
41
|
+
const parsed = JSON.parse(String(row.settings_json ?? "{}"));
|
|
42
|
+
return (0, userSettingsTypes_1.normalizeUserSettings)(parsed);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return (0, userSettingsTypes_1.normalizeUserSettings)(null);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
async replaceUserSettings(usernameRaw, settingsRaw) {
|
|
49
|
+
const username = normalizeUsername(usernameRaw);
|
|
50
|
+
if (!username)
|
|
51
|
+
throw new Error("username is required");
|
|
52
|
+
const normalizedSettings = (0, userSettingsTypes_1.normalizeUserSettings)(settingsRaw);
|
|
53
|
+
upsertSettingsStatement.run({
|
|
54
|
+
username,
|
|
55
|
+
settingsJson: JSON.stringify(normalizedSettings),
|
|
56
|
+
updatedAtMs: Date.now(),
|
|
57
|
+
});
|
|
58
|
+
return normalizedSettings;
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=sqliteUserSettingsStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqliteUserSettingsStore.js","sourceRoot":"","sources":["../../src/settings/sqliteUserSettingsStore.ts"],"names":[],"mappings":";;AA4BA,sEAkDC;AA5ED,2DAA4D;AAgB5D;;GAEG;AACH,SAAS,iBAAiB,CAAC,QAAgB;IACzC,OAAO,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,SAAgB,6BAA6B,CAAC,OAA6C;IACzF,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;IAE7B,MAAM,8BAA8B,GAAG,EAAE,CAAC,OAAO,CAAC;;;;;GAKjD,CAAC,CAAC;IAEH,MAAM,uBAAuB,GAAG,EAAE,CAAC,OAAO,CAAC;;;;;;;;;GAS1C,CAAC,CAAC;IAEH,OAAO;QACL,KAAK,CAAC,eAAe,CAAC,WAAmB;YACvC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;YAChD,IAAI,CAAC,QAAQ;gBAAE,OAAO,IAAA,yCAAqB,EAAC,IAAI,CAAC,CAAC;YAElD,MAAM,GAAG,GAAG,8BAA8B,CAAC,GAAG,CAAC,QAAQ,CAAgC,CAAC;YACxF,IAAI,CAAC,GAAG;gBAAE,OAAO,IAAA,yCAAqB,EAAC,IAAI,CAAC,CAAC;YAE7C,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,IAAI,IAAI,CAAC,CAAC,CAAC;gBAC7D,OAAO,IAAA,yCAAqB,EAAC,MAAM,CAAC,CAAC;YACvC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAA,yCAAqB,EAAC,IAAI,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;QAED,KAAK,CAAC,mBAAmB,CAAC,WAAW,EAAE,WAAW;YAChD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;YAChD,IAAI,CAAC,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;YAEvD,MAAM,kBAAkB,GAAG,IAAA,yCAAqB,EAAC,WAAW,CAAC,CAAC;YAC9D,uBAAuB,CAAC,GAAG,CAAC;gBAC1B,QAAQ;gBACR,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC;gBAChD,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE;aACxB,CAAC,CAAC;YACH,OAAO,kBAAkB,CAAC;QAC5B,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createUserSettingsRoutes = createUserSettingsRoutes;
|
|
7
|
+
const express_1 = __importDefault(require("express"));
|
|
8
|
+
const userSettingsTypes_1 = require("./userSettingsTypes");
|
|
9
|
+
/**
|
|
10
|
+
* 从请求中读取已鉴权用户名;缺失时返回 401。
|
|
11
|
+
*/
|
|
12
|
+
function resolveAuthenticatedUsername(req, res) {
|
|
13
|
+
const username = String(req?.user?.username ?? "").trim();
|
|
14
|
+
if (!username) {
|
|
15
|
+
res.status(401).json({ ok: false, error: "unauthorized" });
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return username;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 创建用户配置同步 API 路由。
|
|
22
|
+
*/
|
|
23
|
+
function createUserSettingsRoutes(opts) {
|
|
24
|
+
const router = express_1.default.Router();
|
|
25
|
+
router.get("/", async (req, res) => {
|
|
26
|
+
const username = resolveAuthenticatedUsername(req, res);
|
|
27
|
+
if (!username)
|
|
28
|
+
return;
|
|
29
|
+
try {
|
|
30
|
+
const settings = await opts.userSettingsStore.getUserSettings(username);
|
|
31
|
+
res.json({ ok: true, settings });
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
res.status(500).json({ ok: false, error: "request_failed", details: String(error) });
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
router.put("/", async (req, res) => {
|
|
38
|
+
const username = resolveAuthenticatedUsername(req, res);
|
|
39
|
+
if (!username)
|
|
40
|
+
return;
|
|
41
|
+
try {
|
|
42
|
+
const body = (req.body ?? {});
|
|
43
|
+
// 兼容两种 payload:`{ settings: ... }` 或直接顶层 settings 对象。
|
|
44
|
+
const rawSettings = Object.prototype.hasOwnProperty.call(body, "settings") ? body.settings : body;
|
|
45
|
+
const normalizedSettings = (0, userSettingsTypes_1.normalizeUserSettings)(rawSettings);
|
|
46
|
+
const settings = await opts.userSettingsStore.replaceUserSettings(username, normalizedSettings);
|
|
47
|
+
res.json({ ok: true, settings });
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
res.status(500).json({ ok: false, error: "request_failed", details: String(error) });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return router;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=userSettingsRoutes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"userSettingsRoutes.js","sourceRoot":"","sources":["../../src/settings/userSettingsRoutes.ts"],"names":[],"mappings":";;;;;AAoBA,4DAgCC;AAnDD,sDAA8B;AAE9B,2DAA4D;AAE5D;;GAEG;AACH,SAAS,4BAA4B,CAAC,GAAQ,EAAE,GAAa;IAC3D,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1D,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,SAAgB,wBAAwB,CAAC,IAA8C;IACrF,MAAM,MAAM,GAAG,iBAAO,CAAC,MAAM,EAAE,CAAC;IAEhC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACjC,MAAM,QAAQ,GAAG,4BAA4B,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACxD,IAAI,CAAC,QAAQ;YAAE,OAAO;QAEtB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;YACxE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACvF,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACjC,MAAM,QAAQ,GAAG,4BAA4B,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACxD,IAAI,CAAC,QAAQ;YAAE,OAAO;QAEtB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAQ,CAAC;YACrC,sDAAsD;YACtD,MAAM,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;YAClG,MAAM,kBAAkB,GAAG,IAAA,yCAAqB,EAAC,WAAW,CAAC,CAAC;YAC9D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,mBAAmB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;YAChG,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACvF,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type AuthDb } from "../auth/sqlite/authDb";
|
|
2
|
+
import type { UserSettings } from "./userSettingsTypes";
|
|
3
|
+
/**
|
|
4
|
+
* 用户配置仓库接口(支持跨端同步)。
|
|
5
|
+
*/
|
|
6
|
+
export type UserSettingsStore = {
|
|
7
|
+
getUserSettings(username: string): Promise<UserSettings>;
|
|
8
|
+
replaceUserSettings(username: string, settings: UserSettings): Promise<UserSettings>;
|
|
9
|
+
};
|
|
10
|
+
type CreateUserSettingsStoreOptions = {
|
|
11
|
+
filePath?: string;
|
|
12
|
+
dbPath?: string;
|
|
13
|
+
authDb?: AuthDb;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* 创建用户配置仓库(当前默认使用 SQLite)。
|
|
17
|
+
*/
|
|
18
|
+
export declare function createUserSettingsStore(opts: CreateUserSettingsStoreOptions): Promise<UserSettingsStore>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createUserSettingsStore = createUserSettingsStore;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const authDb_1 = require("../auth/sqlite/authDb");
|
|
9
|
+
const sqliteUserSettingsStore_1 = require("./sqliteUserSettingsStore");
|
|
10
|
+
/**
|
|
11
|
+
* 解析用户配置仓库 SQLite 路径。
|
|
12
|
+
*/
|
|
13
|
+
function resolveUserSettingsStoreDbPath(opts) {
|
|
14
|
+
const rawDbPath = String(opts.dbPath ?? opts.filePath ?? "").trim();
|
|
15
|
+
if (!rawDbPath)
|
|
16
|
+
throw new Error("user settings store dbPath is required");
|
|
17
|
+
return node_path_1.default.resolve(rawDbPath);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 创建用户配置仓库(当前默认使用 SQLite)。
|
|
21
|
+
*/
|
|
22
|
+
async function createUserSettingsStore(opts) {
|
|
23
|
+
const authDb = opts.authDb ?? (0, authDb_1.createAuthDb)({ dbPath: resolveUserSettingsStoreDbPath(opts) });
|
|
24
|
+
return (0, sqliteUserSettingsStore_1.createSqliteUserSettingsStore)({ authDb });
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=userSettingsStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"userSettingsStore.js","sourceRoot":"","sources":["../../src/settings/userSettingsStore.ts"],"names":[],"mappings":";;;;;AAoCA,0DAGC;AAvCD,0DAA6B;AAC7B,kDAAkE;AAClE,uEAA0E;AAsB1E;;GAEG;AACH,SAAS,8BAA8B,CAAC,IAAoC;IAC1E,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACpE,IAAI,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC1E,OAAO,mBAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AACjC,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,uBAAuB,CAAC,IAAoC;IAChF,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAA,qBAAY,EAAC,EAAE,MAAM,EAAE,8BAA8B,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7F,OAAO,IAAA,uDAA6B,EAAC,EAAE,MAAM,EAAE,CAAC,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 用户可同步的审批策略。
|
|
3
|
+
*/
|
|
4
|
+
export type UserApprovalPolicy = "untrusted" | "on-failure" | "on-request" | "never";
|
|
5
|
+
/**
|
|
6
|
+
* 用户可同步的语言配置。
|
|
7
|
+
*/
|
|
8
|
+
export type UserLocale = "zh-CN" | "en-US";
|
|
9
|
+
/**
|
|
10
|
+
* 用户可同步的沙箱模式。
|
|
11
|
+
*/
|
|
12
|
+
export type UserSandboxMode = "read-only" | "workspace-write" | "danger-full-access";
|
|
13
|
+
/**
|
|
14
|
+
* 线程级模型覆盖配置。
|
|
15
|
+
*/
|
|
16
|
+
export type UserThreadModelOverride = {
|
|
17
|
+
model?: string;
|
|
18
|
+
reasoningEffort?: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Web 默认模型配置。
|
|
22
|
+
*/
|
|
23
|
+
export type UserWebModelDefaults = {
|
|
24
|
+
model?: string;
|
|
25
|
+
reasoningEffort?: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* 线程级权限覆盖配置。
|
|
29
|
+
*/
|
|
30
|
+
export type UserThreadPermissionsOverride = {
|
|
31
|
+
approvalPolicy?: UserApprovalPolicy;
|
|
32
|
+
sandbox?: UserSandboxMode;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* 线程级协作模式覆盖配置。
|
|
36
|
+
*/
|
|
37
|
+
export type UserThreadCollaborationOverride = {
|
|
38
|
+
mode: "plan" | "default";
|
|
39
|
+
settings: {
|
|
40
|
+
model: string;
|
|
41
|
+
developer_instructions?: string | null;
|
|
42
|
+
reasoning_effort?: string | null;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* 用户配置持久化结构(用于跨端同步)。
|
|
47
|
+
*/
|
|
48
|
+
export type UserSettings = {
|
|
49
|
+
locale: UserLocale;
|
|
50
|
+
theme: "light" | "dark";
|
|
51
|
+
accentColor: string | null;
|
|
52
|
+
backgroundColor: string | null;
|
|
53
|
+
sidebarCollapsed: boolean;
|
|
54
|
+
compactView: boolean;
|
|
55
|
+
threadNameOverrides: Record<string, string>;
|
|
56
|
+
threadModelOverrides: Record<string, UserThreadModelOverride>;
|
|
57
|
+
webModelDefaults: UserWebModelDefaults;
|
|
58
|
+
customModels: string[];
|
|
59
|
+
threadPermissionsOverrides: Record<string, UserThreadPermissionsOverride>;
|
|
60
|
+
threadCollaborationOverrides: Record<string, UserThreadCollaborationOverride>;
|
|
61
|
+
defaultWorkspace: string | null;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* 默认用户配置(每次读取时应复制,避免引用污染)。
|
|
65
|
+
*/
|
|
66
|
+
export declare const DEFAULT_USER_SETTINGS: UserSettings;
|
|
67
|
+
/**
|
|
68
|
+
* 规范化用户配置输入,确保可安全入库与返回。
|
|
69
|
+
*/
|
|
70
|
+
export declare function normalizeUserSettings(input: unknown): UserSettings;
|