failproofai 0.0.9-beta.0 → 0.0.9-beta.2
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +2 -2
- package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -2
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
- package/.next/standalone/.next/server/app/policies/page.js +2 -2
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +4 -3
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +2 -2
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g72weg._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0su~k6f._.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_codex-projects_ts_07qqk1g._.js +3 -0
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_0z7w.hh._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01743wx._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__092s1ta._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09icjsf._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ca1zru._.js → [root-of-the-server]__0eu4j_n._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g.lg8b._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0gs6wz4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h..k-e._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0it81ys._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u4a9jq._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ea22pr._.js → [root-of-the-server]__0vrlxf2._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ymlddl._.js +18 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11pa2ra._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12.h2mg._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12t-wym._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_04w00cm._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_10lm7or._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_0cdqd9w._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_0xerkr6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_0q-m0y-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/lib_codex-projects_ts_0eosib~._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/lib_utils_ts_068jk73._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0zaq1hm._.js → node_modules_0ttbz1~._.js} +2 -2
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +2 -2
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +9 -9
- package/.next/standalone/.next/static/chunks/03egp37o1l629.js +1 -0
- package/.next/standalone/.next/static/chunks/06j6c0ofqjy0v.js +6 -0
- package/.next/standalone/.next/static/chunks/06x4-d1~o-opr.js +1 -0
- package/.next/standalone/.next/static/chunks/{0uq_5p-p7myfe.js → 0e8c_1f7-8e7t.js} +3 -3
- package/.next/standalone/.next/static/chunks/0mumk7h5i.1xd.js +1 -0
- package/.next/standalone/.next/static/chunks/0n~s0gafwnp2y.js +1 -0
- package/.next/standalone/.next/static/chunks/{0amfi~vb_gfgo.js → 0zdn~84f58hvf.js} +1 -1
- package/.next/standalone/.next/static/chunks/0zig0fh30t6ou.js +1 -0
- package/.next/standalone/.next/static/chunks/{03lsndql_yml5.js → 10mlwc4y_kqo2.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0~mroziiwl1m5.js → 15_mi91qaeieu.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0vb8xxj_v2tz8.js → 18a9xv2p3~x.9.js} +1 -1
- package/.next/standalone/app/components/cli-badge.tsx +24 -0
- package/.next/standalone/app/components/project-list.tsx +13 -7
- package/.next/standalone/app/components/sessions-list.tsx +4 -2
- package/.next/standalone/app/policies/hooks-client.tsx +66 -10
- package/.next/standalone/app/project/[name]/page.tsx +49 -22
- package/.next/standalone/app/project/[name]/session/[sessionId]/page.tsx +51 -19
- package/.next/standalone/components/reach-developers.tsx +6 -1
- package/.next/standalone/lib/codex-projects.ts +250 -0
- package/.next/standalone/lib/codex-sessions.ts +414 -0
- package/.next/standalone/lib/format-date.ts +21 -0
- package/.next/standalone/lib/log-entries.ts +3 -3
- package/.next/standalone/lib/paths.ts +13 -0
- package/.next/standalone/lib/projects.ts +57 -3
- package/.next/standalone/lib/utils.ts +6 -22
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +1 -0
- package/dist/cli.mjs +1142 -138
- package/lib/codex-projects.ts +250 -0
- package/lib/codex-sessions.ts +414 -0
- package/lib/format-date.ts +21 -0
- package/lib/log-entries.ts +3 -3
- package/lib/paths.ts +13 -0
- package/lib/projects.ts +57 -3
- package/lib/utils.ts +6 -22
- package/package.json +1 -1
- package/scripts/launch.ts +2 -1
- package/src/hooks/builtin-policies.ts +7 -1
- package/src/hooks/hook-activity-store.ts +3 -0
- package/src/hooks/install-prompt.ts +119 -23
- package/src/hooks/manager.ts +1 -1
- package/src/hooks/resolve-permission-mode.ts +6 -91
- package/.next/standalone/.next/server/chunks/[externals]__080wern._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0b57.gk._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__03rd.z8._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e74wa-._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0vu.o-3._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w6l33k._.js +0 -17
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0zqcovi._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__105.l_7._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0uy6m~m._.js +0 -3
- package/.next/standalone/.next/static/chunks/00b5h4r1el.6f.js +0 -1
- package/.next/standalone/.next/static/chunks/0fw2h.g66c0h3.js +0 -1
- package/.next/standalone/.next/static/chunks/0jce49ygr4fdv.js +0 -1
- package/.next/standalone/.next/static/chunks/0mungg3~jpwe7.js +0 -1
- package/.next/standalone/.next/static/chunks/0v.xuf4ynzp~~.js +0 -6
- package/.next/standalone/.next/static/chunks/0vwqucikost_q.js +0 -1
- /package/.next/standalone/.next/static/{oUO8u4z9JvtTzS_2RJoGo → SyaO-J1hupjAiRCG-Syzg}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{oUO8u4z9JvtTzS_2RJoGo → SyaO-J1hupjAiRCG-Syzg}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{oUO8u4z9JvtTzS_2RJoGo → SyaO-J1hupjAiRCG-Syzg}/_ssgManifest.js +0 -0
package/lib/log-entries.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { resolveProjectPath } from "./projects";
|
|
|
5
5
|
import { resolveSubagentPath } from "./resolve-subagent-path";
|
|
6
6
|
import { runtimeCache } from "./runtime-cache";
|
|
7
7
|
import { batchAll } from "./concurrency";
|
|
8
|
-
import { formatDate } from "./
|
|
8
|
+
import { formatDate } from "./format-date";
|
|
9
9
|
import { formatDuration } from "./format-duration";
|
|
10
10
|
|
|
11
11
|
// ── Source Tagging ──
|
|
@@ -115,14 +115,14 @@ export type LogEntryType = LogEntry["type"];
|
|
|
115
115
|
|
|
116
116
|
// ── Helpers ──
|
|
117
117
|
|
|
118
|
-
function formatTimestamp(date: Date): string {
|
|
118
|
+
export function formatTimestamp(date: Date): string {
|
|
119
119
|
const base = formatDate(date);
|
|
120
120
|
const ms = date.getMilliseconds().toString().padStart(3, "0");
|
|
121
121
|
return `${base}.${ms}`;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/** Shared base fields present on every log entry. */
|
|
125
|
-
function baseEntry(raw: Record<string, unknown>, timestamp: string, date: Date, source: LogSource) {
|
|
125
|
+
export function baseEntry(raw: Record<string, unknown>, timestamp: string, date: Date, source: LogSource) {
|
|
126
126
|
return {
|
|
127
127
|
_source: source,
|
|
128
128
|
uuid: (raw.uuid as string) || "",
|
package/lib/paths.ts
CHANGED
|
@@ -29,6 +29,19 @@ export function decodeFolderName(name: string): string {
|
|
|
29
29
|
return name.replace(/-/g, "/");
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Encodes a filesystem path into a Claude-compatible project folder name.
|
|
34
|
+
* Inverse of `decodeFolderName`.
|
|
35
|
+
*/
|
|
36
|
+
export function encodeFolderName(path: string): string {
|
|
37
|
+
// Windows drive-letter pattern: "C:/code/project" → "C--code-project"
|
|
38
|
+
const driveMatch = /^([A-Za-z]):[\\/](.*)$/.exec(path);
|
|
39
|
+
if (driveMatch) {
|
|
40
|
+
return driveMatch[1] + "--" + driveMatch[2].replace(/[\\/]/g, "-");
|
|
41
|
+
}
|
|
42
|
+
return path.replace(/[\\/]/g, "-");
|
|
43
|
+
}
|
|
44
|
+
|
|
32
45
|
export function getClaudeProjectsPath(): string {
|
|
33
46
|
// Check if path is provided via environment variable
|
|
34
47
|
const envPath = process.env.CLAUDE_PROJECTS_PATH;
|
package/lib/projects.ts
CHANGED
|
@@ -11,17 +11,24 @@ import { getClaudeProjectsPath } from "./paths";
|
|
|
11
11
|
import { runtimeCache } from "./runtime-cache";
|
|
12
12
|
import { batchAll } from "./concurrency";
|
|
13
13
|
import { logWarn, logError } from "./logger";
|
|
14
|
-
import { formatDate } from "./
|
|
14
|
+
import { formatDate } from "./format-date";
|
|
15
15
|
|
|
16
16
|
export const UUID_RE = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
|
|
17
17
|
export const PATH_TRAVERSAL_RE = /(^|[\\/])\.\.($|[\\/])/;
|
|
18
18
|
|
|
19
|
+
export type ProjectCli = "claude" | "codex";
|
|
20
|
+
|
|
19
21
|
export interface ProjectFolder {
|
|
20
22
|
name: string;
|
|
21
23
|
path: string;
|
|
22
24
|
isDirectory: boolean;
|
|
23
25
|
lastModified: Date;
|
|
24
26
|
lastModifiedFormatted?: string; // Pre-formatted date string to avoid hydration issues
|
|
27
|
+
/**
|
|
28
|
+
* Which agent CLIs this project's data was found in. Multiple entries when
|
|
29
|
+
* the same cwd has both Claude and Codex transcripts; rendered as badges.
|
|
30
|
+
*/
|
|
31
|
+
cli: ProjectCli[];
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
export interface SessionFile {
|
|
@@ -30,6 +37,9 @@ export interface SessionFile {
|
|
|
30
37
|
lastModified: Date;
|
|
31
38
|
lastModifiedFormatted?: string;
|
|
32
39
|
sessionId?: string;
|
|
40
|
+
/** Originating agent CLI. Set when the session list mixes Claude + Codex sources
|
|
41
|
+
* so the table can render a per-row CLI badge. */
|
|
42
|
+
cli?: ProjectCli;
|
|
33
43
|
}
|
|
34
44
|
|
|
35
45
|
/** Stats a path and returns mtime. Falls back to epoch (1970-01-01) on error
|
|
@@ -54,7 +64,7 @@ async function safeReaddir(dirPath: string) {
|
|
|
54
64
|
}
|
|
55
65
|
}
|
|
56
66
|
|
|
57
|
-
|
|
67
|
+
async function getClaudeProjectFolders(): Promise<ProjectFolder[]> {
|
|
58
68
|
try {
|
|
59
69
|
const projectsPath = getClaudeProjectsPath();
|
|
60
70
|
const entries = await safeReaddir(projectsPath);
|
|
@@ -72,6 +82,7 @@ export async function getProjectFolders(): Promise<ProjectFolder[]> {
|
|
|
72
82
|
isDirectory: true,
|
|
73
83
|
lastModified: mtime,
|
|
74
84
|
lastModifiedFormatted: formatDate(mtime),
|
|
85
|
+
cli: ["claude"],
|
|
75
86
|
} as ProjectFolder;
|
|
76
87
|
}),
|
|
77
88
|
16,
|
|
@@ -83,11 +94,53 @@ export async function getProjectFolders(): Promise<ProjectFolder[]> {
|
|
|
83
94
|
folders.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
|
|
84
95
|
return folders;
|
|
85
96
|
} catch (error) {
|
|
86
|
-
logError("Error reading project folders:", error);
|
|
97
|
+
logError("Error reading Claude project folders:", error);
|
|
87
98
|
return [];
|
|
88
99
|
}
|
|
89
100
|
}
|
|
90
101
|
|
|
102
|
+
/** Merges Claude + Codex project lists by encoded folder name. When both sources have
|
|
103
|
+
* the same name, keeps the Claude entry's `path` (so the Path column still points at
|
|
104
|
+
* `~/.claude/projects/<encoded>`), unions the `cli` arrays in [claude, codex] order,
|
|
105
|
+
* and takes the newer `lastModified`. */
|
|
106
|
+
function mergeProjectFolders(claude: ProjectFolder[], codex: ProjectFolder[]): ProjectFolder[] {
|
|
107
|
+
const byName = new Map<string, ProjectFolder>();
|
|
108
|
+
for (const f of claude) byName.set(f.name, { ...f, cli: [...f.cli] });
|
|
109
|
+
for (const f of codex) {
|
|
110
|
+
const existing = byName.get(f.name);
|
|
111
|
+
if (!existing) {
|
|
112
|
+
byName.set(f.name, { ...f, cli: [...f.cli] });
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const mergedCli: ProjectCli[] = [...existing.cli];
|
|
116
|
+
for (const c of f.cli) if (!mergedCli.includes(c)) mergedCli.push(c);
|
|
117
|
+
const newer = f.lastModified.getTime() > existing.lastModified.getTime() ? f : existing;
|
|
118
|
+
byName.set(f.name, {
|
|
119
|
+
...existing,
|
|
120
|
+
cli: mergedCli,
|
|
121
|
+
lastModified: newer.lastModified,
|
|
122
|
+
lastModifiedFormatted: newer.lastModifiedFormatted,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const merged = Array.from(byName.values());
|
|
126
|
+
merged.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
|
|
127
|
+
return merged;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function getProjectFolders(): Promise<ProjectFolder[]> {
|
|
131
|
+
// Lazy import to keep `lib/codex-projects.ts` out of the dep graph for callers that
|
|
132
|
+
// only need Claude helpers (e.g. CLI codepaths).
|
|
133
|
+
const { getCodexProjects } = await import("./codex-projects");
|
|
134
|
+
const [claude, codex] = await Promise.all([
|
|
135
|
+
getClaudeProjectFolders(),
|
|
136
|
+
getCodexProjects().catch((error) => {
|
|
137
|
+
logError("Error reading Codex projects:", error);
|
|
138
|
+
return [] as ProjectFolder[];
|
|
139
|
+
}),
|
|
140
|
+
]);
|
|
141
|
+
return mergeProjectFolders(claude, codex);
|
|
142
|
+
}
|
|
143
|
+
|
|
91
144
|
/**
|
|
92
145
|
* Gets the full path to a specific project folder
|
|
93
146
|
* @param projectName - Name of the project folder
|
|
@@ -157,6 +210,7 @@ export async function getSessionFiles(projectPath: string): Promise<SessionFile[
|
|
|
157
210
|
lastModified: mtime,
|
|
158
211
|
lastModifiedFormatted: formatDate(mtime),
|
|
159
212
|
sessionId: extractSessionId(entry.name),
|
|
213
|
+
cli: "claude",
|
|
160
214
|
} as SessionFile;
|
|
161
215
|
}),
|
|
162
216
|
16,
|
package/lib/utils.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Tailwind class-name merger.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* -
|
|
4
|
+
* `cn()` combines `clsx` (conditional class composition) with `tailwind-merge`
|
|
5
|
+
* (last-wins conflict resolution for Tailwind utility classes).
|
|
6
|
+
*
|
|
7
|
+
* Date formatting lives in lib/format-date.ts — kept separate so server-side
|
|
8
|
+
* callers don't need to load clsx/tailwind-merge just to print a timestamp.
|
|
6
9
|
*/
|
|
7
10
|
import { clsx, type ClassValue } from "clsx";
|
|
8
11
|
import { twMerge } from "tailwind-merge";
|
|
@@ -10,22 +13,3 @@ import { twMerge } from "tailwind-merge";
|
|
|
10
13
|
export function cn(...inputs: ClassValue[]) {
|
|
11
14
|
return twMerge(clsx(inputs));
|
|
12
15
|
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Formats a date to a readable string format (e.g., "Jan 15, 2024, 3:45 PM").
|
|
16
|
-
*
|
|
17
|
-
* Creates a new Intl.DateTimeFormat on each call intentionally — this runs
|
|
18
|
-
* server-side where there's no shared state concern. The client-side hot-path
|
|
19
|
-
* formatter in lib/log-format.ts caches its instance at module scope instead.
|
|
20
|
-
*/
|
|
21
|
-
export function formatDate(date: Date): string {
|
|
22
|
-
return new Intl.DateTimeFormat("en-US", {
|
|
23
|
-
month: "short",
|
|
24
|
-
day: "numeric",
|
|
25
|
-
year: "numeric",
|
|
26
|
-
hour: "numeric",
|
|
27
|
-
minute: "2-digit",
|
|
28
|
-
hour12: true,
|
|
29
|
-
}).format(date);
|
|
30
|
-
}
|
|
31
|
-
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "failproofai",
|
|
3
|
-
"version": "0.0.9-beta.
|
|
3
|
+
"version": "0.0.9-beta.2",
|
|
4
4
|
"description": "The easiest way to manage policies that keep your AI agents reliable, on-task, and running autonomously — for Claude Code & the Agents SDK",
|
|
5
5
|
"bin": {
|
|
6
6
|
"failproofai": "./dist/cli.mjs"
|
package/scripts/launch.ts
CHANGED
|
@@ -21,7 +21,8 @@ export function launch(mode: "dev" | "start"): void {
|
|
|
21
21
|
/_/ v${version}
|
|
22
22
|
`);
|
|
23
23
|
console.log(` ⭐ Star us: https://github.com/exospherehost/failproofai`);
|
|
24
|
-
console.log(` 📖 Docs: https://befailproof.ai
|
|
24
|
+
console.log(` 📖 Docs: https://befailproof.ai`);
|
|
25
|
+
console.log(` 💬 Slack: https://join.slack.com/t/failproofai/shared_invite/zt-3v63b7k5e-O3NBHmj8X6n9gZSGDx6ggQ\n`);
|
|
25
26
|
|
|
26
27
|
let claudeProjectsPath = parsedPath;
|
|
27
28
|
|
|
@@ -664,10 +664,16 @@ const READ_LIKE_CMDS =
|
|
|
664
664
|
* This catches `cat "/etc/passwd"` while avoiding false positives from grep
|
|
665
665
|
* patterns and find glob patterns that appear in later pipeline stages.
|
|
666
666
|
* Unquoted absolute paths are extracted from the whole command as before.
|
|
667
|
+
*
|
|
668
|
+
* The negative lookbehind also excludes glob metacharacters ('*', '?') and
|
|
669
|
+
* separator characters that appear in compound argv tokens (':' for Docker
|
|
670
|
+
* volume mounts and PATH-like lists, '=' for env var assignments) so that a
|
|
671
|
+
* suffix like '/dashboard.mdx' in 'docs/STAR/dashboard.mdx' or '/docs' in
|
|
672
|
+
* '-v HOST_DIR:/docs' is not misread as a standalone absolute path.
|
|
667
673
|
*/
|
|
668
674
|
function extractAbsolutePaths(command: string): string[] {
|
|
669
675
|
const paths: string[] = [];
|
|
670
|
-
const pathRe = /(?<![a-zA-Z0-9_
|
|
676
|
+
const pathRe = /(?<![a-zA-Z0-9_.\-~\\*?:=])(?:~\/[^\s;|&"'()\[\]{}]*|~(?=\s|$|[;|&"'()\[\]{}])|\/[^\s;|&"'()\[\]{}]*)/g;
|
|
671
677
|
|
|
672
678
|
function addPaths(s: string): void {
|
|
673
679
|
pathRe.lastIndex = 0;
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
} from "node:fs";
|
|
24
24
|
import { join } from "node:path";
|
|
25
25
|
import { homedir } from "node:os";
|
|
26
|
+
import type { IntegrationType } from "./types";
|
|
26
27
|
|
|
27
28
|
export const PAGE_SIZE = 25;
|
|
28
29
|
|
|
@@ -61,6 +62,7 @@ export interface HookActivityFilters {
|
|
|
61
62
|
eventType?: string;
|
|
62
63
|
policyName?: string;
|
|
63
64
|
sessionId?: string;
|
|
65
|
+
integration?: IntegrationType;
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
export interface HookActivityStats {
|
|
@@ -281,6 +283,7 @@ export function searchHookActivity(
|
|
|
281
283
|
) {
|
|
282
284
|
return false;
|
|
283
285
|
}
|
|
286
|
+
if (filters.integration && entry.integration !== filters.integration) return false;
|
|
284
287
|
return true;
|
|
285
288
|
});
|
|
286
289
|
|
|
@@ -37,8 +37,8 @@ export interface PromptOptions {
|
|
|
37
37
|
* • If `explicit` is provided (from `--cli`), use it as-is.
|
|
38
38
|
* • Else, detect installed CLIs (PATH probe).
|
|
39
39
|
* • If exactly one detected → use just that one (no prompt).
|
|
40
|
-
* • If
|
|
41
|
-
* • Otherwise → default to ["claude"]
|
|
40
|
+
* • If multiple detected and stdin is a TTY → arrow-key single-select.
|
|
41
|
+
* • Otherwise → default to all detected (or ["claude"] when none).
|
|
42
42
|
*
|
|
43
43
|
* Returns the selected IntegrationType[] (always non-empty).
|
|
44
44
|
*/
|
|
@@ -61,38 +61,134 @@ export async function resolveTargetClis(explicit?: IntegrationType[]): Promise<I
|
|
|
61
61
|
return detected;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
//
|
|
65
|
-
if (!process.stdin.isTTY) return detected; // non-interactive: install for
|
|
64
|
+
// Multiple detected. Prompt or default.
|
|
65
|
+
if (!process.stdin.isTTY) return detected; // non-interactive: install for all detected
|
|
66
66
|
|
|
67
|
+
return promptCliTargetSelection(detected);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Interactive arrow-key single-select for "install for which CLI?" when
|
|
72
|
+
* multiple agent CLIs are detected. Visual style mirrors promptPolicySelection.
|
|
73
|
+
*/
|
|
74
|
+
async function promptCliTargetSelection(
|
|
75
|
+
detected: IntegrationType[],
|
|
76
|
+
): Promise<IntegrationType[]> {
|
|
67
77
|
const labels = detected.map((id) => getIntegration(id).displayName).join(" + ");
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
78
|
+
const options: Array<{ label: string; description: string; value: IntegrationType[] }> = [
|
|
79
|
+
{ label: "Both", description: labels, value: detected },
|
|
80
|
+
...detected.map((id) => ({
|
|
81
|
+
label: `${getIntegration(id).displayName} only`,
|
|
82
|
+
description: "",
|
|
83
|
+
value: [id] as IntegrationType[],
|
|
84
|
+
})),
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
let cursor = 0;
|
|
88
|
+
let lastLineCount = 0;
|
|
89
|
+
let cursorHidden = false;
|
|
90
|
+
|
|
91
|
+
function hideCursor(): void {
|
|
92
|
+
if (!cursorHidden) {
|
|
93
|
+
process.stdout.write("\x1B[?25l");
|
|
94
|
+
cursorHidden = true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function showCursor(): void {
|
|
98
|
+
if (cursorHidden) {
|
|
99
|
+
process.stdout.write("\x1B[?25h");
|
|
100
|
+
cursorHidden = false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function truncateLine(line: string, width: number): string {
|
|
105
|
+
let visual = 0;
|
|
106
|
+
let result = "";
|
|
107
|
+
let i = 0;
|
|
108
|
+
while (i < line.length) {
|
|
109
|
+
if (line[i] === "\x1B" && line[i + 1] === "[") {
|
|
110
|
+
let j = i + 2;
|
|
111
|
+
while (j < line.length && !/[A-Za-z]/.test(line[j])) j++;
|
|
112
|
+
j++;
|
|
113
|
+
result += line.slice(i, j);
|
|
114
|
+
i = j;
|
|
115
|
+
} else {
|
|
116
|
+
if (visual >= width) break;
|
|
117
|
+
result += line[i];
|
|
118
|
+
visual++;
|
|
119
|
+
i++;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function render(): void {
|
|
126
|
+
const cols = process.stdout.columns || 120;
|
|
127
|
+
hideCursor();
|
|
128
|
+
|
|
129
|
+
const lines: string[] = [];
|
|
130
|
+
lines.push(" Failproof AI — Install Hooks");
|
|
131
|
+
lines.push("");
|
|
132
|
+
lines.push(` \x1B[2mDetected ${labels}. Choose where to install:\x1B[0m`);
|
|
133
|
+
lines.push("");
|
|
134
|
+
|
|
135
|
+
for (let i = 0; i < options.length; i++) {
|
|
136
|
+
const opt = options[i];
|
|
137
|
+
const isActive = i === cursor;
|
|
138
|
+
const pointer = isActive ? "\x1B[36m❯\x1B[0m" : " ";
|
|
139
|
+
const labelPart = isActive ? `\x1B[1;36m${opt.label}\x1B[0m` : opt.label;
|
|
140
|
+
const pad = opt.description ? " ".repeat(Math.max(2, 22 - opt.label.length)) : "";
|
|
141
|
+
const desc = opt.description ? `\x1B[2m${opt.description}\x1B[0m` : "";
|
|
142
|
+
lines.push(` ${pointer} ${labelPart}${pad}${desc}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
lines.push("");
|
|
146
|
+
lines.push(" \x1B[2m" + "─".repeat(Math.max(2, cols - 2)) + "\x1B[0m");
|
|
147
|
+
lines.push(" [↑↓] Move [Enter] Select [^C] Quit");
|
|
148
|
+
|
|
149
|
+
if (lastLineCount > 0) {
|
|
150
|
+
process.stdout.write(`\x1B[${lastLineCount}A\x1B[J`);
|
|
151
|
+
}
|
|
152
|
+
const output =
|
|
153
|
+
lines.map((l) => (l === "" ? l : truncateLine(l, cols))).join("\n") + "\n";
|
|
154
|
+
process.stdout.write(output);
|
|
155
|
+
lastLineCount = lines.length;
|
|
156
|
+
}
|
|
71
157
|
|
|
72
158
|
return new Promise<IntegrationType[]>((resolve) => {
|
|
159
|
+
render();
|
|
73
160
|
readline.emitKeypressEvents(process.stdin);
|
|
74
161
|
const wasRaw = process.stdin.isRaw;
|
|
75
162
|
if (process.stdin.setRawMode) process.stdin.setRawMode(true);
|
|
76
|
-
|
|
77
|
-
|
|
163
|
+
process.stdin.resume();
|
|
164
|
+
|
|
165
|
+
function cleanup(): void {
|
|
166
|
+
showCursor();
|
|
78
167
|
process.stdin.removeListener("keypress", onKey);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (key
|
|
85
|
-
|
|
168
|
+
if (process.stdin.setRawMode) process.stdin.setRawMode(wasRaw ?? false);
|
|
169
|
+
process.stdin.pause();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function onKey(_str: string | undefined, key: readline.Key): void {
|
|
173
|
+
if (!key) return;
|
|
174
|
+
if (key.ctrl && (key.name === "c" || key.name === "d")) {
|
|
175
|
+
cleanup();
|
|
86
176
|
process.stdout.write("\n");
|
|
87
177
|
process.exit(130); // SIGINT-equivalent
|
|
88
178
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
179
|
+
if (key.name === "up") {
|
|
180
|
+
cursor = cursor > 0 ? cursor - 1 : options.length - 1;
|
|
181
|
+
render();
|
|
182
|
+
} else if (key.name === "down") {
|
|
183
|
+
cursor = cursor < options.length - 1 ? cursor + 1 : 0;
|
|
184
|
+
render();
|
|
185
|
+
} else if (key.name === "return" || key.name === "space") {
|
|
186
|
+
cleanup();
|
|
187
|
+
process.stdout.write("\n");
|
|
188
|
+
resolve(options[cursor].value);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
96
192
|
process.stdin.on("keypress", onKey);
|
|
97
193
|
});
|
|
98
194
|
}
|
package/src/hooks/manager.ts
CHANGED
|
@@ -186,7 +186,7 @@ export async function installHooks(
|
|
|
186
186
|
);
|
|
187
187
|
}
|
|
188
188
|
writeScopedHooksConfig(configToWrite, scope, cwd);
|
|
189
|
-
console.log(`\nEnabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}`);
|
|
189
|
+
console.log(`\nEnabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}\n`);
|
|
190
190
|
if (removeCustomHooks) {
|
|
191
191
|
console.log("Custom hooks path cleared.");
|
|
192
192
|
} else if (configToWrite.customPoliciesPath) {
|
|
@@ -11,15 +11,12 @@
|
|
|
11
11
|
* and map: never → full-auto, on-request → default. Other values pass
|
|
12
12
|
* through. If the transcript can't be read, falls back to "default".
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* common case), (2) cache the resolved transcript path to disk keyed by
|
|
18
|
-
* sessionId so subsequent hooks in the same session skip the walk entirely.
|
|
14
|
+
* Transcript discovery (cache → today/yesterday → full tree scan) lives
|
|
15
|
+
* in `lib/codex-sessions.ts` and is shared with the dashboard's Codex
|
|
16
|
+
* session viewer.
|
|
19
17
|
*/
|
|
20
|
-
import { readFileSync
|
|
21
|
-
import {
|
|
22
|
-
import { homedir } from "node:os";
|
|
18
|
+
import { readFileSync } from "node:fs";
|
|
19
|
+
import { findCodexTranscript } from "../../lib/codex-sessions";
|
|
23
20
|
import type { IntegrationType } from "./types";
|
|
24
21
|
|
|
25
22
|
export function resolvePermissionMode(
|
|
@@ -38,91 +35,9 @@ export function resolvePermissionMode(
|
|
|
38
35
|
return "default";
|
|
39
36
|
}
|
|
40
37
|
|
|
41
|
-
const CACHE_PATH = join(homedir(), ".failproofai", "cache", "codex-session-paths.json");
|
|
42
|
-
|
|
43
|
-
function readCache(): Record<string, string> {
|
|
44
|
-
try {
|
|
45
|
-
if (!existsSync(CACHE_PATH)) return {};
|
|
46
|
-
return JSON.parse(readFileSync(CACHE_PATH, "utf-8")) as Record<string, string>;
|
|
47
|
-
} catch {
|
|
48
|
-
return {};
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function writeCacheEntry(sessionId: string, path: string): void {
|
|
53
|
-
try {
|
|
54
|
-
mkdirSync(dirname(CACHE_PATH), { recursive: true });
|
|
55
|
-
const cache = readCache();
|
|
56
|
-
cache[sessionId] = path;
|
|
57
|
-
writeFileSync(CACHE_PATH, JSON.stringify(cache), "utf-8");
|
|
58
|
-
} catch {
|
|
59
|
-
// Cache is best-effort — never block the hook on a write failure.
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function dirSearch(dir: string, sessionId: string): string | null {
|
|
64
|
-
try {
|
|
65
|
-
for (const f of readdirSync(dir, { withFileTypes: true })) {
|
|
66
|
-
if (f.isFile() && f.name.includes(sessionId) && f.name.endsWith(".jsonl")) {
|
|
67
|
-
return join(dir, f.name);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
} catch {
|
|
71
|
-
// dir doesn't exist or unreadable
|
|
72
|
-
}
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function findCodexTranscriptSync(sessionId: string): string | null {
|
|
77
|
-
// 1) Cache hit — fastest path, O(1).
|
|
78
|
-
const cache = readCache();
|
|
79
|
-
const cached = cache[sessionId];
|
|
80
|
-
if (cached && existsSync(cached)) return cached;
|
|
81
|
-
|
|
82
|
-
const root = join(homedir(), ".codex", "sessions");
|
|
83
|
-
|
|
84
|
-
// 2) Today + yesterday (covers the active-session common case).
|
|
85
|
-
const today = new Date();
|
|
86
|
-
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
|
|
87
|
-
const datedDirs = [today, yesterday].map((d) => {
|
|
88
|
-
const y = String(d.getUTCFullYear());
|
|
89
|
-
const m = String(d.getUTCMonth() + 1).padStart(2, "0");
|
|
90
|
-
const day = String(d.getUTCDate()).padStart(2, "0");
|
|
91
|
-
return join(root, y, m, day);
|
|
92
|
-
});
|
|
93
|
-
for (const dir of datedDirs) {
|
|
94
|
-
const hit = dirSearch(dir, sessionId);
|
|
95
|
-
if (hit) {
|
|
96
|
-
writeCacheEntry(sessionId, hit);
|
|
97
|
-
return hit;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// 3) Fallback — full tree scan (rare; older or out-of-clock-skew sessions).
|
|
102
|
-
try {
|
|
103
|
-
for (const y of readdirSync(root, { withFileTypes: true })) {
|
|
104
|
-
if (!y.isDirectory()) continue;
|
|
105
|
-
for (const m of readdirSync(join(root, y.name), { withFileTypes: true })) {
|
|
106
|
-
if (!m.isDirectory()) continue;
|
|
107
|
-
for (const d of readdirSync(join(root, y.name, m.name), { withFileTypes: true })) {
|
|
108
|
-
if (!d.isDirectory()) continue;
|
|
109
|
-
const hit = dirSearch(join(root, y.name, m.name, d.name), sessionId);
|
|
110
|
-
if (hit) {
|
|
111
|
-
writeCacheEntry(sessionId, hit);
|
|
112
|
-
return hit;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
} catch {
|
|
118
|
-
// Session may not have flushed yet; or path doesn't exist.
|
|
119
|
-
}
|
|
120
|
-
return null;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
38
|
function resolveCodexMode(sessionId: string): string | undefined {
|
|
124
39
|
try {
|
|
125
|
-
const path =
|
|
40
|
+
const path = findCodexTranscript(sessionId);
|
|
126
41
|
if (!path) return undefined;
|
|
127
42
|
for (const line of readFileSync(path, "utf-8").split("\n")) {
|
|
128
43
|
if (!line.includes("turn_context")) continue;
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[93695,(e,r,s)=>{r.exports=e.x("next/dist/shared/lib/no-fallback-error.external.js",()=>require("next/dist/shared/lib/no-fallback-error.external.js"))},14747,(e,r,s)=>{r.exports=e.x("path",()=>require("path"))},18622,(e,r,s)=>{r.exports=e.x("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js",()=>require("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js"))},56704,(e,r,s)=>{r.exports=e.x("next/dist/server/app-render/work-async-storage.external.js",()=>require("next/dist/server/app-render/work-async-storage.external.js"))},32319,(e,r,s)=>{r.exports=e.x("next/dist/server/app-render/work-unit-async-storage.external.js",()=>require("next/dist/server/app-render/work-unit-async-storage.external.js"))},24725,(e,r,s)=>{r.exports=e.x("next/dist/server/app-render/after-task-async-storage.external.js",()=>require("next/dist/server/app-render/after-task-async-storage.external.js"))},70406,(e,r,s)=>{r.exports=e.x("next/dist/compiled/@opentelemetry/api",()=>require("next/dist/compiled/@opentelemetry/api"))},46786,(e,r,s)=>{r.exports=e.x("os",()=>require("os"))},24868,(e,r,s)=>{r.exports=e.x("fs/promises",()=>require("fs/promises"))},22734,(e,r,s)=>{r.exports=e.x("fs",()=>require("fs"))}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=%5Bexternals%5D__080wern._.js.map
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[88947,(e,t,r)=>{t.exports=e.x("stream",()=>require("stream"))},9311,e=>{"use strict";var t=e.i(47909),r=e.i(74017),n=e.i(96250),a=e.i(59756),o=e.i(61916),i=e.i(74677),s=e.i(69741),l=e.i(16795),u=e.i(87718),d=e.i(95169),c=e.i(47587),p=e.i(66012),f=e.i(70101),h=e.i(26937),m=e.i(10372),R=e.i(93695);e.i(20232);var w=e.i(220),v=e.i(89171),g=e.i(22734),y=e.i(24868),E=e.i(88947);let C={info:0,warn:1,error:2};function x(e){return C[e]>=C.warn}function A(e){return`[failproofai${new Date().toISOString()}] ${e}`}function T(e,t){void 0!==t?console.error(A("ERROR"),e,t):console.error(A("ERROR"),e)}var N=e.i(14747),j=e.i(46786);function S(){let e=process.env.CLAUDE_PROJECTS_PATH;return e||(0,N.join)((0,j.homedir)(),".claude","projects")}function P(e,t,r){let n=new Map,a=new Map,o=r?.maxSize;return async(...r)=>{let i=JSON.stringify(r),s=n.get(i);if(s&&Date.now()<s.expiry)return s.data;let l=a.get(i);if(l)return l;let u=e(...r).then(e=>{if(a.delete(i),o&&n.size>=o){let e=n.keys().next().value;n.delete(e)}return n.set(i,{data:e,expiry:Date.now()+1e3*t}),e},e=>{throw a.delete(i),e});return a.set(i,u),u}}async function O(e,t){let r=Array(e.length),n=0;async function a(){for(;n<e.length;){let t=n++;try{r[t]={status:"fulfilled",value:await e[t]()}}catch(e){r[t]={status:"rejected",reason:e}}}}let o=Array.from({length:Math.min(t,e.length)},()=>a());return await Promise.all(o),r}function b(e){return new Intl.DateTimeFormat("en-US",{month:"short",day:"numeric",year:"numeric",hour:"numeric",minute:"2-digit",hour12:!0}).format(e)}let M=/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;async function D(e,t){try{return(await (0,y.stat)(e)).mtime}catch(e){var r;return r=`Failed to stat ${t}:`,x("warn")&&(void 0!==e?console.warn(A("WARN"),r,e):console.warn(A("WARN"),r)),new Date(0)}}async function I(e){try{if(!(await (0,y.stat)(e)).isDirectory())return null;return await (0,y.readdir)(e,{withFileTypes:!0})}catch{return null}}function _(e){let t=e.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i);return t?t[0]:void 0}async function q(e){try{let t=await I(e);if(!t)return[];let r=t.filter(e=>e.isFile()&&e.name.endsWith(".jsonl")&&_(e.name)),n=(await O(r.map(t=>async()=>{let r=(0,N.join)(e,t.name),n=await D(r,t.name);return{name:t.name,path:r,lastModified:n,lastModifiedFormatted:b(n),sessionId:_(t.name)}}),16)).filter(e=>"fulfilled"===e.status).map(e=>e.value);return n.sort((e,t)=>t.lastModified.getTime()-e.lastModified.getTime()),n}catch(e){return T("Error reading session files:",e),[]}}function $(e,t){return v.NextResponse.json({error:e},{status:t})}async function H(e,{params:t}){let r,{project:n,session:a}=await t;try{r=function(e,t){if(!M.test(t))throw RangeError("Invalid session ID");let r=function(e){if(!e)throw RangeError("Empty project name");if(/^[/\\]/.test(e))throw RangeError("Absolute project name");let t=(0,N.resolve)(S()),r=(0,N.resolve)((0,N.join)(t,e));if(!r.startsWith(t+N.sep))throw RangeError("Project path escapes root");return r}(e);return(0,N.join)(r,`${t}.jsonl`)}(n,a)}catch{return $("Invalid project or session",400)}!function(e,t,r){if(!x("info"))return;let n=[`user=${e}`,`action=${t}`];r&&n.push(r),console.log(A("ACTIVITY"),n.join(" "))}("anonymous","download-log",`project=${n} session=${a}`);try{await (0,y.stat)(r);let e=(0,g.createReadStream)(r),t=E.Readable.toWeb(e);return new v.NextResponse(t,{headers:{"Content-Type":"application/x-ndjson","Content-Disposition":`attachment; filename="${a}.jsonl"`}})}catch(e){if("ENOENT"===e.code||e instanceof RangeError)return $("Session log not found",404);return $("Failed to read session log",500)}}P(async function e(){try{let e=S(),t=await I(e);if(!t)return[];let r=(await O(t.filter(e=>e.isDirectory()).map(t=>async()=>{let r=(0,N.join)(e,t.name),n=await D(r,t.name);return{name:t.name,path:r,isDirectory:!0,lastModified:n,lastModifiedFormatted:b(n)}}),16)).filter(e=>"fulfilled"===e.status).map(e=>e.value);return r.sort((e,t)=>t.lastModified.getTime()-e.lastModified.getTime()),r}catch(e){return T("Error reading project folders:",e),[]}},30),P(e=>q(e),30),e.s(["GET",0,H],32525);var U=e.i(32525);let F=new t.AppRouteRouteModule({definition:{kind:r.RouteKind.APP_ROUTE,page:"/api/download/[project]/[session]/route",pathname:"/api/download/[project]/[session]",filename:"route",bundlePath:""},distDir:".next",relativeProjectDir:"",resolvedPagePath:"[project]/app/api/download/[project]/[session]/route.ts",nextConfigOutput:"standalone",userland:U,...{}}),{workAsyncStorage:k,workUnitAsyncStorage:K,serverHooks:W}=F;async function B(e,t,n){n.requestMeta&&(0,a.setRequestMeta)(e,n.requestMeta),F.isDev&&(0,a.addRequestMeta)(e,"devRequestTimingInternalsEnd",process.hrtime.bigint());let v="/api/download/[project]/[session]/route";v=v.replace(/\/index$/,"")||"/";let g=await F.prepare(e,t,{srcPage:v,multiZoneDraftMode:!1});if(!g)return t.statusCode=400,t.end("Bad Request"),null==n.waitUntil||n.waitUntil.call(n,Promise.resolve()),null;let{buildId:y,params:E,nextConfig:C,parsedUrl:x,isDraftMode:A,prerenderManifest:T,routerServerContext:N,isOnDemandRevalidate:j,revalidateOnlyGenerated:S,resolvedPathname:P,clientReferenceManifest:O,serverActionsManifest:b}=g,M=(0,s.normalizeAppPath)(v),D=!!(T.dynamicRoutes[M]||T.routes[P]),I=async()=>((null==N?void 0:N.render404)?await N.render404(e,t,x,!1):t.end("This page could not be found"),null);if(D&&!A){let e=!!T.routes[P],t=T.dynamicRoutes[M];if(t&&!1===t.fallback&&!e){if(C.adapterPath)return await I();throw new R.NoFallbackError}}let _=null;!D||F.isDev||A||(_="/index"===(_=P)?"/":_);let q=!0===F.isDev||!D,$=D&&!q;b&&O&&(0,i.setManifestsSingleton)({page:v,clientReferenceManifest:O,serverActionsManifest:b});let H=e.method||"GET",U=(0,o.getTracer)(),k=U.getActiveScopeSpan(),K=!!(null==N?void 0:N.isWrappedByNextServer),W=!!(0,a.getRequestMeta)(e,"minimalMode"),B=(0,a.getRequestMeta)(e,"incrementalCache")||await F.getIncrementalCache(e,C,T,W);null==B||B.resetRequestCache(),globalThis.__incrementalCache=B;let L={params:E,previewProps:T.preview,renderOpts:{experimental:{authInterrupts:!!C.experimental.authInterrupts},cacheComponents:!!C.cacheComponents,supportsDynamicResponse:q,incrementalCache:B,cacheLifeProfiles:C.cacheLife,waitUntil:n.waitUntil,onClose:e=>{t.on("close",e)},onAfterTaskError:void 0,onInstrumentationRequestError:(t,r,n,a)=>F.onRequestError(e,t,n,a,N)},sharedContext:{buildId:y}},G=new l.NodeNextRequest(e),V=new l.NodeNextResponse(t),z=u.NextRequestAdapter.fromNodeNextRequest(G,(0,u.signalFromNodeResponse)(t));try{let a,i=async e=>F.handle(z,L).finally(()=>{if(!e)return;e.setAttributes({"http.status_code":t.statusCode,"next.rsc":!1});let r=U.getRootSpanAttributes();if(!r)return;if(r.get("next.span_type")!==d.BaseServerSpan.handleRequest)return void console.warn(`Unexpected root span type '${r.get("next.span_type")}'. Please report this Next.js issue https://github.com/vercel/next.js`);let n=r.get("next.route");if(n){let t=`${H} ${n}`;e.setAttributes({"next.route":n,"http.route":n,"next.span_name":t}),e.updateName(t),a&&a!==e&&(a.setAttribute("http.route",n),a.updateName(t))}else e.updateName(`${H} ${v}`)}),s=async a=>{var o,s;let l=async({previousCacheEntry:r})=>{try{if(!W&&j&&S&&!r)return t.statusCode=404,t.setHeader("x-nextjs-cache","REVALIDATED"),t.end("This page could not be found"),null;let o=await i(a);e.fetchMetrics=L.renderOpts.fetchMetrics;let s=L.renderOpts.pendingWaitUntil;s&&n.waitUntil&&(n.waitUntil(s),s=void 0);let l=L.renderOpts.collectedTags;if(!D)return await (0,p.sendResponse)(G,V,o,L.renderOpts.pendingWaitUntil),null;{let e=await o.blob(),t=(0,f.toNodeOutgoingHttpHeaders)(o.headers);l&&(t[m.NEXT_CACHE_TAGS_HEADER]=l),!t["content-type"]&&e.type&&(t["content-type"]=e.type);let r=void 0!==L.renderOpts.collectedRevalidate&&!(L.renderOpts.collectedRevalidate>=m.INFINITE_CACHE)&&L.renderOpts.collectedRevalidate,n=void 0===L.renderOpts.collectedExpire||L.renderOpts.collectedExpire>=m.INFINITE_CACHE?void 0:L.renderOpts.collectedExpire;return{value:{kind:w.CachedRouteKind.APP_ROUTE,status:o.status,body:Buffer.from(await e.arrayBuffer()),headers:t},cacheControl:{revalidate:r,expire:n}}}}catch(t){throw(null==r?void 0:r.isStale)&&await F.onRequestError(e,t,{routerKind:"App Router",routePath:v,routeType:"route",revalidateReason:(0,c.getRevalidateReason)({isStaticGeneration:$,isOnDemandRevalidate:j})},!1,N),t}},u=await F.handleResponse({req:e,nextConfig:C,cacheKey:_,routeKind:r.RouteKind.APP_ROUTE,isFallback:!1,prerenderManifest:T,isRoutePPREnabled:!1,isOnDemandRevalidate:j,revalidateOnlyGenerated:S,responseGenerator:l,waitUntil:n.waitUntil,isMinimalMode:W});if(!D)return null;if((null==u||null==(o=u.value)?void 0:o.kind)!==w.CachedRouteKind.APP_ROUTE)throw Object.defineProperty(Error(`Invariant: app-route received invalid cache entry ${null==u||null==(s=u.value)?void 0:s.kind}`),"__NEXT_ERROR_CODE",{value:"E701",enumerable:!1,configurable:!0});W||t.setHeader("x-nextjs-cache",j?"REVALIDATED":u.isMiss?"MISS":u.isStale?"STALE":"HIT"),A&&t.setHeader("Cache-Control","private, no-cache, no-store, max-age=0, must-revalidate");let d=(0,f.fromNodeOutgoingHttpHeaders)(u.value.headers);return W&&D||d.delete(m.NEXT_CACHE_TAGS_HEADER),!u.cacheControl||t.getHeader("Cache-Control")||d.get("Cache-Control")||d.set("Cache-Control",(0,h.getCacheControlHeader)(u.cacheControl)),await (0,p.sendResponse)(G,V,new Response(u.value.body,{headers:d,status:u.value.status||200})),null};K&&k?await s(k):(a=U.getActiveScopeSpan(),await U.withPropagatedContext(e.headers,()=>U.trace(d.BaseServerSpan.handleRequest,{spanName:`${H} ${v}`,kind:o.SpanKind.SERVER,attributes:{"http.method":H,"http.target":e.url}},s),void 0,!K))}catch(t){if(t instanceof R.NoFallbackError||await F.onRequestError(e,t,{routerKind:"App Router",routePath:M,routeType:"route",revalidateReason:(0,c.getRevalidateReason)({isStaticGeneration:$,isOnDemandRevalidate:j})},!1,N),D)throw t;return await (0,p.sendResponse)(G,V,new Response(null,{status:500})),null}}e.s(["handler",0,B,"patchFetch",0,function(){return(0,n.patchFetch)({workAsyncStorage:k,workUnitAsyncStorage:K})},"routeModule",0,F,"serverHooks",0,W,"workAsyncStorage",0,k,"workUnitAsyncStorage",0,K],9311)}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=%5Broot-of-the-server%5D__0b57.gk._.js.map
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[14747,(a,b,c)=>{b.exports=a.x("path",()=>require("path"))},46786,(a,b,c)=>{b.exports=a.x("os",()=>require("os"))},27950,45481,33540,24451,a=>{"use strict";a.s(["FILTER_PRESETS",0,[{value:"all",label:"All"},{value:"last-hour",label:"Last Hour"},{value:"today",label:"Today"},{value:"last-7-days",label:"Last 7 Days"},{value:"last-30-days",label:"Last 30 Days"}],"ITEMS_PER_PAGE",0,25,"filterByDate",0,function(a,b,c){if("all"===b)return a;if("custom"===b){let b,d,e=c.from?((b=new Date(c.from)).setHours(0,0,0,0),b):null,f=c.to?((d=new Date(c.to)).setHours(23,59,59,999),d):null;return a.filter(a=>(!e||!(a.lastModified<e))&&(!f||!(a.lastModified>f)))}let d=function(a){let b=new Date;switch(a){case"last-hour":return new Date(b.getTime()-36e5);case"today":return new Date(b.getFullYear(),b.getMonth(),b.getDate());case"last-7-days":return new Date(b.getTime()-6048e5);case"last-30-days":return new Date(b.getTime()-2592e6);default:return null}}(b);return d?a.filter(a=>a.lastModified>=d):a},"rehydrateDates",0,function(a){return a.map(a=>({...a,lastModified:a.lastModified instanceof Date?a.lastModified:new Date(a.lastModified)}))}],27950);var b=a.i(72131);a.s(["useFilterState",0,function(a=[],c){let[d,e]=(0,b.useState)(c?.filterPreset??"all"),[f,g]=(0,b.useState)(c?.dateRange??{from:null,to:null}),[h,i]=(0,b.useState)(c?.currentPage??1),j=`${d}|${JSON.stringify(f)}|${JSON.stringify(a)}`,[k,l]=(0,b.useState)(j);j!==k&&(l(j),i(1));let m=(0,b.useCallback)(a=>{e(a),"custom"!==a&&g({from:null,to:null})},[]);return{filterPreset:d,dateRange:f,currentPage:h,setCurrentPage:i,handlePresetChange:m,handleDateRangeChange:(0,b.useCallback)((a,b)=>{e("custom"),g(c=>({...c,[a]:b?new Date(b):null}))},[]),clearFilters:(0,b.useCallback)(()=>{e("all"),g({from:null,to:null})},[])}}],45481);var c=a.i(64831);let d=(0,c.default)("search",[["path",{d:"m21 21-4.34-4.34",key:"14j7rj"}],["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}]]);a.s(["Search",0,d],33540);var e=a.i(87924);let f=(0,c.default)("calendar",[["path",{d:"M8 2v4",key:"1cmpym"}],["path",{d:"M16 2v4",key:"4m81vk"}],["rect",{width:"18",height:"18",x:"3",y:"4",rx:"2",key:"1hopcy"}],["path",{d:"M3 10h18",key:"8toen8"}]]);a.s(["default",0,function({id:a,value:b,stringValue:c,onChange:d,"aria-label":g}){let h=null!=c?c:b?b.toISOString().split("T")[0]:"";return(0,e.jsxs)("div",{className:"relative group",children:[(0,e.jsx)("input",{type:"date",id:a,value:h,onChange:a=>d(a.target.value),"aria-label":g,className:"date-input px-3 py-2 pr-8 text-sm bg-input border border-border rounded-md text-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent transition-all hover:border-primary/50 w-[200px]"}),(0,e.jsx)("label",{htmlFor:a,className:"absolute right-2.5 top-1/2 -translate-y-1/2 cursor-pointer pointer-events-auto z-10 p-1 rounded hover:bg-muted/50 transition-colors",onClick:b=>{b.preventDefault();let c=document.getElementById(a);c&&"function"==typeof c.showPicker?c.showPicker():c?.click()},children:(0,e.jsx)(f,{className:"w-4 h-4 text-primary group-hover:text-primary/80 transition-colors"})})]})}],24451)},90619,95947,a=>{"use strict";var b=a.i(50944),c=a.i(72131);function d(a){let b=a.getFullYear(),c=String(a.getMonth()+1).padStart(2,"0"),d=String(a.getDate()).padStart(2,"0");return`${b}-${c}-${d}`}function e(a){var b;if(!a||(b=a,!/^\d{4}-\d{2}-\d{2}$/.test(b)||isNaN(Date.parse(b))))return null;let[c,d,e]=a.split("-").map(Number);return new Date(c,d-1,e,12,0,0)}a.s(["useUrlParams",0,function(){let a=(0,b.useSearchParams)(),d=(0,b.useRouter)(),e=(0,b.usePathname)(),f=(0,c.useRef)(null),g=(0,c.useCallback)(b=>a.get(b),[a]),h=(0,c.useCallback)(()=>new URLSearchParams(a.toString()),[a]),i=(0,c.useCallback)(b=>{f.current&&clearTimeout(f.current),f.current=setTimeout(()=>{let c=new URLSearchParams(a.toString());for(let[a,d]of Object.entries(b))void 0===d||""===d?c.delete(a):c.set(a,d);let f=c.toString(),g=f?`${e}?${f}`:e;d.replace(g,{scroll:!1})},150)},[a,e,d]);return(0,c.useEffect)(()=>()=>{f.current&&clearTimeout(f.current)},[]),{get:g,getAll:h,setAll:i}}],90619),a.s(["dateRangeToParams",0,function(a){let b={};return a.from&&(b.from=d(a.from)),a.to&&(b.to=d(a.to)),b},"keywordsToParam",0,function(a){if(0!==a.length)return a.map(a=>encodeURIComponent(a)).join(",")},"pageToParam",0,function(a){return a<=1?void 0:String(a)},"paramToKeywords",0,function(a){return a?a.split(",").map(a=>decodeURIComponent(a.trim())).filter(a=>a.length>0):[]},"paramToPage",0,function(a){if(!a)return 1;let b=parseInt(a,10);return Number.isFinite(b)&&b>=1?b:1},"paramToPreset",0,function(a){return a&&["all","last-hour","today","last-7-days","last-30-days","custom"].includes(a)?a:"all"},"paramsToDateRange",0,function(a,b){return{from:e(a),to:e(b)}},"presetToParam",0,function(a){return"all"===a?void 0:a}],95947)},65967,a=>{"use strict";var b=a.i(87924),c=a.i(64831);let d=(0,c.default)("chevron-left",[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]]),e=(0,c.default)("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);a.s(["default",0,function({currentPage:a,totalPages:c,onPageChange:f}){if(c<=1)return null;let g=a=>`px-3 py-2 text-sm rounded-md transition-colors flex items-center gap-1 bg-muted text-muted-foreground ${a?"opacity-50 cursor-not-allowed":"hover:bg-muted/80"}`;return(0,b.jsxs)("div",{className:"flex items-center justify-center gap-2 py-4",children:[(0,b.jsxs)("button",{onClick:()=>f(a-1),disabled:1===a,className:g(1===a),"aria-label":"Previous page",children:[(0,b.jsx)(d,{className:"w-4 h-4"}),(0,b.jsx)("span",{className:"hidden sm:inline",children:"Previous"})]}),(0,b.jsx)("div",{className:"flex items-center gap-1",children:(()=>{if(c<=7)return Array.from({length:c},(a,b)=>b+1);let b=[1],d=Math.max(2,a-1),e=Math.min(c-1,a+1);a<=3&&(e=Math.min(5,c-1)),a>=c-2&&(d=Math.max(2,c-4)),d>2&&b.push("ellipsis-start");for(let a=d;a<=e;a++)b.push(a);return e<c-1&&b.push("ellipsis-end"),b.push(c),b})().map(c=>"string"==typeof c?(0,b.jsx)("span",{className:"px-2 text-muted-foreground",children:"..."},c):(0,b.jsx)("button",{onClick:()=>f(c),className:`min-w-[2.5rem] px-3 py-2 text-sm rounded-md transition-colors ${a===c?"bg-primary text-primary-foreground":"bg-muted text-muted-foreground hover:bg-muted/80"}`,"aria-label":`Page ${c}`,"aria-current":a===c?"page":void 0,children:c},c))}),(0,b.jsxs)("button",{onClick:()=>f(a+1),disabled:a===c,className:g(a===c),"aria-label":"Next page",children:[(0,b.jsx)("span",{className:"hidden sm:inline",children:"Next"}),(0,b.jsx)(e,{className:"w-4 h-4"})]})]})}],65967)},74215,a=>{"use strict";let b=(0,a.i(64831).default)("x",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);a.s(["X",0,b],74215)},69349,a=>{"use strict";var b=a.i(87924),c=a.i(72131);a.i(46786),a.i(14747);var d=a.i(97895),e=a.i(27950),f=a.i(45481),g=a.i(90619),h=a.i(95947);let i=(0,a.i(64831).default)("folder",[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",key:"1kt360"}]]);var j=a.i(33540),k=a.i(74215),l=a.i(38246),m=a.i(65967),n=a.i(24451);function o({date:a,formatted:c}){return(0,b.jsx)("span",{children:c||(0,d.formatDate)(a)})}a.s(["default",0,function({folders:a}){let d=(0,g.useUrlParams)(),p=(0,c.useRef)(!1),[q,r]=(0,c.useState)(()=>(0,h.paramToKeywords)(d.get("q"))),[s,t]=(0,c.useState)(""),{filterPreset:u,dateRange:v,currentPage:w,setCurrentPage:x,handlePresetChange:y,handleDateRangeChange:z,clearFilters:A}=(0,f.useFilterState)(q,{filterPreset:(0,h.paramToPreset)(d.get("preset")),dateRange:(0,h.paramsToDateRange)(d.get("from"),d.get("to")),currentPage:(0,h.paramToPage)(d.get("page"))});(0,c.useEffect)(()=>{if(!p.current){p.current=!0;return}d.setAll({preset:(0,h.presetToParam)(u),...(0,h.dateRangeToParams)(v),q:(0,h.keywordsToParam)(q),page:(0,h.pageToParam)(w)})},[u,v,q,w]);let B=a=>{let b=a.trim();b&&!q.includes(b)&&(r([...q,b]),t(""))},C=()=>{r([]),t("")},D=(0,c.useMemo)(()=>(0,e.rehydrateDates)(a),[a]),E=(0,c.useMemo)(()=>{let a=(0,e.filterByDate)(D,u,v);return q.length>0&&(a=a.filter(a=>{let b=a.name.toLowerCase();return q.every(a=>{let c=a.trim().toLowerCase().replace(/\//g,"-");return 0===c.length||b.includes(c)})})),a.sort((a,b)=>b.lastModified.getTime()-a.lastModified.getTime())},[D,u,v,q]),F=Math.max(1,Math.ceil(E.length/e.ITEMS_PER_PAGE));(0,c.useEffect)(()=>{w>F&&x(F)},[w,F,x]);let G=(w-1)*e.ITEMS_PER_PAGE,H=Math.min(G+e.ITEMS_PER_PAGE,E.length),I=E.slice(G,H);return(0,b.jsxs)("div",{className:"space-y-4",children:[(0,b.jsx)("div",{className:"bg-card border border-border rounded-lg p-4",children:(0,b.jsxs)("div",{className:"flex flex-col gap-4",children:[(0,b.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[(0,b.jsx)("span",{className:"text-sm font-medium text-foreground",children:"Filter by:"}),e.FILTER_PRESETS.map(a=>(0,b.jsx)("button",{onClick:()=>y(a.value),className:`px-3 py-1.5 text-sm rounded-md transition-colors ${u===a.value?"bg-primary text-primary-foreground":"bg-muted text-muted-foreground hover:bg-muted/80"}`,children:a.label},a.value))]}),(0,b.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,b.jsxs)("div",{className:"flex items-center gap-2",children:[(0,b.jsx)(j.Search,{className:"w-4 h-4 text-muted-foreground"}),(0,b.jsx)("span",{className:"text-sm font-medium text-foreground",children:"Search Keywords:"})]}),(0,b.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[(0,b.jsxs)("div",{className:"flex items-center gap-2",children:[(0,b.jsx)("input",{type:"text",value:s,onChange:a=>t(a.target.value),onKeyDown:a=>{"Enter"===a.key&&(a.preventDefault(),B(s))},placeholder:"Enter keyword and press Enter",className:"px-3 py-2 text-sm bg-input border border-border rounded-md text-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent transition-all hover:border-primary/50 w-[250px]","aria-label":"Add keyword"}),(0,b.jsx)("button",{onClick:()=>B(s),className:"px-3 py-2 text-sm bg-muted text-muted-foreground hover:bg-muted/80 rounded-md transition-colors","aria-label":"Add keyword",children:"Add"})]}),q.length>0&&(0,b.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[q.map((a,c)=>(0,b.jsxs)("div",{className:"flex items-center gap-1.5 px-3 py-1.5 bg-muted text-muted-foreground rounded-md text-sm",children:[(0,b.jsx)("span",{children:a}),(0,b.jsx)("button",{onClick:()=>{r(q.filter((a,b)=>b!==c))},className:"hover:text-foreground transition-colors p-0.5 rounded hover:bg-muted/80","aria-label":`Remove keyword ${a}`,children:(0,b.jsx)(k.X,{className:"w-3.5 h-3.5"})})]},c)),(0,b.jsx)("button",{onClick:C,className:"px-2 py-1.5 text-xs bg-muted text-muted-foreground hover:bg-muted/80 rounded-md transition-colors","aria-label":"Clear all keywords",children:"Clear all"})]})]})]}),(0,b.jsxs)("div",{className:"flex flex-wrap items-center gap-4",children:[(0,b.jsx)("span",{className:"text-sm font-medium text-foreground",children:"Custom Range:"}),(0,b.jsxs)("div",{className:"flex items-center gap-2",children:[(0,b.jsx)(n.default,{id:"date-from",value:v.from,onChange:a=>z("from",a),"aria-label":"Filter from date"}),(0,b.jsx)("span",{className:"text-muted-foreground",children:"to"}),(0,b.jsx)(n.default,{id:"date-to",value:v.to,onChange:a=>z("to",a),"aria-label":"Filter to date"})]}),("all"!==u||null!==v.from||null!==v.to||q.length>0)&&(0,b.jsx)("button",{onClick:()=>{A(),C()},className:"px-3 py-2 text-sm bg-muted text-muted-foreground hover:bg-muted/80 rounded-md transition-colors",children:"Clear"})]}),(0,b.jsx)("div",{className:"text-sm text-muted-foreground",children:0===E.length?(0,b.jsx)(b.Fragment,{children:"No projects found"}):(0,b.jsxs)(b.Fragment,{children:["Showing ",G+1,"-",H," of ",E.length," projects",E.length!==D.length&&(0,b.jsxs)("span",{className:"ml-1",children:["(filtered from ",D.length," total)"]}),q.length>0&&(0,b.jsxs)("span",{className:"ml-1",children:["with ",q.length," keyword",1!==q.length?"s":""]})]})})]})}),(0,b.jsxs)("div",{className:"bg-card border border-border rounded-lg overflow-hidden",children:[(0,b.jsx)("div",{className:"overflow-x-auto",children:(0,b.jsxs)("table",{className:"w-full",children:[(0,b.jsx)("thead",{children:(0,b.jsxs)("tr",{className:"border-b border-border bg-muted/50",children:[(0,b.jsx)("th",{scope:"col",className:"px-4 py-3 text-left text-sm font-semibold text-foreground w-12",children:(0,b.jsx)("span",{className:"sr-only",children:"Icon"})}),(0,b.jsx)("th",{scope:"col",className:"px-4 py-3 text-left text-sm font-semibold text-foreground max-w-md",children:"Agent Root"}),(0,b.jsx)("th",{scope:"col",className:"px-4 py-3 text-left text-sm font-semibold text-foreground hidden md:table-cell",children:"Path"}),(0,b.jsx)("th",{scope:"col",className:"px-4 py-3 text-left text-sm font-semibold text-foreground",children:"Last Modified"})]})}),(0,b.jsx)("tbody",{children:0===I.length?(0,b.jsx)("tr",{children:(0,b.jsx)("td",{colSpan:4,className:"px-4 py-8 text-center text-muted-foreground",children:"No projects found matching the selected filter."})}):I.map(a=>{var c;return(0,b.jsxs)("tr",{className:"border-b border-border hover:bg-muted/50 transition-colors",children:[(0,b.jsx)("td",{className:"px-4 py-3",children:(0,b.jsx)(i,{className:"w-5 h-5 text-primary"})}),(0,b.jsx)("td",{className:"px-4 py-3 max-w-md",children:(0,b.jsx)(l.default,{href:`/project/${encodeURIComponent(a.name)}`,className:"font-semibold text-foreground hover:text-primary transition-colors break-words break-all inline-block max-w-full",children:(c=a.name,/^[A-Za-z]--/.test(c)?c[0]+":/"+c.slice(3).replace(/-/g,"/"):c.replace(/-/g,"/"))})}),(0,b.jsx)("td",{className:"px-4 py-3 text-sm text-muted-foreground hidden md:table-cell truncate max-w-md",children:a.path}),(0,b.jsx)("td",{className:"px-4 py-3 text-sm text-muted-foreground",children:(0,b.jsx)(o,{date:a.lastModified,formatted:a.lastModifiedFormatted})})]},a.name)})})]})}),E.length>0&&(0,b.jsx)(m.default,{currentPage:w,totalPages:F,onPageChange:x})]})]})}],69349)}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=%5Broot-of-the-server%5D__03rd.z8._.js.map
|