c34dab2cca
Build and Push Docker Container / build-and-push (push) Successful in 4m28s
- Create missing note files before running property:set. - Default date-only datetime values to midnight. - Keep property names out of path authorization checks. - Split policy, vault, daily note, and command target helpers. - Split n8n node args, description, output, and API response helpers. - Split API tests by auth, command, daily note, and property behavior. - Bump API and n8n node versions.
93 lines
2.6 KiB
TypeScript
93 lines
2.6 KiB
TypeScript
import type {
|
|
IExecuteFunctions,
|
|
IDataObject,
|
|
IHttpRequestOptions,
|
|
} from 'n8n-workflow';
|
|
import { NodeOperationError } from 'n8n-workflow';
|
|
|
|
import { toCleanApiError } from './errors';
|
|
import { maybeParseStdout, normalizeCommandResult } from './response';
|
|
|
|
export interface ObsidianCommandResult extends IDataObject {
|
|
id: string;
|
|
command: string[];
|
|
cwd: string;
|
|
exit_code: number;
|
|
stdout: string;
|
|
stderr: string;
|
|
duration_ms: number;
|
|
timed_out: boolean;
|
|
parsed_stdout?: IDataObject;
|
|
}
|
|
|
|
export interface ObsidianCredentials extends IDataObject {
|
|
baseUrl: string;
|
|
apiToken: string;
|
|
defaultVault?: string;
|
|
}
|
|
|
|
export function compactArgs(args: Array<string | undefined | null | false>): string[] {
|
|
return args.filter((arg): arg is string => typeof arg === 'string' && arg.length > 0);
|
|
}
|
|
|
|
export function kv(name: string, value: string | number | boolean | undefined | null): string | undefined {
|
|
if (value === undefined || value === null || value === '') return undefined;
|
|
if (typeof value === 'boolean') return value ? name : undefined;
|
|
return `${name}=${value}`;
|
|
}
|
|
|
|
export function withVault(args: string[], vault?: string): string[] {
|
|
if (!vault || args[0]?.startsWith('vault=')) return args;
|
|
return [`vault=${vault}`, ...args];
|
|
}
|
|
|
|
export async function executeObsidianCommand(
|
|
context: IExecuteFunctions,
|
|
itemIndex: number,
|
|
args: string[],
|
|
options: { vault?: string; timeout?: number; stdin?: string } = {},
|
|
): Promise<ObsidianCommandResult> {
|
|
const credentials = await context.getCredentials('obsidianApi') as ObsidianCredentials;
|
|
const baseUrl = String(credentials.baseUrl).replace(/\/$/, '');
|
|
const vault = options.vault || String(credentials.defaultVault || '');
|
|
const finalArgs = withVault(args, vault);
|
|
|
|
const request: IHttpRequestOptions = {
|
|
method: 'POST',
|
|
url: `${baseUrl}/commands`,
|
|
headers: {
|
|
Authorization: `Bearer ${credentials.apiToken}`,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: {
|
|
args: finalArgs,
|
|
stdin: options.stdin || undefined,
|
|
timeout: options.timeout,
|
|
},
|
|
json: true,
|
|
returnFullResponse: false,
|
|
};
|
|
|
|
let response: unknown;
|
|
try {
|
|
response = await context.helpers.httpRequest(request) as unknown;
|
|
} catch (error) {
|
|
throw toCleanApiError(context, error, itemIndex);
|
|
}
|
|
const result = normalizeCommandResult(response);
|
|
|
|
const parsed = maybeParseStdout(result);
|
|
if (parsed) result.parsed_stdout = parsed;
|
|
|
|
if (result.exit_code !== 0) {
|
|
throw new NodeOperationError(
|
|
context.getNode(),
|
|
`Obsidian CLI command failed with exit code ${result.exit_code}: ${result.stderr || result.stdout}`,
|
|
{ itemIndex },
|
|
);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|