codesesh 0.14.0 → 0.16.0
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/README.md +1 -0
- package/dist/chunk-EWC6IODK.js +166 -0
- package/dist/chunk-EWC6IODK.js.map +1 -0
- package/dist/{chunk-VRVZJDNL.js → chunk-MWSJTNOW.js} +1044 -386
- package/dist/chunk-MWSJTNOW.js.map +1 -0
- package/dist/{dist-C3KC3XKJ.js → dist-AIDCOQZO.js} +20 -2
- package/dist/index.js +131 -239
- package/dist/index.js.map +1 -1
- package/dist/scan-refresh-worker.js +2 -1
- package/dist/scan-refresh-worker.js.map +1 -1
- package/dist/search-index-worker.js +11 -2
- package/dist/search-index-worker.js.map +1 -1
- package/dist/smart-tag-worker.js +2 -1
- package/dist/smart-tag-worker.js.map +1 -1
- package/dist/web/assets/index-BpmbwB2_.js +113 -0
- package/dist/web/assets/index-Bxe686Vr.css +2 -0
- package/dist/web/assets/{syntax-C_RLNYQn.js → syntax-mZXQuVcd.js} +1 -1
- package/dist/web/assets/vendor-BVE7Fps8.js +41 -0
- package/dist/web/icon/agent/cursor.svg +1 -12
- package/dist/web/icon/agent/pi.svg +2 -2
- package/dist/web/index.html +55 -4
- package/package.json +1 -1
- package/dist/chunk-VRVZJDNL.js.map +0 -1
- package/dist/web/assets/index-BNLxu3RT.js +0 -113
- package/dist/web/assets/index-BYElqfX-.css +0 -2
- package/dist/web/assets/vendor-Lx2mL9a6.js +0 -40
- /package/dist/{dist-C3KC3XKJ.js.map → dist-AIDCOQZO.js.map} +0 -0
package/README.md
CHANGED
|
@@ -21,6 +21,7 @@ Your browser will open at `http://localhost:4521` with all your sessions ready t
|
|
|
21
21
|
- **Unified Timeline** — Browse sessions across all your AI agents in a single, searchable interface
|
|
22
22
|
- **Flexible Time Ranges** — Switch between rolling presets, all history, or a custom date range without restarting the server
|
|
23
23
|
- **Session Aliases** — Give important sessions memorable local names that carry through search, bookmarks, and activity views
|
|
24
|
+
- **Persistent Themes** — Choose light, dark, or system appearance and keep your UI preferences across sessions
|
|
24
25
|
- **Structured Global Search** — Search titles, messages, tool output, and file paths with filters for agent, project, smart tag, tool, file activity, and cost
|
|
25
26
|
- **Dashboard & Activity Trends** — See totals, daily activity, agent distribution, model usage, token trends, smart tags, bookmarks, and recent sessions
|
|
26
27
|
- **Project Browse Mode** — Open a dedicated projects view with project-level metrics, sessions, and cross-agent drill-down
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
setCoreDiagnostics
|
|
4
|
+
} from "./chunk-MWSJTNOW.js";
|
|
5
|
+
|
|
6
|
+
// src/logging.ts
|
|
7
|
+
import {
|
|
8
|
+
appendFileSync,
|
|
9
|
+
existsSync,
|
|
10
|
+
mkdirSync,
|
|
11
|
+
readdirSync,
|
|
12
|
+
renameSync,
|
|
13
|
+
statSync,
|
|
14
|
+
unlinkSync
|
|
15
|
+
} from "fs";
|
|
16
|
+
import { homedir } from "os";
|
|
17
|
+
import { join } from "path";
|
|
18
|
+
var LEVEL_WEIGHT = {
|
|
19
|
+
debug: 10,
|
|
20
|
+
info: 20,
|
|
21
|
+
warn: 30,
|
|
22
|
+
error: 40
|
|
23
|
+
};
|
|
24
|
+
function parseLevel(value) {
|
|
25
|
+
if (value === "debug" || value === "info" || value === "warn" || value === "error") {
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
return "info";
|
|
29
|
+
}
|
|
30
|
+
function parsePositiveInt(value, fallback) {
|
|
31
|
+
const parsed = Number(value);
|
|
32
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : fallback;
|
|
33
|
+
}
|
|
34
|
+
function getDefaultLogDir() {
|
|
35
|
+
const base = process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache");
|
|
36
|
+
return join(base, "codesesh", "logs");
|
|
37
|
+
}
|
|
38
|
+
function toLogValue(value, depth = 0) {
|
|
39
|
+
if (value == null || typeof value === "string" || typeof value === "number") return value;
|
|
40
|
+
if (typeof value === "boolean") return value;
|
|
41
|
+
if (typeof value === "bigint") return value.toString();
|
|
42
|
+
if (value instanceof Error) {
|
|
43
|
+
return {
|
|
44
|
+
name: value.name,
|
|
45
|
+
message: value.message,
|
|
46
|
+
stack: value.stack
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (depth >= 4) return "[truncated]";
|
|
50
|
+
if (Array.isArray(value)) return value.slice(0, 50).map((item) => toLogValue(item, depth + 1));
|
|
51
|
+
if (typeof value === "object") {
|
|
52
|
+
return Object.fromEntries(
|
|
53
|
+
Object.entries(value).map(([key, item]) => [
|
|
54
|
+
key,
|
|
55
|
+
toLogValue(item, depth + 1)
|
|
56
|
+
])
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
return String(value);
|
|
60
|
+
}
|
|
61
|
+
function timestampForFile(date = /* @__PURE__ */ new Date()) {
|
|
62
|
+
return date.toISOString().replace(/[:.]/g, "-");
|
|
63
|
+
}
|
|
64
|
+
var AppLogger = class {
|
|
65
|
+
logDir;
|
|
66
|
+
level;
|
|
67
|
+
maxBytes;
|
|
68
|
+
maxFiles;
|
|
69
|
+
currentPath;
|
|
70
|
+
rotationIndex = 0;
|
|
71
|
+
constructor(options = {}) {
|
|
72
|
+
this.logDir = options.logDir ?? process.env.CODESESH_LOG_DIR ?? getDefaultLogDir();
|
|
73
|
+
this.level = options.level ?? parseLevel(process.env.CODESESH_LOG_LEVEL);
|
|
74
|
+
this.maxBytes = options.maxBytes ?? parsePositiveInt(process.env.CODESESH_LOG_MAX_BYTES, 5e6);
|
|
75
|
+
this.maxFiles = options.maxFiles ?? parsePositiveInt(process.env.CODESESH_LOG_MAX_FILES, 5);
|
|
76
|
+
this.currentPath = join(this.logDir, "codesesh.log");
|
|
77
|
+
}
|
|
78
|
+
getLogPath() {
|
|
79
|
+
return this.currentPath;
|
|
80
|
+
}
|
|
81
|
+
debug(event, data = {}) {
|
|
82
|
+
this.write("debug", event, data);
|
|
83
|
+
}
|
|
84
|
+
info(event, data = {}) {
|
|
85
|
+
this.write("info", event, data);
|
|
86
|
+
}
|
|
87
|
+
warn(event, data = {}) {
|
|
88
|
+
this.write("warn", event, data);
|
|
89
|
+
}
|
|
90
|
+
error(event, data = {}) {
|
|
91
|
+
this.write("error", event, data);
|
|
92
|
+
}
|
|
93
|
+
write(level, event, data) {
|
|
94
|
+
if (LEVEL_WEIGHT[level] < LEVEL_WEIGHT[this.level]) return;
|
|
95
|
+
try {
|
|
96
|
+
mkdirSync(this.logDir, { recursive: true });
|
|
97
|
+
const line = `${JSON.stringify({
|
|
98
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
99
|
+
level,
|
|
100
|
+
event,
|
|
101
|
+
pid: process.pid,
|
|
102
|
+
...toLogValue(data)
|
|
103
|
+
})}
|
|
104
|
+
`;
|
|
105
|
+
this.rotateIfNeeded(Buffer.byteLength(line));
|
|
106
|
+
appendFileSync(this.currentPath, line, "utf8");
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
rotateIfNeeded(nextBytes) {
|
|
111
|
+
if (!existsSync(this.currentPath)) {
|
|
112
|
+
this.removeExpiredLogs();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const currentSize = statSync(this.currentPath).size;
|
|
116
|
+
if (currentSize + nextBytes <= this.maxBytes) return;
|
|
117
|
+
this.rotationIndex += 1;
|
|
118
|
+
const rotatedPath = join(
|
|
119
|
+
this.logDir,
|
|
120
|
+
`codesesh-${timestampForFile()}-${process.pid}-${this.rotationIndex}.log`
|
|
121
|
+
);
|
|
122
|
+
renameSync(this.currentPath, rotatedPath);
|
|
123
|
+
this.removeExpiredLogs();
|
|
124
|
+
}
|
|
125
|
+
removeExpiredLogs() {
|
|
126
|
+
const rotated = readdirSync(this.logDir).filter((name) => /^codesesh-.+\.log$/.test(name)).map((name) => {
|
|
127
|
+
const path = join(this.logDir, name);
|
|
128
|
+
return { path, mtimeMs: statSync(path).mtimeMs };
|
|
129
|
+
}).toSorted((a, b) => b.mtimeMs - a.mtimeMs);
|
|
130
|
+
for (const item of rotated.slice(Math.max(0, this.maxFiles - 1))) {
|
|
131
|
+
unlinkSync(item.path);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
var appLogger = new AppLogger();
|
|
136
|
+
function logSearchIndexSync(context, result, data = {}) {
|
|
137
|
+
if (!result || result.mode !== "bulk" || result.rebuildDurationMs == null) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
appLogger.info("search_index.sync", {
|
|
141
|
+
context,
|
|
142
|
+
agent: result.agentName,
|
|
143
|
+
mode: result.mode,
|
|
144
|
+
sessions: result.sessions,
|
|
145
|
+
changed: result.changed,
|
|
146
|
+
deleted: result.deleted,
|
|
147
|
+
indexed: result.indexed,
|
|
148
|
+
skipped: result.skipped,
|
|
149
|
+
duration_ms: Math.round(result.durationMs),
|
|
150
|
+
rebuild_duration_ms: Math.round(result.rebuildDurationMs),
|
|
151
|
+
...data
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// src/diagnostics-bridge.ts
|
|
156
|
+
setCoreDiagnostics({
|
|
157
|
+
warn(event, detail) {
|
|
158
|
+
appLogger.warn(event, detail);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
export {
|
|
163
|
+
appLogger,
|
|
164
|
+
logSearchIndexSync
|
|
165
|
+
};
|
|
166
|
+
//# sourceMappingURL=chunk-EWC6IODK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/logging.ts","../src/diagnostics-bridge.ts"],"sourcesContent":["import {\n appendFileSync,\n existsSync,\n mkdirSync,\n readdirSync,\n renameSync,\n statSync,\n unlinkSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { SearchIndexSyncResult } from \"@codesesh/core\";\n\ntype LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst LEVEL_WEIGHT: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n};\n\nexport interface LoggerOptions {\n logDir?: string;\n level?: LogLevel;\n maxBytes?: number;\n maxFiles?: number;\n}\n\nfunction parseLevel(value: string | undefined): LogLevel {\n if (value === \"debug\" || value === \"info\" || value === \"warn\" || value === \"error\") {\n return value;\n }\n return \"info\";\n}\n\nfunction parsePositiveInt(value: string | undefined, fallback: number): number {\n const parsed = Number(value);\n return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : fallback;\n}\n\nfunction getDefaultLogDir(): string {\n const base = process.env.XDG_CACHE_HOME ?? join(homedir(), \".cache\");\n return join(base, \"codesesh\", \"logs\");\n}\n\nfunction toLogValue(value: unknown, depth = 0): unknown {\n if (value == null || typeof value === \"string\" || typeof value === \"number\") return value;\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"bigint\") return value.toString();\n if (value instanceof Error) {\n return {\n name: value.name,\n message: value.message,\n stack: value.stack,\n };\n }\n if (depth >= 4) return \"[truncated]\";\n if (Array.isArray(value)) return value.slice(0, 50).map((item) => toLogValue(item, depth + 1));\n if (typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>).map(([key, item]) => [\n key,\n toLogValue(item, depth + 1),\n ]),\n );\n }\n return String(value);\n}\n\nfunction timestampForFile(date = new Date()): string {\n return date.toISOString().replace(/[:.]/g, \"-\");\n}\n\nexport class AppLogger {\n private readonly logDir: string;\n private readonly level: LogLevel;\n private readonly maxBytes: number;\n private readonly maxFiles: number;\n private readonly currentPath: string;\n private rotationIndex = 0;\n\n constructor(options: LoggerOptions = {}) {\n this.logDir = options.logDir ?? process.env.CODESESH_LOG_DIR ?? getDefaultLogDir();\n this.level = options.level ?? parseLevel(process.env.CODESESH_LOG_LEVEL);\n this.maxBytes =\n options.maxBytes ?? parsePositiveInt(process.env.CODESESH_LOG_MAX_BYTES, 5_000_000);\n this.maxFiles = options.maxFiles ?? parsePositiveInt(process.env.CODESESH_LOG_MAX_FILES, 5);\n this.currentPath = join(this.logDir, \"codesesh.log\");\n }\n\n getLogPath(): string {\n return this.currentPath;\n }\n\n debug(event: string, data: Record<string, unknown> = {}): void {\n this.write(\"debug\", event, data);\n }\n\n info(event: string, data: Record<string, unknown> = {}): void {\n this.write(\"info\", event, data);\n }\n\n warn(event: string, data: Record<string, unknown> = {}): void {\n this.write(\"warn\", event, data);\n }\n\n error(event: string, data: Record<string, unknown> = {}): void {\n this.write(\"error\", event, data);\n }\n\n private write(level: LogLevel, event: string, data: Record<string, unknown>): void {\n if (LEVEL_WEIGHT[level] < LEVEL_WEIGHT[this.level]) return;\n\n try {\n mkdirSync(this.logDir, { recursive: true });\n const line = `${JSON.stringify({\n ts: new Date().toISOString(),\n level,\n event,\n pid: process.pid,\n ...(toLogValue(data) as Record<string, unknown>),\n })}\\n`;\n this.rotateIfNeeded(Buffer.byteLength(line));\n appendFileSync(this.currentPath, line, \"utf8\");\n } catch {}\n }\n\n private rotateIfNeeded(nextBytes: number): void {\n if (!existsSync(this.currentPath)) {\n this.removeExpiredLogs();\n return;\n }\n\n const currentSize = statSync(this.currentPath).size;\n if (currentSize + nextBytes <= this.maxBytes) return;\n\n this.rotationIndex += 1;\n const rotatedPath = join(\n this.logDir,\n `codesesh-${timestampForFile()}-${process.pid}-${this.rotationIndex}.log`,\n );\n renameSync(this.currentPath, rotatedPath);\n this.removeExpiredLogs();\n }\n\n private removeExpiredLogs(): void {\n const rotated = readdirSync(this.logDir)\n .filter((name) => /^codesesh-.+\\.log$/.test(name))\n .map((name) => {\n const path = join(this.logDir, name);\n return { path, mtimeMs: statSync(path).mtimeMs };\n })\n .toSorted((a, b) => b.mtimeMs - a.mtimeMs);\n\n for (const item of rotated.slice(Math.max(0, this.maxFiles - 1))) {\n unlinkSync(item.path);\n }\n }\n}\n\nexport const appLogger = new AppLogger();\n\nexport function logSearchIndexSync(\n context: string,\n result: SearchIndexSyncResult | null,\n data: Record<string, unknown> = {},\n): void {\n if (!result || result.mode !== \"bulk\" || result.rebuildDurationMs == null) {\n return;\n }\n\n appLogger.info(\"search_index.sync\", {\n context,\n agent: result.agentName,\n mode: result.mode,\n sessions: result.sessions,\n changed: result.changed,\n deleted: result.deleted,\n indexed: result.indexed,\n skipped: result.skipped,\n duration_ms: Math.round(result.durationMs),\n rebuild_duration_ms: Math.round(result.rebuildDurationMs),\n ...data,\n });\n}\n","import { setCoreDiagnostics } from \"@codesesh/core\";\nimport { appLogger } from \"./logging.js\";\n\n/**\n * Bridges core's diagnostics sink to appLogger. Import this once, for its\n * side effect, from every entry point that gets its own module graph — the\n * main CLI thread and each worker_threads script (scan-refresh-worker,\n * search-index-worker, smart-tag-worker) — since core's module-level\n * diagnostics singleton is per-thread, not shared across workers.\n */\nsetCoreDiagnostics({\n warn(event, detail) {\n appLogger.warn(event, detail);\n },\n});\n"],"mappings":"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","names":[]}
|