feat(obsidian): add property frontmatter commands
Build and Push Docker Container / build-and-push (push) Successful in 4m27s

- 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
This commit is contained in:
2026-06-24 12:57:28 +02:00
parent 747fd68726
commit fa766f5354
4 changed files with 145 additions and 3 deletions
@@ -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, unknown>): string[] {
switch (operation) {
case 'vaultInfo':