Move private runtime state outside checkouts and isolate worktrees

This commit is contained in:
HumbleTurtle
2026-09-21 13:19:27 -03:00
parent 2a78262754
commit c427e2432a
12 changed files with 506 additions and 88 deletions
+19 -13
View File
@@ -66,7 +66,7 @@ RUNTIME_SCRIPT_NAMES = (
"napseer_mcp_supervisor.py",
)
MAX_SERVICE_LOG_BYTES = int(os.environ.get("NAPSEER_MAX_SERVICE_LOG_BYTES", str(5 * 1024 * 1024)))
CLI_RELEASE_VERSION = "0.3.0"
CLI_RELEASE_VERSION = "0.4.0"
CLI_DISTRIBUTION_CONTRACT_VERSION = "2026-09-10"
# Older installers have a fixed asset list and would omit the credentials module.
# Reject them before activation; the published bootstrap performs the upgrade.
@@ -630,32 +630,34 @@ def run_wrapper(command_name, args):
def preferred_state_dir():
return pathlib.Path.cwd() / ".napseer"
from napseer_credentials import WorkspacePaths
return WorkspacePaths().state
def cwd_state_dir():
preferred = preferred_state_dir()
preferred.mkdir(exist_ok=True)
preferred.mkdir(parents=True, exist_ok=True)
chmod_best_effort(preferred, 0o700)
return preferred
def state_dir_status():
preferred = preferred_state_dir()
from napseer_credentials import CredentialStore
try: auth_path, credential_source = CredentialStore(preferred).selected()
except RuntimeError: auth_path, credential_source = CredentialStore(preferred).user, "invalid_selection"
from napseer_credentials import CredentialStore, WorkspacePaths
try: auth_path, credential_source = CredentialStore(WorkspacePaths().legacy).selected()
except RuntimeError: auth_path, credential_source = CredentialStore(WorkspacePaths().legacy).user, "invalid_selection"
return {
"credential_source": credential_source,
"migration_required": bool(WorkspacePaths().migration_sources()),
"active_state_dir": str(preferred),
"active_auth_path": str(auth_path),
"state_dir": str(preferred),
"auth_path": str(auth_path),
"state_dir_exists": preferred.exists(),
"state_dir_message": (
".napseer directory is active."
"Private Napseer state is stored in user data."
if preferred.exists()
else ".napseer directory is not initialized."
else "Private Napseer state is not initialized."
),
}
@@ -862,6 +864,8 @@ def validate_cli_options(args, *, flags=(), value_options=(), command_label):
def start_service(kind, args):
from napseer_credentials import WorkspacePaths
WorkspacePaths().require_migrated()
wrapper = runtime_asset_path("napseer_mcp_server.py")
require_runtime_assets()
existing = read_service_state(kind)
@@ -1094,7 +1098,7 @@ def print_command_help(command):
" repair Recover failed automatic credential renewal.\n"
" logout [--project] Revoke the selected login.\n"
" login --project Store a separate project login in user data.\n"
" migrate Move legacy project credentials into user data.\n"
" migrate Move legacy private state into user data after stopping older clients/gateways.\n"
" use-user Explicitly select the general login for this folder.\n"
" api-key --env NAME Configure project access from a named environment variable.\n"
" keys list|create|revoke Manage named, expiring delegated access."
@@ -1366,17 +1370,19 @@ def doctor_status():
auth = {}
auth_error = None
try:
from napseer_credentials import CredentialStore, read_json
store=CredentialStore(preferred_state_dir())
from napseer_credentials import CredentialStore, WorkspacePaths, read_json
store=CredentialStore(WorkspacePaths().legacy)
auth=store.read()
locator=read_json(preferred_state_dir()/"project.json")
locator=read_json(WorkspacePaths().locator)
if not auth.get("project_id"): auth["project_id"]=locator.get("project_id")
except RuntimeError:
auth_error="credential state is unreadable; run nap auth login or inspect the selected override"
missing = runtime_assets_missing()
issues = []
setup = []
if auth_error:
if state.get("migration_required"):
issues.append({"code": "state_migration_required", "message": "Stop older MCP clients and local gateways, then migrate private state into user data.", "next": "nap auth migrate"})
elif auth_error:
issues.append({"code": "auth_file_invalid", "message": auth_error, "next": "nap auth repair"})
elif not auth_path.exists():
setup.append({