Move private runtime state outside checkouts and isolate worktrees
This commit is contained in:
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user