@atomicmail/mcp 0.1.0 → 0.2.1

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.
Files changed (80) hide show
  1. package/README.md +77 -187
  2. package/esm/_dnt.polyfills.d.ts +101 -0
  3. package/esm/_dnt.polyfills.d.ts.map +1 -0
  4. package/esm/_dnt.polyfills.js +127 -0
  5. package/esm/lib/agent/auth/agent-auth-http.d.ts +26 -0
  6. package/esm/lib/agent/auth/agent-auth-http.d.ts.map +1 -0
  7. package/esm/lib/agent/auth/agent-auth-http.js +76 -0
  8. package/esm/lib/agent/auth/agent-jwt.d.ts +14 -0
  9. package/esm/lib/agent/auth/agent-jwt.d.ts.map +1 -0
  10. package/esm/lib/agent/auth/agent-jwt.js +29 -0
  11. package/esm/lib/agent/auth/agent-pow.d.ts +5 -0
  12. package/esm/lib/agent/auth/agent-pow.d.ts.map +1 -0
  13. package/esm/lib/agent/auth/agent-pow.js +49 -0
  14. package/esm/lib/agent/jmap/agent-help-content.d.ts +4 -0
  15. package/esm/lib/agent/jmap/agent-help-content.d.ts.map +1 -0
  16. package/esm/lib/agent/jmap/agent-help-content.js +244 -0
  17. package/esm/lib/agent/jmap/agent-jmap.d.ts +49 -0
  18. package/esm/lib/agent/jmap/agent-jmap.d.ts.map +1 -0
  19. package/esm/lib/agent/jmap/agent-jmap.js +174 -0
  20. package/esm/lib/agent/jmap/agent-vars.d.ts +23 -0
  21. package/esm/lib/agent/jmap/agent-vars.d.ts.map +1 -0
  22. package/esm/lib/agent/jmap/agent-vars.js +65 -0
  23. package/esm/{mcp/src/credentials.d.ts → lib/agent/session/agent-credentials-store.d.ts} +3 -2
  24. package/esm/lib/agent/session/agent-credentials-store.d.ts.map +1 -0
  25. package/esm/{mcp/src/credentials.js → lib/agent/session/agent-credentials-store.js} +19 -16
  26. package/esm/lib/agent/session/agent-resolve-config.d.ts +24 -0
  27. package/esm/lib/agent/session/agent-resolve-config.d.ts.map +1 -0
  28. package/esm/lib/agent/session/agent-resolve-config.js +70 -0
  29. package/esm/lib/agent/session/agent-session.d.ts +62 -0
  30. package/esm/lib/agent/session/agent-session.d.ts.map +1 -0
  31. package/esm/lib/agent/session/agent-session.js +206 -0
  32. package/esm/lib/core/consts.d.ts.map +1 -0
  33. package/esm/lib/core/types.d.ts +2 -0
  34. package/esm/lib/core/types.d.ts.map +1 -0
  35. package/esm/lib/core/types.js +1 -0
  36. package/esm/lib/core/utils.d.ts +10 -0
  37. package/esm/lib/core/utils.d.ts.map +1 -0
  38. package/esm/lib/core/utils.js +28 -0
  39. package/esm/lib/mod.d.ts +14 -0
  40. package/esm/lib/mod.d.ts.map +1 -0
  41. package/esm/lib/mod.js +13 -0
  42. package/esm/lib/network/auth-client.d.ts +57 -0
  43. package/esm/lib/network/auth-client.d.ts.map +1 -0
  44. package/esm/lib/network/auth-client.js +188 -0
  45. package/esm/mcp/main.d.ts +3 -0
  46. package/esm/mcp/main.d.ts.map +1 -0
  47. package/esm/mcp/main.js +86 -0
  48. package/esm/mcp/tools/help.d.ts +3 -0
  49. package/esm/mcp/tools/help.d.ts.map +1 -0
  50. package/esm/mcp/tools/help.js +22 -0
  51. package/esm/mcp/{src/tools → tools}/jmap.d.ts +2 -2
  52. package/esm/mcp/tools/jmap.d.ts.map +1 -0
  53. package/esm/mcp/tools/jmap.js +115 -0
  54. package/esm/mcp/{src/tools → tools}/register.d.ts +2 -2
  55. package/esm/mcp/tools/register.d.ts.map +1 -0
  56. package/esm/mcp/tools/register.js +43 -0
  57. package/package.json +5 -5
  58. package/presets/list_inbox.json +39 -0
  59. package/presets/reply.json +75 -0
  60. package/presets/send_mail.json +42 -0
  61. package/esm/lib/src/consts.d.ts.map +0 -1
  62. package/esm/mcp/src/auth-session.d.ts +0 -88
  63. package/esm/mcp/src/auth-session.d.ts.map +0 -1
  64. package/esm/mcp/src/auth-session.js +0 -378
  65. package/esm/mcp/src/credentials.d.ts.map +0 -1
  66. package/esm/mcp/src/docs-content.d.ts +0 -4
  67. package/esm/mcp/src/docs-content.d.ts.map +0 -1
  68. package/esm/mcp/src/docs-content.js +0 -405
  69. package/esm/mcp/src/main.d.ts +0 -3
  70. package/esm/mcp/src/main.d.ts.map +0 -1
  71. package/esm/mcp/src/main.js +0 -116
  72. package/esm/mcp/src/tools/docs.d.ts +0 -3
  73. package/esm/mcp/src/tools/docs.d.ts.map +0 -1
  74. package/esm/mcp/src/tools/docs.js +0 -22
  75. package/esm/mcp/src/tools/jmap.d.ts.map +0 -1
  76. package/esm/mcp/src/tools/jmap.js +0 -202
  77. package/esm/mcp/src/tools/register.d.ts.map +0 -1
  78. package/esm/mcp/src/tools/register.js +0 -79
  79. /package/esm/lib/{src → core}/consts.d.ts +0 -0
  80. /package/esm/lib/{src → core}/consts.js +0 -0
