Skip to content

feat(cli): add get-sync-token subcommand with auto-capture via volume mount#331

Merged
aliasunder merged 22 commits into
mainfrom
worktree-cli-get-token
Jul 16, 2026
Merged

feat(cli): add get-sync-token subcommand with auto-capture via volume mount#331
aliasunder merged 22 commits into
mainfrom
worktree-cli-get-token

Conversation

@aliasunder

@aliasunder aliasunder commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add npx vault-cortex get-sync-token as a standalone subcommand that auto-captures the Obsidian Sync auth token via a Docker volume mount — no more manual paste
  • Rewire init --mode remote to auto-capture when Docker is available, with a masked paste prompt as fallback
  • Remove the old runGetToken (stdio-inherit, uncapturable) from the DockerRunner interface

How it works

  1. Creates a host temp dir via mkdtempSync
  2. Runs docker run --rm -it --entrypoint ob -v <tempdir>:/home/obsidian/.config <image> login — the interactive login still shows in the terminal, but the token is never printed (it runs ob login directly instead of the image's get-token script, so the credential stays out of terminal scrollback; the manual --entrypoint get-token flow is unchanged)
  3. Reads the auth token from <tempdir>/obsidian-headless/auth_token
  4. Cleans up the temp dir in a finally block (best-effort — a cleanup failure warns instead of discarding a captured token)

On Linux, passes --user uid:gid so the token file is host-user-owned (macOS Docker Desktop translates UIDs automatically).

Error handling is per-operation: each fallible step (temp dir creation, docker run, token file read, cleanup) has its own narrow wrapper with one explicit failure meaning — no catch-all.

Subcommand UX

  • npx vault-cortex get-sync-token — run interactively, print token to stdout
  • npx vault-cortex get-sync-token --dir ./vault-cortex — run interactively, write token directly to .env

The subcommand is named get-sync-token (not get-token) to disambiguate from the MCP auth token that init generates, and to match the docs' "Obsidian Sync token" vocabulary.

Files changed

File Change
cli/src/get-sync-token.ts NewcaptureObsidianToken (shared) + runGetSyncToken (subcommand entry) + per-operation helpers
cli/src/docker.ts Add buildObsidianLoginArgs (pure) + runObsidianLogin; remove old runGetToken
cli/src/scaffold.ts Add patchEnvObsidianToken for .env patching ($-pattern-safe function replacement)
cli/src/init.ts Rewire remote flow: auto-capture → masked paste fallback (prompts.password)
cli/src/program.ts Wire get-sync-token subcommand with --dir flag
cli/src/main.ts Inject deps for runGetSyncToken
cli/src/env.ts Update fill-in comment to npx vault-cortex get-sync-token
cli/README.md Add Get Sync Token section
deploy/remote/README.md Add CLI method to step 3
AGENTS.md Add get-sync-token.ts to structure tree
cli/src/__tests__/get-sync-token.test.ts New — capture + subcommand tests
6 test files Update DockerRunner stubs + remote flow tests

Test plan

  • npm run build:cli — no type errors
  • npm test — 1858 tests pass (57 test files)
  • Pre-commit hooks pass (tsc, eslint, prettier)
  • Manual: npx vault-cortex get-sync-token — interactive login, token printed
  • Manual: npx vault-cortex get-sync-token --dir ./vault-cortex — token written to .env
  • Manual: npx vault-cortex init --mode remote — token auto-captured, no paste
  • Manual: init remote with Docker down — paste prompt fallback works

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added the get-sync-token command to automatically obtain an Obsidian Sync token through Docker.
    • Tokens can be printed to the terminal or written directly to an existing .env file with --dir.
    • Remote initialization now offers automatic token capture, with a paste fallback if needed.
    • Added a matching container-based token setup script.
  • Documentation

    • Updated setup guides, examples, and error messages to use get-sync-token.
    • Clarified token generation and deployment instructions.

Add `npx vault-cortex get-token` as a standalone subcommand and rewire
init's remote flow to auto-capture the Obsidian Sync token via a Docker
volume mount, eliminating the manual paste step.

The command creates a temp directory, mounts it into the container at
/home/obsidian/.config, runs the interactive login, then reads the
auth_token file from the mount — no more copy-paste. On Linux, passes
--user uid:gid so the token file is host-user-owned.

- `get-token` (no flags): prints the captured token to stdout
- `get-token --dir ./vault-cortex`: writes the token directly to .env
- `init --mode remote`: auto-captures when Docker is available, falls
  back to paste prompt when capture fails or the user declines

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@umm-actually

umm-actually Bot commented Jul 14, 2026

Copy link
Copy Markdown

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
Fix stale user message about token printing

The prompts.log message claims get-token will "print a token at the end," but the
new auto-capture design writes the token to a file instead. This stale copy misleads
the user — they might scroll up expecting to see a printed token that never appears.
Update the message to describe the volume-mount capture behavior so the user knows
the token is automatically captured without any terminal output to look for.

cli/src/get-token.ts [27-53]

 export const captureObsidianToken = (deps: {
   docker: DockerRunner
   prompts: Prompts
 }): string | undefined => {
   const { docker, prompts } = deps
   const configMountPath = mkdtempSync(join(tmpdir(), "vault-cortex-get-token-"))
   try {
     prompts.log(
       "Handing the terminal to get-token — it will ask for your Obsidian " +
-        "account login and print a token at the end.",
+        "account login. The token will be captured automatically.",
     )
     const succeeded = docker.runGetTokenWithMount(configMountPath)
     if (!succeeded) {
       prompts.warn(
         "get-token did not complete — you can run it later with:\n" +
           "  npx vault-cortex get-token",
       )
       return undefined
     }
     const tokenPath = join(configMountPath, "obsidian-headless", "auth_token")
     if (!existsSync(tokenPath)) return undefined
     const token = readFileSync(tokenPath, "utf8").trim()
     return token || undefined
   } finally {
     rmSync(configMountPath, { recursive: true, force: true })
   }
 }
Suggestion importance[1-10]: 5

__

Why: The suggestion updates a log message to better describe the new auto-capture behavior, improving clarity for the user. It is a minor enhancement, not a critical fix.

Low

Comment thread cli/src/get-token.ts Outdated

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • In captureObsidianToken, when the token file is missing or empty you currently return undefined silently; consider logging a more specific warning in those paths so users can distinguish between Docker failing vs. get-token succeeding but not producing a token.
  • The patchEnvObsidianToken regex only matches lines starting exactly with OBSIDIAN_AUTH_TOKEN=; if you ever expect leading whitespace, export OBSIDIAN_AUTH_TOKEN=..., or inline comments, you may want to broaden the pattern to avoid surprising false negatives when patching existing .env files.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `captureObsidianToken`, when the token file is missing or empty you currently return `undefined` silently; consider logging a more specific warning in those paths so users can distinguish between Docker failing vs. get-token succeeding but not producing a token.
- The `patchEnvObsidianToken` regex only matches lines starting exactly with `OBSIDIAN_AUTH_TOKEN=`; if you ever expect leading whitespace, `export OBSIDIAN_AUTH_TOKEN=...`, or inline comments, you may want to broaden the pattern to avoid surprising false negatives when patching existing `.env` files.

## Individual Comments

### Comment 1
<location path="cli/src/get-token.ts" line_range="46-49" />
<code_context>
+ *
+ * Returns the token string on success, undefined on any failure.
+ */
+export const captureObsidianToken = (deps: {
+  docker: DockerRunner
+  prompts: Prompts
+}): string | undefined => {
+  const { docker, prompts } = deps
+  const configMountPath = mkdtempSync(join(tmpdir(), "vault-cortex-get-token-"))
+  try {
+    prompts.log(
+      "Handing the terminal to get-token — it will ask for your Obsidian " +
+        "account login and print a token at the end.",
+    )
+    const succeeded = docker.runGetTokenWithMount(configMountPath)
+    if (!succeeded) {
+      prompts.warn(
+        "get-token did not complete — you can run it later with:\n" +
+          "  npx vault-cortex get-token",
+      )
+      return undefined
+    }
+    const tokenPath = join(configMountPath, "obsidian-headless", "auth_token")
+    if (!existsSync(tokenPath)) return undefined
+    const token = readFileSync(tokenPath, "utf8").trim()
</code_context>
<issue_to_address>
**suggestion:** Surface a more specific warning when the token file is missing despite a successful Docker run.

If `runGetTokenWithMount` succeeds but `auth_token` is missing or empty, this function just returns `undefined`, and callers only see the generic "Could not capture the auth token." message. That hides the distinction between Docker errors and issues with the mounted volume/tooling. After `existsSync(tokenPath)` (and possibly after reading the file), consider logging a specific warning explaining that the token file was not found or was empty, with hints like version mismatch or unexpected config path, so users get clearer guidance.

```suggestion
    const tokenPath = join(configMountPath, "obsidian-headless", "auth_token")
    if (!existsSync(tokenPath)) {
      prompts.warn(
        [
          "get-token completed, but no auth_token file was found at:",
          `  ${tokenPath}`,
          "",
          "This usually indicates an issue with the Docker volume mount or the get-token tooling,",
          "such as a version mismatch or a change in the Obsidian config path.",
          "",
          "You can try again with:",
          "  npx vault-cortex get-token",
        ].join("\n"),
      )
      return undefined
    }
    const token = readFileSync(tokenPath, "utf8").trim()
    if (!token) {
      prompts.warn(
        [
          "get-token completed, but the captured auth token file was empty:",
          `  ${tokenPath}`,
          "",
          "This may indicate an issue with the Docker volume mount or the get-token tooling,",
          "such as a version mismatch or an unexpected Obsidian configuration layout.",
          "",
          "You can try again with:",
          "  npx vault-cortex get-token",
        ].join("\n"),
      )
      return undefined
    }
    return token
```
</issue_to_address>

### Comment 2
<location path="cli/src/docker.ts" line_range="47-56" />
<code_context>
+ * On Linux, includes `--user uid:gid` so the token file is host-user-owned
+ * (macOS Docker Desktop translates UIDs automatically).
+ */
+export const buildGetTokenArgs = (params: GetTokenArgParams): string[] => {
+  const { configMountPath, platform = process.platform, uid, gid } = params
+
+  const args = [
+    "run",
+    "--rm",
+    "-it",
+    "--entrypoint",
+    "get-token",
+    "-v",
+    `${configMountPath}:/home/obsidian/.config`,
+  ]
+
+  if (platform === "linux" && uid !== undefined && gid !== undefined) {
+    args.push("--user", `${uid}:${gid}`)
+  }
</code_context>
<issue_to_address>
**nitpick (bug_risk):** Clarify or tighten the `--user` handling on Linux to match the function’s documentation.

The docstring promises that on Linux we always pass `--user uid:gid` so the token file is host-user-owned, but the code only adds `--user` when both `uid` and `gid` are defined. On Linux environments where `getuid/getgid` are unavailable or return `undefined`, this diverges from the documented behavior and the file will be root-owned in the container. Either enforce the documented behavior (e.g., require uid/gid or fail fast on Linux) or adjust the docstring to describe the conditional `--user` usage so callers know when it applies.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread cli/src/get-token.ts Outdated
Comment thread cli/src/docker.ts Outdated
@aliasunder

Copy link
Copy Markdown
Owner Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4a3e7e48-532e-451a-97dd-3532a59e0400

📥 Commits

Reviewing files that changed from the base of the PR and between 47961fb and 36a39b7.

📒 Files selected for processing (26)
  • .env.example
  • AGENTS.md
  • ARCHITECTURE.md
  • DEPLOY.md
  • Dockerfile
  • cli/README.md
  • cli/src/__tests__/docker.test.ts
  • cli/src/__tests__/env.test.ts
  • cli/src/__tests__/get-sync-token.test.ts
  • cli/src/__tests__/init.test.ts
  • cli/src/__tests__/program.test.ts
  • cli/src/__tests__/scaffold.test.ts
  • cli/src/__tests__/upgrade.test.ts
  • cli/src/docker.ts
  • cli/src/env.ts
  • cli/src/get-sync-token.ts
  • cli/src/init.ts
  • cli/src/main.ts
  • cli/src/program.ts
  • cli/src/scaffold.ts
  • deploy/remote/.env.example
  • deploy/remote/README.md
  • rootfs/etc/s6-overlay/scripts/init-check-auth
  • rootfs/etc/s6-overlay/scripts/init-obsidian-login
  • rootfs/usr/local/bin/get-sync-token
  • rootfs/usr/local/bin/get-token
💤 Files with no reviewable changes (1)
  • rootfs/usr/local/bin/get-token
🚧 Files skipped from review as they are similar to previous changes (7)
  • cli/src/main.ts
  • cli/src/scaffold.ts
  • cli/src/tests/env.test.ts
  • cli/src/tests/scaffold.test.ts
  • cli/src/init.ts
  • cli/src/env.ts
  • cli/src/tests/init.test.ts

📝 Walkthrough

Walkthrough

Changes

Obsidian Sync token workflow

Layer / File(s) Summary
Docker login and token capture
cli/src/docker.ts, cli/src/get-sync-token.ts, cli/src/__tests__/docker.test.ts, cli/src/__tests__/get-sync-token.test.ts
Adds mounted Docker login, token-file capture, platform-specific user mapping, failure warnings, and cleanup.
CLI command and .env persistence
cli/src/program.ts, cli/src/main.ts, cli/src/scaffold.ts, cli/src/__tests__/program.test.ts, cli/src/__tests__/scaffold.test.ts
Adds get-sync-token, supports --dir, prints tokens by default, and updates existing .env files.
Remote init auto-capture and fallback
cli/src/init.ts, cli/src/__tests__/init.test.ts
Uses Docker capture during remote initialization and falls back to masked token entry.
Container helper and auth checks
Dockerfile, rootfs/etc/s6-overlay/scripts/*, rootfs/usr/local/bin/get-sync-token
Renames the container helper and updates authentication checks, login diagnostics, and executable setup.
Command guidance and project documentation
cli/README.md, DEPLOY.md, deploy/remote/*, .env.example, ARCHITECTURE.md, AGENTS.md, cli/src/env.ts
Updates token-generation commands, deployment guidance, structure documentation, and decision descriptions.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Clearly summarizes the main CLI addition and volume-mount auto-capture behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-cli-get-token

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
cli/src/__tests__/init.test.ts (1)

617-618: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the parsed environment value exactly.

toContain can pass for a commented, duplicated, or malformed entry. Parse the generated .env and assert that OBSIDIAN_AUTH_TOKEN equals "captured-token".

As per coding guidelines, prefer exact assertions on complete deterministic values over substring checks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli/src/__tests__/init.test.ts` around lines 617 - 618, Update the .env
assertion in the init test to parse the generated environment content and verify
that the parsed OBSIDIAN_AUTH_TOKEN value exactly equals "captured-token".
Replace the substring-based toContain check while preserving the existing
readFileSync and targetDir flow.

Source: Coding guidelines

cli/src/__tests__/get-token.test.ts (1)

120-120: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer exact assertions over substring checks. As per coding guidelines, prefer exact assertions on complete deterministic values over loose matchers like .toContain() for string matching. Using exact assertions ensures that the full message is correct and hasn't subtly changed.

  • cli/src/__tests__/get-token.test.ts#L120-L120: replace .toContain("get-token did not complete") with a .toBe() exact match.
  • cli/src/__tests__/get-token.test.ts#L176-L176: replace .toContain("Handing the terminal to get-token") with a .toBe() exact match.
  • cli/src/__tests__/get-token.test.ts#L203-L203: replace .toContain("Container runtime not running") with a .toBe() exact match.
  • cli/src/__tests__/get-token.test.ts#L215-L215: replace .toContain("Could not capture the auth token") with a .toBe() exact match.
  • cli/src/__tests__/get-token.test.ts#L249-L249: replace .toContain("no OBSIDIAN_AUTH_TOKEN line") with a .toBe() exact match.
  • cli/src/__tests__/get-token.test.ts#L265-L265: replace .toContain("the file is missing") with a .toBe() exact match.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli/src/__tests__/get-token.test.ts` at line 120, Replace the loose warning
substring assertions in cli/src/__tests__/get-token.test.ts at lines 120, 176,
203, 215, 249, and 265 with exact .toBe() assertions, using the complete
deterministic warning emitted by each corresponding get-token scenario.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cli/src/init.ts`:
- Around line 64-70: Update offerGetTokenCapture to catch errors from
captureObsidianToken and return undefined, preserving the existing confirmation
behavior and Promise<string | undefined> contract so initialization can continue
to the paste fallback.
- Around line 367-371: Update the Obsidian Sync token prompt in the init flow to
use prompts.password instead of prompts.text, while preserving the empty default
and existing trim behavior so users can still leave the value blank and fill it
in .env later.

In `@cli/src/scaffold.ts`:
- Line 102: Update the replace call in the scaffolding flow to use a replacer
function that returns the complete OBSIDIAN_AUTH_TOKEN assignment, ensuring
captured token values such as $& or $1 are written literally. Preserve the
existing fullTokenLine match and output format.

---

Nitpick comments:
In `@cli/src/__tests__/get-token.test.ts`:
- Line 120: Replace the loose warning substring assertions in
cli/src/__tests__/get-token.test.ts at lines 120, 176, 203, 215, 249, and 265
with exact .toBe() assertions, using the complete deterministic warning emitted
by each corresponding get-token scenario.

In `@cli/src/__tests__/init.test.ts`:
- Around line 617-618: Update the .env assertion in the init test to parse the
generated environment content and verify that the parsed OBSIDIAN_AUTH_TOKEN
value exactly equals "captured-token". Replace the substring-based toContain
check while preserving the existing readFileSync and targetDir flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5ee381ad-6c05-482a-a4cc-a2d16466aeb6

📥 Commits

Reviewing files that changed from the base of the PR and between 52a9c95 and 47961fb.

📒 Files selected for processing (17)
  • AGENTS.md
  • cli/README.md
  • cli/src/__tests__/docker.test.ts
  • cli/src/__tests__/env.test.ts
  • cli/src/__tests__/get-token.test.ts
  • cli/src/__tests__/init.test.ts
  • cli/src/__tests__/program.test.ts
  • cli/src/__tests__/scaffold.test.ts
  • cli/src/__tests__/upgrade.test.ts
  • cli/src/docker.ts
  • cli/src/env.ts
  • cli/src/get-token.ts
  • cli/src/init.ts
  • cli/src/main.ts
  • cli/src/program.ts
  • cli/src/scaffold.ts
  • deploy/remote/README.md

Comment thread cli/src/init.ts Outdated
Comment thread cli/src/init.ts Outdated
Comment thread cli/src/scaffold.ts Outdated
aliasunder and others added 6 commits July 16, 2026 10:23
…id $ pattern injection

String.prototype.replace interprets $-patterns ($&, $', $$, etc.) in
string replacement arguments. If an Obsidian Sync token contained a $
character, the .env write would silently corrupt it. A function
replacement avoids special-pattern interpretation entirely.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117h7grQjdkAMGD38vCfZGk
…n test

Tighten three substring assertions (toContain) to exact matches (toBe) on
hardcoded error/warning messages in get-token tests. Replace partial
not.toContain/toContain with full toEqual on buildGetTokenArgs Linux-no-uid
test. Add regression test proving patchEnvObsidianToken handles $-pattern
tokens (e.g. $&, $$) literally — mutation-verified against the Phase 1 fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117h7grQjdkAMGD38vCfZGk
…g init

The init flow prompts for confirmation before running get-token
(offerGetTokenCapture calls prompts.confirm). "Runs automatically"
implies no user interaction; "is offered automatically" matches
the actual behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117h7grQjdkAMGD38vCfZGk
… missing token file, mask token paste

- captureObsidianToken: wrap in try/catch so any throw degrades to the
  paste fallback instead of aborting init (CodeRabbit)
- warn specifically when get-token completes but the token file is
  missing or empty, instead of returning undefined silently (Sourcery)
- note auto-capture in the handoff message so users know they don't
  need to copy the printed token (qodo)
- clarify buildGetTokenArgs docstring: --user is set when uid/gid are
  provided, which is always the case on POSIX hosts (Sourcery)
- init: use the masked password prompt for the sync-token paste so the
  credential doesn't echo into scrollback (CodeRabbit)
- tests: exact assertions on the remaining loose toContain checks;
  regression tests for the new warn + throw paths

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117h7grQjdkAMGD38vCfZGk
@aliasunder

Copy link
Copy Markdown
Owner Author

Dispositions for review-body findings without inline threads:

Sourcery overall comment 1 (silent undefined in captureObsidianToken): fixed in 16f6cb2, see the inline thread reply on cli/src/get-token.ts.

Sourcery overall comment 2 (broaden the patchEnvObsidianToken regex for export/leading whitespace): intentional as-is. The .env this patches is generated by init with OBSIDIAN_AUTH_TOKEN= at line start, and Docker's --env-file parser only accepts plain VAR=VALUE lines, so broadened forms would match files the container runtime can't consume anyway. The failure path already directs the user to run init first.

CodeRabbit nitpick (init.test.ts exact env assertion): fixed in 16f6cb2. The test now asserts the exact OBSIDIAN_AUTH_TOKEN=captured-token line via a line-array match.

CodeRabbit nitpick (get-token.test.ts loose toContain assertions): fixed across 3d54288 and 16f6cb2. All warning/error message assertions in that file are now exact toBe matches.


🔍 ship-check · pr-monitor · claude-fable-5

aliasunder and others added 6 commits July 16, 2026 12:48
…ured token

- Replace the nested try/finally + outer try/catch with a single
  try/catch/finally; cleanup is now best-effort, so an rmSync failure
  (e.g. root-owned container files) warns instead of discarding a
  successfully captured token
- Run 'ob login' directly instead of the image's get-token script — the
  script only exists to locate and print the token, and the volume mount
  makes both unnecessary; the credential no longer echoes into terminal
  scrollback, and this works with already-published images (the manual
  --entrypoint get-token flow is unchanged)
- Update the handoff message and test expectations accordingly

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117h7grQjdkAMGD38vCfZGk
…error handling

Each fallible operation gets its own narrow wrapper with one explicit
failure meaning: makeTempMountDir (warn + undefined), runLoginContainer
(throw = failed run), readCapturedTokenFile (missing/empty/unreadable =
no token), removeTempMountDir (best-effort, warn only). The main flow
keeps a single bare try/finally to scope the temp dir — no catch left
that can swallow unrelated errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117h7grQjdkAMGD38vCfZGk
… readCapturedTokenFile

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117h7grQjdkAMGD38vCfZGk
The old name was inherited from the container's get-token script, which
the capture flow no longer uses, and it was ambiguous — the CLI handles
two tokens (MCP auth token from init, Obsidian Sync token from this
command). The new name matches the docs' "Obsidian Sync token"
vocabulary. Pre-release, so no compatibility concern.

Also renames the internals that still described the old flow:
runGetTokenWithMount -> runObsidianLogin, buildGetTokenArgs ->
buildObsidianLoginArgs, and the get-token.ts module -> get-sync-token.ts.
The container script keeps its name — the manual --entrypoint get-token
flow is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117h7grQjdkAMGD38vCfZGk
@aliasunder aliasunder changed the title feat(cli): add get-token subcommand with auto-capture via volume mount feat(cli): add get-sync-token subcommand with auto-capture via volume mount Jul 16, 2026
aliasunder and others added 6 commits July 16, 2026 13:41
…ync-token

The get-token script was the largest artifact inherited from the absorbed
obsidian-headless-sync-docker fork (~98% upstream expression, and the
upstream chain carries no license). Rewritten from scratch in our own
words and renamed to match the CLI's get-sync-token subcommand:

- canonical XDG token path checked first, find as the fallback
- single -s guard covers missing path, missing file, and empty file
- prints one copy-paste OBSIDIAN_AUTH_TOKEN= line instead of echo banners

All references updated: Dockerfile (comments + chmod), init-check-auth
hint, DEPLOY.md, ARCHITECTURE.md, AGENTS.md tree, root and deploy
.env.example, deploy/remote/README.md, and the docker.ts docstring.
Note: images published before this release only contain the old
get-token entrypoint name.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117h7grQjdkAMGD38vCfZGk
…n words

init-check-auth and init-obsidian-login were the last byte-identical
holdovers from the absorbed obsidian-headless-sync-docker fork (whose
upstream chain carries no license). Rewritten with the same semantics:

- init-check-auth: positive-first gate with early exit, grouped stderr
  block, and a fuller hint (generate with get-sync-token, add to .env)
- init-obsidian-login: adds a stale-token hint on rejection and set -u

With these plus the get-sync-token rewrite, no inherited upstream
expression remains in rootfs/.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117h7grQjdkAMGD38vCfZGk
DEPLOY.md readers run SST, so Node.js is already a prerequisite — the
CLI is the natural primary path, with the docker run one-liner kept as
the direct-image alternative. Matches deploy/remote/README.md's
CLI-first pattern.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117h7grQjdkAMGD38vCfZGk
'Captured automatically' described the implementation, not the user
experience. The handoff message, failure warnings, and both READMEs now
say what the user sees: sign in, and vault-cortex picks up the token
itself — nothing to find or copy. Internal docstrings keep the capture
vocabulary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117h7grQjdkAMGD38vCfZGk
'Captured automatically' alone didn't tell the user what actually
happens to the token. Each flow now supplies its destination to the
shared handoff message: init says it is stored in your .env, the bare
subcommand says it is printed at the end, and --dir names the .env file
it writes to. Tests assert each flow's exact message.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117h7grQjdkAMGD38vCfZGk
…alistic constant

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117h7grQjdkAMGD38vCfZGk
@aliasunder

Copy link
Copy Markdown
Owner Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@aliasunder
aliasunder merged commit 3b6fca1 into main Jul 16, 2026
19 checks passed
@aliasunder
aliasunder deleted the worktree-cli-get-token branch July 16, 2026 20:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant