From fa766f5354cc677cbf747d1ae3404b5693fc6189 Mon Sep 17 00:00:00 2001 From: Daniel Dolezal Date: Wed, 24 Jun 2026 12:57:28 +0200 Subject: [PATCH] feat(obsidian): add property frontmatter commands - Expose Obsidian CLI property commands via the n8n Obsidian API node - Add a new Property resource with operations: Set, Read, Remove, List - Implement correct args mapping for property:set/property:read/property:remove/properties - Return raw CLI stdout for property read/list for easier downstream parsing - Update node README with frontmatter/property usage examples - Bump package version to 0.2.5 --- packages/n8n-nodes-obsidian-api/README.md | 28 +++++ .../nodes/ObsidianApi/ObsidianApi.node.ts | 114 ++++++++++++++++++ .../n8n-nodes-obsidian-api/package-lock.json | 4 +- packages/n8n-nodes-obsidian-api/package.json | 2 +- 4 files changed, 145 insertions(+), 3 deletions(-) diff --git a/packages/n8n-nodes-obsidian-api/README.md b/packages/n8n-nodes-obsidian-api/README.md index 88da506..7d23782 100644 --- a/packages/n8n-nodes-obsidian-api/README.md +++ b/packages/n8n-nodes-obsidian-api/README.md @@ -16,6 +16,11 @@ Resources: - Delete - Rename - List Files +- **Property** + - Set + - Read + - Remove + - List - **Daily Note** - Append - Prepend @@ -51,6 +56,29 @@ Raw command example: ["vault=work", "read", "path=Inbox/Note.md"] ``` +## Properties / frontmatter +The official Obsidian CLI supports property/frontmatter commands. The n8n node exposes them as the **Property** resource: + +- **Set** sends `property:set path= name= value= type=` +- **Read** sends `property:read path= name=` +- **Remove** sends `property:remove path= name=` +- **List** sends `properties name= format=` + +Examples: +```text +path=00 Kontext/Feedback Browser CLI Tests.md +name=tags +value=kontext, feedback-browser-cli-tests +type=list +``` + +```text +path=00 Kontext/Feedback Browser CLI Tests.md +name=date +value=2026-06-24 +type=date +``` + ## Local install in self-hosted n8n From this repo: ```bash diff --git a/packages/n8n-nodes-obsidian-api/nodes/ObsidianApi/ObsidianApi.node.ts b/packages/n8n-nodes-obsidian-api/nodes/ObsidianApi/ObsidianApi.node.ts index 4c3fe87..8c12b64 100644 --- a/packages/n8n-nodes-obsidian-api/nodes/ObsidianApi/ObsidianApi.node.ts +++ b/packages/n8n-nodes-obsidian-api/nodes/ObsidianApi/ObsidianApi.node.ts @@ -37,6 +37,7 @@ export class ObsidianApi implements INodeType { options: [ { name: 'Daily Note', value: 'daily' }, { name: 'Note', value: 'note' }, + { name: 'Property', value: 'property' }, { name: 'Raw Command', value: 'raw' }, { name: 'Search', value: 'search' }, { name: 'Vault', value: 'vault' }, @@ -129,6 +130,78 @@ export class ObsidianApi implements INodeType { required: true, }, + // Property operations + { + displayName: 'Operation', + name: 'operation', + type: 'options', + noDataExpression: true, + displayOptions: { show: { resource: ['property'] } }, + options: [ + { name: 'List', value: 'properties' }, + { name: 'Read', value: 'propertyRead' }, + { name: 'Remove', value: 'propertyRemove' }, + { name: 'Set', value: 'propertySet' }, + ], + default: 'propertySet', + }, + { + displayName: 'Path', + name: 'propertyPath', + type: 'string', + displayOptions: { show: { resource: ['property'], operation: ['propertyRead', 'propertyRemove', 'propertySet'] } }, + default: '', + placeholder: 'Inbox/Note.md', + required: true, + }, + { + displayName: 'Name', + name: 'propertyName', + type: 'string', + displayOptions: { show: { resource: ['property'], operation: ['propertyRead', 'propertyRemove', 'propertySet', 'properties'] } }, + default: '', + placeholder: 'tags', + description: 'Frontmatter/property name, e.g. tags, date, description', + }, + { + displayName: 'Value', + name: 'propertyValue', + type: 'string', + displayOptions: { show: { resource: ['property'], operation: ['propertySet'] } }, + default: '', + placeholder: 'kontext, feedback-browser-cli-tests', + description: 'Value passed to the official CLI property:set command', + required: true, + }, + { + displayName: 'Type', + name: 'propertyType', + type: 'options', + displayOptions: { show: { resource: ['property'], operation: ['propertySet'] } }, + options: [ + { name: 'Text', value: 'text' }, + { name: 'List', value: 'list' }, + { name: 'Number', value: 'number' }, + { name: 'Checkbox', value: 'checkbox' }, + { name: 'Date', value: 'date' }, + { name: 'Date Time', value: 'datetime' }, + ], + default: 'text', + description: 'Obsidian property type. Use List for tags and Date for date.', + }, + { + displayName: 'Format', + name: 'propertiesFormat', + type: 'options', + displayOptions: { show: { resource: ['property'], operation: ['properties'] } }, + options: [ + { name: 'YAML', value: 'yaml' }, + { name: 'JSON', value: 'json' }, + { name: 'TSV', value: 'tsv' }, + ], + default: 'yaml', + }, + // Search operations { displayName: 'Operation', @@ -247,6 +320,10 @@ export class ObsidianApi implements INodeType { args = buildSearchArgs(this, operation, itemIndex); break; } + case 'property': { + args = buildPropertyArgs(this, operation, itemIndex); + break; + } case 'vault': { args = buildVaultArgs(this, operation, itemIndex, additionalFields); break; @@ -293,6 +370,15 @@ export class ObsidianApi implements INodeType { continue; } + // Properties: keep CLI output because read/list return useful data. + if (resource === 'property' && (operation === 'propertyRead' || operation === 'properties')) { + const out: any = { content: stdout }; + if (result.stderr) out.stderr = result.stderr; + if (result.timed_out) out.timed_out = result.timed_out; + returnData.push({ json: out, pairedItem: { item: itemIndex } }); + continue; + } + // vault and vaults are parsed as structured JSON const parsed = (result as any).parsed_stdout; if (parsed) { @@ -370,6 +456,34 @@ function buildSearchArgs(context: IExecuteFunctions, operation: string, itemInde ]); } +function buildPropertyArgs(context: IExecuteFunctions, operation: string, itemIndex: number): string[] { + const path = context.getNodeParameter('propertyPath', itemIndex, '') as string; + const name = context.getNodeParameter('propertyName', itemIndex, '') as string; + + switch (operation) { + case 'properties': + return compactArgs([ + 'properties', + kv('name', name), + kv('format', context.getNodeParameter('propertiesFormat', itemIndex) as string), + ]); + case 'propertyRead': + return compactArgs(['property:read', kv('path', path), kv('name', name)]); + case 'propertyRemove': + return compactArgs(['property:remove', kv('path', path), kv('name', name)]); + case 'propertySet': + return compactArgs([ + 'property:set', + kv('path', path), + kv('name', name), + kv('value', context.getNodeParameter('propertyValue', itemIndex) as string), + kv('type', context.getNodeParameter('propertyType', itemIndex) as string), + ]); + default: + throw new NodeOperationError(context.getNode(), `Unsupported property operation: ${operation}`, { itemIndex }); + } +} + function buildVaultArgs(context: IExecuteFunctions, operation: string, itemIndex: number, additionalFields: Record): string[] { switch (operation) { case 'vaultInfo': diff --git a/packages/n8n-nodes-obsidian-api/package-lock.json b/packages/n8n-nodes-obsidian-api/package-lock.json index 2ff999a..b83cb8d 100644 --- a/packages/n8n-nodes-obsidian-api/package-lock.json +++ b/packages/n8n-nodes-obsidian-api/package-lock.json @@ -1,12 +1,12 @@ { "name": "n8n-nodes-obsidian-cli-api", - "version": "0.2.4", + "version": "0.2.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "n8n-nodes-obsidian-cli-api", - "version": "0.2.4", + "version": "0.2.5", "license": "MIT", "devDependencies": { "@types/node": "^24.0.0", diff --git a/packages/n8n-nodes-obsidian-api/package.json b/packages/n8n-nodes-obsidian-api/package.json index fec8564..1b4e7d4 100644 --- a/packages/n8n-nodes-obsidian-api/package.json +++ b/packages/n8n-nodes-obsidian-api/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-obsidian-cli-api", - "version": "0.2.4", + "version": "0.2.5", "description": "n8n community nodes for the Obsidian API Docker service backed by the official Obsidian CLI.", "license": "MIT", "homepage": "https://git.yiprawr.dev/Docker/obsidian-api",