fraim 2.0.165 → 2.0.167
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/src/ai-hub/catalog.js +20 -27
- package/dist/src/ai-hub/server.js +418 -2
- package/dist/src/ai-hub/word-sideload.js +95 -0
- package/dist/src/cli/commands/org.js +40 -0
- package/dist/src/cli/commands/test-mcp.js +171 -0
- package/dist/src/cli/fraim.js +2 -0
- package/dist/src/cli/setup/first-run.js +242 -0
- package/dist/src/cli/utils/org-publish.js +98 -0
- package/dist/src/config/ai-manager-hiring.js +121 -0
- package/dist/src/config/compat.js +16 -0
- package/dist/src/config/feature-flags.js +25 -0
- package/dist/src/config/persona-capability-bundles.js +273 -0
- package/dist/src/config/persona-hiring.js +270 -0
- package/dist/src/config/portfolio-slug-overrides.js +17 -0
- package/dist/src/config/pricing.js +37 -0
- package/dist/src/config/stripe.js +43 -0
- package/dist/src/core/config-loader.js +9 -5
- package/dist/src/core/config-writer.js +75 -0
- package/dist/src/core/fraim-config-schema.generated.js +0 -21
- package/dist/src/core/utils/job-aliases.js +47 -0
- package/dist/src/core/utils/local-registry-resolver.js +8 -1
- package/dist/src/core/utils/workflow-parser.js +174 -0
- package/index.js +1 -1
- package/package.json +5 -1
- package/public/ai-hub/index.html +81 -0
- package/public/ai-hub/powerpoint-taskpane/index.html +236 -236
- package/public/ai-hub/powerpoint-taskpane/manifest.xml +29 -29
- package/public/ai-hub/review.css +13 -0
- package/public/ai-hub/script.js +414 -4
- package/public/ai-hub/styles.css +56 -0
- package/public/first-run/styles.css +73 -73
- package/public/portfolio/ashley.html +523 -0
- package/public/portfolio/auditya.html +83 -0
- package/public/portfolio/banke.html +83 -0
- package/public/portfolio/beza.html +659 -0
- package/public/portfolio/careena.html +632 -0
- package/public/portfolio/casey.html +568 -0
- package/public/portfolio/celia.html +490 -0
- package/public/portfolio/deidre.html +642 -0
- package/public/portfolio/gautam.html +597 -0
- package/public/portfolio/hari.html +469 -0
- package/public/portfolio/huxley.html +1354 -0
- package/public/portfolio/index.html +741 -0
- package/public/portfolio/maestro.html +518 -0
- package/public/portfolio/mandy.html +590 -0
- package/public/portfolio/mona.html +597 -0
- package/public/portfolio/pam.html +887 -0
- package/public/portfolio/procella.html +107 -0
- package/public/portfolio/qasm.html +569 -0
- package/public/portfolio/ricardo.html +489 -0
- package/public/portfolio/sade.html +560 -0
- package/public/portfolio/sam.html +654 -0
- package/public/portfolio/sechar.html +580 -0
- package/public/portfolio/sreya.html +599 -0
- package/public/portfolio/swen.html +601 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PERSONA_AVATAR_CATALOG = exports.PERSONA_HIRE_CATALOG = void 0;
|
|
4
|
+
exports.buildPersonaAvatarUrl = buildPersonaAvatarUrl;
|
|
5
|
+
exports.isPersonaHireKey = isPersonaHireKey;
|
|
6
|
+
exports.getPersonaHireAmountCents = getPersonaHireAmountCents;
|
|
7
|
+
/**
|
|
8
|
+
* PERSONA_HIRE_CATALOG — single source of truth for all AI employee identity,
|
|
9
|
+
* pricing, and card-display fields.
|
|
10
|
+
*
|
|
11
|
+
* Fields added in issue #545:
|
|
12
|
+
* emoji — avatar icon rendered on persona cards
|
|
13
|
+
* gradient — CSS gradient string for card background accent
|
|
14
|
+
* blurb — 1-2 sentence marketing description shown on persona cards
|
|
15
|
+
*
|
|
16
|
+
* These were previously hard-coded in fraim-pro/index.html and
|
|
17
|
+
* fraim-pro/pricing.html. Moving them here makes this catalog the single
|
|
18
|
+
* authoritative record for everything a card needs to render, served via
|
|
19
|
+
* GET /api/personas/catalog.
|
|
20
|
+
*/
|
|
21
|
+
exports.PERSONA_HIRE_CATALOG = {
|
|
22
|
+
maestro: {
|
|
23
|
+
displayName: 'MAESTRO',
|
|
24
|
+
role: 'Full-Brained AI Employee',
|
|
25
|
+
emoji: '★',
|
|
26
|
+
gradient: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #d946ef 100%)',
|
|
27
|
+
blurb: 'One AI employee who can take a job from any function and ship it back with evidence. You set the direction. You sign off on what ships. Maestro does the work.',
|
|
28
|
+
jobPriceCents: 24900, // $249 — full-time only; job mode maps to same price
|
|
29
|
+
fulltimePriceCents: 24900, // $249/mo
|
|
30
|
+
},
|
|
31
|
+
beza: {
|
|
32
|
+
displayName: 'BeZa',
|
|
33
|
+
role: 'AI Business Strategist',
|
|
34
|
+
emoji: '🧭',
|
|
35
|
+
gradient: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
|
36
|
+
blurb: 'Turns ideas into structured business plans, validates founder-market fit, and pressure-tests strategy.',
|
|
37
|
+
jobPriceCents: 990,
|
|
38
|
+
fulltimePriceCents: 4990,
|
|
39
|
+
},
|
|
40
|
+
pam: {
|
|
41
|
+
displayName: 'PaM',
|
|
42
|
+
role: 'AI Product Manager',
|
|
43
|
+
emoji: '📋',
|
|
44
|
+
gradient: 'linear-gradient(135deg, #8b5cf6 0%, #d946ef 100%)',
|
|
45
|
+
blurb: 'Owns specs, PRDs, technical design, issue prep, and the path from idea to shippable artifact.',
|
|
46
|
+
jobPriceCents: 790,
|
|
47
|
+
fulltimePriceCents: 4990,
|
|
48
|
+
},
|
|
49
|
+
swen: {
|
|
50
|
+
displayName: 'SWEn',
|
|
51
|
+
role: 'AI Software Engineer',
|
|
52
|
+
emoji: '💻',
|
|
53
|
+
gradient: 'linear-gradient(135deg, #2563eb 0%, #06b6d4 100%)',
|
|
54
|
+
blurb: 'Implements features, refactors code, and drives PR iteration to merge, reviewing design and code as a senior would.',
|
|
55
|
+
jobPriceCents: 1290,
|
|
56
|
+
fulltimePriceCents: 5990,
|
|
57
|
+
},
|
|
58
|
+
qasm: {
|
|
59
|
+
displayName: 'QAsm',
|
|
60
|
+
role: 'AI QA Engineer',
|
|
61
|
+
emoji: '🛡️',
|
|
62
|
+
gradient: 'linear-gradient(135deg, #10b981 0%, #14b8a6 100%)',
|
|
63
|
+
blurb: 'Runs tests, assesses code and test quality, polishes UI, and drives bug bashes with evidence.',
|
|
64
|
+
jobPriceCents: 590,
|
|
65
|
+
fulltimePriceCents: 3990,
|
|
66
|
+
},
|
|
67
|
+
huxley: {
|
|
68
|
+
displayName: 'hUXley',
|
|
69
|
+
role: 'AI UX / Brand Designer',
|
|
70
|
+
emoji: '🎨',
|
|
71
|
+
gradient: 'linear-gradient(135deg, #ec4899 0%, #f472b6 100%)',
|
|
72
|
+
blurb: 'Builds design systems, prototypes polished user-facing surfaces, and carries brand decisions into shipped product experiences.',
|
|
73
|
+
jobPriceCents: 1490,
|
|
74
|
+
fulltimePriceCents: 5490,
|
|
75
|
+
},
|
|
76
|
+
gautam: {
|
|
77
|
+
displayName: 'GauTaM',
|
|
78
|
+
role: 'AI GTM & Marketing Manager',
|
|
79
|
+
emoji: '📣',
|
|
80
|
+
gradient: 'linear-gradient(135deg, #f97316 0%, #f59e0b 100%)',
|
|
81
|
+
blurb: 'Defines marketing strategy, ships content, runs launches, and owns the brand voice in market.',
|
|
82
|
+
jobPriceCents: 890,
|
|
83
|
+
fulltimePriceCents: 4990,
|
|
84
|
+
},
|
|
85
|
+
cela: {
|
|
86
|
+
displayName: 'CELiA',
|
|
87
|
+
role: 'AI Legal Counsel',
|
|
88
|
+
emoji: '⚖️',
|
|
89
|
+
gradient: 'linear-gradient(135deg, #475569 0%, #6366f1 100%)',
|
|
90
|
+
blurb: 'Drafts and reviews contracts, NDAs, patents, trademarks, and the SaaS legal stack.',
|
|
91
|
+
jobPriceCents: 1990,
|
|
92
|
+
fulltimePriceCents: 6990,
|
|
93
|
+
},
|
|
94
|
+
sekhar: {
|
|
95
|
+
displayName: 'SEChar',
|
|
96
|
+
role: 'AI Security Engineer',
|
|
97
|
+
emoji: '🔒',
|
|
98
|
+
gradient: 'linear-gradient(135deg, #ef4444 0%, #f43f5e 100%)',
|
|
99
|
+
blurb: 'Sets up AI-native security baselines, runs the findings command center, reviews changes for risk, and drives remediation to closure.',
|
|
100
|
+
jobPriceCents: 1790,
|
|
101
|
+
fulltimePriceCents: 6490,
|
|
102
|
+
},
|
|
103
|
+
ashley: {
|
|
104
|
+
displayName: 'AshLey',
|
|
105
|
+
role: 'AI Executive Assistant',
|
|
106
|
+
emoji: '📅',
|
|
107
|
+
gradient: 'linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%)',
|
|
108
|
+
blurb: 'Owns executive coordination, weekly operating reviews, and portfolio reporting across the workforce.',
|
|
109
|
+
jobPriceCents: 490,
|
|
110
|
+
fulltimePriceCents: 2990,
|
|
111
|
+
},
|
|
112
|
+
mandy: {
|
|
113
|
+
displayName: 'MANdy',
|
|
114
|
+
role: 'AI Manager',
|
|
115
|
+
emoji: '🎯',
|
|
116
|
+
gradient: 'linear-gradient(135deg, #7c3aed 0%, #4338ca 100%)',
|
|
117
|
+
blurb: 'Plans the job sequence, runs sub-agents in parallel, coaches them through verification loops, and hands back a synthesized DRAFT for your approval.',
|
|
118
|
+
jobPriceCents: 1490,
|
|
119
|
+
fulltimePriceCents: 5990,
|
|
120
|
+
},
|
|
121
|
+
ricardo: {
|
|
122
|
+
displayName: 'RECardo',
|
|
123
|
+
role: 'AI Recruiter',
|
|
124
|
+
emoji: '🤝',
|
|
125
|
+
gradient: 'linear-gradient(135deg, #6366f1 0%, #ec4899 100%)',
|
|
126
|
+
blurb: 'Sources candidates, writes job descriptions, screens pipelines, and manages the hiring loop end-to-end.',
|
|
127
|
+
jobPriceCents: 790,
|
|
128
|
+
fulltimePriceCents: 4490,
|
|
129
|
+
},
|
|
130
|
+
hari: {
|
|
131
|
+
displayName: 'HaRi',
|
|
132
|
+
role: 'AI HR Manager',
|
|
133
|
+
emoji: '👥',
|
|
134
|
+
gradient: 'linear-gradient(135deg, #0d9488 0%, #059669 100%)',
|
|
135
|
+
blurb: 'Manages onboarding, performance reviews, benefits analysis, payroll coordination, and HR business-partner advisory.',
|
|
136
|
+
jobPriceCents: 790,
|
|
137
|
+
fulltimePriceCents: 4490,
|
|
138
|
+
},
|
|
139
|
+
careena: {
|
|
140
|
+
displayName: 'CAREEna',
|
|
141
|
+
role: 'AI Career Coach',
|
|
142
|
+
emoji: '🎓',
|
|
143
|
+
gradient: 'linear-gradient(135deg, #0ea5e9 0%, #6366f1 100%)',
|
|
144
|
+
blurb: 'Runs the candidate-side search loop: role sourcing, application execution, networking, interview prep, and close-stage offer strategy.',
|
|
145
|
+
jobPriceCents: 890,
|
|
146
|
+
fulltimePriceCents: 4990,
|
|
147
|
+
},
|
|
148
|
+
sade: {
|
|
149
|
+
displayName: 'SADE',
|
|
150
|
+
role: 'AI Salesforce Developer',
|
|
151
|
+
emoji: '☁️',
|
|
152
|
+
gradient: 'linear-gradient(135deg, #0284c7 0%, #0369a1 100%)',
|
|
153
|
+
blurb: 'Deploys Salesforce configuration from ServiceNow tickets, builds reports and dashboards, creates Flows from business requirements, audits org health, and manages users and data at scale.',
|
|
154
|
+
jobPriceCents: 1490,
|
|
155
|
+
fulltimePriceCents: 6490,
|
|
156
|
+
},
|
|
157
|
+
sam: {
|
|
158
|
+
displayName: 'SAM',
|
|
159
|
+
role: 'AI Sales Account Manager',
|
|
160
|
+
emoji: '📈',
|
|
161
|
+
gradient: 'linear-gradient(135deg, #059669 0%, #0d9488 100%)',
|
|
162
|
+
blurb: 'Surfaces stalled deals, computes pipeline health, and drafts account-specific re-engagement proposals for your top at-risk opportunities.',
|
|
163
|
+
jobPriceCents: 1290,
|
|
164
|
+
fulltimePriceCents: 5990,
|
|
165
|
+
},
|
|
166
|
+
casey: {
|
|
167
|
+
displayName: 'CaSey',
|
|
168
|
+
role: 'AI Customer Success + Support',
|
|
169
|
+
emoji: '💬',
|
|
170
|
+
gradient: 'linear-gradient(135deg, #db2777 0%, #9333ea 100%)',
|
|
171
|
+
blurb: 'Scores account churn risk and expansion potential, generates prioritized action plans for CSMs, triages support cases, and routes L1 cases through automated resolution.',
|
|
172
|
+
jobPriceCents: 990,
|
|
173
|
+
fulltimePriceCents: 4990,
|
|
174
|
+
},
|
|
175
|
+
deidre: {
|
|
176
|
+
displayName: 'DEIdre',
|
|
177
|
+
role: 'AI Inclusion Leader',
|
|
178
|
+
emoji: '🌍',
|
|
179
|
+
gradient: 'linear-gradient(135deg, #9333ea 0%, #d946ef 100%)',
|
|
180
|
+
blurb: 'Audits equity gaps, designs bias-aware AI governance, builds ERG toolkits, and creates inclusion-fluency programs.',
|
|
181
|
+
jobPriceCents: 890,
|
|
182
|
+
fulltimePriceCents: 4490,
|
|
183
|
+
},
|
|
184
|
+
mona: {
|
|
185
|
+
displayName: 'MONa',
|
|
186
|
+
role: 'AI Finance Manager',
|
|
187
|
+
emoji: '💰',
|
|
188
|
+
gradient: 'linear-gradient(135deg, #10b981 0%, #f59e0b 100%)',
|
|
189
|
+
blurb: 'Models revenue, tracks unit economics, builds financial forecasts, and owns the metrics that drive growth decisions.',
|
|
190
|
+
jobPriceCents: 990,
|
|
191
|
+
fulltimePriceCents: 4990,
|
|
192
|
+
},
|
|
193
|
+
sreya: {
|
|
194
|
+
displayName: 'SREya',
|
|
195
|
+
role: 'AI Site Reliability Engineer',
|
|
196
|
+
emoji: '☁️',
|
|
197
|
+
gradient: 'linear-gradient(135deg, #2563eb 0%, #10b981 100%)',
|
|
198
|
+
blurb: 'Manages deployments, monitors uptime, optimizes cloud cost, and keeps infrastructure resilient and observable.',
|
|
199
|
+
jobPriceCents: 1290,
|
|
200
|
+
fulltimePriceCents: 5990,
|
|
201
|
+
},
|
|
202
|
+
procella: {
|
|
203
|
+
displayName: 'PROCella',
|
|
204
|
+
role: 'AI Procurement Manager',
|
|
205
|
+
emoji: '📦',
|
|
206
|
+
gradient: 'linear-gradient(135deg, #0f766e 0%, #7c3aed 100%)',
|
|
207
|
+
blurb: 'Frames procurement strategy, sources suppliers, runs RFx packages, evaluates responses, and keeps purchases acceptance-gated.',
|
|
208
|
+
jobPriceCents: 990,
|
|
209
|
+
fulltimePriceCents: 4990,
|
|
210
|
+
},
|
|
211
|
+
banke: {
|
|
212
|
+
displayName: 'BANKe',
|
|
213
|
+
role: 'AI Banking KYC Employee',
|
|
214
|
+
emoji: '\u{1F3E6}',
|
|
215
|
+
gradient: 'linear-gradient(135deg, #0f766e 0%, #2563eb 100%)',
|
|
216
|
+
blurb: 'Runs banking KYC cases with consent-aware evidence capture, decision receipts, and stable audit handoff artifacts.',
|
|
217
|
+
jobPriceCents: 1490,
|
|
218
|
+
fulltimePriceCents: 6490,
|
|
219
|
+
},
|
|
220
|
+
auditya: {
|
|
221
|
+
displayName: 'AUDITya',
|
|
222
|
+
role: 'AI Banking Auditor',
|
|
223
|
+
emoji: '\u{1F50E}',
|
|
224
|
+
gradient: 'linear-gradient(135deg, #7c3aed 0%, #0f172a 100%)',
|
|
225
|
+
blurb: 'Audits AI banking work by tracing decisions back to evidence, receipts, exceptions, and reusable audit reports.',
|
|
226
|
+
jobPriceCents: 1490,
|
|
227
|
+
fulltimePriceCents: 6490,
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
exports.PERSONA_AVATAR_CATALOG = {
|
|
231
|
+
maestro: { seed: 'MAESTRO-founder-mode', bg: 'fde68a', style: 'notionists' },
|
|
232
|
+
beza: { seed: 'BEZA-strategist', bg: 'c7d2fe', style: 'notionists' },
|
|
233
|
+
pam: { seed: 'PAM-product', bg: 'ddd6fe', style: 'notionists' },
|
|
234
|
+
swen: { seed: 'SWEN-engineer', bg: 'bfdbfe', style: 'notionists' },
|
|
235
|
+
qasm: { seed: 'QASM-quality', bg: 'a7f3d0', style: 'notionists' },
|
|
236
|
+
huxley: { seed: 'HUXLEY-design', bg: 'fbcfe8', style: 'notionists' },
|
|
237
|
+
gautam: { seed: 'Gautam-marketing', bg: 'fed7aa', style: 'notionists' },
|
|
238
|
+
cela: { seed: 'CELA-legal', bg: 'cbd5e1', style: 'notionists' },
|
|
239
|
+
sekhar: { seed: 'SEKHAR-security', bg: 'fecaca', style: 'notionists' },
|
|
240
|
+
ashley: { seed: 'Ashley-assistant', bg: 'fde68a', style: 'notionists' },
|
|
241
|
+
mandy: { seed: 'MANDY-manager', bg: 'ede9fe', style: 'notionists' },
|
|
242
|
+
ricardo: { seed: 'RICARDO-recruiter', bg: 'ede9fe', style: 'notionists' },
|
|
243
|
+
hari: { seed: 'HARI-hr', bg: 'ccfbf1', style: 'notionists' },
|
|
244
|
+
careena: { seed: 'CAREENA-career-coach', bg: 'e0f2fe', style: 'notionists' },
|
|
245
|
+
sade: { seed: 'SADE-salesforce', bg: 'bae6fd', style: 'notionists' },
|
|
246
|
+
sam: { seed: 'SAM-sales-account', bg: 'd1fae5', style: 'notionists' },
|
|
247
|
+
casey: { seed: 'CASEY-cx', bg: 'a5f3fc', style: 'notionists' },
|
|
248
|
+
deidre: { seed: 'DEIDRE-inclusion-leader', bg: 'e9d5ff', style: 'notionists' },
|
|
249
|
+
mona: { seed: 'MONA-finance', bg: 'd1fae5', style: 'notionists' },
|
|
250
|
+
sreya: { seed: 'SREYA-sre-cloud', bg: 'bbf7d0', style: 'notionists' },
|
|
251
|
+
procella: { seed: 'PROCELLA-procurement', bg: 'ccfbf1', style: 'notionists' },
|
|
252
|
+
banke: { seed: 'BANKe-banking-kyc', bg: 'ccfbf1', style: 'notionists' },
|
|
253
|
+
auditya: { seed: 'AUDITya-banking-audit', bg: 'e9d5ff', style: 'notionists' },
|
|
254
|
+
};
|
|
255
|
+
function buildPersonaAvatarUrl(personaKey) {
|
|
256
|
+
const avatar = exports.PERSONA_AVATAR_CATALOG[personaKey];
|
|
257
|
+
const params = new URLSearchParams({
|
|
258
|
+
seed: avatar.seed,
|
|
259
|
+
backgroundColor: avatar.bg,
|
|
260
|
+
radius: '50',
|
|
261
|
+
});
|
|
262
|
+
return `https://api.dicebear.com/9.x/${avatar.style}/svg?${params.toString()}`;
|
|
263
|
+
}
|
|
264
|
+
function isPersonaHireKey(value) {
|
|
265
|
+
return value in exports.PERSONA_HIRE_CATALOG;
|
|
266
|
+
}
|
|
267
|
+
function getPersonaHireAmountCents(personaKey, mode) {
|
|
268
|
+
const persona = exports.PERSONA_HIRE_CATALOG[personaKey];
|
|
269
|
+
return mode === 'fulltime' ? persona.fulltimePriceCents : persona.jobPriceCents;
|
|
270
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PORTFOLIO_SLUG_OVERRIDES = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Portfolio filename slug overrides.
|
|
6
|
+
*
|
|
7
|
+
* When a persona's portfolio HTML file name differs from the persona key,
|
|
8
|
+
* add an entry here. Both `scripts/validate-employee-catalog.ts` and
|
|
9
|
+
* `src/routes/persona-catalog-routes.ts` import from this single module so
|
|
10
|
+
* the override map is defined exactly once.
|
|
11
|
+
*
|
|
12
|
+
* Issue #545.
|
|
13
|
+
*/
|
|
14
|
+
exports.PORTFOLIO_SLUG_OVERRIDES = {
|
|
15
|
+
cela: 'celia',
|
|
16
|
+
sekhar: 'sechar',
|
|
17
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Shared pricing configuration
|
|
3
|
+
// Used by both backend (payment-calculator.ts) and frontend (payment.js via API)
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.CONSUMER_EMAIL_DOMAINS = exports.FOUNDER_DISCOUNT_RATE = exports.MANAGED_PRICING = exports.FIXED_FEES = exports.PRICING = void 0;
|
|
6
|
+
exports.PRICING = {
|
|
7
|
+
'self-served': {
|
|
8
|
+
monthly: 20000, // $200/user/month
|
|
9
|
+
annual: 199200, // $200/user/month * 12 months * 0.83 (17% annual discount)
|
|
10
|
+
},
|
|
11
|
+
'managed': {
|
|
12
|
+
monthly: 20000, // $200/user/month (license cost)
|
|
13
|
+
annual: 199200, // $200/user/month * 12 months * 0.83 (17% annual discount)
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
exports.FIXED_FEES = {
|
|
17
|
+
'self-served': 0,
|
|
18
|
+
'managed': 0,
|
|
19
|
+
};
|
|
20
|
+
// Managed plan: $200/user/month (license) + $10K per group of 10 (training/support)
|
|
21
|
+
exports.MANAGED_PRICING = {
|
|
22
|
+
trainingFeePerGroup: 1000000, // $10,000 training/support per group of 10 licenses
|
|
23
|
+
licensesPerGroup: 10,
|
|
24
|
+
monthly: 1000000, // $10,000 training fee per group/month
|
|
25
|
+
annual: 9960000, // $10,000 training fee per group/month * 12 months * 0.83 (17% annual discount)
|
|
26
|
+
};
|
|
27
|
+
exports.FOUNDER_DISCOUNT_RATE = 0.90; // Discount rate for startups and academia (90% off)
|
|
28
|
+
exports.CONSUMER_EMAIL_DOMAINS = [
|
|
29
|
+
'gmail.com',
|
|
30
|
+
'yahoo.com',
|
|
31
|
+
'hotmail.com',
|
|
32
|
+
'outlook.com',
|
|
33
|
+
'aol.com',
|
|
34
|
+
'icloud.com',
|
|
35
|
+
'protonmail.com',
|
|
36
|
+
'mail.com',
|
|
37
|
+
];
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.STRIPE_CONFIG = exports.stripe = void 0;
|
|
7
|
+
const stripe_1 = __importDefault(require("stripe"));
|
|
8
|
+
// Lazy initialization to allow environment variables to be loaded first
|
|
9
|
+
let stripeInstance = null;
|
|
10
|
+
function getStripe() {
|
|
11
|
+
if (!stripeInstance) {
|
|
12
|
+
if (!process.env.STRIPE_SECRET_KEY) {
|
|
13
|
+
throw new Error('STRIPE_SECRET_KEY environment variable is required');
|
|
14
|
+
}
|
|
15
|
+
stripeInstance = new stripe_1.default(process.env.STRIPE_SECRET_KEY, {
|
|
16
|
+
apiVersion: '2026-01-28.clover',
|
|
17
|
+
typescript: true,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return stripeInstance;
|
|
21
|
+
}
|
|
22
|
+
exports.stripe = new Proxy({}, {
|
|
23
|
+
get(target, prop) {
|
|
24
|
+
return getStripe()[prop];
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
exports.STRIPE_CONFIG = {
|
|
28
|
+
get publishableKey() {
|
|
29
|
+
if (!process.env.STRIPE_PUBLISHABLE_KEY) {
|
|
30
|
+
throw new Error('STRIPE_PUBLISHABLE_KEY environment variable is required');
|
|
31
|
+
}
|
|
32
|
+
return process.env.STRIPE_PUBLISHABLE_KEY;
|
|
33
|
+
},
|
|
34
|
+
get webhookSecret() {
|
|
35
|
+
if (!process.env.STRIPE_WEBHOOK_SECRET) {
|
|
36
|
+
throw new Error('STRIPE_WEBHOOK_SECRET environment variable is required');
|
|
37
|
+
}
|
|
38
|
+
return process.env.STRIPE_WEBHOOK_SECRET;
|
|
39
|
+
},
|
|
40
|
+
currency: 'usd',
|
|
41
|
+
successUrl: process.env.STRIPE_SUCCESS_URL || 'https://fraimworks.ai/payment-success.html',
|
|
42
|
+
cancelUrl: process.env.STRIPE_CANCEL_URL || 'https://fraimworks.ai/pricing.html',
|
|
43
|
+
};
|
|
@@ -33,7 +33,8 @@ function normalizeIntegrations(config) {
|
|
|
33
33
|
itsm: current.itsm && typeof current.itsm === 'object'
|
|
34
34
|
? {
|
|
35
35
|
provider: current.itsm.provider,
|
|
36
|
-
instanceUrl: current.itsm.instanceUrl
|
|
36
|
+
instanceUrl: current.itsm.instanceUrl,
|
|
37
|
+
accessScript: current.itsm.accessScript
|
|
37
38
|
}
|
|
38
39
|
: undefined,
|
|
39
40
|
identity: current.identity && typeof current.identity === 'object'
|
|
@@ -49,13 +50,16 @@ function normalizeAutomation(config) {
|
|
|
49
50
|
support: {
|
|
50
51
|
startMode: support.startMode,
|
|
51
52
|
defaultDecisionMode: support.defaultDecisionMode,
|
|
52
|
-
|
|
53
|
+
playbooks: support.playbooks && typeof support.playbooks === 'object'
|
|
53
54
|
? {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
directory: support.playbooks.directory,
|
|
56
|
+
decisionCommand: support.playbooks.decisionCommand,
|
|
57
|
+
decisionTimeoutMs: support.playbooks.decisionTimeoutMs
|
|
57
58
|
}
|
|
58
59
|
: undefined,
|
|
60
|
+
actionAdapters: support.actionAdapters && typeof support.actionAdapters === 'object'
|
|
61
|
+
? support.actionAdapters
|
|
62
|
+
: undefined,
|
|
59
63
|
queue: support.queue && typeof support.queue === 'object'
|
|
60
64
|
? {
|
|
61
65
|
provider: support.queue.provider,
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readFraimConfigRaw = readFraimConfigRaw;
|
|
4
|
+
exports.mergeFraimConfig = mergeFraimConfig;
|
|
5
|
+
exports.writeFraimConfigUpdate = writeFraimConfigUpdate;
|
|
6
|
+
exports.writeFraimConfig = writeFraimConfig;
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const path_1 = require("path");
|
|
9
|
+
const config_loader_1 = require("./config-loader");
|
|
10
|
+
const types_1 = require("./types");
|
|
11
|
+
function isPlainObject(value) {
|
|
12
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
13
|
+
}
|
|
14
|
+
function deepMerge(baseValue, updateValue) {
|
|
15
|
+
if (updateValue === undefined) {
|
|
16
|
+
return baseValue;
|
|
17
|
+
}
|
|
18
|
+
if (Array.isArray(updateValue)) {
|
|
19
|
+
return [...updateValue];
|
|
20
|
+
}
|
|
21
|
+
if (!isPlainObject(updateValue)) {
|
|
22
|
+
return updateValue;
|
|
23
|
+
}
|
|
24
|
+
const baseObject = isPlainObject(baseValue) ? baseValue : {};
|
|
25
|
+
const merged = { ...baseObject };
|
|
26
|
+
for (const [key, value] of Object.entries(updateValue)) {
|
|
27
|
+
merged[key] = deepMerge(baseObject[key], value);
|
|
28
|
+
}
|
|
29
|
+
return merged;
|
|
30
|
+
}
|
|
31
|
+
function ensureWritableFraimConfigShape(rawConfig) {
|
|
32
|
+
const config = { ...rawConfig };
|
|
33
|
+
if (typeof config.version !== 'string' || config.version.trim().length === 0) {
|
|
34
|
+
config.version = types_1.DEFAULT_FRAIM_CONFIG.version;
|
|
35
|
+
}
|
|
36
|
+
const projectConfig = isPlainObject(config.project) ? config.project : {};
|
|
37
|
+
config.project = {
|
|
38
|
+
...types_1.DEFAULT_FRAIM_CONFIG.project,
|
|
39
|
+
...projectConfig
|
|
40
|
+
};
|
|
41
|
+
const customizationsConfig = isPlainObject(config.customizations) ? config.customizations : {};
|
|
42
|
+
config.customizations = {
|
|
43
|
+
...types_1.DEFAULT_FRAIM_CONFIG.customizations,
|
|
44
|
+
...customizationsConfig
|
|
45
|
+
};
|
|
46
|
+
return config;
|
|
47
|
+
}
|
|
48
|
+
function readFraimConfigRaw(configPath) {
|
|
49
|
+
if (!(0, fs_1.existsSync)(configPath)) {
|
|
50
|
+
return {};
|
|
51
|
+
}
|
|
52
|
+
const parsed = JSON.parse((0, fs_1.readFileSync)(configPath, 'utf8'));
|
|
53
|
+
if (!isPlainObject(parsed)) {
|
|
54
|
+
throw new Error('FRAIM config must contain a JSON object at the top level.');
|
|
55
|
+
}
|
|
56
|
+
return parsed;
|
|
57
|
+
}
|
|
58
|
+
function mergeFraimConfig(existingRawConfig, update) {
|
|
59
|
+
const rawConfig = ensureWritableFraimConfigShape(deepMerge(existingRawConfig, update));
|
|
60
|
+
return {
|
|
61
|
+
config: (0, config_loader_1.normalizeFraimConfig)(rawConfig),
|
|
62
|
+
created: Object.keys(existingRawConfig).length === 0,
|
|
63
|
+
rawConfig
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function writeFraimConfigUpdate(configPath, update) {
|
|
67
|
+
const existingRawConfig = readFraimConfigRaw(configPath);
|
|
68
|
+
const result = mergeFraimConfig(existingRawConfig, update);
|
|
69
|
+
(0, fs_1.mkdirSync)((0, path_1.dirname)(configPath), { recursive: true });
|
|
70
|
+
(0, fs_1.writeFileSync)(configPath, JSON.stringify(result.rawConfig, null, 2));
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
function writeFraimConfig(configPath, config) {
|
|
74
|
+
return writeFraimConfigUpdate(configPath, config);
|
|
75
|
+
}
|
|
@@ -335,23 +335,6 @@ exports.FRAIM_CONFIG_SCHEMA = {
|
|
|
335
335
|
"autonomous"
|
|
336
336
|
]
|
|
337
337
|
},
|
|
338
|
-
"contextResolver": {
|
|
339
|
-
"kind": "object",
|
|
340
|
-
"properties": {
|
|
341
|
-
"scriptPath": {
|
|
342
|
-
"kind": "string"
|
|
343
|
-
},
|
|
344
|
-
"arguments": {
|
|
345
|
-
"kind": "array",
|
|
346
|
-
"element": {
|
|
347
|
-
"kind": "string"
|
|
348
|
-
}
|
|
349
|
-
},
|
|
350
|
-
"timeoutMs": {
|
|
351
|
-
"kind": "number"
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
},
|
|
355
338
|
"playbooks": {
|
|
356
339
|
"kind": "object",
|
|
357
340
|
"properties": {
|
|
@@ -604,10 +587,6 @@ exports.SUPPORTED_FRAIM_CONFIG_PATHS = [
|
|
|
604
587
|
"automation.support",
|
|
605
588
|
"automation.support.startMode",
|
|
606
589
|
"automation.support.defaultDecisionMode",
|
|
607
|
-
"automation.support.contextResolver",
|
|
608
|
-
"automation.support.contextResolver.scriptPath",
|
|
609
|
-
"automation.support.contextResolver.arguments",
|
|
610
|
-
"automation.support.contextResolver.timeoutMs",
|
|
611
590
|
"automation.support.playbooks",
|
|
612
591
|
"automation.support.playbooks.directory",
|
|
613
592
|
"automation.support.playbooks.decisionCommand",
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEPRECATED_FRAIM_JOB_NAMES = void 0;
|
|
4
|
+
exports.resolveFraimJobName = resolveFraimJobName;
|
|
5
|
+
exports.isListableFraimJob = isListableFraimJob;
|
|
6
|
+
const DEPRECATED_TO_CANONICAL_JOB_MAP = {
|
|
7
|
+
'learn-and-scale': 'upskill-employee',
|
|
8
|
+
'model-behavior': 'upskill-employee',
|
|
9
|
+
'promote-learning': 'upskill-employee',
|
|
10
|
+
'refine-jobs': 'upskill-employee',
|
|
11
|
+
'refine-skills': 'upskill-employee'
|
|
12
|
+
};
|
|
13
|
+
const DIRECT_JOB_ALIASES = {
|
|
14
|
+
'sleep on learnings': 'sleep-on-learnings'
|
|
15
|
+
};
|
|
16
|
+
exports.DEPRECATED_FRAIM_JOB_NAMES = new Set(Object.keys(DEPRECATED_TO_CANONICAL_JOB_MAP));
|
|
17
|
+
function normalizeJobLookupInput(input) {
|
|
18
|
+
return input.trim().toLowerCase().replace(/[_\s]+/g, '-');
|
|
19
|
+
}
|
|
20
|
+
function resolveFraimJobName(input) {
|
|
21
|
+
const normalizedJobName = normalizeJobLookupInput(input);
|
|
22
|
+
const directAliasTarget = DIRECT_JOB_ALIASES[input.trim().toLowerCase()];
|
|
23
|
+
if (directAliasTarget) {
|
|
24
|
+
return {
|
|
25
|
+
requestedJobName: input,
|
|
26
|
+
normalizedJobName,
|
|
27
|
+
canonicalJobName: directAliasTarget
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const deprecatedAliasTarget = DEPRECATED_TO_CANONICAL_JOB_MAP[normalizedJobName];
|
|
31
|
+
if (deprecatedAliasTarget) {
|
|
32
|
+
return {
|
|
33
|
+
requestedJobName: input,
|
|
34
|
+
normalizedJobName,
|
|
35
|
+
canonicalJobName: deprecatedAliasTarget,
|
|
36
|
+
deprecatedAliasTarget
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
requestedJobName: input,
|
|
41
|
+
normalizedJobName,
|
|
42
|
+
canonicalJobName: normalizedJobName
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function isListableFraimJob(jobName) {
|
|
46
|
+
return !exports.DEPRECATED_FRAIM_JOB_NAMES.has(normalizeJobLookupInput(jobName));
|
|
47
|
+
}
|
|
@@ -371,9 +371,11 @@ class LocalRegistryResolver {
|
|
|
371
371
|
if (!this.hasLocalOverride(path)) {
|
|
372
372
|
const syncedLocalContent = this.readSyncedLocalFile(path);
|
|
373
373
|
if (syncedLocalContent !== null) {
|
|
374
|
+
// Synced baseline (fraim/ai-employee), not personalized.
|
|
374
375
|
return {
|
|
375
376
|
content: syncedLocalContent,
|
|
376
377
|
source: 'local',
|
|
378
|
+
personalized: false,
|
|
377
379
|
inherited: false
|
|
378
380
|
};
|
|
379
381
|
}
|
|
@@ -384,6 +386,7 @@ class LocalRegistryResolver {
|
|
|
384
386
|
return {
|
|
385
387
|
content,
|
|
386
388
|
source: 'remote',
|
|
389
|
+
personalized: false,
|
|
387
390
|
inherited: false
|
|
388
391
|
};
|
|
389
392
|
}
|
|
@@ -408,6 +411,7 @@ class LocalRegistryResolver {
|
|
|
408
411
|
return {
|
|
409
412
|
content,
|
|
410
413
|
source: 'remote',
|
|
414
|
+
personalized: false,
|
|
411
415
|
inherited: false
|
|
412
416
|
};
|
|
413
417
|
}
|
|
@@ -422,13 +426,16 @@ class LocalRegistryResolver {
|
|
|
422
426
|
return {
|
|
423
427
|
content,
|
|
424
428
|
source: 'remote',
|
|
429
|
+
personalized: false,
|
|
425
430
|
inherited: false
|
|
426
431
|
};
|
|
427
432
|
}
|
|
428
|
-
// Build result
|
|
433
|
+
// Build result — a local override hit means this came from the
|
|
434
|
+
// personalized-employee layer (the manager taught/customized it).
|
|
429
435
|
const result = {
|
|
430
436
|
content: resolved.content,
|
|
431
437
|
source: 'local',
|
|
438
|
+
personalized: true,
|
|
432
439
|
inherited: resolved.imports.length > 0,
|
|
433
440
|
imports: resolved.imports.length > 0 ? resolved.imports : undefined
|
|
434
441
|
};
|