@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,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createThreadListServerCache = createThreadListServerCache;
|
|
4
|
+
const DEFAULT_THREAD_LIST_SERVER_CACHE_TTL_MS = 30_000;
|
|
5
|
+
function normalizeTtlMs(ttlMs) {
|
|
6
|
+
if (!Number.isFinite(ttlMs))
|
|
7
|
+
return DEFAULT_THREAD_LIST_SERVER_CACHE_TTL_MS;
|
|
8
|
+
return Math.max(0, Math.floor(Number(ttlMs)));
|
|
9
|
+
}
|
|
10
|
+
function cloneThreads(threads) {
|
|
11
|
+
return [...threads];
|
|
12
|
+
}
|
|
13
|
+
function createThreadListServerCache(options = {}) {
|
|
14
|
+
const nowMs = typeof options.nowMs === "function" ? options.nowMs : Date.now;
|
|
15
|
+
const ttlMs = normalizeTtlMs(options.ttlMs);
|
|
16
|
+
let cacheEntry = null;
|
|
17
|
+
return {
|
|
18
|
+
get() {
|
|
19
|
+
if (!cacheEntry)
|
|
20
|
+
return null;
|
|
21
|
+
if (ttlMs <= 0) {
|
|
22
|
+
cacheEntry = null;
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
if (nowMs() - cacheEntry.cachedAtMs > ttlMs) {
|
|
26
|
+
cacheEntry = null;
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return cloneThreads(cacheEntry.threads);
|
|
30
|
+
},
|
|
31
|
+
set(threads) {
|
|
32
|
+
cacheEntry = {
|
|
33
|
+
threads: cloneThreads(threads),
|
|
34
|
+
cachedAtMs: nowMs(),
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
clear() {
|
|
38
|
+
cacheEntry = null;
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=threadListServerCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threadListServerCache.js","sourceRoot":"","sources":["../../src/threadList/threadListServerCache.ts"],"names":[],"mappings":";;AA2BA,kEA4BC;AAvDD,MAAM,uCAAuC,GAAG,MAAM,CAAC;AAkBvD,SAAS,cAAc,CAAC,KAAyB;IAC/C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,uCAAuC,CAAC;IAC5E,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,YAAY,CAAI,OAAY;IACnC,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;AACtB,CAAC;AAED,SAAgB,2BAA2B,CAAI,UAAwC,EAAE;IACvF,MAAM,KAAK,GAAG,OAAO,OAAO,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;IAC7E,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5C,IAAI,UAAU,GAAyC,IAAI,CAAC;IAE5D,OAAO;QACL,GAAG;YACD,IAAI,CAAC,UAAU;gBAAE,OAAO,IAAI,CAAC;YAC7B,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;gBACf,UAAU,GAAG,IAAI,CAAC;gBAClB,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,KAAK,EAAE,GAAG,UAAU,CAAC,UAAU,GAAG,KAAK,EAAE,CAAC;gBAC5C,UAAU,GAAG,IAAI,CAAC;gBAClB,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAC1C,CAAC;QACD,GAAG,CAAC,OAAO;YACT,UAAU,GAAG;gBACX,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC;gBAC9B,UAAU,EAAE,KAAK,EAAE;aACpB,CAAC;QACJ,CAAC;QACD,KAAK;YACH,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type CwdSuggestErrorCode = "invalid_query" | "path_not_allowed";
|
|
2
|
+
export declare class CwdSuggestError extends Error {
|
|
3
|
+
readonly code: CwdSuggestErrorCode;
|
|
4
|
+
constructor(code: CwdSuggestErrorCode, message: string);
|
|
5
|
+
}
|
|
6
|
+
export declare function suggestCwds(opts: {
|
|
7
|
+
query: string;
|
|
8
|
+
limit?: number;
|
|
9
|
+
allowAnyRoot?: boolean;
|
|
10
|
+
allowedRoots?: string[];
|
|
11
|
+
}): Promise<string[]>;
|
|
@@ -0,0 +1,128 @@
|
|
|
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.CwdSuggestError = void 0;
|
|
7
|
+
exports.suggestCwds = suggestCwds;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const env_1 = require("../env");
|
|
11
|
+
// 目录建议接口的可预期错误类型(客户端可据此展示更友好的提示)。
|
|
12
|
+
class CwdSuggestError extends Error {
|
|
13
|
+
code;
|
|
14
|
+
constructor(code, message) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = "CwdSuggestError";
|
|
17
|
+
this.code = code;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.CwdSuggestError = CwdSuggestError;
|
|
21
|
+
// 判断 candidate 是否位于 root 目录之下(含 root 本身)。
|
|
22
|
+
function isPathWithinRoot(root, candidate) {
|
|
23
|
+
const rel = path_1.default.relative(root, candidate);
|
|
24
|
+
if (!rel)
|
|
25
|
+
return true;
|
|
26
|
+
if (rel.startsWith(".."))
|
|
27
|
+
return false;
|
|
28
|
+
if (path_1.default.isAbsolute(rel))
|
|
29
|
+
return false;
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
// 统一限制返回数量,避免 readdir 结果过大影响响应时间。
|
|
33
|
+
function normalizeLimit(raw) {
|
|
34
|
+
const n = typeof raw === "number" ? raw : typeof raw === "string" ? Number(raw) : NaN;
|
|
35
|
+
if (!Number.isFinite(n))
|
|
36
|
+
return 50;
|
|
37
|
+
const limit = Math.floor(n);
|
|
38
|
+
if (limit <= 0)
|
|
39
|
+
return 50;
|
|
40
|
+
return Math.min(100, limit);
|
|
41
|
+
}
|
|
42
|
+
// 将用户输入解析为“要列举的目录(listDir) + 目录名匹配前缀(namePrefix)”。
|
|
43
|
+
function parseQuery(query) {
|
|
44
|
+
const trimmed = query.trim();
|
|
45
|
+
const normalized = trimmed.replace(/[\\/]+/g, path_1.default.sep);
|
|
46
|
+
const endsWithSep = normalized.endsWith(path_1.default.sep);
|
|
47
|
+
const listDirRaw = endsWithSep ? normalized : path_1.default.dirname(normalized);
|
|
48
|
+
const namePrefix = endsWithSep ? "" : path_1.default.basename(normalized);
|
|
49
|
+
return { listDir: listDirRaw, namePrefix };
|
|
50
|
+
}
|
|
51
|
+
// 在可能存在权限/平台差异时,realpath 失败则回退到 resolve,以确保逻辑可继续执行。
|
|
52
|
+
async function resolveRealpathOrFallback(candidate) {
|
|
53
|
+
return fs_1.default.promises.realpath(candidate).catch(() => path_1.default.resolve(candidate));
|
|
54
|
+
}
|
|
55
|
+
// 校验 listDir 真实路径是否位于允许范围内:
|
|
56
|
+
// - allowAnyRoot=true:admin 不限制;
|
|
57
|
+
// - 提供 allowedRoots:按调用方传入范围(通常是管理员分配目录);
|
|
58
|
+
// - 未提供 allowedRoots:回退到 CODEX_ALLOWED_CWD_ROOTS 全局边界。
|
|
59
|
+
async function ensureListDirAllowed(listDir, opts) {
|
|
60
|
+
const resolved = path_1.default.resolve(listDir);
|
|
61
|
+
const real = await resolveRealpathOrFallback(resolved);
|
|
62
|
+
if (opts.allowAnyRoot)
|
|
63
|
+
return real;
|
|
64
|
+
// 上层显式传入 allowedRoots 时,优先按该范围校验(例如用户分配目录)。
|
|
65
|
+
const allowedRoots = (opts.allowedRoots ?? []).filter(Boolean);
|
|
66
|
+
if (allowedRoots.length) {
|
|
67
|
+
const allowedRootsReal = await Promise.all(allowedRoots.map((root) => resolveRealpathOrFallback(root)));
|
|
68
|
+
const allowed = allowedRootsReal.some((root) => isPathWithinRoot(root, real));
|
|
69
|
+
if (!allowed)
|
|
70
|
+
throw new CwdSuggestError("path_not_allowed", "path not allowed");
|
|
71
|
+
return real;
|
|
72
|
+
}
|
|
73
|
+
// 未提供 allowedRoots 时,退化为全局边界校验,保持独立调用场景安全。
|
|
74
|
+
const globalRoots = (0, env_1.getCodexAllowedCwdRoots)().filter(Boolean);
|
|
75
|
+
if (!globalRoots.length)
|
|
76
|
+
throw new CwdSuggestError("path_not_allowed", "path not allowed");
|
|
77
|
+
const globalRootsReal = await Promise.all(globalRoots.map((root) => resolveRealpathOrFallback(root)));
|
|
78
|
+
const withinGlobal = globalRootsReal.some((root) => isPathWithinRoot(root, real));
|
|
79
|
+
if (!withinGlobal)
|
|
80
|
+
throw new CwdSuggestError("path_not_allowed", "path not allowed");
|
|
81
|
+
return real;
|
|
82
|
+
}
|
|
83
|
+
// 列举 listDir 下满足“目录 + namePrefix 前缀匹配”的子目录,按字典序排序后返回。
|
|
84
|
+
async function listDirectorySuggestions(listDir, namePrefix, limit) {
|
|
85
|
+
let dirents;
|
|
86
|
+
try {
|
|
87
|
+
dirents = await fs_1.default.promises.readdir(listDir, { withFileTypes: true });
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
const out = [];
|
|
93
|
+
for (const d of dirents) {
|
|
94
|
+
if (!d.isDirectory())
|
|
95
|
+
continue;
|
|
96
|
+
if (namePrefix && !d.name.startsWith(namePrefix))
|
|
97
|
+
continue;
|
|
98
|
+
out.push(path_1.default.join(listDir, d.name));
|
|
99
|
+
if (out.length >= limit)
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
out.sort((a, b) => a.localeCompare(b));
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
// 返回用于“工作区添加/切换”的服务器目录建议列表(右模糊:前缀匹配)。
|
|
106
|
+
async function suggestCwds(opts) {
|
|
107
|
+
const rawQuery = String(opts.query ?? "").trim();
|
|
108
|
+
if (!rawQuery)
|
|
109
|
+
return [];
|
|
110
|
+
if (rawQuery.includes("\0"))
|
|
111
|
+
throw new CwdSuggestError("invalid_query", "invalid query");
|
|
112
|
+
const { listDir, namePrefix } = parseQuery(rawQuery);
|
|
113
|
+
const baseCwd = path_1.default.resolve((0, env_1.getCodexCwd)());
|
|
114
|
+
const resolvedListDir = path_1.default.isAbsolute(listDir) ? path_1.default.resolve(listDir) : path_1.default.resolve(baseCwd, listDir);
|
|
115
|
+
let st;
|
|
116
|
+
try {
|
|
117
|
+
st = await fs_1.default.promises.stat(resolvedListDir);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
if (!st.isDirectory())
|
|
123
|
+
return [];
|
|
124
|
+
const allowedListDir = await ensureListDirAllowed(resolvedListDir, { allowAnyRoot: Boolean(opts.allowAnyRoot), allowedRoots: opts.allowedRoots });
|
|
125
|
+
const limit = normalizeLimit(opts.limit);
|
|
126
|
+
return listDirectorySuggestions(allowedListDir, namePrefix, limit);
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=cwdSuggest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cwdSuggest.js","sourceRoot":"","sources":["../../src/tools/cwdSuggest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { UserRole } from "../auth/userTypes";
|
|
2
|
+
export type UserAccessInfo = {
|
|
3
|
+
username: string;
|
|
4
|
+
role: UserRole;
|
|
5
|
+
workspaces: string[];
|
|
6
|
+
};
|
|
7
|
+
export type AllowedRoots = {
|
|
8
|
+
allowAnyRoot: boolean;
|
|
9
|
+
roots: string[];
|
|
10
|
+
};
|
|
11
|
+
export declare function isPathWithinRoot(root: string, candidate: string): boolean;
|
|
12
|
+
export declare function resolveAllowedRootsForUser(user: Pick<UserAccessInfo, "role" | "workspaces"> | null | undefined): AllowedRoots;
|
|
13
|
+
export declare function assertCwdAllowedForUser(opts: {
|
|
14
|
+
cwd: string;
|
|
15
|
+
user: Pick<UserAccessInfo, "role" | "workspaces">;
|
|
16
|
+
}): Promise<string>;
|
|
@@ -0,0 +1,82 @@
|
|
|
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.isPathWithinRoot = isPathWithinRoot;
|
|
7
|
+
exports.resolveAllowedRootsForUser = resolveAllowedRootsForUser;
|
|
8
|
+
exports.assertCwdAllowedForUser = assertCwdAllowedForUser;
|
|
9
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
10
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
+
const env_1 = require("../env");
|
|
12
|
+
const roles_1 = require("../auth/roles");
|
|
13
|
+
// 判断 candidate 是否位于 root 目录之下(含 root 本身)。
|
|
14
|
+
function isPathWithinRoot(root, candidate) {
|
|
15
|
+
const rel = node_path_1.default.relative(root, candidate);
|
|
16
|
+
if (!rel)
|
|
17
|
+
return true;
|
|
18
|
+
if (rel.startsWith(".."))
|
|
19
|
+
return false;
|
|
20
|
+
if (node_path_1.default.isAbsolute(rel))
|
|
21
|
+
return false;
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
function normalizeRoots(roots) {
|
|
25
|
+
const seen = new Set();
|
|
26
|
+
const out = [];
|
|
27
|
+
for (const r of roots) {
|
|
28
|
+
const resolved = node_path_1.default.resolve(String(r ?? "").trim());
|
|
29
|
+
if (!resolved)
|
|
30
|
+
continue;
|
|
31
|
+
if (seen.has(resolved))
|
|
32
|
+
continue;
|
|
33
|
+
seen.add(resolved);
|
|
34
|
+
out.push(resolved);
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
// 计算用户可访问的根目录:
|
|
39
|
+
// - admin:允许从 `/` 起跳;
|
|
40
|
+
// - member:仅允许访问被管理员分配的 workspaces(不再与 CODEX_ALLOWED_CWD_ROOTS 取交集)。
|
|
41
|
+
function resolveAllowedRootsForUser(user) {
|
|
42
|
+
const role = user?.role;
|
|
43
|
+
if ((0, roles_1.isAdminRole)(role)) {
|
|
44
|
+
return { allowAnyRoot: true, roots: [] };
|
|
45
|
+
}
|
|
46
|
+
const assigned = normalizeRoots(user?.workspaces ?? []);
|
|
47
|
+
return { allowAnyRoot: false, roots: assigned };
|
|
48
|
+
}
|
|
49
|
+
async function resolveRealpathOrFallback(candidate) {
|
|
50
|
+
return promises_1.default.realpath(candidate).catch(() => node_path_1.default.resolve(candidate));
|
|
51
|
+
}
|
|
52
|
+
// 校验 cwd 是否在用户允许范围内;返回 canonical(尽力 realpath)后的 cwd。
|
|
53
|
+
async function assertCwdAllowedForUser(opts) {
|
|
54
|
+
const base = node_path_1.default.resolve((0, env_1.getCodexCwd)());
|
|
55
|
+
const raw = typeof opts.cwd === "string" ? opts.cwd.trim() : "";
|
|
56
|
+
if (raw.includes("\0"))
|
|
57
|
+
throw new Error("invalid cwd");
|
|
58
|
+
// 空 cwd 等价于使用默认 CODEX_CWD,保持与历史行为一致。
|
|
59
|
+
const resolved = raw ? (node_path_1.default.isAbsolute(raw) ? node_path_1.default.resolve(raw) : node_path_1.default.resolve(base, raw)) : base;
|
|
60
|
+
let st;
|
|
61
|
+
try {
|
|
62
|
+
st = await promises_1.default.stat(resolved);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
throw new Error(`cwd not found: ${resolved}`);
|
|
66
|
+
}
|
|
67
|
+
if (!st.isDirectory())
|
|
68
|
+
throw new Error(`cwd is not a directory: ${resolved}`);
|
|
69
|
+
const candidateReal = await resolveRealpathOrFallback(resolved);
|
|
70
|
+
const allowed = resolveAllowedRootsForUser(opts.user);
|
|
71
|
+
if (allowed.allowAnyRoot)
|
|
72
|
+
return candidateReal;
|
|
73
|
+
// 非 admin:必须在管理员分配的工作区根目录内。
|
|
74
|
+
if (!allowed.roots.length)
|
|
75
|
+
throw new Error(`cwd not allowed: ${candidateReal}`);
|
|
76
|
+
const allowedRootsReal = await Promise.all(allowed.roots.map((r) => resolveRealpathOrFallback(r)));
|
|
77
|
+
const ok = allowedRootsReal.some((root) => isPathWithinRoot(root, candidateReal));
|
|
78
|
+
if (!ok)
|
|
79
|
+
throw new Error(`cwd not allowed: ${candidateReal}`);
|
|
80
|
+
return candidateReal;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=accessControl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessControl.js","sourceRoot":"","sources":["../../src/workspace/accessControl.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AuthDb } from "../auth/sqlite/authDb";
|
|
2
|
+
import type { UserWorkspaceStore } from "./userWorkspaceStore";
|
|
3
|
+
/**
|
|
4
|
+
* SQLite 用户工作目录仓库创建参数。
|
|
5
|
+
*/
|
|
6
|
+
export type CreateSqliteUserWorkspaceStoreOptions = {
|
|
7
|
+
authDb: AuthDb;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* 基于 SQLite 创建用户工作目录仓库。
|
|
11
|
+
*/
|
|
12
|
+
export declare function createSqliteUserWorkspaceStore(options: CreateSqliteUserWorkspaceStoreOptions): UserWorkspaceStore;
|
|
@@ -0,0 +1,82 @@
|
|
|
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.createSqliteUserWorkspaceStore = createSqliteUserWorkspaceStore;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
/**
|
|
9
|
+
* 规范化用户名输入。
|
|
10
|
+
*/
|
|
11
|
+
function normalizeUsername(username) {
|
|
12
|
+
return String(username ?? "").trim();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 去重并保序地规范化用户工作目录。
|
|
16
|
+
*/
|
|
17
|
+
function normalizeWorkspaceDirs(workspaceDirs) {
|
|
18
|
+
const seen = new Set();
|
|
19
|
+
const normalized = [];
|
|
20
|
+
for (const workspaceDir of workspaceDirs ?? []) {
|
|
21
|
+
const resolvedWorkspaceDir = node_path_1.default.resolve(String(workspaceDir ?? "").trim());
|
|
22
|
+
if (!resolvedWorkspaceDir)
|
|
23
|
+
continue;
|
|
24
|
+
if (seen.has(resolvedWorkspaceDir))
|
|
25
|
+
continue;
|
|
26
|
+
seen.add(resolvedWorkspaceDir);
|
|
27
|
+
normalized.push(resolvedWorkspaceDir);
|
|
28
|
+
}
|
|
29
|
+
return normalized;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 基于 SQLite 创建用户工作目录仓库。
|
|
33
|
+
*/
|
|
34
|
+
function createSqliteUserWorkspaceStore(options) {
|
|
35
|
+
const db = options.authDb.db;
|
|
36
|
+
const runInTransaction = options.authDb.runInTransaction;
|
|
37
|
+
const listWorkspaceDirsByUsernameStatement = db.prepare(`
|
|
38
|
+
SELECT workspace_dir
|
|
39
|
+
FROM auth_user_workspace_dirs
|
|
40
|
+
WHERE username = ?
|
|
41
|
+
ORDER BY ord ASC
|
|
42
|
+
`);
|
|
43
|
+
const deleteWorkspaceDirsByUsernameStatement = db.prepare(`
|
|
44
|
+
DELETE FROM auth_user_workspace_dirs
|
|
45
|
+
WHERE username = ?
|
|
46
|
+
`);
|
|
47
|
+
const insertWorkspaceDirStatement = db.prepare(`
|
|
48
|
+
INSERT OR REPLACE INTO auth_user_workspace_dirs (
|
|
49
|
+
username, workspace_dir, ord
|
|
50
|
+
) VALUES (
|
|
51
|
+
@username, @workspaceDir, @ord
|
|
52
|
+
)
|
|
53
|
+
`);
|
|
54
|
+
return {
|
|
55
|
+
async listUserWorkspaceDirs(usernameRaw) {
|
|
56
|
+
const username = normalizeUsername(usernameRaw);
|
|
57
|
+
if (!username)
|
|
58
|
+
return [];
|
|
59
|
+
const rows = listWorkspaceDirsByUsernameStatement.all(username);
|
|
60
|
+
return rows.map((row) => row.workspace_dir);
|
|
61
|
+
},
|
|
62
|
+
async replaceUserWorkspaceDirs(usernameRaw, workspaceDirs) {
|
|
63
|
+
const username = normalizeUsername(usernameRaw);
|
|
64
|
+
if (!username)
|
|
65
|
+
throw new Error("username is required");
|
|
66
|
+
const normalizedWorkspaceDirs = normalizeWorkspaceDirs(workspaceDirs);
|
|
67
|
+
runInTransaction(() => {
|
|
68
|
+
deleteWorkspaceDirsByUsernameStatement.run(username);
|
|
69
|
+
for (let index = 0; index < normalizedWorkspaceDirs.length; index += 1) {
|
|
70
|
+
const workspaceDir = normalizedWorkspaceDirs[index];
|
|
71
|
+
insertWorkspaceDirStatement.run({
|
|
72
|
+
username,
|
|
73
|
+
workspaceDir,
|
|
74
|
+
ord: index,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
return normalizedWorkspaceDirs;
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=sqliteUserWorkspaceStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqliteUserWorkspaceStore.js","sourceRoot":"","sources":["../../src/workspace/sqliteUserWorkspaceStore.ts"],"names":[],"mappings":";;;;;AA4CA,wEAoDC;AAhGD,0DAA6B;AAkB7B;;GAEG;AACH,SAAS,iBAAiB,CAAC,QAAgB;IACzC,OAAO,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAAC,aAAuB;IACrD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,KAAK,MAAM,YAAY,IAAI,aAAa,IAAI,EAAE,EAAE,CAAC;QAC/C,MAAM,oBAAoB,GAAG,mBAAI,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC7E,IAAI,CAAC,oBAAoB;YAAE,SAAS;QACpC,IAAI,IAAI,CAAC,GAAG,CAAC,oBAAoB,CAAC;YAAE,SAAS;QAC7C,IAAI,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAC/B,UAAU,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,SAAgB,8BAA8B,CAAC,OAA8C;IAC3F,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;IAC7B,MAAM,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC;IAEzD,MAAM,oCAAoC,GAAG,EAAE,CAAC,OAAO,CAAC;;;;;GAKvD,CAAC,CAAC;IAEH,MAAM,sCAAsC,GAAG,EAAE,CAAC,OAAO,CAAC;;;GAGzD,CAAC,CAAC;IAEH,MAAM,2BAA2B,GAAG,EAAE,CAAC,OAAO,CAAC;;;;;;GAM9C,CAAC,CAAC;IAEH,OAAO;QACL,KAAK,CAAC,qBAAqB,CAAC,WAAmB;YAC7C,MAAM,QAAQ,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;YAChD,IAAI,CAAC,QAAQ;gBAAE,OAAO,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,oCAAoC,CAAC,GAAG,CAAC,QAAQ,CAAsB,CAAC;YACrF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QAC9C,CAAC;QAED,KAAK,CAAC,wBAAwB,CAAC,WAAmB,EAAE,aAAuB;YACzE,MAAM,QAAQ,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;YAChD,IAAI,CAAC,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;YAEvD,MAAM,uBAAuB,GAAG,sBAAsB,CAAC,aAAa,CAAC,CAAC;YACtE,gBAAgB,CAAC,GAAG,EAAE;gBACpB,sCAAsC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBACrD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,uBAAuB,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;oBACvE,MAAM,YAAY,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC;oBACpD,2BAA2B,CAAC,GAAG,CAAC;wBAC9B,QAAQ;wBACR,YAAY;wBACZ,GAAG,EAAE,KAAK;qBACX,CAAC,CAAC;gBACL,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,OAAO,uBAAuB,CAAC;QACjC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { UserRole } from "../auth/userTypes";
|
|
2
|
+
import type { CodexAppServer } from "../codex/codexAppServer";
|
|
3
|
+
type ThreadAccessUser = {
|
|
4
|
+
username: string;
|
|
5
|
+
role: UserRole;
|
|
6
|
+
workspaces: string[];
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* 断言当前用户可以访问指定 threadId。
|
|
10
|
+
* 规则:threadId -> 读取 thread.cwd -> 复用 assertCwdAllowedForUser 校验是否在用户分配 workspaces 内。
|
|
11
|
+
*/
|
|
12
|
+
export declare function assertThreadAllowedForUser(opts: {
|
|
13
|
+
threadId: string;
|
|
14
|
+
user: ThreadAccessUser;
|
|
15
|
+
codex: Pick<CodexAppServer, "readThread">;
|
|
16
|
+
}): Promise<{
|
|
17
|
+
canonicalCwd: string;
|
|
18
|
+
}>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertThreadAllowedForUser = assertThreadAllowedForUser;
|
|
4
|
+
const roles_1 = require("../auth/roles");
|
|
5
|
+
const accessControl_1 = require("./accessControl");
|
|
6
|
+
/**
|
|
7
|
+
* 断言当前用户可以访问指定 threadId。
|
|
8
|
+
* 规则:threadId -> 读取 thread.cwd -> 复用 assertCwdAllowedForUser 校验是否在用户分配 workspaces 内。
|
|
9
|
+
*/
|
|
10
|
+
async function assertThreadAllowedForUser(opts) {
|
|
11
|
+
// admin 默认允许访问所有线程(更细粒度的隔离需要 codex/app-server 侧支持)。
|
|
12
|
+
if ((0, roles_1.isAdminRole)(opts.user.role)) {
|
|
13
|
+
return { canonicalCwd: "" };
|
|
14
|
+
}
|
|
15
|
+
const thread = await opts.codex.readThread(opts.threadId, false);
|
|
16
|
+
const cwd = String(thread?.cwd ?? "").trim();
|
|
17
|
+
if (!cwd)
|
|
18
|
+
throw new Error("cwd not allowed");
|
|
19
|
+
const canonicalCwd = await (0, accessControl_1.assertCwdAllowedForUser)({ cwd, user: opts.user });
|
|
20
|
+
return { canonicalCwd };
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=threadAccess.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threadAccess.js","sourceRoot":"","sources":["../../src/workspace/threadAccess.ts"],"names":[],"mappings":";;AAeA,gEAgBC;AA/BD,yCAA4C;AAG5C,mDAA0D;AAQ1D;;;GAGG;AACI,KAAK,UAAU,0BAA0B,CAAC,IAIhD;IACC,oDAAoD;IACpD,IAAI,IAAA,mBAAW,EAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC;IAC9B,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACjE,MAAM,GAAG,GAAG,MAAM,CAAE,MAAc,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACtD,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAE7C,MAAM,YAAY,GAAG,MAAM,IAAA,uCAAuB,EAAC,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7E,OAAO,EAAE,YAAY,EAAE,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { StoredUser } from "../auth/userTypes";
|
|
2
|
+
import type { UserAccessInfo } from "./accessControl";
|
|
3
|
+
export type ThreadListVisibilityUser = Pick<UserAccessInfo, "role" | "workspaces">;
|
|
4
|
+
export type WorkspaceRoutingSnapshot = {
|
|
5
|
+
updatedAtMs: number;
|
|
6
|
+
rootsDesc: string[];
|
|
7
|
+
};
|
|
8
|
+
export declare function normalizeWorkspacePaths(workspaces: string[]): string[];
|
|
9
|
+
export declare function buildWorkspaceRoutingSnapshot(users: Array<Pick<StoredUser, "role" | "workspaces">>): WorkspaceRoutingSnapshot;
|
|
10
|
+
export declare function resolveWorkspaceRootForCwd(snapshot: WorkspaceRoutingSnapshot, cwd: string): string | null;
|
|
11
|
+
export declare function resolveThreadListWorkspaceFilter(requestedCwd: unknown, user: ThreadListVisibilityUser): Promise<string | null>;
|
|
12
|
+
export declare function normalizeThreadCwd(rawThreadCwd: unknown): string;
|
|
13
|
+
export declare function isThreadCwdVisibleToUser(input: {
|
|
14
|
+
cwd: string;
|
|
15
|
+
user: ThreadListVisibilityUser;
|
|
16
|
+
routingSnapshot: WorkspaceRoutingSnapshot | null;
|
|
17
|
+
}): boolean;
|
|
18
|
+
export declare function shouldIncludeThreadForList(input: {
|
|
19
|
+
thread: {
|
|
20
|
+
cwd?: unknown;
|
|
21
|
+
};
|
|
22
|
+
user: ThreadListVisibilityUser;
|
|
23
|
+
workspaceFilterCwd: string | null;
|
|
24
|
+
routingSnapshot: WorkspaceRoutingSnapshot | null;
|
|
25
|
+
}): boolean;
|
|
@@ -0,0 +1,104 @@
|
|
|
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.normalizeWorkspacePaths = normalizeWorkspacePaths;
|
|
7
|
+
exports.buildWorkspaceRoutingSnapshot = buildWorkspaceRoutingSnapshot;
|
|
8
|
+
exports.resolveWorkspaceRootForCwd = resolveWorkspaceRootForCwd;
|
|
9
|
+
exports.resolveThreadListWorkspaceFilter = resolveThreadListWorkspaceFilter;
|
|
10
|
+
exports.normalizeThreadCwd = normalizeThreadCwd;
|
|
11
|
+
exports.isThreadCwdVisibleToUser = isThreadCwdVisibleToUser;
|
|
12
|
+
exports.shouldIncludeThreadForList = shouldIncludeThreadForList;
|
|
13
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
14
|
+
const accessControl_1 = require("./accessControl");
|
|
15
|
+
// WS/HTTP 统一使用绝对路径 + 去重后的 workspace 列表,避免路径比较歧义。
|
|
16
|
+
function normalizeWorkspacePaths(workspaces) {
|
|
17
|
+
const seen = new Set();
|
|
18
|
+
const out = [];
|
|
19
|
+
for (const workspace of workspaces ?? []) {
|
|
20
|
+
const trimmedWorkspace = String(workspace ?? "").trim();
|
|
21
|
+
if (!trimmedWorkspace)
|
|
22
|
+
continue;
|
|
23
|
+
const resolvedWorkspace = node_path_1.default.resolve(trimmedWorkspace);
|
|
24
|
+
if (seen.has(resolvedWorkspace))
|
|
25
|
+
continue;
|
|
26
|
+
seen.add(resolvedWorkspace);
|
|
27
|
+
out.push(resolvedWorkspace);
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
// 基于所有 member 用户 workspaces 生成“最具体根目录优先”的路由快照。
|
|
32
|
+
function buildWorkspaceRoutingSnapshot(users) {
|
|
33
|
+
const roots = new Set();
|
|
34
|
+
for (const user of users) {
|
|
35
|
+
if (user.role !== "member")
|
|
36
|
+
continue;
|
|
37
|
+
for (const workspace of user.workspaces ?? []) {
|
|
38
|
+
const trimmedWorkspace = String(workspace ?? "").trim();
|
|
39
|
+
if (!trimmedWorkspace)
|
|
40
|
+
continue;
|
|
41
|
+
roots.add(node_path_1.default.resolve(trimmedWorkspace));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const rootsDesc = Array.from(roots.values()).sort((leftRoot, rightRoot) => rightRoot.length - leftRoot.length);
|
|
45
|
+
return { updatedAtMs: Date.now(), rootsDesc };
|
|
46
|
+
}
|
|
47
|
+
// 为某个 cwd 匹配最具体 workspace root(最长前缀优先)。
|
|
48
|
+
function resolveWorkspaceRootForCwd(snapshot, cwd) {
|
|
49
|
+
const trimmedCwd = String(cwd ?? "").trim();
|
|
50
|
+
if (!trimmedCwd)
|
|
51
|
+
return null;
|
|
52
|
+
const resolvedCwd = node_path_1.default.resolve(trimmedCwd);
|
|
53
|
+
for (const root of snapshot.rootsDesc) {
|
|
54
|
+
if ((0, accessControl_1.isPathWithinRoot)(root, resolvedCwd))
|
|
55
|
+
return root;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
// 解析 list 请求中的 cwd 过滤参数;空值表示不过滤。
|
|
60
|
+
async function resolveThreadListWorkspaceFilter(requestedCwd, user) {
|
|
61
|
+
const rawRequestedCwd = typeof requestedCwd === "string" ? requestedCwd.trim() : "";
|
|
62
|
+
if (!rawRequestedCwd)
|
|
63
|
+
return null;
|
|
64
|
+
return (0, accessControl_1.assertCwdAllowedForUser)({ cwd: rawRequestedCwd, user });
|
|
65
|
+
}
|
|
66
|
+
// 规整 thread.cwd,避免空白/相对路径造成过滤误判。
|
|
67
|
+
function normalizeThreadCwd(rawThreadCwd) {
|
|
68
|
+
const threadCwd = typeof rawThreadCwd === "string" ? rawThreadCwd.trim() : "";
|
|
69
|
+
if (!threadCwd)
|
|
70
|
+
return "";
|
|
71
|
+
return node_path_1.default.resolve(threadCwd);
|
|
72
|
+
}
|
|
73
|
+
// 判断线程 cwd 是否对指定用户可见(admin 全可见,member 按分配工作区可见)。
|
|
74
|
+
function isThreadCwdVisibleToUser(input) {
|
|
75
|
+
const allowedRoots = (0, accessControl_1.resolveAllowedRootsForUser)(input.user);
|
|
76
|
+
if (allowedRoots.allowAnyRoot)
|
|
77
|
+
return true;
|
|
78
|
+
if (!allowedRoots.roots.length)
|
|
79
|
+
return false;
|
|
80
|
+
if (input.routingSnapshot) {
|
|
81
|
+
const workspaceRoot = resolveWorkspaceRootForCwd(input.routingSnapshot, input.cwd);
|
|
82
|
+
if (workspaceRoot)
|
|
83
|
+
return input.user.workspaces.includes(workspaceRoot);
|
|
84
|
+
}
|
|
85
|
+
return allowedRoots.roots.some((root) => (0, accessControl_1.isPathWithinRoot)(root, input.cwd));
|
|
86
|
+
}
|
|
87
|
+
// 统一判定某线程是否应进入会话列表(权限 + 可选 workspace 精确过滤)。
|
|
88
|
+
function shouldIncludeThreadForList(input) {
|
|
89
|
+
const normalizedThreadCwd = normalizeThreadCwd(input.thread.cwd);
|
|
90
|
+
if (!normalizedThreadCwd)
|
|
91
|
+
return false;
|
|
92
|
+
const visible = isThreadCwdVisibleToUser({
|
|
93
|
+
cwd: normalizedThreadCwd,
|
|
94
|
+
user: input.user,
|
|
95
|
+
routingSnapshot: input.routingSnapshot,
|
|
96
|
+
});
|
|
97
|
+
if (!visible)
|
|
98
|
+
return false;
|
|
99
|
+
if (!input.workspaceFilterCwd)
|
|
100
|
+
return true;
|
|
101
|
+
// 工作区语义按“线程 cwd == 选中 workspace cwd”处理,避免前缀误包含。
|
|
102
|
+
return normalizedThreadCwd === input.workspaceFilterCwd;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=threadListVisibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threadListVisibility.js","sourceRoot":"","sources":["../../src/workspace/threadListVisibility.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import type { UserWorkspaceStore } from "./userWorkspaceStore";
|
|
3
|
+
type CreateUserWorkspaceRoutesOptions = {
|
|
4
|
+
userWorkspaceStore: UserWorkspaceStore;
|
|
5
|
+
};
|
|
6
|
+
export declare function createUserWorkspaceRoutes(opts: CreateUserWorkspaceRoutesOptions): express.Router;
|
|
7
|
+
export {};
|