gws-axi 0.19.0 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/commands/docs.js +22 -13
- package/dist/src/commands/docs.js.map +1 -1
- package/dist/src/commands/drive.js +16 -7
- package/dist/src/commands/drive.js.map +1 -1
- package/dist/src/commands/gmail/compose.d.ts +33 -7
- package/dist/src/commands/gmail/compose.js +44 -13
- package/dist/src/commands/gmail/compose.js.map +1 -1
- package/dist/src/commands/gmail/compose.test.js +49 -9
- package/dist/src/commands/gmail/compose.test.js.map +1 -1
- package/dist/src/commands/gmail/draft.d.ts +1 -1
- package/dist/src/commands/gmail/draft.js +8 -2
- package/dist/src/commands/gmail/draft.js.map +1 -1
- package/dist/src/commands/sheets.js +21 -10
- package/dist/src/commands/sheets.js.map +1 -1
- package/dist/src/commands/slides.js +21 -8
- package/dist/src/commands/slides.js.map +1 -1
- package/dist/src/commands/stub-signposts.d.ts +41 -0
- package/dist/src/commands/stub-signposts.js +51 -0
- package/dist/src/commands/stub-signposts.js.map +1 -0
- package/dist/src/commands/stub-signposts.test.d.ts +1 -0
- package/dist/src/commands/stub-signposts.test.js +54 -0
- package/dist/src/commands/stub-signposts.test.js.map +1 -0
- package/package.json +2 -1
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { AxiError } from "axi-sdk-js";
|
|
2
2
|
import { resolveAccount } from "../google/account.js";
|
|
3
|
+
import { notImplemented, renderAlternatives, withInstead } from "./stub-signposts.js";
|
|
3
4
|
import { docsCommentsCommand, COMMENTS_HELP } from "./docs/comments.js";
|
|
4
5
|
import { docsDiffCommand, DIFF_HELP } from "./docs/diff.js";
|
|
5
6
|
import { docsDownloadCommand, DOWNLOAD_HELP } from "./docs/download.js";
|
|
6
7
|
import { docsFindCommand, FIND_HELP } from "./docs/find.js";
|
|
7
8
|
import { docsReadCommand, READ_HELP } from "./docs/read.js";
|
|
8
9
|
import { driveRevisionsCommand, REVISIONS_HELP } from "./drive/revisions.js";
|
|
10
|
+
// Granular Docs edits need documents.batchUpdate, which isn't wired yet. The
|
|
11
|
+
// download → edit → upload round trip is the shipped substitute, and it
|
|
12
|
+
// replaces the whole document — so the lines say that rather than implying a
|
|
13
|
+
// targeted edit.
|
|
14
|
+
const ROUND_TRIP = [
|
|
15
|
+
"gws-axi docs download <documentId> --out ./doc.md — export the current content to edit locally",
|
|
16
|
+
"gws-axi drive upload ./doc.md --update <documentId> --convert --account <email> — write it back as a NEW REVISION, replacing the ENTIRE document",
|
|
17
|
+
"Multi-tab Docs are refused with MULTI_TAB_TARGET unless --replace-all-tabs; `gws-axi docs diff <documentId> <revA>` compares revisions afterward",
|
|
18
|
+
];
|
|
9
19
|
// Write subcommands are still stubs but we keep per-command --help text
|
|
10
20
|
// so agents can plan around the future surface.
|
|
11
21
|
const APPEND_HELP = `usage: gws-axi docs append <documentId> --text <markdown> [--tab <id>] [flags]
|
|
@@ -53,13 +63,15 @@ const SUBCOMMANDS = [
|
|
|
53
63
|
// model, but the implementation is Drive-wide (any file type).
|
|
54
64
|
{ name: "revisions", mutation: false, help: REVISIONS_HELP, handler: driveRevisionsCommand },
|
|
55
65
|
{ name: "diff", mutation: false, help: DIFF_HELP, handler: docsDiffCommand },
|
|
56
|
-
{ name: "append", mutation: true, help: APPEND_HELP },
|
|
57
|
-
{ name: "insert-text", mutation: true, help: INSERT_TEXT_HELP },
|
|
58
|
-
{ name: "delete-range", mutation: true, help: DELETE_RANGE_HELP },
|
|
59
|
-
{ name: "style-text", mutation: true, help: STYLE_TEXT_HELP },
|
|
60
|
-
{ name: "style-paragraph", mutation: true, help: STYLE_PARAGRAPH_HELP },
|
|
61
|
-
{ name: "insert-table", mutation: true, help: INSERT_TABLE_HELP },
|
|
62
|
-
{ name: "edit-cell", mutation: true, help: EDIT_CELL_HELP },
|
|
66
|
+
{ name: "append", mutation: true, help: APPEND_HELP, instead: ROUND_TRIP },
|
|
67
|
+
{ name: "insert-text", mutation: true, help: INSERT_TEXT_HELP, instead: ROUND_TRIP },
|
|
68
|
+
{ name: "delete-range", mutation: true, help: DELETE_RANGE_HELP, instead: ROUND_TRIP },
|
|
69
|
+
{ name: "style-text", mutation: true, help: STYLE_TEXT_HELP, instead: ROUND_TRIP },
|
|
70
|
+
{ name: "style-paragraph", mutation: true, help: STYLE_PARAGRAPH_HELP, instead: ROUND_TRIP },
|
|
71
|
+
{ name: "insert-table", mutation: true, help: INSERT_TABLE_HELP, instead: ROUND_TRIP },
|
|
72
|
+
{ name: "edit-cell", mutation: true, help: EDIT_CELL_HELP, instead: ROUND_TRIP },
|
|
73
|
+
// Comment writes need the Drive comments API, which no shipped command
|
|
74
|
+
// touches — the round trip below rewrites body content only.
|
|
63
75
|
{ name: "comment-add", mutation: true, help: COMMENT_ADD_HELP },
|
|
64
76
|
{ name: "comment-reply", mutation: true, help: COMMENT_REPLY_HELP },
|
|
65
77
|
{ name: "comment-resolve", mutation: true, help: COMMENT_RESOLVE_HELP },
|
|
@@ -91,7 +103,7 @@ notes:
|
|
|
91
103
|
Reads use the default account when --account is not provided.
|
|
92
104
|
Write subcommands are scaffolded for the next slice — all currently
|
|
93
105
|
throw NOT_IMPLEMENTED after account resolution runs.
|
|
94
|
-
subcommand help:
|
|
106
|
+
${renderAlternatives(SUBCOMMANDS)}subcommand help:
|
|
95
107
|
gws-axi docs read --help for documentId + tab handling
|
|
96
108
|
gws-axi docs find --help for text-match search
|
|
97
109
|
gws-axi docs comments --help for review comments + replies
|
|
@@ -118,7 +130,7 @@ export async function docsCommand(args) {
|
|
|
118
130
|
}
|
|
119
131
|
const rest = args.slice(1);
|
|
120
132
|
if (rest.includes("--help")) {
|
|
121
|
-
return def.help;
|
|
133
|
+
return def.handler ? def.help : withInstead(def.help, def.instead);
|
|
122
134
|
}
|
|
123
135
|
const { account: accountFlag, rest: remaining } = parseAccountFlag(rest);
|
|
124
136
|
const resolution = resolveAccount(accountFlag, {
|
|
@@ -126,10 +138,7 @@ export async function docsCommand(args) {
|
|
|
126
138
|
commandName: `docs ${sub}`,
|
|
127
139
|
});
|
|
128
140
|
if (!def.handler) {
|
|
129
|
-
throw
|
|
130
|
-
`Account resolution succeeded: would run as ${resolution.account}`,
|
|
131
|
-
`See \`gws-axi docs ${sub} --help\` for the planned surface`,
|
|
132
|
-
]);
|
|
141
|
+
throw notImplemented("docs", sub, resolution.account, def.instead);
|
|
133
142
|
}
|
|
134
143
|
return def.handler(resolution.account, remaining);
|
|
135
144
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docs.js","sourceRoot":"","sources":["../../../src/commands/docs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"docs.js","sourceRoot":"","sources":["../../../src/commands/docs.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AxiError } from "axi-sdk-js";
|
|
2
2
|
import { resolveAccount } from "../google/account.js";
|
|
3
|
+
import { notImplemented, renderAlternatives, withInstead } from "./stub-signposts.js";
|
|
3
4
|
import { docsDownloadCommand } from "./docs/download.js";
|
|
4
5
|
import { driveGetCommand, GET_HELP } from "./drive/get.js";
|
|
5
6
|
import { driveLsCommand, LS_HELP } from "./drive/ls.js";
|
|
@@ -82,7 +83,18 @@ const SUBCOMMANDS = [
|
|
|
82
83
|
help: UPLOAD_HELP,
|
|
83
84
|
handler: driveUploadCommand,
|
|
84
85
|
},
|
|
85
|
-
{
|
|
86
|
+
{
|
|
87
|
+
name: "create",
|
|
88
|
+
mutation: true,
|
|
89
|
+
help: CREATE_HELP,
|
|
90
|
+
instead: [
|
|
91
|
+
"gws-axi drive upload <path|-|--content <string>> --name <name> --account <email> — creates a file from content (add --convert for a native Doc/Sheet/Slides)",
|
|
92
|
+
'gws-axi drive mkdir "<name>" --account <email> — creates a folder',
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
// copy / move / delete have no shipped equivalent. `rename` deliberately
|
|
96
|
+
// gets none either: `drive upload --update --name` does rename, but only
|
|
97
|
+
// while replacing the file's content, so advertising it here would mislead.
|
|
86
98
|
{ name: "copy", mutation: true, help: COPY_HELP },
|
|
87
99
|
{ name: "move", mutation: true, help: MOVE_HELP },
|
|
88
100
|
{ name: "rename", mutation: true, help: RENAME_HELP },
|
|
@@ -121,7 +133,7 @@ notes:
|
|
|
121
133
|
Reads use the default account when --account is not provided.
|
|
122
134
|
upload and mkdir are live; the remaining write subcommands are scaffolded
|
|
123
135
|
for the next slice and throw NOT_IMPLEMENTED after account resolution runs.
|
|
124
|
-
subcommand help:
|
|
136
|
+
${renderAlternatives(SUBCOMMANDS)}subcommand help:
|
|
125
137
|
gws-axi drive ls --help for folder listing (incl. --recursive)
|
|
126
138
|
gws-axi drive get --help for full file metadata
|
|
127
139
|
gws-axi drive search --help for full-text search
|
|
@@ -151,7 +163,7 @@ export async function driveCommand(args) {
|
|
|
151
163
|
}
|
|
152
164
|
const rest = args.slice(1);
|
|
153
165
|
if (rest.includes("--help")) {
|
|
154
|
-
return def.help;
|
|
166
|
+
return def.handler ? def.help : withInstead(def.help, def.instead);
|
|
155
167
|
}
|
|
156
168
|
const { account: accountFlag, rest: remaining } = parseAccountFlag(rest);
|
|
157
169
|
const resolution = resolveAccount(accountFlag, {
|
|
@@ -159,10 +171,7 @@ export async function driveCommand(args) {
|
|
|
159
171
|
commandName: `drive ${sub}`,
|
|
160
172
|
});
|
|
161
173
|
if (!def.handler) {
|
|
162
|
-
throw
|
|
163
|
-
`Account resolution succeeded: would run as ${resolution.account}`,
|
|
164
|
-
`See \`gws-axi drive ${sub} --help\` for the planned surface`,
|
|
165
|
-
]);
|
|
174
|
+
throw notImplemented("drive", sub, resolution.account, def.instead);
|
|
166
175
|
}
|
|
167
176
|
return def.handler(resolution.account, remaining);
|
|
168
177
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drive.js","sourceRoot":"","sources":["../../../src/commands/drive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACnF,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"drive.js","sourceRoot":"","sources":["../../../src/commands/drive.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* RFC 5322 message builder for draft creation.
|
|
3
|
+
*
|
|
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.
|
|
21
|
+
*
|
|
22
|
+
* See specs/commands/gmail-draft.md ("Message structure", "Body rendering").
|
|
6
23
|
*/
|
|
7
24
|
export interface ComposeFields {
|
|
8
25
|
/** Sender — the authenticated account's address. */
|
|
@@ -11,14 +28,23 @@ export interface ComposeFields {
|
|
|
11
28
|
cc?: string[];
|
|
12
29
|
bcc?: string[];
|
|
13
30
|
subject: string;
|
|
31
|
+
/** Markdown source, rendered to the HTML body. */
|
|
14
32
|
body: string;
|
|
15
33
|
}
|
|
16
34
|
/** Split a comma-separated recipient flag into trimmed, non-empty addresses. */
|
|
17
35
|
export declare function parseRecipients(value: string): string[];
|
|
36
|
+
/**
|
|
37
|
+
* Render the body markdown to an HTML document.
|
|
38
|
+
*
|
|
39
|
+
* `breaks: true` is deliberate: email bodies carry meaningful single-line
|
|
40
|
+
* breaks (signature blocks, addresses, value lists), and markdown's default of
|
|
41
|
+
* collapsing them into spaces would silently reflow content the author laid
|
|
42
|
+
* out. The flip side — a pre-wrapped body keeps its wrapping — is why
|
|
43
|
+
* DRAFT_HELP tells callers not to hard-wrap.
|
|
44
|
+
*/
|
|
45
|
+
export declare function renderMarkdown(body: string): string;
|
|
18
46
|
/**
|
|
19
47
|
* Build a base64url-encoded RFC 5322 message suitable for
|
|
20
|
-
* `users.drafts.create` / `users.messages.send` `raw` fields.
|
|
21
|
-
* as a single text/plain part with base64 transfer-encoding so any UTF-8
|
|
22
|
-
* content survives intact.
|
|
48
|
+
* `users.drafts.create` / `users.messages.send` `raw` fields.
|
|
23
49
|
*/
|
|
24
50
|
export declare function buildRawMessage(fields: ComposeFields): string;
|
|
@@ -1,9 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* RFC 5322 message builder for draft creation.
|
|
3
|
+
*
|
|
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.
|
|
21
|
+
*
|
|
22
|
+
* See specs/commands/gmail-draft.md ("Message structure", "Body rendering").
|
|
6
23
|
*/
|
|
24
|
+
import { marked } from "marked";
|
|
7
25
|
/** Split a comma-separated recipient flag into trimmed, non-empty addresses. */
|
|
8
26
|
export function parseRecipients(value) {
|
|
9
27
|
return value
|
|
@@ -21,11 +39,28 @@ function encodeHeaderValue(value) {
|
|
|
21
39
|
return value;
|
|
22
40
|
return `=?UTF-8?B?${Buffer.from(value, "utf8").toString("base64")}?=`;
|
|
23
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Render the body markdown to an HTML document.
|
|
44
|
+
*
|
|
45
|
+
* `breaks: true` is deliberate: email bodies carry meaningful single-line
|
|
46
|
+
* breaks (signature blocks, addresses, value lists), and markdown's default of
|
|
47
|
+
* collapsing them into spaces would silently reflow content the author laid
|
|
48
|
+
* out. The flip side — a pre-wrapped body keeps its wrapping — is why
|
|
49
|
+
* DRAFT_HELP tells callers not to hard-wrap.
|
|
50
|
+
*/
|
|
51
|
+
export function renderMarkdown(body) {
|
|
52
|
+
const fragment = marked.parse(body, { gfm: true, breaks: true, async: false });
|
|
53
|
+
return `<html><body>${fragment}</body></html>`;
|
|
54
|
+
}
|
|
55
|
+
/** Base64 with CRLF line endings wrapped at 76 columns (RFC 2045). */
|
|
56
|
+
function encodeBody(text) {
|
|
57
|
+
return Buffer.from(text, "utf8")
|
|
58
|
+
.toString("base64")
|
|
59
|
+
.replace(/(.{76})/g, "$1\r\n");
|
|
60
|
+
}
|
|
24
61
|
/**
|
|
25
62
|
* Build a base64url-encoded RFC 5322 message suitable for
|
|
26
|
-
* `users.drafts.create` / `users.messages.send` `raw` fields.
|
|
27
|
-
* as a single text/plain part with base64 transfer-encoding so any UTF-8
|
|
28
|
-
* content survives intact.
|
|
63
|
+
* `users.drafts.create` / `users.messages.send` `raw` fields.
|
|
29
64
|
*/
|
|
30
65
|
export function buildRawMessage(fields) {
|
|
31
66
|
const headers = [`From: ${fields.from}`, `To: ${fields.to.join(", ")}`];
|
|
@@ -35,13 +70,9 @@ export function buildRawMessage(fields) {
|
|
|
35
70
|
headers.push(`Bcc: ${fields.bcc.join(", ")}`);
|
|
36
71
|
headers.push(`Subject: ${encodeHeaderValue(fields.subject)}`);
|
|
37
72
|
headers.push("MIME-Version: 1.0");
|
|
38
|
-
headers.push('Content-Type: text/
|
|
73
|
+
headers.push('Content-Type: text/html; charset="UTF-8"');
|
|
39
74
|
headers.push("Content-Transfer-Encoding: base64");
|
|
40
|
-
|
|
41
|
-
const encodedBody = Buffer.from(fields.body, "utf8")
|
|
42
|
-
.toString("base64")
|
|
43
|
-
.replace(/(.{76})/g, "$1\r\n");
|
|
44
|
-
const raw = `${headers.join("\r\n")}\r\n\r\n${encodedBody}`;
|
|
75
|
+
const raw = `${headers.join("\r\n")}\r\n\r\n${encodeBody(renderMarkdown(fields.body))}`;
|
|
45
76
|
return Buffer.from(raw, "utf8").toString("base64url");
|
|
46
77
|
}
|
|
47
78
|
//# sourceMappingURL=compose.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compose.js","sourceRoot":"","sources":["../../../../src/commands/gmail/compose.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"compose.js","sourceRoot":"","sources":["../../../../src/commands/gmail/compose.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAahC,gFAAgF;AAChF,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,OAAO,KAAK;SACT,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC;AAED;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,KAAa;IACtC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7E,OAAO,aAAa,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC;AACxE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/E,OAAO,eAAe,QAAQ,gBAAgB,CAAC;AACjD,CAAC;AAED,sEAAsE;AACtE,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;SAC7B,QAAQ,CAAC,QAAQ,CAAC;SAClB,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,MAAqB;IACnD,MAAM,OAAO,GAAa,CAAC,SAAS,MAAM,CAAC,IAAI,EAAE,EAAE,OAAO,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClF,IAAI,MAAM,CAAC,EAAE,EAAE,MAAM;QAAE,OAAO,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnE,IAAI,MAAM,CAAC,GAAG,EAAE,MAAM;QAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtE,OAAO,CAAC,IAAI,CAAC,YAAY,iBAAiB,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9D,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAClC,OAAO,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;IACzD,OAAO,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;IAElD,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,UAAU,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;IACxF,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACxD,CAAC"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import { buildRawMessage, parseRecipients } from "./compose.js";
|
|
3
|
-
/** Decode a base64url raw message
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
return {
|
|
8
|
+
header: text.slice(0, idx),
|
|
9
|
+
body: Buffer.from(text.slice(idx + 4).replace(/\r\n/g, ""), "base64").toString("utf8"),
|
|
10
|
+
};
|
|
10
11
|
}
|
|
11
12
|
describe("parseRecipients", () => {
|
|
12
13
|
it("splits, trims, and drops empties", () => {
|
|
@@ -21,7 +22,7 @@ describe("parseRecipients", () => {
|
|
|
21
22
|
});
|
|
22
23
|
});
|
|
23
24
|
describe("buildRawMessage", () => {
|
|
24
|
-
it("emits the core headers and
|
|
25
|
+
it("emits the core headers and a single text/html body", () => {
|
|
25
26
|
const raw = buildRawMessage({
|
|
26
27
|
from: "me@x.com",
|
|
27
28
|
to: ["alice@x.com", "bob@x.com"],
|
|
@@ -32,8 +33,47 @@ describe("buildRawMessage", () => {
|
|
|
32
33
|
expect(header).toContain("From: me@x.com");
|
|
33
34
|
expect(header).toContain("To: alice@x.com, bob@x.com");
|
|
34
35
|
expect(header).toContain("Subject: Hello");
|
|
35
|
-
expect(header).toContain('Content-Type: text/
|
|
36
|
-
expect(body).
|
|
36
|
+
expect(header).toContain('Content-Type: text/html; charset="UTF-8"');
|
|
37
|
+
expect(body).toMatch(/^<html><body>/);
|
|
38
|
+
expect(body).toMatch(/<\/body><\/html>$/);
|
|
39
|
+
});
|
|
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({
|
|
50
|
+
from: "me@x.com",
|
|
51
|
+
to: ["a@x.com"],
|
|
52
|
+
subject: "s",
|
|
53
|
+
body: "Some **bold** text and a [link](https://axi.md).\n\n- one\n- two",
|
|
54
|
+
}));
|
|
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>");
|
|
58
|
+
});
|
|
59
|
+
it("maps a blank line to a new paragraph and a single newline to <br>", () => {
|
|
60
|
+
const { body } = decodeRaw(buildRawMessage({
|
|
61
|
+
from: "me@x.com",
|
|
62
|
+
to: ["a@x.com"],
|
|
63
|
+
subject: "s",
|
|
64
|
+
body: "First para.\n\nSecond line one\nsecond line two",
|
|
65
|
+
}));
|
|
66
|
+
expect(body.match(/<p>/g)).toHaveLength(2);
|
|
67
|
+
expect(body).toContain("Second line one<br>second line two");
|
|
68
|
+
});
|
|
69
|
+
it("never breaks a long paragraph inside the decoded body", () => {
|
|
70
|
+
// The 76-column wrapping is base64 transport only — it must not survive
|
|
71
|
+
// decoding, or the recipient sees the ragged columns this format exists
|
|
72
|
+
// to prevent.
|
|
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);
|
|
37
77
|
});
|
|
38
78
|
it("includes Cc/Bcc only when provided", () => {
|
|
39
79
|
const withCc = decodeRaw(buildRawMessage({
|
|
@@ -59,7 +99,7 @@ describe("buildRawMessage", () => {
|
|
|
59
99
|
}));
|
|
60
100
|
expect(header).toContain("Subject: =?UTF-8?B?");
|
|
61
101
|
expect(header).not.toContain("café");
|
|
62
|
-
expect(body).
|
|
102
|
+
expect(body).toContain("naïve résumé — ☕");
|
|
63
103
|
});
|
|
64
104
|
it("produces valid base64url (no +,/,= padding chars)", () => {
|
|
65
105
|
const raw = buildRawMessage({
|
|
@@ -69,7 +109,7 @@ describe("buildRawMessage", () => {
|
|
|
69
109
|
body: "x".repeat(200), // force base64 line wrapping
|
|
70
110
|
});
|
|
71
111
|
expect(raw).not.toMatch(/[+/=]/);
|
|
72
|
-
expect(decodeRaw(raw).body).
|
|
112
|
+
expect(decodeRaw(raw).body).toContain("x".repeat(200));
|
|
73
113
|
});
|
|
74
114
|
});
|
|
75
115
|
//# sourceMappingURL=compose.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compose.test.js","sourceRoot":"","sources":["../../../../src/commands/gmail/compose.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEhE,
|
|
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.
|
|
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 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).\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>;
|
|
@@ -20,8 +20,14 @@ examples:
|
|
|
20
20
|
gws-axi gmail draft --to alice@x.com --subject "Re: thread" --body "..." --thread 1899abcd
|
|
21
21
|
notes:
|
|
22
22
|
Creates a DRAFT only — gws-axi never sends mail. Review and send from the
|
|
23
|
-
Gmail UI.
|
|
24
|
-
|
|
23
|
+
Gmail UI. The body is markdown, rendered to a single text/html part so
|
|
24
|
+
recipients get reflowable, formatted text (Gmail adds the plain-text
|
|
25
|
+
alternative itself on send).
|
|
26
|
+
DO NOT hard-wrap the body. Write each paragraph as one long line — every
|
|
27
|
+
newline you write becomes a line break in the sent message, so a body
|
|
28
|
+
pre-wrapped at 72/80 columns arrives locked to that width on every screen.
|
|
29
|
+
Blank line = new paragraph. GFM applies: **bold**, lists, tables, fenced
|
|
30
|
+
code blocks (use those for text meant literally).
|
|
25
31
|
output:
|
|
26
32
|
Returns \`action: drafted\` plus the new draft_id, message_id, recipients,
|
|
27
33
|
and subject. A help line links to where to review/send it.
|
|
@@ -1 +1 @@
|
|
|
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
|
|
1
|
+
{"version":3,"file":"draft.js","sourceRoot":"","sources":["../../../../src/commands/gmail/draft.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import { AxiError } from "axi-sdk-js";
|
|
|
2
2
|
import { resolveAccount } from "../google/account.js";
|
|
3
3
|
import { docsCommentsCommand } from "./docs/comments.js";
|
|
4
4
|
import { READ_HELP, sheetsReadCommand } from "./sheets/read.js";
|
|
5
|
+
import { notImplemented, renderAlternatives, withInstead } from "./stub-signposts.js";
|
|
5
6
|
// Google Sheets comments are Drive comments — the exact same file-agnostic API
|
|
6
7
|
// `docs comments` uses. Alias the handler (cf. `docs revisions` → `drive
|
|
7
8
|
// revisions`) and give it a Sheets-worded help block.
|
|
@@ -21,6 +22,17 @@ notes:
|
|
|
21
22
|
data as \`gws-axi docs comments\`. Cell *notes* are distinct — those come
|
|
22
23
|
back in the notes[] block of \`gws-axi sheets read\`.
|
|
23
24
|
`;
|
|
25
|
+
// Until the Sheets write surface lands, `drive upload --convert` is the only
|
|
26
|
+
// way to put content into a spreadsheet. It replaces the file wholesale, so
|
|
27
|
+
// each line says so — an agent that reads "use drive upload" and nothing else
|
|
28
|
+
// would silently destroy the sheets it never looked at.
|
|
29
|
+
const REPLACE_WHOLESALE = [
|
|
30
|
+
"gws-axi drive upload <file.csv> --update <spreadsheetId> --convert --account <email> — replaces the ENTIRE spreadsheet from CSV/TSV (or --content <string>); NOT a per-range write",
|
|
31
|
+
"Multi-sheet targets are refused with MULTI_TAB_TARGET unless --replace-all-tabs; read them first with `gws-axi sheets read <spreadsheetId>`",
|
|
32
|
+
];
|
|
33
|
+
const CREATE_VIA_UPLOAD = [
|
|
34
|
+
'gws-axi drive upload <file.csv> --convert --name "<title>" --account <email> — creates a new native Spreadsheet and returns its id',
|
|
35
|
+
];
|
|
24
36
|
// Write subcommands are stubs for the next slice — kept with per-command --help
|
|
25
37
|
// so agents can plan around the future surface. They throw NOT_IMPLEMENTED after
|
|
26
38
|
// account resolution runs.
|
|
@@ -56,10 +68,12 @@ const sheetsCommentsCommand = (account, args) => docsCommentsCommand(account, ar
|
|
|
56
68
|
const SUBCOMMANDS = [
|
|
57
69
|
{ name: "read", mutation: false, help: READ_HELP, handler: sheetsReadCommand },
|
|
58
70
|
{ name: "comments", mutation: false, help: COMMENTS_HELP, handler: sheetsCommentsCommand },
|
|
59
|
-
{ name: "update", mutation: true, help: UPDATE_HELP },
|
|
60
|
-
{ name: "append", mutation: true, help: APPEND_HELP },
|
|
61
|
-
{ name: "clear", mutation: true, help: CLEAR_HELP },
|
|
62
|
-
{ name: "create", mutation: true, help: CREATE_HELP },
|
|
71
|
+
{ name: "update", mutation: true, help: UPDATE_HELP, instead: REPLACE_WHOLESALE },
|
|
72
|
+
{ name: "append", mutation: true, help: APPEND_HELP, instead: REPLACE_WHOLESALE },
|
|
73
|
+
{ name: "clear", mutation: true, help: CLEAR_HELP, instead: REPLACE_WHOLESALE },
|
|
74
|
+
{ name: "create", mutation: true, help: CREATE_HELP, instead: CREATE_VIA_UPLOAD },
|
|
75
|
+
// Adding a tab needs spreadsheets.batchUpdate — drive upload can only
|
|
76
|
+
// collapse a spreadsheet to one sheet, never grow it one.
|
|
63
77
|
{ name: "add-tab", mutation: true, help: ADD_TAB_HELP },
|
|
64
78
|
];
|
|
65
79
|
const SUB_BY_NAME = Object.fromEntries(SUBCOMMANDS.map((s) => [s.name, s]));
|
|
@@ -89,7 +103,7 @@ notes:
|
|
|
89
103
|
Reads use the default account when --account is not provided.
|
|
90
104
|
Write subcommands are scaffolded for the next slice — all currently
|
|
91
105
|
throw NOT_IMPLEMENTED after account resolution runs.
|
|
92
|
-
subcommand help:
|
|
106
|
+
${renderAlternatives(SUBCOMMANDS)}subcommand help:
|
|
93
107
|
gws-axi sheets read --help for spreadsheetId + tab/range handling
|
|
94
108
|
gws-axi sheets comments --help for review comments (Drive comments)
|
|
95
109
|
examples:
|
|
@@ -111,7 +125,7 @@ export async function sheetsCommand(args) {
|
|
|
111
125
|
}
|
|
112
126
|
const rest = args.slice(1);
|
|
113
127
|
if (rest.includes("--help")) {
|
|
114
|
-
return def.help;
|
|
128
|
+
return def.handler ? def.help : withInstead(def.help, def.instead);
|
|
115
129
|
}
|
|
116
130
|
const { account: accountFlag, rest: remaining } = parseAccountFlag(rest);
|
|
117
131
|
const resolution = resolveAccount(accountFlag, {
|
|
@@ -119,10 +133,7 @@ export async function sheetsCommand(args) {
|
|
|
119
133
|
commandName: `sheets ${sub}`,
|
|
120
134
|
});
|
|
121
135
|
if (!def.handler) {
|
|
122
|
-
throw
|
|
123
|
-
`Account resolution succeeded: would run as ${resolution.account}`,
|
|
124
|
-
`See \`gws-axi sheets ${sub} --help\` for the planned surface`,
|
|
125
|
-
]);
|
|
136
|
+
throw notImplemented("sheets", sub, resolution.account, def.instead);
|
|
126
137
|
}
|
|
127
138
|
return def.handler(resolution.account, remaining);
|
|
128
139
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sheets.js","sourceRoot":"","sources":["../../../src/commands/sheets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"sheets.js","sourceRoot":"","sources":["../../../src/commands/sheets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEtF,+EAA+E;AAC/E,yEAAyE;AACzE,sDAAsD;AACtD,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;CAerB,CAAC;AAUF,6EAA6E;AAC7E,4EAA4E;AAC5E,8EAA8E;AAC9E,wDAAwD;AACxD,MAAM,iBAAiB,GAAG;IACxB,oLAAoL;IACpL,6IAA6I;CAC9I,CAAC;AACF,MAAM,iBAAiB,GAAG;IACxB,oIAAoI;CACrI,CAAC;AAEF,gFAAgF;AAChF,iFAAiF;AACjF,2BAA2B;AAC3B,MAAM,WAAW,GAAG;;;;;CAKnB,CAAC;AACF,MAAM,WAAW,GAAG;;;;CAInB,CAAC;AACF,MAAM,UAAU,GAAG;;CAElB,CAAC;AACF,MAAM,WAAW,GAAG;;;;;CAKnB,CAAC;AACF,MAAM,YAAY,GAAG;;;;CAIpB,CAAC;AAEF,MAAM,qBAAqB,GAAG,CAAC,OAAe,EAAE,IAAc,EAAmB,EAAE,CACjF,mBAAmB,CAAC,OAAO,EAAE,IAAI,EAAE;IACjC,QAAQ,EAAE,aAAa;IACvB,YAAY,EAAE,uBAAuB;CACtC,CAAC,CAAC;AAEL,MAAM,WAAW,GAAuB;IACtC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,iBAAiB,EAAE;IAC9E,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,qBAAqB,EAAE;IAC1F,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,iBAAiB,EAAE;IACjF,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,iBAAiB,EAAE;IACjF,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,EAAE;IAC/E,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,iBAAiB,EAAE;IACjF,sEAAsE;IACtE,0DAA0D;IAC1D,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE;CACxD,CAAC;AAEF,MAAM,WAAW,GAAqC,MAAM,CAAC,WAAW,CACtE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CACpC,CAAC;AAEF,SAAS,gBAAgB,CAAC,IAAc;IAItC,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,OAA2B,CAAC;IAChC,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,IAAI,GAAG,KAAK,WAAW,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACvC,OAAO,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACtB,CAAC,EAAE,CAAC;YACJ,SAAS;QACX,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC3B,CAAC;AAED,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACxE,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAExE,MAAM,CAAC,MAAM,WAAW,GAAG;QACnB,KAAK,CAAC,MAAM;IAChB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,MAAM;IAClB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;EAMnB,kBAAkB,CAAC,WAAW,CAAC;;;;;;;;CAQhC,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAc;IAChD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;QACrE,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,QAAQ,CAAC,8BAA8B,GAAG,EAAE,EAAE,kBAAkB,EAAE;YAC1E,4DAA4D;SAC7D,CAAC,CAAC;IACL,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACrE,CAAC;IAED,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACzE,MAAM,UAAU,GAAG,cAAc,CAAC,WAAW,EAAE;QAC7C,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,WAAW,EAAE,UAAU,GAAG,EAAE;KAC7B,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACjB,MAAM,cAAc,CAAC,QAAQ,EAAE,GAAG,EAAE,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACvE,CAAC;IAED,OAAO,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AACpD,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AxiError } from "axi-sdk-js";
|
|
2
2
|
import { resolveAccount } from "../google/account.js";
|
|
3
|
+
import { notImplemented, renderAlternatives, withInstead } from "./stub-signposts.js";
|
|
3
4
|
import { docsCommentsCommand } from "./docs/comments.js";
|
|
4
5
|
import { GET_HELP, slidesGetCommand } from "./slides/get.js";
|
|
5
6
|
import { PAGE_HELP, slidesPageCommand } from "./slides/page.js";
|
|
@@ -50,8 +51,23 @@ const SUBCOMMANDS = [
|
|
|
50
51
|
handler: slidesSummarizeCommand,
|
|
51
52
|
},
|
|
52
53
|
{ name: "comments", mutation: false, help: COMMENTS_HELP, handler: slidesCommentsCommand },
|
|
53
|
-
{
|
|
54
|
-
|
|
54
|
+
{
|
|
55
|
+
name: "create",
|
|
56
|
+
mutation: true,
|
|
57
|
+
help: CREATE_HELP,
|
|
58
|
+
instead: [
|
|
59
|
+
'gws-axi drive upload <deck.pptx> --convert --name "<title>" --account <email> — creates a new native Presentation and returns its id',
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "update",
|
|
64
|
+
mutation: true,
|
|
65
|
+
help: UPDATE_HELP,
|
|
66
|
+
instead: [
|
|
67
|
+
"gws-axi drive upload <deck.pptx> --update <presentationId> --convert --account <email> — replaces the ENTIRE deck as a new revision; NOT a per-slide or per-element edit",
|
|
68
|
+
"gws-axi slides summarize <presentationId> — read the current deck first; there is no export-to-pptx round trip",
|
|
69
|
+
],
|
|
70
|
+
},
|
|
55
71
|
];
|
|
56
72
|
const SUB_BY_NAME = Object.fromEntries(SUBCOMMANDS.map((s) => [s.name, s]));
|
|
57
73
|
function parseAccountFlag(args) {
|
|
@@ -80,7 +96,7 @@ notes:
|
|
|
80
96
|
Reads use the default account when --account is not provided.
|
|
81
97
|
Write subcommands are scaffolded for the next slice; they throw
|
|
82
98
|
NOT_IMPLEMENTED after account resolution runs.
|
|
83
|
-
subcommand help:
|
|
99
|
+
${renderAlternatives(SUBCOMMANDS)}subcommand help:
|
|
84
100
|
gws-axi slides get --help for metadata + slide list
|
|
85
101
|
gws-axi slides page --help for a single slide's content
|
|
86
102
|
gws-axi slides summarize --help for the whole deck as markdown
|
|
@@ -104,7 +120,7 @@ export async function slidesCommand(args) {
|
|
|
104
120
|
}
|
|
105
121
|
const rest = args.slice(1);
|
|
106
122
|
if (rest.includes("--help")) {
|
|
107
|
-
return def.help;
|
|
123
|
+
return def.handler ? def.help : withInstead(def.help, def.instead);
|
|
108
124
|
}
|
|
109
125
|
const { account: accountFlag, rest: remaining } = parseAccountFlag(rest);
|
|
110
126
|
const resolution = resolveAccount(accountFlag, {
|
|
@@ -112,10 +128,7 @@ export async function slidesCommand(args) {
|
|
|
112
128
|
commandName: `slides ${sub}`,
|
|
113
129
|
});
|
|
114
130
|
if (!def.handler) {
|
|
115
|
-
throw
|
|
116
|
-
`Account resolution succeeded: would run as ${resolution.account}`,
|
|
117
|
-
`See \`gws-axi slides ${sub} --help\` for the planned surface`,
|
|
118
|
-
]);
|
|
131
|
+
throw notImplemented("slides", sub, resolution.account, def.instead);
|
|
119
132
|
}
|
|
120
133
|
return def.handler(resolution.account, remaining);
|
|
121
134
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slides.js","sourceRoot":"","sources":["../../../src/commands/slides.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAE/E,wEAAwE;AACxE,qEAAqE;AACrE,uDAAuD;AACvD,MAAM,aAAa,GAAG;;;;;;;;;;;;;;CAcrB,CAAC;AAEF,MAAM,qBAAqB,GAAG,CAAC,OAAe,EAAE,IAAc,EAAmB,EAAE,CACjF,mBAAmB,CAAC,OAAO,EAAE,IAAI,EAAE;IACjC,QAAQ,EAAE,cAAc;IACxB,YAAY,EAAE,wBAAwB;CACvC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"slides.js","sourceRoot":"","sources":["../../../src/commands/slides.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signposts for scaffolded subcommands.
|
|
3
|
+
*
|
|
4
|
+
* A stub that refuses with nothing but "not implemented; see --help" — where
|
|
5
|
+
* `--help` lists the same unimplemented command — is a closed circuit, and an
|
|
6
|
+
* agent's way out of a closed circuit is to leave gws-axi entirely and drive
|
|
7
|
+
* the Google APIs with the raw token, discarding write-protection and error
|
|
8
|
+
* translation with it. So every stub declares the working commands that do
|
|
9
|
+
* some or all of its job, and the same list feeds all three surfaces a caller
|
|
10
|
+
* can hit: the NOT_IMPLEMENTED error, the stub's own --help, and the service
|
|
11
|
+
* help screen.
|
|
12
|
+
*
|
|
13
|
+
* See specs/principles.md#no-dead-end-surfaces and specs/api/conventions.md
|
|
14
|
+
* ("Unimplemented and unsupported surfaces").
|
|
15
|
+
*/
|
|
16
|
+
import { AxiError } from "axi-sdk-js";
|
|
17
|
+
export interface StubbableSubcommand {
|
|
18
|
+
name: string;
|
|
19
|
+
handler?: unknown;
|
|
20
|
+
/**
|
|
21
|
+
* Runnable gws-axi commands that accomplish some or all of this subcommand's
|
|
22
|
+
* job. Each line names the command AND where it falls short — these are
|
|
23
|
+
* mostly wholesale-replace substitutes for a granular edit, and a signpost
|
|
24
|
+
* that oversells is worse than none. Omit when nothing comes close.
|
|
25
|
+
*/
|
|
26
|
+
instead?: string[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The refusal for a scaffolded subcommand. Alternatives lead; the account
|
|
30
|
+
* diagnostic and the pointer to the planned surface follow, because they are
|
|
31
|
+
* context rather than a next step.
|
|
32
|
+
*/
|
|
33
|
+
export declare function notImplemented(service: string, sub: string, account: string, instead?: string[]): AxiError;
|
|
34
|
+
/** Append the `instead[N]:` block to a stubbed subcommand's own --help. */
|
|
35
|
+
export declare function withInstead(help: string, instead?: string[]): string;
|
|
36
|
+
/**
|
|
37
|
+
* The service-level `alternatives[N]:` block, deduped across every stub.
|
|
38
|
+
* Returns "" when the service has no stubs, so fully-implemented services
|
|
39
|
+
* (calendar) gain nothing.
|
|
40
|
+
*/
|
|
41
|
+
export declare function renderAlternatives(subs: StubbableSubcommand[]): string;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signposts for scaffolded subcommands.
|
|
3
|
+
*
|
|
4
|
+
* A stub that refuses with nothing but "not implemented; see --help" — where
|
|
5
|
+
* `--help` lists the same unimplemented command — is a closed circuit, and an
|
|
6
|
+
* agent's way out of a closed circuit is to leave gws-axi entirely and drive
|
|
7
|
+
* the Google APIs with the raw token, discarding write-protection and error
|
|
8
|
+
* translation with it. So every stub declares the working commands that do
|
|
9
|
+
* some or all of its job, and the same list feeds all three surfaces a caller
|
|
10
|
+
* can hit: the NOT_IMPLEMENTED error, the stub's own --help, and the service
|
|
11
|
+
* help screen.
|
|
12
|
+
*
|
|
13
|
+
* See specs/principles.md#no-dead-end-surfaces and specs/api/conventions.md
|
|
14
|
+
* ("Unimplemented and unsupported surfaces").
|
|
15
|
+
*/
|
|
16
|
+
import { AxiError } from "axi-sdk-js";
|
|
17
|
+
// Stated explicitly rather than omitted: "no alternative" is itself the answer
|
|
18
|
+
// an agent needs, and silence reads as an oversight worth working around.
|
|
19
|
+
const NO_ALTERNATIVE = "No gws-axi command does this yet — it needs the planned write surface, so there is nothing to fall back to";
|
|
20
|
+
/** Stubs are the entries with no handler. */
|
|
21
|
+
const stubsOf = (subs) => subs.filter((s) => !s.handler);
|
|
22
|
+
/**
|
|
23
|
+
* The refusal for a scaffolded subcommand. Alternatives lead; the account
|
|
24
|
+
* diagnostic and the pointer to the planned surface follow, because they are
|
|
25
|
+
* context rather than a next step.
|
|
26
|
+
*/
|
|
27
|
+
export function notImplemented(service, sub, account, instead) {
|
|
28
|
+
const alternatives = instead?.length ? instead : [NO_ALTERNATIVE];
|
|
29
|
+
return new AxiError(`gws-axi ${service} ${sub} is not yet implemented`, "NOT_IMPLEMENTED", [
|
|
30
|
+
...alternatives,
|
|
31
|
+
`See \`gws-axi ${service} ${sub} --help\` for the planned surface`,
|
|
32
|
+
`Account resolution succeeded: would run as ${account}`,
|
|
33
|
+
]);
|
|
34
|
+
}
|
|
35
|
+
/** Append the `instead[N]:` block to a stubbed subcommand's own --help. */
|
|
36
|
+
export function withInstead(help, instead) {
|
|
37
|
+
const lines = instead?.length ? instead : [NO_ALTERNATIVE];
|
|
38
|
+
return `${help.trimEnd()}\ninstead[${lines.length}]:\n${lines.map((l) => ` ${l}`).join("\n")}\n`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The service-level `alternatives[N]:` block, deduped across every stub.
|
|
42
|
+
* Returns "" when the service has no stubs, so fully-implemented services
|
|
43
|
+
* (calendar) gain nothing.
|
|
44
|
+
*/
|
|
45
|
+
export function renderAlternatives(subs) {
|
|
46
|
+
const lines = [...new Set(stubsOf(subs).flatMap((s) => s.instead ?? []))];
|
|
47
|
+
if (lines.length === 0)
|
|
48
|
+
return "";
|
|
49
|
+
return `alternatives[${lines.length}]:\n${lines.map((l) => ` ${l}`).join("\n")}\n`;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=stub-signposts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stub-signposts.js","sourceRoot":"","sources":["../../../src/commands/stub-signposts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AActC,+EAA+E;AAC/E,0EAA0E;AAC1E,MAAM,cAAc,GAClB,4GAA4G,CAAC;AAE/G,6CAA6C;AAC7C,MAAM,OAAO,GAAG,CAAC,IAA2B,EAAyB,EAAE,CACrE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;AAEjC;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,GAAW,EACX,OAAe,EACf,OAAkB;IAElB,MAAM,YAAY,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;IAClE,OAAO,IAAI,QAAQ,CAAC,WAAW,OAAO,IAAI,GAAG,yBAAyB,EAAE,iBAAiB,EAAE;QACzF,GAAG,YAAY;QACf,iBAAiB,OAAO,IAAI,GAAG,mCAAmC;QAClE,8CAA8C,OAAO,EAAE;KACxD,CAAC,CAAC;AACL,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,OAAkB;IAC1D,MAAM,KAAK,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;IAC3D,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,aAAa,KAAK,CAAC,MAAM,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AACpG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAA2B;IAC5D,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,OAAO,gBAAgB,KAAK,CAAC,MAAM,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AACtF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { notImplemented, renderAlternatives, withInstead } from "./stub-signposts.js";
|
|
3
|
+
const noop = async () => "";
|
|
4
|
+
describe("notImplemented", () => {
|
|
5
|
+
it("leads with the alternatives, not the account diagnostic", () => {
|
|
6
|
+
const err = notImplemented("sheets", "update", "me@x.com", [
|
|
7
|
+
"gws-axi drive upload … --convert — replaces the ENTIRE spreadsheet",
|
|
8
|
+
]);
|
|
9
|
+
expect(err.code).toBe("NOT_IMPLEMENTED");
|
|
10
|
+
expect(err.suggestions[0]).toContain("drive upload");
|
|
11
|
+
// Diagnostics are context, so they come after the next step.
|
|
12
|
+
expect(err.suggestions[err.suggestions.length - 1]).toContain("me@x.com");
|
|
13
|
+
});
|
|
14
|
+
it("still points at the subcommand's planned surface", () => {
|
|
15
|
+
const err = notImplemented("docs", "append", "me@x.com", ["do the other thing"]);
|
|
16
|
+
expect(err.suggestions.some((s) => s.includes("gws-axi docs append --help"))).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
it("says so explicitly when nothing comes close", () => {
|
|
19
|
+
const err = notImplemented("drive", "move", "me@x.com");
|
|
20
|
+
expect(err.suggestions[0]).toContain("No gws-axi command does this yet");
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe("withInstead", () => {
|
|
24
|
+
it("appends an instead[N] block sized to the lines", () => {
|
|
25
|
+
const out = withInstead("usage: gws-axi sheets update …\nstatus: planned\n", ["a", "b"]);
|
|
26
|
+
expect(out).toContain("instead[2]:\n a\n b");
|
|
27
|
+
});
|
|
28
|
+
it("falls back to the no-alternative line", () => {
|
|
29
|
+
expect(withInstead("usage: …\n")).toContain("instead[1]:");
|
|
30
|
+
expect(withInstead("usage: …\n")).toContain("No gws-axi command does this yet");
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe("renderAlternatives", () => {
|
|
34
|
+
it("dedupes lines shared across stubs", () => {
|
|
35
|
+
const block = renderAlternatives([
|
|
36
|
+
{ name: "read", handler: noop },
|
|
37
|
+
{ name: "update", instead: ["shared line", "update only"] },
|
|
38
|
+
{ name: "append", instead: ["shared line"] },
|
|
39
|
+
]);
|
|
40
|
+
expect(block).toContain("alternatives[2]:");
|
|
41
|
+
expect(block.match(/shared line/g)).toHaveLength(1);
|
|
42
|
+
});
|
|
43
|
+
it("ignores alternatives on implemented subcommands", () => {
|
|
44
|
+
const block = renderAlternatives([{ name: "read", handler: noop, instead: ["ignored"] }]);
|
|
45
|
+
expect(block).toBe("");
|
|
46
|
+
});
|
|
47
|
+
it("returns empty for a service with nothing stubbed", () => {
|
|
48
|
+
expect(renderAlternatives([{ name: "events", handler: noop }])).toBe("");
|
|
49
|
+
});
|
|
50
|
+
it("skips stubs that declare no alternative", () => {
|
|
51
|
+
expect(renderAlternatives([{ name: "move" }])).toBe("");
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=stub-signposts.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stub-signposts.test.js","sourceRoot":"","sources":["../../../src/commands/stub-signposts.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gws-axi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.1",
|
|
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": {
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"diff": "^9.0.0",
|
|
54
54
|
"google-auth-library": "^10.7.0",
|
|
55
55
|
"googleapis": "^173.0.0",
|
|
56
|
+
"marked": "^18.0.9",
|
|
56
57
|
"turndown": "^7.2.4"
|
|
57
58
|
},
|
|
58
59
|
"overrides": {
|