obsidian-mcp-server 2.0.7 → 3.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/CLAUDE.md +364 -0
- package/Dockerfile +99 -0
- package/LICENSE +4 -6
- package/README.md +246 -206
- package/changelog/3.0.x/3.0.0.md +102 -0
- package/changelog/3.1.x/3.1.0.md +26 -0
- package/changelog/template.md +51 -0
- package/dist/config/server-config.d.ts +19 -0
- package/dist/config/server-config.d.ts.map +1 -0
- package/dist/config/server-config.js +55 -0
- package/dist/config/server-config.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -295
- package/dist/index.js.map +1 -0
- package/dist/mcp-server/prompts/definitions/index.d.ts +8 -0
- package/dist/mcp-server/prompts/definitions/index.d.ts.map +1 -0
- package/dist/mcp-server/prompts/definitions/index.js +8 -0
- package/dist/mcp-server/prompts/definitions/index.js.map +1 -0
- package/dist/mcp-server/resources/definitions/index.d.ts +36 -0
- package/dist/mcp-server/resources/definitions/index.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/index.js +9 -0
- package/dist/mcp-server/resources/definitions/index.js.map +1 -0
- package/dist/mcp-server/resources/definitions/obsidian-status.resource.d.ts +23 -0
- package/dist/mcp-server/resources/definitions/obsidian-status.resource.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/obsidian-status.resource.js +47 -0
- package/dist/mcp-server/resources/definitions/obsidian-status.resource.js.map +1 -0
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.d.ts +13 -0
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.js +30 -0
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.js.map +1 -0
- package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.d.ts +21 -0
- package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.js +38 -0
- package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.js.map +1 -0
- package/dist/mcp-server/tools/definitions/_shared/schemas.d.ts +45 -0
- package/dist/mcp-server/tools/definitions/_shared/schemas.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/_shared/schemas.js +66 -0
- package/dist/mcp-server/tools/definitions/_shared/schemas.js.map +1 -0
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.d.ts +51 -0
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.js +120 -0
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.js.map +1 -0
- package/dist/mcp-server/tools/definitions/index.d.ts +582 -0
- package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/index.js +40 -0
- package/dist/mcp-server/tools/definitions/index.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.d.ts +42 -0
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.js +58 -0
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.d.ts +46 -0
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.js +66 -0
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-execute-command.tool.d.ts +19 -0
- package/dist/mcp-server/tools/definitions/obsidian-execute-command.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-execute-command.tool.js +43 -0
- package/dist/mcp-server/tools/definitions/obsidian-execute-command.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.d.ts +92 -0
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.js +245 -0
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.d.ts +13 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.js +38 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.d.ts +59 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.js +273 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.d.ts +13 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.js +38 -0
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.d.ts +68 -0
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.js +178 -0
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.d.ts +77 -0
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.js +172 -0
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.d.ts +22 -0
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.js +88 -0
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.d.ts +81 -0
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.js +83 -0
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.d.ts +59 -0
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js +167 -0
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.d.ts +76 -0
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.js +244 -0
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.d.ts +48 -0
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.js +100 -0
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.js.map +1 -0
- package/dist/services/obsidian/frontmatter-ops.d.ts +34 -0
- package/dist/services/obsidian/frontmatter-ops.d.ts.map +1 -0
- package/dist/services/obsidian/frontmatter-ops.js +230 -0
- package/dist/services/obsidian/frontmatter-ops.js.map +1 -0
- package/dist/services/obsidian/obsidian-service.d.ts +83 -0
- package/dist/services/obsidian/obsidian-service.d.ts.map +1 -0
- package/dist/services/obsidian/obsidian-service.js +435 -0
- package/dist/services/obsidian/obsidian-service.js.map +1 -0
- package/dist/services/obsidian/section-extractor.d.ts +13 -0
- package/dist/services/obsidian/section-extractor.d.ts.map +1 -0
- package/dist/services/obsidian/section-extractor.js +124 -0
- package/dist/services/obsidian/section-extractor.js.map +1 -0
- package/dist/services/obsidian/types.d.ts +91 -0
- package/dist/services/obsidian/types.d.ts.map +1 -0
- package/dist/services/obsidian/types.js +7 -0
- package/dist/services/obsidian/types.js.map +1 -0
- package/package.json +63 -69
- package/server.json +167 -0
- package/CHANGELOG.md +0 -124
- package/dist/config/index.d.ts +0 -41
- package/dist/config/index.js +0 -191
- package/dist/mcp-server/server.d.ts +0 -33
- package/dist/mcp-server/server.js +0 -211
- package/dist/mcp-server/tools/obsidianDeleteNoteTool/index.d.ts +0 -12
- package/dist/mcp-server/tools/obsidianDeleteNoteTool/index.js +0 -12
- package/dist/mcp-server/tools/obsidianDeleteNoteTool/logic.d.ts +0 -51
- package/dist/mcp-server/tools/obsidianDeleteNoteTool/logic.js +0 -168
- package/dist/mcp-server/tools/obsidianDeleteNoteTool/registration.d.ts +0 -19
- package/dist/mcp-server/tools/obsidianDeleteNoteTool/registration.js +0 -91
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/index.d.ts +0 -12
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/index.js +0 -12
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/logic.d.ts +0 -77
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/logic.js +0 -341
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/registration.d.ts +0 -18
- package/dist/mcp-server/tools/obsidianGlobalSearchTool/registration.js +0 -69
- package/dist/mcp-server/tools/obsidianListNotesTool/index.d.ts +0 -12
- package/dist/mcp-server/tools/obsidianListNotesTool/index.js +0 -12
- package/dist/mcp-server/tools/obsidianListNotesTool/logic.d.ts +0 -68
- package/dist/mcp-server/tools/obsidianListNotesTool/logic.js +0 -215
- package/dist/mcp-server/tools/obsidianListNotesTool/registration.d.ts +0 -23
- package/dist/mcp-server/tools/obsidianListNotesTool/registration.js +0 -98
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/index.d.ts +0 -3
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/index.js +0 -2
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/logic.d.ts +0 -42
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/logic.js +0 -152
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/registration.d.ts +0 -3
- package/dist/mcp-server/tools/obsidianManageFrontmatterTool/registration.js +0 -52
- package/dist/mcp-server/tools/obsidianManageTagsTool/index.d.ts +0 -3
- package/dist/mcp-server/tools/obsidianManageTagsTool/index.js +0 -2
- package/dist/mcp-server/tools/obsidianManageTagsTool/logic.d.ts +0 -28
- package/dist/mcp-server/tools/obsidianManageTagsTool/logic.js +0 -161
- package/dist/mcp-server/tools/obsidianManageTagsTool/registration.d.ts +0 -3
- package/dist/mcp-server/tools/obsidianManageTagsTool/registration.js +0 -52
- package/dist/mcp-server/tools/obsidianReadNoteTool/index.d.ts +0 -12
- package/dist/mcp-server/tools/obsidianReadNoteTool/index.js +0 -12
- package/dist/mcp-server/tools/obsidianReadNoteTool/logic.d.ts +0 -87
- package/dist/mcp-server/tools/obsidianReadNoteTool/logic.js +0 -216
- package/dist/mcp-server/tools/obsidianReadNoteTool/registration.d.ts +0 -20
- package/dist/mcp-server/tools/obsidianReadNoteTool/registration.js +0 -101
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/index.d.ts +0 -12
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/index.js +0 -12
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/logic.d.ts +0 -255
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/logic.js +0 -583
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/registration.d.ts +0 -22
- package/dist/mcp-server/tools/obsidianSearchReplaceTool/registration.js +0 -111
- package/dist/mcp-server/tools/obsidianUpdateNoteTool/index.d.ts +0 -12
- package/dist/mcp-server/tools/obsidianUpdateNoteTool/index.js +0 -12
- package/dist/mcp-server/tools/obsidianUpdateNoteTool/logic.d.ts +0 -183
- package/dist/mcp-server/tools/obsidianUpdateNoteTool/logic.js +0 -490
- package/dist/mcp-server/tools/obsidianUpdateNoteTool/registration.d.ts +0 -21
- package/dist/mcp-server/tools/obsidianUpdateNoteTool/registration.js +0 -108
- package/dist/mcp-server/transports/auth/core/authContext.d.ts +0 -33
- package/dist/mcp-server/transports/auth/core/authContext.js +0 -24
- package/dist/mcp-server/transports/auth/core/authTypes.d.ts +0 -17
- package/dist/mcp-server/transports/auth/core/authTypes.js +0 -5
- package/dist/mcp-server/transports/auth/core/authUtils.d.ts +0 -18
- package/dist/mcp-server/transports/auth/core/authUtils.js +0 -45
- package/dist/mcp-server/transports/auth/index.d.ts +0 -10
- package/dist/mcp-server/transports/auth/index.js +0 -9
- package/dist/mcp-server/transports/auth/strategies/jwt/jwtMiddleware.d.ts +0 -27
- package/dist/mcp-server/transports/auth/strategies/jwt/jwtMiddleware.js +0 -149
- package/dist/mcp-server/transports/auth/strategies/oauth/oauthMiddleware.d.ts +0 -20
- package/dist/mcp-server/transports/auth/strategies/oauth/oauthMiddleware.js +0 -124
- package/dist/mcp-server/transports/httpErrorHandler.d.ts +0 -26
- package/dist/mcp-server/transports/httpErrorHandler.js +0 -73
- package/dist/mcp-server/transports/httpTransport.d.ts +0 -21
- package/dist/mcp-server/transports/httpTransport.js +0 -208
- package/dist/mcp-server/transports/stdioTransport.d.ts +0 -42
- package/dist/mcp-server/transports/stdioTransport.js +0 -63
- package/dist/services/obsidianRestAPI/index.d.ts +0 -15
- package/dist/services/obsidianRestAPI/index.js +0 -17
- package/dist/services/obsidianRestAPI/methods/activeFileMethods.d.ts +0 -38
- package/dist/services/obsidianRestAPI/methods/activeFileMethods.js +0 -62
- package/dist/services/obsidianRestAPI/methods/commandMethods.d.ts +0 -22
- package/dist/services/obsidianRestAPI/methods/commandMethods.js +0 -31
- package/dist/services/obsidianRestAPI/methods/openMethods.d.ts +0 -16
- package/dist/services/obsidianRestAPI/methods/openMethods.js +0 -21
- package/dist/services/obsidianRestAPI/methods/patchMethods.d.ts +0 -37
- package/dist/services/obsidianRestAPI/methods/patchMethods.js +0 -94
- package/dist/services/obsidianRestAPI/methods/periodicNoteMethods.d.ts +0 -42
- package/dist/services/obsidianRestAPI/methods/periodicNoteMethods.js +0 -66
- package/dist/services/obsidianRestAPI/methods/searchMethods.d.ts +0 -25
- package/dist/services/obsidianRestAPI/methods/searchMethods.js +0 -36
- package/dist/services/obsidianRestAPI/methods/vaultMethods.d.ts +0 -58
- package/dist/services/obsidianRestAPI/methods/vaultMethods.js +0 -144
- package/dist/services/obsidianRestAPI/service.d.ts +0 -195
- package/dist/services/obsidianRestAPI/service.js +0 -379
- package/dist/services/obsidianRestAPI/types.d.ts +0 -127
- package/dist/services/obsidianRestAPI/types.js +0 -7
- package/dist/services/obsidianRestAPI/vaultCache/index.d.ts +0 -4
- package/dist/services/obsidianRestAPI/vaultCache/index.js +0 -4
- package/dist/services/obsidianRestAPI/vaultCache/service.d.ts +0 -88
- package/dist/services/obsidianRestAPI/vaultCache/service.js +0 -299
- package/dist/types-global/errors.d.ts +0 -73
- package/dist/types-global/errors.js +0 -71
- package/dist/utils/index.d.ts +0 -5
- package/dist/utils/index.js +0 -13
- package/dist/utils/internal/asyncUtils.d.ts +0 -54
- package/dist/utils/internal/asyncUtils.js +0 -101
- package/dist/utils/internal/errorHandler.d.ts +0 -176
- package/dist/utils/internal/errorHandler.js +0 -351
- package/dist/utils/internal/index.d.ts +0 -4
- package/dist/utils/internal/index.js +0 -4
- package/dist/utils/internal/logger.d.ts +0 -141
- package/dist/utils/internal/logger.js +0 -406
- package/dist/utils/internal/requestContext.d.ts +0 -83
- package/dist/utils/internal/requestContext.js +0 -72
- package/dist/utils/metrics/index.d.ts +0 -1
- package/dist/utils/metrics/index.js +0 -1
- package/dist/utils/metrics/tokenCounter.d.ts +0 -27
- package/dist/utils/metrics/tokenCounter.js +0 -128
- package/dist/utils/obsidian/index.d.ts +0 -5
- package/dist/utils/obsidian/index.js +0 -5
- package/dist/utils/obsidian/obsidianApiUtils.d.ts +0 -14
- package/dist/utils/obsidian/obsidianApiUtils.js +0 -29
- package/dist/utils/obsidian/obsidianStatUtils.d.ts +0 -68
- package/dist/utils/obsidian/obsidianStatUtils.js +0 -143
- package/dist/utils/parsing/dateParser.d.ts +0 -56
- package/dist/utils/parsing/dateParser.js +0 -104
- package/dist/utils/parsing/index.d.ts +0 -2
- package/dist/utils/parsing/index.js +0 -3
- package/dist/utils/parsing/jsonParser.d.ts +0 -80
- package/dist/utils/parsing/jsonParser.js +0 -133
- package/dist/utils/security/idGenerator.d.ts +0 -140
- package/dist/utils/security/idGenerator.js +0 -194
- package/dist/utils/security/index.d.ts +0 -3
- package/dist/utils/security/index.js +0 -3
- package/dist/utils/security/rateLimiter.d.ts +0 -156
- package/dist/utils/security/rateLimiter.js +0 -235
- package/dist/utils/security/sanitization.d.ts +0 -244
- package/dist/utils/security/sanitization.js +0 -599
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Obsidian Local REST API service. Wraps every upstream HTTP
|
|
3
|
+
* endpoint we use, builds the right URL/headers/body for the consolidated
|
|
4
|
+
* `target` discriminator, and classifies errors for the framework.
|
|
5
|
+
* @module services/obsidian/obsidian-service
|
|
6
|
+
*/
|
|
7
|
+
import { forbidden, notFound, unauthorized, validationError } from '@cyanheads/mcp-ts-core/errors';
|
|
8
|
+
import { httpErrorFromResponse, withRetry } from '@cyanheads/mcp-ts-core/utils';
|
|
9
|
+
import { Agent, fetch as undiciFetch } from 'undici';
|
|
10
|
+
import { getServerConfig } from '../../config/server-config.js';
|
|
11
|
+
const NOTE_JSON_ACCEPT = 'application/vnd.olrapi.note+json';
|
|
12
|
+
const DOCUMENT_MAP_ACCEPT = 'application/vnd.olrapi.document-map+json';
|
|
13
|
+
const DATAVIEW_DQL_CT = 'application/vnd.olrapi.dataview.dql+txt';
|
|
14
|
+
const JSONLOGIC_CT = 'application/vnd.olrapi.jsonlogic+json';
|
|
15
|
+
export class ObsidianService {
|
|
16
|
+
#config;
|
|
17
|
+
#dispatcher;
|
|
18
|
+
#fetch;
|
|
19
|
+
/**
|
|
20
|
+
* @param config - Validated server config (api key, base URL, TLS, timeouts).
|
|
21
|
+
* @param fetchImpl - Optional fetch override for tests. Defaults to undici's
|
|
22
|
+
* `fetch`, which honors the constructed TLS dispatcher in production.
|
|
23
|
+
*/
|
|
24
|
+
constructor(config, fetchImpl) {
|
|
25
|
+
this.#config = config;
|
|
26
|
+
/**
|
|
27
|
+
* Bun's runtime ignores undici's per-dispatcher `connect.rejectUnauthorized`
|
|
28
|
+
* option, so the only reliable opt-out under Bun is the process-wide
|
|
29
|
+
* `NODE_TLS_REJECT_UNAUTHORIZED=0` flag. Node honors the dispatcher option
|
|
30
|
+
* (set below), so the env var fallback is scoped to Bun to avoid mutating
|
|
31
|
+
* process-wide TLS behavior on Node. Default Obsidian Local REST API ships
|
|
32
|
+
* a self-signed cert, so most users run with `OBSIDIAN_VERIFY_SSL=false`.
|
|
33
|
+
*/
|
|
34
|
+
if (!config.verifySsl && typeof globalThis.Bun !== 'undefined') {
|
|
35
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
36
|
+
}
|
|
37
|
+
this.#dispatcher = new Agent({
|
|
38
|
+
connect: { rejectUnauthorized: config.verifySsl },
|
|
39
|
+
headersTimeout: config.requestTimeoutMs,
|
|
40
|
+
bodyTimeout: config.requestTimeoutMs,
|
|
41
|
+
});
|
|
42
|
+
this.#fetch = fetchImpl ?? undiciFetch;
|
|
43
|
+
}
|
|
44
|
+
// ── Status ───────────────────────────────────────────────────────────────
|
|
45
|
+
async getStatus(ctx) {
|
|
46
|
+
const res = await this.#request(ctx, '/', { method: 'GET', skipAuth: true });
|
|
47
|
+
return (await res.json());
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Probe whether the configured `OBSIDIAN_API_KEY` is accepted. Hits the
|
|
51
|
+
* authenticated `/vault/` listing endpoint and reports `true` only on a 2xx
|
|
52
|
+
* response. Network/auth errors yield `false` — the resource caller wants a
|
|
53
|
+
* boolean, not an exception. Aborts are re-thrown so cancellation/timeout
|
|
54
|
+
* doesn't masquerade as an auth failure.
|
|
55
|
+
*/
|
|
56
|
+
async probeAuthenticated(ctx) {
|
|
57
|
+
try {
|
|
58
|
+
const res = await this.#fetch(`${this.#config.baseUrl}/vault/`, {
|
|
59
|
+
method: 'GET',
|
|
60
|
+
headers: { Authorization: `Bearer ${this.#config.apiKey}` },
|
|
61
|
+
dispatcher: this.#dispatcher,
|
|
62
|
+
signal: ctx.signal,
|
|
63
|
+
});
|
|
64
|
+
return res.ok;
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
if (ctx.signal?.aborted)
|
|
68
|
+
throw err;
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// ── Notes ────────────────────────────────────────────────────────────────
|
|
73
|
+
async getNoteContent(ctx, target) {
|
|
74
|
+
const url = this.#targetToPath(target);
|
|
75
|
+
const res = await this.#request(ctx, url, {
|
|
76
|
+
method: 'GET',
|
|
77
|
+
headers: { Accept: 'text/markdown' },
|
|
78
|
+
});
|
|
79
|
+
return await res.text();
|
|
80
|
+
}
|
|
81
|
+
async getNoteJson(ctx, target) {
|
|
82
|
+
const url = this.#targetToPath(target);
|
|
83
|
+
const res = await this.#request(ctx, url, {
|
|
84
|
+
method: 'GET',
|
|
85
|
+
headers: { Accept: NOTE_JSON_ACCEPT },
|
|
86
|
+
});
|
|
87
|
+
return (await res.json());
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Resolve `target` to a vault-relative path. For path targets this is a
|
|
91
|
+
* no-op; for `active` and `periodic` targets we have to ask upstream which
|
|
92
|
+
* concrete file is currently in play.
|
|
93
|
+
*/
|
|
94
|
+
async resolvePath(ctx, target) {
|
|
95
|
+
if (target.type === 'path')
|
|
96
|
+
return target.path;
|
|
97
|
+
return (await this.getNoteJson(ctx, target)).path;
|
|
98
|
+
}
|
|
99
|
+
async getDocumentMap(ctx, target) {
|
|
100
|
+
const url = this.#targetToPath(target);
|
|
101
|
+
const res = await this.#request(ctx, url, {
|
|
102
|
+
method: 'GET',
|
|
103
|
+
headers: { Accept: DOCUMENT_MAP_ACCEPT },
|
|
104
|
+
});
|
|
105
|
+
return (await res.json());
|
|
106
|
+
}
|
|
107
|
+
async writeNote(ctx, target, content, contentType = 'markdown') {
|
|
108
|
+
const url = this.#targetToPath(target);
|
|
109
|
+
await this.#request(ctx, url, {
|
|
110
|
+
method: 'PUT',
|
|
111
|
+
headers: { 'Content-Type': contentType === 'json' ? 'application/json' : 'text/markdown' },
|
|
112
|
+
body: content,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
async appendToNote(ctx, target, content, contentType = 'markdown') {
|
|
116
|
+
const url = this.#targetToPath(target);
|
|
117
|
+
await this.#request(ctx, url, {
|
|
118
|
+
method: 'POST',
|
|
119
|
+
headers: { 'Content-Type': contentType === 'json' ? 'application/json' : 'text/markdown' },
|
|
120
|
+
body: content,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
async patchNote(ctx, target, content, headers) {
|
|
124
|
+
const url = this.#targetToPath(target);
|
|
125
|
+
await this.#request(ctx, url, {
|
|
126
|
+
method: 'PATCH',
|
|
127
|
+
headers: this.#buildPatchHeaders(headers),
|
|
128
|
+
body: content,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
async deleteNote(ctx, target) {
|
|
132
|
+
const url = this.#targetToPath(target);
|
|
133
|
+
await this.#request(ctx, url, { method: 'DELETE' });
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Probe whether a note exists at the target. Returns `true` on 2xx,
|
|
137
|
+
* `false` on 404. Any other status (auth, server) is surfaced through the
|
|
138
|
+
* normal error classifier so callers don't silently proceed past an actual
|
|
139
|
+
* problem (e.g. treating a 401 as "doesn't exist" and overwriting blindly).
|
|
140
|
+
*
|
|
141
|
+
* Bypasses `#request` retries: a HEAD probe should never be retried — a
|
|
142
|
+
* 404 is the answer, not a transient failure.
|
|
143
|
+
*/
|
|
144
|
+
async noteExists(ctx, target) {
|
|
145
|
+
const url = this.#targetToPath(target);
|
|
146
|
+
const res = await this.#fetch(`${this.#config.baseUrl}${url}`, {
|
|
147
|
+
method: 'HEAD',
|
|
148
|
+
headers: { Authorization: `Bearer ${this.#config.apiKey}` },
|
|
149
|
+
dispatcher: this.#dispatcher,
|
|
150
|
+
signal: ctx.signal,
|
|
151
|
+
});
|
|
152
|
+
if (res.status === 404)
|
|
153
|
+
return false;
|
|
154
|
+
if (res.ok)
|
|
155
|
+
return true;
|
|
156
|
+
return await this.#throwForStatus(res, url);
|
|
157
|
+
}
|
|
158
|
+
// ── Listings ─────────────────────────────────────────────────────────────
|
|
159
|
+
async listFiles(ctx, dirPath) {
|
|
160
|
+
let url = '/vault/';
|
|
161
|
+
if (dirPath) {
|
|
162
|
+
const normalized = dirPath.replace(/^\/+|\/+$/g, '');
|
|
163
|
+
if (normalized)
|
|
164
|
+
url = `/vault/${encodeVaultPath(normalized)}/`;
|
|
165
|
+
}
|
|
166
|
+
const res = await this.#request(ctx, url, { method: 'GET' });
|
|
167
|
+
return (await res.json());
|
|
168
|
+
}
|
|
169
|
+
async listTags(ctx) {
|
|
170
|
+
const res = await this.#request(ctx, '/tags/', { method: 'GET' });
|
|
171
|
+
const body = (await res.json());
|
|
172
|
+
return body.tags ?? [];
|
|
173
|
+
}
|
|
174
|
+
async listCommands(ctx) {
|
|
175
|
+
const res = await this.#request(ctx, '/commands/', { method: 'GET' });
|
|
176
|
+
const body = (await res.json());
|
|
177
|
+
return body.commands ?? [];
|
|
178
|
+
}
|
|
179
|
+
// ── Search ───────────────────────────────────────────────────────────────
|
|
180
|
+
async searchText(ctx, query, contextLength = 100) {
|
|
181
|
+
const params = new URLSearchParams({ query, contextLength: String(contextLength) });
|
|
182
|
+
const res = await this.#request(ctx, `/search/simple/?${params}`, { method: 'POST' });
|
|
183
|
+
return (await res.json());
|
|
184
|
+
}
|
|
185
|
+
async searchDataview(ctx, dql) {
|
|
186
|
+
const res = await this.#request(ctx, '/search/', {
|
|
187
|
+
method: 'POST',
|
|
188
|
+
headers: { 'Content-Type': DATAVIEW_DQL_CT },
|
|
189
|
+
body: dql,
|
|
190
|
+
});
|
|
191
|
+
return (await res.json());
|
|
192
|
+
}
|
|
193
|
+
async searchJsonLogic(ctx, logic) {
|
|
194
|
+
const res = await this.#request(ctx, '/search/', {
|
|
195
|
+
method: 'POST',
|
|
196
|
+
headers: { 'Content-Type': JSONLOGIC_CT },
|
|
197
|
+
body: JSON.stringify(logic),
|
|
198
|
+
});
|
|
199
|
+
return (await res.json());
|
|
200
|
+
}
|
|
201
|
+
// ── UI / commands ────────────────────────────────────────────────────────
|
|
202
|
+
async executeCommand(ctx, commandId) {
|
|
203
|
+
await this.#request(ctx, `/commands/${encodeURIComponent(commandId)}/`, { method: 'POST' });
|
|
204
|
+
}
|
|
205
|
+
async openInUi(ctx, path, opts) {
|
|
206
|
+
const params = new URLSearchParams();
|
|
207
|
+
if (opts?.newLeaf)
|
|
208
|
+
params.set('newLeaf', 'true');
|
|
209
|
+
const qs = params.toString();
|
|
210
|
+
await this.#request(ctx, `/open/${encodeVaultPath(path)}${qs ? `?${qs}` : ''}`, {
|
|
211
|
+
method: 'POST',
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
// ── Internals ────────────────────────────────────────────────────────────
|
|
215
|
+
#targetToPath(target) {
|
|
216
|
+
switch (target.type) {
|
|
217
|
+
case 'path':
|
|
218
|
+
return `/vault/${encodeVaultPath(target.path)}`;
|
|
219
|
+
case 'active':
|
|
220
|
+
return '/active/';
|
|
221
|
+
case 'periodic': {
|
|
222
|
+
if (target.date) {
|
|
223
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(target.date);
|
|
224
|
+
if (!m) {
|
|
225
|
+
throw validationError(`Invalid date '${target.date}', expected YYYY-MM-DD.`);
|
|
226
|
+
}
|
|
227
|
+
const [, y, mo, d] = m;
|
|
228
|
+
return `/periodic/${target.period}/${y}/${mo}/${d}/`;
|
|
229
|
+
}
|
|
230
|
+
return `/periodic/${target.period}/`;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
#buildPatchHeaders(p) {
|
|
235
|
+
const headers = {
|
|
236
|
+
Operation: p.operation,
|
|
237
|
+
'Target-Type': p.targetType,
|
|
238
|
+
Target: encodeURIComponent(p.target),
|
|
239
|
+
'Content-Type': p.contentType === 'json' ? 'application/json' : 'text/markdown',
|
|
240
|
+
};
|
|
241
|
+
if (p.targetDelimiter)
|
|
242
|
+
headers['Target-Delimiter'] = p.targetDelimiter;
|
|
243
|
+
if (p.createTargetIfMissing)
|
|
244
|
+
headers['Create-Target-If-Missing'] = 'true';
|
|
245
|
+
if (p.applyIfContentPreexists)
|
|
246
|
+
headers['Apply-If-Content-Preexists'] = 'true';
|
|
247
|
+
if (p.trimTargetWhitespace)
|
|
248
|
+
headers['Trim-Target-Whitespace'] = 'true';
|
|
249
|
+
return headers;
|
|
250
|
+
}
|
|
251
|
+
#request(ctx, pathAndQuery, init) {
|
|
252
|
+
const url = `${this.#config.baseUrl}${pathAndQuery}`;
|
|
253
|
+
const headers = { ...(init.headers ?? {}) };
|
|
254
|
+
if (!init.skipAuth) {
|
|
255
|
+
headers.Authorization = `Bearer ${this.#config.apiKey}`;
|
|
256
|
+
}
|
|
257
|
+
return withRetry(async () => {
|
|
258
|
+
const res = await this.#fetch(url, {
|
|
259
|
+
method: init.method,
|
|
260
|
+
headers,
|
|
261
|
+
...(init.body !== undefined ? { body: init.body } : {}),
|
|
262
|
+
dispatcher: this.#dispatcher,
|
|
263
|
+
signal: ctx.signal,
|
|
264
|
+
});
|
|
265
|
+
if (!res.ok) {
|
|
266
|
+
await this.#throwForStatus(res, pathAndQuery);
|
|
267
|
+
}
|
|
268
|
+
return res;
|
|
269
|
+
}, {
|
|
270
|
+
operation: `obsidian.${init.method} ${pathAndQuery}`,
|
|
271
|
+
context: {
|
|
272
|
+
requestId: ctx.requestId,
|
|
273
|
+
timestamp: ctx.timestamp,
|
|
274
|
+
...(ctx.tenantId !== undefined ? { tenantId: ctx.tenantId } : {}),
|
|
275
|
+
...(ctx.traceId !== undefined ? { traceId: ctx.traceId } : {}),
|
|
276
|
+
...(ctx.spanId !== undefined ? { spanId: ctx.spanId } : {}),
|
|
277
|
+
},
|
|
278
|
+
baseDelayMs: 200,
|
|
279
|
+
maxRetries: 3,
|
|
280
|
+
signal: ctx.signal,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
async #throwForStatus(res, path) {
|
|
284
|
+
const text = await this.#readBodySafe(res);
|
|
285
|
+
const body = parseJsonObject(text);
|
|
286
|
+
const display = displayPath(path);
|
|
287
|
+
const upstream = safeUpstream(body, text);
|
|
288
|
+
const data = (extra) => ({
|
|
289
|
+
path: display,
|
|
290
|
+
...(extra ?? {}),
|
|
291
|
+
...(upstream ? { upstream } : {}),
|
|
292
|
+
});
|
|
293
|
+
switch (res.status) {
|
|
294
|
+
case 401:
|
|
295
|
+
throw unauthorized('Obsidian Local REST API rejected the API key. Verify OBSIDIAN_API_KEY matches the value in Obsidian → Settings → Local REST API.', data());
|
|
296
|
+
case 403:
|
|
297
|
+
throw forbidden('Obsidian Local REST API forbids this request. Check the plugin permissions.', data());
|
|
298
|
+
case 404: {
|
|
299
|
+
if (path.startsWith('/active/')) {
|
|
300
|
+
throw notFound('No file is currently active in Obsidian — open a file in the app first.', data({ reason: 'no_active_file' }));
|
|
301
|
+
}
|
|
302
|
+
if (path.startsWith('/periodic/')) {
|
|
303
|
+
const periodMatch = /^\/periodic\/(daily|weekly|monthly|quarterly|yearly)\//.exec(path);
|
|
304
|
+
const period = periodMatch?.[1] ?? 'periodic';
|
|
305
|
+
const dateMatch = /\/(\d{4})\/(\d{2})\/(\d{2})\/?$/.exec(path);
|
|
306
|
+
const suffix = dateMatch ? ` for ${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}` : '';
|
|
307
|
+
throw notFound(`No ${period} note found${suffix}. Check that the Periodic Notes plugin is enabled and the note exists.`, data({ reason: 'periodic_not_found' }));
|
|
308
|
+
}
|
|
309
|
+
if (path.startsWith('/commands/')) {
|
|
310
|
+
throw notFound(`Unknown Obsidian command: ${display}. Use \`obsidian_list_commands\` to discover valid command IDs.`, data({ reason: 'command_unknown' }));
|
|
311
|
+
}
|
|
312
|
+
throw notFound(`Not found: ${display}`, data({ reason: 'note_missing' }));
|
|
313
|
+
}
|
|
314
|
+
case 405:
|
|
315
|
+
throw validationError(`${display} cannot accept this method (often: the path is a directory, not a file).`, data({ reason: 'path_is_directory' }));
|
|
316
|
+
case 400: {
|
|
317
|
+
const upstreamMsg = body?.message ?? `Bad request to ${display}`;
|
|
318
|
+
// The Local REST API returns a "could not be applied to the target
|
|
319
|
+
// content" / "invalid-target" message when a PATCH names a section that
|
|
320
|
+
// doesn't exist. Translate to actionable guidance.
|
|
321
|
+
const isTargetMiss = /\bcould not be applied\b|\binvalid-target\b/i.test(upstreamMsg);
|
|
322
|
+
if (isTargetMiss) {
|
|
323
|
+
throw validationError(`Section target not found in ${display}. Use \`obsidian_get_note\` with \`format: "document-map"\` to list available headings, blocks, and frontmatter fields. Nested headings need \`Parent::Child\` syntax.`, data({ reason: 'section_target_missing' }));
|
|
324
|
+
}
|
|
325
|
+
throw validationError(upstreamMsg, data());
|
|
326
|
+
}
|
|
327
|
+
default: {
|
|
328
|
+
/**
|
|
329
|
+
* Unhandled 4xx and all 5xx — route through the framework helper so we
|
|
330
|
+
* get the canonical status→code mapping (500/501→InternalError,
|
|
331
|
+
* 502/503→ServiceUnavailable, 504→Timeout) and Retry-After capture.
|
|
332
|
+
* Body has already been consumed above, so disable the helper's read
|
|
333
|
+
* and pass the truncated body in via `data`.
|
|
334
|
+
*/
|
|
335
|
+
const truncated = text ? (text.length > 500 ? `${text.slice(0, 500)}…` : text) : undefined;
|
|
336
|
+
throw await httpErrorFromResponse(res, {
|
|
337
|
+
service: 'Obsidian Local REST API',
|
|
338
|
+
captureBody: false,
|
|
339
|
+
data: {
|
|
340
|
+
...data(),
|
|
341
|
+
...(truncated !== undefined ? { body: truncated } : {}),
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
async #readBodySafe(res) {
|
|
348
|
+
try {
|
|
349
|
+
return await res.text();
|
|
350
|
+
}
|
|
351
|
+
catch {
|
|
352
|
+
return '';
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Encode a vault-relative path for the URL. Each segment is URL-encoded so
|
|
358
|
+
* folder slashes are preserved while spaces and unicode are escaped.
|
|
359
|
+
*/
|
|
360
|
+
export function encodeVaultPath(path) {
|
|
361
|
+
return path
|
|
362
|
+
.split('/')
|
|
363
|
+
.filter((seg) => seg.length > 0)
|
|
364
|
+
.map((seg) => encodeURIComponent(seg))
|
|
365
|
+
.join('/');
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Convert an internal URL path (e.g. `/vault/Projects/My%20Note.md`) to the
|
|
369
|
+
* vault-relative form a caller would recognize. Used in error messages so the
|
|
370
|
+
* user sees the same path they sent in.
|
|
371
|
+
*/
|
|
372
|
+
function displayPath(urlPath) {
|
|
373
|
+
if (urlPath.startsWith('/active/'))
|
|
374
|
+
return '(active file)';
|
|
375
|
+
const noQuery = urlPath.split('?')[0] ?? urlPath;
|
|
376
|
+
let decoded;
|
|
377
|
+
try {
|
|
378
|
+
decoded = decodeURIComponent(noQuery);
|
|
379
|
+
}
|
|
380
|
+
catch {
|
|
381
|
+
decoded = noQuery;
|
|
382
|
+
}
|
|
383
|
+
const periodic = /^\/periodic\/(daily|weekly|monthly|quarterly|yearly)\/(?:(\d{4})\/(\d{2})\/(\d{2})\/?)?$/.exec(decoded);
|
|
384
|
+
if (periodic) {
|
|
385
|
+
const [, period, y, mo, d] = periodic;
|
|
386
|
+
return y && mo && d
|
|
387
|
+
? `${period} note for ${y}-${mo}-${d}`
|
|
388
|
+
: `${period} note for the current period`;
|
|
389
|
+
}
|
|
390
|
+
for (const prefix of ['/vault/', '/open/', '/commands/']) {
|
|
391
|
+
if (decoded.startsWith(prefix)) {
|
|
392
|
+
return decoded.slice(prefix.length).replace(/\/+$/, '') || decoded;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return decoded;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Trim the upstream error body down to a safe, user-presentable shape — drops
|
|
399
|
+
* `errorCode` and any other plugin-internal fields that would otherwise leak
|
|
400
|
+
* into JSON-RPC `error.data`.
|
|
401
|
+
*/
|
|
402
|
+
function safeUpstream(body, text) {
|
|
403
|
+
if (body?.message)
|
|
404
|
+
return { message: body.message };
|
|
405
|
+
const trimmed = text.trim();
|
|
406
|
+
if (trimmed)
|
|
407
|
+
return { message: trimmed.length > 200 ? `${trimmed.slice(0, 200)}…` : trimmed };
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
function parseJsonObject(text) {
|
|
411
|
+
if (!text)
|
|
412
|
+
return;
|
|
413
|
+
try {
|
|
414
|
+
const v = JSON.parse(text);
|
|
415
|
+
return v && typeof v === 'object' ? v : undefined;
|
|
416
|
+
}
|
|
417
|
+
catch {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
let _service;
|
|
422
|
+
export function initObsidianService(config = getServerConfig(), fetchImpl) {
|
|
423
|
+
_service = new ObsidianService(config, fetchImpl);
|
|
424
|
+
}
|
|
425
|
+
/** Test-only: directly install an instance (e.g., one backed by a stub fetch). */
|
|
426
|
+
export function setObsidianService(service) {
|
|
427
|
+
_service = service;
|
|
428
|
+
}
|
|
429
|
+
export function getObsidianService() {
|
|
430
|
+
if (!_service) {
|
|
431
|
+
throw new Error('ObsidianService not initialized — call initObsidianService() in setup().');
|
|
432
|
+
}
|
|
433
|
+
return _service;
|
|
434
|
+
}
|
|
435
|
+
//# sourceMappingURL=obsidian-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"obsidian-service.js","sourceRoot":"","sources":["../../../src/services/obsidian/obsidian-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Client-side section extraction from a NoteJson body.
|
|
3
|
+
* The upstream Local REST API exposes section *targeting* for PATCH but not
|
|
4
|
+
* section-extracted GET, so we slice the markdown ourselves for `format: 'section'`.
|
|
5
|
+
* @module services/obsidian/section-extractor
|
|
6
|
+
*/
|
|
7
|
+
import type { NoteJson, SectionTarget } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Extract a section's raw markdown (heading/block) or JSON value (frontmatter).
|
|
10
|
+
* Throws `NotFound` if the target does not exist in the note.
|
|
11
|
+
*/
|
|
12
|
+
export declare function extractSection(note: NoteJson, section: SectionTarget): string | unknown;
|
|
13
|
+
//# sourceMappingURL=section-extractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section-extractor.d.ts","sourceRoot":"","sources":["../../../src/services/obsidian/section-extractor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE1D;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,GAAG,OAAO,CASvF"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Client-side section extraction from a NoteJson body.
|
|
3
|
+
* The upstream Local REST API exposes section *targeting* for PATCH but not
|
|
4
|
+
* section-extracted GET, so we slice the markdown ourselves for `format: 'section'`.
|
|
5
|
+
* @module services/obsidian/section-extractor
|
|
6
|
+
*/
|
|
7
|
+
import { notFound } from '@cyanheads/mcp-ts-core/errors';
|
|
8
|
+
/**
|
|
9
|
+
* Extract a section's raw markdown (heading/block) or JSON value (frontmatter).
|
|
10
|
+
* Throws `NotFound` if the target does not exist in the note.
|
|
11
|
+
*/
|
|
12
|
+
export function extractSection(note, section) {
|
|
13
|
+
switch (section.type) {
|
|
14
|
+
case 'frontmatter':
|
|
15
|
+
return extractFrontmatterField(note, section.target);
|
|
16
|
+
case 'heading':
|
|
17
|
+
return extractHeading(note.content, section.target);
|
|
18
|
+
case 'block':
|
|
19
|
+
return extractBlock(note.content, section.target);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function extractFrontmatterField(note, key) {
|
|
23
|
+
if (!(key in note.frontmatter)) {
|
|
24
|
+
throw notFound(`Frontmatter key '${key}' not found in ${note.path}.`, {
|
|
25
|
+
path: note.path,
|
|
26
|
+
key,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return note.frontmatter[key];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Match a heading by "::"-delimited hierarchy. "Top::Sub" walks to a level-N
|
|
33
|
+
* heading "Top" and then a deeper heading "Sub" beneath it.
|
|
34
|
+
*/
|
|
35
|
+
function extractHeading(content, target) {
|
|
36
|
+
const parts = target
|
|
37
|
+
.split('::')
|
|
38
|
+
.map((p) => p.trim())
|
|
39
|
+
.filter(Boolean);
|
|
40
|
+
if (parts.length === 0) {
|
|
41
|
+
throw notFound('Empty heading target.', { target });
|
|
42
|
+
}
|
|
43
|
+
const lines = content.split('\n');
|
|
44
|
+
const bodyStart = frontmatterEndLine(lines);
|
|
45
|
+
let cursor = bodyStart;
|
|
46
|
+
let parentLevel = 0;
|
|
47
|
+
for (const part of parts) {
|
|
48
|
+
let found = -1;
|
|
49
|
+
let foundLevel = 0;
|
|
50
|
+
for (let i = cursor; i < lines.length; i++) {
|
|
51
|
+
const m = /^(#{1,6})\s+(.*?)\s*$/.exec(lines[i] ?? '');
|
|
52
|
+
if (!m)
|
|
53
|
+
continue;
|
|
54
|
+
const level = (m[1] ?? '').length;
|
|
55
|
+
const text = (m[2] ?? '').trim();
|
|
56
|
+
if (parentLevel > 0 && level <= parentLevel) {
|
|
57
|
+
// exited the parent; stop searching deeper for this part
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
if (text === part && (parentLevel === 0 || level > parentLevel)) {
|
|
61
|
+
found = i;
|
|
62
|
+
foundLevel = level;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (found === -1) {
|
|
67
|
+
throw notFound(`Heading '${target}' not found.`, { target });
|
|
68
|
+
}
|
|
69
|
+
cursor = found + 1;
|
|
70
|
+
parentLevel = foundLevel;
|
|
71
|
+
}
|
|
72
|
+
// Slice from the matched heading line to the next heading at the same or shallower level.
|
|
73
|
+
const startLine = cursor - 1;
|
|
74
|
+
let endLine = lines.length;
|
|
75
|
+
for (let i = cursor; i < lines.length; i++) {
|
|
76
|
+
const m = /^(#{1,6})\s+/.exec(lines[i] ?? '');
|
|
77
|
+
if (m && (m[1] ?? '').length <= parentLevel) {
|
|
78
|
+
endLine = i;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return lines.slice(startLine, endLine).join('\n').replace(/\n+$/, '');
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Match a block by its `^blockId` reference. Returns the line containing the
|
|
86
|
+
* reference plus any preceding lines belonging to the same paragraph (until a
|
|
87
|
+
* blank line or heading).
|
|
88
|
+
*/
|
|
89
|
+
function extractBlock(content, blockId) {
|
|
90
|
+
const lines = content.split('\n');
|
|
91
|
+
const bodyStart = frontmatterEndLine(lines);
|
|
92
|
+
const escaped = blockId.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&');
|
|
93
|
+
const ref = new RegExp(`(^|\\s)\\^${escaped}\\s*$`);
|
|
94
|
+
for (let i = bodyStart; i < lines.length; i++) {
|
|
95
|
+
if (ref.test(lines[i] ?? '')) {
|
|
96
|
+
let start = i;
|
|
97
|
+
while (start > bodyStart) {
|
|
98
|
+
const prev = lines[start - 1] ?? '';
|
|
99
|
+
if (prev.trim() === '' || /^#{1,6}\s+/.test(prev))
|
|
100
|
+
break;
|
|
101
|
+
start--;
|
|
102
|
+
}
|
|
103
|
+
return lines.slice(start, i + 1).join('\n');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
throw notFound(`Block reference '^${blockId}' not found.`, { blockId });
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Return the line index where the document body starts, skipping a leading
|
|
110
|
+
* `---`/`---` YAML frontmatter block. Returns 0 when no frontmatter is present.
|
|
111
|
+
* Without this guard, heading and block extraction would scan inside the
|
|
112
|
+
* frontmatter and falsely match YAML comment lines or include the fence in a
|
|
113
|
+
* paragraph walk-back.
|
|
114
|
+
*/
|
|
115
|
+
function frontmatterEndLine(lines) {
|
|
116
|
+
if (!/^---\s*$/.test(lines[0] ?? ''))
|
|
117
|
+
return 0;
|
|
118
|
+
for (let i = 1; i < lines.length; i++) {
|
|
119
|
+
if (/^---\s*$/.test(lines[i] ?? ''))
|
|
120
|
+
return i + 1;
|
|
121
|
+
}
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=section-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section-extractor.js","sourceRoot":"","sources":["../../../src/services/obsidian/section-extractor.ts"],"names":[],"mappings":"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"}
|