wiki-viewer 1.14.1 → 1.14.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/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.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/fs/move/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/fs/search/route.js.nft.json +1 -1
- 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.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/assets/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/share/[token]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/share/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/set-root/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/refresh/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/workspaces/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/backlinks/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/content/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/download/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/git-branches/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/git-checkout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/git-diff/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/git-file-info/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/git-history/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/git-pull/route.js.nft.json +1 -1
- 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/outlinks/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.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/search/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 +1 -1
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- 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/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware-manifest.json +1 -1
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/package.json +1 -1
- package/bin/postbuild.js +21 -0
- package/package.json +1 -1
- package/.next/standalone/.github/workflows/publish.yml +0 -37
- package/.next/standalone/.handoffs/archive/handoff-20260612-113332.md +0 -98
- package/.next/standalone/.handoffs/archive/handoff-20260612-141032.md +0 -73
- package/.next/standalone/.pi/ownership.json +0 -3
- package/.next/standalone/AGENTS.md +0 -114
- package/.next/standalone/DESIGN.md +0 -521
- package/.next/standalone/README.md +0 -821
- package/.next/standalone/TODO.md +0 -20
- package/.next/standalone/agents/bootstrap-prompt.md +0 -14
- package/.next/standalone/agents/wiki-viewer-skill/SKILL.md +0 -456
- package/.next/standalone/bin/postbuild.js +0 -78
- package/.next/standalone/bin/wiki-viewer.js +0 -922
- package/.next/standalone/certificates/localhost-key.pem +0 -28
- package/.next/standalone/certificates/localhost.pem +0 -24
- package/.next/standalone/docs/agent-collab-plan.md +0 -1615
- package/.next/standalone/docs/agent-collab-v2-plan.md +0 -771
- package/.next/standalone/docs/agent-fs-plan.md +0 -232
- package/.next/standalone/docs/editorial-skin-spec.md +0 -242
- package/.next/standalone/docs/file-vs-collab-authority.md +0 -124
- package/.next/standalone/docs/mobile-readiness-spec.md +0 -229
- package/.next/standalone/docs/workspacing-plan.md +0 -364
- package/.next/standalone/packages/wiki-viewer-mcp/README.md +0 -195
- package/.next/standalone/packages/wiki-viewer-mcp/bench-e2e.mjs +0 -166
- package/.next/standalone/packages/wiki-viewer-mcp/bench-latency.mjs +0 -87
- package/.next/standalone/packages/wiki-viewer-mcp/bench-payload.mjs +0 -45
- package/.next/standalone/packages/wiki-viewer-mcp/dist/http-client.js +0 -331
- package/.next/standalone/packages/wiki-viewer-mcp/dist/index.js +0 -531
- package/.next/standalone/packages/wiki-viewer-mcp/dist/register.js +0 -94
- package/.next/standalone/packages/wiki-viewer-mcp/dist/state-cache.js +0 -28
- package/.next/standalone/packages/wiki-viewer-mcp/package-lock.json +0 -1716
- package/.next/standalone/packages/wiki-viewer-mcp/package.json +0 -41
- package/.next/standalone/packages/wiki-viewer-mcp/src/http-client.ts +0 -459
- package/.next/standalone/packages/wiki-viewer-mcp/src/index.ts +0 -638
- package/.next/standalone/packages/wiki-viewer-mcp/src/register.ts +0 -144
- package/.next/standalone/packages/wiki-viewer-mcp/src/state-cache.ts +0 -55
- package/.next/standalone/packages/wiki-viewer-mcp/tsconfig.json +0 -19
- package/.next/standalone/pnpm-lock.yaml +0 -6697
- package/.next/standalone/pnpm-workspace.yaml +0 -8
- package/.next/standalone/postcss.config.mjs +0 -2
- package/.next/standalone/src/app/api/agent/activity/route.ts +0 -59
- package/.next/standalone/src/app/api/agent/admin/agents/[agentId]/revoke/route.ts +0 -40
- package/.next/standalone/src/app/api/agent/admin/agents/route.ts +0 -33
- package/.next/standalone/src/app/api/agent/admin/registrations/[regId]/approve/route.ts +0 -83
- package/.next/standalone/src/app/api/agent/admin/registrations/[regId]/deny/route.ts +0 -36
- package/.next/standalone/src/app/api/agent/admin/registrations/route.ts +0 -27
- package/.next/standalone/src/app/api/agent/events/[...path]/route.ts +0 -148
- package/.next/standalone/src/app/api/agent/files/[...path]/route.ts +0 -219
- package/.next/standalone/src/app/api/agent/fs/file/[...path]/route.ts +0 -446
- package/.next/standalone/src/app/api/agent/fs/ls/[[...path]]/route.ts +0 -152
- package/.next/standalone/src/app/api/agent/fs/move/route.ts +0 -130
- package/.next/standalone/src/app/api/agent/fs/search/route.ts +0 -272
- package/.next/standalone/src/app/api/agent/internal/span/route.ts +0 -121
- package/.next/standalone/src/app/api/agent/register/[regId]/route.ts +0 -50
- package/.next/standalone/src/app/api/agent/register/route.ts +0 -118
- package/.next/standalone/src/app/api/agent/settings/route.ts +0 -34
- package/.next/standalone/src/app/api/agent/settings/token/regenerate/route.ts +0 -20
- package/.next/standalone/src/app/api/agent/sidecar/[...path]/route.ts +0 -53
- package/.next/standalone/src/app/api/agents/install/route.ts +0 -143
- package/.next/standalone/src/app/api/agents/skill/route.ts +0 -20
- package/.next/standalone/src/app/api/agents/skill.tar.gz/route.ts +0 -87
- package/.next/standalone/src/app/api/app-proxy/[...path]/route.ts +0 -247
- package/.next/standalone/src/app/api/assets/[...path]/route.ts +0 -75
- package/.next/standalone/src/app/api/auth/[...all]/route.ts +0 -7
- package/.next/standalone/src/app/api/owner/init/route.ts +0 -14
- package/.next/standalone/src/app/api/share/[token]/route.ts +0 -269
- package/.next/standalone/src/app/api/share/route.ts +0 -127
- package/.next/standalone/src/app/api/system/admins/route.ts +0 -103
- package/.next/standalone/src/app/api/system/auth-config/route.ts +0 -22
- package/.next/standalone/src/app/api/system/auth-settings/route.ts +0 -86
- package/.next/standalone/src/app/api/system/browse/route.ts +0 -82
- package/.next/standalone/src/app/api/system/clear-root/route.ts +0 -16
- package/.next/standalone/src/app/api/system/config/route.ts +0 -14
- package/.next/standalone/src/app/api/system/pins/route.ts +0 -38
- package/.next/standalone/src/app/api/system/reveal/route.ts +0 -36
- package/.next/standalone/src/app/api/system/root-status/route.ts +0 -13
- package/.next/standalone/src/app/api/system/set-root/route.ts +0 -53
- package/.next/standalone/src/app/api/system/users/route.ts +0 -120
- package/.next/standalone/src/app/api/system/workspaces/[id]/open/route.ts +0 -34
- package/.next/standalone/src/app/api/system/workspaces/[id]/refresh/route.ts +0 -32
- package/.next/standalone/src/app/api/system/workspaces/[id]/route.ts +0 -78
- package/.next/standalone/src/app/api/system/workspaces/route.ts +0 -128
- package/.next/standalone/src/app/api/upload/[...path]/route.ts +0 -85
- package/.next/standalone/src/app/api/wiki/app/route.ts +0 -60
- package/.next/standalone/src/app/api/wiki/backlinks/route.ts +0 -29
- package/.next/standalone/src/app/api/wiki/content/route.ts +0 -159
- package/.next/standalone/src/app/api/wiki/download/route.ts +0 -100
- package/.next/standalone/src/app/api/wiki/file/route.ts +0 -57
- package/.next/standalone/src/app/api/wiki/folder/route.ts +0 -34
- package/.next/standalone/src/app/api/wiki/git-branches/route.ts +0 -32
- package/.next/standalone/src/app/api/wiki/git-checkout/route.ts +0 -45
- package/.next/standalone/src/app/api/wiki/git-diff/route.ts +0 -33
- package/.next/standalone/src/app/api/wiki/git-file-info/route.ts +0 -27
- package/.next/standalone/src/app/api/wiki/git-history/route.ts +0 -27
- package/.next/standalone/src/app/api/wiki/git-pull/route.ts +0 -48
- package/.next/standalone/src/app/api/wiki/move/route.ts +0 -61
- package/.next/standalone/src/app/api/wiki/new-file/route.ts +0 -56
- package/.next/standalone/src/app/api/wiki/outlinks/route.ts +0 -76
- package/.next/standalone/src/app/api/wiki/page/route.ts +0 -92
- package/.next/standalone/src/app/api/wiki/presence/route.ts +0 -56
- package/.next/standalone/src/app/api/wiki/route.ts +0 -116
- package/.next/standalone/src/app/api/wiki/search/route.ts +0 -44
- package/.next/standalone/src/app/api/wiki/slugs/route.ts +0 -54
- package/.next/standalone/src/app/api/wiki/upload/route.ts +0 -95
- package/.next/standalone/src/app/api/wiki/watch/route.ts +0 -67
- package/.next/standalone/src/app/globals.css +0 -1249
- package/.next/standalone/src/app/icon.svg +0 -11
- package/.next/standalone/src/app/layout.tsx +0 -78
- package/.next/standalone/src/app/manifest.ts +0 -30
- package/.next/standalone/src/app/page.tsx +0 -3260
- package/.next/standalone/src/app/s/[token]/page.tsx +0 -357
- package/.next/standalone/src/app/signin/page.tsx +0 -16
- package/.next/standalone/src/app/signin/signin-form.tsx +0 -238
- package/.next/standalone/src/components/ai-panel/activity-row.tsx +0 -284
- package/.next/standalone/src/components/ai-panel/ai-panel.tsx +0 -311
- package/.next/standalone/src/components/ai-panel/token-section.tsx +0 -313
- package/.next/standalone/src/components/auth-settings-sheet.tsx +0 -474
- package/.next/standalone/src/components/confirm-dialog.tsx +0 -56
- package/.next/standalone/src/components/dir-picker.tsx +0 -606
- package/.next/standalone/src/components/editor/backlinks-panel.tsx +0 -139
- package/.next/standalone/src/components/editor/bubble-menu.tsx +0 -346
- package/.next/standalone/src/components/editor/callout-extension.ts +0 -36
- package/.next/standalone/src/components/editor/comment-pip.tsx +0 -56
- package/.next/standalone/src/components/editor/comment-thread.tsx +0 -254
- package/.next/standalone/src/components/editor/csv-viewer.tsx +0 -379
- package/.next/standalone/src/components/editor/document-outline.tsx +0 -230
- package/.next/standalone/src/components/editor/editor-toolbar.tsx +0 -564
- package/.next/standalone/src/components/editor/editor.tsx +0 -1327
- package/.next/standalone/src/components/editor/extensions/drag-handle.ts +0 -325
- package/.next/standalone/src/components/editor/extensions/heading-anchors.ts +0 -55
- package/.next/standalone/src/components/editor/extensions/proof-span.ts +0 -60
- package/.next/standalone/src/components/editor/extensions/resizable-image.tsx +0 -159
- package/.next/standalone/src/components/editor/extensions.ts +0 -150
- package/.next/standalone/src/components/editor/file-fallback-viewer.tsx +0 -75
- package/.next/standalone/src/components/editor/folder-index.tsx +0 -236
- package/.next/standalone/src/components/editor/google-doc-viewer.tsx +0 -73
- package/.next/standalone/src/components/editor/image-viewer.tsx +0 -57
- package/.next/standalone/src/components/editor/large-file-gate.tsx +0 -108
- package/.next/standalone/src/components/editor/link-popover.tsx +0 -111
- package/.next/standalone/src/components/editor/media-popover.tsx +0 -212
- package/.next/standalone/src/components/editor/media-viewer.tsx +0 -70
- package/.next/standalone/src/components/editor/mermaid-viewer.tsx +0 -286
- package/.next/standalone/src/components/editor/node-app-viewer.tsx +0 -328
- package/.next/standalone/src/components/editor/notebook-viewer.tsx +0 -371
- package/.next/standalone/src/components/editor/office/docx-viewer.tsx +0 -93
- package/.next/standalone/src/components/editor/office/office-chrome.tsx +0 -83
- package/.next/standalone/src/components/editor/office/pptx-viewer.tsx +0 -104
- package/.next/standalone/src/components/editor/office/xlsx-viewer.tsx +0 -192
- package/.next/standalone/src/components/editor/pdf-viewer.tsx +0 -32
- package/.next/standalone/src/components/editor/proof-span-popover.tsx +0 -162
- package/.next/standalone/src/components/editor/slash-commands.tsx +0 -629
- package/.next/standalone/src/components/editor/source-viewer.tsx +0 -289
- package/.next/standalone/src/components/editor/suggest-edit-popover.tsx +0 -229
- package/.next/standalone/src/components/editor/suggestion-card.tsx +0 -206
- package/.next/standalone/src/components/editor/table-menu.tsx +0 -232
- package/.next/standalone/src/components/editor/view-mode-comment-button.tsx +0 -77
- package/.next/standalone/src/components/editor/website-viewer.tsx +0 -75
- package/.next/standalone/src/components/editor/wiki-link-create-dialog.tsx +0 -189
- package/.next/standalone/src/components/editor/wiki-link-decorator.ts +0 -79
- package/.next/standalone/src/components/editor/wiki-link-extension.ts +0 -120
- package/.next/standalone/src/components/editor/wiki-link-picker.tsx +0 -250
- package/.next/standalone/src/components/layout/viewer-toolbar.tsx +0 -54
- package/.next/standalone/src/components/search/search-command-dialog.tsx +0 -246
- package/.next/standalone/src/components/search/sidebar-search-box.tsx +0 -121
- package/.next/standalone/src/components/search/snippet-text.tsx +0 -48
- package/.next/standalone/src/components/share-dialog.tsx +0 -307
- package/.next/standalone/src/components/theme-provider.tsx +0 -16
- package/.next/standalone/src/components/theme-toggle.tsx +0 -24
- package/.next/standalone/src/components/ui/badge.tsx +0 -35
- package/.next/standalone/src/components/ui/button.tsx +0 -56
- package/.next/standalone/src/components/ui/card.tsx +0 -63
- package/.next/standalone/src/components/ui/collapsible.tsx +0 -9
- package/.next/standalone/src/components/ui/command.tsx +0 -151
- package/.next/standalone/src/components/ui/context-menu.tsx +0 -191
- package/.next/standalone/src/components/ui/dialog.tsx +0 -119
- package/.next/standalone/src/components/ui/dropdown-menu.tsx +0 -194
- package/.next/standalone/src/components/ui/input.tsx +0 -22
- package/.next/standalone/src/components/ui/label.tsx +0 -25
- package/.next/standalone/src/components/ui/popover.tsx +0 -33
- package/.next/standalone/src/components/ui/scroll-area.tsx +0 -47
- package/.next/standalone/src/components/ui/select.tsx +0 -156
- package/.next/standalone/src/components/ui/separator.tsx +0 -30
- package/.next/standalone/src/components/ui/sheet.tsx +0 -140
- package/.next/standalone/src/components/ui/skeleton.tsx +0 -15
- package/.next/standalone/src/components/ui/switch.tsx +0 -28
- package/.next/standalone/src/components/ui/tabs.tsx +0 -54
- package/.next/standalone/src/components/ui/textarea.tsx +0 -21
- package/.next/standalone/src/components/ui/tip.tsx +0 -23
- package/.next/standalone/src/components/ui/toggle-group.tsx +0 -41
- package/.next/standalone/src/components/ui/tooltip.tsx +0 -27
- package/.next/standalone/src/components/view-width-toggle.tsx +0 -70
- package/.next/standalone/src/components/wiki/frontmatter-header.tsx +0 -164
- package/.next/standalone/src/hooks/use-is-mobile.ts +0 -14
- package/.next/standalone/src/lib/app-runner.ts +0 -272
- package/.next/standalone/src/lib/auth/admin.ts +0 -120
- package/.next/standalone/src/lib/auth/allowlist.ts +0 -50
- package/.next/standalone/src/lib/auth/client.ts +0 -4
- package/.next/standalone/src/lib/auth/csrf.ts +0 -68
- package/.next/standalone/src/lib/auth/server.ts +0 -215
- package/.next/standalone/src/lib/cabinets/tree.ts +0 -75
- package/.next/standalone/src/lib/config.ts +0 -114
- package/.next/standalone/src/lib/embeds/detect.ts +0 -142
- package/.next/standalone/src/lib/git-secrets.ts +0 -65
- package/.next/standalone/src/lib/git.ts +0 -503
- package/.next/standalone/src/lib/google/detect.ts +0 -107
- package/.next/standalone/src/lib/markdown/parse-frontmatter.ts +0 -104
- package/.next/standalone/src/lib/markdown/remark-wikilinks.ts +0 -100
- package/.next/standalone/src/lib/markdown/sanitize-schema.ts +0 -106
- package/.next/standalone/src/lib/markdown/to-html.ts +0 -282
- package/.next/standalone/src/lib/markdown/to-markdown.ts +0 -252
- package/.next/standalone/src/lib/proof/activity-shared.ts +0 -31
- package/.next/standalone/src/lib/proof/activity.ts +0 -74
- package/.next/standalone/src/lib/proof/audit.ts +0 -88
- package/.next/standalone/src/lib/proof/auth.ts +0 -143
- package/.next/standalone/src/lib/proof/block-refs.ts +0 -133
- package/.next/standalone/src/lib/proof/blocks.ts +0 -73
- package/.next/standalone/src/lib/proof/client-auth.ts +0 -23
- package/.next/standalone/src/lib/proof/collab-state.ts +0 -81
- package/.next/standalone/src/lib/proof/event-bus.ts +0 -47
- package/.next/standalone/src/lib/proof/file-lock.ts +0 -43
- package/.next/standalone/src/lib/proof/glob.ts +0 -51
- package/.next/standalone/src/lib/proof/idempotency.ts +0 -37
- package/.next/standalone/src/lib/proof/lease.ts +0 -117
- package/.next/standalone/src/lib/proof/mutex.ts +0 -36
- package/.next/standalone/src/lib/proof/ops-applier.ts +0 -727
- package/.next/standalone/src/lib/proof/owner-auth.ts +0 -2
- package/.next/standalone/src/lib/proof/pending.ts +0 -97
- package/.next/standalone/src/lib/proof/proof-span.ts +0 -141
- package/.next/standalone/src/lib/proof/rate-limit.ts +0 -53
- package/.next/standalone/src/lib/proof/raw-fs.ts +0 -197
- package/.next/standalone/src/lib/proof/register-rate-limit.ts +0 -53
- package/.next/standalone/src/lib/proof/registry.ts +0 -195
- package/.next/standalone/src/lib/proof/sidecar.ts +0 -103
- package/.next/standalone/src/lib/proof/suggest-capture.ts +0 -69
- package/.next/standalone/src/lib/proof/types.ts +0 -182
- package/.next/standalone/src/lib/proof-config.ts +0 -14
- package/.next/standalone/src/lib/root-dir.ts +0 -35
- package/.next/standalone/src/lib/search/indexable-exts.ts +0 -25
- package/.next/standalone/src/lib/search/indexer.ts +0 -634
- package/.next/standalone/src/lib/search/sanitize.ts +0 -35
- package/.next/standalone/src/lib/search/search-db.ts +0 -57
- package/.next/standalone/src/lib/search/watcher-pool.ts +0 -79
- package/.next/standalone/src/lib/shared-docs/db.ts +0 -177
- package/.next/standalone/src/lib/toast.ts +0 -9
- package/.next/standalone/src/lib/utils.ts +0 -6
- package/.next/standalone/src/lib/wiki-helpers.ts +0 -36
- package/.next/standalone/src/lib/workspace-client.ts +0 -82
- package/.next/standalone/src/lib/workspace-context.ts +0 -173
- package/.next/standalone/src/lib/workspaces.ts +0 -400
- package/.next/standalone/src/middleware.ts +0 -44
- package/.next/standalone/src/stores/ai-panel-store.ts +0 -79
- package/.next/standalone/src/stores/editor-store.ts +0 -439
- package/.next/standalone/src/stores/pin-store.ts +0 -56
- package/.next/standalone/src/stores/proof-store.ts +0 -124
- package/.next/standalone/src/stores/recent-store.ts +0 -51
- package/.next/standalone/src/stores/search-store.ts +0 -104
- package/.next/standalone/src/stores/show-hidden-store.ts +0 -26
- package/.next/standalone/src/stores/sidebar-width-store.ts +0 -39
- package/.next/standalone/src/stores/skin-store.ts +0 -42
- package/.next/standalone/src/stores/tree-store.ts +0 -52
- package/.next/standalone/src/stores/view-width-store.ts +0 -101
- package/.next/standalone/src/stores/wiki-slugs-store.ts +0 -75
- package/.next/standalone/src/tests/proof/activity-aggregator.test.ts +0 -146
- package/.next/standalone/src/tests/proof/admin-create-user.test.ts +0 -43
- package/.next/standalone/src/tests/proof/agent-fs.test.ts +0 -973
- package/.next/standalone/src/tests/proof/agent-ownership.test.ts +0 -140
- package/.next/standalone/src/tests/proof/agent-workspace-scope.test.ts +0 -134
- package/.next/standalone/src/tests/proof/agents-install.test.ts +0 -110
- package/.next/standalone/src/tests/proof/bench-write.ts +0 -179
- package/.next/standalone/src/tests/proof/better-auth.test.ts +0 -68
- package/.next/standalone/src/tests/proof/block-refs.test.ts +0 -108
- package/.next/standalone/src/tests/proof/blocks.test.ts +0 -92
- package/.next/standalone/src/tests/proof/collab-state.test.ts +0 -501
- package/.next/standalone/src/tests/proof/comments-ops.test.ts +0 -177
- package/.next/standalone/src/tests/proof/editor-roundtrip.test.ts +0 -85
- package/.next/standalone/src/tests/proof/external-edit.test.ts +0 -58
- package/.next/standalone/src/tests/proof/file-lock.test.ts +0 -80
- package/.next/standalone/src/tests/proof/git-branches-checkout.test.ts +0 -136
- package/.next/standalone/src/tests/proof/git-read-features.test.ts +0 -473
- package/.next/standalone/src/tests/proof/git-secrets.test.ts +0 -138
- package/.next/standalone/src/tests/proof/git-sparse.test.ts +0 -257
- package/.next/standalone/src/tests/proof/git-workspace.test.ts +0 -135
- package/.next/standalone/src/tests/proof/git.test.ts +0 -243
- package/.next/standalone/src/tests/proof/glob.test.ts +0 -82
- package/.next/standalone/src/tests/proof/helpers/auth-session.ts +0 -27
- package/.next/standalone/src/tests/proof/markdown-to-html.test.ts +0 -46
- package/.next/standalone/src/tests/proof/ops-applier.test.ts +0 -368
- package/.next/standalone/src/tests/proof/owner-init.test.ts +0 -2
- package/.next/standalone/src/tests/proof/parse-frontmatter.test.ts +0 -60
- package/.next/standalone/src/tests/proof/preload.ts +0 -21
- package/.next/standalone/src/tests/proof/proof-span.test.ts +0 -98
- package/.next/standalone/src/tests/proof/rate-limit.test.ts +0 -54
- package/.next/standalone/src/tests/proof/readonly-enforcement.test.ts +0 -161
- package/.next/standalone/src/tests/proof/reconcile-sidecar.test.ts +0 -195
- package/.next/standalone/src/tests/proof/registration-flow.test.ts +0 -330
- package/.next/standalone/src/tests/proof/registry.test.ts +0 -169
- package/.next/standalone/src/tests/proof/routes.test.ts +0 -516
- package/.next/standalone/src/tests/proof/search-index.test.ts +0 -376
- package/.next/standalone/src/tests/proof/sidecar-lifecycle.test.ts +0 -177
- package/.next/standalone/src/tests/proof/sidecar-trim.test.ts +0 -58
- package/.next/standalone/src/tests/proof/suggestion-ops.test.ts +0 -280
- package/.next/standalone/src/tests/proof/wiki-content-put.test.ts +0 -140
- package/.next/standalone/src/tests/proof/wiki-routes-auth.test.ts +0 -208
- package/.next/standalone/src/tests/proof/workspace-isolation.test.ts +0 -87
- package/.next/standalone/src/tests/proof/workspaces-api.test.ts +0 -227
- package/.next/standalone/src/types/index.ts +0 -47
- package/.next/standalone/tailwind.config.ts +0 -191
- package/.next/standalone/tsconfig.json +0 -41
- package/.next/standalone/tsconfig.tsbuildinfo +0 -1
- /package/.next/standalone/.next/static/{WMOTgfV82apmdovxzgbMx → rRyvFORvJoc5OWbCR2A_P}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{WMOTgfV82apmdovxzgbMx → rRyvFORvJoc5OWbCR2A_P}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{WMOTgfV82apmdovxzgbMx → rRyvFORvJoc5OWbCR2A_P}/_ssgManifest.js +0 -0
|
@@ -1,446 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tier-1 Raw FS — file read/write/delete.
|
|
3
|
-
*
|
|
4
|
-
* GET /api/agent/fs/file/<path> Read raw bytes (Range supported).
|
|
5
|
-
* PUT /api/agent/fs/file/<path> Atomic whole-file write.
|
|
6
|
-
* DELETE /api/agent/fs/file/<path> Delete file (+ sidecar for .md).
|
|
7
|
-
*/
|
|
8
|
-
export const runtime = "nodejs";
|
|
9
|
-
|
|
10
|
-
import { readFile, stat, unlink, rm } from "node:fs/promises";
|
|
11
|
-
import path from "node:path";
|
|
12
|
-
import { NextResponse } from "next/server";
|
|
13
|
-
import { checkAuth, enforceScope } from "@/lib/proof/auth";
|
|
14
|
-
import { resolveWorkspaceForAgent } from "@/lib/workspace-context";
|
|
15
|
-
import { safeWorkspacePath } from "@/lib/workspaces";
|
|
16
|
-
import { withFileMutex } from "@/lib/proof/mutex";
|
|
17
|
-
import { readSidecar, emptySidecar, deleteSidecar } from "@/lib/proof/sidecar";
|
|
18
|
-
import { reconcileSidecar } from "@/lib/proof/ops-applier";
|
|
19
|
-
import { writeAuditRow } from "@/lib/proof/audit";
|
|
20
|
-
import {
|
|
21
|
-
safeAbsPath,
|
|
22
|
-
sha256ofBuf,
|
|
23
|
-
extractShaHex,
|
|
24
|
-
mimeByExt,
|
|
25
|
-
isMarkdown,
|
|
26
|
-
atomicWrite,
|
|
27
|
-
ensureParentDir,
|
|
28
|
-
looksLikeBinary,
|
|
29
|
-
} from "@/lib/proof/raw-fs";
|
|
30
|
-
import { computeCollabState } from "@/lib/proof/collab-state";
|
|
31
|
-
|
|
32
|
-
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
33
|
-
|
|
34
|
-
function rel(segments: string[]): string {
|
|
35
|
-
return segments.join("/");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function errJson(code: string, message: string, status: number): NextResponse {
|
|
39
|
-
return NextResponse.json({ error: code, message }, { status });
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// ── GET ──────────────────────────────────────────────────────────────────────
|
|
43
|
-
|
|
44
|
-
export async function GET(
|
|
45
|
-
req: Request,
|
|
46
|
-
{ params }: { params: Promise<{ path: string[] }> },
|
|
47
|
-
): Promise<NextResponse> {
|
|
48
|
-
const auth = await checkAuth(req);
|
|
49
|
-
if (!auth.ok) return errJson("UNAUTHORIZED", auth.message ?? "Unauthorized", 401);
|
|
50
|
-
|
|
51
|
-
const { path: segments } = await params;
|
|
52
|
-
const relPath = rel(segments);
|
|
53
|
-
|
|
54
|
-
const wsx = await resolveWorkspaceForAgent(req);
|
|
55
|
-
if (!wsx.ok) return errJson(wsx.code, wsx.code, wsx.status);
|
|
56
|
-
const { ws, rootDir } = wsx;
|
|
57
|
-
|
|
58
|
-
const basic = safeWorkspacePath(rootDir, relPath);
|
|
59
|
-
if (!basic) return errJson("INVALID_PATH", "Path traversal rejected", 400);
|
|
60
|
-
|
|
61
|
-
const absPath = await safeAbsPath(rootDir, relPath);
|
|
62
|
-
if (!absPath) return errJson("INVALID_PATH", "Path rejected (symlink escape or denied)", 400);
|
|
63
|
-
|
|
64
|
-
const scope = enforceScope(auth.agent, { filePath: relPath, op: "read", workspaceId: ws.id });
|
|
65
|
-
if (!scope.ok) return errJson(scope.code, scope.message, 403);
|
|
66
|
-
|
|
67
|
-
let data: Buffer;
|
|
68
|
-
let fileStat: Awaited<ReturnType<typeof stat>>;
|
|
69
|
-
try {
|
|
70
|
-
[data, fileStat] = await Promise.all([readFile(absPath), stat(absPath)]);
|
|
71
|
-
} catch (e) {
|
|
72
|
-
if ((e as NodeJS.ErrnoException).code === "ENOENT") {
|
|
73
|
-
return errJson("NOT_FOUND", "File not found", 404);
|
|
74
|
-
}
|
|
75
|
-
throw e;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (fileStat.isDirectory()) {
|
|
79
|
-
return errJson("IS_DIRECTORY", "Path is a directory; use /api/agent/fs/ls/", 400);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const sha = sha256ofBuf(data);
|
|
83
|
-
const baseHeaders: Record<string, string> = {
|
|
84
|
-
"ETag": `"${sha}"`,
|
|
85
|
-
"X-File-Size": String(fileStat.size),
|
|
86
|
-
"X-File-Mtime": fileStat.mtime.toISOString(),
|
|
87
|
-
"Content-Type": mimeByExt(relPath),
|
|
88
|
-
"Accept-Ranges": "bytes",
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
// X-Collab-* headers (§3.5 mode signal)
|
|
92
|
-
const collab = await computeCollabState(rootDir, relPath);
|
|
93
|
-
if (collab.state !== "not-markdown") {
|
|
94
|
-
baseHeaders["X-Collab-State"] = collab.state;
|
|
95
|
-
baseHeaders["X-Collab-Revision"] = String(collab.revision);
|
|
96
|
-
baseHeaders["X-Collab-Snapshot"] = collab.snapshotUrl!;
|
|
97
|
-
} else {
|
|
98
|
-
baseHeaders["X-Collab-State"] = collab.state;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const rangeHeader = req.headers.get("range");
|
|
102
|
-
if (rangeHeader) {
|
|
103
|
-
const match = /^bytes=(\d+)-(\d*)$/.exec(rangeHeader);
|
|
104
|
-
if (!match) {
|
|
105
|
-
return new NextResponse("Invalid Range header", { status: 416 });
|
|
106
|
-
}
|
|
107
|
-
const start = parseInt(match[1]!, 10);
|
|
108
|
-
const endRaw = match[2] ? parseInt(match[2], 10) : data.length - 1;
|
|
109
|
-
if (start > endRaw || start >= data.length) {
|
|
110
|
-
return new NextResponse(null, {
|
|
111
|
-
status: 416,
|
|
112
|
-
headers: { "Content-Range": `bytes */${data.length}` },
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
const end = Math.min(endRaw, data.length - 1);
|
|
116
|
-
const slice = data.slice(start, end + 1);
|
|
117
|
-
return new NextResponse(slice as unknown as BodyInit, {
|
|
118
|
-
status: 206,
|
|
119
|
-
headers: {
|
|
120
|
-
...baseHeaders,
|
|
121
|
-
"Content-Range": `bytes ${start}-${end}/${data.length}`,
|
|
122
|
-
"Content-Length": String(slice.length),
|
|
123
|
-
},
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return new NextResponse(data as unknown as BodyInit, {
|
|
128
|
-
status: 200,
|
|
129
|
-
headers: { ...baseHeaders, "Content-Length": String(data.length) },
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// ── Shared mutation path (PUT + PATCH) ────────────────────────────────────────
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Single code path for all Tier-1 byte mutations so PUT and PATCH cannot drift
|
|
137
|
-
* on auth/scope/If-Match/R6/atomicWrite/reconcile/audit.
|
|
138
|
-
*
|
|
139
|
-
* `computeNewBody` receives the existing file buffer (or null if absent) and
|
|
140
|
-
* returns the new bytes, or an error response to short-circuit (e.g. PATCH
|
|
141
|
-
* match-count mismatch). `op` is the audit verb ("put" | "patch").
|
|
142
|
-
*/
|
|
143
|
-
async function applyMutation(
|
|
144
|
-
req: Request,
|
|
145
|
-
segments: string[],
|
|
146
|
-
opts: {
|
|
147
|
-
op: "put" | "patch";
|
|
148
|
-
allowCreate: boolean;
|
|
149
|
-
computeNewBody: (existing: Buffer | null) => Buffer | NextResponse;
|
|
150
|
-
},
|
|
151
|
-
): Promise<NextResponse> {
|
|
152
|
-
const auth = await checkAuth(req);
|
|
153
|
-
if (!auth.ok) return errJson("UNAUTHORIZED", auth.message ?? "Unauthorized", 401);
|
|
154
|
-
|
|
155
|
-
const wsx = await resolveWorkspaceForAgent(req, "write");
|
|
156
|
-
if (!wsx.ok) return errJson(wsx.code, wsx.code, wsx.status);
|
|
157
|
-
const { ws, rootDir } = wsx;
|
|
158
|
-
|
|
159
|
-
const relPath = rel(segments);
|
|
160
|
-
const url = new URL(req.url);
|
|
161
|
-
const mkdirs = url.searchParams.get("mkdirs") === "true";
|
|
162
|
-
const force = url.searchParams.get("force") === "true";
|
|
163
|
-
|
|
164
|
-
const basic = safeWorkspacePath(rootDir, relPath);
|
|
165
|
-
if (!basic) return errJson("INVALID_PATH", "Path traversal rejected", 400);
|
|
166
|
-
|
|
167
|
-
const absPath = await safeAbsPath(rootDir, relPath);
|
|
168
|
-
if (!absPath) return errJson("INVALID_PATH", "Path rejected (symlink escape or denied)", 400);
|
|
169
|
-
|
|
170
|
-
const scope = enforceScope(auth.agent, { filePath: relPath, op: "mutate", workspaceId: ws.id });
|
|
171
|
-
if (!scope.ok) return errJson(scope.code, scope.message, 403);
|
|
172
|
-
|
|
173
|
-
const ifMatch = req.headers.get("if-match");
|
|
174
|
-
const ifCollabMatch = req.headers.get("if-collab-match");
|
|
175
|
-
|
|
176
|
-
// The whole read-existing → precondition → compute → write → reconcile → audit
|
|
177
|
-
// sequence runs inside the mutex for .md (so R6 and reconcile are atomic with
|
|
178
|
-
// the write); non-.md runs it without the mutex.
|
|
179
|
-
const doMutation = async (): Promise<NextResponse> => {
|
|
180
|
-
// Read existing
|
|
181
|
-
let existingBuf: Buffer | null = null;
|
|
182
|
-
let existingSha: string | undefined;
|
|
183
|
-
try {
|
|
184
|
-
existingBuf = await readFile(absPath);
|
|
185
|
-
existingSha = sha256ofBuf(existingBuf);
|
|
186
|
-
} catch (e) {
|
|
187
|
-
if ((e as NodeJS.ErrnoException).code !== "ENOENT") throw e;
|
|
188
|
-
}
|
|
189
|
-
const existed = existingBuf !== null;
|
|
190
|
-
|
|
191
|
-
if (existed) {
|
|
192
|
-
// Overwrite: If-Match required by default (unless ?force=true)
|
|
193
|
-
if (!force && !ifMatch) {
|
|
194
|
-
return errJson(
|
|
195
|
-
"PRECONDITION_REQUIRED",
|
|
196
|
-
"If-Match header required for overwrites (use ?force=true to bypass with audit)",
|
|
197
|
-
412,
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
if (ifMatch && extractShaHex(ifMatch) !== extractShaHex(existingSha!)) {
|
|
201
|
-
return errJson("PRECONDITION_FAILED", "If-Match sha256 mismatch", 412);
|
|
202
|
-
}
|
|
203
|
-
} else {
|
|
204
|
-
if (!opts.allowCreate) {
|
|
205
|
-
return errJson("NOT_FOUND", "File not found", 404);
|
|
206
|
-
}
|
|
207
|
-
const parentOk = await ensureParentDir(absPath, mkdirs);
|
|
208
|
-
if (!parentOk) {
|
|
209
|
-
return errJson(
|
|
210
|
-
"PARENT_NOT_FOUND",
|
|
211
|
-
"Parent directory does not exist (use ?mkdirs=true to create)",
|
|
212
|
-
400,
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// R6: for .md, re-check collab state atomically (inside mutex)
|
|
218
|
-
if (isMarkdown(relPath)) {
|
|
219
|
-
const { state, revision, snapshotUrl } = await computeCollabState(rootDir, relPath);
|
|
220
|
-
if (state === "active") {
|
|
221
|
-
const matchVal = ifCollabMatch?.trim();
|
|
222
|
-
if (!force && (!matchVal || matchVal !== String(revision))) {
|
|
223
|
-
return NextResponse.json(
|
|
224
|
-
{
|
|
225
|
-
error: "COLLAB_ACTIVE",
|
|
226
|
-
message:
|
|
227
|
-
"File is in active collaborative session. Use block-ops (Tier-2) or supply a matching If-Collab-Match header. Use ?force=true to override (audited).",
|
|
228
|
-
snapshotUrl,
|
|
229
|
-
revision,
|
|
230
|
-
},
|
|
231
|
-
{ status: 409 },
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Compute new bytes (PUT: request body; PATCH: str-replace on existing)
|
|
238
|
-
const computed = opts.computeNewBody(existingBuf);
|
|
239
|
-
if (computed instanceof NextResponse) return computed;
|
|
240
|
-
const bodyBuf = computed;
|
|
241
|
-
const newSha = sha256ofBuf(bodyBuf);
|
|
242
|
-
|
|
243
|
-
await atomicWrite(absPath, bodyBuf);
|
|
244
|
-
|
|
245
|
-
if (isMarkdown(relPath)) {
|
|
246
|
-
const content = bodyBuf.toString("utf-8");
|
|
247
|
-
const sidecar = (await readSidecar(rootDir, relPath)) ?? emptySidecar(relPath);
|
|
248
|
-
await reconcileSidecar({
|
|
249
|
-
rootDir,
|
|
250
|
-
mdPath: relPath,
|
|
251
|
-
content,
|
|
252
|
-
sidecar,
|
|
253
|
-
by: auth.agent.id,
|
|
254
|
-
eventType: "file.rawWritten",
|
|
255
|
-
fingerprint: newSha,
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
writeAuditRow({
|
|
260
|
-
agentId: auth.agent.id,
|
|
261
|
-
op: opts.op,
|
|
262
|
-
path: relPath,
|
|
263
|
-
oldSha: existingSha,
|
|
264
|
-
newSha,
|
|
265
|
-
forced: force,
|
|
266
|
-
workspaceId: ws.id,
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
const st = await stat(absPath);
|
|
270
|
-
return NextResponse.json({
|
|
271
|
-
path: relPath,
|
|
272
|
-
sha256: newSha,
|
|
273
|
-
size: st.size,
|
|
274
|
-
mtime: st.mtime.toISOString(),
|
|
275
|
-
created: !existed,
|
|
276
|
-
});
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
return isMarkdown(relPath) ? withFileMutex(`${rootDir}\u0000${relPath}`, doMutation) : doMutation();
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// ── PUT ──────────────────────────────────────────────────────────────────────
|
|
283
|
-
|
|
284
|
-
export async function PUT(
|
|
285
|
-
req: Request,
|
|
286
|
-
{ params }: { params: Promise<{ path: string[] }> },
|
|
287
|
-
): Promise<NextResponse> {
|
|
288
|
-
const { path: segments } = await params;
|
|
289
|
-
const bodyBuf = Buffer.from(await req.arrayBuffer());
|
|
290
|
-
return applyMutation(req, segments, {
|
|
291
|
-
op: "put",
|
|
292
|
-
allowCreate: true,
|
|
293
|
-
computeNewBody: () => bodyBuf,
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// ── PATCH ─────────────────────────────────────────────────────────────────────
|
|
298
|
-
// Server-side str-replace so agents send only the change, not the whole file.
|
|
299
|
-
// Strict: exact substring (no regex), text/UTF-8 only, If-Match required,
|
|
300
|
-
// expectedOccurrences must match exactly (default 1). Shares applyMutation, so
|
|
301
|
-
// lock / R6 / reconcile / audit behave identically to PUT.
|
|
302
|
-
|
|
303
|
-
const MAX_PATCH_STRING = 1_000_000; // 1MB cap on find/replace strings
|
|
304
|
-
|
|
305
|
-
export async function PATCH(
|
|
306
|
-
req: Request,
|
|
307
|
-
{ params }: { params: Promise<{ path: string[] }> },
|
|
308
|
-
): Promise<NextResponse> {
|
|
309
|
-
const { path: segments } = await params;
|
|
310
|
-
|
|
311
|
-
let body: { find?: unknown; replace?: unknown; expectedOccurrences?: unknown };
|
|
312
|
-
try {
|
|
313
|
-
body = (await req.json()) as typeof body;
|
|
314
|
-
} catch {
|
|
315
|
-
return errJson("INVALID_PAYLOAD", "Body must be JSON {find, replace, expectedOccurrences?}", 400);
|
|
316
|
-
}
|
|
317
|
-
const find = body.find;
|
|
318
|
-
const replace = body.replace;
|
|
319
|
-
if (typeof find !== "string" || typeof replace !== "string") {
|
|
320
|
-
return errJson("INVALID_PAYLOAD", "find and replace must be strings", 400);
|
|
321
|
-
}
|
|
322
|
-
if (find.length === 0) {
|
|
323
|
-
return errJson("INVALID_PAYLOAD", "find must not be empty", 400);
|
|
324
|
-
}
|
|
325
|
-
if (find.length > MAX_PATCH_STRING || replace.length > MAX_PATCH_STRING) {
|
|
326
|
-
return errJson("PAYLOAD_TOO_LARGE", "find/replace exceeds 1MB limit", 413);
|
|
327
|
-
}
|
|
328
|
-
let expected = 1;
|
|
329
|
-
if (body.expectedOccurrences !== undefined) {
|
|
330
|
-
if (typeof body.expectedOccurrences !== "number" || !Number.isInteger(body.expectedOccurrences) || body.expectedOccurrences < 1) {
|
|
331
|
-
return errJson("INVALID_PAYLOAD", "expectedOccurrences must be a positive integer", 400);
|
|
332
|
-
}
|
|
333
|
-
expected = body.expectedOccurrences;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return applyMutation(req, segments, {
|
|
337
|
-
op: "patch",
|
|
338
|
-
allowCreate: false, // patch only edits existing files
|
|
339
|
-
computeNewBody: (existing) => {
|
|
340
|
-
if (existing === null) return errJson("NOT_FOUND", "File not found", 404);
|
|
341
|
-
if (looksLikeBinary(existing)) {
|
|
342
|
-
return errJson("UNSUPPORTED", "Cannot patch binary / non-text file", 415);
|
|
343
|
-
}
|
|
344
|
-
let text: string;
|
|
345
|
-
try {
|
|
346
|
-
text = new TextDecoder("utf-8", { fatal: true }).decode(existing);
|
|
347
|
-
} catch {
|
|
348
|
-
return errJson("UNSUPPORTED", "File is not valid UTF-8", 415);
|
|
349
|
-
}
|
|
350
|
-
// Count exact occurrences (no regex).
|
|
351
|
-
let count = 0;
|
|
352
|
-
let idx = text.indexOf(find);
|
|
353
|
-
while (idx !== -1) {
|
|
354
|
-
count++;
|
|
355
|
-
idx = text.indexOf(find, idx + find.length);
|
|
356
|
-
}
|
|
357
|
-
if (count !== expected) {
|
|
358
|
-
return NextResponse.json(
|
|
359
|
-
{
|
|
360
|
-
error: "MATCH_COUNT_MISMATCH",
|
|
361
|
-
message: `Expected ${expected} occurrence(s) of find, found ${count}. Re-read the file or adjust expectedOccurrences.`,
|
|
362
|
-
found: count,
|
|
363
|
-
expected,
|
|
364
|
-
},
|
|
365
|
-
{ status: 422 },
|
|
366
|
-
);
|
|
367
|
-
}
|
|
368
|
-
// Replace all (count == expected) occurrences, literally.
|
|
369
|
-
return Buffer.from(text.split(find).join(replace), "utf-8");
|
|
370
|
-
},
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// ── DELETE ───────────────────────────────────────────────────────────────────
|
|
375
|
-
|
|
376
|
-
export async function DELETE(
|
|
377
|
-
req: Request,
|
|
378
|
-
{ params }: { params: Promise<{ path: string[] }> },
|
|
379
|
-
): Promise<NextResponse> {
|
|
380
|
-
const auth = await checkAuth(req);
|
|
381
|
-
if (!auth.ok) return errJson("UNAUTHORIZED", auth.message ?? "Unauthorized", 401);
|
|
382
|
-
|
|
383
|
-
const { path: segments } = await params;
|
|
384
|
-
const relPath = rel(segments);
|
|
385
|
-
const url = new URL(req.url);
|
|
386
|
-
const recursive = url.searchParams.get("recursive") === "true";
|
|
387
|
-
|
|
388
|
-
const wsx = await resolveWorkspaceForAgent(req, "write");
|
|
389
|
-
if (!wsx.ok) return errJson(wsx.code, wsx.code, wsx.status);
|
|
390
|
-
const { ws, rootDir } = wsx;
|
|
391
|
-
|
|
392
|
-
const basic = safeWorkspacePath(rootDir, relPath);
|
|
393
|
-
if (!basic) return errJson("INVALID_PATH", "Path traversal rejected", 400);
|
|
394
|
-
|
|
395
|
-
const absPath = await safeAbsPath(rootDir, relPath);
|
|
396
|
-
if (!absPath) return errJson("INVALID_PATH", "Path rejected (symlink escape or denied)", 400);
|
|
397
|
-
|
|
398
|
-
// Requires "delete" scope op
|
|
399
|
-
const scope = enforceScope(auth.agent, { filePath: relPath, op: "delete", workspaceId: ws.id });
|
|
400
|
-
if (!scope.ok) return errJson(scope.code, scope.message, 403);
|
|
401
|
-
|
|
402
|
-
const ifMatch = req.headers.get("if-match");
|
|
403
|
-
|
|
404
|
-
let fileStat: Awaited<ReturnType<typeof stat>>;
|
|
405
|
-
try {
|
|
406
|
-
fileStat = await stat(absPath);
|
|
407
|
-
} catch (e) {
|
|
408
|
-
if ((e as NodeJS.ErrnoException).code === "ENOENT") {
|
|
409
|
-
return errJson("NOT_FOUND", "File not found", 404);
|
|
410
|
-
}
|
|
411
|
-
throw e;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
if (fileStat.isDirectory()) {
|
|
415
|
-
if (!recursive) {
|
|
416
|
-
return errJson("IS_DIRECTORY", "Use ?recursive=true to delete a directory", 400);
|
|
417
|
-
}
|
|
418
|
-
// Directory delete — no single sha, skip If-Match; require recursive flag as confirmation
|
|
419
|
-
await rm(absPath, { recursive: true, force: true });
|
|
420
|
-
writeAuditRow({ agentId: auth.agent.id, op: "delete", path: relPath, forced: false, workspaceId: ws.id });
|
|
421
|
-
return NextResponse.json({ deleted: relPath });
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// File: If-Match required as confirmation
|
|
425
|
-
if (!ifMatch) {
|
|
426
|
-
return errJson("PRECONDITION_REQUIRED", "If-Match header required for delete", 412);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
const existing = await readFile(absPath);
|
|
430
|
-
const existingSha = sha256ofBuf(existing);
|
|
431
|
-
if (extractShaHex(ifMatch) !== extractShaHex(existingSha)) {
|
|
432
|
-
return errJson("PRECONDITION_FAILED", "If-Match sha256 mismatch", 412);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
if (isMarkdown(relPath)) {
|
|
436
|
-
await withFileMutex(`${rootDir}\u0000${relPath}`, async () => {
|
|
437
|
-
await unlink(absPath);
|
|
438
|
-
await deleteSidecar(rootDir, relPath);
|
|
439
|
-
});
|
|
440
|
-
} else {
|
|
441
|
-
await unlink(absPath);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
writeAuditRow({ agentId: auth.agent.id, op: "delete", path: relPath, oldSha: existingSha, forced: false, workspaceId: ws.id });
|
|
445
|
-
return NextResponse.json({ deleted: relPath });
|
|
446
|
-
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tier-1 Raw FS — directory listing.
|
|
3
|
-
*
|
|
4
|
-
* GET /api/agent/fs/ls[/<path>]?recursive&limit=N&depth=N
|
|
5
|
-
*
|
|
6
|
-
* Returns {path, entries, truncated}.
|
|
7
|
-
* Each entry: {name, path, type, size?, mtime?}.
|
|
8
|
-
* Excludes .proof/ and .git/. Scope-filters every returned path.
|
|
9
|
-
*/
|
|
10
|
-
export const runtime = "nodejs";
|
|
11
|
-
|
|
12
|
-
import { readdir, stat } from "node:fs/promises";
|
|
13
|
-
import path from "node:path";
|
|
14
|
-
import { NextResponse } from "next/server";
|
|
15
|
-
import { checkAuth, enforceScope } from "@/lib/proof/auth";
|
|
16
|
-
import { resolveWorkspaceForAgent } from "@/lib/workspace-context";
|
|
17
|
-
import { safeWorkspacePath } from "@/lib/workspaces";
|
|
18
|
-
import { safeAbsPath } from "@/lib/proof/raw-fs";
|
|
19
|
-
import type { Agent } from "@/lib/proof/registry";
|
|
20
|
-
|
|
21
|
-
const HARD_MAX_ENTRIES = 10_000;
|
|
22
|
-
const HARD_MAX_DEPTH = 20;
|
|
23
|
-
|
|
24
|
-
export interface LsEntry {
|
|
25
|
-
name: string;
|
|
26
|
-
path: string;
|
|
27
|
-
type: "file" | "dir" | "symlink";
|
|
28
|
-
size?: number;
|
|
29
|
-
mtime?: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function errJson(code: string, message: string, status: number): NextResponse {
|
|
33
|
-
return NextResponse.json({ error: code, message }, { status });
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** Skip these names at any depth */
|
|
37
|
-
const SKIP_NAMES = new Set([".proof", ".git"]);
|
|
38
|
-
|
|
39
|
-
async function walkDir(
|
|
40
|
-
rootDir: string,
|
|
41
|
-
relDir: string,
|
|
42
|
-
agent: Agent,
|
|
43
|
-
recursive: boolean,
|
|
44
|
-
depth: number,
|
|
45
|
-
maxDepth: number,
|
|
46
|
-
limit: number,
|
|
47
|
-
results: LsEntry[],
|
|
48
|
-
workspaceId?: string,
|
|
49
|
-
): Promise<void> {
|
|
50
|
-
if (depth > maxDepth || results.length >= limit) return;
|
|
51
|
-
|
|
52
|
-
const absDir = path.join(rootDir, relDir);
|
|
53
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
|
-
let items: any[];
|
|
55
|
-
try {
|
|
56
|
-
items = await readdir(absDir, { withFileTypes: true }) as any[];
|
|
57
|
-
} catch {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
for (const item of items) {
|
|
62
|
-
if (results.length >= limit) break;
|
|
63
|
-
if (SKIP_NAMES.has(item.name)) continue;
|
|
64
|
-
|
|
65
|
-
const childRel = relDir ? `${relDir}/${item.name}` : item.name;
|
|
66
|
-
|
|
67
|
-
// Scope check per entry
|
|
68
|
-
const sc = enforceScope(agent, { filePath: childRel, op: "read", workspaceId });
|
|
69
|
-
if (!sc.ok) continue;
|
|
70
|
-
|
|
71
|
-
let type: LsEntry["type"];
|
|
72
|
-
let size: number | undefined;
|
|
73
|
-
let mtime: string | undefined;
|
|
74
|
-
|
|
75
|
-
if (item.isSymbolicLink()) {
|
|
76
|
-
type = "symlink";
|
|
77
|
-
} else if (item.isDirectory()) {
|
|
78
|
-
type = "dir";
|
|
79
|
-
} else {
|
|
80
|
-
type = "file";
|
|
81
|
-
try {
|
|
82
|
-
const st = await stat(path.join(rootDir, childRel));
|
|
83
|
-
size = st.size;
|
|
84
|
-
mtime = st.mtime.toISOString();
|
|
85
|
-
} catch {
|
|
86
|
-
// best-effort
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
results.push({ name: item.name, path: childRel, type, size, mtime });
|
|
91
|
-
|
|
92
|
-
if (recursive && type === "dir") {
|
|
93
|
-
await walkDir(rootDir, childRel, agent, recursive, depth + 1, maxDepth, limit, results, workspaceId);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export async function GET(
|
|
99
|
-
req: Request,
|
|
100
|
-
{ params }: { params: Promise<{ path?: string[] }> },
|
|
101
|
-
): Promise<NextResponse> {
|
|
102
|
-
const auth = await checkAuth(req);
|
|
103
|
-
if (!auth.ok) return errJson("UNAUTHORIZED", auth.message ?? "Unauthorized", 401);
|
|
104
|
-
|
|
105
|
-
const { path: segments } = await params;
|
|
106
|
-
const relPath = segments ? segments.join("/") : "";
|
|
107
|
-
|
|
108
|
-
const wsx = await resolveWorkspaceForAgent(req);
|
|
109
|
-
if (!wsx.ok) return errJson(wsx.code, wsx.code, wsx.status);
|
|
110
|
-
const { ws, rootDir } = wsx;
|
|
111
|
-
|
|
112
|
-
if (relPath) {
|
|
113
|
-
const basic = safeWorkspacePath(rootDir, relPath);
|
|
114
|
-
if (!basic) return errJson("INVALID_PATH", "Path traversal rejected", 400);
|
|
115
|
-
const safe = await safeAbsPath(rootDir, relPath);
|
|
116
|
-
if (!safe) return errJson("INVALID_PATH", "Path rejected (symlink escape or denied)", 400);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const url = new URL(req.url);
|
|
120
|
-
const recursive = url.searchParams.has("recursive");
|
|
121
|
-
const limit = Math.min(
|
|
122
|
-
parseInt(url.searchParams.get("limit") ?? "1000", 10) || 1000,
|
|
123
|
-
HARD_MAX_ENTRIES,
|
|
124
|
-
);
|
|
125
|
-
const depth = Math.min(
|
|
126
|
-
parseInt(url.searchParams.get("depth") ?? "10", 10) || 10,
|
|
127
|
-
HARD_MAX_DEPTH,
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
// Verify the dir actually exists
|
|
131
|
-
const absDir = relPath ? path.join(rootDir, relPath) : rootDir;
|
|
132
|
-
try {
|
|
133
|
-
const st = await stat(absDir);
|
|
134
|
-
if (!st.isDirectory()) {
|
|
135
|
-
return errJson("NOT_A_DIRECTORY", "Path is not a directory", 400);
|
|
136
|
-
}
|
|
137
|
-
} catch (e) {
|
|
138
|
-
if ((e as NodeJS.ErrnoException).code === "ENOENT") {
|
|
139
|
-
return errJson("NOT_FOUND", "Directory not found", 404);
|
|
140
|
-
}
|
|
141
|
-
throw e;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const entries: LsEntry[] = [];
|
|
145
|
-
await walkDir(rootDir, relPath, auth.agent, recursive, 0, depth, limit, entries, ws.id);
|
|
146
|
-
|
|
147
|
-
return NextResponse.json({
|
|
148
|
-
path: relPath,
|
|
149
|
-
entries,
|
|
150
|
-
truncated: entries.length >= limit,
|
|
151
|
-
});
|
|
152
|
-
}
|