@digitalpresence/cliclaw 0.1.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.
Files changed (338) hide show
  1. package/dist/__tests__/calendar.integration.test.d.ts +2 -0
  2. package/dist/__tests__/calendar.integration.test.d.ts.map +1 -0
  3. package/dist/__tests__/calendar.integration.test.js +98 -0
  4. package/dist/__tests__/calendar.integration.test.js.map +1 -0
  5. package/dist/__tests__/forms.integration.test.d.ts +2 -0
  6. package/dist/__tests__/forms.integration.test.d.ts.map +1 -0
  7. package/dist/__tests__/forms.integration.test.js +243 -0
  8. package/dist/__tests__/forms.integration.test.js.map +1 -0
  9. package/dist/__tests__/gdrive.integration.test.d.ts +2 -0
  10. package/dist/__tests__/gdrive.integration.test.d.ts.map +1 -0
  11. package/dist/__tests__/gdrive.integration.test.js +186 -0
  12. package/dist/__tests__/gdrive.integration.test.js.map +1 -0
  13. package/dist/__tests__/gmail.integration.test.d.ts +2 -0
  14. package/dist/__tests__/gmail.integration.test.d.ts.map +1 -0
  15. package/dist/__tests__/gmail.integration.test.js +197 -0
  16. package/dist/__tests__/gmail.integration.test.js.map +1 -0
  17. package/dist/__tests__/gslides.integration.test.d.ts +2 -0
  18. package/dist/__tests__/gslides.integration.test.d.ts.map +1 -0
  19. package/dist/__tests__/gslides.integration.test.js +124 -0
  20. package/dist/__tests__/gslides.integration.test.js.map +1 -0
  21. package/dist/__tests__/sheets.integration.test.d.ts +2 -0
  22. package/dist/__tests__/sheets.integration.test.d.ts.map +1 -0
  23. package/dist/__tests__/sheets.integration.test.js +150 -0
  24. package/dist/__tests__/sheets.integration.test.js.map +1 -0
  25. package/dist/agent/crud.d.ts +6 -0
  26. package/dist/agent/crud.d.ts.map +1 -0
  27. package/dist/agent/crud.js +41 -0
  28. package/dist/agent/crud.js.map +1 -0
  29. package/dist/agent/memory.d.ts +14 -0
  30. package/dist/agent/memory.d.ts.map +1 -0
  31. package/dist/agent/memory.js +66 -0
  32. package/dist/agent/memory.js.map +1 -0
  33. package/dist/agent/permissions.d.ts +4 -0
  34. package/dist/agent/permissions.d.ts.map +1 -0
  35. package/dist/agent/permissions.js +32 -0
  36. package/dist/agent/permissions.js.map +1 -0
  37. package/dist/calendar/accounts.d.ts +3 -0
  38. package/dist/calendar/accounts.d.ts.map +1 -0
  39. package/dist/calendar/accounts.js +21 -0
  40. package/dist/calendar/accounts.js.map +1 -0
  41. package/dist/calendar/auth.d.ts +3 -0
  42. package/dist/calendar/auth.d.ts.map +1 -0
  43. package/dist/calendar/auth.js +41 -0
  44. package/dist/calendar/auth.js.map +1 -0
  45. package/dist/calendar/calendars.d.ts +3 -0
  46. package/dist/calendar/calendars.d.ts.map +1 -0
  47. package/dist/calendar/calendars.js +28 -0
  48. package/dist/calendar/calendars.js.map +1 -0
  49. package/dist/calendar/create.d.ts +3 -0
  50. package/dist/calendar/create.d.ts.map +1 -0
  51. package/dist/calendar/create.js +31 -0
  52. package/dist/calendar/create.js.map +1 -0
  53. package/dist/calendar/delete.d.ts +3 -0
  54. package/dist/calendar/delete.d.ts.map +1 -0
  55. package/dist/calendar/delete.js +21 -0
  56. package/dist/calendar/delete.js.map +1 -0
  57. package/dist/calendar/events.d.ts +3 -0
  58. package/dist/calendar/events.d.ts.map +1 -0
  59. package/dist/calendar/events.js +37 -0
  60. package/dist/calendar/events.js.map +1 -0
  61. package/dist/calendar/get.d.ts +3 -0
  62. package/dist/calendar/get.d.ts.map +1 -0
  63. package/dist/calendar/get.js +21 -0
  64. package/dist/calendar/get.js.map +1 -0
  65. package/dist/calendar/update.d.ts +3 -0
  66. package/dist/calendar/update.d.ts.map +1 -0
  67. package/dist/calendar/update.js +31 -0
  68. package/dist/calendar/update.js.map +1 -0
  69. package/dist/cli.d.ts +3 -0
  70. package/dist/cli.d.ts.map +1 -0
  71. package/dist/cli.js +37 -0
  72. package/dist/cli.js.map +1 -0
  73. package/dist/commands/agent.d.ts +6 -0
  74. package/dist/commands/agent.d.ts.map +1 -0
  75. package/dist/commands/agent.js +107 -0
  76. package/dist/commands/agent.js.map +1 -0
  77. package/dist/commands/calendar.d.ts +9 -0
  78. package/dist/commands/calendar.d.ts.map +1 -0
  79. package/dist/commands/calendar.js +104 -0
  80. package/dist/commands/calendar.js.map +1 -0
  81. package/dist/commands/cron.d.ts +6 -0
  82. package/dist/commands/cron.d.ts.map +1 -0
  83. package/dist/commands/cron.js +103 -0
  84. package/dist/commands/cron.js.map +1 -0
  85. package/dist/commands/forms.d.ts +9 -0
  86. package/dist/commands/forms.d.ts.map +1 -0
  87. package/dist/commands/forms.js +139 -0
  88. package/dist/commands/forms.js.map +1 -0
  89. package/dist/commands/gdrive.d.ts +9 -0
  90. package/dist/commands/gdrive.d.ts.map +1 -0
  91. package/dist/commands/gdrive.js +198 -0
  92. package/dist/commands/gdrive.js.map +1 -0
  93. package/dist/commands/gmail.d.ts +9 -0
  94. package/dist/commands/gmail.d.ts.map +1 -0
  95. package/dist/commands/gmail.js +231 -0
  96. package/dist/commands/gmail.js.map +1 -0
  97. package/dist/commands/gslides.d.ts +9 -0
  98. package/dist/commands/gslides.d.ts.map +1 -0
  99. package/dist/commands/gslides.js +167 -0
  100. package/dist/commands/gslides.js.map +1 -0
  101. package/dist/commands/sheets.d.ts +9 -0
  102. package/dist/commands/sheets.d.ts.map +1 -0
  103. package/dist/commands/sheets.js +174 -0
  104. package/dist/commands/sheets.js.map +1 -0
  105. package/dist/cron/daemon.d.ts +3 -0
  106. package/dist/cron/daemon.d.ts.map +1 -0
  107. package/dist/cron/daemon.js +127 -0
  108. package/dist/cron/daemon.js.map +1 -0
  109. package/dist/cron/handlers.d.ts +6 -0
  110. package/dist/cron/handlers.d.ts.map +1 -0
  111. package/dist/cron/handlers.js +58 -0
  112. package/dist/cron/handlers.js.map +1 -0
  113. package/dist/cron/logger.d.ts +4 -0
  114. package/dist/cron/logger.d.ts.map +1 -0
  115. package/dist/cron/logger.js +11 -0
  116. package/dist/cron/logger.js.map +1 -0
  117. package/dist/cron/progress.d.ts +25 -0
  118. package/dist/cron/progress.d.ts.map +1 -0
  119. package/dist/cron/progress.js +71 -0
  120. package/dist/cron/progress.js.map +1 -0
  121. package/dist/cron/ralph-wiggum.d.ts +18 -0
  122. package/dist/cron/ralph-wiggum.d.ts.map +1 -0
  123. package/dist/cron/ralph-wiggum.js +143 -0
  124. package/dist/cron/ralph-wiggum.js.map +1 -0
  125. package/dist/forms/accounts.d.ts +3 -0
  126. package/dist/forms/accounts.d.ts.map +1 -0
  127. package/dist/forms/accounts.js +20 -0
  128. package/dist/forms/accounts.js.map +1 -0
  129. package/dist/forms/auth.d.ts +3 -0
  130. package/dist/forms/auth.d.ts.map +1 -0
  131. package/dist/forms/auth.js +41 -0
  132. package/dist/forms/auth.js.map +1 -0
  133. package/dist/forms/create.d.ts +3 -0
  134. package/dist/forms/create.d.ts.map +1 -0
  135. package/dist/forms/create.js +28 -0
  136. package/dist/forms/create.js.map +1 -0
  137. package/dist/forms/get.d.ts +3 -0
  138. package/dist/forms/get.d.ts.map +1 -0
  139. package/dist/forms/get.js +21 -0
  140. package/dist/forms/get.js.map +1 -0
  141. package/dist/forms/list.d.ts +3 -0
  142. package/dist/forms/list.d.ts.map +1 -0
  143. package/dist/forms/list.js +33 -0
  144. package/dist/forms/list.js.map +1 -0
  145. package/dist/forms/questions.d.ts +6 -0
  146. package/dist/forms/questions.d.ts.map +1 -0
  147. package/dist/forms/questions.js +179 -0
  148. package/dist/forms/questions.js.map +1 -0
  149. package/dist/forms/responses.d.ts +4 -0
  150. package/dist/forms/responses.d.ts.map +1 -0
  151. package/dist/forms/responses.js +40 -0
  152. package/dist/forms/responses.js.map +1 -0
  153. package/dist/forms/update.d.ts +3 -0
  154. package/dist/forms/update.d.ts.map +1 -0
  155. package/dist/forms/update.js +44 -0
  156. package/dist/forms/update.js.map +1 -0
  157. package/dist/gdrive/about.d.ts +3 -0
  158. package/dist/gdrive/about.d.ts.map +1 -0
  159. package/dist/gdrive/about.js +31 -0
  160. package/dist/gdrive/about.js.map +1 -0
  161. package/dist/gdrive/accounts.d.ts +3 -0
  162. package/dist/gdrive/accounts.d.ts.map +1 -0
  163. package/dist/gdrive/accounts.js +20 -0
  164. package/dist/gdrive/accounts.js.map +1 -0
  165. package/dist/gdrive/auth.d.ts +3 -0
  166. package/dist/gdrive/auth.d.ts.map +1 -0
  167. package/dist/gdrive/auth.js +51 -0
  168. package/dist/gdrive/auth.js.map +1 -0
  169. package/dist/gdrive/files.d.ts +12 -0
  170. package/dist/gdrive/files.d.ts.map +1 -0
  171. package/dist/gdrive/files.js +174 -0
  172. package/dist/gdrive/files.js.map +1 -0
  173. package/dist/gdrive/folders.d.ts +4 -0
  174. package/dist/gdrive/folders.d.ts.map +1 -0
  175. package/dist/gdrive/folders.js +46 -0
  176. package/dist/gdrive/folders.js.map +1 -0
  177. package/dist/gdrive/search.d.ts +3 -0
  178. package/dist/gdrive/search.d.ts.map +1 -0
  179. package/dist/gdrive/search.js +23 -0
  180. package/dist/gdrive/search.js.map +1 -0
  181. package/dist/gdrive/sharing.d.ts +5 -0
  182. package/dist/gdrive/sharing.d.ts.map +1 -0
  183. package/dist/gdrive/sharing.js +54 -0
  184. package/dist/gdrive/sharing.js.map +1 -0
  185. package/dist/gmail/accounts.d.ts +3 -0
  186. package/dist/gmail/accounts.d.ts.map +1 -0
  187. package/dist/gmail/accounts.js +18 -0
  188. package/dist/gmail/accounts.js.map +1 -0
  189. package/dist/gmail/auth.d.ts +3 -0
  190. package/dist/gmail/auth.d.ts.map +1 -0
  191. package/dist/gmail/auth.js +50 -0
  192. package/dist/gmail/auth.js.map +1 -0
  193. package/dist/gmail/drafts.d.ts +7 -0
  194. package/dist/gmail/drafts.d.ts.map +1 -0
  195. package/dist/gmail/drafts.js +103 -0
  196. package/dist/gmail/drafts.js.map +1 -0
  197. package/dist/gmail/get.d.ts +4 -0
  198. package/dist/gmail/get.d.ts.map +1 -0
  199. package/dist/gmail/get.js +140 -0
  200. package/dist/gmail/get.js.map +1 -0
  201. package/dist/gmail/inbox.d.ts +4 -0
  202. package/dist/gmail/inbox.d.ts.map +1 -0
  203. package/dist/gmail/inbox.js +45 -0
  204. package/dist/gmail/inbox.js.map +1 -0
  205. package/dist/gmail/labels.d.ts +5 -0
  206. package/dist/gmail/labels.d.ts.map +1 -0
  207. package/dist/gmail/labels.js +45 -0
  208. package/dist/gmail/labels.js.map +1 -0
  209. package/dist/gmail/modify.d.ts +5 -0
  210. package/dist/gmail/modify.d.ts.map +1 -0
  211. package/dist/gmail/modify.js +68 -0
  212. package/dist/gmail/modify.js.map +1 -0
  213. package/dist/gmail/send.d.ts +5 -0
  214. package/dist/gmail/send.d.ts.map +1 -0
  215. package/dist/gmail/send.js +310 -0
  216. package/dist/gmail/send.js.map +1 -0
  217. package/dist/gmail/threads.d.ts +4 -0
  218. package/dist/gmail/threads.d.ts.map +1 -0
  219. package/dist/gmail/threads.js +47 -0
  220. package/dist/gmail/threads.js.map +1 -0
  221. package/dist/gslides/accounts.d.ts +3 -0
  222. package/dist/gslides/accounts.d.ts.map +1 -0
  223. package/dist/gslides/accounts.js +20 -0
  224. package/dist/gslides/accounts.js.map +1 -0
  225. package/dist/gslides/auth.d.ts +3 -0
  226. package/dist/gslides/auth.d.ts.map +1 -0
  227. package/dist/gslides/auth.js +50 -0
  228. package/dist/gslides/auth.js.map +1 -0
  229. package/dist/gslides/presentations.d.ts +29 -0
  230. package/dist/gslides/presentations.d.ts.map +1 -0
  231. package/dist/gslides/presentations.js +320 -0
  232. package/dist/gslides/presentations.js.map +1 -0
  233. package/dist/lib/config.d.ts +6 -0
  234. package/dist/lib/config.d.ts.map +1 -0
  235. package/dist/lib/config.js +12 -0
  236. package/dist/lib/config.js.map +1 -0
  237. package/dist/lib/media-utils.d.ts +5 -0
  238. package/dist/lib/media-utils.d.ts.map +1 -0
  239. package/dist/lib/media-utils.js +79 -0
  240. package/dist/lib/media-utils.js.map +1 -0
  241. package/dist/lib/output.d.ts +4 -0
  242. package/dist/lib/output.d.ts.map +1 -0
  243. package/dist/lib/output.js +12 -0
  244. package/dist/lib/output.js.map +1 -0
  245. package/dist/sheets/accounts.d.ts +3 -0
  246. package/dist/sheets/accounts.d.ts.map +1 -0
  247. package/dist/sheets/accounts.js +20 -0
  248. package/dist/sheets/accounts.js.map +1 -0
  249. package/dist/sheets/auth.d.ts +3 -0
  250. package/dist/sheets/auth.d.ts.map +1 -0
  251. package/dist/sheets/auth.js +56 -0
  252. package/dist/sheets/auth.js.map +1 -0
  253. package/dist/sheets/cells.d.ts +6 -0
  254. package/dist/sheets/cells.d.ts.map +1 -0
  255. package/dist/sheets/cells.js +89 -0
  256. package/dist/sheets/cells.js.map +1 -0
  257. package/dist/sheets/format.d.ts +8 -0
  258. package/dist/sheets/format.d.ts.map +1 -0
  259. package/dist/sheets/format.js +97 -0
  260. package/dist/sheets/format.js.map +1 -0
  261. package/dist/sheets/sheets-tab.d.ts +6 -0
  262. package/dist/sheets/sheets-tab.d.ts.map +1 -0
  263. package/dist/sheets/sheets-tab.js +88 -0
  264. package/dist/sheets/sheets-tab.js.map +1 -0
  265. package/dist/sheets/spreadsheets.d.ts +6 -0
  266. package/dist/sheets/spreadsheets.d.ts.map +1 -0
  267. package/dist/sheets/spreadsheets.js +88 -0
  268. package/dist/sheets/spreadsheets.js.map +1 -0
  269. package/package.json +33 -0
  270. package/src/__tests__/calendar.integration.test.ts +152 -0
  271. package/src/__tests__/forms.integration.test.ts +403 -0
  272. package/src/__tests__/gdrive.integration.test.ts +253 -0
  273. package/src/__tests__/gmail.integration.test.ts +294 -0
  274. package/src/__tests__/gslides.integration.test.ts +195 -0
  275. package/src/__tests__/sheets.integration.test.ts +234 -0
  276. package/src/agent/crud.ts +54 -0
  277. package/src/agent/memory.ts +95 -0
  278. package/src/agent/permissions.ts +54 -0
  279. package/src/calendar/accounts.ts +25 -0
  280. package/src/calendar/auth.ts +45 -0
  281. package/src/calendar/calendars.ts +32 -0
  282. package/src/calendar/create.ts +44 -0
  283. package/src/calendar/delete.ts +27 -0
  284. package/src/calendar/events.ts +45 -0
  285. package/src/calendar/get.ts +27 -0
  286. package/src/calendar/update.ts +44 -0
  287. package/src/cli.ts +41 -0
  288. package/src/commands/agent.ts +128 -0
  289. package/src/commands/calendar.ts +127 -0
  290. package/src/commands/cron.ts +126 -0
  291. package/src/commands/forms.ts +158 -0
  292. package/src/commands/gdrive.ts +225 -0
  293. package/src/commands/gmail.ts +290 -0
  294. package/src/commands/gslides.ts +188 -0
  295. package/src/commands/sheets.ts +193 -0
  296. package/src/cron/daemon.ts +143 -0
  297. package/src/cron/handlers.ts +78 -0
  298. package/src/cron/logger.ts +20 -0
  299. package/src/cron/progress.ts +90 -0
  300. package/src/cron/ralph-wiggum.ts +172 -0
  301. package/src/forms/accounts.ts +24 -0
  302. package/src/forms/auth.ts +45 -0
  303. package/src/forms/create.ts +34 -0
  304. package/src/forms/get.ts +26 -0
  305. package/src/forms/list.ts +38 -0
  306. package/src/forms/questions.ts +209 -0
  307. package/src/forms/responses.ts +50 -0
  308. package/src/forms/update.ts +57 -0
  309. package/src/gdrive/about.ts +36 -0
  310. package/src/gdrive/accounts.ts +24 -0
  311. package/src/gdrive/auth.ts +55 -0
  312. package/src/gdrive/files.ts +237 -0
  313. package/src/gdrive/folders.ts +58 -0
  314. package/src/gdrive/search.ts +30 -0
  315. package/src/gdrive/sharing.ts +72 -0
  316. package/src/gmail/accounts.ts +22 -0
  317. package/src/gmail/auth.ts +53 -0
  318. package/src/gmail/drafts.ts +166 -0
  319. package/src/gmail/get.ts +195 -0
  320. package/src/gmail/inbox.ts +78 -0
  321. package/src/gmail/labels.ts +69 -0
  322. package/src/gmail/modify.ts +89 -0
  323. package/src/gmail/send.ts +424 -0
  324. package/src/gmail/threads.ts +68 -0
  325. package/src/gslides/accounts.ts +24 -0
  326. package/src/gslides/auth.ts +54 -0
  327. package/src/gslides/presentations.ts +384 -0
  328. package/src/lib/config.ts +14 -0
  329. package/src/lib/media-utils.ts +82 -0
  330. package/src/lib/output.ts +13 -0
  331. package/src/sheets/accounts.ts +24 -0
  332. package/src/sheets/auth.ts +60 -0
  333. package/src/sheets/cells.ts +112 -0
  334. package/src/sheets/format.ts +114 -0
  335. package/src/sheets/sheets-tab.ts +109 -0
  336. package/src/sheets/spreadsheets.ts +106 -0
  337. package/tsconfig.json +8 -0
  338. package/vitest.config.ts +7 -0
