heinzel-google 0.9.1__tar.gz
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.
- heinzel_google-0.9.1/.gitignore +15 -0
- heinzel_google-0.9.1/.hypothesis/.gitignore +9 -0
- heinzel_google-0.9.1/.hypothesis/constants/037c2f619c5f5ee0 +4 -0
- heinzel_google-0.9.1/.hypothesis/constants/32d6ed43e73e9c4d +4 -0
- heinzel_google-0.9.1/.hypothesis/constants/4ed33d3d987f61a4 +4 -0
- heinzel_google-0.9.1/.hypothesis/constants/67b0a8ccf18bf5d2 +4 -0
- heinzel_google-0.9.1/.hypothesis/constants/68ca2fef151a93d8 +4 -0
- heinzel_google-0.9.1/.hypothesis/constants/6da411fe19b8f4f4 +4 -0
- heinzel_google-0.9.1/.hypothesis/constants/6dcaadd2e5dfed7e +4 -0
- heinzel_google-0.9.1/.hypothesis/constants/b17d0304d74d3014 +4 -0
- heinzel_google-0.9.1/.hypothesis/constants/b4107e19f0c382e0 +4 -0
- heinzel_google-0.9.1/.hypothesis/constants/bbfc298edfa6d48e +4 -0
- heinzel_google-0.9.1/.hypothesis/constants/be6ec7ab5c779a4a +4 -0
- heinzel_google-0.9.1/.hypothesis/constants/bf69a526b8fa5d9c +4 -0
- heinzel_google-0.9.1/.hypothesis/constants/ca673ccd04b58ff7 +4 -0
- heinzel_google-0.9.1/.hypothesis/unicode_data/15.0.0/charmap.json.gz +0 -0
- heinzel_google-0.9.1/.hypothesis/unicode_data/15.0.0/codec-v2-utf-8.json.gz +0 -0
- heinzel_google-0.9.1/CHANGELOG.md +8 -0
- heinzel_google-0.9.1/LICENSE +21 -0
- heinzel_google-0.9.1/PKG-INFO +182 -0
- heinzel_google-0.9.1/README.md +169 -0
- heinzel_google-0.9.1/plugin-lock.json +41 -0
- heinzel_google-0.9.1/pyproject.toml +131 -0
- heinzel_google-0.9.1/requirements.lock +1241 -0
- heinzel_google-0.9.1/src/heinzel_google/__init__.py +133 -0
- heinzel_google-0.9.1/src/heinzel_google/_calendar.py +422 -0
- heinzel_google-0.9.1/src/heinzel_google/_docs.py +237 -0
- heinzel_google-0.9.1/src/heinzel_google/_documents.py +193 -0
- heinzel_google-0.9.1/src/heinzel_google/_gws.py +542 -0
- heinzel_google-0.9.1/src/heinzel_google/_mail.py +649 -0
- heinzel_google-0.9.1/src/heinzel_google/_models.py +72 -0
- heinzel_google-0.9.1/src/heinzel_google/_module.py +7 -0
- heinzel_google-0.9.1/src/heinzel_google/_service.py +141 -0
- heinzel_google-0.9.1/src/heinzel_google/_setup.py +120 -0
- heinzel_google-0.9.1/src/heinzel_google/_sheets.py +102 -0
- heinzel_google-0.9.1/src/heinzel_google/py.typed +0 -0
- heinzel_google-0.9.1/tests/conftest.py +210 -0
- heinzel_google-0.9.1/tests/integration/capability_test.py +226 -0
- heinzel_google-0.9.1/tests/integration/cli_test.py +63 -0
- heinzel_google-0.9.1/tests/integration/connector_test.py +75 -0
- heinzel_google-0.9.1/tests/integration/runner_test.py +125 -0
- heinzel_google-0.9.1/tests/unit/calendar_test.py +503 -0
- heinzel_google-0.9.1/tests/unit/contract_test.py +50 -0
- heinzel_google-0.9.1/tests/unit/docs_test.py +310 -0
- heinzel_google-0.9.1/tests/unit/documents_test.py +184 -0
- heinzel_google-0.9.1/tests/unit/gws_test.py +366 -0
- heinzel_google-0.9.1/tests/unit/mail_test.py +590 -0
- heinzel_google-0.9.1/tests/unit/service_test.py +136 -0
- heinzel_google-0.9.1/tests/unit/setup_test.py +160 -0
- heinzel_google-0.9.1/tests/unit/sheets_test.py +51 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# This .gitignore file was automatically created by Hypothesis. Hypothesis gitignores
|
|
2
|
+
# .hypothesis by default, because we generally recommend that .hypothesis not be checked
|
|
3
|
+
# into version control.
|
|
4
|
+
#
|
|
5
|
+
# If you *would* like to check .hypothesis into version control, you should delete this
|
|
6
|
+
# file. Hypothesis will not re-create this .gitignore unless .hypothesis is deleted (and
|
|
7
|
+
# if it does, that's a bug - please report it!)
|
|
8
|
+
|
|
9
|
+
*
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# file: /home/runner/work/heinzel-plugins/heinzel-plugins/plugins/heinzel-google/src/heinzel_google/_calendar.py
|
|
2
|
+
# hypothesis_version: 6.170.13
|
|
3
|
+
|
|
4
|
+
[250, 300, 100000, '#', '(no title)', '+00:00', '.webex.com', '.zoom.us', '?', '@', 'Google Calendar', 'Json', 'Z', '\\', '^[a-z0-9.-]+$', 'accepted', 'all', 'attendees', 'attendeesOmitted', 'busy', 'calendar', 'calendarId', 'calendars', 'cancelled', 'conferenceData', 'conferenceId', 'confirmed', 'createRequest', 'date', 'dateTime', 'declined', 'delete', 'description', 'displayName', 'email', 'end', 'entryPointType', 'entryPoints', 'errors', 'eventId', 'events', 'free and busy times', 'freebusy', 'get', 'guestsCanModify', 'hangoutLink', 'hangoutsMeet', 'htmlLink', 'https', 'id', 'insert', 'items', 'list', 'list[object]', 'location', 'maxResults', 'meet.google.com', 'needsAction', 'none', 'organizer', 'originalStartTime', 'patch', 'pending', 'primary', 'query', 'recurrence', 'requestId', 'resource', 'responseStatus', 'self', 'sendUpdates', 'showDeleted', 'singleEvents', 'start', 'status', 'summary', 'teams.live.com', 'teams.microsoft.com', 'tentative', 'timeMax', 'timeMin', 'timeZone', 'type', 'uri', 'video', 'zoom.us']
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# file: /home/runner/work/heinzel-plugins/heinzel-plugins/plugins/heinzel-google/src/heinzel_google/_docs.py
|
|
2
|
+
# hypothesis_version: 6.170.13
|
|
3
|
+
|
|
4
|
+
[1000000, 20000000, '\n---\n', ' --- |', ' |', ' | ', '#', '(', ')', '-', 'Json', 'NONE', 'TITLE', 'Untitled tab', '[', '[ \\t]+', '\\n{3,}', '\\|', ']', 'body', 'bold', 'bullet', 'childTabs', 'content', 'documentTab', 'elements', 'glyphSymbol', 'glyphType', 'horizontalRule', 'http://', 'https://', 'italic', 'link', 'listId', 'listProperties', 'list[object]', 'lists', 'name', 'namedStyleType', 'nestingLevel', 'nestingLevels', 'paragraph', 'paragraphStyle', 'person', 'personProperties', 'richLink', 'richLinkProperties', 'someone', 'tabProperties', 'table', 'tableCells', 'tableRows', 'tabs', 'textRun', 'textStyle', 'title', 'uri', 'url', '|', '| ']
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# file: /home/runner/work/heinzel-plugins/heinzel-plugins/plugins/heinzel-google/src/heinzel_google/__init__.py
|
|
2
|
+
# hypothesis_version: 6.170.13
|
|
3
|
+
|
|
4
|
+
[366, 'CALENDAR_HOSTS', 'CALENDAR_WRITE_SCOPE', 'DOCUMENT_HOSTS', 'Drive', 'DriveFile', 'Gmail', 'GoogleAbout', 'GoogleCalendar', 'GoogleDocuments', 'GoogleService', 'GoogleSession', 'GoogleSettings', 'Gws', 'LOGIN', 'LOGIN_WITH_MAIL', 'MAIL_HOSTS', 'MAIL_SCOPE', 'MAIL_WRITE_SCOPE', 'Process', 'SCOPES', 'calendar', 'docs.googleapis.com', 'documents', 'gmail.googleapis.com', 'login_command', 'mail', 'module', 'read', 'write', 'www.googleapis.com']
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# file: /home/runner/work/heinzel-plugins/heinzel-plugins/plugins/heinzel-google/src/heinzel_google/_sheets.py
|
|
2
|
+
# hypothesis_version: 6.170.13
|
|
3
|
+
|
|
4
|
+
[1000000, ' --- |', ' |', ' | ', '(a chart, not read)', '(empty)', 'Json', 'OBJECT', 'Untitled sheet', '\\|', 'data', 'formattedValue', 'list[object]', 'properties', 'rowData', 'sheetType', 'sheets', 'title', 'values', '|', '| ']
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# file: /home/runner/work/heinzel-plugins/heinzel-plugins/plugins/heinzel-google/src/heinzel_google/_service.py
|
|
2
|
+
# hypothesis_version: 6.170.13
|
|
3
|
+
|
|
4
|
+
[15.0, '0.0.0', 'Google Drive', 'about', 'bindings', 'calendar', 'dict[str, Any]', 'documents', 'drive', 'drive about get', 'emailAddress', 'fields', 'get', 'google', 'google.com', 'gws', 'heinzel-google', 'mail', 'user', 'your account']
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# file: /home/runner/work/heinzel-plugins/heinzel-plugins/plugins/heinzel-google/src/heinzel_google/_mail.py
|
|
2
|
+
# hypothesis_version: 6.170.13
|
|
3
|
+
|
|
4
|
+
[b'-_', 300.0, 100, 200, 500, 1000, 10000, 2000000, '"', ', ', '-', '-->', '/', '<', '<!--', '=', '>', '@', 'CATEGORY_', 'Cc', 'Content-Type', 'DRAFT', 'From', 'Gmail', 'IMPORTANT', 'INBOX', 'In-Reply-To', 'Json', 'Message-ID', 'References', 'Reply-To', 'STARRED', 'Subject', 'To', 'UNREAD', '\\', '\\n{3,}', '_', '_is_text_encoding', 'addLabelIds', 'ascii', 'blockquote', 'body', 'br', 'cc', 'content-type', 'conversation', 'create', 'data', 'div', 'draft', 'drafts', 'emailAddress', 'filename', 'format', 'from', 'full', 'get', 'getProfile', 'gmail', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'headers', 'hr', 'id', 'internalDate', 'labelIds', 'labels', 'li', 'list', 'list[object]', 'maxResults', 'me', 'message', 'message-id', 'messages', 'metadata', 'metadataHeaders', 'mimeType', 'modify', 'name', 'p', 'parts', 'payload', 'q', 'raw', 'raw_unicode_escape', 're:', 'references', 'removeLabelIds', 'replace', 'reply-to', 'script', 'send', 'spam', 'style', 'subject', 'table', 'template', 'text/html', 'text/plain', 'the labels', 'the new draft', 'threadId', 'threads', 'title', 'to', 'tr', 'trash', 'unicode_escape', 'userId', 'users', 'utf-8', 'value', 'your address']
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# file: /home/runner/work/heinzel-plugins/heinzel-plugins/plugins/heinzel-google/src/heinzel_google/_gws.py
|
|
2
|
+
# hypothesis_version: 6.170.13
|
|
3
|
+
|
|
4
|
+
[2.0, 10.0, 100, 300, 400, 401, 403, 404, 409, 429, 500, 65536, 10000000, 25000000, ' google.timeout.', '***', ',', '--format', '--json', '--params', '--version', '.', '.config', '/', ':', 'Gmail', 'HOME', 'HTTPS_PROXY', 'HTTP_PROXY', 'IO[bytes]', 'LANG', 'LC_', 'LOGNAME', 'NODE_EXTRA_CA_CERTS', 'NO_PROXY', 'PATH', 'RATE_LIMIT_EXCEEDED', 'SERVICE_DISABLED', 'SSL_CERT_DIR', 'SSL_CERT_FILE', 'SYSTEMROOT', 'TERM', 'TMPDIR', 'USER', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME', 'XDG_RUNTIME_DIR', 'accessNotConfigured', 'calendar', 'client_secret.json', 'code', 'credentials.enc', 'credentials.json', 'details', 'dict[str, Any]', 'error', 'errors', 'gws', 'http_proxy', 'https_proxy', 'hz setup google', 'json', 'list[object]', 'mail', 'message', 'nextPageToken', 'no details', 'no_proxy', 'object', 'pageToken', 'quotaExceeded', 'rateLimitExceeded', 'reason', 'replace', 'retry, or raise']
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# file: /home/runner/work/heinzel-plugins/heinzel-plugins/plugins/heinzel-google/src/heinzel_google/_documents.py
|
|
2
|
+
# hypothesis_version: 6.170.13
|
|
3
|
+
|
|
4
|
+
[100, 200, " and name contains '", ' or ', "'", '/open', 'Google Docs', 'Google Drive', 'Google Sheets', '\\', "\\'", '\\\\', 'dict[str, Any]', 'docs', 'docs.google.com', 'document', 'documentId', 'documents', 'drive', 'drive.google.com', 'fields', 'fileId', 'files', 'get', 'https', 'id', 'includeGridData', 'includeTabsContent', 'list', 'mimeType', 'modifiedTime', 'modifiedTime desc', 'name', 'orderBy', 'pageSize', 'properties', 'q', 'revisionId', 'sheets', 'shortcutDetails', 'spreadsheet', 'spreadsheetId', 'spreadsheets', 'supportsAllDrives', 'targetId', 'title', 'version']
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# file: /home/runner/work/heinzel-plugins/heinzel-plugins/plugins/heinzel-google/src/heinzel_google/_setup.py
|
|
2
|
+
# hypothesis_version: 6.170.13
|
|
3
|
+
|
|
4
|
+
[900.0, ' only', '?', 'OAuth client', 'automatic', 'calendar', 'configured', 'default', 'documents', 'google', 'gws is signed in.', 'gws_path', 'mail', 'read-only access']
|
|
Binary file
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.9.1
|
|
4
|
+
|
|
5
|
+
Move this plugin to its independent repository project for the Heinzel 0.9.1 plugin contract.
|
|
6
|
+
Own its dependencies, tests and artifacts; consume public contracts without concrete provider dependencies.
|
|
7
|
+
|
|
8
|
+
Earlier history is preserved in the source repository named in README.md.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Helmut Hoffer von Ankershoffen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: heinzel-google
|
|
3
|
+
Version: 0.9.1
|
|
4
|
+
Summary: Google Docs, Sheets, Drive, Calendar and Gmail: the documents, calendar and mail capabilities.
|
|
5
|
+
Project-URL: Source, https://github.com/helmut-hoffer-von-ankershoffen/heinzel-plugins/tree/main/plugins/heinzel-google
|
|
6
|
+
Project-URL: Changelog, https://github.com/helmut-hoffer-von-ankershoffen/heinzel-plugins/blob/main/plugins/heinzel-google/CHANGELOG.md
|
|
7
|
+
Project-URL: Issues, https://github.com/helmut-hoffer-von-ankershoffen/heinzel-plugins/issues
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Python: >=3.12
|
|
11
|
+
Requires-Dist: heinzel<0.10,>=0.9.1
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# heinzel-google
|
|
15
|
+
|
|
16
|
+
Google Docs, Sheets, Drive, Calendar and Gmail: the `documents` (read), `calendar` and `mail` (read and write)
|
|
17
|
+
capabilities ([ADR 0028](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0028-integrations-are-capability-adapters.md)): a first-party
|
|
18
|
+
[Heinzelmännchen](https://github.com/helmut-hoffer-von-ankershoffen/heinzel) plugin. It is independently released and installed explicitly:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
hz system plugin add google --confirm # a compatible plugin release; run it again to upgrade
|
|
22
|
+
hz system plugin remove heinzel-google
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
It is the **default provider** for Google Workspace, not the only possible one: workflows ask for `documents`,
|
|
26
|
+
`calendar` and `mail`, and any other plugin that provides those capabilities can be bound instead, in configuration.
|
|
27
|
+
|
|
28
|
+
## How it reaches Google
|
|
29
|
+
|
|
30
|
+
Through the open-source [Google Workspace CLI](https://github.com/googleworkspace/cli), `gws` (0.22 or newer): it
|
|
31
|
+
signs in with Google, keeps its own credentials (encrypted, with the key in the OS keychain, by default) and calls
|
|
32
|
+
Google's APIs. This plugin never sees a token. It runs `gws <service> <resource> <method> --params <JSON> --format
|
|
33
|
+
json` as an argument list (never through a shell), with the executable resolved to an absolute path, from the root
|
|
34
|
+
directory (so no project's `.env` reaches `gws`), with an allowlisted environment (never Heinzelmännchen's own
|
|
35
|
+
credentials or other secrets; of gws's own variables only `GOOGLE_WORKSPACE_CLI_CONFIG_DIR`, `_KEYRING_BACKEND`,
|
|
36
|
+
`_CLIENT_ID`, `_CLIENT_SECRET` and `GOOGLE_WORKSPACE_PROJECT_ID`, not its token, credentials-file, sanitizing or log
|
|
37
|
+
variables), a
|
|
38
|
+
timeout that stops `gws` and anything it started, and at most 10 MB of output read. Why `gws`:
|
|
39
|
+
[ADR 0042](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0042-google-workspace-through-the-gws-cli.md).
|
|
40
|
+
|
|
41
|
+
What leaves this machine, and where to: file ids, the Drive query (the modified-time window and the `--name` text)
|
|
42
|
+
the calendar window and the mail search, to Google's API hosts (`www.googleapis.com` for Drive and Calendar,
|
|
43
|
+
`docs.googleapis.com`, `sheets.googleapis.com`, `gmail.googleapis.com`); `gws` also fetches API descriptions from Google and refreshes its sign-in at
|
|
44
|
+
`oauth2.googleapis.com`. Nothing goes anywhere else: gws 0.22 documents no telemetry or update check, and none was
|
|
45
|
+
found in it.
|
|
46
|
+
|
|
47
|
+
## Set up
|
|
48
|
+
|
|
49
|
+
Install `gws` (it needs Node.js), give it an OAuth client once (`gws auth setup`, which needs `gcloud`, or a Desktop
|
|
50
|
+
OAuth client as its README describes), then let setup do the rest:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install -g @googleworkspace/cli
|
|
54
|
+
hz setup google # finds gws, signs it in, binds documents, calendar and (if you say so) mail
|
|
55
|
+
hz system doctor # finds gws, checks its version and its stored sign-in, from files; Google isn't contacted
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
By hand, setup is:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
gws auth login --scopes https://www.googleapis.com/auth/documents.readonly,https://www.googleapis.com/auth/spreadsheets.readonly,https://www.googleapis.com/auth/drive.metadata.readonly,https://www.googleapis.com/auth/calendar.readonly
|
|
62
|
+
hz system config set capabilities.documents google
|
|
63
|
+
hz system config set capabilities.calendar google
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Mail is opt-in: setup asks, defaulting to no, and then signs in again with Gmail's read-only scope added
|
|
67
|
+
(`,https://www.googleapis.com/auth/gmail.readonly`), which lets the plugin read all of your mail; then
|
|
68
|
+
`hz system config set capabilities.mail google`.
|
|
69
|
+
|
|
70
|
+
Writes are opt-in too. Setup asks whether hz may change your calendar and your mail, defaulting to no; only then does
|
|
71
|
+
the sign-in ask for `https://www.googleapis.com/auth/calendar.events` (create, move, cancel and answer events) and
|
|
72
|
+
`https://www.googleapis.com/auth/gmail.modify` (keep drafts, send them, file conversations; it reads too, so it
|
|
73
|
+
replaces `gmail.readonly`, and it can't delete mail for good). Heinzelmännchen writes only from publish operations
|
|
74
|
+
that show you what they will do and run after you confirm; a chat's agent only proposes them
|
|
75
|
+
([ADR 0059](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0059-the-calendar-and-the-mail-write-through-hz-after-a-confirmation.md)). Without the
|
|
76
|
+
scope, a write fails with the `gws auth login` that adds it.
|
|
77
|
+
|
|
78
|
+
These are the narrowest read-only scopes for what the adapters do; `drive.metadata.readonly` lists and identifies files
|
|
79
|
+
and never reads their contents (Google classes it as restricted, which matters only for published OAuth clients).
|
|
80
|
+
Bindings go in your user configuration, never in a project `hz.toml`, which Heinzelmännchen ignores for them.
|
|
81
|
+
|
|
82
|
+
Optional settings, in `[google]` or `HZ_GOOGLE__<FIELD>`: `gws_path`, the absolute path of `gws` when it isn't on
|
|
83
|
+
`PATH` (only absolute `PATH` entries are searched; both are ignored in a project `hz.toml`: a repository someone else
|
|
84
|
+
controls must not choose the program that reads your data), and `timeout`, the seconds each `gws` run may take
|
|
85
|
+
(default 60, also ignored in a project `hz.toml`).
|
|
86
|
+
|
|
87
|
+
### Is it connected?
|
|
88
|
+
|
|
89
|
+
Bound automatically to documents and calendar while it is the only installed adapter of them, [ADR
|
|
90
|
+
0055](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0055-connectors-show-their-sign-in-test-live-on-demand-and-bind-themselves-when-alone.md); mail
|
|
91
|
+
never is, so Gmail stays opt-in: `hz setup google` asks. `hz system connector
|
|
92
|
+
list` shows whether gws has a sign-in, from its files alone; `hz system connector
|
|
93
|
+
test google` asks Drive who is signed in (`gws drive about get`, 15 s at most), and only when you run it.
|
|
94
|
+
|
|
95
|
+
## Use
|
|
96
|
+
|
|
97
|
+
Workflows ask for `documents` and `calendar`, never for Google:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from pydantic import AwareDatetime
|
|
101
|
+
|
|
102
|
+
from heinzel import Calendar, Document, Documents, Event, Module, Window
|
|
103
|
+
|
|
104
|
+
module = Module("prep", "One-on-one preparation.", requires=["documents:read", "calendar:read"])
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@module.operation
|
|
108
|
+
def week(calendar: Calendar, since: AwareDatetime, until: AwareDatetime) -> list[Event]:
|
|
109
|
+
"""This week's events."""
|
|
110
|
+
return calendar.events(Window(since=since, until=until))
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@module.operation
|
|
114
|
+
def notes(documents: Documents) -> Document:
|
|
115
|
+
"""The running notes, as Markdown."""
|
|
116
|
+
return documents.read("https://docs.google.com/document/d/1AbCdEfGhIjKlMnOpQrStUvWxYz/edit")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
| Method | Capability | `gws` command |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| `read(ref)` | `documents:read` | `drive files get` (metadata), then `docs documents get` with `includeTabsContent` or `sheets spreadsheets get` with `includeGridData` |
|
|
122
|
+
| `events(window)` | `calendar:read` | `calendar events list` on `primary`, `singleEvents`, `showDeleted`, paged by `pageToken` |
|
|
123
|
+
| `messages(query, window)` | `mail:read` | `gmail users messages list` with Gmail's search, then `messages get` (metadata) for each |
|
|
124
|
+
| `read(message)` | `mail:read` | `gmail users messages get` (full) |
|
|
125
|
+
| `free_busy(people, window)` | `calendar:read` | `calendar freebusy query` for `primary` and each address |
|
|
126
|
+
| `create_event(event)` | `calendar:write` | `calendar events insert` on `primary` with an id of hz's own (no duplicate on a retry), `sendUpdates` as the draft says, a Meet link when asked |
|
|
127
|
+
| `update_event(event, change)` | `calendar:write` | `calendar events get`, then `patch` (only your events, or ones whose guests may change them) |
|
|
128
|
+
| `cancel_event(event, notify)` | `calendar:write` | `calendar events get`, then `delete` (only your events; decline others') |
|
|
129
|
+
| `respond(event, response)` | `calendar:write` | `calendar events get`, then `patch` of your own attendee entry |
|
|
130
|
+
| `thread(thread)` | `mail:read` | `gmail users threads get` (full), without drafts, Spam and Trash |
|
|
131
|
+
| `draft(draft)` | `mail:read` | `gmail users drafts get` (full) |
|
|
132
|
+
| `create_draft(draft)` | `mail:write` | `gmail users drafts create` with a plain-text MIME message; a reply with `threadId`, `In-Reply-To` and `References` |
|
|
133
|
+
| `send_draft(draft, seen)` | `mail:write` | `gmail users drafts get`, then `send` if its message is still the one you saw |
|
|
134
|
+
| `relabel(thread, add, remove)` | `mail:write` | `gmail users threads modify` with label ids; not Spam, Trash, Drafts or Sent |
|
|
135
|
+
|
|
136
|
+
- **Documents** are a Docs, Sheets or Drive link (`https://docs.google.com/document/d/<id>/...`,
|
|
137
|
+
`.../spreadsheets/d/<id>/...`, `https://drive.google.com/file/d/<id>/...`, `.../open?id=<id>`) or a bare file id;
|
|
138
|
+
links to any other host are refused before `gws` runs, and a Drive shortcut is followed once. A Doc comes back as
|
|
139
|
+
Markdown-flavoured text, every tab under its title: headings, lists, bold and italic, links, tables; person chips by
|
|
140
|
+
name, never their email, and mail links by their text; titles and link targets can't start a line of their own;
|
|
141
|
+
images and drawings are left out. A Sheet comes back as one `## <sheet>` table per sheet
|
|
142
|
+
with the values as people see them. `revision` is the Doc's revision id or the Sheet's Drive version. Uploaded Word
|
|
143
|
+
or Excel files are refused: open them as a Google Doc or Sheet first.
|
|
144
|
+
- **Events** are your primary calendar's, every occurrence of a recurring event, cancelled ones included, by start.
|
|
145
|
+
Attendees are display names and emails, without meeting rooms; the workflow packet builder drops the emails before
|
|
146
|
+
anything reaches an agent's prompt. `conference_id` is the Zoom meeting number when the event has a Zoom link (in
|
|
147
|
+
its conferencing data, location or description), else the Meet code, so the `meetings` capability can find the
|
|
148
|
+
event's meeting; conference links are kept only on the conferencing services' own hosts (Meet, Zoom, Teams, Webex),
|
|
149
|
+
without their query, where passcodes live. A display name that is an address is dropped.
|
|
150
|
+
- **Mail** is your Gmail: `messages` takes the shared query syntax (`from:<address or domain> subject:<text>
|
|
151
|
+
label:<name>`) and a window. The window and the sender go into Gmail's search and the label as its id; the subject
|
|
152
|
+
is matched by the plugin, because Gmail's `subject:` matches whole words only. Results come oldest first, at most
|
|
153
|
+
200 messages a window (one `gws` run each). `read` gives the text part, or the HTML part as text, in the part's own
|
|
154
|
+
charset, without attachments, and only the headers that carry no addresses. Senders and recipients are names and
|
|
155
|
+
emails, parsed before any encoded name is decoded, so a display name can't pose as an address; the workflow packet
|
|
156
|
+
builder drops the addresses before anything reaches an agent's prompt. A message longer than 2 million characters,
|
|
157
|
+
or with more than 500 MIME parts or 2 million characters of HTML, is refused, not cut; Spam and Trash are not
|
|
158
|
+
read; reading one window's messages may take at most five minutes.
|
|
159
|
+
- **Writes** change only what the person confirmed, through publish operations. Ids are checked before they reach
|
|
160
|
+
`gws`; a write that times out or fails without Google refusing it is not retried and says to look in Google first;
|
|
161
|
+
a rate-limited one is retried. A recurring series' own id is refused: one occurrence changes at a time. Drafts are built with
|
|
162
|
+
Python's `email` package from plain addresses and one-line subjects, so nothing can add a header.
|
|
163
|
+
- **Text is data.** Every string returned passes `heinzel.clean_text` (no control, invisible or bidirectional
|
|
164
|
+
characters).
|
|
165
|
+
- **Bounds.** One `gws` run prints at most 10 MB, one call reads at most 100 pages or 25 MB, a Doc's text at most four
|
|
166
|
+
times its JSON, tables and tabs nest at most 8 deep, a Sheet at most a million cells; past that, an error says to
|
|
167
|
+
narrow the window or that the file is too large. Rate limits are honoured: a rate-limited call is retried after 1, 2
|
|
168
|
+
and 4 seconds, then fails saying when to retry.
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
hz google info # this plugin, its version, the gws it uses, the scopes and APIs
|
|
172
|
+
hz google files --days 3 # Docs and Sheets you can open that changed in the last three days
|
|
173
|
+
hz system capability list # who reads what, through which adapter and hosts
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Errors say what to do: no `gws` names the install; not signed in or an expired sign-in names the exact
|
|
177
|
+
`gws auth login` command above (doctor names `hz setup google`); a missing scope or a disabled API says which; a file the account can't see says so.
|
|
178
|
+
Token-shaped strings in anything `gws` says are redacted before it is shortened.
|
|
179
|
+
|
|
180
|
+
Development and release commands: [repository guide](../../README.md).
|
|
181
|
+
|
|
182
|
+
Migration provenance: heinzel, before the 0.9 plugin-boundary migration.
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# heinzel-google
|
|
2
|
+
|
|
3
|
+
Google Docs, Sheets, Drive, Calendar and Gmail: the `documents` (read), `calendar` and `mail` (read and write)
|
|
4
|
+
capabilities ([ADR 0028](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0028-integrations-are-capability-adapters.md)): a first-party
|
|
5
|
+
[Heinzelmännchen](https://github.com/helmut-hoffer-von-ankershoffen/heinzel) plugin. It is independently released and installed explicitly:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
hz system plugin add google --confirm # a compatible plugin release; run it again to upgrade
|
|
9
|
+
hz system plugin remove heinzel-google
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
It is the **default provider** for Google Workspace, not the only possible one: workflows ask for `documents`,
|
|
13
|
+
`calendar` and `mail`, and any other plugin that provides those capabilities can be bound instead, in configuration.
|
|
14
|
+
|
|
15
|
+
## How it reaches Google
|
|
16
|
+
|
|
17
|
+
Through the open-source [Google Workspace CLI](https://github.com/googleworkspace/cli), `gws` (0.22 or newer): it
|
|
18
|
+
signs in with Google, keeps its own credentials (encrypted, with the key in the OS keychain, by default) and calls
|
|
19
|
+
Google's APIs. This plugin never sees a token. It runs `gws <service> <resource> <method> --params <JSON> --format
|
|
20
|
+
json` as an argument list (never through a shell), with the executable resolved to an absolute path, from the root
|
|
21
|
+
directory (so no project's `.env` reaches `gws`), with an allowlisted environment (never Heinzelmännchen's own
|
|
22
|
+
credentials or other secrets; of gws's own variables only `GOOGLE_WORKSPACE_CLI_CONFIG_DIR`, `_KEYRING_BACKEND`,
|
|
23
|
+
`_CLIENT_ID`, `_CLIENT_SECRET` and `GOOGLE_WORKSPACE_PROJECT_ID`, not its token, credentials-file, sanitizing or log
|
|
24
|
+
variables), a
|
|
25
|
+
timeout that stops `gws` and anything it started, and at most 10 MB of output read. Why `gws`:
|
|
26
|
+
[ADR 0042](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0042-google-workspace-through-the-gws-cli.md).
|
|
27
|
+
|
|
28
|
+
What leaves this machine, and where to: file ids, the Drive query (the modified-time window and the `--name` text)
|
|
29
|
+
the calendar window and the mail search, to Google's API hosts (`www.googleapis.com` for Drive and Calendar,
|
|
30
|
+
`docs.googleapis.com`, `sheets.googleapis.com`, `gmail.googleapis.com`); `gws` also fetches API descriptions from Google and refreshes its sign-in at
|
|
31
|
+
`oauth2.googleapis.com`. Nothing goes anywhere else: gws 0.22 documents no telemetry or update check, and none was
|
|
32
|
+
found in it.
|
|
33
|
+
|
|
34
|
+
## Set up
|
|
35
|
+
|
|
36
|
+
Install `gws` (it needs Node.js), give it an OAuth client once (`gws auth setup`, which needs `gcloud`, or a Desktop
|
|
37
|
+
OAuth client as its README describes), then let setup do the rest:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install -g @googleworkspace/cli
|
|
41
|
+
hz setup google # finds gws, signs it in, binds documents, calendar and (if you say so) mail
|
|
42
|
+
hz system doctor # finds gws, checks its version and its stored sign-in, from files; Google isn't contacted
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
By hand, setup is:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
gws auth login --scopes https://www.googleapis.com/auth/documents.readonly,https://www.googleapis.com/auth/spreadsheets.readonly,https://www.googleapis.com/auth/drive.metadata.readonly,https://www.googleapis.com/auth/calendar.readonly
|
|
49
|
+
hz system config set capabilities.documents google
|
|
50
|
+
hz system config set capabilities.calendar google
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Mail is opt-in: setup asks, defaulting to no, and then signs in again with Gmail's read-only scope added
|
|
54
|
+
(`,https://www.googleapis.com/auth/gmail.readonly`), which lets the plugin read all of your mail; then
|
|
55
|
+
`hz system config set capabilities.mail google`.
|
|
56
|
+
|
|
57
|
+
Writes are opt-in too. Setup asks whether hz may change your calendar and your mail, defaulting to no; only then does
|
|
58
|
+
the sign-in ask for `https://www.googleapis.com/auth/calendar.events` (create, move, cancel and answer events) and
|
|
59
|
+
`https://www.googleapis.com/auth/gmail.modify` (keep drafts, send them, file conversations; it reads too, so it
|
|
60
|
+
replaces `gmail.readonly`, and it can't delete mail for good). Heinzelmännchen writes only from publish operations
|
|
61
|
+
that show you what they will do and run after you confirm; a chat's agent only proposes them
|
|
62
|
+
([ADR 0059](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0059-the-calendar-and-the-mail-write-through-hz-after-a-confirmation.md)). Without the
|
|
63
|
+
scope, a write fails with the `gws auth login` that adds it.
|
|
64
|
+
|
|
65
|
+
These are the narrowest read-only scopes for what the adapters do; `drive.metadata.readonly` lists and identifies files
|
|
66
|
+
and never reads their contents (Google classes it as restricted, which matters only for published OAuth clients).
|
|
67
|
+
Bindings go in your user configuration, never in a project `hz.toml`, which Heinzelmännchen ignores for them.
|
|
68
|
+
|
|
69
|
+
Optional settings, in `[google]` or `HZ_GOOGLE__<FIELD>`: `gws_path`, the absolute path of `gws` when it isn't on
|
|
70
|
+
`PATH` (only absolute `PATH` entries are searched; both are ignored in a project `hz.toml`: a repository someone else
|
|
71
|
+
controls must not choose the program that reads your data), and `timeout`, the seconds each `gws` run may take
|
|
72
|
+
(default 60, also ignored in a project `hz.toml`).
|
|
73
|
+
|
|
74
|
+
### Is it connected?
|
|
75
|
+
|
|
76
|
+
Bound automatically to documents and calendar while it is the only installed adapter of them, [ADR
|
|
77
|
+
0055](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0055-connectors-show-their-sign-in-test-live-on-demand-and-bind-themselves-when-alone.md); mail
|
|
78
|
+
never is, so Gmail stays opt-in: `hz setup google` asks. `hz system connector
|
|
79
|
+
list` shows whether gws has a sign-in, from its files alone; `hz system connector
|
|
80
|
+
test google` asks Drive who is signed in (`gws drive about get`, 15 s at most), and only when you run it.
|
|
81
|
+
|
|
82
|
+
## Use
|
|
83
|
+
|
|
84
|
+
Workflows ask for `documents` and `calendar`, never for Google:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from pydantic import AwareDatetime
|
|
88
|
+
|
|
89
|
+
from heinzel import Calendar, Document, Documents, Event, Module, Window
|
|
90
|
+
|
|
91
|
+
module = Module("prep", "One-on-one preparation.", requires=["documents:read", "calendar:read"])
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@module.operation
|
|
95
|
+
def week(calendar: Calendar, since: AwareDatetime, until: AwareDatetime) -> list[Event]:
|
|
96
|
+
"""This week's events."""
|
|
97
|
+
return calendar.events(Window(since=since, until=until))
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@module.operation
|
|
101
|
+
def notes(documents: Documents) -> Document:
|
|
102
|
+
"""The running notes, as Markdown."""
|
|
103
|
+
return documents.read("https://docs.google.com/document/d/1AbCdEfGhIjKlMnOpQrStUvWxYz/edit")
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
| Method | Capability | `gws` command |
|
|
107
|
+
| --- | --- | --- |
|
|
108
|
+
| `read(ref)` | `documents:read` | `drive files get` (metadata), then `docs documents get` with `includeTabsContent` or `sheets spreadsheets get` with `includeGridData` |
|
|
109
|
+
| `events(window)` | `calendar:read` | `calendar events list` on `primary`, `singleEvents`, `showDeleted`, paged by `pageToken` |
|
|
110
|
+
| `messages(query, window)` | `mail:read` | `gmail users messages list` with Gmail's search, then `messages get` (metadata) for each |
|
|
111
|
+
| `read(message)` | `mail:read` | `gmail users messages get` (full) |
|
|
112
|
+
| `free_busy(people, window)` | `calendar:read` | `calendar freebusy query` for `primary` and each address |
|
|
113
|
+
| `create_event(event)` | `calendar:write` | `calendar events insert` on `primary` with an id of hz's own (no duplicate on a retry), `sendUpdates` as the draft says, a Meet link when asked |
|
|
114
|
+
| `update_event(event, change)` | `calendar:write` | `calendar events get`, then `patch` (only your events, or ones whose guests may change them) |
|
|
115
|
+
| `cancel_event(event, notify)` | `calendar:write` | `calendar events get`, then `delete` (only your events; decline others') |
|
|
116
|
+
| `respond(event, response)` | `calendar:write` | `calendar events get`, then `patch` of your own attendee entry |
|
|
117
|
+
| `thread(thread)` | `mail:read` | `gmail users threads get` (full), without drafts, Spam and Trash |
|
|
118
|
+
| `draft(draft)` | `mail:read` | `gmail users drafts get` (full) |
|
|
119
|
+
| `create_draft(draft)` | `mail:write` | `gmail users drafts create` with a plain-text MIME message; a reply with `threadId`, `In-Reply-To` and `References` |
|
|
120
|
+
| `send_draft(draft, seen)` | `mail:write` | `gmail users drafts get`, then `send` if its message is still the one you saw |
|
|
121
|
+
| `relabel(thread, add, remove)` | `mail:write` | `gmail users threads modify` with label ids; not Spam, Trash, Drafts or Sent |
|
|
122
|
+
|
|
123
|
+
- **Documents** are a Docs, Sheets or Drive link (`https://docs.google.com/document/d/<id>/...`,
|
|
124
|
+
`.../spreadsheets/d/<id>/...`, `https://drive.google.com/file/d/<id>/...`, `.../open?id=<id>`) or a bare file id;
|
|
125
|
+
links to any other host are refused before `gws` runs, and a Drive shortcut is followed once. A Doc comes back as
|
|
126
|
+
Markdown-flavoured text, every tab under its title: headings, lists, bold and italic, links, tables; person chips by
|
|
127
|
+
name, never their email, and mail links by their text; titles and link targets can't start a line of their own;
|
|
128
|
+
images and drawings are left out. A Sheet comes back as one `## <sheet>` table per sheet
|
|
129
|
+
with the values as people see them. `revision` is the Doc's revision id or the Sheet's Drive version. Uploaded Word
|
|
130
|
+
or Excel files are refused: open them as a Google Doc or Sheet first.
|
|
131
|
+
- **Events** are your primary calendar's, every occurrence of a recurring event, cancelled ones included, by start.
|
|
132
|
+
Attendees are display names and emails, without meeting rooms; the workflow packet builder drops the emails before
|
|
133
|
+
anything reaches an agent's prompt. `conference_id` is the Zoom meeting number when the event has a Zoom link (in
|
|
134
|
+
its conferencing data, location or description), else the Meet code, so the `meetings` capability can find the
|
|
135
|
+
event's meeting; conference links are kept only on the conferencing services' own hosts (Meet, Zoom, Teams, Webex),
|
|
136
|
+
without their query, where passcodes live. A display name that is an address is dropped.
|
|
137
|
+
- **Mail** is your Gmail: `messages` takes the shared query syntax (`from:<address or domain> subject:<text>
|
|
138
|
+
label:<name>`) and a window. The window and the sender go into Gmail's search and the label as its id; the subject
|
|
139
|
+
is matched by the plugin, because Gmail's `subject:` matches whole words only. Results come oldest first, at most
|
|
140
|
+
200 messages a window (one `gws` run each). `read` gives the text part, or the HTML part as text, in the part's own
|
|
141
|
+
charset, without attachments, and only the headers that carry no addresses. Senders and recipients are names and
|
|
142
|
+
emails, parsed before any encoded name is decoded, so a display name can't pose as an address; the workflow packet
|
|
143
|
+
builder drops the addresses before anything reaches an agent's prompt. A message longer than 2 million characters,
|
|
144
|
+
or with more than 500 MIME parts or 2 million characters of HTML, is refused, not cut; Spam and Trash are not
|
|
145
|
+
read; reading one window's messages may take at most five minutes.
|
|
146
|
+
- **Writes** change only what the person confirmed, through publish operations. Ids are checked before they reach
|
|
147
|
+
`gws`; a write that times out or fails without Google refusing it is not retried and says to look in Google first;
|
|
148
|
+
a rate-limited one is retried. A recurring series' own id is refused: one occurrence changes at a time. Drafts are built with
|
|
149
|
+
Python's `email` package from plain addresses and one-line subjects, so nothing can add a header.
|
|
150
|
+
- **Text is data.** Every string returned passes `heinzel.clean_text` (no control, invisible or bidirectional
|
|
151
|
+
characters).
|
|
152
|
+
- **Bounds.** One `gws` run prints at most 10 MB, one call reads at most 100 pages or 25 MB, a Doc's text at most four
|
|
153
|
+
times its JSON, tables and tabs nest at most 8 deep, a Sheet at most a million cells; past that, an error says to
|
|
154
|
+
narrow the window or that the file is too large. Rate limits are honoured: a rate-limited call is retried after 1, 2
|
|
155
|
+
and 4 seconds, then fails saying when to retry.
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
hz google info # this plugin, its version, the gws it uses, the scopes and APIs
|
|
159
|
+
hz google files --days 3 # Docs and Sheets you can open that changed in the last three days
|
|
160
|
+
hz system capability list # who reads what, through which adapter and hosts
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Errors say what to do: no `gws` names the install; not signed in or an expired sign-in names the exact
|
|
164
|
+
`gws auth login` command above (doctor names `hz setup google`); a missing scope or a disabled API says which; a file the account can't see says so.
|
|
165
|
+
Token-shaped strings in anything `gws` says are redacted before it is shortened.
|
|
166
|
+
|
|
167
|
+
Development and release commands: [repository guide](../../README.md).
|
|
168
|
+
|
|
169
|
+
Migration provenance: heinzel, before the 0.9 plugin-boundary migration.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"format": 1,
|
|
3
|
+
"sdk": 1,
|
|
4
|
+
"inputs": [
|
|
5
|
+
"coverage>=7.12",
|
|
6
|
+
"editables>=0.5",
|
|
7
|
+
"hatchling>=1.29.0",
|
|
8
|
+
"heinzel>=0.9.1,<0.10",
|
|
9
|
+
"hypothesis>=6.150,<7",
|
|
10
|
+
"mypy>=1.19.0,<3",
|
|
11
|
+
"pyright[nodejs]==1.1.414",
|
|
12
|
+
"pytest-timeout>=2.4.0,<3",
|
|
13
|
+
"pytest-xdist>=3.8",
|
|
14
|
+
"pytest>=9.0.3,<10",
|
|
15
|
+
"ruff==0.17.2"
|
|
16
|
+
],
|
|
17
|
+
"core": {
|
|
18
|
+
"version": "0.9.1",
|
|
19
|
+
"dependencies": [
|
|
20
|
+
"croniter>=6.2.4",
|
|
21
|
+
"fastapi>=0.161",
|
|
22
|
+
"keyring>=25.6",
|
|
23
|
+
"mcp>=2.3",
|
|
24
|
+
"packaging>=25.0",
|
|
25
|
+
"platformdirs>=4.5",
|
|
26
|
+
"pluggy<2,>=1.6.0",
|
|
27
|
+
"psutil==7.2.2",
|
|
28
|
+
"pydantic-ai-slim==2.61.0",
|
|
29
|
+
"pydantic-ai-slim[anthropic]==2.61.0; extra == \"anthropic\"",
|
|
30
|
+
"pydantic-ai-slim[openai]==2.61.0; extra == \"openai\"",
|
|
31
|
+
"pydantic-settings<3,>=2.11.0",
|
|
32
|
+
"pydantic<3,>=2.12.0",
|
|
33
|
+
"pyyaml<7,>=6.0.2",
|
|
34
|
+
"rich>=14.2.0",
|
|
35
|
+
"tomlkit>=0.13",
|
|
36
|
+
"typer>=0.23.0",
|
|
37
|
+
"uvicorn>=0.54",
|
|
38
|
+
"wsproto>=1.3"
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
}
|