datanotes 1.2.0 → 1.3.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 +5 -4
- package/SECURITY.md +10 -4
- package/dist/api/index.cjs +131 -21
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +131 -21
- package/dist/api/otlp.d.ts +54 -0
- package/dist/datanotes.cjs +81 -32
- package/package.json +1 -1
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
|
-
|
|
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
|
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,14 @@ 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
|
|
49
|
-
|
|
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.
|
|
50
|
+
- **Telemetry** sends logs to the OTLP endpoint you configure (`telemetryEnabled`, off by default):
|
|
51
|
+
operations, routes, note paths, actors, tool names and error messages (which may quote a value),
|
|
52
|
+
not note bodies. `telemetryHeaders` (e.g. an
|
|
53
|
+
ingestion key) is stored in the settings file; `OTEL_EXPORTER_OTLP_HEADERS` keeps it out.
|
|
50
54
|
- **Webhooks** (created by the owner) post change events to the URLs they name.
|
|
55
|
+
- **Apps' MCP endpoints** (registered by the owner): the engine sends each app's `mcp_token`, and
|
|
56
|
+
the arguments and caller of every call of its tools, to the app's `mcp_url`, which may be remote.
|
|
51
57
|
- **Git sync** (`--git-sync`) pushes the vault to its git remote.
|
package/dist/api/index.cjs
CHANGED
|
@@ -26,6 +26,7 @@ __export(index_exports, {
|
|
|
26
26
|
DatanotesError: () => DatanotesError,
|
|
27
27
|
OPERATIONS: () => OPERATIONS,
|
|
28
28
|
OperationInputError: () => OperationInputError,
|
|
29
|
+
OtlpLogExporter: () => OtlpLogExporter,
|
|
29
30
|
ROUTE_PREFIX: () => ROUTE_PREFIX,
|
|
30
31
|
describeBatch: () => describeBatch,
|
|
31
32
|
describeRefusal: () => describeRefusal,
|
|
@@ -33,6 +34,8 @@ __export(index_exports, {
|
|
|
33
34
|
fetchTransport: () => fetchTransport,
|
|
34
35
|
operationRequest: () => operationRequest,
|
|
35
36
|
operationRoute: () => operationRoute,
|
|
37
|
+
otlpFromEnv: () => otlpFromEnv,
|
|
38
|
+
parseOtlpHeaders: () => parseOtlpHeaders,
|
|
36
39
|
resultOf: () => resultOf
|
|
37
40
|
});
|
|
38
41
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -41,7 +44,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
41
44
|
var OPERATIONS = [
|
|
42
45
|
{
|
|
43
46
|
"name": "read_note",
|
|
44
|
-
"description": "Read a vault file (
|
|
47
|
+
"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
48
|
"inputSchema": {
|
|
46
49
|
"additionalProperties": false,
|
|
47
50
|
"properties": {
|
|
@@ -68,7 +71,7 @@ var OPERATIONS = [
|
|
|
68
71
|
},
|
|
69
72
|
{
|
|
70
73
|
"name": "list_directory",
|
|
71
|
-
"description": '
|
|
74
|
+
"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
75
|
"inputSchema": {
|
|
73
76
|
"additionalProperties": false,
|
|
74
77
|
"properties": {
|
|
@@ -89,7 +92,7 @@ var OPERATIONS = [
|
|
|
89
92
|
},
|
|
90
93
|
{
|
|
91
94
|
"name": "search_simple",
|
|
92
|
-
"description": "
|
|
95
|
+
"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
96
|
"inputSchema": {
|
|
94
97
|
"additionalProperties": false,
|
|
95
98
|
"properties": {
|
|
@@ -116,7 +119,7 @@ var OPERATIONS = [
|
|
|
116
119
|
},
|
|
117
120
|
{
|
|
118
121
|
"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)
|
|
122
|
+
"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
123
|
"inputSchema": {
|
|
121
124
|
"additionalProperties": false,
|
|
122
125
|
"properties": {},
|
|
@@ -236,7 +239,8 @@ var OPERATIONS = [
|
|
|
236
239
|
"type": "null"
|
|
237
240
|
}
|
|
238
241
|
],
|
|
239
|
-
"default": null
|
|
242
|
+
"default": null,
|
|
243
|
+
"description": "Untyped fields of the table (name \u2192 default), kept on every row; a field cannot share a name with a column."
|
|
240
244
|
},
|
|
241
245
|
"body": {
|
|
242
246
|
"anyOf": [
|
|
@@ -344,7 +348,7 @@ var OPERATIONS = [
|
|
|
344
348
|
},
|
|
345
349
|
{
|
|
346
350
|
"name": "rename_column",
|
|
347
|
-
"description": "Rename a column
|
|
351
|
+
"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
352
|
"inputSchema": {
|
|
349
353
|
"additionalProperties": false,
|
|
350
354
|
"properties": {
|
|
@@ -715,7 +719,7 @@ var OPERATIONS = [
|
|
|
715
719
|
},
|
|
716
720
|
{
|
|
717
721
|
"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 (
|
|
722
|
+
"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
723
|
"inputSchema": {
|
|
720
724
|
"additionalProperties": false,
|
|
721
725
|
"properties": {},
|
|
@@ -731,7 +735,7 @@ var OPERATIONS = [
|
|
|
731
735
|
},
|
|
732
736
|
{
|
|
733
737
|
"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
|
|
738
|
+
"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
739
|
"inputSchema": {
|
|
736
740
|
"additionalProperties": false,
|
|
737
741
|
"properties": {
|
|
@@ -906,7 +910,8 @@ var OPERATIONS = [
|
|
|
906
910
|
"type": "null"
|
|
907
911
|
}
|
|
908
912
|
],
|
|
909
|
-
"default": null
|
|
913
|
+
"default": null,
|
|
914
|
+
"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
915
|
},
|
|
911
916
|
"dry_run": {
|
|
912
917
|
"default": false,
|
|
@@ -1144,7 +1149,7 @@ var OPERATIONS = [
|
|
|
1144
1149
|
"title": "Update Row",
|
|
1145
1150
|
"readOnlyHint": false,
|
|
1146
1151
|
"destructiveHint": false,
|
|
1147
|
-
"idempotentHint":
|
|
1152
|
+
"idempotentHint": false,
|
|
1148
1153
|
"openWorldHint": false
|
|
1149
1154
|
}
|
|
1150
1155
|
},
|
|
@@ -1298,7 +1303,7 @@ var OPERATIONS = [
|
|
|
1298
1303
|
},
|
|
1299
1304
|
{
|
|
1300
1305
|
"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 (
|
|
1306
|
+
"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
1307
|
"inputSchema": {
|
|
1303
1308
|
"additionalProperties": false,
|
|
1304
1309
|
"properties": {
|
|
@@ -1601,7 +1606,7 @@ var OPERATIONS = [
|
|
|
1601
1606
|
"annotations": {
|
|
1602
1607
|
"title": "Undo Migration",
|
|
1603
1608
|
"readOnlyHint": false,
|
|
1604
|
-
"destructiveHint":
|
|
1609
|
+
"destructiveHint": true,
|
|
1605
1610
|
"idempotentHint": false,
|
|
1606
1611
|
"openWorldHint": false
|
|
1607
1612
|
}
|
|
@@ -1766,7 +1771,7 @@ var OPERATIONS = [
|
|
|
1766
1771
|
},
|
|
1767
1772
|
{
|
|
1768
1773
|
"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).\
|
|
1774
|
+
"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
1775
|
"inputSchema": {
|
|
1771
1776
|
"additionalProperties": false,
|
|
1772
1777
|
"properties": {
|
|
@@ -1829,7 +1834,7 @@ var OPERATIONS = [
|
|
|
1829
1834
|
},
|
|
1830
1835
|
{
|
|
1831
1836
|
"name": "semantic_index",
|
|
1832
|
-
"description": "Semantic search index: `status` (provider
|
|
1837
|
+
"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
1838
|
"inputSchema": {
|
|
1834
1839
|
"additionalProperties": false,
|
|
1835
1840
|
"properties": {
|
|
@@ -1975,7 +1980,7 @@ var OPERATIONS = [
|
|
|
1975
1980
|
},
|
|
1976
1981
|
{
|
|
1977
1982
|
"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
|
|
1983
|
+
"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
1984
|
"inputSchema": {
|
|
1980
1985
|
"additionalProperties": false,
|
|
1981
1986
|
"properties": {
|
|
@@ -2047,7 +2052,7 @@ var OPERATIONS = [
|
|
|
2047
2052
|
},
|
|
2048
2053
|
{
|
|
2049
2054
|
"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`.
|
|
2055
|
+
"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
2056
|
"inputSchema": {
|
|
2052
2057
|
"additionalProperties": false,
|
|
2053
2058
|
"properties": {
|
|
@@ -2573,7 +2578,7 @@ var OPERATIONS = [
|
|
|
2573
2578
|
},
|
|
2574
2579
|
{
|
|
2575
2580
|
"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` (
|
|
2581
|
+
"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
2582
|
"inputSchema": {
|
|
2578
2583
|
"additionalProperties": false,
|
|
2579
2584
|
"properties": {
|
|
@@ -2630,7 +2635,7 @@ var OPERATIONS = [
|
|
|
2630
2635
|
},
|
|
2631
2636
|
{
|
|
2632
2637
|
"name": "recompute_columns",
|
|
2633
|
-
"description": "Bring generated and identity columns up to date (all rows, or only `paths`), e.g. after\nrows were edited
|
|
2638
|
+
"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
2639
|
"inputSchema": {
|
|
2635
2640
|
"additionalProperties": false,
|
|
2636
2641
|
"properties": {
|
|
@@ -2669,7 +2674,7 @@ var OPERATIONS = [
|
|
|
2669
2674
|
"title": "Recompute Columns",
|
|
2670
2675
|
"readOnlyHint": false,
|
|
2671
2676
|
"destructiveHint": false,
|
|
2672
|
-
"idempotentHint":
|
|
2677
|
+
"idempotentHint": false,
|
|
2673
2678
|
"openWorldHint": false
|
|
2674
2679
|
}
|
|
2675
2680
|
},
|
|
@@ -2834,7 +2839,7 @@ var OPERATIONS = [
|
|
|
2834
2839
|
},
|
|
2835
2840
|
{
|
|
2836
2841
|
"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).',
|
|
2842
|
+
"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
2843
|
"inputSchema": {
|
|
2839
2844
|
"additionalProperties": false,
|
|
2840
2845
|
"properties": {
|
|
@@ -2972,7 +2977,8 @@ var ROUTES = {
|
|
|
2972
2977
|
create_webhook: (a) => ["/webhooks/create/", pick(a, ["url", "tables", "ops", "columns", "exclude_actor", "where", "since"])],
|
|
2973
2978
|
delete_webhook: (a) => ["/webhooks/delete/", pick(a, ["id"])],
|
|
2974
2979
|
restore_table: (a) => ["/history/restore/", { ...pick(a, ["table", "version", "timestamp"]), ...dry(a) }],
|
|
2975
|
-
|
|
2980
|
+
// vacuum deletes history for good: a dry run unless the caller says `dry_run: false`
|
|
2981
|
+
vacuum_table: (a) => ["/history/vacuum/", { ...pick(a, ["table", "retain_from_version"]), dry_run: a.dry_run !== false }],
|
|
2976
2982
|
graph_neighbors: (a) => ["/graph/neighbors/", compact({ from: a.from_nodes, direction: a.direction, edges: a.edges })],
|
|
2977
2983
|
graph_traverse: (a) => [
|
|
2978
2984
|
"/graph/traverse/",
|
|
@@ -3059,6 +3065,110 @@ function estimateTokens(text) {
|
|
|
3059
3065
|
return n;
|
|
3060
3066
|
}
|
|
3061
3067
|
|
|
3068
|
+
// src/api/otlp.ts
|
|
3069
|
+
var SEVERITY_NUMBER = { DEBUG: 5, INFO: 9, WARN: 13, ERROR: 17 };
|
|
3070
|
+
function attr(key, v) {
|
|
3071
|
+
if (v === void 0 || v === null) return null;
|
|
3072
|
+
if (typeof v === "boolean") return { key, value: { boolValue: v } };
|
|
3073
|
+
if (typeof v === "number") return Number.isInteger(v) ? { key, value: { intValue: String(v) } } : { key, value: { doubleValue: v } };
|
|
3074
|
+
return { key, value: { stringValue: v } };
|
|
3075
|
+
}
|
|
3076
|
+
var attrs = (a) => Object.entries(a).map(([k, v]) => attr(k, v)).filter((x) => x !== null);
|
|
3077
|
+
var fetchSend = async (url, body, headers) => {
|
|
3078
|
+
const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", ...headers }, body });
|
|
3079
|
+
if (!res.ok) throw new Error(`OTLP HTTP ${res.status}`);
|
|
3080
|
+
};
|
|
3081
|
+
var OtlpLogExporter = class {
|
|
3082
|
+
constructor(o) {
|
|
3083
|
+
this.o = o;
|
|
3084
|
+
this.buffer = [];
|
|
3085
|
+
this.inflight = Promise.resolve();
|
|
3086
|
+
this.timer = null;
|
|
3087
|
+
/** Records sent, and records lost because the collector did not take them. */
|
|
3088
|
+
this.sent = 0;
|
|
3089
|
+
this.dropped = 0;
|
|
3090
|
+
const every = o.flushMs ?? 5e3;
|
|
3091
|
+
if (every > 0) {
|
|
3092
|
+
this.timer = setInterval(() => void this.flush(), every);
|
|
3093
|
+
this.timer.unref?.();
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
log(severity, body, attributes = {}) {
|
|
3097
|
+
const ms = (this.o.now ?? Date.now)();
|
|
3098
|
+
this.buffer.push({ timeUnixNano: `${Math.round(ms)}000000`, severity, body, attributes });
|
|
3099
|
+
if (this.buffer.length >= (this.o.maxBatch ?? 50)) void this.flush();
|
|
3100
|
+
}
|
|
3101
|
+
/** Send what is buffered. Never rejects. */
|
|
3102
|
+
flush() {
|
|
3103
|
+
if (this.buffer.length === 0) return this.inflight;
|
|
3104
|
+
const batch = this.buffer;
|
|
3105
|
+
this.buffer = [];
|
|
3106
|
+
const body = JSON.stringify({
|
|
3107
|
+
resourceLogs: [
|
|
3108
|
+
{
|
|
3109
|
+
resource: { attributes: attrs({ "service.name": this.o.serviceName, "service.version": this.o.serviceVersion, ...this.o.resource }) },
|
|
3110
|
+
scopeLogs: [
|
|
3111
|
+
{
|
|
3112
|
+
scope: { name: this.o.serviceName },
|
|
3113
|
+
logRecords: batch.map((r) => ({
|
|
3114
|
+
timeUnixNano: r.timeUnixNano,
|
|
3115
|
+
observedTimeUnixNano: r.timeUnixNano,
|
|
3116
|
+
severityNumber: SEVERITY_NUMBER[r.severity],
|
|
3117
|
+
severityText: r.severity,
|
|
3118
|
+
body: { stringValue: r.body },
|
|
3119
|
+
attributes: attrs(r.attributes)
|
|
3120
|
+
}))
|
|
3121
|
+
}
|
|
3122
|
+
]
|
|
3123
|
+
}
|
|
3124
|
+
]
|
|
3125
|
+
});
|
|
3126
|
+
const url = `${this.o.endpoint.replace(/\/+$/, "")}/v1/logs`;
|
|
3127
|
+
const send = this.o.send ?? fetchSend;
|
|
3128
|
+
this.inflight = this.inflight.then(
|
|
3129
|
+
() => send(url, body, this.o.headers ?? {}).then(
|
|
3130
|
+
() => void (this.sent += batch.length),
|
|
3131
|
+
() => void (this.dropped += batch.length)
|
|
3132
|
+
)
|
|
3133
|
+
);
|
|
3134
|
+
return this.inflight;
|
|
3135
|
+
}
|
|
3136
|
+
/** Stop the timer and send what is left. */
|
|
3137
|
+
async close() {
|
|
3138
|
+
if (this.timer) clearInterval(this.timer);
|
|
3139
|
+
this.timer = null;
|
|
3140
|
+
await this.flush();
|
|
3141
|
+
}
|
|
3142
|
+
};
|
|
3143
|
+
function parseOtlpHeaders(text) {
|
|
3144
|
+
const out = {};
|
|
3145
|
+
for (const part of (text ?? "").split(",")) {
|
|
3146
|
+
const i = part.indexOf("=");
|
|
3147
|
+
if (i <= 0) continue;
|
|
3148
|
+
const key = part.slice(0, i).trim();
|
|
3149
|
+
const value = part.slice(i + 1).trim();
|
|
3150
|
+
if (!key) continue;
|
|
3151
|
+
try {
|
|
3152
|
+
out[key] = decodeURIComponent(value);
|
|
3153
|
+
} catch {
|
|
3154
|
+
out[key] = value;
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
return out;
|
|
3158
|
+
}
|
|
3159
|
+
function otlpFromEnv(env, defaults) {
|
|
3160
|
+
const logs = env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT?.trim();
|
|
3161
|
+
const base = env.OTEL_EXPORTER_OTLP_ENDPOINT?.trim();
|
|
3162
|
+
const endpoint = logs ? logs.replace(/\/v1\/logs\/?$/, "") : base;
|
|
3163
|
+
if (!endpoint) return null;
|
|
3164
|
+
return {
|
|
3165
|
+
endpoint,
|
|
3166
|
+
serviceName: env.OTEL_SERVICE_NAME?.trim() || defaults.serviceName,
|
|
3167
|
+
...defaults.serviceVersion ? { serviceVersion: defaults.serviceVersion } : {},
|
|
3168
|
+
headers: { ...parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_HEADERS), ...parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_LOGS_HEADERS) }
|
|
3169
|
+
};
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3062
3172
|
// src/api/client.ts
|
|
3063
3173
|
var ACTOR_HEADER = "X-Datanotes-Actor";
|
|
3064
3174
|
var DatanotesError = class extends Error {
|
package/dist/api/index.d.ts
CHANGED
|
@@ -6,4 +6,5 @@ export { OPERATIONS, type OperationDef } from "./catalog";
|
|
|
6
6
|
export { operationRequest, operationRoute, CALLABLE_OPERATIONS, OperationInputError, ROUTE_PREFIX, type OperationRequest } from "./requests";
|
|
7
7
|
export { resultOf, describeRefusal, describeBatch, type OperationResult } from "./results";
|
|
8
8
|
export { estimateTokens } from "./tokens";
|
|
9
|
+
export { OtlpLogExporter, otlpFromEnv, parseOtlpHeaders, type OtlpOptions, type Severity, type AttrValue } from "./otlp";
|
|
9
10
|
export { DatanotesClient, DatanotesError, fetchTransport, ACTOR_HEADER, type ActorInfo, type AppTool, type ClientOptions, type HttpRequest, type HttpResponse, type ToolCallRecord, type Transport, } from "./client";
|
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/",
|
|
@@ -3020,6 +3023,110 @@ function estimateTokens(text) {
|
|
|
3020
3023
|
return n;
|
|
3021
3024
|
}
|
|
3022
3025
|
|
|
3026
|
+
// src/api/otlp.ts
|
|
3027
|
+
var SEVERITY_NUMBER = { DEBUG: 5, INFO: 9, WARN: 13, ERROR: 17 };
|
|
3028
|
+
function attr(key, v) {
|
|
3029
|
+
if (v === void 0 || v === null) return null;
|
|
3030
|
+
if (typeof v === "boolean") return { key, value: { boolValue: v } };
|
|
3031
|
+
if (typeof v === "number") return Number.isInteger(v) ? { key, value: { intValue: String(v) } } : { key, value: { doubleValue: v } };
|
|
3032
|
+
return { key, value: { stringValue: v } };
|
|
3033
|
+
}
|
|
3034
|
+
var attrs = (a) => Object.entries(a).map(([k, v]) => attr(k, v)).filter((x) => x !== null);
|
|
3035
|
+
var fetchSend = async (url, body, headers) => {
|
|
3036
|
+
const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", ...headers }, body });
|
|
3037
|
+
if (!res.ok) throw new Error(`OTLP HTTP ${res.status}`);
|
|
3038
|
+
};
|
|
3039
|
+
var OtlpLogExporter = class {
|
|
3040
|
+
constructor(o) {
|
|
3041
|
+
this.o = o;
|
|
3042
|
+
this.buffer = [];
|
|
3043
|
+
this.inflight = Promise.resolve();
|
|
3044
|
+
this.timer = null;
|
|
3045
|
+
/** Records sent, and records lost because the collector did not take them. */
|
|
3046
|
+
this.sent = 0;
|
|
3047
|
+
this.dropped = 0;
|
|
3048
|
+
const every = o.flushMs ?? 5e3;
|
|
3049
|
+
if (every > 0) {
|
|
3050
|
+
this.timer = setInterval(() => void this.flush(), every);
|
|
3051
|
+
this.timer.unref?.();
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
log(severity, body, attributes = {}) {
|
|
3055
|
+
const ms = (this.o.now ?? Date.now)();
|
|
3056
|
+
this.buffer.push({ timeUnixNano: `${Math.round(ms)}000000`, severity, body, attributes });
|
|
3057
|
+
if (this.buffer.length >= (this.o.maxBatch ?? 50)) void this.flush();
|
|
3058
|
+
}
|
|
3059
|
+
/** Send what is buffered. Never rejects. */
|
|
3060
|
+
flush() {
|
|
3061
|
+
if (this.buffer.length === 0) return this.inflight;
|
|
3062
|
+
const batch = this.buffer;
|
|
3063
|
+
this.buffer = [];
|
|
3064
|
+
const body = JSON.stringify({
|
|
3065
|
+
resourceLogs: [
|
|
3066
|
+
{
|
|
3067
|
+
resource: { attributes: attrs({ "service.name": this.o.serviceName, "service.version": this.o.serviceVersion, ...this.o.resource }) },
|
|
3068
|
+
scopeLogs: [
|
|
3069
|
+
{
|
|
3070
|
+
scope: { name: this.o.serviceName },
|
|
3071
|
+
logRecords: batch.map((r) => ({
|
|
3072
|
+
timeUnixNano: r.timeUnixNano,
|
|
3073
|
+
observedTimeUnixNano: r.timeUnixNano,
|
|
3074
|
+
severityNumber: SEVERITY_NUMBER[r.severity],
|
|
3075
|
+
severityText: r.severity,
|
|
3076
|
+
body: { stringValue: r.body },
|
|
3077
|
+
attributes: attrs(r.attributes)
|
|
3078
|
+
}))
|
|
3079
|
+
}
|
|
3080
|
+
]
|
|
3081
|
+
}
|
|
3082
|
+
]
|
|
3083
|
+
});
|
|
3084
|
+
const url = `${this.o.endpoint.replace(/\/+$/, "")}/v1/logs`;
|
|
3085
|
+
const send = this.o.send ?? fetchSend;
|
|
3086
|
+
this.inflight = this.inflight.then(
|
|
3087
|
+
() => send(url, body, this.o.headers ?? {}).then(
|
|
3088
|
+
() => void (this.sent += batch.length),
|
|
3089
|
+
() => void (this.dropped += batch.length)
|
|
3090
|
+
)
|
|
3091
|
+
);
|
|
3092
|
+
return this.inflight;
|
|
3093
|
+
}
|
|
3094
|
+
/** Stop the timer and send what is left. */
|
|
3095
|
+
async close() {
|
|
3096
|
+
if (this.timer) clearInterval(this.timer);
|
|
3097
|
+
this.timer = null;
|
|
3098
|
+
await this.flush();
|
|
3099
|
+
}
|
|
3100
|
+
};
|
|
3101
|
+
function parseOtlpHeaders(text) {
|
|
3102
|
+
const out = {};
|
|
3103
|
+
for (const part of (text ?? "").split(",")) {
|
|
3104
|
+
const i = part.indexOf("=");
|
|
3105
|
+
if (i <= 0) continue;
|
|
3106
|
+
const key = part.slice(0, i).trim();
|
|
3107
|
+
const value = part.slice(i + 1).trim();
|
|
3108
|
+
if (!key) continue;
|
|
3109
|
+
try {
|
|
3110
|
+
out[key] = decodeURIComponent(value);
|
|
3111
|
+
} catch {
|
|
3112
|
+
out[key] = value;
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3115
|
+
return out;
|
|
3116
|
+
}
|
|
3117
|
+
function otlpFromEnv(env, defaults) {
|
|
3118
|
+
const logs = env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT?.trim();
|
|
3119
|
+
const base = env.OTEL_EXPORTER_OTLP_ENDPOINT?.trim();
|
|
3120
|
+
const endpoint = logs ? logs.replace(/\/v1\/logs\/?$/, "") : base;
|
|
3121
|
+
if (!endpoint) return null;
|
|
3122
|
+
return {
|
|
3123
|
+
endpoint,
|
|
3124
|
+
serviceName: env.OTEL_SERVICE_NAME?.trim() || defaults.serviceName,
|
|
3125
|
+
...defaults.serviceVersion ? { serviceVersion: defaults.serviceVersion } : {},
|
|
3126
|
+
headers: { ...parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_HEADERS), ...parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_LOGS_HEADERS) }
|
|
3127
|
+
};
|
|
3128
|
+
}
|
|
3129
|
+
|
|
3023
3130
|
// src/api/client.ts
|
|
3024
3131
|
var ACTOR_HEADER = "X-Datanotes-Actor";
|
|
3025
3132
|
var DatanotesError = class extends Error {
|
|
@@ -3127,6 +3234,7 @@ export {
|
|
|
3127
3234
|
DatanotesError,
|
|
3128
3235
|
OPERATIONS,
|
|
3129
3236
|
OperationInputError,
|
|
3237
|
+
OtlpLogExporter,
|
|
3130
3238
|
ROUTE_PREFIX,
|
|
3131
3239
|
describeBatch,
|
|
3132
3240
|
describeRefusal,
|
|
@@ -3134,5 +3242,7 @@ export {
|
|
|
3134
3242
|
fetchTransport,
|
|
3135
3243
|
operationRequest,
|
|
3136
3244
|
operationRoute,
|
|
3245
|
+
otlpFromEnv,
|
|
3246
|
+
parseOtlpHeaders,
|
|
3137
3247
|
resultOf
|
|
3138
3248
|
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTLP/HTTP log export for programs around the engine (MCP servers, apps): records are buffered
|
|
3
|
+
* and POSTed as OTLP JSON to `<endpoint>/v1/logs`, the format SigNoz and any OpenTelemetry
|
|
4
|
+
* collector read. No dependency, no SDK. Export failures are counted, never thrown: logging must
|
|
5
|
+
* not break the program.
|
|
6
|
+
*
|
|
7
|
+
* Configure it in code, or from the standard environment variables with `otlpFromEnv`.
|
|
8
|
+
*/
|
|
9
|
+
export type Severity = "DEBUG" | "INFO" | "WARN" | "ERROR";
|
|
10
|
+
export type AttrValue = string | number | boolean | null | undefined;
|
|
11
|
+
export interface OtlpOptions {
|
|
12
|
+
/** Collector base URL, e.g. `http://127.0.0.1:4318` (`/v1/logs` is added). */
|
|
13
|
+
endpoint: string;
|
|
14
|
+
/** `service.name` of the records. */
|
|
15
|
+
serviceName: string;
|
|
16
|
+
serviceVersion?: string;
|
|
17
|
+
/** Extra headers, e.g. `{"signoz-ingestion-key": "…"}`. */
|
|
18
|
+
headers?: Record<string, string>;
|
|
19
|
+
/** More resource attributes (e.g. `host.name`). */
|
|
20
|
+
resource?: Record<string, AttrValue>;
|
|
21
|
+
/** Send at most this many records per request (default 50); a full buffer is sent at once. */
|
|
22
|
+
maxBatch?: number;
|
|
23
|
+
/** Send the buffer every this many ms (default 5000; 0: only on `flush`). */
|
|
24
|
+
flushMs?: number;
|
|
25
|
+
/** POST a body; resolves on 2xx (default: fetch). */
|
|
26
|
+
send?: (url: string, body: string, headers: Record<string, string>) => Promise<void>;
|
|
27
|
+
now?: () => number;
|
|
28
|
+
}
|
|
29
|
+
export declare class OtlpLogExporter {
|
|
30
|
+
private readonly o;
|
|
31
|
+
private buffer;
|
|
32
|
+
private inflight;
|
|
33
|
+
private timer;
|
|
34
|
+
/** Records sent, and records lost because the collector did not take them. */
|
|
35
|
+
sent: number;
|
|
36
|
+
dropped: number;
|
|
37
|
+
constructor(o: OtlpOptions);
|
|
38
|
+
log(severity: Severity, body: string, attributes?: Record<string, AttrValue>): void;
|
|
39
|
+
/** Send what is buffered. Never rejects. */
|
|
40
|
+
flush(): Promise<void>;
|
|
41
|
+
/** Stop the timer and send what is left. */
|
|
42
|
+
close(): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
/** `k1=v1,k2=v2` (values URL-encoded), the format of `OTEL_EXPORTER_OTLP_HEADERS`. */
|
|
45
|
+
export declare function parseOtlpHeaders(text: string | undefined): Record<string, string>;
|
|
46
|
+
/**
|
|
47
|
+
* Exporter options from the standard OpenTelemetry variables, or null when no endpoint is set:
|
|
48
|
+
* `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` (full URL of /v1/logs) or `OTEL_EXPORTER_OTLP_ENDPOINT`,
|
|
49
|
+
* `OTEL_EXPORTER_OTLP_HEADERS` / `OTEL_EXPORTER_OTLP_LOGS_HEADERS`, `OTEL_SERVICE_NAME`.
|
|
50
|
+
*/
|
|
51
|
+
export declare function otlpFromEnv(env: Record<string, string | undefined>, defaults: {
|
|
52
|
+
serviceName: string;
|
|
53
|
+
serviceVersion?: string;
|
|
54
|
+
}): OtlpOptions | null;
|
package/dist/datanotes.cjs
CHANGED
|
@@ -7425,6 +7425,7 @@ var DEFAULT_SETTINGS = {
|
|
|
7425
7425
|
telemetryEnabled: false,
|
|
7426
7426
|
telemetryEndpoint: "http://127.0.0.1:4318",
|
|
7427
7427
|
telemetryServiceName: "datanotes",
|
|
7428
|
+
telemetryHeaders: {},
|
|
7428
7429
|
autoRecompute: true,
|
|
7429
7430
|
viewNotes: true,
|
|
7430
7431
|
semanticEnabled: false,
|
|
@@ -7450,6 +7451,7 @@ var KEYS = [
|
|
|
7450
7451
|
"telemetryEnabled",
|
|
7451
7452
|
"telemetryEndpoint",
|
|
7452
7453
|
"telemetryServiceName",
|
|
7454
|
+
"telemetryHeaders",
|
|
7453
7455
|
"autoRecompute",
|
|
7454
7456
|
"viewNotes",
|
|
7455
7457
|
"semanticEnabled",
|
|
@@ -7500,6 +7502,14 @@ function mergeWithDefaults(loaded) {
|
|
|
7500
7502
|
}
|
|
7501
7503
|
break;
|
|
7502
7504
|
}
|
|
7505
|
+
case "telemetryHeaders": {
|
|
7506
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
7507
|
+
out.telemetryHeaders = Object.fromEntries(
|
|
7508
|
+
Object.entries(raw).filter(([k, v]) => /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/.test(k) && typeof v === "string")
|
|
7509
|
+
);
|
|
7510
|
+
}
|
|
7511
|
+
break;
|
|
7512
|
+
}
|
|
7503
7513
|
case "telemetryServiceName": {
|
|
7504
7514
|
if (typeof raw === "string" && raw.trim().length > 0) {
|
|
7505
7515
|
out.telemetryServiceName = raw.trim();
|
|
@@ -7659,7 +7669,7 @@ function otlpLogsPayload(serviceName, records, resource = {}) {
|
|
|
7659
7669
|
resource: { attributes: attrs({ "service.name": serviceName, ...resource }) },
|
|
7660
7670
|
scopeLogs: [
|
|
7661
7671
|
{
|
|
7662
|
-
scope: { name: "
|
|
7672
|
+
scope: { name: "datanotes" },
|
|
7663
7673
|
logRecords: records.map((r) => ({
|
|
7664
7674
|
timeUnixNano: r.timeUnixNano,
|
|
7665
7675
|
observedTimeUnixNano: r.timeUnixNano,
|
|
@@ -7705,7 +7715,7 @@ var OtlpLogger = class {
|
|
|
7705
7715
|
const url = `${settings.telemetryEndpoint.replace(/\/+$/, "")}/v1/logs`;
|
|
7706
7716
|
const body = otlpLogsPayload(settings.telemetryServiceName, batch, this.opts.resource);
|
|
7707
7717
|
this.inflight = this.inflight.then(
|
|
7708
|
-
() => this.opts.transport(url, body).then(
|
|
7718
|
+
() => this.opts.transport(url, body, { ...settings.telemetryHeaders ?? {} }).then(
|
|
7709
7719
|
() => {
|
|
7710
7720
|
this.sent += batch.length;
|
|
7711
7721
|
},
|
|
@@ -7793,6 +7803,7 @@ var TeeTelemetry = class {
|
|
|
7793
7803
|
|
|
7794
7804
|
// src/server/engine-lock.ts
|
|
7795
7805
|
var LOCK_PATH_SUFFIX = "engine.lock";
|
|
7806
|
+
var LOCK_STALE_MS = 3e4;
|
|
7796
7807
|
var LOCK_HEARTBEAT_MS = 1e4;
|
|
7797
7808
|
function lockPath(storeFolder) {
|
|
7798
7809
|
return `${storeFolder.replace(/\/+$/, "")}/${LOCK_PATH_SUFFIX}`;
|
|
@@ -7805,6 +7816,9 @@ async function readLock(adapter2, storeFolder) {
|
|
|
7805
7816
|
return null;
|
|
7806
7817
|
}
|
|
7807
7818
|
}
|
|
7819
|
+
function isFresh(lock, now = Date.now()) {
|
|
7820
|
+
return !!lock && now - Date.parse(lock.heartbeat) < LOCK_STALE_MS;
|
|
7821
|
+
}
|
|
7808
7822
|
var LockHolder = class {
|
|
7809
7823
|
constructor(adapter2, storeFolder, info) {
|
|
7810
7824
|
this.adapter = adapter2;
|
|
@@ -7836,6 +7850,24 @@ var LockHolder = class {
|
|
|
7836
7850
|
}
|
|
7837
7851
|
};
|
|
7838
7852
|
|
|
7853
|
+
// src/api/otlp.ts
|
|
7854
|
+
function parseOtlpHeaders(text2) {
|
|
7855
|
+
const out = {};
|
|
7856
|
+
for (const part of (text2 ?? "").split(",")) {
|
|
7857
|
+
const i = part.indexOf("=");
|
|
7858
|
+
if (i <= 0) continue;
|
|
7859
|
+
const key = part.slice(0, i).trim();
|
|
7860
|
+
const value = part.slice(i + 1).trim();
|
|
7861
|
+
if (!key) continue;
|
|
7862
|
+
try {
|
|
7863
|
+
out[key] = decodeURIComponent(value);
|
|
7864
|
+
} catch {
|
|
7865
|
+
out[key] = value;
|
|
7866
|
+
}
|
|
7867
|
+
}
|
|
7868
|
+
return out;
|
|
7869
|
+
}
|
|
7870
|
+
|
|
7839
7871
|
// src/core/filename-values.ts
|
|
7840
7872
|
var PLACEHOLDER = /\{([A-Za-z][A-Za-z0-9_-]*)(?::([^}]+))?\}/g;
|
|
7841
7873
|
var FORMAT_TOKEN = /\[([^\]]*)\]|YYYY|MM|DD|HH|mm|ss/g;
|
|
@@ -10087,7 +10119,7 @@ async function tableStateAt(app, settings, full, asOf2 = {}) {
|
|
|
10087
10119
|
if (versions.length === 0) {
|
|
10088
10120
|
if (asOf2.version !== void 0 || asOf2.timestamp !== void 0) {
|
|
10089
10121
|
throw new HistoryError(
|
|
10090
|
-
`${full} has no history: time travel is off for it (turn it on with configure_database(history=true) or alter_table(history=
|
|
10122
|
+
`${full} has no history: time travel is off for it (turn it on with configure_database(history=true) or alter_table(history="on"))`
|
|
10091
10123
|
);
|
|
10092
10124
|
}
|
|
10093
10125
|
return emptyState(full);
|
|
@@ -15006,7 +15038,7 @@ var SemanticIndex = class {
|
|
|
15006
15038
|
}
|
|
15007
15039
|
requireEnabled() {
|
|
15008
15040
|
if (!this.getSettings().semanticEnabled) {
|
|
15009
|
-
throw new OperationInputError("semantic search is off:
|
|
15041
|
+
throw new OperationInputError("semantic search is off: set semanticEnabled, semanticProvider and semanticModel in the engine settings");
|
|
15010
15042
|
}
|
|
15011
15043
|
}
|
|
15012
15044
|
async load() {
|
|
@@ -15054,7 +15086,7 @@ var SemanticIndex = class {
|
|
|
15054
15086
|
/** Notes in scope: every markdown note, or only table rows; minus excluded folders and archives. */
|
|
15055
15087
|
inScope(f, table) {
|
|
15056
15088
|
const s = this.getSettings();
|
|
15057
|
-
if (f.path.includes("/_archive/")) return false;
|
|
15089
|
+
if (f.path.startsWith("_archive/") || f.path.includes("/_archive/")) return false;
|
|
15058
15090
|
if (s.semanticExclude.some((dir) => f.path === dir || f.path.startsWith(`${dir.replace(/\/+$/, "")}/`))) return false;
|
|
15059
15091
|
if (s.semanticScope === "tables" && !table) return false;
|
|
15060
15092
|
return true;
|
|
@@ -16206,7 +16238,7 @@ var HttpApiServer = class {
|
|
|
16206
16238
|
}
|
|
16207
16239
|
/**
|
|
16208
16240
|
* Resolves with the bound port once listening. A busy port is retried a few times: when the
|
|
16209
|
-
*
|
|
16241
|
+
* engine restarts, the previous instance may still be closing.
|
|
16210
16242
|
*/
|
|
16211
16243
|
async start(retries = 6, delayMs = 500) {
|
|
16212
16244
|
for (let i = 0; ; i++) {
|
|
@@ -17533,7 +17565,7 @@ function checkPatterns(patterns) {
|
|
|
17533
17565
|
var OPERATIONS = [
|
|
17534
17566
|
{
|
|
17535
17567
|
"name": "read_note",
|
|
17536
|
-
"description": "Read a vault file (
|
|
17568
|
+
"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.",
|
|
17537
17569
|
"inputSchema": {
|
|
17538
17570
|
"additionalProperties": false,
|
|
17539
17571
|
"properties": {
|
|
@@ -17560,7 +17592,7 @@ var OPERATIONS = [
|
|
|
17560
17592
|
},
|
|
17561
17593
|
{
|
|
17562
17594
|
"name": "list_directory",
|
|
17563
|
-
"description": '
|
|
17595
|
+
"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.',
|
|
17564
17596
|
"inputSchema": {
|
|
17565
17597
|
"additionalProperties": false,
|
|
17566
17598
|
"properties": {
|
|
@@ -17581,7 +17613,7 @@ var OPERATIONS = [
|
|
|
17581
17613
|
},
|
|
17582
17614
|
{
|
|
17583
17615
|
"name": "search_simple",
|
|
17584
|
-
"description": "
|
|
17616
|
+
"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).",
|
|
17585
17617
|
"inputSchema": {
|
|
17586
17618
|
"additionalProperties": false,
|
|
17587
17619
|
"properties": {
|
|
@@ -17608,7 +17640,7 @@ var OPERATIONS = [
|
|
|
17608
17640
|
},
|
|
17609
17641
|
{
|
|
17610
17642
|
"name": "list_tables",
|
|
17611
|
-
"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)
|
|
17643
|
+
"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.",
|
|
17612
17644
|
"inputSchema": {
|
|
17613
17645
|
"additionalProperties": false,
|
|
17614
17646
|
"properties": {},
|
|
@@ -17728,7 +17760,8 @@ var OPERATIONS = [
|
|
|
17728
17760
|
"type": "null"
|
|
17729
17761
|
}
|
|
17730
17762
|
],
|
|
17731
|
-
"default": null
|
|
17763
|
+
"default": null,
|
|
17764
|
+
"description": "Untyped fields of the table (name \u2192 default), kept on every row; a field cannot share a name with a column."
|
|
17732
17765
|
},
|
|
17733
17766
|
"body": {
|
|
17734
17767
|
"anyOf": [
|
|
@@ -17836,7 +17869,7 @@ var OPERATIONS = [
|
|
|
17836
17869
|
},
|
|
17837
17870
|
{
|
|
17838
17871
|
"name": "rename_column",
|
|
17839
|
-
"description": "Rename a column
|
|
17872
|
+
"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.",
|
|
17840
17873
|
"inputSchema": {
|
|
17841
17874
|
"additionalProperties": false,
|
|
17842
17875
|
"properties": {
|
|
@@ -18207,7 +18240,7 @@ var OPERATIONS = [
|
|
|
18207
18240
|
},
|
|
18208
18241
|
{
|
|
18209
18242
|
"name": "describe_database",
|
|
18210
|
-
"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 (
|
|
18243
|
+
"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`.",
|
|
18211
18244
|
"inputSchema": {
|
|
18212
18245
|
"additionalProperties": false,
|
|
18213
18246
|
"properties": {},
|
|
@@ -18223,7 +18256,7 @@ var OPERATIONS = [
|
|
|
18223
18256
|
},
|
|
18224
18257
|
{
|
|
18225
18258
|
"name": "configure_database",
|
|
18226
|
-
"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
|
|
18259
|
+
"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.',
|
|
18227
18260
|
"inputSchema": {
|
|
18228
18261
|
"additionalProperties": false,
|
|
18229
18262
|
"properties": {
|
|
@@ -18398,7 +18431,8 @@ var OPERATIONS = [
|
|
|
18398
18431
|
"type": "null"
|
|
18399
18432
|
}
|
|
18400
18433
|
],
|
|
18401
|
-
"default": null
|
|
18434
|
+
"default": null,
|
|
18435
|
+
"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."
|
|
18402
18436
|
},
|
|
18403
18437
|
"dry_run": {
|
|
18404
18438
|
"default": false,
|
|
@@ -18636,7 +18670,7 @@ var OPERATIONS = [
|
|
|
18636
18670
|
"title": "Update Row",
|
|
18637
18671
|
"readOnlyHint": false,
|
|
18638
18672
|
"destructiveHint": false,
|
|
18639
|
-
"idempotentHint":
|
|
18673
|
+
"idempotentHint": false,
|
|
18640
18674
|
"openWorldHint": false
|
|
18641
18675
|
}
|
|
18642
18676
|
},
|
|
@@ -18790,7 +18824,7 @@ var OPERATIONS = [
|
|
|
18790
18824
|
},
|
|
18791
18825
|
{
|
|
18792
18826
|
"name": "adopt_rows",
|
|
18793
|
-
"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 (
|
|
18827
|
+
"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`.',
|
|
18794
18828
|
"inputSchema": {
|
|
18795
18829
|
"additionalProperties": false,
|
|
18796
18830
|
"properties": {
|
|
@@ -19093,7 +19127,7 @@ var OPERATIONS = [
|
|
|
19093
19127
|
"annotations": {
|
|
19094
19128
|
"title": "Undo Migration",
|
|
19095
19129
|
"readOnlyHint": false,
|
|
19096
|
-
"destructiveHint":
|
|
19130
|
+
"destructiveHint": true,
|
|
19097
19131
|
"idempotentHint": false,
|
|
19098
19132
|
"openWorldHint": false
|
|
19099
19133
|
}
|
|
@@ -19258,7 +19292,7 @@ var OPERATIONS = [
|
|
|
19258
19292
|
},
|
|
19259
19293
|
{
|
|
19260
19294
|
"name": "semantic_search",
|
|
19261
|
-
"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).\
|
|
19295
|
+
"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.',
|
|
19262
19296
|
"inputSchema": {
|
|
19263
19297
|
"additionalProperties": false,
|
|
19264
19298
|
"properties": {
|
|
@@ -19321,7 +19355,7 @@ var OPERATIONS = [
|
|
|
19321
19355
|
},
|
|
19322
19356
|
{
|
|
19323
19357
|
"name": "semantic_index",
|
|
19324
|
-
"description": "Semantic search index: `status` (provider
|
|
19358
|
+
"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).",
|
|
19325
19359
|
"inputSchema": {
|
|
19326
19360
|
"additionalProperties": false,
|
|
19327
19361
|
"properties": {
|
|
@@ -19467,7 +19501,7 @@ var OPERATIONS = [
|
|
|
19467
19501
|
},
|
|
19468
19502
|
{
|
|
19469
19503
|
"name": "describe_history",
|
|
19470
|
-
"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
|
|
19504
|
+
"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`.",
|
|
19471
19505
|
"inputSchema": {
|
|
19472
19506
|
"additionalProperties": false,
|
|
19473
19507
|
"properties": {
|
|
@@ -19539,7 +19573,7 @@ var OPERATIONS = [
|
|
|
19539
19573
|
},
|
|
19540
19574
|
{
|
|
19541
19575
|
"name": "list_changes",
|
|
19542
|
-
"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`.
|
|
19576
|
+
"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`.",
|
|
19543
19577
|
"inputSchema": {
|
|
19544
19578
|
"additionalProperties": false,
|
|
19545
19579
|
"properties": {
|
|
@@ -20065,7 +20099,7 @@ var OPERATIONS = [
|
|
|
20065
20099
|
},
|
|
20066
20100
|
{
|
|
20067
20101
|
"name": "clone_table",
|
|
20068
|
-
"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` (
|
|
20102
|
+
"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.",
|
|
20069
20103
|
"inputSchema": {
|
|
20070
20104
|
"additionalProperties": false,
|
|
20071
20105
|
"properties": {
|
|
@@ -20122,7 +20156,7 @@ var OPERATIONS = [
|
|
|
20122
20156
|
},
|
|
20123
20157
|
{
|
|
20124
20158
|
"name": "recompute_columns",
|
|
20125
|
-
"description": "Bring generated and identity columns up to date (all rows, or only `paths`), e.g. after\nrows were edited
|
|
20159
|
+
"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.",
|
|
20126
20160
|
"inputSchema": {
|
|
20127
20161
|
"additionalProperties": false,
|
|
20128
20162
|
"properties": {
|
|
@@ -20161,7 +20195,7 @@ var OPERATIONS = [
|
|
|
20161
20195
|
"title": "Recompute Columns",
|
|
20162
20196
|
"readOnlyHint": false,
|
|
20163
20197
|
"destructiveHint": false,
|
|
20164
|
-
"idempotentHint":
|
|
20198
|
+
"idempotentHint": false,
|
|
20165
20199
|
"openWorldHint": false
|
|
20166
20200
|
}
|
|
20167
20201
|
},
|
|
@@ -20326,7 +20360,7 @@ var OPERATIONS = [
|
|
|
20326
20360
|
},
|
|
20327
20361
|
{
|
|
20328
20362
|
"name": "graph_path",
|
|
20329
|
-
"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).',
|
|
20363
|
+
"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).',
|
|
20330
20364
|
"inputSchema": {
|
|
20331
20365
|
"additionalProperties": false,
|
|
20332
20366
|
"properties": {
|
|
@@ -20464,7 +20498,8 @@ var ROUTES = {
|
|
|
20464
20498
|
create_webhook: (a) => ["/webhooks/create/", pick(a, ["url", "tables", "ops", "columns", "exclude_actor", "where", "since"])],
|
|
20465
20499
|
delete_webhook: (a) => ["/webhooks/delete/", pick(a, ["id"])],
|
|
20466
20500
|
restore_table: (a) => ["/history/restore/", { ...pick(a, ["table", "version", "timestamp"]), ...dry(a) }],
|
|
20467
|
-
|
|
20501
|
+
// vacuum deletes history for good: a dry run unless the caller says `dry_run: false`
|
|
20502
|
+
vacuum_table: (a) => ["/history/vacuum/", { ...pick(a, ["table", "retain_from_version"]), dry_run: a.dry_run !== false }],
|
|
20468
20503
|
graph_neighbors: (a) => ["/graph/neighbors/", compact({ from: a.from_nodes, direction: a.direction, edges: a.edges })],
|
|
20469
20504
|
graph_traverse: (a) => [
|
|
20470
20505
|
"/graph/traverse/",
|
|
@@ -20755,7 +20790,7 @@ var EngineRuntime = class {
|
|
|
20755
20790
|
const s = this.o.getSettings;
|
|
20756
20791
|
const logger = new OtlpLogger({
|
|
20757
20792
|
getSettings: s,
|
|
20758
|
-
transport: async (url, body) => this.o.postLogs(url, body),
|
|
20793
|
+
transport: async (url, body, headers) => this.o.postLogs(url, body, headers),
|
|
20759
20794
|
resource: { "service.version": this.o.version, "host.name": this.o.hostName }
|
|
20760
20795
|
});
|
|
20761
20796
|
const source = this.o.hostName;
|
|
@@ -22062,7 +22097,7 @@ var NodeWatcher = class {
|
|
|
22062
22097
|
};
|
|
22063
22098
|
|
|
22064
22099
|
// src/node/service.ts
|
|
22065
|
-
var VERSION = true ? "1.
|
|
22100
|
+
var VERSION = true ? "1.3.0" : "dev";
|
|
22066
22101
|
function parseArgs(argv, env = process.env) {
|
|
22067
22102
|
const get = (name) => {
|
|
22068
22103
|
const i = argv.indexOf(`--${name}`);
|
|
@@ -22107,6 +22142,11 @@ async function startService(o, env = process.env) {
|
|
|
22107
22142
|
import_node_fs5.default.writeFileSync(file, JSON.stringify({ ...current2, ...settings }, null, 2));
|
|
22108
22143
|
};
|
|
22109
22144
|
const host = openNodeVault(o.vault);
|
|
22145
|
+
const held = await readLock(host.vault.adapter, storeRoot(settings));
|
|
22146
|
+
if (isFresh(held) && held.pid !== process.pid && (held.host !== import_node_os.default.hostname() || processAlive(held.pid))) {
|
|
22147
|
+
host.metadataCache.dispose();
|
|
22148
|
+
throw new Error(`another engine holds the vault (pid ${held.pid} on ${held.host}, port ${held.port}): stop it first`);
|
|
22149
|
+
}
|
|
22110
22150
|
const watcher = new NodeWatcher(host.vault, { mode: o.watch, intervalMs: o.pollMs });
|
|
22111
22151
|
const keyFromFile = () => {
|
|
22112
22152
|
if (!o.embeddingKeyFile) return void 0;
|
|
@@ -22125,8 +22165,9 @@ async function startService(o, env = process.env) {
|
|
|
22125
22165
|
const res = await fetch(req.url, { method: req.method, headers: req.headers, body: req.body });
|
|
22126
22166
|
return { status: res.status, text: await res.text() };
|
|
22127
22167
|
},
|
|
22128
|
-
postLogs: async (url, body) => {
|
|
22129
|
-
const
|
|
22168
|
+
postLogs: async (url, body, headers) => {
|
|
22169
|
+
const all = { ...headers, ...parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_HEADERS) };
|
|
22170
|
+
const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", ...all }, body });
|
|
22130
22171
|
if (!res.ok) throw new Error(`OTLP HTTP ${res.status}`);
|
|
22131
22172
|
},
|
|
22132
22173
|
embeddingKey: () => env.DATANOTES_EMBEDDING_KEY || keyFromFile(),
|
|
@@ -22184,6 +22225,14 @@ async function startService(o, env = process.env) {
|
|
|
22184
22225
|
}
|
|
22185
22226
|
};
|
|
22186
22227
|
}
|
|
22228
|
+
function processAlive(pid) {
|
|
22229
|
+
try {
|
|
22230
|
+
process.kill(pid, 0);
|
|
22231
|
+
return true;
|
|
22232
|
+
} catch (e) {
|
|
22233
|
+
return e.code === "EPERM";
|
|
22234
|
+
}
|
|
22235
|
+
}
|
|
22187
22236
|
async function main(argv = process.argv.slice(2)) {
|
|
22188
22237
|
const li = argv.indexOf("--log");
|
|
22189
22238
|
if (li >= 0 && argv[li + 1]) {
|
|
@@ -22227,7 +22276,7 @@ Docs: https://datanotes.dev`;
|
|
|
22227
22276
|
}
|
|
22228
22277
|
|
|
22229
22278
|
// src/node/cli.ts
|
|
22230
|
-
var VERSION2 = true ? "1.
|
|
22279
|
+
var VERSION2 = true ? "1.3.0" : "dev";
|
|
22231
22280
|
async function main2(argv = process.argv.slice(2)) {
|
|
22232
22281
|
const [command, ...rest] = argv;
|
|
22233
22282
|
if (command === "--version" || command === "-v" || command === "version") return void process.stdout.write(`${VERSION2}
|