delphi-code 0.2.0__py3-none-any.whl → 0.2.2__py3-none-any.whl
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.
- delphi_code/hosts.py +101 -35
- delphi_code/picker.py +16 -1
- delphi_code/sources.py +2 -1
- {delphi_code-0.2.0.dist-info → delphi_code-0.2.2.dist-info}/METADATA +11 -9
- {delphi_code-0.2.0.dist-info → delphi_code-0.2.2.dist-info}/RECORD +10 -10
- {delphi_code-0.2.0.dist-info → delphi_code-0.2.2.dist-info}/WHEEL +0 -0
- {delphi_code-0.2.0.dist-info → delphi_code-0.2.2.dist-info}/entry_points.txt +0 -0
- {delphi_code-0.2.0.dist-info → delphi_code-0.2.2.dist-info}/licenses/LICENSE +0 -0
- {delphi_code-0.2.0.dist-info → delphi_code-0.2.2.dist-info}/licenses/THIRD_PARTY_NOTICES.md +0 -0
- {delphi_code-0.2.0.dist-info → delphi_code-0.2.2.dist-info}/top_level.txt +0 -0
delphi_code/hosts.py
CHANGED
|
@@ -8,7 +8,7 @@ import subprocess
|
|
|
8
8
|
import sys
|
|
9
9
|
from typing import Any, NamedTuple
|
|
10
10
|
from urllib.error import HTTPError, URLError
|
|
11
|
-
from urllib.parse import quote, urlencode
|
|
11
|
+
from urllib.parse import quote, urlencode, urlsplit
|
|
12
12
|
from urllib.request import Request, urlopen
|
|
13
13
|
|
|
14
14
|
if __package__:
|
|
@@ -24,6 +24,11 @@ class Owner(NamedTuple):
|
|
|
24
24
|
name: str
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
class ApiCredentials(NamedTuple):
|
|
28
|
+
authorization: str
|
|
29
|
+
source: str
|
|
30
|
+
|
|
31
|
+
|
|
27
32
|
class Repository(NamedTuple):
|
|
28
33
|
host: str
|
|
29
34
|
owner: str
|
|
@@ -43,6 +48,7 @@ class RepositoryHost:
|
|
|
43
48
|
default_api_base: str
|
|
44
49
|
credentials_hint: str
|
|
45
50
|
rejected_credentials_hint = ""
|
|
51
|
+
missing_permission_hint = ""
|
|
46
52
|
|
|
47
53
|
def __init__(self, api_authorization=None):
|
|
48
54
|
self._given_api_authorization = api_authorization
|
|
@@ -57,11 +63,14 @@ class RepositoryHost:
|
|
|
57
63
|
def permalink_template(self, owner, repository, commit):
|
|
58
64
|
raise NotImplementedError
|
|
59
65
|
|
|
66
|
+
def suggested_owner(self):
|
|
67
|
+
return None
|
|
68
|
+
|
|
60
69
|
def clone_credentials(self):
|
|
61
70
|
raise NotImplementedError
|
|
62
71
|
|
|
63
72
|
def has_api_credentials(self):
|
|
64
|
-
return
|
|
73
|
+
return self._available_api_credentials is not None
|
|
65
74
|
|
|
66
75
|
def owners(self):
|
|
67
76
|
return [Owner(**item) for item in self._list_in_child_process("owners")]
|
|
@@ -69,7 +78,7 @@ class RepositoryHost:
|
|
|
69
78
|
def repositories(self, owner):
|
|
70
79
|
return [Repository(**item) for item in self._list_in_child_process("repositories", owner)]
|
|
71
80
|
|
|
72
|
-
def
|
|
81
|
+
def _find_api_credentials(self):
|
|
73
82
|
raise NotImplementedError
|
|
74
83
|
|
|
75
84
|
def _list_owners(self):
|
|
@@ -93,19 +102,21 @@ class RepositoryHost:
|
|
|
93
102
|
return os.environ.get(f"{self._variable_prefix}_API_BASE", self.default_api_base).rstrip("/")
|
|
94
103
|
|
|
95
104
|
@cached_property
|
|
96
|
-
def
|
|
97
|
-
|
|
105
|
+
def _available_api_credentials(self):
|
|
106
|
+
if self._given_api_authorization:
|
|
107
|
+
return ApiCredentials(self._given_api_authorization, "given credentials")
|
|
108
|
+
return self._find_api_credentials()
|
|
98
109
|
|
|
99
110
|
@property
|
|
100
|
-
def
|
|
101
|
-
|
|
102
|
-
if
|
|
111
|
+
def _api_credentials(self):
|
|
112
|
+
credentials = self._available_api_credentials
|
|
113
|
+
if credentials is None:
|
|
103
114
|
raise Failure(
|
|
104
115
|
"remote_auth_missing",
|
|
105
116
|
f"Listing {self.name} repositories needs credentials: {self.credentials_hint}",
|
|
106
117
|
ExitCode.RUNTIME_ASSETS,
|
|
107
118
|
)
|
|
108
|
-
return
|
|
119
|
+
return credentials
|
|
109
120
|
|
|
110
121
|
def _repository(self, owner, slug, description, private):
|
|
111
122
|
return Repository(self.domain, owner, slug, description or "", bool(private))
|
|
@@ -129,7 +140,8 @@ class RepositoryHost:
|
|
|
129
140
|
return json.load(response), (response.headers.get("Link") if response.headers else None) or ""
|
|
130
141
|
|
|
131
142
|
def _list_in_child_process(self, listing, *arguments) -> list[dict]:
|
|
132
|
-
|
|
143
|
+
credentials = self._api_credentials
|
|
144
|
+
environment = dict(os.environ, DELPHI_CODE_API_AUTHORIZATION=credentials.authorization)
|
|
133
145
|
try:
|
|
134
146
|
result = subprocess.run(
|
|
135
147
|
[sys.executable, __file__, self.domain, listing, *arguments],
|
|
@@ -152,10 +164,18 @@ class RepositoryHost:
|
|
|
152
164
|
if result.returncode == 0:
|
|
153
165
|
return payload
|
|
154
166
|
error = payload.get("error", {})
|
|
155
|
-
if error.get("status")
|
|
167
|
+
if error.get("status") == 401:
|
|
156
168
|
raise Failure(
|
|
157
169
|
"remote_auth_failed",
|
|
158
|
-
f"{self.name}
|
|
170
|
+
f"{self.name} rejected the {credentials.source}: {error['message']}. "
|
|
171
|
+
f"{self.rejected_credentials_hint or self.credentials_hint}",
|
|
172
|
+
ExitCode.OPERATION,
|
|
173
|
+
)
|
|
174
|
+
if error.get("status") == 403:
|
|
175
|
+
raise Failure(
|
|
176
|
+
"remote_permission_denied",
|
|
177
|
+
f"{self.name} accepted the {credentials.source} but refused {error.get('path')}: {error['message']}. "
|
|
178
|
+
f"{self.missing_permission_hint}",
|
|
159
179
|
ExitCode.OPERATION,
|
|
160
180
|
)
|
|
161
181
|
raise Failure(
|
|
@@ -170,39 +190,43 @@ class Bitbucket(RepositoryHost):
|
|
|
170
190
|
default_api_base = "https://api.bitbucket.org/2.0"
|
|
171
191
|
credentials_hint = "set BITBUCKET_USERNAME and BITBUCKET_APP_PASSWORD, or store git credentials for bitbucket.org"
|
|
172
192
|
rejected_credentials_hint = "With an API token, set BITBUCKET_EMAIL to your Atlassian account email"
|
|
193
|
+
missing_permission_hint = (
|
|
194
|
+
"Listing needs an API token with the scopes read:user:bitbucket, read:workspace:bitbucket and "
|
|
195
|
+
"read:repository:bitbucket, or an app password with Account, Workspace membership and Repositories read access"
|
|
196
|
+
)
|
|
173
197
|
_repository_fields = "next,values.slug,values.description,values.is_private"
|
|
174
198
|
|
|
175
199
|
def permalink_template(self, owner, repository, commit):
|
|
176
200
|
return f"{self.web_url(owner, repository)}/src/{commit}/{{path}}#lines-{{start}}:{{end}}"
|
|
177
201
|
|
|
202
|
+
def suggested_owner(self):
|
|
203
|
+
return os.environ.get("BITBUCKET_WORKSPACE")
|
|
204
|
+
|
|
178
205
|
def clone_credentials(self):
|
|
179
206
|
username, password = os.environ.get("BITBUCKET_USERNAME"), os.environ.get("BITBUCKET_APP_PASSWORD")
|
|
180
207
|
return (username, password) if username and password else None
|
|
181
208
|
|
|
182
|
-
def
|
|
183
|
-
|
|
184
|
-
password = os.environ.get("BITBUCKET_APP_PASSWORD")
|
|
185
|
-
|
|
186
|
-
|
|
209
|
+
def _find_api_credentials(self):
|
|
210
|
+
username_variable = "BITBUCKET_EMAIL" if os.environ.get("BITBUCKET_EMAIL") else "BITBUCKET_USERNAME"
|
|
211
|
+
username, password = os.environ.get(username_variable), os.environ.get("BITBUCKET_APP_PASSWORD")
|
|
212
|
+
if username and password:
|
|
213
|
+
source = f"{username_variable} and BITBUCKET_APP_PASSWORD from the environment"
|
|
214
|
+
else:
|
|
215
|
+
stored = _git_stored_credentials(self.domain)
|
|
216
|
+
if stored is None:
|
|
217
|
+
return None
|
|
218
|
+
(username, password), source = stored, f"git credentials stored for {self.domain}"
|
|
219
|
+
return ApiCredentials("Basic " + base64.b64encode(f"{username}:{password}".encode()).decode(), source)
|
|
187
220
|
|
|
188
221
|
def _workspaces_url(self):
|
|
189
222
|
return f"{self._api_base}/user/workspaces?pagelen={PAGE_LENGTH}"
|
|
190
223
|
|
|
191
|
-
def _legacy_workspaces_url(self):
|
|
192
|
-
return f"{self._api_base}/user/permissions/workspaces?pagelen={PAGE_LENGTH}"
|
|
193
|
-
|
|
194
224
|
def _repositories_url(self, workspace):
|
|
195
225
|
query = urlencode({"role": "member", "pagelen": PAGE_LENGTH, "sort": "slug", "fields": self._repository_fields})
|
|
196
226
|
return f"{self._api_base}/repositories/{quote(workspace)}?{query}"
|
|
197
227
|
|
|
198
228
|
def _list_owners(self):
|
|
199
|
-
|
|
200
|
-
memberships = self._fetch_pages(self._workspaces_url())
|
|
201
|
-
except HTTPError as exc:
|
|
202
|
-
exc.close()
|
|
203
|
-
if exc.code not in (404, 410):
|
|
204
|
-
raise
|
|
205
|
-
memberships = self._fetch_pages(self._legacy_workspaces_url())
|
|
229
|
+
memberships = self._fetch_pages(self._workspaces_url())
|
|
206
230
|
names_by_slug = {}
|
|
207
231
|
for membership in memberships:
|
|
208
232
|
workspace = membership.get("workspace", membership)
|
|
@@ -228,6 +252,9 @@ class GitHub(RepositoryHost):
|
|
|
228
252
|
owner_noun = "account"
|
|
229
253
|
default_api_base = "https://api.github.com"
|
|
230
254
|
credentials_hint = "set GITHUB_TOKEN, sign in with gh auth login, or store git credentials for github.com"
|
|
255
|
+
missing_permission_hint = (
|
|
256
|
+
"Listing needs a classic token with the repo scope, or a fine-grained token with Metadata read access"
|
|
257
|
+
)
|
|
231
258
|
_next_link = re.compile(r'<([^>]+)>;\s*rel="next"')
|
|
232
259
|
|
|
233
260
|
def permalink_template(self, owner, repository, commit):
|
|
@@ -237,12 +264,16 @@ class GitHub(RepositoryHost):
|
|
|
237
264
|
token = _github_environment_token()
|
|
238
265
|
return ("x-access-token", token) if token else None
|
|
239
266
|
|
|
240
|
-
def
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
token
|
|
245
|
-
|
|
267
|
+
def _find_api_credentials(self):
|
|
268
|
+
for source, find_token in (
|
|
269
|
+
("GITHUB_TOKEN or GH_TOKEN from the environment", _github_environment_token),
|
|
270
|
+
("token from gh auth token", lambda: _gh_token(self.domain)),
|
|
271
|
+
(f"token stored in git credentials for {self.domain}", lambda: _git_stored_token(self.domain)),
|
|
272
|
+
):
|
|
273
|
+
token = find_token()
|
|
274
|
+
if token:
|
|
275
|
+
return ApiCredentials(f"Bearer {token}", source)
|
|
276
|
+
return None
|
|
246
277
|
|
|
247
278
|
def owners(self):
|
|
248
279
|
owners = sorted({repository.owner for repository in self._accessible_repositories}, key=str.lower)
|
|
@@ -304,6 +335,11 @@ def _gh_token(domain):
|
|
|
304
335
|
return result.stdout.strip() if result.returncode == 0 and result.stdout.strip() else None
|
|
305
336
|
|
|
306
337
|
|
|
338
|
+
def _git_stored_token(domain):
|
|
339
|
+
stored = _git_stored_credentials(domain)
|
|
340
|
+
return stored[1] if stored else None
|
|
341
|
+
|
|
342
|
+
|
|
307
343
|
def _git_stored_credentials(domain):
|
|
308
344
|
environment = {name: value for name, value in os.environ.items() if name not in {"GIT_ASKPASS", "SSH_ASKPASS"}}
|
|
309
345
|
environment["GIT_TERMINAL_PROMPT"] = "0"
|
|
@@ -324,14 +360,44 @@ def _git_stored_credentials(domain):
|
|
|
324
360
|
return fields["username"], fields["password"]
|
|
325
361
|
|
|
326
362
|
|
|
363
|
+
def _error_body(exc):
|
|
364
|
+
try:
|
|
365
|
+
return exc.read()
|
|
366
|
+
except (OSError, ValueError):
|
|
367
|
+
return b""
|
|
368
|
+
finally:
|
|
369
|
+
exc.close()
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def _service_error_message(body):
|
|
373
|
+
try:
|
|
374
|
+
payload = json.loads(body)
|
|
375
|
+
except ValueError:
|
|
376
|
+
return None
|
|
377
|
+
if not isinstance(payload, dict):
|
|
378
|
+
return None
|
|
379
|
+
bitbucket_error = payload.get("error")
|
|
380
|
+
if not isinstance(bitbucket_error, dict):
|
|
381
|
+
return payload.get("message")
|
|
382
|
+
message, detail = bitbucket_error.get("message"), bitbucket_error.get("detail")
|
|
383
|
+
if isinstance(detail, dict):
|
|
384
|
+
detail = "; ".join(f"{name}: {_listed(value)}" for name, value in detail.items())
|
|
385
|
+
return f"{message} ({detail})" if message and detail else message or detail
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def _listed(value):
|
|
389
|
+
return ", ".join(map(str, value)) if isinstance(value, list) else str(value)
|
|
390
|
+
|
|
391
|
+
|
|
327
392
|
def main(arguments):
|
|
328
393
|
domain, listing, *listing_arguments = arguments
|
|
329
394
|
host = HOSTS_BY_DOMAIN[domain](os.environ["DELPHI_CODE_API_AUTHORIZATION"])
|
|
330
395
|
try:
|
|
331
396
|
items = host._list_owners() if listing == "owners" else host._list_repositories(*listing_arguments)
|
|
332
397
|
except HTTPError as exc:
|
|
333
|
-
exc.
|
|
334
|
-
|
|
398
|
+
reason = _service_error_message(_error_body(exc)) or exc.reason
|
|
399
|
+
path = urlsplit(exc.url).path if exc.url else None
|
|
400
|
+
print(json.dumps({"error": {"status": exc.code, "path": path, "message": f"HTTP {exc.code} {reason}"}}))
|
|
335
401
|
return 1
|
|
336
402
|
except (URLError, OSError, ValueError, KeyError, TypeError) as exc:
|
|
337
403
|
print(json.dumps({"error": {"status": None, "message": str(exc)}}))
|
delphi_code/picker.py
CHANGED
|
@@ -78,7 +78,12 @@ class RepositoryPicker:
|
|
|
78
78
|
def _choose_owner(self, host):
|
|
79
79
|
import questionary
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
try:
|
|
82
|
+
owners = host.owners()
|
|
83
|
+
except Failure as failure:
|
|
84
|
+
if failure.code != "remote_permission_denied":
|
|
85
|
+
raise
|
|
86
|
+
return self._type_owner(host)
|
|
82
87
|
if not owners:
|
|
83
88
|
raise Failure("remote_empty", f"Your {host.name} account has no repositories", ExitCode.USAGE)
|
|
84
89
|
if len(owners) == 1:
|
|
@@ -91,6 +96,16 @@ class RepositoryPicker:
|
|
|
91
96
|
**self._terminal,
|
|
92
97
|
).ask()
|
|
93
98
|
|
|
99
|
+
def _type_owner(self, host):
|
|
100
|
+
import questionary
|
|
101
|
+
|
|
102
|
+
answer = questionary.text(
|
|
103
|
+
f"These credentials cannot list {host.name} {host.owner_noun}s. {host.name} {host.owner_noun} to index:",
|
|
104
|
+
default=host.suggested_owner() or "",
|
|
105
|
+
**self._terminal,
|
|
106
|
+
).ask()
|
|
107
|
+
return (answer or "").strip() or None
|
|
108
|
+
|
|
94
109
|
def _choose_repositories(self, owner, repositories, tracked_keys):
|
|
95
110
|
import questionary
|
|
96
111
|
|
delphi_code/sources.py
CHANGED
|
@@ -110,7 +110,8 @@ class GitRemoteSource:
|
|
|
110
110
|
return Revision(branch, commit)
|
|
111
111
|
|
|
112
112
|
def _named_revision(self, ref):
|
|
113
|
-
|
|
113
|
+
advertised = self._remote_refs(f"refs/heads/{ref}", f"refs/tags/{ref}", f"refs/tags/{ref}^{{}}")
|
|
114
|
+
commits = dict((name, value) for value, name in advertised)
|
|
114
115
|
commit = (
|
|
115
116
|
commits.get(f"refs/heads/{ref}") or commits.get(f"refs/tags/{ref}^{{}}") or commits.get(f"refs/tags/{ref}")
|
|
116
117
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: delphi-code
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Offline local code search for agents
|
|
5
5
|
Author: Mark Dorofeev
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -13,6 +13,8 @@ Classifier: Intended Audience :: Developers
|
|
|
13
13
|
Classifier: Operating System :: MacOS
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
18
|
Classifier: Topic :: Software Development
|
|
17
19
|
Classifier: Topic :: Text Processing :: Indexing
|
|
18
20
|
Requires-Python: >=3.12
|
|
@@ -35,7 +37,7 @@ Offline semantic code search for agents. Delphi Code is a small Python CLI that
|
|
|
35
37
|
## Quick start
|
|
36
38
|
|
|
37
39
|
```sh
|
|
38
|
-
uv tool install --
|
|
40
|
+
uv tool install --managed-python delphi-code
|
|
39
41
|
delphi-code setup
|
|
40
42
|
delphi-code index -p /absolute/path/to/project
|
|
41
43
|
delphi-code search 'where are user passwords checked?'
|
|
@@ -46,7 +48,7 @@ delphi-code search 'where are user passwords checked?'
|
|
|
46
48
|
## Requirements
|
|
47
49
|
|
|
48
50
|
- macOS 14 or later on arm64 (the tested target). Linux has not yet been verified; Windows is not supported by the current file locking implementation.
|
|
49
|
-
- CPython 3.12 with SQLite loadable extensions. Some python.org macOS builds disable that feature; `--managed-python` makes uv use its own Python build, which supports it. `doctor` reports this explicitly.
|
|
51
|
+
- CPython 3.12 or later (tested on 3.12, 3.13 and 3.14) with SQLite loadable extensions. Some python.org macOS builds disable that feature; `--managed-python` makes uv use its own Python build, which supports it. `doctor` reports this explicitly.
|
|
50
52
|
- CPU inference; no GPU is needed.
|
|
51
53
|
|
|
52
54
|
## Installation
|
|
@@ -54,21 +56,21 @@ delphi-code search 'where are user passwords checked?'
|
|
|
54
56
|
From PyPI, as in the quick start:
|
|
55
57
|
|
|
56
58
|
```sh
|
|
57
|
-
uv tool install --
|
|
59
|
+
uv tool install --managed-python delphi-code
|
|
58
60
|
delphi-code setup
|
|
59
61
|
```
|
|
60
62
|
|
|
61
63
|
From a checkout, with uv:
|
|
62
64
|
|
|
63
65
|
```sh
|
|
64
|
-
uv tool install --
|
|
66
|
+
uv tool install --managed-python /absolute/path/to/delphi-code
|
|
65
67
|
delphi-code setup
|
|
66
68
|
```
|
|
67
69
|
|
|
68
|
-
From a checkout, with the pinned dependency versions and a Python 3.12 build that supports SQLite extensions:
|
|
70
|
+
From a checkout, with the pinned dependency versions and a Python 3.12+ build that supports SQLite extensions:
|
|
69
71
|
|
|
70
72
|
```sh
|
|
71
|
-
python3
|
|
73
|
+
python3 -m venv .venv
|
|
72
74
|
.venv/bin/python -m pip install -r requirements-lock.txt
|
|
73
75
|
.venv/bin/python -m pip install --no-deps --no-build-isolation .
|
|
74
76
|
.venv/bin/delphi-code setup
|
|
@@ -168,7 +170,7 @@ delphi-code add github.com/octo/tools
|
|
|
168
170
|
delphi-code add
|
|
169
171
|
```
|
|
170
172
|
|
|
171
|
-
Choose Bitbucket or GitHub (asked only when both have credentials), then a Bitbucket workspace or GitHub account (skipped when there is only one). Then check repositories in a list you can filter by typing. Repositories you already track start checked, so unchecking one stops tracking it and deletes its index. After you confirm, the new repositories are indexed. Filter options and `--ref` apply to the repositories you add. The prompts are drawn on stderr, and stdout still receives one JSON object with the added repositories under `repos` and the untracked ones under `removed`. Cancelling changes nothing and reports `"cancelled": true`. Without a terminal, `add` requires sources.
|
|
173
|
+
Choose Bitbucket or GitHub (asked only when both have credentials), then a Bitbucket workspace or GitHub account (skipped when there is only one). If your Bitbucket credentials may not list workspaces, you type the workspace name instead; `BITBUCKET_WORKSPACE` pre-fills it. Then check repositories in a list you can filter by typing. Repositories you already track start checked, so unchecking one stops tracking it and deletes its index. After you confirm, the new repositories are indexed. Filter options and `--ref` apply to the repositories you add. The prompts are drawn on stderr, and stdout still receives one JSON object with the added repositories under `repos` and the untracked ones under `removed`. Cancelling changes nothing and reports `"cancelled": true`. Without a terminal, `add` requires sources.
|
|
172
174
|
|
|
173
175
|
**Credentials.** Git never prompts. Credentials from the environment reach git through `GIT_ASKPASS`, never in URLs or arguments, and take precedence over configured git credentials. Listing repositories for the picker uses the services' REST APIs. The API requests run in a separate process, like the model download in `setup`, so the main process keeps its network guard.
|
|
174
176
|
|
|
@@ -177,7 +179,7 @@ Choose Bitbucket or GitHub (asked only when both have credentials), then a Bitbu
|
|
|
177
179
|
| Cloning | `BITBUCKET_USERNAME` and `BITBUCKET_APP_PASSWORD`, otherwise git's own credentials | `GITHUB_TOKEN` or `GH_TOKEN`, otherwise git's own credentials (`gh auth setup-git` configures them) |
|
|
178
180
|
| Listing for the picker | The same variables (with `BITBUCKET_EMAIL` in place of the username when set), otherwise the credentials git has stored for bitbucket.org | `GITHUB_TOKEN` or `GH_TOKEN`, otherwise `gh auth token`, otherwise the token git has stored for github.com |
|
|
179
181
|
|
|
180
|
-
Bitbucket has replaced app passwords with API tokens. An API token also works in `BITBUCKET_APP_PASSWORD`. Git over HTTPS takes the username that Bitbucket shows for it, and the REST API takes your Atlassian account email, which you set in `BITBUCKET_EMAIL`.
|
|
182
|
+
Bitbucket has replaced app passwords with API tokens. An API token also works in `BITBUCKET_APP_PASSWORD`. Git over HTTPS takes the username that Bitbucket shows for it, and the REST API takes your Atlassian account email, which you set in `BITBUCKET_EMAIL`. Cloning and listing a workspace's repositories need the `read:repository:bitbucket` scope. Listing your workspaces also needs `read:workspace:bitbucket`; without it, the picker asks for the workspace name. An error names the credentials that were used and the scopes Bitbucket requires.
|
|
181
183
|
|
|
182
184
|
`DELPHI_CODE_BITBUCKET_GIT_BASE` and `DELPHI_CODE_GITHUB_GIT_BASE` send clones to another base URL, such as a mirror, and `DELPHI_CODE_BITBUCKET_API_BASE` and `DELPHI_CODE_GITHUB_API_BASE` do the same for the APIs. Result links still point to bitbucket.org and github.com.
|
|
183
185
|
|
|
@@ -5,7 +5,7 @@ delphi_code/doctor.py,sha256=smuiZANoEGCRKDuiL2ls8wH9Uxl-yVsXf5EcNUHYYlA,1442
|
|
|
5
5
|
delphi_code/download.py,sha256=Mx50naH8omV38ZZ1U1J0ELopD7ccUNVW8H1gfOg6zLE,1048
|
|
6
6
|
delphi_code/errors.py,sha256=GXInITgDqBbqbBNdYXufy20fuH5es46rM-_iw2J6vCY,794
|
|
7
7
|
delphi_code/files.py,sha256=3CIhEBT1B6MjSaLVo7mxXQfIPKk5DpVXtaW8VeSuW-I,3321
|
|
8
|
-
delphi_code/hosts.py,sha256=
|
|
8
|
+
delphi_code/hosts.py,sha256=U_XpAnBM0R2rOziaoMaBCHbimp9HTwXn0oZ_4UI19C4,14918
|
|
9
9
|
delphi_code/indexing.py,sha256=VwVoL-r2khtEyVr4xtSiP6hzQiuwMYsh-qV-mX3BWFQ,3799
|
|
10
10
|
delphi_code/keys.py,sha256=s4G-iGu4JEJGku3_GdwNU8cMcIoA3Y8ClxrChXcqflE,2638
|
|
11
11
|
delphi_code/manifest.py,sha256=Gy5CHiW4tTDEO7lcT5S2w5-SSowulpXej2Q0e6PXvJQ,3989
|
|
@@ -13,17 +13,17 @@ delphi_code/model.py,sha256=8YVkgFKUvmhPjiEvWOjpckbapeonmYbKes4cSsEDIS4,4684
|
|
|
13
13
|
delphi_code/model_provenance.json,sha256=9fPASR9vJZXuKIdFWmc6Rc0A_I2OQnyOm6Ihytn9ab8,1370
|
|
14
14
|
delphi_code/offline.py,sha256=I7Axon7IS9vuVa6x8c0bjCeq9yf969GMkqgLv67OSJA,1119
|
|
15
15
|
delphi_code/paths.py,sha256=RoM1FDEpR-3u-mLfeFgLigPhqtzk-tPxzL_cQI8K1Ok,645
|
|
16
|
-
delphi_code/picker.py,sha256=
|
|
16
|
+
delphi_code/picker.py,sha256=D25tnsMxnBNDnQYJm6rKhgnd93NY5EtZpVIXlC2i-8k,5406
|
|
17
17
|
delphi_code/projects.py,sha256=M1cSVCxzZ0YedVDAhmVzF5Wv1BP_VaPlMLzFOWbp0Rc,12440
|
|
18
18
|
delphi_code/registry.py,sha256=tT4Ohu7L9ix_2NPJLcgOsebOafxJ45EnBiOtsLrePp0,4678
|
|
19
19
|
delphi_code/selection.py,sha256=PI8QvzX7KHcHFNIHHwzwZbOWUsvB9aJhiiWJr32h-qk,614
|
|
20
20
|
delphi_code/setup.py,sha256=eeQnc1K6MkCiPc5XLF5byAdC8QGubFETrzbSuGQM7_4,4596
|
|
21
|
-
delphi_code/sources.py,sha256=
|
|
21
|
+
delphi_code/sources.py,sha256=lgF4Ml9G2ovj48glcpIdVJUk26jUroUNkG_j3p4Q_GY,7139
|
|
22
22
|
delphi_code/store.py,sha256=2MVm2qRjWmuzeevqcgvkAPQ_JfoJ2WkSLviGNQVGEv4,9696
|
|
23
|
-
delphi_code-0.2.
|
|
24
|
-
delphi_code-0.2.
|
|
25
|
-
delphi_code-0.2.
|
|
26
|
-
delphi_code-0.2.
|
|
27
|
-
delphi_code-0.2.
|
|
28
|
-
delphi_code-0.2.
|
|
29
|
-
delphi_code-0.2.
|
|
23
|
+
delphi_code-0.2.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
24
|
+
delphi_code-0.2.2.dist-info/licenses/THIRD_PARTY_NOTICES.md,sha256=7UBHSW7RAHFxx3BG1GtsQ7a_bb1Si9TG2JL4HFvz774,2302
|
|
25
|
+
delphi_code-0.2.2.dist-info/METADATA,sha256=GTUE_q6gv72F6125jyquaeYi3d7WSFWbGLiFIIIX5y8,19036
|
|
26
|
+
delphi_code-0.2.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
27
|
+
delphi_code-0.2.2.dist-info/entry_points.txt,sha256=2UhlG66g8QmTYZko1fdwaZ5ziVY75sBPZeeSLk0fdJo,53
|
|
28
|
+
delphi_code-0.2.2.dist-info/top_level.txt,sha256=duf6zGKXk5n7vYW2b6IKVKfvZwLbXwp3yTiuTt62l2M,12
|
|
29
|
+
delphi_code-0.2.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|