crewly 1.4.61 → 1.4.63

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 (105) hide show
  1. package/config/skills/agent/content-calendar/execute.sh +2 -2
  2. package/config/skills/agent/marketing/brand-onboarding/SKILL.md +76 -0
  3. package/config/skills/agent/marketing/brand-onboarding/execute.sh +312 -0
  4. package/config/skills/agent/marketing/submit-for-approval/SKILL.md +73 -0
  5. package/config/skills/agent/marketing/submit-for-approval/execute.sh +138 -0
  6. package/config/skills/agent/marketing/weekly-content-planning/SKILL.md +52 -0
  7. package/config/skills/agent/marketing/weekly-content-planning/execute.sh +202 -0
  8. package/config/skills/agent/marketing/weekly-content-planning/execute.test.sh +151 -0
  9. package/config/skills/agent/marketing/weekly-marketing-report/SKILL.md +51 -0
  10. package/config/skills/agent/marketing/weekly-marketing-report/execute.sh +190 -0
  11. package/config/skills/agent/marketing/weekly-marketing-report/execute.test.sh +241 -0
  12. package/config/skills/orchestrator/send-to-remote/SKILL.md +51 -0
  13. package/config/skills/orchestrator/send-to-remote/execute.sh +114 -0
  14. package/config/templates/marketing-team/README.md +42 -0
  15. package/config/templates/marketing-team/goals.md +21 -0
  16. package/config/templates/marketing-team/knowledge/docs/brand-voice-guide.md +61 -0
  17. package/config/templates/marketing-team/knowledge/docs/content-best-practices.md +64 -0
  18. package/config/templates/marketing-team/knowledge/index.json +24 -0
  19. package/config/templates/marketing-team/learned-patterns.json +5 -0
  20. package/config/templates/marketing-team/norms/brand-consistency.md +40 -0
  21. package/config/templates/marketing-team/norms/content-quality-checklist.md +45 -0
  22. package/config/templates/marketing-team/quality-gates.yaml +47 -0
  23. package/config/templates/marketing-team/roles/analyst.md +63 -0
  24. package/config/templates/marketing-team/roles/strategist.md +26 -0
  25. package/config/templates/marketing-team/roles/writer.md +58 -0
  26. package/config/templates/marketing-team/template.json +90 -0
  27. package/config/templates/marketing-team/workflows/weekly-content-cycle.yaml +48 -0
  28. package/dist/backend/backend/src/constants.d.ts +9 -0
  29. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  30. package/dist/backend/backend/src/constants.js +9 -0
  31. package/dist/backend/backend/src/constants.js.map +1 -1
  32. package/dist/backend/backend/src/controllers/cross-machine/cross-machine.controller.d.ts +16 -0
  33. package/dist/backend/backend/src/controllers/cross-machine/cross-machine.controller.d.ts.map +1 -0
  34. package/dist/backend/backend/src/controllers/cross-machine/cross-machine.controller.js +140 -0
  35. package/dist/backend/backend/src/controllers/cross-machine/cross-machine.controller.js.map +1 -0
  36. package/dist/backend/backend/src/controllers/cross-machine/index.d.ts +7 -0
  37. package/dist/backend/backend/src/controllers/cross-machine/index.d.ts.map +1 -0
  38. package/dist/backend/backend/src/controllers/cross-machine/index.js +7 -0
  39. package/dist/backend/backend/src/controllers/cross-machine/index.js.map +1 -0
  40. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  41. package/dist/backend/backend/src/routes/api.routes.js +3 -0
  42. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  43. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  44. package/dist/backend/backend/src/services/agent/agent-registration.service.js +46 -6
  45. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  46. package/dist/backend/backend/src/services/ai/prompt-builder.service.d.ts +13 -0
  47. package/dist/backend/backend/src/services/ai/prompt-builder.service.d.ts.map +1 -1
  48. package/dist/backend/backend/src/services/ai/prompt-builder.service.js +50 -5
  49. package/dist/backend/backend/src/services/ai/prompt-builder.service.js.map +1 -1
  50. package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.d.ts +8 -0
  51. package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.d.ts.map +1 -1
  52. package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.js +52 -3
  53. package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.js.map +1 -1
  54. package/dist/backend/backend/src/services/cloud/relay-client.service.d.ts +5 -1
  55. package/dist/backend/backend/src/services/cloud/relay-client.service.d.ts.map +1 -1
  56. package/dist/backend/backend/src/services/cloud/relay-client.service.js +14 -2
  57. package/dist/backend/backend/src/services/cloud/relay-client.service.js.map +1 -1
  58. package/dist/backend/backend/src/services/index.d.ts +2 -0
  59. package/dist/backend/backend/src/services/index.d.ts.map +1 -1
  60. package/dist/backend/backend/src/services/index.js +2 -0
  61. package/dist/backend/backend/src/services/index.js.map +1 -1
  62. package/dist/backend/backend/src/services/onboarding/brand-onboarding.service.d.ts +155 -0
  63. package/dist/backend/backend/src/services/onboarding/brand-onboarding.service.d.ts.map +1 -0
  64. package/dist/backend/backend/src/services/onboarding/brand-onboarding.service.js +469 -0
  65. package/dist/backend/backend/src/services/onboarding/brand-onboarding.service.js.map +1 -0
  66. package/dist/backend/backend/src/services/onboarding/brand-onboarding.types.d.ts +107 -0
  67. package/dist/backend/backend/src/services/onboarding/brand-onboarding.types.d.ts.map +1 -0
  68. package/dist/backend/backend/src/services/onboarding/brand-onboarding.types.js +104 -0
  69. package/dist/backend/backend/src/services/onboarding/brand-onboarding.types.js.map +1 -0
  70. package/dist/backend/backend/src/services/onboarding/content-approval.service.d.ts +124 -0
  71. package/dist/backend/backend/src/services/onboarding/content-approval.service.d.ts.map +1 -0
  72. package/dist/backend/backend/src/services/onboarding/content-approval.service.js +256 -0
  73. package/dist/backend/backend/src/services/onboarding/content-approval.service.js.map +1 -0
  74. package/dist/backend/backend/src/services/onboarding/content-approval.types.d.ts +80 -0
  75. package/dist/backend/backend/src/services/onboarding/content-approval.types.d.ts.map +1 -0
  76. package/dist/backend/backend/src/services/onboarding/content-approval.types.js +16 -0
  77. package/dist/backend/backend/src/services/onboarding/content-approval.types.js.map +1 -0
  78. package/dist/backend/backend/src/services/onboarding/index.d.ts +12 -0
  79. package/dist/backend/backend/src/services/onboarding/index.d.ts.map +1 -0
  80. package/dist/backend/backend/src/services/onboarding/index.js +10 -0
  81. package/dist/backend/backend/src/services/onboarding/index.js.map +1 -0
  82. package/dist/backend/backend/src/services/slack/cross-machine-message.service.d.ts +147 -0
  83. package/dist/backend/backend/src/services/slack/cross-machine-message.service.d.ts.map +1 -0
  84. package/dist/backend/backend/src/services/slack/cross-machine-message.service.js +306 -0
  85. package/dist/backend/backend/src/services/slack/cross-machine-message.service.js.map +1 -0
  86. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts +7 -0
  87. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  88. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +76 -0
  89. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  90. package/dist/backend/backend/src/services/slack/slack.service.d.ts.map +1 -1
  91. package/dist/backend/backend/src/services/slack/slack.service.js +8 -2
  92. package/dist/backend/backend/src/services/slack/slack.service.js.map +1 -1
  93. package/dist/backend/backend/src/types/cross-machine.types.d.ts +103 -0
  94. package/dist/backend/backend/src/types/cross-machine.types.d.ts.map +1 -0
  95. package/dist/backend/backend/src/types/cross-machine.types.js +47 -0
  96. package/dist/backend/backend/src/types/cross-machine.types.js.map +1 -0
  97. package/dist/cli/backend/src/constants.d.ts +9 -0
  98. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  99. package/dist/cli/backend/src/constants.js +9 -0
  100. package/dist/cli/backend/src/constants.js.map +1 -1
  101. package/dist/cli/cli/src/commands/cloud.d.ts +18 -2
  102. package/dist/cli/cli/src/commands/cloud.d.ts.map +1 -1
  103. package/dist/cli/cli/src/commands/cloud.js +72 -16
  104. package/dist/cli/cli/src/commands/cloud.js.map +1 -1
  105. package/package.json +1 -1
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Type definitions for the Brand Onboarding flow.
3
+ *
4
+ * The onboarding flow collects brand information from SMB owners
5
+ * and generates a Brand Voice Guide used by marketing team agents.
6
+ *
7
+ * @module brand-onboarding-types
8
+ */
9
+ /** A single onboarding question with metadata. */
10
+ export interface OnboardingQuestion {
11
+ /** Unique question identifier */
12
+ id: string;
13
+ /** Question text displayed to the user */
14
+ text: string;
15
+ /** Input type determines UI rendering and validation */
16
+ type: 'text' | 'select' | 'multi-select';
17
+ /** Available options for select/multi-select types */
18
+ options?: string[];
19
+ /** Whether a response is required */
20
+ required: boolean;
21
+ /** Placeholder/hint text */
22
+ placeholder?: string;
23
+ /** Default value if not answered */
24
+ defaultValue?: string;
25
+ /** Order in the questionnaire (1-based) */
26
+ order: number;
27
+ }
28
+ /** User's answer to a single question. */
29
+ export interface OnboardingAnswer {
30
+ /** Matches OnboardingQuestion.id */
31
+ questionId: string;
32
+ /** User's response value */
33
+ value: string;
34
+ /** Timestamp of when the answer was provided */
35
+ answeredAt: string;
36
+ }
37
+ /** Status of the onboarding session. */
38
+ export type OnboardingStatus = 'not_started' | 'in_progress' | 'completed' | 'generating' | 'done' | 'failed';
39
+ /** A complete onboarding session tracking state. */
40
+ export interface OnboardingSession {
41
+ /** Unique session identifier */
42
+ id: string;
43
+ /** Team ID this onboarding belongs to */
44
+ teamId: string;
45
+ /** Template ID that triggered onboarding */
46
+ templateId: string;
47
+ /** Current status */
48
+ status: OnboardingStatus;
49
+ /** Collected answers so far */
50
+ answers: OnboardingAnswer[];
51
+ /** Index of the current question (0-based) */
52
+ currentQuestionIndex: number;
53
+ /** Total number of questions */
54
+ totalQuestions: number;
55
+ /** Generated Brand Voice Guide content (populated after completion) */
56
+ brandVoiceGuide?: string;
57
+ /** Path where the guide was saved */
58
+ guidePath?: string;
59
+ /** Session creation time */
60
+ createdAt: string;
61
+ /** Last update time */
62
+ updatedAt: string;
63
+ /** Error message if status is 'failed' */
64
+ error?: string;
65
+ }
66
+ /** Structured brand profile extracted from onboarding answers. */
67
+ export interface BrandProfile {
68
+ /** Business name */
69
+ businessName: string;
70
+ /** Industry/vertical */
71
+ industry: string;
72
+ /** One-sentence business description */
73
+ description: string;
74
+ /** Target customer description */
75
+ targetCustomer: string;
76
+ /** Top competitors */
77
+ competitors: string[];
78
+ /** Brand personality words (e.g., Professional, Friendly, Bold) */
79
+ personality: string[];
80
+ /** Content tone */
81
+ tone: 'formal' | 'casual' | 'playful' | 'authoritative';
82
+ /** Marketing goals */
83
+ goals: string[];
84
+ /** Active social platforms */
85
+ platforms: string[];
86
+ /** Example content URLs or descriptions */
87
+ contentExamples: string[];
88
+ }
89
+ /** Options for generating the Brand Voice Guide. */
90
+ export interface GenerateGuideOptions {
91
+ /** Brand profile from questionnaire answers */
92
+ profile: BrandProfile;
93
+ /** Output directory path for the guide file */
94
+ outputDir: string;
95
+ /** Whether to use LLM to enhance the guide with Do's/Don'ts */
96
+ useLLM?: boolean;
97
+ }
98
+ /** The 10 onboarding questions defined by the marketing template spec. */
99
+ export declare const ONBOARDING_QUESTIONS: OnboardingQuestion[];
100
+ /** Default content mix ratios for the Brand Voice Guide. */
101
+ export declare const DEFAULT_CONTENT_MIX: {
102
+ readonly educational: 60;
103
+ readonly behindTheScenes: 20;
104
+ readonly promotional: 15;
105
+ readonly interactive: 5;
106
+ };
107
+ //# sourceMappingURL=brand-onboarding.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-onboarding.types.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/onboarding/brand-onboarding.types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,kDAAkD;AAClD,MAAM,WAAW,kBAAkB;IACjC,iCAAiC;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,cAAc,CAAC;IACzC,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,qCAAqC;IACrC,QAAQ,EAAE,OAAO,CAAC;IAClB,4BAA4B;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,0CAA0C;AAC1C,MAAM,WAAW,gBAAgB;IAC/B,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wCAAwC;AACxC,MAAM,MAAM,gBAAgB,GACxB,aAAa,GACb,aAAa,GACb,WAAW,GACX,YAAY,GACZ,MAAM,GACN,QAAQ,CAAC;AAEb,oDAAoD;AACpD,MAAM,WAAW,iBAAiB;IAChC,gCAAgC;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,qBAAqB;IACrB,MAAM,EAAE,gBAAgB,CAAC;IACzB,+BAA+B;IAC/B,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,8CAA8C;IAC9C,oBAAoB,EAAE,MAAM,CAAC;IAC7B,gCAAgC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,oBAAoB;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,wBAAwB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,sBAAsB;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,mEAAmE;IACnE,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,mBAAmB;IACnB,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,eAAe,CAAC;IACxD,sBAAsB;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,8BAA8B;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,2CAA2C;IAC3C,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,oDAAoD;AACpD,MAAM,WAAW,oBAAoB;IACnC,+CAA+C;IAC/C,OAAO,EAAE,YAAY,CAAC;IACtB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAMD,0EAA0E;AAC1E,eAAO,MAAM,oBAAoB,EAAE,kBAAkB,EAmFpD,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,mBAAmB;;;;;CAKtB,CAAC"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Type definitions for the Brand Onboarding flow.
3
+ *
4
+ * The onboarding flow collects brand information from SMB owners
5
+ * and generates a Brand Voice Guide used by marketing team agents.
6
+ *
7
+ * @module brand-onboarding-types
8
+ */
9
+ // =============================================================================
10
+ // Constants
11
+ // =============================================================================
12
+ /** The 10 onboarding questions defined by the marketing template spec. */
13
+ export const ONBOARDING_QUESTIONS = [
14
+ {
15
+ id: 'business_name',
16
+ text: 'What is your business name?',
17
+ type: 'text',
18
+ required: true,
19
+ placeholder: 'e.g., Sunrise Bakery',
20
+ order: 1,
21
+ },
22
+ {
23
+ id: 'industry',
24
+ text: 'What industry are you in?',
25
+ type: 'text',
26
+ required: true,
27
+ placeholder: 'e.g., Fashion, Food, Fitness, Tech, Education',
28
+ order: 2,
29
+ },
30
+ {
31
+ id: 'description',
32
+ text: 'Describe your business in one sentence.',
33
+ type: 'text',
34
+ required: true,
35
+ placeholder: 'e.g., We make artisan sourdough bread for health-conscious families.',
36
+ order: 3,
37
+ },
38
+ {
39
+ id: 'target_customer',
40
+ text: 'Who is your target customer? (Age, interests, problems they have)',
41
+ type: 'text',
42
+ required: true,
43
+ placeholder: 'e.g., Health-conscious millennials, 25-40, interested in organic food',
44
+ order: 4,
45
+ },
46
+ {
47
+ id: 'competitors',
48
+ text: 'What are your top 3 competitors?',
49
+ type: 'text',
50
+ required: false,
51
+ placeholder: 'e.g., Blue Apron, HelloFresh, Local Bakery XYZ',
52
+ defaultValue: 'Not specified',
53
+ order: 5,
54
+ },
55
+ {
56
+ id: 'personality',
57
+ text: 'Describe your brand personality in 3 words.',
58
+ type: 'text',
59
+ required: true,
60
+ placeholder: 'e.g., Professional, Friendly, Bold',
61
+ order: 6,
62
+ },
63
+ {
64
+ id: 'tone',
65
+ text: 'What tone should your content have?',
66
+ type: 'select',
67
+ options: ['Formal', 'Casual', 'Playful', 'Authoritative'],
68
+ required: true,
69
+ order: 7,
70
+ },
71
+ {
72
+ id: 'goals',
73
+ text: 'What are your marketing goals?',
74
+ type: 'multi-select',
75
+ options: ['Brand awareness', 'Lead generation', 'Sales', 'Community building'],
76
+ required: true,
77
+ order: 8,
78
+ },
79
+ {
80
+ id: 'platforms',
81
+ text: 'Which platforms do you use?',
82
+ type: 'multi-select',
83
+ options: ['X (Twitter)', 'LinkedIn', 'Instagram', 'Facebook'],
84
+ required: true,
85
+ order: 9,
86
+ },
87
+ {
88
+ id: 'content_examples',
89
+ text: 'Share 2-3 examples of content you like (URLs or descriptions).',
90
+ type: 'text',
91
+ required: false,
92
+ placeholder: 'e.g., https://twitter.com/brand/status/123, "Wendy\'s snarky tone on Twitter"',
93
+ defaultValue: 'Not specified',
94
+ order: 10,
95
+ },
96
+ ];
97
+ /** Default content mix ratios for the Brand Voice Guide. */
98
+ export const DEFAULT_CONTENT_MIX = {
99
+ educational: 60,
100
+ behindTheScenes: 20,
101
+ promotional: 15,
102
+ interactive: 5,
103
+ };
104
+ //# sourceMappingURL=brand-onboarding.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-onboarding.types.js","sourceRoot":"","sources":["../../../../../../backend/src/services/onboarding/brand-onboarding.types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA+GH,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAEhF,0EAA0E;AAC1E,MAAM,CAAC,MAAM,oBAAoB,GAAyB;IACxD;QACE,EAAE,EAAE,eAAe;QACnB,IAAI,EAAE,6BAA6B;QACnC,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,sBAAsB;QACnC,KAAK,EAAE,CAAC;KACT;IACD;QACE,EAAE,EAAE,UAAU;QACd,IAAI,EAAE,2BAA2B;QACjC,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,+CAA+C;QAC5D,KAAK,EAAE,CAAC;KACT;IACD;QACE,EAAE,EAAE,aAAa;QACjB,IAAI,EAAE,yCAAyC;QAC/C,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,sEAAsE;QACnF,KAAK,EAAE,CAAC;KACT;IACD;QACE,EAAE,EAAE,iBAAiB;QACrB,IAAI,EAAE,mEAAmE;QACzE,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,uEAAuE;QACpF,KAAK,EAAE,CAAC;KACT;IACD;QACE,EAAE,EAAE,aAAa;QACjB,IAAI,EAAE,kCAAkC;QACxC,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,gDAAgD;QAC7D,YAAY,EAAE,eAAe;QAC7B,KAAK,EAAE,CAAC;KACT;IACD;QACE,EAAE,EAAE,aAAa;QACjB,IAAI,EAAE,6CAA6C;QACnD,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,oCAAoC;QACjD,KAAK,EAAE,CAAC;KACT;IACD;QACE,EAAE,EAAE,MAAM;QACV,IAAI,EAAE,qCAAqC;QAC3C,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,CAAC;QACzD,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,CAAC;KACT;IACD;QACE,EAAE,EAAE,OAAO;QACX,IAAI,EAAE,gCAAgC;QACtC,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,CAAC,iBAAiB,EAAE,iBAAiB,EAAE,OAAO,EAAE,oBAAoB,CAAC;QAC9E,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,CAAC;KACT;IACD;QACE,EAAE,EAAE,WAAW;QACf,IAAI,EAAE,6BAA6B;QACnC,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,CAAC,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,CAAC;QAC7D,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,CAAC;KACT;IACD;QACE,EAAE,EAAE,kBAAkB;QACtB,IAAI,EAAE,gEAAgE;QACtE,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,+EAA+E;QAC5F,YAAY,EAAE,eAAe;QAC7B,KAAK,EAAE,EAAE;KACV;CACF,CAAC;AAEF,4DAA4D;AAC5D,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,WAAW,EAAE,EAAE;IACf,eAAe,EAAE,EAAE;IACnB,WAAW,EAAE,EAAE;IACf,WAAW,EAAE,CAAC;CACN,CAAC"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Content Approval Service for Marketing Team templates.
3
+ *
4
+ * Manages the approval workflow where marketing agents submit content
5
+ * for human review. Content can be approved/rejected via:
6
+ * - Slack text replies ("approve" / "reject")
7
+ * - REST API endpoints
8
+ *
9
+ * This service is designed for the marketing team use case and is
10
+ * separate from the F27 ToolApprovalQueue (which handles tool execution).
11
+ *
12
+ * @module content-approval-service
13
+ */
14
+ import { ApprovalResolution, ContentApprovalRequest, ContentApprovalStatus, SubmitForApprovalOptions } from './content-approval.types.js';
15
+ /**
16
+ * Service managing content approval requests for marketing teams.
17
+ *
18
+ * Follows singleton pattern. Stores approvals in memory with optional
19
+ * Slack message tracking for interactive approval via text replies.
20
+ */
21
+ export declare class ContentApprovalService {
22
+ private static instance;
23
+ private approvals;
24
+ /**
25
+ * Get or create the singleton instance.
26
+ *
27
+ * @returns Singleton instance
28
+ */
29
+ static getInstance(): ContentApprovalService;
30
+ /**
31
+ * Reset the singleton (for testing).
32
+ */
33
+ static resetInstance(): void;
34
+ /**
35
+ * Submit content for approval.
36
+ *
37
+ * @param options - Content submission details
38
+ * @returns Created approval request
39
+ * @throws Error if team has too many pending approvals
40
+ */
41
+ submit(options: SubmitForApprovalOptions): ContentApprovalRequest;
42
+ /**
43
+ * Approve a content request.
44
+ *
45
+ * @param approvalId - ID of the approval request
46
+ * @param resolvedBy - Who approved (user ID or identifier)
47
+ * @param feedback - Optional feedback or notes
48
+ * @returns Updated approval request or null if not found
49
+ */
50
+ approve(approvalId: string, resolvedBy: string, feedback?: string): ContentApprovalRequest | null;
51
+ /**
52
+ * Reject a content request.
53
+ *
54
+ * @param approvalId - ID of the approval request
55
+ * @param resolvedBy - Who rejected
56
+ * @param feedback - Reason for rejection or edit instructions
57
+ * @returns Updated approval request or null if not found
58
+ */
59
+ reject(approvalId: string, resolvedBy: string, feedback?: string): ContentApprovalRequest | null;
60
+ /**
61
+ * Resolve an approval request (approve or reject).
62
+ *
63
+ * @param resolution - Resolution details
64
+ * @returns Updated request or null
65
+ */
66
+ resolve(resolution: ApprovalResolution): ContentApprovalRequest | null;
67
+ /**
68
+ * Get a specific approval request.
69
+ *
70
+ * @param approvalId - Approval ID
71
+ * @returns The request or null
72
+ */
73
+ get(approvalId: string): ContentApprovalRequest | null;
74
+ /**
75
+ * Get all pending approvals for a team.
76
+ *
77
+ * @param teamId - Team ID
78
+ * @returns Array of pending approval requests
79
+ */
80
+ getPendingByTeam(teamId: string): ContentApprovalRequest[];
81
+ /**
82
+ * Get all approvals for a team (all statuses).
83
+ *
84
+ * @param teamId - Team ID
85
+ * @returns Array of all approval requests
86
+ */
87
+ getAllByTeam(teamId: string): ContentApprovalRequest[];
88
+ /**
89
+ * Get approval by Slack message timestamp (for Slack reply-based approval).
90
+ *
91
+ * @param channelId - Slack channel ID
92
+ * @param messageTs - Slack message timestamp
93
+ * @returns Matching approval request or null
94
+ */
95
+ getBySlackMessage(channelId: string, messageTs: string): ContentApprovalRequest | null;
96
+ /**
97
+ * Link a Slack message to an approval request (for tracking replies).
98
+ *
99
+ * @param approvalId - Approval ID
100
+ * @param channelId - Slack channel where the approval message was posted
101
+ * @param messageTs - Slack message timestamp
102
+ * @returns Updated request or null
103
+ */
104
+ linkSlackMessage(approvalId: string, channelId: string, messageTs: string): ContentApprovalRequest | null;
105
+ /**
106
+ * Get counts by status for a team.
107
+ *
108
+ * @param teamId - Team ID
109
+ * @returns Object with status counts
110
+ */
111
+ getStats(teamId: string): Record<ContentApprovalStatus, number>;
112
+ /**
113
+ * Format an approval request as a Slack message (plain text for MVP).
114
+ *
115
+ * @param request - The approval request to format
116
+ * @returns Formatted message string ready for Slack
117
+ */
118
+ formatForSlack(request: ContentApprovalRequest): string;
119
+ /**
120
+ * Expire old pending approvals past the TTL.
121
+ */
122
+ private expireOldApprovals;
123
+ }
124
+ //# sourceMappingURL=content-approval.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-approval.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/onboarding/content-approval.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EAGrB,wBAAwB,EACzB,MAAM,6BAA6B,CAAC;AAMrC;;;;;GAKG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAuC;IAC9D,OAAO,CAAC,SAAS,CAAkD;IAEnE;;;;OAIG;IACH,MAAM,CAAC,WAAW,IAAI,sBAAsB;IAO5C;;OAEG;IACH,MAAM,CAAC,aAAa,IAAI,IAAI;IAQ5B;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,EAAE,wBAAwB,GAAG,sBAAsB;IAgCjE;;;;;;;OAOG;IACH,OAAO,CACL,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,GAChB,sBAAsB,GAAG,IAAI;IAIhC;;;;;;;OAOG;IACH,MAAM,CACJ,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,GAChB,sBAAsB,GAAG,IAAI;IAIhC;;;;;OAKG;IACH,OAAO,CAAC,UAAU,EAAE,kBAAkB,GAAG,sBAAsB,GAAG,IAAI;IAkBtE;;;;;OAKG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,sBAAsB,GAAG,IAAI;IAItD;;;;;OAKG;IACH,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,sBAAsB,EAAE;IAO1D;;;;;OAKG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,sBAAsB,EAAE;IAMtD;;;;;;OAMG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,sBAAsB,GAAG,IAAI;IAStF;;;;;;;OAOG;IACH,gBAAgB,CACd,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,sBAAsB,GAAG,IAAI;IAUhC;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAAC;IAmB/D;;;;;OAKG;IACH,cAAc,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM;IAiCvD;;OAEG;IACH,OAAO,CAAC,kBAAkB;CAa3B"}
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Content Approval Service for Marketing Team templates.
3
+ *
4
+ * Manages the approval workflow where marketing agents submit content
5
+ * for human review. Content can be approved/rejected via:
6
+ * - Slack text replies ("approve" / "reject")
7
+ * - REST API endpoints
8
+ *
9
+ * This service is designed for the marketing team use case and is
10
+ * separate from the F27 ToolApprovalQueue (which handles tool execution).
11
+ *
12
+ * @module content-approval-service
13
+ */
14
+ import { randomUUID } from 'crypto';
15
+ import { CONTENT_APPROVAL_TTL_MS, MAX_PENDING_APPROVALS, } from './content-approval.types.js';
16
+ // =============================================================================
17
+ // Service
18
+ // =============================================================================
19
+ /**
20
+ * Service managing content approval requests for marketing teams.
21
+ *
22
+ * Follows singleton pattern. Stores approvals in memory with optional
23
+ * Slack message tracking for interactive approval via text replies.
24
+ */
25
+ export class ContentApprovalService {
26
+ static instance = null;
27
+ approvals = new Map();
28
+ /**
29
+ * Get or create the singleton instance.
30
+ *
31
+ * @returns Singleton instance
32
+ */
33
+ static getInstance() {
34
+ if (!ContentApprovalService.instance) {
35
+ ContentApprovalService.instance = new ContentApprovalService();
36
+ }
37
+ return ContentApprovalService.instance;
38
+ }
39
+ /**
40
+ * Reset the singleton (for testing).
41
+ */
42
+ static resetInstance() {
43
+ ContentApprovalService.instance = null;
44
+ }
45
+ // ---------------------------------------------------------------------------
46
+ // Submission
47
+ // ---------------------------------------------------------------------------
48
+ /**
49
+ * Submit content for approval.
50
+ *
51
+ * @param options - Content submission details
52
+ * @returns Created approval request
53
+ * @throws Error if team has too many pending approvals
54
+ */
55
+ submit(options) {
56
+ // Enforce max pending limit
57
+ this.expireOldApprovals();
58
+ const teamPending = this.getPendingByTeam(options.teamId);
59
+ if (teamPending.length >= MAX_PENDING_APPROVALS) {
60
+ throw new Error(`Team ${options.teamId} has ${teamPending.length} pending approvals (max: ${MAX_PENDING_APPROVALS}). Resolve some before submitting more.`);
61
+ }
62
+ const request = {
63
+ id: randomUUID(),
64
+ teamId: options.teamId,
65
+ submittedBy: options.submittedBy,
66
+ platform: options.platform,
67
+ contentType: options.contentType,
68
+ content: options.content,
69
+ hashtags: options.hashtags,
70
+ visualDirection: options.visualDirection,
71
+ scheduledTime: options.scheduledTime,
72
+ status: 'pending',
73
+ submittedAt: new Date().toISOString(),
74
+ };
75
+ this.approvals.set(request.id, request);
76
+ return request;
77
+ }
78
+ // ---------------------------------------------------------------------------
79
+ // Resolution
80
+ // ---------------------------------------------------------------------------
81
+ /**
82
+ * Approve a content request.
83
+ *
84
+ * @param approvalId - ID of the approval request
85
+ * @param resolvedBy - Who approved (user ID or identifier)
86
+ * @param feedback - Optional feedback or notes
87
+ * @returns Updated approval request or null if not found
88
+ */
89
+ approve(approvalId, resolvedBy, feedback) {
90
+ return this.resolve({ approvalId, status: 'approved', resolvedBy, feedback });
91
+ }
92
+ /**
93
+ * Reject a content request.
94
+ *
95
+ * @param approvalId - ID of the approval request
96
+ * @param resolvedBy - Who rejected
97
+ * @param feedback - Reason for rejection or edit instructions
98
+ * @returns Updated approval request or null if not found
99
+ */
100
+ reject(approvalId, resolvedBy, feedback) {
101
+ return this.resolve({ approvalId, status: 'rejected', resolvedBy, feedback });
102
+ }
103
+ /**
104
+ * Resolve an approval request (approve or reject).
105
+ *
106
+ * @param resolution - Resolution details
107
+ * @returns Updated request or null
108
+ */
109
+ resolve(resolution) {
110
+ const request = this.approvals.get(resolution.approvalId);
111
+ if (!request)
112
+ return null;
113
+ if (request.status !== 'pending')
114
+ return request; // Already resolved
115
+ request.status = resolution.status;
116
+ request.resolvedAt = new Date().toISOString();
117
+ request.resolvedBy = resolution.resolvedBy;
118
+ request.feedback = resolution.feedback;
119
+ this.approvals.set(request.id, request);
120
+ return request;
121
+ }
122
+ // ---------------------------------------------------------------------------
123
+ // Queries
124
+ // ---------------------------------------------------------------------------
125
+ /**
126
+ * Get a specific approval request.
127
+ *
128
+ * @param approvalId - Approval ID
129
+ * @returns The request or null
130
+ */
131
+ get(approvalId) {
132
+ return this.approvals.get(approvalId) ?? null;
133
+ }
134
+ /**
135
+ * Get all pending approvals for a team.
136
+ *
137
+ * @param teamId - Team ID
138
+ * @returns Array of pending approval requests
139
+ */
140
+ getPendingByTeam(teamId) {
141
+ this.expireOldApprovals();
142
+ return Array.from(this.approvals.values()).filter(r => r.teamId === teamId && r.status === 'pending');
143
+ }
144
+ /**
145
+ * Get all approvals for a team (all statuses).
146
+ *
147
+ * @param teamId - Team ID
148
+ * @returns Array of all approval requests
149
+ */
150
+ getAllByTeam(teamId) {
151
+ return Array.from(this.approvals.values()).filter(r => r.teamId === teamId);
152
+ }
153
+ /**
154
+ * Get approval by Slack message timestamp (for Slack reply-based approval).
155
+ *
156
+ * @param channelId - Slack channel ID
157
+ * @param messageTs - Slack message timestamp
158
+ * @returns Matching approval request or null
159
+ */
160
+ getBySlackMessage(channelId, messageTs) {
161
+ for (const request of this.approvals.values()) {
162
+ if (request.slackChannelId === channelId && request.slackMessageTs === messageTs) {
163
+ return request;
164
+ }
165
+ }
166
+ return null;
167
+ }
168
+ /**
169
+ * Link a Slack message to an approval request (for tracking replies).
170
+ *
171
+ * @param approvalId - Approval ID
172
+ * @param channelId - Slack channel where the approval message was posted
173
+ * @param messageTs - Slack message timestamp
174
+ * @returns Updated request or null
175
+ */
176
+ linkSlackMessage(approvalId, channelId, messageTs) {
177
+ const request = this.approvals.get(approvalId);
178
+ if (!request)
179
+ return null;
180
+ request.slackChannelId = channelId;
181
+ request.slackMessageTs = messageTs;
182
+ this.approvals.set(request.id, request);
183
+ return request;
184
+ }
185
+ /**
186
+ * Get counts by status for a team.
187
+ *
188
+ * @param teamId - Team ID
189
+ * @returns Object with status counts
190
+ */
191
+ getStats(teamId) {
192
+ const all = this.getAllByTeam(teamId);
193
+ const stats = {
194
+ pending: 0,
195
+ approved: 0,
196
+ rejected: 0,
197
+ expired: 0,
198
+ };
199
+ for (const r of all) {
200
+ stats[r.status] = (stats[r.status] || 0) + 1;
201
+ }
202
+ return stats;
203
+ }
204
+ // ---------------------------------------------------------------------------
205
+ // Formatting
206
+ // ---------------------------------------------------------------------------
207
+ /**
208
+ * Format an approval request as a Slack message (plain text for MVP).
209
+ *
210
+ * @param request - The approval request to format
211
+ * @returns Formatted message string ready for Slack
212
+ */
213
+ formatForSlack(request) {
214
+ const lines = [
215
+ `*Content Approval Request* (ID: \`${request.id.slice(0, 8)}\`)`,
216
+ '',
217
+ `*Platform:* ${request.platform}`,
218
+ `*Type:* ${request.contentType}`,
219
+ `*Submitted by:* ${request.submittedBy}`,
220
+ '',
221
+ '---',
222
+ request.content,
223
+ '---',
224
+ ];
225
+ if (request.hashtags?.length) {
226
+ lines.push(`*Hashtags:* ${request.hashtags.join(' ')}`);
227
+ }
228
+ if (request.visualDirection) {
229
+ lines.push(`*Visual Direction:* ${request.visualDirection}`);
230
+ }
231
+ if (request.scheduledTime) {
232
+ lines.push(`*Scheduled:* ${request.scheduledTime}`);
233
+ }
234
+ lines.push('');
235
+ lines.push('Reply with *approve* or *reject [reason]* to this message.');
236
+ return lines.join('\n');
237
+ }
238
+ // ---------------------------------------------------------------------------
239
+ // Private Helpers
240
+ // ---------------------------------------------------------------------------
241
+ /**
242
+ * Expire old pending approvals past the TTL.
243
+ */
244
+ expireOldApprovals() {
245
+ const now = Date.now();
246
+ for (const [id, request] of this.approvals.entries()) {
247
+ if (request.status === 'pending' &&
248
+ now - new Date(request.submittedAt).getTime() > CONTENT_APPROVAL_TTL_MS) {
249
+ request.status = 'expired';
250
+ request.resolvedAt = new Date().toISOString();
251
+ this.approvals.set(id, request);
252
+ }
253
+ }
254
+ }
255
+ }
256
+ //# sourceMappingURL=content-approval.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-approval.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/onboarding/content-approval.service.ts"],"names":[],"mappings":"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"}