@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.
- package/README.md +51 -12
- package/SKILL.md +56 -38
- package/esm/_dnt.polyfills.d.ts +101 -0
- package/esm/_dnt.polyfills.d.ts.map +1 -0
- package/esm/_dnt.polyfills.js +127 -0
- package/esm/lib/agent/auth/agent-auth-http.d.ts.map +1 -0
- package/esm/lib/agent/auth/agent-auth-http.js +85 -0
- package/esm/lib/{src → agent/auth}/agent-jwt.d.ts +0 -2
- package/esm/lib/agent/auth/agent-jwt.d.ts.map +1 -0
- package/esm/lib/{src → agent/auth}/agent-jwt.js +0 -2
- package/esm/lib/agent/auth/agent-pow.d.ts.map +1 -0
- package/esm/lib/{src → agent/jmap}/agent-help-content.d.ts +1 -0
- package/esm/lib/agent/jmap/agent-help-content.d.ts.map +1 -0
- package/esm/lib/{src → agent/jmap}/agent-help-content.js +46 -19
- package/esm/lib/{src → agent/jmap}/agent-jmap.d.ts +7 -0
- package/esm/lib/agent/jmap/agent-jmap.d.ts.map +1 -0
- package/esm/lib/{src → agent/jmap}/agent-jmap.js +63 -3
- package/esm/lib/agent/jmap/agent-vars.d.ts.map +1 -0
- package/esm/lib/{src → agent/session}/agent-credentials-store.d.ts +2 -0
- package/esm/lib/agent/session/agent-credentials-store.d.ts.map +1 -0
- package/esm/lib/{src → agent/session}/agent-credentials-store.js +2 -0
- package/esm/lib/agent/session/agent-resolve-config.d.ts +24 -0
- package/esm/lib/agent/session/agent-resolve-config.d.ts.map +1 -0
- package/esm/lib/agent/session/agent-resolve-config.js +70 -0
- package/esm/lib/{src → agent/session}/agent-session.d.ts +5 -0
- package/esm/lib/agent/session/agent-session.d.ts.map +1 -0
- package/esm/lib/{src → agent/session}/agent-session.js +46 -10
- package/esm/lib/core/consts.d.ts.map +1 -0
- package/esm/lib/core/read-npm-package-readme.d.ts +6 -0
- package/esm/lib/core/read-npm-package-readme.d.ts.map +1 -0
- package/esm/lib/core/read-npm-package-readme.js +66 -0
- package/esm/lib/core/types.d.ts +2 -0
- package/esm/lib/core/types.d.ts.map +1 -0
- package/esm/lib/core/types.js +1 -0
- package/esm/lib/core/utils.d.ts +10 -0
- package/esm/lib/core/utils.d.ts.map +1 -0
- package/esm/lib/core/utils.js +28 -0
- package/esm/lib/mod.d.ts +15 -0
- package/esm/lib/mod.d.ts.map +1 -0
- package/esm/lib/mod.js +14 -0
- package/esm/lib/network/auth-client.d.ts +57 -0
- package/esm/lib/network/auth-client.d.ts.map +1 -0
- package/esm/lib/network/auth-client.js +211 -0
- package/esm/skill/cli.d.ts +3 -0
- package/esm/skill/cli.d.ts.map +1 -0
- package/esm/skill/{scripts/cli.js → cli.js} +18 -9
- package/package.json +4 -4
- package/presets/list_inbox.json +39 -0
- package/presets/reply.json +75 -0
- package/presets/send_mail.json +42 -0
- package/esm/lib/src/agent-auth-http.d.ts.map +0 -1
- package/esm/lib/src/agent-auth-http.js +0 -76
- package/esm/lib/src/agent-credentials-store.d.ts.map +0 -1
- package/esm/lib/src/agent-help-content.d.ts.map +0 -1
- package/esm/lib/src/agent-jmap.d.ts.map +0 -1
- package/esm/lib/src/agent-jwt.d.ts.map +0 -1
- package/esm/lib/src/agent-pow.d.ts.map +0 -1
- package/esm/lib/src/agent-session.d.ts.map +0 -1
- package/esm/lib/src/agent-vars.d.ts.map +0 -1
- package/esm/lib/src/consts.d.ts.map +0 -1
- package/esm/skill/scripts/cli.d.ts +0 -3
- package/esm/skill/scripts/cli.d.ts.map +0 -1
- /package/esm/lib/{src → agent/auth}/agent-auth-http.d.ts +0 -0
- /package/esm/lib/{src → agent/auth}/agent-pow.d.ts +0 -0
- /package/esm/lib/{src → agent/auth}/agent-pow.js +0 -0
- /package/esm/lib/{src → agent/jmap}/agent-vars.d.ts +0 -0
- /package/esm/lib/{src → agent/jmap}/agent-vars.js +0 -0
- /package/esm/lib/{src → core}/consts.d.ts +0 -0
- /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
|
+
}
|
package/esm/lib/mod.d.ts
ADDED
|
@@ -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 @@
|
|
|
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 "
|
|
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.
|
|
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-
|
|
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-
|
|
23
|
+
"url": "https://github.com/atomic-mail/agentic-clients/issues"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {},
|
|
26
26
|
"bin": {
|
|
27
|
-
"atomicmail": "./esm/skill/
|
|
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"}
|