@alisio/plugin-google-chat 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 (69) hide show
  1. package/.agents/skills/google-chat-context/SKILL.md +63 -0
  2. package/LICENSE +21 -0
  3. package/README.md +170 -0
  4. package/cover.svg +25 -0
  5. package/dist/commands.d.ts +10 -0
  6. package/dist/commands.d.ts.map +1 -0
  7. package/dist/commands.js +86 -0
  8. package/dist/commands.js.map +1 -0
  9. package/dist/config.d.ts +86 -0
  10. package/dist/config.d.ts.map +1 -0
  11. package/dist/config.js +179 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/errors.d.ts +22 -0
  14. package/dist/errors.d.ts.map +1 -0
  15. package/dist/errors.js +44 -0
  16. package/dist/errors.js.map +1 -0
  17. package/dist/http.d.ts +66 -0
  18. package/dist/http.d.ts.map +1 -0
  19. package/dist/http.js +208 -0
  20. package/dist/http.js.map +1 -0
  21. package/dist/index.d.ts +49 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +71 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/oauth.d.ts +78 -0
  26. package/dist/oauth.d.ts.map +1 -0
  27. package/dist/oauth.js +290 -0
  28. package/dist/oauth.js.map +1 -0
  29. package/dist/render.d.ts +18 -0
  30. package/dist/render.d.ts.map +1 -0
  31. package/dist/render.js +50 -0
  32. package/dist/render.js.map +1 -0
  33. package/dist/text.d.ts +26 -0
  34. package/dist/text.d.ts.map +1 -0
  35. package/dist/text.js +63 -0
  36. package/dist/text.js.map +1 -0
  37. package/dist/time.d.ts +12 -0
  38. package/dist/time.d.ts.map +1 -0
  39. package/dist/time.js +24 -0
  40. package/dist/time.js.map +1 -0
  41. package/dist/token-set.d.ts +26 -0
  42. package/dist/token-set.d.ts.map +1 -0
  43. package/dist/token-set.js +62 -0
  44. package/dist/token-set.js.map +1 -0
  45. package/dist/token.d.ts +30 -0
  46. package/dist/token.d.ts.map +1 -0
  47. package/dist/token.js +119 -0
  48. package/dist/token.js.map +1 -0
  49. package/dist/toolkit.d.ts +14 -0
  50. package/dist/toolkit.d.ts.map +1 -0
  51. package/dist/toolkit.js +39 -0
  52. package/dist/toolkit.js.map +1 -0
  53. package/dist/tools.d.ts +5 -0
  54. package/dist/tools.d.ts.map +1 -0
  55. package/dist/tools.js +298 -0
  56. package/dist/tools.js.map +1 -0
  57. package/dist/transport.d.ts +36 -0
  58. package/dist/transport.d.ts.map +1 -0
  59. package/dist/transport.js +114 -0
  60. package/dist/transport.js.map +1 -0
  61. package/dist/validation.d.ts +70 -0
  62. package/dist/validation.d.ts.map +1 -0
  63. package/dist/validation.js +201 -0
  64. package/dist/validation.js.map +1 -0
  65. package/dist/version.d.ts +3 -0
  66. package/dist/version.d.ts.map +1 -0
  67. package/dist/version.js +3 -0
  68. package/dist/version.js.map +1 -0
  69. package/package.json +53 -0
