vault-cortex 0.4.1 → 0.4.3
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/dist/docker.js +2 -2
- package/dist/env.js +32 -1
- package/dist/init.js +6 -6
- package/dist/messages.js +40 -11
- package/package.json +1 -1
- package/templates/local/docker-compose.yml +6 -6
- package/templates/remote/docker-compose.yml +20 -32
package/dist/docker.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
/** The image whose `get-token` entrypoint issues Obsidian Sync auth tokens. */
|
|
3
|
-
export const
|
|
3
|
+
export const GET_TOKEN_IMAGE = "ghcr.io/aliasunder/vault-cortex:remote";
|
|
4
4
|
export const createDockerRunner = () => ({
|
|
5
5
|
isComposeAvailable: () => spawnSync("docker", ["compose", "version"]).status === 0,
|
|
6
6
|
isDaemonRunning: () => spawnSync("docker", ["info"], { timeout: 5_000 }).status === 0,
|
|
7
7
|
composeUp: (cwd) => spawnSync("docker", ["compose", "up", "-d"], { cwd, stdio: "inherit" })
|
|
8
8
|
.status === 0,
|
|
9
|
-
runGetToken: () => spawnSync("docker", ["run", "--rm", "-it", "--entrypoint", "get-token",
|
|
9
|
+
runGetToken: () => spawnSync("docker", ["run", "--rm", "-it", "--entrypoint", "get-token", GET_TOKEN_IMAGE], {
|
|
10
10
|
stdio: "inherit",
|
|
11
11
|
}).status === 0,
|
|
12
12
|
});
|
package/dist/env.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { GET_TOKEN_IMAGE } from "./docker.js";
|
|
1
2
|
// The optional blocks mirror the canonical deploy/<mode>/.env.example files.
|
|
2
3
|
// cli/src/templates.test.ts asserts every required `${VAR:?}` in the compose
|
|
3
4
|
// templates has a matching line here, so a new required var breaks CI until
|
|
@@ -30,6 +31,18 @@ const LOCAL_OPTIONAL_BLOCK = `# Optional ─────────────
|
|
|
30
31
|
# Memory folder name in your vault (default: About Me).
|
|
31
32
|
# MEMORY_DIR=About Me
|
|
32
33
|
|
|
34
|
+
# Comma-separated folders protected from deletion (default: MEMORY_DIR, Daily Notes).
|
|
35
|
+
# If your daily notes folder has a custom name (e.g. "Journal"), override to include it.
|
|
36
|
+
# PROTECTED_PATHS=About Me,Daily Notes
|
|
37
|
+
|
|
38
|
+
# Comma-separated folders excluded from orphan detection
|
|
39
|
+
# (default: Daily Notes, Templates, MEMORY_DIR).
|
|
40
|
+
# ORPHAN_EXCLUDE_FOLDERS=Daily Notes,Templates,About Me
|
|
41
|
+
|
|
42
|
+
# URL shown in OAuth discovery metadata
|
|
43
|
+
# (default: https://github.com/aliasunder/vault-cortex).
|
|
44
|
+
# SERVICE_DOCUMENTATION_URL=https://github.com/youruser/your-fork
|
|
45
|
+
|
|
33
46
|
# Host port to expose (default: 8000).
|
|
34
47
|
# PORT=8000
|
|
35
48
|
|
|
@@ -72,12 +85,30 @@ const REMOTE_OPTIONAL_BLOCK = `# Optional ────────────
|
|
|
72
85
|
# Only takes effect when EMBEDDING_ENABLED=true.
|
|
73
86
|
# RERANK_MODE=blended
|
|
74
87
|
|
|
88
|
+
# Windows bind-mount mode (default: false).
|
|
89
|
+
# Set to true when your vault is on a Windows drive (Docker Desktop).
|
|
90
|
+
# Enables polling for the file watcher and rename-based moves across
|
|
91
|
+
# the Docker Desktop/WSL2 bridge.
|
|
92
|
+
# WINDOWS_MODE=false
|
|
93
|
+
|
|
75
94
|
# Enable or disable the memory layer (default: true).
|
|
76
95
|
# Set to false to hide memory tools and skip About Me/ creation.
|
|
77
96
|
# MEMORY_ENABLED=true
|
|
78
97
|
# Memory folder name in your vault (default: About Me).
|
|
79
98
|
# MEMORY_DIR=About Me
|
|
80
99
|
|
|
100
|
+
# Comma-separated folders protected from deletion (default: MEMORY_DIR, Daily Notes).
|
|
101
|
+
# If your daily notes folder has a custom name (e.g. "Journal"), override to include it.
|
|
102
|
+
# PROTECTED_PATHS=About Me,Daily Notes
|
|
103
|
+
|
|
104
|
+
# Comma-separated folders excluded from orphan detection
|
|
105
|
+
# (default: Daily Notes, Templates, MEMORY_DIR).
|
|
106
|
+
# ORPHAN_EXCLUDE_FOLDERS=Daily Notes,Templates,About Me
|
|
107
|
+
|
|
108
|
+
# URL shown in OAuth discovery metadata
|
|
109
|
+
# (default: https://github.com/aliasunder/vault-cortex).
|
|
110
|
+
# SERVICE_DOCUMENTATION_URL=https://github.com/youruser/your-fork
|
|
111
|
+
|
|
81
112
|
# Host port to expose (default: 8000).
|
|
82
113
|
# PORT=8000
|
|
83
114
|
|
|
@@ -127,7 +158,7 @@ VAULT_PASSWORD=${answers.vaultPassword}`;
|
|
|
127
158
|
? `# Obsidian Sync auth token — FILL THIS IN before docker compose up.
|
|
128
159
|
# Generate once with:
|
|
129
160
|
# docker run --rm -it --entrypoint get-token \\
|
|
130
|
-
#
|
|
161
|
+
# ${GET_TOKEN_IMAGE}`
|
|
131
162
|
: `# Obsidian Sync auth token.`;
|
|
132
163
|
return `# vault-cortex — remote quickstart (Obsidian Sync)
|
|
133
164
|
# Generated by \`npx vault-cortex init\`. Full option reference:
|
package/dist/init.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { join, resolve } from "node:path";
|
|
2
2
|
import { buildLocalEnv, buildRemoteEnv } from "./env.js";
|
|
3
3
|
import { buildLocalConnectMessage, buildRemoteConnectMessage, } from "./messages.js";
|
|
4
|
-
import {
|
|
4
|
+
import { GET_TOKEN_IMAGE, pollHealth } from "./docker.js";
|
|
5
5
|
import { buildFilesToWrite, readEnvPort, writeFiles, } from "./scaffold.js";
|
|
6
6
|
import { generateToken } from "./token.js";
|
|
7
7
|
import { expandTilde, validateVaultPath } from "./vault.js";
|
|
@@ -24,9 +24,9 @@ const askMode = async (prompts) => {
|
|
|
24
24
|
return isMode(selected) ? selected : "local";
|
|
25
25
|
};
|
|
26
26
|
const GET_TOKEN_COMMAND = `docker run --rm -it --entrypoint get-token \\
|
|
27
|
-
${
|
|
27
|
+
${GET_TOKEN_IMAGE}`;
|
|
28
28
|
/**
|
|
29
|
-
* Offers to run the
|
|
29
|
+
* Offers to run the vault-cortex image's get-token flow in this terminal.
|
|
30
30
|
* Returns true only when it ran to completion (and so printed a token the
|
|
31
31
|
* user can scroll up to). The handoff log exists because the clack UI gives
|
|
32
32
|
* way to raw docker output — image pull, then the tool's own login prompts.
|
|
@@ -243,7 +243,7 @@ const runLocalInit = async (flags, deps) => {
|
|
|
243
243
|
// Remote flow (VPS + Obsidian Sync): resolve target dir → PUBLIC_URL →
|
|
244
244
|
// VAULT_NAME → Obsidian Sync token (optionally running get-token via
|
|
245
245
|
// Docker) → optional E2E vault password → generate token → write the
|
|
246
|
-
//
|
|
246
|
+
// single-service compose + .env → optionally start → print connect
|
|
247
247
|
// instructions. Always interactive — the sync-token step can't be defaulted.
|
|
248
248
|
const runRemoteInit = async (flags, deps) => {
|
|
249
249
|
const { prompts, docker } = deps;
|
|
@@ -296,8 +296,8 @@ const runRemoteInit = async (flags, deps) => {
|
|
|
296
296
|
if (tokenWritten)
|
|
297
297
|
prompts.log("Generated MCP auth token (saved to .env).");
|
|
298
298
|
const port = readEnvPort(join(targetDir, ".env"));
|
|
299
|
-
// Without the sync token the
|
|
300
|
-
// only offer compose up when it was provided.
|
|
299
|
+
// Without the sync token the container can't start (init-check-auth fails
|
|
300
|
+
// and s6 stops it), so only offer compose up when it was provided.
|
|
301
301
|
const started = obsidianAuthToken === ""
|
|
302
302
|
? false
|
|
303
303
|
: await offerComposeUp({ targetDir, port }, deps);
|
package/dist/messages.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { styleText } from "node:util";
|
|
2
|
-
// Strip styling when stdout isn't a color TTY (piped output,
|
|
2
|
+
// Strip styling when stdout isn't a color TTY (piped output, CI) or NO_COLOR
|
|
3
|
+
// is set (any value, including empty — per the NO_COLOR spec) so
|
|
3
4
|
// captured/redirected output stays plain — no stray escape codes in copied
|
|
4
5
|
// commands or logs.
|
|
5
|
-
const paint = (style, text) => process.stdout.isTTY && !process.env
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
const
|
|
6
|
+
const paint = (style, text) => process.stdout.isTTY && !("NO_COLOR" in process.env)
|
|
7
|
+
? styleText(style, text)
|
|
8
|
+
: text;
|
|
9
|
+
const RULE_WIDTH = 56;
|
|
10
|
+
const topRule = (label) => paint("dim", `╭── ${label} ${"─".repeat(Math.max(0, RULE_WIDTH - label.length - 6))}╮`);
|
|
11
|
+
const bottomRule = () => paint("dim", `╰${"─".repeat(RULE_WIDTH - 2)}╯`);
|
|
12
|
+
const sectionRule = (label) => paint("dim", `── ${label} ${"─".repeat(Math.max(0, RULE_WIDTH - label.length - 4))}`);
|
|
13
|
+
// targetDir is quoted: these lines are meant to be copy-pasted into a
|
|
14
|
+
// shell, and an unquoted path breaks on spaces or special characters.
|
|
15
|
+
const composeUpCommand = (targetDir) => `cd "${targetDir}" && docker compose up -d`;
|
|
11
16
|
const startServerLine = (targetDir) => `Start the server:\n ${composeUpCommand(targetDir)}`;
|
|
12
17
|
/** Remote start line: running, blocked on the missing sync token, or ready to start. */
|
|
13
18
|
const remoteStartLine = (params) => {
|
|
@@ -57,6 +62,10 @@ const curlGuidance = (mcpUrl) => `Clients without OAuth, scripts, and curl send
|
|
|
57
62
|
curl -H "Authorization: Bearer <token>" ${mcpUrl}`;
|
|
58
63
|
const smokeTest = (healthUrl) => `Smoke test:
|
|
59
64
|
curl ${healthUrl}`;
|
|
65
|
+
// Compose does not pull new images on `up` — without this hint users stay
|
|
66
|
+
// on the image from init day forever while believing they track releases.
|
|
67
|
+
const updateGuidance = (targetDir) => `Update to the latest release:
|
|
68
|
+
cd "${targetDir}" && docker compose pull && docker compose up -d`;
|
|
60
69
|
/**
|
|
61
70
|
* Local-mode "Connect" message. port comes from the .env on disk: a kept file
|
|
62
71
|
* may override the default, so the message must describe the server that will
|
|
@@ -75,10 +84,12 @@ export const buildLocalConnectMessage = (params) => {
|
|
|
75
84
|
// that claude.ai can't reach localhost at all and Claude Desktop needs the
|
|
76
85
|
// mcp-remote bridge (the dialog rejects http, but mcp-remote exempts
|
|
77
86
|
// localhost, so no --allow-http).
|
|
78
|
-
const connectMessage = `${
|
|
87
|
+
const connectMessage = `${topRule("Connect")}
|
|
79
88
|
|
|
80
89
|
${startLine}
|
|
81
90
|
|
|
91
|
+
${sectionRule("MCP client")}
|
|
92
|
+
|
|
82
93
|
${connectUrlBlock(`${baseUrl}/mcp`, tokenLine)}
|
|
83
94
|
|
|
84
95
|
${connectGuidance(`${baseUrl}/mcp`)}
|
|
@@ -93,15 +104,23 @@ it with mcp-remote:
|
|
|
93
104
|
"--header", "Authorization: Bearer <token above>"]
|
|
94
105
|
}
|
|
95
106
|
|
|
107
|
+
${sectionRule("Non-OAuth")}
|
|
108
|
+
|
|
96
109
|
${curlGuidance(`${baseUrl}/mcp`)}
|
|
97
110
|
|
|
98
111
|
${smokeTest(`${baseUrl}/healthz`)}
|
|
99
112
|
|
|
113
|
+
${sectionRule("Settings")}
|
|
114
|
+
|
|
100
115
|
Optional settings (timezone, memory folder, port, logging) are commented
|
|
101
116
|
out in ${targetDir}/.env — uncomment, set a value, then apply with
|
|
102
117
|
"docker compose up -d" (restart alone does not re-read .env).
|
|
103
118
|
|
|
104
|
-
|
|
119
|
+
${updateGuidance(targetDir)}
|
|
120
|
+
|
|
121
|
+
Full docs: https://github.com/aliasunder/vault-cortex/blob/main/deploy/local/README.md
|
|
122
|
+
|
|
123
|
+
${bottomRule()}`;
|
|
105
124
|
return connectMessage;
|
|
106
125
|
};
|
|
107
126
|
/**
|
|
@@ -135,24 +154,34 @@ Claude Desktop only accept https URLs — set up HTTPS for those clients
|
|
|
135
154
|
(see the HTTPS section in the remote guide).`;
|
|
136
155
|
// Flush-left on purpose: this is printed as plain text (see paint), so
|
|
137
156
|
// leading whitespace would render as literal indentation.
|
|
138
|
-
const connectMessage = `${
|
|
157
|
+
const connectMessage = `${topRule("Connect")}
|
|
139
158
|
|
|
140
159
|
${startLine}
|
|
141
160
|
|
|
161
|
+
${sectionRule("MCP client")}
|
|
162
|
+
|
|
142
163
|
${connectUrlBlock(`${publicUrl}/mcp`, tokenLine)}
|
|
143
164
|
|
|
144
165
|
${clientGuidance}
|
|
145
166
|
|
|
167
|
+
${sectionRule("Non-OAuth")}
|
|
168
|
+
|
|
146
169
|
${curlGuidance(`${publicUrl}/mcp`)}
|
|
147
170
|
|
|
148
171
|
${smokeTest(`${publicUrl}/healthz`)}
|
|
149
172
|
|
|
173
|
+
${sectionRule("Settings")}
|
|
174
|
+
|
|
150
175
|
Optional settings (timezone, memory folder, port, logging, sync
|
|
151
176
|
behavior) are commented out in ${targetDir}/.env — uncomment, set a
|
|
152
177
|
value, then apply with "docker compose up -d" (restart alone does not
|
|
153
178
|
re-read .env).
|
|
154
179
|
|
|
180
|
+
${updateGuidance(targetDir)}
|
|
181
|
+
|
|
155
182
|
For HTTPS options (API Gateway, Caddy, Cloudflare Tunnel), see:
|
|
156
|
-
https://github.com/aliasunder/vault-cortex/blob/main/deploy/remote/README.md#https-access
|
|
183
|
+
https://github.com/aliasunder/vault-cortex/blob/main/deploy/remote/README.md#https-access
|
|
184
|
+
|
|
185
|
+
${bottomRule()}`;
|
|
157
186
|
return connectMessage;
|
|
158
187
|
};
|
package/package.json
CHANGED
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
name: vault-cortex
|
|
15
15
|
|
|
16
16
|
services:
|
|
17
|
-
vault-
|
|
18
|
-
image: ghcr.io/aliasunder/vault-
|
|
19
|
-
container_name: vault-
|
|
17
|
+
vault-cortex:
|
|
18
|
+
image: ghcr.io/aliasunder/vault-cortex:latest
|
|
19
|
+
container_name: vault-cortex
|
|
20
20
|
restart: unless-stopped
|
|
21
21
|
environment:
|
|
22
22
|
PORT: "8000"
|
|
@@ -41,9 +41,9 @@ services:
|
|
|
41
41
|
# PROTECTED_PATHS default: "<MEMORY_DIR>, Daily Notes" (blocked from vault_delete_note)
|
|
42
42
|
# ORPHAN_EXCLUDE_FOLDERS default: "Daily Notes, Templates, <MEMORY_DIR>" (excluded from vault_find_orphans)
|
|
43
43
|
# SERVICE_DOCUMENTATION_URL default: https://github.com/aliasunder/vault-cortex
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
PROTECTED_PATHS: ${PROTECTED_PATHS:-}
|
|
45
|
+
ORPHAN_EXCLUDE_FOLDERS: ${ORPHAN_EXCLUDE_FOLDERS:-}
|
|
46
|
+
SERVICE_DOCUMENTATION_URL: ${SERVICE_DOCUMENTATION_URL:-}
|
|
47
47
|
volumes:
|
|
48
48
|
- "${VAULT_PATH:?Set VAULT_PATH to your Obsidian vault folder}:/vault:rw"
|
|
49
49
|
- mcp_data:/data
|
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
# vault-cortex — remote quickstart (Obsidian Sync)
|
|
2
2
|
#
|
|
3
3
|
# Run vault-cortex on a VPS with Obsidian Sync for remote access from any device.
|
|
4
|
-
#
|
|
4
|
+
# One container: the :remote image bundles Obsidian Sync and the MCP server
|
|
5
|
+
# under s6-overlay supervision.
|
|
5
6
|
#
|
|
6
7
|
# 1. cp .env.example .env (then fill in required values)
|
|
7
8
|
# 2. docker compose up -d
|
|
8
9
|
# 3. Connect your MCP client to http://<your-server>:8000/mcp
|
|
9
10
|
#
|
|
11
|
+
# Compose is optional — the same container runs with plain `docker run`
|
|
12
|
+
# (or podman/nerdctl); see the README for the one-liner.
|
|
13
|
+
#
|
|
10
14
|
# Full docs: https://github.com/aliasunder/vault-cortex
|
|
11
15
|
|
|
12
16
|
name: vault-cortex
|
|
13
17
|
|
|
14
18
|
services:
|
|
15
|
-
|
|
16
|
-
image: ghcr.io/
|
|
17
|
-
container_name:
|
|
19
|
+
vault-cortex:
|
|
20
|
+
image: ghcr.io/aliasunder/vault-cortex:remote
|
|
21
|
+
container_name: vault-cortex
|
|
18
22
|
hostname: ${DEVICE_NAME:-vault-cortex}
|
|
19
23
|
restart: unless-stopped
|
|
20
24
|
environment:
|
|
25
|
+
# --- Obsidian Sync (s6 init chain + sync service) ---
|
|
21
26
|
OBSIDIAN_AUTH_TOKEN: "${OBSIDIAN_AUTH_TOKEN:?Set OBSIDIAN_AUTH_TOKEN — see .env.example}"
|
|
22
27
|
VAULT_NAME: "${VAULT_NAME:?Set VAULT_NAME to your Obsidian vault name (case-sensitive)}"
|
|
23
28
|
VAULT_PASSWORD: ${VAULT_PASSWORD:-}
|
|
@@ -26,28 +31,7 @@ services:
|
|
|
26
31
|
DEVICE_NAME: ${DEVICE_NAME:-vault-cortex}
|
|
27
32
|
CONFLICT_STRATEGY: ${CONFLICT_STRATEGY:-merge}
|
|
28
33
|
SYNC_MODE: ${SYNC_MODE:-bidirectional}
|
|
29
|
-
|
|
30
|
-
volumes:
|
|
31
|
-
- vault_data:/vault
|
|
32
|
-
- obsidian_config:/home/obsidian/.config
|
|
33
|
-
healthcheck:
|
|
34
|
-
test: ["CMD-SHELL", "test -d /vault && pgrep -f 'ob sync' >/dev/null"]
|
|
35
|
-
interval: 30s
|
|
36
|
-
timeout: 5s
|
|
37
|
-
retries: 5
|
|
38
|
-
start_period: 60s
|
|
39
|
-
logging:
|
|
40
|
-
driver: json-file
|
|
41
|
-
options: { max-size: "10m", max-file: "3" }
|
|
42
|
-
|
|
43
|
-
vault-mcp:
|
|
44
|
-
image: ghcr.io/aliasunder/vault-mcp:latest
|
|
45
|
-
container_name: vault-mcp
|
|
46
|
-
restart: unless-stopped
|
|
47
|
-
depends_on:
|
|
48
|
-
obsidian-sync:
|
|
49
|
-
condition: service_healthy
|
|
50
|
-
environment:
|
|
34
|
+
# --- MCP server ---
|
|
51
35
|
PORT: "8000"
|
|
52
36
|
HOST: "0.0.0.0"
|
|
53
37
|
VAULT_PATH: /vault
|
|
@@ -62,19 +46,23 @@ services:
|
|
|
62
46
|
LOG_DIR: ${LOG_DIR:-/data/logs}
|
|
63
47
|
LOG_RETENTION_DAYS: ${LOG_RETENTION_DAYS:-30}
|
|
64
48
|
TZ: ${TZ:-UTC}
|
|
49
|
+
WINDOWS_MODE: ${WINDOWS_MODE:-false}
|
|
65
50
|
# Optional overrides. When unset, the server applies smart defaults
|
|
66
51
|
# (<MEMORY_DIR> below is the resolved MEMORY_DIR value, default "About Me"):
|
|
67
52
|
# PROTECTED_PATHS default: "<MEMORY_DIR>, Daily Notes" (blocked from vault_delete_note)
|
|
68
53
|
# ORPHAN_EXCLUDE_FOLDERS default: "Daily Notes, Templates, <MEMORY_DIR>" (excluded from vault_find_orphans)
|
|
69
54
|
# SERVICE_DOCUMENTATION_URL default: https://github.com/aliasunder/vault-cortex
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
55
|
+
PROTECTED_PATHS: ${PROTECTED_PATHS:-}
|
|
56
|
+
ORPHAN_EXCLUDE_FOLDERS: ${ORPHAN_EXCLUDE_FOLDERS:-}
|
|
57
|
+
SERVICE_DOCUMENTATION_URL: ${SERVICE_DOCUMENTATION_URL:-}
|
|
73
58
|
volumes:
|
|
74
|
-
- vault_data:/vault
|
|
59
|
+
- vault_data:/vault
|
|
75
60
|
- mcp_data:/data
|
|
61
|
+
- obsidian_config:/home/obsidian/.config
|
|
76
62
|
ports:
|
|
77
63
|
- "0.0.0.0:${PORT:-8000}:8000"
|
|
64
|
+
# start_period covers the s6 init chain (Obsidian login + sync-setup)
|
|
65
|
+
# that runs before the MCP server boots.
|
|
78
66
|
healthcheck:
|
|
79
67
|
test:
|
|
80
68
|
[
|
|
@@ -85,8 +73,8 @@ services:
|
|
|
85
73
|
]
|
|
86
74
|
interval: 15s
|
|
87
75
|
timeout: 5s
|
|
88
|
-
retries:
|
|
89
|
-
start_period:
|
|
76
|
+
retries: 5
|
|
77
|
+
start_period: 60s
|
|
90
78
|
logging:
|
|
91
79
|
driver: json-file
|
|
92
80
|
options: { max-size: "10m", max-file: "3" }
|