@@ -0,0 +1,57 @@
1
+ import { google } from "googleapis";
2
+ import type { OAuthClientManager } from "@digitalpresence/cliclaw-auth";
3
+ import { outputJson, outputError, outputAuthRequired } from "../lib/output.js";
4
+
5
+ function getForms(clientManager: OAuthClientManager, tokenKey: string) {
6
+ const client = clientManager.getClient(tokenKey);
7
+ if (!client.credentials?.access_token && !client.credentials?.refresh_token) {
8
+ outputAuthRequired("forms");
9
+ }
10
+ return google.forms({ version: "v1", auth: client });
11
+ }
12
+
13
+ export async function handleUpdateForm(
14
+ clientManager: OAuthClientManager,
15
+ account: string,
16
+ formId: string,
17
+ title?: string,
18
+ description?: string,
19
+ ): Promise<void> {
20
+ const tokenKey = `forms:${account}`;
21
+ try {
22
+ const forms = getForms(clientManager, tokenKey);
23
+
24
+ const requests: Array<Record<string, unknown>> = [];
25
+
26
+ if (title) {
27
+ requests.push({
28
+ updateFormInfo: {
29
+ info: { title },
30
+ updateMask: "title",
31
+ },
32
+ });
33
+ }
34
+
35
+ if (description) {
36
+ requests.push({
37
+ updateFormInfo: {
38
+ info: { description },
39
+ updateMask: "description",
40
+ },
41
+ });
42
+ }
43
+
44
+ if (requests.length === 0) {
45
+ outputError("update_form_failed", "No fields to update. Provide --title or --description.");
46
+ }
47
+
48
+ const res = await forms.forms.batchUpdate({
49
+ formId,
50
+ requestBody: { requests },
51
+ });
52
+
53
+ outputJson({ success: true, ...res.data });
54
+ } catch (err) {
55
+ outputError("update_form_failed", err instanceof Error ? err.message : String(err));
56
+ }
57
+ }
@@ -0,0 +1,36 @@
1
+ import { google } from "googleapis";
2
+ import type { OAuthClientManager } from "@digitalpresence/cliclaw-auth";
3
+ import { outputJson, outputError, outputAuthRequired } from "../lib/output.js";
4
+
5
+ export async function handleAbout(
6
+ clientManager: OAuthClientManager,
7
+ account: string,
8
+ ): Promise<void> {
9
+ const tokenKey = `gdrive:${account}`;
10
+ try {
11
+ const client = clientManager.getClient(tokenKey);
12
+ if (!client.credentials?.access_token && !client.credentials?.refresh_token) {
13
+ outputAuthRequired("gdrive");
14
+ }
15
+ const drive = google.drive({ version: "v3", auth: client });
16
+
17
+ const res = await drive.about.get({
18
+ fields: "user, storageQuota",
19
+ });
20
+
21
+ const quota = res.data.storageQuota;
22
+ outputJson({
23
+ user: res.data.user,
24
+ storageQuota: quota
25
+ ? {
26
+ limit: quota.limit ? `${(Number(quota.limit) / 1e9).toFixed(2)} GB` : "unlimited",
27
+ usage: `${(Number(quota.usage ?? 0) / 1e9).toFixed(2)} GB`,
28
+ usageInDrive: `${(Number(quota.usageInDrive ?? 0) / 1e9).toFixed(2)} GB`,
29
+ usageInDriveTrash: `${(Number(quota.usageInDriveTrash ?? 0) / 1e9).toFixed(2)} GB`,
30
+ }
31
+ : null,
32
+ });
33
+ } catch (err) {
34
+ outputError("about_failed", err instanceof Error ? err.message : String(err));
35
+ }
36
+ }
@@ -0,0 +1,24 @@
1
+ import { google } from "googleapis";
2
+ import type { OAuthClientManager } from "@digitalpresence/cliclaw-auth";
3
+ import { outputJson } from "../lib/output.js";
4
+
5
+ export async function handleAccounts(clientManager: OAuthClientManager): Promise<void> {
6
+ const allAccounts = clientManager.listAccounts();
7
+ const gdriveAccounts = allAccounts.filter((a) => a.startsWith("gdrive:"));
8
+
9
+ const accounts = await Promise.all(
10
+ gdriveAccounts.map(async (tokenKey) => {
11
+ const account = tokenKey.replace("gdrive:", "");
12
+ try {
13
+ const client = clientManager.getClient(tokenKey);
14
+ const drive = google.drive({ version: "v3", auth: client });
15
+ const about = await drive.about.get({ fields: "user" });
16
+ return { account, email: about.data.user?.emailAddress ?? null };
17
+ } catch {
18
+ return { account, email: null };
19
+ }
20
+ }),
21
+ );
22
+
23
+ outputJson({ accounts });
24
+ }
@@ -0,0 +1,55 @@
1
+ import { google } from "googleapis";
2
+ import type { OAuthClientManager } from "@digitalpresence/cliclaw-auth";
3
+ import { waitForOAuthCallback, getGDriveAuthUrl } from "@digitalpresence/cliclaw-auth";
4
+ import { outputJson, outputError } from "../lib/output.js";
5
+
6
+ export async function handleAuth(clientManager: OAuthClientManager, port: number, account: string): Promise<void> {
7
+ const tokenKey = `gdrive:${account}`;
8
+
9
+ // Check if already authenticated
10
+ try {
11
+ const client = clientManager.getClient(tokenKey);
12
+ const creds = client.credentials;
13
+ if (creds && (creds.refresh_token || creds.access_token)) {
14
+ const drive = google.drive({ version: "v3", auth: client });
15
+ const about = await drive.about.get({ fields: "user" });
16
+ outputJson({
17
+ status: "already_authenticated",
18
+ account,
19
+ email: about.data.user?.emailAddress ?? "unknown",
20
+ message: "Existing session is still valid. No re-authentication needed.",
21
+ });
22
+ return;
23
+ }
24
+ } catch {
25
+ // Tokens invalid — proceed with re-auth
26
+ }
27
+
28
+ try {
29
+ const open = (await import("open")).default;
30
+ const rawClient = clientManager.getRawClient();
31
+
32
+ const tokens = await waitForOAuthCallback(rawClient, port, (url) => {
33
+ console.error(`Opening browser for Google Drive authentication...`);
34
+ console.error(url);
35
+ open(url).catch(() => {
36
+ console.error("Could not open browser. Please visit the URL above manually.");
37
+ });
38
+ }, getGDriveAuthUrl);
39
+
40
+ const client = clientManager.setCredentials(tokenKey, tokens);
41
+
42
+ const drive = google.drive({ version: "v3", auth: client });
43
+ let email = "unknown";
44
+ try {
45
+ const about = await drive.about.get({ fields: "user" });
46
+ email = about.data.user?.emailAddress ?? "unknown";
47
+ } catch {
48
+ // Non-fatal
49
+ }
50
+
51
+ outputJson({ status: "authenticated", account, email });
52
+ } catch (err) {
53
+ outputError("auth_failed", err instanceof Error ? err.message : String(err));
54
+ }
55
+ }
@@ -0,0 +1,237 @@
1
+ import { google } from "googleapis";
2
+ import { createReadStream, createWriteStream, statSync } from "fs";
3
+ import { basename, join } from "path";
4
+ import { lookup } from "mime-types";
5
+ import type { OAuthClientManager } from "@digitalpresence/cliclaw-auth";
6
+ import { outputJson, outputError, outputAuthRequired } from "../lib/output.js";
7
+
8
+ function getDrive(clientManager: OAuthClientManager, tokenKey: string) {
9
+ const client = clientManager.getClient(tokenKey);
10
+ if (!client.credentials?.access_token && !client.credentials?.refresh_token) {
11
+ outputAuthRequired("gdrive");
12
+ }
13
+ return google.drive({ version: "v3", auth: client });
14
+ }
15
+
16
+ export async function handleList(
17
+ clientManager: OAuthClientManager,
18
+ account: string,
19
+ maxResults: number,
20
+ query?: string,
21
+ ): Promise<void> {
22
+ const tokenKey = `gdrive:${account}`;
23
+ try {
24
+ const drive = getDrive(clientManager, tokenKey);
25
+ const q = query ? query : "trashed = false";
26
+ const res = await drive.files.list({
27
+ q,
28
+ pageSize: maxResults,
29
+ fields: "files(id, name, mimeType, size, modifiedTime, parents, webViewLink)",
30
+ orderBy: "modifiedTime desc",
31
+ });
32
+ outputJson(res.data.files ?? []);
33
+ } catch (err) {
34
+ outputError("list_failed", err instanceof Error ? err.message : String(err));
35
+ }
36
+ }
37
+
38
+ export async function handleGet(
39
+ clientManager: OAuthClientManager,
40
+ account: string,
41
+ fileId: string,
42
+ ): Promise<void> {
43
+ const tokenKey = `gdrive:${account}`;
44
+ try {
45
+ const drive = getDrive(clientManager, tokenKey);
46
+ const res = await drive.files.get({
47
+ fileId,
48
+ fields: "id, name, mimeType, size, modifiedTime, createdTime, parents, webViewLink, description, starred, trashed, owners, permissions",
49
+ });
50
+ outputJson(res.data);
51
+ } catch (err) {
52
+ outputError("get_failed", err instanceof Error ? err.message : String(err));
53
+ }
54
+ }
55
+
56
+ export async function handleDownload(
57
+ clientManager: OAuthClientManager,
58
+ account: string,
59
+ fileId: string,
60
+ saveDir: string,
61
+ filename?: string,
62
+ ): Promise<void> {
63
+ const tokenKey = `gdrive:${account}`;
64
+ try {
65
+ const drive = getDrive(clientManager, tokenKey);
66
+
67
+ // Get file metadata for name
68
+ const meta = await drive.files.get({ fileId, fields: "name, mimeType" });
69
+ const saveName = filename ?? meta.data.name ?? fileId;
70
+ const savePath = join(saveDir, saveName);
71
+
72
+ const res = await drive.files.get(
73
+ { fileId, alt: "media" },
74
+ { responseType: "stream" },
75
+ );
76
+
77
+ await new Promise<void>((resolve, reject) => {
78
+ const dest = createWriteStream(savePath);
79
+ (res.data as NodeJS.ReadableStream)
80
+ .pipe(dest)
81
+ .on("finish", resolve)
82
+ .on("error", reject);
83
+ });
84
+
85
+ outputJson({ success: true, path: savePath, name: saveName });
86
+ } catch (err) {
87
+ outputError("download_failed", err instanceof Error ? err.message : String(err));
88
+ }
89
+ }
90
+
91
+ export async function handleUpload(
92
+ clientManager: OAuthClientManager,
93
+ account: string,
94
+ filePath: string,
95
+ name?: string,
96
+ parentId?: string,
97
+ ): Promise<void> {
98
+ const tokenKey = `gdrive:${account}`;
99
+ try {
100
+ const drive = getDrive(clientManager, tokenKey);
101
+ const fileName = name ?? basename(filePath);
102
+ const mimeType = lookup(filePath) || "application/octet-stream";
103
+
104
+ const fileMetadata: { name: string; parents?: string[] } = { name: fileName };
105
+ if (parentId) fileMetadata.parents = [parentId];
106
+
107
+ const res = await drive.files.create({
108
+ requestBody: fileMetadata,
109
+ media: {
110
+ mimeType,
111
+ body: createReadStream(filePath),
112
+ },
113
+ fields: "id, name, mimeType, size, webViewLink",
114
+ });
115
+
116
+ outputJson({ success: true, ...res.data });
117
+ } catch (err) {
118
+ outputError("upload_failed", err instanceof Error ? err.message : String(err));
119
+ }
120
+ }
121
+
122
+ export async function handleDelete(
123
+ clientManager: OAuthClientManager,
124
+ account: string,
125
+ fileId: string,
126
+ ): Promise<void> {
127
+ const tokenKey = `gdrive:${account}`;
128
+ try {
129
+ const drive = getDrive(clientManager, tokenKey);
130
+ await drive.files.delete({ fileId });
131
+ outputJson({ success: true, fileId });
132
+ } catch (err) {
133
+ outputError("delete_failed", err instanceof Error ? err.message : String(err));
134
+ }
135
+ }
136
+
137
+ export async function handleTrash(
138
+ clientManager: OAuthClientManager,
139
+ account: string,
140
+ fileId: string,
141
+ ): Promise<void> {
142
+ const tokenKey = `gdrive:${account}`;
143
+ try {
144
+ const drive = getDrive(clientManager, tokenKey);
145
+ await drive.files.update({ fileId, requestBody: { trashed: true } });
146
+ outputJson({ success: true, fileId, action: "trash" });
147
+ } catch (err) {
148
+ outputError("trash_failed", err instanceof Error ? err.message : String(err));
149
+ }
150
+ }
151
+
152
+ export async function handleUntrash(
153
+ clientManager: OAuthClientManager,
154
+ account: string,
155
+ fileId: string,
156
+ ): Promise<void> {
157
+ const tokenKey = `gdrive:${account}`;
158
+ try {
159
+ const drive = getDrive(clientManager, tokenKey);
160
+ await drive.files.update({ fileId, requestBody: { trashed: false } });
161
+ outputJson({ success: true, fileId, action: "untrash" });
162
+ } catch (err) {
163
+ outputError("untrash_failed", err instanceof Error ? err.message : String(err));
164
+ }
165
+ }
166
+
167
+ export async function handleMove(
168
+ clientManager: OAuthClientManager,
169
+ account: string,
170
+ fileId: string,
171
+ newParentId: string,
172
+ ): Promise<void> {
173
+ const tokenKey = `gdrive:${account}`;
174
+ try {
175
+ const drive = getDrive(clientManager, tokenKey);
176
+
177
+ // Get current parents
178
+ const file = await drive.files.get({ fileId, fields: "parents" });
179
+ const previousParents = (file.data.parents ?? []).join(",");
180
+
181
+ const res = await drive.files.update({
182
+ fileId,
183
+ addParents: newParentId,
184
+ removeParents: previousParents,
185
+ fields: "id, name, parents",
186
+ });
187
+
188
+ outputJson({ success: true, ...res.data });
189
+ } catch (err) {
190
+ outputError("move_failed", err instanceof Error ? err.message : String(err));
191
+ }
192
+ }
193
+
194
+ export async function handleCopy(
195
+ clientManager: OAuthClientManager,
196
+ account: string,
197
+ fileId: string,
198
+ name?: string,
199
+ ): Promise<void> {
200
+ const tokenKey = `gdrive:${account}`;
201
+ try {
202
+ const drive = getDrive(clientManager, tokenKey);
203
+ const requestBody: { name?: string } = {};
204
+ if (name) requestBody.name = name;
205
+
206
+ const res = await drive.files.copy({
207
+ fileId,
208
+ requestBody,
209
+ fields: "id, name, mimeType, size, webViewLink",
210
+ });
211
+
212
+ outputJson({ success: true, ...res.data });
213
+ } catch (err) {
214
+ outputError("copy_failed", err instanceof Error ? err.message : String(err));
215
+ }
216
+ }
217
+
218
+ export async function handleRename(
219
+ clientManager: OAuthClientManager,
220
+ account: string,
221
+ fileId: string,
222
+ name: string,
223
+ ): Promise<void> {
224
+ const tokenKey = `gdrive:${account}`;
225
+ try {
226
+ const drive = getDrive(clientManager, tokenKey);
227
+ const res = await drive.files.update({
228
+ fileId,
229
+ requestBody: { name },
230
+ fields: "id, name, mimeType",
231
+ });
232
+
233
+ outputJson({ success: true, ...res.data });
234
+ } catch (err) {
235
+ outputError("rename_failed", err instanceof Error ? err.message : String(err));
236
+ }
237
+ }
@@ -0,0 +1,58 @@
1
+ import { google } from "googleapis";
2
+ import type { OAuthClientManager } from "@digitalpresence/cliclaw-auth";
3
+ import { outputJson, outputError, outputAuthRequired } from "../lib/output.js";
4
+
5
+ function getDrive(clientManager: OAuthClientManager, tokenKey: string) {
6
+ const client = clientManager.getClient(tokenKey);
7
+ if (!client.credentials?.access_token && !client.credentials?.refresh_token) {
8
+ outputAuthRequired("gdrive");
9
+ }
10
+ return google.drive({ version: "v3", auth: client });
11
+ }
12
+
13
+ export async function handleMkdir(
14
+ clientManager: OAuthClientManager,
15
+ account: string,
16
+ name: string,
17
+ parentId?: string,
18
+ ): Promise<void> {
19
+ const tokenKey = `gdrive:${account}`;
20
+ try {
21
+ const drive = getDrive(clientManager, tokenKey);
22
+ const fileMetadata: { name: string; mimeType: string; parents?: string[] } = {
23
+ name,
24
+ mimeType: "application/vnd.google-apps.folder",
25
+ };
26
+ if (parentId) fileMetadata.parents = [parentId];
27
+
28
+ const res = await drive.files.create({
29
+ requestBody: fileMetadata,
30
+ fields: "id, name, mimeType, webViewLink",
31
+ });
32
+
33
+ outputJson({ success: true, ...res.data });
34
+ } catch (err) {
35
+ outputError("mkdir_failed", err instanceof Error ? err.message : String(err));
36
+ }
37
+ }
38
+
39
+ export async function handleListFolder(
40
+ clientManager: OAuthClientManager,
41
+ account: string,
42
+ folderId: string,
43
+ maxResults: number,
44
+ ): Promise<void> {
45
+ const tokenKey = `gdrive:${account}`;
46
+ try {
47
+ const drive = getDrive(clientManager, tokenKey);
48
+ const res = await drive.files.list({
49
+ q: `'${folderId}' in parents and trashed = false`,
50
+ pageSize: maxResults,
51
+ fields: "files(id, name, mimeType, size, modifiedTime, webViewLink)",
52
+ orderBy: "folder,name",
53
+ });
54
+ outputJson(res.data.files ?? []);
55
+ } catch (err) {
56
+ outputError("list_folder_failed", err instanceof Error ? err.message : String(err));
57
+ }
58
+ }
@@ -0,0 +1,30 @@
1
+ import { google } from "googleapis";
2
+ import type { OAuthClientManager } from "@digitalpresence/cliclaw-auth";
3
+ import { outputJson, outputError, outputAuthRequired } from "../lib/output.js";
4
+
5
+ export async function handleSearch(
6
+ clientManager: OAuthClientManager,
7
+ account: string,
8
+ query: string,
9
+ maxResults: number,
10
+ ): Promise<void> {
11
+ const tokenKey = `gdrive:${account}`;
12
+ try {
13
+ const client = clientManager.getClient(tokenKey);
14
+ if (!client.credentials?.access_token && !client.credentials?.refresh_token) {
15
+ outputAuthRequired("gdrive");
16
+ }
17
+ const drive = google.drive({ version: "v3", auth: client });
18
+
19
+ const res = await drive.files.list({
20
+ q: `fullText contains '${query.replace(/'/g, "\\'")}' and trashed = false`,
21
+ pageSize: maxResults,
22
+ fields: "files(id, name, mimeType, size, modifiedTime, parents, webViewLink)",
23
+ orderBy: "modifiedTime desc",
24
+ });
25
+
26
+ outputJson(res.data.files ?? []);
27
+ } catch (err) {
28
+ outputError("search_failed", err instanceof Error ? err.message : String(err));
29
+ }
30
+ }
@@ -0,0 +1,72 @@
1
+ import { google } from "googleapis";
2
+ import type { OAuthClientManager } from "@digitalpresence/cliclaw-auth";
3
+ import { outputJson, outputError, outputAuthRequired } from "../lib/output.js";
4
+
5
+ function getDrive(clientManager: OAuthClientManager, tokenKey: string) {
6
+ const client = clientManager.getClient(tokenKey);
7
+ if (!client.credentials?.access_token && !client.credentials?.refresh_token) {
8
+ outputAuthRequired("gdrive");
9
+ }
10
+ return google.drive({ version: "v3", auth: client });
11
+ }
12
+
13
+ export async function handleShare(
14
+ clientManager: OAuthClientManager,
15
+ account: string,
16
+ fileId: string,
17
+ email: string,
18
+ role: string,
19
+ ): Promise<void> {
20
+ const tokenKey = `gdrive:${account}`;
21
+ try {
22
+ const drive = getDrive(clientManager, tokenKey);
23
+ const res = await drive.permissions.create({
24
+ fileId,
25
+ requestBody: {
26
+ type: "user",
27
+ role,
28
+ emailAddress: email,
29
+ },
30
+ fields: "id, type, role, emailAddress",
31
+ });
32
+
33
+ outputJson({ success: true, fileId, permission: res.data });
34
+ } catch (err) {
35
+ outputError("share_failed", err instanceof Error ? err.message : String(err));
36
+ }
37
+ }
38
+
39
+ export async function handleUnshare(
40
+ clientManager: OAuthClientManager,
41
+ account: string,
42
+ fileId: string,
43
+ permissionId: string,
44
+ ): Promise<void> {
45
+ const tokenKey = `gdrive:${account}`;
46
+ try {
47
+ const drive = getDrive(clientManager, tokenKey);
48
+ await drive.permissions.delete({ fileId, permissionId });
49
+ outputJson({ success: true, fileId, permissionId });
50
+ } catch (err) {
51
+ outputError("unshare_failed", err instanceof Error ? err.message : String(err));
52
+ }
53
+ }
54
+
55
+ export async function handlePermissions(
56
+ clientManager: OAuthClientManager,
57
+ account: string,
58
+ fileId: string,
59
+ ): Promise<void> {
60
+ const tokenKey = `gdrive:${account}`;
61
+ try {
62
+ const drive = getDrive(clientManager, tokenKey);
63
+ const res = await drive.permissions.list({
64
+ fileId,
65
+ fields: "permissions(id, type, role, emailAddress, displayName)",
66
+ });
67
+
68
+ outputJson({ fileId, permissions: res.data.permissions ?? [] });
69
+ } catch (err) {
70
+ outputError("permissions_failed", err instanceof Error ? err.message : String(err));
71
+ }
72
+ }
@@ -0,0 +1,22 @@
1
+ import { google } from "googleapis";
2
+ import type { OAuthClientManager } from "@digitalpresence/cliclaw-auth";
3
+ import { outputJson } from "../lib/output.js";
4
+
5
+ export async function handleAccounts(clientManager: OAuthClientManager): Promise<void> {
6
+ const accountNames = clientManager.listAccounts();
7
+
8
+ const accounts = await Promise.all(
9
+ accountNames.map(async (account) => {
10
+ try {
11
+ const client = clientManager.getClient(account);
12
+ const gmail = google.gmail({ version: "v1", auth: client });
13
+ const res = await gmail.users.getProfile({ userId: "me" });
14
+ return { account, email: res.data.emailAddress ?? null };
15
+ } catch {
16
+ return { account, email: null };
17
+ }
18
+ }),
19
+ );
20
+
21
+ outputJson({ accounts });
22
+ }
@@ -0,0 +1,53 @@
1
+ import { google } from "googleapis";
2
+ import type { OAuthClientManager } from "@digitalpresence/cliclaw-auth";
3
+ import { waitForOAuthCallback } from "@digitalpresence/cliclaw-auth";
4
+ import { outputJson, outputError } from "../lib/output.js";
5
+
6
+ export async function handleAuth(clientManager: OAuthClientManager, port: number, account: string): Promise<void> {
7
+ // Check if already authenticated
8
+ try {
9
+ const client = clientManager.getClient(account);
10
+ const creds = client.credentials;
11
+ if (creds && (creds.refresh_token || creds.access_token)) {
12
+ const oauth2 = google.oauth2({ version: "v2", auth: client });
13
+ const info = await oauth2.userinfo.get();
14
+ outputJson({
15
+ status: "already_authenticated",
16
+ account,
17
+ email: info.data.email ?? "unknown",
18
+ message: "Existing session is still valid. No re-authentication needed.",
19
+ });
20
+ return;
21
+ }
22
+ } catch {
23
+ // Tokens invalid — proceed with re-auth
24
+ }
25
+
26
+ try {
27
+ const open = (await import("open")).default;
28
+ const rawClient = clientManager.getRawClient();
29
+
30
+ const tokens = await waitForOAuthCallback(rawClient, port, (url) => {
31
+ console.error(`Opening browser for authentication...`);
32
+ console.error(url);
33
+ open(url).catch(() => {
34
+ console.error("Could not open browser. Please visit the URL above manually.");
35
+ });
36
+ });
37
+
38
+ const client = clientManager.setCredentials(account, tokens);
39
+
40
+ const oauth2 = google.oauth2({ version: "v2", auth: client });
41
+ let email = "unknown";
42
+ try {
43
+ const info = await oauth2.userinfo.get();
44
+ email = info.data.email ?? "unknown";
45
+ } catch {
46
+ // Non-fatal
47
+ }
48
+
49
+ outputJson({ status: "authenticated", account, email });
50
+ } catch (err) {
51
+ outputError("auth_failed", err instanceof Error ? err.message : String(err));
52
+ }
53
+ }