inbox.dog 0.1.0

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.
package/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # inbox.dog
2
+
3
+ Gmail OAuth tokens in 3 API calls. No Google Cloud console required.
4
+
5
+ ```
6
+ npm install inbox.dog
7
+ ```
8
+
9
+ ## Quick start
10
+
11
+ ```ts
12
+ import { InboxDog } from "inbox.dog";
13
+
14
+ const dog = new InboxDog();
15
+
16
+ // 1. Create an API key (one-time)
17
+ const key = await dog.createKey("my-app");
18
+ // → { client_id, client_secret, name, credits: 10 }
19
+
20
+ // 2. Redirect user to Gmail consent
21
+ const url = dog.getAuthUrl({
22
+ clientId: key.client_id,
23
+ redirectUri: "http://localhost:3000/callback",
24
+ scope: "email",
25
+ });
26
+ // → redirect user to `url`
27
+
28
+ // 3. Exchange the code from callback for tokens
29
+ const tokens = await dog.exchangeCode(code, key.client_id, key.client_secret);
30
+ // → { access_token, refresh_token, email, expires_in }
31
+
32
+ // Use tokens directly with Gmail API
33
+ const res = await fetch("https://gmail.googleapis.com/gmail/v1/users/me/messages", {
34
+ headers: { Authorization: `Bearer ${tokens.access_token}` },
35
+ });
36
+ ```
37
+
38
+ ## API
39
+
40
+ ### `new InboxDog(options?)`
41
+
42
+ | Option | Type | Default |
43
+ |-----------|------------|-----------------------|
44
+ | `baseUrl` | `string` | `https://inbox.dog` |
45
+ | `fetch` | `Function` | `globalThis.fetch` |
46
+
47
+ ### `createKey(name?): Promise<CreateKeyResponse>`
48
+
49
+ Create a new API key. Returns `{ client_id, client_secret, name, credits }`.
50
+
51
+ ### `getKey(clientId, clientSecret): Promise<KeyInfo>`
52
+
53
+ Get key info. Returns `{ client_id, name, credits, created_at }`.
54
+
55
+ ### `getAuthUrl(opts): string`
56
+
57
+ Build the OAuth authorization URL. No network request.
58
+
59
+ | Param | Type | Required |
60
+ |---------------|----------|----------|
61
+ | `clientId` | `string` | yes |
62
+ | `redirectUri` | `string` | yes |
63
+ | `scope` | `Scope` | no |
64
+ | `state` | `string` | no |
65
+
66
+ Scopes: `"email"` (default, read-only), `"email:read"`, `"email:send"`, `"email:full"`.
67
+
68
+ ### `exchangeCode(code, clientId, clientSecret): Promise<TokenResponse>`
69
+
70
+ Exchange auth code for tokens. Costs 1 credit.
71
+
72
+ Returns `{ access_token, refresh_token, token_type, expires_in, email }`.
73
+
74
+ ### `refreshToken(refreshToken, clientId, clientSecret): Promise<RefreshResponse>`
75
+
76
+ Refresh an expired access token. Free.
77
+
78
+ Returns `{ access_token, token_type, expires_in }`.
79
+
80
+ ### `checkout(clientId, clientSecret, credits?): Promise<CheckoutResponse>`
81
+
82
+ Create a Stripe checkout session. Returns `{ checkout_url, session_id }`.
83
+
84
+ ## Errors
85
+
86
+ All errors throw `InboxDogError` with these properties:
87
+
88
+ | Property | Type | Description |
89
+ |-----------|----------|------------------------|
90
+ | `code` | `string` | Machine-readable code |
91
+ | `status` | `number` | HTTP status code |
92
+ | `message` | `string` | Human-readable message |
93
+ | `action` | `string` | Suggested fix |
94
+ | `docs` | `string` | Link to docs |
95
+
96
+ Error codes: `INVALID_CREDENTIALS` (401), `INSUFFICIENT_CREDITS` (402), `VALIDATION_ERROR` (400), `STATE_NOT_FOUND` (400), `AUTH_CODE_NOT_FOUND` (400), `TOKEN_EXCHANGE_FAILED` (500).
97
+
98
+ ```ts
99
+ try {
100
+ await dog.exchangeCode(code, clientId, clientSecret);
101
+ } catch (e) {
102
+ if (e instanceof InboxDogError && e.code === "INSUFFICIENT_CREDITS") {
103
+ const { checkout_url } = await dog.checkout(clientId, clientSecret);
104
+ // redirect user to checkout_url
105
+ }
106
+ }
107
+ ```
108
+
109
+ ## Links
110
+
111
+ - Docs: https://inbox.dog/docs
112
+ - API Reference: https://inbox.dog/docs/api
113
+ - GitHub: https://github.com/acoyfellow/inbox.dog
114
+ - MCP Server: `npx @inboxdog/mcp-server`
package/dist/index.cjs ADDED
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ InboxDog: () => InboxDog,
24
+ InboxDogError: () => InboxDogError,
25
+ createClient: () => createClient,
26
+ default: () => index_default
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+ var DEFAULT_BASE_URL = "https://inbox.dog";
30
+ var InboxDogError = class extends Error {
31
+ constructor(status, detail) {
32
+ super(detail.message);
33
+ this.name = "InboxDogError";
34
+ this.code = detail.code;
35
+ this.status = status;
36
+ this.action = detail.action;
37
+ this.docs = detail.docs;
38
+ }
39
+ };
40
+ var InboxDog = class {
41
+ constructor(opts = {}) {
42
+ this.baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
43
+ this.fetchFn = opts.fetch ?? globalThis.fetch;
44
+ }
45
+ // ── API Keys ───────────────────────────────────────
46
+ /** Create a new API key. Returns client_id, client_secret, and 10 free credits. */
47
+ async createKey(name) {
48
+ return this.post("/api/keys", { name: name ?? "default" });
49
+ }
50
+ /** Get API key info (credits remaining, creation date). */
51
+ async getKey(clientId, clientSecret) {
52
+ return this.request(`/api/keys/${clientId}`, {
53
+ headers: { "X-Client-Secret": clientSecret }
54
+ });
55
+ }
56
+ // ── OAuth ──────────────────────────────────────────
57
+ /**
58
+ * Build the authorization URL to redirect users to.
59
+ * This is a pure URL builder — no network request.
60
+ */
61
+ getAuthUrl(opts) {
62
+ const url = new URL(`${this.baseUrl}/oauth/authorize`);
63
+ url.searchParams.set("client_id", opts.clientId);
64
+ url.searchParams.set("redirect_uri", opts.redirectUri);
65
+ if (opts.scope) url.searchParams.set("scope", opts.scope);
66
+ if (opts.state) url.searchParams.set("state", opts.state);
67
+ return url.toString();
68
+ }
69
+ /** Exchange an authorization code for access + refresh tokens. Costs 1 credit. */
70
+ async exchangeCode(code, clientId, clientSecret) {
71
+ return this.post("/oauth/token", {
72
+ code,
73
+ client_id: clientId,
74
+ client_secret: clientSecret
75
+ });
76
+ }
77
+ /** Refresh an expired access token. Free, no credit cost. */
78
+ async refreshToken(refreshToken, clientId, clientSecret) {
79
+ return this.post("/oauth/token", {
80
+ grant_type: "refresh_token",
81
+ refresh_token: refreshToken,
82
+ client_id: clientId,
83
+ client_secret: clientSecret
84
+ });
85
+ }
86
+ // ── Billing ────────────────────────────────────────
87
+ /** Create a Stripe checkout session to purchase credits. */
88
+ async checkout(clientId, clientSecret, credits) {
89
+ return this.post("/api/checkout", {
90
+ client_id: clientId,
91
+ client_secret: clientSecret,
92
+ credits: credits ?? 100
93
+ });
94
+ }
95
+ // ── Internal ───────────────────────────────────────
96
+ async request(path, init = {}) {
97
+ const res = await this.fetchFn(`${this.baseUrl}${path}`, {
98
+ ...init,
99
+ headers: {
100
+ "Content-Type": "application/json",
101
+ ...init.headers
102
+ }
103
+ });
104
+ const body = await res.json();
105
+ if (!res.ok) {
106
+ const err = body;
107
+ throw new InboxDogError(res.status, err.error ?? {
108
+ code: "UNKNOWN",
109
+ message: `HTTP ${res.status}`
110
+ });
111
+ }
112
+ return body;
113
+ }
114
+ async post(path, data) {
115
+ return this.request(path, {
116
+ method: "POST",
117
+ body: JSON.stringify(data)
118
+ });
119
+ }
120
+ };
121
+ function createClient(opts) {
122
+ return new InboxDog(opts);
123
+ }
124
+ var index_default = InboxDog;
125
+ // Annotate the CommonJS export names for ESM import in node:
126
+ 0 && (module.exports = {
127
+ InboxDog,
128
+ InboxDogError,
129
+ createClient
130
+ });
@@ -0,0 +1,80 @@
1
+ /** inbox.dog — Gmail OAuth tokens in 3 API calls */
2
+ type Scope = "email" | "email:read" | "email:send" | "email:full";
3
+ interface CreateKeyResponse {
4
+ client_id: string;
5
+ client_secret: string;
6
+ name: string;
7
+ credits: number;
8
+ }
9
+ interface KeyInfo {
10
+ client_id: string;
11
+ name: string;
12
+ credits: number;
13
+ created_at: number;
14
+ }
15
+ interface TokenResponse {
16
+ access_token: string;
17
+ refresh_token: string;
18
+ token_type: "Bearer";
19
+ expires_in: number;
20
+ email: string;
21
+ }
22
+ interface RefreshResponse {
23
+ access_token: string;
24
+ token_type: "Bearer";
25
+ expires_in: number;
26
+ }
27
+ interface CheckoutResponse {
28
+ checkout_url: string;
29
+ session_id: string;
30
+ }
31
+ interface InboxDogErrorDetail {
32
+ code: string;
33
+ message: string;
34
+ action?: string;
35
+ docs?: string;
36
+ }
37
+ declare class InboxDogError extends Error {
38
+ readonly code: string;
39
+ readonly status: number;
40
+ readonly action?: string;
41
+ readonly docs?: string;
42
+ constructor(status: number, detail: InboxDogErrorDetail);
43
+ }
44
+ interface InboxDogOptions {
45
+ /** Override the base URL (default: https://inbox.dog) */
46
+ baseUrl?: string;
47
+ /** Provide a custom fetch implementation */
48
+ fetch?: typeof globalThis.fetch;
49
+ }
50
+ declare class InboxDog {
51
+ private baseUrl;
52
+ private fetchFn;
53
+ constructor(opts?: InboxDogOptions);
54
+ /** Create a new API key. Returns client_id, client_secret, and 10 free credits. */
55
+ createKey(name?: string): Promise<CreateKeyResponse>;
56
+ /** Get API key info (credits remaining, creation date). */
57
+ getKey(clientId: string, clientSecret: string): Promise<KeyInfo>;
58
+ /**
59
+ * Build the authorization URL to redirect users to.
60
+ * This is a pure URL builder — no network request.
61
+ */
62
+ getAuthUrl(opts: {
63
+ clientId: string;
64
+ redirectUri: string;
65
+ scope?: Scope;
66
+ state?: string;
67
+ }): string;
68
+ /** Exchange an authorization code for access + refresh tokens. Costs 1 credit. */
69
+ exchangeCode(code: string, clientId: string, clientSecret: string): Promise<TokenResponse>;
70
+ /** Refresh an expired access token. Free, no credit cost. */
71
+ refreshToken(refreshToken: string, clientId: string, clientSecret: string): Promise<RefreshResponse>;
72
+ /** Create a Stripe checkout session to purchase credits. */
73
+ checkout(clientId: string, clientSecret: string, credits?: number): Promise<CheckoutResponse>;
74
+ private request;
75
+ private post;
76
+ }
77
+ /** Create an InboxDog client with default options. */
78
+ declare function createClient(opts?: InboxDogOptions): InboxDog;
79
+
80
+ export { type CheckoutResponse, type CreateKeyResponse, InboxDog, InboxDogError, type InboxDogErrorDetail, type InboxDogOptions, type KeyInfo, type RefreshResponse, type Scope, type TokenResponse, createClient, InboxDog as default };
@@ -0,0 +1,80 @@
1
+ /** inbox.dog — Gmail OAuth tokens in 3 API calls */
2
+ type Scope = "email" | "email:read" | "email:send" | "email:full";
3
+ interface CreateKeyResponse {
4
+ client_id: string;
5
+ client_secret: string;
6
+ name: string;
7
+ credits: number;
8
+ }
9
+ interface KeyInfo {
10
+ client_id: string;
11
+ name: string;
12
+ credits: number;
13
+ created_at: number;
14
+ }
15
+ interface TokenResponse {
16
+ access_token: string;
17
+ refresh_token: string;
18
+ token_type: "Bearer";
19
+ expires_in: number;
20
+ email: string;
21
+ }
22
+ interface RefreshResponse {
23
+ access_token: string;
24
+ token_type: "Bearer";
25
+ expires_in: number;
26
+ }
27
+ interface CheckoutResponse {
28
+ checkout_url: string;
29
+ session_id: string;
30
+ }
31
+ interface InboxDogErrorDetail {
32
+ code: string;
33
+ message: string;
34
+ action?: string;
35
+ docs?: string;
36
+ }
37
+ declare class InboxDogError extends Error {
38
+ readonly code: string;
39
+ readonly status: number;
40
+ readonly action?: string;
41
+ readonly docs?: string;
42
+ constructor(status: number, detail: InboxDogErrorDetail);
43
+ }
44
+ interface InboxDogOptions {
45
+ /** Override the base URL (default: https://inbox.dog) */
46
+ baseUrl?: string;
47
+ /** Provide a custom fetch implementation */
48
+ fetch?: typeof globalThis.fetch;
49
+ }
50
+ declare class InboxDog {
51
+ private baseUrl;
52
+ private fetchFn;
53
+ constructor(opts?: InboxDogOptions);
54
+ /** Create a new API key. Returns client_id, client_secret, and 10 free credits. */
55
+ createKey(name?: string): Promise<CreateKeyResponse>;
56
+ /** Get API key info (credits remaining, creation date). */
57
+ getKey(clientId: string, clientSecret: string): Promise<KeyInfo>;
58
+ /**
59
+ * Build the authorization URL to redirect users to.
60
+ * This is a pure URL builder — no network request.
61
+ */
62
+ getAuthUrl(opts: {
63
+ clientId: string;
64
+ redirectUri: string;
65
+ scope?: Scope;
66
+ state?: string;
67
+ }): string;
68
+ /** Exchange an authorization code for access + refresh tokens. Costs 1 credit. */
69
+ exchangeCode(code: string, clientId: string, clientSecret: string): Promise<TokenResponse>;
70
+ /** Refresh an expired access token. Free, no credit cost. */
71
+ refreshToken(refreshToken: string, clientId: string, clientSecret: string): Promise<RefreshResponse>;
72
+ /** Create a Stripe checkout session to purchase credits. */
73
+ checkout(clientId: string, clientSecret: string, credits?: number): Promise<CheckoutResponse>;
74
+ private request;
75
+ private post;
76
+ }
77
+ /** Create an InboxDog client with default options. */
78
+ declare function createClient(opts?: InboxDogOptions): InboxDog;
79
+
80
+ export { type CheckoutResponse, type CreateKeyResponse, InboxDog, InboxDogError, type InboxDogErrorDetail, type InboxDogOptions, type KeyInfo, type RefreshResponse, type Scope, type TokenResponse, createClient, InboxDog as default };
package/dist/index.js ADDED
@@ -0,0 +1,103 @@
1
+ // src/index.ts
2
+ var DEFAULT_BASE_URL = "https://inbox.dog";
3
+ var InboxDogError = class extends Error {
4
+ constructor(status, detail) {
5
+ super(detail.message);
6
+ this.name = "InboxDogError";
7
+ this.code = detail.code;
8
+ this.status = status;
9
+ this.action = detail.action;
10
+ this.docs = detail.docs;
11
+ }
12
+ };
13
+ var InboxDog = class {
14
+ constructor(opts = {}) {
15
+ this.baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
16
+ this.fetchFn = opts.fetch ?? globalThis.fetch;
17
+ }
18
+ // ── API Keys ───────────────────────────────────────
19
+ /** Create a new API key. Returns client_id, client_secret, and 10 free credits. */
20
+ async createKey(name) {
21
+ return this.post("/api/keys", { name: name ?? "default" });
22
+ }
23
+ /** Get API key info (credits remaining, creation date). */
24
+ async getKey(clientId, clientSecret) {
25
+ return this.request(`/api/keys/${clientId}`, {
26
+ headers: { "X-Client-Secret": clientSecret }
27
+ });
28
+ }
29
+ // ── OAuth ──────────────────────────────────────────
30
+ /**
31
+ * Build the authorization URL to redirect users to.
32
+ * This is a pure URL builder — no network request.
33
+ */
34
+ getAuthUrl(opts) {
35
+ const url = new URL(`${this.baseUrl}/oauth/authorize`);
36
+ url.searchParams.set("client_id", opts.clientId);
37
+ url.searchParams.set("redirect_uri", opts.redirectUri);
38
+ if (opts.scope) url.searchParams.set("scope", opts.scope);
39
+ if (opts.state) url.searchParams.set("state", opts.state);
40
+ return url.toString();
41
+ }
42
+ /** Exchange an authorization code for access + refresh tokens. Costs 1 credit. */
43
+ async exchangeCode(code, clientId, clientSecret) {
44
+ return this.post("/oauth/token", {
45
+ code,
46
+ client_id: clientId,
47
+ client_secret: clientSecret
48
+ });
49
+ }
50
+ /** Refresh an expired access token. Free, no credit cost. */
51
+ async refreshToken(refreshToken, clientId, clientSecret) {
52
+ return this.post("/oauth/token", {
53
+ grant_type: "refresh_token",
54
+ refresh_token: refreshToken,
55
+ client_id: clientId,
56
+ client_secret: clientSecret
57
+ });
58
+ }
59
+ // ── Billing ────────────────────────────────────────
60
+ /** Create a Stripe checkout session to purchase credits. */
61
+ async checkout(clientId, clientSecret, credits) {
62
+ return this.post("/api/checkout", {
63
+ client_id: clientId,
64
+ client_secret: clientSecret,
65
+ credits: credits ?? 100
66
+ });
67
+ }
68
+ // ── Internal ───────────────────────────────────────
69
+ async request(path, init = {}) {
70
+ const res = await this.fetchFn(`${this.baseUrl}${path}`, {
71
+ ...init,
72
+ headers: {
73
+ "Content-Type": "application/json",
74
+ ...init.headers
75
+ }
76
+ });
77
+ const body = await res.json();
78
+ if (!res.ok) {
79
+ const err = body;
80
+ throw new InboxDogError(res.status, err.error ?? {
81
+ code: "UNKNOWN",
82
+ message: `HTTP ${res.status}`
83
+ });
84
+ }
85
+ return body;
86
+ }
87
+ async post(path, data) {
88
+ return this.request(path, {
89
+ method: "POST",
90
+ body: JSON.stringify(data)
91
+ });
92
+ }
93
+ };
94
+ function createClient(opts) {
95
+ return new InboxDog(opts);
96
+ }
97
+ var index_default = InboxDog;
98
+ export {
99
+ InboxDog,
100
+ InboxDogError,
101
+ createClient,
102
+ index_default as default
103
+ };
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "inbox.dog",
3
+ "version": "0.1.0",
4
+ "description": "Gmail OAuth tokens in 3 API calls. No Google Cloud console required.",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsup",
26
+ "test": "echo 'No tests yet'",
27
+ "prepublishOnly": "npm run build"
28
+ },
29
+ "keywords": [
30
+ "gmail",
31
+ "oauth",
32
+ "email",
33
+ "google",
34
+ "oauth2",
35
+ "api",
36
+ "tokens"
37
+ ],
38
+ "author": "acoyfellow",
39
+ "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/acoyfellow/inbox.dog",
43
+ "directory": "package"
44
+ },
45
+ "homepage": "https://inbox.dog",
46
+ "devDependencies": {
47
+ "tsup": "^8.0.0",
48
+ "typescript": "^5.5.0"
49
+ }
50
+ }