@atomicmail/langchain 0.3.14

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 (138) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -0
  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/langchain/mod.d.ts +23 -0
  7. package/esm/langchain/mod.d.ts.map +1 -0
  8. package/esm/langchain/mod.js +157 -0
  9. package/esm/lib/agent/auth/agent-auth-http.d.ts +26 -0
  10. package/esm/lib/agent/auth/agent-auth-http.d.ts.map +1 -0
  11. package/esm/lib/agent/auth/agent-auth-http.js +85 -0
  12. package/esm/lib/agent/auth/agent-jwt.d.ts +12 -0
  13. package/esm/lib/agent/auth/agent-jwt.d.ts.map +1 -0
  14. package/esm/lib/agent/auth/agent-jwt.js +27 -0
  15. package/esm/lib/agent/auth/agent-pow.d.ts +5 -0
  16. package/esm/lib/agent/auth/agent-pow.d.ts.map +1 -0
  17. package/esm/lib/agent/auth/agent-pow.js +49 -0
  18. package/esm/lib/agent/jmap/agent-help-content.d.ts +2 -0
  19. package/esm/lib/agent/jmap/agent-help-content.d.ts.map +1 -0
  20. package/esm/lib/agent/jmap/agent-help-content.js +2 -0
  21. package/esm/lib/agent/jmap/agent-jmap-blob-limits.d.ts +27 -0
  22. package/esm/lib/agent/jmap/agent-jmap-blob-limits.d.ts.map +1 -0
  23. package/esm/lib/agent/jmap/agent-jmap-blob-limits.js +166 -0
  24. package/esm/lib/agent/jmap/agent-jmap-blob-upload.d.ts +24 -0
  25. package/esm/lib/agent/jmap/agent-jmap-blob-upload.d.ts.map +1 -0
  26. package/esm/lib/agent/jmap/agent-jmap-blob-upload.js +104 -0
  27. package/esm/lib/agent/jmap/agent-jmap-email-charset.d.ts +8 -0
  28. package/esm/lib/agent/jmap/agent-jmap-email-charset.d.ts.map +1 -0
  29. package/esm/lib/agent/jmap/agent-jmap-email-charset.js +61 -0
  30. package/esm/lib/agent/jmap/agent-jmap-verify.d.ts +9 -0
  31. package/esm/lib/agent/jmap/agent-jmap-verify.d.ts.map +1 -0
  32. package/esm/lib/agent/jmap/agent-jmap-verify.js +50 -0
  33. package/esm/lib/agent/jmap/agent-jmap.d.ts +89 -0
  34. package/esm/lib/agent/jmap/agent-jmap.d.ts.map +1 -0
  35. package/esm/lib/agent/jmap/agent-jmap.js +373 -0
  36. package/esm/lib/agent/jmap/agent-vars.d.ts +30 -0
  37. package/esm/lib/agent/jmap/agent-vars.d.ts.map +1 -0
  38. package/esm/lib/agent/jmap/agent-vars.js +96 -0
  39. package/esm/lib/agent/jmap/help-content/auth.d.ts +2 -0
  40. package/esm/lib/agent/jmap/help-content/auth.d.ts.map +1 -0
  41. package/esm/lib/agent/jmap/help-content/auth.js +33 -0
  42. package/esm/lib/agent/jmap/help-content/cron.d.ts +6 -0
  43. package/esm/lib/agent/jmap/help-content/cron.d.ts.map +1 -0
  44. package/esm/lib/agent/jmap/help-content/cron.js +159 -0
  45. package/esm/lib/agent/jmap/help-content/index.d.ts +6 -0
  46. package/esm/lib/agent/jmap/help-content/index.d.ts.map +1 -0
  47. package/esm/lib/agent/jmap/help-content/index.js +61 -0
  48. package/esm/lib/agent/jmap/help-content/installation.d.ts +2 -0
  49. package/esm/lib/agent/jmap/help-content/installation.d.ts.map +1 -0
  50. package/esm/lib/agent/jmap/help-content/installation.js +47 -0
  51. package/esm/lib/agent/jmap/help-content/jmap-cheatsheet.d.ts +2 -0
  52. package/esm/lib/agent/jmap/help-content/jmap-cheatsheet.d.ts.map +1 -0
  53. package/esm/lib/agent/jmap/help-content/jmap-cheatsheet.js +230 -0
  54. package/esm/lib/agent/jmap/help-content/multi-account.d.ts +2 -0
  55. package/esm/lib/agent/jmap/help-content/multi-account.d.ts.map +1 -0
  56. package/esm/lib/agent/jmap/help-content/multi-account.js +49 -0
  57. package/esm/lib/agent/jmap/help-content/overview.d.ts +2 -0
  58. package/esm/lib/agent/jmap/help-content/overview.d.ts.map +1 -0
  59. package/esm/lib/agent/jmap/help-content/overview.js +49 -0
  60. package/esm/lib/agent/jmap/help-content/presets.d.ts +2 -0
  61. package/esm/lib/agent/jmap/help-content/presets.d.ts.map +1 -0
  62. package/esm/lib/agent/jmap/help-content/presets.js +51 -0
  63. package/esm/lib/agent/jmap/help-content/tools.d.ts +2 -0
  64. package/esm/lib/agent/jmap/help-content/tools.d.ts.map +1 -0
  65. package/esm/lib/agent/jmap/help-content/tools.js +49 -0
  66. package/esm/lib/agent/jmap/help-content/troubleshooting.d.ts +2 -0
  67. package/esm/lib/agent/jmap/help-content/troubleshooting.d.ts.map +1 -0
  68. package/esm/lib/agent/jmap/help-content/troubleshooting.js +65 -0
  69. package/esm/lib/agent/session/agent-credentials-store.d.ts +45 -0
  70. package/esm/lib/agent/session/agent-credentials-store.d.ts.map +1 -0
  71. package/esm/lib/agent/session/agent-credentials-store.js +121 -0
  72. package/esm/lib/agent/session/agent-resolve-config.d.ts +29 -0
  73. package/esm/lib/agent/session/agent-resolve-config.d.ts.map +1 -0
  74. package/esm/lib/agent/session/agent-resolve-config.js +71 -0
  75. package/esm/lib/agent/session/agent-session-for-dir.d.ts +8 -0
  76. package/esm/lib/agent/session/agent-session-for-dir.d.ts.map +1 -0
  77. package/esm/lib/agent/session/agent-session-for-dir.js +33 -0
  78. package/esm/lib/agent/session/agent-session.d.ts +89 -0
  79. package/esm/lib/agent/session/agent-session.d.ts.map +1 -0
  80. package/esm/lib/agent/session/agent-session.js +320 -0
  81. package/esm/lib/agent/session/inbox-id-to-mailbox-email.d.ts +6 -0
  82. package/esm/lib/agent/session/inbox-id-to-mailbox-email.d.ts.map +1 -0
  83. package/esm/lib/agent/session/inbox-id-to-mailbox-email.js +24 -0
  84. package/esm/lib/core/consts.d.ts +17 -0
  85. package/esm/lib/core/consts.d.ts.map +1 -0
  86. package/esm/lib/core/consts.js +28 -0
  87. package/esm/lib/core/messages.d.ts +6 -0
  88. package/esm/lib/core/messages.d.ts.map +1 -0
  89. package/esm/lib/core/messages.js +19 -0
  90. package/esm/lib/core/read-npm-package-readme.d.ts +6 -0
  91. package/esm/lib/core/read-npm-package-readme.d.ts.map +1 -0
  92. package/esm/lib/core/read-npm-package-readme.js +81 -0
  93. package/esm/lib/core/shared-assets.d.ts +6 -0
  94. package/esm/lib/core/shared-assets.d.ts.map +1 -0
  95. package/esm/lib/core/shared-assets.js +46 -0
  96. package/esm/lib/core/types.d.ts +2 -0
  97. package/esm/lib/core/types.d.ts.map +1 -0
  98. package/esm/lib/core/types.js +2 -0
  99. package/esm/lib/core/utils.d.ts +12 -0
  100. package/esm/lib/core/utils.d.ts.map +1 -0
  101. package/esm/lib/core/utils.js +29 -0
  102. package/esm/lib/mod.d.ts +20 -0
  103. package/esm/lib/mod.d.ts.map +1 -0
  104. package/esm/lib/mod.js +19 -0
  105. package/esm/lib/network/auth-client.d.ts +57 -0
  106. package/esm/lib/network/auth-client.d.ts.map +1 -0
  107. package/esm/lib/network/auth-client.js +210 -0
  108. package/esm/package.json +3 -0
  109. package/package.json +51 -0
  110. package/presets/list_inbox.json +46 -0
  111. package/presets/reply.json +97 -0
  112. package/presets/send_mail.json +70 -0
  113. package/presets/send_mail_attachment.json +92 -0
  114. package/presets/send_mail_blob_attachment.json +74 -0
  115. package/shared/consts.json +11 -0
  116. package/shared/fixtures/pow_vectors.json +32 -0
  117. package/shared/help/fragments/inbox_cron_agent_prompt.md +1 -0
  118. package/shared/help/fragments/post_register_cron_reminder.md +5 -0
  119. package/shared/help/readme_stub.md +3 -0
  120. package/shared/help/topics/auth.md +8 -0
  121. package/shared/help/topics/cron.md +217 -0
  122. package/shared/help/topics/installation.md +35 -0
  123. package/shared/help/topics/jmap_cheatsheet.md +19 -0
  124. package/shared/help/topics/multi_account.md +9 -0
  125. package/shared/help/topics/overview.md +27 -0
  126. package/shared/help/topics/presets.md +12 -0
  127. package/shared/help/topics/tools.md +16 -0
  128. package/shared/help/topics/troubleshooting.md +6 -0
  129. package/shared/manifest.json +31 -0
  130. package/shared/messages/errors.json +68 -0
  131. package/shared/messages/hints.json +8 -0
  132. package/shared/presets/list_inbox.json +46 -0
  133. package/shared/presets/reply.json +97 -0
  134. package/shared/presets/send_mail.json +70 -0
  135. package/shared/presets/send_mail_attachment.json +92 -0
  136. package/shared/presets/send_mail_blob_attachment.json +74 -0
  137. package/shared/skill/SKILL.template.md +202 -0
  138. package/shared/skill/manifest.json +89 -0
