Command line interface for accessing the Open Web Index shards and slices.

fix(remote): report an unusable credential as auth, not as an empty scope master

F3. With a valid specifier and no usable LEXIS credential, `remote pull` said "0 datasets found" -- and, after F2 made matching nothing a failure, said "No datasets matched specifier 'all'" with ErrorType.DATA. That is worse than the original silence: it is confidently wrong about where the problem is, and sends an operator to check the scope when the fix is to log in. The credential failure was swallowed twice. LexisRepository.list caught every exception, logged it and returned []; AggregatedRepository.list then caught that per repository and carried on. Nothing above could tell "empty" from "rejected". - New owilix/core/exceptions.py: OwilixError, AuthenticationError, and looks_like_authentication_failure(). - LexisRepository.list re-raises as AuthenticationError when the failure is auth-shaped. Everything else still degrades to [], unchanged. - AggregatedRepository.list re-raises an auth failure only when it left nothing to return; a repository that answered still yields its datasets, so one broken backend does not sink a multi-repository listing. - remote_pull returns ErrorType.AUTH / ExitCode.AUTH_ERROR (3) with a message saying how to authenticate in both interactive and server contexts. Detection matches on type *and* text on purpose. py4lexis has a typed Py4LexisAuthException, but the failure the clusters actually produced arrives as Py4LexisAPIException -- the DDI API answers a missing credential with an errorString py4lexis does not recognise -- so type alone under-detects. The phrase list is kept specific; a bare "token" would misreport network faults as login problems, and there is a test for exactly that. Over HTTP this needs no API shape change: success=False already maps to a failed job, and error_type "auth" rides along in the existing result dict. No 'owilix remote login' command exists to point at, so the message describes what actually works: complete the browser login interactively, or set PY4LEXIS_TOKEN / POST /auth/device/start for a server. Not fixed here, recorded under F3: a caller still cannot tell that a listing was *partial* when one repository failed and another answered. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017phbSd6D8u4iEQsCAPEw6s


+404 -12
5 changed files