Skip to content

Instantly share code, notes, and snippets.

@grahama1970
Last active January 23, 2025 17:20
Show Gist options
  • Save grahama1970/fddfc3c9a3a175a8b6e4bdf45b02dd7f to your computer and use it in GitHub Desktop.
Save grahama1970/fddfc3c9a3a175a8b6e4bdf45b02dd7f to your computer and use it in GitHub Desktop.
Debug for an Raycast extension that tries to access Obsidian's Smart Chat Conversations (within Raycast) to ask questions of local documents
import { Action, Detail, LaunchProps } from "@raycast/api";
import { getConfig } from "./utils/preferences";
interface Preferences {
obsidianVaultPath: string;
}
export default function ResultView(props: LaunchProps<{ context: { answer: string } }>) {
const { obsidianVaultPath } = getConfig();
// Add defensive check for context and answer
if (!props.context || !props.context.answer) {
return <Detail markdown="No response content available" />;
}
const answer = props.context.answer;
// Extract a note path from the answer (if present in [title](path) format)
const notePath = answer.match(/\[.*?\]\((.*?)\)/)?.[1] || "";
const obsidianUri = `obsidian://open?path=${encodeURIComponent(`${obsidianVaultPath}/${notePath}`)}`;
return (
<Detail
markdown={answer}
actions={
notePath ? (
<Action.Open
title="Open in Obsidian"
target={obsidianUri}
shortcut={{ modifiers: ["cmd"], key: "o" }}
/>
) : null
}
/>
);
}
import { LaunchType, LaunchProps, closeMainWindow, launchCommand, showToast, Toast, ErrorBoundary } from "@raycast/api";
import open from "open";
import { getConfig } from "./utils/preferences";
interface Preferences {
obsidianApiKey: string;
obsidianPort: string;
obsidianChatFolder: string;
obsidianVaultPath: string;
}
function AskObsidianImplementation(props: LaunchProps<{ arguments: { query: string } }>) {
const { query } = props.arguments;
const { obsidianApiKey, obsidianPort, obsidianChatFolder, obsidianVaultPath } = getConfig();
const processQuery = async () => {
try {
await open(`obsidian://advanced-uri?command=smart-chat&query=${encodeURIComponent(query)}`);
await closeMainWindow();
let retries = 3;
while (retries-- > 0) {
await new Promise(resolve => setTimeout(resolve, 2000));
const search = await fetch(
`http://localhost:${obsidianPort}/vault/search?query=path:${encodeURIComponent(obsidianChatFolder)}&sort=modified`,
{ headers: { Authorization: `Bearer ${obsidianApiKey}` } }
);
const response = await search.json();
// Check if response exists and has results
if (!response || !Array.isArray(response.results)) {
showToast({
style: Toast.Style.Failure,
title: "Invalid API response",
message: "No results found or API error",
});
return;
}
const results = response.results;
const latestNote = results[0]?.path;
if (latestNote) {
const note = await fetch(
`http://localhost:${obsidianPort}/vault/open?path=${encodeURIComponent(latestNote)}`,
{ headers: { Authorization: `Bearer ${obsidianApiKey}` } }
);
await launchCommand({
name: "ask-obsidian-result",
type: LaunchType.UserInitiated,
context: { answer: await note.text() }
});
return;
}
}
throw new Error("No response found after 3 attempts");
} catch (error) {
showToast({
style: Toast.Style.Failure,
title: "Error",
message: error instanceof Error ? error.message : "Failed to query Obsidian"
});
}
};
processQuery();
return null;
}
export default function Command(props: LaunchProps<{ arguments: { query: string } }>) {
return (
<ErrorBoundary>
<AskObsidianImplementation {...props} />
</ErrorBoundary>
);
}
export const DEV_CONFIG = {
obsidianApiKey: "12345",
obsidianPort: "27124",
obsidianVaultPath: "/Users/me/Documents/Obsidian Vault/",
obsidianChatFolder: "Chats"
};
npm run dev
> [email protected] dev
> ray develop
error - TypeError: Cannot read properties of undefined (reading 'map')
at qxe (/Users/robert/scripts/obsidian-raycast-script/node_modules/@raycast/api/dist/commands/develop/index.js:751:1593)
at /Users/robert/scripts/obsidian-raycast-script/node_modules/@raycast/api/dist/commands/develop/index.js:757:2825
at /Users/robert/scripts/obsidian-raycast-script/node_modules/@raycast/api/dist/commands/develop/index.js:757:3572
at FMt (/Users/robert/scripts/obsidian-raycast-script/node_modules/@raycast/api/dist/commands/develop/index.js:757:3903)
at BKe (/Users/robert/scripts/obsidian-raycast-script/node_modules/@raycast/api/dist/commands/develop/index.js:757:3544)
at u.run (/Users/robert/scripts/obsidian-raycast-script/node_modules/@raycast/api/dist/commands/develop/index.js:757:2730)
at async u._run (/Users/robert/scripts/obsidian-raycast-script/node_modules/@oclif/core/lib/command.js:312:22)
at async Config.runCommand (/Users/robert/scripts/obsidian-raycast-script/node_modules/@oclif/core/lib/config/config.js:435:25)
at async run (/Users/robert/scripts/obsidian-raycast-script/node_modules/@oclif/core/lib/main.js:96:16)
16:15:22 Unable to install extension "obsidian-raycast-script":
> Development session couldn't start due to inability to install from local sources
16:15:22 Unable to install extension "obsidian-raycast-script":
> Unable to handle development session build notification due to inability to install from local sources
{
"name": "obsidian-raycast-script",
"version": "1.0.0",
"license": "MIT",
"main": "dist/index.js",
"scripts": {
"dev": "ray develop",
"build": "ray build -e dist",
"lint": "ray lint"
},
"dependencies": {
"@raycast/api": "^1.57.0",
"open": "^9.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/node": "^20.11.0",
"@types/react": "^18.2.45",
"@types/react-dom": "^18.2.18",
"typescript": "^5.3.3"
},
"raycast": {
"schemaVersion": 1,
"title": "Ask Obsidian",
"description": "Query Obsidian Smart Chat from Raycast",
"icon": "command-icon.png",
"author": "robert",
"categories": ["Productivity"],
"commands": [
{
"name": "ask-obsidian",
"title": "Ask Obsidian",
"description": "Query your Obsidian notes",
"mode": "view",
"component": "dist/src/ask-obsidian.js",
"arguments": [
{
"name": "query",
"placeholder": "Enter your query",
"type": "text",
"required": true
}
]
},
{
"name": "ask-obsidian-result",
"title": "Chat Result",
"description": "Displays Smart Chat response",
"mode": "view",
"component": "dist/src/ask-obsidian-result.js"
}
],
"preferences": [
{
"name": "obsidianApiKey",
"title": "API Key",
"type": "password",
"required": true
},
{
"name": "obsidianPort",
"title": "API Port",
"type": "textfield",
"required": true
},
{
"name": "obsidianVaultPath",
"title": "Vault Path",
"type": "textfield",
"required": true
},
{
"name": "obsidianChatFolder",
"title": "Chat Folder",
"type": "textfield",
"default": "Chats",
"required": true
}
]
}
}
import { getPreferenceValues } from "@raycast/api";
import { DEV_CONFIG } from "../config.dev";
const isDev = process.env.NODE_ENV === "development";
export function getConfig() {
if (isDev) {
return DEV_CONFIG;
}
return getPreferenceValues();
}
{
"$schema": "https://json.schemastore.org/tsconfig",
"include": ["src/**/*"],
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"lib": ["ES2021"],
"module": "CommonJS",
"moduleResolution": "node",
"outDir": "dist",
"target": "ES2021",
"jsx": "react-jsx"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment