@hed-hog/core 0.0.366 → 0.0.368

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 (87) hide show
  1. package/dist/file/file.service.d.ts +1 -0
  2. package/dist/file/file.service.d.ts.map +1 -1
  3. package/dist/file/file.service.js +1 -1
  4. package/dist/file/file.service.js.map +1 -1
  5. package/dist/integration/services/outbox-polling.coordinator.d.ts +2 -0
  6. package/dist/integration/services/outbox-polling.coordinator.d.ts.map +1 -1
  7. package/dist/integration/services/outbox-polling.coordinator.js +14 -4
  8. package/dist/integration/services/outbox-polling.coordinator.js.map +1 -1
  9. package/dist/integration/services/outbox.processor.d.ts.map +1 -1
  10. package/dist/integration/services/outbox.processor.js +4 -3
  11. package/dist/integration/services/outbox.processor.js.map +1 -1
  12. package/dist/locale/locale.controller.d.ts +1 -1
  13. package/dist/locale/locale.controller.d.ts.map +1 -1
  14. package/dist/locale/locale.controller.js +4 -3
  15. package/dist/locale/locale.controller.js.map +1 -1
  16. package/dist/locale/locale.service.d.ts +1 -1
  17. package/dist/locale/locale.service.d.ts.map +1 -1
  18. package/dist/locale/locale.service.js +6 -6
  19. package/dist/locale/locale.service.js.map +1 -1
  20. package/dist/notification/events/notification-stale-progress-check.event.d.ts +6 -0
  21. package/dist/notification/events/notification-stale-progress-check.event.d.ts.map +1 -0
  22. package/dist/notification/events/notification-stale-progress-check.event.js +11 -0
  23. package/dist/notification/events/notification-stale-progress-check.event.js.map +1 -0
  24. package/dist/notification/notification.service.d.ts +4 -1
  25. package/dist/notification/notification.service.d.ts.map +1 -1
  26. package/dist/notification/notification.service.js +9 -26
  27. package/dist/notification/notification.service.js.map +1 -1
  28. package/dist/task/task.service.d.ts +19 -0
  29. package/dist/task/task.service.d.ts.map +1 -1
  30. package/dist/task/task.service.js +46 -0
  31. package/dist/task/task.service.js.map +1 -1
  32. package/dist/user/user.controller.d.ts +23 -5
  33. package/dist/user/user.controller.d.ts.map +1 -1
  34. package/dist/user/user.controller.js +32 -15
  35. package/dist/user/user.controller.js.map +1 -1
  36. package/dist/user/user.service.d.ts +28 -8
  37. package/dist/user/user.service.d.ts.map +1 -1
  38. package/dist/user/user.service.js +120 -8
  39. package/dist/user/user.service.js.map +1 -1
  40. package/hedhog/data/integration_event_catalog.yaml +118 -118
  41. package/hedhog/data/integration_provider.yaml +25 -0
  42. package/hedhog/data/integration_type.yaml +6 -0
  43. package/hedhog/data/locale.yaml +5 -1
  44. package/hedhog/data/route.yaml +9 -0
  45. package/hedhog/data/setting_group.yaml +116 -106
  46. package/hedhog/data/setting_subgroup.yaml +32 -0
  47. package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +56 -0
  48. package/hedhog/frontend/app/roles/page.tsx.ejs +4 -1
  49. package/hedhog/frontend/app/users/user-logs.tsx.ejs +190 -0
  50. package/hedhog/frontend/app/users/user-sheet.tsx.ejs +15 -1
  51. package/hedhog/frontend/messages/en.json +17 -0
  52. package/hedhog/frontend/messages/pt.json +17 -0
  53. package/hedhog/table/ai_instruction.yaml +1 -1
  54. package/hedhog/table/event_webhook.yaml +55 -55
  55. package/hedhog/table/event_webhook_delivery_log.yaml +66 -66
  56. package/hedhog/table/event_webhook_event.yaml +26 -26
  57. package/hedhog/table/integration_event_catalog.yaml +41 -41
  58. package/hedhog/table/mcp_conversation.yaml +12 -12
  59. package/hedhog/table/mcp_conversation_message.yaml +23 -23
  60. package/hedhog/table/mcp_tool_call_log.yaml +22 -22
  61. package/hedhog/table/route.yaml +33 -33
  62. package/hedhog/table/user_change_log.yaml +40 -0
  63. package/hedhog/table/webhook_call_log.yaml +46 -46
  64. package/hedhog/table/webhook_integration.yaml +43 -43
  65. package/package.json +4 -4
  66. package/src/file/file.service.ts +2 -1
  67. package/src/integration/services/outbox-polling.coordinator.ts +16 -4
  68. package/src/integration/services/outbox.processor.ts +14 -8
  69. package/src/integration/types/event.types.ts +4 -4
  70. package/src/locale/locale.controller.ts +2 -1
  71. package/src/locale/locale.service.ts +7 -6
  72. package/src/mcp/decorators/mcp-tool.decorator.ts +7 -7
  73. package/src/mcp/guards/mcp-permission.guard.ts +51 -51
  74. package/src/mcp/interfaces/mcp-tool-definition.interface.ts +6 -6
  75. package/src/mcp/mcp-server.service.ts +87 -87
  76. package/src/mcp/types/mcp-context.type.ts +6 -6
  77. package/src/mcp-chat/dto/create-conversation.dto.ts +7 -7
  78. package/src/mcp-chat/dto/send-message.dto.ts +7 -7
  79. package/src/notification/events/notification-stale-progress-check.event.ts +5 -0
  80. package/src/notification/notification.service.ts +12 -25
  81. package/src/route/dto/create.dto.ts +32 -32
  82. package/src/route/dto/update.dto.ts +31 -31
  83. package/src/task/task.service.ts +52 -0
  84. package/src/user/user.controller.ts +23 -9
  85. package/src/user/user.service.ts +183 -8
  86. package/src/webhook-integration/integration-event-catalog.controller.ts +50 -50
  87. package/src/webhook-integration/integration-event-catalog.service.ts +117 -117
