### Initialize and Start Gateway Client Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Instantiate the GatewayClient with connection details and event handlers. Call `start()` to initiate the connection and enable automatic reconnection. ```typescript const client = new GatewayClient({ url: "ws://127.0.0.1:18789", token: "my-gateway-auth-token", // optional deviceIdentity: identity, // optional — enables Ed25519 scopes onHello: (payload) => { console.log("Connected to gateway", payload); // Now safe to call client.request(...) }, onClose: ({ code, reason }) => { console.warn("Disconnected:", code, reason); // "pairing required" or "device identity required" → prompt user to approve }, onEvent: ({ event, payload, seq }) => { if (event === "chat") { // payload.state: "delta" | "final" | "aborted" | "error" console.log("Chat event:", payload.state, payload.message); } }, }); client.start(); // begins connection + auto-reconnect loop ``` -------------------------------- ### GatewayClient Initialization and Connection Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Demonstrates how to initialize the GatewayClient with connection options and start the connection process. It also shows how to handle connection events like successful connection (onHello) and disconnection (onClose). ```APIDOC ## GatewayClient ### Description A resilient WebSocket client that handles connection lifecycle, request/response correlation with 30-second timeouts, and exponential-backoff reconnection. ### Initialization ```typescript const client = new GatewayClient({ url: "ws://127.0.0.1:18789", token: "my-gateway-auth-token", // optional deviceIdentity: identity, // optional — enables Ed25519 scopes onHello: (payload) => { console.log("Connected to gateway", payload); // Now safe to call client.request(...) }, onClose: ({ code, reason }) => { console.warn("Disconnected:", code, reason); // "pairing required" or "device identity required" → prompt user to approve }, onEvent: ({ event, payload, seq }) => { if (event === "chat") { // payload.state: "delta" | "final" | "aborted" | "error" console.log("Chat event:", payload.state, payload.message); } }, }); ``` ### Starting and Stopping - **`client.start()`**: Begins the connection and auto-reconnect loop. - **`client.stop()`**: Closes the socket and cancels all pending requests. ``` -------------------------------- ### Install and Build Dependencies - Bash Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Commands for installing project dependencies, running a development build with inline sourcemaps, and creating a production build. The dev script automatically copies built artifacts to the Obsidian plugin directory. ```bash # Install dependencies npm install # Development build with inline sourcemaps (auto-copies to .obsidian/plugins/obsidianclaw/) npm run dev # Production build (minified, no sourcemap) npm run build # Manually copy built artefacts to your vault cp main.js manifest.json styles.css /path/to/vault/.obsidian/plugins/obsidianclaw/ ``` -------------------------------- ### Get or Create Device Identity Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Generates or restores a persistent Ed25519 keypair for device identification. Keys are stored in the vault's data.json. ```typescript // First call: generates a new keypair and persists it. // Subsequent calls: restores the keypair from saved plugin data. const identity: DeviceIdentity = await getOrCreateDeviceIdentity( () => plugin.loadData(), (data) => plugin.saveData(data) ); console.log(identity.deviceId); // "a3f1c9..." (64-char hex SHA-256 of pubkey) console.log(identity.publicKey); // "base64url-encoded-raw-ed25519-public-key" console.log(identity.privateKey); // "base64url-encoded-pkcs8-private-key" // identity.cryptoKey — CryptoKey with "sign" usage, ready for crypto.subtle.sign() ``` -------------------------------- ### Clone and Build ObsidianClaw Source: https://github.com/humanitylabs-org/obsidianclaw/blob/main/README.md Instructions for cloning the ObsidianClaw repository and building the plugin from source. After building, copy the necessary files to your Obsidian plugin directory. ```bash git clone https://github.com/humanitylabs-org/obsidianclaw.git cd obsidianclaw npm install npm run build ``` -------------------------------- ### "Ask About Note" Command Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Reads the active note and pre-populates the chat input, allowing users to ask questions about the note's content. ```APIDOC ## "Ask About Note" Command Reads the currently active note from the vault and pre-populates the chat input with its content so the user can ask the agent a question about it. ```typescript // Triggered by: Cmd/Ctrl+P → "OpenClaw: Ask about current note" // Internally calls plugin.askAboutNote(): const file: TFile = app.workspace.getActiveFile(); // e.g. "Projects/Alpha.md" const content: string = await app.vault.read(file); // Populates the chat textarea with: const message = `Here is my current note "Alpha":\n\n${content}\n\nWhat can you tell me about this?`; // The user can edit before sending. ``` ``` -------------------------------- ### Plugin Lifecycle and Commands - TypeScript Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Defines commands accessible via the Obsidian command palette and outlines key lifecycle actions like connecting to the gateway, activating the chat view, and the full onboarding flow. Use these functions to manage plugin state and user interaction. ```typescript // Registered commands (accessible via Cmd/Ctrl+P): // "OpenClaw: Toggle chat sidebar" → plugin.activateView() // "OpenClaw: Ask about current note" → plugin.askAboutNote() // "OpenClaw: Reconnect to gateway" → plugin.connectGateway() // "OpenClaw: Run setup wizard" → new OnboardingModal(app, plugin).open() // Connecting to the gateway (called on load after onboarding, or on reconnect): await plugin.connectGateway(); // - Stops any existing GatewayClient // - Validates the URL scheme (must be ws:// or wss://) // - Calls getOrCreateDeviceIdentity() to load/create the Ed25519 keypair // - Creates a new GatewayClient and starts it // - On hello: sets gatewayConnected = true, loads chat history // - On close with "pairing required": shows a 10-second Notice // Opening / revealing the chat sidebar: await plugin.activateView(); // If already open, reveals the existing leaf. // Otherwise creates a new right-side leaf with view type "openclaw-chat". // Full onboarding flow (first run): // 1. OnboardingModal opens automatically 500 ms after plugin load // 2. User enters gateway URL + token // 3. A test GatewayClient with an 8 s timeout validates the connection // 4. On success, settings are saved and connectGateway() + activateView() are called ``` -------------------------------- ### Configure OpenClaw Gateway for Tailscale Source: https://github.com/humanitylabs-org/obsidianclaw/blob/main/README.md Configure the OpenClaw gateway to bind to the Tailscale interface for cross-device communication. Restart the gateway to apply changes. ```bash openclaw config set gateway.bind tailnet openclaw gateway restart ``` -------------------------------- ### handleFileSelect Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Handles file picker input, saving images to the vault and processing text/binary files. ```APIDOC ## File Attachment — `handleFileSelect` Handles the file picker input in the chat UI. Images are saved to the vault under `openclaw-attachments/` and referenced by absolute path (never inlined as base64). Text files are truncated at 10 000 characters and wrapped in a Markdown code fence. ```typescript // Image attachment (user picks "photo.png"): // 1. Saved to vault: "openclaw-attachments/1700000000000-photo.png" // 2. pendingAttachment becomes: { name: "photo.png", content: "[Attached image: photo.png (image/png, 42KB)]\nFile saved at: /Users/alice/vault/openclaw-attachments/1700000000000-photo.png", vaultPath: "openclaw-attachments/1700000000000-photo.png", } // Text/Markdown attachment (user picks "notes.md", 3 000 chars): { name: "notes.md", content: "File: notes.md\n```\n...file content...\n```", // no vaultPath — not saved to vault } // Binary attachment (user picks "archive.zip"): { name: "archive.zip", content: "[Attached file: archive.zip (application/zip, 128KB)]", } // When sendMessage() is called the content is appended to the user message // and the agent receives the full context including the file path or content. ``` ``` -------------------------------- ### Accessing and Updating Chat View Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Demonstrates how to access the chat view, update its status, load message history, and handle incoming chat events. ```typescript const leaf = app.workspace.getRightLeaf(false); await leaf.setViewState({ type: "openclaw-chat", active: true }); ``` ```typescript plugin.chatView?.updateStatus(); ``` ```typescript await plugin.chatView?.loadHistory(); ``` ```typescript plugin.chatView?.handleChatEvent({ sessionKey: "agent:main:main", state: "delta", message: "Partial response text...", }); ``` -------------------------------- ### Troubleshooting: List Pending OpenClaw Device Pairings Source: https://github.com/humanitylabs-org/obsidianclaw/blob/main/README.md List pending device pairing requests to identify the request ID needed for approval. This is part of the troubleshooting process for 'Pairing required' errors. ```bash openclaw devices list ``` -------------------------------- ### Gateway Protocol `connect` Request Parameters Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Defines the parameters sent in the initial `connect` request after the WebSocket opens. This includes protocol version, client metadata, and authentication details. ```typescript // Internally sent by GatewayClient.sendConnect() const connectParams = { minProtocol: 3, maxProtocol: 3, client: { id: "gateway-client", version: "0.1.0", platform: "obsidian", mode: "ui", }, role: "operator", scopes: ["operator.admin", "operator.write", "operator.read"], auth: { token: "my-gateway-auth-token" }, // omitted if no token device: { id: identity.deviceId, publicKey: identity.publicKey, signature: "", signedAt: 1700000000000, nonce: "server-issued-nonce", // present only for v2 challenge }, caps: [], }; // Wire format (sent as JSON): // { "type": "req", "id": "", "method": "connect", "params": connectParams } // On success the gateway responds: // { "type": "res", "id": "", "ok": true, "payload": { ... hello payload ... } } // On failure: // { "type": "res", "id": "", "ok": false, "error": { "message": "pairing required" } } ``` -------------------------------- ### Approve Device Pairing for OpenClaw Source: https://github.com/humanitylabs-org/obsidianclaw/blob/main/README.md Approve pending device pairing requests for OpenClaw. List pending requests to find the request ID, then use the ID to approve. ```bash openclaw devices list openclaw devices approve ``` -------------------------------- ### Loading and Saving Plugin Settings Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Methods for loading plugin settings from and saving them to the vault's data.json using Obsidian's API. ```typescript // Loading and saving settings inside the plugin class: async loadSettings(): Promise { this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); } async saveSettings(): Promise { await this.saveData(this.settings); } ``` -------------------------------- ### Ask About Note Command Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Reads the active note's content and pre-populates the chat input for asking questions about the note. The user can edit the message before sending. ```typescript // Triggered by: Cmd/Ctrl+P → "OpenClaw: Ask about current note" // Internally calls plugin.askAboutNote(): const file: TFile = app.workspace.getActiveFile(); // e.g. "Projects/Alpha.md" const content: string = await app.vault.read(file); // Populates the chat textarea with: const message = `Here is my current note "Alpha":\n\n${content}\n\nWhat can you tell me about this?`; // The user can edit before sending. ``` -------------------------------- ### Troubleshooting: Check OpenClaw Gateway Status Source: https://github.com/humanitylabs-org/obsidianclaw/blob/main/README.md Verify if the OpenClaw gateway is running. This is a common step in troubleshooting connection issues. ```bash openclaw gateway status ``` -------------------------------- ### OpenClawChatView Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Provides methods to interact with the chat view, such as updating status, loading history, and handling chat events. ```APIDOC ## Chat View — `OpenClawChatView` An Obsidian `ItemView` registered under the type `"openclaw-chat"` that renders the full chat UI in the right sidebar. Key public methods used by the plugin host: ```typescript // Accessing an active chat view (created by the plugin): const leaf = app.workspace.getRightLeaf(false); await leaf.setViewState({ type: "openclaw-chat", active: true }); // The plugin keeps a reference: plugin.chatView // Update the connection status dot (called after gateway connect/disconnect): plugin.chatView?.updateStatus(); // Load and render message history from the gateway: await plugin.chatView?.loadHistory(); // Handle an incoming streaming chat event from the gateway: plugin.chatView?.handleChatEvent({ sessionKey: "agent:main:main", state: "delta", message: "Partial response text...", }); // state values: "delta" → append to stream bubble // "final" → commit stream, reload history // "aborted" → commit partial text // "error" → show error bubble ``` ``` -------------------------------- ### Plugin Settings Interface and Defaults Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Defines the structure for plugin settings and provides default values. These settings are persisted in the vault's data.json. ```typescript interface OpenClawSettings { gatewayUrl: string; // WebSocket URL, e.g. "ws://127.0.0.1:18789" token: string; // Gateway auth token (empty if no auth) sessionKey: string; // Session name to chat in, default "main" onboardingComplete: boolean; // Whether the setup wizard has been finished deviceId?: string; // SHA-256 hex of Ed25519 public key devicePublicKey?: string; // Base64URL-encoded raw public key devicePrivateKey?: string; // Base64URL-encoded PKCS8 private key } const DEFAULT_SETTINGS: OpenClawSettings = { gatewayUrl: "ws://127.0.0.1:18789", token: "", sessionKey: "main", onboardingComplete: false, }; ``` -------------------------------- ### Build and Sign Authentication Payload Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Constructs a versioned, pipe-delimited payload for gateway authentication and signs it using the device's Ed25519 private key. ```typescript // Build the payload string for signing const payload = buildSignaturePayload({ deviceId: identity.deviceId, clientId: "gateway-client", clientMode: "ui", role: "operator", scopes: ["operator.admin", "operator.write", "operator.read"], signedAtMs: Date.now(), token: "my-secret-token", nonce: "server-issued-nonce-abc123", // null → v1; string → v2 }); // payload === "v2|a3f1c9...|gateway-client|ui|operator|operator.admin,operator.write,operator.read|1700000000000|my-secret-token|server-issued-nonce-abc123" // Sign it with the device's Ed25519 private key const signature: string = await signDevicePayload(identity, payload); // signature === "base64url-encoded-64-byte-ed25519-signature" ``` -------------------------------- ### Handling File Attachments Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Processes file picker input, saving images to the vault and wrapping text files in Markdown code fences. The content is appended to user messages for the agent. ```typescript // Image attachment (user picks "photo.png"): // 1. Saved to vault: "openclaw-attachments/1700000000000-photo.png" // 2. pendingAttachment becomes: { name: "photo.png", content: "[Attached image: photo.png (image/png, 42KB)]\nFile saved at: /Users/alice/vault/openclaw-attachments/1700000000000-photo.png", vaultPath: "openclaw-attachments/1700000000000-photo.png", } ``` ```typescript // Text/Markdown attachment (user picks "notes.md", 3 000 chars): { name: "notes.md", content: "File: notes.md\n```\n...file content...\n```", // no vaultPath — not saved to vault } ``` ```typescript // Binary attachment (user picks "archive.zip"): { name: "archive.zip", content: "[Attached file: archive.zip (application/zip, 128KB)]", } ``` ```typescript // When sendMessage() is called the content is appended to the user message // and the agent receives the full context including the file path or content. ``` -------------------------------- ### chat.send RPC Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Sends a user message to the agent session. The gateway streams the assistant reply back via `chat` events on the WebSocket. ```APIDOC ## Gateway RPC — `chat.send` ### Description Sends a user message to the agent session. The gateway streams the assistant reply back via `chat` events on the WebSocket. ### Method ```typescript const runId = crypto.randomUUID(); // used to correlate and potentially abort await client.request("chat.send", { sessionKey: "main", message: "Summarise my project notes", deliver: false, // false = don't push via external channels (Telegram, etc.) idempotencyKey: runId, // prevents duplicate sends on retry }); ``` ### Parameters #### Request Body - **sessionKey** (string) - Required - The key identifying the chat session. - **message** (string) - Required - The user's message content. - **deliver** (boolean) - Optional - Whether to push the message via external channels. Defaults to true. - **idempotencyKey** (string) - Required - A unique key to prevent duplicate sends on retry. ``` -------------------------------- ### Send User Message RPC Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Sends a message from the user to the agent. The assistant's reply is streamed back via `chat` events. An `idempotencyKey` is used to prevent duplicate sends. ```typescript const runId = crypto.randomUUID(); // used to correlate and potentially abort await client.request("chat.send", { sessionKey: "main", message: "Summarise my project notes", deliver: false, // false = don't push via external channels (Telegram, etc.) idempotencyKey: runId, // prevents duplicate sends on retry }); // The gateway then emits chat events: // { type: "event", event: "chat", payload: { sessionKey: "agent:main:main", state: "delta", message: "Summar..." } } // { type: "event", event: "chat", payload: { sessionKey: "agent:main:main", state: "delta", message: "Summarise my..." } } // { type: "event", event: "chat", payload: { sessionKey: "agent:main:main", state: "final" } } ``` -------------------------------- ### Send Gateway RPC Request Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Send a JSON-RPC-style request to the gateway. The method returns a Promise that resolves with the response or rejects after a 30-second timeout. ```typescript // Send a JSON-RPC-style request (returns a Promise, rejects after 30 s) const response = await client.request("chat.history", { sessionKey: "main", limit: 50, }); // response.messages → array of { role, content, timestamp } ``` -------------------------------- ### extractContent Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Normalizes heterogeneous message content into a flat `{ text, images }` structure, stripping gateway metadata and annotations. ```APIDOC ## Message Content Extraction — `extractContent` Normalises the heterogeneous message content format returned by the gateway (plain string or OpenAI-style content-block array) into a flat `{ text, images }` structure. Also strips gateway metadata, timestamp prefixes, data URIs, and inline system annotations. ```typescript // String content: const a = extractContent("Hello, world!"); // → { text: "Hello, world!", images: [] } // Array content (OpenAI-style blocks): const b = extractContent([ { type: "text", text: "Here's the chart:" }, { type: "image_url", image_url: { url: "data:image/png;base64,ABC==" } }, ]); // → { text: "Here's the chart:", images: ["data:image/png;base64,ABC=="] // Gateway metadata is automatically stripped: const c = extractContent( "[Sun 2026-02-22 21:58 GMT+7] Hello!\nConversation info (untrusted metadata):\n```json\n{\"message_id\":\"x\"}\n```" ); // → { text: "Hello!", images: [] } // "File saved at:" lines produce a vault resource URL in images[]: // → { text: "See below:", images: ["app://local/vault/openclaw-attachments/1700000000000-photo.png"] } ``` ``` -------------------------------- ### Revoke Device Access Source: https://github.com/humanitylabs-org/obsidianclaw/blob/main/README.md Use these commands to revoke an operator's access to a device, typically when encountering a 'Missing scope: operator.write' error. Re-pairing the device after revocation is necessary. ```bash openclaw devices list openclaw devices revoke --device --role operator ``` -------------------------------- ### Fetch Chat History RPC Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Retrieves the complete conversation history for a specified session. The `limit` parameter controls the maximum number of messages to fetch. ```typescript const result = await client.request("chat.history", { sessionKey: "main", // resolves to "agent:main:main" on the gateway limit: 200, }); // result.messages example: // [ // { role: "user", content: "Hello!", timestamp: 1700000001000 }, // { role: "assistant", content: "Hi there!", timestamp: 1700000002000 }, // { // role: "assistant", // content: [ // { type: "text", text: "Here is an image:" }, // { type: "image_url", image_url: { url: "data:image/png;base64,வைக்" } } // ], // timestamp: 1700000003000 // } // ] ``` -------------------------------- ### chat.history RPC Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Fetches the full conversation history for a given session from the gateway. This method is useful for retrieving past messages to provide context or for display purposes. ```APIDOC ## Gateway RPC — `chat.history` ### Description Fetches the full conversation history for a given session from the gateway. ### Method ```typescript await client.request("chat.history", { sessionKey: "main", // resolves to "agent:main:main" on the gateway limit: 200, }); ``` ### Parameters #### Request Body - **sessionKey** (string) - Required - The key identifying the chat session. - **limit** (number) - Optional - The maximum number of messages to retrieve. ``` -------------------------------- ### Stop Gateway Client Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Gracefully closes the WebSocket connection and cancels any pending requests initiated by the client. ```typescript client.stop(); // closes the socket and cancels all pending requests ``` -------------------------------- ### Extracting Message Content Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Normalizes heterogeneous message content into a flat { text, images } structure, stripping metadata and annotations. Handles string and OpenAI-style array content. ```typescript const a = extractContent("Hello, world!"); // → { text: "Hello, world!", images: [] } ``` ```typescript const b = extractContent([ { type: "text", text: "Here's the chart:" }, { type: "image_url", image_url: { url: "data:image/png;base64,ABC==" } }, ]); // → { text: "Here's the chart:", images: ["data:image/png;base64,ABC=="] } ``` ```typescript const c = extractContent( "[Sun 2026-02-22 21:58 GMT+7] Hello!\nConversation info (untrusted metadata):\n```json\n{\"message_id\":\"x\"}\n```" ); // → { text: "Hello!", images: [] } ``` ```typescript // "File saved at:" lines produce a vault resource URL in images[]: // → { text: "See below:", images: ["app://local/vault/openclaw-attachments/1700000000000-photo.png"] } ``` -------------------------------- ### chat.abort RPC Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Cancels a streaming assistant response mid-generation. This is useful for stopping a long-running response if the user changes their mind or if the response is no longer needed. ```APIDOC ## Gateway RPC — `chat.abort` ### Description Cancels a streaming assistant response mid-generation. ### Method ```typescript await client.request("chat.abort", { sessionKey: "main", runId: runId, // the idempotencyKey used in chat.send }); ``` ### Parameters #### Request Body - **sessionKey** (string) - Required - The key identifying the chat session. - **runId** (string) - Required - The idempotency key used when sending the original message via `chat.send`. ``` -------------------------------- ### Abort Streaming Response RPC Source: https://context7.com/humanitylabs-org/obsidianclaw/llms.txt Cancels an ongoing streaming response from the assistant mid-generation. Requires the `runId` (idempotencyKey) used in the original `chat.send` request. ```typescript await client.request("chat.abort", { sessionKey: "main", runId: runId, // the idempotencyKey used in chat.send }); // After abort, a final chat event arrives: // { type: "event", event: "chat", payload: { state: "aborted", ... } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.