@atomicmail/agent-skill 0.2.0 → 0.2.2

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 (69) hide show
  1. package/README.md +51 -12
  2. package/SKILL.md +56 -38
  3. package/esm/_dnt.polyfills.d.ts +101 -0
  4. package/esm/_dnt.polyfills.d.ts.map +1 -0
  5. package/esm/_dnt.polyfills.js +127 -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 +85 -0
  8. package/esm/lib/{src → agent/auth}/agent-jwt.d.ts +0 -2
  9. package/esm/lib/agent/auth/agent-jwt.d.ts.map +1 -0
  10. package/esm/lib/{src → agent/auth}/agent-jwt.js +0 -2
  11. package/esm/lib/agent/auth/agent-pow.d.ts.map +1 -0
  12. package/esm/lib/{src → agent/jmap}/agent-help-content.d.ts +1 -0
  13. package/esm/lib/agent/jmap/agent-help-content.d.ts.map +1 -0
  14. package/esm/lib/{src → agent/jmap}/agent-help-content.js +46 -19
  15. package/esm/lib/{src → agent/jmap}/agent-jmap.d.ts +7 -0
  16. package/esm/lib/agent/jmap/agent-jmap.d.ts.map +1 -0
  17. package/esm/lib/{src → agent/jmap}/agent-jmap.js +63 -3
  18. package/esm/lib/agent/jmap/agent-vars.d.ts.map +1 -0
  19. package/esm/lib/{src → agent/session}/agent-credentials-store.d.ts +2 -0
  20. package/esm/lib/agent/session/agent-credentials-store.d.ts.map +1 -0
  21. package/esm/lib/{src → agent/session}/agent-credentials-store.js +2 -0
  22. package/esm/lib/agent/session/agent-resolve-config.d.ts +24 -0
  23. package/esm/lib/agent/session/agent-resolve-config.d.ts.map +1 -0
  24. package/esm/lib/agent/session/agent-resolve-config.js +70 -0
  25. package/esm/lib/{src → agent/session}/agent-session.d.ts +5 -0
  26. package/esm/lib/agent/session/agent-session.d.ts.map +1 -0
  27. package/esm/lib/{src → agent/session}/agent-session.js +46 -10
  28. package/esm/lib/core/consts.d.ts.map +1 -0
  29. package/esm/lib/core/read-npm-package-readme.d.ts +6 -0
  30. package/esm/lib/core/read-npm-package-readme.d.ts.map +1 -0
  31. package/esm/lib/core/read-npm-package-readme.js +66 -0
  32. package/esm/lib/core/types.d.ts +2 -0
  33. package/esm/lib/core/types.d.ts.map +1 -0
  34. package/esm/lib/core/types.js +1 -0
  35. package/esm/lib/core/utils.d.ts +10 -0
  36. package/esm/lib/core/utils.d.ts.map +1 -0
  37. package/esm/lib/core/utils.js +28 -0
  38. package/esm/lib/mod.d.ts +15 -0
  39. package/esm/lib/mod.d.ts.map +1 -0
  40. package/esm/lib/mod.js +14 -0
  41. package/esm/lib/network/auth-client.d.ts +57 -0
  42. package/esm/lib/network/auth-client.d.ts.map +1 -0
  43. package/esm/lib/network/auth-client.js +211 -0
  44. package/esm/skill/cli.d.ts +3 -0
  45. package/esm/skill/cli.d.ts.map +1 -0
  46. package/esm/skill/{scripts/cli.js → cli.js} +18 -9
  47. package/package.json +4 -4
  48. package/presets/list_inbox.json +39 -0
  49. package/presets/reply.json +75 -0
  50. package/presets/send_mail.json +42 -0
  51. package/esm/lib/src/agent-auth-http.d.ts.map +0 -1
  52. package/esm/lib/src/agent-auth-http.js +0 -76
  53. package/esm/lib/src/agent-credentials-store.d.ts.map +0 -1
  54. package/esm/lib/src/agent-help-content.d.ts.map +0 -1
  55. package/esm/lib/src/agent-jmap.d.ts.map +0 -1
  56. package/esm/lib/src/agent-jwt.d.ts.map +0 -1
  57. package/esm/lib/src/agent-pow.d.ts.map +0 -1
  58. package/esm/lib/src/agent-session.d.ts.map +0 -1
  59. package/esm/lib/src/agent-vars.d.ts.map +0 -1
  60. package/esm/lib/src/consts.d.ts.map +0 -1
  61. package/esm/skill/scripts/cli.d.ts +0 -3
  62. package/esm/skill/scripts/cli.d.ts.map +0 -1
  63. /package/esm/lib/{src → agent/auth}/agent-auth-http.d.ts +0 -0
  64. /package/esm/lib/{src → agent/auth}/agent-pow.d.ts +0 -0
  65. /package/esm/lib/{src → agent/auth}/agent-pow.js +0 -0
  66. /package/esm/lib/{src → agent/jmap}/agent-vars.d.ts +0 -0
  67. /package/esm/lib/{src → agent/jmap}/agent-vars.js +0 -0
  68. /package/esm/lib/{src → core}/consts.d.ts +0 -0
  69. /package/esm/lib/{src → core}/consts.js +0 -0