@@ -1,46 +1,46 @@
1
- columns:
2
- - type: pk
3
- - type: fk
4
- name: webhook_integration_id
5
- isNullable: true
6
- references:
7
- table: webhook_integration
8
- column: id
9
- onDelete: set null
10
- onUpdate: cascade
11
- - name: public_uuid
12
- type: varchar
13
- length: 36
14
- - name: status
15
- type: enum
16
- enum:
17
- - success
18
- - failed
19
- - name: remote_ip
20
- type: varchar
21
- length: 127
22
- isNullable: true
23
- - name: duration_ms
24
- type: int
25
- default: 0
26
- - name: error_message
27
- type: text
28
- isNullable: true
29
- - name: payload_summary
30
- type: json
31
- isNullable: true
32
- - name: response_summary
33
- type: json
34
- isNullable: true
35
- - type: created_at
36
- - type: updated_at
37
- indices:
38
- - columns:
39
- - webhook_integration_id
40
- - columns:
41
- - public_uuid
42
- - columns:
43
- - status
44
- - columns:
45
- - created_at
46
-
1
+ columns:
2
+ - type: pk
3
+ - type: fk
4
+ name: webhook_integration_id
5
+ isNullable: true
6
+ references:
7
+ table: webhook_integration
8
+ column: id
9
+ onDelete: set null
10
+ onUpdate: cascade
11
+ - name: public_uuid
12
+ type: varchar
13
+ length: 36
14
+ - name: status
15
+ type: enum
16
+ enum:
17
+ - success
18
+ - failed
19
+ - name: remote_ip
20
+ type: varchar
21
+ length: 127
22
+ isNullable: true
23
+ - name: duration_ms
24
+ type: int
25
+ default: 0
26
+ - name: error_message
27
+ type: text
28
+ isNullable: true
29
+ - name: payload_summary
30
+ type: json
31
+ isNullable: true
32
+ - name: response_summary
33
+ type: json
34
+ isNullable: true
35
+ - type: created_at
36
+ - type: updated_at
37
+ indices:
38
+ - columns:
39
+ - webhook_integration_id
40
+ - columns:
41
+ - public_uuid
42
+ - columns:
43
+ - status
44
+ - columns:
45
+ - created_at
46
+
@@ -1,43 +1,43 @@
1
- columns:
2
- - type: pk
3
- - type: slug
4
- - name: name
5
- type: varchar
6
- length: 127
7
- - name: description
8
- type: text
9
- isNullable: true
10
- - name: public_uuid
11
- type: varchar
12
- length: 36
13
- - name: status
14
- type: enum
15
- enum:
16
- - active
17
- - inactive
18
- default: active
19
- - name: require_token
20
- type: boolean
21
- default: false
22
- - name: token_hash
23
- type: varchar
24
- length: 128
25
- isNullable: true
26
- - name: allowed_ips
27
- type: json
28
- isNullable: true
29
- - name: body_schema
30
- type: json
31
- isNullable: true
32
- - type: created_at
33
- - type: updated_at
34
- indices:
35
- - columns:
36
- - slug
37
- isUnique: true
38
- - columns:
39
- - public_uuid
40
- isUnique: true
41
- - columns:
42
- - status
43
-
1
+ columns:
2
+ - type: pk
3
+ - type: slug
4
+ - name: name
5
+ type: varchar
6
+ length: 127
7
+ - name: description
8
+ type: text
9
+ isNullable: true
10
+ - name: public_uuid
11
+ type: varchar
12
+ length: 36
13
+ - name: status
14
+ type: enum
15
+ enum:
16
+ - active
17
+ - inactive
18
+ default: active
19
+ - name: require_token
20
+ type: boolean
21
+ default: false
22
+ - name: token_hash
23
+ type: varchar
24
+ length: 128
25
+ isNullable: true
26
+ - name: allowed_ips
27
+ type: json
28
+ isNullable: true
29
+ - name: body_schema
30
+ type: json
31
+ isNullable: true
32
+ - type: created_at
33
+ - type: updated_at
34
+ indices:
35
+ - columns:
36
+ - slug
37
+ isUnique: true
38
+ - columns:
39
+ - public_uuid
40
+ isUnique: true
41
+ - columns:
42
+ - status
43
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hed-hog/core",
3
- "version": "0.0.366",
3
+ "version": "0.0.368",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "dependencies": {
@@ -38,12 +38,12 @@
38
38
  "speakeasy": "^2.0.0",
39
39
  "ua-parser-js": "^2.0.10",
40
40
  "uuid": "^11.1.0",
41
- "@hed-hog/api-pagination": "0.0.7",
42
41
  "@hed-hog/api": "0.0.8",
43
42
  "@hed-hog/api-locale": "0.0.14",
43
+ "@hed-hog/api-pagination": "0.0.7",
44
44
  "@hed-hog/api-mail": "0.0.9",
45
- "@hed-hog/api-prisma": "0.0.6",
46
- "@hed-hog/api-types": "0.0.1"
45
+ "@hed-hog/api-types": "0.0.1",
46
+ "@hed-hog/api-prisma": "0.0.6"
47
47
  },
