runline 0.34.0 → 0.35.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/dist/core/engine.js +12 -8
- package/dist/credentials/registry.js +5 -0
- package/dist/credentials/types.d.ts +10 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/plugin/schema.d.ts +16 -4
- package/dist/plugin/schema.js +28 -5
- package/dist/plugin/types.d.ts +8 -0
- package/dist/plugins/_shared/base64.js +30 -0
- package/dist/plugins/_shared/credentialAdapter.js +23 -3
- package/dist/plugins/_shared/credentials.js +29 -2
- package/dist/plugins/_shared/googleAuth.js +62 -1
- package/dist/plugins/_shared/googleCredentials.js +40 -7
- package/dist/plugins/_shared/localFile.js +40 -0
- package/dist/plugins/_shared/mediaFile.js +12 -12
- package/dist/plugins/_shared/microsoftAuth.js +19 -4
- package/dist/plugins/_shared/microsoftCredentials.js +12 -7
- package/dist/plugins/_shared/shiftCloud.js +14 -18
- package/dist/plugins/_shared/shiftUpload.js +14 -10
- package/dist/plugins/agentMail/src/credentials.js +35 -0
- package/dist/plugins/agentMail/src/drafts.js +137 -0
- package/dist/plugins/agentMail/src/inboxes.js +95 -0
- package/dist/plugins/agentMail/src/index.js +35 -0
- package/dist/plugins/agentMail/src/messages.js +154 -0
- package/dist/plugins/agentMail/src/schemas.js +156 -0
- package/dist/plugins/agentMail/src/shared.js +203 -0
- package/dist/plugins/agentMail/src/threads.js +75 -0
- package/dist/plugins/elevenlabs/src/shared.js +29 -43
- package/dist/plugins/fal/src/index.js +1 -1
- package/dist/plugins/gmail/src/index.js +22 -85
- package/dist/plugins/googleAppsScript/src/index.js +2 -1
- package/dist/plugins/googleCalendar/src/index.js +6 -39
- package/dist/plugins/googleContacts/src/index.js +2 -32
- package/dist/plugins/googleDocs/src/index.js +2 -33
- package/dist/plugins/googleDrive/src/index.js +19 -45
- package/dist/plugins/googleImage/src/index.js +1 -1
- package/dist/plugins/googleSheets/src/index.js +2 -34
- package/dist/plugins/googleSlides/src/index.js +2 -32
- package/dist/plugins/googleTasks/src/index.js +2 -32
- package/dist/plugins/highlevel/src/index.js +8 -2
- package/dist/plugins/microsoftCalendar/src/index.js +2 -7
- package/dist/plugins/microsoftFiles/src/index.js +7 -8
- package/dist/plugins/microsoftMail/src/index.js +2 -7
- package/dist/plugins/openai/src/index.js +1 -1
- package/dist/plugins/recraft/src/index.js +1 -1
- package/dist/plugins/replicate/src/index.js +1 -1
- package/dist/plugins/shiftObjects/src/objects.js +1 -1
- package/dist/plugins/shiftOcr/src/index.js +8 -3
- package/dist/plugins/shiftTranscription/src/transcription.js +1 -1
- package/dist/plugins/together/src/index.js +1 -1
- package/dist/plugins/xai/src/index.js +1 -1
- package/package.json +2 -1
- package/dist/tests/helpers/engine-harness.d.ts +0 -1
- package/dist/tests/helpers/engine-harness.js +0 -236
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Gmail plugin for runline.
|
|
3
3
|
*
|
|
4
|
-
* Authentication: OAuth2 user flow, seeded once
|
|
5
|
-
* `runline auth gmail
|
|
6
|
-
* `
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* `https://oauth2.googleapis.com/token` and persists the new
|
|
10
|
-
* token via `ctx.updateConnection`. File-level locking inside
|
|
11
|
-
* runline core keeps concurrent refreshes safe.
|
|
4
|
+
* Authentication: the Google grant's OAuth2 user flow, seeded once
|
|
5
|
+
* via `runline auth gmail`, or a service account. The connection
|
|
6
|
+
* stores `clientId`, `clientSecret` and `refreshToken`; the access
|
|
7
|
+
* token is refreshed lazily (60 s skew) and cached on the connection
|
|
8
|
+
* as the grant's, so every Google plugin sharing it signs with it.
|
|
12
9
|
*
|
|
13
10
|
* This plugin deliberately doesn't depend on nodemailer or
|
|
14
11
|
* mailparser. MIME building is a minimal hand-rolled encoder
|
|
@@ -21,9 +18,10 @@
|
|
|
21
18
|
* or parsed `payload` tree for format=full).
|
|
22
19
|
*/
|
|
23
20
|
import * as t from "typebox";
|
|
21
|
+
import { DownloadedFile, standardBase64 } from "../../_shared/base64.js";
|
|
24
22
|
import { renderEmailJsx } from "../../_shared/emailJsx.js";
|
|
25
23
|
import { pathSegment } from "../../_shared/credentials.js";
|
|
26
|
-
import { googleCredential, googleJsonRequest } from "../../_shared/googleAuth.js";
|
|
24
|
+
import { googleCredential, googleJsonRequest, googleOAuth, } from "../../_shared/googleAuth.js";
|
|
27
25
|
import { GoogleTimestamp, Id, NonEmptyString, stringEnum, } from "../../_shared/googleSchemas.js";
|
|
28
26
|
// ─── Request ─────────────────────────────────────────────────────
|
|
29
27
|
const API_BASE = "https://gmail.googleapis.com/gmail/v1/users/me";
|
|
@@ -87,18 +85,6 @@ function header(name, value) {
|
|
|
87
85
|
function foldedBase64ByteLength(length) {
|
|
88
86
|
return length === 0 ? 0 : length + Math.floor((length - 1) / 76) * CRLF.length;
|
|
89
87
|
}
|
|
90
|
-
function normalizeMimeBase64(value, index) {
|
|
91
|
-
const compact = value.replace(/\s+/g, "");
|
|
92
|
-
if (!/^[A-Za-z0-9+/=_-]*$/.test(compact)) {
|
|
93
|
-
throw new Error(`gmail: attachment ${index} contentBase64 contains invalid base64 characters`);
|
|
94
|
-
}
|
|
95
|
-
const standard = compact.replace(/-/g, "+").replace(/_/g, "/");
|
|
96
|
-
const remainder = standard.length % 4;
|
|
97
|
-
if (remainder === 1) {
|
|
98
|
-
throw new Error(`gmail: attachment ${index} contentBase64 has invalid base64 length`);
|
|
99
|
-
}
|
|
100
|
-
return remainder === 0 ? standard : `${standard}${"=".repeat(4 - remainder)}`;
|
|
101
|
-
}
|
|
102
88
|
function foldBase64(encoded) {
|
|
103
89
|
let folded = "";
|
|
104
90
|
for (let i = 0; i < encoded.length; i += 76) {
|
|
@@ -135,10 +121,20 @@ function normalizeAttachment(input, index) {
|
|
|
135
121
|
if (typeof contentBase64 !== "string") {
|
|
136
122
|
throw new Error(`gmail: attachment ${index} contentBase64 must be a base64 string`);
|
|
137
123
|
}
|
|
124
|
+
const normalized = standardBase64(contentBase64);
|
|
125
|
+
if (normalized === undefined) {
|
|
126
|
+
throw new Error(`gmail: attachment ${index} contentBase64 is not valid base64`);
|
|
127
|
+
}
|
|
128
|
+
// A download result passed as contentBase64 names its own file and type.
|
|
129
|
+
const result = typeof content === "object" && content ? content : {};
|
|
130
|
+
const named = (value) => typeof value === "string" && value ? value : undefined;
|
|
138
131
|
return {
|
|
139
|
-
name: input.name ??
|
|
140
|
-
|
|
141
|
-
|
|
132
|
+
name: input.name ??
|
|
133
|
+
input.filename ??
|
|
134
|
+
named(result.name) ??
|
|
135
|
+
`attachment-${index + 1}`,
|
|
136
|
+
mimeType: input.mimeType ?? named(result.mimeType) ?? "application/octet-stream",
|
|
137
|
+
contentBase64: normalized,
|
|
142
138
|
};
|
|
143
139
|
}
|
|
144
140
|
function attachmentPart(att) {
|
|
@@ -432,9 +428,6 @@ function simplifyMessage(raw, labels) {
|
|
|
432
428
|
* `References` headers plus the original `threadId`.
|
|
433
429
|
*/
|
|
434
430
|
async function replyToMessage(ctx, messageId, p) {
|
|
435
|
-
if (p.replyToSenderOnly && p.replyToRecipientsOnly) {
|
|
436
|
-
throw new Error("gmail: replyToSenderOnly and replyToRecipientsOnly are mutually exclusive");
|
|
437
|
-
}
|
|
438
431
|
// Render before any network round-trip so malformed jsx fails
|
|
439
432
|
// without spending Gmail API calls.
|
|
440
433
|
const { text, html } = await resolveBody(p);
|
|
@@ -515,10 +508,7 @@ const MessageListVisibility = stringEnum(["show", "hide"]);
|
|
|
515
508
|
const MetadataHeaders = t.Array(t.String());
|
|
516
509
|
const LabelIds = t.Array(NonEmptyString, { minItems: 1 });
|
|
517
510
|
const MaxResults = t.Integer({ minimum: 1, maximum: 500 });
|
|
518
|
-
const AttachmentContent = t.Union([
|
|
519
|
-
t.String(),
|
|
520
|
-
t.Object({ contentBase64: t.String() }, strict),
|
|
521
|
-
]);
|
|
511
|
+
const AttachmentContent = t.Union([t.String(), DownloadedFile]);
|
|
522
512
|
const Attachment = t.Object({
|
|
523
513
|
name: t.Optional(t.String()),
|
|
524
514
|
filename: t.Optional(t.String()),
|
|
@@ -582,60 +572,7 @@ export default function gmail(rl) {
|
|
|
582
572
|
rl.setName("gmail");
|
|
583
573
|
rl.setVersion("0.1.0");
|
|
584
574
|
rl.setCredential(googleCredential("gmail", SCOPES));
|
|
585
|
-
rl.setOAuth(
|
|
586
|
-
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
587
|
-
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
588
|
-
scopes: SCOPES,
|
|
589
|
-
// access_type=offline + prompt=consent are both required to
|
|
590
|
-
// make Google return a refresh token. Without them, repeat
|
|
591
|
-
// logins skip the refresh_token field in the response.
|
|
592
|
-
authParams: { access_type: "offline", prompt: "consent" },
|
|
593
|
-
setupHelp: [
|
|
594
|
-
"You need a Google Cloud OAuth client. Takes ~5 minutes, one time.",
|
|
595
|
-
"Do the steps in order — skipping step 4 causes a 403 when you log in.",
|
|
596
|
-
"",
|
|
597
|
-
"1. Create a Google Cloud project (or pick an existing one):",
|
|
598
|
-
" https://console.cloud.google.com/projectcreate",
|
|
599
|
-
" Make sure the new project is selected in the top navigation dropdown.",
|
|
600
|
-
"",
|
|
601
|
-
"2. Enable the Gmail API for that project:",
|
|
602
|
-
" https://console.cloud.google.com/apis/library/gmail.googleapis.com",
|
|
603
|
-
" Click 'Enable'.",
|
|
604
|
-
"",
|
|
605
|
-
"3. Configure the OAuth consent screen (first time only):",
|
|
606
|
-
" https://console.cloud.google.com/apis/credentials/consent",
|
|
607
|
-
" Click 'Get started' and fill in:",
|
|
608
|
-
" • App name: runline (or whatever)",
|
|
609
|
-
" • User support email: your email",
|
|
610
|
-
" • Audience: External",
|
|
611
|
-
" • Developer contact: your email",
|
|
612
|
-
" You can skip the Scopes screen — runline declares scopes at auth time.",
|
|
613
|
-
"",
|
|
614
|
-
"4. Add yourself as a test user (required — External apps in Testing mode",
|
|
615
|
-
" only allow authentication from addresses on this list):",
|
|
616
|
-
" https://console.cloud.google.com/auth/audience",
|
|
617
|
-
" Under 'Test users', click '+ Add users' and enter the Gmail address",
|
|
618
|
-
" you'll use to log in. Consent and refresh tokens expire every 7 days",
|
|
619
|
-
" while the app is in Testing mode.",
|
|
620
|
-
"",
|
|
621
|
-
"5. Create the OAuth client:",
|
|
622
|
-
" https://console.cloud.google.com/apis/credentials",
|
|
623
|
-
" • + Create credentials → OAuth client ID",
|
|
624
|
-
" • Application type: Web application",
|
|
625
|
-
" • Name: runline (or whatever)",
|
|
626
|
-
" • Authorized redirect URIs → + Add URI:",
|
|
627
|
-
" {{redirectUri}}",
|
|
628
|
-
" • Click Create, then copy the Client ID and Client Secret from the",
|
|
629
|
-
" dialog — the secret is only shown once.",
|
|
630
|
-
"",
|
|
631
|
-
"6. Paste them below (or re-run with --client-id / --client-secret,",
|
|
632
|
-
" or export GMAIL_CLIENT_ID and GMAIL_CLIENT_SECRET).",
|
|
633
|
-
"",
|
|
634
|
-
"During login you'll see a 'Google hasn't verified this app' warning.",
|
|
635
|
-
"Click 'Advanced → Go to runline (unsafe)' to continue — this is",
|
|
636
|
-
"expected for apps in Testing mode.",
|
|
637
|
-
],
|
|
638
|
-
});
|
|
575
|
+
rl.setOAuth(googleOAuth("gmail", SCOPES, "GMAIL"));
|
|
639
576
|
rl.setConnectionSchema({
|
|
640
577
|
authMethod: { type: "string", required: false, description: "delegated or serviceAccount (legacy configs infer the method)" },
|
|
641
578
|
clientId: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as t from "typebox";
|
|
2
2
|
import { pathSegment } from "../../_shared/credentials.js";
|
|
3
|
-
import { googleCredential, googleJsonRequest, } from "../../_shared/googleAuth.js";
|
|
3
|
+
import { googleCredential, googleJsonRequest, googleOAuth, } from "../../_shared/googleAuth.js";
|
|
4
4
|
import { Id, NonEmptyString, PositiveInteger, stringEnum, } from "../../_shared/googleSchemas.js";
|
|
5
5
|
const SCRIPT_API = "https://script.googleapis.com/v1";
|
|
6
6
|
const DRIVE_API = "https://www.googleapis.com/drive/v3";
|
|
@@ -42,6 +42,7 @@ export default function googleAppsScript(rl) {
|
|
|
42
42
|
rl.setName("googleAppsScript");
|
|
43
43
|
rl.setVersion("1.0.0");
|
|
44
44
|
rl.setCredential(googleCredential("googleAppsScript", SCOPES));
|
|
45
|
+
rl.setOAuth(googleOAuth("googleAppsScript", SCOPES, "GOOGLE_APPS_SCRIPT"));
|
|
45
46
|
rl.setConnectionSchema({
|
|
46
47
|
authMethod: {
|
|
47
48
|
type: "string",
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Google Calendar plugin for runline.
|
|
3
3
|
*
|
|
4
|
-
* Authentication mirrors the gmail plugin:
|
|
5
|
-
* once via `runline auth googleCalendar
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* when the cached token is missing or within 60 s of expiry the
|
|
9
|
-
* plugin hits `https://oauth2.googleapis.com/token` and persists
|
|
10
|
-
* the new token via `ctx.updateConnection`.
|
|
4
|
+
* Authentication mirrors the gmail plugin: the Google grant's OAuth2
|
|
5
|
+
* user flow, seeded once via `runline auth googleCalendar`, with the
|
|
6
|
+
* access token refreshed lazily (60 s skew) and cached on the
|
|
7
|
+
* connection as the grant's.
|
|
11
8
|
*
|
|
12
9
|
* Surface area:
|
|
13
10
|
*
|
|
@@ -30,7 +27,7 @@
|
|
|
30
27
|
import rrulePkg from "rrule";
|
|
31
28
|
import * as t from "typebox";
|
|
32
29
|
import { pathSegment } from "../../_shared/credentials.js";
|
|
33
|
-
import { googleCredential, googleJsonRequest } from "../../_shared/googleAuth.js";
|
|
30
|
+
import { googleCredential, googleJsonRequest, googleOAuth, } from "../../_shared/googleAuth.js";
|
|
34
31
|
import { Id, NonEmptyString, NonEmptyStringArray, PositiveInteger, StringOrStringArray, stringEnum, } from "../../_shared/googleSchemas.js";
|
|
35
32
|
// `rrule` ships as CJS; named imports fail under Node ESM.
|
|
36
33
|
const { RRule } = rrulePkg;
|
|
@@ -503,37 +500,7 @@ export default function googleCalendar(rl) {
|
|
|
503
500
|
rl.setName("googleCalendar");
|
|
504
501
|
rl.setVersion("0.1.0");
|
|
505
502
|
rl.setCredential(googleCredential("googleCalendar", SCOPES));
|
|
506
|
-
rl.setOAuth(
|
|
507
|
-
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
508
|
-
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
509
|
-
scopes: SCOPES,
|
|
510
|
-
authParams: { access_type: "offline", prompt: "consent" },
|
|
511
|
-
setupHelp: [
|
|
512
|
-
"You need a Google Cloud OAuth client. Takes ~5 minutes, one time.",
|
|
513
|
-
"",
|
|
514
|
-
"1. Create or pick a Google Cloud project:",
|
|
515
|
-
" https://console.cloud.google.com/projectcreate",
|
|
516
|
-
"",
|
|
517
|
-
"2. Enable the Google Calendar API:",
|
|
518
|
-
" https://console.cloud.google.com/apis/library/calendar-json.googleapis.com",
|
|
519
|
-
"",
|
|
520
|
-
"3. Configure the OAuth consent screen (first time only):",
|
|
521
|
-
" https://console.cloud.google.com/apis/credentials/consent",
|
|
522
|
-
" • Audience: External",
|
|
523
|
-
"",
|
|
524
|
-
"4. Add yourself as a test user:",
|
|
525
|
-
" https://console.cloud.google.com/auth/audience",
|
|
526
|
-
"",
|
|
527
|
-
"5. Create the OAuth client:",
|
|
528
|
-
" https://console.cloud.google.com/apis/credentials",
|
|
529
|
-
" • + Create credentials → OAuth client ID",
|
|
530
|
-
" • Application type: Web application",
|
|
531
|
-
" • Authorized redirect URIs → + Add URI: {{redirectUri}}",
|
|
532
|
-
"",
|
|
533
|
-
"6. Paste the Client ID and Client Secret below, or export",
|
|
534
|
-
" GOOGLE_CALENDAR_CLIENT_ID and GOOGLE_CALENDAR_CLIENT_SECRET.",
|
|
535
|
-
],
|
|
536
|
-
});
|
|
503
|
+
rl.setOAuth(googleOAuth("googleCalendar", SCOPES, "GOOGLE_CALENDAR"));
|
|
537
504
|
rl.setConnectionSchema({
|
|
538
505
|
authMethod: { type: "string", required: false, description: "delegated or serviceAccount (legacy configs infer the method)" },
|
|
539
506
|
clientId: {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import * as t from "typebox";
|
|
27
27
|
import { pathSegment } from "../../_shared/credentials.js";
|
|
28
|
-
import { googleCredential, googleJsonRequest } from "../../_shared/googleAuth.js";
|
|
28
|
+
import { googleCredential, googleJsonRequest, googleOAuth, } from "../../_shared/googleAuth.js";
|
|
29
29
|
import { Id, NonEmptyString, PositiveInteger, stringEnum, } from "../../_shared/googleSchemas.js";
|
|
30
30
|
// ─── Fields ─────────────────────────────────────────────────────
|
|
31
31
|
/**
|
|
@@ -375,37 +375,7 @@ export default function googleContacts(rl) {
|
|
|
375
375
|
rl.setName("googleContacts");
|
|
376
376
|
rl.setVersion("0.1.0");
|
|
377
377
|
rl.setCredential(googleCredential("googleContacts", SCOPES));
|
|
378
|
-
rl.setOAuth(
|
|
379
|
-
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
380
|
-
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
381
|
-
scopes: SCOPES,
|
|
382
|
-
authParams: { access_type: "offline", prompt: "consent" },
|
|
383
|
-
setupHelp: [
|
|
384
|
-
"You need a Google Cloud OAuth client. Takes ~5 minutes, one time.",
|
|
385
|
-
"",
|
|
386
|
-
"1. Create or pick a Google Cloud project:",
|
|
387
|
-
" https://console.cloud.google.com/projectcreate",
|
|
388
|
-
"",
|
|
389
|
-
"2. Enable the People API:",
|
|
390
|
-
" https://console.cloud.google.com/apis/library/people.googleapis.com",
|
|
391
|
-
"",
|
|
392
|
-
"3. Configure the OAuth consent screen:",
|
|
393
|
-
" https://console.cloud.google.com/apis/credentials/consent",
|
|
394
|
-
" • Audience: External",
|
|
395
|
-
"",
|
|
396
|
-
"4. Add yourself as a test user:",
|
|
397
|
-
" https://console.cloud.google.com/auth/audience",
|
|
398
|
-
"",
|
|
399
|
-
"5. Create the OAuth client:",
|
|
400
|
-
" https://console.cloud.google.com/apis/credentials",
|
|
401
|
-
" • + Create credentials → OAuth client ID",
|
|
402
|
-
" • Application type: Web application",
|
|
403
|
-
" • Authorized redirect URIs → + Add URI: {{redirectUri}}",
|
|
404
|
-
"",
|
|
405
|
-
"6. Paste the Client ID and Client Secret below, or export",
|
|
406
|
-
" GOOGLE_CONTACTS_CLIENT_ID and GOOGLE_CONTACTS_CLIENT_SECRET.",
|
|
407
|
-
],
|
|
408
|
-
});
|
|
378
|
+
rl.setOAuth(googleOAuth("googleContacts", SCOPES, "GOOGLE_CONTACTS"));
|
|
409
379
|
rl.setConnectionSchema({
|
|
410
380
|
authMethod: { type: "string", required: false, description: "delegated or serviceAccount (legacy configs infer the method)" },
|
|
411
381
|
clientId: { type: "string", required: false, env: "GOOGLE_CONTACTS_CLIENT_ID" },
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* — the Docs API itself only creates blank documents without a
|
|
8
8
|
* target folder.
|
|
9
9
|
*/
|
|
10
|
-
import { googleCredential } from "../../_shared/googleAuth.js";
|
|
10
|
+
import { googleCredential, googleOAuth } from "../../_shared/googleAuth.js";
|
|
11
11
|
import { registerDocumentsActions } from "./documents.js";
|
|
12
12
|
import { registerFormattingActions } from "./formatting.js";
|
|
13
13
|
import { registerImagesActions } from "./images.js";
|
|
@@ -20,38 +20,7 @@ export default function googleDocs(rl) {
|
|
|
20
20
|
rl.setName("googleDocs");
|
|
21
21
|
rl.setVersion("0.1.0");
|
|
22
22
|
rl.setCredential(googleCredential("googleDocs", SCOPES));
|
|
23
|
-
rl.setOAuth(
|
|
24
|
-
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
25
|
-
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
26
|
-
scopes: SCOPES,
|
|
27
|
-
authParams: { access_type: "offline", prompt: "consent" },
|
|
28
|
-
setupHelp: [
|
|
29
|
-
"You need a Google Cloud OAuth client. Takes ~5 minutes, one time.",
|
|
30
|
-
"",
|
|
31
|
-
"1. Create or pick a Google Cloud project:",
|
|
32
|
-
" https://console.cloud.google.com/projectcreate",
|
|
33
|
-
"",
|
|
34
|
-
"2. Enable the Google Docs API (and Drive API for document.create):",
|
|
35
|
-
" https://console.cloud.google.com/apis/library/docs.googleapis.com",
|
|
36
|
-
" https://console.cloud.google.com/apis/library/drive.googleapis.com",
|
|
37
|
-
"",
|
|
38
|
-
"3. Configure the OAuth consent screen:",
|
|
39
|
-
" https://console.cloud.google.com/apis/credentials/consent",
|
|
40
|
-
" • Audience: External",
|
|
41
|
-
"",
|
|
42
|
-
"4. Add yourself as a test user:",
|
|
43
|
-
" https://console.cloud.google.com/auth/audience",
|
|
44
|
-
"",
|
|
45
|
-
"5. Create the OAuth client:",
|
|
46
|
-
" https://console.cloud.google.com/apis/credentials",
|
|
47
|
-
" • + Create credentials → OAuth client ID",
|
|
48
|
-
" • Application type: Web application",
|
|
49
|
-
" • Authorized redirect URIs → + Add URI: {{redirectUri}}",
|
|
50
|
-
"",
|
|
51
|
-
"6. Paste the Client ID and Client Secret below, or export",
|
|
52
|
-
" GOOGLE_DOCS_CLIENT_ID and GOOGLE_DOCS_CLIENT_SECRET.",
|
|
53
|
-
],
|
|
54
|
-
});
|
|
23
|
+
rl.setOAuth(googleOAuth("googleDocs", SCOPES, "GOOGLE_DOCS"));
|
|
55
24
|
rl.setConnectionSchema({
|
|
56
25
|
authMethod: {
|
|
57
26
|
type: "string",
|
|
@@ -53,11 +53,13 @@
|
|
|
53
53
|
* for larger files, driven off whether the caller supplies bytes
|
|
54
54
|
* (Buffer) vs a path (streamed).
|
|
55
55
|
*/
|
|
56
|
-
import { createReadStream,
|
|
56
|
+
import { createReadStream, statSync, writeFileSync } from "node:fs";
|
|
57
57
|
import * as t from "typebox";
|
|
58
|
+
import { standardBase64 } from "../../_shared/base64.js";
|
|
58
59
|
import { jsonAnswer, pathSegment } from "../../_shared/credentials.js";
|
|
59
|
-
import { googleCredential, googleJsonRequest, googleResponse, } from "../../_shared/googleAuth.js";
|
|
60
|
+
import { googleCredential, googleJsonRequest, googleOAuth, googleResponse, } from "../../_shared/googleAuth.js";
|
|
60
61
|
import { Id, NonEmptyString, RawGoogleObject, StringMap, StringOrStringArray, stringEnum, } from "../../_shared/googleSchemas.js";
|
|
62
|
+
import { readLocalFile } from "../../_shared/localFile.js";
|
|
61
63
|
const PermissionRole = stringEnum([
|
|
62
64
|
"owner",
|
|
63
65
|
"organizer",
|
|
@@ -142,9 +144,12 @@ async function paginateAll(ctx, path, key, qs) {
|
|
|
142
144
|
* mimeType. For resumable uploads we keep paths as paths so we can
|
|
143
145
|
* stream instead of buffering 500 MB in memory.
|
|
144
146
|
*/
|
|
145
|
-
function resolveContent(p) {
|
|
147
|
+
async function resolveContent(p) {
|
|
146
148
|
if (typeof p.contentBase64 === "string") {
|
|
147
|
-
const
|
|
149
|
+
const base64 = standardBase64(p.contentBase64);
|
|
150
|
+
if (base64 === undefined)
|
|
151
|
+
throw new Error("googleDrive: contentBase64 is not valid base64");
|
|
152
|
+
const buf = Buffer.from(base64, "base64");
|
|
148
153
|
return {
|
|
149
154
|
buffer: buf,
|
|
150
155
|
size: buf.byteLength,
|
|
@@ -168,9 +173,14 @@ function resolveContent(p) {
|
|
|
168
173
|
const BUFFER_THRESHOLD = 5 * 1024 * 1024;
|
|
169
174
|
const fileName = p.name ?? p.contentPath.split("/").pop();
|
|
170
175
|
if (stat.size <= BUFFER_THRESHOLD) {
|
|
176
|
+
const buffer = await readLocalFile(p.contentPath, {
|
|
177
|
+
subject: "googleDrive: contentPath",
|
|
178
|
+
maxBytes: BUFFER_THRESHOLD,
|
|
179
|
+
oversize: "googleDrive: contentPath grew while reading; retry the upload",
|
|
180
|
+
});
|
|
171
181
|
return {
|
|
172
|
-
buffer
|
|
173
|
-
size:
|
|
182
|
+
buffer,
|
|
183
|
+
size: buffer.byteLength,
|
|
174
184
|
mimeType: p.mimeType,
|
|
175
185
|
fileName,
|
|
176
186
|
};
|
|
@@ -351,37 +361,7 @@ export default function googleDrive(rl) {
|
|
|
351
361
|
rl.setName("googleDrive");
|
|
352
362
|
rl.setVersion("0.1.0");
|
|
353
363
|
rl.setCredential(googleCredential("googleDrive", SCOPES));
|
|
354
|
-
rl.setOAuth(
|
|
355
|
-
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
356
|
-
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
357
|
-
scopes: SCOPES,
|
|
358
|
-
authParams: { access_type: "offline", prompt: "consent" },
|
|
359
|
-
setupHelp: [
|
|
360
|
-
"You need a Google Cloud OAuth client. Takes ~5 minutes, one time.",
|
|
361
|
-
"",
|
|
362
|
-
"1. Create or pick a Google Cloud project:",
|
|
363
|
-
" https://console.cloud.google.com/projectcreate",
|
|
364
|
-
"",
|
|
365
|
-
"2. Enable the Google Drive API:",
|
|
366
|
-
" https://console.cloud.google.com/apis/library/drive.googleapis.com",
|
|
367
|
-
"",
|
|
368
|
-
"3. Configure the OAuth consent screen:",
|
|
369
|
-
" https://console.cloud.google.com/apis/credentials/consent",
|
|
370
|
-
" • Audience: External",
|
|
371
|
-
"",
|
|
372
|
-
"4. Add yourself as a test user:",
|
|
373
|
-
" https://console.cloud.google.com/auth/audience",
|
|
374
|
-
"",
|
|
375
|
-
"5. Create the OAuth client:",
|
|
376
|
-
" https://console.cloud.google.com/apis/credentials",
|
|
377
|
-
" • + Create credentials → OAuth client ID",
|
|
378
|
-
" • Application type: Web application",
|
|
379
|
-
" • Authorized redirect URIs → + Add URI: {{redirectUri}}",
|
|
380
|
-
"",
|
|
381
|
-
"6. Paste the Client ID and Client Secret below, or export",
|
|
382
|
-
" GOOGLE_DRIVE_CLIENT_ID and GOOGLE_DRIVE_CLIENT_SECRET.",
|
|
383
|
-
],
|
|
384
|
-
});
|
|
364
|
+
rl.setOAuth(googleOAuth("googleDrive", SCOPES, "GOOGLE_DRIVE"));
|
|
385
365
|
rl.setConnectionSchema({
|
|
386
366
|
authMethod: { type: "string", required: false, description: "delegated or serviceAccount (legacy configs infer the method)" },
|
|
387
367
|
clientId: {
|
|
@@ -457,7 +437,7 @@ export default function googleDrive(rl) {
|
|
|
457
437
|
}),
|
|
458
438
|
async execute(input, ctx) {
|
|
459
439
|
const p = (input ?? {});
|
|
460
|
-
const content = resolveContent(p);
|
|
440
|
+
const content = await resolveContent(p);
|
|
461
441
|
const metadata = {
|
|
462
442
|
name: content.fileName ?? "Untitled",
|
|
463
443
|
parents: [resolveParent(p.folderId, p.driveId)],
|
|
@@ -713,7 +693,7 @@ export default function googleDrive(rl) {
|
|
|
713
693
|
// Step 1: upload new bytes if provided. Drive requires PATCH on
|
|
714
694
|
// the upload endpoint for content replacement.
|
|
715
695
|
if (hasBytes) {
|
|
716
|
-
const c = resolveContent(p);
|
|
696
|
+
const c = await resolveContent(p);
|
|
717
697
|
const mimeType = c.mimeType ?? p.mimeType ?? "application/octet-stream";
|
|
718
698
|
if (c.buffer) {
|
|
719
699
|
const url = new URL(`${API_BASE}/upload/drive/v3/files/${pathSegment(fileId)}`);
|
|
@@ -1494,9 +1474,6 @@ export default function googleDrive(rl) {
|
|
|
1494
1474
|
if (p[k] !== undefined)
|
|
1495
1475
|
body[k] = p[k];
|
|
1496
1476
|
}
|
|
1497
|
-
if (Object.keys(body).length === 0) {
|
|
1498
|
-
throw new Error("googleDrive.revision.update: pass at least one of keepForever / published / publishAuto / publishedOutsideDomain.");
|
|
1499
|
-
}
|
|
1500
1477
|
return driveRequest(ctx, "PATCH", `/drive/v3/files/${pathSegment(p.fileId)}/revisions/${pathSegment(p.revisionId)}`, body, { fields: REVISION_FIELDS });
|
|
1501
1478
|
},
|
|
1502
1479
|
});
|
|
@@ -1682,9 +1659,6 @@ export default function googleDrive(rl) {
|
|
|
1682
1659
|
qs.removeExpiration = true;
|
|
1683
1660
|
if (p.useDomainAdminAccess)
|
|
1684
1661
|
qs.useDomainAdminAccess = true;
|
|
1685
|
-
if (Object.keys(body).length === 0 && !qs.removeExpiration) {
|
|
1686
|
-
throw new Error("googleDrive.permission.update: pass at least one of role / expirationTime / removeExpiration.");
|
|
1687
|
-
}
|
|
1688
1662
|
return driveRequest(ctx, "PATCH", `/drive/v3/files/${pathSegment(p.fileId)}/permissions/${pathSegment(p.permissionId)}`, body, qs);
|
|
1689
1663
|
},
|
|
1690
1664
|
});
|
|
@@ -117,7 +117,7 @@ export default function googleImage(rl) {
|
|
|
117
117
|
if (typeof p.prompt !== "string" || p.prompt.length === 0) {
|
|
118
118
|
throw new Error("googleImage: prompt is required");
|
|
119
119
|
}
|
|
120
|
-
const img = readImageInput(p.imagePath, "googleImage");
|
|
120
|
+
const img = await readImageInput(p.imagePath, "googleImage");
|
|
121
121
|
const model = p.model ?? "gemini-2.5-flash-image";
|
|
122
122
|
const data = await generateContent(ctx, model, {
|
|
123
123
|
contents: [
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
*/
|
|
37
37
|
import * as t from "typebox";
|
|
38
38
|
import { pathSegment } from "../../_shared/credentials.js";
|
|
39
|
-
import { googleCredential, googleJsonRequest } from "../../_shared/googleAuth.js";
|
|
39
|
+
import { googleCredential, googleJsonRequest, googleOAuth, } from "../../_shared/googleAuth.js";
|
|
40
40
|
import { Id, JsonValue, NonEmptyString, NonNegativeInteger, PositiveInteger, RawGoogleObject, StringArray, stringEnum, } from "../../_shared/googleSchemas.js";
|
|
41
41
|
const ROW_NUMBER = "row_number";
|
|
42
42
|
const sheetReferenceSchema = t.Union([NonEmptyString, NonNegativeInteger]);
|
|
@@ -338,39 +338,7 @@ export default function googleSheets(rl) {
|
|
|
338
338
|
rl.setName("googleSheets");
|
|
339
339
|
rl.setVersion("0.1.0");
|
|
340
340
|
rl.setCredential(googleCredential("googleSheets", SCOPES));
|
|
341
|
-
rl.setOAuth(
|
|
342
|
-
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
343
|
-
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
344
|
-
scopes: SCOPES,
|
|
345
|
-
authParams: { access_type: "offline", prompt: "consent" },
|
|
346
|
-
setupHelp: [
|
|
347
|
-
"You need a Google Cloud OAuth client. Takes ~5 minutes, one time.",
|
|
348
|
-
"",
|
|
349
|
-
"1. Create or pick a Google Cloud project:",
|
|
350
|
-
" https://console.cloud.google.com/projectcreate",
|
|
351
|
-
"",
|
|
352
|
-
"2. Enable the Google Sheets API (and Drive API, required for",
|
|
353
|
-
" spreadsheet.delete):",
|
|
354
|
-
" https://console.cloud.google.com/apis/library/sheets.googleapis.com",
|
|
355
|
-
" https://console.cloud.google.com/apis/library/drive.googleapis.com",
|
|
356
|
-
"",
|
|
357
|
-
"3. Configure the OAuth consent screen:",
|
|
358
|
-
" https://console.cloud.google.com/apis/credentials/consent",
|
|
359
|
-
" • Audience: External",
|
|
360
|
-
"",
|
|
361
|
-
"4. Add yourself as a test user:",
|
|
362
|
-
" https://console.cloud.google.com/auth/audience",
|
|
363
|
-
"",
|
|
364
|
-
"5. Create the OAuth client:",
|
|
365
|
-
" https://console.cloud.google.com/apis/credentials",
|
|
366
|
-
" • + Create credentials → OAuth client ID",
|
|
367
|
-
" • Application type: Web application",
|
|
368
|
-
" • Authorized redirect URIs → + Add URI: {{redirectUri}}",
|
|
369
|
-
"",
|
|
370
|
-
"6. Paste the Client ID and Client Secret below, or export",
|
|
371
|
-
" GOOGLE_SHEETS_CLIENT_ID and GOOGLE_SHEETS_CLIENT_SECRET.",
|
|
372
|
-
],
|
|
373
|
-
});
|
|
341
|
+
rl.setOAuth(googleOAuth("googleSheets", SCOPES, "GOOGLE_SHEETS"));
|
|
374
342
|
rl.setConnectionSchema({
|
|
375
343
|
authMethod: { type: "string", required: false, description: "delegated or serviceAccount (legacy configs infer the method)" },
|
|
376
344
|
clientId: {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
import { writeFileSync } from "node:fs";
|
|
23
23
|
import * as t from "typebox";
|
|
24
24
|
import { pathSegment } from "../../_shared/credentials.js";
|
|
25
|
-
import { googleCredential, googleDownload, googleJsonRequest, } from "../../_shared/googleAuth.js";
|
|
25
|
+
import { googleCredential, googleDownload, googleJsonRequest, googleOAuth, } from "../../_shared/googleAuth.js";
|
|
26
26
|
import { Id, NonEmptyString, PositiveInteger, RawGoogleObject, StringArray, stringEnum, } from "../../_shared/googleSchemas.js";
|
|
27
27
|
// ─── Request ─────────────────────────────────────────────────────
|
|
28
28
|
const API_BASE = "https://slides.googleapis.com/v1";
|
|
@@ -47,37 +47,7 @@ export default function googleSlides(rl) {
|
|
|
47
47
|
rl.setName("googleSlides");
|
|
48
48
|
rl.setVersion("0.1.0");
|
|
49
49
|
rl.setCredential(googleCredential("googleSlides", SCOPES));
|
|
50
|
-
rl.setOAuth(
|
|
51
|
-
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
52
|
-
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
53
|
-
scopes: SCOPES,
|
|
54
|
-
authParams: { access_type: "offline", prompt: "consent" },
|
|
55
|
-
setupHelp: [
|
|
56
|
-
"You need a Google Cloud OAuth client. Takes ~5 minutes, one time.",
|
|
57
|
-
"",
|
|
58
|
-
"1. Create or pick a Google Cloud project:",
|
|
59
|
-
" https://console.cloud.google.com/projectcreate",
|
|
60
|
-
"",
|
|
61
|
-
"2. Enable the Google Slides API:",
|
|
62
|
-
" https://console.cloud.google.com/apis/library/slides.googleapis.com",
|
|
63
|
-
"",
|
|
64
|
-
"3. Configure the OAuth consent screen:",
|
|
65
|
-
" https://console.cloud.google.com/apis/credentials/consent",
|
|
66
|
-
" • Audience: External",
|
|
67
|
-
"",
|
|
68
|
-
"4. Add yourself as a test user:",
|
|
69
|
-
" https://console.cloud.google.com/auth/audience",
|
|
70
|
-
"",
|
|
71
|
-
"5. Create the OAuth client:",
|
|
72
|
-
" https://console.cloud.google.com/apis/credentials",
|
|
73
|
-
" • + Create credentials → OAuth client ID",
|
|
74
|
-
" • Application type: Web application",
|
|
75
|
-
" • Authorized redirect URIs → + Add URI: {{redirectUri}}",
|
|
76
|
-
"",
|
|
77
|
-
"6. Paste the Client ID and Client Secret below, or export",
|
|
78
|
-
" GOOGLE_SLIDES_CLIENT_ID and GOOGLE_SLIDES_CLIENT_SECRET.",
|
|
79
|
-
],
|
|
80
|
-
});
|
|
50
|
+
rl.setOAuth(googleOAuth("googleSlides", SCOPES, "GOOGLE_SLIDES"));
|
|
81
51
|
rl.setConnectionSchema({
|
|
82
52
|
authMethod: { type: "string", required: false, description: "delegated or serviceAccount (legacy configs infer the method)" },
|
|
83
53
|
clientId: {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import * as t from "typebox";
|
|
23
23
|
import { pathSegment } from "../../_shared/credentials.js";
|
|
24
|
-
import { googleCredential, googleJsonRequest } from "../../_shared/googleAuth.js";
|
|
24
|
+
import { googleCredential, googleJsonRequest, googleOAuth, } from "../../_shared/googleAuth.js";
|
|
25
25
|
import { GoogleTimestamp, Id, stringEnum, } from "../../_shared/googleSchemas.js";
|
|
26
26
|
// ─── Request ─────────────────────────────────────────────────────
|
|
27
27
|
const API_BASE = "https://tasks.googleapis.com/tasks/v1";
|
|
@@ -71,37 +71,7 @@ export default function googleTasks(rl) {
|
|
|
71
71
|
rl.setName("googleTasks");
|
|
72
72
|
rl.setVersion("0.1.0");
|
|
73
73
|
rl.setCredential(googleCredential("googleTasks", SCOPES));
|
|
74
|
-
rl.setOAuth(
|
|
75
|
-
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
76
|
-
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
77
|
-
scopes: SCOPES,
|
|
78
|
-
authParams: { access_type: "offline", prompt: "consent" },
|
|
79
|
-
setupHelp: [
|
|
80
|
-
"You need a Google Cloud OAuth client. Takes ~5 minutes, one time.",
|
|
81
|
-
"",
|
|
82
|
-
"1. Create or pick a Google Cloud project:",
|
|
83
|
-
" https://console.cloud.google.com/projectcreate",
|
|
84
|
-
"",
|
|
85
|
-
"2. Enable the Google Tasks API:",
|
|
86
|
-
" https://console.cloud.google.com/apis/library/tasks.googleapis.com",
|
|
87
|
-
"",
|
|
88
|
-
"3. Configure the OAuth consent screen:",
|
|
89
|
-
" https://console.cloud.google.com/apis/credentials/consent",
|
|
90
|
-
" • Audience: External",
|
|
91
|
-
"",
|
|
92
|
-
"4. Add yourself as a test user:",
|
|
93
|
-
" https://console.cloud.google.com/auth/audience",
|
|
94
|
-
"",
|
|
95
|
-
"5. Create the OAuth client:",
|
|
96
|
-
" https://console.cloud.google.com/apis/credentials",
|
|
97
|
-
" • + Create credentials → OAuth client ID",
|
|
98
|
-
" • Application type: Web application",
|
|
99
|
-
" • Authorized redirect URIs → + Add URI: {{redirectUri}}",
|
|
100
|
-
"",
|
|
101
|
-
"6. Paste the Client ID and Client Secret below, or export",
|
|
102
|
-
" GOOGLE_TASKS_CLIENT_ID and GOOGLE_TASKS_CLIENT_SECRET.",
|
|
103
|
-
],
|
|
104
|
-
});
|
|
74
|
+
rl.setOAuth(googleOAuth("googleTasks", SCOPES, "GOOGLE_TASKS"));
|
|
105
75
|
rl.setConnectionSchema({
|
|
106
76
|
authMethod: { type: "string", required: false, description: "delegated or serviceAccount (legacy configs infer the method)" },
|
|
107
77
|
clientId: { type: "string", required: false, env: "GOOGLE_TASKS_CLIENT_ID" },
|
|
@@ -62,7 +62,10 @@ export default function highlevel(rl) {
|
|
|
62
62
|
},
|
|
63
63
|
async execute(input, ctx) {
|
|
64
64
|
const locationId = locationOf(ctx);
|
|
65
|
-
const body = {
|
|
65
|
+
const body = {
|
|
66
|
+
...input,
|
|
67
|
+
locationId,
|
|
68
|
+
};
|
|
66
69
|
if (typeof body.tags === "string")
|
|
67
70
|
body.tags = body.tags
|
|
68
71
|
.split(",")
|
|
@@ -176,7 +179,10 @@ export default function highlevel(rl) {
|
|
|
176
179
|
},
|
|
177
180
|
async execute(input, ctx) {
|
|
178
181
|
const locationId = locationOf(ctx);
|
|
179
|
-
const body = {
|
|
182
|
+
const body = {
|
|
183
|
+
...input,
|
|
184
|
+
locationId,
|
|
185
|
+
};
|
|
180
186
|
if (body.stageId) {
|
|
181
187
|
body.pipelineStageId = body.stageId;
|
|
182
188
|
delete body.stageId;
|