package/README.md CHANGED
@@ -1,128 +1,108 @@
1
1
  # @atomicmail/mcp
2
2
 
3
- AtomicMail MCP server — a local stdio Model Context Protocol server that gives
3
+ Atomic Mail MCP server — a local stdio Model Context Protocol server that gives
4
4
  an AI agent a programmable email inbox over JMAP, with automatic Proof-of-Work
5
5
  auth and capability-token rotation.
6
6
 
7
- The server is authored in TypeScript on Deno and built with
8
- [`dnt`](https://jsr.io/@deno/dnt) into a Node-compatible npm package, so the
9
- **same source** runs unchanged on Deno, Node, and Bun.
7
+ ## Install
10
8
 
11
- It is the MCP companion to [`@atomic-mail/agent-skill`](../../skill/) — a
12
- CLI-first skill with identical credential semantics. The MCP and the skill share
13
- the same on-disk credential layout (`credentials.json` + `session.jwt` +
14
- `capability.jwt`), so you can mix and match the two freely.
9
+ ```json
10
+ // mcp.json
11
+
12
+ {
13
+ "mcpServers": {
14
+ "atomicmail": {
15
+ "command": "npx",
16
+ "args": ["-y", "@atomicmail/mcp"]
17
+ }
18
+ }
19
+ }
20
+ ```
21
+
22
+ Your MCP host spawns this process; see configuration below.
15
23
 
16
24
  ## Tools exposed
17
25
 
18
- | Tool | Description |
19
- | -------------- | ----------------------------------------------------------------- |
20
- | `register` | PoW signup. Persists credentials to disk; returns the API key. |
21
- | `jmap_session` | `GET /.well-known/jmap` discover accountId + mailbox URLs. |
22
- | `jmap_request` | Send any JMAP method-call batch. Inline or via saved preset file. |
23
- | `get_docs` | Built-in docs. Topics include `installation`, `presets`, `auth`. |
26
+ | Tool | Description |
27
+ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
28
+ | `register` | PoW signup; persists credentials. Idempotent when username matches inbox. |
29
+ | `jmap_request` | JMAP batch via `ops` or `ops_file`. Uppercase `$VAR_NAME` tokens are substituted (`$ACCOUNT_ID` / `$INBOX` from session; others via optional `vars` map). |
30
+ | `help` | Built-in docs (`topic` optional). |
24
31
 
25
- ## Install
32
+ ## Typical MCP workflow
26
33
 
27
- The package is published to npm. Any of these work:
34
+ 1. Call `register` with a username (or rely on existing `credentials.json`).
35
+ 2. `jmap_request` with `ops` or `ops_file` (optional `vars` for `$TO`,
36
+ `$SUBJECT`, etc.).
37
+ 3. `help` when stuck.
28
38
 
29
- ```bash
30
- npx -y @atomicmail/mcp
31
- bunx @atomicmail/mcp
32
- deno run -A npm:@atomicmail/mcp/atomicmail-mcp
33
- ```
39
+ ## `jmap_request` input patterns
34
40
 
35
- You don't typically run it directly — your MCP host (Cursor, Claude Desktop,
36
- ...) will spawn it for you. See the configuration section below.
41
+ `jmap_request` accepts either:
37
42
 
38
- ## Configure (priority order)
43
+ - inline `ops` (JMAP methodCalls array), or
44
+ - `ops_file` (JSON file path)
39
45
 
