pockcode 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/build/client/assets/addon-fit-DthTIhi3.js +1 -0
- package/build/client/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/build/client/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/build/client/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/build/client/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/build/client/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/build/client/assets/index-D7-eRInW.css +2 -0
- package/build/client/assets/index-DQn6eSCt.js +31 -0
- package/build/client/assets/xterm-DooSxjI5.js +36 -0
- package/build/client/index.html +27 -0
- package/dist/assets/api.server-D6ONM6ZJ.js +1736 -0
- package/dist/assets/auth.server-B_P8Jm6O.js +87 -0
- package/dist/assets/chat-status-monitor.server-D2VG_D8P.js +136 -0
- package/dist/assets/chats.service-H6m23Fna.js +4684 -0
- package/dist/assets/manager.server-Cru4ZxHo.js +1334 -0
- package/dist/assets/message-schedule-monitor.server-C4NZg62k.js +32 -0
- package/dist/assets/message-schedules.service-Be9t4LDg.js +407 -0
- package/dist/assets/runtime-paths.server-D6rRjGVb.js +32 -0
- package/dist/assets/socket.server-7dC-9Fnw.js +436 -0
- package/dist/pockcode.js +305 -0
- package/package.json +71 -0
- package/prisma/schema.prisma +234 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { r as resolvePockcodeAuthPath, t as ensureParentDirectory } from "./runtime-paths.server-D6rRjGVb.js";
|
|
2
|
+
import { readFile, rename, writeFile } from "node:fs/promises";
|
|
3
|
+
import { randomBytes, scrypt, timingSafeEqual } from "node:crypto";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
//#region app/server/auth.server.ts
|
|
6
|
+
var scrypt$1 = promisify(scrypt);
|
|
7
|
+
var passwordKeyLength = 64;
|
|
8
|
+
var passwordMinLength = 8;
|
|
9
|
+
var scryptOptions = {
|
|
10
|
+
N: 16384,
|
|
11
|
+
maxmem: 64 * 1024 * 1024,
|
|
12
|
+
p: 1,
|
|
13
|
+
r: 8
|
|
14
|
+
};
|
|
15
|
+
var authConfigCache;
|
|
16
|
+
async function hasConfiguredPassword() {
|
|
17
|
+
return Boolean(await readAuthConfig());
|
|
18
|
+
}
|
|
19
|
+
async function setupPassword(password) {
|
|
20
|
+
const normalized = password.trim();
|
|
21
|
+
if (normalized.length < passwordMinLength) throw new Error(`Password must be at least ${passwordMinLength} characters.`);
|
|
22
|
+
if (await readAuthConfig()) throw new Error("Pockcode password is already configured.");
|
|
23
|
+
const salt = randomBytes(16);
|
|
24
|
+
const key = await scrypt$1(normalized, salt, passwordKeyLength, scryptOptions);
|
|
25
|
+
const config = {
|
|
26
|
+
version: 1,
|
|
27
|
+
password: {
|
|
28
|
+
algorithm: "scrypt",
|
|
29
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
30
|
+
key: key.toString("base64"),
|
|
31
|
+
keyLength: passwordKeyLength,
|
|
32
|
+
salt: salt.toString("base64"),
|
|
33
|
+
scrypt: scryptOptions
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const authPath = resolvePockcodeAuthPath();
|
|
37
|
+
ensureParentDirectory(authPath);
|
|
38
|
+
const tmpPath = `${authPath}.${process.pid}.${Date.now()}.tmp`;
|
|
39
|
+
await writeFile(tmpPath, `${JSON.stringify(config, null, 2)}\n`, { mode: 384 });
|
|
40
|
+
await rename(tmpPath, authPath);
|
|
41
|
+
authConfigCache = config;
|
|
42
|
+
}
|
|
43
|
+
async function verifyBasicAuthorization(header) {
|
|
44
|
+
const value = Array.isArray(header) ? header[0] : header;
|
|
45
|
+
if (!value?.startsWith("Basic ")) return false;
|
|
46
|
+
let decoded = "";
|
|
47
|
+
try {
|
|
48
|
+
decoded = Buffer.from(value.slice(6), "base64").toString("utf8");
|
|
49
|
+
} catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const separatorIndex = decoded.indexOf(":");
|
|
53
|
+
if (separatorIndex < 0) return false;
|
|
54
|
+
return verifyPassword(decoded.slice(separatorIndex + 1));
|
|
55
|
+
}
|
|
56
|
+
function pockcodeAuthRealm() {
|
|
57
|
+
return "pockcode";
|
|
58
|
+
}
|
|
59
|
+
async function isRequestAuthorized(req) {
|
|
60
|
+
return verifyBasicAuthorization(req.headers.authorization);
|
|
61
|
+
}
|
|
62
|
+
async function verifyPassword(password) {
|
|
63
|
+
const config = await readAuthConfig();
|
|
64
|
+
if (!config) return false;
|
|
65
|
+
const expectedKey = Buffer.from(config.password.key, "base64");
|
|
66
|
+
const key = await scrypt$1(password, Buffer.from(config.password.salt, "base64"), config.password.keyLength, config.password.scrypt);
|
|
67
|
+
return key.length === expectedKey.length && timingSafeEqual(key, expectedKey);
|
|
68
|
+
}
|
|
69
|
+
async function readAuthConfig() {
|
|
70
|
+
if (authConfigCache !== void 0) return authConfigCache;
|
|
71
|
+
const authPath = resolvePockcodeAuthPath();
|
|
72
|
+
try {
|
|
73
|
+
authConfigCache = readConfig(JSON.parse(await readFile(authPath, "utf8")));
|
|
74
|
+
} catch {
|
|
75
|
+
authConfigCache = null;
|
|
76
|
+
}
|
|
77
|
+
return authConfigCache;
|
|
78
|
+
}
|
|
79
|
+
function readConfig(value) {
|
|
80
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
81
|
+
const record = value;
|
|
82
|
+
const password = record.password;
|
|
83
|
+
if (record.version !== 1 || !password || password.algorithm !== "scrypt" || typeof password.key !== "string" || typeof password.salt !== "string" || typeof password.keyLength !== "number" || !password.scrypt || typeof password.scrypt.N !== "number" || typeof password.scrypt.r !== "number" || typeof password.scrypt.p !== "number") return null;
|
|
84
|
+
return record;
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
export { hasConfiguredPassword, isRequestAuthorized, pockcodeAuthRealm, setupPassword, verifyBasicAuthorization };
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { i as publishProviderEvent, r as onWorkspaceWatchChange, t as listWatchedWorkspacePaths } from "./socket.server-7dC-9Fnw.js";
|
|
2
|
+
import { H as ensureDatabase, M as readCodexHistoryWatchPaths, U as prisma, d as publishMessageDeltas, l as listChats, p as refreshChatStatusesForWorkspaces, y as syncProviderChatsOnce } from "./chats.service-H6m23Fna.js";
|
|
3
|
+
import { watch } from "node:fs";
|
|
4
|
+
import { stat } from "node:fs/promises";
|
|
5
|
+
//#region app/server/chat-status-monitor.server.ts
|
|
6
|
+
var debounceMs = 250;
|
|
7
|
+
var initialized = false;
|
|
8
|
+
var started = false;
|
|
9
|
+
var syncing = false;
|
|
10
|
+
var syncAgain = false;
|
|
11
|
+
var syncTimer = null;
|
|
12
|
+
var snapshots = /* @__PURE__ */ new Map();
|
|
13
|
+
var pendingAllMessages = false;
|
|
14
|
+
var pendingExternalThreadIds = /* @__PURE__ */ new Set();
|
|
15
|
+
var watchers = /* @__PURE__ */ new Map();
|
|
16
|
+
function startChatStatusMonitor() {
|
|
17
|
+
if (started) {
|
|
18
|
+
scheduleProviderSync();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
started = true;
|
|
22
|
+
onWorkspaceWatchChange(() => scheduleProviderSync());
|
|
23
|
+
refreshCodexWatchers().then(() => syncProviderState({ publishExisting: false }));
|
|
24
|
+
}
|
|
25
|
+
function scheduleProviderSync(change) {
|
|
26
|
+
if (change) {
|
|
27
|
+
const threadId = readCodexThreadId(change.filename);
|
|
28
|
+
if (threadId) pendingExternalThreadIds.add(threadId);
|
|
29
|
+
else pendingAllMessages = true;
|
|
30
|
+
}
|
|
31
|
+
if (syncTimer) clearTimeout(syncTimer);
|
|
32
|
+
syncTimer = setTimeout(() => {
|
|
33
|
+
syncTimer = null;
|
|
34
|
+
syncProviderState({ publishExisting: true });
|
|
35
|
+
}, debounceMs);
|
|
36
|
+
}
|
|
37
|
+
async function syncProviderState({ publishExisting }) {
|
|
38
|
+
if (syncing) {
|
|
39
|
+
syncAgain = true;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
syncing = true;
|
|
43
|
+
let targetAllMessages = false;
|
|
44
|
+
let targetExternalThreadIds = /* @__PURE__ */ new Set();
|
|
45
|
+
try {
|
|
46
|
+
await refreshCodexWatchers();
|
|
47
|
+
targetAllMessages = pendingAllMessages;
|
|
48
|
+
targetExternalThreadIds = pendingExternalThreadIds;
|
|
49
|
+
pendingAllMessages = false;
|
|
50
|
+
pendingExternalThreadIds = /* @__PURE__ */ new Set();
|
|
51
|
+
await syncProviderChatsOnce();
|
|
52
|
+
let chats = await listChats();
|
|
53
|
+
const statusUpdates = await refreshChatStatusesForWorkspaces(listWatchedWorkspacePaths());
|
|
54
|
+
if (statusUpdates.length) {
|
|
55
|
+
const statusUpdateById = new Map(statusUpdates.map((chat) => [chat.id, chat]));
|
|
56
|
+
chats = chats.map((chat) => statusUpdateById.get(chat.id) ?? chat);
|
|
57
|
+
}
|
|
58
|
+
const nextSnapshots = /* @__PURE__ */ new Map();
|
|
59
|
+
for (const chat of chats) {
|
|
60
|
+
const snapshot = {
|
|
61
|
+
lastActivityAt: chat.lastActivityAt,
|
|
62
|
+
status: chat.status
|
|
63
|
+
};
|
|
64
|
+
const previous = snapshots.get(chat.id);
|
|
65
|
+
nextSnapshots.set(chat.id, snapshot);
|
|
66
|
+
const changed = !previous || previous.status !== snapshot.status || previous.lastActivityAt !== snapshot.lastActivityAt;
|
|
67
|
+
const targeted = chat.externalThreadId ? targetExternalThreadIds.has(chat.externalThreadId) : false;
|
|
68
|
+
if (changed) publishProviderEvent({
|
|
69
|
+
threadId: chat.id,
|
|
70
|
+
type: "chat.updated",
|
|
71
|
+
payload: chat
|
|
72
|
+
});
|
|
73
|
+
if (initialized && (publishExisting || previous) && (changed || targeted || targetAllMessages || snapshot.status === "RUNNING")) await publishMessageDeltas(chat.id);
|
|
74
|
+
}
|
|
75
|
+
snapshots = nextSnapshots;
|
|
76
|
+
initialized = true;
|
|
77
|
+
} catch {
|
|
78
|
+
pendingAllMessages = pendingAllMessages || targetAllMessages;
|
|
79
|
+
for (const threadId of targetExternalThreadIds) pendingExternalThreadIds.add(threadId);
|
|
80
|
+
} finally {
|
|
81
|
+
syncing = false;
|
|
82
|
+
if (syncAgain) {
|
|
83
|
+
syncAgain = false;
|
|
84
|
+
scheduleProviderSync();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function refreshCodexWatchers() {
|
|
89
|
+
await ensureDatabase();
|
|
90
|
+
const accounts = await prisma.providerAccount.findMany({
|
|
91
|
+
orderBy: { createdAt: "asc" },
|
|
92
|
+
where: {
|
|
93
|
+
providerId: "codex",
|
|
94
|
+
status: "CONNECTED"
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
const paths = new Set(readCodexHistoryWatchPaths());
|
|
98
|
+
for (const account of accounts) for (const path of readCodexHistoryWatchPaths(account)) paths.add(path);
|
|
99
|
+
for (const path of paths) await watchCodexPath(path);
|
|
100
|
+
}
|
|
101
|
+
async function watchCodexPath(path) {
|
|
102
|
+
if (watchers.has(path)) return;
|
|
103
|
+
if (!(await stat(path).catch(() => null))?.isDirectory()) return;
|
|
104
|
+
try {
|
|
105
|
+
const watcher = watch(path, {
|
|
106
|
+
persistent: false,
|
|
107
|
+
recursive: true
|
|
108
|
+
}, (_eventType, filename) => {
|
|
109
|
+
if (isCodexHistoryChange(filename)) scheduleProviderSync({ filename });
|
|
110
|
+
});
|
|
111
|
+
watcher.on("error", () => {
|
|
112
|
+
watchers.delete(path);
|
|
113
|
+
watcher.close();
|
|
114
|
+
});
|
|
115
|
+
watchers.set(path, watcher);
|
|
116
|
+
} catch {
|
|
117
|
+
const watcher = watch(path, { persistent: false }, (_eventType, filename) => {
|
|
118
|
+
if (isCodexHistoryChange(filename)) scheduleProviderSync({ filename });
|
|
119
|
+
});
|
|
120
|
+
watcher.on("error", () => {
|
|
121
|
+
watchers.delete(path);
|
|
122
|
+
watcher.close();
|
|
123
|
+
});
|
|
124
|
+
watchers.set(path, watcher);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function isCodexHistoryChange(filename) {
|
|
128
|
+
if (!filename) return true;
|
|
129
|
+
const path = filename.toString();
|
|
130
|
+
return path.endsWith(".jsonl") || path.includes("session_index.jsonl") || path.includes("logs_2.sqlite");
|
|
131
|
+
}
|
|
132
|
+
function readCodexThreadId(filename) {
|
|
133
|
+
return filename?.toString().match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/iu)?.[1] ?? null;
|
|
134
|
+
}
|
|
135
|
+
//#endregion
|
|
136
|
+
export { startChatStatusMonitor };
|