Skip to content

Commit 5872a9b

Browse files
dnplkndllclaude
andcommitted
feat(ai-bot): make bot account email configurable via platform metadata
Replace hardcoded bot email with platform Metadata pattern, matching how FrontVersion and other config values are handled in the codebase. - Add BotEmail metadata key to ai-bot plugin definition - Add getAiBotAccountEmail() and getAiBotEmailSocialKey() dynamic getters that read from metadata with fallback to default constant - Set metadata from AI_BOT_EMAIL env var in both transactor (__start.ts) and ai-bot service (start.ts), with empty-string guard - Update all server-side consumers to use getters instead of constants: middleware (identity, pluginConfig), pod-ai-bot (account, platform), server-plugins/ai-bot-resources - Keep static constants exported for client-side use (unchanged) - Remove duplicate hardcoded email from middleware identity.ts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Don Kendall <kendall@donkendall.com>
1 parent f9cb970 commit 5872a9b

11 files changed

Lines changed: 47 additions & 20 deletions

File tree

common/config/rush/pnpm-lock.yaml

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

foundations/server/packages/middleware/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"eslint-plugin-svelte": "^2.35.1"
3636
},
3737
"dependencies": {
38+
"@hcengineering/ai-bot": "workspace:^0.7.0",
3839
"@hcengineering/core": "workspace:^0.7.24",
3940
"@hcengineering/contact": "workspace:^0.7.0",
4041
"@hcengineering/platform": "workspace:^0.7.19",

foundations/server/packages/middleware/src/identity.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,14 @@ import core, {
2121
type TxApplyIf
2222
} from '@hcengineering/core'
2323
import platform, { PlatformError, Severity, Status } from '@hcengineering/platform'
24+
import { getAiBotAccountEmail } from '@hcengineering/ai-bot'
2425
import {
2526
BaseMiddleware,
2627
type Middleware,
2728
type TxMiddlewareResult,
2829
type PipelineContext
2930
} from '@hcengineering/server-core'
3031

31-
export const aiBotAccountEmail = 'huly.ai.bot@hc.engineering'
32-
3332
/**
3433
* @public
3534
*/
@@ -49,7 +48,7 @@ export class IdentityMiddleware extends BaseMiddleware implements Middleware {
4948
tx (ctx: MeasureContext<SessionData>, txes: Tx[]): Promise<TxMiddlewareResult> {
5049
const account = ctx.contextData.account
5150

52-
if (account.uuid === systemAccountUuid || account.fullSocialIds.some((it) => it.value === aiBotAccountEmail)) {
51+
if (account.uuid === systemAccountUuid || account.fullSocialIds.some((it) => it.value === getAiBotAccountEmail())) {
5352
// TODO: We need to enhance allowed list in case of user service, on behalf of user activities.
5453

5554
// We pass for system accounts and services.

foundations/server/packages/middleware/src/pluginConfig.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
type TxMiddlewareResult,
3131
type PipelineContext
3232
} from '@hcengineering/server-core'
33-
import { aiBotAccountEmail } from './identity'
33+
import { getAiBotAccountEmail } from '@hcengineering/ai-bot'
3434

3535
/**
3636
* @public
@@ -50,7 +50,7 @@ export class PluginConfigurationMiddleware extends BaseMiddleware implements Mid
5050

5151
tx (ctx: MeasureContext<SessionData>, txes: Tx[]): Promise<TxMiddlewareResult> {
5252
const account = ctx.contextData.account
53-
if (account.uuid === systemAccountUuid || account.fullSocialIds.some((it) => it.value === aiBotAccountEmail)) {
53+
if (account.uuid === systemAccountUuid || account.fullSocialIds.some((it) => it.value === getAiBotAccountEmail())) {
5454
// We pass for system accounts and services.
5555
return this.provideTx(ctx, txes)
5656
}

plugins/ai-bot/src/index.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
import { buildSocialIdString, SocialIdType } from '@hcengineering/core'
1717
import type { Metadata, Plugin } from '@hcengineering/platform'
18-
import { plugin } from '@hcengineering/platform'
18+
import { getMetadata, plugin } from '@hcengineering/platform'
1919

2020
export * from './rest'
2121

@@ -27,9 +27,21 @@ export const aiBotEmailSocialKey = buildSocialIdString({
2727
value: aiBotAccountEmail
2828
})
2929

30+
export function getAiBotAccountEmail (): string {
31+
return getMetadata(aiBot.metadata.BotEmail) ?? aiBotAccountEmail
32+
}
33+
34+
export function getAiBotEmailSocialKey (): string {
35+
return buildSocialIdString({
36+
type: SocialIdType.EMAIL,
37+
value: getAiBotAccountEmail()
38+
})
39+
}
40+
3041
const aiBot = plugin(aiBotId, {
3142
metadata: {
32-
EndpointURL: '' as Metadata<string>
43+
EndpointURL: '' as Metadata<string>,
44+
BotEmail: '' as Metadata<string>
3345
}
3446
})
3547

pods/server/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
"@hcengineering/postgres": "workspace:^0.7.22",
7777
"@hcengineering/rpc": "workspace:^0.7.17",
7878
"@hcengineering/server": "workspace:^0.7.17",
79+
"@hcengineering/ai-bot": "workspace:^0.7.0",
7980
"@hcengineering/server-ai-bot": "workspace:^0.7.0",
8081
"@hcengineering/server-calendar": "workspace:^0.7.0",
8182
"@hcengineering/server-core": "workspace:^0.7.18",

pods/server/src/__start.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { getPlatformQueue } from '@hcengineering/kafka'
1111
import { setMetadata } from '@hcengineering/platform'
1212
import { setDBExtraOptions } from '@hcengineering/postgres'
1313
import { serverConfigFromEnv } from '@hcengineering/server'
14+
import aiBot from '@hcengineering/ai-bot'
1415
import serverAiBot from '@hcengineering/server-ai-bot'
1516
import serverCalendar from '@hcengineering/server-calendar'
1617
import serverCard from '@hcengineering/server-card'
@@ -85,6 +86,9 @@ setMetadata(serverNotification.metadata.MailUrl, config.mailUrl ?? '')
8586
setMetadata(serverNotification.metadata.MailAuthToken, config.mailAuthToken)
8687
setMetadata(serverNotification.metadata.WebPushUrl, config.webPushUrl)
8788
setMetadata(serverAiBot.metadata.EndpointURL, process.env.AI_BOT_URL)
89+
if (process.env.AI_BOT_EMAIL !== undefined && process.env.AI_BOT_EMAIL !== '') {
90+
setMetadata(aiBot.metadata.BotEmail, process.env.AI_BOT_EMAIL)
91+
}
8892
setMetadata(serverCalendar.metadata.EndpointURL, process.env.CALENDAR_URL)
8993
setMetadata(serverCard.metadata.CommunicationEnabled, process.env.COMMUNICATION_API_ENABLED === 'true')
9094

server-plugins/ai-bot-resources/src/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import core, {
2626
} from '@hcengineering/core'
2727
import { TriggerControl } from '@hcengineering/server-core'
2828
import { getAccountBySocialKey } from '@hcengineering/server-contact'
29-
import { aiBotEmailSocialKey, AIEventRequest } from '@hcengineering/ai-bot'
29+
import { getAiBotEmailSocialKey, AIEventRequest } from '@hcengineering/ai-bot'
3030
import chunter, { ChatMessage, DirectMessage, ThreadMessage } from '@hcengineering/chunter'
3131
import contact from '@hcengineering/contact'
3232

@@ -64,7 +64,7 @@ async function OnUserStatus (txes: TxCUD<UserStatus>[], control: TriggerControl)
6464
}
6565

6666
const socialIdentity = (
67-
await control.findAll(control.ctx, contact.class.SocialIdentity, { key: aiBotEmailSocialKey }, { limit: 1 })
67+
await control.findAll(control.ctx, contact.class.SocialIdentity, { key: getAiBotEmailSocialKey() }, { limit: 1 })
6868
)[0]
6969

7070
if (socialIdentity === undefined) {
@@ -82,7 +82,7 @@ async function OnMessageSend (originTxs: TxCreateDoc<ChatMessage>[], control: Tr
8282
}
8383
const { account } = control.ctx.contextData
8484
const primaryIdentity = (
85-
await control.findAll(control.ctx, contact.class.SocialIdentity, { key: aiBotEmailSocialKey }, { limit: 1 })
85+
await control.findAll(control.ctx, contact.class.SocialIdentity, { key: getAiBotEmailSocialKey() }, { limit: 1 })
8686
)[0]
8787

8888
if (primaryIdentity === undefined) return []
@@ -165,7 +165,7 @@ async function getMessageDoc (message: ChatMessage, control: TriggerControl): Pr
165165

166166
async function isDirectAvailable (direct: DirectMessage, control: TriggerControl): Promise<boolean> {
167167
const { members } = direct
168-
const account = await getAccountBySocialKey(control, aiBotEmailSocialKey)
168+
const account = await getAccountBySocialKey(control, getAiBotEmailSocialKey())
169169

170170
if (account == null) {
171171
return false

services/ai-bot/pod-ai-bot/src/start.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414
//
15+
import aiBot from '@hcengineering/ai-bot'
1516
import { setMetadata } from '@hcengineering/platform'
1617
import serverClient, { withRetry } from '@hcengineering/server-client'
1718
import { initStatisticsContext } from '@hcengineering/server-core'
@@ -34,6 +35,9 @@ export const start = async (): Promise<void> => {
3435
setMetadata(serverToken.metadata.Service, 'ai-bot-service')
3536
setMetadata(serverClient.metadata.UserAgent, config.ServiceID)
3637
setMetadata(serverClient.metadata.Endpoint, config.AccountsURL)
38+
if (process.env.AI_BOT_EMAIL !== undefined && process.env.AI_BOT_EMAIL !== '') {
39+
setMetadata(aiBot.metadata.BotEmail, process.env.AI_BOT_EMAIL)
40+
}
3741

3842
registerLoaders()
3943

services/ai-bot/pod-ai-bot/src/utils/account.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
} from '@hcengineering/core'
2424
import { generateToken } from '@hcengineering/server-token'
2525
import { getAccountClient, withRetry } from '@hcengineering/server-client'
26-
import { aiBotAccountEmail, aiBotEmailSocialKey } from '@hcengineering/ai-bot'
26+
import { getAiBotAccountEmail, getAiBotEmailSocialKey } from '@hcengineering/ai-bot'
2727
import { MeasureContext, PersonUuid, systemAccountUuid } from '@hcengineering/core'
2828

2929
import config from '../config'
@@ -105,7 +105,7 @@ export async function tryAssignToWorkspace (workspace: WorkspaceUuid, ctx: Measu
105105

106106
await withRetry(
107107
async () => {
108-
await accountClient.assignWorkspace(aiBotAccountEmail, workspace, AccountRole.User)
108+
await accountClient.assignWorkspace(getAiBotAccountEmail(), workspace, AccountRole.User)
109109
},
110110
(_, attempt) => attempt >= ASSIGN_WORKSPACE_ATTEMPTS,
111111
ASSIGN_WORKSPACE_DELAY
@@ -121,7 +121,7 @@ export async function tryAssignToWorkspace (workspace: WorkspaceUuid, ctx: Measu
121121
}
122122

123123
async function confirmAccount (uuid: PersonUuid): Promise<void> {
124-
const token = generateToken(uuid, undefined, { service: 'aibot', confirmEmail: aiBotAccountEmail })
124+
const token = generateToken(uuid, undefined, { service: 'aibot', confirmEmail: getAiBotAccountEmail() })
125125
const client = getAccountClient(token)
126126
try {
127127
await client.confirm()
@@ -135,17 +135,17 @@ let account: AccountUuid | undefined
135135
export async function getAccountUuid (ctx?: MeasureContext): Promise<AccountUuid | undefined> {
136136
if (account !== undefined) return account
137137

138-
const token = generateToken(systemAccountUuid, undefined, { service: 'aibot', confirmEmail: aiBotAccountEmail })
138+
const token = generateToken(systemAccountUuid, undefined, { service: 'aibot', confirmEmail: getAiBotAccountEmail() })
139139
const accountClient = getAccountClient(token)
140-
const personUuid = await accountClient.findPersonBySocialKey(aiBotEmailSocialKey)
140+
const personUuid = await accountClient.findPersonBySocialKey(getAiBotEmailSocialKey())
141141

142142
if (personUuid !== undefined) {
143143
await confirmAccount(personUuid)
144144
account = personUuid as AccountUuid
145145
return account
146146
}
147147

148-
const result = await accountClient.signUp(aiBotAccountEmail, config.Password, config.FirstName, config.LastName)
148+
const result = await accountClient.signUp(getAiBotAccountEmail(), config.Password, config.FirstName, config.LastName)
149149

150150
if (result !== undefined) {
151151
await confirmAccount(result.account)

0 commit comments

Comments
 (0)