40
- The server resolves configuration from two sources, with environment variables
41
- overriding individual fields from the file:
46
+ When using `ops_file`, relative paths first resolve against the credential
47
+ directory. If a file is not present there, the runtime falls back to bundled
48
+ presets shipped in the npm package.
42
49
 
43
- 1. **`credentials.json` in the credential directory.** The same file produced by
44
- `atomic-mail-signup`. Default location: `~/.atomicmail/credentials.json`.
45
- Override the directory with `ATOMIC_MAIL_CREDENTIALS_DIR`.
46
- 2. **Environment variables.** Useful for hermetic MCP host configs:
50
+ ## Presets and placeholders
47
51
 
48
- | Variable | Required | Description |
49
- | ----------------------------- | -------- | --------------------------------- |
50
- | `ATOMIC_MAIL_AUTH_URL` | Yes\* | Auth service base URL |
51
- | `ATOMIC_MAIL_API_URL` | Yes\* | API service / JMAP base URL |
52
- | `ATOMIC_MAIL_SCRYPT_SALT` | No | Optional PoW salt override |
53
- | `ATOMIC_MAIL_API_KEY` | No | Existing API key (skips signup) |
54
- | `ATOMIC_MAIL_CREDENTIALS_DIR` | No | Override default credential dir |
52
+ Presets are reusable JSON files for `jmap_request` batches:
55
53
 
56
- \* "Yes" means at least one source must provide `authUrl` and `apiUrl`. If
57
- `credentials.json` is present and complete, no env vars are required. PoW
58
- salt defaults to the built-in value that matches `auth-service`.
54
+ - Inline JSON: `{"ops":[...],"vars":{"COUNT":"10"}}`
55
+ - Preset file: `{"ops_file":"list_inbox.json","vars":{"COUNT":"10"}}`
59
56
 
60
- If neither source resolves `authUrl` and `apiUrl`, the server fails fast on
61
- startup with the missing fields listed.
57
+ Resolution order for `ops_file`:
62
58
 
63
- ## Credential files
59
+ 1. Resolve relative to credentials directory (`~/.atomicmail` by default).
60
+ 2. If missing, fall back to bundled presets in the npm package.
64
61
 
65
- All three live in the credential directory and are written with mode `0600`:
62
+ Placeholder rules:
66
63
 
67
- - `credentials.json` `{ apiKey, inboxId, authUrl, apiUrl, scryptSalt }`.
68
- Long-lived. Treat as a secret — copying it to a new machine is enough to log
69
- in.
70
- - `session.jwt` 4-hour TTL, rotated automatically via PoW when near expiry.
71
- - `capability.jwt` 2-minute TTL, rotated automatically before each JMAP
72
- request when near expiry.
64
+ - Pattern: `$VAR_NAME`, where `VAR_NAME` matches `^[A-Z][A-Z0-9_]*$`.
65
+ - Built-ins: `$ACCOUNT_ID`, `$INBOX`.
66
+ - Lowercase `$tokens` such as JMAP back-references (`$draft`) are not matched.
67
+ - Custom placeholders: passed in `vars` as string values.
68
+ - Resolution order per variable: `vars` first, then built-in auto-resolvers.
69
+ - Built-ins can be overridden by providing `ACCOUNT_ID` or `INBOX` in `vars`.
70
+ - If any referenced variable is unresolved, `jmap_request` fails with a missing
71
+ variables error.
72
+ - Substitution is single-pass: inserted values are not scanned again for nested
73
+ `$VAR_NAME` tokens.
73
74
 
74
- Because the on-disk layout is identical to the
75
- [`@atomic-mail/agent-skill`](../../skill/) skill, you can:
75
+ Bundled presets:
76
76
 
77
- - Bootstrap once with `atomic-mail-signup`, then point the MCP at the same
78
- directory.
79
- - Run `atomic-mail-jmap` from a shell while the MCP is also running — both will
80
- see fresh JWTs as the other rotates them.
77
+ - `send_mail.json` (`$TO`, `$SUBJECT`, `$BODY`)
78
+ - `list_inbox.json` (`$COUNT`)
79
+ - `reply.json` (`$MAIL_ID`, `$BODY`)
81
80
 
82
- ## MCP host configuration examples
81
+ `ops-file` resolves against credentials directory first, then bundled presets
82
+ inside the package.
83
83
 
84
- ### Cursor
84
+ Example:
85
85
 
86
- `~/.cursor/mcp.json` (or per-project `.cursor/mcp.json`):
86
+ `{ "ops_file": "list_inbox.json", "vars": { "COUNT": "10" } }`
87
87
 
