Skip to content

Commit f24b6fe

Browse files
dnplkndllclaude
andcommitted
feat(ai-bot): make bot account email configurable via AI_BOT_EMAIL env var
The AI bot email was hardcoded to huly.ai.bot@hc.engineering, causing self-hosted deployments to send notifications to an unreachable address. Add AI_BOT_EMAIL env var to the ai-bot service and transactor: - ai-bot service: reads AI_BOT_EMAIL, calls setAiBotAccountEmail() on startup, uses it for signUp/assignWorkspace/identity lookups - transactor: reads AI_BOT_EMAIL, calls setAiBotAccountEmail() so middleware recognizes the bot as a service account - server plugin triggers: use getAiBotEmailSocialKey() for DB queries - middleware: import from @hcengineering/ai-bot instead of duplicating the hardcoded string - client plugin unchanged: queries by the default constant, which still works because the bot retains its original social identity Defaults to huly.ai.bot@hc.engineering when unset (no behavior change). Tested on self-hosted K8s: bot registers new social identity, DMs work, 0 restarts on both services. Resolves hcengineering/huly-selfhost#239 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Don Kendall <kendall@donkendall.com>
1 parent f9cb970 commit f24b6fe

12 files changed

Lines changed: 52 additions & 18 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
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414
//
1515

16+
import { getAiBotAccountEmail } from '@hcengineering/ai-bot'
1617
import core, {
1718
type MeasureContext,
1819
type Tx,
@@ -28,8 +29,6 @@ import {
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: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,24 @@ export * from './rest'
2222
export const aiBotId = 'ai-bot' as Plugin
2323

2424
export const aiBotAccountEmail = 'huly.ai.bot@hc.engineering'
25+
26+
let _aiBotAccountEmail: string = aiBotAccountEmail
27+
28+
export function getAiBotAccountEmail (): string {
29+
return _aiBotAccountEmail
30+
}
31+
32+
export function setAiBotAccountEmail (email: string): void {
33+
_aiBotAccountEmail = email
34+
}
35+
36+
export function getAiBotEmailSocialKey (): string {
37+
return buildSocialIdString({
38+
type: SocialIdType.EMAIL,
39+
value: _aiBotAccountEmail
40+
})
41+
}
42+
2543
export const aiBotEmailSocialKey = buildSocialIdString({
2644
type: SocialIdType.EMAIL,
2745
value: aiBotAccountEmail

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
@@ -3,6 +3,7 @@
33
//
44

55
// Add this to the VERY top of the first file loaded in your app
6+
import { setAiBotAccountEmail } from '@hcengineering/ai-bot'
67
import { Analytics } from '@hcengineering/analytics'
78
import { configureAnalytics, createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
89
import contactPlugin from '@hcengineering/contact'
@@ -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) {
90+
setAiBotAccountEmail(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/config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414
//
1515

16+
import { aiBotAccountEmail } from '@hcengineering/ai-bot'
1617
import OpenAI from 'openai'
1718

1819
interface Config {
@@ -26,6 +27,7 @@ interface Config {
2627
AvatarPath: string
2728
AvatarName: string
2829
AvatarContentType: string
30+
Email: string
2931
Password: string
3032
OpenAIKey: string
3133
OpenAIModel: OpenAI.ChatModel
@@ -58,6 +60,7 @@ const config: Config = (() => {
5860
AvatarPath: process.env.AVATAR_PATH ?? './assets/avatar.png',
5961
AvatarName: process.env.AVATAR_NAME ?? 'huly_ai_bot_avatar',
6062
AvatarContentType: process.env.AVATAR_CONTENT_TYPE ?? 'image/png',
63+
Email: process.env.AI_BOT_EMAIL ?? aiBotAccountEmail,
6164
Password: process.env.PASSWORD ?? 'password',
6265
OpenAIKey: process.env.OPENAI_API_KEY ?? '',
6366
OpenAIModel: (process.env.OPENAI_MODEL ?? 'gpt-4o-mini') as OpenAI.ChatModel,

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

Lines changed: 2 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 { setAiBotAccountEmail } 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'
@@ -30,6 +31,7 @@ import { getAccountUuid } from './utils/account'
3031
import { updateDeepgramBilling } from './billing'
3132

3233
export const start = async (): Promise<void> => {
34+
setAiBotAccountEmail(config.Email)
3335
setMetadata(serverToken.metadata.Secret, config.ServerSecret)
3436
setMetadata(serverToken.metadata.Service, 'ai-bot-service')
3537
setMetadata(serverClient.metadata.UserAgent, config.ServiceID)

0 commit comments

Comments
 (0)