@@ -0,0 +1,46 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { dirname, resolve as resolvePath } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const moduleDir = dirname(fileURLToPath(globalThis[Symbol.for("import-meta-ponyfill-esmodule")](import.meta).url));
5
+ function resolveSharedRoot() {
6
+ let current = moduleDir;
7
+ for (let depth = 0; depth < 12; depth++) {
8
+ const candidate = resolvePath(current, "shared");
9
+ if (existsSync(candidate))
10
+ return candidate;
11
+ const parent = resolvePath(current, "..");
12
+ if (parent === current)
13
+ break;
14
+ current = parent;
15
+ }
16
+ throw new Error(`Shared asset directory was not found from module path: ${moduleDir}`);
17
+ }
18
+ let cachedSharedRoot;
19
+ export function getSharedRootPath() {
20
+ if (!cachedSharedRoot)
21
+ cachedSharedRoot = resolveSharedRoot();
22
+ return cachedSharedRoot;
23
+ }
24
+ export function readSharedText(relativePath) {
25
+ const fullPath = resolvePath(getSharedRootPath(), relativePath);
26
+ return readFileSync(fullPath, "utf-8");
27
+ }
28
+ export function readSharedJson(relativePath) {
29
+ return JSON.parse(readSharedText(relativePath));
30
+ }
31
+ export function tryReadSharedText(relativePath) {
32
+ try {
33
+ return readSharedText(relativePath);
34
+ }
35
+ catch {
36
+ return undefined;
37
+ }
38
+ }
39
+ export function tryReadSharedJson(relativePath) {
40
+ try {
41
+ return readSharedJson(relativePath);
42
+ }
43
+ catch {
44
+ return undefined;
45
+ }
46
+ }
@@ -0,0 +1,2 @@
1
+ export type MaybePromise<R> = R | Promise<R>;
2
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/lib/core/types.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ // Shared type utilities (lib core).
2
+ export {};
@@ -0,0 +1,12 @@
1
+ import type { MaybePromise } from "./types.js";
2
+ export declare function delay(ms: number): Promise<void>;
3
+ export type RetryCfg = {
4
+ maxTimeoutMs?: number;
5
+ startTimeoutMs?: number;
6
+ backoffMul?: number;
7
+ /** Optional hook before the next retry; must not throw. */
8
+ onBeforeRetry?: (e: unknown) => MaybePromise<void>;
9
+ };
10
+ /** Retries `fn` on throw with exponential backoff until `maxTimeoutMs` is exceeded. */
11
+ export declare function retry<R>(fn: () => MaybePromise<R>, config: RetryCfg): Promise<R>;
12
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/core/utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,YAAY,CAAC,IAAI,CAAC,CAAC;CACpD,CAAC;AAQF,uFAAuF;AACvF,wBAAsB,KAAK,CAAC,CAAC,EAC3B,EAAE,EAAE,MAAM,YAAY,CAAC,CAAC,CAAC,EACzB,MAAM,EAAE,QAAQ,GACf,OAAO,CAAC,CAAC,CAAC,CAgBZ"}
@@ -0,0 +1,29 @@
1
+ // Small async helpers (delay, exponential backoff retry).
2
+ import { ONE_SEC_MS } from "./consts.js";
3
+ export function delay(ms) {
4
+ return new Promise((resolve) => setTimeout(resolve, ms));
5
+ }
6
+ const defaultCfg = {
7
+ maxTimeoutMs: ONE_SEC_MS * 32,
8
+ startTimeoutMs: ONE_SEC_MS,
9
+ backoffMul: 2,
10
+ };
11
+ /** Retries `fn` on throw with exponential backoff until `maxTimeoutMs` is exceeded. */
12
+ export async function retry(fn, config) {
13
+ const cfg = { ...defaultCfg, ...config };
14
+ let curTimeoutMs = cfg.startTimeoutMs;
15
+ while (true) {
16
+ try {
17
+ const res = await fn();
18
+ return res;
19
+ }
20
+ catch (e) {
21
+ if (cfg.onBeforeRetry)
22
+ await cfg.onBeforeRetry(e);
23
+ if (curTimeoutMs > cfg.maxTimeoutMs)
24
+ throw e;
25
+ await delay(curTimeoutMs);
26
+ curTimeoutMs = Math.floor(curTimeoutMs * cfg.backoffMul);
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,20 @@
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/messages.js";
6
+ export * from "./core/shared-assets.js";
7
+ export * from "./core/types.js";
8
+ export * from "./agent/session/agent-credentials-store.js";
9
+ export * from "./agent/session/inbox-id-to-mailbox-email.js";
10
+ export * from "./agent/auth/agent-jwt.js";
11
+ export * from "./agent/auth/agent-pow.js";
12
+ export * from "./agent/auth/agent-auth-http.js";
13
+ export * from "./agent/jmap/agent-jmap.js";
14
+ export * from "./agent/jmap/agent-jmap-verify.js";
15
+ export * from "./agent/session/agent-session.js";
16
+ export * from "./agent/session/agent-resolve-config.js";
17
+ export * from "./agent/session/agent-session-for-dir.js";
18
+ export * from "./agent/jmap/agent-help-content.js";
19
+ export * from "./agent/jmap/agent-vars.js";
20
+ //# 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,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAEhC,cAAc,4CAA4C,CAAC;AAC3D,cAAc,8CAA8C,CAAC;AAC7D,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iCAAiC,CAAC;AAChD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,mCAAmC,CAAC;AAClD,cAAc,kCAAkC,CAAC;AACjD,cAAc,yCAAyC,CAAC;AACxD,cAAc,0CAA0C,CAAC;AACzD,cAAc,oCAAoC,CAAC;AACnD,cAAc,4BAA4B,CAAC"}
package/esm/lib/mod.js ADDED
@@ -0,0 +1,19 @@
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/messages.js";
6
+ export * from "./core/shared-assets.js";
7
+ export * from "./core/types.js";
8
+ export * from "./agent/session/agent-credentials-store.js";
9
+ export * from "./agent/session/inbox-id-to-mailbox-email.js";
10
+ export * from "./agent/auth/agent-jwt.js";
11
+ export * from "./agent/auth/agent-pow.js";
12
+ export * from "./agent/auth/agent-auth-http.js";
13
+ export * from "./agent/jmap/agent-jmap.js";
14
+ export * from "./agent/jmap/agent-jmap-verify.js";
15
+ export * from "./agent/session/agent-session.js";
16
+ export * from "./agent/session/agent-resolve-config.js";
17
+ export * from "./agent/session/agent-session-for-dir.js";
18
+ export * from "./agent/jmap/agent-help-content.js";
19
+ 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":"AAmBA,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,210 @@
1
+ // Thin HTTP client for auth-service (PoW challenge → session → capability).
2
+ //
3
+ // Encapsulates the full PoW challenge → session → capability flow so callers
4
+ // (integration tests, the future agent skill, etc.) don't have to reimplement scrypt grinding.
5
+ //
6
+ // The PoW digest is scrypt-based and uses the SAME salt the auth-service
7
+ // uses on the verify path (see services/auth-service/src/crypto.ts). The
8
+ // client must therefore be configured with that salt — there is no public
9
+ // hash function here, the salt is part of the protocol.
10
+ import { scrypt } from "node:crypto";
11
+ import { DEFAULT_POW_SCRYPT_SALT_HEX } from "../core/consts.js";
12
+ // Mirror services/auth-service/src/crypto.ts exactly. Changing any of these
13
+ // constants on either side breaks PoW interop.
14
+ const SCRYPT_PARAMS = { N: 16384, r: 8, p: 1 };
15
+ const POW_HASH_BYTES = 64;
16
+ /** Thrown for any non-2xx HTTP response or malformed payload. */
17
+ export class AuthClientError extends Error {
18
+ status;
19
+ bodyText;
20
+ constructor(status, bodyText, message) {
21
+ super(message);
22
+ this.name = "AuthClientError";
23
+ this.status = status;
24
+ this.bodyText = bodyText;
25
+ }
26
+ }
27
+ export class AuthClient {
28
+ baseUrl;
29
+ scryptSaltHex;
30
+ constructor(options) {
31
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
32
+ this.scryptSaltHex = options.scryptSaltHex ?? DEFAULT_POW_SCRYPT_SALT_HEX;
33
+ }
34
+ /**
35
+ * Register a new inbox under `username`. Returns the freshly minted API key
36
+ * (the server only ever returns it once — the caller MUST persist it) and
37
+ * a session JWT.
38
+ */
39
+ async signup(username) {
40
+ const { challengeJWT, challenge, difficulty } = await this.fetchChallenge();
41
+ const { powHex, nonce } = await this.solvePoW(challenge, difficulty);
42
+ const { sessionJWT, data } = await this.postSession(challengeJWT, {
43
+ powHex,
44
+ nonce: nonce.toString(),
45
+ username,
46
+ });
47
+ if (typeof data.apiKey !== "string") {
48
+ throw new AuthClientError(200, JSON.stringify(data), "Signup response missing apiKey.");
49
+ }
50
+ return { apiKey: data.apiKey, sessionJWT };
51
+ }
52
+ /** Exchange an existing API key for a fresh session JWT. */
53
+ async login(apiKey) {
54
+ const { challengeJWT, challenge, difficulty } = await this.fetchChallenge();
55
+ const { powHex, nonce } = await this.solvePoW(challenge, difficulty);
56
+ const { sessionJWT } = await this.postSession(challengeJWT, {
57
+ powHex,
58
+ nonce: nonce.toString(),
59
+ apiKey,
60
+ });
61
+ return { sessionJWT };
62
+ }
63
+ /**
64
+ * Exchange a session JWT for a short-lived capability JWT (audience:
65
+ * api-service).
66
+ */
67
+ async renew(sessionJWT) {
68
+ const res = await fetch(`${this.baseUrl}/api/v1/capability`, {
69
+ method: "POST",
70
+ headers: { Authorization: `Bearer ${sessionJWT}` },
71
+ });
72
+ const text = await res.text();
73
+ if (!res.ok) {
74
+ throw new AuthClientError(res.status, text, `auth-service capability returned ${res.status}: ${text}`);
75
+ }
76
+ const capabilityJWT = readBearerToken(res.headers.get("Authorization"), "Capability response missing Authorization bearer token.");
77
+ return { capabilityJWT };
78
+ }
79
+ async fetchChallenge() {
80
+ const res = await fetch(`${this.baseUrl}/api/v1/challenge`, {
81
+ method: "POST",
82
+ });
83
+ const text = await res.text();
84
+ if (!res.ok) {
85
+ throw new AuthClientError(res.status, text, `auth-service challenge returned ${res.status}: ${text}`);
86
+ }
87
+ const challengeJWT = readBearerToken(res.headers.get("Authorization"), "Challenge response missing Authorization bearer token.");
88
+ const payload = decodeJwtPayload(challengeJWT);
89
+ if (typeof payload.jti !== "string" ||
90
+ typeof payload.difficulty !== "number") {
91
+ throw new AuthClientError(res.status, challengeJWT, "Challenge JWT payload is malformed (missing jti or difficulty).");
92
+ }
93
+ return {
94
+ challengeJWT,
95
+ challenge: payload.jti,
96
+ difficulty: payload.difficulty,
97
+ };
98
+ }
99
+ async postSession(challengeJWT, body) {
100
+ const res = await fetch(`${this.baseUrl}/api/v1/session`, {
101
+ method: "POST",
102
+ headers: {
103
+ "Content-Type": "application/json",
104
+ Authorization: `Bearer ${challengeJWT}`,
105
+ },
106
+ body: JSON.stringify(body),
107
+ });
108
+ const text = await res.text();
109
+ if (!res.ok) {
110
+ throw new AuthClientError(res.status, text, `auth-service session returned ${res.status}: ${text}`);
111
+ }
112
+ const sessionJWT = readBearerToken(res.headers.get("Authorization"), "Session response missing Authorization bearer token.");
113
+ let data = {};
114
+ if (text.trim().length > 0) {
115
+ try {
116
+ data = JSON.parse(text);
117
+ }
118
+ catch {
119
+ throw new AuthClientError(res.status, text, "auth-service session returned non-JSON body.");
120
+ }
121
+ }
122
+ return { sessionJWT, data };
123
+ }
124
+ async parseJsonOrThrow(res, endpoint) {
125
+ const text = await res.text();
126
+ if (!res.ok) {
127
+ throw new AuthClientError(res.status, text, `auth-service ${endpoint} returned ${res.status}: ${text}`);
128
+ }
129
+ try {
130
+ return JSON.parse(text);
131
+ }
132
+ catch {
133
+ throw new AuthClientError(res.status, text, `auth-service ${endpoint} returned non-JSON body.`);
134
+ }
135
+ }
136
+ /**
137
+ * Brute-force a PoW nonce. Mirrors `generatePow` in
138
+ * services/auth-service/src/crypto.ts: scrypt(`${challenge}:${nonce}`, salt,
139
+ * 64) until `difficulty` leading bits of the digest are zero.
140
+ *
141
+ * Expected work at the server's POW_DIFFICULTY=6 is ~2^6 = 64 attempts; well
142
+ * within the challenge JWT's 3-minute TTL.
143
+ */
144
+ async solvePoW(challenge, difficulty) {
145
+ let nonce = 0n;
146
+ while (true) {
147
+ const digest = await scryptHash(`${challenge}:${nonce}`, this.scryptSaltHex);
148
+ if (hasLeadingZeroBits(digest, difficulty)) {
149
+ return { powHex: bytesToHex(digest), nonce };
150
+ }
151
+ nonce++;
152
+ }
153
+ }
154
+ }
155
+ function scryptHash(data, salt) {
156
+ const bytes = new TextEncoder().encode(data);
157
+ return new Promise((resolve, reject) => {
158
+ scrypt(bytes, salt, POW_HASH_BYTES, SCRYPT_PARAMS, (err, derived) => {
159
+ if (err)
160
+ return reject(err);
161
+ resolve(new Uint8Array(derived));
162
+ });
163
+ });
164
+ }
165
+ function hasLeadingZeroBits(hash, bits) {
166
+ if (bits > hash.length * 8)
167
+ return false;
168
+ const fullBytes = Math.floor(bits / 8);
169
+ const remainingBits = bits % 8;
170
+ for (let i = 0; i < fullBytes; i++) {
171
+ if (hash[i] !== 0)
172
+ return false;
173
+ }
174
+ if (remainingBits > 0) {
175
+ const mask = (0xff << (8 - remainingBits)) & 0xff;
176
+ if ((hash[fullBytes] & mask) !== 0)
177
+ return false;
178
+ }
179
+ return true;
180
+ }
181
+ function bytesToHex(bytes) {
182
+ let hex = "";
183
+ for (let i = 0; i < bytes.length; i++) {
184
+ hex += bytes[i].toString(16).padStart(2, "0");
185
+ }
186
+ return hex;
187
+ }
188
+ function decodeJwtPayload(jwt) {
189
+ const parts = jwt.split(".");
190
+ if (parts.length < 2) {
191
+ throw new Error("Malformed JWT: expected at least 2 dot-separated segments.");
192
+ }
193
+ const payloadB64Url = parts[1];
194
+ const padLen = (4 - (payloadB64Url.length % 4)) % 4;
195
+ const base64 = payloadB64Url
196
+ .replace(/-/g, "+")
197
+ .replace(/_/g, "/")
198
+ .padEnd(payloadB64Url.length + padLen, "=");
199
+ return JSON.parse(atob(base64));
200
+ }
201
+ function readBearerToken(headerValue, missingError) {
202
+ if (!headerValue) {
203
+ throw new Error(missingError);
204
+ }
205
+ const match = /^\s*Bearer\s+(.+?)\s*$/i.exec(headerValue);
206
+ if (!match || !match[1]) {
207
+ throw new Error("Authorization header must use Bearer scheme.");
208
+ }
209
+ return match[1];
210
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@atomicmail/langchain",
3
+ "version": "0.3.14",
4
+ "description": "Atomic Mail LangChain toolkit — register, jmap_request, and help tools for JS/TS agents.",
5
+ "keywords": [
6
+ "atomic-mail",
7
+ "atomicmail",
8
+ "langchain",
9
+ "toolkit",
10
+ "agent",
11
+ "ai",
12
+ "jmap",
13
+ "email",
14
+ "proof-of-work"
15
+ ],
16
+ "author": "Atomic Mail",
17
+ "homepage": "https://atomicmail.ai?utm_source=npm&utm_medium=package&utm_campaign=website",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/Atomic-Mail/atomic-mail-agentic.git"
21
+ },
22
+ "license": "MIT",
23
+ "bugs": {
24
+ "url": "https://github.com/Atomic-Mail/atomic-mail-agentic/issues"
25
+ },
26
+ "module": "./esm/langchain/mod.js",
27
+ "exports": {
28
+ ".": {
29
+ "import": "./esm/langchain/mod.js"
30
+ }
31
+ },
32
+ "scripts": {},
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "atomicmail": {
37
+ "channel": "default"
38
+ },
39
+ "engines": {
40
+ "node": ">=20"
41
+ },
42
+ "dependencies": {
43
+ "@langchain/core": "^1.1.49",
44
+ "zod": "^4.4.3",
45
+ "langchain": "^1.4.5"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^20.12.0"
49
+ },
50
+ "_generatedBy": "dnt@dev"
51
+ }
@@ -0,0 +1,46 @@
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": {
12
+ "inMailbox": "$INBOX_MAILBOX_ID"
13
+ },
14
+ "sort": [
15
+ {
16
+ "property": "receivedAt",
17
+ "isAscending": false
18
+ }
19
+ ],
20
+ "limit": 50
21
+ },
22
+ "q0"
23
+ ],
24
+ [
25
+ "Email/get",
26
+ {
27
+ "accountId": "$ACCOUNT_ID",
28
+ "#ids": {
29
+ "resultOf": "q0",
30
+ "name": "Email/query",
31
+ "path": "/ids"
32
+ },
33
+ "properties": [
34
+ "id",
35
+ "threadId",
36
+ "receivedAt",
37
+ "from",
38
+ "to",
39
+ "subject",
40
+ "preview"
41
+ ]
42
+ },
43
+ "g0"
44
+ ]
45
+ ]
46
+ }
@@ -0,0 +1,97 @@
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": [
13
+ "$MAIL_ID"
14
+ ],
15
+ "properties": [
16
+ "id",
17
+ "threadId",
18
+ "from",
19
+ "replyTo",
20
+ "subject",
21
+ "messageId"
22
+ ]
23
+ },
24
+ "g0"
25
+ ],
26
+ [
27
+ "Email/set",
28
+ {
29
+ "accountId": "$ACCOUNT_ID",
30
+ "create": {
31
+ "d1": {
32
+ "mailboxIds": {
33
+ "$INBOX_MAILBOX_ID": true
34
+ },
35
+ "from": [
36
+ {
37
+ "email": "$INBOX"
38
+ }
39
+ ],
40
+ "#to": {
41
+ "resultOf": "g0",
42
+ "name": "Email/get",
43
+ "path": "/list/0/replyTo"
44
+ },
45
+ "#subject": {
46
+ "resultOf": "g0",
47
+ "name": "Email/get",
48
+ "path": "/list/0/subject"
49
+ },
50
+ "#inReplyTo": {
51
+ "resultOf": "g0",
52
+ "name": "Email/get",
53
+ "path": "/list/0/messageId"
54
+ },
55
+ "textBody": [
56
+ {
57
+ "partId": "b",
58
+ "type": "text/plain"
59
+ }
60
+ ],
61
+ "bodyValues": {
62
+ "b": {
63
+ "value": "$BODY"
64
+ }
65
+ },
66
+ "keywords": {
67
+ "$draft": true
68
+ }
69
+ }
70
+ }
71
+ },
72
+ "c0"
73
+ ],
74
+ [
75
+ "EmailSubmission/set",
76
+ {
77
+ "accountId": "$ACCOUNT_ID",
78
+ "create": {
79
+ "s1": {
80
+ "emailId": "#d1",
81
+ "envelope": {
82
+ "mailFrom": {
83
+ "email": "$INBOX"
84
+ },
85
+ "#rcptTo": {
86
+ "resultOf": "g0",
87
+ "name": "Email/get",
88
+ "path": "/list/0/replyTo"
89
+ }
90
+ }
91
+ }
92
+ }
93
+ },
94
+ "c1"
95
+ ]
96
+ ]
97
+ }