runline 0.29.0 → 0.30.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 (69) hide show
  1. package/dist/auth/errors.d.ts +22 -0
  2. package/dist/auth/errors.js +25 -0
  3. package/dist/auth/oauth2.d.ts +21 -0
  4. package/dist/auth/oauth2.js +154 -0
  5. package/dist/auth/token.d.ts +11 -0
  6. package/dist/auth/token.js +231 -0
  7. package/dist/auth/types.d.ts +93 -0
  8. package/dist/auth/types.js +1 -0
  9. package/dist/commands/auth.js +26 -13
  10. package/dist/commands/connection.js +3 -6
  11. package/dist/commands/exec.js +7 -2
  12. package/dist/commands/init.js +9 -2
  13. package/dist/config/loader.d.ts +7 -13
  14. package/dist/config/loader.js +58 -88
  15. package/dist/config/store.d.ts +17 -0
  16. package/dist/config/store.js +85 -0
  17. package/dist/connections/file.d.ts +13 -0
  18. package/dist/connections/file.js +53 -0
  19. package/dist/connections/memory.d.ts +15 -0
  20. package/dist/connections/memory.js +70 -0
  21. package/dist/connections/types.d.ts +47 -0
  22. package/dist/connections/types.js +10 -0
  23. package/dist/core/engine.d.ts +5 -2
  24. package/dist/core/engine.js +48 -55
  25. package/dist/core/oauth.d.ts +20 -15
  26. package/dist/core/oauth.js +195 -98
  27. package/dist/credentials/http.d.ts +19 -0
  28. package/dist/credentials/http.js +109 -0
  29. package/dist/credentials/policy.d.ts +5 -0
  30. package/dist/credentials/policy.js +92 -0
  31. package/dist/credentials/registry.d.ts +31 -0
  32. package/dist/credentials/registry.js +171 -0
  33. package/dist/credentials/transport.d.ts +38 -0
  34. package/dist/credentials/transport.js +283 -0
  35. package/dist/credentials/types.d.ts +80 -0
  36. package/dist/credentials/types.js +1 -0
  37. package/dist/index.d.ts +12 -0
  38. package/dist/index.js +8 -0
  39. package/dist/plugin/types.d.ts +30 -17
  40. package/dist/plugins/_shared/credentialAdapter.js +88 -0
  41. package/dist/plugins/_shared/googleAuth.js +89 -103
  42. package/dist/plugins/_shared/googleCredentials.js +160 -0
  43. package/dist/plugins/_shared/microsoftAuth.js +81 -74
  44. package/dist/plugins/_shared/microsoftCredentials.js +90 -0
  45. package/dist/plugins/_shared/tokenRefresh.js +39 -0
  46. package/dist/plugins/bitwarden/src/index.js +13 -31
  47. package/dist/plugins/gmail/src/index.js +8 -44
  48. package/dist/plugins/googleAppsScript/src/index.js +3 -25
  49. package/dist/plugins/googleCalendar/src/index.js +3 -41
  50. package/dist/plugins/googleContacts/src/index.js +3 -36
  51. package/dist/plugins/googleDocs/src/index.js +5 -0
  52. package/dist/plugins/googleDocs/src/shared.js +2 -30
  53. package/dist/plugins/googleDrive/src/index.js +52 -98
  54. package/dist/plugins/googleSheets/src/index.js +3 -40
  55. package/dist/plugins/googleSlides/src/index.js +5 -39
  56. package/dist/plugins/googleTasks/src/index.js +3 -30
  57. package/dist/plugins/halopsa/src/index.js +16 -31
  58. package/dist/plugins/microsoftCalendar/src/index.js +2 -1
  59. package/dist/plugins/microsoftFiles/src/index.js +10 -24
  60. package/dist/plugins/microsoftMail/src/index.js +2 -1
  61. package/dist/plugins/paypal/src/index.js +14 -29
  62. package/dist/plugins/plaud/src/index.js +210 -0
  63. package/dist/plugins/plaud/src/shared.js +221 -0
  64. package/dist/plugins/salesforce/src/shared.js +17 -32
  65. package/dist/sdk.d.ts +13 -5
  66. package/dist/sdk.js +35 -29
  67. package/dist/utils/observer.d.ts +2 -0
  68. package/dist/utils/observer.js +11 -0
  69. package/package.json +1 -1
