gws-axi 0.3.0 → 0.4.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.
- package/README.md +50 -8
- package/dist/src/cli.js +1 -2
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands/docs/comments.d.ts +2 -0
- package/dist/src/commands/docs/comments.js +172 -0
- package/dist/src/commands/docs/comments.js.map +1 -0
- package/dist/src/commands/docs/download.d.ts +2 -0
- package/dist/src/commands/docs/download.js +213 -0
- package/dist/src/commands/docs/download.js.map +1 -0
- package/dist/src/commands/docs/find.d.ts +2 -0
- package/dist/src/commands/docs/find.js +227 -0
- package/dist/src/commands/docs/find.js.map +1 -0
- package/dist/src/commands/docs/markdown.d.ts +8 -0
- package/dist/src/commands/docs/markdown.js +168 -0
- package/dist/src/commands/docs/markdown.js.map +1 -0
- package/dist/src/commands/docs/paths.d.ts +13 -0
- package/dist/src/commands/docs/paths.js +38 -0
- package/dist/src/commands/docs/paths.js.map +1 -0
- package/dist/src/commands/docs/read.d.ts +2 -0
- package/dist/src/commands/docs/read.js +235 -0
- package/dist/src/commands/docs/read.js.map +1 -0
- package/dist/src/commands/docs.d.ts +1 -1
- package/dist/src/commands/docs.js +125 -18
- package/dist/src/commands/docs.js.map +1 -1
- package/dist/src/google/client.js +3 -0
- package/dist/src/google/client.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -12,26 +12,68 @@ Designed for use by AI agents. Every response is structured, every error names a
|
|
|
12
12
|
- A Google account (personal or Workspace)
|
|
13
13
|
- `gcloud` CLI (optional — speeds up initial setup, not required)
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Getting started
|
|
16
|
+
|
|
17
|
+
### 1. Install
|
|
16
18
|
|
|
17
19
|
```bash
|
|
18
20
|
npm install -g gws-axi
|
|
19
21
|
```
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
### 2. Let an agent walk you through setup
|
|
24
|
+
|
|
25
|
+
`gws-axi` is designed to be driven by an AI coding agent (Claude Code, Cursor, anything that can run shell commands). The setup flow is progressive — the CLI emits structured TOON output telling the agent what to do next, and the agent coordinates with you through the handful of Google Cloud Console steps that can't be automated.
|
|
26
|
+
|
|
27
|
+
Start a fresh agent session and paste a prompt like:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
Run `gws-axi auth setup` and walk me through the progressive setup
|
|
31
|
+
flow it describes. The CLI's output will tell you exactly what to do
|
|
32
|
+
at each step — relay the instructions to me, and run follow-up
|
|
33
|
+
commands as you go.
|
|
34
|
+
|
|
35
|
+
I want to authenticate the following Google accounts:
|
|
36
|
+
- you@example.com
|
|
37
|
+
- personal@gmail.com
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The explicit `gws-axi auth setup` in the prompt matters: this is your very first session, so the agent doesn't yet have any ambient context about `gws-axi`. Naming the first command gets the agent into the structured flow, where the CLI's output takes over. Every subsequent session auto-loads a compact `gws-axi` status line via a session-start hook (installed on first invocation) — so after this one, you can skip the command and just say "use gws-axi to ..." and the agent knows.
|
|
41
|
+
|
|
42
|
+
Expect ~10 minutes end-to-end for a fresh Google Cloud project; ~3 minutes if you already have one you want to reuse.
|
|
22
43
|
|
|
23
|
-
|
|
44
|
+
**What happens during setup:** you create your own Google Cloud project with OAuth credentials, enable the Workspace APIs, configure the consent screen, and add your Google accounts as test users. This is the [**bring-your-own OAuth client**](docs/shared-client-future.md) model — avoids public-app verification overhead and keeps the blast radius of any token revocation scoped to you. Tokens live locally at `~/.config/gws-axi/`.
|
|
24
45
|
|
|
25
|
-
The
|
|
46
|
+
The CLI generates an auto-refreshing HTML helper page (`~/.config/gws-axi/setup.html`) with clickable Console deep-links — so you never copy long URLs from terminal to browser and you stay in the right browser profile throughout.
|
|
47
|
+
|
|
48
|
+
### 3. Check it works
|
|
26
49
|
|
|
27
50
|
```bash
|
|
28
|
-
gws-axi
|
|
29
|
-
gws-axi
|
|
51
|
+
gws-axi doctor # setup + live API health
|
|
52
|
+
gws-axi calendar events # upcoming 7 days on your primary calendar
|
|
30
53
|
```
|
|
31
54
|
|
|
32
|
-
|
|
55
|
+
### 4. Make gws-axi your agent's default for Google Workspace
|
|
56
|
+
|
|
57
|
+
On first invocation (which already happened during step 2), `gws-axi` auto-installed a **SessionStart hook** into your agent's config (Claude Code's `~/.claude/settings.json`, Codex's `~/.codex/hooks.json`). From now on, every new agent session will see a compact `gws-axi` status line in ambient context — authenticated accounts, write-protection status, setup health — with no extra wiring. Nothing for you to configure.
|
|
58
|
+
|
|
59
|
+
To make sure agents reach for `gws-axi` instead of stale alternatives, also consider:
|
|
60
|
+
|
|
61
|
+
- **Remove existing Google Workspace MCP servers and skills.** If you already have Gmail / Calendar / Drive / Docs / Slides integrations installed (community MCP servers, bespoke skills, etc.), disable them. Keeping both creates ambiguity — agents may pick whichever they noticed first, and the tools have different auth models, flag names, and output shapes. Typical candidates to disable: `google-calendar-mcp`, `gmail-mcp`, `google-docs-mcp`, any `@modelcontextprotocol/server-google-*`, and skills with similar names.
|
|
62
|
+
|
|
63
|
+
- **Add a directive to your `CLAUDE.md`.** A short note in your project-level (`.claude/CLAUDE.md`) or user-level (`~/.claude/CLAUDE.md`) file nudges agents to consistently use `gws-axi`. Example:
|
|
64
|
+
|
|
65
|
+
```markdown
|
|
66
|
+
## Google Workspace
|
|
67
|
+
|
|
68
|
+
Use `gws-axi` for ALL Google Calendar, Gmail, Docs, Drive, and Slides
|
|
69
|
+
interactions. Check the SessionStart `gws-axi` line for current auth
|
|
70
|
+
state; run `gws-axi --help` for the command surface. Prefer this over
|
|
71
|
+
any other Google integrations.
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Prefer to drive it manually?
|
|
33
75
|
|
|
34
|
-
|
|
76
|
+
The CLI works fine without an agent — each `gws-axi auth setup` invocation prints its own instructions and updates `setup.html`. Run it repeatedly as you complete each step; no state is lost between invocations.
|
|
35
77
|
|
|
36
78
|
## Usage
|
|
37
79
|
|
package/dist/src/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import { authCommand, AUTH_HELP } from "./commands/auth.js";
|
|
|
7
7
|
import { doctorCommand, DOCTOR_HELP } from "./commands/doctor.js";
|
|
8
8
|
import { calendarCommand } from "./commands/calendar.js";
|
|
9
9
|
import { gmailCommand, GMAIL_HELP } from "./commands/gmail.js";
|
|
10
|
-
import { docsCommand
|
|
10
|
+
import { docsCommand } from "./commands/docs.js";
|
|
11
11
|
import { driveCommand, DRIVE_HELP } from "./commands/drive.js";
|
|
12
12
|
import { slidesCommand, SLIDES_HELP } from "./commands/slides.js";
|
|
13
13
|
const DESCRIPTION = "Agent ergonomic CLI for Google Workspace. Unified interface for Gmail, Calendar, Docs, Drive, and Slides with agent-guided OAuth setup.";
|
|
@@ -30,7 +30,6 @@ const COMMAND_HELP = {
|
|
|
30
30
|
auth: AUTH_HELP,
|
|
31
31
|
doctor: DOCTOR_HELP,
|
|
32
32
|
gmail: GMAIL_HELP,
|
|
33
|
-
docs: DOCS_HELP,
|
|
34
33
|
drive: DRIVE_HELP,
|
|
35
34
|
slides: SLIDES_HELP,
|
|
36
35
|
};
|
package/dist/src/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAElE,MAAM,WAAW,GACf,yIAAyI,CAAC;AAE5I,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAC;AAErC,MAAM,CAAC,MAAM,QAAQ,GAAG;;;;;;;;;;CAUvB,CAAC;AAEF,0EAA0E;AAC1E,0EAA0E;AAC1E,iEAAiE;AACjE,MAAM,YAAY,GAA2B;IAC3C,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,WAAW;IACnB,KAAK,EAAE,UAAU;IACjB,KAAK,EAAE,UAAU;IACjB,MAAM,EAAE,WAAW;CACpB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,MAAM,SAAS,CAAC;QACd,WAAW,EAAE,WAAW;QACxB,OAAO,EAAE,OAAO;QAChB,YAAY,EAAE,QAAQ;QACtB,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,WAAW,EAAE;QAC/B,QAAQ,EAAE;YACR,IAAI,EAAE,WAAW;YACjB,MAAM,EAAE,aAAa;YACrB,QAAQ,EAAE,eAAe;YACzB,KAAK,EAAE,YAAY;YACnB,IAAI,EAAE,WAAW;YACjB,KAAK,EAAE,YAAY;YACnB,MAAM,EAAE,aAAa;SACtB;QACD,cAAc,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC;KACnD,CAAC,CAAC;AACL,CAAC;AAED,SAAS,kBAAkB;IACzB,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAErD,KAAK,MAAM,SAAS,IAAI;QACtB,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC;QAChC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC;KACvC,EAAE,CAAC;QACF,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3B,SAAS;QACX,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAEzD,CAAC;QACF,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpE,OAAO,MAAM,CAAC,OAAO,CAAC;QACxB,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;AACjE,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const COMMENTS_HELP = "usage: gws-axi docs comments <documentId> [flags]\nargs[1]:\n <documentId> The Google Doc ID (from the URL after /d/)\nflags[4]:\n --include-resolved Also show comments that have been resolved\n --full Don't truncate comment/reply bodies (default cap: 500 chars)\n --limit <n> Max comments to return (default: 50)\n --account <email> Account override when 2+ are configured\nexamples:\n gws-axi docs comments 1BxAbc...\n gws-axi docs comments 1BxAbc... --include-resolved --full\noutput:\n A `comments[N]{id,author,created,resolved,quoted_content,body,reply_count}`\n list and (when any comment has replies) a sibling `replies[N]{comment,author,created,body}`\n table keyed by parent comment id. Bodies and quoted_content are truncated\n at 500 chars unless --full is passed.\nnotes:\n `quoted_content` is the exact text in the doc that the comment is anchored\n to \u2014 important for agents to know what the comment is *about*. Comments\n without an anchor (overall-document comments) will have an empty value.\n";
|
|
2
|
+
export declare function docsCommentsCommand(account: string, args: string[]): Promise<string>;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { AxiError } from "axi-sdk-js";
|
|
2
|
+
import { driveClient, translateGoogleError } from "../../google/client.js";
|
|
3
|
+
import { field, joinBlocks, renderHelp, renderList, renderObject, } from "../../output/index.js";
|
|
4
|
+
export const COMMENTS_HELP = `usage: gws-axi docs comments <documentId> [flags]
|
|
5
|
+
args[1]:
|
|
6
|
+
<documentId> The Google Doc ID (from the URL after /d/)
|
|
7
|
+
flags[4]:
|
|
8
|
+
--include-resolved Also show comments that have been resolved
|
|
9
|
+
--full Don't truncate comment/reply bodies (default cap: 500 chars)
|
|
10
|
+
--limit <n> Max comments to return (default: 50)
|
|
11
|
+
--account <email> Account override when 2+ are configured
|
|
12
|
+
examples:
|
|
13
|
+
gws-axi docs comments 1BxAbc...
|
|
14
|
+
gws-axi docs comments 1BxAbc... --include-resolved --full
|
|
15
|
+
output:
|
|
16
|
+
A \`comments[N]{id,author,created,resolved,quoted_content,body,reply_count}\`
|
|
17
|
+
list and (when any comment has replies) a sibling \`replies[N]{comment,author,created,body}\`
|
|
18
|
+
table keyed by parent comment id. Bodies and quoted_content are truncated
|
|
19
|
+
at 500 chars unless --full is passed.
|
|
20
|
+
notes:
|
|
21
|
+
\`quoted_content\` is the exact text in the doc that the comment is anchored
|
|
22
|
+
to — important for agents to know what the comment is *about*. Comments
|
|
23
|
+
without an anchor (overall-document comments) will have an empty value.
|
|
24
|
+
`;
|
|
25
|
+
const DEFAULT_LIMIT = 50;
|
|
26
|
+
const BODY_TRUNCATE = 500;
|
|
27
|
+
function parseFlags(args) {
|
|
28
|
+
let documentId;
|
|
29
|
+
let includeResolved = false;
|
|
30
|
+
let full = false;
|
|
31
|
+
let limit = DEFAULT_LIMIT;
|
|
32
|
+
for (let i = 0; i < args.length; i++) {
|
|
33
|
+
const arg = args[i];
|
|
34
|
+
const next = args[i + 1];
|
|
35
|
+
switch (arg) {
|
|
36
|
+
case "--include-resolved":
|
|
37
|
+
includeResolved = true;
|
|
38
|
+
break;
|
|
39
|
+
case "--full":
|
|
40
|
+
full = true;
|
|
41
|
+
break;
|
|
42
|
+
case "--limit":
|
|
43
|
+
limit = Math.max(1, Math.min(100, parseInt(next, 10) || DEFAULT_LIMIT));
|
|
44
|
+
i++;
|
|
45
|
+
break;
|
|
46
|
+
default:
|
|
47
|
+
if (!arg.startsWith("--") && documentId === undefined) {
|
|
48
|
+
documentId = arg;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!documentId) {
|
|
53
|
+
throw new AxiError("Missing documentId argument", "VALIDATION_ERROR", ["Usage: gws-axi docs comments <documentId>"]);
|
|
54
|
+
}
|
|
55
|
+
return { documentId, includeResolved, full, limit };
|
|
56
|
+
}
|
|
57
|
+
function truncate(value, max) {
|
|
58
|
+
if (!value)
|
|
59
|
+
return "";
|
|
60
|
+
if (value.length <= max)
|
|
61
|
+
return value;
|
|
62
|
+
return `${value.slice(0, max - 1)}…`;
|
|
63
|
+
}
|
|
64
|
+
function commentSchema(truncateAt) {
|
|
65
|
+
return [
|
|
66
|
+
field("id"),
|
|
67
|
+
field("author"),
|
|
68
|
+
field("created"),
|
|
69
|
+
field("resolved"),
|
|
70
|
+
{
|
|
71
|
+
name: "quoted_content",
|
|
72
|
+
extract: (item) => truncate(item.quoted_content, truncateAt),
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "body",
|
|
76
|
+
extract: (item) => truncate(item.body, truncateAt),
|
|
77
|
+
},
|
|
78
|
+
field("reply_count"),
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
function replySchema(truncateAt) {
|
|
82
|
+
return [
|
|
83
|
+
field("comment"),
|
|
84
|
+
field("author"),
|
|
85
|
+
field("created"),
|
|
86
|
+
{
|
|
87
|
+
name: "body",
|
|
88
|
+
extract: (item) => truncate(item.body, truncateAt),
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
export async function docsCommentsCommand(account, args) {
|
|
93
|
+
const flags = parseFlags(args);
|
|
94
|
+
const api = await driveClient(account);
|
|
95
|
+
let data;
|
|
96
|
+
try {
|
|
97
|
+
const res = await api.comments.list({
|
|
98
|
+
fileId: flags.documentId,
|
|
99
|
+
includeDeleted: false,
|
|
100
|
+
pageSize: Math.min(100, flags.limit),
|
|
101
|
+
// Request every field we surface. Drive requires explicit `fields`
|
|
102
|
+
// projection on comments.list — the default response omits replies.
|
|
103
|
+
fields: "comments(id,content,quotedFileContent,author(displayName,emailAddress),createdTime,modifiedTime,resolved,anchor,replies(id,content,author(displayName,emailAddress),createdTime))",
|
|
104
|
+
});
|
|
105
|
+
data = res.data;
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
const translated = translateGoogleError(err, {
|
|
109
|
+
account,
|
|
110
|
+
operation: "drive.comments.list",
|
|
111
|
+
});
|
|
112
|
+
if (translated.code === "NOT_FOUND") {
|
|
113
|
+
throw new AxiError(`Document '${flags.documentId}' not found (or ${account} doesn't have access)`, "DOCUMENT_NOT_FOUND", [
|
|
114
|
+
`Verify the document ID is correct (the portion of the URL after /d/)`,
|
|
115
|
+
`Confirm ${account} has at least view access to the document`,
|
|
116
|
+
]);
|
|
117
|
+
}
|
|
118
|
+
throw translated;
|
|
119
|
+
}
|
|
120
|
+
const raw = data.comments ?? [];
|
|
121
|
+
const filtered = flags.includeResolved
|
|
122
|
+
? raw
|
|
123
|
+
: raw.filter((c) => !c.resolved);
|
|
124
|
+
const truncateAt = flags.full ? Number.POSITIVE_INFINITY : BODY_TRUNCATE;
|
|
125
|
+
const commentRows = filtered.map((c) => ({
|
|
126
|
+
id: c.id ?? "",
|
|
127
|
+
author: c.author?.displayName ?? c.author?.emailAddress ?? "",
|
|
128
|
+
created: c.createdTime ?? "",
|
|
129
|
+
resolved: c.resolved ?? false,
|
|
130
|
+
quoted_content: c.quotedFileContent?.value ?? "",
|
|
131
|
+
body: c.content ?? "",
|
|
132
|
+
reply_count: c.replies?.length ?? 0,
|
|
133
|
+
}));
|
|
134
|
+
const replyRows = filtered.flatMap((c) => (c.replies ?? []).map((r) => ({
|
|
135
|
+
comment: c.id ?? "",
|
|
136
|
+
author: r.author?.displayName ?? r.author?.emailAddress ?? "",
|
|
137
|
+
created: r.createdTime ?? "",
|
|
138
|
+
body: r.content ?? "",
|
|
139
|
+
})));
|
|
140
|
+
const blocks = [];
|
|
141
|
+
blocks.push(renderObject({ account, document: flags.documentId }));
|
|
142
|
+
if (commentRows.length === 0) {
|
|
143
|
+
blocks.push(renderObject({ comments: [] }));
|
|
144
|
+
blocks.push(renderObject({
|
|
145
|
+
message: flags.includeResolved
|
|
146
|
+
? "no comments on this document"
|
|
147
|
+
: "no open comments (pass --include-resolved to include resolved)",
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
blocks.push(renderList("comments", commentRows, commentSchema(truncateAt)));
|
|
152
|
+
if (replyRows.length > 0) {
|
|
153
|
+
blocks.push(renderList("replies", replyRows, replySchema(truncateAt)));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const suggestions = [];
|
|
157
|
+
const resolvedHidden = raw.length - filtered.length;
|
|
158
|
+
if (!flags.includeResolved && resolvedHidden > 0) {
|
|
159
|
+
suggestions.push(`${resolvedHidden} resolved comment${resolvedHidden === 1 ? "" : "s"} hidden — add --include-resolved to see them`);
|
|
160
|
+
}
|
|
161
|
+
if (commentRows.length > 0 && !flags.full) {
|
|
162
|
+
const someTruncated = commentRows.some((r) => (typeof r.body === "string" && r.body.length > BODY_TRUNCATE) ||
|
|
163
|
+
(typeof r.quoted_content === "string" && r.quoted_content.length > BODY_TRUNCATE));
|
|
164
|
+
if (someTruncated) {
|
|
165
|
+
suggestions.push(`Add --full to see complete bodies / quoted content`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (suggestions.length > 0)
|
|
169
|
+
blocks.push(renderHelp(suggestions));
|
|
170
|
+
return joinBlocks(...blocks);
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=comments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comments.js","sourceRoot":"","sources":["../../../../src/commands/docs/comments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const DOWNLOAD_HELP = "usage: gws-axi docs download <documentId> [flags]\nargs[1]:\n <documentId> The Google Doc / Drive file ID (from the URL after /d/)\nflags[3]:\n --out <path> Where to save (default: ./<sanitized file name>; pass a\n directory to save inside with the native name)\n --as <mime> Export format for native Google files (default: docx for\n Docs, xlsx for Sheets, pptx for Slides, png for Drawings).\n Ignored for uploaded files (they download as-is).\n --account <email> Account override when 2+ are configured\nexamples:\n gws-axi docs download 1RxHZ...\n gws-axi docs download 1RxHZ... --out /tmp/source.docx\n gws-axi docs download 1Native... --as application/pdf --out ./spec.pdf\nnotes:\n Works on any Drive file, not just docs. Native Google files are exported\n on the server side (via Drive files.export); uploaded files (.docx, .pdf,\n images, etc.) are downloaded as-is. After download, agents can inspect\n the file with tools like `file`, `pandoc`, `textutil -convert html`,\n or language-specific parsers for the file's format.\n";
|
|
2
|
+
export declare function docsDownloadCommand(account: string, args: string[]): Promise<string>;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { writeFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import { basename, dirname, extname } from "node:path";
|
|
3
|
+
import { AxiError } from "axi-sdk-js";
|
|
4
|
+
import { driveClient, translateGoogleError } from "../../google/client.js";
|
|
5
|
+
import { joinBlocks, renderHelp, renderObject } from "../../output/index.js";
|
|
6
|
+
import { resolveOutputPath } from "./paths.js";
|
|
7
|
+
export const DOWNLOAD_HELP = `usage: gws-axi docs download <documentId> [flags]
|
|
8
|
+
args[1]:
|
|
9
|
+
<documentId> The Google Doc / Drive file ID (from the URL after /d/)
|
|
10
|
+
flags[3]:
|
|
11
|
+
--out <path> Where to save (default: ./<sanitized file name>; pass a
|
|
12
|
+
directory to save inside with the native name)
|
|
13
|
+
--as <mime> Export format for native Google files (default: docx for
|
|
14
|
+
Docs, xlsx for Sheets, pptx for Slides, png for Drawings).
|
|
15
|
+
Ignored for uploaded files (they download as-is).
|
|
16
|
+
--account <email> Account override when 2+ are configured
|
|
17
|
+
examples:
|
|
18
|
+
gws-axi docs download 1RxHZ...
|
|
19
|
+
gws-axi docs download 1RxHZ... --out /tmp/source.docx
|
|
20
|
+
gws-axi docs download 1Native... --as application/pdf --out ./spec.pdf
|
|
21
|
+
notes:
|
|
22
|
+
Works on any Drive file, not just docs. Native Google files are exported
|
|
23
|
+
on the server side (via Drive files.export); uploaded files (.docx, .pdf,
|
|
24
|
+
images, etc.) are downloaded as-is. After download, agents can inspect
|
|
25
|
+
the file with tools like \`file\`, \`pandoc\`, \`textutil -convert html\`,
|
|
26
|
+
or language-specific parsers for the file's format.
|
|
27
|
+
`;
|
|
28
|
+
// Default export mime types for native Google file types.
|
|
29
|
+
const NATIVE_EXPORT_DEFAULTS = {
|
|
30
|
+
"application/vnd.google-apps.document": {
|
|
31
|
+
mime: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
32
|
+
extension: ".docx",
|
|
33
|
+
},
|
|
34
|
+
"application/vnd.google-apps.spreadsheet": {
|
|
35
|
+
mime: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
36
|
+
extension: ".xlsx",
|
|
37
|
+
},
|
|
38
|
+
"application/vnd.google-apps.presentation": {
|
|
39
|
+
mime: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
40
|
+
extension: ".pptx",
|
|
41
|
+
},
|
|
42
|
+
"application/vnd.google-apps.drawing": {
|
|
43
|
+
mime: "image/png",
|
|
44
|
+
extension: ".png",
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
// When the user passes --as, try to guess a file extension from the mime.
|
|
48
|
+
const EXTENSION_BY_MIME = {
|
|
49
|
+
"application/pdf": ".pdf",
|
|
50
|
+
"text/plain": ".txt",
|
|
51
|
+
"text/markdown": ".md",
|
|
52
|
+
"text/html": ".html",
|
|
53
|
+
"text/csv": ".csv",
|
|
54
|
+
"application/rtf": ".rtf",
|
|
55
|
+
"image/png": ".png",
|
|
56
|
+
"image/jpeg": ".jpg",
|
|
57
|
+
"image/svg+xml": ".svg",
|
|
58
|
+
"application/epub+zip": ".epub",
|
|
59
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
|
|
60
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx",
|
|
61
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation": ".pptx",
|
|
62
|
+
"application/vnd.oasis.opendocument.text": ".odt",
|
|
63
|
+
};
|
|
64
|
+
function parseFlags(args) {
|
|
65
|
+
let documentId;
|
|
66
|
+
let out;
|
|
67
|
+
let as;
|
|
68
|
+
for (let i = 0; i < args.length; i++) {
|
|
69
|
+
const arg = args[i];
|
|
70
|
+
const next = args[i + 1];
|
|
71
|
+
switch (arg) {
|
|
72
|
+
case "--out":
|
|
73
|
+
out = next;
|
|
74
|
+
i++;
|
|
75
|
+
break;
|
|
76
|
+
case "--as":
|
|
77
|
+
as = next;
|
|
78
|
+
i++;
|
|
79
|
+
break;
|
|
80
|
+
default:
|
|
81
|
+
if (!arg.startsWith("--") && documentId === undefined) {
|
|
82
|
+
documentId = arg;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (!documentId) {
|
|
87
|
+
throw new AxiError("Missing documentId argument", "VALIDATION_ERROR", ["Usage: gws-axi docs download <documentId>"]);
|
|
88
|
+
}
|
|
89
|
+
return { documentId, out, as };
|
|
90
|
+
}
|
|
91
|
+
export async function docsDownloadCommand(account, args) {
|
|
92
|
+
const flags = parseFlags(args);
|
|
93
|
+
const api = await driveClient(account);
|
|
94
|
+
// Step 1: metadata — we need name, mimeType, and size to decide whether
|
|
95
|
+
// to export or media-download and to build a useful response.
|
|
96
|
+
let meta;
|
|
97
|
+
try {
|
|
98
|
+
const res = await api.files.get({
|
|
99
|
+
fileId: flags.documentId,
|
|
100
|
+
fields: "id,name,mimeType,size",
|
|
101
|
+
supportsAllDrives: true,
|
|
102
|
+
});
|
|
103
|
+
meta = {
|
|
104
|
+
name: res.data.name ?? "download",
|
|
105
|
+
mimeType: res.data.mimeType ?? "application/octet-stream",
|
|
106
|
+
size: res.data.size ?? undefined,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
const translated = translateGoogleError(err, {
|
|
111
|
+
account,
|
|
112
|
+
operation: "drive.files.get",
|
|
113
|
+
});
|
|
114
|
+
if (translated.code === "NOT_FOUND") {
|
|
115
|
+
throw new AxiError(`File '${flags.documentId}' not found (or ${account} doesn't have access)`, "FILE_NOT_FOUND", [
|
|
116
|
+
`Verify the file ID is correct (the portion of the URL after /d/)`,
|
|
117
|
+
`Confirm ${account} has at least view access to the file`,
|
|
118
|
+
]);
|
|
119
|
+
}
|
|
120
|
+
throw translated;
|
|
121
|
+
}
|
|
122
|
+
const isNative = meta.mimeType.startsWith("application/vnd.google-apps.");
|
|
123
|
+
const nativeDefault = NATIVE_EXPORT_DEFAULTS[meta.mimeType];
|
|
124
|
+
if (isNative && !nativeDefault && !flags.as) {
|
|
125
|
+
throw new AxiError(`No default export format for native mime type ${meta.mimeType}`, "EXPORT_FORMAT_REQUIRED", [
|
|
126
|
+
"Pass --as <mime> to choose an export format",
|
|
127
|
+
"Common options: application/pdf, text/plain, text/html",
|
|
128
|
+
]);
|
|
129
|
+
}
|
|
130
|
+
// Step 2: fetch bytes.
|
|
131
|
+
let bytes;
|
|
132
|
+
let effectiveMime;
|
|
133
|
+
let extension;
|
|
134
|
+
if (isNative) {
|
|
135
|
+
effectiveMime = flags.as ?? nativeDefault.mime;
|
|
136
|
+
extension = EXTENSION_BY_MIME[effectiveMime] ?? nativeDefault?.extension ?? "";
|
|
137
|
+
try {
|
|
138
|
+
const res = await api.files.export({ fileId: flags.documentId, mimeType: effectiveMime }, { responseType: "arraybuffer" });
|
|
139
|
+
bytes = Buffer.from(res.data);
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
throw translateGoogleError(err, {
|
|
143
|
+
account,
|
|
144
|
+
operation: "drive.files.export",
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
if (flags.as) {
|
|
150
|
+
throw new AxiError(`--as is only valid for native Google files; this is ${meta.mimeType}`, "VALIDATION_ERROR", [
|
|
151
|
+
"Drop --as to download the file as-is",
|
|
152
|
+
`The file will be saved with its original mime type (${meta.mimeType})`,
|
|
153
|
+
]);
|
|
154
|
+
}
|
|
155
|
+
effectiveMime = meta.mimeType;
|
|
156
|
+
extension = extname(meta.name) || EXTENSION_BY_MIME[effectiveMime] || "";
|
|
157
|
+
try {
|
|
158
|
+
const res = await api.files.get({ fileId: flags.documentId, alt: "media", supportsAllDrives: true }, { responseType: "arraybuffer" });
|
|
159
|
+
bytes = Buffer.from(res.data);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
throw translateGoogleError(err, {
|
|
163
|
+
account,
|
|
164
|
+
operation: "drive.files.get(alt=media)",
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Step 3: compute output path. For native files we might need to append
|
|
169
|
+
// the extension because meta.name doesn't carry one for Google-native
|
|
170
|
+
// files (e.g., "My Doc" rather than "My Doc.docx").
|
|
171
|
+
let baseName = meta.name;
|
|
172
|
+
if (isNative && extension && !baseName.toLowerCase().endsWith(extension.toLowerCase())) {
|
|
173
|
+
baseName += extension;
|
|
174
|
+
}
|
|
175
|
+
const outPath = await resolveOutputPath(flags.out, baseName);
|
|
176
|
+
await mkdir(dirname(outPath), { recursive: true });
|
|
177
|
+
await writeFile(outPath, bytes);
|
|
178
|
+
// Step 4: response.
|
|
179
|
+
const blocks = [];
|
|
180
|
+
blocks.push(renderObject({ account }));
|
|
181
|
+
blocks.push(renderObject({
|
|
182
|
+
file: {
|
|
183
|
+
id: flags.documentId,
|
|
184
|
+
name: meta.name,
|
|
185
|
+
source_mime_type: meta.mimeType,
|
|
186
|
+
saved_mime_type: effectiveMime,
|
|
187
|
+
size_bytes: bytes.length,
|
|
188
|
+
},
|
|
189
|
+
}));
|
|
190
|
+
blocks.push(renderObject({ saved: outPath }));
|
|
191
|
+
const suggestions = [];
|
|
192
|
+
if (effectiveMime === "application/vnd.openxmlformats-officedocument.wordprocessingml.document") {
|
|
193
|
+
suggestions.push(`This is a .docx — extract text with \`pandoc "${basename(outPath)}" -t plain\` or \`textutil -convert txt "${basename(outPath)}"\``);
|
|
194
|
+
}
|
|
195
|
+
else if (effectiveMime === "application/pdf") {
|
|
196
|
+
suggestions.push(`This is a PDF — inspect with \`pdftotext "${basename(outPath)}" -\` or open in Preview`);
|
|
197
|
+
}
|
|
198
|
+
else if (effectiveMime.startsWith("text/")) {
|
|
199
|
+
suggestions.push(`Text file — \`cat "${outPath}"\` to view`);
|
|
200
|
+
}
|
|
201
|
+
else if (effectiveMime.startsWith("image/")) {
|
|
202
|
+
suggestions.push(`Image file — open with \`open "${outPath}"\` on macOS`);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
suggestions.push(`Inspect with \`file "${outPath}"\` to identify the format`);
|
|
206
|
+
}
|
|
207
|
+
if (isNative) {
|
|
208
|
+
suggestions.push(`Native Google file exported as ${effectiveMime} — use --as <mime> to pick a different format (e.g. application/pdf, text/plain)`);
|
|
209
|
+
}
|
|
210
|
+
blocks.push(renderHelp(suggestions));
|
|
211
|
+
return joinBlocks(...blocks);
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=download.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.js","sourceRoot":"","sources":["../../../../src/commands/docs/download.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const FIND_HELP = "usage: gws-axi docs find <documentId> --query <text> [flags]\nargs[1]:\n <documentId> The Google Doc ID (from the URL after /d/)\nflags[4]:\n --query <text> Text to search for (case-insensitive; required)\n --tab <id> Tab to search within (default: first tab)\n --limit <n> Max matches to return (default: 50)\n --account <email> Account override when 2+ are configured\nexamples:\n gws-axi docs find 1BxAbc... --query \"sprint goal\"\n gws-axi docs find 1BxAbc... --query foo --tab t.1 --limit 10\noutput:\n A `matches[N]{ref,paragraph,start,end,context}` table. The `ref`\n column holds compact `@N` handles that future mutation subcommands\n (insert-text, delete-range) will accept as shorthand for the matched\n range. `start` and `end` are raw Docs API character offsets.\nnotes:\n Search is restricted to one tab per call \u2014 v1 does not fan out across\n sibling tabs. Use `gws-axi docs read <id>` (no --tab) to see the tabs\n list and pick one.\n";
|
|
2
|
+
export declare function docsFindCommand(account: string, args: string[]): Promise<string>;
|