@hanna84/mcp-writing 1.6.2 → 1.7.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/CHANGELOG.md +20 -0
- package/README.md +41 -4
- package/index.js +45 -1
- package/package.json +2 -1
- package/scripts/setup-openclaw-env.sh +64 -0
- package/sync.js +24 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,11 +4,31 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
+
#### [v1.7.0](https://github.com/hannasdev/mcp-writing.git
|
|
8
|
+
/compare/v1.6.3...v1.7.0)
|
|
9
|
+
|
|
10
|
+
- feat: configurable OpenClaw runtime identity and ownership guardrails [`#49`](https://github.com/hannasdev/mcp-writing.git
|
|
11
|
+
/pull/49)
|
|
12
|
+
|
|
13
|
+
#### [v1.6.3](https://github.com/hannasdev/mcp-writing.git
|
|
14
|
+
/compare/v1.6.2...v1.6.3)
|
|
15
|
+
|
|
16
|
+
> 19 April 2026
|
|
17
|
+
|
|
18
|
+
- docs: sync PRD with current app behavior [`#48`](https://github.com/hannasdev/mcp-writing.git
|
|
19
|
+
/pull/48)
|
|
20
|
+
- Release 1.6.3 [`53add10`](https://github.com/hannasdev/mcp-writing.git
|
|
21
|
+
/commit/53add102551c586c81aaef249509eec2c5487f96)
|
|
22
|
+
|
|
7
23
|
#### [v1.6.2](https://github.com/hannasdev/mcp-writing.git
|
|
8
24
|
/compare/v1.6.1...v1.6.2)
|
|
9
25
|
|
|
26
|
+
> 19 April 2026
|
|
27
|
+
|
|
10
28
|
- docs: correct README license footer to AGPL-3.0-only [`#47`](https://github.com/hannasdev/mcp-writing.git
|
|
11
29
|
/pull/47)
|
|
30
|
+
- Release 1.6.2 [`27e92ab`](https://github.com/hannasdev/mcp-writing.git
|
|
31
|
+
/commit/27e92ab8718b4c5736cf901c479dc91ea8259be0)
|
|
12
32
|
|
|
13
33
|
#### [v1.6.1](https://github.com/hannasdev/mcp-writing.git
|
|
14
34
|
/compare/v1.6.0...v1.6.1)
|
package/README.md
CHANGED
|
@@ -339,13 +339,16 @@ Paginated tools (`find_scenes`, `get_arc`, `list_threads`, `get_thread_arc`, `se
|
|
|
339
339
|
# docker-compose.yml snippet
|
|
340
340
|
writing-mcp:
|
|
341
341
|
build: .
|
|
342
|
-
user: "${
|
|
342
|
+
user: "${OPENCLAW_UID:-1000}:${OPENCLAW_GID:-1000}"
|
|
343
343
|
environment:
|
|
344
344
|
WRITING_SYNC_DIR: /sync
|
|
345
345
|
DB_PATH: /data/writing.db
|
|
346
346
|
HTTP_PORT: "3000"
|
|
347
|
+
OWNERSHIP_GUARD_MODE: "${OWNERSHIP_GUARD_MODE:-warn}"
|
|
348
|
+
GIT_SSH_COMMAND: "ssh -i /ssh/id_ed25519 -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=/ssh/known_hosts"
|
|
347
349
|
volumes:
|
|
348
|
-
- /
|
|
350
|
+
- ${OPENCLAW_WORKSPACE_DIR:?run scripts/setup-openclaw-env.sh first}/sync:/sync
|
|
351
|
+
- ${OPENCLAW_SSH_DIR:?run scripts/setup-openclaw-env.sh first}:/ssh:ro
|
|
349
352
|
- writing-mcp-data:/data
|
|
350
353
|
healthcheck:
|
|
351
354
|
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
|
|
@@ -357,7 +360,15 @@ volumes:
|
|
|
357
360
|
writing-mcp-data:
|
|
358
361
|
```
|
|
359
362
|
|
|
360
|
-
|
|
363
|
+
Recommended: start from `docker-compose.example.yml` and generate `.env` with machine-specific values:
|
|
364
|
+
|
|
365
|
+
```sh
|
|
366
|
+
sh scripts/setup-openclaw-env.sh
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
That script writes `OPENCLAW_UID`, `OPENCLAW_GID`, `OPENCLAW_WORKSPACE_DIR`, and `OPENCLAW_SSH_DIR` to `.env`.
|
|
370
|
+
Running Compose without these values is unsupported and may create invalid mount definitions.
|
|
371
|
+
It also normalizes `OWNERSHIP_GUARD_MODE` to `warn` or `fail` and preserves an existing valid value when rerun.
|
|
361
372
|
|
|
362
373
|
Then register in your OpenClaw config:
|
|
363
374
|
|
|
@@ -380,8 +391,17 @@ When `mcp-writing` runs behind OpenClaw (or any Docker MCP gateway), these detai
|
|
|
380
391
|
|
|
381
392
|
- Set `WRITING_SYNC_DIR=/sync`
|
|
382
393
|
- Set `DB_PATH=/data/writing.db`
|
|
394
|
+
- Set `OWNERSHIP_GUARD_MODE=warn` (or `fail` to block startup on ownership drift)
|
|
383
395
|
- Mount your manuscript sync repo to `/sync`
|
|
384
396
|
- Mount a persistent path for SQLite data at `/data`
|
|
397
|
+
- Mount SSH materials read-only at `/ssh` and use `GIT_SSH_COMMAND` with `/ssh` paths
|
|
398
|
+
|
|
399
|
+
Debug/test-only runtime override knobs:
|
|
400
|
+
|
|
401
|
+
- `RUNTIME_UID_OVERRIDE` — test helper to simulate runtime UID during ownership diagnostics
|
|
402
|
+
- `ALLOW_RUNTIME_UID_OVERRIDE=1` — explicitly enables the override outside `NODE_ENV=test`
|
|
403
|
+
|
|
404
|
+
Do not set these in normal production or desktop deployments.
|
|
385
405
|
|
|
386
406
|
If `/sync` contains raw Scrivener external-sync output, run the importer once before normal `sync` usage:
|
|
387
407
|
|
|
@@ -414,7 +434,7 @@ For private remotes, mount SSH materials read-only and enforce strict host check
|
|
|
414
434
|
Example:
|
|
415
435
|
|
|
416
436
|
```sh
|
|
417
|
-
export GIT_SSH_COMMAND="ssh -i /
|
|
437
|
+
export GIT_SSH_COMMAND="ssh -i /ssh/id_ed25519 -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=/ssh/known_hosts"
|
|
418
438
|
```
|
|
419
439
|
|
|
420
440
|
#### Separate auth and signing keys
|
|
@@ -603,6 +623,22 @@ git config --system --add safe.directory /sync
|
|
|
603
623
|
4. Verify SSH key has write access to the remote and `known_hosts` is mounted.
|
|
604
624
|
5. Prefer branch-per-change workflow (`bot/*` or `edda/*`) if `main` is protected.
|
|
605
625
|
|
|
626
|
+
### "Blocked: file is root-owned" (EACCES / ownership drift)
|
|
627
|
+
|
|
628
|
+
The runtime user can read but cannot overwrite prose files.
|
|
629
|
+
|
|
630
|
+
Fix:
|
|
631
|
+
|
|
632
|
+
1. Repair host ownership once:
|
|
633
|
+
|
|
634
|
+
```sh
|
|
635
|
+
sudo chown -R "$(id -u):$(id -g)" /path/to/sync-dir
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
2. Ensure container user mapping is set from `.env` (`OPENCLAW_UID` / `OPENCLAW_GID`).
|
|
639
|
+
3. Optionally set `OWNERSHIP_GUARD_MODE=fail` to catch mismatches at startup.
|
|
640
|
+
4. Re-check `get_runtime_config` and confirm ownership warnings are gone.
|
|
641
|
+
|
|
606
642
|
### Tests fail after updating Node.js
|
|
607
643
|
|
|
608
644
|
Local install state may be stale after the Node.js change.
|
|
@@ -624,6 +660,7 @@ npm test
|
|
|
624
660
|
| `HTTP_PORT` | `3000` | Port for the MCP SSE endpoint |
|
|
625
661
|
| `MAX_CHAPTER_SCENES` | `10` | Maximum scenes returned by `get_chapter_prose` |
|
|
626
662
|
| `DEFAULT_METADATA_PAGE_SIZE` | `20` | Default page size for paginated tools |
|
|
663
|
+
| `OWNERSHIP_GUARD_MODE` | `warn` | Startup ownership policy: `warn` logs drift, `fail` exits when sampled files are not owned by runtime user |
|
|
627
664
|
|
|
628
665
|
## License
|
|
629
666
|
|
package/index.js
CHANGED
|
@@ -19,6 +19,11 @@ const DB_PATH_DISPLAY = DB_PATH === ":memory:" ? DB_PATH : path.resolve(DB_PATH)
|
|
|
19
19
|
const HTTP_PORT = parseInt(process.env.HTTP_PORT ?? "3000", 10);
|
|
20
20
|
const MAX_CHAPTER_SCENES = parseInt(process.env.MAX_CHAPTER_SCENES ?? "10", 10);
|
|
21
21
|
const DEFAULT_METADATA_PAGE_SIZE = parseInt(process.env.DEFAULT_METADATA_PAGE_SIZE ?? "20", 10);
|
|
22
|
+
const OWNERSHIP_GUARD_MODE_RAW = (process.env.OWNERSHIP_GUARD_MODE ?? "warn").trim().toLowerCase();
|
|
23
|
+
const OWNERSHIP_GUARD_MODE = OWNERSHIP_GUARD_MODE_RAW === "fail" || OWNERSHIP_GUARD_MODE_RAW === "warn"
|
|
24
|
+
? OWNERSHIP_GUARD_MODE_RAW
|
|
25
|
+
: "warn";
|
|
26
|
+
const OWNERSHIP_GUARD_MODE_RAW_DISPLAY = JSON.stringify(OWNERSHIP_GUARD_MODE_RAW);
|
|
22
27
|
|
|
23
28
|
function paginateRows(rows, { page, pageSize, forcePagination = false }) {
|
|
24
29
|
const totalCount = rows.length;
|
|
@@ -302,6 +307,23 @@ function getRuntimeDiagnostics() {
|
|
|
302
307
|
const warnings = [];
|
|
303
308
|
const recommendations = [];
|
|
304
309
|
|
|
310
|
+
if (OWNERSHIP_GUARD_MODE_RAW !== OWNERSHIP_GUARD_MODE) {
|
|
311
|
+
warnings.push(
|
|
312
|
+
`OWNERSHIP_GUARD_MODE_INVALID: Unsupported OWNERSHIP_GUARD_MODE=${OWNERSHIP_GUARD_MODE_RAW_DISPLAY}. Falling back to 'warn'.`
|
|
313
|
+
);
|
|
314
|
+
recommendations.push("Set OWNERSHIP_GUARD_MODE to either 'warn' or 'fail'.");
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (SYNC_OWNERSHIP_DIAGNOSTICS.runtime_uid_override_ignored) {
|
|
318
|
+
warnings.push("RUNTIME_UID_OVERRIDE_IGNORED: RUNTIME_UID_OVERRIDE is ignored unless NODE_ENV=test or ALLOW_RUNTIME_UID_OVERRIDE=1.");
|
|
319
|
+
recommendations.push("Avoid RUNTIME_UID_OVERRIDE in production runtime environments.");
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (SYNC_OWNERSHIP_DIAGNOSTICS.runtime_uid_override_invalid) {
|
|
323
|
+
warnings.push("RUNTIME_UID_OVERRIDE_INVALID: RUNTIME_UID_OVERRIDE must be a non-negative integer when enabled.");
|
|
324
|
+
recommendations.push("Set RUNTIME_UID_OVERRIDE to a non-negative integer, or unset it.");
|
|
325
|
+
}
|
|
326
|
+
|
|
305
327
|
if (!SYNC_DIR_WRITABLE) {
|
|
306
328
|
warnings.push("SYNC_DIR_READ_ONLY: sync dir is read-only; metadata write-back and prose editing tools are unavailable.");
|
|
307
329
|
recommendations.push("Mount WRITING_SYNC_DIR with write access (avoid read-only mounts like ':ro').");
|
|
@@ -316,10 +338,17 @@ function getRuntimeDiagnostics() {
|
|
|
316
338
|
`Repair ownership once on host: sudo chown -R "$(id -u):$(id -g)" "${SYNC_DIR_ABS}"`
|
|
317
339
|
);
|
|
318
340
|
recommendations.push(
|
|
319
|
-
"For Docker, run container as host user (compose: user: \"${
|
|
341
|
+
"For Docker/OpenClaw, run container as host user (compose: user: \"${OPENCLAW_UID:-1000}:${OPENCLAW_GID:-1000}\")."
|
|
320
342
|
);
|
|
321
343
|
}
|
|
322
344
|
|
|
345
|
+
if (OWNERSHIP_GUARD_MODE === "fail" && SYNC_OWNERSHIP_DIAGNOSTICS.runtime_uid === 0) {
|
|
346
|
+
warnings.push(
|
|
347
|
+
"OWNERSHIP_GUARD_SKIPPED_FOR_ROOT: OWNERSHIP_GUARD_MODE=fail is skipped because runtime UID is 0 (root)."
|
|
348
|
+
);
|
|
349
|
+
recommendations.push("Prefer running as a non-root host-mapped UID/GID to make ownership guard checks meaningful.");
|
|
350
|
+
}
|
|
351
|
+
|
|
323
352
|
if (SYNC_OWNERSHIP_DIAGNOSTICS.supported && SYNC_OWNERSHIP_DIAGNOSTICS.root_owned_paths > 0) {
|
|
324
353
|
warnings.push(
|
|
325
354
|
`ROOT_OWNED_PATHS: ${SYNC_OWNERSHIP_DIAGNOSTICS.root_owned_paths} sampled path(s) are owned by UID 0 (root).`
|
|
@@ -353,6 +382,20 @@ if (RUNTIME_DIAGNOSTICS.warnings.length) {
|
|
|
353
382
|
}
|
|
354
383
|
}
|
|
355
384
|
|
|
385
|
+
const SHOULD_ENFORCE_OWNERSHIP_FAIL_GUARD = OWNERSHIP_GUARD_MODE === "fail"
|
|
386
|
+
&& SYNC_OWNERSHIP_DIAGNOSTICS.supported
|
|
387
|
+
&& SYNC_OWNERSHIP_DIAGNOSTICS.runtime_uid !== 0;
|
|
388
|
+
|
|
389
|
+
if (SHOULD_ENFORCE_OWNERSHIP_FAIL_GUARD && SYNC_OWNERSHIP_DIAGNOSTICS.non_runtime_owned_paths > 0) {
|
|
390
|
+
process.stderr.write(
|
|
391
|
+
`[mcp-writing] FATAL: OWNERSHIP_GUARD_MODE=fail and ${SYNC_OWNERSHIP_DIAGNOSTICS.non_runtime_owned_paths} sampled path(s) are not owned by runtime UID ${SYNC_OWNERSHIP_DIAGNOSTICS.runtime_uid}.\n`
|
|
392
|
+
);
|
|
393
|
+
process.stderr.write(
|
|
394
|
+
`[mcp-writing] FATAL: Repair ownership once on the host directory mounted at ${SYNC_DIR_ABS}: sudo chown -R "$(id -u):$(id -g)" /path/to/host-sync-dir\n`
|
|
395
|
+
);
|
|
396
|
+
process.exit(1);
|
|
397
|
+
}
|
|
398
|
+
|
|
356
399
|
// Run sync on startup
|
|
357
400
|
syncAll(db, SYNC_DIR, { writable: SYNC_DIR_WRITABLE });
|
|
358
401
|
|
|
@@ -466,6 +509,7 @@ function createMcpServer() {
|
|
|
466
509
|
sync_dir: SYNC_DIR_ABS,
|
|
467
510
|
db_path: DB_PATH_DISPLAY,
|
|
468
511
|
sync_dir_writable: SYNC_DIR_WRITABLE,
|
|
512
|
+
ownership_guard_mode: OWNERSHIP_GUARD_MODE,
|
|
469
513
|
permission_diagnostics: SYNC_OWNERSHIP_DIAGNOSTICS,
|
|
470
514
|
git_available: GIT_AVAILABLE,
|
|
471
515
|
git_enabled: GIT_ENABLED,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanna84/mcp-writing",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "MCP service for AI-assisted reasoning and editing on long-form fiction projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"scripts": {
|
|
22
22
|
"start": "node --experimental-sqlite index.js",
|
|
23
23
|
"new:entity": "node scripts/new-world-entity.js",
|
|
24
|
+
"setup:openclaw-env": "sh scripts/setup-openclaw-env.sh",
|
|
24
25
|
"release": "release-it",
|
|
25
26
|
"lint": "eslint index.js importer.js db.js sync.js metadata-lint.js scripts/",
|
|
26
27
|
"lint:metadata": "node scripts/lint-metadata.mjs",
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
set -eu
|
|
3
|
+
|
|
4
|
+
ENV_FILE="${1:-.env}"
|
|
5
|
+
OPENCLAW_UID_VALUE="$(id -u)"
|
|
6
|
+
OPENCLAW_GID_VALUE="$(id -g)"
|
|
7
|
+
OPENCLAW_WORKSPACE_DIR_VALUE="${OPENCLAW_WORKSPACE_DIR:-$(pwd)}"
|
|
8
|
+
OPENCLAW_SSH_DIR_VALUE="${OPENCLAW_SSH_DIR:-${HOME:-$(pwd)}/.ssh}"
|
|
9
|
+
OWNERSHIP_GUARD_MODE_VALUE="${2:-${OWNERSHIP_GUARD_MODE:-}}"
|
|
10
|
+
|
|
11
|
+
quote_env_value() {
|
|
12
|
+
printf '"%s"' "$(printf '%s' "$1" | sed 's/["\\]/\\&/g')"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if [ -z "$OWNERSHIP_GUARD_MODE_VALUE" ] && [ -f "$ENV_FILE" ]; then
|
|
16
|
+
EXISTING_GUARD_MODE="$(grep -E '^OWNERSHIP_GUARD_MODE=' "$ENV_FILE" | tail -n1 | cut -d= -f2- || true)"
|
|
17
|
+
OWNERSHIP_GUARD_MODE_VALUE="$EXISTING_GUARD_MODE"
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
if [ -z "$OWNERSHIP_GUARD_MODE_VALUE" ]; then
|
|
21
|
+
OWNERSHIP_GUARD_MODE_VALUE="warn"
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
NORMALIZED_GUARD_MODE="$(printf '%s' "$OWNERSHIP_GUARD_MODE_VALUE" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')"
|
|
25
|
+
if [ "$NORMALIZED_GUARD_MODE" != "warn" ] && [ "$NORMALIZED_GUARD_MODE" != "fail" ]; then
|
|
26
|
+
printf 'Warning: unsupported OWNERSHIP_GUARD_MODE=%s, defaulting to "warn".\n' "$OWNERSHIP_GUARD_MODE_VALUE" >&2
|
|
27
|
+
OWNERSHIP_GUARD_MODE_VALUE="warn"
|
|
28
|
+
else
|
|
29
|
+
OWNERSHIP_GUARD_MODE_VALUE="$NORMALIZED_GUARD_MODE"
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
if TMP_FILE="$(mktemp -t openclaw-env.XXXXXX 2>/dev/null)"; then
|
|
33
|
+
:
|
|
34
|
+
else
|
|
35
|
+
TMP_FILE="$(mktemp "${TMPDIR:-/tmp}/openclaw-env.XXXXXX")"
|
|
36
|
+
fi
|
|
37
|
+
trap 'rm -f "$TMP_FILE"' EXIT
|
|
38
|
+
|
|
39
|
+
if [ -f "$ENV_FILE" ]; then
|
|
40
|
+
awk '
|
|
41
|
+
!/^OPENCLAW_UID=/ &&
|
|
42
|
+
!/^OPENCLAW_GID=/ &&
|
|
43
|
+
!/^OPENCLAW_WORKSPACE_DIR=/ &&
|
|
44
|
+
!/^OPENCLAW_SSH_DIR=/ &&
|
|
45
|
+
!/^OWNERSHIP_GUARD_MODE=/
|
|
46
|
+
' "$ENV_FILE" > "$TMP_FILE"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
{
|
|
50
|
+
cat "$TMP_FILE"
|
|
51
|
+
if [ -s "$TMP_FILE" ]; then
|
|
52
|
+
printf "\n"
|
|
53
|
+
fi
|
|
54
|
+
printf "OPENCLAW_UID=%s\n" "$OPENCLAW_UID_VALUE"
|
|
55
|
+
printf "OPENCLAW_GID=%s\n" "$OPENCLAW_GID_VALUE"
|
|
56
|
+
printf "OPENCLAW_WORKSPACE_DIR=%s\n" "$(quote_env_value "$OPENCLAW_WORKSPACE_DIR_VALUE")"
|
|
57
|
+
printf "OPENCLAW_SSH_DIR=%s\n" "$(quote_env_value "$OPENCLAW_SSH_DIR_VALUE")"
|
|
58
|
+
printf "OWNERSHIP_GUARD_MODE=%s\n" "$OWNERSHIP_GUARD_MODE_VALUE"
|
|
59
|
+
} > "$ENV_FILE"
|
|
60
|
+
|
|
61
|
+
printf "Wrote %s with OPENCLAW runtime variables.\n" "$ENV_FILE"
|
|
62
|
+
printf "UID:GID=%s:%s\n" "$OPENCLAW_UID_VALUE" "$OPENCLAW_GID_VALUE"
|
|
63
|
+
printf "WORKSPACE=%s\n" "$OPENCLAW_WORKSPACE_DIR_VALUE"
|
|
64
|
+
printf "SSH_DIR=%s\n" "$OPENCLAW_SSH_DIR_VALUE"
|
package/sync.js
CHANGED
|
@@ -241,7 +241,26 @@ function collectOwnershipSample(rootDir, limit = 200) {
|
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
export function getSyncOwnershipDiagnostics(syncDir, { sampleLimit = 200 } = {}) {
|
|
244
|
-
|
|
244
|
+
let runtimeUid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
245
|
+
const runtimeUidOverrideRaw = process.env.RUNTIME_UID_OVERRIDE;
|
|
246
|
+
const runtimeUidOverrideAllowed = process.env.NODE_ENV === "test" || process.env.ALLOW_RUNTIME_UID_OVERRIDE === "1";
|
|
247
|
+
let runtimeUidOverrideApplied = false;
|
|
248
|
+
let runtimeUidOverrideIgnored = false;
|
|
249
|
+
let runtimeUidOverrideInvalid = false;
|
|
250
|
+
|
|
251
|
+
if (runtimeUidOverrideRaw !== undefined) {
|
|
252
|
+
if (!runtimeUidOverrideAllowed) {
|
|
253
|
+
runtimeUidOverrideIgnored = true;
|
|
254
|
+
} else {
|
|
255
|
+
const parsed = Number.parseInt(runtimeUidOverrideRaw, 10);
|
|
256
|
+
if (Number.isInteger(parsed) && parsed >= 0) {
|
|
257
|
+
runtimeUid = parsed;
|
|
258
|
+
runtimeUidOverrideApplied = true;
|
|
259
|
+
} else {
|
|
260
|
+
runtimeUidOverrideInvalid = true;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
245
264
|
let syncDirPathExists;
|
|
246
265
|
let syncDirIsDirectory;
|
|
247
266
|
try {
|
|
@@ -261,6 +280,10 @@ export function getSyncOwnershipDiagnostics(syncDir, { sampleLimit = 200 } = {})
|
|
|
261
280
|
sync_dir_exists: syncDirIsDirectory,
|
|
262
281
|
supported: runtimeUid !== null,
|
|
263
282
|
runtime_uid: runtimeUid,
|
|
283
|
+
runtime_uid_override_requested: runtimeUidOverrideRaw !== undefined,
|
|
284
|
+
runtime_uid_override_applied: runtimeUidOverrideApplied,
|
|
285
|
+
runtime_uid_override_ignored: runtimeUidOverrideIgnored,
|
|
286
|
+
runtime_uid_override_invalid: runtimeUidOverrideInvalid,
|
|
264
287
|
sampled_paths: 0,
|
|
265
288
|
sample_limit: sampleLimit,
|
|
266
289
|
root_owned_paths: 0,
|