|
1 | 1 | # @corbits/google-tools |
2 | 2 |
|
3 | | -Google API clients and Interchange tools, starting with Gmail. Authentication |
4 | | -is supplied through the Interchange `gmail-api` mediated credential; the |
5 | | -package never accepts or returns raw tokens. |
| 3 | +Gmail is the only Google API supported today. The package provides a Gmail |
| 4 | +API client and Interchange tools. Authentication is supplied through the |
| 5 | +Interchange `gmail-api` mediated credential; the package never accepts or |
| 6 | +returns raw tokens. |
6 | 7 |
|
7 | 8 | ## Requirements |
8 | 9 |
|
9 | | -Node.js 24 or newer (`engines.node >=24`). |
| 10 | +Node.js 24 or newer (`engines.node >=24`). `@intx/agent` and `@intx/types` |
| 11 | +are required peer dependencies. |
10 | 12 |
|
11 | 13 | ## Install |
12 | 14 |
|
13 | 15 | ```bash |
14 | | -bun add github:corbitsdev/google-tools |
| 16 | +npm add @corbits/google-tools |
15 | 17 | ``` |
16 | 18 |
|
17 | | -## Working on it |
| 19 | +## Quickstart |
| 20 | + |
| 21 | +`createGmailTools` resolves the `gmail-api` credential from the |
| 22 | +`RuntimeCapabilities` it is given. An Interchange host supplies those |
| 23 | +capabilities; outside a host, build them with `createRuntimeCapabilities` from |
| 24 | +`@intx/types` and an http credential that adds a Gmail OAuth access token |
| 25 | +granted `https://www.googleapis.com/auth/gmail.modify`: |
| 26 | + |
| 27 | +```ts |
| 28 | +import { createGmailTools } from "@corbits/google-tools"; |
| 29 | +import { createRuntimeCapabilities } from "@intx/types/runtime-capabilities"; |
| 30 | + |
| 31 | +function gmailFetch(input: string | URL | Request, init?: RequestInit) { |
| 32 | + const request = new Request(input, init); |
| 33 | + request.headers.set("Authorization", `Bearer ${process.env.GMAIL_ACCESS_TOKEN}`); |
| 34 | + return fetch(request); |
| 35 | +} |
| 36 | + |
| 37 | +const capabilities = createRuntimeCapabilities({ |
| 38 | + credentials: { resolve: async () => ({ kind: "http", fetch: gmailFetch, dispose() {} }) }, |
| 39 | +}); |
| 40 | +const tools = createGmailTools({ capabilities }); |
| 41 | +const result = await tools.run({ id: "1", name: "gmail_list_labels", arguments: {} }, AbortSignal.timeout(10_000)); |
| 42 | +console.log(result.content); // { data: { labels: [{ id: "INBOX", name: "INBOX", ... }] } } |
| 43 | +await tools.dispose(); |
| 44 | +``` |
18 | 45 |
|
19 | | -```bash |
20 | | -bun install |
21 | | -bun run typecheck |
22 | | -bun test |
23 | | -bun run build |
| 46 | +## Tools |
| 47 | + |
| 48 | +| Tool | Purpose | Approval | |
| 49 | +| --- | --- | --- | |
| 50 | +| `gmail_search_threads` | Search the mailbox and return thread summaries. | No | |
| 51 | +| `gmail_get_thread` | Fetch one thread with selected message detail. | No | |
| 52 | +| `gmail_get_message` | Fetch one message with selected detail. | No | |
| 53 | +| `gmail_list_labels` | List the mailbox labels. | No | |
| 54 | +| `gmail_create_draft` | Create a draft, optionally as a reply. Never sends. | Yes | |
| 55 | +| `gmail_list_drafts` | List drafts with Gmail-query filtering. | No | |
| 56 | +| `gmail_label_message` | Add labels to a message. | Yes | |
| 57 | +| `gmail_unlabel_message` | Remove labels from a message. | Yes | |
| 58 | +| `gmail_label_thread` | Add labels to every message in a thread. | Yes | |
| 59 | +| `gmail_unlabel_thread` | Remove labels from every message in a thread. | Yes | |
| 60 | + |
| 61 | +Tools marked Yes are declared with `approval: "ask"` in the sidecar bundle, so |
| 62 | +Interchange gates each invocation behind approval. |
| 63 | + |
| 64 | +## Using with Interchange |
| 65 | + |
| 66 | +The `./sidecar-bundle` export is the tool bundle Interchange loads. It exports |
| 67 | +`gmail`, a `defineTool` factory that requires `capabilities` on the agent env |
| 68 | +and builds the tools with `createGmailTools`: |
| 69 | + |
| 70 | +```ts |
| 71 | +import { gmail } from "@corbits/google-tools/sidecar-bundle"; |
24 | 72 | ``` |
25 | 73 |
|
26 | | -The tests inject `fetch` implementations and do not call Gmail. |
| 74 | +`package.json` declares the bundle for Interchange's tool-package loader: |
27 | 75 |
|
28 | | -## Live Gmail checks |
| 76 | +- `interchange.tools` points at `./dist/sidecar-bundle.js`. |
| 77 | +- `interchange.credentials` requests the `gmail-api` handle with the |
| 78 | + `https://www.googleapis.com/auth/gmail.modify` scope. The host binds that |
| 79 | + handle to a Google OAuth credential and hands it to the bundle through |
| 80 | + `env.capabilities`. |
29 | 81 |
|
30 | | -Use a dedicated Gmail fixture account and a Desktop OAuth client granted only |
31 | | -`gmail.modify`. The read smoke requires a query that matches exactly one |
32 | | -one-message thread: |
| 82 | +## Gmail client |
33 | 83 |
|
34 | | -```bash |
35 | | -GMAIL_LIVE_TEST=1 \ |
36 | | -GMAIL_LIVE_CLIENT_ID='...' \ |
37 | | -GMAIL_LIVE_CLIENT_SECRET='...' \ |
38 | | -GMAIL_LIVE_FIXTURE_QUERY='subject:(interchange-gmail-e2e-fixture)' \ |
39 | | -bun run test:live |
| 84 | +`createGmailClient` calls the Gmail REST API directly. It takes a `fetchImpl` |
| 85 | +that authenticates each request; the `gmailFetch` wrapper from the quickstart |
| 86 | +works as-is: |
| 87 | + |
| 88 | +```ts |
| 89 | +import { createGmailClient } from "@corbits/google-tools"; |
| 90 | + |
| 91 | +const client = createGmailClient({ fetchImpl: gmailFetch }); |
| 92 | +const { labels } = await client.listLabels(); |
40 | 93 | ``` |
41 | 94 |
|
42 | | -The full live suite has been verified against a dedicated Gmail fixture account. |
43 | | -It exercises every Gmail tool: |
44 | | - |
45 | | -- `gmail_search_threads` finds the one-message fixture thread. |
46 | | -- `gmail_get_thread` and `gmail_get_message` retrieve its metadata. |
47 | | -- `gmail_list_labels` lists the account labels. |
48 | | -- `gmail_create_draft` and `gmail_list_drafts` create and find a disposable |
49 | | - draft, which the suite then deletes. |
50 | | -- `gmail_label_message` and `gmail_unlabel_message` add and remove `STARRED` |
51 | | - on the fixture message. |
52 | | -- `gmail_label_thread` and `gmail_unlabel_thread` add and remove `STARRED` |
53 | | - on every message in the fixture thread. |
54 | | - |
55 | | -Add `GMAIL_LIVE_MUTATION_TEST=1` to run the draft and label checks. The suite |
56 | | -deletes its draft, restores the fixture labels, and never sends email. |
57 | | - |
58 | | -The first run opens Google OAuth and stores the refresh token in the ignored |
59 | | -`.local/gmail-live-token.json` file. Set `GMAIL_LIVE_TOKEN_FILE` when using a |
60 | | -different token file. Credentials and message contents are not logged. |
| 95 | +Inside Interchange, resolve the `gmail-api` handle from `capabilities` and pass |
| 96 | +the credential's `fetch` instead. |
| 97 | + |
| 98 | +## Contributing |
| 99 | + |
| 100 | +See [CONTRIBUTING.md](./CONTRIBUTING.md) for development and live Gmail checks. |
61 | 101 |
|
62 | 102 | ## License |
63 | 103 |
|
|
0 commit comments