vault-cortex 0.10.7 → 0.12.0-beta.60
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/env.js +55 -22
- package/dist/init.js +4 -2
- package/dist/lifecycle.js +2 -1
- package/dist/optional-settings.js +3 -3
- package/dist/scaffold.js +37 -5
- package/package.json +1 -1
package/dist/env.js
CHANGED
|
@@ -94,17 +94,28 @@ MEMORY_DIR=About Me
|
|
|
94
94
|
# Host port to expose (default: 8000).
|
|
95
95
|
PORT=8000
|
|
96
96
|
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
97
|
+
# These two settings choose the client IP used for OAuth rate limiting and
|
|
98
|
+
# logs (defaults shown below). Without the right values, every visitor
|
|
99
|
+
# behind a proxy shares one rate-limit budget. TRUST_PROXY_HOPS counts the
|
|
100
|
+
# proxies you control that write X-Forwarded-For; TRUST_FORWARDED_HOPS
|
|
101
|
+
# counts the ones that write the RFC 7239 Forwarded header
|
|
102
|
+
# (https://www.rfc-editor.org/rfc/rfc7239), and 0 ignores that header.
|
|
103
|
+
#
|
|
104
|
+
# What sits in front of the server TRUST_PROXY_HOPS TRUST_FORWARDED_HOPS Client IP is
|
|
105
|
+
# Nothing (default) 0 0 the visitor (socket peer)
|
|
106
|
+
# A proxy or tunnel you control 1 0 the visitor (X-Forwarded-For)
|
|
107
|
+
# (Caddy, nginx, Cloudflare Tunnel)
|
|
108
|
+
# A proxy that reports visitors in 1 1 the visitor (Forwarded)
|
|
109
|
+
# Forwarded (e.g. AWS API Gateway)
|
|
110
|
+
# A CDN in front of that proxy, and 1 2 the visitor (Forwarded)
|
|
111
|
+
# the CDN is the only way to reach it
|
|
112
|
+
#
|
|
113
|
+
# When the Forwarded header is read (TRUST_FORWARDED_HOPS above 0), it wins
|
|
114
|
+
# and TRUST_PROXY_HOPS is not consulted; TRUST_PROXY_HOPS is the fallback
|
|
115
|
+
# when that header is ignored or absent. Faked headers from visitors are
|
|
116
|
+
# ignored in every row — only the counted proxies are trusted.
|
|
106
117
|
# TRUST_PROXY_HOPS=0
|
|
107
|
-
#
|
|
118
|
+
# TRUST_FORWARDED_HOPS=0
|
|
108
119
|
|
|
109
120
|
# Log verbosity: debug | info | warn | error (default: info).
|
|
110
121
|
LOG_LEVEL=info
|
|
@@ -221,17 +232,28 @@ MEMORY_DIR=About Me
|
|
|
221
232
|
# Host port to expose (default: 8000).
|
|
222
233
|
PORT=8000
|
|
223
234
|
|
|
224
|
-
#
|
|
225
|
-
#
|
|
226
|
-
#
|
|
227
|
-
#
|
|
228
|
-
#
|
|
229
|
-
#
|
|
230
|
-
#
|
|
231
|
-
#
|
|
232
|
-
#
|
|
235
|
+
# These two settings choose the client IP used for OAuth rate limiting and
|
|
236
|
+
# logs (defaults shown below). Without the right values, every visitor
|
|
237
|
+
# behind a proxy shares one rate-limit budget. TRUST_PROXY_HOPS counts the
|
|
238
|
+
# proxies you control that write X-Forwarded-For; TRUST_FORWARDED_HOPS
|
|
239
|
+
# counts the ones that write the RFC 7239 Forwarded header
|
|
240
|
+
# (https://www.rfc-editor.org/rfc/rfc7239), and 0 ignores that header.
|
|
241
|
+
#
|
|
242
|
+
# What sits in front of the server TRUST_PROXY_HOPS TRUST_FORWARDED_HOPS Client IP is
|
|
243
|
+
# Nothing (default) 0 0 the visitor (socket peer)
|
|
244
|
+
# A proxy or tunnel you control 1 0 the visitor (X-Forwarded-For)
|
|
245
|
+
# (Caddy, nginx, Cloudflare Tunnel)
|
|
246
|
+
# A proxy that reports visitors in 1 1 the visitor (Forwarded)
|
|
247
|
+
# Forwarded (e.g. AWS API Gateway)
|
|
248
|
+
# A CDN in front of that proxy, and 1 2 the visitor (Forwarded)
|
|
249
|
+
# the CDN is the only way to reach it
|
|
250
|
+
#
|
|
251
|
+
# When the Forwarded header is read (TRUST_FORWARDED_HOPS above 0), it wins
|
|
252
|
+
# and TRUST_PROXY_HOPS is not consulted; TRUST_PROXY_HOPS is the fallback
|
|
253
|
+
# when that header is ignored or absent. Faked headers from visitors are
|
|
254
|
+
# ignored in every row — only the counted proxies are trusted.
|
|
233
255
|
# TRUST_PROXY_HOPS=0
|
|
234
|
-
#
|
|
256
|
+
# TRUST_FORWARDED_HOPS=0
|
|
235
257
|
|
|
236
258
|
# Log verbosity: debug | info | warn | error (default: info).
|
|
237
259
|
LOG_LEVEL=info
|
|
@@ -250,16 +272,27 @@ LOG_RETENTION_DAYS=90
|
|
|
250
272
|
PUID=1000
|
|
251
273
|
PGID=1000
|
|
252
274
|
|
|
253
|
-
# Device name
|
|
275
|
+
# Device name the container reports to Obsidian Sync — labels its changes in the sync log.
|
|
254
276
|
DEVICE_NAME=vault-cortex
|
|
255
277
|
|
|
256
278
|
# Obsidian Sync conflict resolution: merge | conflict (default: merge).
|
|
257
279
|
# 'merge' integrates changes automatically; 'conflict' writes a separate conflict file.
|
|
258
280
|
CONFLICT_STRATEGY=merge
|
|
259
281
|
|
|
260
|
-
# Sync direction: bidirectional | pull-only |
|
|
282
|
+
# Sync direction: bidirectional | pull-only | mirror-remote (default: bidirectional).
|
|
283
|
+
# 'pull-only': server edits are kept locally but never uploaded;
|
|
284
|
+
# 'mirror-remote': server edits are undone, so the server is an exact copy.
|
|
261
285
|
SYNC_MODE=bidirectional
|
|
262
286
|
|
|
287
|
+
# Folders to leave out of sync, comma-separated — the same list as Obsidian's
|
|
288
|
+
# Sync → "Excluded folders". Empty keeps the Sync client's default (nothing excluded).
|
|
289
|
+
SYNC_EXCLUDED_FOLDERS=
|
|
290
|
+
|
|
291
|
+
# Attachment types to sync, comma-separated: image, audio, video, pdf, unsupported —
|
|
292
|
+
# the same toggles as Obsidian's Sync → "Selective sync". Empty keeps the Sync
|
|
293
|
+
# client's default.
|
|
294
|
+
SYNC_FILE_TYPES=
|
|
295
|
+
|
|
263
296
|
# Obsidian settings categories to sync into the server's .obsidian/ folder
|
|
264
297
|
# (default: the two the server reads — daily notes settings and community
|
|
265
298
|
# plugin settings such as the Tasks plugin's format; "none" disables).
|
package/dist/init.js
CHANGED
|
@@ -6,7 +6,7 @@ import { buildDaemonNotRunningMessage, buildDockerNotInstalledMessage, buildLoca
|
|
|
6
6
|
import { healthPollTimeoutMs, healthTimeoutMessage, pollHealth, } from "./docker.js";
|
|
7
7
|
import { reportPublicUrlProbe } from "./lifecycle.js";
|
|
8
8
|
import { applyOptionalSettings, askOptionalSettings, derivePublicUrlOverride, } from "./optional-settings.js";
|
|
9
|
-
import { buildFilesToWrite, readEnvPort, readEnvPublicUrl, writeFiles, } from "./scaffold.js";
|
|
9
|
+
import { buildFilesToWrite, readEnvPort, readEnvPublicUrl, stripEnvQuotedValues, writeFiles, } from "./scaffold.js";
|
|
10
10
|
import { generateToken } from "./token.js";
|
|
11
11
|
import { expandTilde, validateVaultPath } from "./vault.js";
|
|
12
12
|
const DEFAULT_TARGET_DIR = "./vault-cortex";
|
|
@@ -174,9 +174,11 @@ const offerDockerRun = async (params, deps) => {
|
|
|
174
174
|
const startNow = await prompts.confirm("Start the server now?", true);
|
|
175
175
|
if (!startNow)
|
|
176
176
|
return "not-started";
|
|
177
|
+
const envFilePath = join(targetDir, ".env");
|
|
178
|
+
stripEnvQuotedValues(envFilePath);
|
|
177
179
|
const containerStarted = docker.dockerRun({
|
|
178
180
|
mode,
|
|
179
|
-
envFilePath
|
|
181
|
+
envFilePath,
|
|
180
182
|
port,
|
|
181
183
|
vaultPath,
|
|
182
184
|
});
|
package/dist/lifecycle.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { join, resolve } from "node:path";
|
|
2
2
|
import { CONTAINER_NAME, healthPollTimeoutMs, healthTimeoutMessage, pollHealth, probeHealth, } from "./docker.js";
|
|
3
3
|
import { buildDaemonNotRunningMessage, buildDockerNotInstalledMessage, } from "./messages.js";
|
|
4
|
-
import { detectMode, hasEnvPublicUrl, readEnvPort, readEnvPublicUrl, readEnvVaultPath, } from "./scaffold.js";
|
|
4
|
+
import { detectMode, hasEnvPublicUrl, readEnvPort, readEnvPublicUrl, readEnvVaultPath, stripEnvQuotedValues, } from "./scaffold.js";
|
|
5
5
|
import { expandTilde } from "./vault.js";
|
|
6
6
|
const DEFAULT_TARGET_DIR = "./vault-cortex";
|
|
7
7
|
/**
|
|
@@ -110,6 +110,7 @@ export const recreateContainer = async (params, deps) => {
|
|
|
110
110
|
prompts.error(`Could not remove the existing container — check: docker rm -f ${CONTAINER_NAME}`);
|
|
111
111
|
return 1;
|
|
112
112
|
}
|
|
113
|
+
stripEnvQuotedValues(deployment.envFilePath);
|
|
113
114
|
prompts.log("Starting container...");
|
|
114
115
|
const containerStarted = docker.dockerRun({
|
|
115
116
|
mode: deployment.mode,
|
|
@@ -69,9 +69,9 @@ const OPTIONAL_SETTINGS = [
|
|
|
69
69
|
hint: "receive changes but never push",
|
|
70
70
|
},
|
|
71
71
|
{
|
|
72
|
-
value: "
|
|
73
|
-
label: "
|
|
74
|
-
hint: "
|
|
72
|
+
value: "mirror-remote",
|
|
73
|
+
label: "Mirror remote",
|
|
74
|
+
hint: "receive changes and revert any server-side edit",
|
|
75
75
|
},
|
|
76
76
|
],
|
|
77
77
|
defaultValue: "bidirectional",
|
package/dist/scaffold.js
CHANGED
|
@@ -2,8 +2,8 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync, } from "
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
/** Default host port — matches the container's internal port. */
|
|
4
4
|
export const DEFAULT_PORT = 8000;
|
|
5
|
-
/** Matches an active (uncommented) PORT line
|
|
6
|
-
const ENV_PORT_LINE = /^PORT=(\d+)
|
|
5
|
+
/** Matches an active (uncommented) PORT line, with optional surrounding quotes. */
|
|
6
|
+
const ENV_PORT_LINE = /^PORT=["']?(\d+)["']?\s*$/m;
|
|
7
7
|
/** Matches an active (uncommented) VAULT_PATH line in a .env file. */
|
|
8
8
|
const ENV_VAULT_PATH_LINE = /^VAULT_PATH=(.+)\s*$/m;
|
|
9
9
|
/** Matches an active (uncommented) PUBLIC_URL line. */
|
|
@@ -12,6 +12,17 @@ const ENV_PUBLIC_URL_LINE = /^PUBLIC_URL=/m;
|
|
|
12
12
|
const ENV_PUBLIC_URL_VALUE_LINE = /^PUBLIC_URL=(.+)\s*$/m;
|
|
13
13
|
/** Matches an active (uncommented) OBSIDIAN_AUTH_TOKEN line. */
|
|
14
14
|
const OBSIDIAN_AUTH_TOKEN_LINE = /^OBSIDIAN_AUTH_TOKEN=/m;
|
|
15
|
+
/** Matches an env line whose value is wrapped in matching quotes. */
|
|
16
|
+
const QUOTED_ENV_VALUE = /^([A-Za-z_][A-Za-z0-9_]*=)(["'])(.*)\2(\s*)$/gm;
|
|
17
|
+
/**
|
|
18
|
+
* Strips matching surrounding quotes from a value — `"foo"` → `foo`,
|
|
19
|
+
* `'bar'` → `bar`, `unquoted` → `unquoted`. Only strips when the
|
|
20
|
+
* opening and closing quote characters match.
|
|
21
|
+
*/
|
|
22
|
+
const stripSurroundingQuotes = (value) => {
|
|
23
|
+
const quoteMatch = /^(["'])(.*)\1$/.exec(value);
|
|
24
|
+
return quoteMatch ? quoteMatch[2] : value;
|
|
25
|
+
};
|
|
15
26
|
export const buildFilesToWrite = (envContent) => [
|
|
16
27
|
// .env holds the bearer token (and possibly a vault password) — owner-only.
|
|
17
28
|
{ name: ".env", content: envContent, mode: 0o600 },
|
|
@@ -36,7 +47,8 @@ export const readEnvVaultPath = (envFilePath) => {
|
|
|
36
47
|
if (!existsSync(envFilePath))
|
|
37
48
|
return undefined;
|
|
38
49
|
const match = ENV_VAULT_PATH_LINE.exec(readFileSync(envFilePath, "utf8"));
|
|
39
|
-
|
|
50
|
+
const rawValue = match?.[1].trim();
|
|
51
|
+
return rawValue ? stripSurroundingQuotes(rawValue) : undefined;
|
|
40
52
|
};
|
|
41
53
|
/**
|
|
42
54
|
* Returns true when the .env file has an active (uncommented) PUBLIC_URL line.
|
|
@@ -63,11 +75,12 @@ export const readEnvPublicUrl = (envFilePath) => {
|
|
|
63
75
|
const match = ENV_PUBLIC_URL_VALUE_LINE.exec(readFileSync(envFilePath, "utf8"));
|
|
64
76
|
// A whitespace-only line matches the regex and trims to "" — normalize to
|
|
65
77
|
// undefined so the non-empty contract holds ("" is never a legitimate URL).
|
|
66
|
-
const
|
|
78
|
+
const rawValue = match?.[1].trim();
|
|
79
|
+
const unquotedValue = rawValue ? stripSurroundingQuotes(rawValue) : undefined;
|
|
67
80
|
// Strip trailing slashes (mirroring askPublicUrl's prompt-side
|
|
68
81
|
// normalization): consumers append paths to this base, and a hand-edited
|
|
69
82
|
// `https://host/` would otherwise print broken `//mcp` connect URLs.
|
|
70
|
-
const normalizedPublicUrl =
|
|
83
|
+
const normalizedPublicUrl = unquotedValue?.replace(/\/+$/, "");
|
|
71
84
|
return normalizedPublicUrl || undefined;
|
|
72
85
|
};
|
|
73
86
|
/**
|
|
@@ -100,6 +113,25 @@ export const patchEnvObsidianToken = (envFilePath, token) => {
|
|
|
100
113
|
writeFileSync(envFilePath, patched);
|
|
101
114
|
return true;
|
|
102
115
|
};
|
|
116
|
+
/**
|
|
117
|
+
* Strips surrounding quotes from env values in the file. `docker run
|
|
118
|
+
* --env-file` passes quotes literally (`VAULT_NAME="My Vault"` becomes
|
|
119
|
+
* the value `"My Vault"` with embedded quotes), while Compose strips
|
|
120
|
+
* them. Removing quotes makes the file work correctly for both paths.
|
|
121
|
+
* Returns true when the file was modified.
|
|
122
|
+
*/
|
|
123
|
+
export const stripEnvQuotedValues = (envFilePath) => {
|
|
124
|
+
if (!existsSync(envFilePath))
|
|
125
|
+
return false;
|
|
126
|
+
const content = readFileSync(envFilePath, "utf8");
|
|
127
|
+
// Reset lastIndex — the /g flag makes the regex stateful.
|
|
128
|
+
QUOTED_ENV_VALUE.lastIndex = 0;
|
|
129
|
+
const sanitized = content.replace(QUOTED_ENV_VALUE, "$1$3$4");
|
|
130
|
+
if (sanitized === content)
|
|
131
|
+
return false;
|
|
132
|
+
writeFileSync(envFilePath, sanitized);
|
|
133
|
+
return true;
|
|
134
|
+
};
|
|
103
135
|
/**
|
|
104
136
|
* Writes the files into targetDir (created if missing). Existing files
|
|
105
137
|
* are never overwritten silently: identical content is skipped, and differing
|
package/package.json
CHANGED