48
48
  "exports": {
49
49
  ".": {
@@ -490,6 +490,7 @@ export class FileService implements OnModuleInit {
490
490
  mimetype?: string;
491
491
  size?: number;
492
492
  encoding?: string;
493
+ skipMimeValidation?: boolean;
493
494
  },
494
495
  ) {
495
496
  if (!filePath) {
@@ -511,7 +512,7 @@ export class FileService implements OnModuleInit {
511
512
  metadata?.originalname ?? metadata?.filename ?? basename(filePath),
512
513
  );
513
514
 
514
- if (!(await this.acceptMimetypes(mimetype))) {
515
+ if (!metadata?.skipMimeValidation && !(await this.acceptMimetypes(mimetype))) {
515
516
  throw new BadRequestException(`Invalid file type: ${mimetype}`);
516
517
  }
517
518
 
@@ -15,6 +15,8 @@ export class OutboxPollingCoordinator {
15
15
  private processingLoopPromise: Promise<void> | null = null;
16
16
  private immediateWakeupTriggered = false;
17
17
  private shouldStopLoop = false;
18
+ private _currentTimeout: ReturnType<typeof setTimeout> | null = null;
19
+ private _resolveDelay: (() => void) | null = null;
18
20
 
19
21
  constructor(
20
22
  private readonly processor: OutboxProcessor,
@@ -62,6 +64,15 @@ export class OutboxPollingCoordinator {
62
64
  */
63
65
  triggerImmediateProcessing(): void {
64
66
  this.immediateWakeupTriggered = true;
67
+ if (this._currentTimeout !== null) {
68
+ clearTimeout(this._currentTimeout);
69
+ this._currentTimeout = null;
70
+ this.logger.debug('[outbox:wakeup] delay interrupted by new event notification');
71
+ }
72
+ if (this._resolveDelay) {
73
+ this._resolveDelay();
74
+ this._resolveDelay = null;
75
+ }
65
76
  }
66
77
 
67
78
  /**
@@ -88,7 +99,9 @@ export class OutboxPollingCoordinator {
88
99
  const processorEnabled = settings.outboxProcessorEnabled;
89
100
 
90
101
  if (!isEnabled || !processorEnabled) {
91
- // If disabled, wait before checking again
102
+ this.logger.debug(
103
+ `[outbox:loop] processor disabled (outboxEnabled=${isEnabled} processorEnabled=${processorEnabled}), sleeping ${settings.idlePollingIntervalMs}ms`,
104
+ );
92
105
  await this.delay(settings.idlePollingIntervalMs);
93
106
  continue;
94
107
  }
@@ -135,9 +148,8 @@ export class OutboxPollingCoordinator {
135
148
  */
136
149
  private delay(ms: number): Promise<void> {
137
150
  return new Promise((resolve) => {
138
- const timeout = setTimeout(resolve, ms);
139
- // Store timeout for potential cancellation (not used yet, but available)
140
- (this as any)._currentTimeout = timeout;
151
+ this._resolveDelay = resolve;
152
+ this._currentTimeout = setTimeout(resolve, ms);
141
153
  });
142
154
  }
143
155
  }
@@ -34,7 +34,9 @@ export class OutboxProcessor {
34
34
  return 0;
35
35
  }
36
36
 
37
- this.logger.debug(`Processing ${events.length} outbox events`);
37
+ this.logger.debug(
38
+ `[outbox:batch] processing ${events.length} event(s): ${events.map((e) => `${e.id}(${e.eventName})`).join(', ')}`,
39
+ );
38
40
 
39
41
  let processedCount = 0;
40
42
 
@@ -48,7 +50,7 @@ export class OutboxProcessor {
48
50
 
49
51
  if (handlers.length === 0) {
50
52
  this.logger.warn(
51
- `No handlers registered for event: ${event.eventName}`,
53
+ `[outbox:batch] no handlers registered for eventName=${event.eventName} eventId=${event.id} — marking as processed and skipping`,
52
54
  );
53
55
  // Mark as processed even though no handlers
54
56
  await this.outboxService.updateStatus(
@@ -80,13 +82,17 @@ export class OutboxProcessor {
80
82
 
81
83
  await this.inboxService.markProcessing(inboxEvent.id);
82
84
 
85
+ this.logger.debug(
86
+ `[outbox:batch] running handler=${handlerDef.consumerName} eventId=${event.id}`,
87
+ );
88
+
83
89
  try {
84
90
  // Execute handler
85
- await handlerDef.handler(
86
- {
87
- outboxEventId: event.id,
88
- eventName: event.eventName,
89
- sourceModule: event.sourceModule,
91
+ await handlerDef.handler(
92
+ {
93
+ outboxEventId: event.id,
94
+ eventName: event.eventName,
95
+ sourceModule: event.sourceModule,
90
96
  aggregateType: event.aggregateType,
91
97
  aggregateId: event.aggregateId,
92
98
  payload: event.payload,
@@ -107,7 +113,7 @@ export class OutboxProcessor {
107
113
  } catch (error) {
108
114
  anyFailed = true;
109
115
  this.logger.error(
110
- `Handler ${handlerDef.consumerName} failed for event ${event.id}:`,
116
+ `[outbox:batch] handler=${handlerDef.consumerName} failed for eventId=${event.id}: ${error instanceof Error ? error.message : String(error)}`,
111
117
  error,
112
118
  );
113
119
  await this.inboxService.markFailed(
@@ -32,10 +32,10 @@ export enum LinkType {
32
32
  /**
33
33
  * Domain event interface that is published and processed
34
34
  */
35
- export interface DomainEvent {
36
- outboxEventId?: string;
37
- eventName: string;
38
- sourceModule: string;
35
+ export interface DomainEvent {
36
+ outboxEventId?: string;
37
+ eventName: string;
38
+ sourceModule: string;
39
39
  aggregateType: string;
40
40
  aggregateId: string;
41
41
  payload: Record<string, any>;
@@ -24,8 +24,9 @@ export class LocaleController {
24
24
  async findAll(
25
25
  @Pagination() paginationDto: PaginationDTO,
26
26
  @Query('search') search?: string,
27
+ @Query('enabled') enabled?: string,
27
28
  ) {
28
- return this.localeService.findAll(paginationDto, search);
29
+ return this.localeService.findAll(paginationDto, search, enabled);
29
30
  }
30
31
 
31
32
  @Post()
@@ -12,7 +12,7 @@ export class LocaleService {
12
12
  private readonly paginationService: PaginationService,
13
13
  ) {}
14
14
 
15
- async findAll(paginationDto: PaginationDTO, search?: string) {
15
+ async findAll(paginationDto: PaginationDTO, search?: string, enabled?: string) {
16
16
  const normalizedSearch = search ?? paginationDto.search;
17
17
  const paginationParams: PaginationDTO = {
18
18
  ...paginationDto,
@@ -24,14 +24,15 @@ export class LocaleService {
24
24
  paginationParams,
25
25
  );
26
26
 
27
+ const where: Record<string, any> = { OR };
28
+ if (enabled !== undefined) {
29
+ where.enabled = enabled === 'true';
30
+ }
31
+
27
32
  return this.paginationService.paginate(
28
33
  this.prismaService.locale,
29
34
  paginationParams,
30
- {
31
- where: {
32
- OR,
33
- },
34
- },
35
+ { where },
35
36
  );
36
37
  }
37
38
 
@@ -1,7 +1,7 @@
1
- import { SetMetadata } from '@nestjs/common';
2
- import { McpToolDefinition } from '../interfaces/mcp-tool-definition.interface';
3
-
4
- export const MCP_TOOL_METADATA = 'mcp:tool';
5
-
6
- export const McpTool = (definition: McpToolDefinition): MethodDecorator =>
7
- SetMetadata(MCP_TOOL_METADATA, definition);
1
+ import { SetMetadata } from '@nestjs/common';
2
+ import { McpToolDefinition } from '../interfaces/mcp-tool-definition.interface';
3
+
4
+ export const MCP_TOOL_METADATA = 'mcp:tool';
5
+
6
+ export const McpTool = (definition: McpToolDefinition): MethodDecorator =>
7
+ SetMetadata(MCP_TOOL_METADATA, definition);
@@ -1,51 +1,51 @@
1
- import { PrismaService } from '@hed-hog/api-prisma';
2
- import {
3
- CanActivate,
4
- ExecutionContext,
5
- ForbiddenException,
6
- Injectable,
7
- } from '@nestjs/common';
8
- import { Request } from 'express';
9
-
10
- @Injectable()
11
- export class McpPermissionGuard implements CanActivate {
12
- constructor(private readonly prisma: PrismaService) {}
13
-
14
- async canActivate(context: ExecutionContext): Promise<boolean> {
15
- const request = context.switchToHttp().getRequest<Request>();
16
-
17
- // Only POST requests carry tool invocations
18
- if (request.method !== 'POST') return true;
19
-
20
- const body = request.body as any;
21
- const toolName: string | undefined =
22
- body?.method === 'tools/call' ? body?.params?.name : undefined;
23
-
24
- // tools/list, initialize, ping, etc. — no per-tool permission check needed
25
- if (!toolName) return true;
26
-
27
- const userId = (request as any)?.auth?.sub;
28
-
29
- const count = await this.prisma.route.count({
30
- where: {
31
- type: 'MCP' as any,
32
- tool_name: toolName,
33
- role_route: {
34
- some: {
35
- role: {
36
- role_user: {
37
- some: { user_id: userId },
38
- },
39
- },
40
- },
41
- },
42
- },
43
- });
44
-
45
- if (count === 0) {
46
- throw new ForbiddenException(`Access denied to MCP tool '${toolName}'`);
47
- }
48
-
49
- return true;
50
- }
51
- }
1
+ import { PrismaService } from '@hed-hog/api-prisma';
2
+ import {
3
+ CanActivate,
4
+ ExecutionContext,
5
+ ForbiddenException,
6
+ Injectable,
7
+ } from '@nestjs/common';
8
+ import { Request } from 'express';
9
+
10
+ @Injectable()
11
+ export class McpPermissionGuard implements CanActivate {
12
+ constructor(private readonly prisma: PrismaService) {}
13
+
14
+ async canActivate(context: ExecutionContext): Promise<boolean> {
15
+ const request = context.switchToHttp().getRequest<Request>();
16
+
17
+ // Only POST requests carry tool invocations
18
+ if (request.method !== 'POST') return true;
19
+
20
+ const body = request.body as any;
21
+ const toolName: string | undefined =
22
+ body?.method === 'tools/call' ? body?.params?.name : undefined;
23
+
24
+ // tools/list, initialize, ping, etc. — no per-tool permission check needed
25
+ if (!toolName) return true;
26
+
27
+ const userId = (request as any)?.auth?.sub;
28
+
29
+ const count = await this.prisma.route.count({
30
+ where: {
31
+ type: 'MCP' as any,
32
+ tool_name: toolName,
33
+ role_route: {
34
+ some: {
35
+ role: {
36
+ role_user: {
37
+ some: { user_id: userId },
38
+ },
39
+ },
40
+ },
41
+ },
42
+ },
43
+ });
44
+
45
+ if (count === 0) {
46
+ throw new ForbiddenException(`Access denied to MCP tool '${toolName}'`);
47
+ }
48
+
49
+ return true;
50
+ }
51
+ }
@@ -1,6 +1,6 @@
1
- export interface McpToolDefinition {
2
- name: string;
3
- description: string;
4
- inputSchema?: Record<string, any>;
5
- readOnly?: boolean;
6
- }
1
+ export interface McpToolDefinition {
2
+ name: string;
3
+ description: string;
4
+ inputSchema?: Record<string, any>;
5
+ readOnly?: boolean;
6
+ }