@openclaw/googlechat 2026.5.2-beta.1 → 2026.5.3-beta.1
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/api-C67jv0ow.js +627 -0
- package/dist/api.js +3 -0
- package/dist/channel-DE2hroMn.js +682 -0
- package/dist/channel-config-api.js +5 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-CoSLc-4R.js +920 -0
- package/dist/contract-api.js +3 -0
- package/dist/doctor-contract-CG1sLToP.js +151 -0
- package/dist/doctor-contract-api.js +2 -0
- package/dist/index.js +22 -0
- package/dist/runtime-api-wkIdfwqY.js +30 -0
- package/dist/runtime-api.js +2 -0
- package/dist/secret-contract-DtQ_IO7J.js +99 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/setup-entry.js +15 -0
- package/dist/setup-plugin-api.js +75 -0
- package/dist/setup-surface-CofP-Gg9.js +309 -0
- package/dist/test-api.js +3 -0
- package/package.json +14 -6
- package/api.ts +0 -3
- package/channel-config-api.ts +0 -1
- package/channel-plugin-api.ts +0 -1
- package/contract-api.ts +0 -5
- package/doctor-contract-api.ts +0 -1
- package/index.ts +0 -20
- package/runtime-api.ts +0 -60
- package/secret-contract-api.ts +0 -5
- package/setup-entry.ts +0 -13
- package/setup-plugin-api.ts +0 -3
- package/src/accounts.ts +0 -169
- package/src/actions.test.ts +0 -265
- package/src/actions.ts +0 -227
- package/src/api.ts +0 -322
- package/src/approval-auth.test.ts +0 -24
- package/src/approval-auth.ts +0 -32
- package/src/auth.ts +0 -209
- package/src/channel-config.test.ts +0 -39
- package/src/channel.adapters.ts +0 -293
- package/src/channel.deps.runtime.ts +0 -28
- package/src/channel.runtime.ts +0 -17
- package/src/channel.setup.ts +0 -98
- package/src/channel.test.ts +0 -695
- package/src/channel.ts +0 -256
- package/src/config-schema.test.ts +0 -16
- package/src/config-schema.ts +0 -3
- package/src/doctor-contract.test.ts +0 -75
- package/src/doctor-contract.ts +0 -182
- package/src/doctor.ts +0 -57
- package/src/gateway.ts +0 -63
- package/src/google-auth.runtime.test.ts +0 -462
- package/src/google-auth.runtime.ts +0 -539
- package/src/group-policy.ts +0 -17
- package/src/monitor-access.test.ts +0 -443
- package/src/monitor-access.ts +0 -405
- package/src/monitor-reply-delivery.ts +0 -156
- package/src/monitor-routing.ts +0 -55
- package/src/monitor-types.ts +0 -33
- package/src/monitor-webhook.test.ts +0 -446
- package/src/monitor-webhook.ts +0 -285
- package/src/monitor.reply-delivery.test.ts +0 -139
- package/src/monitor.ts +0 -428
- package/src/monitor.webhook-routing.test.ts +0 -252
- package/src/runtime.ts +0 -9
- package/src/secret-contract.test.ts +0 -60
- package/src/secret-contract.ts +0 -161
- package/src/sender-allow.ts +0 -46
- package/src/setup-core.ts +0 -40
- package/src/setup-surface.ts +0 -236
- package/src/setup.test.ts +0 -560
- package/src/targets.test.ts +0 -421
- package/src/targets.ts +0 -66
- package/src/types.config.ts +0 -3
- package/src/types.ts +0 -73
- package/test-api.ts +0 -2
- package/tsconfig.json +0 -16
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-types";
|
|
2
|
-
import {
|
|
3
|
-
applyResolvedAssignments,
|
|
4
|
-
createResolverContext,
|
|
5
|
-
resolveSecretRefValues,
|
|
6
|
-
} from "openclaw/plugin-sdk/runtime-secret-resolution";
|
|
7
|
-
import { describe, expect, it } from "vitest";
|
|
8
|
-
import { collectRuntimeConfigAssignments } from "./secret-contract.js";
|
|
9
|
-
|
|
10
|
-
describe("googlechat secret contract", () => {
|
|
11
|
-
it("resolves account serviceAccount SecretRefs for enabled accounts", async () => {
|
|
12
|
-
const sourceConfig = {
|
|
13
|
-
channels: {
|
|
14
|
-
googlechat: {
|
|
15
|
-
enabled: true,
|
|
16
|
-
accounts: {
|
|
17
|
-
work: {
|
|
18
|
-
enabled: true,
|
|
19
|
-
serviceAccountRef: {
|
|
20
|
-
source: "env",
|
|
21
|
-
provider: "default",
|
|
22
|
-
id: "GOOGLECHAT_SERVICE_ACCOUNT",
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
} satisfies OpenClawConfig;
|
|
29
|
-
const resolvedConfig: OpenClawConfig = structuredClone(sourceConfig);
|
|
30
|
-
const context = createResolverContext({
|
|
31
|
-
sourceConfig,
|
|
32
|
-
env: {
|
|
33
|
-
GOOGLECHAT_SERVICE_ACCOUNT: '{"client_email":"bot@example.com"}',
|
|
34
|
-
},
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
collectRuntimeConfigAssignments({
|
|
38
|
-
config: resolvedConfig,
|
|
39
|
-
defaults: undefined,
|
|
40
|
-
context,
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
const resolved = await resolveSecretRefValues(
|
|
44
|
-
context.assignments.map((assignment) => assignment.ref),
|
|
45
|
-
{
|
|
46
|
-
config: sourceConfig,
|
|
47
|
-
env: context.env,
|
|
48
|
-
cache: context.cache,
|
|
49
|
-
},
|
|
50
|
-
);
|
|
51
|
-
applyResolvedAssignments({
|
|
52
|
-
assignments: context.assignments,
|
|
53
|
-
resolved,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const workAccount = resolvedConfig.channels?.googlechat?.accounts?.work;
|
|
57
|
-
expect(workAccount?.serviceAccount).toBe('{"client_email":"bot@example.com"}');
|
|
58
|
-
expect(context.warnings).toEqual([]);
|
|
59
|
-
});
|
|
60
|
-
});
|
package/src/secret-contract.ts
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getChannelSurface,
|
|
3
|
-
hasOwnProperty,
|
|
4
|
-
pushAssignment,
|
|
5
|
-
pushInactiveSurfaceWarning,
|
|
6
|
-
pushWarning,
|
|
7
|
-
resolveChannelAccountSurface,
|
|
8
|
-
type ResolverContext,
|
|
9
|
-
type SecretDefaults,
|
|
10
|
-
} from "openclaw/plugin-sdk/channel-secret-basic-runtime";
|
|
11
|
-
import { coerceSecretRef } from "openclaw/plugin-sdk/secret-ref-runtime";
|
|
12
|
-
|
|
13
|
-
type GoogleChatAccountLike = {
|
|
14
|
-
serviceAccount?: unknown;
|
|
15
|
-
serviceAccountRef?: unknown;
|
|
16
|
-
accounts?: Record<string, unknown>;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export const secretTargetRegistryEntries: import("openclaw/plugin-sdk/channel-secret-basic-runtime").SecretTargetRegistryEntry[] =
|
|
20
|
-
[
|
|
21
|
-
{
|
|
22
|
-
id: "channels.googlechat.accounts.*.serviceAccount",
|
|
23
|
-
targetType: "channels.googlechat.serviceAccount",
|
|
24
|
-
targetTypeAliases: ["channels.googlechat.accounts.*.serviceAccount"],
|
|
25
|
-
configFile: "openclaw.json",
|
|
26
|
-
pathPattern: "channels.googlechat.accounts.*.serviceAccount",
|
|
27
|
-
refPathPattern: "channels.googlechat.accounts.*.serviceAccountRef",
|
|
28
|
-
secretShape: "sibling_ref",
|
|
29
|
-
expectedResolvedValue: "string-or-object",
|
|
30
|
-
includeInPlan: true,
|
|
31
|
-
includeInConfigure: true,
|
|
32
|
-
includeInAudit: true,
|
|
33
|
-
accountIdPathSegmentIndex: 3,
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
id: "channels.googlechat.serviceAccount",
|
|
37
|
-
targetType: "channels.googlechat.serviceAccount",
|
|
38
|
-
configFile: "openclaw.json",
|
|
39
|
-
pathPattern: "channels.googlechat.serviceAccount",
|
|
40
|
-
refPathPattern: "channels.googlechat.serviceAccountRef",
|
|
41
|
-
secretShape: "sibling_ref",
|
|
42
|
-
expectedResolvedValue: "string-or-object",
|
|
43
|
-
includeInPlan: true,
|
|
44
|
-
includeInConfigure: true,
|
|
45
|
-
includeInAudit: true,
|
|
46
|
-
},
|
|
47
|
-
];
|
|
48
|
-
|
|
49
|
-
function resolveSecretInputRef(params: {
|
|
50
|
-
value: unknown;
|
|
51
|
-
refValue?: unknown;
|
|
52
|
-
defaults?: SecretDefaults;
|
|
53
|
-
}) {
|
|
54
|
-
const explicitRef = coerceSecretRef(params.refValue, params.defaults);
|
|
55
|
-
const inlineRef = explicitRef ? null : coerceSecretRef(params.value, params.defaults);
|
|
56
|
-
return {
|
|
57
|
-
explicitRef,
|
|
58
|
-
inlineRef,
|
|
59
|
-
ref: explicitRef ?? inlineRef,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function collectGoogleChatAccountAssignment(params: {
|
|
64
|
-
target: GoogleChatAccountLike;
|
|
65
|
-
path: string;
|
|
66
|
-
defaults?: SecretDefaults;
|
|
67
|
-
context: ResolverContext;
|
|
68
|
-
active?: boolean;
|
|
69
|
-
inactiveReason?: string;
|
|
70
|
-
}): void {
|
|
71
|
-
const { explicitRef, ref } = resolveSecretInputRef({
|
|
72
|
-
value: params.target.serviceAccount,
|
|
73
|
-
refValue: params.target.serviceAccountRef,
|
|
74
|
-
defaults: params.defaults,
|
|
75
|
-
});
|
|
76
|
-
if (!ref) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
if (params.active === false) {
|
|
80
|
-
pushInactiveSurfaceWarning({
|
|
81
|
-
context: params.context,
|
|
82
|
-
path: `${params.path}.serviceAccount`,
|
|
83
|
-
details: params.inactiveReason,
|
|
84
|
-
});
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
if (
|
|
88
|
-
explicitRef &&
|
|
89
|
-
params.target.serviceAccount !== undefined &&
|
|
90
|
-
!coerceSecretRef(params.target.serviceAccount, params.defaults)
|
|
91
|
-
) {
|
|
92
|
-
pushWarning(params.context, {
|
|
93
|
-
code: "SECRETS_REF_OVERRIDES_PLAINTEXT",
|
|
94
|
-
path: params.path,
|
|
95
|
-
message: `${params.path}: serviceAccountRef is set; runtime will ignore plaintext serviceAccount.`,
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
pushAssignment(params.context, {
|
|
99
|
-
ref,
|
|
100
|
-
path: `${params.path}.serviceAccount`,
|
|
101
|
-
expected: "string-or-object",
|
|
102
|
-
apply: (value) => {
|
|
103
|
-
params.target.serviceAccount = value;
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export function collectRuntimeConfigAssignments(params: {
|
|
109
|
-
config: { channels?: Record<string, unknown> };
|
|
110
|
-
defaults?: SecretDefaults;
|
|
111
|
-
context: ResolverContext;
|
|
112
|
-
}): void {
|
|
113
|
-
const resolved = getChannelSurface(params.config, "googlechat");
|
|
114
|
-
if (!resolved) {
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
const googleChat = resolved.channel as GoogleChatAccountLike;
|
|
118
|
-
const surface = resolveChannelAccountSurface(googleChat as Record<string, unknown>);
|
|
119
|
-
const topLevelServiceAccountActive = !surface.channelEnabled
|
|
120
|
-
? false
|
|
121
|
-
: !surface.hasExplicitAccounts
|
|
122
|
-
? true
|
|
123
|
-
: surface.accounts.some(
|
|
124
|
-
({ account, enabled }) =>
|
|
125
|
-
enabled &&
|
|
126
|
-
!hasOwnProperty(account, "serviceAccount") &&
|
|
127
|
-
!hasOwnProperty(account, "serviceAccountRef"),
|
|
128
|
-
);
|
|
129
|
-
collectGoogleChatAccountAssignment({
|
|
130
|
-
target: googleChat,
|
|
131
|
-
path: "channels.googlechat",
|
|
132
|
-
defaults: params.defaults,
|
|
133
|
-
context: params.context,
|
|
134
|
-
active: topLevelServiceAccountActive,
|
|
135
|
-
inactiveReason: "no enabled account inherits this top-level Google Chat serviceAccount.",
|
|
136
|
-
});
|
|
137
|
-
if (!surface.hasExplicitAccounts) {
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
for (const { accountId, account, enabled } of surface.accounts) {
|
|
141
|
-
if (
|
|
142
|
-
!hasOwnProperty(account, "serviceAccount") &&
|
|
143
|
-
!hasOwnProperty(account, "serviceAccountRef")
|
|
144
|
-
) {
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
collectGoogleChatAccountAssignment({
|
|
148
|
-
target: account as GoogleChatAccountLike,
|
|
149
|
-
path: `channels.googlechat.accounts.${accountId}`,
|
|
150
|
-
defaults: params.defaults,
|
|
151
|
-
context: params.context,
|
|
152
|
-
active: enabled,
|
|
153
|
-
inactiveReason: "Google Chat account is disabled.",
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export const channelSecrets = {
|
|
159
|
-
secretTargetRegistryEntries,
|
|
160
|
-
collectRuntimeConfigAssignments,
|
|
161
|
-
};
|
package/src/sender-allow.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
|
2
|
-
|
|
3
|
-
function normalizeUserId(raw?: string | null): string {
|
|
4
|
-
const trimmed = typeof raw === "string" ? raw.trim() : "";
|
|
5
|
-
if (!trimmed) {
|
|
6
|
-
return "";
|
|
7
|
-
}
|
|
8
|
-
return normalizeLowercaseStringOrEmpty(trimmed.replace(/^users\//i, ""));
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function isEmailLike(value: string): boolean {
|
|
12
|
-
// Keep this intentionally loose; allowlists are user-provided config.
|
|
13
|
-
return value.includes("@");
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function isSenderAllowed(
|
|
17
|
-
senderId: string,
|
|
18
|
-
senderEmail: string | undefined,
|
|
19
|
-
allowFrom: string[],
|
|
20
|
-
allowNameMatching = false,
|
|
21
|
-
) {
|
|
22
|
-
if (allowFrom.includes("*")) {
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
const normalizedSenderId = normalizeUserId(senderId);
|
|
26
|
-
const normalizedEmail = normalizeLowercaseStringOrEmpty(senderEmail ?? "");
|
|
27
|
-
return allowFrom.some((entry) => {
|
|
28
|
-
const normalized = normalizeLowercaseStringOrEmpty(entry);
|
|
29
|
-
if (!normalized) {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Accept `googlechat:<id>` but treat `users/...` as an *ID* only (deprecated `users/<email>`).
|
|
34
|
-
const withoutPrefix = normalized.replace(/^(googlechat|google-chat|gchat):/i, "");
|
|
35
|
-
if (withoutPrefix.startsWith("users/")) {
|
|
36
|
-
return normalizeUserId(withoutPrefix) === normalizedSenderId;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Raw email allowlist entries are a break-glass override.
|
|
40
|
-
if (allowNameMatching && normalizedEmail && isEmailLike(withoutPrefix)) {
|
|
41
|
-
return withoutPrefix === normalizedEmail;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return withoutPrefix.replace(/^users\//i, "") === normalizedSenderId;
|
|
45
|
-
});
|
|
46
|
-
}
|
package/src/setup-core.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createPatchedAccountSetupAdapter,
|
|
3
|
-
createSetupInputPresenceValidator,
|
|
4
|
-
} from "openclaw/plugin-sdk/setup-runtime";
|
|
5
|
-
|
|
6
|
-
const channel = "googlechat" as const;
|
|
7
|
-
|
|
8
|
-
export const googlechatSetupAdapter = createPatchedAccountSetupAdapter({
|
|
9
|
-
channelKey: channel,
|
|
10
|
-
validateInput: createSetupInputPresenceValidator({
|
|
11
|
-
defaultAccountOnlyEnvError:
|
|
12
|
-
"GOOGLE_CHAT_SERVICE_ACCOUNT env vars can only be used for the default account.",
|
|
13
|
-
whenNotUseEnv: [
|
|
14
|
-
{
|
|
15
|
-
someOf: ["token", "tokenFile"],
|
|
16
|
-
message: "Google Chat requires --token (service account JSON) or --token-file.",
|
|
17
|
-
},
|
|
18
|
-
],
|
|
19
|
-
}),
|
|
20
|
-
buildPatch: (input) => {
|
|
21
|
-
const patch = input.useEnv
|
|
22
|
-
? {}
|
|
23
|
-
: input.tokenFile
|
|
24
|
-
? { serviceAccountFile: input.tokenFile }
|
|
25
|
-
: input.token
|
|
26
|
-
? { serviceAccount: input.token }
|
|
27
|
-
: {};
|
|
28
|
-
const audienceType = input.audienceType?.trim();
|
|
29
|
-
const audience = input.audience?.trim();
|
|
30
|
-
const webhookPath = input.webhookPath?.trim();
|
|
31
|
-
const webhookUrl = input.webhookUrl?.trim();
|
|
32
|
-
return {
|
|
33
|
-
...patch,
|
|
34
|
-
...(audienceType ? { audienceType } : {}),
|
|
35
|
-
...(audience ? { audience } : {}),
|
|
36
|
-
...(webhookPath ? { webhookPath } : {}),
|
|
37
|
-
...(webhookUrl ? { webhookUrl } : {}),
|
|
38
|
-
};
|
|
39
|
-
},
|
|
40
|
-
});
|
package/src/setup-surface.ts
DELETED
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
addWildcardAllowFrom,
|
|
3
|
-
applySetupAccountConfigPatch,
|
|
4
|
-
createPromptParsedAllowFromForAccount,
|
|
5
|
-
createStandardChannelSetupStatus,
|
|
6
|
-
DEFAULT_ACCOUNT_ID,
|
|
7
|
-
formatDocsLink,
|
|
8
|
-
mergeAllowFromEntries,
|
|
9
|
-
migrateBaseNameToDefaultAccount,
|
|
10
|
-
splitSetupEntries,
|
|
11
|
-
type ChannelSetupDmPolicy,
|
|
12
|
-
type ChannelSetupWizard,
|
|
13
|
-
} from "openclaw/plugin-sdk/setup";
|
|
14
|
-
import {
|
|
15
|
-
normalizeOptionalString,
|
|
16
|
-
normalizeStringifiedOptionalString,
|
|
17
|
-
} from "openclaw/plugin-sdk/text-runtime";
|
|
18
|
-
import { resolveDefaultGoogleChatAccountId, resolveGoogleChatAccount } from "./accounts.js";
|
|
19
|
-
|
|
20
|
-
const channel = "googlechat" as const;
|
|
21
|
-
const ENV_SERVICE_ACCOUNT = "GOOGLE_CHAT_SERVICE_ACCOUNT";
|
|
22
|
-
const ENV_SERVICE_ACCOUNT_FILE = "GOOGLE_CHAT_SERVICE_ACCOUNT_FILE";
|
|
23
|
-
const USE_ENV_FLAG = "__googlechatUseEnv";
|
|
24
|
-
const AUTH_METHOD_FLAG = "__googlechatAuthMethod";
|
|
25
|
-
|
|
26
|
-
type GoogleChatTextInput = NonNullable<ChannelSetupWizard["textInputs"]>[number];
|
|
27
|
-
type GoogleChatTextInputKey = GoogleChatTextInput["inputKey"];
|
|
28
|
-
|
|
29
|
-
const promptAllowFrom = createPromptParsedAllowFromForAccount({
|
|
30
|
-
defaultAccountId: resolveDefaultGoogleChatAccountId,
|
|
31
|
-
message: "Google Chat allowFrom (users/<id> or raw email; avoid users/<email>)",
|
|
32
|
-
placeholder: "users/123456789, name@example.com",
|
|
33
|
-
parseEntries: (raw) => ({
|
|
34
|
-
entries: mergeAllowFromEntries(undefined, splitSetupEntries(raw)),
|
|
35
|
-
}),
|
|
36
|
-
getExistingAllowFrom: ({ cfg, accountId }) =>
|
|
37
|
-
resolveGoogleChatAccount({ cfg, accountId }).config.dm?.allowFrom ?? [],
|
|
38
|
-
applyAllowFrom: ({ cfg, accountId, allowFrom }) =>
|
|
39
|
-
applySetupAccountConfigPatch({
|
|
40
|
-
cfg,
|
|
41
|
-
channelKey: channel,
|
|
42
|
-
accountId,
|
|
43
|
-
patch: {
|
|
44
|
-
dm: {
|
|
45
|
-
...resolveGoogleChatAccount({ cfg, accountId }).config.dm,
|
|
46
|
-
allowFrom,
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
}),
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const googlechatDmPolicy: ChannelSetupDmPolicy = {
|
|
53
|
-
label: "Google Chat",
|
|
54
|
-
channel,
|
|
55
|
-
policyKey: "channels.googlechat.dm.policy",
|
|
56
|
-
allowFromKey: "channels.googlechat.dm.allowFrom",
|
|
57
|
-
resolveConfigKeys: (cfg, accountId) =>
|
|
58
|
-
(accountId ?? resolveDefaultGoogleChatAccountId(cfg)) !== DEFAULT_ACCOUNT_ID
|
|
59
|
-
? {
|
|
60
|
-
policyKey: `channels.googlechat.accounts.${accountId ?? resolveDefaultGoogleChatAccountId(cfg)}.dm.policy`,
|
|
61
|
-
allowFromKey: `channels.googlechat.accounts.${accountId ?? resolveDefaultGoogleChatAccountId(cfg)}.dm.allowFrom`,
|
|
62
|
-
}
|
|
63
|
-
: {
|
|
64
|
-
policyKey: "channels.googlechat.dm.policy",
|
|
65
|
-
allowFromKey: "channels.googlechat.dm.allowFrom",
|
|
66
|
-
},
|
|
67
|
-
getCurrent: (cfg, accountId) =>
|
|
68
|
-
resolveGoogleChatAccount({
|
|
69
|
-
cfg,
|
|
70
|
-
accountId: accountId ?? resolveDefaultGoogleChatAccountId(cfg),
|
|
71
|
-
}).config.dm?.policy ?? "pairing",
|
|
72
|
-
setPolicy: (cfg, policy, accountId) => {
|
|
73
|
-
const resolvedAccountId = accountId ?? resolveDefaultGoogleChatAccountId(cfg);
|
|
74
|
-
const currentDm = resolveGoogleChatAccount({
|
|
75
|
-
cfg,
|
|
76
|
-
accountId: resolvedAccountId,
|
|
77
|
-
}).config.dm;
|
|
78
|
-
return applySetupAccountConfigPatch({
|
|
79
|
-
cfg,
|
|
80
|
-
channelKey: channel,
|
|
81
|
-
accountId: resolvedAccountId,
|
|
82
|
-
patch: {
|
|
83
|
-
dm: {
|
|
84
|
-
...currentDm,
|
|
85
|
-
policy,
|
|
86
|
-
...(policy === "open" ? { allowFrom: addWildcardAllowFrom(currentDm?.allowFrom) } : {}),
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
|
-
},
|
|
91
|
-
promptAllowFrom,
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
function createServiceAccountTextInput(params: {
|
|
95
|
-
inputKey: GoogleChatTextInputKey;
|
|
96
|
-
message: string;
|
|
97
|
-
placeholder: string;
|
|
98
|
-
authMethod: "file" | "inline";
|
|
99
|
-
patchKey: "serviceAccountFile" | "serviceAccount";
|
|
100
|
-
}): GoogleChatTextInput {
|
|
101
|
-
return {
|
|
102
|
-
inputKey: params.inputKey,
|
|
103
|
-
message: params.message,
|
|
104
|
-
placeholder: params.placeholder,
|
|
105
|
-
shouldPrompt: ({ credentialValues }) =>
|
|
106
|
-
credentialValues[USE_ENV_FLAG] !== "1" &&
|
|
107
|
-
credentialValues[AUTH_METHOD_FLAG] === params.authMethod,
|
|
108
|
-
validate: ({ value }) => (normalizeStringifiedOptionalString(value) ? undefined : "Required"),
|
|
109
|
-
normalizeValue: ({ value }) => normalizeStringifiedOptionalString(value) ?? "",
|
|
110
|
-
applySet: async ({ cfg, accountId, value }) =>
|
|
111
|
-
applySetupAccountConfigPatch({
|
|
112
|
-
cfg,
|
|
113
|
-
channelKey: channel,
|
|
114
|
-
accountId,
|
|
115
|
-
patch: { [params.patchKey]: value },
|
|
116
|
-
}),
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export const googlechatSetupWizard: ChannelSetupWizard = {
|
|
121
|
-
channel,
|
|
122
|
-
status: createStandardChannelSetupStatus({
|
|
123
|
-
channelLabel: "Google Chat",
|
|
124
|
-
configuredLabel: "configured",
|
|
125
|
-
unconfiguredLabel: "needs service account",
|
|
126
|
-
configuredHint: "configured",
|
|
127
|
-
unconfiguredHint: "needs auth",
|
|
128
|
-
includeStatusLine: true,
|
|
129
|
-
resolveConfigured: ({ cfg, accountId }) =>
|
|
130
|
-
resolveGoogleChatAccount({ cfg, accountId }).credentialSource !== "none",
|
|
131
|
-
}),
|
|
132
|
-
introNote: {
|
|
133
|
-
title: "Google Chat setup",
|
|
134
|
-
lines: [
|
|
135
|
-
"Google Chat apps use service-account auth and an HTTPS webhook.",
|
|
136
|
-
"Set the Chat API scopes in your service account and configure the Chat app URL.",
|
|
137
|
-
"Webhook verification requires audience type + audience value.",
|
|
138
|
-
`Docs: ${formatDocsLink("/channels/googlechat", "googlechat")}`,
|
|
139
|
-
],
|
|
140
|
-
},
|
|
141
|
-
prepare: async ({ cfg, accountId, credentialValues, prompter }) => {
|
|
142
|
-
const envReady =
|
|
143
|
-
accountId === DEFAULT_ACCOUNT_ID &&
|
|
144
|
-
(Boolean(process.env[ENV_SERVICE_ACCOUNT]) || Boolean(process.env[ENV_SERVICE_ACCOUNT_FILE]));
|
|
145
|
-
if (envReady) {
|
|
146
|
-
const useEnv = await prompter.confirm({
|
|
147
|
-
message: "Use GOOGLE_CHAT_SERVICE_ACCOUNT env vars?",
|
|
148
|
-
initialValue: true,
|
|
149
|
-
});
|
|
150
|
-
if (useEnv) {
|
|
151
|
-
return {
|
|
152
|
-
cfg: applySetupAccountConfigPatch({
|
|
153
|
-
cfg,
|
|
154
|
-
channelKey: channel,
|
|
155
|
-
accountId,
|
|
156
|
-
patch: {},
|
|
157
|
-
}),
|
|
158
|
-
credentialValues: {
|
|
159
|
-
...credentialValues,
|
|
160
|
-
[USE_ENV_FLAG]: "1",
|
|
161
|
-
},
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const method = await prompter.select({
|
|
167
|
-
message: "Google Chat auth method",
|
|
168
|
-
options: [
|
|
169
|
-
{ value: "file", label: "Service account JSON file" },
|
|
170
|
-
{ value: "inline", label: "Paste service account JSON" },
|
|
171
|
-
],
|
|
172
|
-
initialValue: "file",
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
return {
|
|
176
|
-
credentialValues: {
|
|
177
|
-
...credentialValues,
|
|
178
|
-
[USE_ENV_FLAG]: "0",
|
|
179
|
-
[AUTH_METHOD_FLAG]: method,
|
|
180
|
-
},
|
|
181
|
-
};
|
|
182
|
-
},
|
|
183
|
-
credentials: [],
|
|
184
|
-
textInputs: [
|
|
185
|
-
createServiceAccountTextInput({
|
|
186
|
-
inputKey: "tokenFile",
|
|
187
|
-
message: "Service account JSON path",
|
|
188
|
-
placeholder: "/path/to/service-account.json",
|
|
189
|
-
authMethod: "file",
|
|
190
|
-
patchKey: "serviceAccountFile",
|
|
191
|
-
}),
|
|
192
|
-
createServiceAccountTextInput({
|
|
193
|
-
inputKey: "token",
|
|
194
|
-
message: "Service account JSON (single line)",
|
|
195
|
-
placeholder: '{"type":"service_account", ... }',
|
|
196
|
-
authMethod: "inline",
|
|
197
|
-
patchKey: "serviceAccount",
|
|
198
|
-
}),
|
|
199
|
-
],
|
|
200
|
-
finalize: async ({ cfg, accountId, prompter }) => {
|
|
201
|
-
const account = resolveGoogleChatAccount({
|
|
202
|
-
cfg,
|
|
203
|
-
accountId,
|
|
204
|
-
});
|
|
205
|
-
const audienceType = await prompter.select({
|
|
206
|
-
message: "Webhook audience type",
|
|
207
|
-
options: [
|
|
208
|
-
{ value: "app-url", label: "App URL (recommended)" },
|
|
209
|
-
{ value: "project-number", label: "Project number" },
|
|
210
|
-
],
|
|
211
|
-
initialValue: account.config.audienceType === "project-number" ? "project-number" : "app-url",
|
|
212
|
-
});
|
|
213
|
-
const audience = await prompter.text({
|
|
214
|
-
message: audienceType === "project-number" ? "Project number" : "App URL",
|
|
215
|
-
placeholder:
|
|
216
|
-
audienceType === "project-number" ? "1234567890" : "https://your.host/googlechat",
|
|
217
|
-
initialValue: account.config.audience || undefined,
|
|
218
|
-
validate: (value) => (normalizeStringifiedOptionalString(value) ? undefined : "Required"),
|
|
219
|
-
});
|
|
220
|
-
return {
|
|
221
|
-
cfg: migrateBaseNameToDefaultAccount({
|
|
222
|
-
cfg: applySetupAccountConfigPatch({
|
|
223
|
-
cfg,
|
|
224
|
-
channelKey: channel,
|
|
225
|
-
accountId,
|
|
226
|
-
patch: {
|
|
227
|
-
audienceType,
|
|
228
|
-
audience: normalizeOptionalString(audience) ?? "",
|
|
229
|
-
},
|
|
230
|
-
}),
|
|
231
|
-
channelKey: channel,
|
|
232
|
-
}),
|
|
233
|
-
};
|
|
234
|
-
},
|
|
235
|
-
dmPolicy: googlechatDmPolicy,
|
|
236
|
-
};
|