wiki-viewer 1.4.3 → 1.5.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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +5 -0
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/routes-manifest.json +34 -0
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/agent/activity/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/events/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/files/[...path]/route.js +8 -7
- package/.next/standalone/.next/server/app/api/agent/files/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js +17 -0
- package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js +15 -0
- package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/agent/fs/move/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/agent/fs/move/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/api/agent/fs/move/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/agent/fs/move/route.js +15 -0
- package/.next/standalone/.next/server/app/api/agent/fs/move/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/agent/fs/move/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/agent/fs/move/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/agent/fs/search/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/agent/fs/search/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/api/agent/fs/search/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/agent/fs/search/route.js +15 -0
- package/.next/standalone/.next/server/app/api/agent/fs/search/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/agent/fs/search/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/agent/fs/search/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/agent/internal/span/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/settings/route.js +1 -1
- package/.next/standalone/.next/server/app/api/agent/settings/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/sidecar/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/app-proxy/[...path]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/app-proxy/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/assets/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/browse/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/clear-root/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/reveal/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/root-status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/set-root/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/upload/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/app/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/content/route.js +2 -2
- package/.next/standalone/.next/server/app/api/wiki/content/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/file/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/folder/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/move/route.js +3 -3
- package/.next/standalone/.next/server/app/api/wiki/move/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/new-file/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/page/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/presence/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/wiki/presence/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/api/wiki/presence/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/wiki/presence/route.js +14 -0
- package/.next/standalone/.next/server/app/api/wiki/presence/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/wiki/presence/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/wiki/presence/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/wiki/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/slugs/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/upload/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/watch/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +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-paths-manifest.json +5 -0
- package/.next/standalone/.next/server/chunks/0p91_next_dist_esm_build_templates_app-route_0d2jbo5.js +3 -0
- package/.next/standalone/.next/server/chunks/0p91_next_dist_esm_build_templates_app-route_0ytral2.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-ao6a_._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0269d2e._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__05f6x0i._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__06-dtd0._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__06sek6_._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__09s9qkd._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d75a5q._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d_da6q._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e7si9p._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0hhhvm7._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0kk26wc._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0kur4ro._.js +15 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0p12-6~._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qc2~6l._.js +15 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0s~i040._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0w47wil._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xq124z._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0l..bxz._.js → [root-of-the-server]__0xz-_0v._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__10w2q~o._.js +1 -1
- package/.next/standalone/.next/server/chunks/_079xuln._.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_agent_fs_file_[___path]_route_actions_0gb11t~.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_agent_fs_ls_[[___path]]_route_actions_0wfg4nl.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_agent_fs_move_route_actions_0z34mqq.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_agent_fs_search_route_actions_0ut_ug..js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_wiki_presence_route_actions_0x9d7f4.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{0~w0_katex_dist_katex_mjs_055boxx._.js → 0~w0_katex_dist_katex_mjs_00650~e._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/12y~_mermaid_dist_chunks_mermaid_core_diagram-OG6HWLK6_mjs_0t5sus6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/12y~_mermaid_dist_chunks_mermaid_core_sankeyDiagram-5OEKKPKP_mjs_11ifrnu._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__134_q7l._.js → [root-of-the-server]__12ahxbl._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_0pqaawz._.js +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0rc1vh5._.js → node_modules__pnpm_02gx8km._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0t4unpg._.js → node_modules__pnpm_02mew~s._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_12-ni1n._.js → node_modules__pnpm_03ij5.g._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_12~a4c_._.js → node_modules__pnpm_03v21p-._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0f~_mm3._.js → node_modules__pnpm_05qsply._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0alwr5e._.js → node_modules__pnpm_06pgebo._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_02zxe7o._.js → node_modules__pnpm_08ibh0w._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0hr027i._.js → node_modules__pnpm_099cv.b._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0znncy9._.js → node_modules__pnpm_0cwpt9p._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0-pisfi._.js → node_modules__pnpm_0f9fpy5._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0zhbnni._.js → node_modules__pnpm_0f_uzgy._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0-w-d-.._.js → node_modules__pnpm_0fixtms._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_03z0vkh._.js → node_modules__pnpm_0gofu~q._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_10i6lh6._.js → node_modules__pnpm_0iojvye._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0-hfev1._.js → node_modules__pnpm_0li2vzs._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_13kfy_z._.js → node_modules__pnpm_0m5-azh._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_071f6vl._.js → node_modules__pnpm_0n1kfcp._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_07uh2kc._.js → node_modules__pnpm_0rebktf._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0okw1r.._.js → node_modules__pnpm_0tkiej3._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0xuz.8f._.js → node_modules__pnpm_0v_nwsz._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0zb6573._.js → node_modules__pnpm_0wffzo0._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0d7jxsf._.js → node_modules__pnpm_0wouz19._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0egenev._.js → node_modules__pnpm_0xxwmv2._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0j9bq9s._.js → node_modules__pnpm_0zzzsrj._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0irsd.b._.js → node_modules__pnpm_0~.6dgb._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0erwv_m._.js → node_modules__pnpm_0~2~c30._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0y9k6h~._.js → node_modules__pnpm_10413dq._.js} +2 -2
- package/.next/standalone/.next/server/functions-config-manifest.json +5 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware-manifest.json +5 -5
- package/.next/standalone/.next/server/pages/404.html +1 -1
- 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 +1 -1
- package/.next/standalone/.next/static/chunks/{11s9zwr7edrs7.js → 17~r3.8awh3qg.js} +12 -12
- package/.next/standalone/README.md +79 -16
- package/.next/standalone/agents/bootstrap-prompt.md +12 -1
- package/.next/standalone/agents/wiki-viewer-skill/SKILL.md +199 -6
- package/.next/standalone/docs/file-vs-collab-authority.md +124 -0
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/packages/wiki-viewer-mcp/README.md +194 -0
- package/.next/standalone/packages/wiki-viewer-mcp/dist/http-client.js +220 -0
- package/.next/standalone/packages/wiki-viewer-mcp/dist/index.js +472 -0
- package/.next/standalone/packages/wiki-viewer-mcp/dist/register.js +94 -0
- package/.next/standalone/packages/wiki-viewer-mcp/dist/state-cache.js +26 -0
- package/.next/standalone/packages/wiki-viewer-mcp/package-lock.json +1716 -0
- package/.next/standalone/packages/wiki-viewer-mcp/package.json +41 -0
- package/.next/standalone/packages/wiki-viewer-mcp/src/http-client.ts +344 -0
- package/.next/standalone/packages/wiki-viewer-mcp/src/index.ts +575 -0
- package/.next/standalone/packages/wiki-viewer-mcp/src/register.ts +144 -0
- package/.next/standalone/packages/wiki-viewer-mcp/src/state-cache.ts +45 -0
- package/.next/standalone/packages/wiki-viewer-mcp/tsconfig.json +19 -0
- package/.next/standalone/pnpm-lock.yaml +9 -9
- package/.next/standalone/src/app/api/agent/files/[...path]/route.ts +11 -1
- package/.next/standalone/src/app/api/agent/fs/file/[...path]/route.ts +344 -0
- package/.next/standalone/src/app/api/agent/fs/ls/[[...path]]/route.ts +147 -0
- package/.next/standalone/src/app/api/agent/fs/move/route.ts +125 -0
- package/.next/standalone/src/app/api/agent/fs/search/route.ts +236 -0
- package/.next/standalone/src/app/api/agent/register/route.ts +3 -3
- package/.next/standalone/src/app/api/agents/install/route.ts +44 -2
- package/.next/standalone/src/app/api/system/auth-settings/route.ts +1 -0
- package/.next/standalone/src/app/api/wiki/move/route.ts +10 -1
- package/.next/standalone/src/app/api/wiki/presence/route.ts +55 -0
- package/.next/standalone/src/components/ai-panel/ai-panel.tsx +98 -103
- package/.next/standalone/src/components/ai-panel/token-section.tsx +8 -7
- package/.next/standalone/src/components/auth-settings-sheet.tsx +81 -22
- package/.next/standalone/src/components/editor/editor.tsx +38 -0
- package/.next/standalone/src/lib/proof/audit.ts +79 -0
- package/.next/standalone/src/lib/proof/auth.ts +2 -2
- package/.next/standalone/src/lib/proof/collab-state.ts +79 -0
- package/.next/standalone/src/lib/proof/lease.ts +104 -0
- package/.next/standalone/src/lib/proof/ops-applier.ts +74 -25
- package/.next/standalone/src/lib/proof/raw-fs.ts +198 -0
- package/.next/standalone/src/lib/proof/registry.ts +1 -1
- package/.next/standalone/src/lib/proof/sidecar.ts +38 -1
- package/.next/standalone/src/lib/proof/types.ts +13 -1
- package/.next/standalone/src/tests/proof/agent-fs.test.ts +824 -0
- package/.next/standalone/src/tests/proof/agents-install.test.ts +53 -0
- package/.next/standalone/src/tests/proof/collab-state.test.ts +499 -0
- package/.next/standalone/src/tests/proof/reconcile-sidecar.test.ts +195 -0
- package/.next/standalone/src/tests/proof/sidecar-lifecycle.test.ts +177 -0
- package/.next/standalone/tsconfig.tsbuildinfo +1 -1
- package/README.md +79 -16
- package/package.json +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__09~~-nd._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0w87kiz._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__12ssbhv._.js +0 -3
- package/.next/standalone/.next/server/chunks/node_modules__pnpm_0g0fhfk._.js +0 -3
- /package/.next/standalone/.next/static/{kzOuSAQArtvD7uiAJ6ZLh → 3G_3qm2a9nOX7oi5k0mPV}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{kzOuSAQArtvD7uiAJ6ZLh → 3G_3qm2a9nOX7oi5k0mPV}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{kzOuSAQArtvD7uiAJ6ZLh → 3G_3qm2a9nOX7oi5k0mPV}/_ssgManifest.js +0 -0
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useEffect, useCallback, useRef, useState } from "react";
|
|
4
|
-
import { X, Copy, Check, Bot, Wifi,
|
|
4
|
+
import { X, Copy, Check, Bot, Wifi, ExternalLink } from "lucide-react";
|
|
5
5
|
import { Button } from "@/components/ui/button";
|
|
6
6
|
import { useAIPanelStore } from "@/stores/ai-panel-store";
|
|
7
7
|
import { TokenSection } from "./token-section";
|
|
8
8
|
import { ActivityRow } from "./activity-row";
|
|
9
|
-
import { authClient } from "@/lib/auth/client";
|
|
10
9
|
const SKILL_CLI = "npx skills add anh-chu/wiki-viewer/agents/wiki-viewer-skill";
|
|
11
10
|
|
|
12
11
|
function useCopyCurl(currentPath: string | null) {
|
|
@@ -55,7 +54,6 @@ export function AIPanel({ currentPath }: { currentPath?: string | null }) {
|
|
|
55
54
|
const close = useAIPanelStore((s) => s.close);
|
|
56
55
|
const activity = useAIPanelStore((s) => s.activity);
|
|
57
56
|
const connections = useAIPanelStore((s) => s.connections);
|
|
58
|
-
const rateLimit = useAIPanelStore((s) => s.rateLimit);
|
|
59
57
|
const loadActivity = useAIPanelStore((s) => s.loadActivity);
|
|
60
58
|
const { copied, copy } = useCopyCurl(currentPath ?? null);
|
|
61
59
|
const [bootstrapPrompt, setBootstrapPrompt] = useState<string>("");
|
|
@@ -68,8 +66,36 @@ export function AIPanel({ currentPath }: { currentPath?: string | null }) {
|
|
|
68
66
|
() => bootstrapPrompt.replace(/\$WIKI_URL/g, origin),
|
|
69
67
|
[bootstrapPrompt, origin]
|
|
70
68
|
);
|
|
69
|
+
const getMcpRegister = useCallback(
|
|
70
|
+
() =>
|
|
71
|
+
`npx wiki-viewer-mcp register --url ${origin} --id ai:myagent --name "My Agent"`,
|
|
72
|
+
[origin]
|
|
73
|
+
);
|
|
74
|
+
const getMcpJson = useCallback(
|
|
75
|
+
() =>
|
|
76
|
+
JSON.stringify(
|
|
77
|
+
{
|
|
78
|
+
servers: {
|
|
79
|
+
"wiki-viewer": {
|
|
80
|
+
command: "npx",
|
|
81
|
+
args: ["wiki-viewer-mcp"],
|
|
82
|
+
env: {
|
|
83
|
+
WIKI_VIEWER_URL: origin,
|
|
84
|
+
WIKI_VIEWER_TOKEN: "<token from register>",
|
|
85
|
+
WIKI_VIEWER_AGENT_ID: "ai:myagent",
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
null,
|
|
91
|
+
2
|
|
92
|
+
),
|
|
93
|
+
[origin]
|
|
94
|
+
);
|
|
71
95
|
const skillCli = useCopyButton(getSkillCli);
|
|
72
96
|
const bootstrapCopy = useCopyButton(getBootstrapPrompt);
|
|
97
|
+
const mcpRegister = useCopyButton(getMcpRegister);
|
|
98
|
+
const mcpJson = useCopyButton(getMcpJson);
|
|
73
99
|
|
|
74
100
|
// Keyboard: Esc closes
|
|
75
101
|
useEffect(() => {
|
|
@@ -144,74 +170,84 @@ export function AIPanel({ currentPath }: { currentPath?: string | null }) {
|
|
|
144
170
|
|
|
145
171
|
{/* Scrollable body */}
|
|
146
172
|
<div className="flex-1 overflow-y-auto space-y-5 px-4 py-4">
|
|
147
|
-
|
|
173
|
+
{/* What this does — one line */}
|
|
174
|
+
<section className="rounded-md border border-border bg-muted/30 p-3">
|
|
175
|
+
<p className="text-sm font-medium text-foreground">
|
|
176
|
+
Work with this wiki using AI
|
|
177
|
+
</p>
|
|
178
|
+
<p className="mt-1 text-xs leading-relaxed text-muted-foreground">
|
|
179
|
+
Connect Claude, Cursor, ChatGPT, and more. Pick by what you want —
|
|
180
|
+
each option gives a copy-paste setup. You approve every assistant
|
|
181
|
+
in <span className="font-medium text-foreground/80">Agents</span> below before it gets access.
|
|
182
|
+
</p>
|
|
183
|
+
</section>
|
|
148
184
|
|
|
149
|
-
{/*
|
|
150
|
-
<section className="space-y-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
185
|
+
{/* Choose by goal */}
|
|
186
|
+
<section className="space-y-3">
|
|
187
|
+
{/* Goal 1: collaborate (one-off instructions + permanent skill) */}
|
|
188
|
+
<div className="rounded-md border border-border bg-muted/40 p-3 space-y-1.5">
|
|
189
|
+
<p className="text-xs font-semibold text-foreground">
|
|
190
|
+
Co-write docs with you
|
|
191
|
+
</p>
|
|
192
|
+
<p className="text-[10px] text-muted-foreground/70">
|
|
193
|
+
Edits arrive as suggestions with comments you accept or revert.
|
|
194
|
+
No MCP needed.
|
|
195
|
+
</p>
|
|
196
|
+
<Button size="sm" variant="outline" className="h-7 w-full text-xs gap-1.5" onClick={() => void bootstrapCopy.copy()} disabled={!bootstrapPrompt}>
|
|
197
|
+
{bootstrapCopy.copied
|
|
198
|
+
? <><Check className="h-3.5 w-3.5 text-green-500" /> Copied!</>
|
|
199
|
+
: <><Copy className="h-3.5 w-3.5" /> Paste into any chatbot</>}
|
|
200
|
+
</Button>
|
|
201
|
+
<p className="text-[10px] text-muted-foreground/50 pt-1">
|
|
202
|
+
Or install once so it sticks{" "}
|
|
203
|
+
<span className="text-muted-foreground/40">· Claude, OpenCode</span>:
|
|
204
|
+
</p>
|
|
155
205
|
<div className="flex items-center gap-2">
|
|
156
|
-
<code className="flex-1 text-
|
|
157
|
-
{
|
|
206
|
+
<code className="flex-1 text-[10px] font-mono text-foreground/80 truncate bg-muted rounded px-1 py-0.5">
|
|
207
|
+
{SKILL_CLI}
|
|
158
208
|
</code>
|
|
159
|
-
<Button
|
|
160
|
-
|
|
161
|
-
variant="ghost"
|
|
162
|
-
className="h-6 w-6 p-0 shrink-0"
|
|
163
|
-
title="Copy curl example"
|
|
164
|
-
onClick={() => void copy()}
|
|
165
|
-
>
|
|
166
|
-
{copied ? (
|
|
167
|
-
<Check className="h-3.5 w-3.5 text-green-500" />
|
|
168
|
-
) : (
|
|
169
|
-
<Copy className="h-3.5 w-3.5" />
|
|
170
|
-
)}
|
|
209
|
+
<Button size="sm" variant="ghost" className="h-6 w-6 p-0 shrink-0" title="Copy this command" onClick={() => void skillCli.copy()}>
|
|
210
|
+
{skillCli.copied ? <Check className="h-3.5 w-3.5 text-green-500" /> : <Copy className="h-3.5 w-3.5" />}
|
|
171
211
|
</Button>
|
|
172
212
|
</div>
|
|
173
|
-
<p className="text-[10px] text-muted-foreground/70">
|
|
174
|
-
Copy icon copies a registration-flow <code className="bg-muted px-0.5 rounded">curl</code> trace for the current file.
|
|
175
|
-
</p>
|
|
176
213
|
</div>
|
|
177
|
-
</section>
|
|
178
214
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
<
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
</div>
|
|
195
|
-
</div>
|
|
196
|
-
<div className="space-y-1">
|
|
197
|
-
<p className="text-[10px] text-muted-foreground/70">Any agent (paste into chat):</p>
|
|
198
|
-
<Button size="sm" variant="outline" className="h-7 w-full text-xs gap-1.5" onClick={() => void bootstrapCopy.copy()} disabled={!bootstrapPrompt}>
|
|
199
|
-
{bootstrapCopy.copied
|
|
200
|
-
? <><Check className="h-3.5 w-3.5 text-green-500" /> Copied!</>
|
|
201
|
-
: <><Copy className="h-3.5 w-3.5" /> Copy bootstrap prompt</>}
|
|
215
|
+
{/* Goal 2: raw filework */}
|
|
216
|
+
<div className="rounded-md border border-border bg-muted/40 p-3 space-y-1.5">
|
|
217
|
+
<p className="text-xs font-semibold text-foreground">
|
|
218
|
+
Edit files directly{" "}
|
|
219
|
+
<span className="font-normal text-muted-foreground/60">· Claude Code, Cursor, Codex</span>
|
|
220
|
+
</p>
|
|
221
|
+
<p className="text-[10px] text-muted-foreground/70">
|
|
222
|
+
Native read / search / edit tools over MCP. Run to request access:
|
|
223
|
+
</p>
|
|
224
|
+
<div className="flex items-center gap-2">
|
|
225
|
+
<code className="flex-1 text-[10px] font-mono text-foreground/80 truncate bg-muted rounded px-1 py-0.5">
|
|
226
|
+
{getMcpRegister()}
|
|
227
|
+
</code>
|
|
228
|
+
<Button size="sm" variant="ghost" className="h-6 w-6 p-0 shrink-0" title="Copy this command" onClick={() => void mcpRegister.copy()}>
|
|
229
|
+
{mcpRegister.copied ? <Check className="h-3.5 w-3.5 text-green-500" /> : <Copy className="h-3.5 w-3.5" />}
|
|
202
230
|
</Button>
|
|
203
231
|
</div>
|
|
204
|
-
<
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
232
|
+
<p className="text-[10px] text-muted-foreground/50">Approve in <span className="font-medium text-foreground/70">Agents</span>, then add to your tool:</p>
|
|
233
|
+
<Button size="sm" variant="outline" className="h-7 w-full text-xs gap-1.5" onClick={() => void mcpJson.copy()}>
|
|
234
|
+
{mcpJson.copied
|
|
235
|
+
? <><Check className="h-3.5 w-3.5 text-green-500" /> Copied!</>
|
|
236
|
+
: <><Copy className="h-3.5 w-3.5" /> Copy config (mcp.json)</>}
|
|
237
|
+
</Button>
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
<div className="flex flex-wrap items-center gap-x-3 gap-y-1">
|
|
241
|
+
<span className="text-[10px] text-muted-foreground/50">For developers:</span>
|
|
242
|
+
<button type="button" onClick={() => void copy()} className="inline-flex items-center gap-1 text-[10px] text-primary hover:underline">
|
|
243
|
+
{copied ? <Check className="h-3 w-3 text-green-500" /> : <Copy className="h-3 w-3" />} curl example
|
|
244
|
+
</button>
|
|
245
|
+
<a href="/api/agents/skill" target="_blank" rel="noreferrer" className="inline-flex items-center gap-1 text-[10px] text-primary hover:underline">
|
|
246
|
+
<ExternalLink className="h-3 w-3" /> skill
|
|
247
|
+
</a>
|
|
248
|
+
<a href="/api/agents/install" target="_blank" rel="noreferrer" className="inline-flex items-center gap-1 text-[10px] text-primary hover:underline">
|
|
249
|
+
<ExternalLink className="h-3 w-3" /> install JSON
|
|
250
|
+
</a>
|
|
215
251
|
</div>
|
|
216
252
|
</section>
|
|
217
253
|
|
|
@@ -263,24 +299,6 @@ export function AIPanel({ currentPath }: { currentPath?: string | null }) {
|
|
|
263
299
|
</div>
|
|
264
300
|
</section>
|
|
265
301
|
|
|
266
|
-
{/* Settings */}
|
|
267
|
-
<section className="space-y-2">
|
|
268
|
-
<h3 className="text-xs font-semibold uppercase tracking-widest text-muted-foreground">
|
|
269
|
-
Settings
|
|
270
|
-
</h3>
|
|
271
|
-
<div className="rounded-md border border-border bg-muted/40 p-3 space-y-1.5">
|
|
272
|
-
<div className="flex items-center justify-between text-xs">
|
|
273
|
-
<span className="text-muted-foreground">Rate limit</span>
|
|
274
|
-
<span className="font-mono">
|
|
275
|
-
{rateLimit !== null ? `${rateLimit} ops/min` : "--"}
|
|
276
|
-
</span>
|
|
277
|
-
</div>
|
|
278
|
-
<p className="text-[10px] text-muted-foreground/60">
|
|
279
|
-
Override with <code className="bg-muted px-0.5 rounded">AGENT_RATE_LIMIT</code> env var.
|
|
280
|
-
</p>
|
|
281
|
-
</div>
|
|
282
|
-
</section>
|
|
283
|
-
|
|
284
302
|
{/* Docs link */}
|
|
285
303
|
<section>
|
|
286
304
|
<a
|
|
@@ -297,26 +315,3 @@ export function AIPanel({ currentPath }: { currentPath?: string | null }) {
|
|
|
297
315
|
</>
|
|
298
316
|
);
|
|
299
317
|
}
|
|
300
|
-
|
|
301
|
-
function SignedInUser() {
|
|
302
|
-
const { data: session } = authClient.useSession();
|
|
303
|
-
if (!session?.user) return null;
|
|
304
|
-
return (
|
|
305
|
-
<div className="flex items-center justify-between rounded-md border border-border bg-muted/40 p-3">
|
|
306
|
-
<div className="text-sm min-w-0">
|
|
307
|
-
<div className="font-medium truncate">{session.user.name}</div>
|
|
308
|
-
<div className="text-xs text-muted-foreground truncate">{session.user.email}</div>
|
|
309
|
-
</div>
|
|
310
|
-
<button
|
|
311
|
-
type="button"
|
|
312
|
-
onClick={async () => {
|
|
313
|
-
await authClient.signOut();
|
|
314
|
-
window.location.href = "/signin";
|
|
315
|
-
}}
|
|
316
|
-
className="ml-2 shrink-0 text-xs px-2 py-1 rounded border border-border hover:bg-accent"
|
|
317
|
-
>
|
|
318
|
-
Sign out
|
|
319
|
-
</button>
|
|
320
|
-
</div>
|
|
321
|
-
);
|
|
322
|
-
}
|
|
@@ -251,7 +251,7 @@ export function TokenSection() {
|
|
|
251
251
|
<section className="space-y-2">
|
|
252
252
|
<div className="flex items-center justify-between">
|
|
253
253
|
<h3 className="text-xs font-semibold uppercase tracking-widest text-muted-foreground">
|
|
254
|
-
Agents
|
|
254
|
+
Agents · approve & manage access
|
|
255
255
|
</h3>
|
|
256
256
|
<Button
|
|
257
257
|
size="sm"
|
|
@@ -269,7 +269,7 @@ export function TokenSection() {
|
|
|
269
269
|
<div className="rounded-md border border-amber-500/40 bg-amber-50/10 p-2">
|
|
270
270
|
<p className="text-[10px] font-semibold uppercase tracking-widest text-amber-600/80 mb-1 flex items-center gap-1">
|
|
271
271
|
<Clock className="h-3 w-3" />
|
|
272
|
-
|
|
272
|
+
Waiting for your approval ({pending.length})
|
|
273
273
|
</p>
|
|
274
274
|
<ul>
|
|
275
275
|
{pending.map((reg) => (
|
|
@@ -288,9 +288,10 @@ export function TokenSection() {
|
|
|
288
288
|
<div className="rounded-md border border-border bg-muted/40 p-2">
|
|
289
289
|
{agents.length === 0 ? (
|
|
290
290
|
<p className="text-xs text-muted-foreground/60 py-1">
|
|
291
|
-
No
|
|
291
|
+
No assistants connected yet.{" "}
|
|
292
292
|
<span className="text-muted-foreground/40">
|
|
293
|
-
|
|
293
|
+
Connect one above; once it requests access it’ll appear here
|
|
294
|
+
for you to approve.
|
|
294
295
|
</span>
|
|
295
296
|
</p>
|
|
296
297
|
) : (
|
|
@@ -303,9 +304,9 @@ export function TokenSection() {
|
|
|
303
304
|
</div>
|
|
304
305
|
|
|
305
306
|
<p className="text-[10px] text-muted-foreground/60 leading-relaxed">
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
307
|
+
Each assistant gets its own access that you can revoke anytime. New
|
|
308
|
+
requests appear above for your approval before they can read or edit
|
|
309
|
+
anything.
|
|
309
310
|
</p>
|
|
310
311
|
</section>
|
|
311
312
|
);
|
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useCallback, useEffect, useState } from "react";
|
|
4
|
-
import { AlertCircle, Loader2 } from "lucide-react";
|
|
4
|
+
import { AlertCircle, Loader2, X } from "lucide-react";
|
|
5
5
|
|
|
6
6
|
import { Button } from "@/components/ui/button";
|
|
7
|
-
import {
|
|
8
|
-
Sheet,
|
|
9
|
-
SheetContent,
|
|
10
|
-
SheetDescription,
|
|
11
|
-
SheetFooter,
|
|
12
|
-
SheetHeader,
|
|
13
|
-
SheetTitle,
|
|
14
|
-
} from "@/components/ui/sheet";
|
|
7
|
+
import { Sheet, SheetContent, SheetClose } from "@/components/ui/sheet";
|
|
15
8
|
import { showError } from "@/lib/toast";
|
|
9
|
+
import { authClient } from "@/lib/auth/client";
|
|
16
10
|
|
|
17
11
|
interface AuthSettings {
|
|
18
12
|
allowedEmails: string[];
|
|
19
13
|
allowedDomains: string[];
|
|
20
14
|
source: "config" | "env";
|
|
21
15
|
envFallbackActive: boolean;
|
|
16
|
+
rateLimit?: number;
|
|
22
17
|
}
|
|
23
18
|
|
|
24
19
|
function listToText(list: string[]): string {
|
|
@@ -45,6 +40,8 @@ export function AuthSettingsSheet({
|
|
|
45
40
|
const [source, setSource] = useState<"config" | "env">("env");
|
|
46
41
|
const [emailsText, setEmailsText] = useState("");
|
|
47
42
|
const [domainsText, setDomainsText] = useState("");
|
|
43
|
+
const [rateLimit, setRateLimit] = useState<number | null>(null);
|
|
44
|
+
const { data: session } = authClient.useSession();
|
|
48
45
|
|
|
49
46
|
const load = useCallback(async () => {
|
|
50
47
|
setLoading(true);
|
|
@@ -56,6 +53,7 @@ export function AuthSettingsSheet({
|
|
|
56
53
|
setEmailsText(listToText(data.allowedEmails));
|
|
57
54
|
setDomainsText(listToText(data.allowedDomains));
|
|
58
55
|
setSource(data.source);
|
|
56
|
+
setRateLimit(typeof data.rateLimit === "number" ? data.rateLimit : null);
|
|
59
57
|
} catch (e) {
|
|
60
58
|
setError(e instanceof Error ? e.message : "Failed to load");
|
|
61
59
|
} finally {
|
|
@@ -101,22 +99,63 @@ export function AuthSettingsSheet({
|
|
|
101
99
|
|
|
102
100
|
return (
|
|
103
101
|
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
104
|
-
<SheetContent
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
102
|
+
<SheetContent
|
|
103
|
+
side="right"
|
|
104
|
+
className="w-80 sm:max-w-md flex flex-col border-l border-border p-0"
|
|
105
|
+
>
|
|
106
|
+
{/* Header — matches AI panel chrome */}
|
|
107
|
+
<div className="flex items-center justify-between border-b border-border px-4 py-3 shrink-0">
|
|
108
|
+
<span className="text-sm font-semibold">Settings</span>
|
|
109
|
+
<SheetClose className="flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-accent focus:outline-none focus:ring-2 focus:ring-ring">
|
|
110
|
+
<X className="h-3.5 w-3.5" />
|
|
111
|
+
<span className="sr-only">Close</span>
|
|
112
|
+
</SheetClose>
|
|
113
|
+
</div>
|
|
113
114
|
|
|
114
115
|
{loading ? (
|
|
115
116
|
<div className="flex flex-1 items-center justify-center">
|
|
116
117
|
<Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
|
|
117
118
|
</div>
|
|
118
119
|
) : (
|
|
119
|
-
<div className="flex-1 space-y-5 overflow-auto py-4">
|
|
120
|
+
<div className="flex-1 space-y-5 overflow-y-auto px-4 py-4">
|
|
121
|
+
{/* Account */}
|
|
122
|
+
{session?.user && (
|
|
123
|
+
<section className="space-y-2">
|
|
124
|
+
<h3 className="text-xs font-semibold uppercase tracking-widest text-muted-foreground">
|
|
125
|
+
Account
|
|
126
|
+
</h3>
|
|
127
|
+
<div className="flex items-center justify-between rounded-md border border-border bg-muted/40 p-3">
|
|
128
|
+
<div className="text-sm min-w-0">
|
|
129
|
+
<div className="font-medium truncate">{session.user.name}</div>
|
|
130
|
+
<div className="text-xs text-muted-foreground truncate">{session.user.email}</div>
|
|
131
|
+
</div>
|
|
132
|
+
<button
|
|
133
|
+
type="button"
|
|
134
|
+
onClick={async () => {
|
|
135
|
+
await authClient.signOut();
|
|
136
|
+
window.location.href = "/signin";
|
|
137
|
+
}}
|
|
138
|
+
className="ml-2 shrink-0 text-xs px-2 py-1 rounded border border-border hover:bg-accent"
|
|
139
|
+
>
|
|
140
|
+
Sign out
|
|
141
|
+
</button>
|
|
142
|
+
</div>
|
|
143
|
+
</section>
|
|
144
|
+
)}
|
|
145
|
+
|
|
146
|
+
{/* Signup allowlist */}
|
|
147
|
+
<section className="space-y-2">
|
|
148
|
+
<h3 className="text-xs font-semibold uppercase tracking-widest text-muted-foreground">
|
|
149
|
+
Signup allowlist
|
|
150
|
+
</h3>
|
|
151
|
+
<p className="text-xs leading-relaxed text-muted-foreground">
|
|
152
|
+
Control who can create an account. Leave both lists empty to
|
|
153
|
+
allow any email. Saving here overrides the{" "}
|
|
154
|
+
<code className="bg-muted px-0.5 rounded">AUTH_ALLOWED_EMAILS</code>{" "}
|
|
155
|
+
and{" "}
|
|
156
|
+
<code className="bg-muted px-0.5 rounded">AUTH_ALLOWED_DOMAIN</code>{" "}
|
|
157
|
+
environment variables.
|
|
158
|
+
</p>
|
|
120
159
|
{source === "env" && (
|
|
121
160
|
<div className="flex items-start gap-2 rounded-md bg-muted px-3 py-2 text-xs text-muted-foreground">
|
|
122
161
|
<AlertCircle className="h-3.5 w-3.5 shrink-0 mt-0.5" />
|
|
@@ -187,22 +226,42 @@ export function AuthSettingsSheet({
|
|
|
187
226
|
{error}
|
|
188
227
|
</div>
|
|
189
228
|
)}
|
|
229
|
+
</section>
|
|
230
|
+
|
|
231
|
+
{/* Agent rate limit */}
|
|
232
|
+
<section className="space-y-2">
|
|
233
|
+
<h3 className="text-xs font-semibold uppercase tracking-widest text-muted-foreground">
|
|
234
|
+
Agent rate limit
|
|
235
|
+
</h3>
|
|
236
|
+
<div className="rounded-md border border-border bg-muted/40 p-3 space-y-1.5">
|
|
237
|
+
<div className="flex items-center justify-between text-xs">
|
|
238
|
+
<span className="text-muted-foreground">Per agent</span>
|
|
239
|
+
<span className="font-mono">
|
|
240
|
+
{rateLimit !== null ? `${rateLimit} ops/min` : "--"}
|
|
241
|
+
</span>
|
|
242
|
+
</div>
|
|
243
|
+
<p className="text-[10px] text-muted-foreground/60">
|
|
244
|
+
Override with <code className="bg-muted px-0.5 rounded">AGENT_RATE_LIMIT</code> env var.
|
|
245
|
+
</p>
|
|
246
|
+
</div>
|
|
247
|
+
</section>
|
|
190
248
|
</div>
|
|
191
249
|
)}
|
|
192
250
|
|
|
193
|
-
<
|
|
251
|
+
<div className="flex items-center justify-end gap-2 border-t border-border px-4 py-3 shrink-0">
|
|
194
252
|
<Button
|
|
253
|
+
size="sm"
|
|
195
254
|
variant="ghost"
|
|
196
255
|
onClick={() => onOpenChange(false)}
|
|
197
256
|
disabled={saving}
|
|
198
257
|
>
|
|
199
258
|
Cancel
|
|
200
259
|
</Button>
|
|
201
|
-
<Button onClick={handleSave} disabled={saving || loading} className="gap-1.5">
|
|
260
|
+
<Button size="sm" onClick={handleSave} disabled={saving || loading} className="gap-1.5">
|
|
202
261
|
{saving && <Loader2 className="h-3.5 w-3.5 animate-spin" />}
|
|
203
262
|
Save
|
|
204
263
|
</Button>
|
|
205
|
-
</
|
|
264
|
+
</div>
|
|
206
265
|
</SheetContent>
|
|
207
266
|
</Sheet>
|
|
208
267
|
);
|
|
@@ -184,6 +184,44 @@ export function KBEditor() {
|
|
|
184
184
|
void useProofStore.getState().loadSidecar(currentPath);
|
|
185
185
|
}, [currentPath]);
|
|
186
186
|
|
|
187
|
+
// Human edit-lease heartbeat: tell the server a human has this markdown doc
|
|
188
|
+
// open so computeCollabState() reports "active" even before the first
|
|
189
|
+
// suggestion/comment. This is what makes raw-fs writes from agents defer to
|
|
190
|
+
// the collaborative (Tier-2) path while a human is editing. Only markdown
|
|
191
|
+
// files participate in the collab-state machine.
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
if (typeof window === "undefined") return;
|
|
194
|
+
if (!currentPath) return;
|
|
195
|
+
if (!/\.(md|markdown)$/i.test(currentPath)) return;
|
|
196
|
+
|
|
197
|
+
const path = currentPath;
|
|
198
|
+
const ping = (action: "open" | "heartbeat" | "close") => {
|
|
199
|
+
// keepalive lets the "close" beacon survive page unload.
|
|
200
|
+
void fetch("/api/wiki/presence", {
|
|
201
|
+
method: "POST",
|
|
202
|
+
headers: { "Content-Type": "application/json" },
|
|
203
|
+
body: JSON.stringify({ path, action }),
|
|
204
|
+
keepalive: action === "close",
|
|
205
|
+
}).catch(() => {
|
|
206
|
+
/* presence is best-effort; ignore failures */
|
|
207
|
+
});
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
ping("open");
|
|
211
|
+
// Refresh well within the 90s server lease TTL.
|
|
212
|
+
const id = setInterval(() => ping("heartbeat"), 30_000);
|
|
213
|
+
const onHidden = () => {
|
|
214
|
+
if (document.visibilityState === "hidden") ping("heartbeat");
|
|
215
|
+
};
|
|
216
|
+
document.addEventListener("visibilitychange", onHidden);
|
|
217
|
+
|
|
218
|
+
return () => {
|
|
219
|
+
clearInterval(id);
|
|
220
|
+
document.removeEventListener("visibilitychange", onHidden);
|
|
221
|
+
ping("close");
|
|
222
|
+
};
|
|
223
|
+
}, [currentPath]);
|
|
224
|
+
|
|
187
225
|
// Subscribe to chokidar SSE: when current file changes on disk, reload sidecar.
|
|
188
226
|
useEffect(() => {
|
|
189
227
|
if (typeof window === "undefined") return;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal durable audit log for raw-fs mutations (Tier 1).
|
|
3
|
+
*
|
|
4
|
+
* Uses the same ~/.wiki-viewer/auth.db already opened by better-auth.
|
|
5
|
+
* Table: agent_fs_audit
|
|
6
|
+
*
|
|
7
|
+
* For .md files the sidecar event covers provenance; this covers ALL types.
|
|
8
|
+
*/
|
|
9
|
+
import Database from "better-sqlite3";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import os from "node:os";
|
|
12
|
+
import { mkdirSync } from "node:fs";
|
|
13
|
+
|
|
14
|
+
function dataDir(): string {
|
|
15
|
+
return path.join(process.env.HOME ?? os.homedir(), ".wiki-viewer");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Lazily initialised so tests can set HOME before first import.
|
|
19
|
+
let _db: InstanceType<typeof Database> | null = null;
|
|
20
|
+
|
|
21
|
+
function getDb(): InstanceType<typeof Database> {
|
|
22
|
+
if (_db) return _db;
|
|
23
|
+
const dir = dataDir();
|
|
24
|
+
mkdirSync(dir, { recursive: true });
|
|
25
|
+
const dbPath = path.join(dir, "auth.db");
|
|
26
|
+
_db = new Database(dbPath);
|
|
27
|
+
_db.pragma("journal_mode = WAL");
|
|
28
|
+
_db.exec(`
|
|
29
|
+
CREATE TABLE IF NOT EXISTS agent_fs_audit (
|
|
30
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
31
|
+
agent_id TEXT NOT NULL,
|
|
32
|
+
op TEXT NOT NULL,
|
|
33
|
+
path TEXT NOT NULL,
|
|
34
|
+
old_sha TEXT,
|
|
35
|
+
new_sha TEXT,
|
|
36
|
+
forced INTEGER NOT NULL DEFAULT 0,
|
|
37
|
+
at TEXT NOT NULL
|
|
38
|
+
)
|
|
39
|
+
`);
|
|
40
|
+
return _db;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface AuditRow {
|
|
44
|
+
agentId: string;
|
|
45
|
+
op: string;
|
|
46
|
+
path: string;
|
|
47
|
+
oldSha?: string;
|
|
48
|
+
newSha?: string;
|
|
49
|
+
forced?: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Write one audit row synchronously (SQLite sync is fine here — it's a WAL journal append).
|
|
54
|
+
* Fire-and-forget: never throws; logs to stderr on error.
|
|
55
|
+
*/
|
|
56
|
+
export function writeAuditRow(row: AuditRow): void {
|
|
57
|
+
try {
|
|
58
|
+
const db = getDb();
|
|
59
|
+
db.prepare(
|
|
60
|
+
`INSERT INTO agent_fs_audit (agent_id, op, path, old_sha, new_sha, forced, at)
|
|
61
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
62
|
+
).run(
|
|
63
|
+
row.agentId,
|
|
64
|
+
row.op,
|
|
65
|
+
row.path,
|
|
66
|
+
row.oldSha ?? null,
|
|
67
|
+
row.newSha ?? null,
|
|
68
|
+
row.forced ? 1 : 0,
|
|
69
|
+
new Date().toISOString(),
|
|
70
|
+
);
|
|
71
|
+
} catch (e) {
|
|
72
|
+
console.error("[agent-fs audit] write failed:", e);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Reset cached DB handle (used by tests to pick up new HOME). */
|
|
77
|
+
export function _resetAuditDb(): void {
|
|
78
|
+
_db = null;
|
|
79
|
+
}
|
|
@@ -34,7 +34,7 @@ export async function checkAuth(req: Request): Promise<CheckAuthResult> {
|
|
|
34
34
|
id: `user:${u.id}`,
|
|
35
35
|
displayName: u.name,
|
|
36
36
|
tokenHash: "",
|
|
37
|
-
scope: { paths: ["**/*"], ops: ["read", "mutate"] },
|
|
37
|
+
scope: { paths: ["**/*"], ops: ["read", "mutate", "delete"] },
|
|
38
38
|
createdAt: "",
|
|
39
39
|
lastSeen: "",
|
|
40
40
|
},
|
|
@@ -71,7 +71,7 @@ export async function checkAuth(req: Request): Promise<CheckAuthResult> {
|
|
|
71
71
|
|
|
72
72
|
export interface ScopeParams {
|
|
73
73
|
filePath?: string;
|
|
74
|
-
op: "read" | "mutate";
|
|
74
|
+
op: "read" | "mutate" | "delete";
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
export function enforceScope(
|