@lapis-notes/api 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/LICENSE.md +4 -0
- package/README.md +43 -0
- package/dist/__tests__/data-adapter-conformance.js +205 -0
- package/dist/__tests__/lapis-runtime-manifest.js +32 -0
- package/dist/__tests__/svelte-internal-client.d.ts +3 -0
- package/dist/agent-result-views.d.ts +52 -0
- package/dist/agent-result-views.js +89 -0
- package/dist/agent-skills.d.ts +99 -0
- package/dist/agent-skills.js +182 -0
- package/dist/agent-tools.d.ts +92 -0
- package/dist/agent-tools.js +139 -0
- package/dist/app-database-host.d.ts +4 -0
- package/dist/app-database-host.js +4 -0
- package/dist/app-shell-plugin-persistence.d.ts +15 -0
- package/dist/app-shell-plugin-persistence.js +34 -0
- package/dist/app-url.d.ts +54 -0
- package/dist/app-url.js +269 -0
- package/dist/application-compatibility.d.ts +31 -0
- package/dist/application-compatibility.js +64 -0
- package/dist/application-state.svelte.d.ts +14 -0
- package/dist/application-state.svelte.js +30 -0
- package/dist/assets/lapis-logo.svg +471 -0
- package/dist/bases.d.ts +281 -0
- package/dist/bases.js +475 -0
- package/dist/cache.svelte.d.ts +569 -0
- package/dist/cache.svelte.js +1433 -0
- package/dist/command.svelte.d.ts +337 -0
- package/dist/command.svelte.js +571 -0
- package/dist/compat.d.ts +29 -0
- package/dist/compat.js +30 -0
- package/dist/components/configuration/configuration.css +199 -0
- package/dist/components/configuration/date-setting.svelte +71 -0
- package/dist/components/configuration/date-setting.svelte.d.ts +8 -0
- package/dist/components/configuration/list-setting.svelte +128 -0
- package/dist/components/configuration/list-setting.svelte.d.ts +13 -0
- package/dist/components/configuration/object-array-row-grip.svelte +78 -0
- package/dist/components/configuration/object-array-row-grip.svelte.d.ts +9 -0
- package/dist/components/configuration/object-array-row.svelte +163 -0
- package/dist/components/configuration/object-array-row.svelte.d.ts +19 -0
- package/dist/components/configuration/object-array-setting.svelte +196 -0
- package/dist/components/configuration/object-array-setting.svelte.d.ts +15 -0
- package/dist/components/configuration/object-array-table-dnd.d.ts +8 -0
- package/dist/components/configuration/object-array-table-dnd.js +10 -0
- package/dist/components/configuration/object-array-utils.d.ts +11 -0
- package/dist/components/configuration/object-array-utils.js +103 -0
- package/dist/components/configuration/object-grid-setting.svelte +79 -0
- package/dist/components/configuration/object-grid-setting.svelte.d.ts +10 -0
- package/dist/components/configuration/object-map-row.svelte +147 -0
- package/dist/components/configuration/object-map-row.svelte.d.ts +15 -0
- package/dist/components/configuration/object-map-setting.svelte +137 -0
- package/dist/components/configuration/object-map-setting.svelte.d.ts +14 -0
- package/dist/components/configuration/object-map-types.d.ts +5 -0
- package/dist/components/configuration/object-map-types.js +1 -0
- package/dist/components/configuration/options-combobox-setting.svelte +129 -0
- package/dist/components/configuration/options-combobox-setting.svelte.d.ts +12 -0
- package/dist/components/copy-button/button.svelte +93 -0
- package/dist/components/copy-button/button.svelte.d.ts +13 -0
- package/dist/components/copy-button/index.d.ts +2 -0
- package/dist/components/copy-button/index.js +2 -0
- package/dist/components/editor/editor.css +565 -0
- package/dist/components/editor/editor.d.ts +33 -0
- package/dist/components/editor/editor.js +132 -0
- package/dist/components/editor/editor.svelte +176 -0
- package/dist/components/editor/editor.svelte.d.ts +15 -0
- package/dist/components/editor/embedded-editor-surface.d.ts +12 -0
- package/dist/components/editor/embedded-editor-surface.js +22 -0
- package/dist/components/editor/embedded-editor-surface.svelte +115 -0
- package/dist/components/editor/embedded-editor-surface.svelte.d.ts +17 -0
- package/dist/components/editor/extensions/autocomplete/index.d.ts +2 -0
- package/dist/components/editor/extensions/autocomplete/index.js +2 -0
- package/dist/components/editor/extensions/autocomplete/lapis-code-mirror-autocomplete.d.ts +16 -0
- package/dist/components/editor/extensions/autocomplete/lapis-code-mirror-autocomplete.js +34 -0
- package/dist/components/editor/extensions/autocomplete/vault-completion-sources.d.ts +27 -0
- package/dist/components/editor/extensions/autocomplete/vault-completion-sources.js +55 -0
- package/dist/components/editor/extensions/class-highlighter/index.d.ts +1 -0
- package/dist/components/editor/extensions/class-highlighter/index.js +47 -0
- package/dist/components/editor/extensions/fold-gutter/index.d.ts +8 -0
- package/dist/components/editor/extensions/fold-gutter/index.js +40 -0
- package/dist/components/editor/extensions/gutter-line-style-sync/index.d.ts +2 -0
- package/dist/components/editor/extensions/gutter-line-style-sync/index.js +118 -0
- package/dist/components/editor/extensions/lint/index.d.ts +4 -0
- package/dist/components/editor/extensions/lint/index.js +4 -0
- package/dist/components/editor/extensions/lint/lapis-code-mirror-lint.d.ts +26 -0
- package/dist/components/editor/extensions/lint/lapis-code-mirror-lint.js +101 -0
- package/dist/components/editor/extensions/lint/lapis-lint-diagnostic-helpers.d.ts +8 -0
- package/dist/components/editor/extensions/lint/lapis-lint-diagnostic-helpers.js +7 -0
- package/dist/components/editor/extensions/lint/lapis-lint-diagnostic.d.ts +20 -0
- package/dist/components/editor/extensions/lint/lapis-lint-diagnostic.js +47 -0
- package/dist/components/editor/extensions/lint/lapis-lint-hover-tooltip.d.ts +9 -0
- package/dist/components/editor/extensions/lint/lapis-lint-hover-tooltip.js +373 -0
- package/dist/components/editor/extensions/lint/lapis-lint-inline-widget.d.ts +31 -0
- package/dist/components/editor/extensions/lint/lapis-lint-inline-widget.js +191 -0
- package/dist/components/editor/extensions/lint/lapis-lint-tooltip.svelte +176 -0
- package/dist/components/editor/extensions/lint/lapis-lint-tooltip.svelte.d.ts +15 -0
- package/dist/components/editor/extensions/lint/lint-tooltip-actions.d.ts +11 -0
- package/dist/components/editor/extensions/lint/lint-tooltip-actions.js +21 -0
- package/dist/components/editor/extensions/lint/mount-lint-tooltip.d.ts +16 -0
- package/dist/components/editor/extensions/lint/mount-lint-tooltip.js +60 -0
- package/dist/components/editor/extensions/search/dom.d.ts +2 -0
- package/dist/components/editor/extensions/search/dom.js +6 -0
- package/dist/components/editor/extensions/search/index.d.ts +35 -0
- package/dist/components/editor/extensions/search/index.js +44 -0
- package/dist/components/editor/extensions/search/search.svelte +335 -0
- package/dist/components/editor/extensions/search/search.svelte.d.ts +8 -0
- package/dist/components/editor/index.d.ts +9 -0
- package/dist/components/editor/index.js +9 -0
- package/dist/components/editor/language-service/dom.d.ts +1 -0
- package/dist/components/editor/language-service/dom.js +6 -0
- package/dist/components/editor/language-service/index.d.ts +28 -0
- package/dist/components/editor/language-service/index.js +342 -0
- package/dist/components/empty-view/empty-view.css +39 -0
- package/dist/components/empty-view/empty-view.svelte +26 -0
- package/dist/components/empty-view/empty-view.svelte.d.ts +9 -0
- package/dist/components/empty-view/index.d.ts +2 -0
- package/dist/components/empty-view/index.js +2 -0
- package/dist/components/icon/icon-list.css +74 -0
- package/dist/components/icon/icon-list.svelte +213 -0
- package/dist/components/icon/icon-list.svelte.d.ts +11 -0
- package/dist/components/icon/icon.svelte +15 -0
- package/dist/components/icon/icon.svelte.d.ts +7 -0
- package/dist/components/icon/index.d.ts +3 -0
- package/dist/components/icon/index.js +3 -0
- package/dist/components/menu/context-menu.svelte +89 -0
- package/dist/components/menu/context-menu.svelte.d.ts +11 -0
- package/dist/components/menu/drawer-menu.svelte +83 -0
- package/dist/components/menu/drawer-menu.svelte.d.ts +8 -0
- package/dist/components/menu/dropdown-menu.svelte +89 -0
- package/dist/components/menu/dropdown-menu.svelte.d.ts +11 -0
- package/dist/components/menu/index.d.ts +6 -0
- package/dist/components/menu/index.js +6 -0
- package/dist/components/menu/menu.css +139 -0
- package/dist/components/menu/menu.svelte +37 -0
- package/dist/components/menu/menu.svelte.d.ts +11 -0
- package/dist/components/menu/mobile-menu-query.svelte.d.ts +3 -0
- package/dist/components/menu/mobile-menu-query.svelte.js +21 -0
- package/dist/components/menu/popover-menu.svelte +90 -0
- package/dist/components/menu/popover-menu.svelte.d.ts +10 -0
- package/dist/components/select/index.d.ts +2 -0
- package/dist/components/select/index.js +2 -0
- package/dist/components/select/select.svelte +72 -0
- package/dist/components/select/select.svelte.d.ts +25 -0
- package/dist/configuration-option-source-providers.d.ts +17 -0
- package/dist/configuration-option-source-providers.js +59 -0
- package/dist/configuration-option-source-registry.d.ts +59 -0
- package/dist/configuration-option-source-registry.js +123 -0
- package/dist/configuration.svelte.d.ts +258 -0
- package/dist/configuration.svelte.js +887 -0
- package/dist/context-keys.svelte.d.ts +42 -0
- package/dist/context-keys.svelte.js +278 -0
- package/dist/context.svelte.d.ts +247 -0
- package/dist/context.svelte.js +540 -0
- package/dist/daily-document-provider.d.ts +38 -0
- package/dist/daily-document-provider.js +176 -0
- package/dist/design-core-button-types.d.ts +7 -0
- package/dist/design-core-button-types.js +1 -0
- package/dist/diagnostics.d.ts +93 -0
- package/dist/diagnostics.js +84 -0
- package/dist/dialog-portal.d.ts +3 -0
- package/dist/dialog-portal.js +3 -0
- package/dist/docs-api.d.ts +44 -0
- package/dist/docs-api.js +29 -0
- package/dist/editor-view-registry.d.ts +35 -0
- package/dist/editor-view-registry.js +112 -0
- package/dist/editor.svelte.d.ts +126 -0
- package/dist/editor.svelte.js +454 -0
- package/dist/embed-registry.d.ts +15 -0
- package/dist/embed-registry.js +16 -0
- package/dist/enhance.d.ts +111 -0
- package/dist/enhance.js +309 -0
- package/dist/events.d.ts +28 -0
- package/dist/events.js +49 -0
- package/dist/file-manager.d.ts +90 -0
- package/dist/file-manager.js +289 -0
- package/dist/file-tools/app-tools.d.ts +3 -0
- package/dist/file-tools/app-tools.js +242 -0
- package/dist/file-tools/index.d.ts +3 -0
- package/dist/file-tools/index.js +3 -0
- package/dist/file-tools/paths.d.ts +14 -0
- package/dist/file-tools/paths.js +33 -0
- package/dist/file-tools/vault-operations.d.ts +3 -0
- package/dist/file-tools/vault-operations.js +60 -0
- package/dist/generated/plugin-host-modules.schema.generated.d.ts +371 -0
- package/dist/generated/plugin-host-modules.schema.generated.js +374 -0
- package/dist/glob.d.ts +15 -0
- package/dist/glob.js +68 -0
- package/dist/history.svelte.d.ts +24 -0
- package/dist/history.svelte.js +74 -0
- package/dist/hooks/createVirtualizer.svelte.d.ts +7 -0
- package/dist/hooks/createVirtualizer.svelte.js +70 -0
- package/dist/hooks/index.d.ts +5 -0
- package/dist/hooks/index.js +5 -0
- package/dist/hooks/is-mobile.svelte.d.ts +5 -0
- package/dist/hooks/is-mobile.svelte.js +23 -0
- package/dist/hooks/mountComponent.svelte.d.ts +7 -0
- package/dist/hooks/mountComponent.svelte.js +16 -0
- package/dist/hooks/use-text-highlight.d.ts +17 -0
- package/dist/hooks/use-text-highlight.js +82 -0
- package/dist/hooks/useBoundingRect.svelte.d.ts +11 -0
- package/dist/hooks/useBoundingRect.svelte.js +46 -0
- package/dist/icons/index.d.ts +57 -0
- package/dist/icons/index.js +288 -0
- package/dist/index.d.ts +87 -0
- package/dist/index.js +87 -0
- package/dist/language-service/index.d.ts +9 -0
- package/dist/language-service/index.js +7 -0
- package/dist/language-service/manager.d.ts +47 -0
- package/dist/language-service/manager.js +404 -0
- package/dist/language-service/types.d.ts +92 -0
- package/dist/language-service/types.js +1 -0
- package/dist/language-service/virtual-document.d.ts +11 -0
- package/dist/language-service/virtual-document.js +45 -0
- package/dist/language-service/worker-protocol.d.ts +63 -0
- package/dist/language-service/worker-protocol.js +1 -0
- package/dist/language-service/worker-provider.d.ts +9 -0
- package/dist/language-service/worker-provider.js +38 -0
- package/dist/lapis-extension.d.ts +244 -0
- package/dist/lapis-extension.js +591 -0
- package/dist/links.d.ts +34 -0
- package/dist/links.js +191 -0
- package/dist/loaders.d.ts +9 -0
- package/dist/loaders.js +59 -0
- package/dist/localization-manager.svelte.d.ts +56 -0
- package/dist/localization-manager.svelte.js +157 -0
- package/dist/logging.d.ts +172 -0
- package/dist/logging.js +352 -0
- package/dist/markdown-extension-registry.d.ts +31 -0
- package/dist/markdown-extension-registry.js +31 -0
- package/dist/markdown-file-surface-registry.d.ts +25 -0
- package/dist/markdown-file-surface-registry.js +23 -0
- package/dist/markdown.d.ts +205 -0
- package/dist/markdown.js +147 -0
- package/dist/menu-utils.d.ts +1 -0
- package/dist/menu-utils.js +47 -0
- package/dist/menu.svelte.d.ts +121 -0
- package/dist/menu.svelte.js +295 -0
- package/dist/metadata-value.d.ts +10 -0
- package/dist/metadata-value.js +173 -0
- package/dist/metadata.svelte.d.ts +106 -0
- package/dist/metadata.svelte.js +608 -0
- package/dist/notifications.d.ts +209 -0
- package/dist/notifications.js +338 -0
- package/dist/open-file.d.ts +23 -0
- package/dist/open-file.js +102 -0
- package/dist/platform.d.ts +86 -0
- package/dist/platform.js +86 -0
- package/dist/plugin-asset-server.d.ts +60 -0
- package/dist/plugin-asset-server.js +222 -0
- package/dist/plugin-assets.d.ts +4 -0
- package/dist/plugin-assets.js +3 -0
- package/dist/plugin-capability-facade.d.ts +104 -0
- package/dist/plugin-capability-facade.js +61 -0
- package/dist/plugin-dependency-resolver.d.ts +36 -0
- package/dist/plugin-dependency-resolver.js +54 -0
- package/dist/plugin-dependency-scanner.d.ts +9 -0
- package/dist/plugin-dependency-scanner.js +274 -0
- package/dist/plugin-distribution/canonical-json.d.ts +1 -0
- package/dist/plugin-distribution/canonical-json.js +40 -0
- package/dist/plugin-distribution/compatibility.d.ts +5 -0
- package/dist/plugin-distribution/compatibility.js +38 -0
- package/dist/plugin-distribution/errors.d.ts +11 -0
- package/dist/plugin-distribution/errors.js +13 -0
- package/dist/plugin-distribution/hashes.d.ts +2 -0
- package/dist/plugin-distribution/hashes.js +17 -0
- package/dist/plugin-distribution/index.d.ts +18 -0
- package/dist/plugin-distribution/index.js +18 -0
- package/dist/plugin-distribution/installed-plugin-state.d.ts +20 -0
- package/dist/plugin-distribution/installed-plugin-state.js +73 -0
- package/dist/plugin-distribution/installer.d.ts +89 -0
- package/dist/plugin-distribution/installer.js +395 -0
- package/dist/plugin-distribution/manager.d.ts +128 -0
- package/dist/plugin-distribution/manager.js +358 -0
- package/dist/plugin-distribution/path-safety.d.ts +2 -0
- package/dist/plugin-distribution/path-safety.js +20 -0
- package/dist/plugin-distribution/plugin-bundle-verifier.d.ts +28 -0
- package/dist/plugin-distribution/plugin-bundle-verifier.js +96 -0
- package/dist/plugin-distribution/plugin-bundle-worker.d.ts +34 -0
- package/dist/plugin-distribution/plugin-bundle-worker.js +99 -0
- package/dist/plugin-distribution/plugin-bundle.d.ts +16 -0
- package/dist/plugin-distribution/plugin-bundle.js +244 -0
- package/dist/plugin-distribution/plugin-bundle.worker.d.ts +1 -0
- package/dist/plugin-distribution/plugin-bundle.worker.js +38 -0
- package/dist/plugin-distribution/registry-cache.d.ts +26 -0
- package/dist/plugin-distribution/registry-cache.js +85 -0
- package/dist/plugin-distribution/registry-client.d.ts +36 -0
- package/dist/plugin-distribution/registry-client.js +115 -0
- package/dist/plugin-distribution/reserved-ids.d.ts +4 -0
- package/dist/plugin-distribution/reserved-ids.js +10 -0
- package/dist/plugin-distribution/runtime-validation.d.ts +14 -0
- package/dist/plugin-distribution/runtime-validation.js +388 -0
- package/dist/plugin-distribution/schemas.d.ts +4895 -0
- package/dist/plugin-distribution/schemas.js +299 -0
- package/dist/plugin-distribution/signing.d.ts +5 -0
- package/dist/plugin-distribution/signing.js +38 -0
- package/dist/plugin-distribution/types.d.ts +217 -0
- package/dist/plugin-distribution/types.js +1 -0
- package/dist/plugin-install-progress.d.ts +15 -0
- package/dist/plugin-install-progress.js +86 -0
- package/dist/plugin-manager.d.ts +306 -0
- package/dist/plugin-manager.js +2848 -0
- package/dist/plugin-runtime-entry.d.ts +19 -0
- package/dist/plugin-runtime-entry.js +135 -0
- package/dist/plugin.d.ts +350 -0
- package/dist/plugin.js +754 -0
- package/dist/popover.d.ts +20 -0
- package/dist/popover.js +32 -0
- package/dist/promise-worker.d.ts +22 -0
- package/dist/promise-worker.js +65 -0
- package/dist/prompt-confirm.d.ts +9 -0
- package/dist/prompt-confirm.js +33 -0
- package/dist/search-document-provider.d.ts +45 -0
- package/dist/search-document-provider.js +54 -0
- package/dist/search-query/index.d.ts +78 -0
- package/dist/search-query/index.js +426 -0
- package/dist/search-query/parser.d.ts +3 -0
- package/dist/search-query/parser.js +16 -0
- package/dist/search-query/parser.terms.js +26 -0
- package/dist/search-query/search-query.grammar +128 -0
- package/dist/search.d.ts +19 -0
- package/dist/search.js +104 -0
- package/dist/setting-section-layout.d.ts +19 -0
- package/dist/setting-section-layout.js +194 -0
- package/dist/settings.svelte.d.ts +474 -0
- package/dist/settings.svelte.js +1421 -0
- package/dist/source-text-file-view.d.ts +25 -0
- package/dist/source-text-file-view.js +78 -0
- package/dist/status-bar.svelte.d.ts +29 -0
- package/dist/status-bar.svelte.js +72 -0
- package/dist/storage/app-database.d.ts +860 -0
- package/dist/storage/app-database.js +2372 -0
- package/dist/storage/blob-store.d.ts +12 -0
- package/dist/storage/blob-store.js +1 -0
- package/dist/storage/browser-app-database-coordination.d.ts +35 -0
- package/dist/storage/browser-app-database-coordination.js +207 -0
- package/dist/storage/browser-coordinated-app-database.d.ts +109 -0
- package/dist/storage/browser-coordinated-app-database.js +704 -0
- package/dist/storage/browser.d.ts +153 -0
- package/dist/storage/browser.js +794 -0
- package/dist/storage/desktop-native.d.ts +184 -0
- package/dist/storage/desktop-native.js +415 -0
- package/dist/storage/fs.d.ts +188 -0
- package/dist/storage/fs.js +307 -0
- package/dist/storage/index-projection.d.ts +236 -0
- package/dist/storage/index-projection.js +402 -0
- package/dist/storage/index.d.ts +17 -0
- package/dist/storage/index.js +17 -0
- package/dist/storage/keyval.d.ts +94 -0
- package/dist/storage/keyval.js +199 -0
- package/dist/storage/memory-vault-adapter.d.ts +44 -0
- package/dist/storage/memory-vault-adapter.js +351 -0
- package/dist/storage/native-vault-bootstrap-kv.d.ts +22 -0
- package/dist/storage/native-vault-bootstrap-kv.js +96 -0
- package/dist/storage/path.d.ts +22 -0
- package/dist/storage/path.js +110 -0
- package/dist/storage/search-embedding-provider.d.ts +75 -0
- package/dist/storage/search-embedding-provider.js +446 -0
- package/dist/storage/search-embedding-worker.d.ts +1 -0
- package/dist/storage/search-embedding-worker.js +63 -0
- package/dist/storage/search-query-evaluator.d.ts +14 -0
- package/dist/storage/search-query-evaluator.js +678 -0
- package/dist/storage/task-projection.d.ts +152 -0
- package/dist/storage/task-projection.js +154 -0
- package/dist/storage/turso-app-database.d.ts +150 -0
- package/dist/storage/turso-app-database.js +2671 -0
- package/dist/storage/vault-session.d.ts +37 -0
- package/dist/storage/vault-session.js +196 -0
- package/dist/storage/vault-state.d.ts +74 -0
- package/dist/storage/vault-state.js +181 -0
- package/dist/storage/vault.svelte.d.ts +257 -0
- package/dist/storage/vault.svelte.js +661 -0
- package/dist/suggest.d.ts +193 -0
- package/dist/suggest.js +391 -0
- package/dist/tasks.d.ts +7 -0
- package/dist/tasks.js +18 -0
- package/dist/telemetry.d.ts +116 -0
- package/dist/telemetry.js +103 -0
- package/dist/utils.d.ts +55 -0
- package/dist/utils.js +314 -0
- package/dist/vault-api.d.ts +5 -0
- package/dist/vault-api.js +3 -0
- package/dist/vault-maintenance-commands.d.ts +5 -0
- package/dist/vault-maintenance-commands.js +32 -0
- package/dist/view.svelte.d.ts +215 -0
- package/dist/view.svelte.js +381 -0
- package/dist/vite-env.d.ts +2 -0
- package/dist/workspace-host-internal.d.ts +7 -0
- package/dist/workspace-host-internal.js +7 -0
- package/dist/workspace-host.d.ts +10 -0
- package/dist/workspace-host.js +14 -0
- package/dist/workspace-layout-commands.d.ts +3 -0
- package/dist/workspace-layout-commands.js +72 -0
- package/dist/workspace-layout-normalizer.d.ts +92 -0
- package/dist/workspace-layout-normalizer.js +301 -0
- package/dist/workspace-layouts.d.ts +7 -0
- package/dist/workspace-layouts.js +23 -0
- package/dist/workspace-trust.d.ts +32 -0
- package/dist/workspace-trust.js +72 -0
- package/dist/workspace.svelte.d.ts +796 -0
- package/dist/workspace.svelte.js +4380 -0
- package/package.json +240 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { getFrontMatterInfo } from "./cache.svelte";
|
|
2
|
+
import { TFile } from "./storage/fs";
|
|
3
|
+
import { joinPath } from "./storage/path";
|
|
4
|
+
import { defaultLinkSettings, generateInternalLink, } from "./links";
|
|
5
|
+
import { parseLinktext } from "./utils";
|
|
6
|
+
const FRONTMATTER_RETRY_ERROR = "__lapis_frontmatter_retry__";
|
|
7
|
+
function linkOffsets(position) {
|
|
8
|
+
if (!position || typeof position !== "object")
|
|
9
|
+
return null;
|
|
10
|
+
const value = position;
|
|
11
|
+
return typeof value.start?.offset === "number" &&
|
|
12
|
+
typeof value.end?.offset === "number"
|
|
13
|
+
? { start: value.start.offset, end: value.end.offset }
|
|
14
|
+
: null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* High-level helpers for vault file operations that need metadata-aware
|
|
18
|
+
* follow-up work.
|
|
19
|
+
*
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
export class FileManager {
|
|
23
|
+
app;
|
|
24
|
+
constructor(app) {
|
|
25
|
+
this.app = app;
|
|
26
|
+
}
|
|
27
|
+
cloneFrontmatter(frontmatter) {
|
|
28
|
+
return JSON.parse(JSON.stringify(frontmatter));
|
|
29
|
+
}
|
|
30
|
+
async readFrontmatterSeed(file, content) {
|
|
31
|
+
const cachedFrontmatter = (await this.app.metadataCache.getFileCacheAsync(file))?.frontmatter;
|
|
32
|
+
if (cachedFrontmatter && typeof cachedFrontmatter === "object") {
|
|
33
|
+
return this.cloneFrontmatter(cachedFrontmatter);
|
|
34
|
+
}
|
|
35
|
+
const fileContent = content ?? (await this.app.vault.read(file));
|
|
36
|
+
const metadata = await this.app.metadataCache.read(fileContent, file);
|
|
37
|
+
if (metadata?.frontmatter && typeof metadata.frontmatter === "object") {
|
|
38
|
+
return this.cloneFrontmatter(metadata.frontmatter);
|
|
39
|
+
}
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
isFrontmatterEqual(left, right) {
|
|
43
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
44
|
+
}
|
|
45
|
+
writeUpdatedFrontmatter(file, data, frontmatter) {
|
|
46
|
+
const info = getFrontMatterInfo(data);
|
|
47
|
+
const hasEntries = Object.keys(frontmatter).length > 0;
|
|
48
|
+
if (!hasEntries) {
|
|
49
|
+
return info.exists ? data.slice(info.contentStart) : data;
|
|
50
|
+
}
|
|
51
|
+
const updated = this.app.metadataCache.writeFrontmatter(file, frontmatter);
|
|
52
|
+
const block = `---\n${updated}\n---\n`;
|
|
53
|
+
if (info.exists) {
|
|
54
|
+
return `${data.slice(0, info.from)}${block}${data.slice(info.contentStart)}`;
|
|
55
|
+
}
|
|
56
|
+
return `${block}${data}`;
|
|
57
|
+
}
|
|
58
|
+
getLinkSettings() {
|
|
59
|
+
const config = this.app.configuration.getConfiguration();
|
|
60
|
+
return {
|
|
61
|
+
newLinkFormat: config.get("files.links.newLinkFormat", defaultLinkSettings.newLinkFormat),
|
|
62
|
+
useWikilinks: config.get("files.links.useWikilinks", defaultLinkSettings.useWikilinks),
|
|
63
|
+
omitMarkdownExtension: config.get("files.links.omitMarkdownExtension", defaultLinkSettings.omitMarkdownExtension),
|
|
64
|
+
useShortestUniqueSuffix: config.get("files.links.useShortestUniqueSuffix", defaultLinkSettings.useShortestUniqueSuffix),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
buildVaultIndex() {
|
|
68
|
+
return {
|
|
69
|
+
getFiles: () => this.app.vault.getFiles().map((file) => ({
|
|
70
|
+
path: file.path,
|
|
71
|
+
basename: file.basename,
|
|
72
|
+
extension: file.extension,
|
|
73
|
+
})),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
getReplacementLink(sourcePath, targetPath, originalLink, originalText) {
|
|
77
|
+
const parsed = parseLinktext(originalLink);
|
|
78
|
+
const isEmbed = originalText.startsWith("!");
|
|
79
|
+
const alias = this.extractAlias(originalText);
|
|
80
|
+
return generateInternalLink({
|
|
81
|
+
targetPath,
|
|
82
|
+
sourcePath,
|
|
83
|
+
alias,
|
|
84
|
+
embed: isEmbed,
|
|
85
|
+
heading: parsed.subpath?.startsWith("^") ? undefined : parsed.subpath,
|
|
86
|
+
blockId: parsed.subpath?.startsWith("^")
|
|
87
|
+
? parsed.subpath.slice(1)
|
|
88
|
+
: undefined,
|
|
89
|
+
settings: this.getLinkSettings(),
|
|
90
|
+
vaultIndex: this.buildVaultIndex(),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
extractAlias(originalText) {
|
|
94
|
+
if (originalText.startsWith("[[") || originalText.startsWith("![[")) {
|
|
95
|
+
const body = originalText.replace(/^!?\[\[/, "").replace(/\]\]$/, "");
|
|
96
|
+
const separator = body.indexOf("|");
|
|
97
|
+
if (separator === -1) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
const alias = body.slice(separator + 1).trim();
|
|
101
|
+
return alias.length ? alias : undefined;
|
|
102
|
+
}
|
|
103
|
+
const markdownMatch = /^!?\[(.*)]\((.*)\)$/.exec(originalText);
|
|
104
|
+
if (!markdownMatch) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
const alias = markdownMatch[1]?.trim();
|
|
108
|
+
if (!alias.length) {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
return alias;
|
|
112
|
+
}
|
|
113
|
+
async collectRenameEdits(file, newPath) {
|
|
114
|
+
void newPath;
|
|
115
|
+
const editsBySource = new Map();
|
|
116
|
+
const refs = await this.app.metadataCache.queryLinks({
|
|
117
|
+
direction: "incoming",
|
|
118
|
+
path: file.path,
|
|
119
|
+
resolution: "resolved",
|
|
120
|
+
limit: 100_000,
|
|
121
|
+
});
|
|
122
|
+
for (const ref of refs) {
|
|
123
|
+
const offsets = linkOffsets(ref.position);
|
|
124
|
+
if (!offsets)
|
|
125
|
+
continue;
|
|
126
|
+
const sourceEdits = editsBySource.get(ref.sourcePath) ?? [];
|
|
127
|
+
sourceEdits.push({
|
|
128
|
+
start: offsets.start,
|
|
129
|
+
end: offsets.end,
|
|
130
|
+
link: ref.targetText,
|
|
131
|
+
original: ref.original ?? ref.targetText,
|
|
132
|
+
});
|
|
133
|
+
editsBySource.set(ref.sourcePath, sourceEdits);
|
|
134
|
+
}
|
|
135
|
+
return [...editsBySource.entries()].map(([sourcePath, refs]) => ({
|
|
136
|
+
sourcePath,
|
|
137
|
+
refs: refs.sort((left, right) => right.start - left.start),
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Gets the folder that new files should be saved to, given the user's
|
|
142
|
+
* preferences.
|
|
143
|
+
*
|
|
144
|
+
* @param sourcePath - The path to the current open/focused file, used when
|
|
145
|
+
* the user wants new files to be created 'in the same folder'. Use an empty
|
|
146
|
+
* string if there is no active file.
|
|
147
|
+
* @param newFilePath - The path to the file that will be newly created, used
|
|
148
|
+
* to infer what settings to use based on the path's extension.
|
|
149
|
+
* @public
|
|
150
|
+
*/
|
|
151
|
+
getNewFileParent(sourcePath, newFilePath) {
|
|
152
|
+
return this.app.vault.getRoot();
|
|
153
|
+
}
|
|
154
|
+
getAvailablePathForAttachment(filename, sourcePath) {
|
|
155
|
+
const parent = this.getNewFileParent(sourcePath ?? "", filename);
|
|
156
|
+
const extMatch = /(\.[^.]*)$/.exec(filename);
|
|
157
|
+
const stem = extMatch ? filename.slice(0, -extMatch[1].length) : filename;
|
|
158
|
+
const ext = extMatch?.[1] ?? "";
|
|
159
|
+
let index = 0;
|
|
160
|
+
let path = joinPath(parent.path, filename);
|
|
161
|
+
while (this.app.vault.getAbstractFileByPath(path)) {
|
|
162
|
+
index += 1;
|
|
163
|
+
path = joinPath(parent.path, `${stem} ${index}${ext}`);
|
|
164
|
+
}
|
|
165
|
+
return path;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Rename or move a file safely, and update all links to it depending on the
|
|
169
|
+
* user's preferences.
|
|
170
|
+
*
|
|
171
|
+
* @param file - The file to rename
|
|
172
|
+
* @param newPath - The new path for the file
|
|
173
|
+
* @public
|
|
174
|
+
*/
|
|
175
|
+
async renameFile(file, newPath) {
|
|
176
|
+
if (!(file instanceof TFile)) {
|
|
177
|
+
return this.app.vault.rename(file, newPath);
|
|
178
|
+
}
|
|
179
|
+
const oldPath = file.path;
|
|
180
|
+
const edits = await this.collectRenameEdits(file, newPath);
|
|
181
|
+
await this.app.vault.rename(file, newPath);
|
|
182
|
+
for (const edit of edits) {
|
|
183
|
+
const sourcePath = edit.sourcePath === oldPath ? newPath : edit.sourcePath;
|
|
184
|
+
const sourceFile = this.app.vault.getFileByPath(sourcePath);
|
|
185
|
+
if (!sourceFile) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
await this.app.vault.process(sourceFile, (data) => {
|
|
189
|
+
let next = data;
|
|
190
|
+
for (const ref of edit.refs) {
|
|
191
|
+
const replacement = this.getReplacementLink(sourcePath, newPath, ref.link, ref.original);
|
|
192
|
+
next = next.slice(0, ref.start) + replacement + next.slice(ref.end);
|
|
193
|
+
}
|
|
194
|
+
return next;
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Remove a file or a folder from the vault according the user's preferred
|
|
200
|
+
* 'trash' options (either moving the file to .trash/ or the OS trash bin).
|
|
201
|
+
*
|
|
202
|
+
* @param file
|
|
203
|
+
* @public
|
|
204
|
+
*/
|
|
205
|
+
trashFile(file) {
|
|
206
|
+
return this.app.vault.trash(file, true);
|
|
207
|
+
}
|
|
208
|
+
promptForDeletion(file) {
|
|
209
|
+
return this.trashFile(file);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Generate a Markdown link based on the user's preferences.
|
|
213
|
+
*
|
|
214
|
+
* @param file - The file to link to.
|
|
215
|
+
* @param sourcePath - Where the link is stored in, used to compute relative
|
|
216
|
+
* links.
|
|
217
|
+
* @param subpath - A subpath, starting with `#`, used for linking to headings
|
|
218
|
+
* or blocks.
|
|
219
|
+
* @param alias - The display text if it's to be different than the file name.
|
|
220
|
+
* Pass empty string to use file name.
|
|
221
|
+
* @public
|
|
222
|
+
*/
|
|
223
|
+
generateMarkdownLink(file, sourcePath, subpath, alias) {
|
|
224
|
+
const normalizedSubpath = subpath?.trim() ?? "";
|
|
225
|
+
return generateInternalLink({
|
|
226
|
+
targetPath: file.path,
|
|
227
|
+
sourcePath,
|
|
228
|
+
alias,
|
|
229
|
+
heading: normalizedSubpath.startsWith("#^") || !normalizedSubpath.length
|
|
230
|
+
? undefined
|
|
231
|
+
: normalizedSubpath.replace(/^#/, ""),
|
|
232
|
+
blockId: normalizedSubpath.startsWith("#^")
|
|
233
|
+
? normalizedSubpath.slice(2)
|
|
234
|
+
: undefined,
|
|
235
|
+
settings: this.getLinkSettings(),
|
|
236
|
+
vaultIndex: this.buildVaultIndex(),
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Atomically read, modify, and save the frontmatter of a note. The
|
|
241
|
+
* frontmatter is passed in as a JS object, and should be mutated directly to
|
|
242
|
+
* achieve the desired result.
|
|
243
|
+
*
|
|
244
|
+
* Remember to handle errors thrown by this method.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```ts
|
|
248
|
+
* app.fileManager.processFrontMatter(file, (frontmatter) => {
|
|
249
|
+
* frontmatter['key1'] = value;
|
|
250
|
+
* delete frontmatter['key2'];
|
|
251
|
+
* });
|
|
252
|
+
* ```;
|
|
253
|
+
*
|
|
254
|
+
* @param file - The file to be modified. Must be a Markdown file.
|
|
255
|
+
* @param fn - A callback function which mutates the frontmatter object
|
|
256
|
+
* synchronously.
|
|
257
|
+
* @param options - Write options.
|
|
258
|
+
* @throws YAMLParseError if the YAML parsing fails
|
|
259
|
+
* @throws Any errors that your callback function throws
|
|
260
|
+
* @public
|
|
261
|
+
*/
|
|
262
|
+
processFrontMatter(file, fn, options) {
|
|
263
|
+
const attempt = async () => {
|
|
264
|
+
const snapshot = await this.app.vault.read(file);
|
|
265
|
+
const seedFrontmatter = await this.readFrontmatterSeed(file, snapshot);
|
|
266
|
+
const nextFrontmatter = this.cloneFrontmatter(seedFrontmatter);
|
|
267
|
+
fn(nextFrontmatter);
|
|
268
|
+
if (this.isFrontmatterEqual(seedFrontmatter, nextFrontmatter)) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
try {
|
|
272
|
+
await this.app.vault.process(file, (data) => {
|
|
273
|
+
if (data !== snapshot) {
|
|
274
|
+
throw new Error(FRONTMATTER_RETRY_ERROR);
|
|
275
|
+
}
|
|
276
|
+
return this.writeUpdatedFrontmatter(file, data, nextFrontmatter);
|
|
277
|
+
}, options);
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
if (error instanceof Error &&
|
|
281
|
+
error.message === FRONTMATTER_RETRY_ERROR) {
|
|
282
|
+
return attempt();
|
|
283
|
+
}
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
return attempt();
|
|
288
|
+
}
|
|
289
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { APPLY_PATCH_INPUT_SCHEMA, EDIT_INPUT_SCHEMA, READ_INPUT_SCHEMA, WRITE_INPUT_SCHEMA, applyUpdateChunksToText, executeApplyPatch, executeEdit, executeRead, executeWrite, parseApplyPatch, prepareApplyPatchInput, prepareEditInput, prepareReadInput, prepareWriteInput, } from "@lapismd/ai-host/file-tools";
|
|
2
|
+
import { assertPayloadSize, isAllowedFileToolPath, revisionFromStat, } from "./paths";
|
|
3
|
+
import { createVaultFileOperations } from "./vault-operations";
|
|
4
|
+
function asSchema(schema) {
|
|
5
|
+
return structuredClone(schema);
|
|
6
|
+
}
|
|
7
|
+
function createScopedResolver(context) {
|
|
8
|
+
return (path) => {
|
|
9
|
+
const resolved = context.scope.resolve(path);
|
|
10
|
+
if (!isAllowedFileToolPath(resolved)) {
|
|
11
|
+
throw new Error("File path must not address internal content.");
|
|
12
|
+
}
|
|
13
|
+
return resolved;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function conflictResult(path, reason, matchCount, hunkIndex) {
|
|
17
|
+
const structuredContent = {
|
|
18
|
+
status: "conflict",
|
|
19
|
+
path,
|
|
20
|
+
reason,
|
|
21
|
+
...(matchCount === undefined ? {} : { matchCount }),
|
|
22
|
+
...(hunkIndex === undefined ? {} : { hunkIndex }),
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
isError: true,
|
|
26
|
+
content: [
|
|
27
|
+
{
|
|
28
|
+
type: "text",
|
|
29
|
+
text: `Edit conflict for ${path}: ${reason}`,
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
structuredContent,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function createReadTool(vault, operations) {
|
|
36
|
+
return {
|
|
37
|
+
name: "read",
|
|
38
|
+
description: `
|
|
39
|
+
Read a bounded line range from a scoped vault text file.
|
|
40
|
+
Prefer this tool over cat or sed in the agent cwd; host cwd is not the browser vault.
|
|
41
|
+
`.trim(),
|
|
42
|
+
inputSchema: asSchema(READ_INPUT_SCHEMA),
|
|
43
|
+
outputSchema: { type: "object" },
|
|
44
|
+
effect: "read",
|
|
45
|
+
execute: async (input, context) => {
|
|
46
|
+
context.signal.throwIfAborted();
|
|
47
|
+
const prepared = prepareReadInput(input);
|
|
48
|
+
const result = await executeRead(operations, createScopedResolver(context), prepared);
|
|
49
|
+
context.signal.throwIfAborted();
|
|
50
|
+
const structuredContent = {
|
|
51
|
+
...result,
|
|
52
|
+
revision: revisionFromStat(await vault.stat(result.path)),
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: "text", text: result.text }],
|
|
56
|
+
structuredContent,
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function createWriteTool(vault, operations) {
|
|
62
|
+
return {
|
|
63
|
+
name: "write",
|
|
64
|
+
description: `
|
|
65
|
+
Create or overwrite a scoped vault text file after creating missing parents.
|
|
66
|
+
Prefer this tool over shell redirects in the agent cwd; paths are vault-relative.
|
|
67
|
+
`.trim(),
|
|
68
|
+
inputSchema: asSchema(WRITE_INPUT_SCHEMA),
|
|
69
|
+
outputSchema: { type: "object" },
|
|
70
|
+
effect: "write",
|
|
71
|
+
describeApproval: async (input, context) => {
|
|
72
|
+
const prepared = prepareWriteInput(input);
|
|
73
|
+
assertPayloadSize(prepared.content, "File content");
|
|
74
|
+
const path = createScopedResolver(context)(prepared.path);
|
|
75
|
+
const existing = await operations.stat(path);
|
|
76
|
+
if (existing?.type === "directory") {
|
|
77
|
+
throw new Error(`${path} is a folder`);
|
|
78
|
+
}
|
|
79
|
+
const before = existing?.type === "file" ? await operations.readFile(path) : "";
|
|
80
|
+
return {
|
|
81
|
+
title: `Write ${path}`,
|
|
82
|
+
description: existing
|
|
83
|
+
? "Overwrite this scoped text file."
|
|
84
|
+
: "Create this scoped text file.",
|
|
85
|
+
path,
|
|
86
|
+
diff: { before, after: prepared.content },
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
execute: async (input, context) => {
|
|
90
|
+
context.signal.throwIfAborted();
|
|
91
|
+
const prepared = prepareWriteInput(input);
|
|
92
|
+
assertPayloadSize(prepared.content, "File content");
|
|
93
|
+
const result = await executeWrite(operations, createScopedResolver(context), prepared);
|
|
94
|
+
context.signal.throwIfAborted();
|
|
95
|
+
const structuredContent = {
|
|
96
|
+
status: result.created ? "created" : result.changed ? "updated" : "unchanged",
|
|
97
|
+
...result,
|
|
98
|
+
revision: revisionFromStat(await vault.stat(result.path)),
|
|
99
|
+
};
|
|
100
|
+
return {
|
|
101
|
+
content: [
|
|
102
|
+
{
|
|
103
|
+
type: "text",
|
|
104
|
+
text: result.created
|
|
105
|
+
? `Created ${result.path}`
|
|
106
|
+
: result.changed
|
|
107
|
+
? `Wrote ${result.path}`
|
|
108
|
+
: `Unchanged ${result.path}`,
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
structuredContent,
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function createEditTool(vault, operations) {
|
|
117
|
+
return {
|
|
118
|
+
name: "edit",
|
|
119
|
+
description: `
|
|
120
|
+
Replace unique exact text hunks in a scoped vault text file.
|
|
121
|
+
Prefer this tool over sed or patching files in the agent cwd.
|
|
122
|
+
`.trim(),
|
|
123
|
+
inputSchema: asSchema(EDIT_INPUT_SCHEMA),
|
|
124
|
+
outputSchema: { type: "object" },
|
|
125
|
+
effect: "write",
|
|
126
|
+
describeApproval: async (input, context) => {
|
|
127
|
+
const prepared = prepareEditInput(input);
|
|
128
|
+
const path = createScopedResolver(context)(prepared.path);
|
|
129
|
+
return {
|
|
130
|
+
title: `Edit ${path}`,
|
|
131
|
+
description: "Replace unique exact text hunks in this file.",
|
|
132
|
+
path,
|
|
133
|
+
diffs: prepared.edits.map((edit) => ({
|
|
134
|
+
path,
|
|
135
|
+
before: edit.oldText,
|
|
136
|
+
after: edit.newText,
|
|
137
|
+
})),
|
|
138
|
+
};
|
|
139
|
+
},
|
|
140
|
+
execute: async (input, context) => {
|
|
141
|
+
context.signal.throwIfAborted();
|
|
142
|
+
const prepared = prepareEditInput(input);
|
|
143
|
+
const result = await executeEdit(operations, createScopedResolver(context), prepared.path, prepared.edits);
|
|
144
|
+
if (!result.ok) {
|
|
145
|
+
return conflictResult(result.path, result.reason, result.matchCount, result.hunkIndex);
|
|
146
|
+
}
|
|
147
|
+
context.signal.throwIfAborted();
|
|
148
|
+
const structuredContent = {
|
|
149
|
+
status: "edited",
|
|
150
|
+
path: result.path,
|
|
151
|
+
revision: revisionFromStat(await vault.stat(result.path)),
|
|
152
|
+
};
|
|
153
|
+
return {
|
|
154
|
+
content: [{ type: "text", text: `Edited ${result.path}` }],
|
|
155
|
+
structuredContent,
|
|
156
|
+
};
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function createApplyPatchTool(operations) {
|
|
161
|
+
return {
|
|
162
|
+
name: "apply_patch",
|
|
163
|
+
description: `
|
|
164
|
+
Apply a V4A patch that adds, updates, moves, or deletes scoped vault text files.
|
|
165
|
+
Prefer this tool over applying patches in the agent cwd.
|
|
166
|
+
`.trim(),
|
|
167
|
+
inputSchema: asSchema(APPLY_PATCH_INPUT_SCHEMA),
|
|
168
|
+
outputSchema: { type: "object" },
|
|
169
|
+
effect: "write",
|
|
170
|
+
describeApproval: async (input, context) => {
|
|
171
|
+
const prepared = prepareApplyPatchInput(input);
|
|
172
|
+
assertPayloadSize(prepared.input, "Patch");
|
|
173
|
+
const resolvePath = createScopedResolver(context);
|
|
174
|
+
const hunks = parseApplyPatch(prepared.input);
|
|
175
|
+
const diffs = [];
|
|
176
|
+
const paths = [];
|
|
177
|
+
for (const hunk of hunks) {
|
|
178
|
+
if (hunk.kind === "add") {
|
|
179
|
+
const path = resolvePath(hunk.path);
|
|
180
|
+
paths.push(path);
|
|
181
|
+
diffs.push({ path, before: "", after: hunk.contents });
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (hunk.kind === "delete") {
|
|
185
|
+
const path = resolvePath(hunk.path);
|
|
186
|
+
paths.push(path);
|
|
187
|
+
diffs.push({
|
|
188
|
+
path,
|
|
189
|
+
before: await operations.readFile(path),
|
|
190
|
+
after: "",
|
|
191
|
+
});
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const path = resolvePath(hunk.path);
|
|
195
|
+
const current = await operations.readFile(path);
|
|
196
|
+
const after = applyUpdateChunksToText(current, path, hunk.chunks);
|
|
197
|
+
if (hunk.movePath) {
|
|
198
|
+
const destination = resolvePath(hunk.movePath);
|
|
199
|
+
paths.push(path, destination);
|
|
200
|
+
diffs.push({ path, before: current, after: "" });
|
|
201
|
+
diffs.push({ path: destination, before: "", after });
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
paths.push(path);
|
|
205
|
+
diffs.push({ path, before: current, after });
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
title: paths.length === 1
|
|
209
|
+
? `Patch ${paths[0]}`
|
|
210
|
+
: `Patch ${paths.length} files`,
|
|
211
|
+
description: "Apply a validated V4A patch to scoped text files.",
|
|
212
|
+
path: paths[0],
|
|
213
|
+
paths,
|
|
214
|
+
diffs,
|
|
215
|
+
};
|
|
216
|
+
},
|
|
217
|
+
execute: async (input, context) => {
|
|
218
|
+
context.signal.throwIfAborted();
|
|
219
|
+
const prepared = prepareApplyPatchInput(input);
|
|
220
|
+
assertPayloadSize(prepared.input, "Patch");
|
|
221
|
+
const result = await executeApplyPatch(operations, createScopedResolver(context), prepared.input);
|
|
222
|
+
context.signal.throwIfAborted();
|
|
223
|
+
const structuredContent = {
|
|
224
|
+
status: "patched",
|
|
225
|
+
...result.summary,
|
|
226
|
+
};
|
|
227
|
+
return {
|
|
228
|
+
content: [{ type: "text", text: result.text }],
|
|
229
|
+
structuredContent,
|
|
230
|
+
};
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
export function createVaultFileAppTools(vault) {
|
|
235
|
+
const operations = createVaultFileOperations(vault);
|
|
236
|
+
return [
|
|
237
|
+
createReadTool(vault, operations),
|
|
238
|
+
createWriteTool(vault, operations),
|
|
239
|
+
createEditTool(vault, operations),
|
|
240
|
+
createApplyPatchTool(operations),
|
|
241
|
+
];
|
|
242
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const HIDDEN_FILE_TOOL_SEGMENTS: Set<string>;
|
|
2
|
+
export declare const MAX_WRITE_BYTES: number;
|
|
3
|
+
export declare function isAllowedFileToolPath(path: string): boolean;
|
|
4
|
+
export declare function parentDirectory(path: string): string | null;
|
|
5
|
+
export declare function byteLength(value: string): number;
|
|
6
|
+
export declare function assertTextContent(content: string, path: string): void;
|
|
7
|
+
export declare function assertPayloadSize(content: string, label: string): void;
|
|
8
|
+
export declare function revisionFromStat(stat: {
|
|
9
|
+
mtime: number;
|
|
10
|
+
size: number;
|
|
11
|
+
} | null): {
|
|
12
|
+
mtime: number;
|
|
13
|
+
size: number;
|
|
14
|
+
} | null;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const HIDDEN_FILE_TOOL_SEGMENTS = new Set([
|
|
2
|
+
".obsidian",
|
|
3
|
+
".lapis",
|
|
4
|
+
".trash",
|
|
5
|
+
]);
|
|
6
|
+
export const MAX_WRITE_BYTES = 256 * 1024;
|
|
7
|
+
export function isAllowedFileToolPath(path) {
|
|
8
|
+
return !path
|
|
9
|
+
.split("/")
|
|
10
|
+
.some((segment) => HIDDEN_FILE_TOOL_SEGMENTS.has(segment));
|
|
11
|
+
}
|
|
12
|
+
export function parentDirectory(path) {
|
|
13
|
+
const index = path.lastIndexOf("/");
|
|
14
|
+
if (index <= 0)
|
|
15
|
+
return null;
|
|
16
|
+
return path.slice(0, index);
|
|
17
|
+
}
|
|
18
|
+
export function byteLength(value) {
|
|
19
|
+
return new TextEncoder().encode(value).byteLength;
|
|
20
|
+
}
|
|
21
|
+
export function assertTextContent(content, path) {
|
|
22
|
+
if (content.includes("\0")) {
|
|
23
|
+
throw new Error(`${path} is not a UTF-8 text file.`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function assertPayloadSize(content, label) {
|
|
27
|
+
if (byteLength(content) > MAX_WRITE_BYTES) {
|
|
28
|
+
throw new Error(`${label} exceeds the size limit.`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function revisionFromStat(stat) {
|
|
32
|
+
return stat ? { mtime: stat.mtime, size: stat.size } : null;
|
|
33
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { TFile, TFolder } from "../storage/fs";
|
|
2
|
+
import { assertPayloadSize, assertTextContent, parentDirectory, } from "./paths";
|
|
3
|
+
export function createVaultFileOperations(vault) {
|
|
4
|
+
return {
|
|
5
|
+
async readFile(path) {
|
|
6
|
+
const file = vault.getAbstractFileByPath(path);
|
|
7
|
+
if (!(file instanceof TFile))
|
|
8
|
+
throw new Error(`File not found: ${path}`);
|
|
9
|
+
const content = await vault.read(file);
|
|
10
|
+
assertTextContent(content, path);
|
|
11
|
+
return content;
|
|
12
|
+
},
|
|
13
|
+
async writeFile(path, content) {
|
|
14
|
+
assertTextContent(content, path);
|
|
15
|
+
assertPayloadSize(content, "File content");
|
|
16
|
+
const parent = parentDirectory(path);
|
|
17
|
+
if (parent)
|
|
18
|
+
await vault.mkpath(parent);
|
|
19
|
+
const existing = vault.getAbstractFileByPath(path);
|
|
20
|
+
if (existing instanceof TFolder) {
|
|
21
|
+
throw new Error(`${path} is a folder`);
|
|
22
|
+
}
|
|
23
|
+
if (existing instanceof TFile) {
|
|
24
|
+
await vault.modify(existing, content);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
await vault.create(path, content);
|
|
28
|
+
},
|
|
29
|
+
async mkdirp(path) {
|
|
30
|
+
if (!path)
|
|
31
|
+
return;
|
|
32
|
+
await vault.mkpath(path);
|
|
33
|
+
},
|
|
34
|
+
async stat(path) {
|
|
35
|
+
const existing = vault.getAbstractFileByPath(path);
|
|
36
|
+
if (existing instanceof TFolder)
|
|
37
|
+
return { type: "directory", size: 0 };
|
|
38
|
+
if (existing instanceof TFile) {
|
|
39
|
+
const stat = await vault.stat(path);
|
|
40
|
+
return { type: "file", size: stat?.size ?? existing.stat.size };
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
},
|
|
44
|
+
async remove(path) {
|
|
45
|
+
const existing = vault.getAbstractFileByPath(path);
|
|
46
|
+
if (!existing)
|
|
47
|
+
throw new Error(`File not found: ${path}`);
|
|
48
|
+
await vault.trash(existing);
|
|
49
|
+
},
|
|
50
|
+
async rename(from, to) {
|
|
51
|
+
const existing = vault.getAbstractFileByPath(from);
|
|
52
|
+
if (!existing)
|
|
53
|
+
throw new Error(`File not found: ${from}`);
|
|
54
|
+
const parent = parentDirectory(to);
|
|
55
|
+
if (parent)
|
|
56
|
+
await vault.mkpath(parent);
|
|
57
|
+
await vault.rename(existing, to);
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|