datanotes 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -22
- package/dist/api/local.cjs +1 -2
- package/dist/api/local.d.ts +2 -2
- package/dist/api/local.js +1 -2
- package/dist/datanotes.cjs +13 -37
- package/package.json +5 -9
package/README.md
CHANGED
|
@@ -4,30 +4,14 @@ The engine of your second brain: Markdown notes as database tables, for you and
|
|
|
4
4
|
|
|
5
5
|
A row is any note whose `table` property names a table; tables are defined in
|
|
6
6
|
`Database/database.yaml`. Every write is checked against its table, journaled and undoable. Agents
|
|
7
|
-
use
|
|
8
|
-
`datanotes
|
|
7
|
+
programs use its HTTP API or the `datanotes/client` library, and agents use the separate MCP
|
|
8
|
+
server `datanotes-mcp`.
|
|
9
9
|
|
|
10
10
|
**Documentation: [datanotes.dev](https://datanotes.dev)**
|
|
11
11
|
|
|
12
12
|
## Quick start
|
|
13
13
|
|
|
14
|
-
Requires Node.js 20 or newer.
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
claude mcp add datanotes -- npx -y datanotes-mcp --vault /path/to/vault
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
Other clients use the same command:
|
|
21
|
-
|
|
22
|
-
```json
|
|
23
|
-
{
|
|
24
|
-
"mcpServers": {
|
|
25
|
-
"datanotes": { "command": "npx", "args": ["-y", "datanotes-mcp", "--vault", "/path/to/vault"] }
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
To keep the engine running for several clients and apps:
|
|
14
|
+
Requires Node.js 20 or newer. Start the engine on a vault:
|
|
31
15
|
|
|
32
16
|
```bash
|
|
33
17
|
npx -y datanotes serve --vault /path/to/vault
|
|
@@ -43,14 +27,17 @@ const db = new DatanotesClient({ url: "http://127.0.0.1:27150", token });
|
|
|
43
27
|
const { value } = await db.call("select_rows", { table: "book", where: { pages: { gt: 300 } } });
|
|
44
28
|
```
|
|
45
29
|
|
|
30
|
+
AI agents connect through [`datanotes-mcp`](https://www.npmjs.com/package/datanotes-mcp), the MCP
|
|
31
|
+
server of Datanotes, a separate package built on this API.
|
|
32
|
+
|
|
46
33
|
## Learn more
|
|
47
34
|
|
|
48
35
|
- [How it works](https://datanotes.dev/concepts/how-it-works/)
|
|
49
|
-
- [
|
|
50
|
-
- [
|
|
36
|
+
- [Operations](https://datanotes.dev/reference/tools/)
|
|
37
|
+
- [Connect agents (datanotes-mcp)](https://datanotes.dev/start/connect-agents/)
|
|
51
38
|
- [JavaScript client](https://datanotes.dev/guides/client/)
|
|
52
39
|
- [Apps](https://datanotes.dev/guides/apps/)
|
|
53
|
-
- [Obsidian
|
|
40
|
+
- [Obsidian client](https://datanotes.dev/guides/obsidian/)
|
|
54
41
|
|
|
55
42
|
## Security
|
|
56
43
|
|
package/dist/api/local.cjs
CHANGED
|
@@ -40,8 +40,7 @@ var import_node_os = __toESM(require("node:os"), 1);
|
|
|
40
40
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
41
41
|
var LOCK_STALE_MS = 3e4;
|
|
42
42
|
function vaultSettingsPath(vault) {
|
|
43
|
-
|
|
44
|
-
return import_node_fs.default.existsSync(plugin) ? plugin : import_node_path.default.join(vault, ".datanotes", "service.json");
|
|
43
|
+
return import_node_path.default.join(vault, ".datanotes", "service.json");
|
|
45
44
|
}
|
|
46
45
|
function readJson(file) {
|
|
47
46
|
try {
|
package/dist/api/local.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
/** The lock an engine writes in its store folder (`<vault>/.datanotes/engine.lock`). */
|
|
6
6
|
export interface EngineLock {
|
|
7
|
-
kind: "service"
|
|
7
|
+
kind: "service";
|
|
8
8
|
pid: number;
|
|
9
9
|
host: string;
|
|
10
10
|
port: number;
|
|
@@ -18,7 +18,7 @@ export interface LocalEngine {
|
|
|
18
18
|
token: string;
|
|
19
19
|
lock: EngineLock;
|
|
20
20
|
}
|
|
21
|
-
/** The settings file of a vault
|
|
21
|
+
/** The settings file of a vault's engine. */
|
|
22
22
|
export declare function vaultSettingsPath(vault: string): string;
|
|
23
23
|
/**
|
|
24
24
|
* The engine holding `vault`, or null when none runs. Throws when the lock is fresh but held on
|
package/dist/api/local.js
CHANGED
|
@@ -4,8 +4,7 @@ import os from "node:os";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
var LOCK_STALE_MS = 3e4;
|
|
6
6
|
function vaultSettingsPath(vault) {
|
|
7
|
-
|
|
8
|
-
return fs.existsSync(plugin) ? plugin : path.join(vault, ".datanotes", "service.json");
|
|
7
|
+
return path.join(vault, ".datanotes", "service.json");
|
|
9
8
|
}
|
|
10
9
|
function readJson(file) {
|
|
11
10
|
try {
|
package/dist/datanotes.cjs
CHANGED
|
@@ -7424,19 +7424,16 @@ var DEFAULT_SETTINGS = {
|
|
|
7424
7424
|
tableProperty: "table",
|
|
7425
7425
|
telemetryEnabled: false,
|
|
7426
7426
|
telemetryEndpoint: "http://127.0.0.1:4318",
|
|
7427
|
-
telemetryServiceName: "
|
|
7428
|
-
restApiEnabled: false,
|
|
7427
|
+
telemetryServiceName: "datanotes",
|
|
7429
7428
|
autoRecompute: true,
|
|
7430
7429
|
viewNotes: true,
|
|
7431
7430
|
semanticEnabled: false,
|
|
7432
7431
|
semanticProvider: "ollama",
|
|
7433
7432
|
semanticBaseUrl: "",
|
|
7434
7433
|
semanticModel: "",
|
|
7435
|
-
semanticApiKeySecret: "",
|
|
7436
7434
|
semanticScope: "vault",
|
|
7437
7435
|
semanticExclude: [],
|
|
7438
7436
|
semanticBatchSize: 32,
|
|
7439
|
-
httpServerEnabled: false,
|
|
7440
7437
|
httpServerPort: 27150,
|
|
7441
7438
|
httpServerToken: ""
|
|
7442
7439
|
};
|
|
@@ -7453,18 +7450,15 @@ var KEYS = [
|
|
|
7453
7450
|
"telemetryEnabled",
|
|
7454
7451
|
"telemetryEndpoint",
|
|
7455
7452
|
"telemetryServiceName",
|
|
7456
|
-
"restApiEnabled",
|
|
7457
7453
|
"autoRecompute",
|
|
7458
7454
|
"viewNotes",
|
|
7459
7455
|
"semanticEnabled",
|
|
7460
7456
|
"semanticProvider",
|
|
7461
7457
|
"semanticBaseUrl",
|
|
7462
7458
|
"semanticModel",
|
|
7463
|
-
"semanticApiKeySecret",
|
|
7464
7459
|
"semanticScope",
|
|
7465
7460
|
"semanticExclude",
|
|
7466
7461
|
"semanticBatchSize",
|
|
7467
|
-
"httpServerEnabled",
|
|
7468
7462
|
"httpServerPort",
|
|
7469
7463
|
"httpServerToken"
|
|
7470
7464
|
];
|
|
@@ -7548,9 +7542,8 @@ function mergeWithDefaults(loaded) {
|
|
|
7548
7542
|
if (typeof raw === "string" && (raw.trim() === "" || /^https?:\/\/\S+$/.test(raw.trim()))) out.semanticBaseUrl = raw.trim().replace(/\/+$/, "");
|
|
7549
7543
|
break;
|
|
7550
7544
|
}
|
|
7551
|
-
case "semanticModel":
|
|
7552
|
-
|
|
7553
|
-
if (typeof raw === "string") out[key] = raw.trim();
|
|
7545
|
+
case "semanticModel": {
|
|
7546
|
+
if (typeof raw === "string") out.semanticModel = raw.trim();
|
|
7554
7547
|
break;
|
|
7555
7548
|
}
|
|
7556
7549
|
case "semanticScope": {
|
|
@@ -7574,9 +7567,7 @@ function mergeWithDefaults(loaded) {
|
|
|
7574
7567
|
break;
|
|
7575
7568
|
}
|
|
7576
7569
|
case "telemetryEnabled":
|
|
7577
|
-
case "httpServerEnabled":
|
|
7578
7570
|
case "fileLogEnabled":
|
|
7579
|
-
case "restApiEnabled":
|
|
7580
7571
|
case "semanticEnabled":
|
|
7581
7572
|
case "autoRecompute":
|
|
7582
7573
|
case "viewNotes": {
|
|
@@ -7802,7 +7793,6 @@ var TeeTelemetry = class {
|
|
|
7802
7793
|
|
|
7803
7794
|
// src/server/engine-lock.ts
|
|
7804
7795
|
var LOCK_PATH_SUFFIX = "engine.lock";
|
|
7805
|
-
var LOCK_STALE_MS = 3e4;
|
|
7806
7796
|
var LOCK_HEARTBEAT_MS = 1e4;
|
|
7807
7797
|
function lockPath(storeFolder) {
|
|
7808
7798
|
return `${storeFolder.replace(/\/+$/, "")}/${LOCK_PATH_SUFFIX}`;
|
|
@@ -7815,9 +7805,6 @@ async function readLock(adapter2, storeFolder) {
|
|
|
7815
7805
|
return null;
|
|
7816
7806
|
}
|
|
7817
7807
|
}
|
|
7818
|
-
function isFresh(lock, now = Date.now()) {
|
|
7819
|
-
return !!lock && now - Date.parse(lock.heartbeat) < LOCK_STALE_MS;
|
|
7820
|
-
}
|
|
7821
7808
|
var LockHolder = class {
|
|
7822
7809
|
constructor(adapter2, storeFolder, info) {
|
|
7823
7810
|
this.adapter = adapter2;
|
|
@@ -7833,10 +7820,6 @@ var LockHolder = class {
|
|
|
7833
7820
|
async write() {
|
|
7834
7821
|
this.info.heartbeat = (/* @__PURE__ */ new Date()).toISOString();
|
|
7835
7822
|
try {
|
|
7836
|
-
if (this.info.kind === "plugin") {
|
|
7837
|
-
const current2 = await readLock(this.adapter, this.storeFolder);
|
|
7838
|
-
if (current2 && current2.kind === "service" && isFresh(current2)) return;
|
|
7839
|
-
}
|
|
7840
7823
|
await this.adapter.mkdir(this.storeFolder);
|
|
7841
7824
|
await this.adapter.write(lockPath(this.storeFolder), JSON.stringify(this.info));
|
|
7842
7825
|
} catch {
|
|
@@ -15650,13 +15633,13 @@ function createHandlers(app, getSettings, saveSettings = async () => {
|
|
|
15650
15633
|
},
|
|
15651
15634
|
"/tables/describe/": async (body) => ({ status: 200, json: await describeTable(app, s(), str(body, "table")) }),
|
|
15652
15635
|
"/ddl/": ddl,
|
|
15653
|
-
// The
|
|
15654
|
-
//
|
|
15636
|
+
// The engine's API itself, method by method (camelCase inputs as in public-api.ts), for the
|
|
15637
|
+
// owner: `remote-api.ts` is its client.
|
|
15655
15638
|
"/api/": async (body) => {
|
|
15656
15639
|
const method = str(body, "method");
|
|
15657
15640
|
const args = list(body, "args") ?? [];
|
|
15658
15641
|
const fn = pub[method];
|
|
15659
|
-
if (typeof fn !== "function" || API_EXCLUDED.has(method)) throw new BadRequest(`"method" must be a method of the
|
|
15642
|
+
if (typeof fn !== "function" || API_EXCLUDED.has(method)) throw new BadRequest(`"method" must be a method of the engine API`);
|
|
15660
15643
|
const result = await fn.apply(pub, args);
|
|
15661
15644
|
return { status: 200, json: { result: result === void 0 ? null : result } };
|
|
15662
15645
|
},
|
|
@@ -16214,7 +16197,7 @@ async function simpleSearch(app, query, contextLength = 100) {
|
|
|
16214
16197
|
}
|
|
16215
16198
|
return out.sort((a, b) => b.score - a.score || a.filename.localeCompare(b.filename)).slice(0, 200);
|
|
16216
16199
|
}
|
|
16217
|
-
var
|
|
16200
|
+
var HttpApiServer = class {
|
|
16218
16201
|
constructor(opts) {
|
|
16219
16202
|
this.opts = opts;
|
|
16220
16203
|
this.server = null;
|
|
@@ -20775,7 +20758,7 @@ var EngineRuntime = class {
|
|
|
20775
20758
|
transport: async (url, body) => this.o.postLogs(url, body),
|
|
20776
20759
|
resource: { "service.version": this.o.version, "host.name": this.o.hostName }
|
|
20777
20760
|
});
|
|
20778
|
-
const source = this.o.hostName
|
|
20761
|
+
const source = this.o.hostName;
|
|
20779
20762
|
const fileLog = new FileLogger({ enabled: () => s().fileLogEnabled, write: (day, lines) => appendLog(this.o.app, s(), day, lines, source) });
|
|
20780
20763
|
const tee = new TeeTelemetry([logger, fileLog]);
|
|
20781
20764
|
setTelemetry(tee);
|
|
@@ -20813,7 +20796,6 @@ var EngineRuntime = class {
|
|
|
20813
20796
|
this.httpServer = null;
|
|
20814
20797
|
this.httpServerError = null;
|
|
20815
20798
|
const s = this.o.getSettings();
|
|
20816
|
-
if (!s.httpServerEnabled) return;
|
|
20817
20799
|
if (!this.o.canServeHttp) {
|
|
20818
20800
|
this.httpServerError = "the HTTP server runs on desktop only";
|
|
20819
20801
|
return;
|
|
@@ -20824,7 +20806,7 @@ var EngineRuntime = class {
|
|
|
20824
20806
|
}
|
|
20825
20807
|
const handlers = this.handlers();
|
|
20826
20808
|
const guarded = this.apps.guard(dispatch);
|
|
20827
|
-
const server = new
|
|
20809
|
+
const server = new HttpApiServer({
|
|
20828
20810
|
app: this.o.app,
|
|
20829
20811
|
handlers,
|
|
20830
20812
|
token: s.httpServerToken,
|
|
@@ -22080,7 +22062,7 @@ var NodeWatcher = class {
|
|
|
22080
22062
|
};
|
|
22081
22063
|
|
|
22082
22064
|
// src/node/service.ts
|
|
22083
|
-
var VERSION = true ? "1.
|
|
22065
|
+
var VERSION = true ? "1.2.0" : "dev";
|
|
22084
22066
|
function parseArgs(argv, env = process.env) {
|
|
22085
22067
|
const get = (name) => {
|
|
22086
22068
|
const i = argv.indexOf(`--${name}`);
|
|
@@ -22103,8 +22085,6 @@ function parseArgs(argv, env = process.env) {
|
|
|
22103
22085
|
}
|
|
22104
22086
|
function settingsPath(o) {
|
|
22105
22087
|
if (o.settingsFile) return import_node_path5.default.resolve(o.settingsFile);
|
|
22106
|
-
const plugin = import_node_path5.default.join(o.vault, ".obsidian", "plugins", "datanotes", "data.json");
|
|
22107
|
-
if (import_node_fs5.default.existsSync(plugin)) return plugin;
|
|
22108
22088
|
return import_node_path5.default.join(o.vault, ".datanotes", "service.json");
|
|
22109
22089
|
}
|
|
22110
22090
|
async function startService(o, env = process.env) {
|
|
@@ -22123,13 +22103,9 @@ async function startService(o, env = process.env) {
|
|
|
22123
22103
|
} catch {
|
|
22124
22104
|
current2 = {};
|
|
22125
22105
|
}
|
|
22126
|
-
const { httpServerEnabled: _forced, ...mine } = settings;
|
|
22127
22106
|
import_node_fs5.default.mkdirSync(import_node_path5.default.dirname(file), { recursive: true });
|
|
22128
|
-
import_node_fs5.default.writeFileSync(file, JSON.stringify({ ...current2, ...
|
|
22107
|
+
import_node_fs5.default.writeFileSync(file, JSON.stringify({ ...current2, ...settings }, null, 2));
|
|
22129
22108
|
};
|
|
22130
|
-
const view = new Proxy(settings, {
|
|
22131
|
-
get: (t, k) => k === "httpServerEnabled" ? true : t[k]
|
|
22132
|
-
});
|
|
22133
22109
|
const host = openNodeVault(o.vault);
|
|
22134
22110
|
const watcher = new NodeWatcher(host.vault, { mode: o.watch, intervalMs: o.pollMs });
|
|
22135
22111
|
const keyFromFile = () => {
|
|
@@ -22143,7 +22119,7 @@ async function startService(o, env = process.env) {
|
|
|
22143
22119
|
const runtime = new EngineRuntime({
|
|
22144
22120
|
...o.host ? { httpHost: o.host } : {},
|
|
22145
22121
|
app: host.app,
|
|
22146
|
-
getSettings: () =>
|
|
22122
|
+
getSettings: () => settings,
|
|
22147
22123
|
saveSettings: save,
|
|
22148
22124
|
transport: async (req) => {
|
|
22149
22125
|
const res = await fetch(req.url, { method: req.method, headers: req.headers, body: req.body });
|
|
@@ -22251,7 +22227,7 @@ Docs: https://datanotes.dev`;
|
|
|
22251
22227
|
}
|
|
22252
22228
|
|
|
22253
22229
|
// src/node/cli.ts
|
|
22254
|
-
var VERSION2 = true ? "1.
|
|
22230
|
+
var VERSION2 = true ? "1.2.0" : "dev";
|
|
22255
22231
|
async function main2(argv = process.argv.slice(2)) {
|
|
22256
22232
|
const [command, ...rest] = argv;
|
|
22257
22233
|
if (command === "--version" || command === "-v" || command === "version") return void process.stdout.write(`${VERSION2}
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "datanotes",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "The engine of your second brain: Markdown notes as database tables, for you and your agents.",
|
|
5
5
|
"keywords": [
|
|
6
|
-
"obsidian",
|
|
7
6
|
"markdown",
|
|
8
7
|
"database",
|
|
9
8
|
"mcp",
|
|
10
9
|
"model-context-protocol",
|
|
11
10
|
"second-brain",
|
|
12
11
|
"agents",
|
|
13
|
-
"frontmatter"
|
|
12
|
+
"frontmatter",
|
|
13
|
+
"obsidian-vault"
|
|
14
14
|
],
|
|
15
15
|
"homepage": "https://datanotes.dev",
|
|
16
16
|
"bugs": {
|
|
@@ -56,8 +56,6 @@
|
|
|
56
56
|
"node": ">=20"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
|
-
"dev": "node esbuild.config.mjs",
|
|
60
|
-
"build": "node scripts/check-layers.mjs && tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
|
|
61
59
|
"test": "vitest run",
|
|
62
60
|
"test:coverage": "vitest run --coverage",
|
|
63
61
|
"test:watch": "vitest",
|
|
@@ -65,16 +63,14 @@
|
|
|
65
63
|
"check:layers": "node scripts/check-layers.mjs",
|
|
66
64
|
"test:node": "vitest run --config vitest.node.config.ts",
|
|
67
65
|
"build:service": "node scripts/check-layers.mjs && tsc -noEmit -skipLibCheck && node esbuild.service.mjs && npm run build:api",
|
|
68
|
-
"build:client": "node scripts/check-layers.mjs && tsc -noEmit -skipLibCheck && node esbuild.client.mjs",
|
|
69
66
|
"prepack": "npm run build:service",
|
|
70
|
-
"build:api": "tsc -p tsconfig.api.json && node esbuild.api.mjs"
|
|
67
|
+
"build:api": "tsc -p tsconfig.api.json && node esbuild.api.mjs",
|
|
68
|
+
"build": "npm run build:service"
|
|
71
69
|
},
|
|
72
70
|
"devDependencies": {
|
|
73
71
|
"@types/node": "^22.10.0",
|
|
74
72
|
"@vitest/coverage-v8": "^2.1.9",
|
|
75
73
|
"esbuild": "0.25.5",
|
|
76
|
-
"happy-dom": "^20.9.0",
|
|
77
|
-
"obsidian": "1.7.2",
|
|
78
74
|
"tslib": "2.8.1",
|
|
79
75
|
"typescript": "^5.8.3",
|
|
80
76
|
"vitest": "^2.1.9",
|