gws-axi 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,23 @@
1
1
  /**
2
2
  * RFC 5322 message builder for draft creation.
3
3
  *
4
- * Drafts go out as `multipart/alternative`: the `--body` markdown verbatim as
5
- * `text/plain`, and its rendered form as `text/html`. That structure is what
6
- * Gmail's own composer produces, and emitting it is load-bearing rather than
7
- * cosmetic — a single-part `text/plain` draft drops Gmail's composer into
8
- * plain-text mode, and on send Gmail hard-wraps the body at ~70 columns with
9
- * real CRLFs and ships no HTML alternative. The compose window still shows the
10
- * original unwrapped paragraphs, so the recipient's ragged columns are
11
- * invisible until after the message is gone.
4
+ * Drafts go out as a single `text/html` part carrying the rendered `--body`
5
+ * markdown. Gmail's composer adopts exactly one part of a draft as its editing
6
+ * surface and discards the rest, so a message offering only HTML leaves it no
7
+ * choice — the composer opens in rich text, our markup survives the human's
8
+ * edits, and Gmail generates the `text/plain` alternative itself on send.
9
+ *
10
+ * Both alternatives were tried and rejected against live sends:
11
+ *
12
+ * - Single-part `text/plain` (the original bug) drops the composer into
13
+ * plain-text mode. On send Gmail hard-wraps at ~70 columns with real CRLFs
14
+ * and ships no HTML alternative, so recipients get ragged columns nothing
15
+ * can reflow — while the compose window still shows the original unwrapped
16
+ * paragraphs, hiding the damage until the message is gone.
17
+ * - `multipart/alternative` (plain + HTML) fixes the wrapping but is a coin
18
+ * flip: two identically-built drafts behaved differently, one keeping our
19
+ * rendered markup and one adopting the plain part and delivering the raw
20
+ * markdown source to the reader — `**bold**` and `[text](url)` and all.
12
21
  *
13
22
  * See specs/commands/gmail-draft.md ("Message structure", "Body rendering").
14
23
  */
@@ -19,8 +28,14 @@ export interface ComposeFields {
19
28
  cc?: string[];
20
29
  bcc?: string[];
21
30
  subject: string;
22
- /** Markdown source. Rendered into the text/html part, kept verbatim as text/plain. */
31
+ /** Markdown source, rendered to the HTML body — unless `plain` is set. */
23
32
  body: string;
33
+ /**
34
+ * Treat `body` as literal text rather than markdown. Still sends a single
35
+ * `text/html` part (reverting to `text/plain` is the original defect); the
36
+ * HTML is built structurally instead of through the markdown renderer.
37
+ */
38
+ plain?: boolean;
24
39
  }
25
40
  /** Split a comma-separated recipient flag into trimmed, non-empty addresses. */
26
41
  export declare function parseRecipients(value: string): string[];
@@ -34,6 +49,13 @@ export declare function parseRecipients(value: string): string[];
34
49
  * DRAFT_HELP tells callers not to hard-wrap.
35
50
  */
36
51
  export declare function renderMarkdown(body: string): string;
52
+ /**
53
+ * Render the body as literal text: no markdown, so `*`, `_`, `#` and friends
54
+ * reach the reader exactly as written. Blank line starts a paragraph, a single
55
+ * newline is a line break — the same block semantics as the markdown path, so
56
+ * `--plain` changes how the body is interpreted, not how it is laid out.
57
+ */
58
+ export declare function renderPlainText(body: string): string;
37
59
  /**
38
60
  * Build a base64url-encoded RFC 5322 message suitable for
39
61
  * `users.drafts.create` / `users.messages.send` `raw` fields.
@@ -1,18 +1,26 @@
1
1
  /**
2
2
  * RFC 5322 message builder for draft creation.
3
3
  *
4
- * Drafts go out as `multipart/alternative`: the `--body` markdown verbatim as
5
- * `text/plain`, and its rendered form as `text/html`. That structure is what
6
- * Gmail's own composer produces, and emitting it is load-bearing rather than
7
- * cosmetic — a single-part `text/plain` draft drops Gmail's composer into
8
- * plain-text mode, and on send Gmail hard-wraps the body at ~70 columns with
9
- * real CRLFs and ships no HTML alternative. The compose window still shows the
10
- * original unwrapped paragraphs, so the recipient's ragged columns are
11
- * invisible until after the message is gone.
4
+ * Drafts go out as a single `text/html` part carrying the rendered `--body`
5
+ * markdown. Gmail's composer adopts exactly one part of a draft as its editing
6
+ * surface and discards the rest, so a message offering only HTML leaves it no
7
+ * choice — the composer opens in rich text, our markup survives the human's
8
+ * edits, and Gmail generates the `text/plain` alternative itself on send.
9
+ *
10
+ * Both alternatives were tried and rejected against live sends:
11
+ *
12
+ * - Single-part `text/plain` (the original bug) drops the composer into
13
+ * plain-text mode. On send Gmail hard-wraps at ~70 columns with real CRLFs
14
+ * and ships no HTML alternative, so recipients get ragged columns nothing
15
+ * can reflow — while the compose window still shows the original unwrapped
16
+ * paragraphs, hiding the damage until the message is gone.
17
+ * - `multipart/alternative` (plain + HTML) fixes the wrapping but is a coin
18
+ * flip: two identically-built drafts behaved differently, one keeping our
19
+ * rendered markup and one adopting the plain part and delivering the raw
20
+ * markdown source to the reader — `**bold**` and `[text](url)` and all.
12
21
  *
13
22
  * See specs/commands/gmail-draft.md ("Message structure", "Body rendering").
14
23
  */