88
- ```json
89
- {
90
- "mcpServers": {
91
- "atomicmail": {
92
- "command": "npx",
93
- "args": ["-y", "@atomicmail/mcp"],
94
- "env": {
95
- "ATOMIC_MAIL_AUTH_URL": "https://auth.atomicmail.io",
96
- "ATOMIC_MAIL_API_URL": "https://api.atomicmail.io"
97
- }
98
- }
99
- }
100
- }
101
- ```
88
+ ## Credential files and token lifecycle
102
89
 
103
- ### Claude Desktop
90
+ Mode `0600`:
104
91
 
105
- `claude_desktop_config.json`:
92
+ - `credentials.json` — `{ apiKey, inboxId, authUrl, apiUrl, scryptSalt }`
93
+ - `session.jwt` — 4h TTL, rotated via PoW
94
+ - `capability.jwt` — 2m TTL, rotated before JMAP calls
106
95
 
107
- ```json
108
- {
109
- "mcpServers": {
110
- "atomicmail": {
111
- "command": "npx",
112
- "args": ["-y", "@atomicmail/mcp"],
113
- "env": {
114
- "ATOMIC_MAIL_AUTH_URL": "https://auth.atomicmail.io",
115
- "ATOMIC_MAIL_API_URL": "https://api.atomicmail.io"
116
- }
117
- }
118
- }
119
- }
120
- ```
96
+ These files are created and rotated automatically by MCP tool calls. AgentSkill
97
+ CLI uses the same files.
98
+
99
+ ## Defaults
121
100
 
122
- ### Sharing credentials with the skill CLI
101
+ - auth endpoint: `https://auth.atomicmail.ai`
102
+ - api endpoint: `https://api.atomicmail.ai`
103
+ - credentials directory: `~/.atomicmail`
123
104
 
124
- If you've already run `atomic-mail-signup`, no auth-service URL needs to be
125
- repeated — the MCP reads URLs (and optional salt override) from `credentials.json`:
105
+ ## Overriding defaults
126
106
 
127
107
  ```json
128
108
  {
@@ -131,103 +111,13 @@ repeated — the MCP reads URLs (and optional salt override) from `credentials.j
131
111
  "command": "npx",
132
112
  "args": ["-y", "@atomicmail/mcp"],
133
113
  "env": {
134
- "ATOMIC_MAIL_CREDENTIALS_DIR": "/Users/me/.atomicmail"
114
+ "ATOMIC_MAIL_AUTH_URL": "https://custom-auth.example",
115
+ "ATOMIC_MAIL_API_URL": "https://custom-api.example",
116
+ "ATOMIC_MAIL_CREDENTIALS_DIR": "/Users/me/.atomicmail",
117
+ "ATOMIC_MAIL_SCRYPT_SALT": "hex-salt-override",
118
+ "ATOMIC_MAIL_API_KEY": "existing-api-key"
135
119
  }
136
120
  }
137
121
  }
138
122
  }