package/dist/config.js ADDED
@@ -0,0 +1,179 @@
1
+ import { homedir } from "node:os";
2
+ import { join } from "node:path";
3
+ import { GoogleChatError } from "./errors.js";
4
+ export const ENV = {
5
+ webhookUrl: "GOOGLE_CHAT_WEBHOOK_URL",
6
+ clientId: "GOOGLE_CHAT_CLIENT_ID",
7
+ clientSecret: "GOOGLE_CHAT_CLIENT_SECRET",
8
+ scopes: "GOOGLE_CHAT_SCOPES",
9
+ tokenFile: "GOOGLE_CHAT_TOKEN_FILE",
10
+ authTimeoutMs: "GOOGLE_CHAT_AUTH_TIMEOUT_MS",
11
+ configHome: "ALISIO_CONFIG_HOME",
12
+ xdgConfigHome: "XDG_CONFIG_HOME",
13
+ home: "HOME",
14
+ };
15
+ /** Fixed official origins this plugin is ever allowed to transmit to. */
16
+ export const CHAT_API_ORIGIN = "https://chat.googleapis.com";
17
+ export const OAUTH_TOKEN_ORIGIN = "https://oauth2.googleapis.com";
18
+ /** Browser authorization endpoint. It is printed for the user to open, never fetched by the plugin. */
19
+ export const OAUTH_AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/v2/auth";
20
+ /** Scopes requested by default: read, manage, and delete messages, list spaces and members. */
21
+ export const DEFAULT_SCOPES = [
22
+ "https://www.googleapis.com/auth/chat.messages",
23
+ "https://www.googleapis.com/auth/chat.spaces.readonly",
24
+ "https://www.googleapis.com/auth/chat.memberships.readonly",
25
+ ];
26
+ const CHAT_SCOPE = /^https:\/\/www\.googleapis\.com\/auth\/chat\.[a-z.]+$/;
27
+ const WEBHOOK_PATH = /^\/v1\/spaces\/[A-Za-z0-9_-]{1,200}\/messages$/;
28
+ const SAFE_PARAM = /^[A-Za-z0-9_-]{1,1024}$/;
29
+ const ALLOWED_WEBHOOK_PARAMS = new Set(["key", "token", "messageReplyOption"]);
30
+ const DEFAULT_AUTH_TIMEOUT_MS = 300_000;
31
+ const MIN_AUTH_TIMEOUT_MS = 10_000;
32
+ const MAX_AUTH_TIMEOUT_MS = 900_000;
33
+ /** How long before real expiry a token is treated as expired and refreshed. */
34
+ export const TOKEN_SKEW_MS = 60_000;
35
+ function fail(detail) {
36
+ throw new GoogleChatError("configuration missing or invalid", detail);
37
+ }
38
+ function readTrimmed(env, key) {
39
+ const raw = env[key];
40
+ if (typeof raw !== "string")
41
+ return undefined;
42
+ const value = raw.trim();
43
+ return value === "" ? undefined : value;
44
+ }
45
+ /**
46
+ * Validate an incoming webhook URL. Only an HTTPS URL on the official Chat API
47
+ * origin, on the exact `messages` collection path, with no credentials and no
48
+ * unexpected query parameters, is accepted.
49
+ */
50
+ export function parseWebhookUrl(raw) {
51
+ let url;
52
+ try {
53
+ url = new URL(raw.trim());
54
+ }
55
+ catch {
56
+ fail("GOOGLE_CHAT_WEBHOOK_URL is not a valid absolute URL");
57
+ }
58
+ if (url.protocol !== "https:")
59
+ fail("GOOGLE_CHAT_WEBHOOK_URL must use https");
60
+ if (url.username !== "" || url.password !== "")
61
+ fail("GOOGLE_CHAT_WEBHOOK_URL must not embed credentials");
62
+ if (url.hash !== "")
63
+ fail("GOOGLE_CHAT_WEBHOOK_URL must not include a fragment");
64
+ if (url.origin !== CHAT_API_ORIGIN)
65
+ fail(`GOOGLE_CHAT_WEBHOOK_URL must point at ${CHAT_API_ORIGIN}`);
66
+ if (!WEBHOOK_PATH.test(url.pathname))
67
+ fail("GOOGLE_CHAT_WEBHOOK_URL must be a https://chat.googleapis.com/v1/spaces/{space}/messages URL");
68
+ if (!url.searchParams.has("key"))
69
+ fail("GOOGLE_CHAT_WEBHOOK_URL is missing its `key` query parameter");
70
+ const token = url.searchParams.get("token");
71
+ if (token === null || token === "")
72
+ fail("GOOGLE_CHAT_WEBHOOK_URL is missing its `token` query parameter");
73
+ for (const key of url.searchParams.keys())
74
+ if (!ALLOWED_WEBHOOK_PARAMS.has(key))
75
+ fail(`GOOGLE_CHAT_WEBHOOK_URL has an unexpected query parameter "${key}"`);
76
+ for (const value of url.searchParams.values())
77
+ if (!SAFE_PARAM.test(value))
78
+ fail("GOOGLE_CHAT_WEBHOOK_URL query values must be URL-safe tokens");
79
+ const spaceId = url.pathname.split("/")[3];
80
+ if (spaceId === undefined || !/^[A-Za-z0-9_-]{1,200}$/.test(spaceId))
81
+ fail("GOOGLE_CHAT_WEBHOOK_URL has an invalid space id");
82
+ const target = {
83
+ origin: CHAT_API_ORIGIN,
84
+ space: `spaces/${spaceId}`,
85
+ hasKey: true,
86
+ hasToken: true,
87
+ };
88
+ Object.defineProperty(target, "url", {
89
+ value: url.toString(),
90
+ enumerable: false,
91
+ writable: false,
92
+ configurable: false,
93
+ });
94
+ return Object.freeze(target);
95
+ }
96
+ /** Normalize a comma- or space-separated scope list; only Chat API scopes are accepted. */
97
+ export function parseScopes(raw) {
98
+ if (raw === undefined)
99
+ return DEFAULT_SCOPES;
100
+ const scopes = raw
101
+ .split(/[\s,]+/)
102
+ .map((scope) => scope.trim())
103
+ .filter((scope) => scope !== "");
104
+ if (scopes.length === 0)
105
+ return DEFAULT_SCOPES;
106
+ for (const scope of scopes)
107
+ if (!CHAT_SCOPE.test(scope))
108
+ fail(`GOOGLE_CHAT_SCOPES must contain only Google Chat API scopes, got "${scope}"`);
109
+ return Object.freeze([...new Set(scopes)]);
110
+ }
111
+ /** Resolve the Alisio config home with the documented precedence. */
112
+ export function resolveConfigHome(env = process.env) {
113
+ const home = env[ENV.home]?.trim() || homedir();
114
+ const xdg = env[ENV.xdgConfigHome]?.trim();
115
+ return (env[ENV.configHome]?.trim() || (xdg ? join(xdg, "alisio") : join(home, ".config", "alisio")));
116
+ }
117
+ /** The plugin-owned token file: `<configHome>/google-chat/token.json`. */
118
+ export function resolveTokenFile(env = process.env, override) {
119
+ const explicit = override?.trim() || env[ENV.tokenFile]?.trim();
120
+ return explicit || join(resolveConfigHome(env), "google-chat", "token.json");
121
+ }
122
+ function parseAuthTimeout(env) {
123
+ const raw = readTrimmed(env, ENV.authTimeoutMs);
124
+ if (raw === undefined)
125
+ return DEFAULT_AUTH_TIMEOUT_MS;
126
+ if (!/^[0-9]{1,9}$/.test(raw))
127
+ fail("GOOGLE_CHAT_AUTH_TIMEOUT_MS must be an integer");
128
+ const value = Number.parseInt(raw, 10);
129
+ if (value < MIN_AUTH_TIMEOUT_MS || value > MAX_AUTH_TIMEOUT_MS)
130
+ fail(`GOOGLE_CHAT_AUTH_TIMEOUT_MS must be between ${MIN_AUTH_TIMEOUT_MS} and ${MAX_AUTH_TIMEOUT_MS}`);
131
+ return value;
132
+ }
133
+ /**
134
+ * Resolve the full configuration. A partially configured OAuth mode is an
135
+ * operator error and fails closed. With neither mode configured the plugin
136
+ * still loads so `google-chat:status` can explain what is missing, but every
137
+ * capability fails with an actionable message.
138
+ */
139
+ export function loadConfig(env = process.env) {
140
+ const webhookRaw = readTrimmed(env, ENV.webhookUrl);
141
+ const clientId = readTrimmed(env, ENV.clientId);
142
+ const clientSecret = readTrimmed(env, ENV.clientSecret);
143
+ if ((clientId === undefined) !== (clientSecret === undefined))
144
+ fail(`${ENV.clientId} and ${ENV.clientSecret} must be set together to enable OAuth user mode`);
145
+ const webhook = webhookRaw === undefined ? undefined : parseWebhookUrl(webhookRaw);
146
+ let oauth;
147
+ if (clientId !== undefined && clientSecret !== undefined) {
148
+ const oauthConfig = {
149
+ clientId,
150
+ scopes: parseScopes(readTrimmed(env, ENV.scopes)),
151
+ tokenFile: resolveTokenFile(env),
152
+ };
153
+ Object.defineProperty(oauthConfig, "clientSecret", {
154
+ value: clientSecret,
155
+ enumerable: false,
156
+ writable: false,
157
+ configurable: false,
158
+ });
159
+ oauth = Object.freeze(oauthConfig);
160
+ }
161
+ const config = {
162
+ webhook,
163
+ oauth,
164
+ authTimeoutMs: parseAuthTimeout(env),
165
+ tokenSkewMs: TOKEN_SKEW_MS,
166
+ };
167
+ return Object.freeze(config);
168
+ }
169
+ /** A secret-free description of what is configured, for `google-chat:status`. */
170
+ export function describeConfig(config) {
171
+ return {
172
+ webhookConfigured: config.webhook !== undefined,
173
+ webhookSpace: config.webhook?.space ?? null,
174
+ oauthClientConfigured: config.oauth !== undefined,
175
+ scopes: config.oauth?.scopes ?? [],
176
+ tokenFile: config.oauth?.tokenFile ?? null,
177
+ };
178
+ }
179
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Closed failure vocabulary for every network and validation outcome. Callers only
3
+ * ever see these strings (optionally with a bounded, secret-free suffix), never a
4
+ * URL, header, request body, access token, refresh token, authorization code,
5
+ * client secret, or raw upstream payload.
6
+ */
7
+ export declare const FAILURE_CODES: readonly ["invalid input", "not authenticated", "not permitted", "not found", "rate limited", "temporarily unavailable", "invalid response", "response exceeded limit", "configuration missing or invalid"];
8
+ export type FailureCode = (typeof FAILURE_CODES)[number];
9
+ /** A classified failure whose message is safe to show to a user. */
10
+ export declare class GoogleChatError extends Error {
11
+ readonly code: FailureCode;
12
+ constructor(code: FailureCode, detail?: string);
13
+ }
14
+ /**
15
+ * Reduce any thrown value to a stable, secret-free message. Unknown throwables
16
+ * and aborts collapse to `temporarily unavailable` because their own text may
17
+ * embed request details.
18
+ */
19
+ export declare function safeMessage(error: unknown): string;
20
+ /** True when the classified code is safe to retry after a delay. */
21
+ export declare function isRetryableCode(code: FailureCode): boolean;
22
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,aAAa,YACxB,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,WAAW,EACX,cAAc,EACd,yBAAyB,EACzB,kBAAkB,EAClB,yBAAyB,EACzB,kCAAkC,CAC1B,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,oEAAoE;AACpE,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAE3B,YAAY,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,EAI7C;CACF;AAID;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAKlD;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAE1D"}
package/dist/errors.js ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Closed failure vocabulary for every network and validation outcome. Callers only
3
+ * ever see these strings (optionally with a bounded, secret-free suffix), never a
4
+ * URL, header, request body, access token, refresh token, authorization code,
5
+ * client secret, or raw upstream payload.
6
+ */
7
+ export const FAILURE_CODES = [
8
+ "invalid input",
9
+ "not authenticated",
10
+ "not permitted",
11
+ "not found",
12
+ "rate limited",
13
+ "temporarily unavailable",
14
+ "invalid response",
15
+ "response exceeded limit",
16
+ "configuration missing or invalid",
17
+ ];
18
+ /** A classified failure whose message is safe to show to a user. */
19
+ export class GoogleChatError extends Error {
20
+ code;
21
+ constructor(code, detail) {
22
+ super(detail ? `${code}: ${detail}` : code);
23
+ this.name = "GoogleChatError";
24
+ this.code = code;
25
+ }
26
+ }
27
+ const RETRYABLE = /^(rate limited|temporarily unavailable)$/;
28
+ /**
29
+ * Reduce any thrown value to a stable, secret-free message. Unknown throwables
30
+ * and aborts collapse to `temporarily unavailable` because their own text may
31
+ * embed request details.
32
+ */
33
+ export function safeMessage(error) {
34
+ if (error instanceof GoogleChatError)
35
+ return error.message;
36
+ if (error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError"))
37
+ return "temporarily unavailable";
38
+ return "temporarily unavailable";
39
+ }
40
+ /** True when the classified code is safe to retry after a delay. */
41
+ export function isRetryableCode(code) {
42
+ return RETRYABLE.test(code);
43
+ }
44
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,eAAe;IACf,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,cAAc;IACd,yBAAyB;IACzB,kBAAkB;IAClB,yBAAyB;IACzB,kCAAkC;CAC1B,CAAC;AAIX,oEAAoE;AACpE,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,IAAI,CAAc;IAE3B,YAAY,IAAiB,EAAE,MAAe;QAC5C,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,SAAS,GAAG,0CAA0C,CAAC;AAE7D;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,KAAK,YAAY,eAAe;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC;IAC3D,IAAI,KAAK,YAAY,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,CAAC;QAC1F,OAAO,yBAAyB,CAAC;IACnC,OAAO,yBAAyB,CAAC;AACnC,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,eAAe,CAAC,IAAiB;IAC/C,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9B,CAAC"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,66 @@
1
+ import { type GoogleChatConfig } from "./config.js";
2
+ import { type Clock, type Sleep } from "./time.js";
3
+ import { type TokenStore } from "./token.js";
4
+ import { type Fetcher } from "./transport.js";
5
+ export declare const MAX_RETRIES = 2;
6
+ export declare const BASE_BACKOFF_MS = 500;
7
+ export declare const MAX_BACKOFF_MS = 8000;
8
+ export type QueryValue = string | number | boolean | undefined | null;
9
+ /** Build an encoded query string from primitive values only. */
10
+ export declare function buildQuery(params: Record<string, QueryValue>): string;
11
+ export interface Capabilities {
12
+ readonly mode: "oauth" | "webhook" | "none";
13
+ readonly configured: boolean;
14
+ readonly canRead: boolean;
15
+ readonly canSend: boolean;
16
+ readonly canEdit: boolean;
17
+ readonly canDelete: boolean;
18
+ }
19
+ export interface ChatRequest {
20
+ method: "GET" | "POST" | "PATCH" | "DELETE";
21
+ path: string;
22
+ query?: Record<string, QueryValue>;
23
+ body?: unknown;
24
+ signal: AbortSignal;
25
+ /** True only for idempotent reads, which may be retried on 429/5xx. */
26
+ idempotent?: boolean;
27
+ }
28
+ export interface SendMessageRequest {
29
+ space: string | undefined;
30
+ text: string;
31
+ threadKey: string | undefined;
32
+ replyOption: string | undefined;
33
+ messageId: string | undefined;
34
+ signal: AbortSignal;
35
+ }
36
+ export interface SendMessageResult {
37
+ message: unknown;
38
+ via: "oauth" | "webhook";
39
+ }
40
+ /**
41
+ * A Google Chat client with two independent, fail-closed authentication modes.
42
+ * Every request reaches one of two fixed official origins, follows no redirect,
43
+ * and is bounded in time and size.
44
+ */
45
+ export declare class GoogleChatClient {
46
+ readonly config: GoogleChatConfig;
47
+ readonly fetcher: Fetcher;
48
+ readonly clock: Clock;
49
+ readonly sleep: Sleep;
50
+ readonly tokenStore: TokenStore;
51
+ constructor(config: GoogleChatConfig, fetcher: Fetcher | undefined, clock: Clock | undefined, sleep: Sleep | undefined, tokenStore: TokenStore);
52
+ capabilities(): Capabilities;
53
+ hasUserToken(): boolean;
54
+ /** Prefer OAuth user identity when authenticated; otherwise use the webhook. */
55
+ resolveSendMode(): "oauth" | "webhook" | "none";
56
+ private requireUser;
57
+ /** Authenticated Chat API request in OAuth user mode. */
58
+ request(spec: ChatRequest): Promise<unknown>;
59
+ /** Send a message through OAuth user mode or the configured webhook. */
60
+ send(spec: SendMessageRequest): Promise<SendMessageResult>;
61
+ private sendViaWebhook;
62
+ private accessToken;
63
+ private backoffMs;
64
+ private handle;
65
+ }
66
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
package/dist/http.js ADDED
@@ -0,0 +1,208 @@
1
+ import { CHAT_API_ORIGIN, ENV } from "./config.js";
2
+ import { GoogleChatError } from "./errors.js";
3
+ import { systemClock, systemSleep } from "./time.js";
4
+ import { obtainAccessToken } from "./token.js";
5
+ import { MAX_RETRY_AFTER_SECONDS, rawRequest, retryAfterSeconds, } from "./transport.js";
6
+ export const MAX_RETRIES = 2;
7
+ export const BASE_BACKOFF_MS = 500;
8
+ export const MAX_BACKOFF_MS = 8_000;
9
+ /** Build an encoded query string from primitive values only. */
10
+ export function buildQuery(params) {
11
+ const parts = [];
12
+ for (const [key, value] of Object.entries(params)) {
13
+ if (value === undefined || value === null || value === "")
14
+ continue;
15
+ if (!/^[a-z0-9-]+$/i.test(key))
16
+ throw new GoogleChatError("invalid input", "invalid query key");
17
+ parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
18
+ }
19
+ return parts.length > 0 ? `?${parts.join("&")}` : "";
20
+ }
21
+ /**
22
+ * A Google Chat client with two independent, fail-closed authentication modes.
23
+ * Every request reaches one of two fixed official origins, follows no redirect,
24
+ * and is bounded in time and size.
25
+ */
26
+ export class GoogleChatClient {
27
+ config;
28
+ fetcher;
29
+ clock;
30
+ sleep;
31
+ tokenStore;
32
+ constructor(config, fetcher = fetch, clock = systemClock, sleep = systemSleep, tokenStore) {
33
+ this.config = config;
34
+ this.fetcher = fetcher;
35
+ this.clock = clock;
36
+ this.sleep = sleep;
37
+ this.tokenStore = tokenStore;
38
+ }
39
+ capabilities() {
40
+ const oauth = this.config.oauth !== undefined;
41
+ const webhook = this.config.webhook !== undefined;
42
+ return {
43
+ mode: oauth ? "oauth" : webhook ? "webhook" : "none",
44
+ configured: oauth || webhook,
45
+ canRead: oauth,
46
+ canSend: oauth || webhook,
47
+ canEdit: oauth,
48
+ canDelete: oauth,
49
+ };
50
+ }
51
+ hasUserToken() {
52
+ return this.tokenStore.read() !== null;
53
+ }
54
+ /** Prefer OAuth user identity when authenticated; otherwise use the webhook. */
55
+ resolveSendMode() {
56
+ if (this.config.oauth !== undefined && this.hasUserToken())
57
+ return "oauth";
58
+ if (this.config.webhook !== undefined)
59
+ return "webhook";
60
+ return "none";
61
+ }
62
+ requireUser(capability) {
63
+ if (this.config.oauth !== undefined)
64
+ return this.config.oauth;
65
+ const reason = this.config.webhook !== undefined
66
+ ? "an incoming webhook can only send messages; reading, searching, editing, and deleting require OAuth user mode"
67
+ : `set ${ENV.clientId} and ${ENV.clientSecret} (and run google-chat-auth) to enable it`;
68
+ throw new GoogleChatError("configuration missing or invalid", `${capability} requires OAuth user mode: ${reason}`);
69
+ }
70
+ /** Authenticated Chat API request in OAuth user mode. */
71
+ async request(spec) {
72
+ const oauth = this.requireUser("this operation");
73
+ let token = await this.accessToken(oauth, spec.signal, false);
74
+ let refreshed = false;
75
+ let attempt = 0;
76
+ for (;;) {
77
+ const url = `${CHAT_API_ORIGIN}${spec.path}${buildQuery(spec.query ?? {})}`;
78
+ const headers = {
79
+ accept: "application/json",
80
+ authorization: `${token.tokenType} ${token.accessToken}`,
81
+ };
82
+ if (spec.body !== undefined)
83
+ headers["content-type"] = "application/json";
84
+ const response = await rawRequest({
85
+ fetcher: this.fetcher,
86
+ method: spec.method,
87
+ url,
88
+ headers,
89
+ ...(spec.body === undefined ? {} : { body: JSON.stringify(spec.body) }),
90
+ signal: spec.signal,
91
+ origin: CHAT_API_ORIGIN,
92
+ });
93
+ if (response.status === 401 && !refreshed) {
94
+ refreshed = true;
95
+ token = await this.accessToken(oauth, spec.signal, true);
96
+ continue;
97
+ }
98
+ if ((response.status === 429 || response.status >= 500) &&
99
+ spec.idempotent === true &&
100
+ attempt < MAX_RETRIES) {
101
+ const delay = this.backoffMs(response.headers, attempt);
102
+ await this.sleep(delay, spec.signal);
103
+ attempt += 1;
104
+ continue;
105
+ }
106
+ return this.handle(response);
107
+ }
108
+ }
109
+ /** Send a message through OAuth user mode or the configured webhook. */
110
+ async send(spec) {
111
+ const mode = this.resolveSendMode();
112
+ if (mode === "none") {
113
+ if (this.config.oauth !== undefined)
114
+ throw new GoogleChatError("not authenticated", "no stored Google authorization; run google-chat-auth to authorize a user account");
115
+ throw new GoogleChatError("configuration missing or invalid", `set ${ENV.webhookUrl}, or ${ENV.clientId} and ${ENV.clientSecret}, to send messages`);
116
+ }
117
+ const body = { text: spec.text };
118
+ if (spec.threadKey !== undefined)
119
+ body.thread = { threadKey: spec.threadKey };
120
+ if (mode === "oauth") {
121
+ const space = spec.space;
122
+ if (space === undefined)
123
+ throw new GoogleChatError("invalid input", '"space" is required in OAuth user mode');
124
+ const query = {};
125
+ if (spec.replyOption !== undefined)
126
+ query.messageReplyOption = spec.replyOption;
127
+ if (spec.messageId !== undefined)
128
+ query.messageId = spec.messageId;
129
+ const message = await this.request({
130
+ method: "POST",
131
+ path: `/v1/${space}/messages`,
132
+ query,
133
+ body,
134
+ signal: spec.signal,
135
+ });
136
+ return { message, via: "oauth" };
137
+ }
138
+ const message = await this.sendViaWebhook(spec, body);
139
+ return { message, via: "webhook" };
140
+ }
141
+ async sendViaWebhook(spec, body) {
142
+ const webhook = this.config.webhook;
143
+ if (webhook === undefined)
144
+ throw new GoogleChatError("configuration missing or invalid", "no webhook is configured");
145
+ if (spec.space !== undefined && spec.space !== webhook.space)
146
+ throw new GoogleChatError("invalid input", `the configured webhook only posts to ${webhook.space}`);
147
+ if (spec.messageId !== undefined)
148
+ throw new GoogleChatError("invalid input", "client-assigned message ids require OAuth user mode");
149
+ const url = new URL(webhook.url);
150
+ if (spec.replyOption !== undefined)
151
+ url.searchParams.set("messageReplyOption", spec.replyOption);
152
+ const headers = {
153
+ accept: "application/json",
154
+ "content-type": "application/json; charset=utf-8",
155
+ };
156
+ const response = await rawRequest({
157
+ fetcher: this.fetcher,
158
+ method: "POST",
159
+ url: url.toString(),
160
+ headers,
161
+ body: JSON.stringify(body),
162
+ signal: spec.signal,
163
+ origin: CHAT_API_ORIGIN,
164
+ });
165
+ return this.handle(response);
166
+ }
167
+ async accessToken(oauth, signal, force) {
168
+ return obtainAccessToken(this.tokenStore, { oauth, fetcher: this.fetcher, clock: this.clock, signal, skewMs: this.config.tokenSkewMs }, { force });
169
+ }
170
+ backoffMs(headers, attempt) {
171
+ const retryAfter = retryAfterSeconds(headers, this.clock.now(), MAX_RETRY_AFTER_SECONDS);
172
+ const exponential = Math.min(BASE_BACKOFF_MS * 2 ** attempt, MAX_BACKOFF_MS);
173
+ if (retryAfter !== undefined)
174
+ return Math.min(retryAfter * 1000, MAX_BACKOFF_MS);
175
+ return exponential;
176
+ }
177
+ handle(response) {
178
+ const status = response.status;
179
+ if (status >= 200 && status < 300) {
180
+ if (status === 204 || status === 205)
181
+ return {};
182
+ if (response.text.trim() === "")
183
+ return {};
184
+ try {
185
+ return JSON.parse(response.text);
186
+ }
187
+ catch {
188
+ throw new GoogleChatError("invalid response");
189
+ }
190
+ }
191
+ const retry = retryAfterSeconds(response.headers, this.clock.now(), MAX_RETRY_AFTER_SECONDS);
192
+ const detail = retry === undefined ? undefined : `retry after ${retry} seconds`;
193
+ if (status === 401)
194
+ throw new GoogleChatError("not authenticated");
195
+ if (status === 403)
196
+ throw new GoogleChatError("not permitted", "Google Chat returns 403 for inaccessible resources before confirming whether they exist");
197
+ if (status === 404)
198
+ throw new GoogleChatError("not found");
199
+ if (status === 400)
200
+ throw new GoogleChatError("invalid input");
201
+ if (status === 429)
202
+ throw new GoogleChatError("rate limited", detail);
203
+ if (status >= 500)
204
+ throw new GoogleChatError("temporarily unavailable", detail);
205
+ throw new GoogleChatError("invalid response");
206
+ }
207
+ }
208
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import { type Plugin } from "@alisio/sdk";
2
+ import { type Environment } from "./config.js";
3
+ import { type Clock, type Sleep } from "./time.js";
4
+ import { TokenStore } from "./token.js";
5
+ import type { Fetcher } from "./transport.js";
6
+ export * from "./commands.js";
7
+ export * from "./config.js";
8
+ export * from "./errors.js";
9
+ export * from "./http.js";
10
+ export * from "./oauth.js";
11
+ export * from "./render.js";
12
+ export * from "./text.js";
13
+ export * from "./time.js";
14
+ export * from "./token.js";
15
+ export * from "./toolkit.js";
16
+ export * from "./tools.js";
17
+ export * from "./transport.js";
18
+ export * from "./validation.js";
19
+ export { VERSION } from "./version.js";
20
+ /** Package-local resource directories, registered relative to the built entry. */
21
+ export declare const resourcePaths: {
22
+ readonly skills: "../.agents/skills";
23
+ };
24
+ /** Every tool this plugin registers. Kept explicit so tests catch drift. */
25
+ export declare const TOOL_NAMES: readonly ["google_chat_list_spaces", "google_chat_list_messages", "google_chat_search_messages", "google_chat_list_memberships", "google_chat_send_message", "google_chat_edit_message", "google_chat_delete_message"];
26
+ /** Tools that mutate remote state and therefore declare the `write` effect. */
27
+ export declare const WRITE_TOOL_NAMES: readonly ["google_chat_send_message", "google_chat_edit_message", "google_chat_delete_message"];
28
+ export interface CreatePluginOptions {
29
+ /** Environment to read configuration from; defaults to `process.env`. */
30
+ env?: Environment;
31
+ /** Transport seam for tests and embedders; defaults to global `fetch`. */
32
+ fetcher?: Fetcher;
33
+ /** Clock seam used for token expiry and Retry-After dates. */
34
+ clock?: Clock;
35
+ /** Sleep seam used for bounded retry backoff. */
36
+ sleep?: Sleep;
37
+ /** Token store seam; defaults to `<configHome>/google-chat/token.json`. */
38
+ tokenStore?: TokenStore;
39
+ }
40
+ /**
41
+ * Build the plugin. Configuration is validated during `setup`. A partially
42
+ * configured OAuth mode fails closed immediately; with neither mode configured
43
+ * the plugin still loads so `google-chat:status` can explain what is missing,
44
+ * and every capability reports an actionable error.
45
+ */
46
+ export declare function createGoogleChatPlugin(options?: CreatePluginOptions): Plugin;
47
+ declare const _default: Plugin;
48
+ export default _default;
49
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAuC,MAAM,aAAa,CAAC;AAE7F,OAAO,EAAE,KAAK,WAAW,EAAgC,MAAM,aAAa,CAAC;AAE7E,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,KAAK,EAA4B,MAAM,WAAW,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAG9C,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,kFAAkF;AAClF,eAAO,MAAM,aAAa;aACxB,MAAM,EAAE,mBAAmB;CACnB,CAAC;AAEX,4EAA4E;AAC5E,eAAO,MAAM,UAAU,YACrB,yBAAyB,EACzB,2BAA2B,EAC3B,6BAA6B,EAC7B,8BAA8B,EAC9B,0BAA0B,EAC1B,0BAA0B,EAC1B,4BAA4B,CACpB,CAAC;AAEX,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,YAC3B,0BAA0B,EAC1B,0BAA0B,EAC1B,4BAA4B,CACpB,CAAC;AAEX,MAAM,WAAW,mBAAmB;IAClC,yEAAyE;IACzE,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,iDAAiD;IACjD,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,2EAA2E;IAC3E,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,mBAAwB,GAAG,MAAM,CA0BhF"}