15
- import { randomUUID } from "node:crypto";
16
24
  import { marked } from "marked";
17
25
  /** Split a comma-separated recipient flag into trimmed, non-empty addresses. */
18
26
  export function parseRecipients(value) {
@@ -44,8 +52,42 @@ export function renderMarkdown(body) {
44
52
  const fragment = marked.parse(body, { gfm: true, breaks: true, async: false });
45
53
  return `<html><body>${fragment}</body></html>`;
46
54
  }
55
+ function escapeHtml(text) {
56
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
57
+ }
58
+ /**
59
+ * Keep indentation and column alignment that HTML would otherwise collapse.
60
+ *
61
+ * Leading spaces become `&nbsp;` outright; an internal run keeps one real
62
+ * space — a wrap point, so the line still reflows — and pads the rest. This is
63
+ * what Gmail itself does when it converts plain text to HTML, so it is known to
64
+ * survive the composer.
65
+ */
66
+ function preserveSpacing(line) {
67
+ return line
68
+ .replace(/\t/g, " ")
69
+ .replace(/^ +/, (run) => "&nbsp;".repeat(run.length))
70
+ .replace(/ {2,}/g, (run) => ` ${"&nbsp;".repeat(run.length - 1)}`);
71
+ }
72
+ /**
73
+ * Render the body as literal text: no markdown, so `*`, `_`, `#` and friends
74
+ * reach the reader exactly as written. Blank line starts a paragraph, a single
75
+ * newline is a line break — the same block semantics as the markdown path, so
76
+ * `--plain` changes how the body is interpreted, not how it is laid out.
77
+ */
78
+ export function renderPlainText(body) {
79
+ const blocks = body
80
+ .replace(/\r\n/g, "\n")
81
+ .split(/\n{2,}/)
82
+ .filter((block) => block.trim() !== "")
83
+ .map((block) => {
84
+ const lines = escapeHtml(block).split("\n").map(preserveSpacing);
85
+ return `<p>${lines.join("<br>")}</p>`;
86
+ });
87
+ return `<html><body>${blocks.join("\n")}</body></html>`;
88
+ }
47
89
  /** Base64 with CRLF line endings wrapped at 76 columns (RFC 2045). */
48
- function encodePart(text) {
90
+ function encodeBody(text) {
49
91
  return Buffer.from(text, "utf8")
50
92
  .toString("base64")
51
93
  .replace(/(.{76})/g, "$1\r\n");
@@ -55,9 +97,6 @@ function encodePart(text) {
55
97
  * `users.drafts.create` / `users.messages.send` `raw` fields.
56
98
  */
57
99
  export function buildRawMessage(fields) {
58
- // Unique per message, so it can never collide with body content — no
59
- // scanning the body for the boundary, no retry loop.
60
- const boundary = `=_gws-axi_${randomUUID()}`;
61
100
  const headers = [`From: ${fields.from}`, `To: ${fields.to.join(", ")}`];
62
101
  if (fields.cc?.length)
63
102
  headers.push(`Cc: ${fields.cc.join(", ")}`);
@@ -65,14 +104,10 @@ export function buildRawMessage(fields) {
65
104
  headers.push(`Bcc: ${fields.bcc.join(", ")}`);
66
105
  headers.push(`Subject: ${encodeHeaderValue(fields.subject)}`);
67
106
  headers.push("MIME-Version: 1.0");
68
- headers.push(`Content-Type: multipart/alternative; boundary="${boundary}"`);
69
- // RFC 2046 orders alternatives least- to most-faithful; clients render the
70
- // last part they understand, so text/html goes second.
71
- const parts = [
72
- ['Content-Type: text/plain; charset="UTF-8"', fields.body],
73
- ['Content-Type: text/html; charset="UTF-8"', renderMarkdown(fields.body)],
74
- ].map(([contentType, content]) => `--${boundary}\r\n${contentType}\r\nContent-Transfer-Encoding: base64\r\n\r\n${encodePart(content)}`);
75
- const raw = `${headers.join("\r\n")}\r\n\r\n${parts.join("\r\n")}\r\n--${boundary}--`;
107
+ headers.push('Content-Type: text/html; charset="UTF-8"');
108
+ headers.push("Content-Transfer-Encoding: base64");
109
+ const html = fields.plain ? renderPlainText(fields.body) : renderMarkdown(fields.body);
110
+ const raw = `${headers.join("\r\n")}\r\n\r\n${encodeBody(html)}`;
76
111
  return Buffer.from(raw, "utf8").toString("base64url");
77
112
  }
78
113
  //# sourceMappingURL=compose.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../../../src/commands/gmail/compose.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../../../src/commands/gmail/compose.ts"],"names":[],"mappings":"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"}
@@ -1,28 +1,13 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { buildRawMessage, parseRecipients } from "./compose.js";
3
- /** Decode a base64url raw message into its header block and decoded MIME parts. */
3
+ /** Decode a base64url raw message into its header block and decoded HTML body. */
4
4
  function decodeRaw(raw) {
5
5
  const text = Buffer.from(raw, "base64url").toString("utf8");
6
6
  const idx = text.indexOf("\r\n\r\n");
7
- const header = text.slice(0, idx);
8
- const boundary = /boundary="([^"]+)"/.exec(header)?.[1] ?? "";
9
- expect(boundary).not.toBe("");
10
- // Drop the preamble (before the first boundary) and the closing `--<b>--`.
11
- const parts = text
12
- .slice(idx + 4)
13
- .split(`--${boundary}`)
14
- .slice(1, -1)
15
- .map((chunk) => {
16
- const split = chunk.indexOf("\r\n\r\n");
17
- const partHeaders = chunk.slice(0, split);
18
- const b64 = chunk.slice(split + 4).replace(/\r\n/g, "");
19
- return {
20
- contentType: /Content-Type: ([^;\r\n]+)/.exec(partHeaders)?.[1] ?? "",
21
- content: Buffer.from(b64, "base64").toString("utf8"),
22
- };
23
- });
24
- const find = (type) => parts.find((p) => p.contentType === type)?.content ?? "";
25
- return { header, boundary, parts, plain: find("text/plain"), html: find("text/html") };
7
+ return {
8
+ header: text.slice(0, idx),
9
+ body: Buffer.from(text.slice(idx + 4).replace(/\r\n/g, ""), "base64").toString("utf8"),
10
+ };
26
11
  }
27
12
  describe("parseRecipients", () => {
28
13
  it("splits, trims, and drops empties", () => {
@@ -37,76 +22,58 @@ describe("parseRecipients", () => {
37
22
  });
38
23
  });
39
24
  describe("buildRawMessage", () => {
40
- it("emits the core headers and a multipart/alternative body", () => {
25
+ it("emits the core headers and a single text/html body", () => {
41
26
  const raw = buildRawMessage({
42
27
  from: "me@x.com",
43
28
  to: ["alice@x.com", "bob@x.com"],
44
29
  subject: "Hello",
45
30
  body: "Line one\nLine two",
46
31
  });
47
- const { header, boundary, parts } = decodeRaw(raw);
32
+ const { header, body } = decodeRaw(raw);
48
33
  expect(header).toContain("From: me@x.com");
49
34
  expect(header).toContain("To: alice@x.com, bob@x.com");
50
35
  expect(header).toContain("Subject: Hello");
51
- expect(header).toContain(`Content-Type: multipart/alternative; boundary="${boundary}"`);
52
- // RFC 2046: least-faithful alternative first, so clients pick the HTML.
53
- expect(parts.map((p) => p.contentType)).toEqual(["text/plain", "text/html"]);
36
+ expect(header).toContain('Content-Type: text/html; charset="UTF-8"');
37
+ expect(body).toMatch(/^<html><body>/);
38
+ expect(body).toMatch(/<\/body><\/html>$/);
54
39
  });
55
- it("delimits both parts and terminates the multipart body", () => {
56
- const raw = buildRawMessage({
57
- from: "me@x.com",
58
- to: ["a@x.com"],
59
- subject: "s",
60
- body: "hello",
61
- });
62
- const { boundary } = decodeRaw(raw);
63
- const text = Buffer.from(raw, "base64url").toString("utf8");
64
- expect(text.split(`--${boundary}\r\n`)).toHaveLength(3); // two part delimiters
65
- expect(text.endsWith(`--${boundary}--`)).toBe(true);
66
- });
67
- it("uses a fresh boundary per message", () => {
68
- const fields = { from: "me@x.com", to: ["a@x.com"], subject: "s", body: "b" };
69
- expect(decodeRaw(buildRawMessage(fields)).boundary).not.toBe(decodeRaw(buildRawMessage(fields)).boundary);
70
- });
71
- it("keeps the markdown source verbatim in the text/plain part", () => {
72
- const body = "# Heading\n\nSome **bold** text.\n\n- one\n- two";
73
- const { plain } = decodeRaw(buildRawMessage({ from: "me@x.com", to: ["a@x.com"], subject: "s", body }));
74
- expect(plain).toBe(body);
75
- });
76
- it("renders markdown into the text/html part", () => {
77
- const { html } = decodeRaw(buildRawMessage({
40
+ it("offers no text/plain alternative for the composer to adopt", () => {
41
+ // A plain part reopens the coin flip that delivered raw markdown to the
42
+ // reader — see specs/commands/gmail-draft.md.
43
+ const { header } = decodeRaw(buildRawMessage({ from: "me@x.com", to: ["a@x.com"], subject: "s", body: "b" }));
44
+ expect(header).not.toContain("multipart");
45
+ expect(header).not.toContain("text/plain");
46
+ expect(header).not.toContain("boundary");
47
+ });
48
+ it("renders markdown to HTML", () => {
49
+ const { body } = decodeRaw(buildRawMessage({
78
50
  from: "me@x.com",
79
51
  to: ["a@x.com"],
80
52
  subject: "s",
81
- body: "Some **bold** text.\n\n- one\n- two",
53
+ body: "Some **bold** text and a [link](https://axi.md).\n\n- one\n- two",
82
54
  }));
83
- expect(html).toContain("<strong>bold</strong>");
84
- expect(html).toContain("<li>one</li>");
85
- expect(html).toMatch(/^<html><body>/);
86
- expect(html).toMatch(/<\/body><\/html>$/);
55
+ expect(body).toContain("<strong>bold</strong>");
56
+ expect(body).toContain('<a href="https://axi.md">link</a>');
57
+ expect(body).toContain("<li>one</li>");
87
58
  });
88
59
  it("maps a blank line to a new paragraph and a single newline to <br>", () => {
89
- const { html } = decodeRaw(buildRawMessage({
60
+ const { body } = decodeRaw(buildRawMessage({
90
61
  from: "me@x.com",
91
62
  to: ["a@x.com"],
92
63
  subject: "s",
93
64
  body: "First para.\n\nSecond line one\nsecond line two",
94
65
  }));
95
- // Two paragraphs...
96
- expect(html.match(/<p>/g)).toHaveLength(2);
97
- // ...and the intra-paragraph newline survives as a break, not a space.
98
- expect(html).toContain("Second line one<br>second line two");
66
+ expect(body.match(/<p>/g)).toHaveLength(2);
67
+ expect(body).toContain("Second line one<br>second line two");
99
68
  });
100
- it("never breaks a long paragraph inside the decoded content of either part", () => {
69
+ it("never breaks a long paragraph inside the decoded body", () => {
101
70
  // The 76-column wrapping is base64 transport only — it must not survive
102
71
  // decoding, or the recipient sees the ragged columns this format exists
103
72
  // to prevent.
104
- const body = `${"word ".repeat(60).trim()}.`;
105
- const { plain, html } = decodeRaw(buildRawMessage({ from: "me@x.com", to: ["a@x.com"], subject: "s", body }));
106
- expect(plain).toBe(body);
107
- expect(plain).not.toContain("\r\n");
108
- expect(html).not.toContain("\r\n");
109
- expect(html).toContain(body);
73
+ const text = `${"word ".repeat(60).trim()}.`;
74
+ const { body } = decodeRaw(buildRawMessage({ from: "me@x.com", to: ["a@x.com"], subject: "s", body: text }));
75
+ expect(body).not.toContain("\r\n");
76
+ expect(body).toContain(text);
110
77
  });
111
78
  it("includes Cc/Bcc only when provided", () => {
112
79
  const withCc = decodeRaw(buildRawMessage({
@@ -123,8 +90,8 @@ describe("buildRawMessage", () => {
123
90
  expect(without).not.toContain("Cc:");
124
91
  expect(without).not.toContain("Bcc:");
125
92
  });
126
- it("RFC 2047 encodes non-ASCII subjects but leaves both parts decodable", () => {
127
- const { header, plain, html } = decodeRaw(buildRawMessage({
93
+ it("RFC 2047 encodes non-ASCII subjects but leaves the body decodable", () => {
94
+ const { header, body } = decodeRaw(buildRawMessage({
128
95
  from: "me@x.com",
129
96
  to: ["a@x.com"],
130
97
  subject: "café ☕",
@@ -132,8 +99,7 @@ describe("buildRawMessage", () => {
132
99
  }));
133
100
  expect(header).toContain("Subject: =?UTF-8?B?");
134
101
  expect(header).not.toContain("café");
135
- expect(plain).toBe("naïve résumé — ☕");
136
- expect(html).toContain("naïve résumé — ☕");
102
+ expect(body).toContain("naïve résumé — ☕");
137
103
  });
138
104
  it("produces valid base64url (no +,/,= padding chars)", () => {
139
105
  const raw = buildRawMessage({
@@ -143,7 +109,58 @@ describe("buildRawMessage", () => {
143
109
  body: "x".repeat(200), // force base64 line wrapping
144
110
  });
145
111
  expect(raw).not.toMatch(/[+/=]/);
146
- expect(decodeRaw(raw).plain).toBe("x".repeat(200));
112
+ expect(decodeRaw(raw).body).toContain("x".repeat(200));
113
+ });
114
+ });
115
+ describe("buildRawMessage --plain", () => {
116
+ const plainBody = (body) => decodeRaw(buildRawMessage({ from: "me@x.com", to: ["a@x.com"], subject: "s", body, plain: true })).body;
117
+ it("still sends a single text/html part", () => {
118
+ // Reverting to text/plain is the original defect; --plain changes how the
119
+ // body is interpreted, not the message structure.
120
+ const { header } = decodeRaw(buildRawMessage({ from: "me@x.com", to: ["a@x.com"], subject: "s", body: "b", plain: true }));
121
+ expect(header).toContain('Content-Type: text/html; charset="UTF-8"');
122
+ expect(header).not.toContain("multipart");
123
+ expect(header).not.toContain("text/plain");
124
+ });
125
+ it("leaves markdown syntax literal", () => {
126
+ const body = plainBody("Some **bold** text, a [link](https://axi.md), and _underscores_.");
127
+ expect(body).toContain("**bold**");
128
+ expect(body).toContain("[link](https://axi.md)");
129
+ expect(body).toContain("_underscores_");
130
+ expect(body).not.toContain("<strong>");
131
+ expect(body).not.toContain("<a href");
132
+ });
133
+ it("does not turn a leading # or - into a heading or list", () => {
134
+ const body = plainBody("# not a heading\n- not a list item");
135
+ expect(body).toContain("# not a heading");
136
+ expect(body).toContain("- not a list item");
137
+ expect(body).not.toContain("<h1>");
138
+ expect(body).not.toContain("<li>");
139
+ });
140
+ it("escapes HTML so tags reach the reader as text", () => {
141
+ const body = plainBody("Use <script>alert(1)</script> & <b>tags</b> literally");
142
+ expect(body).toContain("&lt;script&gt;alert(1)&lt;/script&gt;");
143
+ expect(body).toContain("&amp;");
144
+ expect(body).toContain("&lt;b&gt;tags&lt;/b&gt;");
145
+ });
146
+ it("keeps the same block semantics as the markdown path", () => {
147
+ const body = plainBody("First para.\n\nSecond line one\nsecond line two");
148
+ expect(body.match(/<p>/g)).toHaveLength(2);
149
+ expect(body).toContain("Second line one<br>second line two");
150
+ });
151
+ it("preserves indentation and column alignment", () => {
152
+ const body = plainBody(" calories 1,895\n protein 100 g");
153
+ // Leading indent is fully non-breaking; an internal run keeps one real
154
+ // space as a wrap point.
155
+ expect(body).toContain("&nbsp;&nbsp;&nbsp;&nbsp;calories");
156
+ expect(body).toContain("calories &nbsp;&nbsp;1,895");
157
+ });
158
+ it("expands tabs and drops blank-only blocks", () => {
159
+ expect(plainBody("\tindented")).toContain("&nbsp;&nbsp;&nbsp;&nbsp;indented");
160
+ expect(plainBody("one\n\n \n\ntwo").match(/<p>/g)).toHaveLength(2);
161
+ });
162
+ it("renders an empty body as an empty document", () => {
163
+ expect(plainBody("")).toBe("<html><body></body></html>");
147
164
  });
148
165
  });
149
166
  //# sourceMappingURL=compose.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compose.test.js","sourceRoot":"","sources":["../../../../src/commands/gmail/compose.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compose.test.js","sourceRoot":"","sources":["../../../../src/commands/gmail/compose.test.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- export declare const DRAFT_HELP = "usage: gws-axi gmail draft --to <emails> --subject <text> --body <markdown> [flags]\nflags[8]:\n --to <emails> REQUIRED \u2014 comma-separated recipient addresses\n --subject <text> Subject line (default: empty)\n --body <text> Body text. Pass via quoted string or shell heredoc\n --body-file <path> Read the body from a file instead of --body (mutually\n exclusive with --body; use for long/multi-line bodies)\n --cc <emails> Comma-separated Cc addresses\n --bcc <emails> Comma-separated Bcc addresses\n --thread <thread-id> Attach the draft to an existing thread (reply draft)\n --account <email> REQUIRED when 2+ accounts are authenticated\nexamples:\n gws-axi gmail draft --to alice@x.com --subject \"Re: budget\" --body \"Looks good \u2014 approving.\"\n gws-axi gmail draft --to a@x.com,b@x.com --subject Hi --body-file ./note.txt\n gws-axi gmail draft --to alice@x.com --subject \"Re: thread\" --body \"...\" --thread 1899abcd\nnotes:\n Creates a DRAFT only \u2014 gws-axi never sends mail. Review and send from the\n Gmail UI. The body is markdown, rendered to HTML: the draft goes out as\n multipart/alternative (markdown source as text/plain, rendered GFM as\n text/html) so recipients get reflowable text.\n DO NOT hard-wrap the body. Write each paragraph as one long line \u2014 every\n newline you write becomes a line break in the sent message, so a body\n pre-wrapped at 72/80 columns arrives locked to that width on every screen.\n Blank line = new paragraph. GFM applies: **bold**, lists, tables, fenced\n code blocks (use those for text meant literally).\noutput:\n Returns `action: drafted` plus the new draft_id, message_id, recipients,\n and subject. A help line links to where to review/send it.\n";
1
+ export declare const DRAFT_HELP = "usage: gws-axi gmail draft --to <emails> --subject <text> --body <markdown> [flags]\nflags[9]:\n --to <emails> REQUIRED \u2014 comma-separated recipient addresses\n --subject <text> Subject line (default: empty)\n --body <text> Body text. Pass via quoted string or shell heredoc\n --body-file <path> Read the body from a file instead of --body (mutually\n exclusive with --body; use for long/multi-line bodies)\n --cc <emails> Comma-separated Cc addresses\n --bcc <emails> Comma-separated Bcc addresses\n --thread <thread-id> Attach the draft to an existing thread (reply draft)\n --plain Treat the body as LITERAL text, not markdown. Use when\n *, _, # or backticks must reach the reader as typed\n --account <email> REQUIRED when 2+ accounts are authenticated\nexamples:\n gws-axi gmail draft --to alice@x.com --subject \"Re: budget\" --body \"Looks good \u2014 approving.\"\n gws-axi gmail draft --to a@x.com,b@x.com --subject Hi --body-file ./note.txt\n gws-axi gmail draft --to alice@x.com --subject \"Re: thread\" --body \"...\" --thread 1899abcd\nnotes:\n Creates a DRAFT only \u2014 gws-axi never sends mail. Review and send from the\n Gmail UI. The body is markdown, rendered to a single text/html part so\n recipients get reflowable, formatted text (Gmail adds the plain-text\n alternative itself on send).\n DO NOT hard-wrap the body. Write each paragraph as one long line \u2014 every\n newline you write becomes a line break in the sent message, so a body\n pre-wrapped at 72/80 columns arrives locked to that width on every screen.\n Blank line = new paragraph. GFM applies: **bold**, lists, tables, fenced\n code blocks (use those for text meant literally) \u2014 or pass --plain to turn\n markdown off entirely and keep indentation as typed.\noutput:\n Returns `action: drafted` plus the new draft_id, message_id, recipients,\n and subject. A help line links to where to review/send it.\n";
2
2
  export declare const SEND_HELP = "usage: gws-axi gmail send \u2014 INTENTIONALLY OUT OF SCOPE\nstatus: not supported by design\nnotes:\n gws-axi deliberately does not send mail. It can draft messages for you, but\n sending is left to a human in the Gmail UI so an automated agent can't email\n people on your behalf. Use `gws-axi gmail draft` to compose, then review and\n send the draft yourself in Gmail.\n";
3
3
  export declare function gmailDraftCommand(account: string, args: string[]): Promise<string>;
@@ -4,7 +4,7 @@ import { gmailClient, translateGoogleError } from "../../google/client.js";
4
4
  import { joinBlocks, renderHelp, renderObject } from "../../output/index.js";
5
5
  import { buildRawMessage, parseRecipients } from "./compose.js";
6
6
  export const DRAFT_HELP = `usage: gws-axi gmail draft --to <emails> --subject <text> --body <markdown> [flags]
7
- flags[8]:
7
+ flags[9]:
8
8
  --to <emails> REQUIRED — comma-separated recipient addresses
9
9
  --subject <text> Subject line (default: empty)
10
10
  --body <text> Body text. Pass via quoted string or shell heredoc
@@ -13,6 +13,8 @@ flags[8]:
13
13
  --cc <emails> Comma-separated Cc addresses
14
14
  --bcc <emails> Comma-separated Bcc addresses
15
15
  --thread <thread-id> Attach the draft to an existing thread (reply draft)
16
+ --plain Treat the body as LITERAL text, not markdown. Use when
17
+ *, _, # or backticks must reach the reader as typed
16
18
  --account <email> REQUIRED when 2+ accounts are authenticated
17
19
  examples:
18
20
  gws-axi gmail draft --to alice@x.com --subject "Re: budget" --body "Looks good — approving."
@@ -20,14 +22,15 @@ examples:
20
22
  gws-axi gmail draft --to alice@x.com --subject "Re: thread" --body "..." --thread 1899abcd
21
23
  notes:
22
24
  Creates a DRAFT only — gws-axi never sends mail. Review and send from the
23
- Gmail UI. The body is markdown, rendered to HTML: the draft goes out as
24
- multipart/alternative (markdown source as text/plain, rendered GFM as
25
- text/html) so recipients get reflowable text.
25
+ Gmail UI. The body is markdown, rendered to a single text/html part so
26
+ recipients get reflowable, formatted text (Gmail adds the plain-text
27
+ alternative itself on send).
26
28
  DO NOT hard-wrap the body. Write each paragraph as one long line — every
27
29
  newline you write becomes a line break in the sent message, so a body
28
30
  pre-wrapped at 72/80 columns arrives locked to that width on every screen.
29
31
  Blank line = new paragraph. GFM applies: **bold**, lists, tables, fenced
30
- code blocks (use those for text meant literally).
32
+ code blocks (use those for text meant literally) — or pass --plain to turn
33
+ markdown off entirely and keep indentation as typed.
31
34
  output:
32
35
  Returns \`action: drafted\` plus the new draft_id, message_id, recipients,
33
36
  and subject. A help line links to where to review/send it.
@@ -49,6 +52,7 @@ function parseFlags(args) {
49
52
  body: undefined,
50
53
  bodyFile: undefined,
51
54
  thread: undefined,
55
+ plain: false,
52
56
  };
53
57
  for (let i = 0; i < args.length; i++) {
54
58
  const arg = args[i];
@@ -82,6 +86,9 @@ function parseFlags(args) {
82
86
  flags.thread = next;
83
87
  i++;
84
88
  break;
89
+ case "--plain":
90
+ flags.plain = true;
91
+ break;
85
92
  }
86
93
  }
87
94
  return flags;
@@ -119,6 +126,7 @@ export async function gmailDraftCommand(account, args) {
119
126
  bcc: flags.bcc.length ? flags.bcc : undefined,
120
127
  subject: flags.subject,
121
128
  body,
129
+ plain: flags.plain,
122
130
  });
123
131
  const message = { raw };
124
132
  if (flags.thread)
@@ -1 +1 @@
1
- {"version":3,"file":"draft.js","sourceRoot":"","sources":["../../../../src/commands/gmail/draft.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"draft.js","sourceRoot":"","sources":["../../../../src/commands/gmail/draft.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEhE,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BzB,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG;;;;;;;CAOxB,CAAC;AAaF,SAAS,UAAU,CAAC,IAAc;IAChC,MAAM,KAAK,GAAgB;QACzB,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,GAAG,EAAE,EAAE;QACP,OAAO,EAAE,EAAE;QACX,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,SAAS;QACnB,MAAM,EAAE,SAAS;QACjB,KAAK,EAAE,KAAK;KACb,CAAC;IACF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACzB,QAAQ,GAAG,EAAE,CAAC;YACZ,KAAK,MAAM;gBACT,KAAK,CAAC,EAAE,GAAG,eAAe,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;gBACvC,CAAC,EAAE,CAAC;gBACJ,MAAM;YACR,KAAK,MAAM;gBACT,KAAK,CAAC,EAAE,GAAG,eAAe,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;gBACvC,CAAC,EAAE,CAAC;gBACJ,MAAM;YACR,KAAK,OAAO;gBACV,KAAK,CAAC,GAAG,GAAG,eAAe,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;gBACxC,CAAC,EAAE,CAAC;gBACJ,MAAM;YACR,KAAK,WAAW;gBACd,KAAK,CAAC,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC;gBAC3B,CAAC,EAAE,CAAC;gBACJ,MAAM;YACR,KAAK,QAAQ;gBACX,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;gBAClB,CAAC,EAAE,CAAC;gBACJ,MAAM;YACR,KAAK,aAAa;gBAChB,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;gBACtB,CAAC,EAAE,CAAC;gBACJ,MAAM;YACR,KAAK,UAAU;gBACb,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;gBACpB,CAAC,EAAE,CAAC;gBACJ,MAAM;YACR,KAAK,SAAS;gBACZ,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;gBACnB,MAAM;QACV,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,WAAW,CAAC,KAAkB;IACrC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC7D,MAAM,IAAI,QAAQ,CAAC,+CAA+C,EAAE,kBAAkB,EAAE;YACtF,4EAA4E;SAC7E,CAAC,CAAC;IACL,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,OAAO,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,QAAQ,CAAC,4BAA4B,KAAK,CAAC,QAAQ,EAAE,EAAE,kBAAkB,EAAE;gBACnF,uCAAuC;aACxC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAe,EAAE,IAAc;IACrE,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB,EAAE;YACzD,yEAAyE;SAC1E,CAAC,CAAC;IACL,CAAC;IACD,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAEhC,MAAM,GAAG,GAAG,eAAe,CAAC;QAC1B,IAAI,EAAE,OAAO;QACb,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS;QAC1C,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;QAC7C,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI;QACJ,KAAK,EAAE,KAAK,CAAC,KAAK;KACnB,CAAC,CAAC;IAEH,MAAM,OAAO,GAA4B,EAAE,GAAG,EAAE,CAAC;IACjD,IAAI,KAAK,CAAC,MAAM;QAAE,OAAO,CAAC,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC;IAElD,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;IACvC,IAAI,KAA4B,CAAC;IACjC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;YACxC,MAAM,EAAE,IAAI;YACZ,WAAW,EAAE,EAAE,OAAO,EAAE;SACzB,CAAC,CAAC;QACH,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC;IACnB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,oBAAoB,CAAC,GAAG,EAAE;YAC9B,OAAO;YACP,SAAS,EAAE,qBAAqB;SACjC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAA4B;QACtC,MAAM,EAAE,SAAS;QACjB,OAAO;QACP,QAAQ,EAAE,KAAK,CAAC,EAAE,IAAI,EAAE;QACxB,UAAU,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE;QACnC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;QACvB,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,cAAc;KACzC,CAAC;IACF,IAAI,KAAK,CAAC,EAAE,CAAC,MAAM;QAAE,MAAM,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrD,IAAI,KAAK,CAAC,MAAM;QAAE,MAAM,CAAC,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC;IAElD,OAAO,UAAU,CACf,YAAY,CAAC,MAAM,CAAC,EACpB,UAAU,CAAC;QACT,8EAA8E;QAC9E,6CAA6C,KAAK,CAAC,EAAE,IAAI,EAAE,GAAG;KAC/D,CAAC,CACH,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gws-axi",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "AXI-compliant Google Workspace CLI — token-efficient TOON output, agent-guided auth setup, unified interface for Gmail, Calendar, Docs, Drive, Slides",
5
5
  "type": "module",
6
6
  "repository": {
@@ -1,8 +0,0 @@
1
- export interface ServiceSubcommand {
2
- name: string;
3
- mutation: boolean;
4
- }
5
- export declare function buildServiceStub(service: string, subcommands: ServiceSubcommand[]): {
6
- help: string;
7
- command: (args: string[]) => Promise<Record<string, unknown>>;
8
- };
@@ -1,69 +0,0 @@
1
- import { AxiError } from "axi-sdk-js";
2
- import { resolveAccount } from "../google/account.js";
3
- function parseAccountFlag(args) {
4
- const rest = [];
5
- let account;
6
- for (let i = 0; i < args.length; i++) {
7
- const arg = args[i];
8
- if (arg === "--account" && args[i + 1]) {
9
- account = args[i + 1];
10
- i++;
11
- continue;
12
- }
13
- rest.push(arg);
14
- }
15
- return { account, rest };
16
- }
17
- export function buildServiceStub(service, subcommands) {
18
- const readOps = subcommands.filter((s) => !s.mutation).map((s) => s.name);
19
- const writeOps = subcommands.filter((s) => s.mutation).map((s) => s.name);
20
- const help = `usage: gws-axi ${service} <subcommand> [args] [--account <email>] [flags]
21
- reads[${readOps.length}]:
22
- ${readOps.join(", ") || "(none)"}
23
- writes[${writeOps.length}]:
24
- ${writeOps.join(", ") || "(none)"}
25
- status: ${service} is not yet implemented in v1-dev
26
- see: docs/design.md for the full command surface plan
27
- notes:
28
- Writes require --account <email> when 2+ accounts are authenticated.
29
- Reads use the default account when --account is not provided.
30
- `;
31
- const command = async (args) => {
32
- if (args.length === 0) {
33
- // Return help as structured output so it integrates with TOON
34
- return {
35
- service,
36
- status: "not_implemented",
37
- reads: readOps,
38
- writes: writeOps,
39
- help: [
40
- `Run \`gws-axi ${service} --help\` for details`,
41
- "See docs/design.md for implementation roadmap",
42
- ],
43
- };
44
- }
45
- const sub = args[0];
46
- const def = subcommands.find((s) => s.name === sub);
47
- if (!def) {
48
- throw new AxiError(`Unknown ${service} subcommand: ${sub}`, "VALIDATION_ERROR", [
49
- `Run \`gws-axi ${service} --help\` to see available subcommands`,
50
- ]);
51
- }
52
- // Account resolution runs even though the operation is stubbed — this
53
- // exercises write-protection and error paths without needing real API
54
- // plumbing, and gives users an accurate preview of the multi-account
55
- // error messages they'd see in production.
56
- const { account } = parseAccountFlag(args.slice(1));
57
- const resolution = resolveAccount(account, {
58
- mutation: def.mutation,
59
- commandName: `${service} ${sub}`,
60
- });
61
- throw new AxiError(`gws-axi ${service} ${sub} is not yet implemented in v1-dev`, "NOT_IMPLEMENTED", [
62
- `Account resolution succeeded: would run as ${resolution.account}`,
63
- `Run \`gws-axi ${service} --help\` for the planned command surface`,
64
- "See docs/design.md for implementation roadmap",
65
- ]);
66
- };
67
- return { help, command };
68
- }
69
- //# sourceMappingURL=service-stub.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"service-stub.js","sourceRoot":"","sources":["../../../src/commands/service-stub.ts"],"names":[],"mappings":"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"}