@marcfargas/go-easy 0.0.1 → 0.3.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/CHANGELOG.md +90 -0
- package/LICENSE +21 -0
- package/README.md +224 -0
- package/dist/auth-flow.d.ts +50 -0
- package/dist/auth-flow.d.ts.map +1 -0
- package/dist/auth-flow.js +219 -0
- package/dist/auth-flow.js.map +1 -0
- package/dist/auth-server.d.ts +18 -0
- package/dist/auth-server.d.ts.map +1 -0
- package/dist/auth-server.js +327 -0
- package/dist/auth-server.js.map +1 -0
- package/dist/auth-store.d.ts +81 -0
- package/dist/auth-store.d.ts.map +1 -0
- package/dist/auth-store.js +185 -0
- package/dist/auth-store.js.map +1 -0
- package/dist/auth.d.ts +47 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +131 -0
- package/dist/auth.js.map +1 -0
- package/dist/bin/calendar.d.ts +17 -0
- package/dist/bin/calendar.d.ts.map +1 -0
- package/dist/bin/calendar.js +224 -0
- package/dist/bin/calendar.js.map +1 -0
- package/dist/bin/drive.d.ts +18 -0
- package/dist/bin/drive.d.ts.map +1 -0
- package/dist/bin/drive.js +205 -0
- package/dist/bin/drive.js.map +1 -0
- package/dist/bin/easy.d.ts +11 -0
- package/dist/bin/easy.d.ts.map +1 -0
- package/dist/bin/easy.js +140 -0
- package/dist/bin/easy.js.map +1 -0
- package/dist/bin/gmail.d.ts +25 -0
- package/dist/bin/gmail.d.ts.map +1 -0
- package/dist/bin/gmail.js +243 -0
- package/dist/bin/gmail.js.map +1 -0
- package/dist/bin/tasks.d.ts +17 -0
- package/dist/bin/tasks.d.ts.map +1 -0
- package/dist/bin/tasks.js +190 -0
- package/dist/bin/tasks.js.map +1 -0
- package/dist/calendar/helpers.d.ts +35 -0
- package/dist/calendar/helpers.d.ts.map +1 -0
- package/dist/calendar/helpers.js +178 -0
- package/dist/calendar/helpers.js.map +1 -0
- package/dist/calendar/index.d.ts +64 -0
- package/dist/calendar/index.d.ts.map +1 -0
- package/dist/calendar/index.js +210 -0
- package/dist/calendar/index.js.map +1 -0
- package/dist/calendar/types.d.ts +191 -0
- package/dist/calendar/types.d.ts.map +1 -0
- package/dist/calendar/types.js +12 -0
- package/dist/calendar/types.js.map +1 -0
- package/dist/drive/helpers.d.ts +22 -0
- package/dist/drive/helpers.d.ts.map +1 -0
- package/dist/drive/helpers.js +85 -0
- package/dist/drive/helpers.js.map +1 -0
- package/dist/drive/index.d.ts +114 -0
- package/dist/drive/index.d.ts.map +1 -0
- package/dist/drive/index.js +418 -0
- package/dist/drive/index.js.map +1 -0
- package/dist/drive/types.d.ts +91 -0
- package/dist/drive/types.d.ts.map +1 -0
- package/dist/drive/types.js +5 -0
- package/dist/drive/types.js.map +1 -0
- package/dist/errors.d.ts +58 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +73 -0
- package/dist/errors.js.map +1 -0
- package/dist/gmail/helpers.d.ts +59 -0
- package/dist/gmail/helpers.d.ts.map +1 -0
- package/dist/gmail/helpers.js +308 -0
- package/dist/gmail/helpers.js.map +1 -0
- package/dist/gmail/index.d.ts +95 -0
- package/dist/gmail/index.d.ts.map +1 -0
- package/dist/gmail/index.js +465 -0
- package/dist/gmail/index.js.map +1 -0
- package/dist/gmail/markdown.d.ts +22 -0
- package/dist/gmail/markdown.d.ts.map +1 -0
- package/dist/gmail/markdown.js +30 -0
- package/dist/gmail/markdown.js.map +1 -0
- package/dist/gmail/types.d.ts +154 -0
- package/dist/gmail/types.d.ts.map +1 -0
- package/dist/gmail/types.js +5 -0
- package/dist/gmail/types.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/safety.d.ts +58 -0
- package/dist/safety.d.ts.map +1 -0
- package/dist/safety.js +61 -0
- package/dist/safety.js.map +1 -0
- package/dist/scopes.d.ts +16 -0
- package/dist/scopes.d.ts.map +1 -0
- package/dist/scopes.js +28 -0
- package/dist/scopes.js.map +1 -0
- package/dist/tasks/helpers.d.ts +10 -0
- package/dist/tasks/helpers.d.ts.map +1 -0
- package/dist/tasks/helpers.js +33 -0
- package/dist/tasks/helpers.js.map +1 -0
- package/dist/tasks/index.d.ts +63 -0
- package/dist/tasks/index.d.ts.map +1 -0
- package/dist/tasks/index.js +253 -0
- package/dist/tasks/index.js.map +1 -0
- package/dist/tasks/types.d.ts +79 -0
- package/dist/tasks/types.d.ts.map +1 -0
- package/dist/tasks/types.js +5 -0
- package/dist/tasks/types.js.map +1 -0
- package/package.json +73 -4
- package/skills/go-easy/SKILL.md +146 -0
- package/skills/go-easy/calendar.md +366 -0
- package/skills/go-easy/drive.md +309 -0
- package/skills/go-easy/gmail.md +478 -0
- package/skills/go-easy/tasks.md +260 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
# go-easy: Calendar Reference
|
|
2
|
+
|
|
3
|
+
## Gateway CLI: `npx go-calendar`
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
npx go-calendar <account> <command> [args...] [--flags]
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
All commands output JSON to stdout. Errors output JSON to stderr with exit code 1.
|
|
10
|
+
|
|
11
|
+
### Commands
|
|
12
|
+
|
|
13
|
+
#### calendars
|
|
14
|
+
List all calendars for the account.
|
|
15
|
+
```bash
|
|
16
|
+
npx go-calendar <account> calendars
|
|
17
|
+
```
|
|
18
|
+
Returns: `Array<{ id, summary, description?, primary?, timeZone?, backgroundColor? }>` (bare array)
|
|
19
|
+
|
|
20
|
+
Use `primary` as calendarId for the main calendar in other commands.
|
|
21
|
+
|
|
22
|
+
#### events
|
|
23
|
+
List events on a calendar. By default returns ALL event types (regular, out-of-office,
|
|
24
|
+
working location, focus time, birthdays).
|
|
25
|
+
```bash
|
|
26
|
+
# Upcoming events (all types)
|
|
27
|
+
npx go-calendar <account> events primary
|
|
28
|
+
|
|
29
|
+
# Date range
|
|
30
|
+
npx go-calendar <account> events primary \
|
|
31
|
+
--from=2026-02-01T00:00:00Z \
|
|
32
|
+
--to=2026-02-28T23:59:59Z
|
|
33
|
+
|
|
34
|
+
# With text search and pagination
|
|
35
|
+
npx go-calendar <account> events primary --query="meeting" --max=10
|
|
36
|
+
npx go-calendar <account> events primary --max=50 --page-token=<token>
|
|
37
|
+
|
|
38
|
+
# Filter by event type
|
|
39
|
+
npx go-calendar <account> events primary --event-types=workingLocation
|
|
40
|
+
npx go-calendar <account> events primary --event-types=default,outOfOffice
|
|
41
|
+
```
|
|
42
|
+
Returns: `{ items: CalendarEvent[], nextPageToken? }`
|
|
43
|
+
|
|
44
|
+
**Defaults:**
|
|
45
|
+
- `--max`: 20 per page
|
|
46
|
+
- `--from`: now (if omitted, returns events from current time onward)
|
|
47
|
+
- `--to`: no upper bound (if omitted, returns all future events up to `--max`)
|
|
48
|
+
- Recurring events are expanded into individual instances (singleEvents=true)
|
|
49
|
+
- All event types included (the Google API hides workingLocation and birthday by default — go-easy includes them)
|
|
50
|
+
|
|
51
|
+
**Event types**: `default`, `outOfOffice`, `workingLocation`, `focusTime`, `birthday`
|
|
52
|
+
|
|
53
|
+
#### event
|
|
54
|
+
Get a single event by ID.
|
|
55
|
+
```bash
|
|
56
|
+
npx go-calendar <account> event <calendarId> <eventId>
|
|
57
|
+
```
|
|
58
|
+
Returns: `CalendarEvent`
|
|
59
|
+
|
|
60
|
+
For recurring events, this returns the recurring event definition, not individual instances.
|
|
61
|
+
Use the `recurringEventId` from a listed instance to find its parent.
|
|
62
|
+
|
|
63
|
+
#### create (WRITE)
|
|
64
|
+
Create a new event.
|
|
65
|
+
|
|
66
|
+
**Required flags:** `--summary`, `--start`, `--end`
|
|
67
|
+
**Optional flags:** `--description`, `--location`, `--tz`, `--attendees`, `--all-day`, `--type` + type-specific flags
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Regular timed event
|
|
71
|
+
npx go-calendar <account> create primary \
|
|
72
|
+
--summary="Team Meeting" \
|
|
73
|
+
--start=2026-02-10T10:00:00+01:00 \
|
|
74
|
+
--end=2026-02-10T11:00:00+01:00 \
|
|
75
|
+
--description="Weekly sync" \
|
|
76
|
+
--location="Office"
|
|
77
|
+
|
|
78
|
+
# All-day event (end date is EXCLUSIVE — Feb 14 only)
|
|
79
|
+
npx go-calendar <account> create primary \
|
|
80
|
+
--summary="Company Holiday" \
|
|
81
|
+
--start=2026-02-14 \
|
|
82
|
+
--end=2026-02-15 \
|
|
83
|
+
--all-day
|
|
84
|
+
|
|
85
|
+
# With attendees (invitation emails sent automatically)
|
|
86
|
+
npx go-calendar <account> create primary \
|
|
87
|
+
--summary="Project Review" \
|
|
88
|
+
--start=2026-02-12T14:00:00+01:00 \
|
|
89
|
+
--end=2026-02-12T15:00:00+01:00 \
|
|
90
|
+
--attendees=alice@example.com,bob@example.com
|
|
91
|
+
|
|
92
|
+
# Working location — home office
|
|
93
|
+
npx go-calendar <account> create primary \
|
|
94
|
+
--type=workingLocation \
|
|
95
|
+
--summary="Home" \
|
|
96
|
+
--start=2026-02-10 --end=2026-02-11 --all-day \
|
|
97
|
+
--wl-type=homeOffice
|
|
98
|
+
|
|
99
|
+
# Working location — office
|
|
100
|
+
npx go-calendar <account> create primary \
|
|
101
|
+
--type=workingLocation \
|
|
102
|
+
--summary="Barcelona" \
|
|
103
|
+
--start=2026-02-10 --end=2026-02-11 --all-day \
|
|
104
|
+
--wl-type=officeLocation --wl-label="Barcelona Office"
|
|
105
|
+
|
|
106
|
+
# Out of office ⚠️ --auto-decline sends decline emails to existing invitations
|
|
107
|
+
npx go-calendar <account> create primary \
|
|
108
|
+
--type=outOfOffice \
|
|
109
|
+
--summary="Vacation" \
|
|
110
|
+
--start=2026-02-14T00:00:00+01:00 \
|
|
111
|
+
--end=2026-02-21T00:00:00+01:00 \
|
|
112
|
+
--auto-decline=declineAllConflictingInvitations \
|
|
113
|
+
--decline-message="On vacation, back Feb 21"
|
|
114
|
+
|
|
115
|
+
# Focus time ⚠️ --auto-decline sends decline emails to existing invitations
|
|
116
|
+
npx go-calendar <account> create primary \
|
|
117
|
+
--type=focusTime \
|
|
118
|
+
--summary="Deep Work" \
|
|
119
|
+
--start=2026-02-10T09:00:00+01:00 \
|
|
120
|
+
--end=2026-02-10T12:00:00+01:00 \
|
|
121
|
+
--auto-decline=declineOnlyNewConflictingInvitations \
|
|
122
|
+
--chat-status=doNotDisturb
|
|
123
|
+
```
|
|
124
|
+
Returns: `{ ok: true, id, htmlLink? }`
|
|
125
|
+
|
|
126
|
+
#### update (WRITE)
|
|
127
|
+
Update an existing event. Uses PATCH — only provided fields are changed, others are preserved.
|
|
128
|
+
|
|
129
|
+
⚠️ If the event has attendees, update notifications will be sent automatically.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Update just the summary (other fields unchanged)
|
|
133
|
+
npx go-calendar <account> update primary <eventId> \
|
|
134
|
+
--summary="Updated Meeting"
|
|
135
|
+
|
|
136
|
+
# Reschedule
|
|
137
|
+
npx go-calendar <account> update primary <eventId> \
|
|
138
|
+
--summary="Updated Meeting" \
|
|
139
|
+
--start=2026-02-10T11:00:00+01:00 \
|
|
140
|
+
--end=2026-02-10T12:00:00+01:00
|
|
141
|
+
|
|
142
|
+
# Update attendees
|
|
143
|
+
npx go-calendar <account> update primary <eventId> \
|
|
144
|
+
--summary="Review" \
|
|
145
|
+
--start=2026-02-10T14:00:00+01:00 \
|
|
146
|
+
--end=2026-02-10T15:00:00+01:00 \
|
|
147
|
+
--attendees=alice@example.com,bob@example.com,carol@example.com
|
|
148
|
+
```
|
|
149
|
+
Returns: `{ ok: true, id, htmlLink? }`
|
|
150
|
+
|
|
151
|
+
**Required flags:** `--summary`, `--start`, `--end` (always required even for PATCH — Google API needs them)
|
|
152
|
+
**Optional flags:** `--description`, `--location`, `--tz`, `--attendees`, `--all-day`
|
|
153
|
+
|
|
154
|
+
#### delete ⚠️ DESTRUCTIVE
|
|
155
|
+
Delete an event. Requires `--confirm`.
|
|
156
|
+
```bash
|
|
157
|
+
npx go-calendar <account> delete primary <eventId> --confirm
|
|
158
|
+
```
|
|
159
|
+
Returns: `{ ok: true, id }`
|
|
160
|
+
|
|
161
|
+
⚠️ If the event has attendees, cancellation emails will be sent automatically.
|
|
162
|
+
|
|
163
|
+
Without `--confirm`:
|
|
164
|
+
```json
|
|
165
|
+
{ "blocked": true, "operation": "calendar.delete", "description": "Delete event \"Meeting\" with 3 attendees — cancellation emails will be sent", "hint": "Add --confirm" }
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
#### freebusy
|
|
169
|
+
Check availability across calendars.
|
|
170
|
+
```bash
|
|
171
|
+
# Single calendar
|
|
172
|
+
npx go-calendar <account> freebusy primary \
|
|
173
|
+
--from=2026-02-10T00:00:00Z \
|
|
174
|
+
--to=2026-02-10T23:59:59Z
|
|
175
|
+
|
|
176
|
+
# Multiple calendars
|
|
177
|
+
npx go-calendar <account> freebusy primary,colleague@example.com \
|
|
178
|
+
--from=2026-02-10T08:00:00Z \
|
|
179
|
+
--to=2026-02-10T18:00:00Z
|
|
180
|
+
```
|
|
181
|
+
Returns: `Array<{ calendarId, busy: [{ start, end }] }>`
|
|
182
|
+
|
|
183
|
+
**Required flags:** `--from`, `--to`
|
|
184
|
+
|
|
185
|
+
## Library API
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { getAuth } from '@marcfargas/go-easy/auth';
|
|
189
|
+
import { listCalendars, listEvents, getEvent, createEvent,
|
|
190
|
+
updateEvent, deleteEvent, queryFreeBusy
|
|
191
|
+
} from '@marcfargas/go-easy/calendar';
|
|
192
|
+
import { setSafetyContext } from '@marcfargas/go-easy';
|
|
193
|
+
|
|
194
|
+
const auth = await getAuth('calendar', '<account>');
|
|
195
|
+
|
|
196
|
+
// List calendars
|
|
197
|
+
const cals = await listCalendars(auth);
|
|
198
|
+
|
|
199
|
+
// List events (with pagination)
|
|
200
|
+
const page1 = await listEvents(auth, 'primary', {
|
|
201
|
+
timeMin: '2026-02-01T00:00:00Z',
|
|
202
|
+
timeMax: '2026-02-28T23:59:59Z',
|
|
203
|
+
maxResults: 50,
|
|
204
|
+
});
|
|
205
|
+
if (page1.nextPageToken) {
|
|
206
|
+
const page2 = await listEvents(auth, 'primary', {
|
|
207
|
+
timeMin: '2026-02-01T00:00:00Z',
|
|
208
|
+
timeMax: '2026-02-28T23:59:59Z',
|
|
209
|
+
maxResults: 50,
|
|
210
|
+
pageToken: page1.nextPageToken,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Get single event
|
|
215
|
+
const event = await getEvent(auth, 'primary', 'eventId');
|
|
216
|
+
|
|
217
|
+
// Create event (WRITE — no safety gate)
|
|
218
|
+
const created = await createEvent(auth, 'primary', {
|
|
219
|
+
summary: 'Meeting',
|
|
220
|
+
start: '2026-02-10T10:00:00+01:00',
|
|
221
|
+
end: '2026-02-10T11:00:00+01:00',
|
|
222
|
+
timeZone: 'Europe/Madrid',
|
|
223
|
+
location: 'Office',
|
|
224
|
+
attendees: ['alice@example.com'],
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Update event (WRITE — PATCH semantics)
|
|
228
|
+
await updateEvent(auth, 'primary', 'eventId', {
|
|
229
|
+
summary: 'Updated',
|
|
230
|
+
start: '2026-02-10T11:00:00+01:00',
|
|
231
|
+
end: '2026-02-10T12:00:00+01:00',
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// Delete event (DESTRUCTIVE — needs safety context)
|
|
235
|
+
setSafetyContext({ confirm: async (op) => { /* ... */ return true; } });
|
|
236
|
+
await deleteEvent(auth, 'primary', 'eventId');
|
|
237
|
+
|
|
238
|
+
// Free/busy
|
|
239
|
+
const availability = await queryFreeBusy(
|
|
240
|
+
auth,
|
|
241
|
+
['primary', 'colleague@example.com'],
|
|
242
|
+
'2026-02-10T08:00:00Z',
|
|
243
|
+
'2026-02-10T18:00:00Z'
|
|
244
|
+
);
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Date/Time Formats
|
|
248
|
+
|
|
249
|
+
- **Timed events**: ISO 8601 with offset — `2026-02-10T10:00:00+01:00` or UTC `2026-02-10T09:00:00Z`
|
|
250
|
+
- **All-day events**: Date only — `2026-02-14` (with `--all-day` flag in CLI)
|
|
251
|
+
- **Timezone (`--tz`)**: IANA format — `Europe/Madrid`, `America/New_York`, etc.
|
|
252
|
+
|
|
253
|
+
### Timezone semantics
|
|
254
|
+
|
|
255
|
+
- If `--start`/`--end` include an offset (e.g. `+01:00`), that offset is used directly
|
|
256
|
+
- `--tz` sets the calendar display timezone (e.g. for recurring events or DST transitions)
|
|
257
|
+
- If `--start`/`--end` are UTC (`Z`) and `--tz` is set, the event displays in that timezone
|
|
258
|
+
|
|
259
|
+
### All-day event end date
|
|
260
|
+
|
|
261
|
+
All-day end dates are **exclusive** (Google Calendar convention):
|
|
262
|
+
- One day event on Feb 14: `--start=2026-02-14 --end=2026-02-15`
|
|
263
|
+
- Three day event Feb 14–16: `--start=2026-02-14 --end=2026-02-17`
|
|
264
|
+
|
|
265
|
+
### Recurring events
|
|
266
|
+
|
|
267
|
+
Recurring events are not directly supported for creation/update.
|
|
268
|
+
When listing events, recurring events are expanded into individual instances by default (`singleEvents=true`).
|
|
269
|
+
Each instance has a `recurringEventId` pointing to the parent definition.
|
|
270
|
+
|
|
271
|
+
## Types
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
type EventType = 'default' | 'outOfOffice' | 'workingLocation' | 'focusTime' | 'birthday';
|
|
275
|
+
|
|
276
|
+
interface CalendarEvent {
|
|
277
|
+
id: string;
|
|
278
|
+
summary: string;
|
|
279
|
+
description?: string;
|
|
280
|
+
start: string; // ISO 8601 datetime or date
|
|
281
|
+
end: string;
|
|
282
|
+
timeZone?: string;
|
|
283
|
+
location?: string;
|
|
284
|
+
attendees?: Attendee[];
|
|
285
|
+
status?: 'confirmed' | 'tentative' | 'cancelled';
|
|
286
|
+
htmlLink?: string;
|
|
287
|
+
recurringEventId?: string; // parent recurring event (for instances)
|
|
288
|
+
allDay?: boolean;
|
|
289
|
+
organizer?: { email: string; displayName?: string };
|
|
290
|
+
creator?: { email: string; displayName?: string };
|
|
291
|
+
eventType?: EventType;
|
|
292
|
+
workingLocation?: WorkingLocationProperties;
|
|
293
|
+
outOfOffice?: OutOfOfficeProperties;
|
|
294
|
+
focusTime?: FocusTimeProperties;
|
|
295
|
+
birthday?: BirthdayProperties; // read-only
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
interface Attendee {
|
|
299
|
+
email: string;
|
|
300
|
+
displayName?: string;
|
|
301
|
+
responseStatus?: 'needsAction' | 'declined' | 'tentative' | 'accepted';
|
|
302
|
+
organizer?: boolean;
|
|
303
|
+
self?: boolean;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
interface WorkingLocationProperties {
|
|
307
|
+
type: 'homeOffice' | 'officeLocation' | 'customLocation';
|
|
308
|
+
homeOffice?: true;
|
|
309
|
+
officeLocation?: { buildingId?; deskId?; floorId?; floorSectionId?; label? };
|
|
310
|
+
customLocation?: { label? };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
interface OutOfOfficeProperties {
|
|
314
|
+
autoDeclineMode?: 'declineNone' | 'declineAllConflictingInvitations' | 'declineOnlyNewConflictingInvitations';
|
|
315
|
+
declineMessage?: string;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
interface FocusTimeProperties {
|
|
319
|
+
autoDeclineMode?: 'declineNone' | 'declineAllConflictingInvitations' | 'declineOnlyNewConflictingInvitations';
|
|
320
|
+
chatStatus?: string; // 'available' or 'doNotDisturb'
|
|
321
|
+
declineMessage?: string;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
interface BirthdayProperties { // read-only, cannot be created
|
|
325
|
+
contact?: string;
|
|
326
|
+
type?: 'birthday' | 'anniversary' | 'custom' | 'self';
|
|
327
|
+
customTypeName?: string;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
interface CalendarInfo {
|
|
331
|
+
id: string;
|
|
332
|
+
summary: string;
|
|
333
|
+
description?: string;
|
|
334
|
+
primary?: boolean;
|
|
335
|
+
timeZone?: string;
|
|
336
|
+
backgroundColor?: string;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
interface FreeBusyResult {
|
|
340
|
+
calendarId: string;
|
|
341
|
+
busy: { start: string; end: string }[];
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## Error Codes
|
|
346
|
+
|
|
347
|
+
| Code | Meaning | Exit Code |
|
|
348
|
+
|------|---------|-----------|
|
|
349
|
+
| `AUTH_NO_ACCOUNT` | Account not configured | 1 |
|
|
350
|
+
| `AUTH_MISSING_SCOPE` | Account exists but missing Calendar scope | 1 |
|
|
351
|
+
| `AUTH_TOKEN_REVOKED` | Refresh token revoked — re-auth needed | 1 |
|
|
352
|
+
| `AUTH_NO_CREDENTIALS` | OAuth credentials missing | 1 |
|
|
353
|
+
| `NOT_FOUND` | Event not found (404) | 1 |
|
|
354
|
+
| `QUOTA_EXCEEDED` | Calendar API rate limit (429) — wait 30s and retry | 1 |
|
|
355
|
+
| `SAFETY_BLOCKED` | Destructive op without `--confirm` | 2 |
|
|
356
|
+
| `CALENDAR_ERROR` | Other Calendar API error | 1 |
|
|
357
|
+
|
|
358
|
+
Auth errors include a `fix` field: `{ "error": "AUTH_NO_ACCOUNT", "fix": "npx go-easy auth add <email>" }`
|
|
359
|
+
|
|
360
|
+
## Available Accounts
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
npx go-easy auth list
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
If an account is missing, add it: `npx go-easy auth add <email>` (see [SKILL.md](SKILL.md) for the full auth workflow).
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# go-easy: Drive Reference
|
|
2
|
+
|
|
3
|
+
## Gateway CLI: `npx go-drive`
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
npx go-drive <account> <command> [args...] [--flags]
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
All commands output JSON to stdout. Errors output JSON to stderr with exit code 1.
|
|
10
|
+
Shared Drives are supported transparently.
|
|
11
|
+
|
|
12
|
+
### Commands
|
|
13
|
+
|
|
14
|
+
#### ls
|
|
15
|
+
List files in a folder or by metadata query.
|
|
16
|
+
```bash
|
|
17
|
+
# List root folder (most recently modified first)
|
|
18
|
+
npx go-drive <account> ls
|
|
19
|
+
|
|
20
|
+
# List specific folder
|
|
21
|
+
npx go-drive <account> ls <folderId>
|
|
22
|
+
|
|
23
|
+
# With metadata query
|
|
24
|
+
npx go-drive <account> ls --query="name contains 'report'"
|
|
25
|
+
|
|
26
|
+
# Combine folder + query + pagination
|
|
27
|
+
npx go-drive <account> ls <folderId> --query="mimeType = 'application/pdf'" --max=10
|
|
28
|
+
npx go-drive <account> ls --max=50 --page-token=<token>
|
|
29
|
+
|
|
30
|
+
# Order by name
|
|
31
|
+
npx go-drive <account> ls --order="name"
|
|
32
|
+
```
|
|
33
|
+
Returns: `{ items: DriveFile[], nextPageToken? }`
|
|
34
|
+
|
|
35
|
+
**Defaults:**
|
|
36
|
+
- `--max`: 20 per page
|
|
37
|
+
- `--order`: `modifiedTime desc` (most recently modified first)
|
|
38
|
+
|
|
39
|
+
**Valid `--order` values:** `name`, `modifiedTime`, `modifiedTime desc`, `createdTime`, `createdTime desc`, `folder`, `quotaBytesUsed`, `recency`
|
|
40
|
+
|
|
41
|
+
#### search
|
|
42
|
+
Full-text content search (searches inside file contents).
|
|
43
|
+
```bash
|
|
44
|
+
npx go-drive <account> search "quarterly revenue"
|
|
45
|
+
npx go-drive <account> search "contract clause 5" --max=5
|
|
46
|
+
npx go-drive <account> search "budget" --page-token=<token>
|
|
47
|
+
```
|
|
48
|
+
Returns: `{ items: DriveFile[], nextPageToken? }`
|
|
49
|
+
|
|
50
|
+
- `--max`: no default (returns all matches, Drive API decides page size)
|
|
51
|
+
|
|
52
|
+
**Note**: `search` searches file *contents*. Use `ls --query` to search by filename/metadata.
|
|
53
|
+
|
|
54
|
+
#### get
|
|
55
|
+
Get file metadata by ID.
|
|
56
|
+
```bash
|
|
57
|
+
npx go-drive <account> get <fileId>
|
|
58
|
+
```
|
|
59
|
+
Returns: `DriveFile`
|
|
60
|
+
|
|
61
|
+
#### download
|
|
62
|
+
Download a file (writes to disk).
|
|
63
|
+
```bash
|
|
64
|
+
npx go-drive <account> download <fileId> # saves as original filename in CWD
|
|
65
|
+
npx go-drive <account> download <fileId> ./output.pdf # saves to specific path
|
|
66
|
+
```
|
|
67
|
+
Returns: `{ ok: true, path, size, mimeType }`
|
|
68
|
+
|
|
69
|
+
⚠️ Without a destination path, the file is saved to the **current working directory**. Use `$TEMP` or a specific path for agent workflows:
|
|
70
|
+
```bash
|
|
71
|
+
npx go-drive <account> download <fileId> "$TEMP/downloaded.pdf"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Note**: Cannot download Google Workspace files (Docs/Sheets/Slides) — they have no binary content. Use `export` instead. Attempting to download one throws `DRIVE_EXPORT_REQUIRED`.
|
|
75
|
+
|
|
76
|
+
#### export
|
|
77
|
+
Export Google Workspace files to standard formats.
|
|
78
|
+
```bash
|
|
79
|
+
npx go-drive <account> export <fileId> pdf
|
|
80
|
+
npx go-drive <account> export <fileId> docx ./output.docx
|
|
81
|
+
npx go-drive <account> export <fileId> xlsx
|
|
82
|
+
```
|
|
83
|
+
Returns: `{ ok: true, path, size, mimeType }`
|
|
84
|
+
|
|
85
|
+
⚠️ Without a destination path, the file is saved to the **current working directory**.
|
|
86
|
+
|
|
87
|
+
**Export format matrix:**
|
|
88
|
+
|
|
89
|
+
| Source type | Available formats |
|
|
90
|
+
|-------------|-------------------|
|
|
91
|
+
| Google Docs | `pdf`, `docx`, `txt`, `html` |
|
|
92
|
+
| Google Sheets | `pdf`, `xlsx`, `csv` |
|
|
93
|
+
| Google Slides | `pdf`, `pptx` |
|
|
94
|
+
| Google Drawings | `pdf` |
|
|
95
|
+
|
|
96
|
+
Not all combinations work — the table shows supported ones. Unsupported combos return `DRIVE_ERROR`.
|
|
97
|
+
|
|
98
|
+
#### upload (WRITE)
|
|
99
|
+
Upload a file.
|
|
100
|
+
```bash
|
|
101
|
+
npx go-drive <account> upload ./report.pdf
|
|
102
|
+
npx go-drive <account> upload ./report.pdf --folder=<folderId>
|
|
103
|
+
npx go-drive <account> upload ./report.pdf --name="Q1 Report.pdf"
|
|
104
|
+
```
|
|
105
|
+
Returns: `{ ok: true, id, name, webViewLink? }`
|
|
106
|
+
|
|
107
|
+
#### mkdir (WRITE)
|
|
108
|
+
Create a folder.
|
|
109
|
+
```bash
|
|
110
|
+
npx go-drive <account> mkdir "New Folder"
|
|
111
|
+
npx go-drive <account> mkdir "Subfolder" --parent=<folderId>
|
|
112
|
+
```
|
|
113
|
+
Returns: `{ ok: true, id, name, webViewLink? }`
|
|
114
|
+
|
|
115
|
+
#### move (WRITE)
|
|
116
|
+
Move a file to a different folder.
|
|
117
|
+
```bash
|
|
118
|
+
npx go-drive <account> move <fileId> <newParentId>
|
|
119
|
+
```
|
|
120
|
+
Returns: `{ ok: true, id, name, webViewLink? }`
|
|
121
|
+
|
|
122
|
+
#### rename (WRITE)
|
|
123
|
+
Rename a file.
|
|
124
|
+
```bash
|
|
125
|
+
npx go-drive <account> rename <fileId> "new-name.pdf"
|
|
126
|
+
```
|
|
127
|
+
Returns: `{ ok: true, id, name, webViewLink? }`
|
|
128
|
+
|
|
129
|
+
#### copy (WRITE)
|
|
130
|
+
Copy a file.
|
|
131
|
+
```bash
|
|
132
|
+
npx go-drive <account> copy <fileId>
|
|
133
|
+
npx go-drive <account> copy <fileId> --name="Copy of report" --parent=<folderId>
|
|
134
|
+
```
|
|
135
|
+
Returns: `{ ok: true, id, name, webViewLink? }`
|
|
136
|
+
|
|
137
|
+
Without `--name`, the copy is named "Copy of \<original name\>". Without `--parent`, it stays in the same folder.
|
|
138
|
+
|
|
139
|
+
#### trash ⚠️ DESTRUCTIVE
|
|
140
|
+
Trash a file. Requires `--confirm`.
|
|
141
|
+
```bash
|
|
142
|
+
npx go-drive <account> trash <fileId> --confirm
|
|
143
|
+
```
|
|
144
|
+
Returns: `{ ok: true, id, name }`
|
|
145
|
+
|
|
146
|
+
#### permissions
|
|
147
|
+
List sharing permissions on a file.
|
|
148
|
+
```bash
|
|
149
|
+
npx go-drive <account> permissions <fileId>
|
|
150
|
+
```
|
|
151
|
+
Returns: `Array<{ id, type, role, emailAddress?, displayName? }>` (bare array)
|
|
152
|
+
|
|
153
|
+
#### share ⚠️ DESTRUCTIVE (when type=anyone or type=domain)
|
|
154
|
+
Share a file. Requires `--confirm` when sharing with "anyone" or "domain" (exposes data broadly).
|
|
155
|
+
```bash
|
|
156
|
+
# Share with specific user (WRITE — no --confirm needed)
|
|
157
|
+
npx go-drive <account> share <fileId> --type=user --role=reader --email=other@example.com
|
|
158
|
+
|
|
159
|
+
# Share publicly (DESTRUCTIVE — needs --confirm)
|
|
160
|
+
npx go-drive <account> share <fileId> --type=anyone --role=reader --confirm
|
|
161
|
+
|
|
162
|
+
# Share with domain (DESTRUCTIVE — needs --confirm)
|
|
163
|
+
npx go-drive <account> share <fileId> --type=domain --role=reader --domain=example.com --confirm
|
|
164
|
+
```
|
|
165
|
+
Returns: `{ ok: true, id, name, webViewLink? }`
|
|
166
|
+
|
|
167
|
+
**Roles:** `reader`, `commenter`, `writer`
|
|
168
|
+
|
|
169
|
+
#### unshare ⚠️ DESTRUCTIVE
|
|
170
|
+
Remove a permission. Requires `--confirm`.
|
|
171
|
+
```bash
|
|
172
|
+
npx go-drive <account> unshare <fileId> <permissionId> --confirm
|
|
173
|
+
```
|
|
174
|
+
Returns: `{ ok: true, id, name }`
|
|
175
|
+
|
|
176
|
+
Get the `permissionId` from the `permissions` command.
|
|
177
|
+
|
|
178
|
+
## Library API
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { getAuth } from '@marcfargas/go-easy/auth';
|
|
182
|
+
import { listFiles, searchFiles, getFile, downloadFile, exportFile,
|
|
183
|
+
uploadFile, createFolder, moveFile, renameFile, copyFile,
|
|
184
|
+
trashFile, listPermissions, shareFile, unshareFile
|
|
185
|
+
} from '@marcfargas/go-easy/drive';
|
|
186
|
+
|
|
187
|
+
const auth = await getAuth('drive', '<account>');
|
|
188
|
+
|
|
189
|
+
// List with pagination
|
|
190
|
+
const page1 = await listFiles(auth, { folderId: 'folder-id', maxResults: 50 });
|
|
191
|
+
if (page1.nextPageToken) {
|
|
192
|
+
const page2 = await listFiles(auth, { folderId: 'folder-id', maxResults: 50, pageToken: page1.nextPageToken });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Full-text search
|
|
196
|
+
const results = await searchFiles(auth, { query: 'contract' });
|
|
197
|
+
|
|
198
|
+
// Get metadata
|
|
199
|
+
const file = await getFile(auth, 'file-id');
|
|
200
|
+
|
|
201
|
+
// Download (returns Buffer — library does NOT write to disk)
|
|
202
|
+
const { data, name, mimeType } = await downloadFile(auth, 'file-id');
|
|
203
|
+
// data is a Buffer — write it yourself: fs.writeFileSync('out.pdf', data)
|
|
204
|
+
|
|
205
|
+
// Export (returns Buffer — library does NOT write to disk)
|
|
206
|
+
const exported = await exportFile(auth, 'doc-id', 'pdf');
|
|
207
|
+
// exported.data is a Buffer
|
|
208
|
+
|
|
209
|
+
// Write operations
|
|
210
|
+
const uploaded = await uploadFile(auth, './file.pdf', { folderId: 'folder-id' });
|
|
211
|
+
const folder = await createFolder(auth, 'New Folder', 'parent-id');
|
|
212
|
+
await moveFile(auth, 'file-id', 'new-parent-id');
|
|
213
|
+
await renameFile(auth, 'file-id', 'new-name.pdf');
|
|
214
|
+
await copyFile(auth, 'file-id', 'Copy', 'parent-id');
|
|
215
|
+
|
|
216
|
+
// Destructive (needs safety context)
|
|
217
|
+
await trashFile(auth, 'file-id');
|
|
218
|
+
await shareFile(auth, 'file-id', { type: 'anyone', role: 'reader' });
|
|
219
|
+
await unshareFile(auth, 'file-id', 'permission-id');
|
|
220
|
+
|
|
221
|
+
// Permissions
|
|
222
|
+
const perms = await listPermissions(auth, 'file-id');
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Library vs CLI difference:** `downloadFile` and `exportFile` return `{ data: Buffer, ... }` in the library. The CLI writes to disk and returns `{ path, size }`.
|
|
226
|
+
|
|
227
|
+
## Drive Query Syntax (for ls --query)
|
|
228
|
+
|
|
229
|
+
Same as Drive API v3 query syntax. **Shell quoting**: wrap the whole query in double quotes; use single quotes for string values inside:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
npx go-drive <account> ls --query="name contains 'report'"
|
|
233
|
+
npx go-drive <account> ls --query="mimeType = 'application/pdf' and name contains 'invoice'"
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Common queries:
|
|
237
|
+
- `name = 'report.pdf'` — exact name match
|
|
238
|
+
- `name contains 'report'` — name contains text
|
|
239
|
+
- `mimeType = 'application/pdf'` — by MIME type
|
|
240
|
+
- `modifiedTime > '2026-01-01'` — modified after date
|
|
241
|
+
- `'me' in owners` — owned by me
|
|
242
|
+
- `sharedWithMe` — shared with me
|
|
243
|
+
- `trashed = false` — not in trash (default)
|
|
244
|
+
|
|
245
|
+
Combine with `and`/`or`:
|
|
246
|
+
```
|
|
247
|
+
name contains 'report' and mimeType = 'application/pdf'
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Google Workspace MIME types
|
|
251
|
+
|
|
252
|
+
| Type | MIME type |
|
|
253
|
+
|------|-----------|
|
|
254
|
+
| Folder | `application/vnd.google-apps.folder` |
|
|
255
|
+
| Document | `application/vnd.google-apps.document` |
|
|
256
|
+
| Spreadsheet | `application/vnd.google-apps.spreadsheet` |
|
|
257
|
+
| Presentation | `application/vnd.google-apps.presentation` |
|
|
258
|
+
| Drawing | `application/vnd.google-apps.drawing` |
|
|
259
|
+
| Form | `application/vnd.google-apps.form` |
|
|
260
|
+
|
|
261
|
+
## Types
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
interface DriveFile {
|
|
265
|
+
id: string;
|
|
266
|
+
name: string;
|
|
267
|
+
mimeType: string;
|
|
268
|
+
size?: number; // not present for Google Workspace files
|
|
269
|
+
createdTime?: string;
|
|
270
|
+
modifiedTime?: string;
|
|
271
|
+
parents?: string[];
|
|
272
|
+
webViewLink?: string;
|
|
273
|
+
driveId?: string; // Shared Drive ID (present for Shared Drive files)
|
|
274
|
+
shared?: boolean;
|
|
275
|
+
trashed?: boolean;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
interface DrivePermission {
|
|
279
|
+
id: string;
|
|
280
|
+
type: 'user' | 'group' | 'domain' | 'anyone';
|
|
281
|
+
role: 'owner' | 'organizer' | 'fileOrganizer' | 'writer' | 'commenter' | 'reader';
|
|
282
|
+
emailAddress?: string;
|
|
283
|
+
displayName?: string;
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Error Codes
|
|
288
|
+
|
|
289
|
+
| Code | Meaning | Exit Code |
|
|
290
|
+
|------|---------|-----------|
|
|
291
|
+
| `AUTH_NO_ACCOUNT` | Account not configured | 1 |
|
|
292
|
+
| `AUTH_MISSING_SCOPE` | Account exists but missing Drive scope | 1 |
|
|
293
|
+
| `AUTH_TOKEN_REVOKED` | Refresh token revoked — re-auth needed | 1 |
|
|
294
|
+
| `AUTH_NO_CREDENTIALS` | OAuth credentials missing | 1 |
|
|
295
|
+
| `NOT_FOUND` | File not found (404) | 1 |
|
|
296
|
+
| `QUOTA_EXCEEDED` | Drive API rate limit (429) — wait 30s and retry | 1 |
|
|
297
|
+
| `SAFETY_BLOCKED` | Destructive op without `--confirm` | 2 |
|
|
298
|
+
| `DRIVE_ERROR` | Other Drive API error | 1 |
|
|
299
|
+
| `DRIVE_EXPORT_REQUIRED` | Tried to download a Google Workspace file — use `export` | 1 |
|
|
300
|
+
|
|
301
|
+
Auth errors include a `fix` field: `{ "error": "AUTH_NO_ACCOUNT", "fix": "npx go-easy auth add <email>" }`
|
|
302
|
+
|
|
303
|
+
## Available Accounts
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
npx go-easy auth list
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
If an account is missing, add it: `npx go-easy auth add <email>` (see [SKILL.md](SKILL.md) for the full auth workflow).
|