paperclip-plugin-google-chat 0.1.2 → 0.1.3
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/config.d.ts +42 -9
- package/dist/config.js +25 -10
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/worker.js +42 -8
- package/package.json +1 -1
package/dist/config.d.ts
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Instance configuration for the Google Chat plugin.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Webhook URLs can be supplied two ways:
|
|
5
|
+
* - **Secret reference** (`*WebhookUrlRef`, a UUID) → resolved at runtime via
|
|
6
|
+
* `ctx.secrets.resolve(ref)`. The most secure option, BUT plugin secret-ref
|
|
7
|
+
* resolution is gated on the host: some Paperclip builds disable it ("Plugin
|
|
8
|
+
* secret references are disabled until company-scoped plugin config lands").
|
|
9
|
+
* - **Raw URL** (`*WebhookUrl`) → the incoming-webhook URL stored directly in
|
|
10
|
+
* config. Works on every build, but the URL (a credential) lives in the
|
|
11
|
+
* instance config in plaintext. The worker **prefers the raw URL** and falls
|
|
12
|
+
* back to the ref, so you can switch to refs once your host supports them.
|
|
9
13
|
*/
|
|
10
14
|
export interface GoogleChatConfig {
|
|
15
|
+
/** Raw Google Chat *incoming webhook* URL for the default space. */
|
|
16
|
+
defaultWebhookUrl?: string;
|
|
17
|
+
/** Optional per-category raw webhook URLs. */
|
|
18
|
+
approvalsWebhookUrl?: string;
|
|
19
|
+
errorsWebhookUrl?: string;
|
|
20
|
+
digestWebhookUrl?: string;
|
|
11
21
|
/** Secret ref → a Google Chat *incoming webhook* URL for the default space. */
|
|
12
22
|
defaultWebhookUrlRef?: string;
|
|
13
23
|
/** Optional per-category routing. Each is a secret ref to a space webhook URL. */
|
|
@@ -29,10 +39,12 @@ export interface GoogleChatConfig {
|
|
|
29
39
|
/** Master switch for inbound slash commands. */
|
|
30
40
|
enableCommands?: boolean;
|
|
31
41
|
/**
|
|
32
|
-
* Shared verification token
|
|
33
|
-
*
|
|
34
|
-
* secret ref
|
|
42
|
+
* Shared verification token Google Chat includes on each request; we compare it
|
|
43
|
+
* to reject forged webhook deliveries. Provide EITHER the raw token
|
|
44
|
+
* (`verificationToken`) or a secret ref (`verificationTokenRef`); the worker
|
|
45
|
+
* prefers the raw value. (For production, prefer the Google-signed bearer JWT.)
|
|
35
46
|
*/
|
|
47
|
+
verificationToken?: string;
|
|
36
48
|
verificationTokenRef?: string;
|
|
37
49
|
/** Allowlist of Google Chat space IDs permitted to issue commands. Empty = all. */
|
|
38
50
|
allowedSpaceIds?: string[];
|
|
@@ -47,10 +59,27 @@ export declare const DEFAULT_CONFIG: GoogleChatConfig;
|
|
|
47
59
|
export declare const INSTANCE_CONFIG_SCHEMA: {
|
|
48
60
|
readonly type: "object";
|
|
49
61
|
readonly properties: {
|
|
62
|
+
readonly defaultWebhookUrl: {
|
|
63
|
+
readonly type: "string";
|
|
64
|
+
readonly title: "Default space webhook URL";
|
|
65
|
+
readonly description: "Raw Google Chat incoming-webhook URL for the default space. Use this if your Paperclip build disables plugin secret references; otherwise prefer the secret-ref field below.";
|
|
66
|
+
};
|
|
67
|
+
readonly approvalsWebhookUrl: {
|
|
68
|
+
readonly type: "string";
|
|
69
|
+
readonly title: "Approvals space webhook URL";
|
|
70
|
+
};
|
|
71
|
+
readonly errorsWebhookUrl: {
|
|
72
|
+
readonly type: "string";
|
|
73
|
+
readonly title: "Errors space webhook URL";
|
|
74
|
+
};
|
|
75
|
+
readonly digestWebhookUrl: {
|
|
76
|
+
readonly type: "string";
|
|
77
|
+
readonly title: "Digest space webhook URL";
|
|
78
|
+
};
|
|
50
79
|
readonly defaultWebhookUrlRef: {
|
|
51
80
|
readonly type: "string";
|
|
52
81
|
readonly title: "Default space webhook (secret ref)";
|
|
53
|
-
readonly description: "Secret reference to a Google Chat incoming-webhook URL.
|
|
82
|
+
readonly description: "Secret reference (UUID) to a Google Chat incoming-webhook URL. Requires host support for plugin secret references; the raw URL field above takes precedence.";
|
|
54
83
|
};
|
|
55
84
|
readonly approvalsWebhookUrlRef: {
|
|
56
85
|
readonly type: "string";
|
|
@@ -99,6 +128,10 @@ export declare const INSTANCE_CONFIG_SCHEMA: {
|
|
|
99
128
|
readonly title: "Enable inbound slash commands";
|
|
100
129
|
readonly default: true;
|
|
101
130
|
};
|
|
131
|
+
readonly verificationToken: {
|
|
132
|
+
readonly type: "string";
|
|
133
|
+
readonly title: "Verification token (raw)";
|
|
134
|
+
};
|
|
102
135
|
readonly verificationTokenRef: {
|
|
103
136
|
readonly type: "string";
|
|
104
137
|
readonly title: "Verification token (secret ref)";
|
package/dist/config.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Instance configuration for the Google Chat plugin.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Webhook URLs can be supplied two ways:
|
|
5
|
+
* - **Secret reference** (`*WebhookUrlRef`, a UUID) → resolved at runtime via
|
|
6
|
+
* `ctx.secrets.resolve(ref)`. The most secure option, BUT plugin secret-ref
|
|
7
|
+
* resolution is gated on the host: some Paperclip builds disable it ("Plugin
|
|
8
|
+
* secret references are disabled until company-scoped plugin config lands").
|
|
9
|
+
* - **Raw URL** (`*WebhookUrl`) → the incoming-webhook URL stored directly in
|
|
10
|
+
* config. Works on every build, but the URL (a credential) lives in the
|
|
11
|
+
* instance config in plaintext. The worker **prefers the raw URL** and falls
|
|
12
|
+
* back to the ref, so you can switch to refs once your host supports them.
|
|
9
13
|
*/
|
|
10
14
|
export const DEFAULT_CONFIG = {
|
|
11
15
|
notifyOnIssueCreated: false,
|
|
@@ -23,10 +27,18 @@ export const DEFAULT_CONFIG = {
|
|
|
23
27
|
export const INSTANCE_CONFIG_SCHEMA = {
|
|
24
28
|
type: "object",
|
|
25
29
|
properties: {
|
|
30
|
+
defaultWebhookUrl: {
|
|
31
|
+
type: "string",
|
|
32
|
+
title: "Default space webhook URL",
|
|
33
|
+
description: "Raw Google Chat incoming-webhook URL for the default space. Use this if your Paperclip build disables plugin secret references; otherwise prefer the secret-ref field below.",
|
|
34
|
+
},
|
|
35
|
+
approvalsWebhookUrl: { type: "string", title: "Approvals space webhook URL" },
|
|
36
|
+
errorsWebhookUrl: { type: "string", title: "Errors space webhook URL" },
|
|
37
|
+
digestWebhookUrl: { type: "string", title: "Digest space webhook URL" },
|
|
26
38
|
defaultWebhookUrlRef: {
|
|
27
39
|
type: "string",
|
|
28
40
|
title: "Default space webhook (secret ref)",
|
|
29
|
-
description: "Secret reference to a Google Chat incoming-webhook URL.
|
|
41
|
+
description: "Secret reference (UUID) to a Google Chat incoming-webhook URL. Requires host support for plugin secret references; the raw URL field above takes precedence.",
|
|
30
42
|
},
|
|
31
43
|
approvalsWebhookUrlRef: { type: "string", title: "Approvals space webhook (secret ref)" },
|
|
32
44
|
errorsWebhookUrlRef: { type: "string", title: "Errors space webhook (secret ref)" },
|
|
@@ -42,6 +54,7 @@ export const INSTANCE_CONFIG_SCHEMA = {
|
|
|
42
54
|
notifyOnAgentRunFailed: { type: "boolean", title: "Notify on agent run failed", default: true },
|
|
43
55
|
useCards: { type: "boolean", title: "Use Cards v2 formatting", default: false },
|
|
44
56
|
enableCommands: { type: "boolean", title: "Enable inbound slash commands", default: true },
|
|
57
|
+
verificationToken: { type: "string", title: "Verification token (raw)" },
|
|
45
58
|
verificationTokenRef: { type: "string", title: "Verification token (secret ref)" },
|
|
46
59
|
allowedSpaceIds: { type: "array", title: "Allowed space IDs", items: { type: "string" } },
|
|
47
60
|
allowedUserEmails: { type: "array", title: "Allowed user emails", items: { type: "string" } },
|
|
@@ -56,15 +69,17 @@ export function validateConfig(config) {
|
|
|
56
69
|
if (config.dailyDigestTime && !/^([01]\d|2[0-3]):[0-5]\d$/.test(config.dailyDigestTime)) {
|
|
57
70
|
errors.push("dailyDigestTime must be 24h HH:MM, e.g. 08:00");
|
|
58
71
|
}
|
|
59
|
-
|
|
60
|
-
|
|
72
|
+
const hasDefault = Boolean(config.defaultWebhookUrl || config.defaultWebhookUrlRef);
|
|
73
|
+
const hasDigest = Boolean(config.digestWebhookUrl || config.digestWebhookUrlRef);
|
|
74
|
+
if (config.digestMode && !hasDigest && !hasDefault) {
|
|
75
|
+
errors.push("digestMode is on but no digest/default webhook (URL or secret ref) is set");
|
|
61
76
|
}
|
|
62
77
|
const anyNotify = config.notifyOnIssueCreated ||
|
|
63
78
|
config.notifyOnIssueCompleted ||
|
|
64
79
|
config.notifyOnApprovalRequested ||
|
|
65
80
|
config.notifyOnAgentRunFailed;
|
|
66
|
-
if (anyNotify && !
|
|
67
|
-
warnings.push("Notifications are enabled but no
|
|
81
|
+
if (anyNotify && !hasDefault) {
|
|
82
|
+
warnings.push("Notifications are enabled but no default webhook (URL or secret ref) is set — category routing only.");
|
|
68
83
|
}
|
|
69
84
|
if (config.serviceAccountKeyRef === "") {
|
|
70
85
|
warnings.push("serviceAccountKeyRef is an empty string; leave it unset for webhook-only mode.");
|
package/dist/constants.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* the same convention).
|
|
5
5
|
*/
|
|
6
6
|
export declare const PLUGIN_ID = "google-chat";
|
|
7
|
-
export declare const PLUGIN_VERSION = "0.1.
|
|
7
|
+
export declare const PLUGIN_VERSION = "0.1.3";
|
|
8
8
|
/** Webhook endpoint keys declared in the manifest and matched in `onWebhook`. */
|
|
9
9
|
export declare const WEBHOOK_KEYS: {
|
|
10
10
|
/** Google Chat app events (MESSAGE, ADDED_TO_SPACE, CARD_CLICKED, …) POST here. */
|
package/dist/constants.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* the same convention).
|
|
5
5
|
*/
|
|
6
6
|
export const PLUGIN_ID = "google-chat";
|
|
7
|
-
export const PLUGIN_VERSION = "0.1.
|
|
7
|
+
export const PLUGIN_VERSION = "0.1.3";
|
|
8
8
|
/** Webhook endpoint keys declared in the manifest and matched in `onWebhook`. */
|
|
9
9
|
export const WEBHOOK_KEYS = {
|
|
10
10
|
/** Google Chat app events (MESSAGE, ADDED_TO_SPACE, CARD_CLICKED, …) POST here. */
|
package/dist/worker.js
CHANGED
|
@@ -22,7 +22,20 @@ async function getConfig(ctx) {
|
|
|
22
22
|
const raw = (await ctx.config.get());
|
|
23
23
|
return { ...DEFAULT_CONFIG, ...raw };
|
|
24
24
|
}
|
|
25
|
-
/**
|
|
25
|
+
/** Raw webhook URL for a route, falling back to the default. */
|
|
26
|
+
function rawUrlForRoute(config, route) {
|
|
27
|
+
switch (route) {
|
|
28
|
+
case "approvals":
|
|
29
|
+
return config.approvalsWebhookUrl ?? config.defaultWebhookUrl;
|
|
30
|
+
case "errors":
|
|
31
|
+
return config.errorsWebhookUrl ?? config.defaultWebhookUrl;
|
|
32
|
+
case "digest":
|
|
33
|
+
return config.digestWebhookUrl ?? config.defaultWebhookUrl;
|
|
34
|
+
default:
|
|
35
|
+
return config.defaultWebhookUrl;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** Secret ref for a route, falling back to the default. */
|
|
26
39
|
function refForRoute(config, route) {
|
|
27
40
|
switch (route) {
|
|
28
41
|
case "approvals":
|
|
@@ -35,15 +48,36 @@ function refForRoute(config, route) {
|
|
|
35
48
|
return config.defaultWebhookUrlRef;
|
|
36
49
|
}
|
|
37
50
|
}
|
|
38
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* Resolve a route's webhook URL — preferring a raw URL (works on every host),
|
|
53
|
+
* falling back to a secret ref (only where the host enables plugin secret-ref
|
|
54
|
+
* resolution; some builds disable it). Returns null if neither yields a URL.
|
|
55
|
+
*/
|
|
56
|
+
async function resolveWebhookUrl(ctx, config, route) {
|
|
57
|
+
const raw = rawUrlForRoute(config, route);
|
|
58
|
+
if (raw)
|
|
59
|
+
return raw;
|
|
60
|
+
const ref = refForRoute(config, route);
|
|
61
|
+
if (!ref)
|
|
62
|
+
return null;
|
|
63
|
+
try {
|
|
64
|
+
return await ctx.secrets.resolve(ref);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
68
|
+
ctx.logger?.warn?.(`Could not resolve secret ref for route "${route}" (${msg}). ` +
|
|
69
|
+
`If your Paperclip build disables plugin secret references, set the raw "${route}WebhookUrl" field instead.`);
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Resolve a route's webhook URL and POST a message. */
|
|
39
74
|
async function post(ctx, route, message) {
|
|
40
75
|
const config = await getConfig(ctx);
|
|
41
|
-
const
|
|
42
|
-
if (!
|
|
43
|
-
ctx.logger?.warn?.(`No webhook
|
|
76
|
+
const url = await resolveWebhookUrl(ctx, config, route);
|
|
77
|
+
if (!url) {
|
|
78
|
+
ctx.logger?.warn?.(`No webhook URL available for route "${route}"`);
|
|
44
79
|
return { ok: false, status: 0, body: `no webhook configured for route ${route}` };
|
|
45
80
|
}
|
|
46
|
-
const url = await ctx.secrets.resolve(ref);
|
|
47
81
|
return postToWebhook((u, init) => ctx.http.fetch(u, init), url, message);
|
|
48
82
|
}
|
|
49
83
|
/** Build the inbound command dependencies against the SDK domain APIs. */
|
|
@@ -117,10 +151,10 @@ const plugin = definePlugin({
|
|
|
117
151
|
async onHealth() {
|
|
118
152
|
const ctx = currentCtx;
|
|
119
153
|
const config = ctx ? await getConfig(ctx) : DEFAULT_CONFIG;
|
|
120
|
-
const configured = Boolean(config.defaultWebhookUrlRef);
|
|
154
|
+
const configured = Boolean(config.defaultWebhookUrl || config.defaultWebhookUrlRef);
|
|
121
155
|
return {
|
|
122
156
|
status: configured ? "ok" : "degraded",
|
|
123
|
-
message: configured ? "google-chat plugin ready" : "no
|
|
157
|
+
message: configured ? "google-chat plugin ready" : "no default webhook URL or secret ref configured",
|
|
124
158
|
details: { commandsEnabled: config.enableCommands !== false, digestMode: config.digestMode === true },
|
|
125
159
|
};
|
|
126
160
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paperclip-plugin-google-chat",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Bidirectional Google Chat integration for Paperclip — post agent/issue notifications to spaces and drive Paperclip from Chat slash commands.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|