@@ -0,0 +1,28 @@
1
+ import { ONE_SEC_MS } from "./consts.js";
2
+ export function delay(ms) {
3
+ return new Promise((resolve) => setTimeout(resolve, ms));
4
+ }
5
+ const defaultCfg = {
6
+ maxTimeoutMs: ONE_SEC_MS * 32,
7
+ startTimeoutMs: ONE_SEC_MS,
8
+ backoffMul: 2,
9
+ };
10
+ // retry with exponential backoff, retries the fn on throw, re-throws on max backoff
11
+ export async function retry(fn, config) {
12
+ const cfg = { ...defaultCfg, ...config };
13
+ let curTimeoutMs = cfg.startTimeoutMs;
14
+ while (true) {
15
+ try {
16
+ const res = await fn();
17
+ return res;
18
+ }
19
+ catch (e) {
20
+ if (cfg.onBeforeRetry)
21
+ await cfg.onBeforeRetry(e);
22
+ if (curTimeoutMs > cfg.maxTimeoutMs)
23
+ throw e;
24
+ await delay(curTimeoutMs);
25
+ curTimeoutMs = Math.floor(curTimeoutMs * cfg.backoffMul);
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,15 @@
1
+ export * from "./network/auth-client.js";
2
+ export * from "./core/utils.js";
3
+ export * from "./core/read-npm-package-readme.js";
4
+ export * from "./core/consts.js";
5
+ export * from "./core/types.js";
6
+ export * from "./agent/session/agent-credentials-store.js";
7
+ export * from "./agent/auth/agent-jwt.js";
8
+ export * from "./agent/auth/agent-pow.js";
9
+ export * from "./agent/auth/agent-auth-http.js";
10
+ export * from "./agent/jmap/agent-jmap.js";
11
+ export * from "./agent/session/agent-session.js";
12
+ export * from "./agent/session/agent-resolve-config.js";
13
+ export * from "./agent/jmap/agent-help-content.js";
14
+ export * from "./agent/jmap/agent-vars.js";
15
+ //# sourceMappingURL=mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../src/lib/mod.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mCAAmC,CAAC;AAClD,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAEhC,cAAc,4CAA4C,CAAC;AAC3D,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iCAAiC,CAAC;AAChD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kCAAkC,CAAC;AACjD,cAAc,yCAAyC,CAAC;AACxD,cAAc,oCAAoC,CAAC;AACnD,cAAc,4BAA4B,CAAC"}
package/esm/lib/mod.js ADDED
@@ -0,0 +1,14 @@
1
+ export * from "./network/auth-client.js";
2
+ export * from "./core/utils.js";
3
+ export * from "./core/read-npm-package-readme.js";
4
+ export * from "./core/consts.js";
5
+ export * from "./core/types.js";
6
+ export * from "./agent/session/agent-credentials-store.js";
7
+ export * from "./agent/auth/agent-jwt.js";
8
+ export * from "./agent/auth/agent-pow.js";
9
+ export * from "./agent/auth/agent-auth-http.js";
10
+ export * from "./agent/jmap/agent-jmap.js";
11
+ export * from "./agent/session/agent-session.js";
12
+ export * from "./agent/session/agent-resolve-config.js";
13
+ export * from "./agent/jmap/agent-help-content.js";
14
+ export * from "./agent/jmap/agent-vars.js";
@@ -0,0 +1,57 @@
1
+ export interface AuthClientOptions {
2
+ /** Base URL of auth-service, e.g. "http://localhost:8000". Trailing slashes are stripped. */
3
+ baseUrl: string;
4
+ /**
5
+ * PoW scrypt salt (hex string). When omitted, {@link DEFAULT_POW_SCRYPT_SALT_HEX}
6
+ * is used so clients match the bundled auth-service.
7
+ */
8
+ scryptSaltHex?: string;
9
+ }
10
+ export interface SignupResult {
11
+ /** Freshly minted API key. The server only returns it once — persist it. */
12
+ apiKey: string;
13
+ sessionJWT: string;
14
+ }
15
+ export interface LoginResult {
16
+ sessionJWT: string;
17
+ }
18
+ export interface RenewResult {
19
+ capabilityJWT: string;
20
+ }
21
+ /** Thrown for any non-2xx HTTP response or malformed payload. */
22
+ export declare class AuthClientError extends Error {
23
+ status: number;
24
+ bodyText: string;
25
+ constructor(status: number, bodyText: string, message: string);
26
+ }
27
+ export declare class AuthClient {
28
+ private readonly baseUrl;
29
+ private readonly scryptSaltHex;
30
+ constructor(options: AuthClientOptions);
31
+ /**
32
+ * Register a new inbox under `username`. Returns the freshly minted API key
33
+ * (the server only ever returns it once — the caller MUST persist it) and
34
+ * a session JWT.
35
+ */
36
+ signup(username: string): Promise<SignupResult>;
37
+ /** Exchange an existing API key for a fresh session JWT. */
38
+ login(apiKey: string): Promise<LoginResult>;
39
+ /**
40
+ * Exchange a session JWT for a short-lived capability JWT (audience:
41
+ * api-service).
42
+ */
43
+ renew(sessionJWT: string): Promise<RenewResult>;
44
+ private fetchChallenge;
45
+ private postSession;
46
+ private parseJsonOrThrow;
47
+ /**
48
+ * Brute-force a PoW nonce. Mirrors `generatePow` in
49
+ * services/auth-service/src/crypto.ts: scrypt(`${challenge}:${nonce}`, salt,
50
+ * 64) until `difficulty` leading bits of the digest are zero.
51
+ *
52
+ * Expected work at the server's POW_DIFFICULTY=6 is ~2^6 = 64 attempts; well
53
+ * within the challenge JWT's 3-minute TTL.
54
+ */
55
+ private solvePoW;
56
+ }
57
+ //# sourceMappingURL=auth-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-client.d.ts","sourceRoot":"","sources":["../../../src/lib/network/auth-client.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,iBAAiB;IAChC,6FAA6F;IAC7F,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,iEAAiE;AACjE,qBAAa,eAAgB,SAAQ,KAAK;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;gBAEL,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAM9D;AAOD,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,OAAO,EAAE,iBAAiB;IAKtC;;;;OAIG;IACG,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAyBrD,4DAA4D;IACtD,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAgBjD;;;OAGG;IACG,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;YAoBvC,cAAc;YAsCd,WAAW;YAyCX,gBAAgB;IAuB9B;;;;;;;OAOG;YACW,QAAQ;CAgBvB"}
@@ -0,0 +1,211 @@
1
+ // auth-client
2
+ //
3
+ // Thin HTTP client for services/auth-service. Encapsulates the full PoW
4
+ // challenge → session → capability flow so callers (integration tests, the
5
+ // future agent skill, etc.) don't have to reimplement scrypt grinding.
6
+ //
7
+ // The PoW digest is scrypt-based and uses the SAME salt the auth-service
8
+ // uses on the verify path (see services/auth-service/src/crypto.ts). The
9
+ // client must therefore be configured with that salt — there is no public
10
+ // hash function here, the salt is part of the protocol.
11
+ import { scrypt } from "node:crypto";
12
+ import { DEFAULT_POW_SCRYPT_SALT_HEX } from "../core/consts.js";
13
+ // Mirror services/auth-service/src/crypto.ts exactly. Changing any of these
14
+ // constants on either side breaks PoW interop.
15
+ const SCRYPT_PARAMS = { N: 16384, r: 8, p: 1 };
16
+ const POW_HASH_BYTES = 64;
17
+ /** Thrown for any non-2xx HTTP response or malformed payload. */
18
+ export class AuthClientError extends Error {
19
+ status;
20
+ bodyText;
21
+ constructor(status, bodyText, message) {
22
+ super(message);
23
+ this.name = "AuthClientError";
24
+ this.status = status;
25
+ this.bodyText = bodyText;
26
+ }
27
+ }
28
+ export class AuthClient {
29
+ baseUrl;
30
+ scryptSaltHex;
31
+ constructor(options) {
32
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
33
+ this.scryptSaltHex = options.scryptSaltHex ?? DEFAULT_POW_SCRYPT_SALT_HEX;
34
+ }
35
+ /**
36
+ * Register a new inbox under `username`. Returns the freshly minted API key
37
+ * (the server only ever returns it once — the caller MUST persist it) and
38
+ * a session JWT.
39
+ */
40
+ async signup(username) {
41
+ const { challengeJWT, challenge, difficulty } = await this.fetchChallenge();
42
+ const { powHex, nonce } = await this.solvePoW(challenge, difficulty);
43
+ const { sessionJWT, data } = await this.postSession(challengeJWT, {
44
+ powHex,
45
+ nonce: nonce.toString(),
46
+ username,
47
+ });
48
+ if (typeof data.apiKey !== "string") {
49
+ throw new AuthClientError(200, JSON.stringify(data), "Signup response missing apiKey.");
50
+ }
51
+ return { apiKey: data.apiKey, sessionJWT };
52
+ }
53
+ /** Exchange an existing API key for a fresh session JWT. */
54
+ async login(apiKey) {
55
+ const { challengeJWT, challenge, difficulty } = await this.fetchChallenge();
56
+ const { powHex, nonce } = await this.solvePoW(challenge, difficulty);
57
+ const { sessionJWT } = await this.postSession(challengeJWT, {
58
+ powHex,
59
+ nonce: nonce.toString(),
60
+ apiKey,
61
+ });
62
+ return { sessionJWT };
63
+ }
64
+ /**
65
+ * Exchange a session JWT for a short-lived capability JWT (audience:
66
+ * api-service).
67
+ */
68
+ async renew(sessionJWT) {
69
+ const res = await fetch(`${this.baseUrl}/api/v1/capability`, {
70
+ method: "POST",
71
+ headers: { Authorization: `Bearer ${sessionJWT}` },
72
+ });
73
+ const text = await res.text();
74
+ if (!res.ok) {
75
+ throw new AuthClientError(res.status, text, `auth-service capability returned ${res.status}: ${text}`);
76
+ }
77
+ const capabilityJWT = readBearerToken(res.headers.get("Authorization"), "Capability response missing Authorization bearer token.");
78
+ return { capabilityJWT };
79
+ }
80
+ async fetchChallenge() {
81
+ const res = await fetch(`${this.baseUrl}/api/v1/challenge`, {
82
+ method: "POST",
83
+ });
84
+ const text = await res.text();
85
+ if (!res.ok) {
86
+ throw new AuthClientError(res.status, text, `auth-service challenge returned ${res.status}: ${text}`);
87
+ }
88
+ const challengeJWT = readBearerToken(res.headers.get("Authorization"), "Challenge response missing Authorization bearer token.");
89
+ const payload = decodeJwtPayload(challengeJWT);
90
+ if (typeof payload.jti !== "string" ||
91
+ typeof payload.difficulty !== "number") {
92
+ throw new AuthClientError(res.status, challengeJWT, "Challenge JWT payload is malformed (missing jti or difficulty).");
93
+ }
94
+ return {
95
+ challengeJWT,
96
+ challenge: payload.jti,
97
+ difficulty: payload.difficulty,
98
+ };
99
+ }
100
+ async postSession(challengeJWT, body) {
101
+ const res = await fetch(`${this.baseUrl}/api/v1/session`, {
102
+ method: "POST",
103
+ headers: {
104
+ "Content-Type": "application/json",
105
+ Authorization: `Bearer ${challengeJWT}`,
106
+ },
107
+ body: JSON.stringify(body),
108
+ });
109
+ const text = await res.text();
110
+ if (!res.ok) {
111
+ throw new AuthClientError(res.status, text, `auth-service session returned ${res.status}: ${text}`);
112
+ }
113
+ const sessionJWT = readBearerToken(res.headers.get("Authorization"), "Session response missing Authorization bearer token.");
114
+ let data = {};
115
+ if (text.trim().length > 0) {
116
+ try {
117
+ data = JSON.parse(text);
118
+ }
119
+ catch {
120
+ throw new AuthClientError(res.status, text, "auth-service session returned non-JSON body.");
121
+ }
122
+ }
123
+ return { sessionJWT, data };
124
+ }
125
+ async parseJsonOrThrow(res, endpoint) {
126
+ const text = await res.text();
127
+ if (!res.ok) {
128
+ throw new AuthClientError(res.status, text, `auth-service ${endpoint} returned ${res.status}: ${text}`);
129
+ }
130
+ try {
131
+ return JSON.parse(text);
132
+ }
133
+ catch {
134
+ throw new AuthClientError(res.status, text, `auth-service ${endpoint} returned non-JSON body.`);
135
+ }
136
+ }
137
+ /**
138
+ * Brute-force a PoW nonce. Mirrors `generatePow` in
139
+ * services/auth-service/src/crypto.ts: scrypt(`${challenge}:${nonce}`, salt,
140
+ * 64) until `difficulty` leading bits of the digest are zero.
141
+ *
142
+ * Expected work at the server's POW_DIFFICULTY=6 is ~2^6 = 64 attempts; well
143
+ * within the challenge JWT's 3-minute TTL.
144
+ */
145
+ async solvePoW(challenge, difficulty) {
146
+ let nonce = 0n;
147
+ while (true) {
148
+ const digest = await scryptHash(`${challenge}:${nonce}`, this.scryptSaltHex);
149
+ if (hasLeadingZeroBits(digest, difficulty)) {
150
+ return { powHex: bytesToHex(digest), nonce };
151
+ }
152
+ nonce++;
153
+ }
154
+ }
155
+ }
156
+ function scryptHash(data, salt) {
157
+ const bytes = new TextEncoder().encode(data);
158
+ return new Promise((resolve, reject) => {
159
+ scrypt(bytes, salt, POW_HASH_BYTES, SCRYPT_PARAMS, (err, derived) => {
160
+ if (err)
161
+ return reject(err);
162
+ resolve(new Uint8Array(derived));
163
+ });
164
+ });
165
+ }
166
+ function hasLeadingZeroBits(hash, bits) {
167
+ if (bits > hash.length * 8)
168
+ return false;
169
+ const fullBytes = Math.floor(bits / 8);
170
+ const remainingBits = bits % 8;
171
+ for (let i = 0; i < fullBytes; i++) {
172
+ if (hash[i] !== 0)
173
+ return false;
174
+ }
175
+ if (remainingBits > 0) {
176
+ const mask = (0xff << (8 - remainingBits)) & 0xff;
177
+ if ((hash[fullBytes] & mask) !== 0)
178
+ return false;
179
+ }
180
+ return true;
181
+ }
182
+ function bytesToHex(bytes) {
183
+ let hex = "";
184
+ for (let i = 0; i < bytes.length; i++) {
185
+ hex += bytes[i].toString(16).padStart(2, "0");
186
+ }
187
+ return hex;
188
+ }
189
+ function decodeJwtPayload(jwt) {
190
+ const parts = jwt.split(".");
191
+ if (parts.length < 2) {
192
+ throw new Error("Malformed JWT: expected at least 2 dot-separated segments.");
193
+ }
194
+ const payloadB64Url = parts[1];
195
+ const padLen = (4 - (payloadB64Url.length % 4)) % 4;
196
+ const base64 = payloadB64Url
197
+ .replace(/-/g, "+")
198
+ .replace(/_/g, "/")
199
+ .padEnd(payloadB64Url.length + padLen, "=");
200
+ return JSON.parse(atob(base64));
201
+ }
202
+ function readBearerToken(headerValue, missingError) {
203
+ if (!headerValue) {
204
+ throw new Error(missingError);
205
+ }
206
+ const match = /^\s*Bearer\s+(.+?)\s*$/i.exec(headerValue);
207
+ if (!match || !match[1]) {
208
+ throw new Error("Authorization header must use Bearer scheme.");
209
+ }
210
+ return match[1];
211
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import "../_dnt.polyfills.js";
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/skill/cli.ts"],"names":[],"mappings":";AAEA,OAAO,sBAAsB,CAAC"}
@@ -1,14 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  // Atomic Mail AgentSkill — register | jmap_request | help
3
+ import "../_dnt.polyfills.js";
3
4
  import process from "node:process";
4
5
  import { parseArgs } from "node:util";
5
6
  import { resolve } from "node:path";
6
7
  import { homedir } from "node:os";
7
- import { DEFAULT_POW_SCRYPT_SALT_HEX } from "../../lib/src/consts.js";
8
- import { getHelp } from "../../lib/src/agent-help-content.js";
9
- import { DEFAULT_JMAP_USING, readOpsFile, runJmapRequest, } from "../../lib/src/agent-jmap.js";
10
- import { AgentSession, persistLoginWithApiKey, } from "../../lib/src/agent-session.js";
11
- import { defaultFilesFromOutDir, readCredentials, } from "../../lib/src/agent-credentials-store.js";
8
+ import { AgentSession, DEFAULT_JMAP_USING, DEFAULT_POW_SCRYPT_SALT_HEX, defaultFilesFromOutDir, getHelp, normalizeHelpTopic, persistLoginWithApiKey, readCredentials, readNpmPackageReadme, readOpsFile, runJmapRequest, } from "../lib/mod.js";
12
9
  const USAGE = `Atomic Mail — AgentSkill
13
10
 
14
11
  Usage:
@@ -17,7 +14,7 @@ Usage:
17
14
  Commands:
18
15
  register PoW signup or login with API key (writes credentials)
19
16
  jmap_request Send a JMAP batch (inline --ops or --ops-file preset)
20
- help Full documentation [--topic TOPIC]
17
+ help Full documentation [--topic TOPIC] (topic readme = package README)
21
18
 
22
19
  Examples:
23
20
  atomicmail register --username alice
@@ -25,6 +22,7 @@ Examples:
25
22
  atomicmail jmap_request --credentials-dir ./.atomic-mail --ops-file fetch.json
26
23
  atomicmail jmap_request --credentials-dir ./.atomic-mail --ops-file send.json --vars '{"TO":"a@b.com","SUBJECT":"Hi"}'
27
24
  atomicmail help --topic presets
25
+ atomicmail help --topic readme
28
26
 
29
27
  Run atomicmail <command> --help for command-specific flags.
30
28
  `;
@@ -255,7 +253,7 @@ Options:
255
253
  }
256
254
  process.stdout.write(bodyText.endsWith("\n") ? bodyText : bodyText + "\n");
257
255
  }
258
- function cmdHelp(argv) {
256
+ async function cmdHelp(argv) {
259
257
  let parsed;
260
258
  try {
261
259
  parsed = parseArgs({
@@ -274,11 +272,22 @@ function cmdHelp(argv) {
274
272
  if (parsed.values.help) {
275
273
  process.stdout.write(`Usage: atomicmail help [--topic TOPIC]
276
274
 
277
- Topics include: overview, installation, auth, jmap_cheatsheet, tools, presets, troubleshooting.
275
+ Topics include: overview, installation, auth, jmap_cheatsheet, tools, presets, troubleshooting, readme.
276
+ Topic readme prints the npm package README.md (requires install from npm).
278
277
  `);
279
278
  process.exit(0);
280
279
  }
281
280
  const topic = parsed.values.topic;
281
+ if (topic !== undefined && normalizeHelpTopic(topic) === "readme") {
282
+ try {
283
+ const text = await readNpmPackageReadme();
284
+ process.stdout.write(text.endsWith("\n") ? text : text + "\n");
285
+ }
286
+ catch (err) {
287
+ fail(err instanceof Error ? err.message : String(err));
288
+ }
289
+ return;
290
+ }
282
291
  process.stdout.write(getHelp(topic) + "\n");
283
292
  }
284
293
  async function main() {
@@ -296,7 +305,7 @@ async function main() {
296
305
  await cmdJmapRequest(rest);
297
306
  break;
298
307
  case "help":
299
- cmdHelp(rest);
308
+ await cmdHelp(rest);
300
309
  break;
301
310
  default:
302
311
  process.stderr.write(`Unknown command: ${cmd}\n\n`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atomicmail/agent-skill",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Atomic Mail AgentSkill — register, jmap_request, and help CLI for AI agents.",
5
5
  "keywords": [
6
6
  "atomic-mail",
@@ -16,15 +16,15 @@
16
16
  ],
17
17
  "repository": {
18
18
  "type": "git",
19
- "url": "git+https://github.com/atomic-mail/agentic-mail.git"
19
+ "url": "git+https://github.com/atomic-mail/agentic-clients.git"
20
20
  },
21
21
  "license": "MIT",
22
22
  "bugs": {
23
- "url": "https://github.com/atomic-mail/agentic-mail/issues"
23
+ "url": "https://github.com/atomic-mail/agentic-clients/issues"
24
24
  },
25
25
  "scripts": {},
26
26
  "bin": {
27
- "atomicmail": "./esm/skill/scripts/cli.js"
27
+ "atomicmail": "./esm/skill/cli.js"
28
28
  },
29
29
  "engines": {
30
30
  "node": ">=20"
@@ -0,0 +1,39 @@
1
+ {
2
+ "using": [
3
+ "urn:ietf:params:jmap:core",
4
+ "urn:ietf:params:jmap:mail"
5
+ ],
6
+ "methodCalls": [
7
+ [
8
+ "Email/query",
9
+ {
10
+ "accountId": "$ACCOUNT_ID",
11
+ "filter": { "inMailbox": "$INBOX" },
12
+ "sort": [{ "property": "receivedAt", "isAscending": false }],
13
+ "limit": "$COUNT"
14
+ },
15
+ "q0"
16
+ ],
17
+ [
18
+ "Email/get",
19
+ {
20
+ "accountId": "$ACCOUNT_ID",
21
+ "#ids": {
22
+ "resultOf": "q0",
23
+ "name": "Email/query",
24
+ "path": "/ids"
25
+ },
26
+ "properties": [
27
+ "id",
28
+ "threadId",
29
+ "receivedAt",
30
+ "from",
31
+ "to",
32
+ "subject",
33
+ "preview"
34
+ ]
35
+ },
36
+ "g0"
37
+ ]
38
+ ]
39
+ }
@@ -0,0 +1,75 @@
1
+ {
2
+ "using": [
3
+ "urn:ietf:params:jmap:core",
4
+ "urn:ietf:params:jmap:mail",
5
+ "urn:ietf:params:jmap:submission"
6
+ ],
7
+ "methodCalls": [
8
+ [
9
+ "Email/get",
10
+ {
11
+ "accountId": "$ACCOUNT_ID",
12
+ "ids": ["$MAIL_ID"],
13
+ "properties": [
14
+ "id",
15
+ "threadId",
16
+ "from",
17
+ "replyTo",
18
+ "subject",
19
+ "messageId"
20
+ ]
21
+ },
22
+ "g0"
23
+ ],
24
+ [
25
+ "Email/set",
26
+ {
27
+ "accountId": "$ACCOUNT_ID",
28
+ "create": {
29
+ "d1": {
30
+ "from": [{ "email": "$INBOX" }],
31
+ "#to": {
32
+ "resultOf": "g0",
33
+ "name": "Email/get",
34
+ "path": "/list/0/replyTo"
35
+ },
36
+ "#subject": {
37
+ "resultOf": "g0",
38
+ "name": "Email/get",
39
+ "path": "/list/0/subject"
40
+ },
41
+ "#inReplyTo": {
42
+ "resultOf": "g0",
43
+ "name": "Email/get",
44
+ "path": "/list/0/messageId"
45
+ },
46
+ "textBody": [{ "partId": "b", "type": "text/plain" }],
47
+ "bodyValues": { "b": { "value": "$BODY" } },
48
+ "keywords": { "$draft": true }
49
+ }
50
+ }
51
+ },
52
+ "c0"
53
+ ],
54
+ [
55
+ "EmailSubmission/set",
56
+ {
57
+ "accountId": "$ACCOUNT_ID",
58
+ "create": {
59
+ "s1": {
60
+ "emailId": "#d1",
61
+ "envelope": {
62
+ "mailFrom": { "email": "$INBOX" },
63
+ "#rcptTo": {
64
+ "resultOf": "g0",
65
+ "name": "Email/get",
66
+ "path": "/list/0/replyTo"
67
+ }
68
+ }
69
+ }
70
+ }
71
+ },
72
+ "c1"
73
+ ]
74
+ ]
75
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "using": [
3
+ "urn:ietf:params:jmap:core",
4
+ "urn:ietf:params:jmap:mail",
5
+ "urn:ietf:params:jmap:submission"
6
+ ],
7
+ "methodCalls": [
8
+ [
9
+ "Email/set",
10
+ {
11
+ "accountId": "$ACCOUNT_ID",
12
+ "create": {
13
+ "d1": {
14
+ "from": [{ "email": "$INBOX" }],
15
+ "to": [{ "email": "$TO" }],
16
+ "subject": "$SUBJECT",
17
+ "textBody": [{ "partId": "b", "type": "text/plain" }],
18
+ "bodyValues": { "b": { "value": "$BODY" } },
19
+ "keywords": { "$draft": true }
20
+ }
21
+ }
22
+ },
23
+ "c0"
24
+ ],
25
+ [
26
+ "EmailSubmission/set",
27
+ {
28
+ "accountId": "$ACCOUNT_ID",
29
+ "create": {
30
+ "s1": {
31
+ "emailId": "#d1",
32
+ "envelope": {
33
+ "mailFrom": { "email": "$INBOX" },
34
+ "rcptTo": [{ "email": "$TO" }]
35
+ }
36
+ }
37
+ }
38
+ },
39
+ "c1"
40
+ ]
41
+ ]
42
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"agent-auth-http.d.ts","sourceRoot":"","sources":["../../../src/lib/src/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"}