@@ -1,29 +1,4 @@
1
- let cachedToken = null;
2
- async function getAccessToken(clientId, clientSecret, tokenUrl) {
3
- if (cachedToken && Date.now() < cachedToken.expiresAt)
4
- return cachedToken.value;
5
- const res = await fetch(tokenUrl, {
6
- method: "POST",
7
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
8
- body: new URLSearchParams({
9
- client_id: clientId,
10
- client_secret: clientSecret,
11
- grant_type: "client_credentials",
12
- scope: "api.organization",
13
- deviceName: "runline",
14
- deviceType: "2",
15
- deviceIdentifier: "runline",
16
- }),
17
- });
18
- if (!res.ok)
19
- throw new Error(`Bitwarden token error ${res.status}: ${await res.text()}`);
20
- const data = (await res.json());
21
- cachedToken = {
22
- value: data.access_token,
23
- expiresAt: Date.now() + (data.expires_in - 60) * 1000,
24
- };
25
- return cachedToken.value;
26
- }
1
+ import { coordinatedAccessToken, requestToken, } from "../../_shared/tokenRefresh.js";
27
2
  async function apiRequest(token, baseUrl, method, endpoint, body, qs) {
28
3
  const url = new URL(`${baseUrl}${endpoint}`);
29
4
  if (qs) {
@@ -57,8 +32,7 @@ async function apiRequest(token, baseUrl, method, endpoint, body, qs) {
57
32
  return res.json();
58
33
  return { success: true };
59
34
  }
60
- function getConn(ctx) {
61
- const cfg = ctx.connection.config;
35
+ function getConn(cfg) {
62
36
  const domain = cfg.domain?.replace(/\/$/, "");
63
37
  const env = cfg.environment;
64
38
  const baseUrl = env === "selfHosted" && domain
@@ -75,9 +49,17 @@ function getConn(ctx) {
75
49
  };
76
50
  }
77
51
  async function authedRequest(ctx, method, endpoint, body, qs) {
78
- const { clientId, clientSecret, baseUrl, tokenUrl } = getConn(ctx);
79
- const token = await getAccessToken(clientId, clientSecret, tokenUrl);
80
- return apiRequest(token, baseUrl, method, endpoint, body, qs);
52
+ const token = await coordinatedAccessToken(ctx, (current) => {
53
+ const { clientId, clientSecret, tokenUrl } = getConn(current);
54
+ return requestToken({ url: tokenUrl, clientAuthentication: "client_secret_post" }, {
55
+ grant_type: "client_credentials",
56
+ scope: "api.organization",
57
+ deviceName: "runline",
58
+ deviceType: "2",
59
+ deviceIdentifier: "runline",
60
+ }, { clientId, clientSecret });
61
+ });
62
+ return apiRequest(token, getConn(ctx.connection.config).baseUrl, method, endpoint, body, qs);
81
63
  }
82
64
  export default function bitwarden(rl) {
83
65
  rl.setName("bitwarden");
@@ -22,12 +22,8 @@
22
22
  */
23
23
  import * as t from "typebox";
24
24
  import { renderEmailJsx } from "../../_shared/emailJsx.js";
25
- import { googleAccessToken } from "../../_shared/googleAuth.js";
25
+ import { googleJsonRequest } from "../../_shared/googleAuth.js";
26
26
  import { GoogleTimestamp, Id, NonEmptyString, stringEnum, } from "../../_shared/googleSchemas.js";
27
- // ─── Auth ────────────────────────────────────────────────────────
28
- async function accessToken(ctx) {
29
- return googleAccessToken(ctx, "gmail", SCOPES);
30
- }
31
27
  // ─── Request ─────────────────────────────────────────────────────
32
28
  const API_BASE = "https://gmail.googleapis.com/gmail/v1/users/me";
33
29
  function sendFailureRetryHint(method, path) {
@@ -36,48 +32,15 @@ function sendFailureRetryHint(method, path) {
36
32
  return " Before retrying, check whether the email was already sent using gmail.message.list({ q: 'in:sent to:<recipient> subject:\"<subject>\"', maxResults: 10 }) and inspect likely matches with gmail.message.get({ id, format: 'metadata' }).";
37
33
  }
38
34
  async function gmailRequest(ctx, method, path, body, qs) {
39
- const token = await accessToken(ctx);
40
- const url = new URL(`${API_BASE}${path}`);
41
- if (qs) {
42
- for (const [k, v] of Object.entries(qs)) {
43
- if (v === undefined || v === null)
44
- continue;
45
- if (Array.isArray(v)) {
46
- for (const entry of v)
47
- url.searchParams.append(k, String(entry));
48
- }
49
- else {
50
- url.searchParams.set(k, String(v));
51
- }
52
- }
53
- }
54
- const init = {
55
- method,
56
- headers: {
57
- Authorization: `Bearer ${token}`,
58
- Accept: "application/json",
59
- },
60
- };
61
- if (body && Object.keys(body).length > 0) {
62
- init.headers["Content-Type"] =
63
- "application/json";
64
- init.body = JSON.stringify(body);
65
- }
66
- let res;
67
35
  try {
68
- res = await fetch(url.toString(), init);
69
- }
70
- catch (err) {
71
- const msg = err instanceof Error ? err.message : String(err);
72
- throw new Error(`gmail: ${method} ${path} failed: ${msg}.${sendFailureRetryHint(method, path)}`);
36
+ return await googleJsonRequest(ctx, "gmail", SCOPES, method, `${API_BASE}${path}`, body, qs);
73
37
  }
74
- if (res.status === 204)
75
- return { success: true };
76
- const text = await res.text();
77
- if (!res.ok) {
78
- throw new Error(`gmail: ${method} ${path} → ${res.status} ${text}${sendFailureRetryHint(method, path)}`);
38
+ catch (error) {
39
+ const hint = sendFailureRetryHint(method, path);
40
+ if (!hint)
41
+ throw error;
42
+ throw new Error(`${error instanceof Error ? error.message : "gmail: request failed"}.${hint}`);
79
43
  }
80
- return text ? JSON.parse(text) : { success: true };
81
44
  }
82
45
  async function paginateAll(ctx, path, key, qs) {
83
46
  const out = [];
@@ -672,6 +635,7 @@ export default function gmail(rl) {
672
635
  ],
673
636
  });
674
637
  rl.setConnectionSchema({
638
+ authMethod: { type: "string", required: false, description: "delegated or serviceAccount (legacy configs infer the method)" },
675
639
  clientId: {
676
640
  type: "string",
677
641
  required: false,
@@ -1,5 +1,5 @@
1
1
  import * as t from "typebox";
2
- import { googleAccessToken } from "../../_shared/googleAuth.js";
2
+ import { googleJsonRequest } from "../../_shared/googleAuth.js";
3
3
  import { Id, NonEmptyString, PositiveInteger, stringEnum, } from "../../_shared/googleSchemas.js";
4
4
  const SCRIPT_API = "https://script.googleapis.com/v1";
5
5
  const DRIVE_API = "https://www.googleapis.com/drive/v3";
@@ -33,36 +33,14 @@ const scriptFileSchema = t.Object({
33
33
  type: scriptFileTypeSchema,
34
34
  source: t.String(),
35
35
  }, { additionalProperties: false });
36
- function accessToken(ctx) {
37
- return googleAccessToken(ctx, "googleAppsScript", SCOPES);
38
- }
39
36
  async function call(ctx, method, url, payload) {
40
- const headers = { Authorization: `Bearer ${await accessToken(ctx)}` };
41
- if (payload !== undefined)
42
- headers["Content-Type"] = "application/json";
43
- const res = await fetch(url, {
44
- method,
45
- headers,
46
- body: payload !== undefined ? JSON.stringify(payload) : undefined,
47
- });
48
- const text = await res.text();
49
- let json;
50
- try {
51
- json = text ? JSON.parse(text) : {};
52
- }
53
- catch {
54
- json = { raw: text };
55
- }
56
- if (!res.ok) {
57
- const msg = json?.error?.message || json?.error_description || text.slice(0, 300) || res.status;
58
- throw new Error(`googleAppsScript: ${method} ${res.status}: ${msg}`);
59
- }
60
- return json;
37
+ return googleJsonRequest(ctx, "googleAppsScript", SCOPES, method, url, payload);
61
38
  }
62
39
  export default function googleAppsScript(rl) {
63
40
  rl.setName("googleAppsScript");
64
41
  rl.setVersion("1.0.0");
65
42
  rl.setConnectionSchema({
43
+ authMethod: { type: "string", required: false, description: "delegated or serviceAccount (legacy configs infer the method)" },
66
44
  clientId: { type: "string", required: false, description: "OAuth client ID", env: "GOOGLE_APPS_SCRIPT_CLIENT_ID" },
67
45
  clientSecret: { type: "string", required: false, description: "OAuth client secret", env: "GOOGLE_APPS_SCRIPT_CLIENT_SECRET" },
68
46
  refreshToken: { type: "string", required: false, description: "OAuth refresh token", env: "GOOGLE_APPS_SCRIPT_REFRESH_TOKEN" },
@@ -29,53 +29,14 @@
29
29
  */
30
30
  import rrulePkg from "rrule";
31
31
  import * as t from "typebox";
32
- import { googleAccessToken } from "../../_shared/googleAuth.js";
32
+ import { googleJsonRequest } from "../../_shared/googleAuth.js";
33
33
  import { Id, NonEmptyString, NonEmptyStringArray, PositiveInteger, StringOrStringArray, stringEnum, } from "../../_shared/googleSchemas.js";
34
34
  // `rrule` ships as CJS; named imports fail under Node ESM.
35
35
  const { RRule } = rrulePkg;
36
- // ─── Auth ────────────────────────────────────────────────────────
37
- async function accessToken(ctx) {
38
- return googleAccessToken(ctx, "googleCalendar", SCOPES);
39
- }
40
36
  // ─── Request ─────────────────────────────────────────────────────
41
37
  const API_BASE = "https://www.googleapis.com/calendar/v3";
42
38
  async function calRequest(ctx, method, path, body, qs) {
43
- const token = await accessToken(ctx);
44
- const url = new URL(`${API_BASE}${path}`);
45
- if (qs) {
46
- for (const [k, v] of Object.entries(qs)) {
47
- if (v === undefined || v === null)
48
- continue;
49
- if (Array.isArray(v)) {
50
- for (const entry of v)
51
- url.searchParams.append(k, String(entry));
52
- }
53
- else {
54
- url.searchParams.set(k, String(v));
55
- }
56
- }
57
- }
58
- const init = {
59
- method,
60
- headers: {
61
- Authorization: `Bearer ${token}`,
62
- Accept: "application/json",
63
- },
64
- };
65
- if (body && Object.keys(body).length > 0) {
66
- init.headers["Content-Type"] = "application/json";
67
- init.body = JSON.stringify(body);
68
- }
69
- const res = await fetch(url.toString(), init);
70
- if (res.status === 204)
71
- return { success: true };
72
- const text = await res.text();
73
- if (!res.ok) {
74
- // 403/429 deserve a retry with exponential backoff — Calendar
75
- // hands these out freely when you hit per-user quota.
76
- throw new Error(`googleCalendar: ${method} ${path} → ${res.status} ${text}`);
77
- }
78
- return text ? JSON.parse(text) : { success: true };
39
+ return googleJsonRequest(ctx, "googleCalendar", SCOPES, method, `${API_BASE}${path}`, body, qs);
79
40
  }
80
41
  async function paginateAll(ctx, path, key, qs) {
81
42
  const out = [];
@@ -572,6 +533,7 @@ export default function googleCalendar(rl) {
572
533
  ],
573
534
  });
574
535
  rl.setConnectionSchema({
536
+ authMethod: { type: "string", required: false, description: "delegated or serviceAccount (legacy configs infer the method)" },
575
537
  clientId: {
576
538
  type: "string",
577
539
  required: false,
@@ -23,7 +23,7 @@
23
23
  * convenience `contactId` field stripped from `resourceName`.
24
24
  */
25
25
  import * as t from "typebox";
26
- import { googleAccessToken } from "../../_shared/googleAuth.js";
26
+ import { googleJsonRequest } from "../../_shared/googleAuth.js";
27
27
  import { Id, NonEmptyString, PositiveInteger, stringEnum, } from "../../_shared/googleSchemas.js";
28
28
  // ─── Fields ─────────────────────────────────────────────────────
29
29
  /**
@@ -81,44 +81,10 @@ const UPDATABLE_PERSON_FIELDS = new Set([
81
81
  "urls",
82
82
  "userDefined",
83
83
  ]);
84
- // ─── Auth ────────────────────────────────────────────────────────
85
- async function accessToken(ctx) {
86
- return googleAccessToken(ctx, "googleContacts", SCOPES);
87
- }
88
84
  // ─── Request ─────────────────────────────────────────────────────
89
85
  const API_BASE = "https://people.googleapis.com/v1";
90
86
  async function peopleRequest(ctx, method, path, body, qs) {
91
- const token = await accessToken(ctx);
92
- const url = new URL(`${API_BASE}${path}`);
93
- if (qs) {
94
- for (const [k, v] of Object.entries(qs)) {
95
- if (v === undefined || v === null)
96
- continue;
97
- if (Array.isArray(v)) {
98
- for (const entry of v)
99
- url.searchParams.append(k, String(entry));
100
- }
101
- else {
102
- url.searchParams.set(k, String(v));
103
- }
104
- }
105
- }
106
- const init = {
107
- method,
108
- headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
109
- };
110
- if (body && Object.keys(body).length > 0) {
111
- init.headers["Content-Type"] = "application/json";
112
- init.body = JSON.stringify(body);
113
- }
114
- const res = await fetch(url.toString(), init);
115
- if (res.status === 204)
116
- return { success: true };
117
- const text = await res.text();
118
- if (!res.ok) {
119
- throw new Error(`googleContacts: ${method} ${path} → ${res.status} ${text}`);
120
- }
121
- return text ? JSON.parse(text) : { success: true };
87
+ return googleJsonRequest(ctx, "googleContacts", SCOPES, method, `${API_BASE}${path}`, body, qs);
122
88
  }
123
89
  async function paginateAll(ctx, path, key, qs) {
124
90
  const out = [];
@@ -438,6 +404,7 @@ export default function googleContacts(rl) {
438
404
  ],
439
405
  });
440
406
  rl.setConnectionSchema({
407
+ authMethod: { type: "string", required: false, description: "delegated or serviceAccount (legacy configs infer the method)" },
441
408
  clientId: { type: "string", required: false, env: "GOOGLE_CONTACTS_CLIENT_ID" },
442
409
  clientSecret: { type: "string", required: false, env: "GOOGLE_CONTACTS_CLIENT_SECRET" },
443
410
  refreshToken: { type: "string", required: false, env: "GOOGLE_CONTACTS_REFRESH_TOKEN" },
@@ -51,6 +51,11 @@ export default function googleDocs(rl) {
51
51
  ],
52
52
  });
53
53
  rl.setConnectionSchema({
54
+ authMethod: {
55
+ type: "string",
56
+ required: false,
57
+ description: "delegated or serviceAccount (legacy configs infer the method)",
58
+ },
54
59
  clientId: { type: "string", required: false, env: "GOOGLE_DOCS_CLIENT_ID" },
55
60
  clientSecret: {
56
61
  type: "string",
@@ -1,5 +1,5 @@
1
1
  import * as t from "typebox";
2
- import { googleAccessToken } from "../../_shared/googleAuth.js";
2
+ import { googleJsonRequest } from "../../_shared/googleAuth.js";
3
3
  import { RawGoogleObject } from "../../_shared/googleSchemas.js";
4
4
  export const SCOPES = [
5
5
  "https://www.googleapis.com/auth/documents",
@@ -60,36 +60,8 @@ export const WriteControl = t.Object({
60
60
  ],
61
61
  });
62
62
  export { RawGoogleObject };
63
- export async function accessToken(ctx) {
64
- return googleAccessToken(ctx, "googleDocs", SCOPES);
65
- }
66
63
  export async function docsRequest(ctx, method, path, body, qs, baseOverride) {
67
- const token = await accessToken(ctx);
68
- const url = new URL(`${baseOverride ?? DOCS_BASE}${path}`);
69
- if (qs) {
70
- for (const [k, v] of Object.entries(qs)) {
71
- if (v === undefined || v === null)
72
- continue;
73
- url.searchParams.set(k, String(v));
74
- }
75
- }
76
- const init = {
77
- method,
78
- headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
79
- };
80
- if (body && Object.keys(body).length > 0) {
81
- init.headers["Content-Type"] =
82
- "application/json";
83
- init.body = JSON.stringify(body);
84
- }
85
- const res = await fetch(url.toString(), init);
86
- if (res.status === 204)
87
- return { success: true };
88
- const text = await res.text();
89
- if (!res.ok) {
90
- throw new Error(`googleDocs: ${method} ${path} → ${res.status} ${text}`);
91
- }
92
- return text ? JSON.parse(text) : { success: true };
64
+ return googleJsonRequest(ctx, "googleDocs", SCOPES, method, `${baseOverride ?? DOCS_BASE}${path}`, body, qs);
93
65
  }
94
66
  const DOC_URL_REGEX = /https:\/\/docs\.google\.com\/document\/d\/([a-zA-Z0-9-_]+)/;
95
67
  export function extractDocumentId(input) {