datanotes 1.1.1 → 1.2.1
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 +6 -5
- package/SECURITY.md +6 -3
- package/dist/api/index.cjs +24 -21
- package/dist/api/index.js +24 -21
- 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 +54 -58
- package/package.json +5 -9
package/README.md
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
The engine of your second brain: Markdown notes as database tables, for you and your AI agents.
|
|
4
4
|
|
|
5
5
|
A row is any note whose `table` property names a table; tables are defined in
|
|
6
|
-
`Database/database.yaml`. Every write is checked against its table, journaled and undoable.
|
|
7
|
-
|
|
8
|
-
`datanotes-mcp
|
|
6
|
+
`Database/database.yaml`. Every write is checked against its table, journaled and undoable.
|
|
7
|
+
Programs use its HTTP API or the `datanotes/client` library; agents use the separate MCP server
|
|
8
|
+
`datanotes-mcp`.
|
|
9
9
|
|
|
10
10
|
**Documentation: [datanotes.dev](https://datanotes.dev)**
|
|
11
11
|
|
|
@@ -17,7 +17,8 @@ Requires Node.js 20 or newer. Start the engine on a vault:
|
|
|
17
17
|
npx -y datanotes serve --vault /path/to/vault
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
It serves the HTTP API on `http://127.0.0.1:27150
|
|
20
|
+
It serves the HTTP API on `http://127.0.0.1:27150` (routes under `/datanotes/*`, plus the
|
|
21
|
+
Local-REST-API-style `/vault/*` and `/search/simple/`), with the owner's token in
|
|
21
22
|
`<vault>/.datanotes/service.json`. From code:
|
|
22
23
|
|
|
23
24
|
```js
|
|
@@ -37,7 +38,7 @@ server of Datanotes, a separate package built on this API.
|
|
|
37
38
|
- [Connect agents (datanotes-mcp)](https://datanotes.dev/start/connect-agents/)
|
|
38
39
|
- [JavaScript client](https://datanotes.dev/guides/client/)
|
|
39
40
|
- [Apps](https://datanotes.dev/guides/apps/)
|
|
40
|
-
- [Obsidian
|
|
41
|
+
- [Obsidian client](https://datanotes.dev/guides/obsidian/)
|
|
41
42
|
|
|
42
43
|
## Security
|
|
43
44
|
|
package/SECURITY.md
CHANGED
|
@@ -25,7 +25,7 @@ run on your own machine, for you and the agents you choose.
|
|
|
25
25
|
| Credential | Where | What it can do |
|
|
26
26
|
| --- | --- | --- |
|
|
27
27
|
| Owner token | `httpServerToken` in the settings | Everything: data, schema, apps, webhooks, undo. Keep it on the machine. |
|
|
28
|
-
| App token | created with `POST /datanotes/apps/create/` (owner only), stored hashed | Read everything; write only the tables in its `write` patterns; change the schema only with `ddl`. It cannot create webhooks, manage apps or undo; with `maintain` it may also undo, restore, vacuum and
|
|
28
|
+
| App token | created with `POST /datanotes/apps/create/` (owner only), stored hashed | Read everything; write only the tables in its `write` patterns; change the schema only with `ddl`. It cannot create webhooks, manage apps or undo; with `maintain` it may also undo, restore, vacuum, configure and rebuild the semantic index (an agent's own token). |
|
|
29
29
|
|
|
30
30
|
Prefer an **app token per device or agent**: it has only the permissions it needs and the journal
|
|
31
31
|
names it. MCP clients use the same tokens: the MCP server passes them to the engine.
|
|
@@ -44,8 +44,11 @@ followed: do not link to folders you would not give the engine.
|
|
|
44
44
|
|
|
45
45
|
Nothing leaves the machine unless you turn it on:
|
|
46
46
|
|
|
47
|
-
- **Semantic search** sends note text to the embedding provider you configure (`
|
|
48
|
-
off by default). A local provider (Ollama
|
|
47
|
+
- **Semantic search** sends note text to the embedding provider you configure (`semanticEnabled`,
|
|
48
|
+
off by default; provider `semanticProvider` / `semanticBaseUrl`). A local provider (Ollama,
|
|
49
|
+
LM Studio) keeps it on the machine.
|
|
49
50
|
- **Telemetry** sends logs to the OTLP endpoint you configure (`telemetryEnabled`, off by default).
|
|
50
51
|
- **Webhooks** (created by the owner) post change events to the URLs they name.
|
|
52
|
+
- **Apps' MCP endpoints** (registered by the owner): the engine sends each app's `mcp_token`, and
|
|
53
|
+
the arguments and caller of every call of its tools, to the app's `mcp_url`, which may be remote.
|
|
51
54
|
- **Git sync** (`--git-sync`) pushes the vault to its git remote.
|
package/dist/api/index.cjs
CHANGED
|
@@ -41,7 +41,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
41
41
|
var OPERATIONS = [
|
|
42
42
|
{
|
|
43
43
|
"name": "read_note",
|
|
44
|
-
"description": "Read a vault file (
|
|
44
|
+
"description": "Read a vault file (`GET /vault/{path}`). Default: the raw markdown. With `as_json`:\n`{content, frontmatter, tags, path, stat}` (`tags` without `#`, from the text and the\nfrontmatter; `stat`: `{ctime, mtime, size}` or null). A missing file is an error.\n`path`: vault-relative, forward slashes.",
|
|
45
45
|
"inputSchema": {
|
|
46
46
|
"additionalProperties": false,
|
|
47
47
|
"properties": {
|
|
@@ -68,7 +68,7 @@ var OPERATIONS = [
|
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
70
|
"name": "list_directory",
|
|
71
|
-
"description": '
|
|
71
|
+
"description": 'Direct children of a vault folder (`GET /vault/{folder}/`): `{files: [...]}`. Folders end with\n`/`, names are sorted, dot-files are hidden. `""` is the vault root. A missing folder is an\nerror.',
|
|
72
72
|
"inputSchema": {
|
|
73
73
|
"additionalProperties": false,
|
|
74
74
|
"properties": {
|
|
@@ -89,7 +89,7 @@ var OPERATIONS = [
|
|
|
89
89
|
},
|
|
90
90
|
{
|
|
91
91
|
"name": "search_simple",
|
|
92
|
-
"description": "
|
|
92
|
+
"description": "Plain-text search (`POST /search/simple/`): every word of `query` must occur, case- and\naccent-insensitive, in the note's path or text; there are no operators. Returns up to 200\nfiles, best first: `[{filename, score, matches: [{match: {start, end}, context}]}]`, at most 20\nmatches per file. `context_length`: characters of context on each side (default 100, max 1000).",
|
|
93
93
|
"inputSchema": {
|
|
94
94
|
"additionalProperties": false,
|
|
95
95
|
"properties": {
|
|
@@ -116,7 +116,7 @@ var OPERATIONS = [
|
|
|
116
116
|
},
|
|
117
117
|
{
|
|
118
118
|
"name": "list_tables",
|
|
119
|
-
"description": "List every table: `name`, `catalog`, `schema`, `qualified` (`personal.metrics.mood` \u2014 use it\nwherever a table is expected; `metrics.mood` or `mood` also work when unambiguous), `path` and,\nwhen declared, `description` and `aliases` (other words for what it records). Tables are\ndefined in `database.yaml` (catalog \u2192 schema \u2192 table)
|
|
119
|
+
"description": "List every table: `name`, `catalog`, `schema`, `qualified` (`personal.metrics.mood` \u2014 use it\nwherever a table is expected; `metrics.mood` or `mood` also work when unambiguous), `path` and,\nwhen declared, `description` and `aliases` (other words for what it records). Tables are\ndefined in `database.yaml` (catalog \u2192 schema \u2192 table). Returns `{tables, views}`: `views` lists the stored views (same fields, no\npath): select them like tables. `system.information_schema` is read with `select_rows`.\n\nCall this first to find the table for what the user reports (match their words against\nname, description and aliases), and before creating tables or columns so you reuse existing\nones instead of creating duplicates.",
|
|
120
120
|
"inputSchema": {
|
|
121
121
|
"additionalProperties": false,
|
|
122
122
|
"properties": {},
|
|
@@ -236,7 +236,8 @@ var OPERATIONS = [
|
|
|
236
236
|
"type": "null"
|
|
237
237
|
}
|
|
238
238
|
],
|
|
239
|
-
"default": null
|
|
239
|
+
"default": null,
|
|
240
|
+
"description": "Untyped fields of the table (name \u2192 default), kept on every row; a field cannot share a name with a column."
|
|
240
241
|
},
|
|
241
242
|
"body": {
|
|
242
243
|
"anyOf": [
|
|
@@ -344,7 +345,7 @@ var OPERATIONS = [
|
|
|
344
345
|
},
|
|
345
346
|
{
|
|
346
347
|
"name": "rename_column",
|
|
347
|
-
"description": "Rename a column
|
|
348
|
+
"description": "Rename a column in database.yaml and on every row. Refused if `to_column` is already a column\nof the table; a row that has a stray `to_column` key with a different value is a conflict, and\nnothing is written. Views and note text that mention the old name are not rewritten.",
|
|
348
349
|
"inputSchema": {
|
|
349
350
|
"additionalProperties": false,
|
|
350
351
|
"properties": {
|
|
@@ -715,7 +716,7 @@ var OPERATIONS = [
|
|
|
715
716
|
},
|
|
716
717
|
{
|
|
717
718
|
"name": "describe_database",
|
|
718
|
-
"description": "Describe the database: its folder (`root`), `metastore` (`database.yaml`), configuration\n(default catalog and schema, row partition and file-name formats, archive folder, time zone),\njournal folder, `bases` (shared column sets and the tables using them), `catalogs` \u2192 schemas \u2192 tables (
|
|
719
|
+
"description": "Describe the database: its folder (`root`), `metastore` (`database.yaml`), configuration\n(default catalog and schema, row partition and file-name formats, archive folder, time zone),\njournal folder, `bases` (shared column sets and the tables using them), `catalogs` \u2192 `schemas` \u2192 `tables` (with `rows` counts) and `views`.",
|
|
719
720
|
"inputSchema": {
|
|
720
721
|
"additionalProperties": false,
|
|
721
722
|
"properties": {},
|
|
@@ -731,7 +732,7 @@ var OPERATIONS = [
|
|
|
731
732
|
},
|
|
732
733
|
{
|
|
733
734
|
"name": "configure_database",
|
|
734
|
-
"description": 'Configure the database (persisted in database.yaml `config:`): `root` (rename the database\nfolder; links follow; revert by renaming back), `default_catalog`, `default_schema`,\n`default_partition` ("" = flat) and `filename` (date formats for new time-based tables),\n`archive_folder`, `timezone` (IANA; completes date-times written without offset), `history`\n(transaction log for every table \u2014 only on the user\'s request). Returns describe_database
|
|
735
|
+
"description": 'Configure the database (persisted in database.yaml `config:`): `root` (rename the database\nfolder; links follow; revert by renaming back), `default_catalog`, `default_schema`,\n`default_partition` ("" = flat) and `filename` (date formats for new time-based tables),\n`archive_folder`, `timezone` (IANA; completes date-times written without offset), `history`\n(transaction log for every table \u2014 only on the user\'s request). Returns describe_database.\nRenaming `root` moves the folder (links to its notes are rewritten); it is not journaled: to\nrevert, call it again with the old value.',
|
|
735
736
|
"inputSchema": {
|
|
736
737
|
"additionalProperties": false,
|
|
737
738
|
"properties": {
|
|
@@ -906,7 +907,8 @@ var OPERATIONS = [
|
|
|
906
907
|
"type": "null"
|
|
907
908
|
}
|
|
908
909
|
],
|
|
909
|
-
"default": null
|
|
910
|
+
"default": null,
|
|
911
|
+
"description": "Optional note path (vault-relative), instead of the table's file-name pattern; `.md` is added if missing; it must stay inside the vault."
|
|
910
912
|
},
|
|
911
913
|
"dry_run": {
|
|
912
914
|
"default": false,
|
|
@@ -1144,7 +1146,7 @@ var OPERATIONS = [
|
|
|
1144
1146
|
"title": "Update Row",
|
|
1145
1147
|
"readOnlyHint": false,
|
|
1146
1148
|
"destructiveHint": false,
|
|
1147
|
-
"idempotentHint":
|
|
1149
|
+
"idempotentHint": false,
|
|
1148
1150
|
"openWorldHint": false
|
|
1149
1151
|
}
|
|
1150
1152
|
},
|
|
@@ -1298,7 +1300,7 @@ var OPERATIONS = [
|
|
|
1298
1300
|
},
|
|
1299
1301
|
{
|
|
1300
1302
|
"name": "adopt_rows",
|
|
1301
|
-
"description": 'Turn existing notes (not rows of any table yet) into rows of `table`, keeping their body and\nlinks: `rename` keys (`{"data": "occurred_at"}`), `set_values` for all, `per_row` values by\nnote path (`{"Home/x.md": {"zone": "[[Kitchen]]"}}`), `drop` keys the table does not have.\nEvery note gets `table` and a system `id`; with `relocate` (default) it moves to the table\'s\nfolder, named by its filename pattern (
|
|
1303
|
+
"description": 'Turn existing notes (not rows of any table yet) into rows of `table`, keeping their body and\nlinks: `rename` keys (`{"data": "occurred_at"}`), `set_values` for all, `per_row` values by\nnote path (`{"Home/x.md": {"zone": "[[Kitchen]]"}}`), `drop` keys the table does not have.\nEvery note gets `table` and a system `id`; with `relocate` (default) it moves to the table\'s\nfolder, named by its filename pattern (links to it are rewritten). All notes are validated first:\nunknown keys and bad values are listed and nothing is written. Run with `dry_run` first; undo\nwith `undo_migration`.',
|
|
1302
1304
|
"inputSchema": {
|
|
1303
1305
|
"additionalProperties": false,
|
|
1304
1306
|
"properties": {
|
|
@@ -1601,7 +1603,7 @@ var OPERATIONS = [
|
|
|
1601
1603
|
"annotations": {
|
|
1602
1604
|
"title": "Undo Migration",
|
|
1603
1605
|
"readOnlyHint": false,
|
|
1604
|
-
"destructiveHint":
|
|
1606
|
+
"destructiveHint": true,
|
|
1605
1607
|
"idempotentHint": false,
|
|
1606
1608
|
"openWorldHint": false
|
|
1607
1609
|
}
|
|
@@ -1766,7 +1768,7 @@ var OPERATIONS = [
|
|
|
1766
1768
|
},
|
|
1767
1769
|
{
|
|
1768
1770
|
"name": "semantic_search",
|
|
1769
|
-
"description": 'Find notes by meaning, not exact words: `{query, results: [{path, score, heading, snippet,\ntable?}]}`, one result per note (its best passage), best first. Use it for vague questions\n("the dinner where we talked about moving", "what did I decide about the car") and to find the\nrows to read or update; then read_note / select_rows for the details. Filters: `tables`\n(qualified or short names), `folder`, `min_score` (cosine 0\u20131; ~0.5+ is usually relevant).\
|
|
1771
|
+
"description": 'Find notes by meaning, not exact words: `{query, results: [{path, score, heading, snippet,\ntable?}]}`, one result per note (its best passage), best first. Use it for vague questions\n("the dinner where we talked about moving", "what did I decide about the car") and to find the\nrows to read or update; then read_note / select_rows for the details. Filters: `tables`\n(qualified or short names), `folder`, `min_score` (cosine 0\u20131; ~0.5+ is usually relevant).\n`limit`: default 10, max 100. Fails with a clear message when semantic search is not enabled in\nthe engine settings.',
|
|
1770
1772
|
"inputSchema": {
|
|
1771
1773
|
"additionalProperties": false,
|
|
1772
1774
|
"properties": {
|
|
@@ -1829,7 +1831,7 @@ var OPERATIONS = [
|
|
|
1829
1831
|
},
|
|
1830
1832
|
{
|
|
1831
1833
|
"name": "semantic_index",
|
|
1832
|
-
"description": "Semantic search index: `status` (provider
|
|
1834
|
+
"description": "Semantic search index: `status` (`enabled`, `provider`, `model`, `files`, `passages`, `pending`,\n`running`, `lastSync`, `lastError`), `update` (embed notes changed since the last run; the\nengine also does it on its own about ten seconds after edits) or `rebuild` (re-embed\neverything: slow, and costs money with a paid provider \u2014 ask first).",
|
|
1833
1835
|
"inputSchema": {
|
|
1834
1836
|
"additionalProperties": false,
|
|
1835
1837
|
"properties": {
|
|
@@ -1975,7 +1977,7 @@ var OPERATIONS = [
|
|
|
1975
1977
|
},
|
|
1976
1978
|
{
|
|
1977
1979
|
"name": "describe_history",
|
|
1978
|
-
"description": "DESCRIBE HISTORY: the table's versions, newest first \u2014 `version`, `timestamp`, `operation`\n(CREATE TABLE, WRITE, UPDATE, DELETE, MERGE, ALTER TABLE, ADD COLUMNS, RESTORE, UNDO, \u2026;\n`EXTERNAL_EDIT` = changed
|
|
1980
|
+
"description": "DESCRIBE HISTORY: the table's versions, newest first \u2014 `version`, `timestamp`, `operation`\n(CREATE TABLE, WRITE, UPDATE, DELETE, MERGE, ALTER TABLE, ADD COLUMNS, RESTORE, UNDO, \u2026;\n`EXTERNAL_EDIT` = changed outside the engine: an editor, git, sync), `operationMetrics` and `migrationId` (undo).\nWorks for dropped tables too (full `catalog.schema.table`). Use a version with `select_rows`\n(time travel), `table_changes` or `restore_table`.",
|
|
1979
1981
|
"inputSchema": {
|
|
1980
1982
|
"additionalProperties": false,
|
|
1981
1983
|
"properties": {
|
|
@@ -2047,7 +2049,7 @@ var OPERATIONS = [
|
|
|
2047
2049
|
},
|
|
2048
2050
|
{
|
|
2049
2051
|
"name": "list_changes",
|
|
2050
|
-
"description": "Change feed across all tables: numbered row changes (`seq`), oldest first, from engine writes\nand from hand edits (`source: external`). Each has `table`, `op` (insert, update, delete, move,\nschema), `path`, `changes` ({column: {before, after}}), `actor`, `migration`.
|
|
2052
|
+
"description": "Change feed across all tables: numbered row changes (`seq`), oldest first, from engine writes\nand from hand edits (`source: external`). Each has `table`, `op` (insert, update, delete, move,\nschema), `path`, `changes` ({column: {before, after}}), `actor`, `migration`. Agents' tool calls (reported by MCP servers) are `op: tool_call` events (`call`: tool, args, ok, value, servedBy), returned only when `ops` includes tool_call. Pass the returned\n`last` as `since` next time (`head` is the newest number); `wait_ms` (max 60000) waits for a new change (long poll); `limit` max 5000. `gap: true` means\nolder changes were dropped: re-read the tables. `where` filters rows as in select_rows (on the values after the change). Per-table versions: `table_changes`.",
|
|
2051
2053
|
"inputSchema": {
|
|
2052
2054
|
"additionalProperties": false,
|
|
2053
2055
|
"properties": {
|
|
@@ -2573,7 +2575,7 @@ var OPERATIONS = [
|
|
|
2573
2575
|
},
|
|
2574
2576
|
{
|
|
2575
2577
|
"name": "clone_table",
|
|
2576
|
-
"description": "CREATE TABLE `target` CLONE `source`: same definition plus independent copies of its rows\n(deep clone), optionally as of a `version` / `timestamp`. `shallow=true` copies the\ndefinition only (CREATE TABLE LIKE). `target`: `catalog.schema.table`, `schema.table` (
|
|
2578
|
+
"description": "CREATE TABLE `target` CLONE `source`: same definition plus independent copies of its rows\n(deep clone), optionally as of a `version` / `timestamp`. `shallow=true` copies the\ndefinition only (CREATE TABLE LIKE). `target`: `catalog.schema.table`, `schema.table` (in the\nsource's catalog) or a name (source's catalog and schema). Useful for experiments, e.g. a sandbox copy\nin the `agent` catalog.",
|
|
2577
2579
|
"inputSchema": {
|
|
2578
2580
|
"additionalProperties": false,
|
|
2579
2581
|
"properties": {
|
|
@@ -2630,7 +2632,7 @@ var OPERATIONS = [
|
|
|
2630
2632
|
},
|
|
2631
2633
|
{
|
|
2632
2634
|
"name": "recompute_columns",
|
|
2633
|
-
"description": "Bring generated and identity columns up to date (all rows, or only `paths`), e.g. after\nrows were edited
|
|
2635
|
+
"description": "Bring generated and identity columns up to date (all rows, or only `paths`), e.g. after\nrows were edited by hand (an editor, git, sync) or a generated expression changed with `alter_column`.\nAlso gives rows without an `id` a new one, and a duplicated note (same `id` as an older row)\na new id. `new_ids=true` regenerates every id in scope (new ones on every call): only on the user's\nexplicit request.\n`check_table` reports stale values with code `generated`. No-op when nothing is stale;\notherwise journaled (undo_migration) and a RECOMPUTE version in the table's history.",
|
|
2634
2636
|
"inputSchema": {
|
|
2635
2637
|
"additionalProperties": false,
|
|
2636
2638
|
"properties": {
|
|
@@ -2669,7 +2671,7 @@ var OPERATIONS = [
|
|
|
2669
2671
|
"title": "Recompute Columns",
|
|
2670
2672
|
"readOnlyHint": false,
|
|
2671
2673
|
"destructiveHint": false,
|
|
2672
|
-
"idempotentHint":
|
|
2674
|
+
"idempotentHint": false,
|
|
2673
2675
|
"openWorldHint": false
|
|
2674
2676
|
}
|
|
2675
2677
|
},
|
|
@@ -2834,7 +2836,7 @@ var OPERATIONS = [
|
|
|
2834
2836
|
},
|
|
2835
2837
|
{
|
|
2836
2838
|
"name": "graph_path",
|
|
2837
|
-
"description": 'Shortest chain of links between two sets of notes (each `{"paths": [...]}` and/or\n`{"table", "where"}`): `found`, `length` and the steps (`from`, `label`, `direction`, `to`).\n`direction` both by default; `max_depth` default 6 (max 10).',
|
|
2839
|
+
"description": 'Shortest chain of links between two sets of notes (each `{"paths": [...]}` and/or\n`{"table", "where"}`): `found`, `length` and `path`: the steps (`from`, `label`, `direction`, `to`).\n`direction` both by default; `max_depth` default 6 (max 10).',
|
|
2838
2840
|
"inputSchema": {
|
|
2839
2841
|
"additionalProperties": false,
|
|
2840
2842
|
"properties": {
|
|
@@ -2972,7 +2974,8 @@ var ROUTES = {
|
|
|
2972
2974
|
create_webhook: (a) => ["/webhooks/create/", pick(a, ["url", "tables", "ops", "columns", "exclude_actor", "where", "since"])],
|
|
2973
2975
|
delete_webhook: (a) => ["/webhooks/delete/", pick(a, ["id"])],
|
|
2974
2976
|
restore_table: (a) => ["/history/restore/", { ...pick(a, ["table", "version", "timestamp"]), ...dry(a) }],
|
|
2975
|
-
|
|
2977
|
+
// vacuum deletes history for good: a dry run unless the caller says `dry_run: false`
|
|
2978
|
+
vacuum_table: (a) => ["/history/vacuum/", { ...pick(a, ["table", "retain_from_version"]), dry_run: a.dry_run !== false }],
|
|
2976
2979
|
graph_neighbors: (a) => ["/graph/neighbors/", compact({ from: a.from_nodes, direction: a.direction, edges: a.edges })],
|
|
2977
2980
|
graph_traverse: (a) => [
|
|
2978
2981
|
"/graph/traverse/",
|
package/dist/api/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
var OPERATIONS = [
|
|
3
3
|
{
|
|
4
4
|
"name": "read_note",
|
|
5
|
-
"description": "Read a vault file (
|
|
5
|
+
"description": "Read a vault file (`GET /vault/{path}`). Default: the raw markdown. With `as_json`:\n`{content, frontmatter, tags, path, stat}` (`tags` without `#`, from the text and the\nfrontmatter; `stat`: `{ctime, mtime, size}` or null). A missing file is an error.\n`path`: vault-relative, forward slashes.",
|
|
6
6
|
"inputSchema": {
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"properties": {
|
|
@@ -29,7 +29,7 @@ var OPERATIONS = [
|
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
"name": "list_directory",
|
|
32
|
-
"description": '
|
|
32
|
+
"description": 'Direct children of a vault folder (`GET /vault/{folder}/`): `{files: [...]}`. Folders end with\n`/`, names are sorted, dot-files are hidden. `""` is the vault root. A missing folder is an\nerror.',
|
|
33
33
|
"inputSchema": {
|
|
34
34
|
"additionalProperties": false,
|
|
35
35
|
"properties": {
|
|
@@ -50,7 +50,7 @@ var OPERATIONS = [
|
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
52
|
"name": "search_simple",
|
|
53
|
-
"description": "
|
|
53
|
+
"description": "Plain-text search (`POST /search/simple/`): every word of `query` must occur, case- and\naccent-insensitive, in the note's path or text; there are no operators. Returns up to 200\nfiles, best first: `[{filename, score, matches: [{match: {start, end}, context}]}]`, at most 20\nmatches per file. `context_length`: characters of context on each side (default 100, max 1000).",
|
|
54
54
|
"inputSchema": {
|
|
55
55
|
"additionalProperties": false,
|
|
56
56
|
"properties": {
|
|
@@ -77,7 +77,7 @@ var OPERATIONS = [
|
|
|
77
77
|
},
|
|
78
78
|
{
|
|
79
79
|
"name": "list_tables",
|
|
80
|
-
"description": "List every table: `name`, `catalog`, `schema`, `qualified` (`personal.metrics.mood` \u2014 use it\nwherever a table is expected; `metrics.mood` or `mood` also work when unambiguous), `path` and,\nwhen declared, `description` and `aliases` (other words for what it records). Tables are\ndefined in `database.yaml` (catalog \u2192 schema \u2192 table)
|
|
80
|
+
"description": "List every table: `name`, `catalog`, `schema`, `qualified` (`personal.metrics.mood` \u2014 use it\nwherever a table is expected; `metrics.mood` or `mood` also work when unambiguous), `path` and,\nwhen declared, `description` and `aliases` (other words for what it records). Tables are\ndefined in `database.yaml` (catalog \u2192 schema \u2192 table). Returns `{tables, views}`: `views` lists the stored views (same fields, no\npath): select them like tables. `system.information_schema` is read with `select_rows`.\n\nCall this first to find the table for what the user reports (match their words against\nname, description and aliases), and before creating tables or columns so you reuse existing\nones instead of creating duplicates.",
|
|
81
81
|
"inputSchema": {
|
|
82
82
|
"additionalProperties": false,
|
|
83
83
|
"properties": {},
|
|
@@ -197,7 +197,8 @@ var OPERATIONS = [
|
|
|
197
197
|
"type": "null"
|
|
198
198
|
}
|
|
199
199
|
],
|
|
200
|
-
"default": null
|
|
200
|
+
"default": null,
|
|
201
|
+
"description": "Untyped fields of the table (name \u2192 default), kept on every row; a field cannot share a name with a column."
|
|
201
202
|
},
|
|
202
203
|
"body": {
|
|
203
204
|
"anyOf": [
|
|
@@ -305,7 +306,7 @@ var OPERATIONS = [
|
|
|
305
306
|
},
|
|
306
307
|
{
|
|
307
308
|
"name": "rename_column",
|
|
308
|
-
"description": "Rename a column
|
|
309
|
+
"description": "Rename a column in database.yaml and on every row. Refused if `to_column` is already a column\nof the table; a row that has a stray `to_column` key with a different value is a conflict, and\nnothing is written. Views and note text that mention the old name are not rewritten.",
|
|
309
310
|
"inputSchema": {
|
|
310
311
|
"additionalProperties": false,
|
|
311
312
|
"properties": {
|
|
@@ -676,7 +677,7 @@ var OPERATIONS = [
|
|
|
676
677
|
},
|
|
677
678
|
{
|
|
678
679
|
"name": "describe_database",
|
|
679
|
-
"description": "Describe the database: its folder (`root`), `metastore` (`database.yaml`), configuration\n(default catalog and schema, row partition and file-name formats, archive folder, time zone),\njournal folder, `bases` (shared column sets and the tables using them), `catalogs` \u2192 schemas \u2192 tables (
|
|
680
|
+
"description": "Describe the database: its folder (`root`), `metastore` (`database.yaml`), configuration\n(default catalog and schema, row partition and file-name formats, archive folder, time zone),\njournal folder, `bases` (shared column sets and the tables using them), `catalogs` \u2192 `schemas` \u2192 `tables` (with `rows` counts) and `views`.",
|
|
680
681
|
"inputSchema": {
|
|
681
682
|
"additionalProperties": false,
|
|
682
683
|
"properties": {},
|
|
@@ -692,7 +693,7 @@ var OPERATIONS = [
|
|
|
692
693
|
},
|
|
693
694
|
{
|
|
694
695
|
"name": "configure_database",
|
|
695
|
-
"description": 'Configure the database (persisted in database.yaml `config:`): `root` (rename the database\nfolder; links follow; revert by renaming back), `default_catalog`, `default_schema`,\n`default_partition` ("" = flat) and `filename` (date formats for new time-based tables),\n`archive_folder`, `timezone` (IANA; completes date-times written without offset), `history`\n(transaction log for every table \u2014 only on the user\'s request). Returns describe_database
|
|
696
|
+
"description": 'Configure the database (persisted in database.yaml `config:`): `root` (rename the database\nfolder; links follow; revert by renaming back), `default_catalog`, `default_schema`,\n`default_partition` ("" = flat) and `filename` (date formats for new time-based tables),\n`archive_folder`, `timezone` (IANA; completes date-times written without offset), `history`\n(transaction log for every table \u2014 only on the user\'s request). Returns describe_database.\nRenaming `root` moves the folder (links to its notes are rewritten); it is not journaled: to\nrevert, call it again with the old value.',
|
|
696
697
|
"inputSchema": {
|
|
697
698
|
"additionalProperties": false,
|
|
698
699
|
"properties": {
|
|
@@ -867,7 +868,8 @@ var OPERATIONS = [
|
|
|
867
868
|
"type": "null"
|
|
868
869
|
}
|
|
869
870
|
],
|
|
870
|
-
"default": null
|
|
871
|
+
"default": null,
|
|
872
|
+
"description": "Optional note path (vault-relative), instead of the table's file-name pattern; `.md` is added if missing; it must stay inside the vault."
|
|
871
873
|
},
|
|
872
874
|
"dry_run": {
|
|
873
875
|
"default": false,
|
|
@@ -1105,7 +1107,7 @@ var OPERATIONS = [
|
|
|
1105
1107
|
"title": "Update Row",
|
|
1106
1108
|
"readOnlyHint": false,
|
|
1107
1109
|
"destructiveHint": false,
|
|
1108
|
-
"idempotentHint":
|
|
1110
|
+
"idempotentHint": false,
|
|
1109
1111
|
"openWorldHint": false
|
|
1110
1112
|
}
|
|
1111
1113
|
},
|
|
@@ -1259,7 +1261,7 @@ var OPERATIONS = [
|
|
|
1259
1261
|
},
|
|
1260
1262
|
{
|
|
1261
1263
|
"name": "adopt_rows",
|
|
1262
|
-
"description": 'Turn existing notes (not rows of any table yet) into rows of `table`, keeping their body and\nlinks: `rename` keys (`{"data": "occurred_at"}`), `set_values` for all, `per_row` values by\nnote path (`{"Home/x.md": {"zone": "[[Kitchen]]"}}`), `drop` keys the table does not have.\nEvery note gets `table` and a system `id`; with `relocate` (default) it moves to the table\'s\nfolder, named by its filename pattern (
|
|
1264
|
+
"description": 'Turn existing notes (not rows of any table yet) into rows of `table`, keeping their body and\nlinks: `rename` keys (`{"data": "occurred_at"}`), `set_values` for all, `per_row` values by\nnote path (`{"Home/x.md": {"zone": "[[Kitchen]]"}}`), `drop` keys the table does not have.\nEvery note gets `table` and a system `id`; with `relocate` (default) it moves to the table\'s\nfolder, named by its filename pattern (links to it are rewritten). All notes are validated first:\nunknown keys and bad values are listed and nothing is written. Run with `dry_run` first; undo\nwith `undo_migration`.',
|
|
1263
1265
|
"inputSchema": {
|
|
1264
1266
|
"additionalProperties": false,
|
|
1265
1267
|
"properties": {
|
|
@@ -1562,7 +1564,7 @@ var OPERATIONS = [
|
|
|
1562
1564
|
"annotations": {
|
|
1563
1565
|
"title": "Undo Migration",
|
|
1564
1566
|
"readOnlyHint": false,
|
|
1565
|
-
"destructiveHint":
|
|
1567
|
+
"destructiveHint": true,
|
|
1566
1568
|
"idempotentHint": false,
|
|
1567
1569
|
"openWorldHint": false
|
|
1568
1570
|
}
|
|
@@ -1727,7 +1729,7 @@ var OPERATIONS = [
|
|
|
1727
1729
|
},
|
|
1728
1730
|
{
|
|
1729
1731
|
"name": "semantic_search",
|
|
1730
|
-
"description": 'Find notes by meaning, not exact words: `{query, results: [{path, score, heading, snippet,\ntable?}]}`, one result per note (its best passage), best first. Use it for vague questions\n("the dinner where we talked about moving", "what did I decide about the car") and to find the\nrows to read or update; then read_note / select_rows for the details. Filters: `tables`\n(qualified or short names), `folder`, `min_score` (cosine 0\u20131; ~0.5+ is usually relevant).\
|
|
1732
|
+
"description": 'Find notes by meaning, not exact words: `{query, results: [{path, score, heading, snippet,\ntable?}]}`, one result per note (its best passage), best first. Use it for vague questions\n("the dinner where we talked about moving", "what did I decide about the car") and to find the\nrows to read or update; then read_note / select_rows for the details. Filters: `tables`\n(qualified or short names), `folder`, `min_score` (cosine 0\u20131; ~0.5+ is usually relevant).\n`limit`: default 10, max 100. Fails with a clear message when semantic search is not enabled in\nthe engine settings.',
|
|
1731
1733
|
"inputSchema": {
|
|
1732
1734
|
"additionalProperties": false,
|
|
1733
1735
|
"properties": {
|
|
@@ -1790,7 +1792,7 @@ var OPERATIONS = [
|
|
|
1790
1792
|
},
|
|
1791
1793
|
{
|
|
1792
1794
|
"name": "semantic_index",
|
|
1793
|
-
"description": "Semantic search index: `status` (provider
|
|
1795
|
+
"description": "Semantic search index: `status` (`enabled`, `provider`, `model`, `files`, `passages`, `pending`,\n`running`, `lastSync`, `lastError`), `update` (embed notes changed since the last run; the\nengine also does it on its own about ten seconds after edits) or `rebuild` (re-embed\neverything: slow, and costs money with a paid provider \u2014 ask first).",
|
|
1794
1796
|
"inputSchema": {
|
|
1795
1797
|
"additionalProperties": false,
|
|
1796
1798
|
"properties": {
|
|
@@ -1936,7 +1938,7 @@ var OPERATIONS = [
|
|
|
1936
1938
|
},
|
|
1937
1939
|
{
|
|
1938
1940
|
"name": "describe_history",
|
|
1939
|
-
"description": "DESCRIBE HISTORY: the table's versions, newest first \u2014 `version`, `timestamp`, `operation`\n(CREATE TABLE, WRITE, UPDATE, DELETE, MERGE, ALTER TABLE, ADD COLUMNS, RESTORE, UNDO, \u2026;\n`EXTERNAL_EDIT` = changed
|
|
1941
|
+
"description": "DESCRIBE HISTORY: the table's versions, newest first \u2014 `version`, `timestamp`, `operation`\n(CREATE TABLE, WRITE, UPDATE, DELETE, MERGE, ALTER TABLE, ADD COLUMNS, RESTORE, UNDO, \u2026;\n`EXTERNAL_EDIT` = changed outside the engine: an editor, git, sync), `operationMetrics` and `migrationId` (undo).\nWorks for dropped tables too (full `catalog.schema.table`). Use a version with `select_rows`\n(time travel), `table_changes` or `restore_table`.",
|
|
1940
1942
|
"inputSchema": {
|
|
1941
1943
|
"additionalProperties": false,
|
|
1942
1944
|
"properties": {
|
|
@@ -2008,7 +2010,7 @@ var OPERATIONS = [
|
|
|
2008
2010
|
},
|
|
2009
2011
|
{
|
|
2010
2012
|
"name": "list_changes",
|
|
2011
|
-
"description": "Change feed across all tables: numbered row changes (`seq`), oldest first, from engine writes\nand from hand edits (`source: external`). Each has `table`, `op` (insert, update, delete, move,\nschema), `path`, `changes` ({column: {before, after}}), `actor`, `migration`.
|
|
2013
|
+
"description": "Change feed across all tables: numbered row changes (`seq`), oldest first, from engine writes\nand from hand edits (`source: external`). Each has `table`, `op` (insert, update, delete, move,\nschema), `path`, `changes` ({column: {before, after}}), `actor`, `migration`. Agents' tool calls (reported by MCP servers) are `op: tool_call` events (`call`: tool, args, ok, value, servedBy), returned only when `ops` includes tool_call. Pass the returned\n`last` as `since` next time (`head` is the newest number); `wait_ms` (max 60000) waits for a new change (long poll); `limit` max 5000. `gap: true` means\nolder changes were dropped: re-read the tables. `where` filters rows as in select_rows (on the values after the change). Per-table versions: `table_changes`.",
|
|
2012
2014
|
"inputSchema": {
|
|
2013
2015
|
"additionalProperties": false,
|
|
2014
2016
|
"properties": {
|
|
@@ -2534,7 +2536,7 @@ var OPERATIONS = [
|
|
|
2534
2536
|
},
|
|
2535
2537
|
{
|
|
2536
2538
|
"name": "clone_table",
|
|
2537
|
-
"description": "CREATE TABLE `target` CLONE `source`: same definition plus independent copies of its rows\n(deep clone), optionally as of a `version` / `timestamp`. `shallow=true` copies the\ndefinition only (CREATE TABLE LIKE). `target`: `catalog.schema.table`, `schema.table` (
|
|
2539
|
+
"description": "CREATE TABLE `target` CLONE `source`: same definition plus independent copies of its rows\n(deep clone), optionally as of a `version` / `timestamp`. `shallow=true` copies the\ndefinition only (CREATE TABLE LIKE). `target`: `catalog.schema.table`, `schema.table` (in the\nsource's catalog) or a name (source's catalog and schema). Useful for experiments, e.g. a sandbox copy\nin the `agent` catalog.",
|
|
2538
2540
|
"inputSchema": {
|
|
2539
2541
|
"additionalProperties": false,
|
|
2540
2542
|
"properties": {
|
|
@@ -2591,7 +2593,7 @@ var OPERATIONS = [
|
|
|
2591
2593
|
},
|
|
2592
2594
|
{
|
|
2593
2595
|
"name": "recompute_columns",
|
|
2594
|
-
"description": "Bring generated and identity columns up to date (all rows, or only `paths`), e.g. after\nrows were edited
|
|
2596
|
+
"description": "Bring generated and identity columns up to date (all rows, or only `paths`), e.g. after\nrows were edited by hand (an editor, git, sync) or a generated expression changed with `alter_column`.\nAlso gives rows without an `id` a new one, and a duplicated note (same `id` as an older row)\na new id. `new_ids=true` regenerates every id in scope (new ones on every call): only on the user's\nexplicit request.\n`check_table` reports stale values with code `generated`. No-op when nothing is stale;\notherwise journaled (undo_migration) and a RECOMPUTE version in the table's history.",
|
|
2595
2597
|
"inputSchema": {
|
|
2596
2598
|
"additionalProperties": false,
|
|
2597
2599
|
"properties": {
|
|
@@ -2630,7 +2632,7 @@ var OPERATIONS = [
|
|
|
2630
2632
|
"title": "Recompute Columns",
|
|
2631
2633
|
"readOnlyHint": false,
|
|
2632
2634
|
"destructiveHint": false,
|
|
2633
|
-
"idempotentHint":
|
|
2635
|
+
"idempotentHint": false,
|
|
2634
2636
|
"openWorldHint": false
|
|
2635
2637
|
}
|
|
2636
2638
|
},
|
|
@@ -2795,7 +2797,7 @@ var OPERATIONS = [
|
|
|
2795
2797
|
},
|
|
2796
2798
|
{
|
|
2797
2799
|
"name": "graph_path",
|
|
2798
|
-
"description": 'Shortest chain of links between two sets of notes (each `{"paths": [...]}` and/or\n`{"table", "where"}`): `found`, `length` and the steps (`from`, `label`, `direction`, `to`).\n`direction` both by default; `max_depth` default 6 (max 10).',
|
|
2800
|
+
"description": 'Shortest chain of links between two sets of notes (each `{"paths": [...]}` and/or\n`{"table", "where"}`): `found`, `length` and `path`: the steps (`from`, `label`, `direction`, `to`).\n`direction` both by default; `max_depth` default 6 (max 10).',
|
|
2799
2801
|
"inputSchema": {
|
|
2800
2802
|
"additionalProperties": false,
|
|
2801
2803
|
"properties": {
|
|
@@ -2933,7 +2935,8 @@ var ROUTES = {
|
|
|
2933
2935
|
create_webhook: (a) => ["/webhooks/create/", pick(a, ["url", "tables", "ops", "columns", "exclude_actor", "where", "since"])],
|
|
2934
2936
|
delete_webhook: (a) => ["/webhooks/delete/", pick(a, ["id"])],
|
|
2935
2937
|
restore_table: (a) => ["/history/restore/", { ...pick(a, ["table", "version", "timestamp"]), ...dry(a) }],
|
|
2936
|
-
|
|
2938
|
+
// vacuum deletes history for good: a dry run unless the caller says `dry_run: false`
|
|
2939
|
+
vacuum_table: (a) => ["/history/vacuum/", { ...pick(a, ["table", "retain_from_version"]), dry_run: a.dry_run !== false }],
|
|
2937
2940
|
graph_neighbors: (a) => ["/graph/neighbors/", compact({ from: a.from_nodes, direction: a.direction, edges: a.edges })],
|
|
2938
2941
|
graph_traverse: (a) => [
|
|
2939
2942
|
"/graph/traverse/",
|
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": {
|
|
@@ -7833,10 +7824,6 @@ var LockHolder = class {
|
|
|
7833
7824
|
async write() {
|
|
7834
7825
|
this.info.heartbeat = (/* @__PURE__ */ new Date()).toISOString();
|
|
7835
7826
|
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
7827
|
await this.adapter.mkdir(this.storeFolder);
|
|
7841
7828
|
await this.adapter.write(lockPath(this.storeFolder), JSON.stringify(this.info));
|
|
7842
7829
|
} catch {
|
|
@@ -10104,7 +10091,7 @@ async function tableStateAt(app, settings, full, asOf2 = {}) {
|
|
|
10104
10091
|
if (versions.length === 0) {
|
|
10105
10092
|
if (asOf2.version !== void 0 || asOf2.timestamp !== void 0) {
|
|
10106
10093
|
throw new HistoryError(
|
|
10107
|
-
`${full} has no history: time travel is off for it (turn it on with configure_database(history=true) or alter_table(history=
|
|
10094
|
+
`${full} has no history: time travel is off for it (turn it on with configure_database(history=true) or alter_table(history="on"))`
|
|
10108
10095
|
);
|
|
10109
10096
|
}
|
|
10110
10097
|
return emptyState(full);
|
|
@@ -15023,7 +15010,7 @@ var SemanticIndex = class {
|
|
|
15023
15010
|
}
|
|
15024
15011
|
requireEnabled() {
|
|
15025
15012
|
if (!this.getSettings().semanticEnabled) {
|
|
15026
|
-
throw new OperationInputError("semantic search is off:
|
|
15013
|
+
throw new OperationInputError("semantic search is off: set semanticEnabled, semanticProvider and semanticModel in the engine settings");
|
|
15027
15014
|
}
|
|
15028
15015
|
}
|
|
15029
15016
|
async load() {
|
|
@@ -15071,7 +15058,7 @@ var SemanticIndex = class {
|
|
|
15071
15058
|
/** Notes in scope: every markdown note, or only table rows; minus excluded folders and archives. */
|
|
15072
15059
|
inScope(f, table) {
|
|
15073
15060
|
const s = this.getSettings();
|
|
15074
|
-
if (f.path.includes("/_archive/")) return false;
|
|
15061
|
+
if (f.path.startsWith("_archive/") || f.path.includes("/_archive/")) return false;
|
|
15075
15062
|
if (s.semanticExclude.some((dir) => f.path === dir || f.path.startsWith(`${dir.replace(/\/+$/, "")}/`))) return false;
|
|
15076
15063
|
if (s.semanticScope === "tables" && !table) return false;
|
|
15077
15064
|
return true;
|
|
@@ -15650,13 +15637,13 @@ function createHandlers(app, getSettings, saveSettings = async () => {
|
|
|
15650
15637
|
},
|
|
15651
15638
|
"/tables/describe/": async (body) => ({ status: 200, json: await describeTable(app, s(), str(body, "table")) }),
|
|
15652
15639
|
"/ddl/": ddl,
|
|
15653
|
-
// The
|
|
15654
|
-
//
|
|
15640
|
+
// The engine's API itself, method by method (camelCase inputs as in public-api.ts), for the
|
|
15641
|
+
// owner: `remote-api.ts` is its client.
|
|
15655
15642
|
"/api/": async (body) => {
|
|
15656
15643
|
const method = str(body, "method");
|
|
15657
15644
|
const args = list(body, "args") ?? [];
|
|
15658
15645
|
const fn = pub[method];
|
|
15659
|
-
if (typeof fn !== "function" || API_EXCLUDED.has(method)) throw new BadRequest(`"method" must be a method of the
|
|
15646
|
+
if (typeof fn !== "function" || API_EXCLUDED.has(method)) throw new BadRequest(`"method" must be a method of the engine API`);
|
|
15660
15647
|
const result = await fn.apply(pub, args);
|
|
15661
15648
|
return { status: 200, json: { result: result === void 0 ? null : result } };
|
|
15662
15649
|
},
|
|
@@ -16214,7 +16201,7 @@ async function simpleSearch(app, query, contextLength = 100) {
|
|
|
16214
16201
|
}
|
|
16215
16202
|
return out.sort((a, b) => b.score - a.score || a.filename.localeCompare(b.filename)).slice(0, 200);
|
|
16216
16203
|
}
|
|
16217
|
-
var
|
|
16204
|
+
var HttpApiServer = class {
|
|
16218
16205
|
constructor(opts) {
|
|
16219
16206
|
this.opts = opts;
|
|
16220
16207
|
this.server = null;
|
|
@@ -16223,7 +16210,7 @@ var PluginHttpServer = class {
|
|
|
16223
16210
|
}
|
|
16224
16211
|
/**
|
|
16225
16212
|
* Resolves with the bound port once listening. A busy port is retried a few times: when the
|
|
16226
|
-
*
|
|
16213
|
+
* engine restarts, the previous instance may still be closing.
|
|
16227
16214
|
*/
|
|
16228
16215
|
async start(retries = 6, delayMs = 500) {
|
|
16229
16216
|
for (let i = 0; ; i++) {
|
|
@@ -17550,7 +17537,7 @@ function checkPatterns(patterns) {
|
|
|
17550
17537
|
var OPERATIONS = [
|
|
17551
17538
|
{
|
|
17552
17539
|
"name": "read_note",
|
|
17553
|
-
"description": "Read a vault file (
|
|
17540
|
+
"description": "Read a vault file (`GET /vault/{path}`). Default: the raw markdown. With `as_json`:\n`{content, frontmatter, tags, path, stat}` (`tags` without `#`, from the text and the\nfrontmatter; `stat`: `{ctime, mtime, size}` or null). A missing file is an error.\n`path`: vault-relative, forward slashes.",
|
|
17554
17541
|
"inputSchema": {
|
|
17555
17542
|
"additionalProperties": false,
|
|
17556
17543
|
"properties": {
|
|
@@ -17577,7 +17564,7 @@ var OPERATIONS = [
|
|
|
17577
17564
|
},
|
|
17578
17565
|
{
|
|
17579
17566
|
"name": "list_directory",
|
|
17580
|
-
"description": '
|
|
17567
|
+
"description": 'Direct children of a vault folder (`GET /vault/{folder}/`): `{files: [...]}`. Folders end with\n`/`, names are sorted, dot-files are hidden. `""` is the vault root. A missing folder is an\nerror.',
|
|
17581
17568
|
"inputSchema": {
|
|
17582
17569
|
"additionalProperties": false,
|
|
17583
17570
|
"properties": {
|
|
@@ -17598,7 +17585,7 @@ var OPERATIONS = [
|
|
|
17598
17585
|
},
|
|
17599
17586
|
{
|
|
17600
17587
|
"name": "search_simple",
|
|
17601
|
-
"description": "
|
|
17588
|
+
"description": "Plain-text search (`POST /search/simple/`): every word of `query` must occur, case- and\naccent-insensitive, in the note's path or text; there are no operators. Returns up to 200\nfiles, best first: `[{filename, score, matches: [{match: {start, end}, context}]}]`, at most 20\nmatches per file. `context_length`: characters of context on each side (default 100, max 1000).",
|
|
17602
17589
|
"inputSchema": {
|
|
17603
17590
|
"additionalProperties": false,
|
|
17604
17591
|
"properties": {
|
|
@@ -17625,7 +17612,7 @@ var OPERATIONS = [
|
|
|
17625
17612
|
},
|
|
17626
17613
|
{
|
|
17627
17614
|
"name": "list_tables",
|
|
17628
|
-
"description": "List every table: `name`, `catalog`, `schema`, `qualified` (`personal.metrics.mood` \u2014 use it\nwherever a table is expected; `metrics.mood` or `mood` also work when unambiguous), `path` and,\nwhen declared, `description` and `aliases` (other words for what it records). Tables are\ndefined in `database.yaml` (catalog \u2192 schema \u2192 table)
|
|
17615
|
+
"description": "List every table: `name`, `catalog`, `schema`, `qualified` (`personal.metrics.mood` \u2014 use it\nwherever a table is expected; `metrics.mood` or `mood` also work when unambiguous), `path` and,\nwhen declared, `description` and `aliases` (other words for what it records). Tables are\ndefined in `database.yaml` (catalog \u2192 schema \u2192 table). Returns `{tables, views}`: `views` lists the stored views (same fields, no\npath): select them like tables. `system.information_schema` is read with `select_rows`.\n\nCall this first to find the table for what the user reports (match their words against\nname, description and aliases), and before creating tables or columns so you reuse existing\nones instead of creating duplicates.",
|
|
17629
17616
|
"inputSchema": {
|
|
17630
17617
|
"additionalProperties": false,
|
|
17631
17618
|
"properties": {},
|
|
@@ -17745,7 +17732,8 @@ var OPERATIONS = [
|
|
|
17745
17732
|
"type": "null"
|
|
17746
17733
|
}
|
|
17747
17734
|
],
|
|
17748
|
-
"default": null
|
|
17735
|
+
"default": null,
|
|
17736
|
+
"description": "Untyped fields of the table (name \u2192 default), kept on every row; a field cannot share a name with a column."
|
|
17749
17737
|
},
|
|
17750
17738
|
"body": {
|
|
17751
17739
|
"anyOf": [
|
|
@@ -17853,7 +17841,7 @@ var OPERATIONS = [
|
|
|
17853
17841
|
},
|
|
17854
17842
|
{
|
|
17855
17843
|
"name": "rename_column",
|
|
17856
|
-
"description": "Rename a column
|
|
17844
|
+
"description": "Rename a column in database.yaml and on every row. Refused if `to_column` is already a column\nof the table; a row that has a stray `to_column` key with a different value is a conflict, and\nnothing is written. Views and note text that mention the old name are not rewritten.",
|
|
17857
17845
|
"inputSchema": {
|
|
17858
17846
|
"additionalProperties": false,
|
|
17859
17847
|
"properties": {
|
|
@@ -18224,7 +18212,7 @@ var OPERATIONS = [
|
|
|
18224
18212
|
},
|
|
18225
18213
|
{
|
|
18226
18214
|
"name": "describe_database",
|
|
18227
|
-
"description": "Describe the database: its folder (`root`), `metastore` (`database.yaml`), configuration\n(default catalog and schema, row partition and file-name formats, archive folder, time zone),\njournal folder, `bases` (shared column sets and the tables using them), `catalogs` \u2192 schemas \u2192 tables (
|
|
18215
|
+
"description": "Describe the database: its folder (`root`), `metastore` (`database.yaml`), configuration\n(default catalog and schema, row partition and file-name formats, archive folder, time zone),\njournal folder, `bases` (shared column sets and the tables using them), `catalogs` \u2192 `schemas` \u2192 `tables` (with `rows` counts) and `views`.",
|
|
18228
18216
|
"inputSchema": {
|
|
18229
18217
|
"additionalProperties": false,
|
|
18230
18218
|
"properties": {},
|
|
@@ -18240,7 +18228,7 @@ var OPERATIONS = [
|
|
|
18240
18228
|
},
|
|
18241
18229
|
{
|
|
18242
18230
|
"name": "configure_database",
|
|
18243
|
-
"description": 'Configure the database (persisted in database.yaml `config:`): `root` (rename the database\nfolder; links follow; revert by renaming back), `default_catalog`, `default_schema`,\n`default_partition` ("" = flat) and `filename` (date formats for new time-based tables),\n`archive_folder`, `timezone` (IANA; completes date-times written without offset), `history`\n(transaction log for every table \u2014 only on the user\'s request). Returns describe_database
|
|
18231
|
+
"description": 'Configure the database (persisted in database.yaml `config:`): `root` (rename the database\nfolder; links follow; revert by renaming back), `default_catalog`, `default_schema`,\n`default_partition` ("" = flat) and `filename` (date formats for new time-based tables),\n`archive_folder`, `timezone` (IANA; completes date-times written without offset), `history`\n(transaction log for every table \u2014 only on the user\'s request). Returns describe_database.\nRenaming `root` moves the folder (links to its notes are rewritten); it is not journaled: to\nrevert, call it again with the old value.',
|
|
18244
18232
|
"inputSchema": {
|
|
18245
18233
|
"additionalProperties": false,
|
|
18246
18234
|
"properties": {
|
|
@@ -18415,7 +18403,8 @@ var OPERATIONS = [
|
|
|
18415
18403
|
"type": "null"
|
|
18416
18404
|
}
|
|
18417
18405
|
],
|
|
18418
|
-
"default": null
|
|
18406
|
+
"default": null,
|
|
18407
|
+
"description": "Optional note path (vault-relative), instead of the table's file-name pattern; `.md` is added if missing; it must stay inside the vault."
|
|
18419
18408
|
},
|
|
18420
18409
|
"dry_run": {
|
|
18421
18410
|
"default": false,
|
|
@@ -18653,7 +18642,7 @@ var OPERATIONS = [
|
|
|
18653
18642
|
"title": "Update Row",
|
|
18654
18643
|
"readOnlyHint": false,
|
|
18655
18644
|
"destructiveHint": false,
|
|
18656
|
-
"idempotentHint":
|
|
18645
|
+
"idempotentHint": false,
|
|
18657
18646
|
"openWorldHint": false
|
|
18658
18647
|
}
|
|
18659
18648
|
},
|
|
@@ -18807,7 +18796,7 @@ var OPERATIONS = [
|
|
|
18807
18796
|
},
|
|
18808
18797
|
{
|
|
18809
18798
|
"name": "adopt_rows",
|
|
18810
|
-
"description": 'Turn existing notes (not rows of any table yet) into rows of `table`, keeping their body and\nlinks: `rename` keys (`{"data": "occurred_at"}`), `set_values` for all, `per_row` values by\nnote path (`{"Home/x.md": {"zone": "[[Kitchen]]"}}`), `drop` keys the table does not have.\nEvery note gets `table` and a system `id`; with `relocate` (default) it moves to the table\'s\nfolder, named by its filename pattern (
|
|
18799
|
+
"description": 'Turn existing notes (not rows of any table yet) into rows of `table`, keeping their body and\nlinks: `rename` keys (`{"data": "occurred_at"}`), `set_values` for all, `per_row` values by\nnote path (`{"Home/x.md": {"zone": "[[Kitchen]]"}}`), `drop` keys the table does not have.\nEvery note gets `table` and a system `id`; with `relocate` (default) it moves to the table\'s\nfolder, named by its filename pattern (links to it are rewritten). All notes are validated first:\nunknown keys and bad values are listed and nothing is written. Run with `dry_run` first; undo\nwith `undo_migration`.',
|
|
18811
18800
|
"inputSchema": {
|
|
18812
18801
|
"additionalProperties": false,
|
|
18813
18802
|
"properties": {
|
|
@@ -19110,7 +19099,7 @@ var OPERATIONS = [
|
|
|
19110
19099
|
"annotations": {
|
|
19111
19100
|
"title": "Undo Migration",
|
|
19112
19101
|
"readOnlyHint": false,
|
|
19113
|
-
"destructiveHint":
|
|
19102
|
+
"destructiveHint": true,
|
|
19114
19103
|
"idempotentHint": false,
|
|
19115
19104
|
"openWorldHint": false
|
|
19116
19105
|
}
|
|
@@ -19275,7 +19264,7 @@ var OPERATIONS = [
|
|
|
19275
19264
|
},
|
|
19276
19265
|
{
|
|
19277
19266
|
"name": "semantic_search",
|
|
19278
|
-
"description": 'Find notes by meaning, not exact words: `{query, results: [{path, score, heading, snippet,\ntable?}]}`, one result per note (its best passage), best first. Use it for vague questions\n("the dinner where we talked about moving", "what did I decide about the car") and to find the\nrows to read or update; then read_note / select_rows for the details. Filters: `tables`\n(qualified or short names), `folder`, `min_score` (cosine 0\u20131; ~0.5+ is usually relevant).\
|
|
19267
|
+
"description": 'Find notes by meaning, not exact words: `{query, results: [{path, score, heading, snippet,\ntable?}]}`, one result per note (its best passage), best first. Use it for vague questions\n("the dinner where we talked about moving", "what did I decide about the car") and to find the\nrows to read or update; then read_note / select_rows for the details. Filters: `tables`\n(qualified or short names), `folder`, `min_score` (cosine 0\u20131; ~0.5+ is usually relevant).\n`limit`: default 10, max 100. Fails with a clear message when semantic search is not enabled in\nthe engine settings.',
|
|
19279
19268
|
"inputSchema": {
|
|
19280
19269
|
"additionalProperties": false,
|
|
19281
19270
|
"properties": {
|
|
@@ -19338,7 +19327,7 @@ var OPERATIONS = [
|
|
|
19338
19327
|
},
|
|
19339
19328
|
{
|
|
19340
19329
|
"name": "semantic_index",
|
|
19341
|
-
"description": "Semantic search index: `status` (provider
|
|
19330
|
+
"description": "Semantic search index: `status` (`enabled`, `provider`, `model`, `files`, `passages`, `pending`,\n`running`, `lastSync`, `lastError`), `update` (embed notes changed since the last run; the\nengine also does it on its own about ten seconds after edits) or `rebuild` (re-embed\neverything: slow, and costs money with a paid provider \u2014 ask first).",
|
|
19342
19331
|
"inputSchema": {
|
|
19343
19332
|
"additionalProperties": false,
|
|
19344
19333
|
"properties": {
|
|
@@ -19484,7 +19473,7 @@ var OPERATIONS = [
|
|
|
19484
19473
|
},
|
|
19485
19474
|
{
|
|
19486
19475
|
"name": "describe_history",
|
|
19487
|
-
"description": "DESCRIBE HISTORY: the table's versions, newest first \u2014 `version`, `timestamp`, `operation`\n(CREATE TABLE, WRITE, UPDATE, DELETE, MERGE, ALTER TABLE, ADD COLUMNS, RESTORE, UNDO, \u2026;\n`EXTERNAL_EDIT` = changed
|
|
19476
|
+
"description": "DESCRIBE HISTORY: the table's versions, newest first \u2014 `version`, `timestamp`, `operation`\n(CREATE TABLE, WRITE, UPDATE, DELETE, MERGE, ALTER TABLE, ADD COLUMNS, RESTORE, UNDO, \u2026;\n`EXTERNAL_EDIT` = changed outside the engine: an editor, git, sync), `operationMetrics` and `migrationId` (undo).\nWorks for dropped tables too (full `catalog.schema.table`). Use a version with `select_rows`\n(time travel), `table_changes` or `restore_table`.",
|
|
19488
19477
|
"inputSchema": {
|
|
19489
19478
|
"additionalProperties": false,
|
|
19490
19479
|
"properties": {
|
|
@@ -19556,7 +19545,7 @@ var OPERATIONS = [
|
|
|
19556
19545
|
},
|
|
19557
19546
|
{
|
|
19558
19547
|
"name": "list_changes",
|
|
19559
|
-
"description": "Change feed across all tables: numbered row changes (`seq`), oldest first, from engine writes\nand from hand edits (`source: external`). Each has `table`, `op` (insert, update, delete, move,\nschema), `path`, `changes` ({column: {before, after}}), `actor`, `migration`.
|
|
19548
|
+
"description": "Change feed across all tables: numbered row changes (`seq`), oldest first, from engine writes\nand from hand edits (`source: external`). Each has `table`, `op` (insert, update, delete, move,\nschema), `path`, `changes` ({column: {before, after}}), `actor`, `migration`. Agents' tool calls (reported by MCP servers) are `op: tool_call` events (`call`: tool, args, ok, value, servedBy), returned only when `ops` includes tool_call. Pass the returned\n`last` as `since` next time (`head` is the newest number); `wait_ms` (max 60000) waits for a new change (long poll); `limit` max 5000. `gap: true` means\nolder changes were dropped: re-read the tables. `where` filters rows as in select_rows (on the values after the change). Per-table versions: `table_changes`.",
|
|
19560
19549
|
"inputSchema": {
|
|
19561
19550
|
"additionalProperties": false,
|
|
19562
19551
|
"properties": {
|
|
@@ -20082,7 +20071,7 @@ var OPERATIONS = [
|
|
|
20082
20071
|
},
|
|
20083
20072
|
{
|
|
20084
20073
|
"name": "clone_table",
|
|
20085
|
-
"description": "CREATE TABLE `target` CLONE `source`: same definition plus independent copies of its rows\n(deep clone), optionally as of a `version` / `timestamp`. `shallow=true` copies the\ndefinition only (CREATE TABLE LIKE). `target`: `catalog.schema.table`, `schema.table` (
|
|
20074
|
+
"description": "CREATE TABLE `target` CLONE `source`: same definition plus independent copies of its rows\n(deep clone), optionally as of a `version` / `timestamp`. `shallow=true` copies the\ndefinition only (CREATE TABLE LIKE). `target`: `catalog.schema.table`, `schema.table` (in the\nsource's catalog) or a name (source's catalog and schema). Useful for experiments, e.g. a sandbox copy\nin the `agent` catalog.",
|
|
20086
20075
|
"inputSchema": {
|
|
20087
20076
|
"additionalProperties": false,
|
|
20088
20077
|
"properties": {
|
|
@@ -20139,7 +20128,7 @@ var OPERATIONS = [
|
|
|
20139
20128
|
},
|
|
20140
20129
|
{
|
|
20141
20130
|
"name": "recompute_columns",
|
|
20142
|
-
"description": "Bring generated and identity columns up to date (all rows, or only `paths`), e.g. after\nrows were edited
|
|
20131
|
+
"description": "Bring generated and identity columns up to date (all rows, or only `paths`), e.g. after\nrows were edited by hand (an editor, git, sync) or a generated expression changed with `alter_column`.\nAlso gives rows without an `id` a new one, and a duplicated note (same `id` as an older row)\na new id. `new_ids=true` regenerates every id in scope (new ones on every call): only on the user's\nexplicit request.\n`check_table` reports stale values with code `generated`. No-op when nothing is stale;\notherwise journaled (undo_migration) and a RECOMPUTE version in the table's history.",
|
|
20143
20132
|
"inputSchema": {
|
|
20144
20133
|
"additionalProperties": false,
|
|
20145
20134
|
"properties": {
|
|
@@ -20178,7 +20167,7 @@ var OPERATIONS = [
|
|
|
20178
20167
|
"title": "Recompute Columns",
|
|
20179
20168
|
"readOnlyHint": false,
|
|
20180
20169
|
"destructiveHint": false,
|
|
20181
|
-
"idempotentHint":
|
|
20170
|
+
"idempotentHint": false,
|
|
20182
20171
|
"openWorldHint": false
|
|
20183
20172
|
}
|
|
20184
20173
|
},
|
|
@@ -20343,7 +20332,7 @@ var OPERATIONS = [
|
|
|
20343
20332
|
},
|
|
20344
20333
|
{
|
|
20345
20334
|
"name": "graph_path",
|
|
20346
|
-
"description": 'Shortest chain of links between two sets of notes (each `{"paths": [...]}` and/or\n`{"table", "where"}`): `found`, `length` and the steps (`from`, `label`, `direction`, `to`).\n`direction` both by default; `max_depth` default 6 (max 10).',
|
|
20335
|
+
"description": 'Shortest chain of links between two sets of notes (each `{"paths": [...]}` and/or\n`{"table", "where"}`): `found`, `length` and `path`: the steps (`from`, `label`, `direction`, `to`).\n`direction` both by default; `max_depth` default 6 (max 10).',
|
|
20347
20336
|
"inputSchema": {
|
|
20348
20337
|
"additionalProperties": false,
|
|
20349
20338
|
"properties": {
|
|
@@ -20481,7 +20470,8 @@ var ROUTES = {
|
|
|
20481
20470
|
create_webhook: (a) => ["/webhooks/create/", pick(a, ["url", "tables", "ops", "columns", "exclude_actor", "where", "since"])],
|
|
20482
20471
|
delete_webhook: (a) => ["/webhooks/delete/", pick(a, ["id"])],
|
|
20483
20472
|
restore_table: (a) => ["/history/restore/", { ...pick(a, ["table", "version", "timestamp"]), ...dry(a) }],
|
|
20484
|
-
|
|
20473
|
+
// vacuum deletes history for good: a dry run unless the caller says `dry_run: false`
|
|
20474
|
+
vacuum_table: (a) => ["/history/vacuum/", { ...pick(a, ["table", "retain_from_version"]), dry_run: a.dry_run !== false }],
|
|
20485
20475
|
graph_neighbors: (a) => ["/graph/neighbors/", compact({ from: a.from_nodes, direction: a.direction, edges: a.edges })],
|
|
20486
20476
|
graph_traverse: (a) => [
|
|
20487
20477
|
"/graph/traverse/",
|
|
@@ -20775,7 +20765,7 @@ var EngineRuntime = class {
|
|
|
20775
20765
|
transport: async (url, body) => this.o.postLogs(url, body),
|
|
20776
20766
|
resource: { "service.version": this.o.version, "host.name": this.o.hostName }
|
|
20777
20767
|
});
|
|
20778
|
-
const source = this.o.hostName
|
|
20768
|
+
const source = this.o.hostName;
|
|
20779
20769
|
const fileLog = new FileLogger({ enabled: () => s().fileLogEnabled, write: (day, lines) => appendLog(this.o.app, s(), day, lines, source) });
|
|
20780
20770
|
const tee = new TeeTelemetry([logger, fileLog]);
|
|
20781
20771
|
setTelemetry(tee);
|
|
@@ -20813,7 +20803,6 @@ var EngineRuntime = class {
|
|
|
20813
20803
|
this.httpServer = null;
|
|
20814
20804
|
this.httpServerError = null;
|
|
20815
20805
|
const s = this.o.getSettings();
|
|
20816
|
-
if (!s.httpServerEnabled) return;
|
|
20817
20806
|
if (!this.o.canServeHttp) {
|
|
20818
20807
|
this.httpServerError = "the HTTP server runs on desktop only";
|
|
20819
20808
|
return;
|
|
@@ -20824,7 +20813,7 @@ var EngineRuntime = class {
|
|
|
20824
20813
|
}
|
|
20825
20814
|
const handlers = this.handlers();
|
|
20826
20815
|
const guarded = this.apps.guard(dispatch);
|
|
20827
|
-
const server = new
|
|
20816
|
+
const server = new HttpApiServer({
|
|
20828
20817
|
app: this.o.app,
|
|
20829
20818
|
handlers,
|
|
20830
20819
|
token: s.httpServerToken,
|
|
@@ -22080,7 +22069,7 @@ var NodeWatcher = class {
|
|
|
22080
22069
|
};
|
|
22081
22070
|
|
|
22082
22071
|
// src/node/service.ts
|
|
22083
|
-
var VERSION = true ? "1.
|
|
22072
|
+
var VERSION = true ? "1.2.1" : "dev";
|
|
22084
22073
|
function parseArgs(argv, env = process.env) {
|
|
22085
22074
|
const get = (name) => {
|
|
22086
22075
|
const i = argv.indexOf(`--${name}`);
|
|
@@ -22103,8 +22092,6 @@ function parseArgs(argv, env = process.env) {
|
|
|
22103
22092
|
}
|
|
22104
22093
|
function settingsPath(o) {
|
|
22105
22094
|
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
22095
|
return import_node_path5.default.join(o.vault, ".datanotes", "service.json");
|
|
22109
22096
|
}
|
|
22110
22097
|
async function startService(o, env = process.env) {
|
|
@@ -22123,14 +22110,15 @@ async function startService(o, env = process.env) {
|
|
|
22123
22110
|
} catch {
|
|
22124
22111
|
current2 = {};
|
|
22125
22112
|
}
|
|
22126
|
-
const { httpServerEnabled: _forced, ...mine } = settings;
|
|
22127
22113
|
import_node_fs5.default.mkdirSync(import_node_path5.default.dirname(file), { recursive: true });
|
|
22128
|
-
import_node_fs5.default.writeFileSync(file, JSON.stringify({ ...current2, ...
|
|
22114
|
+
import_node_fs5.default.writeFileSync(file, JSON.stringify({ ...current2, ...settings }, null, 2));
|
|
22129
22115
|
};
|
|
22130
|
-
const view = new Proxy(settings, {
|
|
22131
|
-
get: (t, k) => k === "httpServerEnabled" ? true : t[k]
|
|
22132
|
-
});
|
|
22133
22116
|
const host = openNodeVault(o.vault);
|
|
22117
|
+
const held = await readLock(host.vault.adapter, storeRoot(settings));
|
|
22118
|
+
if (isFresh(held) && held.pid !== process.pid && (held.host !== import_node_os.default.hostname() || processAlive(held.pid))) {
|
|
22119
|
+
host.metadataCache.dispose();
|
|
22120
|
+
throw new Error(`another engine holds the vault (pid ${held.pid} on ${held.host}, port ${held.port}): stop it first`);
|
|
22121
|
+
}
|
|
22134
22122
|
const watcher = new NodeWatcher(host.vault, { mode: o.watch, intervalMs: o.pollMs });
|
|
22135
22123
|
const keyFromFile = () => {
|
|
22136
22124
|
if (!o.embeddingKeyFile) return void 0;
|
|
@@ -22143,7 +22131,7 @@ async function startService(o, env = process.env) {
|
|
|
22143
22131
|
const runtime = new EngineRuntime({
|
|
22144
22132
|
...o.host ? { httpHost: o.host } : {},
|
|
22145
22133
|
app: host.app,
|
|
22146
|
-
getSettings: () =>
|
|
22134
|
+
getSettings: () => settings,
|
|
22147
22135
|
saveSettings: save,
|
|
22148
22136
|
transport: async (req) => {
|
|
22149
22137
|
const res = await fetch(req.url, { method: req.method, headers: req.headers, body: req.body });
|
|
@@ -22208,6 +22196,14 @@ async function startService(o, env = process.env) {
|
|
|
22208
22196
|
}
|
|
22209
22197
|
};
|
|
22210
22198
|
}
|
|
22199
|
+
function processAlive(pid) {
|
|
22200
|
+
try {
|
|
22201
|
+
process.kill(pid, 0);
|
|
22202
|
+
return true;
|
|
22203
|
+
} catch (e) {
|
|
22204
|
+
return e.code === "EPERM";
|
|
22205
|
+
}
|
|
22206
|
+
}
|
|
22211
22207
|
async function main(argv = process.argv.slice(2)) {
|
|
22212
22208
|
const li = argv.indexOf("--log");
|
|
22213
22209
|
if (li >= 0 && argv[li + 1]) {
|
|
@@ -22251,7 +22247,7 @@ Docs: https://datanotes.dev`;
|
|
|
22251
22247
|
}
|
|
22252
22248
|
|
|
22253
22249
|
// src/node/cli.ts
|
|
22254
|
-
var VERSION2 = true ? "1.
|
|
22250
|
+
var VERSION2 = true ? "1.2.1" : "dev";
|
|
22255
22251
|
async function main2(argv = process.argv.slice(2)) {
|
|
22256
22252
|
const [command, ...rest] = argv;
|
|
22257
22253
|
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.1",
|
|
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",
|