139
123
  ```
140
-
141
- When you skip the env entirely (`{}`), the MCP defaults to `~/.atomicmail/`.
142
-
143
- ## Typical agent workflow
144
-
145
- Once the MCP is wired in, an agent will:
146
-
147
- 1. Call `register` (only on the first run, with no existing API key).
148
- 2. Call `jmap_session` to discover the `accountId` and the INBOX id.
149
- 3. Call `jmap_request` with whatever method calls it needs (inline) or reference
150
- saved presets via `opsFile`.
151
- 4. Call `get_docs` with topic `presets` or `jmap_cheatsheet` for ready-to-use
152
- JMAP recipes.
153
-
154
- ## JMAP presets (the `opsFile` parameter)
155
-
156
- `jmap_request` accepts either an inline `methodCalls` array or an `opsFile`
157
- path. Relative paths resolve against the credential directory, so common batches
158
- can be saved once and reused. This mirrors the skill's
159
- `atomic-mail-jmap --ops-file` behavior — both consume the same files.
160
-
161
- Example: drop `~/.atomicmail/fetch_last_100.json` containing
162
-
163
- ```json
164
- [
165
- [
166
- "Email/query",
167
- {
168
- "accountId": "ACC",
169
- "filter": { "inMailbox": "INBOX" },
170
- "sort": [{ "property": "receivedAt", "isAscending": false }],
171
- "limit": 100
172
- },
173
- "q0"
174
- ]
175
- ]
176
- ```
177
-
178
- …then `jmap_request` with `{ "opsFile": "fetch_last_100.json" }`.
179
-
180
- See `get_docs presets` for the full preset format.
181
-
182
- ## Develop
183
-
184
- The project is a Deno-first codebase. You'll need [Deno](https://deno.com/)
185
- installed.
186
-
187
- ```bash
188
- # Run directly with Deno (live source, no build step)
189
- deno task start
190
-
191
- # Type-check
192
- deno task check
193
-
194
- # Format
195
- deno task fmt
196
-
197
- # Build the npm package via dnt
198
- deno task build:npm
199
-
200
- # Run the built artifact under Node
201
- node npm/esm/main.js
202
- ```
203
-
204
- The dnt build emits to `./npm/`. The package's `bin` is `atomicmail-mcp`, mapped
205
- to `npm/esm/main.js`. After a build you can also `npm install` it locally and
206
- use the binary directly.
207
-
208
- ### Project layout
209
-
210
- ```
211
- services/mcp-server-local/
212
- ├── deno.json # Deno config + import map
213
- ├── build_npm.ts # dnt build script
214
- ├── README.md
215
- ├── src/
216
- │ ├── main.ts # MCP server entry (stdio transport)
217
- │ ├── auth-session.ts# PoW + JWT rotation, with disk persistence
218
- │ ├── credentials.ts # File I/O: credentials.json, *.jwt
219
- │ ├── docs-content.ts# Static text served by the get_docs tool
220
- │ └── tools/
221
- │ ├── register.ts
222
- │ ├── jmap.ts
223
- │ └── docs.ts
224
- └── npm/ # dnt output (gitignored)
225
- ```
226
-
227
- The auth-session and credentials files are kept in-step with their counterparts
228
- in [`skill/scripts/lib/`](../../skill/scripts/lib/) so both projects produce and
229
- consume identical credential files.
230
-
231
- ## License
232
-
233
- MIT
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Based on [import-meta-ponyfill](https://github.com/gaubee/import-meta-ponyfill),
3
+ * but instead of using npm to install additional dependencies,
4
+ * this approach manually consolidates cjs/mjs/d.ts into a single file.
5
+ *
6
+ * Note that this code might be imported multiple times
7
+ * (for example, both dnt.test.polyfills.ts and dnt.polyfills.ts contain this code;
8
+ * or Node.js might dynamically clear the cache and then force a require).
9
+ * Therefore, it's important to avoid redundant writes to global objects.
10
+ * Additionally, consider that commonjs is used alongside esm,
11
+ * so the two ponyfill functions are stored independently in two separate global objects.
12
+ */
13
+ import { createRequire } from "node:module";
14
+ import { type URL } from "node:url";
15
+ declare global {
16
+ interface ImportMeta {
17
+ /** A string representation of the fully qualified module URL. When the
18
+ * module is loaded locally, the value will be a file URL (e.g.
19
+ * `file:///path/module.ts`).
20
+ *
21
+ * You can also parse the string as a URL to determine more information about
22
+ * how the current module was loaded. For example to determine if a module was
23
+ * local or not:
24
+ *
25
+ * ```ts
26
+ * const url = new URL(import.meta.url);
27
+ * if (url.protocol === "file:") {
28
+ * console.log("this module was loaded locally");
29
+ * }
30
+ * ```
31
+ */
32
+ url: string;
33
+ /**
34
+ * A function that returns resolved specifier as if it would be imported
35
+ * using `import(specifier)`.
36
+ *
37
+ * ```ts
38
+ * console.log(import.meta.resolve("./foo.js"));
39
+ * // file:///dev/foo.js
40
+ * ```
41
+ *
42
+ * @param specifier The module specifier to resolve relative to `parent`.
43
+ * @param parent The absolute parent module URL to resolve from.
44
+ * @returns The absolute (`file:`) URL string for the resolved module.
45
+ */
46
+ resolve(specifier: string, parent?: string | URL | undefined): string;
47
+ /** A flag that indicates if the current module is the main module that was
48
+ * called when starting the program under Deno.
49
+ *
50
+ * ```ts
51
+ * if (import.meta.main) {
52
+ * // this was loaded as the main module, maybe do some bootstrapping
53
+ * }
54
+ * ```
55
+ */
56
+ main: boolean;
57
+ /** The absolute path of the current module.
58
+ *
59
+ * This property is only provided for local modules (ie. using `file://` URLs).
60
+ *
61
+ * Example:
62
+ * ```
63
+ * // Unix
64
+ * console.log(import.meta.filename); // /home/alice/my_module.ts
65
+ *
66
+ * // Windows
67
+ * console.log(import.meta.filename); // C:\alice\my_module.ts
68
+ * ```
69
+ */
70
+ filename: string;
71
+ /** The absolute path of the directory containing the current module.
72
+ *
73
+ * This property is only provided for local modules (ie. using `file://` URLs).
74
+ *
75
+ * * Example:
76
+ * ```
77
+ * // Unix
78
+ * console.log(import.meta.dirname); // /home/alice
79
+ *
80
+ * // Windows
81
+ * console.log(import.meta.dirname); // C:\alice
82
+ * ```
83
+ */
84
+ dirname: string;
85
+ }
86
+ }
87
+ type NodeRequest = ReturnType<typeof createRequire>;
88
+ type NodeModule = NonNullable<NodeRequest["main"]>;
89
+ interface ImportMetaPonyfillCommonjs {
90
+ (require: NodeRequest, module: NodeModule): ImportMeta;
91
+ }
92
+ interface ImportMetaPonyfillEsmodule {
93
+ (importMeta: ImportMeta): ImportMeta;
94
+ }
95
+ interface ImportMetaPonyfill extends ImportMetaPonyfillCommonjs, ImportMetaPonyfillEsmodule {
96
+ }
97
+ export declare let import_meta_ponyfill_commonjs: ImportMetaPonyfillCommonjs;
98
+ export declare let import_meta_ponyfill_esmodule: ImportMetaPonyfillEsmodule;
99
+ export declare let import_meta_ponyfill: ImportMetaPonyfill;
100
+ export {};
101
+ //# sourceMappingURL=_dnt.polyfills.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_dnt.polyfills.d.ts","sourceRoot":"","sources":["../src/_dnt.polyfills.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAgC,KAAK,GAAG,EAAE,MAAM,UAAU,CAAC;AAGlE,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,UAAU;QAClB;;;;;;;;;;;;;;WAcG;QACH,GAAG,EAAE,MAAM,CAAC;QACZ;;;;;;;;;;;;WAYG;QACH,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,GAAG,MAAM,CAAC;QACtE;;;;;;;;WAQG;QACH,IAAI,EAAE,OAAO,CAAC;QAEd;;;;;;;;;;;;WAYG;QACH,QAAQ,EAAE,MAAM,CAAC;QAEjB;;;;;;;;;;;;WAYG;QACH,OAAO,EAAE,MAAM,CAAC;KACjB;CACF;AAED,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACpD,KAAK,UAAU,GAAG,WAAW,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;AACnD,UAAU,0BAA0B;IAClC,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,GAAG,UAAU,CAAC;CACxD;AACD,UAAU,0BAA0B;IAClC,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,CAAC;CACtC;AACD,UAAU,kBACR,SAAQ,0BAA0B,EAAE,0BAA0B;CAC/D;AAiBD,eAAO,IAAI,6BAA6B,EA2BnC,0BAA0B,CAAC;AAMhC,eAAO,IAAI,6BAA6B,EA4DnC,0BAA0B,CAAC;AAMhC,eAAO,IAAI,oBAAoB,EAoB1B,kBAAkB,CAAC"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Based on [import-meta-ponyfill](https://github.com/gaubee/import-meta-ponyfill),
3
+ * but instead of using npm to install additional dependencies,
4
+ * this approach manually consolidates cjs/mjs/d.ts into a single file.
5
+ *
6
+ * Note that this code might be imported multiple times
7
+ * (for example, both dnt.test.polyfills.ts and dnt.polyfills.ts contain this code;
8
+ * or Node.js might dynamically clear the cache and then force a require).
9
+ * Therefore, it's important to avoid redundant writes to global objects.
10
+ * Additionally, consider that commonjs is used alongside esm,
11
+ * so the two ponyfill functions are stored independently in two separate global objects.
12
+ */
13
+ //@ts-ignore
14
+ import { createRequire } from "node:module";
15
+ //@ts-ignore
16
+ import { fileURLToPath, pathToFileURL } from "node:url";
17
+ //@ts-ignore
18
+ import { dirname } from "node:path";
19
+ const defineGlobalPonyfill = (symbolFor, fn) => {
20
+ if (!Reflect.has(globalThis, Symbol.for(symbolFor))) {
21
+ Object.defineProperty(globalThis, Symbol.for(symbolFor), {
22
+ configurable: true,
23
+ get() {
24
+ return fn;
25
+ },
26
+ });
27
+ }
28
+ };
29
+ export let import_meta_ponyfill_commonjs = (Reflect.get(globalThis, Symbol.for("import-meta-ponyfill-commonjs")) ??
30
+ (() => {
31
+ const moduleImportMetaWM = new WeakMap();
32
+ return (require, module) => {
33
+ let importMetaCache = moduleImportMetaWM.get(module);
34
+ if (importMetaCache == null) {
35
+ const importMeta = Object.assign(Object.create(null), {
36
+ url: pathToFileURL(module.filename).href,
37
+ main: require.main == module,
38
+ resolve: (specifier, parentURL = importMeta.url) => {
39
+ return pathToFileURL((importMeta.url === parentURL
40
+ ? require
41
+ : createRequire(parentURL))
42
+ .resolve(specifier)).href;
43
+ },
44
+ filename: module.filename,
45
+ dirname: module.path,
46
+ });
47
+ moduleImportMetaWM.set(module, importMeta);
48
+ importMetaCache = importMeta;
49
+ }
50
+ return importMetaCache;
51
+ };
52
+ })());
53
+ defineGlobalPonyfill("import-meta-ponyfill-commonjs", import_meta_ponyfill_commonjs);
54
+ export let import_meta_ponyfill_esmodule = (Reflect.get(globalThis, Symbol.for("import-meta-ponyfill-esmodule")) ??
55
+ ((importMeta) => {
56
+ const resolveFunStr = String(importMeta.resolve);
57
+ const shimWs = new WeakSet();
58
+ //@ts-ignore
59
+ const mainUrl = ("file:///" + process.argv[1].replace(/\\/g, "/"))
60
+ .replace(/\/{3,}/, "///");
61
+ const commonShim = (importMeta) => {
62
+ if (typeof importMeta.main !== "boolean") {
63
+ importMeta.main = importMeta.url === mainUrl;
64
+ }
65
+ if (typeof importMeta.filename !== "string") {
66
+ importMeta.filename = fileURLToPath(importMeta.url);
67
+ importMeta.dirname = dirname(importMeta.filename);
68
+ }
69
+ };
70
+ if (
71
+ // v16.2.0+, v14.18.0+: Add support for WHATWG URL object to parentURL parameter.
72
+ resolveFunStr === "undefined" ||
73
+ // v20.0.0+, v18.19.0+"" This API now returns a string synchronously instead of a Promise.
74
+ resolveFunStr.startsWith("async")
75
+ // enable by --experimental-import-meta-resolve flag
76
+ ) {
77
+ import_meta_ponyfill_esmodule = (importMeta) => {
78
+ if (!shimWs.has(importMeta)) {
79
+ shimWs.add(importMeta);
80
+ const importMetaUrlRequire = {
81
+ url: importMeta.url,
82
+ require: createRequire(importMeta.url),
83
+ };
84
+ importMeta.resolve = function resolve(specifier, parentURL = importMeta.url) {
85
+ return pathToFileURL((importMetaUrlRequire.url === parentURL
86
+ ? importMetaUrlRequire.require
87
+ : createRequire(parentURL)).resolve(specifier)).href;
88
+ };
89
+ commonShim(importMeta);
90
+ }
91
+ return importMeta;
92
+ };
93
+ }
94
+ else {
95
+ /// native support
96
+ import_meta_ponyfill_esmodule = (importMeta) => {
97
+ if (!shimWs.has(importMeta)) {
98
+ shimWs.add(importMeta);
99
+ commonShim(importMeta);
100
+ }
101
+ return importMeta;
102
+ };
103
+ }
104
+ return import_meta_ponyfill_esmodule(importMeta);
105
+ }));
106
+ defineGlobalPonyfill("import-meta-ponyfill-esmodule", import_meta_ponyfill_esmodule);
107
+ export let import_meta_ponyfill = ((...args) => {
108
+ const _MODULE = (() => {
109
+ if (typeof require === "function" && typeof module === "object") {
110
+ return "commonjs";
111
+ }
112
+ else {
113
+ // eval("typeof import.meta");
114
+ return "esmodule";
115
+ }
116
+ })();
117
+ if (_MODULE === "commonjs") {
118
+ //@ts-ignore
119
+ import_meta_ponyfill = (r, m) => import_meta_ponyfill_commonjs(r, m);
120
+ }
121
+ else {
122
+ //@ts-ignore
123
+ import_meta_ponyfill = (im) => import_meta_ponyfill_esmodule(im);
124
+ }
125
+ //@ts-ignore
126
+ return import_meta_ponyfill(...args);
127
+ });
@@ -0,0 +1,26 @@
1
+ export declare function fetchChallenge(authUrl: string): Promise<{
2
+ challengeJWT: string;
3
+ challenge: string;
4
+ difficulty: number;
5
+ }>;
6
+ export interface SessionResponse {
7
+ sessionJWT: string;
8
+ apiKey?: string;
9
+ }
10
+ export declare function exchangeSession(authUrl: string, body: {
11
+ challengeJWT: string;
12
+ powHex: string;
13
+ nonce: string;
14
+ apiKey?: string;
15
+ username?: string;
16
+ }): Promise<SessionResponse>;
17
+ export declare function fetchCapability(authUrl: string, sessionJWT: string): Promise<string>;
18
+ export interface PerformPoWInput {
19
+ authUrl: string;
20
+ scryptSalt: string;
21
+ apiKey?: string;
22
+ username?: string;
23
+ onPowProgress?: (nonce: bigint) => void;
24
+ }
25
+ export declare function performPoWAndSession(input: PerformPoWInput): Promise<SessionResponse>;
26
+ //# sourceMappingURL=agent-auth-http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-auth-http.d.ts","sourceRoot":"","sources":["../../../../src/lib/agent/auth/agent-auth-http.ts"],"names":[],"mappings":"AAoCA,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7D,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC,CAqBD;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IACJ,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GACA,OAAO,CAAC,eAAe,CAAC,CAS1B;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAUjB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,eAAe,CAAC,CAgB1B"}
@@ -0,0 +1,76 @@
1
+ // auth-service HTTP: challenge → session → capability.
2
+ import { decodeJwtPayload } from "./agent-jwt.js";
3
+ import { solvePow } from "./agent-pow.js";
4
+ async function postJson(url, body, headers = {}) {
5
+ const res = await fetch(url, {
6
+ method: "POST",
7
+ headers: {
8
+ ...(body ? { "Content-Type": "application/json" } : {}),
9
+ ...headers,
10
+ },
11
+ body: body ? JSON.stringify(body) : undefined,
12
+ });
13
+ const text = await res.text();
14
+ const path = (() => {
15
+ try {
16
+ return new URL(url).pathname;
17
+ }
18
+ catch {
19
+ return url;
20
+ }
21
+ })();
22
+ if (!res.ok) {
23
+ throw new Error(`auth-service ${path} returned ${res.status}: ${text}`);
24
+ }
25
+ try {
26
+ return JSON.parse(text);
27
+ }
28
+ catch {
29
+ throw new Error(`auth-service ${path} returned non-JSON body: ${text}`);
30
+ }
31
+ }
32
+ export async function fetchChallenge(authUrl) {
33
+ const data = await postJson(`${authUrl}/api/v1/challenge`, undefined);
34
+ if (typeof data.challengeJWT !== "string") {
35
+ throw new Error("Challenge response missing challengeJWT.");
36
+ }
37
+ const payload = decodeJwtPayload(data.challengeJWT);
38
+ if (typeof payload.jti !== "string" ||
39
+ typeof payload.difficulty !== "number") {
40
+ throw new Error("Challenge JWT payload malformed (missing jti or difficulty).");
41
+ }
42
+ return {
43
+ challengeJWT: data.challengeJWT,
44
+ challenge: payload.jti,
45
+ difficulty: payload.difficulty,
46
+ };
47
+ }
48
+ export async function exchangeSession(authUrl, body) {
49
+ const data = await postJson(`${authUrl}/api/v1/session`, { ...body });
50
+ if (typeof data.sessionJWT !== "string") {
51
+ throw new Error("Session response missing sessionJWT.");
52
+ }
53
+ return {
54
+ sessionJWT: data.sessionJWT,
55
+ apiKey: typeof data.apiKey === "string" ? data.apiKey : undefined,
56
+ };
57
+ }
58
+ export async function fetchCapability(authUrl, sessionJWT) {
59
+ const data = await postJson(`${authUrl}/api/v1/capability`, undefined, { Authorization: `Bearer ${sessionJWT}` });
60
+ if (typeof data.capabilityJWT !== "string") {
61
+ throw new Error("Capability response missing capabilityJWT.");
62
+ }
63
+ return data.capabilityJWT;
64
+ }
65
+ export async function performPoWAndSession(input) {
66
+ const { authUrl, scryptSalt } = input;
67
+ const { challengeJWT, challenge, difficulty } = await fetchChallenge(authUrl);
68
+ const { powHex, nonce } = await solvePow(challenge, difficulty, scryptSalt, input.onPowProgress);
69
+ return exchangeSession(authUrl, {
70
+ challengeJWT,
71
+ powHex,
72
+ nonce,
73
+ apiKey: input.apiKey,
74
+ username: input.username,
75
+ });
76
+ }
@@ -0,0 +1,14 @@
1
+ export declare const SESSION_TTL_MS: number;
2
+ export declare const CAPABILITY_TTL_MS: number;
3
+ export declare const SESSION_SAFETY_MARGIN_MS = 60000;
4
+ export declare const CAPABILITY_SAFETY_MARGIN_MS = 20000;
5
+ export interface JwtPayload {
6
+ exp?: number;
7
+ iat?: number;
8
+ jti?: string;
9
+ inboxId?: string;
10
+ [key: string]: unknown;
11
+ }
12
+ export declare function decodeJwtPayload<T = JwtPayload>(jwt: string): T;
13
+ export declare function isJwtExpired(jwt: string, marginMs: number): boolean;
14
+ //# sourceMappingURL=agent-jwt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-jwt.d.ts","sourceRoot":"","sources":["../../../../src/lib/agent/auth/agent-jwt.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,QAAqB,CAAC;AACjD,eAAO,MAAM,iBAAiB,QAAgB,CAAC;AAE/C,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAC/C,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAElD,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,CAc/D;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAQnE"}