feat: add Obsidian CLI API service
Build and Push Docker Container / build-and-push (push) Successful in 4m26s

- Add Docker image for the official Obsidian desktop app and CLI.

- Start Obsidian headlessly with Xvfb and DBus setup.

- Expose a safe structured HTTP command API without shell execution.

- Add JWT-based vault, path, and command authorization.

- Support single-vault and multi-vault container mounts.

- Add TypeScript SDK helpers for Obsidian CLI commands.

- Add n8n community node package with Obsidian operations.

- Add docs, compose config, tests, and production image workflow.
This commit is contained in:
2026-06-24 09:11:13 +02:00
commit 902a3df8b5
34 changed files with 5153 additions and 0 deletions
+59
View File
@@ -0,0 +1,59 @@
# @obsidian-api/sdk
TypeScript SDK for this HTTP wrapper around the **official Obsidian CLI**.
The SDK is built for Node 18+, n8n Code nodes, Prism, and other TypeScript agent harnesses.
## Install locally
```bash
npm install /path/to/obsidian-api/sdk/typescript
```
## Usage
```ts
import { ObsidianCliClient } from '@obsidian-api/sdk';
const obsidian = new ObsidianCliClient({
baseUrl: 'http://localhost:8080',
token: 'change-me',
});
await obsidian.create({
path: 'Inbox/Hello.md',
content: '# Hello from n8n/Prism',
overwrite: true,
});
const note = await obsidian.read({ path: 'Inbox/Hello.md' });
console.log(note.stdout);
const results = await obsidian.search('Hello', { format: 'json' });
console.log(JSON.parse(results.stdout));
```
## Multiple vaults
Use the official CLI `vault=<name>` prefix directly:
```ts
await obsidian.runInVault('work', 'create', 'path=Inbox/Task.md', 'content=Hello');
```
Or create a scoped client:
```ts
const workVault = obsidian.inVault('work');
await workVault.create({ path: 'Inbox/Task.md', content: 'Hello', overwrite: true });
await workVault.read({ path: 'Inbox/Task.md' });
```
You can also set a default vault in the constructor:
```ts
const obsidian = new ObsidianCliClient({
baseUrl: 'http://localhost:8080',
token: 'change-me',
vault: 'work',
});
```
## Raw command escape hatch
Still safe: sends an args array, not a shell command string.
```ts
await obsidian.runRaw('create', 'name=Scratch', 'content=Test', 'overwrite');
```
+33
View File
@@ -0,0 +1,33 @@
{
"name": "@obsidian-api/sdk",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@obsidian-api/sdk",
"version": "1.0.0",
"license": "MIT",
"devDependencies": {
"typescript": "^5.9.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
}
}
}
+28
View File
@@ -0,0 +1,28 @@
{
"name": "@obsidian-api/sdk",
"version": "1.0.0",
"description": "TypeScript SDK for the official Obsidian CLI HTTP wrapper. Built for n8n, Prism, and other TS/Node agent harnesses.",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": ["dist", "src"],
"scripts": {
"build": "tsc -p tsconfig.json",
"typecheck": "tsc -p tsconfig.json --noEmit",
"test": "node --test"
},
"engines": {
"node": ">=18"
},
"license": "MIT",
"devDependencies": {
"typescript": "^5.9.0"
}
}
+258
View File
@@ -0,0 +1,258 @@
export type OutputFormat = "json" | "tsv" | "csv" | "text" | "yaml" | "md" | "paths" | "tree";
export type PaneType = "tab" | "split" | "window";
export type PluginFilter = "core" | "community";
export type PropertyType = "text" | "list" | "number" | "checkbox" | "date" | "datetime";
export type HistoryFilter = "local" | "sync";
export type LogLevel = "log" | "warn" | "error" | "info" | "debug";
export interface CommandResult {
id: string;
command: string[];
cwd: string;
exit_code: number;
stdout: string;
stderr: string;
duration_ms: number;
timed_out: boolean;
}
export class ObsidianApiError extends Error {
constructor(public statusCode: number, public detail: string) {
super(`HTTP ${statusCode}: ${detail}`);
this.name = "ObsidianApiError";
}
}
export interface ObsidianCliClientOptions {
baseUrl: string;
token?: string;
timeoutMs?: number;
/** Optional default vault name/path. Sent as the official CLI `vault=<name>` prefix. */
vault?: string;
}
type ParamValue = string | number | boolean | undefined | null;
function kv(name: string, value: ParamValue): string | undefined {
if (value === undefined || value === null) return undefined;
if (typeof value === "boolean") return value ? name : undefined;
return `${name}=${value}`;
}
function args(...items: Array<string | undefined>): string[] {
return items.filter((item): item is string => item !== undefined);
}
export class ObsidianCliClient {
readonly baseUrl: string;
readonly token?: string;
readonly timeoutMs: number;
readonly defaultVault?: string;
constructor(options: ObsidianCliClientOptions) {
this.baseUrl = options.baseUrl.replace(/\/$/, "");
this.token = options.token;
this.timeoutMs = options.timeoutMs ?? 60_000;
this.defaultVault = options.vault;
}
/**
* Return a scoped client that automatically prefixes every command with
* `vault=<name>`, matching the official Obsidian CLI multi-vault syntax.
*/
inVault(vault: string): ObsidianCliClient {
return new ObsidianCliClient({
baseUrl: this.baseUrl,
token: this.token,
timeoutMs: this.timeoutMs,
vault,
});
}
async run(commandArgs: string[], options: { stdin?: string; timeoutMs?: number } = {}): Promise<CommandResult> {
const timeoutMs = options.timeoutMs ?? this.timeoutMs;
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs + 5_000);
try {
const response = await fetch(`${this.baseUrl}/commands`, {
method: "POST",
headers: {
"content-type": "application/json",
...(this.token ? { authorization: `Bearer ${this.token}` } : {}),
},
body: JSON.stringify({
args: this.defaultVault && !commandArgs[0]?.startsWith("vault=") ? [`vault=${this.defaultVault}`, ...commandArgs] : commandArgs,
stdin: options.stdin,
timeout: Math.ceil(timeoutMs / 1000),
}),
signal: controller.signal,
});
const data = await response.json() as CommandResult | { detail?: string };
if (!response.ok) {
throw new ObsidianApiError(response.status, "detail" in data && data.detail ? data.detail : response.statusText);
}
return data as CommandResult;
} finally {
clearTimeout(timer);
}
}
async runRaw(...commandArgs: string[]): Promise<CommandResult> {
return this.run(commandArgs);
}
async runInVault(vault: string, command: string, ...commandArgs: string[]): Promise<CommandResult> {
return this.run([`vault=${vault}`, command, ...commandArgs]);
}
// General
help = (command?: string) => this.run(args("help", command));
version = () => this.run(["version"]);
reload = () => this.run(["reload"]);
restart = () => this.run(["restart"]);
// Bases
bases = () => this.run(["bases"]);
baseViews = () => this.run(["base:views"]);
baseCreate = (o: { file?: string; path?: string; view?: string; name?: string; content?: string; open?: boolean; newtab?: boolean } = {}) =>
this.run(args("base:create", kv("file", o.file), kv("path", o.path), kv("view", o.view), kv("name", o.name), kv("content", o.content), kv("open", o.open), kv("newtab", o.newtab)));
baseQuery = (o: { file?: string; path?: string; view?: string; format?: "json" | "csv" | "tsv" | "md" | "paths" } = {}) =>
this.run(args("base:query", kv("file", o.file), kv("path", o.path), kv("view", o.view), kv("format", o.format)));
// Bookmarks / command palette / hotkeys
bookmarks = (o: { total?: boolean; verbose?: boolean; format?: "json" | "tsv" | "csv" } = {}) => this.run(args("bookmarks", kv("total", o.total), kv("verbose", o.verbose), kv("format", o.format)));
bookmark = (o: { file?: string; subpath?: string; folder?: string; search?: string; url?: string; title?: string }) => this.run(args("bookmark", kv("file", o.file), kv("subpath", o.subpath), kv("folder", o.folder), kv("search", o.search), kv("url", o.url), kv("title", o.title)));
commands = (o: { filter?: string } = {}) => this.run(args("commands", kv("filter", o.filter)));
command = (id: string) => this.run(["command", kv("id", id)!]);
hotkeys = (o: { total?: boolean; verbose?: boolean; format?: "json" | "tsv" | "csv" } = {}) => this.run(args("hotkeys", kv("total", o.total), kv("verbose", o.verbose), kv("format", o.format)));
hotkey = (id: string, o: { verbose?: boolean } = {}) => this.run(args("hotkey", kv("id", id), kv("verbose", o.verbose)));
// Daily notes
daily = (o: { paneType?: PaneType } = {}) => this.run(args("daily", kv("paneType", o.paneType)));
dailyPath = () => this.run(["daily:path"]);
dailyRead = () => this.run(["daily:read"]);
dailyAppend = (content: string, o: { paneType?: PaneType; inline?: boolean; open?: boolean } = {}) => this.run(args("daily:append", kv("content", content), kv("paneType", o.paneType), kv("inline", o.inline), kv("open", o.open)));
dailyPrepend = (content: string, o: { paneType?: PaneType; inline?: boolean; open?: boolean } = {}) => this.run(args("daily:prepend", kv("content", content), kv("paneType", o.paneType), kv("inline", o.inline), kv("open", o.open)));
// File history
diff = (o: { file?: string; path?: string; from?: number; to?: number; filter?: HistoryFilter } = {}) => this.run(args("diff", kv("file", o.file), kv("path", o.path), kv("from", o.from), kv("to", o.to), kv("filter", o.filter)));
history = (o: { file?: string; path?: string } = {}) => this.run(args("history", kv("file", o.file), kv("path", o.path)));
historyList = () => this.run(["history:list"]);
historyRead = (o: { file?: string; path?: string; version?: number } = {}) => this.run(args("history:read", kv("file", o.file), kv("path", o.path), kv("version", o.version)));
historyRestore = (version: number, o: { file?: string; path?: string } = {}) => this.run(args("history:restore", kv("file", o.file), kv("path", o.path), kv("version", version)));
historyOpen = (o: { file?: string; path?: string } = {}) => this.run(args("history:open", kv("file", o.file), kv("path", o.path)));
// Files and folders
file = (o: { file?: string; path?: string } = {}) => this.run(args("file", kv("file", o.file), kv("path", o.path)));
files = (o: { folder?: string; ext?: string; total?: boolean } = {}) => this.run(args("files", kv("folder", o.folder), kv("ext", o.ext), kv("total", o.total)));
folder = (path: string, o: { info?: "files" | "folders" | "size" } = {}) => this.run(args("folder", kv("path", path), kv("info", o.info)));
folders = (o: { folder?: string; total?: boolean } = {}) => this.run(args("folders", kv("folder", o.folder), kv("total", o.total)));
open = (o: { file?: string; path?: string; newtab?: boolean } = {}) => this.run(args("open", kv("file", o.file), kv("path", o.path), kv("newtab", o.newtab)));
create = (o: { name?: string; path?: string; content?: string; template?: string; overwrite?: boolean; open?: boolean; newtab?: boolean } = {}) => this.run(args("create", kv("name", o.name), kv("path", o.path), kv("content", o.content), kv("template", o.template), kv("overwrite", o.overwrite), kv("open", o.open), kv("newtab", o.newtab)));
read = (o: { file?: string; path?: string } = {}) => this.run(args("read", kv("file", o.file), kv("path", o.path)));
append = (content: string, o: { file?: string; path?: string; inline?: boolean } = {}) => this.run(args("append", kv("file", o.file), kv("path", o.path), kv("content", content), kv("inline", o.inline)));
prepend = (content: string, o: { file?: string; path?: string; inline?: boolean } = {}) => this.run(args("prepend", kv("file", o.file), kv("path", o.path), kv("content", content), kv("inline", o.inline)));
move = (to: string, o: { file?: string; path?: string } = {}) => this.run(args("move", kv("file", o.file), kv("path", o.path), kv("to", to)));
rename = (name: string, o: { file?: string; path?: string } = {}) => this.run(args("rename", kv("file", o.file), kv("path", o.path), kv("name", name)));
delete = (o: { file?: string; path?: string; permanent?: boolean } = {}) => this.run(args("delete", kv("file", o.file), kv("path", o.path), kv("permanent", o.permanent)));
// Links / outline
backlinks = (o: { file?: string; path?: string; counts?: boolean; total?: boolean; format?: "json" | "tsv" | "csv" } = {}) => this.run(args("backlinks", kv("file", o.file), kv("path", o.path), kv("counts", o.counts), kv("total", o.total), kv("format", o.format)));
links = (o: { file?: string; path?: string; total?: boolean } = {}) => this.run(args("links", kv("file", o.file), kv("path", o.path), kv("total", o.total)));
unresolved = (o: { total?: boolean; counts?: boolean; verbose?: boolean; format?: "json" | "tsv" | "csv" } = {}) => this.run(args("unresolved", kv("total", o.total), kv("counts", o.counts), kv("verbose", o.verbose), kv("format", o.format)));
orphans = (o: { total?: boolean } = {}) => this.run(args("orphans", kv("total", o.total)));
deadends = (o: { total?: boolean } = {}) => this.run(args("deadends", kv("total", o.total)));
outline = (o: { file?: string; path?: string; format?: "tree" | "md" | "json"; total?: boolean } = {}) => this.run(args("outline", kv("file", o.file), kv("path", o.path), kv("format", o.format), kv("total", o.total)));
// Plugins
plugins = (o: { filter?: PluginFilter; versions?: boolean; format?: "json" | "tsv" | "csv" } = {}) => this.run(args("plugins", kv("filter", o.filter), kv("versions", o.versions), kv("format", o.format)));
pluginsEnabled = (o: { filter?: PluginFilter; versions?: boolean; format?: "json" | "tsv" | "csv" } = {}) => this.run(args("plugins:enabled", kv("filter", o.filter), kv("versions", o.versions), kv("format", o.format)));
pluginsRestrict = (o: { on?: boolean; off?: boolean } = {}) => this.run(args("plugins:restrict", kv("on", o.on), kv("off", o.off)));
plugin = (id: string) => this.run(["plugin", kv("id", id)!]);
pluginEnable = (id: string, o: { filter?: PluginFilter } = {}) => this.run(args("plugin:enable", kv("id", id), kv("filter", o.filter)));
pluginDisable = (id: string, o: { filter?: PluginFilter } = {}) => this.run(args("plugin:disable", kv("id", id), kv("filter", o.filter)));
pluginInstall = (id: string, o: { enable?: boolean } = {}) => this.run(args("plugin:install", kv("id", id), kv("enable", o.enable)));
pluginUninstall = (id: string) => this.run(["plugin:uninstall", kv("id", id)!]);
pluginReload = (id: string) => this.run(["plugin:reload", kv("id", id)!]);
// Properties
aliases = (o: { file?: string; path?: string; total?: boolean; verbose?: boolean; active?: boolean } = {}) => this.run(args("aliases", kv("file", o.file), kv("path", o.path), kv("total", o.total), kv("verbose", o.verbose), kv("active", o.active)));
properties = (o: { file?: string; path?: string; name?: string; sort?: "count"; format?: "yaml" | "json" | "tsv"; total?: boolean; counts?: boolean; active?: boolean } = {}) => this.run(args("properties", kv("file", o.file), kv("path", o.path), kv("name", o.name), kv("sort", o.sort), kv("format", o.format), kv("total", o.total), kv("counts", o.counts), kv("active", o.active)));
propertySet = (name: string, value: string, o: { type?: PropertyType; file?: string; path?: string } = {}) => this.run(args("property:set", kv("name", name), kv("value", value), kv("type", o.type), kv("file", o.file), kv("path", o.path)));
propertyRemove = (name: string, o: { file?: string; path?: string } = {}) => this.run(args("property:remove", kv("name", name), kv("file", o.file), kv("path", o.path)));
propertyRead = (name: string, o: { file?: string; path?: string } = {}) => this.run(args("property:read", kv("name", name), kv("file", o.file), kv("path", o.path)));
// Publish
publishSite = () => this.run(["publish:site"]);
publishList = (o: { total?: boolean } = {}) => this.run(args("publish:list", kv("total", o.total)));
publishStatus = (o: { total?: boolean; new?: boolean; changed?: boolean; deleted?: boolean } = {}) => this.run(args("publish:status", kv("total", o.total), kv("new", o.new), kv("changed", o.changed), kv("deleted", o.deleted)));
publishAdd = (o: { file?: string; path?: string; changed?: boolean } = {}) => this.run(args("publish:add", kv("file", o.file), kv("path", o.path), kv("changed", o.changed)));
publishRemove = (o: { file?: string; path?: string } = {}) => this.run(args("publish:remove", kv("file", o.file), kv("path", o.path)));
publishOpen = (o: { file?: string; path?: string } = {}) => this.run(args("publish:open", kv("file", o.file), kv("path", o.path)));
// Random / search
random = (o: { folder?: string; newtab?: boolean } = {}) => this.run(args("random", kv("folder", o.folder), kv("newtab", o.newtab)));
randomRead = (o: { folder?: string } = {}) => this.run(args("random:read", kv("folder", o.folder)));
search = (query: string, o: { path?: string; limit?: number; format?: "text" | "json"; total?: boolean; case?: boolean } = {}) => this.run(args("search", kv("query", query), kv("path", o.path), kv("limit", o.limit), kv("format", o.format), kv("total", o.total), kv("case", o.case)));
searchContext = (query: string, o: { path?: string; limit?: number; format?: "text" | "json"; case?: boolean } = {}) => this.run(args("search:context", kv("query", query), kv("path", o.path), kv("limit", o.limit), kv("format", o.format), kv("case", o.case)));
searchOpen = (o: { query?: string } = {}) => this.run(args("search:open", kv("query", o.query)));
// Sync
sync = (o: { on?: boolean; off?: boolean } = {}) => this.run(args("sync", kv("on", o.on), kv("off", o.off)));
syncStatus = () => this.run(["sync:status"]);
syncHistory = (o: { file?: string; path?: string; total?: boolean } = {}) => this.run(args("sync:history", kv("file", o.file), kv("path", o.path), kv("total", o.total)));
syncRead = (version: number, o: { file?: string; path?: string } = {}) => this.run(args("sync:read", kv("file", o.file), kv("path", o.path), kv("version", version)));
syncRestore = (version: number, o: { file?: string; path?: string } = {}) => this.run(args("sync:restore", kv("file", o.file), kv("path", o.path), kv("version", version)));
syncOpen = (o: { file?: string; path?: string } = {}) => this.run(args("sync:open", kv("file", o.file), kv("path", o.path)));
syncDeleted = (o: { total?: boolean } = {}) => this.run(args("sync:deleted", kv("total", o.total)));
// Tags / tasks
tags = (o: { file?: string; path?: string; sort?: "count"; total?: boolean; counts?: boolean; format?: "json" | "tsv" | "csv"; active?: boolean } = {}) => this.run(args("tags", kv("file", o.file), kv("path", o.path), kv("sort", o.sort), kv("total", o.total), kv("counts", o.counts), kv("format", o.format), kv("active", o.active)));
tag = (name: string, o: { total?: boolean; verbose?: boolean } = {}) => this.run(args("tag", kv("name", name), kv("total", o.total), kv("verbose", o.verbose)));
tasks = (o: { file?: string; path?: string; status?: string; total?: boolean; done?: boolean; todo?: boolean; verbose?: boolean; format?: "json" | "tsv" | "csv"; active?: boolean; daily?: boolean } = {}) => this.run(args("tasks", kv("file", o.file), kv("path", o.path), kv("status", o.status), kv("total", o.total), kv("done", o.done), kv("todo", o.todo), kv("verbose", o.verbose), kv("format", o.format), kv("active", o.active), kv("daily", o.daily)));
task = (o: { ref?: string; file?: string; path?: string; line?: number; status?: string; toggle?: boolean; daily?: boolean; done?: boolean; todo?: boolean } = {}) => this.run(args("task", kv("ref", o.ref), kv("file", o.file), kv("path", o.path), kv("line", o.line), kv("status", o.status), kv("toggle", o.toggle), kv("daily", o.daily), kv("done", o.done), kv("todo", o.todo)));
// Templates / themes / snippets
templates = (o: { total?: boolean } = {}) => this.run(args("templates", kv("total", o.total)));
templateRead = (name: string, o: { title?: string; resolve?: boolean } = {}) => this.run(args("template:read", kv("name", name), kv("title", o.title), kv("resolve", o.resolve)));
templateInsert = (name: string) => this.run(["template:insert", kv("name", name)!]);
themes = (o: { versions?: boolean } = {}) => this.run(args("themes", kv("versions", o.versions)));
theme = (o: { name?: string } = {}) => this.run(args("theme", kv("name", o.name)));
themeSet = (name: string) => this.run(["theme:set", kv("name", name)!]);
themeInstall = (name: string, o: { enable?: boolean } = {}) => this.run(args("theme:install", kv("name", name), kv("enable", o.enable)));
themeUninstall = (name: string) => this.run(["theme:uninstall", kv("name", name)!]);
snippets = () => this.run(["snippets"]);
snippetsEnabled = () => this.run(["snippets:enabled"]);
snippetEnable = (name: string) => this.run(["snippet:enable", kv("name", name)!]);
snippetDisable = (name: string) => this.run(["snippet:disable", kv("name", name)!]);
// Unique / vault / web / wordcount / workspace
unique = (o: { name?: string; content?: string; paneType?: PaneType; open?: boolean } = {}) => this.run(args("unique", kv("name", o.name), kv("content", o.content), kv("paneType", o.paneType), kv("open", o.open)));
vault = (o: { info?: "name" | "path" | "files" | "folders" | "size" } = {}) => this.run(args("vault", kv("info", o.info)));
vaults = (o: { total?: boolean; verbose?: boolean } = {}) => this.run(args("vaults", kv("total", o.total), kv("verbose", o.verbose)));
vaultOpen = (name: string) => this.run(["vault:open", kv("name", name)!]);
web = (url: string, o: { newtab?: boolean } = {}) => this.run(args("web", kv("url", url), kv("newtab", o.newtab)));
wordcount = (o: { file?: string; path?: string; words?: boolean; characters?: boolean } = {}) => this.run(args("wordcount", kv("file", o.file), kv("path", o.path), kv("words", o.words), kv("characters", o.characters)));
workspace = (o: { ids?: boolean } = {}) => this.run(args("workspace", kv("ids", o.ids)));
workspaces = (o: { total?: boolean } = {}) => this.run(args("workspaces", kv("total", o.total)));
workspaceSave = (name: string) => this.run(["workspace:save", kv("name", name)!]);
workspaceLoad = (name: string) => this.run(["workspace:load", kv("name", name)!]);
workspaceDelete = (name: string) => this.run(["workspace:delete", kv("name", name)!]);
tabs = (o: { ids?: boolean } = {}) => this.run(args("tabs", kv("ids", o.ids)));
tabOpen = (o: { group?: string; file?: string; view?: string } = {}) => this.run(args("tab:open", kv("group", o.group), kv("file", o.file), kv("view", o.view)));
recents = (o: { total?: boolean } = {}) => this.run(args("recents", kv("total", o.total)));
// Developer commands
devtools = () => this.run(["devtools"]);
devDebug = (o: { on?: boolean; off?: boolean } = {}) => this.run(args("dev:debug", kv("on", o.on), kv("off", o.off)));
devCdp = (method: string, o: { params?: string } = {}) => this.run(args("dev:cdp", kv("method", method), kv("params", o.params)));
devErrors = (o: { clear?: boolean } = {}) => this.run(args("dev:errors", kv("clear", o.clear)));
devScreenshot = (o: { path?: string } = {}) => this.run(args("dev:screenshot", kv("path", o.path)));
devConsole = (o: { limit?: number; level?: LogLevel; clear?: boolean } = {}) => this.run(args("dev:console", kv("limit", o.limit), kv("level", o.level), kv("clear", o.clear)));
devCss = (selector: string, o: { prop?: string } = {}) => this.run(args("dev:css", kv("selector", selector), kv("prop", o.prop)));
devDom = (selector: string, o: { attr?: string; css?: string; total?: boolean; text?: boolean; inner?: boolean; all?: boolean } = {}) => this.run(args("dev:dom", kv("selector", selector), kv("attr", o.attr), kv("css", o.css), kv("total", o.total), kv("text", o.text), kv("inner", o.inner), kv("all", o.all)));
devMobile = (o: { on?: boolean; off?: boolean } = {}) => this.run(args("dev:mobile", kv("on", o.on), kv("off", o.off)));
eval = (code: string) => this.run(["eval", kv("code", code)!]);
}
+17
View File
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"lib": ["ES2022", "DOM"],
"declaration": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"noUncheckedIndexedAccess": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src"]
}